postgrejs 3.1.3 → 3.3.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.
- package/CHANGELOG.md +42 -1
- package/README.md +25 -9
- package/connection/cursor.d.ts +2 -1
- package/connection/cursor.js +6 -6
- package/connection/intl-connection.js +10 -14
- package/connection/prepared-statement.js +2 -1
- package/data-types/bool-type.js +0 -1
- package/data-types/box-type.js +0 -1
- package/data-types/bytea-type.js +5 -3
- package/data-types/char-type.js +2 -2
- package/data-types/circle-type.js +0 -1
- package/data-types/date-type.js +1 -2
- package/data-types/float4-type.js +2 -4
- package/data-types/float8-type.js +2 -5
- package/data-types/int2-type.js +0 -1
- package/data-types/int2-vector-type.js +3 -2
- package/data-types/int4-type.js +0 -1
- package/data-types/int8-type.js +0 -1
- package/data-types/json-type.js +2 -2
- package/data-types/jsonb-type.js +6 -5
- package/data-types/lseg-type.js +0 -1
- package/data-types/numeric-type.js +2 -4
- package/data-types/oid-type.js +0 -1
- package/data-types/oid-vector-type.js +3 -2
- package/data-types/point-type.js +0 -1
- package/data-types/time-type.js +1 -2
- package/data-types/timestamp-type.js +13 -3
- package/data-types/timestamptz-type.js +27 -10
- package/data-types/uuid-type.js +1 -2
- package/data-types/varchar-type.js +2 -2
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/interfaces/command-result.d.ts +3 -2
- package/interfaces/data-type.d.ts +0 -1
- package/interfaces/query-options.d.ts +12 -0
- package/interfaces/script-execute-options.d.ts +12 -0
- package/package.json +14 -2
- package/protocol/backend.js +6 -6
- package/protocol/buffer-reader.d.ts +7 -12
- package/protocol/buffer-reader.js +11 -58
- package/protocol/frontend.js +86 -96
- package/protocol/pg-socket.d.ts +6 -33
- package/protocol/pg-socket.js +19 -74
- package/protocol/pgoutput.js +2 -2
- package/protocol/smart-buffer.d.ts +11 -29
- package/protocol/smart-buffer.js +19 -162
- package/types.d.ts +13 -1
- package/util/bigint-methods.d.ts +0 -1
- package/util/bigint-methods.js +0 -22
- package/util/decode-binaryarray.d.ts +1 -1
- package/util/decode-binaryarray.js +7 -7
- package/util/encode-binaryarray.js +6 -6
- package/util/fast-parsefloat.d.ts +15 -0
- package/util/fast-parsefloat.js +141 -0
- package/util/get-parsers.js +4 -8
- package/util/parse-datetime.js +7 -0
- package/util/parse-pg-timestamp.d.ts +31 -0
- package/util/parse-pg-timestamp.js +199 -0
- package/util/parse-row.js +120 -0
- package/util/row-decoder.d.ts +49 -0
- package/util/row-decoder.js +63 -0
package/protocol/frontend.js
CHANGED
|
@@ -39,31 +39,31 @@ export class Frontend {
|
|
|
39
39
|
this._io = new SmartBuffer(options?.buffer);
|
|
40
40
|
}
|
|
41
41
|
getSSLRequestMessage() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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')
|
|
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
|
-
|
|
66
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
199
|
-
io.
|
|
200
|
-
io.buffer.writeInt32BE(io.
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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)
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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.
|
|
326
|
+
io.buffer.writeUInt32BE(io.size - lengthOffset, lengthOffset);
|
|
337
327
|
return io.flush();
|
|
338
328
|
}
|
package/protocol/pg-socket.d.ts
CHANGED
|
@@ -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;
|
|
@@ -235,39 +233,14 @@ export declare class PgSocket extends SafeEventEmitter {
|
|
|
235
233
|
protected _handleNegotiateProtocolVersion(msg: Protocol.NegotiateProtocolVersionMessage): void;
|
|
236
234
|
protected _handleBackendKeyData(msg: Protocol.BackendKeyDataMessage): void;
|
|
237
235
|
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
236
|
protected _send(data: Buffer | Buffer[], cb?: Callback): boolean;
|
|
261
237
|
/**
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
* deferred write cannot give it.
|
|
238
|
+
* Same write as _send(), but reporting backpressure instead of
|
|
239
|
+
* writability: a COPY IN producer needs write()'s own return value to
|
|
240
|
+
* know when to stop pushing, whereas _send()'s callers only need to know
|
|
241
|
+
* whether the socket could take the bytes at all. Kept separate rather
|
|
242
|
+
* than folded into _send() precisely so those two meanings of `false`
|
|
243
|
+
* cannot be confused at a call site.
|
|
269
244
|
*/
|
|
270
245
|
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
246
|
}
|
package/protocol/pg-socket.js
CHANGED
|
@@ -26,8 +26,6 @@ export class PgSocket extends SafeEventEmitter {
|
|
|
26
26
|
_secretKey;
|
|
27
27
|
_protocolNegotiation;
|
|
28
28
|
_captureQueue = new DoublyLinked();
|
|
29
|
-
_pendingWrites = [];
|
|
30
|
-
_flushScheduled = false;
|
|
31
29
|
_hosts = [];
|
|
32
30
|
_hostIndex = 0;
|
|
33
31
|
_sessionAttrsChecked = false;
|
|
@@ -421,9 +419,6 @@ export class PgSocket extends SafeEventEmitter {
|
|
|
421
419
|
this._processID = undefined;
|
|
422
420
|
this._secretKey = undefined;
|
|
423
421
|
this._saslSession = undefined;
|
|
424
|
-
// Anything still queued belonged to the connection being reset - it
|
|
425
|
-
// must never reach a freshly connected socket.
|
|
426
|
-
this._pendingWrites = [];
|
|
427
422
|
}
|
|
428
423
|
_handleConnect() {
|
|
429
424
|
const socket = this._socket;
|
|
@@ -740,65 +735,38 @@ export class PgSocket extends SafeEventEmitter {
|
|
|
740
735
|
result.rowCount = parseInt(m[2], 10);
|
|
741
736
|
return result;
|
|
742
737
|
}
|
|
743
|
-
/**
|
|
744
|
-
* Queues `data` for the socket instead of writing it straight through:
|
|
745
|
-
* everything handed to _send() during the same synchronous burst (e.g.
|
|
746
|
-
* many concurrent query() calls fired via Promise.all over one
|
|
747
|
-
* connection) is flushed together on the next tick, corked, so Node
|
|
748
|
-
* turns the whole batch into a single underlying writev rather than one
|
|
749
|
-
* socket write per call. Measured against postgres.js, which coalesces
|
|
750
|
-
* the same way at the application level (its connection.js write()/
|
|
751
|
-
* nextWrite() pair, flushing on a 1KB threshold or setImmediate): 50
|
|
752
|
-
* concurrent prepared executes cost it 3 socket writes where this cost
|
|
753
|
-
* us 50, and that write path was the single largest non-idle item in a
|
|
754
|
-
* CPU profile of the scenario.
|
|
755
|
-
*
|
|
756
|
-
* nextTick rather than setImmediate, deliberately: it still batches a
|
|
757
|
-
* whole synchronous burst, but a lone query doesn't have to wait for the
|
|
758
|
-
* event loop's check phase before its bytes go out - which would trade
|
|
759
|
-
* away the sequential/single-query scenarios to win the concurrent ones.
|
|
760
|
-
*
|
|
761
|
-
* Returns whether the socket could accept the data at all; the write
|
|
762
|
-
* itself is reported through `cb` (and a socket that dies before the
|
|
763
|
-
* flush rejects every pending capture via _failPendingCaptures()).
|
|
764
|
-
*/
|
|
765
738
|
_send(data, cb) {
|
|
766
|
-
|
|
739
|
+
const socket = this._socket;
|
|
740
|
+
if (!socket || !socket.writable)
|
|
767
741
|
return false;
|
|
768
|
-
if (Array.isArray(data)) {
|
|
742
|
+
if (!Array.isArray(data)) {
|
|
743
|
+
socket.write(data, cb);
|
|
744
|
+
return true;
|
|
745
|
+
}
|
|
746
|
+
socket.cork();
|
|
747
|
+
try {
|
|
769
748
|
const l = data.length;
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
cb: i === l - 1 ? cb : undefined,
|
|
774
|
-
});
|
|
749
|
+
let i;
|
|
750
|
+
for (i = 0; i < l; i++)
|
|
751
|
+
socket.write(data[i], i === l - 1 ? cb : undefined);
|
|
775
752
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
if (this._captureQueue.length > 1) {
|
|
779
|
-
if (!this._flushScheduled) {
|
|
780
|
-
this._flushScheduled = true;
|
|
781
|
-
process.nextTick(this._flushPendingWrites);
|
|
782
|
-
}
|
|
753
|
+
finally {
|
|
754
|
+
socket.uncork();
|
|
783
755
|
}
|
|
784
|
-
else
|
|
785
|
-
this._flushPendingWrites();
|
|
786
756
|
return true;
|
|
787
757
|
}
|
|
788
758
|
/**
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
*
|
|
792
|
-
*
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
* deferred write cannot give it.
|
|
759
|
+
* Same write as _send(), but reporting backpressure instead of
|
|
760
|
+
* writability: a COPY IN producer needs write()'s own return value to
|
|
761
|
+
* know when to stop pushing, whereas _send()'s callers only need to know
|
|
762
|
+
* whether the socket could take the bytes at all. Kept separate rather
|
|
763
|
+
* than folded into _send() precisely so those two meanings of `false`
|
|
764
|
+
* cannot be confused at a call site.
|
|
796
765
|
*/
|
|
797
766
|
_sendCopy(data, cb) {
|
|
798
767
|
const socket = this._socket;
|
|
799
768
|
if (!socket || !socket.writable)
|
|
800
769
|
return false;
|
|
801
|
-
this._flushPendingWrites();
|
|
802
770
|
if (!Array.isArray(data))
|
|
803
771
|
return socket.write(data, cb);
|
|
804
772
|
socket.cork();
|
|
@@ -814,27 +782,4 @@ export class PgSocket extends SafeEventEmitter {
|
|
|
814
782
|
socket.uncork();
|
|
815
783
|
}
|
|
816
784
|
}
|
|
817
|
-
/** Arrow property, not a method: used as a bare process.nextTick callback. */
|
|
818
|
-
_flushPendingWrites = () => {
|
|
819
|
-
this._flushScheduled = false;
|
|
820
|
-
const pending = this._pendingWrites;
|
|
821
|
-
if (!pending.length)
|
|
822
|
-
return;
|
|
823
|
-
this._pendingWrites = [];
|
|
824
|
-
const socket = this._socket;
|
|
825
|
-
if (!socket || !socket.writable)
|
|
826
|
-
return;
|
|
827
|
-
socket.cork();
|
|
828
|
-
try {
|
|
829
|
-
const l = pending.length;
|
|
830
|
-
let i;
|
|
831
|
-
for (i = 0; i < l; i++) {
|
|
832
|
-
const { data, cb } = pending[i];
|
|
833
|
-
socket.write(data, cb);
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
finally {
|
|
837
|
-
socket.uncork();
|
|
838
|
-
}
|
|
839
|
-
};
|
|
840
785
|
}
|
package/protocol/pgoutput.js
CHANGED
|
@@ -106,7 +106,7 @@ export class PgOutputDecoder {
|
|
|
106
106
|
const lsn = readLsn(io);
|
|
107
107
|
const prefix = io.readCString();
|
|
108
108
|
const length = io.readUInt32BE();
|
|
109
|
-
return { kind: 'message', prefix, lsn, content: io.
|
|
109
|
+
return { kind: 'message', prefix, lsn, content: io.readBytes(length) };
|
|
110
110
|
}
|
|
111
111
|
default:
|
|
112
112
|
return { kind: 'unknown', code };
|
|
@@ -134,7 +134,7 @@ export class PgOutputDecoder {
|
|
|
134
134
|
}
|
|
135
135
|
else {
|
|
136
136
|
const length = io.readUInt32BE();
|
|
137
|
-
out[column.name] = io.
|
|
137
|
+
out[column.name] = io.readBytes(length).toString('utf8');
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
return out;
|
|
@@ -1,37 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FlexyBuffer } from 'flexy-buffer';
|
|
2
2
|
export interface SmartBufferConfig {
|
|
3
3
|
pageSize?: number;
|
|
4
4
|
maxLength?: number;
|
|
5
|
-
|
|
5
|
+
houseKeepMs?: number;
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Adds the PostgreSQL wire format's two string encodings on top of
|
|
9
|
+
* flexy-buffer's `FlexyBuffer`: a NUL-terminated C string, and a
|
|
10
|
+
* length-prefixed string where a `null`/`undefined` value is written as a
|
|
11
|
+
* length of -1 with no bytes following.
|
|
12
|
+
*/
|
|
13
|
+
export declare class SmartBuffer extends FlexyBuffer {
|
|
8
14
|
static DEFAULT_PAGE_SIZE: number;
|
|
9
15
|
static DEFAULT_MAX_SIZE: number;
|
|
10
|
-
private readonly _houseKeepInterval;
|
|
11
|
-
private _houseKeepTimer?;
|
|
12
|
-
private _stMaxPages;
|
|
13
|
-
private _length;
|
|
14
|
-
readonly pageSize: number;
|
|
15
|
-
readonly maxSize: number;
|
|
16
16
|
constructor(cfg?: SmartBufferConfig);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
start(): this;
|
|
20
|
-
flush(): Buffer;
|
|
21
|
-
growSize(len: number): this;
|
|
22
|
-
writeCString(str: string, encoding?: BufferEncoding): this;
|
|
23
|
-
writeLString(str?: string, encoding?: BufferEncoding): this;
|
|
24
|
-
writeString(str: string, encoding?: BufferEncoding): this;
|
|
25
|
-
writeInt8(n: number): this;
|
|
26
|
-
writeUInt8(n: number): this;
|
|
27
|
-
writeUInt16BE(n: number): this;
|
|
28
|
-
writeUInt32BE(n: number): this;
|
|
29
|
-
writeInt16BE(n: number): this;
|
|
30
|
-
writeInt32BE(n: number): this;
|
|
31
|
-
writeBigInt64BE(n: bigint | number): this;
|
|
32
|
-
writeFloatBE(n: number): this;
|
|
33
|
-
writeDoubleBE(n: number): this;
|
|
34
|
-
writeBuffer(buffer: Buffer): this;
|
|
35
|
-
private ensureSize;
|
|
36
|
-
private _houseKeep;
|
|
17
|
+
writeCString(str: string, encoding?: BufferEncoding): number;
|
|
18
|
+
writeLString(str?: string, encoding?: BufferEncoding): number;
|
|
37
19
|
}
|