@prisma-next/target-sqlite 0.13.0-dev.35 → 0.13.0-dev.36
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 -3
- package/dist/control.mjs +2 -2
- package/dist/ddl-DrtjQMFK.mjs +68 -0
- package/dist/ddl-DrtjQMFK.mjs.map +1 -0
- package/dist/migration.d.mts +3 -45
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +3 -3
- package/dist/{op-factory-call-z4TT72k3.mjs → op-factory-call-DmdfD1yd.mjs} +143 -104
- package/dist/op-factory-call-DmdfD1yd.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +12 -6
- package/dist/op-factory-call.d.mts.map +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/{planner-jMHqfl1A.mjs → planner-Ciq8p_dL.mjs} +3 -3
- package/dist/{planner-jMHqfl1A.mjs.map → planner-Ciq8p_dL.mjs.map} +1 -1
- package/dist/{planner-produced-sqlite-migration-CyyvoPmm.mjs → planner-produced-sqlite-migration-0xPEm3R1.mjs} +2 -2
- package/dist/{planner-produced-sqlite-migration-CyyvoPmm.mjs.map → planner-produced-sqlite-migration-0xPEm3R1.mjs.map} +1 -1
- package/dist/{planner-produced-sqlite-migration-BWpnDmhM.d.mts → planner-produced-sqlite-migration-CpgsY-M9.d.mts} +2 -2
- package/dist/{planner-produced-sqlite-migration-BWpnDmhM.d.mts.map → planner-produced-sqlite-migration-CpgsY-M9.d.mts.map} +1 -1
- package/dist/planner-produced-sqlite-migration.d.mts +1 -1
- package/dist/planner-produced-sqlite-migration.mjs +1 -1
- package/dist/planner.d.mts +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/shared-Dhc8mLK1.d.mts.map +1 -1
- package/dist/{sqlite-migration-DhW4ycZV.mjs → sqlite-migration-A0rwqPOG.mjs} +32 -13
- 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/migrations/op-factory-call.ts +191 -43
- 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/sqlite-migration.ts +82 -14
- package/src/exports/contract-free.ts +8 -0
- package/src/exports/migration.ts +0 -3
- package/dist/ddl-CH8V_qcd.mjs +0 -23
- package/dist/ddl-CH8V_qcd.mjs.map +0 -1
- package/dist/op-factory-call-z4TT72k3.mjs.map +0 -1
- package/dist/sqlite-migration-CJrASAxf.d.mts +0 -46
- package/dist/sqlite-migration-CJrASAxf.d.mts.map +0 -1
- package/dist/sqlite-migration-DhW4ycZV.mjs.map +0 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { t as SqlitePlanTargetDetails } from "./planner-target-details-xR6UfIcz.mjs";
|
|
2
|
+
import { i as SqliteTableSpec, n as SqliteColumnSpec, r as SqliteIndexSpec } from "./shared-Dhc8mLK1.mjs";
|
|
3
|
+
import { DdlColumn, DdlTableConstraint } from "@prisma-next/sql-relational-core/ast";
|
|
4
|
+
import { MigrationOperationClass, SqlMigrationPlanOperation } from "@prisma-next/family-sql/control";
|
|
5
|
+
import { Migration } from "@prisma-next/family-sql/migration";
|
|
6
|
+
import { ControlStack } from "@prisma-next/framework-components/control";
|
|
7
|
+
import { SqlControlAdapter } from "@prisma-next/family-sql/control-adapter";
|
|
8
|
+
|
|
9
|
+
//#region src/core/migrations/sqlite-migration.d.ts
|
|
10
|
+
type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
|
|
11
|
+
/**
|
|
12
|
+
* Target-owned base class for SQLite migrations. Fixes the `SqlMigration`
|
|
13
|
+
* generic to `SqlitePlanTargetDetails` and the abstract `targetId` to the
|
|
14
|
+
* SQLite literal, so both user-authored migrations and renderer-generated
|
|
15
|
+
* scaffolds can extend `SqliteMigration` directly without redeclaring
|
|
16
|
+
* target-local identity.
|
|
17
|
+
*
|
|
18
|
+
* The constructor materializes a single SQLite `SqlControlAdapter` from
|
|
19
|
+
* `stack.adapter.create(stack)` and stores it; the protected instance methods
|
|
20
|
+
* forward to the corresponding `*Call` with that stored adapter, so user
|
|
21
|
+
* migrations can write `this.createTable({...})` without threading the adapter
|
|
22
|
+
* through every call.
|
|
23
|
+
*/
|
|
24
|
+
declare abstract class SqliteMigration extends Migration<SqlitePlanTargetDetails, 'sqlite'> {
|
|
25
|
+
readonly targetId: "sqlite";
|
|
26
|
+
/**
|
|
27
|
+
* Materialized SQLite control adapter, created once per migration
|
|
28
|
+
* instance from the injected stack. `undefined` only when the migration
|
|
29
|
+
* was instantiated without a stack (test fixtures); the operation methods
|
|
30
|
+
* throw in that case to surface the misuse.
|
|
31
|
+
*/
|
|
32
|
+
protected readonly controlAdapter: SqlControlAdapter<'sqlite'> | undefined;
|
|
33
|
+
constructor(stack?: ControlStack<'sql', 'sqlite'>);
|
|
34
|
+
protected createTable(options: {
|
|
35
|
+
readonly table: string;
|
|
36
|
+
readonly ifNotExists?: boolean;
|
|
37
|
+
readonly columns: readonly DdlColumn[];
|
|
38
|
+
readonly constraints?: readonly DdlTableConstraint[];
|
|
39
|
+
}): Promise<Op>;
|
|
40
|
+
protected dropTable(options: {
|
|
41
|
+
readonly table: string;
|
|
42
|
+
}): Promise<Op>;
|
|
43
|
+
protected addColumn(options: {
|
|
44
|
+
readonly table: string;
|
|
45
|
+
readonly column: SqliteColumnSpec;
|
|
46
|
+
}): Promise<Op>;
|
|
47
|
+
protected dropColumn(options: {
|
|
48
|
+
readonly table: string;
|
|
49
|
+
readonly column: string;
|
|
50
|
+
}): Promise<Op>;
|
|
51
|
+
protected createIndex(options: {
|
|
52
|
+
readonly table: string;
|
|
53
|
+
readonly index: string;
|
|
54
|
+
readonly columns: readonly string[];
|
|
55
|
+
}): Promise<Op>;
|
|
56
|
+
protected dropIndex(options: {
|
|
57
|
+
readonly table: string;
|
|
58
|
+
readonly index: string;
|
|
59
|
+
}): Promise<Op>;
|
|
60
|
+
protected recreateTable(options: {
|
|
61
|
+
readonly tableName: string;
|
|
62
|
+
readonly contractTable: SqliteTableSpec;
|
|
63
|
+
readonly schemaColumnNames: readonly string[];
|
|
64
|
+
readonly indexes: readonly SqliteIndexSpec[];
|
|
65
|
+
readonly summary: string;
|
|
66
|
+
readonly postchecks: readonly {
|
|
67
|
+
readonly description: string;
|
|
68
|
+
readonly sql: string;
|
|
69
|
+
}[];
|
|
70
|
+
readonly operationClass: MigrationOperationClass;
|
|
71
|
+
}): Promise<Op>;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { SqliteMigration as t };
|
|
75
|
+
//# sourceMappingURL=sqlite-migration-DVfhQwN_.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-migration-DVfhQwN_.d.mts","names":[],"sources":["../src/core/migrations/sqlite-migration.ts"],"mappings":";;;;;;;;;KAsBK,EAAA,GAAK,yBAAyB,CAAC,uBAAA;;AAFoC;;;;AAEb;AAe3D;;;;;;;uBAAsB,eAAA,SAAwB,SAAA,CAAa,uBAAA;EAAA,SAChD,QAAA;EAyBL;;;;;;EAAA,mBAjBe,cAAA,EAAgB,iBAAA;cAEvB,KAAA,GAAQ,YAAA;EAAA,UAUV,WAAA,CAAY,OAAA;IAAA,SACX,KAAA;IAAA,SACA,WAAA;IAAA,SACA,OAAA,WAAkB,SAAA;IAAA,SAClB,WAAA,YAAuB,kBAAA;EAAA,IAC9B,OAAA,CAAQ,EAAA;EAAA,UASF,SAAA,CAAU,OAAA;IAAA,SAAoB,KAAA;EAAA,IAAkB,OAAA,CAAQ,EAAA;EAAA,UAOxD,SAAA,CAAU,OAAA;IAAA,SACT,KAAA;IAAA,SACA,MAAA,EAAQ,gBAAA;EAAA,IACf,OAAA,CAAQ,EAAA;EAAA,UAOF,UAAA,CAAW,OAAA;IAAA,SAAoB,KAAA;IAAA,SAAwB,MAAA;EAAA,IAAmB,OAAA,CAAQ,EAAA;EAAA,UAOlF,WAAA,CAAY,OAAA;IAAA,SACX,KAAA;IAAA,SACA,KAAA;IAAA,SACA,OAAA;EAAA,IACP,OAAA,CAAQ,EAAA;EAAA,UASF,SAAA,CAAU,OAAA;IAAA,SAAoB,KAAA;IAAA,SAAwB,KAAA;EAAA,IAAkB,OAAA,CAAQ,EAAA;EAAA,UAOhF,aAAA,CAAc,OAAA;IAAA,SACb,SAAA;IAAA,SACA,aAAA,EAAe,eAAA;IAAA,SACf,iBAAA;IAAA,SACA,OAAA,WAAkB,eAAA;IAAA,SAClB,OAAA;IAAA,SACA,UAAA;MAAA,SAAgC,WAAA;MAAA,SAA8B,GAAA;IAAA;IAAA,SAC9D,cAAA,EAAgB,uBAAA;EAAA,IACvB,OAAA,CAAQ,EAAA;AAAA"}
|
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/target-sqlite",
|
|
3
|
-
"version": "0.13.0-dev.
|
|
3
|
+
"version": "0.13.0-dev.36",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@prisma-next/cli": "0.13.0-dev.
|
|
9
|
-
"@prisma-next/contract": "0.13.0-dev.
|
|
10
|
-
"@prisma-next/errors": "0.13.0-dev.
|
|
11
|
-
"@prisma-next/family-sql": "0.13.0-dev.
|
|
12
|
-
"@prisma-next/framework-components": "0.13.0-dev.
|
|
13
|
-
"@prisma-next/migration-tools": "0.13.0-dev.
|
|
14
|
-
"@prisma-next/sql-contract": "0.13.0-dev.
|
|
15
|
-
"@prisma-next/sql-errors": "0.13.0-dev.
|
|
16
|
-
"@prisma-next/sql-relational-core": "0.13.0-dev.
|
|
17
|
-
"@prisma-next/sql-runtime": "0.13.0-dev.
|
|
18
|
-
"@prisma-next/sql-schema-ir": "0.13.0-dev.
|
|
19
|
-
"@prisma-next/ts-render": "0.13.0-dev.
|
|
20
|
-
"@prisma-next/utils": "0.13.0-dev.
|
|
8
|
+
"@prisma-next/cli": "0.13.0-dev.36",
|
|
9
|
+
"@prisma-next/contract": "0.13.0-dev.36",
|
|
10
|
+
"@prisma-next/errors": "0.13.0-dev.36",
|
|
11
|
+
"@prisma-next/family-sql": "0.13.0-dev.36",
|
|
12
|
+
"@prisma-next/framework-components": "0.13.0-dev.36",
|
|
13
|
+
"@prisma-next/migration-tools": "0.13.0-dev.36",
|
|
14
|
+
"@prisma-next/sql-contract": "0.13.0-dev.36",
|
|
15
|
+
"@prisma-next/sql-errors": "0.13.0-dev.36",
|
|
16
|
+
"@prisma-next/sql-relational-core": "0.13.0-dev.36",
|
|
17
|
+
"@prisma-next/sql-runtime": "0.13.0-dev.36",
|
|
18
|
+
"@prisma-next/sql-schema-ir": "0.13.0-dev.36",
|
|
19
|
+
"@prisma-next/ts-render": "0.13.0-dev.36",
|
|
20
|
+
"@prisma-next/utils": "0.13.0-dev.36",
|
|
21
21
|
"@standard-schema/spec": "1.1.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@prisma-next/driver-sqlite": "0.13.0-dev.
|
|
25
|
-
"@prisma-next/test-utils": "0.13.0-dev.
|
|
26
|
-
"@prisma-next/tsconfig": "0.13.0-dev.
|
|
27
|
-
"@prisma-next/tsdown": "0.13.0-dev.
|
|
24
|
+
"@prisma-next/driver-sqlite": "0.13.0-dev.36",
|
|
25
|
+
"@prisma-next/test-utils": "0.13.0-dev.36",
|
|
26
|
+
"@prisma-next/tsconfig": "0.13.0-dev.36",
|
|
27
|
+
"@prisma-next/tsdown": "0.13.0-dev.36",
|
|
28
28
|
"tsdown": "0.22.1",
|
|
29
29
|
"typescript": "5.9.3",
|
|
30
30
|
"vitest": "4.1.8"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { FunctionSource, type SelectAst } from '@prisma-next/sql-relational-core/ast';
|
|
2
|
+
import { cfExpr, cfTable, exprSelect } from '@prisma-next/sql-relational-core/contract-free';
|
|
3
|
+
import { SQLITE_TEXT_CODEC_ID } from '../core/codec-ids';
|
|
4
|
+
|
|
5
|
+
export interface ColumnExistsCheckBuilder {
|
|
6
|
+
columnAbsent(): SelectAst;
|
|
7
|
+
columnPresent(): SelectAst;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Typed builder for the migration planner's column-existence checks. Produces
|
|
12
|
+
* `SELECT COUNT(*) {=|>} 0 AS "result" FROM pragma_table_info(?) WHERE "name" = ?`
|
|
13
|
+
* ASTs with the table and column names bound as text parameters — never
|
|
14
|
+
* inlined into the SQL.
|
|
15
|
+
*/
|
|
16
|
+
export function columnExistsAst(table: string, column: string): ColumnExistsCheckBuilder {
|
|
17
|
+
const source = FunctionSource.of('pragma_table_info', [
|
|
18
|
+
cfExpr.param(table, SQLITE_TEXT_CODEC_ID).ast,
|
|
19
|
+
]);
|
|
20
|
+
const where = cfExpr.identifierRef('name').eqParam(column, SQLITE_TEXT_CODEC_ID);
|
|
21
|
+
return {
|
|
22
|
+
columnAbsent: () =>
|
|
23
|
+
exprSelect().from(source).project('result', cfExpr.countStar().eqLit(0)).where(where).build(),
|
|
24
|
+
columnPresent: () =>
|
|
25
|
+
exprSelect().from(source).project('result', cfExpr.countStar().gtLit(0)).where(where).build(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TableExistsCheckBuilder {
|
|
30
|
+
tableAbsent(): SelectAst;
|
|
31
|
+
tablePresent(): SelectAst;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Typed builder for table-existence checks over `sqlite_master`.
|
|
36
|
+
* Produces `SELECT COUNT(*) {=|>} 0 AS "result" FROM "sqlite_master" WHERE "type" = ? AND "name" = ?`
|
|
37
|
+
* with the table name and the literal `'table'` bound as text parameters.
|
|
38
|
+
*/
|
|
39
|
+
export function tableExistsAst(tableName: string): TableExistsCheckBuilder {
|
|
40
|
+
const source = cfTable('sqlite_master');
|
|
41
|
+
const where = cfExpr.allOf([
|
|
42
|
+
cfExpr.identifierRef('type').eqParam('table', SQLITE_TEXT_CODEC_ID),
|
|
43
|
+
cfExpr.identifierRef('name').eqParam(tableName, SQLITE_TEXT_CODEC_ID),
|
|
44
|
+
]);
|
|
45
|
+
return {
|
|
46
|
+
tableAbsent: () =>
|
|
47
|
+
exprSelect().from(source).project('result', cfExpr.countStar().eqLit(0)).where(where).build(),
|
|
48
|
+
tablePresent: () =>
|
|
49
|
+
exprSelect().from(source).project('result', cfExpr.countStar().gtLit(0)).where(where).build(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface IndexExistsCheckBuilder {
|
|
54
|
+
indexAbsent(): SelectAst;
|
|
55
|
+
indexPresent(): SelectAst;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Typed builder for index-existence checks over `sqlite_master`.
|
|
60
|
+
* Produces `SELECT COUNT(*) {=|>} 0 AS "result" FROM "sqlite_master" WHERE "type" = ? AND "name" = ?`
|
|
61
|
+
* with the index name and the literal `'index'` bound as text parameters.
|
|
62
|
+
*/
|
|
63
|
+
export function indexExistsAst(indexName: string): IndexExistsCheckBuilder {
|
|
64
|
+
const source = cfTable('sqlite_master');
|
|
65
|
+
const where = cfExpr.allOf([
|
|
66
|
+
cfExpr.identifierRef('type').eqParam('index', SQLITE_TEXT_CODEC_ID),
|
|
67
|
+
cfExpr.identifierRef('name').eqParam(indexName, SQLITE_TEXT_CODEC_ID),
|
|
68
|
+
]);
|
|
69
|
+
return {
|
|
70
|
+
indexAbsent: () =>
|
|
71
|
+
exprSelect().from(source).project('result', cfExpr.countStar().eqLit(0)).where(where).build(),
|
|
72
|
+
indexPresent: () =>
|
|
73
|
+
exprSelect().from(source).project('result', cfExpr.countStar().gtLit(0)).where(where).build(),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -23,13 +23,14 @@ import type {
|
|
|
23
23
|
} from '@prisma-next/sql-relational-core/ast';
|
|
24
24
|
import { type ImportRequirement, jsonToTsSource, TsExpression } from '@prisma-next/ts-render';
|
|
25
25
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
26
|
+
import { columnExistsAst, indexExistsAst, tableExistsAst } from '../../contract-free/checks';
|
|
26
27
|
import * as contractFreeDdl from '../../contract-free/ddl';
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import { createIndex, dropIndex } from './operations/indexes';
|
|
28
|
+
import { quoteIdentifier } from '../sql-utils';
|
|
29
|
+
import { addColumnExecuteSql, dropColumnExecuteSql } from './operations/columns';
|
|
30
30
|
import type { SqliteColumnSpec, SqliteIndexSpec, SqliteTableSpec } from './operations/shared';
|
|
31
31
|
import { step } from './operations/shared';
|
|
32
|
-
import {
|
|
32
|
+
import { recreateTable } from './operations/tables';
|
|
33
|
+
import { buildCreateIndexSql, buildDropIndexSql } from './planner-ddl-builders';
|
|
33
34
|
import type { SqlitePlanTargetDetails } from './planner-target-details';
|
|
34
35
|
import { buildTargetDetails } from './planner-target-details';
|
|
35
36
|
|
|
@@ -152,19 +153,16 @@ export class CreateTableCall extends SqliteOpFactoryCallNode {
|
|
|
152
153
|
});
|
|
153
154
|
const statement = await lowerer.lowerToExecuteRequest(ddlNode);
|
|
154
155
|
const tableName = this.tableName;
|
|
155
|
-
const
|
|
156
|
+
const tableChecks = tableExistsAst(tableName);
|
|
157
|
+
const absent = await lowerer.lowerToExecuteRequest(tableChecks.tableAbsent());
|
|
158
|
+
const present = await lowerer.lowerToExecuteRequest(tableChecks.tablePresent());
|
|
156
159
|
return {
|
|
157
160
|
id: `table.${tableName}`,
|
|
158
161
|
label: `Create table ${tableName}`,
|
|
159
162
|
summary: `Creates table ${tableName} with required columns`,
|
|
160
163
|
operationClass: 'additive',
|
|
161
164
|
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
|
-
],
|
|
165
|
+
precheck: [step(`ensure table "${tableName}" does not exist`, absent.sql, absent.params)],
|
|
168
166
|
execute: [
|
|
169
167
|
{
|
|
170
168
|
description: `create table "${tableName}"`,
|
|
@@ -172,12 +170,7 @@ export class CreateTableCall extends SqliteOpFactoryCallNode {
|
|
|
172
170
|
params: statement.params ?? [],
|
|
173
171
|
},
|
|
174
172
|
],
|
|
175
|
-
postcheck: [
|
|
176
|
-
step(
|
|
177
|
-
`verify table "${tableName}" exists`,
|
|
178
|
-
`SELECT COUNT(*) > 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapedName}'`,
|
|
179
|
-
),
|
|
180
|
-
],
|
|
173
|
+
postcheck: [step(`verify table "${tableName}" exists`, present.sql, present.params)],
|
|
181
174
|
};
|
|
182
175
|
}
|
|
183
176
|
|
|
@@ -223,12 +216,35 @@ export class DropTableCall extends SqliteOpFactoryCallNode {
|
|
|
223
216
|
this.freeze();
|
|
224
217
|
}
|
|
225
218
|
|
|
226
|
-
toOp(
|
|
227
|
-
|
|
219
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
220
|
+
if (lowerer === undefined) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`DropTableCall.toOp: a lowerer is required on the SQLite planner path (table "${this.tableName}"). Pass the control adapter to createSqliteMigrationPlanner.`,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
const checks = tableExistsAst(this.tableName);
|
|
226
|
+
const present = await lowerer.lowerToExecuteRequest(checks.tablePresent());
|
|
227
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.tableAbsent());
|
|
228
|
+
return {
|
|
229
|
+
id: `dropTable.${this.tableName}`,
|
|
230
|
+
label: `Drop table ${this.tableName}`,
|
|
231
|
+
summary: `Drops table ${this.tableName} which is not in the contract`,
|
|
232
|
+
operationClass: 'destructive',
|
|
233
|
+
target: { id: 'sqlite', details: buildTargetDetails('table', this.tableName) },
|
|
234
|
+
precheck: [step(`ensure table "${this.tableName}" exists`, present.sql, present.params)],
|
|
235
|
+
execute: [
|
|
236
|
+
step(`drop table "${this.tableName}"`, `DROP TABLE ${quoteIdentifier(this.tableName)}`),
|
|
237
|
+
],
|
|
238
|
+
postcheck: [step(`verify table "${this.tableName}" is gone`, absent.sql, absent.params)],
|
|
239
|
+
};
|
|
228
240
|
}
|
|
229
241
|
|
|
230
242
|
renderTypeScript(): string {
|
|
231
|
-
return `dropTable(${jsonToTsSource(this.tableName)})`;
|
|
243
|
+
return `this.dropTable({ table: ${jsonToTsSource(this.tableName)} })`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
247
|
+
return [];
|
|
232
248
|
}
|
|
233
249
|
}
|
|
234
250
|
|
|
@@ -264,16 +280,24 @@ export class RecreateTableCall extends SqliteOpFactoryCallNode {
|
|
|
264
280
|
this.freeze();
|
|
265
281
|
}
|
|
266
282
|
|
|
267
|
-
toOp(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
283
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
284
|
+
if (lowerer === undefined) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`RecreateTableCall.toOp: a lowerer is required on the SQLite planner path (table "${this.tableName}"). Pass the control adapter to createSqliteMigrationPlanner.`,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
return recreateTable(
|
|
290
|
+
{
|
|
291
|
+
tableName: this.tableName,
|
|
292
|
+
contractTable: this.contractTable,
|
|
293
|
+
schemaColumnNames: this.schemaColumnNames,
|
|
294
|
+
indexes: this.indexes,
|
|
295
|
+
summary: this.summary,
|
|
296
|
+
postchecks: this.postchecks,
|
|
297
|
+
operationClass: this.operationClass,
|
|
298
|
+
},
|
|
299
|
+
lowerer,
|
|
300
|
+
);
|
|
277
301
|
}
|
|
278
302
|
|
|
279
303
|
renderTypeScript(): string {
|
|
@@ -286,7 +310,11 @@ export class RecreateTableCall extends SqliteOpFactoryCallNode {
|
|
|
286
310
|
postchecks: this.postchecks,
|
|
287
311
|
operationClass: this.operationClass,
|
|
288
312
|
};
|
|
289
|
-
return `recreateTable(${jsonToTsSource(args)})`;
|
|
313
|
+
return `this.recreateTable(${jsonToTsSource(args)})`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
317
|
+
return [];
|
|
290
318
|
}
|
|
291
319
|
}
|
|
292
320
|
|
|
@@ -311,12 +339,38 @@ export class AddColumnCall extends SqliteOpFactoryCallNode {
|
|
|
311
339
|
this.freeze();
|
|
312
340
|
}
|
|
313
341
|
|
|
314
|
-
toOp(
|
|
315
|
-
|
|
342
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
343
|
+
if (lowerer === undefined) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
`AddColumnCall.toOp: a lowerer is required on the SQLite planner path (column "${this.column.name}" on table "${this.tableName}"). Pass the control adapter to createSqliteMigrationPlanner.`,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
const checks = columnExistsAst(this.tableName, this.column.name);
|
|
349
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.columnAbsent());
|
|
350
|
+
const present = await lowerer.lowerToExecuteRequest(checks.columnPresent());
|
|
351
|
+
return {
|
|
352
|
+
id: `column.${this.tableName}.${this.column.name}`,
|
|
353
|
+
label: `Add column ${this.column.name} on ${this.tableName}`,
|
|
354
|
+
summary: `Adds column ${this.column.name} on ${this.tableName}`,
|
|
355
|
+
operationClass: 'additive',
|
|
356
|
+
target: {
|
|
357
|
+
id: 'sqlite',
|
|
358
|
+
details: buildTargetDetails('column', this.column.name, this.tableName),
|
|
359
|
+
},
|
|
360
|
+
precheck: [step(`ensure column "${this.column.name}" is missing`, absent.sql, absent.params)],
|
|
361
|
+
execute: [
|
|
362
|
+
step(`add column "${this.column.name}"`, addColumnExecuteSql(this.tableName, this.column)),
|
|
363
|
+
],
|
|
364
|
+
postcheck: [step(`verify column "${this.column.name}" exists`, present.sql, present.params)],
|
|
365
|
+
};
|
|
316
366
|
}
|
|
317
367
|
|
|
318
368
|
renderTypeScript(): string {
|
|
319
|
-
return `addColumn(${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.column)})`;
|
|
369
|
+
return `this.addColumn({ table: ${jsonToTsSource(this.tableName)}, column: ${jsonToTsSource(this.column)} })`;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
373
|
+
return [];
|
|
320
374
|
}
|
|
321
375
|
}
|
|
322
376
|
|
|
@@ -335,12 +389,53 @@ export class DropColumnCall extends SqliteOpFactoryCallNode {
|
|
|
335
389
|
this.freeze();
|
|
336
390
|
}
|
|
337
391
|
|
|
338
|
-
toOp(
|
|
339
|
-
|
|
392
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
393
|
+
if (lowerer === undefined) {
|
|
394
|
+
throw new Error(
|
|
395
|
+
`DropColumnCall.toOp: a lowerer is required on the SQLite planner path (column "${this.columnName}" on table "${this.tableName}"). Pass the control adapter to createSqliteMigrationPlanner.`,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
const checks = columnExistsAst(this.tableName, this.columnName);
|
|
399
|
+
const present = await lowerer.lowerToExecuteRequest(checks.columnPresent());
|
|
400
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.columnAbsent());
|
|
401
|
+
return {
|
|
402
|
+
id: `dropColumn.${this.tableName}.${this.columnName}`,
|
|
403
|
+
label: `Drop column ${this.columnName} on ${this.tableName}`,
|
|
404
|
+
summary: `Drops column ${this.columnName} on ${this.tableName} which is not in the contract`,
|
|
405
|
+
operationClass: 'destructive',
|
|
406
|
+
target: {
|
|
407
|
+
id: 'sqlite',
|
|
408
|
+
details: buildTargetDetails('column', this.columnName, this.tableName),
|
|
409
|
+
},
|
|
410
|
+
precheck: [
|
|
411
|
+
step(
|
|
412
|
+
`ensure column "${this.columnName}" exists on "${this.tableName}"`,
|
|
413
|
+
present.sql,
|
|
414
|
+
present.params,
|
|
415
|
+
),
|
|
416
|
+
],
|
|
417
|
+
execute: [
|
|
418
|
+
step(
|
|
419
|
+
`drop column "${this.columnName}" from "${this.tableName}"`,
|
|
420
|
+
dropColumnExecuteSql(this.tableName, this.columnName),
|
|
421
|
+
),
|
|
422
|
+
],
|
|
423
|
+
postcheck: [
|
|
424
|
+
step(
|
|
425
|
+
`verify column "${this.columnName}" is gone from "${this.tableName}"`,
|
|
426
|
+
absent.sql,
|
|
427
|
+
absent.params,
|
|
428
|
+
),
|
|
429
|
+
],
|
|
430
|
+
};
|
|
340
431
|
}
|
|
341
432
|
|
|
342
433
|
renderTypeScript(): string {
|
|
343
|
-
return `dropColumn(${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.columnName)})`;
|
|
434
|
+
return `this.dropColumn({ table: ${jsonToTsSource(this.tableName)}, column: ${jsonToTsSource(this.columnName)} })`;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
438
|
+
return [];
|
|
344
439
|
}
|
|
345
440
|
}
|
|
346
441
|
|
|
@@ -365,12 +460,41 @@ export class CreateIndexCall extends SqliteOpFactoryCallNode {
|
|
|
365
460
|
this.freeze();
|
|
366
461
|
}
|
|
367
462
|
|
|
368
|
-
toOp(
|
|
369
|
-
|
|
463
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
464
|
+
if (lowerer === undefined) {
|
|
465
|
+
throw new Error(
|
|
466
|
+
`CreateIndexCall.toOp: a lowerer is required on the SQLite planner path (index "${this.indexName}" on table "${this.tableName}"). Pass the control adapter to createSqliteMigrationPlanner.`,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
const checks = indexExistsAst(this.indexName);
|
|
470
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.indexAbsent());
|
|
471
|
+
const present = await lowerer.lowerToExecuteRequest(checks.indexPresent());
|
|
472
|
+
return {
|
|
473
|
+
id: `index.${this.tableName}.${this.indexName}`,
|
|
474
|
+
label: `Create index ${this.indexName} on ${this.tableName}`,
|
|
475
|
+
summary: `Creates index ${this.indexName} on ${this.tableName}`,
|
|
476
|
+
operationClass: 'additive',
|
|
477
|
+
target: {
|
|
478
|
+
id: 'sqlite',
|
|
479
|
+
details: buildTargetDetails('index', this.indexName, this.tableName),
|
|
480
|
+
},
|
|
481
|
+
precheck: [step(`ensure index "${this.indexName}" is missing`, absent.sql, absent.params)],
|
|
482
|
+
execute: [
|
|
483
|
+
step(
|
|
484
|
+
`create index "${this.indexName}"`,
|
|
485
|
+
buildCreateIndexSql(this.tableName, this.indexName, this.columns),
|
|
486
|
+
),
|
|
487
|
+
],
|
|
488
|
+
postcheck: [step(`verify index "${this.indexName}" exists`, present.sql, present.params)],
|
|
489
|
+
};
|
|
370
490
|
}
|
|
371
491
|
|
|
372
492
|
renderTypeScript(): string {
|
|
373
|
-
return `createIndex(${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.indexName)}, ${jsonToTsSource(this.columns)})`;
|
|
493
|
+
return `this.createIndex({ table: ${jsonToTsSource(this.tableName)}, index: ${jsonToTsSource(this.indexName)}, columns: ${jsonToTsSource(this.columns)} })`;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
497
|
+
return [];
|
|
374
498
|
}
|
|
375
499
|
}
|
|
376
500
|
|
|
@@ -389,12 +513,36 @@ export class DropIndexCall extends SqliteOpFactoryCallNode {
|
|
|
389
513
|
this.freeze();
|
|
390
514
|
}
|
|
391
515
|
|
|
392
|
-
toOp(
|
|
393
|
-
|
|
516
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
517
|
+
if (lowerer === undefined) {
|
|
518
|
+
throw new Error(
|
|
519
|
+
`DropIndexCall.toOp: a lowerer is required on the SQLite planner path (index "${this.indexName}" on table "${this.tableName}"). Pass the control adapter to createSqliteMigrationPlanner.`,
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
const checks = indexExistsAst(this.indexName);
|
|
523
|
+
const present = await lowerer.lowerToExecuteRequest(checks.indexPresent());
|
|
524
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.indexAbsent());
|
|
525
|
+
return {
|
|
526
|
+
id: `dropIndex.${this.tableName}.${this.indexName}`,
|
|
527
|
+
label: `Drop index ${this.indexName} on ${this.tableName}`,
|
|
528
|
+
summary: `Drops index ${this.indexName} on ${this.tableName} which is not in the contract`,
|
|
529
|
+
operationClass: 'destructive',
|
|
530
|
+
target: {
|
|
531
|
+
id: 'sqlite',
|
|
532
|
+
details: buildTargetDetails('index', this.indexName, this.tableName),
|
|
533
|
+
},
|
|
534
|
+
precheck: [step(`ensure index "${this.indexName}" exists`, present.sql, present.params)],
|
|
535
|
+
execute: [step(`drop index "${this.indexName}"`, buildDropIndexSql(this.indexName))],
|
|
536
|
+
postcheck: [step(`verify index "${this.indexName}" is gone`, absent.sql, absent.params)],
|
|
537
|
+
};
|
|
394
538
|
}
|
|
395
539
|
|
|
396
540
|
renderTypeScript(): string {
|
|
397
|
-
return `dropIndex(${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.indexName)})`;
|
|
541
|
+
return `this.dropIndex({ table: ${jsonToTsSource(this.tableName)}, index: ${jsonToTsSource(this.indexName)} })`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
545
|
+
return [];
|
|
398
546
|
}
|
|
399
547
|
}
|
|
400
548
|
|
|
@@ -1,39 +1,51 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
|
+
import { columnExistsAst } from '../../../contract-free/checks';
|
|
3
|
+
import { quoteIdentifier } from '../../sql-utils';
|
|
2
4
|
import { buildTargetDetails } from '../planner-target-details';
|
|
3
5
|
import { type Op, type SqliteColumnSpec, step } from './shared';
|
|
4
6
|
|
|
5
|
-
export function
|
|
7
|
+
export function addColumnExecuteSql(tableName: string, column: SqliteColumnSpec): string {
|
|
6
8
|
const parts = [
|
|
7
9
|
`ALTER TABLE ${quoteIdentifier(tableName)}`,
|
|
8
10
|
`ADD COLUMN ${quoteIdentifier(column.name)} ${column.typeSql}`,
|
|
9
11
|
column.defaultSql,
|
|
10
12
|
column.nullable ? '' : 'NOT NULL',
|
|
11
13
|
].filter(Boolean);
|
|
12
|
-
|
|
14
|
+
return parts.join(' ');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function dropColumnExecuteSql(tableName: string, columnName: string): string {
|
|
18
|
+
return `ALTER TABLE ${quoteIdentifier(tableName)} DROP COLUMN ${quoteIdentifier(columnName)}`;
|
|
19
|
+
}
|
|
13
20
|
|
|
21
|
+
export async function addColumn(
|
|
22
|
+
tableName: string,
|
|
23
|
+
column: SqliteColumnSpec,
|
|
24
|
+
lowerer: ExecuteRequestLowerer,
|
|
25
|
+
): Promise<Op> {
|
|
26
|
+
const checks = columnExistsAst(tableName, column.name);
|
|
27
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.columnAbsent());
|
|
28
|
+
const present = await lowerer.lowerToExecuteRequest(checks.columnPresent());
|
|
14
29
|
return {
|
|
15
30
|
id: `column.${tableName}.${column.name}`,
|
|
16
31
|
label: `Add column ${column.name} on ${tableName}`,
|
|
17
32
|
summary: `Adds column ${column.name} on ${tableName}`,
|
|
18
33
|
operationClass: 'additive',
|
|
19
34
|
target: { id: 'sqlite', details: buildTargetDetails('column', column.name, tableName) },
|
|
20
|
-
precheck: [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`SELECT COUNT(*) = 0 FROM pragma_table_info('${escapeLiteral(tableName)}') WHERE name = '${escapeLiteral(column.name)}'`,
|
|
24
|
-
),
|
|
25
|
-
],
|
|
26
|
-
execute: [step(`add column "${column.name}"`, addSql)],
|
|
27
|
-
postcheck: [
|
|
28
|
-
step(
|
|
29
|
-
`verify column "${column.name}" exists`,
|
|
30
|
-
`SELECT COUNT(*) > 0 FROM pragma_table_info('${escapeLiteral(tableName)}') WHERE name = '${escapeLiteral(column.name)}'`,
|
|
31
|
-
),
|
|
32
|
-
],
|
|
35
|
+
precheck: [step(`ensure column "${column.name}" is missing`, absent.sql, absent.params)],
|
|
36
|
+
execute: [step(`add column "${column.name}"`, addColumnExecuteSql(tableName, column))],
|
|
37
|
+
postcheck: [step(`verify column "${column.name}" exists`, present.sql, present.params)],
|
|
33
38
|
};
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
export function dropColumn(
|
|
41
|
+
export async function dropColumn(
|
|
42
|
+
tableName: string,
|
|
43
|
+
columnName: string,
|
|
44
|
+
lowerer: ExecuteRequestLowerer,
|
|
45
|
+
): Promise<Op> {
|
|
46
|
+
const checks = columnExistsAst(tableName, columnName);
|
|
47
|
+
const present = await lowerer.lowerToExecuteRequest(checks.columnPresent());
|
|
48
|
+
const absent = await lowerer.lowerToExecuteRequest(checks.columnAbsent());
|
|
37
49
|
return {
|
|
38
50
|
id: `dropColumn.${tableName}.${columnName}`,
|
|
39
51
|
label: `Drop column ${columnName} on ${tableName}`,
|
|
@@ -41,22 +53,16 @@ export function dropColumn(tableName: string, columnName: string): Op {
|
|
|
41
53
|
operationClass: 'destructive',
|
|
42
54
|
target: { id: 'sqlite', details: buildTargetDetails('column', columnName, tableName) },
|
|
43
55
|
precheck: [
|
|
44
|
-
step(
|
|
45
|
-
`ensure column "${columnName}" exists on "${tableName}"`,
|
|
46
|
-
`SELECT COUNT(*) > 0 FROM pragma_table_info('${escapeLiteral(tableName)}') WHERE name = '${escapeLiteral(columnName)}'`,
|
|
47
|
-
),
|
|
56
|
+
step(`ensure column "${columnName}" exists on "${tableName}"`, present.sql, present.params),
|
|
48
57
|
],
|
|
49
58
|
execute: [
|
|
50
59
|
step(
|
|
51
60
|
`drop column "${columnName}" from "${tableName}"`,
|
|
52
|
-
|
|
61
|
+
dropColumnExecuteSql(tableName, columnName),
|
|
53
62
|
),
|
|
54
63
|
],
|
|
55
64
|
postcheck: [
|
|
56
|
-
step(
|
|
57
|
-
`verify column "${columnName}" is gone from "${tableName}"`,
|
|
58
|
-
`SELECT COUNT(*) = 0 FROM pragma_table_info('${escapeLiteral(tableName)}') WHERE name = '${escapeLiteral(columnName)}'`,
|
|
59
|
-
),
|
|
65
|
+
step(`verify column "${columnName}" is gone from "${tableName}"`, absent.sql, absent.params),
|
|
60
66
|
],
|
|
61
67
|
};
|
|
62
68
|
}
|