postgrejs 3.1.3 → 3.3.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 +42 -1
- package/README.md +25 -9
- package/connection/cursor.d.ts +2 -1
- package/connection/cursor.js +6 -6
- package/connection/intl-connection.js +10 -14
- package/connection/prepared-statement.js +2 -1
- package/data-types/bool-type.js +0 -1
- package/data-types/box-type.js +0 -1
- package/data-types/bytea-type.js +5 -3
- package/data-types/char-type.js +2 -2
- package/data-types/circle-type.js +0 -1
- package/data-types/date-type.js +1 -2
- package/data-types/float4-type.js +2 -4
- package/data-types/float8-type.js +2 -5
- package/data-types/int2-type.js +0 -1
- package/data-types/int2-vector-type.js +3 -2
- package/data-types/int4-type.js +0 -1
- package/data-types/int8-type.js +0 -1
- package/data-types/json-type.js +2 -2
- package/data-types/jsonb-type.js +6 -5
- package/data-types/lseg-type.js +0 -1
- package/data-types/numeric-type.js +2 -4
- package/data-types/oid-type.js +0 -1
- package/data-types/oid-vector-type.js +3 -2
- package/data-types/point-type.js +0 -1
- package/data-types/time-type.js +1 -2
- package/data-types/timestamp-type.js +13 -3
- package/data-types/timestamptz-type.js +27 -10
- package/data-types/uuid-type.js +1 -2
- package/data-types/varchar-type.js +2 -2
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/interfaces/command-result.d.ts +3 -2
- package/interfaces/data-type.d.ts +0 -1
- package/interfaces/query-options.d.ts +12 -0
- package/interfaces/script-execute-options.d.ts +12 -0
- package/package.json +14 -2
- package/protocol/backend.js +6 -6
- package/protocol/buffer-reader.d.ts +7 -12
- package/protocol/buffer-reader.js +11 -58
- package/protocol/frontend.js +86 -96
- package/protocol/pg-socket.d.ts +6 -33
- package/protocol/pg-socket.js +19 -74
- package/protocol/pgoutput.js +2 -2
- package/protocol/smart-buffer.d.ts +11 -29
- package/protocol/smart-buffer.js +19 -162
- package/types.d.ts +13 -1
- package/util/bigint-methods.d.ts +0 -1
- package/util/bigint-methods.js +0 -22
- package/util/decode-binaryarray.d.ts +1 -1
- package/util/decode-binaryarray.js +7 -7
- package/util/encode-binaryarray.js +6 -6
- package/util/fast-parsefloat.d.ts +15 -0
- package/util/fast-parsefloat.js +141 -0
- package/util/get-parsers.js +4 -8
- package/util/parse-datetime.js +7 -0
- package/util/parse-pg-timestamp.d.ts +31 -0
- package/util/parse-pg-timestamp.js +199 -0
- package/util/parse-row.js +120 -0
- package/util/row-decoder.d.ts +49 -0
- package/util/row-decoder.js +63 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
-
### [v3.
|
|
3
|
+
### [v3.3.0](https://github.com/panates/postgrejs/compare/v3.2.0...v3.3.0) -
|
|
4
|
+
|
|
5
|
+
#### 🚀 New Features
|
|
6
|
+
|
|
7
|
+
- feat: add first-class Bun support with dedicated benchmark suite @Eray Hanoğlu
|
|
8
|
+
- feat: add pluggable RowDecoder for custom row decoding @Eray Hanoğlu
|
|
9
|
+
|
|
10
|
+
#### 🪲 Fixes
|
|
11
|
+
|
|
12
|
+
- fix: return the actual instant when decoding timestamptz from binary @Eray Hanoğlu
|
|
13
|
+
- fix: point coverage badge at dev instead of the abandoned master branch @Eray Hanoğlu
|
|
14
|
+
|
|
15
|
+
#### 📖 Documentation Changes
|
|
16
|
+
|
|
17
|
+
- docs: refresh both benchmark reports on 3.3.0 @Eray Hanoğlu
|
|
18
|
+
- docs: document Bun support and the Bun benchmark report in the README @Eray Hanoğlu
|
|
19
|
+
- docs: cover the Bun benchmarks in the README's speed section too @Eray Hanoğlu
|
|
20
|
+
- docs: document RowDecoder's data-immutability contract @Eray Hanoğlu
|
|
21
|
+
|
|
22
|
+
#### ⚡️ Performance Improvments
|
|
23
|
+
|
|
24
|
+
- perf: decode timestamps from the wire bytes instead of a string @Eray Hanoğlu
|
|
25
|
+
- perf: build object rows from a compiled literal instead of per-column assignment @Eray Hanoğlu
|
|
26
|
+
- perf: decode float and numeric text without materialising a string @Eray Hanoğlu
|
|
27
|
+
- perf: decode PostgreSQL's own timestamp text shape without the engine parser @Eray Hanoğlu
|
|
28
|
+
- perf: write protocol messages immediately instead of deferring a tick @Eray Hanoğlu
|
|
29
|
+
|
|
30
|
+
#### 💬 General Changes
|
|
31
|
+
|
|
32
|
+
- bench: report near-ties as ties instead of ranking them @Eray Hanoğlu
|
|
33
|
+
- feat!: give decodeBinary the value's length, not a slice of it @Eray Hanoğlu
|
|
34
|
+
- bench: stop measuring type-declaration policy in the Extended Query scenarios @Eray Hanoğlu
|
|
35
|
+
- bench: stop one stalled repeat from making every result a tie @Eray Hanoğlu
|
|
36
|
+
- bench: document why concurrent Simple Query varies its literal @Eray Hanoğlu
|
|
37
|
+
|
|
38
|
+
### [v3.2.0](https://github.com/panates/postgrejs/compare/v3.1.3...v3.2.0) - 12 September 2026
|
|
39
|
+
|
|
40
|
+
#### 🛠 Refactoring and Updates
|
|
41
|
+
|
|
42
|
+
- refactor: build SmartBuffer/BufferReader on top of flexy-buffer @Eray Hanoğlu
|
|
43
|
+
|
|
44
|
+
### [v3.1.3](https://github.com/panates/postgrejs/compare/v3.1.2...v3.1.3) - 11 September 2026
|
|
4
45
|
|
|
5
46
|
#### 🪲 Fixes
|
|
6
47
|
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
## Why PostgreJS?
|
|
14
14
|
|
|
15
|
-
**PostgreJS** is a PostgreSQL driver for Node.js built from the wire protocol up - no `libpq`, no native bindings,
|
|
15
|
+
**PostgreJS** is a PostgreSQL driver for Node.js and Bun built from the wire protocol up - no `libpq`, no native bindings,
|
|
16
16
|
just TypeScript talking directly to PostgreSQL. That from-scratch design is also what makes it fast and light: every
|
|
17
17
|
byte on the wire is handled by code written for exactly that purpose, with a binary-first protocol, shared buffers,
|
|
18
18
|
and row/column decoding pipelines built to avoid unnecessary allocation, instead of generic string plumbing bolted
|
|
@@ -22,11 +22,14 @@ onto a client meant for text.
|
|
|
22
22
|
|
|
23
23
|
The numbers back it up. In PostgreJS's own benchmark suite - run head-to-head against
|
|
24
24
|
[`pg`](https://github.com/brianc/node-postgres) (node-postgres) and [`postgres`](https://github.com/porsager/postgres)
|
|
25
|
-
(postgres.js) on identical workloads - PostgreJS opens a connection up to **3x faster** than postgres.js
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
(postgres.js) on identical workloads - PostgreJS opens a connection up to **3x faster** than postgres.js, pushes
|
|
26
|
+
pooled queries through up to **6.6x faster** than postgres.js, and fetches large result sets nearly **5x faster**
|
|
27
|
+
than pg. It's also the only one of the three Node drivers with a complete binary wire protocol across every data
|
|
28
|
+
type, rather than falling back to text for most of them. See [`doc/BENCHMARKS.md`](doc/BENCHMARKS.md) for the full
|
|
29
|
+
methodology and every scenario.
|
|
30
|
+
|
|
31
|
+
The same suite runs under Bun, where Bun's own built-in `Bun.sql` joins the comparison as a fourth client - see
|
|
32
|
+
[`doc/BENCHMARKS-bun.md`](doc/BENCHMARKS-bun.md).
|
|
30
33
|
|
|
31
34
|
### 🪶 Small Footprint
|
|
32
35
|
|
|
@@ -223,14 +226,27 @@ concurrency, each library run through its own idiomatic fast path. The numbers t
|
|
|
223
226
|
machine via `npm run bench` against the repo's own
|
|
224
227
|
`docker/docker-compose.yml` Postgres instance; see [`benchmark/README.md`](./benchmark/README.md) for details.
|
|
225
228
|
|
|
229
|
+
The same suite runs under Bun via `npm run bench:bun`, where it also measures Bun's own built-in `Bun.sql` client as
|
|
230
|
+
a fourth library. Those results live in [`doc/BENCHMARKS-bun.md`](doc/BENCHMARKS-bun.md) and are deliberately kept
|
|
231
|
+
in a separate report rather than merged into the Node one: GC instrumentation, cursor streaming and binary-format
|
|
232
|
+
control all differ enough between the two runtimes that a single table would conflate a library difference with a
|
|
233
|
+
runtime difference.
|
|
234
|
+
|
|
235
|
+
Both reports list libraries in a fixed order rather than fastest-first, and mark every result statistically tied
|
|
236
|
+
with the leader rather than bolding a lone winner - several scenarios separate the leading drivers by about a
|
|
237
|
+
percent, which is less than the run-to-run spread of the measurement itself.
|
|
238
|
+
|
|
226
239
|
## Support
|
|
227
240
|
|
|
228
241
|
You can report bugs and discuss features on the [GitHub issues](https://github.com/panates/postgrejs/issues) page When
|
|
229
242
|
you open an issue please provide version of NodeJS and PostgreSQL server.
|
|
230
243
|
|
|
231
|
-
##
|
|
244
|
+
## Runtime Compatibility
|
|
232
245
|
|
|
233
|
-
-
|
|
246
|
+
- Node.js >= 20.x
|
|
247
|
+
- Bun - the same test suite runs under Bun in CI against PostgreSQL 18 on every push, so Bun is a supported target
|
|
248
|
+
rather than an untested coincidence. (Coverage is collected on the Node matrix only: `c8` instruments V8's
|
|
249
|
+
coverage APIs, which JavaScriptCore doesn't have.)
|
|
234
250
|
|
|
235
251
|
## License
|
|
236
252
|
|
|
@@ -242,5 +258,5 @@ PostgreJS is available under the [BSD 3-Clause](LICENSE) license.
|
|
|
242
258
|
[downloads-url]: https://npmjs.org/package/postgrejs
|
|
243
259
|
[ci-test-image]: https://github.com/panates/postgrejs/actions/workflows/test.yml/badge.svg
|
|
244
260
|
[ci-test-url]: https://github.com/panates/postgrejs/actions/workflows/test.yml
|
|
245
|
-
[coveralls-image]: https://img.shields.io/coveralls/panates/postgrejs/
|
|
261
|
+
[coveralls-image]: https://img.shields.io/coveralls/panates/postgrejs/dev.svg
|
|
246
262
|
[coveralls-url]: https://coveralls.io/r/panates/postgrejs
|
package/connection/cursor.d.ts
CHANGED
|
@@ -9,12 +9,13 @@ export declare class Cursor extends SafeEventEmitter implements AsyncDisposable
|
|
|
9
9
|
private readonly _portal;
|
|
10
10
|
private readonly _parsers;
|
|
11
11
|
private readonly _queryOptions;
|
|
12
|
+
private readonly _rowDecoder;
|
|
12
13
|
private _taskQueue;
|
|
13
14
|
private _rows;
|
|
14
15
|
private _closed;
|
|
15
16
|
readonly fields: FieldInfo[];
|
|
16
17
|
constructor(statement: PreparedStatement, portal: Portal, fields: FieldInfo[], parsers: AnyParseFunction[], queryOptions: QueryOptions);
|
|
17
|
-
get rowType(): 'array' | 'object';
|
|
18
|
+
get rowType(): 'array' | 'object' | 'custom';
|
|
18
19
|
get isClosed(): boolean;
|
|
19
20
|
next(): Promise<Maybe<Row>>;
|
|
20
21
|
fetch(nRows: number): Promise<Row[]>;
|
package/connection/cursor.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import DoublyLinked from 'doublylinked';
|
|
2
2
|
import { TaskQueue } from 'power-tasks';
|
|
3
3
|
import { SafeEventEmitter } from '../safe-event-emitter.js';
|
|
4
|
-
import {
|
|
4
|
+
import { resolveRowDecoder, resolveRowType } from '../util/row-decoder.js';
|
|
5
5
|
export class Cursor extends SafeEventEmitter {
|
|
6
6
|
_statement;
|
|
7
7
|
_portal;
|
|
8
8
|
_parsers;
|
|
9
9
|
_queryOptions;
|
|
10
|
+
_rowDecoder;
|
|
10
11
|
_taskQueue = new TaskQueue({ concurrency: 1 });
|
|
11
12
|
_rows = new DoublyLinked();
|
|
12
13
|
_closed = false;
|
|
@@ -18,9 +19,10 @@ export class Cursor extends SafeEventEmitter {
|
|
|
18
19
|
this._parsers = parsers;
|
|
19
20
|
this._queryOptions = queryOptions;
|
|
20
21
|
this.fields = fields;
|
|
22
|
+
this._rowDecoder = resolveRowDecoder(queryOptions);
|
|
21
23
|
}
|
|
22
24
|
get rowType() {
|
|
23
|
-
return this._queryOptions
|
|
25
|
+
return resolveRowType(this._queryOptions);
|
|
24
26
|
}
|
|
25
27
|
get isClosed() {
|
|
26
28
|
return this._closed;
|
|
@@ -71,16 +73,14 @@ export class Cursor extends SafeEventEmitter {
|
|
|
71
73
|
if (r && r.rows && r.rows.length) {
|
|
72
74
|
const rows = r.rows;
|
|
73
75
|
if (this._parsers) {
|
|
74
|
-
const objectRows = queryOptions.objectRows;
|
|
75
76
|
const fields = this.fields;
|
|
76
77
|
const parsers = this._parsers;
|
|
78
|
+
const rowDecoder = this._rowDecoder;
|
|
77
79
|
const rowLen = rows.length;
|
|
78
80
|
let i;
|
|
79
81
|
for (i = 0; i < rowLen; i++) {
|
|
80
82
|
const { data, columnCount } = rows[i];
|
|
81
|
-
rows[i] =
|
|
82
|
-
? parseObjectRow(parsers, data, columnCount, this._queryOptions, fields)
|
|
83
|
-
: parseRow(parsers, data, columnCount, this._queryOptions);
|
|
83
|
+
rows[i] = rowDecoder.decode(parsers, data, columnCount, this._queryOptions, fields);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
this._rows.push(...rows);
|
|
@@ -7,7 +7,7 @@ import { SafeEventEmitter } from '../safe-event-emitter.js';
|
|
|
7
7
|
import { getConnectionConfig } from '../util/connection-config.js';
|
|
8
8
|
import { escapeLiteral } from '../util/escape-literal.js';
|
|
9
9
|
import { getParsers } from '../util/get-parsers.js';
|
|
10
|
-
import {
|
|
10
|
+
import { resolveRowDecoder, resolveRowType } from '../util/row-decoder.js';
|
|
11
11
|
import { wrapRowDescription } from '../util/wrap-row-description.js';
|
|
12
12
|
import { CopyFromStream, CopyToStream } from './copy-stream.js';
|
|
13
13
|
const DataFormat = Protocol.DataFormat;
|
|
@@ -398,6 +398,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
398
398
|
let fields;
|
|
399
399
|
let error;
|
|
400
400
|
const typeMap = options.typeMap || GlobalTypeMap;
|
|
401
|
+
const rowDecoder = resolveRowDecoder(options);
|
|
401
402
|
this.runningQueryCount++;
|
|
402
403
|
return await this.socket.sendQueryMessage(sql, (code, msg, done) => {
|
|
403
404
|
switch (code) {
|
|
@@ -426,9 +427,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
426
427
|
break;
|
|
427
428
|
case Protocol.BackendMessageCode.DataRow:
|
|
428
429
|
{
|
|
429
|
-
const row =
|
|
430
|
-
? parseObjectRow(parsers, msg.data, msg.columnCount, options, current.fields)
|
|
431
|
-
: parseRow(parsers, msg.data, msg.columnCount, options);
|
|
430
|
+
const row = rowDecoder.decode(parsers, msg.data, msg.columnCount, options, current.fields);
|
|
432
431
|
if (cb)
|
|
433
432
|
cb('row', row);
|
|
434
433
|
current.rows = current.rows || [];
|
|
@@ -446,8 +445,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
446
445
|
if (timingEnabled)
|
|
447
446
|
current.executeTime = performance.now() - currentStart;
|
|
448
447
|
if (current.rows)
|
|
449
|
-
current.rowType =
|
|
450
|
-
options.objectRows && current.fields ? 'object' : 'array';
|
|
448
|
+
current.rowType = resolveRowType(options);
|
|
451
449
|
result.results.push(current);
|
|
452
450
|
if (cb)
|
|
453
451
|
cb('command-complete', current);
|
|
@@ -497,6 +495,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
497
495
|
let resultFields;
|
|
498
496
|
let commandTag;
|
|
499
497
|
let error;
|
|
498
|
+
const rowDecoder = resolveRowDecoder(options);
|
|
500
499
|
this.runningQueryCount++;
|
|
501
500
|
await this.socket
|
|
502
501
|
.sendExtendedQueryMessages({
|
|
@@ -516,7 +515,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
516
515
|
parsers = getParsers(typeMap, msg.fields);
|
|
517
516
|
resultFields = wrapRowDescription(typeMap, msg.fields, options.columnFormat || DEFAULT_COLUMN_FORMAT);
|
|
518
517
|
result.fields = resultFields;
|
|
519
|
-
result.rowType = options
|
|
518
|
+
result.rowType = resolveRowType(options);
|
|
520
519
|
break;
|
|
521
520
|
case Protocol.BackendMessageCode.DataRow:
|
|
522
521
|
rows.push(msg);
|
|
@@ -547,9 +546,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
547
546
|
const l = rows.length;
|
|
548
547
|
let i;
|
|
549
548
|
for (i = 0; i < l; i++) {
|
|
550
|
-
rows[i] = options
|
|
551
|
-
? parseObjectRow(parsers, rows[i].data, rows[i].columnCount, options, resultFields)
|
|
552
|
-
: parseRow(parsers, rows[i].data, rows[i].columnCount, options);
|
|
549
|
+
rows[i] = rowDecoder.decode(parsers, rows[i].data, rows[i].columnCount, options, resultFields);
|
|
553
550
|
}
|
|
554
551
|
}
|
|
555
552
|
if (result.command === 'DELETE' ||
|
|
@@ -636,6 +633,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
636
633
|
let resultFields;
|
|
637
634
|
let commandTag;
|
|
638
635
|
let error;
|
|
636
|
+
const rowDecoder = resolveRowDecoder(options);
|
|
639
637
|
if (cachedFields) {
|
|
640
638
|
const columnFormat = options.columnFormat != null
|
|
641
639
|
? options.columnFormat
|
|
@@ -664,7 +662,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
664
662
|
});
|
|
665
663
|
}
|
|
666
664
|
result.fields = resultFields;
|
|
667
|
-
result.rowType = options
|
|
665
|
+
result.rowType = resolveRowType(options);
|
|
668
666
|
}
|
|
669
667
|
this.runningQueryCount++;
|
|
670
668
|
await this.socket
|
|
@@ -712,9 +710,7 @@ export class IntlConnection extends SafeEventEmitter {
|
|
|
712
710
|
const l = rows.length;
|
|
713
711
|
let i;
|
|
714
712
|
for (i = 0; i < l; i++) {
|
|
715
|
-
rows[i] = options
|
|
716
|
-
? parseObjectRow(parsers, rows[i].data, rows[i].columnCount, options, resultFields)
|
|
717
|
-
: parseRow(parsers, rows[i].data, rows[i].columnCount, options);
|
|
713
|
+
rows[i] = rowDecoder.decode(parsers, rows[i].data, rows[i].columnCount, options, resultFields);
|
|
718
714
|
}
|
|
719
715
|
}
|
|
720
716
|
if (result.command === 'DELETE' ||
|
|
@@ -4,6 +4,7 @@ import { Protocol } from '../protocol/protocol.js';
|
|
|
4
4
|
import { SafeEventEmitter } from '../safe-event-emitter.js';
|
|
5
5
|
import { withAbortSignal } from '../util/abort-signal.js';
|
|
6
6
|
import { getParsers } from '../util/get-parsers.js';
|
|
7
|
+
import { resolveRowType } from '../util/row-decoder.js';
|
|
7
8
|
import { wrapRowDescription } from '../util/wrap-row-description.js';
|
|
8
9
|
import { Cursor } from './cursor.js';
|
|
9
10
|
import { getIntlConnection } from './intl-connection.js';
|
|
@@ -124,7 +125,7 @@ export class PreparedStatement extends SafeEventEmitter {
|
|
|
124
125
|
const result = {
|
|
125
126
|
command: undefined,
|
|
126
127
|
fields: resultFields,
|
|
127
|
-
rowType: options
|
|
128
|
+
rowType: resolveRowType(options),
|
|
128
129
|
cursor: new Cursor(this, portal, resultFields, parsers, options),
|
|
129
130
|
};
|
|
130
131
|
this._refCount++;
|
package/data-types/bool-type.js
CHANGED
|
@@ -3,7 +3,6 @@ export const BoolType = {
|
|
|
3
3
|
name: 'bool',
|
|
4
4
|
oid: DataTypeOIDs.bool,
|
|
5
5
|
jsType: 'boolean',
|
|
6
|
-
fixedBinarySize: 1,
|
|
7
6
|
// 't'/'f', the form decodeText above reads and the server itself
|
|
8
7
|
// emits - encodeText and decodeText are a public pair and have to stay
|
|
9
8
|
// each other's inverse.
|
package/data-types/box-type.js
CHANGED
package/data-types/bytea-type.js
CHANGED
|
@@ -9,10 +9,12 @@ export const ByteaType = {
|
|
|
9
9
|
return '\\x' + v.toString('hex');
|
|
10
10
|
},
|
|
11
11
|
encodeBinary(buf, v) {
|
|
12
|
-
buf.
|
|
12
|
+
buf.writeBytes(v);
|
|
13
13
|
},
|
|
14
|
-
decodeBinary(v, offset = 0) {
|
|
15
|
-
|
|
14
|
+
decodeBinary(v, offset = 0, len) {
|
|
15
|
+
// A view, not a copy - the value handed back is the caller's own
|
|
16
|
+
// window onto the wire bytes, which is what bytea has always returned.
|
|
17
|
+
return v.subarray(offset, offset + len);
|
|
16
18
|
},
|
|
17
19
|
decodeText: parseBytea,
|
|
18
20
|
decodeTextBuffer: parseByteaBuffer,
|
package/data-types/char-type.js
CHANGED
|
@@ -9,8 +9,8 @@ export const CharType = {
|
|
|
9
9
|
encodeBinary(buf, v) {
|
|
10
10
|
buf.writeString((v ? '' + v : ' ')[0], 'utf8');
|
|
11
11
|
},
|
|
12
|
-
decodeBinary(v, offset = 0) {
|
|
13
|
-
return v.toString('utf8', offset);
|
|
12
|
+
decodeBinary(v, offset = 0, len) {
|
|
13
|
+
return v.toString('utf8', offset, offset + len);
|
|
14
14
|
},
|
|
15
15
|
decodeText(v) {
|
|
16
16
|
return v;
|
package/data-types/date-type.js
CHANGED
|
@@ -6,7 +6,6 @@ export const DateType = {
|
|
|
6
6
|
name: 'date',
|
|
7
7
|
oid: DataTypeOIDs.date,
|
|
8
8
|
jsType: 'Date',
|
|
9
|
-
fixedBinarySize: 4,
|
|
10
9
|
encodeText(v, options) {
|
|
11
10
|
return formatDate(v, options);
|
|
12
11
|
},
|
|
@@ -30,7 +29,7 @@ export const DateType = {
|
|
|
30
29
|
const t = Math.trunc(n + Number.EPSILON);
|
|
31
30
|
buf.writeInt32BE(t);
|
|
32
31
|
},
|
|
33
|
-
decodeBinary(v, offset = 0, options) {
|
|
32
|
+
decodeBinary(v, offset = 0, _len, options) {
|
|
34
33
|
const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.date);
|
|
35
34
|
const t = v.readInt32BE(offset);
|
|
36
35
|
if (t === 0x7fffffff)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { fastParseFloatBuffer } from '../util/fast-parsefloat.js';
|
|
2
3
|
export const Float4Type = {
|
|
3
4
|
name: 'float4',
|
|
4
5
|
oid: DataTypeOIDs.float4,
|
|
5
6
|
jsType: 'number',
|
|
6
|
-
fixedBinarySize: 4,
|
|
7
7
|
encodeText(v) {
|
|
8
8
|
return '' + v;
|
|
9
9
|
},
|
|
@@ -14,9 +14,7 @@ export const Float4Type = {
|
|
|
14
14
|
return v.readFloatBE(offset);
|
|
15
15
|
},
|
|
16
16
|
decodeText: parseFloat,
|
|
17
|
-
decodeTextBuffer
|
|
18
|
-
return parseFloat(buf.toString('latin1', offset, offset + len));
|
|
19
|
-
},
|
|
17
|
+
decodeTextBuffer: fastParseFloatBuffer,
|
|
20
18
|
isType(v) {
|
|
21
19
|
return typeof v === 'number';
|
|
22
20
|
},
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { fastParseFloatBuffer } from '../util/fast-parsefloat.js';
|
|
2
3
|
export const Float8Type = {
|
|
3
4
|
name: 'float8',
|
|
4
5
|
oid: DataTypeOIDs.float8,
|
|
5
6
|
jsType: 'number',
|
|
6
|
-
fixedBinarySize: 8,
|
|
7
7
|
// Infinity/NaN stringify to the words PostgreSQL itself uses.
|
|
8
8
|
encodeText(v) {
|
|
9
9
|
return '' + v;
|
|
@@ -15,10 +15,7 @@ export const Float8Type = {
|
|
|
15
15
|
return v.readDoubleBE(offset);
|
|
16
16
|
},
|
|
17
17
|
decodeText: parseFloat,
|
|
18
|
-
|
|
19
|
-
decodeTextBuffer(buf, offset, len) {
|
|
20
|
-
return parseFloat(buf.toString('latin1', offset, offset + len));
|
|
21
|
-
},
|
|
18
|
+
decodeTextBuffer: fastParseFloatBuffer,
|
|
22
19
|
isType(v) {
|
|
23
20
|
return typeof v === 'number';
|
|
24
21
|
},
|
package/data-types/int2-type.js
CHANGED
|
@@ -15,8 +15,9 @@ export const Int2VectorType = {
|
|
|
15
15
|
encodeCalculateDim(v) {
|
|
16
16
|
return [v.length];
|
|
17
17
|
},
|
|
18
|
-
decodeBinary(v) {
|
|
19
|
-
return decodeBinaryArray(v,
|
|
18
|
+
decodeBinary(v, offset = 0) {
|
|
19
|
+
return (decodeBinaryArray(v, offset, (b, off) => b.readInt16BE(off)) ||
|
|
20
|
+
undefined);
|
|
20
21
|
},
|
|
21
22
|
decodeText(str) {
|
|
22
23
|
return str.split(' ').map(fastParseInt);
|
package/data-types/int4-type.js
CHANGED
package/data-types/int8-type.js
CHANGED
package/data-types/json-type.js
CHANGED
|
@@ -3,8 +3,8 @@ export const JsonType = {
|
|
|
3
3
|
name: 'json',
|
|
4
4
|
oid: DataTypeOIDs.json,
|
|
5
5
|
jsType: 'string',
|
|
6
|
-
decodeBinary(v, offset = 0, options) {
|
|
7
|
-
const content = v.toString('utf8', offset);
|
|
6
|
+
decodeBinary(v, offset = 0, len, options) {
|
|
7
|
+
const content = v.toString('utf8', offset, offset + len);
|
|
8
8
|
const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.json);
|
|
9
9
|
if (fetchAsString)
|
|
10
10
|
return content;
|
package/data-types/jsonb-type.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
-
import { BufferReader } from '../protocol/buffer-reader.js';
|
|
3
2
|
export const JsonbType = {
|
|
4
3
|
name: 'jsonb',
|
|
5
4
|
oid: DataTypeOIDs.jsonb,
|
|
@@ -15,12 +14,14 @@ export const JsonbType = {
|
|
|
15
14
|
buf.writeUInt8(1);
|
|
16
15
|
buf.writeString(JsonbType.encodeText(v, {}), 'utf8');
|
|
17
16
|
},
|
|
18
|
-
decodeBinary(v, offset = 0, options) {
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
decodeBinary(v, offset = 0, len, options) {
|
|
18
|
+
// jsonb's binary form is a one-byte version followed by the JSON text,
|
|
19
|
+
// which carries no terminator of its own - so its end is whatever
|
|
20
|
+
// `len` says, not the end of the buffer it happens to sit in.
|
|
21
|
+
if (v[offset] !== 1)
|
|
21
22
|
throw new Error('Unexpected Jsonb version value in header');
|
|
23
|
+
const content = v.toString('utf8', offset + 1, offset + len);
|
|
22
24
|
const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.jsonb);
|
|
23
|
-
const content = buf.readLString(buf.length - buf.offset);
|
|
24
25
|
if (fetchAsString)
|
|
25
26
|
return content;
|
|
26
27
|
return content ? JSON.parse(content) : undefined;
|
package/data-types/lseg-type.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
|
+
import { fastParseFloatBuffer } from '../util/fast-parsefloat.js';
|
|
2
3
|
const NUMERIC_NEG = 0x4000;
|
|
3
4
|
const NUMERIC_NAN = 0xc000;
|
|
4
5
|
const NUMERIC_PINF = 0xd000;
|
|
@@ -109,10 +110,7 @@ export const NumericType = {
|
|
|
109
110
|
return '' + n;
|
|
110
111
|
},
|
|
111
112
|
decodeText: parseFloat,
|
|
112
|
-
|
|
113
|
-
decodeTextBuffer(buf, offset, len) {
|
|
114
|
-
return parseFloat(buf.toString('latin1', offset, offset + len));
|
|
115
|
-
},
|
|
113
|
+
decodeTextBuffer: fastParseFloatBuffer,
|
|
116
114
|
isType(v) {
|
|
117
115
|
return typeof v === 'number';
|
|
118
116
|
},
|
package/data-types/oid-type.js
CHANGED
|
@@ -21,8 +21,9 @@ export const OidVectorType = {
|
|
|
21
21
|
encodeCalculateDim(v) {
|
|
22
22
|
return [v.length];
|
|
23
23
|
},
|
|
24
|
-
decodeBinary(v) {
|
|
25
|
-
return decodeBinaryArray(v,
|
|
24
|
+
decodeBinary(v, offset = 0) {
|
|
25
|
+
return (decodeBinaryArray(v, offset, (b, off) => b.readUInt32BE(off)) ||
|
|
26
|
+
undefined);
|
|
26
27
|
},
|
|
27
28
|
decodeText(str) {
|
|
28
29
|
return str.split(' ').map(fastParseInt);
|
package/data-types/point-type.js
CHANGED
package/data-types/time-type.js
CHANGED
|
@@ -6,7 +6,6 @@ export const TimeType = {
|
|
|
6
6
|
name: 'time',
|
|
7
7
|
oid: DataTypeOIDs.time,
|
|
8
8
|
jsType: 'string',
|
|
9
|
-
fixedBinarySize: 8,
|
|
10
9
|
encodeText(v, options) {
|
|
11
10
|
return formatTime(v, options);
|
|
12
11
|
},
|
|
@@ -25,7 +24,7 @@ export const TimeType = {
|
|
|
25
24
|
buf.writeInt32BE(hi);
|
|
26
25
|
buf.writeUInt32BE(lo);
|
|
27
26
|
},
|
|
28
|
-
decodeBinary(v, offset = 0, options) {
|
|
27
|
+
decodeBinary(v, offset = 0, _len, options) {
|
|
29
28
|
const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.time);
|
|
30
29
|
const hi = v.readInt32BE(offset);
|
|
31
30
|
const lo = v.readUInt32BE(offset + 4);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { DataTypeOIDs } from '../constants.js';
|
|
2
2
|
import { formatTimestamp } from '../util/format-datetime.js';
|
|
3
3
|
import { parseDateTime } from '../util/parse-datetime.js';
|
|
4
|
+
import { parsePgTimestampBuffer } from '../util/parse-pg-timestamp.js';
|
|
4
5
|
const timeShift = 946684800000;
|
|
5
6
|
const timeMul = 4294967296;
|
|
6
7
|
export const TimestampType = {
|
|
7
8
|
name: 'timestamp',
|
|
8
9
|
oid: DataTypeOIDs.timestamp,
|
|
9
10
|
jsType: 'Date',
|
|
10
|
-
fixedBinarySize: 8,
|
|
11
11
|
encodeText(v, options) {
|
|
12
12
|
return formatTimestamp(v, options);
|
|
13
13
|
},
|
|
@@ -36,7 +36,7 @@ export const TimestampType = {
|
|
|
36
36
|
buf.writeInt32BE(hi);
|
|
37
37
|
buf.writeUInt32BE(lo);
|
|
38
38
|
},
|
|
39
|
-
decodeBinary(v, offset = 0, options) {
|
|
39
|
+
decodeBinary(v, offset = 0, _len, options) {
|
|
40
40
|
const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.timestamp);
|
|
41
41
|
const hi = v.readInt32BE(offset);
|
|
42
42
|
const lo = v.readUInt32BE(offset + 4);
|
|
@@ -56,8 +56,18 @@ export const TimestampType = {
|
|
|
56
56
|
return v;
|
|
57
57
|
return parseDateTime(v, options.utcDates);
|
|
58
58
|
},
|
|
59
|
-
//
|
|
59
|
+
// Reads PostgreSQL's own timestamp shape straight from the wire bytes -
|
|
60
|
+
// see timestamptz-type.ts's decodeTextBuffer for why. The two guards are
|
|
61
|
+
// what decodeText()/parseDateTime() would have applied anyway: with
|
|
62
|
+
// fetchAsString the raw string is the answer, and with utcDates the
|
|
63
|
+
// string has to go through the regex path that reads it as UTC.
|
|
60
64
|
decodeTextBuffer(buf, offset, len, options) {
|
|
65
|
+
if (!options.utcDates &&
|
|
66
|
+
!options.fetchAsString?.includes(DataTypeOIDs.timestamp)) {
|
|
67
|
+
const d = parsePgTimestampBuffer(buf, offset, offset + len);
|
|
68
|
+
if (d !== undefined)
|
|
69
|
+
return d;
|
|
70
|
+
}
|
|
61
71
|
return TimestampType.decodeText(buf.toString('latin1', offset, offset + len), options);
|
|
62
72
|
},
|
|
63
73
|
isType(v) {
|