First Working Version #1

Merged
jusax23 merged 14 commits from dev into main 2023-02-14 23:33:55 +01:00
5 changed files with 220 additions and 81 deletions
Showing only changes of commit ea3100c4ae - Show all commits

View file

@ -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;

View file

@ -53,14 +53,14 @@ export interface Constraint{
name: string;
serialize(handler: Handler): QueryBuilder;
uses(attr: Attribute): boolean;
check(attr:Table): boolean | string;
check(table: Table): boolean | string;
}
export class checkConstraint implements Constraint {
checkQuery: BooleanModifier;
name: string;
constructor(name: string, check: BooleanModifier) {
this.name = name;
this.name = name.toLowerCase();
this.checkQuery = check;
}
check(attr: Table): boolean {
@ -78,12 +78,13 @@ export class uniqueConstraint implements Constraint{
name: string;
attrs: Attribute[];
constructor(name: string, attrs: Attribute[]) {
this.name = name;
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";
if (this.attrs[i].table != table) return "Referencing Attributes must be in host Table.";
}
return false;
}
@ -95,3 +96,32 @@ export class uniqueConstraint implements Constraint{
}
}
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;
}
}

View file

@ -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;
},