turbine-orm 0.50.0 → 0.51.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 +66 -66
- package/dist/adapters/cockroachdb.d.ts +5 -5
- package/dist/adapters/cockroachdb.js +10 -10
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/yugabytedb.d.ts +7 -7
- package/dist/adapters/yugabytedb.js +10 -10
- package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
- package/dist/cjs/adapters/cockroachdb.js +10 -10
- package/dist/cjs/adapters/index.d.ts +5 -5
- package/dist/cjs/adapters/index.js +7 -7
- package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
- package/dist/cjs/adapters/yugabytedb.js +10 -10
- package/dist/cjs/cli/config.d.ts +13 -2
- package/dist/cjs/cli/config.js +3 -2
- package/dist/cjs/cli/destructive.d.ts +1 -1
- package/dist/cjs/cli/destructive.js +1 -1
- package/dist/cjs/cli/index.d.ts +10 -10
- package/dist/cjs/cli/index.js +49 -45
- package/dist/cjs/cli/loader.d.ts +7 -7
- package/dist/cjs/cli/loader.js +9 -9
- package/dist/cjs/cli/mcp.js +4 -4
- package/dist/cjs/cli/migrate.d.ts +5 -5
- package/dist/cjs/cli/migrate.js +11 -11
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/ui.d.ts +2 -2
- package/dist/cjs/cli/ui.js +2 -2
- package/dist/cjs/client.d.ts +49 -38
- package/dist/cjs/client.js +57 -56
- package/dist/cjs/dialect.d.ts +62 -18
- package/dist/cjs/dialect.js +40 -2
- package/dist/cjs/errors.d.ts +5 -5
- package/dist/cjs/errors.js +11 -11
- package/dist/cjs/generate.d.ts +6 -6
- package/dist/cjs/generate.js +31 -29
- package/dist/cjs/index-advisor.d.ts +5 -5
- package/dist/cjs/index-advisor.js +0 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +7 -7
- package/dist/cjs/introspect.d.ts +35 -9
- package/dist/cjs/introspect.js +83 -32
- package/dist/cjs/mssql.d.ts +11 -11
- package/dist/cjs/mssql.js +64 -29
- package/dist/cjs/mysql.d.ts +8 -8
- package/dist/cjs/mysql.js +61 -23
- package/dist/cjs/nested-write.d.ts +21 -2
- package/dist/cjs/nested-write.js +51 -14
- package/dist/cjs/optional-peer-import.cjs +7 -7
- package/dist/cjs/optional-peer-import.d.cts +7 -7
- package/dist/cjs/pipeline-submittable.d.ts +2 -2
- package/dist/cjs/pipeline-submittable.js +6 -6
- package/dist/cjs/pipeline.d.ts +1 -1
- package/dist/cjs/pipeline.js +4 -4
- package/dist/cjs/powdb-introspect.d.ts +1 -1
- package/dist/cjs/powdb-introspect.js +1 -1
- package/dist/cjs/powdb.d.ts +28 -28
- package/dist/cjs/powdb.js +66 -66
- package/dist/cjs/powql.d.ts +27 -27
- package/dist/cjs/powql.js +73 -52
- package/dist/cjs/query/aggregates.d.ts +1 -1
- package/dist/cjs/query/aggregates.js +5 -5
- package/dist/cjs/query/batched-loader.d.ts +11 -11
- package/dist/cjs/query/batched-loader.js +24 -24
- package/dist/cjs/query/builder.d.ts +39 -21
- package/dist/cjs/query/builder.js +99 -57
- package/dist/cjs/query/compound-unique.d.ts +1 -1
- package/dist/cjs/query/compound-unique.js +0 -0
- package/dist/cjs/query/deferred.d.ts +12 -6
- package/dist/cjs/query/deferred.js +1 -1
- package/dist/cjs/query/filters.d.ts +31 -11
- package/dist/cjs/query/filters.js +67 -14
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/index.js +1 -1
- package/dist/cjs/query/relations.d.ts +9 -9
- package/dist/cjs/query/relations.js +164 -57
- package/dist/cjs/query/types.d.ts +86 -35
- package/dist/cjs/query/types.js +1 -1
- package/dist/cjs/query/utils.d.ts +27 -10
- package/dist/cjs/query/utils.js +86 -14
- package/dist/cjs/query/where.d.ts +47 -28
- package/dist/cjs/query/where.js +130 -31
- package/dist/cjs/query/writes.d.ts +24 -5
- package/dist/cjs/query/writes.js +102 -13
- package/dist/cjs/realtime.d.ts +7 -7
- package/dist/cjs/realtime.js +9 -9
- package/dist/cjs/schema-builder.d.ts +18 -7
- package/dist/cjs/schema-builder.js +17 -10
- package/dist/cjs/schema-metadata.d.ts +3 -3
- package/dist/cjs/schema-metadata.js +9 -9
- package/dist/cjs/schema-sql.d.ts +9 -9
- package/dist/cjs/schema-sql.js +20 -20
- package/dist/cjs/schema.d.ts +19 -9
- package/dist/cjs/schema.js +6 -6
- package/dist/cjs/serverless.d.ts +15 -15
- package/dist/cjs/serverless.js +16 -16
- package/dist/cjs/sqlite.d.ts +8 -8
- package/dist/cjs/sqlite.js +53 -22
- package/dist/cjs/typed-sql.d.ts +4 -4
- package/dist/cjs/typed-sql.js +5 -5
- package/dist/cli/config.d.ts +13 -2
- package/dist/cli/config.js +3 -2
- package/dist/cli/destructive.d.ts +1 -1
- package/dist/cli/destructive.js +1 -1
- package/dist/cli/index.d.ts +10 -10
- package/dist/cli/index.js +49 -45
- package/dist/cli/loader.d.ts +7 -7
- package/dist/cli/loader.js +9 -9
- package/dist/cli/mcp.js +4 -4
- package/dist/cli/migrate.d.ts +5 -5
- package/dist/cli/migrate.js +11 -11
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/ui.d.ts +2 -2
- package/dist/cli/ui.js +2 -2
- package/dist/client.d.ts +49 -38
- package/dist/client.js +57 -56
- package/dist/dialect.d.ts +62 -18
- package/dist/dialect.js +40 -2
- package/dist/errors.d.ts +5 -5
- package/dist/errors.js +11 -11
- package/dist/generate.d.ts +6 -6
- package/dist/generate.js +31 -29
- package/dist/index-advisor.d.ts +5 -5
- package/dist/index-advisor.js +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/introspect.d.ts +35 -9
- package/dist/introspect.js +82 -32
- package/dist/mssql.d.ts +11 -11
- package/dist/mssql.js +64 -29
- package/dist/mysql.d.ts +8 -8
- package/dist/mysql.js +61 -23
- package/dist/nested-write.d.ts +21 -2
- package/dist/nested-write.js +51 -14
- package/dist/optional-peer-import.cjs +7 -7
- package/dist/optional-peer-import.d.cts +7 -7
- package/dist/pipeline-submittable.d.ts +2 -2
- package/dist/pipeline-submittable.js +6 -6
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +4 -4
- package/dist/powdb-introspect.d.ts +1 -1
- package/dist/powdb-introspect.js +1 -1
- package/dist/powdb.d.ts +28 -28
- package/dist/powdb.js +66 -66
- package/dist/powql.d.ts +27 -27
- package/dist/powql.js +73 -52
- package/dist/query/aggregates.d.ts +1 -1
- package/dist/query/aggregates.js +5 -5
- package/dist/query/batched-loader.d.ts +11 -11
- package/dist/query/batched-loader.js +24 -24
- package/dist/query/builder.d.ts +39 -21
- package/dist/query/builder.js +100 -58
- package/dist/query/compound-unique.d.ts +1 -1
- package/dist/query/compound-unique.js +0 -0
- package/dist/query/deferred.d.ts +12 -6
- package/dist/query/deferred.js +1 -1
- package/dist/query/filters.d.ts +31 -11
- package/dist/query/filters.js +66 -13
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +9 -9
- package/dist/query/relations.js +165 -58
- package/dist/query/types.d.ts +86 -35
- package/dist/query/types.js +1 -1
- package/dist/query/utils.d.ts +27 -10
- package/dist/query/utils.js +84 -14
- package/dist/query/where.d.ts +47 -28
- package/dist/query/where.js +129 -32
- package/dist/query/writes.d.ts +24 -5
- package/dist/query/writes.js +101 -13
- package/dist/realtime.d.ts +7 -7
- package/dist/realtime.js +9 -9
- package/dist/schema-builder.d.ts +18 -7
- package/dist/schema-builder.js +17 -10
- package/dist/schema-metadata.d.ts +3 -3
- package/dist/schema-metadata.js +9 -9
- package/dist/schema-sql.d.ts +9 -9
- package/dist/schema-sql.js +20 -20
- package/dist/schema.d.ts +19 -9
- package/dist/schema.js +6 -6
- package/dist/serverless.d.ts +15 -15
- package/dist/serverless.js +16 -16
- package/dist/sqlite.d.ts +8 -8
- package/dist/sqlite.js +53 -22
- package/dist/typed-sql.d.ts +4 -4
- package/dist/typed-sql.js +5 -5
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm
|
|
2
|
+
* turbine-orm, TurbineClient
|
|
3
3
|
*
|
|
4
4
|
* The main entry point for the Turbine TypeScript SDK.
|
|
5
5
|
* Manages connection pooling and provides typed table accessors.
|
|
@@ -87,12 +87,12 @@ const READ_OPERATIONS = new Set([
|
|
|
87
87
|
/**
|
|
88
88
|
* Internal marker on the config object that tells the `TurbineClient`
|
|
89
89
|
* constructor to build a lightweight "primary-only" view sharing an existing
|
|
90
|
-
* client's primary pool, dialect, query options, and middleware
|
|
90
|
+
* client's primary pool, dialect, query options, and middleware, instead of
|
|
91
91
|
* creating a fresh pool. Produced solely by `$primary()`; never public.
|
|
92
92
|
*/
|
|
93
93
|
const PRIMARY_VIEW = Symbol('turbine.primaryView');
|
|
94
94
|
// ---------------------------------------------------------------------------
|
|
95
|
-
// TransactionClient
|
|
95
|
+
// TransactionClient, provides typed table accessors within a transaction
|
|
96
96
|
// ---------------------------------------------------------------------------
|
|
97
97
|
/**
|
|
98
98
|
* A transaction-scoped client that provides the same table accessor API as TurbineClient.
|
|
@@ -107,7 +107,7 @@ export class TransactionClient {
|
|
|
107
107
|
sourcePool;
|
|
108
108
|
tableCache = new Map();
|
|
109
109
|
savepointCounter = 0;
|
|
110
|
-
/** Active SQL dialect
|
|
110
|
+
/** Active SQL dialect, owns savepoint keywords and raw-SQL placeholders. */
|
|
111
111
|
dialect;
|
|
112
112
|
constructor(client, schema, middlewares, queryOptions,
|
|
113
113
|
/**
|
|
@@ -235,14 +235,14 @@ export class TransactionClient {
|
|
|
235
235
|
// TurbineClient
|
|
236
236
|
// ---------------------------------------------------------------------------
|
|
237
237
|
export class TurbineClient {
|
|
238
|
-
/** The underlying pg.Pool
|
|
238
|
+
/** The underlying pg.Pool, exposed for escape hatches */
|
|
239
239
|
pool;
|
|
240
240
|
/** The schema metadata this client was built from */
|
|
241
241
|
schema;
|
|
242
242
|
static int8ParserRegistered = false;
|
|
243
243
|
static utcTimestampParserRegistered = false;
|
|
244
244
|
logging;
|
|
245
|
-
/** Active SQL dialect
|
|
245
|
+
/** Active SQL dialect, owns transaction keywords, set_config, raw-SQL placeholders, capability flags. */
|
|
246
246
|
dialect;
|
|
247
247
|
tableCache = new Map();
|
|
248
248
|
middlewares = [];
|
|
@@ -251,7 +251,7 @@ export class TurbineClient {
|
|
|
251
251
|
errorMessagesSafe;
|
|
252
252
|
/** True when Turbine created the pool and is responsible for tearing it down */
|
|
253
253
|
ownsPool = true;
|
|
254
|
-
/** Active LISTEN subscriptions
|
|
254
|
+
/** Active LISTEN subscriptions, torn down on disconnect() so it never hangs */
|
|
255
255
|
activeSubscriptions = new Set();
|
|
256
256
|
/**
|
|
257
257
|
* Read-replica pools in round-robin order. Empty when no replicas are
|
|
@@ -347,7 +347,7 @@ export class TurbineClient {
|
|
|
347
347
|
*/
|
|
348
348
|
// Only register the int8 parser when the PRIMARY pool is Turbine-owned.
|
|
349
349
|
// External pools (Neon HTTP, Vercel Postgres) may ship their own pg-types
|
|
350
|
-
// fork and rely on their own parser configuration
|
|
350
|
+
// fork and rely on their own parser configuration, registration is
|
|
351
351
|
// process-global, so flipping it because a string replica exists alongside
|
|
352
352
|
// an external primary would silently change the external primary's parsing
|
|
353
353
|
// too. String replicas configured next to an external primary therefore
|
|
@@ -364,8 +364,8 @@ export class TurbineClient {
|
|
|
364
364
|
// Parse `timestamp` (OID 1114) as UTC instead of server-local time. The
|
|
365
365
|
// pg driver's default hands back a Date built in the process's local zone,
|
|
366
366
|
// so the same row yields a different instant per deployment region. The
|
|
367
|
-
// ORM convention (Prisma, Rails, Django)
|
|
368
|
-
// that round-trips what Postgres stores
|
|
367
|
+
// ORM convention (Prisma, Rails, Django), and the only interpretation
|
|
368
|
+
// that round-trips what Postgres stores, is UTC. Same ownership rule as
|
|
369
369
|
// the int8 parser: never mutate parser state on external pools.
|
|
370
370
|
if (ownsAnyPool && config.utcTimestamps !== false && !TurbineClient.utcTimestampParserRegistered) {
|
|
371
371
|
pg.types.setTypeParser(1114, (val) => new Date(`${val.replace(' ', 'T')}Z`));
|
|
@@ -381,6 +381,7 @@ export class TurbineClient {
|
|
|
381
381
|
defaultLimit: config.defaultLimit,
|
|
382
382
|
warnOnUnlimited: config.warnOnUnlimited,
|
|
383
383
|
utcTimestamps: config.utcTimestamps,
|
|
384
|
+
scopedConnect: config.scopedConnect,
|
|
384
385
|
relationLoadStrategy: config.relationLoadStrategy,
|
|
385
386
|
stableRelationOrder: config.stableRelationOrder,
|
|
386
387
|
implicitPkOrdering: config.implicitPkOrdering,
|
|
@@ -412,17 +413,17 @@ export class TurbineClient {
|
|
|
412
413
|
}
|
|
413
414
|
},
|
|
414
415
|
};
|
|
415
|
-
// Apply NotFoundError message redaction mode (default: safe
|
|
416
|
+
// Apply NotFoundError message redaction mode (default: safe, values are
|
|
416
417
|
// stripped from messages to avoid leaking PII into error logs).
|
|
417
418
|
if (config.errorMessages) {
|
|
418
419
|
setErrorMessageMode(config.errorMessages);
|
|
419
420
|
}
|
|
420
421
|
if (config.pool) {
|
|
421
|
-
// External pool
|
|
422
|
+
// External pool, use directly. Turbine doesn't manage its lifecycle.
|
|
422
423
|
this.pool = config.pool;
|
|
423
424
|
this.ownsPool = false;
|
|
424
425
|
if (this.logging) {
|
|
425
|
-
console.log(`[turbine] Using external pool
|
|
426
|
+
console.log(`[turbine] Using external pool, ${Object.keys(schema.tables).length} tables`);
|
|
426
427
|
}
|
|
427
428
|
}
|
|
428
429
|
else {
|
|
@@ -466,7 +467,7 @@ export class TurbineClient {
|
|
|
466
467
|
console.error('[turbine] Unexpected pool error:', err.message);
|
|
467
468
|
});
|
|
468
469
|
if (this.logging) {
|
|
469
|
-
console.log(`[turbine] Pool created
|
|
470
|
+
console.log(`[turbine] Pool created, max ${poolConfig.max} connections, ${Object.keys(schema.tables).length} tables`);
|
|
470
471
|
}
|
|
471
472
|
}
|
|
472
473
|
// Build read-replica pools (if any). String entries become owned pg.Pools
|
|
@@ -514,14 +515,14 @@ export class TurbineClient {
|
|
|
514
515
|
}
|
|
515
516
|
}
|
|
516
517
|
// -------------------------------------------------------------------------
|
|
517
|
-
// Middleware
|
|
518
|
+
// Middleware, intercept all queries
|
|
518
519
|
// -------------------------------------------------------------------------
|
|
519
520
|
/**
|
|
520
521
|
* Register a middleware function that runs around every query.
|
|
521
522
|
*
|
|
522
523
|
* Middleware can inspect and log query parameters, measure timing, and
|
|
523
524
|
* transform the result returned by `next()`. Note: query SQL is generated
|
|
524
|
-
* BEFORE middleware runs
|
|
525
|
+
* BEFORE middleware runs, `params.args` is a read-only snapshot, and
|
|
525
526
|
* mutating it does NOT change the executed SQL. Cross-cutting filters
|
|
526
527
|
* (e.g. soft deletes) belong in the query itself: pass an explicit
|
|
527
528
|
* `where: { deletedAt: null }` or wrap the table accessor in a small helper.
|
|
@@ -536,7 +537,7 @@ export class TurbineClient {
|
|
|
536
537
|
* return result;
|
|
537
538
|
* });
|
|
538
539
|
*
|
|
539
|
-
* // Result transformation middleware
|
|
540
|
+
* // Result transformation middleware, redact a field on the way out
|
|
540
541
|
* db.$use(async (params, next) => {
|
|
541
542
|
* const result = await next(params);
|
|
542
543
|
* if (params.model === 'users' && Array.isArray(result)) {
|
|
@@ -558,7 +559,7 @@ export class TurbineClient {
|
|
|
558
559
|
cache.clear();
|
|
559
560
|
}
|
|
560
561
|
// -------------------------------------------------------------------------
|
|
561
|
-
// Event emitter
|
|
562
|
+
// Event emitter, subscribe to query lifecycle events
|
|
562
563
|
// -------------------------------------------------------------------------
|
|
563
564
|
$on(_event, listener) {
|
|
564
565
|
this.queryListeners.add(listener);
|
|
@@ -567,7 +568,7 @@ export class TurbineClient {
|
|
|
567
568
|
this.queryListeners.delete(listener);
|
|
568
569
|
}
|
|
569
570
|
// -------------------------------------------------------------------------
|
|
570
|
-
// Observability
|
|
571
|
+
// Observability, automatic metrics collection
|
|
571
572
|
// -------------------------------------------------------------------------
|
|
572
573
|
observeEngine;
|
|
573
574
|
async $observe(config) {
|
|
@@ -589,16 +590,16 @@ export class TurbineClient {
|
|
|
589
590
|
};
|
|
590
591
|
}
|
|
591
592
|
// -------------------------------------------------------------------------
|
|
592
|
-
// Table accessor
|
|
593
|
+
// Table accessor, creates QueryInterface for any table
|
|
593
594
|
// -------------------------------------------------------------------------
|
|
594
595
|
/**
|
|
595
596
|
* Get a QueryInterface for a table.
|
|
596
|
-
* Results are cached
|
|
597
|
+
* Results are cached, calling `table('users')` twice returns the same instance.
|
|
597
598
|
*
|
|
598
599
|
* When read replicas are configured, this returns a thin routing proxy: the
|
|
599
600
|
* read-only operations in {@link READ_OPERATIONS} are dispatched to a
|
|
600
|
-
* round-robin replica-bound QueryInterface (so an entire read
|
|
601
|
-
* any batched sub-queries
|
|
601
|
+
* round-robin replica-bound QueryInterface (so an entire read, base rows and
|
|
602
|
+
* any batched sub-queries, runs against a single consistent replica), while
|
|
602
603
|
* writes and every other member fall through to the primary-bound instance.
|
|
603
604
|
* With no replicas the original single-pool instance is returned directly.
|
|
604
605
|
*/
|
|
@@ -673,7 +674,7 @@ export class TurbineClient {
|
|
|
673
674
|
});
|
|
674
675
|
}
|
|
675
676
|
/**
|
|
676
|
-
* Return a view of this client that pins EVERY operation
|
|
677
|
+
* Return a view of this client that pins EVERY operation, reads included -
|
|
677
678
|
* to the primary pool, bypassing replica routing. Use it to read your own
|
|
678
679
|
* write without replication lag, or for any read that must see the latest
|
|
679
680
|
* committed data.
|
|
@@ -681,7 +682,7 @@ export class TurbineClient {
|
|
|
681
682
|
* The view shares the primary pool, schema, dialect, query options, and
|
|
682
683
|
* middleware; it owns nothing, so its `disconnect()` is a no-op. When no
|
|
683
684
|
* replicas are configured this simply returns the client itself (already
|
|
684
|
-
* primary-only). The view is cached
|
|
685
|
+
* primary-only). The view is cached, repeated calls return the same instance.
|
|
685
686
|
*
|
|
686
687
|
* @example
|
|
687
688
|
* ```ts
|
|
@@ -699,14 +700,14 @@ export class TurbineClient {
|
|
|
699
700
|
return this.primaryView;
|
|
700
701
|
}
|
|
701
702
|
// -------------------------------------------------------------------------
|
|
702
|
-
// Pipeline
|
|
703
|
+
// Pipeline, batch multiple queries into one round-trip
|
|
703
704
|
// -------------------------------------------------------------------------
|
|
704
705
|
/**
|
|
705
706
|
* Execute multiple queries in a single database round-trip.
|
|
706
707
|
*
|
|
707
708
|
* Two call styles:
|
|
708
|
-
* - `db.pipeline(q1, q2, q3)
|
|
709
|
-
* - `db.pipeline([q1, q2, q3], { transactional: false })
|
|
709
|
+
* - `db.pipeline(q1, q2, q3)`, rest params (backward-compatible)
|
|
710
|
+
* - `db.pipeline([q1, q2, q3], { transactional: false })`, array + options
|
|
710
711
|
*
|
|
711
712
|
* On pg.Pool-backed connections with TCP, this uses the real Postgres
|
|
712
713
|
* extended-query pipeline protocol (one TCP flush, one round-trip).
|
|
@@ -728,7 +729,7 @@ export class TurbineClient {
|
|
|
728
729
|
queries = args;
|
|
729
730
|
}
|
|
730
731
|
if (this.logging) {
|
|
731
|
-
console.log(`[turbine] Pipeline: ${queries.length} queries
|
|
732
|
+
console.log(`[turbine] Pipeline: ${queries.length} queries, ${queries.map((q) => q.tag).join(', ')}`);
|
|
732
733
|
}
|
|
733
734
|
return executePipeline(this.pool, queries, options);
|
|
734
735
|
}
|
|
@@ -741,7 +742,7 @@ export class TurbineClient {
|
|
|
741
742
|
return pipelineSupported(this.pool);
|
|
742
743
|
}
|
|
743
744
|
// -------------------------------------------------------------------------
|
|
744
|
-
// Raw SQL
|
|
745
|
+
// Raw SQL, tagged template literal escape hatch
|
|
745
746
|
// -------------------------------------------------------------------------
|
|
746
747
|
/**
|
|
747
748
|
* Execute a raw SQL query with parameter interpolation via tagged templates.
|
|
@@ -775,7 +776,7 @@ export class TurbineClient {
|
|
|
775
776
|
}
|
|
776
777
|
}
|
|
777
778
|
/**
|
|
778
|
-
* Execute a **typed** raw SQL query
|
|
779
|
+
* Execute a **typed** raw SQL query, Turbine's answer to Prisma's TypedSQL.
|
|
779
780
|
*
|
|
780
781
|
* Like {@link raw}, every interpolated `${value}` becomes a `$N` parameter
|
|
781
782
|
* (never string-concatenated), so it is injection-safe by construction. The
|
|
@@ -783,7 +784,7 @@ export class TurbineClient {
|
|
|
783
784
|
* returned {@link TypedSqlQuery} can be `await`ed directly for `T[]`, or
|
|
784
785
|
* refined with `.one()` (→ `T | null`) or `.scalar<V>()` (→ `V | null`).
|
|
785
786
|
*
|
|
786
|
-
* Rows are returned as-is
|
|
787
|
+
* Rows are returned as-is, no snake→camel mapping (matching `raw()`). Alias
|
|
787
788
|
* columns in SQL if you want camelCase keys.
|
|
788
789
|
*
|
|
789
790
|
* @example
|
|
@@ -809,7 +810,7 @@ export class TurbineClient {
|
|
|
809
810
|
return new TypedSqlQuery(this.pool, sql, params, this.logging);
|
|
810
811
|
}
|
|
811
812
|
// -------------------------------------------------------------------------
|
|
812
|
-
// Transaction support (raw
|
|
813
|
+
// Transaction support (raw, legacy)
|
|
813
814
|
// -------------------------------------------------------------------------
|
|
814
815
|
/**
|
|
815
816
|
* Execute a function within a database transaction (raw pg.PoolClient).
|
|
@@ -828,7 +829,7 @@ export class TurbineClient {
|
|
|
828
829
|
* Only true once BEGIN has actually succeeded. If BEGIN itself throws
|
|
829
830
|
* (e.g. a single-writer engine's transaction gate times out or rejects a
|
|
830
831
|
* re-entrant begin), issuing a "best-effort" ROLLBACK would be a stray
|
|
831
|
-
* statement from a context that never opened a transaction
|
|
832
|
+
* statement from a context that never opened a transaction, on a driver
|
|
832
833
|
* with one shared engine handle (PowDB embedded) it would roll back a
|
|
833
834
|
* DIFFERENT caller's open transaction.
|
|
834
835
|
*/
|
|
@@ -851,7 +852,7 @@ export class TurbineClient {
|
|
|
851
852
|
await client.query(this.dialect.rollbackStatement());
|
|
852
853
|
}
|
|
853
854
|
catch {
|
|
854
|
-
// Best-effort rollback
|
|
855
|
+
// Best-effort rollback, the connection may have died mid-query.
|
|
855
856
|
}
|
|
856
857
|
}
|
|
857
858
|
throw err;
|
|
@@ -883,21 +884,21 @@ export class TurbineClient {
|
|
|
883
884
|
client.release(err);
|
|
884
885
|
}
|
|
885
886
|
catch {
|
|
886
|
-
// pg may throw if the client is already released
|
|
887
|
+
// pg may throw if the client is already released, swallow.
|
|
887
888
|
}
|
|
888
889
|
};
|
|
889
890
|
let timedOut = false;
|
|
890
891
|
/**
|
|
891
|
-
* Only true once BEGIN has actually succeeded. If BEGIN itself throws
|
|
892
|
+
* Only true once BEGIN has actually succeeded. If BEGIN itself throws -
|
|
892
893
|
* e.g. a single-writer engine's transaction gate times out in its FIFO
|
|
893
|
-
* queue or rejects a re-entrant begin (PowDB, E002/E017)
|
|
894
|
+
* queue or rejects a re-entrant begin (PowDB, E002/E017), this context
|
|
894
895
|
* never opened a transaction, so the catch below must NOT issue its
|
|
895
896
|
* best-effort ROLLBACK: on a driver with one shared engine handle that
|
|
896
897
|
* stray ROLLBACK would tear down a DIFFERENT caller's open transaction.
|
|
897
898
|
*/
|
|
898
899
|
let began = false;
|
|
899
900
|
try {
|
|
900
|
-
// BEGIN with optional isolation level
|
|
901
|
+
// BEGIN with optional isolation level, the dialect owns the keyword and
|
|
901
902
|
// BEGIN+isolation composition (Postgres appends ` ISOLATION LEVEL …`).
|
|
902
903
|
const isolationSql = options?.isolationLevel ? ISOLATION_LEVELS[options.isolationLevel] : undefined;
|
|
903
904
|
await client.query(this.dialect.beginStatement(isolationSql));
|
|
@@ -906,15 +907,15 @@ export class TurbineClient {
|
|
|
906
907
|
// Order matters: BEGIN -> isolation level (above) -> set_config loop ->
|
|
907
908
|
// user fn. Any error here propagates to the catch below and rolls back
|
|
908
909
|
// like any other transaction failure. We use set_config(name, value,
|
|
909
|
-
// is_local=true)
|
|
910
|
-
// SET LOCAL
|
|
910
|
+
// is_local=true), the parameterizable, transaction-scoped equivalent of
|
|
911
|
+
// SET LOCAL, so both name and value are BOUND params, never interpolated.
|
|
911
912
|
if (options?.sessionContext) {
|
|
912
913
|
if (!this.dialect.supportsRLS) {
|
|
913
914
|
throw new UnsupportedFeatureError('sessionContext (RLS session GUCs)', this.dialect.name, 'set_config-based row-level-security context requires PostgreSQL.');
|
|
914
915
|
}
|
|
915
916
|
for (const [name, value] of Object.entries(options.sessionContext)) {
|
|
916
917
|
if (!GUC_NAME_REGEX.test(name)) {
|
|
917
|
-
throw new ValidationError(`[turbine] Invalid session-context GUC name "${name}"
|
|
918
|
+
throw new ValidationError(`[turbine] Invalid session-context GUC name "${name}", must match ` +
|
|
918
919
|
'/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)?$/ (optionally namespaced, e.g. "app.current_tenant")');
|
|
919
920
|
}
|
|
920
921
|
const cfg = this.dialect.buildSetSessionConfig(name, String(value));
|
|
@@ -944,7 +945,7 @@ export class TurbineClient {
|
|
|
944
945
|
const runCallback = () => (wrap ? wrap.call(client, () => fn(tx)) : fn(tx));
|
|
945
946
|
if (timeout) {
|
|
946
947
|
// Race between the function and a timeout. If the timeout fires we
|
|
947
|
-
// need to actually abort the in-flight query
|
|
948
|
+
// need to actually abort the in-flight query, otherwise the backend
|
|
948
949
|
// keeps running until pg's own timeout, holding a pool slot the whole
|
|
949
950
|
// time. The simplest reliable cancellation is to destroy the
|
|
950
951
|
// connection: passing a truthy argument to client.release() tells the
|
|
@@ -958,7 +959,7 @@ export class TurbineClient {
|
|
|
958
959
|
// Destroy the connection to abort the in-flight backend query.
|
|
959
960
|
// We do this BEFORE rejecting so the socket is gone by the time
|
|
960
961
|
// the caller's catch block runs.
|
|
961
|
-
releaseOnce(new Error('[turbine] Transaction timeout
|
|
962
|
+
releaseOnce(new Error('[turbine] Transaction timeout, connection destroyed'));
|
|
962
963
|
reject(new TimeoutError(timeout, 'Transaction'));
|
|
963
964
|
}, timeout);
|
|
964
965
|
});
|
|
@@ -979,11 +980,11 @@ export class TurbineClient {
|
|
|
979
980
|
return result;
|
|
980
981
|
}
|
|
981
982
|
catch (err) {
|
|
982
|
-
// If the timeout fired we already destroyed the connection
|
|
983
|
+
// If the timeout fired we already destroyed the connection, issuing a
|
|
983
984
|
// ROLLBACK on a released client would throw "Client has already been
|
|
984
985
|
// released". Skip the rollback in that case (the backend rolled back
|
|
985
986
|
// when its socket was closed). Likewise skip it when BEGIN never
|
|
986
|
-
// succeeded (`began` false)
|
|
987
|
+
// succeeded (`began` false), there is no transaction to roll back and
|
|
987
988
|
// the stray statement could hit another caller's transaction on a
|
|
988
989
|
// shared-handle engine.
|
|
989
990
|
if (began && !timedOut && !released) {
|
|
@@ -991,7 +992,7 @@ export class TurbineClient {
|
|
|
991
992
|
await client.query(this.dialect.rollbackStatement());
|
|
992
993
|
}
|
|
993
994
|
catch {
|
|
994
|
-
// Best-effort rollback
|
|
995
|
+
// Best-effort rollback, the connection may have died mid-query.
|
|
995
996
|
}
|
|
996
997
|
}
|
|
997
998
|
if (this.logging) {
|
|
@@ -1017,7 +1018,7 @@ export class TurbineClient {
|
|
|
1017
1018
|
* {@link PgCompatPoolClient.supportsPipelining} (its `query()` accepts
|
|
1018
1019
|
* concurrent calls and completes them in FIFO submission order), all
|
|
1019
1020
|
* statements are dispatched in one write burst and the replies are
|
|
1020
|
-
* collected in order
|
|
1021
|
+
* collected in order, ~1 round trip plus server time. Only taken when
|
|
1021
1022
|
* the dialect's writes surface rows directly (`resultStrategy` !==
|
|
1022
1023
|
* 'reselect'): a reselect plan is itself a sequential write+read pair.
|
|
1023
1024
|
*
|
|
@@ -1087,7 +1088,7 @@ export class TurbineClient {
|
|
|
1087
1088
|
return this.$transaction(fn, { sessionContext: context });
|
|
1088
1089
|
}
|
|
1089
1090
|
// -------------------------------------------------------------------------
|
|
1090
|
-
// LISTEN / NOTIFY
|
|
1091
|
+
// LISTEN / NOTIFY, Postgres realtime pub/sub
|
|
1091
1092
|
// -------------------------------------------------------------------------
|
|
1092
1093
|
/**
|
|
1093
1094
|
* Subscribe to a Postgres NOTIFY channel. The handler fires with each
|
|
@@ -1102,12 +1103,12 @@ export class TurbineClient {
|
|
|
1102
1103
|
*
|
|
1103
1104
|
* The channel name CANNOT be a bound parameter (`LISTEN $1` is a syntax
|
|
1104
1105
|
* error), so it is validated against a strict identifier regex AND quoted via
|
|
1105
|
-
* `quoteIdent` before interpolation
|
|
1106
|
+
* `quoteIdent` before interpolation, it is the only identifier this method
|
|
1106
1107
|
* places into SQL text.
|
|
1107
1108
|
*
|
|
1108
1109
|
* **Serverless caveat:** LISTEN needs a persistent connection that can push
|
|
1109
1110
|
* async notifications. Stateless HTTP drivers (Neon HTTP, Vercel Postgres)
|
|
1110
|
-
* cannot do this
|
|
1111
|
+
* cannot do this, `$listen` throws a `ConnectionError` rather than hang.
|
|
1111
1112
|
* `$notify` works on every driver.
|
|
1112
1113
|
*
|
|
1113
1114
|
* @example
|
|
@@ -1138,7 +1139,7 @@ export class TurbineClient {
|
|
|
1138
1139
|
/**
|
|
1139
1140
|
* Send a Postgres NOTIFY on `channel` with an optional payload string.
|
|
1140
1141
|
*
|
|
1141
|
-
* Issued as `SELECT pg_notify($1, $2)
|
|
1142
|
+
* Issued as `SELECT pg_notify($1, $2)`, both the channel and payload are
|
|
1142
1143
|
* BOUND parameters (no quoting/injection concern). The channel is still
|
|
1143
1144
|
* validated against the identifier regex for parity with `$listen` and to
|
|
1144
1145
|
* catch typos loudly. Works on every driver, including serverless HTTP pools.
|
|
@@ -1164,7 +1165,7 @@ export class TurbineClient {
|
|
|
1164
1165
|
}
|
|
1165
1166
|
}
|
|
1166
1167
|
// -------------------------------------------------------------------------
|
|
1167
|
-
// Retry
|
|
1168
|
+
// Retry, automatic retry for retryable errors (deadlock, serialization)
|
|
1168
1169
|
// -------------------------------------------------------------------------
|
|
1169
1170
|
/**
|
|
1170
1171
|
* Execute an async function with automatic retry on retryable errors.
|
|
@@ -1207,7 +1208,7 @@ export class TurbineClient {
|
|
|
1207
1208
|
* Gracefully shut down the connection pool.
|
|
1208
1209
|
*
|
|
1209
1210
|
* If Turbine was given an external pool via `TurbineConfig.pool`, this
|
|
1210
|
-
* method is a no-op
|
|
1211
|
+
* method is a no-op, the caller is responsible for the pool's lifecycle.
|
|
1211
1212
|
*/
|
|
1212
1213
|
async disconnect() {
|
|
1213
1214
|
// Tear down any live LISTEN subscriptions first. Each holds a dedicated
|
|
@@ -1226,7 +1227,7 @@ export class TurbineClient {
|
|
|
1226
1227
|
this.activeSubscriptions.clear();
|
|
1227
1228
|
}
|
|
1228
1229
|
// Close owned (string-configured) replica pools regardless of whether the
|
|
1229
|
-
// primary is owned
|
|
1230
|
+
// primary is owned, external replica pools are left untouched (caller owns
|
|
1230
1231
|
// their lifecycle), same contract as an external primary.
|
|
1231
1232
|
for (const replicaPool of this.ownedReplicaPools) {
|
|
1232
1233
|
try {
|
|
@@ -1240,7 +1241,7 @@ export class TurbineClient {
|
|
|
1240
1241
|
}
|
|
1241
1242
|
if (!this.ownsPool) {
|
|
1242
1243
|
if (this.logging) {
|
|
1243
|
-
console.log('[turbine] disconnect() skipped
|
|
1244
|
+
console.log('[turbine] disconnect() skipped, external primary pool is not owned by Turbine');
|
|
1244
1245
|
}
|
|
1245
1246
|
return;
|
|
1246
1247
|
}
|
package/dist/dialect.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm
|
|
2
|
+
* turbine-orm, SQL dialect contract
|
|
3
3
|
*
|
|
4
4
|
* Phase-1 seam for future database packages. The current package remains
|
|
5
5
|
* PostgreSQL-native by default, but query generation now depends on this
|
|
@@ -40,6 +40,28 @@ export interface BulkInsertStatementInput {
|
|
|
40
40
|
skipDuplicates?: boolean;
|
|
41
41
|
/** Optional SQL-ready RETURNING selection. */
|
|
42
42
|
returning?: ReturningSelection;
|
|
43
|
+
/**
|
|
44
|
+
* Force the row-major `VALUES (…), (…)` form even on a dialect that would
|
|
45
|
+
* otherwise batch column-major.
|
|
46
|
+
*
|
|
47
|
+
* Set when any target column is ARRAY-typed: PostgreSQL's `UNNEST` transpose
|
|
48
|
+
* flattens a nested array, so an array-valued column cannot survive it. The
|
|
49
|
+
* flag costs one placeholder per cell rather than per column, which is why
|
|
50
|
+
* it is opt-in rather than the default. Dialects that already emit `VALUES`
|
|
51
|
+
* ignore it.
|
|
52
|
+
*/
|
|
53
|
+
requireRowValues?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The cast/decode pair for one JSON-divergent column type. See
|
|
57
|
+
* {@link Dialect.jsonWireRule}. The two halves MUST agree: a cast with no
|
|
58
|
+
* matching decode silently returns text where a value is expected.
|
|
59
|
+
*/
|
|
60
|
+
export interface JsonWireRule {
|
|
61
|
+
/** The SQL expression to place in the JSON builder, given the column reference. */
|
|
62
|
+
sql(columnRef: string): string;
|
|
63
|
+
/** Turn the JSON-carried value back into the driver's representation. */
|
|
64
|
+
decode(value: unknown): unknown;
|
|
43
65
|
}
|
|
44
66
|
export interface BuiltStatement {
|
|
45
67
|
sql: string;
|
|
@@ -58,7 +80,7 @@ export interface UpsertStatementInput {
|
|
|
58
80
|
updateSetClauses: string[];
|
|
59
81
|
/**
|
|
60
82
|
* Optional SQL-ready predicate (no `WHERE` keyword) restricting the
|
|
61
|
-
* conflict-UPDATE to matching rows
|
|
83
|
+
* conflict-UPDATE to matching rows, used by global filters (soft-delete /
|
|
62
84
|
* multi-tenancy) so an upsert never resurrects/steals a row outside the
|
|
63
85
|
* filter. Only honored by dialects that set
|
|
64
86
|
* {@link Dialect.supportsUpsertUpdateWhere}; others must never receive it.
|
|
@@ -107,13 +129,13 @@ export interface CreateIndexStatementInput {
|
|
|
107
129
|
/**
|
|
108
130
|
* How a dialect surfaces the row(s) produced by an INSERT/UPDATE/DELETE/upsert.
|
|
109
131
|
*
|
|
110
|
-
* - `'returning'
|
|
132
|
+
* - `'returning'`, a trailing `RETURNING *` clause returns the affected rows
|
|
111
133
|
* in the same statement (PostgreSQL, SQLite ≥ 3.35). The executor reads them
|
|
112
134
|
* directly from the statement result.
|
|
113
|
-
* - `'output'
|
|
135
|
+
* - `'output'`, the statement itself emits the rows in a non-RETURNING shape
|
|
114
136
|
* (SQL Server `OUTPUT INSERTED.*`). Executed exactly like `'returning'`: the
|
|
115
137
|
* rows come back on the statement result.
|
|
116
|
-
* - `'reselect'
|
|
138
|
+
* - `'reselect'`, the engine cannot return rows from a write (MySQL). The
|
|
117
139
|
* executor runs the write, then issues a follow-up `SELECT` (by primary
|
|
118
140
|
* key / unique / where predicate) to fetch the affected row(s). The build
|
|
119
141
|
* method supplies the {@link DeferredQuery} `reselect` plan that owns the
|
|
@@ -144,7 +166,7 @@ export interface OpenStreamOptions {
|
|
|
144
166
|
ambientTransaction?: boolean;
|
|
145
167
|
}
|
|
146
168
|
/**
|
|
147
|
-
* Inputs for {@link Dialect.buildUpdateStatement}
|
|
169
|
+
* Inputs for {@link Dialect.buildUpdateStatement}, full UPDATE assembly. Used by
|
|
148
170
|
* engines whose returning shape is injected MID-statement rather than as a trailing
|
|
149
171
|
* clause (SQL Server `OUTPUT INSERTED.*` lands between `SET …` and `WHERE …`).
|
|
150
172
|
*/
|
|
@@ -159,7 +181,7 @@ export interface UpdateStatementInput {
|
|
|
159
181
|
returning?: ReturningSelection;
|
|
160
182
|
}
|
|
161
183
|
/**
|
|
162
|
-
* Inputs for {@link Dialect.buildDeleteStatement}
|
|
184
|
+
* Inputs for {@link Dialect.buildDeleteStatement}, full DELETE assembly. SQL Server
|
|
163
185
|
* injects `OUTPUT DELETED.*` between `DELETE FROM <t>` and `WHERE …`.
|
|
164
186
|
*/
|
|
165
187
|
export interface DeleteStatementInput {
|
|
@@ -171,10 +193,10 @@ export interface DeleteStatementInput {
|
|
|
171
193
|
returning?: ReturningSelection;
|
|
172
194
|
}
|
|
173
195
|
/**
|
|
174
|
-
* Inputs for {@link Dialect.buildLimitOffset}
|
|
196
|
+
* Inputs for {@link Dialect.buildLimitOffset}, the trailing pagination clause of an
|
|
175
197
|
* outer SELECT. PostgreSQL/MySQL/SQLite use `LIMIT x [OFFSET y]`; SQL Server has no
|
|
176
198
|
* `LIMIT` and uses `[ORDER BY …] OFFSET y ROWS [FETCH NEXT x ROWS ONLY]` (which
|
|
177
|
-
* requires an ORDER BY
|
|
199
|
+
* requires an ORDER BY, a stable default is injected when {@link hasOrderBy} is false).
|
|
178
200
|
*/
|
|
179
201
|
export interface LimitOffsetInput {
|
|
180
202
|
/** SQL-ready placeholder/literal for the row LIMIT, or undefined for no limit. */
|
|
@@ -190,13 +212,13 @@ export interface LimitOffsetInput {
|
|
|
190
212
|
* `json_agg(json_build_object(...))` (SQL Server's `FOR JSON PATH` expresses the
|
|
191
213
|
* object shape through the child SELECT's column ALIASES rather than an explicit
|
|
192
214
|
* `JSON_OBJECT`, so it cannot be assembled from {@link Dialect.buildJsonObject} /
|
|
193
|
-
* {@link Dialect.buildJsonArrayAgg} primitives
|
|
215
|
+
* {@link Dialect.buildJsonArrayAgg} primitives, see {@link Dialect.buildRelationSubquery}).
|
|
194
216
|
*
|
|
195
217
|
* The query builder pre-resolves the parts that are engine-independent (alias,
|
|
196
218
|
* select/omit-resolved columns, recursion + param threading) and hands them to the
|
|
197
219
|
* dialect. **Param-push ordering contract:** the override MUST push values to
|
|
198
220
|
* {@link params} in the same order the builder's collect path expects so the SQL
|
|
199
|
-
* cache and pipeline batching stay in sync
|
|
221
|
+
* cache and pipeline batching stay in sync -
|
|
200
222
|
* - to-many with `limit`/`orderBy` (the "wrap" path) and manyToMany:
|
|
201
223
|
* `buildWhere(...)` → push `limit` → `recurse(...)` for each nested relation;
|
|
202
224
|
* - everything else (to-one, unordered/unlimited to-many): `recurse(...)` for each
|
|
@@ -207,7 +229,7 @@ export interface RelationSubqueryContext {
|
|
|
207
229
|
relDef: RelationDef;
|
|
208
230
|
/** The `with` spec for this relation: `true`, or a {@link WithOptions} object. */
|
|
209
231
|
spec: true | WithOptions;
|
|
210
|
-
/** Shared parameter array
|
|
232
|
+
/** Shared parameter array, push BOUND values here in the order described above. */
|
|
211
233
|
params: unknown[];
|
|
212
234
|
/** Parent alias or table name (RAW identifier, not quoted) to correlate against. */
|
|
213
235
|
parentRef: string;
|
|
@@ -258,12 +280,12 @@ export interface Dialect {
|
|
|
258
280
|
/** Build a JSON object expression from output keys and SQL expressions. */
|
|
259
281
|
buildJsonObject(pairs: [key: string, expr: string][]): string;
|
|
260
282
|
/**
|
|
261
|
-
* Build a positional JSON ARRAY expression from ordered SQL expressions
|
|
283
|
+
* Build a positional JSON ARRAY expression from ordered SQL expressions -
|
|
262
284
|
* the key-less counterpart to {@link buildJsonObject} used by the opt-in
|
|
263
285
|
* `jsonEncoding: 'positional'` mode. Emitting `json_build_array(v1, v2, …)`
|
|
264
286
|
* instead of `json_build_object('k1', v1, …)` drops every repeated key name
|
|
265
287
|
* 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
|
|
288
|
+
* to keys via a build-time shape descriptor). Postgres-only in v1, other
|
|
267
289
|
* engines never reach this because the builder gates positional encoding to
|
|
268
290
|
* `dialect.name === 'postgresql'`, so the method is optional on the contract.
|
|
269
291
|
*/
|
|
@@ -303,7 +325,7 @@ export interface Dialect {
|
|
|
303
325
|
* (a predicate on the conflict-UPDATE, e.g. Postgres `ON CONFLICT … DO UPDATE
|
|
304
326
|
* SET … WHERE …`). Global filters only push a conflict-UPDATE predicate when
|
|
305
327
|
* this is true, so engines whose upsert cannot express one (MySQL
|
|
306
|
-
* `ON DUPLICATE KEY UPDATE`) never receive an orphaned parameter. Optional
|
|
328
|
+
* `ON DUPLICATE KEY UPDATE`) never receive an orphaned parameter. Optional -
|
|
307
329
|
* absent is treated as `false`.
|
|
308
330
|
*/
|
|
309
331
|
readonly supportsUpsertUpdateWhere?: boolean;
|
|
@@ -374,6 +396,28 @@ export interface Dialect {
|
|
|
374
396
|
buildJsonPathExtract(column: string, pathParamRef: string): string;
|
|
375
397
|
/** Build a correlation clause across single or composite keys. */
|
|
376
398
|
buildCorrelation(leftRef: string, leftColumns: string | string[], rightRef: string, rightColumns: string | string[]): string;
|
|
399
|
+
/**
|
|
400
|
+
* How a column whose JSON rendering DIVERGES from the driver's own value
|
|
401
|
+
* should be carried through a `with` subquery, or `undefined` when the
|
|
402
|
+
* column's JSON form already matches.
|
|
403
|
+
*
|
|
404
|
+
* A nested-relation subquery builds JSON (`json_build_object`,
|
|
405
|
+
* `json_group_array`, …) and the JSON number grammar is not the wire
|
|
406
|
+
* grammar: PostgreSQL renders `numeric` as a JSON number, SQLite renders a
|
|
407
|
+
* 64-bit INTEGER as one, and a JSON number is an IEEE double. So the same
|
|
408
|
+
* column read at top level and read through a relation came back as
|
|
409
|
+
* different values, LOSSILY past 2^53, and because `auto` picks between the
|
|
410
|
+
* join and batched strategies by row count, the same query could return
|
|
411
|
+
* either. The fix is to carry the value as text and decode it back with the
|
|
412
|
+
* engine's own rule, which is what this hook pairs up: `sql` must emit an
|
|
413
|
+
* expression the JSON builder can hold, and `decode` must turn that back
|
|
414
|
+
* into exactly what the driver would have produced.
|
|
415
|
+
*
|
|
416
|
+
* Omitting the hook keeps a dialect on the old behavior (no cast, no
|
|
417
|
+
* decode), which is correct for any engine whose JSON rendering agrees with
|
|
418
|
+
* its wire format.
|
|
419
|
+
*/
|
|
420
|
+
jsonWireRule?(columnType: string): JsonWireRule | undefined;
|
|
377
421
|
/** Optional type mapping hook for code generation/introspection. */
|
|
378
422
|
typeToTypeScript?(dialectType: string, nullable: boolean): string;
|
|
379
423
|
/**
|
|
@@ -469,7 +513,7 @@ export interface Dialect {
|
|
|
469
513
|
buildLimitOffset?(input: LimitOffsetInput): string;
|
|
470
514
|
/**
|
|
471
515
|
* Assemble a full UPDATE statement. Optional: omitted by engines whose returning
|
|
472
|
-
* shape is a trailing clause (PG/SQLite `RETURNING`, MySQL none)
|
|
516
|
+
* shape is a trailing clause (PG/SQLite `RETURNING`, MySQL none), the builder
|
|
473
517
|
* falls back to `UPDATE <t> SET <set> <where><returningClause>`. SQL Server
|
|
474
518
|
* implements this to inject `OUTPUT INSERTED.*` between `SET …` and `WHERE …`.
|
|
475
519
|
*/
|
|
@@ -483,7 +527,7 @@ export interface Dialect {
|
|
|
483
527
|
* OVERRIDE nested-relation subquery generation entirely. Optional: when a dialect
|
|
484
528
|
* omits it, the builder uses its native `json_agg(json_build_object(...))` path
|
|
485
529
|
* (PG) routed through {@link buildJsonObject} / {@link buildJsonArrayAgg} /
|
|
486
|
-
* {@link wrapJsonSubresult}
|
|
530
|
+
* {@link wrapJsonSubresult}, so MySQL and SQLite, which only swap those
|
|
487
531
|
* primitives, produce identical output and never define this hook. SQL Server
|
|
488
532
|
* defines it to emit `FOR JSON PATH` correlated subqueries (its JSON aggregate
|
|
489
533
|
* is expressed through the child SELECT's column aliases, which does not map onto
|
|
@@ -503,7 +547,7 @@ export interface IntrospectOptions {
|
|
|
503
547
|
}
|
|
504
548
|
/**
|
|
505
549
|
* @deprecated Migration locking is owned by the {@link DatabaseAdapter} seam
|
|
506
|
-
* (`src/adapters/index.ts
|
|
550
|
+
* (`src/adapters/index.ts`, `acquireLock`/`releaseLock`/`statementTimeout`),
|
|
507
551
|
* which is the single canonical locking seam. This interface was never
|
|
508
552
|
* implemented or wired and is retained only for type back-compat; new engines
|
|
509
553
|
* MUST provide a `DatabaseAdapter` instead. Will be removed in a future major.
|