@prisma-next/target-postgres 0.14.0-dev.4 → 0.14.0-dev.40
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/authoring-BX1WHIdN.mjs +220 -0
- package/dist/authoring-BX1WHIdN.mjs.map +1 -0
- package/dist/canonicalize-Bn3BM1Cn.mjs +46 -0
- package/dist/canonicalize-Bn3BM1Cn.mjs.map +1 -0
- package/dist/canonicalize-CAbXtVtB.d.mts +37 -0
- package/dist/canonicalize-CAbXtVtB.d.mts.map +1 -0
- package/dist/{codec-types-DHCkwPKE.d.mts → codec-types-CueCoGD0.d.mts} +2 -2
- package/dist/{codec-types-DHCkwPKE.d.mts.map → codec-types-CueCoGD0.d.mts.map} +1 -1
- package/dist/codec-types.d.mts +1 -1
- package/dist/{codecs--0A5_4Bq.d.mts → codecs-Ca8upop6.d.mts} +12 -1
- package/dist/codecs-Ca8upop6.d.mts.map +1 -0
- package/dist/codecs.d.mts +1 -1
- package/dist/codecs.mjs +34 -1
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.d.mts +8 -3
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +3 -3
- package/dist/control.d.mts +1 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +639 -21
- package/dist/control.mjs.map +1 -1
- package/dist/data-transform-BOWpliq8.mjs.map +1 -1
- package/dist/{data-transform-DDgWdB5o.d.mts → data-transform-qKy1U5V9.d.mts} +2 -2
- package/dist/{data-transform-DDgWdB5o.d.mts.map → data-transform-qKy1U5V9.d.mts.map} +1 -1
- package/dist/data-transform.d.mts +1 -1
- package/dist/{ddl-QDyOSeLc.mjs → ddl-BmLIe1z_.mjs} +62 -5
- package/dist/ddl-BmLIe1z_.mjs.map +1 -0
- package/dist/ddl.d.mts +3 -2
- package/dist/ddl.mjs +2 -2
- package/dist/{default-normalizer-DyyCHQWs.mjs → default-normalizer-Dug8Fez9.mjs} +119 -2
- package/dist/default-normalizer-Dug8Fez9.mjs.map +1 -0
- package/dist/default-normalizer.d.mts +1 -1
- package/dist/default-normalizer.d.mts.map +1 -1
- package/dist/default-normalizer.mjs +1 -1
- package/dist/descriptor-meta-runtime-UwYdV8fD.mjs +12 -0
- package/dist/descriptor-meta-runtime-UwYdV8fD.mjs.map +1 -0
- package/dist/descriptor-meta-tjatyhtZ.mjs +18 -0
- package/dist/descriptor-meta-tjatyhtZ.mjs.map +1 -0
- package/dist/{issue-planner-DL6g3CmE.mjs → issue-planner-DbFvaAgu.mjs} +35 -10
- package/dist/issue-planner-DbFvaAgu.mjs.map +1 -0
- package/dist/issue-planner.d.mts +3 -3
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +5 -5
- package/dist/migration.mjs +4 -4
- package/dist/nodes-CSkPX3rI.d.mts +120 -0
- package/dist/nodes-CSkPX3rI.d.mts.map +1 -0
- package/dist/{nodes-Bbhs2rwj.mjs → nodes-Dh__NS58.mjs} +56 -2
- package/dist/nodes-Dh__NS58.mjs.map +1 -0
- package/dist/{op-factory-call-DmQEc3XV.d.mts → op-factory-call-CJWUxyUW.d.mts} +44 -13
- package/dist/op-factory-call-CJWUxyUW.d.mts.map +1 -0
- package/dist/{op-factory-call-D_p5vxwt.mjs → op-factory-call-t65c4jyY.mjs} +201 -28
- package/dist/op-factory-call-t65c4jyY.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/pack.d.mts +74 -2
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/planner-CG2pLHk-.mjs +640 -0
- package/dist/planner-CG2pLHk-.mjs.map +1 -0
- package/dist/{planner-ddl-builders-B2wOwLqI.mjs → planner-ddl-builders-B8Nn6nHN.mjs} +12 -27
- package/dist/planner-ddl-builders-B8Nn6nHN.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +2 -4
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +2 -2
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs → planner-produced-postgres-migration-BolD0Mvc.mjs} +3 -3
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs.map → planner-produced-postgres-migration-BolD0Mvc.mjs.map} +1 -1
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts → planner-produced-postgres-migration-Cz9--XOT.d.mts} +4 -4
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts.map → planner-produced-postgres-migration-Cz9--XOT.d.mts.map} +1 -1
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/{planner-sql-checks-jqUUGyQR.mjs → planner-sql-checks-DE9c4519.mjs} +2 -2
- package/dist/{planner-sql-checks-jqUUGyQR.mjs.map → planner-sql-checks-DE9c4519.mjs.map} +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/{planner-target-details-CIY6tLeo.d.mts → planner-target-details-HkP4RrRO.d.mts} +2 -2
- package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -0
- package/dist/planner-target-details.d.mts +1 -1
- package/dist/planner.d.mts +82 -4
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-contract-serializer-k3TAcPMY.mjs → postgres-contract-view-BegduLr6.mjs} +50 -23
- package/dist/postgres-contract-view-BegduLr6.mjs.map +1 -0
- package/dist/postgres-contract-view-C9j5cqP0.d.mts +34 -0
- package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +1 -0
- package/dist/postgres-database-schema-node-os8ogEap.d.mts +183 -0
- package/dist/postgres-database-schema-node-os8ogEap.d.mts.map +1 -0
- package/dist/{postgres-migration-B5jKrXv3.mjs → postgres-migration-BfMmK6tv.mjs} +21 -2
- package/dist/postgres-migration-BfMmK6tv.mjs.map +1 -0
- package/dist/{postgres-migration-Y4YBJqkS.d.mts → postgres-migration-D7kNVWVG.d.mts} +19 -6
- package/dist/postgres-migration-D7kNVWVG.d.mts.map +1 -0
- package/dist/postgres-rls-policy--CSddvMR.d.mts +52 -0
- package/dist/postgres-rls-policy--CSddvMR.d.mts.map +1 -0
- package/dist/{postgres-schema-COGZ1ark.mjs → postgres-schema-BLIHBEfX.mjs} +108 -5
- package/dist/postgres-schema-BLIHBEfX.mjs.map +1 -0
- package/dist/postgres-schema-BywoeKlt.d.mts +189 -0
- package/dist/postgres-schema-BywoeKlt.d.mts.map +1 -0
- package/dist/postgres-table-schema-node-1nNc8XDT.mjs +248 -0
- package/dist/postgres-table-schema-node-1nNc8XDT.mjs.map +1 -0
- package/dist/render-ops.d.mts +1 -1
- package/dist/{render-typescript-KMgosran.mjs → render-typescript-7yqILcwr.mjs} +45 -16
- package/dist/render-typescript-7yqILcwr.mjs.map +1 -0
- package/dist/render-typescript.mjs +1 -1
- package/dist/resolve-ddl-schema-CL1RR1mx.mjs +20 -0
- package/dist/resolve-ddl-schema-CL1RR1mx.mjs.map +1 -0
- package/dist/rls-canonicalize.d.mts +2 -0
- package/dist/rls-canonicalize.mjs +2 -0
- package/dist/runtime.d.mts +7 -6
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +3 -3
- package/dist/runtime.mjs.map +1 -1
- package/dist/schema-ir-annotations.d.mts +14 -0
- package/dist/schema-ir-annotations.d.mts.map +1 -0
- package/dist/schema-ir-annotations.mjs +2 -0
- package/dist/types.d.mts +4 -140
- package/dist/types.mjs +3 -2
- package/package.json +22 -17
- package/src/contract-free/checks.ts +73 -0
- package/src/contract-free/ddl.ts +45 -1
- package/src/core/authoring.ts +115 -1
- package/src/core/codecs.ts +35 -1
- package/src/core/ddl/nodes.ts +94 -2
- package/src/core/default-normalizer.ts +122 -2
- package/src/core/descriptor-meta.ts +4 -0
- package/src/core/entity-kinds.ts +16 -0
- package/src/core/migrations/bound-schema.ts +10 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +125 -0
- package/src/core/migrations/control-policy.ts +3 -0
- package/src/core/migrations/diff-database-schema.ts +191 -0
- package/src/core/migrations/issue-planner.ts +44 -3
- package/src/core/migrations/op-factory-call.ts +109 -21
- package/src/core/migrations/operations/columns.ts +24 -12
- package/src/core/migrations/operations/data-transform.ts +6 -0
- package/src/core/migrations/operations/rls.ts +106 -0
- package/src/core/migrations/operations/shared.ts +0 -30
- package/src/core/migrations/planner-ddl-builders.ts +15 -56
- package/src/core/migrations/planner-recipes.ts +31 -13
- package/src/core/migrations/planner-strategies.ts +3 -11
- package/src/core/migrations/planner-target-details.ts +3 -1
- package/src/core/migrations/planner.ts +165 -33
- package/src/core/migrations/postgres-migration.ts +34 -4
- package/src/core/migrations/render-typescript.ts +42 -15
- package/src/core/migrations/resolve-ddl-schema.ts +24 -0
- package/src/core/migrations/runner.ts +13 -16
- package/src/core/migrations/verify-postgres-namespaces.ts +24 -19
- package/src/core/postgres-contract-serializer.ts +50 -33
- package/src/core/postgres-contract-view.ts +48 -0
- package/src/core/postgres-rls-policy.ts +64 -0
- package/src/core/postgres-role.ts +37 -0
- package/src/core/postgres-schema.ts +37 -13
- package/src/core/postgres-validators.ts +20 -0
- package/src/core/psl-infer/infer-psl-contract.ts +834 -0
- package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
- package/src/core/psl-infer/postgres-type-map.ts +157 -0
- package/src/core/rls/canonicalize.ts +48 -0
- package/src/core/schema-ir/postgres-database-schema-node.ts +63 -0
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +61 -0
- package/src/core/schema-ir/postgres-policy-schema-node.ts +93 -0
- package/src/core/schema-ir/postgres-role-schema-node.ts +66 -0
- package/src/core/schema-ir/postgres-table-schema-node.ts +107 -0
- package/src/core/schema-ir/schema-node-kinds.ts +28 -0
- package/src/exports/contract-free.ts +7 -1
- package/src/exports/control.ts +39 -12
- package/src/exports/ddl.ts +4 -0
- package/src/exports/migration.ts +1 -0
- package/src/exports/planner-ddl-builders.ts +0 -2
- package/src/exports/planner.ts +2 -0
- package/src/exports/rls-canonicalize.ts +6 -0
- package/src/exports/runtime.ts +1 -0
- package/src/exports/schema-ir-annotations.ts +1 -0
- package/src/exports/types.ts +28 -0
- package/dist/codecs--0A5_4Bq.d.mts.map +0 -1
- package/dist/ddl-QDyOSeLc.mjs.map +0 -1
- package/dist/default-normalizer-DyyCHQWs.mjs.map +0 -1
- package/dist/descriptor-meta-CpGygXpI.mjs +0 -140
- package/dist/descriptor-meta-CpGygXpI.mjs.map +0 -1
- package/dist/issue-planner-DL6g3CmE.mjs.map +0 -1
- package/dist/nodes-Bbhs2rwj.mjs.map +0 -1
- package/dist/nodes-pLeLgdis.d.mts +0 -67
- package/dist/nodes-pLeLgdis.d.mts.map +0 -1
- package/dist/op-factory-call-D_p5vxwt.mjs.map +0 -1
- package/dist/op-factory-call-DmQEc3XV.d.mts.map +0 -1
- package/dist/planner-Bs_baQax.mjs +0 -324
- package/dist/planner-Bs_baQax.mjs.map +0 -1
- package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +0 -1
- package/dist/planner-target-details-CIY6tLeo.d.mts.map +0 -1
- package/dist/postgres-contract-serializer-k3TAcPMY.mjs.map +0 -1
- package/dist/postgres-migration-B5jKrXv3.mjs.map +0 -1
- package/dist/postgres-migration-Y4YBJqkS.d.mts.map +0 -1
- package/dist/postgres-schema-COGZ1ark.mjs.map +0 -1
- package/dist/render-typescript-KMgosran.mjs.map +0 -1
- package/dist/types.d.mts.map +0 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { ContractToSchemaIROptions } from '@prisma-next/family-sql/control';
|
|
2
|
+
import { contractNamespaceToSchemaIR } from '@prisma-next/family-sql/control';
|
|
3
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
4
|
+
import type { PostgresRlsPolicy } from '../postgres-rls-policy';
|
|
5
|
+
import type { PostgresContract } from '../postgres-schema';
|
|
6
|
+
import { isPostgresSchema } from '../postgres-schema';
|
|
7
|
+
import { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';
|
|
8
|
+
import { PostgresNamespaceSchemaNode } from '../schema-ir/postgres-namespace-schema-node';
|
|
9
|
+
import { PostgresPolicySchemaNode } from '../schema-ir/postgres-policy-schema-node';
|
|
10
|
+
import { PostgresRoleSchemaNode } from '../schema-ir/postgres-role-schema-node';
|
|
11
|
+
import { PostgresTableSchemaNode } from '../schema-ir/postgres-table-schema-node';
|
|
12
|
+
import { resolveDdlSchemaForNamespaceStorage } from './resolve-ddl-schema';
|
|
13
|
+
|
|
14
|
+
function toPolicyNode(policy: PostgresRlsPolicy, namespaceId: string): PostgresPolicySchemaNode {
|
|
15
|
+
return new PostgresPolicySchemaNode({
|
|
16
|
+
name: policy.name,
|
|
17
|
+
prefix: policy.prefix,
|
|
18
|
+
tableName: policy.tableName,
|
|
19
|
+
namespaceId,
|
|
20
|
+
operation: policy.operation,
|
|
21
|
+
roles: [...policy.roles],
|
|
22
|
+
...ifDefined('using', policy.using),
|
|
23
|
+
...ifDefined('withCheck', policy.withCheck),
|
|
24
|
+
permissive: policy.permissive,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Projects a Postgres contract into the expected schema-diff tree: a
|
|
30
|
+
* `PostgresDatabaseSchemaNode` root holding one `PostgresNamespaceSchemaNode`
|
|
31
|
+
* per Postgres namespace, each holding its `PostgresTableSchemaNode`s with
|
|
32
|
+
* their `PostgresPolicySchemaNode`s, plus the database roles on the root.
|
|
33
|
+
*
|
|
34
|
+
* Not a duplicate of the family's `contractToSchemaIR`: that builds a flat,
|
|
35
|
+
* single `{ tables }` map (and throws on cross-namespace name collisions, with
|
|
36
|
+
* no RLS/role concept) for SQLite's single-schema world. This is the
|
|
37
|
+
* Postgres-specific *tree* shape — multi-schema, RLS-policy-aware, role-aware.
|
|
38
|
+
* It reuses the family's per-namespace table conversion (`contractNamespaceToSchemaIR`)
|
|
39
|
+
* for column/FK/index building and only adds the Postgres tree/policy/role shape.
|
|
40
|
+
*
|
|
41
|
+
* Tables are grouped by their owning namespace (resolved DDL schema name) so
|
|
42
|
+
* the tree mirrors Postgres's object hierarchy. The DDL schema name is
|
|
43
|
+
* resolved once per namespace.
|
|
44
|
+
*
|
|
45
|
+
* A policy that references a table absent from its namespace is a malformed
|
|
46
|
+
* contract — the loop throws rather than fabricating a stub table.
|
|
47
|
+
*/
|
|
48
|
+
export function contractToPostgresDatabaseSchemaNode(
|
|
49
|
+
contract: PostgresContract | null,
|
|
50
|
+
options: ContractToSchemaIROptions,
|
|
51
|
+
): PostgresDatabaseSchemaNode {
|
|
52
|
+
if (contract === null) {
|
|
53
|
+
return new PostgresDatabaseSchemaNode({
|
|
54
|
+
namespaces: {},
|
|
55
|
+
roles: [],
|
|
56
|
+
existingSchemas: [],
|
|
57
|
+
pgVersion: '',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const namespaces: Record<string, PostgresNamespaceSchemaNode> = {};
|
|
62
|
+
const roles: PostgresRoleSchemaNode[] = [];
|
|
63
|
+
const ownedSchemas: string[] = [];
|
|
64
|
+
|
|
65
|
+
for (const ns of Object.values(contract.storage.namespaces)) {
|
|
66
|
+
if (!isPostgresSchema(ns)) continue;
|
|
67
|
+
const ddlSchema = resolveDdlSchemaForNamespaceStorage(contract.storage, ns.id);
|
|
68
|
+
ownedSchemas.push(ddlSchema);
|
|
69
|
+
|
|
70
|
+
// Convert only THIS namespace's tables (passing the full storage for
|
|
71
|
+
// type/value-set/enum resolution that spans namespaces), so the same table
|
|
72
|
+
// name can exist in two schemas without colliding in a bare-keyed record.
|
|
73
|
+
const sqlTables = contractNamespaceToSchemaIR(contract.storage, ns.id, options).tables;
|
|
74
|
+
|
|
75
|
+
const policiesByTable = new Map<string, PostgresPolicySchemaNode[]>();
|
|
76
|
+
for (const policy of Object.values(ns.policy)) {
|
|
77
|
+
const list = policiesByTable.get(policy.tableName) ?? [];
|
|
78
|
+
list.push(toPolicyNode(policy, ddlSchema));
|
|
79
|
+
policiesByTable.set(policy.tableName, list);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const tables: Record<string, PostgresTableSchemaNode> = {};
|
|
83
|
+
for (const tableName of Object.keys(ns.table)) {
|
|
84
|
+
const sqlTable = sqlTables[tableName];
|
|
85
|
+
if (sqlTable === undefined) continue;
|
|
86
|
+
tables[tableName] = new PostgresTableSchemaNode({
|
|
87
|
+
name: sqlTable.name,
|
|
88
|
+
columns: sqlTable.columns,
|
|
89
|
+
foreignKeys: sqlTable.foreignKeys,
|
|
90
|
+
uniques: sqlTable.uniques,
|
|
91
|
+
indexes: sqlTable.indexes,
|
|
92
|
+
...ifDefined('primaryKey', sqlTable.primaryKey),
|
|
93
|
+
...ifDefined('annotations', sqlTable.annotations),
|
|
94
|
+
...ifDefined('checks', sqlTable.checks),
|
|
95
|
+
policies: policiesByTable.get(tableName) ?? [],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const [tableName, tablePolicies] of policiesByTable) {
|
|
100
|
+
if (!(tableName in tables)) {
|
|
101
|
+
const policyName = tablePolicies[0]?.name ?? '(unknown)';
|
|
102
|
+
throw new Error(
|
|
103
|
+
`contract-to-postgres-database-schema-node: policy "${policyName}" references table "${tableName}" not present in namespace "${ddlSchema}"`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
namespaces[ddlSchema] = new PostgresNamespaceSchemaNode({
|
|
109
|
+
schemaName: ddlSchema,
|
|
110
|
+
tables,
|
|
111
|
+
nativeEnumTypeNames: [],
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
for (const role of Object.values(ns.role)) {
|
|
115
|
+
roles.push(new PostgresRoleSchemaNode({ name: role.name, namespaceId: role.namespaceId }));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return new PostgresDatabaseSchemaNode({
|
|
120
|
+
namespaces,
|
|
121
|
+
roles,
|
|
122
|
+
existingSchemas: ownedSchemas,
|
|
123
|
+
pgVersion: '',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
@@ -21,6 +21,8 @@ import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
|
21
21
|
const OBJECT_CREATION_FACTORIES: ReadonlySet<string> = new Set<string>([
|
|
22
22
|
'createTable',
|
|
23
23
|
'createSchema',
|
|
24
|
+
'createRlsPolicy',
|
|
25
|
+
'enableRowLevelSecurity',
|
|
24
26
|
]);
|
|
25
27
|
|
|
26
28
|
function createsNewTopLevelObject(call: PostgresOpFactoryCall): boolean {
|
|
@@ -151,6 +153,7 @@ export function resolvePostgresCallControlPolicySubject(
|
|
|
151
153
|
const POSTGRES_ISSUE_CREATION_FACTORY: Readonly<Record<string, string>> = Object.freeze({
|
|
152
154
|
missing_schema: 'createSchema',
|
|
153
155
|
missing_table: 'createTable',
|
|
156
|
+
missing_rls_policy: 'createRlsPolicy',
|
|
154
157
|
});
|
|
155
158
|
|
|
156
159
|
export function resolvePostgresIssueCreationFactoryName(issue: SchemaIssue): string | undefined {
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
+
import { verifySqlSchemaTree } from '@prisma-next/family-sql/diff';
|
|
3
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
4
|
+
import type {
|
|
5
|
+
SchemaDiffIssue,
|
|
6
|
+
VerifyDatabaseSchemaResult,
|
|
7
|
+
} from '@prisma-next/framework-components/control';
|
|
8
|
+
import { diffSchemas, SchemaDiff } from '@prisma-next/framework-components/control';
|
|
9
|
+
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
10
|
+
import type { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
11
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
12
|
+
import { parsePostgresDefault } from '../default-normalizer';
|
|
13
|
+
import { normalizeSchemaNativeType } from '../native-type-normalizer';
|
|
14
|
+
import type { PostgresContract } from '../postgres-schema';
|
|
15
|
+
import { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';
|
|
16
|
+
import { PostgresPolicySchemaNode } from '../schema-ir/postgres-policy-schema-node';
|
|
17
|
+
import type { SqlSchemaDiffNode } from '../schema-ir/schema-node-kinds';
|
|
18
|
+
import { contractToPostgresDatabaseSchemaNode } from './contract-to-postgres-database-schema-node';
|
|
19
|
+
|
|
20
|
+
interface PostgresDiffDatabaseSchemaInput {
|
|
21
|
+
readonly contract: Contract<SqlStorage>;
|
|
22
|
+
readonly actualSchema: SqlSchemaIRNode;
|
|
23
|
+
readonly strict: boolean;
|
|
24
|
+
readonly typeMetadataRegistry: ReadonlyMap<string, { readonly nativeType?: string }>;
|
|
25
|
+
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The single combined database-schema comparison — the one computation the
|
|
30
|
+
* migration planner and the family schema verify both consume. Composes,
|
|
31
|
+
* once each:
|
|
32
|
+
*
|
|
33
|
+
* - the per-namespace-paired relational diff (`verifySqlSchemaTree`) → table /
|
|
34
|
+
* column / constraint findings as framework `SchemaIssue`s (with the
|
|
35
|
+
* verification-tree `root` and pass/warn/fail counts);
|
|
36
|
+
* - the policy diff (`diffPostgresSchema` over the two trees) → RLS policy
|
|
37
|
+
* presence as `SchemaDiffIssue`s, ownership-filtered to the contract's owned
|
|
38
|
+
* schemas.
|
|
39
|
+
*
|
|
40
|
+
* `diffPostgresDatabaseSchema` and `verifyPostgresDatabaseSchema` both read
|
|
41
|
+
* this single result, so the relational walk runs once per caller — never
|
|
42
|
+
* once for the diff and again for the verify tree.
|
|
43
|
+
*/
|
|
44
|
+
function computePostgresSchemaComparison(input: PostgresDiffDatabaseSchemaInput): {
|
|
45
|
+
readonly relational: VerifyDatabaseSchemaResult;
|
|
46
|
+
readonly schemaDiffIssues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[];
|
|
47
|
+
} {
|
|
48
|
+
const postgresContract = blindCast<
|
|
49
|
+
PostgresContract,
|
|
50
|
+
'diffPostgresDatabaseSchema is only called with a postgres contract'
|
|
51
|
+
>(input.contract);
|
|
52
|
+
|
|
53
|
+
// Relational diff: per-namespace-paired so a multi-schema database checks each
|
|
54
|
+
// contract namespace against its own actual node.
|
|
55
|
+
const relational = verifySqlSchemaTree({
|
|
56
|
+
contract: input.contract,
|
|
57
|
+
actualSchema: input.actualSchema,
|
|
58
|
+
buildExpectedSchema: (scopedContract) =>
|
|
59
|
+
contractToPostgresDatabaseSchemaNode(
|
|
60
|
+
blindCast<
|
|
61
|
+
PostgresContract | null,
|
|
62
|
+
'the relational pairing projects a scoped postgres contract'
|
|
63
|
+
>(scopedContract),
|
|
64
|
+
{ annotationNamespace: 'pg' },
|
|
65
|
+
),
|
|
66
|
+
strict: input.strict,
|
|
67
|
+
typeMetadataRegistry: input.typeMetadataRegistry,
|
|
68
|
+
frameworkComponents: input.frameworkComponents,
|
|
69
|
+
normalizeDefault: parsePostgresDefault,
|
|
70
|
+
normalizeNativeType: normalizeSchemaNativeType,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Policy diff: the generic node differ over the expected/actual policy trees,
|
|
74
|
+
// ownership-filtered to the schemas the contract owns (so unowned-namespace
|
|
75
|
+
// policies are not reported as extras). The actual schema is always the
|
|
76
|
+
// Postgres database root in production — assert it, matching the prior
|
|
77
|
+
// `collectSchemaDiffIssues` / `planPostgresSchemaDiff` behaviour.
|
|
78
|
+
PostgresDatabaseSchemaNode.assert(input.actualSchema);
|
|
79
|
+
const expected = contractToPostgresDatabaseSchemaNode(postgresContract, {
|
|
80
|
+
annotationNamespace: 'pg',
|
|
81
|
+
});
|
|
82
|
+
const schemaDiffIssues = filterIssuesByOwnership(
|
|
83
|
+
diffPostgresSchema(expected, input.actualSchema),
|
|
84
|
+
ownedSchemaNames(expected),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return { relational, schemaDiffIssues };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The `SchemaDiffer` for Postgres: the target's black-box comparison,
|
|
92
|
+
* projected to the two issue lists. Namespace presence (`missing_schema` →
|
|
93
|
+
* `CREATE SCHEMA`) is intentionally NOT composed here: it is a planner-only
|
|
94
|
+
* op-generation concern (verify rejects on the relational `missing_table` a
|
|
95
|
+
* missing schema already produces), so the planner stitches it in around this
|
|
96
|
+
* diff. Control-policy suppression of the policy issues is likewise a
|
|
97
|
+
* per-consumer post-step (verify filters the issues; the planner filters the
|
|
98
|
+
* calls).
|
|
99
|
+
*/
|
|
100
|
+
export function diffPostgresDatabaseSchema(
|
|
101
|
+
input: PostgresDiffDatabaseSchemaInput,
|
|
102
|
+
): SchemaDiff<SqlSchemaDiffNode> {
|
|
103
|
+
const { relational, schemaDiffIssues } = computePostgresSchemaComparison(input);
|
|
104
|
+
return new SchemaDiff(relational.schema.issues, schemaDiffIssues);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The same combined comparison as {@link diffPostgresDatabaseSchema}, wrapped
|
|
109
|
+
* in the verify envelope (`ok`/`summary`/`code`/`target`/`timings`) plus the
|
|
110
|
+
* pass/warn/fail tree the CLI renders — i.e. exactly the existing verify-result
|
|
111
|
+
* schema shape, so nothing downstream changes.
|
|
112
|
+
*/
|
|
113
|
+
export function verifyPostgresDatabaseSchema(
|
|
114
|
+
input: PostgresDiffDatabaseSchemaInput,
|
|
115
|
+
): VerifyDatabaseSchemaResult {
|
|
116
|
+
const { relational, schemaDiffIssues } = computePostgresSchemaComparison(input);
|
|
117
|
+
return {
|
|
118
|
+
...relational,
|
|
119
|
+
schema: { ...relational.schema, schemaDiffIssues },
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function ownedSchemaNames(expected: PostgresDatabaseSchemaNode): ReadonlySet<string> {
|
|
124
|
+
const policyNamespaces = Object.values(expected.namespaces).flatMap((ns) =>
|
|
125
|
+
Object.values(ns.tables).flatMap((t) => t.policies.map((p) => p.namespaceId)),
|
|
126
|
+
);
|
|
127
|
+
return new Set([...policyNamespaces, ...expected.existingSchemas]);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Renders a display-only reference string for the diff message. If policy
|
|
131
|
+
// rendering grows, route it through the adapter's SQL renderer so the message
|
|
132
|
+
// can't diverge from the emitted policy SQL.
|
|
133
|
+
function renderPostgresPolicyReference(policy: PostgresPolicySchemaNode): string {
|
|
134
|
+
return `policy "${policy.name}" on "${policy.namespaceId}"."${policy.tableName}"`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The policy node-diff — the structural half of the combined comparison above.
|
|
139
|
+
* Computes RLS-policy drift between two derived schema trees:
|
|
140
|
+
*
|
|
141
|
+
* 1. Runs the framework total diff over the two `PostgresDatabaseSchemaNode`
|
|
142
|
+
* roots (database → namespace → table → policy).
|
|
143
|
+
* 2. Filters to policy-subject issues only — this is transitional: the generic
|
|
144
|
+
* differ walks the whole tree, but the legacy relational verifier still owns
|
|
145
|
+
* table/column drift, so non-policy issues are dropped here.
|
|
146
|
+
* 3. Remaps the message to a human-readable policy reference.
|
|
147
|
+
*
|
|
148
|
+
* Both trees are `PostgresDatabaseSchemaNode`s, so every issue node is a
|
|
149
|
+
* `SqlSchemaDiffNode` — narrow the framework's `SchemaDiffIssue<DiffableNode>`
|
|
150
|
+
* output once here (the single boundary cast), so every downstream consumer
|
|
151
|
+
* (the ownership filter, the planner) reads the concrete node with no cast.
|
|
152
|
+
*
|
|
153
|
+
* Ownership filtering (dropping `extra` issues in namespaces a contract doesn't
|
|
154
|
+
* own) is the caller's responsibility — use `filterIssuesByOwnership`.
|
|
155
|
+
*/
|
|
156
|
+
export function diffPostgresSchema(
|
|
157
|
+
expected: PostgresDatabaseSchemaNode,
|
|
158
|
+
actual: PostgresDatabaseSchemaNode,
|
|
159
|
+
): readonly SchemaDiffIssue<SqlSchemaDiffNode>[] {
|
|
160
|
+
const issues = blindCast<
|
|
161
|
+
readonly SchemaDiffIssue<SqlSchemaDiffNode>[],
|
|
162
|
+
'both trees are PostgresDatabaseSchemaNodes, so every diff-issue node is a SqlSchemaDiffNode'
|
|
163
|
+
>(diffSchemas(expected, actual));
|
|
164
|
+
|
|
165
|
+
return issues
|
|
166
|
+
.filter((i) => {
|
|
167
|
+
const node = i.expected ?? i.actual;
|
|
168
|
+
return node !== undefined && PostgresPolicySchemaNode.is(node);
|
|
169
|
+
})
|
|
170
|
+
.map((i) => {
|
|
171
|
+
const node = i.expected ?? i.actual;
|
|
172
|
+
if (node === undefined || !PostgresPolicySchemaNode.is(node)) return i;
|
|
173
|
+
return { ...i, message: `${i.outcome}: ${renderPostgresPolicyReference(node)}` };
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Filters `extra` policy issues to those in owned namespaces. Call after
|
|
179
|
+
* `diffPostgresSchema` with the union of namespace ids from the expected tree's
|
|
180
|
+
* policies and its `existingSchemas`.
|
|
181
|
+
*/
|
|
182
|
+
export function filterIssuesByOwnership(
|
|
183
|
+
issues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[],
|
|
184
|
+
ownedSchemaNameSet: ReadonlySet<string>,
|
|
185
|
+
): readonly SchemaDiffIssue<SqlSchemaDiffNode>[] {
|
|
186
|
+
return issues.filter((i) => {
|
|
187
|
+
if (i.outcome !== 'extra') return true;
|
|
188
|
+
if (i.actual === undefined) return false;
|
|
189
|
+
return PostgresPolicySchemaNode.is(i.actual) && ownedSchemaNameSet.has(i.actual.namespaceId);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
SqlPlannerConflict,
|
|
16
16
|
SqlPlannerConflictLocation,
|
|
17
17
|
} from '@prisma-next/family-sql/control';
|
|
18
|
-
import { arraysEqual } from '@prisma-next/family-sql/
|
|
18
|
+
import { arraysEqual } from '@prisma-next/family-sql/diff';
|
|
19
19
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
20
20
|
import type { SchemaIssue } from '@prisma-next/framework-components/control';
|
|
21
21
|
import type {
|
|
@@ -31,6 +31,7 @@ import { blindCast } from '@prisma-next/utils/casts';
|
|
|
31
31
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
32
32
|
import type { Result } from '@prisma-next/utils/result';
|
|
33
33
|
import { notOk, ok } from '@prisma-next/utils/result';
|
|
34
|
+
import { quoteIdentifier } from '../sql-utils';
|
|
34
35
|
import {
|
|
35
36
|
AddColumnCall,
|
|
36
37
|
AddForeignKeyCall,
|
|
@@ -67,6 +68,26 @@ import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
|
67
68
|
|
|
68
69
|
export type { CallMigrationStrategy, StrategyContext };
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Deterministic name for the element-non-null CHECK constraint on a scalar-array
|
|
73
|
+
* column. Distinct `_elem_not_null` suffix avoids collision with the enum
|
|
74
|
+
* value-set `_check` constraints. Re-emitting the same schema produces the same
|
|
75
|
+
* name, so `pg_get_constraintdef`-based verify sees no drift.
|
|
76
|
+
*/
|
|
77
|
+
function elementNonNullCheckName(tableName: string, columnName: string): string {
|
|
78
|
+
return `${tableName}_${columnName}_elem_not_null`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Predicate enforcing that a scalar-array column carries no NULL element. The
|
|
83
|
+
* array column itself may be NULL (container nullability is the column's NOT NULL
|
|
84
|
+
* clause); `array_position` over a NULL array yields NULL, which a CHECK treats
|
|
85
|
+
* as satisfied, so a nullable array column is unaffected.
|
|
86
|
+
*/
|
|
87
|
+
function elementNonNullCheckExpression(columnName: string): string {
|
|
88
|
+
return `array_position(${quoteIdentifier(columnName)}, NULL) IS NULL`;
|
|
89
|
+
}
|
|
90
|
+
|
|
70
91
|
// ============================================================================
|
|
71
92
|
// Issue kind ordering (dependency order)
|
|
72
93
|
// ============================================================================
|
|
@@ -254,16 +275,28 @@ function mapIssueToCall(
|
|
|
254
275
|
);
|
|
255
276
|
}
|
|
256
277
|
const schemaForTable = tableSchema(issue);
|
|
278
|
+
const missingTableName = issue.table;
|
|
257
279
|
const ddlColumns: DdlColumn[] = Object.entries(contractTable.columns).map(([name, column]) =>
|
|
258
280
|
toDdlColumn(name, column, codecHooks, storageTypes),
|
|
259
281
|
);
|
|
260
|
-
const
|
|
282
|
+
const primaryKeyConstraints: DdlTableConstraint[] = contractTable.primaryKey
|
|
261
283
|
? [
|
|
262
284
|
contractFree.primaryKey(contractTable.primaryKey.columns, {
|
|
263
285
|
...(contractTable.primaryKey.name ? { name: contractTable.primaryKey.name } : {}),
|
|
264
286
|
}),
|
|
265
287
|
]
|
|
266
|
-
:
|
|
288
|
+
: [];
|
|
289
|
+
const elementNonNullChecks: DdlTableConstraint[] = Object.entries(contractTable.columns)
|
|
290
|
+
.filter(([, column]) => column.many === true)
|
|
291
|
+
.map(([columnName]) =>
|
|
292
|
+
contractFree.checkExpression(
|
|
293
|
+
elementNonNullCheckName(missingTableName, columnName),
|
|
294
|
+
elementNonNullCheckExpression(columnName),
|
|
295
|
+
),
|
|
296
|
+
);
|
|
297
|
+
const allTableConstraints = [...primaryKeyConstraints, ...elementNonNullChecks];
|
|
298
|
+
const ddlConstraints: DdlTableConstraint[] | undefined =
|
|
299
|
+
allTableConstraints.length > 0 ? allTableConstraints : undefined;
|
|
267
300
|
const calls: PostgresOpFactoryCall[] = [
|
|
268
301
|
new CreateTableCall(schemaForTable, issue.table, ddlColumns, ddlConstraints),
|
|
269
302
|
];
|
|
@@ -695,6 +728,8 @@ type CallCategory =
|
|
|
695
728
|
| 'dep'
|
|
696
729
|
| 'drop'
|
|
697
730
|
| 'table'
|
|
731
|
+
| 'rlsEnable'
|
|
732
|
+
| 'rlsPolicy'
|
|
698
733
|
| 'column'
|
|
699
734
|
| 'alter'
|
|
700
735
|
| 'primaryKey'
|
|
@@ -724,6 +759,12 @@ function classifyCall(call: PostgresOpFactoryCall): CallCategory {
|
|
|
724
759
|
return 'unique'; // after uniques, before indexes
|
|
725
760
|
case 'createTable':
|
|
726
761
|
return 'table';
|
|
762
|
+
case 'enableRowLevelSecurity':
|
|
763
|
+
return 'rlsEnable';
|
|
764
|
+
case 'createRlsPolicy':
|
|
765
|
+
return 'rlsPolicy';
|
|
766
|
+
case 'dropRlsPolicy':
|
|
767
|
+
return 'drop';
|
|
727
768
|
case 'addColumn':
|
|
728
769
|
return 'column';
|
|
729
770
|
case 'alterColumnType':
|
|
@@ -40,8 +40,10 @@ import { blindCast } from '@prisma-next/utils/casts';
|
|
|
40
40
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
41
41
|
import { columnExistsAst, tableExistsAst } from '../../contract-free/checks';
|
|
42
42
|
import * as contractFreeDdl from '../../contract-free/ddl';
|
|
43
|
+
import type { PostgresRlsPolicy } from '../postgres-rls-policy';
|
|
43
44
|
import { escapeLiteral, quoteIdentifier } from '../sql-utils';
|
|
44
45
|
import type { PostgresColumnDefault } from '../types';
|
|
46
|
+
import { boundSchema } from './bound-schema';
|
|
45
47
|
import {
|
|
46
48
|
addNotNullColumnDirect,
|
|
47
49
|
alterColumnType,
|
|
@@ -61,6 +63,7 @@ import {
|
|
|
61
63
|
} from './operations/constraints';
|
|
62
64
|
import { createExtension } from './operations/dependencies';
|
|
63
65
|
import { createIndex, dropIndex } from './operations/indexes';
|
|
66
|
+
import { createRlsPolicy, dropRlsPolicy, enableRowLevelSecurity } from './operations/rls';
|
|
64
67
|
import type { ForeignKeySpec } from './operations/shared';
|
|
65
68
|
import { step, targetDetails } from './operations/shared';
|
|
66
69
|
import { dropTable } from './operations/tables';
|
|
@@ -83,10 +86,6 @@ type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
|
83
86
|
// transitive package on disk.
|
|
84
87
|
const POSTGRES_MIGRATION_FACADE = '@prisma-next/postgres/migration';
|
|
85
88
|
|
|
86
|
-
function boundSchema(schemaName: string): string | undefined {
|
|
87
|
-
return schemaName === UNBOUND_NAMESPACE_ID ? undefined : schemaName;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
89
|
abstract class PostgresOpFactoryCallNode extends TsExpression implements FrameworkOpFactoryCall {
|
|
91
90
|
abstract readonly factoryName: string;
|
|
92
91
|
abstract readonly operationClass: MigrationOperationClass;
|
|
@@ -169,6 +168,8 @@ function renderDdlConstraintAsTsCall(constraint: DdlTableConstraint): string {
|
|
|
169
168
|
const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
|
|
170
169
|
return `unique(${jsonToTsSource(constraint.columns)}${nameOpt})`;
|
|
171
170
|
}
|
|
171
|
+
case 'check-expression':
|
|
172
|
+
return `checkExpression(${jsonToTsSource(constraint.name)}, ${jsonToTsSource(constraint.expression)})`;
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
|
|
@@ -183,6 +184,7 @@ function constraintImportSymbols(constraints: readonly DdlTableConstraint[] | un
|
|
|
183
184
|
if (c.kind === 'primary-key') symbols.add('primaryKey');
|
|
184
185
|
else if (c.kind === 'foreign-key') symbols.add('foreignKey');
|
|
185
186
|
else if (c.kind === 'unique') symbols.add('unique');
|
|
187
|
+
else if (c.kind === 'check-expression') symbols.add('checkExpression');
|
|
186
188
|
}
|
|
187
189
|
return [...symbols];
|
|
188
190
|
}
|
|
@@ -684,10 +686,8 @@ export class DropDefaultCall extends PostgresOpFactoryCallNode {
|
|
|
684
686
|
|
|
685
687
|
/**
|
|
686
688
|
* Planner-internal call for adding a NOT NULL column (no contract default) to
|
|
687
|
-
* a table that must be empty at migration time. Carries the
|
|
688
|
-
*
|
|
689
|
-
* DDL) plus the parameters needed to lower the typed pre/postchecks at render
|
|
690
|
-
* time.
|
|
689
|
+
* a table that must be empty at migration time. Carries the typed `DdlColumn`
|
|
690
|
+
* and lowers it to an ADD COLUMN execute step via the adapter at `toOp` time.
|
|
691
691
|
*
|
|
692
692
|
* No authored `PostgresMigration` method: this call is only emitted by the
|
|
693
693
|
* planner, never hand-written by migration authors.
|
|
@@ -698,15 +698,15 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
|
|
|
698
698
|
readonly schemaName: string;
|
|
699
699
|
readonly tableName: string;
|
|
700
700
|
readonly columnName: string;
|
|
701
|
-
readonly
|
|
701
|
+
readonly column: DdlColumn;
|
|
702
702
|
readonly label: string;
|
|
703
703
|
|
|
704
|
-
constructor(schemaName: string, tableName: string, columnName: string,
|
|
704
|
+
constructor(schemaName: string, tableName: string, columnName: string, column: DdlColumn) {
|
|
705
705
|
super();
|
|
706
706
|
this.schemaName = schemaName;
|
|
707
707
|
this.tableName = tableName;
|
|
708
708
|
this.columnName = columnName;
|
|
709
|
-
this.
|
|
709
|
+
this.column = column;
|
|
710
710
|
this.label = `Add column ${columnName} to ${tableName}`;
|
|
711
711
|
this.freeze();
|
|
712
712
|
}
|
|
@@ -717,13 +717,7 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
|
|
|
717
717
|
`AddNotNullColumnDirectCall.toOp: a lowerer is required on the Postgres planner path (column "${this.columnName}" on table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
718
718
|
);
|
|
719
719
|
}
|
|
720
|
-
return addNotNullColumnDirect(
|
|
721
|
-
this.schemaName,
|
|
722
|
-
this.tableName,
|
|
723
|
-
this.columnName,
|
|
724
|
-
this.executeStepSql,
|
|
725
|
-
lowerer,
|
|
726
|
-
);
|
|
720
|
+
return addNotNullColumnDirect(this.schemaName, this.tableName, this.column, lowerer);
|
|
727
721
|
}
|
|
728
722
|
|
|
729
723
|
renderTypeScript(): string {
|
|
@@ -738,9 +732,9 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
|
|
|
738
732
|
/**
|
|
739
733
|
* Planner-internal call for adding a NOT NULL column (no contract default)
|
|
740
734
|
* using a temporary default value for non-empty tables. Carries all parameters
|
|
741
|
-
* needed for `buildAddNotNullColumnWithTemporaryDefaultOperation`; the
|
|
742
|
-
* pre/postchecks are lowered
|
|
743
|
-
*
|
|
735
|
+
* needed for `buildAddNotNullColumnWithTemporaryDefaultOperation`; both the
|
|
736
|
+
* typed ADD COLUMN execute step and the pre/postchecks are lowered via the
|
|
737
|
+
* adapter at `toOp` time.
|
|
744
738
|
*
|
|
745
739
|
* No authored `PostgresMigration` method: this call is only emitted by the
|
|
746
740
|
* planner, never hand-written by migration authors.
|
|
@@ -1376,6 +1370,97 @@ export class DataTransformCall extends PostgresOpFactoryCallNode {
|
|
|
1376
1370
|
}
|
|
1377
1371
|
}
|
|
1378
1372
|
|
|
1373
|
+
export class CreatePostgresRlsPolicyCall extends PostgresOpFactoryCallNode {
|
|
1374
|
+
readonly factoryName = 'createRlsPolicy' as const;
|
|
1375
|
+
readonly operationClass = 'additive' as const;
|
|
1376
|
+
readonly schemaName: string;
|
|
1377
|
+
readonly tableName: string;
|
|
1378
|
+
readonly policy: PostgresRlsPolicy;
|
|
1379
|
+
readonly label: string;
|
|
1380
|
+
|
|
1381
|
+
constructor(schemaName: string, tableName: string, policy: PostgresRlsPolicy) {
|
|
1382
|
+
super();
|
|
1383
|
+
this.schemaName = schemaName;
|
|
1384
|
+
this.tableName = tableName;
|
|
1385
|
+
this.policy = policy;
|
|
1386
|
+
this.label = `Create RLS policy "${policy.name}" on "${tableName}"`;
|
|
1387
|
+
this.freeze();
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1391
|
+
if (lowerer === undefined) {
|
|
1392
|
+
throw new Error(
|
|
1393
|
+
`CreatePostgresRlsPolicyCall.toOp: a lowerer is required on the Postgres planner path (policy "${this.policy.name}" on table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
return createRlsPolicy(this.schemaName, this.tableName, this.policy, lowerer);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
renderTypeScript(): string {
|
|
1400
|
+
return `createRlsPolicy(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.policy)})`;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
export class DropPostgresRlsPolicyCall extends PostgresOpFactoryCallNode {
|
|
1405
|
+
readonly factoryName = 'dropRlsPolicy' as const;
|
|
1406
|
+
readonly operationClass = 'destructive' as const;
|
|
1407
|
+
readonly schemaName: string;
|
|
1408
|
+
readonly tableName: string;
|
|
1409
|
+
readonly policyName: string;
|
|
1410
|
+
readonly label: string;
|
|
1411
|
+
|
|
1412
|
+
constructor(schemaName: string, tableName: string, policyName: string) {
|
|
1413
|
+
super();
|
|
1414
|
+
this.schemaName = schemaName;
|
|
1415
|
+
this.tableName = tableName;
|
|
1416
|
+
this.policyName = policyName;
|
|
1417
|
+
this.label = `Drop RLS policy "${policyName}" on "${tableName}"`;
|
|
1418
|
+
this.freeze();
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1422
|
+
if (lowerer === undefined) {
|
|
1423
|
+
throw new Error(
|
|
1424
|
+
`DropPostgresRlsPolicyCall.toOp: a lowerer is required on the Postgres planner path (policy "${this.policyName}" on table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
return dropRlsPolicy(this.schemaName, this.tableName, this.policyName, lowerer);
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
renderTypeScript(): string {
|
|
1431
|
+
return `dropRlsPolicy(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.policyName)})`;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
export class EnableRowLevelSecurityCall extends PostgresOpFactoryCallNode {
|
|
1436
|
+
readonly factoryName = 'enableRowLevelSecurity' as const;
|
|
1437
|
+
readonly operationClass = 'additive' as const;
|
|
1438
|
+
readonly schemaName: string;
|
|
1439
|
+
readonly tableName: string;
|
|
1440
|
+
readonly label: string;
|
|
1441
|
+
|
|
1442
|
+
constructor(schemaName: string, tableName: string) {
|
|
1443
|
+
super();
|
|
1444
|
+
this.schemaName = schemaName;
|
|
1445
|
+
this.tableName = tableName;
|
|
1446
|
+
this.label = `Enable row-level security on "${tableName}"`;
|
|
1447
|
+
this.freeze();
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1451
|
+
if (lowerer === undefined) {
|
|
1452
|
+
throw new Error(
|
|
1453
|
+
`EnableRowLevelSecurityCall.toOp: a lowerer is required on the Postgres planner path (table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
return enableRowLevelSecurity(this.schemaName, this.tableName, lowerer);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
renderTypeScript(): string {
|
|
1460
|
+
return `enableRowLevelSecurity(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)})`;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1379
1464
|
export type PostgresOpFactoryCall =
|
|
1380
1465
|
| CreateTableCall
|
|
1381
1466
|
| DropTableCall
|
|
@@ -1399,4 +1484,7 @@ export type PostgresOpFactoryCall =
|
|
|
1399
1484
|
| RawSqlCall
|
|
1400
1485
|
| CreateExtensionCall
|
|
1401
1486
|
| CreateSchemaCall
|
|
1487
|
+
| CreatePostgresRlsPolicyCall
|
|
1488
|
+
| DropPostgresRlsPolicyCall
|
|
1489
|
+
| EnableRowLevelSecurityCall
|
|
1402
1490
|
| DataTransformCall;
|