postgrejs 3.8.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +59 -1
  2. package/README.md +50 -0
  3. package/connection/connection.d.ts +14 -2
  4. package/connection/connection.js +63 -11
  5. package/connection/intl-connection.d.ts +164 -6
  6. package/connection/intl-connection.js +558 -37
  7. package/connection/pool.d.ts +30 -6
  8. package/connection/pool.js +22 -3
  9. package/connection/prepared-statement.js +22 -2
  10. package/data-type-map.d.ts +16 -0
  11. package/data-type-map.js +56 -12
  12. package/data-types/circle-type.js +5 -2
  13. package/data-types/classes/geometric.d.ts +49 -7
  14. package/data-types/classes/geometric.js +59 -11
  15. package/data-types/classes/interval.d.ts +40 -16
  16. package/data-types/classes/interval.js +68 -40
  17. package/data-types/classes/numeric.d.ts +6 -0
  18. package/data-types/classes/numeric.js +8 -0
  19. package/data-types/classes/range.d.ts +6 -1
  20. package/data-types/classes/range.js +6 -1
  21. package/data-types/date-type.js +2 -2
  22. package/data-types/interval-type.js +1 -1
  23. package/data-types/money-type.d.ts +38 -0
  24. package/data-types/money-type.js +212 -0
  25. package/data-types/numeric-type.d.ts +2 -0
  26. package/data-types/numeric-type.js +22 -11
  27. package/data-types/temporal-types.d.ts +19 -0
  28. package/data-types/temporal-types.js +520 -0
  29. package/data-types/timestamp-type.js +2 -2
  30. package/data-types/timestamptz-type.js +2 -2
  31. package/index.d.ts +3 -0
  32. package/index.js +3 -0
  33. package/interfaces/data-mapping-options.d.ts +126 -4
  34. package/interfaces/database-connection-params.d.ts +32 -5
  35. package/interfaces/query-options.d.ts +32 -0
  36. package/interfaces/script-execute-options.d.ts +32 -0
  37. package/interfaces/transaction-options.d.ts +27 -0
  38. package/interfaces/transaction-options.js +1 -0
  39. package/package.json +1 -1
  40. package/protocol/database-error.d.ts +15 -0
  41. package/protocol/database-error.js +19 -0
  42. package/protocol/frontend.js +1 -1
  43. package/protocol/pg-socket.js +7 -10
  44. package/util/array-leaf.d.ts +10 -0
  45. package/util/array-leaf.js +22 -0
  46. package/util/connection-config.js +11 -1
  47. package/util/date-style.d.ts +47 -0
  48. package/util/date-style.js +123 -0
  49. package/util/decimal-as-string.d.ts +11 -0
  50. package/util/decimal-as-string.js +30 -0
  51. package/util/fetch-as-string.d.ts +33 -0
  52. package/util/fetch-as-string.js +122 -0
  53. package/util/get-parsers.js +18 -1
  54. package/util/parse-datetime.d.ts +4 -3
  55. package/util/parse-datetime.js +34 -9
  56. package/util/resolve-column-formats.d.ts +5 -11
  57. package/util/resolve-column-formats.js +28 -24
  58. package/util/row-decoder.d.ts +24 -4
  59. package/util/row-decoder.js +25 -10
  60. package/util/sql-tag.js +2 -1
  61. package/util/stringify-for-sql.js +6 -5
  62. package/util/temporal.d.ts +47 -0
  63. package/util/temporal.js +61 -0
  64. package/util/transaction-command.d.ts +7 -0
  65. package/util/transaction-command.js +32 -0
  66. package/util/unspecified-param.d.ts +18 -8
  67. package/util/unspecified-param.js +22 -14
@@ -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
- export interface PoolPipelineOptions {
14
- pipeline?: boolean;
15
- }
16
- export type PoolQueryOptions = QueryOptions & PoolPipelineOptions;
17
- export type PoolScriptExecuteOptions = ScriptExecuteOptions & PoolPipelineOptions;
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>): 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
@@ -272,10 +272,10 @@ export class Pool extends SafeEventEmitter {
272
272
  * is free to pick a different connection, and a transaction lives on
273
273
  * one. This is the way to get several statements onto the same one.
274
274
  */
275
- async transaction(fn) {
275
+ async transaction(fn, options) {
276
276
  const connection = await this.acquire();
277
277
  try {
278
- return await connection.transaction(fn);
278
+ return await connection.transaction(fn, options);
279
279
  }
280
280
  finally {
281
281
  await this.release(connection);
@@ -361,8 +361,27 @@ export class Pool extends SafeEventEmitter {
361
361
  // connection that could not be created stays unambiguous.
362
362
  this.emit('error', reason);
363
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
+ */
364
383
  _canPipeline(pipeline, sql, autoCommit, signal) {
365
- return (pipeline === true &&
384
+ return ((pipeline != null ? pipeline : this.config.pipeline === true) &&
366
385
  !signal &&
367
386
  this._pipelineMaxQueries > 1 &&
368
387
  this._pipelineMaxConnections > 0 &&
@@ -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,6 +113,7 @@ 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)
@@ -153,7 +159,17 @@ export class PreparedStatement extends SafeEventEmitter {
153
159
  */
154
160
  async _withTransaction(options = {}, fn, canInlineSavepoint = false) {
155
161
  const intlCon = getIntlConnection(this.connection);
156
- const transactionCommand = this.sql.match(/^(\bBEGIN\b|\bCOMMIT\b|\bSTART\b|\bROLLBACK|SAVEPOINT|RELEASE\b)/i);
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);
157
173
  let beginFirst = false;
158
174
  let commitLast = false;
159
175
  const autoCommit = options?.autoCommit;
@@ -170,7 +186,11 @@ export class PreparedStatement extends SafeEventEmitter {
170
186
  await intlCon.execute('BEGIN');
171
187
  // See IntlConnection.execute()'s own rollbackOnError for why
172
188
  // intlCon.inTransaction goes first here but last in the checks below.
173
- const rollbackOnError = !transactionCommand &&
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) &&
174
194
  intlCon.inTransaction &&
175
195
  (options?.rollbackOnError ?? intlCon.config.rollbackOnError ?? true);
176
196
  // When `fn` can put the savepoint on the wire itself, SAVEPOINT and
@@ -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 = this._items.length - 1; i >= 0; i--) {
101
- t = this._items[i];
102
- // Walked newest-registered first, so a later registration of the
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]);
@@ -8,13 +8,16 @@ export const CircleType = {
8
8
  name: 'circle',
9
9
  oid: DataTypeOIDs.circle,
10
10
  jsType: 'Circle',
11
+ // isType() accepts a plain object as well as a Circle, and that object
12
+ // may spell the radius either way - hence Circle.radiusOf() rather
13
+ // than the property.
11
14
  encodeText(v) {
12
- return `<(${v.x},${v.y}),${v.r}>`;
15
+ return `<(${v.x},${v.y}),${Circle.radiusOf(v)}>`;
13
16
  },
14
17
  encodeBinary(buf, v) {
15
18
  buf.writeDoubleBE(v.x);
16
19
  buf.writeDoubleBE(v.y);
17
- buf.writeDoubleBE(v.r);
20
+ buf.writeDoubleBE(Circle.radiusOf(v));
18
21
  },
19
22
  decodeBinary(v, offset = 0) {
20
23
  return new Circle(v.readDoubleBE(offset), v.readDoubleBE(offset + 8), v.readDoubleBE(offset + 16));
@@ -5,19 +5,46 @@ export declare class Point {
5
5
  constructor(x?: number, y?: number);
6
6
  /** As PostgreSQL prints it, so it casts back through `::point`. */
7
7
  toString(): string;
8
- toJSON(): string;
8
+ /**
9
+ * The literal PostgreSQL reads back, for an encoder that asks the value
10
+ * how to write itself - `pg`'s convention, and the reason a value this
11
+ * client decoded can be handed straight to one.
12
+ */
13
+ toPostgres(): string;
9
14
  static isPointLike(v: any): boolean;
10
15
  }
11
16
  /** A `circle`: a centre and a radius. */
12
17
  export declare class Circle {
13
18
  x: number;
14
19
  y: number;
15
- r: number;
16
- constructor(x?: number, y?: number, r?: number);
20
+ radius: number;
21
+ constructor(x?: number, y?: number, radius?: number);
22
+ /**
23
+ * The radius under the name this client used up to 3.9. `pg` spells it
24
+ * `radius`, and a value decoded here should be usable wherever one of
25
+ * its own is, so `radius` is what the property is now - but `r` was a
26
+ * released name, so it stays as an accessor onto the same number
27
+ * rather than silently reading back undefined.
28
+ *
29
+ * @deprecated use `radius`
30
+ */
31
+ get r(): number;
32
+ set r(value: number);
17
33
  /** As PostgreSQL prints it, so it casts back through `::circle`. */
18
34
  toString(): string;
19
- toJSON(): string;
35
+ /**
36
+ * The literal PostgreSQL reads back, for an encoder that asks the value
37
+ * how to write itself - `pg`'s convention, and the reason a value this
38
+ * client decoded can be handed straight to one.
39
+ */
40
+ toPostgres(): string;
20
41
  static isCircleLike(v: any): boolean;
42
+ /**
43
+ * The radius of anything isCircleLike() accepts: a Circle, the plain
44
+ * `{x, y, radius}` object `pg` hands back, or the `{x, y, r}` that was
45
+ * this client's own shape before the classes existed.
46
+ */
47
+ static radiusOf(v: any): number;
21
48
  }
22
49
  /**
23
50
  * Two corners, which is how both `box` and `lseg` are carried - and why
@@ -32,7 +59,12 @@ declare abstract class TwoPoints {
32
59
  x2: number;
33
60
  y2: number;
34
61
  constructor(x1?: number, y1?: number, x2?: number, y2?: number);
35
- toJSON(): string;
62
+ /**
63
+ * The literal PostgreSQL reads back, for an encoder that asks the value
64
+ * how to write itself - `pg`'s convention, and the reason a value this
65
+ * client decoded can be handed straight to one.
66
+ */
67
+ toPostgres(): string;
36
68
  static isTwoPointsLike(v: any): boolean;
37
69
  }
38
70
  /** A `box`: a rectangle given by two opposite corners. */
@@ -61,7 +93,12 @@ export declare class Line {
61
93
  constructor(a?: number, b?: number, c?: number);
62
94
  /** As PostgreSQL prints it, so it casts back through `::line`. */
63
95
  toString(): string;
64
- toJSON(): string;
96
+ /**
97
+ * The literal PostgreSQL reads back, for an encoder that asks the value
98
+ * how to write itself - `pg`'s convention, and the reason a value this
99
+ * client decoded can be handed straight to one.
100
+ */
101
+ toPostgres(): string;
65
102
  static isLineLike(v: any): boolean;
66
103
  }
67
104
  /**
@@ -77,7 +114,12 @@ declare abstract class PointList {
77
114
  x: number;
78
115
  y: number;
79
116
  })[]);
80
- toJSON(): string;
117
+ /**
118
+ * The literal PostgreSQL reads back, for an encoder that asks the value
119
+ * how to write itself - `pg`'s convention, and the reason a value this
120
+ * client decoded can be handed straight to one.
121
+ */
122
+ toPostgres(): string;
81
123
  protected join(): string;
82
124
  }
83
125
  /**
@@ -27,7 +27,12 @@ export class Point {
27
27
  toString() {
28
28
  return `(${this.x},${this.y})`;
29
29
  }
30
- toJSON() {
30
+ /**
31
+ * The literal PostgreSQL reads back, for an encoder that asks the value
32
+ * how to write itself - `pg`'s convention, and the reason a value this
33
+ * client decoded can be handed straight to one.
34
+ */
35
+ toPostgres() {
31
36
  return this.toString();
32
37
  }
33
38
  /* c8 ignore next 3 */
@@ -42,17 +47,37 @@ export class Point {
42
47
  export class Circle {
43
48
  x;
44
49
  y;
45
- r;
46
- constructor(x = 0, y = 0, r = 0) {
50
+ radius;
51
+ constructor(x = 0, y = 0, radius = 0) {
47
52
  this.x = x;
48
53
  this.y = y;
49
- this.r = r;
54
+ this.radius = radius;
55
+ }
56
+ /**
57
+ * The radius under the name this client used up to 3.9. `pg` spells it
58
+ * `radius`, and a value decoded here should be usable wherever one of
59
+ * its own is, so `radius` is what the property is now - but `r` was a
60
+ * released name, so it stays as an accessor onto the same number
61
+ * rather than silently reading back undefined.
62
+ *
63
+ * @deprecated use `radius`
64
+ */
65
+ get r() {
66
+ return this.radius;
67
+ }
68
+ set r(value) {
69
+ this.radius = value;
50
70
  }
51
71
  /** As PostgreSQL prints it, so it casts back through `::circle`. */
52
72
  toString() {
53
- return `<(${this.x},${this.y}),${this.r}>`;
54
- }
55
- toJSON() {
73
+ return `<(${this.x},${this.y}),${this.radius}>`;
74
+ }
75
+ /**
76
+ * The literal PostgreSQL reads back, for an encoder that asks the value
77
+ * how to write itself - `pg`'s convention, and the reason a value this
78
+ * client decoded can be handed straight to one.
79
+ */
80
+ toPostgres() {
56
81
  return this.toString();
57
82
  }
58
83
  static isCircleLike(v) {
@@ -60,7 +85,15 @@ export class Circle {
60
85
  Object.keys(v).length === 3 &&
61
86
  isNum(v.x) &&
62
87
  isNum(v.y) &&
63
- isNum(v.r));
88
+ (isNum(v.radius) || isNum(v.r)));
89
+ }
90
+ /**
91
+ * The radius of anything isCircleLike() accepts: a Circle, the plain
92
+ * `{x, y, radius}` object `pg` hands back, or the `{x, y, r}` that was
93
+ * this client's own shape before the classes existed.
94
+ */
95
+ static radiusOf(v) {
96
+ return v.radius ?? v.r;
64
97
  }
65
98
  }
66
99
  /**
@@ -81,7 +114,12 @@ class TwoPoints {
81
114
  this.x2 = x2;
82
115
  this.y2 = y2;
83
116
  }
84
- toJSON() {
117
+ /**
118
+ * The literal PostgreSQL reads back, for an encoder that asks the value
119
+ * how to write itself - `pg`'s convention, and the reason a value this
120
+ * client decoded can be handed straight to one.
121
+ */
122
+ toPostgres() {
85
123
  return this.toString();
86
124
  }
87
125
  static isTwoPointsLike(v) {
@@ -129,7 +167,12 @@ export class Line {
129
167
  toString() {
130
168
  return `{${this.a},${this.b},${this.c}}`;
131
169
  }
132
- toJSON() {
170
+ /**
171
+ * The literal PostgreSQL reads back, for an encoder that asks the value
172
+ * how to write itself - `pg`'s convention, and the reason a value this
173
+ * client decoded can be handed straight to one.
174
+ */
175
+ toPostgres() {
133
176
  return this.toString();
134
177
  }
135
178
  static isLineLike(v) {
@@ -152,7 +195,12 @@ class PointList {
152
195
  constructor(points = []) {
153
196
  this.points = points.map(p => p instanceof Point ? p : new Point(p.x, p.y));
154
197
  }
155
- toJSON() {
198
+ /**
199
+ * The literal PostgreSQL reads back, for an encoder that asks the value
200
+ * how to write itself - `pg`'s convention, and the reason a value this
201
+ * client decoded can be handed straight to one.
202
+ */
203
+ toPostgres() {
156
204
  return this.toString();
157
205
  }
158
206
  join() {
@@ -1,4 +1,4 @@
1
- /** The fields an Interval is built from. All optional, all defaulting to 0. */
1
+ /** The fields an Interval is built from; it keeps the ones that have a value. */
2
2
  export interface IntervalFields {
3
3
  years?: number;
4
4
  months?: number;
@@ -18,20 +18,29 @@ export interface IntervalFields {
18
18
  * zone, can add one to a timestamp. Anything here that answered "how many
19
19
  * milliseconds is this" would have to invent those lengths.
20
20
  *
21
- * Every field is always present, 0 when the value has none:
21
+ * It carries the fields that have a value and no others, which is what
22
+ * the wire format carries too - three quantities, and a zero among them
23
+ * is not a field:
22
24
  *
23
25
  * ```ts
24
26
  * const iv = new Interval({ days: 1, hours: 2 });
25
- * iv.minutes; // 0, not undefined
27
+ * Object.keys(iv); // ['days', 'hours']
28
+ * iv.minutes; // undefined
26
29
  * String(iv); // '1 day 02:00:00'
27
30
  * iv.toISOString(); // 'P0Y0M1DT2H0M0S'
28
31
  * ```
29
32
  *
30
- * The field names are `pg`'s (`postgres-interval`), so code ported from it
31
- * reads the same values from the same places. The difference is that
32
- * package's objects are sparse - `interval '1 day'` gives it `{days: 1}`
33
- * and nothing else, so `iv.hours + 1` is NaN and a zero interval is `{}` -
34
- * and that it has no `toString()`, so a log line gets `[object Object]`.
33
+ * The field names and the shape are `pg`'s (`postgres-interval`), so code
34
+ * ported from it reads the same values from the same places and a
35
+ * `JSON.stringify` of a row says the same thing. The difference is that
36
+ * this has a `toString()` - `pg`'s object gives `[object Object]` in a log
37
+ * line - and a `toPostgres()`, so the value can be sent back.
38
+ *
39
+ * The fields are optional, so `iv.hours + 1` does not compile without
40
+ * saying what an absent one means (`(iv.hours ?? 0) + 1`). That is the
41
+ * point: which fields a value has depends on the value, not on the type,
42
+ * and the older shape - every field present and 0 - hid that behind
43
+ * numbers nobody wrote.
35
44
  *
36
45
  * `milliseconds` carries the sub-second part and can be fractional, since
37
46
  * PostgreSQL stores microseconds: `interval '0.000001 seconds'` is
@@ -40,17 +49,27 @@ export interface IntervalFields {
40
49
  * A negative interval carries the sign on each field that has one, the way
41
50
  * the server prints it: `interval '-1 day -2 hours'` is
42
51
  * `{ days: -1, hours: -2 }`.
52
+ *
53
+ * `toPostgres()` is `pg`'s convention for "write yourself back", so a
54
+ * value read here can be passed to any encoder that follows it - this
55
+ * client's own parameter path never needed it, since it knows the class.
43
56
  */
44
57
  export declare class Interval implements IntervalFields {
45
- years: number;
46
- months: number;
47
- days: number;
48
- hours: number;
49
- minutes: number;
58
+ years?: number;
59
+ months?: number;
60
+ days?: number;
61
+ hours?: number;
62
+ minutes?: number;
50
63
  /** Whole seconds; the sub-second part is in `milliseconds`. */
51
- seconds: number;
64
+ seconds?: number;
52
65
  /** The sub-second part, fractional down to a microsecond. */
53
- milliseconds: number;
66
+ milliseconds?: number;
67
+ /**
68
+ * Keeps the fields that have a value. A zero is not one: it is what
69
+ * every field of every interval would otherwise be filled with, and
70
+ * `{ days: 1 }` says what `interval '1 day'` is far better than the
71
+ * same thing plus six zeroes nobody wrote.
72
+ */
54
73
  constructor(fields?: IntervalFields);
55
74
  /**
56
75
  * Builds one from the three quantities the wire format carries: a signed
@@ -69,5 +88,10 @@ export declare class Interval implements IntervalFields {
69
88
  toString(): string;
70
89
  /** The interval as an ISO 8601 duration. */
71
90
  toISOString(): string;
72
- toJSON(): string;
91
+ /**
92
+ * The literal PostgreSQL reads back, for an encoder that asks the value
93
+ * how to write itself - `pg`'s convention, and the reason a value this
94
+ * client decoded can be handed straight to one.
95
+ */
96
+ toPostgres(): string;
73
97
  }