@prisma-next/target-postgres 0.15.0 → 0.16.0-dev.2
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-BtsOWgI3.mjs → authoring-34L18kA1.mjs} +18 -8
- package/dist/authoring-34L18kA1.mjs.map +1 -0
- package/dist/canonicalize-D1F4vUnP.d.mts.map +1 -1
- package/dist/{codec-ids-CI144z7g.d.mts → codec-ids-BswpvvND.d.mts} +3 -3
- package/dist/codec-ids-BswpvvND.d.mts.map +1 -0
- package/dist/{codec-ids-B10ZZHzw.mjs → codec-ids-D3G8K0YM.mjs} +3 -2
- package/dist/codec-ids-D3G8K0YM.mjs.map +1 -0
- package/dist/codec-ids.d.mts +2 -2
- package/dist/codec-ids.mjs +2 -2
- package/dist/{codec-types-CTfT4U4O.d.mts → codec-types-CQ07nOz-.d.mts} +4 -5
- package/dist/codec-types-CQ07nOz-.d.mts.map +1 -0
- package/dist/codec-types.d.mts +1 -1
- package/dist/{codecs-D7W9O0Xx.d.mts → codecs-G4DTY0F_.d.mts} +35 -7
- package/dist/codecs-G4DTY0F_.d.mts.map +1 -0
- package/dist/{codecs-DRbb26Or.mjs → codecs-TCsGgW-Y.mjs} +77 -5
- package/dist/codecs-TCsGgW-Y.mjs.map +1 -0
- package/dist/codecs.d.mts +2 -3
- package/dist/codecs.d.mts.map +1 -1
- package/dist/codecs.mjs +2 -2
- package/dist/contract-free.d.mts +2 -3
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +3 -3
- package/dist/{control-policy-DtbkLLGv.mjs → control-policy-CZKdMztu.mjs} +44 -75
- package/dist/control-policy-CZKdMztu.mjs.map +1 -0
- package/dist/control.d.mts +0 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +75 -25
- package/dist/control.mjs.map +1 -1
- package/dist/data-transform-BOWpliq8.mjs.map +1 -1
- package/dist/{data-transform-DPzwBMhW.d.mts → data-transform-CGvE8yGx.d.mts} +1 -2
- package/dist/data-transform-CGvE8yGx.d.mts.map +1 -0
- package/dist/data-transform.d.mts +1 -1
- package/dist/{ddl-GTTOvjx_.mjs → ddl-DHXKzQ99.mjs} +3 -3
- package/dist/{ddl-GTTOvjx_.mjs.map → ddl-DHXKzQ99.mjs.map} +1 -1
- package/dist/ddl.d.mts +1 -1
- package/dist/default-normalizer-B9ZUiyUE.mjs +264 -0
- package/dist/default-normalizer-B9ZUiyUE.mjs.map +1 -0
- package/dist/default-normalizer.d.mts +14 -2
- package/dist/default-normalizer.d.mts.map +1 -1
- package/dist/default-normalizer.mjs +2 -248
- package/dist/{descriptor-meta-CFQKLXl_.mjs → descriptor-meta-qubYaxQA.mjs} +9 -4
- package/dist/descriptor-meta-qubYaxQA.mjs.map +1 -0
- package/dist/{diff-database-schema-GncqMuSI.mjs → diff-database-schema-C-hnJ5mr.mjs} +92 -26
- package/dist/diff-database-schema-C-hnJ5mr.mjs.map +1 -0
- package/dist/diff-database-schema.d.mts +4 -13
- package/dist/diff-database-schema.d.mts.map +1 -1
- package/dist/diff-database-schema.mjs +1 -1
- package/dist/{errors-DO-w0EUS.mjs → errors-ByDS0xJL.mjs} +6 -6
- package/dist/errors-ByDS0xJL.mjs.map +1 -0
- package/dist/errors.d.mts +4 -4
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +1 -1
- package/dist/issue-planner.d.mts +1 -2
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +3 -4
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +2 -2
- package/dist/native-type-normalizer.d.mts.map +1 -1
- package/dist/{nodes-Dv5eCSq9.d.mts → nodes-CQzM2hR0.d.mts} +1 -2
- package/dist/nodes-CQzM2hR0.d.mts.map +1 -0
- package/dist/{op-factory-call-Bn9zDx4_.d.mts → op-factory-call-DT7kdjVP.d.mts} +10 -7
- package/dist/op-factory-call-DT7kdjVP.d.mts.map +1 -0
- package/dist/{op-factory-call-C38NzvQD.mjs → op-factory-call-DuRj7WXX.mjs} +37 -16
- package/dist/{op-factory-call-C38NzvQD.mjs.map → op-factory-call-DuRj7WXX.mjs.map} +1 -1
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/pack.d.mts +122 -6
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-kql24i_F.mjs → planner-DPGxFcXo.mjs} +13 -13
- package/dist/planner-DPGxFcXo.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +1 -2
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-identity-values.d.mts +0 -1
- package/dist/planner-identity-values.d.mts.map +1 -1
- package/dist/{planner-produced-postgres-migration-BEY1T0Xm.d.mts → planner-produced-postgres-migration-C-dwGdb2.d.mts} +2 -3
- package/dist/planner-produced-postgres-migration-C-dwGdb2.d.mts.map +1 -0
- package/dist/{planner-produced-postgres-migration-D7y-PEHK.mjs → planner-produced-postgres-migration-CFDpqWWE.mjs} +2 -2
- package/dist/planner-produced-postgres-migration-CFDpqWWE.mjs.map +1 -0
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/planner-schema-lookup.d.mts +0 -1
- package/dist/planner-schema-lookup.d.mts.map +1 -1
- package/dist/{planner-sql-checks-zqNfs2XZ.mjs → planner-sql-checks-BuB877wH.mjs} +2 -2
- package/dist/{planner-sql-checks-zqNfs2XZ.mjs.map → planner-sql-checks-BuB877wH.mjs.map} +1 -1
- package/dist/planner-sql-checks.d.mts +0 -1
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -1
- package/dist/planner.d.mts +3 -4
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-contract-view-C9j5cqP0.d.mts → postgres-contract-view-CeU7FJjW.d.mts} +4 -3
- package/dist/postgres-contract-view-CeU7FJjW.d.mts.map +1 -0
- package/dist/{postgres-contract-view-DM8sN1Ot.mjs → postgres-contract-view-DebHMqGR.mjs} +4 -4
- package/dist/{postgres-contract-view-DM8sN1Ot.mjs.map → postgres-contract-view-DebHMqGR.mjs.map} +1 -1
- package/dist/{postgres-database-schema-node-CJv5bfbs.d.mts → postgres-database-schema-node-DMXsIwAb.d.mts} +12 -4
- package/dist/postgres-database-schema-node-DMXsIwAb.d.mts.map +1 -0
- package/dist/{postgres-migration-arMSWU9b.mjs → postgres-migration-C_OPFhIF.mjs} +22 -6
- package/dist/postgres-migration-C_OPFhIF.mjs.map +1 -0
- package/dist/{postgres-migration-DbckK1su.d.mts → postgres-migration-DLIvzHjX.d.mts} +31 -7
- package/dist/postgres-migration-DLIvzHjX.d.mts.map +1 -0
- package/dist/{postgres-rls-policy--CSddvMR.d.mts → postgres-rls-policy-CYXdqG9B.d.mts} +1 -2
- package/dist/postgres-rls-policy-CYXdqG9B.d.mts.map +1 -0
- package/dist/{postgres-role-schema-node-Cm-j0ZGM.mjs → postgres-role-schema-node-bg32e7I-.mjs} +3 -2
- package/dist/postgres-role-schema-node-bg32e7I-.mjs.map +1 -0
- package/dist/{postgres-schema-Cm4DtiGA.mjs → postgres-schema-CewLYHB7.mjs} +2 -2
- package/dist/{postgres-schema-Cm4DtiGA.mjs.map → postgres-schema-CewLYHB7.mjs.map} +1 -1
- package/dist/{postgres-schema-DKq6s1P3.d.mts → postgres-schema-FdKRwe7X.d.mts} +3 -4
- package/dist/postgres-schema-FdKRwe7X.d.mts.map +1 -0
- package/dist/render-ops.d.mts +0 -1
- package/dist/render-ops.d.mts.map +1 -1
- package/dist/render-typescript.d.mts +0 -1
- package/dist/render-typescript.d.mts.map +1 -1
- package/dist/{resolve-ddl-schema-XyBaaenM.mjs → resolve-ddl-schema-D0wi_P9Q.mjs} +2 -2
- package/dist/{resolve-ddl-schema-XyBaaenM.mjs.map → resolve-ddl-schema-D0wi_P9Q.mjs.map} +1 -1
- package/dist/rls-canonicalize.d.mts +0 -1
- package/dist/rls-canonicalize.d.mts.map +1 -1
- package/dist/runtime.d.mts +1 -2
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/schema-ir-annotations.d.mts +0 -1
- package/dist/schema-ir-annotations.d.mts.map +1 -1
- package/dist/schema-ir-annotations.mjs +1 -1
- package/dist/schema-node-kinds-DxvWh2Ic.d.mts +29 -0
- package/dist/schema-node-kinds-DxvWh2Ic.d.mts.map +1 -0
- package/dist/sql-utils.d.mts.map +1 -1
- package/dist/{table-source-BvFo7gVs.d.mts → table-source-D5c2fxAQ.d.mts} +1 -2
- package/dist/table-source-D5c2fxAQ.d.mts.map +1 -0
- package/dist/{types-BDKkx8MA.d.mts → types-D2MLzLBD.d.mts} +1 -2
- package/dist/types-D2MLzLBD.d.mts.map +1 -0
- package/dist/types.d.mts +6 -5
- package/dist/types.mjs +4 -4
- package/package.json +21 -21
- package/src/core/authoring.ts +18 -5
- package/src/core/codec-helpers.ts +53 -1
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codecs.ts +56 -3
- package/src/core/default-normalizer.ts +22 -0
- package/src/core/descriptor-meta.ts +2 -0
- package/src/core/errors.ts +5 -5
- package/src/core/index-types.ts +15 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +99 -19
- package/src/core/migrations/control-policy.ts +4 -3
- package/src/core/migrations/diff-database-schema.ts +6 -3
- package/src/core/migrations/issue-planner.ts +41 -80
- package/src/core/migrations/op-factory-call.ts +40 -16
- package/src/core/migrations/operations/data-transform.ts +1 -1
- package/src/core/migrations/planner-produced-postgres-migration.ts +1 -1
- package/src/core/migrations/planner-strategies.ts +6 -6
- package/src/core/migrations/planner.ts +5 -4
- package/src/core/migrations/postgres-migration.ts +62 -2
- package/src/core/migrations/runner.ts +12 -11
- package/src/core/migrations/verify-postgres-namespaces.ts +0 -1
- package/src/core/psl-infer/infer-psl-contract.ts +130 -12
- package/src/core/schema-ir/postgres-policy-schema-node.ts +12 -2
- package/src/exports/codecs.ts +2 -0
- package/src/exports/control.ts +2 -0
- package/src/exports/default-normalizer.ts +1 -1
- package/src/exports/op-factory-call.ts +5 -0
- package/src/exports/types.ts +1 -0
- package/dist/authoring-BtsOWgI3.mjs.map +0 -1
- package/dist/codec-ids-B10ZZHzw.mjs.map +0 -1
- package/dist/codec-ids-CI144z7g.d.mts.map +0 -1
- package/dist/codec-types-CTfT4U4O.d.mts.map +0 -1
- package/dist/codecs-D7W9O0Xx.d.mts.map +0 -1
- package/dist/codecs-DRbb26Or.mjs.map +0 -1
- package/dist/control-policy-DtbkLLGv.mjs.map +0 -1
- package/dist/data-transform-DPzwBMhW.d.mts.map +0 -1
- package/dist/default-normalizer.mjs.map +0 -1
- package/dist/descriptor-meta-CFQKLXl_.mjs.map +0 -1
- package/dist/diff-database-schema-GncqMuSI.mjs.map +0 -1
- package/dist/errors-DO-w0EUS.mjs.map +0 -1
- package/dist/nodes-Dv5eCSq9.d.mts.map +0 -1
- package/dist/op-factory-call-Bn9zDx4_.d.mts.map +0 -1
- package/dist/planner-kql24i_F.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-BEY1T0Xm.d.mts.map +0 -1
- package/dist/planner-produced-postgres-migration-D7y-PEHK.mjs.map +0 -1
- package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +0 -1
- package/dist/postgres-database-schema-node-CJv5bfbs.d.mts.map +0 -1
- package/dist/postgres-migration-DbckK1su.d.mts.map +0 -1
- package/dist/postgres-migration-arMSWU9b.mjs.map +0 -1
- package/dist/postgres-rls-policy--CSddvMR.d.mts.map +0 -1
- package/dist/postgres-role-schema-node-Cm-j0ZGM.mjs.map +0 -1
- package/dist/postgres-schema-DKq6s1P3.d.mts.map +0 -1
- package/dist/table-source-BvFo7gVs.d.mts.map +0 -1
- package/dist/types-BDKkx8MA.d.mts.map +0 -1
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Placeholder-bearing plans: `renderTypeScript()` always succeeds and embeds
|
|
16
16
|
* `() => placeholder("slot")` at each stub. `operations`, in contrast, is
|
|
17
17
|
* _not safe to enumerate_ on a stub-bearing plan — `DataTransformCall.toOp()`
|
|
18
|
-
* throws `
|
|
18
|
+
* throws `MIGRATION.UNFILLED_PLACEHOLDER` because a planner-stubbed closure cannot be lowered
|
|
19
19
|
* to a runtime op. Callers that know a plan may carry stubs must render to
|
|
20
20
|
* `migration.ts`, let the user fill the slots, and re-load the edited
|
|
21
21
|
* migration before enumerating ops. The walk-schema planner does not emit
|
|
@@ -35,6 +35,7 @@ import type {
|
|
|
35
35
|
import { resolveValueSetValues } from '@prisma-next/family-sql/control';
|
|
36
36
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
37
37
|
import type { SchemaDiffIssue } from '@prisma-next/framework-components/control';
|
|
38
|
+
import { issueOutcome } from '@prisma-next/framework-components/control';
|
|
38
39
|
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
39
40
|
import {
|
|
40
41
|
type SqlStorage,
|
|
@@ -198,7 +199,7 @@ export const notNullBackfillCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
198
199
|
const calls: PostgresOpFactoryCall[] = [];
|
|
199
200
|
|
|
200
201
|
for (const issue of issues) {
|
|
201
|
-
if (issue
|
|
202
|
+
if (issueOutcome(issue) !== 'not-found') continue;
|
|
202
203
|
const node = issueNode(issue);
|
|
203
204
|
if (node === undefined || node.nodeKind !== RelationalSchemaNodeKind.column) continue;
|
|
204
205
|
const expected = blindCast<
|
|
@@ -263,7 +264,7 @@ export const typeChangeCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
|
263
264
|
const calls: PostgresOpFactoryCall[] = [];
|
|
264
265
|
|
|
265
266
|
for (const issue of issues) {
|
|
266
|
-
if (issue
|
|
267
|
+
if (issueOutcome(issue) !== 'not-equal') continue;
|
|
267
268
|
const pair = columnNodePair(issue);
|
|
268
269
|
if (pair === undefined) continue;
|
|
269
270
|
const { expected, actual } = pair;
|
|
@@ -357,7 +358,7 @@ export const nullableTighteningCallStrategy: CallMigrationStrategy = (issues, ct
|
|
|
357
358
|
const calls: PostgresOpFactoryCall[] = [];
|
|
358
359
|
|
|
359
360
|
for (const issue of issues) {
|
|
360
|
-
if (issue
|
|
361
|
+
if (issueOutcome(issue) !== 'not-equal') continue;
|
|
361
362
|
const pair = columnNodePair(issue);
|
|
362
363
|
if (pair === undefined) continue;
|
|
363
364
|
const { expected, actual } = pair;
|
|
@@ -590,7 +591,7 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
590
591
|
const mutableStorageTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
591
592
|
|
|
592
593
|
for (const issue of issues) {
|
|
593
|
-
if (issue
|
|
594
|
+
if (issueOutcome(issue) !== 'not-found') continue;
|
|
594
595
|
const node = issueNode(issue);
|
|
595
596
|
if (node === undefined || node.nodeKind !== RelationalSchemaNodeKind.column) continue;
|
|
596
597
|
const expected = blindCast<
|
|
@@ -685,8 +686,7 @@ function canUseSharedTemporaryDefaultStrategy(options: {
|
|
|
685
686
|
}
|
|
686
687
|
|
|
687
688
|
for (const foreignKey of table.foreignKeys) {
|
|
688
|
-
if (
|
|
689
|
-
continue;
|
|
689
|
+
if (!foreignKey.source.columns.includes(columnName)) continue;
|
|
690
690
|
if (!schemaLookup || !hasForeignKey(schemaLookup, foreignKey)) return false;
|
|
691
691
|
}
|
|
692
692
|
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
SchemaDiffIssue,
|
|
22
22
|
SchemaOwnership,
|
|
23
23
|
} from '@prisma-next/framework-components/control';
|
|
24
|
+
import { issueOutcome } from '@prisma-next/framework-components/control';
|
|
24
25
|
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
25
26
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
26
27
|
import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
|
|
@@ -211,7 +212,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
211
212
|
options.policy.allowedOperationClasses.includes('destructive');
|
|
212
213
|
const coalesced = coalesceSubtreeIssues(relationalDiffIssues);
|
|
213
214
|
const owned = retainUnownedExtras(coalesced, options.ownership, options.contract);
|
|
214
|
-
const gated = strict ? owned : owned.filter((issue) => issue
|
|
215
|
+
const gated = strict ? owned : owned.filter((issue) => issueOutcome(issue) !== 'not-expected');
|
|
215
216
|
|
|
216
217
|
// Namespace presence (`CREATE SCHEMA`) is a planner-only op-generation
|
|
217
218
|
// concern stitched in here rather than inside the shared diff — verify
|
|
@@ -369,7 +370,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
369
370
|
// 'not-equal' is unreachable for content-addressed policies: the wire name
|
|
370
371
|
// encodes the body hash, so two policies sharing a local key (same name)
|
|
371
372
|
// are always equal and isEqualTo never returns false.
|
|
372
|
-
if (issue
|
|
373
|
+
if (issueOutcome(issue) === 'not-found') {
|
|
373
374
|
const expected = issue.expected;
|
|
374
375
|
PostgresPolicySchemaNode.assert(expected);
|
|
375
376
|
// expected.namespaceId is the DDL schema name (resolved during projection);
|
|
@@ -381,7 +382,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
381
382
|
expected.namespaceId,
|
|
382
383
|
),
|
|
383
384
|
});
|
|
384
|
-
} else if (issue
|
|
385
|
+
} else if (issueOutcome(issue) === 'not-expected') {
|
|
385
386
|
const actual = issue.actual;
|
|
386
387
|
PostgresPolicySchemaNode.assert(actual);
|
|
387
388
|
extra.push({
|
|
@@ -505,7 +506,7 @@ function retainUnownedExtras(
|
|
|
505
506
|
): readonly SchemaDiffIssue<SqlSchemaDiffNode>[] {
|
|
506
507
|
if (ownership === undefined) return issues;
|
|
507
508
|
return issues.filter((issue) => {
|
|
508
|
-
if (issue
|
|
509
|
+
if (issueOutcome(issue) !== 'not-expected') return true;
|
|
509
510
|
const node = issueNode(issue);
|
|
510
511
|
if (node === undefined) return true;
|
|
511
512
|
const coordinate = postgresNodeStorageCoordinate(node);
|
|
@@ -8,6 +8,7 @@ import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
|
8
8
|
import type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
|
|
9
9
|
import { errorPostgresMigrationStackMissing } from '../errors';
|
|
10
10
|
import { PostgresContractView } from '../postgres-contract-view';
|
|
11
|
+
import { PostgresRlsPolicy, type PostgresRlsPolicyInput } from '../postgres-rls-policy';
|
|
11
12
|
import {
|
|
12
13
|
AddCheckConstraintCall,
|
|
13
14
|
AddColumnCall,
|
|
@@ -19,8 +20,10 @@ import {
|
|
|
19
20
|
type AlterColumnTypeOptions,
|
|
20
21
|
CreateIndexCall,
|
|
21
22
|
CreateNativeEnumTypeCall,
|
|
23
|
+
CreatePostgresRlsPolicyCall,
|
|
22
24
|
CreateSchemaCall,
|
|
23
25
|
CreateTableCall,
|
|
26
|
+
DisableRowLevelSecurityCall,
|
|
24
27
|
DropCheckConstraintCall,
|
|
25
28
|
DropColumnCall,
|
|
26
29
|
DropConstraintCall,
|
|
@@ -28,7 +31,10 @@ import {
|
|
|
28
31
|
DropIndexCall,
|
|
29
32
|
DropNativeEnumTypeCall,
|
|
30
33
|
DropNotNullCall,
|
|
34
|
+
DropPostgresRlsPolicyCall,
|
|
31
35
|
DropTableCall,
|
|
36
|
+
EnableRowLevelSecurityCall,
|
|
37
|
+
RenamePostgresRlsPolicyCall,
|
|
32
38
|
SetDefaultCall,
|
|
33
39
|
SetNotNullCall,
|
|
34
40
|
} from './op-factory-call';
|
|
@@ -75,7 +81,7 @@ export abstract class PostgresMigration<
|
|
|
75
81
|
* Materialized Postgres control adapter, created once per migration
|
|
76
82
|
* instance from the injected stack. `undefined` only when the migration
|
|
77
83
|
* was instantiated without a stack (test fixtures); `controlAdapterFor`
|
|
78
|
-
* throws a
|
|
84
|
+
* throws a MIGRATION.POSTGRES_CONTROL_STACK_MISSING in that case to surface the misuse.
|
|
79
85
|
*/
|
|
80
86
|
protected readonly controlAdapter: SqlControlAdapter<'postgres'> | undefined;
|
|
81
87
|
|
|
@@ -101,7 +107,7 @@ export abstract class PostgresMigration<
|
|
|
101
107
|
}
|
|
102
108
|
|
|
103
109
|
/**
|
|
104
|
-
* Returns the materialized control adapter, or throws a
|
|
110
|
+
* Returns the materialized control adapter, or throws a MIGRATION.POSTGRES_CONTROL_STACK_MISSING naming
|
|
105
111
|
* `operation` when the migration was constructed without a `ControlStack`.
|
|
106
112
|
* Single home for the null-check that every DDL/DML method shares.
|
|
107
113
|
*/
|
|
@@ -420,4 +426,58 @@ export abstract class PostgresMigration<
|
|
|
420
426
|
}): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
|
|
421
427
|
return installExtension(options, this.controlAdapterFor('installExtension'));
|
|
422
428
|
}
|
|
429
|
+
|
|
430
|
+
protected enableRowLevelSecurity(options: {
|
|
431
|
+
readonly schema: string;
|
|
432
|
+
readonly table: string;
|
|
433
|
+
}): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
|
|
434
|
+
return new EnableRowLevelSecurityCall(options.schema, options.table).toOp(
|
|
435
|
+
this.controlAdapterFor('enableRowLevelSecurity'),
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
protected disableRowLevelSecurity(options: {
|
|
440
|
+
readonly schema: string;
|
|
441
|
+
readonly table: string;
|
|
442
|
+
}): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
|
|
443
|
+
return new DisableRowLevelSecurityCall(options.schema, options.table).toOp(
|
|
444
|
+
this.controlAdapterFor('disableRowLevelSecurity'),
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
protected createRlsPolicy(options: {
|
|
449
|
+
readonly schema: string;
|
|
450
|
+
readonly table: string;
|
|
451
|
+
readonly policy: PostgresRlsPolicyInput;
|
|
452
|
+
}): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
|
|
453
|
+
return new CreatePostgresRlsPolicyCall(
|
|
454
|
+
options.schema,
|
|
455
|
+
options.table,
|
|
456
|
+
new PostgresRlsPolicy(options.policy),
|
|
457
|
+
).toOp(this.controlAdapterFor('createRlsPolicy'));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
protected dropRlsPolicy(options: {
|
|
461
|
+
readonly schema: string;
|
|
462
|
+
readonly table: string;
|
|
463
|
+
readonly policy: string;
|
|
464
|
+
}): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
|
|
465
|
+
return new DropPostgresRlsPolicyCall(options.schema, options.table, options.policy).toOp(
|
|
466
|
+
this.controlAdapterFor('dropRlsPolicy'),
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
protected renameRlsPolicy(options: {
|
|
471
|
+
readonly schema: string;
|
|
472
|
+
readonly table: string;
|
|
473
|
+
readonly from: string;
|
|
474
|
+
readonly to: string;
|
|
475
|
+
}): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
|
|
476
|
+
return new RenamePostgresRlsPolicyCall(
|
|
477
|
+
options.schema,
|
|
478
|
+
options.table,
|
|
479
|
+
options.from,
|
|
480
|
+
options.to,
|
|
481
|
+
).toOp(this.controlAdapterFor('renameRlsPolicy'));
|
|
482
|
+
}
|
|
423
483
|
}
|
|
@@ -137,7 +137,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
137
137
|
frameworkComponents: options.frameworkComponents,
|
|
138
138
|
});
|
|
139
139
|
if (!schemaVerifyResult.ok) {
|
|
140
|
-
return runnerFailure('SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {
|
|
140
|
+
return runnerFailure('MIGRATION.SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {
|
|
141
141
|
why: 'The resulting database schema does not satisfy the destination contract.',
|
|
142
142
|
meta: {
|
|
143
143
|
issues: schemaVerifyResult.schema.issues,
|
|
@@ -313,7 +313,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
313
313
|
return okVoid();
|
|
314
314
|
}
|
|
315
315
|
return runnerFailure(
|
|
316
|
-
'LEGACY_MARKER_SHAPE',
|
|
316
|
+
'MIGRATION.LEGACY_MARKER_SHAPE',
|
|
317
317
|
'Legacy marker-table shape detected on prisma_contract.marker (no `space` column). ' +
|
|
318
318
|
'Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. ' +
|
|
319
319
|
'Drop `prisma_contract.marker` and re-run `dbInit` to reinitialise from a clean baseline.',
|
|
@@ -335,7 +335,8 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
335
335
|
for (const step of steps) {
|
|
336
336
|
const result = await driver.query(step.sql, step.params ?? []);
|
|
337
337
|
if (!this.stepResultIsTrue(result.rows)) {
|
|
338
|
-
const code =
|
|
338
|
+
const code =
|
|
339
|
+
phase === 'precheck' ? 'MIGRATION.PRECHECK_FAILED' : 'MIGRATION.POSTCHECK_FAILED';
|
|
339
340
|
return runnerFailure(
|
|
340
341
|
code,
|
|
341
342
|
`Operation ${operation.id} failed during ${phase}: ${step.description}`,
|
|
@@ -363,7 +364,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
363
364
|
} catch (error: unknown) {
|
|
364
365
|
if (SqlQueryError.is(error)) {
|
|
365
366
|
return runnerFailure(
|
|
366
|
-
'EXECUTION_FAILED',
|
|
367
|
+
'MIGRATION.EXECUTION_FAILED',
|
|
367
368
|
`Operation ${operation.id} failed during execution: ${step.description}`,
|
|
368
369
|
{
|
|
369
370
|
why: error.message,
|
|
@@ -487,7 +488,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
487
488
|
for (const operation of operations) {
|
|
488
489
|
if (!allowedClasses.has(operation.operationClass)) {
|
|
489
490
|
return runnerFailure(
|
|
490
|
-
'POLICY_VIOLATION',
|
|
491
|
+
'MIGRATION.POLICY_VIOLATION',
|
|
491
492
|
`Operation ${operation.id} has class "${operation.operationClass}" which is not allowed by policy.`,
|
|
492
493
|
{
|
|
493
494
|
why: `Policy only allows: ${policy.allowedOperationClasses.join(', ')}.`,
|
|
@@ -517,7 +518,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
517
518
|
|
|
518
519
|
if (!marker) {
|
|
519
520
|
return runnerFailure(
|
|
520
|
-
'MARKER_ORIGIN_MISMATCH',
|
|
521
|
+
'MIGRATION.MARKER_ORIGIN_MISMATCH',
|
|
521
522
|
`Missing contract marker: expected origin storage hash ${origin.storageHash}.`,
|
|
522
523
|
{
|
|
523
524
|
meta: {
|
|
@@ -528,7 +529,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
528
529
|
}
|
|
529
530
|
if (marker.storageHash !== origin.storageHash) {
|
|
530
531
|
return runnerFailure(
|
|
531
|
-
'MARKER_ORIGIN_MISMATCH',
|
|
532
|
+
'MIGRATION.MARKER_ORIGIN_MISMATCH',
|
|
532
533
|
`Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`,
|
|
533
534
|
{
|
|
534
535
|
meta: {
|
|
@@ -540,7 +541,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
540
541
|
}
|
|
541
542
|
if (origin.profileHash && marker.profileHash !== origin.profileHash) {
|
|
542
543
|
return runnerFailure(
|
|
543
|
-
'MARKER_ORIGIN_MISMATCH',
|
|
544
|
+
'MIGRATION.MARKER_ORIGIN_MISMATCH',
|
|
544
545
|
`Existing contract marker profile hash (${marker.profileHash}) does not match plan origin profile hash (${origin.profileHash}).`,
|
|
545
546
|
{
|
|
546
547
|
meta: {
|
|
@@ -559,7 +560,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
559
560
|
): Result<void, SqlMigrationRunnerFailure> {
|
|
560
561
|
if (destination.storageHash !== contract.storage.storageHash) {
|
|
561
562
|
return runnerFailure(
|
|
562
|
-
'DESTINATION_CONTRACT_MISMATCH',
|
|
563
|
+
'MIGRATION.DESTINATION_CONTRACT_MISMATCH',
|
|
563
564
|
`Plan destination storage hash (${destination.storageHash}) does not match provided contract storage hash (${contract.storage.storageHash}).`,
|
|
564
565
|
{
|
|
565
566
|
meta: {
|
|
@@ -575,7 +576,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
575
576
|
destination.profileHash !== contract.profileHash
|
|
576
577
|
) {
|
|
577
578
|
return runnerFailure(
|
|
578
|
-
'DESTINATION_CONTRACT_MISMATCH',
|
|
579
|
+
'MIGRATION.DESTINATION_CONTRACT_MISMATCH',
|
|
579
580
|
`Plan destination profile hash (${destination.profileHash}) does not match provided contract profile hash (${contract.profileHash}).`,
|
|
580
581
|
{
|
|
581
582
|
meta: {
|
|
@@ -614,7 +615,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
614
615
|
});
|
|
615
616
|
if (!updated) {
|
|
616
617
|
return runnerFailure(
|
|
617
|
-
'MARKER_CAS_FAILURE',
|
|
618
|
+
'MIGRATION.MARKER_CAS_FAILURE',
|
|
618
619
|
'Marker was modified by another process during migration execution.',
|
|
619
620
|
{
|
|
620
621
|
meta: {
|
|
@@ -162,6 +162,15 @@ type ForeignKeyResolution = {
|
|
|
162
162
|
readonly extraRelationsByTable: ReadonlyMap<string, readonly RelationField[]>;
|
|
163
163
|
/** Synthetic field-name maps for cross-space-referenced pack tables, merged into `fieldNamesByTable`. */
|
|
164
164
|
readonly crossSpaceFieldNamesByTable: ReadonlyMap<string, TableColumnFieldNameMap>;
|
|
165
|
+
/** Dangling foreign keys dropped per host table, kept so the model can explain the drop. */
|
|
166
|
+
readonly danglingForeignKeysByTable: ReadonlyMap<string, readonly DanglingForeignKeyInfo[]>;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/** A foreign key dropped because its target lives outside the introspected scope. */
|
|
170
|
+
type DanglingForeignKeyInfo = {
|
|
171
|
+
readonly columns: readonly string[];
|
|
172
|
+
readonly referencedSchema: string | undefined;
|
|
173
|
+
readonly referencedTable: string;
|
|
165
174
|
};
|
|
166
175
|
|
|
167
176
|
/**
|
|
@@ -198,6 +207,7 @@ function resolveForeignKeys(
|
|
|
198
207
|
const resultTables: Record<string, SqlTableIR> = {};
|
|
199
208
|
const extraRelationsByTable = new Map<string, RelationField[]>();
|
|
200
209
|
const crossSpaceFieldNamesByTable = new Map<string, TableColumnFieldNameMap>();
|
|
210
|
+
const danglingForeignKeysByTable = new Map<string, DanglingForeignKeyInfo[]>();
|
|
201
211
|
|
|
202
212
|
for (const [tableName, table] of Object.entries(tables)) {
|
|
203
213
|
const keptForeignKeys: SqlForeignKeyIR[] = [];
|
|
@@ -248,9 +258,22 @@ function resolveForeignKeys(
|
|
|
248
258
|
|
|
249
259
|
// Not a pack-owned coordinate: keep the foreign key if the referenced
|
|
250
260
|
// table survived introspection (local), otherwise drop it while keeping
|
|
251
|
-
// the scalar column (dangling)
|
|
261
|
+
// the scalar column (dangling) — recording it so the model can explain
|
|
262
|
+
// the drop instead of the relation vanishing without a trace.
|
|
252
263
|
if (tables[fk.referencedTable] !== undefined) {
|
|
253
264
|
keptForeignKeys.push(fk);
|
|
265
|
+
} else {
|
|
266
|
+
const dangling: DanglingForeignKeyInfo = {
|
|
267
|
+
columns: fk.columns,
|
|
268
|
+
referencedSchema: fk.referencedSchema,
|
|
269
|
+
referencedTable: fk.referencedTable,
|
|
270
|
+
};
|
|
271
|
+
const existingDangling = danglingForeignKeysByTable.get(tableName);
|
|
272
|
+
if (existingDangling) {
|
|
273
|
+
existingDangling.push(dangling);
|
|
274
|
+
} else {
|
|
275
|
+
danglingForeignKeysByTable.set(tableName, [dangling]);
|
|
276
|
+
}
|
|
254
277
|
}
|
|
255
278
|
}
|
|
256
279
|
|
|
@@ -260,7 +283,12 @@ function resolveForeignKeys(
|
|
|
260
283
|
: new SqlTableIR({ ...table, foreignKeys: keptForeignKeys });
|
|
261
284
|
}
|
|
262
285
|
|
|
263
|
-
return {
|
|
286
|
+
return {
|
|
287
|
+
tables: resultTables,
|
|
288
|
+
extraRelationsByTable,
|
|
289
|
+
crossSpaceFieldNamesByTable,
|
|
290
|
+
danglingForeignKeysByTable,
|
|
291
|
+
};
|
|
264
292
|
}
|
|
265
293
|
|
|
266
294
|
/**
|
|
@@ -405,6 +433,7 @@ export function inferPostgresPslContract(
|
|
|
405
433
|
tables: resolvedTables,
|
|
406
434
|
extraRelationsByTable,
|
|
407
435
|
crossSpaceFieldNamesByTable,
|
|
436
|
+
danglingForeignKeysByTable,
|
|
408
437
|
} = resolveForeignKeys(tables, owners);
|
|
409
438
|
const schemaIR = new SqlSchemaIR({ tables: resolvedTables });
|
|
410
439
|
|
|
@@ -437,6 +466,7 @@ export function inferPostgresPslContract(
|
|
|
437
466
|
{
|
|
438
467
|
extraRelationsByTable,
|
|
439
468
|
crossSpaceFieldNamesByTable,
|
|
469
|
+
danglingForeignKeysByTable,
|
|
440
470
|
},
|
|
441
471
|
wrapNamespaceName,
|
|
442
472
|
);
|
|
@@ -447,12 +477,13 @@ export function buildPslDocumentAst(
|
|
|
447
477
|
options: PslPrinterOptions,
|
|
448
478
|
foreignKeyExtras: Pick<
|
|
449
479
|
ForeignKeyResolution,
|
|
450
|
-
'extraRelationsByTable' | 'crossSpaceFieldNamesByTable'
|
|
480
|
+
'extraRelationsByTable' | 'crossSpaceFieldNamesByTable' | 'danglingForeignKeysByTable'
|
|
451
481
|
>,
|
|
452
482
|
namespaceName?: string,
|
|
453
483
|
): PslDocumentAst {
|
|
454
484
|
const { typeMap, defaultMapping, parseRawDefault: rawDefaultParser } = options;
|
|
455
|
-
const { extraRelationsByTable, crossSpaceFieldNamesByTable } =
|
|
485
|
+
const { extraRelationsByTable, crossSpaceFieldNamesByTable, danglingForeignKeysByTable } =
|
|
486
|
+
foreignKeyExtras;
|
|
456
487
|
|
|
457
488
|
const modelNames = buildTopLevelNameMap(
|
|
458
489
|
Object.keys(schemaIR.tables),
|
|
@@ -508,6 +539,7 @@ export function buildPslDocumentAst(
|
|
|
508
539
|
...(relationsByTable.get(table.name) ?? []),
|
|
509
540
|
...(extraRelationsByTable.get(table.name) ?? []),
|
|
510
541
|
],
|
|
542
|
+
danglingForeignKeysByTable.get(table.name) ?? [],
|
|
511
543
|
),
|
|
512
544
|
);
|
|
513
545
|
}
|
|
@@ -642,6 +674,7 @@ function buildModel(
|
|
|
642
674
|
defaultMapping: DefaultMappingOptions | undefined,
|
|
643
675
|
rawDefaultParser: PslPrinterOptions['parseRawDefault'],
|
|
644
676
|
relationFields: readonly RelationField[],
|
|
677
|
+
danglingForeignKeys: readonly DanglingForeignKeyInfo[],
|
|
645
678
|
): PslModel {
|
|
646
679
|
const { name: modelName, map: mapName } = toModelName(table.name);
|
|
647
680
|
const fieldNameMap = fieldNamesByTable.get(table.name);
|
|
@@ -719,13 +752,23 @@ function buildModel(
|
|
|
719
752
|
modelAttributes.push(buildMapAttribute('model', mapName));
|
|
720
753
|
}
|
|
721
754
|
|
|
722
|
-
// Surface introspection
|
|
723
|
-
//
|
|
724
|
-
//
|
|
725
|
-
//
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
755
|
+
// Surface introspection advisories the user would otherwise have no way to
|
|
756
|
+
// discover from the emitted PSL alone. Both warnings are part of the
|
|
757
|
+
// emitted SQL output and are asserted byte-for-byte, so keep the exact
|
|
758
|
+
// wording; a table hitting both is combined onto the single comment line
|
|
759
|
+
// `PslModel.comment` supports.
|
|
760
|
+
const warnings: string[] = [];
|
|
761
|
+
if (!table.primaryKey) {
|
|
762
|
+
// Tables without a primary key cannot serve as the right-hand side of a
|
|
763
|
+
// `findUnique`-style query downstream, so the user should add an `@id`.
|
|
764
|
+
warnings.push('This table has no primary key in the database');
|
|
765
|
+
}
|
|
766
|
+
if (danglingForeignKeys.length > 0) {
|
|
767
|
+
warnings.push(
|
|
768
|
+
buildDanglingForeignKeyWarning(danglingForeignKeys, fieldNamesByTable, table.name),
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
const comment = warnings.length > 0 ? `// WARNING: ${warnings.join(' ')}` : undefined;
|
|
729
772
|
|
|
730
773
|
return {
|
|
731
774
|
kind: 'model',
|
|
@@ -737,6 +780,35 @@ function buildModel(
|
|
|
737
780
|
};
|
|
738
781
|
}
|
|
739
782
|
|
|
783
|
+
/**
|
|
784
|
+
* Explains a foreign key `resolveForeignKeys` dropped as dangling: the
|
|
785
|
+
* database enforces it, but its target lives outside the introspected
|
|
786
|
+
* schema, so infer has no model to point a relation at. The suggested fix
|
|
787
|
+
* targets the common cause — an unconfigured extension pack's schema
|
|
788
|
+
* (e.g. Supabase's `auth`).
|
|
789
|
+
*/
|
|
790
|
+
function buildDanglingForeignKeyWarning(
|
|
791
|
+
danglingForeignKeys: readonly DanglingForeignKeyInfo[],
|
|
792
|
+
fieldNamesByTable: ReadonlyMap<string, TableColumnFieldNameMap>,
|
|
793
|
+
tableName: string,
|
|
794
|
+
): string {
|
|
795
|
+
const descriptions = danglingForeignKeys.map((fk) => {
|
|
796
|
+
const fieldNames = fk.columns.map((columnName) =>
|
|
797
|
+
resolveColumnFieldName(fieldNamesByTable, tableName, columnName),
|
|
798
|
+
);
|
|
799
|
+
const target =
|
|
800
|
+
fk.referencedSchema !== undefined
|
|
801
|
+
? `${fk.referencedSchema}.${fk.referencedTable}`
|
|
802
|
+
: fk.referencedTable;
|
|
803
|
+
return `"${fieldNames.join(', ')}" -> "${target}"`;
|
|
804
|
+
});
|
|
805
|
+
return (
|
|
806
|
+
`Foreign key ${descriptions.join(', ')} exists in the database, but its target schema is ` +
|
|
807
|
+
'outside the introspected scope, so no relation field was generated. If the target schema ' +
|
|
808
|
+
'is described by an extension pack, add it to extensionPacks and re-run infer.'
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
740
812
|
function buildScalarField(
|
|
741
813
|
column: SqlColumnIR,
|
|
742
814
|
table: SqlTableIR,
|
|
@@ -799,7 +871,32 @@ function buildScalarField(
|
|
|
799
871
|
attributes.push(buildSimpleConstraintFieldAttribute('id', singlePkConstraintName));
|
|
800
872
|
}
|
|
801
873
|
|
|
802
|
-
if (
|
|
874
|
+
if (
|
|
875
|
+
column.default === undefined &&
|
|
876
|
+
column.resolvedDefault?.kind === 'function' &&
|
|
877
|
+
column.resolvedDefault.expression === 'autoincrement()'
|
|
878
|
+
) {
|
|
879
|
+
// An identity column: a `resolvedDefault` with no raw `default` is the
|
|
880
|
+
// only introspected shape the control adapter produces for
|
|
881
|
+
// `GENERATED ... AS IDENTITY` (Postgres reports no `column_default`;
|
|
882
|
+
// the adapter stamps `autoincrement()` directly). There is no
|
|
883
|
+
// `identity` field on the column IR — this pairing is the marker.
|
|
884
|
+
attributes.push(parseDefaultAttributeString('@default(autoincrement())'));
|
|
885
|
+
} else if (column.many === true && column.resolvedDefault?.kind === 'literal') {
|
|
886
|
+
// A list column's default must print from `resolvedDefault`: the raw SQL
|
|
887
|
+
// text (e.g. `'{}'::text[]`) only parses to `dbgenerated(...)`, which the
|
|
888
|
+
// interpreter rejects on a list column (lists accept literal defaults
|
|
889
|
+
// only). PSL literal-list elements are string/number/boolean only, so a
|
|
890
|
+
// resolved value holding anything else has no spelling — the default is
|
|
891
|
+
// then omitted, which verify reports as a live-only "extra", not a
|
|
892
|
+
// false mismatch.
|
|
893
|
+
const formatted = Array.isArray(column.resolvedDefault.value)
|
|
894
|
+
? formatPslListLiteralValue(column.resolvedDefault.value)
|
|
895
|
+
: undefined;
|
|
896
|
+
if (formatted !== undefined) {
|
|
897
|
+
attributes.push(parseDefaultAttributeString(`@default(${formatted})`));
|
|
898
|
+
}
|
|
899
|
+
} else if (column.default !== undefined) {
|
|
803
900
|
const parsed = parseColumnDefault(column.default, column.nativeType, rawDefaultParser);
|
|
804
901
|
if (parsed) {
|
|
805
902
|
const result = mapDefault(parsed, defaultMapping);
|
|
@@ -989,6 +1086,27 @@ function escapePslString(value: string): string {
|
|
|
989
1086
|
.replace(/\r/g, '\\r');
|
|
990
1087
|
}
|
|
991
1088
|
|
|
1089
|
+
/**
|
|
1090
|
+
* Formats a resolved literal-default array as PSL literal-list syntax
|
|
1091
|
+
* (`[1, 2, 3]`, `["a", "b"]`, `[]`). PSL's list-literal grammar only accepts
|
|
1092
|
+
* string/number/boolean elements, so any other element (e.g. `null`, a
|
|
1093
|
+
* nested array/object) makes the value unrepresentable and this returns
|
|
1094
|
+
* `undefined`.
|
|
1095
|
+
*/
|
|
1096
|
+
function formatPslListLiteralValue(elements: readonly unknown[]): string | undefined {
|
|
1097
|
+
const parts: string[] = [];
|
|
1098
|
+
for (const element of elements) {
|
|
1099
|
+
if (typeof element === 'string') {
|
|
1100
|
+
parts.push(`"${escapePslString(element)}"`);
|
|
1101
|
+
} else if (typeof element === 'number' || typeof element === 'boolean') {
|
|
1102
|
+
parts.push(String(element));
|
|
1103
|
+
} else {
|
|
1104
|
+
return undefined;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
return `[${parts.join(', ')}]`;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
992
1110
|
/**
|
|
993
1111
|
* Resolves a `SqlColumnIR.default` value into a normalized {@link ColumnDefault}.
|
|
994
1112
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { DiffableNode } from '@prisma-next/framework-components/control';
|
|
1
|
+
import type { DiffableNode, SchemaNodeRef } from '@prisma-next/framework-components/control';
|
|
2
2
|
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
3
|
-
import { assertNode, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
3
|
+
import { assertNode, defineNonEnumerable, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
4
4
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
5
5
|
import type { RlsPolicyOperation } from '../postgres-rls-policy';
|
|
6
6
|
import { PostgresSchemaNodeKind } from './schema-node-kinds';
|
|
@@ -23,6 +23,13 @@ export interface PostgresPolicySchemaNodeInput {
|
|
|
23
23
|
readonly withCheck?: string;
|
|
24
24
|
/** `true` = `AS PERMISSIVE`, `false` = `AS RESTRICTIVE`. */
|
|
25
25
|
readonly permissive: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* This policy's table node, plus one entry per role it grants to — each
|
|
28
|
+
* as the root-anchored chain the differ pairs siblings with. Stamped by
|
|
29
|
+
* the derivation, which holds the parent (database/namespace) context.
|
|
30
|
+
* Never compared by `isEqualTo`.
|
|
31
|
+
*/
|
|
32
|
+
readonly dependsOn?: readonly SchemaNodeRef[];
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
/**
|
|
@@ -48,6 +55,8 @@ export class PostgresPolicySchemaNode extends SqlSchemaIRNode implements Diffabl
|
|
|
48
55
|
declare readonly using?: string;
|
|
49
56
|
declare readonly withCheck?: string;
|
|
50
57
|
readonly permissive: boolean;
|
|
58
|
+
/** See {@link PostgresPolicySchemaNodeInput.dependsOn}. Non-enumerable so it stays out of JSON and structural equality, matching `SqlColumnIR.codecRef`. */
|
|
59
|
+
declare readonly dependsOn?: readonly SchemaNodeRef[];
|
|
51
60
|
|
|
52
61
|
constructor(input: PostgresPolicySchemaNodeInput) {
|
|
53
62
|
super();
|
|
@@ -60,6 +69,7 @@ export class PostgresPolicySchemaNode extends SqlSchemaIRNode implements Diffabl
|
|
|
60
69
|
if (input.using !== undefined) this.using = input.using;
|
|
61
70
|
if (input.withCheck !== undefined) this.withCheck = input.withCheck;
|
|
62
71
|
this.permissive = input.permissive;
|
|
72
|
+
defineNonEnumerable(this, 'dependsOn', input.dependsOn);
|
|
63
73
|
freezeNode(this);
|
|
64
74
|
}
|
|
65
75
|
|
package/src/exports/codecs.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type {
|
|
|
2
2
|
PgBitDescriptor,
|
|
3
3
|
PgBoolDescriptor,
|
|
4
4
|
PgCharDescriptor,
|
|
5
|
+
PgDateDescriptor,
|
|
5
6
|
PgEnumCodec,
|
|
6
7
|
PgEnumDescriptor,
|
|
7
8
|
PgFloat4Descriptor,
|
|
@@ -29,6 +30,7 @@ export {
|
|
|
29
30
|
pgBitColumn,
|
|
30
31
|
pgBoolColumn,
|
|
31
32
|
pgCharColumn,
|
|
33
|
+
pgDateColumn,
|
|
32
34
|
pgEnumDescriptor,
|
|
33
35
|
pgFloat4Column,
|
|
34
36
|
pgFloat8Column,
|
package/src/exports/control.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
import type { StorageColumn } from '@prisma-next/sql-contract/types';
|
|
10
10
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
11
11
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
12
|
+
import { postgresResolveDefault } from '../core/default-normalizer';
|
|
12
13
|
import { postgresTargetDescriptorMeta } from '../core/descriptor-meta';
|
|
13
14
|
import { contractToPostgresDatabaseSchemaNode } from '../core/migrations/contract-to-postgres-database-schema-node';
|
|
14
15
|
import { diffPostgresSchema } from '../core/migrations/diff-database-schema';
|
|
@@ -64,6 +65,7 @@ const postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresP
|
|
|
64
65
|
annotationNamespace: 'pg',
|
|
65
66
|
...ifDefined('expandNativeType', expander),
|
|
66
67
|
renderDefault: postgresRenderDefault,
|
|
68
|
+
resolveDefault: postgresResolveDefault,
|
|
67
69
|
});
|
|
68
70
|
},
|
|
69
71
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { parsePostgresDefault } from '../core/default-normalizer';
|
|
1
|
+
export { parsePostgresDefault, postgresResolveDefault } from '../core/default-normalizer';
|
|
@@ -10,9 +10,11 @@ export {
|
|
|
10
10
|
CreateExtensionCall,
|
|
11
11
|
CreateIndexCall,
|
|
12
12
|
CreateNativeEnumTypeCall,
|
|
13
|
+
CreatePostgresRlsPolicyCall,
|
|
13
14
|
CreateSchemaCall,
|
|
14
15
|
CreateTableCall,
|
|
15
16
|
DataTransformCall,
|
|
17
|
+
DisableRowLevelSecurityCall,
|
|
16
18
|
DropCheckConstraintCall,
|
|
17
19
|
DropColumnCall,
|
|
18
20
|
DropConstraintCall,
|
|
@@ -20,8 +22,11 @@ export {
|
|
|
20
22
|
DropIndexCall,
|
|
21
23
|
DropNativeEnumTypeCall,
|
|
22
24
|
DropNotNullCall,
|
|
25
|
+
DropPostgresRlsPolicyCall,
|
|
23
26
|
DropTableCall,
|
|
27
|
+
EnableRowLevelSecurityCall,
|
|
24
28
|
RawSqlCall,
|
|
29
|
+
RenamePostgresRlsPolicyCall,
|
|
25
30
|
SetDefaultCall,
|
|
26
31
|
SetNotNullCall,
|
|
27
32
|
} from '../core/migrations/op-factory-call';
|
package/src/exports/types.ts
CHANGED
|
@@ -40,4 +40,5 @@ export {
|
|
|
40
40
|
PostgresTableSchemaNode,
|
|
41
41
|
type PostgresTableSchemaNodeInput,
|
|
42
42
|
} from '../core/schema-ir/postgres-table-schema-node';
|
|
43
|
+
export { PostgresSchemaNodeKind } from '../core/schema-ir/schema-node-kinds';
|
|
43
44
|
export type { PostgresColumnDefault } from '../core/types';
|