postgrejs 3.6.1 → 3.8.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 (78) hide show
  1. package/CHANGELOG.md +66 -6
  2. package/README.md +9 -83
  3. package/connection/connection.js +13 -1
  4. package/connection/intl-connection.d.ts +5 -3
  5. package/connection/intl-connection.js +70 -20
  6. package/connection/pool.js +6 -0
  7. package/connection/prepared-statement.d.ts +11 -0
  8. package/connection/prepared-statement.js +19 -5
  9. package/constants.d.ts +60 -0
  10. package/constants.js +119 -1
  11. package/data-type-map.js +82 -0
  12. package/data-types/bit-type.d.ts +5 -0
  13. package/data-types/bit-type.js +91 -0
  14. package/data-types/box-type.js +9 -19
  15. package/data-types/circle-type.js +6 -16
  16. package/data-types/classes/geometric.d.ts +105 -0
  17. package/data-types/classes/geometric.js +197 -0
  18. package/data-types/classes/interval.d.ts +73 -0
  19. package/data-types/classes/interval.js +181 -0
  20. package/data-types/classes/numeric.d.ts +41 -0
  21. package/data-types/classes/numeric.js +51 -0
  22. package/data-types/classes/range.d.ts +74 -0
  23. package/data-types/classes/range.js +111 -0
  24. package/data-types/classes/type-oid.d.ts +34 -0
  25. package/data-types/classes/type-oid.js +45 -0
  26. package/data-types/inet-type.d.ts +5 -0
  27. package/data-types/inet-type.js +286 -0
  28. package/data-types/int2-vector-type.js +4 -1
  29. package/data-types/interval-type.d.ts +5 -0
  30. package/data-types/interval-type.js +79 -0
  31. package/data-types/jsonpath-type.d.ts +3 -0
  32. package/data-types/jsonpath-type.js +53 -0
  33. package/data-types/line-type.d.ts +3 -0
  34. package/data-types/line-type.js +41 -0
  35. package/data-types/lseg-type.js +9 -19
  36. package/data-types/macaddr-type.d.ts +5 -0
  37. package/data-types/macaddr-type.js +86 -0
  38. package/data-types/numeric-type.js +98 -69
  39. package/data-types/oid-vector-type.js +4 -1
  40. package/data-types/path-type.d.ts +5 -0
  41. package/data-types/path-type.js +139 -0
  42. package/data-types/pg-lsn-type.d.ts +3 -0
  43. package/data-types/pg-lsn-type.js +58 -0
  44. package/data-types/point-type.js +8 -13
  45. package/data-types/range-type.d.ts +23 -0
  46. package/data-types/range-type.js +342 -0
  47. package/data-types/snapshot-type.d.ts +5 -0
  48. package/data-types/snapshot-type.js +96 -0
  49. package/data-types/tid-type.d.ts +3 -0
  50. package/data-types/tid-type.js +50 -0
  51. package/data-types/timestamptz-type.js +9 -0
  52. package/data-types/timetz-type.d.ts +3 -0
  53. package/data-types/timetz-type.js +184 -0
  54. package/data-types/tsvector-type.d.ts +5 -0
  55. package/data-types/tsvector-type.js +216 -0
  56. package/data-types/xid-type.d.ts +7 -0
  57. package/data-types/xid-type.js +110 -0
  58. package/index.d.ts +4 -0
  59. package/index.js +4 -0
  60. package/interfaces/data-mapping-options.d.ts +26 -0
  61. package/interfaces/data-type.d.ts +8 -9
  62. package/package.json +1 -1
  63. package/protocol/connection-lost-error.d.ts +5 -3
  64. package/protocol/connection-lost-error.js +5 -3
  65. package/protocol/frontend.js +17 -0
  66. package/protocol/pg-socket.js +16 -2
  67. package/util/decode-binaryarray.js +8 -1
  68. package/util/encode-binaryarray.d.ts +12 -1
  69. package/util/encode-binaryarray.js +13 -2
  70. package/util/format-datetime.d.ts +18 -0
  71. package/util/format-datetime.js +33 -0
  72. package/util/parse-array.js +17 -2
  73. package/util/resolve-column-formats.d.ts +2 -1
  74. package/util/resolve-column-formats.js +35 -8
  75. package/util/sql-tag.js +15 -0
  76. package/util/stringify-for-sql.js +44 -1
  77. package/util/unspecified-param.d.ts +32 -0
  78. package/util/unspecified-param.js +40 -0
@@ -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[];
@@ -0,0 +1,342 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ import { Range } from './classes/range.js';
3
+ import { setTypeOid } from './classes/type-oid.js';
4
+ import { DateType } from './date-type.js';
5
+ import { Int4Type } from './int4-type.js';
6
+ import { Int8Type } from './int8-type.js';
7
+ import { NumericType } from './numeric-type.js';
8
+ import { TimestampType } from './timestamp-type.js';
9
+ import { TimestamptzType } from './timestamptz-type.js';
10
+ // src/include/utils/rangetypes.h
11
+ const RANGE_EMPTY = 0x01;
12
+ const RANGE_LB_INC = 0x02;
13
+ const RANGE_UB_INC = 0x04;
14
+ const RANGE_LB_INF = 0x08;
15
+ const RANGE_UB_INF = 0x10;
16
+ /** `empty`, or two bounds with the brackets that say whether each is included. */
17
+ const RANGE_PATTERN = /^\s*([[(])(.*),(.*)([\])])\s*$/s;
18
+ function boundsOf(r) {
19
+ return ((r.lowerInclusive && r.lower !== null ? '[' : '(') +
20
+ (r.upperInclusive && r.upper !== null ? ']' : ')'));
21
+ }
22
+ /**
23
+ * Reads one bound out of a range literal. An empty string is an absent
24
+ * bound; a quoted one has its doubled quotes and backslashes put back.
25
+ */
26
+ function parseBound(s, decode, options) {
27
+ const t = s.trim();
28
+ if (t === '')
29
+ return null;
30
+ if (t.charAt(0) === '"') {
31
+ let out = '';
32
+ let i = 1;
33
+ while (i < t.length) {
34
+ const c = t.charAt(i);
35
+ if (c === '\\') {
36
+ out += t.charAt(i + 1);
37
+ i += 2;
38
+ continue;
39
+ }
40
+ if (c === '"') {
41
+ // A doubled quote is one quote; a single one ends the bound.
42
+ if (t.charAt(i + 1) === '"') {
43
+ out += '"';
44
+ i += 2;
45
+ continue;
46
+ }
47
+ break;
48
+ }
49
+ out += c;
50
+ i++;
51
+ }
52
+ return decode(out, options);
53
+ }
54
+ return decode(t, options);
55
+ }
56
+ function toRange(v) {
57
+ if (v instanceof Range)
58
+ return v;
59
+ if (Array.isArray(v))
60
+ return new Range(v[0] ?? null, v[1] ?? null);
61
+ throw new TypeError(`"${typeof v}" cannot be encoded as a range - pass a Range, or a ` +
62
+ '[lower, upper] pair');
63
+ }
64
+ /**
65
+ * Builds the DataType pair for one of PostgreSQL's range types.
66
+ *
67
+ * Every range shares one wire format regardless of what it ranges over -
68
+ * a flags byte, then each bound that exists as a length-prefixed value of
69
+ * the element type - so the element's own codec does the work and this
70
+ * only has to say which bounds are there.
71
+ *
72
+ * Note what is deliberately not set: `elementsOID`. That marks an *array*
73
+ * type, and get-parsers.ts routes anything carrying it through
74
+ * decodeBinaryArray. A range is a scalar whose parts happen to be typed.
75
+ */
76
+ export function createRangeType(name, oid, element) {
77
+ return {
78
+ name,
79
+ oid,
80
+ jsType: 'Range',
81
+ decodeBinary(v, offset = 0, _len, options) {
82
+ const flags = v[offset];
83
+ // Stamped with the type it came from, so it can go back as that
84
+ // type without being named again - which nothing about a Range
85
+ // could otherwise tell.
86
+ if (flags & RANGE_EMPTY)
87
+ return Range.empty(oid);
88
+ let p = offset + 1;
89
+ let lower = null;
90
+ let upper = null;
91
+ let l;
92
+ if (!(flags & RANGE_LB_INF)) {
93
+ l = v.readInt32BE(p);
94
+ p += 4;
95
+ lower = element.decodeBinary(v, p, l, options);
96
+ p += l;
97
+ }
98
+ if (!(flags & RANGE_UB_INF)) {
99
+ l = v.readInt32BE(p);
100
+ p += 4;
101
+ upper = element.decodeBinary(v, p, l, options);
102
+ }
103
+ return new Range(lower, upper, ((flags & RANGE_LB_INC ? '[' : '(') +
104
+ (flags & RANGE_UB_INC ? ']' : ')')), oid);
105
+ },
106
+ encodeBinary(buf, v, options) {
107
+ const r = toRange(v);
108
+ if (r.isEmpty) {
109
+ buf.writeUInt8(RANGE_EMPTY);
110
+ return;
111
+ }
112
+ let flags = 0;
113
+ if (r.lower === null)
114
+ flags |= RANGE_LB_INF;
115
+ else if (r.lowerInclusive)
116
+ flags |= RANGE_LB_INC;
117
+ if (r.upper === null)
118
+ flags |= RANGE_UB_INF;
119
+ else if (r.upperInclusive)
120
+ flags |= RANGE_UB_INC;
121
+ buf.writeUInt8(flags);
122
+ let pos;
123
+ if (r.lower !== null) {
124
+ buf.writeInt32BE(0); // reserved for the bound's length
125
+ pos = buf.position;
126
+ element.encodeBinary(buf, r.lower, options);
127
+ buf.buffer.writeInt32BE(buf.size - pos, pos - 4);
128
+ }
129
+ if (r.upper !== null) {
130
+ buf.writeInt32BE(0);
131
+ pos = buf.position;
132
+ element.encodeBinary(buf, r.upper, options);
133
+ buf.buffer.writeInt32BE(buf.size - pos, pos - 4);
134
+ }
135
+ },
136
+ decodeText(v, options) {
137
+ if (v.trim() === 'empty')
138
+ return Range.empty(oid);
139
+ const m = RANGE_PATTERN.exec(v);
140
+ if (!m)
141
+ throw new Error(`"${v}" is not a ${name} literal`);
142
+ return new Range(parseBound(m[2], element.decodeText, options), parseBound(m[3], element.decodeText, options), (m[1] + m[4]), oid);
143
+ },
144
+ encodeText(v, options) {
145
+ const r = toRange(v);
146
+ if (r.isEmpty)
147
+ return 'empty';
148
+ // Rendered through the element's own encodeText rather than
149
+ // Range.toString(), which has no element type to ask and falls back
150
+ // on String() - the difference shows on a Date bound.
151
+ const render = (x) => x === null
152
+ ? ''
153
+ : quote(element.encodeText ? element.encodeText(x, options) : String(x));
154
+ const bounds = boundsOf(r);
155
+ return (bounds.charAt(0) +
156
+ render(r.lower) +
157
+ ',' +
158
+ render(r.upper) +
159
+ bounds.charAt(1));
160
+ },
161
+ isType(v) {
162
+ return v instanceof Range;
163
+ },
164
+ // Every one of the six range types answers `instanceof Range` for the
165
+ // same value, and nothing about a Range says which it is - bounds of
166
+ // numbers fit int4range, int8range and numrange alike, Dates fit
167
+ // daterange, tsrange and tstzrange. So determine() would pick
168
+ // whichever was registered first and send an int4range as a
169
+ // tstzrange, which is what it did before this line: `new Range(1, 10)`
170
+ // arrived as `[1970-01-01T00:00:00.001Z,...)`. Ask for the one you
171
+ // mean with `new BindParam(DataTypeOIDs.int4range, value)`.
172
+ inferrable: false,
173
+ };
174
+ }
175
+ function quote(s) {
176
+ if (s === '' || /["\\()[\],]|\s/.test(s))
177
+ return '"' + s.replace(/\\/g, '\\\\').replace(/"/g, '""') + '"';
178
+ return s;
179
+ }
180
+ /**
181
+ * The multirange counterpart: a count, then each range length-prefixed,
182
+ * and in text a brace-wrapped comma-separated list. The ranges themselves
183
+ * are the same format, so this delegates to the range type rather than
184
+ * repeating it.
185
+ */
186
+ export function createMultiRangeType(name, oid, range) {
187
+ return {
188
+ name,
189
+ oid,
190
+ jsType: 'Range[]',
191
+ decodeBinary(v, offset = 0, _len, options) {
192
+ const count = v.readInt32BE(offset);
193
+ let p = offset + 4;
194
+ const out = new Array(count);
195
+ let l;
196
+ let i;
197
+ for (i = 0; i < count; i++) {
198
+ l = v.readInt32BE(p);
199
+ p += 4;
200
+ out[i] = range.decodeBinary(v, p, l, options);
201
+ p += l;
202
+ }
203
+ // The array carries the multirange's own OID; its elements already
204
+ // carry the range type's.
205
+ return setTypeOid(out, oid);
206
+ },
207
+ encodeBinary(buf, v, options) {
208
+ const items = Array.isArray(v) ? v : [v];
209
+ buf.writeInt32BE(items.length);
210
+ let pos;
211
+ let i;
212
+ for (i = 0; i < items.length; i++) {
213
+ buf.writeInt32BE(0); // reserved for this range's length
214
+ pos = buf.position;
215
+ range.encodeBinary(buf, items[i], options);
216
+ buf.buffer.writeInt32BE(buf.size - pos, pos - 4);
217
+ }
218
+ },
219
+ decodeText(v, options) {
220
+ const s = v.trim();
221
+ if (!s.startsWith('{') || !s.endsWith('}'))
222
+ throw new Error(`"${v}" is not a ${name} literal`);
223
+ const body = s.substring(1, s.length - 1).trim();
224
+ if (!body)
225
+ return setTypeOid([], oid);
226
+ // Ranges are split on the comma that follows a closing bracket, so
227
+ // the comma between a range's own two bounds is left alone.
228
+ const out = [];
229
+ let depth = 0;
230
+ let start = 0;
231
+ let quoted = false;
232
+ let i;
233
+ let c;
234
+ for (i = 0; i < body.length; i++) {
235
+ c = body.charAt(i);
236
+ if (quoted) {
237
+ if (c === '\\')
238
+ i++;
239
+ else if (c === '"')
240
+ quoted = false;
241
+ continue;
242
+ }
243
+ if (c === '"')
244
+ quoted = true;
245
+ else if (c === '[' || c === '(')
246
+ depth++;
247
+ else if (c === ']' || c === ')')
248
+ depth--;
249
+ else if (c === ',' && depth === 0) {
250
+ out.push(range.decodeText(body.substring(start, i), options));
251
+ start = i + 1;
252
+ }
253
+ }
254
+ out.push(range.decodeText(body.substring(start), options));
255
+ return setTypeOid(out, oid);
256
+ },
257
+ encodeText(v, options) {
258
+ const items = Array.isArray(v) ? v : [v];
259
+ return ('{' + items.map(x => range.encodeText(x, options)).join(',') + '}');
260
+ },
261
+ isType(v) {
262
+ return (Array.isArray(v) && v.length > 0 && v.every(x => x instanceof Range));
263
+ },
264
+ // An array of Ranges is also what a `Range[]` column decodes to, so
265
+ // leaving this to inference would make the two indistinguishable for
266
+ // an untyped parameter. Ask for it with BindParam when you mean it.
267
+ inferrable: false,
268
+ };
269
+ }
270
+ /**
271
+ * The six range types PostgreSQL ships, their multirange counterparts and
272
+ * the array form of each - twelve scalar types over one format, which is
273
+ * why they are built here rather than written out one file at a time.
274
+ */
275
+ const BUILT_IN = [
276
+ [
277
+ 'int4range',
278
+ DataTypeOIDs.int4range,
279
+ DataTypeOIDs._int4range,
280
+ DataTypeOIDs.int4multirange,
281
+ DataTypeOIDs._int4multirange,
282
+ DataTypeOIDs.int4,
283
+ Int4Type,
284
+ ],
285
+ [
286
+ 'int8range',
287
+ DataTypeOIDs.int8range,
288
+ DataTypeOIDs._int8range,
289
+ DataTypeOIDs.int8multirange,
290
+ DataTypeOIDs._int8multirange,
291
+ DataTypeOIDs.int8,
292
+ Int8Type,
293
+ ],
294
+ [
295
+ 'numrange',
296
+ DataTypeOIDs.numrange,
297
+ DataTypeOIDs._numrange,
298
+ DataTypeOIDs.nummultirange,
299
+ DataTypeOIDs._nummultirange,
300
+ DataTypeOIDs.numeric,
301
+ NumericType,
302
+ ],
303
+ [
304
+ 'daterange',
305
+ DataTypeOIDs.daterange,
306
+ DataTypeOIDs._daterange,
307
+ DataTypeOIDs.datemultirange,
308
+ DataTypeOIDs._datemultirange,
309
+ DataTypeOIDs.date,
310
+ DateType,
311
+ ],
312
+ [
313
+ 'tsrange',
314
+ DataTypeOIDs.tsrange,
315
+ DataTypeOIDs._tsrange,
316
+ DataTypeOIDs.tsmultirange,
317
+ DataTypeOIDs._tsmultirange,
318
+ DataTypeOIDs.timestamp,
319
+ TimestampType,
320
+ ],
321
+ [
322
+ 'tstzrange',
323
+ DataTypeOIDs.tstzrange,
324
+ DataTypeOIDs._tstzrange,
325
+ DataTypeOIDs.tstzmultirange,
326
+ DataTypeOIDs._tstzmultirange,
327
+ DataTypeOIDs.timestamptz,
328
+ TimestamptzType,
329
+ ],
330
+ ];
331
+ export const RangeTypes = [];
332
+ for (const [name, oid, arrayOid, multiOid, multiArrayOid, , element,] of BUILT_IN) {
333
+ const rangeType = createRangeType(name, oid, element);
334
+ const multiName = name.replace('range', 'multirange');
335
+ const multiType = createMultiRangeType(multiName, multiOid, rangeType);
336
+ RangeTypes.push(rangeType, { ...rangeType, name: '_' + name, oid: arrayOid, elementsOID: oid }, multiType, {
337
+ ...multiType,
338
+ name: '_' + multiName,
339
+ oid: multiArrayOid,
340
+ elementsOID: multiOid,
341
+ });
342
+ }
@@ -0,0 +1,5 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ export declare const PgSnapshotType: DataType;
3
+ export declare const ArrayPgSnapshotType: DataType;
4
+ export declare const TxidSnapshotType: DataType;
5
+ export declare const ArrayTxidSnapshotType: DataType;
@@ -0,0 +1,96 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ /**
3
+ * `pg_snapshot` and its older name `txid_snapshot` - what
4
+ * `pg_current_snapshot()` answers with, and what tells you which
5
+ * transactions were in flight at a moment.
6
+ *
7
+ * Both decode to the `xmin:xmax:xip,xip` string the server prints. The
8
+ * parts are transaction ids, and there is nothing to do with them but
9
+ * compare them against another snapshot - which the server's own
10
+ * operators do far better than JavaScript could, since these are the
11
+ * 64-bit counters `xid8` carries. The string is the whole value and
12
+ * casts straight back.
13
+ */
14
+ const BIG32 = BigInt(32);
15
+ const MASK32 = BigInt(0xffffffff);
16
+ /** The largest high half a JavaScript number still holds exactly. */
17
+ const MAX_SAFE_HIGH = 0x1fffff;
18
+ const SNAPSHOT_PATTERN = /^(\d+):(\d+):(\d+(?:,\d+)*)?$/;
19
+ /** A 64-bit transaction id as its decimal text, whatever its magnitude. */
20
+ function readXid(v, offset) {
21
+ const hi = v.readUInt32BE(offset);
22
+ const lo = v.readUInt32BE(offset + 4);
23
+ if (hi <= MAX_SAFE_HIGH)
24
+ return '' + (hi * 4294967296 + lo);
25
+ return ((BigInt(hi) << BIG32) | BigInt(lo)).toString();
26
+ }
27
+ function writeXid(buf, s) {
28
+ const n = BigInt(s);
29
+ buf.writeUInt32BE(Number(n >> BIG32));
30
+ buf.writeUInt32BE(Number(n & MASK32));
31
+ }
32
+ function createType(name, oid) {
33
+ return {
34
+ name,
35
+ oid,
36
+ jsType: 'string',
37
+ // See inet-type.ts: a string-shaped type stays out of inference.
38
+ inferrable: false,
39
+ encodeText(v) {
40
+ return '' + v;
41
+ },
42
+ /**
43
+ * An int32 count of in-flight transactions, then xmin and xmax, then
44
+ * that many ids - each of them 64 bits.
45
+ */
46
+ encodeBinary(buf, v) {
47
+ const m = typeof v === 'string' ? SNAPSHOT_PATTERN.exec(v) : undefined;
48
+ if (!m)
49
+ throw new Error(`"${v}" is not a valid ${name} value`);
50
+ const xip = m[3] ? m[3].split(',') : [];
51
+ buf.writeInt32BE(xip.length);
52
+ writeXid(buf, m[1]);
53
+ writeXid(buf, m[2]);
54
+ let i;
55
+ const l = xip.length;
56
+ for (i = 0; i < l; i++)
57
+ writeXid(buf, xip[i]);
58
+ },
59
+ decodeBinary(v, offset = 0) {
60
+ const count = v.readInt32BE(offset);
61
+ let out = readXid(v, offset + 4) + ':' + readXid(v, offset + 12) + ':';
62
+ let i;
63
+ let p = offset + 20;
64
+ for (i = 0; i < count; i++, p += 8) {
65
+ if (i)
66
+ out += ',';
67
+ out += readXid(v, p);
68
+ }
69
+ return out;
70
+ },
71
+ decodeText(v) {
72
+ return v;
73
+ },
74
+ // Digits, colons and commas - see inet-type.ts for why 'latin1'.
75
+ decodeTextBuffer(buf, offset, len) {
76
+ return buf.toString('latin1', offset, offset + len);
77
+ },
78
+ isType(v) {
79
+ return typeof v === 'string' && SNAPSHOT_PATTERN.test(v);
80
+ },
81
+ };
82
+ }
83
+ export const PgSnapshotType = createType('pg_snapshot', DataTypeOIDs.pg_snapshot);
84
+ export const ArrayPgSnapshotType = {
85
+ ...PgSnapshotType,
86
+ name: '_pg_snapshot',
87
+ oid: DataTypeOIDs._pg_snapshot,
88
+ elementsOID: DataTypeOIDs.pg_snapshot,
89
+ };
90
+ export const TxidSnapshotType = createType('txid_snapshot', DataTypeOIDs.txid_snapshot);
91
+ export const ArrayTxidSnapshotType = {
92
+ ...TxidSnapshotType,
93
+ name: '_txid_snapshot',
94
+ oid: DataTypeOIDs._txid_snapshot,
95
+ elementsOID: DataTypeOIDs.txid_snapshot,
96
+ };
@@ -0,0 +1,3 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ export declare const TidType: DataType;
3
+ export declare const ArrayTidType: DataType;
@@ -0,0 +1,50 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ /**
3
+ * A `tid` is where a row physically sits - the `ctid` system column,
4
+ * which applications select to identify a row without a key, to
5
+ * deduplicate, or to update exactly one of a set of identical rows.
6
+ *
7
+ * It decodes to the `(block,offset)` string the server prints. The two
8
+ * numbers are a physical address that VACUUM is free to change, so there
9
+ * is nothing to compute with them; what a caller does with a ctid is
10
+ * compare it or hand it straight back, and the string does both. `pg`
11
+ * leaves it a string too.
12
+ */
13
+ const TID_PATTERN = /^\(\s*(\d+)\s*,\s*(\d+)\s*\)$/;
14
+ export const TidType = {
15
+ name: 'tid',
16
+ oid: DataTypeOIDs.tid,
17
+ jsType: 'string',
18
+ // `(0,1)` is an ordinary-looking string - see inet-type.ts.
19
+ inferrable: false,
20
+ encodeText(v) {
21
+ return '' + v;
22
+ },
23
+ /** A uint32 block number and a uint16 offset within it. */
24
+ encodeBinary(buf, v) {
25
+ const m = typeof v === 'string' ? TID_PATTERN.exec(v) : undefined;
26
+ if (!m)
27
+ throw new Error(`"${v}" is not a valid tid value`);
28
+ buf.writeUInt32BE(+m[1]);
29
+ buf.writeUInt16BE(+m[2]);
30
+ },
31
+ decodeBinary(v, offset = 0) {
32
+ return ('(' + v.readUInt32BE(offset) + ',' + v.readUInt16BE(offset + 4) + ')');
33
+ },
34
+ decodeText(v) {
35
+ return v;
36
+ },
37
+ // Digits, a comma and two parentheses - see inet-type.ts for 'latin1'.
38
+ decodeTextBuffer(buf, offset, len) {
39
+ return buf.toString('latin1', offset, offset + len);
40
+ },
41
+ isType(v) {
42
+ return typeof v === 'string' && TID_PATTERN.test(v);
43
+ },
44
+ };
45
+ export const ArrayTidType = {
46
+ ...TidType,
47
+ name: '_tid',
48
+ oid: DataTypeOIDs._tid,
49
+ elementsOID: DataTypeOIDs.tid,
50
+ };
@@ -67,6 +67,15 @@ export const TimestamptzType = {
67
67
  return d;
68
68
  return TimestamptzType.decodeText(buf.toString('latin1', offset, offset + len), options);
69
69
  },
70
+ /**
71
+ * Unreachable through `determine()`: TimestampType, TimeType and
72
+ * DateType are all consulted before this one and every Date is claimed
73
+ * by one of them. Nothing depends on it - a Date parameter goes out
74
+ * unspecified and the column decides what it means (see
75
+ * format-datetime.ts's formatDateParam) - so this stays as the
76
+ * truthful answer to a direct call rather than being narrowed to
77
+ * describe a walk it never wins.
78
+ */
70
79
  isType(v) {
71
80
  return v instanceof Date;
72
81
  },
@@ -0,0 +1,3 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ export declare const TimeTzType: DataType;
3
+ export declare const ArrayTimeTzType: DataType;