@prisma-next/target-postgres 0.14.0-dev.5 → 0.14.0-dev.50
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-DzHOdWTl.mjs +364 -0
- package/dist/authoring-DzHOdWTl.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-ids-CnXu9Qy3.d.mts → codec-ids-BzrFF-I4.d.mts} +3 -2
- package/dist/codec-ids-BzrFF-I4.d.mts.map +1 -0
- package/dist/{codec-ids-BvytN2P8.mjs → codec-ids-C_-Hj6bL.mjs} +3 -2
- package/dist/codec-ids-C_-Hj6bL.mjs.map +1 -0
- package/dist/codec-ids.d.mts +2 -2
- package/dist/codec-ids.mjs +2 -2
- package/dist/{codec-types-DHCkwPKE.d.mts → codec-types-BDVmvKAW.d.mts} +3 -2
- package/dist/codec-types-BDVmvKAW.d.mts.map +1 -0
- package/dist/codec-types.d.mts +1 -1
- package/dist/{codecs--0A5_4Bq.d.mts → codecs-Dk2sxGLv.d.mts} +77 -4
- package/dist/codecs-Dk2sxGLv.d.mts.map +1 -0
- package/dist/codecs.d.mts +1 -1
- package/dist/codecs.mjs +117 -2
- 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 +4 -4
- package/dist/control-policy-DUFgy9jO.mjs +1103 -0
- package/dist/control-policy-DUFgy9jO.mjs.map +1 -0
- package/dist/control.d.mts +2 -2
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +757 -35
- 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-DPzwBMhW.d.mts} +3 -3
- package/dist/{data-transform-DDgWdB5o.d.mts.map → data-transform-DPzwBMhW.d.mts.map} +1 -1
- package/dist/data-transform.d.mts +1 -1
- package/dist/{ddl-QDyOSeLc.mjs → ddl-ujxH2A7r.mjs} +63 -6
- package/dist/ddl-ujxH2A7r.mjs.map +1 -0
- package/dist/ddl.d.mts +3 -2
- package/dist/ddl.mjs +2 -2
- package/dist/default-normalizer.d.mts +1 -1
- package/dist/default-normalizer.d.mts.map +1 -1
- package/dist/default-normalizer.mjs +247 -1
- package/dist/default-normalizer.mjs.map +1 -0
- package/dist/descriptor-meta-CuGDsrG4.mjs +19 -0
- package/dist/descriptor-meta-CuGDsrG4.mjs.map +1 -0
- package/dist/descriptor-meta-runtime-UwYdV8fD.mjs +12 -0
- package/dist/descriptor-meta-runtime-UwYdV8fD.mjs.map +1 -0
- package/dist/diff-database-schema-BmSmWcaY.mjs +313 -0
- package/dist/diff-database-schema-BmSmWcaY.mjs.map +1 -0
- package/dist/diff-database-schema.d.mts +46 -0
- package/dist/diff-database-schema.d.mts.map +1 -0
- package/dist/diff-database-schema.mjs +2 -0
- package/dist/issue-planner.d.mts +35 -11
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +2 -2
- package/dist/migration.d.mts +5 -5
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +4 -4
- package/dist/namespace-ids-xtp_ZY86.mjs +7 -0
- package/dist/namespace-ids-xtp_ZY86.mjs.map +1 -0
- package/dist/native-type-normalizer.mjs +37 -1
- package/dist/native-type-normalizer.mjs.map +1 -0
- 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-D_p5vxwt.mjs → op-factory-call-BKRnMgrB.mjs} +205 -31
- package/dist/op-factory-call-BKRnMgrB.mjs.map +1 -0
- package/dist/{op-factory-call-DmQEc3XV.d.mts → op-factory-call-Dt-Jx5JS.d.mts} +46 -15
- package/dist/op-factory-call-Dt-Jx5JS.d.mts.map +1 -0
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/pack.d.mts +115 -3
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/planner-BMBRPzsv.mjs +367 -0
- package/dist/planner-BMBRPzsv.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 +4 -6
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +2 -2
- package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -1
- package/dist/planner-identity-values.d.mts +8 -2
- package/dist/planner-identity-values.d.mts.map +1 -1
- package/dist/planner-identity-values.mjs +2 -2
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts → planner-produced-postgres-migration-C0a7I1-e.d.mts} +4 -4
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts.map → planner-produced-postgres-migration-C0a7I1-e.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs → planner-produced-postgres-migration-Dp6IO_gj.mjs} +3 -3
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs.map → planner-produced-postgres-migration-Dp6IO_gj.mjs.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-Cze3vMfe.mjs} +2 -2
- package/dist/{planner-sql-checks-jqUUGyQR.mjs.map → planner-sql-checks-Cze3vMfe.mjs.map} +1 -1
- package/dist/planner-sql-checks.d.mts +2 -2
- package/dist/planner-sql-checks.d.mts.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 +55 -17
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +3 -2
- package/dist/{postgres-contract-serializer-k3TAcPMY.mjs → postgres-contract-view-3xfUgjnC.mjs} +44 -41
- package/dist/postgres-contract-view-3xfUgjnC.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-C9bTuMrd.d.mts +190 -0
- package/dist/postgres-database-schema-node-C9bTuMrd.d.mts.map +1 -0
- package/dist/{postgres-migration-B5jKrXv3.mjs → postgres-migration-D0jwzYDT.mjs} +21 -2
- package/dist/postgres-migration-D0jwzYDT.mjs.map +1 -0
- package/dist/{postgres-migration-Y4YBJqkS.d.mts → postgres-migration-DFaxuRyI.d.mts} +20 -7
- package/dist/postgres-migration-DFaxuRyI.d.mts.map +1 -0
- package/dist/postgres-native-enum-xdiqmBf_.mjs +41 -0
- package/dist/postgres-native-enum-xdiqmBf_.mjs.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-CajYAwny.mjs} +135 -7
- package/dist/postgres-schema-CajYAwny.mjs.map +1 -0
- package/dist/postgres-schema-DqnhQRvR.d.mts +226 -0
- package/dist/postgres-schema-DqnhQRvR.d.mts.map +1 -0
- package/dist/postgres-table-schema-node-BgaSrxYN.mjs +249 -0
- package/dist/postgres-table-schema-node-BgaSrxYN.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-Bv1ZvjX8.mjs +22 -0
- package/dist/resolve-ddl-schema-Bv1ZvjX8.mjs.map +1 -0
- package/dist/rls-canonicalize.d.mts +2 -0
- package/dist/rls-canonicalize.mjs +2 -0
- package/dist/runtime.d.mts +6 -8
- 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 +15 -0
- package/dist/schema-ir-annotations.d.mts.map +1 -0
- package/dist/schema-ir-annotations.mjs +2 -0
- package/dist/schema-node-kinds-ClScchhi.mjs +76 -0
- package/dist/schema-node-kinds-ClScchhi.mjs.map +1 -0
- package/dist/types.d.mts +4 -140
- package/dist/types.mjs +3 -2
- package/package.json +23 -17
- package/src/contract-free/checks.ts +73 -0
- package/src/contract-free/ddl.ts +45 -1
- package/src/core/authoring.ts +287 -2
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codec-type-map.ts +2 -0
- package/src/core/codecs.ts +141 -1
- package/src/core/ddl/nodes.ts +94 -2
- package/src/core/default-normalizer.ts +122 -2
- package/src/core/descriptor-meta.ts +6 -1
- package/src/core/entity-kinds.ts +30 -0
- package/src/core/migrations/bound-schema.ts +10 -0
- package/src/core/migrations/column-ddl-rendering.ts +117 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +149 -0
- package/src/core/migrations/control-policy.ts +70 -47
- package/src/core/migrations/diff-database-schema.ts +280 -0
- package/src/core/migrations/issue-planner.ts +647 -652
- package/src/core/migrations/op-factory-call.ts +109 -21
- package/src/core/migrations/operations/columns.ts +24 -12
- package/src/core/migrations/operations/constraints.ts +1 -1
- package/src/core/migrations/operations/data-transform.ts +6 -0
- package/src/core/migrations/operations/dependencies.ts +2 -1
- 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 +20 -58
- package/src/core/migrations/planner-identity-values.ts +1 -1
- package/src/core/migrations/planner-recipes.ts +31 -13
- package/src/core/migrations/planner-sql-checks.ts +1 -1
- package/src/core/migrations/planner-strategies.ts +248 -216
- package/src/core/migrations/planner-target-details.ts +3 -1
- package/src/core/migrations/planner.ts +297 -59
- 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 +26 -0
- package/src/core/migrations/runner.ts +10 -16
- package/src/core/migrations/verify-postgres-namespaces.ts +47 -30
- package/src/core/namespace-ids.ts +3 -0
- package/src/core/postgres-contract-serializer.ts +41 -71
- package/src/core/postgres-contract-view.ts +48 -0
- package/src/core/postgres-native-enum.ts +51 -0
- package/src/core/postgres-rls-policy.ts +64 -0
- package/src/core/postgres-role.ts +37 -0
- package/src/core/postgres-schema-verifier.ts +10 -7
- package/src/core/postgres-schema.ts +62 -14
- package/src/core/postgres-validators.ts +27 -0
- package/src/core/psl-infer/infer-psl-contract.ts +1068 -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 +60 -0
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +60 -0
- package/src/core/schema-ir/postgres-policy-schema-node.ts +90 -0
- package/src/core/schema-ir/postgres-role-schema-node.ts +63 -0
- package/src/core/schema-ir/postgres-table-schema-node.ts +118 -0
- package/src/core/schema-ir/schema-node-kinds.ts +106 -0
- package/src/exports/contract-free.ts +7 -1
- package/src/exports/control.ts +27 -33
- package/src/exports/ddl.ts +4 -0
- package/src/exports/diff-database-schema.ts +7 -0
- package/src/exports/issue-planner.ts +1 -1
- package/src/exports/migration.ts +1 -0
- package/src/exports/op-factory-call.ts +1 -0
- package/src/exports/planner-ddl-builders.ts +0 -2
- package/src/exports/planner-identity-values.ts +4 -1
- package/src/exports/planner.ts +1 -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/codec-ids-BvytN2P8.mjs.map +0 -1
- package/dist/codec-ids-CnXu9Qy3.d.mts.map +0 -1
- package/dist/codec-types-DHCkwPKE.d.mts.map +0 -1
- package/dist/codecs--0A5_4Bq.d.mts.map +0 -1
- package/dist/ddl-QDyOSeLc.mjs.map +0 -1
- package/dist/default-normalizer-DyyCHQWs.mjs +0 -131
- 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 +0 -824
- package/dist/issue-planner-DL6g3CmE.mjs.map +0 -1
- package/dist/native-type-normalizer-Bc9XJzWC.mjs +0 -38
- package/dist/native-type-normalizer-Bc9XJzWC.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
- package/src/core/postgres-enum-type-schema.ts +0 -17
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
2
|
import type { ControlPolicySubject } from '@prisma-next/family-sql/control';
|
|
3
|
-
import type {
|
|
3
|
+
import type { SchemaDiffIssue } from '@prisma-next/framework-components/control';
|
|
4
4
|
import { entityAt, UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
5
5
|
import type { SqlStorage, StorageTable } from '@prisma-next/sql-contract/types';
|
|
6
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
6
7
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
7
8
|
import { isPostgresSchema } from '../postgres-schema';
|
|
9
|
+
import type { PostgresNamespaceSchemaNode } from '../schema-ir/postgres-namespace-schema-node';
|
|
10
|
+
import type { SqlSchemaDiffNode } from '../schema-ir/schema-node-kinds';
|
|
11
|
+
import { PostgresSchemaNodeKind } from '../schema-ir/schema-node-kinds';
|
|
12
|
+
import { issueColumnName } from './issue-planner';
|
|
8
13
|
import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
9
14
|
|
|
10
15
|
/**
|
|
@@ -21,6 +26,8 @@ import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
|
21
26
|
const OBJECT_CREATION_FACTORIES: ReadonlySet<string> = new Set<string>([
|
|
22
27
|
'createTable',
|
|
23
28
|
'createSchema',
|
|
29
|
+
'createRlsPolicy',
|
|
30
|
+
'enableRowLevelSecurity',
|
|
24
31
|
]);
|
|
25
32
|
|
|
26
33
|
function createsNewTopLevelObject(call: PostgresOpFactoryCall): boolean {
|
|
@@ -54,7 +61,7 @@ function resolveNamespaceIdForTable(
|
|
|
54
61
|
return UNBOUND_NAMESPACE_ID;
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
function resolveNamespaceIdForDdlSchema(
|
|
64
|
+
export function resolveNamespaceIdForDdlSchema(
|
|
58
65
|
contract: Contract<SqlStorage>,
|
|
59
66
|
ddlSchemaName: string,
|
|
60
67
|
): string {
|
|
@@ -141,64 +148,80 @@ export function resolvePostgresCallControlPolicySubject(
|
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
/**
|
|
144
|
-
*
|
|
145
|
-
* object — the same
|
|
146
|
-
* Used by {@link
|
|
147
|
-
* a `tolerated` subject permits the issue to flow into the planner
|
|
151
|
+
* Node kinds that describe the absence of a whole, top-level Postgres
|
|
152
|
+
* object — the same objects `createsNewTopLevelObject` recognises for calls.
|
|
153
|
+
* Used by {@link resolvePostgresNodeIssueCreationFactoryName} to decide
|
|
154
|
+
* whether a `tolerated` subject permits the issue to flow into the planner
|
|
148
155
|
* (create-if-absent) and to seed the suppressed-subject warning's
|
|
149
|
-
* `factoryName` when the planner is skipped.
|
|
156
|
+
* `factoryName` when the planner is skipped. RLS policy creation is not
|
|
157
|
+
* listed here — policy issues never reach this issue-based partition (they
|
|
158
|
+
* are routed to `planPostgresSchemaDiff` and gated via the call-based
|
|
159
|
+
* {@link resolvePostgresCallControlPolicySubject} instead).
|
|
150
160
|
*/
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
161
|
+
const POSTGRES_NODE_CREATION_FACTORY: Readonly<Record<string, string>> = Object.freeze({
|
|
162
|
+
[PostgresSchemaNodeKind.namespace]: 'createSchema',
|
|
163
|
+
[PostgresSchemaNodeKind.table]: 'createTable',
|
|
154
164
|
});
|
|
155
165
|
|
|
156
|
-
export function
|
|
157
|
-
|
|
166
|
+
export function resolvePostgresNodeIssueCreationFactoryName(
|
|
167
|
+
issue: SchemaDiffIssue<SqlSchemaDiffNode>,
|
|
168
|
+
): string | undefined {
|
|
169
|
+
if (issue.reason !== 'not-found') return undefined;
|
|
170
|
+
const node = issue.expected ?? issue.actual;
|
|
171
|
+
if (node === undefined) return undefined;
|
|
172
|
+
return POSTGRES_NODE_CREATION_FACTORY[node.nodeKind];
|
|
158
173
|
}
|
|
159
174
|
|
|
160
175
|
/**
|
|
161
|
-
* Resolve the control-policy subject coordinate for a single
|
|
162
|
-
* {@link
|
|
163
|
-
* performs for a generated DDL
|
|
164
|
-
*
|
|
165
|
-
* runs. `createsNewObject` is
|
|
166
|
-
*
|
|
167
|
-
* touches an existing object.
|
|
176
|
+
* Resolve the control-policy subject coordinate for a single node-typed
|
|
177
|
+
* {@link SchemaDiffIssue}. Mirrors the resolution
|
|
178
|
+
* `resolvePostgresCallControlPolicySubject` performs for a generated DDL
|
|
179
|
+
* call, but works *off the issue* — so the planner can partition issues by
|
|
180
|
+
* effective policy before the diff engine runs. `createsNewObject` is
|
|
181
|
+
* derived from the node kind + reason: a `not-found` schema/table describes
|
|
182
|
+
* a brand-new top-level object; everything else touches an existing object.
|
|
168
183
|
*
|
|
169
|
-
*
|
|
170
|
-
* isn't
|
|
171
|
-
* back to {@link UNBOUND_NAMESPACE_ID}
|
|
172
|
-
*
|
|
184
|
+
* A `not-expected` table carries no contract namespace coordinate (the live
|
|
185
|
+
* table isn't claimed by any contract namespace), so the subject's
|
|
186
|
+
* `namespaceId` falls back to {@link UNBOUND_NAMESPACE_ID} via
|
|
187
|
+
* `resolveNamespaceIdForTable`'s own fallback — the call-side resolver does
|
|
188
|
+
* the same for the `DropTableCall` it produces.
|
|
173
189
|
*/
|
|
174
|
-
export function
|
|
175
|
-
issue:
|
|
190
|
+
export function resolvePostgresNodeIssueControlPolicySubject(
|
|
191
|
+
issue: SchemaDiffIssue<SqlSchemaDiffNode>,
|
|
176
192
|
contract: Contract<SqlStorage>,
|
|
177
193
|
): ControlPolicySubject | undefined {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
'namespaceId' in issue && issue.namespaceId
|
|
187
|
-
? issue.namespaceId
|
|
188
|
-
: resolveNamespaceIdForTable(contract, issue.table, undefined);
|
|
189
|
-
const table = entityAt<StorageTable>(contract.storage, {
|
|
190
|
-
namespaceId,
|
|
191
|
-
entityKind: 'table',
|
|
192
|
-
entityName: issue.table,
|
|
193
|
-
});
|
|
194
|
+
const node = issue.expected ?? issue.actual;
|
|
195
|
+
if (node === undefined) return undefined;
|
|
196
|
+
|
|
197
|
+
if (node.nodeKind === PostgresSchemaNodeKind.namespace) {
|
|
198
|
+
const namespaceNode = blindCast<
|
|
199
|
+
PostgresNamespaceSchemaNode,
|
|
200
|
+
'a postgres-namespace diff node is always a PostgresNamespaceSchemaNode'
|
|
201
|
+
>(node);
|
|
194
202
|
return {
|
|
195
|
-
namespaceId,
|
|
196
|
-
|
|
197
|
-
table: issue.table,
|
|
198
|
-
...ifDefined('column', 'column' in issue ? issue.column : undefined),
|
|
199
|
-
createsNewObject,
|
|
203
|
+
namespaceId: resolveNamespaceIdForDdlSchema(contract, namespaceNode.schemaName),
|
|
204
|
+
createsNewObject: issue.reason === 'not-found',
|
|
200
205
|
};
|
|
201
206
|
}
|
|
202
207
|
|
|
203
|
-
|
|
208
|
+
const tableName = issue.path[2];
|
|
209
|
+
if (tableName === undefined) return undefined;
|
|
210
|
+
const ddlSchemaName = issue.path[1];
|
|
211
|
+
const namespaceId = resolveNamespaceIdForTable(contract, tableName, ddlSchemaName);
|
|
212
|
+
const table = entityAt<StorageTable>(contract.storage, {
|
|
213
|
+
namespaceId,
|
|
214
|
+
entityKind: 'table',
|
|
215
|
+
entityName: tableName,
|
|
216
|
+
});
|
|
217
|
+
const createsNewObject =
|
|
218
|
+
node.nodeKind === PostgresSchemaNodeKind.table && issue.reason === 'not-found';
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
namespaceId,
|
|
222
|
+
...ifDefined('explicitNodeControlPolicy', table?.control),
|
|
223
|
+
table: tableName,
|
|
224
|
+
...ifDefined('column', issueColumnName(issue)),
|
|
225
|
+
createsNewObject,
|
|
226
|
+
};
|
|
204
227
|
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type { Contract, ControlPolicy } from '@prisma-next/contract/types';
|
|
2
|
+
import type { SqlSchemaDiffResult } from '@prisma-next/family-sql/control';
|
|
3
|
+
import { buildNativeTypeExpander } from '@prisma-next/family-sql/control';
|
|
4
|
+
import {
|
|
5
|
+
classifyDiffSubjectGranularity,
|
|
6
|
+
resolveSemanticSatisfaction,
|
|
7
|
+
} from '@prisma-next/family-sql/diff';
|
|
8
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
9
|
+
import type { SchemaDiffIssue } from '@prisma-next/framework-components/control';
|
|
10
|
+
import { diffSchemas } from '@prisma-next/framework-components/control';
|
|
11
|
+
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
12
|
+
import type { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
13
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
14
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
15
|
+
import type { PostgresContract } from '../postgres-schema';
|
|
16
|
+
import { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';
|
|
17
|
+
import { PostgresNamespaceSchemaNode } from '../schema-ir/postgres-namespace-schema-node';
|
|
18
|
+
import { PostgresTableSchemaNode } from '../schema-ir/postgres-table-schema-node';
|
|
19
|
+
import {
|
|
20
|
+
postgresDiffSubjectGranularity,
|
|
21
|
+
type SqlSchemaDiffNode,
|
|
22
|
+
} from '../schema-ir/schema-node-kinds';
|
|
23
|
+
import { contractToPostgresDatabaseSchemaNode } from './contract-to-postgres-database-schema-node';
|
|
24
|
+
import { resolvePostgresNodeIssueControlPolicySubject } from './control-policy';
|
|
25
|
+
|
|
26
|
+
function ownedSchemaNames(expected: PostgresDatabaseSchemaNode): ReadonlySet<string> {
|
|
27
|
+
const policyNamespaces = Object.values(expected.namespaces).flatMap((ns) =>
|
|
28
|
+
Object.values(ns.tables).flatMap((t) => t.policies.map((p) => p.namespaceId)),
|
|
29
|
+
);
|
|
30
|
+
return new Set([...policyNamespaces, ...expected.existingSchemas]);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Applies the family's semantic-satisfaction normalization across a Postgres
|
|
35
|
+
* tree pair: every actual table with an expected counterpart (paired by
|
|
36
|
+
* namespace id, then table id) gets its unique/index child lists adjusted;
|
|
37
|
+
* everything else passes through untouched.
|
|
38
|
+
*/
|
|
39
|
+
export function normalizePostgresActualForDiff(
|
|
40
|
+
expected: PostgresDatabaseSchemaNode,
|
|
41
|
+
actual: PostgresDatabaseSchemaNode,
|
|
42
|
+
): PostgresDatabaseSchemaNode {
|
|
43
|
+
const namespaces: Record<string, PostgresNamespaceSchemaNode> = {};
|
|
44
|
+
for (const [nsId, actualNs] of Object.entries(actual.namespaces)) {
|
|
45
|
+
const expectedNs = expected.namespaces[nsId];
|
|
46
|
+
if (expectedNs === undefined) {
|
|
47
|
+
namespaces[nsId] = actualNs;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const tables: Record<string, PostgresTableSchemaNode> = {};
|
|
51
|
+
for (const [tableName, actualTable] of Object.entries(actualNs.tables)) {
|
|
52
|
+
const expectedTable = expectedNs.tables[tableName];
|
|
53
|
+
if (expectedTable === undefined) {
|
|
54
|
+
tables[tableName] = actualTable;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const adjusted = resolveSemanticSatisfaction({
|
|
58
|
+
expectedUniques: expectedTable.uniques,
|
|
59
|
+
expectedIndexes: expectedTable.indexes,
|
|
60
|
+
actualUniques: actualTable.uniques,
|
|
61
|
+
actualIndexes: actualTable.indexes,
|
|
62
|
+
});
|
|
63
|
+
tables[tableName] = new PostgresTableSchemaNode({
|
|
64
|
+
name: actualTable.name,
|
|
65
|
+
columns: actualTable.columns,
|
|
66
|
+
foreignKeys: actualTable.foreignKeys,
|
|
67
|
+
uniques: adjusted.actualUniques,
|
|
68
|
+
indexes: adjusted.actualIndexes,
|
|
69
|
+
...ifDefined('primaryKey', actualTable.primaryKey),
|
|
70
|
+
...ifDefined('annotations', actualTable.annotations),
|
|
71
|
+
...ifDefined('checks', actualTable.checks),
|
|
72
|
+
policies: [...actualTable.policies],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
namespaces[nsId] = new PostgresNamespaceSchemaNode({
|
|
76
|
+
schemaName: actualNs.schemaName,
|
|
77
|
+
tables,
|
|
78
|
+
nativeEnumTypeNames: actualNs.nativeEnumTypeNames,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return new PostgresDatabaseSchemaNode({
|
|
82
|
+
namespaces,
|
|
83
|
+
roles: [...actual.roles],
|
|
84
|
+
existingSchemas: [...actual.existingSchemas],
|
|
85
|
+
pgVersion: actual.pgVersion,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Drops contract namespaces that declare no tables from the verdict-diff
|
|
91
|
+
* expected tree and the relational owned-schema set. The legacy relational
|
|
92
|
+
* walk skipped a table-less namespace (e.g. an enums-only schema) before
|
|
93
|
+
* pairing, so neither its DDL schema's absence nor that schema's live
|
|
94
|
+
* relational contents ever reached the verdict — the pruned tree
|
|
95
|
+
* reproduces that. The prune loses no expected policies: policies attach
|
|
96
|
+
* to tables, so a table-less namespace carries none (the projection
|
|
97
|
+
* throws on a policy referencing an absent table). Live policies in a
|
|
98
|
+
* pruned schema remain governed via the full owned set (see
|
|
99
|
+
* {@link diffPostgresSchema}).
|
|
100
|
+
*/
|
|
101
|
+
function pruneTableLessNamespaces(
|
|
102
|
+
expected: PostgresDatabaseSchemaNode,
|
|
103
|
+
): PostgresDatabaseSchemaNode {
|
|
104
|
+
const namespaces = Object.fromEntries(
|
|
105
|
+
Object.entries(expected.namespaces).filter(([, ns]) => Object.keys(ns.tables).length > 0),
|
|
106
|
+
);
|
|
107
|
+
return new PostgresDatabaseSchemaNode({
|
|
108
|
+
namespaces,
|
|
109
|
+
roles: [...expected.roles],
|
|
110
|
+
existingSchemas: expected.existingSchemas.filter((s) => namespaces[s] !== undefined),
|
|
111
|
+
pgVersion: expected.pgVersion,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Resolves a verdict-diff issue's subject table's declared control policy
|
|
117
|
+
* directly from the contract, by delegating to the same node-typed resolver
|
|
118
|
+
* ({@link resolvePostgresNodeIssueControlPolicySubject}) the planner uses to
|
|
119
|
+
* gate DDL calls. `undefined` when the issue resolves to no contract table.
|
|
120
|
+
*/
|
|
121
|
+
function resolveControlPolicy(
|
|
122
|
+
issue: SchemaDiffIssue,
|
|
123
|
+
contract: Contract<SqlStorage>,
|
|
124
|
+
): ControlPolicy | undefined {
|
|
125
|
+
const nodeIssue = blindCast<
|
|
126
|
+
SchemaDiffIssue<SqlSchemaDiffNode>,
|
|
127
|
+
'every node in a Postgres schema diff tree is a SqlSchemaDiffNode'
|
|
128
|
+
>(issue);
|
|
129
|
+
return resolvePostgresNodeIssueControlPolicySubject(nodeIssue, contract)
|
|
130
|
+
?.explicitNodeControlPolicy;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The Postgres full-tree node diff for the family verify verdict: derive
|
|
135
|
+
* the expected tree (resolved leaf values, expander threaded, FK schemas
|
|
136
|
+
* resolved, table-less namespaces pruned), normalize the actual tree for
|
|
137
|
+
* semantic satisfaction, run the generic
|
|
138
|
+
* differ, and scope out `not-expected` findings under namespaces the
|
|
139
|
+
* contract does not own. Ownership is role-aware, mirroring the legacy
|
|
140
|
+
* decomposition: relational extras check the PRUNED owned set (the legacy
|
|
141
|
+
* per-namespace walk never visited a table-less namespace, so its live
|
|
142
|
+
* relational contents are invisible), while `structural` extras (RLS
|
|
143
|
+
* policies) check the FULL owned set (the legacy policy diff governed
|
|
144
|
+
* every contract schema regardless of tables — RLS governance does not
|
|
145
|
+
* shrink because a namespace declares no tables). The codec `verifyType`
|
|
146
|
+
* hooks run once per contract namespace with tables against that
|
|
147
|
+
* namespace's paired actual node (the hooks read namespace-scoped state
|
|
148
|
+
* such as `nativeEnumTypeNames`).
|
|
149
|
+
*/
|
|
150
|
+
export function diffPostgresSchema(input: {
|
|
151
|
+
readonly contract: Contract<SqlStorage>;
|
|
152
|
+
readonly schema: SqlSchemaIRNode;
|
|
153
|
+
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
154
|
+
}): SqlSchemaDiffResult {
|
|
155
|
+
const postgresContract = blindCast<
|
|
156
|
+
PostgresContract,
|
|
157
|
+
'diffPostgresSchema is only called with a postgres contract'
|
|
158
|
+
>(input.contract);
|
|
159
|
+
PostgresDatabaseSchemaNode.assert(input.schema);
|
|
160
|
+
const actual = input.schema;
|
|
161
|
+
const expandNativeType = buildNativeTypeExpander(input.frameworkComponents);
|
|
162
|
+
const fullExpected = contractToPostgresDatabaseSchemaNode(postgresContract, {
|
|
163
|
+
annotationNamespace: 'pg',
|
|
164
|
+
...ifDefined('expandNativeType', expandNativeType),
|
|
165
|
+
});
|
|
166
|
+
const expected = pruneTableLessNamespaces(fullExpected);
|
|
167
|
+
const normalizedActual = normalizePostgresActualForDiff(expected, actual);
|
|
168
|
+
const relationalOwned = ownedSchemaNames(expected);
|
|
169
|
+
const structuralOwned = ownedSchemaNames(fullExpected);
|
|
170
|
+
const issues = diffSchemas(expected, normalizedActual).filter((issue) => {
|
|
171
|
+
if (issue.reason !== 'not-expected') return true;
|
|
172
|
+
const namespaceSegment = issue.path[1];
|
|
173
|
+
if (namespaceSegment === undefined) return true;
|
|
174
|
+
const granularity = classifyDiffSubjectGranularity(issue, postgresDiffSubjectGranularity);
|
|
175
|
+
const owned = granularity === 'structural' ? structuralOwned : relationalOwned;
|
|
176
|
+
return owned.has(namespaceSegment);
|
|
177
|
+
});
|
|
178
|
+
const namespacePairs = Object.values(expected.namespaces).map((ns) => ({
|
|
179
|
+
actual: actual.namespaces[ns.schemaName],
|
|
180
|
+
}));
|
|
181
|
+
return {
|
|
182
|
+
issues,
|
|
183
|
+
resolveControlPolicy: (issue) => resolveControlPolicy(issue, postgresContract),
|
|
184
|
+
namespacePairs,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Adds an empty namespace node to the actual tree for every expected namespace
|
|
190
|
+
* absent from it. The relational plan diff pairs on namespace: a contract
|
|
191
|
+
* namespace whose live schema does not exist yet must surface each of its
|
|
192
|
+
* tables as `not-found` (→ `CREATE TABLE`), NOT as a single namespace
|
|
193
|
+
* `not-found` that subtree-coalescing would collapse (leaving `CREATE SCHEMA`
|
|
194
|
+
* with no tables). Padding makes the namespaces pair, so only table/column/
|
|
195
|
+
* policy drift surfaces; `CREATE SCHEMA` comes separately from the synthesized
|
|
196
|
+
* namespace-presence stitch (`verifyPostgresNamespacePresence`), never from the
|
|
197
|
+
* tree diff — matching the retired per-namespace-paired relational walk, which
|
|
198
|
+
* paired a missing schema against an empty namespace node.
|
|
199
|
+
*/
|
|
200
|
+
function padActualNamespaces(
|
|
201
|
+
expected: PostgresDatabaseSchemaNode,
|
|
202
|
+
actual: PostgresDatabaseSchemaNode,
|
|
203
|
+
): PostgresDatabaseSchemaNode {
|
|
204
|
+
const namespaces: Record<string, PostgresNamespaceSchemaNode> = { ...actual.namespaces };
|
|
205
|
+
let padded = false;
|
|
206
|
+
for (const schemaName of Object.keys(expected.namespaces)) {
|
|
207
|
+
if (namespaces[schemaName] === undefined) {
|
|
208
|
+
namespaces[schemaName] = new PostgresNamespaceSchemaNode({
|
|
209
|
+
schemaName,
|
|
210
|
+
tables: {},
|
|
211
|
+
nativeEnumTypeNames: [],
|
|
212
|
+
});
|
|
213
|
+
padded = true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!padded) return actual;
|
|
217
|
+
return new PostgresDatabaseSchemaNode({
|
|
218
|
+
namespaces,
|
|
219
|
+
roles: [...actual.roles],
|
|
220
|
+
existingSchemas: [...actual.existingSchemas],
|
|
221
|
+
pgVersion: actual.pgVersion,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface PostgresPlanDiff {
|
|
226
|
+
/** The desired ("end") tree — resolved leaf values (incl. `codecRef`) on every column, table-less namespaces pruned. */
|
|
227
|
+
readonly expected: PostgresDatabaseSchemaNode;
|
|
228
|
+
/** The live ("start") tree, padded with empty namespaces and normalized for semantic satisfaction against `expected`. */
|
|
229
|
+
readonly actual: PostgresDatabaseSchemaNode;
|
|
230
|
+
/** The one node diff over the two trees: relational + policy drift, role-aware ownership filtered. */
|
|
231
|
+
readonly issues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The Postgres planner's diff input: the SAME tree-building
|
|
236
|
+
* `diffPostgresSchema` uses (expander threaded, FK schemas resolved,
|
|
237
|
+
* table-less namespaces pruned, actual normalized for semantic satisfaction,
|
|
238
|
+
* role-aware ownership filter) plus actual namespace padding (so a missing
|
|
239
|
+
* schema's tables surface as `not-found` instead of a swallowed namespace
|
|
240
|
+
* `not-found`). One differ drives both verify and plan; this is the
|
|
241
|
+
* plan-side derivation. The single issue list covers tables / columns /
|
|
242
|
+
* constraints / indexes / defaults AND policies — the caller splits it
|
|
243
|
+
* (relational → `mapNodeIssueToCall`; policy → RLS ops) and stitches in
|
|
244
|
+
* `CREATE SCHEMA` separately.
|
|
245
|
+
*/
|
|
246
|
+
export function buildPostgresPlanDiff(input: {
|
|
247
|
+
readonly contract: Contract<SqlStorage>;
|
|
248
|
+
readonly actualSchema: SqlSchemaIRNode;
|
|
249
|
+
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
250
|
+
}): PostgresPlanDiff {
|
|
251
|
+
const postgresContract = blindCast<
|
|
252
|
+
PostgresContract,
|
|
253
|
+
'buildPostgresPlanDiff is only called with a postgres contract'
|
|
254
|
+
>(input.contract);
|
|
255
|
+
PostgresDatabaseSchemaNode.assert(input.actualSchema);
|
|
256
|
+
const actual = input.actualSchema;
|
|
257
|
+
const expandNativeType = buildNativeTypeExpander(input.frameworkComponents);
|
|
258
|
+
const projectionOptions = {
|
|
259
|
+
annotationNamespace: 'pg',
|
|
260
|
+
...ifDefined('expandNativeType', expandNativeType),
|
|
261
|
+
};
|
|
262
|
+
const fullExpected = contractToPostgresDatabaseSchemaNode(postgresContract, projectionOptions);
|
|
263
|
+
const expected = pruneTableLessNamespaces(fullExpected);
|
|
264
|
+
const paddedActual = padActualNamespaces(expected, actual);
|
|
265
|
+
const normalizedActual = normalizePostgresActualForDiff(expected, paddedActual);
|
|
266
|
+
const relationalOwned = ownedSchemaNames(expected);
|
|
267
|
+
const structuralOwned = ownedSchemaNames(fullExpected);
|
|
268
|
+
const issues = blindCast<
|
|
269
|
+
readonly SchemaDiffIssue<SqlSchemaDiffNode>[],
|
|
270
|
+
'both trees are PostgresDatabaseSchemaNodes, so every diff-issue node is a SqlSchemaDiffNode'
|
|
271
|
+
>(diffSchemas(expected, normalizedActual)).filter((issue) => {
|
|
272
|
+
if (issue.reason !== 'not-expected') return true;
|
|
273
|
+
const namespaceSegment = issue.path[1];
|
|
274
|
+
if (namespaceSegment === undefined) return true;
|
|
275
|
+
const granularity = classifyDiffSubjectGranularity(issue, postgresDiffSubjectGranularity);
|
|
276
|
+
const owned = granularity === 'structural' ? structuralOwned : relationalOwned;
|
|
277
|
+
return owned.has(namespaceSegment);
|
|
278
|
+
});
|
|
279
|
+
return { expected, actual: normalizedActual, issues };
|
|
280
|
+
}
|