postgrejs 2.23.1 → 3.0.2

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 (107) hide show
  1. package/CHANGELOG.md +171 -1
  2. package/LICENSE +26 -17
  3. package/README.md +195 -175
  4. package/connection/connection.d.ts +169 -16
  5. package/connection/connection.js +318 -56
  6. package/connection/copy-stream.d.ts +88 -0
  7. package/connection/copy-stream.js +244 -0
  8. package/connection/cursor.js +20 -13
  9. package/connection/intl-connection.d.ts +113 -5
  10. package/connection/intl-connection.js +500 -70
  11. package/connection/large-object.d.ts +65 -0
  12. package/connection/large-object.js +151 -0
  13. package/connection/logical-replication.d.ts +121 -0
  14. package/connection/logical-replication.js +279 -0
  15. package/connection/pool.d.ts +61 -4
  16. package/connection/pool.js +177 -29
  17. package/connection/portal.d.ts +18 -4
  18. package/connection/portal.js +53 -59
  19. package/connection/prepared-statement.d.ts +22 -1
  20. package/connection/prepared-statement.js +129 -109
  21. package/data-type-map.js +4 -2
  22. package/data-types/bool-type.js +14 -10
  23. package/data-types/box-type.js +15 -8
  24. package/data-types/bytea-type.js +9 -5
  25. package/data-types/char-type.js +11 -5
  26. package/data-types/circle-type.js +15 -7
  27. package/data-types/date-type.js +25 -19
  28. package/data-types/float4-type.js +10 -3
  29. package/data-types/float8-type.js +12 -3
  30. package/data-types/int2-type.js +9 -4
  31. package/data-types/int2-vector-type.js +9 -4
  32. package/data-types/int4-type.js +11 -5
  33. package/data-types/int8-type.js +22 -7
  34. package/data-types/json-type.js +17 -8
  35. package/data-types/jsonb-type.js +20 -14
  36. package/data-types/lseg-type.js +16 -8
  37. package/data-types/numeric-type.d.ts +8 -0
  38. package/data-types/numeric-type.js +118 -9
  39. package/data-types/oid-type.d.ts +0 -1
  40. package/data-types/oid-type.js +9 -10
  41. package/data-types/oid-vector-type.d.ts +9 -0
  42. package/data-types/oid-vector-type.js +51 -0
  43. package/data-types/point-type.js +14 -6
  44. package/data-types/time-type.js +20 -13
  45. package/data-types/timestamp-type.js +32 -25
  46. package/data-types/timestamptz-type.js +33 -21
  47. package/data-types/uuid-type.js +21 -11
  48. package/data-types/varchar-type.js +16 -4
  49. package/index.d.ts +5 -0
  50. package/index.js +5 -0
  51. package/interfaces/command-result.d.ts +2 -1
  52. package/interfaces/data-type.d.ts +5 -3
  53. package/interfaces/database-connection-params.d.ts +108 -0
  54. package/interfaces/query-options.d.ts +23 -0
  55. package/interfaces/script-execute-options.d.ts +23 -0
  56. package/interfaces/script-result.d.ts +3 -2
  57. package/package.json +2 -3
  58. package/protocol/backend.d.ts +6 -1
  59. package/protocol/backend.js +70 -45
  60. package/protocol/buffer-reader.d.ts +0 -2
  61. package/protocol/buffer-reader.js +3 -10
  62. package/protocol/cert-signature.d.ts +12 -0
  63. package/protocol/cert-signature.js +114 -0
  64. package/protocol/frontend.d.ts +16 -0
  65. package/protocol/frontend.js +70 -11
  66. package/protocol/pg-socket.d.ts +220 -9
  67. package/protocol/pg-socket.js +539 -102
  68. package/protocol/pgoutput.d.ts +76 -0
  69. package/protocol/pgoutput.js +155 -0
  70. package/protocol/protocol.d.ts +3 -4
  71. package/protocol/protocol.js +1 -1
  72. package/protocol/sasl.d.ts +11 -1
  73. package/protocol/sasl.js +36 -5
  74. package/protocol/smart-buffer.d.ts +0 -2
  75. package/protocol/smart-buffer.js +11 -16
  76. package/types.d.ts +5 -3
  77. package/util/abort-signal.d.ts +34 -0
  78. package/util/abort-signal.js +76 -0
  79. package/util/array-calculatedim.js +3 -1
  80. package/util/connection-config.js +77 -3
  81. package/util/decode-binaryarray.d.ts +1 -1
  82. package/util/decode-binaryarray.js +15 -6
  83. package/util/encode-binaryarray.js +7 -3
  84. package/util/escape-identifier.d.ts +14 -0
  85. package/util/escape-identifier.js +32 -0
  86. package/util/escape-literal.js +3 -0
  87. package/util/fast-parseint.d.ts +1 -0
  88. package/util/fast-parseint.js +12 -17
  89. package/util/format-datetime.d.ts +10 -0
  90. package/util/format-datetime.js +71 -0
  91. package/util/get-parsers.js +31 -10
  92. package/util/parse-array.js +2 -2
  93. package/util/parse-bytea.d.ts +2 -0
  94. package/util/parse-bytea.js +83 -0
  95. package/util/parse-datetime.d.ts +3 -1
  96. package/util/parse-datetime.js +86 -17
  97. package/util/parse-row.d.ts +3 -1
  98. package/util/parse-row.js +60 -5
  99. package/util/parse-time.js +8 -1
  100. package/util/sql-tag.d.ts +60 -0
  101. package/util/sql-tag.js +202 -0
  102. package/util/starts-transaction.d.ts +38 -0
  103. package/util/starts-transaction.js +148 -0
  104. package/util/stringify-arrayliteral.js +13 -1
  105. package/util/wrap-row-description.js +13 -4
  106. package/util/convert-row-to-object.d.ts +0 -2
  107. package/util/convert-row-to-object.js +0 -9
@@ -1,3 +1,5 @@
1
+ import net from 'node:net';
2
+ import tls from 'node:tls';
1
3
  import { ConnectionState } from '../constants.js';
2
4
  import type { ConnectionConfiguration } from '../interfaces/database-connection-params.js';
3
5
  import { SafeEventEmitter } from '../safe-event-emitter.js';
@@ -18,33 +20,242 @@ export declare class PgSocket extends SafeEventEmitter {
18
20
  private _saslSession?;
19
21
  private _processID?;
20
22
  private _secretKey?;
23
+ private _captureQueue;
24
+ private _pendingWrites;
25
+ private _flushScheduled;
26
+ private _hosts;
27
+ private _hostIndex;
28
+ private _sessionAttrsChecked;
29
+ private _standbyState?;
21
30
  constructor(options: ConnectionConfiguration);
22
31
  get state(): ConnectionState;
23
32
  get processID(): Maybe<number>;
24
33
  get secretKey(): Maybe<number>;
25
34
  get sessionParameters(): Record<string, string>;
26
35
  connect(): void;
36
+ protected _connectToHost(): void;
37
+ /**
38
+ * Negotiates TLS on an already TCP-connected `socket`, if configured -
39
+ * shared between the long-lived connection above (_connectToHost) and
40
+ * cancel() below, which needs the exact same host/TLS handling for its
41
+ * own short-lived socket, just without ever sending a StartupMessage
42
+ * afterwards. Resolves `onReady` with whichever socket ends up ready to
43
+ * speak the PostgreSQL protocol: the plain one, or its TLS upgrade.
44
+ */
45
+ protected _negotiateTls(socket: net.Socket, target: {
46
+ host: string;
47
+ port?: number;
48
+ }, onReady: (socket: net.Socket | tls.TLSSocket) => void, onError: (err: Error) => void): void;
49
+ /**
50
+ * Asks the server to cancel whatever this session is currently running.
51
+ *
52
+ * Opens its own short-lived connection and closes it again: a backend busy
53
+ * with a query is not reading its own socket, so the request cannot travel
54
+ * down the connection it is meant to interrupt. The server answers nothing
55
+ * - it either finds a matching session and signals it or does not - so
56
+ * this resolves once the bytes are out, and the cancelled query reports
57
+ * the outcome itself, as an ordinary error on its own connection.
58
+ *
59
+ * No StartupMessage or authentication is ever sent here - the
60
+ * CancelRequest is the only message this socket carries, and it is
61
+ * self-authorized by the processID/secretKey pair BackendKeyData handed
62
+ * out to the session being cancelled, not by a separate login. TLS is
63
+ * still negotiated via _negotiateTls() when configured, because a
64
+ * `hostssl` pg_hba.conf rule can reject a plaintext connection outright
65
+ * before the server ever reads what request it carries - and the target
66
+ * is `_hosts[_hostIndex]`, the host this session actually ended up
67
+ * connected to, not `options.host`, which after a multi-host failover may
68
+ * name a candidate this session never used.
69
+ */
70
+ cancel(): Promise<void>;
27
71
  close(): void;
28
- sendParseMessage(args: Frontend.ParseMessageArgs, cb?: Callback): void;
29
- sendBindMessage(args: Frontend.BindMessageArgs, cb?: Callback): void;
30
- sendDescribeMessage(args: Frontend.DescribeMessageArgs, cb?: Callback): void;
31
- sendExecuteMessage(args: Frontend.ExecuteMessageArgs, cb?: Callback): void;
32
- sendCloseMessage(args: Frontend.CloseMessageArgs, cb?: Callback): void;
33
- sendQueryMessage(sql: string, cb?: Callback): void;
72
+ sendExecuteMessage(args: Frontend.ExecuteMessageArgs, cb: CaptureCallback): Promise<any>;
73
+ sendCloseMessage(args: Frontend.CloseMessageArgs, cb: CaptureCallback): Promise<any>;
74
+ /**
75
+ * Sends Parse+Bind+Describe+Execute+Sync as a single write with a single
76
+ * FIFO capture entry, instead of 5 separate sendXMessage() round trips -
77
+ * the one-shot Extended Query fast path (IntlConnection.queryOnce()).
78
+ * Callers pass unnamed statement/portal (omit statement/portal/name) so
79
+ * no Close message is needed at all: PostgreSQL auto-clears an unnamed
80
+ * statement/portal at the next Parse/Bind referencing the unnamed name.
81
+ */
82
+ sendExtendedQueryMessages(args: {
83
+ parse: Frontend.ParseMessageArgs;
84
+ bind: Frontend.BindMessageArgs;
85
+ describe: Frontend.DescribeMessageArgs;
86
+ execute: Frontend.ExecuteMessageArgs;
87
+ }, cb: CaptureCallback): Promise<any>;
88
+ /**
89
+ * Parse + Describe(statement) + Sync as a single round trip - used by
90
+ * PreparedStatement.prepare() instead of a separate awaited Parse then a
91
+ * separate awaited Sync, and fetches the RowDescription/NoData in the
92
+ * same round trip so PreparedStatement can cache it instead of every
93
+ * later execute() re-Describing its own portal.
94
+ */
95
+ sendPrepareMessages(args: {
96
+ parse: Frontend.ParseMessageArgs;
97
+ describe: Frontend.DescribeMessageArgs;
98
+ }, cb: CaptureCallback): Promise<any>;
99
+ /**
100
+ * Bind + Execute + Sync as a single round trip, against an UNNAMED
101
+ * portal (args.bind.portal/args.execute.portal left unset) - used by
102
+ * PreparedStatement._execute() to reuse an already-prepared (named)
103
+ * statement without the per-call Describe/Close that Portal's own
104
+ * bind()/retrieveFields()/execute()/close() sequence needs for a
105
+ * freshly-created named portal. No Close message is needed: PostgreSQL
106
+ * auto-clears an unnamed portal at the next Bind naming it.
107
+ */
108
+ sendBindExecuteMessages(args: {
109
+ bind: Frontend.BindMessageArgs;
110
+ execute: Frontend.ExecuteMessageArgs;
111
+ }, cb: CaptureCallback): Promise<any>;
112
+ /**
113
+ * Bind + Describe(portal) + Flush as a single round trip (NOT Sync - see
114
+ * Portal.bindAndRetrieveFields()'s doc comment for why a Sync here would
115
+ * be unsafe: it would commit an implicit/autocommit transaction and
116
+ * destroy the just-bound named portal before any fetch() could use it).
117
+ * Used by PreparedStatement._execute()'s cursor branch instead of two
118
+ * separately-awaited Portal.bind()/retrieveFields() round trips.
119
+ */
120
+ sendBindDescribeMessages(args: {
121
+ bind: Frontend.BindMessageArgs;
122
+ describe: Frontend.DescribeMessageArgs;
123
+ }, cb: CaptureCallback): Promise<any>;
124
+ /**
125
+ * Close + Sync under ONE capture, rather than a Close capture followed by
126
+ * a separate Sync capture. That split is unsafe after an ErrorResponse:
127
+ * the server then skips every message until Sync, so the only reply is
128
+ * the Sync's ReadyForQuery - which the FIFO hands to the *Close* capture
129
+ * (the older entry), making it fail with "unexpected response message
130
+ * (Z)" and leaving the Sync capture orphaned, to silently swallow the
131
+ * first message of whatever request comes next. With one capture,
132
+ * ReadyForQuery is simply this request's end marker whether or not the
133
+ * Close itself was honoured.
134
+ */
135
+ sendCloseAndSyncMessages(args: Frontend.CloseMessageArgs, cb: CaptureCallback): Promise<any>;
136
+ /**
137
+ * Close(portal) + Close(statement) + Sync as a single round trip - used
138
+ * by PreparedStatement._maybeCloseWithPortal() when a Cursor's close()
139
+ * brings the owning PreparedStatement's refcount to 0, instead of two
140
+ * separately-batched Close+Sync round trips (Portal.close() then
141
+ * PreparedStatement._close()). Sync here is safe/intended: this is final
142
+ * teardown, no portal needs to survive past it.
143
+ */
144
+ sendClosePortalAndStatementMessages(args: {
145
+ portal: Frontend.CloseMessageArgs;
146
+ statement: Frontend.CloseMessageArgs;
147
+ }, cb: CaptureCallback): Promise<any>;
148
+ sendQueryMessage(sql: string, cb: CaptureCallback): Promise<any>;
149
+ /**
150
+ * COPY IN data. Unlike every other send*() here this pushes nothing onto
151
+ * the capture queue: while a COPY is in progress the server answers no
152
+ * individual CopyData message, so the response still belongs to the
153
+ * Query capture that opened the copy.
154
+ *
155
+ * Returns false when the socket's buffer is full, exactly as
156
+ * net.Socket.write() does - the caller is expected to stop writing until
157
+ * `cb` fires, which is what CopyFromStream hands straight to its own
158
+ * Writable callback.
159
+ */
160
+ sendCopyData(data: Buffer, cb?: Callback): boolean;
161
+ /** Ends a COPY IN normally; the server replies CommandComplete. */
162
+ sendCopyDone(cb?: Callback): boolean;
163
+ /**
164
+ * Aborts a COPY IN. The server discards the copy, reports `message` as an
165
+ * ErrorResponse and returns to its normal state - without this a failed
166
+ * import would leave the connection waiting for data forever.
167
+ */
168
+ sendCopyFail(message: string, cb?: Callback): boolean;
169
+ /**
170
+ * Stops reading from the socket, so a COPY OUT consumer that cannot keep
171
+ * up doesn't buffer the whole export in memory. Safe to call repeatedly.
172
+ */
173
+ pause(): void;
174
+ resume(): void;
34
175
  sendFlushMessage(cb?: Callback): void;
35
176
  sendTerminateMessage(cb?: Callback): void;
36
- sendSyncMessage(): void;
37
- capture(callback: CaptureCallback): Promise<any>;
177
+ sendSyncMessage(cb: CaptureCallback): Promise<any>;
178
+ /**
179
+ * The only way anything outside this class can receive a correlated
180
+ * backend response. Sends `data` and pushes `cb` onto the FIFO capture
181
+ * queue in the same call, so a response can never arrive before its
182
+ * capturer is registered, and a capturer can never be registered without
183
+ * a message actually going out for it - there is no separate, decoupled
184
+ * "start listening" step to race or misuse.
185
+ */
186
+ private _sendAndCapture;
38
187
  protected _removeListeners(): void;
39
188
  protected _reset(): void;
40
189
  protected _handleConnect(): void;
190
+ /**
191
+ * Called once the server is ready. With `targetSessionAttrs` set, the
192
+ * session has to be checked before it is handed over: a server that does
193
+ * not match is dropped and the next candidate tried, which is how
194
+ * `read-write` finds whichever node is currently the primary.
195
+ */
196
+ protected _finishConnect(): void;
197
+ /** Accepts this server, or drops it and moves to the next candidate. */
198
+ protected _applySessionAttrs(wanted: string, standby: boolean, readOnly: boolean): void;
41
199
  protected _handleClose(): void;
42
200
  protected _handleError(err: unknown): void;
201
+ /** Rejects and clears every still-open capture, e.g. on close/error. */
202
+ private _failPendingCaptures;
43
203
  protected _handleData(data: Buffer): void;
204
+ /**
205
+ * Routes one backend message to the capture at the front of the FIFO -
206
+ * the oldest still-open request, i.e. the one whose turn it is given the
207
+ * order messages were written to the socket. This *is* the request/
208
+ * response correlation, since the wire protocol has no id of its own.
209
+ * The same entry keeps receiving messages (ParseComplete, BindComplete,
210
+ * RowDescription, DataRow*, CommandComplete, ReadyForQuery, ...) until
211
+ * its callback calls `done()`, at which point it is dequeued and the
212
+ * next entry becomes the one messages are routed to.
213
+ */
214
+ private _dispatch;
44
215
  protected _resolvePassword(cb: (password: string) => void): void;
45
216
  protected _handleAuthenticationMessage(msg?: any): void;
217
+ /**
218
+ * tls-server-end-point: the server certificate hashed with the algorithm
219
+ * its own signature used (RFC 5929), which is why the DER has to be read
220
+ * rather than one of Node's fixed fingerprints taken.
221
+ */
222
+ protected _channelBindingData(socket: tls.TLSSocket): Buffer;
46
223
  protected _handleParameterStatus(msg: Protocol.ParameterStatusMessage): void;
47
224
  protected _handleBackendKeyData(msg: Protocol.BackendKeyDataMessage): void;
48
225
  protected _handleCommandComplete(msg: any): Protocol.CommandCompleteMessage;
49
- protected _send(data: Buffer, cb?: Callback): void;
226
+ /**
227
+ * Queues `data` for the socket instead of writing it straight through:
228
+ * everything handed to _send() during the same synchronous burst (e.g.
229
+ * many concurrent query() calls fired via Promise.all over one
230
+ * connection) is flushed together on the next tick, corked, so Node
231
+ * turns the whole batch into a single underlying writev rather than one
232
+ * socket write per call. Measured against postgres.js, which coalesces
233
+ * the same way at the application level (its connection.js write()/
234
+ * nextWrite() pair, flushing on a 1KB threshold or setImmediate): 50
235
+ * concurrent prepared executes cost it 3 socket writes where this cost
236
+ * us 50, and that write path was the single largest non-idle item in a
237
+ * CPU profile of the scenario.
238
+ *
239
+ * nextTick rather than setImmediate, deliberately: it still batches a
240
+ * whole synchronous burst, but a lone query doesn't have to wait for the
241
+ * event loop's check phase before its bytes go out - which would trade
242
+ * away the sequential/single-query scenarios to win the concurrent ones.
243
+ *
244
+ * Returns whether the socket could accept the data at all; the write
245
+ * itself is reported through `cb` (and a socket that dies before the
246
+ * flush rejects every pending capture via _failPendingCaptures()).
247
+ */
248
+ protected _send(data: Buffer | Buffer[], cb?: Callback): boolean;
249
+ /**
250
+ * Writes COPY bytes straight through instead of queueing them for the
251
+ * next tick like _send() does. Two reasons: during a copy the capture
252
+ * queue holds exactly one entry (the Query that opened it), so _send()'s
253
+ * "batch only when more than one request is in flight" test would never
254
+ * fire and every chunk would be written on its own anyway; and the
255
+ * caller needs write()'s own return value to know when to stop, which a
256
+ * deferred write cannot give it.
257
+ */
258
+ protected _sendCopy(data: Buffer | Buffer[], cb?: Callback): boolean;
259
+ /** Arrow property, not a method: used as a bare process.nextTick callback. */
260
+ private _flushPendingWrites;
50
261
  }