@prisma-next/target-postgres 0.12.0 → 0.13.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.
Files changed (207) hide show
  1. package/dist/{codec-ids-D9fJ4HP5.d.mts → codec-ids-B1vOchLE.d.mts} +3 -2
  2. package/dist/codec-ids-B1vOchLE.d.mts.map +1 -0
  3. package/dist/{codec-ids-C5qzBqus.mjs → codec-ids-CTikp1if.mjs} +3 -2
  4. package/dist/codec-ids-CTikp1if.mjs.map +1 -0
  5. package/dist/codec-ids.d.mts +2 -2
  6. package/dist/codec-ids.mjs +2 -2
  7. package/dist/{codec-types-CRlHq7Cz.d.mts → codec-types-CnFiNML4.d.mts} +8 -9
  8. package/dist/codec-types-CnFiNML4.d.mts.map +1 -0
  9. package/dist/codec-types.d.mts +2 -2
  10. package/dist/{codecs-Dud5KDNk.d.mts → codecs-CBpEv4s5.d.mts} +33 -35
  11. package/dist/codecs-CBpEv4s5.d.mts.map +1 -0
  12. package/dist/codecs.d.mts +1 -1
  13. package/dist/codecs.mjs +37 -2
  14. package/dist/codecs.mjs.map +1 -1
  15. package/dist/contract-free.d.mts +80 -0
  16. package/dist/contract-free.d.mts.map +1 -0
  17. package/dist/contract-free.mjs +117 -0
  18. package/dist/contract-free.mjs.map +1 -0
  19. package/dist/control.d.mts +1 -1
  20. package/dist/control.d.mts.map +1 -1
  21. package/dist/control.mjs +66 -40
  22. package/dist/control.mjs.map +1 -1
  23. package/dist/{data-transform-CdtGUWp2.mjs → data-transform-D25tLeYU.mjs} +1 -1
  24. package/dist/{data-transform-CdtGUWp2.mjs.map → data-transform-D25tLeYU.mjs.map} +1 -1
  25. package/dist/{data-transform-bmOKkygi.d.mts → data-transform-DGOqcLrf.d.mts} +2 -2
  26. package/dist/{data-transform-bmOKkygi.d.mts.map → data-transform-DGOqcLrf.d.mts.map} +1 -1
  27. package/dist/data-transform.d.mts +1 -1
  28. package/dist/data-transform.mjs +1 -1
  29. package/dist/ddl-77SyXgFt.mjs +30 -0
  30. package/dist/ddl-77SyXgFt.mjs.map +1 -0
  31. package/dist/ddl.d.mts +2 -0
  32. package/dist/ddl.mjs +2 -0
  33. package/dist/{default-normalizer-CRscvhS5.mjs → default-normalizer-DyyCHQWs.mjs} +1 -1
  34. package/dist/{default-normalizer-CRscvhS5.mjs.map → default-normalizer-DyyCHQWs.mjs.map} +1 -1
  35. package/dist/default-normalizer.mjs +1 -1
  36. package/dist/descriptor-meta-DKmj-IMN.mjs +14 -0
  37. package/dist/descriptor-meta-DKmj-IMN.mjs.map +1 -0
  38. package/dist/{descriptor-meta-DLA2xV6B.mjs → descriptor-meta-runtime-My8_s4cs.mjs} +82 -78
  39. package/dist/descriptor-meta-runtime-My8_s4cs.mjs.map +1 -0
  40. package/dist/{enum-planning-Dz0Ye3Lb.mjs → enum-planning-BCyvlFHk.mjs} +0 -0
  41. package/dist/{enum-planning-Dz0Ye3Lb.mjs.map → enum-planning-BCyvlFHk.mjs.map} +1 -1
  42. package/dist/enum-planning.d.mts +1 -1
  43. package/dist/enum-planning.mjs +1 -1
  44. package/dist/{errors--zafB5_n.mjs → errors-CUk87ByX.mjs} +1 -1
  45. package/dist/{errors--zafB5_n.mjs.map → errors-CUk87ByX.mjs.map} +1 -1
  46. package/dist/errors.d.mts.map +1 -1
  47. package/dist/errors.mjs +1 -1
  48. package/dist/{issue-planner-ByQhUzS4.mjs → issue-planner-Br0pt1Ea.mjs} +130 -28
  49. package/dist/issue-planner-Br0pt1Ea.mjs.map +1 -0
  50. package/dist/issue-planner.d.mts +1 -1
  51. package/dist/issue-planner.d.mts.map +1 -1
  52. package/dist/issue-planner.mjs +1 -1
  53. package/dist/migration.d.mts +7 -8
  54. package/dist/migration.d.mts.map +1 -1
  55. package/dist/migration.mjs +5 -4
  56. package/dist/migration.mjs.map +1 -1
  57. package/dist/{native-type-normalizer-ClNPq__-.mjs → native-type-normalizer-Bc9XJzWC.mjs} +1 -1
  58. package/dist/{native-type-normalizer-ClNPq__-.mjs.map → native-type-normalizer-Bc9XJzWC.mjs.map} +1 -1
  59. package/dist/native-type-normalizer.mjs +1 -1
  60. package/dist/nodes-779hmCfL.d.mts +40 -0
  61. package/dist/nodes-779hmCfL.d.mts.map +1 -0
  62. package/dist/nodes-DZk2JZG3.mjs +47 -0
  63. package/dist/nodes-DZk2JZG3.mjs.map +1 -0
  64. package/dist/op-factory-call-D2aAUhmS.mjs +1307 -0
  65. package/dist/op-factory-call-D2aAUhmS.mjs.map +1 -0
  66. package/dist/{op-factory-call-Drccm_JD.d.mts → op-factory-call-DMA86_2D.d.mts} +39 -14
  67. package/dist/op-factory-call-DMA86_2D.d.mts.map +1 -0
  68. package/dist/op-factory-call.d.mts +2 -2
  69. package/dist/op-factory-call.mjs +2 -2
  70. package/dist/pack.d.mts +13 -12
  71. package/dist/pack.d.mts.map +1 -1
  72. package/dist/pack.mjs +1 -1
  73. package/dist/planner-CAYPJObw.mjs +344 -0
  74. package/dist/planner-CAYPJObw.mjs.map +1 -0
  75. package/dist/{planner-ddl-builders-BxRCSn_b.mjs → planner-ddl-builders-Cw2n2llW.mjs} +7 -30
  76. package/dist/planner-ddl-builders-Cw2n2llW.mjs.map +1 -0
  77. package/dist/planner-ddl-builders.d.mts +6 -7
  78. package/dist/planner-ddl-builders.d.mts.map +1 -1
  79. package/dist/planner-ddl-builders.mjs +2 -2
  80. package/dist/{planner-identity-values-ojX-6cPV.mjs → planner-identity-values-BIpa5p2I.mjs} +1 -1
  81. package/dist/{planner-identity-values-ojX-6cPV.mjs.map → planner-identity-values-BIpa5p2I.mjs.map} +1 -1
  82. package/dist/planner-identity-values.mjs +1 -1
  83. package/dist/{planner-produced-postgres-migration-p-VKkCia.d.mts → planner-produced-postgres-migration-B4EDvLdz.d.mts} +5 -4
  84. package/dist/planner-produced-postgres-migration-B4EDvLdz.d.mts.map +1 -0
  85. package/dist/{planner-produced-postgres-migration-N1yqYg20.mjs → planner-produced-postgres-migration-NSEhWL0L.mjs} +8 -6
  86. package/dist/planner-produced-postgres-migration-NSEhWL0L.mjs.map +1 -0
  87. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  88. package/dist/planner-produced-postgres-migration.mjs +1 -1
  89. package/dist/{planner-schema-lookup-BGyukuzG.mjs → planner-schema-lookup-CiVaAQP-.mjs} +1 -1
  90. package/dist/{planner-schema-lookup-BGyukuzG.mjs.map → planner-schema-lookup-CiVaAQP-.mjs.map} +1 -1
  91. package/dist/planner-schema-lookup.mjs +1 -1
  92. package/dist/{planner-sql-checks-D3H-xOO1.mjs → planner-sql-checks-DAdhnI2c.mjs} +41 -30
  93. package/dist/planner-sql-checks-DAdhnI2c.mjs.map +1 -0
  94. package/dist/planner-sql-checks.d.mts.map +1 -1
  95. package/dist/planner-sql-checks.mjs +1 -1
  96. package/dist/{planner-target-details-CIj61DUj.d.mts → planner-target-details-CIY6tLeo.d.mts} +2 -2
  97. package/dist/planner-target-details-CIY6tLeo.d.mts.map +1 -0
  98. package/dist/planner-target-details.d.mts +2 -2
  99. package/dist/planner-type-resolution-836DExFN.mjs +20 -0
  100. package/dist/planner-type-resolution-836DExFN.mjs.map +1 -0
  101. package/dist/planner.d.mts +7 -3
  102. package/dist/planner.d.mts.map +1 -1
  103. package/dist/planner.mjs +1 -1
  104. package/dist/{postgres-contract-serializer-YJvjKrmo.mjs → postgres-contract-serializer-DYTyXjPf.mjs} +33 -24
  105. package/dist/postgres-contract-serializer-DYTyXjPf.mjs.map +1 -0
  106. package/dist/{postgres-enum-type-CNhPTDhy.d.mts → postgres-enum-type-BVn63a89.d.mts} +4 -1
  107. package/dist/postgres-enum-type-BVn63a89.d.mts.map +1 -0
  108. package/dist/{postgres-enum-type-DS-KLVRH.mjs → postgres-enum-type-DPKqCBem.mjs} +2 -1
  109. package/dist/postgres-enum-type-DPKqCBem.mjs.map +1 -0
  110. package/dist/{postgres-migration-uADmx0dW.mjs → postgres-migration-COore9Mz.mjs} +23 -3
  111. package/dist/postgres-migration-COore9Mz.mjs.map +1 -0
  112. package/dist/{postgres-migration-Fd4fQkBw.d.mts → postgres-migration-DZ_gLUOW.d.mts} +25 -3
  113. package/dist/postgres-migration-DZ_gLUOW.d.mts.map +1 -0
  114. package/dist/{postgres-schema-Bm7vjlOv.mjs → postgres-schema-BuxCxbvB.mjs} +29 -14
  115. package/dist/postgres-schema-BuxCxbvB.mjs.map +1 -0
  116. package/dist/{render-ops-BC2PtCkj.mjs → render-ops-BpjstrKQ.mjs} +4 -3
  117. package/dist/{render-ops-BC2PtCkj.mjs.map → render-ops-BpjstrKQ.mjs.map} +1 -1
  118. package/dist/render-ops.d.mts +3 -2
  119. package/dist/render-ops.d.mts.map +1 -1
  120. package/dist/render-ops.mjs +1 -1
  121. package/dist/{render-typescript-CPk7hhWH.mjs → render-typescript-KMgosran.mjs} +5 -2
  122. package/dist/render-typescript-KMgosran.mjs.map +1 -0
  123. package/dist/render-typescript.mjs +1 -1
  124. package/dist/runtime.d.mts.map +1 -1
  125. package/dist/runtime.mjs +3 -3
  126. package/dist/runtime.mjs.map +1 -1
  127. package/dist/{shared-ByhSooBS.d.mts → shared-DarONYBZ.d.mts} +5 -5
  128. package/dist/{shared-ByhSooBS.d.mts.map → shared-DarONYBZ.d.mts.map} +1 -1
  129. package/dist/{sql-utils-B_ruBD-M.mjs → sql-utils-DcfMz4MQ.mjs} +1 -1
  130. package/dist/{sql-utils-B_ruBD-M.mjs.map → sql-utils-DcfMz4MQ.mjs.map} +1 -1
  131. package/dist/sql-utils.mjs +1 -1
  132. package/dist/{types-D-XIpzHA.d.mts → types-BDKkx8MA.d.mts} +1 -1
  133. package/dist/types-BDKkx8MA.d.mts.map +1 -0
  134. package/dist/types.d.mts +16 -11
  135. package/dist/types.d.mts.map +1 -1
  136. package/dist/types.mjs +2 -2
  137. package/package.json +21 -20
  138. package/src/contract-free/columns.ts +49 -0
  139. package/src/contract-free/control-bootstrap.ts +55 -0
  140. package/src/contract-free/ddl.ts +37 -0
  141. package/src/core/ast/table-source.ts +23 -0
  142. package/src/core/authoring.ts +1 -1
  143. package/src/core/codec-ids.ts +1 -0
  144. package/src/core/codecs.ts +44 -0
  145. package/src/core/ddl/nodes.ts +72 -0
  146. package/src/core/descriptor-meta-runtime.ts +28 -0
  147. package/src/core/descriptor-meta.ts +3 -6
  148. package/src/core/migrations/control-policy.ts +234 -0
  149. package/src/core/migrations/issue-planner.ts +81 -13
  150. package/src/core/migrations/op-factory-call.ts +289 -46
  151. package/src/core/migrations/operations/constraints.ts +79 -10
  152. package/src/core/migrations/operations/dependencies.ts +0 -17
  153. package/src/core/migrations/operations/shared.ts +3 -3
  154. package/src/core/migrations/operations/tables.ts +1 -39
  155. package/src/core/migrations/planner-ddl-builders.ts +7 -48
  156. package/src/core/migrations/planner-produced-postgres-migration.ts +11 -6
  157. package/src/core/migrations/planner-sql-checks.ts +9 -9
  158. package/src/core/migrations/planner-strategies.ts +149 -11
  159. package/src/core/migrations/planner-target-details.ts +2 -1
  160. package/src/core/migrations/planner.ts +66 -8
  161. package/src/core/migrations/postgres-migration.ts +41 -0
  162. package/src/core/migrations/render-ops.ts +7 -2
  163. package/src/core/migrations/render-typescript.ts +5 -1
  164. package/src/core/migrations/runner.ts +78 -50
  165. package/src/core/postgres-contract-serializer.ts +52 -46
  166. package/src/core/postgres-enum-type.ts +4 -0
  167. package/src/core/postgres-schema.ts +43 -25
  168. package/src/exports/contract-free.ts +7 -0
  169. package/src/exports/control.ts +6 -8
  170. package/src/exports/ddl.ts +7 -0
  171. package/src/exports/migration.ts +11 -2
  172. package/src/exports/op-factory-call.ts +2 -0
  173. package/src/exports/planner-ddl-builders.ts +0 -1
  174. package/src/exports/runtime.ts +2 -2
  175. package/dist/codec-ids-C5qzBqus.mjs.map +0 -1
  176. package/dist/codec-ids-D9fJ4HP5.d.mts.map +0 -1
  177. package/dist/codec-types-CRlHq7Cz.d.mts.map +0 -1
  178. package/dist/codecs-Dud5KDNk.d.mts.map +0 -1
  179. package/dist/descriptor-meta-DLA2xV6B.mjs.map +0 -1
  180. package/dist/issue-planner-ByQhUzS4.mjs.map +0 -1
  181. package/dist/op-factory-call-B0WNg30h.mjs +0 -625
  182. package/dist/op-factory-call-B0WNg30h.mjs.map +0 -1
  183. package/dist/op-factory-call-Drccm_JD.d.mts.map +0 -1
  184. package/dist/planner-ClF0y0YR.mjs +0 -177
  185. package/dist/planner-ClF0y0YR.mjs.map +0 -1
  186. package/dist/planner-ddl-builders-BxRCSn_b.mjs.map +0 -1
  187. package/dist/planner-produced-postgres-migration-N1yqYg20.mjs.map +0 -1
  188. package/dist/planner-produced-postgres-migration-p-VKkCia.d.mts.map +0 -1
  189. package/dist/planner-sql-checks-D3H-xOO1.mjs.map +0 -1
  190. package/dist/planner-target-details-CIj61DUj.d.mts.map +0 -1
  191. package/dist/postgres-contract-serializer-YJvjKrmo.mjs.map +0 -1
  192. package/dist/postgres-enum-type-CNhPTDhy.d.mts.map +0 -1
  193. package/dist/postgres-enum-type-DS-KLVRH.mjs.map +0 -1
  194. package/dist/postgres-migration-Fd4fQkBw.d.mts.map +0 -1
  195. package/dist/postgres-migration-uADmx0dW.mjs.map +0 -1
  196. package/dist/postgres-schema-Bm7vjlOv.mjs.map +0 -1
  197. package/dist/render-typescript-CPk7hhWH.mjs.map +0 -1
  198. package/dist/statement-builders-vImtdfmM.mjs +0 -131
  199. package/dist/statement-builders-vImtdfmM.mjs.map +0 -1
  200. package/dist/statement-builders.d.mts +0 -51
  201. package/dist/statement-builders.d.mts.map +0 -1
  202. package/dist/statement-builders.mjs +0 -2
  203. package/dist/tables-Dcb2q9zV.mjs +0 -516
  204. package/dist/tables-Dcb2q9zV.mjs.map +0 -1
  205. package/dist/types-D-XIpzHA.d.mts.map +0 -1
  206. package/src/core/migrations/statement-builders.ts +0 -183
  207. package/src/exports/statement-builders.ts +0 -8
@@ -1,16 +1,8 @@
1
- import type { ReferentialAction } from '@prisma-next/sql-contract/types';
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 `createTable` and
11
- * `addColumn`. Fully materialized: codec expansion and default rendering have
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 ColumnSpec, type Op, renderColumnDefinition, step, targetDetails } from './shared';
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: Map<string, CodecControlHooks>,
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: Map<string, CodecControlHooks>,
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: Map<string, CodecControlHooks>,
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: column.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: Map<string, CodecControlHooks>,
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(calls: readonly OpFactoryCall[], meta: MigrationMeta, spaceId: string) {
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
- type StorageTable,
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]?.tables[tableName] as StorageTable | undefined;
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 (!ns || !('enum' in ns) || ns.enum == null) return false;
138
- return (ns.enum as Record<string, PostgresEnumStorageEntry>)[typeName] !== undefined;
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].enum`. Returns the
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
- if (!ns || !('enum' in ns) || ns.enum == null) return undefined;
180
- return (ns.enum as Record<string, PostgresEnumStorageEntry>)[typeName];
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.tables)) {
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 (!('enum' in ns) || ns.enum == null) continue;
643
- const nsEnums = ns.enum as Record<string, unknown>;
644
- for (const [name, instance] of Object.entries(nsEnums).sort(([a], [b]) => a.localeCompare(b))) {
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
  ];
@@ -8,7 +8,8 @@ export type OperationClass =
8
8
  | 'primaryKey'
9
9
  | 'unique'
10
10
  | 'index'
11
- | 'foreignKey';
11
+ | 'foreignKey'
12
+ | 'checkConstraint';
12
13
 
13
14
  export interface PostgresPlanTargetDetails {
14
15
  readonly schema: string;