@prisma/orm-target-sqlite 8.0.0-rc.17-dev.36 → 8.0.0-rc.17-dev.38
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/{adapter-Cfp6xn4m-BtODbERC.mjs → adapter-Cfp6xn4m-CqnRsbnV.mjs} +2 -2
- package/dist/{adapter-Cfp6xn4m-BtODbERC.mjs.map → adapter-Cfp6xn4m-CqnRsbnV.mjs.map} +1 -1
- package/dist/adapter.mjs +2 -2
- package/dist/adapter__adapter.mjs +1 -1
- package/dist/adapter__control.mjs +3 -3
- package/dist/adapter__runtime.mjs +1 -1
- package/dist/adapter__sql-renderer.mjs +1 -1
- package/dist/{control-DJq7MF_s.mjs → control-CVmnzYUI.mjs} +113 -18
- package/dist/control-CVmnzYUI.mjs.map +1 -0
- package/dist/{control-4njwExvT.mjs → control-DKT_rL3s.mjs} +11 -3
- package/dist/control-DKT_rL3s.mjs.map +1 -0
- package/dist/driver.mjs +1 -1
- package/dist/driver__control.mjs +1 -1
- package/dist/{migration-CIKnw9my.mjs → migration-DOkUBr8w.mjs} +2 -2
- package/dist/{migration-CIKnw9my.mjs.map → migration-DOkUBr8w.mjs.map} +1 -1
- package/dist/{migration-BsYgVFeM.d.mts → migration-wH0Hw2VD.d.mts} +3 -3
- package/dist/{migration-BsYgVFeM.d.mts.map → migration-wH0Hw2VD.d.mts.map} +1 -1
- package/dist/{op-factory-call-C-6Lxtlm.d.mts → op-factory-call-DAI1NqNz.d.mts} +16 -10
- package/dist/op-factory-call-DAI1NqNz.d.mts.map +1 -0
- package/dist/{op-factory-call-gwVgzGbc-caCULmpj.mjs → op-factory-call-hxIbAMSY-CinxsQHg.mjs} +234 -25
- package/dist/op-factory-call-hxIbAMSY-CinxsQHg.mjs.map +1 -0
- package/dist/{planner-Ddpm7j-w-BzyQ-raY.mjs → planner-BENMBCnG-CW6-1do-.mjs} +65 -23
- package/dist/planner-BENMBCnG-CW6-1do-.mjs.map +1 -0
- package/dist/planner-produced-sqlite-migration-CB9UDEdR.d.mts +1 -0
- package/dist/{planner-produced-sqlite-migration-D50U99OT--9-v0jj6.d.mts → planner-produced-sqlite-migration-CkHb8T4n-SM_tztzo.d.mts} +3 -3
- package/dist/planner-produced-sqlite-migration-CkHb8T4n-SM_tztzo.d.mts.map +1 -0
- package/dist/{planner-produced-sqlite-migration-Cj_QCROn-0eatEGzt.mjs → planner-produced-sqlite-migration-DjEZTiif-DpC6rk_q.mjs} +4 -4
- package/dist/{planner-produced-sqlite-migration-Cj_QCROn-0eatEGzt.mjs.map → planner-produced-sqlite-migration-DjEZTiif-DpC6rk_q.mjs.map} +1 -1
- package/dist/{planner-DRDXnFF-.d.mts → planner-tv5z1cn8.d.mts} +5 -4
- package/dist/planner-tv5z1cn8.d.mts.map +1 -0
- package/dist/{render-ops-CBkWzmKj-6vfxyU3K.mjs → render-ops-B889CY71-JvEeW9B4.mjs} +3 -3
- package/dist/{render-ops-CBkWzmKj-6vfxyU3K.mjs.map → render-ops-B889CY71-JvEeW9B4.mjs.map} +1 -1
- package/dist/{sql-utils-C2O4zVkP-Dz3Hb85I.mjs → sql-utils-XGT5Esi2-5k9pvJeF.mjs} +30 -3
- package/dist/sql-utils-XGT5Esi2-5k9pvJeF.mjs.map +1 -0
- package/dist/{sqlite-contract-view-BrXalcTm-DYDH4LQC.mjs → sqlite-contract-view-TELBWEt4-CVsdZnpX.mjs} +3 -3
- package/dist/{sqlite-contract-view-BrXalcTm-DYDH4LQC.mjs.map → sqlite-contract-view-TELBWEt4-CVsdZnpX.mjs.map} +1 -1
- package/dist/{sqlite-migration-omhBdJEh-CizoFjMg.d.mts → sqlite-migration-BqbQmUY1-CTmurARU.d.mts} +9 -3
- package/dist/{sqlite-migration-omhBdJEh-CizoFjMg.d.mts.map → sqlite-migration-BqbQmUY1-CTmurARU.d.mts.map} +1 -1
- package/dist/{sqlite-migration-D7DWt0JL-D_5jFkOx.mjs → sqlite-migration-CTNkjYJ1-eRdmsbpK.mjs} +235 -66
- package/dist/sqlite-migration-CTNkjYJ1-eRdmsbpK.mjs.map +1 -0
- package/dist/{tables-CvLOKCiJ-DYQkJ90L.d.mts → tables-DD_EmImp-B8ZaVBMr.d.mts} +16 -7
- package/dist/tables-DD_EmImp-B8ZaVBMr.d.mts.map +1 -0
- package/dist/target.d.mts +6 -6
- package/dist/target.mjs +9 -9
- package/dist/target__control.mjs +2 -2
- package/dist/target__migration.d.mts +2 -2
- package/dist/target__migration.mjs +2 -2
- package/dist/target__op-factory-call.d.mts +1 -1
- package/dist/target__op-factory-call.mjs +1 -1
- package/dist/target__planner-produced-sqlite-migration.d.mts +2 -2
- package/dist/target__planner-produced-sqlite-migration.mjs +1 -1
- package/dist/target__planner.d.mts +1 -1
- package/dist/target__planner.mjs +1 -1
- package/dist/target__render-ops.mjs +1 -1
- package/dist/target__runtime.mjs +1 -1
- package/dist/target__runtime.mjs.map +1 -1
- package/dist/target__sql-utils.mjs +1 -1
- package/package.json +9 -9
- package/dist/control-4njwExvT.mjs.map +0 -1
- package/dist/control-DJq7MF_s.mjs.map +0 -1
- package/dist/op-factory-call-C-6Lxtlm.d.mts.map +0 -1
- package/dist/op-factory-call-gwVgzGbc-caCULmpj.mjs.map +0 -1
- package/dist/planner-DRDXnFF-.d.mts.map +0 -1
- package/dist/planner-Ddpm7j-w-BzyQ-raY.mjs.map +0 -1
- package/dist/planner-produced-sqlite-migration-CWUahMOX.d.mts +0 -1
- package/dist/planner-produced-sqlite-migration-D50U99OT--9-v0jj6.d.mts.map +0 -1
- package/dist/sql-utils-C2O4zVkP-Dz3Hb85I.mjs.map +0 -1
- package/dist/sqlite-migration-D7DWt0JL-D_5jFkOx.mjs.map +0 -1
- package/dist/tables-CvLOKCiJ-DYQkJ90L.d.mts.map +0 -1
package/dist/{op-factory-call-gwVgzGbc-caCULmpj.mjs → op-factory-call-hxIbAMSY-CinxsQHg.mjs}
RENAMED
|
@@ -2,21 +2,22 @@ import { o as sqliteInteger } from "./data-types-pFQMl37_-B-3aK_wd.mjs";
|
|
|
2
2
|
import { n as sqliteError } from "./errors-DnR-sMHS-KkMLIP2U.mjs";
|
|
3
3
|
import { n as decodeSqliteDatetime, r as encodeSqliteDatetime } from "./codecs-BctEWIFt-DgMd3dUl.mjs";
|
|
4
4
|
import { a as tableExistsAst, i as indexExistsAst, n as columnNameTakenAst, o as tableNameTakenAst, r as createTable, t as columnExistsAst } from "./ddl-CDoa1Ehx-BOT670XL.mjs";
|
|
5
|
-
import { n as quoteIdentifier, t as
|
|
5
|
+
import { n as escapeLiteral, r as quoteIdentifier, t as SQLITE_QUOTING } from "./sql-utils-XGT5Esi2-5k9pvJeF.mjs";
|
|
6
6
|
import { r as stripOuterParens } from "./default-normalizer-Ch3VFnEH-C-h1XtlJ.mjs";
|
|
7
7
|
import { t as buildTargetDetails } from "./planner-target-details-CIQ789Gc-C5U4yVY9.mjs";
|
|
8
8
|
import { DdlColumn, FunctionColumnDefault, LiteralColumnDefault, opaqueSql } from "@prisma/orm-family-sql/relational-core/ast";
|
|
9
9
|
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
10
10
|
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
11
11
|
import { dataTypeParams, renderSqlTypeName, sqlDataTypeOfCodec } from "@prisma/orm-family-sql/contract/data-type";
|
|
12
|
+
import { type } from "arktype";
|
|
12
13
|
import { issueOutcome } from "@prisma/orm-framework/components/control";
|
|
13
|
-
import { createSqlTextSources } from "@prisma/orm-family-sql/family/control";
|
|
14
|
+
import { checkSqlExpressionText, convertedColumnMeta, createSqlTextSources, isUnfilledSlot, requiredColumnMeta, slotSource, sqlOfSlot } from "@prisma/orm-family-sql/family/control";
|
|
14
15
|
import { RelationalSchemaNodeKind } from "@prisma/orm-family-sql/schema-ir/types";
|
|
15
16
|
import { assertNever } from "@prisma/orm-framework/utils/internal-error";
|
|
16
17
|
import { errorMigrationOperationOptionRemoved, unfilledPlaceholderOperation } from "@prisma/orm-framework/errors/migration";
|
|
17
18
|
import { REFERENTIAL_ACTION_SQL } from "@prisma/orm-family-sql/contract/referential-action-sql";
|
|
18
19
|
import { TsExpression, jsonToTsSource, tsArraySource, tsObjectSource, tsQuotedTextSource } from "@prisma/orm-framework/ts-render";
|
|
19
|
-
//#region ../../../3-targets/3-targets/sqlite/dist/op-factory-call-
|
|
20
|
+
//#region ../../../3-targets/3-targets/sqlite/dist/op-factory-call-hxIbAMSY.mjs
|
|
20
21
|
/**
|
|
21
22
|
* Low-level DDL fragment builders for SQLite migrations.
|
|
22
23
|
*
|
|
@@ -260,18 +261,35 @@ function renameTableSteps(fromName, toName) {
|
|
|
260
261
|
const via = quoteIdentifier(viaName);
|
|
261
262
|
return [step(`rename table "${fromName}" to "${viaName}" (SQLite table names are case-insensitive)`, `ALTER TABLE ${from} RENAME TO ${via}`), step(`rename table "${viaName}" to "${toName}"`, `ALTER TABLE ${via} RENAME TO ${to}`)];
|
|
262
263
|
}
|
|
264
|
+
/** The table a rebuild of `tableName` creates, copies into, and renames to `tableName`. */
|
|
265
|
+
function rebuiltTableName(tableName) {
|
|
266
|
+
return `_prisma_new_${tableName}`;
|
|
267
|
+
}
|
|
263
268
|
async function recreateTable(args, lowerer) {
|
|
264
|
-
const { tableName, contractTable, schemaColumnNames, indexes, summary, postchecks, operationClass } = args;
|
|
269
|
+
const { tableName, contractTable, schemaColumnNames, indexes, summary, postchecks, operationClass, copyExpressions = {}, temporaryValues = {}, retypedColumns = [] } = args;
|
|
265
270
|
refuseEarlierColumnSpecs("recreateTable", tableName, contractTable.columns);
|
|
266
|
-
const tempName =
|
|
271
|
+
const tempName = rebuiltTableName(tableName);
|
|
267
272
|
const liveSet = new Set(schemaColumnNames);
|
|
268
|
-
const
|
|
273
|
+
const convertedBy = (column) => Object.hasOwn(copyExpressions, column) ? copyExpressions[column] : void 0;
|
|
274
|
+
const copiedFrom = (column) => convertedBy(column) ?? (Object.hasOwn(temporaryValues, column) ? temporaryValues[column] : void 0);
|
|
275
|
+
const sharedColumns = contractTable.columns.filter((c) => liveSet.has(c.name) || copiedFrom(c.name) !== void 0).map((c) => c.name);
|
|
269
276
|
const columnList = sharedColumns.map(quoteIdentifier).join(", ");
|
|
277
|
+
const selectList = sharedColumns.map((column) => copiedFrom(column) ?? quoteIdentifier(column)).join(", ");
|
|
270
278
|
const indexStatements = indexes.map((idx) => ({
|
|
271
279
|
description: `recreate index "${idx.name}" on "${tableName}"`,
|
|
272
280
|
sql: buildCreateIndexSql(tableName, idx.name, idx.columns)
|
|
273
281
|
}));
|
|
274
|
-
const
|
|
282
|
+
const conversions = Object.fromEntries(sharedColumns.flatMap((column) => {
|
|
283
|
+
const expression = convertedBy(column);
|
|
284
|
+
return expression === void 0 ? [] : [[column, expression]];
|
|
285
|
+
}));
|
|
286
|
+
const copyStep = sharedColumns.length > 0 ? [{
|
|
287
|
+
...step(`copy data from "${tableName}" to "${tempName}"`, `INSERT INTO ${quoteIdentifier(tempName)} (${columnList}) SELECT ${selectList} FROM ${quoteIdentifier(tableName)}`),
|
|
288
|
+
...Object.keys(conversions).length === 0 ? {} : { meta: { conversionCopy: {
|
|
289
|
+
table: tableName,
|
|
290
|
+
expressions: conversions
|
|
291
|
+
} } }
|
|
292
|
+
}] : [];
|
|
275
293
|
const defaultClauses = await renderSpecDefaults(contractTable, tableName, lowerer);
|
|
276
294
|
const clauseByColumn = new Map(contractTable.columns.map((column, index) => [column.name, defaultClauses[index] ?? ""]));
|
|
277
295
|
const tableSteps = await tableExistsSteps(lowerer, tableName);
|
|
@@ -296,10 +314,70 @@ async function recreateTable(args, lowerer) {
|
|
|
296
314
|
postcheck: [
|
|
297
315
|
step(`verify table "${tableName}" exists`, tableSteps.present.sql, tableSteps.present.params),
|
|
298
316
|
step(`verify temp table "${tempName}" is gone`, tempSteps.absent.sql, tempSteps.absent.params),
|
|
299
|
-
...postchecks.flatMap((check) => "sql" in check ? [check] : defaultPostcheck(tableName, check, clauseByColumn.get(check.columnDefault)))
|
|
317
|
+
...postchecks.flatMap((check) => "sql" in check ? [markedStep(tableName, check)] : defaultPostcheck(tableName, check, clauseByColumn.get(check.columnDefault))),
|
|
318
|
+
...contractTable.columns.flatMap((column) => {
|
|
319
|
+
const by = Object.hasOwn(conversions, column.name) ? "conversion" : retypedColumns.includes(column.name) ? "cast" : void 0;
|
|
320
|
+
return by === void 0 ? [] : [{
|
|
321
|
+
...storageClassCheck(tableName, column),
|
|
322
|
+
meta: convertedColumnMeta(tableName, column.name, by)
|
|
323
|
+
}];
|
|
324
|
+
})
|
|
300
325
|
]
|
|
301
326
|
};
|
|
302
327
|
}
|
|
328
|
+
function markedStep(tableName, check) {
|
|
329
|
+
const { requiredColumn, ...step } = check;
|
|
330
|
+
const meta = { ...requiredColumn === void 0 ? {} : requiredColumnMeta(tableName, requiredColumn.column, requiredColumn.reason) };
|
|
331
|
+
return Object.keys(meta).length === 0 ? step : {
|
|
332
|
+
...step,
|
|
333
|
+
meta
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* The storage classes SQLite keeps a value in once a column of `typeSql` has taken it: the column's
|
|
338
|
+
* type affinity converts what it can, so a value of another class is one it could not convert.
|
|
339
|
+
* See https://www.sqlite.org/datatype3.html#determination_of_column_affinity.
|
|
340
|
+
*/
|
|
341
|
+
function affinityStorageClasses(typeSql) {
|
|
342
|
+
const declared = typeSql.toUpperCase();
|
|
343
|
+
if (declared.includes("INT")) return ["integer"];
|
|
344
|
+
if ([
|
|
345
|
+
"CHAR",
|
|
346
|
+
"CLOB",
|
|
347
|
+
"TEXT"
|
|
348
|
+
].some((part) => declared.includes(part))) return ["text"];
|
|
349
|
+
if (declared === "" || declared.includes("BLOB")) return ["blob"];
|
|
350
|
+
if ([
|
|
351
|
+
"REAL",
|
|
352
|
+
"FLOA",
|
|
353
|
+
"DOUB"
|
|
354
|
+
].some((part) => declared.includes(part))) return ["real"];
|
|
355
|
+
return ["integer", "real"];
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* The check that the copy into a rebuilt table converted every value of a retyped column to the
|
|
359
|
+
* column's new type. SQLite keeps a value it cannot convert in its old storage class, so a value of
|
|
360
|
+
* any other class than the column's affinity keeps fails the check.
|
|
361
|
+
*/
|
|
362
|
+
function storageClassCheck(tableName, column) {
|
|
363
|
+
const classes = ["null", ...affinityStorageClasses(column.typeSql)].map((storageClass) => `'${storageClass}'`).join(", ");
|
|
364
|
+
return {
|
|
365
|
+
description: `verify every value of "${column.name}" on "${tableName}" is stored as ${column.typeSql}`,
|
|
366
|
+
sql: `SELECT NOT EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE typeof(${quoteIdentifier(column.name)}) NOT IN (${classes}))`
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
const ConversionCopyMeta = type({ conversionCopy: {
|
|
370
|
+
table: "string",
|
|
371
|
+
expressions: "Record<string, string>"
|
|
372
|
+
} });
|
|
373
|
+
/**
|
|
374
|
+
* The table a rebuild's copy step reads and the copy expression of each column its conversions
|
|
375
|
+
* convert, when the step converts any: an error of the step may be an error of one of them.
|
|
376
|
+
*/
|
|
377
|
+
function conversionCopyOf(step) {
|
|
378
|
+
const meta = ConversionCopyMeta(step.meta ?? {});
|
|
379
|
+
return meta instanceof type.errors ? void 0 : meta.conversionCopy;
|
|
380
|
+
}
|
|
303
381
|
function renderSpecDefaults(spec, tableName, lowerer) {
|
|
304
382
|
return Promise.all(spec.columns.map((column) => renderSpecDefault$1(column, tableName, lowerer)));
|
|
305
383
|
}
|
|
@@ -386,7 +464,7 @@ function columnNameFromDefaultIssuePath(issue) {
|
|
|
386
464
|
* `RecreateTableCall` doesn't have to carry `SchemaDiffIssue` objects through
|
|
387
465
|
* to render time.
|
|
388
466
|
*/
|
|
389
|
-
function buildRecreatePostchecks(tableName, issues, spec) {
|
|
467
|
+
function buildRecreatePostchecks(tableName, issues, spec, copiedColumns = /* @__PURE__ */ new Set(), backfilledColumns = /* @__PURE__ */ new Set()) {
|
|
390
468
|
const checks = [];
|
|
391
469
|
const t = escapeLiteral(tableName);
|
|
392
470
|
const byName = new Map(spec.columns.map((c) => [c.name, c]));
|
|
@@ -395,6 +473,24 @@ function buildRecreatePostchecks(tableName, issues, spec) {
|
|
|
395
473
|
let hasFkIssue = false;
|
|
396
474
|
for (const issue of issues) {
|
|
397
475
|
const nodeKind = nodeKindOf(issue);
|
|
476
|
+
if (nodeKind === RelationalSchemaNodeKind.column && issueOutcome(issue) === "not-found") {
|
|
477
|
+
const columnName = columnNameFromNode(issue);
|
|
478
|
+
const column = columnName === void 0 ? void 0 : byName.get(columnName);
|
|
479
|
+
if (columnName === void 0 || column === void 0) continue;
|
|
480
|
+
const takesNoValue = !column.nullable && column.default === void 0 && !copiedColumns.has(columnName);
|
|
481
|
+
checks.push({
|
|
482
|
+
description: `verify "${columnName}" nullability on "${tableName}"`,
|
|
483
|
+
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${escapeLiteral(columnName)}' AND "notnull" = ${column.nullable ? 0 : 1}`,
|
|
484
|
+
...backfilledColumns.has(columnName) ? { requiredColumn: {
|
|
485
|
+
column: columnName,
|
|
486
|
+
reason: "backfillMissedRows"
|
|
487
|
+
} } : takesNoValue ? { requiredColumn: {
|
|
488
|
+
column: columnName,
|
|
489
|
+
reason: "newField"
|
|
490
|
+
} } : {}
|
|
491
|
+
});
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
398
494
|
if (nodeKind === RelationalSchemaNodeKind.column && issueOutcome(issue) === "not-equal") {
|
|
399
495
|
const columnName = columnNameFromNode(issue);
|
|
400
496
|
if (columnName === void 0) continue;
|
|
@@ -403,7 +499,11 @@ function buildRecreatePostchecks(tableName, issues, spec) {
|
|
|
403
499
|
const actual = blindCast(issue.actual);
|
|
404
500
|
if (expected.nullable !== actual.nullable) checks.push({
|
|
405
501
|
description: `verify "${columnName}" nullability on "${tableName}"`,
|
|
406
|
-
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND "notnull" = ${expected.nullable ? 0 : 1}
|
|
502
|
+
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND "notnull" = ${expected.nullable ? 0 : 1}`,
|
|
503
|
+
...expected.nullable ? {} : { requiredColumn: {
|
|
504
|
+
column: columnName,
|
|
505
|
+
reason: backfilledColumns.has(columnName) ? "backfillMissedRows" : "nullValues"
|
|
506
|
+
} }
|
|
407
507
|
});
|
|
408
508
|
if (columnTypeChanged(expected, actual)) {
|
|
409
509
|
const colSpec = byName.get(columnName);
|
|
@@ -791,6 +891,86 @@ var RenameColumnCall = class extends SqliteOpFactoryCallNode {
|
|
|
791
891
|
return [];
|
|
792
892
|
}
|
|
793
893
|
};
|
|
894
|
+
function refuseUnsafeCopyExpressions(tableName, option, expressions) {
|
|
895
|
+
for (const [column, expression] of Object.entries(expressions)) {
|
|
896
|
+
if (expression.kind !== "sql") continue;
|
|
897
|
+
const refused = checkSqlExpressionText(expression.sql, SQLITE_QUOTING);
|
|
898
|
+
if (refused === void 0) continue;
|
|
899
|
+
const call = `recreateTable({ tableName: ${jsonToTsSource(tableName)} })`;
|
|
900
|
+
throw sqliteError("MIGRATION.SQL_EXPRESSION_INVALID", `${call}: ${option}.${column} ${JSON.stringify(expression.sql)} holds ${refused}, which could end or extend the INSERT … SELECT statement it is written into`, {
|
|
901
|
+
why: "A copy expression or a temporary value is written into the SELECT that copies the rows into the rebuilt table. The check refuses a semicolon, a SQL comment, dollar quoting, and a comma or a statement keyword outside parentheses, since each could end or extend that statement; it does not check that the text is valid SQL.",
|
|
902
|
+
fix: `Write ${option}.${column} as one SQL expression over the old table's columns, such as CAST("${column}" AS INTEGER). If it holds a comma or a keyword such as FROM outside parentheses, wrap it in parentheses.`,
|
|
903
|
+
meta: {
|
|
904
|
+
call,
|
|
905
|
+
field: `${option}.${column}`,
|
|
906
|
+
expression: expression.sql,
|
|
907
|
+
refused
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
function refuseUnknownCopyExpressionColumns(tableName, contractTable, named) {
|
|
913
|
+
const columns = contractTable.columns.map((column) => column.name);
|
|
914
|
+
const unknown = [
|
|
915
|
+
...Object.keys(named.copyExpressions).map((key) => ({
|
|
916
|
+
option: `copyExpressions.${key}`,
|
|
917
|
+
key
|
|
918
|
+
})),
|
|
919
|
+
...Object.keys(named.temporaryValues).map((key) => ({
|
|
920
|
+
option: `temporaryValues.${key}`,
|
|
921
|
+
key
|
|
922
|
+
})),
|
|
923
|
+
...named.retypedColumns.map((key) => ({
|
|
924
|
+
option: `retypedColumns entry ${JSON.stringify(key)}`,
|
|
925
|
+
key
|
|
926
|
+
}))
|
|
927
|
+
].find(({ key }) => !columns.includes(key));
|
|
928
|
+
if (unknown === void 0) return;
|
|
929
|
+
const call = `recreateTable({ tableName: ${jsonToTsSource(tableName)} })`;
|
|
930
|
+
throw sqliteError("MIGRATION.COPY_EXPRESSION_COLUMN_UNKNOWN", `${call}: ${unknown.option} names no column of the new table. Its columns are ${columns.map((column) => JSON.stringify(column)).join(", ")}.`, {
|
|
931
|
+
why: "Each key of copyExpressions and temporaryValues, and each entry of retypedColumns, names a column of the new table. One that names no column applies to nothing, so the column meant would be copied unchanged or left without a value, or its values would go unchecked.",
|
|
932
|
+
fix: "Name a column in contractTable.columns, with the same letters and case.",
|
|
933
|
+
meta: {
|
|
934
|
+
call,
|
|
935
|
+
key: unknown.key,
|
|
936
|
+
columns
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* A temporary value fills a column the old table does not have, and only that: for a column the
|
|
942
|
+
* old table has it would replace every existing value, and a column with a copy expression is
|
|
943
|
+
* already filled by its conversion.
|
|
944
|
+
*/
|
|
945
|
+
function refuseMisplacedTemporaryValues(tableName, schemaColumnNames, copyExpressions, temporaryValues) {
|
|
946
|
+
const call = `recreateTable({ tableName: ${jsonToTsSource(tableName)} })`;
|
|
947
|
+
for (const column of Object.keys(temporaryValues)) {
|
|
948
|
+
if (schemaColumnNames.includes(column)) throw sqliteError("MIGRATION.TEMPORARY_VALUE_INVALID", `${call}: temporaryValues.${column} names a column the old table already has, so the copy would replace every value of it.`, {
|
|
949
|
+
why: "A temporary value fills a new column in the existing rows. A column the old table has keeps its values through the copy, or is converted by a copy expression.",
|
|
950
|
+
fix: `Remove temporaryValues.${column}. To change the values of "${column}", write a conversion in copyExpressions.${column} instead.`,
|
|
951
|
+
meta: {
|
|
952
|
+
call,
|
|
953
|
+
column,
|
|
954
|
+
reason: "existingColumn"
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
if (Object.hasOwn(copyExpressions, column)) throw sqliteError("MIGRATION.TEMPORARY_VALUE_INVALID", `${call}: "${column}" is named in both copyExpressions and temporaryValues, so it would be filled twice.`, {
|
|
958
|
+
why: "A column is filled either by the conversion in copyExpressions or by a temporary value, not both.",
|
|
959
|
+
fix: `Keep one of copyExpressions.${column} and temporaryValues.${column}.`,
|
|
960
|
+
meta: {
|
|
961
|
+
call,
|
|
962
|
+
column,
|
|
963
|
+
reason: "alsoConverted"
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function sqlOfSlots(slots) {
|
|
969
|
+
return Object.fromEntries(Object.entries(slots).flatMap(([column, slot]) => {
|
|
970
|
+
const sql = sqlOfSlot(slot);
|
|
971
|
+
return sql === void 0 ? [] : [[column, sql]];
|
|
972
|
+
}));
|
|
973
|
+
}
|
|
794
974
|
var RecreateTableCall = class extends SqliteOpFactoryCallNode {
|
|
795
975
|
factoryName = "recreateTable";
|
|
796
976
|
operationClass;
|
|
@@ -801,14 +981,25 @@ var RecreateTableCall = class extends SqliteOpFactoryCallNode {
|
|
|
801
981
|
summary;
|
|
802
982
|
postchecks;
|
|
803
983
|
label;
|
|
804
|
-
/**
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
984
|
+
/** The conversion each named column of the new table is copied through, in place of the live column. */
|
|
985
|
+
copyExpressions;
|
|
986
|
+
/** The value each named new column takes in the existing rows. */
|
|
987
|
+
temporaryValues;
|
|
988
|
+
/** The columns whose type changes; the rebuild checks each after its copy, as a conversion when it has a copy expression and as SQLite's cast otherwise. */
|
|
989
|
+
retypedColumns;
|
|
990
|
+
constructor(args) {
|
|
810
991
|
super();
|
|
811
|
-
|
|
992
|
+
refuseUnsafeCopyExpressions(args.tableName, "copyExpressions", args.copyExpressions ?? {});
|
|
993
|
+
refuseUnsafeCopyExpressions(args.tableName, "temporaryValues", args.temporaryValues ?? {});
|
|
994
|
+
refuseUnknownCopyExpressionColumns(args.tableName, args.contractTable, {
|
|
995
|
+
copyExpressions: args.copyExpressions ?? {},
|
|
996
|
+
temporaryValues: args.temporaryValues ?? {},
|
|
997
|
+
retypedColumns: args.retypedColumns ?? []
|
|
998
|
+
});
|
|
999
|
+
refuseMisplacedTemporaryValues(args.tableName, args.schemaColumnNames, args.copyExpressions ?? {}, args.temporaryValues ?? {});
|
|
1000
|
+
this.copyExpressions = Object.freeze({ ...args.copyExpressions });
|
|
1001
|
+
this.temporaryValues = Object.freeze({ ...args.temporaryValues });
|
|
1002
|
+
this.retypedColumns = Object.freeze([...args.retypedColumns ?? []]);
|
|
812
1003
|
this.tableName = args.tableName;
|
|
813
1004
|
this.contractTable = args.contractTable;
|
|
814
1005
|
this.schemaColumnNames = args.schemaColumnNames;
|
|
@@ -824,6 +1015,8 @@ var RecreateTableCall = class extends SqliteOpFactoryCallNode {
|
|
|
824
1015
|
factory: this.factoryName,
|
|
825
1016
|
tableName: this.tableName
|
|
826
1017
|
} });
|
|
1018
|
+
const copies = Object.entries(this.copyExpressions);
|
|
1019
|
+
if (copies.some(([, expression]) => isUnfilledSlot(expression))) return unfilledPlaceholderOperation(this.label);
|
|
827
1020
|
return recreateTable({
|
|
828
1021
|
tableName: this.tableName,
|
|
829
1022
|
contractTable: this.contractTable,
|
|
@@ -831,14 +1024,23 @@ var RecreateTableCall = class extends SqliteOpFactoryCallNode {
|
|
|
831
1024
|
indexes: this.indexes,
|
|
832
1025
|
summary: this.summary,
|
|
833
1026
|
postchecks: this.postchecks,
|
|
834
|
-
operationClass: this.operationClass
|
|
1027
|
+
operationClass: this.operationClass,
|
|
1028
|
+
retypedColumns: this.retypedColumns,
|
|
1029
|
+
...copies.length === 0 ? {} : { copyExpressions: sqlOfSlots(this.copyExpressions) },
|
|
1030
|
+
...Object.keys(this.temporaryValues).length === 0 ? {} : { temporaryValues: sqlOfSlots(this.temporaryValues) }
|
|
835
1031
|
}, lowerer);
|
|
836
1032
|
}
|
|
1033
|
+
#slotsSource(slots, sql) {
|
|
1034
|
+
return `{ ${Object.entries(slots).map(([column, expression]) => `${jsonToTsSource(column)}: ${slotSource(expression, sql)}`).join(", ")} }`;
|
|
1035
|
+
}
|
|
837
1036
|
renderTypeScript() {
|
|
838
1037
|
return this.#written().source;
|
|
839
1038
|
}
|
|
840
1039
|
importRequirements() {
|
|
841
|
-
return this
|
|
1040
|
+
return [...Object.values(this.copyExpressions).some(isUnfilledSlot) ? [{
|
|
1041
|
+
moduleSpecifier: TARGET_MIGRATION_MODULE,
|
|
1042
|
+
symbol: "placeholder"
|
|
1043
|
+
}] : [], ...this.#written().imports];
|
|
842
1044
|
}
|
|
843
1045
|
#written() {
|
|
844
1046
|
const sql = createSqlTextSources(TARGET_MIGRATION_MODULE);
|
|
@@ -850,7 +1052,10 @@ var RecreateTableCall = class extends SqliteOpFactoryCallNode {
|
|
|
850
1052
|
["indexes", jsonToTsSource(this.indexes)],
|
|
851
1053
|
["summary", jsonToTsSource(this.summary)],
|
|
852
1054
|
["postchecks", tsArraySource(this.postchecks.map(renderPostcheck))],
|
|
853
|
-
["operationClass", jsonToTsSource(this.operationClass)]
|
|
1055
|
+
["operationClass", jsonToTsSource(this.operationClass)],
|
|
1056
|
+
...Object.keys(this.copyExpressions).length === 0 ? [] : [["copyExpressions", this.#slotsSource(this.copyExpressions, sql)]],
|
|
1057
|
+
...Object.keys(this.temporaryValues).length === 0 ? [] : [["temporaryValues", this.#slotsSource(this.temporaryValues, sql)]],
|
|
1058
|
+
...this.retypedColumns.length === 0 ? [] : [["retypedColumns", jsonToTsSource(this.retypedColumns)]]
|
|
854
1059
|
])})`,
|
|
855
1060
|
imports: sql.imports()
|
|
856
1061
|
};
|
|
@@ -1051,19 +1256,22 @@ var DataTransformCall = class extends SqliteOpFactoryCallNode {
|
|
|
1051
1256
|
label;
|
|
1052
1257
|
tableName;
|
|
1053
1258
|
columnName;
|
|
1054
|
-
|
|
1259
|
+
/** The slot the user fills with the statement that fills the column: `<Model.field>:run`. */
|
|
1260
|
+
slot;
|
|
1261
|
+
constructor(id, label, tableName, columnName, slot) {
|
|
1055
1262
|
super();
|
|
1056
1263
|
this.id = id;
|
|
1057
1264
|
this.label = label;
|
|
1058
1265
|
this.tableName = tableName;
|
|
1059
1266
|
this.columnName = columnName;
|
|
1267
|
+
this.slot = slot;
|
|
1060
1268
|
this.freeze();
|
|
1061
1269
|
}
|
|
1062
1270
|
toOp(_lowerer) {
|
|
1063
1271
|
return unfilledPlaceholderOperation(this.label);
|
|
1064
1272
|
}
|
|
1065
1273
|
renderTypeScript() {
|
|
1066
|
-
const slot =
|
|
1274
|
+
const slot = this.slot;
|
|
1067
1275
|
return [
|
|
1068
1276
|
"dataTransform({",
|
|
1069
1277
|
` id: ${jsonToTsSource(this.id)},`,
|
|
@@ -1146,7 +1354,8 @@ function renderSpecDefault(columnDefault, sql) {
|
|
|
1146
1354
|
function renderPostcheck(postcheck) {
|
|
1147
1355
|
if ("sql" in postcheck) return tsObjectSource(definedSourceEntries({
|
|
1148
1356
|
description: jsonToTsSource(postcheck.description),
|
|
1149
|
-
sql: tsQuotedTextSource(postcheck.sql)
|
|
1357
|
+
sql: tsQuotedTextSource(postcheck.sql),
|
|
1358
|
+
requiredColumn: postcheck.requiredColumn === void 0 ? void 0 : jsonToTsSource(postcheck.requiredColumn)
|
|
1150
1359
|
}));
|
|
1151
1360
|
return tsObjectSource(definedSourceEntries({
|
|
1152
1361
|
description: jsonToTsSource(postcheck.description),
|
|
@@ -1154,6 +1363,6 @@ function renderPostcheck(postcheck) {
|
|
|
1154
1363
|
}));
|
|
1155
1364
|
}
|
|
1156
1365
|
//#endregion
|
|
1157
|
-
export {
|
|
1366
|
+
export { sqliteIdentifiersCollide as C, sqliteDefaultToDdlColumnDefault as S, indexReplacementCalls as _, DropColumnCall as a, renameTableSteps as b, RawSqlCall as c, RenameTableCall as d, affinityStorageClasses as f, conversionCopyOf as g, buildRecreateSummary as h, DataTransformCall as i, RecreateTableCall as l, buildRecreatePostchecks as m, CreateIndexCall as n, DropIndexCall as o, buildColumnTypeSql as p, CreateTableCall as r, DropTableCall as s, AddColumnCall as t, RenameColumnCall as u, isSqliteOpFactoryCall as v, step as w, renderDefaultLiteral as x, rebuiltTableName as y };
|
|
1158
1367
|
|
|
1159
|
-
//# sourceMappingURL=op-factory-call-
|
|
1368
|
+
//# sourceMappingURL=op-factory-call-hxIbAMSY-CinxsQHg.mjs.map
|