@coffre/db 0.14.5 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/change-records-_fKvahvs.js +55 -0
- package/dist/connect.d.ts +22 -10
- package/dist/connect.js +74 -12
- package/dist/database-CVteLhp1.d.ts +410 -0
- package/dist/{database-BadNeeN-.js → database-D55ENPX-.js} +46 -25
- package/dist/dialect.d.ts +4 -3
- package/dist/dialect.js +14 -5
- package/dist/grants.d.ts +4 -4
- package/dist/grants.js +4 -4
- package/dist/hyperdrive.d.ts +48 -23
- package/dist/hyperdrive.js +168 -91
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/log-DaD7fyUq.d.ts +722 -0
- package/dist/log.d.ts +2 -622
- package/dist/log.js +178 -13
- package/dist/migrate.d.ts +43 -3
- package/dist/migrate.js +149 -29
- package/dist/migrations/postgres/0022_log_015.sql +672 -0
- package/dist/migrations/postgres/0023_change_path.sql +153 -0
- package/dist/migrations/postgres/0024_use_015.sql +556 -0
- package/dist/migrations/postgres/0025_app_change.sql +221 -0
- package/dist/migrations/postgres/0026_issue_lock.sql +106 -0
- package/dist/migrations/postgres/0027_retention.sql +118 -0
- package/dist/migrations/postgres/0028_use_stalls.sql +594 -0
- package/dist/migrations/postgres/0029_use_objects.sql +887 -0
- package/dist/migrations/postgres/0030_unarchive_restores.sql +259 -0
- package/dist/migrations/postgres/0031_app_read.sql +65 -0
- package/dist/migrations/postgres/0032_secrets_live_index.sql +4 -0
- package/dist/migrations/postgres/0033_use_generations.sql +582 -0
- package/dist/migrations/postgres/0034_vault_credentials.sql +153 -0
- package/dist/migrations/postgres/0035_first_days.sql +3 -0
- package/dist/migrations/postgres/0036_vault_issues.sql +91 -0
- package/dist/migrations/postgres/0037_vault_bindings.sql +271 -0
- package/dist/migrations/postgres/0040_binding_origin.sql +159 -0
- package/dist/migrations/postgres/0041_cutover.sql +132 -0
- package/dist/migrations/postgres/0042_sign_in_index.sql +7 -0
- package/dist/migrations/postgres/0043_issue_revoke_index.sql +4 -0
- package/dist/migrations/postgres/0044_use_by_name.sql +705 -0
- package/dist/migrations/postgres/0045_decide_first.sql +878 -0
- package/dist/migrations/postgres/0046_way_in_uses.sql +1149 -0
- package/dist/migrations/postgres/0047_cutover_finish.sql +268 -0
- package/dist/migrations/postgres/0048_use_batch.sql +59 -0
- package/dist/migrations/postgres/0049_reads.sql +171 -0
- package/dist/migrations/postgres/meta/0022_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0023_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0024_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0025_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0026_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0027_snapshot.json +3853 -0
- package/dist/migrations/postgres/meta/0028_snapshot.json +3853 -0
- package/dist/migrations/postgres/meta/0029_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0030_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0031_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0032_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0033_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0034_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0035_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0036_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0037_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0038_snapshot.json +3881 -0
- package/dist/migrations/postgres/meta/0039_snapshot.json +3881 -0
- package/dist/migrations/postgres/meta/0040_snapshot.json +3931 -0
- package/dist/migrations/postgres/meta/0041_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0042_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0043_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0044_snapshot.json +3991 -0
- package/dist/migrations/postgres/meta/0045_snapshot.json +3997 -0
- package/dist/migrations/postgres/meta/0046_snapshot.json +3997 -0
- package/dist/migrations/postgres/meta/0047_snapshot.json +4234 -0
- package/dist/migrations/postgres/meta/_journal.json +182 -0
- package/dist/migrations/sqlite/0018_log_015.sql +84 -0
- package/dist/migrations/sqlite/0019_retention.sql +2 -0
- package/dist/migrations/sqlite/0020_ended_references.sql +1 -0
- package/dist/migrations/sqlite/0021_secrets_live_index.sql +1 -0
- package/dist/migrations/sqlite/0022_binding_origin.sql +1 -0
- package/dist/migrations/sqlite/0023_sign_in_index.sql +2 -0
- package/dist/migrations/sqlite/0024_issue_revoke_index.sql +2 -0
- package/dist/migrations/sqlite/0025_way_in_uses.sql +42 -0
- package/dist/migrations/sqlite/0026_cutover_finish.sql +11 -0
- package/dist/migrations/sqlite/0027_reads.sql +38 -0
- package/dist/migrations/sqlite/meta/0018_snapshot.json +3529 -0
- package/dist/migrations/sqlite/meta/0019_snapshot.json +3538 -0
- package/dist/migrations/sqlite/meta/0020_snapshot.json +3545 -0
- package/dist/migrations/sqlite/meta/0021_snapshot.json +3553 -0
- package/dist/migrations/sqlite/meta/0022_snapshot.json +3560 -0
- package/dist/migrations/sqlite/meta/0023_snapshot.json +3579 -0
- package/dist/migrations/sqlite/meta/0024_snapshot.json +3598 -0
- package/dist/migrations/sqlite/meta/0025_snapshot.json +3609 -0
- package/dist/migrations/sqlite/meta/0026_snapshot.json +3616 -0
- package/dist/migrations/sqlite/meta/0027_snapshot.json +3834 -0
- package/dist/migrations/sqlite/meta/_journal.json +70 -0
- package/dist/platform-Y3gICK6n.d.ts +58 -0
- package/dist/portable.d.ts +2 -2
- package/dist/postgres-BI3xTnyw.js +1718 -0
- package/dist/postgres-platform-node.d.ts +14 -0
- package/dist/postgres-platform-node.js +21 -0
- package/dist/postgres-platform-workerd.d.ts +61 -0
- package/dist/postgres-platform-workerd.js +163 -0
- package/dist/{schema-BfQEBqFv.d.ts → schema-DgoPqqsI.d.ts} +1326 -7
- package/dist/schema-sqlite.d.ts +1306 -6
- package/dist/schema-sqlite.js +154 -18
- package/dist/schema-version.d.ts +1 -1
- package/dist/schema-version.js +254 -2
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +226 -20
- package/package.json +13 -5
- package/src/active-subject.ts +9 -0
- package/src/change-records.ts +38 -0
- package/src/connect.ts +67 -13
- package/src/database.ts +67 -51
- package/src/dialect.ts +17 -8
- package/src/grants.ts +3 -3
- package/src/hyperdrive.ts +181 -104
- package/src/log.ts +196 -15
- package/src/migrate.ts +157 -30
- package/src/migrations/postgres/0022_log_015.sql +672 -0
- package/src/migrations/postgres/0023_change_path.sql +153 -0
- package/src/migrations/postgres/0024_use_015.sql +556 -0
- package/src/migrations/postgres/0025_app_change.sql +221 -0
- package/src/migrations/postgres/0026_issue_lock.sql +106 -0
- package/src/migrations/postgres/0027_retention.sql +118 -0
- package/src/migrations/postgres/0028_use_stalls.sql +594 -0
- package/src/migrations/postgres/0029_use_objects.sql +887 -0
- package/src/migrations/postgres/0030_unarchive_restores.sql +259 -0
- package/src/migrations/postgres/0031_app_read.sql +65 -0
- package/src/migrations/postgres/0032_secrets_live_index.sql +4 -0
- package/src/migrations/postgres/0033_use_generations.sql +582 -0
- package/src/migrations/postgres/0034_vault_credentials.sql +153 -0
- package/src/migrations/postgres/0035_first_days.sql +3 -0
- package/src/migrations/postgres/0036_vault_issues.sql +91 -0
- package/src/migrations/postgres/0037_vault_bindings.sql +271 -0
- package/src/migrations/postgres/0040_binding_origin.sql +159 -0
- package/src/migrations/postgres/0041_cutover.sql +132 -0
- package/src/migrations/postgres/0042_sign_in_index.sql +7 -0
- package/src/migrations/postgres/0043_issue_revoke_index.sql +4 -0
- package/src/migrations/postgres/0044_use_by_name.sql +705 -0
- package/src/migrations/postgres/0045_decide_first.sql +878 -0
- package/src/migrations/postgres/0046_way_in_uses.sql +1149 -0
- package/src/migrations/postgres/0047_cutover_finish.sql +268 -0
- package/src/migrations/postgres/0048_use_batch.sql +59 -0
- package/src/migrations/postgres/0049_reads.sql +171 -0
- package/src/migrations/postgres/meta/0022_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0023_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0024_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0025_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0026_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0027_snapshot.json +3853 -0
- package/src/migrations/postgres/meta/0028_snapshot.json +3853 -0
- package/src/migrations/postgres/meta/0029_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0030_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0031_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0032_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0033_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0034_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0035_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0036_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0037_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0038_snapshot.json +3881 -0
- package/src/migrations/postgres/meta/0039_snapshot.json +3881 -0
- package/src/migrations/postgres/meta/0040_snapshot.json +3931 -0
- package/src/migrations/postgres/meta/0041_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0042_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0043_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0044_snapshot.json +3991 -0
- package/src/migrations/postgres/meta/0045_snapshot.json +3997 -0
- package/src/migrations/postgres/meta/0046_snapshot.json +3997 -0
- package/src/migrations/postgres/meta/0047_snapshot.json +4234 -0
- package/src/migrations/postgres/meta/_journal.json +182 -0
- package/src/migrations/sqlite/0018_log_015.sql +84 -0
- package/src/migrations/sqlite/0019_retention.sql +2 -0
- package/src/migrations/sqlite/0020_ended_references.sql +1 -0
- package/src/migrations/sqlite/0021_secrets_live_index.sql +1 -0
- package/src/migrations/sqlite/0022_binding_origin.sql +1 -0
- package/src/migrations/sqlite/0023_sign_in_index.sql +2 -0
- package/src/migrations/sqlite/0024_issue_revoke_index.sql +2 -0
- package/src/migrations/sqlite/0025_way_in_uses.sql +42 -0
- package/src/migrations/sqlite/0026_cutover_finish.sql +11 -0
- package/src/migrations/sqlite/0027_reads.sql +38 -0
- package/src/migrations/sqlite/meta/0018_snapshot.json +3529 -0
- package/src/migrations/sqlite/meta/0019_snapshot.json +3538 -0
- package/src/migrations/sqlite/meta/0020_snapshot.json +3545 -0
- package/src/migrations/sqlite/meta/0021_snapshot.json +3553 -0
- package/src/migrations/sqlite/meta/0022_snapshot.json +3560 -0
- package/src/migrations/sqlite/meta/0023_snapshot.json +3579 -0
- package/src/migrations/sqlite/meta/0024_snapshot.json +3598 -0
- package/src/migrations/sqlite/meta/0025_snapshot.json +3609 -0
- package/src/migrations/sqlite/meta/0026_snapshot.json +3616 -0
- package/src/migrations/sqlite/meta/0027_snapshot.json +3834 -0
- package/src/migrations/sqlite/meta/_journal.json +70 -0
- package/src/postgres-js/README.md +183 -0
- package/src/postgres-js/UNLICENSE +24 -0
- package/src/postgres-js/bytes.ts +89 -0
- package/src/postgres-js/connection.ts +882 -0
- package/src/postgres-js/errors.ts +84 -0
- package/src/postgres-js/index.ts +516 -0
- package/src/postgres-js/platform.node.ts +29 -0
- package/src/postgres-js/platform.ts +60 -0
- package/src/postgres-js/platform.workerd.ts +225 -0
- package/src/postgres-js/query.ts +123 -0
- package/src/postgres-js/queue.ts +42 -0
- package/src/postgres-js/result.ts +46 -0
- package/src/postgres-js/types.ts +52 -0
- package/src/postgres.ts +375 -26
- package/src/schema-version.ts +2 -2
- package/src/schema.sqlite.ts +198 -7
- package/src/schema.ts +291 -6
- package/src/verify-runtime.ts +18 -29
- package/LICENSE +0 -21
- package/dist/database-wq1AVRAP.d.ts +0 -73
- package/dist/postgres-Bmar8y0q.js +0 -29
- package/dist/relations-BfR3JjT1.js +0 -38
- package/src/relations.ts +0 -40
|
@@ -127,6 +127,76 @@
|
|
|
127
127
|
"when": 1793017781451,
|
|
128
128
|
"tag": "0017_vault_releases",
|
|
129
129
|
"breakpoints": true
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"idx": 18,
|
|
133
|
+
"version": "6",
|
|
134
|
+
"when": 1793068925491,
|
|
135
|
+
"tag": "0018_log_015",
|
|
136
|
+
"breakpoints": true
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"idx": 19,
|
|
140
|
+
"version": "6",
|
|
141
|
+
"when": 1793120000000,
|
|
142
|
+
"tag": "0019_retention",
|
|
143
|
+
"breakpoints": true
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"idx": 20,
|
|
147
|
+
"version": "6",
|
|
148
|
+
"when": 1793130000000,
|
|
149
|
+
"tag": "0020_ended_references",
|
|
150
|
+
"breakpoints": true
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"idx": 21,
|
|
154
|
+
"version": "6",
|
|
155
|
+
"when": 1793180484098,
|
|
156
|
+
"tag": "0021_secrets_live_index",
|
|
157
|
+
"breakpoints": true
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"idx": 22,
|
|
161
|
+
"version": "6",
|
|
162
|
+
"when": 1793247590011,
|
|
163
|
+
"tag": "0022_binding_origin",
|
|
164
|
+
"breakpoints": true
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"idx": 23,
|
|
168
|
+
"version": "6",
|
|
169
|
+
"when": 1793268597317,
|
|
170
|
+
"tag": "0023_sign_in_index",
|
|
171
|
+
"breakpoints": true
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"idx": 24,
|
|
175
|
+
"version": "6",
|
|
176
|
+
"when": 1793306933393,
|
|
177
|
+
"tag": "0024_issue_revoke_index",
|
|
178
|
+
"breakpoints": true
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"idx": 25,
|
|
182
|
+
"version": "6",
|
|
183
|
+
"when": 1793352372923,
|
|
184
|
+
"tag": "0025_way_in_uses",
|
|
185
|
+
"breakpoints": true
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"idx": 26,
|
|
189
|
+
"version": "6",
|
|
190
|
+
"when": 1793372630506,
|
|
191
|
+
"tag": "0026_cutover_finish",
|
|
192
|
+
"breakpoints": true
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"idx": 27,
|
|
196
|
+
"version": "6",
|
|
197
|
+
"when": 1793445615378,
|
|
198
|
+
"tag": "0027_reads",
|
|
199
|
+
"breakpoints": true
|
|
130
200
|
}
|
|
131
201
|
]
|
|
132
202
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# postgres.js, Coffre's copy
|
|
2
|
+
|
|
3
|
+
[postgres.js](https://github.com/porsager/postgres) 3.4.9 (npm tarball
|
|
4
|
+
`sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==`,
|
|
5
|
+
commit `e7dfa14519f363229ccc3ead7b1b2f2051937efb`), released under the
|
|
6
|
+
Unlicense (`UNLICENSE`), in TypeScript. Coffre owns this copy, and keeps of
|
|
7
|
+
it what coffre uses. One tree runs on Node and on workerd: what differs, the
|
|
8
|
+
socket, TLS, hashes and a timer, is behind `#postgres/platform` (`@coffre/db`'s
|
|
9
|
+
`package.json`), `platform.node.ts` or `platform.workerd.ts` (upstream's
|
|
10
|
+
`cf/polyfills.js`), which both satisfy `platform.ts`.
|
|
11
|
+
|
|
12
|
+
| File | What |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `index.ts` | The pool: `unsafe`, `begin` and its savepoints, `reserve`, `end`; its options |
|
|
15
|
+
| `connection.ts` | One connection: TLS, startup, authentication (SCRAM-SHA-256, MD5, cleartext), the extended and simple protocols |
|
|
16
|
+
| `query.ts` | A statement: a promise, sent once awaited or `execute`d; `.values()` for its rows as arrays |
|
|
17
|
+
| `result.ts` | Its rows, with `count`, `command` and `state` (the backend's pid and secret) |
|
|
18
|
+
| `types.ts` | The built-in parsers and serializers (text, numbers, JSON, booleans, dates, bytea) |
|
|
19
|
+
| `errors.ts`, `bytes.ts`, `queue.ts` | `PostgresError` and the driver's own errors, the message writer, the pool's queues |
|
|
20
|
+
|
|
21
|
+
## What is left out
|
|
22
|
+
|
|
23
|
+
What nothing in coffre reached: logical replication (`subscribe`), large
|
|
24
|
+
objects, COPY streams (a COPY a statement asks for fails, with an error that
|
|
25
|
+
says so), LISTEN/NOTIFY (a notification is dropped), cursors and `forEach`,
|
|
26
|
+
`fetch_types` and array types read from `pg_type` (`postgresClient` takes
|
|
27
|
+
Postgres's own as known), named prepared statements and their cache (every
|
|
28
|
+
statement is unnamed, as coffre sent them), `transform`, the tagged template
|
|
29
|
+
and its helpers (`sql\`…\``, `sql(rows)`, `sql.json`, `sql.typed`,
|
|
30
|
+
`sql.file`), two-phase commit (`prepare`), several hosts and
|
|
31
|
+
`target_session_attrs`, a statement's `cancel()`, and reading a URL and the
|
|
32
|
+
`PG*` environment: the driver takes an options object, every option given
|
|
33
|
+
or its default, and reads nothing from the environment (`postgresOptions`, in
|
|
34
|
+
`../postgres.ts`, reads the URL). A transaction's own statements, `begin`,
|
|
35
|
+
`savepoint`, `rollback` and `commit`, go as simple statements, where upstream
|
|
36
|
+
wrote them with its tagged template.
|
|
37
|
+
|
|
38
|
+
## Changes from upstream
|
|
39
|
+
|
|
40
|
+
Ten, each marked `Coffre:` in the code:
|
|
41
|
+
|
|
42
|
+
- **One round trip per statement** (`typedByServer`, in `connection.ts`).
|
|
43
|
+
Upstream asks the server for an unnamed statement's parameter types (Parse,
|
|
44
|
+
Describe, Flush) and waits for them before it sends the values (Bind,
|
|
45
|
+
Execute, Sync), which doubles the round trips of every query with parameters
|
|
46
|
+
that Drizzle sends. Here the values go with the statement, as text the server
|
|
47
|
+
types, as node-postgres sends them, unless one is an object postgres.js has
|
|
48
|
+
no type for (an array is typed by its first element, so `[true]` is sent
|
|
49
|
+
as a boolean: `postgresClient` sends arrays as their text before the
|
|
50
|
+
driver sees them) (porsager/postgres#1225). `postgresClient`
|
|
51
|
+
(`packages/db/src/postgres.ts`) sends arrays, dates and objects as text, so none is.
|
|
52
|
+
- **A transaction's BEGIN goes with its first statement** (`begin`, in
|
|
53
|
+
`index.ts`; `toBuffer` and `CommandComplete`, in `connection.ts`), a round
|
|
54
|
+
trip less for every transaction. Upstream sent BEGIN alone and waited for
|
|
55
|
+
it. The two go under one Sync: as the extended protocol's Parse, Bind and
|
|
56
|
+
Execute ahead of the statement's, or, for a statement without parameters,
|
|
57
|
+
in one Query message (`begin;\n<statement>`). Either way, a BEGIN that
|
|
58
|
+
fails has Postgres skip the statement, which so never runs outside its
|
|
59
|
+
transaction; what waited behind it is not sent, nor anything the
|
|
60
|
+
transaction asks after, which fails `TRANSACTION_NOT_BEGUN`, and nothing
|
|
61
|
+
is rolled back. Nothing waits behind the BEGIN's statement for less: one
|
|
62
|
+
the client failed to write (a value it cannot send) takes its BEGIN with
|
|
63
|
+
it. A transaction that asks nothing sends nothing. It holds its
|
|
64
|
+
connection from the start, as `reserve()` does, and gives it back once
|
|
65
|
+
it has ended: upstream gave it back once Postgres said the connection
|
|
66
|
+
was idle, which it also says after a BEGIN that failed. Its end is
|
|
67
|
+
decided once its work has settled (`settle`): what the work left running
|
|
68
|
+
asks from then on is refused, unsent (`TRANSACTION_ENDED`), as is what it
|
|
69
|
+
asks once its connection closed (the close's error); the transaction
|
|
70
|
+
then waits for every statement it asked to answer, and ends with a
|
|
71
|
+
ROLLBACK if any failed, waited for or not, or if its work asked anything
|
|
72
|
+
once settled (`TRANSACTION_UNFINISHED`: it would commit only part of
|
|
73
|
+
it), else a COMMIT; and a COMMIT that Postgres answers with ROLLBACK (a
|
|
74
|
+
savepoint's statement that failed once its savepoint's work had
|
|
75
|
+
returned) fails it. A refused BEGIN's error names the BEGIN; a first
|
|
76
|
+
statement's error its position in the statement. Upstream sent
|
|
77
|
+
the COMMIT or ROLLBACK at once: a statement asked during it ran after it,
|
|
78
|
+
outside the transaction, in autocommit, kept even when the transaction
|
|
79
|
+
rolled back; and a statement that failed unwaited left a COMMIT, which
|
|
80
|
+
Postgres answers with ROLLBACK, not an error, and upstream took for
|
|
81
|
+
success. No ROLLBACK goes to a connection gone. A held
|
|
82
|
+
connection asked to end meanwhile, its `max_lifetime` say, closes once
|
|
83
|
+
given back (`release`, in `connection.ts`), and its socket draining
|
|
84
|
+
never gives it to the pool (`drain`), as upstream's could, mid-transaction. Through Hyperdrive, the
|
|
85
|
+
first statement reaches the pooler with its BEGIN, in one batch: the
|
|
86
|
+
pooler keeps the connection for the transaction from the ReadyForQuery
|
|
87
|
+
after it, which says so (`T`), and coffre's Hyperdrive configurations
|
|
88
|
+
cache nothing (`docs/deploy.md`), so that statement is never answered
|
|
89
|
+
from a cache. Both shapes reach the pooler: Parse, Bind and Execute under
|
|
90
|
+
one Sync, and one Query message of two statements. Both were checked on
|
|
91
|
+
a real Hyperdrive for 0.15: `select pg_backend_pid()` as a transaction's
|
|
92
|
+
first statement (with a parameter, and without, and after the vault's
|
|
93
|
+
`SET LOCAL`) and again later in it, the same backend and transaction
|
|
94
|
+
each time, over 11,000 transactions, 40 to 50 at once over 10 backends;
|
|
95
|
+
what rolled back or failed kept nothing. A change to how they go is
|
|
96
|
+
checked there again. Hyperdrive sends a client's Syncs on one at a
|
|
97
|
+
time, each once the one before has answered: what costs a round trip
|
|
98
|
+
there is a Sync, not a write, so `test/hyperdrive.test.ts` counts
|
|
99
|
+
both. A first statement described first (Parse, Describe, Flush,
|
|
100
|
+
then a wait) would reach Hyperdrive before any ReadyForQuery had pinned
|
|
101
|
+
the connection, a flow Cloudflare says hangs under its pooling; coffre
|
|
102
|
+
sends none (`postgresClient`), and none is supported there. This
|
|
103
|
+
replaces PR A's fix for a BEGIN queued behind `max_pipeline` statements,
|
|
104
|
+
which left `begin()` without its connection: a transaction now waits for
|
|
105
|
+
a connection of its own instead.
|
|
106
|
+
- **Statements in a group go under one Sync** (`group`, in `index.ts`;
|
|
107
|
+
`toBuffer`, `build` and `CommandComplete`, in `connection.ts`), a round
|
|
108
|
+
trip for them all through a pooler that sends a client's Syncs on one at
|
|
109
|
+
a time, as Hyperdrive does (`HyperdrivePool`'s `groupReads`, in
|
|
110
|
+
`../hyperdrive.ts`). Each is its own Parse, Describe, Bind and Execute,
|
|
111
|
+
its values sent with it; one Sync follows the last. The group is one
|
|
112
|
+
query to the connection: each statement's rows are its own result,
|
|
113
|
+
split at its CommandComplete, and the group answers once Postgres is
|
|
114
|
+
ready again, every result at once. Postgres runs them as one implicit
|
|
115
|
+
transaction, so if one fails it skips the rest, and the group fails with
|
|
116
|
+
that error, naming the statement. A group holds plain reads alone
|
|
117
|
+
(`select …`, `GROUP_NOT_READ` otherwise: a write grouped with a statement
|
|
118
|
+
that fails would be rolled back by an error not its own; a `select` that
|
|
119
|
+
writes through a function is not seen, and says so ahead of itself, as
|
|
120
|
+
`/* writes */ select …`, which no group takes), and is the
|
|
121
|
+
pool's alone: a transaction's statements each go under its own Sync, so
|
|
122
|
+
BEGIN and COMMIT mean what they did. Upstream has no such thing; its
|
|
123
|
+
pipelined statements each carry a Sync.
|
|
124
|
+
- **A connection closed while it opens fails its query** (`closed`, in
|
|
125
|
+
`connection.ts`), as node-postgres's "Connection terminated
|
|
126
|
+
unexpectedly" did. Upstream dialled again, at once and for ever (thousands
|
|
127
|
+
of dials a second), the query never answering; its retry was for the next
|
|
128
|
+
of several hosts, which coffre never names (`postgresOptions` refuses
|
|
129
|
+
them).
|
|
130
|
+
- **A query cut short by its connection's close leaves it** (`closed`, in
|
|
131
|
+
`connection.ts`). Upstream rejected the query but kept it as the
|
|
132
|
+
connection's, so `end()` waited for it to answer, for ever, unless another
|
|
133
|
+
query opened the connection again: a Worker call whose connection was cut
|
|
134
|
+
under a query, then closed (`HyperdrivePool.end`), hung until Cloudflare
|
|
135
|
+
cancelled it. Likewise an `end()` asked while the connection opened or
|
|
136
|
+
answered: the connection's close ends it. And what the cut query's answer
|
|
137
|
+
had left half read goes with it, its error, its row count, its results:
|
|
138
|
+
upstream kept them, so the next query on the connection, opened again,
|
|
139
|
+
failed with the dead one's error, and the one after answered its rows
|
|
140
|
+
after the dead one's count, holes first.
|
|
141
|
+
- **An ending client opens no connection again** (`onclose`, in
|
|
142
|
+
`index.ts`). Upstream opened a closed connection again for a query still
|
|
143
|
+
waiting for one, even once `end()` had been asked, and answered: that
|
|
144
|
+
connection outlived the client's end, which no other `end()` could close.
|
|
145
|
+
Those queries fail, `CONNECTION_ENDED`, as any asked after `end()` does,
|
|
146
|
+
and so does a `reserve()` (in `index.ts`); a connection reserved before
|
|
147
|
+
`end()` and released after it closes, which `end()` waits for, where
|
|
148
|
+
upstream gave it back to the pool.
|
|
149
|
+
- **A `reserve()` that opens its connection gets it** (`ReadyForQuery`, in
|
|
150
|
+
`connection.ts`; `hold`, in `index.ts`). Upstream handed it over after
|
|
151
|
+
`fetch_types`'s own query, so without `fetch_types`, as coffre opened
|
|
152
|
+
every client, a `reserve()` on a pool with no connection open waited for
|
|
153
|
+
ever. And upstream's reservation both waited in the pool's queue and
|
|
154
|
+
opened a connection, which gave itself to the queue's first: one taken
|
|
155
|
+
from the queue to open a connection again, after one closed, was no
|
|
156
|
+
longer there, and waited for ever, as a transaction waiting for the
|
|
157
|
+
connection its predecessor lost would have. A reservation now either
|
|
158
|
+
waits or opens a connection, which is given to it.
|
|
159
|
+
- **The pool takes the connection idle the shortest** (`Queue.pop`, in
|
|
160
|
+
`queue.ts` and `index.ts`), as pg-pool does. Upstream takes the one idle the
|
|
161
|
+
longest, so a steady load cycles every connection of the pool and each
|
|
162
|
+
backend's caches stay cold: Postgres spent ~8% more CPU per use at a pool of
|
|
163
|
+
16 (`packages/db/test/pool.test.ts`).
|
|
164
|
+
- **An opening the client cannot finish fails its connection**
|
|
165
|
+
(`openingFailed`, `secure`, `connected`, `createSocket` and
|
|
166
|
+
`authenticate`, in `connection.ts`): its TLS (a key or certificate it
|
|
167
|
+
cannot read), its startup, its sign-in (a hash that throws, a server's SCRAM
|
|
168
|
+
message it cannot read, a method it has not: GSSAPI, SSPI, Kerberos), or a
|
|
169
|
+
`socket` factory that throws (which backs off as a refused connection does)
|
|
170
|
+
fails what waits for the connection and closes it, so that it opens again
|
|
171
|
+
for the next query. Upstream left the error unhandled, which ends a Node
|
|
172
|
+
process, or the connection opening until its `connect_timeout`, if any,
|
|
173
|
+
every query after it waiting. A step of the opening that finishes once its
|
|
174
|
+
socket has closed (on workerd, the server's answer to the SSLRequest and
|
|
175
|
+
the hashes are promises, which can settle after the close) writes nothing
|
|
176
|
+
and fails nothing: upstream went on with the connection opened again, or
|
|
177
|
+
failed on its null socket.
|
|
178
|
+
- **A statement the client cannot write fails alone** (`execute`, in
|
|
179
|
+
`connection.ts`): too many parameters, a value it cannot serialize.
|
|
180
|
+
Nothing of it is sent; on an idle connection, a lone Sync answers it, so
|
|
181
|
+
the connection goes on as after any answer. Upstream failed the statement
|
|
182
|
+
already answering instead, and left this one waiting, its transaction and
|
|
183
|
+
connection with it, for ever.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
|
|
3
|
+
/** The NUL that ends a string in the protocol. */
|
|
4
|
+
export const N = '\0';
|
|
5
|
+
|
|
6
|
+
const SIZE = 256;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The message being written: started with its type (`b.typed('Q')`), or
|
|
10
|
+
* none (`b.untyped()`, for the startup's and the SSLRequest's), then its
|
|
11
|
+
* fields, then `end()`, which writes its length and hands it over. One at a
|
|
12
|
+
* time, each written whole before the next starts.
|
|
13
|
+
*/
|
|
14
|
+
class Writer {
|
|
15
|
+
#buffer = Buffer.allocUnsafe(SIZE);
|
|
16
|
+
/** Where the next field goes. */
|
|
17
|
+
i = 0;
|
|
18
|
+
|
|
19
|
+
/** A message of `type`, a letter: its length, written by `end()`, follows it. */
|
|
20
|
+
typed(type: string): this {
|
|
21
|
+
this.#buffer[0] = type.charCodeAt(0);
|
|
22
|
+
this.i = 5;
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
untyped(): this {
|
|
27
|
+
this.i = 0;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Skip `n` bytes, to be written later (`i32(x, at)`). */
|
|
32
|
+
inc(n: number): this {
|
|
33
|
+
this.i += n;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
str(x: string): this {
|
|
38
|
+
const length = Buffer.byteLength(x);
|
|
39
|
+
this.#fit(length);
|
|
40
|
+
this.i += this.#buffer.write(x, this.i, length, 'utf8');
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
i16(x: number): this {
|
|
45
|
+
this.#fit(2);
|
|
46
|
+
this.#buffer.writeUInt16BE(x, this.i);
|
|
47
|
+
this.i += 2;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** `x` next, or at `at`, a place skipped before. */
|
|
52
|
+
i32(x: number, at?: number): this {
|
|
53
|
+
if (at !== undefined) {
|
|
54
|
+
this.#buffer.writeUInt32BE(x, at);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
this.#fit(4);
|
|
58
|
+
this.#buffer.writeUInt32BE(x, this.i);
|
|
59
|
+
this.i += 4;
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** `n` zero bytes. */
|
|
64
|
+
z(n: number): this {
|
|
65
|
+
this.#fit(n);
|
|
66
|
+
this.#buffer.fill(0, this.i, this.i + n);
|
|
67
|
+
this.i += n;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The message, its length written at `at`: 1 after a type, 0 for an untyped one. */
|
|
72
|
+
end(at = 1): Buffer {
|
|
73
|
+
this.#buffer.writeUInt32BE(this.i - at, at);
|
|
74
|
+
const out = this.#buffer.subarray(0, this.i);
|
|
75
|
+
this.i = 0;
|
|
76
|
+
this.#buffer = Buffer.allocUnsafe(SIZE);
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#fit(n: number): void {
|
|
81
|
+
if (this.#buffer.length - this.i < n) {
|
|
82
|
+
const prev = this.#buffer;
|
|
83
|
+
this.#buffer = Buffer.allocUnsafe(prev.length + (prev.length >> 1) + n);
|
|
84
|
+
prev.copy(this.#buffer);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const b = new Writer();
|