-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c57934c
commit 56260f9
Showing
8 changed files
with
228 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,81 @@ | ||
import { Initialiser } from "./IStrongPG"; | ||
import Schema, { TableSchema } from "./Schema"; | ||
import InsertIntoTable, { InsertIntoTableFactory } from "./statements/Insert"; | ||
import SelectFromTable from "./statements/Select"; | ||
import UpdateTable from "./statements/Update"; | ||
|
||
export default class Table<SCHEMA extends TableSchema> { | ||
public constructor (protected readonly name: string, protected readonly schema: SCHEMA) { | ||
} | ||
|
||
/** | ||
* SELECT * | ||
*/ | ||
public select (): SelectFromTable<SCHEMA, "*"[]>; | ||
/** | ||
* SELECT * | ||
* ...then provide an initialiser for tweaking the query | ||
*/ | ||
public select<RETURN extends SelectFromTable<SCHEMA, "*"[], any> = SelectFromTable<SCHEMA, "*"[]>> (initialiser: Initialiser<SelectFromTable<SCHEMA, "*"[]>, RETURN>): RETURN; | ||
/** | ||
* SELECT columns | ||
*/ | ||
public select<COLUMNS extends Schema.Column<SCHEMA>[]> (...columns: COLUMNS): SelectFromTable<SCHEMA, COLUMNS>; | ||
public select<COLUMNS extends Schema.Column<SCHEMA>[]> (...columnsAndInitialiser: [...COLUMNS, Initialiser<SelectFromTable<SCHEMA, COLUMNS>>]): SelectFromTable<SCHEMA, COLUMNS>; | ||
public select (...params: (Schema.Column<SCHEMA> | Initialiser<SelectFromTable<SCHEMA>>)[]) { | ||
/** | ||
* SELECT columns | ||
* ...then provide an initialiser for tweaking the query | ||
*/ | ||
public select<COLUMNS extends Schema.Column<SCHEMA>[], RETURN extends SelectFromTable<SCHEMA, COLUMNS, any>> (...columnsAndInitialiser: [...COLUMNS, Initialiser<SelectFromTable<SCHEMA, COLUMNS>, RETURN>]): RETURN; | ||
public select (...params: (Schema.Column<SCHEMA> | "*" | Initialiser<SelectFromTable<SCHEMA>> | Initialiser<SelectFromTable<SCHEMA, "*"[]>>)[]): SelectFromTable<SCHEMA, Schema.Column<SCHEMA>[]> | SelectFromTable<SCHEMA, "*"[]> { | ||
const initialiser = typeof params[params.length - 1] === "function" ? params.pop() as Initialiser<SelectFromTable<SCHEMA>> : undefined; | ||
const query = new SelectFromTable<SCHEMA>(this.name, this.schema, params as Schema.Column<SCHEMA>[]); | ||
initialiser?.(query); | ||
return query; | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return | ||
return initialiser?.(query) ?? query; | ||
} | ||
|
||
public insert (data: Partial<Schema.RowInput<SCHEMA>>): InsertIntoTable<SCHEMA>; | ||
public insert (data: Partial<Schema.RowInput<SCHEMA>>, initialiser: Initialiser<InsertIntoTable<SCHEMA>>): InsertIntoTable<SCHEMA>; | ||
public insert<COLUMNS extends Schema.Column<SCHEMA>[]> (...columns: COLUMNS): InsertIntoTableFactory<SCHEMA, COLUMNS>; | ||
public insert<COLUMNS extends Schema.Column<SCHEMA>[], RETURN extends InsertIntoTableFactory<SCHEMA, COLUMNS> | InsertIntoTable<SCHEMA>> (...columnsAndInitialiser: [...COLUMNS, Initialiser<InsertIntoTableFactory<SCHEMA, COLUMNS>, RETURN>]): RETURN; | ||
public insert (...params: (boolean | Partial<Schema.RowInput<SCHEMA>> | Schema.Column<SCHEMA> | Initialiser<InsertIntoTableFactory<SCHEMA>> | Initialiser<InsertIntoTable<SCHEMA>>)[]): InsertIntoTableFactory<SCHEMA> | InsertIntoTable<SCHEMA> { | ||
const isUpsert = params[0] === true; | ||
if (typeof params[0] === "boolean") | ||
params.shift(); | ||
|
||
const initialiser = typeof params[params.length - 1] === "function" ? params.pop() as Initialiser<InsertIntoTableFactory<SCHEMA> | InsertIntoTable<SCHEMA>> : undefined; | ||
|
||
if (typeof params[0] === "object") { | ||
const keys = Object.keys(params[0]); | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call | ||
const query = ((this.insert as any)(isUpsert as any, ...keys as Schema.Column<SCHEMA>[]) as InsertIntoTableFactory<SCHEMA>) | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return | ||
.values(...keys.map(key => (params[0] as any)[key])); | ||
|
||
return initialiser?.(query as InsertIntoTable<SCHEMA>) as InsertIntoTable<SCHEMA> ?? query; | ||
} | ||
|
||
const query = InsertIntoTable.columns<SCHEMA>(this.name, this.schema, params as Schema.Column<SCHEMA>[], isUpsert); | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return | ||
return initialiser?.(query) ?? query; | ||
} | ||
|
||
public upsert (data: Schema.RowInput<SCHEMA>): InsertIntoTable<SCHEMA>; | ||
public upsert<RETURN extends InsertIntoTable<SCHEMA, any>> (data: Schema.RowInput<SCHEMA>, initialiser: Initialiser<InsertIntoTable<SCHEMA>, RETURN>): RETURN; | ||
public upsert<COLUMNS extends Schema.Column<SCHEMA>[]> (...columns: COLUMNS): InsertIntoTableFactory<SCHEMA, COLUMNS>; | ||
public upsert<COLUMNS extends Schema.Column<SCHEMA>[], RETURN extends InsertIntoTableFactory<SCHEMA, COLUMNS> | InsertIntoTable<SCHEMA>> (...columnsAndInitialiser: [...COLUMNS, Initialiser<InsertIntoTableFactory<SCHEMA, COLUMNS>, RETURN>]): RETURN; | ||
public upsert (...params: (Schema.RowInput<SCHEMA> | Schema.Column<SCHEMA> | Initialiser<InsertIntoTableFactory<SCHEMA>> | Initialiser<InsertIntoTable<SCHEMA>>)[]): InsertIntoTableFactory<SCHEMA> | InsertIntoTable<SCHEMA> { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call | ||
return (this.insert as any)(true, ...params as Schema.Column<SCHEMA>[]); | ||
} | ||
|
||
public update (data: Schema.RowInput<SCHEMA>): UpdateTable<SCHEMA>; | ||
public update<RETURN extends UpdateTable<SCHEMA, any>> (data: Schema.RowInput<SCHEMA>, initialiser: Initialiser<UpdateTable<SCHEMA>, RETURN>): RETURN; | ||
public update (data: Schema.RowInput<SCHEMA>, initialiser?: Initialiser<UpdateTable<SCHEMA>, UpdateTable<SCHEMA, any>>): UpdateTable<SCHEMA, any> { | ||
const query = new UpdateTable<SCHEMA, any>(this.name, this.schema); | ||
for (const key of Object.keys(data)) | ||
query.set(key as Schema.Column<SCHEMA>, data[key]); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument | ||
return initialiser?.(query) ?? query; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { QueryResult } from "pg"; | ||
import { Initialiser, InputTypeFromString, Value } from "../IStrongPG"; | ||
import Schema, { TableSchema } from "../Schema"; | ||
import Expression from "../expressions/Expression"; | ||
import Statement from "./Statement"; | ||
import UpdateTable from "./Update"; | ||
|
||
export interface InsertIntoTableFactory<SCHEMA extends TableSchema, COLUMNS extends Schema.Column<SCHEMA>[] = Schema.Column<SCHEMA>[]> { | ||
values (...values: { [I in keyof COLUMNS]: InputTypeFromString<SCHEMA[COLUMNS[I]]> }): InsertIntoTable<SCHEMA, COLUMNS>; | ||
} | ||
|
||
export default class InsertIntoTable<SCHEMA extends TableSchema, RESULT = []> extends Statement<RESULT> { | ||
|
||
public static columns<SCHEMA extends TableSchema, COLUMNS extends Schema.Column<SCHEMA>[] = Schema.Column<SCHEMA>[]> (tableName: string, schema: SCHEMA, columns: COLUMNS, isUpsert = false): InsertIntoTableFactory<SCHEMA, COLUMNS> { | ||
return { | ||
values: (...values: any[]) => { | ||
const query = new InsertIntoTable<SCHEMA, COLUMNS>(tableName, schema, columns, values as never); | ||
if (isUpsert) { | ||
query.onConflictDoUpdate(update => { | ||
for (let i = 0; i < columns.length; i++) { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | ||
update.set(columns[i], values[i]); | ||
} | ||
}); | ||
} | ||
|
||
return query; | ||
}, | ||
}; | ||
} | ||
|
||
private vars: any[] = []; | ||
public constructor (public readonly tableName: string, public readonly schema: SCHEMA, public readonly columns: Schema.Column<SCHEMA>[], public readonly values: Value<Schema.RowInput<SCHEMA>>[]) { | ||
super(); | ||
} | ||
|
||
private onConflict?: null | UpdateTable<SCHEMA, any>; | ||
public onConflictDoNothing () { | ||
this.onConflict = null; | ||
return this; | ||
} | ||
|
||
public onConflictDoUpdate (initialiser: Initialiser<UpdateTable<SCHEMA, any>>) { | ||
this.onConflict = new UpdateTable(undefined, this.schema, this.vars); | ||
initialiser(this.onConflict); | ||
return this; | ||
} | ||
|
||
public compile () { | ||
const values = this.values.map(value => Expression.stringifyValue(value, this.vars)).join(","); | ||
let onConflict = this.onConflict === undefined ? " " | ||
: this.onConflict === null ? "ON CONFLICT DO NOTHING" | ||
: undefined; | ||
|
||
if (this.onConflict) { | ||
const compiled = this.onConflict.compile()[0]; | ||
onConflict = `ON CONFLICT DO ${compiled.text}`; | ||
} | ||
|
||
return this.queryable(`INSERT INTO ${this.tableName} (${this.columns.join(",")}) VALUES (${values}) ${onConflict!}`, undefined, this.vars); | ||
} | ||
|
||
protected override resolveQueryOutput (output: QueryResult<any>) { | ||
return output.rows as RESULT; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { QueryResult } from "pg"; | ||
import { InputTypeFromString, ValidType } from "../IStrongPG"; | ||
import Schema, { TableSchema } from "../Schema"; | ||
import Expression from "../expressions/Expression"; | ||
import Statement from "./Statement"; | ||
|
||
export default class UpdateTable<SCHEMA extends TableSchema, RESULT = []> extends Statement<RESULT> { | ||
|
||
private vars?: any[]; | ||
public constructor (public readonly tableName: string | undefined, public readonly schema: SCHEMA, vars?: any[]) { | ||
super(); | ||
this.vars = vars ?? []; | ||
} | ||
|
||
private assignments: string[] = []; | ||
public set (input: Partial<Schema.RowInput<SCHEMA>>): this; | ||
public set<COLUMN_NAME extends Schema.Column<SCHEMA>> (column: COLUMN_NAME, value: InputTypeFromString<SCHEMA[COLUMN_NAME]>): this; | ||
public set (input: Schema.Column<SCHEMA> | Partial<Schema.RowInput<SCHEMA>>, value?: ValidType) { | ||
if (typeof input === "object") | ||
for (const column of Object.keys(input)) | ||
this.assignments.push(`${column} = ${Expression.stringifyValue(input[column], this.vars)}`); | ||
else | ||
this.assignments.push(`${String(input)} = ${Expression.stringifyValue(value, this.vars)}`); | ||
return this; | ||
} | ||
|
||
public compile () { | ||
return this.queryable(`UPDATE ${this.tableName ?? ""} SET ${this.assignments.join(", ")}`, undefined, this.vars); | ||
} | ||
|
||
protected override resolveQueryOutput (output: QueryResult<any>) { | ||
return output.rows as RESULT; | ||
} | ||
} |
Oops, something went wrong.