@prisma-next/target-postgres 0.13.0-dev.3 → 0.13.0-dev.30

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 (175) hide show
  1. package/dist/{codec-ids-CTikp1if.mjs → codec-ids-BvytN2P8.mjs} +3 -3
  2. package/dist/codec-ids-BvytN2P8.mjs.map +1 -0
  3. package/dist/{codec-ids-B1vOchLE.d.mts → codec-ids-CnXu9Qy3.d.mts} +3 -3
  4. package/dist/codec-ids-CnXu9Qy3.d.mts.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-CnFiNML4.d.mts → codec-types-DHCkwPKE.d.mts} +3 -3
  8. package/dist/{codec-types-CnFiNML4.d.mts.map → codec-types-DHCkwPKE.d.mts.map} +1 -1
  9. package/dist/codec-types.d.mts +1 -1
  10. package/dist/{codecs-CBpEv4s5.d.mts → codecs--0A5_4Bq.d.mts} +26 -23
  11. package/dist/codecs--0A5_4Bq.d.mts.map +1 -0
  12. package/dist/codecs.d.mts +2 -2
  13. package/dist/codecs.mjs +28 -35
  14. package/dist/codecs.mjs.map +1 -1
  15. package/dist/contract-free.d.mts +17 -2
  16. package/dist/contract-free.d.mts.map +1 -1
  17. package/dist/contract-free.mjs +3 -3
  18. package/dist/control.d.mts.map +1 -1
  19. package/dist/control.mjs +21 -27
  20. package/dist/control.mjs.map +1 -1
  21. package/dist/{data-transform-D25tLeYU.mjs → data-transform-BOWpliq8.mjs} +9 -17
  22. package/dist/data-transform-BOWpliq8.mjs.map +1 -0
  23. package/dist/{data-transform-DGOqcLrf.d.mts → data-transform-DDgWdB5o.d.mts} +2 -2
  24. package/dist/data-transform-DDgWdB5o.d.mts.map +1 -0
  25. package/dist/data-transform.d.mts +1 -1
  26. package/dist/data-transform.mjs +1 -1
  27. package/dist/{ddl-77SyXgFt.mjs → ddl-DY2R_Yqz.mjs} +18 -3
  28. package/dist/ddl-DY2R_Yqz.mjs.map +1 -0
  29. package/dist/ddl.d.mts +2 -2
  30. package/dist/ddl.mjs +2 -2
  31. package/dist/{descriptor-meta-DKmj-IMN.mjs → descriptor-meta-BKma_hQ5.mjs} +2 -2
  32. package/dist/{descriptor-meta-DKmj-IMN.mjs.map → descriptor-meta-BKma_hQ5.mjs.map} +1 -1
  33. package/dist/descriptor-meta-runtime-e5f2tscJ.mjs +131 -0
  34. package/dist/descriptor-meta-runtime-e5f2tscJ.mjs.map +1 -0
  35. package/dist/{issue-planner-Br0pt1Ea.mjs → issue-planner-DsjB7xDj.mjs} +48 -252
  36. package/dist/issue-planner-DsjB7xDj.mjs.map +1 -0
  37. package/dist/issue-planner.d.mts +8 -11
  38. package/dist/issue-planner.d.mts.map +1 -1
  39. package/dist/issue-planner.mjs +1 -1
  40. package/dist/migration.d.mts +4 -15
  41. package/dist/migration.d.mts.map +1 -1
  42. package/dist/migration.mjs +4 -4
  43. package/dist/{nodes-DZk2JZG3.mjs → nodes-Bbhs2rwj.mjs} +31 -2
  44. package/dist/nodes-Bbhs2rwj.mjs.map +1 -0
  45. package/dist/{nodes-779hmCfL.d.mts → nodes-pLeLgdis.d.mts} +30 -3
  46. package/dist/nodes-pLeLgdis.d.mts.map +1 -0
  47. package/dist/{op-factory-call-DMA86_2D.d.mts → op-factory-call-CdtMyrlU.d.mts} +12 -56
  48. package/dist/op-factory-call-CdtMyrlU.d.mts.map +1 -0
  49. package/dist/{op-factory-call-D2aAUhmS.mjs → op-factory-call-CjR846f7.mjs} +70 -198
  50. package/dist/op-factory-call-CjR846f7.mjs.map +1 -0
  51. package/dist/op-factory-call.d.mts +2 -2
  52. package/dist/op-factory-call.mjs +2 -2
  53. package/dist/pack.d.mts +36 -15
  54. package/dist/pack.d.mts.map +1 -1
  55. package/dist/pack.mjs +1 -1
  56. package/dist/{planner-CAYPJObw.mjs → planner-_FOL4I21.mjs} +25 -45
  57. package/dist/planner-_FOL4I21.mjs.map +1 -0
  58. package/dist/{planner-ddl-builders-Cw2n2llW.mjs → planner-ddl-builders-B2wOwLqI.mjs} +2 -2
  59. package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +1 -0
  60. package/dist/planner-ddl-builders.d.mts +4 -4
  61. package/dist/planner-ddl-builders.d.mts.map +1 -1
  62. package/dist/planner-ddl-builders.mjs +1 -1
  63. package/dist/{planner-identity-values-BIpa5p2I.mjs → planner-identity-values-CJPha2Sz.mjs} +3 -9
  64. package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -0
  65. package/dist/planner-identity-values.d.mts +1 -1
  66. package/dist/planner-identity-values.d.mts.map +1 -1
  67. package/dist/planner-identity-values.mjs +1 -1
  68. package/dist/{planner-produced-postgres-migration-NSEhWL0L.mjs → planner-produced-postgres-migration-BmCpyWLJ.mjs} +6 -4
  69. package/dist/planner-produced-postgres-migration-BmCpyWLJ.mjs.map +1 -0
  70. package/dist/{planner-produced-postgres-migration-B4EDvLdz.d.mts → planner-produced-postgres-migration-wLhnJMMA.d.mts} +5 -6
  71. package/dist/planner-produced-postgres-migration-wLhnJMMA.d.mts.map +1 -0
  72. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  73. package/dist/planner-produced-postgres-migration.mjs +1 -1
  74. package/dist/{planner-sql-checks-DAdhnI2c.mjs → planner-sql-checks-CJJtPfDH.mjs} +3 -3
  75. package/dist/planner-sql-checks-CJJtPfDH.mjs.map +1 -0
  76. package/dist/planner-sql-checks.d.mts +2 -2
  77. package/dist/planner-sql-checks.d.mts.map +1 -1
  78. package/dist/planner-sql-checks.mjs +1 -1
  79. package/dist/{planner-type-resolution-836DExFN.mjs → planner-type-resolution-Bt2f_q-F.mjs} +1 -6
  80. package/dist/planner-type-resolution-Bt2f_q-F.mjs.map +1 -0
  81. package/dist/planner.d.mts +4 -4
  82. package/dist/planner.d.mts.map +1 -1
  83. package/dist/planner.mjs +1 -1
  84. package/dist/{postgres-contract-serializer-DYTyXjPf.mjs → postgres-contract-serializer-CyAe8ZFv.mjs} +27 -37
  85. package/dist/postgres-contract-serializer-CyAe8ZFv.mjs.map +1 -0
  86. package/dist/{postgres-migration-DZ_gLUOW.d.mts → postgres-migration-DLXL0GBf.d.mts} +10 -5
  87. package/dist/postgres-migration-DLXL0GBf.d.mts.map +1 -0
  88. package/dist/{postgres-migration-COore9Mz.mjs → postgres-migration-dG-J0aI8.mjs} +7 -3
  89. package/dist/postgres-migration-dG-J0aI8.mjs.map +1 -0
  90. package/dist/{postgres-schema-BuxCxbvB.mjs → postgres-schema-CTKYiTHu.mjs} +30 -13
  91. package/dist/postgres-schema-CTKYiTHu.mjs.map +1 -0
  92. package/dist/{render-ops-BpjstrKQ.mjs → render-ops-BREh1kHe.mjs} +10 -5
  93. package/dist/render-ops-BREh1kHe.mjs.map +1 -0
  94. package/dist/render-ops.d.mts +2 -2
  95. package/dist/render-ops.d.mts.map +1 -1
  96. package/dist/render-ops.mjs +1 -1
  97. package/dist/runtime.d.mts.map +1 -1
  98. package/dist/runtime.mjs +2 -2
  99. package/dist/{shared-DarONYBZ.d.mts → shared-jcsbXxiW.d.mts} +2 -20
  100. package/dist/shared-jcsbXxiW.d.mts.map +1 -0
  101. package/dist/types.d.mts +8 -13
  102. package/dist/types.d.mts.map +1 -1
  103. package/dist/types.mjs +2 -3
  104. package/package.json +17 -18
  105. package/src/contract-free/ddl.ts +28 -1
  106. package/src/core/authoring.ts +43 -44
  107. package/src/core/codec-helpers.ts +0 -17
  108. package/src/core/codec-ids.ts +1 -1
  109. package/src/core/codec-type-map.ts +2 -2
  110. package/src/core/codecs.ts +43 -48
  111. package/src/core/ddl/nodes.ts +59 -1
  112. package/src/core/migrations/control-policy.ts +17 -47
  113. package/src/core/migrations/issue-planner.ts +34 -70
  114. package/src/core/migrations/op-factory-call.ts +89 -142
  115. package/src/core/migrations/operations/data-transform.ts +15 -18
  116. package/src/core/migrations/planner-ddl-builders.ts +3 -4
  117. package/src/core/migrations/planner-identity-values.ts +4 -28
  118. package/src/core/migrations/planner-produced-postgres-migration.ts +15 -7
  119. package/src/core/migrations/planner-recipes.ts +2 -6
  120. package/src/core/migrations/planner-sql-checks.ts +2 -6
  121. package/src/core/migrations/planner-strategies.ts +51 -376
  122. package/src/core/migrations/planner-type-resolution.ts +2 -20
  123. package/src/core/migrations/planner.ts +6 -6
  124. package/src/core/migrations/postgres-migration.ts +19 -4
  125. package/src/core/migrations/render-ops.ts +26 -13
  126. package/src/core/migrations/runner.ts +26 -20
  127. package/src/core/postgres-contract-serializer.ts +32 -54
  128. package/src/core/postgres-enum-type-schema.ts +17 -0
  129. package/src/core/postgres-schema.ts +56 -34
  130. package/src/exports/codecs.ts +2 -2
  131. package/src/exports/contract-free.ts +1 -1
  132. package/src/exports/control.ts +0 -22
  133. package/src/exports/ddl.ts +4 -0
  134. package/src/exports/migration.ts +0 -7
  135. package/src/exports/op-factory-call.ts +0 -4
  136. package/src/exports/types.ts +0 -1
  137. package/dist/codec-ids-B1vOchLE.d.mts.map +0 -1
  138. package/dist/codec-ids-CTikp1if.mjs.map +0 -1
  139. package/dist/codecs-CBpEv4s5.d.mts.map +0 -1
  140. package/dist/data-transform-D25tLeYU.mjs.map +0 -1
  141. package/dist/data-transform-DGOqcLrf.d.mts.map +0 -1
  142. package/dist/ddl-77SyXgFt.mjs.map +0 -1
  143. package/dist/descriptor-meta-runtime-My8_s4cs.mjs +0 -130
  144. package/dist/descriptor-meta-runtime-My8_s4cs.mjs.map +0 -1
  145. package/dist/enum-planning-BCyvlFHk.mjs +0 -0
  146. package/dist/enum-planning-BCyvlFHk.mjs.map +0 -1
  147. package/dist/enum-planning.d.mts +0 -86
  148. package/dist/enum-planning.d.mts.map +0 -1
  149. package/dist/enum-planning.mjs +0 -2
  150. package/dist/issue-planner-Br0pt1Ea.mjs.map +0 -1
  151. package/dist/nodes-779hmCfL.d.mts.map +0 -1
  152. package/dist/nodes-DZk2JZG3.mjs.map +0 -1
  153. package/dist/op-factory-call-D2aAUhmS.mjs.map +0 -1
  154. package/dist/op-factory-call-DMA86_2D.d.mts.map +0 -1
  155. package/dist/planner-CAYPJObw.mjs.map +0 -1
  156. package/dist/planner-ddl-builders-Cw2n2llW.mjs.map +0 -1
  157. package/dist/planner-identity-values-BIpa5p2I.mjs.map +0 -1
  158. package/dist/planner-produced-postgres-migration-B4EDvLdz.d.mts.map +0 -1
  159. package/dist/planner-produced-postgres-migration-NSEhWL0L.mjs.map +0 -1
  160. package/dist/planner-sql-checks-DAdhnI2c.mjs.map +0 -1
  161. package/dist/planner-type-resolution-836DExFN.mjs.map +0 -1
  162. package/dist/postgres-contract-serializer-DYTyXjPf.mjs.map +0 -1
  163. package/dist/postgres-enum-type-BVn63a89.d.mts +0 -72
  164. package/dist/postgres-enum-type-BVn63a89.d.mts.map +0 -1
  165. package/dist/postgres-enum-type-DPKqCBem.mjs +0 -62
  166. package/dist/postgres-enum-type-DPKqCBem.mjs.map +0 -1
  167. package/dist/postgres-migration-COore9Mz.mjs.map +0 -1
  168. package/dist/postgres-migration-DZ_gLUOW.d.mts.map +0 -1
  169. package/dist/postgres-schema-BuxCxbvB.mjs.map +0 -1
  170. package/dist/render-ops-BpjstrKQ.mjs.map +0 -1
  171. package/dist/shared-DarONYBZ.d.mts.map +0 -1
  172. package/src/core/migrations/enum-planning.ts +0 -213
  173. package/src/core/migrations/operations/enums.ts +0 -114
  174. package/src/core/postgres-enum-type.ts +0 -89
  175. package/src/exports/enum-planning.ts +0 -11
@@ -11,7 +11,7 @@
11
11
  * two journeys differ only in `policy.allowedOperationClasses`:
12
12
  *
13
13
  * - When `'data'` is in the policy, data-safe strategies (NOT NULL backfill,
14
- * nullability tightening, unsafe type changes, enum shrink/rebuild) emit
14
+ * nullability tightening, unsafe type changes) emit
15
15
  * `DataTransformCall` placeholders that the user fills in.
16
16
  * - When `'data'` is excluded, those strategies short-circuit so the
17
17
  * downstream walk-schema strategies (codec-hook type ops and temp-default
@@ -29,40 +29,29 @@ import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-comp
29
29
  import type { SchemaIssue } from '@prisma-next/framework-components/control';
30
30
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
31
31
  import {
32
- isPostgresEnumStorageEntry,
33
- type PostgresEnumStorageEntry,
34
32
  type SqlStorage,
35
33
  StorageTable,
36
34
  type StorageTypeInstance,
37
35
  } from '@prisma-next/sql-contract/types';
36
+ import type { CodecRef, DdlColumn } from '@prisma-next/sql-relational-core/ast';
37
+ import { col } from '@prisma-next/sql-relational-core/contract-free';
38
38
  import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
39
39
  import { blindCast } from '@prisma-next/utils/casts';
40
- import { PostgresEnumType } from '../postgres-enum-type';
40
+ import { ifDefined } from '@prisma-next/utils/defined';
41
+ import type { JsonValue } from '@prisma-next/utils/json';
41
42
  import { isPostgresSchema } from '../postgres-schema';
42
- import {
43
- determineEnumDiff,
44
- readExistingEnumValues,
45
- resolveDdlSchemaForNamespaceStorage,
46
- } from './enum-planning';
47
43
  import {
48
44
  AddCheckConstraintCall,
49
45
  AddColumnCall,
50
- AddEnumValuesCall,
51
46
  AlterColumnTypeCall,
52
- CreateEnumTypeCall,
53
47
  DataTransformCall,
54
48
  DropCheckConstraintCall,
55
- DropEnumTypeCall,
56
49
  type PostgresOpFactoryCall,
50
+ postgresDefaultToDdlColumnDefault,
57
51
  RawSqlCall,
58
- RenameTypeCall,
59
52
  SetNotNullCall,
60
53
  } from './op-factory-call';
61
- import {
62
- buildAddColumnSql,
63
- buildColumnDefaultSql,
64
- buildColumnTypeSql,
65
- } from './planner-ddl-builders';
54
+ import { buildAddColumnSql, buildColumnTypeSql } from './planner-ddl-builders';
66
55
  import { resolveIdentityValue } from './planner-identity-values';
67
56
  import {
68
57
  buildAddColumnOperationIdentity,
@@ -77,8 +66,7 @@ import {
77
66
  tableIsEmptyCheck,
78
67
  } from './planner-sql-checks';
79
68
  import { buildTargetDetails, type PostgresPlanTargetDetails } from './planner-target-details';
80
-
81
- const REBUILD_SUFFIX = '__prisma_next_new';
69
+ import { resolveColumnTypeMetadata } from './planner-type-resolution';
82
70
 
83
71
  /**
84
72
  * Look up a storage table by its explicit namespace coordinate. Returns
@@ -95,9 +83,9 @@ export function tableAt(
95
83
  namespaceId: string,
96
84
  tableName: string,
97
85
  ): StorageTable | undefined {
98
- // Namespace.tables is typed as Record<string, IRNode> at the interface level;
99
- // SQL family namespaces always hold StorageTable instances.
100
- return storage.namespaces[namespaceId]?.entries.table[tableName] as StorageTable | undefined;
86
+ const ns = storage.namespaces[namespaceId];
87
+ if (ns === undefined) return undefined;
88
+ return ns.entries.table?.[tableName];
101
89
  }
102
90
 
103
91
  /**
@@ -131,63 +119,6 @@ export function resolveDdlSchemaForNamespace(ctx: StrategyContext, namespaceId:
131
119
  return namespaceId;
132
120
  }
133
121
 
134
- /** Default Postgres enum landing namespace — where contract-level (`types:`)
135
- * enums are placed by the authoring builder when no explicit namespace is
136
- * given. Mirrors `POSTGRES_ENUM_NAMESPACE_ID` in the contract-ts builder. */
137
- const DEFAULT_ENUM_NAMESPACE_ID = 'public';
138
-
139
- function namespaceHasEnum(storage: SqlStorage, namespaceId: string, typeName: string): boolean {
140
- const ns = storage.namespaces[namespaceId];
141
- if (!isPostgresSchema(ns)) return false;
142
- return ns.entries.type[typeName] !== undefined;
143
- }
144
-
145
- /**
146
- * Resolves which namespace's enum a column's bare `typeRef` binds to.
147
- *
148
- * Columns carry a bare (non-namespace-qualified) `typeRef`; the enum it names
149
- * may live in a different namespace than the column's own (the authoring
150
- * builder places contract-level `types:` enums in the default `public`
151
- * namespace while a model's table may sit in the unbound namespace). The
152
- * binding rule: an enum declared in the column's *own* namespace shadows
153
- * everything; otherwise the column references the ambient enum — the sole
154
- * namespace that defines `typeName`, preferring the default `public`
155
- * namespace when several do. Returns `undefined` when no namespace defines it.
156
- */
157
- function resolveColumnEnumNamespace(
158
- storage: SqlStorage,
159
- columnNamespaceId: string,
160
- typeName: string,
161
- ): string | undefined {
162
- if (namespaceHasEnum(storage, columnNamespaceId, typeName)) return columnNamespaceId;
163
- const owners = Object.keys(storage.namespaces).filter((nsId) =>
164
- namespaceHasEnum(storage, nsId, typeName),
165
- );
166
- if (owners.length === 1) return owners[0];
167
- if (owners.includes(DEFAULT_ENUM_NAMESPACE_ID)) return DEFAULT_ENUM_NAMESPACE_ID;
168
- return owners[0];
169
- }
170
-
171
- /**
172
- * Finds a type entry by explicit namespace coordinate. Namespace types (e.g.
173
- * Postgres enums) live under `storage.namespaces[nsId].entries.type`. Returns the
174
- * entry from the named namespace only — never scans other namespaces, so two
175
- * namespaces that hold an enum with the same name resolve independently.
176
- */
177
- function locateNamespaceType(
178
- storage: SqlStorage,
179
- namespaceId: string,
180
- typeName: string,
181
- ): PostgresEnumStorageEntry | undefined {
182
- const ns = storage.namespaces[namespaceId];
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);
189
- }
190
-
191
122
  // ============================================================================
192
123
  // Strategy types
193
124
  // ============================================================================
@@ -206,7 +137,7 @@ export interface StrategyContext {
206
137
  readonly fromContract: Contract<SqlStorage> | null;
207
138
  readonly schemaName: string;
208
139
  readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;
209
- readonly storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
140
+ readonly storageTypes: Readonly<Record<string, StorageTypeInstance>>;
210
141
  readonly schema: SqlSchemaIR;
211
142
  readonly policy: MigrationOperationPolicy;
212
143
  readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
@@ -227,12 +158,9 @@ export type CallMigrationStrategy = (
227
158
  /**
228
159
  * `true` for strategies that emit cohesive sequential recipes whose
229
160
  * calls must stay contiguous and in the returned order — e.g.
230
- * `nativeEnumPlanCallStrategy` (createEnumTypealterColumnType
231
- * dropEnumType renameType, optionally prefixed by a
232
- * `DataTransformCall` placeholder), `notNullBackfillCallStrategy`
233
- * (addColumn → dataTransform → setNotNull). Defaults to `false`,
234
- * which lets `planIssues` hoist individual calls into their DDL
235
- * sequencing bucket.
161
+ * `notNullBackfillCallStrategy` (addColumndataTransform setNotNull).
162
+ * Defaults to `false`, which lets `planIssues` hoist individual calls
163
+ * into their DDL sequencing bucket.
236
164
  */
237
165
  recipe?: boolean;
238
166
  }
@@ -244,21 +172,34 @@ function buildColumnSpec(
244
172
  column: string,
245
173
  ctx: StrategyContext,
246
174
  overrides?: { nullable?: boolean },
247
- ) {
248
- const col = tableAt(ctx.toContract.storage, namespaceId, table)?.columns[column];
249
- if (!col) throw new Error(`Column "${table}"."${column}" not found in destination contract`);
175
+ ): DdlColumn {
176
+ const storageCol = tableAt(ctx.toContract.storage, namespaceId, table)?.columns[column];
177
+ if (!storageCol)
178
+ throw new Error(`Column "${table}"."${column}" not found in destination contract`);
250
179
  const mutableHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
251
- const mutableTypes = ctx.storageTypes as Record<
252
- string,
253
- StorageTypeInstance | PostgresEnumStorageEntry
254
- >;
255
- return {
256
- name: column,
257
- typeSql: buildColumnTypeSql(col, mutableHooks, mutableTypes),
258
- defaultSql: buildColumnDefaultSql(col.default, col),
259
- columnDefault: col.default,
260
- nullable: overrides?.nullable ?? col.nullable,
261
- };
180
+ const mutableTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
181
+ const typeSql = buildColumnTypeSql(storageCol, mutableHooks, mutableTypes);
182
+ const ddlDefault = postgresDefaultToDdlColumnDefault(storageCol.default);
183
+ const resolved = resolveColumnTypeMetadata(storageCol, mutableTypes);
184
+ const typeParams =
185
+ resolved.typeParams === undefined
186
+ ? undefined
187
+ : blindCast<
188
+ JsonValue,
189
+ 'resolved.typeParams is JsonValue-shaped storage metadata; the narrowed value lands in CodecRef.typeParams which is JsonValue'
190
+ >(resolved.typeParams);
191
+ const codecRef: CodecRef | undefined = resolved.codecId
192
+ ? {
193
+ codecId: resolved.codecId,
194
+ ...ifDefined('typeParams', typeParams),
195
+ }
196
+ : undefined;
197
+ const nullable = overrides?.nullable ?? storageCol.nullable;
198
+ return col(column, typeSql, {
199
+ ...(!nullable ? { notNull: true } : {}),
200
+ ...ifDefined('default', ddlDefault),
201
+ ...ifDefined('codecRef', codecRef),
202
+ });
262
203
  }
263
204
 
264
205
  function buildAlterTypeOptions(
@@ -271,10 +212,7 @@ function buildAlterTypeOptions(
271
212
  const col = tableAt(ctx.toContract.storage, namespaceId, table)?.columns[column];
272
213
  if (!col) throw new Error(`Column "${table}"."${column}" not found in destination contract`);
273
214
  const mutableHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
274
- const mutableTypes = ctx.storageTypes as Record<
275
- string,
276
- StorageTypeInstance | PostgresEnumStorageEntry
277
- >;
215
+ const mutableTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
278
216
  const qualifiedTargetType = buildColumnTypeSql(col, mutableHooks, mutableTypes, false);
279
217
  const formatTypeExpected = buildExpectedFormatType(col, mutableHooks, mutableTypes);
280
218
  return {
@@ -417,249 +355,6 @@ export const nullableTighteningCallStrategy: CallMigrationStrategy = (issues, ct
417
355
  };
418
356
  };
419
357
 
420
- function enumRebuildCallRecipe(
421
- namespaceId: string,
422
- typeName: string,
423
- ctx: StrategyContext,
424
- ): readonly PostgresOpFactoryCall[] {
425
- const toType = locateNamespaceType(ctx.toContract.storage, namespaceId, typeName);
426
- if (!toType) return [];
427
- const isEnum = isPostgresEnumStorageEntry(toType);
428
- const nativeType = toType.nativeType;
429
- const desiredValues: readonly string[] = isEnum
430
- ? toType.values
431
- : (((toType as StorageTypeInstance).typeParams['values'] ?? []) as readonly string[]);
432
- const tempName = `${nativeType}${REBUILD_SUFFIX}`;
433
- // Type DDL targets the enum's real schema — the unbound coordinate resolves
434
- // to the introspected `current_schema()`, never the `__unbound__` sentinel.
435
- const ddlSchema = resolveDdlSchemaForNamespaceStorage(
436
- ctx.toContract.storage,
437
- namespaceId,
438
- ctx.schema,
439
- );
440
-
441
- // Migrate every column whose `typeRef` binds to *this* enum. The column's
442
- // bare `typeRef` resolves to an enum namespace (own-namespace shadows;
443
- // otherwise the ambient/default `public` enum), so a column in the unbound
444
- // namespace correctly binds to a `public`-namespace enum, while two
445
- // same-named enums in distinct namespaces keep their columns disjoint.
446
- const columnRefs: { namespaceId: string; table: string; column: string }[] = [];
447
- for (const [nsId, ns] of Object.entries(ctx.toContract.storage.namespaces)) {
448
- for (const [tableName, tableNode] of Object.entries(ns.entries.table)) {
449
- const table = tableNode as StorageTable;
450
- for (const [columnName, column] of Object.entries(table.columns)) {
451
- if (
452
- column.typeRef === typeName &&
453
- resolveColumnEnumNamespace(ctx.toContract.storage, nsId, typeName) === namespaceId
454
- ) {
455
- columnRefs.push({ namespaceId: nsId, table: tableName, column: columnName });
456
- }
457
- }
458
- }
459
- }
460
-
461
- return [
462
- new CreateEnumTypeCall(ddlSchema, tempName, desiredValues),
463
- ...columnRefs.map((ref) => {
464
- const using = `${ref.column}::text::${tempName}`;
465
- return new AlterColumnTypeCall(
466
- resolveDdlSchemaForNamespace(ctx, ref.namespaceId),
467
- ref.table,
468
- ref.column,
469
- {
470
- qualifiedTargetType: tempName,
471
- formatTypeExpected: tempName,
472
- rawTargetTypeForLabel: tempName,
473
- using,
474
- },
475
- );
476
- }),
477
- new DropEnumTypeCall(ddlSchema, nativeType),
478
- new RenameTypeCall(ddlSchema, tempName, nativeType),
479
- ];
480
- }
481
-
482
- // ============================================================================
483
- // Native enum planner strategy
484
- // ============================================================================
485
-
486
- /**
487
- * Single planner strategy for `PostgresEnumType` instances. Walks
488
- * `toContract.storage.types` directly (no codec-hook dispatch) and
489
- * resolves existing values via `readExistingEnumValues`, the same
490
- * Postgres bridging adapter the verifier uses.
491
- *
492
- * Per-enum dispatch:
493
- *
494
- * - No existing type → `CreateEnumTypeCall` with the contract's desired
495
- * values.
496
- * - Diff is `unchanged` → no calls emitted (consumes the matching
497
- * `enum_values_changed` issue if present).
498
- * - Diff is `add_values` → `AddEnumValuesCall` with the new labels.
499
- * - Diff is `rebuild` → the create-temp / migrate-columns /
500
- * drop-original / rename rebuild recipe. When
501
- * `policy.allowedOperationClasses` includes `'data'` and the rebuild
502
- * removes labels (`removedValues.length > 0`), prepend a
503
- * `DataTransformCall` placeholder so the user can author the value
504
- * remap before the destructive recipe runs. Without `'data'` in the
505
- * policy (`db update` / `db init`), the rebuild's PG `USING ::text`
506
- * cast surfaces any value-removal data loss as a runtime error rather
507
- * than silent loss.
508
- *
509
- * Returns `recipe: true` only when a rebuild recipe was emitted (its
510
- * `createEnumType(temp) → alterColumnType → dropEnumType(orig) →
511
- * renameType` sequence mixes `dep`-class and `alter`-class calls that
512
- * would mis-order if the planner hoisted them into its DDL sequencing
513
- * buckets). For the create-only and add-values paths the strategy
514
- * returns `recipe: false` so the planner hoists `CreateEnumTypeCall`
515
- * into the `dep` bucket — i.e. `CREATE TYPE` runs before any
516
- * `CreateTableCall` that references the new enum.
517
- */
518
- /**
519
- * Separator character for compound enum map keys (`namespaceId\u0000typeName`).
520
- * NUL (`\u0000`) is invalid in both Postgres identifiers and TypeScript symbol
521
- * names so it cannot appear in either component — unambiguous separator.
522
- */
523
- const COMPOUND_KEY_SEP = '\u0000';
524
-
525
- /** Builds the compound map key for a namespace-qualified enum entry. */
526
- function enumCompoundKey(namespaceId: string, typeName: string): string {
527
- return `${namespaceId}${COMPOUND_KEY_SEP}${typeName}`;
528
- }
529
-
530
- export const nativeEnumPlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
531
- const enumTypes = collectPostgresEnumTypes(ctx.toContract.storage);
532
- if (enumTypes.size === 0) return { kind: 'no_match' };
533
-
534
- const dataAllowed = ctx.policy.allowedOperationClasses.includes('data');
535
-
536
- const calls: PostgresOpFactoryCall[] = [];
537
- const handledKeys = new Set<string>();
538
- const introducedKeys = new Set<string>();
539
- const rebuiltKeys = new Set<string>();
540
- let emittedRebuildRecipe = false;
541
-
542
- for (const [key, enumType] of enumTypes) {
543
- const sepIdx = key.indexOf(COMPOUND_KEY_SEP);
544
- const enumNamespaceId = key.slice(0, sepIdx);
545
- const typeName = key.slice(sepIdx + 1);
546
-
547
- const desired = enumType.values;
548
- // The enum's live schema: for the unbound coordinate this resolves to the
549
- // introspected `current_schema()` (e.g. `public`), never the `__unbound__`
550
- // DDL-emit sentinel — so both the existing-values lookup key and the
551
- // emitted `CREATE TYPE` / `ALTER TYPE` target the real schema the type
552
- // lives in. Named namespaces resolve to their own DDL schema.
553
- const ddlSchema = resolveDdlSchemaForNamespaceStorage(
554
- ctx.toContract.storage,
555
- enumNamespaceId,
556
- ctx.schema,
557
- );
558
- const existing = readExistingEnumValues(ctx.schema, ddlSchema, enumType.nativeType);
559
- if (!existing) {
560
- calls.push(new CreateEnumTypeCall(ddlSchema, typeName, desired, enumType.nativeType));
561
- handledKeys.add(key);
562
- introducedKeys.add(key);
563
- continue;
564
- }
565
- const diff = determineEnumDiff(existing, desired);
566
- if (diff.kind === 'unchanged') {
567
- handledKeys.add(key);
568
- continue;
569
- }
570
- if (diff.kind === 'add_values') {
571
- calls.push(new AddEnumValuesCall(ddlSchema, typeName, enumType.nativeType, diff.values));
572
- handledKeys.add(key);
573
- continue;
574
- }
575
- if (dataAllowed && diff.removedValues.length > 0) {
576
- calls.push(
577
- new DataTransformCall(
578
- `migrate-${typeName}-values`,
579
- `migrate-${typeName}-values:check`,
580
- `migrate-${typeName}-values:run`,
581
- ),
582
- );
583
- }
584
- calls.push(...enumRebuildCallRecipe(enumNamespaceId, typeName, ctx));
585
- emittedRebuildRecipe = true;
586
- handledKeys.add(key);
587
- rebuiltKeys.add(key);
588
- }
589
-
590
- // The strategy emits a single `recipe` flag for the entire pass,
591
- // which routes every emitted call to either the contiguous recipe
592
- // slot (rebuild path) or the `dep` bucket (introduce / add-values
593
- // path). A plan that needs both shapes simultaneously cannot be
594
- // expressed today — the introduced `CreateEnumTypeCall` would land
595
- // in the recipe slot and any `CreateTableCall` referencing the new
596
- // enum would fail at runtime with a confusing `type "X" does not
597
- // exist` error. Surface the unrepresentable case here as a
598
- // planner-time error so the failure mode is loud, not silent.
599
- if (introducedKeys.size > 0 && rebuiltKeys.size > 0) {
600
- const introducedDisplay = [...introducedKeys]
601
- .sort()
602
- .map((k) => k.replace(COMPOUND_KEY_SEP, '.'))
603
- .join(', ');
604
- const rebuiltDisplay = [...rebuiltKeys]
605
- .sort()
606
- .map((k) => k.replace(COMPOUND_KEY_SEP, '.'))
607
- .join(', ');
608
- throw new Error(
609
- `nativeEnumPlanCallStrategy: cannot emit both a brand-new enum and a rebuild on a different enum in the same plan; the single recipe flag cannot route them to different buckets. Introduced: [${introducedDisplay}]; rebuilt: [${rebuiltDisplay}]. Split the strategy or grow the \`match\` return type before this case lands.`,
610
- );
611
- }
612
-
613
- const remaining = issues.filter(
614
- (issue) =>
615
- !(
616
- (issue.kind === 'type_missing' || issue.kind === 'enum_values_changed') &&
617
- issue.typeName &&
618
- handledKeys.has(enumCompoundKey(resolveNamespaceIdForIssue(issue), issue.typeName))
619
- ),
620
- );
621
-
622
- if (calls.length === 0 && remaining.length === issues.length) {
623
- return { kind: 'no_match' };
624
- }
625
- // `recipe: true` is required for the rebuild path — its
626
- // `createEnumType(temp) → alterColumnType → dropEnumType(orig) →
627
- // renameType` mixes `dep`-class and `alter`-class calls that would
628
- // mis-order if the planner hoisted them into its DDL sequencing
629
- // buckets. For the type_missing / add_values paths we want the
630
- // opposite: hoisted into the `dep` bucket so a brand-new
631
- // `CreateEnumTypeCall` runs *before* the `CreateTableCall` that
632
- // references it. The two cases never co-occur in the same plan
633
- // (introducing a new enum type and rebuilding an existing one in
634
- // one shot would require both buckets — a shape today's interface
635
- // does not surface; if that combination ever needs to land we'd
636
- // split this strategy or grow the `match` return type).
637
- return { kind: 'match', issues: remaining, calls, recipe: emittedRebuildRecipe };
638
- };
639
-
640
- /**
641
- * Collects every `PostgresEnumType` instance across all declared namespaces,
642
- * returning a compound-keyed map (`${namespaceId}\u0000${typeName}`). Two
643
- * namespaces that declare an enum with the same name produce two distinct
644
- * entries — no name collision, no last-write-wins.
645
- *
646
- * Entries within each namespace are sorted by name for deterministic ordering.
647
- */
648
- function collectPostgresEnumTypes(storage: SqlStorage): ReadonlyMap<string, PostgresEnumType> {
649
- const result = new Map<string, PostgresEnumType>();
650
- for (const [nsId, ns] of Object.entries(storage.namespaces)) {
651
- if (!isPostgresSchema(ns)) continue;
652
- for (const [name, instance] of Object.entries(ns.entries.type).sort(([a], [b]) =>
653
- a.localeCompare(b),
654
- )) {
655
- if (instance instanceof PostgresEnumType) {
656
- result.set(enumCompoundKey(nsId, name), instance);
657
- }
658
- }
659
- }
660
- return result;
661
- }
662
-
663
358
  /**
664
359
  * Collects every check constraint from a table in the contract storage.
665
360
  * Returns an empty array when the table has no checks or the table is absent.
@@ -670,7 +365,7 @@ function collectContractChecks(
670
365
  tableName: string,
671
366
  ): ReadonlyArray<{ name: string; column: string; permittedValues: readonly string[] }> {
672
367
  const ns = storage.namespaces[namespaceId];
673
- const tableRaw = ns?.entries.table[tableName];
368
+ const tableRaw = ns !== undefined ? ns.entries.table?.[tableName] : undefined;
674
369
  if (!(tableRaw instanceof StorageTable)) return [];
675
370
  const checks = tableRaw.checks;
676
371
  if (!checks || checks.length === 0) return [];
@@ -708,15 +403,13 @@ function checkValueSetsEqual(a: readonly string[], b: readonly string[]): boolea
708
403
  * be altered in place).
709
404
  *
710
405
  * Consumes `check_missing`, `check_removed`, and `check_mismatch` issues.
711
- * Does not touch the native enum path (`nativeEnumPlanCallStrategy` is
712
- * unchanged).
713
406
  */
714
407
  export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
715
408
  const calls: PostgresOpFactoryCall[] = [];
716
409
  const handledIssueKeys = new Set<string>();
717
410
 
718
411
  for (const [namespaceId, ns] of Object.entries(ctx.toContract.storage.namespaces)) {
719
- for (const tableName of Object.keys(ns.entries.table)) {
412
+ for (const tableName of Object.keys(ns.entries.table ?? {})) {
720
413
  const contractChecks = collectContractChecks(ctx.toContract.storage, namespaceId, tableName);
721
414
  if (contractChecks.length === 0) continue;
722
415
 
@@ -788,10 +481,9 @@ export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, c
788
481
  };
789
482
 
790
483
  /**
791
- * Dispatches non-enum codec-typed storage types through their codec's
484
+ * Dispatches codec-typed storage types through their codec's
792
485
  * `planTypeOperations` hook (the authoritative source for codec-driven DDL
793
- * such as custom type creation). Enum dispatch lives in
794
- * `nativeEnumPlanCallStrategy` and no longer relies on codec hooks.
486
+ * such as custom type creation).
795
487
  */
796
488
  export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
797
489
  const storageTypes = ctx.toContract.storage.types ?? {};
@@ -803,10 +495,6 @@ export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx)
803
495
  for (const [typeName, typeInstance] of Object.entries(storageTypes).sort(([a], [b]) =>
804
496
  a.localeCompare(b),
805
497
  )) {
806
- // Enums walk natively in `nativeEnumPlanCallStrategy`; codec-hook
807
- // dispatch here is reserved for genuinely codec-typed entries
808
- // (decimal, varchar, pgvector, …).
809
- if (isPostgresEnumStorageEntry(typeInstance)) continue;
810
498
  const codecInstance = typeInstance as StorageTypeInstance;
811
499
  const hook = ctx.codecHooks.get(codecInstance.codecId);
812
500
  if (!hook?.planTypeOperations) continue;
@@ -874,10 +562,7 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
874
562
  const schemaLookups = buildSchemaLookupMap(ctx.schema);
875
563
 
876
564
  const mutableCodecHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
877
- const mutableStorageTypes = ctx.storageTypes as Record<
878
- string,
879
- StorageTypeInstance | PostgresEnumType
880
- >;
565
+ const mutableStorageTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
881
566
 
882
567
  for (const issue of issues) {
883
568
  if (issue.kind !== 'missing_column' || !issue.table || !issue.column) continue;
@@ -1033,8 +718,7 @@ function canUseSharedTemporaryDefaultStrategy(options: {
1033
718
  *
1034
719
  * - When `'data'` is allowed (`migration plan`), the data-safe strategies
1035
720
  * (`notNullBackfillCallStrategy`, `typeChangeCallStrategy`,
1036
- * `nullableTighteningCallStrategy`) and the enum walk
1037
- * (`nativeEnumPlanCallStrategy`) consume their matching issues and emit
721
+ * `nullableTighteningCallStrategy`) consume their matching issues and emit
1038
722
  * `DataTransformCall` placeholders or recipe ops.
1039
723
  *
1040
724
  * - When `'data'` is not allowed (`db update` / `db init`), the
@@ -1042,23 +726,14 @@ function canUseSharedTemporaryDefaultStrategy(options: {
1042
726
  * the issue for the downstream walk-schema strategies
1043
727
  * (`storageTypePlanCallStrategy`, `notNullAddColumnCallStrategy`) or the
1044
728
  * `mapIssueToCall` default to handle with direct DDL.
1045
- * `nativeEnumPlanCallStrategy` runs in both modes; under `db update` /
1046
- * `db init` it emits the rebuild recipe without the data-transform
1047
- * placeholder so value-removal data loss surfaces as a runtime cast
1048
- * error rather than silent loss.
1049
729
  *
1050
- * Enum dispatch is unified into a single strategy: the
1051
- * `nativeEnumPlanCallStrategy` decides per-emission whether to emit a
1052
- * rebuild recipe (`recipe: true`, contiguous slot) or hoist the call
1053
- * into the `dep` bucket (`recipe: false`, so a brand-new
1054
- * `CreateEnumTypeCall` runs before any `CreateTableCall` referencing
1055
- * it). Codec-typed entries continue through `storageTypePlanCallStrategy`.
730
+ * Codec-typed storage type entries are dispatched through
731
+ * `storageTypePlanCallStrategy`.
1056
732
  */
1057
733
  export const postgresPlannerStrategies: readonly CallMigrationStrategy[] = [
1058
734
  notNullBackfillCallStrategy,
1059
735
  typeChangeCallStrategy,
1060
736
  nullableTighteningCallStrategy,
1061
- nativeEnumPlanCallStrategy,
1062
737
  checkConstraintPlanCallStrategy,
1063
738
  storageTypePlanCallStrategy,
1064
739
  notNullAddColumnCallStrategy,
@@ -1,9 +1,4 @@
1
- import {
2
- isPostgresEnumStorageEntry,
3
- type PostgresEnumStorageEntry,
4
- type StorageColumn,
5
- type StorageTypeInstance,
6
- } from '@prisma-next/sql-contract/types';
1
+ import type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';
7
2
 
8
3
  export type ResolvedColumnTypeMetadata = Pick<
9
4
  StorageColumn,
@@ -12,7 +7,7 @@ export type ResolvedColumnTypeMetadata = Pick<
12
7
 
13
8
  export function resolveColumnTypeMetadata(
14
9
  column: StorageColumn,
15
- storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>,
10
+ storageTypes: Readonly<Record<string, StorageTypeInstance>>,
16
11
  ): ResolvedColumnTypeMetadata {
17
12
  if (!column.typeRef) {
18
13
  return column;
@@ -23,19 +18,6 @@ export function resolveColumnTypeMetadata(
23
18
  return column;
24
19
  }
25
20
 
26
- if (isPostgresEnumStorageEntry(referencedType)) {
27
- // Enum types are referenced by name (`quoteIdentifier(nativeType)`),
28
- // not via parameterised codec expansion. The structural shape
29
- // carries `codecId` as an enumerable property (mirroring the
30
- // codec-typed view); `typeParams` is intentionally omitted here so
31
- // `expandParameterizedTypeSql` does not try to look up a
32
- // (deliberately absent) `expandNativeType` hook for `pg/enum@*`.
33
- return {
34
- codecId: referencedType.codecId,
35
- nativeType: referencedType.nativeType,
36
- };
37
- }
38
-
39
21
  return {
40
22
  codecId: referencedType.codecId,
41
23
  nativeType: referencedType.nativeType,
@@ -12,7 +12,7 @@ import {
12
12
  planFieldEventOperations,
13
13
  plannerFailure,
14
14
  } from '@prisma-next/family-sql/control';
15
- import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
15
+ import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
16
16
  import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
17
17
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
18
18
  import type {
@@ -31,7 +31,6 @@ import {
31
31
  resolvePostgresIssueControlPolicySubject,
32
32
  resolvePostgresIssueCreationFactoryName,
33
33
  } from './control-policy';
34
- import { createResolveExistingEnumValues } from './enum-planning';
35
34
  import { planIssues } from './issue-planner';
36
35
  import type { PostgresOpFactoryCall } from './op-factory-call';
37
36
  import { TypeScriptRenderablePostgresMigration } from './planner-produced-postgres-migration';
@@ -52,7 +51,9 @@ type VerifySqlSchemaOptionsWithComponents = Parameters<typeof verifySqlSchema>[0
52
51
  readonly frameworkComponents: PlannerFrameworkComponents;
53
52
  };
54
53
 
55
- export function createPostgresMigrationPlanner(lowerer: Lowerer): PostgresMigrationPlanner {
54
+ export function createPostgresMigrationPlanner(
55
+ lowerer: ExecuteRequestLowerer,
56
+ ): PostgresMigrationPlanner {
56
57
  return new PostgresMigrationPlanner(lowerer);
57
58
  }
58
59
 
@@ -88,9 +89,9 @@ export type PostgresPlanResult =
88
89
  * authoring surface.
89
90
  */
90
91
  export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgres'> {
91
- readonly #lowerer: Lowerer | undefined;
92
+ readonly #lowerer: ExecuteRequestLowerer | undefined;
92
93
 
93
- constructor(lowerer?: Lowerer) {
94
+ constructor(lowerer?: ExecuteRequestLowerer) {
94
95
  this.#lowerer = lowerer;
95
96
  }
96
97
 
@@ -266,7 +267,6 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
266
267
  frameworkComponents: options.frameworkComponents,
267
268
  normalizeDefault: parsePostgresDefault,
268
269
  normalizeNativeType: normalizeSchemaNativeType,
269
- resolveExistingEnumValues: createResolveExistingEnumValues(options.contract.storage),
270
270
  };
271
271
  const verifyResult = verifySqlSchema(verifyOptions);
272
272
  // Schema presence is a Postgres-specific concern (no equivalent in