787 lines
No EOL
34 KiB
TypeScript
787 lines
No EOL
34 KiB
TypeScript
import { Attribute, DB, Table } from "./db"
|
|
import { Aggregation, checkConstraint, joinCross, Datatype, foreignConstraint, Modifier, joinNatural, onJoin, uniqueConstraint, usingJoin } from "./dbStructure"
|
|
import { insertQuery, Query, QueryBuilder, removeQuery, selectQuery, updateQuery } from "./query"
|
|
import { allModifierInput, joinType, onAction, primaryData } from "./types"
|
|
|
|
export class Handler {
|
|
async syncDB(db: DB, handler: Handler, deleteInDB: boolean = false) {
|
|
let gd = new QueryBuilder();
|
|
gd.addCode(`SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where CONSTRAINT_SCHEMA = `);
|
|
gd.addInjection(db.name);
|
|
gd.addCode(` or TABLE_SCHEMA = `);
|
|
gd.addInjection(db.name);
|
|
gd.addCode(`;select * from information_schema.table_constraints where CONSTRAINT_SCHEMA = `);
|
|
gd.addInjection(db.name)
|
|
gd.addCode(` or TABLE_SCHEMA = `);
|
|
gd.addInjection(db.name);
|
|
|
|
let [key, constraints] = await db.query(handler.builders.query(gd));
|
|
|
|
//Table List
|
|
let allTables = (await db.query(handler.builders.query(handler.querys.listTables(handler, db)))).map((d: any) => d.TABLE_NAME);
|
|
|
|
// gether Table Schematics
|
|
let tableData: { [key: string]: any; } = {};
|
|
for (let i = 0; i < db.tables.length; i++) {
|
|
const table = db.tables[i];
|
|
try {
|
|
const tableDataBuilder = new QueryBuilder();
|
|
tableDataBuilder.addCode("DESCRIBE ");
|
|
tableDataBuilder.addCode(table.serialize(handler));
|
|
tableData[table.dbLangTableName.toLowerCase()] =
|
|
Object.fromEntries((await db.query(handler.builders.query(tableDataBuilder))).map((d: any) => [d.Field.toLowerCase(), d]));
|
|
} catch (_) {
|
|
tableData[table.dbLangTableName.toLowerCase()] = null;
|
|
}
|
|
}
|
|
|
|
const del = new QueryBuilder();
|
|
const create = new QueryBuilder();
|
|
|
|
function checkUniqueIsVaild(n: string, table: string) {
|
|
let t = db.getTable(table);
|
|
if (t == null) return false;
|
|
for (let i = 0; i < t.dbLangConstrains.length; i++) {
|
|
const c = t.dbLangConstrains[i];
|
|
if (c.name == n.toLowerCase() && c instanceof uniqueConstraint) {
|
|
let attrs = c.attrs.map(a => a.name);
|
|
let found = 0;
|
|
for (let j = 0; j < key.length; j++) {
|
|
if (
|
|
key[j].CONSTRAINT_NAME == n
|
|
&& key[j].TABLE_NAME == table
|
|
) {
|
|
if (attrs.includes(key[j].COLUMN_NAME.toLowerCase())) found++;
|
|
else return false;
|
|
}
|
|
}
|
|
return found == attrs.length;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function checkForeignIsVaild(n: string, table: string) {
|
|
let t = db.getTable(table);
|
|
if (t == null) return false;
|
|
for (let i = 0; i < t.dbLangConstrains.length; i++) {
|
|
const c = t.dbLangConstrains[i];
|
|
if (c.name == n.toLowerCase() && c instanceof foreignConstraint) {
|
|
let fromAttrs = c.fromAttrs.map(a => a.name);
|
|
let toAttrs = c.toAttrs.map(a => a.name);
|
|
let refTable = c.toAttrs[0].table.dbLangTableName;
|
|
let found = 0;
|
|
for (let j = 0; j < key.length; j++) {
|
|
if (
|
|
key[j].CONSTRAINT_NAME == n
|
|
&& key[j].TABLE_NAME == table
|
|
&& key[j].REFERENCED_TABLE_NAME == refTable
|
|
) {
|
|
let inF = fromAttrs.indexOf(key[j].COLUMN_NAME.toLowerCase());
|
|
let inT = toAttrs.indexOf(key[j].REFERENCED_COLUMN_NAME.toLowerCase());
|
|
if (inF != -1 && inT == inF) found++;
|
|
else return false;
|
|
}
|
|
}
|
|
return found == fromAttrs.length && found == toAttrs.length;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function checkUniqueExists(tabel: Table, c: uniqueConstraint) {
|
|
let attrs = c.attrs.map(a => a.name);
|
|
let found = 0;
|
|
for (let j = 0; j < key.length; j++) {
|
|
if (
|
|
key[j].CONSTRAINT_NAME.toLowerCase() == c.name
|
|
&& key[j].TABLE_NAME == tabel.dbLangTableName
|
|
) {
|
|
if (attrs.includes(key[j].COLUMN_NAME.toLowerCase())) found++;
|
|
else return false;
|
|
}
|
|
}
|
|
return found == attrs.length;
|
|
}
|
|
function checkForeignExists(tabel: Table, c: foreignConstraint) {
|
|
let fromAttrs = c.fromAttrs.map(a => a.name);
|
|
let toAttrs = c.toAttrs.map(a => a.name);
|
|
let refTable = c.toAttrs[0].table.dbLangTableName;
|
|
let found = 0;
|
|
for (let j = 0; j < key.length; j++) {
|
|
if (
|
|
key[j].CONSTRAINT_NAME == c.name
|
|
&& key[j].TABLE_NAME == tabel.dbLangTableName
|
|
&& key[j].REFERENCED_TABLE_NAME == refTable
|
|
) {
|
|
let inF = fromAttrs.indexOf(key[j].COLUMN_NAME.toLowerCase());
|
|
let inT = toAttrs.indexOf(key[j].REFERENCED_COLUMN_NAME.toLowerCase());
|
|
if (inF != -1 && inT == inF) found++;
|
|
else return false;
|
|
}
|
|
}
|
|
return found == fromAttrs.length && found == toAttrs.length;
|
|
}
|
|
|
|
//delete check and unused/false(unique, foreign) constraints
|
|
for (let i = 0; i < constraints.length; i++) {
|
|
const c = constraints[i];
|
|
if (c.CONSTRAINT_TYPE == "CHECK") {
|
|
del.appendEnding(handler.querys.removeCheck(handler, c.TABLE_NAME, c.CONSTRAINT_NAME));
|
|
} else if (c.CONSTRAINT_TYPE == "UNIQUE") {
|
|
if (
|
|
(typeof tableData[c.TABLE_NAME.toLowerCase()] == "undefined" && deleteInDB)
|
|
|| !checkUniqueIsVaild(c.CONSTRAINT_NAME, c.TABLE_NAME)
|
|
) del.appendEnding(handler.querys.removeUnique(handler, c.TABLE_NAME, c.CONSTRAINT_NAME));
|
|
} else if (c.CONSTRAINT_TYPE == "FOREIGN KEY") {
|
|
if (
|
|
(typeof tableData[c.TABLE_NAME.toLowerCase()] == "undefined" && deleteInDB)
|
|
|| !checkForeignIsVaild(c.CONSTRAINT_NAME, c.TABLE_NAME)
|
|
) del.appendEnding(handler.querys.removeForeignKey(handler, c.TABLE_NAME, c.CONSTRAINT_NAME));
|
|
}
|
|
}
|
|
//delete unused Tables
|
|
if (deleteInDB) for (let i = 0; i < allTables.length; i++) {
|
|
if (typeof tableData[allTables[i].toLowerCase()] == "undefined") del.appendEnding(handler.querys.deleteTable(handler, allTables[i]));
|
|
}
|
|
|
|
function freeForUpdate(attr: string, table: string) {
|
|
for (let i = 0; i < key.length; i++) {
|
|
const k = key[i];
|
|
if (
|
|
k.REFERENCED_TABLE_NAME == table
|
|
&& k.REFERENCED_COLUMN_NAME.toLowerCase() == attr.toLowerCase()
|
|
) {
|
|
del.appendEnding(handler.querys.removeForeignKey(handler, k.TABLE_NAME, k.CONSTRAINT_NAME));
|
|
}
|
|
}
|
|
}
|
|
|
|
//create tables
|
|
for (let i = 0; i < db.tables.length; i++) {
|
|
const table = db.tables[i];
|
|
const tableD = tableData[table.dbLangTableName.toLowerCase()];
|
|
//delete unused Columns
|
|
if (deleteInDB && tableD != null) {
|
|
let keys = Object.keys(tableD);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
if (table.dbLangTableAttributes[keys[i]] == null) {
|
|
freeForUpdate(keys[i].toLowerCase(), table.dbLangTableName);
|
|
create.appendEnding(handler.querys.removeColumn(handler, table, keys[i]));
|
|
}
|
|
}
|
|
}
|
|
//add/mofify columns
|
|
let changePrimary = false;
|
|
if (tableD == null) {
|
|
create.appendEnding(handler.querys.create(handler, table));
|
|
changePrimary = true;
|
|
} else {
|
|
let keys = Object.keys(table.dbLangTableAttributes);
|
|
for (let j = 0; j < keys.length; j++) {
|
|
const a = table.dbLangTableAttributes[keys[j]];
|
|
const attrData = tableD[keys[j]];
|
|
if (attrData == null) {
|
|
create.appendEnding(handler.querys.addColumn(handler, a));
|
|
changePrimary = true;
|
|
} else if (
|
|
!handler.builders.compareDatatypes(handler, a.type, attrData.Type) ||
|
|
a.ops.default != attrData.Default ||
|
|
(!!a.ops.notNull || !!a.ops.autoIncrement || !!a.ops.primaryKey) != (attrData.Null == "NO") ||
|
|
(!!a.ops.autoIncrement) != (attrData.Extra == "auto_increment")
|
|
) {
|
|
/*console.log(!handler.builders.compareDatatypes(handler, a.type, attrData.Type), "|",
|
|
a.ops.default, attrData.Default, "|",
|
|
(!!a.ops.notNull || !!a.ops.autoIncrement || !!a.ops.primaryKey), (attrData.Null == "NO"), "|",
|
|
(!!a.ops.autoIncrement), (attrData.Extra == "auto_increment"));*/
|
|
freeForUpdate(a.name, a.table.dbLangTableName);
|
|
create.appendEnding(handler.querys.changeColumn(handler, a));
|
|
}
|
|
if (attrData == null) {
|
|
changePrimary = true;
|
|
} else {
|
|
if ((attrData.Key == "PRI") != (!!a.ops.primaryKey)) {
|
|
freeForUpdate(a.name, a.table.dbLangTableName);
|
|
changePrimary = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (changePrimary) {
|
|
create.appendEnding(handler.querys.removePrimaryKey(handler, table.dbLangTableName));
|
|
create.appendEnding(handler.querys.addPrimaryKey(handler, table));
|
|
}
|
|
for (let j = 0; j < table.dbLangConstrains.length; j++) {
|
|
const c = table.dbLangConstrains[j];
|
|
if (c instanceof checkConstraint) {
|
|
create.appendEnding(handler.querys.addCheck(handler, table, c));
|
|
} else if (c instanceof uniqueConstraint) {
|
|
if (!checkUniqueExists(table, c)) {
|
|
create.appendEnding(handler.querys.addUnique(handler, table, c));
|
|
}
|
|
} else if (c instanceof foreignConstraint) {
|
|
if (!checkForeignExists(table, c)) {
|
|
create.appendEnding(handler.querys.addForeignKey(handler, table, c));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!create.isEmpty()) del.append(create);
|
|
if (!del.isEmpty()) await db.query(handler.builders.query(del));
|
|
}
|
|
|
|
querys = {
|
|
select: (handler: Handler, q: selectQuery): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode("select ");
|
|
builder.append(joinArg(", ")(handler, q.attr));
|
|
builder.addCode(` from `);
|
|
if (q.from == null) {
|
|
builder.addCode(" DUAL");
|
|
} else if (q.from instanceof Table) {
|
|
builder.addCode(q.from.serialize(handler));
|
|
} else {
|
|
builder.append(q.from.serialize(handler));
|
|
}
|
|
if (q.whereD) {
|
|
builder.addCode(" where ");
|
|
builder.append(q.whereD.serialize(handler));
|
|
}
|
|
if (q.groupByD.length > 0) {
|
|
builder.addCode(" group by ");
|
|
builder.append(joinArg(",")(handler, q.groupByD));
|
|
if (q.havingD) {
|
|
builder.addCode(" having ");
|
|
builder.append(q.havingD.serialize(handler));
|
|
}
|
|
}
|
|
if (q.limitD != null) {
|
|
if (q.limitOffsetD == null) {
|
|
builder.addCode(" limit ");
|
|
builder.addInjection(q.limitD);
|
|
} else {
|
|
builder.addCode(" limit ");
|
|
builder.addInjection(q.limitOffsetD);
|
|
builder.addCode(", ");
|
|
builder.addInjection(q.limitD);
|
|
}
|
|
}
|
|
|
|
/*builder.setHandler((json)=>{
|
|
return json;
|
|
});*/
|
|
|
|
return builder;
|
|
},
|
|
insert: (hander: Handler, q: insertQuery): QueryBuilder => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode("INSERT INTO ");
|
|
qb.addCode(q.attrs[0].table.serialize(hander));
|
|
qb.addCode("(");
|
|
qb.addCodeCommaSeperated(q.attrs.map(a => a.serialize(hander)));
|
|
qb.addCode(") ");
|
|
if (q.select == null) {
|
|
qb.addCode("VALUES ");
|
|
for (let i = 0; i < q.values.length; i++) {
|
|
qb.addCode("(");
|
|
qb.addInjectionCommaSeperated(q.values[i]);
|
|
qb.addCode(")");
|
|
if (i + 1 < q.values.length) qb.addCode(",");
|
|
}
|
|
} else {
|
|
qb.append(q.select.serialize(hander));
|
|
}
|
|
return qb;
|
|
},
|
|
update: (handler: Handler, q: updateQuery): QueryBuilder => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode("UPDATE ");
|
|
qb.addCode(q.table.serialize(handler));
|
|
qb.addCode(" SET ");
|
|
for (let i = 0; i < q.setD.length; i++) {
|
|
const s = q.setD[i];
|
|
qb.addCode(s[0].serialize(handler));
|
|
qb.addCode(" = (");
|
|
if (s[1] instanceof Attribute) qb.addCode(s[1].serialize(handler));
|
|
else if (s[1] instanceof Modifier || s[1] instanceof selectQuery || s[1] instanceof Aggregation) {
|
|
qb.append(s[1].serialize(handler));
|
|
} else {
|
|
qb.addInjection(s[1]);
|
|
}
|
|
qb.addCode(")");
|
|
if (i + 1 < q.setD.length) qb.addCode(", ");
|
|
}
|
|
if (q.whereD) {
|
|
qb.addCode(" where ");
|
|
qb.append(q.whereD.serialize(handler));
|
|
}
|
|
return qb;
|
|
},
|
|
remove: (handler: Handler, q: removeQuery): QueryBuilder => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode("DELETE FROM ");
|
|
qb.addCode(q.table.serialize(handler));
|
|
if (q.whereD) {
|
|
qb.addCode(" where ");
|
|
qb.append(q.whereD.serialize(handler));
|
|
}
|
|
return qb;
|
|
},
|
|
listTables: (handler: Handler, db: DB) => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode(`SELECT * FROM information_schema.tables where TABLE_SCHEMA = `);
|
|
qb.addInjection(db.name);
|
|
qb.addCode(` and TABLE_TYPE = "BASE TABLE"`);
|
|
return qb;
|
|
},
|
|
create: (handler: Handler, table: Table): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode(`create table if not exists ${table.toString(handler)}(`);
|
|
let keys = Object.keys(table.dbLangTableAttributes);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const a = table.dbLangTableAttributes[keys[i]];
|
|
builder.addCode(a.serialize(handler) + " ");
|
|
builder.append(a.serializeSettings(handler));
|
|
if (i + 1 < keys.length) builder.addCode(", ");
|
|
}
|
|
builder.addCode(")");
|
|
return builder;
|
|
},
|
|
deleteTable: (handler: Handler, table: string): QueryBuilder => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode(`DROP TABLE `);
|
|
qb.addCode(handler.builders.escapeID(table));
|
|
qb.addCode(` CASCADE`);
|
|
return qb;
|
|
},
|
|
addColumn: (handler: Handler, attr: Attribute): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode(`alter table `);
|
|
builder.addCode(attr.table.toString(handler));
|
|
builder.addCode(` add if not exists `);
|
|
builder.addCode(attr.serialize(handler) + " ");
|
|
builder.append(attr.serializeSettings(handler));
|
|
return builder;
|
|
},
|
|
changeColumn: (handler: Handler, attr: Attribute): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode(`alter table `);
|
|
builder.addCode(attr.table.toString(handler));
|
|
builder.addCode(` change `);
|
|
builder.addCode(attr.serialize(handler) + " ");
|
|
builder.addCode(attr.serialize(handler) + " ");
|
|
builder.append(attr.serializeSettings(handler));
|
|
return builder;
|
|
},
|
|
removeColumn: (handler: Handler, tabel: Table, attr: string) => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode("ALTER TABLE ");
|
|
builder.addCode(tabel.serialize(handler));
|
|
builder.addCode(" DROP COLUMN ");
|
|
builder.addCode(handler.builders.escapeID(attr));
|
|
return builder;
|
|
},
|
|
addForeignKey: (handler: Handler, table: Table, c: foreignConstraint) => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode("ALTER TABLE ");
|
|
builder.addCode(table.serialize(handler));
|
|
builder.addCode(" ADD CONSTRAINT ");
|
|
builder.addCode(handler.builders.escapeID(c.name));
|
|
builder.addCode(" FOREIGN KEY (");
|
|
builder.addCodeCommaSeperated(c.fromAttrs.map(a => a.serialize(handler)));
|
|
builder.addCode(") REFERENCES ");
|
|
builder.addCode(c.toAttrs[0].table.serialize(handler));
|
|
builder.addCode("(");
|
|
builder.addCodeCommaSeperated(c.toAttrs.map(a => a.serialize(handler)));
|
|
builder.addCode(")");
|
|
|
|
if (c.onUpdate == onAction.cascade) builder.addCode(" ON UPDATE CASCADE");
|
|
if (c.onUpdate == onAction.noAction) builder.addCode(" ON UPDATE NO ACTION");
|
|
if (c.onUpdate == onAction.setDefault) builder.addCode(" ON UPDATE SET DEFUALT");
|
|
if (c.onUpdate == onAction.setNull) builder.addCode(" ON UPDATE SET NULL");
|
|
|
|
if (c.onDelete == onAction.cascade) builder.addCode(" ON DELETE CASCADE");
|
|
if (c.onDelete == onAction.noAction) builder.addCode(" ON DELETE NO ACTION");
|
|
if (c.onDelete == onAction.setDefault) builder.addCode(" ON DELETE SET DEFUALT");
|
|
if (c.onDelete == onAction.setNull) builder.addCode(" ON DELETE SET NULL");
|
|
return builder;
|
|
},
|
|
removeForeignKey: (handler: Handler, tablenName: string, name: string) => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode("ALTER TABLE ");
|
|
builder.addCode(handler.builders.escapeID(tablenName));
|
|
builder.addCode(" DROP FOREIGN KEY IF EXISTS ");
|
|
builder.addCode(handler.builders.escapeID(name));
|
|
builder.addCode(";ALTER TABLE ");
|
|
builder.addCode(handler.builders.escapeID(tablenName));
|
|
builder.addCode(" DROP INDEX IF EXISTS ");
|
|
builder.addCode(handler.builders.escapeID(name));
|
|
|
|
return builder;
|
|
},
|
|
addPrimaryKey: (handler: Handler, table: Table) => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode("ALTER TABLE ");
|
|
qb.addCode(table.serialize(handler));
|
|
qb.addCode(" add PRIMARY KEY if not exists (");
|
|
qb.addCodeCommaSeperated(
|
|
Object.entries(table.dbLangTableAttributes)
|
|
.filter(([n, attr]) => !!attr.ops.primaryKey)
|
|
.map(([n, attr]) => handler.builders.escapeID(n))
|
|
);
|
|
qb.addCode(")");
|
|
return qb;
|
|
},
|
|
removePrimaryKey: (handler: Handler, table: string) => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode("ALTER TABLE ");
|
|
qb.addCode(handler.builders.escapeID(table));
|
|
qb.addCode(" DROP INDEX IF EXISTS `PRIMARY`");
|
|
return qb;
|
|
},
|
|
removeCheck: (handler: Handler, table: string, name: string) => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode("ALTER TABLE ");
|
|
qb.addCode(handler.builders.escapeID(table));
|
|
qb.addCode(" DROP CONSTRAINT IF EXISTS ");
|
|
qb.addCode(handler.builders.escapeID(name));
|
|
return qb;
|
|
},
|
|
addCheck: (handler: Handler, table: Table, c: checkConstraint) => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode(`ALTER TABLE `);
|
|
qb.addCode(table.serialize(handler));
|
|
qb.addCode(` ADD CONSTRAINT `);
|
|
qb.addCode(handler.builders.escapeID(c.name));
|
|
qb.addCode(` CHECK (`);
|
|
qb.append(c.checkQuery.serialize(handler));
|
|
qb.addCode(")");
|
|
return qb;
|
|
},
|
|
removeUnique: (handler: Handler, table: string, name: string) => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode("ALTER TABLE ");
|
|
qb.addCode(handler.builders.escapeID(table));
|
|
qb.addCode(" DROP INDEX IF EXISTS ");
|
|
qb.addCode(handler.builders.escapeID(name));
|
|
return qb;
|
|
},
|
|
addUnique: (handler: Handler, table: Table, u: uniqueConstraint) => {
|
|
const qb = new QueryBuilder();
|
|
qb.addCode("ALTER TABLE ");
|
|
qb.addCode(table.serialize(handler));
|
|
qb.addCode(" ADD CONSTRAINT ");
|
|
qb.addCode(handler.builders.escapeID(u.name));
|
|
qb.addCode(" UNIQUE (");
|
|
qb.addCodeCommaSeperated(u.attrs.map(a => a.serialize(handler)));
|
|
qb.addCode(")");
|
|
return qb;
|
|
},
|
|
}
|
|
|
|
builders = {
|
|
query: (qb: QueryBuilder): Query => {
|
|
let args: primaryData[] = [];
|
|
let sql = "";
|
|
for (let i = 0; i < qb.list.length; i++) {
|
|
const [inject, data] = qb.list[i];
|
|
if (inject) {
|
|
sql += "?";
|
|
args.push(data);
|
|
} else {
|
|
sql += data;
|
|
}
|
|
}
|
|
return new Query([sql, args]);
|
|
},
|
|
|
|
attributeSettings: (handler: Handler, a: Attribute): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
builder.append(a.type.serialize(handler));
|
|
if (a.ops.autoIncrement) {
|
|
builder.addCode(" auto_increment");
|
|
}
|
|
if (a.ops.default != null) {
|
|
if (a.ops.autoIncrement || a.ops.primaryKey || a.ops.notNull)
|
|
throw new Error(`Can not set default when autoIncrement, primaryKey or notNull ist set on Attribute: ${a.toStringFunc(handler)}`);
|
|
builder.addCode(" default ");
|
|
builder.addInjection(a.ops.default);
|
|
}
|
|
if (a.ops.notNull != null) {
|
|
if (!a.ops.autoIncrement && !a.ops.primaryKey && a.ops.default == null) {
|
|
builder.addCode(" not null");
|
|
} else builder.addCode(" null");
|
|
} else builder.addCode(" null");
|
|
return builder;
|
|
},
|
|
escapeID: (key: string): string => {
|
|
if (!key || key === "" || key.includes('\u0000')) throw new Error("Can not escape empty key or with null unicode!");
|
|
if (key.match(/^`.+`$/g)) return key;
|
|
return `\`${key.replace(/`/g, '``')}\``;
|
|
},
|
|
compareDatatypes: (handler: Handler, attr: Datatype, curr: String) => {
|
|
let qb = attr.serialize(handler);
|
|
let sql = "";
|
|
for (let i = 0; i < qb.list.length; i++) {
|
|
const [inject, data] = qb.list[i];
|
|
sql += data;
|
|
}
|
|
|
|
return curr.split(" ").join("").startsWith(sql.split(" ").join(""));
|
|
},
|
|
}
|
|
|
|
aggregations = {
|
|
count: (handler: Handler, a: Attribute): QueryBuilder => new QueryBuilder([{ data: "count(" + a.toString(handler) + ")" }]),
|
|
sum: (handler: Handler, a: Attribute): QueryBuilder => new QueryBuilder([{ data: "sum(" + a.toString(handler) + ")" }]),
|
|
avg: (handler: Handler, a: Attribute): QueryBuilder => new QueryBuilder([{ data: "avg(" + a.toString(handler) + ")" }]),
|
|
min: (handler: Handler, a: Attribute): QueryBuilder => new QueryBuilder([{ data: "min(" + a.toString(handler) + ")" }]),
|
|
max: (handler: Handler, a: Attribute): QueryBuilder => new QueryBuilder([{ data: "max(" + a.toString(handler) + ")" }]),
|
|
}
|
|
|
|
joins = {
|
|
natural: (handler: Handler, j: joinNatural) => {
|
|
const qb = new QueryBuilder();
|
|
for (let i = 0; i < j.tables.length; i++) {
|
|
const t = j.tables[i];
|
|
if (t instanceof Table) qb.addCode(t.serialize(handler));
|
|
else {
|
|
qb.addCode("(");
|
|
qb.append(t.serialize(handler));
|
|
qb.addCode(")");
|
|
}
|
|
if (i + 1 < j.tables.length) {
|
|
qb.addCode(" natural ");
|
|
if (j.type == joinType.left) qb.addCode("left");
|
|
if (j.type == joinType.right) qb.addCode("right");
|
|
if (j.type == joinType.full) qb.addCode("full");
|
|
qb.addCode(" join ");
|
|
}
|
|
}
|
|
return qb;
|
|
},
|
|
using: (handler: Handler, j: usingJoin) => {
|
|
const qb = new QueryBuilder();
|
|
if (j.tableA instanceof Table) qb.addCode(j.tableA.serialize(handler));
|
|
else {
|
|
qb.addCode("(");
|
|
qb.append(j.tableA.serialize(handler));
|
|
qb.addCode(")");
|
|
}
|
|
qb.addCode(" ");
|
|
if (j.type == joinType.left) qb.addCode("left ");
|
|
if (j.type == joinType.right) qb.addCode("right ");
|
|
if (j.type == joinType.full) qb.addCode("full ");
|
|
qb.addCode("join ");
|
|
if (j.tableA instanceof Table) qb.addCode(j.tableA.serialize(handler));
|
|
else {
|
|
qb.addCode("(");
|
|
qb.append(j.tableA.serialize(handler));
|
|
qb.addCode(")");
|
|
}
|
|
qb.addCode(" using (");
|
|
qb.addCodeCommaSeperated(j.using.map(t => t.serialize(handler)));
|
|
qb.addCode(")");
|
|
return qb;
|
|
},
|
|
on: (handler: Handler, j: onJoin) => {
|
|
const qb = new QueryBuilder();
|
|
if (j.tableA instanceof Table) qb.addCode(j.tableA.serialize(handler));
|
|
else {
|
|
qb.addCode("(");
|
|
qb.append(j.tableA.serialize(handler));
|
|
qb.addCode(")");
|
|
}
|
|
qb.addCode(" ");
|
|
if (j.type == joinType.left) qb.addCode("left ");
|
|
if (j.type == joinType.right) qb.addCode("right ");
|
|
if (j.type == joinType.full) qb.addCode("full ");
|
|
qb.addCode("join ");
|
|
if (j.tableA instanceof Table) qb.addCode(j.tableA.serialize(handler));
|
|
else {
|
|
qb.addCode("(");
|
|
qb.append(j.tableA.serialize(handler));
|
|
qb.addCode(")");
|
|
}
|
|
qb.addCode(" on (");
|
|
qb.append(j.on.serialize(handler));
|
|
qb.addCode(")");
|
|
return qb;
|
|
},
|
|
cross: (handler: Handler, j: joinCross) => {
|
|
const qb = new QueryBuilder();
|
|
for (let i = 0; i < j.tables.length; i++) {
|
|
const t = j.tables[i];
|
|
if (t instanceof Table) qb.addCode(t.serialize(handler));
|
|
else {
|
|
qb.addCode("(");
|
|
qb.append(t.serialize(handler));
|
|
qb.addCode(")");
|
|
}
|
|
if (i + 1 < j.tables.length) qb.addCode(" cross join ");
|
|
}
|
|
return qb;
|
|
}
|
|
}
|
|
|
|
modifiers = {
|
|
and: joinArg("and"),
|
|
or: joinArg("or"),
|
|
le: joinArg("<"),
|
|
leq: joinArg("<="),
|
|
eq: joinArg("="),
|
|
geq: joinArg(">="),
|
|
ge: joinArg(">"),
|
|
plus: joinArg("+"),
|
|
minus: joinArg("-"),
|
|
mult: joinArg("*"),
|
|
divide: joinArg("-/"),
|
|
not: (handler: Handler, a: allModifierInput[]): QueryBuilder => {
|
|
let e = a[0];
|
|
if (e instanceof Attribute) return new QueryBuilder([{ data: "not (" + e.toString(handler) + ")" }])
|
|
if (e instanceof Modifier || e instanceof selectQuery || e instanceof Aggregation) {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode("not (");
|
|
builder.append(e.serialize(handler));
|
|
builder.addCode(")");
|
|
return builder;
|
|
}
|
|
return new QueryBuilder([
|
|
{ data: "not(" },
|
|
{ inject: true, data: e },
|
|
{ data: ")" }
|
|
]);
|
|
},
|
|
like: (handler: Handler, a: allModifierInput[]): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
if (a[0] instanceof Attribute) builder.addCode(a[0].toString());
|
|
else if (a[0] instanceof Modifier || a[0] instanceof selectQuery || a[0] instanceof Aggregation) {
|
|
builder.append(a[0].serialize(handler));
|
|
} else {
|
|
builder.addInjection(a[0]);
|
|
}
|
|
builder.addCode(" LIKE ");
|
|
if (a[1] instanceof Attribute) builder.addCode(a[1].toString());
|
|
else if (a[1] instanceof Modifier || a[1] instanceof selectQuery || a[1] instanceof Aggregation) {
|
|
builder.append(a[1].serialize(handler));
|
|
} else {
|
|
builder.addInjection(a[1]);
|
|
}
|
|
return builder;
|
|
},
|
|
concat: (handler: Handler, a: allModifierInput[]): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode("CONCAT(");
|
|
for (let i = 0; i < a.length; i++) {
|
|
const e = a[i];
|
|
if (e instanceof Attribute) builder.addCode(e.toString());
|
|
else if (e instanceof Modifier || e instanceof selectQuery || e instanceof Aggregation) {
|
|
builder.append(e.serialize(handler));
|
|
} else {
|
|
builder.addInjection(e);
|
|
}
|
|
if (i < a.length - 1) builder.addCode(", ");
|
|
}
|
|
builder.addCode(")");
|
|
return builder;
|
|
}
|
|
}
|
|
|
|
datatypes = {
|
|
char: dataTypeSingleNum("char"),
|
|
varchar: dataTypeSingleNum("varchar"),
|
|
binary: dataTypeSingleNum("binary"),
|
|
varbinary: dataTypeSingleNum("varbinary"),
|
|
|
|
tinyblob: dataTypeNoArg("tinyblob"),
|
|
blob: dataTypeNoArg("blob"),
|
|
mediumblob: dataTypeNoArg("mediumblob"),
|
|
longblob: dataTypeNoArg("longblob"),
|
|
tinytext: dataTypeNoArg("tinytext"),
|
|
text: dataTypeNoArg("text"),
|
|
mediumtext: dataTypeNoArg("mediumtext"),
|
|
longtext: dataTypeNoArg("longtext"),
|
|
|
|
enum: (a: primaryData[]): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode("enum(");
|
|
builder.addInjectionCommaSeperated(a);
|
|
builder.addCode(")");
|
|
return builder;
|
|
},
|
|
set: (a: primaryData[]): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
builder.addCode("set(");
|
|
builder.addInjectionCommaSeperated(a);
|
|
builder.addCode(")");
|
|
return builder;
|
|
},
|
|
bool: dataTypeNoArg("bool"),
|
|
bit: dataTypeNoArg("bit"),
|
|
tinyint: dataTypeNoArg("tinyint"),
|
|
smallint: dataTypeNoArg("smallint"),
|
|
mediumint: dataTypeNoArg("mediumint"),
|
|
int: dataTypeNoArg("int"),
|
|
bigint: dataTypeNoArg("bigint"),
|
|
|
|
float: dataTypeDblNum("float"),
|
|
double: dataTypeDblNum("double"),
|
|
decimal: dataTypeDblNum("decimal"),
|
|
|
|
date: dataTypeNoArg("date"),
|
|
datetime: dataTypeNoArg("datatime"),
|
|
timestamp: dataTypeNoArg("timestamp"),
|
|
time: dataTypeNoArg("time"),
|
|
year: dataTypeNoArg("year"),
|
|
}
|
|
|
|
};
|
|
|
|
function dataTypeNoArg(type: string) {
|
|
return (a: primaryData[]): QueryBuilder => {
|
|
return new QueryBuilder([{
|
|
inject: false,
|
|
data: type
|
|
}]);
|
|
}
|
|
}
|
|
function dataTypeSingleNum(type: string) {
|
|
return (a: primaryData[]): QueryBuilder => {
|
|
return new QueryBuilder([
|
|
{ data: type + "(" },
|
|
{ inject: true, data: a[0] },
|
|
{ data: ")" }
|
|
]);
|
|
}
|
|
}
|
|
function dataTypeDblNum(type: string) {
|
|
return (a: primaryData[]): QueryBuilder => {
|
|
return new QueryBuilder([
|
|
{ data: type + "(" },
|
|
{ inject: true, data: a[0] },
|
|
{ data: ", " },
|
|
{ inject: true, data: a[1] },
|
|
{ data: ")" }
|
|
]);
|
|
}
|
|
}
|
|
|
|
|
|
function joinArg(type: string) {
|
|
return (handler: Handler, a: (allModifierInput)[]): QueryBuilder => {
|
|
const builder = new QueryBuilder();
|
|
for (let i = 0; i < a.length; i++) {
|
|
const d = a[i];
|
|
if (d instanceof Attribute) builder.addCode(d.toString(handler));
|
|
else if (d instanceof Modifier || d instanceof selectQuery || d instanceof Aggregation) {
|
|
builder.addCode("(");
|
|
builder.append(d.serialize(handler));
|
|
builder.addCode(")");
|
|
} else {
|
|
builder.addInjection(d);
|
|
}
|
|
if (i + 1 < a.length) builder.addCode(" " + type + " ");
|
|
}
|
|
return builder;
|
|
}
|
|
} |