@prisma/orm-target-sqlite 8.0.0-rc.22-dev.7 → 8.0.0-rc.22-dev.9
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-DhxErV2N.d.mts → adapter-BLfNDWw-.d.mts} +2 -2
- package/dist/{adapter-DhxErV2N.d.mts.map → adapter-BLfNDWw-.d.mts.map} +1 -1
- package/dist/{adapter-gpb0ICA2-DPjB1dMR.mjs → adapter-gpb0ICA2-Ddnczc5D.mjs} +3 -3
- package/dist/{adapter-gpb0ICA2-DPjB1dMR.mjs.map → adapter-gpb0ICA2-Ddnczc5D.mjs.map} +1 -1
- package/dist/{adapter-xFPDhkod-Cio6up_5.d.mts → adapter-xFPDhkod-DB6XDGgl.d.mts} +3 -3
- package/dist/{adapter-xFPDhkod-Cio6up_5.d.mts.map → adapter-xFPDhkod-DB6XDGgl.d.mts.map} +1 -1
- package/dist/adapter.d.mts +9 -9
- package/dist/adapter.mjs +2 -2
- package/dist/adapter__adapter.d.mts +2 -2
- package/dist/adapter__adapter.mjs +1 -1
- package/dist/adapter__codec-types.d.mts +3 -3
- package/dist/adapter__control.d.mts +2 -2
- package/dist/adapter__control.mjs +3 -3
- package/dist/adapter__runtime.d.mts +1 -1
- package/dist/adapter__runtime.mjs +3 -3
- package/dist/adapter__sql-renderer.d.mts +2 -2
- package/dist/adapter__sql-renderer.mjs +1 -1
- package/dist/adapter__types.d.mts +2 -2
- package/dist/{codec-types-CG5ZkqDU-DCx8ZvBg.d.mts → codec-types-CGtSWeZ5-B5RH39BA.d.mts} +3 -3
- package/dist/{codec-types-CG5ZkqDU-DCx8ZvBg.d.mts.map → codec-types-CGtSWeZ5-B5RH39BA.d.mts.map} +1 -1
- package/dist/codec-types-Dx9dQe-Y.d.mts +1 -0
- package/dist/codec-types-IproBv-Q.d.mts +2 -0
- package/dist/{codecs-BqOj4EKm-BaGeNd2m.d.mts → codecs-A0DbaZRE-CMyLhO_K.d.mts} +10 -2
- package/dist/codecs-A0DbaZRE-CMyLhO_K.d.mts.map +1 -0
- package/dist/{codecs-My4bmOU6-D4-b5qLd.mjs → codecs-BSYEsc7M-C4nov_lo.mjs} +10 -2
- package/dist/{codecs-My4bmOU6-D4-b5qLd.mjs.map → codecs-BSYEsc7M-C4nov_lo.mjs.map} +1 -1
- package/dist/{codecs-R3kGRyq2.mjs → codecs-BhRFtE69.mjs} +2 -2
- package/dist/{codecs-R3kGRyq2.mjs.map → codecs-BhRFtE69.mjs.map} +1 -1
- package/dist/codecs-CX5BZUdO.d.mts +1 -0
- package/dist/{control-DXzV_IVS.d.mts → control-Bc3l_1mV.d.mts} +3 -3
- package/dist/{control-DXzV_IVS.d.mts.map → control-Bc3l_1mV.d.mts.map} +1 -1
- package/dist/{descriptor-meta-DrNwC4vO-B9xeeDp9.mjs → descriptor-meta-DrNwC4vO-C_RZ1Tmp.mjs} +2 -2
- package/dist/{descriptor-meta-DrNwC4vO-B9xeeDp9.mjs.map → descriptor-meta-DrNwC4vO-C_RZ1Tmp.mjs.map} +1 -1
- package/dist/{migration-DYyfcQB_.mjs → migration-DBqx2jWN.mjs} +2 -2
- package/dist/{migration-DYyfcQB_.mjs.map → migration-DBqx2jWN.mjs.map} +1 -1
- package/dist/{migration-BHhnGEA2.d.mts → migration-Dekon_QC.d.mts} +3 -3
- package/dist/{migration-BHhnGEA2.d.mts.map → migration-Dekon_QC.d.mts.map} +1 -1
- package/dist/{op-factory-call-BQYECylm-K7S_xgRS.mjs → op-factory-call-Dxsyn5gF-CvHmM-h_.mjs} +506 -21
- package/dist/op-factory-call-Dxsyn5gF-CvHmM-h_.mjs.map +1 -0
- package/dist/{op-factory-call-B8bJlvkP.d.mts → op-factory-call-gQ5ddSys.d.mts} +78 -9
- package/dist/op-factory-call-gQ5ddSys.d.mts.map +1 -0
- package/dist/{pack-CXW0_XyO.d.mts → pack-D3gC0XlT.d.mts} +3 -3
- package/dist/{pack-CXW0_XyO.d.mts.map → pack-D3gC0XlT.d.mts.map} +1 -1
- package/dist/{planner-DNwf8s8s-BoXZwpVs.mjs → planner-DgEGQuj9-Ca6jqWrd.mjs} +111 -64
- package/dist/planner-DgEGQuj9-Ca6jqWrd.mjs.map +1 -0
- package/dist/{planner-DcOz-1na.d.mts → planner-ccevdpwf.d.mts} +2 -2
- package/dist/{planner-DcOz-1na.d.mts.map → planner-ccevdpwf.d.mts.map} +1 -1
- package/dist/{planner-produced-sqlite-migration-DyUPHRKW-CFtZi2VG.d.mts → planner-produced-sqlite-migration-BHH_ftCV-CndNmTrH.d.mts} +3 -3
- package/dist/planner-produced-sqlite-migration-BHH_ftCV-CndNmTrH.d.mts.map +1 -0
- package/dist/{planner-produced-sqlite-migration-Z9OWyjQn-DGMHz6I1.mjs → planner-produced-sqlite-migration-BMogETJ0-COe4tate.mjs} +4 -4
- package/dist/{planner-produced-sqlite-migration-Z9OWyjQn-DGMHz6I1.mjs.map → planner-produced-sqlite-migration-BMogETJ0-COe4tate.mjs.map} +1 -1
- package/dist/planner-produced-sqlite-migration-_n61WaiD.d.mts +1 -0
- package/dist/{render-ops-CJXrOY63-1y0nLsb8.mjs → render-ops-BUk3wlAR-Awdho1_L.mjs} +3 -3
- package/dist/{render-ops-CJXrOY63-1y0nLsb8.mjs.map → render-ops-BUk3wlAR-Awdho1_L.mjs.map} +1 -1
- package/dist/{runtime-BQKQH94p.d.mts → runtime-H_OgXoaz.d.mts} +2 -2
- package/dist/{runtime-BQKQH94p.d.mts.map → runtime-H_OgXoaz.d.mts.map} +1 -1
- package/dist/sql-renderer-_NdIrQuX.d.mts +1 -0
- package/dist/{sqlite-migration-BxQ65pKq-C7siJGuR.mjs → sqlite-migration-B42dCAqX-DiSO-PeZ.mjs} +47 -7
- package/dist/sqlite-migration-B42dCAqX-DiSO-PeZ.mjs.map +1 -0
- package/dist/{sqlite-migration-CcXBptQr-DlKSNedI.d.mts → sqlite-migration-C_9TPlLD-DtKN8bi9.d.mts} +52 -4
- package/dist/sqlite-migration-C_9TPlLD-DtKN8bi9.d.mts.map +1 -0
- package/dist/{tables-C9afyQN9-CkIhVNYn.d.mts → tables-BY3u6rl_-Coqqy4JK.d.mts} +11 -3
- package/dist/tables-BY3u6rl_-Coqqy4JK.d.mts.map +1 -0
- package/dist/target.d.mts +12 -12
- package/dist/target.mjs +9 -9
- package/dist/target__codec-types.d.mts +3 -3
- package/dist/target__codecs.d.mts +2 -2
- package/dist/target__codecs.mjs +2 -2
- package/dist/target__control.mjs +52 -32
- package/dist/target__control.mjs.map +1 -1
- package/dist/target__migration.d.mts +2 -2
- package/dist/target__migration.mjs +2 -2
- package/dist/target__op-factory-call.d.mts +2 -2
- package/dist/target__op-factory-call.mjs +2 -2
- package/dist/target__pack.d.mts +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/{types-DpDK9e9U.d.mts → types-28E593y9.d.mts} +1 -1
- package/package.json +10 -10
- package/dist/codec-types-BQCSRfvR.d.mts +0 -1
- package/dist/codec-types-YpVaNlY0.d.mts +0 -2
- package/dist/codecs-BqOj4EKm-BaGeNd2m.d.mts.map +0 -1
- package/dist/codecs-DzkRnjjE.d.mts +0 -1
- package/dist/op-factory-call-B8bJlvkP.d.mts.map +0 -1
- package/dist/op-factory-call-BQYECylm-K7S_xgRS.mjs.map +0 -1
- package/dist/planner-DNwf8s8s-BoXZwpVs.mjs.map +0 -1
- package/dist/planner-produced-sqlite-migration-DxyN096L.d.mts +0 -1
- package/dist/planner-produced-sqlite-migration-DyUPHRKW-CFtZi2VG.d.mts.map +0 -1
- package/dist/sql-renderer-CgY2Sghp.d.mts +0 -1
- package/dist/sqlite-migration-BxQ65pKq-C7siJGuR.mjs.map +0 -1
- package/dist/sqlite-migration-CcXBptQr-DlKSNedI.d.mts.map +0 -1
- package/dist/tables-C9afyQN9-CkIhVNYn.d.mts.map +0 -1
package/dist/{op-factory-call-BQYECylm-K7S_xgRS.mjs → op-factory-call-Dxsyn5gF-CvHmM-h_.mjs}
RENAMED
|
@@ -7,16 +7,17 @@ import { DdlColumn, FunctionColumnDefault, LiteralColumnDefault, opaqueSql } fro
|
|
|
7
7
|
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
8
8
|
import "@prisma/orm-framework/components/codec";
|
|
9
9
|
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
10
|
-
import { assertNever } from "@prisma/orm-framework/utils/internal-error";
|
|
10
|
+
import { InternalError, assertNever } from "@prisma/orm-framework/utils/internal-error";
|
|
11
11
|
import "@prisma/orm-family-sql/contract/data-type";
|
|
12
12
|
import { type } from "arktype";
|
|
13
|
+
import { CliStructuredError } from "@prisma/orm-framework/errors/control";
|
|
13
14
|
import { expectAbsent, expectPresent, issueOutcome } from "@prisma/orm-framework/components/control";
|
|
14
|
-
import { checkSqlExpressionText, convertedColumnMeta, createSqlTextSources, encodeStoredValue, isUnfilledSlot, requiredColumnMeta, rowsHoldingValueMeta, slotSource, sqlOfSlot } from "@prisma/orm-family-sql/family/control";
|
|
15
|
+
import { UnfilledValue, checkSqlExpressionText, convertedColumnMeta, createSqlTextSources, encodeStoredValue, fillKeyProblem, fillValueProblem, isUnfilledSlot, keyPathOf, parseJsonPath, requiredColumnMeta, rowsHoldingValueMeta, slotSource, sqlOfSlot } from "@prisma/orm-family-sql/family/control";
|
|
15
16
|
import { RelationalSchemaNodeKind, sqlColumnTypesEqual } from "@prisma/orm-family-sql/schema-ir/types";
|
|
16
|
-
import { errorMigrationOperationOptionRemoved, unfilledPlaceholderOperation } from "@prisma/orm-framework/errors/migration";
|
|
17
|
+
import { errorMigrationOperationOptionRemoved, errorUnfilledPlaceholder, 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/sqlite/1-target/dist/op-factory-call-
|
|
20
|
+
//#region ../../../3-targets/sqlite/1-target/dist/op-factory-call-Dxsyn5gF.mjs
|
|
20
21
|
function foldAsciiLetters(identifier) {
|
|
21
22
|
return identifier.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
|
|
22
23
|
}
|
|
@@ -208,6 +209,130 @@ async function renameColumn(tableName, fromName, toName, lowerer) {
|
|
|
208
209
|
postcheck: [step(`verify column "${toName}" exists on "${tableName}"`, toPresent.sql, toPresent.params, expectPresent(`column "${toName}" on "${tableName}"`)), step(`verify column "${fromName}" no longer exists on "${tableName}"`, fromAbsent.sql, fromAbsent.params, expectAbsent(`column "${fromName}" on "${tableName}"`))]
|
|
209
210
|
};
|
|
210
211
|
}
|
|
212
|
+
/** The SQLite JSON path of member `name`, as an SQL string literal. */
|
|
213
|
+
function memberPath(name) {
|
|
214
|
+
return `'${escapeLiteral(`$.${JSON.stringify(name)}`)}'`;
|
|
215
|
+
}
|
|
216
|
+
function child(value, depth) {
|
|
217
|
+
return `(${value} -> e${depth}.fullkey)`;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The children of a list or an object rewritten one by one, keeping their order; `kept` filters
|
|
221
|
+
* them. Any other value is left as it is.
|
|
222
|
+
*/
|
|
223
|
+
function rewrittenChildren(value, depth, rewrite, kept) {
|
|
224
|
+
const e = `e${depth}`;
|
|
225
|
+
const each = `FROM json_each(${value}) AS ${e}${kept === void 0 ? "" : ` WHERE ${kept(child(value, depth))}`}`;
|
|
226
|
+
const rewritten = `json(${rewrite(child(value, depth))})`;
|
|
227
|
+
return `CASE json_type(${value}) WHEN 'array' THEN (SELECT json_group_array(${rewritten} ORDER BY ${e}.key) ${each}) WHEN 'object' THEN (SELECT json_group_object(${e}.key, ${rewritten} ORDER BY ${e}.id) ${each}) ELSE ${value} END`;
|
|
228
|
+
}
|
|
229
|
+
/** Whether `value` is a list or an object with a child that passes `test`. */
|
|
230
|
+
function someChild(value, depth, test) {
|
|
231
|
+
return `(json_type(${value}) IN ('array', 'object') AND EXISTS (SELECT 1 FROM json_each(${value}) AS e${depth} WHERE ${test(child(value, depth))}))`;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* The JSON text that rewrites `value` along `steps` with `leaf`. A member step that meets no object
|
|
235
|
+
* with the member, or a wildcard step that meets neither a list nor an object, leaves the value as
|
|
236
|
+
* it is.
|
|
237
|
+
*/
|
|
238
|
+
function jsonRewrite(value, steps, leaf, depth = 0) {
|
|
239
|
+
const [first, ...rest] = steps;
|
|
240
|
+
if (first === void 0) return leaf(value, depth);
|
|
241
|
+
if (first.kind === "member") {
|
|
242
|
+
const member = memberPath(first.name);
|
|
243
|
+
return `CASE WHEN json_type(${value}) = 'object' AND json_type(${value}, ${member}) IS NOT NULL THEN json_set(${value}, ${member}, json(${jsonRewrite(`(${value} -> ${member})`, rest, leaf, depth)})) ELSE ${value} END`;
|
|
244
|
+
}
|
|
245
|
+
return rewrittenChildren(value, depth, (c) => jsonRewrite(c, rest, leaf, depth + 1));
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Whether the JSON text `value` holds, at the end of `steps`, a value that passes `test`. Each step
|
|
249
|
+
* tests the type of what it reads first, so a SQL `NULL`, a JSON `null`, a missing member, a value
|
|
250
|
+
* of another type, or a `null` element or member value never fails the match.
|
|
251
|
+
*/
|
|
252
|
+
function jsonMatches(value, steps, test, depth = 0) {
|
|
253
|
+
const [first, ...rest] = steps;
|
|
254
|
+
if (first === void 0) return test(value, depth);
|
|
255
|
+
if (first.kind === "member") {
|
|
256
|
+
const member = memberPath(first.name);
|
|
257
|
+
return `(json_type(${value}) = 'object' AND json_type(${value}, ${member}) IS NOT NULL AND ${jsonMatches(`(${value} -> ${member})`, rest, test, depth)})`;
|
|
258
|
+
}
|
|
259
|
+
return someChild(value, depth, (c) => jsonMatches(c, rest, test, depth + 1));
|
|
260
|
+
}
|
|
261
|
+
/** Whether the JSON text `value` is an object that has member `name`. */
|
|
262
|
+
function hasMember(value, name) {
|
|
263
|
+
return `(json_type(${value}) = 'object' AND json_type(${value}, ${memberPath(name)}) IS NOT NULL)`;
|
|
264
|
+
}
|
|
265
|
+
function at(keyPath) {
|
|
266
|
+
return `at path ${JSON.stringify(keyPath.objectsText)}`;
|
|
267
|
+
}
|
|
268
|
+
/** Where in a column a key operation acts, as its label and step descriptions write it. */
|
|
269
|
+
function keyPlace(keyPath, tableName, columnName) {
|
|
270
|
+
return `${at(keyPath)} in column "${columnName}" of "${tableName}"`;
|
|
271
|
+
}
|
|
272
|
+
function noRowWhere(tableName, condition) {
|
|
273
|
+
return `SELECT NOT EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE ${condition}) AS result`;
|
|
274
|
+
}
|
|
275
|
+
function objectsMatch(columnName, keyPath, test) {
|
|
276
|
+
return jsonMatches(quoteIdentifier(columnName), keyPath.objects, test);
|
|
277
|
+
}
|
|
278
|
+
function renameStoredKey(tableName, columnName, keyPath, to) {
|
|
279
|
+
const column = quoteIdentifier(columnName);
|
|
280
|
+
const from = memberPath(keyPath.key);
|
|
281
|
+
const renamed = jsonRewrite(column, keyPath.objects, (object) => `CASE WHEN ${hasMember(object, keyPath.key)} THEN json_set(json_remove(${object}, ${from}), ${memberPath(to)}, json(${object} -> ${from})) ELSE ${object} END`);
|
|
282
|
+
const holdsKey = objectsMatch(columnName, keyPath, (object) => hasMember(object, keyPath.key));
|
|
283
|
+
const place = keyPlace(keyPath, tableName, columnName);
|
|
284
|
+
return {
|
|
285
|
+
id: `renameStoredKey.${tableName}.${columnName}.${keyPath.path.text}`,
|
|
286
|
+
label: `Rename key "${keyPath.key}" to "${to}" ${place}`,
|
|
287
|
+
operationClass: "widening",
|
|
288
|
+
target: {
|
|
289
|
+
id: "sqlite",
|
|
290
|
+
details: buildTargetDetails("column", columnName, tableName)
|
|
291
|
+
},
|
|
292
|
+
precheck: [step(`ensure no object ${place} has both key "${keyPath.key}" and key "${to}"; remove one of the two keys from those objects`, noRowWhere(tableName, objectsMatch(columnName, keyPath, (object) => `(${hasMember(object, keyPath.key)} AND ${hasMember(object, to)})`)))],
|
|
293
|
+
execute: [step(`rename key "${keyPath.key}" to "${to}" ${place}`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${renamed} WHERE ${holdsKey}`)],
|
|
294
|
+
postcheck: [step(`verify no object ${place} has key "${keyPath.key}"`, noRowWhere(tableName, holdsKey))]
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function removeStoredKey(tableName, columnName, keyPath) {
|
|
298
|
+
const column = quoteIdentifier(columnName);
|
|
299
|
+
const removed = jsonRewrite(column, keyPath.objects, (object) => `CASE WHEN ${hasMember(object, keyPath.key)} THEN json_remove(${object}, ${memberPath(keyPath.key)}) ELSE ${object} END`);
|
|
300
|
+
const holdsKey = objectsMatch(columnName, keyPath, (object) => hasMember(object, keyPath.key));
|
|
301
|
+
const place = keyPlace(keyPath, tableName, columnName);
|
|
302
|
+
return {
|
|
303
|
+
id: `removeStoredKey.${tableName}.${columnName}.${keyPath.path.text}`,
|
|
304
|
+
label: `Remove key "${keyPath.key}" ${place}`,
|
|
305
|
+
operationClass: "destructive",
|
|
306
|
+
target: {
|
|
307
|
+
id: "sqlite",
|
|
308
|
+
details: buildTargetDetails("column", columnName, tableName)
|
|
309
|
+
},
|
|
310
|
+
precheck: [],
|
|
311
|
+
execute: [step(`remove key "${keyPath.key}" ${place}`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${removed} WHERE ${holdsKey}`)],
|
|
312
|
+
postcheck: [step(`verify no object ${place} has key "${keyPath.key}"`, noRowWhere(tableName, holdsKey))]
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
/** `value` is the JSON text of the value the key takes, passed as a parameter. A key that holds JSON `null` is filled too, as a member that becomes required needs. */
|
|
316
|
+
function fillStoredKey(tableName, columnName, keyPath, value) {
|
|
317
|
+
const column = quoteIdentifier(columnName);
|
|
318
|
+
const member = memberPath(keyPath.key);
|
|
319
|
+
const lacks = (object) => `(json_type(${object}) = 'object' AND coalesce(json_type(${object}, ${member}), 'null') = 'null')`;
|
|
320
|
+
const filled = jsonRewrite(column, keyPath.objects, (object) => `CASE WHEN ${lacks(object)} THEN json_set(${object}, ${member}, json(?1)) ELSE ${object} END`);
|
|
321
|
+
const lacksKey = objectsMatch(columnName, keyPath, lacks);
|
|
322
|
+
const place = keyPlace(keyPath, tableName, columnName);
|
|
323
|
+
return {
|
|
324
|
+
id: `fillStoredKey.${tableName}.${columnName}.${keyPath.path.text}`,
|
|
325
|
+
label: `Fill key "${keyPath.key}" ${place}`,
|
|
326
|
+
operationClass: "widening",
|
|
327
|
+
target: {
|
|
328
|
+
id: "sqlite",
|
|
329
|
+
details: buildTargetDetails("column", columnName, tableName)
|
|
330
|
+
},
|
|
331
|
+
precheck: [],
|
|
332
|
+
execute: [step(`fill key "${keyPath.key}" ${place} in every object that lacks it or holds JSON null there`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${filled} WHERE ${lacksKey}`, [value])],
|
|
333
|
+
postcheck: [step(`verify every object ${place} has key "${keyPath.key}"`, noRowWhere(tableName, lacksKey))]
|
|
334
|
+
};
|
|
335
|
+
}
|
|
211
336
|
/** A stored value as operation ids, labels and step descriptions write it. */
|
|
212
337
|
function storedValueText(value) {
|
|
213
338
|
return JSON.stringify(value);
|
|
@@ -246,12 +371,140 @@ function removeStoredValue(tableName, columnName, value, mark) {
|
|
|
246
371
|
},
|
|
247
372
|
precheck: [{
|
|
248
373
|
...columnTakesNullOrValueAbsentCheck(tableName, columnName, value, takesNull),
|
|
249
|
-
...mark === void 0 ? {} : { meta: rowsHoldingValueMeta(
|
|
374
|
+
...mark === void 0 ? {} : { meta: rowsHoldingValueMeta({
|
|
375
|
+
...mark,
|
|
376
|
+
table: tableName,
|
|
377
|
+
column: columnName,
|
|
378
|
+
storedValue: value
|
|
379
|
+
}) }
|
|
250
380
|
}],
|
|
251
381
|
execute: [step(`remove value ${storedValueText(value)} from column "${columnName}"`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = NULL WHERE ${column} = ?1`, [value])],
|
|
252
382
|
postcheck: [valueAbsentCheck(tableName, columnName, value)]
|
|
253
383
|
};
|
|
254
384
|
}
|
|
385
|
+
function cascadeStep(tableName, columnName, value, cascade) {
|
|
386
|
+
const referenced = cascade.columns.map((column, index) => {
|
|
387
|
+
const reference = cascade.references[index];
|
|
388
|
+
if (reference === void 0) throw new InternalError(`Cascade of table "${cascade.table}" has no reference for column "${column}"`);
|
|
389
|
+
return `${quoteIdentifier(tableName)}.${quoteIdentifier(reference)} = ${quoteIdentifier(cascade.table)}.${quoteIdentifier(column)}`;
|
|
390
|
+
}).join(" AND ");
|
|
391
|
+
return step(`delete the rows of "${cascade.table}" whose row of "${tableName}" holds value ${storedValueText(value)} in column "${columnName}", as their foreign key's cascade would`, `DELETE FROM ${quoteIdentifier(cascade.table)} WHERE EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE ${quoteIdentifier(tableName)}.${quoteIdentifier(columnName)} = ?1 AND ${referenced})`, [value]);
|
|
392
|
+
}
|
|
393
|
+
/** The label of a `deleteRows` operation, which names each `cascade` table whose rows it deletes too. */
|
|
394
|
+
function deleteRowsLabel(tableName, columnName, value, cascade) {
|
|
395
|
+
const rows = `Delete the rows of "${tableName}" that hold value ${storedValueText(value)} in column "${columnName}"`;
|
|
396
|
+
if (cascade.length === 0) return rows;
|
|
397
|
+
return `${rows}, and the rows of ${cascade.map(({ table }) => `"${table}"`).join(", ")} that reference them`;
|
|
398
|
+
}
|
|
399
|
+
/** Deletes the rows that hold `value`, after the rows of each `cascade` table that reference them: the runner turns foreign keys off, so no declared cascade runs. */
|
|
400
|
+
function deleteRows(tableName, columnName, value, cascade) {
|
|
401
|
+
return {
|
|
402
|
+
id: `deleteRows.${tableName}.${columnName}.${idText(value)}`,
|
|
403
|
+
label: deleteRowsLabel(tableName, columnName, value, cascade),
|
|
404
|
+
operationClass: "destructive",
|
|
405
|
+
target: {
|
|
406
|
+
id: "sqlite",
|
|
407
|
+
details: buildTargetDetails("column", columnName, tableName)
|
|
408
|
+
},
|
|
409
|
+
precheck: [],
|
|
410
|
+
execute: [...cascade.map((foreignKey) => cascadeStep(tableName, columnName, value, foreignKey)), step(`delete the rows of "${tableName}" that hold value ${storedValueText(value)} in column "${columnName}"`, `DELETE FROM ${quoteIdentifier(tableName)} WHERE ${quoteIdentifier(columnName)} = ?1`, [value])],
|
|
411
|
+
postcheck: [valueAbsentCheck(tableName, columnName, value)]
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function pathParts(path) {
|
|
415
|
+
const last = path.steps.at(-1);
|
|
416
|
+
if (last === void 0) throw new InternalError("A stored value path ends at a selector");
|
|
417
|
+
return {
|
|
418
|
+
reach: path.steps.slice(0, -1),
|
|
419
|
+
last
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/** Whether the JSON text `value` is the JSON form of `stored`, bound as parameter `param`. */
|
|
423
|
+
function isStoredValue(value, stored, param) {
|
|
424
|
+
return `(json_type(${value}) ${typeof stored === "string" ? `= 'text'` : `IN ('integer', 'real')`} AND (${value} ->> '$') = ${param})`;
|
|
425
|
+
}
|
|
426
|
+
function memberHolds(object, name, stored, param) {
|
|
427
|
+
return `(json_type(${object}) = 'object' AND ${isStoredValue(`(${object} -> ${memberPath(name)})`, stored, param)})`;
|
|
428
|
+
}
|
|
429
|
+
/** Whether the JSON text of `column` holds `stored`, bound as parameter `param`, at the end of `path`. */
|
|
430
|
+
function holdsAtPath(column, path, stored, param) {
|
|
431
|
+
const { reach, last } = pathParts(path);
|
|
432
|
+
return jsonMatches(column, reach, (value, depth) => last.kind === "member" ? memberHolds(value, last.name, stored, param) : someChild(value, depth, (child) => isStoredValue(child, stored, param)));
|
|
433
|
+
}
|
|
434
|
+
/** The JSON text that sets the value at the end of `path` that equals `?1` to `replacement`, or, when `replacement` is undefined, removes it from a list or removes the member of an object that holds it. */
|
|
435
|
+
function rewriteAtPath(column, path, stored, replacement) {
|
|
436
|
+
const { reach, last } = pathParts(path);
|
|
437
|
+
if (last.kind === "member") return jsonRewrite(column, reach, (object) => `CASE WHEN ${memberHolds(object, last.name, stored, "?1")} THEN json_set(${object}, ${memberPath(last.name)}, ${replacement ?? "json('null')"}) ELSE ${object} END`);
|
|
438
|
+
return jsonRewrite(column, reach, (container, depth) => replacement === void 0 ? rewrittenChildren(container, depth, (child) => child, (child) => `NOT ${isStoredValue(child, stored, "?1")}`) : rewrittenChildren(container, depth, (child) => `CASE WHEN ${isStoredValue(child, stored, "?1")} THEN json_quote(${replacement}) ELSE ${child} END`));
|
|
439
|
+
}
|
|
440
|
+
function atPath(path, tableName, columnName) {
|
|
441
|
+
return `at path ${JSON.stringify(path.text)} in column "${columnName}" of "${tableName}"`;
|
|
442
|
+
}
|
|
443
|
+
function valueAbsentAtPathCheck(tableName, columnName, value, path) {
|
|
444
|
+
return step(`verify no row holds value ${storedValueText(value)} ${atPath(path, tableName, columnName)}`, `SELECT NOT EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE ${holdsAtPath(quoteIdentifier(columnName), path, value, "?1")}) AS result`, [value]);
|
|
445
|
+
}
|
|
446
|
+
/** The condition and parameters that find the rows of a column that hold a stored value, at `path` inside its JSON when it is given. */
|
|
447
|
+
function holdingStoredValue(columnName, storedValue, path) {
|
|
448
|
+
const column = quoteIdentifier(columnName);
|
|
449
|
+
if (path === void 0) return {
|
|
450
|
+
condition: `${column} = ?1`,
|
|
451
|
+
params: [storedValue]
|
|
452
|
+
};
|
|
453
|
+
const parsed = parseJsonPath(path);
|
|
454
|
+
if (!parsed.ok) throw new InternalError(`A stored value path is a JSON path: ${parsed.failure}`);
|
|
455
|
+
return {
|
|
456
|
+
condition: holdsAtPath(column, parsed.value, storedValue, "?1"),
|
|
457
|
+
params: [storedValue]
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function replaceStoredValueAtPath(tableName, columnName, from, to, path) {
|
|
461
|
+
const column = quoteIdentifier(columnName);
|
|
462
|
+
const place = atPath(path, tableName, columnName);
|
|
463
|
+
return {
|
|
464
|
+
id: `replaceStoredValue.${tableName}.${columnName}.${path.text}.${idText(from)}`,
|
|
465
|
+
label: `Replace value ${storedValueText(from)} with ${storedValueText(to)} ${place}`,
|
|
466
|
+
operationClass: "widening",
|
|
467
|
+
target: {
|
|
468
|
+
id: "sqlite",
|
|
469
|
+
details: buildTargetDetails("column", columnName, tableName)
|
|
470
|
+
},
|
|
471
|
+
precheck: [],
|
|
472
|
+
execute: [step(`replace value ${storedValueText(from)} with ${storedValueText(to)} ${place}`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${rewriteAtPath(column, path, from, "?2")} WHERE ${holdsAtPath(column, path, from, "?1")}`, [from, to])],
|
|
473
|
+
postcheck: [valueAbsentAtPathCheck(tableName, columnName, from, path)]
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Removes a stored value inside JSON: takes it out of a list, or removes its member from an object,
|
|
478
|
+
* when `path` ends at a wildcard, else sets the member to JSON `null`. When the member takes no
|
|
479
|
+
* null, the check fails while a row holds the value.
|
|
480
|
+
*/
|
|
481
|
+
function removeStoredValueAtPath(tableName, columnName, value, path, nullable, mark) {
|
|
482
|
+
const column = quoteIdentifier(columnName);
|
|
483
|
+
const place = atPath(path, tableName, columnName);
|
|
484
|
+
const holds = holdsAtPath(column, path, value, "?1");
|
|
485
|
+
const required = pathParts(path).last.kind === "member" && !nullable;
|
|
486
|
+
return {
|
|
487
|
+
id: `removeStoredValue.${tableName}.${columnName}.${path.text}.${idText(value)}`,
|
|
488
|
+
label: `Remove value ${storedValueText(value)} ${place}`,
|
|
489
|
+
operationClass: "destructive",
|
|
490
|
+
target: {
|
|
491
|
+
id: "sqlite",
|
|
492
|
+
details: buildTargetDetails("column", columnName, tableName)
|
|
493
|
+
},
|
|
494
|
+
precheck: required ? [{
|
|
495
|
+
...step(`ensure no row holds ${storedValueText(value)} ${place}, a member that takes no null`, `SELECT NOT EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE ${holds}) AS result`, [value]),
|
|
496
|
+
...mark === void 0 ? {} : { meta: rowsHoldingValueMeta({
|
|
497
|
+
...mark,
|
|
498
|
+
table: tableName,
|
|
499
|
+
column: columnName,
|
|
500
|
+
storedValue: value,
|
|
501
|
+
path: path.text
|
|
502
|
+
}) }
|
|
503
|
+
}] : [],
|
|
504
|
+
execute: [step(`remove value ${storedValueText(value)} ${place}`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${rewriteAtPath(column, path, value, void 0)} WHERE ${holds}`, [value])],
|
|
505
|
+
postcheck: [valueAbsentAtPathCheck(tableName, columnName, value, path)]
|
|
506
|
+
};
|
|
507
|
+
}
|
|
255
508
|
function buildCreateIndexSql(tableName, indexName, columns, unique = false) {
|
|
256
509
|
return `CREATE ${unique ? "UNIQUE " : ""}INDEX ${quoteIdentifier(indexName)} ON ${quoteIdentifier(tableName)} (${columns.map(quoteIdentifier).join(", ")})`;
|
|
257
510
|
}
|
|
@@ -842,7 +1095,7 @@ var DropTableCall = class extends SqliteOpFactoryCallNode {
|
|
|
842
1095
|
constructor(tableName) {
|
|
843
1096
|
super();
|
|
844
1097
|
this.tableName = tableName;
|
|
845
|
-
this.label = `Drop table ${tableName}`;
|
|
1098
|
+
this.label = `Drop table "${tableName}"`;
|
|
846
1099
|
this.freeze();
|
|
847
1100
|
}
|
|
848
1101
|
async toOp(lowerer) {
|
|
@@ -855,7 +1108,7 @@ var DropTableCall = class extends SqliteOpFactoryCallNode {
|
|
|
855
1108
|
const absent = await lowerer.lowerToExecuteRequest(checks.tableAbsent());
|
|
856
1109
|
return {
|
|
857
1110
|
id: `dropTable.${this.tableName}`,
|
|
858
|
-
label: `Drop table ${this.tableName}`,
|
|
1111
|
+
label: `Drop table "${this.tableName}"`,
|
|
859
1112
|
summary: `Drops table ${this.tableName} which is not in the contract`,
|
|
860
1113
|
operationClass: "destructive",
|
|
861
1114
|
target: {
|
|
@@ -1206,7 +1459,7 @@ var DropColumnCall = class extends SqliteOpFactoryCallNode {
|
|
|
1206
1459
|
super();
|
|
1207
1460
|
this.tableName = tableName;
|
|
1208
1461
|
this.columnName = columnName;
|
|
1209
|
-
this.label = `Drop column ${columnName}
|
|
1462
|
+
this.label = `Drop column "${columnName}" from "${tableName}"`;
|
|
1210
1463
|
this.freeze();
|
|
1211
1464
|
}
|
|
1212
1465
|
async toOp(lowerer) {
|
|
@@ -1220,7 +1473,7 @@ var DropColumnCall = class extends SqliteOpFactoryCallNode {
|
|
|
1220
1473
|
const absent = await lowerer.lowerToExecuteRequest(checks.columnAbsent());
|
|
1221
1474
|
return {
|
|
1222
1475
|
id: `dropColumn.${this.tableName}.${this.columnName}`,
|
|
1223
|
-
label: `Drop column ${this.columnName}
|
|
1476
|
+
label: `Drop column "${this.columnName}" from "${this.tableName}"`,
|
|
1224
1477
|
summary: `Drops column ${this.columnName} on ${this.tableName} which is not in the contract`,
|
|
1225
1478
|
operationClass: "destructive",
|
|
1226
1479
|
target: {
|
|
@@ -1335,15 +1588,34 @@ var DropIndexCall = class extends SqliteOpFactoryCallNode {
|
|
|
1335
1588
|
return [];
|
|
1336
1589
|
}
|
|
1337
1590
|
};
|
|
1338
|
-
function replaceStoredValueSource(tableName, columnName, from, to, codec) {
|
|
1591
|
+
function replaceStoredValueSource(tableName, columnName, from, to, codec, path) {
|
|
1339
1592
|
return `this.replaceStoredValue({ ${[
|
|
1340
1593
|
`table: ${jsonToTsSource(tableName)}`,
|
|
1341
1594
|
`column: ${jsonToTsSource(columnName)}`,
|
|
1595
|
+
...pathSource(path),
|
|
1342
1596
|
`from: ${from}`,
|
|
1343
1597
|
`to: ${to}`,
|
|
1344
1598
|
...codecRefSource(codec)
|
|
1345
1599
|
].join(", ")} })`;
|
|
1346
1600
|
}
|
|
1601
|
+
function pathSource(path) {
|
|
1602
|
+
return path === void 0 ? [] : [`path: ${jsonToTsSource(path.text)}`];
|
|
1603
|
+
}
|
|
1604
|
+
function storedValuePlace(path, tableName, columnName) {
|
|
1605
|
+
return path === void 0 ? `in column "${columnName}" of "${tableName}"` : `at path ${JSON.stringify(path.text)} in column "${columnName}" of "${tableName}"`;
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* The path a stored value call acts at inside JSON: it ends at a member name or at a list
|
|
1609
|
+
* wildcard, and the value is compared as a JSON value, so it takes no codec.
|
|
1610
|
+
*/
|
|
1611
|
+
function requireValuePath(path, codec, factory, tableName, columnName) {
|
|
1612
|
+
if (path === void 0) return void 0;
|
|
1613
|
+
const location = columnCallLocation(tableName, columnName);
|
|
1614
|
+
const parsed = requirePath(path, factory, location);
|
|
1615
|
+
if (parsed.steps.length === 0) refusedArgument(factory, location, "path", `path ${JSON.stringify(path)} does not end at a member name or a wildcard ([*] or .*)`);
|
|
1616
|
+
if (codec !== void 0) refusedArgument(factory, location, "path", "path takes no codecRef: a value inside JSON is compared as a JSON value");
|
|
1617
|
+
return parsed;
|
|
1618
|
+
}
|
|
1347
1619
|
function codecRefSource(codec) {
|
|
1348
1620
|
return codec === void 0 ? [] : [`codecRef: ${jsonToTsSource(codec.codecRef)}`];
|
|
1349
1621
|
}
|
|
@@ -1352,7 +1624,7 @@ function requireStoredValue(value, factory, tableName, columnName, argument) {
|
|
|
1352
1624
|
const got = value;
|
|
1353
1625
|
throw sqliteError("CONTRACT.ARGUMENT_INVALID", `${factory}({ table: ${jsonToTsSource(tableName)}, column: ${jsonToTsSource(columnName)} }): ${argument} must be a string or a number; got ${typeof got === "number" || got === void 0 ? String(got) : JSON.stringify(got)}`, { meta: { what: `${factory} ${argument}` } });
|
|
1354
1626
|
}
|
|
1355
|
-
/** Changes one stored value of a column to another, in every row that holds it. */
|
|
1627
|
+
/** Changes one stored value of a column to another, in every row that holds it, or at a path inside the JSON of the column. */
|
|
1356
1628
|
var ReplaceStoredValueCall = class extends SqliteOpFactoryCallNode {
|
|
1357
1629
|
factoryName = "replaceStoredValue";
|
|
1358
1630
|
operationClass = "widening";
|
|
@@ -1362,28 +1634,32 @@ var ReplaceStoredValueCall = class extends SqliteOpFactoryCallNode {
|
|
|
1362
1634
|
to;
|
|
1363
1635
|
/** The column's codec, which writes `from` and `to` as the database stores them. */
|
|
1364
1636
|
codec;
|
|
1637
|
+
/** The path inside the column's JSON at which the value is replaced, compared as a JSON value. */
|
|
1638
|
+
path;
|
|
1365
1639
|
label;
|
|
1366
|
-
constructor(tableName, columnName, from, to, codec) {
|
|
1640
|
+
constructor(tableName, columnName, from, to, codec, path) {
|
|
1367
1641
|
super();
|
|
1368
1642
|
this.codec = codec;
|
|
1369
1643
|
this.tableName = tableName;
|
|
1370
1644
|
this.columnName = columnName;
|
|
1371
1645
|
this.from = requireStoredValue(from, this.factoryName, tableName, columnName, "from");
|
|
1372
1646
|
this.to = requireStoredValue(to, this.factoryName, tableName, columnName, "to");
|
|
1373
|
-
this.
|
|
1647
|
+
this.path = requireValuePath(path, codec, this.factoryName, tableName, columnName);
|
|
1648
|
+
this.label = `Replace value ${storedValueText(from)} with ${storedValueText(to)} ${storedValuePlace(this.path, tableName, columnName)}`;
|
|
1374
1649
|
this.freeze();
|
|
1375
1650
|
}
|
|
1376
1651
|
async toOp() {
|
|
1652
|
+
if (this.path !== void 0) return replaceStoredValueAtPath(this.tableName, this.columnName, this.from, this.to, this.path);
|
|
1377
1653
|
return replaceStoredValue(this.tableName, this.columnName, await encodeStoredValue(this.from, this.codec), await encodeStoredValue(this.to, this.codec));
|
|
1378
1654
|
}
|
|
1379
1655
|
renderTypeScript() {
|
|
1380
|
-
return replaceStoredValueSource(this.tableName, this.columnName, jsonToTsSource(this.from), jsonToTsSource(this.to), this.codec);
|
|
1656
|
+
return replaceStoredValueSource(this.tableName, this.columnName, jsonToTsSource(this.from), jsonToTsSource(this.to), this.codec, this.path);
|
|
1381
1657
|
}
|
|
1382
1658
|
importRequirements() {
|
|
1383
1659
|
return [];
|
|
1384
1660
|
}
|
|
1385
1661
|
};
|
|
1386
|
-
/** Removes one stored value of a column by setting it to NULL in every row that holds it. */
|
|
1662
|
+
/** Removes one stored value of a column by setting it to NULL in every row that holds it, or removes it at a path inside the JSON of the column. */
|
|
1387
1663
|
var RemoveStoredValueCall = class extends SqliteOpFactoryCallNode {
|
|
1388
1664
|
factoryName = "removeStoredValue";
|
|
1389
1665
|
operationClass = "destructive";
|
|
@@ -1394,29 +1670,40 @@ var RemoveStoredValueCall = class extends SqliteOpFactoryCallNode {
|
|
|
1394
1670
|
codec;
|
|
1395
1671
|
/** The enum value whose stored value is removed, which the check that fails while rows of a required column hold it names. */
|
|
1396
1672
|
enumValue;
|
|
1673
|
+
/** The path inside the column's JSON at which the value is removed, compared as a JSON value. */
|
|
1674
|
+
path;
|
|
1675
|
+
/** Whether the member at `path` takes JSON `null`; when it does not, the check fails while a row holds the value. */
|
|
1676
|
+
nullable;
|
|
1397
1677
|
label;
|
|
1398
|
-
constructor(tableName, columnName, value, codec, enumValue) {
|
|
1678
|
+
constructor(tableName, columnName, value, codec, enumValue, path, nullable) {
|
|
1399
1679
|
super();
|
|
1400
1680
|
this.codec = codec;
|
|
1401
1681
|
this.enumValue = enumValue;
|
|
1402
1682
|
this.tableName = tableName;
|
|
1403
1683
|
this.columnName = columnName;
|
|
1404
1684
|
this.value = requireStoredValue(value, this.factoryName, tableName, columnName, "value");
|
|
1405
|
-
this.
|
|
1685
|
+
this.path = requireValuePath(path, codec, this.factoryName, tableName, columnName);
|
|
1686
|
+
if (nullable !== void 0 && this.path === void 0) refusedArgument(this.factoryName, columnCallLocation(tableName, columnName), "nullable", "nullable needs a path: a column says itself whether it takes NULL");
|
|
1687
|
+
this.nullable = nullable === true;
|
|
1688
|
+
this.label = `Remove value ${storedValueText(value)} ${this.path === void 0 ? `from column "${columnName}" of "${tableName}"` : storedValuePlace(this.path, tableName, columnName)}`;
|
|
1406
1689
|
this.freeze();
|
|
1407
1690
|
}
|
|
1408
1691
|
async toOp() {
|
|
1409
|
-
|
|
1692
|
+
const mark = this.enumValue === void 0 ? void 0 : {
|
|
1410
1693
|
enumValue: this.enumValue,
|
|
1411
1694
|
value: this.value,
|
|
1412
1695
|
...this.codec === void 0 ? {} : { codecRef: this.codec.codecRef }
|
|
1413
|
-
}
|
|
1696
|
+
};
|
|
1697
|
+
if (this.path !== void 0) return removeStoredValueAtPath(this.tableName, this.columnName, this.value, this.path, this.nullable, mark);
|
|
1698
|
+
return removeStoredValue(this.tableName, this.columnName, await encodeStoredValue(this.value, this.codec), mark);
|
|
1414
1699
|
}
|
|
1415
1700
|
renderTypeScript() {
|
|
1416
1701
|
return `this.removeStoredValue({ ${[
|
|
1417
1702
|
`table: ${jsonToTsSource(this.tableName)}`,
|
|
1418
1703
|
`column: ${jsonToTsSource(this.columnName)}`,
|
|
1704
|
+
...pathSource(this.path),
|
|
1419
1705
|
`value: ${jsonToTsSource(this.value)}`,
|
|
1706
|
+
...this.nullable ? ["nullable: true"] : [],
|
|
1420
1707
|
...codecRefSource(this.codec),
|
|
1421
1708
|
...this.enumValue === void 0 ? [] : [`enumValue: ${jsonToTsSource(this.enumValue)}`]
|
|
1422
1709
|
].join(", ")} })`;
|
|
@@ -1425,6 +1712,204 @@ var RemoveStoredValueCall = class extends SqliteOpFactoryCallNode {
|
|
|
1425
1712
|
return [];
|
|
1426
1713
|
}
|
|
1427
1714
|
};
|
|
1715
|
+
/** Deletes the rows whose column holds one stored value. */
|
|
1716
|
+
var DeleteRowsCall = class extends SqliteOpFactoryCallNode {
|
|
1717
|
+
factoryName = "deleteRows";
|
|
1718
|
+
operationClass = "destructive";
|
|
1719
|
+
tableName;
|
|
1720
|
+
columnName;
|
|
1721
|
+
value;
|
|
1722
|
+
/** The column's codec, which writes `value` as the database stores it. */
|
|
1723
|
+
codec;
|
|
1724
|
+
/** The tables whose rows reference the deleted rows through a foreign key that cascades, deleted first. */
|
|
1725
|
+
cascade;
|
|
1726
|
+
label;
|
|
1727
|
+
constructor(tableName, columnName, value, codec, cascade = []) {
|
|
1728
|
+
super();
|
|
1729
|
+
this.codec = codec;
|
|
1730
|
+
this.tableName = tableName;
|
|
1731
|
+
this.columnName = columnName;
|
|
1732
|
+
this.value = requireStoredValue(value, this.factoryName, tableName, columnName, "value");
|
|
1733
|
+
this.cascade = cascade.map((foreignKey) => requireCascade(foreignKey, this.factoryName, columnCallLocation(tableName, columnName)));
|
|
1734
|
+
this.label = deleteRowsLabel(tableName, columnName, value, this.cascade);
|
|
1735
|
+
this.freeze();
|
|
1736
|
+
}
|
|
1737
|
+
async toOp() {
|
|
1738
|
+
return deleteRows(this.tableName, this.columnName, await encodeStoredValue(this.value, this.codec), this.cascade);
|
|
1739
|
+
}
|
|
1740
|
+
renderTypeScript() {
|
|
1741
|
+
return `this.deleteRows({ ${[
|
|
1742
|
+
`table: ${jsonToTsSource(this.tableName)}`,
|
|
1743
|
+
`column: ${jsonToTsSource(this.columnName)}`,
|
|
1744
|
+
`value: ${jsonToTsSource(this.value)}`,
|
|
1745
|
+
...codecRefSource(this.codec),
|
|
1746
|
+
...this.cascade.length === 0 ? [] : [`cascade: ${jsonToTsSource(this.cascade)}`]
|
|
1747
|
+
].join(", ")} })`;
|
|
1748
|
+
}
|
|
1749
|
+
importRequirements() {
|
|
1750
|
+
return [];
|
|
1751
|
+
}
|
|
1752
|
+
};
|
|
1753
|
+
function requireCascade(foreignKey, factory, location) {
|
|
1754
|
+
const { columns, references } = foreignKey;
|
|
1755
|
+
return columns.length > 0 && columns.length === references.length ? {
|
|
1756
|
+
table: foreignKey.table,
|
|
1757
|
+
columns: [...columns],
|
|
1758
|
+
references: [...references]
|
|
1759
|
+
} : refusedArgument(factory, location, "cascade", `cascade of table ${JSON.stringify(foreignKey.table)} must name as many columns as references, and at least one`);
|
|
1760
|
+
}
|
|
1761
|
+
function columnCallLocation(tableName, columnName) {
|
|
1762
|
+
return [`table: ${jsonToTsSource(tableName)}`, `column: ${jsonToTsSource(columnName)}`];
|
|
1763
|
+
}
|
|
1764
|
+
function refusedArgument(factory, location, argument, why) {
|
|
1765
|
+
throw sqliteError("CONTRACT.ARGUMENT_INVALID", `${factory}({ ${location.join(", ")} }): ${why}`, { meta: { what: `${factory} ${argument}` } });
|
|
1766
|
+
}
|
|
1767
|
+
function requirePath(text, factory, location) {
|
|
1768
|
+
const parsed = parseJsonPath(text);
|
|
1769
|
+
return parsed.ok ? parsed.value : refusedArgument(factory, location, "path", parsed.failure);
|
|
1770
|
+
}
|
|
1771
|
+
function requireKeyPath(text, factory, location) {
|
|
1772
|
+
return keyPathOf(requirePath(text, factory, location)) ?? refusedArgument(factory, location, "path", `path ${JSON.stringify(text)} does not end at a member name, the key the operation acts on`);
|
|
1773
|
+
}
|
|
1774
|
+
/** Renames the key at the end of a JSON path, in every object of a column the path reaches. */
|
|
1775
|
+
var RenameStoredKeyCall = class extends SqliteOpFactoryCallNode {
|
|
1776
|
+
factoryName = "renameStoredKey";
|
|
1777
|
+
operationClass = "widening";
|
|
1778
|
+
tableName;
|
|
1779
|
+
columnName;
|
|
1780
|
+
path;
|
|
1781
|
+
to;
|
|
1782
|
+
label;
|
|
1783
|
+
constructor(tableName, columnName, path, to) {
|
|
1784
|
+
super();
|
|
1785
|
+
const location = columnCallLocation(tableName, columnName);
|
|
1786
|
+
this.tableName = tableName;
|
|
1787
|
+
this.columnName = columnName;
|
|
1788
|
+
this.path = requireKeyPath(path, this.factoryName, location);
|
|
1789
|
+
if (typeof to !== "string" || to === this.path.key) refusedArgument(this.factoryName, location, "to", `to must be a key other than ${JSON.stringify(this.path.key)}; got ${JSON.stringify(to)}`);
|
|
1790
|
+
this.to = to;
|
|
1791
|
+
this.label = `Rename key "${this.path.key}" to "${to}" ${keyPlace(this.path, tableName, columnName)}`;
|
|
1792
|
+
this.freeze();
|
|
1793
|
+
}
|
|
1794
|
+
toOp() {
|
|
1795
|
+
return renameStoredKey(this.tableName, this.columnName, this.path, this.to);
|
|
1796
|
+
}
|
|
1797
|
+
renderTypeScript() {
|
|
1798
|
+
return `this.renameStoredKey({ ${[
|
|
1799
|
+
...columnCallLocation(this.tableName, this.columnName),
|
|
1800
|
+
`path: ${jsonToTsSource(this.path.path.text)}`,
|
|
1801
|
+
`to: ${jsonToTsSource(this.to)}`
|
|
1802
|
+
].join(", ")} })`;
|
|
1803
|
+
}
|
|
1804
|
+
importRequirements() {
|
|
1805
|
+
return [];
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
/** Removes the key at the end of a JSON path, from every object of a column the path reaches. */
|
|
1809
|
+
var RemoveStoredKeyCall = class extends SqliteOpFactoryCallNode {
|
|
1810
|
+
factoryName = "removeStoredKey";
|
|
1811
|
+
operationClass = "destructive";
|
|
1812
|
+
tableName;
|
|
1813
|
+
columnName;
|
|
1814
|
+
path;
|
|
1815
|
+
label;
|
|
1816
|
+
constructor(tableName, columnName, path) {
|
|
1817
|
+
super();
|
|
1818
|
+
this.tableName = tableName;
|
|
1819
|
+
this.columnName = columnName;
|
|
1820
|
+
this.path = requireKeyPath(path, this.factoryName, columnCallLocation(tableName, columnName));
|
|
1821
|
+
this.label = `Remove key "${this.path.key}" ${keyPlace(this.path, tableName, columnName)}`;
|
|
1822
|
+
this.freeze();
|
|
1823
|
+
}
|
|
1824
|
+
toOp() {
|
|
1825
|
+
return removeStoredKey(this.tableName, this.columnName, this.path);
|
|
1826
|
+
}
|
|
1827
|
+
renderTypeScript() {
|
|
1828
|
+
return `this.removeStoredKey({ ${[...columnCallLocation(this.tableName, this.columnName), `path: ${jsonToTsSource(this.path.path.text)}`].join(", ")} })`;
|
|
1829
|
+
}
|
|
1830
|
+
importRequirements() {
|
|
1831
|
+
return [];
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
function requireJsonValue(value, factory, location) {
|
|
1835
|
+
return JSON.stringify(value) === void 0 ? refusedArgument(factory, location, "value", "value must be a JSON value; got undefined") : value;
|
|
1836
|
+
}
|
|
1837
|
+
function requireFillValue(value, field, factory, location) {
|
|
1838
|
+
const json = requireJsonValue(value, factory, location);
|
|
1839
|
+
const problem = fillValueProblem(json, field);
|
|
1840
|
+
return problem === void 0 ? json : refusedArgument(factory, location, "value", problem);
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* The operation of a fill whose value is an unfilled placeholder: a promise that rejects with
|
|
1844
|
+
* `MIGRATION.UNFILLED_PLACEHOLDER`, whose meta names the operation it stands for, so that a plan can
|
|
1845
|
+
* list it before the user fills the value.
|
|
1846
|
+
*/
|
|
1847
|
+
function unfilledFillOperation(slot, standsFor) {
|
|
1848
|
+
const unfilled = errorUnfilledPlaceholder(slot);
|
|
1849
|
+
const operation = Promise.reject(new CliStructuredError(unfilled.code, unfilled.message, {
|
|
1850
|
+
...ifDefined("why", unfilled.why),
|
|
1851
|
+
...ifDefined("fix", unfilled.fix),
|
|
1852
|
+
meta: {
|
|
1853
|
+
slot,
|
|
1854
|
+
operation: {
|
|
1855
|
+
id: standsFor.id,
|
|
1856
|
+
label: standsFor.label,
|
|
1857
|
+
operationClass: standsFor.operationClass
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
}));
|
|
1861
|
+
operation.catch(() => void 0);
|
|
1862
|
+
return operation;
|
|
1863
|
+
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Sets the key at the end of a JSON path to a value, in every object of a column the path reaches that lacks it or holds JSON `null` there. An unfilled value is a placeholder the user fills in `migration.ts`; until then the call lowers to no operation.
|
|
1866
|
+
*/
|
|
1867
|
+
var FillStoredKeyCall = class extends SqliteOpFactoryCallNode {
|
|
1868
|
+
factoryName = "fillStoredKey";
|
|
1869
|
+
operationClass = "widening";
|
|
1870
|
+
tableName;
|
|
1871
|
+
columnName;
|
|
1872
|
+
path;
|
|
1873
|
+
value;
|
|
1874
|
+
/** The value object field the key holds, which must take `value`. */
|
|
1875
|
+
field;
|
|
1876
|
+
label;
|
|
1877
|
+
constructor(tableName, columnName, path, value, field) {
|
|
1878
|
+
super();
|
|
1879
|
+
const location = columnCallLocation(tableName, columnName);
|
|
1880
|
+
this.tableName = tableName;
|
|
1881
|
+
this.columnName = columnName;
|
|
1882
|
+
this.path = requireKeyPath(path, this.factoryName, location);
|
|
1883
|
+
const keyProblem = fillKeyProblem({
|
|
1884
|
+
key: this.path.key,
|
|
1885
|
+
path: this.path.path.text
|
|
1886
|
+
}, field);
|
|
1887
|
+
if (keyProblem !== void 0) refusedArgument(this.factoryName, location, "field", keyProblem);
|
|
1888
|
+
this.field = field;
|
|
1889
|
+
this.value = value instanceof UnfilledValue ? value : requireFillValue(value, field, this.factoryName, location);
|
|
1890
|
+
this.label = `Fill key "${this.path.key}" ${keyPlace(this.path, tableName, columnName)}`;
|
|
1891
|
+
this.freeze();
|
|
1892
|
+
}
|
|
1893
|
+
toOp() {
|
|
1894
|
+
if (this.value instanceof UnfilledValue) return unfilledFillOperation(this.value.slot, fillStoredKey(this.tableName, this.columnName, this.path, "null"));
|
|
1895
|
+
return fillStoredKey(this.tableName, this.columnName, this.path, JSON.stringify(this.value));
|
|
1896
|
+
}
|
|
1897
|
+
renderTypeScript() {
|
|
1898
|
+
const value = this.value instanceof UnfilledValue ? `placeholder(${jsonToTsSource(this.value.slot)})` : jsonToTsSource(this.value);
|
|
1899
|
+
return `this.fillStoredKey({ ${[
|
|
1900
|
+
...columnCallLocation(this.tableName, this.columnName),
|
|
1901
|
+
`path: ${jsonToTsSource(this.path.path.text)}`,
|
|
1902
|
+
`value: ${value}`,
|
|
1903
|
+
...this.field === void 0 ? [] : [`field: ${jsonToTsSource(this.field.text)}`]
|
|
1904
|
+
].join(", ")} })`;
|
|
1905
|
+
}
|
|
1906
|
+
importRequirements() {
|
|
1907
|
+
return this.value instanceof UnfilledValue ? [{
|
|
1908
|
+
moduleSpecifier: TARGET_MIGRATION_MODULE,
|
|
1909
|
+
symbol: "placeholder"
|
|
1910
|
+
}] : [];
|
|
1911
|
+
}
|
|
1912
|
+
};
|
|
1428
1913
|
/**
|
|
1429
1914
|
* A planner-generated data-transform stub. The current default strategy
|
|
1430
1915
|
* (`nullabilityTighteningBackfillStrategy`) emits one of these with a
|
|
@@ -1551,6 +2036,6 @@ function renderPostcheck(postcheck) {
|
|
|
1551
2036
|
}));
|
|
1552
2037
|
}
|
|
1553
2038
|
//#endregion
|
|
1554
|
-
export {
|
|
2039
|
+
export { step as A, holdingStoredValue as C, renameTableSteps as D, rebuiltTableName as E, sqliteDefaultToDdlColumnDefault as O, conversionCopyOf as S, isSqliteOpFactoryCall as T, RenameTableCall as _, DeleteRowsCall as a, buildRecreatePostchecks as b, DropTableCall as c, RawSqlCall as d, RecreateTableCall as f, RenameStoredKeyCall as g, RenameColumnCall as h, DataTransformCall as i, sqliteIdentifiersCollide as k, FillStoredKeyCall as l, RemoveStoredValueCall as m, CreateIndexCall as n, DropColumnCall as o, RemoveStoredKeyCall as p, CreateTableCall as r, DropIndexCall as s, AddColumnCall as t, INLINE_AUTOINCREMENT_PRIMARY_KEY as u, ReplaceStoredValueCall as v, indexReplacementCalls as w, buildRecreateSummary as x, affinityStorageClasses as y };
|
|
1555
2040
|
|
|
1556
|
-
//# sourceMappingURL=op-factory-call-
|
|
2041
|
+
//# sourceMappingURL=op-factory-call-Dxsyn5gF-CvHmM-h_.mjs.map
|