First Working Version #1
9 changed files with 1395 additions and 161 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
ltests
|
ltests
|
||||||
|
sqlsave
|
|
@ -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
214
readme.md
|
@ -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)
|
151
src/db.ts
151
src/db.ts
|
@ -1,75 +1,142 @@
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
newTable(name:string){
|
async sync(force = false) {
|
||||||
return new Table(name);
|
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) {
|
||||||
|
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) + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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?: {
|
if (!noErrorOnNameConflict) throw new Error("You cannot name Attribute like Methode of this Table!");
|
||||||
link: Attribute,
|
} else {
|
||||||
onDelete?: onAction,
|
this[lowName] = attr;
|
||||||
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!");
|
|
||||||
}else{
|
|
||||||
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';
|
||||||
export {onAction};
|
export { onAction };
|
|
@ -1,39 +1,131 @@
|
||||||
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;
|
||||||
a : Array<allModifierInput>;
|
a: Array<allModifierInput>;
|
||||||
constructor(type: string, args: (allModifierInput)[]) {
|
constructor(type: string, args: (allModifierInput)[]) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BooleanModifier extends Modifier{}
|
export class BooleanModifier extends Modifier { }
|
||||||
export class NumberModifier extends Modifier{}
|
export class NumberModifier extends Modifier { }
|
||||||
export class StringModifier extends Modifier{}
|
export class StringModifier extends Modifier { }
|
||||||
|
|
||||||
export class Aggregation{
|
export class Aggregation {
|
||||||
t : string;
|
t: string;
|
||||||
a : Attribute;
|
a: Attribute;
|
||||||
constructor(type: string, args: Attribute) {
|
constructor(type: string, args: Attribute) {
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 => {
|
||||||
args.push(d);
|
return new QueryBuilder([
|
||||||
return "?";
|
{ data: type + "(" },
|
||||||
}).join(" " + type + " ") + ")";
|
{ inject: true, data: a[0] },
|
||||||
return [sql, args]
|
{ 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: ")" }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
101
src/funcs.ts
101
src/funcs.ts
|
@ -1,22 +1,93 @@
|
||||||
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);
|
export const min = (a: Attribute) => new Aggregation("min", a);
|
||||||
export const min = (a:Attribute) => new Aggregation("min",a);
|
export const max = (a: Attribute) => new Aggregation("max", a);
|
||||||
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
|
||||||
|
*/
|
174
src/query.ts
174
src/query.ts
|
@ -1,26 +1,73 @@
|
||||||
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;
|
||||||
constructor(a: selectElements[], from:selectFromElements) {
|
constructor(a: selectElements[], from: selectFromElements) {
|
||||||
this.attr.push(...a.flat(Infinity));
|
this.attr.push(...a.flat(Infinity));
|
||||||
this.from = from ? from : null;
|
this.from = from ? from : null;
|
||||||
}
|
}
|
||||||
whereD:BooleanModifier | null = null;
|
whereD: BooleanModifier | null = null;
|
||||||
where(m: BooleanModifier) {
|
where(m: BooleanModifier) {
|
||||||
this.whereD = m;
|
this.whereD = m;
|
||||||
return this;
|
return this;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
37
src/types.ts
37
src/types.ts
|
@ -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 enum onAction{
|
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 {
|
||||||
|
nothing,
|
||||||
cascade,
|
cascade,
|
||||||
noAction,
|
noAction,
|
||||||
setNull,
|
setNull,
|
||||||
|
|
Loading…
Reference in a new issue