diff --git a/src/dbStructure.ts b/src/dbStructure.ts index e61404c..1ca68d8 100644 --- a/src/dbStructure.ts +++ b/src/dbStructure.ts @@ -1,7 +1,7 @@ import { Attribute, Table } from "./db"; import { Handler } from "./defaultHandler"; import { QueryBuilder } from "./query"; -import { allModifierInput, onAction, primaryData, serializeReturn } from "./types"; +import { allModifierInput, joinElements, joinType, onAction, primaryData, serializeReturn } from "./types"; export class Datatype { type: string; @@ -42,11 +42,62 @@ export class Aggregation { return handler.aggregations[this.t as keyof typeof handler.aggregations](handler, this.a); } } -export class Joins { - +export interface Joins { + serialize(handler: Handler): 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 new 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 { diff --git a/src/defaultHandler.ts b/src/defaultHandler.ts index 3765dfb..d30ff0f 100644 --- a/src/defaultHandler.ts +++ b/src/defaultHandler.ts @@ -1,7 +1,7 @@ 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 { allModifierInput, onAction, primaryData, serializeReturn } from "./types" +import { allModifierInput, joinType, onAction, primaryData } from "./types" export class Handler { async syncDB(db: DB, handler: Handler, deleteInDB: boolean = false) { @@ -237,7 +237,14 @@ export class Handler { const builder = new QueryBuilder(); builder.addCode("select "); 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) { builder.addCode(" where "); builder.append(q.whereD.serialize(handler)); @@ -305,7 +312,7 @@ export class Handler { } return qb; }, - remove: (handler: Handler, q: removeQuery): QueryBuilder =>{ + remove: (handler: Handler, q: removeQuery): QueryBuilder => { const qb = new QueryBuilder(); qb.addCode("DELETE FROM "); qb.addCode(q.table.serialize(handler)); @@ -527,6 +534,91 @@ export class 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 = { and: joinArg("and"), or: joinArg("or"), diff --git a/src/funcs.ts b/src/funcs.ts index 77ad471..e5e5ac3 100644 --- a/src/funcs.ts +++ b/src/funcs.ts @@ -1,7 +1,7 @@ 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 { allModifierInput, selectElements, selectFromElements } from "./types"; +import { allModifierInput, joinType, selectElements, selectFromElements } from "./types"; //modifiers 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 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 export const select = (args: selectElements[], from: selectFromElements) => new selectQuery(args, from); export const insert = (...attrs: Attribute[]) => new insertQuery(attrs); diff --git a/src/types.ts b/src/types.ts index 026c038..5b26bfc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,6 +7,7 @@ export type allModifierInput = primaryData | Modifier | selectQuery | Attribute export type selectElements = primaryData | Attribute | Aggregation | selectQuery export type selectFromElements = Table | Joins | null; +export type joinElements = Table | Joins; export type serializeReturn = [string, primaryData[]]; @@ -48,3 +49,11 @@ export enum onAction { setNull, setDefault } + +export enum joinType { + inner, + left, + right, + full, + cross +} \ No newline at end of file