First Working Version #1

Merged
jusax23 merged 14 commits from dev into main 2023-02-14 23:33:55 +01:00
9 changed files with 1395 additions and 161 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
dist dist
node_modules node_modules
ltests ltests
sqlsave

View file

@ -5,11 +5,15 @@ pipeline:
build: build:
image: node:18-alpine image: node:18-alpine
commands: commands:
- apk add git - apk add git zip tar
- npm install - npm install
- npm install git+https://jusax.de/git/jusax23/gitea-release.git - npm install git+https://jusax.de/git/jusax23/gitea-release.git
- mkdir build - mkdir build
- mkdir dblang
- mkdir upload
- npm run prepublish - npm run prepublish
- ls dist - cp dist/* dblang
- npx gitea-release "https://jusax.de/git/api/v1/" "$${GITEA_TOKEN}" "$${CI_REPO}" "$${CI_COMMIT_BRANCH}" "$${CI_COMMIT_TAG}" "dist" "$${CI_COMMIT_MESSAGE}" - 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 ] secrets: [ gitea_token ]

214
readme.md
View file

@ -1 +1,215 @@
# DBlang
[![status-badge](https://ci.jusax.de/api/badges/jusax23/dblang/status.svg)](https://ci.jusax.de/jusax23/dblang) [![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
- [ ] joins
- [ ] 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)

135
src/db.ts
View file

@ -1,36 +1,99 @@
import mariadb from 'mariadb'; import mariadb from 'mariadb';
import { checkConstraint, Constraint, Datatype, foreignConstraint, uniqueConstraint } from './dbStructure';
import { Handler } from './defaultHandler'; import { Handler } from './defaultHandler';
import { Query } from './query'; import { Query } from './query';
import { onAction, primaryData } from './types'; import { attributeSettings, extendedAttributeSettings, onAction } from './types';
export class DB { export class DB {
//pool:mariadb.Pool; tables: Table[] = [];
constructor(/*{ host, user, password, database, connectionLimit = 5 }*/) { handler: Handler;
//this.pool = mariadb.createPool({ host, user, password, database, connectionLimit, multipleStatements: true }); name: string;
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 = database;
} }
async query(query: Query) { async query(query: Query) {
//return this.pool.query(query); //console.log(query);
return await this.pool.query(query);
} }
getHandler() { getHandler() {
return Handler; return this.handler;
}
async sync(force = false) {
let handler = this.getHandler();
await handler.syncDB(this, handler, force);
//this.tables.forEach(t=>{
// console.log(handler.builders.query(handler.querys.create(handler,t)));
//})
} }
newTable(name: string) { newTable(name: string) {
return new Table(name); let tabel = new Table(name, this);
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;
}
async close() {
if (this.pool) await this.pool.end();
} }
} }
export class Attribute { export class Attribute {
name: string; name: string;
constructor(name: string){ table: Table;
this.name = name; ops: attributeSettings;
type: Datatype;
constructor(name: string, table: Table, type: Datatype, ops: attributeSettings) {
this.name = name.toLowerCase();
this.type = type;
this.table = table;
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",
ops.check
))
} }
serialize(){ if (ops.unique != null) {
return this.toString(); table.addConstraint(new uniqueConstraint(
table.dbLangDatabaseInstance.name + "_" + table.dbLangTableName + "_" + name + "_unique_constraint",
[this]
))
} }
toString(){ if (ops.foreginKey != null) {
return this.name; 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);
}
serializeSettings(handler: Handler) {
return handler.builders.attributeSettings(handler, this);
}
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) + ")";
} }
} }
@ -38,37 +101,41 @@ export class Attribute {
export class Table { export class Table {
dbLangTableName: string; dbLangTableName: string;
dbLangTableAttributes: { [key: string]: Attribute; } = {}; dbLangTableAttributes: { [key: string]: Attribute; } = {};
[key:string]: Attribute | any dbLangDatabaseInstance: DB;
constructor(name: string){ dbLangConstrains: Constraint[] = [];
[key: string]: Attribute | any;
constructor(name: string, db: DB) {
this.dbLangTableName = name; this.dbLangTableName = name;
this.dbLangDatabaseInstance = db;
} }
serialize(){ serialize(handler: Handler) {
return this.toString(); return this.toString(handler);
} }
toString(){ toString(handler: Handler = this.dbLangDatabaseInstance.getHandler()) {
return this.dbLangTableName; return handler.builders.escapeID(this.dbLangTableName);
} }
addAttribute(name:string,ops:{ addAttribute(name: string, type: Datatype, ops: attributeSettings = {}, noErrorOnNameConflict = false) {
unique?: boolean, let lowName = name.toLowerCase();
A_I?: boolean, if (this.dbLangTableAttributes[lowName] != null) throw new Error("You are tring to create an Attribute twise!");
default?: primaryData, let attr = new Attribute(lowName, this, type, ops);
notNull?: boolean this.dbLangTableAttributes[lowName] = attr;
primaryKey?: boolean, if (["serialize", "toString", "addAttribute", "dbLangTableName", "dbLangTableAttributes", "dbLangDatabaseInstance", "addConstraint", "addAttributes"].includes(lowName)) {
foreginKey?: {
link: Attribute,
onDelete?: onAction,
onUpdate?: onAction
}
},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!"); if (!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!");
} else { } else {
this[lowName] = attr;
this[name] = attr; this[name] = attr;
} }
return 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'; export * from './funcs';

View file

@ -1,8 +1,19 @@
import { Attribute } from "./db"; import { Attribute, Table } from "./db";
import { Handler } from "./defaultHandler"; import { Handler } from "./defaultHandler";
import { allModifierInput, primaryData, serializeReturn } from "./types"; import { QueryBuilder } from "./query";
import { allModifierInput, onAction, 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): QueryBuilder {
return handler.datatypes[this.type as keyof typeof handler.datatypes](this.args);
}
}
export abstract class Modifier { export abstract class Modifier {
t: string; t: string;
@ -11,8 +22,8 @@ export abstract class Modifier {
this.t = type; this.t = type;
this.a = args; this.a = args;
} }
serialize(handler = Handler):serializeReturn { serialize(handler: Handler): QueryBuilder {
return handler.modifiers[this.t as keyof typeof handler.modifiers](this.a); return handler.modifiers[this.t as keyof typeof handler.modifiers](handler, this.a);
} }
} }
@ -27,13 +38,94 @@ export class Aggregation{
this.t = type; this.t = type;
this.a = args; this.a = args;
} }
serialize(handler = Handler):serializeReturn{ serialize(handler: Handler): QueryBuilder {
return handler.aggregations[this.t as keyof typeof handler.aggregations](this.a); return handler.aggregations[this.t as keyof typeof handler.aggregations](handler, this.a);
} }
} }
export class Joins { export class Joins {
serialize(handler = Handler):serializeReturn { serialize(handler: Handler): QueryBuilder {
return ["",[]]; return new QueryBuilder();
} }
} }
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;
name: string;
constructor(name: string, check: BooleanModifier) {
this.name = name.toLowerCase();
this.checkQuery = check;
}
check(attr: Table): boolean {
return true;
}
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;
attrs: Attribute[];
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";
if (this.attrs[i].table != table) return "Referencing Attributes must be in host Table.";
}
return false;
}
uses(attr: Attribute): boolean {
return this.attrs.includes(attr);
}
serialize(handler: Handler): QueryBuilder {
throw new Error("Method not implemented.");
}
}
export class foreignConstraint implements Constraint {
name: string;
fromAttrs: Attribute[];
toAttrs: 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.");
}
uses(attr: Attribute): boolean {
throw new Error("Method not implemented.");
}
check(t: Table): string | boolean {
let table = this.toAttrs[0].table;
for (let i = 0; i < this.toAttrs.length; i++) {
if (table != this.toAttrs[i].table) return "Referenced Attributes must be in one Table.";
if (this.toAttrs[i].ops.primaryKey) return "Can not reference non primary keys.";
}
for (let i = 0; i < this.fromAttrs.length; i++) {
if (this.fromAttrs[i].table != t) return "Referencing Attributes must be in host Table.";
}
return false;
}
}

View file

@ -1,76 +1,690 @@
import { Attribute } from "./db" import { Attribute, DB, Table } from "./db"
import { Aggregation, Modifier } from "./dbStructure" import { Aggregation, checkConstraint, Constraint, Datatype, foreignConstraint, Modifier, uniqueConstraint } from "./dbStructure"
import { selectQuery } from "./query" import { insertQuery, Query, QueryBuilder, removeQuery, selectQuery, updateQuery } from "./query"
import { allModifierInput, primaryData, serializeReturn } from "./types" import { allModifierInput, onAction, primaryData, serializeReturn } from "./types"
export class Handler { export class Handler {
static querys = { async syncDB(db: DB, handler: Handler, deleteInDB: boolean = false) {
select: (q: selectQuery): serializeReturn => { console.log("start sync");
let args: primaryData[] = [];
let w = joinArg(", ", this)(q.attr); let gd = new QueryBuilder();
args.push(...w[1]); gd.addCode(`SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where CONSTRAINT_SCHEMA = `);
let sql = `select ${w[0]} from ${q.from == null ? 'DUAL' : q.from.serialize(this)}`; 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));
//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];
try {
const tableDataBuilder = new QueryBuilder();
tableDataBuilder.addCode("DESCRIBE ");
tableDataBuilder.addCode(table.serialize(handler));
tableData[table.dbLangTableName.toLowerCase()] =
Object.fromEntries((await db.query(handler.builders.query(tableDataBuilder))).map((d: any) => [d.Field.toLowerCase(), d]));
} catch (_) {
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);
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 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;
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;
}
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++) {
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") {
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]));
}
function freeForUpdate(attr: string, table: string) {
for (let i = 0; i < key.length; i++) {
const k = key[i];
if (
k.REFERENCED_TABLE_NAME == table
&& k.REFERENCED_COLUMN_NAME.toLowerCase() == attr.toLowerCase()
) {
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[table.dbLangTableName.toLowerCase()];
//delete unused Columns
if (deleteInDB && tableD != null) {
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));
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.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.name, a.table.dbLangTableName);
changePrimary = true;
}
}
}
}
if (changePrimary) {
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));
}
} else if (c instanceof foreignConstraint) {
if (!checkForeignExists(table, c)) {
create.appendEnding(handler.querys.addForeignKey(handler, table, c));
}
}
}
}
if (!create.isEmpty()) del.append(create);
if (!del.isEmpty()) await db.query(handler.builders.query(del));
}
querys = {
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) { if (q.whereD) {
let whereS = q.whereD.serialize(this); builder.addCode(" where ");
args.push(...whereS[1]); builder.append(q.whereD.serialize(handler));
sql += " where "+whereS[0];
} }
if (q.groupByD.length > 0) { if (q.groupByD.length > 0) {
let groupByS = joinArg(",", this)(q.groupByD); builder.addCode(" group by ");
args.push(...groupByS[1]); builder.append(joinArg(",")(handler, q.groupByD));
sql += " group by "+groupByS[0];
if (q.havingD) { if (q.havingD) {
let havingS = q.havingD.serialize(this); builder.addCode(" having ");
args.push(...havingS[1]); builder.append(q.havingD.serialize(handler));
sql += " having "+havingS[0];
} }
} }
if (q.havingD) {
builder.addCode(" limit ");
builder.addInjection(q.limitD);
}
return [sql, args]; /*builder.setHandler((json)=>{
return json;
});*/
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 = `);
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)}(`);
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;
},
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 `);
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;
},
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, table: Table, c: foreignConstraint) => {
const builder = new QueryBuilder();
builder.addCode("ALTER TABLE ");
builder.addCode(table.serialize(handler));
builder.addCode(" ADD CONSTRAINT ");
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(c.toAttrs[0].table.serialize(handler));
builder.addCode("(");
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) => {
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));
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: string) => {
const qb = new QueryBuilder();
qb.addCode("ALTER TABLE ");
qb.addCode(handler.builders.escapeID(table));
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;
},
} }
static aggregations = { builders = {
count: (a: Attribute): serializeReturn => ["count(" + a + ")", []], query: (qb: QueryBuilder): Query => {
sum: (a: Attribute): serializeReturn => ["sum(" + a + ")", []], let args: primaryData[] = [];
avg: (a: Attribute): serializeReturn => ["avg(" + a + ")", []], let sql = "";
min: (a: Attribute): serializeReturn => ["min(" + a + ")", []], for (let i = 0; i < qb.list.length; i++) {
max: (a: Attribute): serializeReturn => ["max(" + a + ")", []], const [inject, data] = qb.list[i];
if (inject) {
sql += "?";
args.push(data);
} else {
sql += data;
}
}
return new Query([sql, args]);
},
attributeSettings: (handler: Handler, a: Attribute): QueryBuilder => {
const builder = new QueryBuilder();
builder.append(a.type.serialize(handler));
if (a.ops.autoIncrement) {
builder.addCode(" auto_increment");
}
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.notNull != null) {
if (!a.ops.autoIncrement && !a.ops.primaryKey && a.ops.default == null) {
builder.addCode(" not null");
} 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;
} }
static modifiers = { return curr.split(" ").join("").startsWith(sql.split(" ").join(""));
and: joinArg("and", this), },
or: joinArg("or", this), }
eq: joinArg("=", this),
plus: joinArg("+", this), aggregations = {
minus: joinArg("-", this), count: (handler: Handler, a: Attribute): QueryBuilder => new QueryBuilder([{ data: "count(" + a.toString(handler) + ")" }]),
not: (a: allModifierInput[]):serializeReturn => { 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 = {
and: joinArg("and"),
or: joinArg("or"),
le: joinArg("<"),
leq: joinArg("<="),
eq: joinArg("="),
geq: joinArg(">="),
ge: joinArg(">"),
plus: joinArg("+"),
minus: joinArg("-"),
mult: joinArg("*"),
divide: joinArg("-/"),
not: (handler: Handler, a: allModifierInput[]): QueryBuilder => {
let e = a[0]; let e = a[0];
if (e instanceof Attribute) return ["not (" + e + ")", []]; if (e instanceof Attribute) return new QueryBuilder([{ data: "not (" + e.toString(handler) + ")" }])
if (e instanceof Modifier || e instanceof selectQuery || e instanceof Aggregation) { if (e instanceof Modifier || e instanceof selectQuery || e instanceof Aggregation) {
let [sqli, argsi] = e.serialize(this); const builder = new QueryBuilder();
return ["not (" + sqli + ")", argsi] 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: ")" }
]);
},
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) {
builder.append(a[0].serialize(handler));
} 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) {
builder.append(a[1].serialize(handler));
} 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"),
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[]): QueryBuilder => {
const builder = new QueryBuilder();
builder.addCode("enum(");
builder.addInjectionCommaSeperated(a);
builder.addCode(")");
return builder;
},
set: (a: primaryData[]): QueryBuilder => {
const builder = new QueryBuilder();
builder.addCode("set(");
builder.addInjectionCommaSeperated(a);
builder.addCode(")");
return builder;
},
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"),
date: dataTypeNoArg("date"),
datetime: dataTypeNoArg("datatime"),
timestamp: dataTypeNoArg("timestamp"),
time: dataTypeNoArg("time"),
year: dataTypeNoArg("year"),
} }
}; };
function joinArg(type: string, s: any) { function dataTypeNoArg(type: string) {
return (a: (allModifierInput)[]): serializeReturn => { return (a: primaryData[]): QueryBuilder => {
let args: primaryData[] = []; return new QueryBuilder([{
let sql = "(" + a.map(d => { inject: false,
if (d instanceof Attribute) return d; data: type
if (d instanceof Modifier || d instanceof selectQuery || d instanceof Aggregation) { }]);
let [sqli, argsi] = d.serialize(s); }
args.push(...(argsi.flat(Infinity))); }
return sqli; function dataTypeSingleNum(type: string) {
return (a: primaryData[]): QueryBuilder => {
return new QueryBuilder([
{ data: type + "(" },
{ inject: true, data: a[0] },
{ data: ")" }
]);
}
}
function dataTypeDblNum(type: string) {
return (a: primaryData[]): QueryBuilder => {
return new QueryBuilder([
{ data: type + "(" },
{ inject: true, data: a[0] },
{ data: ", " },
{ inject: true, data: a[1] },
{ data: ")" }
]);
}
} }
args.push(d);
return "?";
}).join(" " + type + " ") + ")";
return [sql, args]
function joinArg(type: string) {
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);
}
if (i + 1 < a.length) builder.addCode(" " + type + " ");
}
return builder;
} }
} }

View file

@ -1,17 +1,27 @@
import { Attribute } from "./db"; import { Attribute, Table } from "./db";
import { Aggregation, BooleanModifier, NumberModifier } from "./dbStructure"; import { Aggregation, BooleanModifier, checkConstraint, Datatype, foreignConstraint, NumberModifier, StringModifier, uniqueConstraint } from "./dbStructure";
import { selectQuery } from "./query"; import { insertQuery, removeQuery, selectQuery, updateQuery } from "./query";
import { allModifierInput, selectElements, selectFromElements } from "./types"; import { allModifierInput, selectElements, selectFromElements } from "./types";
//modifiers //modifiers
export const and = (...args: (BooleanModifier)[]) => new BooleanModifier("and",args); export const and = (...args: BooleanModifier[]) => new BooleanModifier("and", args);
export const or = (...args: (BooleanModifier)[]) => new BooleanModifier("or",args); export const or = (...args: BooleanModifier[]) => new BooleanModifier("or", args);
export const eq = (...args: (allModifierInput)[]) => new BooleanModifier("eq",args); export const ge = (...args: allModifierInput[]) => new BooleanModifier("ge", args);
export const plus = (...args: (allModifierInput)[]) => new NumberModifier("plus",args); export const geq = (...args: allModifierInput[]) => new BooleanModifier("geq", args);
export const minus = (...args: (allModifierInput)[]) => new NumberModifier("minus",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 = (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);
//aggregations //aggregations
export const count = (a: Attribute) => new Aggregation("count", a); export const count = (a: Attribute) => new Aggregation("count", a);
export const sum = (a: Attribute) => new Aggregation("sum", a); export const sum = (a: Attribute) => new Aggregation("sum", a);
export const avg = (a: Attribute) => new Aggregation("avg", a); export const avg = (a: Attribute) => new Aggregation("avg", a);
@ -20,3 +30,64 @@ export const max = (a:Attribute) => new Aggregation("max",a);
//query //query
export const select = (args: selectElements[], from: selectFromElements) => new selectQuery(args, from); 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
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 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 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);
/**
* primary key: kein richtiger Constraint -> renew = drop
* foreign key: ?
* unique: richtiger Constraint -> achtung manchmal nicht entfernabr
* check: richtiger Constraint
*/

View file

@ -1,18 +1,65 @@
import { Attribute, DB } from "./db"; import { Attribute, DB, Table } from "./db";
import { BooleanModifier, Modifier } from "./dbStructure"; import { BooleanModifier, Modifier } from "./dbStructure";
import { Handler } from "./defaultHandler"; import { Handler } from "./defaultHandler";
import { primaryData, selectElements, selectFromElements, serializeReturn } from "./types"; import { allModifierInput, primaryData, selectElements, selectFromElements, serializeReturn } from "./types";
export class Query { export class Query {
sql: string; sql: string;
values: primaryData[]; values: primaryData[];
constructor(sql: string, values: primaryData[]) { constructor([sql, values]: serializeReturn) {
this.sql = sql; this.sql = sql;
this.values = values; 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]);
}
addCodeCommaSeperated(data: string[], comma = ", ") {
for (let i = 0; i < data.length; i++) {
const e = data[i];
this.list.push([false, e]);
if (i + 1 < data.length) this.list.push([false, comma]);
}
}
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 < data.length) this.list.push([false, comma]);
}
}
append(qb: QueryBuilder) {
this.list.push(...qb.list);
}
appendEnding(qb: QueryBuilder) {
this.append(qb);
this.list.push([false, ";"]);
}
isEmpty() {
return this.list.length == 0;
}
/*setHandler(fun:(d:any)=>any){
}*/
}
export class selectQuery { export class selectQuery {
attr: selectElements[] = []; attr: selectElements[] = [];
from: selectFromElements; from: selectFromElements;
@ -30,18 +77,111 @@ export class selectQuery {
this.groupByD = a; this.groupByD = a;
return this; return this;
} }
havingD:Modifier | null = null; havingD: BooleanModifier | null = null;
having(m: Modifier) { having(m: BooleanModifier) {
this.havingD = m; this.havingD = m;
return this; return this;
} }
limitD: number | null = null;
serialize(handler = Handler) : serializeReturn { limit(i: number) {
return handler.querys.select(this); this.limitD = i;
return this;
} }
query(db: DB) { serialize(handler: Handler): QueryBuilder {
const s = this.serialize(db.getHandler()); return handler.querys.select(handler, this);
return new Query(s[0],s[1]); }
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);
const s = handler.builders.query(builder);
return await db.query(s);
} }
} }

View file

@ -1,6 +1,6 @@
import { Attribute, Table } from "./db"; import { Attribute, Table } from "./db";
import { Aggregation, Joins, Modifier } from "./dbStructure"; 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 primaryData = string | number | boolean | null;
export type allModifierInput = primaryData | Modifier | selectQuery | Attribute | Aggregation; export type allModifierInput = primaryData | Modifier | selectQuery | Attribute | Aggregation;
@ -10,8 +10,39 @@ export type selectFromElements = Table | Joins | null;
export type serializeReturn = [string, primaryData[]]; export type serializeReturn = [string, primaryData[]];
export type DatatypeBuild = [QueryBuilder, number];
export type attributeSettings = {
unique?: boolean,
autoIncrement?: boolean,
default?: primaryData,
notNull?: boolean
primaryKey?: boolean,
foreginKey?: {
link: Attribute,
onDelete?: onAction,
onUpdate?: onAction
},
check?: BooleanModifier | ((a: Attribute) => BooleanModifier)
};
export type extendedAttributeSettings = {
type: Datatype,
unique?: boolean,
autoIncrement?: boolean,
default?: primaryData,
notNull?: boolean
primaryKey?: boolean,
foreginKey?: {
link: Attribute,
onDelete?: onAction,
onUpdate?: onAction
},
check?: BooleanModifier | ((a: Attribute) => BooleanModifier)
}
export enum onAction { export enum onAction {
nothing,
cascade, cascade,
noAction, noAction,
setNull, setNull,