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.
Files changed (107) hide show
  1. package/CHANGELOG.md +171 -1
  2. package/LICENSE +26 -17
  3. package/README.md +195 -175
  4. package/connection/connection.d.ts +169 -16
  5. package/connection/connection.js +318 -56
  6. package/connection/copy-stream.d.ts +88 -0
  7. package/connection/copy-stream.js +244 -0
  8. package/connection/cursor.js +20 -13
  9. package/connection/intl-connection.d.ts +113 -5
  10. package/connection/intl-connection.js +500 -70
  11. package/connection/large-object.d.ts +65 -0
  12. package/connection/large-object.js +151 -0
  13. package/connection/logical-replication.d.ts +121 -0
  14. package/connection/logical-replication.js +279 -0
  15. package/connection/pool.d.ts +61 -4
  16. package/connection/pool.js +177 -29
  17. package/connection/portal.d.ts +18 -4
  18. package/connection/portal.js +53 -59
  19. package/connection/prepared-statement.d.ts +22 -1
  20. package/connection/prepared-statement.js +129 -109
  21. package/data-type-map.js +4 -2
  22. package/data-types/bool-type.js +14 -10
  23. package/data-types/box-type.js +15 -8
  24. package/data-types/bytea-type.js +9 -5
  25. package/data-types/char-type.js +11 -5
  26. package/data-types/circle-type.js +15 -7
  27. package/data-types/date-type.js +25 -19
  28. package/data-types/float4-type.js +10 -3
  29. package/data-types/float8-type.js +12 -3
  30. package/data-types/int2-type.js +9 -4
  31. package/data-types/int2-vector-type.js +9 -4
  32. package/data-types/int4-type.js +11 -5
  33. package/data-types/int8-type.js +22 -7
  34. package/data-types/json-type.js +17 -8
  35. package/data-types/jsonb-type.js +20 -14
  36. package/data-types/lseg-type.js +16 -8
  37. package/data-types/numeric-type.d.ts +8 -0
  38. package/data-types/numeric-type.js +118 -9
  39. package/data-types/oid-type.d.ts +0 -1
  40. package/data-types/oid-type.js +9 -10
  41. package/data-types/oid-vector-type.d.ts +9 -0
  42. package/data-types/oid-vector-type.js +51 -0
  43. package/data-types/point-type.js +14 -6
  44. package/data-types/time-type.js +20 -13
  45. package/data-types/timestamp-type.js +32 -25
  46. package/data-types/timestamptz-type.js +33 -21
  47. package/data-types/uuid-type.js +21 -11
  48. package/data-types/varchar-type.js +16 -4
  49. package/index.d.ts +5 -0
  50. package/index.js +5 -0
  51. package/interfaces/command-result.d.ts +2 -1
  52. package/interfaces/data-type.d.ts +5 -3
  53. package/interfaces/database-connection-params.d.ts +108 -0
  54. package/interfaces/query-options.d.ts +23 -0
  55. package/interfaces/script-execute-options.d.ts +23 -0
  56. package/interfaces/script-result.d.ts +3 -2
  57. package/package.json +2 -3
  58. package/protocol/backend.d.ts +6 -1
  59. package/protocol/backend.js +70 -45
  60. package/protocol/buffer-reader.d.ts +0 -2
  61. package/protocol/buffer-reader.js +3 -10
  62. package/protocol/cert-signature.d.ts +12 -0
  63. package/protocol/cert-signature.js +114 -0
  64. package/protocol/frontend.d.ts +16 -0
  65. package/protocol/frontend.js +70 -11
  66. package/protocol/pg-socket.d.ts +220 -9
  67. package/protocol/pg-socket.js +539 -102
  68. package/protocol/pgoutput.d.ts +76 -0
  69. package/protocol/pgoutput.js +155 -0
  70. package/protocol/protocol.d.ts +3 -4
  71. package/protocol/protocol.js +1 -1
  72. package/protocol/sasl.d.ts +11 -1
  73. package/protocol/sasl.js +36 -5
  74. package/protocol/smart-buffer.d.ts +0 -2
  75. package/protocol/smart-buffer.js +11 -16
  76. package/types.d.ts +5 -3
  77. package/util/abort-signal.d.ts +34 -0
  78. package/util/abort-signal.js +76 -0
  79. package/util/array-calculatedim.js +3 -1
  80. package/util/connection-config.js +77 -3
  81. package/util/decode-binaryarray.d.ts +1 -1
  82. package/util/decode-binaryarray.js +15 -6
  83. package/util/encode-binaryarray.js +7 -3
  84. package/util/escape-identifier.d.ts +14 -0
  85. package/util/escape-identifier.js +32 -0
  86. package/util/escape-literal.js +3 -0
  87. package/util/fast-parseint.d.ts +1 -0
  88. package/util/fast-parseint.js +12 -17
  89. package/util/format-datetime.d.ts +10 -0
  90. package/util/format-datetime.js +71 -0
  91. package/util/get-parsers.js +31 -10
  92. package/util/parse-array.js +2 -2
  93. package/util/parse-bytea.d.ts +2 -0
  94. package/util/parse-bytea.js +83 -0
  95. package/util/parse-datetime.d.ts +3 -1
  96. package/util/parse-datetime.js +86 -17
  97. package/util/parse-row.d.ts +3 -1
  98. package/util/parse-row.js +60 -5
  99. package/util/parse-time.js +8 -1
  100. package/util/sql-tag.d.ts +60 -0
  101. package/util/sql-tag.js +202 -0
  102. package/util/starts-transaction.d.ts +38 -0
  103. package/util/starts-transaction.js +148 -0
  104. package/util/stringify-arrayliteral.js +13 -1
  105. package/util/wrap-row-description.js +13 -4
  106. package/util/convert-row-to-object.d.ts +0 -2
  107. package/util/convert-row-to-object.js +0 -9
@@ -3,13 +3,22 @@ export const Float8Type = {
3
3
  name: 'float8',
4
4
  oid: DataTypeOIDs.float8,
5
5
  jsType: 'number',
6
- parseBinary(v) {
7
- return v.readDoubleBE(0);
6
+ fixedBinarySize: 8,
7
+ // Infinity/NaN stringify to the words PostgreSQL itself uses.
8
+ encodeText(v) {
9
+ return '' + v;
8
10
  },
9
11
  encodeBinary(buf, v) {
10
12
  buf.writeDoubleBE(typeof v === 'number' ? v : parseFloat(v));
11
13
  },
12
- parseText: parseFloat,
14
+ decodeBinary(v, offset = 0) {
15
+ return v.readDoubleBE(offset);
16
+ },
17
+ decodeText: parseFloat,
18
+ // See float4-type.ts's decodeTextBuffer comment - same rationale.
19
+ decodeTextBuffer(buf, offset, len) {
20
+ return parseFloat(buf.toString('latin1', offset, offset + len));
21
+ },
13
22
  isType(v) {
14
23
  return typeof v === 'number';
15
24
  },
@@ -1,16 +1,21 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
- import { fastParseInt } from '../util/fast-parseint.js';
2
+ import { fastParseInt, fastParseIntBuffer } from '../util/fast-parseint.js';
3
3
  export const Int2Type = {
4
4
  name: 'int2',
5
5
  oid: DataTypeOIDs.int2,
6
6
  jsType: 'number',
7
- parseBinary(v) {
8
- return v.readInt16BE(0);
7
+ fixedBinarySize: 2,
8
+ encodeText(v) {
9
+ return '' + v;
9
10
  },
10
11
  encodeBinary(buf, v) {
11
12
  buf.writeInt16BE(fastParseInt(v));
12
13
  },
13
- parseText: fastParseInt,
14
+ decodeBinary(v, offset = 0) {
15
+ return v.readInt16BE(offset);
16
+ },
17
+ decodeText: fastParseInt,
18
+ decodeTextBuffer: fastParseIntBuffer,
14
19
  isType(v) {
15
20
  return (typeof v === 'number' && Number.isInteger(v) && v >= -32768 && v <= 32767);
16
21
  },
@@ -7,9 +7,6 @@ export const Int2VectorType = {
7
7
  name: 'int2vector',
8
8
  oid: DataTypeOIDs.int2vector,
9
9
  jsType: 'array',
10
- parseBinary(v) {
11
- return decodeBinaryArray(v, b => b.readInt16BE()) || undefined;
12
- },
13
10
  encodeBinary(buf, v) {
14
11
  encodeBinaryArray(buf, v, DataTypeOIDs.int2, {}, (io, x) => {
15
12
  io.writeInt16BE(x);
@@ -18,9 +15,17 @@ export const Int2VectorType = {
18
15
  encodeCalculateDim(v) {
19
16
  return [v.length];
20
17
  },
21
- parseText(str) {
18
+ decodeBinary(v) {
19
+ return decodeBinaryArray(v, 0, b => b.readInt16BE()) || undefined;
20
+ },
21
+ decodeText(str) {
22
22
  return str.split(' ').map(fastParseInt);
23
23
  },
24
+ // See box-type.ts's decodeTextBuffer comment - same rationale (this text
25
+ // grammar is pure ASCII digits/minus/space).
26
+ decodeTextBuffer(buf, offset, len, options) {
27
+ return Int2VectorType.decodeText(buf.toString('latin1', offset, offset + len), options);
28
+ },
24
29
  encodeText(v) {
25
30
  return v.join(' ');
26
31
  },
@@ -1,20 +1,26 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
- import { fastParseInt } from '../util/fast-parseint.js';
2
+ import { fastParseInt, fastParseIntBuffer } from '../util/fast-parseint.js';
3
3
  export const Int4Type = {
4
4
  name: 'int4',
5
5
  oid: DataTypeOIDs.int4,
6
6
  jsType: 'number',
7
- parseBinary(v) {
8
- return v.readInt32BE(0);
7
+ fixedBinarySize: 4,
8
+ encodeText(v) {
9
+ return '' + v;
9
10
  },
10
11
  encodeBinary(buf, v) {
11
12
  buf.writeInt32BE(fastParseInt(v));
12
13
  },
13
- parseText: fastParseInt,
14
+ decodeBinary(v, offset = 0) {
15
+ return v.readInt32BE(offset);
16
+ },
17
+ decodeText: fastParseInt,
18
+ decodeTextBuffer: fastParseIntBuffer,
14
19
  isType(v) {
15
20
  return (typeof v === 'number' &&
16
21
  Number.isInteger(v) &&
17
- v <= Number.MAX_SAFE_INTEGER);
22
+ v >= -2147483648 &&
23
+ v <= 2147483647);
18
24
  },
19
25
  };
20
26
  export const ArrayInt4Type = {
@@ -1,28 +1,43 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
2
  import { readBigInt64BE } from '../util/bigint-methods.js';
3
+ import { fastParseIntBuffer } from '../util/fast-parseint.js';
3
4
  const maxSafeInteger = BigInt(Number.MAX_SAFE_INTEGER);
4
5
  export const Int8Type = {
5
6
  name: 'int8',
6
7
  oid: DataTypeOIDs.int8,
7
8
  jsType: 'BigInt',
8
- parseBinary(buf) {
9
- const v = typeof buf.readBigInt64BE === 'function'
10
- ? buf.readBigInt64BE(0)
11
- : readBigInt64BE(buf);
12
- return v >= -maxSafeInteger && v <= maxSafeInteger ? Number(v) : v;
9
+ fixedBinarySize: 8,
10
+ encodeText(v) {
11
+ return '' + v;
13
12
  },
14
13
  encodeBinary(buf, v) {
15
14
  buf.writeBigInt64BE(v);
16
15
  },
17
- parseText(s) {
16
+ decodeBinary(buf, offset = 0) {
17
+ const v = typeof buf.readBigInt64BE === 'function'
18
+ ? buf.readBigInt64BE(offset)
19
+ : readBigInt64BE(buf, offset);
20
+ return v >= -maxSafeInteger && v <= maxSafeInteger ? Number(v) : v;
21
+ },
22
+ decodeText(s) {
23
+ const digits = s.charCodeAt(0) === 45 /* '-' */ ? s.length - 1 : s.length;
24
+ if (digits <= 15)
25
+ return Number(s);
18
26
  const v = BigInt(s);
19
27
  return v >= -maxSafeInteger && v <= maxSafeInteger ? Number(v) : v;
20
28
  },
29
+ decodeTextBuffer(buf, offset, len) {
30
+ const digits = buf[offset] === 45 /* '-' */ ? len - 1 : len;
31
+ if (digits <= 15)
32
+ return fastParseIntBuffer(buf, offset, len);
33
+ const v = BigInt(buf.toString('utf8', offset, offset + len));
34
+ return v >= -maxSafeInteger && v <= maxSafeInteger ? Number(v) : v;
35
+ },
21
36
  isType(v) {
22
37
  return (typeof v === 'bigint' ||
23
38
  (typeof v === 'number' &&
24
39
  Number.isInteger(v) &&
25
- v > Number.MAX_SAFE_INTEGER));
40
+ (v > 2147483647 || v < -2147483648)));
26
41
  },
27
42
  };
28
43
  export const ArrayInt8Type = {
@@ -3,28 +3,37 @@ export const JsonType = {
3
3
  name: 'json',
4
4
  oid: DataTypeOIDs.json,
5
5
  jsType: 'string',
6
- parseBinary(v, options) {
7
- const content = v.toString('utf8');
8
- const fetchAsString = options.fetchAsString &&
9
- options.fetchAsString.includes(DataTypeOIDs.jsonb);
6
+ decodeBinary(v, offset = 0, options) {
7
+ const content = v.toString('utf8', offset);
8
+ const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.json);
10
9
  if (fetchAsString)
11
10
  return content;
12
11
  return content ? JSON.parse(content) : undefined;
13
12
  },
13
+ // json is stored as the text itself, so its binary form is those bytes.
14
+ encodeBinary(buf, v) {
15
+ buf.writeString(JsonType.encodeText(v, {}), 'utf8');
16
+ },
14
17
  encodeText(v) {
15
- if (typeof v === 'object' || typeof v === 'bigint')
18
+ // JSON.stringify() cannot serialize a BigInt itself (it throws), so a
19
+ // bigint is written directly as a bare numeric literal instead.
20
+ if (typeof v === 'bigint')
21
+ return v.toString();
22
+ if (typeof v === 'object')
16
23
  return JSON.stringify(v);
17
24
  if (typeof v === 'boolean')
18
25
  return v ? 'true' : 'false';
19
26
  return '' + v;
20
27
  },
21
- parseText(v, options) {
22
- const fetchAsString = options.fetchAsString &&
23
- options.fetchAsString.includes(DataTypeOIDs.jsonb);
28
+ decodeText(v, options) {
29
+ const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.json);
24
30
  if (fetchAsString)
25
31
  return v;
26
32
  return v ? JSON.parse(v) : null;
27
33
  },
34
+ decodeTextBuffer(buf, offset, len, options) {
35
+ return JsonType.decodeText(buf.toString('utf8', offset, offset + len), options);
36
+ },
28
37
  isType(v) {
29
38
  return v && typeof v === 'object';
30
39
  },
@@ -4,31 +4,37 @@ export const JsonbType = {
4
4
  name: 'jsonb',
5
5
  oid: DataTypeOIDs.jsonb,
6
6
  jsType: 'string',
7
- parseBinary(v, options) {
8
- const buf = new BufferReader(v);
7
+ encodeText(v) {
8
+ if (typeof v === 'object' || typeof v === 'bigint')
9
+ return JSON.stringify(v);
10
+ if (typeof v === 'boolean')
11
+ return v ? 'true' : 'false';
12
+ return '' + v;
13
+ },
14
+ encodeBinary(buf, v) {
15
+ buf.writeUInt8(1);
16
+ buf.writeString(JsonbType.encodeText(v, {}), 'utf8');
17
+ },
18
+ decodeBinary(v, offset = 0, options) {
19
+ const buf = new BufferReader(offset ? v.subarray(offset) : v);
9
20
  if (buf.readUInt8() !== 1)
10
21
  throw new Error('Unexpected Jsonb version value in header');
11
- const fetchAsString = options.fetchAsString &&
12
- options.fetchAsString.includes(DataTypeOIDs.json);
22
+ const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.jsonb);
13
23
  const content = buf.readLString(buf.length - buf.offset);
14
24
  if (fetchAsString)
15
25
  return content;
16
26
  return content ? JSON.parse(content) : undefined;
17
27
  },
18
- encodeText(v) {
19
- if (typeof v === 'object' || typeof v === 'bigint')
20
- return JSON.stringify(v);
21
- if (typeof v === 'boolean')
22
- return v ? 'true' : 'false';
23
- return '\x0001' + v;
24
- },
25
- parseText(v, options) {
26
- const fetchAsString = options.fetchAsString &&
27
- options.fetchAsString.includes(DataTypeOIDs.json);
28
+ decodeText(v, options) {
29
+ const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.jsonb);
28
30
  if (fetchAsString)
29
31
  return v;
30
32
  return v ? JSON.parse(v) : null;
31
33
  },
34
+ // See json-type.ts's decodeTextBuffer comment - same rationale.
35
+ decodeTextBuffer(buf, offset, len, options) {
36
+ return JsonbType.decodeText(buf.toString('utf8', offset, offset + len), options);
37
+ },
32
38
  isType(v) {
33
39
  return v && typeof v === 'object';
34
40
  },
@@ -7,13 +7,9 @@ export const LsegType = {
7
7
  name: 'lseg',
8
8
  oid: DataTypeOIDs.lseg,
9
9
  jsType: 'object',
10
- parseBinary(v) {
11
- return {
12
- x1: v.readDoubleBE(0),
13
- y1: v.readDoubleBE(8),
14
- x2: v.readDoubleBE(16),
15
- y2: v.readDoubleBE(24),
16
- };
10
+ fixedBinarySize: 32,
11
+ encodeText(v) {
12
+ return `[(${v.x1},${v.y1}),(${v.x2},${v.y2})]`;
17
13
  },
18
14
  encodeBinary(buf, v) {
19
15
  buf.writeDoubleBE(v.x1);
@@ -21,7 +17,15 @@ export const LsegType = {
21
17
  buf.writeDoubleBE(v.x2);
22
18
  buf.writeDoubleBE(v.y2);
23
19
  },
24
- parseText(v) {
20
+ decodeBinary(v, offset = 0) {
21
+ return {
22
+ x1: v.readDoubleBE(offset),
23
+ y1: v.readDoubleBE(offset + 8),
24
+ x2: v.readDoubleBE(offset + 16),
25
+ y2: v.readDoubleBE(offset + 24),
26
+ };
27
+ },
28
+ decodeText(v) {
25
29
  const m = v.match(LSEG_PATTERN1) ||
26
30
  v.match(LSEG_PATTERN2) ||
27
31
  v.match(LSEG_PATTERN3) ||
@@ -35,6 +39,10 @@ export const LsegType = {
35
39
  y2: parseFloat(m[4]),
36
40
  };
37
41
  },
42
+ // See box-type.ts's decodeTextBuffer comment - same rationale.
43
+ decodeTextBuffer(buf, offset, len, options) {
44
+ return LsegType.decodeText(buf.toString('latin1', offset, offset + len), options);
45
+ },
38
46
  isType(v) {
39
47
  return (typeof v === 'object' &&
40
48
  Object.keys(v).length === 4 &&
@@ -1,6 +1,14 @@
1
1
  import type { DataType } from '../interfaces/data-type.js';
2
2
  export declare const NumericType: DataType;
3
3
  export declare const ArrayNumericType: DataType;
4
+ /**
5
+ * Turns a JS exponential-notation number string (e.g. "1e+21", "-1.5e-7")
6
+ * into plain decimal notation by shifting the decimal point across the
7
+ * mantissa's own digits - no floating-point math involved, so it stays
8
+ * exact for magnitudes past what toFixed() can express (it reverts to
9
+ * exponential notation itself once |x| reaches 1e21).
10
+ */
11
+ export declare function expandExponential(str: string): string;
4
12
  /**
5
13
  * Convert a number from binary representation to text representation.
6
14
  * @param digits array of shorts that can be decoded as the number String
@@ -1,22 +1,105 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
2
  const NUMERIC_NEG = 0x4000;
3
3
  const NUMERIC_NAN = 0xc000;
4
+ const NUMERIC_PINF = 0xd000;
5
+ const NUMERIC_NINF = 0xf000;
4
6
  const DEC_DIGITS = 4;
5
7
  const ROUND_POWERS = [0, 1000, 100, 10];
6
8
  export const NumericType = {
7
9
  name: 'numeric',
8
10
  oid: DataTypeOIDs.numeric,
9
11
  jsType: 'number',
10
- parseBinary(v) {
11
- const len = v.readInt16BE();
12
- const weight = v.readInt16BE(2);
13
- const sign = v.readInt16BE(4);
14
- const scale = v.readInt16BE(6);
12
+ /**
13
+ * numeric on the wire is a base-10000 number: a digit count, the weight
14
+ * of the first group (in groups of four decimal digits, not digits), a
15
+ * sign mask, the display scale, then the groups themselves.
16
+ *
17
+ * Encoded from the decimal text rather than from the float, so a value
18
+ * that JavaScript cannot hold exactly - which is most of what numeric
19
+ * exists for - survives when it was given as a string.
20
+ */
21
+ encodeBinary(buf, v) {
22
+ const writeHeader = (ndigits, weight, sign, dscale) => {
23
+ buf.writeInt16BE(ndigits);
24
+ buf.writeInt16BE(weight);
25
+ buf.writeUInt16BE(sign);
26
+ buf.writeInt16BE(dscale);
27
+ };
28
+ if (typeof v === 'number' && !Number.isFinite(v)) {
29
+ writeHeader(0, 0, Number.isNaN(v) ? NUMERIC_NAN : v > 0 ? NUMERIC_PINF : NUMERIC_NINF, 0);
30
+ return;
31
+ }
32
+ let str = typeof v === 'string' ? v.trim() : String(v);
33
+ if (str === 'NaN')
34
+ return writeHeader(0, 0, NUMERIC_NAN, 0);
35
+ if (str === 'Infinity')
36
+ return writeHeader(0, 0, NUMERIC_PINF, 0);
37
+ if (str === '-Infinity')
38
+ return writeHeader(0, 0, NUMERIC_NINF, 0);
39
+ // Exponent form has no place in the wire format; go through Number to
40
+ // get it back into plain notation. toFixed() cannot do this itself -
41
+ // per spec it falls back to exponential notation once the magnitude
42
+ // reaches 1e21, so this shifts the decimal point manually instead.
43
+ if (/e/i.test(str))
44
+ str = expandExponential(Number(str).toString());
45
+ let sign = 0;
46
+ if (str.startsWith('-')) {
47
+ sign = NUMERIC_NEG;
48
+ str = str.substring(1);
49
+ }
50
+ else if (str.startsWith('+'))
51
+ str = str.substring(1);
52
+ const dot = str.indexOf('.');
53
+ let intPart = dot < 0 ? str : str.substring(0, dot);
54
+ let fracPart = dot < 0 ? '' : str.substring(dot + 1);
55
+ const dscale = fracPart.length;
56
+ // Group into fours, aligned on the decimal point from both sides.
57
+ const intPad = (DEC_DIGITS - (intPart.length % DEC_DIGITS)) % DEC_DIGITS;
58
+ intPart = '0'.repeat(intPad) + intPart;
59
+ const fracPad = (DEC_DIGITS - (fracPart.length % DEC_DIGITS)) % DEC_DIGITS;
60
+ fracPart = fracPart + '0'.repeat(fracPad);
61
+ const digits = [];
62
+ let i;
63
+ let l = intPart.length;
64
+ for (i = 0; i < l; i += DEC_DIGITS)
65
+ digits.push(+intPart.substring(i, i + DEC_DIGITS));
66
+ l = fracPart.length;
67
+ for (i = 0; i < l; i += DEC_DIGITS)
68
+ digits.push(+fracPart.substring(i, i + DEC_DIGITS));
69
+ // weight counts groups before the point, less one; leading zero groups
70
+ // are dropped and lower it, trailing ones are simply dropped.
71
+ let weight = intPart.length / DEC_DIGITS - 1;
72
+ while (digits.length && digits[0] === 0) {
73
+ digits.shift();
74
+ weight--;
75
+ }
76
+ while (digits.length && digits[digits.length - 1] === 0)
77
+ digits.pop();
78
+ if (!digits.length)
79
+ weight = 0;
80
+ writeHeader(digits.length, weight, sign, dscale);
81
+ l = digits.length;
82
+ for (i = 0; i < l; i++) {
83
+ buf.writeInt16BE(digits[i]);
84
+ }
85
+ },
86
+ decodeBinary(v, offset = 0) {
87
+ const len = v.readInt16BE(offset);
88
+ const weight = v.readInt16BE(offset + 2);
89
+ // sign is a bitmask (0x0000/0x4000/0xC000/0xD000/0xF000), not a two's
90
+ // complement quantity - must be read unsigned or NaN/Infinity sign
91
+ // values (which set the top bit) never compare equal to the constants.
92
+ const sign = v.readUInt16BE(offset + 4);
93
+ const scale = v.readInt16BE(offset + 6);
15
94
  if (sign === NUMERIC_NAN)
16
95
  return NaN;
96
+ if (sign === NUMERIC_PINF)
97
+ return Infinity;
98
+ if (sign === NUMERIC_NINF)
99
+ return -Infinity;
17
100
  const digits = [];
18
101
  for (let i = 0; i < len; i++) {
19
- digits[i] = v.readInt16BE(8 + i * 2);
102
+ digits[i] = v.readInt16BE(offset + 8 + i * 2);
20
103
  }
21
104
  const numString = numberBytesToString(digits, scale, weight, sign);
22
105
  return parseFloat(numString);
@@ -25,7 +108,11 @@ export const NumericType = {
25
108
  const n = typeof v === 'number' ? v : parseFloat(v);
26
109
  return '' + n;
27
110
  },
28
- parseText: parseFloat,
111
+ decodeText: parseFloat,
112
+ // See float4-type.ts's decodeTextBuffer comment - same rationale.
113
+ decodeTextBuffer(buf, offset, len) {
114
+ return parseFloat(buf.toString('latin1', offset, offset + len));
115
+ },
29
116
  isType(v) {
30
117
  return typeof v === 'number';
31
118
  },
@@ -36,6 +123,26 @@ export const ArrayNumericType = {
36
123
  oid: DataTypeOIDs._numeric,
37
124
  elementsOID: DataTypeOIDs.numeric,
38
125
  };
126
+ /**
127
+ * Turns a JS exponential-notation number string (e.g. "1e+21", "-1.5e-7")
128
+ * into plain decimal notation by shifting the decimal point across the
129
+ * mantissa's own digits - no floating-point math involved, so it stays
130
+ * exact for magnitudes past what toFixed() can express (it reverts to
131
+ * exponential notation itself once |x| reaches 1e21).
132
+ */
133
+ export function expandExponential(str) {
134
+ const m = str.match(/^(-)?(\d+)(?:\.(\d+))?e([+-]?\d+)$/i);
135
+ if (!m)
136
+ return str;
137
+ const sign = m[1] || '';
138
+ const digits = m[2] + (m[3] || '');
139
+ const pointPos = m[2].length + parseInt(m[4], 10);
140
+ if (pointPos <= 0)
141
+ return sign + '0.' + '0'.repeat(-pointPos) + digits;
142
+ if (pointPos >= digits.length)
143
+ return sign + digits + '0'.repeat(pointPos - digits.length);
144
+ return sign + digits.slice(0, pointPos) + '.' + digits.slice(pointPos);
145
+ }
39
146
  /* https://github.com/pgjdbc/pgjdbc/blob/3eca3a76aa4a04cb28cb960ed674cb67db30b5e3/pgjdbc/src/main/java/org/postgresql/util/ByteConverter.java */
40
147
  /**
41
148
  * Convert a number from binary representation to text representation.
@@ -88,7 +195,7 @@ export function numberBytesToString(digits, scale, weight, sign) {
88
195
  }
89
196
  }
90
197
  const extra = (i - scale) % DEC_DIGITS;
91
- return out.substr(0, out.length - extra);
198
+ return out.substring(0, out.length - extra);
92
199
  }
93
200
  /* https://github.com/pgjdbc/pgjdbc/blob/3eca3a76aa4a04cb28cb960ed674cb67db30b5e3/pgjdbc/src/main/java/org/postgresql/util/ByteConverter.java */
94
201
  /**
@@ -103,7 +210,9 @@ function digitToString(idx, digits, alwaysPutIt) {
103
210
  let dig = idx >= 0 && idx < digits.length ? digits[idx] : 0;
104
211
  // Each dig represents 4 decimal digits (e.g. 9999)
105
212
  // If we continue the number, then we need to print 0 as 0000 (alwaysPutIt parameter is true)
106
- for (let p = 1; p < ROUND_POWERS.length; p++) {
213
+ const l = ROUND_POWERS.length;
214
+ let p;
215
+ for (p = 1; p < l; p++) {
107
216
  const pow = ROUND_POWERS[p];
108
217
  const d1 = Math.trunc(dig / pow);
109
218
  dig -= d1 * pow;
@@ -1,4 +1,3 @@
1
1
  import type { DataType } from '../interfaces/data-type.js';
2
2
  export declare const OidType: DataType;
3
3
  export declare const ArrayOidType: DataType;
4
- export declare const VectorOidType: DataType;
@@ -1,16 +1,21 @@
1
1
  import { DataTypeNames, DataTypeOIDs } from '../constants.js';
2
- import { fastParseInt } from '../util/fast-parseint.js';
2
+ import { fastParseInt, fastParseIntBuffer } from '../util/fast-parseint.js';
3
3
  export const OidType = {
4
4
  name: 'oid',
5
5
  oid: DataTypeOIDs.oid,
6
6
  jsType: 'number',
7
- parseBinary(v) {
8
- return v.readUInt32BE(0);
7
+ fixedBinarySize: 4,
8
+ encodeText(v) {
9
+ return '' + v;
9
10
  },
10
11
  encodeBinary(buf, v) {
11
12
  buf.writeUInt32BE(fastParseInt(v));
12
13
  },
13
- parseText: fastParseInt,
14
+ decodeBinary(v, offset = 0) {
15
+ return v.readUInt32BE(offset);
16
+ },
17
+ decodeText: fastParseInt,
18
+ decodeTextBuffer: fastParseIntBuffer,
14
19
  isType(v) {
15
20
  return typeof v === 'number' && Number.isInteger(v) && !!DataTypeNames[v];
16
21
  },
@@ -21,9 +26,3 @@ export const ArrayOidType = {
21
26
  oid: DataTypeOIDs._oid,
22
27
  elementsOID: DataTypeOIDs.oid,
23
28
  };
24
- export const VectorOidType = {
25
- ...OidType,
26
- name: '_oidvector',
27
- oid: DataTypeOIDs.oidvector,
28
- elementsOID: DataTypeOIDs.oid,
29
- };
@@ -0,0 +1,9 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ /**
3
+ * `oidvector` (OID 30) is a list of OIDs, not the array type of `oid` -
4
+ * that is `_oid` (1028). It has its own array type, `_oidvector` (1013),
5
+ * exactly as `int2vector` has `_int2vector`, and its text form is
6
+ * space-separated rather than braced.
7
+ */
8
+ export declare const OidVectorType: DataType;
9
+ export declare const ArrayOidVectorType: DataType;
@@ -0,0 +1,51 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ import { SmartBuffer } from '../protocol/smart-buffer.js';
3
+ import { decodeBinaryArray } from '../util/decode-binaryarray.js';
4
+ import { encodeBinaryArray } from '../util/encode-binaryarray.js';
5
+ import { fastParseInt } from '../util/fast-parseint.js';
6
+ /**
7
+ * `oidvector` (OID 30) is a list of OIDs, not the array type of `oid` -
8
+ * that is `_oid` (1028). It has its own array type, `_oidvector` (1013),
9
+ * exactly as `int2vector` has `_int2vector`, and its text form is
10
+ * space-separated rather than braced.
11
+ */
12
+ export const OidVectorType = {
13
+ name: 'oidvector',
14
+ oid: DataTypeOIDs.oidvector,
15
+ jsType: 'array',
16
+ encodeBinary(buf, v) {
17
+ encodeBinaryArray(buf, v, DataTypeOIDs.oid, {}, (io, x) => {
18
+ io.writeUInt32BE(x);
19
+ });
20
+ },
21
+ encodeCalculateDim(v) {
22
+ return [v.length];
23
+ },
24
+ decodeBinary(v) {
25
+ return decodeBinaryArray(v, 0, b => b.readUInt32BE()) || undefined;
26
+ },
27
+ decodeText(str) {
28
+ return str.split(' ').map(fastParseInt);
29
+ },
30
+ // See box-type.ts's decodeTextBuffer comment - same rationale (this text
31
+ // grammar is pure ASCII digits and spaces).
32
+ decodeTextBuffer(buf, offset, len, options) {
33
+ return OidVectorType.decodeText(buf.toString('latin1', offset, offset + len), options);
34
+ },
35
+ encodeText(v) {
36
+ return v.join(' ');
37
+ },
38
+ isType(v) {
39
+ return (Array.isArray(v) &&
40
+ !v.find(x => !(typeof x === 'number' &&
41
+ Number.isInteger(x) &&
42
+ x >= 0 &&
43
+ x <= 4294967295)));
44
+ },
45
+ };
46
+ export const ArrayOidVectorType = {
47
+ ...OidVectorType,
48
+ name: '_oidvector',
49
+ oid: DataTypeOIDs._oidvector,
50
+ elementsOID: DataTypeOIDs.oidvector,
51
+ };
@@ -5,17 +5,21 @@ export const PointType = {
5
5
  name: 'point',
6
6
  oid: DataTypeOIDs.point,
7
7
  jsType: 'object',
8
- parseBinary(v) {
9
- return {
10
- x: v.readDoubleBE(0),
11
- y: v.readDoubleBE(8),
12
- };
8
+ fixedBinarySize: 16,
9
+ encodeText(v) {
10
+ return `(${v.x},${v.y})`;
13
11
  },
14
12
  encodeBinary(buf, v) {
15
13
  buf.writeDoubleBE(v.x);
16
14
  buf.writeDoubleBE(v.y);
17
15
  },
18
- parseText(v) {
16
+ decodeBinary(v, offset = 0) {
17
+ return {
18
+ x: v.readDoubleBE(offset),
19
+ y: v.readDoubleBE(offset + 8),
20
+ };
21
+ },
22
+ decodeText(v) {
19
23
  const m = v.match(POINT_PATTERN1) || v.match(POINT_PATTERN2);
20
24
  if (!m)
21
25
  return undefined;
@@ -24,6 +28,10 @@ export const PointType = {
24
28
  y: parseFloat(m[2]),
25
29
  };
26
30
  },
31
+ // See box-type.ts's decodeTextBuffer comment - same rationale.
32
+ decodeTextBuffer(buf, offset, len, options) {
33
+ return PointType.decodeText(buf.toString('latin1', offset, offset + len), options);
34
+ },
27
35
  isType(v) {
28
36
  return (typeof v === 'object' &&
29
37
  Object.keys(v).length === 2 &&