pg-boss 12.30.0 → 12.31.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/README.md +5 -5
- package/dist/adapters/bun.d.ts +33 -0
- package/dist/adapters/bun.d.ts.map +1 -0
- package/dist/adapters/bun.js +86 -0
- package/dist/adapters/drizzle.d.ts.map +1 -1
- package/dist/adapters/drizzle.js +44 -7
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/placeholders.d.ts +15 -0
- package/dist/adapters/placeholders.d.ts.map +1 -1
- package/dist/adapters/placeholders.js +28 -0
- package/dist/attorney.d.ts +2 -1
- package/dist/attorney.d.ts.map +1 -1
- package/dist/attorney.js +6 -1
- package/dist/cli.js +61 -15
- package/dist/contractor.d.ts +3 -7
- package/dist/contractor.d.ts.map +1 -1
- package/dist/contractor.js +35 -6
- package/dist/index.d.ts +15 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -5
- package/dist/manager.d.ts +3 -1
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +14 -3
- package/dist/migrationStore.d.ts +3 -2
- package/dist/migrationStore.d.ts.map +1 -1
- package/dist/migrationStore.js +77 -3
- package/dist/plans.d.ts +52 -1
- package/dist/plans.d.ts.map +1 -1
- package/dist/plans.js +163 -23
- package/dist/rrule.d.ts +85 -0
- package/dist/rrule.d.ts.map +1 -0
- package/dist/rrule.js +384 -0
- package/dist/schema.json +39 -3
- package/dist/timekeeper.d.ts +103 -2
- package/dist/timekeeper.d.ts.map +1 -1
- package/dist/timekeeper.js +499 -44
- package/dist/timezone.d.ts +18 -0
- package/dist/timezone.d.ts.map +1 -0
- package/dist/timezone.js +34 -0
- package/dist/types.d.ts +88 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ This will likely cater the most to teams already familiar with the simplicity of
|
|
|
46
46
|
* Create jobs in an existing db transaction, including adapters for popular ORMs such as Drizzle, Knex, Kysely, Prisma
|
|
47
47
|
* Backpressure-compatible polling workers, including support for LISTEN/NOTIFY low latency delivery
|
|
48
48
|
* Job dependency workflow orchestration
|
|
49
|
-
* Cron scheduling, job deferral
|
|
49
|
+
* Cron and RRULE scheduling, job deferral
|
|
50
50
|
* Queue storage policies to support a variety of rate limiting, debouncing, and concurrency use cases
|
|
51
51
|
* Priority queues, dead letter queues with redrive, automatic retries with exponential backoff
|
|
52
52
|
* Pub/sub API for fan-out queue relationships
|
|
@@ -85,10 +85,10 @@ pg-boss is MIT licensed and always will be. It is maintained by one person, and
|
|
|
85
85
|
|
|
86
86
|
<!-- sponsors:start The logos below are generated from GitHub Sponsors by scripts/sync-sponsors.js. Do not edit them directly. -->
|
|
87
87
|
<div style="display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin: 1.5rem 0;">
|
|
88
|
-
<a href="https://superpower.com/" target="_blank" rel="noopener"><img src="https://avatars.githubusercontent.com/u/114334709?s=
|
|
89
|
-
<a href="https://altruistiq.com/" target="_blank" rel="noopener"><img src="https://avatars.githubusercontent.com/u/88195975?s=
|
|
90
|
-
<a href="https://wasp.sh/" target="_blank" rel="noopener"><img src="https://avatars.githubusercontent.com/u/55102317?s=
|
|
91
|
-
<a href="https://github.com/Ferry-Health" target="_blank" rel="noopener"><img src="https://avatars.githubusercontent.com/u/158637456?s=
|
|
88
|
+
<a href="https://superpower.com/" target="_blank" rel="noopener"><img src="https://avatars.githubusercontent.com/u/114334709?s=256&v=4" alt="Superpower" title="Superpower" width="96" height="96" style="border-radius: 8px;"></a>
|
|
89
|
+
<a href="https://altruistiq.com/" target="_blank" rel="noopener"><img src="https://avatars.githubusercontent.com/u/88195975?s=256&v=4" alt="Altruistiq" title="Altruistiq" width="64" height="64" style="border-radius: 8px;"></a>
|
|
90
|
+
<a href="https://wasp.sh/" target="_blank" rel="noopener"><img src="https://avatars.githubusercontent.com/u/55102317?s=256&v=4" alt="wasp-lang" title="wasp-lang" width="64" height="64" style="border-radius: 8px;"></a>
|
|
91
|
+
<a href="https://github.com/Ferry-Health" target="_blank" rel="noopener"><img src="https://avatars.githubusercontent.com/u/158637456?s=256&v=4" alt="Ferry Health" title="Ferry Health" width="64" height="64" style="border-radius: 8px;"></a>
|
|
92
92
|
</div>
|
|
93
93
|
<!-- sponsors:end -->
|
|
94
94
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { IDatabase } from '../types.ts';
|
|
2
|
+
export interface BunSqlLike {
|
|
3
|
+
unsafe(text: string, values?: unknown[]): Promise<unknown>;
|
|
4
|
+
reserve(): Promise<BunReservedSqlLike>;
|
|
5
|
+
listen?(channel: string, onNotification: (payload: string) => void, onSubscribe?: () => void): Promise<BunSubscriptionLike>;
|
|
6
|
+
}
|
|
7
|
+
export interface BunSubscriptionLike {
|
|
8
|
+
unlisten(): Promise<void> | void;
|
|
9
|
+
}
|
|
10
|
+
export interface BunReservedSqlLike {
|
|
11
|
+
unsafe(text: string, values?: unknown[]): Promise<unknown>;
|
|
12
|
+
release(): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Adapts a `Bun.SQL` client to pg-boss's IDatabase, for running pg-boss on Bun's built-in
|
|
16
|
+
* PostgreSQL driver instead of the bundled `pg` pool.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { SQL } from 'bun'
|
|
21
|
+
* import { PgBoss, fromBunSql } from 'pg-boss'
|
|
22
|
+
*
|
|
23
|
+
* const boss = new PgBoss({ db: fromBunSql(new SQL(connectionString)) })
|
|
24
|
+
* await boss.start()
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* The caller owns the client's lifecycle: pg-boss never calls `end()` on it.
|
|
28
|
+
*
|
|
29
|
+
* `useListenNotify` needs `sql.listen()`, which Bun added in 1.4.0. On an older runtime the adapter
|
|
30
|
+
* exposes no `listen` and pg-boss falls back to polling.
|
|
31
|
+
*/
|
|
32
|
+
export declare function fromBunSql(client: BunSqlLike): IDatabase;
|
|
33
|
+
//# sourceMappingURL=bun.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../../src/adapters/bun.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAM5C,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1D,OAAO,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAEtC,MAAM,CAAC,CACL,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACzC,WAAW,CAAC,EAAE,MAAM,IAAI,GACvB,OAAO,CAAC,mBAAmB,CAAC,CAAA;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1D,OAAO,IAAI,IAAI,CAAA;CAChB;AAeD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAE,MAAM,EAAE,UAAU,GAAG,SAAS,CAiCzD"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { expandArrayParams } from "./placeholders.js";
|
|
2
|
+
// pg-boss ships schema installation, migration and maintenance as `BEGIN; ... COMMIT;` scripts
|
|
3
|
+
// (plans.transaction / plans.locked). Bun rejects those on a pooled connection with `Only use
|
|
4
|
+
// sql.begin, sql.reserved or max: 1`, because the pool is free to hand the next statement to a
|
|
5
|
+
// different connection. A reserved connection is pinned for its lifetime and takes them as-is.
|
|
6
|
+
const STARTS_TRANSACTION = /^\s*BEGIN\b/i;
|
|
7
|
+
// Bun reports every server error as ERR_POSTGRES_SERVER_ERROR and puts the SQLSTATE in `errno`,
|
|
8
|
+
// where node-postgres puts it in `code`. pg-boss reads `code` to recognise a handful of states it
|
|
9
|
+
// treats as control flow rather than failure - 23505 from a lost fetch race, 22012 from the
|
|
10
|
+
// in-SQL insert guard, 25001 from REINDEX inside a transaction - so an unmapped error turns a
|
|
11
|
+
// tolerated race into a thrown one.
|
|
12
|
+
const BUN_SERVER_ERROR = 'ERR_POSTGRES_SERVER_ERROR';
|
|
13
|
+
/**
|
|
14
|
+
* Adapts a `Bun.SQL` client to pg-boss's IDatabase, for running pg-boss on Bun's built-in
|
|
15
|
+
* PostgreSQL driver instead of the bundled `pg` pool.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { SQL } from 'bun'
|
|
20
|
+
* import { PgBoss, fromBunSql } from 'pg-boss'
|
|
21
|
+
*
|
|
22
|
+
* const boss = new PgBoss({ db: fromBunSql(new SQL(connectionString)) })
|
|
23
|
+
* await boss.start()
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* The caller owns the client's lifecycle: pg-boss never calls `end()` on it.
|
|
27
|
+
*
|
|
28
|
+
* `useListenNotify` needs `sql.listen()`, which Bun added in 1.4.0. On an older runtime the adapter
|
|
29
|
+
* exposes no `listen` and pg-boss falls back to polling.
|
|
30
|
+
*/
|
|
31
|
+
export function fromBunSql(client) {
|
|
32
|
+
const db = {
|
|
33
|
+
async executeSql(text, values) {
|
|
34
|
+
const query = expandArrayParams(text, values);
|
|
35
|
+
// A parameterless call goes through the simple protocol, which is what lets a multi-statement
|
|
36
|
+
// script run at all; pg-boss only ever concatenates statements when there is nothing to bind.
|
|
37
|
+
const run = (target) => query.values.length
|
|
38
|
+
? target.unsafe(query.text, query.values)
|
|
39
|
+
: target.unsafe(query.text);
|
|
40
|
+
try {
|
|
41
|
+
return unwrapBunResult(await (STARTS_TRANSACTION.test(text) ? runReserved(client, run) : run(client)));
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
throw withSqlState(err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
// Bun's third argument fires on the initial subscribe and again after each reconnect - it
|
|
49
|
+
// re-establishes the connection and re-subscribes every channel on its own - which is exactly
|
|
50
|
+
// what pg-boss's onReconnect is for: a notification sent while the listener was down is gone,
|
|
51
|
+
// so the gap has to be closed by a fetch. Only expose `listen` when the client actually has it
|
|
52
|
+
// (Bun < 1.4.0, or a mock), so the notifier cleanly falls back to polling otherwise.
|
|
53
|
+
if (typeof client.listen === 'function') {
|
|
54
|
+
db.listen = async (channel, onNotification, onReconnect) => {
|
|
55
|
+
const subscription = await client.listen(channel, onNotification, onReconnect);
|
|
56
|
+
return { close: async () => { await subscription.unlisten(); } };
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return db;
|
|
60
|
+
}
|
|
61
|
+
async function runReserved(client, run) {
|
|
62
|
+
const reserved = await client.reserve();
|
|
63
|
+
try {
|
|
64
|
+
return await run(reserved);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
reserved.release();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Bun returns the rows of a single statement as a flat array, and one such array per statement for
|
|
71
|
+
// a multi-statement script. Flatten the latter so a RETURNING in the middle of a transaction block
|
|
72
|
+
// is not lost behind the trailing COMMIT's empty result - the same unwrapping the PGlite adapter
|
|
73
|
+
// does for exec() and pg-boss does for node-postgres (see unwrapSQLResult).
|
|
74
|
+
function unwrapBunResult(result) {
|
|
75
|
+
if (!Array.isArray(result)) {
|
|
76
|
+
return { rows: [] };
|
|
77
|
+
}
|
|
78
|
+
return { rows: result.some(Array.isArray) ? result.flat() : result };
|
|
79
|
+
}
|
|
80
|
+
function withSqlState(err) {
|
|
81
|
+
const error = err;
|
|
82
|
+
if (error?.code === BUN_SERVER_ERROR && error.errno) {
|
|
83
|
+
error.code = error.errno;
|
|
84
|
+
}
|
|
85
|
+
return err;
|
|
86
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../../src/adapters/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAI5C,MAAM,WAAW,sBAAsB;IAGrC,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,EAAE,GAAG,GAAG,EAAE,CAAC,CAAA;CAC9E;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAC9D,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAA;CAC/B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAE,EAAE,EAAE,sBAAsB,EAAE,GAAG,EAAE,iBAAiB,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../../src/adapters/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAI5C,MAAM,WAAW,sBAAsB;IAGrC,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,EAAE,GAAG,GAAG,EAAE,CAAC,CAAA;CAC9E;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAC9D,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAA;CAC/B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAE,EAAE,EAAE,sBAAsB,EAAE,GAAG,EAAE,iBAAiB,GAAG,SAAS,CAQ1F"}
|
package/dist/adapters/drizzle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parsePlaceholders } from "./placeholders.js";
|
|
1
|
+
import { ARRAY_CAST, parsePlaceholders } from "./placeholders.js";
|
|
2
2
|
import { unwrapSQLResult } from "../tools.js";
|
|
3
3
|
/**
|
|
4
4
|
* Wraps a drizzle-orm transaction as an {@link IDatabase}.
|
|
@@ -21,13 +21,50 @@ export function fromDrizzle(tx, sql) {
|
|
|
21
21
|
return {
|
|
22
22
|
async executeSql(text, values) {
|
|
23
23
|
const { parts, reordered } = parsePlaceholders(text, values);
|
|
24
|
-
const strings =
|
|
25
|
-
// Bind each value through sql.param so drizzle emits exactly one placeholder
|
|
26
|
-
// per value. A bare array would otherwise be expanded into a parameter list
|
|
27
|
-
// ($2, $3, ...) instead of a single array-typed parameter, breaking any query
|
|
28
|
-
// with `= ANY($N::uuid[])` (deleteJob/complete/fail/cancel/resume/retry).
|
|
29
|
-
const params = reordered.map(value => sql.param(value));
|
|
24
|
+
const { strings, params } = buildQuery(parts, reordered, sql);
|
|
30
25
|
return unwrapSQLResult(await tx.execute(sql(strings, ...params)));
|
|
31
26
|
}
|
|
32
27
|
};
|
|
33
28
|
}
|
|
29
|
+
// A parameter cast to an array type (`$N::uuid[]`) is expanded into an inline
|
|
30
|
+
// ARRAY[...] constructor of scalar parameters rather than bound whole.
|
|
31
|
+
//
|
|
32
|
+
// Binding the array whole (sql.param([a, b])) is what postgres wants, and it is
|
|
33
|
+
// what node-postgres and postgres-js do, but Bun's SQL client cannot encode a JS
|
|
34
|
+
// array: it stringifies it to `a,b`, and every `= ANY($N::uuid[])` in pg-boss then
|
|
35
|
+
// fails with `malformed array literal` (oven-sh/bun#18775). Letting drizzle expand
|
|
36
|
+
// the array on its own is no better - it emits a bare `$2, $3` list, which is not
|
|
37
|
+
// valid where the statement expects one array expression.
|
|
38
|
+
//
|
|
39
|
+
// ARRAY[$2, $3] is valid everywhere a single array parameter was, keeps every
|
|
40
|
+
// element a real bind parameter (so no value is ever escaped into SQL text), and
|
|
41
|
+
// carries no driver-specific knowledge.
|
|
42
|
+
//
|
|
43
|
+
// The array cast is what makes the rewrite safe to apply, so it is required rather
|
|
44
|
+
// than assumed (see ARRAY_CAST).
|
|
45
|
+
function buildQuery(parts, values, sql) {
|
|
46
|
+
const literals = [];
|
|
47
|
+
const params = [];
|
|
48
|
+
let pending = parts[0];
|
|
49
|
+
for (let i = 0; i < values.length; i++) {
|
|
50
|
+
const value = values[i];
|
|
51
|
+
if (Array.isArray(value) && ARRAY_CAST.test(parts[i + 1])) {
|
|
52
|
+
// ARRAY[] alone has no element type; the cast that qualified this parameter
|
|
53
|
+
// for the rewrite is what resolves it.
|
|
54
|
+
pending += 'ARRAY[';
|
|
55
|
+
for (let element = 0; element < value.length; element++) {
|
|
56
|
+
literals.push(pending);
|
|
57
|
+
params.push(sql.param(value[element]));
|
|
58
|
+
pending = element === value.length - 1 ? '' : ',';
|
|
59
|
+
}
|
|
60
|
+
pending += ']' + parts[i + 1];
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
literals.push(pending);
|
|
64
|
+
params.push(sql.param(value));
|
|
65
|
+
pending = parts[i + 1];
|
|
66
|
+
}
|
|
67
|
+
literals.push(pending);
|
|
68
|
+
const strings = Object.assign(literals, { raw: [...literals] });
|
|
69
|
+
return { strings, params };
|
|
70
|
+
}
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ export { fromKysely } from './kysely.ts';
|
|
|
3
3
|
export { fromDrizzle } from './drizzle.ts';
|
|
4
4
|
export { fromPrisma } from './prisma.ts';
|
|
5
5
|
export { fromPglite } from './pglite.ts';
|
|
6
|
+
export { fromBunSql } from './bun.ts';
|
|
6
7
|
export type { KnexTransactionLike } from './knex.ts';
|
|
7
8
|
export type { KyselyTransactionLike } from './kysely.ts';
|
|
8
9
|
export type { DrizzleTransactionLike, DrizzleSqlTagLike } from './drizzle.ts';
|
|
9
10
|
export type { PrismaTransactionLike } from './prisma.ts';
|
|
10
11
|
export type { PGliteLike } from './pglite.ts';
|
|
12
|
+
export type { BunSqlLike, BunReservedSqlLike } from './bun.ts';
|
|
11
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAErC,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AACpD,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,YAAY,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC7E,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA"}
|
package/dist/adapters/index.js
CHANGED
|
@@ -11,4 +11,19 @@ export declare function parsePlaceholders(text: string, values?: readonly unknow
|
|
|
11
11
|
parts: string[];
|
|
12
12
|
reordered: unknown[];
|
|
13
13
|
};
|
|
14
|
+
export declare const ARRAY_CAST: RegExp;
|
|
15
|
+
/**
|
|
16
|
+
* Rewrites `$N` placeholders into a fresh sequential list, expanding any array-cast parameter
|
|
17
|
+
* into an inline `ARRAY[...]` constructor of scalar parameters.
|
|
18
|
+
*
|
|
19
|
+
* Postgres wants an array bound whole, and node-postgres, postgres-js and PGlite all encode one.
|
|
20
|
+
* Bun's SQL client cannot: it stringifies the array, so `= ANY($N::uuid[])` reaches the server as
|
|
21
|
+
* `a,b` and fails with `malformed array literal` (oven-sh/bun#18775). `ARRAY[$2,$3]` is valid
|
|
22
|
+
* everywhere a single array parameter was, and every element stays a bind parameter, so no value
|
|
23
|
+
* is ever escaped into SQL text.
|
|
24
|
+
*/
|
|
25
|
+
export declare function expandArrayParams(text: string, values?: readonly unknown[]): {
|
|
26
|
+
text: string;
|
|
27
|
+
values: unknown[];
|
|
28
|
+
};
|
|
14
29
|
//# sourceMappingURL=placeholders.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/adapters/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG;IAC7E,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,SAAS,EAAE,OAAO,EAAE,CAAA;CACrB,CAcA"}
|
|
1
|
+
{"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/adapters/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG;IAC7E,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,SAAS,EAAE,OAAO,EAAE,CAAA;CACrB,CAcA;AAMD,eAAO,MAAM,UAAU,QAA6B,CAAA;AAEpD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG;IAC7E,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,EAAE,CAAA;CAClB,CAgBA"}
|
|
@@ -22,3 +22,31 @@ export function parsePlaceholders(text, values) {
|
|
|
22
22
|
parts.push(text.slice(lastIndex));
|
|
23
23
|
return { parts, reordered };
|
|
24
24
|
}
|
|
25
|
+
// A parameter cast to an array type, as in `$2::uuid[]`. pg-boss binds job ids and queue names
|
|
26
|
+
// this way, and the cast is what tells an adapter the value is a postgres array rather than a
|
|
27
|
+
// JSON one - a JS array bound to a json column is a JSON array, and rewriting that would change
|
|
28
|
+
// what gets stored.
|
|
29
|
+
export const ARRAY_CAST = /^::[a-z_][a-z0-9_]*\[\]/i;
|
|
30
|
+
/**
|
|
31
|
+
* Rewrites `$N` placeholders into a fresh sequential list, expanding any array-cast parameter
|
|
32
|
+
* into an inline `ARRAY[...]` constructor of scalar parameters.
|
|
33
|
+
*
|
|
34
|
+
* Postgres wants an array bound whole, and node-postgres, postgres-js and PGlite all encode one.
|
|
35
|
+
* Bun's SQL client cannot: it stringifies the array, so `= ANY($N::uuid[])` reaches the server as
|
|
36
|
+
* `a,b` and fails with `malformed array literal` (oven-sh/bun#18775). `ARRAY[$2,$3]` is valid
|
|
37
|
+
* everywhere a single array parameter was, and every element stays a bind parameter, so no value
|
|
38
|
+
* is ever escaped into SQL text.
|
|
39
|
+
*/
|
|
40
|
+
export function expandArrayParams(text, values) {
|
|
41
|
+
const { parts, reordered } = parsePlaceholders(text, values);
|
|
42
|
+
const expanded = [];
|
|
43
|
+
let rewritten = parts[0];
|
|
44
|
+
for (let i = 0; i < reordered.length; i++) {
|
|
45
|
+
const value = reordered[i];
|
|
46
|
+
rewritten += Array.isArray(value) && ARRAY_CAST.test(parts[i + 1])
|
|
47
|
+
? `ARRAY[${value.map(element => `$${expanded.push(element)}`).join(',')}]`
|
|
48
|
+
: `$${expanded.push(value)}`;
|
|
49
|
+
rewritten += parts[i + 1];
|
|
50
|
+
}
|
|
51
|
+
return { text: rewritten, values: expanded };
|
|
52
|
+
}
|
package/dist/attorney.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ declare const POLICY: {
|
|
|
4
4
|
MIN_POLLING_INTERVAL_MS: number;
|
|
5
5
|
MAX_RETENTION_DAYS: number;
|
|
6
6
|
};
|
|
7
|
+
declare const COMPATIBILITY_FLAGS: readonly ["noSkipLocked", "noMultiMutationCte", "noTablePartitioning", "noDeferrableConstraints", "noAdvisoryLocks", "noCoveringIndexes", "noAddColumnBackfill", "noListenNotify", "noIndexProgressView", "noReindex", "noMonitorVacuum"];
|
|
7
8
|
declare function validateQueueArgs(config?: any): void;
|
|
8
9
|
declare function pinZonelessDateTime(value: string): string;
|
|
9
10
|
declare function checkSendArgs(args: any): types.Request;
|
|
@@ -21,5 +22,5 @@ declare function getConfig(value: string | types.ConstructorOptions): types.Reso
|
|
|
21
22
|
declare function assertPostgresObjectName(name: string): void;
|
|
22
23
|
declare function assertQueueName(name: string): void;
|
|
23
24
|
declare function assertKey(key: string): void;
|
|
24
|
-
export { assertKey, assertPostgresObjectName, assertQueueName, checkFetchArgs, checkSendArgs, checkUpdateArgs, checkWorkArgs, getConfig, pinZonelessDateTime, POLICY, validateFlowJobs, validateGroupConfig, validateQueueArgs };
|
|
25
|
+
export { assertKey, COMPATIBILITY_FLAGS, assertPostgresObjectName, assertQueueName, checkFetchArgs, checkSendArgs, checkUpdateArgs, checkWorkArgs, getConfig, pinZonelessDateTime, POLICY, validateFlowJobs, validateGroupConfig, validateQueueArgs };
|
|
25
26
|
//# sourceMappingURL=attorney.d.ts.map
|
package/dist/attorney.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attorney.d.ts","sourceRoot":"","sources":["../src/attorney.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AAExC,QAAA,MAAM,MAAM;;;;CAIX,CAAA;
|
|
1
|
+
{"version":3,"file":"attorney.d.ts","sourceRoot":"","sources":["../src/attorney.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AAExC,QAAA,MAAM,MAAM;;;;CAIX,CAAA;AAKD,QAAA,MAAM,mBAAmB,2OAYf,CAAA;AAsEV,iBAAS,iBAAiB,CAAE,MAAM,GAAE,GAAQ,QAc3C;AAgBD,iBAAS,mBAAmB,CAAE,KAAK,EAAE,MAAM,UAE1C;AAED,iBAAS,aAAa,CAAE,IAAI,EAAE,GAAG,GAAG,KAAK,CAAC,OAAO,CAgDhD;AAOD,iBAAS,eAAe,CAAE,IAAI,EAAE,GAAG,EAAE,EAAE,MAAc,EAAE;;CAAK,GAAG,KAAK,CAAC,OAAO,CA4D3E;AAED,iBAAS,mBAAmB,CAAE,MAAM,EAAE,GAAG,QAOxC;AAED,iBAAS,gBAAgB,CAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,QA2D/C;AA2GD,iBAAS,aAAa,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG;IAClD,OAAO,EAAE,KAAK,CAAC,mBAAmB,CAAA;IAClC,QAAQ,EAAE,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;CACjC,CA8BA;AAED,iBAAS,cAAc,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,QAQlD;AAED,iBAAS,SAAS,CAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,0BAA0B,CAyB9F;AA4FD,iBAAS,wBAAwB,CAAE,IAAI,EAAE,MAAM,QAiC9C;AAED,iBAAS,eAAe,CAAE,IAAI,EAAE,MAAM,QAIrC;AAED,iBAAS,SAAS,CAAE,GAAG,EAAE,MAAM,QAI9B;AA+LD,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,aAAa,EACb,SAAS,EACT,mBAAmB,EACnB,MAAM,EACN,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EAClB,CAAA"}
|
package/dist/attorney.js
CHANGED
|
@@ -15,6 +15,7 @@ const COMPATIBILITY_FLAGS = [
|
|
|
15
15
|
'noDeferrableConstraints',
|
|
16
16
|
'noAdvisoryLocks',
|
|
17
17
|
'noCoveringIndexes',
|
|
18
|
+
'noAddColumnBackfill',
|
|
18
19
|
'noListenNotify',
|
|
19
20
|
'noIndexProgressView',
|
|
20
21
|
'noReindex',
|
|
@@ -32,6 +33,10 @@ const BACKEND_PROFILES = {
|
|
|
32
33
|
noDeferrableConstraints: true,
|
|
33
34
|
noAdvisoryLocks: true,
|
|
34
35
|
noCoveringIndexes: true,
|
|
36
|
+
// ADD COLUMN runs as a schema-change job, so a transaction cannot write the column it just
|
|
37
|
+
// added: the UPDATE fails with "column is being backfilled". A migration that seeds a new
|
|
38
|
+
// column leaves the seeding statement out here.
|
|
39
|
+
noAddColumnBackfill: true,
|
|
35
40
|
noListenNotify: true,
|
|
36
41
|
// Online DDL runs as a schema-change job, not the PG CONCURRENTLY path, and
|
|
37
42
|
// pg_stat_progress_create_index isn't available — so BAM can't use liveness-based reclaim.
|
|
@@ -617,4 +622,4 @@ function applyFlowConfig(config) {
|
|
|
617
622
|
assert(!('flowIntervalSeconds' in config) || config.flowIntervalSeconds >= minInterval, `configuration assert: flowIntervalSeconds must be at least ${minInterval} seconds`);
|
|
618
623
|
config.flowIntervalSeconds = config.flowIntervalSeconds || 5;
|
|
619
624
|
}
|
|
620
|
-
export { assertKey, assertPostgresObjectName, assertQueueName, checkFetchArgs, checkSendArgs, checkUpdateArgs, checkWorkArgs, getConfig, pinZonelessDateTime, POLICY, validateFlowJobs, validateGroupConfig, validateQueueArgs };
|
|
625
|
+
export { assertKey, COMPATIBILITY_FLAGS, assertPostgresObjectName, assertQueueName, checkFetchArgs, checkSendArgs, checkUpdateArgs, checkWorkArgs, getConfig, pinZonelessDateTime, POLICY, validateFlowJobs, validateGroupConfig, validateQueueArgs };
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import Db from "./db.js";
|
|
|
6
6
|
import * as plans from "./plans.js";
|
|
7
7
|
import Contractor from "./contractor.js";
|
|
8
8
|
import * as migrationStore from "./migrationStore.js";
|
|
9
|
+
import * as attorney from "./attorney.js";
|
|
9
10
|
import packageJson from '../package.json' with { type: 'json' };
|
|
10
11
|
const schemaVersion = packageJson.pgboss.schema;
|
|
11
12
|
function printHelp() {
|
|
@@ -34,6 +35,8 @@ Options:
|
|
|
34
35
|
--password, -p <pass> Database password
|
|
35
36
|
--connection-string Full connection string (overrides other connection options)
|
|
36
37
|
--ssl Enable SSL connection
|
|
38
|
+
--backend <profile> Database backend: postgres (default), cockroachdb, yugabytedb, citus.
|
|
39
|
+
Non-postgres backends need this to emit schema they accept.
|
|
37
40
|
--dry-run Output SQL without executing (for plans and reindex commands)
|
|
38
41
|
--force Rebuild every job index, not just the bloated ones (reindex)
|
|
39
42
|
|
|
@@ -45,6 +48,7 @@ Environment Variables:
|
|
|
45
48
|
PGBOSS_USER Database user
|
|
46
49
|
PGBOSS_PASSWORD Database password
|
|
47
50
|
PGBOSS_SCHEMA Schema name (default: pgboss)
|
|
51
|
+
PGBOSS_BACKEND Database backend profile (default: postgres)
|
|
48
52
|
|
|
49
53
|
Config File (pgboss.json):
|
|
50
54
|
{
|
|
@@ -54,7 +58,8 @@ Config File (pgboss.json):
|
|
|
54
58
|
"user": "postgres",
|
|
55
59
|
"password": "secret",
|
|
56
60
|
"schema": "pgboss",
|
|
57
|
-
"ssl": false
|
|
61
|
+
"ssl": false,
|
|
62
|
+
"backend": "postgres"
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
Examples:
|
|
@@ -62,6 +67,7 @@ Examples:
|
|
|
62
67
|
pg-boss migrate --schema my_schema
|
|
63
68
|
pg-boss create --connection-string postgres://user:pass@localhost/db
|
|
64
69
|
pg-boss plans migrate --dry-run
|
|
70
|
+
pg-boss migrate --backend cockroachdb --connection-string postgres://root@localhost:26257/db
|
|
65
71
|
pg-boss version
|
|
66
72
|
PGBOSS_DATABASE_URL=postgres://localhost/mydb pg-boss migrate
|
|
67
73
|
`);
|
|
@@ -90,6 +96,13 @@ function loadConfigFile(configPath) {
|
|
|
90
96
|
}
|
|
91
97
|
return {};
|
|
92
98
|
}
|
|
99
|
+
// Resolves the connection AND the backend compatibility flags, because every command that writes
|
|
100
|
+
// schema needs both. Without the flags a CockroachDB target is handed stock-PostgreSQL DDL - table
|
|
101
|
+
// partitioning, advisory locks, covering indexes, a backfill of a column added in the same
|
|
102
|
+
// transaction - and the command fails partway through a migration rather than up front. The flags
|
|
103
|
+
// are derived from --backend / PGBOSS_BACKEND / the config file by the same attorney.getConfig() the
|
|
104
|
+
// library constructor uses, so the CLI and a running boss cannot disagree about what a backend
|
|
105
|
+
// supports.
|
|
93
106
|
function getConnectionConfig(args) {
|
|
94
107
|
const fileConfig = loadConfigFile(args.config);
|
|
95
108
|
const config = {
|
|
@@ -110,7 +123,28 @@ function getConnectionConfig(args) {
|
|
|
110
123
|
console.error('Run "pg-boss --help" for more information.');
|
|
111
124
|
process.exit(1);
|
|
112
125
|
}
|
|
113
|
-
return config;
|
|
126
|
+
return resolveBackendConfig(config, args, fileConfig);
|
|
127
|
+
}
|
|
128
|
+
// Runs a config through the library's own resolver so the compatibility flags come from one place.
|
|
129
|
+
// Split out of getConnectionConfig because `plans` has to answer offline, where there is a backend
|
|
130
|
+
// to honour but no connection to read.
|
|
131
|
+
function resolveBackendConfig(config, args, fileConfig) {
|
|
132
|
+
const backend = args.backend || process.env.PGBOSS_BACKEND || fileConfig.backend;
|
|
133
|
+
if (backend) {
|
|
134
|
+
// pglite is in-process and has no connection string, so it can only be driven from library code.
|
|
135
|
+
if (backend === 'pglite') {
|
|
136
|
+
console.error('Error: the pglite backend is in-process and cannot be used from the CLI.');
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
config.backend = backend;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
return attorney.getConfig(config);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
console.error(`Error: ${err.message}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
114
148
|
}
|
|
115
149
|
function parseCliArgs() {
|
|
116
150
|
const { values, positionals } = parseArgs({
|
|
@@ -125,6 +159,7 @@ function parseCliArgs() {
|
|
|
125
159
|
password: { type: 'string', short: 'p' },
|
|
126
160
|
'connection-string': { type: 'string' },
|
|
127
161
|
ssl: { type: 'boolean' },
|
|
162
|
+
backend: { type: 'string' },
|
|
128
163
|
'dry-run': { type: 'boolean' },
|
|
129
164
|
force: { type: 'boolean' }
|
|
130
165
|
},
|
|
@@ -141,6 +176,7 @@ function parseCliArgs() {
|
|
|
141
176
|
password: values.password,
|
|
142
177
|
connectionString: values['connection-string'],
|
|
143
178
|
ssl: values.ssl,
|
|
179
|
+
backend: values.backend,
|
|
144
180
|
dryRun: values['dry-run'],
|
|
145
181
|
force: values.force,
|
|
146
182
|
command: positionals[0],
|
|
@@ -214,7 +250,7 @@ async function cmdCreate(args) {
|
|
|
214
250
|
const config = getConnectionConfig(args);
|
|
215
251
|
const schema = config.schema || plans.DEFAULT_SCHEMA;
|
|
216
252
|
if (args.dryRun) {
|
|
217
|
-
const sql = plans.create(schema, schemaVersion, { createSchema: true });
|
|
253
|
+
const sql = plans.create(schema, schemaVersion, { ...config, createSchema: true });
|
|
218
254
|
console.log('-- SQL to create pg-boss schema:');
|
|
219
255
|
console.log(sql);
|
|
220
256
|
return;
|
|
@@ -227,7 +263,7 @@ async function cmdCreate(args) {
|
|
|
227
263
|
return;
|
|
228
264
|
}
|
|
229
265
|
console.log(`Creating pg-boss schema "${schema}"...`);
|
|
230
|
-
const sql = plans.create(schema, schemaVersion, { createSchema: true });
|
|
266
|
+
const sql = plans.create(schema, schemaVersion, { ...config, createSchema: true });
|
|
231
267
|
await db.executeSql(sql);
|
|
232
268
|
console.log(`Successfully created pg-boss schema "${schema}" at version ${schemaVersion}`);
|
|
233
269
|
}
|
|
@@ -265,7 +301,7 @@ async function cmdMigrate(args) {
|
|
|
265
301
|
// Offline (or not yet installed) we can't know it, so fall back to the oldest supported starting
|
|
266
302
|
// version — the full chain — instead of a bogus "from 0" that fails on non-idempotent steps.
|
|
267
303
|
const fromVersion = version ?? migrationStore.getMinVersion(schema);
|
|
268
|
-
const sql = migrationStore.migrate(schema, fromVersion,
|
|
304
|
+
const sql = migrationStore.migrate(schema, fromVersion, migrationStore.getAllForConfig(config), config.noAdvisoryLocks, { inlineAsync: true, partitionTables });
|
|
269
305
|
console.log(`-- SQL to migrate pg-boss from version ${fromVersion} to ${schemaVersion}:`);
|
|
270
306
|
console.log(sql);
|
|
271
307
|
return;
|
|
@@ -275,7 +311,7 @@ async function cmdMigrate(args) {
|
|
|
275
311
|
const version = await getSchemaVersion(db, schema);
|
|
276
312
|
if (version === null) {
|
|
277
313
|
console.log(`pg-boss is not installed. Creating schema "${schema}"...`);
|
|
278
|
-
const sql = plans.create(schema, schemaVersion, { createSchema: true });
|
|
314
|
+
const sql = plans.create(schema, schemaVersion, { ...config, createSchema: true });
|
|
279
315
|
await db.executeSql(sql);
|
|
280
316
|
console.log(`Successfully created pg-boss schema "${schema}" at version ${schemaVersion}`);
|
|
281
317
|
return;
|
|
@@ -288,7 +324,7 @@ async function cmdMigrate(args) {
|
|
|
288
324
|
// Inline the async index builds rather than enqueuing BAM rows that nothing will run
|
|
289
325
|
// (the CLI exits without a worker); enumerate partitions so they are covered too.
|
|
290
326
|
const partitionTables = await getPartitionTables(db, schema);
|
|
291
|
-
const { sql, concurrent } = migrationStore.migrateCommands(schema, version,
|
|
327
|
+
const { sql, concurrent } = migrationStore.migrateCommands(schema, version, migrationStore.getAllForConfig(config), config.noAdvisoryLocks, { inlineAsync: true, partitionTables });
|
|
292
328
|
await db.executeSql(sql);
|
|
293
329
|
// CONCURRENTLY index builds must run outside the migration transaction, one at a time.
|
|
294
330
|
for (const statement of concurrent) {
|
|
@@ -315,13 +351,13 @@ async function cmdRollback(args) {
|
|
|
315
351
|
return;
|
|
316
352
|
}
|
|
317
353
|
if (args.dryRun) {
|
|
318
|
-
const sql = migrationStore.rollback(schema, version);
|
|
354
|
+
const sql = migrationStore.rollback(schema, version, migrationStore.getAllForConfig(config), config.noAdvisoryLocks);
|
|
319
355
|
console.log(`-- SQL to rollback pg-boss from version ${version} to ${version - 1}:`);
|
|
320
356
|
console.log(sql);
|
|
321
357
|
return;
|
|
322
358
|
}
|
|
323
359
|
console.log(`Rolling back pg-boss schema "${schema}" from version ${version} to ${version - 1}...`);
|
|
324
|
-
const sql = migrationStore.rollback(schema, version);
|
|
360
|
+
const sql = migrationStore.rollback(schema, version, migrationStore.getAllForConfig(config), config.noAdvisoryLocks);
|
|
325
361
|
await db.executeSql(sql);
|
|
326
362
|
console.log(`Successfully rolled back pg-boss schema "${schema}" to version ${version - 1}`);
|
|
327
363
|
}
|
|
@@ -489,6 +525,14 @@ async function cmdReindex(args) {
|
|
|
489
525
|
process.exitCode = 1;
|
|
490
526
|
return;
|
|
491
527
|
}
|
|
528
|
+
// A declared backend answers this without a round trip: REINDEX is rejected outright on
|
|
529
|
+
// CockroachDB and unimplemented on YugabyteDB, and neither can measure bloat in the first place.
|
|
530
|
+
if (config.noReindex) {
|
|
531
|
+
console.error(`Reindexing is not supported on the ${config.backend} backend.`);
|
|
532
|
+
console.error('Index maintenance there is handled by the storage engine, and the bloat check cannot run: pg_class.relpages and pg_relation_size() are unavailable or always zero.');
|
|
533
|
+
process.exitCode = 1;
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
492
536
|
const sql = args.force ? plans.getJobIndexes(schema) : plans.getBloatedIndexes(schema);
|
|
493
537
|
let rows;
|
|
494
538
|
try {
|
|
@@ -497,9 +541,8 @@ async function cmdReindex(args) {
|
|
|
497
541
|
catch (err) {
|
|
498
542
|
// The check reads pg_class.relpages and pg_relation_size(). CockroachDB has neither (it
|
|
499
543
|
// rejects `reltuples / relpages` as an unsupported binary operator) and YugabyteDB reports
|
|
500
|
-
// zeroes for every relation.
|
|
501
|
-
//
|
|
502
|
-
// catalog error.
|
|
544
|
+
// zeroes for every relation. --backend catches those up front (above); this stays for a target
|
|
545
|
+
// that was not declared — say what happened instead of surfacing a raw catalog error.
|
|
503
546
|
console.error(`Could not read index statistics from schema "${schema}": ${err.message}`);
|
|
504
547
|
console.error('The bloat check reads pg_class.relpages and pg_relation_size(), which CockroachDB and YugabyteDB do not provide.');
|
|
505
548
|
process.exitCode = 1;
|
|
@@ -569,11 +612,14 @@ async function cmdPlans(args) {
|
|
|
569
612
|
const fileConfig = loadConfigFile(args.config);
|
|
570
613
|
const schema = args.schema || process.env.PGBOSS_SCHEMA || fileConfig.schema || plans.DEFAULT_SCHEMA;
|
|
571
614
|
const subCommand = args.subCommand || 'migrate';
|
|
615
|
+
// A connection is optional here, but the backend is not: printed SQL is meant to be run, so it has
|
|
616
|
+
// to be the SQL that backend accepts.
|
|
617
|
+
const config = resolveBackendConfig({ schema }, args, fileConfig);
|
|
572
618
|
switch (subCommand) {
|
|
573
619
|
case 'create':
|
|
574
620
|
case 'construct':
|
|
575
621
|
console.log('-- SQL to create pg-boss schema:');
|
|
576
|
-
console.log(plans.create(schema, schemaVersion, { createSchema: true }));
|
|
622
|
+
console.log(plans.create(schema, schemaVersion, { ...config, createSchema: true }));
|
|
577
623
|
break;
|
|
578
624
|
case 'migrate': {
|
|
579
625
|
// Inline the async index builds (no BAM worker runs an exported script). A connection
|
|
@@ -600,12 +646,12 @@ async function cmdPlans(args) {
|
|
|
600
646
|
console.log('-- note: no database connection provided; partitioned queue tables were not enumerated.');
|
|
601
647
|
console.log('-- Run with a connection (e.g. --connection-string) to include per-partition index builds.');
|
|
602
648
|
}
|
|
603
|
-
console.log(migrationStore.migrate(schema, 0,
|
|
649
|
+
console.log(migrationStore.migrate(schema, 0, migrationStore.getAllForConfig(config), config.noAdvisoryLocks, { inlineAsync: true, partitionTables }));
|
|
604
650
|
break;
|
|
605
651
|
}
|
|
606
652
|
case 'rollback':
|
|
607
653
|
console.log(`-- SQL to rollback pg-boss from version ${schemaVersion} to ${schemaVersion - 1}:`);
|
|
608
|
-
console.log(migrationStore.rollback(schema, schemaVersion));
|
|
654
|
+
console.log(migrationStore.rollback(schema, schemaVersion, migrationStore.getAllForConfig(config), config.noAdvisoryLocks));
|
|
609
655
|
break;
|
|
610
656
|
default:
|
|
611
657
|
console.error(`Unknown plans subcommand: ${subCommand}`);
|
package/dist/contractor.d.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import type * as types from './types.ts';
|
|
2
2
|
declare class Contractor {
|
|
3
|
-
static constructionPlans(schema?: string, options?:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
static migrationPlans(schema?: string, version?: number, options?: {
|
|
7
|
-
partitionTables?: types.MigrationPartition[];
|
|
8
|
-
}): string;
|
|
9
|
-
static rollbackPlans(schema?: string, version?: number): string;
|
|
3
|
+
static constructionPlans(schema?: string, options?: types.ConstructionPlanOptions): string;
|
|
4
|
+
static migrationPlans(schema?: string, version?: number, options?: types.MigrationPlanOptions): string;
|
|
5
|
+
static rollbackPlans(schema?: string, version?: number, options?: types.PlanOptions): string;
|
|
10
6
|
private config;
|
|
11
7
|
private db;
|
|
12
8
|
private migrations;
|
package/dist/contractor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contractor.d.ts","sourceRoot":"","sources":["../src/contractor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contractor.d.ts","sourceRoot":"","sources":["../src/contractor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AAkBxC,cAAM,UAAU;IACd,MAAM,CAAC,iBAAiB,CAAE,MAAM,SAAuB,EAAE,OAAO,GAAE,KAAK,CAAC,uBAA4B;IAapG,MAAM,CAAC,cAAc,CAAE,MAAM,SAAuB,EAAE,OAAO,SAAoB,EAAE,OAAO,GAAE,KAAK,CAAC,oBAAyB;IAS3H,MAAM,CAAC,aAAa,CAAE,MAAM,SAAuB,EAAE,OAAO,SAAgB,EAAE,OAAO,GAAE,KAAK,CAAC,WAAgB;IAM7G,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,UAAU,CAAmB;gBAExB,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,0BAA0B;IAgBpE,aAAa;IAKb,WAAW;IAKX,KAAK;YAsBG,yBAAyB;IAkCjC,WAAW,IAAK,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC;IA4GhD,KAAK;IAcL,MAAM;IASN,OAAO,CAAE,OAAO,EAAE,MAAM;IASxB,IAAI,CAAE,OAAO,EAAE,MAAM;IAKrB,QAAQ,CAAE,OAAO,EAAE,MAAM;CAIhC;AAED,eAAe,UAAU,CAAA"}
|