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,