postgrejs 3.9.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -1
- package/connection/connection.js +6 -2
- package/connection/intl-connection.d.ts +18 -1
- package/connection/intl-connection.js +104 -11
- package/data-types/circle-type.js +5 -2
- package/data-types/classes/geometric.d.ts +49 -7
- package/data-types/classes/geometric.js +59 -11
- package/data-types/classes/interval.d.ts +40 -16
- package/data-types/classes/interval.js +68 -40
- package/data-types/classes/numeric.d.ts +6 -0
- package/data-types/classes/numeric.js +8 -0
- package/data-types/classes/range.d.ts +6 -1
- package/data-types/classes/range.js +6 -1
- package/data-types/interval-type.js +1 -1
- package/data-types/money-type.js +11 -2
- package/data-types/numeric-type.js +21 -10
- package/data-types/temporal-types.d.ts +19 -0
- package/data-types/temporal-types.js +520 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/interfaces/data-mapping-options.d.ts +99 -4
- package/package.json +1 -1
- package/util/connection-config.js +11 -1
- package/util/decimal-as-string.d.ts +11 -0
- package/util/decimal-as-string.js +30 -0
- package/util/fetch-as-string.d.ts +33 -0
- package/util/fetch-as-string.js +122 -0
- package/util/get-parsers.js +18 -1
- package/util/resolve-column-formats.d.ts +5 -11
- package/util/resolve-column-formats.js +24 -24
- package/util/stringify-for-sql.js +6 -5
- package/util/temporal.d.ts +47 -0
- package/util/temporal.js +61 -0
- package/util/unspecified-param.d.ts +18 -3
- package/util/unspecified-param.js +21 -4
|
@@ -14,20 +14,29 @@ function pad2(v) {
|
|
|
14
14
|
* zone, can add one to a timestamp. Anything here that answered "how many
|
|
15
15
|
* milliseconds is this" would have to invent those lengths.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
17
|
+
* It carries the fields that have a value and no others, which is what
|
|
18
|
+
* the wire format carries too - three quantities, and a zero among them
|
|
19
|
+
* is not a field:
|
|
18
20
|
*
|
|
19
21
|
* ```ts
|
|
20
22
|
* const iv = new Interval({ days: 1, hours: 2 });
|
|
21
|
-
* iv
|
|
23
|
+
* Object.keys(iv); // ['days', 'hours']
|
|
24
|
+
* iv.minutes; // undefined
|
|
22
25
|
* String(iv); // '1 day 02:00:00'
|
|
23
26
|
* iv.toISOString(); // 'P0Y0M1DT2H0M0S'
|
|
24
27
|
* ```
|
|
25
28
|
*
|
|
26
|
-
* The field names are `pg`'s (`postgres-interval`), so code
|
|
27
|
-
* reads the same values from the same places
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* The field names and the shape are `pg`'s (`postgres-interval`), so code
|
|
30
|
+
* ported from it reads the same values from the same places and a
|
|
31
|
+
* `JSON.stringify` of a row says the same thing. The difference is that
|
|
32
|
+
* this has a `toString()` - `pg`'s object gives `[object Object]` in a log
|
|
33
|
+
* line - and a `toPostgres()`, so the value can be sent back.
|
|
34
|
+
*
|
|
35
|
+
* The fields are optional, so `iv.hours + 1` does not compile without
|
|
36
|
+
* saying what an absent one means (`(iv.hours ?? 0) + 1`). That is the
|
|
37
|
+
* point: which fields a value has depends on the value, not on the type,
|
|
38
|
+
* and the older shape - every field present and 0 - hid that behind
|
|
39
|
+
* numbers nobody wrote.
|
|
31
40
|
*
|
|
32
41
|
* `milliseconds` carries the sub-second part and can be fractional, since
|
|
33
42
|
* PostgreSQL stores microseconds: `interval '0.000001 seconds'` is
|
|
@@ -36,25 +45,35 @@ function pad2(v) {
|
|
|
36
45
|
* A negative interval carries the sign on each field that has one, the way
|
|
37
46
|
* the server prints it: `interval '-1 day -2 hours'` is
|
|
38
47
|
* `{ days: -1, hours: -2 }`.
|
|
48
|
+
*
|
|
49
|
+
* `toPostgres()` is `pg`'s convention for "write yourself back", so a
|
|
50
|
+
* value read here can be passed to any encoder that follows it - this
|
|
51
|
+
* client's own parameter path never needed it, since it knows the class.
|
|
39
52
|
*/
|
|
40
53
|
export class Interval {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
seconds;
|
|
48
|
-
/** The sub-second part, fractional down to a microsecond. */
|
|
49
|
-
milliseconds;
|
|
54
|
+
/**
|
|
55
|
+
* Keeps the fields that have a value. A zero is not one: it is what
|
|
56
|
+
* every field of every interval would otherwise be filled with, and
|
|
57
|
+
* `{ days: 1 }` says what `interval '1 day'` is far better than the
|
|
58
|
+
* same thing plus six zeroes nobody wrote.
|
|
59
|
+
*/
|
|
50
60
|
constructor(fields) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
if (!fields)
|
|
62
|
+
return;
|
|
63
|
+
if (fields.years)
|
|
64
|
+
this.years = fields.years;
|
|
65
|
+
if (fields.months)
|
|
66
|
+
this.months = fields.months;
|
|
67
|
+
if (fields.days)
|
|
68
|
+
this.days = fields.days;
|
|
69
|
+
if (fields.hours)
|
|
70
|
+
this.hours = fields.hours;
|
|
71
|
+
if (fields.minutes)
|
|
72
|
+
this.minutes = fields.minutes;
|
|
73
|
+
if (fields.seconds)
|
|
74
|
+
this.seconds = fields.seconds;
|
|
75
|
+
if (fields.milliseconds)
|
|
76
|
+
this.milliseconds = fields.milliseconds;
|
|
58
77
|
}
|
|
59
78
|
/**
|
|
60
79
|
* Builds one from the three quantities the wire format carries: a signed
|
|
@@ -85,21 +104,22 @@ export class Interval {
|
|
|
85
104
|
}
|
|
86
105
|
/** The whole month count, as the wire format carries it. */
|
|
87
106
|
get totalMonths() {
|
|
88
|
-
return this.years * 12 + this.months;
|
|
107
|
+
return (this.years ?? 0) * 12 + (this.months ?? 0);
|
|
89
108
|
}
|
|
90
109
|
/** The time part as a signed microsecond count, as the wire format carries it. */
|
|
91
110
|
get totalMicroseconds() {
|
|
92
111
|
// Split so that a fractional hours/minutes/seconds is folded in rather
|
|
93
112
|
// than silently truncated, while whole values stay exact past what a
|
|
94
113
|
// double can hold.
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
(
|
|
114
|
+
const h = this.hours ?? 0;
|
|
115
|
+
const mi = this.minutes ?? 0;
|
|
116
|
+
const sec = this.seconds ?? 0;
|
|
117
|
+
const whole = Math.trunc(h) * 3600 + Math.trunc(mi) * 60 + Math.trunc(sec);
|
|
118
|
+
const fraction = (h - Math.trunc(h)) * 3600 +
|
|
119
|
+
(mi - Math.trunc(mi)) * 60 +
|
|
120
|
+
(sec - Math.trunc(sec));
|
|
101
121
|
return (BigInt(whole) * US_PER_SECOND +
|
|
102
|
-
BigInt(Math.round(fraction * 1e6 + this.milliseconds * 1000)));
|
|
122
|
+
BigInt(Math.round(fraction * 1e6 + (this.milliseconds ?? 0) * 1000)));
|
|
103
123
|
}
|
|
104
124
|
/**
|
|
105
125
|
* The interval as PostgreSQL itself prints it under the default
|
|
@@ -155,27 +175,35 @@ export class Interval {
|
|
|
155
175
|
left -= minutes * US_PER_MINUTE;
|
|
156
176
|
const seconds = left / US_PER_SECOND;
|
|
157
177
|
left -= seconds * US_PER_SECOND;
|
|
178
|
+
// The sign goes on the components that have a value, not on all of
|
|
179
|
+
// them: `interval '-1 day -2 hours'` is `P0Y0M-1DT-2H0M0S`, and
|
|
180
|
+
// signing the empty ones printed `-0M-0S` - which is what `pg` does
|
|
181
|
+
// not write and no reader expects.
|
|
158
182
|
const sign = negative ? '-' : '';
|
|
159
|
-
|
|
183
|
+
const signed = (v) => (v ? sign : '') + v;
|
|
184
|
+
let secondsPart = (seconds || left ? sign : '') + seconds;
|
|
160
185
|
if (left)
|
|
161
186
|
secondsPart += '.' + String(left).padStart(6, '0').replace(/0+$/, '');
|
|
162
187
|
return ('P' +
|
|
163
|
-
this.years +
|
|
188
|
+
(this.years ?? 0) +
|
|
164
189
|
'Y' +
|
|
165
|
-
this.months +
|
|
190
|
+
(this.months ?? 0) +
|
|
166
191
|
'M' +
|
|
167
|
-
this.days +
|
|
192
|
+
(this.days ?? 0) +
|
|
168
193
|
'DT' +
|
|
169
|
-
|
|
170
|
-
hours +
|
|
194
|
+
signed(hours) +
|
|
171
195
|
'H' +
|
|
172
|
-
|
|
173
|
-
minutes +
|
|
196
|
+
signed(minutes) +
|
|
174
197
|
'M' +
|
|
175
198
|
secondsPart +
|
|
176
199
|
'S');
|
|
177
200
|
}
|
|
178
|
-
|
|
201
|
+
/**
|
|
202
|
+
* The literal PostgreSQL reads back, for an encoder that asks the value
|
|
203
|
+
* how to write itself - `pg`'s convention, and the reason a value this
|
|
204
|
+
* client decoded can be handed straight to one.
|
|
205
|
+
*/
|
|
206
|
+
toPostgres() {
|
|
179
207
|
return this.toString();
|
|
180
208
|
}
|
|
181
209
|
}
|
|
@@ -29,6 +29,12 @@ export declare class Numeric {
|
|
|
29
29
|
/** The decimal exactly as PostgreSQL wrote it; casts back through `::numeric`. */
|
|
30
30
|
toString(): string;
|
|
31
31
|
toJSON(): string;
|
|
32
|
+
/**
|
|
33
|
+
* The literal PostgreSQL reads back, for an encoder that asks the value
|
|
34
|
+
* how to write itself - `pg`'s convention, and the reason a value this
|
|
35
|
+
* client decoded can be handed straight to one.
|
|
36
|
+
*/
|
|
37
|
+
toPostgres(): string;
|
|
32
38
|
/** The value as a double, which is lossy - that is the whole point of this class. */
|
|
33
39
|
toNumber(): number;
|
|
34
40
|
/**
|
|
@@ -35,6 +35,14 @@ export class Numeric {
|
|
|
35
35
|
toJSON() {
|
|
36
36
|
return this.value;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The literal PostgreSQL reads back, for an encoder that asks the value
|
|
40
|
+
* how to write itself - `pg`'s convention, and the reason a value this
|
|
41
|
+
* client decoded can be handed straight to one.
|
|
42
|
+
*/
|
|
43
|
+
toPostgres() {
|
|
44
|
+
return this.toString();
|
|
45
|
+
}
|
|
38
46
|
/** The value as a double, which is lossy - that is the whole point of this class. */
|
|
39
47
|
toNumber() {
|
|
40
48
|
return parseFloat(this.value);
|
|
@@ -70,5 +70,10 @@ export declare class Range<T = any> {
|
|
|
70
70
|
* `::int4range` and reads the way the same value does in psql.
|
|
71
71
|
*/
|
|
72
72
|
toString(): string;
|
|
73
|
-
|
|
73
|
+
/**
|
|
74
|
+
* The literal PostgreSQL reads back, for an encoder that asks the value
|
|
75
|
+
* how to write itself - `pg`'s convention, and the reason a value this
|
|
76
|
+
* client decoded can be handed straight to one.
|
|
77
|
+
*/
|
|
78
|
+
toPostgres(): string;
|
|
74
79
|
}
|
|
@@ -105,7 +105,12 @@ export class Range {
|
|
|
105
105
|
formatBound(this.upper) +
|
|
106
106
|
(this.upperInclusive && this.upper !== null ? ']' : ')'));
|
|
107
107
|
}
|
|
108
|
-
|
|
108
|
+
/**
|
|
109
|
+
* The literal PostgreSQL reads back, for an encoder that asks the value
|
|
110
|
+
* how to write itself - `pg`'s convention, and the reason a value this
|
|
111
|
+
* client decoded can be handed straight to one.
|
|
112
|
+
*/
|
|
113
|
+
toPostgres() {
|
|
109
114
|
return this.toString();
|
|
110
115
|
}
|
|
111
116
|
}
|
|
@@ -60,7 +60,7 @@ export const IntervalType = {
|
|
|
60
60
|
encodeBinary(buf, v) {
|
|
61
61
|
const iv = toInterval(v);
|
|
62
62
|
buf.writeBigInt64BE(iv.totalMicroseconds);
|
|
63
|
-
buf.writeInt32BE(iv.days);
|
|
63
|
+
buf.writeInt32BE(iv.days ?? 0);
|
|
64
64
|
buf.writeInt32BE(iv.totalMonths);
|
|
65
65
|
},
|
|
66
66
|
decodeText: parseIntervalText,
|
package/data-types/money-type.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
2
|
import { readBigInt64BE } from '../util/bigint-methods.js';
|
|
3
|
+
import { wantsDecimalString } from '../util/decimal-as-string.js';
|
|
3
4
|
import { Numeric } from './classes/numeric.js';
|
|
4
5
|
import { trimTrailingZeros } from './numeric-type.js';
|
|
5
6
|
/**
|
|
@@ -142,11 +143,19 @@ export const MoneyType = {
|
|
|
142
143
|
const v = typeof buf.readBigInt64BE === 'function'
|
|
143
144
|
? buf.readBigInt64BE(offset)
|
|
144
145
|
: readBigInt64BE(buf, offset);
|
|
145
|
-
|
|
146
|
+
const s = moneyToString(v, formatOf(options).scale);
|
|
147
|
+
// The exact decimal is what the decode already built; handing it
|
|
148
|
+
// back is the whole of `decimalAsString`.
|
|
149
|
+
return wantsDecimalString(options, DataTypeOIDs.money)
|
|
150
|
+
? s
|
|
151
|
+
: toNumberOrNumeric(s);
|
|
146
152
|
},
|
|
147
153
|
decodeText(s, options) {
|
|
148
154
|
const { scale } = formatOf(options);
|
|
149
|
-
|
|
155
|
+
const exact = moneyToString(parseMoneyText(s), scale);
|
|
156
|
+
return wantsDecimalString(options, DataTypeOIDs.money)
|
|
157
|
+
? exact
|
|
158
|
+
: toNumberOrNumeric(exact);
|
|
150
159
|
},
|
|
151
160
|
/**
|
|
152
161
|
* Never inferred: a JavaScript number is `int4`, `float8` or `numeric`
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
2
|
import { assertCoercedNumber } from '../util/assert-integer.js';
|
|
3
|
+
import { wantsDecimalString } from '../util/decimal-as-string.js';
|
|
3
4
|
import { fastParseFloatBuffer } from '../util/fast-parsefloat.js';
|
|
4
5
|
import { Numeric } from './classes/numeric.js';
|
|
5
6
|
const NUMERIC_NEG = 0x4000;
|
|
@@ -130,7 +131,7 @@ export const NumericType = {
|
|
|
130
131
|
buf.writeInt16BE(digits[i]);
|
|
131
132
|
}
|
|
132
133
|
},
|
|
133
|
-
decodeBinary(v, offset = 0) {
|
|
134
|
+
decodeBinary(v, offset = 0, _len, options) {
|
|
134
135
|
const len = v.readInt16BE(offset);
|
|
135
136
|
const weight = v.readInt16BE(offset + 2);
|
|
136
137
|
// sign is a bitmask (0x0000/0x4000/0xC000/0xD000/0xF000), not a two's
|
|
@@ -138,12 +139,16 @@ export const NumericType = {
|
|
|
138
139
|
// values (which set the top bit) never compare equal to the constants.
|
|
139
140
|
const sign = v.readUInt16BE(offset + 4);
|
|
140
141
|
const scale = v.readInt16BE(offset + 6);
|
|
142
|
+
// The words PostgreSQL itself writes for these, when the caller
|
|
143
|
+
// asked for strings: a decimal string type that sometimes answers a
|
|
144
|
+
// number would have to be checked for on every value.
|
|
145
|
+
const asString = wantsDecimalString(options, DataTypeOIDs.numeric);
|
|
141
146
|
if (sign === NUMERIC_NAN)
|
|
142
|
-
return NaN;
|
|
147
|
+
return asString ? 'NaN' : NaN;
|
|
143
148
|
if (sign === NUMERIC_PINF)
|
|
144
|
-
return Infinity;
|
|
149
|
+
return asString ? 'Infinity' : Infinity;
|
|
145
150
|
if (sign === NUMERIC_NINF)
|
|
146
|
-
return -Infinity;
|
|
151
|
+
return asString ? '-Infinity' : -Infinity;
|
|
147
152
|
const digits = [];
|
|
148
153
|
for (let i = 0; i < len; i++) {
|
|
149
154
|
digits[i] = v.readInt16BE(offset + 8 + i * 2);
|
|
@@ -157,10 +162,13 @@ export const NumericType = {
|
|
|
157
162
|
// and `scale` the digits after the point, which is enough to rule
|
|
158
163
|
// both out. `weight === -1` is a value in [0.0001, 1), far from the
|
|
159
164
|
// exponent that would make JavaScript switch notation.
|
|
160
|
-
if (
|
|
161
|
-
(weight
|
|
165
|
+
if (!asString &&
|
|
166
|
+
((weight >= 0 &&
|
|
167
|
+
weight <= 4 &&
|
|
168
|
+
(weight + 1) * DEC_DIGITS + scale <= 15) ||
|
|
169
|
+
(weight === -1 && scale <= 15)))
|
|
162
170
|
return parseFloat(numString);
|
|
163
|
-
return toNumberOrNumeric(numString);
|
|
171
|
+
return asString ? numString : toNumberOrNumeric(numString);
|
|
164
172
|
},
|
|
165
173
|
encodeText(v) {
|
|
166
174
|
// Handed over as written. Going through a double here would undo
|
|
@@ -169,7 +177,9 @@ export const NumericType = {
|
|
|
169
177
|
// the exponent form a JavaScript number stringifies to.
|
|
170
178
|
return typeof v === 'string' ? v.trim() : String(v);
|
|
171
179
|
},
|
|
172
|
-
decodeText(v) {
|
|
180
|
+
decodeText(v, options) {
|
|
181
|
+
if (wantsDecimalString(options, DataTypeOIDs.numeric))
|
|
182
|
+
return v;
|
|
173
183
|
// NaN and the infinities arrive as those words and stay numbers.
|
|
174
184
|
const n = parseFloat(v);
|
|
175
185
|
if (!Number.isFinite(n))
|
|
@@ -180,8 +190,9 @@ export const NumericType = {
|
|
|
180
190
|
// Eight characters hold at most eight digits, cannot reach 1e21 -
|
|
181
191
|
// which needs twenty-two - and cannot reach 1e-7, which needs `0.`
|
|
182
192
|
// and seven more. So a value this short is carried faithfully by
|
|
183
|
-
// definition and keeps the parser that never builds a string
|
|
184
|
-
|
|
193
|
+
// definition and keeps the parser that never builds a string - but
|
|
194
|
+
// only while a number is what the caller wants back.
|
|
195
|
+
if (len <= 8 && !wantsDecimalString(options, DataTypeOIDs.numeric))
|
|
185
196
|
return fastParseFloatBuffer(buf, offset, len);
|
|
186
197
|
return NumericType.decodeText(buf.toString('latin1', offset, offset + len), options);
|
|
187
198
|
},
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DataTypeMap } from '../data-type-map.js';
|
|
2
|
+
import type { DataType } from '../interfaces/data-type.js';
|
|
3
|
+
import type { OID } from '../types.js';
|
|
4
|
+
import { type TemporalApi } from '../util/temporal.js';
|
|
5
|
+
/** The five types a caller may ask for by OID. */
|
|
6
|
+
export declare const TemporalCapableOIDs: readonly OID[];
|
|
7
|
+
/** Every registration one selected OID stands for. */
|
|
8
|
+
export declare function temporalTypesFor(oid: OID, T: TemporalApi): DataType[];
|
|
9
|
+
/**
|
|
10
|
+
* A type map that decodes the selected date/time types into `Temporal`
|
|
11
|
+
* values, built on top of `base` - which keeps whatever else the caller
|
|
12
|
+
* registered there.
|
|
13
|
+
*
|
|
14
|
+
* The same selection on the same base always gives back the same map.
|
|
15
|
+
* That is not only to save the copy: a prepared statement is reused only
|
|
16
|
+
* while the map it was parsed with is the same object, so handing out a
|
|
17
|
+
* fresh map per statement would quietly empty the statement cache.
|
|
18
|
+
*/
|
|
19
|
+
export declare function temporalTypeMap(selection?: boolean | OID[], base?: DataTypeMap): DataTypeMap;
|