@prisma-next/target-postgres 0.13.0 → 0.14.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 (190) 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 +163 -15
  16. package/dist/contract-free.d.mts.map +1 -1
  17. package/dist/contract-free.mjs +4 -17
  18. package/dist/contract-free.mjs.map +1 -1
  19. package/dist/control.d.mts.map +1 -1
  20. package/dist/control.mjs +21 -27
  21. package/dist/control.mjs.map +1 -1
  22. package/dist/{data-transform-D25tLeYU.mjs → data-transform-BOWpliq8.mjs} +9 -17
  23. package/dist/data-transform-BOWpliq8.mjs.map +1 -0
  24. package/dist/{data-transform-DGOqcLrf.d.mts → data-transform-DDgWdB5o.d.mts} +2 -2
  25. package/dist/data-transform-DDgWdB5o.d.mts.map +1 -0
  26. package/dist/data-transform.d.mts +1 -1
  27. package/dist/data-transform.mjs +1 -1
  28. package/dist/ddl-QDyOSeLc.mjs +251 -0
  29. package/dist/ddl-QDyOSeLc.mjs.map +1 -0
  30. package/dist/ddl.d.mts +2 -2
  31. package/dist/ddl.mjs +2 -2
  32. package/dist/descriptor-meta-CpGygXpI.mjs +140 -0
  33. package/dist/descriptor-meta-CpGygXpI.mjs.map +1 -0
  34. package/dist/{issue-planner-Br0pt1Ea.mjs → issue-planner-DL6g3CmE.mjs} +52 -366
  35. package/dist/issue-planner-DL6g3CmE.mjs.map +1 -0
  36. package/dist/issue-planner.d.mts +8 -11
  37. package/dist/issue-planner.d.mts.map +1 -1
  38. package/dist/issue-planner.mjs +1 -1
  39. package/dist/migration.d.mts +5 -92
  40. package/dist/migration.d.mts.map +1 -1
  41. package/dist/migration.mjs +4 -4
  42. package/dist/{nodes-DZk2JZG3.mjs → nodes-Bbhs2rwj.mjs} +31 -2
  43. package/dist/nodes-Bbhs2rwj.mjs.map +1 -0
  44. package/dist/{nodes-779hmCfL.d.mts → nodes-pLeLgdis.d.mts} +30 -3
  45. package/dist/nodes-pLeLgdis.d.mts.map +1 -0
  46. package/dist/{op-factory-call-D2aAUhmS.mjs → op-factory-call-D_p5vxwt.mjs} +601 -418
  47. package/dist/op-factory-call-D_p5vxwt.mjs.map +1 -0
  48. package/dist/{op-factory-call-DMA86_2D.d.mts → op-factory-call-DmQEc3XV.d.mts} +119 -72
  49. package/dist/op-factory-call-DmQEc3XV.d.mts.map +1 -0
  50. package/dist/op-factory-call.d.mts +2 -2
  51. package/dist/op-factory-call.mjs +2 -2
  52. package/dist/pack.d.mts +36 -15
  53. package/dist/pack.d.mts.map +1 -1
  54. package/dist/pack.mjs +1 -1
  55. package/dist/{planner-CAYPJObw.mjs → planner-Bs_baQax.mjs} +25 -45
  56. package/dist/planner-Bs_baQax.mjs.map +1 -0
  57. package/dist/{planner-ddl-builders-Cw2n2llW.mjs → planner-ddl-builders-B2wOwLqI.mjs} +2 -2
  58. package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +1 -0
  59. package/dist/planner-ddl-builders.d.mts +4 -4
  60. package/dist/planner-ddl-builders.d.mts.map +1 -1
  61. package/dist/planner-ddl-builders.mjs +1 -1
  62. package/dist/{planner-identity-values-BIpa5p2I.mjs → planner-identity-values-CJPha2Sz.mjs} +3 -9
  63. package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -0
  64. package/dist/planner-identity-values.d.mts +1 -1
  65. package/dist/planner-identity-values.d.mts.map +1 -1
  66. package/dist/planner-identity-values.mjs +1 -1
  67. package/dist/{planner-produced-postgres-migration-NSEhWL0L.mjs → planner-produced-postgres-migration-Cji5vxUf.mjs} +6 -4
  68. package/dist/planner-produced-postgres-migration-Cji5vxUf.mjs.map +1 -0
  69. package/dist/{planner-produced-postgres-migration-B4EDvLdz.d.mts → planner-produced-postgres-migration-QqHa2C2l.d.mts} +5 -6
  70. package/dist/planner-produced-postgres-migration-QqHa2C2l.d.mts.map +1 -0
  71. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  72. package/dist/planner-produced-postgres-migration.mjs +1 -1
  73. package/dist/planner-sql-checks-jqUUGyQR.mjs +152 -0
  74. package/dist/planner-sql-checks-jqUUGyQR.mjs.map +1 -0
  75. package/dist/planner-sql-checks.d.mts +3 -49
  76. package/dist/planner-sql-checks.d.mts.map +1 -1
  77. package/dist/planner-sql-checks.mjs +2 -2
  78. package/dist/{planner-type-resolution-836DExFN.mjs → planner-type-resolution-Bt2f_q-F.mjs} +1 -6
  79. package/dist/planner-type-resolution-Bt2f_q-F.mjs.map +1 -0
  80. package/dist/planner.d.mts +4 -4
  81. package/dist/planner.d.mts.map +1 -1
  82. package/dist/planner.mjs +1 -1
  83. package/dist/{postgres-contract-serializer-DYTyXjPf.mjs → postgres-contract-serializer-k3TAcPMY.mjs} +30 -37
  84. package/dist/postgres-contract-serializer-k3TAcPMY.mjs.map +1 -0
  85. package/dist/postgres-migration-B5jKrXv3.mjs +145 -0
  86. package/dist/postgres-migration-B5jKrXv3.mjs.map +1 -0
  87. package/dist/postgres-migration-Y4YBJqkS.d.mts +181 -0
  88. package/dist/postgres-migration-Y4YBJqkS.d.mts.map +1 -0
  89. package/dist/{postgres-schema-BuxCxbvB.mjs → postgres-schema-COGZ1ark.mjs} +82 -23
  90. package/dist/postgres-schema-COGZ1ark.mjs.map +1 -0
  91. package/dist/{render-ops-BpjstrKQ.mjs → render-ops-BREh1kHe.mjs} +10 -5
  92. package/dist/render-ops-BREh1kHe.mjs.map +1 -0
  93. package/dist/render-ops.d.mts +2 -2
  94. package/dist/render-ops.d.mts.map +1 -1
  95. package/dist/render-ops.mjs +1 -1
  96. package/dist/runtime.d.mts +1 -0
  97. package/dist/runtime.d.mts.map +1 -1
  98. package/dist/runtime.mjs +2 -2
  99. package/dist/table-source-BvFo7gVs.d.mts +15 -0
  100. package/dist/table-source-BvFo7gVs.d.mts.map +1 -0
  101. package/dist/types.d.mts +34 -19
  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/checks.ts +363 -0
  106. package/src/contract-free/ddl.ts +28 -1
  107. package/src/core/authoring.ts +43 -44
  108. package/src/core/codec-helpers.ts +0 -17
  109. package/src/core/codec-ids.ts +1 -1
  110. package/src/core/codec-type-map.ts +2 -2
  111. package/src/core/codecs.ts +43 -48
  112. package/src/core/ddl/nodes.ts +59 -1
  113. package/src/core/migrations/control-policy.ts +17 -47
  114. package/src/core/migrations/issue-planner.ts +34 -70
  115. package/src/core/migrations/op-factory-call.ts +486 -215
  116. package/src/core/migrations/operations/columns.ts +175 -140
  117. package/src/core/migrations/operations/constraints.ts +79 -108
  118. package/src/core/migrations/operations/data-transform.ts +15 -18
  119. package/src/core/migrations/operations/dependencies.ts +16 -14
  120. package/src/core/migrations/operations/indexes.ts +31 -28
  121. package/src/core/migrations/operations/shared.ts +2 -2
  122. package/src/core/migrations/operations/tables.ts +13 -14
  123. package/src/core/migrations/planner-ddl-builders.ts +3 -4
  124. package/src/core/migrations/planner-identity-values.ts +4 -28
  125. package/src/core/migrations/planner-produced-postgres-migration.ts +15 -7
  126. package/src/core/migrations/planner-recipes.ts +44 -39
  127. package/src/core/migrations/planner-sql-checks.ts +3 -178
  128. package/src/core/migrations/planner-strategies.ts +76 -449
  129. package/src/core/migrations/planner-type-resolution.ts +2 -20
  130. package/src/core/migrations/planner.ts +6 -6
  131. package/src/core/migrations/postgres-migration.ts +287 -7
  132. package/src/core/migrations/render-ops.ts +26 -13
  133. package/src/core/migrations/runner.ts +26 -20
  134. package/src/core/postgres-contract-serializer.ts +37 -54
  135. package/src/core/postgres-enum-type-schema.ts +17 -0
  136. package/src/core/postgres-schema.ts +86 -46
  137. package/src/exports/codecs.ts +2 -2
  138. package/src/exports/contract-free.ts +22 -1
  139. package/src/exports/control.ts +0 -22
  140. package/src/exports/ddl.ts +4 -0
  141. package/src/exports/migration.ts +1 -29
  142. package/src/exports/op-factory-call.ts +0 -4
  143. package/src/exports/planner-sql-checks.ts +0 -7
  144. package/src/exports/types.ts +0 -1
  145. package/dist/codec-ids-B1vOchLE.d.mts.map +0 -1
  146. package/dist/codec-ids-CTikp1if.mjs.map +0 -1
  147. package/dist/codecs-CBpEv4s5.d.mts.map +0 -1
  148. package/dist/data-transform-D25tLeYU.mjs.map +0 -1
  149. package/dist/data-transform-DGOqcLrf.d.mts.map +0 -1
  150. package/dist/ddl-77SyXgFt.mjs +0 -30
  151. package/dist/ddl-77SyXgFt.mjs.map +0 -1
  152. package/dist/descriptor-meta-DKmj-IMN.mjs +0 -14
  153. package/dist/descriptor-meta-DKmj-IMN.mjs.map +0 -1
  154. package/dist/descriptor-meta-runtime-My8_s4cs.mjs +0 -130
  155. package/dist/descriptor-meta-runtime-My8_s4cs.mjs.map +0 -1
  156. package/dist/enum-planning-BCyvlFHk.mjs +0 -0
  157. package/dist/enum-planning-BCyvlFHk.mjs.map +0 -1
  158. package/dist/enum-planning.d.mts +0 -86
  159. package/dist/enum-planning.d.mts.map +0 -1
  160. package/dist/enum-planning.mjs +0 -2
  161. package/dist/issue-planner-Br0pt1Ea.mjs.map +0 -1
  162. package/dist/nodes-779hmCfL.d.mts.map +0 -1
  163. package/dist/nodes-DZk2JZG3.mjs.map +0 -1
  164. package/dist/op-factory-call-D2aAUhmS.mjs.map +0 -1
  165. package/dist/op-factory-call-DMA86_2D.d.mts.map +0 -1
  166. package/dist/planner-CAYPJObw.mjs.map +0 -1
  167. package/dist/planner-ddl-builders-Cw2n2llW.mjs.map +0 -1
  168. package/dist/planner-identity-values-BIpa5p2I.mjs.map +0 -1
  169. package/dist/planner-produced-postgres-migration-B4EDvLdz.d.mts.map +0 -1
  170. package/dist/planner-produced-postgres-migration-NSEhWL0L.mjs.map +0 -1
  171. package/dist/planner-sql-checks-DAdhnI2c.mjs +0 -272
  172. package/dist/planner-sql-checks-DAdhnI2c.mjs.map +0 -1
  173. package/dist/planner-type-resolution-836DExFN.mjs.map +0 -1
  174. package/dist/postgres-contract-serializer-DYTyXjPf.mjs.map +0 -1
  175. package/dist/postgres-enum-type-BVn63a89.d.mts +0 -72
  176. package/dist/postgres-enum-type-BVn63a89.d.mts.map +0 -1
  177. package/dist/postgres-enum-type-DPKqCBem.mjs +0 -62
  178. package/dist/postgres-enum-type-DPKqCBem.mjs.map +0 -1
  179. package/dist/postgres-migration-COore9Mz.mjs +0 -71
  180. package/dist/postgres-migration-COore9Mz.mjs.map +0 -1
  181. package/dist/postgres-migration-DZ_gLUOW.d.mts +0 -72
  182. package/dist/postgres-migration-DZ_gLUOW.d.mts.map +0 -1
  183. package/dist/postgres-schema-BuxCxbvB.mjs.map +0 -1
  184. package/dist/render-ops-BpjstrKQ.mjs.map +0 -1
  185. package/dist/shared-DarONYBZ.d.mts +0 -43
  186. package/dist/shared-DarONYBZ.d.mts.map +0 -1
  187. package/src/core/migrations/enum-planning.ts +0 -213
  188. package/src/core/migrations/operations/enums.ts +0 -114
  189. package/src/core/postgres-enum-type.ts +0 -89
  190. package/src/exports/enum-planning.ts +0 -11
@@ -1,79 +1,53 @@
1
- import { quoteIdentifier } from '../../sql-utils';
1
+ import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
2
2
  import {
3
- columnDefaultExistsCheck,
4
- columnExistsCheck,
5
- columnNullabilityCheck,
6
- columnTypeCheck,
7
- qualifyTableName,
8
- } from '../planner-sql-checks';
9
- import { type ColumnSpec, type Op, step, targetDetails } from './shared';
3
+ columnDefaultAst,
4
+ columnExistsAst,
5
+ columnNullabilityAst,
6
+ columnTypeAst,
7
+ noNullValuesAst,
8
+ tableIsEmptyAst,
9
+ } from '../../../contract-free/checks';
10
+ import { quoteIdentifier } from '../../sql-utils';
11
+ import { qualifyTableName } from '../planner-sql-checks';
12
+ import { type Op, step, targetDetails } from './shared';
10
13
 
11
- export function addColumn(schemaName: string, tableName: string, column: ColumnSpec): Op {
12
- const qualified = qualifyTableName(schemaName, tableName);
13
- const parts = [
14
- `ALTER TABLE ${qualified}`,
15
- `ADD COLUMN ${quoteIdentifier(column.name)} ${column.typeSql}`,
16
- column.defaultSql,
17
- column.nullable ? '' : 'NOT NULL',
18
- ].filter(Boolean);
19
- const addSql = parts.join(' ');
14
+ type CheckStep = { sql: string; params?: readonly unknown[] };
20
15
 
21
- return {
22
- id: `column.${tableName}.${column.name}`,
23
- label: `Add column "${column.name}" to "${tableName}"`,
24
- operationClass: 'additive',
25
- target: targetDetails('column', column.name, schemaName, tableName),
26
- precheck: [
27
- step(
28
- `ensure column "${column.name}" is missing`,
29
- columnExistsCheck({
30
- schema: schemaName,
31
- table: tableName,
32
- column: column.name,
33
- exists: false,
34
- }),
35
- ),
36
- ],
37
- execute: [step(`add column "${column.name}"`, addSql)],
38
- postcheck: [
39
- step(
40
- `verify column "${column.name}" exists`,
41
- columnExistsCheck({ schema: schemaName, table: tableName, column: column.name }),
42
- ),
43
- ],
44
- };
16
+ async function columnExistsSteps(
17
+ lowerer: ExecuteRequestLowerer,
18
+ options: { schema: string; table: string; column: string },
19
+ ): Promise<{ present: CheckStep; absent: CheckStep }> {
20
+ const checks = columnExistsAst(options);
21
+ const present = await lowerer.lowerToExecuteRequest(checks.columnPresent());
22
+ const absent = await lowerer.lowerToExecuteRequest(checks.columnAbsent());
23
+ return { present, absent };
45
24
  }
46
25
 
47
- export function dropColumn(schemaName: string, tableName: string, columnName: string): Op {
26
+ export async function dropColumn(
27
+ schemaName: string,
28
+ tableName: string,
29
+ columnName: string,
30
+ lowerer: ExecuteRequestLowerer,
31
+ ): Promise<Op> {
48
32
  const qualified = qualifyTableName(schemaName, tableName);
33
+ const { present, absent } = await columnExistsSteps(lowerer, {
34
+ schema: schemaName,
35
+ table: tableName,
36
+ column: columnName,
37
+ });
49
38
  return {
50
39
  id: `dropColumn.${tableName}.${columnName}`,
51
40
  label: `Drop column "${columnName}" from "${tableName}"`,
52
41
  operationClass: 'destructive',
53
42
  target: targetDetails('column', columnName, schemaName, tableName),
54
- precheck: [
55
- step(
56
- `ensure column "${columnName}" exists`,
57
- columnExistsCheck({ schema: schemaName, table: tableName, column: columnName }),
58
- ),
59
- ],
43
+ precheck: [step(`ensure column "${columnName}" exists`, present.sql, present.params)],
60
44
  execute: [
61
45
  step(
62
46
  `drop column "${columnName}"`,
63
47
  `ALTER TABLE ${qualified} DROP COLUMN ${quoteIdentifier(columnName)}`,
64
48
  ),
65
49
  ],
66
- postcheck: [
67
- step(
68
- `verify column "${columnName}" does not exist`,
69
- columnExistsCheck({
70
- schema: schemaName,
71
- table: tableName,
72
- column: columnName,
73
- exists: false,
74
- }),
75
- ),
76
- ],
50
+ postcheck: [step(`verify column "${columnName}" does not exist`, absent.sql, absent.params)],
77
51
  };
78
52
  }
79
53
 
@@ -85,7 +59,7 @@ export function dropColumn(schemaName: string, tableName: string, columnName: st
85
59
  * string appearing in the human-readable label (typically `toType` when
86
60
  * explicit, else the column's native type).
87
61
  */
88
- export function alterColumnType(
62
+ export async function alterColumnType(
89
63
  schemaName: string,
90
64
  tableName: string,
91
65
  columnName: string,
@@ -95,22 +69,31 @@ export function alterColumnType(
95
69
  readonly rawTargetTypeForLabel: string;
96
70
  readonly using?: string;
97
71
  },
98
- ): Op {
72
+ lowerer: ExecuteRequestLowerer,
73
+ ): Promise<Op> {
99
74
  const qualified = qualifyTableName(schemaName, tableName);
100
75
  const usingClause = options.using
101
76
  ? ` USING ${options.using}`
102
77
  : ` USING ${quoteIdentifier(columnName)}::${options.qualifiedTargetType}`;
78
+ const { present } = await columnExistsSteps(lowerer, {
79
+ schema: schemaName,
80
+ table: tableName,
81
+ column: columnName,
82
+ });
83
+ const typeCheck = await lowerer.lowerToExecuteRequest(
84
+ columnTypeAst({
85
+ schema: schemaName,
86
+ table: tableName,
87
+ column: columnName,
88
+ expectedType: options.formatTypeExpected,
89
+ }),
90
+ );
103
91
  return {
104
92
  id: `alterType.${tableName}.${columnName}`,
105
93
  label: `Alter type of "${tableName}"."${columnName}" to ${options.rawTargetTypeForLabel}`,
106
94
  operationClass: 'destructive',
107
95
  target: targetDetails('column', columnName, schemaName, tableName),
108
- precheck: [
109
- step(
110
- `ensure column "${columnName}" exists`,
111
- columnExistsCheck({ schema: schemaName, table: tableName, column: columnName }),
112
- ),
113
- ],
96
+ precheck: [step(`ensure column "${columnName}" exists`, present.sql, present.params)],
114
97
  execute: [
115
98
  step(
116
99
  `alter type of "${columnName}"`,
@@ -120,34 +103,45 @@ export function alterColumnType(
120
103
  postcheck: [
121
104
  step(
122
105
  `verify column "${columnName}" has type "${options.formatTypeExpected}"`,
123
- columnTypeCheck({
124
- schema: schemaName,
125
- table: tableName,
126
- column: columnName,
127
- expectedType: options.formatTypeExpected,
128
- }),
106
+ typeCheck.sql,
107
+ typeCheck.params,
129
108
  ),
130
109
  ],
131
110
  meta: { warning: 'TABLE_REWRITE' },
132
111
  };
133
112
  }
134
113
 
135
- export function setNotNull(schemaName: string, tableName: string, columnName: string): Op {
114
+ export async function setNotNull(
115
+ schemaName: string,
116
+ tableName: string,
117
+ columnName: string,
118
+ lowerer: ExecuteRequestLowerer,
119
+ ): Promise<Op> {
136
120
  const qualified = qualifyTableName(schemaName, tableName);
121
+ const { present } = await columnExistsSteps(lowerer, {
122
+ schema: schemaName,
123
+ table: tableName,
124
+ column: columnName,
125
+ });
126
+ const noNulls = await lowerer.lowerToExecuteRequest(
127
+ noNullValuesAst({ schema: schemaName, table: tableName, column: columnName }),
128
+ );
129
+ const notNullable = await lowerer.lowerToExecuteRequest(
130
+ columnNullabilityAst({
131
+ schema: schemaName,
132
+ table: tableName,
133
+ column: columnName,
134
+ nullable: false,
135
+ }),
136
+ );
137
137
  return {
138
138
  id: `alterNullability.setNotNull.${tableName}.${columnName}`,
139
139
  label: `Set NOT NULL on "${tableName}"."${columnName}"`,
140
140
  operationClass: 'destructive',
141
141
  target: targetDetails('column', columnName, schemaName, tableName),
142
142
  precheck: [
143
- step(
144
- `ensure column "${columnName}" exists`,
145
- columnExistsCheck({ schema: schemaName, table: tableName, column: columnName }),
146
- ),
147
- step(
148
- `ensure no NULL values in "${columnName}"`,
149
- `SELECT NOT EXISTS (SELECT 1 FROM ${qualified} WHERE ${quoteIdentifier(columnName)} IS NULL)`,
150
- ),
143
+ step(`ensure column "${columnName}" exists`, present.sql, present.params),
144
+ step(`ensure no NULL values in "${columnName}"`, noNulls.sql, noNulls.params),
151
145
  ],
152
146
  execute: [
153
147
  step(
@@ -156,49 +150,44 @@ export function setNotNull(schemaName: string, tableName: string, columnName: st
156
150
  ),
157
151
  ],
158
152
  postcheck: [
159
- step(
160
- `verify column "${columnName}" is NOT NULL`,
161
- columnNullabilityCheck({
162
- schema: schemaName,
163
- table: tableName,
164
- column: columnName,
165
- nullable: false,
166
- }),
167
- ),
153
+ step(`verify column "${columnName}" is NOT NULL`, notNullable.sql, notNullable.params),
168
154
  ],
169
155
  };
170
156
  }
171
157
 
172
- export function dropNotNull(schemaName: string, tableName: string, columnName: string): Op {
158
+ export async function dropNotNull(
159
+ schemaName: string,
160
+ tableName: string,
161
+ columnName: string,
162
+ lowerer: ExecuteRequestLowerer,
163
+ ): Promise<Op> {
173
164
  const qualified = qualifyTableName(schemaName, tableName);
165
+ const { present } = await columnExistsSteps(lowerer, {
166
+ schema: schemaName,
167
+ table: tableName,
168
+ column: columnName,
169
+ });
170
+ const nullable = await lowerer.lowerToExecuteRequest(
171
+ columnNullabilityAst({
172
+ schema: schemaName,
173
+ table: tableName,
174
+ column: columnName,
175
+ nullable: true,
176
+ }),
177
+ );
174
178
  return {
175
179
  id: `alterNullability.dropNotNull.${tableName}.${columnName}`,
176
180
  label: `Drop NOT NULL on "${tableName}"."${columnName}"`,
177
181
  operationClass: 'widening',
178
182
  target: targetDetails('column', columnName, schemaName, tableName),
179
- precheck: [
180
- step(
181
- `ensure column "${columnName}" exists`,
182
- columnExistsCheck({ schema: schemaName, table: tableName, column: columnName }),
183
- ),
184
- ],
183
+ precheck: [step(`ensure column "${columnName}" exists`, present.sql, present.params)],
185
184
  execute: [
186
185
  step(
187
186
  `drop NOT NULL on "${columnName}"`,
188
187
  `ALTER TABLE ${qualified} ALTER COLUMN ${quoteIdentifier(columnName)} DROP NOT NULL`,
189
188
  ),
190
189
  ],
191
- postcheck: [
192
- step(
193
- `verify column "${columnName}" is nullable`,
194
- columnNullabilityCheck({
195
- schema: schemaName,
196
- table: tableName,
197
- column: columnName,
198
- nullable: true,
199
- }),
200
- ),
201
- ],
190
+ postcheck: [step(`verify column "${columnName}" is nullable`, nullable.sql, nullable.params)],
202
191
  };
203
192
  }
204
193
 
@@ -212,25 +201,29 @@ export function dropNotNull(schemaName: string, tableName: string, columnName: s
212
201
  * when the column already has a different default — policy enforcement
213
202
  * treats that as a widening change rather than an additive one.
214
203
  */
215
- export function setDefault(
204
+ export async function setDefault(
216
205
  schemaName: string,
217
206
  tableName: string,
218
207
  columnName: string,
219
208
  defaultSql: string,
209
+ lowerer: ExecuteRequestLowerer,
220
210
  operationClass: 'additive' | 'widening' = 'additive',
221
- ): Op {
211
+ ): Promise<Op> {
222
212
  const qualified = qualifyTableName(schemaName, tableName);
213
+ const { present } = await columnExistsSteps(lowerer, {
214
+ schema: schemaName,
215
+ table: tableName,
216
+ column: columnName,
217
+ });
218
+ const hasDefault = await lowerer.lowerToExecuteRequest(
219
+ columnDefaultAst({ schema: schemaName, table: tableName, column: columnName }).defaultPresent(),
220
+ );
223
221
  return {
224
222
  id: `setDefault.${tableName}.${columnName}`,
225
223
  label: `Set default on "${tableName}"."${columnName}"`,
226
224
  operationClass,
227
225
  target: targetDetails('column', columnName, schemaName, tableName),
228
- precheck: [
229
- step(
230
- `ensure column "${columnName}" exists`,
231
- columnExistsCheck({ schema: schemaName, table: tableName, column: columnName }),
232
- ),
233
- ],
226
+ precheck: [step(`ensure column "${columnName}" exists`, present.sql, present.params)],
234
227
  execute: [
235
228
  step(
236
229
  `set default on "${columnName}"`,
@@ -238,32 +231,32 @@ export function setDefault(
238
231
  ),
239
232
  ],
240
233
  postcheck: [
241
- step(
242
- `verify column "${columnName}" has a default`,
243
- columnDefaultExistsCheck({
244
- schema: schemaName,
245
- table: tableName,
246
- column: columnName,
247
- exists: true,
248
- }),
249
- ),
234
+ step(`verify column "${columnName}" has a default`, hasDefault.sql, hasDefault.params),
250
235
  ],
251
236
  };
252
237
  }
253
238
 
254
- export function dropDefault(schemaName: string, tableName: string, columnName: string): Op {
239
+ export async function dropDefault(
240
+ schemaName: string,
241
+ tableName: string,
242
+ columnName: string,
243
+ lowerer: ExecuteRequestLowerer,
244
+ ): Promise<Op> {
255
245
  const qualified = qualifyTableName(schemaName, tableName);
246
+ const { present } = await columnExistsSteps(lowerer, {
247
+ schema: schemaName,
248
+ table: tableName,
249
+ column: columnName,
250
+ });
251
+ const noDefault = await lowerer.lowerToExecuteRequest(
252
+ columnDefaultAst({ schema: schemaName, table: tableName, column: columnName }).defaultAbsent(),
253
+ );
256
254
  return {
257
255
  id: `dropDefault.${tableName}.${columnName}`,
258
256
  label: `Drop default on "${tableName}"."${columnName}"`,
259
257
  operationClass: 'destructive',
260
258
  target: targetDetails('column', columnName, schemaName, tableName),
261
- precheck: [
262
- step(
263
- `ensure column "${columnName}" exists`,
264
- columnExistsCheck({ schema: schemaName, table: tableName, column: columnName }),
265
- ),
266
- ],
259
+ precheck: [step(`ensure column "${columnName}" exists`, present.sql, present.params)],
267
260
  execute: [
268
261
  step(
269
262
  `drop default on "${columnName}"`,
@@ -271,15 +264,57 @@ export function dropDefault(schemaName: string, tableName: string, columnName: s
271
264
  ),
272
265
  ],
273
266
  postcheck: [
267
+ step(`verify column "${columnName}" has no default`, noDefault.sql, noDefault.params),
268
+ ],
269
+ };
270
+ }
271
+
272
+ /**
273
+ * Builds the op for adding a NOT NULL column (no contract default) to a
274
+ * non-empty table. Prechecks assert the column is absent and the table is
275
+ * empty; the execute step is the raw ADD COLUMN SQL passed by the caller
276
+ * (slice-7 deferred); postchecks assert the column exists and is NOT NULL.
277
+ */
278
+ export async function addNotNullColumnDirect(
279
+ schemaName: string,
280
+ tableName: string,
281
+ columnName: string,
282
+ executeStepSql: string,
283
+ lowerer: ExecuteRequestLowerer,
284
+ ): Promise<Op> {
285
+ const absent = await lowerer.lowerToExecuteRequest(
286
+ columnExistsAst({ schema: schemaName, table: tableName, column: columnName }).columnAbsent(),
287
+ );
288
+ const tableEmpty = await lowerer.lowerToExecuteRequest(tableIsEmptyAst(schemaName, tableName));
289
+ const present = await lowerer.lowerToExecuteRequest(
290
+ columnExistsAst({ schema: schemaName, table: tableName, column: columnName }).columnPresent(),
291
+ );
292
+ const notNullable = await lowerer.lowerToExecuteRequest(
293
+ columnNullabilityAst({
294
+ schema: schemaName,
295
+ table: tableName,
296
+ column: columnName,
297
+ nullable: false,
298
+ }),
299
+ );
300
+ return {
301
+ id: `column.${tableName}.${columnName}`,
302
+ label: `Add column ${columnName} to ${tableName}`,
303
+ summary: `Adds column ${columnName} to table ${tableName}`,
304
+ operationClass: 'additive',
305
+ target: targetDetails('column', columnName, schemaName, tableName),
306
+ precheck: [
307
+ step(`ensure column "${columnName}" is missing`, absent.sql, absent.params),
274
308
  step(
275
- `verify column "${columnName}" has no default`,
276
- columnDefaultExistsCheck({
277
- schema: schemaName,
278
- table: tableName,
279
- column: columnName,
280
- exists: false,
281
- }),
309
+ `ensure table "${tableName}" is empty before adding NOT NULL column without default`,
310
+ tableEmpty.sql,
311
+ tableEmpty.params,
282
312
  ),
283
313
  ],
314
+ execute: [step(`add column "${columnName}"`, executeStepSql)],
315
+ postcheck: [
316
+ step(`verify column "${columnName}" exists`, present.sql, present.params),
317
+ step(`verify column "${columnName}" is NOT NULL`, notNullable.sql, notNullable.params),
318
+ ],
284
319
  };
285
320
  }