postgrejs 3.1.3 → 3.4.0

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 (80) hide show
  1. package/CHANGELOG.md +79 -1
  2. package/README.md +79 -13
  3. package/connection/connection.d.ts +97 -0
  4. package/connection/connection.js +141 -2
  5. package/connection/cursor.d.ts +2 -1
  6. package/connection/cursor.js +6 -6
  7. package/connection/intl-connection.d.ts +113 -3
  8. package/connection/intl-connection.js +530 -45
  9. package/connection/prepared-statement.d.ts +64 -1
  10. package/connection/prepared-statement.js +97 -6
  11. package/data-types/bool-type.js +0 -1
  12. package/data-types/box-type.js +0 -1
  13. package/data-types/bytea-type.js +5 -3
  14. package/data-types/char-type.js +2 -2
  15. package/data-types/circle-type.js +0 -1
  16. package/data-types/date-type.js +1 -2
  17. package/data-types/float4-type.js +4 -5
  18. package/data-types/float8-type.js +4 -6
  19. package/data-types/int2-type.js +2 -2
  20. package/data-types/int2-vector-type.js +3 -2
  21. package/data-types/int4-type.js +2 -2
  22. package/data-types/int8-type.js +2 -2
  23. package/data-types/json-type.js +2 -2
  24. package/data-types/jsonb-type.js +6 -5
  25. package/data-types/lseg-type.js +0 -1
  26. package/data-types/numeric-type.js +9 -4
  27. package/data-types/oid-type.js +2 -2
  28. package/data-types/oid-vector-type.js +3 -2
  29. package/data-types/point-type.js +0 -1
  30. package/data-types/time-type.js +1 -2
  31. package/data-types/timestamp-type.js +13 -3
  32. package/data-types/timestamptz-type.js +27 -10
  33. package/data-types/uuid-type.js +1 -2
  34. package/data-types/varchar-type.js +2 -2
  35. package/index.d.ts +4 -0
  36. package/index.js +4 -0
  37. package/interfaces/batch-result.d.ts +42 -0
  38. package/interfaces/batch-result.js +1 -0
  39. package/interfaces/command-result.d.ts +3 -2
  40. package/interfaces/copy-from-rows-options.d.ts +58 -0
  41. package/interfaces/copy-from-rows-options.js +1 -0
  42. package/interfaces/data-type.d.ts +0 -1
  43. package/interfaces/database-connection-params.d.ts +25 -0
  44. package/interfaces/pipeline-request.d.ts +16 -0
  45. package/interfaces/pipeline-request.js +1 -0
  46. package/interfaces/query-options.d.ts +18 -0
  47. package/interfaces/script-execute-options.d.ts +12 -0
  48. package/package.json +18 -3
  49. package/protocol/backend.js +6 -6
  50. package/protocol/buffer-reader.d.ts +7 -12
  51. package/protocol/buffer-reader.js +11 -58
  52. package/protocol/database-error.d.ts +16 -0
  53. package/protocol/database-error.js +15 -0
  54. package/protocol/frontend.js +86 -96
  55. package/protocol/pg-socket.d.ts +82 -33
  56. package/protocol/pg-socket.js +143 -86
  57. package/protocol/pgoutput.js +2 -2
  58. package/protocol/smart-buffer.d.ts +11 -29
  59. package/protocol/smart-buffer.js +19 -162
  60. package/types.d.ts +13 -1
  61. package/util/assert-integer.d.ts +43 -0
  62. package/util/assert-integer.js +58 -0
  63. package/util/bigint-methods.d.ts +0 -1
  64. package/util/bigint-methods.js +0 -22
  65. package/util/copy-from-rows.d.ts +56 -0
  66. package/util/copy-from-rows.js +95 -0
  67. package/util/decode-binaryarray.d.ts +1 -1
  68. package/util/decode-binaryarray.js +7 -7
  69. package/util/encode-binaryarray.js +6 -6
  70. package/util/encode-copy-binary.d.ts +48 -0
  71. package/util/encode-copy-binary.js +112 -0
  72. package/util/fast-parsefloat.d.ts +15 -0
  73. package/util/fast-parsefloat.js +141 -0
  74. package/util/get-parsers.js +4 -8
  75. package/util/parse-datetime.js +7 -0
  76. package/util/parse-pg-timestamp.d.ts +31 -0
  77. package/util/parse-pg-timestamp.js +199 -0
  78. package/util/parse-row.js +120 -0
  79. package/util/row-decoder.d.ts +49 -0
  80. package/util/row-decoder.js +63 -0
@@ -39,31 +39,31 @@ export class Frontend {
39
39
  this._io = new SmartBuffer(options?.buffer);
40
40
  }
41
41
  getSSLRequestMessage() {
42
- return this._io
43
- .start()
44
- .writeUInt32BE(8) // Length of message contents in bytes, including self.
45
- .writeUInt16BE(1234)
46
- .writeUInt16BE(5679)
47
- .flush();
42
+ const io = this._io.start();
43
+ io.writeUInt32BE(8); // Length of message contents in bytes, including self.
44
+ io.writeUInt16BE(1234);
45
+ io.writeUInt16BE(5679);
46
+ return io.flush();
48
47
  }
49
48
  getStartupMessage(args, minorVersion = Protocol.VERSION_MINOR) {
50
- const io = this._io
51
- .start()
52
- .writeInt32BE(0) // Preserve length
53
- .writeInt16BE(Protocol.VERSION_MAJOR)
54
- .writeInt16BE(minorVersion);
49
+ const io = this._io.start();
50
+ io.writeInt32BE(0); // Preserve length
51
+ io.writeInt16BE(Protocol.VERSION_MAJOR);
52
+ io.writeInt16BE(minorVersion);
55
53
  const entries = Object.entries(args);
56
54
  const l = entries.length;
57
55
  let k;
58
56
  let v;
59
57
  for (let i = 0; i < l; i++) {
60
58
  [k, v] = entries[i];
61
- if (k !== 'client_encoding')
62
- io.writeCString(k, 'utf8').writeCString(v, 'utf8');
59
+ if (k !== 'client_encoding') {
60
+ io.writeCString(k, 'utf8');
61
+ io.writeCString(v, 'utf8');
62
+ }
63
63
  }
64
- io.writeCString('client_encoding', 'utf8')
65
- .writeCString('UTF8', 'utf8')
66
- .writeUInt8(0);
64
+ io.writeCString('client_encoding', 'utf8');
65
+ io.writeCString('UTF8', 'utf8');
66
+ io.writeUInt8(0);
67
67
  return setLengthAndFlush(io, 0);
68
68
  }
69
69
  /**
@@ -73,51 +73,46 @@ export class Frontend {
73
73
  * down the normal one.
74
74
  */
75
75
  getCancelRequestMessage(processID, secretKey) {
76
- return this._io
77
- .start()
78
- .writeUInt32BE(12 + secretKey.length) // Length of message contents in bytes, including self.
79
- .writeUInt16BE(1234)
80
- .writeUInt16BE(5678)
81
- .writeUInt32BE(processID)
82
- .writeBuffer(secretKey)
83
- .flush();
76
+ const io = this._io.start();
77
+ io.writeUInt32BE(12 + secretKey.length); // Length of message contents in bytes, including self.
78
+ io.writeUInt16BE(1234);
79
+ io.writeUInt16BE(5678);
80
+ io.writeUInt32BE(processID);
81
+ io.writeBytes(secretKey);
82
+ return io.flush();
84
83
  }
85
84
  getPasswordMessage(password) {
86
- const io = this._io
87
- .start()
88
- .writeInt8(Protocol.FrontendMessageCode.PasswordMessage)
89
- .writeInt32BE(0) // Preserve header
90
- .writeCString(password, 'utf8');
85
+ const io = this._io.start();
86
+ io.writeInt8(Protocol.FrontendMessageCode.PasswordMessage);
87
+ io.writeInt32BE(0); // Preserve header
88
+ io.writeCString(password, 'utf8');
91
89
  return setLengthAndFlush(io, 1);
92
90
  }
93
91
  getSASLMessage(sasl) {
94
- const io = this._io
95
- .start()
96
- .writeInt8(Protocol.FrontendMessageCode.PasswordMessage)
97
- .writeInt32BE(0) // Preserve header
98
- .writeCString(sasl.mechanism, 'utf8')
99
- .writeLString(sasl.clientFirstMessage);
92
+ const io = this._io.start();
93
+ io.writeInt8(Protocol.FrontendMessageCode.PasswordMessage);
94
+ io.writeInt32BE(0); // Preserve header
95
+ io.writeCString(sasl.mechanism, 'utf8');
96
+ io.writeLString(sasl.clientFirstMessage);
100
97
  return setLengthAndFlush(io, 1);
101
98
  }
102
99
  getSASLFinalMessage(session) {
103
- const io = this._io
104
- .start()
105
- .writeInt8(Protocol.FrontendMessageCode.PasswordMessage)
106
- .writeInt32BE(0) // Preserve header
107
- .writeString(session.clientFinalMessage);
100
+ const io = this._io.start();
101
+ io.writeInt8(Protocol.FrontendMessageCode.PasswordMessage);
102
+ io.writeInt32BE(0); // Preserve header
103
+ io.writeString(session.clientFinalMessage);
108
104
  return setLengthAndFlush(io, 1);
109
105
  }
110
106
  getParseMessage(args) {
111
107
  if (args.statement && args.statement.length > 63)
112
108
  throw new Error('Query name length must be lower than 63');
113
109
  const { paramTypes } = args;
114
- const io = this._io
115
- .start()
116
- .writeInt8(Protocol.FrontendMessageCode.Parse)
117
- .writeInt32BE(0) // Preserve header
118
- .writeCString(args.statement || '', 'utf8')
119
- .writeCString(args.sql, 'utf8')
120
- .writeUInt16BE(paramTypes?.length || 0);
110
+ const io = this._io.start();
111
+ io.writeInt8(Protocol.FrontendMessageCode.Parse);
112
+ io.writeInt32BE(0); // Preserve header
113
+ io.writeCString(args.statement || '', 'utf8');
114
+ io.writeCString(args.sql, 'utf8');
115
+ io.writeUInt16BE(paramTypes?.length || 0);
121
116
  if (paramTypes) {
122
117
  let i;
123
118
  const l = paramTypes.length;
@@ -132,19 +127,18 @@ export class Frontend {
132
127
  throw new Error('Portal name length must be lower than 63');
133
128
  if (args.statement && args.statement.length > 63)
134
129
  throw new Error('Query name length must be lower than 63');
135
- const io = this._io
136
- .start()
137
- .writeInt8(Protocol.FrontendMessageCode.Bind)
138
- .writeInt32BE(0) // Preserve header
139
- .writeCString(args.portal || '', 'utf8')
140
- .writeCString(args.statement || '', 'utf8');
130
+ const io = this._io.start();
131
+ io.writeInt8(Protocol.FrontendMessageCode.Bind);
132
+ io.writeInt32BE(0); // Preserve header
133
+ io.writeCString(args.portal || '', 'utf8');
134
+ io.writeCString(args.statement || '', 'utf8');
141
135
  const { params, paramTypes, queryOptions } = args;
142
136
  const columnFormat = queryOptions.columnFormat != null
143
137
  ? queryOptions.columnFormat
144
138
  : DEFAULT_COLUMN_FORMAT;
145
139
  if (params && params.length) {
146
140
  io.writeInt16BE(params.length);
147
- const formatOffset = io.offset;
141
+ const formatOffset = io.position;
148
142
  const l = params.length;
149
143
  let i;
150
144
  for (i = 0; i < l; i++) {
@@ -172,7 +166,7 @@ export class Frontend {
172
166
  io.buffer.writeInt16BE(Protocol.DataFormat.binary, formatOffset + i * 2);
173
167
  // Preserve data length
174
168
  io.writeInt32BE(0);
175
- const dataOffset = io.offset;
169
+ const dataOffset = io.position;
176
170
  if (dt.elementsOID) {
177
171
  // If data type is array
178
172
  v = Array.isArray(v) ? v : [v];
@@ -181,7 +175,7 @@ export class Frontend {
181
175
  else {
182
176
  dt.encodeBinary(io, v, queryOptions);
183
177
  }
184
- io.buffer.writeInt32BE(io.length - dataOffset, dataOffset - 4); // Update length
178
+ io.buffer.writeInt32BE(io.size - dataOffset, dataOffset - 4); // Update length
185
179
  }
186
180
  else if (typeof dt.encodeText === 'function') {
187
181
  v = dt.elementsOID
@@ -195,9 +189,9 @@ export class Frontend {
195
189
  io.buffer.writeInt16BE(Protocol.DataFormat.binary, formatOffset + i * 2);
196
190
  // Preserve data length
197
191
  io.writeInt32BE(0);
198
- const dataOffset = io.offset;
199
- io.writeBuffer(v);
200
- io.buffer.writeInt32BE(io.length - dataOffset, dataOffset - 4); // Update length
192
+ const dataOffset = io.position;
193
+ io.writeBytes(v);
194
+ io.buffer.writeInt32BE(io.size - dataOffset, dataOffset - 4); // Update length
201
195
  }
202
196
  else {
203
197
  io.writeLString('' + v, 'utf8');
@@ -230,12 +224,11 @@ export class Frontend {
230
224
  ? 'Portal'
231
225
  : 'Statement name length must be lower than 63');
232
226
  }
233
- const io = this._io
234
- .start()
235
- .writeInt8(Protocol.FrontendMessageCode.Describe)
236
- .writeInt32BE(0) // Preserve header
237
- .writeUInt8(args.type.charCodeAt(0))
238
- .writeCString(args.name || '', 'utf8');
227
+ const io = this._io.start();
228
+ io.writeInt8(Protocol.FrontendMessageCode.Describe);
229
+ io.writeInt32BE(0); // Preserve header
230
+ io.writeUInt8(args.type.charCodeAt(0));
231
+ io.writeCString(args.name || '', 'utf8');
239
232
  return setLengthAndFlush(io, 1);
240
233
  }
241
234
  getExecuteMessage(args) {
@@ -243,12 +236,11 @@ export class Frontend {
243
236
  (args.fetchCount < 0 || args.fetchCount > 4294967295)) {
244
237
  throw new Error('fetchCount can be between 0 and 4294967295');
245
238
  }
246
- const io = this._io
247
- .start()
248
- .writeInt8(Protocol.FrontendMessageCode.Execute)
249
- .writeInt32BE(0) // Preserve header
250
- .writeCString(args.portal || '', 'utf8')
251
- .writeUInt32BE(args.fetchCount || 0);
239
+ const io = this._io.start();
240
+ io.writeInt8(Protocol.FrontendMessageCode.Execute);
241
+ io.writeInt32BE(0); // Preserve header
242
+ io.writeCString(args.portal || '', 'utf8');
243
+ io.writeUInt32BE(args.fetchCount || 0);
252
244
  return setLengthAndFlush(io, 1);
253
245
  }
254
246
  getCloseMessage(args) {
@@ -257,20 +249,18 @@ export class Frontend {
257
249
  ? 'Portal'
258
250
  : 'Statement name length must be lower than 63');
259
251
  }
260
- const io = this._io
261
- .start()
262
- .writeInt8(Protocol.FrontendMessageCode.Close)
263
- .writeInt32BE(0) // Preserve header
264
- .writeUInt8(args.type.charCodeAt(0))
265
- .writeCString(args.name || '', 'utf8');
252
+ const io = this._io.start();
253
+ io.writeInt8(Protocol.FrontendMessageCode.Close);
254
+ io.writeInt32BE(0); // Preserve header
255
+ io.writeUInt8(args.type.charCodeAt(0));
256
+ io.writeCString(args.name || '', 'utf8');
266
257
  return setLengthAndFlush(io, 1);
267
258
  }
268
259
  getQueryMessage(sql) {
269
- const io = this._io
270
- .start()
271
- .writeInt8(Protocol.FrontendMessageCode.Query)
272
- .writeInt32BE(0) // Preserve header
273
- .writeCString(sql || '', 'utf8');
260
+ const io = this._io.start();
261
+ io.writeInt8(Protocol.FrontendMessageCode.Query);
262
+ io.writeInt32BE(0); // Preserve header
263
+ io.writeCString(sql || '', 'utf8');
274
264
  return setLengthAndFlush(io, 1);
275
265
  }
276
266
  getFunctionCallMessage(args) {
@@ -279,12 +269,11 @@ export class Frontend {
279
269
  const fl = formats.length;
280
270
  const l = values.length;
281
271
  let i;
282
- const io = this._io
283
- .start()
284
- .writeInt8(Protocol.FrontendMessageCode.FunctionCall)
285
- .writeInt32BE(0) // Preserve header
286
- .writeInt32BE(functionId)
287
- .writeInt16BE(fl);
272
+ const io = this._io.start();
273
+ io.writeInt8(Protocol.FrontendMessageCode.FunctionCall);
274
+ io.writeInt32BE(0); // Preserve header
275
+ io.writeInt32BE(functionId);
276
+ io.writeInt16BE(fl);
288
277
  for (i = 0; i < fl; i++)
289
278
  io.writeInt16BE(formats[i]);
290
279
  io.writeInt16BE(l);
@@ -293,8 +282,10 @@ export class Frontend {
293
282
  v = values[i];
294
283
  if (v == null)
295
284
  io.writeInt32BE(-1);
296
- else
297
- io.writeInt32BE(v.length).writeBuffer(v);
285
+ else {
286
+ io.writeInt32BE(v.length);
287
+ io.writeBytes(v);
288
+ }
298
289
  }
299
290
  io.writeInt16BE(resultFormat ?? DataFormat.text);
300
291
  return setLengthAndFlush(io, 1);
@@ -315,11 +306,10 @@ export class Frontend {
315
306
  return StaticCopyDoneBuffer;
316
307
  }
317
308
  getCopyFailMessage(message) {
318
- const io = this._io
319
- .start()
320
- .writeInt8(Protocol.FrontendMessageCode.CopyFail)
321
- .writeInt32BE(0) // Preserve header
322
- .writeCString(message || '', 'utf8');
309
+ const io = this._io.start();
310
+ io.writeInt8(Protocol.FrontendMessageCode.CopyFail);
311
+ io.writeInt32BE(0); // Preserve header
312
+ io.writeCString(message || '', 'utf8');
323
313
  return setLengthAndFlush(io, 1);
324
314
  }
325
315
  getFlushMessage() {
@@ -333,6 +323,6 @@ export class Frontend {
333
323
  }
334
324
  }
335
325
  function setLengthAndFlush(io, lengthOffset) {
336
- io.buffer.writeUInt32BE(io.length - lengthOffset, lengthOffset);
326
+ io.buffer.writeUInt32BE(io.size - lengthOffset, lengthOffset);
337
327
  return io.flush();
338
328
  }
@@ -22,8 +22,6 @@ export declare class PgSocket extends SafeEventEmitter {
22
22
  private _secretKey?;
23
23
  private _protocolNegotiation?;
24
24
  private _captureQueue;
25
- private _pendingWrites;
26
- private _flushScheduled;
27
25
  private _hosts;
28
26
  private _hostIndex;
29
27
  private _sessionAttrsChecked;
@@ -95,6 +93,37 @@ export declare class PgSocket extends SafeEventEmitter {
95
93
  bind: Frontend.BindMessageArgs;
96
94
  describe: Frontend.DescribeMessageArgs;
97
95
  execute: Frontend.ExecuteMessageArgs;
96
+ before?: string;
97
+ after?: string;
98
+ }, cb: CaptureCallback): Promise<any>;
99
+ /**
100
+ * Parse + Bind + Describe(portal) + Execute per statement, then a SINGLE
101
+ * Sync for the lot - the wire shape behind Connection.pipeline().
102
+ *
103
+ * The same framing as sendBatchBindExecuteMessages(), and the same
104
+ * consequences, except that each statement carries its own Parse rather
105
+ * than reusing one prepared statement: the statements share one implicit
106
+ * transaction, and PostgreSQL discards everything between an error and
107
+ * the Sync, so a rejected statement stops the ones behind it.
108
+ *
109
+ * Describe is not optional here even though nothing caches its answer.
110
+ * Without it the server sends DataRows for a row-returning statement with
111
+ * no RowDescription in front of them, leaving the column names and types
112
+ * unknown - the reason the one-shot path in sendExtendedQueryMessages()
113
+ * includes it too. It costs about a quarter of a millisecond across
114
+ * twenty statements, measured.
115
+ *
116
+ * Every Execute is unlimited (fetchCount 0): a portal that suspended
117
+ * mid-pipeline would answer PortalSuspended instead of CommandComplete
118
+ * and desynchronise the positional statement-to-result mapping.
119
+ */
120
+ sendPipelineMessages(args: {
121
+ statements: {
122
+ parse: Frontend.ParseMessageArgs;
123
+ bind: Frontend.BindMessageArgs;
124
+ describe: Frontend.DescribeMessageArgs;
125
+ execute: Frontend.ExecuteMessageArgs;
126
+ }[];
98
127
  }, cb: CaptureCallback): Promise<any>;
99
128
  /**
100
129
  * Parse + Describe(statement) + Sync as a single round trip - used by
@@ -119,6 +148,33 @@ export declare class PgSocket extends SafeEventEmitter {
119
148
  sendBindExecuteMessages(args: {
120
149
  bind: Frontend.BindMessageArgs;
121
150
  execute: Frontend.ExecuteMessageArgs;
151
+ before?: string;
152
+ after?: string;
153
+ }, cb: CaptureCallback): Promise<any>;
154
+ /**
155
+ * One Bind+Execute pair per parameter set against the same prepared
156
+ * statement, then a SINGLE Sync for the lot - the wire shape behind
157
+ * PreparedStatement.executeBatch().
158
+ *
159
+ * The single Sync is the whole point, and it is also what gives the
160
+ * batch its semantics. Sending Bind/Execute/Sync per set (what N
161
+ * separate execute() calls do, even pipelined) makes the server finish
162
+ * an implicit transaction and answer ReadyForQuery every time; one Sync
163
+ * at the end collapses that to one, which is where the bulk of the
164
+ * speedup comes from. It also means the sets share one implicit
165
+ * transaction, and that PostgreSQL discards everything between an error
166
+ * and the Sync - so a set that fails stops the ones behind it from
167
+ * running at all. Both of those are inherent to this framing rather
168
+ * than choices made above it.
169
+ *
170
+ * Every Execute here must be unlimited (fetchCount 0): a portal that
171
+ * suspends mid-batch would answer PortalSuspended instead of
172
+ * CommandComplete and desynchronise the positional set-to-result
173
+ * mapping the caller relies on.
174
+ */
175
+ sendBatchBindExecuteMessages(args: {
176
+ binds: Frontend.BindMessageArgs[];
177
+ execute: Frontend.ExecuteMessageArgs;
122
178
  }, cb: CaptureCallback): Promise<any>;
123
179
  /**
124
180
  * Bind + Describe(portal) + Flush as a single round trip (NOT Sync - see
@@ -195,6 +251,24 @@ export declare class PgSocket extends SafeEventEmitter {
195
251
  * "start listening" step to race or misuse.
196
252
  */
197
253
  private _sendAndCapture;
254
+ /**
255
+ * Parse + Bind + Execute for a parameterless utility statement, appended
256
+ * to a message list that is about to be closed by one Sync - how
257
+ * sendBindExecuteMessages() and sendExtendedQueryMessages() carry their
258
+ * `before`/`after` SQL (today, SAVEPOINT and RELEASE) in the same round
259
+ * trip as the statement itself rather than as a round trip each.
260
+ *
261
+ * Everything here goes through the unnamed statement and the unnamed
262
+ * portal. That is safe in this order: the caller's own Bind names a
263
+ * prepared statement rather than the unnamed one, and PostgreSQL
264
+ * processes the messages in sequence, so each Bind only ever discards an
265
+ * unnamed portal whose Execute has already run.
266
+ *
267
+ * No Describe: neither statement returns rows, so there is no
268
+ * RowDescription worth asking for. Result formats come from the caller's
269
+ * own Bind args, which likewise cost nothing against no columns.
270
+ */
271
+ private _pushUtilityStatement;
198
272
  protected _removeListeners(): void;
199
273
  protected _reset(): void;
200
274
  protected _handleConnect(): void;
@@ -235,39 +309,14 @@ export declare class PgSocket extends SafeEventEmitter {
235
309
  protected _handleNegotiateProtocolVersion(msg: Protocol.NegotiateProtocolVersionMessage): void;
236
310
  protected _handleBackendKeyData(msg: Protocol.BackendKeyDataMessage): void;
237
311
  protected _handleCommandComplete(msg: any): Protocol.CommandCompleteMessage;
238
- /**
239
- * Queues `data` for the socket instead of writing it straight through:
240
- * everything handed to _send() during the same synchronous burst (e.g.
241
- * many concurrent query() calls fired via Promise.all over one
242
- * connection) is flushed together on the next tick, corked, so Node
243
- * turns the whole batch into a single underlying writev rather than one
244
- * socket write per call. Measured against postgres.js, which coalesces
245
- * the same way at the application level (its connection.js write()/
246
- * nextWrite() pair, flushing on a 1KB threshold or setImmediate): 50
247
- * concurrent prepared executes cost it 3 socket writes where this cost
248
- * us 50, and that write path was the single largest non-idle item in a
249
- * CPU profile of the scenario.
250
- *
251
- * nextTick rather than setImmediate, deliberately: it still batches a
252
- * whole synchronous burst, but a lone query doesn't have to wait for the
253
- * event loop's check phase before its bytes go out - which would trade
254
- * away the sequential/single-query scenarios to win the concurrent ones.
255
- *
256
- * Returns whether the socket could accept the data at all; the write
257
- * itself is reported through `cb` (and a socket that dies before the
258
- * flush rejects every pending capture via _failPendingCaptures()).
259
- */
260
312
  protected _send(data: Buffer | Buffer[], cb?: Callback): boolean;
261
313
  /**
262
- * Writes COPY bytes straight through instead of queueing them for the
263
- * next tick like _send() does. Two reasons: during a copy the capture
264
- * queue holds exactly one entry (the Query that opened it), so _send()'s
265
- * "batch only when more than one request is in flight" test would never
266
- * fire and every chunk would be written on its own anyway; and the
267
- * caller needs write()'s own return value to know when to stop, which a
268
- * deferred write cannot give it.
314
+ * Same write as _send(), but reporting backpressure instead of
315
+ * writability: a COPY IN producer needs write()'s own return value to
316
+ * know when to stop pushing, whereas _send()'s callers only need to know
317
+ * whether the socket could take the bytes at all. Kept separate rather
318
+ * than folded into _send() precisely so those two meanings of `false`
319
+ * cannot be confused at a call site.
269
320
  */
270
321
  protected _sendCopy(data: Buffer | Buffer[], cb?: Callback): boolean;
271
- /** Arrow property, not a method: used as a bare process.nextTick callback. */
272
- private _flushPendingWrites;
273
322
  }