@prisma-next/target-sqlite 0.13.0-dev.2 → 0.13.0-dev.20
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.mjs +1 -20
- package/dist/contract-free.mjs.map +1 -1
- package/dist/control.mjs +17 -19
- package/dist/control.mjs.map +1 -1
- package/dist/ddl-CH8V_qcd.mjs +23 -0
- package/dist/ddl-CH8V_qcd.mjs.map +1 -0
- package/dist/migration.d.mts +3 -3
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +4 -3
- package/dist/migration.mjs.map +1 -1
- package/dist/{tables-CjB7vXCr.mjs → op-factory-call-BX69rHxs.mjs} +375 -17
- package/dist/op-factory-call-BX69rHxs.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +16 -12
- package/dist/op-factory-call.d.mts.map +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/{planner-DSNDwQy9.mjs → planner-iYg56pzJ.mjs} +78 -11
- package/dist/planner-iYg56pzJ.mjs.map +1 -0
- package/dist/{planner-produced-sqlite-migration-C1yqJAiM.d.mts → planner-produced-sqlite-migration-BWpnDmhM.d.mts} +5 -4
- package/dist/planner-produced-sqlite-migration-BWpnDmhM.d.mts.map +1 -0
- package/dist/{planner-produced-sqlite-migration-DowV_vHw.mjs → planner-produced-sqlite-migration-DwQSUgSk.mjs} +9 -5
- package/dist/planner-produced-sqlite-migration-DwQSUgSk.mjs.map +1 -0
- package/dist/planner-produced-sqlite-migration.d.mts +1 -1
- package/dist/planner-produced-sqlite-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/render-ops-BDW2tUeR.mjs +22 -0
- package/dist/render-ops-BDW2tUeR.mjs.map +1 -0
- 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/runtime.mjs +1 -1
- package/dist/{sqlite-contract-serializer-jcRu8aHh.mjs → sqlite-contract-serializer-C41PO7DT.mjs} +2 -2
- package/dist/{sqlite-contract-serializer-jcRu8aHh.mjs.map → sqlite-contract-serializer-C41PO7DT.mjs.map} +1 -1
- package/dist/sqlite-migration-CCYnBXZp.mjs +73 -0
- package/dist/sqlite-migration-CCYnBXZp.mjs.map +1 -0
- package/dist/sqlite-migration-CJrASAxf.d.mts +46 -0
- package/dist/sqlite-migration-CJrASAxf.d.mts.map +1 -0
- package/package.json +18 -18
- package/src/core/control-target.ts +4 -4
- package/src/core/errors.ts +28 -0
- package/src/core/migrations/issue-planner.ts +148 -4
- package/src/core/migrations/op-factory-call.ts +162 -23
- package/src/core/migrations/planner-ddl-builders.ts +1 -1
- package/src/core/migrations/planner-produced-sqlite-migration.ts +8 -2
- package/src/core/migrations/planner.ts +14 -2
- package/src/core/migrations/render-ops.ts +37 -9
- package/src/core/migrations/runner.ts +16 -12
- package/src/core/migrations/sqlite-migration.ts +51 -1
- package/src/exports/migration.ts +9 -1
- package/dist/op-factory-call-DymqdXQW.mjs +0 -279
- package/dist/op-factory-call-DymqdXQW.mjs.map +0 -1
- package/dist/planner-DSNDwQy9.mjs.map +0 -1
- package/dist/planner-produced-sqlite-migration-C1yqJAiM.d.mts.map +0 -1
- package/dist/planner-produced-sqlite-migration-DowV_vHw.mjs.map +0 -1
- package/dist/render-ops-CFRbJ3Yb.mjs +0 -8
- package/dist/render-ops-CFRbJ3Yb.mjs.map +0 -1
- package/dist/sqlite-migration-CUqgmzQH.mjs +0 -16
- package/dist/sqlite-migration-CUqgmzQH.mjs.map +0 -1
- package/dist/sqlite-migration-D4XGYzgQ.d.mts +0 -17
- package/dist/sqlite-migration-D4XGYzgQ.d.mts.map +0 -1
- package/dist/tables-CjB7vXCr.mjs.map +0 -1
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
* SQLite migration IR: one concrete `*Call` class per pure factory under
|
|
3
3
|
* `operations/`, plus a shared `SqliteOpFactoryCallNode` abstract base.
|
|
4
4
|
*
|
|
5
|
-
* Each call class carries fully-resolved literal arguments
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* TypeScript scaffold is fully self-contained and does not need access to a
|
|
11
|
-
* `storageTypes` map at runtime.
|
|
5
|
+
* Each call class carries fully-resolved literal arguments. `CreateTableCall`
|
|
6
|
+
* holds structured `DdlColumn[]` + `DdlTableConstraint[]` and lowers via the
|
|
7
|
+
* adapter's DDL path; other call classes carry flat SQL fragments. Codec /
|
|
8
|
+
* `typeRef` / default expansion happens upstream in the issue-planner /
|
|
9
|
+
* strategies, mirroring the Postgres `ColumnSpec` pattern.
|
|
12
10
|
*/
|
|
13
11
|
|
|
14
12
|
import { errorUnfilledPlaceholder } from '@prisma-next/errors/migration';
|
|
@@ -16,13 +14,24 @@ import type {
|
|
|
16
14
|
MigrationOperationClass,
|
|
17
15
|
SqlMigrationPlanOperation,
|
|
18
16
|
} from '@prisma-next/family-sql/control';
|
|
17
|
+
import type { ExecuteRequestLowerer, Lowerer } from '@prisma-next/family-sql/control-adapter';
|
|
19
18
|
import type { OpFactoryCall as FrameworkOpFactoryCall } from '@prisma-next/framework-components/control';
|
|
19
|
+
import type {
|
|
20
|
+
AnyDdlColumnDefault,
|
|
21
|
+
DdlColumn,
|
|
22
|
+
DdlTableConstraint,
|
|
23
|
+
} from '@prisma-next/sql-relational-core/ast';
|
|
20
24
|
import { type ImportRequirement, jsonToTsSource, TsExpression } from '@prisma-next/ts-render';
|
|
25
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
26
|
+
import * as contractFreeDdl from '../../contract-free/ddl';
|
|
27
|
+
import { escapeLiteral } from '../sql-utils';
|
|
21
28
|
import { addColumn, dropColumn } from './operations/columns';
|
|
22
29
|
import { createIndex, dropIndex } from './operations/indexes';
|
|
23
30
|
import type { SqliteColumnSpec, SqliteIndexSpec, SqliteTableSpec } from './operations/shared';
|
|
24
|
-
import {
|
|
31
|
+
import { step } from './operations/shared';
|
|
32
|
+
import { dropTable, recreateTable } from './operations/tables';
|
|
25
33
|
import type { SqlitePlanTargetDetails } from './planner-target-details';
|
|
34
|
+
import { buildTargetDetails } from './planner-target-details';
|
|
26
35
|
|
|
27
36
|
type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
|
|
28
37
|
|
|
@@ -32,7 +41,7 @@ abstract class SqliteOpFactoryCallNode extends TsExpression implements Framework
|
|
|
32
41
|
abstract readonly factoryName: string;
|
|
33
42
|
abstract readonly operationClass: MigrationOperationClass;
|
|
34
43
|
abstract readonly label: string;
|
|
35
|
-
abstract toOp(): Op
|
|
44
|
+
abstract toOp(lowerer?: Lowerer): Op | Promise<Op>;
|
|
36
45
|
|
|
37
46
|
importRequirements(): readonly ImportRequirement[] {
|
|
38
47
|
return [{ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: this.factoryName }];
|
|
@@ -47,27 +56,157 @@ abstract class SqliteOpFactoryCallNode extends TsExpression implements Framework
|
|
|
47
56
|
// Table
|
|
48
57
|
// ============================================================================
|
|
49
58
|
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// TypeScript rendering helpers for DdlColumn / DdlTableConstraint
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
function renderDdlColumnDefault(def: AnyDdlColumnDefault | undefined): string {
|
|
64
|
+
if (!def) return '';
|
|
65
|
+
if (def.kind === 'literal') {
|
|
66
|
+
return `lit(${jsonToTsSource(def.value)})`;
|
|
67
|
+
}
|
|
68
|
+
return `fn(${jsonToTsSource(def.expression)})`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function renderDdlColumnAsTsCall(column: DdlColumn): string {
|
|
72
|
+
const opts: string[] = [];
|
|
73
|
+
if (column.notNull) opts.push('notNull: true');
|
|
74
|
+
if (column.primaryKey) opts.push('primaryKey: true');
|
|
75
|
+
if (column.default) opts.push(`default: ${renderDdlColumnDefault(column.default)}`);
|
|
76
|
+
const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
|
|
77
|
+
return `col(${jsonToTsSource(column.name)}, ${jsonToTsSource(column.type)}${optsStr})`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function renderDdlConstraintAsTsCall(constraint: DdlTableConstraint): string {
|
|
81
|
+
switch (constraint.kind) {
|
|
82
|
+
case 'primary-key': {
|
|
83
|
+
const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
|
|
84
|
+
return `primaryKey(${jsonToTsSource(constraint.columns)}${nameOpt})`;
|
|
85
|
+
}
|
|
86
|
+
case 'foreign-key': {
|
|
87
|
+
const opts: string[] = [];
|
|
88
|
+
if (constraint.name) opts.push(`name: ${jsonToTsSource(constraint.name)}`);
|
|
89
|
+
if (constraint.onDelete) opts.push(`onDelete: ${jsonToTsSource(constraint.onDelete)}`);
|
|
90
|
+
if (constraint.onUpdate) opts.push(`onUpdate: ${jsonToTsSource(constraint.onUpdate)}`);
|
|
91
|
+
const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
|
|
92
|
+
return `foreignKey(${jsonToTsSource(constraint.columns)}, ${jsonToTsSource(constraint.refTable)}, ${jsonToTsSource(constraint.refColumns)}${optsStr})`;
|
|
93
|
+
}
|
|
94
|
+
case 'unique': {
|
|
95
|
+
const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
|
|
96
|
+
return `unique(${jsonToTsSource(constraint.columns)}${nameOpt})`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function constraintImportSymbols(constraints: readonly DdlTableConstraint[] | undefined): string[] {
|
|
102
|
+
if (!constraints || constraints.length === 0) return [];
|
|
103
|
+
const symbols = new Set<string>();
|
|
104
|
+
for (const c of constraints) {
|
|
105
|
+
if (c.kind === 'primary-key') symbols.add('primaryKey');
|
|
106
|
+
else if (c.kind === 'foreign-key') symbols.add('foreignKey');
|
|
107
|
+
else if (c.kind === 'unique') symbols.add('unique');
|
|
108
|
+
}
|
|
109
|
+
return [...symbols];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function defaultImportSymbols(columns: readonly DdlColumn[]): string[] {
|
|
113
|
+
const symbols = new Set<string>();
|
|
114
|
+
for (const col of columns) {
|
|
115
|
+
if (col.default?.kind === 'literal') symbols.add('lit');
|
|
116
|
+
else if (col.default?.kind === 'function') symbols.add('fn');
|
|
117
|
+
}
|
|
118
|
+
return [...symbols];
|
|
119
|
+
}
|
|
120
|
+
|
|
50
121
|
export class CreateTableCall extends SqliteOpFactoryCallNode {
|
|
51
122
|
readonly factoryName = 'createTable' as const;
|
|
52
123
|
readonly operationClass = 'additive' as const;
|
|
53
124
|
readonly tableName: string;
|
|
54
|
-
readonly
|
|
125
|
+
readonly columns: readonly DdlColumn[];
|
|
126
|
+
readonly constraints: readonly DdlTableConstraint[] | undefined;
|
|
55
127
|
readonly label: string;
|
|
56
128
|
|
|
57
|
-
constructor(
|
|
129
|
+
constructor(
|
|
130
|
+
tableName: string,
|
|
131
|
+
columns: readonly DdlColumn[],
|
|
132
|
+
constraints?: readonly DdlTableConstraint[],
|
|
133
|
+
) {
|
|
58
134
|
super();
|
|
59
135
|
this.tableName = tableName;
|
|
60
|
-
this.
|
|
136
|
+
this.columns = Object.freeze([...columns]);
|
|
137
|
+
this.constraints = constraints ? Object.freeze([...constraints]) : undefined;
|
|
61
138
|
this.label = `Create table ${tableName}`;
|
|
62
139
|
this.freeze();
|
|
63
140
|
}
|
|
64
141
|
|
|
65
|
-
toOp(): Op {
|
|
66
|
-
|
|
142
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
143
|
+
if (lowerer === undefined) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`CreateTableCall.toOp: a DDL lowerer is required on the SQLite planner path (table "${this.tableName}"). Pass the control adapter to createSqliteMigrationPlanner.`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
const ddlNode = contractFreeDdl.createTable({
|
|
149
|
+
table: this.tableName,
|
|
150
|
+
columns: this.columns,
|
|
151
|
+
...ifDefined('constraints', this.constraints),
|
|
152
|
+
});
|
|
153
|
+
const statement = await lowerer.lowerToExecuteRequest(ddlNode);
|
|
154
|
+
const tableName = this.tableName;
|
|
155
|
+
const escapedName = escapeLiteral(tableName);
|
|
156
|
+
return {
|
|
157
|
+
id: `table.${tableName}`,
|
|
158
|
+
label: `Create table ${tableName}`,
|
|
159
|
+
summary: `Creates table ${tableName} with required columns`,
|
|
160
|
+
operationClass: 'additive',
|
|
161
|
+
target: { id: 'sqlite', details: buildTargetDetails('table', tableName) },
|
|
162
|
+
precheck: [
|
|
163
|
+
step(
|
|
164
|
+
`ensure table "${tableName}" does not exist`,
|
|
165
|
+
`SELECT COUNT(*) = 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapedName}'`,
|
|
166
|
+
),
|
|
167
|
+
],
|
|
168
|
+
execute: [
|
|
169
|
+
{
|
|
170
|
+
description: `create table "${tableName}"`,
|
|
171
|
+
sql: statement.sql,
|
|
172
|
+
params: statement.params ?? [],
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
postcheck: [
|
|
176
|
+
step(
|
|
177
|
+
`verify table "${tableName}" exists`,
|
|
178
|
+
`SELECT COUNT(*) > 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapedName}'`,
|
|
179
|
+
),
|
|
180
|
+
],
|
|
181
|
+
};
|
|
67
182
|
}
|
|
68
183
|
|
|
69
184
|
renderTypeScript(): string {
|
|
70
|
-
|
|
185
|
+
const columnsList = this.columns.map(renderDdlColumnAsTsCall).join(', ');
|
|
186
|
+
const constraintsList = this.constraints
|
|
187
|
+
? this.constraints.map(renderDdlConstraintAsTsCall).join(', ')
|
|
188
|
+
: undefined;
|
|
189
|
+
|
|
190
|
+
const opts: string[] = [];
|
|
191
|
+
opts.push(`table: ${jsonToTsSource(this.tableName)}`);
|
|
192
|
+
opts.push(`columns: [${columnsList}]`);
|
|
193
|
+
if (constraintsList) opts.push(`constraints: [${constraintsList}]`);
|
|
194
|
+
|
|
195
|
+
return `this.createTable({ ${opts.join(', ')} })`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
199
|
+
const req: ImportRequirement[] = [];
|
|
200
|
+
if (this.columns.length > 0) {
|
|
201
|
+
req.push({ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: 'col' });
|
|
202
|
+
for (const sym of defaultImportSymbols(this.columns)) {
|
|
203
|
+
req.push({ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: sym });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for (const sym of constraintImportSymbols(this.constraints)) {
|
|
207
|
+
req.push({ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: sym });
|
|
208
|
+
}
|
|
209
|
+
return req;
|
|
71
210
|
}
|
|
72
211
|
}
|
|
73
212
|
|
|
@@ -84,7 +223,7 @@ export class DropTableCall extends SqliteOpFactoryCallNode {
|
|
|
84
223
|
this.freeze();
|
|
85
224
|
}
|
|
86
225
|
|
|
87
|
-
toOp(): Op {
|
|
226
|
+
toOp(_lowerer?: Lowerer): Op {
|
|
88
227
|
return dropTable(this.tableName);
|
|
89
228
|
}
|
|
90
229
|
|
|
@@ -125,7 +264,7 @@ export class RecreateTableCall extends SqliteOpFactoryCallNode {
|
|
|
125
264
|
this.freeze();
|
|
126
265
|
}
|
|
127
266
|
|
|
128
|
-
toOp(): Op {
|
|
267
|
+
toOp(_lowerer?: Lowerer): Op {
|
|
129
268
|
return recreateTable({
|
|
130
269
|
tableName: this.tableName,
|
|
131
270
|
contractTable: this.contractTable,
|
|
@@ -172,7 +311,7 @@ export class AddColumnCall extends SqliteOpFactoryCallNode {
|
|
|
172
311
|
this.freeze();
|
|
173
312
|
}
|
|
174
313
|
|
|
175
|
-
toOp(): Op {
|
|
314
|
+
toOp(_lowerer?: Lowerer): Op {
|
|
176
315
|
return addColumn(this.tableName, this.column);
|
|
177
316
|
}
|
|
178
317
|
|
|
@@ -196,7 +335,7 @@ export class DropColumnCall extends SqliteOpFactoryCallNode {
|
|
|
196
335
|
this.freeze();
|
|
197
336
|
}
|
|
198
337
|
|
|
199
|
-
toOp(): Op {
|
|
338
|
+
toOp(_lowerer?: Lowerer): Op {
|
|
200
339
|
return dropColumn(this.tableName, this.columnName);
|
|
201
340
|
}
|
|
202
341
|
|
|
@@ -226,7 +365,7 @@ export class CreateIndexCall extends SqliteOpFactoryCallNode {
|
|
|
226
365
|
this.freeze();
|
|
227
366
|
}
|
|
228
367
|
|
|
229
|
-
toOp(): Op {
|
|
368
|
+
toOp(_lowerer?: Lowerer): Op {
|
|
230
369
|
return createIndex(this.tableName, this.indexName, this.columns);
|
|
231
370
|
}
|
|
232
371
|
|
|
@@ -250,7 +389,7 @@ export class DropIndexCall extends SqliteOpFactoryCallNode {
|
|
|
250
389
|
this.freeze();
|
|
251
390
|
}
|
|
252
391
|
|
|
253
|
-
toOp(): Op {
|
|
392
|
+
toOp(_lowerer?: Lowerer): Op {
|
|
254
393
|
return dropIndex(this.tableName, this.indexName);
|
|
255
394
|
}
|
|
256
395
|
|
|
@@ -292,7 +431,7 @@ export class DataTransformCall extends SqliteOpFactoryCallNode {
|
|
|
292
431
|
this.freeze();
|
|
293
432
|
}
|
|
294
433
|
|
|
295
|
-
toOp(): Op {
|
|
434
|
+
toOp(_lowerer?: Lowerer): Op {
|
|
296
435
|
throw errorUnfilledPlaceholder(this.label);
|
|
297
436
|
}
|
|
298
437
|
|
|
@@ -344,7 +483,7 @@ export class RawSqlCall extends SqliteOpFactoryCallNode {
|
|
|
344
483
|
this.freeze();
|
|
345
484
|
}
|
|
346
485
|
|
|
347
|
-
toOp(): Op {
|
|
486
|
+
toOp(_lowerer?: Lowerer): Op {
|
|
348
487
|
return this.op;
|
|
349
488
|
}
|
|
350
489
|
|
|
@@ -123,7 +123,7 @@ export function isInlineAutoincrementPrimaryKey(table: StorageTable, columnName:
|
|
|
123
123
|
|
|
124
124
|
type ResolvedColumnTypeMetadata = Pick<StorageColumn, 'nativeType' | 'codecId' | 'typeParams'>;
|
|
125
125
|
|
|
126
|
-
function resolveColumnTypeMetadata(
|
|
126
|
+
export function resolveColumnTypeMetadata(
|
|
127
127
|
column: StorageColumn,
|
|
128
128
|
storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry>,
|
|
129
129
|
): ResolvedColumnTypeMetadata {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
3
|
import type {
|
|
3
4
|
MigrationPlanWithAuthoringSurface,
|
|
4
5
|
OpFactoryCall,
|
|
@@ -24,22 +25,27 @@ export class TypeScriptRenderableSqliteMigration
|
|
|
24
25
|
readonly #meta: MigrationMeta;
|
|
25
26
|
readonly #destination: SqliteMigrationDestinationInfo;
|
|
26
27
|
readonly #spaceId: string;
|
|
28
|
+
readonly #lowerer: ExecuteRequestLowerer | undefined;
|
|
29
|
+
#operationsCache: readonly (Op | Promise<Op>)[] | undefined;
|
|
27
30
|
|
|
28
31
|
constructor(
|
|
29
32
|
calls: readonly OpFactoryCall[],
|
|
30
33
|
meta: MigrationMeta,
|
|
31
34
|
spaceId: string,
|
|
32
35
|
destination?: SqliteMigrationDestinationInfo,
|
|
36
|
+
lowerer?: ExecuteRequestLowerer,
|
|
33
37
|
) {
|
|
34
38
|
super();
|
|
35
39
|
this.#calls = calls;
|
|
36
40
|
this.#meta = meta;
|
|
37
41
|
this.#spaceId = spaceId;
|
|
38
42
|
this.#destination = destination ?? { storageHash: meta.to };
|
|
43
|
+
this.#lowerer = lowerer;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
override get operations(): readonly Op[] {
|
|
42
|
-
|
|
46
|
+
override get operations(): readonly (Op | Promise<Op>)[] {
|
|
47
|
+
this.#operationsCache ??= renderOps(this.#calls, this.#lowerer);
|
|
48
|
+
return this.#operationsCache;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
override describe(): MigrationMeta {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
planFieldEventOperations,
|
|
11
11
|
plannerFailure,
|
|
12
12
|
} from '@prisma-next/family-sql/control';
|
|
13
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
13
14
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
14
15
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
15
16
|
import type {
|
|
@@ -27,8 +28,10 @@ import {
|
|
|
27
28
|
import { sqlitePlannerStrategies } from './planner-strategies';
|
|
28
29
|
import type { SqlitePlanTargetDetails } from './planner-target-details';
|
|
29
30
|
|
|
30
|
-
export function createSqliteMigrationPlanner(
|
|
31
|
-
|
|
31
|
+
export function createSqliteMigrationPlanner(
|
|
32
|
+
lowerer: ExecuteRequestLowerer,
|
|
33
|
+
): SqliteMigrationPlanner {
|
|
34
|
+
return new SqliteMigrationPlanner(lowerer);
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
export type SqlitePlanResult =
|
|
@@ -52,6 +55,12 @@ export type SqlitePlanResult =
|
|
|
52
55
|
export class SqliteMigrationPlanner
|
|
53
56
|
implements SqlMigrationPlanner<SqlitePlanTargetDetails>, MigrationPlanner<'sql', 'sqlite'>
|
|
54
57
|
{
|
|
58
|
+
readonly #lowerer: ExecuteRequestLowerer;
|
|
59
|
+
|
|
60
|
+
constructor(lowerer: ExecuteRequestLowerer) {
|
|
61
|
+
this.#lowerer = lowerer;
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
plan(options: {
|
|
56
65
|
readonly contract: unknown;
|
|
57
66
|
readonly schema: unknown;
|
|
@@ -89,6 +98,8 @@ export class SqliteMigrationPlanner
|
|
|
89
98
|
to: context.toHash,
|
|
90
99
|
},
|
|
91
100
|
spaceId,
|
|
101
|
+
undefined,
|
|
102
|
+
this.#lowerer,
|
|
92
103
|
);
|
|
93
104
|
}
|
|
94
105
|
|
|
@@ -149,6 +160,7 @@ export class SqliteMigrationPlanner
|
|
|
149
160
|
},
|
|
150
161
|
options.spaceId,
|
|
151
162
|
destination,
|
|
163
|
+
this.#lowerer,
|
|
152
164
|
),
|
|
153
165
|
};
|
|
154
166
|
}
|
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
3
|
+
import type {
|
|
4
|
+
MigrationPlanOperation,
|
|
5
|
+
OpFactoryCall,
|
|
6
|
+
} from '@prisma-next/framework-components/control';
|
|
7
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
8
|
+
import { isThenable } from '@prisma-next/utils/promise';
|
|
3
9
|
import type { SqlitePlanTargetDetails } from './planner-target-details';
|
|
4
10
|
|
|
5
11
|
type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
function assertSqliteOp(op: MigrationPlanOperation, callFactoryName: string): asserts op is Op {
|
|
14
|
+
const targetId = blindCast<
|
|
15
|
+
{ target?: { id?: string } },
|
|
16
|
+
'op.target is present on concrete SqlMigrationPlanOperation but absent on the framework MigrationPlanOperation base'
|
|
17
|
+
>(op).target?.id;
|
|
18
|
+
if (targetId !== 'sqlite') {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`renderOps: expected sqlite op but got target.id="${String(targetId)}" for op.id="${op.id}" (factoryName="${callFactoryName}"). An OpFactoryCall produced an op for a different target on the sqlite planner path; check the call's target binding.`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function renderOps(
|
|
26
|
+
calls: readonly OpFactoryCall[],
|
|
27
|
+
lowerer?: ExecuteRequestLowerer,
|
|
28
|
+
): (Op | Promise<Op>)[] {
|
|
29
|
+
return calls.map((c) => {
|
|
30
|
+
const opOrPromise = blindCast<
|
|
31
|
+
{ toOp(lowerer?: ExecuteRequestLowerer): Op | Promise<Op> },
|
|
32
|
+
'SQLite OpFactoryCall.toOp accepts an optional ExecuteRequestLowerer; the framework interface omits it because not all targets need a lowerer — the SQLite target overrides with this extended signature'
|
|
33
|
+
>(c).toOp(lowerer);
|
|
34
|
+
if (isThenable(opOrPromise)) {
|
|
35
|
+
return opOrPromise.then((op) => {
|
|
36
|
+
assertSqliteOp(op, c.factoryName);
|
|
37
|
+
return op;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
assertSqliteOp(opOrPromise, c.factoryName);
|
|
41
|
+
return opOrPromise;
|
|
42
|
+
});
|
|
15
43
|
}
|
|
@@ -16,7 +16,8 @@ import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
|
16
16
|
import type { MigrationRunnerResult } from '@prisma-next/framework-components/control';
|
|
17
17
|
import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
|
|
18
18
|
import type { SqlControlDriverInstance, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
19
|
-
import type {
|
|
19
|
+
import type { SqlExecuteRequest } from '@prisma-next/sql-relational-core/ast';
|
|
20
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
20
21
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
21
22
|
import type { Result } from '@prisma-next/utils/result';
|
|
22
23
|
import { notOk, ok, okVoid } from '@prisma-next/utils/result';
|
|
@@ -51,13 +52,19 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
51
52
|
}
|
|
52
53
|
const space = options.plan.spaceId;
|
|
53
54
|
|
|
55
|
+
// Materialize any async ops before running checks or executing.
|
|
56
|
+
const planOps = blindCast<
|
|
57
|
+
readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],
|
|
58
|
+
'ops were produced by the SQLite planner and are SqlMigrationPlanOperation<SqlitePlanTargetDetails>; MigrationPlan.operations uses the wider framework type to accommodate Promise covariance'
|
|
59
|
+
>(await Promise.all(options.plan.operations));
|
|
60
|
+
|
|
54
61
|
const destinationCheck = this.ensurePlanMatchesDestinationContract(
|
|
55
62
|
options.plan.destination,
|
|
56
63
|
options.destinationContract,
|
|
57
64
|
);
|
|
58
65
|
if (!destinationCheck.ok) return destinationCheck;
|
|
59
66
|
|
|
60
|
-
const policyCheck = this.enforcePolicyCompatibility(options.policy,
|
|
67
|
+
const policyCheck = this.enforcePolicyCompatibility(options.policy, planOps);
|
|
61
68
|
if (!policyCheck.ok) return policyCheck;
|
|
62
69
|
|
|
63
70
|
const ensureResult = await this.ensureControlTables(driver, options.destinationContract);
|
|
@@ -78,7 +85,7 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
78
85
|
operationsExecuted = 0;
|
|
79
86
|
executedOperations = [];
|
|
80
87
|
} else {
|
|
81
|
-
const applyResult = await this.applyPlan(driver, options);
|
|
88
|
+
const applyResult = await this.applyPlan(driver, options, planOps);
|
|
82
89
|
if (!applyResult.ok) return applyResult;
|
|
83
90
|
operationsExecuted = applyResult.value.operationsExecuted;
|
|
84
91
|
executedOperations = applyResult.value.executedOperations;
|
|
@@ -122,7 +129,7 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
122
129
|
}
|
|
123
130
|
|
|
124
131
|
return runnerSuccess({
|
|
125
|
-
operationsPlanned:
|
|
132
|
+
operationsPlanned: planOps.length,
|
|
126
133
|
operationsExecuted,
|
|
127
134
|
});
|
|
128
135
|
}
|
|
@@ -226,6 +233,7 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
226
233
|
private async applyPlan(
|
|
227
234
|
driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
|
|
228
235
|
options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,
|
|
236
|
+
ops: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],
|
|
229
237
|
): Promise<
|
|
230
238
|
Result<
|
|
231
239
|
{
|
|
@@ -243,7 +251,7 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
243
251
|
let operationsExecuted = 0;
|
|
244
252
|
const executedOperations: Array<SqlMigrationPlanOperation<SqlitePlanTargetDetails>> = [];
|
|
245
253
|
|
|
246
|
-
for (const operation of
|
|
254
|
+
for (const operation of ops) {
|
|
247
255
|
options.callbacks?.onOperationStart?.(operation);
|
|
248
256
|
try {
|
|
249
257
|
if (runPostchecks && runIdempotency) {
|
|
@@ -305,7 +313,7 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
305
313
|
}
|
|
306
314
|
const lowererContext = { contract };
|
|
307
315
|
for (const query of this.family.bootstrapControlTableQueries()) {
|
|
308
|
-
await this.executeStatement(driver, this.family.lowerAst(query, lowererContext));
|
|
316
|
+
await this.executeStatement(driver, await this.family.lowerAst(query, lowererContext));
|
|
309
317
|
}
|
|
310
318
|
return okVoid();
|
|
311
319
|
}
|
|
@@ -659,12 +667,8 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
659
667
|
|
|
660
668
|
private async executeStatement(
|
|
661
669
|
driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
|
|
662
|
-
statement:
|
|
670
|
+
statement: SqlExecuteRequest,
|
|
663
671
|
): Promise<void> {
|
|
664
|
-
|
|
665
|
-
await driver.query(statement.sql, statement.params);
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
await driver.query(statement.sql);
|
|
672
|
+
await driver.query(statement.sql, statement.params);
|
|
669
673
|
}
|
|
670
674
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
|
+
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
1
3
|
import { Migration as SqlMigration } from '@prisma-next/family-sql/migration';
|
|
4
|
+
import type { ControlStack } from '@prisma-next/framework-components/control';
|
|
5
|
+
import type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
|
|
6
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
7
|
+
import { errorSqliteMigrationStackMissing } from '../errors';
|
|
8
|
+
import { CreateTableCall } from './op-factory-call';
|
|
2
9
|
import type { SqlitePlanTargetDetails } from './planner-target-details';
|
|
3
10
|
|
|
4
11
|
/**
|
|
@@ -7,7 +14,50 @@ import type { SqlitePlanTargetDetails } from './planner-target-details';
|
|
|
7
14
|
* SQLite literal, so both user-authored migrations and renderer-generated
|
|
8
15
|
* scaffolds can extend `SqliteMigration` directly without redeclaring
|
|
9
16
|
* target-local identity.
|
|
17
|
+
*
|
|
18
|
+
* The constructor materializes a single SQLite `SqlControlAdapter` from
|
|
19
|
+
* `stack.adapter.create(stack)` and stores it; the protected `createTable`
|
|
20
|
+
* instance method forwards to `CreateTableCall` with that stored adapter,
|
|
21
|
+
* so user migrations can write `this.createTable({...})` without threading
|
|
22
|
+
* the adapter through every call.
|
|
10
23
|
*/
|
|
11
|
-
export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetails> {
|
|
24
|
+
export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetails, 'sqlite'> {
|
|
12
25
|
readonly targetId = 'sqlite' as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Materialized SQLite control adapter, created once per migration
|
|
29
|
+
* instance from the injected stack. `undefined` only when the migration
|
|
30
|
+
* was instantiated without a stack (test fixtures); `createTable`
|
|
31
|
+
* throws in that case to surface the misuse.
|
|
32
|
+
*/
|
|
33
|
+
protected readonly controlAdapter: SqlControlAdapter<'sqlite'> | undefined;
|
|
34
|
+
|
|
35
|
+
constructor(stack?: ControlStack<'sql', 'sqlite'>) {
|
|
36
|
+
super(stack);
|
|
37
|
+
this.controlAdapter = stack?.adapter
|
|
38
|
+
? blindCast<
|
|
39
|
+
SqlControlAdapter<'sqlite'>,
|
|
40
|
+
'The SQLite descriptor create() returns SqlControlAdapter<sqlite>; typed as wider ControlAdapterInstance at the framework boundary'
|
|
41
|
+
>(stack.adapter.create(stack))
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Emit a `CREATE TABLE` migration operation. Builds a typed DDL node from
|
|
47
|
+
* the supplied options and lowers it through the stored control adapter.
|
|
48
|
+
* Throws if no adapter is present (i.e. migration instantiated without a stack).
|
|
49
|
+
*/
|
|
50
|
+
protected createTable(options: {
|
|
51
|
+
readonly table: string;
|
|
52
|
+
readonly ifNotExists?: boolean;
|
|
53
|
+
readonly columns: readonly DdlColumn[];
|
|
54
|
+
readonly constraints?: readonly DdlTableConstraint[];
|
|
55
|
+
}): Promise<SqlMigrationPlanOperation<SqlitePlanTargetDetails>> {
|
|
56
|
+
if (!this.controlAdapter) {
|
|
57
|
+
throw errorSqliteMigrationStackMissing();
|
|
58
|
+
}
|
|
59
|
+
return new CreateTableCall(options.table, options.columns, options.constraints).toOp(
|
|
60
|
+
this.controlAdapter,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
13
63
|
}
|
package/src/exports/migration.ts
CHANGED
|
@@ -9,6 +9,14 @@ export { MigrationCLI } from '@prisma-next/cli/migration-cli';
|
|
|
9
9
|
// `placeholder("…")` slots, instead of pulling in `@prisma-next/errors`
|
|
10
10
|
// directly. The planner emits an import from this same module.
|
|
11
11
|
export { placeholder } from '@prisma-next/errors/migration';
|
|
12
|
+
export {
|
|
13
|
+
col,
|
|
14
|
+
fn,
|
|
15
|
+
foreignKey,
|
|
16
|
+
lit,
|
|
17
|
+
primaryKey,
|
|
18
|
+
unique,
|
|
19
|
+
} from '@prisma-next/sql-relational-core/contract-free';
|
|
12
20
|
export { addColumn, dropColumn } from '../core/migrations/operations/columns';
|
|
13
21
|
export {
|
|
14
22
|
type DataTransformOptions,
|
|
@@ -16,7 +24,7 @@ export {
|
|
|
16
24
|
} from '../core/migrations/operations/data-transform';
|
|
17
25
|
export { createIndex, dropIndex } from '../core/migrations/operations/indexes';
|
|
18
26
|
export { rawSql } from '../core/migrations/operations/raw';
|
|
19
|
-
export {
|
|
27
|
+
export { dropTable, recreateTable } from '../core/migrations/operations/tables';
|
|
20
28
|
// Target-owned base class for migrations. Aliased to `Migration` so
|
|
21
29
|
// user-edited migration.ts files (and the renderer's scaffold) read as
|
|
22
30
|
// `class M extends Migration { … }` without having to thread the
|