postgrejs 2.23.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +152 -1
- package/LICENSE +26 -17
- package/README.md +195 -175
- package/connection/connection.d.ts +152 -13
- package/connection/connection.js +296 -49
- package/connection/copy-stream.d.ts +88 -0
- package/connection/copy-stream.js +244 -0
- package/connection/cursor.js +20 -13
- package/connection/intl-connection.d.ts +68 -3
- package/connection/intl-connection.js +414 -57
- package/connection/large-object.d.ts +65 -0
- package/connection/large-object.js +151 -0
- package/connection/logical-replication.d.ts +121 -0
- package/connection/logical-replication.js +279 -0
- package/connection/pool.d.ts +61 -4
- package/connection/pool.js +177 -29
- package/connection/portal.d.ts +18 -4
- package/connection/portal.js +53 -59
- package/connection/prepared-statement.d.ts +22 -1
- package/connection/prepared-statement.js +117 -97
- package/data-type-map.js +4 -2
- package/data-types/bool-type.js +14 -10
- package/data-types/box-type.js +15 -8
- package/data-types/bytea-type.js +9 -5
- package/data-types/char-type.js +11 -5
- package/data-types/circle-type.js +15 -7
- package/data-types/date-type.js +25 -19
- package/data-types/float4-type.js +10 -3
- package/data-types/float8-type.js +12 -3
- package/data-types/int2-type.js +9 -4
- package/data-types/int2-vector-type.js +9 -4
- package/data-types/int4-type.js +11 -5
- package/data-types/int8-type.js +22 -7
- package/data-types/json-type.js +17 -8
- package/data-types/jsonb-type.js +20 -14
- package/data-types/lseg-type.js +16 -8
- package/data-types/numeric-type.d.ts +8 -0
- package/data-types/numeric-type.js +112 -8
- package/data-types/oid-type.d.ts +0 -1
- package/data-types/oid-type.js +9 -10
- package/data-types/oid-vector-type.d.ts +9 -0
- package/data-types/oid-vector-type.js +51 -0
- package/data-types/point-type.js +14 -6
- package/data-types/time-type.js +20 -13
- package/data-types/timestamp-type.js +32 -25
- package/data-types/timestamptz-type.js +33 -21
- package/data-types/uuid-type.js +21 -11
- package/data-types/varchar-type.js +16 -4
- package/index.d.ts +5 -0
- package/index.js +5 -0
- package/interfaces/data-type.d.ts +5 -3
- package/interfaces/database-connection-params.d.ts +95 -0
- package/interfaces/query-options.d.ts +17 -0
- package/interfaces/script-execute-options.d.ts +17 -0
- package/package.json +2 -3
- package/protocol/backend.d.ts +6 -1
- package/protocol/backend.js +70 -45
- package/protocol/buffer-reader.d.ts +0 -2
- package/protocol/buffer-reader.js +3 -10
- package/protocol/cert-signature.d.ts +12 -0
- package/protocol/cert-signature.js +114 -0
- package/protocol/frontend.d.ts +16 -0
- package/protocol/frontend.js +57 -6
- package/protocol/pg-socket.d.ts +220 -9
- package/protocol/pg-socket.js +539 -102
- package/protocol/pgoutput.d.ts +76 -0
- package/protocol/pgoutput.js +155 -0
- package/protocol/protocol.d.ts +3 -4
- package/protocol/protocol.js +1 -1
- package/protocol/sasl.d.ts +11 -1
- package/protocol/sasl.js +35 -4
- package/protocol/smart-buffer.d.ts +0 -2
- package/protocol/smart-buffer.js +11 -16
- package/types.d.ts +5 -3
- package/util/abort-signal.d.ts +24 -0
- package/util/abort-signal.js +63 -0
- package/util/array-calculatedim.js +3 -1
- package/util/connection-config.js +77 -3
- package/util/decode-binaryarray.d.ts +1 -1
- package/util/decode-binaryarray.js +15 -6
- package/util/encode-binaryarray.js +7 -3
- package/util/escape-identifier.d.ts +14 -0
- package/util/escape-identifier.js +32 -0
- package/util/escape-literal.js +3 -0
- package/util/fast-parseint.d.ts +1 -0
- package/util/fast-parseint.js +12 -17
- package/util/format-datetime.d.ts +10 -0
- package/util/format-datetime.js +71 -0
- package/util/get-parsers.js +31 -10
- package/util/parse-array.js +2 -2
- package/util/parse-bytea.d.ts +2 -0
- package/util/parse-bytea.js +83 -0
- package/util/parse-datetime.d.ts +3 -1
- package/util/parse-datetime.js +86 -17
- package/util/parse-row.d.ts +3 -1
- package/util/parse-row.js +60 -5
- package/util/parse-time.js +8 -1
- package/util/sql-tag.d.ts +60 -0
- package/util/sql-tag.js +202 -0
- package/util/starts-transaction.d.ts +38 -0
- package/util/starts-transaction.js +148 -0
- package/util/stringify-arrayliteral.js +13 -1
- package/util/wrap-row-description.js +13 -4
- package/util/convert-row-to-object.d.ts +0 -2
- package/util/convert-row-to-object.js +0 -9
|
@@ -39,6 +39,8 @@ export class BufferReader {
|
|
|
39
39
|
}
|
|
40
40
|
readCString(encoding) {
|
|
41
41
|
const idx = this.buffer.indexOf(0, this.offset);
|
|
42
|
+
if (idx === -1)
|
|
43
|
+
throw new Error('Eof in buffer detected (readCString)');
|
|
42
44
|
const v = this.buffer.toString(encoding, this.offset, idx);
|
|
43
45
|
this.offset = idx + 1;
|
|
44
46
|
return v;
|
|
@@ -55,19 +57,10 @@ export class BufferReader {
|
|
|
55
57
|
if (len)
|
|
56
58
|
this._checkReadable(len);
|
|
57
59
|
const end = len !== undefined ? this.offset + len : this.length;
|
|
58
|
-
const buf = this.buffer.
|
|
60
|
+
const buf = this.buffer.subarray(this.offset, end);
|
|
59
61
|
this.offset = end;
|
|
60
62
|
return buf;
|
|
61
63
|
}
|
|
62
|
-
moveBy(n) {
|
|
63
|
-
return this.moveTo(this.offset + n);
|
|
64
|
-
}
|
|
65
|
-
moveTo(pos) {
|
|
66
|
-
if (pos >= this.length)
|
|
67
|
-
throw new Error('Eof in buffer detected');
|
|
68
|
-
this.offset = pos;
|
|
69
|
-
return this;
|
|
70
|
-
}
|
|
71
64
|
_checkReadable(size) {
|
|
72
65
|
if (this.offset + size - 1 >= this.length)
|
|
73
66
|
throw new Error('Eof in buffer detected');
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the hash a certificate was signed with, which is the hash
|
|
3
|
+
* `tls-server-end-point` channel binding has to use (RFC 5929).
|
|
4
|
+
*
|
|
5
|
+
* Node exposes a certificate's SHA-1, SHA-256 and SHA-512 fingerprints but
|
|
6
|
+
* not the algorithm it was signed with, and X509Certificate.toLegacyObject()
|
|
7
|
+
* does not carry it either - so the DER has to be read. Only as far as the
|
|
8
|
+
* signature algorithm's OID: a Certificate is a SEQUENCE of tbsCertificate,
|
|
9
|
+
* signatureAlgorithm and signatureValue, so this skips the first and reads
|
|
10
|
+
* the OID out of the second.
|
|
11
|
+
*/
|
|
12
|
+
export declare function signatureHashOfCertificate(der: Buffer): string;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the hash a certificate was signed with, which is the hash
|
|
3
|
+
* `tls-server-end-point` channel binding has to use (RFC 5929).
|
|
4
|
+
*
|
|
5
|
+
* Node exposes a certificate's SHA-1, SHA-256 and SHA-512 fingerprints but
|
|
6
|
+
* not the algorithm it was signed with, and X509Certificate.toLegacyObject()
|
|
7
|
+
* does not carry it either - so the DER has to be read. Only as far as the
|
|
8
|
+
* signature algorithm's OID: a Certificate is a SEQUENCE of tbsCertificate,
|
|
9
|
+
* signatureAlgorithm and signatureValue, so this skips the first and reads
|
|
10
|
+
* the OID out of the second.
|
|
11
|
+
*/
|
|
12
|
+
function readLength(data, index) {
|
|
13
|
+
let length = data[index++];
|
|
14
|
+
if (length < 0x80)
|
|
15
|
+
return { length, index };
|
|
16
|
+
const lengthBytes = length & 0x7f;
|
|
17
|
+
if (lengthBytes > 4)
|
|
18
|
+
throw certError('length field too large', data);
|
|
19
|
+
length = 0;
|
|
20
|
+
for (let i = 0; i < lengthBytes; i++)
|
|
21
|
+
length = (length << 8) | data[index++];
|
|
22
|
+
return { length, index };
|
|
23
|
+
}
|
|
24
|
+
function expectSequence(data, index) {
|
|
25
|
+
if (data[index++] !== 0x30)
|
|
26
|
+
throw certError('expected a SEQUENCE', data);
|
|
27
|
+
return { index: readLength(data, index).index };
|
|
28
|
+
}
|
|
29
|
+
function readOID(data, index) {
|
|
30
|
+
if (data[index++] !== 0x06)
|
|
31
|
+
throw certError('expected an OID', data);
|
|
32
|
+
const { length, index: start } = readLength(data, index);
|
|
33
|
+
index = start;
|
|
34
|
+
const end = index + length;
|
|
35
|
+
// The first byte packs the first two arcs as 40 * a + b.
|
|
36
|
+
const first = data[index++];
|
|
37
|
+
let oid = Math.floor(first / 40) + '.' + (first % 40);
|
|
38
|
+
let value = 0;
|
|
39
|
+
while (index < end) {
|
|
40
|
+
const byte = data[index++];
|
|
41
|
+
value = (value << 7) | (byte & 0x7f);
|
|
42
|
+
// The high bit marks a continuation, so an arc ends when it is clear.
|
|
43
|
+
if (!(byte & 0x80)) {
|
|
44
|
+
oid += '.' + value;
|
|
45
|
+
value = 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { oid, index };
|
|
49
|
+
}
|
|
50
|
+
function certError(message, data) {
|
|
51
|
+
return new Error(`Channel binding: ${message} while reading the server certificate (${data.length} bytes)`);
|
|
52
|
+
}
|
|
53
|
+
// OID -> the Node hash name to feed createHash().
|
|
54
|
+
const SIGNATURE_HASHES = {
|
|
55
|
+
// RSA
|
|
56
|
+
'1.2.840.113549.1.1.4': 'md5',
|
|
57
|
+
'1.2.840.113549.1.1.5': 'sha1',
|
|
58
|
+
'1.2.840.113549.1.1.11': 'sha256',
|
|
59
|
+
'1.2.840.113549.1.1.12': 'sha384',
|
|
60
|
+
'1.2.840.113549.1.1.13': 'sha512',
|
|
61
|
+
'1.2.840.113549.1.1.14': 'sha224',
|
|
62
|
+
'1.2.840.113549.1.1.15': 'sha512-224',
|
|
63
|
+
'1.2.840.113549.1.1.16': 'sha512-256',
|
|
64
|
+
// ECDSA
|
|
65
|
+
'1.2.840.10045.4.1': 'sha1',
|
|
66
|
+
'1.2.840.10045.4.3.1': 'sha224',
|
|
67
|
+
'1.2.840.10045.4.3.2': 'sha256',
|
|
68
|
+
'1.2.840.10045.4.3.3': 'sha384',
|
|
69
|
+
'1.2.840.10045.4.3.4': 'sha512',
|
|
70
|
+
// Ed25519, whose signature hashes with SHA-512.
|
|
71
|
+
'1.3.101.110': 'sha512',
|
|
72
|
+
'1.3.101.112': 'sha512',
|
|
73
|
+
};
|
|
74
|
+
// RSASSA-PSS names its hash separately, in the algorithm parameters.
|
|
75
|
+
const PSS_HASHES = {
|
|
76
|
+
'1.2.840.113549.2.5': 'md5',
|
|
77
|
+
'1.3.14.3.2.26': 'sha1',
|
|
78
|
+
'2.16.840.1.101.3.4.2.1': 'sha256',
|
|
79
|
+
'2.16.840.1.101.3.4.2.2': 'sha384',
|
|
80
|
+
'2.16.840.1.101.3.4.2.3': 'sha512',
|
|
81
|
+
};
|
|
82
|
+
export function signatureHashOfCertificate(der) {
|
|
83
|
+
let index = expectSequence(der, 0).index; // Certificate
|
|
84
|
+
const tbs = readLength(der, index + 1); // tbsCertificate's own header
|
|
85
|
+
if (der[index] !== 0x30)
|
|
86
|
+
throw certError('expected tbsCertificate', der);
|
|
87
|
+
index = tbs.index + tbs.length; // skip it whole
|
|
88
|
+
index = expectSequence(der, index).index; // AlgorithmIdentifier
|
|
89
|
+
const { oid, index: afterOID } = readOID(der, index);
|
|
90
|
+
if (oid === '1.2.840.113549.1.1.10') {
|
|
91
|
+
// RSASSA-PSS: parameters SEQUENCE, then [0] holding the hash algorithm.
|
|
92
|
+
let i = expectSequence(der, afterOID).index;
|
|
93
|
+
if (der[i++] !== 0xa0)
|
|
94
|
+
throw certError('expected the PSS hash tag', der);
|
|
95
|
+
i = readLength(der, i).index;
|
|
96
|
+
i = expectSequence(der, i).index;
|
|
97
|
+
const { oid: hashOID } = readOID(der, i);
|
|
98
|
+
const hash = PSS_HASHES[hashOID];
|
|
99
|
+
if (!hash)
|
|
100
|
+
throw certError(`unknown PSS hash OID ${hashOID}`, der);
|
|
101
|
+
return upgradeWeak(hash);
|
|
102
|
+
}
|
|
103
|
+
const hash = SIGNATURE_HASHES[oid];
|
|
104
|
+
if (!hash) {
|
|
105
|
+
if (oid === '1.3.101.111' || oid === '1.3.101.113')
|
|
106
|
+
throw certError('Ed448 certificates are not supported by PostgreSQL', der);
|
|
107
|
+
throw certError(`unknown signature algorithm OID ${oid}`, der);
|
|
108
|
+
}
|
|
109
|
+
return upgradeWeak(hash);
|
|
110
|
+
}
|
|
111
|
+
/** RFC 5929: a certificate signed with MD5 or SHA-1 binds with SHA-256. */
|
|
112
|
+
function upgradeWeak(hash) {
|
|
113
|
+
return hash === 'md5' || hash === 'sha1' ? 'sha256' : hash;
|
|
114
|
+
}
|
package/protocol/frontend.d.ts
CHANGED
|
@@ -43,6 +43,13 @@ export declare class Frontend {
|
|
|
43
43
|
constructor(options?: FrontendOptions);
|
|
44
44
|
getSSLRequestMessage(): Buffer;
|
|
45
45
|
getStartupMessage(args: Frontend.StartupMessageArgs): Buffer;
|
|
46
|
+
/**
|
|
47
|
+
* Asks the server to cancel whatever the given session is currently
|
|
48
|
+
* running. Sent on its own fresh connection - a backend busy with a query
|
|
49
|
+
* is not reading its own socket, which is the whole reason this cannot go
|
|
50
|
+
* down the normal one.
|
|
51
|
+
*/
|
|
52
|
+
getCancelRequestMessage(processID: number, secretKey: number): Buffer;
|
|
46
53
|
getPasswordMessage(password: string): Buffer;
|
|
47
54
|
getSASLMessage(sasl: SASL.Session): Buffer;
|
|
48
55
|
getSASLFinalMessage(session: SASL.Session): Buffer;
|
|
@@ -52,6 +59,15 @@ export declare class Frontend {
|
|
|
52
59
|
getExecuteMessage(args: Frontend.ExecuteMessageArgs): Buffer;
|
|
53
60
|
getCloseMessage(args: Frontend.CloseMessageArgs): Buffer;
|
|
54
61
|
getQueryMessage(sql: string): Buffer;
|
|
62
|
+
/**
|
|
63
|
+
* Returns the CopyData header and the caller's payload as two buffers
|
|
64
|
+
* rather than one, so a bulk import never copies the caller's bytes.
|
|
65
|
+
* PgSocket writes an array of buffers as a single corked write anyway,
|
|
66
|
+
* so this costs nothing on the wire.
|
|
67
|
+
*/
|
|
68
|
+
getCopyDataMessage(data: Buffer): Buffer[];
|
|
69
|
+
getCopyDoneMessage(): Buffer;
|
|
70
|
+
getCopyFailMessage(message: string): Buffer;
|
|
55
71
|
getFlushMessage(): Buffer;
|
|
56
72
|
getTerminateMessage(): Buffer;
|
|
57
73
|
getSyncMessage(): Buffer;
|
package/protocol/frontend.js
CHANGED
|
@@ -26,6 +26,13 @@ const StaticSyncBuffer = Buffer.from([
|
|
|
26
26
|
0x00,
|
|
27
27
|
0x04,
|
|
28
28
|
]);
|
|
29
|
+
const StaticCopyDoneBuffer = Buffer.from([
|
|
30
|
+
Protocol.FrontendMessageCode.CopyDone,
|
|
31
|
+
0x00,
|
|
32
|
+
0x00,
|
|
33
|
+
0x00,
|
|
34
|
+
0x04,
|
|
35
|
+
]);
|
|
29
36
|
export class Frontend {
|
|
30
37
|
_io;
|
|
31
38
|
constructor(options) {
|
|
@@ -54,6 +61,22 @@ export class Frontend {
|
|
|
54
61
|
.writeUInt8(0);
|
|
55
62
|
return setLengthAndFlush(io, 0);
|
|
56
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Asks the server to cancel whatever the given session is currently
|
|
66
|
+
* running. Sent on its own fresh connection - a backend busy with a query
|
|
67
|
+
* is not reading its own socket, which is the whole reason this cannot go
|
|
68
|
+
* down the normal one.
|
|
69
|
+
*/
|
|
70
|
+
getCancelRequestMessage(processID, secretKey) {
|
|
71
|
+
return this._io
|
|
72
|
+
.start()
|
|
73
|
+
.writeUInt32BE(16) // Length of message contents in bytes, including self.
|
|
74
|
+
.writeUInt16BE(1234)
|
|
75
|
+
.writeUInt16BE(5678)
|
|
76
|
+
.writeUInt32BE(processID)
|
|
77
|
+
.writeUInt32BE(secretKey)
|
|
78
|
+
.flush();
|
|
79
|
+
}
|
|
57
80
|
getPasswordMessage(password) {
|
|
58
81
|
const io = this._io
|
|
59
82
|
.start()
|
|
@@ -114,13 +137,16 @@ export class Frontend {
|
|
|
114
137
|
if (params && params.length) {
|
|
115
138
|
io.writeInt16BE(params.length);
|
|
116
139
|
const formatOffset = io.offset;
|
|
117
|
-
|
|
140
|
+
const l = params.length;
|
|
141
|
+
let i;
|
|
142
|
+
for (i = 0; i < l; i++) {
|
|
118
143
|
io.writeInt16BE(0); // Preserve
|
|
119
144
|
}
|
|
120
145
|
// Write parameter values
|
|
121
|
-
io.writeUInt16BE(
|
|
122
|
-
|
|
123
|
-
|
|
146
|
+
io.writeUInt16BE(l);
|
|
147
|
+
let v;
|
|
148
|
+
for (i = 0; i < l; i++) {
|
|
149
|
+
v = params[i];
|
|
124
150
|
if (v === null || v === undefined) {
|
|
125
151
|
io.writeInt32BE(-1);
|
|
126
152
|
continue;
|
|
@@ -175,8 +201,10 @@ export class Frontend {
|
|
|
175
201
|
io.writeUInt16BE(0);
|
|
176
202
|
}
|
|
177
203
|
if (Array.isArray(columnFormat)) {
|
|
178
|
-
|
|
179
|
-
|
|
204
|
+
const l = columnFormat.length;
|
|
205
|
+
io.writeUInt16BE(l);
|
|
206
|
+
let i;
|
|
207
|
+
for (i = 0; i < l; i++) {
|
|
180
208
|
io.writeUInt16BE(columnFormat[i]);
|
|
181
209
|
}
|
|
182
210
|
}
|
|
@@ -237,6 +265,29 @@ export class Frontend {
|
|
|
237
265
|
.writeCString(sql || '', 'utf8');
|
|
238
266
|
return setLengthAndFlush(io, 1);
|
|
239
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Returns the CopyData header and the caller's payload as two buffers
|
|
270
|
+
* rather than one, so a bulk import never copies the caller's bytes.
|
|
271
|
+
* PgSocket writes an array of buffers as a single corked write anyway,
|
|
272
|
+
* so this costs nothing on the wire.
|
|
273
|
+
*/
|
|
274
|
+
getCopyDataMessage(data) {
|
|
275
|
+
const header = Buffer.allocUnsafe(5);
|
|
276
|
+
header[0] = Protocol.FrontendMessageCode.CopyData;
|
|
277
|
+
header.writeUInt32BE(data.length + 4, 1);
|
|
278
|
+
return [header, data];
|
|
279
|
+
}
|
|
280
|
+
getCopyDoneMessage() {
|
|
281
|
+
return StaticCopyDoneBuffer;
|
|
282
|
+
}
|
|
283
|
+
getCopyFailMessage(message) {
|
|
284
|
+
const io = this._io
|
|
285
|
+
.start()
|
|
286
|
+
.writeInt8(Protocol.FrontendMessageCode.CopyFail)
|
|
287
|
+
.writeInt32BE(0) // Preserve header
|
|
288
|
+
.writeCString(message || '', 'utf8');
|
|
289
|
+
return setLengthAndFlush(io, 1);
|
|
290
|
+
}
|
|
240
291
|
getFlushMessage() {
|
|
241
292
|
return StaticFlushBuffer;
|
|
242
293
|
}
|
package/protocol/pg-socket.d.ts
CHANGED
|
@@ -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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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():
|
|
37
|
-
|
|
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
|
-
|
|
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
|
}
|