@vibeorm/migrate 2.0.0-alpha.6 → 2.0.0-alpha.7

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/execute.d.ts CHANGED
@@ -10,7 +10,10 @@
10
10
  * `usesSelfManagedTransactions` detects them. Plain statements in a
11
11
  * self-managed script auto-commit individually (documented sqlite
12
12
  * limitation: a mixed script is not atomic as a whole; each rebuild block
13
- * is).
13
+ * is). Because of that, the migration runner records per-statement progress
14
+ * for these scripts and resumes with `selfManagedResumeStart`, which rewinds
15
+ * INTO the enclosing block so the session-scoped `PRAGMA foreign_keys=OFF`
16
+ * is re-issued before the block replays.
14
17
  * - `PRAGMA foreign_key_check` RETURNS VIOLATION ROWS instead of failing —
15
18
  * `executeStatement` runs it row-returning (`values: []`, the adapter-sqlite
16
19
  * contract for reads) and throws VIBE_MIGRATION when any come back.
@@ -21,6 +24,28 @@ import type { SqlExecutor } from "./types.ts";
21
24
  export declare function usesSelfManagedTransactions(params: {
22
25
  sql: readonly string[];
23
26
  }): boolean;
27
+ /**
28
+ * Where a resume of a self-managed sqlite script must actually restart.
29
+ *
30
+ * A recorded statement_index can point INSIDE a rebuild block: the progress
31
+ * UPDATE issued after `PRAGMA foreign_keys=OFF;` auto-commits (no transaction
32
+ * is open yet), while every UPDATE after the block's `BEGIN;` is rolled back
33
+ * with the crash — so the persisted index lands on the block's `BEGIN;`, one
34
+ * past a pragma that is SESSION-scoped and gone in the new session. Resuming
35
+ * there would run the rebuild — `DROP TABLE` of a possibly-referenced parent
36
+ * included — with foreign keys ON.
37
+ *
38
+ * This rewinds `start` to the nearest preceding `PRAGMA foreign_keys=OFF` that
39
+ * is not already closed by a `PRAGMA foreign_keys=ON`. Replaying from a
40
+ * block's start is safe: the block is transactional (nothing of it survived a
41
+ * crash) and re-running a COMPLETED block is the idempotence the recipe
42
+ * already relies on. Scripts with no marker before `start` are returned
43
+ * unchanged.
44
+ */
45
+ export declare function selfManagedResumeStart(params: {
46
+ sql: readonly string[];
47
+ start: number;
48
+ }): number;
24
49
  /**
25
50
  * Execute one migration statement. On sqlite, `PRAGMA foreign_key_check` is
26
51
  * executed row-returning and its violations become a VIBE_MIGRATION error.
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../src/execute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAO9C,2EAA2E;AAC3E,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,OAAO,CAEvF;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,WAAW,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBhB;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhG"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../src/execute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAU9C,2EAA2E;AAC3E,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,OAAO,CAEvF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAST;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,WAAW,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBhB;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhG"}
package/dist/index.js CHANGED
@@ -1957,10 +1957,22 @@ function artifactCreateSql(params) {
1957
1957
  // packages/migrate/src/execute.ts
1958
1958
  import { VibeError as VibeError8 } from "@vibeorm/schema";
1959
1959
  var SELF_MANAGED_MARKER = "PRAGMA foreign_keys=OFF";
1960
+ var SELF_MANAGED_RESTORE_MARKER = "PRAGMA foreign_keys=ON";
1960
1961
  var FK_CHECK_PREFIX = "PRAGMA foreign_key_check";
1961
1962
  function usesSelfManagedTransactions(params) {
1962
1963
  return params.sql.some((statement) => statement.startsWith(SELF_MANAGED_MARKER));
1963
1964
  }
1965
+ function selfManagedResumeStart(params) {
1966
+ const { sql, start } = params;
1967
+ for (let index = start - 1;index >= 0; index -= 1) {
1968
+ const statement = sql[index];
1969
+ if (statement.startsWith(SELF_MANAGED_RESTORE_MARKER))
1970
+ return start;
1971
+ if (statement.startsWith(SELF_MANAGED_MARKER))
1972
+ return index;
1973
+ }
1974
+ return start;
1975
+ }
1964
1976
  async function executeStatement(params) {
1965
1977
  const { executor, statement, dialect } = params;
1966
1978
  if (dialect === "sqlite" && statement.startsWith(FK_CHECK_PREFIX)) {
@@ -2057,10 +2069,9 @@ function generateMigration(params) {
2057
2069
  async function applyMigrations(params) {
2058
2070
  const { executor, migrations } = params;
2059
2071
  const dialect = params.dialect ?? "postgres";
2060
- const transactional = DIALECT_CAPABILITIES[dialect].transactionalDdl;
2061
- await ensureMigrationsTable({ executor, dialect });
2062
2072
  await acquireLock({ executor, dialect });
2063
2073
  try {
2074
+ await ensureMigrationsTable({ executor, dialect });
2064
2075
  const recorded = await recordedRows({ executor, dialect });
2065
2076
  const applied = [];
2066
2077
  const skipped = [];
@@ -2074,8 +2085,7 @@ async function applyMigrations(params) {
2074
2085
  meta: { migration: migration.name, statementIndex: stored.statementIndex }
2075
2086
  });
2076
2087
  }
2077
- const complete = stored.checksum === "" || transactional || stored.statementIndex >= migration.sql.length;
2078
- if (complete) {
2088
+ if (isCompleteRecord({ dialect, migration, stored })) {
2079
2089
  assertChecksumMatches({ migration, stored: stored.checksum });
2080
2090
  skipped.push(migration.name);
2081
2091
  continue;
@@ -2091,10 +2101,10 @@ async function applyMigrations(params) {
2091
2101
  applied.push(migration.name);
2092
2102
  continue;
2093
2103
  }
2094
- if (transactional) {
2095
- await runTransactional({ executor, dialect, migration });
2096
- } else {
2104
+ if (usesStatementBookkeeping({ dialect, sql: migration.sql })) {
2097
2105
  await runNonTransactional({ executor, dialect, migration, start: 0, insertRecord: true });
2106
+ } else {
2107
+ await runTransactional({ executor, dialect, migration });
2098
2108
  }
2099
2109
  applied.push(migration.name);
2100
2110
  }
@@ -2106,7 +2116,6 @@ async function applyMigrations(params) {
2106
2116
  async function migrationStatus(params) {
2107
2117
  const { executor, migrations } = params;
2108
2118
  const dialect = params.dialect ?? "postgres";
2109
- const transactional = DIALECT_CAPABILITIES[dialect].transactionalDdl;
2110
2119
  await ensureMigrationsTable({ executor, dialect });
2111
2120
  const recorded = await recordedRows({ executor, dialect });
2112
2121
  const applied = [];
@@ -2131,8 +2140,7 @@ async function migrationStatus(params) {
2131
2140
  mismatched.push(migration.name);
2132
2141
  continue;
2133
2142
  }
2134
- const complete = transactional || stored.statementIndex >= migration.sql.length;
2135
- if (complete)
2143
+ if (isCompleteRecord({ dialect, migration, stored }))
2136
2144
  applied.push(migration.name);
2137
2145
  else
2138
2146
  pending.push(migration.name);
@@ -2157,7 +2165,6 @@ async function resolveMigration(params) {
2157
2165
  async function rollbackMigration(params) {
2158
2166
  const { executor, migration } = params;
2159
2167
  const dialect = params.dialect ?? "postgres";
2160
- const transactional = DIALECT_CAPABILITIES[dialect].transactionalDdl;
2161
2168
  const downSql = migration.downSql;
2162
2169
  if (downSql === undefined) {
2163
2170
  throw new VibeError9({
@@ -2166,9 +2173,9 @@ async function rollbackMigration(params) {
2166
2173
  meta: { migration: migration.name }
2167
2174
  });
2168
2175
  }
2169
- await ensureMigrationsTable({ executor, dialect });
2170
2176
  await acquireLock({ executor, dialect });
2171
2177
  try {
2178
+ await ensureMigrationsTable({ executor, dialect });
2172
2179
  const recorded = await recordedRows({ executor, dialect });
2173
2180
  const stored = recorded.get(migration.name);
2174
2181
  if (stored === undefined) {
@@ -2181,7 +2188,7 @@ async function rollbackMigration(params) {
2181
2188
  const resumingDown = stored.statementIndex < 0;
2182
2189
  assertChecksumMatches({ migration, stored: stored.checksum });
2183
2190
  if (!resumingDown) {
2184
- const partialUp = !transactional && stored.checksum !== "" && stored.statementIndex < migration.sql.length;
2191
+ const partialUp = !isCompleteRecord({ dialect, migration, stored });
2185
2192
  if (partialUp) {
2186
2193
  throw new VibeError9({
2187
2194
  code: "VIBE_MIGRATION",
@@ -2190,11 +2197,11 @@ async function rollbackMigration(params) {
2190
2197
  });
2191
2198
  }
2192
2199
  }
2193
- if (transactional) {
2194
- await runTransactionalDown({ executor, dialect, migration, downSql });
2195
- } else {
2200
+ if (usesStatementBookkeeping({ dialect, sql: downSql })) {
2196
2201
  const start = resumingDown ? -stored.statementIndex - 1 : 0;
2197
2202
  await runNonTransactionalDown({ executor, dialect, migration, downSql, start });
2203
+ } else {
2204
+ await runTransactionalDown({ executor, dialect, migration, downSql });
2198
2205
  }
2199
2206
  } finally {
2200
2207
  await releaseLock({ executor, dialect });
@@ -2233,8 +2240,10 @@ async function runTransactionalDown(params) {
2233
2240
  }
2234
2241
  }
2235
2242
  async function runNonTransactionalDown(params) {
2236
- const { executor, dialect, migration, downSql, start } = params;
2243
+ const { executor, dialect, migration, downSql } = params;
2237
2244
  const table = q4(dialect, MIGRATIONS_TABLE_NAME);
2245
+ const selfManaged = dialect === "sqlite" && usesSelfManagedTransactions({ sql: downSql });
2246
+ const start = selfManaged ? selfManagedResumeStart({ sql: downSql, start: params.start }) : params.start;
2238
2247
  const recordProgress = async (completed) => {
2239
2248
  await executor({
2240
2249
  text: `UPDATE ${table} SET ${q4(dialect, "statement_index")} = ${ph(dialect, 1)}, ${q4(dialect, "error")} = NULL WHERE ${q4(dialect, "name")} = ${ph(dialect, 2)}`,
@@ -2247,6 +2256,8 @@ async function runNonTransactionalDown(params) {
2247
2256
  try {
2248
2257
  await executeStatement({ executor, statement: downSql[index], dialect });
2249
2258
  } catch (error) {
2259
+ if (selfManaged)
2260
+ await cleanupSelfManagedFailure({ executor });
2250
2261
  try {
2251
2262
  await executor({
2252
2263
  text: `UPDATE ${table} SET ${q4(dialect, "error")} = ${ph(dialect, 1)} WHERE ${q4(dialect, "name")} = ${ph(dialect, 2)}`,
@@ -2302,8 +2313,10 @@ async function runTransactional(params) {
2302
2313
  }
2303
2314
  }
2304
2315
  async function runNonTransactional(params) {
2305
- const { executor, dialect, migration, start, insertRecord } = params;
2316
+ const { executor, dialect, migration, insertRecord } = params;
2306
2317
  const table = q4(dialect, MIGRATIONS_TABLE_NAME);
2318
+ const selfManaged = dialect === "sqlite" && usesSelfManagedTransactions({ sql: migration.sql });
2319
+ const start = selfManaged ? selfManagedResumeStart({ sql: migration.sql, start: params.start }) : params.start;
2307
2320
  if (insertRecord) {
2308
2321
  await executor({
2309
2322
  text: `INSERT INTO ${table} (${q4(dialect, "name")}, ${q4(dialect, "checksum")}, ${q4(dialect, "statement_index")}) VALUES (${ph(dialect, 1)}, ${ph(dialect, 2)}, 0)`,
@@ -2314,6 +2327,8 @@ async function runNonTransactional(params) {
2314
2327
  try {
2315
2328
  await executeStatement({ executor, statement: migration.sql[index], dialect });
2316
2329
  } catch (error) {
2330
+ if (selfManaged)
2331
+ await cleanupSelfManagedFailure({ executor });
2317
2332
  try {
2318
2333
  await executor({
2319
2334
  text: `UPDATE ${table} SET ${q4(dialect, "error")} = ${ph(dialect, 1)} WHERE ${q4(dialect, "name")} = ${ph(dialect, 2)}`,
@@ -2364,6 +2379,20 @@ async function releaseLock(params) {
2364
2379
  }
2365
2380
  } catch {}
2366
2381
  }
2382
+ function usesStatementBookkeeping(params) {
2383
+ const { dialect, sql } = params;
2384
+ if (!DIALECT_CAPABILITIES[dialect].transactionalDdl)
2385
+ return true;
2386
+ return dialect === "sqlite" && usesSelfManagedTransactions({ sql });
2387
+ }
2388
+ function isCompleteRecord(params) {
2389
+ const { dialect, migration, stored } = params;
2390
+ if (stored.checksum === "")
2391
+ return true;
2392
+ if (!usesStatementBookkeeping({ dialect, sql: migration.sql }))
2393
+ return true;
2394
+ return stored.statementIndex >= migration.sql.length;
2395
+ }
2367
2396
  async function recordedRows(params) {
2368
2397
  const { executor, dialect } = params;
2369
2398
  const rows = await executor({
@@ -4057,4 +4086,4 @@ export {
4057
4086
  MIGRATIONS_TABLE_NAME
4058
4087
  };
4059
4088
 
4060
- //# debugId=CA4C1473A398313F64756E2164756E21
4089
+ //# debugId=DA60A1F7881A22C064756E2164756E21