postgrejs 2.23.0 → 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 +162 -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.d.ts +2 -2
- 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 +10 -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.d.ts +1 -1
- package/data-types/timestamptz-type.js +34 -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 +3 -4
- 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/database-error.js +1 -0
- package/protocol/frontend.d.ts +16 -0
- package/protocol/frontend.js +58 -6
- package/protocol/pg-socket.d.ts +221 -10
- 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/bigint-methods.js +1 -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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { formatTimestamp } from '../util/format-datetime.js';
|
|
2
3
|
import { parseDateTime } from '../util/parse-datetime.js';
|
|
3
4
|
const timeShift = 946684800000;
|
|
4
5
|
const timeMul = 4294967296;
|
|
@@ -6,25 +7,13 @@ export const TimestampType = {
|
|
|
6
7
|
name: 'timestamp',
|
|
7
8
|
oid: DataTypeOIDs.timestamp,
|
|
8
9
|
jsType: 'Date',
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const hi = v.readInt32BE();
|
|
13
|
-
const lo = v.readUInt32BE(4);
|
|
14
|
-
if (lo === 0xffffffff && hi === 0x7fffffff)
|
|
15
|
-
return fetchAsString ? 'infinity' : Infinity;
|
|
16
|
-
if (lo === 0x00000000 && hi === -0x80000000)
|
|
17
|
-
return fetchAsString ? '-infinity' : -Infinity;
|
|
18
|
-
// Shift from 2000 to 1970
|
|
19
|
-
let d = new Date((lo + hi * timeMul) / 1000 + timeShift);
|
|
20
|
-
if (fetchAsString || !options.utcDates) {
|
|
21
|
-
d = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds());
|
|
22
|
-
}
|
|
23
|
-
return fetchAsString ? dateToTimestampString(d) : d;
|
|
10
|
+
fixedBinarySize: 8,
|
|
11
|
+
encodeText(v, options) {
|
|
12
|
+
return formatTimestamp(v, options);
|
|
24
13
|
},
|
|
25
14
|
encodeBinary(buf, v, options) {
|
|
26
15
|
if (typeof v === 'string')
|
|
27
|
-
v = parseDateTime(v,
|
|
16
|
+
v = parseDateTime(v, options.utcDates);
|
|
28
17
|
if (v === Infinity) {
|
|
29
18
|
buf.writeInt32BE(0x7fffffff); // hi
|
|
30
19
|
buf.writeUInt32BE(0xffffffff); // lo
|
|
@@ -47,19 +36,37 @@ export const TimestampType = {
|
|
|
47
36
|
buf.writeInt32BE(hi);
|
|
48
37
|
buf.writeUInt32BE(lo);
|
|
49
38
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
decodeBinary(v, offset = 0, options) {
|
|
40
|
+
const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.timestamp);
|
|
41
|
+
const hi = v.readInt32BE(offset);
|
|
42
|
+
const lo = v.readUInt32BE(offset + 4);
|
|
43
|
+
if (lo === 0xffffffff && hi === 0x7fffffff)
|
|
44
|
+
return fetchAsString ? 'infinity' : Infinity;
|
|
45
|
+
if (lo === 0x00000000 && hi === -0x80000000)
|
|
46
|
+
return fetchAsString ? '-infinity' : -Infinity;
|
|
47
|
+
// Shift from 2000 to 1970
|
|
48
|
+
let d = new Date((lo + hi * timeMul) / 1000 + timeShift);
|
|
49
|
+
if (fetchAsString || !options.utcDates) {
|
|
50
|
+
d = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds());
|
|
51
|
+
}
|
|
52
|
+
return fetchAsString ? dateToTimestampString(d) : d;
|
|
53
|
+
},
|
|
54
|
+
decodeText(v, options) {
|
|
55
|
+
if (options.fetchAsString?.includes(DataTypeOIDs.timestamp))
|
|
53
56
|
return v;
|
|
54
|
-
return parseDateTime(v,
|
|
57
|
+
return parseDateTime(v, options.utcDates);
|
|
58
|
+
},
|
|
59
|
+
// See date-type.ts's decodeTextBuffer comment - same rationale.
|
|
60
|
+
decodeTextBuffer(buf, offset, len, options) {
|
|
61
|
+
return TimestampType.decodeText(buf.toString('latin1', offset, offset + len), options);
|
|
55
62
|
},
|
|
56
63
|
isType(v) {
|
|
64
|
+
// A midnight-exact Date is ambiguous between "date-only" and "a real
|
|
65
|
+
// timestamp that happens to fall on midnight" - default to timestamp,
|
|
66
|
+
// since a timestamp/timestamptz column represents midnight just fine,
|
|
67
|
+
// whereas a date column can never represent a non-midnight time.
|
|
57
68
|
return (v instanceof Date &&
|
|
58
|
-
!(v.getFullYear() === 1970 && v.getMonth() === 0 && v.getDate() === 1)
|
|
59
|
-
!(v.getHours() === 0 &&
|
|
60
|
-
v.getMinutes() === 0 &&
|
|
61
|
-
v.getSeconds() === 0 &&
|
|
62
|
-
v.getMilliseconds() === 0));
|
|
69
|
+
!(v.getFullYear() === 1970 && v.getMonth() === 0 && v.getDate() === 1));
|
|
63
70
|
},
|
|
64
71
|
};
|
|
65
72
|
function padZero(v) {
|
|
@@ -1,30 +1,20 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
-
import {
|
|
2
|
+
import { SmartBuffer } from '../protocol/smart-buffer.js';
|
|
3
|
+
import { formatTimestamptz } from '../util/format-datetime.js';
|
|
4
|
+
import { parseDateTimeTz } from '../util/parse-datetime.js';
|
|
3
5
|
const timeShift = 946684800000;
|
|
4
6
|
const timeMul = 4294967296;
|
|
5
7
|
export const TimestamptzType = {
|
|
6
8
|
name: 'timestamptz',
|
|
7
9
|
oid: DataTypeOIDs.timestamptz,
|
|
8
10
|
jsType: 'Date',
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const hi = v.readInt32BE();
|
|
13
|
-
const lo = v.readUInt32BE(4);
|
|
14
|
-
if (lo === 0xffffffff && hi === 0x7fffffff)
|
|
15
|
-
return fetchAsString ? 'infinity' : Infinity;
|
|
16
|
-
if (lo === 0x00000000 && hi === -0x80000000)
|
|
17
|
-
return fetchAsString ? '-infinity' : -Infinity;
|
|
18
|
-
// Shift from 2000 to 1970
|
|
19
|
-
let d = new Date((lo + hi * timeMul) / 1000 + timeShift);
|
|
20
|
-
if (fetchAsString || !options.utcDates) {
|
|
21
|
-
d = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
|
|
22
|
-
}
|
|
23
|
-
return fetchAsString ? dateToTimestamptzString(d) : d;
|
|
11
|
+
fixedBinarySize: 8,
|
|
12
|
+
encodeText(v) {
|
|
13
|
+
return formatTimestamptz(v);
|
|
24
14
|
},
|
|
25
15
|
encodeBinary(buf, v, options) {
|
|
26
16
|
if (typeof v === 'string')
|
|
27
|
-
v =
|
|
17
|
+
v = parseDateTimeTz(v, options.utcDates);
|
|
28
18
|
if (v === Infinity) {
|
|
29
19
|
buf.writeInt32BE(0x7fffffff); // hi
|
|
30
20
|
buf.writeUInt32BE(0xffffffff); // lo
|
|
@@ -44,20 +34,43 @@ export const TimestamptzType = {
|
|
|
44
34
|
buf.writeInt32BE(hi);
|
|
45
35
|
buf.writeUInt32BE(lo);
|
|
46
36
|
},
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
37
|
+
decodeBinary(v, offset = 0, options) {
|
|
38
|
+
const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.timestamptz);
|
|
39
|
+
const hi = v.readInt32BE(offset);
|
|
40
|
+
const lo = v.readUInt32BE(offset + 4);
|
|
41
|
+
if (lo === 0xffffffff && hi === 0x7fffffff)
|
|
42
|
+
return fetchAsString ? 'infinity' : Infinity;
|
|
43
|
+
if (lo === 0x00000000 && hi === -0x80000000)
|
|
44
|
+
return fetchAsString ? '-infinity' : -Infinity;
|
|
45
|
+
// Shift from 2000 to 1970
|
|
46
|
+
let d = new Date((lo + hi * timeMul) / 1000 + timeShift);
|
|
47
|
+
if (fetchAsString || !options.utcDates) {
|
|
48
|
+
d = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
|
|
49
|
+
}
|
|
50
|
+
return fetchAsString ? dateToTimestamptzString(d) : d;
|
|
51
|
+
},
|
|
52
|
+
decodeText(v, options) {
|
|
53
|
+
const d = parseDateTimeTz(v, options.utcDates);
|
|
54
|
+
if (options.fetchAsString?.includes(DataTypeOIDs.timestamptz)) {
|
|
51
55
|
if (d instanceof Date)
|
|
52
56
|
return dateToTimestamptzString(d);
|
|
53
57
|
if (d === Infinity)
|
|
54
58
|
return 'infinity';
|
|
55
59
|
if (d === -Infinity)
|
|
56
60
|
return '-infinity';
|
|
61
|
+
// parseDateTimeTz() only ever returns a Date, Infinity or -Infinity,
|
|
62
|
+
// never anything else, so this is unreachable.
|
|
57
63
|
return '';
|
|
58
64
|
}
|
|
59
65
|
return d;
|
|
60
66
|
},
|
|
67
|
+
// See date-type.ts's decodeTextBuffer comment - same rationale. Note this
|
|
68
|
+
// type's decodeText always parses first and only branches on
|
|
69
|
+
// fetchAsString afterward (unlike date/time/timestamp, which early-return
|
|
70
|
+
// the raw string) - delegating by reference preserves that as-is.
|
|
71
|
+
decodeTextBuffer(buf, offset, len, options) {
|
|
72
|
+
return TimestamptzType.decodeText(buf.toString('latin1', offset, offset + len), options);
|
|
73
|
+
},
|
|
61
74
|
isType(v) {
|
|
62
75
|
return v instanceof Date;
|
|
63
76
|
},
|
package/data-types/uuid-type.js
CHANGED
|
@@ -4,16 +4,9 @@ export const UuidType = {
|
|
|
4
4
|
name: 'uuid',
|
|
5
5
|
oid: DataTypeOIDs.uuid,
|
|
6
6
|
jsType: 'String',
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
v.toString('hex', 4, 6) +
|
|
11
|
-
'-' +
|
|
12
|
-
v.toString('hex', 6, 8) +
|
|
13
|
-
'-' +
|
|
14
|
-
v.toString('hex', 8, 10) +
|
|
15
|
-
'-' +
|
|
16
|
-
v.toString('hex', 10, 16));
|
|
7
|
+
fixedBinarySize: 16,
|
|
8
|
+
encodeText(v) {
|
|
9
|
+
return '' + v;
|
|
17
10
|
},
|
|
18
11
|
encodeBinary(buf, v) {
|
|
19
12
|
if (!GUID_PATTERN.test(v))
|
|
@@ -21,9 +14,26 @@ export const UuidType = {
|
|
|
21
14
|
const b = Buffer.from(v.replace(/-/g, ''), 'hex');
|
|
22
15
|
buf.writeBuffer(b);
|
|
23
16
|
},
|
|
24
|
-
|
|
17
|
+
decodeBinary(v, offset = 0) {
|
|
18
|
+
return (v.toString('hex', offset, offset + 4) +
|
|
19
|
+
'-' +
|
|
20
|
+
v.toString('hex', offset + 4, offset + 6) +
|
|
21
|
+
'-' +
|
|
22
|
+
v.toString('hex', offset + 6, offset + 8) +
|
|
23
|
+
'-' +
|
|
24
|
+
v.toString('hex', offset + 8, offset + 10) +
|
|
25
|
+
'-' +
|
|
26
|
+
v.toString('hex', offset + 10, offset + 16));
|
|
27
|
+
},
|
|
28
|
+
decodeText(v) {
|
|
25
29
|
return v;
|
|
26
30
|
},
|
|
31
|
+
// Canonical UUID text is fixed-format hex+dashes, pure ASCII - 'latin1'
|
|
32
|
+
// decodes identically to 'utf8' here but skips V8's multi-byte-sequence
|
|
33
|
+
// detection.
|
|
34
|
+
decodeTextBuffer(buf, offset, len) {
|
|
35
|
+
return buf.toString('latin1', offset, offset + len);
|
|
36
|
+
},
|
|
27
37
|
isType(v) {
|
|
28
38
|
return typeof v === 'string' && GUID_PATTERN.test(v);
|
|
29
39
|
},
|
|
@@ -3,14 +3,26 @@ export const VarcharType = {
|
|
|
3
3
|
name: 'varchar',
|
|
4
4
|
oid: DataTypeOIDs.varchar,
|
|
5
5
|
jsType: 'string',
|
|
6
|
-
|
|
7
|
-
return
|
|
6
|
+
encodeText(v) {
|
|
7
|
+
return '' + v;
|
|
8
8
|
},
|
|
9
9
|
encodeBinary(buf, v) {
|
|
10
10
|
buf.writeString('' + v, 'utf8');
|
|
11
11
|
},
|
|
12
|
-
|
|
13
|
-
return ''
|
|
12
|
+
decodeBinary(v, offset = 0) {
|
|
13
|
+
return v.toString('utf8', offset);
|
|
14
|
+
},
|
|
15
|
+
decodeText(v) {
|
|
16
|
+
// The wire decoder always hands this a string (see intl-connection.ts's
|
|
17
|
+
// DataRow handling) - no coercion needed.
|
|
18
|
+
return v;
|
|
19
|
+
},
|
|
20
|
+
// varchar can hold arbitrary Unicode text, so this must stay 'utf8' -
|
|
21
|
+
// same allocation as the default (v.toString('utf8')) path, this only
|
|
22
|
+
// skips the extra decodeText wrapper-closure call get-parsers.ts would
|
|
23
|
+
// otherwise add.
|
|
24
|
+
decodeTextBuffer(buf, offset, len) {
|
|
25
|
+
return buf.toString('utf8', offset, offset + len);
|
|
14
26
|
},
|
|
15
27
|
isType(v) {
|
|
16
28
|
return typeof v === 'string';
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from './connection/bind-param.js';
|
|
2
2
|
export * from './connection/connection.js';
|
|
3
|
+
export * from './connection/copy-stream.js';
|
|
3
4
|
export * from './connection/cursor.js';
|
|
5
|
+
export * from './connection/large-object.js';
|
|
6
|
+
export * from './connection/logical-replication.js';
|
|
4
7
|
export * from './connection/pool.js';
|
|
5
8
|
export * from './connection/prepared-statement.js';
|
|
6
9
|
export * from './constants.js';
|
|
@@ -19,7 +22,9 @@ export * from './protocol/database-error.js';
|
|
|
19
22
|
export * from './protocol/smart-buffer.js';
|
|
20
23
|
export * from './types.js';
|
|
21
24
|
export * from './util/connection-config.js';
|
|
25
|
+
export * from './util/escape-identifier.js';
|
|
22
26
|
export * from './util/escape-literal.js';
|
|
23
27
|
export * from './util/parse-datetime.js';
|
|
28
|
+
export * from './util/sql-tag.js';
|
|
24
29
|
export * from './util/stringify-arrayliteral.js';
|
|
25
30
|
export * from './util/stringify-for-sql.js';
|
package/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from './connection/bind-param.js';
|
|
2
2
|
export * from './connection/connection.js';
|
|
3
|
+
export * from './connection/copy-stream.js';
|
|
3
4
|
export * from './connection/cursor.js';
|
|
5
|
+
export * from './connection/large-object.js';
|
|
6
|
+
export * from './connection/logical-replication.js';
|
|
4
7
|
export * from './connection/pool.js';
|
|
5
8
|
export * from './connection/prepared-statement.js';
|
|
6
9
|
export * from './constants.js';
|
|
@@ -19,7 +22,9 @@ export * from './protocol/database-error.js';
|
|
|
19
22
|
export * from './protocol/smart-buffer.js';
|
|
20
23
|
export * from './types.js';
|
|
21
24
|
export * from './util/connection-config.js';
|
|
25
|
+
export * from './util/escape-identifier.js';
|
|
22
26
|
export * from './util/escape-literal.js';
|
|
23
27
|
export * from './util/parse-datetime.js';
|
|
28
|
+
export * from './util/sql-tag.js';
|
|
24
29
|
export * from './util/stringify-arrayliteral.js';
|
|
25
30
|
export * from './util/stringify-for-sql.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DecodeBinaryFunction, EncodeAsNullFunction, EncodeBinaryFunction, EncodeCalculateDimFunction, EncodeTextFunction, OID
|
|
1
|
+
import type { DecodeBinaryFunction, DecodeTextBufferFunction, DecodeTextFunction, EncodeAsNullFunction, EncodeBinaryFunction, EncodeCalculateDimFunction, EncodeTextFunction, OID } from '../types.js';
|
|
2
2
|
export interface DataType {
|
|
3
3
|
oid: OID;
|
|
4
4
|
name: string;
|
|
@@ -7,8 +7,10 @@ export interface DataType {
|
|
|
7
7
|
jsType: string;
|
|
8
8
|
arraySeparator?: string;
|
|
9
9
|
isType: (v: any) => boolean;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
decodeBinary: DecodeBinaryFunction;
|
|
11
|
+
decodeText: DecodeTextFunction;
|
|
12
|
+
decodeTextBuffer?: DecodeTextBufferFunction;
|
|
13
|
+
fixedBinarySize?: number;
|
|
12
14
|
encodeAsNull?: EncodeAsNullFunction;
|
|
13
15
|
encodeBinary?: EncodeBinaryFunction;
|
|
14
16
|
encodeText?: EncodeTextFunction;
|
|
@@ -9,6 +9,58 @@ export interface DatabaseConnectionParams {
|
|
|
9
9
|
password?: string | (() => string | Promise<string>);
|
|
10
10
|
database?: string;
|
|
11
11
|
applicationName?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Opens the connection in replication mode, which is what lets it run
|
|
14
|
+
* START_REPLICATION. Set by LogicalReplication; a connection in this mode
|
|
15
|
+
* cannot serve ordinary queries once streaming has begun.
|
|
16
|
+
*/
|
|
17
|
+
replication?: 'database' | 'true';
|
|
18
|
+
/**
|
|
19
|
+
* Additional servers to try, in order, when the first one cannot be used.
|
|
20
|
+
*
|
|
21
|
+
* Also accepted as a comma-separated `host`, or in a connection string:
|
|
22
|
+
* `postgres://a:5432,b:5433/db`. Each entry falls back to the top-level
|
|
23
|
+
* `port` when it does not carry one of its own.
|
|
24
|
+
*
|
|
25
|
+
* Selection happens when a connection is opened, so a cluster that has
|
|
26
|
+
* failed over to another node is found on the next connect - a query
|
|
27
|
+
* already in flight when a server goes down still fails.
|
|
28
|
+
*/
|
|
29
|
+
hosts?: {
|
|
30
|
+
host: string;
|
|
31
|
+
port?: number;
|
|
32
|
+
}[];
|
|
33
|
+
/**
|
|
34
|
+
* Which server in `hosts` is acceptable, mirroring libpq's option of the
|
|
35
|
+
* same name: `read-write`, `read-only`, `primary`, `standby` or
|
|
36
|
+
* `prefer-standby`. A server that does not match is dropped and the next
|
|
37
|
+
* one tried, which is how `read-write` finds the current primary.
|
|
38
|
+
*/
|
|
39
|
+
targetSessionAttrs?: TargetSessionAttrs;
|
|
40
|
+
/**
|
|
41
|
+
* How TLS is started: `postgres` (the default) asks first with an
|
|
42
|
+
* SSLRequest and waits for the server's yes or no; `direct` begins the
|
|
43
|
+
* TLS handshake straight away, announcing the `postgresql` protocol over
|
|
44
|
+
* ALPN so the server can tell what it is talking to.
|
|
45
|
+
*
|
|
46
|
+
* Direct saves a round trip and leaves no plaintext preamble for a
|
|
47
|
+
* middlebox to read or strip, but it needs PostgreSQL 17 or later and
|
|
48
|
+
* `ssl` set - an older server just closes the connection.
|
|
49
|
+
*/
|
|
50
|
+
sslNegotiation?: 'postgres' | 'direct';
|
|
51
|
+
/**
|
|
52
|
+
* Whether SCRAM authentication binds itself to the TLS channel, mirroring
|
|
53
|
+
* libpq's option of the same name: `prefer` (the default there and here)
|
|
54
|
+
* uses it when the server offers it, `require` refuses to connect
|
|
55
|
+
* otherwise, `disable` never asks for it.
|
|
56
|
+
*
|
|
57
|
+
* Binding mixes a hash of the server's certificate into the SCRAM proof,
|
|
58
|
+
* so an attacker who terminates TLS in the middle - necessarily with a
|
|
59
|
+
* different certificate - cannot relay the exchange. It is what protects
|
|
60
|
+
* the login when the certificate itself is not verified, which is the
|
|
61
|
+
* usual case with `sslmode=require` or a self-signed certificate.
|
|
62
|
+
*/
|
|
63
|
+
channelBinding?: 'prefer' | 'require' | 'disable';
|
|
12
64
|
requireSSL?: boolean;
|
|
13
65
|
ssl?: TlsConnectionOptions;
|
|
14
66
|
timezone?: string;
|
|
@@ -26,8 +78,22 @@ export interface DatabaseConnectionParams {
|
|
|
26
78
|
* @default true
|
|
27
79
|
*/
|
|
28
80
|
rollbackOnError?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Whether a thrown error's stack trace points at the application code
|
|
83
|
+
* that called query()/execute() (etc.) across the `await`, instead of an
|
|
84
|
+
* internal async frame inside this library.
|
|
85
|
+
*
|
|
86
|
+
* Getting this right costs a real, measurable amount of CPU when many
|
|
87
|
+
* calls are in flight at once (e.g. a burst of pipelined queries on one
|
|
88
|
+
* connection) - set to `false` to skip it, which is also what makes an
|
|
89
|
+
* apples-to-apples benchmark against a client that does not offer this
|
|
90
|
+
* fair.
|
|
91
|
+
* @default true
|
|
92
|
+
*/
|
|
93
|
+
asyncErrorHandling?: boolean;
|
|
29
94
|
debugLogger?: DebugLogger;
|
|
30
95
|
}
|
|
96
|
+
export type TargetSessionAttrs = 'read-write' | 'read-only' | 'primary' | 'standby' | 'prefer-standby';
|
|
31
97
|
export interface SocketOptions {
|
|
32
98
|
keepAlive?: boolean;
|
|
33
99
|
}
|
|
@@ -35,4 +101,33 @@ export interface ConnectionConfiguration extends DatabaseConnectionParams, Socke
|
|
|
35
101
|
buffer?: SmartBufferConfig;
|
|
36
102
|
}
|
|
37
103
|
export interface PoolConfiguration extends ConnectionConfiguration, LPoolConfiguration {
|
|
104
|
+
/**
|
|
105
|
+
* How many of Pool.query()'s one-shot queries may share a single pooled
|
|
106
|
+
* connection at the same time (default 100).
|
|
107
|
+
*
|
|
108
|
+
* PostgreSQL correlates responses to requests by order, so several
|
|
109
|
+
* queries can be in flight on one connection at once, each with its own
|
|
110
|
+
* Sync and therefore its own error boundary. Dispatching that way means
|
|
111
|
+
* `max` stops being a ceiling on concurrent queries and becomes only a
|
|
112
|
+
* ceiling on connections - with the default pool of 10, a burst of 1000
|
|
113
|
+
* queries no longer has to run as 100 sequential rounds of 10.
|
|
114
|
+
*
|
|
115
|
+
* Set to 1 for the older behaviour, where Pool.query() holds a
|
|
116
|
+
* connection exclusively for the duration of each query. Connections
|
|
117
|
+
* handed out by acquire() are never shared, whatever this is set to, so
|
|
118
|
+
* transactions, cursors and prepared statements are unaffected.
|
|
119
|
+
*/
|
|
120
|
+
pipelineMaxQueries?: number;
|
|
121
|
+
/**
|
|
122
|
+
* How many pooled connections the pipelined path may borrow at once
|
|
123
|
+
* (default: the pool's own `max`).
|
|
124
|
+
*
|
|
125
|
+
* The server runs one connection's statements serially, so a burst
|
|
126
|
+
* spread over ten connections finishes in a fraction of the time it
|
|
127
|
+
* takes on one. Borrowed connections are handed straight back the
|
|
128
|
+
* moment they go idle, so this is a ceiling on how wide a single burst
|
|
129
|
+
* may fan out, not a reservation - lower it to keep connections free
|
|
130
|
+
* for acquire() while a burst is running.
|
|
131
|
+
*/
|
|
132
|
+
pipelineMaxConnections?: number;
|
|
38
133
|
}
|
|
@@ -43,4 +43,21 @@ export interface QueryOptions extends DataMappingOptions {
|
|
|
43
43
|
* @default true
|
|
44
44
|
*/
|
|
45
45
|
rollbackOnError?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Aborts the call when the signal fires: the running statement is
|
|
48
|
+
* cancelled on the server and the promise rejects with the signal's own
|
|
49
|
+
* reason, with the database error attached as `cause`.
|
|
50
|
+
*
|
|
51
|
+
* Cancelling is a request rather than a guarantee - it travels on its own
|
|
52
|
+
* connection and the statement may finish first - so the call settles when
|
|
53
|
+
* the server has actually finished with it, not the moment abort() is
|
|
54
|
+
* called. `AbortSignal.timeout(ms)` gives a per-call timeout.
|
|
55
|
+
*/
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
/**
|
|
58
|
+
* Overrides the connection's own `asyncErrorHandling` for this call only -
|
|
59
|
+
* see `DatabaseConnectionParams.asyncErrorHandling`.
|
|
60
|
+
* @default the connection's own setting
|
|
61
|
+
*/
|
|
62
|
+
asyncErrorHandling?: boolean;
|
|
46
63
|
}
|
|
@@ -23,4 +23,21 @@ export interface ScriptExecuteOptions extends DataMappingOptions {
|
|
|
23
23
|
* @default true
|
|
24
24
|
*/
|
|
25
25
|
rollbackOnError?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Aborts the call when the signal fires: the running statement is
|
|
28
|
+
* cancelled on the server and the promise rejects with the signal's own
|
|
29
|
+
* reason, with the database error attached as `cause`.
|
|
30
|
+
*
|
|
31
|
+
* Cancelling is a request rather than a guarantee - it travels on its own
|
|
32
|
+
* connection and the statement may finish first - so the call settles when
|
|
33
|
+
* the server has actually finished with it, not the moment abort() is
|
|
34
|
+
* called. `AbortSignal.timeout(ms)` gives a per-call timeout.
|
|
35
|
+
*/
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
/**
|
|
38
|
+
* Overrides the connection's own `asyncErrorHandling` for this call only -
|
|
39
|
+
* see `DatabaseConnectionParams.asyncErrorHandling`.
|
|
40
|
+
* @default the connection's own setting
|
|
41
|
+
*/
|
|
42
|
+
asyncErrorHandling?: boolean;
|
|
26
43
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postgrejs",
|
|
3
3
|
"description": "Professional PostgreSQL client NodeJS",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.1",
|
|
5
5
|
"author": "Panates",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "BSD-3-Clause",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@jsopen/objects": "^2.2.
|
|
8
|
+
"@jsopen/objects": "^2.2.3",
|
|
9
9
|
"doublylinked": "^2.5.6",
|
|
10
10
|
"lightning-pool": "^4.12.0",
|
|
11
|
-
"postgres-bytea": "^3.0.0",
|
|
12
11
|
"power-tasks": "^1.14.2",
|
|
13
12
|
"putil-promisify": "^1.10.1",
|
|
14
13
|
"putil-varhelpers": "^1.7.0",
|
package/protocol/backend.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { Protocol } from './protocol.js';
|
|
2
2
|
declare type ParseCallback = (code: Protocol.BackendMessageCode, data?: any) => void;
|
|
3
3
|
export declare class Backend {
|
|
4
|
-
private
|
|
4
|
+
private readonly _headerBuf;
|
|
5
|
+
private _headerFilled;
|
|
6
|
+
private _code?;
|
|
7
|
+
private _len;
|
|
8
|
+
private _bodyBuf?;
|
|
9
|
+
private _bodyFilled;
|
|
5
10
|
reset(): void;
|
|
6
11
|
parse(data: Buffer, callback: ParseCallback): void;
|
|
7
12
|
}
|
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;
|