@prisma-next/target-postgres 0.14.0-dev.5 → 0.14.0-dev.51
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
|
@@ -40,8 +40,10 @@ import { blindCast } from '@prisma-next/utils/casts';
|
|
|
40
40
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
41
41
|
import { columnExistsAst, tableExistsAst } from '../../contract-free/checks';
|
|
42
42
|
import * as contractFreeDdl from '../../contract-free/ddl';
|
|
43
|
+
import type { PostgresRlsPolicy } from '../postgres-rls-policy';
|
|
43
44
|
import { escapeLiteral, quoteIdentifier } from '../sql-utils';
|
|
44
45
|
import type { PostgresColumnDefault } from '../types';
|
|
46
|
+
import { boundSchema } from './bound-schema';
|
|
45
47
|
import {
|
|
46
48
|
addNotNullColumnDirect,
|
|
47
49
|
alterColumnType,
|
|
@@ -61,6 +63,7 @@ import {
|
|
|
61
63
|
} from './operations/constraints';
|
|
62
64
|
import { createExtension } from './operations/dependencies';
|
|
63
65
|
import { createIndex, dropIndex } from './operations/indexes';
|
|
66
|
+
import { createRlsPolicy, dropRlsPolicy, enableRowLevelSecurity } from './operations/rls';
|
|
64
67
|
import type { ForeignKeySpec } from './operations/shared';
|
|
65
68
|
import { step, targetDetails } from './operations/shared';
|
|
66
69
|
import { dropTable } from './operations/tables';
|
|
@@ -83,10 +86,6 @@ type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
|
83
86
|
// transitive package on disk.
|
|
84
87
|
const POSTGRES_MIGRATION_FACADE = '@prisma-next/postgres/migration';
|
|
85
88
|
|
|
86
|
-
function boundSchema(schemaName: string): string | undefined {
|
|
87
|
-
return schemaName === UNBOUND_NAMESPACE_ID ? undefined : schemaName;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
89
|
abstract class PostgresOpFactoryCallNode extends TsExpression implements FrameworkOpFactoryCall {
|
|
91
90
|
abstract readonly factoryName: string;
|
|
92
91
|
abstract readonly operationClass: MigrationOperationClass;
|
|
@@ -169,6 +168,8 @@ function renderDdlConstraintAsTsCall(constraint: DdlTableConstraint): string {
|
|
|
169
168
|
const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
|
|
170
169
|
return `unique(${jsonToTsSource(constraint.columns)}${nameOpt})`;
|
|
171
170
|
}
|
|
171
|
+
case 'check-expression':
|
|
172
|
+
return `checkExpression(${jsonToTsSource(constraint.name)}, ${jsonToTsSource(constraint.expression)})`;
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
|
|
@@ -183,6 +184,7 @@ function constraintImportSymbols(constraints: readonly DdlTableConstraint[] | un
|
|
|
183
184
|
if (c.kind === 'primary-key') symbols.add('primaryKey');
|
|
184
185
|
else if (c.kind === 'foreign-key') symbols.add('foreignKey');
|
|
185
186
|
else if (c.kind === 'unique') symbols.add('unique');
|
|
187
|
+
else if (c.kind === 'check-expression') symbols.add('checkExpression');
|
|
186
188
|
}
|
|
187
189
|
return [...symbols];
|
|
188
190
|
}
|
|
@@ -684,10 +686,8 @@ export class DropDefaultCall extends PostgresOpFactoryCallNode {
|
|
|
684
686
|
|
|
685
687
|
/**
|
|
686
688
|
* Planner-internal call for adding a NOT NULL column (no contract default) to
|
|
687
|
-
* a table that must be empty at migration time. Carries the
|
|
688
|
-
*
|
|
689
|
-
* DDL) plus the parameters needed to lower the typed pre/postchecks at render
|
|
690
|
-
* time.
|
|
689
|
+
* a table that must be empty at migration time. Carries the typed `DdlColumn`
|
|
690
|
+
* and lowers it to an ADD COLUMN execute step via the adapter at `toOp` time.
|
|
691
691
|
*
|
|
692
692
|
* No authored `PostgresMigration` method: this call is only emitted by the
|
|
693
693
|
* planner, never hand-written by migration authors.
|
|
@@ -698,15 +698,15 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
|
|
|
698
698
|
readonly schemaName: string;
|
|
699
699
|
readonly tableName: string;
|
|
700
700
|
readonly columnName: string;
|
|
701
|
-
readonly
|
|
701
|
+
readonly column: DdlColumn;
|
|
702
702
|
readonly label: string;
|
|
703
703
|
|
|
704
|
-
constructor(schemaName: string, tableName: string, columnName: string,
|
|
704
|
+
constructor(schemaName: string, tableName: string, columnName: string, column: DdlColumn) {
|
|
705
705
|
super();
|
|
706
706
|
this.schemaName = schemaName;
|
|
707
707
|
this.tableName = tableName;
|
|
708
708
|
this.columnName = columnName;
|
|
709
|
-
this.
|
|
709
|
+
this.column = column;
|
|
710
710
|
this.label = `Add column ${columnName} to ${tableName}`;
|
|
711
711
|
this.freeze();
|
|
712
712
|
}
|
|
@@ -717,13 +717,7 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
|
|
|
717
717
|
`AddNotNullColumnDirectCall.toOp: a lowerer is required on the Postgres planner path (column "${this.columnName}" on table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
718
718
|
);
|
|
719
719
|
}
|
|
720
|
-
return addNotNullColumnDirect(
|
|
721
|
-
this.schemaName,
|
|
722
|
-
this.tableName,
|
|
723
|
-
this.columnName,
|
|
724
|
-
this.executeStepSql,
|
|
725
|
-
lowerer,
|
|
726
|
-
);
|
|
720
|
+
return addNotNullColumnDirect(this.schemaName, this.tableName, this.column, lowerer);
|
|
727
721
|
}
|
|
728
722
|
|
|
729
723
|
renderTypeScript(): string {
|
|
@@ -738,9 +732,9 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
|
|
|
738
732
|
/**
|
|
739
733
|
* Planner-internal call for adding a NOT NULL column (no contract default)
|
|
740
734
|
* using a temporary default value for non-empty tables. Carries all parameters
|
|
741
|
-
* needed for `buildAddNotNullColumnWithTemporaryDefaultOperation`; the
|
|
742
|
-
* pre/postchecks are lowered
|
|
743
|
-
*
|
|
735
|
+
* needed for `buildAddNotNullColumnWithTemporaryDefaultOperation`; both the
|
|
736
|
+
* typed ADD COLUMN execute step and the pre/postchecks are lowered via the
|
|
737
|
+
* adapter at `toOp` time.
|
|
744
738
|
*
|
|
745
739
|
* No authored `PostgresMigration` method: this call is only emitted by the
|
|
746
740
|
* planner, never hand-written by migration authors.
|
|
@@ -1376,6 +1370,97 @@ export class DataTransformCall extends PostgresOpFactoryCallNode {
|
|
|
1376
1370
|
}
|
|
1377
1371
|
}
|
|
1378
1372
|
|
|
1373
|
+
export class CreatePostgresRlsPolicyCall extends PostgresOpFactoryCallNode {
|
|
1374
|
+
readonly factoryName = 'createRlsPolicy' as const;
|
|
1375
|
+
readonly operationClass = 'additive' as const;
|
|
1376
|
+
readonly schemaName: string;
|
|
1377
|
+
readonly tableName: string;
|
|
1378
|
+
readonly policy: PostgresRlsPolicy;
|
|
1379
|
+
readonly label: string;
|
|
1380
|
+
|
|
1381
|
+
constructor(schemaName: string, tableName: string, policy: PostgresRlsPolicy) {
|
|
1382
|
+
super();
|
|
1383
|
+
this.schemaName = schemaName;
|
|
1384
|
+
this.tableName = tableName;
|
|
1385
|
+
this.policy = policy;
|
|
1386
|
+
this.label = `Create RLS policy "${policy.name}" on "${tableName}"`;
|
|
1387
|
+
this.freeze();
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1391
|
+
if (lowerer === undefined) {
|
|
1392
|
+
throw new Error(
|
|
1393
|
+
`CreatePostgresRlsPolicyCall.toOp: a lowerer is required on the Postgres planner path (policy "${this.policy.name}" on table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
return createRlsPolicy(this.schemaName, this.tableName, this.policy, lowerer);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
renderTypeScript(): string {
|
|
1400
|
+
return `createRlsPolicy(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.policy)})`;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
export class DropPostgresRlsPolicyCall extends PostgresOpFactoryCallNode {
|
|
1405
|
+
readonly factoryName = 'dropRlsPolicy' as const;
|
|
1406
|
+
readonly operationClass = 'destructive' as const;
|
|
1407
|
+
readonly schemaName: string;
|
|
1408
|
+
readonly tableName: string;
|
|
1409
|
+
readonly policyName: string;
|
|
1410
|
+
readonly label: string;
|
|
1411
|
+
|
|
1412
|
+
constructor(schemaName: string, tableName: string, policyName: string) {
|
|
1413
|
+
super();
|
|
1414
|
+
this.schemaName = schemaName;
|
|
1415
|
+
this.tableName = tableName;
|
|
1416
|
+
this.policyName = policyName;
|
|
1417
|
+
this.label = `Drop RLS policy "${policyName}" on "${tableName}"`;
|
|
1418
|
+
this.freeze();
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1422
|
+
if (lowerer === undefined) {
|
|
1423
|
+
throw new Error(
|
|
1424
|
+
`DropPostgresRlsPolicyCall.toOp: a lowerer is required on the Postgres planner path (policy "${this.policyName}" on table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
return dropRlsPolicy(this.schemaName, this.tableName, this.policyName, lowerer);
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
renderTypeScript(): string {
|
|
1431
|
+
return `dropRlsPolicy(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.policyName)})`;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
export class EnableRowLevelSecurityCall extends PostgresOpFactoryCallNode {
|
|
1436
|
+
readonly factoryName = 'enableRowLevelSecurity' as const;
|
|
1437
|
+
readonly operationClass = 'additive' as const;
|
|
1438
|
+
readonly schemaName: string;
|
|
1439
|
+
readonly tableName: string;
|
|
1440
|
+
readonly label: string;
|
|
1441
|
+
|
|
1442
|
+
constructor(schemaName: string, tableName: string) {
|
|
1443
|
+
super();
|
|
1444
|
+
this.schemaName = schemaName;
|
|
1445
|
+
this.tableName = tableName;
|
|
1446
|
+
this.label = `Enable row-level security on "${tableName}"`;
|
|
1447
|
+
this.freeze();
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1451
|
+
if (lowerer === undefined) {
|
|
1452
|
+
throw new Error(
|
|
1453
|
+
`EnableRowLevelSecurityCall.toOp: a lowerer is required on the Postgres planner path (table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
return enableRowLevelSecurity(this.schemaName, this.tableName, lowerer);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
renderTypeScript(): string {
|
|
1460
|
+
return `enableRowLevelSecurity(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)})`;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1379
1464
|
export type PostgresOpFactoryCall =
|
|
1380
1465
|
| CreateTableCall
|
|
1381
1466
|
| DropTableCall
|
|
@@ -1399,4 +1484,7 @@ export type PostgresOpFactoryCall =
|
|
|
1399
1484
|
| RawSqlCall
|
|
1400
1485
|
| CreateExtensionCall
|
|
1401
1486
|
| CreateSchemaCall
|
|
1487
|
+
| CreatePostgresRlsPolicyCall
|
|
1488
|
+
| DropPostgresRlsPolicyCall
|
|
1489
|
+
| EnableRowLevelSecurityCall
|
|
1402
1490
|
| DataTransformCall;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
|
+
import type { DdlColumn } from '@prisma-next/sql-relational-core/ast';
|
|
3
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
2
4
|
import {
|
|
3
5
|
columnDefaultAst,
|
|
4
6
|
columnExistsAst,
|
|
@@ -7,7 +9,9 @@ import {
|
|
|
7
9
|
noNullValuesAst,
|
|
8
10
|
tableIsEmptyAst,
|
|
9
11
|
} from '../../../contract-free/checks';
|
|
12
|
+
import * as contractFreeDdl from '../../../contract-free/ddl';
|
|
10
13
|
import { quoteIdentifier } from '../../sql-utils';
|
|
14
|
+
import { boundSchema } from '../bound-schema';
|
|
11
15
|
import { qualifyTableName } from '../planner-sql-checks';
|
|
12
16
|
import { type Op, step, targetDetails } from './shared';
|
|
13
17
|
|
|
@@ -242,12 +246,18 @@ export async function dropDefault(
|
|
|
242
246
|
columnName: string,
|
|
243
247
|
lowerer: ExecuteRequestLowerer,
|
|
244
248
|
): Promise<Op> {
|
|
245
|
-
const qualified = qualifyTableName(schemaName, tableName);
|
|
246
249
|
const { present } = await columnExistsSteps(lowerer, {
|
|
247
250
|
schema: schemaName,
|
|
248
251
|
table: tableName,
|
|
249
252
|
column: columnName,
|
|
250
253
|
});
|
|
254
|
+
const dropDefaultExec = await lowerer.lowerToExecuteRequest(
|
|
255
|
+
contractFreeDdl.alterTable({
|
|
256
|
+
...ifDefined('schema', boundSchema(schemaName)),
|
|
257
|
+
table: tableName,
|
|
258
|
+
actions: [contractFreeDdl.dropDefaultAction(columnName)],
|
|
259
|
+
}),
|
|
260
|
+
);
|
|
251
261
|
const noDefault = await lowerer.lowerToExecuteRequest(
|
|
252
262
|
columnDefaultAst({ schema: schemaName, table: tableName, column: columnName }).defaultAbsent(),
|
|
253
263
|
);
|
|
@@ -257,12 +267,7 @@ export async function dropDefault(
|
|
|
257
267
|
operationClass: 'destructive',
|
|
258
268
|
target: targetDetails('column', columnName, schemaName, tableName),
|
|
259
269
|
precheck: [step(`ensure column "${columnName}" exists`, present.sql, present.params)],
|
|
260
|
-
execute: [
|
|
261
|
-
step(
|
|
262
|
-
`drop default on "${columnName}"`,
|
|
263
|
-
`ALTER TABLE ${qualified} ALTER COLUMN ${quoteIdentifier(columnName)} DROP DEFAULT`,
|
|
264
|
-
),
|
|
265
|
-
],
|
|
270
|
+
execute: [step(`drop default on "${columnName}"`, dropDefaultExec.sql)],
|
|
266
271
|
postcheck: [
|
|
267
272
|
step(`verify column "${columnName}" has no default`, noDefault.sql, noDefault.params),
|
|
268
273
|
],
|
|
@@ -272,16 +277,23 @@ export async function dropDefault(
|
|
|
272
277
|
/**
|
|
273
278
|
* Builds the op for adding a NOT NULL column (no contract default) to a
|
|
274
279
|
* non-empty table. Prechecks assert the column is absent and the table is
|
|
275
|
-
* empty; the execute step
|
|
276
|
-
*
|
|
280
|
+
* empty; the execute step lowers the typed `AddColumn` DDL node through the
|
|
281
|
+
* adapter; postchecks assert the column exists and is NOT NULL.
|
|
277
282
|
*/
|
|
278
283
|
export async function addNotNullColumnDirect(
|
|
279
284
|
schemaName: string,
|
|
280
285
|
tableName: string,
|
|
281
|
-
|
|
282
|
-
executeStepSql: string,
|
|
286
|
+
column: DdlColumn,
|
|
283
287
|
lowerer: ExecuteRequestLowerer,
|
|
284
288
|
): Promise<Op> {
|
|
289
|
+
const columnName = column.name;
|
|
290
|
+
const addColumn = await lowerer.lowerToExecuteRequest(
|
|
291
|
+
contractFreeDdl.alterTable({
|
|
292
|
+
...ifDefined('schema', boundSchema(schemaName)),
|
|
293
|
+
table: tableName,
|
|
294
|
+
actions: [contractFreeDdl.addColumnAction(column)],
|
|
295
|
+
}),
|
|
296
|
+
);
|
|
285
297
|
const absent = await lowerer.lowerToExecuteRequest(
|
|
286
298
|
columnExistsAst({ schema: schemaName, table: tableName, column: columnName }).columnAbsent(),
|
|
287
299
|
);
|
|
@@ -311,7 +323,7 @@ export async function addNotNullColumnDirect(
|
|
|
311
323
|
tableEmpty.params,
|
|
312
324
|
),
|
|
313
325
|
],
|
|
314
|
-
execute: [step(`add column "${columnName}"`,
|
|
326
|
+
execute: [step(`add column "${columnName}"`, addColumn.sql)],
|
|
315
327
|
postcheck: [
|
|
316
328
|
step(`verify column "${columnName}" exists`, present.sql, present.params),
|
|
317
329
|
step(`verify column "${columnName}" is NOT NULL`, notNullable.sql, notNullable.params),
|
|
@@ -196,7 +196,7 @@ export async function dropCheckConstraint(
|
|
|
196
196
|
* `kind` feeds the operation's `target.details.objectType`. Descriptor-flow
|
|
197
197
|
* does not carry kind information in its drop-constraint descriptor, so the
|
|
198
198
|
* default is `'unique'`. The reconciliation planner passes the correct kind
|
|
199
|
-
* (`'foreignKey'`, `'primaryKey'`, or `'unique'`) based on the
|
|
199
|
+
* (`'foreignKey'`, `'primaryKey'`, or `'unique'`) based on the diff issue
|
|
200
200
|
* that produced the drop.
|
|
201
201
|
*/
|
|
202
202
|
export async function dropConstraint(
|
|
@@ -113,6 +113,12 @@ export async function dataTransform<TContract extends Contract<SqlStorage>>(
|
|
|
113
113
|
runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name)),
|
|
114
114
|
);
|
|
115
115
|
|
|
116
|
+
// Raw remnant: the factory lowered the user's `check` plan above before
|
|
117
|
+
// we get a chance to wrap it, so the EXISTS lifting only has text by the
|
|
118
|
+
// time it runs. The fix is to rewrite the factory to defer lowering until
|
|
119
|
+
// after wrapping — so `cfExpr.exists(checkPlan.ast)` can produce the
|
|
120
|
+
// EXISTS query, then lower once at the boundary — not to grow new AST
|
|
121
|
+
// substrate. Out of scope for the slice that introduced this comment.
|
|
116
122
|
const precheck: readonly SqlMigrationPlanOperationStep[] = checkPlan
|
|
117
123
|
? [
|
|
118
124
|
{
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
2
|
import { extensionExistsAst } from '../../../contract-free/checks';
|
|
3
|
+
import { DEFAULT_NAMESPACE_ID } from '../../namespace-ids';
|
|
3
4
|
import { quoteIdentifier } from '../../sql-utils';
|
|
4
5
|
import { type Op, step } from './shared';
|
|
5
6
|
|
|
@@ -55,7 +56,7 @@ export async function installExtension(
|
|
|
55
56
|
invariantId,
|
|
56
57
|
target: {
|
|
57
58
|
id: 'postgres',
|
|
58
|
-
details: { schema:
|
|
59
|
+
details: { schema: DEFAULT_NAMESPACE_ID, objectType: 'dependency', name: extensionName },
|
|
59
60
|
},
|
|
60
61
|
precheck: [
|
|
61
62
|
step(`verify extension "${extensionName}" is not already enabled`, absent.sql, absent.params),
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
3
|
+
import { rlsEnabledAst, rlsPolicyExistsAst } from '../../../contract-free/checks';
|
|
4
|
+
import { createPolicy, dropPolicy } from '../../../contract-free/ddl';
|
|
5
|
+
import type { PostgresRlsPolicy } from '../../postgres-rls-policy';
|
|
6
|
+
import { qualifyTableName } from '../planner-sql-checks';
|
|
7
|
+
import { type Op, step, targetDetails } from './shared';
|
|
8
|
+
|
|
9
|
+
const PLAIN_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
10
|
+
|
|
11
|
+
function validateRoleName(role: string): string {
|
|
12
|
+
if (!PLAIN_IDENTIFIER.test(role)) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`Invalid role name ${JSON.stringify(role)}: role names must be plain SQL identifiers matching ^[A-Za-z_][A-Za-z0-9_$]*$`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return role;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function createRlsPolicy(
|
|
21
|
+
schemaName: string,
|
|
22
|
+
tableName: string,
|
|
23
|
+
policy: PostgresRlsPolicy,
|
|
24
|
+
lowerer: ExecuteRequestLowerer,
|
|
25
|
+
): Promise<Op> {
|
|
26
|
+
const validatedRoles = policy.roles.map(validateRoleName);
|
|
27
|
+
const ddlNode = createPolicy({
|
|
28
|
+
schema: schemaName,
|
|
29
|
+
table: tableName,
|
|
30
|
+
name: policy.name,
|
|
31
|
+
permissive: policy.permissive,
|
|
32
|
+
operation: policy.operation,
|
|
33
|
+
roles: validatedRoles,
|
|
34
|
+
...ifDefined('using', policy.using),
|
|
35
|
+
...ifDefined('withCheck', policy.withCheck),
|
|
36
|
+
});
|
|
37
|
+
const checks = rlsPolicyExistsAst({
|
|
38
|
+
schema: schemaName,
|
|
39
|
+
table: tableName,
|
|
40
|
+
policyName: policy.name,
|
|
41
|
+
});
|
|
42
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.policyAbsent());
|
|
43
|
+
const execute = await lowerer.lowerToExecuteRequest(ddlNode);
|
|
44
|
+
const present = await lowerer.lowerToExecuteRequest(checks.policyPresent());
|
|
45
|
+
return {
|
|
46
|
+
id: `rlsPolicy.${schemaName}.${tableName}.${policy.name}`,
|
|
47
|
+
label: `Create RLS policy "${policy.name}" on "${tableName}"`,
|
|
48
|
+
operationClass: 'additive',
|
|
49
|
+
target: targetDetails('rlsPolicy', policy.name, schemaName, tableName),
|
|
50
|
+
precheck: [
|
|
51
|
+
step(`ensure RLS policy "${policy.name}" does not exist`, absent.sql, absent.params),
|
|
52
|
+
],
|
|
53
|
+
execute: [step(`create RLS policy "${policy.name}"`, execute.sql, execute.params)],
|
|
54
|
+
postcheck: [step(`verify RLS policy "${policy.name}" exists`, present.sql, present.params)],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function dropRlsPolicy(
|
|
59
|
+
schemaName: string,
|
|
60
|
+
tableName: string,
|
|
61
|
+
policyName: string,
|
|
62
|
+
lowerer: ExecuteRequestLowerer,
|
|
63
|
+
): Promise<Op> {
|
|
64
|
+
const ddlNode = dropPolicy({ schema: schemaName, table: tableName, name: policyName });
|
|
65
|
+
const checks = rlsPolicyExistsAst({ schema: schemaName, table: tableName, policyName });
|
|
66
|
+
const present = await lowerer.lowerToExecuteRequest(checks.policyPresent());
|
|
67
|
+
const execute = await lowerer.lowerToExecuteRequest(ddlNode);
|
|
68
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.policyAbsent());
|
|
69
|
+
return {
|
|
70
|
+
id: `rlsPolicy.${schemaName}.${tableName}.${policyName}.drop`,
|
|
71
|
+
label: `Drop RLS policy "${policyName}" on "${tableName}"`,
|
|
72
|
+
operationClass: 'destructive',
|
|
73
|
+
target: targetDetails('rlsPolicy', policyName, schemaName, tableName),
|
|
74
|
+
precheck: [step(`ensure RLS policy "${policyName}" exists`, present.sql, present.params)],
|
|
75
|
+
execute: [step(`drop RLS policy "${policyName}"`, execute.sql, execute.params)],
|
|
76
|
+
postcheck: [step(`verify RLS policy "${policyName}" is absent`, absent.sql, absent.params)],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function enableRowLevelSecurity(
|
|
81
|
+
schemaName: string,
|
|
82
|
+
tableName: string,
|
|
83
|
+
lowerer: ExecuteRequestLowerer,
|
|
84
|
+
): Promise<Op> {
|
|
85
|
+
const checks = rlsEnabledAst(schemaName, tableName);
|
|
86
|
+
const disabled = await lowerer.lowerToExecuteRequest(checks.rlsDisabled());
|
|
87
|
+
const enabled = await lowerer.lowerToExecuteRequest(checks.rlsEnabled());
|
|
88
|
+
return {
|
|
89
|
+
id: `rowLevelSecurity.${schemaName}.${tableName}`,
|
|
90
|
+
label: `Enable row-level security on "${tableName}"`,
|
|
91
|
+
operationClass: 'additive',
|
|
92
|
+
target: targetDetails('rowLevelSecurity', tableName, schemaName),
|
|
93
|
+
precheck: [
|
|
94
|
+
step(`check RLS is not already enabled on "${tableName}"`, disabled.sql, disabled.params),
|
|
95
|
+
],
|
|
96
|
+
execute: [
|
|
97
|
+
step(
|
|
98
|
+
`enable row-level security on "${tableName}"`,
|
|
99
|
+
`ALTER TABLE ${qualifyTableName(schemaName, tableName)} ENABLE ROW LEVEL SECURITY`,
|
|
100
|
+
),
|
|
101
|
+
],
|
|
102
|
+
postcheck: [
|
|
103
|
+
step(`verify row-level security is enabled on "${tableName}"`, enabled.sql, enabled.params),
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -1,30 +1,10 @@
|
|
|
1
1
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
2
|
import type { ReferentialAction } from '@prisma-next/sql-contract/types';
|
|
3
3
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
4
|
-
import { quoteIdentifier } from '../../sql-utils';
|
|
5
4
|
import type { OperationClass, PostgresPlanTargetDetails } from '../planner-target-details';
|
|
6
5
|
|
|
7
6
|
export type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
8
7
|
|
|
9
|
-
/**
|
|
10
|
-
* Literal-args shape for a column definition consumed by `addColumn` and
|
|
11
|
-
* similar operations. Fully materialized: codec expansion and default
|
|
12
|
-
* rendering have already happened in the wrapper.
|
|
13
|
-
*
|
|
14
|
-
* - `typeSql` is the column's DDL type string (e.g. `"integer"`, `"SERIAL"`,
|
|
15
|
-
* `"varchar(100)"`), already produced by `buildColumnTypeSql` in the
|
|
16
|
-
* call-factory wrapper.
|
|
17
|
-
* - `defaultSql` is the full `DEFAULT …` clause (e.g. `"DEFAULT 42"`) or an
|
|
18
|
-
* empty string when the column has no default, matching
|
|
19
|
-
* `buildColumnDefaultSql`'s output.
|
|
20
|
-
*/
|
|
21
|
-
export interface ColumnSpec {
|
|
22
|
-
readonly name: string;
|
|
23
|
-
readonly typeSql: string;
|
|
24
|
-
readonly defaultSql: string;
|
|
25
|
-
readonly nullable: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
8
|
/**
|
|
29
9
|
* Literal-args shape for a foreign key definition. `references.schema`
|
|
30
10
|
* carries the target table's namespace (schema) coordinate so the rendered
|
|
@@ -57,13 +37,3 @@ export function targetDetails(
|
|
|
57
37
|
details: { schema, objectType, name, ...ifDefined('table', table) },
|
|
58
38
|
};
|
|
59
39
|
}
|
|
60
|
-
|
|
61
|
-
export function renderColumnDefinition(column: ColumnSpec): string {
|
|
62
|
-
const parts = [
|
|
63
|
-
quoteIdentifier(column.name),
|
|
64
|
-
column.typeSql,
|
|
65
|
-
column.defaultSql,
|
|
66
|
-
column.nullable ? '' : 'NOT NULL',
|
|
67
|
-
].filter(Boolean);
|
|
68
|
-
return parts.join(' ');
|
|
69
|
-
}
|
|
@@ -1,43 +1,10 @@
|
|
|
1
1
|
import type { CodecControlHooks } from '@prisma-next/family-sql/control';
|
|
2
|
-
import type {
|
|
3
|
-
StorageColumn,
|
|
4
|
-
StorageTable,
|
|
5
|
-
StorageTypeInstance,
|
|
6
|
-
} from '@prisma-next/sql-contract/types';
|
|
2
|
+
import type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';
|
|
7
3
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
8
4
|
import { escapeLiteral, quoteIdentifier } from '../sql-utils';
|
|
9
5
|
import type { PostgresColumnDefault } from '../types';
|
|
10
6
|
import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
11
7
|
|
|
12
|
-
export function buildCreateTableSql(
|
|
13
|
-
qualifiedTableName: string,
|
|
14
|
-
table: StorageTable,
|
|
15
|
-
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
16
|
-
storageTypes: Record<string, StorageTypeInstance> = {},
|
|
17
|
-
): string {
|
|
18
|
-
const columnDefinitions = Object.entries(table.columns).map(
|
|
19
|
-
([columnName, column]: [string, StorageColumn]) => {
|
|
20
|
-
const parts = [
|
|
21
|
-
quoteIdentifier(columnName),
|
|
22
|
-
buildColumnTypeSql(column, codecHooks, storageTypes),
|
|
23
|
-
buildColumnDefaultSql(column.default, column),
|
|
24
|
-
column.nullable ? '' : 'NOT NULL',
|
|
25
|
-
].filter(Boolean);
|
|
26
|
-
return parts.join(' ');
|
|
27
|
-
},
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
const constraintDefinitions: string[] = [];
|
|
31
|
-
if (table.primaryKey) {
|
|
32
|
-
constraintDefinitions.push(
|
|
33
|
-
`PRIMARY KEY (${table.primaryKey.columns.map(quoteIdentifier).join(', ')})`,
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const allDefinitions = [...columnDefinitions, ...constraintDefinitions];
|
|
38
|
-
return `CREATE TABLE ${qualifiedTableName} (\n ${allDefinitions.join(',\n ')}\n)`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
8
|
/**
|
|
42
9
|
* Pattern for safe PostgreSQL type names.
|
|
43
10
|
* Allows letters, digits, underscores, spaces (for "double precision", "character varying"),
|
|
@@ -100,15 +67,16 @@ export function buildColumnTypeSql(
|
|
|
100
67
|
|
|
101
68
|
const expanded = expandParameterizedTypeSql(resolved, codecHooks);
|
|
102
69
|
if (expanded !== null) {
|
|
103
|
-
return expanded;
|
|
70
|
+
return column.many ? `${expanded}[]` : expanded;
|
|
104
71
|
}
|
|
105
72
|
|
|
106
73
|
if (column.typeRef) {
|
|
107
|
-
|
|
74
|
+
const base = quoteIdentifier(resolved.nativeType);
|
|
75
|
+
return column.many ? `${base}[]` : base;
|
|
108
76
|
}
|
|
109
77
|
|
|
110
78
|
assertSafeNativeType(resolved.nativeType);
|
|
111
|
-
return resolved.nativeType;
|
|
79
|
+
return column.many ? `${resolved.nativeType}[]` : resolved.nativeType;
|
|
112
80
|
}
|
|
113
81
|
|
|
114
82
|
function expandParameterizedTypeSql(
|
|
@@ -150,7 +118,7 @@ function expandParameterizedTypeSql(
|
|
|
150
118
|
/** Autoincrement columns use SERIAL types, so this returns empty for them. */
|
|
151
119
|
export function buildColumnDefaultSql(
|
|
152
120
|
columnDefault: PostgresColumnDefault | undefined,
|
|
153
|
-
column?: StorageColumn,
|
|
121
|
+
column?: Pick<StorageColumn, 'many' | 'nativeType'>,
|
|
154
122
|
): string {
|
|
155
123
|
if (!columnDefault) {
|
|
156
124
|
return '';
|
|
@@ -171,9 +139,16 @@ export function buildColumnDefaultSql(
|
|
|
171
139
|
}
|
|
172
140
|
}
|
|
173
141
|
|
|
174
|
-
export function renderDefaultLiteral(
|
|
142
|
+
export function renderDefaultLiteral(
|
|
143
|
+
value: unknown,
|
|
144
|
+
column?: Pick<StorageColumn, 'many' | 'nativeType'>,
|
|
145
|
+
): string {
|
|
175
146
|
const isJsonColumn = column?.nativeType === 'json' || column?.nativeType === 'jsonb';
|
|
176
147
|
|
|
148
|
+
if (column?.many && Array.isArray(value)) {
|
|
149
|
+
return renderArrayLiteralDefault(value);
|
|
150
|
+
}
|
|
151
|
+
|
|
177
152
|
if (value instanceof Date) {
|
|
178
153
|
return `'${escapeLiteral(value.toISOString())}'`;
|
|
179
154
|
}
|
|
@@ -193,23 +168,10 @@ export function renderDefaultLiteral(value: unknown, column?: StorageColumn): st
|
|
|
193
168
|
return `'${escapeLiteral(json)}'`;
|
|
194
169
|
}
|
|
195
170
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
storageTypes: Record<string, StorageTypeInstance> = {},
|
|
203
|
-
): string {
|
|
204
|
-
const typeSql = buildColumnTypeSql(column, codecHooks, storageTypes);
|
|
205
|
-
const defaultSql =
|
|
206
|
-
buildColumnDefaultSql(column.default, column) ||
|
|
207
|
-
(temporaryDefault ? `DEFAULT ${temporaryDefault}` : '');
|
|
208
|
-
const parts = [
|
|
209
|
-
`ALTER TABLE ${qualifiedTableName}`,
|
|
210
|
-
`ADD COLUMN ${quoteIdentifier(columnName)} ${typeSql}`,
|
|
211
|
-
defaultSql,
|
|
212
|
-
column.nullable ? '' : 'NOT NULL',
|
|
213
|
-
].filter(Boolean);
|
|
214
|
-
return parts.join(' ');
|
|
171
|
+
function renderArrayLiteralDefault(elements: unknown[]): string {
|
|
172
|
+
if (elements.length === 0) {
|
|
173
|
+
return "'{}'";
|
|
174
|
+
}
|
|
175
|
+
const rendered = elements.map((el) => renderDefaultLiteral(el)).join(', ');
|
|
176
|
+
return `ARRAY[${rendered}]`;
|
|
215
177
|
}
|
|
@@ -9,7 +9,7 @@ import { ifDefined } from '@prisma-next/utils/defined';
|
|
|
9
9
|
*/
|
|
10
10
|
export function resolveIdentityValue(
|
|
11
11
|
column: StorageColumn,
|
|
12
|
-
codecHooks:
|
|
12
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
13
13
|
storageTypes: Record<string, StorageTypeInstance> = {},
|
|
14
14
|
): string | null {
|
|
15
15
|
const referencedType = column.typeRef ? storageTypes[column.typeRef] : undefined;
|