@prisma-next/target-postgres 0.14.0-dev.15 → 0.14.0-dev.17

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 (66) hide show
  1. package/dist/codecs.mjs.map +1 -1
  2. package/dist/contract-free.d.mts +8 -3
  3. package/dist/contract-free.d.mts.map +1 -1
  4. package/dist/contract-free.mjs +2 -2
  5. package/dist/control.mjs +2 -2
  6. package/dist/data-transform-BOWpliq8.mjs.map +1 -1
  7. package/dist/{ddl-BlsTIBeP.mjs → ddl-D1K9soOg.mjs} +11 -4
  8. package/dist/{ddl-BlsTIBeP.mjs.map → ddl-D1K9soOg.mjs.map} +1 -1
  9. package/dist/ddl.d.mts +2 -2
  10. package/dist/ddl.mjs +2 -2
  11. package/dist/{issue-planner-CwoeupPM.mjs → issue-planner-DSQ07ihi.mjs} +5 -6
  12. package/dist/issue-planner-DSQ07ihi.mjs.map +1 -0
  13. package/dist/issue-planner.d.mts +1 -1
  14. package/dist/issue-planner.mjs +1 -1
  15. package/dist/migration.d.mts +2 -2
  16. package/dist/migration.mjs +2 -2
  17. package/dist/{nodes-YvdohJ2X.d.mts → nodes-CSkPX3rI.d.mts} +19 -3
  18. package/dist/nodes-CSkPX3rI.d.mts.map +1 -0
  19. package/dist/{nodes-DhDIIhBT.mjs → nodes-Dh__NS58.mjs} +14 -2
  20. package/dist/nodes-Dh__NS58.mjs.map +1 -0
  21. package/dist/{op-factory-call-tMksrGIq.mjs → op-factory-call-BZFhzxUH.mjs} +55 -26
  22. package/dist/op-factory-call-BZFhzxUH.mjs.map +1 -0
  23. package/dist/{op-factory-call-DXHAIisl.d.mts → op-factory-call-CQ4P40QC.d.mts} +8 -10
  24. package/dist/op-factory-call-CQ4P40QC.d.mts.map +1 -0
  25. package/dist/op-factory-call.d.mts +1 -1
  26. package/dist/op-factory-call.mjs +1 -1
  27. package/dist/{planner-BbSZJVW7.mjs → planner-Cu8YdghT.mjs} +4 -4
  28. package/dist/{planner-BbSZJVW7.mjs.map → planner-Cu8YdghT.mjs.map} +1 -1
  29. package/dist/{planner-ddl-builders-B2wOwLqI.mjs → planner-ddl-builders-C1IsRjVl.mjs} +2 -25
  30. package/dist/planner-ddl-builders-C1IsRjVl.mjs.map +1 -0
  31. package/dist/planner-ddl-builders.d.mts +2 -4
  32. package/dist/planner-ddl-builders.d.mts.map +1 -1
  33. package/dist/planner-ddl-builders.mjs +2 -2
  34. package/dist/{planner-produced-postgres-migration-DfM5lBv1.d.mts → planner-produced-postgres-migration-CNTSCMl7.d.mts} +2 -2
  35. package/dist/{planner-produced-postgres-migration-DfM5lBv1.d.mts.map → planner-produced-postgres-migration-CNTSCMl7.d.mts.map} +1 -1
  36. package/dist/{planner-produced-postgres-migration-C0FHnEsV.mjs → planner-produced-postgres-migration-SkStEa6Q.mjs} +2 -2
  37. package/dist/{planner-produced-postgres-migration-C0FHnEsV.mjs.map → planner-produced-postgres-migration-SkStEa6Q.mjs.map} +1 -1
  38. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  39. package/dist/planner-produced-postgres-migration.mjs +1 -1
  40. package/dist/planner.d.mts +1 -1
  41. package/dist/planner.mjs +1 -1
  42. package/dist/{postgres-migration-BJWxullf.mjs → postgres-migration-BcDtTMs5.mjs} +2 -2
  43. package/dist/{postgres-migration-BJWxullf.mjs.map → postgres-migration-BcDtTMs5.mjs.map} +1 -1
  44. package/dist/{postgres-migration-D22a2y0l.d.mts → postgres-migration-DJNJHcdP.d.mts} +2 -2
  45. package/dist/{postgres-migration-D22a2y0l.d.mts.map → postgres-migration-DJNJHcdP.d.mts.map} +1 -1
  46. package/package.json +19 -19
  47. package/src/contract-free/ddl.ts +10 -1
  48. package/src/core/codecs.ts +1 -1
  49. package/src/core/ddl/nodes.ts +26 -1
  50. package/src/core/migrations/bound-schema.ts +10 -0
  51. package/src/core/migrations/op-factory-call.ts +10 -21
  52. package/src/core/migrations/operations/columns.ts +24 -12
  53. package/src/core/migrations/operations/data-transform.ts +6 -0
  54. package/src/core/migrations/operations/shared.ts +0 -30
  55. package/src/core/migrations/planner-ddl-builders.ts +1 -55
  56. package/src/core/migrations/planner-recipes.ts +31 -13
  57. package/src/core/migrations/planner-strategies.ts +3 -11
  58. package/src/exports/contract-free.ts +7 -1
  59. package/src/exports/ddl.ts +1 -0
  60. package/src/exports/planner-ddl-builders.ts +0 -2
  61. package/dist/issue-planner-CwoeupPM.mjs.map +0 -1
  62. package/dist/nodes-DhDIIhBT.mjs.map +0 -1
  63. package/dist/nodes-YvdohJ2X.d.mts.map +0 -1
  64. package/dist/op-factory-call-DXHAIisl.d.mts.map +0 -1
  65. package/dist/op-factory-call-tMksrGIq.mjs.map +0 -1
  66. package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +0 -1
@@ -11,6 +11,7 @@ import type { RlsPolicyOperation } from '../rls/canonicalize';
11
11
 
12
12
  export interface AlterTableActionVisitor<R> {
13
13
  addColumn(action: AddColumnAction): R;
14
+ dropDefault(action: DropDefaultAction): R;
14
15
  }
15
16
 
16
17
  export abstract class AlterTableAction {
@@ -33,7 +34,31 @@ export class AddColumnAction extends AlterTableAction {
33
34
  }
34
35
  }
35
36
 
36
- export type AnyAlterTableAction = AddColumnAction;
37
+ export class DropDefaultAction extends AlterTableAction {
38
+ readonly kind = 'drop-default' as const;
39
+ readonly columnName: string;
40
+
41
+ constructor(columnName: string) {
42
+ super();
43
+ this.columnName = columnName;
44
+ Object.freeze(this);
45
+ }
46
+
47
+ override accept<R>(visitor: AlterTableActionVisitor<R>): R {
48
+ return visitor.dropDefault(this);
49
+ }
50
+ }
51
+
52
+ /**
53
+ * The set of ALTER TABLE subactions currently expressible as typed DDL.
54
+ *
55
+ * The remaining actions — SetDefault, SetNotNull, DropNotNull,
56
+ * AlterColumnType — are still emitted as raw SQL by `operations/columns.ts`
57
+ * and join this union as they are converted to typed DDL. Until then it is
58
+ * intentionally partial: only the ALTER subactions used by the
59
+ * already-converted ops (AddColumn, DropDefault) appear here.
60
+ */
61
+ export type AnyAlterTableAction = AddColumnAction | DropDefaultAction;
37
62
 
38
63
  // ---------------------------------------------------------------------------
39
64
  // Top-level DDL visitor
@@ -0,0 +1,10 @@
1
+ import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
2
+
3
+ /**
4
+ * Maps a planner schema name to the contract-free DDL builder's optional
5
+ * `schema` field: the unbound sentinel becomes `undefined` (the builder omits
6
+ * the schema qualifier), every other name passes through.
7
+ */
8
+ export function boundSchema(schemaName: string): string | undefined {
9
+ return schemaName === UNBOUND_NAMESPACE_ID ? undefined : schemaName;
10
+ }
@@ -43,6 +43,7 @@ import * as contractFreeDdl from '../../contract-free/ddl';
43
43
  import type { PostgresRlsPolicy } from '../postgres-rls-policy';
44
44
  import { escapeLiteral, quoteIdentifier } from '../sql-utils';
45
45
  import type { PostgresColumnDefault } from '../types';
46
+ import { boundSchema } from './bound-schema';
46
47
  import {
47
48
  addNotNullColumnDirect,
48
49
  alterColumnType,
@@ -85,10 +86,6 @@ type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
85
86
  // transitive package on disk.
86
87
  const POSTGRES_MIGRATION_FACADE = '@prisma-next/postgres/migration';
87
88
 
88
- function boundSchema(schemaName: string): string | undefined {
89
- return schemaName === UNBOUND_NAMESPACE_ID ? undefined : schemaName;
90
- }
91
-
92
89
  abstract class PostgresOpFactoryCallNode extends TsExpression implements FrameworkOpFactoryCall {
93
90
  abstract readonly factoryName: string;
94
91
  abstract readonly operationClass: MigrationOperationClass;
@@ -686,10 +683,8 @@ export class DropDefaultCall extends PostgresOpFactoryCallNode {
686
683
 
687
684
  /**
688
685
  * Planner-internal call for adding a NOT NULL column (no contract default) to
689
- * a table that must be empty at migration time. Carries the raw ADD COLUMN SQL
690
- * (produced by `buildAddColumnSql` at plan time deferred from slice-7 typed
691
- * DDL) plus the parameters needed to lower the typed pre/postchecks at render
692
- * time.
686
+ * a table that must be empty at migration time. Carries the typed `DdlColumn`
687
+ * and lowers it to an ADD COLUMN execute step via the adapter at `toOp` time.
693
688
  *
694
689
  * No authored `PostgresMigration` method: this call is only emitted by the
695
690
  * planner, never hand-written by migration authors.
@@ -700,15 +695,15 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
700
695
  readonly schemaName: string;
701
696
  readonly tableName: string;
702
697
  readonly columnName: string;
703
- readonly executeStepSql: string;
698
+ readonly column: DdlColumn;
704
699
  readonly label: string;
705
700
 
706
- constructor(schemaName: string, tableName: string, columnName: string, executeStepSql: string) {
701
+ constructor(schemaName: string, tableName: string, columnName: string, column: DdlColumn) {
707
702
  super();
708
703
  this.schemaName = schemaName;
709
704
  this.tableName = tableName;
710
705
  this.columnName = columnName;
711
- this.executeStepSql = executeStepSql;
706
+ this.column = column;
712
707
  this.label = `Add column ${columnName} to ${tableName}`;
713
708
  this.freeze();
714
709
  }
@@ -719,13 +714,7 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
719
714
  `AddNotNullColumnDirectCall.toOp: a lowerer is required on the Postgres planner path (column "${this.columnName}" on table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
720
715
  );
721
716
  }
722
- return addNotNullColumnDirect(
723
- this.schemaName,
724
- this.tableName,
725
- this.columnName,
726
- this.executeStepSql,
727
- lowerer,
728
- );
717
+ return addNotNullColumnDirect(this.schemaName, this.tableName, this.column, lowerer);
729
718
  }
730
719
 
731
720
  renderTypeScript(): string {
@@ -740,9 +729,9 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
740
729
  /**
741
730
  * Planner-internal call for adding a NOT NULL column (no contract default)
742
731
  * using a temporary default value for non-empty tables. Carries all parameters
743
- * needed for `buildAddNotNullColumnWithTemporaryDefaultOperation`; the typed
744
- * pre/postchecks are lowered at render time via `toOp(lowerer)`. The execute
745
- * SQL (`buildAddColumnSql`) is still raw per slice-7 deferral.
732
+ * needed for `buildAddNotNullColumnWithTemporaryDefaultOperation`; both the
733
+ * typed ADD COLUMN execute step and the pre/postchecks are lowered via the
734
+ * adapter at `toOp` time.
746
735
  *
747
736
  * No authored `PostgresMigration` method: this call is only emitted by the
748
737
  * planner, never hand-written by migration authors.
@@ -1,4 +1,6 @@
1
1
  import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
2
+ import type { DdlColumn } from '@prisma-next/sql-relational-core/ast';
3
+ import { ifDefined } from '@prisma-next/utils/defined';
2
4
  import {
3
5
  columnDefaultAst,
4
6
  columnExistsAst,
@@ -7,7 +9,9 @@ import {
7
9
  noNullValuesAst,
8
10
  tableIsEmptyAst,
9
11
  } from '../../../contract-free/checks';
12
+ import * as contractFreeDdl from '../../../contract-free/ddl';
10
13
  import { quoteIdentifier } from '../../sql-utils';
14
+ import { boundSchema } from '../bound-schema';
11
15
  import { qualifyTableName } from '../planner-sql-checks';
12
16
  import { type Op, step, targetDetails } from './shared';
13
17
 
@@ -242,12 +246,18 @@ export async function dropDefault(
242
246
  columnName: string,
243
247
  lowerer: ExecuteRequestLowerer,
244
248
  ): Promise<Op> {
245
- const qualified = qualifyTableName(schemaName, tableName);
246
249
  const { present } = await columnExistsSteps(lowerer, {
247
250
  schema: schemaName,
248
251
  table: tableName,
249
252
  column: columnName,
250
253
  });
254
+ const dropDefaultExec = await lowerer.lowerToExecuteRequest(
255
+ contractFreeDdl.alterTable({
256
+ ...ifDefined('schema', boundSchema(schemaName)),
257
+ table: tableName,
258
+ actions: [contractFreeDdl.dropDefaultAction(columnName)],
259
+ }),
260
+ );
251
261
  const noDefault = await lowerer.lowerToExecuteRequest(
252
262
  columnDefaultAst({ schema: schemaName, table: tableName, column: columnName }).defaultAbsent(),
253
263
  );
@@ -257,12 +267,7 @@ export async function dropDefault(
257
267
  operationClass: 'destructive',
258
268
  target: targetDetails('column', columnName, schemaName, tableName),
259
269
  precheck: [step(`ensure column "${columnName}" exists`, present.sql, present.params)],
260
- execute: [
261
- step(
262
- `drop default on "${columnName}"`,
263
- `ALTER TABLE ${qualified} ALTER COLUMN ${quoteIdentifier(columnName)} DROP DEFAULT`,
264
- ),
265
- ],
270
+ execute: [step(`drop default on "${columnName}"`, dropDefaultExec.sql)],
266
271
  postcheck: [
267
272
  step(`verify column "${columnName}" has no default`, noDefault.sql, noDefault.params),
268
273
  ],
@@ -272,16 +277,23 @@ export async function dropDefault(
272
277
  /**
273
278
  * Builds the op for adding a NOT NULL column (no contract default) to a
274
279
  * non-empty table. Prechecks assert the column is absent and the table is
275
- * empty; the execute step is the raw ADD COLUMN SQL passed by the caller
276
- * (slice-7 deferred); postchecks assert the column exists and is NOT NULL.
280
+ * empty; the execute step lowers the typed `AddColumn` DDL node through the
281
+ * adapter; postchecks assert the column exists and is NOT NULL.
277
282
  */
278
283
  export async function addNotNullColumnDirect(
279
284
  schemaName: string,
280
285
  tableName: string,
281
- columnName: string,
282
- executeStepSql: string,
286
+ column: DdlColumn,
283
287
  lowerer: ExecuteRequestLowerer,
284
288
  ): Promise<Op> {
289
+ const columnName = column.name;
290
+ const addColumn = await lowerer.lowerToExecuteRequest(
291
+ contractFreeDdl.alterTable({
292
+ ...ifDefined('schema', boundSchema(schemaName)),
293
+ table: tableName,
294
+ actions: [contractFreeDdl.addColumnAction(column)],
295
+ }),
296
+ );
285
297
  const absent = await lowerer.lowerToExecuteRequest(
286
298
  columnExistsAst({ schema: schemaName, table: tableName, column: columnName }).columnAbsent(),
287
299
  );
@@ -311,7 +323,7 @@ export async function addNotNullColumnDirect(
311
323
  tableEmpty.params,
312
324
  ),
313
325
  ],
314
- execute: [step(`add column "${columnName}"`, executeStepSql)],
326
+ execute: [step(`add column "${columnName}"`, addColumn.sql)],
315
327
  postcheck: [
316
328
  step(`verify column "${columnName}" exists`, present.sql, present.params),
317
329
  step(`verify column "${columnName}" is NOT NULL`, notNullable.sql, notNullable.params),
@@ -113,6 +113,12 @@ export async function dataTransform<TContract extends Contract<SqlStorage>>(
113
113
  runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name)),
114
114
  );
115
115
 
116
+ // Raw remnant: the factory lowered the user's `check` plan above before
117
+ // we get a chance to wrap it, so the EXISTS lifting only has text by the
118
+ // time it runs. The fix is to rewrite the factory to defer lowering until
119
+ // after wrapping — so `cfExpr.exists(checkPlan.ast)` can produce the
120
+ // EXISTS query, then lower once at the boundary — not to grow new AST
121
+ // substrate. Out of scope for the slice that introduced this comment.
116
122
  const precheck: readonly SqlMigrationPlanOperationStep[] = checkPlan
117
123
  ? [
118
124
  {
@@ -1,30 +1,10 @@
1
1
  import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
2
2
  import type { ReferentialAction } from '@prisma-next/sql-contract/types';
3
3
  import { ifDefined } from '@prisma-next/utils/defined';
4
- import { quoteIdentifier } from '../../sql-utils';
5
4
  import type { OperationClass, PostgresPlanTargetDetails } from '../planner-target-details';
6
5
 
7
6
  export type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
8
7
 
9
- /**
10
- * Literal-args shape for a column definition consumed by `addColumn` and
11
- * similar operations. Fully materialized: codec expansion and default
12
- * rendering have already happened in the wrapper.
13
- *
14
- * - `typeSql` is the column's DDL type string (e.g. `"integer"`, `"SERIAL"`,
15
- * `"varchar(100)"`), already produced by `buildColumnTypeSql` in the
16
- * call-factory wrapper.
17
- * - `defaultSql` is the full `DEFAULT …` clause (e.g. `"DEFAULT 42"`) or an
18
- * empty string when the column has no default, matching
19
- * `buildColumnDefaultSql`'s output.
20
- */
21
- export interface ColumnSpec {
22
- readonly name: string;
23
- readonly typeSql: string;
24
- readonly defaultSql: string;
25
- readonly nullable: boolean;
26
- }
27
-
28
8
  /**
29
9
  * Literal-args shape for a foreign key definition. `references.schema`
30
10
  * carries the target table's namespace (schema) coordinate so the rendered
@@ -57,13 +37,3 @@ export function targetDetails(
57
37
  details: { schema, objectType, name, ...ifDefined('table', table) },
58
38
  };
59
39
  }
60
-
61
- export function renderColumnDefinition(column: ColumnSpec): string {
62
- const parts = [
63
- quoteIdentifier(column.name),
64
- column.typeSql,
65
- column.defaultSql,
66
- column.nullable ? '' : 'NOT NULL',
67
- ].filter(Boolean);
68
- return parts.join(' ');
69
- }
@@ -1,43 +1,10 @@
1
1
  import type { CodecControlHooks } from '@prisma-next/family-sql/control';
2
- import type {
3
- StorageColumn,
4
- StorageTable,
5
- StorageTypeInstance,
6
- } from '@prisma-next/sql-contract/types';
2
+ import type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';
7
3
  import { ifDefined } from '@prisma-next/utils/defined';
8
4
  import { escapeLiteral, quoteIdentifier } from '../sql-utils';
9
5
  import type { PostgresColumnDefault } from '../types';
10
6
  import { resolveColumnTypeMetadata } from './planner-type-resolution';
11
7
 
12
- export function buildCreateTableSql(
13
- qualifiedTableName: string,
14
- table: StorageTable,
15
- codecHooks: ReadonlyMap<string, CodecControlHooks>,
16
- storageTypes: Record<string, StorageTypeInstance> = {},
17
- ): string {
18
- const columnDefinitions = Object.entries(table.columns).map(
19
- ([columnName, column]: [string, StorageColumn]) => {
20
- const parts = [
21
- quoteIdentifier(columnName),
22
- buildColumnTypeSql(column, codecHooks, storageTypes),
23
- buildColumnDefaultSql(column.default, column),
24
- column.nullable ? '' : 'NOT NULL',
25
- ].filter(Boolean);
26
- return parts.join(' ');
27
- },
28
- );
29
-
30
- const constraintDefinitions: string[] = [];
31
- if (table.primaryKey) {
32
- constraintDefinitions.push(
33
- `PRIMARY KEY (${table.primaryKey.columns.map(quoteIdentifier).join(', ')})`,
34
- );
35
- }
36
-
37
- const allDefinitions = [...columnDefinitions, ...constraintDefinitions];
38
- return `CREATE TABLE ${qualifiedTableName} (\n ${allDefinitions.join(',\n ')}\n)`;
39
- }
40
-
41
8
  /**
42
9
  * Pattern for safe PostgreSQL type names.
43
10
  * Allows letters, digits, underscores, spaces (for "double precision", "character varying"),
@@ -192,24 +159,3 @@ export function renderDefaultLiteral(value: unknown, column?: StorageColumn): st
192
159
  }
193
160
  return `'${escapeLiteral(json)}'`;
194
161
  }
195
-
196
- export function buildAddColumnSql(
197
- qualifiedTableName: string,
198
- columnName: string,
199
- column: StorageColumn,
200
- codecHooks: ReadonlyMap<string, CodecControlHooks>,
201
- temporaryDefault?: string | null,
202
- storageTypes: Record<string, StorageTypeInstance> = {},
203
- ): string {
204
- const typeSql = buildColumnTypeSql(column, codecHooks, storageTypes);
205
- const defaultSql =
206
- buildColumnDefaultSql(column.default, column) ||
207
- (temporaryDefault ? `DEFAULT ${temporaryDefault}` : '');
208
- const parts = [
209
- `ALTER TABLE ${qualifiedTableName}`,
210
- `ADD COLUMN ${quoteIdentifier(columnName)} ${typeSql}`,
211
- defaultSql,
212
- column.nullable ? '' : 'NOT NULL',
213
- ].filter(Boolean);
214
- return parts.join(' ');
215
- }
@@ -1,15 +1,18 @@
1
1
  import type { CodecControlHooks, SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
2
2
  import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
3
3
  import type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';
4
+ import { FunctionColumnDefault } from '@prisma-next/sql-relational-core/ast';
5
+ import { col } from '@prisma-next/sql-relational-core/contract-free';
6
+ import { ifDefined } from '@prisma-next/utils/defined';
4
7
  import {
5
8
  columnDefaultAst,
6
9
  columnExistsAst,
7
10
  columnNullabilityAst,
8
11
  } from '../../contract-free/checks';
9
- import { quoteIdentifier } from '../sql-utils';
12
+ import * as contractFreeDdl from '../../contract-free/ddl';
13
+ import { boundSchema } from './bound-schema';
10
14
  import { step } from './operations/shared';
11
- import { buildAddColumnSql } from './planner-ddl-builders';
12
- import { qualifyTableName } from './planner-sql-checks';
15
+ import { buildColumnTypeSql } from './planner-ddl-builders';
13
16
  import { buildTargetDetails, type PostgresPlanTargetDetails } from './planner-target-details';
14
17
 
15
18
  export function buildAddColumnOperationIdentity(
@@ -51,7 +54,29 @@ export async function buildAddNotNullColumnWithTemporaryDefaultOperation(options
51
54
  temporaryDefault,
52
55
  lowerer,
53
56
  } = options;
54
- const qualified = qualifyTableName(schema, tableName);
57
+
58
+ // The recipe handles NOT NULL columns that carry no contract default, so the
59
+ // temporary backfill value is the only default. It is a pre-rendered SQL
60
+ // fragment (e.g. `''`, `0`, `'{}'::jsonb`), carried verbatim as a
61
+ // `FunctionColumnDefault` so the adapter emits it as a `DEFAULT (...)` clause.
62
+ const ddlColumn = col(columnName, buildColumnTypeSql(column, codecHooks, storageTypes), {
63
+ notNull: true,
64
+ default: new FunctionColumnDefault(temporaryDefault),
65
+ });
66
+ const addColumn = await lowerer.lowerToExecuteRequest(
67
+ contractFreeDdl.alterTable({
68
+ ...ifDefined('schema', boundSchema(schema)),
69
+ table: tableName,
70
+ actions: [contractFreeDdl.addColumnAction(ddlColumn)],
71
+ }),
72
+ );
73
+ const dropTempDefault = await lowerer.lowerToExecuteRequest(
74
+ contractFreeDdl.alterTable({
75
+ ...ifDefined('schema', boundSchema(schema)),
76
+ table: tableName,
77
+ actions: [contractFreeDdl.dropDefaultAction(columnName)],
78
+ }),
79
+ );
55
80
 
56
81
  const absent = await lowerer.lowerToExecuteRequest(
57
82
  columnExistsAst({ schema, table: tableName, column: columnName }).columnAbsent(),
@@ -73,18 +98,11 @@ export async function buildAddNotNullColumnWithTemporaryDefaultOperation(options
73
98
  execute: [
74
99
  {
75
100
  description: `add column "${columnName}"`,
76
- sql: buildAddColumnSql(
77
- qualified,
78
- columnName,
79
- column,
80
- codecHooks,
81
- temporaryDefault,
82
- storageTypes,
83
- ),
101
+ sql: addColumn.sql,
84
102
  },
85
103
  {
86
104
  description: `drop temporary default from column "${columnName}"`,
87
- sql: `ALTER TABLE ${qualified} ALTER COLUMN ${quoteIdentifier(columnName)} DROP DEFAULT`,
105
+ sql: dropTempDefault.sql,
88
106
  },
89
107
  ],
90
108
  postcheck: [
@@ -53,10 +53,10 @@ import {
53
53
  RawSqlCall,
54
54
  SetNotNullCall,
55
55
  } from './op-factory-call';
56
- import { buildAddColumnSql, buildColumnTypeSql } from './planner-ddl-builders';
56
+ import { buildColumnTypeSql } from './planner-ddl-builders';
57
57
  import { resolveIdentityValue } from './planner-identity-values';
58
58
  import { buildSchemaLookupMap, hasForeignKey, hasUniqueConstraint } from './planner-schema-lookup';
59
- import { buildExpectedFormatType, qualifyTableName } from './planner-sql-checks';
59
+ import { buildExpectedFormatType } from './planner-sql-checks';
60
60
  import { buildTargetDetails, type PostgresPlanTargetDetails } from './planner-target-details';
61
61
  import { resolveColumnTypeMetadata } from './planner-type-resolution';
62
62
 
@@ -600,20 +600,12 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
600
600
  continue;
601
601
  }
602
602
 
603
- const qualified = qualifyTableName(schemaForTable, issue.table);
604
603
  calls.push(
605
604
  new AddNotNullColumnDirectCall(
606
605
  schemaForTable,
607
606
  issue.table,
608
607
  issue.column,
609
- buildAddColumnSql(
610
- qualified,
611
- issue.column,
612
- column,
613
- mutableCodecHooks,
614
- undefined,
615
- mutableStorageTypes,
616
- ),
608
+ buildColumnSpec(namespaceId, issue.table, issue.column, ctx),
617
609
  ),
618
610
  );
619
611
  }
@@ -24,5 +24,11 @@ export {
24
24
  buildControlTableBootstrapQueries,
25
25
  buildSignMarkerBootstrapQueries,
26
26
  } from '../contract-free/control-bootstrap';
27
- export { addColumnAction, alterTable, createSchema, createTable } from '../contract-free/ddl';
27
+ export {
28
+ addColumnAction,
29
+ alterTable,
30
+ createSchema,
31
+ createTable,
32
+ dropDefaultAction,
33
+ } from '../contract-free/ddl';
28
34
  export { PostgresTableSource } from '../core/ast/table-source';
@@ -3,6 +3,7 @@ export {
3
3
  type AlterTableActionVisitor,
4
4
  type AnyAlterTableAction,
5
5
  type AnyPostgresDdlNode,
6
+ DropDefaultAction,
6
7
  PostgresAlterTable,
7
8
  PostgresCreatePolicy,
8
9
  PostgresCreateSchema,
@@ -1,7 +1,5 @@
1
1
  export {
2
- buildAddColumnSql,
3
2
  buildColumnDefaultSql,
4
3
  buildColumnTypeSql,
5
- buildCreateTableSql,
6
4
  renderDefaultLiteral,
7
5
  } from '../core/migrations/planner-ddl-builders';