joist-orm 2.3.0-next.41 → 2.3.0-next.43
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/build/_virtual/_rolldown/runtime.cjs +23 -0
- package/build/_virtual/_rolldown/runtime.js +5 -0
- package/build/codegen.cjs +19 -0
- package/build/codegen.cjs.map +1 -0
- package/build/codegen.d.cts +1 -0
- package/build/codegen.d.mts +1 -0
- package/build/codegen.js +11 -28
- package/build/codegen.js.map +1 -1
- package/build/drivers/PostgresDriver.cjs +290 -0
- package/build/drivers/PostgresDriver.cjs.map +1 -0
- package/build/drivers/PostgresDriver.d.cts +77 -0
- package/build/drivers/PostgresDriver.d.cts.map +1 -0
- package/build/drivers/PostgresDriver.d.mts +77 -0
- package/build/drivers/PostgresDriver.d.mts.map +1 -0
- package/build/drivers/PostgresDriver.js +249 -325
- package/build/drivers/PostgresDriver.js.map +1 -1
- package/build/drivers/WireRowData.cjs +374 -0
- package/build/drivers/WireRowData.cjs.map +1 -0
- package/build/drivers/WireRowData.d.cts +111 -0
- package/build/drivers/WireRowData.d.cts.map +1 -0
- package/build/drivers/WireRowData.d.mts +111 -0
- package/build/drivers/WireRowData.d.mts.map +1 -0
- package/build/drivers/WireRowData.js +352 -412
- package/build/drivers/WireRowData.js.map +1 -1
- package/build/drivers/binaryParsers.cjs +422 -0
- package/build/drivers/binaryParsers.cjs.map +1 -0
- package/build/drivers/{binaryParsers.d.ts → binaryParsers.d.cts} +18 -14
- package/build/drivers/binaryParsers.d.cts.map +1 -0
- package/build/drivers/binaryParsers.d.mts +80 -0
- package/build/drivers/binaryParsers.d.mts.map +1 -0
- package/build/drivers/binaryParsers.js +315 -385
- package/build/drivers/binaryParsers.js.map +1 -1
- package/build/drivers/patchPgProtocol.cjs +255 -0
- package/build/drivers/patchPgProtocol.cjs.map +1 -0
- package/build/drivers/{patchPgProtocol.d.ts → patchPgProtocol.d.cts} +18 -14
- package/build/drivers/patchPgProtocol.d.cts.map +1 -0
- package/build/drivers/patchPgProtocol.d.mts +47 -0
- package/build/drivers/patchPgProtocol.d.mts.map +1 -0
- package/build/drivers/patchPgProtocol.js +211 -232
- package/build/drivers/patchPgProtocol.js.map +1 -1
- package/build/graphql-codegen-export.cjs +10 -0
- package/build/graphql-codegen-export.d.cts +1 -0
- package/build/graphql-codegen-export.d.mts +1 -0
- package/build/graphql-codegen-export.js +2 -19
- package/build/graphql-export.cjs +10 -0
- package/build/graphql-export.d.cts +1 -0
- package/build/graphql-export.d.mts +1 -0
- package/build/graphql-export.js +2 -19
- package/build/index.cjs +41 -0
- package/build/index.d.cts +3 -0
- package/build/index.d.mts +3 -0
- package/build/index.js +3 -27
- package/build/knex-export.cjs +10 -0
- package/build/knex-export.d.cts +1 -0
- package/build/knex-export.d.mts +1 -0
- package/build/knex-export.js +2 -19
- package/build/pg-export.cjs +13 -0
- package/build/pg-export.d.cts +4 -0
- package/build/pg-export.d.mts +4 -0
- package/build/pg-export.js +4 -20
- package/build/pg-migrate.cjs +22 -0
- package/build/pg-migrate.cjs.map +1 -0
- package/build/pg-migrate.d.cts +1 -0
- package/build/pg-migrate.d.mts +1 -0
- package/build/pg-migrate.js +13 -28
- package/build/pg-migrate.js.map +1 -1
- package/build/seed.cjs +49 -0
- package/build/seed.cjs.map +1 -0
- package/build/{seed.d.ts → seed.d.cts} +5 -1
- package/build/seed.d.cts.map +1 -0
- package/build/seed.d.mts +24 -0
- package/build/seed.d.mts.map +1 -0
- package/build/seed.js +39 -45
- package/build/seed.js.map +1 -1
- package/build/tests-export.cjs +10 -0
- package/build/tests-export.d.cts +1 -0
- package/build/tests-export.d.mts +1 -0
- package/build/tests-export.js +2 -19
- package/package.json +150 -28
- package/build/codegen.d.ts +0 -2
- package/build/drivers/PostgresDriver.d.ts +0 -73
- package/build/drivers/WireRowData.d.ts +0 -107
- package/build/graphql-codegen-export.d.ts +0 -1
- package/build/graphql-codegen-export.js.map +0 -1
- package/build/graphql-export.d.ts +0 -1
- package/build/graphql-export.js.map +0 -1
- package/build/index.d.ts +0 -3
- package/build/index.js.map +0 -1
- package/build/knex-export.d.ts +0 -1
- package/build/knex-export.js.map +0 -1
- package/build/pg-export.d.ts +0 -3
- package/build/pg-export.js.map +0 -1
- package/build/pg-migrate.d.ts +0 -2
- package/build/tests-export.d.ts +0 -1
- package/build/tests-export.js.map +0 -1
|
@@ -1,253 +1,204 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.setBinaryTypeParser = setBinaryTypeParser;
|
|
4
|
-
exports.getBinaryTypeParser = getBinaryTypeParser;
|
|
5
|
-
exports.registerDatabaseBinaryParsers = registerDatabaseBinaryParsers;
|
|
6
|
-
exports.setSessionTimeZone = setSessionTimeZone;
|
|
7
|
-
exports.binaryTextParser = binaryTextParser;
|
|
8
|
-
exports.binaryArrayParser = binaryArrayParser;
|
|
9
|
-
exports.binaryDateToDate = binaryDateToDate;
|
|
10
|
-
exports.binaryTimestampToDate = binaryTimestampToDate;
|
|
11
|
-
exports.binaryTimestamptzToDate = binaryTimestamptzToDate;
|
|
12
|
-
exports.registerTemporalBinaryParsers = registerTemporalBinaryParsers;
|
|
13
|
-
const joist_core_1 = require("joist-core");
|
|
1
|
+
import { requireTemporal } from "joist-core";
|
|
2
|
+
//#region src/drivers/binaryParsers.ts
|
|
14
3
|
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
4
|
+
* Joist's registry of PostgreSQL *binary-format* cell parsers, used by `WireRowData` for lazy
|
|
5
|
+
* `em.find`/`em.load` results (which request binary output; see `executeRowDataQuery`).
|
|
6
|
+
*
|
|
7
|
+
* This registry is deliberately **separate from `pg.types`**: `pg.types.setTypeParser` and
|
|
8
|
+
* pool/client `TypeOverrides` continue to govern classic *text* results (knex, `pool.query`,
|
|
9
|
+
* non-lazy drivers), while binary cells decode wire-bytes -> values directly — ints via
|
|
10
|
+
* `readInt32BE`, timestamps via µs arithmetic, Temporal values constructed without ever
|
|
11
|
+
* materializing a string. There is no fallback from one registry to the other:
|
|
12
|
+
*
|
|
13
|
+
* - Built-in parsers cover the standard scalar/array types below; `setupLatestPgTypes`
|
|
14
|
+
* registers the date/time parsers appropriate to Date-vs-Temporal mode.
|
|
15
|
+
* - Custom/extension types (native enums, citext, domains, hstore, ...) must be registered
|
|
16
|
+
* explicitly with {@link setBinaryTypeParser} — text-like types can use
|
|
17
|
+
* {@link binaryTextParser}, arrays of registered elements can use {@link binaryArrayParser}.
|
|
18
|
+
* - A query selecting a column whose oid has no registered parser **fails** with a descriptive
|
|
19
|
+
* error (see `WireRowData.setRowDescription`), rather than guessing at a lossy decoding.
|
|
20
|
+
*
|
|
21
|
+
* Values produced here should match what the classic text path produces for the same column
|
|
22
|
+
* (i.e. `numeric` stays a string, `int8` stays a string, Date-mode timestamps become `Date`s),
|
|
23
|
+
* so entities hydrate identically in either mode; Temporal-mode parsers construct
|
|
24
|
+
* `Temporal.PlainDate`/`ZonedDateTime`/etc. directly, and the temporal mappers pass
|
|
25
|
+
* already-constructed instances through.
|
|
26
|
+
*/
|
|
38
27
|
function setBinaryTypeParser(oid, parse) {
|
|
39
|
-
|
|
28
|
+
parsers.set(oid, parse);
|
|
40
29
|
}
|
|
41
30
|
/** Returns the registered binary parser for `oid`, i.e. for tests to save/restore. */
|
|
42
31
|
function getBinaryTypeParser(oid) {
|
|
43
|
-
|
|
32
|
+
return parsers.get(oid);
|
|
44
33
|
}
|
|
45
34
|
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
35
|
+
* Auto-registers binary parsers for the database's text-like dynamic-oid types — native enums
|
|
36
|
+
* and citext, plus their array types — so apps get those for free.
|
|
37
|
+
*
|
|
38
|
+
* `PostgresDriver` calls this lazily before its first lazy query; apps can also call it
|
|
39
|
+
* directly (i.e. at boot) if they want the registrations eagerly. Explicit
|
|
40
|
+
* `setBinaryTypeParser` registrations are never overwritten.
|
|
41
|
+
*/
|
|
53
42
|
async function registerDatabaseBinaryParsers(pool) {
|
|
54
|
-
|
|
55
|
-
// what classic text parsing would have produced (pg renders timestamps in the session zone)
|
|
56
|
-
const [{ rows: settings }, { rows }] = await Promise.all([
|
|
57
|
-
pool.query(`select current_setting('TimeZone') as tz`),
|
|
58
|
-
pool.query(`select t.oid, t.typarray
|
|
43
|
+
const [{ rows: settings }, { rows }] = await Promise.all([pool.query(`select current_setting('TimeZone') as tz`), pool.query(`select t.oid, t.typarray
|
|
59
44
|
from pg_type t
|
|
60
45
|
where t.typtype = 'e'
|
|
61
46
|
or t.typname = 'citext'
|
|
62
|
-
or (t.typtype = 'd' and (select b.typcategory from pg_type b where b.oid = t.typbasetype) = 'S')`)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (arrayOid !== 0 && getBinaryTypeParser(arrayOid) === undefined) {
|
|
71
|
-
setBinaryTypeParser(arrayOid, binaryArrayParser);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
47
|
+
or (t.typtype = 'd' and (select b.typcategory from pg_type b where b.oid = t.typbasetype) = 'S')`)]);
|
|
48
|
+
setSessionTimeZone(settings[0].tz);
|
|
49
|
+
for (const row of rows) {
|
|
50
|
+
const oid = Number(row.oid);
|
|
51
|
+
if (getBinaryTypeParser(oid) === void 0) setBinaryTypeParser(oid, binaryTextParser);
|
|
52
|
+
const arrayOid = Number(row.typarray);
|
|
53
|
+
if (arrayOid !== 0 && getBinaryTypeParser(arrayOid) === void 0) setBinaryTypeParser(arrayOid, binaryArrayParser);
|
|
54
|
+
}
|
|
74
55
|
}
|
|
75
56
|
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
57
|
+
* Sets the session `TimeZone` used to zone temporal-mode `timestamptz` loads; normally captured
|
|
58
|
+
* automatically by {@link registerDatabaseBinaryParsers}. One zone per process — pools with
|
|
59
|
+
* differing session TimeZones are not supported by the binary path.
|
|
60
|
+
*/
|
|
80
61
|
function setSessionTimeZone(timeZone) {
|
|
81
|
-
|
|
62
|
+
sessionTimeZone = timeZone === "Etc/UTC" ? "UTC" : timeZone;
|
|
82
63
|
}
|
|
83
64
|
let sessionTimeZone = "UTC";
|
|
84
65
|
/** Decodes a cell's bytes as utf8 text, i.e. for text-like custom types (enums, citext, domains). */
|
|
85
66
|
function binaryTextParser(chunk, start, length) {
|
|
86
|
-
|
|
67
|
+
return chunk.toString("utf8", start, start + length);
|
|
87
68
|
}
|
|
88
69
|
/**
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
70
|
+
* Decodes a binary array cell into a JS array of element values.
|
|
71
|
+
*
|
|
72
|
+
* The element oid is embedded in the wire format, and each element decodes through this
|
|
73
|
+
* registry — so arrays of any registered type (including custom-registered oids and the
|
|
74
|
+
* mode-appropriate temporal types) work without separate element wiring. Registered for the
|
|
75
|
+
* standard `_type` oids below; register it for custom array oids as needed.
|
|
76
|
+
*/
|
|
96
77
|
function binaryArrayParser(chunk, start, length) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
else {
|
|
120
|
-
values.push(parse(chunk, pos, len));
|
|
121
|
-
pos += len;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return values;
|
|
126
|
-
}
|
|
127
|
-
return readDim(0);
|
|
78
|
+
const nDims = chunk.readInt32BE(start);
|
|
79
|
+
if (nDims === 0) return [];
|
|
80
|
+
const elemOid = chunk.readInt32BE(start + 8);
|
|
81
|
+
const parse = parsers.get(elemOid) ?? fail$1(`joist-orm: no binary type parser registered for array element oid ${elemOid}; see setBinaryTypeParser`);
|
|
82
|
+
const dims = [];
|
|
83
|
+
for (let d = 0; d < nDims; d++) dims.push(chunk.readInt32BE(start + 12 + d * 8));
|
|
84
|
+
let pos = start + 12 + nDims * 8;
|
|
85
|
+
function readDim(dim) {
|
|
86
|
+
const values = [];
|
|
87
|
+
for (let i = 0; i < dims[dim]; i++) if (dim < nDims - 1) values.push(readDim(dim + 1));
|
|
88
|
+
else {
|
|
89
|
+
const len = chunk.readInt32BE(pos);
|
|
90
|
+
pos += 4;
|
|
91
|
+
if (len === -1) values.push(null);
|
|
92
|
+
else {
|
|
93
|
+
values.push(parse(chunk, pos, len));
|
|
94
|
+
pos += len;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return values;
|
|
98
|
+
}
|
|
99
|
+
return readDim(0);
|
|
128
100
|
}
|
|
129
101
|
/** Decodes binary `date` cells to `Date`s, matching postgres-date's text semantics; Date mode. */
|
|
130
102
|
function binaryDateToDate(chunk, start) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// Mirror postgres-date's fixup for years 0-99 being interpreted as 1900-1999
|
|
139
|
-
if (year < 100)
|
|
140
|
-
date.setFullYear(year);
|
|
141
|
-
return date;
|
|
103
|
+
const days = chunk.readInt32BE(start);
|
|
104
|
+
if (days === INFINITY_DAYS) return Infinity;
|
|
105
|
+
if (days === NEG_INFINITY_DAYS) return -Infinity;
|
|
106
|
+
const [year, month, day] = civilFromDays(days + PG_EPOCH_DAYS);
|
|
107
|
+
const date = new Date(year, month - 1, day);
|
|
108
|
+
if (year < 100) date.setFullYear(year);
|
|
109
|
+
return date;
|
|
142
110
|
}
|
|
143
111
|
/** Decodes binary `timestamp` (without zone) cells to local-time `Date`s; Date mode. */
|
|
144
112
|
function binaryTimestampToDate(chunk, start) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (year < 100)
|
|
155
|
-
date.setFullYear(year);
|
|
156
|
-
return date;
|
|
113
|
+
const pgMicros = chunk.readBigInt64BE(start);
|
|
114
|
+
if (pgMicros === INFINITY_US) return Infinity;
|
|
115
|
+
if (pgMicros === NEG_INFINITY_US) return -Infinity;
|
|
116
|
+
const { days, us } = splitPgMicros(pgMicros);
|
|
117
|
+
const [year, month, day] = civilFromDays(days);
|
|
118
|
+
const seconds = Math.floor(us / 1e6);
|
|
119
|
+
const date = new Date(year, month - 1, day, Math.floor(seconds / 3600), Math.floor(seconds / 60 % 60), seconds % 60, Math.floor(us % 1e6 / 1e3));
|
|
120
|
+
if (year < 100) date.setFullYear(year);
|
|
121
|
+
return date;
|
|
157
122
|
}
|
|
158
123
|
/** Decodes binary `timestamptz` cells to `Date`s (µs floor to ms, like the text parse); Date mode. */
|
|
159
124
|
function binaryTimestamptzToDate(chunk, start) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (epochMicros % 1000n < 0n)
|
|
168
|
-
ms -= 1n;
|
|
169
|
-
return new Date(Number(ms));
|
|
125
|
+
const pgMicros = chunk.readBigInt64BE(start);
|
|
126
|
+
if (pgMicros === INFINITY_US) return Infinity;
|
|
127
|
+
if (pgMicros === NEG_INFINITY_US) return -Infinity;
|
|
128
|
+
const epochMicros = pgMicros + PG_EPOCH_US;
|
|
129
|
+
let ms = epochMicros / 1000n;
|
|
130
|
+
if (epochMicros % 1000n < 0n) ms -= 1n;
|
|
131
|
+
return new Date(Number(ms));
|
|
170
132
|
}
|
|
171
133
|
/**
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
134
|
+
* Registers the Temporal-mode binary parsers: date/time/timestamp/timestamptz cells construct
|
|
135
|
+
* `Temporal` values *directly* from the wire µs/days — no intermediate strings, no
|
|
136
|
+
* `temporalMappers.fromDb` parsing (the mappers pass already-constructed instances through).
|
|
137
|
+
*
|
|
138
|
+
* `timestamptz` matches the text path's zoning exactly: classic parsing zones the value by the
|
|
139
|
+
* offset the session's `TimeZone` rendered (normalizing `+00` to `UTC`), so we compute the
|
|
140
|
+
* session zone's offset at each instant (see `setSessionTimeZone`; the configured
|
|
141
|
+
* `temporal.timeZone` only governs `now`-conversions, not loads).
|
|
142
|
+
*/
|
|
181
143
|
function registerTemporalBinaryParsers() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// Zone by the session zone's offset at this instant, i.e. what pg's text rendering carries
|
|
212
|
-
const zoned = instant.toZonedDateTimeISO(sessionTimeZone);
|
|
213
|
-
const { offset } = zoned;
|
|
214
|
-
return offset === "+00:00" ? zoned.withTimeZone("UTC") : zoned.withTimeZone(offset);
|
|
215
|
-
});
|
|
144
|
+
const { Temporal: t } = requireTemporal();
|
|
145
|
+
setBinaryTypeParser(oids.DATE, (chunk, start) => {
|
|
146
|
+
const days = chunk.readInt32BE(start);
|
|
147
|
+
if (days === INFINITY_DAYS || days === NEG_INFINITY_DAYS) failTemporalInfinity("date");
|
|
148
|
+
const [year, month, day] = civilFromDays(days + PG_EPOCH_DAYS);
|
|
149
|
+
return new t.PlainDate(year, month, day);
|
|
150
|
+
});
|
|
151
|
+
setBinaryTypeParser(1083, (chunk, start) => {
|
|
152
|
+
const us = Number(chunk.readBigInt64BE(start));
|
|
153
|
+
const seconds = Math.floor(us / 1e6);
|
|
154
|
+
return new t.PlainTime(Math.floor(seconds / 3600), Math.floor(seconds / 60 % 60), seconds % 60, Math.floor(us % 1e6 / 1e3), us % 1e3);
|
|
155
|
+
});
|
|
156
|
+
setBinaryTypeParser(oids.TIMESTAMP, (chunk, start) => {
|
|
157
|
+
const pgMicros = chunk.readBigInt64BE(start);
|
|
158
|
+
if (pgMicros === INFINITY_US || pgMicros === NEG_INFINITY_US) failTemporalInfinity("timestamp");
|
|
159
|
+
const { days, us } = splitPgMicros(pgMicros);
|
|
160
|
+
const [year, month, day] = civilFromDays(days);
|
|
161
|
+
const seconds = Math.floor(us / 1e6);
|
|
162
|
+
return new t.PlainDateTime(year, month, day, Math.floor(seconds / 3600), Math.floor(seconds / 60 % 60), seconds % 60, Math.floor(us % 1e6 / 1e3), us % 1e3);
|
|
163
|
+
});
|
|
164
|
+
setBinaryTypeParser(oids.TIMESTAMPTZ, (chunk, start) => {
|
|
165
|
+
const pgMicros = chunk.readBigInt64BE(start);
|
|
166
|
+
if (pgMicros === INFINITY_US || pgMicros === NEG_INFINITY_US) failTemporalInfinity("timestamptz");
|
|
167
|
+
const instant = t.Instant.fromEpochNanoseconds((pgMicros + PG_EPOCH_US) * 1000n);
|
|
168
|
+
if (sessionTimeZone === "UTC") return instant.toZonedDateTimeISO("UTC");
|
|
169
|
+
const zoned = instant.toZonedDateTimeISO(sessionTimeZone);
|
|
170
|
+
const { offset } = zoned;
|
|
171
|
+
return offset === "+00:00" ? zoned.withTimeZone("UTC") : zoned.withTimeZone(offset);
|
|
172
|
+
});
|
|
216
173
|
}
|
|
217
|
-
// Microseconds between the Postgres epoch (2000-01-01) and the Unix epoch (1970-01-01)
|
|
218
174
|
const PG_EPOCH_US = 946684800000000n;
|
|
219
|
-
const PG_EPOCH_DAYS =
|
|
175
|
+
const PG_EPOCH_DAYS = 10957;
|
|
220
176
|
const US_PER_DAY = 86400000000n;
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
const NEG_INFINITY_DAYS = -0x80000000;
|
|
226
|
-
// pg's builtin oids, inlined to avoid importing pg here (these are protocol constants)
|
|
177
|
+
const INFINITY_US = 9223372036854775807n;
|
|
178
|
+
const NEG_INFINITY_US = -9223372036854775808n;
|
|
179
|
+
const INFINITY_DAYS = 2147483647;
|
|
180
|
+
const NEG_INFINITY_DAYS = -2147483648;
|
|
227
181
|
const oids = {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
182
|
+
BOOL: 16,
|
|
183
|
+
BYTEA: 17,
|
|
184
|
+
INT8: 20,
|
|
185
|
+
INT2: 21,
|
|
186
|
+
INT4: 23,
|
|
187
|
+
TEXT: 25,
|
|
188
|
+
OID: 26,
|
|
189
|
+
JSON: 114,
|
|
190
|
+
FLOAT4: 700,
|
|
191
|
+
FLOAT8: 701,
|
|
192
|
+
BPCHAR: 1042,
|
|
193
|
+
VARCHAR: 1043,
|
|
194
|
+
DATE: 1082,
|
|
195
|
+
TIMESTAMP: 1114,
|
|
196
|
+
TIMESTAMPTZ: 1184,
|
|
197
|
+
NUMERIC: 1700,
|
|
198
|
+
UUID: 2950,
|
|
199
|
+
JSONB: 3802
|
|
246
200
|
};
|
|
247
|
-
const parsers = new Map();
|
|
248
|
-
// The standard scalar builtins; values match what the classic text path produces for the same
|
|
249
|
-
// column, i.e. int8/numeric stay strings, bytea stays a Buffer (byte-exact, where classic pg's
|
|
250
|
-
// binary handling is utf8-lossy)
|
|
201
|
+
const parsers = /* @__PURE__ */ new Map();
|
|
251
202
|
setBinaryTypeParser(oids.BOOL, (chunk, start) => chunk[start] !== 0);
|
|
252
203
|
setBinaryTypeParser(oids.INT2, (chunk, start) => chunk.readInt16BE(start));
|
|
253
204
|
setBinaryTypeParser(oids.INT4, (chunk, start) => chunk.readInt32BE(start));
|
|
@@ -258,225 +209,204 @@ setBinaryTypeParser(oids.FLOAT8, (chunk, start) => chunk.readDoubleBE(start));
|
|
|
258
209
|
setBinaryTypeParser(oids.TEXT, binaryTextParser);
|
|
259
210
|
setBinaryTypeParser(oids.VARCHAR, binaryTextParser);
|
|
260
211
|
setBinaryTypeParser(oids.BPCHAR, binaryTextParser);
|
|
261
|
-
setBinaryTypeParser(19
|
|
212
|
+
setBinaryTypeParser(19, binaryTextParser);
|
|
262
213
|
setBinaryTypeParser(oids.UUID, (chunk, start) => renderUuid(chunk, start));
|
|
263
214
|
setBinaryTypeParser(oids.BYTEA, copyBytes);
|
|
264
215
|
setBinaryTypeParser(oids.JSON, (chunk, start, length) => JSON.parse(chunk.toString("utf8", start, start + length)));
|
|
265
|
-
// jsonb payloads have a 1-byte version prefix before the JSON text
|
|
266
216
|
setBinaryTypeParser(oids.JSONB, (chunk, start, length) => JSON.parse(chunk.toString("utf8", start + 1, start + length)));
|
|
267
217
|
setBinaryTypeParser(oids.NUMERIC, renderNumeric);
|
|
268
|
-
setBinaryTypeParser(3614
|
|
269
|
-
setBinaryTypeParser(3910
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return text === "infinity" || text === "-infinity" ? text : `"${text}"`;
|
|
218
|
+
setBinaryTypeParser(3614, renderTsVector);
|
|
219
|
+
setBinaryTypeParser(3910, (chunk, start) => renderRange(chunk, start, (c, s) => {
|
|
220
|
+
const text = renderTimestamp(c.readBigInt64BE(s), "+00");
|
|
221
|
+
return text === "infinity" || text === "-infinity" ? text : `"${text}"`;
|
|
273
222
|
}));
|
|
274
|
-
|
|
275
|
-
setBinaryTypeParser(1083 /* time */, (chunk, start) => renderTimeOfDay(Number(chunk.readBigInt64BE(start))));
|
|
276
|
-
// The standard `_type` array oids; elements decode through the registry (see binaryArrayParser)
|
|
223
|
+
setBinaryTypeParser(1083, (chunk, start) => renderTimeOfDay(Number(chunk.readBigInt64BE(start))));
|
|
277
224
|
for (const arrayOid of [
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
])
|
|
299
|
-
setBinaryTypeParser(arrayOid, binaryArrayParser);
|
|
300
|
-
}
|
|
301
|
-
// Default the date/time oids to Date-mode semantics; `setupLatestPgTypes` re-registers the
|
|
302
|
-
// Temporal-mode parsers when the app runs with `temporal` configured
|
|
225
|
+
1e3,
|
|
226
|
+
1001,
|
|
227
|
+
1005,
|
|
228
|
+
1007,
|
|
229
|
+
1009,
|
|
230
|
+
1014,
|
|
231
|
+
1015,
|
|
232
|
+
1016,
|
|
233
|
+
1021,
|
|
234
|
+
1022,
|
|
235
|
+
1182,
|
|
236
|
+
1183,
|
|
237
|
+
1115,
|
|
238
|
+
1185,
|
|
239
|
+
1231,
|
|
240
|
+
2951,
|
|
241
|
+
199,
|
|
242
|
+
3807,
|
|
243
|
+
3643,
|
|
244
|
+
3911
|
|
245
|
+
]) setBinaryTypeParser(arrayOid, binaryArrayParser);
|
|
303
246
|
setBinaryTypeParser(oids.DATE, binaryDateToDate);
|
|
304
247
|
setBinaryTypeParser(oids.TIMESTAMP, binaryTimestampToDate);
|
|
305
248
|
setBinaryTypeParser(oids.TIMESTAMPTZ, binaryTimestamptzToDate);
|
|
306
249
|
/** Splits pg timestamp µs into days-since-1970 and µs-within-day, flooring correctly pre-epoch. */
|
|
307
250
|
function splitPgMicros(pgMicros) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
251
|
+
const epochMicros = pgMicros + PG_EPOCH_US;
|
|
252
|
+
let days = epochMicros / US_PER_DAY;
|
|
253
|
+
let micros = epochMicros % US_PER_DAY;
|
|
254
|
+
if (micros < 0n) {
|
|
255
|
+
days -= 1n;
|
|
256
|
+
micros += US_PER_DAY;
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
days: Number(days),
|
|
260
|
+
us: Number(micros)
|
|
261
|
+
};
|
|
316
262
|
}
|
|
317
263
|
/** Fails on the ±infinity wire sentinels, which have no Temporal representation. */
|
|
318
264
|
function failTemporalInfinity(type) {
|
|
319
|
-
|
|
265
|
+
return fail$1(`joist-orm: cannot represent an 'infinity' ${type} as a Temporal value`);
|
|
320
266
|
}
|
|
321
267
|
/** Reads a float4 as its shortest round-trip decimal, matching pg's shortest text output. */
|
|
322
268
|
function readFloat4(chunk, start) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
// (binary32 needs at most 9 significant decimal digits)
|
|
332
|
-
for (let precision = 1; precision <= 9; precision++) {
|
|
333
|
-
const shortest = Number(value.toPrecision(precision));
|
|
334
|
-
if (Math.fround(shortest) === value)
|
|
335
|
-
return shortest;
|
|
336
|
-
}
|
|
337
|
-
return value;
|
|
269
|
+
const value = chunk.readFloatBE(start);
|
|
270
|
+
if (!Number.isFinite(value)) return value;
|
|
271
|
+
if (value === 0) return value;
|
|
272
|
+
for (let precision = 1; precision <= 9; precision++) {
|
|
273
|
+
const shortest = Number(value.toPrecision(precision));
|
|
274
|
+
if (Math.fround(shortest) === value) return shortest;
|
|
275
|
+
}
|
|
276
|
+
return value;
|
|
338
277
|
}
|
|
339
278
|
/** Copies the exact cell bytes, i.e. for bytea values. */
|
|
340
279
|
function copyBytes(chunk, start, length) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
280
|
+
const cell = Buffer.allocUnsafe(length);
|
|
281
|
+
chunk.copy(cell, 0, start, start + length);
|
|
282
|
+
return cell;
|
|
344
283
|
}
|
|
345
284
|
/** Renders a binary uuid (16 bytes) as the canonical dashed-hex string. */
|
|
346
285
|
function renderUuid(chunk, start) {
|
|
347
|
-
|
|
348
|
-
|
|
286
|
+
const hex = chunk.toString("hex", start, start + 16);
|
|
287
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
349
288
|
}
|
|
350
289
|
/** Renders a binary numeric (base-10000 digit groups) to pg's text form, i.e. `-12.340`. */
|
|
351
290
|
function renderNumeric(chunk, start) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
for (let k = 1; fraction.length < dscale; k++) {
|
|
375
|
-
const i = weight + k;
|
|
376
|
-
const group = i >= 0 && i < nDigits ? chunk.readUInt16BE(start + 8 + i * 2) : 0;
|
|
377
|
-
fraction += String(group).padStart(4, "0");
|
|
378
|
-
}
|
|
379
|
-
fraction = fraction.slice(0, dscale);
|
|
380
|
-
return `${sign === 0x4000 ? "-" : ""}${integer}.${fraction}`;
|
|
291
|
+
const nDigits = chunk.readInt16BE(start);
|
|
292
|
+
const weight = chunk.readInt16BE(start + 2);
|
|
293
|
+
const sign = chunk.readUInt16BE(start + 4);
|
|
294
|
+
const dscale = chunk.readUInt16BE(start + 6);
|
|
295
|
+
if (sign === 49152) return "NaN";
|
|
296
|
+
if (sign === 53248) return "Infinity";
|
|
297
|
+
if (sign === 61440) return "-Infinity";
|
|
298
|
+
let integer = "";
|
|
299
|
+
for (let w = weight, i = 0; w >= 0; w--, i++) {
|
|
300
|
+
const group = i < nDigits ? chunk.readUInt16BE(start + 8 + i * 2) : 0;
|
|
301
|
+
integer += integer === "" ? String(group) : String(group).padStart(4, "0");
|
|
302
|
+
}
|
|
303
|
+
if (integer === "") integer = "0";
|
|
304
|
+
if (dscale === 0) return sign === 16384 ? `-${integer}` : integer;
|
|
305
|
+
let fraction = "";
|
|
306
|
+
for (let k = 1; fraction.length < dscale; k++) {
|
|
307
|
+
const i = weight + k;
|
|
308
|
+
const group = i >= 0 && i < nDigits ? chunk.readUInt16BE(start + 8 + i * 2) : 0;
|
|
309
|
+
fraction += String(group).padStart(4, "0");
|
|
310
|
+
}
|
|
311
|
+
fraction = fraction.slice(0, dscale);
|
|
312
|
+
return `${sign === 16384 ? "-" : ""}${integer}.${fraction}`;
|
|
381
313
|
}
|
|
382
314
|
/** Renders a binary tsvector (lexeme cstrings + position/weight words) to pg's text form, i.e. `'new':2A`. */
|
|
383
315
|
function renderTsVector(chunk, start) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
return lexemes.join(" ");
|
|
316
|
+
const count = chunk.readInt32BE(start);
|
|
317
|
+
let pos = start + 4;
|
|
318
|
+
const lexemes = [];
|
|
319
|
+
for (let i = 0; i < count; i++) {
|
|
320
|
+
const end = chunk.indexOf(0, pos);
|
|
321
|
+
const word = chunk.toString("utf8", pos, end).replace(/\\/g, "\\\\").replace(/'/g, "''");
|
|
322
|
+
pos = end + 1;
|
|
323
|
+
const nPositions = chunk.readUInt16BE(pos);
|
|
324
|
+
pos += 2;
|
|
325
|
+
const positions = [];
|
|
326
|
+
for (let p = 0; p < nPositions; p++) {
|
|
327
|
+
const entry = chunk.readUInt16BE(pos);
|
|
328
|
+
pos += 2;
|
|
329
|
+
positions.push(`${entry & 16383}${weightSuffixes[entry >> 14]}`);
|
|
330
|
+
}
|
|
331
|
+
lexemes.push(positions.length > 0 ? `'${word}':${positions.join(",")}` : `'${word}'`);
|
|
332
|
+
}
|
|
333
|
+
return lexemes.join(" ");
|
|
404
334
|
}
|
|
405
|
-
const weightSuffixes = [
|
|
335
|
+
const weightSuffixes = [
|
|
336
|
+
"",
|
|
337
|
+
"C",
|
|
338
|
+
"B",
|
|
339
|
+
"A"
|
|
340
|
+
];
|
|
406
341
|
/** Renders a binary range (flags byte + length-prefixed bounds) to pg's text form, i.e. `["a","b")`. */
|
|
407
342
|
function renderRange(chunk, start, bound) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
upper = bound(chunk, pos + 4);
|
|
421
|
-
}
|
|
422
|
-
return `${flags & 0x02 ? "[" : "("}${lower},${upper}${flags & 0x04 ? "]" : ")"}`;
|
|
423
|
-
}
|
|
424
|
-
/** Renders days-since-1970 as pg's date text, i.e. `2020-01-02` or `0001-01-01 BC`. */
|
|
425
|
-
function renderDate(epochDays) {
|
|
426
|
-
const { text, bc } = renderDatePart(epochDays);
|
|
427
|
-
return bc ? `${text} BC` : text;
|
|
343
|
+
const flags = chunk[start];
|
|
344
|
+
if (flags & 1) return "empty";
|
|
345
|
+
let pos = start + 1;
|
|
346
|
+
let lower = "";
|
|
347
|
+
let upper = "";
|
|
348
|
+
if (!(flags & 8)) {
|
|
349
|
+
const len = chunk.readInt32BE(pos);
|
|
350
|
+
lower = bound(chunk, pos + 4);
|
|
351
|
+
pos += 4 + len;
|
|
352
|
+
}
|
|
353
|
+
if (!(flags & 16)) upper = bound(chunk, pos + 4);
|
|
354
|
+
return `${flags & 2 ? "[" : "("}${lower},${upper}${flags & 4 ? "]" : ")"}`;
|
|
428
355
|
}
|
|
429
356
|
/**
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
357
|
+
* Renders the `YYYY-MM-DD` part, converting astronomical years to pg's BC numbering
|
|
358
|
+
* (astronomical year 0 = `0001 BC`); timestamps append the ` BC` marker after the time/zone.
|
|
359
|
+
*/
|
|
433
360
|
function renderDatePart(epochDays) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
361
|
+
const [year, month, day] = civilFromDays(epochDays);
|
|
362
|
+
const bc = year <= 0;
|
|
363
|
+
const displayYear = bc ? 1 - year : year;
|
|
364
|
+
return {
|
|
365
|
+
text: `${String(displayYear).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
|
|
366
|
+
bc
|
|
367
|
+
};
|
|
441
368
|
}
|
|
442
369
|
/** Renders a binary timestamp (µs since 2000-01-01) to pg's text form, i.e. `2020-01-02 03:04:05.678+00`. */
|
|
443
370
|
function renderTimestamp(pgMicros, suffix) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const { text: date, bc } = renderDatePart(days);
|
|
450
|
-
return `${date} ${renderTimeOfDay(us)}${suffix}${bc ? " BC" : ""}`;
|
|
371
|
+
if (pgMicros === INFINITY_US) return "infinity";
|
|
372
|
+
if (pgMicros === NEG_INFINITY_US) return "-infinity";
|
|
373
|
+
const { days, us } = splitPgMicros(pgMicros);
|
|
374
|
+
const { text: date, bc } = renderDatePart(days);
|
|
375
|
+
return `${date} ${renderTimeOfDay(us)}${suffix}${bc ? " BC" : ""}`;
|
|
451
376
|
}
|
|
452
377
|
/** Renders µs-since-midnight to pg's time text, i.e. `03:04:05.678` with trailing zeros trimmed. */
|
|
453
378
|
function renderTimeOfDay(us) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
fraction = `.${String(usPart).padStart(6, "0")}`.replace(/0+$/, "");
|
|
463
|
-
return `${hh}:${mm}:${ss}${fraction}`;
|
|
379
|
+
const seconds = Math.floor(us / 1e6);
|
|
380
|
+
const hh = String(Math.floor(seconds / 3600)).padStart(2, "0");
|
|
381
|
+
const mm = String(Math.floor(seconds / 60 % 60)).padStart(2, "0");
|
|
382
|
+
const ss = String(seconds % 60).padStart(2, "0");
|
|
383
|
+
let fraction = "";
|
|
384
|
+
const usPart = us % 1e6;
|
|
385
|
+
if (usPart > 0) fraction = `.${String(usPart).padStart(6, "0")}`.replace(/0+$/, "");
|
|
386
|
+
return `${hh}:${mm}:${ss}${fraction}`;
|
|
464
387
|
}
|
|
465
388
|
/** Converts days-since-1970 to a civil [year, month, day], i.e. Howard Hinnant's algorithm. */
|
|
466
389
|
function civilFromDays(epochDays) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
390
|
+
const z = epochDays + 719468;
|
|
391
|
+
const era = Math.floor(z / 146097);
|
|
392
|
+
const doe = z - era * 146097;
|
|
393
|
+
const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);
|
|
394
|
+
const y = yoe + era * 400;
|
|
395
|
+
const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));
|
|
396
|
+
const mp = Math.floor((5 * doy + 2) / 153);
|
|
397
|
+
const d = doy - Math.floor((153 * mp + 2) / 5) + 1;
|
|
398
|
+
const m = mp < 10 ? mp + 3 : mp - 9;
|
|
399
|
+
return [
|
|
400
|
+
m <= 2 ? y + 1 : y,
|
|
401
|
+
m,
|
|
402
|
+
d
|
|
403
|
+
];
|
|
477
404
|
}
|
|
478
405
|
/** Throws an `Error` with `message`. */
|
|
479
|
-
function fail(message) {
|
|
480
|
-
|
|
406
|
+
function fail$1(message) {
|
|
407
|
+
throw new Error(message);
|
|
481
408
|
}
|
|
409
|
+
//#endregion
|
|
410
|
+
export { binaryArrayParser, binaryDateToDate, binaryTextParser, binaryTimestampToDate, binaryTimestamptzToDate, getBinaryTypeParser, registerDatabaseBinaryParsers, registerTemporalBinaryParsers, setBinaryTypeParser, setSessionTimeZone };
|
|
411
|
+
|
|
482
412
|
//# sourceMappingURL=binaryParsers.js.map
|