postgrejs 3.11.1 → 3.12.1
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/connection/bind-param.d.ts +21 -0
- package/connection/bind-param.js +21 -0
- package/connection/connection.d.ts +136 -0
- package/connection/connection.js +132 -5
- package/connection/copy-stream.d.ts +2 -0
- package/connection/copy-stream.js +2 -0
- package/connection/cursor.d.ts +67 -5
- package/connection/cursor.js +88 -15
- package/connection/intl-connection.d.ts +22 -0
- package/connection/intl-connection.js +107 -17
- package/connection/large-object.d.ts +18 -0
- package/connection/large-object.js +17 -0
- package/connection/logical-replication.d.ts +15 -0
- package/connection/logical-replication.js +6 -0
- package/connection/pool.d.ts +80 -0
- package/connection/pool.js +82 -9
- package/connection/portal.d.ts +37 -0
- package/connection/portal.js +29 -0
- package/connection/prepared-statement.d.ts +109 -0
- package/connection/prepared-statement.js +150 -7
- package/constants.d.ts +18 -0
- package/constants.js +18 -0
- package/data-type-map.d.ts +38 -0
- package/data-type-map.js +38 -0
- package/data-types/bit-type.d.ts +4 -0
- package/data-types/bit-type.js +4 -0
- package/data-types/bool-type.d.ts +2 -0
- package/data-types/bool-type.js +2 -0
- package/data-types/box-type.d.ts +2 -0
- package/data-types/box-type.js +2 -0
- package/data-types/bytea-type.d.ts +2 -0
- package/data-types/bytea-type.js +2 -0
- package/data-types/char-type.d.ts +2 -0
- package/data-types/char-type.js +2 -0
- package/data-types/circle-type.d.ts +2 -0
- package/data-types/circle-type.js +2 -0
- package/data-types/date-type.d.ts +2 -0
- package/data-types/date-type.js +2 -0
- package/data-types/float4-type.d.ts +2 -0
- package/data-types/float4-type.js +2 -0
- package/data-types/float8-type.d.ts +2 -0
- package/data-types/float8-type.js +2 -0
- package/data-types/inet-type.d.ts +4 -0
- package/data-types/inet-type.js +4 -0
- package/data-types/int2-type.d.ts +2 -0
- package/data-types/int2-type.js +3 -1
- package/data-types/int2-vector-type.d.ts +2 -0
- package/data-types/int2-vector-type.js +2 -0
- package/data-types/int4-type.d.ts +2 -0
- package/data-types/int4-type.js +3 -1
- package/data-types/int8-type.d.ts +2 -0
- package/data-types/int8-type.js +4 -2
- package/data-types/interval-type.d.ts +9 -0
- package/data-types/interval-type.js +9 -0
- package/data-types/json-type.d.ts +2 -0
- package/data-types/json-type.js +2 -0
- package/data-types/jsonb-type.d.ts +2 -0
- package/data-types/jsonb-type.js +2 -0
- package/data-types/jsonpath-type.d.ts +2 -0
- package/data-types/jsonpath-type.js +2 -0
- package/data-types/line-type.d.ts +2 -0
- package/data-types/line-type.js +2 -0
- package/data-types/lseg-type.d.ts +2 -0
- package/data-types/lseg-type.js +2 -0
- package/data-types/macaddr-type.d.ts +4 -0
- package/data-types/macaddr-type.js +4 -0
- package/data-types/money-type.d.ts +1 -0
- package/data-types/money-type.js +17 -10
- package/data-types/numeric-type.d.ts +2 -0
- package/data-types/numeric-type.js +2 -0
- package/data-types/oid-type.d.ts +2 -0
- package/data-types/oid-type.js +3 -1
- package/data-types/oid-vector-type.d.ts +1 -0
- package/data-types/oid-vector-type.js +1 -0
- package/data-types/path-type.d.ts +4 -0
- package/data-types/path-type.js +4 -0
- package/data-types/pg-lsn-type.d.ts +2 -0
- package/data-types/pg-lsn-type.js +2 -0
- package/data-types/point-type.d.ts +2 -0
- package/data-types/point-type.js +2 -0
- package/data-types/range-type.d.ts +4 -0
- package/data-types/range-type.js +4 -0
- package/data-types/snapshot-type.d.ts +4 -0
- package/data-types/snapshot-type.js +4 -0
- package/data-types/tid-type.d.ts +2 -0
- package/data-types/tid-type.js +2 -0
- package/data-types/time-type.d.ts +2 -0
- package/data-types/time-type.js +2 -0
- package/data-types/timestamp-type.d.ts +2 -0
- package/data-types/timestamp-type.js +2 -0
- package/data-types/timestamptz-type.d.ts +2 -0
- package/data-types/timestamptz-type.js +2 -0
- package/data-types/timetz-type.d.ts +2 -0
- package/data-types/timetz-type.js +2 -0
- package/data-types/tsvector-type.d.ts +4 -0
- package/data-types/tsvector-type.js +4 -0
- package/data-types/uuid-type.d.ts +2 -0
- package/data-types/uuid-type.js +2 -0
- package/data-types/varchar-type.d.ts +2 -0
- package/data-types/varchar-type.js +2 -0
- package/data-types/xid-type.d.ts +6 -0
- package/data-types/xid-type.js +8 -1
- package/interfaces/command-result.d.ts +7 -0
- package/interfaces/copy-from-rows-options.d.ts +4 -0
- package/interfaces/data-mapping-options.d.ts +6 -0
- package/interfaces/data-type.d.ts +37 -0
- package/interfaces/database-connection-params.d.ts +31 -0
- package/interfaces/field-info.d.ts +8 -0
- package/interfaces/function-call-options.d.ts +1 -0
- package/interfaces/function-call-result.d.ts +6 -0
- package/interfaces/pipeline-request.d.ts +2 -0
- package/interfaces/query-options.d.ts +7 -0
- package/interfaces/query-result.d.ts +7 -0
- package/interfaces/script-execute-options.d.ts +4 -0
- package/interfaces/script-result.d.ts +1 -0
- package/interfaces/statement-prepare-options.d.ts +1 -0
- package/package.json +5 -5
- package/protocol/backend.d.ts +6 -0
- package/protocol/backend.js +35 -5
- package/protocol/cert-signature.d.ts +8 -0
- package/protocol/cert-signature.js +8 -0
- package/protocol/database-error.d.ts +8 -0
- package/protocol/database-error.js +8 -0
- package/protocol/frontend.d.ts +16 -0
- package/protocol/frontend.js +13 -6
- package/protocol/pg-socket.d.ts +10 -0
- package/protocol/pg-socket.js +14 -7
- package/protocol/pgoutput.d.ts +15 -0
- package/protocol/pgoutput.js +7 -0
- package/protocol/protocol.d.ts +50 -2
- package/protocol/protocol.js +17 -1
- package/protocol/sasl.d.ts +22 -0
- package/protocol/sasl.js +21 -0
- package/protocol/smart-buffer.d.ts +4 -0
- package/safe-event-emitter.d.ts +7 -0
- package/safe-event-emitter.js +7 -0
- package/types.d.ts +21 -0
- package/util/array-calculatedim.d.ts +10 -0
- package/util/array-calculatedim.js +10 -0
- package/util/assert-integer.d.ts +40 -25
- package/util/assert-integer.js +97 -32
- package/util/bigint-methods.d.ts +7 -0
- package/util/bigint-methods.js +9 -0
- package/util/config-from-env.d.ts +10 -0
- package/util/config-from-env.js +10 -0
- package/util/connection-config.d.ts +17 -0
- package/util/connection-config.js +17 -0
- package/util/copy-from-rows.d.ts +12 -0
- package/util/copy-from-rows.js +9 -0
- package/util/decimal-as-string.d.ts +6 -0
- package/util/decimal-as-string.js +6 -0
- package/util/decode-binaryarray.d.ts +9 -0
- package/util/decode-binaryarray.js +9 -0
- package/util/escape-literal.d.ts +11 -0
- package/util/escape-literal.js +13 -0
- package/util/fast-parseint.d.ts +14 -0
- package/util/fast-parseint.js +14 -0
- package/util/format-datetime.d.ts +26 -0
- package/util/format-datetime.js +26 -0
- package/util/get-parsers.d.ts +9 -0
- package/util/get-parsers.js +9 -0
- package/util/hex-text.d.ts +13 -21
- package/util/hex-text.js +24 -21
- package/util/local-date.d.ts +2 -41
- package/util/local-date.js +39 -41
- package/util/parse-array.d.ts +8 -0
- package/util/parse-array.js +8 -0
- package/util/parse-bytea.d.ts +15 -0
- package/util/parse-bytea.js +15 -0
- package/util/parse-datetime.d.ts +26 -0
- package/util/parse-datetime.js +26 -0
- package/util/parse-row.d.ts +23 -2
- package/util/parse-row.js +56 -36
- package/util/parse-time.d.ts +10 -0
- package/util/parse-time.js +10 -0
- package/util/resolve-column-formats.d.ts +1 -0
- package/util/row-decoder.d.ts +32 -0
- package/util/row-decoder.js +22 -0
- package/util/stringify-arrayliteral.d.ts +33 -0
- package/util/stringify-arrayliteral.js +242 -7
- package/util/stringify-for-sql.d.ts +17 -0
- package/util/stringify-for-sql.js +17 -0
- package/util/unspecified-param.d.ts +26 -2
- package/util/unspecified-param.js +26 -2
- package/util/wrap-row-description.d.ts +10 -0
- package/util/wrap-row-description.js +10 -0
- package/CHANGELOG.md +0 -1426
package/connection/cursor.js
CHANGED
|
@@ -1,7 +1,38 @@
|
|
|
1
|
+
import { updateErrorMessage } from '@jsopen/objects';
|
|
1
2
|
import DoublyLinked from 'doublylinked';
|
|
2
3
|
import { TaskQueue } from 'power-tasks';
|
|
3
4
|
import { SafeEventEmitter } from '../safe-event-emitter.js';
|
|
4
|
-
import { resolveRowDecoder, resolveRowType } from '../util/row-decoder.js';
|
|
5
|
+
import { dataRowBytes, resolveRowDecoder, resolveRowType, } from '../util/row-decoder.js';
|
|
6
|
+
/**
|
|
7
|
+
* A result read a batch at a time instead of all at once.
|
|
8
|
+
*
|
|
9
|
+
* Returned by `Connection.query()`/`PreparedStatement.execute()` when
|
|
10
|
+
* `cursor: true` is passed, and backed by a server-side portal: rows are
|
|
11
|
+
* fetched `fetchCount` at a time (100 by default) as they are asked for,
|
|
12
|
+
* so a result larger than memory can be read through without ever
|
|
13
|
+
* holding it.
|
|
14
|
+
*
|
|
15
|
+
* The cursor closes itself when the rows run out, and `for await`
|
|
16
|
+
* closes it however the loop ends. Anything else - breaking out of a
|
|
17
|
+
* manual `next()` loop, an error in the middle - wants `close()`, or
|
|
18
|
+
* `await using`, which calls it.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* const result = await connection.query('select * from big_table', {
|
|
22
|
+
* cursor: true,
|
|
23
|
+
* fetchCount: 500,
|
|
24
|
+
* });
|
|
25
|
+
* for await (const row of result.cursor!) {
|
|
26
|
+
* // one row at a time; 500 arrive per round trip
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Emits `fetch` with each batch as it arrives, and `close` once.
|
|
31
|
+
*/
|
|
32
|
+
/* The portal lives inside the implicit transaction the statement opened,
|
|
33
|
+
so any other statement on the same connection ends it - see
|
|
34
|
+
_fetchRows() for what that looks like when it happens, and why the
|
|
35
|
+
error says more than the server's own does. */
|
|
5
36
|
export class Cursor extends SafeEventEmitter {
|
|
6
37
|
_statement;
|
|
7
38
|
_portal;
|
|
@@ -11,6 +42,7 @@ export class Cursor extends SafeEventEmitter {
|
|
|
11
42
|
_taskQueue = new TaskQueue({ concurrency: 1 });
|
|
12
43
|
_rows = new DoublyLinked();
|
|
13
44
|
_closed = false;
|
|
45
|
+
/** What the columns of every row this cursor returns are. */
|
|
14
46
|
fields;
|
|
15
47
|
constructor(statement, portal, fields, parsers, queryOptions) {
|
|
16
48
|
super();
|
|
@@ -21,12 +53,29 @@ export class Cursor extends SafeEventEmitter {
|
|
|
21
53
|
this.fields = fields;
|
|
22
54
|
this._rowDecoder = resolveRowDecoder(queryOptions);
|
|
23
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The shape of the rows this cursor hands back - `'array'`,
|
|
58
|
+
* `'object'`, or `'custom'` when a `RowDecoder` of the caller's own is
|
|
59
|
+
* producing them.
|
|
60
|
+
*/
|
|
24
61
|
get rowType() {
|
|
25
62
|
return resolveRowType(this._queryOptions);
|
|
26
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Whether the cursor is finished: the rows ran out, `close()` was
|
|
66
|
+
* called, or the portal was lost.
|
|
67
|
+
*/
|
|
27
68
|
get isClosed() {
|
|
28
69
|
return this._closed;
|
|
29
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* The next row, or `undefined` once there are none left.
|
|
73
|
+
*
|
|
74
|
+
* Fetches a batch from the server when the current one is used up, and
|
|
75
|
+
* closes the cursor when the server answers with nothing.
|
|
76
|
+
*
|
|
77
|
+
* @returns The next row, or `undefined` at the end.
|
|
78
|
+
*/
|
|
30
79
|
async next() {
|
|
31
80
|
if (!this._rows.length) {
|
|
32
81
|
if (this._closed)
|
|
@@ -35,6 +84,16 @@ export class Cursor extends SafeEventEmitter {
|
|
|
35
84
|
}
|
|
36
85
|
return this._rows.shift();
|
|
37
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Up to `nRows` rows, fetching as many batches as it takes.
|
|
89
|
+
*
|
|
90
|
+
* Shorter than asked for only at the end of the result, and empty once
|
|
91
|
+
* the cursor is closed - which is how the end is told apart from a
|
|
92
|
+
* batch boundary.
|
|
93
|
+
*
|
|
94
|
+
* @param nRows How many rows to read at most.
|
|
95
|
+
* @returns The rows read, in order.
|
|
96
|
+
*/
|
|
38
97
|
async fetch(nRows) {
|
|
39
98
|
const out = [];
|
|
40
99
|
if (this._closed)
|
|
@@ -50,6 +109,12 @@ export class Cursor extends SafeEventEmitter {
|
|
|
50
109
|
}
|
|
51
110
|
return out;
|
|
52
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Closes the cursor and releases the portal behind it.
|
|
114
|
+
*
|
|
115
|
+
* Idempotent, and called automatically when the rows run out or a
|
|
116
|
+
* `for await` loop ends. Emits `close`.
|
|
117
|
+
*/
|
|
53
118
|
async close() {
|
|
54
119
|
if (this._closed)
|
|
55
120
|
return;
|
|
@@ -59,10 +124,12 @@ export class Cursor extends SafeEventEmitter {
|
|
|
59
124
|
this.emit('close');
|
|
60
125
|
this._closed = true;
|
|
61
126
|
}
|
|
127
|
+
/** Reads one batch from the portal and queues it up for the callers. */
|
|
128
|
+
/* Both callers (next()/fetch()) already gate on _closed with no await
|
|
129
|
+
in between, so the test below cannot currently observe a change -
|
|
130
|
+
kept as a backstop for this method's own contract rather than for
|
|
131
|
+
anything that reaches it today. */
|
|
62
132
|
async _fetchRows() {
|
|
63
|
-
// Both callers (next()/fetch()) already gate on _closed with no await
|
|
64
|
-
// in between, so this can't currently observe a change - kept as a
|
|
65
|
-
// defensive backstop for this private method's own contract.
|
|
66
133
|
if (this._closed)
|
|
67
134
|
return;
|
|
68
135
|
const portal = this._portal;
|
|
@@ -83,11 +150,11 @@ export class Cursor extends SafeEventEmitter {
|
|
|
83
150
|
// connection for this cursor (Pool.query()) have it back.
|
|
84
151
|
if (e?.code === '34000') {
|
|
85
152
|
await this.close().catch(() => undefined);
|
|
86
|
-
e.message
|
|
153
|
+
updateErrorMessage(e, e.message +
|
|
87
154
|
' - the portal was destroyed by another statement running on' +
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
155
|
+
' the same connection, which ends the implicit transaction it' +
|
|
156
|
+
' lives in. Open the cursor inside an explicit transaction, or' +
|
|
157
|
+
' give it a connection of its own.');
|
|
91
158
|
}
|
|
92
159
|
throw e;
|
|
93
160
|
}
|
|
@@ -100,8 +167,10 @@ export class Cursor extends SafeEventEmitter {
|
|
|
100
167
|
const rowLen = rows.length;
|
|
101
168
|
let i;
|
|
102
169
|
for (i = 0; i < rowLen; i++) {
|
|
103
|
-
const
|
|
104
|
-
rows[i] = rowDecoder.
|
|
170
|
+
const raw = rows[i];
|
|
171
|
+
rows[i] = rowDecoder.decodeAt
|
|
172
|
+
? rowDecoder.decodeAt(parsers, raw.buffer, raw.offset, raw.len, raw.columnCount, this._queryOptions, fields)
|
|
173
|
+
: rowDecoder.decode(parsers, dataRowBytes(raw), raw.columnCount, this._queryOptions, fields);
|
|
105
174
|
}
|
|
106
175
|
}
|
|
107
176
|
this._rows.push(...rows);
|
|
@@ -114,12 +183,15 @@ export class Cursor extends SafeEventEmitter {
|
|
|
114
183
|
.toPromise();
|
|
115
184
|
}
|
|
116
185
|
/**
|
|
117
|
-
* Iterates the remaining rows,
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
186
|
+
* Iterates the remaining rows, a batch at a time, and closes the cursor
|
|
187
|
+
* when the loop ends - whether it ran out of rows, broke early, or the
|
|
188
|
+
* body threw.
|
|
189
|
+
*
|
|
190
|
+
* @returns An iterator over the rows left to read.
|
|
122
191
|
*/
|
|
192
|
+
/* Nothing is decoded ahead of what is asked for, so this streams a
|
|
193
|
+
result larger than memory the same way next() does one row at a
|
|
194
|
+
time. */
|
|
123
195
|
async *[Symbol.asyncIterator]() {
|
|
124
196
|
try {
|
|
125
197
|
let row;
|
|
@@ -130,6 +202,7 @@ export class Cursor extends SafeEventEmitter {
|
|
|
130
202
|
await this.close();
|
|
131
203
|
}
|
|
132
204
|
}
|
|
205
|
+
/** Closes the cursor, so `await using` can own one. */
|
|
133
206
|
[Symbol.asyncDispose]() {
|
|
134
207
|
return this.close();
|
|
135
208
|
}
|
|
@@ -34,6 +34,14 @@ interface DeferredRows {
|
|
|
34
34
|
parsers: AnyParseFunction[];
|
|
35
35
|
fields: any;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The internals `Connection` and `Pool` are both built on.
|
|
39
|
+
*
|
|
40
|
+
* Holds the socket, the session's state, the prepared statement cache
|
|
41
|
+
* and the reference count that decides when a connection may go back to
|
|
42
|
+
* its pool. Not part of the public API: `Connection` is the handle a
|
|
43
|
+
* caller holds, and this is what it and the pool share underneath.
|
|
44
|
+
*/
|
|
37
45
|
export declare class IntlConnection extends SafeEventEmitter {
|
|
38
46
|
/**
|
|
39
47
|
* Server-side prepared statements this connection has built, keyed by
|
|
@@ -332,9 +340,17 @@ export declare class IntlConnection extends SafeEventEmitter {
|
|
|
332
340
|
* a separately-awaited Sync, and fetches the RowDescription/NoData in the
|
|
333
341
|
* same round trip so the caller can cache it (see executeReused()) rather
|
|
334
342
|
* than every later execute() re-Describing its own portal.
|
|
343
|
+
*
|
|
344
|
+
* The Describe answers with the parameters too, and that answer is worth
|
|
345
|
+
* keeping: it is the only place the server says what it made of a
|
|
346
|
+
* parameter the client declared nothing for. `insert into t(v)
|
|
347
|
+
* values($1)` resolves `$1` from the column, not from the value - which
|
|
348
|
+
* is why it is stable across executions and why a caller who wants the
|
|
349
|
+
* binary encoding back can read it and name that type with BindParam.
|
|
335
350
|
*/
|
|
336
351
|
prepareOnce(sql: string, paramTypes: Maybe<Maybe<OID>[]>, statementName: string): Promise<{
|
|
337
352
|
fields?: Protocol.RowDescription[];
|
|
353
|
+
resolvedParamTypes?: OID[];
|
|
338
354
|
}>;
|
|
339
355
|
/**
|
|
340
356
|
* Bind+Execute+Sync as a single round trip against an unnamed portal, for
|
|
@@ -442,5 +458,11 @@ export declare class IntlConnection extends SafeEventEmitter {
|
|
|
442
458
|
protected _currentTimeZone(): Maybe<string>;
|
|
443
459
|
protected _currentDateStyle(): Maybe<PgDateStyle>;
|
|
444
460
|
}
|
|
461
|
+
/**
|
|
462
|
+
* The internals behind a `Connection`.
|
|
463
|
+
*
|
|
464
|
+
* @param connection The handle.
|
|
465
|
+
* @returns What it wraps.
|
|
466
|
+
*/
|
|
445
467
|
export declare function getIntlConnection(connection: Connection): IntlConnection;
|
|
446
468
|
export {};
|
|
@@ -15,7 +15,7 @@ import { escapeLiteral } from '../util/escape-literal.js';
|
|
|
15
15
|
import { fetchAsStringEqual, fetchAsStringNamesElement, fetchAsStringNamesOid, validateFetchAsString, } from '../util/fetch-as-string.js';
|
|
16
16
|
import { getParsers } from '../util/get-parsers.js';
|
|
17
17
|
import { resolveColumnFormats } from '../util/resolve-column-formats.js';
|
|
18
|
-
import { resolveRowDecoder, resolveRowType, } from '../util/row-decoder.js';
|
|
18
|
+
import { dataRowBytes, resolveRowDecoder, resolveRowType, } from '../util/row-decoder.js';
|
|
19
19
|
import { requireTimeZone } from '../util/temporal.js';
|
|
20
20
|
import { isTransactionCommand, refusesSavepoint, } from '../util/transaction-command.js';
|
|
21
21
|
import { wrapRowDescription } from '../util/wrap-row-description.js';
|
|
@@ -165,6 +165,14 @@ const EMPTY_QUERY_TAG = { command: '' };
|
|
|
165
165
|
function preparedCacheKey(sql, paramTypes) {
|
|
166
166
|
return paramTypes?.length ? sql + '\u0000' + paramTypes.join(',') : sql;
|
|
167
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* The internals `Connection` and `Pool` are both built on.
|
|
170
|
+
*
|
|
171
|
+
* Holds the socket, the session's state, the prepared statement cache
|
|
172
|
+
* and the reference count that decides when a connection may go back to
|
|
173
|
+
* its pool. Not part of the public API: `Connection` is the handle a
|
|
174
|
+
* caller holds, and this is what it and the pool share underneath.
|
|
175
|
+
*/
|
|
168
176
|
export class IntlConnection extends SafeEventEmitter {
|
|
169
177
|
/**
|
|
170
178
|
* Server-side prepared statements this connection has built, keyed by
|
|
@@ -851,7 +859,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
851
859
|
currentDeferred.raw.push(msg);
|
|
852
860
|
break;
|
|
853
861
|
}
|
|
854
|
-
const row = rowDecoder.decode(parsers, msg
|
|
862
|
+
const row = rowDecoder.decode(parsers, dataRowBytes(msg), msg.columnCount, options, current.fields);
|
|
855
863
|
if (cb)
|
|
856
864
|
cb('row', row);
|
|
857
865
|
current.rows = current.rows || [];
|
|
@@ -1111,6 +1119,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1111
1119
|
const startTime = timingEnabled ? performance.now() : 0;
|
|
1112
1120
|
const result = { command: undefined };
|
|
1113
1121
|
const rows = [];
|
|
1122
|
+
let streaming = false;
|
|
1114
1123
|
let parsers;
|
|
1115
1124
|
let resultFields;
|
|
1116
1125
|
let commandTag;
|
|
@@ -1173,9 +1182,45 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1173
1182
|
resultFields = wrapRowDescription(typeMap, msg.fields, columnFormat, options);
|
|
1174
1183
|
result.fields = resultFields;
|
|
1175
1184
|
result.rowType = resolveRowType(options);
|
|
1185
|
+
// Everything a row needs to be read is now in hand, so it
|
|
1186
|
+
// can be read as it arrives rather than set aside - unless
|
|
1187
|
+
// something still has to be asked first, which is the one
|
|
1188
|
+
// case below.
|
|
1189
|
+
streaming = !this.needsMoneyFormat(resultFields, options);
|
|
1176
1190
|
break;
|
|
1177
1191
|
case Protocol.BackendMessageCode.DataRow:
|
|
1178
|
-
|
|
1192
|
+
// Decoded here rather than collected and walked again
|
|
1193
|
+
// afterwards. Collecting costs a `{ columnCount, data }`
|
|
1194
|
+
// and its buffer view per row, alive until the second pass
|
|
1195
|
+
// reaches them - so a result holds every row twice at its
|
|
1196
|
+
// peak, once raw and once read. Measured against `pg` on
|
|
1197
|
+
// 5000 rows of one float8 column, that was 166 bytes a row
|
|
1198
|
+
// between the two clients and is 109 now; by six columns
|
|
1199
|
+
// the difference was already the other way and stays
|
|
1200
|
+
// there. Timings do not move either way.
|
|
1201
|
+
//
|
|
1202
|
+
// `parsers` and `resultFields` are set together with
|
|
1203
|
+
// `streaming` above, so they are here whenever it is true.
|
|
1204
|
+
if (streaming && parsers && resultFields) {
|
|
1205
|
+
try {
|
|
1206
|
+
rows.push(rowDecoder.decodeAt
|
|
1207
|
+
? rowDecoder.decodeAt(parsers, msg.buffer, msg.offset, msg.len, msg.columnCount, options, resultFields)
|
|
1208
|
+
: rowDecoder.decode(parsers, dataRowBytes(msg), msg.columnCount, options, resultFields));
|
|
1209
|
+
}
|
|
1210
|
+
catch (e) {
|
|
1211
|
+
// A decoder of the caller's own can throw. Recorded
|
|
1212
|
+
// like a server error and reported at ReadyForQuery,
|
|
1213
|
+
// so the rest of the response is still drained; the
|
|
1214
|
+
// call then rejects before the pass below could run
|
|
1215
|
+
// over a row already read, which is why `rows` can
|
|
1216
|
+
// never end up holding a mixture.
|
|
1217
|
+
if (!error)
|
|
1218
|
+
error = e;
|
|
1219
|
+
streaming = false;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
else
|
|
1223
|
+
rows.push(msg);
|
|
1179
1224
|
break;
|
|
1180
1225
|
// An empty statement - '' or nothing but a comment - answers
|
|
1181
1226
|
// with this *instead of* a CommandComplete, so it has to take
|
|
@@ -1215,10 +1260,15 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1215
1260
|
if (!result.command)
|
|
1216
1261
|
result.command = 'SELECT';
|
|
1217
1262
|
result.rows = rows;
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1263
|
+
// Only what the loop above set aside: with `streaming` the rows
|
|
1264
|
+
// are already read, and re-reading one would be reading a value
|
|
1265
|
+
// rather than a row.
|
|
1266
|
+
if (!streaming) {
|
|
1267
|
+
const l = rows.length;
|
|
1268
|
+
let i;
|
|
1269
|
+
for (i = 0; i < l; i++) {
|
|
1270
|
+
rows[i] = rowDecoder.decode(parsers, dataRowBytes(rows[i]), rows[i].columnCount, options, resultFields);
|
|
1271
|
+
}
|
|
1222
1272
|
}
|
|
1223
1273
|
}
|
|
1224
1274
|
if (suspended)
|
|
@@ -1239,12 +1289,20 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1239
1289
|
* a separately-awaited Sync, and fetches the RowDescription/NoData in the
|
|
1240
1290
|
* same round trip so the caller can cache it (see executeReused()) rather
|
|
1241
1291
|
* than every later execute() re-Describing its own portal.
|
|
1292
|
+
*
|
|
1293
|
+
* The Describe answers with the parameters too, and that answer is worth
|
|
1294
|
+
* keeping: it is the only place the server says what it made of a
|
|
1295
|
+
* parameter the client declared nothing for. `insert into t(v)
|
|
1296
|
+
* values($1)` resolves `$1` from the column, not from the value - which
|
|
1297
|
+
* is why it is stable across executions and why a caller who wants the
|
|
1298
|
+
* binary encoding back can read it and name that type with BindParam.
|
|
1242
1299
|
*/
|
|
1243
1300
|
async prepareOnce(sql, paramTypes, statementName) {
|
|
1244
1301
|
this.assertConnected();
|
|
1245
1302
|
this.ref();
|
|
1246
1303
|
try {
|
|
1247
1304
|
let fields;
|
|
1305
|
+
let resolvedParamTypes;
|
|
1248
1306
|
let error;
|
|
1249
1307
|
this.runningQueryCount++;
|
|
1250
1308
|
await this.socket
|
|
@@ -1257,8 +1315,10 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1257
1315
|
// Not an error: handed over, and the loop carries on.
|
|
1258
1316
|
this.emit('notice', msg);
|
|
1259
1317
|
break;
|
|
1260
|
-
case Protocol.BackendMessageCode.ParseComplete:
|
|
1261
1318
|
case Protocol.BackendMessageCode.ParameterDescription:
|
|
1319
|
+
resolvedParamTypes = msg.parameterIds;
|
|
1320
|
+
break;
|
|
1321
|
+
case Protocol.BackendMessageCode.ParseComplete:
|
|
1262
1322
|
case Protocol.BackendMessageCode.NoData:
|
|
1263
1323
|
break;
|
|
1264
1324
|
case Protocol.BackendMessageCode.RowDescription:
|
|
@@ -1280,7 +1340,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1280
1340
|
.finally(() => {
|
|
1281
1341
|
this.runningQueryCount--;
|
|
1282
1342
|
});
|
|
1283
|
-
return { fields };
|
|
1343
|
+
return { fields, resolvedParamTypes };
|
|
1284
1344
|
}
|
|
1285
1345
|
finally {
|
|
1286
1346
|
this.unref();
|
|
@@ -1487,7 +1547,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1487
1547
|
const l = rows.length;
|
|
1488
1548
|
let i;
|
|
1489
1549
|
for (i = 0; i < l; i++) {
|
|
1490
|
-
rows[i] = rowDecoder.decode(parsers, rows[i]
|
|
1550
|
+
rows[i] = rowDecoder.decode(parsers, dataRowBytes(rows[i]), rows[i].columnCount, options, resultFields);
|
|
1491
1551
|
}
|
|
1492
1552
|
result.rows = rows;
|
|
1493
1553
|
}
|
|
@@ -1620,7 +1680,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1620
1680
|
const l = pendingRows.length;
|
|
1621
1681
|
let i;
|
|
1622
1682
|
for (i = 0; i < l; i++) {
|
|
1623
|
-
pendingRows[i] = rowDecoder.decode(parsers, pendingRows[i]
|
|
1683
|
+
pendingRows[i] = rowDecoder.decode(parsers, dataRowBytes(pendingRows[i]), pendingRows[i].columnCount, options, resultFields);
|
|
1624
1684
|
}
|
|
1625
1685
|
item.rows = pendingRows;
|
|
1626
1686
|
if (!item.command)
|
|
@@ -1684,6 +1744,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1684
1744
|
let commandTag;
|
|
1685
1745
|
let error;
|
|
1686
1746
|
let suspended = false;
|
|
1747
|
+
let streaming = false;
|
|
1687
1748
|
const rowDecoder = resolveRowDecoder(options);
|
|
1688
1749
|
// The whole point of reusing a prepared statement here: its
|
|
1689
1750
|
// RowDescription is already in hand, so fetchAsString's OID list can
|
|
@@ -1696,6 +1757,11 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1696
1757
|
resultFields = resolved.resultFields;
|
|
1697
1758
|
result.fields = resultFields;
|
|
1698
1759
|
result.rowType = resolveRowType(options);
|
|
1760
|
+
// See queryOnce() for what this decides and what it costs. The
|
|
1761
|
+
// answer is known before the first row here rather than at the
|
|
1762
|
+
// RowDescription, since the statement's fields were already in
|
|
1763
|
+
// hand.
|
|
1764
|
+
streaming = !this.needsMoneyFormat(resultFields, options);
|
|
1699
1765
|
}
|
|
1700
1766
|
// The SAVEPOINT/RELEASE pair rollbackOnError needs travels with the
|
|
1701
1767
|
// statement instead of costing a round trip each - see
|
|
@@ -1730,7 +1796,22 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1730
1796
|
suspended = true;
|
|
1731
1797
|
break;
|
|
1732
1798
|
case Protocol.BackendMessageCode.DataRow:
|
|
1733
|
-
|
|
1799
|
+
// Read as it arrives - see queryOnce(), including why a
|
|
1800
|
+
// decoder that throws here cannot leave a mixture behind.
|
|
1801
|
+
if (streaming) {
|
|
1802
|
+
try {
|
|
1803
|
+
rows.push(rowDecoder.decodeAt
|
|
1804
|
+
? rowDecoder.decodeAt(parsers, msg.buffer, msg.offset, msg.len, msg.columnCount, options, resultFields)
|
|
1805
|
+
: rowDecoder.decode(parsers, dataRowBytes(msg), msg.columnCount, options, resultFields));
|
|
1806
|
+
}
|
|
1807
|
+
catch (e) {
|
|
1808
|
+
if (!error)
|
|
1809
|
+
error = e;
|
|
1810
|
+
streaming = false;
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
else
|
|
1814
|
+
rows.push(msg);
|
|
1734
1815
|
break;
|
|
1735
1816
|
// See queryOnce() for why an empty statement lands here.
|
|
1736
1817
|
case Protocol.BackendMessageCode.EmptyQueryResponse:
|
|
@@ -1770,10 +1851,13 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1770
1851
|
if (!result.command)
|
|
1771
1852
|
result.command = 'SELECT';
|
|
1772
1853
|
result.rows = rows;
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1854
|
+
// See queryOnce(): only the rows the loop set aside.
|
|
1855
|
+
if (!streaming) {
|
|
1856
|
+
const l = rows.length;
|
|
1857
|
+
let i;
|
|
1858
|
+
for (i = 0; i < l; i++) {
|
|
1859
|
+
rows[i] = rowDecoder.decode(parsers, dataRowBytes(rows[i]), rows[i].columnCount, options, resultFields);
|
|
1860
|
+
}
|
|
1777
1861
|
}
|
|
1778
1862
|
}
|
|
1779
1863
|
if (suspended)
|
|
@@ -1850,7 +1934,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1850
1934
|
const l = d.raw.length;
|
|
1851
1935
|
const out = new Array(l);
|
|
1852
1936
|
for (k = 0; k < l; k++)
|
|
1853
|
-
out[k] = rowDecoder.decode(d.parsers, d.raw[k]
|
|
1937
|
+
out[k] = rowDecoder.decode(d.parsers, dataRowBytes(d.raw[k]), d.raw[k].columnCount, opts, d.fields);
|
|
1854
1938
|
d.target.rows = out;
|
|
1855
1939
|
}
|
|
1856
1940
|
return opts;
|
|
@@ -1960,6 +2044,12 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1960
2044
|
return this._dateStyle;
|
|
1961
2045
|
}
|
|
1962
2046
|
}
|
|
2047
|
+
/**
|
|
2048
|
+
* The internals behind a `Connection`.
|
|
2049
|
+
*
|
|
2050
|
+
* @param connection The handle.
|
|
2051
|
+
* @returns What it wraps.
|
|
2052
|
+
*/
|
|
1963
2053
|
export function getIntlConnection(connection) {
|
|
1964
2054
|
return connection._intlCon;
|
|
1965
2055
|
}
|
|
@@ -2,10 +2,14 @@ import { Readable, Writable } from 'node:stream';
|
|
|
2
2
|
import type { Connection } from './connection.js';
|
|
3
3
|
/** Open modes, as PostgreSQL's own INV_READ / INV_WRITE constants. */
|
|
4
4
|
export declare const LargeObjectMode: {
|
|
5
|
+
/** Open for reading only. */
|
|
5
6
|
readonly read: 262144;
|
|
7
|
+
/** Open for writing only. */
|
|
6
8
|
readonly write: 131072;
|
|
9
|
+
/** Open for both. */
|
|
7
10
|
readonly readWrite: 393216;
|
|
8
11
|
};
|
|
12
|
+
/** How a large object's `readable()`/`writable()` move their bytes. */
|
|
9
13
|
export interface LargeObjectStreamOptions {
|
|
10
14
|
/**
|
|
11
15
|
* Bytes per round trip (default 65536). Each chunk is its own `loread`
|
|
@@ -33,12 +37,26 @@ export interface LargeObjectStreamOptions {
|
|
|
33
37
|
* the caller started, which stays theirs to finish.
|
|
34
38
|
*/
|
|
35
39
|
export declare class LargeObject {
|
|
40
|
+
/**
|
|
41
|
+
* What this object is called, and the only way back to it: a large
|
|
42
|
+
* object belongs to no row, so the oid is what a caller stores.
|
|
43
|
+
*/
|
|
36
44
|
readonly oid: number;
|
|
37
45
|
protected readonly _connection: Connection;
|
|
38
46
|
protected readonly _fd: number;
|
|
39
47
|
/** True when this object opened the transaction and so should commit it. */
|
|
40
48
|
protected readonly _ownsTransaction: boolean;
|
|
41
49
|
protected _closed: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Built by `Connection.createLargeObject()`/`openLargeObject()`, which
|
|
52
|
+
* are what open the descriptor this wraps.
|
|
53
|
+
*
|
|
54
|
+
* @param connection The connection the descriptor lives on.
|
|
55
|
+
* @param oid The large object's own identifier.
|
|
56
|
+
* @param fd The open descriptor, valid for this transaction only.
|
|
57
|
+
* @param ownsTransaction Whether opening it started the transaction,
|
|
58
|
+
* and so whether `close()` should commit.
|
|
59
|
+
*/
|
|
42
60
|
constructor(connection: Connection, oid: number, fd: number, ownsTransaction: boolean);
|
|
43
61
|
/** Reads at most `length` bytes from the current position. */
|
|
44
62
|
read(length: number): Promise<Buffer>;
|
|
@@ -3,8 +3,11 @@ import { DataTypeOIDs } from '../constants.js';
|
|
|
3
3
|
import { BindParam } from './bind-param.js';
|
|
4
4
|
/** Open modes, as PostgreSQL's own INV_READ / INV_WRITE constants. */
|
|
5
5
|
export const LargeObjectMode = {
|
|
6
|
+
/** Open for reading only. */
|
|
6
7
|
read: 0x00040000,
|
|
8
|
+
/** Open for writing only. */
|
|
7
9
|
write: 0x00020000,
|
|
10
|
+
/** Open for both. */
|
|
8
11
|
readWrite: 0x00060000,
|
|
9
12
|
};
|
|
10
13
|
/**
|
|
@@ -26,12 +29,26 @@ export const LargeObjectMode = {
|
|
|
26
29
|
* the caller started, which stays theirs to finish.
|
|
27
30
|
*/
|
|
28
31
|
export class LargeObject {
|
|
32
|
+
/**
|
|
33
|
+
* What this object is called, and the only way back to it: a large
|
|
34
|
+
* object belongs to no row, so the oid is what a caller stores.
|
|
35
|
+
*/
|
|
29
36
|
oid;
|
|
30
37
|
_connection;
|
|
31
38
|
_fd;
|
|
32
39
|
/** True when this object opened the transaction and so should commit it. */
|
|
33
40
|
_ownsTransaction;
|
|
34
41
|
_closed = false;
|
|
42
|
+
/**
|
|
43
|
+
* Built by `Connection.createLargeObject()`/`openLargeObject()`, which
|
|
44
|
+
* are what open the descriptor this wraps.
|
|
45
|
+
*
|
|
46
|
+
* @param connection The connection the descriptor lives on.
|
|
47
|
+
* @param oid The large object's own identifier.
|
|
48
|
+
* @param fd The open descriptor, valid for this transaction only.
|
|
49
|
+
* @param ownsTransaction Whether opening it started the transaction,
|
|
50
|
+
* and so whether `close()` should commit.
|
|
51
|
+
*/
|
|
35
52
|
constructor(connection, oid, fd, ownsTransaction) {
|
|
36
53
|
this._connection = connection;
|
|
37
54
|
this.oid = oid;
|
|
@@ -3,12 +3,17 @@ import type { CaptureCallback } from '../protocol/pg-socket.js';
|
|
|
3
3
|
import { PgOutputDecoder, type PgOutputMessage, type PgOutputRelation, type TupleValues } from '../protocol/pgoutput.js';
|
|
4
4
|
import { SafeEventEmitter } from '../safe-event-emitter.js';
|
|
5
5
|
import { IntlConnection } from './intl-connection.js';
|
|
6
|
+
/** What happened to a row, as the replication stream reports it. */
|
|
6
7
|
export type ChangeCommand = 'insert' | 'update' | 'delete' | 'truncate';
|
|
8
|
+
/** One row change, decoded from the `pgoutput` stream. */
|
|
7
9
|
export interface Change {
|
|
10
|
+
/** Which of the four it was. */
|
|
8
11
|
command: ChangeCommand;
|
|
9
12
|
/** Schema-qualified, e.g. `public.users`. */
|
|
10
13
|
table: string;
|
|
14
|
+
/** The schema on its own, for a caller who does not want it qualified. */
|
|
11
15
|
schema: string;
|
|
16
|
+
/** The table's own description, as the stream declared it. */
|
|
12
17
|
relation: PgOutputRelation;
|
|
13
18
|
/** The row after the change; absent for delete and truncate. */
|
|
14
19
|
row?: TupleValues;
|
|
@@ -23,6 +28,10 @@ export interface Change {
|
|
|
23
28
|
/** Commit time of the transaction it belongs to. */
|
|
24
29
|
timestamp?: Date;
|
|
25
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* How to subscribe: everything a connection takes, plus which
|
|
33
|
+
* publication to follow and which slot to follow it in.
|
|
34
|
+
*/
|
|
26
35
|
export interface LogicalReplicationOptions extends ConnectionConfiguration {
|
|
27
36
|
/** Publication(s) to subscribe to; they must already exist on the server. */
|
|
28
37
|
publication: string | string[];
|
|
@@ -69,6 +78,7 @@ export interface LogicalReplicationOptions extends ConnectionConfiguration {
|
|
|
69
78
|
* caller has to remember to acknowledge anything.
|
|
70
79
|
*/
|
|
71
80
|
export declare class LogicalReplication extends SafeEventEmitter {
|
|
81
|
+
/** What this subscription was built with. */
|
|
72
82
|
readonly options: LogicalReplicationOptions;
|
|
73
83
|
protected _intlCon?: IntlConnection;
|
|
74
84
|
protected readonly _decoder: PgOutputDecoder;
|
|
@@ -84,6 +94,11 @@ export declare class LogicalReplication extends SafeEventEmitter {
|
|
|
84
94
|
protected _pendingLsn: bigint;
|
|
85
95
|
protected _commitTime?: Date;
|
|
86
96
|
protected _streamDone?: Promise<any>;
|
|
97
|
+
/**
|
|
98
|
+
* @param options The publication to follow, the slot to follow it in,
|
|
99
|
+
* and how to reach the server.
|
|
100
|
+
* @throws TypeError When no publication is named.
|
|
101
|
+
*/
|
|
87
102
|
constructor(options: LogicalReplicationOptions);
|
|
88
103
|
/** The slot this subscription is reading from. */
|
|
89
104
|
get slot(): string;
|
|
@@ -31,6 +31,7 @@ const DEFAULT_KEEPALIVE_MS = 10000;
|
|
|
31
31
|
* caller has to remember to acknowledge anything.
|
|
32
32
|
*/
|
|
33
33
|
export class LogicalReplication extends SafeEventEmitter {
|
|
34
|
+
/** What this subscription was built with. */
|
|
34
35
|
options;
|
|
35
36
|
_intlCon;
|
|
36
37
|
_decoder = new PgOutputDecoder();
|
|
@@ -46,6 +47,11 @@ export class LogicalReplication extends SafeEventEmitter {
|
|
|
46
47
|
_pendingLsn = 0n;
|
|
47
48
|
_commitTime;
|
|
48
49
|
_streamDone;
|
|
50
|
+
/**
|
|
51
|
+
* @param options The publication to follow, the slot to follow it in,
|
|
52
|
+
* and how to reach the server.
|
|
53
|
+
* @throws TypeError When no publication is named.
|
|
54
|
+
*/
|
|
49
55
|
constructor(options) {
|
|
50
56
|
super();
|
|
51
57
|
if (!options.publication)
|