UNPKG

alasql

Version:

AlaSQL.js - JavaScript SQL database library for relational and graph data manipulation with support of localStorage, IndexedDB, and Excel

457 lines (409 loc) 13.2 kB
/* // // CREATE TABLE for Alasql.js // Date: 03.11.2014 // (c) 2014, Andrey Gershun // */ yy.ColumnDef = function (params) { return yy.extend(this, params); } yy.ColumnDef.prototype.toString = function() { var s = this.columnid; if(this.dbtypeid) s += ' '+this.dbtypeid; if(this.dbsize) { s += '('+this.dbsize; if(this.dbprecision) s += ','+this.dbprecision; s += ')'; }; if(this.primarykey) s += ' PRIMARY KEY'; if(this.notnull) s += ' NOT NULL'; return s; } yy.CreateTable = function (params) { return yy.extend(this, params); } yy.CreateTable.prototype.toString = function() { var s = K('CREATE'); if(this.temporary) s+=' '+K('TEMPORARY'); if(this.view) s += ' '+K('VIEW'); else s += ' '+(this.class?K('CLASS'):K('TABLE')); if(this.ifnotexists) s += ' '+K('IF')+' '+K('NOT')+' '+K('EXISTS'); s += ' '+this.table.toString(); if(this.viewcolumns) { s += '('+this.viewcolumns.map(function(vcol){ return vcol.toString(); }).join(',')+')'; } if(this.as) s += ' '+K('AS')+' '+L(this.as); else { var ss = this.columns.map(function(col){ return col.toString(); }); s += ' ('+NL()+ID()+ss.join(','+NL()+ID())+')'; }; if(this.view && this.select) { s += ' AS '+this.select.toString(); } return s; } // CREATE TABLE //yy.CreateTable.prototype.compile = returnUndefined; yy.CreateTable.prototype.execute = function (databaseid, params, cb) { // var self = this; var db = alasql.databases[this.table.databaseid || databaseid]; var tableid = this.table.tableid; if(!tableid) { throw new Error('Table name is not defined'); } // var ifnotexists = this.ifnotexists; var columns = this.columns; // if(false) { // if(!columns) { // throw new Error('Columns are not defined'); // } // } var constraints = this.constraints||[]; // console.log(this); // IF NOT EXISTS if(this.ifnotexists && db.tables[tableid]) return 0; if(db.tables[tableid]) { throw new Error('Can not create table \''+tableid +'\', because it already exists in the database \''+db.databaseid+'\''); } var table = db.tables[tableid] = new alasql.Table(); // TODO Can use special object? // If this is a class if(this.class) { table.isclass = true; } table.identities = {}; table.checkfn = []; var ss = []; if(this.columns) { this.columns.forEach(function(col) { var dbtypeid = col.dbtypeid; if(!alasql.fn[dbtypeid]) dbtypeid = dbtypeid.toUpperCase(); var newcol = { columnid: col.columnid, dbtypeid: dbtypeid, dbsize: col.dbsize, // Fixed issue #150 dbprecision: col.dbprecision, // Fixed issue #150 notnull: col.notnull, identity: col.identity }; if(col.identity) { table.identities[col.columnid]={value:+col.identity.value,step:+col.identity.step}; // ss.push('\''+col.columnid+'\':(alasql.databases[\''+db.databaseid+'\'].tables[\'' // +tableid+'\'].identities[\''+col.columnid+'\'].value)'); } if(col.check) { table.checkfn.push(new Function("r",'return '+col.check.expression.toJavaScript('r',''))); } if(col.default) { ss.push('\''+col.columnid+'\':'+col.default.toJavaScript('r','')); } // Check for primary key if(col.primarykey) { var pk = table.pk = {}; pk.columns = [col.columnid]; pk.onrightfns = 'r[\''+col.columnid+'\']'; pk.onrightfn = new Function("r",'return '+pk.onrightfns); pk.hh = hash(pk.onrightfns); table.uniqs[pk.hh] = {}; }; // UNIQUE clause if(col.unique) { var uk = {}; if(typeof table.uk == 'undefined') table.uk = []; table.uk.push(uk); uk.columns = [col.columnid]; uk.onrightfns = 'r[\''+col.columnid+'\']'; uk.onrightfn = new Function("r",'return '+uk.onrightfns); uk.hh = hash(uk.onrightfns); table.uniqs[uk.hh] = {}; }; // UNIQUE clause if(col.foreignkey) { // console.log(138,col.foreignkey); var fk = col.foreignkey.table; var fktable = alasql.databases[fk.databaseid||alasql.useid].tables[fk.tableid]; if(typeof fk.columnid == 'undefined') { if(fktable.pk.columns && fktable.pk.columns.length >0 ){ fk.columnid = fktable.pk.columns[0]; } else { throw new Error('FOREIGN KEY allowed only to tables with PRIMARY KEYs'); } } // console.log(fktable.pk); var fkfn = function(r) { var rr = {}; if(typeof r[col.columnid] == 'undefined') return true; rr[fk.columnid] = r[col.columnid]; var addr = fktable.pk.onrightfn(rr); // console.log(r, rr, addr); // console.log(fktable.uniqs[fktable.pk.hh][addr]); if(!fktable.uniqs[fktable.pk.hh][addr]) { throw new Error('Foreign key "'+r[col.columnid]+'" is not found in table '+fktable.tableid); } return true; }; table.checkfn.push(fkfn); /* var uk = {}; if(typeof table.uk == 'undefined') table.uk = []; table.uk.push(uk); uk.columns = [col.columnid]; uk.onrightfns = 'r[\''+col.columnid+'\']'; uk.onrightfn = new Function("r",'return '+uk.onrightfns); uk.hh = hash(uk.onrightfns); table.uniqs[uk.hh] = {}; */ }; table.columns.push(newcol); table.xcolumns[newcol.columnid] = newcol; }); }; table.defaultfns = ss.join(','); // if(constraints) { constraints.forEach(function(con) { //console.log(con, con.columns); if(con.type == 'PRIMARY KEY') { if(table.pk) { throw new Error('Primary key already exists'); } var pk = table.pk = {}; pk.columns = con.columns; pk.onrightfns = pk.columns.map(function(columnid){ return 'r[\''+columnid+'\']' }).join("+'`'+"); pk.onrightfn = new Function("r",'return '+pk.onrightfns); pk.hh = hash(pk.onrightfns); table.uniqs[pk.hh] = {}; } else if(con.type == 'CHECK') { // console.log(con.expression.toJavaScript('r','')); table.checkfn.push(new Function("r",'return '+con.expression.toJavaScript('r',''))); } else if(con.type == 'UNIQUE') { // console.log(con); var uk = {}; if(!table.uk) table.uk = []; table.uk.push(uk); uk.columns = con.columns; uk.onrightfns = uk.columns.map(function(columnid){ return 'r[\''+columnid+'\']' }).join("+'`'+"); uk.onrightfn = new Function("r",'return '+uk.onrightfns); uk.hh = hash(uk.onrightfns); table.uniqs[uk.hh] = {}; } else if(con.type == 'FOREIGN KEY') { // console.log(con); var col = table.xcolumns[con.columns[0]]; var fk = con.fktable; if(con.fkcolumns && con.fkcolumns.length>0) fk.columnid = con.fkcolumns[0]; var fktable = alasql.databases[fk.databaseid||alasql.useid].tables[fk.tableid]; if(typeof fk.columnid == 'undefined') { fk.columnid = fktable.pk.columns[0]; } // console.log(fktable.pk); var fkfn = function(r) { var rr = {}; if(typeof r[col.columnid] == 'undefined') return true; rr[fk.columnid] = r[col.columnid]; var addr = fktable.pk.onrightfn(rr); // console.log(r, rr, addr); // console.log(fktable.uniqs[fktable.pk.hh][addr]); if(!fktable.uniqs[fktable.pk.hh][addr]) { //console.log(228,table,col,fk); throw new Error('Foreign key "'+r[col.columnid]+'" is not found in table '+fktable.tableid); } return true; }; table.checkfn.push(fkfn); } }); if(this.view && this.viewcolumns) { var self = this; this.viewcolumns.forEach(function(vcol,idx){ self.select.columns[idx].as = vcol.columnid; }); } // console.log(100,db.engineid); if(db.engineid) { // console.log(101,db.engineid); return alasql.engines[db.engineid].createTable(this.table.databaseid || databaseid, tableid, this.ifnotexists, cb); // console.log('createtable',res1); // return res1; } // } // if(table.pk) { table.insert = function(r) { var table = this; // IDENTINY or AUTO_INCREMENT // if(table.identities && table.identities.length>0) { // table.identities.forEach(function(ident){ // r[ident.columnid] = ident.value; // }); // } //console.log(262,r); //console.log(263,table.identities) for(var columnid in table.identities){ var ident = table.identities[columnid]; // console.log(ident); r[columnid] = ident.value; // console.log(ident); }; //console.log(270,r); if(table.checkfn && table.checkfn.length>0) { table.checkfn.forEach(function(checkfn){ if(!checkfn(r)) { throw new Error('Violation of CHECK constraint'); }; }); }; table.columns.forEach(function(column){ if(column.notnull && typeof r[column.columnid] == 'undefined') { throw new Error('Wrong NULL value in NOT NULL column '+column.columnid); } }); if(table.pk) { var pk = table.pk; var addr = pk.onrightfn(r); if(typeof table.uniqs[pk.hh][addr] != 'undefined') { throw new Error('Cannot insert record, because it already exists in primary key index'); } // table.uniqs[pk.hh][addr]=r; } if(table.uk && table.uk.length) { table.uk.forEach(function(uk){ var ukaddr = uk.onrightfn(r); if(typeof table.uniqs[uk.hh][ukaddr] != 'undefined') { throw new Error('Cannot insert record, because it already exists in primary key'); } // table.uniqs[uk.hh][ukaddr]=r; }); }; // Final change before insert table.data.push(r); // Update indices for(var columnid in table.identities){ var ident = table.identities[columnid]; // console.log(ident); ident.value += ident.step; // console.log(ident); }; if(table.pk) { var pk = table.pk; var addr = pk.onrightfn(r); table.uniqs[pk.hh][addr]=r; } if(table.uk && table.uk.length) { table.uk.forEach(function(uk){ var ukaddr = uk.onrightfn(r); table.uniqs[uk.hh][ukaddr]=r; }); }; }; table.delete = function(i,params,alasql) { var table = this; var r = this.data[i]; if(this.pk) { var pk = this.pk; var addr = pk.onrightfn(r); if(typeof this.uniqs[pk.hh][addr] == 'undefined') { throw new Error('Something wrong with primary key index on table'); } else { this.uniqs[pk.hh][addr]=undefined; }; } if(table.uk && table.uk.length) { table.uk.forEach(function(uk){ var ukaddr = uk.onrightfn(r); if(typeof table.uniqs[uk.hh][ukaddr] == 'undefined') { throw new Error('Something wrong with unique index on table'); } table.uniqs[uk.hh][ukaddr]=undefined; }); } }; table.deleteall = function() { this.data.length = 0; if(this.pk) { // var r = this.data[i]; this.uniqs[this.pk.hh] = {}; } if(table.uk && table.uk.length) { table.uk.forEach(function(uk){ table.uniqs[uk.hh]={}; }); } }; table.update = function(assignfn, i, params) { // TODO: Analyze the speed var r = cloneDeep(this.data[i]); // PART 1 - PRECHECK if(this.pk) { var pk = this.pk; pk.pkaddr = pk.onrightfn(r,params); if(typeof this.uniqs[pk.hh][pk.pkaddr] == 'undefined') { throw new Error('Something wrong with index on table'); } else { } } if(table.uk && table.uk.length) { table.uk.forEach(function(uk){ uk.ukaddr = uk.onrightfn(r); if(typeof table.uniqs[uk.hh][uk.ukaddr] == 'undefined') { throw new Error('Something wrong with unique index on table'); } }); } assignfn(r,params,alasql); // PART 2 - POST CHECK if(table.checkfn && table.checkfn.length>0) { table.checkfn.forEach(function(checkfn){ if(!checkfn(r)) { throw new Error('Violation of CHECK constraint'); }; }); }; table.columns.forEach(function(column){ if(column.notnull && typeof r[column.columnid] == 'undefined') { throw new Error('Wrong NULL value in NOT NULL column '+column.columnid); } }); if(this.pk) { pk.newpkaddr = pk.onrightfn(r); if(typeof this.uniqs[pk.hh][pk.newpkaddr] != 'undefined' && pk.newpkaddr != pk.pkaddr) { throw new Error('Record already exists'); } else { } }; if(table.uk && table.uk.length) { table.uk.forEach(function(uk){ uk.newukaddr = uk.onrightfn(r); if(typeof table.uniqs[uk.hh][uk.newukaddr] != 'undefined' && uk.newukaddr != uk.ukaddr) { throw new Error('Record already exists'); } }); } // PART 3 UPDATE if(this.pk) { this.uniqs[pk.hh][pk.pkaddr]=undefined; this.uniqs[pk.hh][pk.newpkaddr] = r; } if(table.uk && table.uk.length) { table.uk.forEach(function(uk){ table.uniqs[uk.hh][uk.ukaddr]=undefined; table.uniqs[uk.hh][uk.newukaddr]=r; }); } this.data[i] = r; }; if(this.view && this.select) { table.view = true; // console.log(this.select.toString()); // console.log('this.table.databaseid',this.table.databaseid); // console.log(this.select.compile(this.table.databaseid||databaseid)); table.select = this.select.compile(this.table.databaseid||databaseid); } // console.log(databaseid); // console.log(db.databaseid,db.tables); // console.log(table); if(cb) cb(1); return 1; };