pipework 0.31.0 → 0.32.0
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/CHANGELOG.md +4 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -1
- package/dist/cli/commands/migrate.js +10 -0
- package/dist/cli/commands/migrate.js.map +1 -1
- package/dist/data/migrate/apply.d.ts +20 -0
- package/dist/data/migrate/apply.d.ts.map +1 -1
- package/dist/data/migrate/apply.js +66 -2
- package/dist/data/migrate/apply.js.map +1 -1
- package/dist/data/migrate/run.d.ts +8 -0
- package/dist/data/migrate/run.d.ts.map +1 -1
- package/dist/data/migrate/run.js +3 -0
- package/dist/data/migrate/run.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.32.0
|
|
4
|
+
|
|
5
|
+
- **A no-transaction migration interrupted mid-file now reruns to completion instead of becoming hand surgery.** A file carrying `-- pipework:no-transaction` runs its statements outside a transaction, so there is no rollback: a runner killed between statements left the database half-changed with no tracker row — the row is written last — and the rerun replayed from the top and died on the first already-applied statement (`relation already exists`). Nor could the file be edited to add `IF NOT EXISTS` guards, because a hash mismatch is refused as `Applied migrations are immutable history`. There was no way forward from the runner. Each statement's completion is now recorded as it lands, in a `<migrationsTable>_progress` companion table, and a rerun starts from the first statement not recorded — statements that already ran are never replayed, the tracker row lands, and the migration becomes ordinary applied history. The mark is written *after* the statement it describes, deliberately: marking first would let a death between the mark and the statement skip work silently, which is worse than replaying one. So one statement — the one whose mark had not landed yet — can still be replayed, instead of all of them. The progress row is deleted the moment the tracker row is written, so the two records can never disagree about whether a migration is done, and the table is created only for databases that actually have a no-transaction migration. The content guard still holds and is now specific: a recorded partial whose file has since changed is refused with its own message, saying how many statements ran, that the count indexes into the file as it was, and the two ways out (restore the file and let the resume finish, or delete the progress row if the partial work has been undone by hand) — rather than the generic immutability error, which named a cure that did not apply. `applyMigrations` takes an `onResume` callback, `MigrateResult` carries `resumed`, and `pipework migrate` prints a warning naming the tag and the statement it resumed from — finishing a half-applied file says so out loud, because it means a previous run died mid-file.
|
|
6
|
+
|
|
3
7
|
## 0.31.0
|
|
4
8
|
|
|
5
9
|
- **A finished test file no longer waits out a cluster-wide checkpoint to drop its database.** `CREATE DATABASE ... WITH TEMPLATE` was suspected of forcing checkpoints; it does not. Since PG15 its default strategy is `WAL_LOG`, which needs no checkpoint, and pipework issues no `STRATEGY` clause — measured on PG17, forty clones force zero. Every `DROP DATABASE` forces one: Postgres's own `dropdb()` calls `RequestCheckpoint(CHECKPOINT_IMMEDIATE | FORCE | WAIT)`, so the dropping backend blocks until the whole dirty cluster is synced. Teardown dropped one database per test file, which put that wait inside every `afterAll`; on a saturated cluster single checkpoints were measured at 47.1 s / 28.6 s / 24.4 s, which is a teardown hook timing out on a run that leaked nothing. Teardown now terminates the file's backends and **queues** the database instead of dropping it. The queue is emptied when it reaches `test.dropBatchSize` (default 8) and again at worker shutdown, so one teardown in eight pays the wait rather than all of them, and the rest of a worker's drops settle after its files are done instead of in the middle of a wide start burst. The number of checkpoints is fixed by the number of databases and batching does not change it — what changes is which hook is standing there waiting. Each flush logs one `pipework:test-drop <batch|shutdown> queued=… dropped=… elapsed_ms=…` line, so what the drops cost is a number rather than an inference. Two supporting changes: a clone's name now carries a per-clone sequence number (`<project>_test_app_3_<pid>_<nonce>`) so the next file in a worker never asks for a name still queued for drop, and the create path's own `DROP DATABASE IF EXISTS` is gone — with unique names it could only ever be a no-op, and a non-no-op would have been a checkpoint on the create path too. Nothing leaks: a worker killed with drops still queued loses its run lock with its connection, and the next run's reaper drops them exactly as it always has. `flushPendingDrops()` is exported from `pipework/test` for a consumer whose own teardown wants the queue settled before it looks at `pg_database`. Set `test.dropBatchSize: 1` to restore the drop-per-file behaviour.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,gBAAgB,EAAE,OAAO,GAAG,SAAS,CAAA;IACrC,eAAe,EAAE,OAAO,GAAG,SAAS,CAAA;CACrC;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4D3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,gBAAgB,EAAE,OAAO,GAAG,SAAS,CAAA;IACrC,eAAe,EAAE,OAAO,GAAG,SAAS,CAAA;CACrC,GAAG,MAAM,GAAG,IAAI,CAmBhB"}
|
|
@@ -28,12 +28,14 @@ export async function migrate(options) {
|
|
|
28
28
|
if (options.db !== undefined) {
|
|
29
29
|
info(`Migrating database: ${options.db}`);
|
|
30
30
|
const result = await migrateOne(instance, instance.config.database(options.db), migrateOptions);
|
|
31
|
+
reportResumed(result);
|
|
31
32
|
success(`${options.db}: ${result.migrationsRun} migrations applied`);
|
|
32
33
|
}
|
|
33
34
|
else {
|
|
34
35
|
info('Migrating all databases...');
|
|
35
36
|
const results = await migrateAll(instance, migrateOptions);
|
|
36
37
|
for (const result of results) {
|
|
38
|
+
reportResumed(result);
|
|
37
39
|
success(`${result.database}: ${result.migrationsRun} migrations applied`);
|
|
38
40
|
}
|
|
39
41
|
}
|
|
@@ -79,4 +81,12 @@ export function rejectRetiredFlags(options) {
|
|
|
79
81
|
' -- pipework:expect-destructive\n' +
|
|
80
82
|
' DROP TABLE "graph_co_change_edges";\n');
|
|
81
83
|
}
|
|
84
|
+
// A half-applied no-transaction file that was picked up and finished is not a
|
|
85
|
+
// routine apply: it says the previous run died mid-file. Name it. Issue #378.
|
|
86
|
+
function reportResumed(result) {
|
|
87
|
+
for (const r of result.resumed) {
|
|
88
|
+
warn(`${result.database}: resumed "${r.tag}" from statement ${r.fromStatement} of ` +
|
|
89
|
+
`${r.totalStatements} — a previous run stopped mid-file.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
82
92
|
//# sourceMappingURL=migrate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,QAAQ,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,12 @@ export interface ApplyOptions {
|
|
|
15
15
|
* await and a caller watching for progress sees none. Issue #380.
|
|
16
16
|
*/
|
|
17
17
|
onMigration?: (tag: string, index: number, total: number) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Called when a no-transaction migration resumes a half-applied file, with
|
|
20
|
+
* the statement it starts from (1-based) and how many the file holds.
|
|
21
|
+
* Issue #378.
|
|
22
|
+
*/
|
|
23
|
+
onResume?: (tag: string, fromStatement: number, total: number) => void;
|
|
18
24
|
}
|
|
19
25
|
export interface Migration {
|
|
20
26
|
tag: string;
|
|
@@ -34,6 +40,20 @@ export declare function readMigrations(options: ApplyOptions): Migration[];
|
|
|
34
40
|
* they did, and the gate was the one that was wrong (#334).
|
|
35
41
|
*/
|
|
36
42
|
export declare function parseStatements(raw: string): string[];
|
|
43
|
+
/** Companion to the tracker table, holding half-applied no-transaction files. */
|
|
44
|
+
export declare function progressTableName(migrationsTable: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* The statement index a half-applied no-transaction file resumes from. Zero
|
|
47
|
+
* when there is no record of one.
|
|
48
|
+
*
|
|
49
|
+
* A recorded partial whose hash no longer matches the file is refused rather
|
|
50
|
+
* than resumed: the mark counts statements in the file as it was, and the file
|
|
51
|
+
* on disk is now a different one, so no index into it means anything.
|
|
52
|
+
*/
|
|
53
|
+
export declare function resumePoint(migration: Migration, recorded: {
|
|
54
|
+
hash: string;
|
|
55
|
+
done: number;
|
|
56
|
+
} | undefined, migrationsTable: string): number;
|
|
37
57
|
/**
|
|
38
58
|
* Rewrite every `CREATE TABLE` (including the `IF NOT EXISTS` form) to
|
|
39
59
|
* `CREATE UNLOGGED TABLE`. Used only when applying schema to a test database —
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/data/migrate/apply.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,gBAAgB,EAAE,MAAM,CAAA;IACxB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/data/migrate/apply.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,gBAAgB,EAAE,MAAM,CAAA;IACxB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACvE;AASD,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAqI5E;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,EAAE,CA4CjE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAErD;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EACpD,eAAe,EAAE,MAAM,GACtB,MAAM,CAiBR;AAMD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD"}
|
|
@@ -37,6 +37,30 @@ export async function applyMigrations(options) {
|
|
|
37
37
|
await client.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS ${quoteIdent(`${options.migrationsTable}_tag_key`)} ON ${table} (tag)`);
|
|
38
38
|
const rows = await client.unsafe(`SELECT tag, hash FROM ${table} WHERE tag IS NOT NULL`);
|
|
39
39
|
const appliedHashByTag = new Map(rows.map((row) => [row.tag, row.hash]));
|
|
40
|
+
// A no-transaction file has no rollback: interrupted between statements it
|
|
41
|
+
// leaves the database half-changed with no tracker row, and the rerun
|
|
42
|
+
// replays from the top and dies on the first already-applied statement.
|
|
43
|
+
// Nor can the file be edited to add guards — its content is immutable
|
|
44
|
+
// history. So each statement's completion is recorded as it lands, and a
|
|
45
|
+
// rerun starts from the first one not recorded. The table exists only for
|
|
46
|
+
// databases that actually have a no-transaction migration. Issue #378.
|
|
47
|
+
const progress = quoteIdent(progressTableName(options.migrationsTable));
|
|
48
|
+
const hasNoTransaction = migrations.some((migration) => migration.noTransaction);
|
|
49
|
+
const progressByTag = new Map();
|
|
50
|
+
if (hasNoTransaction) {
|
|
51
|
+
await client.unsafe(`
|
|
52
|
+
CREATE TABLE IF NOT EXISTS ${progress} (
|
|
53
|
+
tag text PRIMARY KEY,
|
|
54
|
+
hash text NOT NULL,
|
|
55
|
+
statements_done integer NOT NULL,
|
|
56
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
57
|
+
)
|
|
58
|
+
`);
|
|
59
|
+
const partial = await client.unsafe(`SELECT tag, hash, statements_done FROM ${progress}`);
|
|
60
|
+
for (const row of partial) {
|
|
61
|
+
progressByTag.set(row.tag, { hash: row.hash, done: row.statements_done });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
40
64
|
let applied = 0;
|
|
41
65
|
for (const migration of migrations) {
|
|
42
66
|
const appliedHash = appliedHashByTag.get(migration.tag);
|
|
@@ -53,10 +77,21 @@ export async function applyMigrations(options) {
|
|
|
53
77
|
continue;
|
|
54
78
|
}
|
|
55
79
|
if (migration.noTransaction) {
|
|
56
|
-
|
|
57
|
-
|
|
80
|
+
const from = resumePoint(migration, progressByTag.get(migration.tag), options.migrationsTable);
|
|
81
|
+
if (from > 0)
|
|
82
|
+
options.onResume?.(migration.tag, from + 1, migration.statements.length);
|
|
83
|
+
for (let i = from; i < migration.statements.length; i++) {
|
|
84
|
+
await client.unsafe(migration.statements[i]);
|
|
85
|
+
// Recorded AFTER the statement it describes: marking first would let
|
|
86
|
+
// a death between the mark and the statement skip work silently,
|
|
87
|
+
// which is worse than replaying one statement. The window that
|
|
88
|
+
// remains is exactly one statement wide — the one whose mark had not
|
|
89
|
+
// landed yet — instead of the whole file.
|
|
90
|
+
await client.unsafe(`INSERT INTO ${progress} (tag, hash, statements_done) VALUES ($1, $2, $3)
|
|
91
|
+
ON CONFLICT (tag) DO UPDATE SET statements_done = EXCLUDED.statements_done, updated_at = now()`, [migration.tag, migration.hash, i + 1]);
|
|
58
92
|
}
|
|
59
93
|
await client.unsafe(`INSERT INTO ${table} ("hash", "created_at", "tag") VALUES ($1, $2, $3)`, [migration.hash, migration.folderMillis, migration.tag]);
|
|
94
|
+
await client.unsafe(`DELETE FROM ${progress} WHERE tag = $1`, [migration.tag]);
|
|
60
95
|
}
|
|
61
96
|
else {
|
|
62
97
|
await client.begin(async (tx) => {
|
|
@@ -118,6 +153,35 @@ export function readMigrations(options) {
|
|
|
118
153
|
export function parseStatements(raw) {
|
|
119
154
|
return scanStatements(raw, { breakpointSeparator: true });
|
|
120
155
|
}
|
|
156
|
+
/** Companion to the tracker table, holding half-applied no-transaction files. */
|
|
157
|
+
export function progressTableName(migrationsTable) {
|
|
158
|
+
return `${migrationsTable}_progress`;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The statement index a half-applied no-transaction file resumes from. Zero
|
|
162
|
+
* when there is no record of one.
|
|
163
|
+
*
|
|
164
|
+
* A recorded partial whose hash no longer matches the file is refused rather
|
|
165
|
+
* than resumed: the mark counts statements in the file as it was, and the file
|
|
166
|
+
* on disk is now a different one, so no index into it means anything.
|
|
167
|
+
*/
|
|
168
|
+
export function resumePoint(migration, recorded, migrationsTable) {
|
|
169
|
+
if (recorded === undefined)
|
|
170
|
+
return 0;
|
|
171
|
+
if (recorded.hash !== migration.hash) {
|
|
172
|
+
throw new Error(`Migration "${migration.tag}" is half applied — ${recorded.done} of its statements ran before the ` +
|
|
173
|
+
`runner stopped, recorded in ${progressTableName(migrationsTable)} — and its file content has ` +
|
|
174
|
+
`changed since (recorded sha256 ${recorded.hash.slice(0, 12)}…, file is now ` +
|
|
175
|
+
`${migration.hash.slice(0, 12)}…). The recorded count indexes into the file as it was, so it ` +
|
|
176
|
+
`cannot be carried into the new one. Restore the file to the content that was running, let the ` +
|
|
177
|
+
`resume finish, and put the change in a NEW migration; or, if the half-applied statements have ` +
|
|
178
|
+
`been undone by hand, delete the "${migration.tag}" row from ` +
|
|
179
|
+
`${progressTableName(migrationsTable)} to start the file over.`);
|
|
180
|
+
}
|
|
181
|
+
// A count past the end means every statement landed and only the tracker row
|
|
182
|
+
// was missed: nothing left to run, and the caller writes the row.
|
|
183
|
+
return Math.min(recorded.done, migration.statements.length);
|
|
184
|
+
}
|
|
121
185
|
function quoteIdent(name) {
|
|
122
186
|
return `"${name.replace(/"/g, '""')}"`;
|
|
123
187
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../../src/data/migrate/apply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,qBAAqB,GAAG,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../../src/data/migrate/apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,14 @@ export interface MigrateResult {
|
|
|
8
8
|
migrationsRun: number;
|
|
9
9
|
extensions: string[];
|
|
10
10
|
safetyCheck: MigrationCheckResult;
|
|
11
|
+
/** No-transaction files picked up half applied and finished. Issue #378. */
|
|
12
|
+
resumed: ResumedMigration[];
|
|
13
|
+
}
|
|
14
|
+
export interface ResumedMigration {
|
|
15
|
+
tag: string;
|
|
16
|
+
/** 1-based statement the rerun started from. */
|
|
17
|
+
fromStatement: number;
|
|
18
|
+
totalStatements: number;
|
|
11
19
|
}
|
|
12
20
|
export declare class UnsafeMigrationError extends Error {
|
|
13
21
|
readonly check: MigrationCheckResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/data/migrate/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAElE,OAAO,EAAmB,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAQvE,MAAM,WAAW,cAAc;CAAG;AAElC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,WAAW,EAAE,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/data/migrate/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAElE,OAAO,EAAmB,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAQvE,MAAM,WAAW,cAAc;CAAG;AAElC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,WAAW,EAAE,oBAAoB,CAAA;IACjC,4EAA4E;IAC5E,OAAO,EAAE,gBAAgB,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,qBAAa,oBAAqB,SAAQ,KAAK;aAG3B,KAAK,EAAE,oBAAoB;IAF7C,SAAkB,IAAI,0BAAyB;gBAE7B,KAAK,EAAE,oBAAoB,EAC3C,OAAO,EAAE,MAAM;CAIlB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAU3G;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CA+CtI"}
|
package/dist/data/migrate/run.js
CHANGED
|
@@ -45,16 +45,19 @@ export async function migrateOne(instance, dbConfig, _options = {}) {
|
|
|
45
45
|
}
|
|
46
46
|
extensionsCreated = [...dbConfig.extensions];
|
|
47
47
|
}
|
|
48
|
+
const resumed = [];
|
|
48
49
|
const migrationsRun = await applyMigrations({
|
|
49
50
|
migrationsFolder: dbConfig.migrations,
|
|
50
51
|
migrationsTable: dbConfig.migrationTable,
|
|
51
52
|
connectionUrl: dbConfig.url,
|
|
53
|
+
onResume: (tag, fromStatement, totalStatements) => resumed.push({ tag, fromStatement, totalStatements }),
|
|
52
54
|
});
|
|
53
55
|
return {
|
|
54
56
|
database: dbConfig.name,
|
|
55
57
|
migrationsRun,
|
|
56
58
|
extensions: extensionsCreated,
|
|
57
59
|
safetyCheck,
|
|
60
|
+
resumed,
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
63
|
//# sourceMappingURL=run.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/data/migrate/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAGvC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,eAAe,EAA6B,MAAM,YAAY,CAAA;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/data/migrate/run.ts"],"names":[],"mappings":"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"}
|