postgrejs 2.23.0 → 3.0.1

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +162 -1
  2. package/LICENSE +26 -17
  3. package/README.md +195 -175
  4. package/connection/connection.d.ts +152 -13
  5. package/connection/connection.js +296 -49
  6. package/connection/copy-stream.d.ts +88 -0
  7. package/connection/copy-stream.js +244 -0
  8. package/connection/cursor.js +20 -13
  9. package/connection/intl-connection.d.ts +68 -3
  10. package/connection/intl-connection.js +414 -57
  11. package/connection/large-object.d.ts +65 -0
  12. package/connection/large-object.js +151 -0
  13. package/connection/logical-replication.d.ts +121 -0
  14. package/connection/logical-replication.js +279 -0
  15. package/connection/pool.d.ts +61 -4
  16. package/connection/pool.js +177 -29
  17. package/connection/portal.d.ts +18 -4
  18. package/connection/portal.js +53 -59
  19. package/connection/prepared-statement.d.ts +22 -1
  20. package/connection/prepared-statement.js +117 -97
  21. package/data-type-map.d.ts +2 -2
  22. package/data-type-map.js +4 -2
  23. package/data-types/bool-type.js +14 -10
  24. package/data-types/box-type.js +15 -8
  25. package/data-types/bytea-type.js +9 -5
  26. package/data-types/char-type.js +11 -5
  27. package/data-types/circle-type.js +15 -7
  28. package/data-types/date-type.js +25 -19
  29. package/data-types/float4-type.js +10 -3
  30. package/data-types/float8-type.js +12 -3
  31. package/data-types/int2-type.js +9 -4
  32. package/data-types/int2-vector-type.js +10 -4
  33. package/data-types/int4-type.js +11 -5
  34. package/data-types/int8-type.js +22 -7
  35. package/data-types/json-type.js +17 -8
  36. package/data-types/jsonb-type.js +20 -14
  37. package/data-types/lseg-type.js +16 -8
  38. package/data-types/numeric-type.d.ts +8 -0
  39. package/data-types/numeric-type.js +112 -8
  40. package/data-types/oid-type.d.ts +0 -1
  41. package/data-types/oid-type.js +9 -10
  42. package/data-types/oid-vector-type.d.ts +9 -0
  43. package/data-types/oid-vector-type.js +51 -0
  44. package/data-types/point-type.js +14 -6
  45. package/data-types/time-type.js +20 -13
  46. package/data-types/timestamp-type.js +32 -25
  47. package/data-types/timestamptz-type.d.ts +1 -1
  48. package/data-types/timestamptz-type.js +34 -21
  49. package/data-types/uuid-type.js +21 -11
  50. package/data-types/varchar-type.js +16 -4
  51. package/index.d.ts +5 -0
  52. package/index.js +5 -0
  53. package/interfaces/data-type.d.ts +5 -3
  54. package/interfaces/database-connection-params.d.ts +95 -0
  55. package/interfaces/query-options.d.ts +17 -0
  56. package/interfaces/script-execute-options.d.ts +17 -0
  57. package/package.json +3 -4
  58. package/protocol/backend.d.ts +6 -1
  59. package/protocol/backend.js +70 -45
  60. package/protocol/buffer-reader.d.ts +0 -2
  61. package/protocol/buffer-reader.js +3 -10
  62. package/protocol/cert-signature.d.ts +12 -0
  63. package/protocol/cert-signature.js +114 -0
  64. package/protocol/database-error.js +1 -0
  65. package/protocol/frontend.d.ts +16 -0
  66. package/protocol/frontend.js +58 -6
  67. package/protocol/pg-socket.d.ts +221 -10
  68. package/protocol/pg-socket.js +539 -102
  69. package/protocol/pgoutput.d.ts +76 -0
  70. package/protocol/pgoutput.js +155 -0
  71. package/protocol/protocol.d.ts +3 -4
  72. package/protocol/protocol.js +1 -1
  73. package/protocol/sasl.d.ts +11 -1
  74. package/protocol/sasl.js +35 -4
  75. package/protocol/smart-buffer.d.ts +0 -2
  76. package/protocol/smart-buffer.js +11 -16
  77. package/types.d.ts +5 -3
  78. package/util/abort-signal.d.ts +24 -0
  79. package/util/abort-signal.js +63 -0
  80. package/util/array-calculatedim.js +3 -1
  81. package/util/bigint-methods.js +1 -1
  82. package/util/connection-config.js +77 -3
  83. package/util/decode-binaryarray.d.ts +1 -1
  84. package/util/decode-binaryarray.js +15 -6
  85. package/util/encode-binaryarray.js +7 -3
  86. package/util/escape-identifier.d.ts +14 -0
  87. package/util/escape-identifier.js +32 -0
  88. package/util/escape-literal.js +3 -0
  89. package/util/fast-parseint.d.ts +1 -0
  90. package/util/fast-parseint.js +12 -17
  91. package/util/format-datetime.d.ts +10 -0
  92. package/util/format-datetime.js +71 -0
  93. package/util/get-parsers.js +31 -10
  94. package/util/parse-array.js +2 -2
  95. package/util/parse-bytea.d.ts +2 -0
  96. package/util/parse-bytea.js +83 -0
  97. package/util/parse-datetime.d.ts +3 -1
  98. package/util/parse-datetime.js +86 -17
  99. package/util/parse-row.d.ts +3 -1
  100. package/util/parse-row.js +60 -5
  101. package/util/parse-time.js +8 -1
  102. package/util/sql-tag.d.ts +60 -0
  103. package/util/sql-tag.js +202 -0
  104. package/util/starts-transaction.d.ts +38 -0
  105. package/util/starts-transaction.js +148 -0
  106. package/util/stringify-arrayliteral.js +13 -1
  107. package/util/wrap-row-description.js +13 -4
  108. package/util/convert-row-to-object.d.ts +0 -2
  109. package/util/convert-row-to-object.js +0 -9
@@ -3,9 +3,8 @@ import { DEFAULT_COLUMN_FORMAT } from '../constants.js';
3
3
  import { GlobalTypeMap } from '../data-type-map.js';
4
4
  import { Protocol } from '../protocol/protocol.js';
5
5
  import { SafeEventEmitter } from '../safe-event-emitter.js';
6
- import { convertRowToObject } from '../util/convert-row-to-object.js';
6
+ import { withAbortSignal } from '../util/abort-signal.js';
7
7
  import { getParsers } from '../util/get-parsers.js';
8
- import { parseRow } from '../util/parse-row.js';
9
8
  import { wrapRowDescription } from '../util/wrap-row-description.js';
10
9
  import { Cursor } from './cursor.js';
11
10
  import { getIntlConnection } from './intl-connection.js';
@@ -17,8 +16,10 @@ export class PreparedStatement extends SafeEventEmitter {
17
16
  _sql = '';
18
17
  _name = '';
19
18
  _paramTypes;
19
+ _fields;
20
20
  _onErrorSavePoint;
21
21
  _refCount = 0;
22
+ _closed = false;
22
23
  constructor(connection, sql, paramTypes) {
23
24
  super();
24
25
  this._connection = connection;
@@ -30,39 +31,9 @@ export class PreparedStatement extends SafeEventEmitter {
30
31
  static async prepare(connection, sql, options) {
31
32
  const intoCon = getIntlConnection(connection);
32
33
  intoCon.assertConnected();
33
- const socket = intoCon.socket;
34
34
  const statement = new PreparedStatement(connection, sql, options?.paramTypes);
35
- await intoCon.statementQueue
36
- .enqueue(async () => {
37
- intoCon.ref();
38
- try {
39
- socket.sendParseMessage({
40
- statement: statement.name,
41
- sql: statement.sql,
42
- paramTypes: statement.paramTypes,
43
- });
44
- socket.sendFlushMessage();
45
- try {
46
- await socket.capture(async (code, msg, done) => {
47
- if (code === Protocol.BackendMessageCode.ParseComplete)
48
- done();
49
- // May be Protocol.BackendMessageCode.NoticeResponse
50
- });
51
- }
52
- finally {
53
- socket.sendSyncMessage();
54
- await socket.capture(async (code, msg, done) => {
55
- if (code === Protocol.BackendMessageCode.ReadyForQuery)
56
- done();
57
- // May be Protocol.BackendMessageCode.NoticeResponse
58
- });
59
- }
60
- }
61
- finally {
62
- intoCon.unref();
63
- }
64
- })
65
- .toPromise();
35
+ const { fields } = await intoCon.prepareOnce(sql, statement.paramTypes, statement.name);
36
+ statement._fields = fields;
66
37
  statement._refCount = 1;
67
38
  return statement;
68
39
  }
@@ -79,6 +50,12 @@ export class PreparedStatement extends SafeEventEmitter {
79
50
  return this._paramTypes;
80
51
  }
81
52
  async execute(options = {}) {
53
+ const intlCon = getIntlConnection(this.connection);
54
+ if (options.signal)
55
+ return withAbortSignal(options.signal, () => intlCon.cancel(), () => this._executeWithTransaction(options));
56
+ return this._executeWithTransaction(options);
57
+ }
58
+ async _executeWithTransaction(options = {}) {
82
59
  const intlCon = getIntlConnection(this.connection);
83
60
  const transactionCommand = this.sql.match(/^(\bBEGIN\b|\bCOMMIT\b|\bSTART\b|\bROLLBACK|SAVEPOINT|RELEASE\b)/i);
84
61
  let beginFirst = false;
@@ -102,9 +79,7 @@ export class PreparedStatement extends SafeEventEmitter {
102
79
  if (intlCon.inTransaction && rollbackOnError)
103
80
  await intlCon.execute('SAVEPOINT ' + this._onErrorSavePoint);
104
81
  try {
105
- const result = await intlCon.statementQueue
106
- .enqueue(() => this._execute(options))
107
- .toPromise();
82
+ const result = await this._execute(options);
108
83
  if (commitLast)
109
84
  await intlCon.execute('COMMIT');
110
85
  else if (intlCon.inTransaction && rollbackOnError) {
@@ -120,99 +95,144 @@ export class PreparedStatement extends SafeEventEmitter {
120
95
  }
121
96
  }
122
97
  async close() {
98
+ if (this._closed)
99
+ return;
123
100
  --this._refCount;
124
101
  if (this._refCount > 0)
125
102
  return;
126
- const intoCon = getIntlConnection(this.connection);
127
- await intoCon.statementQueue.enqueue(() => this._close()).toPromise();
103
+ this._closed = true;
104
+ await this._close();
128
105
  }
106
+ /**
107
+ * Asks the server to cancel whatever this statement's connection is
108
+ * currently running. See Connection.cancel(); prefer the per-call `signal`
109
+ * option, which reports the abort to the caller that asked for it.
110
+ */
129
111
  async cancel() {
130
- throw new Error('Not implemented yet');
112
+ return getIntlConnection(this.connection).cancel();
131
113
  }
132
114
  async _execute(options = {}) {
133
- let portal;
134
115
  const intlCon = getIntlConnection(this.connection);
135
- intlCon.ref();
136
- try {
137
- const result = { command: undefined };
138
- const startTime = Date.now();
139
- const t = Date.now();
140
- // Create portal
141
- const portalName = 'P_' + ++portalCounter;
142
- portal = new Portal(this, portalName);
143
- await portal.bind(options.params, options);
144
- const fields = await portal.retrieveFields();
145
- const typeMap = options.typeMap || GlobalTypeMap;
146
- let parsers;
147
- let resultFields;
148
- if (fields) {
149
- parsers = getParsers(typeMap, fields);
150
- resultFields = wrapRowDescription(typeMap, fields, options.columnFormat || DEFAULT_COLUMN_FORMAT);
151
- result.fields = resultFields;
152
- result.rowType = options.objectRows ? 'object' : 'array';
153
- if (options.cursor) {
154
- result.cursor = new Cursor(this, portal, resultFields, parsers, options);
155
- this._refCount++;
156
- portal = undefined;
157
- return result;
158
- }
159
- }
160
- const executeResult = await portal.execute(options.fetchCount);
161
- result.executeTime = Date.now() - t;
162
- if (executeResult.command)
163
- result.command = executeResult.command;
164
- if (resultFields && parsers && executeResult.rows) {
165
- if (!result.command)
166
- result.command = 'SELECT';
167
- const rows = (result.rows = executeResult.rows);
168
- const l = rows.length;
169
- let row;
170
- for (let i = 0; i < l; i++) {
171
- row = rows[i];
172
- parseRow(parsers, row, options);
173
- if (options.objectRows) {
174
- rows[i] = convertRowToObject(resultFields, row);
175
- }
176
- }
116
+ if (options.cursor && this._fields) {
117
+ intlCon.ref();
118
+ let portal = new Portal(this, 'P_' + ++portalCounter);
119
+ try {
120
+ const fields = await portal.bindAndRetrieveFields(options.params, options);
121
+ const typeMap = options.typeMap || GlobalTypeMap;
122
+ const parsers = getParsers(typeMap, fields);
123
+ const resultFields = wrapRowDescription(typeMap, fields, options.columnFormat || DEFAULT_COLUMN_FORMAT);
124
+ const result = {
125
+ command: undefined,
126
+ fields: resultFields,
127
+ rowType: options.objectRows ? 'object' : 'array',
128
+ cursor: new Cursor(this, portal, resultFields, parsers, options),
129
+ };
130
+ this._refCount++;
131
+ portal = undefined;
132
+ return result;
177
133
  }
178
- if (result.command === 'DELETE' ||
179
- result.command === 'INSERT' ||
180
- result.command === 'UPDATE') {
181
- result.rowsAffected = executeResult.rowCount;
134
+ finally {
135
+ intlCon.unref();
136
+ if (portal)
137
+ await portal.close().catch(() => undefined);
182
138
  }
183
- result.executeTime = Date.now() - startTime;
184
- return result;
139
+ }
140
+ // Non-cursor path: Bind+Execute+Sync as a single round trip against an
141
+ // unnamed portal, reusing the RowDescription prepare() already fetched
142
+ // instead of re-Describing (and re-Close-ing) a fresh named portal on
143
+ // every call.
144
+ return intlCon.executeReused(this.name, this._fields, this.paramTypes, options.params, options);
145
+ }
146
+ /**
147
+ * Called by Cursor.close() instead of separately awaiting
148
+ * Portal.close() then this.close(). Returns false (no wire action taken,
149
+ * caller must still close just the portal itself) when either this
150
+ * statement was already fully closed by someone else (mirrors close()'s
151
+ * own `if (this._closed) return;` idempotency guard - re-sending
152
+ * Close(statement) for an already-closed name would error) or the
153
+ * decremented refcount is still > 0 (statement still referenced
154
+ * elsewhere - e.g. another cursor from the same PreparedStatement, or
155
+ * the statement's own +1 from prepare()). Returns true only when it
156
+ * actually performed the combined Close(portal)+Close(statement)+Sync.
157
+ */
158
+ async _maybeCloseWithPortal(portalName) {
159
+ if (this._closed)
160
+ return false;
161
+ --this._refCount;
162
+ if (this._refCount > 0)
163
+ return false;
164
+ this._closed = true;
165
+ const intoCon = getIntlConnection(this.connection);
166
+ intoCon.ref();
167
+ try {
168
+ const socket = intoCon.socket;
169
+ let error;
170
+ await socket.sendClosePortalAndStatementMessages({
171
+ portal: { type: 'P', name: portalName },
172
+ statement: { type: 'S', name: this.name },
173
+ }, (code, msg, done) => {
174
+ switch (code) {
175
+ case Protocol.BackendMessageCode.NoticeResponse:
176
+ this.emit('notice', msg);
177
+ break;
178
+ case Protocol.BackendMessageCode.CloseComplete:
179
+ // Arrives twice (once per Close) - neither call is terminal.
180
+ break;
181
+ case Protocol.BackendMessageCode.ErrorResponse:
182
+ error = msg;
183
+ break;
184
+ case Protocol.BackendMessageCode.ReadyForQuery:
185
+ intoCon.transactionStatus = msg.status;
186
+ done(error);
187
+ break;
188
+ default:
189
+ done(new Error(`Server returned unexpected response message (0x${code.toString(16)})`));
190
+ }
191
+ });
185
192
  }
186
193
  finally {
187
- intlCon.unref();
188
- if (portal)
189
- await portal.close();
194
+ intoCon.unref();
190
195
  }
196
+ this.emit('close');
197
+ return true;
191
198
  }
192
199
  async _close() {
193
- if (--this._refCount > 0)
194
- return;
195
200
  const intoCon = getIntlConnection(this.connection);
196
201
  intoCon.ref();
197
202
  try {
198
203
  const socket = intoCon.socket;
199
- socket.sendCloseMessage({ type: 'S', name: this.name });
200
- socket.sendSyncMessage();
201
- await socket.capture(async (code, msg, done) => {
204
+ const closePromise = socket.sendCloseMessage({ type: 'S', name: this.name }, (code, msg, done) => {
202
205
  switch (code) {
203
206
  case Protocol.BackendMessageCode.NoticeResponse:
204
207
  this.emit('notice', msg);
205
208
  break;
206
209
  case Protocol.BackendMessageCode.CloseComplete:
210
+ done();
211
+ break;
212
+ case Protocol.BackendMessageCode.ErrorResponse:
213
+ done(msg);
214
+ break;
215
+ default:
216
+ done(new Error(`Server returned unexpected response message (0x${code.toString(16)})`));
217
+ }
218
+ });
219
+ const syncPromise = socket.sendSyncMessage((code, msg, done) => {
220
+ switch (code) {
221
+ case Protocol.BackendMessageCode.NoticeResponse:
222
+ this.emit('notice', msg);
207
223
  break;
208
224
  case Protocol.BackendMessageCode.ReadyForQuery:
209
225
  intoCon.transactionStatus = msg.status;
210
226
  done();
211
227
  break;
228
+ case Protocol.BackendMessageCode.ErrorResponse:
229
+ done(msg);
230
+ break;
212
231
  default:
213
232
  done(new Error(`Server returned unexpected response message (0x${code.toString(16)})`));
214
233
  }
215
234
  });
235
+ await Promise.all([closePromise, syncPromise]);
216
236
  }
217
237
  finally {
218
238
  intoCon.unref();
@@ -1,5 +1,5 @@
1
- import { DataType } from './interfaces/data-type.js';
2
- import { OID } from './types.js';
1
+ import type { DataType } from './interfaces/data-type.js';
2
+ import type { OID } from './types.js';
3
3
  export declare class DataTypeMap {
4
4
  private _itemsByOID;
5
5
  private _items;
package/data-type-map.js CHANGED
@@ -15,7 +15,8 @@ import { ArrayJsonType, JsonType } from './data-types/json-type.js';
15
15
  import { ArrayJsonbType, JsonbType } from './data-types/jsonb-type.js';
16
16
  import { ArrayLsegType, LsegType } from './data-types/lseg-type.js';
17
17
  import { ArrayNumericType, NumericType } from './data-types/numeric-type.js';
18
- import { ArrayOidType, OidType, VectorOidType } from './data-types/oid-type.js';
18
+ import { ArrayOidType, OidType } from './data-types/oid-type.js';
19
+ import { ArrayOidVectorType, OidVectorType, } from './data-types/oid-vector-type.js';
19
20
  import { ArrayPointType, PointType } from './data-types/point-type.js';
20
21
  import { ArrayTimeType, TimeType } from './data-types/time-type.js';
21
22
  import { ArrayTimestampType, TimestampType, } from './data-types/timestamp-type.js';
@@ -62,7 +63,8 @@ export class DataTypeMap {
62
63
  }
63
64
  }
64
65
  export const GlobalTypeMap = new DataTypeMap();
65
- GlobalTypeMap.register([OidType, VectorOidType, ArrayOidType]);
66
+ GlobalTypeMap.register([OidType, ArrayOidType]);
67
+ GlobalTypeMap.register([OidVectorType, ArrayOidVectorType]);
66
68
  GlobalTypeMap.register([JsonbType, ArrayJsonbType]);
67
69
  GlobalTypeMap.register([JsonType, ArrayJsonType]);
68
70
  GlobalTypeMap.register([BoolType, ArrayBoolType]);
@@ -3,20 +3,24 @@ export const BoolType = {
3
3
  name: 'bool',
4
4
  oid: DataTypeOIDs.bool,
5
5
  jsType: 'boolean',
6
- parseBinary(v) {
7
- return !!v.readUInt8();
6
+ fixedBinarySize: 1,
7
+ // 't'/'f', the form decodeText above reads and the server itself
8
+ // emits - encodeText and decodeText are a public pair and have to stay
9
+ // each other's inverse.
10
+ encodeText(v) {
11
+ return v ? 't' : 'f';
8
12
  },
9
13
  encodeBinary(buf, v) {
10
14
  buf.writeInt8(v ? 1 : 0);
11
15
  },
12
- parseText(v) {
13
- return (v === 'TRUE' ||
14
- v === 't' ||
15
- v === 'true' ||
16
- v === 'y' ||
17
- v === 'yes' ||
18
- v === 'on' ||
19
- v === '1');
16
+ decodeBinary(v, offset = 0) {
17
+ return !!v.readUInt8(offset);
18
+ },
19
+ decodeText(v) {
20
+ return v === 't';
21
+ },
22
+ decodeTextBuffer(buf, offset) {
23
+ return buf[offset] === 0x74; /* 't' */
20
24
  },
21
25
  isType(v) {
22
26
  return typeof v === 'boolean';
@@ -7,13 +7,9 @@ export const BoxType = {
7
7
  oid: DataTypeOIDs.box,
8
8
  jsType: 'object',
9
9
  arraySeparator: ';',
10
- parseBinary(v) {
11
- return {
12
- x1: v.readDoubleBE(0),
13
- y1: v.readDoubleBE(8),
14
- x2: v.readDoubleBE(16),
15
- y2: v.readDoubleBE(24),
16
- };
10
+ fixedBinarySize: 32,
11
+ encodeText(v) {
12
+ return `(${v.x1},${v.y1}),(${v.x2},${v.y2})`;
17
13
  },
18
14
  encodeBinary(buf, v) {
19
15
  buf.writeDoubleBE(v.x1);
@@ -21,7 +17,15 @@ export const BoxType = {
21
17
  buf.writeDoubleBE(v.x2);
22
18
  buf.writeDoubleBE(v.y2);
23
19
  },
24
- parseText(v) {
20
+ decodeBinary(v, offset = 0) {
21
+ return {
22
+ x1: v.readDoubleBE(offset),
23
+ y1: v.readDoubleBE(offset + 8),
24
+ x2: v.readDoubleBE(offset + 16),
25
+ y2: v.readDoubleBE(offset + 24),
26
+ };
27
+ },
28
+ decodeText(v) {
25
29
  const m = v.match(BOX_PATTERN1) || v.match(BOX_PATTERN2) || v.match(BOX_PATTERN3);
26
30
  if (!m)
27
31
  return undefined;
@@ -32,6 +36,9 @@ export const BoxType = {
32
36
  y2: parseFloat(m[4]),
33
37
  };
34
38
  },
39
+ decodeTextBuffer(buf, offset, len, options) {
40
+ return BoxType.decodeText(buf.toString('latin1', offset, offset + len), options);
41
+ },
35
42
  isType(v) {
36
43
  return (typeof v === 'object' &&
37
44
  Object.keys(v).length === 4 &&
@@ -1,17 +1,21 @@
1
- // @ts-ignore -- no type definitions for decodeBytea
2
- import decodeBytea from 'postgres-bytea';
3
1
  import { DataTypeOIDs } from '../constants.js';
2
+ import { parseBytea, parseByteaBuffer } from '../util/parse-bytea.js';
4
3
  export const ByteaType = {
5
4
  name: 'bytea',
6
5
  oid: DataTypeOIDs.bytea,
7
6
  jsType: 'Buffer',
8
- parseBinary(v) {
9
- return v;
7
+ // The hex form PostgreSQL has emitted by default since 9.0.
8
+ encodeText(v) {
9
+ return '\\x' + v.toString('hex');
10
10
  },
11
11
  encodeBinary(buf, v) {
12
12
  buf.writeBuffer(v);
13
13
  },
14
- parseText: decodeBytea,
14
+ decodeBinary(v, offset = 0) {
15
+ return offset ? v.subarray(offset) : v;
16
+ },
17
+ decodeText: parseBytea,
18
+ decodeTextBuffer: parseByteaBuffer,
15
19
  isType(v) {
16
20
  return v instanceof Buffer;
17
21
  },
@@ -3,17 +3,23 @@ export const CharType = {
3
3
  name: 'char',
4
4
  oid: DataTypeOIDs.char,
5
5
  jsType: 'string',
6
- parseBinary(v) {
7
- return v.toString('utf8');
6
+ encodeText(v) {
7
+ return '' + v;
8
8
  },
9
9
  encodeBinary(buf, v) {
10
10
  buf.writeString((v ? '' + v : ' ')[0], 'utf8');
11
11
  },
12
- parseText(v) {
13
- return '' + v;
12
+ decodeBinary(v, offset = 0) {
13
+ return v.toString('utf8', offset);
14
+ },
15
+ decodeText(v) {
16
+ return v;
17
+ },
18
+ decodeTextBuffer(buf, offset, len) {
19
+ return buf.toString('utf8', offset, offset + len);
14
20
  },
15
21
  isType(v) {
16
- return typeof v === 'string' && v.length === 1;
22
+ return typeof v === 'string' && Buffer.byteLength(v, 'utf8') === 1;
17
23
  },
18
24
  };
19
25
  export const ArrayCharType = {
@@ -7,19 +7,23 @@ export const CircleType = {
7
7
  name: 'circle',
8
8
  oid: DataTypeOIDs.circle,
9
9
  jsType: 'object',
10
- parseBinary(v) {
11
- return {
12
- x: v.readDoubleBE(0),
13
- y: v.readDoubleBE(8),
14
- r: v.readDoubleBE(16),
15
- };
10
+ fixedBinarySize: 24,
11
+ encodeText(v) {
12
+ return `<(${v.x},${v.y}),${v.r}>`;
16
13
  },
17
14
  encodeBinary(buf, v) {
18
15
  buf.writeDoubleBE(v.x);
19
16
  buf.writeDoubleBE(v.y);
20
17
  buf.writeDoubleBE(v.r);
21
18
  },
22
- parseText(v) {
19
+ decodeBinary(v, offset = 0) {
20
+ return {
21
+ x: v.readDoubleBE(offset),
22
+ y: v.readDoubleBE(offset + 8),
23
+ r: v.readDoubleBE(offset + 16),
24
+ };
25
+ },
26
+ decodeText(v) {
23
27
  const m = v.match(CIRCLE_PATTERN1) ||
24
28
  v.match(CIRCLE_PATTERN2) ||
25
29
  v.match(CIRCLE_PATTERN3) ||
@@ -32,6 +36,10 @@ export const CircleType = {
32
36
  r: parseFloat(m[3]),
33
37
  };
34
38
  },
39
+ // See box-type.ts's decodeTextBuffer comment - same rationale.
40
+ decodeTextBuffer(buf, offset, len, options) {
41
+ return CircleType.decodeText(buf.toString('latin1', offset, offset + len), options);
42
+ },
35
43
  isType(v) {
36
44
  return (typeof v === 'object' &&
37
45
  Object.keys(v).length === 3 &&
@@ -1,27 +1,18 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
- import { parseDateTime } from '../util/parse-datetime.js';
2
+ import { formatDate } from '../util/format-datetime.js';
3
+ import { parseDate } from '../util/parse-datetime.js';
3
4
  const timeShift = 946684800000;
4
5
  export const DateType = {
5
6
  name: 'date',
6
7
  oid: DataTypeOIDs.date,
7
8
  jsType: 'Date',
8
- parseBinary(v, options) {
9
- const fetchAsString = options.fetchAsString &&
10
- options.fetchAsString.includes(DataTypeOIDs.date);
11
- const t = v.readInt32BE();
12
- if (t === 0x7fffffff)
13
- return fetchAsString ? 'infinity' : Infinity;
14
- if (t === -0x80000000)
15
- return fetchAsString ? '-infinity' : -Infinity;
16
- // Shift from 2000 to 1970
17
- let d = new Date(t * 1000 * 86400 + timeShift);
18
- if (fetchAsString || !options.utcDates)
19
- d = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
20
- return fetchAsString ? dateToDateString(d) : d;
9
+ fixedBinarySize: 4,
10
+ encodeText(v, options) {
11
+ return formatDate(v, options);
21
12
  },
22
13
  encodeBinary(buf, v, options) {
23
14
  if (typeof v === 'string')
24
- v = parseDateTime(v, false, false, options.utcDates);
15
+ v = parseDate(v, options.utcDates);
25
16
  if (v === Infinity) {
26
17
  buf.writeInt32BE(0x7fffffff);
27
18
  return;
@@ -39,12 +30,27 @@ export const DateType = {
39
30
  const t = Math.trunc(n + Number.EPSILON);
40
31
  buf.writeInt32BE(t);
41
32
  },
42
- parseText(v, options) {
43
- const fetchAsString = options.fetchAsString &&
44
- options.fetchAsString.includes(DataTypeOIDs.date);
33
+ decodeBinary(v, offset = 0, options) {
34
+ const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.date);
35
+ const t = v.readInt32BE(offset);
36
+ if (t === 0x7fffffff)
37
+ return fetchAsString ? 'infinity' : Infinity;
38
+ if (t === -0x80000000)
39
+ return fetchAsString ? '-infinity' : -Infinity;
40
+ // Shift from 2000 to 1970
41
+ let d = new Date(t * 1000 * 86400 + timeShift);
42
+ if (fetchAsString || !options.utcDates)
43
+ d = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
44
+ return fetchAsString ? dateToDateString(d) : d;
45
+ },
46
+ decodeText(v, options) {
47
+ const fetchAsString = options.fetchAsString?.includes(DataTypeOIDs.date);
45
48
  if (fetchAsString)
46
49
  return v;
47
- return parseDateTime(v, false, false, options.utcDates);
50
+ return parseDate(v, options.utcDates);
51
+ },
52
+ decodeTextBuffer(buf, offset, len, options) {
53
+ return DateType.decodeText(buf.toString('latin1', offset, offset + len), options);
48
54
  },
49
55
  isType(v) {
50
56
  return (v instanceof Date &&
@@ -3,13 +3,20 @@ export const Float4Type = {
3
3
  name: 'float4',
4
4
  oid: DataTypeOIDs.float4,
5
5
  jsType: 'number',
6
- parseBinary(v) {
7
- return Math.round((v.readFloatBE(0) + Number.EPSILON) * 100) / 100;
6
+ fixedBinarySize: 4,
7
+ encodeText(v) {
8
+ return '' + v;
8
9
  },
9
10
  encodeBinary(buf, v) {
10
11
  buf.writeFloatBE(typeof v === 'number' ? v : parseFloat(v));
11
12
  },
12
- parseText: parseFloat,
13
+ decodeBinary(v, offset = 0) {
14
+ return v.readFloatBE(offset);
15
+ },
16
+ decodeText: parseFloat,
17
+ decodeTextBuffer(buf, offset, len) {
18
+ return parseFloat(buf.toString('latin1', offset, offset + len));
19
+ },
13
20
  isType(v) {
14
21
  return typeof v === 'number';
15
22
  },
@@ -3,13 +3,22 @@ export const Float8Type = {
3
3
  name: 'float8',
4
4
  oid: DataTypeOIDs.float8,
5
5
  jsType: 'number',
6
- parseBinary(v) {
7
- return v.readDoubleBE(0);
6
+ fixedBinarySize: 8,
7
+ // Infinity/NaN stringify to the words PostgreSQL itself uses.
8
+ encodeText(v) {
9
+ return '' + v;
8
10
  },
9
11
  encodeBinary(buf, v) {
10
12
  buf.writeDoubleBE(typeof v === 'number' ? v : parseFloat(v));
11
13
  },
12
- parseText: parseFloat,
14
+ decodeBinary(v, offset = 0) {
15
+ return v.readDoubleBE(offset);
16
+ },
17
+ decodeText: parseFloat,
18
+ // See float4-type.ts's decodeTextBuffer comment - same rationale.
19
+ decodeTextBuffer(buf, offset, len) {
20
+ return parseFloat(buf.toString('latin1', offset, offset + len));
21
+ },
13
22
  isType(v) {
14
23
  return typeof v === 'number';
15
24
  },
@@ -1,16 +1,21 @@
1
1
  import { DataTypeOIDs } from '../constants.js';
2
- import { fastParseInt } from '../util/fast-parseint.js';
2
+ import { fastParseInt, fastParseIntBuffer } from '../util/fast-parseint.js';
3
3
  export const Int2Type = {
4
4
  name: 'int2',
5
5
  oid: DataTypeOIDs.int2,
6
6
  jsType: 'number',
7
- parseBinary(v) {
8
- return v.readInt16BE(0);
7
+ fixedBinarySize: 2,
8
+ encodeText(v) {
9
+ return '' + v;
9
10
  },
10
11
  encodeBinary(buf, v) {
11
12
  buf.writeInt16BE(fastParseInt(v));
12
13
  },
13
- parseText: fastParseInt,
14
+ decodeBinary(v, offset = 0) {
15
+ return v.readInt16BE(offset);
16
+ },
17
+ decodeText: fastParseInt,
18
+ decodeTextBuffer: fastParseIntBuffer,
14
19
  isType(v) {
15
20
  return (typeof v === 'number' && Number.isInteger(v) && v >= -32768 && v <= 32767);
16
21
  },