@prisma-next/target-postgres 0.12.0 → 0.13.0-dev.1
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/{codec-ids-D9fJ4HP5.d.mts → codec-ids-B1vOchLE.d.mts} +3 -2
- package/dist/codec-ids-B1vOchLE.d.mts.map +1 -0
- package/dist/{codec-ids-C5qzBqus.mjs → codec-ids-CTikp1if.mjs} +3 -2
- package/dist/codec-ids-CTikp1if.mjs.map +1 -0
- package/dist/codec-ids.d.mts +2 -2
- package/dist/codec-ids.mjs +2 -2
- package/dist/{codec-types-CRlHq7Cz.d.mts → codec-types-CnFiNML4.d.mts} +8 -9
- package/dist/codec-types-CnFiNML4.d.mts.map +1 -0
- package/dist/codec-types.d.mts +2 -2
- package/dist/{codecs-Dud5KDNk.d.mts → codecs-CBpEv4s5.d.mts} +33 -35
- package/dist/codecs-CBpEv4s5.d.mts.map +1 -0
- package/dist/codecs.d.mts +1 -1
- package/dist/codecs.mjs +37 -2
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.d.mts +80 -0
- package/dist/contract-free.d.mts.map +1 -0
- package/dist/contract-free.mjs +117 -0
- package/dist/contract-free.mjs.map +1 -0
- package/dist/control.d.mts +1 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +66 -40
- package/dist/control.mjs.map +1 -1
- package/dist/{data-transform-CdtGUWp2.mjs → data-transform-D25tLeYU.mjs} +1 -1
- package/dist/{data-transform-CdtGUWp2.mjs.map → data-transform-D25tLeYU.mjs.map} +1 -1
- package/dist/{data-transform-bmOKkygi.d.mts → data-transform-DGOqcLrf.d.mts} +2 -2
- package/dist/{data-transform-bmOKkygi.d.mts.map → data-transform-DGOqcLrf.d.mts.map} +1 -1
- package/dist/data-transform.d.mts +1 -1
- package/dist/data-transform.mjs +1 -1
- package/dist/ddl-77SyXgFt.mjs +30 -0
- package/dist/ddl-77SyXgFt.mjs.map +1 -0
- package/dist/ddl.d.mts +2 -0
- package/dist/ddl.mjs +2 -0
- package/dist/{default-normalizer-CRscvhS5.mjs → default-normalizer-DyyCHQWs.mjs} +1 -1
- package/dist/{default-normalizer-CRscvhS5.mjs.map → default-normalizer-DyyCHQWs.mjs.map} +1 -1
- package/dist/default-normalizer.mjs +1 -1
- package/dist/descriptor-meta-DKmj-IMN.mjs +14 -0
- package/dist/descriptor-meta-DKmj-IMN.mjs.map +1 -0
- package/dist/{descriptor-meta-DLA2xV6B.mjs → descriptor-meta-runtime-My8_s4cs.mjs} +82 -78
- package/dist/descriptor-meta-runtime-My8_s4cs.mjs.map +1 -0
- package/dist/{enum-planning-Dz0Ye3Lb.mjs → enum-planning-BCyvlFHk.mjs} +0 -0
- package/dist/{enum-planning-Dz0Ye3Lb.mjs.map → enum-planning-BCyvlFHk.mjs.map} +1 -1
- package/dist/enum-planning.d.mts +1 -1
- package/dist/enum-planning.mjs +1 -1
- package/dist/{errors--zafB5_n.mjs → errors-CUk87ByX.mjs} +1 -1
- package/dist/{errors--zafB5_n.mjs.map → errors-CUk87ByX.mjs.map} +1 -1
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +1 -1
- package/dist/{issue-planner-ByQhUzS4.mjs → issue-planner-Br0pt1Ea.mjs} +130 -28
- package/dist/issue-planner-Br0pt1Ea.mjs.map +1 -0
- package/dist/issue-planner.d.mts +1 -1
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +7 -8
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +5 -4
- package/dist/migration.mjs.map +1 -1
- package/dist/{native-type-normalizer-ClNPq__-.mjs → native-type-normalizer-Bc9XJzWC.mjs} +1 -1
- package/dist/{native-type-normalizer-ClNPq__-.mjs.map → native-type-normalizer-Bc9XJzWC.mjs.map} +1 -1
- package/dist/native-type-normalizer.mjs +1 -1
- package/dist/nodes-779hmCfL.d.mts +40 -0
- package/dist/nodes-779hmCfL.d.mts.map +1 -0
- package/dist/nodes-DZk2JZG3.mjs +47 -0
- package/dist/nodes-DZk2JZG3.mjs.map +1 -0
- package/dist/op-factory-call-D2aAUhmS.mjs +1307 -0
- package/dist/op-factory-call-D2aAUhmS.mjs.map +1 -0
- package/dist/{op-factory-call-Drccm_JD.d.mts → op-factory-call-DMA86_2D.d.mts} +39 -14
- package/dist/op-factory-call-DMA86_2D.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 +13 -12
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/planner-CAYPJObw.mjs +344 -0
- package/dist/planner-CAYPJObw.mjs.map +1 -0
- package/dist/{planner-ddl-builders-BxRCSn_b.mjs → planner-ddl-builders-Cw2n2llW.mjs} +7 -30
- package/dist/planner-ddl-builders-Cw2n2llW.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +6 -7
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +2 -2
- package/dist/{planner-identity-values-ojX-6cPV.mjs → planner-identity-values-BIpa5p2I.mjs} +1 -1
- package/dist/{planner-identity-values-ojX-6cPV.mjs.map → planner-identity-values-BIpa5p2I.mjs.map} +1 -1
- package/dist/planner-identity-values.mjs +1 -1
- package/dist/{planner-produced-postgres-migration-p-VKkCia.d.mts → planner-produced-postgres-migration-B4EDvLdz.d.mts} +5 -4
- package/dist/planner-produced-postgres-migration-B4EDvLdz.d.mts.map +1 -0
- package/dist/{planner-produced-postgres-migration-N1yqYg20.mjs → planner-produced-postgres-migration-NSEhWL0L.mjs} +8 -6
- package/dist/planner-produced-postgres-migration-NSEhWL0L.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-BGyukuzG.mjs → planner-schema-lookup-CiVaAQP-.mjs} +1 -1
- package/dist/{planner-schema-lookup-BGyukuzG.mjs.map → planner-schema-lookup-CiVaAQP-.mjs.map} +1 -1
- package/dist/planner-schema-lookup.mjs +1 -1
- package/dist/{planner-sql-checks-D3H-xOO1.mjs → planner-sql-checks-DAdhnI2c.mjs} +41 -30
- package/dist/planner-sql-checks-DAdhnI2c.mjs.map +1 -0
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/{planner-target-details-CIj61DUj.d.mts → planner-target-details-CIY6tLeo.d.mts} +2 -2
- package/dist/planner-target-details-CIY6tLeo.d.mts.map +1 -0
- package/dist/planner-target-details.d.mts +2 -2
- package/dist/planner-type-resolution-836DExFN.mjs +20 -0
- package/dist/planner-type-resolution-836DExFN.mjs.map +1 -0
- package/dist/planner.d.mts +7 -3
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/{postgres-contract-serializer-YJvjKrmo.mjs → postgres-contract-serializer-DYTyXjPf.mjs} +33 -24
- package/dist/postgres-contract-serializer-DYTyXjPf.mjs.map +1 -0
- package/dist/{postgres-enum-type-CNhPTDhy.d.mts → postgres-enum-type-BVn63a89.d.mts} +4 -1
- package/dist/postgres-enum-type-BVn63a89.d.mts.map +1 -0
- package/dist/{postgres-enum-type-DS-KLVRH.mjs → postgres-enum-type-DPKqCBem.mjs} +2 -1
- package/dist/postgres-enum-type-DPKqCBem.mjs.map +1 -0
- package/dist/{postgres-migration-uADmx0dW.mjs → postgres-migration-COore9Mz.mjs} +23 -3
- package/dist/postgres-migration-COore9Mz.mjs.map +1 -0
- package/dist/{postgres-migration-Fd4fQkBw.d.mts → postgres-migration-DZ_gLUOW.d.mts} +25 -3
- package/dist/postgres-migration-DZ_gLUOW.d.mts.map +1 -0
- package/dist/{postgres-schema-Bm7vjlOv.mjs → postgres-schema-BuxCxbvB.mjs} +29 -14
- package/dist/postgres-schema-BuxCxbvB.mjs.map +1 -0
- package/dist/{render-ops-BC2PtCkj.mjs → render-ops-BpjstrKQ.mjs} +4 -3
- package/dist/{render-ops-BC2PtCkj.mjs.map → render-ops-BpjstrKQ.mjs.map} +1 -1
- package/dist/render-ops.d.mts +3 -2
- package/dist/render-ops.d.mts.map +1 -1
- package/dist/render-ops.mjs +1 -1
- package/dist/{render-typescript-CPk7hhWH.mjs → render-typescript-KMgosran.mjs} +5 -2
- package/dist/render-typescript-KMgosran.mjs.map +1 -0
- package/dist/render-typescript.mjs +1 -1
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +3 -3
- package/dist/runtime.mjs.map +1 -1
- package/dist/{shared-ByhSooBS.d.mts → shared-DarONYBZ.d.mts} +5 -5
- package/dist/{shared-ByhSooBS.d.mts.map → shared-DarONYBZ.d.mts.map} +1 -1
- package/dist/{sql-utils-B_ruBD-M.mjs → sql-utils-DcfMz4MQ.mjs} +1 -1
- package/dist/{sql-utils-B_ruBD-M.mjs.map → sql-utils-DcfMz4MQ.mjs.map} +1 -1
- package/dist/sql-utils.mjs +1 -1
- package/dist/{types-D-XIpzHA.d.mts → types-BDKkx8MA.d.mts} +1 -1
- package/dist/types-BDKkx8MA.d.mts.map +1 -0
- package/dist/types.d.mts +16 -11
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +2 -2
- package/package.json +21 -20
- package/src/contract-free/columns.ts +49 -0
- package/src/contract-free/control-bootstrap.ts +55 -0
- package/src/contract-free/ddl.ts +37 -0
- package/src/core/ast/table-source.ts +23 -0
- package/src/core/authoring.ts +1 -1
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codecs.ts +44 -0
- package/src/core/ddl/nodes.ts +72 -0
- package/src/core/descriptor-meta-runtime.ts +28 -0
- package/src/core/descriptor-meta.ts +3 -6
- package/src/core/migrations/control-policy.ts +234 -0
- package/src/core/migrations/issue-planner.ts +81 -13
- package/src/core/migrations/op-factory-call.ts +289 -46
- package/src/core/migrations/operations/constraints.ts +79 -10
- package/src/core/migrations/operations/dependencies.ts +0 -17
- package/src/core/migrations/operations/shared.ts +3 -3
- package/src/core/migrations/operations/tables.ts +1 -39
- package/src/core/migrations/planner-ddl-builders.ts +7 -48
- package/src/core/migrations/planner-produced-postgres-migration.ts +11 -6
- package/src/core/migrations/planner-sql-checks.ts +9 -9
- package/src/core/migrations/planner-strategies.ts +149 -11
- package/src/core/migrations/planner-target-details.ts +2 -1
- package/src/core/migrations/planner.ts +66 -8
- package/src/core/migrations/postgres-migration.ts +41 -0
- package/src/core/migrations/render-ops.ts +7 -2
- package/src/core/migrations/render-typescript.ts +5 -1
- package/src/core/migrations/runner.ts +78 -50
- package/src/core/postgres-contract-serializer.ts +52 -46
- package/src/core/postgres-enum-type.ts +4 -0
- package/src/core/postgres-schema.ts +43 -25
- package/src/exports/contract-free.ts +7 -0
- package/src/exports/control.ts +6 -8
- package/src/exports/ddl.ts +7 -0
- package/src/exports/migration.ts +11 -2
- package/src/exports/op-factory-call.ts +2 -0
- package/src/exports/planner-ddl-builders.ts +0 -1
- package/src/exports/runtime.ts +2 -2
- package/dist/codec-ids-C5qzBqus.mjs.map +0 -1
- package/dist/codec-ids-D9fJ4HP5.d.mts.map +0 -1
- package/dist/codec-types-CRlHq7Cz.d.mts.map +0 -1
- package/dist/codecs-Dud5KDNk.d.mts.map +0 -1
- package/dist/descriptor-meta-DLA2xV6B.mjs.map +0 -1
- package/dist/issue-planner-ByQhUzS4.mjs.map +0 -1
- package/dist/op-factory-call-B0WNg30h.mjs +0 -625
- package/dist/op-factory-call-B0WNg30h.mjs.map +0 -1
- package/dist/op-factory-call-Drccm_JD.d.mts.map +0 -1
- package/dist/planner-ClF0y0YR.mjs +0 -177
- package/dist/planner-ClF0y0YR.mjs.map +0 -1
- package/dist/planner-ddl-builders-BxRCSn_b.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-N1yqYg20.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-p-VKkCia.d.mts.map +0 -1
- package/dist/planner-sql-checks-D3H-xOO1.mjs.map +0 -1
- package/dist/planner-target-details-CIj61DUj.d.mts.map +0 -1
- package/dist/postgres-contract-serializer-YJvjKrmo.mjs.map +0 -1
- package/dist/postgres-enum-type-CNhPTDhy.d.mts.map +0 -1
- package/dist/postgres-enum-type-DS-KLVRH.mjs.map +0 -1
- package/dist/postgres-migration-Fd4fQkBw.d.mts.map +0 -1
- package/dist/postgres-migration-uADmx0dW.mjs.map +0 -1
- package/dist/postgres-schema-Bm7vjlOv.mjs.map +0 -1
- package/dist/render-typescript-CPk7hhWH.mjs.map +0 -1
- package/dist/statement-builders-vImtdfmM.mjs +0 -131
- package/dist/statement-builders-vImtdfmM.mjs.map +0 -1
- package/dist/statement-builders.d.mts +0 -51
- package/dist/statement-builders.d.mts.map +0 -1
- package/dist/statement-builders.mjs +0 -2
- package/dist/tables-Dcb2q9zV.mjs +0 -516
- package/dist/tables-Dcb2q9zV.mjs.map +0 -1
- package/dist/types-D-XIpzHA.d.mts.map +0 -1
- package/src/core/migrations/statement-builders.ts +0 -183
- package/src/exports/statement-builders.ts +0 -8
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { quoteIdentifier } from '../../sql-utils';
|
|
1
|
+
import { REFERENTIAL_ACTION_SQL } from '@prisma-next/sql-contract/referential-action-sql';
|
|
2
|
+
import { escapeLiteral, quoteIdentifier } from '../../sql-utils';
|
|
3
3
|
import { constraintExistsCheck, qualifyTableName } from '../planner-sql-checks';
|
|
4
4
|
import { type ForeignKeySpec, type Op, step, targetDetails } from './shared';
|
|
5
5
|
|
|
6
|
-
const REFERENTIAL_ACTION_SQL: Record<ReferentialAction, string> = {
|
|
7
|
-
noAction: 'NO ACTION',
|
|
8
|
-
restrict: 'RESTRICT',
|
|
9
|
-
cascade: 'CASCADE',
|
|
10
|
-
setNull: 'SET NULL',
|
|
11
|
-
setDefault: 'SET DEFAULT',
|
|
12
|
-
};
|
|
13
|
-
|
|
14
6
|
function renderForeignKeySql(schemaName: string, tableName: string, fk: ForeignKeySpec): string {
|
|
15
7
|
let sql = `ALTER TABLE ${qualifyTableName(schemaName, tableName)}
|
|
16
8
|
ADD CONSTRAINT ${quoteIdentifier(fk.name)}
|
|
@@ -145,6 +137,83 @@ export function addForeignKey(schemaName: string, tableName: string, fk: Foreign
|
|
|
145
137
|
};
|
|
146
138
|
}
|
|
147
139
|
|
|
140
|
+
export function addCheckConstraint(
|
|
141
|
+
schemaName: string,
|
|
142
|
+
tableName: string,
|
|
143
|
+
constraintName: string,
|
|
144
|
+
column: string,
|
|
145
|
+
values: readonly string[],
|
|
146
|
+
): Op {
|
|
147
|
+
const qualified = qualifyTableName(schemaName, tableName);
|
|
148
|
+
const valueList = values.map((v) => `'${escapeLiteral(v)}'`).join(', ');
|
|
149
|
+
return {
|
|
150
|
+
id: `checkConstraint.${tableName}.${constraintName}`,
|
|
151
|
+
label: `Add check constraint "${constraintName}" on "${tableName}"."${column}"`,
|
|
152
|
+
operationClass: 'additive',
|
|
153
|
+
target: targetDetails('checkConstraint', constraintName, schemaName, tableName),
|
|
154
|
+
precheck: [
|
|
155
|
+
step(
|
|
156
|
+
`ensure constraint "${constraintName}" does not exist`,
|
|
157
|
+
constraintExistsCheck({
|
|
158
|
+
constraintName,
|
|
159
|
+
schema: schemaName,
|
|
160
|
+
table: tableName,
|
|
161
|
+
exists: false,
|
|
162
|
+
}),
|
|
163
|
+
),
|
|
164
|
+
],
|
|
165
|
+
execute: [
|
|
166
|
+
step(
|
|
167
|
+
`add check constraint "${constraintName}"`,
|
|
168
|
+
`ALTER TABLE ${qualified} ADD CONSTRAINT ${quoteIdentifier(constraintName)} CHECK (${quoteIdentifier(column)} IN (${valueList}))`,
|
|
169
|
+
),
|
|
170
|
+
],
|
|
171
|
+
postcheck: [
|
|
172
|
+
step(
|
|
173
|
+
`verify constraint "${constraintName}" exists`,
|
|
174
|
+
constraintExistsCheck({ constraintName, schema: schemaName, table: tableName }),
|
|
175
|
+
),
|
|
176
|
+
],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function dropCheckConstraint(
|
|
181
|
+
schemaName: string,
|
|
182
|
+
tableName: string,
|
|
183
|
+
constraintName: string,
|
|
184
|
+
): Op {
|
|
185
|
+
const qualified = qualifyTableName(schemaName, tableName);
|
|
186
|
+
return {
|
|
187
|
+
id: `dropCheckConstraint.${tableName}.${constraintName}`,
|
|
188
|
+
label: `Drop check constraint "${constraintName}" on "${tableName}"`,
|
|
189
|
+
operationClass: 'destructive',
|
|
190
|
+
target: targetDetails('checkConstraint', constraintName, schemaName, tableName),
|
|
191
|
+
precheck: [
|
|
192
|
+
step(
|
|
193
|
+
`ensure constraint "${constraintName}" exists`,
|
|
194
|
+
constraintExistsCheck({ constraintName, schema: schemaName, table: tableName }),
|
|
195
|
+
),
|
|
196
|
+
],
|
|
197
|
+
execute: [
|
|
198
|
+
step(
|
|
199
|
+
`drop check constraint "${constraintName}"`,
|
|
200
|
+
`ALTER TABLE ${qualified} DROP CONSTRAINT ${quoteIdentifier(constraintName)}`,
|
|
201
|
+
),
|
|
202
|
+
],
|
|
203
|
+
postcheck: [
|
|
204
|
+
step(
|
|
205
|
+
`verify constraint "${constraintName}" does not exist`,
|
|
206
|
+
constraintExistsCheck({
|
|
207
|
+
constraintName,
|
|
208
|
+
schema: schemaName,
|
|
209
|
+
table: tableName,
|
|
210
|
+
exists: false,
|
|
211
|
+
}),
|
|
212
|
+
),
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
148
217
|
/**
|
|
149
218
|
* `kind` feeds the operation's `target.details.objectType`. Descriptor-flow
|
|
150
219
|
* does not carry kind information in its drop-constraint descriptor, so the
|
|
@@ -69,20 +69,3 @@ export function installExtension(options: {
|
|
|
69
69
|
],
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
export function createSchema(schemaName: string): Op {
|
|
74
|
-
return {
|
|
75
|
-
id: `schema.${schemaName}`,
|
|
76
|
-
label: `Create schema "${schemaName}"`,
|
|
77
|
-
operationClass: 'additive',
|
|
78
|
-
target: { id: 'postgres' },
|
|
79
|
-
precheck: [],
|
|
80
|
-
execute: [
|
|
81
|
-
step(
|
|
82
|
-
`Create schema "${schemaName}"`,
|
|
83
|
-
`CREATE SCHEMA IF NOT EXISTS ${quoteIdentifier(schemaName)}`,
|
|
84
|
-
),
|
|
85
|
-
],
|
|
86
|
-
postcheck: [],
|
|
87
|
-
};
|
|
88
|
-
}
|
|
@@ -7,9 +7,9 @@ import type { OperationClass, PostgresPlanTargetDetails } from '../planner-targe
|
|
|
7
7
|
export type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Literal-args shape for a column definition consumed by `
|
|
11
|
-
*
|
|
12
|
-
* already happened in the wrapper.
|
|
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
13
|
*
|
|
14
14
|
* - `typeSql` is the column's DDL type string (e.g. `"integer"`, `"SERIAL"`,
|
|
15
15
|
* `"varchar(100)"`), already produced by `buildColumnTypeSql` in the
|
|
@@ -1,43 +1,5 @@
|
|
|
1
|
-
import { quoteIdentifier } from '../../sql-utils';
|
|
2
1
|
import { qualifyTableName, toRegclassLiteral } from '../planner-sql-checks';
|
|
3
|
-
import { type
|
|
4
|
-
|
|
5
|
-
export function createTable(
|
|
6
|
-
schemaName: string,
|
|
7
|
-
tableName: string,
|
|
8
|
-
columns: ReadonlyArray<ColumnSpec>,
|
|
9
|
-
primaryKey?: { readonly columns: readonly string[] },
|
|
10
|
-
): Op {
|
|
11
|
-
const qualified = qualifyTableName(schemaName, tableName);
|
|
12
|
-
const columnDefs = columns.map(renderColumnDefinition);
|
|
13
|
-
const constraintDefs: string[] = [];
|
|
14
|
-
if (primaryKey) {
|
|
15
|
-
constraintDefs.push(`PRIMARY KEY (${primaryKey.columns.map(quoteIdentifier).join(', ')})`);
|
|
16
|
-
}
|
|
17
|
-
const allDefs = [...columnDefs, ...constraintDefs];
|
|
18
|
-
const createSql = `CREATE TABLE ${qualified} (\n ${allDefs.join(',\n ')}\n)`;
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
id: `table.${tableName}`,
|
|
22
|
-
label: `Create table "${tableName}"`,
|
|
23
|
-
summary: `Creates table "${tableName}"`,
|
|
24
|
-
operationClass: 'additive',
|
|
25
|
-
target: targetDetails('table', tableName, schemaName),
|
|
26
|
-
precheck: [
|
|
27
|
-
step(
|
|
28
|
-
`ensure table "${tableName}" does not exist`,
|
|
29
|
-
`SELECT to_regclass(${toRegclassLiteral(schemaName, tableName)}) IS NULL`,
|
|
30
|
-
),
|
|
31
|
-
],
|
|
32
|
-
execute: [step(`create table "${tableName}"`, createSql)],
|
|
33
|
-
postcheck: [
|
|
34
|
-
step(
|
|
35
|
-
`verify table "${tableName}" exists`,
|
|
36
|
-
`SELECT to_regclass(${toRegclassLiteral(schemaName, tableName)}) IS NOT NULL`,
|
|
37
|
-
),
|
|
38
|
-
],
|
|
39
|
-
};
|
|
40
|
-
}
|
|
2
|
+
import { type Op, step, targetDetails } from './shared';
|
|
41
3
|
|
|
42
4
|
export function dropTable(schemaName: string, tableName: string): Op {
|
|
43
5
|
const qualified = qualifyTableName(schemaName, tableName);
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import type { CodecControlHooks } from '@prisma-next/family-sql/control';
|
|
2
2
|
import type {
|
|
3
|
-
ForeignKey,
|
|
4
3
|
PostgresEnumStorageEntry,
|
|
5
|
-
ReferentialAction,
|
|
6
4
|
StorageColumn,
|
|
7
5
|
StorageTable,
|
|
8
6
|
StorageTypeInstance,
|
|
9
7
|
} from '@prisma-next/sql-contract/types';
|
|
8
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
10
9
|
import { escapeLiteral, quoteIdentifier } from '../sql-utils';
|
|
11
10
|
import type { PostgresColumnDefault } from '../types';
|
|
12
|
-
import { qualifyTableName } from './planner-sql-checks';
|
|
13
11
|
import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
14
12
|
|
|
15
13
|
export function buildCreateTableSql(
|
|
16
14
|
qualifiedTableName: string,
|
|
17
15
|
table: StorageTable,
|
|
18
|
-
codecHooks:
|
|
16
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
19
17
|
storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {},
|
|
20
18
|
): string {
|
|
21
19
|
const columnDefinitions = Object.entries(table.columns).map(
|
|
@@ -80,7 +78,7 @@ function assertSafeDefaultExpression(expression: string): void {
|
|
|
80
78
|
*/
|
|
81
79
|
export function buildColumnTypeSql(
|
|
82
80
|
column: StorageColumn,
|
|
83
|
-
codecHooks:
|
|
81
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
84
82
|
storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {},
|
|
85
83
|
allowPseudoTypes = true,
|
|
86
84
|
): string {
|
|
@@ -116,9 +114,9 @@ export function buildColumnTypeSql(
|
|
|
116
114
|
|
|
117
115
|
function expandParameterizedTypeSql(
|
|
118
116
|
column: Pick<StorageColumn, 'nativeType' | 'codecId' | 'typeParams'>,
|
|
119
|
-
codecHooks:
|
|
117
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
120
118
|
): string | null {
|
|
121
|
-
if (!column.typeParams) {
|
|
119
|
+
if (!column.typeParams || Object.keys(column.typeParams).length === 0) {
|
|
122
120
|
return null;
|
|
123
121
|
}
|
|
124
122
|
|
|
@@ -144,7 +142,7 @@ function expandParameterizedTypeSql(
|
|
|
144
142
|
const expanded = hooks.expandNativeType({
|
|
145
143
|
nativeType: column.nativeType,
|
|
146
144
|
codecId: column.codecId,
|
|
147
|
-
typeParams
|
|
145
|
+
...ifDefined('typeParams', column.typeParams),
|
|
148
146
|
});
|
|
149
147
|
|
|
150
148
|
return expanded !== column.nativeType ? expanded : null;
|
|
@@ -200,7 +198,7 @@ export function buildAddColumnSql(
|
|
|
200
198
|
qualifiedTableName: string,
|
|
201
199
|
columnName: string,
|
|
202
200
|
column: StorageColumn,
|
|
203
|
-
codecHooks:
|
|
201
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
204
202
|
temporaryDefault?: string | null,
|
|
205
203
|
storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {},
|
|
206
204
|
): string {
|
|
@@ -216,42 +214,3 @@ export function buildAddColumnSql(
|
|
|
216
214
|
].filter(Boolean);
|
|
217
215
|
return parts.join(' ');
|
|
218
216
|
}
|
|
219
|
-
|
|
220
|
-
const REFERENTIAL_ACTION_SQL: Record<ReferentialAction, string> = {
|
|
221
|
-
noAction: 'NO ACTION',
|
|
222
|
-
restrict: 'RESTRICT',
|
|
223
|
-
cascade: 'CASCADE',
|
|
224
|
-
setNull: 'SET NULL',
|
|
225
|
-
setDefault: 'SET DEFAULT',
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
export function buildForeignKeySql(
|
|
229
|
-
schemaName: string,
|
|
230
|
-
tableName: string,
|
|
231
|
-
fkName: string,
|
|
232
|
-
foreignKey: ForeignKey,
|
|
233
|
-
): string {
|
|
234
|
-
let sql = `ALTER TABLE ${qualifyTableName(schemaName, tableName)}
|
|
235
|
-
ADD CONSTRAINT ${quoteIdentifier(fkName)}
|
|
236
|
-
FOREIGN KEY (${foreignKey.source.columns.map(quoteIdentifier).join(', ')})
|
|
237
|
-
REFERENCES ${qualifyTableName(schemaName, foreignKey.target.tableName)} (${foreignKey.target.columns
|
|
238
|
-
.map(quoteIdentifier)
|
|
239
|
-
.join(', ')})`;
|
|
240
|
-
|
|
241
|
-
if (foreignKey.onDelete !== undefined) {
|
|
242
|
-
const action = REFERENTIAL_ACTION_SQL[foreignKey.onDelete];
|
|
243
|
-
if (!action) {
|
|
244
|
-
throw new Error(`Unknown referential action for onDelete: ${String(foreignKey.onDelete)}`);
|
|
245
|
-
}
|
|
246
|
-
sql += `\nON DELETE ${action}`;
|
|
247
|
-
}
|
|
248
|
-
if (foreignKey.onUpdate !== undefined) {
|
|
249
|
-
const action = REFERENTIAL_ACTION_SQL[foreignKey.onUpdate];
|
|
250
|
-
if (!action) {
|
|
251
|
-
throw new Error(`Unknown referential action for onUpdate: ${String(foreignKey.onUpdate)}`);
|
|
252
|
-
}
|
|
253
|
-
sql += `\nON UPDATE ${action}`;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return sql;
|
|
257
|
-
}
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
27
|
+
import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
|
|
27
28
|
import type {
|
|
28
29
|
MigrationPlanWithAuthoringSurface,
|
|
29
30
|
OpFactoryCall,
|
|
@@ -43,16 +44,23 @@ export class TypeScriptRenderablePostgresMigration
|
|
|
43
44
|
readonly #calls: readonly OpFactoryCall[];
|
|
44
45
|
readonly #meta: MigrationMeta;
|
|
45
46
|
readonly #spaceId: string;
|
|
47
|
+
readonly #lowerer: Lowerer | undefined;
|
|
46
48
|
|
|
47
|
-
constructor(
|
|
49
|
+
constructor(
|
|
50
|
+
calls: readonly OpFactoryCall[],
|
|
51
|
+
meta: MigrationMeta,
|
|
52
|
+
spaceId: string,
|
|
53
|
+
lowerer?: Lowerer,
|
|
54
|
+
) {
|
|
48
55
|
super();
|
|
49
56
|
this.#calls = calls;
|
|
50
57
|
this.#meta = meta;
|
|
51
58
|
this.#spaceId = spaceId;
|
|
59
|
+
this.#lowerer = lowerer;
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
override get operations(): readonly Op[] {
|
|
55
|
-
return renderOps(this.#calls);
|
|
63
|
+
return renderOps(this.#calls, this.#lowerer);
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
override describe(): MigrationMeta {
|
|
@@ -69,9 +77,6 @@ export class TypeScriptRenderablePostgresMigration
|
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
renderTypeScript(): string {
|
|
72
|
-
return renderCallsToTypeScript(this.#calls, {
|
|
73
|
-
from: this.#meta.from,
|
|
74
|
-
to: this.#meta.to,
|
|
75
|
-
});
|
|
80
|
+
return renderCallsToTypeScript(this.#calls, { from: this.#meta.from, to: this.#meta.to });
|
|
76
81
|
}
|
|
77
82
|
}
|
|
@@ -20,11 +20,11 @@ import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
|
20
20
|
* site.
|
|
21
21
|
*/
|
|
22
22
|
export function qualifyTableName(schema: string, table: string): string {
|
|
23
|
-
return postgresCreateNamespace({ id: schema }).qualifyTable(table);
|
|
23
|
+
return postgresCreateNamespace({ id: schema, entries: { table: {} } }).qualifyTable(table);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function toRegclassLiteral(schema: string, name: string): string {
|
|
27
|
-
return postgresCreateNamespace({ id: schema }).regclassLiteral(name);
|
|
27
|
+
return postgresCreateNamespace({ id: schema, entries: { table: {} } }).regclassLiteral(name);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -43,7 +43,7 @@ export function constraintExistsCheck({
|
|
|
43
43
|
table?: string;
|
|
44
44
|
exists?: boolean;
|
|
45
45
|
}): string {
|
|
46
|
-
const namespace = postgresCreateNamespace({ id: schema });
|
|
46
|
+
const namespace = postgresCreateNamespace({ id: schema, entries: { table: {} } });
|
|
47
47
|
const existsClause = exists ? 'EXISTS' : 'NOT EXISTS';
|
|
48
48
|
const tableFilter = table
|
|
49
49
|
? `AND c.conrelid = to_regclass(${namespace.regclassLiteral(table)})`
|
|
@@ -68,7 +68,7 @@ export function columnExistsCheck({
|
|
|
68
68
|
column: string;
|
|
69
69
|
exists?: boolean;
|
|
70
70
|
}): string {
|
|
71
|
-
const namespace = postgresCreateNamespace({ id: schema });
|
|
71
|
+
const namespace = postgresCreateNamespace({ id: schema, entries: { table: {} } });
|
|
72
72
|
const existsClause = exists ? '' : 'NOT ';
|
|
73
73
|
return `SELECT ${existsClause}EXISTS (
|
|
74
74
|
SELECT 1
|
|
@@ -90,7 +90,7 @@ export function columnNullabilityCheck({
|
|
|
90
90
|
column: string;
|
|
91
91
|
nullable: boolean;
|
|
92
92
|
}): string {
|
|
93
|
-
const namespace = postgresCreateNamespace({ id: schema });
|
|
93
|
+
const namespace = postgresCreateNamespace({ id: schema, entries: { table: {} } });
|
|
94
94
|
const expected = nullable ? 'YES' : 'NO';
|
|
95
95
|
return `SELECT EXISTS (
|
|
96
96
|
SELECT 1
|
|
@@ -111,7 +111,7 @@ export function columnHasNoDefaultCheck(opts: {
|
|
|
111
111
|
table: string;
|
|
112
112
|
column: string;
|
|
113
113
|
}): string {
|
|
114
|
-
const namespace = postgresCreateNamespace({ id: opts.schema });
|
|
114
|
+
const namespace = postgresCreateNamespace({ id: opts.schema, entries: { table: {} } });
|
|
115
115
|
return `SELECT NOT EXISTS (
|
|
116
116
|
SELECT 1
|
|
117
117
|
FROM information_schema.columns
|
|
@@ -282,7 +282,7 @@ export function columnTypeCheck({
|
|
|
282
282
|
column: string;
|
|
283
283
|
expectedType: string;
|
|
284
284
|
}): string {
|
|
285
|
-
const namespace = postgresCreateNamespace({ id: schema });
|
|
285
|
+
const namespace = postgresCreateNamespace({ id: schema, entries: { table: {} } });
|
|
286
286
|
return `SELECT EXISTS (
|
|
287
287
|
SELECT 1
|
|
288
288
|
FROM pg_attribute a
|
|
@@ -307,7 +307,7 @@ export function columnDefaultExistsCheck({
|
|
|
307
307
|
column: string;
|
|
308
308
|
exists?: boolean;
|
|
309
309
|
}): string {
|
|
310
|
-
const namespace = postgresCreateNamespace({ id: schema });
|
|
310
|
+
const namespace = postgresCreateNamespace({ id: schema, entries: { table: {} } });
|
|
311
311
|
const nullCheck = exists ? 'IS NOT NULL' : 'IS NULL';
|
|
312
312
|
return `SELECT EXISTS (
|
|
313
313
|
SELECT 1
|
|
@@ -325,7 +325,7 @@ export function tableHasPrimaryKeyCheck(
|
|
|
325
325
|
exists: boolean,
|
|
326
326
|
constraintName?: string,
|
|
327
327
|
): string {
|
|
328
|
-
const namespace = postgresCreateNamespace({ id: schema });
|
|
328
|
+
const namespace = postgresCreateNamespace({ id: schema, entries: { table: {} } });
|
|
329
329
|
const comparison = exists ? '' : 'NOT ';
|
|
330
330
|
const constraintFilter = constraintName
|
|
331
331
|
? `AND c2.relname = '${escapeLiteral(constraintName)}'`
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
MigrationOperationPolicy,
|
|
25
25
|
SqlMigrationPlanOperation,
|
|
26
26
|
} from '@prisma-next/family-sql/control';
|
|
27
|
+
import { resolveValueSetValues } from '@prisma-next/family-sql/control';
|
|
27
28
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
28
29
|
import type { SchemaIssue } from '@prisma-next/framework-components/control';
|
|
29
30
|
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
@@ -31,10 +32,11 @@ import {
|
|
|
31
32
|
isPostgresEnumStorageEntry,
|
|
32
33
|
type PostgresEnumStorageEntry,
|
|
33
34
|
type SqlStorage,
|
|
34
|
-
|
|
35
|
+
StorageTable,
|
|
35
36
|
type StorageTypeInstance,
|
|
36
37
|
} from '@prisma-next/sql-contract/types';
|
|
37
38
|
import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
|
|
39
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
38
40
|
import { PostgresEnumType } from '../postgres-enum-type';
|
|
39
41
|
import { isPostgresSchema } from '../postgres-schema';
|
|
40
42
|
import {
|
|
@@ -43,11 +45,13 @@ import {
|
|
|
43
45
|
resolveDdlSchemaForNamespaceStorage,
|
|
44
46
|
} from './enum-planning';
|
|
45
47
|
import {
|
|
48
|
+
AddCheckConstraintCall,
|
|
46
49
|
AddColumnCall,
|
|
47
50
|
AddEnumValuesCall,
|
|
48
51
|
AlterColumnTypeCall,
|
|
49
52
|
CreateEnumTypeCall,
|
|
50
53
|
DataTransformCall,
|
|
54
|
+
DropCheckConstraintCall,
|
|
51
55
|
DropEnumTypeCall,
|
|
52
56
|
type PostgresOpFactoryCall,
|
|
53
57
|
RawSqlCall,
|
|
@@ -93,7 +97,7 @@ export function tableAt(
|
|
|
93
97
|
): StorageTable | undefined {
|
|
94
98
|
// Namespace.tables is typed as Record<string, IRNode> at the interface level;
|
|
95
99
|
// SQL family namespaces always hold StorageTable instances.
|
|
96
|
-
return storage.namespaces[namespaceId]?.
|
|
100
|
+
return storage.namespaces[namespaceId]?.entries.table[tableName] as StorageTable | undefined;
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
/**
|
|
@@ -134,8 +138,8 @@ const DEFAULT_ENUM_NAMESPACE_ID = 'public';
|
|
|
134
138
|
|
|
135
139
|
function namespaceHasEnum(storage: SqlStorage, namespaceId: string, typeName: string): boolean {
|
|
136
140
|
const ns = storage.namespaces[namespaceId];
|
|
137
|
-
if (!
|
|
138
|
-
return
|
|
141
|
+
if (!isPostgresSchema(ns)) return false;
|
|
142
|
+
return ns.entries.type[typeName] !== undefined;
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
/**
|
|
@@ -166,7 +170,7 @@ function resolveColumnEnumNamespace(
|
|
|
166
170
|
|
|
167
171
|
/**
|
|
168
172
|
* Finds a type entry by explicit namespace coordinate. Namespace types (e.g.
|
|
169
|
-
* Postgres enums) live under `storage.namespaces[nsId].
|
|
173
|
+
* Postgres enums) live under `storage.namespaces[nsId].entries.type`. Returns the
|
|
170
174
|
* entry from the named namespace only — never scans other namespaces, so two
|
|
171
175
|
* namespaces that hold an enum with the same name resolve independently.
|
|
172
176
|
*/
|
|
@@ -176,8 +180,12 @@ function locateNamespaceType(
|
|
|
176
180
|
typeName: string,
|
|
177
181
|
): PostgresEnumStorageEntry | undefined {
|
|
178
182
|
const ns = storage.namespaces[namespaceId];
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
const raw = ns?.entries['type']?.[typeName];
|
|
184
|
+
if (raw === undefined) return undefined;
|
|
185
|
+
return blindCast<
|
|
186
|
+
PostgresEnumStorageEntry,
|
|
187
|
+
'postgres type slot carries PostgresEnumStorageEntry at the postgres target layer'
|
|
188
|
+
>(raw);
|
|
181
189
|
}
|
|
182
190
|
|
|
183
191
|
// ============================================================================
|
|
@@ -248,6 +256,7 @@ function buildColumnSpec(
|
|
|
248
256
|
name: column,
|
|
249
257
|
typeSql: buildColumnTypeSql(col, mutableHooks, mutableTypes),
|
|
250
258
|
defaultSql: buildColumnDefaultSql(col.default, col),
|
|
259
|
+
columnDefault: col.default,
|
|
251
260
|
nullable: overrides?.nullable ?? col.nullable,
|
|
252
261
|
};
|
|
253
262
|
}
|
|
@@ -436,7 +445,7 @@ function enumRebuildCallRecipe(
|
|
|
436
445
|
// same-named enums in distinct namespaces keep their columns disjoint.
|
|
437
446
|
const columnRefs: { namespaceId: string; table: string; column: string }[] = [];
|
|
438
447
|
for (const [nsId, ns] of Object.entries(ctx.toContract.storage.namespaces)) {
|
|
439
|
-
for (const [tableName, tableNode] of Object.entries(ns.
|
|
448
|
+
for (const [tableName, tableNode] of Object.entries(ns.entries.table)) {
|
|
440
449
|
const table = tableNode as StorageTable;
|
|
441
450
|
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
442
451
|
if (
|
|
@@ -639,9 +648,10 @@ export const nativeEnumPlanCallStrategy: CallMigrationStrategy = (issues, ctx) =
|
|
|
639
648
|
function collectPostgresEnumTypes(storage: SqlStorage): ReadonlyMap<string, PostgresEnumType> {
|
|
640
649
|
const result = new Map<string, PostgresEnumType>();
|
|
641
650
|
for (const [nsId, ns] of Object.entries(storage.namespaces)) {
|
|
642
|
-
if (!(
|
|
643
|
-
const
|
|
644
|
-
|
|
651
|
+
if (!isPostgresSchema(ns)) continue;
|
|
652
|
+
for (const [name, instance] of Object.entries(ns.entries.type).sort(([a], [b]) =>
|
|
653
|
+
a.localeCompare(b),
|
|
654
|
+
)) {
|
|
645
655
|
if (instance instanceof PostgresEnumType) {
|
|
646
656
|
result.set(enumCompoundKey(nsId, name), instance);
|
|
647
657
|
}
|
|
@@ -650,6 +660,133 @@ function collectPostgresEnumTypes(storage: SqlStorage): ReadonlyMap<string, Post
|
|
|
650
660
|
return result;
|
|
651
661
|
}
|
|
652
662
|
|
|
663
|
+
/**
|
|
664
|
+
* Collects every check constraint from a table in the contract storage.
|
|
665
|
+
* Returns an empty array when the table has no checks or the table is absent.
|
|
666
|
+
*/
|
|
667
|
+
function collectContractChecks(
|
|
668
|
+
storage: SqlStorage,
|
|
669
|
+
namespaceId: string,
|
|
670
|
+
tableName: string,
|
|
671
|
+
): ReadonlyArray<{ name: string; column: string; permittedValues: readonly string[] }> {
|
|
672
|
+
const ns = storage.namespaces[namespaceId];
|
|
673
|
+
const tableRaw = ns?.entries.table[tableName];
|
|
674
|
+
if (!(tableRaw instanceof StorageTable)) return [];
|
|
675
|
+
const checks = tableRaw.checks;
|
|
676
|
+
if (!checks || checks.length === 0) return [];
|
|
677
|
+
return checks.map((c) => ({
|
|
678
|
+
name: c.name,
|
|
679
|
+
column: c.column,
|
|
680
|
+
permittedValues: resolveValueSetValues(
|
|
681
|
+
c.valueSet,
|
|
682
|
+
storage,
|
|
683
|
+
`check "${c.name}" on "${tableName}"`,
|
|
684
|
+
),
|
|
685
|
+
}));
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Compares two value arrays as unordered sets.
|
|
690
|
+
*/
|
|
691
|
+
function checkValueSetsEqual(a: readonly string[], b: readonly string[]): boolean {
|
|
692
|
+
if (a.length !== b.length) return false;
|
|
693
|
+
const bSet = new Set(b);
|
|
694
|
+
return a.every((v) => bSet.has(v));
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Plans check-constraint migrations for `enumType`-authored columns.
|
|
699
|
+
*
|
|
700
|
+
* Walks every namespace's tables in the target contract. For each table that
|
|
701
|
+
* carries `checks`, diffs the contract-expected checks against the live
|
|
702
|
+
* schema's checks:
|
|
703
|
+
*
|
|
704
|
+
* - Check in contract, absent from live DB → `AddCheckConstraintCall`.
|
|
705
|
+
* - Check in live DB, absent from contract → `DropCheckConstraintCall`.
|
|
706
|
+
* - Check on both sides but value sets differ → `DropCheckConstraintCall`
|
|
707
|
+
* then `AddCheckConstraintCall` (drop + recreate; a check predicate cannot
|
|
708
|
+
* be altered in place).
|
|
709
|
+
*
|
|
710
|
+
* Consumes `check_missing`, `check_removed`, and `check_mismatch` issues.
|
|
711
|
+
* Does not touch the native enum path (`nativeEnumPlanCallStrategy` is
|
|
712
|
+
* unchanged).
|
|
713
|
+
*/
|
|
714
|
+
export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
715
|
+
const calls: PostgresOpFactoryCall[] = [];
|
|
716
|
+
const handledIssueKeys = new Set<string>();
|
|
717
|
+
|
|
718
|
+
for (const [namespaceId, ns] of Object.entries(ctx.toContract.storage.namespaces)) {
|
|
719
|
+
for (const tableName of Object.keys(ns.entries.table)) {
|
|
720
|
+
const contractChecks = collectContractChecks(ctx.toContract.storage, namespaceId, tableName);
|
|
721
|
+
if (contractChecks.length === 0) continue;
|
|
722
|
+
|
|
723
|
+
const schemaTable = ctx.schema.tables[tableName];
|
|
724
|
+
const liveChecks = schemaTable?.checks ?? [];
|
|
725
|
+
const ddlSchema = resolveDdlSchemaForNamespace(ctx, namespaceId);
|
|
726
|
+
|
|
727
|
+
for (const contractCheck of contractChecks) {
|
|
728
|
+
const liveCheck = liveChecks.find((c) => c.name === contractCheck.name);
|
|
729
|
+
const issueKey = `${tableName}${contractCheck.name}`;
|
|
730
|
+
if (!liveCheck) {
|
|
731
|
+
calls.push(
|
|
732
|
+
new AddCheckConstraintCall(
|
|
733
|
+
ddlSchema,
|
|
734
|
+
tableName,
|
|
735
|
+
contractCheck.name,
|
|
736
|
+
contractCheck.column,
|
|
737
|
+
contractCheck.permittedValues,
|
|
738
|
+
),
|
|
739
|
+
);
|
|
740
|
+
handledIssueKeys.add(issueKey);
|
|
741
|
+
} else if (!checkValueSetsEqual(contractCheck.permittedValues, liveCheck.permittedValues)) {
|
|
742
|
+
calls.push(
|
|
743
|
+
new DropCheckConstraintCall(ddlSchema, tableName, contractCheck.name),
|
|
744
|
+
new AddCheckConstraintCall(
|
|
745
|
+
ddlSchema,
|
|
746
|
+
tableName,
|
|
747
|
+
contractCheck.name,
|
|
748
|
+
contractCheck.column,
|
|
749
|
+
contractCheck.permittedValues,
|
|
750
|
+
),
|
|
751
|
+
);
|
|
752
|
+
handledIssueKeys.add(issueKey);
|
|
753
|
+
}
|
|
754
|
+
// else: values match — no op needed, still consume the issue
|
|
755
|
+
else {
|
|
756
|
+
handledIssueKeys.add(issueKey);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Emit drops for checks that are live but not in the contract.
|
|
761
|
+
for (const liveCheck of liveChecks) {
|
|
762
|
+
const inContract = contractChecks.some((c) => c.name === liveCheck.name);
|
|
763
|
+
if (!inContract) {
|
|
764
|
+
const issueKey = `${tableName}${liveCheck.name}`;
|
|
765
|
+
calls.push(new DropCheckConstraintCall(ddlSchema, tableName, liveCheck.name));
|
|
766
|
+
handledIssueKeys.add(issueKey);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (calls.length === 0 && handledIssueKeys.size === 0) return { kind: 'no_match' };
|
|
773
|
+
|
|
774
|
+
const remaining = issues.filter((issue) => {
|
|
775
|
+
if (
|
|
776
|
+
issue.kind !== 'check_missing' &&
|
|
777
|
+
issue.kind !== 'check_removed' &&
|
|
778
|
+
issue.kind !== 'check_mismatch'
|
|
779
|
+
) {
|
|
780
|
+
return true;
|
|
781
|
+
}
|
|
782
|
+
if (!issue.table || !issue.indexOrConstraint) return true;
|
|
783
|
+
const key = `${issue.table}${issue.indexOrConstraint}`;
|
|
784
|
+
return !handledIssueKeys.has(key);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
return { kind: 'match', issues: remaining, calls };
|
|
788
|
+
};
|
|
789
|
+
|
|
653
790
|
/**
|
|
654
791
|
* Dispatches non-enum codec-typed storage types through their codec's
|
|
655
792
|
* `planTypeOperations` hook (the authoritative source for codec-driven DDL
|
|
@@ -922,6 +1059,7 @@ export const postgresPlannerStrategies: readonly CallMigrationStrategy[] = [
|
|
|
922
1059
|
typeChangeCallStrategy,
|
|
923
1060
|
nullableTighteningCallStrategy,
|
|
924
1061
|
nativeEnumPlanCallStrategy,
|
|
1062
|
+
checkConstraintPlanCallStrategy,
|
|
925
1063
|
storageTypePlanCallStrategy,
|
|
926
1064
|
notNullAddColumnCallStrategy,
|
|
927
1065
|
];
|