@deterministic-code/datasources-postgres 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PostgresAlterTableStatementRenderer.d.ts +6 -0
- package/dist/PostgresAlterTableStatementRenderer.js +20 -0
- package/dist/PostgresBulkUpdateStatementTextRenderer.d.ts +1 -1
- package/dist/PostgresBulkUpdateStatementTextRenderer.js +24 -0
- package/dist/PostgresCreateRoutineStatementRenderer.d.ts +4 -0
- package/dist/PostgresCreateTableStatementRenderer.d.ts +1 -1
- package/dist/PostgresCreateTableStatementRenderer.js +26 -0
- package/dist/PostgresCreateTriggerStatementRenderer.d.ts +4 -0
- package/dist/PostgresCreateTriggerStatementRenderer.js +13 -0
- package/dist/PostgresDriver.d.ts +5 -1
- package/dist/PostgresDriver.js +75 -0
- package/dist/PostgresDropRoutineStatementRenderer.d.ts +4 -0
- package/dist/PostgresDropRoutineStatementRenderer.js +6 -0
- package/dist/PostgresDropTriggerStatementRenderer.d.ts +4 -0
- package/dist/PostgresDropTriggerStatementRenderer.js +6 -0
- package/dist/PostgresIdentityInsertStatementRenderer.d.ts +4 -0
- package/dist/PostgresIdentityInsertStatementRenderer.js +11 -0
- package/dist/PostgresStatementRenderer.d.ts +13 -1
- package/dist/PostgresStatementRenderer.js +64 -0
- package/dist/PostgresStoredProcedureStatementRenderer.d.ts +10 -0
- package/dist/PostgresStoredProcedureStatementRenderer.js +111 -0
- package/dist/bind.d.ts +1 -1
- package/dist/bind.js +35 -0
- package/dist/columnTypes.js +10 -0
- package/dist/command.d.ts +1 -1
- package/dist/command.js +92 -0
- package/dist/connection-options.js +1 -0
- package/dist/connection.d.ts +1 -1
- package/dist/connection.js +72 -0
- package/dist/converters/BinaryConverter.d.ts +1 -1
- package/dist/converters/BinaryConverter.js +14 -0
- package/dist/converters/BoolConverter.d.ts +1 -1
- package/dist/converters/BoolConverter.js +5 -0
- package/dist/converters/DateTimeConverter.d.ts +1 -1
- package/dist/converters/DateTimeConverter.js +6 -0
- package/dist/converters/DecimalConverter.d.ts +1 -1
- package/dist/converters/DecimalConverter.js +5 -0
- package/dist/converters/PostgresRowTypeConverters.d.ts +1 -1
- package/dist/converters/PostgresRowTypeConverters.js +19 -0
- package/dist/converters/UuidConverter.d.ts +1 -1
- package/dist/converters/UuidConverter.js +5 -0
- package/dist/i-postgres-driver.d.ts +1 -1
- package/dist/i-postgres-driver.js +22 -0
- package/dist/index.js +1 -0
- package/dist/scalar.js +1 -0
- package/dist/transaction.d.ts +1 -1
- package/dist/transaction.js +15 -0
- package/package.json +9 -10
- package/src/PostgresBulkUpdateStatementTextRenderer.ts +0 -27
- package/src/PostgresCreateTableStatementRenderer.ts +0 -33
- package/src/PostgresDriver.ts +0 -55
- package/src/PostgresStatementRenderer.test.ts +0 -105
- package/src/PostgresStatementRenderer.ts +0 -34
- package/src/bind.ts +0 -50
- package/src/columnTypes.ts +0 -10
- package/src/command.ts +0 -129
- package/src/connection-options.ts +0 -7
- package/src/connection.ts +0 -95
- package/src/converters/BinaryConverter.test.ts +0 -14
- package/src/converters/BinaryConverter.ts +0 -20
- package/src/converters/BoolConverter.test.ts +0 -14
- package/src/converters/BoolConverter.ts +0 -6
- package/src/converters/DateTimeConverter.test.ts +0 -13
- package/src/converters/DateTimeConverter.ts +0 -11
- package/src/converters/DecimalConverter.test.ts +0 -11
- package/src/converters/DecimalConverter.ts +0 -7
- package/src/converters/PostgresRowTypeConverters.test.ts +0 -18
- package/src/converters/PostgresRowTypeConverters.ts +0 -24
- package/src/converters/UuidConverter.test.ts +0 -11
- package/src/converters/UuidConverter.ts +0 -6
- package/src/i-postgres-driver.ts +0 -61
- package/src/index.ts +0 -1
- package/src/postgres.test.ts +0 -345
- package/src/scalar.ts +0 -3
- package/src/transaction.ts +0 -21
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AlterTableStatementTextRenderer, type AlterAction, type Column } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class PostgresAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
|
|
3
|
+
protected typeName(column: Column): string;
|
|
4
|
+
protected actionSql(action: AlterAction): string;
|
|
5
|
+
private postgresAlter;
|
|
6
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AlterTableStatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { columnTypes } from "./columnTypes.js";
|
|
3
|
+
export class PostgresAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
|
|
4
|
+
typeName(column) {
|
|
5
|
+
return columnTypes[column.type] ?? column.type;
|
|
6
|
+
}
|
|
7
|
+
actionSql(action) {
|
|
8
|
+
return action.kind === "alterColumn" ? this.postgresAlter(action.column) : super.actionSql(action);
|
|
9
|
+
}
|
|
10
|
+
postgresAlter(column) {
|
|
11
|
+
const name = this.quote(column.name);
|
|
12
|
+
const defaultSql = column.default == null
|
|
13
|
+
? `ALTER COLUMN ${name} DROP DEFAULT`
|
|
14
|
+
: `ALTER COLUMN ${name} SET DEFAULT ${column.default.value}`;
|
|
15
|
+
const nullSql = column.notNull == null
|
|
16
|
+
? `ALTER COLUMN ${name} DROP NOT NULL`
|
|
17
|
+
: `ALTER COLUMN ${name} SET NOT NULL`;
|
|
18
|
+
return `ALTER COLUMN ${name} TYPE ${this.typeName(column)}, ${defaultSql}, ${nullSql}`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BulkUpdateStatementTextRenderer, type Cell } from "@datasources
|
|
1
|
+
import { BulkUpdateStatementTextRenderer, type Cell } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class PostgresBulkUpdateStatementTextRenderer extends BulkUpdateStatementTextRenderer {
|
|
3
3
|
protected cell: (column: string, index: number, value: Cell) => string;
|
|
4
4
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BulkUpdateStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
import { columnTypes } from "./columnTypes.js";
|
|
3
|
+
import { isBase64 } from "./converters/BinaryConverter.js";
|
|
4
|
+
import { isIsoDate } from "./converters/DateTimeConverter.js";
|
|
5
|
+
const logical = (value) => value === null
|
|
6
|
+
? undefined
|
|
7
|
+
: typeof value === "boolean"
|
|
8
|
+
? "bool"
|
|
9
|
+
: typeof value === "bigint" || (typeof value === "number" && Number.isInteger(value))
|
|
10
|
+
? "int"
|
|
11
|
+
: typeof value === "number"
|
|
12
|
+
? "float"
|
|
13
|
+
: value instanceof Date || (typeof value === "string" && isIsoDate(value))
|
|
14
|
+
? "datetime"
|
|
15
|
+
: value instanceof Uint8Array || (typeof value === "string" && isBase64(value))
|
|
16
|
+
? "binary"
|
|
17
|
+
: "string";
|
|
18
|
+
export class PostgresBulkUpdateStatementTextRenderer extends BulkUpdateStatementTextRenderer {
|
|
19
|
+
cell = (column, index, value) => {
|
|
20
|
+
const type = logical(value);
|
|
21
|
+
const sql = type ? columnTypes[type] : undefined;
|
|
22
|
+
return sql ? `$${column}__${index}::${sql} AS ${column}` : `$${column}__${index} AS ${column}`;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateTableStatementTextRenderer, type Column } from "@datasources
|
|
1
|
+
import { CreateTableStatementTextRenderer, type Column } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class PostgresCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
|
|
3
3
|
protected typeName(column: Column): string;
|
|
4
4
|
protected columnDefault(column: Column): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ColumnDefault, CreateTableStatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { PostgresRowTypeConverters } from "./converters/PostgresRowTypeConverters.js";
|
|
3
|
+
import { columnTypes } from "./columnTypes.js";
|
|
4
|
+
export class PostgresCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
|
|
5
|
+
typeName(column) {
|
|
6
|
+
return columnTypes[column.type] ?? column.type;
|
|
7
|
+
}
|
|
8
|
+
columnDefault(column) {
|
|
9
|
+
const literal = sqlLiteral(column);
|
|
10
|
+
const value = column.default;
|
|
11
|
+
return value == null || literal == null
|
|
12
|
+
? super.columnDefault(column)
|
|
13
|
+
: super.columnDefault({ ...column, default: new ColumnDefault(literal, value.name) });
|
|
14
|
+
}
|
|
15
|
+
identity(column) {
|
|
16
|
+
return column.identity ? " GENERATED BY DEFAULT AS IDENTITY" : "";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function sqlLiteral(column) {
|
|
20
|
+
const raw = column.default?.value;
|
|
21
|
+
const converter = PostgresRowTypeConverters[column.type];
|
|
22
|
+
if (raw == null || !converter || !/^-?\d+$/.test(raw))
|
|
23
|
+
return undefined;
|
|
24
|
+
const converted = converter.convert(Number(raw));
|
|
25
|
+
return converted === true ? "TRUE" : converted === false ? "FALSE" : undefined;
|
|
26
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CreateTriggerStatementTextRenderer, type CreateTriggerStatement } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class PostgresCreateTriggerStatementRenderer extends CreateTriggerStatementTextRenderer {
|
|
3
|
+
render(statement: CreateTriggerStatement): string;
|
|
4
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CreateTriggerStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
export class PostgresCreateTriggerStatementRenderer extends CreateTriggerStatementTextRenderer {
|
|
3
|
+
render(statement) {
|
|
4
|
+
return `-- Shared updated_at function
|
|
5
|
+
CREATE OR REPLACE FUNCTION set_updated_at() RETURNS TRIGGER AS $$
|
|
6
|
+
BEGIN
|
|
7
|
+
NEW.${statement.updatedColumn} = NOW() AT TIME ZONE 'UTC';
|
|
8
|
+
RETURN NEW;
|
|
9
|
+
END;
|
|
10
|
+
$$ LANGUAGE plpgsql;
|
|
11
|
+
CREATE TRIGGER ${statement.name} BEFORE UPDATE ON ${statement.table} FOR EACH ROW EXECUTE FUNCTION set_updated_at()`;
|
|
12
|
+
}
|
|
13
|
+
}
|
package/dist/PostgresDriver.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { StatementTextRenderer, type Cell, type DatasourceOptions, type ICommand, type IConnection, type IDriver, type Information, type ITransaction, type IsolationLevel, type TypeConverter } from "@datasources
|
|
1
|
+
import { DefaultValueConverter, DeterministicTypeConverter, StatementTextRenderer, type Cell, type DatasourceOptions, type ICommand, type IConnection, type IDriver, type Information, type ITransaction, type IsolationLevel, type TypeConverter } from "@deterministic-code/datasources";
|
|
2
2
|
import { columnTypes } from "./columnTypes.js";
|
|
3
3
|
export { columnTypes };
|
|
4
4
|
export declare class PostgresDriver implements IDriver {
|
|
5
5
|
readonly statementRenderer: StatementTextRenderer;
|
|
6
6
|
readonly information: Information;
|
|
7
7
|
rowTypeConverters: Record<string, TypeConverter<Cell, Cell>>;
|
|
8
|
+
readonly quoteLeft = "\"";
|
|
9
|
+
readonly quoteRight = "\"";
|
|
10
|
+
readonly typeConverter: DeterministicTypeConverter;
|
|
11
|
+
readonly defaultValueConverter: DefaultValueConverter;
|
|
8
12
|
columnType: (name: string) => string;
|
|
9
13
|
connection: (options?: DatasourceOptions) => IConnection;
|
|
10
14
|
command: (connection: IConnection) => ICommand;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { DatasourceError, DefaultValueConverter, DeterministicTypeConverter, StatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { PostgresConnection } from "./connection.js";
|
|
3
|
+
import { PostgresCommand } from "./command.js";
|
|
4
|
+
import { PostgresTransaction } from "./transaction.js";
|
|
5
|
+
import { PostgresRowTypeConverters } from "./converters/PostgresRowTypeConverters.js";
|
|
6
|
+
import { PostgresStatementRenderer } from "./PostgresStatementRenderer.js";
|
|
7
|
+
import { columnTypes } from "./columnTypes.js";
|
|
8
|
+
export { columnTypes };
|
|
9
|
+
const statements = new StatementTextRenderer(t => new PostgresStatementRenderer(t));
|
|
10
|
+
export class PostgresDriver {
|
|
11
|
+
statementRenderer = statements;
|
|
12
|
+
information = {
|
|
13
|
+
descriptions: [
|
|
14
|
+
"PostgreSQL server driver.",
|
|
15
|
+
"Full connection, command, transaction, and repository support.",
|
|
16
|
+
],
|
|
17
|
+
overrides: [
|
|
18
|
+
"Overrides SelectInsertedIdStatement to SELECT lastval() AS {column}",
|
|
19
|
+
"Overrides CreateTableStatement identity to GENERATED BY DEFAULT AS IDENTITY",
|
|
20
|
+
"Overrides CreateTableStatement boolean defaults (0/1 → TRUE/FALSE)",
|
|
21
|
+
"Overrides CreateTableStatement type mapping (int→INTEGER, uuid→UUID, string→TEXT, bool→BOOLEAN, decimal→NUMERIC, float→DOUBLE PRECISION, datetime→TIMESTAMPTZ, binary→BYTEA)",
|
|
22
|
+
"Overrides CreateCreateStoredProcedureStatement to PROCEDURE Create{Plural}",
|
|
23
|
+
"Overrides CreateReadStoredProcedureStatement to PROCEDURE Read{Plural}By{Properties}",
|
|
24
|
+
"Overrides CreateUpdateStoredProcedureStatement to PROCEDURE Update{Plural}By{Properties}",
|
|
25
|
+
"Overrides CreateDeleteStoredProcedureStatement to PROCEDURE Delete{Plural}By{Properties}",
|
|
26
|
+
"Overrides CreateBulkCreateStoredProcedureStatement to PROCEDURE BulkCreate{Plural}",
|
|
27
|
+
"Overrides CreateBulkUpdateStoredProcedureStatement to PROCEDURE BulkUpdate{Plural}",
|
|
28
|
+
"Overrides CreateBulkDeleteStoredProcedureStatement to PROCEDURE BulkDelete{Plural}",
|
|
29
|
+
],
|
|
30
|
+
changeLogs: [],
|
|
31
|
+
};
|
|
32
|
+
rowTypeConverters = PostgresRowTypeConverters;
|
|
33
|
+
quoteLeft = '"';
|
|
34
|
+
quoteRight = '"';
|
|
35
|
+
typeConverter = new DeterministicTypeConverter({
|
|
36
|
+
string: "TEXT",
|
|
37
|
+
character: "CHAR(1)",
|
|
38
|
+
number: "INTEGER",
|
|
39
|
+
integer: "INTEGER",
|
|
40
|
+
unsignedinteger: "BIGINT",
|
|
41
|
+
smallinteger: "SMALLINT",
|
|
42
|
+
unsignedsmallinteger: "INTEGER",
|
|
43
|
+
biginteger: "BIGINT",
|
|
44
|
+
unsignedbiginteger: "NUMERIC(20)",
|
|
45
|
+
float: "DOUBLE PRECISION",
|
|
46
|
+
decimal: "NUMERIC",
|
|
47
|
+
boolean: "BOOLEAN",
|
|
48
|
+
datetime: "TIMESTAMPTZ",
|
|
49
|
+
binary: "BYTEA",
|
|
50
|
+
uuid: "UUID",
|
|
51
|
+
reference: "INTEGER",
|
|
52
|
+
}, {
|
|
53
|
+
dialectName: "postgres",
|
|
54
|
+
stringUnlimited: "TEXT",
|
|
55
|
+
stringSized: (n) => `VARCHAR(${n})`,
|
|
56
|
+
character: "CHAR",
|
|
57
|
+
decimal: { kind: "optional", type: "NUMERIC", fallback: "NUMERIC" },
|
|
58
|
+
});
|
|
59
|
+
defaultValueConverter = new DefaultValueConverter({
|
|
60
|
+
newId: () => "gen_random_uuid()",
|
|
61
|
+
now: () => "LOCALTIMESTAMP",
|
|
62
|
+
utcNow: () => `(NOW() AT TIME ZONE 'UTC')`,
|
|
63
|
+
booleanOn: "TRUE",
|
|
64
|
+
booleanOff: "FALSE",
|
|
65
|
+
hex: (arg) => `'\\x${arg}'`,
|
|
66
|
+
hexReverse: /^'\\x(.*)'$/,
|
|
67
|
+
});
|
|
68
|
+
columnType = (name) => columnTypes[name] ??
|
|
69
|
+
(() => {
|
|
70
|
+
throw new DatasourceError(`Unknown postgres type "${name}"`);
|
|
71
|
+
})();
|
|
72
|
+
connection = (options) => PostgresConnection.from(options);
|
|
73
|
+
command = (connection) => new PostgresCommand(connection, statements);
|
|
74
|
+
transaction = (connection, isolation = "readCommitted") => new PostgresTransaction(connection, isolation);
|
|
75
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DropTriggerStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
export class PostgresDropTriggerStatementRenderer extends DropTriggerStatementTextRenderer {
|
|
3
|
+
render(statement) {
|
|
4
|
+
return `DROP TRIGGER IF EXISTS ${statement.name} ON ${statement.table}`;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { IdentityInsertStatementTextRenderer, type IdentityInsertStatement } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class PostgresIdentityInsertStatementRenderer extends IdentityInsertStatementTextRenderer {
|
|
3
|
+
render(statement: IdentityInsertStatement): string;
|
|
4
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IdentityInsertStatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
export class PostgresIdentityInsertStatementRenderer extends IdentityInsertStatementTextRenderer {
|
|
3
|
+
render(statement) {
|
|
4
|
+
const inserts = super.render(statement);
|
|
5
|
+
if (!statement.identity || statement.inserts.length === 0)
|
|
6
|
+
return inserts;
|
|
7
|
+
const quoted = statement.inserts[0].table;
|
|
8
|
+
return `${inserts}
|
|
9
|
+
SELECT setval(pg_get_serial_sequence('${statement.table}', '${statement.idColumn}'), (SELECT MAX("${statement.idColumn}") FROM ${quoted}))`;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
import { BulkUpdateStatement, CreateTableStatement, SelectInsertedIdStatement, StatementRenderer, type StatementTextRenderer } from "@datasources
|
|
1
|
+
import { AlterTableStatement, BulkUpdateStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateCreateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateTableStatement, CreateTriggerStatement, CreateUpdateStoredProcedureStatement, DropRoutineStatement, DropTriggerStatement, IdentityInsertStatement, SelectInsertedIdStatement, StatementRenderer, type StatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class PostgresStatementRenderer extends StatementRenderer {
|
|
3
3
|
constructor(text: StatementTextRenderer);
|
|
4
4
|
protected visitSelectInsertedId(inserted: SelectInsertedIdStatement): string;
|
|
5
5
|
protected visitCreateTable(create: CreateTableStatement): string;
|
|
6
|
+
protected visitAlterTable(alter: AlterTableStatement): string;
|
|
7
|
+
protected visitDropTrigger(trigger: DropTriggerStatement): string;
|
|
6
8
|
protected visitBulkUpdate(update: BulkUpdateStatement): string;
|
|
9
|
+
protected visitCreateTrigger(trigger: CreateTriggerStatement): string;
|
|
10
|
+
protected visitIdentityInsert(insert: IdentityInsertStatement): string;
|
|
11
|
+
protected visitDropRoutine(routine: DropRoutineStatement): string;
|
|
12
|
+
protected visitCreateCreateStoredProcedureStatement(statement: CreateCreateStoredProcedureStatement): string;
|
|
13
|
+
protected visitCreateReadStoredProcedureStatement(statement: CreateReadStoredProcedureStatement): string;
|
|
14
|
+
protected visitCreateUpdateStoredProcedureStatement(statement: CreateUpdateStoredProcedureStatement): string;
|
|
15
|
+
protected visitCreateDeleteStoredProcedureStatement(statement: CreateDeleteStoredProcedureStatement): string;
|
|
16
|
+
protected visitCreateBulkCreateStoredProcedureStatement(statement: CreateBulkCreateStoredProcedureStatement): string;
|
|
17
|
+
protected visitCreateBulkUpdateStoredProcedureStatement(statement: CreateBulkUpdateStoredProcedureStatement): string;
|
|
18
|
+
protected visitCreateBulkDeleteStoredProcedureStatement(statement: CreateBulkDeleteStoredProcedureStatement): string;
|
|
7
19
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { SelectInsertedIdStatementTextRenderer, StatementRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { PostgresAlterTableStatementRenderer } from "./PostgresAlterTableStatementRenderer.js";
|
|
3
|
+
import { PostgresBulkUpdateStatementTextRenderer } from "./PostgresBulkUpdateStatementTextRenderer.js";
|
|
4
|
+
import { PostgresCreateTableStatementRenderer } from "./PostgresCreateTableStatementRenderer.js";
|
|
5
|
+
import { PostgresCreateTriggerStatementRenderer } from "./PostgresCreateTriggerStatementRenderer.js";
|
|
6
|
+
import { PostgresDropRoutineStatementRenderer } from "./PostgresDropRoutineStatementRenderer.js";
|
|
7
|
+
import { PostgresStoredProcedureStatementRenderer } from "./PostgresStoredProcedureStatementRenderer.js";
|
|
8
|
+
import { PostgresDropTriggerStatementRenderer } from "./PostgresDropTriggerStatementRenderer.js";
|
|
9
|
+
import { PostgresIdentityInsertStatementRenderer } from "./PostgresIdentityInsertStatementRenderer.js";
|
|
10
|
+
class PostgresSelectInsertedIdStatementTextRenderer extends SelectInsertedIdStatementTextRenderer {
|
|
11
|
+
render(statement) {
|
|
12
|
+
return `SELECT lastval() AS ${statement.column}`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class PostgresStatementRenderer extends StatementRenderer {
|
|
16
|
+
constructor(text) {
|
|
17
|
+
super(text);
|
|
18
|
+
}
|
|
19
|
+
visitSelectInsertedId(inserted) {
|
|
20
|
+
return new PostgresSelectInsertedIdStatementTextRenderer().render(inserted);
|
|
21
|
+
}
|
|
22
|
+
visitCreateTable(create) {
|
|
23
|
+
return new PostgresCreateTableStatementRenderer().render(create);
|
|
24
|
+
}
|
|
25
|
+
visitAlterTable(alter) {
|
|
26
|
+
return new PostgresAlterTableStatementRenderer().renderAlter(alter);
|
|
27
|
+
}
|
|
28
|
+
visitDropTrigger(trigger) {
|
|
29
|
+
return new PostgresDropTriggerStatementRenderer().render(trigger);
|
|
30
|
+
}
|
|
31
|
+
visitBulkUpdate(update) {
|
|
32
|
+
return new PostgresBulkUpdateStatementTextRenderer().render(update);
|
|
33
|
+
}
|
|
34
|
+
visitCreateTrigger(trigger) {
|
|
35
|
+
return new PostgresCreateTriggerStatementRenderer().render(trigger);
|
|
36
|
+
}
|
|
37
|
+
visitIdentityInsert(insert) {
|
|
38
|
+
return new PostgresIdentityInsertStatementRenderer().render(insert);
|
|
39
|
+
}
|
|
40
|
+
visitDropRoutine(routine) {
|
|
41
|
+
return new PostgresDropRoutineStatementRenderer().render(routine);
|
|
42
|
+
}
|
|
43
|
+
visitCreateCreateStoredProcedureStatement(statement) {
|
|
44
|
+
return new PostgresStoredProcedureStatementRenderer().renderCreate(statement);
|
|
45
|
+
}
|
|
46
|
+
visitCreateReadStoredProcedureStatement(statement) {
|
|
47
|
+
return new PostgresStoredProcedureStatementRenderer().renderRead(statement);
|
|
48
|
+
}
|
|
49
|
+
visitCreateUpdateStoredProcedureStatement(statement) {
|
|
50
|
+
return new PostgresStoredProcedureStatementRenderer().renderUpdate(statement);
|
|
51
|
+
}
|
|
52
|
+
visitCreateDeleteStoredProcedureStatement(statement) {
|
|
53
|
+
return new PostgresStoredProcedureStatementRenderer().renderDelete(statement);
|
|
54
|
+
}
|
|
55
|
+
visitCreateBulkCreateStoredProcedureStatement(statement) {
|
|
56
|
+
return new PostgresStoredProcedureStatementRenderer().renderBulkCreate(statement);
|
|
57
|
+
}
|
|
58
|
+
visitCreateBulkUpdateStoredProcedureStatement(statement) {
|
|
59
|
+
return new PostgresStoredProcedureStatementRenderer().renderBulkUpdate(statement);
|
|
60
|
+
}
|
|
61
|
+
visitCreateBulkDeleteStoredProcedureStatement(statement) {
|
|
62
|
+
return new PostgresStoredProcedureStatementRenderer().renderBulkDelete(statement);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CreateBulkCreateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateCreateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateUpdateStoredProcedureStatement } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class PostgresStoredProcedureStatementRenderer {
|
|
3
|
+
renderCreate(statement: CreateCreateStoredProcedureStatement): string;
|
|
4
|
+
renderRead(statement: CreateReadStoredProcedureStatement): string;
|
|
5
|
+
renderUpdate(statement: CreateUpdateStoredProcedureStatement): string;
|
|
6
|
+
renderDelete(statement: CreateDeleteStoredProcedureStatement): string;
|
|
7
|
+
renderBulkCreate(statement: CreateBulkCreateStoredProcedureStatement): string;
|
|
8
|
+
renderBulkUpdate(statement: CreateBulkUpdateStoredProcedureStatement): string;
|
|
9
|
+
renderBulkDelete(statement: CreateBulkDeleteStoredProcedureStatement): string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const params = (list) => list.map((p) => ` ${p.name} ${p.type}`).join(",\n");
|
|
2
|
+
const recordset = (columns) => columns.map((c) => `${c.name} ${c.type}`).join(", ");
|
|
3
|
+
export class PostgresStoredProcedureStatementRenderer {
|
|
4
|
+
renderCreate(statement) {
|
|
5
|
+
return `CREATE OR REPLACE FUNCTION ${statement.name}(
|
|
6
|
+
${params(statement.parameters)}
|
|
7
|
+
) RETURNS ${statement.keyType} LANGUAGE plpgsql AS $$
|
|
8
|
+
DECLARE
|
|
9
|
+
new_id ${statement.keyType};
|
|
10
|
+
BEGIN
|
|
11
|
+
INSERT INTO ${statement.table} (${statement.parameters.map((p) => p.name).join(", ")})
|
|
12
|
+
VALUES (${statement.parameters.map((p) => p.name).join(", ")})
|
|
13
|
+
RETURNING ${statement.key} INTO new_id;
|
|
14
|
+
RETURN new_id;
|
|
15
|
+
END;
|
|
16
|
+
$$`;
|
|
17
|
+
}
|
|
18
|
+
renderRead(statement) {
|
|
19
|
+
const aliased = statement.columns.map((c) => `t.${c}`).join(", ");
|
|
20
|
+
if (statement.byProperties.length === 0) {
|
|
21
|
+
return `CREATE OR REPLACE FUNCTION ${statement.name}() RETURNS SETOF ${statement.table}
|
|
22
|
+
LANGUAGE sql AS $$ SELECT ${aliased} FROM ${statement.table} t ORDER BY t.${statement.key}; $$`;
|
|
23
|
+
}
|
|
24
|
+
const signature = statement.byProperties
|
|
25
|
+
.map((c) => `${c} ${statement.parameters.find((p) => p.name === c)?.type ?? statement.keyType}`)
|
|
26
|
+
.join(", ");
|
|
27
|
+
const pred = statement.byProperties
|
|
28
|
+
.map((c) => `t.${c} = ${statement.name}.${c}`)
|
|
29
|
+
.join(" AND ");
|
|
30
|
+
return `CREATE OR REPLACE FUNCTION ${statement.name}(${signature}) RETURNS SETOF ${statement.table}
|
|
31
|
+
LANGUAGE sql AS $$ SELECT ${aliased} FROM ${statement.table} t WHERE ${pred}; $$`;
|
|
32
|
+
}
|
|
33
|
+
renderUpdate(statement) {
|
|
34
|
+
const sets = statement.writable.map((c) => ` ${c} = ${statement.name}.${c}`);
|
|
35
|
+
if (statement.auditUpdated !== undefined) {
|
|
36
|
+
const auditParam = statement.parameters.find((p) => p.name.includes("new_updated"));
|
|
37
|
+
if (auditParam)
|
|
38
|
+
sets.push(` ${statement.auditUpdated} = ${statement.name}.${auditParam.name}`);
|
|
39
|
+
}
|
|
40
|
+
const by = statement.byProperties.length > 0 ? statement.byProperties : [statement.key];
|
|
41
|
+
const pred = by.map((c) => `t.${c} = ${statement.name}.${c}`).join(" AND ");
|
|
42
|
+
return `CREATE OR REPLACE FUNCTION ${statement.name}(
|
|
43
|
+
${params(statement.parameters)}
|
|
44
|
+
) RETURNS INT LANGUAGE plpgsql AS $$
|
|
45
|
+
DECLARE
|
|
46
|
+
affected INT;
|
|
47
|
+
BEGIN
|
|
48
|
+
UPDATE ${statement.table} t
|
|
49
|
+
SET
|
|
50
|
+
${sets.join(",\n")}
|
|
51
|
+
WHERE ${pred};
|
|
52
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
53
|
+
RETURN affected;
|
|
54
|
+
END;
|
|
55
|
+
$$`;
|
|
56
|
+
}
|
|
57
|
+
renderDelete(statement) {
|
|
58
|
+
const by = statement.byProperties.length > 0 ? statement.byProperties : [statement.key];
|
|
59
|
+
const signature = by
|
|
60
|
+
.map((c) => `${c} ${statement.parameters.find((p) => p.name === c)?.type ?? statement.keyType}`)
|
|
61
|
+
.join(", ");
|
|
62
|
+
const pred = by.map((c) => `t.${c} = ${statement.name}.${c}`).join(" AND ");
|
|
63
|
+
return `CREATE OR REPLACE FUNCTION ${statement.name}(${signature}) RETURNS INT LANGUAGE plpgsql AS $$
|
|
64
|
+
DECLARE
|
|
65
|
+
affected INT;
|
|
66
|
+
BEGIN
|
|
67
|
+
DELETE FROM ${statement.table} t WHERE ${pred};
|
|
68
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
69
|
+
RETURN affected;
|
|
70
|
+
END;
|
|
71
|
+
$$`;
|
|
72
|
+
}
|
|
73
|
+
renderBulkCreate(statement) {
|
|
74
|
+
const cols = statement.columns.map((c) => c.name).join(", ");
|
|
75
|
+
return `CREATE OR REPLACE PROCEDURE ${statement.name}(${statement.payload.name} jsonb, INOUT ids jsonb DEFAULT NULL)
|
|
76
|
+
LANGUAGE plpgsql AS $$
|
|
77
|
+
BEGIN
|
|
78
|
+
WITH inserted AS (
|
|
79
|
+
INSERT INTO ${statement.table} (${cols})
|
|
80
|
+
SELECT ${cols}
|
|
81
|
+
FROM jsonb_to_recordset(${statement.payload.name}) AS x(${recordset(statement.columns)})
|
|
82
|
+
RETURNING ${statement.key}
|
|
83
|
+
)
|
|
84
|
+
SELECT COALESCE(jsonb_agg(${statement.key}), '[]'::jsonb) INTO ids FROM inserted;
|
|
85
|
+
END;
|
|
86
|
+
$$`;
|
|
87
|
+
}
|
|
88
|
+
renderBulkUpdate(statement) {
|
|
89
|
+
const sets = statement.columns
|
|
90
|
+
.filter((c) => c.name !== statement.key)
|
|
91
|
+
.map((c) => `${c.name} = x.${c.name}`)
|
|
92
|
+
.join(", ");
|
|
93
|
+
return `CREATE OR REPLACE PROCEDURE ${statement.name}(${statement.payload.name} jsonb)
|
|
94
|
+
LANGUAGE plpgsql AS $$
|
|
95
|
+
BEGIN
|
|
96
|
+
UPDATE ${statement.table} t SET
|
|
97
|
+
${sets}
|
|
98
|
+
FROM jsonb_to_recordset(${statement.payload.name}) AS x(${recordset(statement.columns)})
|
|
99
|
+
WHERE t.${statement.key} = x.${statement.key};
|
|
100
|
+
END;
|
|
101
|
+
$$`;
|
|
102
|
+
}
|
|
103
|
+
renderBulkDelete(statement) {
|
|
104
|
+
return `CREATE OR REPLACE PROCEDURE ${statement.name}(${statement.payload.name} jsonb)
|
|
105
|
+
LANGUAGE plpgsql AS $$
|
|
106
|
+
BEGIN
|
|
107
|
+
DELETE FROM ${statement.table} WHERE ${statement.key} IN (SELECT ${statement.key} FROM jsonb_to_recordset(${statement.payload.name}) AS x(${statement.key} ${statement.keyType}));
|
|
108
|
+
END;
|
|
109
|
+
$$`;
|
|
110
|
+
}
|
|
111
|
+
}
|
package/dist/bind.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Parameter } from "@datasources
|
|
1
|
+
import { type Parameter } from "@deterministic-code/datasources";
|
|
2
2
|
import type { Scalar, Wire } from "./scalar.js";
|
|
3
3
|
export declare const toPostgres: (sql: string, params?: Record<string, Scalar>) => {
|
|
4
4
|
text: string;
|
package/dist/bind.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { parametersToRecord } from "@deterministic-code/datasources";
|
|
2
|
+
import { isBase64 } from "./converters/BinaryConverter.js";
|
|
3
|
+
import { isIsoDate } from "./converters/DateTimeConverter.js";
|
|
4
|
+
import { reverse } from "./converters/PostgresRowTypeConverters.js";
|
|
5
|
+
const NAME = /\$([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
6
|
+
const quoteMixedCase = (sql) => sql.replace(/\$[A-Za-z_][A-Za-z0-9_]*|'[^']*'|"[^"]*"|`[^`]*`|\b[A-Za-z_][A-Za-z0-9_]*\b/g, (token) => token.startsWith("$") || token.startsWith("'") || token.startsWith("\"") || token.startsWith("`")
|
|
7
|
+
|| !/[A-Z]/.test(token) || !/[a-z]/.test(token)
|
|
8
|
+
? token
|
|
9
|
+
: `"${token}"`);
|
|
10
|
+
const wire = (value) => typeof value !== "string"
|
|
11
|
+
? value
|
|
12
|
+
: isIsoDate(value)
|
|
13
|
+
? new Date(value)
|
|
14
|
+
: isBase64(value)
|
|
15
|
+
? Uint8Array.from(Buffer.from(value, "base64"))
|
|
16
|
+
: value;
|
|
17
|
+
export const toPostgres = (sql, params) => {
|
|
18
|
+
const names = [];
|
|
19
|
+
const text = quoteMixedCase(sql).replace(NAME, (_match, name) => {
|
|
20
|
+
let index = names.indexOf(name);
|
|
21
|
+
if (index < 0) {
|
|
22
|
+
names.push(name);
|
|
23
|
+
index = names.length - 1;
|
|
24
|
+
}
|
|
25
|
+
return `$${index + 1}`;
|
|
26
|
+
});
|
|
27
|
+
return { text, values: names.map((name) => wire(params?.[name] ?? null)) };
|
|
28
|
+
};
|
|
29
|
+
export const bindParameters = (parameters) => {
|
|
30
|
+
const record = parametersToRecord(parameters);
|
|
31
|
+
const bound = {};
|
|
32
|
+
for (const [name, value] of Object.entries(record))
|
|
33
|
+
bound[name] = reverse(value);
|
|
34
|
+
return bound;
|
|
35
|
+
};
|
package/dist/command.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommandMiddlewareHost, Native, StatementTextRenderer, Response, type Cells, type ICommand, type IReader, type IStatement, type Parameter } from "@datasources
|
|
1
|
+
import { CommandMiddlewareHost, Native, StatementTextRenderer, Response, type Cells, type ICommand, type IReader, type IStatement, type Parameter } from "@deterministic-code/datasources";
|
|
2
2
|
import type { PostgresConnection } from "./connection.js";
|
|
3
3
|
export declare class PostgresCommand extends CommandMiddlewareHost implements ICommand {
|
|
4
4
|
readonly connection: PostgresConnection;
|
package/dist/command.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ArrayReader, CommandMiddlewareHost, FirstResultReader, DatasourceError, Native, ParameterDirection, ReturnStatement, SelectStatement, StatementTextRenderer, Response, splitStatements, statementParameters, statementReader, } from "@deterministic-code/datasources";
|
|
2
|
+
import { bindParameters, toPostgres } from "./bind.js";
|
|
3
|
+
import { PostgresStatementRenderer } from "./PostgresStatementRenderer.js";
|
|
4
|
+
export class PostgresCommand extends CommandMiddlewareHost {
|
|
5
|
+
connection;
|
|
6
|
+
statements;
|
|
7
|
+
constructor(connection, statements) {
|
|
8
|
+
super();
|
|
9
|
+
this.connection = connection;
|
|
10
|
+
this.statements = statements ?? new StatementTextRenderer(t => new PostgresStatementRenderer(t));
|
|
11
|
+
}
|
|
12
|
+
async executeReaderNative(text, parameters) {
|
|
13
|
+
return this.run(text.text, parameters, async (query, params) => {
|
|
14
|
+
if (!query.trim())
|
|
15
|
+
return { value: new ArrayReader([]), rows: [] };
|
|
16
|
+
this.connection.ensureOpen();
|
|
17
|
+
const statements = splitStatements(query);
|
|
18
|
+
if (statements.length === 0)
|
|
19
|
+
return { value: new ArrayReader([]), rows: [] };
|
|
20
|
+
const bound = bindParameters(params);
|
|
21
|
+
return FirstResultReader.capture(statementReader(statements, (sql) => this.query(sql, bound)));
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async executeReaderStatement(statement, parameters) {
|
|
25
|
+
return this.executeReaderNative(new Native(this.statements.render(statement)), parameters);
|
|
26
|
+
}
|
|
27
|
+
async executeStatement(statement, parameters) {
|
|
28
|
+
return statement instanceof SelectStatement || statement instanceof ReturnStatement
|
|
29
|
+
? this.firstCell(statement, parameters)
|
|
30
|
+
: this.executeNative(new Native(this.statements.render(statement)), parameters);
|
|
31
|
+
}
|
|
32
|
+
async firstCell(statement, parameters) {
|
|
33
|
+
const reader = await this.executeReaderStatement(statement, parameters);
|
|
34
|
+
try {
|
|
35
|
+
if (!(await reader.next()))
|
|
36
|
+
return new Response(null, 0, parameters);
|
|
37
|
+
const row = reader.current;
|
|
38
|
+
/* v8 ignore next */
|
|
39
|
+
if (!row)
|
|
40
|
+
throw new DatasourceError("Reader current must be a row");
|
|
41
|
+
const values = Object.values(row);
|
|
42
|
+
return new Response(values.length === 0 ? null : values[0], 1, parameters);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await reader.dispose();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async executeNative(text, parameters) {
|
|
49
|
+
return this.run(text.text, parameters, async (query, params) => {
|
|
50
|
+
if (!query.trim())
|
|
51
|
+
return { value: new Response(undefined, 0, params), rows: [] };
|
|
52
|
+
this.connection.ensureOpen();
|
|
53
|
+
const named = bindParameters(params);
|
|
54
|
+
let rowsAffected = 0;
|
|
55
|
+
let parameters = params;
|
|
56
|
+
for (const part of splitStatements(query)) {
|
|
57
|
+
const names = new Set(statementParameters(part));
|
|
58
|
+
const used = Object.fromEntries(Object.entries(named).filter(([name]) => names.has(name.replace(/^[$:@]/, ""))));
|
|
59
|
+
const result = await this.runSql(part, used, params);
|
|
60
|
+
rowsAffected += result.rowsAffected;
|
|
61
|
+
parameters = result.parameters ?? params;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
value: new Response(null, rowsAffected, parameters),
|
|
65
|
+
rows: [],
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async runSql(sql, params, parameters) {
|
|
70
|
+
const { text, values } = toPostgres(sql, params);
|
|
71
|
+
const queried = await this.connection.require().query(text, values);
|
|
72
|
+
const row = queried.rows[0];
|
|
73
|
+
return {
|
|
74
|
+
rowsAffected: queried.rowCount ?? queried.rows.length,
|
|
75
|
+
result: null,
|
|
76
|
+
parameters: parameters?.map((parameter) => {
|
|
77
|
+
if (!parameter.direction || parameter.direction === ParameterDirection.Input || !row)
|
|
78
|
+
return parameter;
|
|
79
|
+
const cells = row;
|
|
80
|
+
const cell = cells[parameter.name] ?? cells[parameter.name.toLowerCase()];
|
|
81
|
+
if (cell === undefined)
|
|
82
|
+
return parameter;
|
|
83
|
+
return { ...parameter, value: cellValue(cell) };
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
query(sql, params) {
|
|
88
|
+
const { text, values } = toPostgres(sql, params);
|
|
89
|
+
return this.connection.require().rows(text, values);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const cellValue = (cell) => cell !== null && typeof cell === "object" ? JSON.stringify(cell) : cell;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DatasourceOptions, type ICommand, type IConnection, type IsolationLevelName, type ITransaction } from "@datasources
|
|
1
|
+
import { type DatasourceOptions, type ICommand, type IConnection, type IsolationLevelName, type ITransaction } from "@deterministic-code/datasources";
|
|
2
2
|
import type { PostgresConnectionOptions } from "./connection-options.js";
|
|
3
3
|
import { type IPgClient } from "./i-postgres-driver.js";
|
|
4
4
|
export declare class PostgresConnection implements IConnection {
|