From 7d6ee6a675b1e46b755dc0bca96f5ac689985eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20W=C3=A4rting?= Date: Mon, 9 Jan 2023 13:04:55 +0100 Subject: [PATCH] Refactor Select and ExistValue (#1607) --- src/40select.js | 718 +++++++++++++++++++++++++----------------------- src/41exists.js | 49 +--- 2 files changed, 383 insertions(+), 384 deletions(-) diff --git a/src/40select.js b/src/40select.js index 51f4e1aa0a..44273bc256 100755 --- a/src/40select.js +++ b/src/40select.js @@ -12,393 +12,422 @@ /* global yy */ -yy.Select = function (params) { - return Object.assign(this, params); -}; -yy.Select.prototype.toString = function () { - var s; - s = ''; - if (this.explain) { - s += 'EXPLAIN '; - } - s += 'SELECT '; - if (this.modifier) { - s += this.modifier + ' '; +yy.Select = class Select { + constructor(params) { + Object.assign(this, params); } - if (this.distinct) { - s += 'DISTINCT '; - } - if (this.top) { - s += 'TOP ' + this.top.value + ' '; - if (this.percent) { - s += 'PERCENT '; + + toString() { + var s; + s = ''; + if (this.explain) { + s += 'EXPLAIN '; } - } - s += this.columns - .map(function (col) { - var s; - s = col.toString(); - if (typeof col.as !== 'undefined') { - s += ' AS ' + col.as; + s += 'SELECT '; + if (this.modifier) { + s += this.modifier + ' '; + } + if (this.distinct) { + s += 'DISTINCT '; + } + if (this.top) { + s += 'TOP ' + this.top.value + ' '; + if (this.percent) { + s += 'PERCENT '; } - return s; - }) - .join(', '); - if (this.from) { - s += - ' FROM ' + - this.from - .map(function (f) { + } + s += this.columns + .map(function (col) { + var s; + s = col.toString(); + if (typeof col.as !== 'undefined') { + s += ' AS ' + col.as; + } + return s; + }) + .join(', '); + if (this.from) { + s += + ' FROM ' + + this.from + .map(function (f) { + var ss; + ss = f.toString(); + if (f.as) { + ss += ' AS ' + f.as; + } + return ss; + }) + .join(','); + } + if (this.joins) { + s += this.joins + .map(function (jn) { var ss; - ss = f.toString(); - if (f.as) { - ss += ' AS ' + f.as; + ss = ' '; + if (jn.joinmode) { + ss += jn.joinmode + ' '; + } + if (jn.table) { + ss += 'JOIN ' + jn.table.toString(); + } else if (jn.select) { + ss += 'JOIN (' + jn.select.toString() + ')'; + } else if (jn instanceof alasql.yy.Apply) { + ss += jn.toString(); + } else { + throw new Error('Wrong type in JOIN mode'); + } + if (jn.as) { + ss += ' AS ' + jn.as; + } + if (jn.using) { + ss += ' USING ' + jn.using.toString(); + } + if (jn.on) { + ss += ' ON ' + jn.on.toString(); } return ss; }) - .join(','); - } - if (this.joins) { - s += this.joins - .map(function (jn) { - var ss; - ss = ' '; - if (jn.joinmode) { - ss += jn.joinmode + ' '; - } - if (jn.table) { - ss += 'JOIN ' + jn.table.toString(); - } else if (jn.select) { - ss += 'JOIN (' + jn.select.toString() + ')'; - } else if (jn instanceof alasql.yy.Apply) { - ss += jn.toString(); - } else { - throw new Error('Wrong type in JOIN mode'); - } - if (jn.as) { - ss += ' AS ' + jn.as; - } - if (jn.using) { - ss += ' USING ' + jn.using.toString(); - } - if (jn.on) { - ss += ' ON ' + jn.on.toString(); - } - return ss; - }) - .join(''); - } - if (this.where) { - s += ' WHERE ' + this.where.toString(); - } - if (this.group && this.group.length > 0) { - s += - ' GROUP BY ' + - this.group - .map(function (grp) { - return grp.toString(); - }) - .join(', '); - } - if (this.having) { - s += ' HAVING ' + this.having.toString(); - } - if (this.order && this.order.length > 0) { - s += - ' ORDER BY ' + - this.order - .map(function (ord) { - return ord.toString(); - }) - .join(', '); - } - if (this.limit) { - s += ' LIMIT ' + this.limit.value; - } - if (this.offset) { - s += ' OFFSET ' + this.offset.value; - } - if (this.union) { - s += ' UNION ' + (this.corresponding ? 'CORRESPONDING ' : '') + this.union.toString(); - } - if (this.unionall) { - s += ' UNION ALL ' + (this.corresponding ? 'CORRESPONDING ' : '') + this.unionall.toString(); - } - if (this.except) { - s += ' EXCEPT ' + (this.corresponding ? 'CORRESPONDING ' : '') + this.except.toString(); - } - if (this.intersect) { - s += ' INTERSECT ' + (this.corresponding ? 'CORRESPONDING ' : '') + this.intersect.toString(); + .join(''); + } + if (this.where) { + s += ' WHERE ' + this.where.toString(); + } + if (this.group && this.group.length > 0) { + s += + ' GROUP BY ' + + this.group + .map(function (grp) { + return grp.toString(); + }) + .join(', '); + } + if (this.having) { + s += ' HAVING ' + this.having.toString(); + } + if (this.order && this.order.length > 0) { + s += + ' ORDER BY ' + + this.order + .map(function (ord) { + return ord.toString(); + }) + .join(', '); + } + if (this.limit) { + s += ' LIMIT ' + this.limit.value; + } + if (this.offset) { + s += ' OFFSET ' + this.offset.value; + } + if (this.union) { + s += ' UNION ' + (this.corresponding ? 'CORRESPONDING ' : '') + this.union.toString(); + } + if (this.unionall) { + s += ' UNION ALL ' + (this.corresponding ? 'CORRESPONDING ' : '') + this.unionall.toString(); + } + if (this.except) { + s += ' EXCEPT ' + (this.corresponding ? 'CORRESPONDING ' : '') + this.except.toString(); + } + if (this.intersect) { + s += ' INTERSECT ' + (this.corresponding ? 'CORRESPONDING ' : '') + this.intersect.toString(); + } + return s; } - return s; -}; - -/** - Select statement in expression - */ -yy.Select.prototype.toJS = function (context) { - // console.log('Expression',this); - // if(this.expression.reduced) return 'true'; - // return this.expression.toJS(context, tableid, defcols); - // console.log('Select.toJS', 81, this.queriesidx); - // var s = 'this.queriesdata['+(this.queriesidx-1)+'][0]'; - - var s = - 'alasql.utils.flatArray(this.queriesfn[' + - (this.queriesidx - 1) + - '](this.params,null,' + - context + - '))[0]'; - - // var s = '(ee=alasql.utils.flatArray(this.queriesfn['+(this.queriesidx-1)+'](this.params,null,'+context+')),console.log(999,ee),ee[0])'; - - return s; -}; -// Compile SELECT statement -yy.Select.prototype.compile = function (databaseid, params) { - var db = alasql.databases[databaseid]; - // Create variable for query - var query = new Query(); + /** + Select statement in expression + */ + toJS(context) { + // console.log('Expression',this); + // if(this.expression.reduced) return 'true'; + // return this.expression.toJS(context, tableid, defcols); + // console.log('Select.toJS', 81, this.queriesidx); + // var s = 'this.queriesdata['+(this.queriesidx-1)+'][0]'; + var s = + 'alasql.utils.flatArray(this.queriesfn[' + + (this.queriesidx - 1) + + '](this.params,null,' + + context + + '))[0]'; + + // var s = '(ee=alasql.utils.flatArray(this.queriesfn['+(this.queriesidx-1)+'](this.params,null,'+context+')),console.log(999,ee),ee[0])'; + return s; + } - // Array with columns to be removed - query.removeKeys = []; - query.aggrKeys = []; + // Compile SELECT statement + compile(databaseid, params) { + var db = alasql.databases[databaseid]; + // Create variable for query + var query = new Query(); - query.explain = this.explain; // Explain - query.explaination = []; - query.explid = 1; - //console.log(this.modifier); - query.modifier = this.modifier; + // Array with columns to be removed + query.removeKeys = []; + query.aggrKeys = []; - query.database = db; - // 0. Precompile whereexists - this.compileWhereExists(query); + query.explain = this.explain; // Explain + query.explaination = []; + query.explid = 1; + //console.log(this.modifier); + query.modifier = this.modifier; - // 0. Precompile queries for IN, NOT IN, ANY and ALL operators - this.compileQueries(query); + query.database = db; + // 0. Precompile whereexists + this.compileWhereExists(query); - query.defcols = this.compileDefCols(query, databaseid); + // 0. Precompile queries for IN, NOT IN, ANY and ALL operators + this.compileQueries(query); - // 1. Compile FROM clause - query.fromfn = this.compileFrom(query); + query.defcols = this.compileDefCols(query, databaseid); - // 2. Compile JOIN clauses - if (this.joins) { - this.compileJoins(query); - } + // 1. Compile FROM clause + query.fromfn = this.compileFrom(query); - // todo?: 3. Compile SELECT clause + // 2. Compile JOIN clauses + if (this.joins) { + this.compileJoins(query); + } - // For ROWNUM() - query.rownums = []; + // todo?: 3. Compile SELECT clause + // For ROWNUM() + query.rownums = []; - this.compileSelectGroup0(query); + this.compileSelectGroup0(query); - if (this.group || query.selectGroup.length > 0) { - query.selectgfns = this.compileSelectGroup1(query); - } else { - query.selectfns = this.compileSelect1(query, params); - } + if (this.group || query.selectGroup.length > 0) { + query.selectgfns = this.compileSelectGroup1(query); + } else { + query.selectfns = this.compileSelect1(query, params); + } - // Remove columns clause - this.compileRemoveColumns(query); + // Remove columns clause + this.compileRemoveColumns(query); - // 5. Optimize WHERE and JOINS - if (this.where) { - this.compileWhereJoins(query); - } + // 5. Optimize WHERE and JOINS + if (this.where) { + this.compileWhereJoins(query); + } - // 4. Compile WHERE clause - query.wherefn = this.compileWhere(query); + // 4. Compile WHERE clause + query.wherefn = this.compileWhere(query); - // 6. Compile GROUP BY - if (this.group || query.selectGroup.length > 0) { - query.groupfn = this.compileGroup(query); - } + // 6. Compile GROUP BY + if (this.group || query.selectGroup.length > 0) { + query.groupfn = this.compileGroup(query); + } - // 6. Compile HAVING - if (this.having) { - query.havingfn = this.compileHaving(query); - } + // 6. Compile HAVING + if (this.having) { + query.havingfn = this.compileHaving(query); + } - // 8. Compile ORDER BY clause - if (this.order) { - query.orderfn = this.compileOrder(query, params); - } + // 8. Compile ORDER BY clause + if (this.order) { + query.orderfn = this.compileOrder(query, params); + } - if (this.group || query.selectGroup.length > 0) { - query.selectgfn = this.compileSelectGroup2(query); - } else { - query.selectfn = this.compileSelect2(query, params); - } + if (this.group || query.selectGroup.length > 0) { + query.selectgfn = this.compileSelectGroup2(query); + } else { + query.selectfn = this.compileSelect2(query, params); + } - // 7. Compile DISTINCT, LIMIT and OFFSET - query.distinct = this.distinct; + // 7. Compile DISTINCT, LIMIT and OFFSET + query.distinct = this.distinct; - // 9. Compile PIVOT clause - if (this.pivot) query.pivotfn = this.compilePivot(query); - if (this.unpivot) query.pivotfn = this.compileUnpivot(query); + // 9. Compile PIVOT clause + if (this.pivot) query.pivotfn = this.compilePivot(query); + if (this.unpivot) query.pivotfn = this.compileUnpivot(query); - // 10. Compile TOP/LIMIT/OFFSET/FETCH clause - if (this.top) { - query.limit = this.top.value; - } else if (this.limit) { - query.limit = this.limit.value; - if (this.offset) { - query.offset = this.offset.value; + // 10. Compile TOP/LIMIT/OFFSET/FETCH clause + if (this.top) { + query.limit = this.top.value; + } else if (this.limit) { + query.limit = this.limit.value; + if (this.offset) { + query.offset = this.offset.value; + } } - } - query.percent = this.percent; + query.percent = this.percent; - // 9. Compile ordering function for UNION and UNIONALL - query.corresponding = this.corresponding; // If CORRESPONDING flag exists - if (this.union) { - query.unionfn = this.union.compile(databaseid); - if (this.union.order) { - query.orderfn = this.union.compileOrder(query, params); - } else { - query.orderfn = null; - } - } else if (this.unionall) { - query.unionallfn = this.unionall.compile(databaseid); - if (this.unionall.order) { - query.orderfn = this.unionall.compileOrder(query, params); - } else { - query.orderfn = null; - } - } else if (this.except) { - query.exceptfn = this.except.compile(databaseid); - if (this.except.order) { - query.orderfn = this.except.compileOrder(query, params); - } else { - query.orderfn = null; - } - } else if (this.intersect) { - query.intersectfn = this.intersect.compile(databaseid); - if (this.intersect.order) { - query.intersectfn = this.intersect.compileOrder(query, params); - } else { - query.orderfn = null; + // 9. Compile ordering function for UNION and UNIONALL + query.corresponding = this.corresponding; // If CORRESPONDING flag exists + if (this.union) { + query.unionfn = this.union.compile(databaseid); + if (this.union.order) { + query.orderfn = this.union.compileOrder(query, params); + } else { + query.orderfn = null; + } + } else if (this.unionall) { + query.unionallfn = this.unionall.compile(databaseid); + if (this.unionall.order) { + query.orderfn = this.unionall.compileOrder(query, params); + } else { + query.orderfn = null; + } + } else if (this.except) { + query.exceptfn = this.except.compile(databaseid); + if (this.except.order) { + query.orderfn = this.except.compileOrder(query, params); + } else { + query.orderfn = null; + } + } else if (this.intersect) { + query.intersectfn = this.intersect.compile(databaseid); + if (this.intersect.order) { + query.intersectfn = this.intersect.compileOrder(query, params); + } else { + query.orderfn = null; + } } - } - // SELECT INTO - if (this.into) { - if (this.into instanceof yy.Table) { - // - // Save into the table in database - // - if ( - alasql.options.autocommit && - alasql.databases[this.into.databaseid || databaseid].engineid - ) { - // For external database when AUTOCOMMIT is ONs + // SELECT INTO + if (this.into) { + if (this.into instanceof yy.Table) { + // + // Save into the table in database + // + if ( + alasql.options.autocommit && + alasql.databases[this.into.databaseid || databaseid].engineid + ) { + // For external database when AUTOCOMMIT is ONs + query.intoallfns = + 'return alasql.engines["' + + alasql.databases[this.into.databaseid || databaseid].engineid + + '"]' + + '.intoTable("' + + (this.into.databaseid || databaseid) + + '","' + + this.into.tableid + + '",this.data, columns, cb);'; + } else { + // Into AlaSQL tables + query.intofns = + "alasql.databases['" + + (this.into.databaseid || databaseid) + + "'].tables" + + "['" + + this.into.tableid + + "'].data.push(r);"; + } + } else if (this.into instanceof yy.VarValue) { + // + // Save into local variable + // SELECT * INTO @VAR1 FROM ? + // query.intoallfns = - 'return alasql.engines["' + - alasql.databases[this.into.databaseid || databaseid].engineid + - '"]' + - '.intoTable("' + - (this.into.databaseid || databaseid) + - '","' + - this.into.tableid + - '",this.data, columns, cb);'; - } else { - // Into AlaSQL tables - query.intofns = - "alasql.databases['" + - (this.into.databaseid || databaseid) + - "'].tables" + - "['" + - this.into.tableid + - "'].data.push(r);"; - } - } else if (this.into instanceof yy.VarValue) { - // - // Save into local variable - // SELECT * INTO @VAR1 FROM ? - // - query.intoallfns = - 'alasql.vars["' + - this.into.variable + - '"]=this.data;res=this.data.length;if(cb)res=cb(res);return res;'; - } else if (this.into instanceof yy.FuncValue) { - // - // If this is INTO() function, then call it - // with one or two parameters - // - var qs = 'return alasql.into[' + JSON.stringify(this.into.funcid.toUpperCase()) + ']('; - if (this.into.args && this.into.args.length > 0) { - qs += this.into.args[0].toJS() + ','; - if (this.into.args.length > 1) { - qs += this.into.args[1].toJS() + ','; + 'alasql.vars["' + + this.into.variable + + '"]=this.data;res=this.data.length;if(cb)res=cb(res);return res;'; + } else if (this.into instanceof yy.FuncValue) { + // + // If this is INTO() function, then call it + // with one or two parameters + // + var qs = 'return alasql.into[' + JSON.stringify(this.into.funcid.toUpperCase()) + ']('; + if (this.into.args && this.into.args.length > 0) { + qs += this.into.args[0].toJS() + ','; + if (this.into.args.length > 1) { + qs += this.into.args[1].toJS() + ','; + } else { + qs += 'undefined,'; + } } else { - qs += 'undefined,'; + qs += 'undefined, undefined,'; } - } else { - qs += 'undefined, undefined,'; + query.intoallfns = qs + 'this.data,columns,cb)'; + //console.log('999'); + } else if (this.into instanceof yy.ParamValue) { + // + // Save data into parameters array + // like alasql('SELECT * INTO ? FROM ?',[outdata,srcdata]); + // + query.intofns = "params['" + this.into.param + "'].push(r)"; } - query.intoallfns = qs + 'this.data,columns,cb)'; - //console.log('999'); - } else if (this.into instanceof yy.ParamValue) { - // - // Save data into parameters array - // like alasql('SELECT * INTO ? FROM ?',[outdata,srcdata]); - // - query.intofns = "params['" + this.into.param + "'].push(r)"; - } - - if (query.intofns) { - // Create intofn function - // console.log(234234, query.intofns); - query.intofn = new Function('r,i,params,alasql', 'var y;' + query.intofns); - } else if (query.intoallfns) { - // Create intoallfn function - // console.log(23423234, query.intoallfns); - query.intoallfn = new Function('columns,cb,params,alasql', 'var y;' + query.intoallfns); - } - } - //console.log(query); - - // Now, compile all togeather into one function with query object in scope - var statement = function (params, cb, oldscope) { - query.params = params; - // Note the callback function has the data and error reversed due to existing code in promiseExec which has the - // err and data swapped. This trickles down into alasql.exec and further. Rather than risk breaking the whole thing, - // the (data, err) standard is maintained here. - var res1 = queryfn(query, oldscope, function (res, err) { - if (err) { - return cb(err, null); + + if (query.intofns) { + // Create intofn function + // console.log(234234, query.intofns); + query.intofn = new Function('r,i,params,alasql', 'var y;' + query.intofns); + } else if (query.intoallfns) { + // Create intoallfn function + // console.log(23423234, query.intoallfns); + query.intoallfn = new Function('columns,cb,params,alasql', 'var y;' + query.intoallfns); } - if (query.rownums.length > 0) { - for (var i = 0, ilen = res.length; i < ilen; i++) { - for (var j = 0, jlen = query.rownums.length; j < jlen; j++) { - res[i][query.rownums[j]] = i + 1; + } + //console.log(query); + // Now, compile all togeather into one function with query object in scope + var statement = function (params, cb, oldscope) { + query.params = params; + // Note the callback function has the data and error reversed due to existing code in promiseExec which has the + // err and data swapped. This trickles down into alasql.exec and further. Rather than risk breaking the whole thing, + // the (data, err) standard is maintained here. + var res1 = queryfn(query, oldscope, function (res, err) { + if (err) { + return cb(err, null); + } + if (query.rownums.length > 0) { + for (var i = 0, ilen = res.length; i < ilen; i++) { + for (var j = 0, jlen = query.rownums.length; j < jlen; j++) { + res[i][query.rownums[j]] = i + 1; + } } } - } - var res2 = modify(query, res); + var res2 = modify(query, res); - if (cb) { - cb(res2); - } - //console.log(8888,res2); - return res2; - }); - //console.log(9999,res1); + if (cb) { + cb(res2); + } + //console.log(8888,res2); + return res2; + }); + //console.log(9999,res1); + // if(typeof res1 != 'undefined') res1 = modify(query,res1); + return res1; + }; + + // statement.dbversion = ; + // console.log(statement.query); + //console.log(202,statement); + statement.query = query; + return statement; + } + + execute(databaseid, params, cb) { + return this.compile(databaseid)(params, cb); + // throw new Error('Insert statement is should be compiled') + } - // if(typeof res1 != 'undefined') res1 = modify(query,res1); + compileWhereExists(query) { + if (!this.exists) return; + query.existsfn = this.exists.map(function (ex) { + var nq = ex.compile(query.database.databaseid); + // console.log(nq); + // if(!nq.query.modifier) nq.query.modifier = 'RECORDSET'; + nq.query.modifier = 'RECORDSET'; + return nq; + }); + } - return res1; - }; + compileQueries(query) { + if (!this.queries) return; + query.queriesfn = this.queries.map(function (q) { + var nq = q.compile(query.database.databaseid); + // console.log(nq); + // if(!nq.query) nq.query = {}; + nq.query.modifier = 'RECORDSET'; + // if(!nq.query.modifier) nq.query.modifier = 'RECORDSET'; + return nq; + }); + } - // statement.dbversion = ; - // console.log(statement.query); - //console.log(202,statement); - statement.query = query; - return statement; + // exec(databaseid) { + // throw new Error('Select statement should be precompiled'); + // } }; /** @@ -530,12 +559,3 @@ function modify(query, res) { } return res; } - -// yy.Select.prototype.exec = function(databaseid) { -// throw new Error('Select statement should be precompiled'); - -// }; -yy.Select.prototype.execute = function (databaseid, params, cb) { - return this.compile(databaseid)(params, cb); - // throw new Error('Insert statement is should be compiled') -}; diff --git a/src/41exists.js b/src/41exists.js index 1d465df290..62d3d544ee 100755 --- a/src/41exists.js +++ b/src/41exists.js @@ -6,44 +6,23 @@ // */ -yy.ExistsValue = function (params) { - return Object.assign(this, params); -}; -yy.ExistsValue.prototype.toString = function () { - return 'EXISTS(' + this.value.toString() + ')'; -}; - -yy.ExistsValue.prototype.toType = function () { - return 'boolean'; -}; - -yy.ExistsValue.prototype.toJS = function (context, tableid, defcols) { - // return 'ww=this.existsfn['+this.existsidx+'](params,null,p),console.log(ww),ww.length'; +yy.ExistsValue = class ExistsValue { + constructor(params) { + Object.assign(this, params); + } - return 'this.existsfn[' + this.existsidx + '](params,null,' + context + ').data.length'; -}; + toString() { + return 'EXISTS(' + this.value.toString() + ')'; + } -yy.Select.prototype.compileWhereExists = function (query) { - if (!this.exists) return; - query.existsfn = this.exists.map(function (ex) { - var nq = ex.compile(query.database.databaseid); - // console.log(nq); - // if(!nq.query.modifier) nq.query.modifier = 'RECORDSET'; - nq.query.modifier = 'RECORDSET'; - return nq; - }); -}; + toType() { + return 'boolean'; + } -yy.Select.prototype.compileQueries = function (query) { - if (!this.queries) return; - query.queriesfn = this.queries.map(function (q) { - var nq = q.compile(query.database.databaseid); - // console.log(nq); - // if(!nq.query) nq.query = {}; - nq.query.modifier = 'RECORDSET'; - // if(!nq.query.modifier) nq.query.modifier = 'RECORDSET'; - return nq; - }); + toJS(context, tableid, defcols) { + // return 'ww=this.existsfn['+this.existsidx+'](params,null,p),console.log(ww),ww.length'; + return 'this.existsfn[' + this.existsidx + '](params,null,' + context + ').data.length'; + } }; //