diff --git a/src/db.ts b/src/db.ts index 387ad23..75511ed 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,5 +1,5 @@ import mariadb from 'mariadb'; -import { checkConstraint, Constraint, Datatype, uniqueConstraint } from './dbStructure'; +import { checkConstraint, Constraint, Datatype, foreignConstraint, uniqueConstraint } from './dbStructure'; import { Handler } from './defaultHandler'; import { Query } from './query'; import { attributeSettings, extendedAttributeSettings, onAction } from './types'; @@ -37,8 +37,8 @@ export class DB { return tabel; } - getTable(name:string){ - for(let i = 0; i < this.tables.length; i++) if(this.tables[i].dbLangTableName == name) return this.tables[i]; + getTable(name: string) { + for (let i = 0; i < this.tables.length; i++) if (this.tables[i].dbLangTableName == name) return this.tables[i]; return null; } } @@ -55,7 +55,7 @@ export class Attribute { this.ops = ops; if (ops.check != null) { - if(typeof ops.check == "function")ops.check = ops.check(this); + if (typeof ops.check == "function") ops.check = ops.check(this); table.addConstraint(new checkConstraint( table.dbLangDatabaseInstance.name + "_" + table.dbLangTableName + "_" + name + "_check_constraint", ops.check @@ -67,6 +67,15 @@ export class Attribute { [this] )) } + if (ops.foreginKey != null) { + table.addConstraint(new foreignConstraint( + table.dbLangDatabaseInstance.name + "_" + table.dbLangTableName + "_" + name + "_foreign_constraint_to_"+ops.foreginKey.link.name, + [this], + [ops.foreginKey.link], + ops.foreginKey.onDelete, + ops.foreginKey.onUpdate + )); + } } serializeDatatype(handler: Handler) { return this.type.serialize(handler); diff --git a/src/dbStructure.ts b/src/dbStructure.ts index a7bd542..e61404c 100644 --- a/src/dbStructure.ts +++ b/src/dbStructure.ts @@ -1,7 +1,7 @@ import { Attribute, Table } from "./db"; import { Handler } from "./defaultHandler"; import { QueryBuilder } from "./query"; -import { allModifierInput, primaryData, serializeReturn } from "./types"; +import { allModifierInput, onAction, primaryData, serializeReturn } from "./types"; export class Datatype { type: string; @@ -101,10 +101,14 @@ export class foreignConstraint implements Constraint { name: string; fromAttrs: Attribute[]; toAttrs: Attribute[]; - constructor(name: string, from: Attribute[], to: Attribute[]) { + onUpdate: onAction; + onDelete: onAction; + constructor(name: string, from: Attribute[], to: Attribute[], onDelete: onAction = onAction.nothing, onUpdate: onAction = onAction.nothing) { this.name = name.toLowerCase(); this.fromAttrs = from; this.toAttrs = to; + this.onUpdate = onUpdate; + this.onDelete = onDelete; } serialize(handler: Handler): QueryBuilder { throw new Error("Method not implemented."); diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index c9393ef..957f87a 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -1,7 +1,7 @@ import { Attribute, DB, Table } from "./db" -import { Aggregation, checkConstraint, Constraint, Datatype, Modifier, uniqueConstraint } from "./dbStructure" +import { Aggregation, checkConstraint, Constraint, Datatype, foreignConstraint, Modifier, uniqueConstraint } from "./dbStructure" import { Query, QueryBuilder, selectQuery } from "./query" -import { allModifierInput, primaryData, serializeReturn } from "./types" +import { allModifierInput, onAction, primaryData, serializeReturn } from "./types" export class Handler { async syncDB(db: DB, handler: Handler, deleteInDB: boolean = false) { @@ -19,8 +19,10 @@ export class Handler { 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]; @@ -45,7 +47,6 @@ export class Handler { const c = t.dbLangConstrains[i]; if (c.name == n.toLowerCase() && c instanceof uniqueConstraint) { let attrs = c.attrs.map(a => a.name); - console.log(attrs); let found = 0; for (let j = 0; j < key.length; j++) { if ( @@ -61,6 +62,35 @@ export class Handler { } 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; @@ -75,6 +105,25 @@ export class Handler { } 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++) { @@ -86,15 +135,17 @@ export class Handler { (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" && deleteInDB) { - if (typeof tableData[c.TABLE_NAME.toLowerCase()] == "undefined") del.appendEnding(handler.querys.removeForeignKey(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])); } - // TODO: delete old constraints/tables function freeForUpdate(attr: string, table: string) { for (let i = 0; i < key.length; i++) { @@ -113,7 +164,7 @@ export class Handler { const table = db.tables[i]; const tableD = tableData[table.dbLangTableName.toLowerCase()]; //delete unused Columns - if (deleteInDB) { + if (deleteInDB && tableD != null) { let keys = Object.keys(tableD); for (let i = 0; i < keys.length; i++) { if (table.dbLangTableAttributes[keys[i]] == null) { @@ -141,10 +192,10 @@ export class Handler { (!!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), "|", + /*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")); + (!!a.ops.autoIncrement), (attrData.Extra == "auto_increment"));*/ freeForUpdate(a.name, a.table.dbLangTableName); create.appendEnding(handler.querys.changeColumn(handler, a)); } @@ -170,10 +221,12 @@ export class Handler { 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)); + } } } - - // TODO: unique, foreignkey } if (!create.isEmpty()) del.append(create); if (!del.isEmpty()) await db.query(handler.builders.query(del)); @@ -262,22 +315,29 @@ export class Handler { builder.addCode(handler.builders.escapeID(attr)); return builder; }, - addForeignKey: (handler: Handler, attr: Attribute, target: Attribute) => { + addForeignKey: (handler: Handler, table: Table, c: foreignConstraint) => { const builder = new QueryBuilder(); builder.addCode("ALTER TABLE "); - builder.addCode(attr.table.serialize(handler)); + builder.addCode(table.serialize(handler)); builder.addCode(" ADD CONSTRAINT "); - builder.addCode(handler.builders.escapeID(attr.table.dbLangDatabaseInstance.name - + "_" + attr.table.dbLangTableName - + "_" + attr.name + "_foreign_key_constraint_to_" - + target.table.dbLangTableName + "_" + target.name)); - builder.addCode("FOREIGN KEY ("); - builder.addCode(attr.serialize(handler)); + 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(target.table.serialize(handler)); + builder.addCode(c.toAttrs[0].table.serialize(handler)); builder.addCode("("); - builder.addCode(target.serialize(handler)); + 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) => { diff --git a/src/types.ts b/src/types.ts index c7bc431..026c038 100644 --- a/src/types.ts +++ b/src/types.ts @@ -42,6 +42,7 @@ export type extendedAttributeSettings = { } export enum onAction { + nothing, cascade, noAction, setNull,