@metaobjectsdev/migrate-ts 0.24.4 → 0.24.5

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.
@@ -203,13 +203,39 @@ async function runSqlFileWithLedgerMutation(db, sqlText, mutateLedger) {
203
203
  // rejects a nested BEGIN, which made table-rebuild migrations un-appliable on the
204
204
  // scaffold's default dialect) and its own `PRAGMA foreign_keys = OFF`, which is a
205
205
  // no-op inside a transaction. See runner-transaction-pass.ts for the full rationale.
206
- const { statements } = prepareForRunnerTransaction(sqlText);
207
- await db.transaction().execute(async (trx) => {
208
- for (const stmt of statements) {
209
- await sql.raw(stmt).execute(trx);
206
+ const { statements, requiresForeignKeysOff, postTransactionChecks } = prepareForRunnerTransaction(sqlText);
207
+ // OUTSIDE the transaction, deliberately: `PRAGMA foreign_keys` is a no-op inside one,
208
+ // and `defer_foreign_keys` cannot stand in for it here `DROP TABLE`'s implicit delete
209
+ // records a deferred violation per referencing row that the repairing RENAME never
210
+ // decrements, so COMMIT fails. See runner-transaction-pass.ts.
211
+ if (requiresForeignKeysOff) {
212
+ await sql.raw("PRAGMA foreign_keys = OFF").execute(db);
213
+ }
214
+ try {
215
+ await db.transaction().execute(async (trx) => {
216
+ for (const stmt of statements) {
217
+ await sql.raw(stmt).execute(trx);
218
+ }
219
+ await mutateLedger(trx);
220
+ });
221
+ }
222
+ finally {
223
+ // Restore before anything else touches this connection, even if the migration threw.
224
+ if (requiresForeignKeysOff) {
225
+ await sql.raw("PRAGMA foreign_keys = ON").execute(db);
210
226
  }
211
- await mutateLedger(trx);
212
- });
227
+ }
228
+ // The file's own safety net, finally able to report. Running it inside the transaction
229
+ // discarded its rows, so a rebuild that DID strand a reference passed silently.
230
+ for (const check of postTransactionChecks) {
231
+ const result = await sql.raw(check).execute(db);
232
+ const rows = result.rows ?? [];
233
+ if (rows.length > 0) {
234
+ throw new Error(`migration left ${rows.length} dangling foreign-key reference(s): ${check} reported ` +
235
+ `${JSON.stringify(rows.slice(0, 5))}. The database is NOT consistent — restore from ` +
236
+ `backup rather than continuing.`);
237
+ }
238
+ }
213
239
  }
214
240
  async function discoverMigrations(dir) {
215
241
  let entries;
@@ -1 +1 @@
1
- {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/apply/apply.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/apply/apply.ts"],"names":[],"mappings":"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"}
@@ -32,13 +32,33 @@
32
32
  * <h3>What it does</h3>
33
33
  * 1. **Drops transaction control** (`BEGIN`/`COMMIT`/`ROLLBACK`/`SAVEPOINT`/`RELEASE`).
34
34
  * The runner's transaction supplies the atomicity the file was asking for.
35
- * 2. **Rewrites `PRAGMA foreign_keys = OFF` to `PRAGMA defer_foreign_keys = ON`.** This is
36
- * not cosmetic: `foreign_keys` is a **no-op inside a transaction**, so the rebuild would
37
- * lose its FK protection precisely where it needs it (dropping a referenced table).
38
- * `defer_foreign_keys` is the in-transaction equivalent enforcement is deferred to
39
- * commit — and is exactly what the D1 cascade emitter uses for the same reason.
40
- * 3. **Drops the matching `PRAGMA foreign_keys = ON`**, whose only job was to undo (1);
41
- * `defer_foreign_keys` resets itself at commit.
35
+ * 2. **Reports `PRAGMA foreign_keys = OFF` to the caller as `requiresForeignKeysOff`**
36
+ * instead of executing it, because the pragma is a **no-op inside a transaction** and
37
+ * must be issued BEFORE the transaction opens. That is SQLite's own documented
38
+ * recreate-and-copy procedure, and it is the only form that works.
39
+ *
40
+ * This used to rewrite it to `PRAGMA defer_foreign_keys = ON`, reasoning that deferral
41
+ * is "the in-transaction equivalent". **It is not, for this recipe.** Deferral makes
42
+ * COMMIT check the violations rather than skipping them, and the rebuild's repair step
43
+ * is `ALTER TABLE __new_x RENAME TO x` — a RENAME, not an INSERT. `DROP TABLE x`'s
44
+ * implicit delete records one deferred violation per referencing row, nothing ever
45
+ * decrements that counter, and COMMIT fails:
46
+ *
47
+ * SQLITE_CONSTRAINT_FOREIGNKEY: FOREIGN KEY constraint failed
48
+ *
49
+ * So ANY rebuild of a table another populated table references — a CHECK added by
50
+ * adopting a `field.enum`, an evolved `@values`, a column type change — was
51
+ * un-appliable on the scaffold's default dialect. Found by adopting MetaObjects into an
52
+ * existing three-table app (`authors <- books <- reviews`), which is the documented
53
+ * migration path. Proven with a controlled pair on identical database copies: deferral
54
+ * fails, `foreign_keys = OFF` outside the transaction applies cleanly with every row
55
+ * preserved. (D1 cannot do this — its implicit transaction owns the connection — which
56
+ * is why D1 needed the full referrer cascade of #241 instead.)
57
+ * 3. **Drops the matching `PRAGMA foreign_keys = ON`.** Restoring it is the caller's job,
58
+ * since the caller is now the one that turned it off.
59
+ * 4. **Lifts `PRAGMA foreign_key_check` out** as `postTransactionChecks`. Run inside the
60
+ * transaction its result set was simply discarded, so the file's own safety net never
61
+ * caught anything; the caller now runs it after commit and fails on any row.
42
62
  *
43
63
  * Postgres migrations contain none of these constructs, so this is a no-op for them —
44
64
  * which is why it keys on statement text rather than needing the dialect threaded in.
@@ -48,6 +68,14 @@ export interface RunnerTransactionPassResult {
48
68
  statements: string[];
49
69
  /** Human-readable adaptations made, for `--verbose`/diagnostics. Empty when untouched. */
50
70
  notes: string[];
71
+ /**
72
+ * The file asked for FK enforcement to be off. The caller MUST issue
73
+ * `PRAGMA foreign_keys = OFF` before opening its transaction and restore it after —
74
+ * inside a transaction the pragma does nothing (see the class doc).
75
+ */
76
+ requiresForeignKeysOff: boolean;
77
+ /** Statements to run AFTER the transaction commits; any returned row is a failure. */
78
+ postTransactionChecks: string[];
51
79
  }
52
80
  export declare function prepareForRunnerTransaction(sqlText: string): RunnerTransactionPassResult;
53
81
  //# sourceMappingURL=runner-transaction-pass.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner-transaction-pass.d.ts","sourceRoot":"","sources":["../../src/apply/runner-transaction-pass.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,WAAW,2BAA2B;IAC1C,wEAAwE;IACxE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0FAA0F;IAC1F,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAMD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,2BAA2B,CAwBxF"}
1
+ {"version":3,"file":"runner-transaction-pass.d.ts","sourceRoot":"","sources":["../../src/apply/runner-transaction-pass.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,wEAAwE;IACxE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0FAA0F;IAC1F,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,sBAAsB,EAAE,OAAO,CAAC;IAChC,sFAAsF;IACtF,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAOD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,2BAA2B,CA+BxF"}
@@ -2,28 +2,36 @@ import { splitSqlStatements } from "../sql/split-statements.js";
2
2
  const TRANSACTION_CONTROL = /^\s*(BEGIN|COMMIT|END\s+TRANSACTION|ROLLBACK|SAVEPOINT|RELEASE)\b/i;
3
3
  const FK_OFF = /^\s*PRAGMA\s+foreign_keys\s*=\s*(OFF|0|false)\s*$/i;
4
4
  const FK_ON = /^\s*PRAGMA\s+foreign_keys\s*=\s*(ON|1|true)\s*$/i;
5
+ const FK_CHECK = /^\s*PRAGMA\s+foreign_key_check\s*$/i;
5
6
  export function prepareForRunnerTransaction(sqlText) {
6
7
  const notes = [];
7
8
  const statements = [];
9
+ const postTransactionChecks = [];
10
+ let requiresForeignKeysOff = false;
8
11
  for (const stmt of splitSqlStatements(sqlText)) {
9
12
  if (TRANSACTION_CONTROL.test(stmt)) {
10
13
  notes.push(`dropped transaction control (runner owns the transaction): ${firstWords(stmt)}`);
11
14
  continue;
12
15
  }
13
16
  if (FK_OFF.test(stmt)) {
14
- // Preserve the INTENT. A bare foreign_keys pragma does nothing inside a
15
- // transaction, so keeping it verbatim would silently drop FK protection.
16
- statements.push("PRAGMA defer_foreign_keys = ON");
17
- notes.push("rewrote `PRAGMA foreign_keys = OFF` to `PRAGMA defer_foreign_keys = ON` (the in-transaction equivalent)");
17
+ // Hand the INTENT to the caller rather than executing it here. The pragma is a
18
+ // no-op inside a transaction, and deferral is not a substitute for this recipe.
19
+ requiresForeignKeysOff = true;
20
+ notes.push("lifted `PRAGMA foreign_keys = OFF` out of the transaction (it is a no-op inside one)");
18
21
  continue;
19
22
  }
20
23
  if (FK_ON.test(stmt)) {
21
- notes.push("dropped `PRAGMA foreign_keys = ON` (defer_foreign_keys resets at commit)");
24
+ notes.push("dropped `PRAGMA foreign_keys = ON` (the caller restores what the caller disabled)");
25
+ continue;
26
+ }
27
+ if (FK_CHECK.test(stmt)) {
28
+ postTransactionChecks.push(stmt);
29
+ notes.push("deferred `PRAGMA foreign_key_check` to after commit (its rows are discarded inside a transaction)");
22
30
  continue;
23
31
  }
24
32
  statements.push(stmt);
25
33
  }
26
- return { statements, notes };
34
+ return { statements, notes, requiresForeignKeysOff, postTransactionChecks };
27
35
  }
28
36
  function firstWords(stmt) {
29
37
  return stmt.trim().split(/\s+/).slice(0, 3).join(" ");
@@ -1 +1 @@
1
- {"version":3,"file":"runner-transaction-pass.js","sourceRoot":"","sources":["../../src/apply/runner-transaction-pass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAsDhE,MAAM,mBAAmB,GAAG,oEAAoE,CAAC;AACjG,MAAM,MAAM,GAAG,oDAAoD,CAAC;AACpE,MAAM,KAAK,GAAG,kDAAkD,CAAC;AAEjE,MAAM,UAAU,2BAA2B,CAAC,OAAe;IACzD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,8DAA8D,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7F,SAAS;QACX,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,wEAAwE;YACxE,yEAAyE;YACzE,UAAU,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;YAClD,KAAK,CAAC,IAAI,CAAC,yGAAyG,CAAC,CAAC;YACtH,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;YACvF,SAAS;QACX,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxD,CAAC"}
1
+ {"version":3,"file":"runner-transaction-pass.js","sourceRoot":"","sources":["../../src/apply/runner-transaction-pass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAkFhE,MAAM,mBAAmB,GAAG,oEAAoE,CAAC;AACjG,MAAM,MAAM,GAAG,oDAAoD,CAAC;AACpE,MAAM,KAAK,GAAG,kDAAkD,CAAC;AACjE,MAAM,QAAQ,GAAG,qCAAqC,CAAC;AAEvD,MAAM,UAAU,2BAA2B,CAAC,OAAe;IACzD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,qBAAqB,GAAa,EAAE,CAAC;IAC3C,IAAI,sBAAsB,GAAG,KAAK,CAAC;IAEnC,KAAK,MAAM,IAAI,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,8DAA8D,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7F,SAAS;QACX,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,+EAA+E;YAC/E,gFAAgF;YAChF,sBAAsB,GAAG,IAAI,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;YACnG,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,mFAAmF,CAAC,CAAC;YAChG,SAAS;QACX,CAAC;QACD,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,mGAAmG,CAAC,CAAC;YAChH,SAAS;QACX,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;AAC9E,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/migrate-ts",
3
- "version": "0.24.4",
3
+ "version": "0.24.5",
4
4
  "description": "Schema migration tooling for MetaObjects: diff metadata vs DB and emit SQL for Postgres and SQLite.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@iarna/toml": "^2.2.5",
48
- "@metaobjectsdev/metadata": "0.24.4"
48
+ "@metaobjectsdev/metadata": "0.24.5"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "kysely": ">=0.27.0 <0.30.0",
@@ -304,13 +304,43 @@ async function runSqlFileWithLedgerMutation(
304
304
  // rejects a nested BEGIN, which made table-rebuild migrations un-appliable on the
305
305
  // scaffold's default dialect) and its own `PRAGMA foreign_keys = OFF`, which is a
306
306
  // no-op inside a transaction. See runner-transaction-pass.ts for the full rationale.
307
- const { statements } = prepareForRunnerTransaction(sqlText);
308
- await db.transaction().execute(async (trx) => {
309
- for (const stmt of statements) {
310
- await sql.raw(stmt).execute(trx);
307
+ const { statements, requiresForeignKeysOff, postTransactionChecks } =
308
+ prepareForRunnerTransaction(sqlText);
309
+
310
+ // OUTSIDE the transaction, deliberately: `PRAGMA foreign_keys` is a no-op inside one,
311
+ // and `defer_foreign_keys` cannot stand in for it here — `DROP TABLE`'s implicit delete
312
+ // records a deferred violation per referencing row that the repairing RENAME never
313
+ // decrements, so COMMIT fails. See runner-transaction-pass.ts.
314
+ if (requiresForeignKeysOff) {
315
+ await sql.raw("PRAGMA foreign_keys = OFF").execute(db);
316
+ }
317
+ try {
318
+ await db.transaction().execute(async (trx) => {
319
+ for (const stmt of statements) {
320
+ await sql.raw(stmt).execute(trx);
321
+ }
322
+ await mutateLedger(trx);
323
+ });
324
+ } finally {
325
+ // Restore before anything else touches this connection, even if the migration threw.
326
+ if (requiresForeignKeysOff) {
327
+ await sql.raw("PRAGMA foreign_keys = ON").execute(db);
311
328
  }
312
- await mutateLedger(trx);
313
- });
329
+ }
330
+
331
+ // The file's own safety net, finally able to report. Running it inside the transaction
332
+ // discarded its rows, so a rebuild that DID strand a reference passed silently.
333
+ for (const check of postTransactionChecks) {
334
+ const result = await sql.raw(check).execute(db);
335
+ const rows = (result as { rows?: unknown[] }).rows ?? [];
336
+ if (rows.length > 0) {
337
+ throw new Error(
338
+ `migration left ${rows.length} dangling foreign-key reference(s): ${check} reported ` +
339
+ `${JSON.stringify(rows.slice(0, 5))}. The database is NOT consistent — restore from ` +
340
+ `backup rather than continuing.`,
341
+ );
342
+ }
343
+ }
314
344
  }
315
345
 
316
346
  async function discoverMigrations(dir: string): Promise<DiscoveredMigration[]> {
@@ -34,13 +34,33 @@ import { splitSqlStatements } from "../sql/split-statements.js";
34
34
  * <h3>What it does</h3>
35
35
  * 1. **Drops transaction control** (`BEGIN`/`COMMIT`/`ROLLBACK`/`SAVEPOINT`/`RELEASE`).
36
36
  * The runner's transaction supplies the atomicity the file was asking for.
37
- * 2. **Rewrites `PRAGMA foreign_keys = OFF` to `PRAGMA defer_foreign_keys = ON`.** This is
38
- * not cosmetic: `foreign_keys` is a **no-op inside a transaction**, so the rebuild would
39
- * lose its FK protection precisely where it needs it (dropping a referenced table).
40
- * `defer_foreign_keys` is the in-transaction equivalent enforcement is deferred to
41
- * commit — and is exactly what the D1 cascade emitter uses for the same reason.
42
- * 3. **Drops the matching `PRAGMA foreign_keys = ON`**, whose only job was to undo (1);
43
- * `defer_foreign_keys` resets itself at commit.
37
+ * 2. **Reports `PRAGMA foreign_keys = OFF` to the caller as `requiresForeignKeysOff`**
38
+ * instead of executing it, because the pragma is a **no-op inside a transaction** and
39
+ * must be issued BEFORE the transaction opens. That is SQLite's own documented
40
+ * recreate-and-copy procedure, and it is the only form that works.
41
+ *
42
+ * This used to rewrite it to `PRAGMA defer_foreign_keys = ON`, reasoning that deferral
43
+ * is "the in-transaction equivalent". **It is not, for this recipe.** Deferral makes
44
+ * COMMIT check the violations rather than skipping them, and the rebuild's repair step
45
+ * is `ALTER TABLE __new_x RENAME TO x` — a RENAME, not an INSERT. `DROP TABLE x`'s
46
+ * implicit delete records one deferred violation per referencing row, nothing ever
47
+ * decrements that counter, and COMMIT fails:
48
+ *
49
+ * SQLITE_CONSTRAINT_FOREIGNKEY: FOREIGN KEY constraint failed
50
+ *
51
+ * So ANY rebuild of a table another populated table references — a CHECK added by
52
+ * adopting a `field.enum`, an evolved `@values`, a column type change — was
53
+ * un-appliable on the scaffold's default dialect. Found by adopting MetaObjects into an
54
+ * existing three-table app (`authors <- books <- reviews`), which is the documented
55
+ * migration path. Proven with a controlled pair on identical database copies: deferral
56
+ * fails, `foreign_keys = OFF` outside the transaction applies cleanly with every row
57
+ * preserved. (D1 cannot do this — its implicit transaction owns the connection — which
58
+ * is why D1 needed the full referrer cascade of #241 instead.)
59
+ * 3. **Drops the matching `PRAGMA foreign_keys = ON`.** Restoring it is the caller's job,
60
+ * since the caller is now the one that turned it off.
61
+ * 4. **Lifts `PRAGMA foreign_key_check` out** as `postTransactionChecks`. Run inside the
62
+ * transaction its result set was simply discarded, so the file's own safety net never
63
+ * caught anything; the caller now runs it after commit and fails on any row.
44
64
  *
45
65
  * Postgres migrations contain none of these constructs, so this is a no-op for them —
46
66
  * which is why it keys on statement text rather than needing the dialect threaded in.
@@ -50,15 +70,26 @@ export interface RunnerTransactionPassResult {
50
70
  statements: string[];
51
71
  /** Human-readable adaptations made, for `--verbose`/diagnostics. Empty when untouched. */
52
72
  notes: string[];
73
+ /**
74
+ * The file asked for FK enforcement to be off. The caller MUST issue
75
+ * `PRAGMA foreign_keys = OFF` before opening its transaction and restore it after —
76
+ * inside a transaction the pragma does nothing (see the class doc).
77
+ */
78
+ requiresForeignKeysOff: boolean;
79
+ /** Statements to run AFTER the transaction commits; any returned row is a failure. */
80
+ postTransactionChecks: string[];
53
81
  }
54
82
 
55
83
  const TRANSACTION_CONTROL = /^\s*(BEGIN|COMMIT|END\s+TRANSACTION|ROLLBACK|SAVEPOINT|RELEASE)\b/i;
56
84
  const FK_OFF = /^\s*PRAGMA\s+foreign_keys\s*=\s*(OFF|0|false)\s*$/i;
57
85
  const FK_ON = /^\s*PRAGMA\s+foreign_keys\s*=\s*(ON|1|true)\s*$/i;
86
+ const FK_CHECK = /^\s*PRAGMA\s+foreign_key_check\s*$/i;
58
87
 
59
88
  export function prepareForRunnerTransaction(sqlText: string): RunnerTransactionPassResult {
60
89
  const notes: string[] = [];
61
90
  const statements: string[] = [];
91
+ const postTransactionChecks: string[] = [];
92
+ let requiresForeignKeysOff = false;
62
93
 
63
94
  for (const stmt of splitSqlStatements(sqlText)) {
64
95
  if (TRANSACTION_CONTROL.test(stmt)) {
@@ -66,20 +97,25 @@ export function prepareForRunnerTransaction(sqlText: string): RunnerTransactionP
66
97
  continue;
67
98
  }
68
99
  if (FK_OFF.test(stmt)) {
69
- // Preserve the INTENT. A bare foreign_keys pragma does nothing inside a
70
- // transaction, so keeping it verbatim would silently drop FK protection.
71
- statements.push("PRAGMA defer_foreign_keys = ON");
72
- notes.push("rewrote `PRAGMA foreign_keys = OFF` to `PRAGMA defer_foreign_keys = ON` (the in-transaction equivalent)");
100
+ // Hand the INTENT to the caller rather than executing it here. The pragma is a
101
+ // no-op inside a transaction, and deferral is not a substitute for this recipe.
102
+ requiresForeignKeysOff = true;
103
+ notes.push("lifted `PRAGMA foreign_keys = OFF` out of the transaction (it is a no-op inside one)");
73
104
  continue;
74
105
  }
75
106
  if (FK_ON.test(stmt)) {
76
- notes.push("dropped `PRAGMA foreign_keys = ON` (defer_foreign_keys resets at commit)");
107
+ notes.push("dropped `PRAGMA foreign_keys = ON` (the caller restores what the caller disabled)");
108
+ continue;
109
+ }
110
+ if (FK_CHECK.test(stmt)) {
111
+ postTransactionChecks.push(stmt);
112
+ notes.push("deferred `PRAGMA foreign_key_check` to after commit (its rows are discarded inside a transaction)");
77
113
  continue;
78
114
  }
79
115
  statements.push(stmt);
80
116
  }
81
117
 
82
- return { statements, notes };
118
+ return { statements, notes, requiresForeignKeysOff, postTransactionChecks };
83
119
  }
84
120
 
85
121
  function firstWords(stmt: string): string {