postgrejs 3.6.1 → 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 (63) hide show
  1. package/CHANGELOG.md +50 -6
  2. package/README.md +9 -83
  3. package/connection/intl-connection.d.ts +5 -3
  4. package/connection/intl-connection.js +43 -15
  5. package/connection/prepared-statement.js +2 -2
  6. package/constants.d.ts +60 -0
  7. package/constants.js +119 -1
  8. package/data-type-map.js +82 -0
  9. package/data-types/bit-type.d.ts +5 -0
  10. package/data-types/bit-type.js +91 -0
  11. package/data-types/box-type.js +9 -19
  12. package/data-types/circle-type.js +6 -16
  13. package/data-types/classes/geometric.d.ts +105 -0
  14. package/data-types/classes/geometric.js +197 -0
  15. package/data-types/classes/interval.d.ts +73 -0
  16. package/data-types/classes/interval.js +181 -0
  17. package/data-types/classes/numeric.d.ts +37 -0
  18. package/data-types/classes/numeric.js +47 -0
  19. package/data-types/classes/range.d.ts +74 -0
  20. package/data-types/classes/range.js +111 -0
  21. package/data-types/classes/type-oid.d.ts +34 -0
  22. package/data-types/classes/type-oid.js +45 -0
  23. package/data-types/inet-type.d.ts +5 -0
  24. package/data-types/inet-type.js +286 -0
  25. package/data-types/interval-type.d.ts +5 -0
  26. package/data-types/interval-type.js +79 -0
  27. package/data-types/jsonpath-type.d.ts +3 -0
  28. package/data-types/jsonpath-type.js +53 -0
  29. package/data-types/line-type.d.ts +3 -0
  30. package/data-types/line-type.js +41 -0
  31. package/data-types/lseg-type.js +9 -19
  32. package/data-types/macaddr-type.d.ts +5 -0
  33. package/data-types/macaddr-type.js +86 -0
  34. package/data-types/numeric-type.js +98 -69
  35. package/data-types/path-type.d.ts +5 -0
  36. package/data-types/path-type.js +139 -0
  37. package/data-types/pg-lsn-type.d.ts +3 -0
  38. package/data-types/pg-lsn-type.js +58 -0
  39. package/data-types/point-type.js +8 -13
  40. package/data-types/range-type.d.ts +23 -0
  41. package/data-types/range-type.js +342 -0
  42. package/data-types/snapshot-type.d.ts +5 -0
  43. package/data-types/snapshot-type.js +96 -0
  44. package/data-types/tid-type.d.ts +3 -0
  45. package/data-types/tid-type.js +50 -0
  46. package/data-types/timetz-type.d.ts +3 -0
  47. package/data-types/timetz-type.js +184 -0
  48. package/data-types/tsvector-type.d.ts +5 -0
  49. package/data-types/tsvector-type.js +216 -0
  50. package/data-types/xid-type.d.ts +7 -0
  51. package/data-types/xid-type.js +110 -0
  52. package/index.d.ts +4 -0
  53. package/index.js +4 -0
  54. package/interfaces/data-mapping-options.d.ts +26 -0
  55. package/interfaces/data-type.d.ts +8 -9
  56. package/package.json +1 -1
  57. package/protocol/connection-lost-error.d.ts +5 -3
  58. package/protocol/connection-lost-error.js +5 -3
  59. package/protocol/pg-socket.js +16 -2
  60. package/util/parse-array.js +17 -2
  61. package/util/resolve-column-formats.d.ts +2 -1
  62. package/util/resolve-column-formats.js +35 -8
  63. package/util/stringify-for-sql.js +37 -1
@@ -0,0 +1,286 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ /**
3
+ * `inet` and `cidr` share a file because they share everything that
4
+ * matters: one wire format, one JavaScript shape, and one pair of
5
+ * conversions. A single flag byte tells them apart on the wire, and on
6
+ * output the only difference is whether the netmask is always printed.
7
+ *
8
+ * Both decode to a string. JavaScript has no address type, and every Node
9
+ * API that takes an address - `net`, `dns`, `http` - takes a string, so an
10
+ * object of parts would be something every caller has to join back
11
+ * together before using. `pg` answers with a string too.
12
+ */
13
+ /**
14
+ * PostgreSQL's own address-family numbers, deliberately not the
15
+ * platform's: they are written into the stored value, so they cannot
16
+ * depend on how a particular `<sys/socket.h>` happens to number `AF_INET6`.
17
+ */
18
+ const PGSQL_AF_INET = 2;
19
+ const PGSQL_AF_INET6 = 3;
20
+ const IPV4_PATTERN = /^\d{1,3}(?:\.\d{1,3}){0,3}$/;
21
+ const IPV6_GROUP_PATTERN = /^[0-9a-fA-F]{1,4}$/;
22
+ /**
23
+ * Writes the four octets of an IPv4 address.
24
+ *
25
+ * Fewer than four is allowed, the rest reading as zero - `10/8` for
26
+ * `10.0.0.0/8`. That is PostgreSQL's own rule for both types, confirmed
27
+ * against the server rather than assumed: `inet '10.0/16'` is accepted
28
+ * and stored as `10.0.0.0/16`.
29
+ */
30
+ function writeIPv4(out, offset, v) {
31
+ if (!IPV4_PATTERN.test(v))
32
+ return false;
33
+ const parts = v.split('.');
34
+ const l = parts.length;
35
+ let i;
36
+ let n;
37
+ for (i = 0; i < l; i++) {
38
+ n = +parts[i];
39
+ if (n > 255)
40
+ return false;
41
+ out[offset + i] = n;
42
+ }
43
+ return true;
44
+ }
45
+ /**
46
+ * Writes the sixteen bytes of an IPv6 address, accepting the two
47
+ * shorthands the server does: a single `::` standing for at least one
48
+ * all-zero group, and a trailing dotted-quad for the last two groups.
49
+ */
50
+ function writeIPv6(out, offset, v) {
51
+ const dot = v.indexOf('.');
52
+ if (dot >= 0) {
53
+ // An embedded IPv4 tail occupies the last two groups. Rewritten into
54
+ // hex here so the group walk below does not have to know about it.
55
+ const i = v.lastIndexOf(':', dot);
56
+ if (i < 0)
57
+ return false;
58
+ const quad = Buffer.alloc(4);
59
+ const tail = v.slice(i + 1);
60
+ // Unlike a bare IPv4 address, the embedded form has no abbreviated
61
+ // spelling - `::1.2` is not an address.
62
+ if (tail.split('.').length !== 4 || !writeIPv4(quad, 0, tail))
63
+ return false;
64
+ v =
65
+ v.slice(0, i + 1) +
66
+ quad.readUInt16BE(0).toString(16) +
67
+ ':' +
68
+ quad.readUInt16BE(2).toString(16);
69
+ }
70
+ const halves = v.split('::');
71
+ if (halves.length > 2)
72
+ return false;
73
+ const head = halves[0] ? halves[0].split(':') : [];
74
+ const tail = halves.length === 2 && halves[1] ? halves[1].split(':') : [];
75
+ const given = head.length + tail.length;
76
+ // Without `::` every group must be written out; with it, at least one
77
+ // must be left for the `::` itself to stand for.
78
+ if (halves.length === 2 ? given > 7 : given !== 8)
79
+ return false;
80
+ out.fill(0, offset, offset + 16);
81
+ let p = offset;
82
+ let g;
83
+ for (g of head) {
84
+ if (!IPV6_GROUP_PATTERN.test(g))
85
+ return false;
86
+ out.writeUInt16BE(parseInt(g, 16), p);
87
+ p += 2;
88
+ }
89
+ p = offset + 16 - tail.length * 2;
90
+ for (g of tail) {
91
+ if (!IPV6_GROUP_PATTERN.test(g))
92
+ return false;
93
+ out.writeUInt16BE(parseInt(g, 16), p);
94
+ p += 2;
95
+ }
96
+ return true;
97
+ }
98
+ /**
99
+ * Renders sixteen bytes the way `inet_net_ntop()` does, which is what
100
+ * this has to match - not any general IPv6 convention - because the
101
+ * point is to hand back the string the server itself would have printed.
102
+ *
103
+ * The longest run of zero groups collapses to `::`, but only when it
104
+ * covers at least two of them (`::2:3:4:5:6:7:8` comes back from the
105
+ * server as `0:2:3:4:5:6:7:8`), and the earliest run wins a tie. An
106
+ * address whose last four bytes are really an IPv4 one is printed in
107
+ * dotted form.
108
+ */
109
+ function formatIPv6(buf, offset) {
110
+ const words = new Array(8);
111
+ let i;
112
+ for (i = 0; i < 8; i++)
113
+ words[i] = buf.readUInt16BE(offset + i * 2);
114
+ let bestBase = -1;
115
+ let bestLen = 0;
116
+ let curBase = -1;
117
+ let curLen = 0;
118
+ for (i = 0; i < 8; i++) {
119
+ if (words[i] !== 0) {
120
+ curBase = -1;
121
+ continue;
122
+ }
123
+ if (curBase === -1) {
124
+ curBase = i;
125
+ curLen = 1;
126
+ }
127
+ else
128
+ curLen++;
129
+ if (curLen > bestLen) {
130
+ bestBase = curBase;
131
+ bestLen = curLen;
132
+ }
133
+ }
134
+ if (bestLen < 2)
135
+ bestBase = -1;
136
+ let out = '';
137
+ for (i = 0; i < 8; i++) {
138
+ if (bestBase !== -1 && i >= bestBase && i < bestBase + bestLen) {
139
+ if (i === bestBase)
140
+ out += ':';
141
+ continue;
142
+ }
143
+ if (i !== 0)
144
+ out += ':';
145
+ // `::a.b.c.d` and `::ffff:a.b.c.d` - the two prefixes that mean the
146
+ // tail is an IPv4 address. Nothing else qualifies, so `::1:1.2.3.4`
147
+ // prints as `::1:102:304`.
148
+ if (i === 6 &&
149
+ bestBase === 0 &&
150
+ (bestLen === 6 || (bestLen === 5 && words[5] === 0xffff))) {
151
+ out +=
152
+ buf[offset + 12] +
153
+ '.' +
154
+ buf[offset + 13] +
155
+ '.' +
156
+ buf[offset + 14] +
157
+ '.' +
158
+ buf[offset + 15];
159
+ break;
160
+ }
161
+ out += words[i].toString(16);
162
+ }
163
+ // A run that reaches the end leaves only one colon behind.
164
+ if (bestBase !== -1 && bestBase + bestLen === 8)
165
+ out += ':';
166
+ return out;
167
+ }
168
+ /**
169
+ * The address and its netmask, or undefined if the string is not one.
170
+ * Splitting is done here rather than by the caller because the two types
171
+ * differ only in what they do with the parts.
172
+ */
173
+ function parseAddress(v) {
174
+ if (typeof v !== 'string')
175
+ return undefined;
176
+ const slash = v.indexOf('/');
177
+ const addr = slash < 0 ? v : v.slice(0, slash);
178
+ const family = addr.includes(':') ? PGSQL_AF_INET6 : PGSQL_AF_INET;
179
+ const size = family === PGSQL_AF_INET6 ? 16 : 4;
180
+ const bytes = Buffer.alloc(size);
181
+ const ok = family === PGSQL_AF_INET6
182
+ ? writeIPv6(bytes, 0, addr)
183
+ : writeIPv4(bytes, 0, addr);
184
+ if (!ok)
185
+ return undefined;
186
+ let bits = size * 8;
187
+ if (slash >= 0) {
188
+ const s = v.slice(slash + 1);
189
+ if (!/^\d{1,3}$/.test(s))
190
+ return undefined;
191
+ bits = +s;
192
+ if (bits > size * 8)
193
+ return undefined;
194
+ }
195
+ return { family, bits, bytes };
196
+ }
197
+ function encode(buf, v, isCidr) {
198
+ const a = parseAddress(v);
199
+ if (!a)
200
+ throw new Error(`"${v}" is not a valid ${isCidr ? 'cidr' : 'inet'} value`);
201
+ buf.writeUInt8(a.family);
202
+ // Unsigned: a full IPv6 mask is 128, which does not fit a signed byte.
203
+ buf.writeUInt8(a.bits);
204
+ buf.writeUInt8(isCidr ? 1 : 0);
205
+ buf.writeUInt8(a.bytes.length);
206
+ buf.writeBytes(a.bytes);
207
+ }
208
+ /**
209
+ * Four header bytes - family, netmask bits, the cidr flag and the
210
+ * address length - then the address itself. The flag is what decides
211
+ * whether the netmask is printed, so one decoder serves both types: a
212
+ * `cidr` always carries its mask, an `inet` only when it is not the whole
213
+ * address.
214
+ */
215
+ function decode(v, offset) {
216
+ const family = v[offset];
217
+ const bits = v[offset + 1];
218
+ const isCidr = v[offset + 2];
219
+ const size = v[offset + 3];
220
+ if (family !== PGSQL_AF_INET && family !== PGSQL_AF_INET6)
221
+ throw new Error(`Unknown address family ${family} in inet value`);
222
+ const text = family === PGSQL_AF_INET6
223
+ ? formatIPv6(v, offset + 4)
224
+ : v[offset + 4] +
225
+ '.' +
226
+ v[offset + 5] +
227
+ '.' +
228
+ v[offset + 6] +
229
+ '.' +
230
+ v[offset + 7];
231
+ return isCidr || bits !== size * 8 ? text + '/' + bits : text;
232
+ }
233
+ export const InetType = {
234
+ name: 'inet',
235
+ oid: DataTypeOIDs.inet,
236
+ jsType: 'string',
237
+ // An address is not a shape - it is a string like any other, and an
238
+ // application storing one in a `text` column passes exactly the same
239
+ // value. Inferring this type from a string would send `inet` where
240
+ // `text` was meant and break that query, so the type is reached only by
241
+ // naming it: `new BindParam(DataTypeOIDs.inet, '192.168.0.1')`, or a
242
+ // column whose declared type says so.
243
+ inferrable: false,
244
+ encodeText(v) {
245
+ // Handed to the server as written; it does its own parsing here, and
246
+ // accepts spellings this file's own parser is not asked to know.
247
+ return '' + v;
248
+ },
249
+ encodeBinary(buf, v) {
250
+ encode(buf, v, false);
251
+ },
252
+ decodeBinary(v, offset = 0) {
253
+ return decode(v, offset);
254
+ },
255
+ decodeText(v) {
256
+ return v;
257
+ },
258
+ // An address is pure ASCII, so 'latin1' decodes identically to 'utf8'
259
+ // while skipping V8's multi-byte-sequence detection.
260
+ decodeTextBuffer(buf, offset, len) {
261
+ return buf.toString('latin1', offset, offset + len);
262
+ },
263
+ isType(v) {
264
+ return !!parseAddress(v);
265
+ },
266
+ };
267
+ export const ArrayInetType = {
268
+ ...InetType,
269
+ name: '_inet',
270
+ oid: DataTypeOIDs._inet,
271
+ elementsOID: DataTypeOIDs.inet,
272
+ };
273
+ export const CidrType = {
274
+ ...InetType,
275
+ name: 'cidr',
276
+ oid: DataTypeOIDs.cidr,
277
+ encodeBinary(buf, v) {
278
+ encode(buf, v, true);
279
+ },
280
+ };
281
+ export const ArrayCidrType = {
282
+ ...CidrType,
283
+ name: '_cidr',
284
+ oid: DataTypeOIDs._cidr,
285
+ elementsOID: DataTypeOIDs.cidr,
286
+ };
@@ -0,0 +1,5 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ import { Interval } from './classes/interval.js';
3
+ export declare function parseIntervalText(v: string): Interval;
4
+ export declare const IntervalType: DataType;
5
+ export declare const ArrayIntervalType: DataType;
@@ -0,0 +1,79 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ import { readBigInt64BE } from '../util/bigint-methods.js';
3
+ import { Interval } from './classes/interval.js';
4
+ /**
5
+ * What PostgreSQL prints under the default `IntervalStyle` - years, months
6
+ * and days as counted words, then a signed clock time. Every part is
7
+ * optional and the server omits the ones that are zero, except that a
8
+ * wholly zero interval still prints `00:00:00`.
9
+ */
10
+ const INTERVAL_PATTERN = /^\s*(?:([+-]?\d+)\s+years?\s*)?(?:([+-]?\d+)\s+mons?\s*)?(?:([+-]?\d+)\s+days?\s*)?(?:([+-])?(\d+):(\d\d):(\d\d)(?:\.(\d{1,6}))?)?\s*$/;
11
+ function toInterval(v) {
12
+ if (v instanceof Interval)
13
+ return v;
14
+ if (typeof v === 'string')
15
+ return parseIntervalText(v);
16
+ if (v && typeof v === 'object')
17
+ return new Interval(v);
18
+ throw new TypeError(`"${typeof v}" cannot be encoded as an interval - pass an Interval, ` +
19
+ 'a plain object of its fields, or a string PostgreSQL would accept');
20
+ }
21
+ export function parseIntervalText(v) {
22
+ const m = INTERVAL_PATTERN.exec(v);
23
+ if (!m) {
24
+ // Rather than answering with a zero interval, which is what reading
25
+ // an unrecognized shape leniently would amount to. The other styles
26
+ // (`sql_standard`, `postgres_verbose`, `iso_8601`) are not parsed
27
+ // here, and the binary path - which is the default - never sees any
28
+ // of this.
29
+ throw new Error(`"${v}" is not an interval in PostgreSQL's default IntervalStyle`);
30
+ }
31
+ const sign = m[4] === '-' ? -1 : 1;
32
+ return new Interval({
33
+ years: m[1] ? parseInt(m[1], 10) : 0,
34
+ months: m[2] ? parseInt(m[2], 10) : 0,
35
+ days: m[3] ? parseInt(m[3], 10) : 0,
36
+ hours: m[5] ? sign * parseInt(m[5], 10) : 0,
37
+ minutes: m[6] ? sign * parseInt(m[6], 10) : 0,
38
+ seconds: m[7] ? sign * parseInt(m[7], 10) : 0,
39
+ // The fraction is microseconds with trailing zeroes dropped, so `.5`
40
+ // is half a second rather than five microseconds.
41
+ milliseconds: m[8] ? (sign * parseInt(m[8].padEnd(6, '0'), 10)) / 1000 : 0,
42
+ });
43
+ }
44
+ export const IntervalType = {
45
+ name: 'interval',
46
+ oid: DataTypeOIDs.interval,
47
+ jsType: 'Interval',
48
+ /**
49
+ * 16 bytes, and three separate quantities rather than one total: a
50
+ * signed microsecond count for the time, then a day count and a month
51
+ * count. The server keeps them apart because they are not convertible -
52
+ * see the Interval class.
53
+ */
54
+ decodeBinary(v, offset = 0) {
55
+ const micros = typeof v.readBigInt64BE === 'function'
56
+ ? v.readBigInt64BE(offset)
57
+ : readBigInt64BE(v, offset);
58
+ return Interval.fromParts(micros, v.readInt32BE(offset + 8), v.readInt32BE(offset + 12));
59
+ },
60
+ encodeBinary(buf, v) {
61
+ const iv = toInterval(v);
62
+ buf.writeBigInt64BE(iv.totalMicroseconds);
63
+ buf.writeInt32BE(iv.days);
64
+ buf.writeInt32BE(iv.totalMonths);
65
+ },
66
+ decodeText: parseIntervalText,
67
+ encodeText(v) {
68
+ return toInterval(v).toString();
69
+ },
70
+ isType(v) {
71
+ return v instanceof Interval;
72
+ },
73
+ };
74
+ export const ArrayIntervalType = {
75
+ ...IntervalType,
76
+ name: '_interval',
77
+ oid: DataTypeOIDs._interval,
78
+ elementsOID: DataTypeOIDs.interval,
79
+ };
@@ -0,0 +1,3 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ export declare const JsonPathType: DataType;
3
+ export declare const ArrayJsonPathType: DataType;
@@ -0,0 +1,53 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ /**
3
+ * A jsonpath decodes to a string, and there is nothing else it could
4
+ * sensibly be: the binary form is the text form with one byte in front
5
+ * of it. Parsing the expression into a tree would be inventing an API
6
+ * for something the server already understands and this client never
7
+ * evaluates. `pg` leaves it a string too.
8
+ *
9
+ * The string that comes back is PostgreSQL's normalized spelling, not
10
+ * what was written - `$.a[*].b` is stored and printed as `$."a"[*]."b"`.
11
+ */
12
+ /** The only format the server has ever written, and the one it reads. */
13
+ const JSONPATH_VERSION = 1;
14
+ export const JsonPathType = {
15
+ name: 'jsonpath',
16
+ oid: DataTypeOIDs.jsonpath,
17
+ jsType: 'string',
18
+ // A jsonpath is a string, and inferring this type from one would send
19
+ // `jsonpath` where `text` was meant. See inet-type.ts.
20
+ inferrable: false,
21
+ encodeText(v) {
22
+ return '' + v;
23
+ },
24
+ /** A version byte, then the expression as UTF-8 - no length prefix. */
25
+ encodeBinary(buf, v) {
26
+ if (typeof v !== 'string')
27
+ throw new Error(`"${v}" is not a valid jsonpath value`);
28
+ buf.writeUInt8(JSONPATH_VERSION);
29
+ buf.writeString(v, 'utf8');
30
+ },
31
+ decodeBinary(v, offset = 0, len) {
32
+ const version = v[offset];
33
+ if (version !== JSONPATH_VERSION)
34
+ throw new Error(`Unsupported jsonpath version ${version}`);
35
+ return v.toString('utf8', offset + 1, offset + len);
36
+ },
37
+ decodeText(v) {
38
+ return v;
39
+ },
40
+ isType(v) {
41
+ // Every jsonpath begins with a variable or a mode word, and `$` is
42
+ // the only variable an expression can start with once `strict` or
43
+ // `lax` is allowed for - enough to tell a path from ordinary text
44
+ // without parsing one.
45
+ return typeof v === 'string' && /^\s*(?:(?:strict|lax)\s+)?[$@]/.test(v);
46
+ },
47
+ };
48
+ export const ArrayJsonPathType = {
49
+ ...JsonPathType,
50
+ name: '_jsonpath',
51
+ oid: DataTypeOIDs._jsonpath,
52
+ elementsOID: DataTypeOIDs.jsonpath,
53
+ };
@@ -0,0 +1,3 @@
1
+ import type { DataType } from '../interfaces/data-type.js';
2
+ export declare const LineType: DataType;
3
+ export declare const ArrayLineType: DataType;
@@ -0,0 +1,41 @@
1
+ import { DataTypeOIDs } from '../constants.js';
2
+ import { Line } from './classes/geometric.js';
3
+ const LINE_PATTERN = /^\{ *(-?[\d.]+(?:e[-+]?\d+)?) *, *(-?[\d.]+(?:e[-+]?\d+)?) *, *(-?[\d.]+(?:e[-+]?\d+)?) *\}$/i;
4
+ export const LineType = {
5
+ name: 'line',
6
+ oid: DataTypeOIDs.line,
7
+ jsType: 'Line',
8
+ encodeText(v) {
9
+ return `{${v.a},${v.b},${v.c}}`;
10
+ },
11
+ /** Three float8s: the A, B and C of Ax + By + C = 0. */
12
+ encodeBinary(buf, v) {
13
+ buf.writeDoubleBE(v.a);
14
+ buf.writeDoubleBE(v.b);
15
+ buf.writeDoubleBE(v.c);
16
+ },
17
+ decodeBinary(v, offset = 0) {
18
+ return new Line(v.readDoubleBE(offset), v.readDoubleBE(offset + 8), v.readDoubleBE(offset + 16));
19
+ },
20
+ decodeText(v) {
21
+ const m = v.match(LINE_PATTERN);
22
+ if (!m)
23
+ return undefined;
24
+ return new Line(parseFloat(m[1]), parseFloat(m[2]), parseFloat(m[3]));
25
+ },
26
+ // See box-type.ts's decodeTextBuffer comment - same rationale.
27
+ decodeTextBuffer(buf, offset, len, options) {
28
+ return LineType.decodeText(buf.toString('latin1', offset, offset + len), options);
29
+ },
30
+ // A plain `{a, b, c}` is accepted for the same reason `{x, y}` is by
31
+ // point - see point-type.ts.
32
+ isType(v) {
33
+ return v instanceof Line || Line.isLineLike(v);
34
+ },
35
+ };
36
+ export const ArrayLineType = {
37
+ ...LineType,
38
+ name: '_line',
39
+ oid: DataTypeOIDs._line,
40
+ elementsOID: DataTypeOIDs.line,
41
+ };
@@ -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
+ };