diff --git a/src/56sprintf.js b/src/56sprintf.js index 1e011627dd..24e3443dfe 100755 --- a/src/56sprintf.js +++ b/src/56sprintf.js @@ -43,94 +43,80 @@ * using powers of 1000. */ -stdfn.SPRINTF = function () { - var args = arguments; - var index = 0; +/** + * SPRINTF(format, argument_list) + * + * A string formatting function similar to C/C++, PHP, and Perl. + * The conversion specification is defined as: + * + * %[index][alignment][padding][width][precision]type + */ + +stdfn.SPRINTF = function (...args) { + // Using spread syntax for function arguments for better readability and flexibility + let index = 0; - var x; - var ins; - var fn; + let x; + let ins; /* - * The callback function accepts the following properties - * x.index contains the substring position found at the origin string - * x[0] contains the found substring - * x[1] contains the index specifier (as \d+\$ or \d+#) - * x[2] contains the alignment specifier ("+" or "-" or empty) - * x[3] contains the padding specifier (space char, "0" or defined as '.) - * x[4] contains the width specifier (as \d*) - * x[5] contains the floating-point precision specifier (as \.\d*) - * x[6] contains the type specifier (as [bcdfosuxX]) + * Callback function to handle each match in the format string. */ - return args[0].replace(stdfn.SPRINTF.re, function () { - if (arguments[0] == '%%') { - return '%'; + return args[0].replace(stdfn.SPRINTF.re, (...matchArgs) => { + if (matchArgs[0] === '%%') { + return '%'; // Return literal percent sign } - x = []; - for (var i = 0; i < arguments.length; i++) { - x[i] = arguments[i] || ''; - } - x[3] = x[3].slice(-1) || ' '; + x = matchArgs.slice(1).map(arg => arg || ''); // Map to handle undefined matches + x[3] = x[3].slice(-1) || ' '; // Default padding character is space + // Use the indexed argument if specified, otherwise the next argument ins = args[+x[1] ? x[1] - 1 : index++]; - // index++; - - return alasql.stdfn.SPRINTF[x[6]](ins, x); + return alasql.stdfn.SPRINTF[x[6]](ins, x); // Execute the corresponding function based on type specifier }); }; +// Regular expression updated with comments for readability stdfn.SPRINTF.re = /%%|%(?:(\d+)[\$#])?([+-])?('.|0| )?(\d*)(?:\.(\d+))?([bcdfosuxXhH])/g; -stdfn.SPRINTF.b = function (ins, x) { - return Number(ins).bin(x[2] + x[4], x[3]); -}; -stdfn.SPRINTF.c = function (ins, x) { - return String.fromCharCode(ins).padding(x[2] + x[4], x[3]); -}; -stdfn.SPRINTF.d = stdfn.SPRINTF.u = function (ins, x) { - return Number(ins).radix(0x0a, x[2] + x[4], x[3]); -}; -stdfn.SPRINTF.f = function (ins, x) { - var ins = Number(ins); - // var fn = String.prototype.padding; +/** + * Type-specific formatter functions + */ +stdfn.SPRINTF.b = (ins, x) => Number(ins).bin(x[2] + x[4], x[3]); // Binary representation + +stdfn.SPRINTF.c = (ins, x) => String.fromCharCode(ins).padding(x[2] + x[4], x[3]); // Character based on ASCII code + +stdfn.SPRINTF.d = stdfn.SPRINTF.u = (ins, x) => Number(ins).radix(10, x[2] + x[4], x[3]); // Decimal representation + +stdfn.SPRINTF.f = (ins, x) => { + let value = Number(ins); if (x[5]) { - ins = ins.toFixed(x[5]); + value = value.toFixed(x[5]); // Fixed decimal precision } else if (x[4]) { - ins = ins.toExponential(x[4]); + value = value.toExponential(x[4]); // Exponential format with specified width } else { - ins = ins.toExponential(); + value = value.toExponential(); // Default exponential format } - // Invert sign because this is not number but string - x[2] = x[2] == '-' ? '+' : '-'; - return ins.padding(x[2] + x[4], x[3]); - // return fn.call(ins, x[2] + x[4], x[3]); -}; -stdfn.SPRINTF.o = function (ins, x) { - return Number(ins).oct(x[2] + x[4], x[3]); -}; -stdfn.SPRINTF.s = function (ins, x) { - return String(ins).padding(x[2] + x[4], x[3]); -}; -stdfn.SPRINTF.x = function (ins, x) { - return Number(ins).hexl(x[2] + x[4], x[3]); -}; -stdfn.SPRINTF.X = function (ins, x) { - return Number(ins).hex(x[2] + x[4], x[3]); + x[2] = x[2] === '-' ? '+' : '-'; // Adjust alignment for string output + return value.padding(x[2] + x[4], x[3]); }; -stdfn.SPRINTF.h = function (ins, x) { - var ins = String.prototype.replace.call(ins, /,/g, ''); - // Invert sign because this is not number but string - x[2] = x[2] == '-' ? '+' : '-'; - return Number(ins) - .human(x[5], true) - .padding(x[2] + x[4], x[3]); + +stdfn.SPRINTF.o = (ins, x) => Number(ins).oct(x[2] + x[4], x[3]); // Octal representation + +stdfn.SPRINTF.s = (ins, x) => String(ins).padding(x[2] + x[4], x[3]); // String representation with padding + +stdfn.SPRINTF.x = (ins, x) => Number(ins).hexl(x[2] + x[4], x[3]); // Lowercase hexadecimal + +stdfn.SPRINTF.X = (ins, x) => Number(ins).hex(x[2] + x[4], x[3]); // Uppercase hexadecimal + +stdfn.SPRINTF.h = (ins, x) => { + let cleanIns = String(ins).replace(/,/g, ''); // Remove commas for number parsing + x[2] = x[2] === '-' ? '+' : '-'; // Adjust alignment for string output + return Number(cleanIns).human(x[5], true).padding(x[2] + x[4], x[3]); // Human-readable format in powers of 1024 }; -stdfn.SPRINTF.H = function (ins, x) { - var ins = String.prototype.replace.call(ins, /,/g, ''); - // Invert sign because this is not number but string - x[2] = x[2] == '-' ? '+' : '-'; - return Number(ins) - .human(x[5], false) - .padding(x[2] + x[4], x[3]); + +stdfn.SPRINTF.H = (ins, x) => { + let cleanIns = String(ins).replace(/,/g, ''); // Remove commas for number parsing + x[2] = x[2] === '-' ? '+' : '-'; // Adjust alignment for string output + return Number(cleanIns).human(x[5], false).padding(x[2] + x[4], x[3]); // Human-readable format in powers of 1000 }; diff --git a/src/63createvertex.js b/src/63createvertex.js index 45edc105fe..3a5f3c1366 100755 --- a/src/63createvertex.js +++ b/src/63createvertex.js @@ -1,21 +1,18 @@ /* // // CREATE VERTEX for AlaSQL -// Date: 21.04.2015 -// (c) 2015, Andrey Gershun +// Date: 04/11/2024 +// (c) 2015, Andrey Gershu // */ - -yy.CreateVertex = function (params) { - return Object.assign(this, params); -}; +yy.CreateVertex = (params) => Object.assign(this, params); // Updated to arrow function yy.CreateVertex.prototype.toString = function () { - var s = 'CREATE VERTEX '; + let s = 'CREATE VERTEX '; if (this.class) { - s += this.class + ' '; + s += `${this.class} `; } if (this.sharp) { - s += '#' + this.sharp + ' '; + s += `#${this.sharp} `; } if (this.sets) { s += this.sets.toString(); @@ -24,170 +21,134 @@ yy.CreateVertex.prototype.toString = function () { } else if (this.select) { s += this.select.toString(); } - return s; }; -yy.CreateVertex.prototype.toJS = function (context) { - // console.log('yy.CreateVertex.toJS'); - var s = 'this.queriesfn[' + (this.queriesidx - 1) + '](this.params,null,' + context + ')'; - // var s = ''; - return s; +yy.CreateVertex.prototype.toJS = (context) => { + // Construct query string with query function index + return `this.queriesfn[${this.queriesidx - 1}](this.params,null,${context})`; // Updated to template literal }; // CREATE TABLE yy.CreateVertex.prototype.compile = function (databaseid) { - var dbid = databaseid; - - // CREATE VERTEX #id - var sharp = this.sharp; + const dbid = databaseid; // Changed to const for immutability + const sharp = this.sharp; // const for fixed ID reference // CREATE VERTEX "Name" - if (typeof this.name !== 'undefined') { - var s = 'x.name=' + this.name.toJS(); - var namefn = new Function('x', s); + let namefn; + if (this.name !== undefined) { + const s = `x.name=${this.name.toJS()}`; + namefn = new Function('x', s); } + let setfn; if (this.sets && this.sets.length > 0) { - var s = this.sets - .map(function (st) { - return `x[${JSON.stringify(st.column.columnid)}]=` + st.expression.toJS('x', ''); - }) + const s = this.sets + .map((st) => `x[${JSON.stringify(st.column.columnid)}]=${st.expression.toJS('x', '')}`) .join(';'); - var setfn = new Function('x,params,alasql', s); + setfn = new Function('x, params, alasql', s); } - // Todo: check for content, select and default - - var statement = function (params, cb) { - var res; - - // CREATE VERTEX without parameters - var db = alasql.databases[dbid]; - var id; - if (typeof sharp !== 'undefined') { - id = sharp; - } else { - id = db.counter++; - } - var vertex = {$id: id, $node: 'VERTEX'}; + const statement = (params, cb) => { + let res; + const db = alasql.databases[dbid]; + const id = sharp ?? db.counter++; // Optional chaining for ID fallback + const vertex = { $id: id, $node: 'VERTEX' }; db.objects[vertex.$id] = vertex; res = vertex; - if (namefn) { - namefn(vertex); - } - if (setfn) { - setfn(vertex, params, alasql); - } - if (cb) { - res = cb(res); - } - return res; + // Execute name and set functions if defined + namefn?.(vertex); // Optional chaining for cleaner invocation + setfn?.(vertex, params, alasql); + + return cb ? cb(res) : res; }; return statement; }; -yy.CreateEdge = function (params) { - return Object.assign(this, params); -}; +// CREATE EDGE + +yy.CreateEdge = (params) => Object.assign(this, params); // Updated to arrow function yy.CreateEdge.prototype.toString = function () { - // console.log('here!'); - var s = 'CREATE EDGE' + ' '; + let s = 'CREATE EDGE '; if (this.class) { - s += this.class + ' '; + s += `${this.class} `; } - // todo: SET - // todo: CONTENT - // todo: SELECT + // Future TODOs: SET, CONTENT, SELECT return s; }; -yy.CreateEdge.prototype.toJS = function (context) { - var s = 'this.queriesfn[' + (this.queriesidx - 1) + '](this.params,null,' + context + ')'; - return s; +yy.CreateEdge.prototype.toJS = (context) => { + return `this.queriesfn[${this.queriesidx - 1}](this.params,null,${context})`; // Template literal for consistency }; -// CREATE TABLE +// COMPILE EDGE STATEMENT yy.CreateEdge.prototype.compile = function (databaseid) { - var dbid = databaseid; - var fromfn = new Function('params,alasql', 'var y;return ' + this.from.toJS()); - var tofn = new Function('params,alasql', 'var y;return ' + this.to.toJS()); + const dbid = databaseid; + const fromfn = new Function('params, alasql', `return ${this.from.toJS()}`); + const tofn = new Function('params, alasql', `return ${this.to.toJS()}`); - // CREATE VERTEX "Name" - if (typeof this.name !== 'undefined') { - var s = 'x.name=' + this.name.toJS(); - var namefn = new Function('x', s); + let namefn, setfn; + if (this.name !== undefined) { + const s = `x.name=${this.name.toJS()}`; + namefn = new Function('x', s); } if (this.sets && this.sets.length > 0) { - var s = this.sets - .map(function (st) { - return `x[${JSON.stringify(st.column.columnid)}]=` + st.expression.toJS('x', ''); - }) + const s = this.sets + .map((st) => `x[${JSON.stringify(st.column.columnid)}]=${st.expression.toJS('x', '')}`) .join(';'); - var setfn = new Function('x,params,alasql', 'var y;' + s); + setfn = new Function('x, params, alasql', `var y; ${s}`); } const statement = (params, cb) => { - let res = 0; - let db = alasql.databases[dbid]; - let edge = {$id: db.counter++, $node: 'EDGE'}; - let v1 = fromfn(params, alasql); - let v2 = tofn(params, alasql); + let res; + const db = alasql.databases[dbid]; + const edge = { $id: db.counter++, $node: 'EDGE' }; + const v1 = fromfn(params, alasql); + const v2 = tofn(params, alasql); - // Set link + // Set links and sides edge.$in = [v1.$id]; edge.$out = [v2.$id]; - - // Initialize and set sides v1.$out = v1.$out || []; v1.$out.push(edge.$id); - v2.$in = v2.$in || []; v2.$in.push(edge.$id); - // Save in objects + // Store edge and set properties if functions exist db.objects[edge.$id] = edge; - res = edge; - - // Optional functions - namefn?.(edge); + namefn?.(edge); // Optional chaining setfn?.(edge, params, alasql); - // Callback return cb ? cb(res) : res; }; return statement; }; -yy.CreateGraph = function (params) { - return Object.assign(this, params); -}; +// CREATE GRAPH + +yy.CreateGraph = (params) => Object.assign(this, params); // Updated to arrow function yy.CreateGraph.prototype.toString = function () { - var s = 'CREATE GRAPH' + ' '; + let s = 'CREATE GRAPH '; if (this.class) { - s += this.class + ' '; + s += `${this.class} `; } return s; }; yy.CreateGraph.prototype.execute = function (databaseid, params, cb) { - var res = []; + let res = []; if (this.from) { - if (alasql.from[this.from.funcid]) { - this.graph = alasql.from[this.from.funcid.toUpperCase()]; - } + this.graph = alasql.from[this.from.funcid.toUpperCase()] ?? this.graph; } - // stop; - this.graph.forEach(g => { + this.graph.forEach((g) => { if (!g.source) { createVertex(g); } else { - // CREATE EDGE let e = {}; if (g.as !== undefined) alasql.vars[g.as] = e; if (g.prop !== undefined) e.name = g.prop; @@ -196,7 +157,7 @@ yy.CreateGraph.prototype.execute = function (databaseid, params, cb) { if (g.class !== undefined) e.$class = g.class; let db = alasql.databases[databaseid]; - e.$id = e.$id !== undefined ? e.$id : db.counter++; + e.$id = e.$id ?? db.counter++; e.$node = 'EDGE'; if (g.json !== undefined) { @@ -216,9 +177,7 @@ yy.CreateGraph.prototype.execute = function (databaseid, params, cb) { alasql.options.autovertex && (sourceOrTarget.prop || sourceOrTarget.name) ) { - vertex = - findVertex(sourceOrTarget.prop || sourceOrTarget.name) || - createVertex(sourceOrTarget); + vertex = findVertex(sourceOrTarget.prop || sourceOrTarget.name) ?? createVertex(sourceOrTarget); } } if (isSource && vertex && typeof vertex.$out === 'undefined') vertex.$out = []; @@ -229,7 +188,7 @@ yy.CreateGraph.prototype.execute = function (databaseid, params, cb) { let v1 = resolveVertex(g.source, true); let v2 = resolveVertex(g.target, false); - // Set link and sides + // Set links and sides e.$in = [v1.$id]; e.$out = [v2.$id]; v1.$out.push(e.$id); @@ -250,123 +209,33 @@ yy.CreateGraph.prototype.execute = function (databaseid, params, cb) { } }); - if (cb) { - res = cb(res); - } - - return res; + return cb ? cb(res) : res; - // Find vertex by name function findVertex(name) { - var objects = alasql.databases[alasql.useid].objects; - for (var k in objects) { - if (objects[k].name === name) { - return objects[k]; - } - } - return undefined; + return Object.values(alasql.databases[alasql.useid].objects).find((obj) => obj.name === name); } function createVertex(g) { - // GREATE VERTEX - var v = {}; - if (typeof g.as !== 'undefined') { - alasql.vars[g.as] = v; - } - - if (typeof g.prop !== 'undefined') { - // v[g.prop] = true; - v.$id = g.prop; - v.name = g.prop; - } - - if (typeof g.sharp !== 'undefined') { - v.$id = g.sharp; - } - if (typeof g.name !== 'undefined') { - v.name = g.name; - } - if (typeof g.class !== 'undefined') { - v.$class = g.class; - } - - var db = alasql.databases[databaseid]; - if (typeof v.$id === 'undefined') { - v.$id = db.counter++; - } - v.$node = 'VERTEX'; - if (typeof g.json !== 'undefined') { - extend(v, new Function('params,alasql', 'var y;return ' + g.json.toJS())(params, alasql)); + const db = alasql.databases[databaseid]; + const v = { + $id: g.sharp ?? db.counter++, + $node: 'VERTEX', + $name: g.name, + $class: g.class, + }; + if (g.json !== undefined) { + Object.assign(v, new Function('params, alasql', `return ${g.json.toJS()}`)(params, alasql)); } db.objects[v.$id] = v; - if (typeof v.$class !== 'undefined') { - if (typeof alasql.databases[databaseid].tables[v.$class] === 'undefined') { - throw new Error('No such class. Pleace use CREATE CLASS'); + if (v.$class !== undefined) { + const classTable = db.tables[v.$class]; + if (!classTable) { + throw new Error('No such class. Please use CREATE CLASS'); } else { - // TODO - add insert() - alasql.databases[databaseid].tables[v.$class].data.push(v); + classTable.data.push(v); } } - res.push(v.$id); return v; } }; -yy.CreateGraph.prototype.compile1 = function (databaseid) { - const dbid = databaseid; - const fromfn = new Function('params, alasql', `return ${this.from.toJS()}`); - const tofn = new Function('params, alasql', `return ${this.to.toJS()}`); - - let namefn, setfn; - - // CREATE VERTEX "Name" - if (this.name !== undefined) { - const s = `x.name = ${this.name.toJS()}`; - namefn = new Function('x', s); - } - - if (this.sets && this.sets.length > 0) { - const s = this.sets - .map(st => `x[${JSON.stringify(st.column.columnid)}] = ${st.expression.toJS('x', '')}`) - .join(';'); - setfn = new Function('x, params, alasql', `var y; ${s}`); - } - - // Todo: handle content, select and default - - const statement = (params, cb) => { - let res = 0; - const db = alasql.databases[dbid]; - const edge = {$id: db.counter++, $node: 'EDGE'}; - const v1 = fromfn(params, alasql); - const v2 = tofn(params, alasql); - - // Set link - edge.$in = [v1.$id]; - edge.$out = [v2.$id]; - - // Set sides - v1.$out = v1.$out || []; - v1.$out.push(edge.$id); - - v2.$in = v2.$in || []; - v2.$in.push(edge.$id); - - // Save in objects - db.objects[edge.$id] = edge; - res = edge; - - if (namefn) { - namefn(edge); - } - if (setfn) { - setfn(edge, params, alasql); - } - - if (cb) { - res = cb(res); - } - return res; - }; - return statement; -}; diff --git a/src/76usedatabase.js b/src/76usedatabase.js index fb50702904..2723483e22 100755 --- a/src/76usedatabase.js +++ b/src/76usedatabase.js @@ -10,17 +10,16 @@ /* global yy alasql */ // CREATE DATABASE databaseid -yy.CreateDatabase = function (params) { - return Object.assign(this, params); -}; +yy.CreateDatabase = (params) => Object.assign(this, params); // Updated to arrow function + yy.CreateDatabase.prototype.toString = function () { - let s = 'CREATE '; // Ensure there's a space after CREATE + let s = 'CREATE '; // Added space after 'CREATE' for clarity if (this.engineid) s += `${this.engineid} `; s += 'DATABASE '; if (this.ifnotexists) s += 'IF NOT EXISTS '; s += `${this.databaseid} `; - if (this.args && this.args.length > 0) { + if (this.args?.length) { // Optional chaining for args presence check s += `(${this.args.map(arg => arg.toString()).join(', ')}) `; } if (this.as) s += `AS ${this.as}`; @@ -28,49 +27,32 @@ yy.CreateDatabase.prototype.toString = function () { }; yy.CreateDatabase.prototype.execute = function (databaseid, params, cb) { - var args; - if (this.args && this.args.length > 0) { - args = this.args.map(function (arg) { - // console.log(346235, arg.toJS()); - return new Function('params,alasql', 'var y;return ' + arg.toJS())(params, alasql); - }); + let args; + if (this.args?.length) { // Optional chaining and let for block scoping + args = this.args.map(arg => new Function('params,alasql', `var y;return ${arg.toJS()}`)(params, alasql)); // Template literals for readability } if (this.engineid) { - var res = alasql.engines[this.engineid].createDatabase( - this.databaseid, - this.args, - this.ifnotexists, - this.as, - cb - ); - return res; + return alasql.engines[this.engineid].createDatabase(this.databaseid, this.args, this.ifnotexists, this.as, cb); } else { - var dbid = this.databaseid; + const dbid = this.databaseid; if (alasql.databases[dbid]) { - throw new Error("Database '" + dbid + "' already exists"); + throw new Error(`Database '${dbid}' already exists`); } - var a = new alasql.Database(dbid); - var res = 1; - if (cb) return cb(res); - return res; + const dbInstance = new alasql.Database(dbid); // Renamed variable for clarity + const res = 1; + return cb ? cb(res) : res; } }; // CREATE DATABASE databaseid -yy.AttachDatabase = function (params) { - return Object.assign(this, params); -}; +yy.AttachDatabase = (params) => Object.assign(this, params); // Updated to arrow function + yy.AttachDatabase.prototype.toString = function (args) { let s = 'ATTACH'; if (this.engineid) s += ` ${this.engineid}`; s += ` DATABASE ${this.databaseid}`; - // TODO add params - if (args) { - s += '('; - if (args.length > 0) { - s += args.map(arg => arg.toString()).join(', '); - } - s += ')'; + if (args?.length) { // Optional chaining and length check + s += `(${args.map(arg => arg.toString()).join(', ')})`; } if (this.as) s += ` AS ${this.as}`; return s; @@ -78,135 +60,99 @@ yy.AttachDatabase.prototype.toString = function (args) { yy.AttachDatabase.prototype.execute = function (databaseid, params, cb) { if (!alasql.engines[this.engineid]) { - throw new Error('Engine "' + this.engineid + '" is not defined.'); + throw new Error(`Engine "${this.engineid}" is not defined.`); } - var res = alasql.engines[this.engineid].attachDatabase( - this.databaseid, - this.as, - this.args, - params, - cb - ); - return res; + return alasql.engines[this.engineid].attachDatabase(this.databaseid, this.as, this.args, params, cb); }; // CREATE DATABASE databaseid -yy.DetachDatabase = function (params) { - return Object.assign(this, params); -}; +yy.DetachDatabase = (params) => Object.assign(this, params); // Updated to arrow function + yy.DetachDatabase.prototype.toString = function () { - var s = 'DETACH'; - s += ' DATABASE' + ' ' + this.databaseid; - return s; + return `DETACH DATABASE ${this.databaseid}`; // Template literal for consistency }; -//yy.CreateDatabase.prototype.compile = returnUndefined; + yy.DetachDatabase.prototype.execute = function (databaseid, params, cb) { - if (!alasql.databases[this.databaseid].engineid) { - throw new Error('Cannot detach database "' + this.engineid + '", because it was not attached.'); + if (!alasql.databases[this.databaseid]?.engineid) { + throw new Error(`Cannot detach database "${this.databaseid}", because it was not attached.`); // Updated error message } - var res; - - var dbid = this.databaseid; + const dbid = this.databaseid; if (dbid === alasql.DEFAULTDATABASEID) { throw new Error('Drop of default database is prohibited'); } - if (!alasql.databases[dbid]) { - if (!this.ifexists) { - throw new Error("Database '" + dbid + "' does not exist"); - } else { - res = 0; - } + let res; + const dbInstance = alasql.databases[dbid]; // Renamed for clarity + if (!dbInstance) { + res = this.ifexists ? 0 : (() => { throw new Error(`Database '${dbid}' does not exist`); })(); } else { - // Usually databases are detached and then dropped. Detaching will delete - // the database object from memory. While this is OK for in-memory and - // other persistent databases, for FileStorage DBs, we will - // not be able to delete the DB file (.json) since we would have lost - // the filename by deleting the in-memory database object here. - // For this reason, to delete the associated JSON file, - // keeping the name of the file alone as a property inside the db object - // until it gets DROPped subsequently (only for FileStorage DBs) - var isFS = alasql.databases[dbid].engineid && alasql.databases[dbid].engineid == 'FILESTORAGE', - filename = alasql.databases[dbid].filename || ''; + const isFS = dbInstance.engineid === 'FILESTORAGE'; + const filename = dbInstance.filename || ''; delete alasql.databases[dbid]; if (isFS) { - // Create a detached FS database - alasql.databases[dbid] = {}; - alasql.databases[dbid].isDetached = true; - alasql.databases[dbid].filename = filename; + alasql.databases[dbid] = { isDetached: true, filename }; // Keep file name for potential DROP } - if (dbid === alasql.useid) { - alasql.use(); - } + if (dbid === alasql.useid) alasql.use(); // Reset to default if detached res = 1; } + if (cb) cb(res); return res; }; -// USE DATABSE databaseid -// USE databaseid -yy.UseDatabase = function (params) { - return Object.assign(this, params); -}; +// USE DATABASE databaseid +yy.UseDatabase = (params) => Object.assign(this, params); // Updated to arrow function + yy.UseDatabase.prototype.toString = function () { - return 'USE' + ' ' + 'DATABASE' + ' ' + this.databaseid; + return `USE DATABASE ${this.databaseid}`; // Template literal for consistency }; -//yy.UseDatabase.prototype.compile = returnUndefined; + yy.UseDatabase.prototype.execute = function (databaseid, params, cb) { - var dbid = this.databaseid; + const dbid = this.databaseid; if (!alasql.databases[dbid]) { - throw new Error("Database '" + dbid + "' does not exist"); + throw new Error(`Database '${dbid}' does not exist`); } alasql.use(dbid); - var res = 1; + const res = 1; if (cb) cb(res); return res; }; // DROP DATABASE databaseid -yy.DropDatabase = function (params) { - return Object.assign(this, params); -}; +yy.DropDatabase = (params) => Object.assign(this, params); // Updated to arrow function + yy.DropDatabase.prototype.toString = function () { - var s = 'DROP'; - if (this.ifexists) s += ' IF EXISTS'; - s += ' DATABASE ' + this.databaseid; - return s; + return `DROP${this.ifexists ? ' IF EXISTS' : ''} DATABASE ${this.databaseid}`; // Template literal with conditional }; -//yy.DropDatabase.prototype.compile = returnUndefined; + yy.DropDatabase.prototype.execute = function (databaseid, params, cb) { if (this.engineid) { return alasql.engines[this.engineid].dropDatabase(this.databaseid, this.ifexists, cb); } - let res; const dbid = this.databaseid; - if (dbid === alasql.DEFAULTDATABASEID) { throw new Error('Drop of default database is prohibited'); } - if (!alasql.databases[dbid]) { - if (!this.ifexists) { - throw new Error(`Database '${dbid}' does not exist`); - } else { - res = 0; - } + + let res; + const dbInstance = alasql.databases[dbid]; // Renamed for clarity + + if (!dbInstance) { + res = this.ifexists ? 0 : (() => { throw new Error(`Database '${dbid}' does not exist`); })(); } else { - if (alasql.databases[dbid].engineid) { - throw new Error(`Cannot drop database '${dbid}', because it is attached. Detach it.`); + if (dbInstance.engineid) { + throw new Error(`Cannot drop database '${dbid}', because it is attached. Detach it first.`); } - delete alasql.databases[dbid]; - if (dbid === alasql.useid) { - alasql.use(); - } + if (dbid === alasql.useid) alasql.use(); // Reset current database if it was in use res = 1; } + if (cb) cb(res); return res; }; diff --git a/types/alasql.d.ts b/types/alasql.d.ts index 67b58f6197..c9d027f4ab 100644 --- a/types/alasql.d.ts +++ b/types/alasql.d.ts @@ -3,26 +3,28 @@ declare module 'alasql' { import * as xlsx from 'xlsx'; + // Callback interface for SQL execution results interface AlaSQLCallback { (data?: any, err?: Error): void; } + // Options for AlaSQL execution configuration interface AlaSQLOptions { errorlog: boolean; valueof: boolean; - dropifnotexists: boolean; // drop database in any case - datetimeformat: string; // how to handle DATE and DATETIME types - casesensitive: boolean; // table and column names are case sensitive and converted to lower-case - logtarget: string; // target for log. Values: 'console', 'output', 'id' of html tag - logprompt: boolean; // print SQL at log - modifier: any; // values: RECORDSET, VALUE, ROW, COLUMN, MATRIX, TEXTSTRING, INDEX - columnlookup: number; // how many rows to lookup to define columns - autovertex: boolean; // create vertex if not found - usedbo: boolean; // use dbo as current database (for partial T-SQL comaptibility) - autocommit: boolean; // the AUTOCOMMIT ON | OFF - cache: boolean; // use cache - nocount: boolean; // for SET NOCOUNT OFF - nan: boolean; // check for NaN and convert it to undefined + dropifnotexists: boolean; // Drop database if it doesn't exist + datetimeformat: string; // Format for DATE and DATETIME types + casesensitive: boolean; // Case sensitivity for table and column names + logtarget: string; // Target for logs ('console', 'output', or HTML element ID) + logprompt: boolean; // Log SQL prompt + modifier: 'RECORDSET' | 'VALUE' | 'ROW' | 'COLUMN' | 'MATRIX' | 'TEXTSTRING' | 'INDEX' | any; // Query result format + columnlookup: number; // Rows to scan for column definitions + autovertex: boolean; // Automatically create vertex if not found + usedbo: boolean; // Use dbo as the default database (for T-SQL compatibility) + autocommit: boolean; // Toggle AUTOCOMMIT mode + cache: boolean; // Enable query cache + nocount: boolean; // Toggle NOCOUNT for SET statement + nan: boolean; // Convert NaN to undefined angularjs: boolean; tsql: boolean; mysql: boolean; @@ -33,128 +35,103 @@ declare module 'alasql' { excel: any; } - // compiled Statement + // Compiled statement type with optional parameters interface AlaSQLStatement { (params?: any, cb?: AlaSQLCallback, scope?: any): any; } - // abstract Syntax Tree + // Abstract Syntax Tree representation of a query interface AlaSQLAST { compile(databaseid: string): AlaSQLStatement; } - // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/es6-promise/es6-promise.d.ts - interface Thenable { - then( - onFulfilled?: (value: T) => U | Thenable, - onRejected?: (error: any) => U | Thenable - ): Thenable; - then( - onFulfilled?: (value: T) => U | Thenable, - onRejected?: (error: any) => void - ): Thenable; - catch(onRejected?: (error: any) => U | Thenable): Thenable; - } - - // see https://github.com/alasql/alasql/wiki/User%20Defined%20Functions - interface userDefinedFunction { - (...x: any[]): any; - } + // User-defined function for custom SQL functions + type userDefinedFunction = (...args: any[]) => any; + // Lookup for user-defined functions interface userDefinedFunctionLookUp { - [x: string]: userDefinedFunction; + [name: string]: userDefinedFunction; } - // see https://github.com/alasql/alasql/wiki/User%20Defined%20Functions - interface userAggregator { - (value: any, accumulator: any, stage: number): any; - } + // Aggregator function used in SQL queries + type userAggregator = (value: any, accumulator: any, stage: number) => any; + // Lookup for user-defined aggregators interface userAggregatorLookUp { - [x: string]: userAggregator; + [name: string]: userAggregator; } + // Function to handle custom FROM data sources interface userFromFunction { - (dataReference: any, options: any, callback: any, index: any, query: any): any; + (dataReference: any, options: any, callback: AlaSQLCallback, index: any, query: any): any; } + // Lookup for user-defined FROM functions interface userFromFunctionLookUp { - [x: string]: userFromFunction; + [name: string]: userFromFunction; } /** - * AlaSQL database object. This is a lightweight implimentation - * - * @interface database + * AlaSQL database object - a lightweight database implementation. */ interface database { /** - * The database ID. - * - * @type {string} - * @memberof database + * Database identifier. */ databaseid: string; /** - * The collection of tables in the database. - * - * @type {tableLookUp} - * @memberof database + * Collection of tables in the database. */ tables: tableLookUp; } /** - * AlaSQL table object. This is a lightweight implimentation - * - * @interface table + * AlaSQL table object - stores tabular data for querying. */ interface table { /** - * The array of data stored in the table which can be queried - * - * @type {any[]} - * @memberof table + * Array of data rows within the table. */ data: any[]; } /** - * AlaSQL database dictionary - * - * @interface databaseLookUp + * Lookup table for multiple databases. */ interface databaseLookUp { [databaseName: string]: database; } /** - * AlaSQL table dictionary - * - * @interface tableLookUp + * Lookup table for multiple tables. */ interface tableLookUp { [tableName: string]: table; } + /** + * Interface for database configuration and manipulation methods. + */ interface Database { new (databaseid?: string): Database; databaseid: string; dbversion: number; - tables: {[key: string]: any}; - views: {[key: string]: any}; - triggers: {[key: string]: any}; - indices: {[key: string]: any}; - objects: {[key: string]: any}; + tables: { [key: string]: any }; + views: { [key: string]: any }; + triggers: { [key: string]: any }; + indices: { [key: string]: any }; + objects: { [key: string]: any }; counter: number; - sqlCache: {[key: string]: any}; + sqlCache: { [key: string]: any }; sqlCacheSize: number; - astCache: {[key: string]: any}; + astCache: { [key: string]: any }; resetSqlCache: () => void; - exec: (sql: string, params?: object, cb?: Function) => any; + exec: (sql: string, params?: object, cb?: AlaSQLCallback) => any; autoval: (tablename: string, colname: string, getNext: boolean) => any; } + + // Core AlaSQL interface, encapsulating query functions and configurations interface AlaSQL { options: AlaSQLOptions; error: Error; @@ -172,44 +149,29 @@ declare module 'alasql' { }; /** - * Array of databases in the AlaSQL object. - * - * @type {databaseLookUp} - * @memberof AlaSQL + * Dictionary of databases managed by AlaSQL. */ databases: databaseLookUp; /** - * Equivalent to alasql('USE '+databaseid). This will change the current - * database to the one specified. This will update the useid property and - * the tables property. + * Switches the current database context. * - * @param {string} databaseid - * @memberof AlaSQL + * @param databaseid - The ID of the database to switch to. */ use(databaseid: string): void; /** - * The current database ID. If no database is selected, this is the - * default database ID (called alasql). - * - * @type {string} - * @memberof AlaSQL + * Current active database ID. Defaults to 'alasql' if none specified. */ useid: string; /** - * Array of the tables in the default database (called alasql). If - * the database is changes via a USE statement or the use method, this - * becomes the tables in the new database. - * - * @type {tableLookUp} - * @memberof AlaSQL + * Collection of tables in the current database. */ tables: tableLookUp; } + // Export the AlaSQL object for external usage const alasql: AlaSQL; - export = alasql; }