@steadybrew/postgresjs 4.4.0 → 4.5.0-rc1
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/README.md +234 -18
- package/cf/src/connection.js +42 -14
- package/cf/src/index.js +45 -9
- package/cf/src/query.js +3 -10
- package/cf/src/subscribe.js +5 -0
- package/cf/src/types.js +5 -0
- package/cjs/src/connection.js +42 -14
- package/cjs/src/index.js +45 -9
- package/cjs/src/query.js +3 -10
- package/cjs/src/subscribe.js +5 -0
- package/cjs/src/types.js +5 -0
- package/package.json +3 -2
- package/src/connection.js +42 -14
- package/src/index.js +45 -9
- package/src/query.js +3 -10
- package/src/subscribe.js +5 -0
- package/src/types.js +5 -0
- package/types/index.d.ts +30 -0
- package/types/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Postgres.js by Steady Brew
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A maintained fork of [Postgres.js](https://github.com/porsager/postgres), the PostgreSQL client created by [Rasmus Porsager](https://github.com/porsager), with the same API.
|
|
4
|
+
|
|
5
|
+
- **Fixes reported Postgres.js bugs:** 40 open upstream issues, from queries receiving another query's rows to hung pools and crashed processes, each covered by a regression test. See [the list](#fixed-postgresjs-issues).
|
|
6
|
+
- **Built for serverless and connection poolers:** bounded connection retries with backoff, idle connections replaced when a frozen instance resumes, pool statistics from `sql.stats()`, and an adapter for Vercel Fluid. Query timeouts and cancellable pool waits are next on the [roadmap](ROADMAP.md).
|
|
7
|
+
- **Switch with one line:** your code and the libraries that import `postgres` keep working. See [Switching from Postgres.js](#switching-from-postgresjs).
|
|
4
8
|
|
|
5
9
|
---
|
|
6
10
|
|
|
@@ -13,33 +17,122 @@ This is an independently maintained fork of [Postgres.js](https://github.com/por
|
|
|
13
17
|
|
|
14
18
|
<br clear="all">
|
|
15
19
|
|
|
16
|
-
## Compatibility
|
|
20
|
+
## Compatibility
|
|
21
|
+
|
|
22
|
+
[](https://github.com/steadybrew/postgresjs/actions/workflows/test.yml?query=branch%3Amain)
|
|
23
|
+
|
|
24
|
+
**Version 4 runs on Node.js 22 or newer** and supports PostgreSQL 15–18. Every release passes the full test matrix before it is published.
|
|
17
25
|
|
|
18
|
-
|
|
26
|
+
| Runtime | PostgreSQL | Notes |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| Node 22, 24 and 26, ESM and CJS | 15–18 | |
|
|
29
|
+
| Deno 1.46.3 | 17 | No Deno 2 claim |
|
|
30
|
+
| Cloudflare Workers (workerd) | 17 | A "Stream was cancelled" rejection on socket close remains ([#1196](https://github.com/porsager/postgres/issues/1196), [#1202](https://github.com/porsager/postgres/issues/1202)) |
|
|
31
|
+
| Bun | | Best effort, not tested in CI |
|
|
19
32
|
|
|
20
|
-
|
|
21
|
-
| --- | --- | ---: | --- |
|
|
22
|
-
| Node 24, ESM and CJS | 15, 16, 17, 18 | 4 | Passed release-candidate CI |
|
|
23
|
-
| Node 26, ESM and CJS | 15, 16, 17, 18 | 4 | Passed release-candidate CI |
|
|
24
|
-
| Deno 1.46.3 | 17 | 1 | Passed release-candidate CI; no Deno 2 claim |
|
|
25
|
-
| Cloudflare workerd, Wrangler 4.123.0 | 17 | 1 | Passed release-candidate CI; known "Stream was cancelled" rejection remains |
|
|
26
|
-
| Bun export | No dedicated target | 0 | Retained as best effort; unverified, without dedicated CI |
|
|
33
|
+
[The test workflow](https://github.com/steadybrew/postgresjs/blob/main/.github/workflows/test.yml) defines which pairs run on each push. Supabase PostgreSQL and Supabase Edge are separate environments; Deno 1.46.3 coverage does not imply Deno 2 or hosted Supabase Edge.
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
## Switching from Postgres.js
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npm install postgres@npm:@steadybrew/postgresjs@^4
|
|
39
|
+
```
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
This installs the fork under the name `postgres`, so `import postgres from 'postgres'` stays as it is, and so do libraries that import it, such as `drizzle-orm/postgres-js`, `drizzle-kit` and `kysely-postgres-js`. Then read [Upgrading to 4.0.0](#upgrading-to-400) for the behaviour changes.
|
|
42
|
+
|
|
43
|
+
Tracing: from 4.5.0, dd-trace records the same database spans as with Postgres.js when the fork is installed under the `postgres` name. Sentry's automatic integration instruments `postgres` versions below 4 only, so widen its range before Sentry loads:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
// sentry-postgres.mjs
|
|
47
|
+
import { SENTRY_RUNTIME_INSTRUMENTATIONS } from '@sentry/server-utils/orchestrion/config'
|
|
48
|
+
|
|
49
|
+
for (const instrumentation of SENTRY_RUNTIME_INSTRUMENTATIONS) {
|
|
50
|
+
if (instrumentation.module.name === 'postgres')
|
|
51
|
+
instrumentation.module.versionRange = '>=3.0.0 <5'
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
node --import ./sentry-postgres.mjs --import @sentry/node/import app.js
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This covers Sentry's runtime hook, not its bundler plugins. With pnpm, add `@sentry/server-utils` at the same version as `@sentry/node` so the file can import it.
|
|
31
60
|
|
|
32
61
|
## Upgrading to 4.0.0
|
|
33
62
|
|
|
34
|
-
Version 4.0.0 fixes connection startup, reservations, reconnects, shutdown, transaction ownership, and built-in array handling. When upgrading from `@steadybrew/postgresjs` 3.x:
|
|
63
|
+
Version 4.0.0 fixes connection startup, reservations, reconnects, shutdown, transaction ownership, and built-in array handling. When upgrading from Postgres.js 3.4 or `@steadybrew/postgresjs` 3.x:
|
|
35
64
|
|
|
36
|
-
- Upgrade to **Node.js
|
|
65
|
+
- Upgrade to **Node.js 22 or newer** before installing.
|
|
37
66
|
- Keep queries within the reservation or transaction that owns them. Reserved handles used after `release()`, and transaction handles used after completion, reject with `CONNECTION_ENDED`. A handle whose connection has closed cannot run queries on a reconnected session.
|
|
38
67
|
- Pass a non-empty string without NUL characters to transaction `sql.prepare(name)`. Invalid names throw `INVALID_TRANSACTION_NAME`.
|
|
39
68
|
- If you use `fetch_types: false`, review code that expects built-in arrays as raw strings: supported built-in arrays are now decoded as JavaScript arrays. SQL `NULL` elements become JavaScript `null`; quoted `"NULL"` text remains a string.
|
|
40
69
|
|
|
41
70
|
See the [changelog](./CHANGELOG.md#breaking-changes-and-migration) for the full migration notes and fixes.
|
|
42
71
|
|
|
72
|
+
## Fixed Postgres.js issues
|
|
73
|
+
|
|
74
|
+
Upstream issues on [porsager/postgres](https://github.com/porsager/postgres/issues) that this fork fixes, and the version that fixed them. Each fix has a test that reproduces the report. The [changelog](./CHANGELOG.md) has the details.
|
|
75
|
+
|
|
76
|
+
<details>
|
|
77
|
+
<summary>40 bugs and 4 feature requests</summary>
|
|
78
|
+
|
|
79
|
+
**Wrong results and data safety**
|
|
80
|
+
|
|
81
|
+
| Issue | What happens now | Since |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| [#1082](https://github.com/porsager/postgres/issues/1082), [#729](https://github.com/porsager/postgres/issues/729) | A query whose values fail to serialize rejects on its own; the query running alongside no longer gets its error, and the next query no longer gets another query's rows | 4.3.0 |
|
|
84
|
+
| [#1216](https://github.com/porsager/postgres/issues/1216) | A transaction handle whose connection closed can't run on a reconnected session, so it can't read another user's rows through RLS | 4.0.0 |
|
|
85
|
+
| [#1248](https://github.com/porsager/postgres/issues/1248) | Statements in a `sql.begin()` callback after its connection closes reject instead of running outside the transaction | 4.0.0 |
|
|
86
|
+
| [#1204](https://github.com/porsager/postgres/issues/1204) | A transaction's connection is never handed to another transaction | 4.0.0 |
|
|
87
|
+
| [#1217](https://github.com/porsager/postgres/issues/1217) | `sql.begin()` rejects with `TRANSACTION_ROLLED_BACK` when the server rolled back the COMMIT | 4.4.0 |
|
|
88
|
+
| [#1249](https://github.com/porsager/postgres/issues/1249), [#1226](https://github.com/porsager/postgres/issues/1226), [#1223](https://github.com/porsager/postgres/issues/1223) | The first query after a reconnect no longer rejects with an error from the closed session | 4.0.0 |
|
|
89
|
+
| [#789](https://github.com/porsager/postgres/issues/789) | `sql.array()` infers the right array type on first use | 4.0.0 |
|
|
90
|
+
|
|
91
|
+
**Hangs**
|
|
92
|
+
|
|
93
|
+
| Issue | What happens now | Since |
|
|
94
|
+
| --- | --- | --- |
|
|
95
|
+
| [#751](https://github.com/porsager/postgres/issues/751), [#1219](https://github.com/porsager/postgres/issues/1219), [#1203](https://github.com/porsager/postgres/issues/1203) | `sql.reserve()` on a pool that hasn't opened a connection yet resolves, with or without `fetch_types` and on Cloudflare Workers | 4.0.0 |
|
|
96
|
+
| [#1195](https://github.com/porsager/postgres/issues/1195), [#1199](https://github.com/porsager/postgres/issues/1199) | A reservation survives its connection being terminated by the server, and the pool recovers after it is released | 4.0.0 |
|
|
97
|
+
| [#1186](https://github.com/porsager/postgres/issues/1186) | Queries queued on a reserved or transaction handle settle when its connection closes | 4.0.0 |
|
|
98
|
+
| [#1097](https://github.com/porsager/postgres/issues/1097), [#1130](https://github.com/porsager/postgres/issues/1130), [#1242](https://github.com/porsager/postgres/issues/1242), [#861](https://github.com/porsager/postgres/issues/861) | `sql.end()` resolves after a disconnect, after a failed query and after a connection closes inside `sql.begin()`, and waits for queries queued in the pool | 4.0.0 |
|
|
99
|
+
| [#1246](https://github.com/porsager/postgres/issues/1246) | A query sent between a socket error and its close settles | 4.1.0 |
|
|
100
|
+
| [#1193](https://github.com/porsager/postgres/issues/1193) | A connection closed during startup settles its first query instead of reconnecting in a tight loop | 4.0.0 |
|
|
101
|
+
| [#1234](https://github.com/porsager/postgres/issues/1234) | Concurrent queries in `sql.begin()` after a column type change roll the transaction back and return the connection to the pool | 4.4.0 |
|
|
102
|
+
|
|
103
|
+
**Crashes**
|
|
104
|
+
|
|
105
|
+
| Issue | What happens now | Since |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| [#1066](https://github.com/porsager/postgres/issues/1066), [#1154](https://github.com/porsager/postgres/issues/1154), [#1208](https://github.com/porsager/postgres/issues/1208), [#1133](https://github.com/porsager/postgres/issues/1133) | A reserved or transaction handle whose backend died rejects its statements instead of throwing an uncaught exception | 4.0.0 |
|
|
108
|
+
| [#1192](https://github.com/porsager/postgres/issues/1192), [#1205](https://github.com/porsager/postgres/issues/1205), [#1086](https://github.com/porsager/postgres/issues/1086) | A failed or cancelled type query at startup rejects the query instead of crashing the process | 4.0.0 |
|
|
109
|
+
| [#1088](https://github.com/porsager/postgres/issues/1088) | Timers no longer fail on Cloudflare Workers | 4.0.0 |
|
|
110
|
+
|
|
111
|
+
**Reconnects and failover**
|
|
112
|
+
|
|
113
|
+
| Issue | What happens now | Since |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| [#1179](https://github.com/porsager/postgres/issues/1179) | Failed connection attempts back off instead of the whole pool retrying at once | 4.0.0 |
|
|
116
|
+
| [#1174](https://github.com/porsager/postgres/issues/1174), [#815](https://github.com/porsager/postgres/issues/815), [#988](https://github.com/porsager/postgres/issues/988) | With several hosts, retries move to the next host, a connect timeout fails over, and `prefer-standby` falls back to a primary | 4.0.0 |
|
|
117
|
+
|
|
118
|
+
**Transactions, notices and subscriptions**
|
|
119
|
+
|
|
120
|
+
| Issue | What happens now | Since |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| [#823](https://github.com/porsager/postgres/issues/823), [#1189](https://github.com/porsager/postgres/issues/1189), [#1210](https://github.com/porsager/postgres/issues/1210) | `sql.begin()` no longer fails with `UNSAFE_TRANSACTION` under write backpressure or with `max_pipeline: 0` | 4.0.0 |
|
|
123
|
+
| [#1063](https://github.com/porsager/postgres/issues/1063) | `onnotice: false` silences notices | 4.4.0 |
|
|
124
|
+
| [#1087](https://github.com/porsager/postgres/issues/1087) | `sql.subscribe()` no longer adds a listener on each subscribe or loses changes after a transform throws | 4.4.0 |
|
|
125
|
+
|
|
126
|
+
**Feature requests**
|
|
127
|
+
|
|
128
|
+
| Issue | What you get | Since |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| [#443](https://github.com/porsager/postgres/issues/443), [#908](https://github.com/porsager/postgres/issues/908) | `sql.stats()` reports connections in use and idle, and queries waiting | 4.3.0 |
|
|
131
|
+
| [#1100](https://github.com/porsager/postgres/issues/1100) | An `onidle` callback and the `@steadybrew/postgresjs/vercel` adapter | 4.2.0 |
|
|
132
|
+
| [#1164](https://github.com/porsager/postgres/issues/1164) | Built-in array types are decoded with `fetch_types: false` | 4.0.0 |
|
|
133
|
+
|
|
134
|
+
</details>
|
|
135
|
+
|
|
43
136
|
## Getting started
|
|
44
137
|
|
|
45
138
|
<br>
|
|
@@ -113,6 +206,7 @@ const { default: postgres } = await import('@steadybrew/postgresjs')
|
|
|
113
206
|
* [Listen & notify](#listen--notify)
|
|
114
207
|
* [Realtime subscribe](#realtime-subscribe)
|
|
115
208
|
* [Numbers, bigint, numeric](#numbers-bigint-numeric)
|
|
209
|
+
* [Parameter types](#parameter-types)
|
|
116
210
|
* [Result Array](#result-array)
|
|
117
211
|
* [Connection details](#connection-details)
|
|
118
212
|
* [Custom Types](#custom-types)
|
|
@@ -151,7 +245,7 @@ Postgres.js utilizes [Tagged template functions](https://developer.mozilla.org/e
|
|
|
151
245
|
1. **Enforcing** safe query generation
|
|
152
246
|
2. Giving the ` sql`` ` function powerful [utility](#dynamic-inserts) and [query building](#building-queries) features.
|
|
153
247
|
|
|
154
|
-
Any generic value will be serialized
|
|
248
|
+
Any generic value will be serialized as described in [Parameter types](#parameter-types), and replaced by a PostgreSQL protocol placeholder `$1, $2, ...`. The values are then sent separately to PostgreSQL, which handles escaping & casting.
|
|
155
249
|
|
|
156
250
|
All queries will return a `Result` array, with objects mapping column names to each row.
|
|
157
251
|
|
|
@@ -592,7 +686,7 @@ for await (const chunk of readableStream) {
|
|
|
592
686
|
|
|
593
687
|
> **NOTE** This is a low-level API which does not provide any type safety. To make this work, you must match your [`copy query` parameters](https://www.postgresql.org/docs/14/sql-copy.html) correctly to your [Node.js stream read or write](https://nodejs.org/api/stream.html) code. Ensure [Node.js stream backpressure](https://nodejs.org/en/learn/modules/backpressuring-in-streams) is handled correctly to avoid memory exhaustion.
|
|
594
688
|
|
|
595
|
-
A `COPY` holds its connection until it finishes. Postgres.js cannot tell that a simple-protocol query is a `COPY` until the server answers, so it sends nothing behind a simple query whose text contains the word `copy` (`sql.unsafe()` or `sql.file()` without parameters, `.simple()`, `.readable()`, `.writable()`). Queries sent through `sql` meanwhile run on another connection or wait for one.
|
|
689
|
+
A `COPY` holds its connection until it finishes. Postgres.js cannot tell that a simple-protocol query is a `COPY` until the server answers, so it sends nothing behind a simple query whose text contains the word `copy` (`sql.unsafe()` or `sql.file()` without parameters, `.simple()`, `.readable()`, `.writable()`). Queries sent through `sql` meanwhile run on another connection or wait for one. When every connection is running a `COPY` that has not been ended, for example with `max: 1`, queries, `sql.reserve()` and `sql.begin()` through `sql` reject with `COPY_IN_PROGRESS` instead, because only that `COPY` finishing could free a connection. Statements on a `sql.reserve()` or `sql.begin()` handle reject with `COPY_IN_PROGRESS` while its `COPY` runs.
|
|
596
690
|
|
|
597
691
|
### Canceling Queries in Progress
|
|
598
692
|
|
|
@@ -977,6 +1071,103 @@ const sql = postgres({
|
|
|
977
1071
|
|
|
978
1072
|
There is currently no guaranteed way to handle `numeric` / `decimal` types in native Javascript. **These [and similar] types will be returned as a `string`**. The best way in this case is to use [custom types](#custom-types).
|
|
979
1073
|
|
|
1074
|
+
## Parameter types
|
|
1075
|
+
|
|
1076
|
+
A `$1` placeholder has a type that PostgreSQL infers from the SQL around it (the column it is compared to or inserted into, a function argument, a cast). Postgres.js has to turn each JavaScript value into text for that type, and `parameter_types` chooses how.
|
|
1077
|
+
|
|
1078
|
+
Some values carry a type Postgres.js can tell from the JavaScript value: `Date`, `Buffer` / `Uint8Array`, booleans, `bigint`, and anything wrapped in `sql.typed(value, oid)`, `sql.types.*`, `sql.json` or `sql.array` with elements. They are serialized for that type and sent in a single round trip, in every mode. Everything else (strings, numbers, plain objects, `sql.typed(value)` without an OID, an empty `sql.array([])`, arrays) has no such type. `null` is always SQL `NULL`.
|
|
1079
|
+
|
|
1080
|
+
### `parameter_types: 'server'` (default)
|
|
1081
|
+
|
|
1082
|
+
For a value whose type Postgres.js cannot tell, it first asks PostgreSQL for the type of the placeholder, which costs an extra round trip on every query with `prepare: false` and the first time each connection runs that SQL with `prepare: true`, and then serializes the value for that type. This has consequences worth knowing:
|
|
1083
|
+
|
|
1084
|
+
- a string sent to a `json` or `jsonb` placeholder is sent as JSON, so `'{"a":1}'` is stored as a JSON string, not an object
|
|
1085
|
+
- a string or number sent to a `date`, `timestamp` or `timestamptz` placeholder goes through `new Date(x).toISOString()`, where a date-only string such as `'2020-01-02'` is read as UTC and a date-and-time string without an offset such as `'2020-01-02T10:00'` is read in the Node.js process time zone (`TZ`)
|
|
1086
|
+
- anything other than `true` sent to a `boolean` placeholder is stored as `false`, including the strings `'true'` and `'t'` and the number `1`
|
|
1087
|
+
- a string sent to a `bytea` placeholder is sent as hex of its UTF-8 bytes
|
|
1088
|
+
- a custom type registered in `types` with a `from` list runs its `serialize` function
|
|
1089
|
+
|
|
1090
|
+
### `parameter_types: 'client'`
|
|
1091
|
+
|
|
1092
|
+
The JavaScript value decides how it is serialized and PostgreSQL parses the text, so a query with values is one round trip:
|
|
1093
|
+
|
|
1094
|
+
- a string is sent unchanged as text
|
|
1095
|
+
- a number is sent as its JavaScript text (`'' + n`)
|
|
1096
|
+
- a plain object (prototype `Object.prototype` or `null`) is sent as `JSON.stringify(x)`
|
|
1097
|
+
- any other object (Decimal, Luxon, moment, URL, class instances) is sent as `'' + x`
|
|
1098
|
+
- `null` is SQL `NULL`; `undefined` is handled as before
|
|
1099
|
+
- a value with a type Postgres.js can tell from the JavaScript value is serialized for that type, as with `'server'`
|
|
1100
|
+
- an array whose elements have no such type still makes Postgres.js ask PostgreSQL for the placeholder's type first, because only PostgreSQL knows whether the placeholder is an array, JSON or a custom type; that query takes the extra round trip
|
|
1101
|
+
|
|
1102
|
+
What this changes compared to `'server'`:
|
|
1103
|
+
|
|
1104
|
+
| Value | Type PostgreSQL infers for the placeholder | `'server'` | `'client'` |
|
|
1105
|
+
| --- | --- | --- | --- |
|
|
1106
|
+
| string | json, jsonb | sent as JSON: stores a JSON string | parsed as JSON; text that is not JSON is rejected |
|
|
1107
|
+
| string | boolean | always `false` | PostgreSQL boolean input (`'t'`, `'yes'`, `'on'`, `'1'` are true); invalid text is rejected |
|
|
1108
|
+
| string | date, timestamp, timestamptz | `new Date(s).toISOString()`: date-only strings are read as UTC, date-and-time strings without an offset in the Node.js process time zone (`TZ`) | parsed by PostgreSQL in the PostgreSQL session's `TimeZone` setting; `date` and `timestamp` ignore an explicit offset |
|
|
1109
|
+
| string | bytea | hex of the UTF-8 bytes | bytea text input; differs only for strings containing backslashes |
|
|
1110
|
+
| string, number | a custom type registered in `types` with a `from` list | the type's `serialize` | sent unchanged as text |
|
|
1111
|
+
| number | json, jsonb | `JSON.stringify` (`NaN` and `Infinity` become `null`) | the number's text (`NaN` and `Infinity` are rejected) |
|
|
1112
|
+
| number | boolean | `false` | `1` is true, `0` is false, anything else is rejected |
|
|
1113
|
+
| number | date, timestamp, timestamptz | `new Date(ms)` | rejected, except `Infinity`; a 6- or 8-digit number is read as a `YYMMDD` or `YYYYMMDD` date |
|
|
1114
|
+
| plain object | json, jsonb | `JSON.stringify` or your json `serialize` | `JSON.stringify` |
|
|
1115
|
+
| plain object | text | `[object Object]` | JSON text |
|
|
1116
|
+
| plain object | a custom type registered in `types` with a `from` list | the type's `serialize` | JSON text, normally rejected |
|
|
1117
|
+
| class instance | json, jsonb | `JSON.stringify`, so `toJSON` applies | `'' + x` |
|
|
1118
|
+
| class instance | date, timestamp, timestamptz | `new Date(x)` | `'' + x`, normally rejected |
|
|
1119
|
+
|
|
1120
|
+
`time`, `timetz` and `interval` placeholders have no serializer in either mode: strings and numbers are sent unchanged as text.
|
|
1121
|
+
|
|
1122
|
+
In practice:
|
|
1123
|
+
|
|
1124
|
+
- pass `new Date(ms)` for epoch numbers, and set `connection: { timezone }` so date strings are read in the time zone you expect
|
|
1125
|
+
- pass `sql.json(s)` for a JSON string value, and `sql.types.name(v)` or `sql.typed(v, oid)` for a custom type; a custom type's `serialize` also runs for a whole array bound to that type, never for a plain value
|
|
1126
|
+
- `NaN`, `Infinity` and `-0` are sent as `NaN`, `Infinity` and `0`, and PostgreSQL's input rules for the placeholder's type decide
|
|
1127
|
+
|
|
1128
|
+
Run `'warn'` first to see which of your queries are affected.
|
|
1129
|
+
|
|
1130
|
+
**Planned for 5.0:** `'client'` becomes the default. Pass `parameter_types: 'server'` to keep the 4.x behaviour.
|
|
1131
|
+
|
|
1132
|
+
### `parameter_types: 'warn'`
|
|
1133
|
+
|
|
1134
|
+
`'warn'` behaves exactly like `'server'`, and also reports every query in which a value would be sent differently by `'client'`. The report goes to `ontypemismatch`, or to `console.warn` when it is not set:
|
|
1135
|
+
|
|
1136
|
+
```js
|
|
1137
|
+
const sql = postgres({
|
|
1138
|
+
parameter_types: 'warn',
|
|
1139
|
+
ontypemismatch: ({ query, parameters }) => {
|
|
1140
|
+
// parameters: [{ position: 2, oid: 3802, typeof: 'string' }]
|
|
1141
|
+
// position is the $n number, oid the type PostgreSQL inferred for the placeholder
|
|
1142
|
+
}
|
|
1143
|
+
})
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
With `debug: true` each entry also carries `serverText` and `clientText`, the two texts that were compared; `clientText` is the error when the `'client'` mode would throw. A query is reported on every execution that has a difference; sample in `ontypemismatch` if that is too much. `'warn'` serializes each untyped value twice and compares the strings. The option can also be set with `?parameter_types=warn` in the connection URL or `PGPARAMETER_TYPES`.
|
|
1147
|
+
|
|
1148
|
+
`'warn'` compares the serialized text, not the stored value, so a report does not always mean the stored value would change.
|
|
1149
|
+
|
|
1150
|
+
These reported cases store the same value under both modes:
|
|
1151
|
+
|
|
1152
|
+
- `0`, `'0'`, `'false'`, `'no'` and `'off'` sent to a `boolean` placeholder (the texts differ, both store `false`)
|
|
1153
|
+
- a date string with an explicit offset sent to a `timestamptz` placeholder, as long as it has no more than millisecond precision (`toISOString()` truncates microseconds, so a string with microseconds stores a different value under `'server'`)
|
|
1154
|
+
- a string without backslashes sent to a `bytea` placeholder
|
|
1155
|
+
|
|
1156
|
+
These reported cases store a different value, or are rejected, under `'client'`:
|
|
1157
|
+
|
|
1158
|
+
- `1`, `'t'`, `'true'`, `'yes'`, `'on'` and `'1'` sent to a `boolean` placeholder (stored `false` today, `true` under `'client'`)
|
|
1159
|
+
- a date string with an explicit offset sent to a `timestamp` or `date` placeholder (`'server'` converts to UTC first, `'client'` drops the offset)
|
|
1160
|
+
- a string sent to a `json` or `jsonb` placeholder, a plain object sent to a `text` placeholder, and a number sent to a date, timestamp or boolean placeholder
|
|
1161
|
+
- a string or number sent to a custom type registered in `types` with a `from` list, which skips its `serialize`
|
|
1162
|
+
|
|
1163
|
+
Date strings without an explicit offset:
|
|
1164
|
+
|
|
1165
|
+
| String | Placeholder | Stored the same under both modes when |
|
|
1166
|
+
| --- | --- | --- |
|
|
1167
|
+
| date and time, such as `'2020-01-02T10:00'` | `timestamptz` | the Node.js process time zone and the PostgreSQL session's `TimeZone` setting match |
|
|
1168
|
+
| date only, such as `'2020-01-02'` | `timestamptz` | the PostgreSQL session's `TimeZone` setting is UTC |
|
|
1169
|
+
| date and time | `timestamp` or `date` | the Node.js process runs in UTC (`'server'` stores the UTC wall time, `'client'` stores the text as written) |
|
|
1170
|
+
|
|
980
1171
|
## Result Array
|
|
981
1172
|
|
|
982
1173
|
The `Result` Array returned from queries is a custom array allowing for easy destructuring or passing on directly to JSON.stringify or general Array usage. It includes the following properties.
|
|
@@ -1057,10 +1248,12 @@ const sql = postgres('postgres://username:password@host:port/database', {
|
|
|
1057
1248
|
// ensure only connecting to primary
|
|
1058
1249
|
fetch_types : true, // Automatically fetches types on connect
|
|
1059
1250
|
// on initial connection.
|
|
1251
|
+
parameter_types : 'server', // 'client' or 'warn', see Parameter types (PGPARAMETER_TYPES)
|
|
1252
|
+
ontypemismatch : fn, // Called by parameter_types: 'warn', default console.warn
|
|
1060
1253
|
})
|
|
1061
1254
|
```
|
|
1062
1255
|
|
|
1063
|
-
The `onnotice`, `onparameter`, `onnotify`, `onclose`, `onidle` and `debug` callbacks passed in the options are called synchronously from inside the driver. If one throws, the error is rethrown as an uncaught exception and the connection and its queries are not affected; under Node's default handling that ends the process.
|
|
1256
|
+
The `onnotice`, `onparameter`, `ontypemismatch`, `onnotify`, `onclose`, `onidle` and `debug` callbacks passed in the options are called synchronously from inside the driver. If one throws, the error is rethrown as an uncaught exception and the connection and its queries are not affected; under Node's default handling that ends the process.
|
|
1064
1257
|
|
|
1065
1258
|
Note that `max_lifetime = 60 * (30 + Math.random() * 30)` by default. This resolves to an interval between 30 and 60 minutes to optimize for the benefits of prepared statements **and** working nicely with Linux's OOM killer.
|
|
1066
1259
|
|
|
@@ -1276,11 +1469,14 @@ const sql = postgres()
|
|
|
1276
1469
|
| `application_name` | `PGAPPNAME` |
|
|
1277
1470
|
| `idle_timeout` | `PGIDLE_TIMEOUT` |
|
|
1278
1471
|
| `connect_timeout` | `PGCONNECT_TIMEOUT` |
|
|
1472
|
+
| `parameter_types` | `PGPARAMETER_TYPES` |
|
|
1279
1473
|
|
|
1280
1474
|
### Prepared statements
|
|
1281
1475
|
|
|
1282
1476
|
Prepared statements will automatically be created for any queries where it can be inferred that the query is static. This can be disabled by using the `prepare: false` option. For instance — this is useful when [using PGBouncer in `transaction mode`](https://github.com/porsager/postgres/issues/93#issuecomment-656290493).
|
|
1283
1477
|
|
|
1478
|
+
With `prepare: false`, `parameter_types: 'client'` sends queries with values in one round trip, unless a value is an untyped array; see [Parameter types](#parameter-types).
|
|
1479
|
+
|
|
1284
1480
|
**update**: [since 1.21.0](https://www.pgbouncer.org/2023/10/pgbouncer-1-21-0)
|
|
1285
1481
|
PGBouncer supports protocol-level named prepared statements when [configured
|
|
1286
1482
|
properly](https://www.pgbouncer.org/config.html#max_prepared_statements)
|
|
@@ -1326,6 +1522,8 @@ const [custom] = await sql`
|
|
|
1326
1522
|
|
|
1327
1523
|
```
|
|
1328
1524
|
|
|
1525
|
+
With `parameter_types: 'client'` a type's `serialize` runs for `sql.typed.name(v)` and for a whole array bound to that type, not for a plain value; see [Parameter types](#parameter-types).
|
|
1526
|
+
|
|
1329
1527
|
### Custom socket
|
|
1330
1528
|
|
|
1331
1529
|
Easily do in-process ssh tunneling to your database by providing a custom socket for Postgres.js to use. The function (optionally async) must return a socket-like duplex stream.
|
|
@@ -1357,6 +1555,17 @@ To ensure proper teardown and cleanup on server restarts use `await sql.end()` b
|
|
|
1357
1555
|
|
|
1358
1556
|
Calling `sql.end()` will reject new queries and return a Promise which resolves when all queries are finished and the underlying connections are closed. If a `{ timeout }` option is provided any pending queries will be rejected once the timeout (in seconds) is reached and the connections will be destroyed.
|
|
1359
1557
|
|
|
1558
|
+
With explicit resource management the pool ends itself at the end of a scope:
|
|
1559
|
+
|
|
1560
|
+
```js
|
|
1561
|
+
async function main() {
|
|
1562
|
+
await using sql = postgres()
|
|
1563
|
+
await sql`select 1`
|
|
1564
|
+
}
|
|
1565
|
+
```
|
|
1566
|
+
|
|
1567
|
+
This calls `sql.end()` when the scope exits, so it suits scripts and jobs, not a shared client at a module's top level. The syntax needs Node.js 24+ or TypeScript 5.2+.
|
|
1568
|
+
|
|
1360
1569
|
#### Sample shutdown using [Prexit](https://github.com/porsager/prexit)
|
|
1361
1570
|
|
|
1362
1571
|
```js
|
|
@@ -1384,6 +1593,13 @@ await reserved.release()
|
|
|
1384
1593
|
|
|
1385
1594
|
Once you have finished with the reserved connection, call `release` to add it back to the pool.
|
|
1386
1595
|
|
|
1596
|
+
```js
|
|
1597
|
+
using reserved = await sql.reserve()
|
|
1598
|
+
await reserved`select * from users`
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
A `using` or `await using` declaration calls `release()` when the scope exits.
|
|
1602
|
+
|
|
1387
1603
|
After release, queries through that reserved handle reject with `CONNECTION_ENDED`. Call `sql.reserve()` again to acquire a new handle. A reserved handle whose connection has closed cannot be reused after reconnect.
|
|
1388
1604
|
|
|
1389
1605
|
## Error handling
|
|
@@ -1468,7 +1684,7 @@ If the only attempt times out, the error is `CONNECT_TIMEOUT`. Once earlier atte
|
|
|
1468
1684
|
##### COPY_IN_PROGRESS
|
|
1469
1685
|
> You cannot execute queries during copy
|
|
1470
1686
|
|
|
1471
|
-
This error is thrown for a statement on a `sql.reserve()` or `sql.begin()` handle while its copy operation (writable / readable) runs, including statements sent before the server started it.
|
|
1687
|
+
This error is thrown for a statement on a `sql.reserve()` or `sql.begin()` handle while its copy operation (writable / readable) runs, including statements sent before the server started it. A query, `sql.reserve()` or `sql.begin()` through `sql` gets it when every connection is running a copy operation whose writable has not been ended; otherwise it waits for a connection.
|
|
1472
1688
|
|
|
1473
1689
|
##### TRANSACTION_ROLLED_BACK
|
|
1474
1690
|
> The server rolled the transaction back instead of committing it
|
package/cf/src/connection.js
CHANGED
|
@@ -6,7 +6,7 @@ import { crypto } from '../polyfills.js'
|
|
|
6
6
|
import Stream from 'node:stream'
|
|
7
7
|
import { performance } from '../polyfills.js'
|
|
8
8
|
|
|
9
|
-
import { stringify, handleValue, addArrayType } from './types.js'
|
|
9
|
+
import { stringify, handleValue, addArrayType, clientSerialize } from './types.js'
|
|
10
10
|
import { Errors, callback } from './errors.js'
|
|
11
11
|
import Result from './result.js'
|
|
12
12
|
import Queue from './queue.js'
|
|
@@ -216,7 +216,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
216
216
|
const connection = {
|
|
217
217
|
queue: queues.closed,
|
|
218
218
|
idleTimer,
|
|
219
|
-
connect
|
|
219
|
+
// Sentry's hook wraps connect and reads the connection from this, so it must stay a method
|
|
220
|
+
connect(request) {
|
|
221
|
+
dispatch('acquire', request)
|
|
222
|
+
},
|
|
220
223
|
terminate: () => dispatch('terminate'),
|
|
221
224
|
execute,
|
|
222
225
|
release: () => dispatch('release'),
|
|
@@ -231,6 +234,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
231
234
|
get pipelined() { return sent.length },
|
|
232
235
|
get accepting() { return accepting() },
|
|
233
236
|
get copying() { return stream !== null },
|
|
237
|
+
get copyHeld() { return stream !== null && !stream.writableEnded },
|
|
234
238
|
get acquiring() { return acquisition && !acquisition.owner.cancelled ? 1 : 0 },
|
|
235
239
|
count: 0,
|
|
236
240
|
id
|
|
@@ -539,7 +543,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
539
543
|
|
|
540
544
|
build(q)
|
|
541
545
|
const written = write(toBuffer(q))
|
|
542
|
-
if (q.describeFirst || q.cursorFn || mayCopy(q))
|
|
546
|
+
if (q.describeFirst || q.cursorFn || mayCopy(q) || (q.prepare && !q.prepared && !q.options.simple && q.parameters.length))
|
|
543
547
|
barrier = q
|
|
544
548
|
return written && accepting()
|
|
545
549
|
} catch (error) {
|
|
@@ -577,7 +581,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
577
581
|
|
|
578
582
|
function prepared(q) {
|
|
579
583
|
return Buffer.concat([
|
|
580
|
-
Bind(q
|
|
584
|
+
Bind(q),
|
|
581
585
|
q.cursorFn
|
|
582
586
|
? Execute('', q.cursorRows)
|
|
583
587
|
: ExecuteUnnamed
|
|
@@ -605,8 +609,11 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
605
609
|
q.signature = q.prepare && types + string
|
|
606
610
|
q.onlyDescribe && (delete statements[q.signature])
|
|
607
611
|
q.parameters = q.parameters || parameters
|
|
612
|
+
q.inferred = options.parameter_types === 'server' ? null : types.slice()
|
|
608
613
|
q.prepared = q.prepare && q.signature in statements
|
|
609
|
-
q.describeFirst = q.onlyDescribe || (
|
|
614
|
+
q.describeFirst = q.onlyDescribe || (!q.prepared && types.some((x, i) =>
|
|
615
|
+
!x && (options.parameter_types !== 'client' || Array.isArray(q.parameters[i]))
|
|
616
|
+
))
|
|
610
617
|
q.statement = q.prepared
|
|
611
618
|
? statements[q.signature]
|
|
612
619
|
: { string, types, name: q.prepare ? statementId + statementCount++ : '' }
|
|
@@ -690,7 +697,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
690
697
|
err = new Error(err)
|
|
691
698
|
|
|
692
699
|
'query' in err || 'parameters' in err || Object.defineProperties(err, {
|
|
693
|
-
stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug },
|
|
700
|
+
stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug, writable: true, configurable: true },
|
|
694
701
|
query: { value: query.string, enumerable: options.debug },
|
|
695
702
|
parameters: { value: query.parameters, enumerable: options.debug },
|
|
696
703
|
args: { value: query.args, enumerable: options.debug },
|
|
@@ -1214,20 +1221,21 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1214
1221
|
}
|
|
1215
1222
|
|
|
1216
1223
|
/* Messages */
|
|
1217
|
-
function Bind(
|
|
1224
|
+
function Bind(q) {
|
|
1225
|
+
const differences = []
|
|
1218
1226
|
let prev
|
|
1219
1227
|
, type
|
|
1220
1228
|
|
|
1221
|
-
b().B().str(
|
|
1229
|
+
b().B().str((q.cursorName || '') + b.N).str(q.statement.name + b.N).i16(0).i16(q.parameters.length)
|
|
1222
1230
|
|
|
1223
|
-
parameters.forEach((x, i) => {
|
|
1231
|
+
q.parameters.forEach((x, i) => {
|
|
1224
1232
|
if (x === null)
|
|
1225
1233
|
return b.i32(0xFFFFFFFF)
|
|
1226
1234
|
|
|
1227
|
-
type = types[i]
|
|
1228
|
-
x =
|
|
1229
|
-
?
|
|
1230
|
-
: ''
|
|
1235
|
+
type = q.statement.types[i]
|
|
1236
|
+
x = !q.inferred || q.inferred[i] || Array.isArray(x)
|
|
1237
|
+
? serialize(x, type)
|
|
1238
|
+
: options.parameter_types === 'client' ? clientSerialize(x) : compare(x, type, i, differences)
|
|
1231
1239
|
|
|
1232
1240
|
prev = b.i
|
|
1233
1241
|
b.inc(4).str(x).i32(b.i - prev - 4, prev)
|
|
@@ -1235,7 +1243,27 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1235
1243
|
|
|
1236
1244
|
b.i16(0)
|
|
1237
1245
|
|
|
1238
|
-
|
|
1246
|
+
const message = b.end()
|
|
1247
|
+
differences.length && callback(options.ontypemismatch || console.warn, { query: q.string, parameters: differences }) // eslint-disable-line
|
|
1248
|
+
return message
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
function serialize(x, type) {
|
|
1252
|
+
return type in options.serializers
|
|
1253
|
+
? options.serializers[type](x)
|
|
1254
|
+
: '' + x
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
function compare(x, type, i, differences) {
|
|
1258
|
+
const serverText = serialize(x, type)
|
|
1259
|
+
let clientText
|
|
1260
|
+
try {
|
|
1261
|
+
clientText = clientSerialize(x)
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
clientText = error
|
|
1264
|
+
}
|
|
1265
|
+
serverText === clientText || differences.push({ position: i + 1, oid: type, typeof: typeof x, ...options.debug && { serverText, clientText } })
|
|
1266
|
+
return serverText
|
|
1239
1267
|
}
|
|
1240
1268
|
|
|
1241
1269
|
function Parse(str, parameters, types, name = '') {
|
package/cf/src/index.js
CHANGED
|
@@ -87,7 +87,8 @@ function Postgres(a, b) {
|
|
|
87
87
|
listen,
|
|
88
88
|
begin,
|
|
89
89
|
close,
|
|
90
|
-
end
|
|
90
|
+
end,
|
|
91
|
+
...Symbol.asyncDispose && { [Symbol.asyncDispose]: () => end() }
|
|
91
92
|
})
|
|
92
93
|
|
|
93
94
|
return sql
|
|
@@ -274,7 +275,9 @@ function Postgres(a, b) {
|
|
|
274
275
|
const query = { reserve: c => resolve(own(c, l)), reject }
|
|
275
276
|
closed.length
|
|
276
277
|
? connect(closed.shift(), query)
|
|
277
|
-
:
|
|
278
|
+
: allHeldByCopy()
|
|
279
|
+
? reject(copyInProgress())
|
|
280
|
+
: queries.push(query)
|
|
278
281
|
})
|
|
279
282
|
return l
|
|
280
283
|
}
|
|
@@ -326,10 +329,17 @@ function Postgres(a, b) {
|
|
|
326
329
|
|
|
327
330
|
async function reserve() {
|
|
328
331
|
const l = await lease(false)
|
|
329
|
-
const sql = Sql(
|
|
332
|
+
const sql = Sql(handler)
|
|
330
333
|
sql.release = () => settle(l, Lease.Released)
|
|
334
|
+
Symbol.dispose && (sql[Symbol.dispose] = () => sql.release())
|
|
335
|
+
Symbol.asyncDispose && (sql[Symbol.asyncDispose] = async() => sql.release())
|
|
331
336
|
sql.stats = stats
|
|
332
337
|
return sql
|
|
338
|
+
|
|
339
|
+
// dd-trace starts query spans only inside function declarations named handler
|
|
340
|
+
function handler(q) {
|
|
341
|
+
send(l, q)
|
|
342
|
+
}
|
|
333
343
|
}
|
|
334
344
|
|
|
335
345
|
async function begin(options, fn) {
|
|
@@ -346,7 +356,7 @@ function Postgres(a, b) {
|
|
|
346
356
|
}
|
|
347
357
|
|
|
348
358
|
async function transaction() {
|
|
349
|
-
await Sql(
|
|
359
|
+
await Sql(handler).unsafe('begin ' + options.replace(/[^a-z ]/ig, ''), []).execute().catch(e => {
|
|
350
360
|
e instanceof PostgresError && settle(l, Lease.Released)
|
|
351
361
|
throw e
|
|
352
362
|
})
|
|
@@ -357,7 +367,7 @@ function Postgres(a, b) {
|
|
|
357
367
|
let result
|
|
358
368
|
l.state = Lease.Settling
|
|
359
369
|
try {
|
|
360
|
-
result = await statement(Sql(q =>
|
|
370
|
+
result = await statement(Sql(q => handler(q, Lease.Settling)))
|
|
361
371
|
} catch (e) {
|
|
362
372
|
settle(l, Lease.Discarded)
|
|
363
373
|
throw e
|
|
@@ -367,7 +377,7 @@ function Postgres(a, b) {
|
|
|
367
377
|
}
|
|
368
378
|
|
|
369
379
|
async function scope(fn, name) {
|
|
370
|
-
const sql = Sql(
|
|
380
|
+
const sql = Sql(track)
|
|
371
381
|
sql.savepoint = savepoint
|
|
372
382
|
sql.prepare = x => {
|
|
373
383
|
if (typeof x !== 'string' || !x || x.includes('\0'))
|
|
@@ -421,11 +431,16 @@ function Postgres(a, b) {
|
|
|
421
431
|
return scope(fn, 's' + savepoints++ + (name ? '_' + name : ''))
|
|
422
432
|
}
|
|
423
433
|
|
|
424
|
-
function
|
|
434
|
+
function track(q) {
|
|
425
435
|
q.catch(e => uncaughtError || (uncaughtError = e))
|
|
426
|
-
|
|
436
|
+
handler(q)
|
|
427
437
|
}
|
|
428
438
|
}
|
|
439
|
+
|
|
440
|
+
// dd-trace starts query spans only inside function declarations named handler
|
|
441
|
+
function handler(q, expected) {
|
|
442
|
+
send(l, q, expected)
|
|
443
|
+
}
|
|
429
444
|
}
|
|
430
445
|
|
|
431
446
|
function move(c, queue) {
|
|
@@ -463,7 +478,17 @@ function Postgres(a, b) {
|
|
|
463
478
|
const c = takeBusy()
|
|
464
479
|
c
|
|
465
480
|
? go(c, query)
|
|
466
|
-
:
|
|
481
|
+
: allHeldByCopy()
|
|
482
|
+
? query.reject(copyInProgress())
|
|
483
|
+
: queries.push(query)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function allHeldByCopy() {
|
|
487
|
+
return connections.every(c => c.copyHeld)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function copyInProgress() {
|
|
491
|
+
return Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy')
|
|
467
492
|
}
|
|
468
493
|
|
|
469
494
|
function takeBusy() {
|
|
@@ -663,6 +688,7 @@ function parseOptions(a, b) {
|
|
|
663
688
|
prepare : true,
|
|
664
689
|
debug : false,
|
|
665
690
|
fetch_types : true,
|
|
691
|
+
parameter_types : 'server',
|
|
666
692
|
publications : 'alltables',
|
|
667
693
|
target_session_attrs: null
|
|
668
694
|
}
|
|
@@ -693,11 +719,13 @@ function parseOptions(a, b) {
|
|
|
693
719
|
},
|
|
694
720
|
types : o.types || {},
|
|
695
721
|
target_session_attrs: tsa(o, url, env),
|
|
722
|
+
parameter_types : parameterTypes(o, url, env),
|
|
696
723
|
onnotice : o.onnotice,
|
|
697
724
|
onnotify : o.onnotify,
|
|
698
725
|
onclose : o.onclose,
|
|
699
726
|
onidle : o.onidle,
|
|
700
727
|
onparameter : o.onparameter,
|
|
728
|
+
ontypemismatch : o.ontypemismatch,
|
|
701
729
|
socket : o.socket,
|
|
702
730
|
transform : parseTransform(o.transform || { undefined: undefined }),
|
|
703
731
|
parameters : {},
|
|
@@ -717,6 +745,14 @@ function tsa(o, url, env) {
|
|
|
717
745
|
throw new Error('target_session_attrs ' + x + ' is not supported')
|
|
718
746
|
}
|
|
719
747
|
|
|
748
|
+
function parameterTypes(o, url, env) {
|
|
749
|
+
const x = o.parameter_types || url.searchParams.get('parameter_types') || env.PGPARAMETER_TYPES || 'server'
|
|
750
|
+
if (['server', 'client', 'warn'].includes(x))
|
|
751
|
+
return x
|
|
752
|
+
|
|
753
|
+
throw new Error('parameter_types ' + x + ' is not supported')
|
|
754
|
+
}
|
|
755
|
+
|
|
720
756
|
function max_lifetime() {
|
|
721
757
|
return 60 * (30 + Math.random() * 30)
|
|
722
758
|
}
|
package/cf/src/query.js
CHANGED
|
@@ -23,16 +23,9 @@ export class Query extends Promise {
|
|
|
23
23
|
this.state = null
|
|
24
24
|
this.statement = null
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
resolve(x)
|
|
30
|
-
}
|
|
31
|
-
this.reject = x => {
|
|
32
|
-
this.active = false
|
|
33
|
-
this.settled = true
|
|
34
|
-
reject(x)
|
|
35
|
-
}
|
|
26
|
+
// dd-trace's load-time rewrite only accepts single-expression arrows here
|
|
27
|
+
this.resolve = x => (this.active = false, this.settled = true, resolve(x))
|
|
28
|
+
this.reject = x => (this.active = false, this.settled = true, reject(x))
|
|
36
29
|
|
|
37
30
|
this.active = false
|
|
38
31
|
this.settled = false
|