Merge pull request 'Added Joins' (#2) from dev into main
Reviewed-on: #2
This commit is contained in:
commit
f0b3b6a0d8
5 changed files with 229 additions and 18 deletions
50
readme.md
50
readme.md
|
@ -4,14 +4,14 @@
|
||||||
|
|
||||||
TypeScript Libary for relational Database Requests. Requests are written in js/ts and it supports automatic Schema evulution.
|
TypeScript Libary for relational Database Requests. Requests are written in js/ts and it supports automatic Schema evulution.
|
||||||
|
|
||||||
Fetures
|
Features:
|
||||||
- [x] select Query
|
- [x] select Query
|
||||||
- [x] insert Query
|
- [x] insert Query
|
||||||
- [x] update Query
|
- [x] update Query
|
||||||
- [x] remove Query
|
- [x] remove Query
|
||||||
- [x] create Schema
|
- [x] create Schema
|
||||||
- [x] remove unused Schema
|
- [x] remove unused Schema
|
||||||
- [ ] joins
|
- [x] joins
|
||||||
- [ ] change Schema
|
- [ ] change Schema
|
||||||
- [ ] Key-Value Store Shortcut
|
- [ ] Key-Value Store Shortcut
|
||||||
- [ ] Views
|
- [ ] Views
|
||||||
|
@ -101,9 +101,29 @@ let res = await select([Table2.AttrName2], Table1)
|
||||||
.having(le(Table2.AttrName3, 5)) //optional
|
.having(le(Table2.AttrName3, 5)) //optional
|
||||||
.limit(10) //optional
|
.limit(10) //optional
|
||||||
.query(db);
|
.query(db);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Joins:
|
||||||
|
```javascript
|
||||||
|
import { INT, onAction } from "dblang"
|
||||||
|
|
||||||
|
const TableA = db.newTable("TableA");
|
||||||
|
const TableB = db.newTable("TableB");
|
||||||
|
TableA.addAttribute("A1", INT, {
|
||||||
|
primaryKey: true
|
||||||
|
});
|
||||||
|
TableB.addAttribute("B1", INT, {
|
||||||
|
primaryKey: true,
|
||||||
|
foreignKey:{
|
||||||
|
link: TableA.A1,
|
||||||
|
onDelete: onAction.cascade
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let res = await select([TableA.A1, TableB.B1], innerJoinOn(eq(TableA.A1, Tableb.B1)))
|
||||||
|
.query(db);
|
||||||
|
```
|
||||||
|
See: [Joins](#joins)
|
||||||
|
|
||||||
#### Insert:
|
#### Insert:
|
||||||
```javascript
|
```javascript
|
||||||
import { select } from "dblang"
|
import { select } from "dblang"
|
||||||
|
@ -121,6 +141,8 @@ await insert(Table2.AttrName2, Table2.AttrName3)
|
||||||
|
|
||||||
#### Update:
|
#### Update:
|
||||||
```javascript
|
```javascript
|
||||||
|
import { update } from "dblang"
|
||||||
|
|
||||||
await update(Table2)
|
await update(Table2)
|
||||||
.set(Table2.AttrName2, plus(Table2.AttrName2,1))
|
.set(Table2.AttrName2, plus(Table2.AttrName2,1))
|
||||||
.set(Table2.AttrName3, minus(Table2.AttrName2,1))
|
.set(Table2.AttrName3, minus(Table2.AttrName2,1))
|
||||||
|
@ -130,6 +152,8 @@ await update(Table2)
|
||||||
|
|
||||||
#### Remove:
|
#### Remove:
|
||||||
```javascript
|
```javascript
|
||||||
|
import { remove } from "dblang"
|
||||||
|
|
||||||
await remove(Table2)
|
await remove(Table2)
|
||||||
.where(geq(Table2.AttrName2, 5))
|
.where(geq(Table2.AttrName2, 5))
|
||||||
.query(db);
|
.query(db);
|
||||||
|
@ -205,7 +229,25 @@ StringModifier:
|
||||||
|
|
||||||
(v\* = string, number, boolean, null, Modifier, Aggregation, select Query, Attribute)
|
(v\* = string, number, boolean, null, Modifier, Aggregation, select Query, Attribute)
|
||||||
|
|
||||||
# Aggregations
|
#### Joins
|
||||||
|
|
||||||
|
- `crossJoin(t1, t2, ...)`
|
||||||
|
- `naturalJoin(t1, t2, ...)`
|
||||||
|
- `leftNaturalJoin(t1, t2, ...)`
|
||||||
|
- `rightNaturalJoin(t1, t2, ...)`
|
||||||
|
- `fullNaturalJoin(t1, t2, ...)`
|
||||||
|
- `innerJoinUsing(t1, t2, a1, a2, ...)`
|
||||||
|
- `leftJoinUsing(t1, t2, a1, a2, ...)`
|
||||||
|
- `rightJoinUsing(t1, t2, a1, a2, ...)`
|
||||||
|
- `fullJoinUsing(t1, t2, a1, a2, ...)`
|
||||||
|
- `innerJoinOn(t1, t2, b)`
|
||||||
|
- `leftJoinOn(t1, t2, b)`
|
||||||
|
- `rightJoinOn(t1, t2, b)`
|
||||||
|
- `fullJoinOn(t1, t2, b)`
|
||||||
|
|
||||||
|
(t\* = Table or Join; a\* = Attributes; b = Boolean Modifier)
|
||||||
|
|
||||||
|
#### Aggregations
|
||||||
- `count(a)`
|
- `count(a)`
|
||||||
- `sum(a)`
|
- `sum(a)`
|
||||||
- `avg(a)`
|
- `avg(a)`
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Attribute, Table } from "./db";
|
import { Attribute, Table } from "./db";
|
||||||
import { Handler } from "./defaultHandler";
|
import { Handler } from "./defaultHandler";
|
||||||
import { QueryBuilder } from "./query";
|
import { QueryBuilder } from "./query";
|
||||||
import { allModifierInput, onAction, primaryData, serializeReturn } from "./types";
|
import { allModifierInput, joinElements, joinType, onAction, primaryData, serializeReturn } from "./types";
|
||||||
|
|
||||||
export class Datatype {
|
export class Datatype {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -42,11 +42,62 @@ export class Aggregation {
|
||||||
return handler.aggregations[this.t as keyof typeof handler.aggregations](handler, this.a);
|
return handler.aggregations[this.t as keyof typeof handler.aggregations](handler, this.a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class Joins {
|
export interface Joins {
|
||||||
|
serialize(handler: Handler): QueryBuilder
|
||||||
serialize(handler: Handler): QueryBuilder {
|
|
||||||
return new QueryBuilder();
|
|
||||||
}
|
}
|
||||||
|
export class joinNatural implements Joins{
|
||||||
|
tables: joinElements[];
|
||||||
|
type: joinType;
|
||||||
|
constructor(tables:joinElements[], type: joinType){
|
||||||
|
this.tables = tables;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
serialize(handler: Handler): QueryBuilder {
|
||||||
|
return handler.joins.natural(handler,this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export class onJoin implements Joins{
|
||||||
|
tableA: joinElements;
|
||||||
|
tableB: joinElements;
|
||||||
|
type: joinType;
|
||||||
|
on: BooleanModifier;
|
||||||
|
constructor(tableA: joinElements, tableB: joinElements, type: joinType, on: BooleanModifier){
|
||||||
|
this.tableA = tableA;
|
||||||
|
this.tableB = tableB;
|
||||||
|
this.type = type;
|
||||||
|
this.on = on;
|
||||||
|
}
|
||||||
|
serialize(handler: Handler): QueryBuilder {
|
||||||
|
return handler.joins.on(handler,this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export class usingJoin implements Joins{
|
||||||
|
tableA: joinElements;
|
||||||
|
tableB: joinElements;
|
||||||
|
type: joinType;
|
||||||
|
using: Attribute[];
|
||||||
|
constructor(tableA: joinElements, tableB: joinElements, type: joinType, using: Attribute[]){
|
||||||
|
this.tableA = tableA;
|
||||||
|
this.tableB = tableB;
|
||||||
|
this.type = type;
|
||||||
|
this.using = using;
|
||||||
|
}
|
||||||
|
serialize(handler: Handler): QueryBuilder {
|
||||||
|
return handler.joins.using(handler,this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export class joinCross implements Joins{
|
||||||
|
tables: joinElements[];
|
||||||
|
constructor(tables:joinElements[]){
|
||||||
|
this.tables = tables;
|
||||||
|
}
|
||||||
|
serialize(handler: Handler): QueryBuilder {
|
||||||
|
return handler.joins.cross(handler,this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Constraint {
|
export interface Constraint {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { Attribute, DB, Table } from "./db"
|
import { Attribute, DB, Table } from "./db"
|
||||||
import { Aggregation, checkConstraint, Constraint, Datatype, foreignConstraint, Modifier, uniqueConstraint } from "./dbStructure"
|
import { Aggregation, checkConstraint, joinCross, Datatype, foreignConstraint, Modifier, joinNatural, onJoin, uniqueConstraint, usingJoin } from "./dbStructure"
|
||||||
import { insertQuery, Query, QueryBuilder, removeQuery, selectQuery, updateQuery } from "./query"
|
import { insertQuery, Query, QueryBuilder, removeQuery, selectQuery, updateQuery } from "./query"
|
||||||
import { allModifierInput, onAction, primaryData, serializeReturn } from "./types"
|
import { allModifierInput, joinType, onAction, primaryData } from "./types"
|
||||||
|
|
||||||
export class Handler {
|
export class Handler {
|
||||||
async syncDB(db: DB, handler: Handler, deleteInDB: boolean = false) {
|
async syncDB(db: DB, handler: Handler, deleteInDB: boolean = false) {
|
||||||
console.log("start sync");
|
|
||||||
|
|
||||||
let gd = new QueryBuilder();
|
let gd = new QueryBuilder();
|
||||||
gd.addCode(`SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where CONSTRAINT_SCHEMA = `);
|
gd.addCode(`SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where CONSTRAINT_SCHEMA = `);
|
||||||
gd.addInjection(db.name);
|
gd.addInjection(db.name);
|
||||||
|
@ -237,7 +235,14 @@ export class Handler {
|
||||||
const builder = new QueryBuilder();
|
const builder = new QueryBuilder();
|
||||||
builder.addCode("select ");
|
builder.addCode("select ");
|
||||||
builder.append(joinArg(", ")(handler, q.attr));
|
builder.append(joinArg(", ")(handler, q.attr));
|
||||||
builder.addCode(` from ${q.from == null ? 'DUAL' : q.from.serialize(handler)}`);
|
builder.addCode(` from `);
|
||||||
|
if(q.from == null){
|
||||||
|
builder.addCode(" DUAL");
|
||||||
|
}else if (q.from instanceof Table){
|
||||||
|
builder.addCode(q.from.serialize(handler));
|
||||||
|
}else{
|
||||||
|
builder.append(q.from.serialize(handler));
|
||||||
|
}
|
||||||
if (q.whereD) {
|
if (q.whereD) {
|
||||||
builder.addCode(" where ");
|
builder.addCode(" where ");
|
||||||
builder.append(q.whereD.serialize(handler));
|
builder.append(q.whereD.serialize(handler));
|
||||||
|
@ -527,6 +532,91 @@ export class Handler {
|
||||||
max: (handler: Handler, a: Attribute): QueryBuilder => new QueryBuilder([{ data: "max(" + a.toString(handler) + ")" }]),
|
max: (handler: Handler, a: Attribute): QueryBuilder => new QueryBuilder([{ data: "max(" + a.toString(handler) + ")" }]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
joins = {
|
||||||
|
natural: (handler: Handler, j: joinNatural) => {
|
||||||
|
const qb = new QueryBuilder();
|
||||||
|
for (let i = 0; i < j.tables.length; i++) {
|
||||||
|
const t = j.tables[i];
|
||||||
|
if (t instanceof Table) qb.addCode(t.serialize(handler));
|
||||||
|
else {
|
||||||
|
qb.addCode("(");
|
||||||
|
qb.append(t.serialize(handler));
|
||||||
|
qb.addCode(")");
|
||||||
|
}
|
||||||
|
if (i + 1 < j.tables.length) {
|
||||||
|
qb.addCode(" natural ");
|
||||||
|
if (j.type == joinType.left) qb.addCode("left");
|
||||||
|
if (j.type == joinType.right) qb.addCode("right");
|
||||||
|
if (j.type == joinType.full) qb.addCode("full");
|
||||||
|
qb.addCode(" join ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qb;
|
||||||
|
},
|
||||||
|
using: (handler: Handler, j: usingJoin) => {
|
||||||
|
const qb = new QueryBuilder();
|
||||||
|
if (j.tableA instanceof Table) qb.addCode(j.tableA.serialize(handler));
|
||||||
|
else {
|
||||||
|
qb.addCode("(");
|
||||||
|
qb.append(j.tableA.serialize(handler));
|
||||||
|
qb.addCode(")");
|
||||||
|
}
|
||||||
|
qb.addCode(" ");
|
||||||
|
if (j.type == joinType.left) qb.addCode("left ");
|
||||||
|
if (j.type == joinType.right) qb.addCode("right ");
|
||||||
|
if (j.type == joinType.full) qb.addCode("full ");
|
||||||
|
qb.addCode("join ");
|
||||||
|
if (j.tableA instanceof Table) qb.addCode(j.tableA.serialize(handler));
|
||||||
|
else {
|
||||||
|
qb.addCode("(");
|
||||||
|
qb.append(j.tableA.serialize(handler));
|
||||||
|
qb.addCode(")");
|
||||||
|
}
|
||||||
|
qb.addCode(" using (");
|
||||||
|
qb.addCodeCommaSeperated(j.using.map(t => t.serialize(handler)));
|
||||||
|
qb.addCode(")");
|
||||||
|
return qb;
|
||||||
|
},
|
||||||
|
on: (handler: Handler, j: onJoin) => {
|
||||||
|
const qb = new QueryBuilder();
|
||||||
|
if (j.tableA instanceof Table) qb.addCode(j.tableA.serialize(handler));
|
||||||
|
else {
|
||||||
|
qb.addCode("(");
|
||||||
|
qb.append(j.tableA.serialize(handler));
|
||||||
|
qb.addCode(")");
|
||||||
|
}
|
||||||
|
qb.addCode(" ");
|
||||||
|
if (j.type == joinType.left) qb.addCode("left ");
|
||||||
|
if (j.type == joinType.right) qb.addCode("right ");
|
||||||
|
if (j.type == joinType.full) qb.addCode("full ");
|
||||||
|
qb.addCode("join ");
|
||||||
|
if (j.tableA instanceof Table) qb.addCode(j.tableA.serialize(handler));
|
||||||
|
else {
|
||||||
|
qb.addCode("(");
|
||||||
|
qb.append(j.tableA.serialize(handler));
|
||||||
|
qb.addCode(")");
|
||||||
|
}
|
||||||
|
qb.addCode(" on (");
|
||||||
|
qb.append(j.on.serialize(handler));
|
||||||
|
qb.addCode(")");
|
||||||
|
return qb;
|
||||||
|
},
|
||||||
|
cross: (handler: Handler, j: joinCross) => {
|
||||||
|
const qb = new QueryBuilder();
|
||||||
|
for (let i = 0; i < j.tables.length; i++) {
|
||||||
|
const t = j.tables[i];
|
||||||
|
if (t instanceof Table) qb.addCode(t.serialize(handler));
|
||||||
|
else {
|
||||||
|
qb.addCode("(");
|
||||||
|
qb.append(t.serialize(handler));
|
||||||
|
qb.addCode(")");
|
||||||
|
}
|
||||||
|
if (i + 1 < j.tables.length) qb.addCode(" cross join ");
|
||||||
|
}
|
||||||
|
return qb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
modifiers = {
|
modifiers = {
|
||||||
and: joinArg("and"),
|
and: joinArg("and"),
|
||||||
or: joinArg("or"),
|
or: joinArg("or"),
|
||||||
|
|
23
src/funcs.ts
23
src/funcs.ts
|
@ -1,7 +1,7 @@
|
||||||
import { Attribute, Table } from "./db";
|
import { Attribute, Table } from "./db";
|
||||||
import { Aggregation, BooleanModifier, checkConstraint, Datatype, foreignConstraint, NumberModifier, StringModifier, uniqueConstraint } from "./dbStructure";
|
import { Aggregation, BooleanModifier, checkConstraint, Datatype, foreignConstraint, joinCross, joinNatural, usingJoin, onJoin, NumberModifier, StringModifier, uniqueConstraint } from "./dbStructure";
|
||||||
import { insertQuery, removeQuery, selectQuery, updateQuery } from "./query";
|
import { insertQuery, removeQuery, selectQuery, updateQuery } from "./query";
|
||||||
import { allModifierInput, selectElements, selectFromElements } from "./types";
|
import { allModifierInput, joinType, selectElements, selectFromElements } from "./types";
|
||||||
|
|
||||||
//modifiers
|
//modifiers
|
||||||
export const and = (...args: BooleanModifier[]) => new BooleanModifier("and", args);
|
export const and = (...args: BooleanModifier[]) => new BooleanModifier("and", args);
|
||||||
|
@ -28,6 +28,25 @@ 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);
|
||||||
|
|
||||||
|
//join
|
||||||
|
|
||||||
|
export const crossJoin = (...tables: Table[]) => new joinCross(tables);
|
||||||
|
|
||||||
|
export const naturalJoin = (...tables: Table[]) => new joinNatural(tables, joinType.inner);
|
||||||
|
export const leftNaturalJoin = (...tables: Table[]) => new joinNatural(tables, joinType.left);
|
||||||
|
export const rightNaturalJoin = (...tables: Table[]) => new joinNatural(tables, joinType.right);
|
||||||
|
export const fullNaturalJoin = (...tables: Table[]) => new joinNatural(tables, joinType.full);
|
||||||
|
|
||||||
|
export const innerJoinUsing = (tableA: Table, tableB: Table, ...using: Attribute[]) => new usingJoin(tableA, tableB, joinType.inner, using);
|
||||||
|
export const leftJoinUsing = (tableA: Table, tableB: Table, ...using: Attribute[]) => new usingJoin(tableA, tableB, joinType.left, using);
|
||||||
|
export const rightJoinUsing = (tableA: Table, tableB: Table, ...using: Attribute[]) => new usingJoin(tableA, tableB, joinType.right, using);
|
||||||
|
export const fullJoinUsing = (tableA: Table, tableB: Table, ...using: Attribute[]) => new usingJoin(tableA, tableB, joinType.full, using);
|
||||||
|
|
||||||
|
export const innerJoinOn = (tableA: Table, tableB: Table, on: BooleanModifier) => new onJoin(tableA, tableB, joinType.inner, on);
|
||||||
|
export const leftJoinOn = (tableA: Table, tableB: Table, on: BooleanModifier) => new onJoin(tableA, tableB, joinType.left, on);
|
||||||
|
export const rightJoinOn = (tableA: Table, tableB: Table, on: BooleanModifier) => new onJoin(tableA, tableB, joinType.right, on);
|
||||||
|
export const fullJoinOn = (tableA: Table, tableB: Table, on: BooleanModifier) => new onJoin(tableA, tableB, joinType.full, on);
|
||||||
|
|
||||||
//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 insert = (...attrs: Attribute[]) => new insertQuery(attrs);
|
||||||
|
|
|
@ -7,6 +7,7 @@ export type allModifierInput = primaryData | Modifier | selectQuery | Attribute
|
||||||
|
|
||||||
export type selectElements = primaryData | Attribute | Aggregation | selectQuery
|
export type selectElements = primaryData | Attribute | Aggregation | selectQuery
|
||||||
export type selectFromElements = Table | Joins | null;
|
export type selectFromElements = Table | Joins | null;
|
||||||
|
export type joinElements = Table | Joins;
|
||||||
|
|
||||||
export type serializeReturn = [string, primaryData[]];
|
export type serializeReturn = [string, primaryData[]];
|
||||||
|
|
||||||
|
@ -48,3 +49,11 @@ export enum onAction {
|
||||||
setNull,
|
setNull,
|
||||||
setDefault
|
setDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum joinType {
|
||||||
|
inner,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
full,
|
||||||
|
cross
|
||||||
|
}
|
Loading…
Reference in a new issue