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/cjs/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* turbine-orm
|
|
3
|
+
* turbine-orm, TurbineClient
|
|
4
4
|
*
|
|
5
5
|
* The main entry point for the Turbine TypeScript SDK.
|
|
6
6
|
* Manages connection pooling and provides typed table accessors.
|
|
@@ -94,12 +94,12 @@ const READ_OPERATIONS = new Set([
|
|
|
94
94
|
/**
|
|
95
95
|
* Internal marker on the config object that tells the `TurbineClient`
|
|
96
96
|
* constructor to build a lightweight "primary-only" view sharing an existing
|
|
97
|
-
* client's primary pool, dialect, query options, and middleware
|
|
97
|
+
* client's primary pool, dialect, query options, and middleware, instead of
|
|
98
98
|
* creating a fresh pool. Produced solely by `$primary()`; never public.
|
|
99
99
|
*/
|
|
100
100
|
const PRIMARY_VIEW = Symbol('turbine.primaryView');
|
|
101
101
|
// ---------------------------------------------------------------------------
|
|
102
|
-
// TransactionClient
|
|
102
|
+
// TransactionClient, provides typed table accessors within a transaction
|
|
103
103
|
// ---------------------------------------------------------------------------
|
|
104
104
|
/**
|
|
105
105
|
* A transaction-scoped client that provides the same table accessor API as TurbineClient.
|
|
@@ -114,7 +114,7 @@ class TransactionClient {
|
|
|
114
114
|
sourcePool;
|
|
115
115
|
tableCache = new Map();
|
|
116
116
|
savepointCounter = 0;
|
|
117
|
-
/** Active SQL dialect
|
|
117
|
+
/** Active SQL dialect, owns savepoint keywords and raw-SQL placeholders. */
|
|
118
118
|
dialect;
|
|
119
119
|
constructor(client, schema, middlewares, queryOptions,
|
|
120
120
|
/**
|
|
@@ -243,14 +243,14 @@ exports.TransactionClient = TransactionClient;
|
|
|
243
243
|
// TurbineClient
|
|
244
244
|
// ---------------------------------------------------------------------------
|
|
245
245
|
class TurbineClient {
|
|
246
|
-
/** The underlying pg.Pool
|
|
246
|
+
/** The underlying pg.Pool, exposed for escape hatches */
|
|
247
247
|
pool;
|
|
248
248
|
/** The schema metadata this client was built from */
|
|
249
249
|
schema;
|
|
250
250
|
static int8ParserRegistered = false;
|
|
251
251
|
static utcTimestampParserRegistered = false;
|
|
252
252
|
logging;
|
|
253
|
-
/** Active SQL dialect
|
|
253
|
+
/** Active SQL dialect, owns transaction keywords, set_config, raw-SQL placeholders, capability flags. */
|
|
254
254
|
dialect;
|
|
255
255
|
tableCache = new Map();
|
|
256
256
|
middlewares = [];
|
|
@@ -259,7 +259,7 @@ class TurbineClient {
|
|
|
259
259
|
errorMessagesSafe;
|
|
260
260
|
/** True when Turbine created the pool and is responsible for tearing it down */
|
|
261
261
|
ownsPool = true;
|
|
262
|
-
/** Active LISTEN subscriptions
|
|
262
|
+
/** Active LISTEN subscriptions, torn down on disconnect() so it never hangs */
|
|
263
263
|
activeSubscriptions = new Set();
|
|
264
264
|
/**
|
|
265
265
|
* Read-replica pools in round-robin order. Empty when no replicas are
|
|
@@ -355,7 +355,7 @@ class TurbineClient {
|
|
|
355
355
|
*/
|
|
356
356
|
// Only register the int8 parser when the PRIMARY pool is Turbine-owned.
|
|
357
357
|
// External pools (Neon HTTP, Vercel Postgres) may ship their own pg-types
|
|
358
|
-
// fork and rely on their own parser configuration
|
|
358
|
+
// fork and rely on their own parser configuration, registration is
|
|
359
359
|
// process-global, so flipping it because a string replica exists alongside
|
|
360
360
|
// an external primary would silently change the external primary's parsing
|
|
361
361
|
// too. String replicas configured next to an external primary therefore
|
|
@@ -372,8 +372,8 @@ class TurbineClient {
|
|
|
372
372
|
// Parse `timestamp` (OID 1114) as UTC instead of server-local time. The
|
|
373
373
|
// pg driver's default hands back a Date built in the process's local zone,
|
|
374
374
|
// so the same row yields a different instant per deployment region. The
|
|
375
|
-
// ORM convention (Prisma, Rails, Django)
|
|
376
|
-
// that round-trips what Postgres stores
|
|
375
|
+
// ORM convention (Prisma, Rails, Django), and the only interpretation
|
|
376
|
+
// that round-trips what Postgres stores, is UTC. Same ownership rule as
|
|
377
377
|
// the int8 parser: never mutate parser state on external pools.
|
|
378
378
|
if (ownsAnyPool && config.utcTimestamps !== false && !TurbineClient.utcTimestampParserRegistered) {
|
|
379
379
|
pg_1.default.types.setTypeParser(1114, (val) => new Date(`${val.replace(' ', 'T')}Z`));
|
|
@@ -389,6 +389,7 @@ class TurbineClient {
|
|
|
389
389
|
defaultLimit: config.defaultLimit,
|
|
390
390
|
warnOnUnlimited: config.warnOnUnlimited,
|
|
391
391
|
utcTimestamps: config.utcTimestamps,
|
|
392
|
+
scopedConnect: config.scopedConnect,
|
|
392
393
|
relationLoadStrategy: config.relationLoadStrategy,
|
|
393
394
|
stableRelationOrder: config.stableRelationOrder,
|
|
394
395
|
implicitPkOrdering: config.implicitPkOrdering,
|
|
@@ -420,17 +421,17 @@ class TurbineClient {
|
|
|
420
421
|
}
|
|
421
422
|
},
|
|
422
423
|
};
|
|
423
|
-
// Apply NotFoundError message redaction mode (default: safe
|
|
424
|
+
// Apply NotFoundError message redaction mode (default: safe, values are
|
|
424
425
|
// stripped from messages to avoid leaking PII into error logs).
|
|
425
426
|
if (config.errorMessages) {
|
|
426
427
|
(0, errors_js_1.setErrorMessageMode)(config.errorMessages);
|
|
427
428
|
}
|
|
428
429
|
if (config.pool) {
|
|
429
|
-
// External pool
|
|
430
|
+
// External pool, use directly. Turbine doesn't manage its lifecycle.
|
|
430
431
|
this.pool = config.pool;
|
|
431
432
|
this.ownsPool = false;
|
|
432
433
|
if (this.logging) {
|
|
433
|
-
console.log(`[turbine] Using external pool
|
|
434
|
+
console.log(`[turbine] Using external pool, ${Object.keys(schema.tables).length} tables`);
|
|
434
435
|
}
|
|
435
436
|
}
|
|
436
437
|
else {
|
|
@@ -474,7 +475,7 @@ class TurbineClient {
|
|
|
474
475
|
console.error('[turbine] Unexpected pool error:', err.message);
|
|
475
476
|
});
|
|
476
477
|
if (this.logging) {
|
|
477
|
-
console.log(`[turbine] Pool created
|
|
478
|
+
console.log(`[turbine] Pool created, max ${poolConfig.max} connections, ${Object.keys(schema.tables).length} tables`);
|
|
478
479
|
}
|
|
479
480
|
}
|
|
480
481
|
// Build read-replica pools (if any). String entries become owned pg.Pools
|
|
@@ -522,14 +523,14 @@ class TurbineClient {
|
|
|
522
523
|
}
|
|
523
524
|
}
|
|
524
525
|
// -------------------------------------------------------------------------
|
|
525
|
-
// Middleware
|
|
526
|
+
// Middleware, intercept all queries
|
|
526
527
|
// -------------------------------------------------------------------------
|
|
527
528
|
/**
|
|
528
529
|
* Register a middleware function that runs around every query.
|
|
529
530
|
*
|
|
530
531
|
* Middleware can inspect and log query parameters, measure timing, and
|
|
531
532
|
* transform the result returned by `next()`. Note: query SQL is generated
|
|
532
|
-
* BEFORE middleware runs
|
|
533
|
+
* BEFORE middleware runs, `params.args` is a read-only snapshot, and
|
|
533
534
|
* mutating it does NOT change the executed SQL. Cross-cutting filters
|
|
534
535
|
* (e.g. soft deletes) belong in the query itself: pass an explicit
|
|
535
536
|
* `where: { deletedAt: null }` or wrap the table accessor in a small helper.
|
|
@@ -544,7 +545,7 @@ class TurbineClient {
|
|
|
544
545
|
* return result;
|
|
545
546
|
* });
|
|
546
547
|
*
|
|
547
|
-
* // Result transformation middleware
|
|
548
|
+
* // Result transformation middleware, redact a field on the way out
|
|
548
549
|
* db.$use(async (params, next) => {
|
|
549
550
|
* const result = await next(params);
|
|
550
551
|
* if (params.model === 'users' && Array.isArray(result)) {
|
|
@@ -566,7 +567,7 @@ class TurbineClient {
|
|
|
566
567
|
cache.clear();
|
|
567
568
|
}
|
|
568
569
|
// -------------------------------------------------------------------------
|
|
569
|
-
// Event emitter
|
|
570
|
+
// Event emitter, subscribe to query lifecycle events
|
|
570
571
|
// -------------------------------------------------------------------------
|
|
571
572
|
$on(_event, listener) {
|
|
572
573
|
this.queryListeners.add(listener);
|
|
@@ -575,7 +576,7 @@ class TurbineClient {
|
|
|
575
576
|
this.queryListeners.delete(listener);
|
|
576
577
|
}
|
|
577
578
|
// -------------------------------------------------------------------------
|
|
578
|
-
// Observability
|
|
579
|
+
// Observability, automatic metrics collection
|
|
579
580
|
// -------------------------------------------------------------------------
|
|
580
581
|
observeEngine;
|
|
581
582
|
async $observe(config) {
|
|
@@ -597,16 +598,16 @@ class TurbineClient {
|
|
|
597
598
|
};
|
|
598
599
|
}
|
|
599
600
|
// -------------------------------------------------------------------------
|
|
600
|
-
// Table accessor
|
|
601
|
+
// Table accessor, creates QueryInterface for any table
|
|
601
602
|
// -------------------------------------------------------------------------
|
|
602
603
|
/**
|
|
603
604
|
* Get a QueryInterface for a table.
|
|
604
|
-
* Results are cached
|
|
605
|
+
* Results are cached, calling `table('users')` twice returns the same instance.
|
|
605
606
|
*
|
|
606
607
|
* When read replicas are configured, this returns a thin routing proxy: the
|
|
607
608
|
* read-only operations in {@link READ_OPERATIONS} are dispatched to a
|
|
608
|
-
* round-robin replica-bound QueryInterface (so an entire read
|
|
609
|
-
* any batched sub-queries
|
|
609
|
+
* round-robin replica-bound QueryInterface (so an entire read, base rows and
|
|
610
|
+
* any batched sub-queries, runs against a single consistent replica), while
|
|
610
611
|
* writes and every other member fall through to the primary-bound instance.
|
|
611
612
|
* With no replicas the original single-pool instance is returned directly.
|
|
612
613
|
*/
|
|
@@ -681,7 +682,7 @@ class TurbineClient {
|
|
|
681
682
|
});
|
|
682
683
|
}
|
|
683
684
|
/**
|
|
684
|
-
* Return a view of this client that pins EVERY operation
|
|
685
|
+
* Return a view of this client that pins EVERY operation, reads included -
|
|
685
686
|
* to the primary pool, bypassing replica routing. Use it to read your own
|
|
686
687
|
* write without replication lag, or for any read that must see the latest
|
|
687
688
|
* committed data.
|
|
@@ -689,7 +690,7 @@ class TurbineClient {
|
|
|
689
690
|
* The view shares the primary pool, schema, dialect, query options, and
|
|
690
691
|
* middleware; it owns nothing, so its `disconnect()` is a no-op. When no
|
|
691
692
|
* replicas are configured this simply returns the client itself (already
|
|
692
|
-
* primary-only). The view is cached
|
|
693
|
+
* primary-only). The view is cached, repeated calls return the same instance.
|
|
693
694
|
*
|
|
694
695
|
* @example
|
|
695
696
|
* ```ts
|
|
@@ -707,14 +708,14 @@ class TurbineClient {
|
|
|
707
708
|
return this.primaryView;
|
|
708
709
|
}
|
|
709
710
|
// -------------------------------------------------------------------------
|
|
710
|
-
// Pipeline
|
|
711
|
+
// Pipeline, batch multiple queries into one round-trip
|
|
711
712
|
// -------------------------------------------------------------------------
|
|
712
713
|
/**
|
|
713
714
|
* Execute multiple queries in a single database round-trip.
|
|
714
715
|
*
|
|
715
716
|
* Two call styles:
|
|
716
|
-
* - `db.pipeline(q1, q2, q3)
|
|
717
|
-
* - `db.pipeline([q1, q2, q3], { transactional: false })
|
|
717
|
+
* - `db.pipeline(q1, q2, q3)`, rest params (backward-compatible)
|
|
718
|
+
* - `db.pipeline([q1, q2, q3], { transactional: false })`, array + options
|
|
718
719
|
*
|
|
719
720
|
* On pg.Pool-backed connections with TCP, this uses the real Postgres
|
|
720
721
|
* extended-query pipeline protocol (one TCP flush, one round-trip).
|
|
@@ -736,7 +737,7 @@ class TurbineClient {
|
|
|
736
737
|
queries = args;
|
|
737
738
|
}
|
|
738
739
|
if (this.logging) {
|
|
739
|
-
console.log(`[turbine] Pipeline: ${queries.length} queries
|
|
740
|
+
console.log(`[turbine] Pipeline: ${queries.length} queries, ${queries.map((q) => q.tag).join(', ')}`);
|
|
740
741
|
}
|
|
741
742
|
return (0, pipeline_js_1.executePipeline)(this.pool, queries, options);
|
|
742
743
|
}
|
|
@@ -749,7 +750,7 @@ class TurbineClient {
|
|
|
749
750
|
return (0, pipeline_js_1.pipelineSupported)(this.pool);
|
|
750
751
|
}
|
|
751
752
|
// -------------------------------------------------------------------------
|
|
752
|
-
// Raw SQL
|
|
753
|
+
// Raw SQL, tagged template literal escape hatch
|
|
753
754
|
// -------------------------------------------------------------------------
|
|
754
755
|
/**
|
|
755
756
|
* Execute a raw SQL query with parameter interpolation via tagged templates.
|
|
@@ -783,7 +784,7 @@ class TurbineClient {
|
|
|
783
784
|
}
|
|
784
785
|
}
|
|
785
786
|
/**
|
|
786
|
-
* Execute a **typed** raw SQL query
|
|
787
|
+
* Execute a **typed** raw SQL query, Turbine's answer to Prisma's TypedSQL.
|
|
787
788
|
*
|
|
788
789
|
* Like {@link raw}, every interpolated `${value}` becomes a `$N` parameter
|
|
789
790
|
* (never string-concatenated), so it is injection-safe by construction. The
|
|
@@ -791,7 +792,7 @@ class TurbineClient {
|
|
|
791
792
|
* returned {@link TypedSqlQuery} can be `await`ed directly for `T[]`, or
|
|
792
793
|
* refined with `.one()` (→ `T | null`) or `.scalar<V>()` (→ `V | null`).
|
|
793
794
|
*
|
|
794
|
-
* Rows are returned as-is
|
|
795
|
+
* Rows are returned as-is, no snake→camel mapping (matching `raw()`). Alias
|
|
795
796
|
* columns in SQL if you want camelCase keys.
|
|
796
797
|
*
|
|
797
798
|
* @example
|
|
@@ -817,7 +818,7 @@ class TurbineClient {
|
|
|
817
818
|
return new typed_sql_js_1.TypedSqlQuery(this.pool, sql, params, this.logging);
|
|
818
819
|
}
|
|
819
820
|
// -------------------------------------------------------------------------
|
|
820
|
-
// Transaction support (raw
|
|
821
|
+
// Transaction support (raw, legacy)
|
|
821
822
|
// -------------------------------------------------------------------------
|
|
822
823
|
/**
|
|
823
824
|
* Execute a function within a database transaction (raw pg.PoolClient).
|
|
@@ -836,7 +837,7 @@ class TurbineClient {
|
|
|
836
837
|
* Only true once BEGIN has actually succeeded. If BEGIN itself throws
|
|
837
838
|
* (e.g. a single-writer engine's transaction gate times out or rejects a
|
|
838
839
|
* re-entrant begin), issuing a "best-effort" ROLLBACK would be a stray
|
|
839
|
-
* statement from a context that never opened a transaction
|
|
840
|
+
* statement from a context that never opened a transaction, on a driver
|
|
840
841
|
* with one shared engine handle (PowDB embedded) it would roll back a
|
|
841
842
|
* DIFFERENT caller's open transaction.
|
|
842
843
|
*/
|
|
@@ -859,7 +860,7 @@ class TurbineClient {
|
|
|
859
860
|
await client.query(this.dialect.rollbackStatement());
|
|
860
861
|
}
|
|
861
862
|
catch {
|
|
862
|
-
// Best-effort rollback
|
|
863
|
+
// Best-effort rollback, the connection may have died mid-query.
|
|
863
864
|
}
|
|
864
865
|
}
|
|
865
866
|
throw err;
|
|
@@ -891,21 +892,21 @@ class TurbineClient {
|
|
|
891
892
|
client.release(err);
|
|
892
893
|
}
|
|
893
894
|
catch {
|
|
894
|
-
// pg may throw if the client is already released
|
|
895
|
+
// pg may throw if the client is already released, swallow.
|
|
895
896
|
}
|
|
896
897
|
};
|
|
897
898
|
let timedOut = false;
|
|
898
899
|
/**
|
|
899
|
-
* Only true once BEGIN has actually succeeded. If BEGIN itself throws
|
|
900
|
+
* Only true once BEGIN has actually succeeded. If BEGIN itself throws -
|
|
900
901
|
* e.g. a single-writer engine's transaction gate times out in its FIFO
|
|
901
|
-
* queue or rejects a re-entrant begin (PowDB, E002/E017)
|
|
902
|
+
* queue or rejects a re-entrant begin (PowDB, E002/E017), this context
|
|
902
903
|
* never opened a transaction, so the catch below must NOT issue its
|
|
903
904
|
* best-effort ROLLBACK: on a driver with one shared engine handle that
|
|
904
905
|
* stray ROLLBACK would tear down a DIFFERENT caller's open transaction.
|
|
905
906
|
*/
|
|
906
907
|
let began = false;
|
|
907
908
|
try {
|
|
908
|
-
// BEGIN with optional isolation level
|
|
909
|
+
// BEGIN with optional isolation level, the dialect owns the keyword and
|
|
909
910
|
// BEGIN+isolation composition (Postgres appends ` ISOLATION LEVEL …`).
|
|
910
911
|
const isolationSql = options?.isolationLevel ? ISOLATION_LEVELS[options.isolationLevel] : undefined;
|
|
911
912
|
await client.query(this.dialect.beginStatement(isolationSql));
|
|
@@ -914,15 +915,15 @@ class TurbineClient {
|
|
|
914
915
|
// Order matters: BEGIN -> isolation level (above) -> set_config loop ->
|
|
915
916
|
// user fn. Any error here propagates to the catch below and rolls back
|
|
916
917
|
// like any other transaction failure. We use set_config(name, value,
|
|
917
|
-
// is_local=true)
|
|
918
|
-
// SET LOCAL
|
|
918
|
+
// is_local=true), the parameterizable, transaction-scoped equivalent of
|
|
919
|
+
// SET LOCAL, so both name and value are BOUND params, never interpolated.
|
|
919
920
|
if (options?.sessionContext) {
|
|
920
921
|
if (!this.dialect.supportsRLS) {
|
|
921
922
|
throw new errors_js_1.UnsupportedFeatureError('sessionContext (RLS session GUCs)', this.dialect.name, 'set_config-based row-level-security context requires PostgreSQL.');
|
|
922
923
|
}
|
|
923
924
|
for (const [name, value] of Object.entries(options.sessionContext)) {
|
|
924
925
|
if (!GUC_NAME_REGEX.test(name)) {
|
|
925
|
-
throw new errors_js_1.ValidationError(`[turbine] Invalid session-context GUC name "${name}"
|
|
926
|
+
throw new errors_js_1.ValidationError(`[turbine] Invalid session-context GUC name "${name}", must match ` +
|
|
926
927
|
'/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)?$/ (optionally namespaced, e.g. "app.current_tenant")');
|
|
927
928
|
}
|
|
928
929
|
const cfg = this.dialect.buildSetSessionConfig(name, String(value));
|
|
@@ -952,7 +953,7 @@ class TurbineClient {
|
|
|
952
953
|
const runCallback = () => (wrap ? wrap.call(client, () => fn(tx)) : fn(tx));
|
|
953
954
|
if (timeout) {
|
|
954
955
|
// Race between the function and a timeout. If the timeout fires we
|
|
955
|
-
// need to actually abort the in-flight query
|
|
956
|
+
// need to actually abort the in-flight query, otherwise the backend
|
|
956
957
|
// keeps running until pg's own timeout, holding a pool slot the whole
|
|
957
958
|
// time. The simplest reliable cancellation is to destroy the
|
|
958
959
|
// connection: passing a truthy argument to client.release() tells the
|
|
@@ -966,7 +967,7 @@ class TurbineClient {
|
|
|
966
967
|
// Destroy the connection to abort the in-flight backend query.
|
|
967
968
|
// We do this BEFORE rejecting so the socket is gone by the time
|
|
968
969
|
// the caller's catch block runs.
|
|
969
|
-
releaseOnce(new Error('[turbine] Transaction timeout
|
|
970
|
+
releaseOnce(new Error('[turbine] Transaction timeout, connection destroyed'));
|
|
970
971
|
reject(new errors_js_1.TimeoutError(timeout, 'Transaction'));
|
|
971
972
|
}, timeout);
|
|
972
973
|
});
|
|
@@ -987,11 +988,11 @@ class TurbineClient {
|
|
|
987
988
|
return result;
|
|
988
989
|
}
|
|
989
990
|
catch (err) {
|
|
990
|
-
// If the timeout fired we already destroyed the connection
|
|
991
|
+
// If the timeout fired we already destroyed the connection, issuing a
|
|
991
992
|
// ROLLBACK on a released client would throw "Client has already been
|
|
992
993
|
// released". Skip the rollback in that case (the backend rolled back
|
|
993
994
|
// when its socket was closed). Likewise skip it when BEGIN never
|
|
994
|
-
// succeeded (`began` false)
|
|
995
|
+
// succeeded (`began` false), there is no transaction to roll back and
|
|
995
996
|
// the stray statement could hit another caller's transaction on a
|
|
996
997
|
// shared-handle engine.
|
|
997
998
|
if (began && !timedOut && !released) {
|
|
@@ -999,7 +1000,7 @@ class TurbineClient {
|
|
|
999
1000
|
await client.query(this.dialect.rollbackStatement());
|
|
1000
1001
|
}
|
|
1001
1002
|
catch {
|
|
1002
|
-
// Best-effort rollback
|
|
1003
|
+
// Best-effort rollback, the connection may have died mid-query.
|
|
1003
1004
|
}
|
|
1004
1005
|
}
|
|
1005
1006
|
if (this.logging) {
|
|
@@ -1025,7 +1026,7 @@ class TurbineClient {
|
|
|
1025
1026
|
* {@link PgCompatPoolClient.supportsPipelining} (its `query()` accepts
|
|
1026
1027
|
* concurrent calls and completes them in FIFO submission order), all
|
|
1027
1028
|
* statements are dispatched in one write burst and the replies are
|
|
1028
|
-
* collected in order
|
|
1029
|
+
* collected in order, ~1 round trip plus server time. Only taken when
|
|
1029
1030
|
* the dialect's writes surface rows directly (`resultStrategy` !==
|
|
1030
1031
|
* 'reselect'): a reselect plan is itself a sequential write+read pair.
|
|
1031
1032
|
*
|
|
@@ -1095,7 +1096,7 @@ class TurbineClient {
|
|
|
1095
1096
|
return this.$transaction(fn, { sessionContext: context });
|
|
1096
1097
|
}
|
|
1097
1098
|
// -------------------------------------------------------------------------
|
|
1098
|
-
// LISTEN / NOTIFY
|
|
1099
|
+
// LISTEN / NOTIFY, Postgres realtime pub/sub
|
|
1099
1100
|
// -------------------------------------------------------------------------
|
|
1100
1101
|
/**
|
|
1101
1102
|
* Subscribe to a Postgres NOTIFY channel. The handler fires with each
|
|
@@ -1110,12 +1111,12 @@ class TurbineClient {
|
|
|
1110
1111
|
*
|
|
1111
1112
|
* The channel name CANNOT be a bound parameter (`LISTEN $1` is a syntax
|
|
1112
1113
|
* error), so it is validated against a strict identifier regex AND quoted via
|
|
1113
|
-
* `quoteIdent` before interpolation
|
|
1114
|
+
* `quoteIdent` before interpolation, it is the only identifier this method
|
|
1114
1115
|
* places into SQL text.
|
|
1115
1116
|
*
|
|
1116
1117
|
* **Serverless caveat:** LISTEN needs a persistent connection that can push
|
|
1117
1118
|
* async notifications. Stateless HTTP drivers (Neon HTTP, Vercel Postgres)
|
|
1118
|
-
* cannot do this
|
|
1119
|
+
* cannot do this, `$listen` throws a `ConnectionError` rather than hang.
|
|
1119
1120
|
* `$notify` works on every driver.
|
|
1120
1121
|
*
|
|
1121
1122
|
* @example
|
|
@@ -1146,7 +1147,7 @@ class TurbineClient {
|
|
|
1146
1147
|
/**
|
|
1147
1148
|
* Send a Postgres NOTIFY on `channel` with an optional payload string.
|
|
1148
1149
|
*
|
|
1149
|
-
* Issued as `SELECT pg_notify($1, $2)
|
|
1150
|
+
* Issued as `SELECT pg_notify($1, $2)`, both the channel and payload are
|
|
1150
1151
|
* BOUND parameters (no quoting/injection concern). The channel is still
|
|
1151
1152
|
* validated against the identifier regex for parity with `$listen` and to
|
|
1152
1153
|
* catch typos loudly. Works on every driver, including serverless HTTP pools.
|
|
@@ -1172,7 +1173,7 @@ class TurbineClient {
|
|
|
1172
1173
|
}
|
|
1173
1174
|
}
|
|
1174
1175
|
// -------------------------------------------------------------------------
|
|
1175
|
-
// Retry
|
|
1176
|
+
// Retry, automatic retry for retryable errors (deadlock, serialization)
|
|
1176
1177
|
// -------------------------------------------------------------------------
|
|
1177
1178
|
/**
|
|
1178
1179
|
* Execute an async function with automatic retry on retryable errors.
|
|
@@ -1215,7 +1216,7 @@ class TurbineClient {
|
|
|
1215
1216
|
* Gracefully shut down the connection pool.
|
|
1216
1217
|
*
|
|
1217
1218
|
* If Turbine was given an external pool via `TurbineConfig.pool`, this
|
|
1218
|
-
* method is a no-op
|
|
1219
|
+
* method is a no-op, the caller is responsible for the pool's lifecycle.
|
|
1219
1220
|
*/
|
|
1220
1221
|
async disconnect() {
|
|
1221
1222
|
// Tear down any live LISTEN subscriptions first. Each holds a dedicated
|
|
@@ -1234,7 +1235,7 @@ class TurbineClient {
|
|
|
1234
1235
|
this.activeSubscriptions.clear();
|
|
1235
1236
|
}
|
|
1236
1237
|
// Close owned (string-configured) replica pools regardless of whether the
|
|
1237
|
-
// primary is owned
|
|
1238
|
+
// primary is owned, external replica pools are left untouched (caller owns
|
|
1238
1239
|
// their lifecycle), same contract as an external primary.
|
|
1239
1240
|
for (const replicaPool of this.ownedReplicaPools) {
|
|
1240
1241
|
try {
|
|
@@ -1248,7 +1249,7 @@ class TurbineClient {
|
|
|
1248
1249
|
}
|
|
1249
1250
|
if (!this.ownsPool) {
|
|
1250
1251
|
if (this.logging) {
|
|
1251
|
-
console.log('[turbine] disconnect() skipped
|
|
1252
|
+
console.log('[turbine] disconnect() skipped, external primary pool is not owned by Turbine');
|
|
1252
1253
|
}
|
|
1253
1254
|
return;
|
|
1254
1255
|
}
|
package/dist/cjs/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.
|