@rebasepro/server-postgres 0.24.0 → 0.24.1-canary.gcb918fc
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 +16 -0
- 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 +91 -2
- package/dist/PostgresBootstrapper.d.ts +101 -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-DnpJi6I7.js} +37 -29
- package/dist/backup-cli-DnpJi6I7.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-console-Ca6ZlAVU.js +154 -0
- package/dist/cli-console-Ca6ZlAVU.js.map +1 -0
- package/dist/cli-console.d.ts +39 -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-JNI-pwSf.js} +6 -3
- package/dist/cli-flags-JNI-pwSf.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 +566 -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-C9mT8bgf.js} +24 -112
- package/dist/connection-C9mT8bgf.js.map +1 -0
- package/dist/connection.d.ts +13 -2
- package/dist/databasePoolManager-BIuewidb.js +119 -0
- package/dist/databasePoolManager-BIuewidb.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-CVm_yvzb.js} +173 -77
- package/dist/ensure-tables-CVm_yvzb.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 +64 -5
- package/dist/history/ensure-history-table.d.ts +63 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +139 -19236
- 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/open-rebase-CbOur0TU.js +20858 -0
- package/dist/open-rebase-CbOur0TU.js.map +1 -0
- package/dist/open-rebase.d.ts +86 -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-bus/index.d.ts +1 -1
- 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-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-BLGc5yhs.js +1604 -0
- package/dist/websocket-BLGc5yhs.js.map +1 -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
|
@@ -1,5 +1,125 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { SqlScriptResult } from "@rebasepro/types";
|
|
1
|
+
import { type Connection, type Pool, type PoolClient, type Submittable } from "pg";
|
|
2
|
+
import type { SqlCursor, SqlScriptResult } from "@rebasepro/types";
|
|
3
|
+
/**
|
|
4
|
+
* Running a SQL script a person wrote — the Studio console — and saying what
|
|
5
|
+
* came back, column by column.
|
|
6
|
+
*
|
|
7
|
+
* The console used to receive bare rows and guess, from the query's text, which
|
|
8
|
+
* table a cell belonged to and which column held its key. The guess wrote
|
|
9
|
+
* `UPDATE authors SET name = … WHERE id = <the post's id>` for a cell of
|
|
10
|
+
* `SELECT p.id, a.name FROM posts p JOIN authors a …`, and `UPDATE users SET
|
|
11
|
+
* name = …` for `lower(email) AS name`. The database knows the answer: every
|
|
12
|
+
* column of a result's row description carries the table and column it was
|
|
13
|
+
* read from, or zero when it was computed. This reads it off the wire.
|
|
14
|
+
*
|
|
15
|
+
* Values come back as the text the database wrote, never parsed. Parsed, a
|
|
16
|
+
* `text[]` read as a JavaScript array and went back as JSON — a malformed array
|
|
17
|
+
* literal; a `bytea` as a serialized Buffer; a `timestamp` as a Date shifted by
|
|
18
|
+
* the server's timezone; a `numeric` as a float. Text is what the database
|
|
19
|
+
* reads back exactly.
|
|
20
|
+
*/
|
|
21
|
+
/** The parts of a `RowDescription` field this reads. */
|
|
22
|
+
interface WireField {
|
|
23
|
+
name: string;
|
|
24
|
+
/** The OID of the table the column was read from; 0 when it was computed. */
|
|
25
|
+
tableID: number;
|
|
26
|
+
/** Its attribute number in that table; 0 when it was computed. */
|
|
27
|
+
columnID: number;
|
|
28
|
+
dataTypeID: number;
|
|
29
|
+
}
|
|
30
|
+
/** One statement of the script, as it came back. */
|
|
31
|
+
export interface StatementOutcome {
|
|
32
|
+
fields: WireField[];
|
|
33
|
+
rows: Record<string, string | null>[];
|
|
34
|
+
/** How many rows the database sent for it, kept or not. */
|
|
35
|
+
received: number;
|
|
36
|
+
/** Rows past the {@link SqlResultLimits} were counted and not kept. */
|
|
37
|
+
truncated: boolean;
|
|
38
|
+
/** The command tag, e.g. `SELECT 3`, `INSERT 0 1`, `CREATE TABLE`. */
|
|
39
|
+
tag?: string;
|
|
40
|
+
}
|
|
41
|
+
/** How much of one statement's result a run keeps. */
|
|
42
|
+
export interface SqlResultLimits {
|
|
43
|
+
/** Rows kept; the rest are counted and let go. */
|
|
44
|
+
maxRows: number;
|
|
45
|
+
/** Characters of values kept, summed over the kept rows. */
|
|
46
|
+
maxTextLength: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* What the Studio console keeps of a result: its first 10,000 rows, or its
|
|
50
|
+
* first 32 Mi characters of values, whichever ends first.
|
|
51
|
+
*
|
|
52
|
+
* Every row used to be kept. `TABLE events` on a million rows held 418 MB of
|
|
53
|
+
* row objects and built a 475 MB frame, and a server with 1 GiB died of it —
|
|
54
|
+
* every other user's connection with it. Ten times the console's own
|
|
55
|
+
* `LIMIT 1000`, so a run that asked for a page always gets it whole; the text
|
|
56
|
+
* budget is for the rows a count does not bound — a document column, a
|
|
57
|
+
* `jsonb` blob — and keeps the frame, the string it is built from and the
|
|
58
|
+
* socket's copy of it around 100 MB at worst.
|
|
59
|
+
*
|
|
60
|
+
* Postgres still sends every row (a simple query cannot be stopped short of
|
|
61
|
+
* cancelling it, which would abort the statements after it); they are counted,
|
|
62
|
+
* so the result says how many there were.
|
|
63
|
+
*/
|
|
64
|
+
export declare const SQL_CONSOLE_RESULT_LIMITS: SqlResultLimits;
|
|
65
|
+
/**
|
|
66
|
+
* The script, sent as one simple query and read message by message.
|
|
67
|
+
*
|
|
68
|
+
* A `Submittable` rather than `client.query(text)`: node-postgres parses every
|
|
69
|
+
* value through its type parsers and drops each field's origin, and for a
|
|
70
|
+
* multi-statement script returns an array the driver's callers took for a
|
|
71
|
+
* result with no rows. The message handlers below are the ones node-postgres
|
|
72
|
+
* calls on the active query — the same protocol `pg-cursor` implements.
|
|
73
|
+
*/
|
|
74
|
+
export declare class ScriptRun implements Submittable {
|
|
75
|
+
private readonly text;
|
|
76
|
+
private readonly limits;
|
|
77
|
+
/**
|
|
78
|
+
* Set by node-postgres when the pool has a `query_timeout`: a wrapper that
|
|
79
|
+
* clears its read timer. Called on every outcome, so the timer never
|
|
80
|
+
* outlives the run.
|
|
81
|
+
*/
|
|
82
|
+
callback?: (error: Error | null) => void;
|
|
83
|
+
/**
|
|
84
|
+
* Every statement of the script, as it came back. Only the last holds its
|
|
85
|
+
* rows: the console shows the last, and a statement's rows are let go when
|
|
86
|
+
* the next one begins — `SELECT * FROM big; SELECT 1` held every row of
|
|
87
|
+
* `big` to the end, to show one.
|
|
88
|
+
*/
|
|
89
|
+
readonly statements: StatementOutcome[];
|
|
90
|
+
readonly done: Promise<StatementOutcome[]>;
|
|
91
|
+
/** The statement whose row description arrived and whose command has not completed. */
|
|
92
|
+
private open?;
|
|
93
|
+
/** What {@link open} has kept of its rows. */
|
|
94
|
+
private budget;
|
|
95
|
+
private settled;
|
|
96
|
+
private resolveRun;
|
|
97
|
+
private rejectRun;
|
|
98
|
+
constructor(text: string, limits?: SqlResultLimits);
|
|
99
|
+
submit(connection: Connection): void;
|
|
100
|
+
handleRowDescription(message: {
|
|
101
|
+
fields: WireField[];
|
|
102
|
+
}): void;
|
|
103
|
+
handleDataRow(message: {
|
|
104
|
+
fields: (string | null)[];
|
|
105
|
+
}): void;
|
|
106
|
+
handleCommandComplete(message: {
|
|
107
|
+
text: string;
|
|
108
|
+
}): void;
|
|
109
|
+
handleEmptyQuery(): void;
|
|
110
|
+
handlePortalSuspended(): void;
|
|
111
|
+
/**
|
|
112
|
+
* `COPY … FROM STDIN` waits for data the console has no way to send. Fail
|
|
113
|
+
* the copy rather than leave the session waiting for it.
|
|
114
|
+
*/
|
|
115
|
+
handleCopyInResponse(connection: Connection): void;
|
|
116
|
+
handleCopyData(): void;
|
|
117
|
+
handleError(error: Error): void;
|
|
118
|
+
handleReadyForQuery(): void;
|
|
119
|
+
/** Let go of the rows of the statement before the one beginning: only the last is shown. */
|
|
120
|
+
private releaseRows;
|
|
121
|
+
private settle;
|
|
122
|
+
}
|
|
3
123
|
/**
|
|
4
124
|
* `INSERT 0 1` → `INSERT`, 1. `CREATE TABLE` → `CREATE TABLE`, no count.
|
|
5
125
|
*
|
|
@@ -44,6 +164,17 @@ export interface OwnSessionOptions {
|
|
|
44
164
|
* it ran as the connection owner.
|
|
45
165
|
*/
|
|
46
166
|
assumeRole?: (connection: PoolClient) => Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Cancels the run when aborted: the statement in progress is stopped with
|
|
169
|
+
* `pg_cancel_backend`, and the run fails with the database's "canceling
|
|
170
|
+
* statement due to user request". A signal already aborted runs nothing.
|
|
171
|
+
*/
|
|
172
|
+
signal?: AbortSignal;
|
|
173
|
+
}
|
|
174
|
+
/** A run whose signal was aborted before it began. */
|
|
175
|
+
export declare class SqlRunCancelledError extends Error {
|
|
176
|
+
readonly code = "SQL_RUN_CANCELLED";
|
|
177
|
+
constructor();
|
|
47
178
|
}
|
|
48
179
|
/**
|
|
49
180
|
* Run `work` — SQL a person wrote — on a connection of its own from `pool`,
|
|
@@ -68,4 +199,77 @@ export declare function runSqlScriptOnPool(pool: Pool, sql: string, options?: Ow
|
|
|
68
199
|
* pool (PGlite in process): every value as text, and no column with a source —
|
|
69
200
|
* nothing to say where a value came from, so nothing the console will edit.
|
|
70
201
|
*/
|
|
71
|
-
export declare function sqlScriptResultFromRows(rows: Record<string, unknown>[]): SqlScriptResult;
|
|
202
|
+
export declare function sqlScriptResultFromRows(rows: Record<string, unknown>[], limits?: SqlResultLimits): SqlScriptResult;
|
|
203
|
+
/**
|
|
204
|
+
* How many bytes of rows one answer of the admin `executeSql` door — the
|
|
205
|
+
* WebSocket `EXECUTE_SQL` without `mode: "script"` — may carry: 128 MiB.
|
|
206
|
+
*
|
|
207
|
+
* That door had no limit at all. A million-row `SELECT` held every row parsed
|
|
208
|
+
* (432 MB of heap on the console's door before it was capped), then built the
|
|
209
|
+
* frame from them, and a server with 1 GiB died of it — with every other
|
|
210
|
+
* user's connection. Unlike the console's script door it does not keep a
|
|
211
|
+
* prefix: a program reading rows through it would take a prefix for the whole
|
|
212
|
+
* answer, so past the budget the answer is refused, {@link SqlResultTooLargeError}.
|
|
213
|
+
*
|
|
214
|
+
* Generous on purpose. The catalogue reads the admin UI makes through this door
|
|
215
|
+
* are far below it — the SQL console's schema browser reads one
|
|
216
|
+
* `information_schema.columns` row per column, 7 MB for the 100,000 columns of
|
|
217
|
+
* 5,000 tables — and anything near it is a result no browser tab should hold.
|
|
218
|
+
*/
|
|
219
|
+
export declare const SQL_RESULT_BYTE_BUDGET: number;
|
|
220
|
+
/**
|
|
221
|
+
* A result larger than the bytes its caller allowed, refused rather than
|
|
222
|
+
* returned in part. Its message is for the person who wrote the SQL.
|
|
223
|
+
*/
|
|
224
|
+
export declare class SqlResultTooLargeError extends Error {
|
|
225
|
+
readonly limitBytes: number;
|
|
226
|
+
readonly code = "RESULT_TOO_LARGE";
|
|
227
|
+
constructor(limitBytes: number);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Refuse rows that came back whole and are past `maxBytes`.
|
|
231
|
+
*
|
|
232
|
+
* Where nothing could watch the read, it is measured once done — and the
|
|
233
|
+
* answer still refused rather than sent.
|
|
234
|
+
*/
|
|
235
|
+
export declare function refuseOversizedRows<T extends Record<string, unknown>>(rows: T[], maxBytes: number): T[];
|
|
236
|
+
/**
|
|
237
|
+
* Run `work` — one query on `connection` — and stop reading once its rows pass
|
|
238
|
+
* `maxBytes`.
|
|
239
|
+
*
|
|
240
|
+
* node-postgres keeps every row of a query until the query completes, and a
|
|
241
|
+
* simple query cannot be stopped short of its end, so the only way to stop
|
|
242
|
+
* holding rows is to stop receiving them: the socket is closed. Postgres sees
|
|
243
|
+
* its client gone and abandons the statement; the session — and anything it
|
|
244
|
+
* held — goes with it, and `onOwnSession` destroys the connection rather than
|
|
245
|
+
* return it to the pool. What the caller gets is {@link SqlResultTooLargeError},
|
|
246
|
+
* never the rows read so far.
|
|
247
|
+
*
|
|
248
|
+
* Counted from the wire, message by message, as they arrive: what is held when
|
|
249
|
+
* it stops is the budget plus one network read.
|
|
250
|
+
*/
|
|
251
|
+
export declare function readWithinBudget<T extends Record<string, unknown>>(connection: PoolClient, maxBytes: number, work: () => Promise<T[]>): Promise<T[]>;
|
|
252
|
+
/** Rows one read of a {@link SqlCursor} fetches. */
|
|
253
|
+
export declare const SQL_CURSOR_BATCH_ROWS = 1000;
|
|
254
|
+
/**
|
|
255
|
+
* Open a cursor over ONE query a person wrote — the SQL console's "Download
|
|
256
|
+
* full result as CSV".
|
|
257
|
+
*
|
|
258
|
+
* On a connection of its own, in the role asked for, inside
|
|
259
|
+
* `BEGIN TRANSACTION READ ONLY`: the database refuses a write, a
|
|
260
|
+
* data-modifying `WITH` and a function that writes. The query is declared over
|
|
261
|
+
* the extended protocol, which refuses a second statement — `SELECT 1; DROP
|
|
262
|
+
* TABLE t` cannot ride along. Rows are then fetched {@link SQL_CURSOR_BATCH_ROWS}
|
|
263
|
+
* at a time, each `FETCH` under the session's `statement_timeout`; nothing
|
|
264
|
+
* fetches ahead of the reader, so memory holds one batch. The first batch is
|
|
265
|
+
* read here, so a query that fails on its first rows fails before anything has
|
|
266
|
+
* been sent.
|
|
267
|
+
*
|
|
268
|
+
* The caller closes the cursor — always. A reader that stops reading without
|
|
269
|
+
* closing is ended by `idle_in_transaction_session_timeout` after
|
|
270
|
+
* {@link SQL_CURSOR_IDLE_TIMEOUT_MS}.
|
|
271
|
+
*/
|
|
272
|
+
export declare function openSqlCursorOnPool(pool: Pool, sql: string, options?: OwnSessionOptions & {
|
|
273
|
+
batchRows?: number;
|
|
274
|
+
}): Promise<SqlCursor>;
|
|
275
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { DrizzleClient } from "../interfaces.js";
|
|
2
|
+
/**
|
|
3
|
+
* pgvector's iterative index scans, for a vector read that also filters.
|
|
4
|
+
*
|
|
5
|
+
* An HNSW index answers a nearest-neighbour query with the `hnsw.ef_search`
|
|
6
|
+
* (40 by default) candidates it found, and every other condition of the query
|
|
7
|
+
* — a `where`, a search, a soft-delete stamp, a row-level-security policy — is
|
|
8
|
+
* applied to those candidates afterwards. Where few of them pass, the page
|
|
9
|
+
* comes back short, or empty, though plenty of rows match: measured on 20,000
|
|
10
|
+
* rows with half of them matching a `status` filter far from the query vector,
|
|
11
|
+
* `LIMIT 10` served 0 rows. Offset pages over the index were empty or short the
|
|
12
|
+
* same way, while deeper pages, where the planner switched to an exact scan,
|
|
13
|
+
* served rows, so a walk skipped some and could repeat others.
|
|
14
|
+
*
|
|
15
|
+
* From pgvector 0.8.0, `hnsw.iterative_scan` makes the index keep scanning
|
|
16
|
+
* until the query has its rows (bounded by `hnsw.max_scan_tuples`, 20,000 by
|
|
17
|
+
* default). `strict_order` and not `relaxed_order`: the planner reads the
|
|
18
|
+
* index's output as already sorted by distance, so out-of-order rows would be
|
|
19
|
+
* cut at the wrong place by `LIMIT` and the same row could land on two pages.
|
|
20
|
+
* Measured on the case above: 0/10 → 10/10, and the first 200 rows by offset
|
|
21
|
+
* pages of 10 with no duplicates (`relaxed_order`: 18).
|
|
22
|
+
*
|
|
23
|
+
* Set with `set_config(…, true)` — local to the transaction the read runs in,
|
|
24
|
+
* so it never outlives it on a pooled connection. A read not in a transaction
|
|
25
|
+
* is given one.
|
|
26
|
+
*
|
|
27
|
+
* pgvector before 0.8.0 has no such setting, and with its library loaded it
|
|
28
|
+
* refuses an unknown `hnsw.` parameter, so the setting is applied only where
|
|
29
|
+
* the extension's version has it; there a filtered vector read keeps the
|
|
30
|
+
* behaviour above. IVFFlat indexes are not touched: their iterative scan only
|
|
31
|
+
* comes in relaxed order.
|
|
32
|
+
*/
|
|
33
|
+
/** Whether a pgvector version (`extversion`, e.g. `"0.8.1"`) has iterative index scans. */
|
|
34
|
+
export declare function hasIterativeIndexScans(version: string | undefined | null): boolean;
|
|
35
|
+
/** Whether `db`'s pgvector has iterative index scans. Asked once per `key`. */
|
|
36
|
+
export declare function iterativeScansAvailable(db: DrizzleClient, key: object): Promise<boolean>;
|
|
37
|
+
/** Whether `db` is a transaction, in which a `set_config(…, true)` stays. */
|
|
38
|
+
export declare function inTransaction(db: DrizzleClient): boolean;
|
|
39
|
+
/** Turn on strict-order iterative HNSW scans for the rest of `db`'s transaction. */
|
|
40
|
+
export declare function enableIterativeScan(db: DrizzleClient): Promise<void>;
|
|
@@ -14,3 +14,9 @@ export interface WriteFrame {
|
|
|
14
14
|
}
|
|
15
15
|
/** Run one write as a frame, refusing it when the chain is already too deep. */
|
|
16
16
|
export declare function inWriteFrame<T>(path: string, operation: "save" | "delete", fn: (frame: WriteFrame) => Promise<T>): Promise<T>;
|
|
17
|
+
/**
|
|
18
|
+
* How deep in hook-started writes the code running now is: 0 outside any
|
|
19
|
+
* write, 1 inside a write a caller started, 2 or more inside a write a hook of
|
|
20
|
+
* another write started (`context.data`).
|
|
21
|
+
*/
|
|
22
|
+
export declare function currentWriteDepth(): number;
|
|
@@ -28,6 +28,8 @@ export declare class WriteTransactionScope implements AmbientTransaction {
|
|
|
28
28
|
/** The first tracked statement that failed, whether or not its caller caught it. */
|
|
29
29
|
private failure?;
|
|
30
30
|
private readonly settledHooks;
|
|
31
|
+
/** Whether application code (a hook) has run on this transaction. See {@link noteApplicationCode}. */
|
|
32
|
+
private applicationCodeRan;
|
|
31
33
|
private settledContext?;
|
|
32
34
|
/** The transaction handle, for statements in this package that are already written against drizzle. */
|
|
33
35
|
tx?: DrizzleClient;
|
|
@@ -74,6 +76,13 @@ export declare class WriteTransactionScope implements AmbientTransaction {
|
|
|
74
76
|
* was safe.)
|
|
75
77
|
*/
|
|
76
78
|
assertCommittable(): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Application code — a collection, global or property hook, a
|
|
81
|
+
* `beforeQuery` — is about to run on this transaction, so it may catch a
|
|
82
|
+
* failed statement and leave the transaction aborted: the commit then has
|
|
83
|
+
* to ask ({@link assertCommittable}).
|
|
84
|
+
*/
|
|
85
|
+
noteApplicationCode(): void;
|
|
77
86
|
/**
|
|
78
87
|
* What a hook deferred with {@link afterSettled} is handed as `context`:
|
|
79
88
|
* the same caller, on a driver where every call is a transaction of its
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { WebSocket } from "ws";
|
|
2
|
+
/**
|
|
3
|
+
* Behaviours a client opts into when it opens the socket.
|
|
4
|
+
*
|
|
5
|
+
* The client names them on the socket URL, `?rebase_caps=heartbeat,channel_seq,cancel_sql`,
|
|
6
|
+
* and the server answers with a `hello` frame listing the ones it will honour:
|
|
7
|
+
*
|
|
8
|
+
* ```json
|
|
9
|
+
* { "type": "hello", "capabilities": ["heartbeat", "channel_seq", "cancel_sql"], "heartbeatMs": 30000, "clientId": "client_…" }
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Both halves are needed, so both directions of a version mismatch keep
|
|
13
|
+
* working. An older client names none and gets no `hello`, no heartbeat and no
|
|
14
|
+
* acknowledgement frames — nothing it would not recognise. An older server
|
|
15
|
+
* ignores the query parameter and sends no `hello`, and a newer client that
|
|
16
|
+
* hears none behaves as it always did.
|
|
17
|
+
*
|
|
18
|
+
* One list for both kinds: what the server sends (`heartbeat`, `channel_seq`)
|
|
19
|
+
* and the frames it takes beyond the base protocol (`cancel_sql`). Named before
|
|
20
|
+
* sign-in, so a socket that never signs in is served as one that does.
|
|
21
|
+
*
|
|
22
|
+
* - `heartbeat`: a `{ "type": "heartbeat" }` frame every `heartbeatMs`. A
|
|
23
|
+
* browser cannot send a WebSocket ping or see one, so a connection that went
|
|
24
|
+
* half-open — a NAT that forgot it, a laptop that slept — looks connected to
|
|
25
|
+
* it forever. With these, two missed beats tell it to redial.
|
|
26
|
+
* - `channel_seq`: on a retained channel, the sender of a broadcast gets
|
|
27
|
+
* `{ "type": "broadcast_ack", "channel", "seq" }` where every other member
|
|
28
|
+
* gets the message. Its own messages then leave no hole in the sequence it
|
|
29
|
+
* watches, so a hole means a message it really missed — see
|
|
30
|
+
* `RealtimeChannel` in the SDK.
|
|
31
|
+
* - `cancel_sql`: the server takes `CANCEL_SQL { requestId }`, which stops a
|
|
32
|
+
* console run (`EXECUTE_SQL`, `mode: "script"`) of the same socket. A client
|
|
33
|
+
* sends it only to a server whose `hello` listed it.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
/** The query parameter a client names its opt-ins in. */
|
|
38
|
+
export declare const SOCKET_OPT_IN_PARAM = "rebase_caps";
|
|
39
|
+
/** Every behaviour this server can be asked for. */
|
|
40
|
+
export declare const SOCKET_OPT_INS: readonly ["heartbeat", "channel_seq", "cancel_sql"];
|
|
41
|
+
export type SocketOptIn = typeof SOCKET_OPT_INS[number];
|
|
42
|
+
/** How often a socket that asked for heartbeats is sent one, by default. */
|
|
43
|
+
export declare const DEFAULT_HEARTBEAT_INTERVAL_MS = 30000;
|
|
44
|
+
/**
|
|
45
|
+
* What the client that opened this connection asked for, from its request
|
|
46
|
+
* URL: `undefined` when it named nothing (a client that predates this, which
|
|
47
|
+
* is sent no `hello`), otherwise the names this server honours — possibly
|
|
48
|
+
* none.
|
|
49
|
+
*/
|
|
50
|
+
export declare function requestedOptIns(url: string | undefined): Set<SocketOptIn> | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* The `hello` frame for a client that asked for `optIns`. With `channel_seq`
|
|
53
|
+
* it also names the socket's own id, the `senderId` its broadcasts carry in a
|
|
54
|
+
* channel's history, so a client filling a hole from history can tell its own
|
|
55
|
+
* messages (which it is never sent live) from the ones it missed.
|
|
56
|
+
*/
|
|
57
|
+
export declare function helloFrame(optIns: ReadonlySet<SocketOptIn>, heartbeatMs: number, clientId: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Send a heartbeat every `intervalMs` to each socket `sockets()` lists. One
|
|
60
|
+
* timer for the whole server, unref'd so it never holds the process open.
|
|
61
|
+
*
|
|
62
|
+
* @returns Stops it.
|
|
63
|
+
*/
|
|
64
|
+
export declare function startHeartbeats(sockets: () => Iterable<WebSocket>, intervalMs: number): () => void;
|