turbine-orm 0.49.0 → 0.50.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 +122 -39
- package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
- package/dist/cjs/adapters/index.d.ts +110 -0
- package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
- package/dist/cjs/cli/config.d.ts +181 -0
- package/dist/cjs/cli/config.js +32 -6
- package/dist/cjs/cli/destructive.d.ts +38 -0
- package/dist/cjs/cli/index.d.ts +359 -0
- package/dist/cjs/cli/index.js +228 -56
- package/dist/cjs/cli/loader.d.ts +61 -0
- package/dist/cjs/cli/mcp.d.ts +42 -0
- package/dist/cjs/cli/migrate.d.ts +356 -0
- package/dist/cjs/cli/migrate.js +131 -40
- package/dist/cjs/cli/observe-ui.d.ts +1 -0
- package/dist/cjs/cli/observe-ui.js +14 -5
- package/dist/cjs/cli/observe.d.ts +25 -0
- package/dist/cjs/cli/observe.js +49 -12
- package/dist/cjs/cli/pii-tags.d.ts +53 -0
- package/dist/cjs/cli/prisma-report.d.ts +33 -0
- package/dist/cjs/cli/prisma-report.js +73 -0
- package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
- package/dist/cjs/cli/prisma-resolve.js +1 -0
- package/dist/cjs/cli/prisma-schema.d.ts +176 -0
- package/dist/cjs/cli/prisma-schema.js +82 -4
- package/dist/cjs/cli/rate-limit.d.ts +32 -0
- package/dist/cjs/cli/rate-limit.js +45 -0
- package/dist/cjs/cli/studio-demo.d.ts +43 -0
- package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
- package/dist/cjs/cli/studio.d.ts +207 -0
- package/dist/cjs/cli/studio.js +136 -71
- package/dist/cjs/cli/ui.d.ts +73 -0
- package/dist/cjs/cli/ui.js +51 -9
- package/dist/cjs/client.d.ts +837 -0
- package/dist/cjs/client.js +3 -0
- package/dist/cjs/dialect.d.ts +516 -0
- package/dist/cjs/dialect.js +37 -12
- package/dist/cjs/errors.d.ts +370 -0
- package/dist/cjs/generate.d.ts +137 -0
- package/dist/cjs/generate.js +39 -6
- package/dist/cjs/index-advisor.d.ts +153 -0
- package/dist/cjs/index-stats.d.ts +384 -0
- package/dist/cjs/index.d.ts +55 -0
- package/dist/cjs/index.js +7 -2
- package/dist/cjs/introspect.d.ts +269 -0
- package/dist/cjs/mssql.d.ts +232 -0
- package/dist/cjs/mssql.js +6 -0
- package/dist/cjs/mysql.d.ts +173 -0
- package/dist/cjs/mysql.js +16 -0
- package/dist/cjs/nested-write.d.ts +96 -0
- package/dist/cjs/nested-write.js +414 -24
- package/dist/cjs/observe.d.ts +115 -0
- package/dist/cjs/optional-peer-import.d.cts +72 -0
- package/dist/cjs/pipeline-submittable.d.ts +93 -0
- package/dist/cjs/pipeline.d.ts +71 -0
- package/dist/cjs/powdb-introspect.d.ts +84 -0
- package/dist/cjs/powdb.d.ts +931 -0
- package/dist/cjs/powdb.js +106 -21
- package/dist/cjs/powql.d.ts +592 -0
- package/dist/cjs/powql.js +42 -6
- package/dist/cjs/prisma-compat.d.ts +283 -0
- package/dist/cjs/prisma-compat.js +167 -9
- package/dist/cjs/query/aggregates.d.ts +92 -0
- package/dist/cjs/query/aggregates.js +7 -3
- package/dist/cjs/query/batched-loader.d.ts +193 -0
- package/dist/cjs/query/builder.d.ts +849 -0
- package/dist/cjs/query/builder.js +571 -65
- package/dist/cjs/query/compound-unique.d.ts +51 -0
- package/dist/cjs/query/deferred.d.ts +223 -0
- package/dist/cjs/query/filters.d.ts +201 -0
- package/dist/cjs/query/index.d.ts +14 -0
- package/dist/cjs/query/index.js +6 -1
- package/dist/cjs/query/relations.d.ts +609 -0
- package/dist/cjs/query/relations.js +693 -46
- package/dist/cjs/query/types.d.ts +1300 -0
- package/dist/cjs/query/utils.d.ts +209 -0
- package/dist/cjs/query/utils.js +208 -1
- package/dist/cjs/query/warn-registry.d.ts +68 -0
- package/dist/cjs/query/warn-registry.js +9 -0
- package/dist/cjs/query/where-compile.d.ts +139 -0
- package/dist/cjs/query/where.d.ts +548 -0
- package/dist/cjs/query/where.js +58 -22
- package/dist/cjs/query/writes.d.ts +172 -0
- package/dist/cjs/query/writes.js +105 -12
- package/dist/cjs/realtime.d.ts +70 -0
- package/dist/cjs/schema-builder.d.ts +354 -0
- package/dist/cjs/schema-metadata.d.ts +83 -0
- package/dist/cjs/schema-sql.d.ts +217 -0
- package/dist/cjs/schema-sql.js +23 -5
- package/dist/cjs/schema.d.ts +356 -0
- package/dist/cjs/schema.js +125 -0
- package/dist/cjs/seed.d.ts +15 -0
- package/dist/cjs/serverless.d.ts +142 -0
- package/dist/cjs/sqlite.d.ts +143 -0
- package/dist/cjs/sqlite.js +4 -0
- package/dist/cjs/typed-sql.d.ts +102 -0
- package/dist/cli/config.d.ts +18 -4
- package/dist/cli/config.js +31 -6
- package/dist/cli/index.d.ts +123 -0
- package/dist/cli/index.js +223 -58
- package/dist/cli/migrate.d.ts +59 -10
- package/dist/cli/migrate.js +128 -41
- package/dist/cli/observe-ui.d.ts +1 -1
- package/dist/cli/observe-ui.js +14 -5
- package/dist/cli/observe.d.ts +7 -1
- package/dist/cli/observe.js +48 -12
- package/dist/cli/prisma-report.d.ts +14 -0
- package/dist/cli/prisma-report.js +72 -0
- package/dist/cli/prisma-resolve.d.ts +6 -0
- package/dist/cli/prisma-resolve.js +1 -0
- package/dist/cli/prisma-schema.d.ts +62 -2
- package/dist/cli/prisma-schema.js +81 -4
- package/dist/cli/rate-limit.d.ts +32 -0
- package/dist/cli/rate-limit.js +40 -0
- package/dist/cli/studio.d.ts +5 -5
- package/dist/cli/studio.js +135 -70
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.js +51 -9
- package/dist/client.d.ts +40 -0
- package/dist/client.js +3 -0
- package/dist/dialect.d.ts +17 -1
- package/dist/dialect.js +37 -12
- package/dist/generate.js +40 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mssql.js +6 -0
- package/dist/mysql.js +16 -0
- package/dist/nested-write.d.ts +2 -0
- package/dist/nested-write.js +415 -25
- package/dist/powdb.d.ts +4 -2
- package/dist/powdb.js +106 -21
- package/dist/powql.d.ts +5 -0
- package/dist/powql.js +42 -6
- package/dist/prisma-compat.d.ts +2 -0
- package/dist/prisma-compat.js +166 -8
- package/dist/query/aggregates.js +7 -3
- package/dist/query/builder.d.ts +292 -21
- package/dist/query/builder.js +570 -64
- package/dist/query/deferred.d.ts +39 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +173 -5
- package/dist/query/relations.js +688 -47
- package/dist/query/types.d.ts +123 -39
- package/dist/query/utils.d.ts +116 -0
- package/dist/query/utils.js +198 -0
- package/dist/query/warn-registry.d.ts +9 -0
- package/dist/query/warn-registry.js +9 -0
- package/dist/query/where.d.ts +38 -1
- package/dist/query/where.js +58 -23
- package/dist/query/writes.d.ts +42 -1
- package/dist/query/writes.js +104 -13
- package/dist/schema-sql.d.ts +14 -0
- package/dist/schema-sql.js +23 -5
- package/dist/schema.d.ts +38 -0
- package/dist/schema.js +123 -0
- package/dist/sqlite.js +4 -0
- package/package.json +77 -28
package/dist/cjs/client.js
CHANGED
|
@@ -391,6 +391,9 @@ class TurbineClient {
|
|
|
391
391
|
utcTimestamps: config.utcTimestamps,
|
|
392
392
|
relationLoadStrategy: config.relationLoadStrategy,
|
|
393
393
|
stableRelationOrder: config.stableRelationOrder,
|
|
394
|
+
implicitPkOrdering: config.implicitPkOrdering,
|
|
395
|
+
autoToOneJoinMaxRows: config.autoToOneJoinMaxRows,
|
|
396
|
+
autoRoundTripMs: config.autoRoundTripMs,
|
|
394
397
|
jsonEncoding: config.jsonEncoding,
|
|
395
398
|
globalFilters: config.globalFilters,
|
|
396
399
|
preparedStatements: envDisablePrepared ? false : (config.preparedStatements ?? !config.pool),
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm — SQL dialect contract
|
|
3
|
+
*
|
|
4
|
+
* Phase-1 seam for future database packages. The current package remains
|
|
5
|
+
* PostgreSQL-native by default, but query generation now depends on this
|
|
6
|
+
* contract for the SQL primitives that vary across MySQL and SQLite.
|
|
7
|
+
*/
|
|
8
|
+
import type { WithOptions } from './query/types.js';
|
|
9
|
+
import { type RelationDef, type SchemaMetadata, type TableMetadata } from './schema.js';
|
|
10
|
+
export type DialectName = 'postgresql' | 'mysql' | 'sqlite' | (string & {});
|
|
11
|
+
/**
|
|
12
|
+
* A write statement's returning/output selection. `'*'` returns every column
|
|
13
|
+
* (the historical default, byte-identical SQL). A `string[]` is an explicit,
|
|
14
|
+
* SQL-ready quoted column list used to exclude PII-tagged columns from a
|
|
15
|
+
* write's returned row at the SQL level, so PII never crosses the wire
|
|
16
|
+
* unrequested. Each dialect renders it into its own returning surface
|
|
17
|
+
* (`RETURNING …`, `OUTPUT INSERTED.…`).
|
|
18
|
+
*/
|
|
19
|
+
export type ReturningSelection = '*' | readonly string[];
|
|
20
|
+
export interface InsertStatementInput {
|
|
21
|
+
/** SQL-ready quoted table name. */
|
|
22
|
+
table: string;
|
|
23
|
+
/** SQL-ready quoted insert columns. */
|
|
24
|
+
columns: string[];
|
|
25
|
+
/** SQL-ready parameter placeholders/expressions for VALUES. */
|
|
26
|
+
valuePlaceholders: string[];
|
|
27
|
+
/** Optional SQL-ready RETURNING selection. */
|
|
28
|
+
returning?: ReturningSelection;
|
|
29
|
+
}
|
|
30
|
+
export interface BulkInsertStatementInput {
|
|
31
|
+
/** SQL-ready quoted table name. */
|
|
32
|
+
table: string;
|
|
33
|
+
/** SQL-ready quoted insert columns. */
|
|
34
|
+
columns: string[];
|
|
35
|
+
/** Row-major values, one inner array per inserted row. */
|
|
36
|
+
rowValues: unknown[][];
|
|
37
|
+
/** Optional SQL-ready array casts for dialects that batch by column arrays (PostgreSQL UNNEST). */
|
|
38
|
+
columnArrayTypes?: string[];
|
|
39
|
+
/** Skip duplicate rows when supported by the dialect. */
|
|
40
|
+
skipDuplicates?: boolean;
|
|
41
|
+
/** Optional SQL-ready RETURNING selection. */
|
|
42
|
+
returning?: ReturningSelection;
|
|
43
|
+
}
|
|
44
|
+
export interface BuiltStatement {
|
|
45
|
+
sql: string;
|
|
46
|
+
params: unknown[];
|
|
47
|
+
}
|
|
48
|
+
export interface UpsertStatementInput {
|
|
49
|
+
/** SQL-ready quoted table name. */
|
|
50
|
+
table: string;
|
|
51
|
+
/** SQL-ready quoted insert columns. */
|
|
52
|
+
insertColumns: string[];
|
|
53
|
+
/** SQL-ready parameter placeholders/expressions for VALUES. */
|
|
54
|
+
valuePlaceholders: string[];
|
|
55
|
+
/** SQL-ready quoted conflict/unique columns. */
|
|
56
|
+
conflictColumns: string[];
|
|
57
|
+
/** SQL-ready update SET clauses. */
|
|
58
|
+
updateSetClauses: string[];
|
|
59
|
+
/**
|
|
60
|
+
* Optional SQL-ready predicate (no `WHERE` keyword) restricting the
|
|
61
|
+
* conflict-UPDATE to matching rows — used by global filters (soft-delete /
|
|
62
|
+
* multi-tenancy) so an upsert never resurrects/steals a row outside the
|
|
63
|
+
* filter. Only honored by dialects that set
|
|
64
|
+
* {@link Dialect.supportsUpsertUpdateWhere}; others must never receive it.
|
|
65
|
+
*/
|
|
66
|
+
updateWhere?: string;
|
|
67
|
+
/** Optional SQL-ready RETURNING selection. */
|
|
68
|
+
returning?: ReturningSelection;
|
|
69
|
+
}
|
|
70
|
+
export interface ColumnTypeInput {
|
|
71
|
+
/** Schema-builder column type name (PostgreSQL-native in the root package). */
|
|
72
|
+
type: string;
|
|
73
|
+
/** Optional VARCHAR length. */
|
|
74
|
+
maxLength?: number | null;
|
|
75
|
+
}
|
|
76
|
+
export interface ColumnDefinitionInput extends ColumnTypeInput {
|
|
77
|
+
/** SQL-ready quoted column name. */
|
|
78
|
+
name: string;
|
|
79
|
+
/** Whether this column is a single-column primary key. */
|
|
80
|
+
primaryKey?: boolean;
|
|
81
|
+
/** Whether this column is unique. Ignored when primaryKey is true. */
|
|
82
|
+
unique?: boolean;
|
|
83
|
+
/** Whether this column is NOT NULL. */
|
|
84
|
+
notNull?: boolean;
|
|
85
|
+
/** SQL-ready default expression. */
|
|
86
|
+
defaultValue?: string;
|
|
87
|
+
/** SQL-ready REFERENCES clause without the leading REFERENCES keyword. */
|
|
88
|
+
references?: {
|
|
89
|
+
table: string;
|
|
90
|
+
column: string;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export interface CreateTableStatementInput {
|
|
94
|
+
/** SQL-ready quoted table name. */
|
|
95
|
+
table: string;
|
|
96
|
+
/** SQL-ready column and table constraints. */
|
|
97
|
+
definitions: string[];
|
|
98
|
+
}
|
|
99
|
+
export interface CreateIndexStatementInput {
|
|
100
|
+
/** SQL-ready quoted index name. */
|
|
101
|
+
name: string;
|
|
102
|
+
/** SQL-ready quoted table name. */
|
|
103
|
+
table: string;
|
|
104
|
+
/** SQL-ready quoted index columns. */
|
|
105
|
+
columns: string[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* How a dialect surfaces the row(s) produced by an INSERT/UPDATE/DELETE/upsert.
|
|
109
|
+
*
|
|
110
|
+
* - `'returning'` — a trailing `RETURNING *` clause returns the affected rows
|
|
111
|
+
* in the same statement (PostgreSQL, SQLite ≥ 3.35). The executor reads them
|
|
112
|
+
* directly from the statement result.
|
|
113
|
+
* - `'output'` — the statement itself emits the rows in a non-RETURNING shape
|
|
114
|
+
* (SQL Server `OUTPUT INSERTED.*`). Executed exactly like `'returning'`: the
|
|
115
|
+
* rows come back on the statement result.
|
|
116
|
+
* - `'reselect'` — the engine cannot return rows from a write (MySQL). The
|
|
117
|
+
* executor runs the write, then issues a follow-up `SELECT` (by primary
|
|
118
|
+
* key / unique / where predicate) to fetch the affected row(s). The build
|
|
119
|
+
* method supplies the {@link DeferredQuery} `reselect` plan that owns the
|
|
120
|
+
* statement ordering (write-then-select for create/update/upsert;
|
|
121
|
+
* select-then-write for delete, whose row is gone after the statement runs).
|
|
122
|
+
*/
|
|
123
|
+
export type ResultStrategy = 'returning' | 'output' | 'reselect';
|
|
124
|
+
/**
|
|
125
|
+
* Minimal connection surface needed to drive a server-side stream (cursor /
|
|
126
|
+
* driver iterator). `pg.PoolClient` and `PgCompatPoolClient` both satisfy it.
|
|
127
|
+
* Declared locally so {@link Dialect.openStream} stays free of an import cycle
|
|
128
|
+
* back into `client.ts`.
|
|
129
|
+
*/
|
|
130
|
+
export interface StreamableConnection {
|
|
131
|
+
query(text: string, values?: unknown[]): Promise<{
|
|
132
|
+
rows: Record<string, unknown>[];
|
|
133
|
+
}>;
|
|
134
|
+
}
|
|
135
|
+
/** Options for {@link Dialect.openStream}. */
|
|
136
|
+
export interface OpenStreamOptions {
|
|
137
|
+
/**
|
|
138
|
+
* The connection is already inside a caller-owned transaction. Dialects that
|
|
139
|
+
* would otherwise wrap the stream in their own transaction MUST emit no
|
|
140
|
+
* BEGIN / COMMIT / ROLLBACK when this is true: the caller opened the
|
|
141
|
+
* transaction and owns ending it. Cursor cleanup (`CLOSE`) still runs, since
|
|
142
|
+
* the cursor lives on the caller's connection until their transaction ends.
|
|
143
|
+
*/
|
|
144
|
+
ambientTransaction?: boolean;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Inputs for {@link Dialect.buildUpdateStatement} — full UPDATE assembly. Used by
|
|
148
|
+
* engines whose returning shape is injected MID-statement rather than as a trailing
|
|
149
|
+
* clause (SQL Server `OUTPUT INSERTED.*` lands between `SET …` and `WHERE …`).
|
|
150
|
+
*/
|
|
151
|
+
export interface UpdateStatementInput {
|
|
152
|
+
/** SQL-ready quoted table name. */
|
|
153
|
+
table: string;
|
|
154
|
+
/** SQL-ready `col = expr` assignments. */
|
|
155
|
+
setClauses: string[];
|
|
156
|
+
/** SQL-ready WHERE fragment INCLUDING the leading ` WHERE ` (or '' for none). */
|
|
157
|
+
whereSql: string;
|
|
158
|
+
/** SQL-ready returning selection (default `*`). */
|
|
159
|
+
returning?: ReturningSelection;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Inputs for {@link Dialect.buildDeleteStatement} — full DELETE assembly. SQL Server
|
|
163
|
+
* injects `OUTPUT DELETED.*` between `DELETE FROM <t>` and `WHERE …`.
|
|
164
|
+
*/
|
|
165
|
+
export interface DeleteStatementInput {
|
|
166
|
+
/** SQL-ready quoted table name. */
|
|
167
|
+
table: string;
|
|
168
|
+
/** SQL-ready WHERE fragment INCLUDING the leading ` WHERE ` (or '' for none). */
|
|
169
|
+
whereSql: string;
|
|
170
|
+
/** SQL-ready returning selection (default `*`). */
|
|
171
|
+
returning?: ReturningSelection;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Inputs for {@link Dialect.buildLimitOffset} — the trailing pagination clause of an
|
|
175
|
+
* outer SELECT. PostgreSQL/MySQL/SQLite use `LIMIT x [OFFSET y]`; SQL Server has no
|
|
176
|
+
* `LIMIT` and uses `[ORDER BY …] OFFSET y ROWS [FETCH NEXT x ROWS ONLY]` (which
|
|
177
|
+
* requires an ORDER BY — a stable default is injected when {@link hasOrderBy} is false).
|
|
178
|
+
*/
|
|
179
|
+
export interface LimitOffsetInput {
|
|
180
|
+
/** SQL-ready placeholder/literal for the row LIMIT, or undefined for no limit. */
|
|
181
|
+
limitPlaceholder?: string;
|
|
182
|
+
/** SQL-ready placeholder/literal for the OFFSET, or undefined for no offset. */
|
|
183
|
+
offsetPlaceholder?: string;
|
|
184
|
+
/** Whether the outer SELECT already carries an ORDER BY (so none must be injected). */
|
|
185
|
+
hasOrderBy: boolean;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Everything an engine needs to OVERRIDE nested-relation subquery generation, for
|
|
189
|
+
* dialects whose JSON-aggregation shape is fundamentally different from PostgreSQL's
|
|
190
|
+
* `json_agg(json_build_object(...))` (SQL Server's `FOR JSON PATH` expresses the
|
|
191
|
+
* object shape through the child SELECT's column ALIASES rather than an explicit
|
|
192
|
+
* `JSON_OBJECT`, so it cannot be assembled from {@link Dialect.buildJsonObject} /
|
|
193
|
+
* {@link Dialect.buildJsonArrayAgg} primitives — see {@link Dialect.buildRelationSubquery}).
|
|
194
|
+
*
|
|
195
|
+
* The query builder pre-resolves the parts that are engine-independent (alias,
|
|
196
|
+
* select/omit-resolved columns, recursion + param threading) and hands them to the
|
|
197
|
+
* dialect. **Param-push ordering contract:** the override MUST push values to
|
|
198
|
+
* {@link params} in the same order the builder's collect path expects so the SQL
|
|
199
|
+
* cache and pipeline batching stay in sync —
|
|
200
|
+
* - to-many with `limit`/`orderBy` (the "wrap" path) and manyToMany:
|
|
201
|
+
* `buildWhere(...)` → push `limit` → `recurse(...)` for each nested relation;
|
|
202
|
+
* - everything else (to-one, unordered/unlimited to-many): `recurse(...)` for each
|
|
203
|
+
* nested relation → `buildWhere(...)` (no limit param).
|
|
204
|
+
*/
|
|
205
|
+
export interface RelationSubqueryContext {
|
|
206
|
+
/** The relation being expanded (its `type`, `to`, `foreignKey`/`referenceKey`, `through`). */
|
|
207
|
+
relDef: RelationDef;
|
|
208
|
+
/** The `with` spec for this relation: `true`, or a {@link WithOptions} object. */
|
|
209
|
+
spec: true | WithOptions;
|
|
210
|
+
/** Shared parameter array — push BOUND values here in the order described above. */
|
|
211
|
+
params: unknown[];
|
|
212
|
+
/** Parent alias or table name (RAW identifier, not quoted) to correlate against. */
|
|
213
|
+
parentRef: string;
|
|
214
|
+
/** Pre-allocated unique alias for this relation's target rows (e.g. `t0`). */
|
|
215
|
+
alias: string;
|
|
216
|
+
/** Target table name (snake_case). */
|
|
217
|
+
targetTable: string;
|
|
218
|
+
/** Target table metadata. */
|
|
219
|
+
targetMeta: TableMetadata;
|
|
220
|
+
/** Resolved target columns (snake_case) honoring `select` / `omit`. */
|
|
221
|
+
targetColumns: string[];
|
|
222
|
+
/** Current recursion depth (for nested {@link recurse} calls, pass `depth + 1`). */
|
|
223
|
+
depth: number;
|
|
224
|
+
/** Breadcrumb path of relation/table names for circular-relation errors. */
|
|
225
|
+
path: string[];
|
|
226
|
+
/** Quote a RAW identifier through the active dialect. */
|
|
227
|
+
quote(name: string): string;
|
|
228
|
+
/**
|
|
229
|
+
* Build a WHERE fragment for `spec.where` against `whereAlias`, PUSHING its params
|
|
230
|
+
* to {@link params}. Returns '' when the spec has no `where`. Call exactly once.
|
|
231
|
+
*/
|
|
232
|
+
buildWhere(whereAlias: string): string;
|
|
233
|
+
/**
|
|
234
|
+
* Recurse to build a nested relation's subquery (PUSHING its params to
|
|
235
|
+
* {@link params}). Uses the shared alias counter, so nested aliases never collide.
|
|
236
|
+
*/
|
|
237
|
+
recurse(relDef: RelationDef, spec: true | WithOptions, parentRef: string, depth: number, path: string[]): string;
|
|
238
|
+
}
|
|
239
|
+
export interface Dialect {
|
|
240
|
+
/** Dialect identifier. */
|
|
241
|
+
readonly name: DialectName;
|
|
242
|
+
/**
|
|
243
|
+
* How write statements surface their affected rows. PostgreSQL uses
|
|
244
|
+
* `'returning'`; the executor branches on this so non-RETURNING engines
|
|
245
|
+
* (MySQL `'reselect'`, SQL Server `'output'`) can still return rows.
|
|
246
|
+
*/
|
|
247
|
+
readonly resultStrategy: ResultStrategy;
|
|
248
|
+
/** Parameter placeholder for the Nth value, using a 1-indexed public count. */
|
|
249
|
+
paramPlaceholder(index: number): string;
|
|
250
|
+
/** Quote a SQL identifier (table, column, cursor, alias). */
|
|
251
|
+
quoteIdentifier(name: string): string;
|
|
252
|
+
/** Escape a string literal body for SQL single-quoted strings. */
|
|
253
|
+
escapeStringLiteral(value: string): string;
|
|
254
|
+
/** Empty JSON array literal used as a fallback for to-many relations. */
|
|
255
|
+
readonly emptyJsonArrayLiteral: string;
|
|
256
|
+
/** JSON null literal/fallback for to-one relations. */
|
|
257
|
+
readonly nullJsonLiteral: string;
|
|
258
|
+
/** Build a JSON object expression from output keys and SQL expressions. */
|
|
259
|
+
buildJsonObject(pairs: [key: string, expr: string][]): string;
|
|
260
|
+
/**
|
|
261
|
+
* Build a positional JSON ARRAY expression from ordered SQL expressions —
|
|
262
|
+
* the key-less counterpart to {@link buildJsonObject} used by the opt-in
|
|
263
|
+
* `jsonEncoding: 'positional'` mode. Emitting `json_build_array(v1, v2, …)`
|
|
264
|
+
* instead of `json_build_object('k1', v1, …)` drops every repeated key name
|
|
265
|
+
* from every nested object of every row (the decode side maps positions back
|
|
266
|
+
* to keys via a build-time shape descriptor). Postgres-only in v1 — other
|
|
267
|
+
* engines never reach this because the builder gates positional encoding to
|
|
268
|
+
* `dialect.name === 'postgresql'`, so the method is optional on the contract.
|
|
269
|
+
*/
|
|
270
|
+
buildJsonArray?(exprs: string[]): string;
|
|
271
|
+
/** Build a JSON array aggregation expression with a dialect-specific empty-array fallback. */
|
|
272
|
+
buildJsonArrayAgg(jsonObjectExpr: string, orderBy?: string): string;
|
|
273
|
+
/**
|
|
274
|
+
* Whether the array-aggregate (`buildJsonArrayAgg`) can take an inline
|
|
275
|
+
* `ORDER BY` argument. PostgreSQL's `json_agg(... ORDER BY ...)` can, so this
|
|
276
|
+
* is `true`. Engines whose array aggregate has no ORDER BY argument
|
|
277
|
+
* (MySQL `JSON_ARRAYAGG`, SQLite `json_group_array`) set this `false` to force
|
|
278
|
+
* the inner-subquery rewrite for every ordered to-many relation.
|
|
279
|
+
*/
|
|
280
|
+
readonly aggSupportsInlineOrderBy: boolean;
|
|
281
|
+
/**
|
|
282
|
+
* Render `LIMIT` / `OFFSET` as inline integer literals instead of bound
|
|
283
|
+
* parameters. MySQL sets this `true`: mysql2's binary (prepared) protocol sends
|
|
284
|
+
* JS numbers as `DOUBLE`, which MySQL's `LIMIT`/`OFFSET` reject ("Incorrect
|
|
285
|
+
* arguments to mysqld_stmt_execute"). The values are Turbine-validated
|
|
286
|
+
* non-negative integers (never user strings), so inlining is injection-safe.
|
|
287
|
+
* PostgreSQL / SQLite / SQL Server leave this falsy and keep pagination
|
|
288
|
+
* parameterized, so their generated SQL stays byte-identical.
|
|
289
|
+
*/
|
|
290
|
+
readonly inlineLimitOffset?: boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Wrap a correlated nested-relation subquery (and its empty/null fallback) for
|
|
293
|
+
* embedding as a JSON value in a parent `json_build_object`. PostgreSQL emits
|
|
294
|
+
* `COALESCE((subquery), fallback)`. SQLite must additionally `json(...)`-wrap
|
|
295
|
+
* the subquery (its `json_group_array` double-encodes nested objects);
|
|
296
|
+
* SQL Server uses `ISNULL((... FOR JSON PATH), '[]')`.
|
|
297
|
+
*/
|
|
298
|
+
wrapJsonSubresult(subquery: string, fallback: string): string;
|
|
299
|
+
/** Whether INSERT/UPDATE/DELETE support RETURNING rows. */
|
|
300
|
+
readonly supportsReturning: boolean;
|
|
301
|
+
/**
|
|
302
|
+
* Whether {@link buildUpsertStatement} honors {@link UpsertStatementInput.updateWhere}
|
|
303
|
+
* (a predicate on the conflict-UPDATE, e.g. Postgres `ON CONFLICT … DO UPDATE
|
|
304
|
+
* SET … WHERE …`). Global filters only push a conflict-UPDATE predicate when
|
|
305
|
+
* this is true, so engines whose upsert cannot express one (MySQL
|
|
306
|
+
* `ON DUPLICATE KEY UPDATE`) never receive an orphaned parameter. Optional —
|
|
307
|
+
* absent is treated as `false`.
|
|
308
|
+
*/
|
|
309
|
+
readonly supportsUpsertUpdateWhere?: boolean;
|
|
310
|
+
/** Whether this dialect/engine supports pgvector distance ops (KNN / distance WHERE). */
|
|
311
|
+
readonly supportsVector: boolean;
|
|
312
|
+
/**
|
|
313
|
+
* Whether this dialect/engine supports the PostgreSQL full-text `search`
|
|
314
|
+
* filter (`to_tsvector(...) @@ to_tsquery(...)`). Optional: absent is treated
|
|
315
|
+
* as `false`, so only dialects that set it true admit a `search` filter and
|
|
316
|
+
* every other engine throws {@link UnsupportedFeatureError} (E017) instead of
|
|
317
|
+
* emitting SQL its parser cannot read. MySQL `MATCH … AGAINST` and SQL Server
|
|
318
|
+
* `CONTAINS` are different surfaces with different index requirements, so they
|
|
319
|
+
* deliberately stay `false` rather than silently changing the semantics.
|
|
320
|
+
*/
|
|
321
|
+
readonly supportsFullTextSearch?: boolean;
|
|
322
|
+
/**
|
|
323
|
+
* Whether this dialect/engine supports native array columns and the array
|
|
324
|
+
* filter operators built on them (`has` → `= ANY(col)`, `hasEvery` → `@>`,
|
|
325
|
+
* `hasSome` → `&&`, `isEmpty` → `cardinality(col)`). Optional: absent is
|
|
326
|
+
* treated as `false`, so SQLite / MySQL / SQL Server (which have no array
|
|
327
|
+
* column type; a JSON column is not the same thing) throw
|
|
328
|
+
* {@link UnsupportedFeatureError} (E017) instead of emitting `ANY(...)`.
|
|
329
|
+
*/
|
|
330
|
+
readonly supportsArrayColumns?: boolean;
|
|
331
|
+
/** Whether this dialect/engine supports LISTEN/NOTIFY realtime pub/sub. */
|
|
332
|
+
readonly supportsListenNotify: boolean;
|
|
333
|
+
/** Whether this dialect/engine supports row-level-security session GUCs (set_config). */
|
|
334
|
+
readonly supportsRLS: boolean;
|
|
335
|
+
/** Whether this dialect/engine supports advisory-lock-style migration locking. */
|
|
336
|
+
readonly supportsAdvisoryLock: boolean;
|
|
337
|
+
/**
|
|
338
|
+
* Whether this dialect supports `LEFT JOIN LATERAL (...) ON true` in the FROM
|
|
339
|
+
* clause. Gates the opt-in `plan: 'lateral'` pick-row ordering. Optional:
|
|
340
|
+
* absent is treated as `false`, so only dialects that set it true admit the
|
|
341
|
+
* lateral plan (else E017). PostgreSQL only in this release.
|
|
342
|
+
*/
|
|
343
|
+
readonly supportsLateralJoin?: boolean;
|
|
344
|
+
/**
|
|
345
|
+
* How this dialect surfaces a query plan for a compiled SELECT. When present,
|
|
346
|
+
* `QueryInterface.explain()` prepends `prefix` (plus a single space) to the
|
|
347
|
+
* compiled findMany SQL and runs it as a read, returning the plan text lines.
|
|
348
|
+
* PostgreSQL / CockroachDB / YugabyteDB and MySQL use `EXPLAIN`, SQLite
|
|
349
|
+
* `EXPLAIN QUERY PLAN`. Absent means the engine
|
|
350
|
+
* cannot explain a compiled query in-band (SQL Server, whose SHOWPLAN needs a
|
|
351
|
+
* separate session toggle), so `QueryInterface.explain()` throws E017.
|
|
352
|
+
* Optional: dialects that predate this hook keep throwing E017.
|
|
353
|
+
*/
|
|
354
|
+
readonly explainQuery?: {
|
|
355
|
+
prefix: string;
|
|
356
|
+
};
|
|
357
|
+
/** Build a dialect-specific RETURNING clause. Return an empty string when unsupported. */
|
|
358
|
+
buildReturningClause(selection?: ReturningSelection): string;
|
|
359
|
+
/** Build a single-row INSERT statement. Inputs are SQL-ready quoted fragments. */
|
|
360
|
+
buildInsertStatement(input: InsertStatementInput): string;
|
|
361
|
+
/** Build a multi-row bulk INSERT statement and its dialect-shaped params. */
|
|
362
|
+
buildBulkInsertStatement(input: BulkInsertStatementInput): BuiltStatement;
|
|
363
|
+
/** Build an upsert statement. Inputs are SQL-ready quoted fragments. */
|
|
364
|
+
buildUpsertStatement(input: UpsertStatementInput): string;
|
|
365
|
+
/** Whether native ILIKE is supported. */
|
|
366
|
+
readonly supportsILike: boolean;
|
|
367
|
+
/** Build a case-insensitive LIKE equivalent. */
|
|
368
|
+
buildInsensitiveLike(column: string, paramRef: string): string;
|
|
369
|
+
/** JSON operator support level for this dialect. */
|
|
370
|
+
readonly jsonPathSupport: 'native' | 'function' | 'limited';
|
|
371
|
+
/** Build a JSON containment check. */
|
|
372
|
+
buildJsonContains(column: string, paramRef: string): string;
|
|
373
|
+
/** Build a JSON path text extraction expression. */
|
|
374
|
+
buildJsonPathExtract(column: string, pathParamRef: string): string;
|
|
375
|
+
/** Build a correlation clause across single or composite keys. */
|
|
376
|
+
buildCorrelation(leftRef: string, leftColumns: string | string[], rightRef: string, rightColumns: string | string[]): string;
|
|
377
|
+
/** Optional type mapping hook for code generation/introspection. */
|
|
378
|
+
typeToTypeScript?(dialectType: string, nullable: boolean): string;
|
|
379
|
+
/**
|
|
380
|
+
* Cast an aggregate result expression to an integer or float SQL type.
|
|
381
|
+
* PostgreSQL uses the postfix casts `expr::int` / `expr::float`; portable
|
|
382
|
+
* engines (e.g. SQLite, which has no `::` cast operator) emit
|
|
383
|
+
* `CAST(expr AS INTEGER/REAL)`. Optional: when a dialect omits it, the query
|
|
384
|
+
* builder falls back to the PostgreSQL postfix cast, so dialects that predate
|
|
385
|
+
* this hook keep emitting byte-identical SQL.
|
|
386
|
+
*/
|
|
387
|
+
castAggregate?(expr: string, target: 'int' | 'float'): string;
|
|
388
|
+
/**
|
|
389
|
+
* Build a membership (`IN` / `NOT IN`) predicate from a column/expression and
|
|
390
|
+
* a single bound parameter reference. PostgreSQL binds the whole list as one
|
|
391
|
+
* array param (`expr = ANY($n)` / `expr != ALL($n)`), so the placeholder count
|
|
392
|
+
* stays independent of the list length and the SQL cache remains valid.
|
|
393
|
+
* Engines without array parameters (SQLite) override this together with
|
|
394
|
+
* {@link inClauseParam} to use a length-independent single-placeholder form
|
|
395
|
+
* (e.g. `expr IN (SELECT value FROM json_each(?))`). Optional: the query
|
|
396
|
+
* builder falls back to the PostgreSQL `ANY`/`ALL` form when absent.
|
|
397
|
+
*/
|
|
398
|
+
buildInClause?(expr: string, paramRef: string, negated: boolean): string;
|
|
399
|
+
/**
|
|
400
|
+
* The single bound value for an `IN` list (paired with {@link buildInClause}).
|
|
401
|
+
* PostgreSQL passes the array through unchanged; SQLite serializes it to a
|
|
402
|
+
* JSON string consumed by `json_each`. Optional: defaults to the array itself.
|
|
403
|
+
*/
|
|
404
|
+
inClauseParam?(values: unknown[]): unknown;
|
|
405
|
+
/** Optional array-cast hook for bulk insert implementations. */
|
|
406
|
+
arrayType?(baseType: string): string;
|
|
407
|
+
/** Map a schema-builder column type to dialect DDL. */
|
|
408
|
+
buildColumnType(input: ColumnTypeInput): string;
|
|
409
|
+
/** Build a column definition line for CREATE/ALTER TABLE. */
|
|
410
|
+
buildColumnDefinition(input: ColumnDefinitionInput): string;
|
|
411
|
+
/** Build a table-level PRIMARY KEY constraint. */
|
|
412
|
+
buildPrimaryKeyConstraint(columns: string[]): string;
|
|
413
|
+
/** Build a CREATE TABLE statement from SQL-ready definitions. */
|
|
414
|
+
buildCreateTableStatement(input: CreateTableStatementInput): string;
|
|
415
|
+
/** Build a CREATE INDEX statement. */
|
|
416
|
+
buildCreateIndexStatement(input: CreateIndexStatementInput): string;
|
|
417
|
+
/** Build the migration tracking table DDL. */
|
|
418
|
+
buildMigrationTrackingTable(table: string): string;
|
|
419
|
+
/** Build the query that reads applied migrations. */
|
|
420
|
+
buildMigrationSelectApplied(table: string): string;
|
|
421
|
+
/** Build the query that updates an applied migration checksum. */
|
|
422
|
+
buildMigrationUpdateChecksum(table: string): string;
|
|
423
|
+
/** Build the query that records an applied migration. */
|
|
424
|
+
buildMigrationInsertApplied(table: string): string;
|
|
425
|
+
/** Build the query that deletes a rolled-back migration record. */
|
|
426
|
+
buildMigrationDeleteApplied(table: string): string;
|
|
427
|
+
/** `BEGIN`, optionally with a SQL-ready isolation-level suffix. */
|
|
428
|
+
beginStatement(isolationLevel?: string): string;
|
|
429
|
+
/** Commit the current transaction. */
|
|
430
|
+
commitStatement(): string;
|
|
431
|
+
/** Roll back the current transaction. */
|
|
432
|
+
rollbackStatement(): string;
|
|
433
|
+
/** Establish a savepoint with the given (SQL-ready) name. */
|
|
434
|
+
savepointStatement(name: string): string;
|
|
435
|
+
/** Release the named savepoint. */
|
|
436
|
+
releaseSavepointStatement(name: string): string;
|
|
437
|
+
/** Roll back to the named savepoint. */
|
|
438
|
+
rollbackToSavepointStatement(name: string): string;
|
|
439
|
+
/**
|
|
440
|
+
* Build a transaction-local session-config (GUC) assignment for RLS /
|
|
441
|
+
* multi-tenant context. Both name and value are bound parameters.
|
|
442
|
+
*/
|
|
443
|
+
buildSetSessionConfig(name: string, value: string): BuiltStatement;
|
|
444
|
+
/**
|
|
445
|
+
* Drive a server-side stream of result rows on a single connection, yielding
|
|
446
|
+
* row batches of up to `batchSize`. PostgreSQL uses a `DECLARE CURSOR` /
|
|
447
|
+
* `FETCH` / `CLOSE` loop inside a transaction; other engines use their
|
|
448
|
+
* driver's native streaming iterator.
|
|
449
|
+
*
|
|
450
|
+
* `opts.ambientTransaction` tells the dialect the connection is ALREADY inside
|
|
451
|
+
* a caller-owned transaction, so it must not emit its own BEGIN / COMMIT /
|
|
452
|
+
* ROLLBACK (doing so would commit or discard the caller's uncommitted work
|
|
453
|
+
* mid-stream). Engines that emit no transaction control here ignore it.
|
|
454
|
+
*/
|
|
455
|
+
openStream(connection: StreamableConnection, sql: string, params: unknown[], batchSize: number, opts?: OpenStreamOptions): AsyncGenerator<Record<string, unknown>[], void, undefined>;
|
|
456
|
+
/**
|
|
457
|
+
* Schema introspector for this engine. `introspect()` routes through it so
|
|
458
|
+
* engines can override the catalog SQL. PostgreSQL wraps the
|
|
459
|
+
* information_schema / pg_catalog reader in `introspect.ts`.
|
|
460
|
+
*/
|
|
461
|
+
readonly introspector?: DialectIntrospector;
|
|
462
|
+
/**
|
|
463
|
+
* Build the trailing pagination clause for an OUTER SELECT. Optional: when a
|
|
464
|
+
* dialect omits it, the query builder emits the PostgreSQL form
|
|
465
|
+
* (` LIMIT <ph>` and/or ` OFFSET <ph>`), so PG/MySQL/SQLite stay byte-identical.
|
|
466
|
+
* SQL Server has no `LIMIT`, so it implements this to emit
|
|
467
|
+
* `[ORDER BY (SELECT NULL)] OFFSET <off> ROWS [FETCH NEXT <lim> ROWS ONLY]`.
|
|
468
|
+
*/
|
|
469
|
+
buildLimitOffset?(input: LimitOffsetInput): string;
|
|
470
|
+
/**
|
|
471
|
+
* Assemble a full UPDATE statement. Optional: omitted by engines whose returning
|
|
472
|
+
* shape is a trailing clause (PG/SQLite `RETURNING`, MySQL none) — the builder
|
|
473
|
+
* falls back to `UPDATE <t> SET <set> <where><returningClause>`. SQL Server
|
|
474
|
+
* implements this to inject `OUTPUT INSERTED.*` between `SET …` and `WHERE …`.
|
|
475
|
+
*/
|
|
476
|
+
buildUpdateStatement?(input: UpdateStatementInput): string;
|
|
477
|
+
/**
|
|
478
|
+
* Assemble a full DELETE statement. Optional: see {@link buildUpdateStatement}.
|
|
479
|
+
* SQL Server injects `OUTPUT DELETED.*` between `DELETE FROM <t>` and `WHERE …`.
|
|
480
|
+
*/
|
|
481
|
+
buildDeleteStatement?(input: DeleteStatementInput): string;
|
|
482
|
+
/**
|
|
483
|
+
* OVERRIDE nested-relation subquery generation entirely. Optional: when a dialect
|
|
484
|
+
* omits it, the builder uses its native `json_agg(json_build_object(...))` path
|
|
485
|
+
* (PG) routed through {@link buildJsonObject} / {@link buildJsonArrayAgg} /
|
|
486
|
+
* {@link wrapJsonSubresult} — so MySQL and SQLite, which only swap those
|
|
487
|
+
* primitives, produce identical output and never define this hook. SQL Server
|
|
488
|
+
* defines it to emit `FOR JSON PATH` correlated subqueries (its JSON aggregate
|
|
489
|
+
* is expressed through the child SELECT's column aliases, which does not map onto
|
|
490
|
+
* the primitive hooks). See {@link RelationSubqueryContext} for the
|
|
491
|
+
* param-push-ordering contract the override must honor.
|
|
492
|
+
*/
|
|
493
|
+
buildRelationSubquery?(ctx: RelationSubqueryContext): string;
|
|
494
|
+
}
|
|
495
|
+
export interface DialectIntrospector {
|
|
496
|
+
introspect(options: IntrospectOptions): Promise<SchemaMetadata>;
|
|
497
|
+
}
|
|
498
|
+
export interface IntrospectOptions {
|
|
499
|
+
connectionString: string;
|
|
500
|
+
schema?: string;
|
|
501
|
+
include?: string[];
|
|
502
|
+
exclude?: string[];
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* @deprecated Migration locking is owned by the {@link DatabaseAdapter} seam
|
|
506
|
+
* (`src/adapters/index.ts` — `acquireLock`/`releaseLock`/`statementTimeout`),
|
|
507
|
+
* which is the single canonical locking seam. This interface was never
|
|
508
|
+
* implemented or wired and is retained only for type back-compat; new engines
|
|
509
|
+
* MUST provide a `DatabaseAdapter` instead. Will be removed in a future major.
|
|
510
|
+
*/
|
|
511
|
+
export interface DialectMigrator {
|
|
512
|
+
acquireLock(lockId: number): Promise<boolean>;
|
|
513
|
+
releaseLock(lockId: number): Promise<void>;
|
|
514
|
+
}
|
|
515
|
+
/** PostgreSQL implementation of the dialect contract. */
|
|
516
|
+
export declare const postgresDialect: Dialect;
|
package/dist/cjs/dialect.js
CHANGED
|
@@ -238,13 +238,18 @@ exports.postgresDialect = {
|
|
|
238
238
|
params: [name, value],
|
|
239
239
|
};
|
|
240
240
|
},
|
|
241
|
-
async *openStream(connection, sql, params, batchSize) {
|
|
242
|
-
// Cursors require a single connection inside a transaction
|
|
243
|
-
//
|
|
244
|
-
//
|
|
241
|
+
async *openStream(connection, sql, params, batchSize, opts) {
|
|
242
|
+
// Cursors require a single connection inside a transaction: BEGIN → DECLARE
|
|
243
|
+
// … NO SCROLL CURSOR FOR → FETCH n (loop) → CLOSE → COMMIT; ROLLBACK on
|
|
244
|
+
// error. Under an ambient (caller-owned) transaction the three transaction
|
|
245
|
+
// statements are skipped, but the cursor is still CLOSEd: it would otherwise
|
|
246
|
+
// sit open on the caller's connection until their transaction ends.
|
|
247
|
+
const ambient = opts?.ambientTransaction === true;
|
|
245
248
|
const cursorName = `turbine_cursor_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
246
249
|
const quotedCursor = this.quoteIdentifier(cursorName);
|
|
247
|
-
|
|
250
|
+
if (!ambient)
|
|
251
|
+
await connection.query(this.beginStatement());
|
|
252
|
+
let failed = false;
|
|
248
253
|
try {
|
|
249
254
|
await connection.query(`DECLARE ${quotedCursor} NO SCROLL CURSOR FOR ${sql}`, params);
|
|
250
255
|
while (true) {
|
|
@@ -255,18 +260,38 @@ exports.postgresDialect = {
|
|
|
255
260
|
if (batch.rows.length < batchSize)
|
|
256
261
|
break;
|
|
257
262
|
}
|
|
258
|
-
await connection.query(`CLOSE ${quotedCursor}`);
|
|
259
|
-
await connection.query(this.commitStatement());
|
|
260
263
|
}
|
|
261
264
|
catch (err) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
// The transaction is aborted, so CLOSE would fail too: skip cleanup and
|
|
266
|
+
// let the rollback (ours, or the caller's on an ambient transaction) drop
|
|
267
|
+
// the cursor. Never let a cleanup failure mask this error.
|
|
268
|
+
failed = true;
|
|
269
|
+
if (!ambient) {
|
|
270
|
+
try {
|
|
271
|
+
await connection.query(this.rollbackStatement());
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// Connection may already be broken, ignore rollback error.
|
|
275
|
+
}
|
|
267
276
|
}
|
|
268
277
|
throw err;
|
|
269
278
|
}
|
|
279
|
+
finally {
|
|
280
|
+
// Reached on normal completion AND on early exit (`break` from the
|
|
281
|
+
// consumer's `for await` runs the generator's return path through here),
|
|
282
|
+
// which is the case the old try-only cleanup leaked.
|
|
283
|
+
if (!failed) {
|
|
284
|
+
try {
|
|
285
|
+
await connection.query(`CLOSE ${quotedCursor}`);
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// Best-effort: a broken CLOSE must not replace a clean drain with an
|
|
289
|
+
// error. Ending the transaction drops the cursor regardless.
|
|
290
|
+
}
|
|
291
|
+
if (!ambient)
|
|
292
|
+
await connection.query(this.commitStatement());
|
|
293
|
+
}
|
|
294
|
+
}
|
|
270
295
|
},
|
|
271
296
|
// Postgres introspection wraps the information_schema / pg_catalog reader in
|
|
272
297
|
// introspect.ts. A dynamic import keeps the static graph acyclic (introspect.ts
|