From ea3100c4ae00c82246a30ddb3a2080bd8b98b406 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 14 Feb 2023 16:45:21 +0100 Subject: [PATCH] unique evolve --- src/db.ts | 14 +++-- src/dbStructure.ts | 76 ++++++++++++++++-------- src/defaultHandler.ts | 133 +++++++++++++++++++++++++++++++++++++----- src/funcs.ts | 64 ++++++++++---------- src/query.ts | 14 ++--- 5 files changed, 220 insertions(+), 81 deletions(-) diff --git a/src/db.ts b/src/db.ts index 5566360..387ad23 100644 --- a/src/db.ts +++ b/src/db.ts @@ -23,9 +23,9 @@ export class DB { return this.handler; } - async sync() { + async sync(force = false) { let handler = this.getHandler(); - await handler.syncDB(this, handler); + await handler.syncDB(this, handler, force); //this.tables.forEach(t=>{ // console.log(handler.builders.query(handler.querys.create(handler,t))); //}) @@ -36,6 +36,11 @@ export class DB { this.tables.push(tabel); return tabel; } + + getTable(name:string){ + for(let i = 0; i < this.tables.length; i++) if(this.tables[i].dbLangTableName == name) return this.tables[i]; + return null; + } } export class Attribute { @@ -44,7 +49,7 @@ export class Attribute { ops: attributeSettings; type: Datatype; constructor(name: string, table: Table, type: Datatype, ops: attributeSettings) { - this.name = name; + this.name = name.toLowerCase(); this.type = type; this.table = table; this.ops = ops; @@ -58,7 +63,7 @@ export class Attribute { } if (ops.unique != null) { table.addConstraint(new uniqueConstraint( - table.dbLangDatabaseInstance.name + "_" + table.dbLangTableName + " " + name + "_unique_constraint", + table.dbLangDatabaseInstance.name + "_" + table.dbLangTableName + "_" + name + "_unique_constraint", [this] )) } @@ -98,6 +103,7 @@ export class Table { return handler.builders.escapeID(this.dbLangTableName); } addAttribute(name: string, type: Datatype, ops: attributeSettings = {}, noErrorOnNameConflict = false) { + name = name.toLowerCase(); if (this.dbLangTableAttributes[name] != null) throw new Error("You are tring to create an Attribute twise!"); let attr = new Attribute(name, this, type, ops); this.dbLangTableAttributes[name] = attr; diff --git a/src/dbStructure.ts b/src/dbStructure.ts index b5fe4fd..a7bd542 100644 --- a/src/dbStructure.ts +++ b/src/dbStructure.ts @@ -3,14 +3,14 @@ import { Handler } from "./defaultHandler"; import { QueryBuilder } from "./query"; import { allModifierInput, primaryData, serializeReturn } from "./types"; -export class Datatype{ +export class Datatype { type: string; args: primaryData[]; - constructor(type:string, args: primaryData[]){ + constructor(type: string, args: primaryData[]) { this.type = type; this.args = args; } - serialize(handler : Handler): QueryBuilder{ + serialize(handler: Handler): QueryBuilder { return handler.datatypes[this.type as keyof typeof handler.datatypes](this.args); } } @@ -22,8 +22,8 @@ export abstract class Modifier { this.t = type; this.a = args; } - serialize(handler : Handler): QueryBuilder { - return handler.modifiers[this.t as keyof typeof handler.modifiers](handler,this.a); + serialize(handler: Handler): QueryBuilder { + return handler.modifiers[this.t as keyof typeof handler.modifiers](handler, this.a); } } @@ -38,29 +38,29 @@ export class Aggregation { this.t = type; this.a = args; } - serialize(handler : Handler): QueryBuilder { - return handler.aggregations[this.t as keyof typeof handler.aggregations](handler,this.a); + serialize(handler: Handler): QueryBuilder { + return handler.aggregations[this.t as keyof typeof handler.aggregations](handler, this.a); } } export class Joins { - serialize(handler : Handler): QueryBuilder { + serialize(handler: Handler): QueryBuilder { return new QueryBuilder(); } } -export interface Constraint{ - name:string; - serialize( handler: Handler) : QueryBuilder; - uses(attr:Attribute): boolean; - check(attr:Table): boolean | string; +export interface Constraint { + name: string; + serialize(handler: Handler): QueryBuilder; + uses(attr: Attribute): boolean; + check(table: Table): boolean | string; } -export class checkConstraint implements Constraint{ - checkQuery:BooleanModifier; +export class checkConstraint implements Constraint { + checkQuery: BooleanModifier; name: string; - constructor(name:string, check: BooleanModifier){ - this.name = name; + constructor(name: string, check: BooleanModifier) { + this.name = name.toLowerCase(); this.checkQuery = check; } check(attr: Table): boolean { @@ -74,16 +74,17 @@ export class checkConstraint implements Constraint{ } } -export class uniqueConstraint implements Constraint{ +export class uniqueConstraint implements Constraint { name: string; attrs: Attribute[]; - constructor(name:string, attrs:Attribute[]){ - this.name = name; + constructor(name: string, attrs: Attribute[]) { + this.name = name.toLowerCase(); this.attrs = attrs; } - check(table: Table): boolean|string { - for(let i = 0; i < this.attrs.length; i++){ - if(this.attrs[i].ops.primaryKey) return "Can not combine unique Constraint and primary key"; + check(table: Table): boolean | string { + for (let i = 0; i < this.attrs.length; i++) { + if (this.attrs[i].ops.primaryKey) return "Can not combine unique Constraint and primary key"; + if (this.attrs[i].table != table) return "Referencing Attributes must be in host Table."; } return false; } @@ -94,4 +95,33 @@ export class uniqueConstraint implements Constraint{ throw new Error("Method not implemented."); } +} + +export class foreignConstraint implements Constraint { + name: string; + fromAttrs: Attribute[]; + toAttrs: Attribute[]; + constructor(name: string, from: Attribute[], to: Attribute[]) { + this.name = name.toLowerCase(); + this.fromAttrs = from; + this.toAttrs = to; + } + serialize(handler: Handler): QueryBuilder { + throw new Error("Method not implemented."); + } + uses(attr: Attribute): boolean { + throw new Error("Method not implemented."); + } + check(t: Table): string | boolean { + let table = this.toAttrs[0].table; + for (let i = 0; i < this.toAttrs.length; i++) { + if (table != this.toAttrs[i].table) return "Referenced Attributes must be in one Table."; + if (this.toAttrs[i].ops.primaryKey) return "Can not reference non primary keys."; + } + for (let i = 0; i < this.fromAttrs.length; i++) { + if (this.fromAttrs[i].table != t) return "Referencing Attributes must be in host Table."; + } + return false; + } + } \ No newline at end of file diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 9aa48af..c9393ef 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -19,37 +19,89 @@ export class Handler { let [key, constraints] = await db.query(handler.builders.query(gd)); + let allTables = (await db.query(handler.builders.query(handler.querys.listTables(handler, db)))).map((d: any) => d.TABLE_NAME); - let tableData = []; + 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[i] = Object.fromEntries((await db.query(handler.builders.query(tableDataBuilder))).map((d: any) => [d.Field, d])); + tableData[table.dbLangTableName.toLowerCase()] = + Object.fromEntries((await db.query(handler.builders.query(tableDataBuilder))).map((d: any) => [d.Field.toLowerCase(), d])); } catch (_) { - tableData[i] = null; + 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); + console.log(attrs); + 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 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; + } + + //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" && deleteInDB) { + if (typeof tableData[c.TABLE_NAME.toLowerCase()] == "undefined") 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(a: Attribute) { + function freeForUpdate(attr: string, table: string) { for (let i = 0; i < key.length; i++) { const k = key[i]; if ( - k.REFERENCED_TABLE_NAME == a.table.dbLangTableName - && k.REFERENCED_TABLE_NAME == a.name + k.REFERENCED_TABLE_NAME == table + && k.REFERENCED_COLUMN_NAME.toLowerCase() == attr.toLowerCase() ) { del.appendEnding(handler.querys.removeForeignKey(handler, k.TABLE_NAME, k.CONSTRAINT_NAME)); } @@ -59,7 +111,18 @@ export class Handler { //create tables for (let i = 0; i < db.tables.length; i++) { const table = db.tables[i]; - const tableD = tableData[i]; + const tableD = tableData[table.dbLangTableName.toLowerCase()]; + //delete unused Columns + if (deleteInDB) { + 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)); @@ -82,30 +145,35 @@ export class Handler { 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); + 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); + freeForUpdate(a.name, a.table.dbLangTableName); changePrimary = true; } } } } if (changePrimary) { - create.appendEnding(handler.querys.removePrimaryKey(handler, table)); + 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)); + } } } - // TODO: unique + + // TODO: unique, foreignkey } if (!create.isEmpty()) del.append(create); if (!del.isEmpty()) await db.query(handler.builders.query(del)); @@ -140,6 +208,13 @@ export class Handler { return builder; }, + 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)}(`); @@ -153,6 +228,13 @@ export class Handler { 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 `); @@ -172,9 +254,30 @@ export class Handler { builder.append(attr.serializeSettings(handler)); return builder; }, - addForeignKey: (handler: Handler, table: Table) => { + 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, attr: Attribute, target: Attribute) => { + const builder = new QueryBuilder(); + builder.addCode("ALTER TABLE "); + builder.addCode(attr.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(") REFERENCES "); + builder.addCode(target.table.serialize(handler)); + builder.addCode("("); + builder.addCode(target.serialize(handler)); + builder.addCode(")"); return builder; }, removeForeignKey: (handler: Handler, tablenName: string, name: string) => { @@ -203,10 +306,10 @@ export class Handler { qb.addCode(")"); return qb; }, - removePrimaryKey: (handler: Handler, table: Table) => { + removePrimaryKey: (handler: Handler, table: string) => { const qb = new QueryBuilder(); qb.addCode("ALTER TABLE "); - qb.addCode(table.serialize(handler)); + qb.addCode(handler.builders.escapeID(table)); qb.addCode(" DROP INDEX IF EXISTS `PRIMARY`"); return qb; }, diff --git a/src/funcs.ts b/src/funcs.ts index a135fd7..96928ab 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -27,48 +27,48 @@ export const select = (args: selectElements[], from: selectFromElements) => new //datatypes -export const CHAR = (size:number) => new Datatype("char",[size]); -export const VARCHAR = (size:number) => new Datatype("varchar",[size]); -export const BINARY = (size:number) => new Datatype("binary",[size]); -export const VARBINARY = (size:number) => new Datatype("varbinary",[size]); +export const CHAR = (size: number) => new Datatype("char", [size]); +export const VARCHAR = (size: number) => new Datatype("varchar", [size]); +export const BINARY = (size: number) => new Datatype("binary", [size]); +export const VARBINARY = (size: number) => new Datatype("varbinary", [size]); -export const TINYBLOB = new Datatype("tinyblob",[]); -export const BLOB = new Datatype("blob",[]); -export const MEDIUMBLOB = new Datatype("mediumblob",[]); -export const LONGBLOB = new Datatype("longblob",[]); +export const TINYBLOB = new Datatype("tinyblob", []); +export const BLOB = new Datatype("blob", []); +export const MEDIUMBLOB = new Datatype("mediumblob", []); +export const LONGBLOB = new Datatype("longblob", []); -export const TINYTEXT = new Datatype("tinytext",[]); -export const TEXT = new Datatype("text",[]); -export const MEDIUMTEXT = new Datatype("mediumtext",[]); -export const LONGTEXT = new Datatype("longtext",[]); +export const TINYTEXT = new Datatype("tinytext", []); +export const TEXT = new Datatype("text", []); +export const MEDIUMTEXT = new Datatype("mediumtext", []); +export const LONGTEXT = new Datatype("longtext", []); -export const ENUM = (...values:string[]) => new Datatype("enum",values); -export const SET = (...values:string[]) => new Datatype("set",values); +export const ENUM = (...values: string[]) => new Datatype("enum", values); +export const SET = (...values: string[]) => new Datatype("set", values); -export const BOOL = new Datatype("bool",[]); -export const BIT = new Datatype("bit",[]); -export const TINYINT = new Datatype("tinyint",[]); -export const SMALLINT = new Datatype("smallint",[]); -export const MEDIUMINT = new Datatype("mediumint",[]); -export const INT = new Datatype("int",[]); -export const BIGINT = new Datatype("bigint",[]); +export const BOOL = new Datatype("bool", []); +export const BIT = new Datatype("bit", []); +export const TINYINT = new Datatype("tinyint", []); +export const SMALLINT = new Datatype("smallint", []); +export const MEDIUMINT = new Datatype("mediumint", []); +export const INT = new Datatype("int", []); +export const BIGINT = new Datatype("bigint", []); -export const FLOAT = (size:number,d:number) => new Datatype("float",[size,d]); -export const DOUBLE = (size:number,d:number) => new Datatype("double",[size,d]); -export const DECIMAL = (size:number,d:number) => new Datatype("decimal",[size,d]); +export const FLOAT = (size: number, d: number) => new Datatype("float", [size, d]); +export const DOUBLE = (size: number, d: number) => new Datatype("double", [size, d]); +export const DECIMAL = (size: number, d: number) => new Datatype("decimal", [size, d]); -export const DATE = new Datatype("date",[]); -export const DATETIME = new Datatype("datetime",[]); -export const TIMESTAMP = new Datatype("timestamp",[]); -export const TIME = new Datatype("time",[]); -export const YEAR = new Datatype("year",[]); +export const DATE = new Datatype("date", []); +export const DATETIME = new Datatype("datetime", []); +export const TIMESTAMP = new Datatype("timestamp", []); +export const TIME = new Datatype("time", []); +export const YEAR = new Datatype("year", []); // Constraints //TODO: //primary key -export const foreginKey = (attrs:Attribute[],target:Attribute[])=>{}; -export const uniqueKey = (attr:Attribute[])=>{}; -export const check = (name:string,mod:BooleanModifier)=>new checkConstraint(name,mod); +export const foreginKey = (attrs: Attribute[], target: Attribute[]) => { }; +export const uniqueKey = (attr: Attribute[]) => { }; +export const check = (name: string, mod: BooleanModifier) => new checkConstraint(name, mod); /** diff --git a/src/query.ts b/src/query.ts index 3debfff..c6e04d1 100644 --- a/src/query.ts +++ b/src/query.ts @@ -7,7 +7,7 @@ import { primaryData, selectElements, selectFromElements, serializeReturn } from export class Query { sql: string; values: primaryData[]; - constructor([sql, values]:serializeReturn) { + constructor([sql, values]: serializeReturn) { this.sql = sql; this.values = values; } @@ -16,7 +16,7 @@ export class Query { export class QueryBuilder { //injekt and data list: ([boolean, primaryData])[] = []; - + constructor(l?: ({ inject?: boolean, data: primaryData })[]) { if (Array.isArray(l)) for (let i = 0; i < l.length; i++) { @@ -31,7 +31,7 @@ export class QueryBuilder { for (let i = 0; i < data.length; i++) { const e = data[i]; this.list.push([false, e]); - if(i+1any){