postgrejs 3.12.0 → 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 +14 -0
- package/connection/intl-connection.js +95 -15
- 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 +68 -0
- package/connection/prepared-statement.js +71 -2
- 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 +2 -0
- 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 +2 -0
- package/data-types/int8-type.d.ts +2 -0
- package/data-types/int8-type.js +2 -0
- 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 +2 -0
- 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 +6 -0
- 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/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 +220 -20
- package/util/stringify-for-sql.d.ts +17 -0
- package/util/stringify-for-sql.js +17 -0
- package/util/wrap-row-description.d.ts +10 -0
- package/util/wrap-row-description.js +10 -0
- package/CHANGELOG.md +0 -1453
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
|
|
@@ -450,5 +458,11 @@ export declare class IntlConnection extends SafeEventEmitter {
|
|
|
450
458
|
protected _currentTimeZone(): Maybe<string>;
|
|
451
459
|
protected _currentDateStyle(): Maybe<PgDateStyle>;
|
|
452
460
|
}
|
|
461
|
+
/**
|
|
462
|
+
* The internals behind a `Connection`.
|
|
463
|
+
*
|
|
464
|
+
* @param connection The handle.
|
|
465
|
+
* @returns What it wraps.
|
|
466
|
+
*/
|
|
453
467
|
export declare function getIntlConnection(connection: Connection): IntlConnection;
|
|
454
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)
|
|
@@ -1497,7 +1547,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1497
1547
|
const l = rows.length;
|
|
1498
1548
|
let i;
|
|
1499
1549
|
for (i = 0; i < l; i++) {
|
|
1500
|
-
rows[i] = rowDecoder.decode(parsers, rows[i]
|
|
1550
|
+
rows[i] = rowDecoder.decode(parsers, dataRowBytes(rows[i]), rows[i].columnCount, options, resultFields);
|
|
1501
1551
|
}
|
|
1502
1552
|
result.rows = rows;
|
|
1503
1553
|
}
|
|
@@ -1630,7 +1680,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1630
1680
|
const l = pendingRows.length;
|
|
1631
1681
|
let i;
|
|
1632
1682
|
for (i = 0; i < l; i++) {
|
|
1633
|
-
pendingRows[i] = rowDecoder.decode(parsers, pendingRows[i]
|
|
1683
|
+
pendingRows[i] = rowDecoder.decode(parsers, dataRowBytes(pendingRows[i]), pendingRows[i].columnCount, options, resultFields);
|
|
1634
1684
|
}
|
|
1635
1685
|
item.rows = pendingRows;
|
|
1636
1686
|
if (!item.command)
|
|
@@ -1694,6 +1744,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1694
1744
|
let commandTag;
|
|
1695
1745
|
let error;
|
|
1696
1746
|
let suspended = false;
|
|
1747
|
+
let streaming = false;
|
|
1697
1748
|
const rowDecoder = resolveRowDecoder(options);
|
|
1698
1749
|
// The whole point of reusing a prepared statement here: its
|
|
1699
1750
|
// RowDescription is already in hand, so fetchAsString's OID list can
|
|
@@ -1706,6 +1757,11 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1706
1757
|
resultFields = resolved.resultFields;
|
|
1707
1758
|
result.fields = resultFields;
|
|
1708
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);
|
|
1709
1765
|
}
|
|
1710
1766
|
// The SAVEPOINT/RELEASE pair rollbackOnError needs travels with the
|
|
1711
1767
|
// statement instead of costing a round trip each - see
|
|
@@ -1740,7 +1796,22 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1740
1796
|
suspended = true;
|
|
1741
1797
|
break;
|
|
1742
1798
|
case Protocol.BackendMessageCode.DataRow:
|
|
1743
|
-
|
|
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);
|
|
1744
1815
|
break;
|
|
1745
1816
|
// See queryOnce() for why an empty statement lands here.
|
|
1746
1817
|
case Protocol.BackendMessageCode.EmptyQueryResponse:
|
|
@@ -1780,10 +1851,13 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1780
1851
|
if (!result.command)
|
|
1781
1852
|
result.command = 'SELECT';
|
|
1782
1853
|
result.rows = rows;
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
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
|
+
}
|
|
1787
1861
|
}
|
|
1788
1862
|
}
|
|
1789
1863
|
if (suspended)
|
|
@@ -1860,7 +1934,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1860
1934
|
const l = d.raw.length;
|
|
1861
1935
|
const out = new Array(l);
|
|
1862
1936
|
for (k = 0; k < l; k++)
|
|
1863
|
-
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);
|
|
1864
1938
|
d.target.rows = out;
|
|
1865
1939
|
}
|
|
1866
1940
|
return opts;
|
|
@@ -1970,6 +2044,12 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
1970
2044
|
return this._dateStyle;
|
|
1971
2045
|
}
|
|
1972
2046
|
}
|
|
2047
|
+
/**
|
|
2048
|
+
* The internals behind a `Connection`.
|
|
2049
|
+
*
|
|
2050
|
+
* @param connection The handle.
|
|
2051
|
+
* @returns What it wraps.
|
|
2052
|
+
*/
|
|
1973
2053
|
export function getIntlConnection(connection) {
|
|
1974
2054
|
return connection._intlCon;
|
|
1975
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)
|