@effect/sql-pg 4.0.0-rc.110 → 4.0.0-rc.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai-docs/package.json +2 -2
- package/dist/PgAuth.d.ts +69 -0
- package/dist/PgAuth.d.ts.map +1 -0
- package/dist/PgAuth.js +202 -0
- package/dist/PgAuth.js.map +1 -0
- package/dist/PgProtocol.d.ts +750 -0
- package/dist/PgProtocol.d.ts.map +1 -0
- package/dist/PgProtocol.js +1160 -0
- package/dist/PgProtocol.js.map +1 -0
- package/dist/PgTypes.d.ts +361 -0
- package/dist/PgTypes.d.ts.map +1 -0
- package/dist/PgTypes.js +1514 -0
- package/dist/PgTypes.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/package.json +11 -7
- package/src/PgAuth.ts +287 -0
- package/src/PgProtocol.ts +1820 -0
- package/src/PgTypes.ts +1930 -0
- package/src/index.ts +15 -0
package/dist/PgTypes.js
ADDED
|
@@ -0,0 +1,1514 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary codecs for PostgreSQL values, keyed by type OID.
|
|
3
|
+
*
|
|
4
|
+
* Version 1 implements the binary wire format (`format = 1`) only; passing
|
|
5
|
+
* `format = 0` to `decode` is an error. Layouts follow rust-postgres'
|
|
6
|
+
* `postgres-types`, including the infinity sentinels, and assume the server
|
|
7
|
+
* was built with `integer_datetimes` (the only supported configuration since
|
|
8
|
+
* PostgreSQL 10).
|
|
9
|
+
*
|
|
10
|
+
* There is no `typeof` inference: an OID is always supplied, either directly
|
|
11
|
+
* or through a constructor such as `int4` that carries it.
|
|
12
|
+
*
|
|
13
|
+
* `timestamp` has no time zone on the wire and is treated as UTC in both
|
|
14
|
+
* directions. Decoding drops sub-millisecond precision by truncating toward
|
|
15
|
+
* zero, including for timestamps before the PostgreSQL epoch.
|
|
16
|
+
*
|
|
17
|
+
* @since 4.0.0
|
|
18
|
+
*/
|
|
19
|
+
import * as Data from "effect/Data";
|
|
20
|
+
import * as Result from "effect/Result";
|
|
21
|
+
/**
|
|
22
|
+
* Failure returned when a value cannot be encoded or decoded for its OID.
|
|
23
|
+
*
|
|
24
|
+
* @category errors
|
|
25
|
+
* @since 4.0.0
|
|
26
|
+
*/
|
|
27
|
+
export class CodecError extends /*#__PURE__*/Data.TaggedError("PgTypesCodecError") {}
|
|
28
|
+
const fail = message => {
|
|
29
|
+
throw new CodecError({
|
|
30
|
+
message
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const result = evaluate => {
|
|
34
|
+
try {
|
|
35
|
+
return Result.succeed(evaluate());
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error instanceof CodecError) return Result.fail(error);
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const textEncoder = /*#__PURE__*/new TextEncoder();
|
|
42
|
+
const textDecoder = /*#__PURE__*/new TextDecoder("utf-8", {
|
|
43
|
+
fatal: true
|
|
44
|
+
});
|
|
45
|
+
const writeInt16 = (bytes, offset, value) => {
|
|
46
|
+
bytes[offset] = value >>> 8;
|
|
47
|
+
bytes[offset + 1] = value;
|
|
48
|
+
};
|
|
49
|
+
const readInt16 = (bytes, offset) => (bytes[offset] << 8 | bytes[offset + 1]) << 16 >> 16;
|
|
50
|
+
const readUint16 = (bytes, offset) => bytes[offset] << 8 | bytes[offset + 1];
|
|
51
|
+
const writeInt32 = (bytes, offset, value) => {
|
|
52
|
+
bytes[offset] = value >>> 24;
|
|
53
|
+
bytes[offset + 1] = value >>> 16;
|
|
54
|
+
bytes[offset + 2] = value >>> 8;
|
|
55
|
+
bytes[offset + 3] = value;
|
|
56
|
+
};
|
|
57
|
+
const readInt32 = (bytes, offset) => bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3];
|
|
58
|
+
/** Writes an int64, given a value the caller has checked fits a double exactly. */
|
|
59
|
+
const writeInt64 = (bytes, offset, value) => {
|
|
60
|
+
const high = Math.floor(value / 4294967296);
|
|
61
|
+
writeInt32(bytes, offset, high);
|
|
62
|
+
writeInt32(bytes, offset + 4, value - high * 4294967296);
|
|
63
|
+
};
|
|
64
|
+
const readUint32 = (bytes, offset) => (bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]) >>> 0;
|
|
65
|
+
/**
|
|
66
|
+
* Constructing a `DataView` costs more than the reads and writes it performs,
|
|
67
|
+
* so the fixed-width codecs stage values through these instead of wrapping
|
|
68
|
+
* every freshly allocated array.
|
|
69
|
+
*/
|
|
70
|
+
const scratch4 = /*#__PURE__*/new ArrayBuffer(4);
|
|
71
|
+
const scratchView4 = /*#__PURE__*/new DataView(scratch4);
|
|
72
|
+
const scratchBytes4 = /*#__PURE__*/new Uint8Array(scratch4);
|
|
73
|
+
const scratch8 = /*#__PURE__*/new ArrayBuffer(8);
|
|
74
|
+
const scratchView8 = /*#__PURE__*/new DataView(scratch8);
|
|
75
|
+
const scratchBytes8 = /*#__PURE__*/new Uint8Array(scratch8);
|
|
76
|
+
/**
|
|
77
|
+
* Stages eight wire bytes into `scratchView8`. Two big-endian `setInt32` calls
|
|
78
|
+
* beat both `Uint8Array.prototype.set` and eight stores, and unlike `set` they
|
|
79
|
+
* read from anywhere in `bytes` without a view of their own.
|
|
80
|
+
*/
|
|
81
|
+
const stage8 = (bytes, offset) => {
|
|
82
|
+
scratchView8.setInt32(0, readInt32(bytes, offset));
|
|
83
|
+
scratchView8.setInt32(4, readInt32(bytes, offset + 4));
|
|
84
|
+
};
|
|
85
|
+
/** Decimal text for every two-digit number, so formatting never calls `padStart`. */
|
|
86
|
+
const twoDigits = /* @__PURE__ */Array.from({
|
|
87
|
+
length: 100
|
|
88
|
+
}, (_, value) => (value < 10 ? "0" : "") + value);
|
|
89
|
+
/**
|
|
90
|
+
* Above this length `TextEncoder.encodeInto` beats a per-character loop, below
|
|
91
|
+
* it the call overhead dominates. Measured on V8: the loop runs at about a
|
|
92
|
+
* nanosecond per character and `encodeInto` costs about 50 ns whatever the
|
|
93
|
+
* length, so the crossover is around 50 characters.
|
|
94
|
+
*/
|
|
95
|
+
const asciiEncodeLimit = 48;
|
|
96
|
+
/**
|
|
97
|
+
* `encodeInto` needs somewhere to write. Encoding through this and copying out
|
|
98
|
+
* still beats `TextEncoder.encode`, which allocates inside the call. Strings
|
|
99
|
+
* that would need a larger one are rare enough to encode the slow way rather
|
|
100
|
+
* than keep that much memory alive.
|
|
101
|
+
*/
|
|
102
|
+
let scratchUtf8 = /*#__PURE__*/new Uint8Array(4096);
|
|
103
|
+
const scratchUtf8Limit = 64 * 1024;
|
|
104
|
+
/** Encodes into `scratchUtf8`, returning how many bytes it holds, or -1. */
|
|
105
|
+
const encodeUtf8Scratch = text => {
|
|
106
|
+
const capacity = text.length * 3;
|
|
107
|
+
if (capacity > scratchUtf8Limit) return -1;
|
|
108
|
+
if (capacity > scratchUtf8.length) scratchUtf8 = new Uint8Array(capacity);
|
|
109
|
+
return textEncoder.encodeInto(text, scratchUtf8).written;
|
|
110
|
+
};
|
|
111
|
+
const encodeUtf8 = text => {
|
|
112
|
+
const length = text.length;
|
|
113
|
+
if (length <= asciiEncodeLimit) {
|
|
114
|
+
const bytes = new Uint8Array(length);
|
|
115
|
+
let i = 0;
|
|
116
|
+
for (; i < length; i++) {
|
|
117
|
+
const code = text.charCodeAt(i);
|
|
118
|
+
if (code > 0x7f) break;
|
|
119
|
+
bytes[i] = code;
|
|
120
|
+
}
|
|
121
|
+
if (i === length) return bytes;
|
|
122
|
+
}
|
|
123
|
+
const written = encodeUtf8Scratch(text);
|
|
124
|
+
return written === -1 ? textEncoder.encode(text) : scratchUtf8.slice(0, written);
|
|
125
|
+
};
|
|
126
|
+
/** As `encodeUtf8`, but leaves `prefix` bytes free at the front. */
|
|
127
|
+
const encodeUtf8Prefixed = (text, prefix) => {
|
|
128
|
+
const length = text.length;
|
|
129
|
+
if (length <= asciiEncodeLimit) {
|
|
130
|
+
const bytes = new Uint8Array(prefix + length);
|
|
131
|
+
let i = 0;
|
|
132
|
+
for (; i < length; i++) {
|
|
133
|
+
const code = text.charCodeAt(i);
|
|
134
|
+
if (code > 0x7f) break;
|
|
135
|
+
bytes[prefix + i] = code;
|
|
136
|
+
}
|
|
137
|
+
if (i === length) return bytes;
|
|
138
|
+
}
|
|
139
|
+
const written = encodeUtf8Scratch(text);
|
|
140
|
+
if (written === -1) {
|
|
141
|
+
const body = textEncoder.encode(text);
|
|
142
|
+
const bytes = new Uint8Array(prefix + body.length);
|
|
143
|
+
bytes.set(body, prefix);
|
|
144
|
+
return bytes;
|
|
145
|
+
}
|
|
146
|
+
const bytes = new Uint8Array(prefix + written);
|
|
147
|
+
bytes.set(scratchUtf8.subarray(0, written), prefix);
|
|
148
|
+
return bytes;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* A view of `size` bytes at `offset`, or `bytes` itself when that is already
|
|
152
|
+
* the whole of it. Only the codecs that hand their bytes to something else
|
|
153
|
+
* need one; the rest read through `bytes` and `offset` directly.
|
|
154
|
+
*/
|
|
155
|
+
const region = (bytes, offset, size) => offset === 0 && size === bytes.length ? bytes : new Uint8Array(bytes.buffer, bytes.byteOffset + offset, size);
|
|
156
|
+
/**
|
|
157
|
+
* Below this length building the string a character at a time beats
|
|
158
|
+
* `TextDecoder.decode`, which costs about 50 ns before it looks at a byte.
|
|
159
|
+
* Above it the per-character cost of about 3 ns takes over.
|
|
160
|
+
*/
|
|
161
|
+
const asciiDecodeLimit = 10;
|
|
162
|
+
/**
|
|
163
|
+
* Node's own UTF-8 decoder, which reads straight out of the buffer with no
|
|
164
|
+
* view and about a quarter less overhead than `TextDecoder`. It swaps
|
|
165
|
+
* replacement characters in where `TextDecoder` would fail, so a result that
|
|
166
|
+
* contains one is handed to the strict decoder after all: either the
|
|
167
|
+
* character was really in the text and the same string comes back, or the
|
|
168
|
+
* bytes were invalid and the failure is the one `TextDecoder` always raised.
|
|
169
|
+
*/
|
|
170
|
+
const utf8Slice = globalThis.Buffer?.prototype?.utf8Slice;
|
|
171
|
+
const decodeUtf8 = (bytes, offset, size) => {
|
|
172
|
+
if (size <= asciiDecodeLimit) {
|
|
173
|
+
let text = "";
|
|
174
|
+
let index = 0;
|
|
175
|
+
for (; index < size; index++) {
|
|
176
|
+
const code = bytes[offset + index];
|
|
177
|
+
if (code > 0x7f) break;
|
|
178
|
+
text += String.fromCharCode(code);
|
|
179
|
+
}
|
|
180
|
+
if (index === size) return text;
|
|
181
|
+
}
|
|
182
|
+
if (utf8Slice !== undefined) {
|
|
183
|
+
const text = utf8Slice.call(bytes, offset, offset + size);
|
|
184
|
+
if (text.indexOf("\ufffd") === -1) return text;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
return textDecoder.decode(region(bytes, offset, size));
|
|
188
|
+
} catch {
|
|
189
|
+
return fail("Invalid UTF-8 in text value");
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const requireSize = (size, expected, name) => {
|
|
193
|
+
if (size !== expected) {
|
|
194
|
+
fail(`Expected ${expected} byte(s) for ${name}, received ${size}`);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const requireString = (value, name) => typeof value === "string" ? value : fail(`Expected a string for ${name}`);
|
|
198
|
+
const requireBigInt = (value, name) => typeof value === "bigint" ? value : fail(`Expected a bigint for ${name}`);
|
|
199
|
+
const requireNumber = (value, name) => typeof value === "number" ? value : fail(`Expected a number for ${name}`);
|
|
200
|
+
const requireInteger = (value, name, min, max) => {
|
|
201
|
+
const num = requireNumber(value, name);
|
|
202
|
+
if (!Number.isInteger(num) || num < min || num > max) {
|
|
203
|
+
fail(`Expected an integer in [${min}, ${max}] for ${name}, received ${num}`);
|
|
204
|
+
}
|
|
205
|
+
return num;
|
|
206
|
+
};
|
|
207
|
+
const ZERO = /*#__PURE__*/BigInt(0);
|
|
208
|
+
const THOUSAND = /*#__PURE__*/BigInt(1000);
|
|
209
|
+
const INT64_MIN = /*#__PURE__*/BigInt("-9223372036854775808");
|
|
210
|
+
const INT64_MAX = /*#__PURE__*/BigInt("9223372036854775807");
|
|
211
|
+
const INT32_MIN = -2147483648;
|
|
212
|
+
const INT32_MAX = 2147483647;
|
|
213
|
+
/** Milliseconds between the Unix epoch and the PostgreSQL epoch (2000-01-01). */
|
|
214
|
+
const PG_EPOCH_MS = 946684800000;
|
|
215
|
+
const PG_EPOCH_MICROS = 946684800000000;
|
|
216
|
+
/** Days between the Unix epoch and the PostgreSQL epoch. */
|
|
217
|
+
const PG_EPOCH_DAYS = 10957;
|
|
218
|
+
/**
|
|
219
|
+
* Beyond this an integer no longer has an exact `Number`, so the timestamp
|
|
220
|
+
* codecs fall back to `BigInt` rather than round. It is about 285 years of
|
|
221
|
+
* microseconds either side of 2000-01-01.
|
|
222
|
+
*/
|
|
223
|
+
const MAX_EXACT = 9007199254740992;
|
|
224
|
+
/** The `high` half of an int64 whose magnitude is below `MAX_EXACT`. */
|
|
225
|
+
const MAX_EXACT_HIGH = 0x200000;
|
|
226
|
+
/** Midnight to midnight, the widest PostgreSQL `time` and `timetz` allow. */
|
|
227
|
+
const MAX_TIME_MICROS_NUMBER = 86_400_000_000;
|
|
228
|
+
/** PostgreSQL accepts time zone displacements strictly inside +/-16 hours. */
|
|
229
|
+
const TZDISP_LIMIT_SECONDS = 57_600;
|
|
230
|
+
// -----------------------------------------------------------------------------
|
|
231
|
+
// OIDs
|
|
232
|
+
// -----------------------------------------------------------------------------
|
|
233
|
+
/**
|
|
234
|
+
* Type OIDs implemented by version 1 of this codec.
|
|
235
|
+
*
|
|
236
|
+
* @category constants
|
|
237
|
+
* @since 4.0.0
|
|
238
|
+
*/
|
|
239
|
+
export const OID = {
|
|
240
|
+
bool: 16,
|
|
241
|
+
bytea: 17,
|
|
242
|
+
name: 19,
|
|
243
|
+
int8: 20,
|
|
244
|
+
int2: 21,
|
|
245
|
+
int4: 23,
|
|
246
|
+
text: 25,
|
|
247
|
+
oid: 26,
|
|
248
|
+
json: 114,
|
|
249
|
+
cidr: 650,
|
|
250
|
+
float4: 700,
|
|
251
|
+
float8: 701,
|
|
252
|
+
inet: 869,
|
|
253
|
+
bpchar: 1042,
|
|
254
|
+
varchar: 1043,
|
|
255
|
+
date: 1082,
|
|
256
|
+
time: 1083,
|
|
257
|
+
timestamp: 1114,
|
|
258
|
+
timestamptz: 1184,
|
|
259
|
+
timetz: 1266,
|
|
260
|
+
numeric: 1700,
|
|
261
|
+
uuid: 2950,
|
|
262
|
+
jsonb: 3802,
|
|
263
|
+
boolArray: 1000,
|
|
264
|
+
byteaArray: 1001,
|
|
265
|
+
nameArray: 1003,
|
|
266
|
+
int8Array: 1016,
|
|
267
|
+
int2Array: 1005,
|
|
268
|
+
int4Array: 1007,
|
|
269
|
+
textArray: 1009,
|
|
270
|
+
oidArray: 1028,
|
|
271
|
+
jsonArray: 199,
|
|
272
|
+
cidrArray: 651,
|
|
273
|
+
float4Array: 1021,
|
|
274
|
+
float8Array: 1022,
|
|
275
|
+
inetArray: 1041,
|
|
276
|
+
bpcharArray: 1014,
|
|
277
|
+
varcharArray: 1015,
|
|
278
|
+
dateArray: 1182,
|
|
279
|
+
timeArray: 1183,
|
|
280
|
+
timestampArray: 1115,
|
|
281
|
+
timestamptzArray: 1185,
|
|
282
|
+
timetzArray: 1270,
|
|
283
|
+
numericArray: 1231,
|
|
284
|
+
uuidArray: 2951,
|
|
285
|
+
jsonbArray: 3807
|
|
286
|
+
};
|
|
287
|
+
const arrayToElement = /*#__PURE__*/new Map([[OID.boolArray, OID.bool], [OID.byteaArray, OID.bytea], [OID.nameArray, OID.name], [OID.int8Array, OID.int8], [OID.int2Array, OID.int2], [OID.int4Array, OID.int4], [OID.textArray, OID.text], [OID.oidArray, OID.oid], [OID.jsonArray, OID.json], [OID.cidrArray, OID.cidr], [OID.float4Array, OID.float4], [OID.float8Array, OID.float8], [OID.inetArray, OID.inet], [OID.bpcharArray, OID.bpchar], [OID.varcharArray, OID.varchar], [OID.dateArray, OID.date], [OID.timeArray, OID.time], [OID.timestampArray, OID.timestamp], [OID.timestamptzArray, OID.timestamptz], [OID.timetzArray, OID.timetz], [OID.numericArray, OID.numeric], [OID.uuidArray, OID.uuid], [OID.jsonbArray, OID.jsonb]]);
|
|
288
|
+
const elementToArray = /*#__PURE__*/new Map(/*#__PURE__*/Array.from(arrayToElement, ([array, element]) => [element, array]));
|
|
289
|
+
/**
|
|
290
|
+
* Returns the array OID whose elements have the given OID, or `undefined`
|
|
291
|
+
* when there is no array type registered for it.
|
|
292
|
+
*
|
|
293
|
+
* @category getters
|
|
294
|
+
* @since 4.0.0
|
|
295
|
+
*/
|
|
296
|
+
export const arrayOidFor = elementOid => elementToArray.get(elementOid);
|
|
297
|
+
// -----------------------------------------------------------------------------
|
|
298
|
+
// calendar helpers
|
|
299
|
+
// -----------------------------------------------------------------------------
|
|
300
|
+
const pad = (value, size) => String(value).padStart(size, "0");
|
|
301
|
+
/** Civil date from days since 1970-01-01, after Howard Hinnant's algorithm. */
|
|
302
|
+
const civilFromDays = days => {
|
|
303
|
+
const z = days + 719468;
|
|
304
|
+
const era = Math.floor(z / 146097);
|
|
305
|
+
const doe = z - era * 146097;
|
|
306
|
+
const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);
|
|
307
|
+
const y = yoe + era * 400;
|
|
308
|
+
const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));
|
|
309
|
+
const mp = Math.floor((5 * doy + 2) / 153);
|
|
310
|
+
const day = doy - Math.floor((153 * mp + 2) / 5) + 1;
|
|
311
|
+
const month = mp < 10 ? mp + 3 : mp - 9;
|
|
312
|
+
return {
|
|
313
|
+
year: month <= 2 ? y + 1 : y,
|
|
314
|
+
month,
|
|
315
|
+
day
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
/** Days since 1970-01-01 from a civil date. */
|
|
319
|
+
const daysFromCivil = (year, month, day) => {
|
|
320
|
+
const y = month <= 2 ? year - 1 : year;
|
|
321
|
+
const era = Math.floor(y / 400);
|
|
322
|
+
const yoe = y - era * 400;
|
|
323
|
+
const mp = month > 2 ? month - 3 : month + 9;
|
|
324
|
+
const doy = Math.floor((153 * mp + 2) / 5) + day - 1;
|
|
325
|
+
const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
|
|
326
|
+
return era * 146097 + doe - 719468;
|
|
327
|
+
};
|
|
328
|
+
const monthLengths = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
329
|
+
const isLeapYear = year => year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
330
|
+
/** The two-digit number at `at`, or `-1` when those characters are not digits. */
|
|
331
|
+
const twoDigitsAt = (text, at) => {
|
|
332
|
+
const tens = text.charCodeAt(at) - 48;
|
|
333
|
+
const units = text.charCodeAt(at + 1) - 48;
|
|
334
|
+
return tens >= 0 && tens <= 9 && units >= 0 && units <= 9 ? tens * 10 + units : -1;
|
|
335
|
+
};
|
|
336
|
+
/** Parses `[-]YYYY-MM-DD`, with as many year digits as the caller likes. */
|
|
337
|
+
const parseDate = text => {
|
|
338
|
+
const yearStart = text.charCodeAt(0) === 45 ? 1 : 0;
|
|
339
|
+
const yearEnd = text.length - 6;
|
|
340
|
+
if (yearEnd - yearStart < 4 || text.charCodeAt(yearEnd) !== 45 || text.charCodeAt(yearEnd + 3) !== 45) {
|
|
341
|
+
return fail(`Expected a YYYY-MM-DD date, received "${text}"`);
|
|
342
|
+
}
|
|
343
|
+
let year = 0;
|
|
344
|
+
for (let index = yearStart; index < yearEnd; index++) {
|
|
345
|
+
const digit = text.charCodeAt(index) - 48;
|
|
346
|
+
if (digit < 0 || digit > 9) {
|
|
347
|
+
return fail(`Expected a YYYY-MM-DD date, received "${text}"`);
|
|
348
|
+
}
|
|
349
|
+
year = year * 10 + digit;
|
|
350
|
+
}
|
|
351
|
+
const month = twoDigitsAt(text, yearEnd + 1);
|
|
352
|
+
const day = twoDigitsAt(text, yearEnd + 4);
|
|
353
|
+
if (month === -1 || day === -1) {
|
|
354
|
+
return fail(`Expected a YYYY-MM-DD date, received "${text}"`);
|
|
355
|
+
}
|
|
356
|
+
if (yearStart === 1) year = -year;
|
|
357
|
+
// Enough digits and the year is Infinity, which `daysFromCivil` turns into a
|
|
358
|
+
// NaN the range check below would let through as a zero.
|
|
359
|
+
if (!Number.isFinite(year)) {
|
|
360
|
+
return fail(`date out of range: "${text}"`);
|
|
361
|
+
}
|
|
362
|
+
const monthLength = month === 2 && isLeapYear(year) ? 29 : monthLengths[month];
|
|
363
|
+
if (month < 1 || month > 12 || day < 1 || day > monthLength) {
|
|
364
|
+
return fail(`Invalid date "${text}"`);
|
|
365
|
+
}
|
|
366
|
+
return daysFromCivil(year, month, day);
|
|
367
|
+
};
|
|
368
|
+
const formatDate = days => {
|
|
369
|
+
const {
|
|
370
|
+
day,
|
|
371
|
+
month,
|
|
372
|
+
year
|
|
373
|
+
} = civilFromDays(days);
|
|
374
|
+
const yearText = year < 0 ? `-${pad(-year, 4)}` : year < 10000 ? twoDigits[year / 100 | 0] + twoDigits[year % 100] : String(year);
|
|
375
|
+
return `${yearText}-${twoDigits[month]}-${twoDigits[day]}`;
|
|
376
|
+
};
|
|
377
|
+
const timeRegex = /^(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,6}))?)?$/;
|
|
378
|
+
/** Microseconds since midnight. A day of them is well inside `Number`'s exact range. */
|
|
379
|
+
const parseTimeOfDay = text => {
|
|
380
|
+
const match = timeRegex.exec(text);
|
|
381
|
+
if (match === null) {
|
|
382
|
+
return fail(`Expected a HH:MM[:SS[.ffffff]] time, received "${text}"`);
|
|
383
|
+
}
|
|
384
|
+
const hours = Number(match[1]);
|
|
385
|
+
const minutes = Number(match[2]);
|
|
386
|
+
const seconds = match[3] === undefined ? 0 : Number(match[3]);
|
|
387
|
+
const fraction = match[4] === undefined ? 0 : Number(match[4].padEnd(6, "0"));
|
|
388
|
+
const micros = (hours * 3600 + minutes * 60 + seconds) * 1_000_000 + fraction;
|
|
389
|
+
if (hours > 24 || minutes > 59 || seconds > 60 || micros > MAX_TIME_MICROS_NUMBER) {
|
|
390
|
+
return fail(`Invalid time "${text}"`);
|
|
391
|
+
}
|
|
392
|
+
return micros;
|
|
393
|
+
};
|
|
394
|
+
const formatTimeOfDay = micros => {
|
|
395
|
+
const seconds = micros / 1_000_000 | 0;
|
|
396
|
+
const fraction = micros - seconds * 1_000_000;
|
|
397
|
+
const base = `${twoDigits[seconds / 3600 | 0]}:${twoDigits[(seconds / 60 | 0) % 60]}:${twoDigits[seconds % 60]}`;
|
|
398
|
+
if (fraction === 0) return base;
|
|
399
|
+
return `${base}.${pad(fraction, 6).replace(/0+$/, "")}`;
|
|
400
|
+
};
|
|
401
|
+
const zoneRegex = /^(?:Z|([+-])(\d{2})(?::?(\d{2}))?(?::?(\d{2}))?)$/;
|
|
402
|
+
/** Parses a trailing time zone into seconds east of UTC. */
|
|
403
|
+
const parseZone = text => {
|
|
404
|
+
const match = zoneRegex.exec(text);
|
|
405
|
+
if (match === null) {
|
|
406
|
+
return fail(`Expected a time zone offset, received "${text}"`);
|
|
407
|
+
}
|
|
408
|
+
if (match[1] === undefined) return 0;
|
|
409
|
+
const minutes = match[3] === undefined ? 0 : Number(match[3]);
|
|
410
|
+
const trailingSeconds = match[4] === undefined ? 0 : Number(match[4]);
|
|
411
|
+
const seconds = Number(match[2]) * 3600 + minutes * 60 + trailingSeconds;
|
|
412
|
+
if (minutes > 59 || trailingSeconds > 59 || seconds >= TZDISP_LIMIT_SECONDS) {
|
|
413
|
+
return fail(`Time zone offset out of range, received "${text}"`);
|
|
414
|
+
}
|
|
415
|
+
return match[1] === "-" ? -seconds : seconds;
|
|
416
|
+
};
|
|
417
|
+
const formatZone = secondsEast => {
|
|
418
|
+
const sign = secondsEast < 0 ? "-" : "+";
|
|
419
|
+
const total = Math.abs(secondsEast);
|
|
420
|
+
return `${sign}${pad(total / 3600 | 0, 2)}:${twoDigits[(total / 60 | 0) % 60]}`;
|
|
421
|
+
};
|
|
422
|
+
// -----------------------------------------------------------------------------
|
|
423
|
+
// numeric
|
|
424
|
+
// -----------------------------------------------------------------------------
|
|
425
|
+
const NUMERIC_POS = 0x0000;
|
|
426
|
+
const NUMERIC_NEG = 0x4000;
|
|
427
|
+
const NUMERIC_NAN = 0xc000;
|
|
428
|
+
const NUMERIC_PINF = 0xd000;
|
|
429
|
+
const NUMERIC_NINF = 0xf000;
|
|
430
|
+
const numericRegex = /^([+-])?(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/;
|
|
431
|
+
const encodeNumeric = value => {
|
|
432
|
+
const text = requireString(value, "numeric");
|
|
433
|
+
if (text === "NaN") return numericSpecial(NUMERIC_NAN);
|
|
434
|
+
if (text === "Infinity") return numericSpecial(NUMERIC_PINF);
|
|
435
|
+
if (text === "-Infinity") return numericSpecial(NUMERIC_NINF);
|
|
436
|
+
const match = numericRegex.exec(text);
|
|
437
|
+
if (match === null || (match[2] ?? "") + (match[3] ?? "") === "") {
|
|
438
|
+
return fail(`Expected a decimal numeric string, received "${text}"`);
|
|
439
|
+
}
|
|
440
|
+
const exponent = match[4] === undefined ? 0 : Number(match[4]);
|
|
441
|
+
let digits = (match[2] ?? "") + (match[3] ?? "");
|
|
442
|
+
let scale = (match[3] ?? "").length - exponent;
|
|
443
|
+
if (scale < 0) {
|
|
444
|
+
digits += "0".repeat(-scale);
|
|
445
|
+
scale = 0;
|
|
446
|
+
}
|
|
447
|
+
if (scale > 0x3fff) {
|
|
448
|
+
return fail(`numeric scale ${scale} exceeds the PostgreSQL maximum`);
|
|
449
|
+
}
|
|
450
|
+
const rightPad = (4 - scale % 4) % 4;
|
|
451
|
+
digits += "0".repeat(rightPad);
|
|
452
|
+
const fractionLength = scale + rightPad;
|
|
453
|
+
const leftPad = (4 - (digits.length - fractionLength) % 4) % 4;
|
|
454
|
+
digits = "0".repeat(leftPad) + digits;
|
|
455
|
+
// Groups are read out of `digits` on demand rather than collected, so
|
|
456
|
+
// trimming the leading and trailing zero groups is two moving indices
|
|
457
|
+
// instead of an array and a `shift` per leading zero.
|
|
458
|
+
const groupAt = index => {
|
|
459
|
+
const at = index * 4;
|
|
460
|
+
return (digits.charCodeAt(at) - 48) * 1000 + (digits.charCodeAt(at + 1) - 48) * 100 + (digits.charCodeAt(at + 2) - 48) * 10 + (digits.charCodeAt(at + 3) - 48);
|
|
461
|
+
};
|
|
462
|
+
const groupCount = digits.length / 4;
|
|
463
|
+
let first = 0;
|
|
464
|
+
while (first < groupCount && groupAt(first) === 0) first++;
|
|
465
|
+
let last = groupCount;
|
|
466
|
+
while (last > first && groupAt(last - 1) === 0) last--;
|
|
467
|
+
const count = last - first;
|
|
468
|
+
const weight = count === 0 ? 0 : (digits.length - fractionLength) / 4 - 1 - first;
|
|
469
|
+
const sign = count > 0 && match[1] === "-" ? NUMERIC_NEG : NUMERIC_POS;
|
|
470
|
+
const bytes = new Uint8Array(8 + count * 2);
|
|
471
|
+
writeInt16(bytes, 0, count);
|
|
472
|
+
writeInt16(bytes, 2, weight);
|
|
473
|
+
writeInt16(bytes, 4, sign);
|
|
474
|
+
writeInt16(bytes, 6, scale);
|
|
475
|
+
for (let i = 0; i < count; i++) {
|
|
476
|
+
writeInt16(bytes, 8 + i * 2, groupAt(first + i));
|
|
477
|
+
}
|
|
478
|
+
return bytes;
|
|
479
|
+
};
|
|
480
|
+
const numericSpecial = sign => {
|
|
481
|
+
const bytes = new Uint8Array(8);
|
|
482
|
+
writeInt16(bytes, 4, sign);
|
|
483
|
+
return bytes;
|
|
484
|
+
};
|
|
485
|
+
const decodeNumeric = (bytes, offset, size) => {
|
|
486
|
+
if (size < 8) return fail("Truncated numeric value");
|
|
487
|
+
const count = readInt16(bytes, offset);
|
|
488
|
+
const weight = readInt16(bytes, offset + 2);
|
|
489
|
+
const sign = readUint16(bytes, offset + 4);
|
|
490
|
+
const scale = readInt16(bytes, offset + 6);
|
|
491
|
+
if (sign === NUMERIC_NAN) return "NaN";
|
|
492
|
+
if (sign === NUMERIC_PINF) return "Infinity";
|
|
493
|
+
if (sign === NUMERIC_NINF) return "-Infinity";
|
|
494
|
+
if (sign !== NUMERIC_POS && sign !== NUMERIC_NEG) {
|
|
495
|
+
return fail(`Invalid numeric sign: ${sign}`);
|
|
496
|
+
}
|
|
497
|
+
requireSize(size, 8 + count * 2, "numeric");
|
|
498
|
+
const digits = offset + 8;
|
|
499
|
+
// Groups outside 0-9999 have no four-digit text, and reading past the ones
|
|
500
|
+
// the value carries is how the fractional part is padded out.
|
|
501
|
+
const digitAt = index => {
|
|
502
|
+
if (index < 0 || index >= count) return 0;
|
|
503
|
+
const digit = readInt16(bytes, digits + index * 2);
|
|
504
|
+
return digit >= 0 && digit <= 9999 ? digit : fail(`Invalid numeric digit: ${digit}`);
|
|
505
|
+
};
|
|
506
|
+
let result = sign === NUMERIC_NEG ? "-" : "";
|
|
507
|
+
if (weight < 0) {
|
|
508
|
+
result += "0";
|
|
509
|
+
} else {
|
|
510
|
+
for (let i = 0; i <= weight; i++) {
|
|
511
|
+
const digit = digitAt(i);
|
|
512
|
+
if (i === 0) {
|
|
513
|
+
result += String(digit);
|
|
514
|
+
} else {
|
|
515
|
+
result += twoDigits[digit / 100 | 0];
|
|
516
|
+
result += twoDigits[digit % 100];
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (scale > 0) {
|
|
521
|
+
let fraction = "";
|
|
522
|
+
for (let i = weight + 1; fraction.length < scale; i++) {
|
|
523
|
+
const digit = digitAt(i);
|
|
524
|
+
fraction += twoDigits[digit / 100 | 0];
|
|
525
|
+
fraction += twoDigits[digit % 100];
|
|
526
|
+
}
|
|
527
|
+
result += `.${fraction.slice(0, scale)}`;
|
|
528
|
+
}
|
|
529
|
+
return result;
|
|
530
|
+
};
|
|
531
|
+
// -----------------------------------------------------------------------------
|
|
532
|
+
// network addresses
|
|
533
|
+
// -----------------------------------------------------------------------------
|
|
534
|
+
const PGSQL_AF_INET = 2;
|
|
535
|
+
const PGSQL_AF_INET6 = 3;
|
|
536
|
+
const parseIPv4 = text => {
|
|
537
|
+
const parts = text.split(".");
|
|
538
|
+
if (parts.length !== 4) return undefined;
|
|
539
|
+
const bytes = new Uint8Array(4);
|
|
540
|
+
for (let i = 0; i < 4; i++) {
|
|
541
|
+
if (!/^\d{1,3}$/.test(parts[i])) return undefined;
|
|
542
|
+
const value = Number(parts[i]);
|
|
543
|
+
if (value > 255) return undefined;
|
|
544
|
+
bytes[i] = value;
|
|
545
|
+
}
|
|
546
|
+
return bytes;
|
|
547
|
+
};
|
|
548
|
+
const parseIPv6 = text => {
|
|
549
|
+
const halves = text.split("::");
|
|
550
|
+
if (halves.length > 2) return undefined;
|
|
551
|
+
const toGroups = part => part === "" ? [] : part.split(":");
|
|
552
|
+
const head = toGroups(halves[0]);
|
|
553
|
+
const tail = halves.length === 2 ? toGroups(halves[1]) : [];
|
|
554
|
+
const bytes = new Uint8Array(16);
|
|
555
|
+
const trailing = tail.length > 0 ? tail[tail.length - 1] : head.length > 0 ? head[head.length - 1] : "";
|
|
556
|
+
const embedded = trailing.includes(".") ? parseIPv4(trailing) : undefined;
|
|
557
|
+
if (trailing.includes(".") && embedded === undefined) return undefined;
|
|
558
|
+
if (embedded !== undefined) {
|
|
559
|
+
if (tail.length > 0) tail.pop();else head.pop();
|
|
560
|
+
}
|
|
561
|
+
const groupCount = embedded === undefined ? 8 : 6;
|
|
562
|
+
if (head.length + tail.length > groupCount) return undefined;
|
|
563
|
+
if (halves.length === 1 && head.length !== groupCount) return undefined;
|
|
564
|
+
const writeGroup = (group, offset) => {
|
|
565
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(group)) return false;
|
|
566
|
+
const value = Number.parseInt(group, 16);
|
|
567
|
+
bytes[offset] = value >> 8;
|
|
568
|
+
bytes[offset + 1] = value & 0xff;
|
|
569
|
+
return true;
|
|
570
|
+
};
|
|
571
|
+
for (let i = 0; i < head.length; i++) {
|
|
572
|
+
if (!writeGroup(head[i], i * 2)) return undefined;
|
|
573
|
+
}
|
|
574
|
+
for (let i = 0; i < tail.length; i++) {
|
|
575
|
+
if (!writeGroup(tail[i], (groupCount - tail.length + i) * 2)) return undefined;
|
|
576
|
+
}
|
|
577
|
+
if (embedded !== undefined) bytes.set(embedded, 12);
|
|
578
|
+
return bytes;
|
|
579
|
+
};
|
|
580
|
+
const formatIPv4 = (bytes, offset) => `${bytes[offset]}.${bytes[offset + 1]}.${bytes[offset + 2]}.${bytes[offset + 3]}`;
|
|
581
|
+
const formatIPv6 = (bytes, offset) => {
|
|
582
|
+
let isV4Mapped = bytes[offset + 10] === 0xff && bytes[offset + 11] === 0xff;
|
|
583
|
+
for (let i = 0; isV4Mapped && i < 10; i++) isV4Mapped = bytes[offset + i] === 0;
|
|
584
|
+
if (isV4Mapped) return `::ffff:${formatIPv4(bytes, offset + 12)}`;
|
|
585
|
+
const groups = [];
|
|
586
|
+
for (let i = 0; i < 8; i++) groups.push(readUint16(bytes, offset + i * 2));
|
|
587
|
+
let bestStart = -1;
|
|
588
|
+
let bestLength = 0;
|
|
589
|
+
let start = -1;
|
|
590
|
+
for (let i = 0; i <= 8; i++) {
|
|
591
|
+
if (i < 8 && groups[i] === 0) {
|
|
592
|
+
if (start === -1) start = i;
|
|
593
|
+
} else if (start !== -1) {
|
|
594
|
+
if (i - start > bestLength) {
|
|
595
|
+
bestStart = start;
|
|
596
|
+
bestLength = i - start;
|
|
597
|
+
}
|
|
598
|
+
start = -1;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (bestLength < 2) {
|
|
602
|
+
return groups.map(group => group.toString(16)).join(":");
|
|
603
|
+
}
|
|
604
|
+
const head = groups.slice(0, bestStart).map(group => group.toString(16)).join(":");
|
|
605
|
+
const tail = groups.slice(bestStart + bestLength).map(group => group.toString(16)).join(":");
|
|
606
|
+
return `${head}::${tail}`;
|
|
607
|
+
};
|
|
608
|
+
const hasHostBits = (bytes, offset, length, bits) => {
|
|
609
|
+
const wholeBytes = Math.floor(bits / 8);
|
|
610
|
+
const partialBits = bits % 8;
|
|
611
|
+
if (partialBits !== 0 && (bytes[offset + wholeBytes] & (1 << 8 - partialBits) - 1) !== 0) {
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
for (let index = wholeBytes + (partialBits === 0 ? 0 : 1); index < length; index++) {
|
|
615
|
+
if (bytes[offset + index] !== 0) return true;
|
|
616
|
+
}
|
|
617
|
+
return false;
|
|
618
|
+
};
|
|
619
|
+
const encodeInet = (value, isCidr) => {
|
|
620
|
+
const text = requireString(value, isCidr ? "cidr" : "inet");
|
|
621
|
+
const slash = text.lastIndexOf("/");
|
|
622
|
+
const address = slash === -1 ? text : text.slice(0, slash);
|
|
623
|
+
const v4 = parseIPv4(address);
|
|
624
|
+
const bytes = v4 ?? parseIPv6(address);
|
|
625
|
+
if (bytes === undefined) {
|
|
626
|
+
return fail(`Expected an IP address, received "${text}"`);
|
|
627
|
+
}
|
|
628
|
+
const fullBits = bytes.length * 8;
|
|
629
|
+
const bits = slash === -1 ? fullBits : Number(text.slice(slash + 1));
|
|
630
|
+
if (!Number.isInteger(bits) || bits < 0 || bits > fullBits) {
|
|
631
|
+
return fail(`Invalid netmask length in "${text}"`);
|
|
632
|
+
}
|
|
633
|
+
if (isCidr && hasHostBits(bytes, 0, bytes.length, bits)) {
|
|
634
|
+
return fail(`CIDR address has host bits set in "${text}"`);
|
|
635
|
+
}
|
|
636
|
+
const result = new Uint8Array(4 + bytes.length);
|
|
637
|
+
result[0] = v4 === undefined ? PGSQL_AF_INET6 : PGSQL_AF_INET;
|
|
638
|
+
result[1] = bits;
|
|
639
|
+
result[2] = isCidr ? 1 : 0;
|
|
640
|
+
result[3] = bytes.length;
|
|
641
|
+
result.set(bytes, 4);
|
|
642
|
+
return result;
|
|
643
|
+
};
|
|
644
|
+
const decodeInet = (bytes, offset, size) => {
|
|
645
|
+
if (size < 4) return fail("Truncated inet value");
|
|
646
|
+
const family = bytes[offset];
|
|
647
|
+
const bits = bytes[offset + 1];
|
|
648
|
+
const cidrFlag = bytes[offset + 2];
|
|
649
|
+
if (cidrFlag > 1) return fail(`Invalid inet CIDR flag: ${cidrFlag}`);
|
|
650
|
+
const isCidr = cidrFlag === 1;
|
|
651
|
+
const addressSize = bytes[offset + 3];
|
|
652
|
+
const expected = family === PGSQL_AF_INET ? 4 : family === PGSQL_AF_INET6 ? 16 : -1;
|
|
653
|
+
if (expected === -1 || addressSize !== expected) {
|
|
654
|
+
return fail(`Invalid inet address family ${family} with ${addressSize} byte(s)`);
|
|
655
|
+
}
|
|
656
|
+
requireSize(size, 4 + addressSize, "inet");
|
|
657
|
+
if (bits > addressSize * 8) return fail(`Invalid inet netmask length: ${bits}`);
|
|
658
|
+
if (isCidr && hasHostBits(bytes, offset + 4, addressSize, bits)) {
|
|
659
|
+
return fail("CIDR address has host bits set");
|
|
660
|
+
}
|
|
661
|
+
const text = family === PGSQL_AF_INET ? formatIPv4(bytes, offset + 4) : formatIPv6(bytes, offset + 4);
|
|
662
|
+
return isCidr || bits !== addressSize * 8 ? `${text}/${bits}` : text;
|
|
663
|
+
};
|
|
664
|
+
// -----------------------------------------------------------------------------
|
|
665
|
+
// uuid
|
|
666
|
+
// -----------------------------------------------------------------------------
|
|
667
|
+
const hexDigits = "0123456789abcdef";
|
|
668
|
+
/** Byte value of each hex character code, `-1` for everything else. */
|
|
669
|
+
const hexValues = /*#__PURE__*/new Int8Array(128).fill(-1);
|
|
670
|
+
for (let i = 0; i < 16; i++) {
|
|
671
|
+
hexValues[/*#__PURE__*/hexDigits.charCodeAt(i)] = i;
|
|
672
|
+
hexValues[/*#__PURE__*/"0123456789ABCDEF".charCodeAt(i)] = i;
|
|
673
|
+
}
|
|
674
|
+
/** Character codes of each byte value's two hex digits. */
|
|
675
|
+
const hexHigh = /* @__PURE__ */(() => {
|
|
676
|
+
const codes = /*#__PURE__*/new Uint8Array(256);
|
|
677
|
+
for (let i = 0; i < 256; i++) codes[i] = /*#__PURE__*/hexDigits.charCodeAt(i >> 4);
|
|
678
|
+
return codes;
|
|
679
|
+
})();
|
|
680
|
+
const hexLow = /* @__PURE__ */(() => {
|
|
681
|
+
const codes = /*#__PURE__*/new Uint8Array(256);
|
|
682
|
+
for (let i = 0; i < 256; i++) codes[i] = /*#__PURE__*/hexDigits.charCodeAt(i & 0xf);
|
|
683
|
+
return codes;
|
|
684
|
+
})();
|
|
685
|
+
/** Offsets of the 16 uuid bytes within the 36-character hyphenated text. */
|
|
686
|
+
const uuidOffsets = [0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34];
|
|
687
|
+
/** The hex digit at `at`, or `-1` when that character is not one. */
|
|
688
|
+
const hexAt = (text, at) => {
|
|
689
|
+
const code = text.charCodeAt(at);
|
|
690
|
+
return code < 128 ? hexValues[code] : -1;
|
|
691
|
+
};
|
|
692
|
+
/**
|
|
693
|
+
* The hyphen positions do the validating that a regular expression used to:
|
|
694
|
+
* anything else in the 36 characters has to be a hex digit for the pairs to
|
|
695
|
+
* come out, and `hexAt` reports the ones that are not.
|
|
696
|
+
*/
|
|
697
|
+
/** The four int32 words of the uuid `uuidWords` last parsed. */
|
|
698
|
+
const uuidWord = /*#__PURE__*/new Int32Array(4);
|
|
699
|
+
/**
|
|
700
|
+
* Parses the hyphenated text into `uuidWord`. Both encoding paths read the
|
|
701
|
+
* words from there rather than from a returned array, so neither allocates.
|
|
702
|
+
*/
|
|
703
|
+
const uuidWords = value => {
|
|
704
|
+
const text = requireString(value, "uuid");
|
|
705
|
+
if (text.length !== 36 || text.charCodeAt(8) !== 45 || text.charCodeAt(13) !== 45 || text.charCodeAt(18) !== 45 || text.charCodeAt(23) !== 45) {
|
|
706
|
+
return fail(`Expected a UUID, received "${text}"`);
|
|
707
|
+
}
|
|
708
|
+
for (let word = 0; word < 4; word++) {
|
|
709
|
+
let bits = 0;
|
|
710
|
+
let valid = 0;
|
|
711
|
+
for (let i = word * 4; i < word * 4 + 4; i++) {
|
|
712
|
+
const at = uuidOffsets[i];
|
|
713
|
+
const high = hexAt(text, at);
|
|
714
|
+
const low = hexAt(text, at + 1);
|
|
715
|
+
valid |= high | low;
|
|
716
|
+
bits = bits << 8 | high << 4 | low;
|
|
717
|
+
}
|
|
718
|
+
if (valid < 0) {
|
|
719
|
+
return fail(`Expected a UUID, received "${text}"`);
|
|
720
|
+
}
|
|
721
|
+
uuidWord[word] = bits;
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
const encodeUuid = value => {
|
|
725
|
+
uuidWords(value);
|
|
726
|
+
const bytes = new Uint8Array(16);
|
|
727
|
+
writeInt32(bytes, 0, uuidWord[0]);
|
|
728
|
+
writeInt32(bytes, 4, uuidWord[1]);
|
|
729
|
+
writeInt32(bytes, 8, uuidWord[2]);
|
|
730
|
+
writeInt32(bytes, 12, uuidWord[3]);
|
|
731
|
+
return bytes;
|
|
732
|
+
};
|
|
733
|
+
// Four int32s are the sixteen bytes, so a uuid parameter needs no array of
|
|
734
|
+
// its own on the way into a frame.
|
|
735
|
+
const writeUuid = (sink, value) => {
|
|
736
|
+
uuidWords(value);
|
|
737
|
+
sink.int32(uuidWord[0]);
|
|
738
|
+
sink.int32(uuidWord[1]);
|
|
739
|
+
sink.int32(uuidWord[2]);
|
|
740
|
+
sink.int32(uuidWord[3]);
|
|
741
|
+
};
|
|
742
|
+
// One `String.fromCharCode` call, so the result is a flat string rather than
|
|
743
|
+
// a rope of pair concatenations that its first reader has to flatten. 45 is
|
|
744
|
+
// the hyphen.
|
|
745
|
+
const decodeUuid = (bytes, offset, size) => {
|
|
746
|
+
requireSize(size, 16, "uuid");
|
|
747
|
+
const b0 = bytes[offset];
|
|
748
|
+
const b1 = bytes[offset + 1];
|
|
749
|
+
const b2 = bytes[offset + 2];
|
|
750
|
+
const b3 = bytes[offset + 3];
|
|
751
|
+
const b4 = bytes[offset + 4];
|
|
752
|
+
const b5 = bytes[offset + 5];
|
|
753
|
+
const b6 = bytes[offset + 6];
|
|
754
|
+
const b7 = bytes[offset + 7];
|
|
755
|
+
const b8 = bytes[offset + 8];
|
|
756
|
+
const b9 = bytes[offset + 9];
|
|
757
|
+
const b10 = bytes[offset + 10];
|
|
758
|
+
const b11 = bytes[offset + 11];
|
|
759
|
+
const b12 = bytes[offset + 12];
|
|
760
|
+
const b13 = bytes[offset + 13];
|
|
761
|
+
const b14 = bytes[offset + 14];
|
|
762
|
+
const b15 = bytes[offset + 15];
|
|
763
|
+
return String.fromCharCode(hexHigh[b0], hexLow[b0], hexHigh[b1], hexLow[b1], hexHigh[b2], hexLow[b2], hexHigh[b3], hexLow[b3], 45, hexHigh[b4], hexLow[b4], hexHigh[b5], hexLow[b5], 45, hexHigh[b6], hexLow[b6], hexHigh[b7], hexLow[b7], 45, hexHigh[b8], hexLow[b8], hexHigh[b9], hexLow[b9], 45, hexHigh[b10], hexLow[b10], hexHigh[b11], hexLow[b11], hexHigh[b12], hexLow[b12], hexHigh[b13], hexLow[b13], hexHigh[b14], hexLow[b14], hexHigh[b15], hexLow[b15]);
|
|
764
|
+
};
|
|
765
|
+
/** A codec whose `decode` is its `read` over the whole of its bytes. */
|
|
766
|
+
const codecOf = (read, encode, write) => {
|
|
767
|
+
const decode = bytes => read(bytes, 0, bytes.length);
|
|
768
|
+
return write === undefined ? {
|
|
769
|
+
encode,
|
|
770
|
+
decode,
|
|
771
|
+
read
|
|
772
|
+
} : {
|
|
773
|
+
encode,
|
|
774
|
+
decode,
|
|
775
|
+
read,
|
|
776
|
+
write
|
|
777
|
+
};
|
|
778
|
+
};
|
|
779
|
+
/** A fresh copy of the first four staged bytes; `set` costs more than the stores. */
|
|
780
|
+
const takeScratch4 = () => {
|
|
781
|
+
const bytes = new Uint8Array(4);
|
|
782
|
+
bytes[0] = scratchBytes4[0];
|
|
783
|
+
bytes[1] = scratchBytes4[1];
|
|
784
|
+
bytes[2] = scratchBytes4[2];
|
|
785
|
+
bytes[3] = scratchBytes4[3];
|
|
786
|
+
return bytes;
|
|
787
|
+
};
|
|
788
|
+
const takeScratch8 = () => {
|
|
789
|
+
const bytes = new Uint8Array(8);
|
|
790
|
+
bytes[0] = scratchBytes8[0];
|
|
791
|
+
bytes[1] = scratchBytes8[1];
|
|
792
|
+
bytes[2] = scratchBytes8[2];
|
|
793
|
+
bytes[3] = scratchBytes8[3];
|
|
794
|
+
bytes[4] = scratchBytes8[4];
|
|
795
|
+
bytes[5] = scratchBytes8[5];
|
|
796
|
+
bytes[6] = scratchBytes8[6];
|
|
797
|
+
bytes[7] = scratchBytes8[7];
|
|
798
|
+
return bytes;
|
|
799
|
+
};
|
|
800
|
+
const utf8Codec = /*#__PURE__*/codecOf(decodeUtf8, value => encodeUtf8(requireString(value, "text")), (sink, value) => sink.utf8(requireString(value, "text")));
|
|
801
|
+
const int8Value = value => {
|
|
802
|
+
const big = requireBigInt(value, "int8");
|
|
803
|
+
if (big < INT64_MIN || big > INT64_MAX) fail(`int8 out of range: ${big}`);
|
|
804
|
+
return big;
|
|
805
|
+
};
|
|
806
|
+
const MAX_TIME_MICROS = /*#__PURE__*/BigInt(MAX_TIME_MICROS_NUMBER);
|
|
807
|
+
const timeValue = value => {
|
|
808
|
+
const micros = requireBigInt(value, "time");
|
|
809
|
+
if (micros < ZERO || micros > MAX_TIME_MICROS) fail(`time out of range: ${micros}`);
|
|
810
|
+
return micros;
|
|
811
|
+
};
|
|
812
|
+
/**
|
|
813
|
+
* Microseconds since midnight, which always fit a `Number` exactly. Formatting
|
|
814
|
+
* one assumes that, so a value the type cannot hold is an error rather than a
|
|
815
|
+
* nonsense time.
|
|
816
|
+
*/
|
|
817
|
+
const readTimeMicros = (bytes, offset) => {
|
|
818
|
+
const high = readInt32(bytes, offset);
|
|
819
|
+
const micros = high * 4294967296 + readUint32(bytes, offset + 4);
|
|
820
|
+
if (high < 0 || micros > MAX_TIME_MICROS_NUMBER) {
|
|
821
|
+
return fail(`timetz out of range: ${micros}`);
|
|
822
|
+
}
|
|
823
|
+
return micros;
|
|
824
|
+
};
|
|
825
|
+
/** The two halves of the int64 `timestampInt64` last produced. */
|
|
826
|
+
let timestampHigh = 0;
|
|
827
|
+
let timestampLow = 0;
|
|
828
|
+
/**
|
|
829
|
+
* Converts epoch milliseconds to the halves of the wire int64. Both encoding
|
|
830
|
+
* paths read them from here rather than from a returned pair, so neither
|
|
831
|
+
* allocates.
|
|
832
|
+
*/
|
|
833
|
+
const timestampInt64 = value => {
|
|
834
|
+
const ms = requireNumber(value, "timestamp");
|
|
835
|
+
if (Number.isNaN(ms)) {
|
|
836
|
+
fail("timestamp cannot be NaN");
|
|
837
|
+
} else if (ms === Number.POSITIVE_INFINITY) {
|
|
838
|
+
timestampHigh = INT32_MAX;
|
|
839
|
+
timestampLow = -1;
|
|
840
|
+
} else if (ms === Number.NEGATIVE_INFINITY) {
|
|
841
|
+
timestampHigh = INT32_MIN;
|
|
842
|
+
timestampLow = 0;
|
|
843
|
+
} else {
|
|
844
|
+
const unixMicros = Math.trunc(ms * 1000);
|
|
845
|
+
if (!Number.isFinite(unixMicros)) {
|
|
846
|
+
fail(`timestamp out of range: ${ms}`);
|
|
847
|
+
}
|
|
848
|
+
const micros = unixMicros - PG_EPOCH_MICROS;
|
|
849
|
+
if (micros > -MAX_EXACT && micros < MAX_EXACT) {
|
|
850
|
+
timestampHigh = Math.floor(micros / 4294967296);
|
|
851
|
+
timestampLow = micros - timestampHigh * 4294967296;
|
|
852
|
+
} else {
|
|
853
|
+
const exact = BigInt(unixMicros) - BigInt(PG_EPOCH_MICROS);
|
|
854
|
+
if (exact < INT64_MIN || exact > INT64_MAX) {
|
|
855
|
+
fail(`timestamp out of range: ${ms}`);
|
|
856
|
+
}
|
|
857
|
+
scratchView8.setBigInt64(0, exact);
|
|
858
|
+
timestampHigh = scratchView8.getInt32(0);
|
|
859
|
+
timestampLow = scratchView8.getInt32(4);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
const timestampCodec = /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
864
|
+
requireSize(size, 8, "timestamp");
|
|
865
|
+
const high = readInt32(bytes, offset);
|
|
866
|
+
if (high >= -MAX_EXACT_HIGH && high < MAX_EXACT_HIGH) {
|
|
867
|
+
// Inside these bounds the whole conversion is float arithmetic, so it
|
|
868
|
+
// allocates no BigInt. Everything outside them, the sentinels included,
|
|
869
|
+
// needs the exact 64-bit value.
|
|
870
|
+
const micros = high * 4294967296 + readUint32(bytes, offset + 4);
|
|
871
|
+
return (micros - micros % 1000) / 1000 + PG_EPOCH_MS;
|
|
872
|
+
}
|
|
873
|
+
stage8(bytes, offset);
|
|
874
|
+
const micros = scratchView8.getBigInt64(0);
|
|
875
|
+
if (micros === INT64_MAX) return Number.POSITIVE_INFINITY;
|
|
876
|
+
if (micros === INT64_MIN) return Number.NEGATIVE_INFINITY;
|
|
877
|
+
return Number(micros / THOUSAND) + PG_EPOCH_MS;
|
|
878
|
+
}, value => {
|
|
879
|
+
timestampInt64(value);
|
|
880
|
+
const bytes = new Uint8Array(8);
|
|
881
|
+
writeInt32(bytes, 0, timestampHigh);
|
|
882
|
+
writeInt32(bytes, 4, timestampLow);
|
|
883
|
+
return bytes;
|
|
884
|
+
},
|
|
885
|
+
// Two int32s are the int64, so the sink needs nothing of its own for it.
|
|
886
|
+
(sink, value) => {
|
|
887
|
+
timestampInt64(value);
|
|
888
|
+
sink.int32(timestampHigh);
|
|
889
|
+
sink.int32(timestampLow);
|
|
890
|
+
});
|
|
891
|
+
/** Days since the PostgreSQL epoch, or an infinity sentinel. */
|
|
892
|
+
const dateDays = value => {
|
|
893
|
+
const text = requireString(value, "date");
|
|
894
|
+
if (text === "infinity") return INT32_MAX;
|
|
895
|
+
if (text === "-infinity") return INT32_MIN;
|
|
896
|
+
const days = parseDate(text) - PG_EPOCH_DAYS;
|
|
897
|
+
if (days <= INT32_MIN || days >= INT32_MAX) {
|
|
898
|
+
return fail(`date out of range: "${text}"`);
|
|
899
|
+
}
|
|
900
|
+
return days;
|
|
901
|
+
};
|
|
902
|
+
const dateCodec = /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
903
|
+
requireSize(size, 4, "date");
|
|
904
|
+
const days = readInt32(bytes, offset);
|
|
905
|
+
if (days === INT32_MAX) return "infinity";
|
|
906
|
+
if (days === INT32_MIN) return "-infinity";
|
|
907
|
+
return formatDate(days + PG_EPOCH_DAYS);
|
|
908
|
+
}, value => {
|
|
909
|
+
const bytes = new Uint8Array(4);
|
|
910
|
+
writeInt32(bytes, 0, dateDays(value));
|
|
911
|
+
return bytes;
|
|
912
|
+
}, (sink, value) => sink.int32(dateDays(value)));
|
|
913
|
+
const timetzCodec = /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
914
|
+
requireSize(size, 12, "timetz");
|
|
915
|
+
const zone = readInt32(bytes, offset + 8);
|
|
916
|
+
if (zone <= -TZDISP_LIMIT_SECONDS || zone >= TZDISP_LIMIT_SECONDS) {
|
|
917
|
+
return fail(`timetz time zone displacement out of range: ${zone}`);
|
|
918
|
+
}
|
|
919
|
+
return formatTimeOfDay(readTimeMicros(bytes, offset)) + formatZone(-zone);
|
|
920
|
+
}, value => {
|
|
921
|
+
const text = requireString(value, "timetz");
|
|
922
|
+
const split = Math.max(text.lastIndexOf("+"), text.lastIndexOf("-"), text.lastIndexOf("Z"));
|
|
923
|
+
if (split <= 0) {
|
|
924
|
+
return fail(`Expected a timetz with a zone offset, received "${text}"`);
|
|
925
|
+
}
|
|
926
|
+
const bytes = new Uint8Array(12);
|
|
927
|
+
writeInt64(bytes, 0, parseTimeOfDay(text.slice(0, split)));
|
|
928
|
+
writeInt32(bytes, 8, -parseZone(text.slice(split)));
|
|
929
|
+
return bytes;
|
|
930
|
+
});
|
|
931
|
+
const jsonCodec = /*#__PURE__*/codecOf((bytes, offset, size) => JSON.parse(decodeUtf8(bytes, offset, size)), value => {
|
|
932
|
+
const text = JSON.stringify(value);
|
|
933
|
+
if (text === undefined) return fail("Value cannot be serialised as JSON");
|
|
934
|
+
return encodeUtf8(text);
|
|
935
|
+
}, (sink, value) => {
|
|
936
|
+
const text = JSON.stringify(value);
|
|
937
|
+
if (text === undefined) return fail("Value cannot be serialised as JSON");
|
|
938
|
+
sink.utf8(text);
|
|
939
|
+
});
|
|
940
|
+
const jsonbCodec = /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
941
|
+
if (size === 0 || bytes[offset] !== 1) {
|
|
942
|
+
return fail("Unsupported jsonb version byte");
|
|
943
|
+
}
|
|
944
|
+
return JSON.parse(decodeUtf8(bytes, offset + 1, size - 1));
|
|
945
|
+
}, value => {
|
|
946
|
+
const text = JSON.stringify(value);
|
|
947
|
+
if (text === undefined) return fail("Value cannot be serialised as JSON");
|
|
948
|
+
const bytes = encodeUtf8Prefixed(text, 1);
|
|
949
|
+
bytes[0] = 1;
|
|
950
|
+
return bytes;
|
|
951
|
+
}, (sink, value) => {
|
|
952
|
+
const text = JSON.stringify(value);
|
|
953
|
+
if (text === undefined) return fail("Value cannot be serialised as JSON");
|
|
954
|
+
sink.uint8(1);
|
|
955
|
+
sink.utf8(text);
|
|
956
|
+
});
|
|
957
|
+
const builtins = /*#__PURE__*/new Map([[OID.bool, /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
958
|
+
requireSize(size, 1, "bool");
|
|
959
|
+
return bytes[offset] !== 0;
|
|
960
|
+
}, value => {
|
|
961
|
+
if (typeof value !== "boolean") fail("Expected a boolean for bool");
|
|
962
|
+
const bytes = new Uint8Array(1);
|
|
963
|
+
bytes[0] = value ? 1 : 0;
|
|
964
|
+
return bytes;
|
|
965
|
+
}, (sink, value) => {
|
|
966
|
+
if (typeof value !== "boolean") fail("Expected a boolean for bool");
|
|
967
|
+
sink.uint8(value ? 1 : 0);
|
|
968
|
+
})], [OID.bytea, /*#__PURE__*/codecOf(region, value => value instanceof Uint8Array ? value.slice() : fail("Expected a Uint8Array for bytea"), (sink, value) => value instanceof Uint8Array ? sink.raw(value) : fail("Expected a Uint8Array for bytea"))], [OID.int2, /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
969
|
+
requireSize(size, 2, "int2");
|
|
970
|
+
return (bytes[offset] << 8 | bytes[offset + 1]) << 16 >> 16;
|
|
971
|
+
}, value => {
|
|
972
|
+
const num = requireInteger(value, "int2", -32768, 32767);
|
|
973
|
+
const bytes = new Uint8Array(2);
|
|
974
|
+
bytes[0] = num >>> 8;
|
|
975
|
+
bytes[1] = num;
|
|
976
|
+
return bytes;
|
|
977
|
+
}, (sink, value) => sink.int16(requireInteger(value, "int2", -32768, 32767)))], [OID.int4, /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
978
|
+
requireSize(size, 4, "int4");
|
|
979
|
+
return readInt32(bytes, offset);
|
|
980
|
+
}, value => {
|
|
981
|
+
const num = requireInteger(value, "int4", INT32_MIN, INT32_MAX);
|
|
982
|
+
const bytes = new Uint8Array(4);
|
|
983
|
+
bytes[0] = num >>> 24;
|
|
984
|
+
bytes[1] = num >>> 16;
|
|
985
|
+
bytes[2] = num >>> 8;
|
|
986
|
+
bytes[3] = num;
|
|
987
|
+
return bytes;
|
|
988
|
+
}, (sink, value) => sink.int32(requireInteger(value, "int4", INT32_MIN, INT32_MAX)))], [OID.oid, /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
989
|
+
requireSize(size, 4, "oid");
|
|
990
|
+
return readUint32(bytes, offset);
|
|
991
|
+
}, value => {
|
|
992
|
+
const num = requireInteger(value, "oid", 0, 4294967295);
|
|
993
|
+
const bytes = new Uint8Array(4);
|
|
994
|
+
bytes[0] = num >>> 24;
|
|
995
|
+
bytes[1] = num >>> 16;
|
|
996
|
+
bytes[2] = num >>> 8;
|
|
997
|
+
bytes[3] = num;
|
|
998
|
+
return bytes;
|
|
999
|
+
}, (sink, value) => sink.int32(requireInteger(value, "oid", 0, 4294967295)))], [OID.int8, /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
1000
|
+
requireSize(size, 8, "int8");
|
|
1001
|
+
stage8(bytes, offset);
|
|
1002
|
+
return scratchView8.getBigInt64(0);
|
|
1003
|
+
}, value => {
|
|
1004
|
+
scratchView8.setBigInt64(0, int8Value(value));
|
|
1005
|
+
return takeScratch8();
|
|
1006
|
+
}, (sink, value) => sink.bigInt64(int8Value(value)))], [OID.float4, /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
1007
|
+
requireSize(size, 4, "float4");
|
|
1008
|
+
scratchView4.setInt32(0, readInt32(bytes, offset));
|
|
1009
|
+
return scratchView4.getFloat32(0);
|
|
1010
|
+
}, value => {
|
|
1011
|
+
scratchView4.setFloat32(0, requireNumber(value, "float4"));
|
|
1012
|
+
return takeScratch4();
|
|
1013
|
+
}, (sink, value) => sink.float32(requireNumber(value, "float4")))], [OID.float8, /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
1014
|
+
requireSize(size, 8, "float8");
|
|
1015
|
+
stage8(bytes, offset);
|
|
1016
|
+
return scratchView8.getFloat64(0);
|
|
1017
|
+
}, value => {
|
|
1018
|
+
scratchView8.setFloat64(0, requireNumber(value, "float8"));
|
|
1019
|
+
return takeScratch8();
|
|
1020
|
+
}, (sink, value) => sink.float64(requireNumber(value, "float8")))], [OID.time, /*#__PURE__*/codecOf((bytes, offset, size) => {
|
|
1021
|
+
requireSize(size, 8, "time");
|
|
1022
|
+
stage8(bytes, offset);
|
|
1023
|
+
return scratchView8.getBigInt64(0);
|
|
1024
|
+
}, value => {
|
|
1025
|
+
scratchView8.setBigInt64(0, timeValue(value));
|
|
1026
|
+
return takeScratch8();
|
|
1027
|
+
}, (sink, value) => sink.bigInt64(timeValue(value)))], [OID.numeric, /*#__PURE__*/codecOf(decodeNumeric, encodeNumeric)], [OID.text, utf8Codec], [OID.varchar, utf8Codec], [OID.bpchar, utf8Codec], [OID.name, utf8Codec], [OID.json, jsonCodec], [OID.jsonb, jsonbCodec], [OID.uuid, /*#__PURE__*/codecOf(decodeUuid, encodeUuid, writeUuid)], [OID.inet, /*#__PURE__*/codecOf(decodeInet, value => encodeInet(value, false))], [OID.cidr, /*#__PURE__*/codecOf(decodeInet, value => encodeInet(value, true))], [OID.date, dateCodec], [OID.timetz, timetzCodec], [OID.timestamp, timestampCodec], [OID.timestamptz, timestampCodec]]);
|
|
1028
|
+
for (const [arrayOid, elementOid] of arrayToElement) {
|
|
1029
|
+
builtins.set(arrayOid, codecOf((bytes, offset, size) => decodeArray(bytes, offset, size, elementOid), value => encodeArray(value, elementOid), (sink, value) => writeArray(sink, value, elementOid)));
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Built-ins with registered codecs layered over them, so `encode` and `decode`
|
|
1033
|
+
* resolve an OID with a single lookup.
|
|
1034
|
+
*/
|
|
1035
|
+
const codecs = /*#__PURE__*/new Map(builtins);
|
|
1036
|
+
/**
|
|
1037
|
+
* Every built-in OID is below this, and PostgreSQL hands user-defined types
|
|
1038
|
+
* OIDs from 16384 up, so a direct table covers the common case and the map
|
|
1039
|
+
* covers registered ones.
|
|
1040
|
+
*/
|
|
1041
|
+
const tableSize = 4096;
|
|
1042
|
+
const table = /*#__PURE__*/new Array(tableSize);
|
|
1043
|
+
for (const [oid, codec] of codecs) table[oid] = codec;
|
|
1044
|
+
const lookup = oid => oid >= 0 && oid < tableSize ? table[oid] : codecs.get(oid);
|
|
1045
|
+
/**
|
|
1046
|
+
* Registers a binary codec for an OID the built-in catalogue does not cover,
|
|
1047
|
+
* or overrides a built-in one. Registered codecs take precedence.
|
|
1048
|
+
*
|
|
1049
|
+
* @category registry
|
|
1050
|
+
* @since 4.0.0
|
|
1051
|
+
*/
|
|
1052
|
+
export const register = (oid, codec) => {
|
|
1053
|
+
const unsafe = {
|
|
1054
|
+
encode(value) {
|
|
1055
|
+
const encoded = codec.encode(value);
|
|
1056
|
+
if (Result.isFailure(encoded)) throw encoded.failure;
|
|
1057
|
+
return encoded.success;
|
|
1058
|
+
},
|
|
1059
|
+
decode(bytes) {
|
|
1060
|
+
const decoded = codec.decode(bytes);
|
|
1061
|
+
if (Result.isFailure(decoded)) throw decoded.failure;
|
|
1062
|
+
return decoded.success;
|
|
1063
|
+
},
|
|
1064
|
+
...(codec.write === undefined ? undefined : {
|
|
1065
|
+
write(sink, value) {
|
|
1066
|
+
const written = codec.write(sink, value);
|
|
1067
|
+
if (Result.isFailure(written)) throw written.failure;
|
|
1068
|
+
}
|
|
1069
|
+
}),
|
|
1070
|
+
...(codec.read === undefined ? undefined : {
|
|
1071
|
+
read(bytes, offset, size) {
|
|
1072
|
+
const decoded = codec.read(bytes, offset, size);
|
|
1073
|
+
if (Result.isFailure(decoded)) throw decoded.failure;
|
|
1074
|
+
return decoded.success;
|
|
1075
|
+
}
|
|
1076
|
+
})
|
|
1077
|
+
};
|
|
1078
|
+
codecs.set(oid, unsafe);
|
|
1079
|
+
if (oid >= 0 && oid < tableSize) table[oid] = unsafe;
|
|
1080
|
+
};
|
|
1081
|
+
/**
|
|
1082
|
+
* Removes a previously registered codec.
|
|
1083
|
+
*
|
|
1084
|
+
* @category registry
|
|
1085
|
+
* @since 4.0.0
|
|
1086
|
+
*/
|
|
1087
|
+
export const unregister = oid => {
|
|
1088
|
+
const builtin = builtins.get(oid);
|
|
1089
|
+
if (builtin === undefined) codecs.delete(oid);else codecs.set(oid, builtin);
|
|
1090
|
+
if (oid >= 0 && oid < tableSize) table[oid] = builtin;
|
|
1091
|
+
};
|
|
1092
|
+
// -----------------------------------------------------------------------------
|
|
1093
|
+
// arrays
|
|
1094
|
+
// -----------------------------------------------------------------------------
|
|
1095
|
+
const encodeArray = (value, elementOid) => {
|
|
1096
|
+
if (!Array.isArray(value)) {
|
|
1097
|
+
return fail("Expected an array");
|
|
1098
|
+
}
|
|
1099
|
+
const codec = lookup(elementOid);
|
|
1100
|
+
if (codec === undefined) return fail(`No codec registered for OID ${elementOid}`);
|
|
1101
|
+
const count = value.length;
|
|
1102
|
+
const elements = new Array(count);
|
|
1103
|
+
let hasNull = false;
|
|
1104
|
+
let payloadSize = 0;
|
|
1105
|
+
for (let i = 0; i < count; i++) {
|
|
1106
|
+
const element = value[i];
|
|
1107
|
+
if (element === null) {
|
|
1108
|
+
elements[i] = null;
|
|
1109
|
+
hasNull = true;
|
|
1110
|
+
payloadSize += 4;
|
|
1111
|
+
} else {
|
|
1112
|
+
const encoded = codec.encode(element);
|
|
1113
|
+
elements[i] = encoded;
|
|
1114
|
+
payloadSize += 4 + encoded.length;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
const dimensions = count === 0 ? 0 : 1;
|
|
1118
|
+
const bytes = new Uint8Array(12 + dimensions * 8 + payloadSize);
|
|
1119
|
+
writeInt32(bytes, 0, dimensions);
|
|
1120
|
+
writeInt32(bytes, 4, hasNull ? 1 : 0);
|
|
1121
|
+
writeInt32(bytes, 8, elementOid);
|
|
1122
|
+
let offset = 12;
|
|
1123
|
+
if (dimensions === 1) {
|
|
1124
|
+
writeInt32(bytes, offset, count);
|
|
1125
|
+
writeInt32(bytes, offset + 4, 1);
|
|
1126
|
+
offset += 8;
|
|
1127
|
+
}
|
|
1128
|
+
for (let i = 0; i < count; i++) {
|
|
1129
|
+
const element = elements[i];
|
|
1130
|
+
if (element === null) {
|
|
1131
|
+
writeInt32(bytes, offset, -1);
|
|
1132
|
+
offset += 4;
|
|
1133
|
+
} else {
|
|
1134
|
+
writeInt32(bytes, offset, element.length);
|
|
1135
|
+
bytes.set(element, offset + 4);
|
|
1136
|
+
offset += 4 + element.length;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return bytes;
|
|
1140
|
+
};
|
|
1141
|
+
/**
|
|
1142
|
+
* As `encodeArray`, but into a sink: each element is framed with `beginLength`
|
|
1143
|
+
* and written in place, so neither the elements nor the array itself needs an
|
|
1144
|
+
* array of bytes of its own.
|
|
1145
|
+
*/
|
|
1146
|
+
const writeArray = (sink, value, elementOid) => {
|
|
1147
|
+
if (!Array.isArray(value)) {
|
|
1148
|
+
return fail("Expected an array");
|
|
1149
|
+
}
|
|
1150
|
+
const count = value.length;
|
|
1151
|
+
// The null flag sits ahead of the elements, so it is the one thing that has
|
|
1152
|
+
// to be known before any of them are written.
|
|
1153
|
+
let hasNull = false;
|
|
1154
|
+
for (let i = 0; i < count; i++) {
|
|
1155
|
+
if (value[i] === null) {
|
|
1156
|
+
hasNull = true;
|
|
1157
|
+
break;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
const dimensions = count === 0 ? 0 : 1;
|
|
1161
|
+
sink.int32(dimensions);
|
|
1162
|
+
sink.int32(hasNull ? 1 : 0);
|
|
1163
|
+
sink.int32(elementOid);
|
|
1164
|
+
if (dimensions === 1) {
|
|
1165
|
+
sink.int32(count);
|
|
1166
|
+
sink.int32(1);
|
|
1167
|
+
}
|
|
1168
|
+
// One lookup for the whole array. A codec with no writer of its own is left
|
|
1169
|
+
// to `writeValue`, which allocates for the element anyway.
|
|
1170
|
+
const write = lookup(elementOid)?.write;
|
|
1171
|
+
for (let i = 0; i < count; i++) {
|
|
1172
|
+
const element = value[i];
|
|
1173
|
+
if (element === null) {
|
|
1174
|
+
sink.int32(-1);
|
|
1175
|
+
} else {
|
|
1176
|
+
const token = sink.beginLength();
|
|
1177
|
+
if (write === undefined) writeValue(sink, element, elementOid);else write(sink, element);
|
|
1178
|
+
sink.endLength(token);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
/**
|
|
1183
|
+
* The element codec is resolved once for the whole array, and read in place
|
|
1184
|
+
* where it can be, so a thousand elements cost a thousand reads rather than a
|
|
1185
|
+
* thousand lookups and a thousand views.
|
|
1186
|
+
*/
|
|
1187
|
+
const decodeArray = (bytes, start, size, elementOid) => {
|
|
1188
|
+
if (size < 12) return fail("Truncated array value");
|
|
1189
|
+
const dimensions = readInt32(bytes, start);
|
|
1190
|
+
const wireElementOid = readUint32(bytes, start + 8);
|
|
1191
|
+
if (wireElementOid !== elementOid) {
|
|
1192
|
+
return fail(`Array element OID ${wireElementOid} does not match expected OID ${elementOid}`);
|
|
1193
|
+
}
|
|
1194
|
+
if (dimensions === 0) {
|
|
1195
|
+
if (size !== 12) return fail("Zero-dimensional array has trailing bytes");
|
|
1196
|
+
return [];
|
|
1197
|
+
}
|
|
1198
|
+
if (dimensions !== 1) {
|
|
1199
|
+
return fail(`Only 1-dimensional arrays are supported, received ${dimensions} dimensions`);
|
|
1200
|
+
}
|
|
1201
|
+
if (size < 20) return fail("Truncated array value");
|
|
1202
|
+
const length = readInt32(bytes, start + 12);
|
|
1203
|
+
if (length < 0) return fail(`Invalid array length: ${length}`);
|
|
1204
|
+
const lowerBound = readInt32(bytes, start + 16);
|
|
1205
|
+
if (lowerBound !== 1) return fail(`Only arrays with a lower bound of 1 are supported, received ${lowerBound}`);
|
|
1206
|
+
const codec = lookup(elementOid);
|
|
1207
|
+
const read = codec?.read;
|
|
1208
|
+
const values = new Array(length);
|
|
1209
|
+
const limit = start + size;
|
|
1210
|
+
let offset = start + 20;
|
|
1211
|
+
for (let i = 0; i < length; i++) {
|
|
1212
|
+
if (offset + 4 > limit) return fail("Truncated array element");
|
|
1213
|
+
const elementSize = readInt32(bytes, offset);
|
|
1214
|
+
offset += 4;
|
|
1215
|
+
if (elementSize < -1) return fail(`Invalid array element length: ${elementSize}`);
|
|
1216
|
+
if (elementSize === -1) {
|
|
1217
|
+
values[i] = null;
|
|
1218
|
+
} else {
|
|
1219
|
+
if (offset + elementSize > limit) return fail("Truncated array element");
|
|
1220
|
+
values[i] = read !== undefined ? read(bytes, offset, elementSize) : codec === undefined ? region(bytes, offset, elementSize) : codec.decode(region(bytes, offset, elementSize));
|
|
1221
|
+
offset += elementSize;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
if (offset !== limit) return fail("Array value has trailing bytes");
|
|
1225
|
+
return values;
|
|
1226
|
+
};
|
|
1227
|
+
/**
|
|
1228
|
+
* Builds a field reader for `PgProtocol.makeParser`, so a result's rows decode
|
|
1229
|
+
* as they are parsed rather than through a view per column.
|
|
1230
|
+
*
|
|
1231
|
+
* Every column is resolved once here rather than once per row, and a codec that
|
|
1232
|
+
* can read in place does; the rest are handed a view. SQL NULL reads as `null`,
|
|
1233
|
+
* and a column whose OID has no codec reads as a copy of its bytes.
|
|
1234
|
+
*
|
|
1235
|
+
* Only the binary format is supported, and a text column returns a
|
|
1236
|
+
* `CodecError` failure here rather than once per row. A successful result
|
|
1237
|
+
* contains the parser's internal throwing fast path; its failures are terminal
|
|
1238
|
+
* for that parser. The standalone `encode` and `decode` APIs remain typed
|
|
1239
|
+
* `Result` values.
|
|
1240
|
+
*
|
|
1241
|
+
* ```ts
|
|
1242
|
+
* import { PgProtocol, PgTypes } from "@effect/sql-pg"
|
|
1243
|
+
*
|
|
1244
|
+
* const parser = PgProtocol.makeParser({ readField: Result.getOrThrow(PgTypes.makeFieldReader([])) })
|
|
1245
|
+
* // on each RowDescription
|
|
1246
|
+
* declare const description: PgProtocol.RowDescription
|
|
1247
|
+
* parser.readField = Result.getOrThrow(PgTypes.makeFieldReader(description.fields))
|
|
1248
|
+
* ```
|
|
1249
|
+
*
|
|
1250
|
+
* @category decoding
|
|
1251
|
+
* @since 4.0.0
|
|
1252
|
+
*/
|
|
1253
|
+
export const makeFieldReader = columns => result(() => {
|
|
1254
|
+
const codecs = columns.map((column, index) => {
|
|
1255
|
+
if (column.format !== 1) {
|
|
1256
|
+
return fail(`Only the binary format is supported, column ${index} has format ${column.format}`);
|
|
1257
|
+
}
|
|
1258
|
+
return lookup(column.dataTypeOid);
|
|
1259
|
+
});
|
|
1260
|
+
return (bytes, offset, size, column) => {
|
|
1261
|
+
if (size < 0) return null;
|
|
1262
|
+
const codec = codecs[column];
|
|
1263
|
+
if (codec === undefined) return bytes.slice(offset, offset + size);
|
|
1264
|
+
const read = codec.read;
|
|
1265
|
+
return read === undefined ? codec.decode(bytes.subarray(offset, offset + size)) : read(bytes, offset, size);
|
|
1266
|
+
};
|
|
1267
|
+
});
|
|
1268
|
+
/**
|
|
1269
|
+
* Encodes a JavaScript value as the binary representation of the given OID.
|
|
1270
|
+
*
|
|
1271
|
+
* Returns a `CodecError` failure when the value has the wrong JavaScript type,
|
|
1272
|
+
* or when the OID is neither built in nor registered.
|
|
1273
|
+
*
|
|
1274
|
+
* @category encoding
|
|
1275
|
+
* @since 4.0.0
|
|
1276
|
+
*/
|
|
1277
|
+
export const encode = (value, oid) => result(() => {
|
|
1278
|
+
const codec = lookup(oid);
|
|
1279
|
+
if (codec === undefined) return fail(`No codec registered for OID ${oid}`);
|
|
1280
|
+
return codec.encode(value);
|
|
1281
|
+
});
|
|
1282
|
+
/**
|
|
1283
|
+
* Decodes the binary representation of the given OID.
|
|
1284
|
+
*
|
|
1285
|
+
* `format` must be `1`; the text format is not implemented. An OID that is
|
|
1286
|
+
* neither built in nor registered decodes to the raw bytes.
|
|
1287
|
+
*
|
|
1288
|
+
* @category decoding
|
|
1289
|
+
* @since 4.0.0
|
|
1290
|
+
*/
|
|
1291
|
+
export const decode = (bytes, oid, format) => result(() => {
|
|
1292
|
+
if (format !== 1) {
|
|
1293
|
+
return fail(`Only the binary format is supported, received format ${format}`);
|
|
1294
|
+
}
|
|
1295
|
+
const codec = lookup(oid);
|
|
1296
|
+
return codec === undefined ? bytes : codec.decode(bytes);
|
|
1297
|
+
});
|
|
1298
|
+
/**
|
|
1299
|
+
* Encodes a parameter for a `Bind` message. SQL NULL stays `null`.
|
|
1300
|
+
*
|
|
1301
|
+
* @category encoding
|
|
1302
|
+
* @since 4.0.0
|
|
1303
|
+
*/
|
|
1304
|
+
export const encodeParameter = parameter => parameter.value === null ? Result.succeed(null) : encode(parameter.value, parameter.oid);
|
|
1305
|
+
const writeValue = (sink, value, oid) => {
|
|
1306
|
+
const codec = lookup(oid);
|
|
1307
|
+
if (codec === undefined) return fail(`No codec registered for OID ${oid}`);
|
|
1308
|
+
if (codec.write === undefined) sink.raw(codec.encode(value));else codec.write(sink, value);
|
|
1309
|
+
};
|
|
1310
|
+
/**
|
|
1311
|
+
* Writes a parameter into a `Bind` frame, for `PgProtocol.makeBindEncoder`.
|
|
1312
|
+
* Codecs that can write their bytes in place do; the rest fall back to
|
|
1313
|
+
* `encode` and a copy.
|
|
1314
|
+
*
|
|
1315
|
+
* @category encoding
|
|
1316
|
+
* @since 4.0.0
|
|
1317
|
+
*/
|
|
1318
|
+
const writeParameterUnsafe = (sink, parameter) => parameter.value === null ? sink.sqlNull() : writeValue(sink, parameter.value, parameter.oid);
|
|
1319
|
+
export const writeParameter = (sink, parameter) => {
|
|
1320
|
+
try {
|
|
1321
|
+
writeParameterUnsafe(sink, parameter);
|
|
1322
|
+
return Result.void;
|
|
1323
|
+
} catch (error) {
|
|
1324
|
+
if (error instanceof CodecError) return Result.fail(error);
|
|
1325
|
+
throw error;
|
|
1326
|
+
}
|
|
1327
|
+
};
|
|
1328
|
+
const valueWriterUnsafe = /*#__PURE__*/Symbol.for("@effect/sql-pg/PgProtocol/ValueWriter/unsafe");
|
|
1329
|
+
Object.defineProperty(writeParameter, valueWriterUnsafe, {
|
|
1330
|
+
value: writeParameterUnsafe
|
|
1331
|
+
});
|
|
1332
|
+
const parameter = oid => value => ({
|
|
1333
|
+
oid,
|
|
1334
|
+
value
|
|
1335
|
+
});
|
|
1336
|
+
/**
|
|
1337
|
+
* A `bool` parameter.
|
|
1338
|
+
*
|
|
1339
|
+
* @category constructors
|
|
1340
|
+
* @since 4.0.0
|
|
1341
|
+
*/
|
|
1342
|
+
export const bool = /*#__PURE__*/parameter(OID.bool);
|
|
1343
|
+
/**
|
|
1344
|
+
* An `int2` parameter.
|
|
1345
|
+
*
|
|
1346
|
+
* @category constructors
|
|
1347
|
+
* @since 4.0.0
|
|
1348
|
+
*/
|
|
1349
|
+
export const int2 = /*#__PURE__*/parameter(OID.int2);
|
|
1350
|
+
/**
|
|
1351
|
+
* An `int4` parameter.
|
|
1352
|
+
*
|
|
1353
|
+
* @category constructors
|
|
1354
|
+
* @since 4.0.0
|
|
1355
|
+
*/
|
|
1356
|
+
export const int4 = /*#__PURE__*/parameter(OID.int4);
|
|
1357
|
+
/**
|
|
1358
|
+
* An `int8` parameter.
|
|
1359
|
+
*
|
|
1360
|
+
* @category constructors
|
|
1361
|
+
* @since 4.0.0
|
|
1362
|
+
*/
|
|
1363
|
+
export const int8 = /*#__PURE__*/parameter(OID.int8);
|
|
1364
|
+
/**
|
|
1365
|
+
* An `oid` parameter.
|
|
1366
|
+
*
|
|
1367
|
+
* @category constructors
|
|
1368
|
+
* @since 4.0.0
|
|
1369
|
+
*/
|
|
1370
|
+
export const oid = /*#__PURE__*/parameter(OID.oid);
|
|
1371
|
+
/**
|
|
1372
|
+
* A `float4` parameter.
|
|
1373
|
+
*
|
|
1374
|
+
* @category constructors
|
|
1375
|
+
* @since 4.0.0
|
|
1376
|
+
*/
|
|
1377
|
+
export const float4 = /*#__PURE__*/parameter(OID.float4);
|
|
1378
|
+
/**
|
|
1379
|
+
* A `float8` parameter.
|
|
1380
|
+
*
|
|
1381
|
+
* @category constructors
|
|
1382
|
+
* @since 4.0.0
|
|
1383
|
+
*/
|
|
1384
|
+
export const float8 = /*#__PURE__*/parameter(OID.float8);
|
|
1385
|
+
/**
|
|
1386
|
+
* A `numeric` parameter, given as a decimal string or `"NaN"`.
|
|
1387
|
+
*
|
|
1388
|
+
* @category constructors
|
|
1389
|
+
* @since 4.0.0
|
|
1390
|
+
*/
|
|
1391
|
+
export const numeric = /*#__PURE__*/parameter(OID.numeric);
|
|
1392
|
+
/**
|
|
1393
|
+
* A `text` parameter.
|
|
1394
|
+
*
|
|
1395
|
+
* @category constructors
|
|
1396
|
+
* @since 4.0.0
|
|
1397
|
+
*/
|
|
1398
|
+
export const text = /*#__PURE__*/parameter(OID.text);
|
|
1399
|
+
/**
|
|
1400
|
+
* A `varchar` parameter.
|
|
1401
|
+
*
|
|
1402
|
+
* @category constructors
|
|
1403
|
+
* @since 4.0.0
|
|
1404
|
+
*/
|
|
1405
|
+
export const varchar = /*#__PURE__*/parameter(OID.varchar);
|
|
1406
|
+
/**
|
|
1407
|
+
* A `bpchar` parameter.
|
|
1408
|
+
*
|
|
1409
|
+
* @category constructors
|
|
1410
|
+
* @since 4.0.0
|
|
1411
|
+
*/
|
|
1412
|
+
export const bpchar = /*#__PURE__*/parameter(OID.bpchar);
|
|
1413
|
+
/**
|
|
1414
|
+
* A `name` parameter.
|
|
1415
|
+
*
|
|
1416
|
+
* @category constructors
|
|
1417
|
+
* @since 4.0.0
|
|
1418
|
+
*/
|
|
1419
|
+
export const name = /*#__PURE__*/parameter(OID.name);
|
|
1420
|
+
/**
|
|
1421
|
+
* A `bytea` parameter.
|
|
1422
|
+
*
|
|
1423
|
+
* @category constructors
|
|
1424
|
+
* @since 4.0.0
|
|
1425
|
+
*/
|
|
1426
|
+
export const bytea = /*#__PURE__*/parameter(OID.bytea);
|
|
1427
|
+
/**
|
|
1428
|
+
* A `json` parameter.
|
|
1429
|
+
*
|
|
1430
|
+
* @category constructors
|
|
1431
|
+
* @since 4.0.0
|
|
1432
|
+
*/
|
|
1433
|
+
export const json = /*#__PURE__*/parameter(OID.json);
|
|
1434
|
+
/**
|
|
1435
|
+
* A `jsonb` parameter.
|
|
1436
|
+
*
|
|
1437
|
+
* @category constructors
|
|
1438
|
+
* @since 4.0.0
|
|
1439
|
+
*/
|
|
1440
|
+
export const jsonb = /*#__PURE__*/parameter(OID.jsonb);
|
|
1441
|
+
/**
|
|
1442
|
+
* A `uuid` parameter.
|
|
1443
|
+
*
|
|
1444
|
+
* @category constructors
|
|
1445
|
+
* @since 4.0.0
|
|
1446
|
+
*/
|
|
1447
|
+
export const uuid = /*#__PURE__*/parameter(OID.uuid);
|
|
1448
|
+
/**
|
|
1449
|
+
* An `inet` parameter, such as `"10.0.0.1"` or `"10.0.0.0/8"`.
|
|
1450
|
+
*
|
|
1451
|
+
* @category constructors
|
|
1452
|
+
* @since 4.0.0
|
|
1453
|
+
*/
|
|
1454
|
+
export const inet = /*#__PURE__*/parameter(OID.inet);
|
|
1455
|
+
/**
|
|
1456
|
+
* A `cidr` parameter.
|
|
1457
|
+
*
|
|
1458
|
+
* @category constructors
|
|
1459
|
+
* @since 4.0.0
|
|
1460
|
+
*/
|
|
1461
|
+
export const cidr = /*#__PURE__*/parameter(OID.cidr);
|
|
1462
|
+
/**
|
|
1463
|
+
* A `date` parameter, given as `YYYY-MM-DD`, `"infinity"`, or `"-infinity"`.
|
|
1464
|
+
*
|
|
1465
|
+
* @category constructors
|
|
1466
|
+
* @since 4.0.0
|
|
1467
|
+
*/
|
|
1468
|
+
export const date = /*#__PURE__*/parameter(OID.date);
|
|
1469
|
+
/**
|
|
1470
|
+
* A `time` parameter, given as microseconds since midnight.
|
|
1471
|
+
*
|
|
1472
|
+
* @category constructors
|
|
1473
|
+
* @since 4.0.0
|
|
1474
|
+
*/
|
|
1475
|
+
export const time = /*#__PURE__*/parameter(OID.time);
|
|
1476
|
+
/**
|
|
1477
|
+
* A `timetz` parameter, such as `"12:34:56+02:00"`.
|
|
1478
|
+
*
|
|
1479
|
+
* @category constructors
|
|
1480
|
+
* @since 4.0.0
|
|
1481
|
+
*/
|
|
1482
|
+
export const timetz = /*#__PURE__*/parameter(OID.timetz);
|
|
1483
|
+
/**
|
|
1484
|
+
* A `timestamp` parameter, given as Unix epoch milliseconds and interpreted
|
|
1485
|
+
* as UTC.
|
|
1486
|
+
*
|
|
1487
|
+
* @category constructors
|
|
1488
|
+
* @since 4.0.0
|
|
1489
|
+
*/
|
|
1490
|
+
export const timestamp = /*#__PURE__*/parameter(OID.timestamp);
|
|
1491
|
+
/**
|
|
1492
|
+
* A `timestamptz` parameter, given as Unix epoch milliseconds.
|
|
1493
|
+
*
|
|
1494
|
+
* @category constructors
|
|
1495
|
+
* @since 4.0.0
|
|
1496
|
+
*/
|
|
1497
|
+
export const timestamptz = /*#__PURE__*/parameter(OID.timestamptz);
|
|
1498
|
+
/**
|
|
1499
|
+
* A one-dimensional array parameter whose elements have the given OID.
|
|
1500
|
+
*
|
|
1501
|
+
* @category constructors
|
|
1502
|
+
* @since 4.0.0
|
|
1503
|
+
*/
|
|
1504
|
+
export const array = (values, elementOid) => result(() => {
|
|
1505
|
+
const arrayOid = elementToArray.get(elementOid);
|
|
1506
|
+
if (arrayOid === undefined) {
|
|
1507
|
+
return fail(`No array type known for element OID ${elementOid}`);
|
|
1508
|
+
}
|
|
1509
|
+
return {
|
|
1510
|
+
oid: arrayOid,
|
|
1511
|
+
value: values
|
|
1512
|
+
};
|
|
1513
|
+
});
|
|
1514
|
+
//# sourceMappingURL=PgTypes.js.map
|