prostgles-server 4.2.606 → 4.2.607
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/README.md +29 -0
- package/dist/Audit/AuditTable.d.ts +18 -0
- package/dist/Audit/AuditTable.d.ts.map +1 -0
- package/dist/Audit/AuditTable.js +18 -0
- package/dist/Audit/AuditTable.js.map +1 -0
- package/dist/Audit/AuditTypes.d.ts +25 -0
- package/dist/Audit/AuditTypes.d.ts.map +1 -0
- package/dist/Audit/AuditTypes.js +3 -0
- package/dist/Audit/AuditTypes.js.map +1 -0
- package/dist/Audit/getAuditTableConfig.d.ts +13 -0
- package/dist/Audit/getAuditTableConfig.d.ts.map +1 -0
- package/dist/Audit/getAuditTableConfig.js +54 -0
- package/dist/Audit/getAuditTableConfig.js.map +1 -0
- package/dist/Audit/parseAuditConfig.d.ts +5 -0
- package/dist/Audit/parseAuditConfig.d.ts.map +1 -0
- package/dist/Audit/parseAuditConfig.js +123 -0
- package/dist/Audit/parseAuditConfig.js.map +1 -0
- package/dist/DboBuilder/DboBuilder.js +2 -2
- package/dist/DboBuilder/DboBuilder.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +1 -1
- package/dist/DboBuilder/runSql/QueryStreamer.d.ts.map +1 -1
- package/dist/DboBuilder/runSql/QueryStreamer.js +1 -0
- package/dist/DboBuilder/runSql/QueryStreamer.js.map +1 -1
- package/dist/DboBuilder/schema/getTablesForSchemaPostgresSQL.d.ts +3 -3
- package/dist/DboBuilder/schema/getTablesForSchemaPostgresSQL.d.ts.map +1 -1
- package/dist/DboBuilder/schema/getTablesForSchemaPostgresSQL.js +2 -2
- package/dist/DboBuilder/schema/getTablesForSchemaPostgresSQL.js.map +1 -1
- package/dist/DboBuilder/schema/getViewReferenceColumns.d.ts +2 -2
- package/dist/DboBuilder/schema/getViewReferenceColumns.d.ts.map +1 -1
- package/dist/DboBuilder/schema/getViewReferenceColumns.js +4 -2
- package/dist/DboBuilder/schema/getViewReferenceColumns.js.map +1 -1
- package/dist/Prostgles.d.ts +6 -0
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +33 -5
- package/dist/Prostgles.js.map +1 -1
- package/dist/ProstglesTypes.d.ts +3 -0
- package/dist/ProstglesTypes.d.ts.map +1 -1
- package/dist/ProstglesTypes.js.map +1 -1
- package/dist/PubSubManager/PubSubManager.d.ts +1 -1
- package/dist/PubSubManager/getCreatePubSubManagerError.js +1 -1
- package/dist/PubSubManager/getCreatePubSubManagerError.js.map +1 -1
- package/dist/PubSubManager/init/getPubSubManagerInitQuery.js +4 -4
- package/dist/PubSubManager/init/getPubSubManagerInitQuery.js.map +1 -1
- package/dist/PublishParser/getTableRulesWithoutFileTable.d.ts.map +1 -1
- package/dist/PublishParser/getTableRulesWithoutFileTable.js +5 -1
- package/dist/PublishParser/getTableRulesWithoutFileTable.js.map +1 -1
- package/dist/TableConfig/TableConfigTypes.d.ts +1 -1
- package/dist/TableConfig/TableConfigTypes.d.ts.map +1 -1
- package/dist/TableConfig/TableConfigurator.js +3 -3
- package/dist/TableConfig/TableConfigurator.js.map +1 -1
- package/dist/TableConfig/getTableConfigSchemaQueries.d.ts.map +1 -1
- package/dist/TableConfig/getTableConfigSchemaQueries.js +3 -2
- package/dist/TableConfig/getTableConfigSchemaQueries.js.map +1 -1
- package/dist/TableConfig/getTableTriggerQueries.d.ts +2 -2
- package/dist/TableConfig/getTableTriggerQueries.d.ts.map +1 -1
- package/dist/TableConfig/getTableTriggerQueries.js +57 -34
- package/dist/TableConfig/getTableTriggerQueries.js.map +1 -1
- package/dist/TableConfig/initTableConfig.d.ts.map +1 -1
- package/dist/TableConfig/initTableConfig.js +5 -6
- package/dist/TableConfig/initTableConfig.js.map +1 -1
- package/dist/TableConfig/managedTriggerNames.d.ts +8 -0
- package/dist/TableConfig/managedTriggerNames.d.ts.map +1 -0
- package/dist/TableConfig/managedTriggerNames.js +17 -0
- package/dist/TableConfig/managedTriggerNames.js.map +1 -0
- package/dist/TableConfig/runSQLFile.js +1 -1
- package/dist/TableConfig/runSQLFile.js.map +1 -1
- package/dist/TableConfig/syncTableTriggers.d.ts +4 -0
- package/dist/TableConfig/syncTableTriggers.d.ts.map +1 -0
- package/dist/TableConfig/syncTableTriggers.js +32 -0
- package/dist/TableConfig/syncTableTriggers.js.map +1 -0
- package/dist/deferDbQueries.d.ts +4 -0
- package/dist/deferDbQueries.d.ts.map +1 -0
- package/dist/deferDbQueries.js +24 -0
- package/dist/deferDbQueries.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/initProstgles.d.ts +1 -1
- package/dist/initProstgles.d.ts.map +1 -1
- package/dist/initProstgles.js +24 -13
- package/dist/initProstgles.js.map +1 -1
- package/dist/updateConfiguration.d.ts.map +1 -1
- package/dist/updateConfiguration.js +1 -6
- package/dist/updateConfiguration.js.map +1 -1
- package/lib/Audit/AuditTable.ts +25 -0
- package/lib/Audit/AuditTypes.ts +35 -0
- package/lib/Audit/getAuditTableConfig.ts +76 -0
- package/lib/Audit/parseAuditConfig.ts +158 -0
- package/lib/DboBuilder/DboBuilder.ts +2 -2
- package/lib/DboBuilder/runSql/QueryStreamer.ts +1 -0
- package/lib/DboBuilder/schema/getTablesForSchemaPostgresSQL.ts +2 -2
- package/lib/DboBuilder/schema/getViewReferenceColumns.ts +7 -8
- package/lib/Prostgles.ts +34 -5
- package/lib/ProstglesTypes.ts +3 -0
- package/lib/PubSubManager/getCreatePubSubManagerError.ts +1 -1
- package/lib/PubSubManager/init/getPubSubManagerInitQuery.ts +4 -4
- package/lib/PublishParser/getTableRulesWithoutFileTable.ts +6 -1
- package/lib/TableConfig/TableConfigTypes.ts +1 -1
- package/lib/TableConfig/TableConfigurator.ts +2 -2
- package/lib/TableConfig/getTableConfigSchemaQueries.ts +3 -2
- package/lib/TableConfig/getTableTriggerQueries.ts +85 -41
- package/lib/TableConfig/initTableConfig.ts +4 -7
- package/lib/TableConfig/managedTriggerNames.ts +16 -0
- package/lib/TableConfig/runSQLFile.ts +1 -1
- package/lib/TableConfig/syncTableTriggers.ts +42 -0
- package/lib/deferDbQueries.ts +27 -0
- package/lib/index.ts +3 -0
- package/lib/initProstgles.ts +24 -12
- package/lib/updateConfiguration.ts +1 -6
- package/package.json +1 -1
- package/dist/TableConfig/getTableTriggerQueries.spec.d.ts +0 -2
- package/dist/TableConfig/getTableTriggerQueries.spec.d.ts.map +0 -1
- package/dist/TableConfig/getTableTriggerQueries.spec.js +0 -56
- package/dist/TableConfig/getTableTriggerQueries.spec.js.map +0 -1
- package/lib/TableConfig/getTableTriggerQueries.spec.ts +0 -77
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateConfiguration.js","sourceRoot":"","sources":["../lib/updateConfiguration.ts"],"names":[],"mappings":";;;AAAA,qDAAiF;AAI1E,MAAM,mBAAmB,GAAG,KAAK,EACtC,IAAe,EACf,OAA6B,EAC7B,OAAyC,EACzC,KAAY,EACZ,EAAE;IACF,MAAM,kBAAkB,GAAG,IAAA,yBAAO,EAAC,OAAO,CAAC;SACxC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,6DAA6D;QAC7D,IAAI,KAAK,IAAI,CAAC,IAAA,yBAAO,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,YAAY;YACZ,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1B,OAAO,CAAC,CAAC;QACX,CAAC;QACD,OAAO;IACT,CAAC,CAAC;SACD,MAAM,CAAC,2BAAS,CAAC,CAAC;IACrB,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC;QAC/B,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IAED,IAAI,IAAA,0BAAQ,EAAC,kBAAkB,EAAE,SAAS,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IACD,IAAI,IAAA,0BAAQ,EAAC,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"updateConfiguration.js","sourceRoot":"","sources":["../lib/updateConfiguration.ts"],"names":[],"mappings":";;;AAAA,qDAAiF;AAI1E,MAAM,mBAAmB,GAAG,KAAK,EACtC,IAAe,EACf,OAA6B,EAC7B,OAAyC,EACzC,KAAY,EACZ,EAAE;IACF,MAAM,kBAAkB,GAAG,IAAA,yBAAO,EAAC,OAAO,CAAC;SACxC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,6DAA6D;QAC7D,IAAI,KAAK,IAAI,CAAC,IAAA,yBAAO,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,YAAY;YACZ,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1B,OAAO,CAAC,CAAC;QACX,CAAC;QACD,OAAO;IACT,CAAC,CAAC;SACD,MAAM,CAAC,2BAAS,CAAC,CAAC;IACrB,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,IAAI,CAAC,WAAW,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IAED,IAAI,IAAA,0BAAQ,EAAC,kBAAkB,EAAE,SAAS,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IACD,IAAI,IAAA,0BAAQ,EAAC,kBAAkB,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;IAC1B,CAAC;IACD,IAAI,IAAA,0BAAQ,EAAC,kBAAkB,EAAE,MAAM,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IACD,IAAI,IAAA,0BAAQ,EAAC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YACvC,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IACE,IAAA,0BAAQ,EAAC,kBAAkB,EAAE,qBAAqB,CAAC;QACnD,IAAA,0BAAQ,EAAC,kBAAkB,EAAE,+BAA+B,CAAC,EAC7D,CAAC;QACD,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAED,IAAI,IAAA,yBAAO,EAAC,OAAO,CAAC;QAAE,OAAO;IAE7B,IACE,kBAAkB,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,EAChG,CAAC;QACD,OAAO;IACT,CAAC;IAED;;;OAGG;IACH,IACE,kBAAkB,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,4BAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,EAChG,CAAC;QACD,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACvB,IAAI,EAAE,aAAa;YACnB,OAAO;SACR,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC;AAnEW,QAAA,mBAAmB,uBAmE9B;AAEF;;GAEG;AACU,QAAA,oBAAoB,GAAG;IAClC,IAAI;IACJ,MAAM;IACN,SAAS;IACT,WAAW;IACX,eAAe;IACf,oBAAoB;CACyB,CAAC;AAEhD,MAAM,oBAAoB,GAAG,CAAC,YAAY,CAA+C,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const AUDIT_TABLE_COLUMN_DEFINITIONS = {
|
|
2
|
+
id: "BIGSERIAL PRIMARY KEY",
|
|
3
|
+
created_at: "TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp()",
|
|
4
|
+
schema_name: "TEXT NOT NULL",
|
|
5
|
+
table_name: "TEXT NOT NULL",
|
|
6
|
+
entity_type: "TEXT NOT NULL",
|
|
7
|
+
operation:
|
|
8
|
+
"TEXT NOT NULL CHECK (operation IN ('INSERT', 'UPDATE', 'DELETE'))",
|
|
9
|
+
old_id: "JSONB",
|
|
10
|
+
new_id: "JSONB",
|
|
11
|
+
old_row: "JSONB",
|
|
12
|
+
new_row: "JSONB",
|
|
13
|
+
actor: "JSONB",
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
export type AuditTableRow = {
|
|
17
|
+
[K in keyof typeof AUDIT_TABLE_COLUMN_DEFINITIONS]: K extends "operation"
|
|
18
|
+
? "INSERT" | "UPDATE" | "DELETE"
|
|
19
|
+
: (typeof AUDIT_TABLE_COLUMN_DEFINITIONS)[K] extends "JSONB"
|
|
20
|
+
? Record<string, unknown> | null
|
|
21
|
+
: string;
|
|
22
|
+
};
|
|
23
|
+
export const AUDIT_TABLE_COLUMNS = Object.keys(
|
|
24
|
+
AUDIT_TABLE_COLUMN_DEFINITIONS,
|
|
25
|
+
) as (keyof AuditTableRow)[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type SchemaConfigTableName<S> = [S] extends [void]
|
|
2
|
+
? string
|
|
3
|
+
: Extract<keyof S, string>;
|
|
4
|
+
export type SchemaConfigColumnName<S, T extends SchemaConfigTableName<S>> = [
|
|
5
|
+
S,
|
|
6
|
+
] extends [void]
|
|
7
|
+
? string
|
|
8
|
+
: T extends keyof S
|
|
9
|
+
? S[T] extends { columns: infer C }
|
|
10
|
+
? Extract<keyof C, string>
|
|
11
|
+
: never
|
|
12
|
+
: never;
|
|
13
|
+
|
|
14
|
+
export type SchemaConfigAuditTableOptions<
|
|
15
|
+
S,
|
|
16
|
+
T extends SchemaConfigTableName<S>,
|
|
17
|
+
> = {
|
|
18
|
+
entityType?: string;
|
|
19
|
+
/** Defaults to all primary-key columns. Required for tables without a primary key. */
|
|
20
|
+
idColumns?: readonly SchemaConfigColumnName<S, T>[];
|
|
21
|
+
excludeColumns?: readonly SchemaConfigColumnName<S, T>[];
|
|
22
|
+
};
|
|
23
|
+
export type SchemaConfigAuditIncludedTables<S> = {
|
|
24
|
+
[T in SchemaConfigTableName<S>]?: 1 | SchemaConfigAuditTableOptions<S, T>;
|
|
25
|
+
};
|
|
26
|
+
export type SchemaConfigAuditExcludedTables<S> = {
|
|
27
|
+
[T in SchemaConfigTableName<S>]?: 0;
|
|
28
|
+
};
|
|
29
|
+
export type SchemaConfigAudit<S = void> = {
|
|
30
|
+
/** Managed append-only table. Uses the same table-name handling as tableConfig. */
|
|
31
|
+
tableName: string;
|
|
32
|
+
/** Omitted/empty means all eligible tables in schemaFilter. Cannot mix enabled and disabled entries. */
|
|
33
|
+
tables?:
|
|
34
|
+
SchemaConfigAuditIncludedTables<S> | SchemaConfigAuditExcludedTables<S>;
|
|
35
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { md5 } from "prostgles-types/dist/md5";
|
|
2
|
+
import type { Prostgles } from "../Prostgles";
|
|
3
|
+
import type {
|
|
4
|
+
TableConfig,
|
|
5
|
+
TableDefinition,
|
|
6
|
+
} from "../TableConfig/TableConfigTypes";
|
|
7
|
+
|
|
8
|
+
import { AUDIT_TABLE_COLUMN_DEFINITIONS } from "./AuditTable";
|
|
9
|
+
import {
|
|
10
|
+
AUDIT_HISTORY_PROTECTION_PREFIX,
|
|
11
|
+
AUDIT_TRUNCATE_PROTECTION_PREFIX,
|
|
12
|
+
AUDIT_WRITER_PREFIX,
|
|
13
|
+
} from "../TableConfig/managedTriggerNames";
|
|
14
|
+
|
|
15
|
+
export type ParsedAuditConfig = {
|
|
16
|
+
/** Includes previously configured history tables, whose protection is retained. */
|
|
17
|
+
auditTableNames: string[];
|
|
18
|
+
tableConfigs: TableConfig;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function getAuditProtection(
|
|
22
|
+
tableName: string,
|
|
23
|
+
actions: NonNullable<TableDefinition["triggers"]>[string]["actions"],
|
|
24
|
+
): NonNullable<TableDefinition["triggers"]> {
|
|
25
|
+
return {
|
|
26
|
+
[(actions.includes("update")
|
|
27
|
+
? AUDIT_HISTORY_PROTECTION_PREFIX
|
|
28
|
+
: AUDIT_TRUNCATE_PROTECTION_PREFIX) + md5(tableName)]: {
|
|
29
|
+
type: "before",
|
|
30
|
+
actions,
|
|
31
|
+
forEach: "statement",
|
|
32
|
+
query:
|
|
33
|
+
"BEGIN RAISE EXCEPTION 'Audit protection: % is not allowed on %.%', TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME; END;",
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Adds audit definitions to the same config used for file tables and application tables. */
|
|
39
|
+
export function getAuditTableConfig(
|
|
40
|
+
prgl: Prostgles,
|
|
41
|
+
tableConfig: TableConfig | undefined,
|
|
42
|
+
): TableConfig | undefined {
|
|
43
|
+
const { audit } = prgl.opts;
|
|
44
|
+
let result = { ...tableConfig };
|
|
45
|
+
if (audit) {
|
|
46
|
+
const { tableName } = audit;
|
|
47
|
+
if (typeof tableName !== "string" || !tableName)
|
|
48
|
+
throw new Error("audit.tableName is required");
|
|
49
|
+
if (tableConfig?.[tableName]) {
|
|
50
|
+
throw new Error("The audit table cannot also be defined in tableConfig");
|
|
51
|
+
}
|
|
52
|
+
result = {
|
|
53
|
+
[tableName]: {
|
|
54
|
+
columns: AUDIT_TABLE_COLUMN_DEFINITIONS,
|
|
55
|
+
triggers: getAuditProtection(tableName, ["update", "delete", "truncate"]),
|
|
56
|
+
},
|
|
57
|
+
...result,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
for (const [name, config] of Object.entries(
|
|
61
|
+
prgl.parsedAuditConfig?.tableConfigs ?? {},
|
|
62
|
+
)) {
|
|
63
|
+
result[name] = {
|
|
64
|
+
...result[name],
|
|
65
|
+
triggers: { ...result[name]?.triggers, ...config.triggers },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return Object.keys(result).length ? result : undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function isAuditTable(prgl: Prostgles, tableName: string): boolean {
|
|
72
|
+
return prgl.parsedAuditConfig?.auditTableNames.includes(tableName) ?? false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const getAuditWriterName = (auditTable: string, sourceTable: string) =>
|
|
76
|
+
AUDIT_WRITER_PREFIX + md5(JSON.stringify([auditTable, sourceTable]));
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { as } from "pg-promise";
|
|
2
|
+
import { asName } from "prostgles-types";
|
|
3
|
+
import type { Prostgles } from "../Prostgles";
|
|
4
|
+
import type { SchemaConfigAuditTableOptions } from "./AuditTypes";
|
|
5
|
+
import {
|
|
6
|
+
getAuditProtection,
|
|
7
|
+
getAuditWriterName,
|
|
8
|
+
type ParsedAuditConfig,
|
|
9
|
+
} from "./getAuditTableConfig";
|
|
10
|
+
|
|
11
|
+
/** Resolve column defaults against the completed schema; generate tableConfig entries only. */
|
|
12
|
+
export async function parseAuditConfig(
|
|
13
|
+
prgl: Prostgles,
|
|
14
|
+
): Promise<ParsedAuditConfig> {
|
|
15
|
+
const { audit } = prgl.opts;
|
|
16
|
+
const tables = prgl.dboBuilder.tables;
|
|
17
|
+
const auditTableNames = [
|
|
18
|
+
...new Set([
|
|
19
|
+
...(prgl.parsedAuditConfig?.auditTableNames ?? []),
|
|
20
|
+
...(audit ? [audit.tableName] : []),
|
|
21
|
+
]),
|
|
22
|
+
];
|
|
23
|
+
const result: ParsedAuditConfig = {
|
|
24
|
+
auditTableNames,
|
|
25
|
+
tableConfigs: {},
|
|
26
|
+
};
|
|
27
|
+
for (const name of auditTableNames) {
|
|
28
|
+
if (!tables.some((t) => t.name === name)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Audit table name must exactly match a schema table name: ${name}`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
result.tableConfigs[name] = {
|
|
34
|
+
triggers: getAuditProtection(name, ["update", "delete", "truncate"]),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!audit) return result;
|
|
38
|
+
const tableOptions: unknown = audit.tables;
|
|
39
|
+
if (
|
|
40
|
+
tableOptions !== undefined &&
|
|
41
|
+
(!tableOptions ||
|
|
42
|
+
typeof tableOptions !== "object" ||
|
|
43
|
+
Array.isArray(tableOptions))
|
|
44
|
+
) {
|
|
45
|
+
throw new Error("audit.tables must be a table map");
|
|
46
|
+
}
|
|
47
|
+
const entries = Object.entries(audit.tables ?? {}).filter(
|
|
48
|
+
([, value]) => value !== undefined,
|
|
49
|
+
);
|
|
50
|
+
const exclusion = entries.some(([, value]) => value === 0);
|
|
51
|
+
if (exclusion && entries.some(([, value]) => value !== 0))
|
|
52
|
+
throw new Error("audit.tables cannot mix enabled and disabled entries");
|
|
53
|
+
for (const [name, value] of entries) {
|
|
54
|
+
if (!tables.some((t) => t.name === name))
|
|
55
|
+
throw new Error(`Unknown audit table: ${name}`);
|
|
56
|
+
if (
|
|
57
|
+
value !== 0 &&
|
|
58
|
+
value !== 1 &&
|
|
59
|
+
(!value || typeof value !== "object" || Array.isArray(value))
|
|
60
|
+
)
|
|
61
|
+
throw new Error(`Invalid audit options for ${name}`);
|
|
62
|
+
}
|
|
63
|
+
const relations = await prgl.dbForSchema!.any<{
|
|
64
|
+
oid: number;
|
|
65
|
+
relkind: string;
|
|
66
|
+
relispartition: boolean;
|
|
67
|
+
}>(
|
|
68
|
+
"SELECT oid, relkind, relispartition FROM pg_class WHERE oid = ANY($1::oid[])",
|
|
69
|
+
[tables.map((t) => t.oid)],
|
|
70
|
+
);
|
|
71
|
+
for (const table of tables) {
|
|
72
|
+
const entry = entries.find(([name]) => name === table.name);
|
|
73
|
+
if (entries.length && (exclusion ? entry?.[1] === 0 : !entry)) continue;
|
|
74
|
+
const relation = relations.find((r) => r.oid === table.oid);
|
|
75
|
+
const isHistory = auditTableNames.includes(table.name);
|
|
76
|
+
const eligible =
|
|
77
|
+
!isHistory &&
|
|
78
|
+
table.schema !== "prostgles" &&
|
|
79
|
+
!table.is_view &&
|
|
80
|
+
!table.isHyperTable &&
|
|
81
|
+
relation &&
|
|
82
|
+
relation.relkind === "r" &&
|
|
83
|
+
!relation.relispartition;
|
|
84
|
+
if (!eligible) {
|
|
85
|
+
if (entry && entry[1] !== 0)
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Unsupported audit target: ${table.name}. Select ordinary, non-partitioned tables.`,
|
|
88
|
+
);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const options: SchemaConfigAuditTableOptions<void, string> =
|
|
92
|
+
typeof entry?.[1] === "object" ? entry[1] : {};
|
|
93
|
+
if (
|
|
94
|
+
Object.keys(options).some(
|
|
95
|
+
(k) => !["entityType", "idColumns", "excludeColumns"].includes(k),
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
throw new Error(`Unknown audit option for ${table.name}`);
|
|
99
|
+
const ids =
|
|
100
|
+
options.idColumns ??
|
|
101
|
+
table.columns.filter((c) => c.is_pkey).map((c) => c.name);
|
|
102
|
+
const excluded = options.excludeColumns ?? [];
|
|
103
|
+
if (!Array.isArray(ids) || !ids.length)
|
|
104
|
+
throw new Error(
|
|
105
|
+
`audit.idColumns is required for ${table.name} without a primary key`,
|
|
106
|
+
);
|
|
107
|
+
for (const list of [ids, excluded]) {
|
|
108
|
+
if (
|
|
109
|
+
!Array.isArray(list) ||
|
|
110
|
+
new Set(list).size !== list.length ||
|
|
111
|
+
list.some((c) => !table.columns.some((col) => col.name === c))
|
|
112
|
+
)
|
|
113
|
+
throw new Error(`Invalid audit columns for ${table.name}`);
|
|
114
|
+
}
|
|
115
|
+
if (ids.some((c) => excluded.includes(c)))
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Audit identity columns cannot be excluded: ${table.name}`,
|
|
118
|
+
);
|
|
119
|
+
if (
|
|
120
|
+
options.entityType !== undefined &&
|
|
121
|
+
(typeof options.entityType !== "string" || !options.entityType)
|
|
122
|
+
)
|
|
123
|
+
throw new Error(`Invalid audit entityType for ${table.name}`);
|
|
124
|
+
const functionName = getAuditWriterName(audit.tableName, table.name);
|
|
125
|
+
result.tableConfigs[table.name] = {
|
|
126
|
+
triggers: {
|
|
127
|
+
...getAuditProtection(audit.tableName, ["truncate"]),
|
|
128
|
+
[functionName]: {
|
|
129
|
+
type: "after",
|
|
130
|
+
actions: ["insert", "update", "delete"],
|
|
131
|
+
forEach: "row",
|
|
132
|
+
query: `DECLARE
|
|
133
|
+
before_row jsonb; after_row jsonb; before_id jsonb; after_id jsonb;
|
|
134
|
+
ids text[] := ARRAY[${ids.map((c) => as.text(c)).join(", ")}]::text[];
|
|
135
|
+
excluded text[] := ARRAY[${excluded.map((c) => as.text(c)).join(", ")}]::text[];
|
|
136
|
+
BEGIN
|
|
137
|
+
IF TG_OP <> 'INSERT' THEN
|
|
138
|
+
before_row := to_jsonb(OLD);
|
|
139
|
+
SELECT jsonb_object_agg(k, before_row -> k) INTO before_id FROM unnest(ids) k;
|
|
140
|
+
before_row := before_row - excluded;
|
|
141
|
+
END IF;
|
|
142
|
+
IF TG_OP <> 'DELETE' THEN
|
|
143
|
+
after_row := to_jsonb(NEW);
|
|
144
|
+
SELECT jsonb_object_agg(k, after_row -> k) INTO after_id FROM unnest(ids) k;
|
|
145
|
+
after_row := after_row - excluded;
|
|
146
|
+
END IF;
|
|
147
|
+
IF TG_OP = 'UPDATE' AND before_row IS NOT DISTINCT FROM after_row THEN RETURN NULL; END IF;
|
|
148
|
+
INSERT INTO ${asName(audit.tableName)} (schema_name, table_name, entity_type, operation, old_id, new_id, old_row, new_row, actor)
|
|
149
|
+
VALUES (TG_TABLE_SCHEMA, TG_TABLE_NAME, ${as.text(options.entityType ?? table.name)}, TG_OP, before_id, after_id, before_row, after_row,
|
|
150
|
+
nullif(current_setting('prostgles.user', true), '')::jsonb);
|
|
151
|
+
RETURN NULL;
|
|
152
|
+
END;`,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
@@ -250,7 +250,7 @@ export class DboBuilder {
|
|
|
250
250
|
this.checkingCanSubscribe = false;
|
|
251
251
|
}
|
|
252
252
|
const start = Date.now();
|
|
253
|
-
const tablesOrViewsReq = await getTablesForSchemaPostgresSQL(this, {
|
|
253
|
+
const tablesOrViewsReq = await getTablesForSchemaPostgresSQL({ db: this.prostgles.dbForSchema! }, {
|
|
254
254
|
schemaFilter: this.prostgles.opts.schemaFilter,
|
|
255
255
|
});
|
|
256
256
|
await this.prostgles.opts.onLog?.({
|
|
@@ -262,7 +262,7 @@ export class DboBuilder {
|
|
|
262
262
|
const tablesOrViews = tablesOrViewsReq.result;
|
|
263
263
|
this.tablesOrViews = tablesOrViews;
|
|
264
264
|
|
|
265
|
-
this.constraints = await getConstraints(this.
|
|
265
|
+
this.constraints = await getConstraints(this.prostgles.dbForSchema!, this.prostgles.opts.schemaFilter);
|
|
266
266
|
await this.prepareShortestJoinPaths();
|
|
267
267
|
|
|
268
268
|
this.dbo = {};
|
|
@@ -107,6 +107,7 @@ export class QueryStreamer {
|
|
|
107
107
|
let streamState: "started" | "ended" | "errored" | undefined;
|
|
108
108
|
|
|
109
109
|
const startStream = async (client: pg.Client | undefined, query: ClientStreamedRequest) => {
|
|
110
|
+
await this.dboBuilder.prostgles.schemaReady;
|
|
110
111
|
await this.dboBuilder.cacheDBTypes();
|
|
111
112
|
const socketQuery = this.socketQueries.get(socketId)?.get(id);
|
|
112
113
|
if (!socketQuery) {
|
|
@@ -25,7 +25,7 @@ export const getSchemaFilter = (schema: ProstglesInitOptions["schemaFilter"] = {
|
|
|
25
25
|
// TODO: Add a onSocketConnect timeout for this query.
|
|
26
26
|
// Reason: this query gets blocked by prostgles.app_triggers from PubSubManager.addTrigger in some cases (pg_dump locks that table)
|
|
27
27
|
export async function getTablesForSchemaPostgresSQL(
|
|
28
|
-
{ db
|
|
28
|
+
{ db }: Pick<DboBuilder, "db">,
|
|
29
29
|
{
|
|
30
30
|
schemaFilter,
|
|
31
31
|
ddlWithRollback,
|
|
@@ -288,7 +288,7 @@ export async function getTablesForSchemaPostgresSQL(
|
|
|
288
288
|
});
|
|
289
289
|
|
|
290
290
|
/** Get view reference cols (based on parent table) */
|
|
291
|
-
const viewReferenceColumns = await getViewReferenceColumns(table, tableSchemaList,
|
|
291
|
+
const viewReferenceColumns = await getViewReferenceColumns(table, tableSchemaList, db);
|
|
292
292
|
|
|
293
293
|
table.columns = table.columns.map((col) => {
|
|
294
294
|
if (col.has_default) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { isDefined
|
|
2
|
-
import type {
|
|
1
|
+
import { isDefined } from "prostgles-types";
|
|
2
|
+
import type { DB } from "../../Prostgles";
|
|
3
|
+
import { getDbTypes, getDetailedFieldInfo } from "../runSql/runSqlUtils";
|
|
3
4
|
import type { TableSchema, TableSchemaColumn } from "../DboBuilderTypes";
|
|
4
5
|
|
|
5
6
|
export const getViewReferenceColumns = async (
|
|
6
7
|
table: TableSchema,
|
|
7
8
|
tableSchemaList: TableSchema[],
|
|
8
|
-
|
|
9
|
+
db: DB
|
|
9
10
|
) => {
|
|
10
11
|
const viewReferenceColumns: Pick<TableSchemaColumn, "name" | "references">[] = [];
|
|
11
12
|
if (!table.is_view || !table.view_definition) {
|
|
@@ -16,12 +17,10 @@ export const getViewReferenceColumns = async (
|
|
|
16
17
|
table.view_definition.endsWith(";") ?
|
|
17
18
|
table.view_definition.slice(0, -1)
|
|
18
19
|
: table.view_definition;
|
|
19
|
-
const { fields
|
|
20
|
+
const { fields } = await db.result(
|
|
20
21
|
`SELECT * FROM \n ( ${view_definition} \n) t LIMIT 0`,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
undefined
|
|
24
|
-
)) as SQLResult<undefined>;
|
|
22
|
+
);
|
|
23
|
+
const viewFieldsWithInfo = getDetailedFieldInfo(await getDbTypes(db), fields);
|
|
25
24
|
const viewTables = tableSchemaList.filter((r) =>
|
|
26
25
|
viewFieldsWithInfo.some((f) => f.tableID === r.oid)
|
|
27
26
|
);
|
package/lib/Prostgles.ts
CHANGED
|
@@ -63,6 +63,9 @@ import type { getAdminClient } from "./DboBuilder/runSql/getAdminClient";
|
|
|
63
63
|
import type { TableHandler } from "./DboBuilder/TableHandler/TableHandler";
|
|
64
64
|
import { getFileTableConfig } from "./StorageClient/getFileTableConfig";
|
|
65
65
|
import { dirname } from "path";
|
|
66
|
+
import { getAuditTableConfig, type ParsedAuditConfig } from "./Audit/getAuditTableConfig";
|
|
67
|
+
import { syncTableTriggers } from "./TableConfig/syncTableTriggers";
|
|
68
|
+
import { isManagedTriggerName } from "./TableConfig/managedTriggerNames";
|
|
66
69
|
|
|
67
70
|
export class Prostgles {
|
|
68
71
|
/**
|
|
@@ -106,6 +109,16 @@ export class Prostgles {
|
|
|
106
109
|
|
|
107
110
|
keywords = DEFAULT_KEYWORDS;
|
|
108
111
|
loaded = false;
|
|
112
|
+
/** Only schema setup uses this original connection. Application handlers use db. */
|
|
113
|
+
dbForSchema?: DB;
|
|
114
|
+
schemaReady: Promise<void> = Promise.resolve();
|
|
115
|
+
|
|
116
|
+
runSchemaQueries = (queries: () => Promise<void>) => {
|
|
117
|
+
this.schemaReady = this.schemaReady.catch(() => {}).then(queries);
|
|
118
|
+
void this.schemaReady.catch(() => {});
|
|
119
|
+
return this.schemaReady;
|
|
120
|
+
};
|
|
121
|
+
parsedAuditConfig?: ParsedAuditConfig;
|
|
109
122
|
preparingTableConfig = false;
|
|
110
123
|
|
|
111
124
|
dbEventsManager?: DBEventsManager;
|
|
@@ -116,7 +129,15 @@ export class Prostgles {
|
|
|
116
129
|
tableConfigurator?: TableConfigurator;
|
|
117
130
|
|
|
118
131
|
get mergedTableConfig() {
|
|
119
|
-
|
|
132
|
+
for (const [tableName, table] of Object.entries(this.opts.tableConfig ?? {})) {
|
|
133
|
+
for (const name of Object.keys(table.triggers ?? {})) {
|
|
134
|
+
if (isManagedTriggerName(name)) {
|
|
135
|
+
throw new Error(`Trigger ${tableName}.${name} uses a prefix reserved for prostgles`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const config = getFileTableConfig(this);
|
|
140
|
+
return { ...config, tableConfig: getAuditTableConfig(this, config.tableConfig) };
|
|
120
141
|
}
|
|
121
142
|
|
|
122
143
|
isMedia(tableName: string) {
|
|
@@ -148,6 +169,7 @@ export class Prostgles {
|
|
|
148
169
|
onQuery: 1,
|
|
149
170
|
onConnectionError: 1,
|
|
150
171
|
tableConfig: 1,
|
|
172
|
+
audit: 1,
|
|
151
173
|
tableHooks: 1,
|
|
152
174
|
tableConfigMigrations: 1,
|
|
153
175
|
onNotice: 1,
|
|
@@ -286,10 +308,18 @@ export class Prostgles {
|
|
|
286
308
|
|
|
287
309
|
/** Rebuilds the DBO and replaces the application context. */
|
|
288
310
|
refreshDBO = async () => {
|
|
311
|
+
this.loaded = false;
|
|
289
312
|
await this.cleanupContext();
|
|
290
|
-
|
|
313
|
+
await this.runSchemaQueries(async () => {
|
|
314
|
+
await this.rebuildDBO();
|
|
315
|
+
if (this.opts.audit || this.parsedAuditConfig) {
|
|
316
|
+
await syncTableTriggers(this);
|
|
317
|
+
await this.rebuildDBO();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
291
320
|
await this.createContext({ type: "dbo.refresh" });
|
|
292
|
-
|
|
321
|
+
this.loaded = true;
|
|
322
|
+
return this.dbo!;
|
|
293
323
|
};
|
|
294
324
|
|
|
295
325
|
initRestApi = () => {
|
|
@@ -307,7 +337,6 @@ export class Prostgles {
|
|
|
307
337
|
if (this.tableConfigurator?.initialising) {
|
|
308
338
|
console.error("TableConfigurator WILL deadlock", { reason });
|
|
309
339
|
}
|
|
310
|
-
await this.tableConfigurator?.destroy();
|
|
311
340
|
this.tableConfigurator = new TableConfigurator(this);
|
|
312
341
|
try {
|
|
313
342
|
const now = Date.now();
|
|
@@ -323,7 +352,7 @@ export class Prostgles {
|
|
|
323
352
|
duration: Date.now() - now,
|
|
324
353
|
});
|
|
325
354
|
} catch (e) {
|
|
326
|
-
if (this.opts.tableConfigMigrations?.silentFail === false) {
|
|
355
|
+
if (!this.opts.audit && this.opts.tableConfigMigrations?.silentFail === false) {
|
|
327
356
|
console.error("TableConfigurator silentFail: ", e);
|
|
328
357
|
} else {
|
|
329
358
|
throw e;
|
package/lib/ProstglesTypes.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type { StorageClient } from "./StorageClient/StorageClientTypes";
|
|
|
14
14
|
import type { PGConstraint } from "./TableConfig/fetchTableConstraints";
|
|
15
15
|
import type { TableConfig } from "./TableConfig/TableConfigTypes";
|
|
16
16
|
import type { TableHooks } from "./TableHooks/TableHooks";
|
|
17
|
+
import type { SchemaConfigAudit } from "./Audit/AuditTypes";
|
|
17
18
|
|
|
18
19
|
import type { PRGLIOSocket } from "./DboBuilder/DboBuilder";
|
|
19
20
|
|
|
@@ -312,6 +313,8 @@ export type ProstglesInitOptions<
|
|
|
312
313
|
* made to the database schema through SQL queries
|
|
313
314
|
*/
|
|
314
315
|
tableConfig?: TableConfig;
|
|
316
|
+
/** Audit committed row changes after bootstrap, before application callbacks run. */
|
|
317
|
+
audit?: SchemaConfigAudit<S>;
|
|
315
318
|
|
|
316
319
|
/**
|
|
317
320
|
* Migration logic used when the new tableConfig version is higher than the one in the database.
|
|
@@ -6,7 +6,7 @@ import type { DboBuilder } from "../DboBuilder/DboBuilder";
|
|
|
6
6
|
export const getCreatePubSubManagerError = async (
|
|
7
7
|
dboBuilder: DboBuilder
|
|
8
8
|
): Promise<string | undefined> => {
|
|
9
|
-
const db = dboBuilder.
|
|
9
|
+
const db = dboBuilder.prostgles.dbForSchema!;
|
|
10
10
|
|
|
11
11
|
const canExecute = await getCanExecute(db);
|
|
12
12
|
if (!canExecute) return "Cannot run EXECUTE statements on this connection";
|
|
@@ -500,19 +500,19 @@ COMMIT;
|
|
|
500
500
|
export const getPubSubManagerInitQuery = async function (
|
|
501
501
|
this: DboBuilder,
|
|
502
502
|
): Promise<string | undefined> {
|
|
503
|
-
const versionNum = await this.
|
|
503
|
+
const versionNum = await this.prostgles.dbForSchema!.one("SELECT current_setting('server_version_num')::int as val");
|
|
504
504
|
const initQuery = getInitQuery(this.prostgles.opts.DEBUG_MODE, versionNum.val);
|
|
505
|
-
const { schema_md5 = "none" } = await this.
|
|
505
|
+
const { schema_md5 = "none" } = await this.prostgles.dbForSchema!.oneOrNone("SELECT md5($1) as schema_md5", [
|
|
506
506
|
initQuery.trim(),
|
|
507
507
|
]);
|
|
508
508
|
const query = pgp.as.format(initQuery, { schema_md5, version });
|
|
509
|
-
const existingSchema = await this.
|
|
509
|
+
const existingSchema = await this.prostgles.dbForSchema!.any(PROSTGLES_SCHEMA_EXISTS_QUERY);
|
|
510
510
|
if (!existingSchema.length) {
|
|
511
511
|
console.log("getPubSubManagerInitQuery: No prostgles.versions table found. Creating...");
|
|
512
512
|
return query;
|
|
513
513
|
}
|
|
514
514
|
const { data: existingSchemaVersions } = await tryCatchV2(async () => {
|
|
515
|
-
const existingSchemaVersions = await this.
|
|
515
|
+
const existingSchemaVersions = await this.prostgles.dbForSchema!.any(PROSTGLES_SCHEMA_VERSION_OK_QUERY, {
|
|
516
516
|
schema_md5,
|
|
517
517
|
version,
|
|
518
518
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAuditTable } from "../Audit/getAuditTableConfig";
|
|
1
2
|
import { getObjectEntries, isObject } from "prostgles-types";
|
|
2
3
|
import type { AuthResultWithSID } from "../Auth/AuthTypes";
|
|
3
4
|
import type { TableHandler } from "../DboBuilder/TableHandler/TableHandler";
|
|
@@ -42,7 +43,7 @@ export async function getTableRulesWithoutFileTable(
|
|
|
42
43
|
|
|
43
44
|
const isStarOrTrue = (value: any): value is "*" | true => value === "*" || value === true;
|
|
44
45
|
const { privileges } = tableHandler.tableOrViewInfo;
|
|
45
|
-
const
|
|
46
|
+
const rawTableRulesObject =
|
|
46
47
|
isStarOrTrue(rawTableRule) ?
|
|
47
48
|
{
|
|
48
49
|
select: privileges.select ? TABLE_RULE_NO_LIMITS.select : undefined,
|
|
@@ -52,6 +53,10 @@ export async function getTableRulesWithoutFileTable(
|
|
|
52
53
|
}
|
|
53
54
|
: rawTableRule;
|
|
54
55
|
|
|
56
|
+
const tableRulesObject = isAuditTable(this.prostgles, tableName)
|
|
57
|
+
? { select: rawTableRulesObject.select, insert: undefined, update: undefined, delete: undefined }
|
|
58
|
+
: rawTableRulesObject;
|
|
59
|
+
|
|
55
60
|
const selectRule =
|
|
56
61
|
!tableRulesObject.select ? undefined
|
|
57
62
|
: isStarOrTrue(tableRulesObject.select) ? TABLE_RULE_NO_LIMITS.select
|
|
@@ -31,7 +31,7 @@ type BaseTableDefinition = {
|
|
|
31
31
|
* Use "before" when you need to change the data before the action
|
|
32
32
|
*/
|
|
33
33
|
type: "before" | "after" | "instead of";
|
|
34
|
-
actions: ("insert" | "update" | "delete")[];
|
|
34
|
+
actions: ("insert" | "update" | "delete" | "truncate")[];
|
|
35
35
|
forEach: "statement" | "row";
|
|
36
36
|
/**
|
|
37
37
|
* @example
|
|
@@ -23,8 +23,8 @@ export class TableConfigurator {
|
|
|
23
23
|
return this.prostgles.dbo;
|
|
24
24
|
}
|
|
25
25
|
get db(): DB {
|
|
26
|
-
if (!this.prostgles.
|
|
27
|
-
return this.prostgles.
|
|
26
|
+
if (!this.prostgles.dbForSchema) throw "Schema database connection missing";
|
|
27
|
+
return this.prostgles.dbForSchema;
|
|
28
28
|
}
|
|
29
29
|
prostgles: Prostgles;
|
|
30
30
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getManagedTriggerName } from "./managedTriggerNames";
|
|
1
2
|
import { as } from "pg-promise";
|
|
2
3
|
import { isDefined } from "prostgles-types";
|
|
3
4
|
import type { DB } from "../initProstgles";
|
|
@@ -226,14 +227,14 @@ const getTriggerQueries = (
|
|
|
226
227
|
];
|
|
227
228
|
|
|
228
229
|
trigger.actions.forEach((action) => {
|
|
229
|
-
const triggerActionName = triggerFuncName
|
|
230
|
+
const triggerActionName = getManagedTriggerName(triggerFuncName, action);
|
|
230
231
|
|
|
231
232
|
const triggerActionNameParsed = asName(triggerActionName);
|
|
232
233
|
|
|
233
234
|
const newTableName = action !== "delete" ? "NEW TABLE AS new_table" : "";
|
|
234
235
|
const oldTableName = action !== "insert" ? "OLD TABLE AS old_table" : "";
|
|
235
236
|
const transitionTables =
|
|
236
|
-
trigger.forEach === "row" ? "" : `REFERENCING ${newTableName} ${oldTableName}`;
|
|
237
|
+
trigger.forEach === "row" || trigger.type !== "after" || action === "truncate" ? "" : `REFERENCING ${newTableName} ${oldTableName}`;
|
|
237
238
|
queries.push(`
|
|
238
239
|
CREATE TRIGGER ${triggerActionNameParsed}
|
|
239
240
|
${trigger.type} ${action} ON ${tableName}
|