@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.
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.d.mts +8 -3
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +2 -2
- package/dist/control.mjs +2 -2
- package/dist/data-transform-BOWpliq8.mjs.map +1 -1
- package/dist/{ddl-BlsTIBeP.mjs → ddl-D1K9soOg.mjs} +11 -4
- package/dist/{ddl-BlsTIBeP.mjs.map → ddl-D1K9soOg.mjs.map} +1 -1
- package/dist/ddl.d.mts +2 -2
- package/dist/ddl.mjs +2 -2
- package/dist/{issue-planner-CwoeupPM.mjs → issue-planner-DSQ07ihi.mjs} +5 -6
- package/dist/issue-planner-DSQ07ihi.mjs.map +1 -0
- package/dist/issue-planner.d.mts +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +2 -2
- package/dist/{nodes-YvdohJ2X.d.mts → nodes-CSkPX3rI.d.mts} +19 -3
- package/dist/nodes-CSkPX3rI.d.mts.map +1 -0
- package/dist/{nodes-DhDIIhBT.mjs → nodes-Dh__NS58.mjs} +14 -2
- package/dist/nodes-Dh__NS58.mjs.map +1 -0
- package/dist/{op-factory-call-tMksrGIq.mjs → op-factory-call-BZFhzxUH.mjs} +55 -26
- package/dist/op-factory-call-BZFhzxUH.mjs.map +1 -0
- package/dist/{op-factory-call-DXHAIisl.d.mts → op-factory-call-CQ4P40QC.d.mts} +8 -10
- package/dist/op-factory-call-CQ4P40QC.d.mts.map +1 -0
- package/dist/op-factory-call.d.mts +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/{planner-BbSZJVW7.mjs → planner-Cu8YdghT.mjs} +4 -4
- package/dist/{planner-BbSZJVW7.mjs.map → planner-Cu8YdghT.mjs.map} +1 -1
- package/dist/{planner-ddl-builders-B2wOwLqI.mjs → planner-ddl-builders-C1IsRjVl.mjs} +2 -25
- package/dist/planner-ddl-builders-C1IsRjVl.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +2 -4
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +2 -2
- package/dist/{planner-produced-postgres-migration-DfM5lBv1.d.mts → planner-produced-postgres-migration-CNTSCMl7.d.mts} +2 -2
- package/dist/{planner-produced-postgres-migration-DfM5lBv1.d.mts.map → planner-produced-postgres-migration-CNTSCMl7.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-C0FHnEsV.mjs → planner-produced-postgres-migration-SkStEa6Q.mjs} +2 -2
- package/dist/{planner-produced-postgres-migration-C0FHnEsV.mjs.map → planner-produced-postgres-migration-SkStEa6Q.mjs.map} +1 -1
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/planner.d.mts +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/{postgres-migration-BJWxullf.mjs → postgres-migration-BcDtTMs5.mjs} +2 -2
- package/dist/{postgres-migration-BJWxullf.mjs.map → postgres-migration-BcDtTMs5.mjs.map} +1 -1
- package/dist/{postgres-migration-D22a2y0l.d.mts → postgres-migration-DJNJHcdP.d.mts} +2 -2
- package/dist/{postgres-migration-D22a2y0l.d.mts.map → postgres-migration-DJNJHcdP.d.mts.map} +1 -1
- package/package.json +19 -19
- package/src/contract-free/ddl.ts +10 -1
- package/src/core/codecs.ts +1 -1
- package/src/core/ddl/nodes.ts +26 -1
- package/src/core/migrations/bound-schema.ts +10 -0
- package/src/core/migrations/op-factory-call.ts +10 -21
- package/src/core/migrations/operations/columns.ts +24 -12
- package/src/core/migrations/operations/data-transform.ts +6 -0
- package/src/core/migrations/operations/shared.ts +0 -30
- package/src/core/migrations/planner-ddl-builders.ts +1 -55
- package/src/core/migrations/planner-recipes.ts +31 -13
- package/src/core/migrations/planner-strategies.ts +3 -11
- package/src/exports/contract-free.ts +7 -1
- package/src/exports/ddl.ts +1 -0
- package/src/exports/planner-ddl-builders.ts +0 -2
- package/dist/issue-planner-CwoeupPM.mjs.map +0 -1
- package/dist/nodes-DhDIIhBT.mjs.map +0 -1
- package/dist/nodes-YvdohJ2X.d.mts.map +0 -1
- package/dist/op-factory-call-DXHAIisl.d.mts.map +0 -1
- package/dist/op-factory-call-tMksrGIq.mjs.map +0 -1
- package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +0 -1
package/src/core/ddl/nodes.ts
CHANGED
|
@@ -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
|
|
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
|
|
690
|
-
*
|
|
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
|
|
698
|
+
readonly column: DdlColumn;
|
|
704
699
|
readonly label: string;
|
|
705
700
|
|
|
706
|
-
constructor(schemaName: string, tableName: string, columnName: 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.
|
|
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
|
|
744
|
-
* pre/postchecks are lowered
|
|
745
|
-
*
|
|
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
|
|
276
|
-
*
|
|
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
|
-
|
|
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}"`,
|
|
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
|
|
12
|
+
import * as contractFreeDdl from '../../contract-free/ddl';
|
|
13
|
+
import { boundSchema } from './bound-schema';
|
|
10
14
|
import { step } from './operations/shared';
|
|
11
|
-
import {
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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';
|
package/src/exports/ddl.ts
CHANGED