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.
Files changed (183) hide show
  1. package/connection/bind-param.d.ts +21 -0
  2. package/connection/bind-param.js +21 -0
  3. package/connection/connection.d.ts +136 -0
  4. package/connection/connection.js +132 -5
  5. package/connection/copy-stream.d.ts +2 -0
  6. package/connection/copy-stream.js +2 -0
  7. package/connection/cursor.d.ts +67 -5
  8. package/connection/cursor.js +88 -15
  9. package/connection/intl-connection.d.ts +14 -0
  10. package/connection/intl-connection.js +95 -15
  11. package/connection/large-object.d.ts +18 -0
  12. package/connection/large-object.js +17 -0
  13. package/connection/logical-replication.d.ts +15 -0
  14. package/connection/logical-replication.js +6 -0
  15. package/connection/pool.d.ts +80 -0
  16. package/connection/pool.js +82 -9
  17. package/connection/portal.d.ts +37 -0
  18. package/connection/portal.js +29 -0
  19. package/connection/prepared-statement.d.ts +68 -0
  20. package/connection/prepared-statement.js +71 -2
  21. package/constants.d.ts +18 -0
  22. package/constants.js +18 -0
  23. package/data-type-map.d.ts +38 -0
  24. package/data-type-map.js +38 -0
  25. package/data-types/bit-type.d.ts +4 -0
  26. package/data-types/bit-type.js +4 -0
  27. package/data-types/bool-type.d.ts +2 -0
  28. package/data-types/bool-type.js +2 -0
  29. package/data-types/box-type.d.ts +2 -0
  30. package/data-types/box-type.js +2 -0
  31. package/data-types/bytea-type.d.ts +2 -0
  32. package/data-types/bytea-type.js +2 -0
  33. package/data-types/char-type.d.ts +2 -0
  34. package/data-types/char-type.js +2 -0
  35. package/data-types/circle-type.d.ts +2 -0
  36. package/data-types/circle-type.js +2 -0
  37. package/data-types/date-type.d.ts +2 -0
  38. package/data-types/date-type.js +2 -0
  39. package/data-types/float4-type.d.ts +2 -0
  40. package/data-types/float4-type.js +2 -0
  41. package/data-types/float8-type.d.ts +2 -0
  42. package/data-types/float8-type.js +2 -0
  43. package/data-types/inet-type.d.ts +4 -0
  44. package/data-types/inet-type.js +4 -0
  45. package/data-types/int2-type.d.ts +2 -0
  46. package/data-types/int2-type.js +2 -0
  47. package/data-types/int2-vector-type.d.ts +2 -0
  48. package/data-types/int2-vector-type.js +2 -0
  49. package/data-types/int4-type.d.ts +2 -0
  50. package/data-types/int4-type.js +2 -0
  51. package/data-types/int8-type.d.ts +2 -0
  52. package/data-types/int8-type.js +2 -0
  53. package/data-types/interval-type.d.ts +9 -0
  54. package/data-types/interval-type.js +9 -0
  55. package/data-types/json-type.d.ts +2 -0
  56. package/data-types/json-type.js +2 -0
  57. package/data-types/jsonb-type.d.ts +2 -0
  58. package/data-types/jsonb-type.js +2 -0
  59. package/data-types/jsonpath-type.d.ts +2 -0
  60. package/data-types/jsonpath-type.js +2 -0
  61. package/data-types/line-type.d.ts +2 -0
  62. package/data-types/line-type.js +2 -0
  63. package/data-types/lseg-type.d.ts +2 -0
  64. package/data-types/lseg-type.js +2 -0
  65. package/data-types/macaddr-type.d.ts +4 -0
  66. package/data-types/macaddr-type.js +4 -0
  67. package/data-types/money-type.d.ts +1 -0
  68. package/data-types/money-type.js +17 -10
  69. package/data-types/numeric-type.d.ts +2 -0
  70. package/data-types/numeric-type.js +2 -0
  71. package/data-types/oid-type.d.ts +2 -0
  72. package/data-types/oid-type.js +2 -0
  73. package/data-types/oid-vector-type.d.ts +1 -0
  74. package/data-types/oid-vector-type.js +1 -0
  75. package/data-types/path-type.d.ts +4 -0
  76. package/data-types/path-type.js +4 -0
  77. package/data-types/pg-lsn-type.d.ts +2 -0
  78. package/data-types/pg-lsn-type.js +2 -0
  79. package/data-types/point-type.d.ts +2 -0
  80. package/data-types/point-type.js +2 -0
  81. package/data-types/range-type.d.ts +4 -0
  82. package/data-types/range-type.js +4 -0
  83. package/data-types/snapshot-type.d.ts +4 -0
  84. package/data-types/snapshot-type.js +4 -0
  85. package/data-types/tid-type.d.ts +2 -0
  86. package/data-types/tid-type.js +2 -0
  87. package/data-types/time-type.d.ts +2 -0
  88. package/data-types/time-type.js +2 -0
  89. package/data-types/timestamp-type.d.ts +2 -0
  90. package/data-types/timestamp-type.js +2 -0
  91. package/data-types/timestamptz-type.d.ts +2 -0
  92. package/data-types/timestamptz-type.js +2 -0
  93. package/data-types/timetz-type.d.ts +2 -0
  94. package/data-types/timetz-type.js +2 -0
  95. package/data-types/tsvector-type.d.ts +4 -0
  96. package/data-types/tsvector-type.js +4 -0
  97. package/data-types/uuid-type.d.ts +2 -0
  98. package/data-types/uuid-type.js +2 -0
  99. package/data-types/varchar-type.d.ts +2 -0
  100. package/data-types/varchar-type.js +2 -0
  101. package/data-types/xid-type.d.ts +6 -0
  102. package/data-types/xid-type.js +6 -0
  103. package/interfaces/command-result.d.ts +7 -0
  104. package/interfaces/copy-from-rows-options.d.ts +4 -0
  105. package/interfaces/data-mapping-options.d.ts +6 -0
  106. package/interfaces/data-type.d.ts +37 -0
  107. package/interfaces/database-connection-params.d.ts +31 -0
  108. package/interfaces/field-info.d.ts +8 -0
  109. package/interfaces/function-call-options.d.ts +1 -0
  110. package/interfaces/function-call-result.d.ts +6 -0
  111. package/interfaces/pipeline-request.d.ts +2 -0
  112. package/interfaces/query-options.d.ts +7 -0
  113. package/interfaces/query-result.d.ts +7 -0
  114. package/interfaces/script-execute-options.d.ts +4 -0
  115. package/interfaces/script-result.d.ts +1 -0
  116. package/interfaces/statement-prepare-options.d.ts +1 -0
  117. package/package.json +5 -5
  118. package/protocol/backend.d.ts +6 -0
  119. package/protocol/backend.js +35 -5
  120. package/protocol/cert-signature.d.ts +8 -0
  121. package/protocol/cert-signature.js +8 -0
  122. package/protocol/database-error.d.ts +8 -0
  123. package/protocol/database-error.js +8 -0
  124. package/protocol/frontend.d.ts +16 -0
  125. package/protocol/frontend.js +13 -6
  126. package/protocol/pg-socket.d.ts +10 -0
  127. package/protocol/pg-socket.js +14 -7
  128. package/protocol/pgoutput.d.ts +15 -0
  129. package/protocol/pgoutput.js +7 -0
  130. package/protocol/protocol.d.ts +50 -2
  131. package/protocol/protocol.js +17 -1
  132. package/protocol/sasl.d.ts +22 -0
  133. package/protocol/sasl.js +21 -0
  134. package/protocol/smart-buffer.d.ts +4 -0
  135. package/safe-event-emitter.d.ts +7 -0
  136. package/safe-event-emitter.js +7 -0
  137. package/types.d.ts +21 -0
  138. package/util/array-calculatedim.d.ts +10 -0
  139. package/util/array-calculatedim.js +10 -0
  140. package/util/bigint-methods.d.ts +7 -0
  141. package/util/bigint-methods.js +9 -0
  142. package/util/config-from-env.d.ts +10 -0
  143. package/util/config-from-env.js +10 -0
  144. package/util/connection-config.d.ts +17 -0
  145. package/util/connection-config.js +17 -0
  146. package/util/copy-from-rows.d.ts +12 -0
  147. package/util/copy-from-rows.js +9 -0
  148. package/util/decimal-as-string.d.ts +6 -0
  149. package/util/decimal-as-string.js +6 -0
  150. package/util/decode-binaryarray.d.ts +9 -0
  151. package/util/decode-binaryarray.js +9 -0
  152. package/util/escape-literal.d.ts +11 -0
  153. package/util/escape-literal.js +13 -0
  154. package/util/fast-parseint.d.ts +14 -0
  155. package/util/fast-parseint.js +14 -0
  156. package/util/format-datetime.d.ts +26 -0
  157. package/util/format-datetime.js +26 -0
  158. package/util/get-parsers.d.ts +9 -0
  159. package/util/get-parsers.js +9 -0
  160. package/util/hex-text.d.ts +13 -21
  161. package/util/hex-text.js +24 -21
  162. package/util/local-date.d.ts +2 -41
  163. package/util/local-date.js +39 -41
  164. package/util/parse-array.d.ts +8 -0
  165. package/util/parse-array.js +8 -0
  166. package/util/parse-bytea.d.ts +15 -0
  167. package/util/parse-bytea.js +15 -0
  168. package/util/parse-datetime.d.ts +26 -0
  169. package/util/parse-datetime.js +26 -0
  170. package/util/parse-row.d.ts +23 -2
  171. package/util/parse-row.js +56 -36
  172. package/util/parse-time.d.ts +10 -0
  173. package/util/parse-time.js +10 -0
  174. package/util/resolve-column-formats.d.ts +1 -0
  175. package/util/row-decoder.d.ts +32 -0
  176. package/util/row-decoder.js +22 -0
  177. package/util/stringify-arrayliteral.d.ts +33 -0
  178. package/util/stringify-arrayliteral.js +220 -20
  179. package/util/stringify-for-sql.d.ts +17 -0
  180. package/util/stringify-for-sql.js +17 -0
  181. package/util/wrap-row-description.d.ts +10 -0
  182. package/util/wrap-row-description.js +10 -0
  183. package/CHANGELOG.md +0 -1453
@@ -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
- ' the same connection, which ends the implicit transaction it' +
89
- ' lives in. Open the cursor inside an explicit transaction, or' +
90
- ' give it a connection of its own.';
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 { data, columnCount } = rows[i];
104
- rows[i] = rowDecoder.decode(parsers, data, columnCount, this._queryOptions, fields);
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, fetching a batch at a time, and closes
118
- * the cursor when the loop ends - whether it ran out of rows, broke
119
- * early, or the body threw. Nothing is decoded ahead of what is asked
120
- * for, so this streams a result larger than memory the same way next()
121
- * does one row at a time.
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.data, msg.columnCount, options, current.fields);
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
- rows.push(msg);
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
- const l = rows.length;
1219
- let i;
1220
- for (i = 0; i < l; i++) {
1221
- rows[i] = rowDecoder.decode(parsers, rows[i].data, rows[i].columnCount, options, resultFields);
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].data, rows[i].columnCount, options, resultFields);
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].data, pendingRows[i].columnCount, options, resultFields);
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
- rows.push(msg);
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
- const l = rows.length;
1784
- let i;
1785
- for (i = 0; i < l; i++) {
1786
- rows[i] = rowDecoder.decode(parsers, rows[i].data, rows[i].columnCount, options, resultFields);
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].data, d.raw[k].columnCount, opts, d.fields);
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)