alasql
Version:
AlaSQL.js - JavaScript SQL database library for relational and graph data manipulation with support of localStorage, IndexedDB, and Excel
409 lines (350 loc) • 11.5 kB
JavaScript
/*
//
// Select run-time part for Alasql.js
// Date: 03.11.2014
// (c) 2014, Andrey Gershun
//
*/
//
// Main part of SELECT procedure
//
yy.Select = function (params) { return yy.extend(this, params); }
yy.Select.prototype.toString = function() {
var s = '';
if(this.explain) s+= K('EXPLAIN')+' ';
s += K('SELECT')+' ';
if(this.modifier) s += K(this.modifier)+' ';
if(this.top) {
s += K('TOP')+' '+N(this.top.value)+' ';
if(this.percent) s += K('PERCENT')+' ';
}
s += this.columns.map(function(col){
var s = col.toString();
// console.log(col);
if(typeof col.as != "undefined") s += ' '+K('AS')+' '+L(col.as);
return s;
}).join(', ');
if(this.from) {
s += NL()+ID()+K('FROM')+' '+this.from.map(function(f){
// console.log(f);
var ss = f.toString();
if(f.as) ss += ' '+K('AS')+' '+f.as;
return ss;
}).join(',');
};
if(this.joins) {
s += this.joins.map(function(jn){
var ss = NL()+ID();
if(jn.joinmode) ss += K(jn.joinmode)+' ';
if(jn.table) ss += K('JOIN')+' '+jn.table.toString();
else if(jn instanceof yy.Apply) ss += jn.toString();
else {
throw new Error('Wrong type in JOIN mode');
}
if(jn.using) ss += ' '+K('USING')+' '+jn.using.toString();
if(jn.on) ss += ' '+K('ON')+' '+jn.on.toString();
return ss;
});
}
if(this.where) s += NL()+ID()+K('WHERE')+' '+this.where.toString();
if(this.group && this.group.length>0) {
s += NL()+ID()+K('GROUP BY')+' '+this.group.map(function(grp){
return grp.toString();
}).join(', ');
};
if(this.having) s += NL()+ID()+K('HAVING')+' '+this.having.toString();
if(this.order && this.order.length>0) {
s += NL()+ID()+K('ORDER BY')+' '+this.order.map(function(ord){
return ord.toString();
}).join(', ');
};
if(this.limit) s += NL()+ID()+K('LIMIT')+' '+this.limit.value;
if(this.offset) s += NL()+ID()+K('OFFSET')+' '+this.offset.value;
if(this.union) s += NL()+K('UNION')+(this.corresponding?(' '+K('CORRESPONDING')):'')+NL()+this.union.toString();
if(this.unionall) s += NL()+K('UNION ALL')+(this.corresponding?(' '+K('CORRESPONDING')):'')+NL()+this.unionall.toString();
if(this.except) s += NL()+K('EXCEPT')+(this.corresponding?(' '+K('CORRESPONDING')):'')+NL()+this.except.toString();
if(this.intersect) s += NL()+K('INTERSECT')+(this.corresponding?(' '+K('CORRESPONDING')):'')+NL()+this.intersect.toString();
return s;
};
/**
Select statement in expression
*/
yy.Select.prototype.toJavaScript = function(context, tableid, defcols) {
// console.log('Expression',this);
// if(this.expression.reduced) return 'true';
// return this.expression.toJavaScript(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]';
// s = '(console.log(this.queriesfn[0]),'+s+')';
return s;
};
// Compile SELECT statement
yy.Select.prototype.compile = function(databaseid) {
var db = alasql.databases[databaseid];
// Create variable for query
var query = new Query();
// Array with columns to be removed
query.removeKeys = [];
query.explain = this.explain; // Explain
query.explaination = [];
query.explid = 1;
query.modifier = this.modifier;
query.database = db;
// 0. Precompile whereexists
this.compileWhereExists(query);
// 0. Precompile queries for IN, NOT IN, ANY and ALL operators
this.compileQueries(query);
query.defcols = this.compileDefCols(query, databaseid);
// 1. Compile FROM clause
query.fromfn = this.compileFrom(query);
// 2. Compile JOIN clauses
if(this.joins) this.compileJoins(query);
// 3. Compile SELECT clause
this.compileSelectGroup0(query);
if(this.group || query.selectGroup.length>0) {
query.selectgfns = this.compileSelectGroup1(query);
} else {
query.selectfns = this.compileSelect1(query);
}
// Remove columns clause
this.compileRemoveColumns(query);
// 5. Optimize WHERE and JOINS
if(this.where) this.compileWhereJoins(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 HAVING
if(this.having) query.havingfn = this.compileHaving(query);
if(this.group || query.selectGroup.length>0) {
query.selectgfn = this.compileSelectGroup2(query);
} else {
query.selectfn = this.compileSelect2(query);
}
// 7. Compile DISTINCT, LIMIT and OFFSET
query.distinct = this.distinct;
// 8. Compile ORDER BY clause
if(this.order) query.orderfn = this.compileOrder(query);
// TOP
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;
// 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);
} else {
query.orderfn = null;
}
} else if(this.unionall) {
query.unionallfn = this.unionall.compile(databaseid);
if(this.unionall.order) {
query.orderfn = this.unionall.compileOrder(query);
} else {
query.orderfn = null;
}
} else if(this.except) {
query.exceptfn = this.except.compile(databaseid);
if(this.except.order) {
query.orderfn = this.except.compileOrder(query);
} else {
query.orderfn = null;
}
} else if(this.intersect) {
query.intersectfn = this.intersect.compile(databaseid);
if(this.intersect.order) {
query.intersectfn = this.intersect.compileOrder(query);
} 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
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[\''+this.into.funcid.toUpperCase()+'\'](';
if(this.into.args && this.into.args.length>0 ) {
qs += this.into.args[0].toJavaScript()+',';
if(this.into.args.length > 1) {
qs += this.into.args[1].toJavaScript()+',';
} else {
qs += '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)";
};
if(query.intofns) {
// Create intofn function
query.intofn = new Function("r,i,params,alasql",query.intofns);
} else if(query.intoallfns) {
// Create intoallfn function
query.intoallfn = new Function("columns,cb,params,alasql",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;
var res1 = queryfn(query,oldscope,function(res){
//console.log(res[0].schoolid);
//console.log(184,res);
var res2 = modify(query, res);
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;
};
function modify(query, res) {
var modifier = query.modifier || alasql.options.modifier;
var columns = query.columns;
if(typeof columns == 'undefined' || columns.length == 0) {
// Try to create columns
if(res.length > 0) {
var allcol = {};
for(var i=0;i<Math.min(res.length,alasql.options.columnlookup||10);i++) {
for(var key in res[i]) {
allcol[key] = true;
}
}
columns = Object.keys(allcol).map(function(columnid){
return {columnid:columnid};
});
} else {
// Cannot recognize columns
columns = [];
}
}
// console.log(columns);
if(modifier == 'VALUE') {
// console.log(222,res);
if(res.length > 0) {
var key;
if(columns && columns.length > 0) key = columns[0].columnid;
else key = Object.keys(res[0])[0];
res = res[0][key];
} else {
res = undefined;
}
} if(modifier == 'ROW') {
if(res.length > 0) {
var key;
var a = [];
for(var key in res[0]) {
a.push(res[0][key]);
};
res = a;
} else {
res = undefined;
}
} if(modifier == 'COLUMN') {
var ar = [];
if(res.length > 0) {
var key;
if(columns && columns.length > 0) key = columns[0].columnid;
else key = Object.keys(res[0])[0];
for(var i=0, ilen=res.length; i<ilen; i++){
ar.push(res[i][key]);
}
};
res = ar;
} if(modifier == 'MATRIX') {
// Returns square matrix of rows
var ar = [];
for(var i=0;i<res.length;i++) {
var a = [];
var r = res[i];
for(var j=0;j<columns.length;j++) {
a.push(r[columns[j].columnid]);
}
ar.push(a);
}
res = ar;
} if(modifier == 'INDEX') {
var ar = {};
var key,val;
if(columns && columns.length > 0) {
key = columns[0].columnid;
val = columns[1].columnid;
} else {
var okeys = Object.keys(res[0]);
key = okeys[0];
val = okeys[1];
}
for(var i=0, ilen=res.length; i<ilen; i++){
ar[res[i][key]] = res[i][val];
}
res = ar;
// res = arrayOfArrays(res);
} if(modifier == 'RECORDSET') {
res = new alasql.Recordset({data:res, columns:columns});
// res = arrayOfArrays(res);
} if(modifier == 'TEXTSTRING') {
var key;
if(columns && columns.length > 0) key = columns[0].columnid;
else key = Object.keys(res[0])[0];
var s = '';
for(var i=0, ilen=res.length; i<ilen; i++){
res[i] = res[i][key];
}
res = res.join('\n');
// res = arrayOfArrays(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')
}