postgrejs 3.7.0 → 3.9.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 +53 -0
- package/README.md +50 -0
- package/connection/connection.d.ts +14 -2
- package/connection/connection.js +70 -10
- package/connection/intl-connection.d.ts +146 -5
- package/connection/intl-connection.js +485 -35
- package/connection/pool.d.ts +30 -6
- package/connection/pool.js +28 -3
- package/connection/prepared-statement.d.ts +11 -0
- package/connection/prepared-statement.js +39 -5
- package/data-type-map.d.ts +16 -0
- package/data-type-map.js +56 -12
- package/data-types/classes/numeric.d.ts +8 -4
- package/data-types/classes/numeric.js +8 -4
- package/data-types/date-type.js +2 -2
- package/data-types/int2-vector-type.js +4 -1
- package/data-types/money-type.d.ts +38 -0
- package/data-types/money-type.js +203 -0
- package/data-types/numeric-type.d.ts +2 -0
- package/data-types/numeric-type.js +1 -1
- package/data-types/oid-vector-type.js +4 -1
- package/data-types/timestamp-type.js +2 -2
- package/data-types/timestamptz-type.js +11 -2
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/interfaces/data-mapping-options.d.ts +27 -0
- package/interfaces/database-connection-params.d.ts +32 -5
- package/interfaces/query-options.d.ts +32 -0
- package/interfaces/script-execute-options.d.ts +32 -0
- package/interfaces/transaction-options.d.ts +27 -0
- package/interfaces/transaction-options.js +1 -0
- package/package.json +1 -1
- package/protocol/database-error.d.ts +15 -0
- package/protocol/database-error.js +19 -0
- package/protocol/frontend.js +17 -0
- package/protocol/pg-socket.js +7 -10
- package/util/array-leaf.d.ts +10 -0
- package/util/array-leaf.js +22 -0
- package/util/date-style.d.ts +47 -0
- package/util/date-style.js +123 -0
- package/util/decode-binaryarray.js +8 -1
- package/util/encode-binaryarray.d.ts +12 -1
- package/util/encode-binaryarray.js +13 -2
- package/util/format-datetime.d.ts +18 -0
- package/util/format-datetime.js +33 -0
- package/util/parse-datetime.d.ts +4 -3
- package/util/parse-datetime.js +34 -9
- package/util/resolve-column-formats.js +4 -0
- package/util/row-decoder.d.ts +24 -4
- package/util/row-decoder.js +25 -10
- package/util/sql-tag.js +16 -0
- package/util/stringify-for-sql.js +7 -0
- package/util/transaction-command.d.ts +7 -0
- package/util/transaction-command.js +32 -0
- package/util/unspecified-param.d.ts +27 -0
- package/util/unspecified-param.js +31 -0
package/connection/pool.d.ts
CHANGED
|
@@ -5,16 +5,21 @@ import type { QueryResult } from '../interfaces/query-result.js';
|
|
|
5
5
|
import type { ScriptExecuteOptions } from '../interfaces/script-execute-options.js';
|
|
6
6
|
import type { ScriptResult } from '../interfaces/script-result.js';
|
|
7
7
|
import type { StatementPrepareOptions } from '../interfaces/statement-prepare-options.js';
|
|
8
|
+
import type { TransactionOptions } from '../interfaces/transaction-options.js';
|
|
8
9
|
import { SafeEventEmitter } from '../safe-event-emitter.js';
|
|
9
10
|
import { QueryRequest } from '../util/sql-tag.js';
|
|
10
11
|
import { Connection, type NotificationCallback } from './connection.js';
|
|
11
12
|
import { IntlConnection } from './intl-connection.js';
|
|
12
13
|
import type { PreparedStatement } from './prepared-statement.js';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated `pipeline` is part of `QueryOptions` and
|
|
16
|
+
* `ScriptExecuteOptions` now, so every connection takes it too. Kept as
|
|
17
|
+
* an alias of that one field so the exported type still means what it
|
|
18
|
+
* did.
|
|
19
|
+
*/
|
|
20
|
+
export type PoolPipelineOptions = Pick<QueryOptions, 'pipeline'>;
|
|
21
|
+
export type PoolQueryOptions = QueryOptions;
|
|
22
|
+
export type PoolScriptExecuteOptions = ScriptExecuteOptions;
|
|
18
23
|
/**
|
|
19
24
|
* One pooled connection the pipelined path is currently borrowing.
|
|
20
25
|
*
|
|
@@ -93,7 +98,7 @@ export declare class Pool extends SafeEventEmitter {
|
|
|
93
98
|
* is free to pick a different connection, and a transaction lives on
|
|
94
99
|
* one. This is the way to get several statements onto the same one.
|
|
95
100
|
*/
|
|
96
|
-
transaction<T>(fn: (connection: Connection) => Promise<T
|
|
101
|
+
transaction<T>(fn: (connection: Connection) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
97
102
|
prepare(sql: string, options?: StatementPrepareOptions): Promise<PreparedStatement>;
|
|
98
103
|
release(connection: Connection): Promise<void>;
|
|
99
104
|
listen(channel: string, callback: NotificationCallback): Promise<void>;
|
|
@@ -113,6 +118,25 @@ export declare class Pool extends SafeEventEmitter {
|
|
|
113
118
|
* the connection had no query in flight to reject.
|
|
114
119
|
*/
|
|
115
120
|
protected _onConnectionDestroyed(intlCon: IntlConnection): void;
|
|
121
|
+
/**
|
|
122
|
+
* Whether a one-shot query may share a pooled connection with the
|
|
123
|
+
* queries already in flight on it.
|
|
124
|
+
*
|
|
125
|
+
* **Opt-in**, per call or with `pipeline: true` on the pool. It is
|
|
126
|
+
* worth asking for - `max` stops being a ceiling on concurrent
|
|
127
|
+
* queries and becomes one on connections, and a burst of 500 through
|
|
128
|
+
* a pool of 10 measured 12ms against 29ms - but it is not the default,
|
|
129
|
+
* because it moves the query onto a connection other callers are
|
|
130
|
+
* using and session state there stops being the caller's own.
|
|
131
|
+
*
|
|
132
|
+
* The exclusions below are not policy, they are statements that need a
|
|
133
|
+
* connection to themselves for longer than the call takes to resolve:
|
|
134
|
+
* a cursor reads from a portal afterwards, a transaction spans later
|
|
135
|
+
* statements, a COPY holds the connection mid-stream, `autoCommit:
|
|
136
|
+
* false` says a transaction is being managed by hand, and an
|
|
137
|
+
* `AbortSignal` cancels by tearing down whatever the connection is
|
|
138
|
+
* doing - which, shared, is not only this query.
|
|
139
|
+
*/
|
|
116
140
|
protected _canPipeline(pipeline: boolean | undefined, sql: string, autoCommit: boolean | undefined, signal: AbortSignal | undefined): boolean;
|
|
117
141
|
/**
|
|
118
142
|
* Picks the pooled connection a pipelined query should ride on, or
|
package/connection/pool.js
CHANGED
|
@@ -157,6 +157,12 @@ export class Pool extends SafeEventEmitter {
|
|
|
157
157
|
connection.on('execute', (...args) => this.emit('execute', ...args));
|
|
158
158
|
if (this.listenerCount('query'))
|
|
159
159
|
connection.on('query', (...args) => this.emit('query', ...args));
|
|
160
|
+
// Forwarded with the connection that raised it, the way 'destroy'
|
|
161
|
+
// names its own subject: a caller using pool.query() never sees the
|
|
162
|
+
// Connection, so without this a notice raised by their statement has
|
|
163
|
+
// nowhere to go at all.
|
|
164
|
+
if (this.listenerCount('notice'))
|
|
165
|
+
connection.on('notice', msg => this.emit('notice', msg, connection));
|
|
160
166
|
return connection;
|
|
161
167
|
}
|
|
162
168
|
/**
|
|
@@ -266,10 +272,10 @@ export class Pool extends SafeEventEmitter {
|
|
|
266
272
|
* is free to pick a different connection, and a transaction lives on
|
|
267
273
|
* one. This is the way to get several statements onto the same one.
|
|
268
274
|
*/
|
|
269
|
-
async transaction(fn) {
|
|
275
|
+
async transaction(fn, options) {
|
|
270
276
|
const connection = await this.acquire();
|
|
271
277
|
try {
|
|
272
|
-
return await connection.transaction(fn);
|
|
278
|
+
return await connection.transaction(fn, options);
|
|
273
279
|
}
|
|
274
280
|
finally {
|
|
275
281
|
await this.release(connection);
|
|
@@ -355,8 +361,27 @@ export class Pool extends SafeEventEmitter {
|
|
|
355
361
|
// connection that could not be created stays unambiguous.
|
|
356
362
|
this.emit('error', reason);
|
|
357
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Whether a one-shot query may share a pooled connection with the
|
|
366
|
+
* queries already in flight on it.
|
|
367
|
+
*
|
|
368
|
+
* **Opt-in**, per call or with `pipeline: true` on the pool. It is
|
|
369
|
+
* worth asking for - `max` stops being a ceiling on concurrent
|
|
370
|
+
* queries and becomes one on connections, and a burst of 500 through
|
|
371
|
+
* a pool of 10 measured 12ms against 29ms - but it is not the default,
|
|
372
|
+
* because it moves the query onto a connection other callers are
|
|
373
|
+
* using and session state there stops being the caller's own.
|
|
374
|
+
*
|
|
375
|
+
* The exclusions below are not policy, they are statements that need a
|
|
376
|
+
* connection to themselves for longer than the call takes to resolve:
|
|
377
|
+
* a cursor reads from a portal afterwards, a transaction spans later
|
|
378
|
+
* statements, a COPY holds the connection mid-stream, `autoCommit:
|
|
379
|
+
* false` says a transaction is being managed by hand, and an
|
|
380
|
+
* `AbortSignal` cancels by tearing down whatever the connection is
|
|
381
|
+
* doing - which, shared, is not only this query.
|
|
382
|
+
*/
|
|
358
383
|
_canPipeline(pipeline, sql, autoCommit, signal) {
|
|
359
|
-
return (pipeline === true &&
|
|
384
|
+
return ((pipeline != null ? pipeline : this.config.pipeline === true) &&
|
|
360
385
|
!signal &&
|
|
361
386
|
this._pipelineMaxQueries > 1 &&
|
|
362
387
|
this._pipelineMaxConnections > 0 &&
|
|
@@ -68,6 +68,17 @@ export declare class PreparedStatement extends SafeEventEmitter implements Async
|
|
|
68
68
|
* takes, minus `fetchCount` and `cursor`, which a batch cannot honour.
|
|
69
69
|
*/
|
|
70
70
|
executeBatch(paramSets: any[][], options?: QueryOptions): Promise<BatchResult>;
|
|
71
|
+
/**
|
|
72
|
+
* A notice reaches both this statement and the connection it ran on.
|
|
73
|
+
*
|
|
74
|
+
* These are the three loops the statement keeps for its own close and
|
|
75
|
+
* cancel traffic; an `execute()` runs through the connection's loop
|
|
76
|
+
* instead and is forwarded there. Without this second hop a notice
|
|
77
|
+
* arriving on one of these three would be the only kind a caller
|
|
78
|
+
* holding a `Connection` never saw. Hard to provoke - `DEALLOCATE`
|
|
79
|
+
* does not normally raise one - which is why there is no test for it.
|
|
80
|
+
*/
|
|
81
|
+
protected _emitNotice(msg: any): void;
|
|
71
82
|
protected _executeBatchWithTransaction(paramSets: any[][], options: QueryOptions): Promise<BatchResult>;
|
|
72
83
|
protected _executeWithTransaction(options?: QueryOptions): Promise<QueryResult>;
|
|
73
84
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DataTypeOIDs } from '../constants.js';
|
|
1
2
|
import { GlobalTypeMap } from '../data-type-map.js';
|
|
2
3
|
import { Protocol } from '../protocol/protocol.js';
|
|
3
4
|
import { SafeEventEmitter } from '../safe-event-emitter.js';
|
|
@@ -5,6 +6,7 @@ import { withAbortSignal } from '../util/abort-signal.js';
|
|
|
5
6
|
import { getParsers } from '../util/get-parsers.js';
|
|
6
7
|
import { resolveColumnFormats } from '../util/resolve-column-formats.js';
|
|
7
8
|
import { resolveRowType } from '../util/row-decoder.js';
|
|
9
|
+
import { isTransactionCommand, refusesSavepoint, } from '../util/transaction-command.js';
|
|
8
10
|
import { wrapRowDescription } from '../util/wrap-row-description.js';
|
|
9
11
|
import { Cursor } from './cursor.js';
|
|
10
12
|
import { getIntlConnection } from './intl-connection.js';
|
|
@@ -51,6 +53,9 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
51
53
|
}
|
|
52
54
|
async execute(options = {}) {
|
|
53
55
|
const intlCon = getIntlConnection(this.connection);
|
|
56
|
+
// Folded in here rather than deeper: a cursor keeps these options
|
|
57
|
+
// and decodes its own rows with them long after this call is over.
|
|
58
|
+
options = intlCon.withDefaults(options);
|
|
54
59
|
if (options.signal)
|
|
55
60
|
return withAbortSignal(options.signal, () => intlCon.cancel(), () => this._executeWithTransaction(options));
|
|
56
61
|
return this._executeWithTransaction(options);
|
|
@@ -108,12 +113,27 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
108
113
|
throw new Error('executeBatch() cannot return a cursor - a batch runs every set to ' +
|
|
109
114
|
'completion under one Sync, so there is no portal left to fetch from');
|
|
110
115
|
const intlCon = getIntlConnection(this.connection);
|
|
116
|
+
options = intlCon.withDefaults(options);
|
|
111
117
|
if (!paramSets.length)
|
|
112
118
|
return { results: [], totalRowsAffected: 0 };
|
|
113
119
|
if (options.signal)
|
|
114
120
|
return withAbortSignal(options.signal, () => intlCon.cancel(), () => this._executeBatchWithTransaction(paramSets, options));
|
|
115
121
|
return this._executeBatchWithTransaction(paramSets, options);
|
|
116
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* A notice reaches both this statement and the connection it ran on.
|
|
125
|
+
*
|
|
126
|
+
* These are the three loops the statement keeps for its own close and
|
|
127
|
+
* cancel traffic; an `execute()` runs through the connection's loop
|
|
128
|
+
* instead and is forwarded there. Without this second hop a notice
|
|
129
|
+
* arriving on one of these three would be the only kind a caller
|
|
130
|
+
* holding a `Connection` never saw. Hard to provoke - `DEALLOCATE`
|
|
131
|
+
* does not normally raise one - which is why there is no test for it.
|
|
132
|
+
*/
|
|
133
|
+
_emitNotice(msg) {
|
|
134
|
+
this.emit('notice', msg);
|
|
135
|
+
getIntlConnection(this.connection).emit('notice', msg);
|
|
136
|
+
}
|
|
117
137
|
_executeBatchWithTransaction(paramSets, options) {
|
|
118
138
|
const intlCon = getIntlConnection(this.connection);
|
|
119
139
|
return this._withTransaction(options, () => intlCon.executeBatchReused(this.name, this._fields, this.paramTypes, paramSets, options));
|
|
@@ -139,7 +159,17 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
139
159
|
*/
|
|
140
160
|
async _withTransaction(options = {}, fn, canInlineSavepoint = false) {
|
|
141
161
|
const intlCon = getIntlConnection(this.connection);
|
|
142
|
-
|
|
162
|
+
// See Connection._query(): a money parameter is written as an int64
|
|
163
|
+
// of minor units, so the server's scale has to be known before the
|
|
164
|
+
// Bind goes out.
|
|
165
|
+
if (this.paramTypes?.includes(DataTypeOIDs.money) ||
|
|
166
|
+
this.paramTypes?.includes(DataTypeOIDs._money) ||
|
|
167
|
+
// And before any of this statement's own money columns are read:
|
|
168
|
+
// a cursor and a batch both decode inside the message loop, where
|
|
169
|
+
// there is no longer anywhere to ask from.
|
|
170
|
+
intlCon.needsMoneyFormat(this._fields, options))
|
|
171
|
+
await intlCon.ensureMoneyFormat();
|
|
172
|
+
const transactionCommand = isTransactionCommand(this.sql);
|
|
143
173
|
let beginFirst = false;
|
|
144
174
|
let commitLast = false;
|
|
145
175
|
const autoCommit = options?.autoCommit;
|
|
@@ -156,7 +186,11 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
156
186
|
await intlCon.execute('BEGIN');
|
|
157
187
|
// See IntlConnection.execute()'s own rollbackOnError for why
|
|
158
188
|
// intlCon.inTransaction goes first here but last in the checks below.
|
|
159
|
-
const rollbackOnError =
|
|
189
|
+
const rollbackOnError =
|
|
190
|
+
// See IntlConnection.execute(): `SET TRANSACTION` keeps the
|
|
191
|
+
// implicit BEGIN and loses only the savepoint, which PostgreSQL
|
|
192
|
+
// refuses to let it run inside.
|
|
193
|
+
!refusesSavepoint(this.sql) &&
|
|
160
194
|
intlCon.inTransaction &&
|
|
161
195
|
(options?.rollbackOnError ?? intlCon.config.rollbackOnError ?? true);
|
|
162
196
|
// When `fn` can put the savepoint on the wire itself, SAVEPOINT and
|
|
@@ -268,7 +302,7 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
268
302
|
}, (code, msg, done) => {
|
|
269
303
|
switch (code) {
|
|
270
304
|
case Protocol.BackendMessageCode.NoticeResponse:
|
|
271
|
-
this.
|
|
305
|
+
this._emitNotice(msg);
|
|
272
306
|
break;
|
|
273
307
|
case Protocol.BackendMessageCode.CloseComplete:
|
|
274
308
|
// Arrives twice (once per Close) - neither call is terminal.
|
|
@@ -299,7 +333,7 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
299
333
|
const closePromise = socket.sendCloseMessage({ type: 'S', name: this.name }, (code, msg, done) => {
|
|
300
334
|
switch (code) {
|
|
301
335
|
case Protocol.BackendMessageCode.NoticeResponse:
|
|
302
|
-
this.
|
|
336
|
+
this._emitNotice(msg);
|
|
303
337
|
break;
|
|
304
338
|
case Protocol.BackendMessageCode.CloseComplete:
|
|
305
339
|
done();
|
|
@@ -314,7 +348,7 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
314
348
|
const syncPromise = socket.sendSyncMessage((code, msg, done) => {
|
|
315
349
|
switch (code) {
|
|
316
350
|
case Protocol.BackendMessageCode.NoticeResponse:
|
|
317
|
-
this.
|
|
351
|
+
this._emitNotice(msg);
|
|
318
352
|
break;
|
|
319
353
|
case Protocol.BackendMessageCode.ReadyForQuery:
|
|
320
354
|
intoCon.transactionStatus = msg.status;
|
package/data-type-map.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DataType } from './interfaces/data-type.js';
|
|
2
2
|
import type { OID } from './types.js';
|
|
3
3
|
export declare class DataTypeMap {
|
|
4
|
+
protected _inferScalars: DataType[];
|
|
5
|
+
protected _inferArrays: DataType[];
|
|
4
6
|
private _itemsByOID;
|
|
5
7
|
private _items;
|
|
6
8
|
/**
|
|
@@ -17,6 +19,20 @@ export declare class DataTypeMap {
|
|
|
17
19
|
constructor(other?: DataTypeMap);
|
|
18
20
|
get(oid: OID): DataType;
|
|
19
21
|
register(dataTypes: DataType | DataType[]): void;
|
|
22
|
+
/**
|
|
23
|
+
* The two lists determine() actually walks, newest-registered first.
|
|
24
|
+
*
|
|
25
|
+
* A value is either an array or it is not, so half of `_items` can
|
|
26
|
+
* never match it - and the half that can is the smaller one: of 127
|
|
27
|
+
* registered types, a plain number used to be tested against 22 and
|
|
28
|
+
* skipped past 105. Rebuilt on registration, which happens at startup
|
|
29
|
+
* and hardly ever again, so the walk itself reads a list that is
|
|
30
|
+
* already only what it needs.
|
|
31
|
+
*
|
|
32
|
+
* A type that is not inferrable is left out of both: determine() may
|
|
33
|
+
* never pick it, however well its isType matches.
|
|
34
|
+
*/
|
|
35
|
+
protected _rebuildInferenceLists(): void;
|
|
20
36
|
determine(value: any): OID;
|
|
21
37
|
}
|
|
22
38
|
export declare const GlobalTypeMap: DataTypeMap;
|
package/data-type-map.js
CHANGED
|
@@ -21,6 +21,7 @@ import { ArrayJsonPathType, JsonPathType } from './data-types/jsonpath-type.js';
|
|
|
21
21
|
import { ArrayLineType, LineType } from './data-types/line-type.js';
|
|
22
22
|
import { ArrayLsegType, LsegType } from './data-types/lseg-type.js';
|
|
23
23
|
import { ArrayMacaddr8Type, ArrayMacaddrType, Macaddr8Type, MacaddrType, } from './data-types/macaddr-type.js';
|
|
24
|
+
import { ArrayMoneyType, MoneyType } from './data-types/money-type.js';
|
|
24
25
|
import { ArrayNumericType, NumericType } from './data-types/numeric-type.js';
|
|
25
26
|
import { ArrayOidType, OidType } from './data-types/oid-type.js';
|
|
26
27
|
import { ArrayOidVectorType, OidVectorType, } from './data-types/oid-vector-type.js';
|
|
@@ -38,7 +39,10 @@ import { ArrayTsQueryType, ArrayTsVectorType, TsQueryType, TsVectorType, } from
|
|
|
38
39
|
import { ArrayUuidType, UuidType } from './data-types/uuid-type.js';
|
|
39
40
|
import { ArrayVarcharType, VarcharType } from './data-types/varchar-type.js';
|
|
40
41
|
import { ArrayCidType, ArrayXid8Type, ArrayXidType, CidType, Xid8Type, XidType, } from './data-types/xid-type.js';
|
|
42
|
+
import { arrayLeaf } from './util/array-leaf.js';
|
|
41
43
|
export class DataTypeMap {
|
|
44
|
+
_inferScalars = [];
|
|
45
|
+
_inferArrays = [];
|
|
42
46
|
_itemsByOID = {};
|
|
43
47
|
_items = [];
|
|
44
48
|
/**
|
|
@@ -56,6 +60,11 @@ export class DataTypeMap {
|
|
|
56
60
|
if (other instanceof DataTypeMap) {
|
|
57
61
|
this._items = [...other._items];
|
|
58
62
|
this._itemsByOID = { ...other._itemsByOID };
|
|
63
|
+
// Shared, not copied: they are rebuilt wholesale by register(),
|
|
64
|
+
// so a copy that registers nothing never diverges and one that
|
|
65
|
+
// does gets its own pair then.
|
|
66
|
+
this._inferScalars = other._inferScalars;
|
|
67
|
+
this._inferArrays = other._inferArrays;
|
|
59
68
|
}
|
|
60
69
|
}
|
|
61
70
|
get(oid) {
|
|
@@ -71,6 +80,37 @@ export class DataTypeMap {
|
|
|
71
80
|
else
|
|
72
81
|
this._items.push(t);
|
|
73
82
|
}
|
|
83
|
+
this._rebuildInferenceLists();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The two lists determine() actually walks, newest-registered first.
|
|
87
|
+
*
|
|
88
|
+
* A value is either an array or it is not, so half of `_items` can
|
|
89
|
+
* never match it - and the half that can is the smaller one: of 127
|
|
90
|
+
* registered types, a plain number used to be tested against 22 and
|
|
91
|
+
* skipped past 105. Rebuilt on registration, which happens at startup
|
|
92
|
+
* and hardly ever again, so the walk itself reads a list that is
|
|
93
|
+
* already only what it needs.
|
|
94
|
+
*
|
|
95
|
+
* A type that is not inferrable is left out of both: determine() may
|
|
96
|
+
* never pick it, however well its isType matches.
|
|
97
|
+
*/
|
|
98
|
+
_rebuildInferenceLists() {
|
|
99
|
+
const scalars = [];
|
|
100
|
+
const arrays = [];
|
|
101
|
+
let i;
|
|
102
|
+
let t;
|
|
103
|
+
// Built back to front, so index 0 is the newest registration and the
|
|
104
|
+
// walk runs forward - which is the direction a JS array is fastest
|
|
105
|
+
// to read and the one that needs no length arithmetic per step.
|
|
106
|
+
for (i = this._items.length - 1; i >= 0; i--) {
|
|
107
|
+
t = this._items[i];
|
|
108
|
+
if (t.inferrable === false)
|
|
109
|
+
continue;
|
|
110
|
+
(t.elementsOID ? arrays : scalars).push(t);
|
|
111
|
+
}
|
|
112
|
+
this._inferScalars = scalars;
|
|
113
|
+
this._inferArrays = arrays;
|
|
74
114
|
}
|
|
75
115
|
determine(value) {
|
|
76
116
|
if (value == null)
|
|
@@ -94,21 +134,22 @@ export class DataTypeMap {
|
|
|
94
134
|
'it at the call site (`new BindParam(oid, value)`)');
|
|
95
135
|
}
|
|
96
136
|
}
|
|
137
|
+
// An array is typed from the first value inside it that is actually
|
|
138
|
+
// a value: `value[0]` answers `_int2vector` for [[1, 2], [3, 4]],
|
|
139
|
+
// because the first element is itself an array and a vector is the
|
|
140
|
+
// one registered type an array of numbers matches, and answers
|
|
141
|
+
// nothing at all for [null, 2, 3].
|
|
97
142
|
const valueIsArray = Array.isArray(value);
|
|
143
|
+
const candidates = valueIsArray ? this._inferArrays : this._inferScalars;
|
|
144
|
+
const v = valueIsArray ? arrayLeaf(value) : value;
|
|
145
|
+
// Newest-registered first, so a later registration of the same OID
|
|
146
|
+
// wins - see _rebuildInferenceLists() for how the order is kept.
|
|
147
|
+
const l = candidates.length;
|
|
98
148
|
let i;
|
|
99
149
|
let t;
|
|
100
|
-
for (i =
|
|
101
|
-
t =
|
|
102
|
-
|
|
103
|
-
// same OID wins - and a type marked `inferrable: false` is never
|
|
104
|
-
// picked here at all, however well `isType` matches.
|
|
105
|
-
if (t.inferrable === false)
|
|
106
|
-
continue;
|
|
107
|
-
if (valueIsArray) {
|
|
108
|
-
if (t.elementsOID && t.isType(value[0]))
|
|
109
|
-
return t.oid;
|
|
110
|
-
}
|
|
111
|
-
else if (!t.elementsOID && t.isType(value))
|
|
150
|
+
for (i = 0; i < l; i++) {
|
|
151
|
+
t = candidates[i];
|
|
152
|
+
if (t.isType(v))
|
|
112
153
|
return t.oid;
|
|
113
154
|
}
|
|
114
155
|
return DataTypeOIDs.unknown;
|
|
@@ -121,6 +162,9 @@ GlobalTypeMap.register([JsonbType, ArrayJsonbType]);
|
|
|
121
162
|
GlobalTypeMap.register([JsonType, ArrayJsonType]);
|
|
122
163
|
GlobalTypeMap.register([BoolType, ArrayBoolType]);
|
|
123
164
|
GlobalTypeMap.register([NumericType, ArrayNumericType]);
|
|
165
|
+
// Registered after numeric and not inferrable: a JavaScript number is
|
|
166
|
+
// never money by shape, and the type is reached by naming it.
|
|
167
|
+
GlobalTypeMap.register([MoneyType, ArrayMoneyType]);
|
|
124
168
|
GlobalTypeMap.register([Float4Type, ArrayFloat4Type]);
|
|
125
169
|
GlobalTypeMap.register([Float8Type, ArrayFloat8Type]);
|
|
126
170
|
GlobalTypeMap.register([Int2Type, ArrayInt2Type]);
|
|
@@ -9,10 +9,14 @@
|
|
|
9
9
|
* to a plain number; this class is what the rest become, and it carries
|
|
10
10
|
* the digits exactly as the server wrote them.
|
|
11
11
|
*
|
|
12
|
-
* It is a class rather than a bare string so that
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* It is a class rather than a bare string so that the value keeps
|
|
13
|
+
* saying what it is. A bare string parameter does reach a numeric
|
|
14
|
+
* column - it goes out with no declared type and the server resolves it
|
|
15
|
+
* there (see `util/unspecified-param.ts`) - but only where there is a
|
|
16
|
+
* column to resolve it from: `determine()` answers `varchar` for it, an
|
|
17
|
+
* inlined literal is written with no cast, and a decoded one is
|
|
18
|
+
* indistinguishable from text. A `Numeric` carries the type with the
|
|
19
|
+
* digits.
|
|
16
20
|
*
|
|
17
21
|
* There is deliberately no `valueOf()`. Arithmetic on one of these has
|
|
18
22
|
* to be an explicit decision - `Symbol.toPrimitive` hands back the exact
|
|
@@ -9,10 +9,14 @@
|
|
|
9
9
|
* to a plain number; this class is what the rest become, and it carries
|
|
10
10
|
* the digits exactly as the server wrote them.
|
|
11
11
|
*
|
|
12
|
-
* It is a class rather than a bare string so that
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* It is a class rather than a bare string so that the value keeps
|
|
13
|
+
* saying what it is. A bare string parameter does reach a numeric
|
|
14
|
+
* column - it goes out with no declared type and the server resolves it
|
|
15
|
+
* there (see `util/unspecified-param.ts`) - but only where there is a
|
|
16
|
+
* column to resolve it from: `determine()` answers `varchar` for it, an
|
|
17
|
+
* inlined literal is written with no cast, and a decoded one is
|
|
18
|
+
* indistinguishable from text. A `Numeric` carries the type with the
|
|
19
|
+
* digits.
|
|
16
20
|
*
|
|
17
21
|
* There is deliberately no `valueOf()`. Arithmetic on one of these has
|
|
18
22
|
* to be an explicit decision - `Symbol.toPrimitive` hands back the exact
|
package/data-types/date-type.js
CHANGED
|
@@ -11,7 +11,7 @@ export const DateType = {
|
|
|
11
11
|
},
|
|
12
12
|
encodeBinary(buf, v, options) {
|
|
13
13
|
if (typeof v === 'string')
|
|
14
|
-
v = parseDate(v, options.utcDates);
|
|
14
|
+
v = parseDate(v, options.utcDates, options.dateStyle);
|
|
15
15
|
if (v === Infinity) {
|
|
16
16
|
buf.writeInt32BE(0x7fffffff);
|
|
17
17
|
return;
|
|
@@ -42,7 +42,7 @@ export const DateType = {
|
|
|
42
42
|
return d;
|
|
43
43
|
},
|
|
44
44
|
decodeText(v, options) {
|
|
45
|
-
return parseDate(v, options.utcDates);
|
|
45
|
+
return parseDate(v, options.utcDates, options.dateStyle);
|
|
46
46
|
},
|
|
47
47
|
decodeTextBuffer(buf, offset, len, options) {
|
|
48
48
|
return DateType.decodeText(buf.toString('latin1', offset, offset + len), options);
|
|
@@ -10,7 +10,10 @@ export const Int2VectorType = {
|
|
|
10
10
|
encodeBinary(buf, v) {
|
|
11
11
|
encodeBinaryArray(buf, v, DataTypeOIDs.int2, {}, (io, x) => {
|
|
12
12
|
io.writeInt16BE(x);
|
|
13
|
-
}
|
|
13
|
+
},
|
|
14
|
+
// A vector is genuinely 0-based, unlike every ordinary array -
|
|
15
|
+
// `array_lower(pg_index.indkey, 1)` is 0 on the server.
|
|
16
|
+
undefined, 0);
|
|
14
17
|
},
|
|
15
18
|
encodeCalculateDim(v) {
|
|
16
19
|
return [v.length];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { MoneyFormat } from '../interfaces/data-mapping-options.js';
|
|
2
|
+
import type { DataType } from '../interfaces/data-type.js';
|
|
3
|
+
/**
|
|
4
|
+
* Reads the server's own rendering of `1::money` - see
|
|
5
|
+
* `IntlConnection.ensureMoneyFormat()` for why it is that value: with no
|
|
6
|
+
* thousands separator in it, any separator present can only be the
|
|
7
|
+
* decimal one, and the digits after it can only be the fraction.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseMoneyFormat(text: string): MoneyFormat;
|
|
10
|
+
/** The exact decimal `minorUnits` stands for at `scale` fraction digits. */
|
|
11
|
+
export declare function moneyToString(minorUnits: bigint, scale: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* PostgreSQL's fixed-point currency type.
|
|
14
|
+
*
|
|
15
|
+
* On the wire it is an int64 of the smallest currency unit - `$12.34`
|
|
16
|
+
* arrives as `1234` - and how many of those make a unit is `lc_monetary`,
|
|
17
|
+
* which the server does not report in its startup parameters. So the
|
|
18
|
+
* connection asks it once, on its way up, and every money value is read
|
|
19
|
+
* against that answer. See `IntlConnection.ensureMoneyFormat()`.
|
|
20
|
+
*
|
|
21
|
+
* The value decodes to a plain number, or a `Numeric` when a double
|
|
22
|
+
* cannot carry it. The currency symbol is *not* part of it: it is the
|
|
23
|
+
* server's rendering of the value, not the value, and
|
|
24
|
+
* `fetchAsString: [DataTypeOIDs.money]` asks for that rendering when it
|
|
25
|
+
* is what the caller wants - which is also what `pg` always returns.
|
|
26
|
+
*
|
|
27
|
+
* The question is asked the first time a money column is on its way
|
|
28
|
+
* back, while the rows are still raw bytes - so no value is ever read
|
|
29
|
+
* against a scale nobody confirmed, and a caller who never touches
|
|
30
|
+
* money never asks. `execute()` and `pipeline()` decode inside the
|
|
31
|
+
* message loop, where there is nowhere left to ask from, so those two
|
|
32
|
+
* set the rows aside and read them once the loop is over.
|
|
33
|
+
*
|
|
34
|
+
* `moneyFormat` in the options answers it in advance, for a caller who
|
|
35
|
+
* knows the server or is reading a value some other server rendered.
|
|
36
|
+
*/
|
|
37
|
+
export declare const MoneyType: DataType;
|
|
38
|
+
export declare const ArrayMoneyType: DataType;
|