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.
Files changed (157) hide show
  1. package/README.md +122 -39
  2. package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
  3. package/dist/cjs/adapters/index.d.ts +110 -0
  4. package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
  5. package/dist/cjs/cli/config.d.ts +181 -0
  6. package/dist/cjs/cli/config.js +32 -6
  7. package/dist/cjs/cli/destructive.d.ts +38 -0
  8. package/dist/cjs/cli/index.d.ts +359 -0
  9. package/dist/cjs/cli/index.js +228 -56
  10. package/dist/cjs/cli/loader.d.ts +61 -0
  11. package/dist/cjs/cli/mcp.d.ts +42 -0
  12. package/dist/cjs/cli/migrate.d.ts +356 -0
  13. package/dist/cjs/cli/migrate.js +131 -40
  14. package/dist/cjs/cli/observe-ui.d.ts +1 -0
  15. package/dist/cjs/cli/observe-ui.js +14 -5
  16. package/dist/cjs/cli/observe.d.ts +25 -0
  17. package/dist/cjs/cli/observe.js +49 -12
  18. package/dist/cjs/cli/pii-tags.d.ts +53 -0
  19. package/dist/cjs/cli/prisma-report.d.ts +33 -0
  20. package/dist/cjs/cli/prisma-report.js +73 -0
  21. package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
  22. package/dist/cjs/cli/prisma-resolve.js +1 -0
  23. package/dist/cjs/cli/prisma-schema.d.ts +176 -0
  24. package/dist/cjs/cli/prisma-schema.js +82 -4
  25. package/dist/cjs/cli/rate-limit.d.ts +32 -0
  26. package/dist/cjs/cli/rate-limit.js +45 -0
  27. package/dist/cjs/cli/studio-demo.d.ts +43 -0
  28. package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
  29. package/dist/cjs/cli/studio.d.ts +207 -0
  30. package/dist/cjs/cli/studio.js +136 -71
  31. package/dist/cjs/cli/ui.d.ts +73 -0
  32. package/dist/cjs/cli/ui.js +51 -9
  33. package/dist/cjs/client.d.ts +837 -0
  34. package/dist/cjs/client.js +3 -0
  35. package/dist/cjs/dialect.d.ts +516 -0
  36. package/dist/cjs/dialect.js +37 -12
  37. package/dist/cjs/errors.d.ts +370 -0
  38. package/dist/cjs/generate.d.ts +137 -0
  39. package/dist/cjs/generate.js +39 -6
  40. package/dist/cjs/index-advisor.d.ts +153 -0
  41. package/dist/cjs/index-stats.d.ts +384 -0
  42. package/dist/cjs/index.d.ts +55 -0
  43. package/dist/cjs/index.js +7 -2
  44. package/dist/cjs/introspect.d.ts +269 -0
  45. package/dist/cjs/mssql.d.ts +232 -0
  46. package/dist/cjs/mssql.js +6 -0
  47. package/dist/cjs/mysql.d.ts +173 -0
  48. package/dist/cjs/mysql.js +16 -0
  49. package/dist/cjs/nested-write.d.ts +96 -0
  50. package/dist/cjs/nested-write.js +414 -24
  51. package/dist/cjs/observe.d.ts +115 -0
  52. package/dist/cjs/optional-peer-import.d.cts +72 -0
  53. package/dist/cjs/pipeline-submittable.d.ts +93 -0
  54. package/dist/cjs/pipeline.d.ts +71 -0
  55. package/dist/cjs/powdb-introspect.d.ts +84 -0
  56. package/dist/cjs/powdb.d.ts +931 -0
  57. package/dist/cjs/powdb.js +106 -21
  58. package/dist/cjs/powql.d.ts +592 -0
  59. package/dist/cjs/powql.js +42 -6
  60. package/dist/cjs/prisma-compat.d.ts +283 -0
  61. package/dist/cjs/prisma-compat.js +167 -9
  62. package/dist/cjs/query/aggregates.d.ts +92 -0
  63. package/dist/cjs/query/aggregates.js +7 -3
  64. package/dist/cjs/query/batched-loader.d.ts +193 -0
  65. package/dist/cjs/query/builder.d.ts +849 -0
  66. package/dist/cjs/query/builder.js +571 -65
  67. package/dist/cjs/query/compound-unique.d.ts +51 -0
  68. package/dist/cjs/query/deferred.d.ts +223 -0
  69. package/dist/cjs/query/filters.d.ts +201 -0
  70. package/dist/cjs/query/index.d.ts +14 -0
  71. package/dist/cjs/query/index.js +6 -1
  72. package/dist/cjs/query/relations.d.ts +609 -0
  73. package/dist/cjs/query/relations.js +693 -46
  74. package/dist/cjs/query/types.d.ts +1300 -0
  75. package/dist/cjs/query/utils.d.ts +209 -0
  76. package/dist/cjs/query/utils.js +208 -1
  77. package/dist/cjs/query/warn-registry.d.ts +68 -0
  78. package/dist/cjs/query/warn-registry.js +9 -0
  79. package/dist/cjs/query/where-compile.d.ts +139 -0
  80. package/dist/cjs/query/where.d.ts +548 -0
  81. package/dist/cjs/query/where.js +58 -22
  82. package/dist/cjs/query/writes.d.ts +172 -0
  83. package/dist/cjs/query/writes.js +105 -12
  84. package/dist/cjs/realtime.d.ts +70 -0
  85. package/dist/cjs/schema-builder.d.ts +354 -0
  86. package/dist/cjs/schema-metadata.d.ts +83 -0
  87. package/dist/cjs/schema-sql.d.ts +217 -0
  88. package/dist/cjs/schema-sql.js +23 -5
  89. package/dist/cjs/schema.d.ts +356 -0
  90. package/dist/cjs/schema.js +125 -0
  91. package/dist/cjs/seed.d.ts +15 -0
  92. package/dist/cjs/serverless.d.ts +142 -0
  93. package/dist/cjs/sqlite.d.ts +143 -0
  94. package/dist/cjs/sqlite.js +4 -0
  95. package/dist/cjs/typed-sql.d.ts +102 -0
  96. package/dist/cli/config.d.ts +18 -4
  97. package/dist/cli/config.js +31 -6
  98. package/dist/cli/index.d.ts +123 -0
  99. package/dist/cli/index.js +223 -58
  100. package/dist/cli/migrate.d.ts +59 -10
  101. package/dist/cli/migrate.js +128 -41
  102. package/dist/cli/observe-ui.d.ts +1 -1
  103. package/dist/cli/observe-ui.js +14 -5
  104. package/dist/cli/observe.d.ts +7 -1
  105. package/dist/cli/observe.js +48 -12
  106. package/dist/cli/prisma-report.d.ts +14 -0
  107. package/dist/cli/prisma-report.js +72 -0
  108. package/dist/cli/prisma-resolve.d.ts +6 -0
  109. package/dist/cli/prisma-resolve.js +1 -0
  110. package/dist/cli/prisma-schema.d.ts +62 -2
  111. package/dist/cli/prisma-schema.js +81 -4
  112. package/dist/cli/rate-limit.d.ts +32 -0
  113. package/dist/cli/rate-limit.js +40 -0
  114. package/dist/cli/studio.d.ts +5 -5
  115. package/dist/cli/studio.js +135 -70
  116. package/dist/cli/ui.d.ts +1 -1
  117. package/dist/cli/ui.js +51 -9
  118. package/dist/client.d.ts +40 -0
  119. package/dist/client.js +3 -0
  120. package/dist/dialect.d.ts +17 -1
  121. package/dist/dialect.js +37 -12
  122. package/dist/generate.js +40 -7
  123. package/dist/index.d.ts +1 -1
  124. package/dist/index.js +1 -1
  125. package/dist/mssql.js +6 -0
  126. package/dist/mysql.js +16 -0
  127. package/dist/nested-write.d.ts +2 -0
  128. package/dist/nested-write.js +415 -25
  129. package/dist/powdb.d.ts +4 -2
  130. package/dist/powdb.js +106 -21
  131. package/dist/powql.d.ts +5 -0
  132. package/dist/powql.js +42 -6
  133. package/dist/prisma-compat.d.ts +2 -0
  134. package/dist/prisma-compat.js +166 -8
  135. package/dist/query/aggregates.js +7 -3
  136. package/dist/query/builder.d.ts +292 -21
  137. package/dist/query/builder.js +570 -64
  138. package/dist/query/deferred.d.ts +39 -0
  139. package/dist/query/index.d.ts +1 -1
  140. package/dist/query/index.js +1 -1
  141. package/dist/query/relations.d.ts +173 -5
  142. package/dist/query/relations.js +688 -47
  143. package/dist/query/types.d.ts +123 -39
  144. package/dist/query/utils.d.ts +116 -0
  145. package/dist/query/utils.js +198 -0
  146. package/dist/query/warn-registry.d.ts +9 -0
  147. package/dist/query/warn-registry.js +9 -0
  148. package/dist/query/where.d.ts +38 -1
  149. package/dist/query/where.js +58 -23
  150. package/dist/query/writes.d.ts +42 -1
  151. package/dist/query/writes.js +104 -13
  152. package/dist/schema-sql.d.ts +14 -0
  153. package/dist/schema-sql.js +23 -5
  154. package/dist/schema.d.ts +38 -0
  155. package/dist/schema.js +123 -0
  156. package/dist/sqlite.js +4 -0
  157. package/package.json +77 -28
@@ -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;
@@ -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. Identical SQL
243
- // sequence to the historical inline implementation: BEGINDECLARE NO
244
- // SCROLL CURSOR FOR FETCH n (loop) CLOSE COMMIT; ROLLBACK on error.
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
- await connection.query(this.beginStatement());
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
- try {
263
- await connection.query(this.rollbackStatement());
264
- }
265
- catch {
266
- // Connection may already be broken — ignore rollback error.
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