@rebasepro/server-postgres 0.24.0 → 0.24.1-canary.g56af4a4
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/{BranchService-BRt78gfa.js → BranchService-Bsn2wfaj.js} +3 -2
- package/dist/{BranchService-BRt78gfa.js.map → BranchService-Bsn2wfaj.js.map} +1 -1
- package/dist/PostgresBackendDriver.d.ts +83 -2
- package/dist/auth/ensure-tables.d.ts +12 -0
- package/dist/auth/services.d.ts +6 -0
- package/dist/backup/backup-cli.d.ts +14 -0
- package/dist/backup/backup-service.d.ts +2 -2
- package/dist/backup/pg-binary.d.ts +5 -0
- package/dist/{backup-cli-DW5p9_zv.js → backup-cli-aUfRv16h.js} +37 -29
- package/dist/backup-cli-aUfRv16h.js.map +1 -0
- package/dist/{backup-service-EwcDVG-8.js → backup-service-BTBB7RXn.js} +55 -21
- package/dist/backup-service-BTBB7RXn.js.map +1 -0
- package/dist/boot-ddl-DVpsFN6r.js +105 -0
- package/dist/boot-ddl-DVpsFN6r.js.map +1 -0
- package/dist/cli-errors-CaI4ntyD.js +1919 -0
- package/dist/cli-errors-CaI4ntyD.js.map +1 -0
- package/dist/cli-errors.d.ts +51 -5
- package/dist/{cli-flags-BglvpjHv.js → cli-flags-B-Dawocl.js} +5 -2
- package/dist/cli-flags-B-Dawocl.js.map +1 -0
- package/dist/cli-helpers.d.ts +40 -0
- package/dist/cli-output.d.ts +16 -28
- package/dist/cli-push.d.ts +87 -0
- package/dist/cli.js +563 -182
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-1-LQD0yI.js → column-plan-helpers-drZIsSNf.js} +10 -4
- package/dist/{column-plan-helpers-1-LQD0yI.js.map → column-plan-helpers-drZIsSNf.js.map} +1 -1
- package/dist/{databasePoolManager-Bj5FbeAs.js → connection-BPoNILnD.js} +19 -110
- package/dist/connection-BPoNILnD.js.map +1 -0
- package/dist/connection.d.ts +9 -1
- package/dist/databasePoolManager-BkEy2w18.js +119 -0
- package/dist/databasePoolManager-BkEy2w18.js.map +1 -0
- package/dist/{ensure-collection-tables-kkkHk8oo.js → deferred-index-build-DP1_SNFo.js} +484 -346
- package/dist/deferred-index-build-DP1_SNFo.js.map +1 -0
- package/dist/{doctor-C-sYWbmt.js → doctor-C4WiftHm.js} +63 -52
- package/dist/doctor-C4WiftHm.js.map +1 -0
- package/dist/ensure-collection-policies-CXpbeNag.js +541 -0
- package/dist/ensure-collection-policies-CXpbeNag.js.map +1 -0
- package/dist/ensure-collection-tables-BhsU6qUE.js +532 -0
- package/dist/ensure-collection-tables-BhsU6qUE.js.map +1 -0
- package/dist/{ensure-tables-BmI_tRxc.js → ensure-tables-CHsgyLqg.js} +172 -76
- package/dist/ensure-tables-CHsgyLqg.js.map +1 -0
- package/dist/enum-labels-kept-D8fI1MoX.js +56 -0
- package/dist/enum-labels-kept-D8fI1MoX.js.map +1 -0
- package/dist/generate-drizzle-schema-Bz52jH2h.js +57 -0
- package/dist/generate-drizzle-schema-Bz52jH2h.js.map +1 -0
- package/dist/{generate-drizzle-schema-logic-sSFDp6LR.js → generate-drizzle-schema-logic-B74WN37S.js} +5 -5
- package/dist/generate-drizzle-schema-logic-B74WN37S.js.map +1 -0
- package/dist/generate-files-CdEckX6c.js +199 -0
- package/dist/generate-files-CdEckX6c.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-BJsLaVNX.js → generate-postgres-ddl-logic-B64O2oZF.js} +3 -3
- package/dist/{generate-postgres-ddl-logic-BJsLaVNX.js.map → generate-postgres-ddl-logic-B64O2oZF.js.map} +1 -1
- package/dist/generated-sql.d.ts +8 -2
- package/dist/history/HistoryService.d.ts +38 -6
- package/dist/history/ensure-history-table.d.ts +42 -4
- package/dist/index.es.js +2922 -7475
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-kCETE8TY.js → introspect-db-logic-C-Di8dA9.js} +44 -6
- package/dist/introspect-db-logic-C-Di8dA9.js.map +1 -0
- package/dist/{introspect-db-queries-C_Q5VgQw.js → introspect-db-queries-D6kAqReI.js} +23 -1
- package/dist/introspect-db-queries-D6kAqReI.js.map +1 -0
- package/dist/pg-error-utils-vJezlXdI.js +388 -0
- package/dist/pg-error-utils-vJezlXdI.js.map +1 -0
- package/dist/{plan-schema-DU9exq6C.js → plan-schema-B1YF_tYq.js} +151 -41
- package/dist/plan-schema-B1YF_tYq.js.map +1 -0
- package/dist/{policy-drift-B-J2hhm0.js → policy-drift-Dm8i-s1J.js} +23 -6
- package/dist/policy-drift-Dm8i-s1J.js.map +1 -0
- package/dist/push-index-prebuild-Db4AQF4i.js +45 -0
- package/dist/push-index-prebuild-Db4AQF4i.js.map +1 -0
- package/dist/push-legacy-renames-BK6ilQq2.js +34 -0
- package/dist/push-legacy-renames-BK6ilQq2.js.map +1 -0
- package/dist/{render-ddl-Ds2t_d9V.js → render-ddl-CtC3y9Ol.js} +8 -4
- package/dist/render-ddl-CtC3y9Ol.js.map +1 -0
- package/dist/{rls-bootstrap-sql-_KNnjanK.js → rls-bootstrap-sql-RQUljnJ1.js} +125 -1270
- package/dist/rls-bootstrap-sql-RQUljnJ1.js.map +1 -0
- package/dist/{rls-enforcement-CfXOJJaW.js → rls-enforcement-DvzqlaL1.js} +129 -17
- package/dist/rls-enforcement-DvzqlaL1.js.map +1 -0
- package/dist/schema/auth-schema.d.ts +2 -2
- package/dist/schema/boot-ddl.d.ts +47 -0
- package/dist/schema/collection-index.d.ts +45 -0
- package/dist/schema/column-plan-helpers.d.ts +5 -0
- package/dist/schema/deferred-index-build.d.ts +131 -0
- package/dist/schema/destructive-sql.d.ts +7 -0
- package/dist/schema/doctor-cli.js +110 -6
- package/dist/schema/doctor-cli.js.map +1 -1
- package/dist/schema/doctor-search-scale.d.ts +45 -0
- package/dist/schema/doctor.d.ts +20 -1
- package/dist/schema/ensure-collection-policies.d.ts +73 -35
- package/dist/schema/ensure-collection-tables.d.ts +56 -3
- package/dist/schema/enum-labels-kept.d.ts +26 -0
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-files.d.ts +44 -0
- package/dist/schema/generate-next-step.d.ts +0 -13
- package/dist/schema/generate-postgres-ddl.js +10 -81
- package/dist/schema/generate-postgres-ddl.js.map +1 -1
- package/dist/schema/introspect-db-logic.d.ts +12 -0
- package/dist/schema/introspect-db-queries.d.ts +11 -1
- package/dist/schema/introspect-db-structure.d.ts +10 -0
- package/dist/schema/introspect-db.js +4 -5
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/migration-post-process.d.ts +34 -0
- package/dist/schema/plan/diff-plan.d.ts +126 -0
- package/dist/schema/plan/types.d.ts +8 -0
- package/dist/schema/push-index-prebuild.d.ts +25 -0
- package/dist/schema/push-legacy-renames.d.ts +20 -0
- package/dist/schema/rls-bootstrap-sql.d.ts +15 -0
- package/dist/security/rls-enforcement.d.ts +32 -4
- package/dist/services/FetchService.d.ts +58 -8
- package/dist/services/PersistService.d.ts +15 -0
- package/dist/services/RelationService.d.ts +88 -3
- package/dist/services/cdc/CdcListener.d.ts +17 -6
- package/dist/services/cdc/trigger-cdc.d.ts +151 -27
- package/dist/services/channel-history.d.ts +15 -4
- package/dist/services/collection-helpers.d.ts +15 -0
- package/dist/services/field-viewer.d.ts +15 -1
- package/dist/services/pg-notify-listener.d.ts +7 -0
- package/dist/services/realtimeService.d.ts +314 -14
- package/dist/services/row-pipeline.d.ts +16 -9
- package/dist/services/socket-liveness.d.ts +42 -2
- package/dist/services/sql-script.d.ts +207 -3
- package/dist/services/vector-iterative-scan.d.ts +40 -0
- package/dist/services/write-depth.d.ts +6 -0
- package/dist/services/write-transaction-scope.d.ts +9 -0
- package/dist/socket-liveness-D3UD5_BW.js +7570 -0
- package/dist/socket-liveness-D3UD5_BW.js.map +1 -0
- package/dist/socket-opt-ins.d.ts +64 -0
- package/dist/source-DBDbBTL4.js +811 -0
- package/dist/source-DBDbBTL4.js.map +1 -0
- package/dist/tsx-loader.d.ts +14 -0
- package/dist/utils/drizzle-conditions.d.ts +21 -1
- package/dist/utils/error-cause.d.ts +7 -0
- package/dist/utils/pg-error-utils.d.ts +2 -4
- package/dist/utils/result-rows.d.ts +10 -0
- package/dist/websocket.d.ts +13 -0
- package/package.json +7 -7
- package/dist/backup-cli-DW5p9_zv.js.map +0 -1
- package/dist/backup-service-EwcDVG-8.js.map +0 -1
- package/dist/cli-errors-DsA-K9uP.js +0 -893
- package/dist/cli-errors-DsA-K9uP.js.map +0 -1
- package/dist/cli-flags-BglvpjHv.js.map +0 -1
- package/dist/cli-output-CNdMql-L.js +0 -47
- package/dist/cli-output-CNdMql-L.js.map +0 -1
- package/dist/collection-index-DdnsxiJ_.js +0 -286
- package/dist/collection-index-DdnsxiJ_.js.map +0 -1
- package/dist/databasePoolManager-Bj5FbeAs.js.map +0 -1
- package/dist/doctor-C-sYWbmt.js.map +0 -1
- package/dist/ensure-collection-policies-B1ureSIV.js +0 -160
- package/dist/ensure-collection-policies-B1ureSIV.js.map +0 -1
- package/dist/ensure-collection-tables-kkkHk8oo.js.map +0 -1
- package/dist/ensure-tables-BmI_tRxc.js.map +0 -1
- package/dist/generate-drizzle-schema-93M0lUxK.js +0 -123
- package/dist/generate-drizzle-schema-93M0lUxK.js.map +0 -1
- package/dist/generate-drizzle-schema-logic-sSFDp6LR.js.map +0 -1
- package/dist/introspect-db-logic-kCETE8TY.js.map +0 -1
- package/dist/introspect-db-queries-C_Q5VgQw.js.map +0 -1
- package/dist/plan-schema-DU9exq6C.js.map +0 -1
- package/dist/policy-drift-B-J2hhm0.js.map +0 -1
- package/dist/render-ddl-Ds2t_d9V.js.map +0 -1
- package/dist/rls-bootstrap-sql-_KNnjanK.js.map +0 -1
- package/dist/rls-enforcement-CfXOJJaW.js.map +0 -1
- package/dist/source-Br7L7GOI.js +0 -410
- package/dist/source-Br7L7GOI.js.map +0 -1
- package/dist/utils/sql-redaction.d.ts +0 -21
|
@@ -81,7 +81,83 @@ export interface ExistingSchema {
|
|
|
81
81
|
columnTypes?: Map<string, string>;
|
|
82
82
|
/** `schema.table.trigger` of every trigger that already exists. */
|
|
83
83
|
triggers?: Set<string>;
|
|
84
|
+
/**
|
|
85
|
+
* `schema.name` of every index (any relation of kind `i`/`I`) that already
|
|
86
|
+
* exists. Absent means unknown, and every index is planned as before —
|
|
87
|
+
* `IF NOT EXISTS` makes that correct, just not free.
|
|
88
|
+
*/
|
|
89
|
+
indexes?: Set<string>;
|
|
90
|
+
/**
|
|
91
|
+
* `schema.name` of every index whose `pg_index.indisvalid` is false — the
|
|
92
|
+
* husk a `CREATE INDEX CONCURRENTLY` leaves behind when it is cancelled,
|
|
93
|
+
* times out, or loses a race with a peer. Such an index is in
|
|
94
|
+
* {@link indexes} too, maintained on every write and used by no query, and
|
|
95
|
+
* `IF NOT EXISTS` skips it by name forever. Absent means unknown.
|
|
96
|
+
*/
|
|
97
|
+
invalidIndexes?: Set<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Names of the extensions installed in the database. `CREATE EXTENSION IF
|
|
100
|
+
* NOT EXISTS` skips by name whatever schema the extension is in, so the name
|
|
101
|
+
* is the whole test. Absent means unknown.
|
|
102
|
+
*/
|
|
103
|
+
extensions?: Set<string>;
|
|
104
|
+
/**
|
|
105
|
+
* {@link HELPER_FUNCTIONS} as they are in the catalogue: `schema.name(args)`
|
|
106
|
+
* → {@link describeHelperFunction} of what `CREATE OR REPLACE` would set.
|
|
107
|
+
* Absent means unknown, and every helper is re-created as before.
|
|
108
|
+
*/
|
|
109
|
+
functions?: Map<string, string>;
|
|
84
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The functions the plan creates with `CREATE OR REPLACE`, which the catalogue
|
|
113
|
+
* reader looks up so a boot that finds them unchanged plans nothing.
|
|
114
|
+
*
|
|
115
|
+
* A helper missing from this list is not wrong, only re-created on every boot,
|
|
116
|
+
* which is what all of them were before.
|
|
117
|
+
*/
|
|
118
|
+
export declare const HELPER_FUNCTIONS: readonly string[];
|
|
119
|
+
/**
|
|
120
|
+
* Everything `CREATE OR REPLACE FUNCTION` sets on a function, as one string.
|
|
121
|
+
*
|
|
122
|
+
* Built from the catalogue by the reader and from the statement by
|
|
123
|
+
* {@link parseHelperFunction}, so the two meet in one shape. It covers every
|
|
124
|
+
* attribute a `CREATE OR REPLACE` resets — body, language, volatility,
|
|
125
|
+
* strictness, parallel safety, security, leakproofness, cost and `SET` options —
|
|
126
|
+
* so a function someone altered by hand (`ALTER FUNCTION … VOLATILE`) still
|
|
127
|
+
* compares unequal and is re-created, exactly as the unconditional statement
|
|
128
|
+
* used to re-create it.
|
|
129
|
+
*/
|
|
130
|
+
export interface HelperFunctionFacts {
|
|
131
|
+
returns: string;
|
|
132
|
+
language: string;
|
|
133
|
+
/** `provolatile`: `i`mmutable, `s`table, `v`olatile. */
|
|
134
|
+
volatility: string;
|
|
135
|
+
strict: boolean;
|
|
136
|
+
/** `proparallel`: `s`afe, `r`estricted, `u`nsafe. */
|
|
137
|
+
parallel: string;
|
|
138
|
+
securityDefiner: boolean;
|
|
139
|
+
leakproof: boolean;
|
|
140
|
+
cost: number;
|
|
141
|
+
/** Whether the function carries any `SET` options (`proconfig`). */
|
|
142
|
+
hasConfig: boolean;
|
|
143
|
+
/** `prosrc`: the text between the dollar quotes, exactly. */
|
|
144
|
+
body: string;
|
|
145
|
+
}
|
|
146
|
+
export declare const helperFunctionKey: (schema: string, name: string, args: string) => string;
|
|
147
|
+
export declare const describeHelperFunction: (facts: HelperFunctionFacts) => string;
|
|
148
|
+
/**
|
|
149
|
+
* What a helper's `CREATE OR REPLACE FUNCTION` statement would leave in the
|
|
150
|
+
* catalogue, or `undefined` for a statement this does not recognise — which is
|
|
151
|
+
* then planned unconditionally, as every statement was before.
|
|
152
|
+
*
|
|
153
|
+
* Only ever given statements this package renders (`searchHelperFunctions`,
|
|
154
|
+
* `setUpdatedAtFunction`), so a strict pattern is the right tool: anything it
|
|
155
|
+
* does not match exactly falls back to the safe behaviour.
|
|
156
|
+
*/
|
|
157
|
+
export declare function parseHelperFunction(statement: string): {
|
|
158
|
+
key: string;
|
|
159
|
+
definition: string;
|
|
160
|
+
} | undefined;
|
|
85
161
|
/**
|
|
86
162
|
* How far the planner may go in making the database's constraints match the
|
|
87
163
|
* configuration.
|
|
@@ -100,6 +176,18 @@ export type ConstraintPolicy = "additive" | "converge";
|
|
|
100
176
|
export interface DiffOptions {
|
|
101
177
|
/** Defaults to `additive`. See {@link ConstraintPolicy}. */
|
|
102
178
|
constraints?: ConstraintPolicy;
|
|
179
|
+
/**
|
|
180
|
+
* Plan every index a table that already exists needs — declared, search
|
|
181
|
+
* and vector, missing or left INVALID under its name — as a
|
|
182
|
+
* {@link DeferredIndexBuild} rather than an action, as the derived
|
|
183
|
+
* foreign-key indexes always are. `ensureCollectionTables` sets it: it
|
|
184
|
+
* hands them to `buildDeferredIndexes`, which builds them on a connection
|
|
185
|
+
* of its own with no `statement_timeout`, and drops and rebuilds a husk.
|
|
186
|
+
*
|
|
187
|
+
* Absent — the live schema editor, which applies `statements` as text —
|
|
188
|
+
* they stay `CREATE INDEX CONCURRENTLY IF NOT EXISTS` actions, as before.
|
|
189
|
+
*/
|
|
190
|
+
deferLiveIndexes?: boolean;
|
|
103
191
|
}
|
|
104
192
|
export interface EnsureAction {
|
|
105
193
|
kind: "create-enum" | "create-table" | "enable-rls" | "add-column" | "add-constraint" | "rename-column" | "create-extension" | "create-function" | "create-index" | "comment-column" | "add-enum-value" | "set-not-null" | "drop-not-null" | "set-default" | "create-trigger";
|
|
@@ -166,6 +254,42 @@ export interface LegacyForeignKey {
|
|
|
166
254
|
/** The name the old rule derived, which the table already has. */
|
|
167
255
|
legacy: string;
|
|
168
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* An index a table that already exists is missing, or holds INVALID — a
|
|
259
|
+
* derived foreign-key index always, and under
|
|
260
|
+
* {@link DiffOptions.deferLiveIndexes} a declared, search or vector one too.
|
|
261
|
+
*
|
|
262
|
+
* Not an {@link EnsureAction}, because it must not hold the boot: on a table
|
|
263
|
+
* with millions of rows `CREATE INDEX CONCURRENTLY` takes minutes, and every
|
|
264
|
+
* minute of it would be a minute the deployment is not serving. The caller
|
|
265
|
+
* builds these after the fact — boot in the background once it serves, every
|
|
266
|
+
* other caller inline after the actions — through `buildDeferredIndexes`. A
|
|
267
|
+
* {@link unique} one is the exception: it is built before the ensure returns.
|
|
268
|
+
*
|
|
269
|
+
* Kept out of {@link EnsurePlan.statements} for the same reason: a caller that
|
|
270
|
+
* applies statements as text (the live schema editor) may run them inside a
|
|
271
|
+
* transaction, which `CONCURRENTLY` refuses.
|
|
272
|
+
*/
|
|
273
|
+
export interface DeferredIndexBuild {
|
|
274
|
+
schema: string;
|
|
275
|
+
/** Bare table name. */
|
|
276
|
+
table: string;
|
|
277
|
+
indexName: string;
|
|
278
|
+
/** `CREATE [UNIQUE] INDEX CONCURRENTLY IF NOT EXISTS …;` */
|
|
279
|
+
sql: string;
|
|
280
|
+
/**
|
|
281
|
+
* The catalogue already holds an INVALID index under this name, which has
|
|
282
|
+
* to be dropped before the build — `IF NOT EXISTS` would skip it.
|
|
283
|
+
*/
|
|
284
|
+
invalid: boolean;
|
|
285
|
+
/**
|
|
286
|
+
* A declared `unique: true` index. Not an optimization but the collection
|
|
287
|
+
* promising two rows cannot share a value, so `ensureCollectionTables`
|
|
288
|
+
* builds it before it returns — never after the server is serving — and
|
|
289
|
+
* fails when it cannot, as it did when this was an action. Absent is false.
|
|
290
|
+
*/
|
|
291
|
+
unique?: boolean;
|
|
292
|
+
}
|
|
169
293
|
export interface EnsurePlan {
|
|
170
294
|
actions: EnsureAction[];
|
|
171
295
|
/** Every statement, in dependency order. Empty when the schema is current. */
|
|
@@ -217,5 +341,7 @@ export interface EnsurePlan {
|
|
|
217
341
|
* exactly once, which is not a decision to take unattended.
|
|
218
342
|
*/
|
|
219
343
|
orphanedRequiredColumns: OrphanedRequiredColumn[];
|
|
344
|
+
/** @see DeferredIndexBuild */
|
|
345
|
+
deferredIndexes: DeferredIndexBuild[];
|
|
220
346
|
}
|
|
221
347
|
export declare function diffPlanAgainstCatalogue(plan: SchemaPlan, existing: ExistingSchema, options?: DiffOptions): EnsurePlan;
|
|
@@ -280,6 +280,14 @@ export interface PolicyPlan {
|
|
|
280
280
|
* what `securityRules` reads like — `policies.sql` says so in a comment.
|
|
281
281
|
*/
|
|
282
282
|
injected: boolean;
|
|
283
|
+
/**
|
|
284
|
+
* Policy names, as Postgres stored them, that this policy replaces — set
|
|
285
|
+
* only on the first policy of a rule whose per-operation names used to
|
|
286
|
+
* collide at 63 bytes (see `getPolicySupersededNames`). Rendered as a
|
|
287
|
+
* `DROP POLICY IF EXISTS` ahead of the policy, so a boot or push swaps the
|
|
288
|
+
* one truncated survivor for a policy per operation. Absent otherwise.
|
|
289
|
+
*/
|
|
290
|
+
supersedes?: string[];
|
|
283
291
|
}
|
|
284
292
|
/** A `BEFORE UPDATE` trigger stamping `now()` onto an `on_update` column. */
|
|
285
293
|
export interface TriggerPlan {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The foreign-key indexes `rebase db push` builds before Atlas plans.
|
|
3
|
+
*
|
|
4
|
+
* Rebase derives a btree for every `belongsTo` key and every junction's target
|
|
5
|
+
* column (15b00afcd). On a table that already exists — every table of a
|
|
6
|
+
* project upgraded across that release — the index is missing, and a push left
|
|
7
|
+
* it to Atlas, which builds it inside the push's one transaction: a plain
|
|
8
|
+
* `CREATE INDEX`, holding writes to the table for the whole build, about 0.4 s
|
|
9
|
+
* at a million rows and 6 s at ten million. Atlas's own concurrent mode would
|
|
10
|
+
* run the entire apply outside a transaction, which the push relies on.
|
|
11
|
+
*
|
|
12
|
+
* So the push builds those first, `CONCURRENTLY`, with the builder the boot
|
|
13
|
+
* uses for the same indexes (`deferred-index-build.ts`: one at a time, a lock
|
|
14
|
+
* budget for getting the table, an INVALID leftover dropped and rebuilt, a
|
|
15
|
+
* claim shared with booting instances). Atlas then finds them and plans
|
|
16
|
+
* nothing for them. One it could not build is still Atlas's, as before.
|
|
17
|
+
*
|
|
18
|
+
* Only an index whose columns are already there: a column this push adds is
|
|
19
|
+
* created by Atlas, and its index with it, on a column that holds no values.
|
|
20
|
+
*/
|
|
21
|
+
import { type CollectionConfig } from "@rebasepro/types";
|
|
22
|
+
import { type DeferredIndexBuild } from "./plan/diff-plan.js";
|
|
23
|
+
import { type Queryable } from "./ensure-collection-tables.js";
|
|
24
|
+
/** The derived foreign-key indexes a live table lacks (or holds INVALID) whose columns it already has. */
|
|
25
|
+
export declare function missingForeignKeyIndexes(client: Queryable, collections: CollectionConfig[]): Promise<DeferredIndexBuild[]>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The relation columns an aged database still holds under the name Rebase
|
|
3
|
+
* derived before 0.13 (`categorie_id`, `people_id`), for `db push` to rename
|
|
4
|
+
* before Atlas plans.
|
|
5
|
+
*
|
|
6
|
+
* Boot renames them losslessly (`diff-plan.ts`, `renameLegacyColumn`). Push
|
|
7
|
+
* runs Atlas, which sees only "drop `categorie_id`, add `category_id` NOT
|
|
8
|
+
* NULL": it listed the links as destructive changes to approve, and with
|
|
9
|
+
* `--allow-destructive` dropped them. A pipeline that runs only `db push`
|
|
10
|
+
* could not upgrade an aged database at all. Renaming first — the same
|
|
11
|
+
* metadata-only statement boot runs — leaves Atlas nothing to plan.
|
|
12
|
+
*/
|
|
13
|
+
import { type CollectionConfig } from "@rebasepro/types";
|
|
14
|
+
import { type LegacyForeignKey } from "./plan/diff-plan.js";
|
|
15
|
+
import { type Queryable } from "./ensure-collection-tables.js";
|
|
16
|
+
export interface LegacyRename extends LegacyForeignKey {
|
|
17
|
+
/** `ALTER TABLE … RENAME COLUMN …;` */
|
|
18
|
+
sql: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function legacyForeignKeyRenames(client: Queryable, collections: CollectionConfig[]): Promise<LegacyRename[]>;
|
|
@@ -45,6 +45,21 @@
|
|
|
45
45
|
* a parser for a problem that does not need one.
|
|
46
46
|
*/
|
|
47
47
|
export declare const RLS_BOOTSTRAP_STATEMENTS: readonly string[];
|
|
48
|
+
/**
|
|
49
|
+
* Whether the `rebase` schema and every RLS helper function are exactly what
|
|
50
|
+
* {@link RLS_BOOTSTRAP_STATEMENTS} would make them — one catalogue read.
|
|
51
|
+
*
|
|
52
|
+
* The boot ran those statements unconditionally, on every boot and on every
|
|
53
|
+
* source: four `CREATE OR REPLACE FUNCTION`s in a transaction under an
|
|
54
|
+
* advisory lock. Replacing a function rewrites its `pg_proc` row even when
|
|
55
|
+
* nothing changed, and every cached plan that inlined it — every plan of every
|
|
56
|
+
* statement a policy applies to, on every connection — is invalidated by it.
|
|
57
|
+
* A function that differs in anything `CREATE OR REPLACE` sets (body,
|
|
58
|
+
* language, volatility, strictness, parallel safety, security, leakproofness,
|
|
59
|
+
* cost, `SET` options), or is missing, answers `false`, and the statements run
|
|
60
|
+
* as before.
|
|
61
|
+
*/
|
|
62
|
+
export declare function rlsRuntimeIsCurrent(run: (sql: string) => Promise<Record<string, unknown>[]>): Promise<boolean>;
|
|
48
63
|
/** The same statements as one script, for migration files and raw clients. */
|
|
49
64
|
export declare const RLS_BOOTSTRAP_SQL: string;
|
|
50
65
|
/**
|
|
@@ -31,8 +31,9 @@ import { REBASE_USER_ROLE } from "@rebasepro/common";
|
|
|
31
31
|
* SELECT/INSERT/UPDATE/DELETE grants (+ default privileges so future
|
|
32
32
|
* tables stay covered).
|
|
33
33
|
* 3. {@link applyAuthContext} — per-transaction: set the `app.*` GUCs the
|
|
34
|
-
* policies read (`rebase.uid()` etc.) and
|
|
35
|
-
* RLS binds. Transaction-scoped, so it
|
|
34
|
+
* policies read (`rebase.uid()` etc.) and the transaction-local role
|
|
35
|
+
* `rebase_user` so RLS binds, in one statement. Transaction-scoped, so it
|
|
36
|
+
* composes with poolers.
|
|
36
37
|
*
|
|
37
38
|
* Provisioning runs from the framework's own bootstrap/migrate (which already
|
|
38
39
|
* self-creates the `auth` schema and functions) — enforcement is default-on,
|
|
@@ -203,11 +204,38 @@ export declare function appRoleSetupInstructions(connectionRole: string, schemas
|
|
|
203
204
|
* connection cannot create it.
|
|
204
205
|
*/
|
|
205
206
|
export declare function ensureAppRole(run: RawSqlRunner, schemas: string[]): Promise<void>;
|
|
207
|
+
/**
|
|
208
|
+
* The grant tier of {@link ensureAppRole} on its own: `USAGE` on each existing
|
|
209
|
+
* schema in `schemas`, DML on its tables and `USAGE, SELECT` on its
|
|
210
|
+
* sequences, the default privileges that cover what this role creates later,
|
|
211
|
+
* and Rebase's own tables taken back — for a role and membership already
|
|
212
|
+
* provisioned.
|
|
213
|
+
*
|
|
214
|
+
* Every statement is issued only when the catalogue says it would change
|
|
215
|
+
* something. They used to run unconditionally, twice per boot (at connect, and
|
|
216
|
+
* again in the final posture check): a `GRANT … ON ALL TABLES` rewrites every
|
|
217
|
+
* table's `pg_class` row whether or not the ACL changes, which invalidates
|
|
218
|
+
* every table's cached plans on every connection, and the internal-table sweep
|
|
219
|
+
* ran 23 `REVOKE`s per schema. A boot against a database whose grants already
|
|
220
|
+
* hold now reads one catalogue query and writes nothing; one that is missing
|
|
221
|
+
* any of them runs exactly the statements it always ran.
|
|
222
|
+
*
|
|
223
|
+
* Table owners (the expected non-superuser posture) can always grant on their
|
|
224
|
+
* own objects, so this needs no extra privilege.
|
|
225
|
+
*/
|
|
226
|
+
export declare function ensureAppRoleGrants(run: RawSqlRunner, schemas: string[]): Promise<void>;
|
|
227
|
+
/**
|
|
228
|
+
* Rebase's own RLS-off tables in `schema` that a `revokeInternalTableAccess`
|
|
229
|
+
* sweep would change — the user role holds a privilege on them, or their ACL
|
|
230
|
+
* has never been written. One read; the sweep it narrows is one `DO` per
|
|
231
|
+
* table name.
|
|
232
|
+
*/
|
|
233
|
+
export declare function internalTablesGrantedToUserRole(run: RawSqlRunner, schema: string): Promise<string[]>;
|
|
206
234
|
/**
|
|
207
235
|
* Apply the authenticated context to a transaction: the `app.*` GUCs that RLS
|
|
208
236
|
* policies read via `rebase.uid()` / `rebase.roles()` / `rebase.jwt()`, and — when
|
|
209
|
-
* `userRole` is set — `
|
|
210
|
-
* transaction (reads *and* writes).
|
|
237
|
+
* `userRole` is set — the `LOCAL` role switch, in the same statement, so RLS
|
|
238
|
+
* binds every statement in this transaction (reads *and* writes).
|
|
211
239
|
*
|
|
212
240
|
* GUCs are set with `is_local = true` and the role switch is `LOCAL`: both
|
|
213
241
|
* reset at commit/rollback, so pooled connections are never polluted.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SQL } from "drizzle-orm";
|
|
2
|
-
import { PgTable } from "drizzle-orm/pg-core";
|
|
2
|
+
import { AnyPgColumn, PgTable } from "drizzle-orm/pg-core";
|
|
3
3
|
import { FilterValues, OrderByTuple, LogicalCondition } from "@rebasepro/types";
|
|
4
4
|
import type { IncludeSpec, VectorSearchParams } from "@rebasepro/types";
|
|
5
5
|
import { RelationService } from "./RelationService.js";
|
|
@@ -218,6 +218,18 @@ export declare class FetchService {
|
|
|
218
218
|
* no related row, so a typo in an `include` looked like empty data.
|
|
219
219
|
*/
|
|
220
220
|
private resolveIncludeLevel;
|
|
221
|
+
/**
|
|
222
|
+
* Resolve every level of an include tree against the collections, without
|
|
223
|
+
* loading anything — the 400 for an unknown relation, decided before the
|
|
224
|
+
* read rather than by it.
|
|
225
|
+
*
|
|
226
|
+
* {@link loadIncludes} resolves a level only once it has rows to load it
|
|
227
|
+
* onto. On its own that made the refusal depend on the data: a typo in
|
|
228
|
+
* `include` against an empty table, under a filter that matched nothing,
|
|
229
|
+
* or below a relation that loaded no rows answered 200, and the same
|
|
230
|
+
* request became a 400 the first time a row matched.
|
|
231
|
+
*/
|
|
232
|
+
private assertIncludeResolvable;
|
|
221
233
|
/**
|
|
222
234
|
* The extra `WHERE` an include's own `where`/`logical` puts on the target.
|
|
223
235
|
*
|
|
@@ -227,14 +239,25 @@ export declare class FetchService {
|
|
|
227
239
|
*/
|
|
228
240
|
private includeNarrowing;
|
|
229
241
|
/**
|
|
230
|
-
*
|
|
242
|
+
* Refuse a `count` the loader cannot serve honestly, before anything runs.
|
|
231
243
|
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
|
|
244
|
+
* - On a to-one relation: there is nothing to count past a row or none,
|
|
245
|
+
* and answering 0 or 1 under `_count` would invent a shape for it.
|
|
246
|
+
* - On a collection with a property named `_count`: the total would be
|
|
247
|
+
* written over that column's value. A silent overwrite answers 200 with
|
|
248
|
+
* the column gone, so it is a 400 naming why.
|
|
249
|
+
*/
|
|
250
|
+
private assertCountsAllowed;
|
|
251
|
+
/**
|
|
252
|
+
* Cap and project the rows of ONE relation, per parent.
|
|
253
|
+
*
|
|
254
|
+
* The order and the limit are Postgres's: the batch loader ranks each
|
|
255
|
+
* parent's related rows by the include's `orderBy` and reads back only the
|
|
256
|
+
* ones within its `limit` (see
|
|
257
|
+
* {@link RelationService.batchFetchRelatedEntitiesMany}), so `rows`
|
|
258
|
+
* arrives sorted and already cut. The slice here is what holds the limit
|
|
259
|
+
* where a `via` chain reaches one target along two paths — those rows share
|
|
260
|
+
* a rank — and it costs nothing where nothing is left to cut.
|
|
238
261
|
*/
|
|
239
262
|
private shapeRelatedRows;
|
|
240
263
|
/**
|
|
@@ -646,6 +669,33 @@ export declare class FetchService {
|
|
|
646
669
|
/** See `FetchCollectionProps.withDeleted`. */
|
|
647
670
|
withDeleted?: WithDeleted;
|
|
648
671
|
}): Promise<Record<string, unknown> | null>;
|
|
672
|
+
/**
|
|
673
|
+
* {@link fetchOneForRest}'s read of a row an INSERT just wrote, taken from
|
|
674
|
+
* the INSERT's own `RETURNING` instead of a second statement — or
|
|
675
|
+
* `undefined` when the two could answer differently.
|
|
676
|
+
*
|
|
677
|
+
* A write answers with `fetchOneForRest(slug, id, undefined, …, { withDeleted: true })`
|
|
678
|
+
* (see `PersistService.save`): the row's visible columns, rendered by
|
|
679
|
+
* `toRestRow`. For a row the statement itself wrote, with nothing written
|
|
680
|
+
* after it, `RETURNING` those same columns is that row — the same values
|
|
681
|
+
* through the same column decoders, in the same order, rendered the same
|
|
682
|
+
* way. Two things narrow the read and not the statement, and either one
|
|
683
|
+
* keeps the read:
|
|
684
|
+
*
|
|
685
|
+
* - a `beforeQuery` adds conditions to the read (and runs application
|
|
686
|
+
* code): a row outside them is no answer today, and would be one from
|
|
687
|
+
* `RETURNING`;
|
|
688
|
+
* - per-field read access, which projects the read for the viewer.
|
|
689
|
+
*
|
|
690
|
+
* Row-level security is the same either way. `INSERT … RETURNING` holds
|
|
691
|
+
* the new row to the table's SELECT policies and refuses one they hide
|
|
692
|
+
* (42501) — which the INSERT's `RETURNING <key>` already did, so returning
|
|
693
|
+
* more columns changes nothing about who may write or read what.
|
|
694
|
+
*/
|
|
695
|
+
insertReturning(collectionPath: string): {
|
|
696
|
+
columns: Record<string, AnyPgColumn>;
|
|
697
|
+
render: (returned: Record<string, unknown>) => Record<string, unknown>;
|
|
698
|
+
} | undefined;
|
|
649
699
|
/**
|
|
650
700
|
* Fetch raw rows without any relation processing (for REST fast path)
|
|
651
701
|
*/
|
|
@@ -75,6 +75,21 @@ export declare class PersistService {
|
|
|
75
75
|
* for why a zero-row write cannot be reported as success.
|
|
76
76
|
*/
|
|
77
77
|
private explainZeroRowWrite;
|
|
78
|
+
/**
|
|
79
|
+
* Lock one row against every other write to it until this transaction
|
|
80
|
+
* ends: `FOR NO KEY UPDATE`, the lock an UPDATE of it takes anyway.
|
|
81
|
+
*
|
|
82
|
+
* For a conditional write (`If-Match`): taken before the row is read and
|
|
83
|
+
* compared, so a write that lands meanwhile is either already committed and
|
|
84
|
+
* read, or waits for this one. Without it two writes carrying the same tag
|
|
85
|
+
* both read the old row, both passed, and the second overwrote the first.
|
|
86
|
+
*
|
|
87
|
+
* Under row-level security the lock reads through the caller's UPDATE
|
|
88
|
+
* policies as well as their SELECT ones, so a row they may read but not
|
|
89
|
+
* update is simply not locked — and their write is refused by the database
|
|
90
|
+
* all the same. A nested address locks the row it ends in.
|
|
91
|
+
*/
|
|
92
|
+
lockRow(collectionPath: string, id: string | number): Promise<void>;
|
|
78
93
|
/**
|
|
79
94
|
* Delete an row by ID
|
|
80
95
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SQL } from "drizzle-orm";
|
|
2
|
-
import { PgTable } from "drizzle-orm/pg-core";
|
|
2
|
+
import { AnyPgColumn, PgTable } from "drizzle-orm/pg-core";
|
|
3
3
|
import { DrizzleClient } from "../interfaces.js";
|
|
4
4
|
import { CollectionConfig, FilterValues, OrderByTuple, ResolvedRelation, ResolvedHasMany, ResolvedHasOne } from "@rebasepro/types";
|
|
5
5
|
import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry.js";
|
|
@@ -34,6 +34,52 @@ export declare function joinStepCondition(currentTable: PgTable, joinTable: PgTa
|
|
|
34
34
|
} | {
|
|
35
35
|
missing: string;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* What an include asks of ONE to-many relation's rows, per parent: how many,
|
|
39
|
+
* and in what order. See {@link RelationService.batchFetchRelatedEntitiesMany}.
|
|
40
|
+
*/
|
|
41
|
+
export interface RelatedRowWindow {
|
|
42
|
+
/** Rows to keep per parent. */
|
|
43
|
+
limit?: number;
|
|
44
|
+
/** Their order, by the target's own columns. */
|
|
45
|
+
orderBy?: OrderByTuple[];
|
|
46
|
+
/**
|
|
47
|
+
* Filled, when given and a `limit` is set, with each parent's number of
|
|
48
|
+
* matching related rows *before* the limit — what `include: { x: { limit,
|
|
49
|
+
* count: true } }` serves under `_count`. Counted in the ranking subquery,
|
|
50
|
+
* so it is the same statement, the same conditions and the same RLS
|
|
51
|
+
* context as the rows. A parent with no related row is absent (zero).
|
|
52
|
+
* Without a limit nothing is cut, and the caller counts what it got.
|
|
53
|
+
*/
|
|
54
|
+
totals?: Map<string, number>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* How the batch loaders render a target row.
|
|
58
|
+
*
|
|
59
|
+
* - `"admin"` — the view model the admin panel reads: declared types parsed
|
|
60
|
+
* (`parseDataFromServer`), the target's own to-one relations as
|
|
61
|
+
* `{ __type: "relation" }` refs, undeclared columns dropped.
|
|
62
|
+
* - `"rest"` — the row the target's own route serves (`toRestRow`): what an
|
|
63
|
+
* `include` embeds. The REST contract is that a developer never sees a ref,
|
|
64
|
+
* and an included row is rendered exactly as the same row is at the top
|
|
65
|
+
* level.
|
|
66
|
+
*/
|
|
67
|
+
export type RelatedRowStyle = "admin" | "rest";
|
|
68
|
+
/**
|
|
69
|
+
* The order one parent's related rows come in: the include's own keys, then
|
|
70
|
+
* the target's key ascending.
|
|
71
|
+
*
|
|
72
|
+
* The key ascending is the order an unordered batch read handed rows back in
|
|
73
|
+
* on an append-only table, and so the rows a `limit` with no `orderBy` kept —
|
|
74
|
+
* stated now rather than left to the plan. A key that names no column of the
|
|
75
|
+
* target sorts nothing, as it sorted nothing when the sort ran over the
|
|
76
|
+
* returned rows in JavaScript; the caller's field access on it was already
|
|
77
|
+
* judged at the route.
|
|
78
|
+
*
|
|
79
|
+
* NULLs go where a top-level `orderBy` puts them: the stated placement, else
|
|
80
|
+
* Postgres's own (last ascending, first descending).
|
|
81
|
+
*/
|
|
82
|
+
export declare function relatedRowOrder(targetTable: PgTable, targetKeys: AnyPgColumn[], orderBy: OrderByTuple[] | undefined): SQL[];
|
|
37
83
|
/**
|
|
38
84
|
* Service for handling all relation-related operations.
|
|
39
85
|
* Handles fetching, updating, and managing row relations.
|
|
@@ -119,6 +165,21 @@ export declare class RelationService {
|
|
|
119
165
|
* nobody was comparing.
|
|
120
166
|
*/
|
|
121
167
|
private toRelatedRow;
|
|
168
|
+
/**
|
|
169
|
+
* A target row as its own REST route serves it, for the `"rest"` style.
|
|
170
|
+
*
|
|
171
|
+
* The loaders read the target with `SELECT *`, so the columns a REST read
|
|
172
|
+
* never selects — a generated search vector — are dropped first; then
|
|
173
|
+
* `toRestRow` renders the rest, which applies the target's field access.
|
|
174
|
+
* Built once per batch: the hidden set is a property of the table.
|
|
175
|
+
*
|
|
176
|
+
* It replaces a second pass. An include used to render each target row as
|
|
177
|
+
* the admin's view model (`parseDataFromServer`, which also minted a
|
|
178
|
+
* `{ __type: "relation" }` ref for the target's own to-one relations) and
|
|
179
|
+
* then re-render that for REST: 3.4 µs a row against 1.4 µs for the row
|
|
180
|
+
* itself, and an envelope in a response whose contract has none.
|
|
181
|
+
*/
|
|
182
|
+
private restRendering;
|
|
122
183
|
/**
|
|
123
184
|
* The junction row's own columns, as the caller may see them.
|
|
124
185
|
*
|
|
@@ -242,13 +303,37 @@ export declare class RelationService {
|
|
|
242
303
|
* the rows that come back, because filtering afterwards reads every related
|
|
243
304
|
* row of every parent in order to discard most of them.
|
|
244
305
|
*/
|
|
245
|
-
batchFetchRelatedEntities(parentCollectionPath: string, parentIds: (string | number)[], _relationKey: string, relation: ResolvedRelation, narrow?: SQL
|
|
306
|
+
batchFetchRelatedEntities(parentCollectionPath: string, parentIds: (string | number)[], _relationKey: string, relation: ResolvedRelation, narrow?: SQL, options?: {
|
|
307
|
+
/** How each target row is rendered; see {@link RelatedRowStyle}. */
|
|
308
|
+
style?: RelatedRowStyle;
|
|
309
|
+
/**
|
|
310
|
+
* The parent rows themselves, in `parentIds` order, when the caller
|
|
311
|
+
* holds them. A `belongsTo` reads each one's foreign key off its
|
|
312
|
+
* row rather than selecting it again.
|
|
313
|
+
*/
|
|
314
|
+
parentRows?: Record<string, unknown>[];
|
|
315
|
+
}): Promise<Map<string, RelatedRow<Record<string, unknown>>>>;
|
|
246
316
|
/**
|
|
247
317
|
* Batch fetch many-cardinality related rows for multiple parent rows.
|
|
248
318
|
* Returns a Map<parentId, RelatedRow[]> instead of Map<parentId, RelatedRow>.
|
|
249
319
|
* Uses a single SQL query with IN clause to avoid N+1.
|
|
320
|
+
*
|
|
321
|
+
* `window` is an include's own `limit` and `orderBy`, and both are applied
|
|
322
|
+
* by Postgres. A limit is *per parent*, so it is a rank within each
|
|
323
|
+
* parent's rows rather than a `LIMIT`: a subquery ranks the keys of every
|
|
324
|
+
* matching row — under the same relation condition, `narrow`, soft delete
|
|
325
|
+
* and `beforeQuery` the unlimited read applies — and the read joins back to
|
|
326
|
+
* the ones ranked within the limit. It used to read every related row of
|
|
327
|
+
* every parent and cut the list in JavaScript: 200 rows served out of
|
|
328
|
+
* 200,000 read, parsed and rendered for `{ messages: { limit: 2 } }` on a
|
|
329
|
+
* page of 100 threads. Rows come back in the window's order, so the
|
|
330
|
+
* per-parent lists are already sorted.
|
|
250
331
|
*/
|
|
251
332
|
batchFetchRelatedEntitiesMany(parentCollectionPath: string, parentIds: (string | number)[], _relationKey: string, relation: ResolvedRelation,
|
|
252
333
|
/** An extra condition on the target — see {@link batchFetchRelatedEntities}. */
|
|
253
|
-
narrow?: SQL
|
|
334
|
+
narrow?: SQL, window?: RelatedRowWindow,
|
|
335
|
+
/** `style` — how each target row is rendered; see {@link RelatedRowStyle}. */
|
|
336
|
+
options?: {
|
|
337
|
+
style?: RelatedRowStyle;
|
|
338
|
+
}): Promise<Map<string, RelatedRow<Record<string, unknown>>[]>>;
|
|
254
339
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { type PgNotifyListenerStatus } from "../pg-notify-listener.js";
|
|
2
2
|
/**
|
|
3
3
|
* A single database change captured by the CDC triggers and delivered over the
|
|
4
|
-
* `rebase_cdc` NOTIFY channel.
|
|
4
|
+
* `rebase_cdc` NOTIFY channel — one row of a notification, which may name many.
|
|
5
5
|
*/
|
|
6
6
|
export interface CdcChangeEvent {
|
|
7
7
|
schema: string;
|
|
8
8
|
table: string;
|
|
9
|
-
|
|
9
|
+
/** `TRUNCATE` changed every row, and `row` is empty. */
|
|
10
|
+
op: "INSERT" | "UPDATE" | "DELETE" | "TRUNCATE";
|
|
10
11
|
/**
|
|
11
12
|
* The changed row's identity, by column: its key, and for a junction table
|
|
12
13
|
* the two ids naming the child list (from NEW for insert/update, OLD for
|
|
@@ -18,10 +19,20 @@ export interface CdcChangeEvent {
|
|
|
18
19
|
truncated?: boolean;
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
|
-
* Parse a `rebase_cdc` NOTIFY payload
|
|
22
|
-
* a single bad message can never
|
|
22
|
+
* Parse a `rebase_cdc` NOTIFY payload into the changes it names, one per row.
|
|
23
|
+
* Returns `null` for anything malformed so a single bad message can never
|
|
24
|
+
* crash the listener.
|
|
25
|
+
*
|
|
26
|
+
* Reads every shape a capture trigger sends, whichever version installed it —
|
|
27
|
+
* during a rolling deploy the database's triggers and this instance need not
|
|
28
|
+
* be the same version:
|
|
29
|
+
* - `{ schema, table, op, row }` — one row: the row trigger, and a statement
|
|
30
|
+
* that changed one row;
|
|
31
|
+
* - `{ schema, table, op, cols, keys }` — a statement's rows in a batch. With
|
|
32
|
+
* one column a key is the value; with several, an array in `cols` order;
|
|
33
|
+
* - `{ schema, table, op: "TRUNCATE" }`.
|
|
23
34
|
*/
|
|
24
|
-
export declare function parseCdcPayload(payload: string): CdcChangeEvent | null;
|
|
35
|
+
export declare function parseCdcPayload(payload: string): CdcChangeEvent[] | null;
|
|
25
36
|
/**
|
|
26
37
|
* Dedicated Postgres LISTEN client for database-level CDC.
|
|
27
38
|
*
|
|
@@ -38,7 +49,7 @@ export declare class CdcListener {
|
|
|
38
49
|
* drop. Every change committed in the gap was notified to nobody, so
|
|
39
50
|
* this is where subscribers are told to look again.
|
|
40
51
|
*/
|
|
41
|
-
constructor(connectionString: string,
|
|
52
|
+
constructor(connectionString: string, onEvents: (events: CdcChangeEvent[]) => void | Promise<void>, onReconnect?: () => void, applicationName?: string);
|
|
42
53
|
/**
|
|
43
54
|
* Connect and begin listening. Idempotent.
|
|
44
55
|
*
|