Skip to content

Commit

Permalink
Add support for calling functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ChiriVulpes committed Dec 6, 2024
1 parent c14aa36 commit b688fa1
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 22 deletions.
8 changes: 7 additions & 1 deletion src/Database.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Pool, PoolClient } from "pg";
import FunctionCall from "./FunctionCall";
import { History } from "./History";
import { DatabaseSchema } from "./Schema";
import { DatabaseSchema, FunctionParameters } from "./Schema";
import Table from "./Table";

export default class Database<SCHEMA extends DatabaseSchema> {
Expand Down Expand Up @@ -34,4 +35,9 @@ export default class Database<SCHEMA extends DatabaseSchema> {
type TABLE = DatabaseSchema.Table<SCHEMA, TABLE_NAME>;
return new Table<TABLE, SCHEMA, TABLE_NAME>(tableName, this.schema.tables[tableName as string] as TABLE);
}

public function<FUNCTION_NAME extends DatabaseSchema.FunctionName<SCHEMA>> (functionName: FUNCTION_NAME, ...params: FunctionParameters<DatabaseSchema.Function<SCHEMA, FUNCTION_NAME>>) {
type FUNCTION = DatabaseSchema.Function<SCHEMA, FUNCTION_NAME>;
return FunctionCall<FUNCTION, SCHEMA, FUNCTION_NAME>(functionName, params);
}
}
30 changes: 30 additions & 0 deletions src/FunctionCall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { OptionalTypeString, TypeString } from "./IStrongPG";
import Schema, { DatabaseSchema, FunctionParameters, FunctionSchema, TableSchema } from "./Schema";
import { SelectFromVirtualTable } from "./statements/Select";

export type FunctionOutput<SCHEMA extends DatabaseSchema, FUNCTION extends FunctionSchema, FUNCTION_NAME extends DatabaseSchema.FunctionName<SCHEMA>> =
FUNCTION extends FunctionSchema<(TypeString | OptionalTypeString)[], infer OUT, infer RETURN> ?
{ [I in keyof OUT as OUT[I] extends [TypeString, infer NAME extends PropertyKey] ? NAME : never]: OUT[I] extends [infer TYPE extends TypeString, string] ? TYPE : never } extends infer OUT_COLUMNS ?
RETURN extends `SETOF ${infer TABLE_NAME extends DatabaseSchema.TableName<SCHEMA>}` ?
DatabaseSchema.Table<SCHEMA, TABLE_NAME> extends infer TABLE ?

OUT["length"] extends 0 ? TABLE
: { [KEY in keyof TABLE | keyof OUT_COLUMNS]: KEY extends keyof OUT_COLUMNS ? OUT_COLUMNS[KEY] : KEY extends keyof TABLE ? Extract<TABLE[KEY], TypeString> : never }

: never
: (OUT["length"] extends 0 ? { [KEY in FUNCTION_NAME]: RETURN } : "Error! Function return type must be a table if there are OUT parameters")
: never
: never

interface FunctionCall<FUNCTION extends FunctionSchema, SCHEMA extends DatabaseSchema, FUNCTION_NAME extends DatabaseSchema.FunctionName<SCHEMA>, OUTPUT = FunctionOutput<SCHEMA, FUNCTION, FUNCTION_NAME>> {
select<COLUMNS extends Schema.Column<OUTPUT>[]> (...columns: COLUMNS): SelectFromVirtualTable<Extract<OUTPUT, TableSchema>, never, COLUMNS>
}

function FunctionCall<FUNCTION extends FunctionSchema, SCHEMA extends DatabaseSchema, FUNCTION_NAME extends DatabaseSchema.FunctionName<SCHEMA>> (name: FUNCTION_NAME, params: FunctionParameters<DatabaseSchema.Function<SCHEMA, FUNCTION_NAME>>): FunctionCall<FUNCTION, SCHEMA, FUNCTION_NAME> {
return {
select: (...columns) => new SelectFromVirtualTable<never, never, never>(name, columns as never) as never,
}
}

export default FunctionCall

10 changes: 5 additions & 5 deletions src/Migration.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Database from "./Database";
import { StackUtil, TypeString } from "./IStrongPG";
import { DatabaseSchema, TableSchema } from "./Schema";
import { OptionalTypeString, StackUtil, TypeString } from "./IStrongPG";
import { DatabaseSchema, FunctionSchema, TableSchema } from "./Schema";
import CreateCollation from "./statements/collation/CreateCollation";
import DropCollation from "./statements/collation/DropCollation";
import AlterEnum, { AlterEnumInitialiser } from "./statements/enum/AlterEnum";
import CreateEnum from "./statements/enum/CreateEnum";
import DropEnum from "./statements/enum/DropEnum";
import CreateOrReplaceFunction, { CreateOrReplaceFunctionInitialiser, Function } from "./statements/function/CreateOrReplaceFunction";
import CreateOrReplaceFunction, { CreateOrReplaceFunctionInitialiser } from "./statements/function/CreateOrReplaceFunction";
import DropFunction from "./statements/function/DropFunction";
import CreateIndex, { CreateIndexInitialiser } from "./statements/index/CreateIndex";
import DropIndex from "./statements/index/DropIndex";
Expand Down Expand Up @@ -223,12 +223,12 @@ export default class Migration<SCHEMA_START extends DatabaseSchema | null = null
return this as any;
}

public createOrReplaceFunction<NAME extends string, IN extends TypeString[], OUT extends [TypeString, string][], RETURN extends TypeString> (
public createOrReplaceFunction<NAME extends string, IN extends (TypeString | OptionalTypeString)[], OUT extends [TypeString, string][], RETURN extends TypeString> (
name: NAME,
initialiser: CreateOrReplaceFunctionInitialiser<IN, OUT, RETURN>,
): Migration<SCHEMA_START, {
[KEY in keyof SCHEMA_END]: KEY extends "functions"
? ({ [FUNCTION_NAME in NAME | keyof SCHEMA_END["functions"]]: FUNCTION_NAME extends NAME ? Function<IN, OUT, RETURN> : SCHEMA_END["functions"][FUNCTION_NAME] })
? ({ [FUNCTION_NAME in NAME | keyof SCHEMA_END["functions"]]: FUNCTION_NAME extends NAME ? FunctionSchema<IN, OUT, RETURN> : SCHEMA_END["functions"][FUNCTION_NAME] })
: SCHEMA_END[KEY]
}> {
this.add(initialiser(new CreateOrReplaceFunction(name)).setCaller());
Expand Down
24 changes: 17 additions & 7 deletions src/Schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { DataTypeID, EnumToTuple, InputTypeFromString, OptionalTypeString, OutputTypeFromString, TypeString, TypeStringMap } from "./IStrongPG";
import { Function } from "./statements/function/CreateOrReplaceFunction";

interface SpecialKeys<SCHEMA> {
PRIMARY_KEY?: keyof SCHEMA | (keyof SCHEMA)[];
Expand All @@ -16,10 +15,18 @@ export interface DatabaseSchema {
indices: Record<string, {}>;
enums: Record<string, string[]>;
triggers: Record<string, {}>;
functions: Record<string, (...args: any[]) => any>;
functions: Record<string, FunctionSchema>;
collations: Record<string, {}>;
}

export interface FunctionSchema<IN extends (TypeString | OptionalTypeString)[] = (TypeString | OptionalTypeString)[], OUT extends [TypeString, string][] = [TypeString, string][], RETURN extends TypeString = TypeString> {
in: IN;
out: OUT;
return: RETURN;
}

export type FunctionParameters<SCHEMA extends FunctionSchema> = SCHEMA extends FunctionSchema<infer IN, any, any> ? { [I in keyof IN]: InputTypeFromString<IN[I]> | (IN[I] extends OptionalTypeString ? null | undefined : never) } : never;

export namespace DatabaseSchema {
export interface Empty {
tables: {};
Expand All @@ -40,6 +47,9 @@ export namespace DatabaseSchema {
export type Table<SCHEMA extends DatabaseSchema, NAME extends TableName<SCHEMA>> =
SCHEMA["tables"][NAME] extends infer TABLE ? TABLE extends TableSchema ? TABLE : never : never;

export type Function<SCHEMA extends DatabaseSchema, NAME extends FunctionName<SCHEMA>> =
SCHEMA["functions"][NAME] extends infer FUNCTION extends FunctionSchema<infer IN, infer OUT, infer RETURN> ? FUNCTION : never;

export type Enum<SCHEMA extends DatabaseSchema, NAME extends EnumName<SCHEMA>> =
SCHEMA["enums"][NAME] extends infer ENUM ? ENUM extends string[] ? ENUM : never : never;
}
Expand Down Expand Up @@ -90,10 +100,10 @@ export interface SchemaEnum<ENUM> {
VALUES: ENUM;
}

export interface SchemaFunctionFactory<IN extends TypeString[], OUT extends [TypeString, string][] = [], RETURNS extends TypeString = "VOID"> {
export interface SchemaFunctionFactory<IN extends (TypeString | OptionalTypeString)[], OUT extends [TypeString, string][] = [], RETURNS extends TypeString = "VOID"> {
out<TYPE extends TypeString, NAME extends string> (type: TYPE, name: NAME): SchemaFunctionFactory<IN, [...OUT, [TYPE, NAME]]>
returns<TYPE extends TypeString> (returns: TYPE): SchemaFunctionFactory<IN, OUT, TYPE>
get (): Function<IN, OUT, RETURNS>
get (): FunctionSchema<IN, OUT, RETURNS>
}

class Schema {
Expand Down Expand Up @@ -134,14 +144,14 @@ class Schema {

public static readonly INDEX = {};
public static readonly TRIGGER = {};
public static readonly TRIGGER_FUNCTION: Function<[], [], "TRIGGER"> = () => ({ return: "TRIGGER", out: [] });
public static readonly TRIGGER_FUNCTION: FunctionSchema<[], [], "TRIGGER"> = { in: [], out: [], return: "TRIGGER" };
public static readonly COLLATION = {};

public static function<IN extends TypeString[]> (...args: IN): SchemaFunctionFactory<IN> {
public static function<IN extends (TypeString | OptionalTypeString)[]> (...args: IN): SchemaFunctionFactory<IN> {
const factory: SchemaFunctionFactory<any[], any[], any> = {
out: (type, name) => factory as never,
returns: returns => factory as never,
get: () => () => 0 as never,
get: () => 0 as never,
};

return factory as never;
Expand Down
15 changes: 6 additions & 9 deletions src/statements/function/CreateOrReplaceFunction.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { Initialiser, TypeString } from "../../IStrongPG";
import { Initialiser, OptionalTypeString, TypeString } from "../../IStrongPG";
import Statement from "../Statement";

export type CreateOrReplaceFunctionInitialiser<IN extends TypeString[], OUT extends [TypeString, string][], RETURN extends TypeString> =
export type CreateOrReplaceFunctionInitialiser<IN extends (TypeString | OptionalTypeString)[], OUT extends [TypeString, string][], RETURN extends TypeString> =
Initialiser<CreateOrReplaceFunction, CreateOrReplaceFunction<true, IN, OUT, RETURN>>;

export type Function<IN extends TypeString[], OUT extends [TypeString, string][], RETURN extends TypeString> =
(...args: { [I in keyof IN]: IN[I][0] }) => { return: RETURN, out: OUT };

export default class CreateOrReplaceFunction<HAS_CODE extends boolean = false, IN extends TypeString[] = [], OUT extends [TypeString, string][] = [], RETURN extends TypeString = never> extends Statement {
export default class CreateOrReplaceFunction<HAS_CODE extends boolean = false, IN extends (TypeString | OptionalTypeString)[] = [], OUT extends [TypeString, string][] = [], RETURN extends TypeString = never> extends Statement {

protected readonly hasCode!: HAS_CODE;
private argsIn: [TypeString, string][] = [];
private argsIn: [TypeString | OptionalTypeString, string][] = [];
private argsOut: OUT = [] as never;
private returnType?: RETURN;
private code!: string;
Expand All @@ -20,7 +17,7 @@ export default class CreateOrReplaceFunction<HAS_CODE extends boolean = false, I
super();
}

public in<TYPE extends TypeString> (type: TYPE, name: string): CreateOrReplaceFunction<HAS_CODE, [...IN, TYPE], OUT, RETURN> {
public in<TYPE extends TypeString | OptionalTypeString> (type: TYPE, name: string): CreateOrReplaceFunction<HAS_CODE, [...IN, TYPE], OUT, RETURN> {
this.argsIn.push([type, name]);
return this as never;
}
Expand Down Expand Up @@ -57,7 +54,7 @@ export default class CreateOrReplaceFunction<HAS_CODE extends boolean = false, I
}

public compile () {
const params = this.argsIn.map(([type, name]) => `${name ?? ""} ${type}`)
const params = this.argsIn.map(([type, name]) => `${name ?? ""} ${typeof type === "string" ? type : type.type}`)
.concat(this.argsOut.map(([type, name]) => `OUT ${name ?? ""} ${type}`))
.join(", ");
const out = this.returnType ?? "TRIGGER"
Expand Down

0 comments on commit b688fa1

Please sign in to comment.