@prisma-next/target-postgres 0.14.0-dev.6 → 0.14.0-dev.61
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-BLkL4fFG.mjs +528 -0
- package/dist/authoring-BLkL4fFG.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-BiIWziRO.d.mts} +3 -2
- package/dist/codec-types-BiIWziRO.d.mts.map +1 -0
- package/dist/codec-types.d.mts +1 -1
- package/dist/codecs-CkVQrZ4G.mjs +913 -0
- package/dist/codecs-CkVQrZ4G.mjs.map +1 -0
- package/dist/{codecs--0A5_4Bq.d.mts → codecs-Cm-nS4mQ.d.mts} +111 -4
- package/dist/codecs-Cm-nS4mQ.d.mts.map +1 -0
- package/dist/codecs.d.mts +2 -2
- package/dist/codecs.mjs +3 -757
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.d.mts +33 -3
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +4 -4
- package/dist/control-policy-BpmChlAK.mjs +1151 -0
- package/dist/control-policy-BpmChlAK.mjs.map +1 -0
- package/dist/control.d.mts +2 -2
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +901 -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-BovUVFVi.mjs} +77 -6
- package/dist/ddl-BovUVFVi.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-C1T4BXJ8.mjs +22 -0
- package/dist/descriptor-meta-C1T4BXJ8.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-RMwnSOnz.mjs +298 -0
- package/dist/diff-database-schema-RMwnSOnz.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/errors-DO-w0EUS.mjs +35 -0
- package/dist/errors-DO-w0EUS.mjs.map +1 -0
- package/dist/errors.d.mts +11 -9
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +1 -1
- 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-B815pj_X.mjs +162 -0
- package/dist/nodes-B815pj_X.mjs.map +1 -0
- package/dist/nodes-BtQyZha6.d.mts +146 -0
- package/dist/nodes-BtQyZha6.d.mts.map +1 -0
- package/dist/{op-factory-call-D_p5vxwt.mjs → op-factory-call-CI00nS4z.mjs} +300 -31
- package/dist/op-factory-call-CI00nS4z.mjs.map +1 -0
- package/dist/{op-factory-call-DmQEc3XV.d.mts → op-factory-call-D6_7-7Vc.d.mts} +68 -15
- package/dist/op-factory-call-D6_7-7Vc.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 +287 -3
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/planner-CAHZN1Ez.mjs +422 -0
- package/dist/planner-CAHZN1Ez.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-a-tjfKsA.d.mts} +4 -4
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts.map → planner-produced-postgres-migration-a-tjfKsA.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs → planner-produced-postgres-migration-flkG4Y7n.mjs} +3 -3
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs.map → planner-produced-postgres-migration-flkG4Y7n.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-C1-Kk3Eu.mjs} +2 -2
- package/dist/{planner-sql-checks-jqUUGyQR.mjs.map → planner-sql-checks-C1-Kk3Eu.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 +72 -17
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +3 -2
- package/dist/postgres-contract-view-C9j5cqP0.d.mts +34 -0
- package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +1 -0
- package/dist/{postgres-contract-serializer-k3TAcPMY.mjs → postgres-contract-view-DWLNQbdR.mjs} +45 -41
- package/dist/postgres-contract-view-DWLNQbdR.mjs.map +1 -0
- package/dist/postgres-database-schema-node-BVdOffjC.d.mts +221 -0
- package/dist/postgres-database-schema-node-BVdOffjC.d.mts.map +1 -0
- package/dist/{postgres-migration-Y4YBJqkS.d.mts → postgres-migration-Bzzi0Asr.d.mts} +28 -9
- package/dist/postgres-migration-Bzzi0Asr.d.mts.map +1 -0
- package/dist/{postgres-migration-B5jKrXv3.mjs → postgres-migration-C92swCzd.mjs} +53 -45
- package/dist/postgres-migration-C92swCzd.mjs.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-role-schema-node-BmCboEa_.mjs +198 -0
- package/dist/postgres-role-schema-node-BmCboEa_.mjs.map +1 -0
- package/dist/{postgres-schema-COGZ1ark.mjs → postgres-schema-COEca21H.mjs} +188 -10
- package/dist/postgres-schema-COEca21H.mjs.map +1 -0
- package/dist/postgres-schema-DsrtYp2v.d.mts +269 -0
- package/dist/postgres-schema-DsrtYp2v.d.mts.map +1 -0
- package/dist/postgres-table-schema-node-9FCtCUz2.mjs +153 -0
- package/dist/postgres-table-schema-node-9FCtCUz2.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-ZkKxt2Fm.mjs +22 -0
- package/dist/resolve-ddl-schema-ZkKxt2Fm.mjs.map +1 -0
- package/dist/rls-canonicalize.d.mts +20 -0
- package/dist/rls-canonicalize.d.mts.map +1 -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/types.d.mts +4 -140
- package/dist/types.mjs +5 -2
- package/dist/wire-name-rHHtr1h0.mjs +75 -0
- package/dist/wire-name-rHHtr1h0.mjs.map +1 -0
- package/package.json +23 -17
- package/src/contract-free/checks.ts +73 -0
- package/src/contract-free/ddl.ts +71 -1
- package/src/core/authoring.ts +467 -2
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codec-type-map.ts +2 -0
- package/src/core/codecs.ts +180 -1
- package/src/core/ddl/nodes.ts +141 -2
- package/src/core/default-normalizer.ts +122 -2
- package/src/core/descriptor-meta.ts +10 -1
- package/src/core/entity-kinds.ts +41 -0
- package/src/core/errors.ts +14 -12
- 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 +160 -0
- package/src/core/migrations/control-policy.ts +126 -44
- package/src/core/migrations/diff-database-schema.ts +253 -0
- package/src/core/migrations/issue-planner.ts +692 -652
- package/src/core/migrations/op-factory-call.ts +189 -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 +187 -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 +386 -59
- package/src/core/migrations/postgres-migration.ts +70 -86
- 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 -31
- package/src/core/namespace-ids.ts +3 -0
- package/src/core/postgres-contract-serializer.ts +46 -71
- package/src/core/postgres-contract-view.ts +48 -0
- package/src/core/postgres-native-enum.ts +51 -0
- package/src/core/postgres-rls-enablement.ts +34 -0
- package/src/core/postgres-rls-policy.ts +64 -0
- package/src/core/postgres-role.ts +59 -0
- package/src/core/postgres-schema-verifier.ts +10 -7
- package/src/core/postgres-schema.ts +64 -15
- package/src/core/postgres-validators.ts +34 -0
- package/src/core/psl-infer/infer-psl-contract.ts +1326 -0
- package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
- package/src/core/psl-infer/postgres-type-map.ts +128 -0
- package/src/core/rls/canonicalize.ts +48 -0
- package/src/core/rls/wire-name.ts +33 -0
- package/src/core/schema-ir/postgres-database-schema-node.ts +61 -0
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +79 -0
- package/src/core/schema-ir/postgres-policy-schema-node.ts +90 -0
- package/src/core/schema-ir/postgres-role-schema-node.ts +65 -0
- package/src/core/schema-ir/postgres-table-schema-node.ts +140 -0
- package/src/core/schema-ir/schema-node-kinds.ts +113 -0
- package/src/exports/codecs.ts +3 -0
- package/src/exports/contract-free.ts +11 -1
- package/src/exports/control.ts +27 -33
- package/src/exports/ddl.ts +6 -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 +7 -0
- package/src/exports/runtime.ts +1 -0
- package/src/exports/schema-ir-annotations.ts +1 -0
- package/src/exports/types.ts +34 -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/errors-CUk87ByX.mjs +0 -33
- package/dist/errors-CUk87ByX.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 +0 -76
- 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
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DefaultMappingOptions } from '@prisma-next/family-sql/psl-infer';
|
|
2
|
+
|
|
3
|
+
const POSTGRES_FUNCTION_ATTRIBUTES: Readonly<Record<string, string>> = {
|
|
4
|
+
'gen_random_uuid()': '@default(dbgenerated("gen_random_uuid()"))',
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
function formatDbGeneratedAttribute(expression: string): string {
|
|
8
|
+
return `@default(dbgenerated(${JSON.stringify(expression)}))`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createPostgresDefaultMapping(): DefaultMappingOptions {
|
|
12
|
+
return {
|
|
13
|
+
functionAttributes: POSTGRES_FUNCTION_ATTRIBUTES,
|
|
14
|
+
fallbackFunctionAttribute: formatDbGeneratedAttribute,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PslNativeTypeAttribute,
|
|
3
|
+
PslTypeMap,
|
|
4
|
+
PslTypeResolution,
|
|
5
|
+
} from '@prisma-next/family-sql/psl-infer';
|
|
6
|
+
|
|
7
|
+
const POSTGRES_TO_PSL: Record<string, string> = {
|
|
8
|
+
text: 'String',
|
|
9
|
+
bool: 'Boolean',
|
|
10
|
+
boolean: 'Boolean',
|
|
11
|
+
int4: 'Int',
|
|
12
|
+
integer: 'Int',
|
|
13
|
+
int8: 'BigInt',
|
|
14
|
+
bigint: 'BigInt',
|
|
15
|
+
float8: 'Float',
|
|
16
|
+
'double precision': 'Float',
|
|
17
|
+
numeric: 'Decimal',
|
|
18
|
+
decimal: 'Decimal',
|
|
19
|
+
timestamptz: 'DateTime',
|
|
20
|
+
'timestamp with time zone': 'DateTime',
|
|
21
|
+
jsonb: 'Json',
|
|
22
|
+
bytea: 'Bytes',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const PRESERVED_NATIVE_TYPES: Record<
|
|
26
|
+
string,
|
|
27
|
+
{ readonly pslType: string; readonly attributeName: string }
|
|
28
|
+
> = {
|
|
29
|
+
'character varying': { pslType: 'String', attributeName: 'db.VarChar' },
|
|
30
|
+
character: { pslType: 'String', attributeName: 'db.Char' },
|
|
31
|
+
char: { pslType: 'String', attributeName: 'db.Char' },
|
|
32
|
+
varchar: { pslType: 'String', attributeName: 'db.VarChar' },
|
|
33
|
+
uuid: { pslType: 'String', attributeName: 'db.Uuid' },
|
|
34
|
+
int2: { pslType: 'Int', attributeName: 'db.SmallInt' },
|
|
35
|
+
smallint: { pslType: 'Int', attributeName: 'db.SmallInt' },
|
|
36
|
+
float4: { pslType: 'Float', attributeName: 'db.Real' },
|
|
37
|
+
real: { pslType: 'Float', attributeName: 'db.Real' },
|
|
38
|
+
timestamp: { pslType: 'DateTime', attributeName: 'db.Timestamp' },
|
|
39
|
+
'timestamp without time zone': { pslType: 'DateTime', attributeName: 'db.Timestamp' },
|
|
40
|
+
date: { pslType: 'DateTime', attributeName: 'db.Date' },
|
|
41
|
+
time: { pslType: 'DateTime', attributeName: 'db.Time' },
|
|
42
|
+
'time without time zone': { pslType: 'DateTime', attributeName: 'db.Time' },
|
|
43
|
+
timetz: { pslType: 'DateTime', attributeName: 'db.Timetz' },
|
|
44
|
+
'time with time zone': { pslType: 'DateTime', attributeName: 'db.Timetz' },
|
|
45
|
+
json: { pslType: 'Json', attributeName: 'db.Json' },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const PARAMETERIZED_NATIVE_TYPES: Record<
|
|
49
|
+
string,
|
|
50
|
+
{ readonly pslType: string; readonly attributeName: string }
|
|
51
|
+
> = {
|
|
52
|
+
'character varying': { pslType: 'String', attributeName: 'db.VarChar' },
|
|
53
|
+
character: { pslType: 'String', attributeName: 'db.Char' },
|
|
54
|
+
char: { pslType: 'String', attributeName: 'db.Char' },
|
|
55
|
+
varchar: { pslType: 'String', attributeName: 'db.VarChar' },
|
|
56
|
+
numeric: { pslType: 'Decimal', attributeName: 'db.Numeric' },
|
|
57
|
+
timestamp: { pslType: 'DateTime', attributeName: 'db.Timestamp' },
|
|
58
|
+
timestamptz: { pslType: 'DateTime', attributeName: 'db.Timestamptz' },
|
|
59
|
+
time: { pslType: 'DateTime', attributeName: 'db.Time' },
|
|
60
|
+
timetz: { pslType: 'DateTime', attributeName: 'db.Timetz' },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const PARAMETERIZED_TYPE_PATTERN = /^(.+?)\((.+)\)$/;
|
|
64
|
+
|
|
65
|
+
function getOwnMappingValue(map: Record<string, string>, key: string): string | undefined {
|
|
66
|
+
return Object.hasOwn(map, key) ? map[key] : undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getOwnRecordValue<T>(map: Record<string, T>, key: string): T | undefined {
|
|
70
|
+
return Object.hasOwn(map, key) ? map[key] : undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function createNativeTypeAttribute(name: string, args?: readonly string[]): PslNativeTypeAttribute {
|
|
74
|
+
return args && args.length > 0 ? { name, args } : { name };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function splitTypeParameterList(params: string): readonly string[] {
|
|
78
|
+
return params
|
|
79
|
+
.split(',')
|
|
80
|
+
.map((part) => part.trim())
|
|
81
|
+
.filter((part) => part.length > 0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function createPostgresTypeMap(enumTypeNames?: ReadonlySet<string>): PslTypeMap {
|
|
85
|
+
return {
|
|
86
|
+
resolve(nativeType: string): PslTypeResolution {
|
|
87
|
+
if (enumTypeNames?.has(nativeType)) {
|
|
88
|
+
return { pslType: nativeType, nativeType };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const paramMatch = nativeType.match(PARAMETERIZED_TYPE_PATTERN);
|
|
92
|
+
if (paramMatch) {
|
|
93
|
+
const [, baseType = nativeType, params = ''] = paramMatch;
|
|
94
|
+
const template = getOwnRecordValue(PARAMETERIZED_NATIVE_TYPES, baseType);
|
|
95
|
+
if (template) {
|
|
96
|
+
return {
|
|
97
|
+
pslType: template.pslType,
|
|
98
|
+
nativeType,
|
|
99
|
+
typeParams: { baseType, params },
|
|
100
|
+
nativeTypeAttribute: createNativeTypeAttribute(
|
|
101
|
+
template.attributeName,
|
|
102
|
+
splitTypeParameterList(params),
|
|
103
|
+
),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const preservedType = getOwnRecordValue(PRESERVED_NATIVE_TYPES, nativeType);
|
|
109
|
+
if (preservedType) {
|
|
110
|
+
return {
|
|
111
|
+
pslType: preservedType.pslType,
|
|
112
|
+
nativeType,
|
|
113
|
+
nativeTypeAttribute: createNativeTypeAttribute(preservedType.attributeName),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const pslType = getOwnMappingValue(POSTGRES_TO_PSL, nativeType);
|
|
118
|
+
if (pslType) {
|
|
119
|
+
return {
|
|
120
|
+
pslType,
|
|
121
|
+
nativeType,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { unsupported: true, nativeType };
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export type RlsPolicyOperation = 'select' | 'insert' | 'update' | 'delete' | 'all';
|
|
4
|
+
|
|
5
|
+
export interface ContentHashParts {
|
|
6
|
+
readonly using?: string;
|
|
7
|
+
readonly withCheck?: string;
|
|
8
|
+
readonly roles: readonly string[];
|
|
9
|
+
readonly operation: RlsPolicyOperation;
|
|
10
|
+
readonly permissive: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Stabilizes an authored predicate for hashing: trim, and collapse runs of
|
|
15
|
+
* internal whitespace to a single space.
|
|
16
|
+
*
|
|
17
|
+
* This is deliberately minimal. The content hash is the equivalence relation
|
|
18
|
+
* for a policy, and the wire name (prefix + hash) is the only thing ever
|
|
19
|
+
* compared — we never recompute the hash from an introspected policy body, so
|
|
20
|
+
* there is no need to match Postgres's reprinted form. Minimal normalization
|
|
21
|
+
* also protects the no-collision property: aggressive rewriting (lowercasing,
|
|
22
|
+
* paren-stripping, cast-alias folding) risks collapsing two distinct predicates
|
|
23
|
+
* onto one hash. Out-of-band alteration of a hashed policy is unsupported.
|
|
24
|
+
*
|
|
25
|
+
* The normalizer is a stability commitment: any change re-suffixes all wire names.
|
|
26
|
+
*/
|
|
27
|
+
export function normalizePredicate(sql: string): string {
|
|
28
|
+
return sql.replace(/\s+/g, ' ').trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Returns the first 8 lowercase hex characters of the SHA-256 digest over the
|
|
33
|
+
* canonical content tuple for an RLS policy:
|
|
34
|
+
*
|
|
35
|
+
* [normalize(using), normalize(withCheck), sortedRoles, operation, permissive]
|
|
36
|
+
*
|
|
37
|
+
* Schema and table are excluded (they are orthogonal to policy equivalence).
|
|
38
|
+
* Uses `JSON.stringify` for a deterministic encoding.
|
|
39
|
+
*/
|
|
40
|
+
export function computeContentHash(parts: ContentHashParts): string {
|
|
41
|
+
const using = normalizePredicate(parts.using ?? '');
|
|
42
|
+
const withCheck = normalizePredicate(parts.withCheck ?? '');
|
|
43
|
+
const roles = [...new Set(parts.roles)].sort();
|
|
44
|
+
const permissive = parts.permissive ? 'permissive' : 'restrictive';
|
|
45
|
+
|
|
46
|
+
const tuple = JSON.stringify([using, withCheck, roles, parts.operation, permissive]);
|
|
47
|
+
return createHash('sha256').update(tuple).digest('hex').slice(0, 8);
|
|
48
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface RlsPolicyWireName {
|
|
2
|
+
/** The user-supplied part before the `_<8hex>` suffix. */
|
|
3
|
+
readonly prefix: string;
|
|
4
|
+
/** The 8-lowercase-hex content-hash suffix. */
|
|
5
|
+
readonly hash: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const WIRE_NAME_PATTERN = /^(.+)_([0-9a-f]{8})$/;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Assembles an RLS policy wire name from its user-supplied prefix and its
|
|
12
|
+
* 8-hex content-hash suffix. This module owns the `<prefix>_<hash>` format
|
|
13
|
+
* on both sides — construction here and parsing in
|
|
14
|
+
* {@link parseRlsPolicyWireName} — so the two never drift.
|
|
15
|
+
*/
|
|
16
|
+
export function formatRlsPolicyWireName(prefix: string, hash: string): string {
|
|
17
|
+
return `${prefix}_${hash}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Splits an RLS policy wire name (`<prefix>_<8hex>`) into its prefix and
|
|
22
|
+
* content-hash suffix. Returns `undefined` when the name does not follow the
|
|
23
|
+
* wire-name shape (e.g. a policy created outside the toolchain) — callers
|
|
24
|
+
* treat such names as all-prefix. Consumed by introspection (prefix
|
|
25
|
+
* extraction) and by rename pairing (same hash, different prefix).
|
|
26
|
+
*/
|
|
27
|
+
export function parseRlsPolicyWireName(name: string): RlsPolicyWireName | undefined {
|
|
28
|
+
const match = WIRE_NAME_PATTERN.exec(name);
|
|
29
|
+
const prefix = match?.[1];
|
|
30
|
+
const hash = match?.[2];
|
|
31
|
+
if (prefix === undefined || hash === undefined) return undefined;
|
|
32
|
+
return { prefix, hash };
|
|
33
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { DiffableNode } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
3
|
+
import { assertNode, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
4
|
+
import type { PostgresNamespaceSchemaNode } from './postgres-namespace-schema-node';
|
|
5
|
+
import type { PostgresRoleSchemaNode } from './postgres-role-schema-node';
|
|
6
|
+
import { PostgresSchemaNodeKind } from './schema-node-kinds';
|
|
7
|
+
|
|
8
|
+
export interface PostgresDatabaseSchemaNodeInput {
|
|
9
|
+
readonly namespaces: Readonly<Record<string, PostgresNamespaceSchemaNode>>;
|
|
10
|
+
readonly roles: readonly PostgresRoleSchemaNode[];
|
|
11
|
+
readonly existingSchemas: readonly string[];
|
|
12
|
+
readonly pgVersion: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The root of the Postgres schema-diff tree: one node per database.
|
|
17
|
+
*
|
|
18
|
+
* `id` is the fixed sentinel `'database'` — the root has no siblings and
|
|
19
|
+
* the value is never emitted into migration paths. `isEqualTo` is identity
|
|
20
|
+
* (roots always share the `'database'` id). `children()` yields the namespace
|
|
21
|
+
* nodes followed by the role nodes — both are root-level diff subjects. The
|
|
22
|
+
* differ pairs siblings by `(nodeKind, id)`, so a role and a namespace may
|
|
23
|
+
* share a name without colliding.
|
|
24
|
+
*/
|
|
25
|
+
export class PostgresDatabaseSchemaNode extends SqlSchemaIRNode implements DiffableNode {
|
|
26
|
+
override readonly nodeKind = PostgresSchemaNodeKind.database;
|
|
27
|
+
|
|
28
|
+
readonly namespaces: Readonly<Record<string, PostgresNamespaceSchemaNode>>;
|
|
29
|
+
readonly roles: readonly PostgresRoleSchemaNode[];
|
|
30
|
+
readonly existingSchemas: readonly string[];
|
|
31
|
+
readonly pgVersion: string;
|
|
32
|
+
|
|
33
|
+
constructor(input: PostgresDatabaseSchemaNodeInput) {
|
|
34
|
+
super();
|
|
35
|
+
this.namespaces = Object.freeze({ ...input.namespaces });
|
|
36
|
+
this.roles = Object.freeze([...input.roles]);
|
|
37
|
+
this.existingSchemas = Object.freeze([...input.existingSchemas]);
|
|
38
|
+
this.pgVersion = input.pgVersion;
|
|
39
|
+
freezeNode(this);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get id(): string {
|
|
43
|
+
return 'database';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
isEqualTo(other: DiffableNode): boolean {
|
|
47
|
+
return this.id === other.id;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
children(): readonly DiffableNode[] {
|
|
51
|
+
return [...Object.values(this.namespaces), ...this.roles];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static is(node: SqlSchemaIRNode): node is PostgresDatabaseSchemaNode {
|
|
55
|
+
return node.nodeKind === PostgresSchemaNodeKind.database;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static assert(node: SqlSchemaIRNode): asserts node is PostgresDatabaseSchemaNode {
|
|
59
|
+
assertNode(node, 'PostgresDatabaseSchemaNode', PostgresDatabaseSchemaNode.is);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { DiffableNode } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
3
|
+
import { assertNode, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
4
|
+
import type { PostgresTableSchemaNode } from './postgres-table-schema-node';
|
|
5
|
+
import { PostgresSchemaNodeKind } from './schema-node-kinds';
|
|
6
|
+
|
|
7
|
+
/** One introspected native Postgres enum type, in `pg_enum.enumsortorder` (declaration) order. */
|
|
8
|
+
export interface PostgresNativeEnumIntrospection {
|
|
9
|
+
readonly typeName: string;
|
|
10
|
+
readonly values: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface PostgresNamespaceSchemaNodeInput {
|
|
14
|
+
readonly schemaName: string;
|
|
15
|
+
readonly tables: Readonly<Record<string, PostgresTableSchemaNode>>;
|
|
16
|
+
readonly nativeEnumTypeNames: readonly string[];
|
|
17
|
+
readonly nativeEnums?: readonly PostgresNativeEnumIntrospection[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* One-per-Postgres-schema diff-tree node. Groups the tables belonging to a
|
|
22
|
+
* single namespace. Per-schema consumers (the relational planner,
|
|
23
|
+
* toSchemaView) read this node's `tables` field structurally via
|
|
24
|
+
* `blindCast`/`SqlSchemaIRNode` — not through a static `SqlSchemaIR`
|
|
25
|
+
* assignment — because `nodeKind` now carries this node's own literal
|
|
26
|
+
* (`postgres-namespace`), distinct from `SqlSchemaIR`'s own (`sql-schema`).
|
|
27
|
+
*
|
|
28
|
+
* `id` is the schema name. `isEqualTo` is identity — two namespace nodes are
|
|
29
|
+
* equal iff their ids (schema names) match. `children()` returns the table
|
|
30
|
+
* nodes. Per-schema metadata is carried on the typed `nativeEnumTypeNames`
|
|
31
|
+
* field, not an annotations bag.
|
|
32
|
+
*
|
|
33
|
+
* `nativeEnums` carries the same enum types with their ordered member
|
|
34
|
+
* values (`{ typeName, values }`), for consumers that need the values
|
|
35
|
+
* (PSL inference, the printer). `nativeEnumTypeNames` stays a plain name
|
|
36
|
+
* list read independently by existing consumers (codec `planTypeOperations`
|
|
37
|
+
* hooks, the infer throw) so none of them need the values to keep working.
|
|
38
|
+
*/
|
|
39
|
+
export class PostgresNamespaceSchemaNode extends SqlSchemaIRNode implements DiffableNode {
|
|
40
|
+
override readonly nodeKind = PostgresSchemaNodeKind.namespace;
|
|
41
|
+
|
|
42
|
+
readonly schemaName: string;
|
|
43
|
+
readonly tables: Readonly<Record<string, PostgresTableSchemaNode>>;
|
|
44
|
+
readonly nativeEnumTypeNames: readonly string[];
|
|
45
|
+
readonly nativeEnums: readonly PostgresNativeEnumIntrospection[];
|
|
46
|
+
|
|
47
|
+
constructor(input: PostgresNamespaceSchemaNodeInput) {
|
|
48
|
+
super();
|
|
49
|
+
this.schemaName = input.schemaName;
|
|
50
|
+
this.tables = Object.freeze({ ...input.tables });
|
|
51
|
+
this.nativeEnumTypeNames = Object.freeze([...input.nativeEnumTypeNames]);
|
|
52
|
+
this.nativeEnums = Object.freeze(
|
|
53
|
+
(input.nativeEnums ?? []).map((entry) =>
|
|
54
|
+
Object.freeze({ typeName: entry.typeName, values: Object.freeze([...entry.values]) }),
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
freezeNode(this);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get id(): string {
|
|
61
|
+
return this.schemaName;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
isEqualTo(other: DiffableNode): boolean {
|
|
65
|
+
return this.id === other.id;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
children(): readonly DiffableNode[] {
|
|
69
|
+
return Object.values(this.tables);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static is(node: SqlSchemaIRNode): node is PostgresNamespaceSchemaNode {
|
|
73
|
+
return node.nodeKind === PostgresSchemaNodeKind.namespace;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static assert(node: SqlSchemaIRNode): asserts node is PostgresNamespaceSchemaNode {
|
|
77
|
+
assertNode(node, 'PostgresNamespaceSchemaNode', PostgresNamespaceSchemaNode.is);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { DiffableNode } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
3
|
+
import { assertNode, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
4
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
5
|
+
import type { RlsPolicyOperation } from '../postgres-rls-policy';
|
|
6
|
+
import { PostgresSchemaNodeKind } from './schema-node-kinds';
|
|
7
|
+
|
|
8
|
+
export interface PostgresPolicySchemaNodeInput {
|
|
9
|
+
/** Full wire name: `<prefix>_<8hex>`. */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
/** User-supplied prefix (the part before the `_<8hex>` suffix). */
|
|
12
|
+
readonly prefix: string;
|
|
13
|
+
/** Name of the table this policy attaches to, by name within the same schema. */
|
|
14
|
+
readonly tableName: string;
|
|
15
|
+
/** Namespace coordinate (schema name). */
|
|
16
|
+
readonly namespaceId: string;
|
|
17
|
+
readonly operation: RlsPolicyOperation;
|
|
18
|
+
/** Sorted role names rendered in `TO <roles>`. */
|
|
19
|
+
readonly roles: readonly string[];
|
|
20
|
+
/** USING predicate SQL string, if present. */
|
|
21
|
+
readonly using?: string;
|
|
22
|
+
/** WITH CHECK predicate SQL string, if present. */
|
|
23
|
+
readonly withCheck?: string;
|
|
24
|
+
/** `true` = `AS PERMISSIVE`, `false` = `AS RESTRICTIVE`. */
|
|
25
|
+
readonly permissive: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Schema-diff leaf node for a Postgres row-level security policy.
|
|
30
|
+
*
|
|
31
|
+
* This is a derived, transient node walked by the differ — it is NEVER serialized.
|
|
32
|
+
* Built by project-from-contract and project-from-database from their respective
|
|
33
|
+
* `PostgresRlsPolicy` contract entities / introspected rows.
|
|
34
|
+
*
|
|
35
|
+
* `id` is the wire name (`<prefix>_<sha256(body)[0..8]>`), so name-equality is
|
|
36
|
+
* body-equality. `isEqualTo` compares names only — never byte-compare predicate
|
|
37
|
+
* bodies, because Postgres reprints them.
|
|
38
|
+
*/
|
|
39
|
+
export class PostgresPolicySchemaNode extends SqlSchemaIRNode implements DiffableNode {
|
|
40
|
+
override readonly nodeKind = PostgresSchemaNodeKind.policy;
|
|
41
|
+
|
|
42
|
+
readonly name: string;
|
|
43
|
+
readonly prefix: string;
|
|
44
|
+
readonly tableName: string;
|
|
45
|
+
readonly namespaceId: string;
|
|
46
|
+
readonly operation: RlsPolicyOperation;
|
|
47
|
+
readonly roles: readonly string[];
|
|
48
|
+
declare readonly using?: string;
|
|
49
|
+
declare readonly withCheck?: string;
|
|
50
|
+
readonly permissive: boolean;
|
|
51
|
+
|
|
52
|
+
constructor(input: PostgresPolicySchemaNodeInput) {
|
|
53
|
+
super();
|
|
54
|
+
this.name = input.name;
|
|
55
|
+
this.prefix = input.prefix;
|
|
56
|
+
this.tableName = input.tableName;
|
|
57
|
+
this.namespaceId = input.namespaceId;
|
|
58
|
+
this.operation = input.operation;
|
|
59
|
+
this.roles = Object.freeze([...input.roles]);
|
|
60
|
+
if (input.using !== undefined) this.using = input.using;
|
|
61
|
+
if (input.withCheck !== undefined) this.withCheck = input.withCheck;
|
|
62
|
+
this.permissive = input.permissive;
|
|
63
|
+
freezeNode(this);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get id(): string {
|
|
67
|
+
return this.name;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
children(): readonly DiffableNode[] {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
isEqualTo(other: DiffableNode): boolean {
|
|
75
|
+
const node = blindCast<
|
|
76
|
+
SqlSchemaIRNode,
|
|
77
|
+
'every diff-tree node the differ pairs is a SqlSchemaIRNode; the guard rejects non-policy kinds'
|
|
78
|
+
>(other);
|
|
79
|
+
PostgresPolicySchemaNode.assert(node);
|
|
80
|
+
return this.id === node.id;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static is(node: SqlSchemaIRNode): node is PostgresPolicySchemaNode {
|
|
84
|
+
return node.nodeKind === PostgresSchemaNodeKind.policy;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static assert(node: SqlSchemaIRNode | undefined): asserts node is PostgresPolicySchemaNode {
|
|
88
|
+
assertNode(node, 'PostgresPolicySchemaNode', PostgresPolicySchemaNode.is);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { DiffableNode } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
3
|
+
import { assertNode, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
4
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
5
|
+
import { PostgresSchemaNodeKind } from './schema-node-kinds';
|
|
6
|
+
|
|
7
|
+
export interface PostgresRoleSchemaNodeInput {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
/**
|
|
10
|
+
* Namespace coordinate. Roles are cluster-scoped; callers pass
|
|
11
|
+
* `UNBOUND_NAMESPACE_ID` from `@prisma-next/framework-components/ir`.
|
|
12
|
+
*/
|
|
13
|
+
readonly namespaceId: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Schema-diff leaf node for a Postgres database role.
|
|
18
|
+
*
|
|
19
|
+
* This is a derived, transient node walked by the differ — it is NEVER serialized.
|
|
20
|
+
* Built by project-from-contract and project-from-database from their respective
|
|
21
|
+
* `PostgresRole` contract entities / introspected rows.
|
|
22
|
+
*
|
|
23
|
+
* Roles are cluster-scoped, so `id` is the bare role name. The differ pairs
|
|
24
|
+
* siblings by `(nodeKind, id)`, so a role and a namespace may share a name
|
|
25
|
+
* (role `public`, schema `public`) without colliding. `isEqualTo` compares
|
|
26
|
+
* ids — name-equality is role-equality for cluster-scoped objects.
|
|
27
|
+
*/
|
|
28
|
+
export class PostgresRoleSchemaNode extends SqlSchemaIRNode implements DiffableNode {
|
|
29
|
+
override readonly nodeKind = PostgresSchemaNodeKind.role;
|
|
30
|
+
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly namespaceId: string;
|
|
33
|
+
|
|
34
|
+
constructor(input: PostgresRoleSchemaNodeInput) {
|
|
35
|
+
super();
|
|
36
|
+
this.name = input.name;
|
|
37
|
+
this.namespaceId = input.namespaceId;
|
|
38
|
+
freezeNode(this);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get id(): string {
|
|
42
|
+
return this.name;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
children(): readonly DiffableNode[] {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
isEqualTo(other: DiffableNode): boolean {
|
|
50
|
+
const node = blindCast<
|
|
51
|
+
SqlSchemaIRNode,
|
|
52
|
+
'every diff-tree node the differ pairs is a SqlSchemaIRNode; the guard rejects non-role kinds'
|
|
53
|
+
>(other);
|
|
54
|
+
PostgresRoleSchemaNode.assert(node);
|
|
55
|
+
return this.id === node.id;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static is(node: SqlSchemaIRNode): node is PostgresRoleSchemaNode {
|
|
59
|
+
return node.nodeKind === PostgresSchemaNodeKind.role;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static assert(node: SqlSchemaIRNode): asserts node is PostgresRoleSchemaNode {
|
|
63
|
+
assertNode(node, 'PostgresRoleSchemaNode', PostgresRoleSchemaNode.is);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { DiffableNode } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
3
|
+
import {
|
|
4
|
+
assertNode,
|
|
5
|
+
PrimaryKey,
|
|
6
|
+
type SqlAnnotations,
|
|
7
|
+
SqlCheckConstraintIR,
|
|
8
|
+
SqlColumnIR,
|
|
9
|
+
SqlForeignKeyIR,
|
|
10
|
+
SqlIndexIR,
|
|
11
|
+
SqlSchemaIRNode,
|
|
12
|
+
type SqlTableIRInput,
|
|
13
|
+
SqlUniqueIR,
|
|
14
|
+
} from '@prisma-next/sql-schema-ir/types';
|
|
15
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
16
|
+
import type { PostgresPolicySchemaNode } from './postgres-policy-schema-node';
|
|
17
|
+
import { PostgresSchemaNodeKind } from './schema-node-kinds';
|
|
18
|
+
|
|
19
|
+
export interface PostgresTableSchemaNodeInput extends SqlTableIRInput {
|
|
20
|
+
readonly policies?: readonly PostgresPolicySchemaNode[];
|
|
21
|
+
/**
|
|
22
|
+
* Whether row-level security is enabled on this table. Expected side:
|
|
23
|
+
* stamped from the contract's `entries.rls` marker (never from the
|
|
24
|
+
* policy set). Actual side: stamped from `pg_class.relrowsecurity` at
|
|
25
|
+
* introspection. Required with no default so every construction site
|
|
26
|
+
* supplies a deliberate value.
|
|
27
|
+
*/
|
|
28
|
+
readonly rlsEnabled: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Postgres-specific table schema-diff node. Carries all `SqlTableIR` fields
|
|
33
|
+
* plus `policies`, and implements `DiffableNode` so the table instance is
|
|
34
|
+
* directly the diff-tree node — no separate wrapper needed.
|
|
35
|
+
*
|
|
36
|
+
* Extends `SqlSchemaIRNode` directly rather than `SqlTableIR` because
|
|
37
|
+
* `SqlTableIR` calls `freezeNode` in its own constructor, which prevents
|
|
38
|
+
* subclass field initialisation.
|
|
39
|
+
*
|
|
40
|
+
* `id` is the table name. `isEqualTo` compares id + `rlsEnabled` — the
|
|
41
|
+
* table's one own diffable attribute; all other structural drift is
|
|
42
|
+
* expressed by its children. `children()` returns every column, the primary
|
|
43
|
+
* key (when present), every foreign key, unique, index, and check constraint,
|
|
44
|
+
* plus the policy nodes — one flat list, so a drifted column and a drifted
|
|
45
|
+
* policy are both reported by the same walk. Order is deterministic (object
|
|
46
|
+
* key order for columns, array order for the rest) so two structurally equal
|
|
47
|
+
* tables always produce the same child list.
|
|
48
|
+
*/
|
|
49
|
+
export class PostgresTableSchemaNode extends SqlSchemaIRNode implements DiffableNode {
|
|
50
|
+
override readonly nodeKind = PostgresSchemaNodeKind.table;
|
|
51
|
+
|
|
52
|
+
readonly name: string;
|
|
53
|
+
readonly columns: Readonly<Record<string, SqlColumnIR>>;
|
|
54
|
+
readonly foreignKeys: ReadonlyArray<SqlForeignKeyIR>;
|
|
55
|
+
readonly uniques: ReadonlyArray<SqlUniqueIR>;
|
|
56
|
+
readonly indexes: ReadonlyArray<SqlIndexIR>;
|
|
57
|
+
declare readonly primaryKey?: PrimaryKey;
|
|
58
|
+
declare readonly annotations?: SqlAnnotations;
|
|
59
|
+
declare readonly checks?: ReadonlyArray<SqlCheckConstraintIR>;
|
|
60
|
+
readonly policies: readonly PostgresPolicySchemaNode[];
|
|
61
|
+
readonly rlsEnabled: boolean;
|
|
62
|
+
|
|
63
|
+
constructor(input: PostgresTableSchemaNodeInput) {
|
|
64
|
+
super();
|
|
65
|
+
this.name = input.name;
|
|
66
|
+
this.rlsEnabled = input.rlsEnabled;
|
|
67
|
+
this.columns = Object.freeze(
|
|
68
|
+
Object.fromEntries(
|
|
69
|
+
Object.entries(input.columns).map(([key, col]) => [
|
|
70
|
+
key,
|
|
71
|
+
col instanceof SqlColumnIR ? col : new SqlColumnIR(col),
|
|
72
|
+
]),
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
this.foreignKeys = Object.freeze(
|
|
76
|
+
input.foreignKeys.map((fk) => (fk instanceof SqlForeignKeyIR ? fk : new SqlForeignKeyIR(fk))),
|
|
77
|
+
);
|
|
78
|
+
this.uniques = Object.freeze(
|
|
79
|
+
input.uniques.map((u) => (u instanceof SqlUniqueIR ? u : new SqlUniqueIR(u))),
|
|
80
|
+
);
|
|
81
|
+
this.indexes = Object.freeze(
|
|
82
|
+
input.indexes.map((i) => (i instanceof SqlIndexIR ? i : new SqlIndexIR(i))),
|
|
83
|
+
);
|
|
84
|
+
if (input.primaryKey !== undefined) {
|
|
85
|
+
this.primaryKey =
|
|
86
|
+
input.primaryKey instanceof PrimaryKey
|
|
87
|
+
? input.primaryKey
|
|
88
|
+
: new PrimaryKey(input.primaryKey);
|
|
89
|
+
}
|
|
90
|
+
if (input.annotations !== undefined) this.annotations = input.annotations;
|
|
91
|
+
if (input.checks !== undefined && input.checks.length > 0) {
|
|
92
|
+
this.checks = Object.freeze(
|
|
93
|
+
input.checks.map((c) =>
|
|
94
|
+
c instanceof SqlCheckConstraintIR ? c : new SqlCheckConstraintIR(c),
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
this.policies = Object.freeze([...(input.policies ?? [])]);
|
|
99
|
+
freezeNode(this);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get id(): string {
|
|
103
|
+
return this.name;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Equal iff the ids (names) match AND `rlsEnabled` matches — the table's
|
|
108
|
+
* one own attribute in the diff; all other structural drift is expressed
|
|
109
|
+
* by children. A paired `not-equal` therefore always means enablement
|
|
110
|
+
* drift, which the planner maps to ENABLE/DISABLE ROW LEVEL SECURITY.
|
|
111
|
+
*/
|
|
112
|
+
isEqualTo(other: DiffableNode): boolean {
|
|
113
|
+
const node = blindCast<
|
|
114
|
+
SqlSchemaIRNode,
|
|
115
|
+
'every diff-tree node the differ pairs is a SqlSchemaIRNode'
|
|
116
|
+
>(other);
|
|
117
|
+
PostgresTableSchemaNode.assert(node);
|
|
118
|
+
return this.id === node.id && this.rlsEnabled === node.rlsEnabled;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
children(): readonly DiffableNode[] {
|
|
122
|
+
return [
|
|
123
|
+
...Object.values(this.columns),
|
|
124
|
+
...(this.primaryKey ? [this.primaryKey] : []),
|
|
125
|
+
...this.foreignKeys,
|
|
126
|
+
...this.uniques,
|
|
127
|
+
...this.indexes,
|
|
128
|
+
...(this.checks ?? []),
|
|
129
|
+
...this.policies,
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static is(node: SqlSchemaIRNode): node is PostgresTableSchemaNode {
|
|
134
|
+
return node.nodeKind === PostgresSchemaNodeKind.table;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static assert(node: SqlSchemaIRNode): asserts node is PostgresTableSchemaNode {
|
|
138
|
+
assertNode(node, 'PostgresTableSchemaNode', PostgresTableSchemaNode.is);
|
|
139
|
+
}
|
|
140
|
+
}
|