@prisma-next/target-postgres 0.12.0-dev.62 → 0.12.0-dev.63
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/contract-free.d.mts +7 -2
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +2 -24
- package/dist/contract-free.mjs.map +1 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +5 -5
- package/dist/control.mjs.map +1 -1
- package/dist/ddl-77SyXgFt.mjs +30 -0
- package/dist/ddl-77SyXgFt.mjs.map +1 -0
- package/dist/ddl.d.mts +1 -1
- package/dist/ddl.mjs +1 -1
- package/dist/{issue-planner-hxJaxYHv.mjs → issue-planner-Wd6zcYtT.mjs} +15 -5
- package/dist/issue-planner-Wd6zcYtT.mjs.map +1 -0
- package/dist/issue-planner.d.mts +1 -1
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +4 -7
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +4 -3
- package/dist/migration.mjs.map +1 -1
- package/dist/{nodes-Dq0ALHOk.d.mts → nodes-779hmCfL.d.mts} +4 -2
- package/dist/nodes-779hmCfL.d.mts.map +1 -0
- package/dist/{nodes-Dm1XR7UL.mjs → nodes-DZk2JZG3.mjs} +6 -1
- package/dist/nodes-DZk2JZG3.mjs.map +1 -0
- package/dist/{op-factory-call-D47zfLOp.d.mts → op-factory-call-Bj1jPDpD.d.mts} +12 -11
- package/dist/op-factory-call-Bj1jPDpD.d.mts.map +1 -0
- package/dist/op-factory-call-q9Tpw6wc.mjs +1217 -0
- package/dist/op-factory-call-q9Tpw6wc.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/{planner-C5tapy2_.mjs → planner-BI6l-3ft.mjs} +11 -7
- package/dist/planner-BI6l-3ft.mjs.map +1 -0
- package/dist/planner-ddl-builders-DDyptTt5.mjs.map +1 -1
- package/dist/planner-ddl-builders.d.mts +3 -3
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/{planner-produced-postgres-migration-BCy50btZ.mjs → planner-produced-postgres-migration-7aaRoTZN.mjs} +8 -6
- package/dist/planner-produced-postgres-migration-7aaRoTZN.mjs.map +1 -0
- package/dist/{planner-produced-postgres-migration-CpsGhewL.d.mts → planner-produced-postgres-migration-B11fjNTx.d.mts} +4 -3
- package/dist/planner-produced-postgres-migration-B11fjNTx.d.mts.map +1 -0
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/planner.d.mts +5 -2
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/{postgres-migration--iqDr7rB.d.mts → postgres-migration-B6rKNT4N.d.mts} +23 -1
- package/dist/postgres-migration-B6rKNT4N.d.mts.map +1 -0
- package/dist/{postgres-migration-DcymFlMG.mjs → postgres-migration-C_wPoWz_.mjs} +21 -1
- package/dist/postgres-migration-C_wPoWz_.mjs.map +1 -0
- package/dist/{render-ops-Wb6SCGmj.mjs → render-ops-BpjstrKQ.mjs} +4 -3
- package/dist/{render-ops-Wb6SCGmj.mjs.map → render-ops-BpjstrKQ.mjs.map} +1 -1
- package/dist/render-ops.d.mts +2 -1
- package/dist/render-ops.d.mts.map +1 -1
- package/dist/render-ops.mjs +1 -1
- package/dist/{render-typescript-BShM6uyS.mjs → render-typescript-KMgosran.mjs} +5 -2
- package/dist/render-typescript-KMgosran.mjs.map +1 -0
- package/dist/render-typescript.mjs +1 -1
- package/dist/{shared-C3gXQyxj.d.mts → shared-C6QxSU0W.d.mts} +4 -4
- package/dist/{shared-C3gXQyxj.d.mts.map → shared-C6QxSU0W.d.mts.map} +1 -1
- package/package.json +17 -17
- package/src/contract-free/ddl.ts +6 -1
- package/src/core/ddl/nodes.ts +14 -1
- package/src/core/migrations/issue-planner.ts +27 -10
- package/src/core/migrations/op-factory-call.ts +197 -42
- package/src/core/migrations/operations/constraints.ts +1 -9
- package/src/core/migrations/operations/dependencies.ts +0 -17
- package/src/core/migrations/operations/shared.ts +3 -3
- package/src/core/migrations/operations/tables.ts +1 -39
- package/src/core/migrations/planner-ddl-builders.ts +4 -4
- package/src/core/migrations/planner-produced-postgres-migration.ts +11 -6
- package/src/core/migrations/planner-strategies.ts +1 -0
- package/src/core/migrations/planner.ts +11 -2
- package/src/core/migrations/postgres-migration.ts +41 -0
- package/src/core/migrations/render-ops.ts +7 -2
- package/src/core/migrations/render-typescript.ts +5 -1
- package/src/exports/control.ts +6 -8
- package/src/exports/migration.ts +9 -2
- package/dist/issue-planner-hxJaxYHv.mjs.map +0 -1
- package/dist/nodes-Dm1XR7UL.mjs.map +0 -1
- package/dist/nodes-Dq0ALHOk.d.mts.map +0 -1
- package/dist/op-factory-call-D47zfLOp.d.mts.map +0 -1
- package/dist/op-factory-call-G9G02kxC.mjs +0 -625
- package/dist/op-factory-call-G9G02kxC.mjs.map +0 -1
- package/dist/planner-C5tapy2_.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-BCy50btZ.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-CpsGhewL.d.mts.map +0 -1
- package/dist/postgres-migration--iqDr7rB.d.mts.map +0 -1
- package/dist/postgres-migration-DcymFlMG.mjs.map +0 -1
- package/dist/render-typescript-BShM6uyS.mjs.map +0 -1
- package/dist/tables-OLIoVjxf.mjs +0 -516
- package/dist/tables-OLIoVjxf.mjs.map +0 -1
|
@@ -22,11 +22,23 @@
|
|
|
22
22
|
|
|
23
23
|
import { errorUnfilledPlaceholder } from '@prisma-next/errors/migration';
|
|
24
24
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
25
|
+
import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
|
|
25
26
|
import type {
|
|
26
27
|
OpFactoryCall as FrameworkOpFactoryCall,
|
|
27
28
|
MigrationOperationClass,
|
|
28
29
|
} from '@prisma-next/framework-components/control';
|
|
30
|
+
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
31
|
+
import type {
|
|
32
|
+
AnyDdlColumnDefault,
|
|
33
|
+
DdlColumn,
|
|
34
|
+
DdlTableConstraint,
|
|
35
|
+
} from '@prisma-next/sql-relational-core/ast';
|
|
36
|
+
import { FunctionColumnDefault, LiteralColumnDefault } from '@prisma-next/sql-relational-core/ast';
|
|
29
37
|
import { type ImportRequirement, jsonToTsSource, TsExpression } from '@prisma-next/ts-render';
|
|
38
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
39
|
+
import * as contractFreeDdl from '../../contract-free/ddl';
|
|
40
|
+
import { escapeLiteral, quoteIdentifier } from '../sql-utils';
|
|
41
|
+
import type { PostgresColumnDefault } from '../types';
|
|
30
42
|
import {
|
|
31
43
|
addColumn,
|
|
32
44
|
alterColumnType,
|
|
@@ -37,22 +49,25 @@ import {
|
|
|
37
49
|
setNotNull,
|
|
38
50
|
} from './operations/columns';
|
|
39
51
|
import { addForeignKey, addPrimaryKey, addUnique, dropConstraint } from './operations/constraints';
|
|
40
|
-
import { createExtension
|
|
52
|
+
import { createExtension } from './operations/dependencies';
|
|
41
53
|
import { addEnumValues, createEnumType, dropEnumType, renameType } from './operations/enums';
|
|
42
54
|
import { createIndex, dropIndex } from './operations/indexes';
|
|
43
55
|
import type { ColumnSpec, ForeignKeySpec } from './operations/shared';
|
|
44
|
-
import {
|
|
56
|
+
import { step, targetDetails } from './operations/shared';
|
|
57
|
+
import { dropTable } from './operations/tables';
|
|
58
|
+
import { toRegclassLiteral } from './planner-sql-checks';
|
|
45
59
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
46
60
|
|
|
47
61
|
type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
48
62
|
|
|
49
63
|
const TARGET_MIGRATION_MODULE = '@prisma-next/postgres/migration';
|
|
64
|
+
const RELATIONAL_CORE_CONTRACT_FREE = '@prisma-next/sql-relational-core/contract-free';
|
|
50
65
|
|
|
51
66
|
abstract class PostgresOpFactoryCallNode extends TsExpression implements FrameworkOpFactoryCall {
|
|
52
67
|
abstract readonly factoryName: string;
|
|
53
68
|
abstract readonly operationClass: MigrationOperationClass;
|
|
54
69
|
abstract readonly label: string;
|
|
55
|
-
abstract toOp(): Op;
|
|
70
|
+
abstract toOp(lowerer?: Lowerer): Op;
|
|
56
71
|
|
|
57
72
|
importRequirements(): readonly ImportRequirement[] {
|
|
58
73
|
return [{ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: this.factoryName }];
|
|
@@ -67,8 +82,93 @@ abstract class PostgresOpFactoryCallNode extends TsExpression implements Framewo
|
|
|
67
82
|
// Table
|
|
68
83
|
// ============================================================================
|
|
69
84
|
|
|
70
|
-
export
|
|
71
|
-
|
|
85
|
+
export function postgresDefaultToDdlColumnDefault(
|
|
86
|
+
columnDefault: PostgresColumnDefault | undefined,
|
|
87
|
+
): DdlColumn['default'] {
|
|
88
|
+
if (!columnDefault) return undefined;
|
|
89
|
+
switch (columnDefault.kind) {
|
|
90
|
+
case 'literal':
|
|
91
|
+
return new LiteralColumnDefault(columnDefault.value);
|
|
92
|
+
case 'function':
|
|
93
|
+
if (columnDefault.expression === 'autoincrement()') return undefined;
|
|
94
|
+
return new FunctionColumnDefault(columnDefault.expression);
|
|
95
|
+
case 'sequence':
|
|
96
|
+
return new FunctionColumnDefault(
|
|
97
|
+
`nextval('${escapeLiteral(quoteIdentifier(columnDefault.name))}'::regclass)`,
|
|
98
|
+
);
|
|
99
|
+
default: {
|
|
100
|
+
const exhaustive: never = columnDefault;
|
|
101
|
+
throw new Error(
|
|
102
|
+
`postgresDefaultToDdlColumnDefault: unhandled kind "${blindCast<{ kind: string }, 'exhaustiveness: surface the unhandled default kind'>(exhaustive).kind}"`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// TypeScript rendering helpers for DdlColumn / DdlTableConstraint
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
function renderDdlColumnDefault(def: AnyDdlColumnDefault | undefined): string {
|
|
113
|
+
if (!def) return '';
|
|
114
|
+
if (def.kind === 'literal') {
|
|
115
|
+
return `lit(${jsonToTsSource(def.value)})`;
|
|
116
|
+
}
|
|
117
|
+
return `fn(${jsonToTsSource(def.expression)})`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function renderDdlColumnAsTsCall(col: DdlColumn): string {
|
|
121
|
+
const opts: string[] = [];
|
|
122
|
+
if (col.notNull) opts.push('notNull: true');
|
|
123
|
+
if (col.primaryKey) opts.push('primaryKey: true');
|
|
124
|
+
if (col.default) opts.push(`default: ${renderDdlColumnDefault(col.default)}`);
|
|
125
|
+
const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
|
|
126
|
+
return `col(${jsonToTsSource(col.name)}, ${jsonToTsSource(col.type)}${optsStr})`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function renderDdlConstraintAsTsCall(constraint: DdlTableConstraint): string {
|
|
130
|
+
switch (constraint.kind) {
|
|
131
|
+
case 'primary-key': {
|
|
132
|
+
const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
|
|
133
|
+
return `primaryKey(${jsonToTsSource(constraint.columns)}${nameOpt})`;
|
|
134
|
+
}
|
|
135
|
+
case 'foreign-key': {
|
|
136
|
+
const opts: string[] = [];
|
|
137
|
+
if (constraint.name) opts.push(`name: ${jsonToTsSource(constraint.name)}`);
|
|
138
|
+
if (constraint.onDelete) opts.push(`onDelete: ${jsonToTsSource(constraint.onDelete)}`);
|
|
139
|
+
if (constraint.onUpdate) opts.push(`onUpdate: ${jsonToTsSource(constraint.onUpdate)}`);
|
|
140
|
+
const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
|
|
141
|
+
return `foreignKey(${jsonToTsSource(constraint.columns)}, ${jsonToTsSource(constraint.refTable)}, ${jsonToTsSource(constraint.refColumns)}${optsStr})`;
|
|
142
|
+
}
|
|
143
|
+
case 'unique': {
|
|
144
|
+
const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
|
|
145
|
+
return `unique(${jsonToTsSource(constraint.columns)}${nameOpt})`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function needsColOrConstraintImport(columns: readonly DdlColumn[]): boolean {
|
|
151
|
+
return columns.length > 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function constraintImportSymbols(constraints: readonly DdlTableConstraint[] | undefined): string[] {
|
|
155
|
+
if (!constraints || constraints.length === 0) return [];
|
|
156
|
+
const symbols = new Set<string>();
|
|
157
|
+
for (const c of constraints) {
|
|
158
|
+
if (c.kind === 'primary-key') symbols.add('primaryKey');
|
|
159
|
+
else if (c.kind === 'foreign-key') symbols.add('foreignKey');
|
|
160
|
+
else if (c.kind === 'unique') symbols.add('unique');
|
|
161
|
+
}
|
|
162
|
+
return [...symbols];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function defaultImportSymbols(columns: readonly DdlColumn[]): string[] {
|
|
166
|
+
const symbols = new Set<string>();
|
|
167
|
+
for (const col of columns) {
|
|
168
|
+
if (col.default?.kind === 'literal') symbols.add('lit');
|
|
169
|
+
else if (col.default?.kind === 'function') symbols.add('fn');
|
|
170
|
+
}
|
|
171
|
+
return [...symbols];
|
|
72
172
|
}
|
|
73
173
|
|
|
74
174
|
export class CreateTableCall extends PostgresOpFactoryCallNode {
|
|
@@ -76,37 +176,91 @@ export class CreateTableCall extends PostgresOpFactoryCallNode {
|
|
|
76
176
|
readonly operationClass = 'additive' as const;
|
|
77
177
|
readonly schemaName: string;
|
|
78
178
|
readonly tableName: string;
|
|
79
|
-
readonly columns: readonly
|
|
80
|
-
readonly
|
|
179
|
+
readonly columns: readonly DdlColumn[];
|
|
180
|
+
readonly constraints: readonly DdlTableConstraint[] | undefined;
|
|
81
181
|
readonly label: string;
|
|
82
182
|
|
|
83
183
|
constructor(
|
|
84
184
|
schemaName: string,
|
|
85
185
|
tableName: string,
|
|
86
|
-
columns: readonly
|
|
87
|
-
|
|
186
|
+
columns: readonly DdlColumn[],
|
|
187
|
+
constraints?: readonly DdlTableConstraint[],
|
|
88
188
|
) {
|
|
89
189
|
super();
|
|
90
190
|
this.schemaName = schemaName;
|
|
91
191
|
this.tableName = tableName;
|
|
92
|
-
this.columns = columns;
|
|
93
|
-
this.
|
|
192
|
+
this.columns = Object.freeze([...columns]);
|
|
193
|
+
this.constraints = constraints ? Object.freeze([...constraints]) : undefined;
|
|
94
194
|
this.label = `Create table "${tableName}"`;
|
|
95
195
|
this.freeze();
|
|
96
196
|
}
|
|
97
197
|
|
|
98
|
-
toOp(): Op {
|
|
99
|
-
|
|
198
|
+
toOp(lowerer?: Lowerer): Op {
|
|
199
|
+
if (lowerer === undefined) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`CreateTableCall.toOp: a DDL lowerer is required on the Postgres planner path (table "${this.tableName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const ddlNode = contractFreeDdl.createTable({
|
|
205
|
+
...(this.schemaName !== UNBOUND_NAMESPACE_ID ? { schema: this.schemaName } : {}),
|
|
206
|
+
table: this.tableName,
|
|
207
|
+
columns: this.columns,
|
|
208
|
+
...(this.constraints ? { constraints: this.constraints } : {}),
|
|
209
|
+
});
|
|
210
|
+
const { sql } = lowerer.lower(ddlNode, { contract: {} });
|
|
211
|
+
const schemaName = this.schemaName;
|
|
212
|
+
const tableName = this.tableName;
|
|
213
|
+
return {
|
|
214
|
+
id: `table.${tableName}`,
|
|
215
|
+
label: `Create table "${tableName}"`,
|
|
216
|
+
summary: `Creates table "${tableName}"`,
|
|
217
|
+
operationClass: 'additive',
|
|
218
|
+
target: targetDetails('table', tableName, schemaName),
|
|
219
|
+
precheck: [
|
|
220
|
+
step(
|
|
221
|
+
`ensure table "${tableName}" does not exist`,
|
|
222
|
+
`SELECT to_regclass(${toRegclassLiteral(schemaName, tableName)}) IS NULL`,
|
|
223
|
+
),
|
|
224
|
+
],
|
|
225
|
+
execute: [step(`create table "${tableName}"`, sql)],
|
|
226
|
+
postcheck: [
|
|
227
|
+
step(
|
|
228
|
+
`verify table "${tableName}" exists`,
|
|
229
|
+
`SELECT to_regclass(${toRegclassLiteral(schemaName, tableName)}) IS NOT NULL`,
|
|
230
|
+
),
|
|
231
|
+
],
|
|
232
|
+
};
|
|
100
233
|
}
|
|
101
234
|
|
|
102
235
|
renderTypeScript(): string {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
236
|
+
const columnsList = this.columns.map(renderDdlColumnAsTsCall).join(', ');
|
|
237
|
+
const constraintsList = this.constraints
|
|
238
|
+
? this.constraints.map(renderDdlConstraintAsTsCall).join(', ')
|
|
239
|
+
: undefined;
|
|
240
|
+
|
|
241
|
+
const opts: string[] = [];
|
|
242
|
+
if (this.schemaName !== UNBOUND_NAMESPACE_ID) {
|
|
243
|
+
opts.push(`schema: ${jsonToTsSource(this.schemaName)}`);
|
|
244
|
+
}
|
|
245
|
+
opts.push(`table: ${jsonToTsSource(this.tableName)}`);
|
|
246
|
+
opts.push(`columns: [${columnsList}]`);
|
|
247
|
+
if (constraintsList) opts.push(`constraints: [${constraintsList}]`);
|
|
248
|
+
|
|
249
|
+
return `this.createTable({ ${opts.join(', ')} })`;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
253
|
+
const req: ImportRequirement[] = [];
|
|
254
|
+
if (needsColOrConstraintImport(this.columns)) {
|
|
255
|
+
req.push({ moduleSpecifier: RELATIONAL_CORE_CONTRACT_FREE, symbol: 'col' });
|
|
256
|
+
for (const sym of defaultImportSymbols(this.columns)) {
|
|
257
|
+
req.push({ moduleSpecifier: RELATIONAL_CORE_CONTRACT_FREE, symbol: sym });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const sym of constraintImportSymbols(this.constraints)) {
|
|
261
|
+
req.push({ moduleSpecifier: RELATIONAL_CORE_CONTRACT_FREE, symbol: sym });
|
|
262
|
+
}
|
|
263
|
+
return req;
|
|
110
264
|
}
|
|
111
265
|
}
|
|
112
266
|
|
|
@@ -776,12 +930,32 @@ export class CreateSchemaCall extends PostgresOpFactoryCallNode {
|
|
|
776
930
|
this.freeze();
|
|
777
931
|
}
|
|
778
932
|
|
|
779
|
-
toOp(): Op {
|
|
780
|
-
|
|
933
|
+
toOp(lowerer?: Lowerer): Op {
|
|
934
|
+
if (lowerer === undefined) {
|
|
935
|
+
throw new Error(
|
|
936
|
+
`CreateSchemaCall.toOp: a DDL lowerer is required on the Postgres planner path (schema "${this.schemaName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
const ddlNode = contractFreeDdl.createSchema({ schema: this.schemaName, ifNotExists: true });
|
|
940
|
+
const { sql } = lowerer.lower(ddlNode, { contract: {} });
|
|
941
|
+
const schemaName = this.schemaName;
|
|
942
|
+
return {
|
|
943
|
+
id: `schema.${schemaName}`,
|
|
944
|
+
label: `Create schema "${schemaName}"`,
|
|
945
|
+
operationClass: 'additive',
|
|
946
|
+
target: { id: 'postgres' },
|
|
947
|
+
precheck: [],
|
|
948
|
+
execute: [step(`Create schema "${schemaName}"`, sql)],
|
|
949
|
+
postcheck: [],
|
|
950
|
+
};
|
|
781
951
|
}
|
|
782
952
|
|
|
783
953
|
renderTypeScript(): string {
|
|
784
|
-
return `createSchema(${jsonToTsSource(this.schemaName)})`;
|
|
954
|
+
return `this.createSchema({ schema: ${jsonToTsSource(this.schemaName)} })`;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
958
|
+
return [];
|
|
785
959
|
}
|
|
786
960
|
}
|
|
787
961
|
|
|
@@ -867,22 +1041,3 @@ export type PostgresOpFactoryCall =
|
|
|
867
1041
|
| CreateExtensionCall
|
|
868
1042
|
| CreateSchemaCall
|
|
869
1043
|
| DataTransformCall;
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Stable identity key for reconciliation-level dedup.
|
|
873
|
-
*
|
|
874
|
-
* Two calls whose runtime ops would share the same `id` return the same
|
|
875
|
-
* key, so a `Set<string>` can collapse them before they're emitted. The
|
|
876
|
-
* current implementation delegates to `toOp().id`, which is the
|
|
877
|
-
* authoritative identity; isolating dedup behind this helper lets a future
|
|
878
|
-
* pass replace it with an allocation-free computation directly from the
|
|
879
|
-
* call's fields without touching call sites.
|
|
880
|
-
*
|
|
881
|
-
* `DataTransformCall` intentionally has no sensible identity today — it
|
|
882
|
-
* throws `PN-MIG-2001` on `toOp()`. Reconciliation never produces one; the
|
|
883
|
-
* helper is unspecified for that variant and only meant for
|
|
884
|
-
* reconciliation-emitted calls.
|
|
885
|
-
*/
|
|
886
|
-
export function identityKeyFor(call: PostgresOpFactoryCall): string {
|
|
887
|
-
return call.toOp().id;
|
|
888
|
-
}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { REFERENTIAL_ACTION_SQL } from '@prisma-next/sql-contract/referential-action-sql';
|
|
2
2
|
import { quoteIdentifier } from '../../sql-utils';
|
|
3
3
|
import { constraintExistsCheck, qualifyTableName } from '../planner-sql-checks';
|
|
4
4
|
import { type ForeignKeySpec, type Op, step, targetDetails } from './shared';
|
|
5
5
|
|
|
6
|
-
const REFERENTIAL_ACTION_SQL: Record<ReferentialAction, string> = {
|
|
7
|
-
noAction: 'NO ACTION',
|
|
8
|
-
restrict: 'RESTRICT',
|
|
9
|
-
cascade: 'CASCADE',
|
|
10
|
-
setNull: 'SET NULL',
|
|
11
|
-
setDefault: 'SET DEFAULT',
|
|
12
|
-
};
|
|
13
|
-
|
|
14
6
|
function renderForeignKeySql(schemaName: string, tableName: string, fk: ForeignKeySpec): string {
|
|
15
7
|
let sql = `ALTER TABLE ${qualifyTableName(schemaName, tableName)}
|
|
16
8
|
ADD CONSTRAINT ${quoteIdentifier(fk.name)}
|
|
@@ -69,20 +69,3 @@ export function installExtension(options: {
|
|
|
69
69
|
],
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
export function createSchema(schemaName: string): Op {
|
|
74
|
-
return {
|
|
75
|
-
id: `schema.${schemaName}`,
|
|
76
|
-
label: `Create schema "${schemaName}"`,
|
|
77
|
-
operationClass: 'additive',
|
|
78
|
-
target: { id: 'postgres' },
|
|
79
|
-
precheck: [],
|
|
80
|
-
execute: [
|
|
81
|
-
step(
|
|
82
|
-
`Create schema "${schemaName}"`,
|
|
83
|
-
`CREATE SCHEMA IF NOT EXISTS ${quoteIdentifier(schemaName)}`,
|
|
84
|
-
),
|
|
85
|
-
],
|
|
86
|
-
postcheck: [],
|
|
87
|
-
};
|
|
88
|
-
}
|
|
@@ -7,9 +7,9 @@ import type { OperationClass, PostgresPlanTargetDetails } from '../planner-targe
|
|
|
7
7
|
export type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Literal-args shape for a column definition consumed by `
|
|
11
|
-
*
|
|
12
|
-
* already happened in the wrapper.
|
|
10
|
+
* Literal-args shape for a column definition consumed by `addColumn` and
|
|
11
|
+
* similar operations. Fully materialized: codec expansion and default
|
|
12
|
+
* rendering have already happened in the wrapper.
|
|
13
13
|
*
|
|
14
14
|
* - `typeSql` is the column's DDL type string (e.g. `"integer"`, `"SERIAL"`,
|
|
15
15
|
* `"varchar(100)"`), already produced by `buildColumnTypeSql` in the
|
|
@@ -1,43 +1,5 @@
|
|
|
1
|
-
import { quoteIdentifier } from '../../sql-utils';
|
|
2
1
|
import { qualifyTableName, toRegclassLiteral } from '../planner-sql-checks';
|
|
3
|
-
import { type
|
|
4
|
-
|
|
5
|
-
export function createTable(
|
|
6
|
-
schemaName: string,
|
|
7
|
-
tableName: string,
|
|
8
|
-
columns: ReadonlyArray<ColumnSpec>,
|
|
9
|
-
primaryKey?: { readonly columns: readonly string[] },
|
|
10
|
-
): Op {
|
|
11
|
-
const qualified = qualifyTableName(schemaName, tableName);
|
|
12
|
-
const columnDefs = columns.map(renderColumnDefinition);
|
|
13
|
-
const constraintDefs: string[] = [];
|
|
14
|
-
if (primaryKey) {
|
|
15
|
-
constraintDefs.push(`PRIMARY KEY (${primaryKey.columns.map(quoteIdentifier).join(', ')})`);
|
|
16
|
-
}
|
|
17
|
-
const allDefs = [...columnDefs, ...constraintDefs];
|
|
18
|
-
const createSql = `CREATE TABLE ${qualified} (\n ${allDefs.join(',\n ')}\n)`;
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
id: `table.${tableName}`,
|
|
22
|
-
label: `Create table "${tableName}"`,
|
|
23
|
-
summary: `Creates table "${tableName}"`,
|
|
24
|
-
operationClass: 'additive',
|
|
25
|
-
target: targetDetails('table', tableName, schemaName),
|
|
26
|
-
precheck: [
|
|
27
|
-
step(
|
|
28
|
-
`ensure table "${tableName}" does not exist`,
|
|
29
|
-
`SELECT to_regclass(${toRegclassLiteral(schemaName, tableName)}) IS NULL`,
|
|
30
|
-
),
|
|
31
|
-
],
|
|
32
|
-
execute: [step(`create table "${tableName}"`, createSql)],
|
|
33
|
-
postcheck: [
|
|
34
|
-
step(
|
|
35
|
-
`verify table "${tableName}" exists`,
|
|
36
|
-
`SELECT to_regclass(${toRegclassLiteral(schemaName, tableName)}) IS NOT NULL`,
|
|
37
|
-
),
|
|
38
|
-
],
|
|
39
|
-
};
|
|
40
|
-
}
|
|
2
|
+
import { type Op, step, targetDetails } from './shared';
|
|
41
3
|
|
|
42
4
|
export function dropTable(schemaName: string, tableName: string): Op {
|
|
43
5
|
const qualified = qualifyTableName(schemaName, tableName);
|
|
@@ -12,7 +12,7 @@ import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
|
12
12
|
export function buildCreateTableSql(
|
|
13
13
|
qualifiedTableName: string,
|
|
14
14
|
table: StorageTable,
|
|
15
|
-
codecHooks:
|
|
15
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
16
16
|
storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {},
|
|
17
17
|
): string {
|
|
18
18
|
const columnDefinitions = Object.entries(table.columns).map(
|
|
@@ -77,7 +77,7 @@ function assertSafeDefaultExpression(expression: string): void {
|
|
|
77
77
|
*/
|
|
78
78
|
export function buildColumnTypeSql(
|
|
79
79
|
column: StorageColumn,
|
|
80
|
-
codecHooks:
|
|
80
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
81
81
|
storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {},
|
|
82
82
|
allowPseudoTypes = true,
|
|
83
83
|
): string {
|
|
@@ -113,7 +113,7 @@ export function buildColumnTypeSql(
|
|
|
113
113
|
|
|
114
114
|
function expandParameterizedTypeSql(
|
|
115
115
|
column: Pick<StorageColumn, 'nativeType' | 'codecId' | 'typeParams'>,
|
|
116
|
-
codecHooks:
|
|
116
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
117
117
|
): string | null {
|
|
118
118
|
if (!column.typeParams) {
|
|
119
119
|
return null;
|
|
@@ -197,7 +197,7 @@ export function buildAddColumnSql(
|
|
|
197
197
|
qualifiedTableName: string,
|
|
198
198
|
columnName: string,
|
|
199
199
|
column: StorageColumn,
|
|
200
|
-
codecHooks:
|
|
200
|
+
codecHooks: ReadonlyMap<string, CodecControlHooks>,
|
|
201
201
|
temporaryDefault?: string | null,
|
|
202
202
|
storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {},
|
|
203
203
|
): string {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
27
|
+
import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
|
|
27
28
|
import type {
|
|
28
29
|
MigrationPlanWithAuthoringSurface,
|
|
29
30
|
OpFactoryCall,
|
|
@@ -43,16 +44,23 @@ export class TypeScriptRenderablePostgresMigration
|
|
|
43
44
|
readonly #calls: readonly OpFactoryCall[];
|
|
44
45
|
readonly #meta: MigrationMeta;
|
|
45
46
|
readonly #spaceId: string;
|
|
47
|
+
readonly #lowerer: Lowerer | undefined;
|
|
46
48
|
|
|
47
|
-
constructor(
|
|
49
|
+
constructor(
|
|
50
|
+
calls: readonly OpFactoryCall[],
|
|
51
|
+
meta: MigrationMeta,
|
|
52
|
+
spaceId: string,
|
|
53
|
+
lowerer?: Lowerer,
|
|
54
|
+
) {
|
|
48
55
|
super();
|
|
49
56
|
this.#calls = calls;
|
|
50
57
|
this.#meta = meta;
|
|
51
58
|
this.#spaceId = spaceId;
|
|
59
|
+
this.#lowerer = lowerer;
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
override get operations(): readonly Op[] {
|
|
55
|
-
return renderOps(this.#calls);
|
|
63
|
+
return renderOps(this.#calls, this.#lowerer);
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
override describe(): MigrationMeta {
|
|
@@ -69,9 +77,6 @@ export class TypeScriptRenderablePostgresMigration
|
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
renderTypeScript(): string {
|
|
72
|
-
return renderCallsToTypeScript(this.#calls, {
|
|
73
|
-
from: this.#meta.from,
|
|
74
|
-
to: this.#meta.to,
|
|
75
|
-
});
|
|
80
|
+
return renderCallsToTypeScript(this.#calls, { from: this.#meta.from, to: this.#meta.to });
|
|
76
81
|
}
|
|
77
82
|
}
|
|
@@ -253,6 +253,7 @@ function buildColumnSpec(
|
|
|
253
253
|
name: column,
|
|
254
254
|
typeSql: buildColumnTypeSql(col, mutableHooks, mutableTypes),
|
|
255
255
|
defaultSql: buildColumnDefaultSql(col.default, col),
|
|
256
|
+
columnDefault: col.default,
|
|
256
257
|
nullable: overrides?.nullable ?? col.nullable,
|
|
257
258
|
};
|
|
258
259
|
}
|
|
@@ -12,6 +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
16
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
16
17
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
17
18
|
import type {
|
|
@@ -51,8 +52,8 @@ type VerifySqlSchemaOptionsWithComponents = Parameters<typeof verifySqlSchema>[0
|
|
|
51
52
|
readonly frameworkComponents: PlannerFrameworkComponents;
|
|
52
53
|
};
|
|
53
54
|
|
|
54
|
-
export function createPostgresMigrationPlanner(): PostgresMigrationPlanner {
|
|
55
|
-
return new PostgresMigrationPlanner();
|
|
55
|
+
export function createPostgresMigrationPlanner(lowerer: Lowerer): PostgresMigrationPlanner {
|
|
56
|
+
return new PostgresMigrationPlanner(lowerer);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/**
|
|
@@ -87,6 +88,12 @@ export type PostgresPlanResult =
|
|
|
87
88
|
* authoring surface.
|
|
88
89
|
*/
|
|
89
90
|
export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgres'> {
|
|
91
|
+
readonly #lowerer: Lowerer | undefined;
|
|
92
|
+
|
|
93
|
+
constructor(lowerer?: Lowerer) {
|
|
94
|
+
this.#lowerer = lowerer;
|
|
95
|
+
}
|
|
96
|
+
|
|
90
97
|
plan(options: {
|
|
91
98
|
readonly contract: unknown;
|
|
92
99
|
readonly schema: unknown;
|
|
@@ -129,6 +136,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
129
136
|
to: context.toHash,
|
|
130
137
|
},
|
|
131
138
|
spaceId,
|
|
139
|
+
this.#lowerer,
|
|
132
140
|
);
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -225,6 +233,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
225
233
|
to: options.contract.storage.storageHash,
|
|
226
234
|
},
|
|
227
235
|
options.spaceId,
|
|
236
|
+
this.#lowerer,
|
|
228
237
|
),
|
|
229
238
|
...(warnings.length > 0 ? { warnings: Object.freeze(warnings) } : {}),
|
|
230
239
|
});
|
|
@@ -3,8 +3,11 @@ import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control'
|
|
|
3
3
|
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
4
4
|
import { Migration as SqlMigration } from '@prisma-next/family-sql/migration';
|
|
5
5
|
import type { ControlStack } from '@prisma-next/framework-components/control';
|
|
6
|
+
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
6
7
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
8
|
+
import type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
|
|
7
9
|
import { errorPostgresMigrationStackMissing } from '../errors';
|
|
10
|
+
import { CreateSchemaCall, CreateTableCall } from './op-factory-call';
|
|
8
11
|
import { type DataTransformOptions, dataTransform } from './operations/data-transform';
|
|
9
12
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
10
13
|
|
|
@@ -67,4 +70,42 @@ export abstract class PostgresMigration extends SqlMigration<
|
|
|
67
70
|
}
|
|
68
71
|
return dataTransform(contract, name, options, this.controlAdapter);
|
|
69
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Emit a `CREATE TABLE` migration operation. Builds a typed DDL node from
|
|
76
|
+
* the supplied options and lowers it through the stored control adapter.
|
|
77
|
+
* Throws if no adapter is present (i.e. migration instantiated without a stack).
|
|
78
|
+
*/
|
|
79
|
+
protected createTable(options: {
|
|
80
|
+
readonly schema?: string;
|
|
81
|
+
readonly table: string;
|
|
82
|
+
readonly ifNotExists?: boolean;
|
|
83
|
+
readonly columns: readonly DdlColumn[];
|
|
84
|
+
readonly constraints?: readonly DdlTableConstraint[];
|
|
85
|
+
}): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
|
|
86
|
+
if (!this.controlAdapter) {
|
|
87
|
+
throw errorPostgresMigrationStackMissing();
|
|
88
|
+
}
|
|
89
|
+
return new CreateTableCall(
|
|
90
|
+
options.schema ?? UNBOUND_NAMESPACE_ID,
|
|
91
|
+
options.table,
|
|
92
|
+
options.columns,
|
|
93
|
+
options.constraints,
|
|
94
|
+
).toOp(this.controlAdapter);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Emit a `CREATE SCHEMA` migration operation. Builds a typed DDL node from
|
|
99
|
+
* the supplied options and lowers it through the stored control adapter.
|
|
100
|
+
* Throws if no adapter is present (i.e. migration instantiated without a stack).
|
|
101
|
+
*/
|
|
102
|
+
protected createSchema(options: {
|
|
103
|
+
readonly schema: string;
|
|
104
|
+
readonly ifNotExists?: boolean;
|
|
105
|
+
}): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
|
|
106
|
+
if (!this.controlAdapter) {
|
|
107
|
+
throw errorPostgresMigrationStackMissing();
|
|
108
|
+
}
|
|
109
|
+
return new CreateSchemaCall(options.schema).toOp(this.controlAdapter);
|
|
110
|
+
}
|
|
70
111
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
|
+
import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
3
|
import type { OpFactoryCall } from '@prisma-next/framework-components/control';
|
|
4
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
3
5
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
4
6
|
|
|
5
7
|
type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
@@ -23,9 +25,12 @@ function assertPostgresOp(
|
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
export function renderOps(calls: readonly OpFactoryCall[]): Op[] {
|
|
28
|
+
export function renderOps(calls: readonly OpFactoryCall[], lowerer?: Lowerer): Op[] {
|
|
27
29
|
return calls.map((c) => {
|
|
28
|
-
const op =
|
|
30
|
+
const op = blindCast<
|
|
31
|
+
{ toOp(lowerer?: Lowerer): ReturnType<OpFactoryCall['toOp']> },
|
|
32
|
+
'PG OpFactoryCall.toOp accepts an optional Lowerer; the framework interface omits it because not all targets need a lowerer — the PG target overrides with this extended signature'
|
|
33
|
+
>(c).toOp(lowerer);
|
|
29
34
|
assertPostgresOp(op, c.factoryName);
|
|
30
35
|
return op;
|
|
31
36
|
});
|
|
@@ -46,7 +46,7 @@ export function renderCallsToTypeScript(
|
|
|
46
46
|
meta: RenderMigrationMeta,
|
|
47
47
|
): string {
|
|
48
48
|
const imports = buildImports(calls);
|
|
49
|
-
const operationsBody = calls.map((c) => c
|
|
49
|
+
const operationsBody = calls.map((c) => renderCall(c)).join(',\n');
|
|
50
50
|
|
|
51
51
|
return [
|
|
52
52
|
shebangLineFor(detectScaffoldRuntime()),
|
|
@@ -66,6 +66,10 @@ export function renderCallsToTypeScript(
|
|
|
66
66
|
].join('\n');
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
function renderCall(call: OpFactoryCall): string {
|
|
70
|
+
return call.renderTypeScript();
|
|
71
|
+
}
|
|
72
|
+
|
|
69
73
|
function buildImports(calls: ReadonlyArray<OpFactoryCall>): string {
|
|
70
74
|
const requirements: ImportRequirement[] = [...BASE_IMPORTS];
|
|
71
75
|
for (const call of calls) {
|