From 50081a3c58ca30b2b7566d6c0c03411de49fff60 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 23:09:21 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/db/dbS.js | 502 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 js/db/dbS.js diff --git a/js/db/dbS.js b/js/db/dbS.js new file mode 100644 index 0000000..3cfce52 --- /dev/null +++ b/js/db/dbS.js @@ -0,0 +1,502 @@ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2017 bLAB + ** $CREATED: 09/02/16 by sbosse. + ** $VERSION: 1.4.2 + ** + ** $INFO: + ** + ** JavaScript AIOS: SQLD Database Client Module using named FIFO pipes + ** + ** path_in: the write to SQLD channel (SQLD input) + ** path_out: the read from SQLD channel (SQLD output) + ** + ** + ** Asynchronous (using non-blocking Stream IO) + ** + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var Fs = Require('fs'); +var current={}; +var Aios=none; +var NL='\n'; + +function exists(path) { + try { + var fd=Fs.openSync(path,'r'); + Fs.close(fd); + return true; + } catch (e) { return false } +} + + +var sqlc = function (path,chan) { + this.path=path; + // Client -> Server + this.input=none; + // Server -> Client + this.output=none; + this.chan=chan; + this.id='SQLC'; + this.log=function (msg) {Io.log('[DB] '+msg)}; + this.todo=[]; + this.error=undefined; +} + +/** Return string list of comma seperated value list + * + Strip string delimeters '' + * + */ +sqlc.prototype.args = function (msg) { + var args=Comp.string.split(',',msg); + return Comp.array.filtermap(args, function (arg) { + if (arg=='') return none; + else if (Comp.string.get(arg,0)=="'") return Comp.string.trim(arg,1,1); + else return arg; + }); +}; +/** Close all streams + * + */ +sqlc.prototype.close = function () { + if (this.input) this.input.end('set close'); + this.input=none; + this.output=none; +} + +/** Create a matrix + * + * + * + */ + +sqlc.prototype.createMatrix = function (matname,header,callback) { + var repl,self=this, + intf='', sep=''; + if (this.input==undefined || this.output==undefined) return false; + if (header.length==0) return false; + Comp.array.iter(header,function (col,i) { + var tokens = Comp.obj.isNumber(col)?[col]:col.split(':'), + name=tokens[0],typ=tokens[1]; + if (Comp.obj.isNumber(name)) intf = intf + sep + 'c'+col+(typ?typ:'integer'); + else intf += sep+name+' '+(typ?typ:' varchar(32)'); + sep=','; + }); + function done(_repl) { + repl=_repl[0]; + if (!repl || self.err(repl)) {self.error=repl;return false;} + else return true; + } + return this.seq([ + ['exec','drop table if exists '+matname, done], + ['exec','create table '+matname+' ('+intf+')', done] + ],callback); + +} + +/** Create a generic table + * + * typeof @header = {} is type and name interface provider for all rows; + * + */ +sqlc.prototype.createTable = function (matname,header,callback) { + var repl, + intf='', sep='', self=this; + if (this.input==undefined || this.output==undefined) return false; + if (header.length==0) return false; + Comp.obj.iter(header,function (attr,key) { + intf += (sep+key+(Comp.obj.isNumber(attr)?' integer':' varchar(32)')); sep=','; + }); + function done(_repl) { + repl=_repl[0]; + if (!repl || self.err(repl)) {self.error=repl;return false;} + else return true; + } + return this.seq([ + ['exec','drop table if exists '+matname, done], + ['exec','create table '+matname+' ('+intf+')', done], + ],callback); +} + +/** Drop table operation + * + */ +sqlc.prototype.drop = function (tbl,callback) { + var repl,self=this, + line, + sep=''; + if (this.input==undefined || this.output==undefined) return false; + function done(_repl) { + repl=_repl[0]; + if (!repl || self.err(repl)) {self.error=repl;return false;} + else return true; + } + return this.seq([ + ['exec','drop table if exists '+tbl, done] + ], callback); +} + +/** Check end message + * + */ +sqlc.prototype.end = function (msg) { return msg.indexOf('END') == 0 } +/** Check error message + * + */ +sqlc.prototype.err = function (msg) { return msg.indexOf('ERR') == 0 || msg.indexOf('Error') == 0} + +/** Execute: send a request and wait for reply. + */ +sqlc.prototype.exec = function (cmd,callback) { + var n,str='',fd; + if (this.input==undefined || this.output==undefined) return none; + this.callback=callback; + this.input.write(cmd+NL,'utf8',function (){ + }); +}; + +/** GET ROW operation + * fmt: object + * callback: function () -> string [] | * [] | none + * + */ +sqlc.prototype.get = function (fmt,callback) { + var row,self=this,repl; + function done(_repl) { + repl=_repl?_repl[0]:none; + if (!repl || self.err(repl)) {self.error=repl; row=none; return false;} + else return true; + } + this.seq([ + ['exec','get row',function (_repl) { + var cols,i,p; + if (done(_repl) && !self.end(repl)) { + repl=Comp.string.replace_all('\n','',repl); + cols=Comp.string.split(',',repl); + if (fmt) { + i=0;row=[]; + for(p in fmt) { + switch (fmt[p]) { + case 'string': + row.push(cols[i]); + break; + case 'number': + row.push(Number(cols[i])); + break; + default: + row.push(cols[i]); + } + i++; + } + } else row=cols; + } + return true; + }] + ], callback?function (res) { if (res) callback(row); else callback(none);}:undefined); + if (callback) return; else return row; + +} + +/** Setup client-server connection. + * Only the input stream is opened (used for sending data to the SQLD server). + * + */ +sqlc.prototype.init = function (callback) { + var path_in = Comp.printf.sprintf("%sI%s%d",this.path,this.chan<10?'0':'',this.chan), + path_out = Comp.printf.sprintf("%sO%s%d",this.path,this.chan<10?'0':'',this.chan), + stat,repl,self=this; + this.id = Comp.printf.sprintf("[SQLC%s%d]",this.chan<10?'0':'',this.chan); + this.log (Comp.printf.sprintf("%s Connecting to server channel %s...", + this.id,path_in)); + if (!exists(path_in) || !exists(path_out)) { + this.log (Comp.printf.sprintf("%s Connecting to server channel %s failed: %s", + this.id,path_in,'No such file(s)')); + if (callback) {callback(false); return;} + else return false; + } + + try { + this.input = Fs.createWriteStream(path_in); + } catch (e) { + this.log (Comp.printf.sprintf("%s Connecting to server channel %s failed: %s", + this.id,path_in,e)); + if (callback) callback(false); return; + } + this.output = path_out; + this.callback=none; + + return this.seq([ + ['open',function (repl) {return (repl && !self.err(repl[0]))}], + ['set','nl',function (repl) {return (repl && !self.err(repl[0]))}], + ['set','csv',function (repl) {return (repl && !self.err(repl[0]))}] + ], callback); +} +/** Insert operation + * + */ +sqlc.prototype.insert = function (tbl,row,callback) { + var repl,self=this, + line='', + sep=''; + if (this.input==undefined || this.output==undefined) return false; + Comp.array.iter(row,function (col,i) {line += sep+(Comp.obj.isNumber(col)?int(col):"'"+col+"'"); sep=',';}); + function done(_repl) { + repl=_repl[0]; + if (!repl || self.err(repl)) {self.error=repl;return false;} + else return true; + } + return this.seq([ + ['exec','insert into '+tbl+' values ('+line+')', done] + ], callback); +} + +/** Insert a row in a matrix table + * + */ +sqlc.prototype.insertMatrix = function (matname,row,callback) { + var repl,self=this, + line='', + sep=''; + if (this.input==undefined || this.output==undefined) return false; + Comp.array.iter(row,function (col,i) {line += sep+(Comp.obj.isNumber(col)?int(col):"'"+col+"'"); sep=',';}); + function done(_repl) { + repl=_repl[0]; + if (!repl || self.err(repl)) {self.error=repl;return false;} + else return true; + } + return this.seq([ + ['exec','insert into '+matname+' values ('+line+')', done] + ], callback); +} + +sqlc.prototype.insertTable = sqlc.prototype.insert; + +/** Read a matrix + * + */ +sqlc.prototype.readMatrix = function (matname,callback) { + var mat=[],self=this, + repl, + cols; + if (this.input==undefined || this.output==undefined) return none; + + function done(_repl) { + repl=_repl[0]; + if (!repl || self.err(repl)) {self.error=repl; mat=none; return false;} + else return true; + } + + this.seq([ + ['exec','select * from '+matname,done], + ['exec','get row',function (_repl) { + if (done(_repl)) { + if (!self.end(repl)) { + repl=Comp.string.replace_all('\n','',repl); + var cols=Comp.array.map(Comp.string.split(',',repl),function (col) { + return Comp.pervasives.int_of_string(col); + }); + mat.push(cols); + return none; + } else return true; + } else return false; + }] + ], callback?function (res) { if (res) callback(mat); else callback(none);}:undefined); + if (callback) return; else return mat; +} + +// TODO +sqlc.prototype.readTable = sqlc.prototype.readMatrix; + +/** SELECT operation + * tbl: string + * vars?: string | string [] + * cond: string | undefined + * callback: function () -> status string + * + */ +sqlc.prototype.select = function (tbl,vars,cond,callback) { + var self=this,repl; + function done(_repl) { + repl=_repl?_repl[0]:none; + if (!repl || self.err(repl)) {self.error=repl; return false;} + else return true; + } + if (vars==undefined) vars='*'; + return this.seq([ + ['exec',Comp.printf.sprintf('select %s from %s%s', + Comp.obj.isArray(vars)?Comp.printf.list(vars):vars, + tbl, + cond?(' WHERE '+cond):''),done] + ],callback); + +} + +/** Execute a SQL operation sequence + ** todo format: [op: string, + ** args?: string, + ** result: function returning boolean (false: break (error), true: next, _:loop)] + ** callback: function () -> status + */ +sqlc.prototype.seq = function (todo,callback) { + var l=todo,self=this,status,res, + next; + function Todo() { + if (self.todo.length>0) { + var f = Comp.array.head(self.todo); + self.error=undefined; + f(); + self.todo = Comp.array.tail(self.todo); + Todo(); + } + } + function Todo() { + if (self.todo.length>0) { + var f = Comp.array.head(self.todo); + self.error=undefined; + f(_,function () { + self.todo = Comp.array.tail(self.todo); + Todo(); + }); + } + } + next=function (loop,finalize) { + if (l.length==0 && !loop) { callback(status); if (finalize) finalize() } + else { + var hd; + if (!loop) { + hd= Comp.array.head(l); + l = Comp.array.tail(l); + } else hd=loop; + switch (hd[0]) { + case 'open': + if (!Comp.obj.isString(self.output)) return; + try { + self.callback=function (repl) { + status=hd[1](repl); + if (status) next(_,finalize); else callback(status); + }; + self.input.write('set stream'); + var path_out = self.output; + self.output = Fs.createReadStream(path_out); + self.output.on('close',function () { + // console.log('CLOSE'); + // TODO ?? + self.log('Close on output stream '+path_out); + }); + self.output.on('data',function (chunk) { + // Assumption: one chunk == one reply !? + if (self.callback) { + var cb=self.callback; + self.callback=none; + cb(Comp.array.filter(Comp.string.split('\n',chunk.toString()),function(line) {return line!=''})); + } + }); + } catch (e) { + self.log (Comp.printf.sprintf("%s Connecting to server channel %s failed: %s", + this.id,path_out,e)); + callback(false); + } + break; + case 'set': + self.set(hd[1],function (repl) { + status=hd[2](repl); + if (status) next(_,finalize); else callback(status,self.error); + }); + break; + case 'exec': + self.exec(hd[1],function (repl) { + status=hd[2](repl); + if (status) next(_,finalize); + else if (status==undefined) next(hd,finalize); + else callback(status,self.error); + }); + break; + } + } + } + self.todo.push(next); + if (self.todo.length==1) Todo(); + return; +}; + +/** Set a SQLD flag (nl,csv,..). + * + */ +sqlc.prototype.set = function (flag,callback) { + var n,fd,str=''; + if (this.input==undefined) return false; + this.callback=callback; + this.input.write('set '+flag,'utf8',function (){ + }); +}; + +sqlc.prototype.setLog = function (f) { + this.log=function (msg) {f('[DB] '+msg)}; +} + +/** Write matrix values + * + */ +sqlc.prototype.writeMatrix = function (matname,matrix,callback) { + var repl, line='', seq=[], self=this, + intf='', sep='', i=0, row; + if (this.input==undefined || this.output==undefined) return false; + if (matrix.length==0) return false; + Comp.array.iter(matrix[0],function (col,i) { + intf += sep+'c'+(i+1)+(Comp.obj.isNumber(col)?' integer':' varchar(32)'); sep=','; + }); + row=matrix[0]; + Comp.array.iter(row,function (col,i) { + line += sep+(Comp.obj.isNumber(col)?int(col):"'"+col+"'"); sep=','; + }); + function done(_repl) { + repl=_repl[0]; + if (!repl || self.err(repl)) {self.error=repl;return false;} + else return true; + } + seq=[ + ['exec','drop table if exists '+matname,done], + ['exec','create table '+matname+' ('+intf+')',done] + ]; + for(i=0;i