postgrejs 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +64 -6
  2. package/README.md +9 -79
  3. package/connection/intl-connection.d.ts +5 -3
  4. package/connection/intl-connection.js +47 -17
  5. package/connection/pool.d.ts +22 -0
  6. package/connection/pool.js +45 -3
  7. package/connection/prepared-statement.js +2 -2
  8. package/constants.d.ts +60 -0
  9. package/constants.js +119 -1
  10. package/data-type-map.js +82 -0
  11. package/data-types/bit-type.d.ts +5 -0
  12. package/data-types/bit-type.js +91 -0
  13. package/data-types/box-type.js +9 -19
  14. package/data-types/circle-type.js +6 -16
  15. package/data-types/classes/geometric.d.ts +105 -0
  16. package/data-types/classes/geometric.js +197 -0
  17. package/data-types/classes/interval.d.ts +73 -0
  18. package/data-types/classes/interval.js +181 -0
  19. package/data-types/classes/numeric.d.ts +37 -0
  20. package/data-types/classes/numeric.js +47 -0
  21. package/data-types/classes/range.d.ts +74 -0
  22. package/data-types/classes/range.js +111 -0
  23. package/data-types/classes/type-oid.d.ts +34 -0
  24. package/data-types/classes/type-oid.js +45 -0
  25. package/data-types/inet-type.d.ts +5 -0
  26. package/data-types/inet-type.js +286 -0
  27. package/data-types/interval-type.d.ts +5 -0
  28. package/data-types/interval-type.js +79 -0
  29. package/data-types/jsonpath-type.d.ts +3 -0
  30. package/data-types/jsonpath-type.js +53 -0
  31. package/data-types/line-type.d.ts +3 -0
  32. package/data-types/line-type.js +41 -0
  33. package/data-types/lseg-type.js +9 -19
  34. package/data-types/macaddr-type.d.ts +5 -0
  35. package/data-types/macaddr-type.js +86 -0
  36. package/data-types/numeric-type.js +98 -69
  37. package/data-types/path-type.d.ts +5 -0
  38. package/data-types/path-type.js +139 -0
  39. package/data-types/pg-lsn-type.d.ts +3 -0
  40. package/data-types/pg-lsn-type.js +58 -0
  41. package/data-types/point-type.js +8 -13
  42. package/data-types/range-type.d.ts +23 -0
  43. package/data-types/range-type.js +342 -0
  44. package/data-types/snapshot-type.d.ts +5 -0
  45. package/data-types/snapshot-type.js +96 -0
  46. package/data-types/tid-type.d.ts +3 -0
  47. package/data-types/tid-type.js +50 -0
  48. package/data-types/timetz-type.d.ts +3 -0
  49. package/data-types/timetz-type.js +184 -0
  50. package/data-types/tsvector-type.d.ts +5 -0
  51. package/data-types/tsvector-type.js +216 -0
  52. package/data-types/xid-type.d.ts +7 -0
  53. package/data-types/xid-type.js +110 -0
  54. package/index.d.ts +5 -0
  55. package/index.js +5 -0
  56. package/interfaces/data-mapping-options.d.ts +26 -0
  57. package/interfaces/data-type.d.ts +8 -9
  58. package/package.json +1 -1
  59. package/protocol/connection-lost-error.d.ts +42 -0
  60. package/protocol/connection-lost-error.js +56 -0
  61. package/protocol/pg-socket.d.ts +7 -0
  62. package/protocol/pg-socket.js +41 -3
  63. package/util/parse-array.js +17 -2
  64. package/util/resolve-column-formats.d.ts +2 -1
  65. package/util/resolve-column-formats.js +35 -8
  66. package/util/stringify-for-sql.js +37 -1
@@ -1,4 +1,5 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
+ import { LineSegment, TwoPoints } from './classes/geometric.js';
2
3
  const LSEG_PATTERN1 = /^\[ *\( *(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *\) *, *\( *(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *\) *]$/;
3
4
  const LSEG_PATTERN2 = /^\( *\( *(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *\) *, *\( *(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *\) *\)$/;
4
5
  const LSEG_PATTERN3 = /^\( *(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *\) *, *\( *(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *\)$/;
@@ -6,7 +7,7 @@ const LSEG_PATTERN4 = /^(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *, *(-
6
7
  export const LsegType = {
7
8
  name: 'lseg',
8
9
  oid: DataTypeOIDs.lseg,
9
- jsType: 'object',
10
+ jsType: 'LineSegment',
10
11
  encodeText(v) {
11
12
  return `[(${v.x1},${v.y1}),(${v.x2},${v.y2})]`;
12
13
  },
@@ -17,12 +18,7 @@ export const LsegType = {
17
18
  buf.writeDoubleBE(v.y2);
18
19
  },
19
20
  decodeBinary(v, offset = 0) {
20
- return {
21
- x1: v.readDoubleBE(offset),
22
- y1: v.readDoubleBE(offset + 8),
23
- x2: v.readDoubleBE(offset + 16),
24
- y2: v.readDoubleBE(offset + 24),
25
- };
21
+ return new LineSegment(v.readDoubleBE(offset), v.readDoubleBE(offset + 8), v.readDoubleBE(offset + 16), v.readDoubleBE(offset + 24));
26
22
  },
27
23
  decodeText(v) {
28
24
  const m = v.match(LSEG_PATTERN1) ||
@@ -31,24 +27,18 @@ export const LsegType = {
31
27
  v.match(LSEG_PATTERN4);
32
28
  if (!m)
33
29
  return undefined;
34
- return {
35
- x1: parseFloat(m[1]),
36
- y1: parseFloat(m[2]),
37
- x2: parseFloat(m[3]),
38
- y2: parseFloat(m[4]),
39
- };
30
+ return new LineSegment(parseFloat(m[1]), parseFloat(m[2]), parseFloat(m[3]), parseFloat(m[4]));
40
31
  },
41
32
  // See box-type.ts's decodeTextBuffer comment - same rationale.
42
33
  decodeTextBuffer(buf, offset, len, options) {
43
34
  return LsegType.decodeText(buf.toString('latin1', offset, offset + len), options);
44
35
  },
36
+ // A plain object of the four numbers is still accepted - it is what
37
+ // this type returned before these classes existed - but only a plain
38
+ // one: box and lseg carry the same four, so an instance of the other
39
+ // must not be claimed here.
45
40
  isType(v) {
46
- return (typeof v === 'object' &&
47
- Object.keys(v).length === 4 &&
48
- typeof v.x1 === 'number' &&
49
- typeof v.y1 === 'number' &&
50
- typeof v.x2 === 'number' &&
51
- typeof v.y2 === 'number');
41
+ return v instanceof LineSegment || TwoPoints.isTwoPointsLike(v);
52
42
  },
53
43
  };
54
44
  export const ArrayLsegType = {
@@ -0,0 +1,5 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ export declare const MacaddrType: DataType;
3
+ export declare const ArrayMacaddrType: DataType;
4
+ export declare const Macaddr8Type: DataType;
5
+ export declare const ArrayMacaddr8Type: DataType;
@@ -0,0 +1,86 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ /**
3
+ * `macaddr` and `macaddr8` share a file for the same reason `inet` and
4
+ * `cidr` do: one wire format - the address bytes and nothing else - and
5
+ * one JavaScript shape, differing only in length.
6
+ *
7
+ * Both decode to a string, in the `08:00:2b:01:02:03` form the server
8
+ * prints. `pg` answers with a string too.
9
+ */
10
+ const SEPARATORS = /[.:-]/g;
11
+ const HEX_PATTERN = /^[0-9a-fA-F]+$/;
12
+ /**
13
+ * PostgreSQL accepts a MAC address written half a dozen ways -
14
+ * `08:00:2b:01:02:03`, `08-00-2b-01-02-03`, `0800.2b01.0203`,
15
+ * `08002b-010203`, `08002b010203` - which all come to the same thing
16
+ * once the separators are dropped: the right number of hex digits. It is
17
+ * not checked that the separators were placed sensibly, since nothing is
18
+ * gained by rejecting `0:800:2b0:102:03` on the client instead of just
19
+ * reading it.
20
+ */
21
+ function parseMacaddr(v, size) {
22
+ if (typeof v !== 'string')
23
+ return undefined;
24
+ const hex = v.replace(SEPARATORS, '');
25
+ if (hex.length !== size * 2 || !HEX_PATTERN.test(hex)) {
26
+ // A six-byte address in a macaddr8 is not an error: the server widens
27
+ // it by inserting ff:fe in the middle, the modified EUI-64 rule, and
28
+ // `macaddr8 '08:00:2b:01:02:03'` is stored as `08:00:2b:ff:fe:01:02:03`.
29
+ if (size !== 8 || hex.length !== 12 || !HEX_PATTERN.test(hex))
30
+ return undefined;
31
+ return Buffer.from(hex.slice(0, 6) + 'fffe' + hex.slice(6), 'hex');
32
+ }
33
+ return Buffer.from(hex, 'hex');
34
+ }
35
+ function createType(name, oid, size) {
36
+ return {
37
+ name,
38
+ oid,
39
+ jsType: 'string',
40
+ // See inet-type.ts: a MAC address is an ordinary string as far as
41
+ // JavaScript is concerned, and inferring this type from one would
42
+ // send `macaddr` where `text` was meant. Name it to reach it.
43
+ inferrable: false,
44
+ encodeText(v) {
45
+ // Handed over as written - the server parses every spelling.
46
+ return '' + v;
47
+ },
48
+ encodeBinary(buf, v) {
49
+ const b = parseMacaddr(v, size);
50
+ if (!b)
51
+ throw new Error(`"${v}" is not a valid ${name} value`);
52
+ buf.writeBytes(b);
53
+ },
54
+ decodeBinary(v, offset = 0) {
55
+ let out = v.toString('hex', offset, offset + 1);
56
+ let i;
57
+ for (i = 1; i < size; i++)
58
+ out += ':' + v.toString('hex', offset + i, offset + i + 1);
59
+ return out;
60
+ },
61
+ decodeText(v) {
62
+ return v;
63
+ },
64
+ // Hex and colons - see inet-type.ts for why 'latin1'.
65
+ decodeTextBuffer(buf, offset, len) {
66
+ return buf.toString('latin1', offset, offset + len);
67
+ },
68
+ isType(v) {
69
+ return !!parseMacaddr(v, size);
70
+ },
71
+ };
72
+ }
73
+ export const MacaddrType = createType('macaddr', DataTypeOIDs.macaddr, 6);
74
+ export const ArrayMacaddrType = {
75
+ ...MacaddrType,
76
+ name: '_macaddr',
77
+ oid: DataTypeOIDs._macaddr,
78
+ elementsOID: DataTypeOIDs.macaddr,
79
+ };
80
+ export const Macaddr8Type = createType('macaddr8', DataTypeOIDs.macaddr8, 8);
81
+ export const ArrayMacaddr8Type = {
82
+ ...Macaddr8Type,
83
+ name: '_macaddr8',
84
+ oid: DataTypeOIDs._macaddr8,
85
+ elementsOID: DataTypeOIDs.macaddr8,
86
+ };
@@ -1,16 +1,55 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
2
  import { assertCoercedNumber } from '../util/assert-integer.js';
3
3
  import { fastParseFloatBuffer } from '../util/fast-parsefloat.js';
4
+ import { Numeric } from './classes/numeric.js';
4
5
  const NUMERIC_NEG = 0x4000;
5
6
  const NUMERIC_NAN = 0xc000;
6
7
  const NUMERIC_PINF = 0xd000;
7
8
  const NUMERIC_NINF = 0xf000;
8
9
  const DEC_DIGITS = 4;
9
- const ROUND_POWERS = [0, 1000, 100, 10];
10
+ /**
11
+ * Every two-digit string, so a base-10000 group is written with two
12
+ * lookups and one join instead of four divisions and four number-to-
13
+ * string conversions. Built once; 100 short strings.
14
+ */
15
+ const PAIRS = new Array(100);
16
+ for (let i = 0; i < 100; i++)
17
+ PAIRS[i] = i < 10 ? '0' + i : '' + i;
18
+ /** The decimal without the zeroes a declared scale pads it out to. */
19
+ function trimTrailingZeros(s) {
20
+ const dot = s.indexOf('.');
21
+ if (dot < 0)
22
+ return s;
23
+ let end = s.length;
24
+ while (end > dot + 1 && s.charCodeAt(end - 1) === 48 /* 0 */)
25
+ end--;
26
+ return s.substring(0, end === dot + 1 ? dot : end);
27
+ }
28
+ /**
29
+ * A number when a double carries the decimal faithfully, a Numeric when
30
+ * it does not.
31
+ *
32
+ * Faithfully means printing back the same digits, which is the only
33
+ * thing a caller can observe - so the test is the round trip itself
34
+ * rather than a rule about magnitude. It catches both ways of losing a
35
+ * value: digits a double cannot hold (`12345678901234567.89` comes back
36
+ * as `...68`) and a magnitude JavaScript prints in exponential notation
37
+ * (`-0.00000000000000001` as `-1e-17`), which PostgreSQL never writes.
38
+ *
39
+ * The padding a declared scale adds is not a difference - a
40
+ * `numeric(40,6)` holding 19.99 arrives as `19.990000` - so it comes off
41
+ * before the comparison.
42
+ */
43
+ function toNumberOrNumeric(s) {
44
+ const n = parseFloat(s);
45
+ return String(n) === trimTrailingZeros(s) ? n : new Numeric(s);
46
+ }
10
47
  export const NumericType = {
11
48
  name: 'numeric',
12
49
  oid: DataTypeOIDs.numeric,
13
- jsType: 'number',
50
+ // A number while one carries the value, a Numeric after that - the
51
+ // widened type is named here the way int8 names BigInt.
52
+ jsType: 'Numeric',
14
53
  /**
15
54
  * numeric on the wire is a base-10000 number: a digit count, the weight
16
55
  * of the first group (in groups of four decimal digits, not digits), a
@@ -110,16 +149,44 @@ export const NumericType = {
110
149
  digits[i] = v.readInt16BE(offset + 8 + i * 2);
111
150
  }
112
151
  const numString = numberBytesToString(digits, scale, weight, sign);
113
- return parseFloat(numString);
152
+ // The header bounds the value before the digits are even looked at,
153
+ // so the ordinary column never pays for the round-trip check below.
154
+ // A double carries about fifteen significant decimal digits and
155
+ // prints in plain notation while its exponent stays inside
156
+ // (-7, 21); `weight` is the first group's position counted in fours
157
+ // and `scale` the digits after the point, which is enough to rule
158
+ // both out. `weight === -1` is a value in [0.0001, 1), far from the
159
+ // exponent that would make JavaScript switch notation.
160
+ if ((weight >= 0 && weight <= 4 && (weight + 1) * DEC_DIGITS + scale <= 15) ||
161
+ (weight === -1 && scale <= 15))
162
+ return parseFloat(numString);
163
+ return toNumberOrNumeric(numString);
114
164
  },
115
165
  encodeText(v) {
116
- const n = typeof v === 'number' ? v : parseFloat(v);
117
- return '' + n;
166
+ // Handed over as written. Going through a double here would undo
167
+ // exactly what encodeBinary is careful to preserve, and the server
168
+ // accepts every spelling this could normalize to anyway - including
169
+ // the exponent form a JavaScript number stringifies to.
170
+ return typeof v === 'string' ? v.trim() : String(v);
171
+ },
172
+ decodeText(v) {
173
+ // NaN and the infinities arrive as those words and stay numbers.
174
+ const n = parseFloat(v);
175
+ if (!Number.isFinite(n))
176
+ return n;
177
+ return String(n) === trimTrailingZeros(v) ? n : new Numeric(v);
178
+ },
179
+ decodeTextBuffer(buf, offset, len, options) {
180
+ // Eight characters hold at most eight digits, cannot reach 1e21 -
181
+ // which needs twenty-two - and cannot reach 1e-7, which needs `0.`
182
+ // and seven more. So a value this short is carried faithfully by
183
+ // definition and keeps the parser that never builds a string.
184
+ if (len <= 8)
185
+ return fastParseFloatBuffer(buf, offset, len);
186
+ return NumericType.decodeText(buf.toString('latin1', offset, offset + len), options);
118
187
  },
119
- decodeText: parseFloat,
120
- decodeTextBuffer: fastParseFloatBuffer,
121
188
  isType(v) {
122
- return typeof v === 'number';
189
+ return typeof v === 'number' || v instanceof Numeric;
123
190
  },
124
191
  };
125
192
  export const ArrayNumericType = {
@@ -158,78 +225,40 @@ export function expandExponential(str) {
158
225
  * @return String the number as String
159
226
  */
160
227
  export function numberBytesToString(digits, scale, weight, sign) {
161
- let i;
162
- let d;
163
- /*
164
- * Allocate space for the result.
165
- *
166
- * i is set to the # of decimal digits before decimal point.
167
- * dscale is the # of decimal digits we will print after decimal point.
168
- * We may generate as many as DEC_DIGITS-1 excess digits at the end, and in addition we
169
- * need room for sign, decimal point, null terminator.
170
- */
171
- i = (weight + 1) * DEC_DIGITS;
172
- if (i <= 0)
173
- i = 1;
174
- /*
175
- * Output a dash for negative values
176
- */
228
+ const l = digits.length;
177
229
  let out = sign === NUMERIC_NEG ? '-' : '';
178
- /*
179
- * Output all digits before the decimal point
180
- */
230
+ let d;
231
+ let g;
181
232
  if (weight < 0) {
233
+ // The value is below 1, so the integer part is a bare zero and the
234
+ // first digit group belongs after the point.
182
235
  d = weight + 1;
183
236
  out += '0';
184
237
  }
185
238
  else {
186
- for (d = 0; d <= weight; d++) {
187
- /* In the first digit, suppress extra leading decimal zeroes */
188
- out += digitToString(d, digits, d !== 0);
239
+ // Leading zeroes are suppressed in the first group only - it is the
240
+ // most significant one - and every group after it is four digits.
241
+ out += l > 0 ? '' + digits[0] : '0';
242
+ for (d = 1; d <= weight; d++) {
243
+ g = d < l ? digits[d] : 0;
244
+ out += PAIRS[(g / 100) | 0] + PAIRS[g % 100];
189
245
  }
190
246
  }
191
- /*
192
- * If requested, output a decimal point and all the digits that follow it.
193
- * We initially put out a multiple of DEC_DIGITS digits, then truncate if
194
- * needed.
195
- */
196
247
  if (scale > 0) {
197
- out += '.';
248
+ let frac = '';
249
+ let i;
198
250
  for (i = 0; i < scale; d++, i += DEC_DIGITS) {
199
- out += digitToString(d, digits, true);
200
- }
201
- }
202
- const extra = (i - scale) % DEC_DIGITS;
203
- return out.substring(0, out.length - extra);
204
- }
205
- /* https://github.com/pgjdbc/pgjdbc/blob/3eca3a76aa4a04cb28cb960ed674cb67db30b5e3/pgjdbc/src/main/java/org/postgresql/util/ByteConverter.java */
206
- /**
207
- * Convert a number from binary representation to text representation.
208
- * @param idx index of the digit to be converted in the digits array
209
- * @param digits array of shorts that can be decoded as the number String
210
- * @param alwaysPutIt a flag that indicate whether or not to put the digit char even if it is zero
211
- * @return String the number as String
212
- */
213
- function digitToString(idx, digits, alwaysPutIt) {
214
- let out = '';
215
- let dig = idx >= 0 && idx < digits.length ? digits[idx] : 0;
216
- // Each dig represents 4 decimal digits (e.g. 9999)
217
- // If we continue the number, then we need to print 0 as 0000 (alwaysPutIt parameter is true)
218
- const l = ROUND_POWERS.length;
219
- let p;
220
- for (p = 1; p < l; p++) {
221
- const pow = ROUND_POWERS[p];
222
- const d1 = Math.trunc(dig / pow);
223
- dig -= d1 * pow;
224
- const putit = d1 > 0;
225
- if (putit || alwaysPutIt) {
226
- out += d1;
227
- // We printed a character, so we need to print the rest of the current digits in dig
228
- // For instance, we need to keep printing 000 from 1000 even if idx==0 (== it is the very
229
- // beginning)
230
- alwaysPutIt = true;
251
+ g = d >= 0 && d < l ? digits[d] : 0;
252
+ frac += PAIRS[(g / 100) | 0] + PAIRS[g % 100];
231
253
  }
254
+ // Whole groups are written, so the last one can overshoot the
255
+ // display scale by up to three digits. Trimmed from the fraction
256
+ // rather than from the whole string: the two are the same thing
257
+ // whenever there is a fraction, and when there is not - a value
258
+ // below 1 with a scale of 0 - trimming the whole string took the
259
+ // bare `0` with it.
260
+ out +=
261
+ '.' + (i > scale ? frac.substring(0, frac.length - (i - scale)) : frac);
232
262
  }
233
- out += dig;
234
263
  return out;
235
264
  }
@@ -0,0 +1,5 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ export declare const PathType: DataType;
3
+ export declare const ArrayPathType: DataType;
4
+ export declare const PolygonType: DataType;
5
+ export declare const ArrayPolygonType: DataType;
@@ -0,0 +1,139 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ import { Path, Point, Polygon } from './classes/geometric.js';
3
+ /**
4
+ * `path` and `polygon` are a series of points each, and differ on the
5
+ * wire only in that a path carries a flag saying whether it is closed.
6
+ * They share this file the way `box` and `lseg` share their shape, and
7
+ * for the same reason they are two classes: nothing about the points
8
+ * themselves says which type they were meant for.
9
+ */
10
+ /**
11
+ * Every coordinate in the literal, in order. The server writes
12
+ * `((1,2),(3,4))` for a closed path or a polygon and `[(1,2),(3,4)]` for
13
+ * an open path, and a float8 may reach exponent form - so the numbers
14
+ * are read out rather than matched against a fixed shape.
15
+ */
16
+ const NUMBER_PATTERN = /-?\d+(?:\.\d+)?(?:e[-+]?\d+)?/gi;
17
+ const POINTS_PATTERN = /^\s*([[(])\s*\(.*\)\s*[\])]\s*$/;
18
+ function parsePoints(v) {
19
+ if (!POINTS_PATTERN.test(v))
20
+ return undefined;
21
+ const nums = v.match(NUMBER_PATTERN);
22
+ // A path or polygon holds at least one point, and coordinates come in
23
+ // pairs - an odd count is a literal this does not understand.
24
+ if (!nums || !nums.length || nums.length % 2)
25
+ return undefined;
26
+ const points = new Array(nums.length / 2);
27
+ let i;
28
+ for (i = 0; i < points.length; i++)
29
+ points[i] = new Point(parseFloat(nums[i * 2]), parseFloat(nums[i * 2 + 1]));
30
+ return points;
31
+ }
32
+ /** An int32 count, then each point as two float8s. */
33
+ function writePoints(buf, points) {
34
+ const l = points.length;
35
+ buf.writeInt32BE(l);
36
+ let i;
37
+ for (i = 0; i < l; i++) {
38
+ buf.writeDoubleBE(points[i].x);
39
+ buf.writeDoubleBE(points[i].y);
40
+ }
41
+ }
42
+ function readPoints(v, offset) {
43
+ const count = v.readInt32BE(offset);
44
+ const points = new Array(count);
45
+ let i;
46
+ let p = offset + 4;
47
+ for (i = 0; i < count; i++) {
48
+ points[i] = new Point(v.readDoubleBE(p), v.readDoubleBE(p + 8));
49
+ p += 16;
50
+ }
51
+ return points;
52
+ }
53
+ /**
54
+ * A plain array of points is accepted as a parameter - it cannot be
55
+ * inferred, since `determine()` answers `point[]` for one, but it is
56
+ * unambiguous once the type has been named with a BindParam.
57
+ */
58
+ function toPoints(v, name) {
59
+ if (Array.isArray(v))
60
+ return v.map(p => new Point(p.x, p.y));
61
+ if (v && Array.isArray(v.points))
62
+ return v.points;
63
+ throw new TypeError(`"${typeof v}" cannot be encoded as a ${name} - pass a ${name === 'path' ? 'Path' : 'Polygon'} or an array of points`);
64
+ }
65
+ export const PathType = {
66
+ name: 'path',
67
+ oid: DataTypeOIDs.path,
68
+ jsType: 'Path',
69
+ encodeText(v) {
70
+ return v instanceof Path
71
+ ? v.toString()
72
+ : new Path(toPoints(v, 'path')).toString();
73
+ },
74
+ /** A closed flag, then the count and the points. */
75
+ encodeBinary(buf, v) {
76
+ buf.writeUInt8(v instanceof Path ? (v.isClosed ? 1 : 0) : 1);
77
+ writePoints(buf, toPoints(v, 'path'));
78
+ },
79
+ decodeBinary(v, offset = 0) {
80
+ return new Path(readPoints(v, offset + 1), !!v[offset]);
81
+ },
82
+ decodeText(v) {
83
+ const points = parsePoints(v);
84
+ if (!points)
85
+ return undefined;
86
+ // `[...]` is an open path, `(...)` a closed one.
87
+ return new Path(points, v.trimStart()[0] !== '[');
88
+ },
89
+ // See box-type.ts's decodeTextBuffer comment - same rationale.
90
+ decodeTextBuffer(buf, offset, len, options) {
91
+ return PathType.decodeText(buf.toString('latin1', offset, offset + len), options);
92
+ },
93
+ // Only a Path: a plain array of points is a `point[]` as far as
94
+ // inference is concerned, and claiming it here would take those arrays
95
+ // away from that type.
96
+ isType(v) {
97
+ return v instanceof Path;
98
+ },
99
+ };
100
+ export const ArrayPathType = {
101
+ ...PathType,
102
+ name: '_path',
103
+ oid: DataTypeOIDs._path,
104
+ elementsOID: DataTypeOIDs.path,
105
+ };
106
+ export const PolygonType = {
107
+ name: 'polygon',
108
+ oid: DataTypeOIDs.polygon,
109
+ jsType: 'Polygon',
110
+ encodeText(v) {
111
+ return v instanceof Polygon
112
+ ? v.toString()
113
+ : new Polygon(toPoints(v, 'polygon')).toString();
114
+ },
115
+ encodeBinary(buf, v) {
116
+ writePoints(buf, toPoints(v, 'polygon'));
117
+ },
118
+ decodeBinary(v, offset = 0) {
119
+ return new Polygon(readPoints(v, offset));
120
+ },
121
+ decodeText(v) {
122
+ const points = parsePoints(v);
123
+ return points && new Polygon(points);
124
+ },
125
+ // See box-type.ts's decodeTextBuffer comment - same rationale.
126
+ decodeTextBuffer(buf, offset, len, options) {
127
+ return PolygonType.decodeText(buf.toString('latin1', offset, offset + len), options);
128
+ },
129
+ // See PathType.isType().
130
+ isType(v) {
131
+ return v instanceof Polygon;
132
+ },
133
+ };
134
+ export const ArrayPolygonType = {
135
+ ...PolygonType,
136
+ name: '_polygon',
137
+ oid: DataTypeOIDs._polygon,
138
+ elementsOID: DataTypeOIDs.polygon,
139
+ };
@@ -0,0 +1,3 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ export declare const PgLsnType: DataType;
3
+ export declare const ArrayPgLsnType: DataType;
@@ -0,0 +1,58 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ /**
3
+ * A `pg_lsn` is a position in the write-ahead log - what
4
+ * `pg_current_wal_lsn()` and every column of `pg_stat_replication`
5
+ * answer with, so replication and monitoring tools read these routinely.
6
+ *
7
+ * It decodes to the `16/B374D848` string the server prints: the two
8
+ * 32-bit halves in upper-case hex, separated by a slash and neither of
9
+ * them padded - `0/0` and `A/B` are what the server writes for those,
10
+ * not `0/00000000`. That spelling is how an LSN is written everywhere -
11
+ * in logs, in
12
+ * `recovery_target_lsn`, in the documentation - and it casts straight
13
+ * back. A number would lose that and gain nothing, since the value is
14
+ * compared rather than arithmetic.
15
+ */
16
+ const LSN_PATTERN = /^([0-9a-fA-F]{1,8})\/([0-9a-fA-F]{1,8})$/;
17
+ export const PgLsnType = {
18
+ name: 'pg_lsn',
19
+ oid: DataTypeOIDs.pg_lsn,
20
+ jsType: 'string',
21
+ // See inet-type.ts: a string-shaped type stays out of inference.
22
+ inferrable: false,
23
+ encodeText(v) {
24
+ return '' + v;
25
+ },
26
+ /** One unsigned 64-bit value, written as its two halves. */
27
+ encodeBinary(buf, v) {
28
+ const m = typeof v === 'string' ? LSN_PATTERN.exec(v) : undefined;
29
+ if (!m)
30
+ throw new Error(`"${v}" is not a valid pg_lsn value`);
31
+ buf.writeUInt32BE(parseInt(m[1], 16));
32
+ buf.writeUInt32BE(parseInt(m[2], 16));
33
+ },
34
+ decodeBinary(v, offset = 0) {
35
+ return (v.readUInt32BE(offset).toString(16).toUpperCase() +
36
+ '/' +
37
+ v
38
+ .readUInt32BE(offset + 4)
39
+ .toString(16)
40
+ .toUpperCase());
41
+ },
42
+ decodeText(v) {
43
+ return v;
44
+ },
45
+ // Hex and a slash - see inet-type.ts for why 'latin1'.
46
+ decodeTextBuffer(buf, offset, len) {
47
+ return buf.toString('latin1', offset, offset + len);
48
+ },
49
+ isType(v) {
50
+ return typeof v === 'string' && LSN_PATTERN.test(v);
51
+ },
52
+ };
53
+ export const ArrayPgLsnType = {
54
+ ...PgLsnType,
55
+ name: '_pg_lsn',
56
+ oid: DataTypeOIDs._pg_lsn,
57
+ elementsOID: DataTypeOIDs.pg_lsn,
58
+ };
@@ -1,10 +1,11 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
+ import { Point } from './classes/geometric.js';
2
3
  const POINT_PATTERN1 = /^\( *(-?\d+\.?\d*) *, *(-?\d+\.?\d*) *\)$/;
3
4
  const POINT_PATTERN2 = /^(-?\d+\.?\d*) *, *(-?\d+\.?\d*)$/;
4
5
  export const PointType = {
5
6
  name: 'point',
6
7
  oid: DataTypeOIDs.point,
7
- jsType: 'object',
8
+ jsType: 'Point',
8
9
  encodeText(v) {
9
10
  return `(${v.x},${v.y})`;
10
11
  },
@@ -13,29 +14,23 @@ export const PointType = {
13
14
  buf.writeDoubleBE(v.y);
14
15
  },
15
16
  decodeBinary(v, offset = 0) {
16
- return {
17
- x: v.readDoubleBE(offset),
18
- y: v.readDoubleBE(offset + 8),
19
- };
17
+ return new Point(v.readDoubleBE(offset), v.readDoubleBE(offset + 8));
20
18
  },
21
19
  decodeText(v) {
22
20
  const m = v.match(POINT_PATTERN1) || v.match(POINT_PATTERN2);
23
21
  if (!m)
24
22
  return undefined;
25
- return {
26
- x: parseFloat(m[1]),
27
- y: parseFloat(m[2]),
28
- };
23
+ return new Point(parseFloat(m[1]), parseFloat(m[2]));
29
24
  },
30
25
  // See box-type.ts's decodeTextBuffer comment - same rationale.
31
26
  decodeTextBuffer(buf, offset, len, options) {
32
27
  return PointType.decodeText(buf.toString('latin1', offset, offset + len), options);
33
28
  },
29
+ // A plain `{x, y}` is still accepted: it is what this type returned
30
+ // before Point existed, and code that builds one by hand should keep
31
+ // working.
34
32
  isType(v) {
35
- return (typeof v === 'object' &&
36
- Object.keys(v).length === 2 &&
37
- typeof v.x === 'number' &&
38
- typeof v.y === 'number');
33
+ return v instanceof Point || Point.isPointLike(v);
39
34
  },
40
35
  };
41
36
  export const ArrayPointType = {
@@ -0,0 +1,23 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ import type { OID } from '../types.js';
3
+ /**
4
+ * Builds the DataType pair for one of PostgreSQL's range types.
5
+ *
6
+ * Every range shares one wire format regardless of what it ranges over -
7
+ * a flags byte, then each bound that exists as a length-prefixed value of
8
+ * the element type - so the element's own codec does the work and this
9
+ * only has to say which bounds are there.
10
+ *
11
+ * Note what is deliberately not set: `elementsOID`. That marks an *array*
12
+ * type, and get-parsers.ts routes anything carrying it through
13
+ * decodeBinaryArray. A range is a scalar whose parts happen to be typed.
14
+ */
15
+ export declare function createRangeType(name: string, oid: OID, element: DataType): DataType;
16
+ /**
17
+ * The multirange counterpart: a count, then each range length-prefixed,
18
+ * and in text a brace-wrapped comma-separated list. The ranges themselves
19
+ * are the same format, so this delegates to the range type rather than
20
+ * repeating it.
21
+ */
22
+ export declare function createMultiRangeType(name: string, oid: OID, range: DataType): DataType;
23
+ export declare const RangeTypes: DataType[];