@vibeorm/adapter-pg 5.0.1 → 5.1.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 +10 -0
- package/dist/batch.d.ts +56 -2
- package/dist/index.js +218 -51
- package/dist/index.js.map +6 -6
- package/dist/statement-cache.d.ts +25 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -98,6 +98,16 @@ Behind any other transaction-mode pooler (PgBouncer before 1.21 or with `max_pre
|
|
|
98
98
|
|
|
99
99
|
One case stays silent with every naming scheme: a statement prepared before DDL **shadows** a table. If a migration creates a table with the same name in a schema that comes earlier in the unchanged `search_path`, the prepared statement keeps reading the table it was parsed against until the connection is recycled. (A changed `search_path` itself is safe: PostgreSQL re-parses the statement.) Recycle connections after such a migration, as for the result-shape changes below.
|
|
100
100
|
|
|
101
|
+
### Named raw statements
|
|
102
|
+
|
|
103
|
+
The raw path takes a caller-chosen statement name: `executeUnsafe({ text, values, name })`, which the runtime exposes as `$queryRawUnsafe({ text, values, name })` / `$executeRawUnsafe(…)`, `commitWith: { query, name }` and `commitQuery({ query, name })`. The name is honoured whatever `preparedStatements` says; without one, nothing is named.
|
|
104
|
+
|
|
105
|
+
- **Parse once per connection.** A plain query is sent as `{ name, text, values }`, so node-postgres parses it the first time on each connection. In a one-Sync batch (pipelined statements, the opening, the statement that rides with `COMMIT`) the step writes `Parse` with the name only when this connection has not prepared it, then `Bind` / `Execute` against the name. Both paths keep ONE record: node-postgres' own `parsedStatements` / `submittedNamedStatements`. A name counts as prepared only after its `ParseComplete` arrived; a Parse the server refused or skipped is forgotten. A statement prepared by a protocol Parse stays prepared when its transaction rolls back, and the record keeps it.
|
|
106
|
+
- **One text per name.** The first text a name is used with is its text for the pool's lifetime; a different text is refused before it is sent (`VIBE_VALIDATION`, `meta.reason: "statement-name-reused"`). Names are a fixed set: each one is prepared on every connection that runs it.
|
|
107
|
+
- **Name rules.** Not empty, at most 63 UTF-8 bytes (PostgreSQL truncates longer names), no NUL character (`VIBE_VALIDATION`).
|
|
108
|
+
- **Poolers.** A named statement must reach the backend that prepared it. Behind a transaction-mode pooler without prepared-statement support the call fails with `26000` / `42P05`, with the pooler hint above. Send no name there.
|
|
109
|
+
- **Schema changes.** A DDL change of a named statement's result shape answers `0A000`, as described next; recycle the connections.
|
|
110
|
+
|
|
101
111
|
### Schema changes with prepared statements
|
|
102
112
|
|
|
103
113
|
**Recycle backend connections after a migration changes a prepared query's result shape.** PostgreSQL can reject the retained plan with SQLSTATE `0A000` (`cached plan must not change result type`), for example after changing the type of a selected column. `SELECT *` can also be affected by adding a column. `planCacheMode: "force_custom_plan"` does not prevent result-descriptor invalidation.
|
package/dist/batch.d.ts
CHANGED
|
@@ -20,11 +20,28 @@
|
|
|
20
20
|
* so parameters serialize exactly as `client.query` serializes them. Rows are
|
|
21
21
|
* decoded HERE, with the adapter's query-local type parsers — never through
|
|
22
22
|
* node-postgres' internal `lib/result.js`.
|
|
23
|
+
*
|
|
24
|
+
* Named steps (throughput request 1): a step with a `name` is parsed under
|
|
25
|
+
* that name only when this connection has not prepared it yet, and bound to
|
|
26
|
+
* the name either way. The record of prepared names is node-postgres' OWN
|
|
27
|
+
* (`connection.parsedStatements` / `submittedNamedStatements`, the objects its
|
|
28
|
+
* `Query` reads), kept exactly as `Query` keeps it: a name is "submitted" when
|
|
29
|
+
* its Parse is written, "parsed" only when its ParseComplete arrives (the
|
|
30
|
+
* connection's `parseComplete` event, counted per Parse this batch wrote), and
|
|
31
|
+
* forgotten when the server refused or skipped its Parse. So a name a plain
|
|
32
|
+
* query prepared is never parsed again here, and the other way round.
|
|
23
33
|
*/
|
|
24
34
|
/** One statement of a batch. Values are the adapter's already-serialized params. */
|
|
25
35
|
export type BatchStep = {
|
|
26
36
|
readonly text: string;
|
|
27
37
|
readonly values?: readonly unknown[];
|
|
38
|
+
/**
|
|
39
|
+
* A caller-chosen prepared-statement name (raw path, throughput request 1):
|
|
40
|
+
* parsed under it once per connection, bound to it every time. A text that
|
|
41
|
+
* differs from the one this connection already holds is refused before any
|
|
42
|
+
* step or bookkeeping change.
|
|
43
|
+
*/
|
|
44
|
+
readonly name?: string;
|
|
28
45
|
/**
|
|
29
46
|
* Nobody reads this step's rows (statement-count pass EPIC 5: the final
|
|
30
47
|
* statement that rides with COMMIT): they are not decoded, so no type parser
|
|
@@ -58,6 +75,16 @@ export type BatchOutcome = {
|
|
|
58
75
|
* server did with the batch is unknown; `steps` lists only what was seen.
|
|
59
76
|
*/
|
|
60
77
|
readonly transportError?: unknown;
|
|
78
|
+
/**
|
|
79
|
+
* With `transportError`: PROVABLY no byte of this batch reached the socket,
|
|
80
|
+
* so the server received none of its statements. Either node-postgres never
|
|
81
|
+
* submitted the batch (a client already not queryable or ending answers
|
|
82
|
+
* `query()` with `handleError` and never calls `submit`; a client that fails
|
|
83
|
+
* errors its queued queries the same way), or its stream was no longer
|
|
84
|
+
* writable when it was (node-postgres' `Connection._send` writes nothing to a
|
|
85
|
+
* stream that is not writable, and a socket never becomes writable again).
|
|
86
|
+
*/
|
|
87
|
+
readonly unsent?: true;
|
|
61
88
|
};
|
|
62
89
|
/** The query-local type parser surface (the adapter's `EXACT_TYPES`). */
|
|
63
90
|
export type BatchTypeParsers = {
|
|
@@ -93,6 +120,10 @@ export declare function prepareBatchValues(params: {
|
|
|
93
120
|
* The Submittable. Construct with steps whose values are already prepared
|
|
94
121
|
* ({@link prepareBatchValues}); pass to `client.query(batch)`; await
|
|
95
122
|
* {@link PgBatch.done}.
|
|
123
|
+
*
|
|
124
|
+
* It has NO `name` property on purpose: node-postgres' own ParseComplete and
|
|
125
|
+
* ErrorResponse handlers record `activeQuery.name`, and a batch's names are
|
|
126
|
+
* recorded per step here instead.
|
|
96
127
|
*/
|
|
97
128
|
export declare class PgBatch {
|
|
98
129
|
/** Settles once: at the batch's ReadyForQuery, at its first server error, or at a transport failure. */
|
|
@@ -113,12 +144,35 @@ export declare class PgBatch {
|
|
|
113
144
|
private rowError;
|
|
114
145
|
private settled;
|
|
115
146
|
private resolve;
|
|
147
|
+
/** The connection's statement record while a named Parse of this batch may still be answered. */
|
|
148
|
+
private book;
|
|
149
|
+
/** Every Parse this batch wrote, in order: the k-th ParseComplete answers the k-th entry. */
|
|
150
|
+
private parses;
|
|
151
|
+
private parsesAnswered;
|
|
152
|
+
private stopParseTracking;
|
|
153
|
+
/** Whether `submit` wrote this batch's messages to a writable stream ({@link BatchOutcome.unsent} otherwise). */
|
|
154
|
+
private written;
|
|
116
155
|
constructor(params: {
|
|
117
156
|
steps: readonly BatchStep[];
|
|
118
157
|
types: BatchTypeParsers;
|
|
119
158
|
});
|
|
120
|
-
/**
|
|
121
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Called by node-postgres when the batch reaches the head of the client's
|
|
161
|
+
* queue. Returns an error, with nothing written, when a statement name
|
|
162
|
+
* conflicts or the stream is no longer writable. node-postgres then calls
|
|
163
|
+
* `handleError`, as for its own `Query.submit` refusals; `null` once written.
|
|
164
|
+
*/
|
|
165
|
+
submit(connection: unknown): Error | null;
|
|
166
|
+
/** One ParseComplete: the oldest unanswered Parse of this batch succeeded — its name is prepared now. */
|
|
167
|
+
private answerParse;
|
|
168
|
+
/**
|
|
169
|
+
* No more ParseComplete can come for this batch (its ReadyForQuery, or a
|
|
170
|
+
* server error: the server skips every message up to the Sync). A Parse
|
|
171
|
+
* still unanswered was refused or skipped, so its name is NOT prepared:
|
|
172
|
+
* forget it, as node-postgres' `_handleErrorMessage` forgets a query's
|
|
173
|
+
* submitted name. Recording it would answer 26000 on its next use.
|
|
174
|
+
*/
|
|
175
|
+
private endParseTracking;
|
|
122
176
|
handleRowDescription(message: {
|
|
123
177
|
fields: readonly RowDescriptionField[];
|
|
124
178
|
}): void;
|