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
JavaScript
/*
//
// 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;
};