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.
- package/CHANGELOG.md +171 -1
- package/LICENSE +26 -17
- package/README.md +195 -175
- package/connection/connection.d.ts +169 -16
- package/connection/connection.js +318 -56
- 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 +113 -5
- package/connection/intl-connection.js +500 -70
- 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 +129 -109
- 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 +118 -9
- 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/command-result.d.ts +2 -1
- package/interfaces/data-type.d.ts +5 -3
- package/interfaces/database-connection-params.d.ts +108 -0
- package/interfaces/query-options.d.ts +23 -0
- package/interfaces/script-execute-options.d.ts +23 -0
- package/interfaces/script-result.d.ts +3 -2
- 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 +70 -11
- 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 +36 -5
- 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 +34 -0
- package/util/abort-signal.js +76 -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
package/protocol/backend.js
CHANGED
|
@@ -23,35 +23,70 @@ const ErrorFieldTypes = {
|
|
|
23
23
|
R: 'routine',
|
|
24
24
|
};
|
|
25
25
|
export class Backend {
|
|
26
|
-
|
|
26
|
+
_headerBuf = Buffer.allocUnsafe(HEADER_LENGTH);
|
|
27
|
+
_headerFilled = 0;
|
|
28
|
+
_code;
|
|
29
|
+
_len = 0;
|
|
30
|
+
_bodyBuf;
|
|
31
|
+
_bodyFilled = 0;
|
|
27
32
|
reset() {
|
|
28
|
-
this.
|
|
33
|
+
this._headerFilled = 0;
|
|
34
|
+
this._code = undefined;
|
|
35
|
+
this._bodyBuf = undefined;
|
|
36
|
+
this._bodyFilled = 0;
|
|
29
37
|
}
|
|
30
38
|
parse(data, callback) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
const dataLen = data.length;
|
|
40
|
+
let pos = 0;
|
|
41
|
+
while (pos < dataLen) {
|
|
42
|
+
if (this._headerFilled === 0 &&
|
|
43
|
+
!this._bodyBuf &&
|
|
44
|
+
dataLen - pos >= HEADER_LENGTH) {
|
|
45
|
+
const code = data.readUInt8(pos);
|
|
46
|
+
const len = data.readUInt32BE(pos + 1);
|
|
47
|
+
const bodyStart = pos + HEADER_LENGTH;
|
|
48
|
+
const bodyEnd = bodyStart + (len - 4);
|
|
49
|
+
if (bodyEnd <= dataLen) {
|
|
50
|
+
const io = new BufferReader(data.subarray(bodyStart, bodyEnd));
|
|
51
|
+
const parser = MessageParsers[code];
|
|
52
|
+
const v = parser && parser(io, code, len);
|
|
53
|
+
callback(code, v);
|
|
54
|
+
pos = bodyEnd;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!this._bodyBuf) {
|
|
59
|
+
const need = HEADER_LENGTH - this._headerFilled;
|
|
60
|
+
const take = Math.min(need, dataLen - pos);
|
|
61
|
+
data.copy(this._headerBuf, this._headerFilled, pos, pos + take);
|
|
62
|
+
this._headerFilled += take;
|
|
63
|
+
pos += take;
|
|
64
|
+
if (this._headerFilled < HEADER_LENGTH)
|
|
65
|
+
return; // header not complete yet
|
|
66
|
+
this._code = this._headerBuf.readUInt8(0);
|
|
67
|
+
this._len = this._headerBuf.readUInt32BE(1);
|
|
68
|
+
this._bodyBuf = Buffer.allocUnsafe(this._len - 4);
|
|
69
|
+
this._bodyFilled = 0;
|
|
70
|
+
}
|
|
71
|
+
const need = this._bodyBuf.length - this._bodyFilled;
|
|
72
|
+
const take = Math.min(need, dataLen - pos);
|
|
73
|
+
if (take > 0) {
|
|
74
|
+
data.copy(this._bodyBuf, this._bodyFilled, pos, pos + take);
|
|
75
|
+
this._bodyFilled += take;
|
|
76
|
+
pos += take;
|
|
46
77
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
78
|
+
if (this._bodyFilled < this._bodyBuf.length)
|
|
79
|
+
return; // body not complete yet
|
|
80
|
+
const io = new BufferReader(this._bodyBuf);
|
|
81
|
+
const parser = MessageParsers[this._code];
|
|
82
|
+
const v = parser && parser(io, this._code, this._len);
|
|
83
|
+
callback(this._code, v);
|
|
84
|
+
// Reset reassembly state for the next message.
|
|
85
|
+
this._headerFilled = 0;
|
|
86
|
+
this._code = undefined;
|
|
87
|
+
this._bodyBuf = undefined;
|
|
88
|
+
this._bodyFilled = 0;
|
|
52
89
|
}
|
|
53
|
-
if (io.offset < io.length)
|
|
54
|
-
this._buf = io.readBuffer(io.length - io.offset);
|
|
55
90
|
}
|
|
56
91
|
}
|
|
57
92
|
const MessageParsers = {
|
|
@@ -158,7 +193,9 @@ function parseCopyResponse(io) {
|
|
|
158
193
|
};
|
|
159
194
|
if (out.columnCount) {
|
|
160
195
|
out.columnFormats = [];
|
|
161
|
-
|
|
196
|
+
const l = out.columnCount;
|
|
197
|
+
let i;
|
|
198
|
+
for (i = 0; i < l; i++) {
|
|
162
199
|
out.columnFormats.push(io.readUInt16BE() === 0
|
|
163
200
|
? Protocol.DataFormat.text
|
|
164
201
|
: Protocol.DataFormat.binary);
|
|
@@ -166,24 +203,10 @@ function parseCopyResponse(io) {
|
|
|
166
203
|
}
|
|
167
204
|
return out;
|
|
168
205
|
}
|
|
169
|
-
function parseDataRow(io) {
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
};
|
|
173
|
-
if (out.columnCount) {
|
|
174
|
-
out.columns = [];
|
|
175
|
-
for (let i = 0; i < out.columnCount; i++) {
|
|
176
|
-
// The length of the column value, in bytes (this count does not include itself).
|
|
177
|
-
// Can be zero. As a special case, -1 indicates a NULL column value.
|
|
178
|
-
// No value bytes follow in the NULL case.
|
|
179
|
-
const l = io.readInt32BE();
|
|
180
|
-
if (l < 0)
|
|
181
|
-
out.columns.push(null);
|
|
182
|
-
else
|
|
183
|
-
out.columns.push(io.readBuffer(l));
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return out;
|
|
206
|
+
function parseDataRow(io, code, len) {
|
|
207
|
+
const columnCount = io.readUInt16BE();
|
|
208
|
+
const data = io.readBuffer(len - 6);
|
|
209
|
+
return { columnCount, data };
|
|
187
210
|
}
|
|
188
211
|
function parseErrorResponse(io) {
|
|
189
212
|
const out = {};
|
|
@@ -217,10 +240,12 @@ function parseNegotiateProtocolVersion(io) {
|
|
|
217
240
|
}
|
|
218
241
|
function parseParameterDescription(io) {
|
|
219
242
|
const out = {
|
|
220
|
-
parameterCount: io.
|
|
243
|
+
parameterCount: io.readUInt16BE(),
|
|
221
244
|
parameterIds: [],
|
|
222
245
|
};
|
|
223
|
-
|
|
246
|
+
const l = out.parameterCount;
|
|
247
|
+
let i;
|
|
248
|
+
for (i = 0; i < l; i++) {
|
|
224
249
|
out.parameterIds.push(io.readUInt32BE());
|
|
225
250
|
}
|
|
226
251
|
return out;
|
|
@@ -11,7 +11,5 @@ export declare class BufferReader {
|
|
|
11
11
|
readCString(encoding?: BufferEncoding): string;
|
|
12
12
|
readLString(len: number, encoding?: BufferEncoding): string | null;
|
|
13
13
|
readBuffer(len?: number): Buffer;
|
|
14
|
-
moveBy(n: number): this;
|
|
15
|
-
moveTo(pos: number): this;
|
|
16
14
|
private _checkReadable;
|
|
17
15
|
}
|
|
@@ -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) {
|
|
@@ -45,7 +52,12 @@ export class Frontend {
|
|
|
45
52
|
.writeInt32BE(0) // Preserve length
|
|
46
53
|
.writeInt16BE(Protocol.VERSION_MAJOR)
|
|
47
54
|
.writeInt16BE(Protocol.VERSION_MINOR);
|
|
48
|
-
|
|
55
|
+
const entries = Object.entries(args);
|
|
56
|
+
const l = entries.length;
|
|
57
|
+
let k;
|
|
58
|
+
let v;
|
|
59
|
+
for (let i = 0; i < l; i++) {
|
|
60
|
+
[k, v] = entries[i];
|
|
49
61
|
if (k !== 'client_encoding')
|
|
50
62
|
io.writeCString(k, 'utf8').writeCString(v, 'utf8');
|
|
51
63
|
}
|
|
@@ -54,6 +66,22 @@ export class Frontend {
|
|
|
54
66
|
.writeUInt8(0);
|
|
55
67
|
return setLengthAndFlush(io, 0);
|
|
56
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Asks the server to cancel whatever the given session is currently
|
|
71
|
+
* running. Sent on its own fresh connection - a backend busy with a query
|
|
72
|
+
* is not reading its own socket, which is the whole reason this cannot go
|
|
73
|
+
* down the normal one.
|
|
74
|
+
*/
|
|
75
|
+
getCancelRequestMessage(processID, secretKey) {
|
|
76
|
+
return this._io
|
|
77
|
+
.start()
|
|
78
|
+
.writeUInt32BE(16) // Length of message contents in bytes, including self.
|
|
79
|
+
.writeUInt16BE(1234)
|
|
80
|
+
.writeUInt16BE(5678)
|
|
81
|
+
.writeUInt32BE(processID)
|
|
82
|
+
.writeUInt32BE(secretKey)
|
|
83
|
+
.flush();
|
|
84
|
+
}
|
|
57
85
|
getPasswordMessage(password) {
|
|
58
86
|
const io = this._io
|
|
59
87
|
.start()
|
|
@@ -82,16 +110,19 @@ export class Frontend {
|
|
|
82
110
|
getParseMessage(args) {
|
|
83
111
|
if (args.statement && args.statement.length > 63)
|
|
84
112
|
throw new Error('Query name length must be lower than 63');
|
|
113
|
+
const { paramTypes } = args;
|
|
85
114
|
const io = this._io
|
|
86
115
|
.start()
|
|
87
116
|
.writeInt8(Protocol.FrontendMessageCode.Parse)
|
|
88
117
|
.writeInt32BE(0) // Preserve header
|
|
89
118
|
.writeCString(args.statement || '', 'utf8')
|
|
90
119
|
.writeCString(args.sql, 'utf8')
|
|
91
|
-
.writeUInt16BE(
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
.writeUInt16BE(paramTypes?.length || 0);
|
|
121
|
+
if (paramTypes) {
|
|
122
|
+
let i;
|
|
123
|
+
const l = paramTypes.length;
|
|
124
|
+
for (i = 0; i < l; i++) {
|
|
125
|
+
io.writeUInt32BE(paramTypes[i] || 0);
|
|
95
126
|
}
|
|
96
127
|
}
|
|
97
128
|
return setLengthAndFlush(io, 1);
|
|
@@ -114,13 +145,16 @@ export class Frontend {
|
|
|
114
145
|
if (params && params.length) {
|
|
115
146
|
io.writeInt16BE(params.length);
|
|
116
147
|
const formatOffset = io.offset;
|
|
117
|
-
|
|
148
|
+
const l = params.length;
|
|
149
|
+
let i;
|
|
150
|
+
for (i = 0; i < l; i++) {
|
|
118
151
|
io.writeInt16BE(0); // Preserve
|
|
119
152
|
}
|
|
120
153
|
// Write parameter values
|
|
121
|
-
io.writeUInt16BE(
|
|
122
|
-
|
|
123
|
-
|
|
154
|
+
io.writeUInt16BE(l);
|
|
155
|
+
let v;
|
|
156
|
+
for (i = 0; i < l; i++) {
|
|
157
|
+
v = params[i];
|
|
124
158
|
if (v === null || v === undefined) {
|
|
125
159
|
io.writeInt32BE(-1);
|
|
126
160
|
continue;
|
|
@@ -175,8 +209,10 @@ export class Frontend {
|
|
|
175
209
|
io.writeUInt16BE(0);
|
|
176
210
|
}
|
|
177
211
|
if (Array.isArray(columnFormat)) {
|
|
178
|
-
|
|
179
|
-
|
|
212
|
+
const l = columnFormat.length;
|
|
213
|
+
io.writeUInt16BE(l);
|
|
214
|
+
let i;
|
|
215
|
+
for (i = 0; i < l; i++) {
|
|
180
216
|
io.writeUInt16BE(columnFormat[i]);
|
|
181
217
|
}
|
|
182
218
|
}
|
|
@@ -237,6 +273,29 @@ export class Frontend {
|
|
|
237
273
|
.writeCString(sql || '', 'utf8');
|
|
238
274
|
return setLengthAndFlush(io, 1);
|
|
239
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Returns the CopyData header and the caller's payload as two buffers
|
|
278
|
+
* rather than one, so a bulk import never copies the caller's bytes.
|
|
279
|
+
* PgSocket writes an array of buffers as a single corked write anyway,
|
|
280
|
+
* so this costs nothing on the wire.
|
|
281
|
+
*/
|
|
282
|
+
getCopyDataMessage(data) {
|
|
283
|
+
const header = Buffer.allocUnsafe(5);
|
|
284
|
+
header[0] = Protocol.FrontendMessageCode.CopyData;
|
|
285
|
+
header.writeUInt32BE(data.length + 4, 1);
|
|
286
|
+
return [header, data];
|
|
287
|
+
}
|
|
288
|
+
getCopyDoneMessage() {
|
|
289
|
+
return StaticCopyDoneBuffer;
|
|
290
|
+
}
|
|
291
|
+
getCopyFailMessage(message) {
|
|
292
|
+
const io = this._io
|
|
293
|
+
.start()
|
|
294
|
+
.writeInt8(Protocol.FrontendMessageCode.CopyFail)
|
|
295
|
+
.writeInt32BE(0) // Preserve header
|
|
296
|
+
.writeCString(message || '', 'utf8');
|
|
297
|
+
return setLengthAndFlush(io, 1);
|
|
298
|
+
}
|
|
240
299
|
getFlushMessage() {
|
|
241
300
|
return StaticFlushBuffer;
|
|
242
301
|
}
|