@prisma-next/target-sqlite 0.13.0-dev.4 → 0.13.0-dev.40
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 +35 -2
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +3 -22
- package/dist/contract-free.mjs.map +1 -1
- package/dist/control.d.mts +5 -8
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +18 -20
- package/dist/control.mjs.map +1 -1
- package/dist/ddl-DrtjQMFK.mjs +68 -0
- package/dist/ddl-DrtjQMFK.mjs.map +1 -0
- package/dist/{descriptor-meta-Dxx2A6PT.mjs → descriptor-meta-DxmEeTJ-.mjs} +10 -3
- package/dist/descriptor-meta-DxmEeTJ-.mjs.map +1 -0
- package/dist/migration.d.mts +4 -46
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +4 -3
- package/dist/migration.mjs.map +1 -1
- package/dist/op-factory-call-DmdfD1yd.mjs +794 -0
- package/dist/op-factory-call-DmdfD1yd.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +22 -12
- package/dist/op-factory-call.d.mts.map +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-DSNDwQy9.mjs → planner-Ciq8p_dL.mjs} +80 -12
- package/dist/planner-Ciq8p_dL.mjs.map +1 -0
- package/dist/{planner-produced-sqlite-migration-DowV_vHw.mjs → planner-produced-sqlite-migration-0xPEm3R1.mjs} +9 -5
- package/dist/planner-produced-sqlite-migration-0xPEm3R1.mjs.map +1 -0
- package/dist/{planner-produced-sqlite-migration-C1yqJAiM.d.mts → planner-produced-sqlite-migration-CpgsY-M9.d.mts} +5 -4
- package/dist/planner-produced-sqlite-migration-CpgsY-M9.d.mts.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.d.mts +1 -0
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/dist/shared-Dhc8mLK1.d.mts.map +1 -1
- package/dist/{sqlite-contract-serializer-jcRu8aHh.mjs → sqlite-contract-serializer--iaDgC8e.mjs} +17 -6
- package/dist/sqlite-contract-serializer--iaDgC8e.mjs.map +1 -0
- package/dist/sqlite-migration-A0rwqPOG.mjs +92 -0
- package/dist/sqlite-migration-A0rwqPOG.mjs.map +1 -0
- package/dist/sqlite-migration-DVfhQwN_.d.mts +75 -0
- package/dist/sqlite-migration-DVfhQwN_.d.mts.map +1 -0
- package/package.json +18 -18
- package/src/contract-free/checks.ts +75 -0
- package/src/core/control-target.ts +4 -4
- package/src/core/errors.ts +28 -0
- package/src/core/migrations/issue-planner.ts +151 -8
- package/src/core/migrations/op-factory-call.ts +332 -45
- package/src/core/migrations/operations/columns.ts +32 -26
- package/src/core/migrations/operations/indexes.ts +31 -27
- package/src/core/migrations/operations/shared.ts +11 -3
- package/src/core/migrations/operations/tables.ts +39 -37
- package/src/core/migrations/planner-ddl-builders.ts +7 -16
- package/src/core/migrations/planner-produced-sqlite-migration.ts +8 -2
- package/src/core/migrations/planner-strategies.ts +3 -3
- 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 +119 -1
- package/src/core/sqlite-contract-serializer.ts +5 -0
- package/src/core/sqlite-unbound-database.ts +30 -54
- package/src/exports/contract-free.ts +8 -0
- package/src/exports/migration.ts +8 -3
- package/dist/descriptor-meta-Dxx2A6PT.mjs.map +0 -1
- package/dist/descriptor-meta-runtime-BkXK3OjD.mjs +0 -12
- package/dist/descriptor-meta-runtime-BkXK3OjD.mjs.map +0 -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-contract-serializer-jcRu8aHh.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 +0 -412
- package/dist/tables-CjB7vXCr.mjs.map +0 -1
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
SqlMigrationPlanOperation,
|
|
3
|
+
SqlMigrationPlanOperationStep,
|
|
4
|
+
} from '@prisma-next/family-sql/control';
|
|
2
5
|
import { REFERENTIAL_ACTION_SQL } from '@prisma-next/sql-contract/referential-action-sql';
|
|
3
6
|
import type { ReferentialAction } from '@prisma-next/sql-contract/types';
|
|
7
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
4
8
|
import { quoteIdentifier } from '../../sql-utils';
|
|
5
9
|
import type { SqlitePlanTargetDetails } from '../planner-target-details';
|
|
6
10
|
|
|
7
11
|
export type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
|
|
8
12
|
|
|
9
|
-
export function step(
|
|
10
|
-
|
|
13
|
+
export function step(
|
|
14
|
+
description: string,
|
|
15
|
+
sql: string,
|
|
16
|
+
params?: readonly unknown[],
|
|
17
|
+
): SqlMigrationPlanOperationStep {
|
|
18
|
+
return { description, sql, ...ifDefined('params', params) };
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
/**
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { MigrationOperationClass } from '@prisma-next/family-sql/control';
|
|
2
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
3
|
import type { SchemaIssue } from '@prisma-next/framework-components/control';
|
|
4
|
+
import { tableExistsAst } from '../../../contract-free/checks';
|
|
3
5
|
import { stripOuterParens } from '../../default-normalizer';
|
|
4
6
|
import { escapeLiteral, quoteIdentifier } from '../../sql-utils';
|
|
5
7
|
import { buildCreateIndexSql } from '../planner-ddl-builders';
|
|
@@ -13,6 +15,18 @@ import {
|
|
|
13
15
|
step,
|
|
14
16
|
} from './shared';
|
|
15
17
|
|
|
18
|
+
type CheckStep = { sql: string; params?: readonly unknown[] };
|
|
19
|
+
|
|
20
|
+
async function tableExistsSteps(
|
|
21
|
+
lowerer: ExecuteRequestLowerer,
|
|
22
|
+
tableName: string,
|
|
23
|
+
): Promise<{ present: CheckStep; absent: CheckStep }> {
|
|
24
|
+
const checks = tableExistsAst(tableName);
|
|
25
|
+
const present = await lowerer.lowerToExecuteRequest(checks.tablePresent());
|
|
26
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.tableAbsent());
|
|
27
|
+
return { present, absent };
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
/**
|
|
17
31
|
* Renders the body of a `CREATE TABLE <name> ( … )` statement from a flat
|
|
18
32
|
* `SqliteTableSpec`. SQLite's `INTEGER PRIMARY KEY AUTOINCREMENT` form is
|
|
@@ -42,49 +56,35 @@ function renderCreateTableSql(tableName: string, spec: SqliteTableSpec): string
|
|
|
42
56
|
return `CREATE TABLE ${quoteIdentifier(tableName)} (\n ${allDefs.join(',\n ')}\n)`;
|
|
43
57
|
}
|
|
44
58
|
|
|
45
|
-
export function createTable(
|
|
59
|
+
export async function createTable(
|
|
60
|
+
tableName: string,
|
|
61
|
+
spec: SqliteTableSpec,
|
|
62
|
+
lowerer: ExecuteRequestLowerer,
|
|
63
|
+
): Promise<Op> {
|
|
64
|
+
const { present, absent } = await tableExistsSteps(lowerer, tableName);
|
|
46
65
|
return {
|
|
47
66
|
id: `table.${tableName}`,
|
|
48
67
|
label: `Create table ${tableName}`,
|
|
49
68
|
summary: `Creates table ${tableName} with required columns`,
|
|
50
69
|
operationClass: 'additive',
|
|
51
70
|
target: { id: 'sqlite', details: buildTargetDetails('table', tableName) },
|
|
52
|
-
precheck: [
|
|
53
|
-
step(
|
|
54
|
-
`ensure table "${tableName}" does not exist`,
|
|
55
|
-
`SELECT COUNT(*) = 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapeLiteral(tableName)}'`,
|
|
56
|
-
),
|
|
57
|
-
],
|
|
71
|
+
precheck: [step(`ensure table "${tableName}" does not exist`, absent.sql, absent.params)],
|
|
58
72
|
execute: [step(`create table "${tableName}"`, renderCreateTableSql(tableName, spec))],
|
|
59
|
-
postcheck: [
|
|
60
|
-
step(
|
|
61
|
-
`verify table "${tableName}" exists`,
|
|
62
|
-
`SELECT COUNT(*) > 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapeLiteral(tableName)}'`,
|
|
63
|
-
),
|
|
64
|
-
],
|
|
73
|
+
postcheck: [step(`verify table "${tableName}" exists`, present.sql, present.params)],
|
|
65
74
|
};
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
export function dropTable(tableName: string): Op {
|
|
77
|
+
export async function dropTable(tableName: string, lowerer: ExecuteRequestLowerer): Promise<Op> {
|
|
78
|
+
const { present, absent } = await tableExistsSteps(lowerer, tableName);
|
|
69
79
|
return {
|
|
70
80
|
id: `dropTable.${tableName}`,
|
|
71
81
|
label: `Drop table ${tableName}`,
|
|
72
82
|
summary: `Drops table ${tableName} which is not in the contract`,
|
|
73
83
|
operationClass: 'destructive',
|
|
74
84
|
target: { id: 'sqlite', details: buildTargetDetails('table', tableName) },
|
|
75
|
-
precheck: [
|
|
76
|
-
step(
|
|
77
|
-
`ensure table "${tableName}" exists`,
|
|
78
|
-
`SELECT COUNT(*) > 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapeLiteral(tableName)}'`,
|
|
79
|
-
),
|
|
80
|
-
],
|
|
85
|
+
precheck: [step(`ensure table "${tableName}" exists`, present.sql, present.params)],
|
|
81
86
|
execute: [step(`drop table "${tableName}"`, `DROP TABLE ${quoteIdentifier(tableName)}`)],
|
|
82
|
-
postcheck: [
|
|
83
|
-
step(
|
|
84
|
-
`verify table "${tableName}" is gone`,
|
|
85
|
-
`SELECT COUNT(*) = 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapeLiteral(tableName)}'`,
|
|
86
|
-
),
|
|
87
|
-
],
|
|
87
|
+
postcheck: [step(`verify table "${tableName}" is gone`, absent.sql, absent.params)],
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -115,7 +115,10 @@ export interface RecreateTableArgs {
|
|
|
115
115
|
readonly operationClass: MigrationOperationClass;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export function recreateTable(
|
|
118
|
+
export async function recreateTable(
|
|
119
|
+
args: RecreateTableArgs,
|
|
120
|
+
lowerer: ExecuteRequestLowerer,
|
|
121
|
+
): Promise<Op> {
|
|
119
122
|
const {
|
|
120
123
|
tableName,
|
|
121
124
|
contractTable,
|
|
@@ -149,6 +152,9 @@ export function recreateTable(args: RecreateTableArgs): Op {
|
|
|
149
152
|
]
|
|
150
153
|
: [];
|
|
151
154
|
|
|
155
|
+
const tableSteps = await tableExistsSteps(lowerer, tableName);
|
|
156
|
+
const tempSteps = await tableExistsSteps(lowerer, tempName);
|
|
157
|
+
|
|
152
158
|
return {
|
|
153
159
|
id: `recreateTable.${tableName}`,
|
|
154
160
|
label: `Recreate table ${tableName}`,
|
|
@@ -156,13 +162,11 @@ export function recreateTable(args: RecreateTableArgs): Op {
|
|
|
156
162
|
operationClass,
|
|
157
163
|
target: { id: 'sqlite', details: buildTargetDetails('table', tableName) },
|
|
158
164
|
precheck: [
|
|
159
|
-
step(
|
|
160
|
-
`ensure table "${tableName}" exists`,
|
|
161
|
-
`SELECT COUNT(*) > 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapeLiteral(tableName)}'`,
|
|
162
|
-
),
|
|
165
|
+
step(`ensure table "${tableName}" exists`, tableSteps.present.sql, tableSteps.present.params),
|
|
163
166
|
step(
|
|
164
167
|
`ensure temp table "${tempName}" does not exist`,
|
|
165
|
-
|
|
168
|
+
tempSteps.absent.sql,
|
|
169
|
+
tempSteps.absent.params,
|
|
166
170
|
),
|
|
167
171
|
],
|
|
168
172
|
execute: [
|
|
@@ -179,13 +183,11 @@ export function recreateTable(args: RecreateTableArgs): Op {
|
|
|
179
183
|
...indexStatements,
|
|
180
184
|
],
|
|
181
185
|
postcheck: [
|
|
182
|
-
step(
|
|
183
|
-
`verify table "${tableName}" exists`,
|
|
184
|
-
`SELECT COUNT(*) > 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapeLiteral(tableName)}'`,
|
|
185
|
-
),
|
|
186
|
+
step(`verify table "${tableName}" exists`, tableSteps.present.sql, tableSteps.present.params),
|
|
186
187
|
step(
|
|
187
188
|
`verify temp table "${tempName}" is gone`,
|
|
188
|
-
|
|
189
|
+
tempSteps.absent.sql,
|
|
190
|
+
tempSteps.absent.params,
|
|
189
191
|
),
|
|
190
192
|
...postchecks,
|
|
191
193
|
],
|
|
@@ -8,12 +8,10 @@
|
|
|
8
8
|
* see `StorageColumn` or `storageTypes`.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
type StorageTable,
|
|
16
|
-
type StorageTypeInstance,
|
|
11
|
+
import type {
|
|
12
|
+
StorageColumn,
|
|
13
|
+
StorageTable,
|
|
14
|
+
StorageTypeInstance,
|
|
17
15
|
} from '@prisma-next/sql-contract/types';
|
|
18
16
|
import { escapeLiteral, quoteIdentifier } from '../sql-utils';
|
|
19
17
|
|
|
@@ -46,7 +44,7 @@ function assertSafeDefaultExpression(expression: string): void {
|
|
|
46
44
|
*/
|
|
47
45
|
export function buildColumnTypeSql(
|
|
48
46
|
column: StorageColumn,
|
|
49
|
-
storageTypes: Record<string, StorageTypeInstance
|
|
47
|
+
storageTypes: Record<string, StorageTypeInstance> = {},
|
|
50
48
|
): string {
|
|
51
49
|
const resolved = resolveColumnTypeMetadata(column, storageTypes);
|
|
52
50
|
assertSafeNativeType(resolved.nativeType);
|
|
@@ -123,9 +121,9 @@ export function isInlineAutoincrementPrimaryKey(table: StorageTable, columnName:
|
|
|
123
121
|
|
|
124
122
|
type ResolvedColumnTypeMetadata = Pick<StorageColumn, 'nativeType' | 'codecId' | 'typeParams'>;
|
|
125
123
|
|
|
126
|
-
function resolveColumnTypeMetadata(
|
|
124
|
+
export function resolveColumnTypeMetadata(
|
|
127
125
|
column: StorageColumn,
|
|
128
|
-
storageTypes: Record<string, StorageTypeInstance
|
|
126
|
+
storageTypes: Record<string, StorageTypeInstance>,
|
|
129
127
|
): ResolvedColumnTypeMetadata {
|
|
130
128
|
if (!column.typeRef) {
|
|
131
129
|
return column;
|
|
@@ -136,13 +134,6 @@ function resolveColumnTypeMetadata(
|
|
|
136
134
|
`Storage type "${column.typeRef}" referenced by column is not defined in storage.types.`,
|
|
137
135
|
);
|
|
138
136
|
}
|
|
139
|
-
if (isPostgresEnumStorageEntry(referencedType)) {
|
|
140
|
-
return {
|
|
141
|
-
codecId: referencedType.codecId,
|
|
142
|
-
nativeType: referencedType.nativeType,
|
|
143
|
-
typeParams: { values: referencedType.values } as Record<string, unknown>,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
137
|
return {
|
|
147
138
|
codecId: referencedType.codecId,
|
|
148
139
|
nativeType: referencedType.nativeType,
|
|
@@ -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 {
|
|
@@ -23,7 +23,6 @@ import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-comp
|
|
|
23
23
|
import type { SchemaIssue } from '@prisma-next/framework-components/control';
|
|
24
24
|
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
25
25
|
import type {
|
|
26
|
-
PostgresEnumStorageEntry,
|
|
27
26
|
SqlStorage,
|
|
28
27
|
StorageTable,
|
|
29
28
|
StorageTypeInstance,
|
|
@@ -39,7 +38,7 @@ export interface StrategyContext {
|
|
|
39
38
|
readonly toContract: Contract<SqlStorage>;
|
|
40
39
|
readonly fromContract: Contract<SqlStorage> | null;
|
|
41
40
|
readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;
|
|
42
|
-
readonly storageTypes: Readonly<Record<string, StorageTypeInstance
|
|
41
|
+
readonly storageTypes: Readonly<Record<string, StorageTypeInstance>>;
|
|
43
42
|
readonly schema: SqlSchemaIR;
|
|
44
43
|
readonly policy: MigrationOperationPolicy;
|
|
45
44
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
@@ -61,7 +60,8 @@ export function tableAt(
|
|
|
61
60
|
namespaceId: string,
|
|
62
61
|
tableName: string,
|
|
63
62
|
): StorageTable | undefined {
|
|
64
|
-
|
|
63
|
+
const ns = storage.namespaces[namespaceId];
|
|
64
|
+
return ns !== undefined ? ns.entries.table?.[tableName] : undefined;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
@@ -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,13 +1,131 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MigrationOperationClass,
|
|
3
|
+
SqlMigrationPlanOperation,
|
|
4
|
+
} from '@prisma-next/family-sql/control';
|
|
5
|
+
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
1
6
|
import { Migration as SqlMigration } from '@prisma-next/family-sql/migration';
|
|
7
|
+
import type { ControlStack } from '@prisma-next/framework-components/control';
|
|
8
|
+
import type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
|
|
9
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
10
|
+
import { errorSqliteMigrationStackMissing } from '../errors';
|
|
11
|
+
import {
|
|
12
|
+
AddColumnCall,
|
|
13
|
+
CreateIndexCall,
|
|
14
|
+
CreateTableCall,
|
|
15
|
+
DropColumnCall,
|
|
16
|
+
DropIndexCall,
|
|
17
|
+
DropTableCall,
|
|
18
|
+
RecreateTableCall,
|
|
19
|
+
} from './op-factory-call';
|
|
20
|
+
import type { SqliteColumnSpec, SqliteIndexSpec, SqliteTableSpec } from './operations/shared';
|
|
2
21
|
import type { SqlitePlanTargetDetails } from './planner-target-details';
|
|
3
22
|
|
|
23
|
+
type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
|
|
24
|
+
|
|
4
25
|
/**
|
|
5
26
|
* Target-owned base class for SQLite migrations. Fixes the `SqlMigration`
|
|
6
27
|
* generic to `SqlitePlanTargetDetails` and the abstract `targetId` to the
|
|
7
28
|
* SQLite literal, so both user-authored migrations and renderer-generated
|
|
8
29
|
* scaffolds can extend `SqliteMigration` directly without redeclaring
|
|
9
30
|
* target-local identity.
|
|
31
|
+
*
|
|
32
|
+
* The constructor materializes a single SQLite `SqlControlAdapter` from
|
|
33
|
+
* `stack.adapter.create(stack)` and stores it; the protected instance methods
|
|
34
|
+
* forward to the corresponding `*Call` with that stored adapter, so user
|
|
35
|
+
* migrations can write `this.createTable({...})` without threading the adapter
|
|
36
|
+
* through every call.
|
|
10
37
|
*/
|
|
11
|
-
export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetails> {
|
|
38
|
+
export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetails, 'sqlite'> {
|
|
12
39
|
readonly targetId = 'sqlite' as const;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Materialized SQLite control adapter, created once per migration
|
|
43
|
+
* instance from the injected stack. `undefined` only when the migration
|
|
44
|
+
* was instantiated without a stack (test fixtures); the operation methods
|
|
45
|
+
* throw in that case to surface the misuse.
|
|
46
|
+
*/
|
|
47
|
+
protected readonly controlAdapter: SqlControlAdapter<'sqlite'> | undefined;
|
|
48
|
+
|
|
49
|
+
constructor(stack?: ControlStack<'sql', 'sqlite'>) {
|
|
50
|
+
super(stack);
|
|
51
|
+
this.controlAdapter = stack?.adapter
|
|
52
|
+
? blindCast<
|
|
53
|
+
SqlControlAdapter<'sqlite'>,
|
|
54
|
+
'The SQLite descriptor create() returns SqlControlAdapter<sqlite>; typed as wider ControlAdapterInstance at the framework boundary'
|
|
55
|
+
>(stack.adapter.create(stack))
|
|
56
|
+
: undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
protected createTable(options: {
|
|
60
|
+
readonly table: string;
|
|
61
|
+
readonly ifNotExists?: boolean;
|
|
62
|
+
readonly columns: readonly DdlColumn[];
|
|
63
|
+
readonly constraints?: readonly DdlTableConstraint[];
|
|
64
|
+
}): Promise<Op> {
|
|
65
|
+
if (!this.controlAdapter) {
|
|
66
|
+
throw errorSqliteMigrationStackMissing();
|
|
67
|
+
}
|
|
68
|
+
return new CreateTableCall(options.table, options.columns, options.constraints).toOp(
|
|
69
|
+
this.controlAdapter,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected dropTable(options: { readonly table: string }): Promise<Op> {
|
|
74
|
+
if (!this.controlAdapter) {
|
|
75
|
+
throw errorSqliteMigrationStackMissing();
|
|
76
|
+
}
|
|
77
|
+
return new DropTableCall(options.table).toOp(this.controlAdapter);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected addColumn(options: {
|
|
81
|
+
readonly table: string;
|
|
82
|
+
readonly column: SqliteColumnSpec;
|
|
83
|
+
}): Promise<Op> {
|
|
84
|
+
if (!this.controlAdapter) {
|
|
85
|
+
throw errorSqliteMigrationStackMissing();
|
|
86
|
+
}
|
|
87
|
+
return new AddColumnCall(options.table, options.column).toOp(this.controlAdapter);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
protected dropColumn(options: { readonly table: string; readonly column: string }): Promise<Op> {
|
|
91
|
+
if (!this.controlAdapter) {
|
|
92
|
+
throw errorSqliteMigrationStackMissing();
|
|
93
|
+
}
|
|
94
|
+
return new DropColumnCall(options.table, options.column).toOp(this.controlAdapter);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
protected createIndex(options: {
|
|
98
|
+
readonly table: string;
|
|
99
|
+
readonly index: string;
|
|
100
|
+
readonly columns: readonly string[];
|
|
101
|
+
}): Promise<Op> {
|
|
102
|
+
if (!this.controlAdapter) {
|
|
103
|
+
throw errorSqliteMigrationStackMissing();
|
|
104
|
+
}
|
|
105
|
+
return new CreateIndexCall(options.table, options.index, options.columns).toOp(
|
|
106
|
+
this.controlAdapter,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected dropIndex(options: { readonly table: string; readonly index: string }): Promise<Op> {
|
|
111
|
+
if (!this.controlAdapter) {
|
|
112
|
+
throw errorSqliteMigrationStackMissing();
|
|
113
|
+
}
|
|
114
|
+
return new DropIndexCall(options.table, options.index).toOp(this.controlAdapter);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected recreateTable(options: {
|
|
118
|
+
readonly tableName: string;
|
|
119
|
+
readonly contractTable: SqliteTableSpec;
|
|
120
|
+
readonly schemaColumnNames: readonly string[];
|
|
121
|
+
readonly indexes: readonly SqliteIndexSpec[];
|
|
122
|
+
readonly summary: string;
|
|
123
|
+
readonly postchecks: readonly { readonly description: string; readonly sql: string }[];
|
|
124
|
+
readonly operationClass: MigrationOperationClass;
|
|
125
|
+
}): Promise<Op> {
|
|
126
|
+
if (!this.controlAdapter) {
|
|
127
|
+
throw errorSqliteMigrationStackMissing();
|
|
128
|
+
}
|
|
129
|
+
return new RecreateTableCall(options).toOp(this.controlAdapter);
|
|
130
|
+
}
|
|
13
131
|
}
|
|
@@ -3,6 +3,7 @@ import { SqlContractSerializerBase } from '@prisma-next/family-sql/ir';
|
|
|
3
3
|
import { type Namespace, NamespaceBase } from '@prisma-next/framework-components/ir';
|
|
4
4
|
import type { SqlNamespaceTablesInput, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
5
5
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
6
|
+
import { sqliteTargetDescriptorMeta } from './descriptor-meta';
|
|
6
7
|
import { buildSqliteNamespace } from './sqlite-unbound-database';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -16,6 +17,10 @@ export class SqliteContractSerializer extends SqlContractSerializerBase<Contract
|
|
|
16
17
|
super(new Map());
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
protected override get defaultNamespaceId(): string {
|
|
21
|
+
return sqliteTargetDescriptorMeta.defaultNamespaceId;
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
protected override hydrateSqlNamespaceEntry(
|
|
20
25
|
nsId: string,
|
|
21
26
|
raw: Namespace | Record<string, unknown>,
|