postgrejs 3.3.0 → 3.4.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 +38 -1
- package/README.md +56 -6
- package/connection/connection.d.ts +97 -0
- package/connection/connection.js +141 -2
- package/connection/intl-connection.d.ts +113 -3
- package/connection/intl-connection.js +520 -31
- package/connection/prepared-statement.d.ts +64 -1
- package/connection/prepared-statement.js +95 -5
- package/data-types/float4-type.js +2 -1
- package/data-types/float8-type.js +2 -1
- package/data-types/int2-type.js +2 -1
- package/data-types/int4-type.js +2 -1
- package/data-types/int8-type.js +2 -1
- package/data-types/numeric-type.js +7 -0
- package/data-types/oid-type.js +2 -1
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/interfaces/batch-result.d.ts +42 -0
- package/interfaces/batch-result.js +1 -0
- package/interfaces/copy-from-rows-options.d.ts +58 -0
- package/interfaces/copy-from-rows-options.js +1 -0
- package/interfaces/database-connection-params.d.ts +25 -0
- package/interfaces/pipeline-request.d.ts +16 -0
- package/interfaces/pipeline-request.js +1 -0
- package/interfaces/query-options.d.ts +6 -0
- package/package.json +5 -2
- package/protocol/database-error.d.ts +16 -0
- package/protocol/database-error.js +15 -0
- package/protocol/pg-socket.d.ts +76 -0
- package/protocol/pg-socket.js +124 -12
- package/util/assert-integer.d.ts +43 -0
- package/util/assert-integer.js +58 -0
- package/util/copy-from-rows.d.ts +56 -0
- package/util/copy-from-rows.js +95 -0
- package/util/encode-copy-binary.d.ts +48 -0
- package/util/encode-copy-binary.js +112 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BatchResult } from '../interfaces/batch-result.js';
|
|
1
2
|
import type { QueryOptions } from '../interfaces/query-options.js';
|
|
2
3
|
import type { QueryResult } from '../interfaces/query-result.js';
|
|
3
4
|
import type { StatementPrepareOptions } from '../interfaces/statement-prepare-options.js';
|
|
@@ -20,7 +21,69 @@ export declare class PreparedStatement extends SafeEventEmitter implements Async
|
|
|
20
21
|
get sql(): string;
|
|
21
22
|
get paramTypes(): Maybe<Maybe<OID>[]>;
|
|
22
23
|
execute(options?: QueryOptions): Promise<QueryResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Runs this statement once per parameter set, sending every Bind/Execute
|
|
26
|
+
* before waiting for any of them and closing the lot with a single Sync.
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* const st = await connection.prepare('update users set name = $1 where id = $2');
|
|
30
|
+
* const batch = await st.executeBatch([
|
|
31
|
+
* ['John', 1],
|
|
32
|
+
* ['Jane', 2],
|
|
33
|
+
* ['Bob', 3],
|
|
34
|
+
* ]);
|
|
35
|
+
* batch.results.map(r => r.rowsAffected); // [1, 1, 0]
|
|
36
|
+
* batch.totalRowsAffected; // 2
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* Three things follow from the single Sync, and they are the reasons to
|
|
40
|
+
* reach for this over a loop or a Promise.all() of execute() calls:
|
|
41
|
+
*
|
|
42
|
+
* - **Speed.** Per-set Sync makes the server close an implicit
|
|
43
|
+
* transaction and answer ReadyForQuery every time. Collapsing that to
|
|
44
|
+
* one turned 1000 updates from 194ms (pipelined execute() calls) into
|
|
45
|
+
* 21ms locally, with socket reads dropping from 972 to 3.
|
|
46
|
+
* - **One transaction.** Unless an explicit transaction is already open,
|
|
47
|
+
* the whole batch commits or rolls back together. This differs from N
|
|
48
|
+
* separate execute() calls, where each commits on its own.
|
|
49
|
+
* - **A failing set stops the rest.** PostgreSQL discards everything
|
|
50
|
+
* between an error and the Sync, so sets after a rejected one never
|
|
51
|
+
* run. The error carries `failedIndex` (which set was rejected) and
|
|
52
|
+
* `batchResults` (the ones that had completed) so the caller can see
|
|
53
|
+
* how far it got.
|
|
54
|
+
*
|
|
55
|
+
* Every set runs the same statement, so results map to sets by position.
|
|
56
|
+
* Sets that return rows get them decoded into `rows` exactly as
|
|
57
|
+
* `execute()` would, `rowDecoder` included; `fetchCount` does not apply,
|
|
58
|
+
* since a portal suspended mid-batch would break that mapping - each set
|
|
59
|
+
* runs to completion and buffers its rows.
|
|
60
|
+
*
|
|
61
|
+
* For a bulk INSERT specifically, `connection.copyFrom()` is faster
|
|
62
|
+
* still, and a single `UPDATE ... FROM (VALUES ...)` beats this for bulk
|
|
63
|
+
* updates of one shape - at the cost of PostgreSQL's 65535-parameter
|
|
64
|
+
* ceiling. This is the general answer when neither fits.
|
|
65
|
+
*
|
|
66
|
+
* @param paramSets One array of bind parameters per execution.
|
|
67
|
+
* @param options Applied to every set - the same options `execute()`
|
|
68
|
+
* takes, minus `fetchCount` and `cursor`, which a batch cannot honour.
|
|
69
|
+
*/
|
|
70
|
+
executeBatch(paramSets: any[][], options?: QueryOptions): Promise<BatchResult>;
|
|
71
|
+
protected _executeBatchWithTransaction(paramSets: any[][], options: QueryOptions): Promise<BatchResult>;
|
|
23
72
|
protected _executeWithTransaction(options?: QueryOptions): Promise<QueryResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Runs `fn` inside whatever transaction handling the connection's config
|
|
75
|
+
* and `options` call for: an implicit BEGIN when autoCommit is off, a
|
|
76
|
+
* SAVEPOINT to roll back to when one is already open, a COMMIT when the
|
|
77
|
+
* call is the one turning autoCommit back on.
|
|
78
|
+
*
|
|
79
|
+
* Generic over `fn` rather than calling _execute() directly so that
|
|
80
|
+
* executeBatch() gets the same handling for the batch as a whole. Per
|
|
81
|
+
* set would be wrong twice over: the savepoints alone would add two
|
|
82
|
+
* statements per set, and the batch already shares one transaction by
|
|
83
|
+
* virtue of its single Sync, so a per-set savepoint could not roll one
|
|
84
|
+
* set back independently anyway.
|
|
85
|
+
*/
|
|
86
|
+
protected _withTransaction<T>(options: QueryOptions | undefined, fn: (inlineSavepoint?: string) => Promise<T>, canInlineSavepoint?: boolean): Promise<T>;
|
|
24
87
|
close(): Promise<void>;
|
|
25
88
|
/**
|
|
26
89
|
* Asks the server to cancel whatever this statement's connection is
|
|
@@ -28,7 +91,7 @@ export declare class PreparedStatement extends SafeEventEmitter implements Async
|
|
|
28
91
|
* option, which reports the abort to the caller that asked for it.
|
|
29
92
|
*/
|
|
30
93
|
cancel(): Promise<void>;
|
|
31
|
-
protected _execute(options?: QueryOptions): Promise<QueryResult>;
|
|
94
|
+
protected _execute(options?: QueryOptions, savepoint?: string): Promise<QueryResult>;
|
|
32
95
|
/**
|
|
33
96
|
* Called by Cursor.close() instead of separately awaiting
|
|
34
97
|
* Portal.close() then this.close(). Returns false (no wire action taken,
|
|
@@ -55,7 +55,89 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
55
55
|
return withAbortSignal(options.signal, () => intlCon.cancel(), () => this._executeWithTransaction(options));
|
|
56
56
|
return this._executeWithTransaction(options);
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Runs this statement once per parameter set, sending every Bind/Execute
|
|
60
|
+
* before waiting for any of them and closing the lot with a single Sync.
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* const st = await connection.prepare('update users set name = $1 where id = $2');
|
|
64
|
+
* const batch = await st.executeBatch([
|
|
65
|
+
* ['John', 1],
|
|
66
|
+
* ['Jane', 2],
|
|
67
|
+
* ['Bob', 3],
|
|
68
|
+
* ]);
|
|
69
|
+
* batch.results.map(r => r.rowsAffected); // [1, 1, 0]
|
|
70
|
+
* batch.totalRowsAffected; // 2
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* Three things follow from the single Sync, and they are the reasons to
|
|
74
|
+
* reach for this over a loop or a Promise.all() of execute() calls:
|
|
75
|
+
*
|
|
76
|
+
* - **Speed.** Per-set Sync makes the server close an implicit
|
|
77
|
+
* transaction and answer ReadyForQuery every time. Collapsing that to
|
|
78
|
+
* one turned 1000 updates from 194ms (pipelined execute() calls) into
|
|
79
|
+
* 21ms locally, with socket reads dropping from 972 to 3.
|
|
80
|
+
* - **One transaction.** Unless an explicit transaction is already open,
|
|
81
|
+
* the whole batch commits or rolls back together. This differs from N
|
|
82
|
+
* separate execute() calls, where each commits on its own.
|
|
83
|
+
* - **A failing set stops the rest.** PostgreSQL discards everything
|
|
84
|
+
* between an error and the Sync, so sets after a rejected one never
|
|
85
|
+
* run. The error carries `failedIndex` (which set was rejected) and
|
|
86
|
+
* `batchResults` (the ones that had completed) so the caller can see
|
|
87
|
+
* how far it got.
|
|
88
|
+
*
|
|
89
|
+
* Every set runs the same statement, so results map to sets by position.
|
|
90
|
+
* Sets that return rows get them decoded into `rows` exactly as
|
|
91
|
+
* `execute()` would, `rowDecoder` included; `fetchCount` does not apply,
|
|
92
|
+
* since a portal suspended mid-batch would break that mapping - each set
|
|
93
|
+
* runs to completion and buffers its rows.
|
|
94
|
+
*
|
|
95
|
+
* For a bulk INSERT specifically, `connection.copyFrom()` is faster
|
|
96
|
+
* still, and a single `UPDATE ... FROM (VALUES ...)` beats this for bulk
|
|
97
|
+
* updates of one shape - at the cost of PostgreSQL's 65535-parameter
|
|
98
|
+
* ceiling. This is the general answer when neither fits.
|
|
99
|
+
*
|
|
100
|
+
* @param paramSets One array of bind parameters per execution.
|
|
101
|
+
* @param options Applied to every set - the same options `execute()`
|
|
102
|
+
* takes, minus `fetchCount` and `cursor`, which a batch cannot honour.
|
|
103
|
+
*/
|
|
104
|
+
async executeBatch(paramSets, options = {}) {
|
|
105
|
+
if (!Array.isArray(paramSets))
|
|
106
|
+
throw new TypeError('executeBatch() requires an array of parameter sets');
|
|
107
|
+
if (options.cursor)
|
|
108
|
+
throw new Error('executeBatch() cannot return a cursor - a batch runs every set to ' +
|
|
109
|
+
'completion under one Sync, so there is no portal left to fetch from');
|
|
110
|
+
const intlCon = getIntlConnection(this.connection);
|
|
111
|
+
if (!paramSets.length)
|
|
112
|
+
return { results: [], totalRowsAffected: 0 };
|
|
113
|
+
if (options.signal)
|
|
114
|
+
return withAbortSignal(options.signal, () => intlCon.cancel(), () => this._executeBatchWithTransaction(paramSets, options));
|
|
115
|
+
return this._executeBatchWithTransaction(paramSets, options);
|
|
116
|
+
}
|
|
117
|
+
_executeBatchWithTransaction(paramSets, options) {
|
|
118
|
+
const intlCon = getIntlConnection(this.connection);
|
|
119
|
+
return this._withTransaction(options, () => intlCon.executeBatchReused(this.name, this._fields, this.paramTypes, paramSets, options));
|
|
120
|
+
}
|
|
58
121
|
async _executeWithTransaction(options = {}) {
|
|
122
|
+
// The Bind+Execute+Sync path can carry the rollbackOnError savepoint
|
|
123
|
+
// in its own round trip; the cursor path cannot, because the portal it
|
|
124
|
+
// opens outlives this call and the savepoint has to outlive it too.
|
|
125
|
+
return this._withTransaction(options, savepoint => this._execute(options, savepoint), !(options.cursor && this._fields));
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Runs `fn` inside whatever transaction handling the connection's config
|
|
129
|
+
* and `options` call for: an implicit BEGIN when autoCommit is off, a
|
|
130
|
+
* SAVEPOINT to roll back to when one is already open, a COMMIT when the
|
|
131
|
+
* call is the one turning autoCommit back on.
|
|
132
|
+
*
|
|
133
|
+
* Generic over `fn` rather than calling _execute() directly so that
|
|
134
|
+
* executeBatch() gets the same handling for the batch as a whole. Per
|
|
135
|
+
* set would be wrong twice over: the savepoints alone would add two
|
|
136
|
+
* statements per set, and the batch already shares one transaction by
|
|
137
|
+
* virtue of its single Sync, so a per-set savepoint could not roll one
|
|
138
|
+
* set back independently anyway.
|
|
139
|
+
*/
|
|
140
|
+
async _withTransaction(options = {}, fn, canInlineSavepoint = false) {
|
|
59
141
|
const intlCon = getIntlConnection(this.connection);
|
|
60
142
|
const transactionCommand = this.sql.match(/^(\bBEGIN\b|\bCOMMIT\b|\bSTART\b|\bROLLBACK|SAVEPOINT|RELEASE\b)/i);
|
|
61
143
|
let beginFirst = false;
|
|
@@ -77,13 +159,21 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
77
159
|
const rollbackOnError = !transactionCommand &&
|
|
78
160
|
intlCon.inTransaction &&
|
|
79
161
|
(options?.rollbackOnError ?? intlCon.config.rollbackOnError ?? true);
|
|
80
|
-
|
|
162
|
+
// When `fn` can put the savepoint on the wire itself, SAVEPOINT and
|
|
163
|
+
// RELEASE ride along inside its single Sync instead of costing a round
|
|
164
|
+
// trip each - measured at 51us for the pair that way against 511us as
|
|
165
|
+
// two separate round trips. The error path is unchanged either way:
|
|
166
|
+
// PostgreSQL discards everything between a failed statement and the
|
|
167
|
+
// Sync, so an inlined RELEASE never runs, and ROLLBACK TO goes out on
|
|
168
|
+
// its own below exactly as before.
|
|
169
|
+
const inlineSavepoint = canInlineSavepoint && rollbackOnError && intlCon.inTransaction;
|
|
170
|
+
if (rollbackOnError && intlCon.inTransaction && !inlineSavepoint)
|
|
81
171
|
await intlCon.execute('SAVEPOINT ' + this._onErrorSavePoint);
|
|
82
172
|
try {
|
|
83
|
-
const result = await this.
|
|
173
|
+
const result = await fn(inlineSavepoint ? this._onErrorSavePoint : undefined);
|
|
84
174
|
if (commitLast)
|
|
85
175
|
await intlCon.execute('COMMIT');
|
|
86
|
-
else if (rollbackOnError && intlCon.inTransaction) {
|
|
176
|
+
else if (rollbackOnError && intlCon.inTransaction && !inlineSavepoint) {
|
|
87
177
|
await intlCon.execute('RELEASE ' + this._onErrorSavePoint + ';');
|
|
88
178
|
}
|
|
89
179
|
return result;
|
|
@@ -112,7 +202,7 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
112
202
|
async cancel() {
|
|
113
203
|
return getIntlConnection(this.connection).cancel();
|
|
114
204
|
}
|
|
115
|
-
async _execute(options = {}) {
|
|
205
|
+
async _execute(options = {}, savepoint) {
|
|
116
206
|
const intlCon = getIntlConnection(this.connection);
|
|
117
207
|
if (options.cursor && this._fields) {
|
|
118
208
|
intlCon.ref();
|
|
@@ -142,7 +232,7 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
142
232
|
// unnamed portal, reusing the RowDescription prepare() already fetched
|
|
143
233
|
// instead of re-Describing (and re-Close-ing) a fresh named portal on
|
|
144
234
|
// every call.
|
|
145
|
-
return intlCon.executeReused(this.name, this._fields, this.paramTypes, options.params, options);
|
|
235
|
+
return intlCon.executeReused(this.name, this._fields, this.paramTypes, options.params, options, savepoint);
|
|
146
236
|
}
|
|
147
237
|
/**
|
|
148
238
|
* Called by Cursor.close() instead of separately awaiting
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { assertCoercedNumber } from '../util/assert-integer.js';
|
|
2
3
|
import { fastParseFloatBuffer } from '../util/fast-parsefloat.js';
|
|
3
4
|
export const Float4Type = {
|
|
4
5
|
name: 'float4',
|
|
@@ -8,7 +9,7 @@ export const Float4Type = {
|
|
|
8
9
|
return '' + v;
|
|
9
10
|
},
|
|
10
11
|
encodeBinary(buf, v) {
|
|
11
|
-
buf.writeFloatBE(typeof v === 'number' ? v : parseFloat(v));
|
|
12
|
+
buf.writeFloatBE(typeof v === 'number' ? v : assertCoercedNumber(parseFloat(v), 'float4'));
|
|
12
13
|
},
|
|
13
14
|
decodeBinary(v, offset = 0) {
|
|
14
15
|
return v.readFloatBE(offset);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { assertCoercedNumber } from '../util/assert-integer.js';
|
|
2
3
|
import { fastParseFloatBuffer } from '../util/fast-parsefloat.js';
|
|
3
4
|
export const Float8Type = {
|
|
4
5
|
name: 'float8',
|
|
@@ -9,7 +10,7 @@ export const Float8Type = {
|
|
|
9
10
|
return '' + v;
|
|
10
11
|
},
|
|
11
12
|
encodeBinary(buf, v) {
|
|
12
|
-
buf.writeDoubleBE(typeof v === 'number' ? v : parseFloat(v));
|
|
13
|
+
buf.writeDoubleBE(typeof v === 'number' ? v : assertCoercedNumber(parseFloat(v), 'float8'));
|
|
13
14
|
},
|
|
14
15
|
decodeBinary(v, offset = 0) {
|
|
15
16
|
return v.readDoubleBE(offset);
|
package/data-types/int2-type.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { assertInteger } from '../util/assert-integer.js';
|
|
2
3
|
import { fastParseInt, fastParseIntBuffer } from '../util/fast-parseint.js';
|
|
3
4
|
export const Int2Type = {
|
|
4
5
|
name: 'int2',
|
|
@@ -8,7 +9,7 @@ export const Int2Type = {
|
|
|
8
9
|
return '' + v;
|
|
9
10
|
},
|
|
10
11
|
encodeBinary(buf, v) {
|
|
11
|
-
buf.writeInt16BE(fastParseInt(v));
|
|
12
|
+
buf.writeInt16BE(assertInteger(fastParseInt(v), 'int2'));
|
|
12
13
|
},
|
|
13
14
|
decodeBinary(v, offset = 0) {
|
|
14
15
|
return v.readInt16BE(offset);
|
package/data-types/int4-type.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { assertInteger } from '../util/assert-integer.js';
|
|
2
3
|
import { fastParseInt, fastParseIntBuffer } from '../util/fast-parseint.js';
|
|
3
4
|
export const Int4Type = {
|
|
4
5
|
name: 'int4',
|
|
@@ -8,7 +9,7 @@ export const Int4Type = {
|
|
|
8
9
|
return '' + v;
|
|
9
10
|
},
|
|
10
11
|
encodeBinary(buf, v) {
|
|
11
|
-
buf.writeInt32BE(fastParseInt(v));
|
|
12
|
+
buf.writeInt32BE(assertInteger(fastParseInt(v), 'int4'));
|
|
12
13
|
},
|
|
13
14
|
decodeBinary(v, offset = 0) {
|
|
14
15
|
return v.readInt32BE(offset);
|
package/data-types/int8-type.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { assertBigIntSource } from '../util/assert-integer.js';
|
|
2
3
|
import { readBigInt64BE } from '../util/bigint-methods.js';
|
|
3
4
|
import { fastParseIntBuffer } from '../util/fast-parseint.js';
|
|
4
5
|
const maxSafeInteger = BigInt(Number.MAX_SAFE_INTEGER);
|
|
@@ -10,7 +11,7 @@ export const Int8Type = {
|
|
|
10
11
|
return '' + v;
|
|
11
12
|
},
|
|
12
13
|
encodeBinary(buf, v) {
|
|
13
|
-
buf.writeBigInt64BE(v);
|
|
14
|
+
buf.writeBigInt64BE(assertBigIntSource(v, 'int8'));
|
|
14
15
|
},
|
|
15
16
|
decodeBinary(buf, offset = 0) {
|
|
16
17
|
const v = typeof buf.readBigInt64BE === 'function'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { assertCoercedNumber } from '../util/assert-integer.js';
|
|
2
3
|
import { fastParseFloatBuffer } from '../util/fast-parsefloat.js';
|
|
3
4
|
const NUMERIC_NEG = 0x4000;
|
|
4
5
|
const NUMERIC_NAN = 0xc000;
|
|
@@ -37,6 +38,12 @@ export const NumericType = {
|
|
|
37
38
|
return writeHeader(0, 0, NUMERIC_PINF, 0);
|
|
38
39
|
if (str === '-Infinity')
|
|
39
40
|
return writeHeader(0, 0, NUMERIC_NINF, 0);
|
|
41
|
+
// Anything with no numeric reading at all - `{}` stringifies to
|
|
42
|
+
// "[object Object]", `true` to "true" - would otherwise be walked
|
|
43
|
+
// digit by digit below and stored as 0.
|
|
44
|
+
// Number('') is 0, not NaN, so an empty string - what `[]` and `null`
|
|
45
|
+
// stringify to - would slip past the NaN check below.
|
|
46
|
+
assertCoercedNumber(str.length ? Number(str) : NaN, 'numeric');
|
|
40
47
|
// Exponent form has no place in the wire format; go through Number to
|
|
41
48
|
// get it back into plain notation. toFixed() cannot do this itself -
|
|
42
49
|
// per spec it falls back to exponential notation once the magnitude
|
package/data-types/oid-type.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeNames, DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { assertInteger } from '../util/assert-integer.js';
|
|
2
3
|
import { fastParseInt, fastParseIntBuffer } from '../util/fast-parseint.js';
|
|
3
4
|
export const OidType = {
|
|
4
5
|
name: 'oid',
|
|
@@ -8,7 +9,7 @@ export const OidType = {
|
|
|
8
9
|
return '' + v;
|
|
9
10
|
},
|
|
10
11
|
encodeBinary(buf, v) {
|
|
11
|
-
buf.writeUInt32BE(fastParseInt(v));
|
|
12
|
+
buf.writeUInt32BE(assertInteger(fastParseInt(v), 'oid'));
|
|
12
13
|
},
|
|
13
14
|
decodeBinary(v, offset = 0) {
|
|
14
15
|
return v.readUInt32BE(offset);
|
package/index.d.ts
CHANGED
|
@@ -8,13 +8,16 @@ export * from './connection/pool.js';
|
|
|
8
8
|
export * from './connection/prepared-statement.js';
|
|
9
9
|
export * from './constants.js';
|
|
10
10
|
export * from './data-type-map.js';
|
|
11
|
+
export * from './interfaces/batch-result.js';
|
|
11
12
|
export * from './interfaces/command-result.js';
|
|
13
|
+
export * from './interfaces/copy-from-rows-options.js';
|
|
12
14
|
export * from './interfaces/data-mapping-options.js';
|
|
13
15
|
export * from './interfaces/data-type.js';
|
|
14
16
|
export * from './interfaces/database-connection-params.js';
|
|
15
17
|
export * from './interfaces/field-info.js';
|
|
16
18
|
export * from './interfaces/function-call-options.js';
|
|
17
19
|
export * from './interfaces/function-call-result.js';
|
|
20
|
+
export * from './interfaces/pipeline-request.js';
|
|
18
21
|
export * from './interfaces/query-options.js';
|
|
19
22
|
export * from './interfaces/query-result.js';
|
|
20
23
|
export * from './interfaces/script-execute-options.js';
|
package/index.js
CHANGED
|
@@ -8,13 +8,16 @@ export * from './connection/pool.js';
|
|
|
8
8
|
export * from './connection/prepared-statement.js';
|
|
9
9
|
export * from './constants.js';
|
|
10
10
|
export * from './data-type-map.js';
|
|
11
|
+
export * from './interfaces/batch-result.js';
|
|
11
12
|
export * from './interfaces/command-result.js';
|
|
13
|
+
export * from './interfaces/copy-from-rows-options.js';
|
|
12
14
|
export * from './interfaces/data-mapping-options.js';
|
|
13
15
|
export * from './interfaces/data-type.js';
|
|
14
16
|
export * from './interfaces/database-connection-params.js';
|
|
15
17
|
export * from './interfaces/field-info.js';
|
|
16
18
|
export * from './interfaces/function-call-options.js';
|
|
17
19
|
export * from './interfaces/function-call-result.js';
|
|
20
|
+
export * from './interfaces/pipeline-request.js';
|
|
18
21
|
export * from './interfaces/query-options.js';
|
|
19
22
|
export * from './interfaces/query-result.js';
|
|
20
23
|
export * from './interfaces/script-execute-options.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FieldInfo } from './field-info.js';
|
|
2
|
+
/**
|
|
3
|
+
* What one parameter set in a batch produced. There is exactly one of
|
|
4
|
+
* these per set handed to `PreparedStatement.executeBatch()`, in the same
|
|
5
|
+
* order - the server answers each Execute with its own CommandComplete,
|
|
6
|
+
* so the mapping is positional and does not depend on the statement.
|
|
7
|
+
*/
|
|
8
|
+
export interface BatchCommandResult {
|
|
9
|
+
/** The command tag PostgreSQL reported for this set (INSERT/UPDATE/...). */
|
|
10
|
+
command?: string;
|
|
11
|
+
/** Rows affected, for INSERT/UPDATE/DELETE - as in QueryResult. */
|
|
12
|
+
rowsAffected?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Rows this set returned, decoded the same way `execute()` decodes them
|
|
15
|
+
* (including any `rowDecoder` given in the options). Only present when
|
|
16
|
+
* the statement actually returns rows - a plain UPDATE without RETURNING
|
|
17
|
+
* leaves this undefined rather than setting it to an empty array.
|
|
18
|
+
*/
|
|
19
|
+
rows?: any[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The result of `PreparedStatement.executeBatch()`.
|
|
23
|
+
*
|
|
24
|
+
* `results` is always the full set of *completed* sets. On success that is
|
|
25
|
+
* every set that was submitted; when the server rejects one, the batch
|
|
26
|
+
* stops there (see the class doc) and the completed prefix is attached to
|
|
27
|
+
* the thrown error as `batchResults` instead.
|
|
28
|
+
*/
|
|
29
|
+
export interface BatchResult {
|
|
30
|
+
/** One entry per parameter set, in submission order. */
|
|
31
|
+
results: BatchCommandResult[];
|
|
32
|
+
/** Sum of every set's `rowsAffected`, for the common bulk-write case. */
|
|
33
|
+
totalRowsAffected: number;
|
|
34
|
+
/**
|
|
35
|
+
* Column descriptions, when the statement returns rows. One copy for the
|
|
36
|
+
* whole batch rather than per set: every set runs the same prepared
|
|
37
|
+
* statement, so the shape cannot differ between them.
|
|
38
|
+
*/
|
|
39
|
+
fields?: FieldInfo[];
|
|
40
|
+
/** Wall time for the whole batch, when `timing` is enabled. */
|
|
41
|
+
executeTime?: number;
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { DataTypeMap } from '../data-type-map.js';
|
|
2
|
+
import type { OID } from '../types.js';
|
|
3
|
+
import type { DataMappingOptions } from './data-mapping-options.js';
|
|
4
|
+
export interface CopyFromRowsOptions extends DataMappingOptions {
|
|
5
|
+
/** Type registry used to encode values; defaults to GlobalTypeMap. */
|
|
6
|
+
typeMap?: DataTypeMap;
|
|
7
|
+
/**
|
|
8
|
+
* Destination columns, in the order the rows supply them. Omitted means
|
|
9
|
+
* every column of the table, in table order - the same default `COPY
|
|
10
|
+
* table FROM STDIN` has without a column list.
|
|
11
|
+
*/
|
|
12
|
+
columns?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Type OIDs for those columns, skipping the round trip that would
|
|
15
|
+
* otherwise ask the server for them.
|
|
16
|
+
*
|
|
17
|
+
* Only worth setting to avoid that probe on a hot path, and only when the
|
|
18
|
+
* OIDs are known to match the table: binary COPY does no conversion, so a
|
|
19
|
+
* wrong OID here is not a mismatch the server can reconcile - it either
|
|
20
|
+
* rejects the stream or, for two types that happen to share a width,
|
|
21
|
+
* stores the bytes as the wrong value.
|
|
22
|
+
*/
|
|
23
|
+
columnTypes?: OID[];
|
|
24
|
+
/**
|
|
25
|
+
* Bytes to accumulate before sending a CopyData message. Defaults to
|
|
26
|
+
* 128KB; anything from 64KB to 256KB measures much the same, while a
|
|
27
|
+
* message per row is roughly ten times slower.
|
|
28
|
+
*/
|
|
29
|
+
chunkSize?: number;
|
|
30
|
+
/**
|
|
31
|
+
* What to do with a value the destination column's type cannot encode -
|
|
32
|
+
* `'abc'` for an integer column, `{}` for a numeric one.
|
|
33
|
+
*
|
|
34
|
+
* `'throw'` (default) aborts the copy, naming the row and column. The
|
|
35
|
+
* other two exist for a load big enough that abandoning it over one bad
|
|
36
|
+
* record costs more than the record is worth: `'null'` writes NULL in
|
|
37
|
+
* that column and keeps the row, `'skip'` drops the row entirely. Both
|
|
38
|
+
* report how many they touched, so a tolerant load still says what it
|
|
39
|
+
* swallowed rather than looking clean.
|
|
40
|
+
*
|
|
41
|
+
* None of them apply to NaN or Infinity in a float or numeric column -
|
|
42
|
+
* PostgreSQL stores those as values distinct from NULL, so they encode
|
|
43
|
+
* normally and are never "invalid".
|
|
44
|
+
*/
|
|
45
|
+
onInvalidValue?: 'throw' | 'null' | 'skip';
|
|
46
|
+
}
|
|
47
|
+
/** What `copyFromRows()` reports back. */
|
|
48
|
+
export interface CopyFromRowsResult {
|
|
49
|
+
/** Rows actually sent to the server. */
|
|
50
|
+
rowCount: number;
|
|
51
|
+
/**
|
|
52
|
+
* Rows dropped under `onInvalidValue: 'skip'`. Always 0 otherwise, and a
|
|
53
|
+
* non-zero value here is the only sign a tolerant load lost anything.
|
|
54
|
+
*/
|
|
55
|
+
skippedRows: number;
|
|
56
|
+
/** Values replaced with NULL under `onInvalidValue: 'null'`. */
|
|
57
|
+
nulledValues: number;
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -83,6 +83,31 @@ export interface DatabaseConnectionParams {
|
|
|
83
83
|
* @default false
|
|
84
84
|
*/
|
|
85
85
|
autoCommit?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Reuse a server-side prepared statement for SQL this connection has run
|
|
88
|
+
* before, instead of parsing it again every time.
|
|
89
|
+
*
|
|
90
|
+
* On by default. A repeated query then costs Bind/Execute rather than
|
|
91
|
+
* Parse/Bind/Describe/Execute, which on fifty concurrent calls of one
|
|
92
|
+
* statement measured 0.97ms against 3.13ms.
|
|
93
|
+
*
|
|
94
|
+
* Turn it off when named prepared statements cannot survive between
|
|
95
|
+
* calls - the usual case being PgBouncer in transaction pooling mode
|
|
96
|
+
* before 1.21, which hands each transaction a different backend, so a
|
|
97
|
+
* statement prepared on one is missing on the next.
|
|
98
|
+
*
|
|
99
|
+
* @default true
|
|
100
|
+
*/
|
|
101
|
+
prepare?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* How many prepared statements one connection keeps. The least recently
|
|
104
|
+
* used is closed when the limit is reached, so an application that
|
|
105
|
+
* builds SQL text dynamically cannot accumulate statements on the server
|
|
106
|
+
* without bound.
|
|
107
|
+
*
|
|
108
|
+
* @default 64
|
|
109
|
+
*/
|
|
110
|
+
preparedStatementCacheSize?: number;
|
|
86
111
|
/**
|
|
87
112
|
* When on, if a statement in a transaction block generates an error,
|
|
88
113
|
* the error is ignored and the transaction continues.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Maybe, OID } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* One statement in a `Connection.pipeline()` call. The `sql` tag's own
|
|
4
|
+
* output satisfies this shape, so a pipeline can be written straight from
|
|
5
|
+
* tagged templates without any conversion.
|
|
6
|
+
*/
|
|
7
|
+
export interface PipelineRequest {
|
|
8
|
+
sql: string;
|
|
9
|
+
params?: any[];
|
|
10
|
+
/**
|
|
11
|
+
* Parameter type OIDs for this statement. Per statement rather than per
|
|
12
|
+
* pipeline: the statements differ, so one shared list could only ever be
|
|
13
|
+
* right for one of them.
|
|
14
|
+
*/
|
|
15
|
+
paramTypes?: Maybe<OID>[];
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -16,6 +16,12 @@ export interface QueryOptions extends DataMappingOptions {
|
|
|
16
16
|
* @default false
|
|
17
17
|
*/
|
|
18
18
|
objectRows?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Overrides the connection's `prepare` setting for this call - `false`
|
|
21
|
+
* to keep a one-off statement out of the cache, `true` to use it on a
|
|
22
|
+
* connection that has caching off.
|
|
23
|
+
*/
|
|
24
|
+
prepare?: boolean;
|
|
19
25
|
/**
|
|
20
26
|
* Controls how each row's raw column data is turned into the value handed back to the
|
|
21
27
|
* caller. `'array'` and `'object'` select the built-in decoders (the same output
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postgrejs",
|
|
3
3
|
"description": "Professional PostgreSQL client NodeJS",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.4.0",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
7
7
|
"dependencies": {
|
|
@@ -29,7 +29,10 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://www.postgrejs.com/",
|
|
31
31
|
"contributors": [
|
|
32
|
-
"Eray Hanoglu <e.hanoglu@panates.com>"
|
|
32
|
+
"Eray Hanoglu <e.hanoglu@panates.com>",
|
|
33
|
+
"Ilker Gurelli <i.gurelli@panates.com>",
|
|
34
|
+
"Onur Tokel <o.tokel@panates.com>",
|
|
35
|
+
"Bircan Yuruk <b.yuruk@panates.com>"
|
|
33
36
|
],
|
|
34
37
|
"repository": {
|
|
35
38
|
"type": "git",
|
|
@@ -1,5 +1,21 @@
|
|
|
1
|
+
import type { BatchCommandResult } from '../interfaces/batch-result.js';
|
|
1
2
|
import { Protocol } from './protocol.js';
|
|
2
3
|
export declare class DatabaseError extends Error {
|
|
4
|
+
/**
|
|
5
|
+
* Which entry of a multi-statement submission the server rejected -
|
|
6
|
+
* PreparedStatement.executeBatch()'s parameter sets, or the statements
|
|
7
|
+
* given to Connection.pipeline(). Entries after this index never ran:
|
|
8
|
+
* PostgreSQL discards everything between an error and the single Sync
|
|
9
|
+
* that closes such a submission.
|
|
10
|
+
*/
|
|
11
|
+
failedIndex?: number;
|
|
12
|
+
/**
|
|
13
|
+
* The sets that had already completed when the batch failed, in
|
|
14
|
+
* submission order - only set by PreparedStatement.executeBatch(). They
|
|
15
|
+
* did run, but the batch shares one implicit transaction, so unless an
|
|
16
|
+
* explicit transaction was already open they are rolled back with it.
|
|
17
|
+
*/
|
|
18
|
+
batchResults?: BatchCommandResult[];
|
|
3
19
|
severity?: string;
|
|
4
20
|
code?: string;
|
|
5
21
|
detail?: string;
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { Protocol } from './protocol.js';
|
|
2
2
|
export class DatabaseError extends Error {
|
|
3
|
+
/**
|
|
4
|
+
* Which entry of a multi-statement submission the server rejected -
|
|
5
|
+
* PreparedStatement.executeBatch()'s parameter sets, or the statements
|
|
6
|
+
* given to Connection.pipeline(). Entries after this index never ran:
|
|
7
|
+
* PostgreSQL discards everything between an error and the single Sync
|
|
8
|
+
* that closes such a submission.
|
|
9
|
+
*/
|
|
10
|
+
failedIndex;
|
|
11
|
+
/**
|
|
12
|
+
* The sets that had already completed when the batch failed, in
|
|
13
|
+
* submission order - only set by PreparedStatement.executeBatch(). They
|
|
14
|
+
* did run, but the batch shares one implicit transaction, so unless an
|
|
15
|
+
* explicit transaction was already open they are rolled back with it.
|
|
16
|
+
*/
|
|
17
|
+
batchResults;
|
|
3
18
|
severity;
|
|
4
19
|
code;
|
|
5
20
|
detail;
|