From e33dbcdfcac23418d871d52d893d6d740249db97 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Mon, 23 Jan 2023 22:10:33 +0100 Subject: [PATCH 01/14] add limit --- readme.md | 3 +++ src/db.ts | 36 ++++++++++++++++++------------------ src/dbStructure.ts | 26 +++++++++++++------------- src/defaultHandler.ts | 20 ++++++++++++-------- src/funcs.ts | 22 +++++++++++----------- src/query.ts | 23 ++++++++++++++--------- src/types.ts | 2 +- 7 files changed, 72 insertions(+), 60 deletions(-) diff --git a/readme.md b/readme.md index 72fa516..197a59e 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,4 @@ +# DBlang +sql Querys with js ot ts Syntax. + [![status-badge](https://ci.jusax.de/api/badges/jusax23/dblang/status.svg)](https://ci.jusax.de/jusax23/dblang) \ No newline at end of file diff --git a/src/db.ts b/src/db.ts index 1266a2a..d954eb5 100644 --- a/src/db.ts +++ b/src/db.ts @@ -16,39 +16,39 @@ export class DB { return Handler; } - newTable(name:string){ + newTable(name: string) { return new Table(name); } } export class Attribute { - name : string; - constructor(name: string){ + name: string; + constructor(name: string) { this.name = name; } - serialize(){ + serialize() { return this.toString(); } - toString(){ + toString() { return this.name; } } -export class Table{ - dbLangTableName : string; - dbLangTableAttributes:{ [key: string]: Attribute; } = {}; - [key:string]: Attribute | any - constructor(name: string){ +export class Table { + dbLangTableName: string; + dbLangTableAttributes: { [key: string]: Attribute; } = {}; + [key: string]: Attribute | any + constructor(name: string) { this.dbLangTableName = name; } - serialize(){ + serialize() { return this.toString(); } - toString(){ + toString() { return this.dbLangTableName; } - addAttribute(name:string,ops:{ + addAttribute(name: string, ops: { unique?: boolean, A_I?: boolean, default?: primaryData, @@ -59,12 +59,12 @@ export class Table{ onDelete?: onAction, onUpdate?: onAction } - },noErrorOnNameConflict = false){ + }, noErrorOnNameConflict = false) { let attr = new Attribute(name); this.dbLangTableAttributes[name] = attr; - if(["serialize", "toString","addAttribute","dbLangTableName","dbLangTableAttributes"].includes(name)){ - if(!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!"); - }else{ + if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes"].includes(name)) { + if (!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!"); + } else { this[name] = attr; } return attr; @@ -72,4 +72,4 @@ export class Table{ } export * from './funcs'; -export {onAction}; \ No newline at end of file +export { onAction }; \ No newline at end of file diff --git a/src/dbStructure.ts b/src/dbStructure.ts index 186b08e..ac3f88b 100644 --- a/src/dbStructure.ts +++ b/src/dbStructure.ts @@ -5,35 +5,35 @@ import { allModifierInput, primaryData, serializeReturn } from "./types"; export abstract class Modifier { - t : string; - a : Array; + t: string; + a: Array; constructor(type: string, args: (allModifierInput)[]) { this.t = type; this.a = args; } - serialize(handler = Handler):serializeReturn { + serialize(handler = Handler): serializeReturn { return handler.modifiers[this.t as keyof typeof handler.modifiers](this.a); } } -export class BooleanModifier extends Modifier{} -export class NumberModifier extends Modifier{} -export class StringModifier extends Modifier{} +export class BooleanModifier extends Modifier { } +export class NumberModifier extends Modifier { } +export class StringModifier extends Modifier { } -export class Aggregation{ - t : string; - a : Attribute; +export class Aggregation { + t: string; + a: Attribute; constructor(type: string, args: Attribute) { this.t = type; this.a = args; } - serialize(handler = Handler):serializeReturn{ + serialize(handler = Handler): serializeReturn { return handler.aggregations[this.t as keyof typeof handler.aggregations](this.a); } } -export class Joins{ +export class Joins { - serialize(handler = Handler):serializeReturn { - return ["",[]]; + serialize(handler = Handler): serializeReturn { + return ["", []]; } } \ No newline at end of file diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 9299829..11c3ff2 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -13,18 +13,22 @@ export class Handler { if (q.whereD) { let whereS = q.whereD.serialize(this); args.push(...whereS[1]); - sql += " where "+whereS[0]; + sql += " where " + whereS[0]; } - if (q.groupByD.length>0) { + if (q.groupByD.length > 0) { let groupByS = joinArg(",", this)(q.groupByD); args.push(...groupByS[1]); - sql += " group by "+groupByS[0]; + sql += " group by " + groupByS[0]; if (q.havingD) { let havingS = q.havingD.serialize(this); args.push(...havingS[1]); - sql += " having "+havingS[0]; + sql += " having " + havingS[0]; } } + if (q.limitD != null) { + sql += " limit ?"; + args.push(q.limitD); + } return [sql, args]; } @@ -44,7 +48,7 @@ export class Handler { eq: joinArg("=", this), plus: joinArg("+", this), minus: joinArg("-", this), - not: (a: allModifierInput[]):serializeReturn => { + not: (a: allModifierInput[]): serializeReturn => { let e = a[0]; if (e instanceof Attribute) return ["not (" + e + ")", []]; if (e instanceof Modifier || e instanceof selectQuery || e instanceof Aggregation) { @@ -60,16 +64,16 @@ export class Handler { function joinArg(type: string, s: any) { return (a: (allModifierInput)[]): serializeReturn => { let args: primaryData[] = []; - let sql = "(" + a.map(d => { + let sql = a.map(d => { if (d instanceof Attribute) return d; if (d instanceof Modifier || d instanceof selectQuery || d instanceof Aggregation) { let [sqli, argsi] = d.serialize(s); args.push(...(argsi.flat(Infinity))); - return sqli; + return "("+sqli+")"; } args.push(d); return "?"; - }).join(" " + type + " ") + ")"; + }).join(" " + type + " "); return [sql, args] } diff --git a/src/funcs.ts b/src/funcs.ts index 700f537..bb61e1a 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -4,19 +4,19 @@ import { selectQuery } from "./query"; import { allModifierInput, selectElements, selectFromElements } from "./types"; //modifiers -export const and = (...args: (BooleanModifier)[]) => new BooleanModifier("and",args); -export const or = (...args: (BooleanModifier)[]) => new BooleanModifier("or",args); -export const eq = (...args: (allModifierInput)[]) => new BooleanModifier("eq",args); -export const plus = (...args: (allModifierInput)[]) => new NumberModifier("plus",args); -export const minus = (...args: (allModifierInput)[]) => new NumberModifier("minus",args); +export const and = (...args: (BooleanModifier)[]) => new BooleanModifier("and", args); +export const or = (...args: (BooleanModifier)[]) => new BooleanModifier("or", args); +export const eq = (...args: (allModifierInput)[]) => new BooleanModifier("eq", args); +export const plus = (...args: (allModifierInput)[]) => new NumberModifier("plus", args); +export const minus = (...args: (allModifierInput)[]) => new NumberModifier("minus", args); //aggregations -export const count = (a:Attribute) => new Aggregation("count",a); -export const sum = (a:Attribute) => new Aggregation("sum",a); -export const avg = (a:Attribute) => new Aggregation("avg",a); -export const min = (a:Attribute) => new Aggregation("min",a); -export const max = (a:Attribute) => new Aggregation("max",a); +export const count = (a: Attribute) => new Aggregation("count", a); +export const sum = (a: Attribute) => new Aggregation("sum", a); +export const avg = (a: Attribute) => new Aggregation("avg", a); +export const min = (a: Attribute) => new Aggregation("min", a); +export const max = (a: Attribute) => new Aggregation("max", a); //query -export const select = (args: selectElements[],from: selectFromElements) => new selectQuery(args,from); +export const select = (args: selectElements[], from: selectFromElements) => new selectQuery(args, from); diff --git a/src/query.ts b/src/query.ts index 10e6252..b0f9c86 100644 --- a/src/query.ts +++ b/src/query.ts @@ -5,8 +5,8 @@ import { primaryData, selectElements, selectFromElements, serializeReturn } from export class Query { - sql : string; - values : primaryData[]; + sql: string; + values: primaryData[]; constructor(sql: string, values: primaryData[]) { this.sql = sql; this.values = values; @@ -14,13 +14,13 @@ export class Query { } export class selectQuery { - attr:selectElements[] = []; - from:selectFromElements; - constructor(a: selectElements[], from:selectFromElements) { + attr: selectElements[] = []; + from: selectFromElements; + constructor(a: selectElements[], from: selectFromElements) { this.attr.push(...a.flat(Infinity)); this.from = from ? from : null; } - whereD:BooleanModifier | null = null; + whereD: BooleanModifier | null = null; where(m: BooleanModifier) { this.whereD = m; return this; @@ -30,18 +30,23 @@ export class selectQuery { this.groupByD = a; return this; } - havingD:Modifier | null = null; + havingD: Modifier | null = null; having(m: Modifier) { this.havingD = m; return this; } + limitD: number | null = null; + limit(i: number) { + this.limitD = i; + return this; + } - serialize(handler = Handler) : serializeReturn { + serialize(handler = Handler): serializeReturn { return handler.querys.select(this); } query(db: DB) { const s = this.serialize(db.getHandler()); - return new Query(s[0],s[1]); + return new Query(s[0], s[1]); } } \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 0ecfc4e..24adafb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,7 +11,7 @@ export type selectFromElements = Table | Joins | null; export type serializeReturn = [string, primaryData[]]; -export enum onAction{ +export enum onAction { cascade, noAction, setNull, From 6f97f42d49579d3ddd24bac5a94da754a30fd617 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 24 Jan 2023 10:40:16 +0100 Subject: [PATCH 02/14] Datatypes --- .gitignore | 3 ++- src/db.ts | 27 ++++++++++----------- src/dbStructure.ts | 12 +++++++++- src/defaultHandler.ts | 56 +++++++++++++++++++++++++++++++++++++++++++ src/funcs.ts | 42 ++++++++++++++++++++++++++++++-- src/types.ts | 12 ++++++++++ 6 files changed, 133 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index a34ff48..a3c17fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ dist node_modules -ltests \ No newline at end of file +ltests +sqlsave \ No newline at end of file diff --git a/src/db.ts b/src/db.ts index d954eb5..e83170d 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,7 +1,8 @@ import mariadb from 'mariadb'; +import { Datatype } from './dbStructure'; import { Handler } from './defaultHandler'; import { Query } from './query'; -import { onAction, primaryData } from './types'; +import { attributeSettings, onAction, primaryData } from './types'; export class DB { @@ -23,8 +24,15 @@ export class DB { export class Attribute { name: string; - constructor(name: string) { + ops: attributeSettings; + type: Datatype; + constructor(name: string, type: Datatype, ops: attributeSettings) { this.name = name; + this.ops = ops; + this.type = type; + } + serializeDatatype(){ + return this.type.serialize(); } serialize() { return this.toString(); @@ -48,19 +56,8 @@ export class Table { toString() { return this.dbLangTableName; } - addAttribute(name: string, ops: { - unique?: boolean, - A_I?: boolean, - default?: primaryData, - notNull?: boolean - primaryKey?: boolean, - foreginKey?: { - link: Attribute, - onDelete?: onAction, - onUpdate?: onAction - } - }, noErrorOnNameConflict = false) { - let attr = new Attribute(name); + addAttribute(name: string, type: Datatype, ops: attributeSettings, noErrorOnNameConflict = false) { + let attr = new Attribute(name,type,ops); this.dbLangTableAttributes[name] = attr; if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes"].includes(name)) { if (!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!"); diff --git a/src/dbStructure.ts b/src/dbStructure.ts index ac3f88b..4d63ebc 100644 --- a/src/dbStructure.ts +++ b/src/dbStructure.ts @@ -2,7 +2,17 @@ import { Attribute } from "./db"; import { Handler } from "./defaultHandler"; import { allModifierInput, primaryData, serializeReturn } from "./types"; - +export class Datatype{ + type: string; + args: primaryData[]; + constructor(type:string, args: primaryData[]){ + this.type = type; + this.args = args; + } + serialize(handler = Handler): serializeReturn{ + return handler.datatypes[this.type as keyof typeof handler.datatypes](this.args); + } +} export abstract class Modifier { t: string; diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 11c3ff2..702f7b8 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -58,9 +58,65 @@ export class Handler { return ["not (?)", [e]]; } } + static 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[]): serializeReturn =>{ + return ["enum("+a.map(()=>"?").join(", ")+")",a]; + }, + set: (a: primaryData[]): serializeReturn =>{ + return ["set("+a.map(()=>"?").join(", ")+")",a]; + }, + 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"), + + data: dataTypeNoArg("data"), + datatime: dataTypeNoArg("datatime"), + timestamp: dataTypeNoArg("timestamp"), + time: dataTypeNoArg("time"), + year: dataTypeNoArg("year"), + } }; +function dataTypeNoArg(type:string){ + return (a : primaryData[]): serializeReturn =>{ + return [type,[]]; + } +} +function dataTypeSingleNum(type:string){ + return (a : primaryData[]): serializeReturn =>{ + return [type+"(?)",[a[0]]]; + } +} +function dataTypeDblNum(type:string){ + return (a : primaryData[]): serializeReturn =>{ + return [type+"(?,?)",[a[0],a[1]]]; + } +} + + function joinArg(type: string, s: any) { return (a: (allModifierInput)[]): serializeReturn => { let args: primaryData[] = []; diff --git a/src/funcs.ts b/src/funcs.ts index bb61e1a..1c117cc 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -1,7 +1,7 @@ import { Attribute } from "./db"; -import { Aggregation, BooleanModifier, NumberModifier } from "./dbStructure"; +import { Aggregation, BooleanModifier, Datatype, NumberModifier } from "./dbStructure"; import { selectQuery } from "./query"; -import { allModifierInput, selectElements, selectFromElements } from "./types"; +import { allModifierInput, primaryData, selectElements, selectFromElements } from "./types"; //modifiers export const and = (...args: (BooleanModifier)[]) => new BooleanModifier("and", args); @@ -20,3 +20,41 @@ export const max = (a: Attribute) => new Aggregation("max", a); //query export const select = (args: selectElements[], from: selectFromElements) => new selectQuery(args, from); + +//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 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 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 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 DATA = new Datatype("data",[]); +export const DATATIME = new Datatype("datatime",[]); +export const TIMESTAMP = new Datatype("timestamp",[]); +export const TIME = new Datatype("time",[]); +export const YEAR = new Datatype("year",[]); \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 24adafb..bab3058 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,6 +10,18 @@ export type selectFromElements = Table | Joins | null; export type serializeReturn = [string, primaryData[]]; +export type attributeSettings = { + unique?: boolean, + A_I?: boolean, + default?: primaryData, + notNull?: boolean + primaryKey?: boolean, + foreginKey?: { + link: Attribute, + onDelete?: onAction, + onUpdate?: onAction + } +}; export enum onAction { cascade, From 6eae7f0fadeedbad8e524709cbf95a8209f126ea Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 24 Jan 2023 20:34:03 +0100 Subject: [PATCH 03/14] added create Table --- src/db.ts | 61 +++++++++++------ src/dbStructure.ts | 12 ++-- src/defaultHandler.ts | 152 ++++++++++++++++++++++++++++++------------ src/funcs.ts | 12 ++-- src/query.ts | 6 +- src/types.ts | 2 +- 6 files changed, 168 insertions(+), 77 deletions(-) diff --git a/src/db.ts b/src/db.ts index e83170d..31767fc 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,44 +1,65 @@ import mariadb from 'mariadb'; import { Datatype } from './dbStructure'; import { Handler } from './defaultHandler'; -import { Query } from './query'; -import { attributeSettings, onAction, primaryData } from './types'; +import { Query, selectQuery } from './query'; +import { attributeSettings, onAction, primaryData, serializeReturn } from './types'; export class DB { + tables:Table[] = []; + handler: Handler; //pool:mariadb.Pool; constructor(/*{ host, user, password, database, connectionLimit = 5 }*/) { //this.pool = mariadb.createPool({ host, user, password, database, connectionLimit, multipleStatements: true }); + this.handler = new Handler(); } async query(query: Query) { + console.log(query); //return this.pool.query(query); } getHandler() { - return Handler; + return this.handler; + } + + sync(){ + let handler = this.getHandler(); + this.tables.forEach(t=>{ + console.log(handler.querys.create(handler,t)); + }) } newTable(name: string) { - return new Table(name); + let tabel = new Table(name,this); + this.tables.push(tabel); + return tabel; } } export class Attribute { name: string; + table: Table; ops: attributeSettings; type: Datatype; - constructor(name: string, type: Datatype, ops: attributeSettings) { + constructor(name: string, table:Table, type: Datatype, ops: attributeSettings) { this.name = name; this.ops = ops; this.type = type; + this.table = table; } - serializeDatatype(){ - return this.type.serialize(); + serializeDatatype(handler : Handler){ + return this.type.serialize(handler); } - serialize() { - return this.toString(); + serializeSettings(handler : Handler){ + return handler.builders.attributeSettings(handler,this); } - toString() { - return this.name; + serialize(handler : Handler) { + return handler.builders.escapeID(this.name); + } + toString(handler : Handler = this.table.dbLangDatabaseInstance.getHandler()) { + return this.table.serialize(handler)+"."+this.serialize(handler); + } + toStringFunc(handler : Handler){ + return this.table.serialize(handler)+"("+this.serialize(handler)+")"; } } @@ -46,20 +67,22 @@ export class Attribute { export class Table { dbLangTableName: string; dbLangTableAttributes: { [key: string]: Attribute; } = {}; + dbLangDatabaseInstance: DB; [key: string]: Attribute | any - constructor(name: string) { + constructor(name: string, table:DB) { this.dbLangTableName = name; + this.dbLangDatabaseInstance = table; } - serialize() { - return this.toString(); + serialize(handler : Handler) { + return this.toString(handler); } - toString() { - return this.dbLangTableName; + toString(handler : Handler = this.dbLangDatabaseInstance.getHandler()) { + return handler.builders.escapeID(this.dbLangTableName); } - addAttribute(name: string, type: Datatype, ops: attributeSettings, noErrorOnNameConflict = false) { - let attr = new Attribute(name,type,ops); + addAttribute(name: string, type: Datatype, ops: attributeSettings = {}, noErrorOnNameConflict = false) { + let attr = new Attribute(name,this,type,ops); this.dbLangTableAttributes[name] = attr; - if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes"].includes(name)) { + if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes", "dbLangDatabaseInstance"].includes(name)) { if (!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!"); } else { this[name] = attr; diff --git a/src/dbStructure.ts b/src/dbStructure.ts index 4d63ebc..0dee547 100644 --- a/src/dbStructure.ts +++ b/src/dbStructure.ts @@ -9,7 +9,7 @@ export class Datatype{ this.type = type; this.args = args; } - serialize(handler = Handler): serializeReturn{ + serialize(handler : Handler): serializeReturn{ return handler.datatypes[this.type as keyof typeof handler.datatypes](this.args); } } @@ -21,8 +21,8 @@ export abstract class Modifier { this.t = type; this.a = args; } - serialize(handler = Handler): serializeReturn { - return handler.modifiers[this.t as keyof typeof handler.modifiers](this.a); + serialize(handler : Handler): serializeReturn { + return handler.modifiers[this.t as keyof typeof handler.modifiers](handler,this.a); } } @@ -37,13 +37,13 @@ export class Aggregation { this.t = type; this.a = args; } - serialize(handler = Handler): serializeReturn { - return handler.aggregations[this.t as keyof typeof handler.aggregations](this.a); + serialize(handler : Handler): serializeReturn { + return handler.aggregations[this.t as keyof typeof handler.aggregations](handler,this.a); } } export class Joins { - serialize(handler = Handler): serializeReturn { + serialize(handler : Handler): serializeReturn { return ["", []]; } } \ No newline at end of file diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 702f7b8..635de1e 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -1,26 +1,30 @@ -import { Attribute } from "./db" +import { Attribute, DB, Table } from "./db" import { Aggregation, Modifier } from "./dbStructure" import { selectQuery } from "./query" import { allModifierInput, primaryData, serializeReturn } from "./types" export class Handler { - static querys = { - select: (q: selectQuery): serializeReturn => { + syncDB(db : DB){ + + } + + querys = { + select: (handler:Handler,q: selectQuery): serializeReturn => { let args: primaryData[] = []; - let w = joinArg(", ", this)(q.attr); + let w = joinArg(", ")(handler,q.attr); args.push(...w[1]); - let sql = `select ${w[0]} from ${q.from == null ? 'DUAL' : q.from.serialize(this)}`; + let sql = `select ${w[0]} from ${q.from == null ? 'DUAL' : q.from.serialize(handler)}`; if (q.whereD) { - let whereS = q.whereD.serialize(this); + let whereS = q.whereD.serialize(handler); args.push(...whereS[1]); sql += " where " + whereS[0]; } if (q.groupByD.length > 0) { - let groupByS = joinArg(",", this)(q.groupByD); + let groupByS = joinArg(",")(handler,q.groupByD); args.push(...groupByS[1]); sql += " group by " + groupByS[0]; if (q.havingD) { - let havingS = q.havingD.serialize(this); + let havingS = q.havingD.serialize(handler); args.push(...havingS[1]); sql += " having " + havingS[0]; } @@ -31,34 +35,93 @@ export class Handler { } return [sql, args]; + }, + create:(handler:Handler,table: Table): serializeReturn=>{ + let args:primaryData[] = []; + let sql = `create table if not exists ${table.toString(handler)}( +${Object.entries(table.dbLangTableAttributes).map(([_,a])=>{ + let atype = a.serializeSettings(handler); + args.push(...(atype[1])); + return a.serialize(handler)+" "+atype[0]; + }).join(",\n")} +)`; + return [sql,args]; } } - static aggregations = { - count: (a: Attribute): serializeReturn => ["count(" + a + ")", []], - sum: (a: Attribute): serializeReturn => ["sum(" + a + ")", []], - avg: (a: Attribute): serializeReturn => ["avg(" + a + ")", []], - min: (a: Attribute): serializeReturn => ["min(" + a + ")", []], - max: (a: Attribute): serializeReturn => ["max(" + a + ")", []], + + builders = { + attributeSettings: (handler:Handler,a: Attribute): serializeReturn => { + let dtype = a.type.serialize(handler); + let sql = ""+dtype[0]; + let args:primaryData[] = [...dtype[1]]; + if (a.ops.autoIncrement) { + sql += " auto_increment"; + } + if (a.ops.primaryKey) { + sql += " primary key"; + } + 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)}`); + sql += " default ?"; + args.push(a.ops.default); + } + + if (a.ops.unique != null) { + if (!a.ops.autoIncrement && !a.ops.primaryKey){ + sql += " unique"; + } + } + if (a.ops.notNull != null) { + if (!a.ops.autoIncrement && !a.ops.primaryKey && a.ops.default == null){ + sql += " not null"; + } + } + + if (a.ops.foreginKey != null) { + sql += ` foreign key references (${a.ops.foreginKey.link.toStringFunc(handler)})` + } + + return [sql, args]; + }, + 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, '``')}\``; + } } - static modifiers = { - and: joinArg("and", this), - or: joinArg("or", this), - eq: joinArg("=", this), - plus: joinArg("+", this), - minus: joinArg("-", this), - not: (a: allModifierInput[]): serializeReturn => { + + aggregations = { + count: (handler:Handler,a: Attribute): serializeReturn => ["count(" + a.toString(handler) + ")", []], + sum: (handler:Handler,a: Attribute): serializeReturn => ["sum(" + a.toString(handler) + ")", []], + avg: (handler:Handler,a: Attribute): serializeReturn => ["avg(" + a.toString(handler) + ")", []], + min: (handler:Handler,a: Attribute): serializeReturn => ["min(" + a.toString(handler) + ")", []], + max: (handler:Handler,a: Attribute): serializeReturn => ["max(" + a.toString(handler) + ")", []], + } + + modifiers = { + and: joinArg("and"), + or: joinArg("or"), + le: joinArg("<"), + leq: joinArg("<="), + eq: joinArg("="), + geq: joinArg(">="), + ge: joinArg(">"), + plus: joinArg("+"), + minus: joinArg("-"), + not: (handler:Handler,a: allModifierInput[]): serializeReturn => { let e = a[0]; - if (e instanceof Attribute) return ["not (" + e + ")", []]; + if (e instanceof Attribute) return ["not (" + e.toString(handler) + ")", []]; if (e instanceof Modifier || e instanceof selectQuery || e instanceof Aggregation) { - let [sqli, argsi] = e.serialize(this); + let [sqli, argsi] = e.serialize(handler); return ["not (" + sqli + ")", argsi] } return ["not (?)", [e]]; } } - static datatypes = { + datatypes = { char: dataTypeSingleNum("char"), varchar: dataTypeSingleNum("varchar"), binary: dataTypeSingleNum("binary"), @@ -73,11 +136,12 @@ export class Handler { mediumtext: dataTypeNoArg("mediumtext"), longtext: dataTypeNoArg("longtext"), - enum: (a: primaryData[]): serializeReturn =>{ - return ["enum("+a.map(()=>"?").join(", ")+")",a]; + enum: (a: primaryData[]): serializeReturn => { + console.log(a); + return ["enum(" + a.map(() => "?").join(", ") + ")", a]; }, - set: (a: primaryData[]): serializeReturn =>{ - return ["set("+a.map(()=>"?").join(", ")+")",a]; + set: (a: primaryData[]): serializeReturn => { + return ["set(" + a.map(() => "?").join(", ") + ")", a]; }, bool: dataTypeNoArg("bool"), bit: dataTypeNoArg("bit"), @@ -91,8 +155,8 @@ export class Handler { double: dataTypeDblNum("double"), decimal: dataTypeDblNum("decimal"), - data: dataTypeNoArg("data"), - datatime: dataTypeNoArg("datatime"), + date: dataTypeNoArg("data"), + datetime: dataTypeNoArg("datatime"), timestamp: dataTypeNoArg("timestamp"), time: dataTypeNoArg("time"), year: dataTypeNoArg("year"), @@ -100,32 +164,32 @@ export class Handler { }; -function dataTypeNoArg(type:string){ - return (a : primaryData[]): serializeReturn =>{ - return [type,[]]; +function dataTypeNoArg(type: string) { + return (a: primaryData[]): serializeReturn => { + return [type, []]; } } -function dataTypeSingleNum(type:string){ - return (a : primaryData[]): serializeReturn =>{ - return [type+"(?)",[a[0]]]; +function dataTypeSingleNum(type: string) { + return (a: primaryData[]): serializeReturn => { + return [type + "(?)", [a[0]]]; } } -function dataTypeDblNum(type:string){ - return (a : primaryData[]): serializeReturn =>{ - return [type+"(?,?)",[a[0],a[1]]]; +function dataTypeDblNum(type: string) { + return (a: primaryData[]): serializeReturn => { + return [type + "(?,?)", [a[0], a[1]]]; } } -function joinArg(type: string, s: any) { - return (a: (allModifierInput)[]): serializeReturn => { +function joinArg(type: string) { + return (handler:Handler,a: (allModifierInput)[]): serializeReturn => { let args: primaryData[] = []; let sql = a.map(d => { - if (d instanceof Attribute) return d; + if (d instanceof Attribute) return d.toString(handler); if (d instanceof Modifier || d instanceof selectQuery || d instanceof Aggregation) { - let [sqli, argsi] = d.serialize(s); + let [sqli, argsi] = d.serialize(handler); args.push(...(argsi.flat(Infinity))); - return "("+sqli+")"; + return "(" + sqli + ")"; } args.push(d); return "?"; diff --git a/src/funcs.ts b/src/funcs.ts index 1c117cc..1092389 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -6,7 +6,11 @@ import { allModifierInput, primaryData, selectElements, selectFromElements } fro //modifiers export const and = (...args: (BooleanModifier)[]) => new BooleanModifier("and", args); export const or = (...args: (BooleanModifier)[]) => new BooleanModifier("or", args); +export const ge = (...args: (allModifierInput)[]) => new BooleanModifier("ge", args); +export const geq = (...args: (allModifierInput)[]) => new BooleanModifier("geq", args); export const eq = (...args: (allModifierInput)[]) => new BooleanModifier("eq", args); +export const leq = (...args: (allModifierInput)[]) => new BooleanModifier("leq", args); +export const le = (...args: (allModifierInput)[]) => new BooleanModifier("le", args); export const plus = (...args: (allModifierInput)[]) => new NumberModifier("plus", args); export const minus = (...args: (allModifierInput)[]) => new NumberModifier("minus", args); @@ -38,8 +42,8 @@ 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",[]); @@ -53,8 +57,8 @@ 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 DATA = new Datatype("data",[]); -export const DATATIME = new Datatype("datatime",[]); +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",[]); \ No newline at end of file diff --git a/src/query.ts b/src/query.ts index b0f9c86..4567a83 100644 --- a/src/query.ts +++ b/src/query.ts @@ -41,12 +41,12 @@ export class selectQuery { return this; } - serialize(handler = Handler): serializeReturn { - return handler.querys.select(this); + serialize(handler : Handler): serializeReturn { + return handler.querys.select(handler,this); } query(db: DB) { const s = this.serialize(db.getHandler()); - return new Query(s[0], s[1]); + return db.query(new Query(s[0], s[1])); } } \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index bab3058..4d3c025 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,7 +12,7 @@ export type serializeReturn = [string, primaryData[]]; export type attributeSettings = { unique?: boolean, - A_I?: boolean, + autoIncrement?: boolean, default?: primaryData, notNull?: boolean primaryKey?: boolean, From a926960e11cc514aad13f0b94107c66af74288b2 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Sun, 29 Jan 2023 21:19:32 +0100 Subject: [PATCH 04/14] upload only zip --- .woodpecker.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index c946d1e..ef8339a 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -9,7 +9,12 @@ pipeline: - npm install - npm install git+https://jusax.de/git/jusax23/gitea-release.git - mkdir build + - mkdir dblang + - mkdir upload - npm run prepublish - ls dist - - npx gitea-release "https://jusax.de/git/api/v1/" "$${GITEA_TOKEN}" "$${CI_REPO}" "$${CI_COMMIT_BRANCH}" "$${CI_COMMIT_TAG}" "dist" "$${CI_COMMIT_MESSAGE}" + - cp dist/* dblang + - zip -r upload/DBlang.zip dblang/* + - tar -czvf upload/DBlang.tar.gz dblang/* + - npx gitea-release "https://jusax.de/git/api/v1/" "$${GITEA_TOKEN}" "$${CI_REPO}" "$${CI_COMMIT_BRANCH}" "$${CI_COMMIT_TAG}" "upload" "$${CI_COMMIT_MESSAGE}" secrets: [ gitea_token ] \ No newline at end of file From dd2098eab621e21bd1395f0b923410ca4377a481 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Sun, 29 Jan 2023 21:21:45 +0100 Subject: [PATCH 05/14] fix ci --- .woodpecker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index ef8339a..8d3ec32 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -5,7 +5,7 @@ pipeline: build: image: node:18-alpine commands: - - apk add git + - apk add git zip tar - npm install - npm install git+https://jusax.de/git/jusax23/gitea-release.git - mkdir build From 37385589d67214767afbd8decb4a9a8dbe0170ed Mon Sep 17 00:00:00 2001 From: jusax23 Date: Sun, 29 Jan 2023 22:11:24 +0100 Subject: [PATCH 06/14] lots of changes --- .woodpecker.yml | 1 - src/db.ts | 38 +++++-- src/dbStructure.ts | 60 +++++++++-- src/defaultHandler.ts | 229 ++++++++++++++++++++++++++---------------- src/funcs.ts | 19 +++- src/query.ts | 40 +++++++- src/types.ts | 21 +++- 7 files changed, 300 insertions(+), 108 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 8d3ec32..133f850 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -12,7 +12,6 @@ pipeline: - mkdir dblang - mkdir upload - npm run prepublish - - ls dist - cp dist/* dblang - zip -r upload/DBlang.zip dblang/* - tar -czvf upload/DBlang.tar.gz dblang/* diff --git a/src/db.ts b/src/db.ts index 31767fc..b7187fd 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,17 +1,19 @@ import mariadb from 'mariadb'; -import { Datatype } from './dbStructure'; +import { checkConstraint, Constraint, Datatype, uniqueConstraint } from './dbStructure'; import { Handler } from './defaultHandler'; import { Query, selectQuery } from './query'; -import { attributeSettings, onAction, primaryData, serializeReturn } from './types'; +import { attributeSettings, extendedAttributeSettings, onAction, primaryData, serializeReturn } from './types'; export class DB { tables:Table[] = []; handler: Handler; + name: string; //pool:mariadb.Pool; constructor(/*{ host, user, password, database, connectionLimit = 5 }*/) { //this.pool = mariadb.createPool({ host, user, password, database, connectionLimit, multipleStatements: true }); this.handler = new Handler(); + this.name = "notimplemented" } async query(query: Query) { console.log(query); @@ -24,7 +26,7 @@ export class DB { sync(){ let handler = this.getHandler(); this.tables.forEach(t=>{ - console.log(handler.querys.create(handler,t)); + console.log(new Query(handler.builders.query(handler.querys.create(handler,t)))); }) } @@ -45,6 +47,19 @@ export class Attribute { this.ops = ops; this.type = type; this.table = table; + + if(ops.check != null){ + table.addConstraint(new checkConstraint( + table.dbLangDatabaseInstance.name+"_"+table.dbLangTableName+"_"+name+"_check_constraint", + ops.check + )) + } + if(ops.unique != null){ + table.addConstraint(new uniqueConstraint( + table.dbLangDatabaseInstance.name+"_"+table.dbLangTableName+" "+name+"_unique_constraint", + [this] + )) + } } serializeDatatype(handler : Handler){ return this.type.serialize(handler); @@ -68,10 +83,11 @@ export class Table { dbLangTableName: string; dbLangTableAttributes: { [key: string]: Attribute; } = {}; dbLangDatabaseInstance: DB; + dbLangConstrains: Constraint[] = []; [key: string]: Attribute | any - constructor(name: string, table:DB) { + constructor(name: string, db:DB) { this.dbLangTableName = name; - this.dbLangDatabaseInstance = table; + this.dbLangDatabaseInstance = db; } serialize(handler : Handler) { return this.toString(handler); @@ -80,15 +96,25 @@ export class Table { return handler.builders.escapeID(this.dbLangTableName); } addAttribute(name: string, type: Datatype, ops: attributeSettings = {}, noErrorOnNameConflict = false) { + 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; - if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes", "dbLangDatabaseInstance"].includes(name)) { + if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes", "dbLangDatabaseInstance","addConstraint","addAttributes"].includes(name)) { if (!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!"); } else { this[name] = attr; } return attr; } + addAttributes(list:{[key: string]:extendedAttributeSettings}):{[key: string]:Attribute} { + return Object.fromEntries(Object.entries(list).map(([k,a])=>{ + return [k,this.addAttribute(k,a.type,a)]; + })); + } + addConstraint(c:Constraint){ + c.check(this); + this.dbLangConstrains.push(c); + } } export * from './funcs'; diff --git a/src/dbStructure.ts b/src/dbStructure.ts index 0dee547..d3ecde2 100644 --- a/src/dbStructure.ts +++ b/src/dbStructure.ts @@ -1,5 +1,6 @@ -import { Attribute } from "./db"; +import { Attribute, Table } from "./db"; import { Handler } from "./defaultHandler"; +import { QueryBuilder } from "./query"; import { allModifierInput, primaryData, serializeReturn } from "./types"; export class Datatype{ @@ -9,7 +10,7 @@ export class Datatype{ this.type = type; this.args = args; } - serialize(handler : Handler): serializeReturn{ + serialize(handler : Handler): QueryBuilder{ return handler.datatypes[this.type as keyof typeof handler.datatypes](this.args); } } @@ -21,7 +22,7 @@ export abstract class Modifier { this.t = type; this.a = args; } - serialize(handler : Handler): serializeReturn { + serialize(handler : Handler): QueryBuilder { return handler.modifiers[this.t as keyof typeof handler.modifiers](handler,this.a); } } @@ -37,13 +38,60 @@ export class Aggregation { this.t = type; this.a = args; } - serialize(handler : Handler): serializeReturn { + serialize(handler : Handler): QueryBuilder { return handler.aggregations[this.t as keyof typeof handler.aggregations](handler,this.a); } } export class Joins { - serialize(handler : Handler): serializeReturn { - return ["", []]; + 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 class checkConstraint implements Constraint{ + checkQuery:BooleanModifier; + name: string; + constructor(name:string, check: BooleanModifier){ + this.name = name; + this.checkQuery = check; + } + check(attr: Table): boolean { + throw new Error("Method not implemented."); + } + uses(attr: Attribute): boolean { + throw new Error("Method not implemented."); + } + serialize(handler: Handler): QueryBuilder { + throw new Error("Method not implemented."); + } +} + +export class uniqueConstraint implements Constraint{ + name: string; + attr: Attribute[]; + constructor(name:string, attr:Attribute[]){ + this.name = name; + this.attr = attr; + } + check(table: Table): boolean|string { + for(let i = 0; i < this.attr.length; i++){ + if(this.attr[i].ops.primaryKey) return "Can not combine unique Constraint and primary key"; + } + return false; + } + uses(attr: Attribute): boolean { + throw new Error("Method not implemented."); + } + serialize(handler: Handler): QueryBuilder { + throw new Error("Method not implemented."); + } + } \ No newline at end of file diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 635de1e..156ea49 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -1,92 +1,123 @@ import { Attribute, DB, Table } from "./db" import { Aggregation, Modifier } from "./dbStructure" -import { selectQuery } from "./query" +import { QueryBuilder, selectQuery } from "./query" import { allModifierInput, primaryData, serializeReturn } from "./types" export class Handler { - syncDB(db : DB){ + /*syncDB(db : DB){ - } + }*/ querys = { - select: (handler:Handler,q: selectQuery): serializeReturn => { - let args: primaryData[] = []; - let w = joinArg(", ")(handler,q.attr); - args.push(...w[1]); - let sql = `select ${w[0]} from ${q.from == null ? 'DUAL' : q.from.serialize(handler)}`; + select: (handler: Handler, q: selectQuery): QueryBuilder => { + const builder = new QueryBuilder(); + builder.addCode("select "); + builder.append(joinArg(", ")(handler, q.attr)); + builder.addCode(` from ${q.from == null ? 'DUAL' : q.from.serialize(handler)}`); if (q.whereD) { - let whereS = q.whereD.serialize(handler); - args.push(...whereS[1]); - sql += " where " + whereS[0]; + builder.addCode(" where "); + builder.append(q.whereD.serialize(handler)); } if (q.groupByD.length > 0) { - let groupByS = joinArg(",")(handler,q.groupByD); - args.push(...groupByS[1]); - sql += " group by " + groupByS[0]; + builder.addCode(" group by "); + builder.append(joinArg(",")(handler, q.groupByD)); if (q.havingD) { - let havingS = q.havingD.serialize(handler); - args.push(...havingS[1]); - sql += " having " + havingS[0]; + builder.addCode(" having "); + builder.append(q.havingD.serialize(handler)); } } - if (q.limitD != null) { - sql += " limit ?"; - args.push(q.limitD); - } - return [sql, args]; + if (q.havingD) { + builder.addCode(" limit "); + builder.addInjection(q.limitD); + } + return builder; }, - create:(handler:Handler,table: Table): serializeReturn=>{ - let args:primaryData[] = []; - let sql = `create table if not exists ${table.toString(handler)}( -${Object.entries(table.dbLangTableAttributes).map(([_,a])=>{ - let atype = a.serializeSettings(handler); - args.push(...(atype[1])); - return a.serialize(handler)+" "+atype[0]; - }).join(",\n")} -)`; - return [sql,args]; - } + 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; + }, + } + constraints = { + // check constraints + listPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + listForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + listUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + listChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + // add constraints + appPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + appForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + addUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + addChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + //should drop all keys to be able to recreate them + dropPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + dropForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + dropUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + dropChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + } builders = { - attributeSettings: (handler:Handler,a: Attribute): serializeReturn => { - let dtype = a.type.serialize(handler); - let sql = ""+dtype[0]; - let args:primaryData[] = [...dtype[1]]; + query: (qb: QueryBuilder): serializeReturn => { + 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 [sql, args]; + }, + + attributeSettings: (handler: Handler, a: Attribute): QueryBuilder => { + const builder = new QueryBuilder(); + builder.append(a.type.serialize(handler)); if (a.ops.autoIncrement) { - sql += " auto_increment"; + builder.addCode(" auto_increment"); } - if (a.ops.primaryKey) { + /*if (a.ops.primaryKey) { sql += " primary key"; - } + }*/ 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)}`); - sql += " default ?"; - args.push(a.ops.default); + builder.addCode(" default "); + builder.addInjection(a.ops.default); } - if (a.ops.unique != null) { + /*if (a.ops.unique != null) { if (!a.ops.autoIncrement && !a.ops.primaryKey){ sql += " unique"; } - } + }*/ if (a.ops.notNull != null) { - if (!a.ops.autoIncrement && !a.ops.primaryKey && a.ops.default == null){ - sql += " not null"; + if (!a.ops.autoIncrement && !a.ops.primaryKey && a.ops.default == null) { + builder.addCode(" not null"); } } - if (a.ops.foreginKey != null) { + /*if (a.ops.foreginKey != null) { sql += ` foreign key references (${a.ops.foreginKey.link.toStringFunc(handler)})` - } + }*/ - return [sql, args]; + return builder; }, - escapeID: (key:string) :string =>{ - if(!key || key === "" || key.includes('\u0000')) throw new Error("Can not escape empty key or with null unicode!"); + 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, '``')}\``; } @@ -94,11 +125,11 @@ ${Object.entries(table.dbLangTableAttributes).map(([_,a])=>{ aggregations = { - count: (handler:Handler,a: Attribute): serializeReturn => ["count(" + a.toString(handler) + ")", []], - sum: (handler:Handler,a: Attribute): serializeReturn => ["sum(" + a.toString(handler) + ")", []], - avg: (handler:Handler,a: Attribute): serializeReturn => ["avg(" + a.toString(handler) + ")", []], - min: (handler:Handler,a: Attribute): serializeReturn => ["min(" + a.toString(handler) + ")", []], - max: (handler:Handler,a: Attribute): serializeReturn => ["max(" + a.toString(handler) + ")", []], + 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) + ")" }]), } modifiers = { @@ -111,14 +142,21 @@ ${Object.entries(table.dbLangTableAttributes).map(([_,a])=>{ ge: joinArg(">"), plus: joinArg("+"), minus: joinArg("-"), - not: (handler:Handler,a: allModifierInput[]): serializeReturn => { + not: (handler: Handler, a: allModifierInput[]): QueryBuilder => { let e = a[0]; - if (e instanceof Attribute) return ["not (" + e.toString(handler) + ")", []]; + if (e instanceof Attribute) return new QueryBuilder([{ data: "not (" + e.toString(handler) + ")" }]) if (e instanceof Modifier || e instanceof selectQuery || e instanceof Aggregation) { - let [sqli, argsi] = e.serialize(handler); - return ["not (" + sqli + ")", argsi] + const builder = new QueryBuilder(); + builder.addCode("not ("); + builder.append(e.serialize(handler)); + builder.addCode(")"); + return builder; } - return ["not (?)", [e]]; + return new QueryBuilder([ + { data: "not(" }, + { inject: true, data: e }, + { data: ")" } + ]) } } datatypes = { @@ -136,12 +174,19 @@ ${Object.entries(table.dbLangTableAttributes).map(([_,a])=>{ mediumtext: dataTypeNoArg("mediumtext"), longtext: dataTypeNoArg("longtext"), - enum: (a: primaryData[]): serializeReturn => { - console.log(a); - return ["enum(" + a.map(() => "?").join(", ") + ")", a]; + enum: (a: primaryData[]): QueryBuilder => { + const builder = new QueryBuilder(); + builder.addCode("enum("); + builder.addInjectionCommaSeperated(a); + builder.addCode(")"); + return builder; }, - set: (a: primaryData[]): serializeReturn => { - return ["set(" + a.map(() => "?").join(", ") + ")", a]; + set: (a: primaryData[]): QueryBuilder => { + const builder = new QueryBuilder(); + builder.addCode("set("); + builder.addInjectionCommaSeperated(a); + builder.addCode(")"); + return builder; }, bool: dataTypeNoArg("bool"), bit: dataTypeNoArg("bit"), @@ -165,36 +210,50 @@ ${Object.entries(table.dbLangTableAttributes).map(([_,a])=>{ }; function dataTypeNoArg(type: string) { - return (a: primaryData[]): serializeReturn => { - return [type, []]; + return (a: primaryData[]): QueryBuilder => { + return new QueryBuilder([{ + inject: false, + data: type + }]); } } function dataTypeSingleNum(type: string) { - return (a: primaryData[]): serializeReturn => { - return [type + "(?)", [a[0]]]; + return (a: primaryData[]): QueryBuilder => { + return new QueryBuilder([ + { data: type + "(" }, + { inject: true, data: a[0] }, + { data: ")" } + ]); } } function dataTypeDblNum(type: string) { - return (a: primaryData[]): serializeReturn => { - return [type + "(?,?)", [a[0], a[1]]]; + 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)[]): serializeReturn => { - let args: primaryData[] = []; - let sql = a.map(d => { - if (d instanceof Attribute) return d.toString(handler); - if (d instanceof Modifier || d instanceof selectQuery || d instanceof Aggregation) { - let [sqli, argsi] = d.serialize(handler); - args.push(...(argsi.flat(Infinity))); - return "(" + sqli + ")"; + 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); } - args.push(d); - return "?"; - }).join(" " + type + " "); - return [sql, args] - + if (i + 1 < a.length) builder.addCode(" " + type + " "); + } + return builder; } } \ No newline at end of file diff --git a/src/funcs.ts b/src/funcs.ts index 1092389..a135fd7 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -1,5 +1,5 @@ import { Attribute } from "./db"; -import { Aggregation, BooleanModifier, Datatype, NumberModifier } from "./dbStructure"; +import { Aggregation, BooleanModifier, checkConstraint, Datatype, NumberModifier } from "./dbStructure"; import { selectQuery } from "./query"; import { allModifierInput, primaryData, selectElements, selectFromElements } from "./types"; @@ -61,4 +61,19 @@ 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",[]); \ No newline at end of file +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); + + +/** + * primary key: kein richtiger Constraint -> renew = drop + * foreign key: ? + * unique: richtiger Constraint -> achtung manchmal nicht entfernabr + * check: richtiger Constraint + */ \ No newline at end of file diff --git a/src/query.ts b/src/query.ts index 4567a83..4cf4e16 100644 --- a/src/query.ts +++ b/src/query.ts @@ -7,12 +7,41 @@ import { primaryData, selectElements, selectFromElements, serializeReturn } from export class Query { sql: string; values: primaryData[]; - constructor(sql: string, values: primaryData[]) { + constructor([sql, values]:serializeReturn) { this.sql = sql; this.values = values; } } +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++) { + const e = l[i]; + this.list.push([e.inject ? true : false, e.data]); + } + } + addCode(text: string) { + this.list.push([false, text]); + } + addInjection(data: primaryData) { + this.list.push([true, data]); + } + addInjectionCommaSeperated(data: primaryData[], comma = ", ") { + for (let i = 0; i < data.length; i++) { + const e = data[i]; + this.list.push([true, e]); + if(i+1 Date: Mon, 13 Feb 2023 23:44:08 +0100 Subject: [PATCH 07/14] step in evolution --- readme.md | 2 +- src/db.ts | 80 ++++++------- src/dbStructure.ts | 14 +-- src/defaultHandler.ts | 260 ++++++++++++++++++++++++++++++++++++------ src/query.ts | 25 +++- src/types.ts | 9 +- 6 files changed, 304 insertions(+), 86 deletions(-) diff --git a/readme.md b/readme.md index 197a59e..d023b05 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ # DBlang -sql Querys with js ot ts Syntax. +sql Querys with js or ts Syntax. [![status-badge](https://ci.jusax.de/api/badges/jusax23/dblang/status.svg)](https://ci.jusax.de/jusax23/dblang) \ No newline at end of file diff --git a/src/db.ts b/src/db.ts index b7187fd..5566360 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,37 +1,38 @@ import mariadb from 'mariadb'; import { checkConstraint, Constraint, Datatype, uniqueConstraint } from './dbStructure'; import { Handler } from './defaultHandler'; -import { Query, selectQuery } from './query'; -import { attributeSettings, extendedAttributeSettings, onAction, primaryData, serializeReturn } from './types'; +import { Query } from './query'; +import { attributeSettings, extendedAttributeSettings, onAction } from './types'; export class DB { - tables:Table[] = []; + tables: Table[] = []; handler: Handler; name: string; - //pool:mariadb.Pool; - constructor(/*{ host, user, password, database, connectionLimit = 5 }*/) { - //this.pool = mariadb.createPool({ host, user, password, database, connectionLimit, multipleStatements: true }); + pool: mariadb.Pool; + constructor({ host, user, password, database, connectionLimit = 5 }: { host: string, user: string, password: string, database: string, connectionLimit: number }) { + this.pool = mariadb.createPool({ host, user, password, database, connectionLimit, multipleStatements: true }); this.handler = new Handler(); - this.name = "notimplemented" + this.name = database; } async query(query: Query) { console.log(query); - //return this.pool.query(query); + return await this.pool.query(query); } getHandler() { return this.handler; } - sync(){ + async sync() { let handler = this.getHandler(); - this.tables.forEach(t=>{ - console.log(new Query(handler.builders.query(handler.querys.create(handler,t)))); - }) + await handler.syncDB(this, handler); + //this.tables.forEach(t=>{ + // console.log(handler.builders.query(handler.querys.create(handler,t))); + //}) } newTable(name: string) { - let tabel = new Table(name,this); + let tabel = new Table(name, this); this.tables.push(tabel); return tabel; } @@ -42,39 +43,40 @@ export class Attribute { table: Table; ops: attributeSettings; type: Datatype; - constructor(name: string, table:Table, type: Datatype, ops: attributeSettings) { + constructor(name: string, table: Table, type: Datatype, ops: attributeSettings) { this.name = name; - this.ops = ops; this.type = type; this.table = table; - - if(ops.check != null){ + this.ops = ops; + + if (ops.check != null) { + if(typeof ops.check == "function")ops.check = ops.check(this); table.addConstraint(new checkConstraint( - table.dbLangDatabaseInstance.name+"_"+table.dbLangTableName+"_"+name+"_check_constraint", + table.dbLangDatabaseInstance.name + "_" + table.dbLangTableName + "_" + name + "_check_constraint", ops.check )) } - if(ops.unique != null){ + 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] )) } } - serializeDatatype(handler : Handler){ + serializeDatatype(handler: Handler) { return this.type.serialize(handler); } - serializeSettings(handler : Handler){ - return handler.builders.attributeSettings(handler,this); + serializeSettings(handler: Handler) { + return handler.builders.attributeSettings(handler, this); } - serialize(handler : Handler) { + serialize(handler: Handler) { return handler.builders.escapeID(this.name); } - toString(handler : Handler = this.table.dbLangDatabaseInstance.getHandler()) { - return this.table.serialize(handler)+"."+this.serialize(handler); + toString(handler: Handler = this.table.dbLangDatabaseInstance.getHandler()) { + return this.table.serialize(handler) + "." + this.serialize(handler); } - toStringFunc(handler : Handler){ - return this.table.serialize(handler)+"("+this.serialize(handler)+")"; + toStringFunc(handler: Handler) { + return this.table.serialize(handler) + "(" + this.serialize(handler) + ")"; } } @@ -84,34 +86,34 @@ export class Table { dbLangTableAttributes: { [key: string]: Attribute; } = {}; dbLangDatabaseInstance: DB; dbLangConstrains: Constraint[] = []; - [key: string]: Attribute | any - constructor(name: string, db:DB) { + [key: string]: Attribute | any; + constructor(name: string, db: DB) { this.dbLangTableName = name; this.dbLangDatabaseInstance = db; } - serialize(handler : Handler) { + serialize(handler: Handler) { return this.toString(handler); } - toString(handler : Handler = this.dbLangDatabaseInstance.getHandler()) { + toString(handler: Handler = this.dbLangDatabaseInstance.getHandler()) { return handler.builders.escapeID(this.dbLangTableName); } addAttribute(name: string, type: Datatype, ops: attributeSettings = {}, noErrorOnNameConflict = false) { - if(this.dbLangTableAttributes[name] != null) throw new Error("You are tring to create an Attribute twise!"); - let attr = new Attribute(name,this,type,ops); + 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; - if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes", "dbLangDatabaseInstance","addConstraint","addAttributes"].includes(name)) { + if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes", "dbLangDatabaseInstance", "addConstraint", "addAttributes"].includes(name)) { if (!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!"); } else { this[name] = attr; } return attr; } - addAttributes(list:{[key: string]:extendedAttributeSettings}):{[key: string]:Attribute} { - return Object.fromEntries(Object.entries(list).map(([k,a])=>{ - return [k,this.addAttribute(k,a.type,a)]; + addAttributes(list: { [key: string]: (extendedAttributeSettings) }): { [key: string]: Attribute } { + return Object.fromEntries(Object.entries(list).map(([k, a]) => { + return [k, this.addAttribute(k, a.type, a)]; })); } - addConstraint(c:Constraint){ + addConstraint(c: Constraint) { c.check(this); this.dbLangConstrains.push(c); } diff --git a/src/dbStructure.ts b/src/dbStructure.ts index d3ecde2..b5fe4fd 100644 --- a/src/dbStructure.ts +++ b/src/dbStructure.ts @@ -64,7 +64,7 @@ export class checkConstraint implements Constraint{ this.checkQuery = check; } check(attr: Table): boolean { - throw new Error("Method not implemented."); + return true; } uses(attr: Attribute): boolean { throw new Error("Method not implemented."); @@ -76,19 +76,19 @@ export class checkConstraint implements Constraint{ export class uniqueConstraint implements Constraint{ name: string; - attr: Attribute[]; - constructor(name:string, attr:Attribute[]){ + attrs: Attribute[]; + constructor(name:string, attrs:Attribute[]){ this.name = name; - this.attr = attr; + this.attrs = attrs; } check(table: Table): boolean|string { - for(let i = 0; i < this.attr.length; i++){ - if(this.attr[i].ops.primaryKey) return "Can not combine unique Constraint and primary key"; + for(let i = 0; i < this.attrs.length; i++){ + if(this.attrs[i].ops.primaryKey) return "Can not combine unique Constraint and primary key"; } return false; } uses(attr: Attribute): boolean { - throw new Error("Method not implemented."); + return this.attrs.includes(attr); } serialize(handler: Handler): QueryBuilder { throw new Error("Method not implemented."); diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 156ea49..9aa48af 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -1,12 +1,115 @@ import { Attribute, DB, Table } from "./db" -import { Aggregation, Modifier } from "./dbStructure" -import { QueryBuilder, selectQuery } from "./query" +import { Aggregation, checkConstraint, Constraint, Datatype, Modifier, uniqueConstraint } from "./dbStructure" +import { Query, QueryBuilder, selectQuery } from "./query" import { allModifierInput, primaryData, serializeReturn } from "./types" export class Handler { - /*syncDB(db : DB){ + async syncDB(db: DB, handler: Handler, deleteInDB: boolean = false) { + console.log("start sync"); - }*/ + 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)); + + + let tableData = []; + 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])); + } catch (_) { + tableData[i] = null; + } + } + + const del = new QueryBuilder(); + const create = new QueryBuilder(); + + 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)); + } + } + // TODO: delete old constraints/tables + + function freeForUpdate(a: Attribute) { + 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 + ) { + 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[i]; + 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); + create.appendEnding(handler.querys.changeColumn(handler, a)); + } + if (attrData == null) { + changePrimary = true; + } else { + if ((attrData.Key == "PRI") != (!!a.ops.primaryKey)) { + freeForUpdate(a); + changePrimary = true; + } + } + } + } + if (changePrimary) { + create.appendEnding(handler.querys.removePrimaryKey(handler, table)); + 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)); + } + } + // TODO: unique + } + if (!create.isEmpty()) del.append(create); + if (!del.isEmpty()) await db.query(handler.builders.query(del)); + } querys = { select: (handler: Handler, q: selectQuery): QueryBuilder => { @@ -26,11 +129,15 @@ export class Handler { builder.append(q.havingD.serialize(handler)); } } - if (q.havingD) { builder.addCode(" limit "); builder.addInjection(q.limitD); } + + /*builder.setHandler((json)=>{ + return json; + });*/ + return builder; }, create: (handler: Handler, table: Table): QueryBuilder => { @@ -46,16 +153,102 @@ export class Handler { builder.addCode(")"); return builder; }, + 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; + }, + addForeignKey: (handler: Handler, table: Table) => { + const builder = new QueryBuilder(); - } + 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)); - constraints = { - // check constraints - listPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - listForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - listUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - listChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - // add constraints + 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: Table) => { + const qb = new QueryBuilder(); + qb.addCode("ALTER TABLE "); + qb.addCode(table.serialize(handler)); + 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; + }, + /*// add constraints appPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), appForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), addUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), @@ -65,10 +258,15 @@ export class Handler { dropForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), dropUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), dropChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + // check constraints + listPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + listForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + listUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), + listChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(),*/ } builders = { - query: (qb: QueryBuilder): serializeReturn => { + query: (qb: QueryBuilder): Query => { let args: primaryData[] = []; let sql = ""; for (let i = 0; i < qb.list.length; i++) { @@ -80,7 +278,7 @@ export class Handler { sql += data; } } - return [sql, args]; + return new Query([sql, args]); }, attributeSettings: (handler: Handler, a: Attribute): QueryBuilder => { @@ -89,38 +287,34 @@ export class Handler { if (a.ops.autoIncrement) { builder.addCode(" auto_increment"); } - /*if (a.ops.primaryKey) { - sql += " primary key"; - }*/ 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.unique != null) { - if (!a.ops.autoIncrement && !a.ops.primaryKey){ - sql += " unique"; - } - }*/ if (a.ops.notNull != null) { if (!a.ops.autoIncrement && !a.ops.primaryKey && a.ops.default == null) { builder.addCode(" not null"); - } - } - - /*if (a.ops.foreginKey != null) { - sql += ` foreign key references (${a.ops.foreginKey.link.toStringFunc(handler)})` - }*/ - + } 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("")); + }, } @@ -200,7 +394,7 @@ export class Handler { double: dataTypeDblNum("double"), decimal: dataTypeDblNum("decimal"), - date: dataTypeNoArg("data"), + date: dataTypeNoArg("date"), datetime: dataTypeNoArg("datatime"), timestamp: dataTypeNoArg("timestamp"), time: dataTypeNoArg("time"), diff --git a/src/query.ts b/src/query.ts index 4cf4e16..3debfff 100644 --- a/src/query.ts +++ b/src/query.ts @@ -16,6 +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++) { @@ -26,6 +27,13 @@ export class QueryBuilder { addCode(text: string) { this.list.push([false, text]); } + addCodeCommaSeperated(data: string[], comma = ", ") { + for (let i = 0; i < data.length; i++) { + const e = data[i]; + this.list.push([false, e]); + if(i+1any){ + + }*/ } @@ -74,9 +92,10 @@ export class selectQuery { return handler.querys.select(handler, this); } - query(db: DB) { + async query(db: DB) { const handler = db.getHandler(); - const s = handler.builders.query(this.serialize(handler)); - return db.query(new Query(s)); + const builder = this.serialize(handler); + const s = handler.builders.query(builder); + return await db.query(s); } } \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index e0f6ea7..c7bc431 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,6 @@ import { Attribute, Table } from "./db"; import { Aggregation, BooleanModifier, Datatype, Joins, Modifier } from "./dbStructure"; -import { selectQuery } from "./query"; +import { QueryBuilder, selectQuery } from "./query"; export type primaryData = string | number | boolean | null; export type allModifierInput = primaryData | Modifier | selectQuery | Attribute | Aggregation; @@ -10,6 +10,8 @@ export type selectFromElements = Table | Joins | null; export type serializeReturn = [string, primaryData[]]; +export type DatatypeBuild = [QueryBuilder, number]; + export type attributeSettings = { unique?: boolean, autoIncrement?: boolean, @@ -21,8 +23,9 @@ export type attributeSettings = { onDelete?: onAction, onUpdate?: onAction }, - check?:BooleanModifier + check?: BooleanModifier | ((a: Attribute) => BooleanModifier) }; + export type extendedAttributeSettings = { type: Datatype, unique?: boolean, @@ -35,7 +38,7 @@ export type extendedAttributeSettings = { onDelete?: onAction, onUpdate?: onAction }, - check?:BooleanModifier + check?: BooleanModifier | ((a: Attribute) => BooleanModifier) } export enum onAction { From ea3100c4ae00c82246a30ddb3a2080bd8b98b406 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 14 Feb 2023 16:45:21 +0100 Subject: [PATCH 08/14] 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){ From d763d2a35e267a1a89446c37dac555d75ed50897 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 14 Feb 2023 17:37:01 +0100 Subject: [PATCH 09/14] foreign evolution --- src/db.ts | 17 +++++-- src/dbStructure.ts | 8 +++- src/defaultHandler.ts | 102 +++++++++++++++++++++++++++++++++--------- src/types.ts | 1 + 4 files changed, 101 insertions(+), 27 deletions(-) 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, From 711d2d2c7a145c880d6898a0d766de89c6f76bed Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 14 Feb 2023 18:13:06 +0100 Subject: [PATCH 10/14] clean up and view modifier --- src/db.ts | 11 ++++++----- src/defaultHandler.ts | 40 ++++++++++++++++++++++++---------------- src/funcs.ts | 5 +++++ 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/db.ts b/src/db.ts index 75511ed..fc9262c 100644 --- a/src/db.ts +++ b/src/db.ts @@ -112,13 +112,14 @@ 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; - if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes", "dbLangDatabaseInstance", "addConstraint", "addAttributes"].includes(name)) { + let lowName = name.toLowerCase(); + if (this.dbLangTableAttributes[lowName] != null) throw new Error("You are tring to create an Attribute twise!"); + let attr = new Attribute(lowName, this, type, ops); + this.dbLangTableAttributes[lowName] = attr; + if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes", "dbLangDatabaseInstance", "addConstraint", "addAttributes"].includes(lowName)) { if (!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!"); } else { + this[lowName] = attr; this[name] = attr; } return attr; diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 957f87a..8102338 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -411,21 +411,6 @@ export class Handler { qb.addCode(")"); return qb; }, - /*// add constraints - appPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - appForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - addUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - addChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - //should drop all keys to be able to recreate them - dropPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - dropForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - dropUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - dropChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - // check constraints - listPrimaryKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - listForeignKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - listUniqueKeys: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(), - listChecks: (handler: Handler, table: Table): QueryBuilder => new QueryBuilder(),*/ } builders = { @@ -499,6 +484,8 @@ export class Handler { 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) + ")" }]) @@ -513,7 +500,28 @@ export class Handler { { 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) { + const builder = new QueryBuilder(); + builder.append(a[0].serialize(handler)); + return builder; + }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) { + const builder = new QueryBuilder(); + builder.append(a[1].serialize(handler)); + return builder; + }else{ + builder.addInjection(a[1]); + } + return builder; } } datatypes = { diff --git a/src/funcs.ts b/src/funcs.ts index 96928ab..d9e9dee 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -13,6 +13,11 @@ export const leq = (...args: (allModifierInput)[]) => new BooleanModifier("leq", export const le = (...args: (allModifierInput)[]) => new BooleanModifier("le", args); export const plus = (...args: (allModifierInput)[]) => new NumberModifier("plus", args); export const minus = (...args: (allModifierInput)[]) => new NumberModifier("minus", args); +export const mult = (...args: (allModifierInput)[]) => new NumberModifier("mult", args); +export const divide = (...args: (allModifierInput)[]) => new NumberModifier("divide", args); + +export const not = (...args: (allModifierInput)[]) => new BooleanModifier("not", args); +export const like = (a: allModifierInput, b: allModifierInput) => new BooleanModifier("like", [a, b]); //aggregations From 7c4d4fb9c738ce56e34763db7bf135cd40316ed2 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 14 Feb 2023 20:40:25 +0100 Subject: [PATCH 11/14] fixed custom constraints --- src/db.ts | 7 +++++-- src/defaultHandler.ts | 2 +- src/funcs.ts | 11 ++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/db.ts b/src/db.ts index fc9262c..bc3cb92 100644 --- a/src/db.ts +++ b/src/db.ts @@ -16,7 +16,7 @@ export class DB { this.name = database; } async query(query: Query) { - console.log(query); + //console.log(query); return await this.pool.query(query); } getHandler() { @@ -41,6 +41,9 @@ export class DB { for (let i = 0; i < this.tables.length; i++) if (this.tables[i].dbLangTableName == name) return this.tables[i]; return null; } + async close() { + if (this.pool) await this.pool.end(); + } } export class Attribute { @@ -69,7 +72,7 @@ export class Attribute { } if (ops.foreginKey != null) { table.addConstraint(new foreignConstraint( - table.dbLangDatabaseInstance.name + "_" + table.dbLangTableName + "_" + name + "_foreign_constraint_to_"+ops.foreginKey.link.name, + table.dbLangDatabaseInstance.name + "_" + table.dbLangTableName + "_" + name + "_foreign_constraint_to_" + ops.foreginKey.link.name, [this], [ops.foreginKey.link], ops.foreginKey.onDelete, diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 8102338..450babb 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -465,7 +465,6 @@ export class Handler { }, } - 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) + ")" }]), @@ -524,6 +523,7 @@ export class Handler { return builder; } } + datatypes = { char: dataTypeSingleNum("char"), varchar: dataTypeSingleNum("varchar"), diff --git a/src/funcs.ts b/src/funcs.ts index d9e9dee..870d2d0 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -1,5 +1,5 @@ import { Attribute } from "./db"; -import { Aggregation, BooleanModifier, checkConstraint, Datatype, NumberModifier } from "./dbStructure"; +import { Aggregation, BooleanModifier, checkConstraint, Datatype, foreignConstraint, NumberModifier, uniqueConstraint } from "./dbStructure"; import { selectQuery } from "./query"; import { allModifierInput, primaryData, selectElements, selectFromElements } from "./types"; @@ -71,8 +71,13 @@ export const YEAR = new Datatype("year", []); // Constraints //TODO: //primary key -export const foreginKey = (attrs: Attribute[], target: Attribute[]) => { }; -export const uniqueKey = (attr: Attribute[]) => { }; +export const foreignKey = (attrs: Attribute[], target: Attribute[]) => new foreignConstraint( + "foreign_constraint_" + + attrs.map(a => a.name + "_" + a.table.dbLangTableName).join("_") + + "_to_" + + target.map(a => a.name + "_" + a.table.dbLangTableName).join("_"), + attrs, target); +export const uniqueKey = (attrs: Attribute[]) => new uniqueConstraint("unique_constraint_" + attrs.map(a => a.name + "_" + a.table.dbLangTableName).join("_"), attrs); export const check = (name: string, mod: BooleanModifier) => new checkConstraint(name, mod); From 1fe81203b17170216fd4aa21f260feffdf7e104b Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 14 Feb 2023 22:23:21 +0100 Subject: [PATCH 12/14] insert, update, remove, fixes --- src/defaultHandler.ts | 82 +++++++++++++++++++++++++++++++++++---- src/funcs.ts | 40 ++++++++++--------- src/query.ts | 90 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 184 insertions(+), 28 deletions(-) diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 450babb..3765dfb 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -1,6 +1,6 @@ import { Attribute, DB, Table } from "./db" import { Aggregation, checkConstraint, Constraint, Datatype, foreignConstraint, Modifier, uniqueConstraint } from "./dbStructure" -import { Query, QueryBuilder, selectQuery } from "./query" +import { insertQuery, Query, QueryBuilder, removeQuery, selectQuery, updateQuery } from "./query" import { allModifierInput, onAction, primaryData, serializeReturn } from "./types" export class Handler { @@ -261,6 +261,60 @@ export class Handler { 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 = `); @@ -505,25 +559,37 @@ export class Handler { 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) { - const builder = new QueryBuilder(); builder.append(a[0].serialize(handler)); - return builder; - }else{ + } 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) { - const builder = new QueryBuilder(); builder.append(a[1].serialize(handler)); - return builder; - }else{ + } 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"), diff --git a/src/funcs.ts b/src/funcs.ts index 870d2d0..9c9aebe 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -1,26 +1,27 @@ -import { Attribute } from "./db"; -import { Aggregation, BooleanModifier, checkConstraint, Datatype, foreignConstraint, NumberModifier, uniqueConstraint } from "./dbStructure"; -import { selectQuery } from "./query"; -import { allModifierInput, primaryData, selectElements, selectFromElements } from "./types"; +import { Attribute, Table } from "./db"; +import { Aggregation, BooleanModifier, checkConstraint, Datatype, foreignConstraint, NumberModifier, StringModifier, uniqueConstraint } from "./dbStructure"; +import { insertQuery, removeQuery, selectQuery, updateQuery } from "./query"; +import { allModifierInput, selectElements, selectFromElements } from "./types"; //modifiers -export const and = (...args: (BooleanModifier)[]) => new BooleanModifier("and", args); -export const or = (...args: (BooleanModifier)[]) => new BooleanModifier("or", args); -export const ge = (...args: (allModifierInput)[]) => new BooleanModifier("ge", args); -export const geq = (...args: (allModifierInput)[]) => new BooleanModifier("geq", args); -export const eq = (...args: (allModifierInput)[]) => new BooleanModifier("eq", args); -export const leq = (...args: (allModifierInput)[]) => new BooleanModifier("leq", args); -export const le = (...args: (allModifierInput)[]) => new BooleanModifier("le", args); -export const plus = (...args: (allModifierInput)[]) => new NumberModifier("plus", args); -export const minus = (...args: (allModifierInput)[]) => new NumberModifier("minus", args); -export const mult = (...args: (allModifierInput)[]) => new NumberModifier("mult", args); -export const divide = (...args: (allModifierInput)[]) => new NumberModifier("divide", args); +export const and = (...args: BooleanModifier[]) => new BooleanModifier("and", args); +export const or = (...args: BooleanModifier[]) => new BooleanModifier("or", args); +export const ge = (...args: allModifierInput[]) => new BooleanModifier("ge", args); +export const geq = (...args: allModifierInput[]) => new BooleanModifier("geq", args); +export const eq = (...args: allModifierInput[]) => new BooleanModifier("eq", args); +export const leq = (...args: allModifierInput[]) => new BooleanModifier("leq", args); +export const le = (...args: allModifierInput[]) => new BooleanModifier("le", args); +export const plus = (...args: allModifierInput[]) => new NumberModifier("plus", args); +export const minus = (...args: allModifierInput[]) => new NumberModifier("minus", args); +export const mult = (...args: allModifierInput[]) => new NumberModifier("mult", args); +export const divide = (...args: allModifierInput[]) => new NumberModifier("divide", args); -export const not = (...args: (allModifierInput)[]) => new BooleanModifier("not", args); +export const not = (...args: allModifierInput[]) => new BooleanModifier("not", args); export const like = (a: allModifierInput, b: allModifierInput) => new BooleanModifier("like", [a, b]); -//aggregations +export const concat = (...args: allModifierInput[]) => new StringModifier("concat", args); +//aggregations export const count = (a: Attribute) => new Aggregation("count", a); export const sum = (a: Attribute) => new Aggregation("sum", a); export const avg = (a: Attribute) => new Aggregation("avg", a); @@ -29,6 +30,9 @@ export const max = (a: Attribute) => new Aggregation("max", a); //query export const select = (args: selectElements[], from: selectFromElements) => new selectQuery(args, from); +export const insert = (...attrs: Attribute[]) => new insertQuery(attrs); +export const update = (table: Table) => new updateQuery(table); +export const remove = (table: Table) => new removeQuery(table); //datatypes @@ -75,7 +79,7 @@ export const foreignKey = (attrs: Attribute[], target: Attribute[]) => new forei "foreign_constraint_" + attrs.map(a => a.name + "_" + a.table.dbLangTableName).join("_") + "_to_" - + target.map(a => a.name + "_" + a.table.dbLangTableName).join("_"), + + target.map(a => a.name + "_" + a.table.dbLangTableName).join("_"), attrs, target); export const uniqueKey = (attrs: Attribute[]) => new uniqueConstraint("unique_constraint_" + attrs.map(a => a.name + "_" + a.table.dbLangTableName).join("_"), attrs); export const check = (name: string, mod: BooleanModifier) => new checkConstraint(name, mod); diff --git a/src/query.ts b/src/query.ts index c6e04d1..597ffb2 100644 --- a/src/query.ts +++ b/src/query.ts @@ -1,7 +1,7 @@ -import { Attribute, DB } from "./db"; +import { Attribute, DB, Table } from "./db"; import { BooleanModifier, Modifier } from "./dbStructure"; import { Handler } from "./defaultHandler"; -import { primaryData, selectElements, selectFromElements, serializeReturn } from "./types"; +import { allModifierInput, primaryData, selectElements, selectFromElements, serializeReturn } from "./types"; export class Query { @@ -92,6 +92,92 @@ export class selectQuery { return handler.querys.select(handler, this); } + async query(db: DB) { + const handler = db.getHandler(); + const builder = this.serialize(handler); + const s = handler.builders.query(builder); + return await db.query(s); + } +} + +export class insertQuery { + attrs: Attribute[]; + values: primaryData[][] = []; + select: selectQuery | null = null; + constructor(attrs: Attribute[]) { + if (attrs.length == 0) throw new Error("Insertion must be done in at least one Column."); + for (let i = 0; i < attrs.length; i++) { + if (attrs[i].table != attrs[0].table) throw new Error("Insertion Columns must be in one Table."); + } + this.attrs = attrs; + } + add(...data: primaryData[]) { + if (this.select != null) throw new Error("Can not add Values when using select!"); + this.values.push(data); + return this; + } + addValues(...data: primaryData[][]) { + if (this.select != null) throw new Error("Can not add Values when using select!"); + this.values.push(...data); + return this; + } + setSelect(state: selectQuery) { + if (this.values.length != 0) throw new Error("Can not add select when using values!"); + this.select = state; + return this; + } + serialize(handler: Handler): QueryBuilder { + return handler.querys.insert(handler, this); + } + async query(db: DB) { + const handler = db.getHandler(); + const builder = this.serialize(handler); + const s = handler.builders.query(builder); + return await db.query(s); + } +} + +export class updateQuery { + table: Table; + setD: ([Attribute, allModifierInput])[] = []; + whereD: BooleanModifier | null = null; + + constructor(table: Table) { + this.table = table; + } + set(attr: Attribute, value: allModifierInput) { + if (this.table != attr.table) throw new Error("Can only edit columns of the updated table!"); + this.setD.push([attr, value]); + return this; + } + where(w: BooleanModifier) { + this.whereD = w; + return this; + } + serialize(handler: Handler): QueryBuilder { + return handler.querys.update(handler, this); + } + async query(db: DB) { + const handler = db.getHandler(); + const builder = this.serialize(handler); + const s = handler.builders.query(builder); + return await db.query(s); + } +} + +export class removeQuery { + table: Table; + whereD: BooleanModifier | null = null; + constructor(table: Table) { + this.table = table; + } + where(w: BooleanModifier) { + this.whereD = w; + return this; + } + serialize(handler: Handler): QueryBuilder { + return handler.querys.remove(handler, this); + } async query(db: DB) { const handler = db.getHandler(); const builder = this.serialize(handler); From 89a0e8e68f0cb7f048aa331a3998124065fde0d9 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 14 Feb 2023 23:29:16 +0100 Subject: [PATCH 13/14] fixes and readme --- readme.md | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/funcs.ts | 2 +- src/query.ts | 4 +- 3 files changed, 215 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index d023b05..5c91f67 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,214 @@ # DBlang -sql Querys with js or ts Syntax. -[![status-badge](https://ci.jusax.de/api/badges/jusax23/dblang/status.svg)](https://ci.jusax.de/jusax23/dblang) \ No newline at end of file +[![status-badge](https://ci.jusax.de/api/badges/jusax23/dblang/status.svg)](https://ci.jusax.de/jusax23/dblang) + +TypeScript Libary for relational Database Requests. Requests are written in js/ts and it supports automatic Schema evulution. + +Fetures +- [x] select Query +- [x] insert Query +- [x] update Query +- [x] remove Query +- [x] create Schema +- [x] remove unused Schema +- [ ] change Schema +- [ ] Key-Value Store Shortcut +- [ ] Views + +Supported Databses: +- [x] mariadb +- [ ] mysql (not tested) +- [ ] postrges + +## Getting Started + +### Connect + +```javascript +import { DB } from "dblang"; +const db = new DB({ + host: "localhost", + user: "root", + password: "0123456789", + database: "databaseName" +}); + +``` + +### Schema + +Tables: +```javascript +const Table1 = db.newTable("Table1Name"); +const Table2 = db.newTable("Table2Name"); +``` + +Add Attributes: +```javascript +Table1.addAttribute("AttrName", TYPE, { + default: 5, + // other settings +}); + +Table2.addAttributes({ + "AttrName2":{ + type: TYPE, + default: 6 + // other settings + }, + "AttrName3":{ + type: TYPE, + default: 6 + // other settings + }, + // more Attributes +}); +``` + +TYPE: See [Types](#types) + +Other Settings: +- `unique: boolean`: (optional) Add Unique Constraint +- `autoIncrement: boolean`: (optional) Set auto Increment (must be primary key) +- `default: any`: (optional) Set default Property. +- `notNull: boolean`: (optional) Set not nullable. +- `primaryKey: boolean`: (optinal) mark as part of primary Key. +- `foreignKey: Object`: Add foreign Constraint for single Attribute. + - `link: Attribute`: Linked Attribute + - `onDelete: onAction`: Specify delete Action [onAction](#onaction) + - `onUpdate: onAction`: Specify update Action [onAction](#onaction) +- `check: BooleanModifier | ((a: Attribute) => BooleanModifier)`: Add check Constraint (A function must be used when check references its self.) + +Sync: +Create Tables, Attributes and Constraints +```javascript +await db.sync(); + +await db.sync(true); //delete unused Elements (May do a backup befor.) +``` + +### Querys +See: [Modifiers](#modifier), [Aggregations](#aggregations) + +#### Select: +```javascript +import {select} from "dblang" + +let res = await select([Table2.AttrName2], Table1) + .where(eq(Table2.AttrName2, 5)) //optional + .groupBy(Table2.AttrName3) //optional + .having(le(Table2.AttrName3, 5)) //optional + .limit(10) //optional + .query(db); + +``` + +#### Insert: +```javascript +import {select} from "dblang" + +await insert(Table2.AttrName2, Table2.AttrName3) + .add(1, 2) + .add(2, 3) + .addValues([4, 5], [3, 4]) + .query(db); + +await insert(Table2.AttrName2, Table2.AttrName3) + .setSelect(select(Table1.AttrName, 4)) + .query(db); +``` + +#### Update: +```javascript +await update(Table2) + .set(Table2.AttrName2, plus(Table2.AttrName2,1)) + .set(Table2.AttrName3, minus(Table2.AttrName2,1)) + .where(leq(Table2.AttrName2, 5)) + .query(db); +``` + +#### Remove: +```javascript +await remove(Table2) + .where(geq(Table2.AttrName2, 5)) + .query(db); +``` + +### Content List +#### Types + +- `CHAR(size)` +- `VARCHAR(size)` +- `BINARY(size)` +- `VARBINARY(size)` +- `TINYBLOB` +- `BLOB` +- `MEDIUMBLOB` +- `LONGBLOB` +- `TINYTEXT` +- `TEXT` +- `MEDIUMTEXT` +- `LONGTEXT` +- `ENUM(val1, val2, ...)` +- `SET(val1, val2, ...)` +- `BOOL` +- `BIT` +- `TINYINT` +- `SMALLINT` +- `MEDIUMINT` +- `INT` +- `BIGINT` +- `FLOAT(size, d)` +- `DOUBLE(size, d)` +- `DECIMAL(size, d)` +- `DATE` +- `DATETIME` +- `TIMESTAMP` +- `TIME` +- `YEAR` + +#### onAction + +Action for onDelete or onUpdate. + +Usage: +```javascript +import { onAction } from "dblang" + +onAction.cascade; +onAction.noAction; // dangerous +onAction.setNull; +onAction.setDefault; +``` + +#### Modifier +BooleanModifier: +- `and(v1, v2, ...)` +- `or(v1, v2, ...)` +- `ge(v1, v2, ...)` +- `geq(v1, v2, ...)` +- `eq(v1, v2, ...)` +- `leq(v1, v2, ...)` +- `le(v1, v2, ...)` +- `not(v1)` +- `like(v1, v2)` + +NumberModifier: +- `plus(v1, v2, ...)` +- `minus(v1, v2, ...)` +- `mult(v1, v2, ...)` +- `divide(v1, v2, ...)` + +StringModifier: +- `concat(v1, v2, ...)` + +(v\* = string, number, boolean, null, Modifier, Aggregation, select Query, Attribute) + +# Aggregations +- `count(a)` +- `sum(a)` +- `avg(a)` +- `min(a)` +- `max(a)` + +(a = Attribute) \ No newline at end of file diff --git a/src/funcs.ts b/src/funcs.ts index 9c9aebe..77ad471 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -16,7 +16,7 @@ export const minus = (...args: allModifierInput[]) => new NumberModifier("minus" export const mult = (...args: allModifierInput[]) => new NumberModifier("mult", args); export const divide = (...args: allModifierInput[]) => new NumberModifier("divide", args); -export const not = (...args: allModifierInput[]) => new BooleanModifier("not", args); +export const not = (arg: allModifierInput) => new BooleanModifier("not", [arg]); export const like = (a: allModifierInput, b: allModifierInput) => new BooleanModifier("like", [a, b]); export const concat = (...args: allModifierInput[]) => new StringModifier("concat", args); diff --git a/src/query.ts b/src/query.ts index 597ffb2..9fd00d6 100644 --- a/src/query.ts +++ b/src/query.ts @@ -77,8 +77,8 @@ export class selectQuery { this.groupByD = a; return this; } - havingD: Modifier | null = null; - having(m: Modifier) { + havingD: BooleanModifier | null = null; + having(m: BooleanModifier) { this.havingD = m; return this; } From 48bd1374d397622da4d7348c32dae7eb42683900 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 14 Feb 2023 23:30:04 +0100 Subject: [PATCH 14/14] joins --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 5c91f67..ec357fd 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,7 @@ Fetures - [x] remove Query - [x] create Schema - [x] remove unused Schema +- [ ] joins - [ ] change Schema - [ ] Key-Value Store Shortcut - [ ] Views