turbine-orm 0.55.0 → 0.57.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 +16 -1
- package/dist/cjs/cli/index.d.ts +3 -1
- package/dist/cjs/cli/index.js +341 -14
- package/dist/cjs/client.d.ts +16 -1
- package/dist/cjs/client.js +7 -39
- package/dist/cjs/dialect.d.ts +9 -0
- package/dist/cjs/index-stats.d.ts +46 -0
- package/dist/cjs/index-stats.js +42 -1
- package/dist/cjs/plan-divergence.d.ts +511 -0
- package/dist/cjs/plan-divergence.js +790 -0
- package/dist/cjs/powql.d.ts +11 -0
- package/dist/cjs/powql.js +22 -0
- package/dist/cjs/prisma-compat.d.ts +32 -1
- package/dist/cjs/prisma-compat.js +297 -41
- package/dist/cjs/query/builder.d.ts +45 -0
- package/dist/cjs/query/builder.js +90 -17
- package/dist/cjs/query/deferred.d.ts +9 -0
- package/dist/cjs/query/index.d.ts +2 -0
- package/dist/cjs/query/index.js +18 -1
- package/dist/cjs/query/option-surface.d.ts +100 -0
- package/dist/cjs/query/option-surface.js +214 -0
- package/dist/cjs/query/types.d.ts +140 -0
- package/dist/cjs/query/utils.d.ts +30 -0
- package/dist/cjs/query/utils.js +67 -3
- package/dist/cjs/query/warn-registry.d.ts +8 -0
- package/dist/cjs/query/warn-registry.js +8 -0
- package/dist/cli/index.d.ts +3 -1
- package/dist/cli/index.js +341 -14
- package/dist/client.d.ts +16 -1
- package/dist/client.js +8 -40
- package/dist/dialect.d.ts +9 -0
- package/dist/index-stats.d.ts +46 -0
- package/dist/index-stats.js +42 -1
- package/dist/plan-divergence.d.ts +511 -0
- package/dist/plan-divergence.js +783 -0
- package/dist/powql.d.ts +11 -0
- package/dist/powql.js +22 -0
- package/dist/prisma-compat.d.ts +32 -1
- package/dist/prisma-compat.js +297 -41
- package/dist/query/builder.d.ts +45 -0
- package/dist/query/builder.js +90 -17
- package/dist/query/deferred.d.ts +9 -0
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +1 -0
- package/dist/query/option-surface.d.ts +100 -0
- package/dist/query/option-surface.js +209 -0
- package/dist/query/types.d.ts +140 -0
- package/dist/query/utils.d.ts +30 -0
- package/dist/query/utils.js +66 -3
- package/dist/query/warn-registry.d.ts +8 -0
- package/dist/query/warn-registry.js +8 -0
- package/package.json +1 -1
|
@@ -712,6 +712,51 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
|
|
|
712
712
|
*/
|
|
713
713
|
resetUnlimitedWarnings(): void;
|
|
714
714
|
private emitQueryEvent;
|
|
715
|
+
/**
|
|
716
|
+
* Resolve the prepared-statement name a read should execute under, honouring
|
|
717
|
+
* the per-query {@link FindManyArgs.forceCustomPlan} opt-in.
|
|
718
|
+
*
|
|
719
|
+
* `forceCustomPlan: true` returns `undefined`, which sends the statement
|
|
720
|
+
* UNNAMED. The mechanism is NOT "PostgreSQL treats an unnamed statement as a
|
|
721
|
+
* one-shot plan that never enters the plan cache": the backend builds and
|
|
722
|
+
* saves a `CachedPlanSource` for the unnamed statement too. It works because
|
|
723
|
+
* node-postgres only skips Parse for a statement it has already parsed BY
|
|
724
|
+
* NAME (`Query.hasBeenParsed` is `this.name && connection.parsedStatements[this.name]`),
|
|
725
|
+
* so an unnamed statement is re-Parsed on every execution, each Parse
|
|
726
|
+
* replaces the unnamed cached plan source with a fresh one whose custom-plan
|
|
727
|
+
* counter is zero, and the five-execution threshold that precedes promotion
|
|
728
|
+
* is never reached. Every execution is therefore planned with the real
|
|
729
|
+
* parameter values.
|
|
730
|
+
*
|
|
731
|
+
* No GUC is set, no `SET LOCAL` is emitted, no transaction is opened, and no
|
|
732
|
+
* extra round trip is added, which is exactly why the opt-in can be per query
|
|
733
|
+
* while the client-level `planCacheMode` (a connection parameter) cannot be.
|
|
734
|
+
*
|
|
735
|
+
* The refusal is deliberately here, at the one seam every read execution
|
|
736
|
+
* passes through, rather than in each build method: the flag changes NOTHING
|
|
737
|
+
* about the SQL text, so a build-time check would have had to be repeated in
|
|
738
|
+
* every builder and could still be bypassed by a hand-executed
|
|
739
|
+
* `DeferredQuery`.
|
|
740
|
+
*
|
|
741
|
+
* Engines whose dialect does not report {@link Dialect.supportsPlanCacheMode}
|
|
742
|
+
* throw {@link UnsupportedFeatureError} (E017): the flag names a PostgreSQL
|
|
743
|
+
* plan-cache guarantee, and an engine with no such cache cannot make it.
|
|
744
|
+
* The same flag left unset (or `false`) is accepted everywhere.
|
|
745
|
+
*
|
|
746
|
+
* THE ONE COMBINATION THAT IS REFUSED RATHER THAN HONOURED. A client-level
|
|
747
|
+
* `planCacheMode: 'force_generic_plan'` DEFEATS this option, and that was
|
|
748
|
+
* MEASURED rather than reasoned about: on PostgreSQL 16.14, five executions
|
|
749
|
+
* of one unnamed statement read 19,107 buffers with that setting in force and
|
|
750
|
+
* 55 buffers with the same connection set back to `auto`, against 19,107 for
|
|
751
|
+
* the named statement. So the setting governs the unnamed statement too, and
|
|
752
|
+
* withholding the name buys nothing against it. Accepting the flag there
|
|
753
|
+
* would report a guarantee the very next execution breaks, so the
|
|
754
|
+
* contradiction throws {@link ValidationError} (E003) naming both settings.
|
|
755
|
+
* Turbine can only see the setting IT applied: a `plan_cache_mode` installed
|
|
756
|
+
* by the caller's own `SET`, by `ALTER ROLE`, or by a pooler is invisible
|
|
757
|
+
* here and is not refused.
|
|
758
|
+
*/
|
|
759
|
+
private preparedNameFor;
|
|
715
760
|
/**
|
|
716
761
|
* Execute a pool.query with an optional timeout.
|
|
717
762
|
* If timeout is set, races the query against a timer and rejects on expiry.
|
|
@@ -1388,11 +1388,11 @@ class QueryInterface {
|
|
|
1388
1388
|
const deferred = single
|
|
1389
1389
|
? this.buildFindUnique(baseArgs)
|
|
1390
1390
|
: this.buildFindMany(baseArgs);
|
|
1391
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, deferred.preparedName);
|
|
1391
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
1392
1392
|
const rows = deferred.transform(result);
|
|
1393
1393
|
const entities = single ? (rows ? [rows] : []) : rows;
|
|
1394
1394
|
if (entities.length > 0) {
|
|
1395
|
-
await (0, batched_loader_js_1.loadRelationsBatched)(this.batchedContext(args.timeout, skip, args.includePii === true), entities, batchedWith, args.timeout);
|
|
1395
|
+
await (0, batched_loader_js_1.loadRelationsBatched)(this.batchedContext(args.timeout, skip, args.includePii === true, args.forceCustomPlan === true), entities, batchedWith, args.timeout);
|
|
1396
1396
|
}
|
|
1397
1397
|
(0, batched_loader_js_1.stripFields)(entities, proj.strip);
|
|
1398
1398
|
return single ? (entities[0] ?? null) : entities;
|
|
@@ -1408,7 +1408,7 @@ class QueryInterface {
|
|
|
1408
1408
|
* and unlimited-warnings silenced, a relation load must fetch every matching
|
|
1409
1409
|
* child, and the per-relation `limit` is applied client-side by the loader.
|
|
1410
1410
|
*/
|
|
1411
|
-
batchedContext(timeout, skip, includePii) {
|
|
1411
|
+
batchedContext(timeout, skip, includePii, forceCustomPlan = false) {
|
|
1412
1412
|
const childOptions = {
|
|
1413
1413
|
...this.options,
|
|
1414
1414
|
defaultLimit: undefined,
|
|
@@ -1418,7 +1418,11 @@ class QueryInterface {
|
|
|
1418
1418
|
parentMeta: this.tableMeta,
|
|
1419
1419
|
schema: this.schema,
|
|
1420
1420
|
makeChild: (table) => new QueryInterface(this.pool, table, this.schema, [], childOptions),
|
|
1421
|
-
|
|
1421
|
+
// The per-query `forceCustomPlan` opt-in covers the relation follow-ups
|
|
1422
|
+
// too: a batched load re-issues the SAME tenant-shaped predicate one
|
|
1423
|
+
// level down, so leaving those named would keep exactly the plan-cache
|
|
1424
|
+
// exposure the caller asked to be rid of.
|
|
1425
|
+
exec: (sql, params, preparedName) => this.queryWithTimeout(sql, params, timeout, this.preparedNameFor({ forceCustomPlan }, preparedName)),
|
|
1422
1426
|
quote: (name) => this.q(name),
|
|
1423
1427
|
buildInClause: (expr, paramRef, negated) => this.inClause(expr, paramRef, negated),
|
|
1424
1428
|
inClauseParam: (values) => this.inParam(values),
|
|
@@ -1470,10 +1474,10 @@ class QueryInterface {
|
|
|
1470
1474
|
const { baseArgs, strip } = this.prepareBatchedBase(args, withClause);
|
|
1471
1475
|
// baseArgs.with is always undefined here; the cast just bridges the R generic.
|
|
1472
1476
|
const deferred = this.buildFindMany(baseArgs);
|
|
1473
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, deferred.preparedName);
|
|
1477
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
1474
1478
|
const entities = deferred.transform(result);
|
|
1475
1479
|
if (entities.length > 0) {
|
|
1476
|
-
await (0, batched_loader_js_1.loadRelationsBatched)(this.batchedContext(args.timeout, skip, args.includePii === true), entities, withClause, args.timeout);
|
|
1480
|
+
await (0, batched_loader_js_1.loadRelationsBatched)(this.batchedContext(args.timeout, skip, args.includePii === true, args.forceCustomPlan === true), entities, withClause, args.timeout);
|
|
1477
1481
|
}
|
|
1478
1482
|
(0, batched_loader_js_1.stripFields)(entities, strip);
|
|
1479
1483
|
return entities;
|
|
@@ -1629,6 +1633,69 @@ class QueryInterface {
|
|
|
1629
1633
|
// Listener errors must never crash a query
|
|
1630
1634
|
}
|
|
1631
1635
|
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Resolve the prepared-statement name a read should execute under, honouring
|
|
1638
|
+
* the per-query {@link FindManyArgs.forceCustomPlan} opt-in.
|
|
1639
|
+
*
|
|
1640
|
+
* `forceCustomPlan: true` returns `undefined`, which sends the statement
|
|
1641
|
+
* UNNAMED. The mechanism is NOT "PostgreSQL treats an unnamed statement as a
|
|
1642
|
+
* one-shot plan that never enters the plan cache": the backend builds and
|
|
1643
|
+
* saves a `CachedPlanSource` for the unnamed statement too. It works because
|
|
1644
|
+
* node-postgres only skips Parse for a statement it has already parsed BY
|
|
1645
|
+
* NAME (`Query.hasBeenParsed` is `this.name && connection.parsedStatements[this.name]`),
|
|
1646
|
+
* so an unnamed statement is re-Parsed on every execution, each Parse
|
|
1647
|
+
* replaces the unnamed cached plan source with a fresh one whose custom-plan
|
|
1648
|
+
* counter is zero, and the five-execution threshold that precedes promotion
|
|
1649
|
+
* is never reached. Every execution is therefore planned with the real
|
|
1650
|
+
* parameter values.
|
|
1651
|
+
*
|
|
1652
|
+
* No GUC is set, no `SET LOCAL` is emitted, no transaction is opened, and no
|
|
1653
|
+
* extra round trip is added, which is exactly why the opt-in can be per query
|
|
1654
|
+
* while the client-level `planCacheMode` (a connection parameter) cannot be.
|
|
1655
|
+
*
|
|
1656
|
+
* The refusal is deliberately here, at the one seam every read execution
|
|
1657
|
+
* passes through, rather than in each build method: the flag changes NOTHING
|
|
1658
|
+
* about the SQL text, so a build-time check would have had to be repeated in
|
|
1659
|
+
* every builder and could still be bypassed by a hand-executed
|
|
1660
|
+
* `DeferredQuery`.
|
|
1661
|
+
*
|
|
1662
|
+
* Engines whose dialect does not report {@link Dialect.supportsPlanCacheMode}
|
|
1663
|
+
* throw {@link UnsupportedFeatureError} (E017): the flag names a PostgreSQL
|
|
1664
|
+
* plan-cache guarantee, and an engine with no such cache cannot make it.
|
|
1665
|
+
* The same flag left unset (or `false`) is accepted everywhere.
|
|
1666
|
+
*
|
|
1667
|
+
* THE ONE COMBINATION THAT IS REFUSED RATHER THAN HONOURED. A client-level
|
|
1668
|
+
* `planCacheMode: 'force_generic_plan'` DEFEATS this option, and that was
|
|
1669
|
+
* MEASURED rather than reasoned about: on PostgreSQL 16.14, five executions
|
|
1670
|
+
* of one unnamed statement read 19,107 buffers with that setting in force and
|
|
1671
|
+
* 55 buffers with the same connection set back to `auto`, against 19,107 for
|
|
1672
|
+
* the named statement. So the setting governs the unnamed statement too, and
|
|
1673
|
+
* withholding the name buys nothing against it. Accepting the flag there
|
|
1674
|
+
* would report a guarantee the very next execution breaks, so the
|
|
1675
|
+
* contradiction throws {@link ValidationError} (E003) naming both settings.
|
|
1676
|
+
* Turbine can only see the setting IT applied: a `plan_cache_mode` installed
|
|
1677
|
+
* by the caller's own `SET`, by `ALTER ROLE`, or by a pooler is invisible
|
|
1678
|
+
* here and is not refused.
|
|
1679
|
+
*/
|
|
1680
|
+
preparedNameFor(args, name) {
|
|
1681
|
+
if (args?.forceCustomPlan !== true)
|
|
1682
|
+
return name;
|
|
1683
|
+
if (this.dialect.supportsPlanCacheMode !== true) {
|
|
1684
|
+
throw new errors_js_1.UnsupportedFeatureError('The forceCustomPlan query option', this.dialect.name, 'Forcing a per-query custom plan means keeping the statement out of the PostgreSQL plan cache, and this ' +
|
|
1685
|
+
'engine has no such cache to keep it out of. Remove the option, or set it only on PostgreSQL queries.');
|
|
1686
|
+
}
|
|
1687
|
+
if (this.options?.planCacheMode === 'force_generic_plan') {
|
|
1688
|
+
throw new errors_js_1.ValidationError('[turbine] forceCustomPlan: true cannot be honoured on a client configured with ' +
|
|
1689
|
+
"planCacheMode: 'force_generic_plan'. That setting is a connection parameter and it governs UNNAMED " +
|
|
1690
|
+
'statements as well as named ones, so the mechanism this option uses (withholding the ' +
|
|
1691
|
+
'prepared-statement name, so the driver re-parses the statement on every execution and it is planned ' +
|
|
1692
|
+
'with its real values) is ' +
|
|
1693
|
+
'overridden by it and the query would be planned generically anyway. Leave the client on the default ' +
|
|
1694
|
+
'(`planCacheMode` unset, or `auto`) and force the custom plan per query: that is the combination that ' +
|
|
1695
|
+
'expresses "custom here, auto there".');
|
|
1696
|
+
}
|
|
1697
|
+
return undefined;
|
|
1698
|
+
}
|
|
1632
1699
|
/**
|
|
1633
1700
|
* Execute a pool.query with an optional timeout.
|
|
1634
1701
|
* If timeout is set, races the query against a timer and rejects on expiry.
|
|
@@ -1777,7 +1844,7 @@ class QueryInterface {
|
|
|
1777
1844
|
}
|
|
1778
1845
|
}
|
|
1779
1846
|
const deferred = this.buildFindUnique(args);
|
|
1780
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, deferred.preparedName);
|
|
1847
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
1781
1848
|
return deferred.transform(result);
|
|
1782
1849
|
});
|
|
1783
1850
|
}
|
|
@@ -1798,11 +1865,11 @@ class QueryInterface {
|
|
|
1798
1865
|
const proj = (0, batched_loader_js_1.includeKeysForBatching)(args.select, args.omit, needed, (0, batched_loader_js_1.defaultProjectionFields)(this.tableMeta, args.includePii));
|
|
1799
1866
|
const baseArgs = { ...args, with: undefined, select: proj.select, omit: proj.omit };
|
|
1800
1867
|
const deferred = this.buildFindUnique(baseArgs);
|
|
1801
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, deferred.preparedName);
|
|
1868
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
1802
1869
|
const entity = deferred.transform(result);
|
|
1803
1870
|
if (!entity)
|
|
1804
1871
|
return null;
|
|
1805
|
-
await (0, batched_loader_js_1.loadRelationsBatched)(this.batchedContext(args.timeout, args.skipGlobalFilters, args.includePii === true), [entity], withClause, args.timeout);
|
|
1872
|
+
await (0, batched_loader_js_1.loadRelationsBatched)(this.batchedContext(args.timeout, args.skipGlobalFilters, args.includePii === true, args.forceCustomPlan === true), [entity], withClause, args.timeout);
|
|
1806
1873
|
(0, batched_loader_js_1.stripFields)([entity], proj.strip);
|
|
1807
1874
|
return entity;
|
|
1808
1875
|
}
|
|
@@ -1973,7 +2040,7 @@ class QueryInterface {
|
|
|
1973
2040
|
}
|
|
1974
2041
|
}
|
|
1975
2042
|
const deferred = this.buildFindMany(args);
|
|
1976
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args?.timeout, deferred.preparedName);
|
|
2043
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args?.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
1977
2044
|
return deferred.transform(result);
|
|
1978
2045
|
});
|
|
1979
2046
|
}
|
|
@@ -2424,7 +2491,13 @@ class QueryInterface {
|
|
|
2424
2491
|
limit: batchSize + 1,
|
|
2425
2492
|
});
|
|
2426
2493
|
this.currentAction = 'findManyStream';
|
|
2427
|
-
|
|
2494
|
+
// Streaming is ALREADY immune to the generic-plan cliff: the speculative
|
|
2495
|
+
// fetch has never passed a prepared name, and the cursor path runs through
|
|
2496
|
+
// DECLARE, so neither statement enters the plan cache. `preparedNameFor` is
|
|
2497
|
+
// still called with no name so that `forceCustomPlan: true` is VALIDATED on
|
|
2498
|
+
// an engine that cannot honour it here either, rather than being quietly
|
|
2499
|
+
// satisfied by an accident of this code path.
|
|
2500
|
+
const speculativeResult = await this.queryWithTimeout(speculativeDeferred.sql, speculativeDeferred.params, args?.timeout, this.preparedNameFor(args, undefined));
|
|
2428
2501
|
if (speculativeResult.rows.length <= batchSize) {
|
|
2429
2502
|
// Small drain, yield all rows and return, no cursor needed
|
|
2430
2503
|
for (const row of speculativeResult.rows) {
|
|
@@ -2487,7 +2560,7 @@ class QueryInterface {
|
|
|
2487
2560
|
}
|
|
2488
2561
|
}
|
|
2489
2562
|
const deferred = this.buildFindFirst(args);
|
|
2490
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args?.timeout, deferred.preparedName);
|
|
2563
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args?.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
2491
2564
|
return deferred.transform(result);
|
|
2492
2565
|
});
|
|
2493
2566
|
}
|
|
@@ -2512,7 +2585,7 @@ class QueryInterface {
|
|
|
2512
2585
|
async findFirstOrThrow(args) {
|
|
2513
2586
|
return this.executeWithMiddleware('findFirstOrThrow', (args ?? {}), async () => {
|
|
2514
2587
|
const deferred = this.buildFindFirstOrThrow(args);
|
|
2515
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args?.timeout, deferred.preparedName);
|
|
2588
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args?.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
2516
2589
|
return deferred.transform(result);
|
|
2517
2590
|
});
|
|
2518
2591
|
}
|
|
@@ -2542,7 +2615,7 @@ class QueryInterface {
|
|
|
2542
2615
|
async findUniqueOrThrow(args) {
|
|
2543
2616
|
return this.executeWithMiddleware('findUniqueOrThrow', args, async () => {
|
|
2544
2617
|
const deferred = this.buildFindUniqueOrThrow(args);
|
|
2545
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, deferred.preparedName);
|
|
2618
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
2546
2619
|
return deferred.transform(result);
|
|
2547
2620
|
});
|
|
2548
2621
|
}
|
|
@@ -2727,7 +2800,7 @@ class QueryInterface {
|
|
|
2727
2800
|
async count(args) {
|
|
2728
2801
|
return this.executeWithMiddleware('count', (args ?? {}), async () => {
|
|
2729
2802
|
const deferred = this.buildCount(args);
|
|
2730
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args?.timeout, deferred.preparedName);
|
|
2803
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args?.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
2731
2804
|
return deferred.transform(result);
|
|
2732
2805
|
});
|
|
2733
2806
|
}
|
|
@@ -2771,7 +2844,7 @@ class QueryInterface {
|
|
|
2771
2844
|
async groupBy(args) {
|
|
2772
2845
|
return this.executeWithMiddleware('groupBy', args, async () => {
|
|
2773
2846
|
const deferred = this.buildGroupBy(args);
|
|
2774
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, deferred.preparedName);
|
|
2847
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
2775
2848
|
return deferred.transform(result);
|
|
2776
2849
|
});
|
|
2777
2850
|
}
|
|
@@ -2790,7 +2863,7 @@ class QueryInterface {
|
|
|
2790
2863
|
async aggregate(args) {
|
|
2791
2864
|
return this.executeWithMiddleware('aggregate', args, async () => {
|
|
2792
2865
|
const deferred = this.buildAggregate(args);
|
|
2793
|
-
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, deferred.preparedName);
|
|
2866
|
+
const result = await this.queryWithTimeout(deferred.sql, deferred.params, args.timeout, this.preparedNameFor(args, deferred.preparedName));
|
|
2794
2867
|
return deferred.transform(result);
|
|
2795
2868
|
});
|
|
2796
2869
|
}
|
|
@@ -127,6 +127,15 @@ export interface QueryInterfaceOptions {
|
|
|
127
127
|
* (serverless drivers may not support named statements).
|
|
128
128
|
*/
|
|
129
129
|
preparedStatements?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* The client-level `plan_cache_mode` this interface's connections were opened
|
|
132
|
+
* with, forwarded from `TurbineConfig.planCacheMode` purely so the per-query
|
|
133
|
+
* `forceCustomPlan` option can REFUSE the one combination it cannot honour
|
|
134
|
+
* (`'force_generic_plan'`, see `preparedNameFor` in builder.ts). Nothing else
|
|
135
|
+
* reads it, and it is never emitted into SQL: the setting itself travels as a
|
|
136
|
+
* connection parameter, set by client.ts when it opens the pool.
|
|
137
|
+
*/
|
|
138
|
+
planCacheMode?: 'auto' | 'force_custom_plan' | 'force_generic_plan';
|
|
130
139
|
/**
|
|
131
140
|
* Enable the SQL template cache. When true, repeated queries with the
|
|
132
141
|
* same shape (same keys, operators, relations, different values) reuse
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
export type { AggregateArgs, AggregateResult, ArrayFilter, ColumnRef, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GlobalFilters, GroupByAggregateSpec, GroupByArgs, GroupByDistinctOn, GroupByResult, HavingClause, JsonFilter, JsonPathAggregateTarget, JsonPathGroupKey, JsonPathOrderBy, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderByObject, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, RelationLoadStrategy, RelationPickBy, RelationPickOrderBy, SelectResult, SkipGlobalFilters, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithOrderByObject, WithResult, } from './types.js';
|
|
9
9
|
export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, InsertStatementInput, UpsertStatementInput, } from '../dialect.js';
|
|
10
10
|
export { postgresDialect } from '../dialect.js';
|
|
11
|
+
export type { OptionKind, OptionTable } from './option-surface.js';
|
|
12
|
+
export { AGGREGATE_OPTIONS, ALL_OPTION_TABLES, applyNativeOptions, COUNT_OPTIONS, CREATE_MANY_OPTIONS, CREATE_OPTIONS, DELETE_MANY_OPTIONS, DELETE_OPTIONS, FIND_MANY_OPTIONS, FIND_MANY_STREAM_OPTIONS, FIND_UNIQUE_OPTIONS, GROUP_BY_OPTIONS, optionKeysOfKind, UPDATE_MANY_OPTIONS, UPDATE_OPTIONS, UPSERT_OPTIONS, } from './option-surface.js';
|
|
11
13
|
export type { SqlCacheEntry } from './utils.js';
|
|
12
14
|
export { buildCorrelation, escapeLike, escSingleQuote, fnv1a64Hex, LRUCache, OPERATOR_KEYS, quoteIdent, sqlToPreparedName, } from './utils.js';
|
|
13
15
|
export type { DeferredQuery, MiddlewareFn, QueryEvent, QueryEventListener, QueryInterfaceOptions, ReselectExecutor, TemporalInfinityReading, } from './builder.js';
|
package/dist/cjs/query/index.js
CHANGED
|
@@ -7,9 +7,26 @@
|
|
|
7
7
|
* former monolithic `import { … } from './query.js'`.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.QueryInterface = exports.AUTO_TO_ONE_JOIN_ROWS_MIN = exports.AUTO_TO_ONE_JOIN_ROWS_MAX = exports.AUTO_TO_ONE_JOIN_MAX_ROWS = exports.AUTO_JOIN_PENALTY_MS_PER_ROW = exports.AUTO_COUNT_BATCH_MIN_PARENT_ROWS = exports.AUTO_ASSUMED_ROUND_TRIP_MS = exports.sqlToPreparedName = exports.quoteIdent = exports.OPERATOR_KEYS = exports.LRUCache = exports.fnv1a64Hex = exports.escSingleQuote = exports.escapeLike = exports.buildCorrelation = exports.postgresDialect = void 0;
|
|
10
|
+
exports.QueryInterface = exports.AUTO_TO_ONE_JOIN_ROWS_MIN = exports.AUTO_TO_ONE_JOIN_ROWS_MAX = exports.AUTO_TO_ONE_JOIN_MAX_ROWS = exports.AUTO_JOIN_PENALTY_MS_PER_ROW = exports.AUTO_COUNT_BATCH_MIN_PARENT_ROWS = exports.AUTO_ASSUMED_ROUND_TRIP_MS = exports.sqlToPreparedName = exports.quoteIdent = exports.OPERATOR_KEYS = exports.LRUCache = exports.fnv1a64Hex = exports.escSingleQuote = exports.escapeLike = exports.buildCorrelation = exports.UPSERT_OPTIONS = exports.UPDATE_OPTIONS = exports.UPDATE_MANY_OPTIONS = exports.optionKeysOfKind = exports.GROUP_BY_OPTIONS = exports.FIND_UNIQUE_OPTIONS = exports.FIND_MANY_STREAM_OPTIONS = exports.FIND_MANY_OPTIONS = exports.DELETE_OPTIONS = exports.DELETE_MANY_OPTIONS = exports.CREATE_OPTIONS = exports.CREATE_MANY_OPTIONS = exports.COUNT_OPTIONS = exports.applyNativeOptions = exports.ALL_OPTION_TABLES = exports.AGGREGATE_OPTIONS = exports.postgresDialect = void 0;
|
|
11
11
|
var dialect_js_1 = require("../dialect.js");
|
|
12
12
|
Object.defineProperty(exports, "postgresDialect", { enumerable: true, get: function () { return dialect_js_1.postgresDialect; } });
|
|
13
|
+
var option_surface_js_1 = require("./option-surface.js");
|
|
14
|
+
Object.defineProperty(exports, "AGGREGATE_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.AGGREGATE_OPTIONS; } });
|
|
15
|
+
Object.defineProperty(exports, "ALL_OPTION_TABLES", { enumerable: true, get: function () { return option_surface_js_1.ALL_OPTION_TABLES; } });
|
|
16
|
+
Object.defineProperty(exports, "applyNativeOptions", { enumerable: true, get: function () { return option_surface_js_1.applyNativeOptions; } });
|
|
17
|
+
Object.defineProperty(exports, "COUNT_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.COUNT_OPTIONS; } });
|
|
18
|
+
Object.defineProperty(exports, "CREATE_MANY_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.CREATE_MANY_OPTIONS; } });
|
|
19
|
+
Object.defineProperty(exports, "CREATE_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.CREATE_OPTIONS; } });
|
|
20
|
+
Object.defineProperty(exports, "DELETE_MANY_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.DELETE_MANY_OPTIONS; } });
|
|
21
|
+
Object.defineProperty(exports, "DELETE_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.DELETE_OPTIONS; } });
|
|
22
|
+
Object.defineProperty(exports, "FIND_MANY_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.FIND_MANY_OPTIONS; } });
|
|
23
|
+
Object.defineProperty(exports, "FIND_MANY_STREAM_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.FIND_MANY_STREAM_OPTIONS; } });
|
|
24
|
+
Object.defineProperty(exports, "FIND_UNIQUE_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.FIND_UNIQUE_OPTIONS; } });
|
|
25
|
+
Object.defineProperty(exports, "GROUP_BY_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.GROUP_BY_OPTIONS; } });
|
|
26
|
+
Object.defineProperty(exports, "optionKeysOfKind", { enumerable: true, get: function () { return option_surface_js_1.optionKeysOfKind; } });
|
|
27
|
+
Object.defineProperty(exports, "UPDATE_MANY_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.UPDATE_MANY_OPTIONS; } });
|
|
28
|
+
Object.defineProperty(exports, "UPDATE_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.UPDATE_OPTIONS; } });
|
|
29
|
+
Object.defineProperty(exports, "UPSERT_OPTIONS", { enumerable: true, get: function () { return option_surface_js_1.UPSERT_OPTIONS; } });
|
|
13
30
|
var utils_js_1 = require("./utils.js");
|
|
14
31
|
Object.defineProperty(exports, "buildCorrelation", { enumerable: true, get: function () { return utils_js_1.buildCorrelation; } });
|
|
15
32
|
Object.defineProperty(exports, "escapeLike", { enumerable: true, get: function () { return utils_js_1.escapeLike; } });
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, the query-argument OPTION SURFACE as runtime data.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this file exists
|
|
5
|
+
*
|
|
6
|
+
* TypeScript erases interfaces, so `FindManyArgs` does not exist at runtime and
|
|
7
|
+
* any layer that has to decide, key by key, what to do with an args object has
|
|
8
|
+
* to keep its own hand-written list. `turbine-orm/prisma-compat` is exactly
|
|
9
|
+
* such a layer: it builds a FRESH turbine args object out of Prisma-shaped
|
|
10
|
+
* input and copies over the keys it recognizes. Every time core gained a
|
|
11
|
+
* query-level option, that ad-hoc allowlist silently failed to gain it, and the
|
|
12
|
+
* option was accepted by the caller's type-checker and then dropped on the
|
|
13
|
+
* floor. There was no feedback of any kind: no error, no warning, no test.
|
|
14
|
+
*
|
|
15
|
+
* These tables are the fix. Each one is a `Record<keyof SomeArgs<Row>,
|
|
16
|
+
* OptionKind>`, the same mechanism `TURBINE_CONFIG_KEYS` (client.ts) uses for
|
|
17
|
+
* the client-config surface, and it binds the compiler in BOTH directions:
|
|
18
|
+
*
|
|
19
|
+
* - add an option to an arg interface and this file stops compiling until a
|
|
20
|
+
* human classifies it ("Property 'fooMode' is missing in type ..."), so an
|
|
21
|
+
* option can no longer be stranded BY OMISSION;
|
|
22
|
+
* - list a key here that is not on the interface and it fails as an excess
|
|
23
|
+
* property, so a table can never drift into describing an option that does
|
|
24
|
+
* not exist.
|
|
25
|
+
*
|
|
26
|
+
* It deliberately does NOT make "add the option in one place" sufficient: it
|
|
27
|
+
* makes the second edit a BUILD FAILURE rather than a silent drop. That trade is
|
|
28
|
+
* intentional. A passthrough-by-default translator would satisfy the shorter
|
|
29
|
+
* wording and be actively wrong, because two of the options below carry FIELD
|
|
30
|
+
* NAMES in their values (`optimisticLock.field`, `distinctOn.columns`), which a
|
|
31
|
+
* compat layer must rename before core ever sees them. Copying those blind
|
|
32
|
+
* works on a schema whose names happen to coincide and breaks on one that
|
|
33
|
+
* renames a column, i.e. it makes the failure mode depend on the schema.
|
|
34
|
+
*
|
|
35
|
+
* ## THE ONE RULE for classifying a new option
|
|
36
|
+
*
|
|
37
|
+
* Classify a key `'native'` ONLY when its value contains no field, relation,
|
|
38
|
+
* column, or model NAME. If the value names anything in the schema, it is
|
|
39
|
+
* `'prisma'`: a name-translating consumer has to walk it by hand.
|
|
40
|
+
*
|
|
41
|
+
* @module
|
|
42
|
+
*/
|
|
43
|
+
import type { AggregateArgs, CountArgs, CreateArgs, CreateManyArgs, DeleteArgs, DeleteManyArgs, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GroupByArgs, UpdateArgs, UpdateManyArgs, UpsertArgs } from './types.js';
|
|
44
|
+
/**
|
|
45
|
+
* How a name-translating consumer (today: `turbine-orm/prisma-compat`) must
|
|
46
|
+
* handle one key of a turbine query-arg interface.
|
|
47
|
+
*
|
|
48
|
+
* - `'prisma'`, the key is a Prisma concept too, or its VALUE carries names
|
|
49
|
+
* that live in the caller's naming space. Translated by hand; NEVER copied
|
|
50
|
+
* verbatim.
|
|
51
|
+
* - `'native'`, turbine-only and its value is opaque to naming (a boolean, a
|
|
52
|
+
* number, a list of table names). Copied through untouched.
|
|
53
|
+
* - `'nativeAlias'`, the turbine SPELLING of a concept the caller's surface
|
|
54
|
+
* already has under another name (`with`/`limit`/`offset` vs
|
|
55
|
+
* `include`/`take`/`skip`). Refused, because forwarding it would collide with
|
|
56
|
+
* the translated key and would carry turbine relation names into a call
|
|
57
|
+
* written in the caller's names. The diagnostic names the right key instead.
|
|
58
|
+
* - `'internal'`, not reachable through the compat surface at all
|
|
59
|
+
* (`batchSize` belongs to a streaming method compat does not expose), so it
|
|
60
|
+
* is not part of any known set and passing it is reported as unknown.
|
|
61
|
+
*/
|
|
62
|
+
export type OptionKind = 'prisma' | 'native' | 'nativeAlias' | 'internal';
|
|
63
|
+
/**
|
|
64
|
+
* The generic parameter the tables are instantiated at. `keyof FindManyArgs<T>`
|
|
65
|
+
* is the literal union of the DECLARED key names regardless of `T`, so a
|
|
66
|
+
* neutral row type keeps the tables stable and free of entity coupling.
|
|
67
|
+
*/
|
|
68
|
+
type Row = Record<string, unknown>;
|
|
69
|
+
/** One option table: every declared key of one arg interface, classified. */
|
|
70
|
+
export type OptionTable<A> = Readonly<Record<keyof A, OptionKind>>;
|
|
71
|
+
export declare const FIND_UNIQUE_OPTIONS: OptionTable<FindUniqueArgs<Row>>;
|
|
72
|
+
export declare const FIND_MANY_OPTIONS: OptionTable<FindManyArgs<Row>>;
|
|
73
|
+
export declare const FIND_MANY_STREAM_OPTIONS: OptionTable<FindManyStreamArgs<Row>>;
|
|
74
|
+
export declare const CREATE_OPTIONS: OptionTable<CreateArgs<Row>>;
|
|
75
|
+
export declare const CREATE_MANY_OPTIONS: OptionTable<CreateManyArgs<Row>>;
|
|
76
|
+
export declare const UPDATE_OPTIONS: OptionTable<UpdateArgs<Row>>;
|
|
77
|
+
export declare const UPDATE_MANY_OPTIONS: OptionTable<UpdateManyArgs<Row>>;
|
|
78
|
+
export declare const DELETE_OPTIONS: OptionTable<DeleteArgs<Row>>;
|
|
79
|
+
export declare const DELETE_MANY_OPTIONS: OptionTable<DeleteManyArgs<Row>>;
|
|
80
|
+
export declare const UPSERT_OPTIONS: OptionTable<UpsertArgs<Row>>;
|
|
81
|
+
export declare const COUNT_OPTIONS: OptionTable<CountArgs<Row>>;
|
|
82
|
+
export declare const AGGREGATE_OPTIONS: OptionTable<AggregateArgs<Row>>;
|
|
83
|
+
export declare const GROUP_BY_OPTIONS: OptionTable<GroupByArgs<Row>>;
|
|
84
|
+
/**
|
|
85
|
+
* Every table, so a test can assert the set is complete and well-formed without
|
|
86
|
+
* naming each one (a table stubbed out during a refactor shows up here).
|
|
87
|
+
*/
|
|
88
|
+
export declare const ALL_OPTION_TABLES: Readonly<Record<string, Readonly<Record<string, OptionKind>>>>;
|
|
89
|
+
/**
|
|
90
|
+
* Copy every `'native'` key present on `src` onto `dst`.
|
|
91
|
+
*
|
|
92
|
+
* Iterates `src` (a small caller-supplied object) rather than the table, so the
|
|
93
|
+
* cost is proportional to what was actually passed. `undefined` values are
|
|
94
|
+
* skipped: `{ ...maybeOpts }` routinely materializes keys with no value, and
|
|
95
|
+
* writing `undefined` through would be indistinguishable from passing it.
|
|
96
|
+
*/
|
|
97
|
+
export declare function applyNativeOptions(table: Readonly<Record<string, OptionKind>>, src: Record<string, unknown>, dst: Record<string, unknown>): void;
|
|
98
|
+
/** The keys of `table` with the given kind, as a set. */
|
|
99
|
+
export declare function optionKeysOfKind(table: Readonly<Record<string, OptionKind>>, ...kinds: OptionKind[]): string[];
|
|
100
|
+
export {};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm, the query-argument OPTION SURFACE as runtime data.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this file exists
|
|
6
|
+
*
|
|
7
|
+
* TypeScript erases interfaces, so `FindManyArgs` does not exist at runtime and
|
|
8
|
+
* any layer that has to decide, key by key, what to do with an args object has
|
|
9
|
+
* to keep its own hand-written list. `turbine-orm/prisma-compat` is exactly
|
|
10
|
+
* such a layer: it builds a FRESH turbine args object out of Prisma-shaped
|
|
11
|
+
* input and copies over the keys it recognizes. Every time core gained a
|
|
12
|
+
* query-level option, that ad-hoc allowlist silently failed to gain it, and the
|
|
13
|
+
* option was accepted by the caller's type-checker and then dropped on the
|
|
14
|
+
* floor. There was no feedback of any kind: no error, no warning, no test.
|
|
15
|
+
*
|
|
16
|
+
* These tables are the fix. Each one is a `Record<keyof SomeArgs<Row>,
|
|
17
|
+
* OptionKind>`, the same mechanism `TURBINE_CONFIG_KEYS` (client.ts) uses for
|
|
18
|
+
* the client-config surface, and it binds the compiler in BOTH directions:
|
|
19
|
+
*
|
|
20
|
+
* - add an option to an arg interface and this file stops compiling until a
|
|
21
|
+
* human classifies it ("Property 'fooMode' is missing in type ..."), so an
|
|
22
|
+
* option can no longer be stranded BY OMISSION;
|
|
23
|
+
* - list a key here that is not on the interface and it fails as an excess
|
|
24
|
+
* property, so a table can never drift into describing an option that does
|
|
25
|
+
* not exist.
|
|
26
|
+
*
|
|
27
|
+
* It deliberately does NOT make "add the option in one place" sufficient: it
|
|
28
|
+
* makes the second edit a BUILD FAILURE rather than a silent drop. That trade is
|
|
29
|
+
* intentional. A passthrough-by-default translator would satisfy the shorter
|
|
30
|
+
* wording and be actively wrong, because two of the options below carry FIELD
|
|
31
|
+
* NAMES in their values (`optimisticLock.field`, `distinctOn.columns`), which a
|
|
32
|
+
* compat layer must rename before core ever sees them. Copying those blind
|
|
33
|
+
* works on a schema whose names happen to coincide and breaks on one that
|
|
34
|
+
* renames a column, i.e. it makes the failure mode depend on the schema.
|
|
35
|
+
*
|
|
36
|
+
* ## THE ONE RULE for classifying a new option
|
|
37
|
+
*
|
|
38
|
+
* Classify a key `'native'` ONLY when its value contains no field, relation,
|
|
39
|
+
* column, or model NAME. If the value names anything in the schema, it is
|
|
40
|
+
* `'prisma'`: a name-translating consumer has to walk it by hand.
|
|
41
|
+
*
|
|
42
|
+
* @module
|
|
43
|
+
*/
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.ALL_OPTION_TABLES = exports.GROUP_BY_OPTIONS = exports.AGGREGATE_OPTIONS = exports.COUNT_OPTIONS = exports.UPSERT_OPTIONS = exports.DELETE_MANY_OPTIONS = exports.DELETE_OPTIONS = exports.UPDATE_MANY_OPTIONS = exports.UPDATE_OPTIONS = exports.CREATE_MANY_OPTIONS = exports.CREATE_OPTIONS = exports.FIND_MANY_STREAM_OPTIONS = exports.FIND_MANY_OPTIONS = exports.FIND_UNIQUE_OPTIONS = void 0;
|
|
46
|
+
exports.applyNativeOptions = applyNativeOptions;
|
|
47
|
+
exports.optionKeysOfKind = optionKeysOfKind;
|
|
48
|
+
exports.FIND_UNIQUE_OPTIONS = {
|
|
49
|
+
where: 'prisma',
|
|
50
|
+
select: 'prisma',
|
|
51
|
+
omit: 'prisma',
|
|
52
|
+
// Prisma spells this `include`; forwarding `with` would collide with the
|
|
53
|
+
// translated projection and carry turbine relation names into a Prisma call.
|
|
54
|
+
with: 'nativeAlias',
|
|
55
|
+
// Same key on both surfaces, DIFFERENT value domains ('query' | 'join' vs
|
|
56
|
+
// 'join' | 'batched' | 'auto' | 'flatten'), so the value needs mapping.
|
|
57
|
+
relationLoadStrategy: 'prisma',
|
|
58
|
+
timeout: 'native',
|
|
59
|
+
stableRelationOrder: 'native',
|
|
60
|
+
skipGlobalFilters: 'native',
|
|
61
|
+
includePii: 'native',
|
|
62
|
+
forceCustomPlan: 'native',
|
|
63
|
+
};
|
|
64
|
+
exports.FIND_MANY_OPTIONS = {
|
|
65
|
+
where: 'prisma',
|
|
66
|
+
select: 'prisma',
|
|
67
|
+
omit: 'prisma',
|
|
68
|
+
orderBy: 'prisma',
|
|
69
|
+
cursor: 'prisma',
|
|
70
|
+
take: 'prisma',
|
|
71
|
+
distinct: 'prisma',
|
|
72
|
+
relationLoadStrategy: 'prisma',
|
|
73
|
+
with: 'nativeAlias',
|
|
74
|
+
limit: 'nativeAlias',
|
|
75
|
+
offset: 'nativeAlias',
|
|
76
|
+
timeout: 'native',
|
|
77
|
+
stableRelationOrder: 'native',
|
|
78
|
+
skipGlobalFilters: 'native',
|
|
79
|
+
warnOnUnlimited: 'native',
|
|
80
|
+
includePii: 'native',
|
|
81
|
+
forceCustomPlan: 'native',
|
|
82
|
+
};
|
|
83
|
+
exports.FIND_MANY_STREAM_OPTIONS = {
|
|
84
|
+
...exports.FIND_MANY_OPTIONS,
|
|
85
|
+
// No streaming delegate exists on the compat surface, so this is not a known
|
|
86
|
+
// key there and passing it is reported rather than quietly ignored.
|
|
87
|
+
batchSize: 'internal',
|
|
88
|
+
};
|
|
89
|
+
exports.CREATE_OPTIONS = {
|
|
90
|
+
data: 'prisma',
|
|
91
|
+
timeout: 'native',
|
|
92
|
+
};
|
|
93
|
+
exports.CREATE_MANY_OPTIONS = {
|
|
94
|
+
data: 'prisma',
|
|
95
|
+
skipDuplicates: 'prisma',
|
|
96
|
+
timeout: 'native',
|
|
97
|
+
};
|
|
98
|
+
exports.UPDATE_OPTIONS = {
|
|
99
|
+
where: 'prisma',
|
|
100
|
+
data: 'prisma',
|
|
101
|
+
// `{ field, expected }`, and `field` is a FIELD NAME, so it has to be renamed
|
|
102
|
+
// into turbine's naming space rather than copied. See THE ONE RULE above.
|
|
103
|
+
optimisticLock: 'prisma',
|
|
104
|
+
timeout: 'native',
|
|
105
|
+
allowFullTableScan: 'native',
|
|
106
|
+
skipGlobalFilters: 'native',
|
|
107
|
+
};
|
|
108
|
+
exports.UPDATE_MANY_OPTIONS = {
|
|
109
|
+
where: 'prisma',
|
|
110
|
+
data: 'prisma',
|
|
111
|
+
timeout: 'native',
|
|
112
|
+
allowFullTableScan: 'native',
|
|
113
|
+
skipGlobalFilters: 'native',
|
|
114
|
+
};
|
|
115
|
+
exports.DELETE_OPTIONS = {
|
|
116
|
+
where: 'prisma',
|
|
117
|
+
timeout: 'native',
|
|
118
|
+
allowFullTableScan: 'native',
|
|
119
|
+
skipGlobalFilters: 'native',
|
|
120
|
+
};
|
|
121
|
+
exports.DELETE_MANY_OPTIONS = {
|
|
122
|
+
where: 'prisma',
|
|
123
|
+
timeout: 'native',
|
|
124
|
+
allowFullTableScan: 'native',
|
|
125
|
+
skipGlobalFilters: 'native',
|
|
126
|
+
};
|
|
127
|
+
exports.UPSERT_OPTIONS = {
|
|
128
|
+
where: 'prisma',
|
|
129
|
+
create: 'prisma',
|
|
130
|
+
update: 'prisma',
|
|
131
|
+
timeout: 'native',
|
|
132
|
+
skipGlobalFilters: 'native',
|
|
133
|
+
};
|
|
134
|
+
exports.COUNT_OPTIONS = {
|
|
135
|
+
where: 'prisma',
|
|
136
|
+
timeout: 'native',
|
|
137
|
+
skipGlobalFilters: 'native',
|
|
138
|
+
forceCustomPlan: 'native',
|
|
139
|
+
};
|
|
140
|
+
exports.AGGREGATE_OPTIONS = {
|
|
141
|
+
where: 'prisma',
|
|
142
|
+
_count: 'prisma',
|
|
143
|
+
_sum: 'prisma',
|
|
144
|
+
_avg: 'prisma',
|
|
145
|
+
_min: 'prisma',
|
|
146
|
+
_max: 'prisma',
|
|
147
|
+
timeout: 'native',
|
|
148
|
+
skipGlobalFilters: 'native',
|
|
149
|
+
includePii: 'native',
|
|
150
|
+
forceCustomPlan: 'native',
|
|
151
|
+
};
|
|
152
|
+
exports.GROUP_BY_OPTIONS = {
|
|
153
|
+
by: 'prisma',
|
|
154
|
+
where: 'prisma',
|
|
155
|
+
having: 'prisma',
|
|
156
|
+
orderBy: 'prisma',
|
|
157
|
+
_count: 'prisma',
|
|
158
|
+
_sum: 'prisma',
|
|
159
|
+
_avg: 'prisma',
|
|
160
|
+
_min: 'prisma',
|
|
161
|
+
_max: 'prisma',
|
|
162
|
+
// `{ columns, orderBy }`, both in FIELD-NAME space. See THE ONE RULE.
|
|
163
|
+
distinctOn: 'prisma',
|
|
164
|
+
limit: 'nativeAlias',
|
|
165
|
+
offset: 'nativeAlias',
|
|
166
|
+
timeout: 'native',
|
|
167
|
+
skipGlobalFilters: 'native',
|
|
168
|
+
includePii: 'native',
|
|
169
|
+
forceCustomPlan: 'native',
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Every table, so a test can assert the set is complete and well-formed without
|
|
173
|
+
* naming each one (a table stubbed out during a refactor shows up here).
|
|
174
|
+
*/
|
|
175
|
+
exports.ALL_OPTION_TABLES = {
|
|
176
|
+
findUnique: exports.FIND_UNIQUE_OPTIONS,
|
|
177
|
+
findMany: exports.FIND_MANY_OPTIONS,
|
|
178
|
+
findManyStream: exports.FIND_MANY_STREAM_OPTIONS,
|
|
179
|
+
create: exports.CREATE_OPTIONS,
|
|
180
|
+
createMany: exports.CREATE_MANY_OPTIONS,
|
|
181
|
+
update: exports.UPDATE_OPTIONS,
|
|
182
|
+
updateMany: exports.UPDATE_MANY_OPTIONS,
|
|
183
|
+
delete: exports.DELETE_OPTIONS,
|
|
184
|
+
deleteMany: exports.DELETE_MANY_OPTIONS,
|
|
185
|
+
upsert: exports.UPSERT_OPTIONS,
|
|
186
|
+
count: exports.COUNT_OPTIONS,
|
|
187
|
+
aggregate: exports.AGGREGATE_OPTIONS,
|
|
188
|
+
groupBy: exports.GROUP_BY_OPTIONS,
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Copy every `'native'` key present on `src` onto `dst`.
|
|
192
|
+
*
|
|
193
|
+
* Iterates `src` (a small caller-supplied object) rather than the table, so the
|
|
194
|
+
* cost is proportional to what was actually passed. `undefined` values are
|
|
195
|
+
* skipped: `{ ...maybeOpts }` routinely materializes keys with no value, and
|
|
196
|
+
* writing `undefined` through would be indistinguishable from passing it.
|
|
197
|
+
*/
|
|
198
|
+
function applyNativeOptions(table, src, dst) {
|
|
199
|
+
// Total on any input: a delegate whose args are optional can be called with
|
|
200
|
+
// none, and a diagnostic-adjacent helper must not be the thing that throws.
|
|
201
|
+
if (src === null || typeof src !== 'object')
|
|
202
|
+
return;
|
|
203
|
+
for (const key of Object.keys(src)) {
|
|
204
|
+
if (table[key] !== 'native')
|
|
205
|
+
continue;
|
|
206
|
+
const value = src[key];
|
|
207
|
+
if (value !== undefined)
|
|
208
|
+
dst[key] = value;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** The keys of `table` with the given kind, as a set. */
|
|
212
|
+
function optionKeysOfKind(table, ...kinds) {
|
|
213
|
+
return Object.keys(table).filter((k) => kinds.includes(table[k]));
|
|
214
|
+
}
|