turbine-orm 0.78.0-next.b792a6a → 0.79.0-next.c53fa34
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/dist/cjs/client.d.ts +47 -2
- package/dist/cjs/client.js +122 -41
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/prisma-compat.d.ts +1 -1
- package/dist/cjs/prisma-compat.js +11 -7
- package/dist/cjs/query/deferred.d.ts +15 -0
- package/dist/cjs/query-events.d.ts +82 -0
- package/dist/cjs/query-events.js +137 -0
- package/dist/cjs/typed-sql.d.ts +14 -1
- package/dist/cjs/typed-sql.js +12 -8
- package/dist/client.d.ts +47 -2
- package/dist/client.js +123 -42
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/prisma-compat.d.ts +1 -1
- package/dist/prisma-compat.js +11 -7
- package/dist/query/deferred.d.ts +15 -0
- package/dist/query-events.d.ts +82 -0
- package/dist/query-events.js +127 -0
- package/dist/typed-sql.d.ts +14 -1
- package/dist/typed-sql.js +12 -8
- package/package.json +1 -1
package/dist/cjs/client.d.ts
CHANGED
|
@@ -648,9 +648,11 @@ export declare class TransactionClient {
|
|
|
648
648
|
* inside the same BEGIN/COMMIT (and any active SAVEPOINT) as every other
|
|
649
649
|
* statement in the callback. Driver errors are translated by `wrapPgError`,
|
|
650
650
|
* exactly as pool-scoped and table-scoped queries are. Like {@link raw}, it
|
|
651
|
-
*
|
|
651
|
+
* runs no middleware, and it emits one `$on('query')` event with model
|
|
652
|
+
* `'$raw'` and `action` set to the optional `action` label (the adapter
|
|
653
|
+
* passes `'$queryRaw'` / `'$executeRaw'` ...; default `'rawQuery'`).
|
|
652
654
|
*/
|
|
653
|
-
rawQuery<T extends object = Record<string, unknown>>(text: string, params?: readonly unknown[]): Promise<{
|
|
655
|
+
rawQuery<T extends object = Record<string, unknown>>(text: string, params?: readonly unknown[], action?: string): Promise<{
|
|
654
656
|
rows: T[];
|
|
655
657
|
rowCount: number | null;
|
|
656
658
|
}>;
|
|
@@ -986,6 +988,16 @@ export declare class TurbineClient {
|
|
|
986
988
|
* On HTTP-based drivers it falls back to sequential execution.
|
|
987
989
|
*/
|
|
988
990
|
pipeline<T extends readonly DeferredQuery<unknown>[]>(...args: T | [T, PipelineOptions?]): Promise<PipelineResults<T>>;
|
|
991
|
+
/**
|
|
992
|
+
* {@link pipeline} with one `$on('query')` event per statement. Only taken
|
|
993
|
+
* when a listener is registered, so an unobserved pipeline runs exactly as
|
|
994
|
+
* before. Each statement's `transform` is wrapped to capture the driver's
|
|
995
|
+
* row count on the way through (the pipeline returns transformed values, not
|
|
996
|
+
* driver results). The statements share one round trip, so the batch's wall
|
|
997
|
+
* time is split evenly across them and each event carries
|
|
998
|
+
* `batch: 'pipeline'`.
|
|
999
|
+
*/
|
|
1000
|
+
private reportedPipeline;
|
|
989
1001
|
/**
|
|
990
1002
|
* Check whether the underlying pool supports the real pipeline protocol.
|
|
991
1003
|
* Returns `true` for standard pg.Pool TCP connections, `false` for HTTP
|
|
@@ -1005,6 +1017,39 @@ export declare class TurbineClient {
|
|
|
1005
1017
|
* ```
|
|
1006
1018
|
*/
|
|
1007
1019
|
raw<T extends Record<string, unknown> = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]): Promise<T[]>;
|
|
1020
|
+
/**
|
|
1021
|
+
* @internal The `turbine-orm/prisma-compat` adapter's pool-level raw seam,
|
|
1022
|
+
* the twin of {@link TransactionClient.rawQuery}. NOT application API: use
|
|
1023
|
+
* {@link raw} or {@link sql}, whose tagged templates make concatenating a
|
|
1024
|
+
* value into the SQL text impossible. It exists so the adapter's
|
|
1025
|
+
* `$queryRaw` / `$executeRaw` statements emit a `$on('query')` event (model
|
|
1026
|
+
* `'$raw'`, `action` the adapter's label) instead of running unobserved on
|
|
1027
|
+
* the pool. Errors are translated by `wrapPgError`, like {@link raw}.
|
|
1028
|
+
*/
|
|
1029
|
+
rawQuery<T extends object = Record<string, unknown>>(text: string, params?: readonly unknown[], action?: string): Promise<{
|
|
1030
|
+
rows: T[];
|
|
1031
|
+
rowCount: number | null;
|
|
1032
|
+
}>;
|
|
1033
|
+
/**
|
|
1034
|
+
* Run `fn` with `tag` attached to every `$on('query')` event it causes, so a
|
|
1035
|
+
* listener can attribute queries to the feature or code path that issued
|
|
1036
|
+
* them. The scope follows async context: queries in awaited helpers,
|
|
1037
|
+
* transactions, pipelines and raw SQL inside `fn` are all tagged. An inner
|
|
1038
|
+
* `$tag` replaces an outer one for its own duration. The tag is event
|
|
1039
|
+
* metadata only and never reaches SQL.
|
|
1040
|
+
*
|
|
1041
|
+
* Throws {@link ValidationError} (E003) for an empty tag or one longer than
|
|
1042
|
+
* {@link MAX_QUERY_TAG_LENGTH} (128) characters.
|
|
1043
|
+
*
|
|
1044
|
+
* @example
|
|
1045
|
+
* ```ts
|
|
1046
|
+
* const order = await db.$tag('checkout', async () => {
|
|
1047
|
+
* const cart = await db.carts.findUnique({ where: { id } });
|
|
1048
|
+
* return db.orders.create({ data: { cartId: cart.id } });
|
|
1049
|
+
* });
|
|
1050
|
+
* ```
|
|
1051
|
+
*/
|
|
1052
|
+
$tag<R>(tag: string, fn: () => R): R;
|
|
1008
1053
|
/**
|
|
1009
1054
|
* Execute a **typed** raw SQL query, Turbine's answer to Prisma's TypedSQL.
|
|
1010
1055
|
*
|
package/dist/cjs/client.js
CHANGED
|
@@ -37,6 +37,7 @@ const pipeline_js_1 = require("./pipeline.js");
|
|
|
37
37
|
const index_js_1 = require("./query/index.js");
|
|
38
38
|
const utils_js_1 = require("./query/utils.js");
|
|
39
39
|
const warn_registry_js_1 = require("./query/warn-registry.js");
|
|
40
|
+
const query_events_js_1 = require("./query-events.js");
|
|
40
41
|
const realtime_js_1 = require("./realtime.js");
|
|
41
42
|
const typed_sql_js_1 = require("./typed-sql.js");
|
|
42
43
|
async function withRetry(fn, options) {
|
|
@@ -538,13 +539,8 @@ class TransactionClient {
|
|
|
538
539
|
sql += this.dialect.paramPlaceholder(i + 1);
|
|
539
540
|
}
|
|
540
541
|
});
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return result.rows;
|
|
544
|
-
}
|
|
545
|
-
catch (err) {
|
|
546
|
-
throw (0, errors_js_1.wrapPgError)(err);
|
|
547
|
-
}
|
|
542
|
+
const result = await (0, query_events_js_1.runReportedStatement)(this.queryOptions?._onQuery, (0, query_events_js_1.rawIdentity)('raw'), sql, values, () => this.client.query(sql, values), errors_js_1.wrapPgError);
|
|
543
|
+
return result.rows;
|
|
548
544
|
}
|
|
549
545
|
/**
|
|
550
546
|
* @internal The `turbine-orm/prisma-compat` adapter's transaction seam. NOT
|
|
@@ -568,16 +564,13 @@ class TransactionClient {
|
|
|
568
564
|
* inside the same BEGIN/COMMIT (and any active SAVEPOINT) as every other
|
|
569
565
|
* statement in the callback. Driver errors are translated by `wrapPgError`,
|
|
570
566
|
* exactly as pool-scoped and table-scoped queries are. Like {@link raw}, it
|
|
571
|
-
*
|
|
567
|
+
* runs no middleware, and it emits one `$on('query')` event with model
|
|
568
|
+
* `'$raw'` and `action` set to the optional `action` label (the adapter
|
|
569
|
+
* passes `'$queryRaw'` / `'$executeRaw'` ...; default `'rawQuery'`).
|
|
572
570
|
*/
|
|
573
|
-
async rawQuery(text, params = []) {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
return { rows: result.rows, rowCount: result.rowCount };
|
|
577
|
-
}
|
|
578
|
-
catch (err) {
|
|
579
|
-
throw (0, errors_js_1.wrapPgError)(err);
|
|
580
|
-
}
|
|
571
|
+
async rawQuery(text, params = [], action = 'rawQuery') {
|
|
572
|
+
const result = await (0, query_events_js_1.runReportedStatement)(this.queryOptions?._onQuery, (0, query_events_js_1.rawIdentity)(action), text, params, () => this.client.query(text, params), errors_js_1.wrapPgError);
|
|
573
|
+
return { rows: result.rows, rowCount: result.rowCount };
|
|
581
574
|
}
|
|
582
575
|
/**
|
|
583
576
|
* Create a pool-like wrapper around the transaction client.
|
|
@@ -908,7 +901,11 @@ class TurbineClient {
|
|
|
908
901
|
_onQuery: (event) => {
|
|
909
902
|
if (this.queryListeners.size === 0)
|
|
910
903
|
return;
|
|
911
|
-
|
|
904
|
+
// The $tag() scope is read here, at the one seam every emitter shares,
|
|
905
|
+
// so a tag reaches model, raw, pipeline and batch events alike.
|
|
906
|
+
const tag = event.tag ?? (0, query_events_js_1.currentQueryTag)();
|
|
907
|
+
const tagged = tag === undefined || event.tag !== undefined ? event : { ...event, tag };
|
|
908
|
+
const emitted = this.queryParamsVisible ? tagged : { ...tagged, params: tagged.params.map(() => '[REDACTED]') };
|
|
912
909
|
for (const listener of this.queryListeners) {
|
|
913
910
|
try {
|
|
914
911
|
listener(emitted);
|
|
@@ -1520,7 +1517,63 @@ class TurbineClient {
|
|
|
1520
1517
|
// where VALUES as soon as any verbose client existed in the process. The
|
|
1521
1518
|
// scope is established around the await, so every continuation of the batch
|
|
1522
1519
|
// resolves this client's mode.
|
|
1523
|
-
|
|
1520
|
+
const sink = this.queryListeners.size > 0 ? this.queryOptions._onQuery : undefined;
|
|
1521
|
+
if (!sink)
|
|
1522
|
+
return this.withErrorMode(() => (0, pipeline_js_1.executePipeline)(this.pool, queries, options));
|
|
1523
|
+
return this.withErrorMode(() => this.reportedPipeline(sink, queries, options));
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* {@link pipeline} with one `$on('query')` event per statement. Only taken
|
|
1527
|
+
* when a listener is registered, so an unobserved pipeline runs exactly as
|
|
1528
|
+
* before. Each statement's `transform` is wrapped to capture the driver's
|
|
1529
|
+
* row count on the way through (the pipeline returns transformed values, not
|
|
1530
|
+
* driver results). The statements share one round trip, so the batch's wall
|
|
1531
|
+
* time is split evenly across them and each event carries
|
|
1532
|
+
* `batch: 'pipeline'`.
|
|
1533
|
+
*/
|
|
1534
|
+
async reportedPipeline(sink, queries, options) {
|
|
1535
|
+
const rowCounts = new Array(queries.length).fill(undefined);
|
|
1536
|
+
const observed = queries.map((q, i) => ({
|
|
1537
|
+
...q,
|
|
1538
|
+
transform: (raw) => {
|
|
1539
|
+
rowCounts[i] = (0, query_events_js_1.resultRowCount)(raw);
|
|
1540
|
+
return q.transform(raw);
|
|
1541
|
+
},
|
|
1542
|
+
}));
|
|
1543
|
+
const start = performance.now();
|
|
1544
|
+
let failure;
|
|
1545
|
+
try {
|
|
1546
|
+
return await (0, pipeline_js_1.executePipeline)(this.pool, observed, options);
|
|
1547
|
+
}
|
|
1548
|
+
catch (err) {
|
|
1549
|
+
failure = err;
|
|
1550
|
+
throw err;
|
|
1551
|
+
}
|
|
1552
|
+
finally {
|
|
1553
|
+
const duration = (performance.now() - start) / queries.length;
|
|
1554
|
+
// PipelineError names the failing slot; any other failure (connect,
|
|
1555
|
+
// BEGIN) is charged to the first statement that produced no result.
|
|
1556
|
+
const failedIndex = failure === undefined
|
|
1557
|
+
? -1
|
|
1558
|
+
: failure instanceof errors_js_1.PipelineError && failure.failedIndex !== undefined
|
|
1559
|
+
? failure.failedIndex
|
|
1560
|
+
: rowCounts.indexOf(undefined);
|
|
1561
|
+
queries.forEach((q, i) => {
|
|
1562
|
+
const failed = i === failedIndex;
|
|
1563
|
+
// A slot that never ran (or rolled back unseen) is not reported.
|
|
1564
|
+
if (failed || rowCounts[i] !== undefined) {
|
|
1565
|
+
(0, query_events_js_1.emitStatementEvent)(sink, {
|
|
1566
|
+
sql: q.sql,
|
|
1567
|
+
params: q.params,
|
|
1568
|
+
duration,
|
|
1569
|
+
...(0, query_events_js_1.deferredIdentity)(q.tag),
|
|
1570
|
+
rows: rowCounts[i] ?? 0,
|
|
1571
|
+
batch: 'pipeline',
|
|
1572
|
+
...(failed ? { error: failure instanceof Error ? failure : new Error(String(failure)) } : {}),
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1524
1577
|
}
|
|
1525
1578
|
/**
|
|
1526
1579
|
* Check whether the underlying pool supports the real pipeline protocol.
|
|
@@ -1556,13 +1609,43 @@ class TurbineClient {
|
|
|
1556
1609
|
if (this.logging) {
|
|
1557
1610
|
console.log(`[turbine] Raw SQL: ${sql.trim().substring(0, 120)}...`);
|
|
1558
1611
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1612
|
+
const result = await (0, query_events_js_1.runReportedStatement)(this.queryOptions._onQuery, (0, query_events_js_1.rawIdentity)('raw'), sql, values, () => this.pool.query(sql, values), (err) => this.withErrorMode(() => (0, errors_js_1.wrapPgError)(err)));
|
|
1613
|
+
return result.rows;
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* @internal The `turbine-orm/prisma-compat` adapter's pool-level raw seam,
|
|
1617
|
+
* the twin of {@link TransactionClient.rawQuery}. NOT application API: use
|
|
1618
|
+
* {@link raw} or {@link sql}, whose tagged templates make concatenating a
|
|
1619
|
+
* value into the SQL text impossible. It exists so the adapter's
|
|
1620
|
+
* `$queryRaw` / `$executeRaw` statements emit a `$on('query')` event (model
|
|
1621
|
+
* `'$raw'`, `action` the adapter's label) instead of running unobserved on
|
|
1622
|
+
* the pool. Errors are translated by `wrapPgError`, like {@link raw}.
|
|
1623
|
+
*/
|
|
1624
|
+
async rawQuery(text, params = [], action = 'rawQuery') {
|
|
1625
|
+
const result = await (0, query_events_js_1.runReportedStatement)(this.queryOptions._onQuery, (0, query_events_js_1.rawIdentity)(action), text, params, () => this.pool.query(text, params), (err) => this.withErrorMode(() => (0, errors_js_1.wrapPgError)(err)));
|
|
1626
|
+
return { rows: result.rows, rowCount: result.rowCount };
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Run `fn` with `tag` attached to every `$on('query')` event it causes, so a
|
|
1630
|
+
* listener can attribute queries to the feature or code path that issued
|
|
1631
|
+
* them. The scope follows async context: queries in awaited helpers,
|
|
1632
|
+
* transactions, pipelines and raw SQL inside `fn` are all tagged. An inner
|
|
1633
|
+
* `$tag` replaces an outer one for its own duration. The tag is event
|
|
1634
|
+
* metadata only and never reaches SQL.
|
|
1635
|
+
*
|
|
1636
|
+
* Throws {@link ValidationError} (E003) for an empty tag or one longer than
|
|
1637
|
+
* {@link MAX_QUERY_TAG_LENGTH} (128) characters.
|
|
1638
|
+
*
|
|
1639
|
+
* @example
|
|
1640
|
+
* ```ts
|
|
1641
|
+
* const order = await db.$tag('checkout', async () => {
|
|
1642
|
+
* const cart = await db.carts.findUnique({ where: { id } });
|
|
1643
|
+
* return db.orders.create({ data: { cartId: cart.id } });
|
|
1644
|
+
* });
|
|
1645
|
+
* ```
|
|
1646
|
+
*/
|
|
1647
|
+
$tag(tag, fn) {
|
|
1648
|
+
return (0, query_events_js_1.runWithQueryTag)(tag, fn);
|
|
1566
1649
|
}
|
|
1567
1650
|
/**
|
|
1568
1651
|
* Execute a **typed** raw SQL query, Turbine's answer to Prisma's TypedSQL.
|
|
@@ -1602,7 +1685,7 @@ class TurbineClient {
|
|
|
1602
1685
|
// `withErrorMode` docstring already claimed to cover the typed-SQL builder
|
|
1603
1686
|
// and did not, which left two raw-SQL entry points disagreeing about the
|
|
1604
1687
|
// same statement, since the adjacent `raw` tag WAS scoped.
|
|
1605
|
-
return new typed_sql_js_1.TypedSqlQuery(this.pool, sql, params, this.logging, (fn) => this.withErrorMode(fn));
|
|
1688
|
+
return new typed_sql_js_1.TypedSqlQuery(this.pool, sql, params, this.logging, (fn) => this.withErrorMode(fn), this.queryOptions._onQuery);
|
|
1606
1689
|
}
|
|
1607
1690
|
// -------------------------------------------------------------------------
|
|
1608
1691
|
// Transaction support (raw, legacy)
|
|
@@ -1842,13 +1925,18 @@ class TurbineClient {
|
|
|
1842
1925
|
if (queries.length === 0) {
|
|
1843
1926
|
return [];
|
|
1844
1927
|
}
|
|
1928
|
+
// One `$on('query')` event per statement, `batch: 'transaction'`, only
|
|
1929
|
+
// when someone is listening; each statement is timed on its own.
|
|
1930
|
+
const sink = this.queryListeners.size > 0 ? this.queryOptions._onQuery : undefined;
|
|
1931
|
+
const reported = (dq, run) => (0, query_events_js_1.runReportedStatement)(sink, { ...(0, query_events_js_1.deferredIdentity)(dq.tag), batch: 'transaction' }, dq.sql, dq.params, run, errors_js_1.wrapPgError);
|
|
1845
1932
|
return this.transaction(async (client) => {
|
|
1846
1933
|
const pipelined = client.supportsPipelining === true &&
|
|
1847
1934
|
this.dialect.resultStrategy !== 'reselect';
|
|
1848
1935
|
if (pipelined) {
|
|
1849
1936
|
// Dispatch every statement before awaiting any reply. The driver's
|
|
1850
|
-
// FIFO guarantee makes settled[i] the reply to queries[i].
|
|
1851
|
-
|
|
1937
|
+
// FIFO guarantee makes settled[i] the reply to queries[i]. Each
|
|
1938
|
+
// statement is timed from its dispatch to its own reply.
|
|
1939
|
+
const settled = await Promise.allSettled(queries.map((dq) => reported(dq, () => client.query(dq.sql, dq.params))));
|
|
1852
1940
|
const results = [];
|
|
1853
1941
|
for (let i = 0; i < settled.length; i++) {
|
|
1854
1942
|
const outcome = settled[i];
|
|
@@ -1861,19 +1949,12 @@ class TurbineClient {
|
|
|
1861
1949
|
}
|
|
1862
1950
|
const results = [];
|
|
1863
1951
|
for (const dq of queries) {
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
this.dialect.resultStrategy === 'reselect' && dq.reselect
|
|
1871
|
-
? await dq.reselect((sql, params) => client.query(sql, params))
|
|
1872
|
-
: await client.query(dq.sql, dq.params);
|
|
1873
|
-
}
|
|
1874
|
-
catch (err) {
|
|
1875
|
-
throw (0, errors_js_1.wrapPgError)(err);
|
|
1876
|
-
}
|
|
1952
|
+
// Non-RETURNING engines (resultStrategy 'reselect', e.g. MySQL) attach a
|
|
1953
|
+
// reselect plan that runs the write plus a follow-up SELECT; running
|
|
1954
|
+
// dq.sql alone would transform a row-less write result.
|
|
1955
|
+
const raw = await reported(dq, () => this.dialect.resultStrategy === 'reselect' && dq.reselect
|
|
1956
|
+
? dq.reselect((sql, params) => client.query(sql, params))
|
|
1957
|
+
: client.query(dq.sql, dq.params));
|
|
1877
1958
|
results.push(dq.transform(raw));
|
|
1878
1959
|
}
|
|
1879
1960
|
return results;
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export { HttpJsonSink, type HttpJsonSinkOptions, type MetricsFlushBatch, type Me
|
|
|
45
45
|
export { executePipeline, type PipelineOptions, type PipelineResults, pipelineSupported } from './pipeline.js';
|
|
46
46
|
export { fingerprintPrismaSchema } from './prisma-schema-fingerprint.js';
|
|
47
47
|
export { type AggregateArgs, type AggregateResult, type ArrayFilter, AUTO_ASSUMED_ROUND_TRIP_MS, AUTO_COUNT_BATCH_MIN_PARENT_ROWS, AUTO_JOIN_PENALTY_MS_PER_ROW, AUTO_TO_ONE_JOIN_MAX_ROWS, AUTO_TO_ONE_JOIN_ROWS_MAX, AUTO_TO_ONE_JOIN_ROWS_MIN, type ColumnRef, type ConnectOrCreateOp, type CountArgs, type CreateArgs, type CreateDataInput, type CreateManyArgs, type DeferredQuery, type DeleteArgs, type DeleteManyArgs, type FieldResult, type FindManyArgs, type FindManyStreamArgs, type FindUniqueArgs, type GlobalFilters, type GroupByAggregateSpec, type GroupByArgs, type GroupByDistinctOn, type GroupByResult, type HavingClause, type JsonEncoding, type JsonFilter, type JsonPathAggregateTarget, type JsonPathGroupKey, type JsonPathOrderBy, type MiddlewareFn, type NestedCreateOp, type NestedUpdateOp, type NestedUpdateOpItem, type NestedUpsertOpItem, type OmitResult, type OrderByClause, type OrderByObject, type OrderBySpec, type OrderDirection, type PrivilegeOption, type QueryEvent, type QueryEventListener, QueryInterface, type QueryResult, type RelationDescriptor, type RelationFilter, type RelationLoadStrategy, type RelationOrderBy, type RelationOrderByChain, type RelationPickBy, type RelationPickOrderBy, type SelectResult, type SkipGlobalFilters, type TemporalInfinityReading, type TextSearchFilter, type TypedWithClause, UNSAFE, type Unsafe, type UpdateArgs, type UpdateDataInput, type UpdateInput, type UpdateManyArgs, type UpdateOperatorInput, type UpsertArgs, type VectorDistanceFilter, type VectorFilter, type VectorMetric, type VectorOrderBy, type VectorOrderByDistance, type WhereClause, type WhereOperator, type WhereValue, type WithClause, type WithOptions, type WithOrderByObject, type WithResult, } from './query/index.js';
|
|
48
|
+
export { MAX_QUERY_TAG_LENGTH, RAW_QUERY_MODEL } from './query-events.js';
|
|
48
49
|
export { type ActiveSubscription, type NotificationHandler, type Subscription, validateChannel } from './realtime.js';
|
|
49
50
|
export type { CheckMetadata, ColumnMetadata, IndexMetadata, PrismaCompatMap, PrismaModelMap, PrismaRelationMap, PrismaSchemaSource, ReferentialAction, RelationDef, SchemaMetadata, TableMetadata, } from './schema.js';
|
|
50
51
|
export { camelToSnake, isDateType, normalizeKeyColumns, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, withDbFieldNames, } from './schema.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
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.fingerprintPrismaSchema = exports.pipelineSupported = exports.executePipeline = exports.PgMetricsSink = exports.HttpJsonSink = exports.hasRelationFields = exports.executeNestedUpdate = exports.executeNestedCreate = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UnsupportedFeatureError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.ReadOnlyError = exports.REDACTED_DETAIL = exports.PipelineError = exports.OptimisticLockError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.ExclusionConstraintError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.withRetry = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
|
|
38
|
-
exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.DestructivePushRefusal = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.applyManyToManyRelations = exports.withDbFieldNames = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.validateChannel = exports.UNSAFE = void 0;
|
|
38
|
+
exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.DestructivePushRefusal = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.applyManyToManyRelations = exports.withDbFieldNames = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.validateChannel = exports.RAW_QUERY_MODEL = exports.MAX_QUERY_TAG_LENGTH = exports.UNSAFE = void 0;
|
|
39
39
|
var index_js_1 = require("./adapters/index.js");
|
|
40
40
|
Object.defineProperty(exports, "alloydb", { enumerable: true, get: function () { return index_js_1.alloydb; } });
|
|
41
41
|
Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return index_js_1.cockroachdb; } });
|
|
@@ -110,6 +110,10 @@ Object.defineProperty(exports, "AUTO_TO_ONE_JOIN_ROWS_MAX", { enumerable: true,
|
|
|
110
110
|
Object.defineProperty(exports, "AUTO_TO_ONE_JOIN_ROWS_MIN", { enumerable: true, get: function () { return index_js_2.AUTO_TO_ONE_JOIN_ROWS_MIN; } });
|
|
111
111
|
Object.defineProperty(exports, "QueryInterface", { enumerable: true, get: function () { return index_js_2.QueryInterface; } });
|
|
112
112
|
Object.defineProperty(exports, "UNSAFE", { enumerable: true, get: function () { return index_js_2.UNSAFE; } });
|
|
113
|
+
// $on('query') event metadata: raw-statement model name, $tag() label limit
|
|
114
|
+
var query_events_js_1 = require("./query-events.js");
|
|
115
|
+
Object.defineProperty(exports, "MAX_QUERY_TAG_LENGTH", { enumerable: true, get: function () { return query_events_js_1.MAX_QUERY_TAG_LENGTH; } });
|
|
116
|
+
Object.defineProperty(exports, "RAW_QUERY_MODEL", { enumerable: true, get: function () { return query_events_js_1.RAW_QUERY_MODEL; } });
|
|
113
117
|
// Realtime, LISTEN/NOTIFY pub/sub
|
|
114
118
|
var realtime_js_1 = require("./realtime.js");
|
|
115
119
|
Object.defineProperty(exports, "validateChannel", { enumerable: true, get: function () { return realtime_js_1.validateChannel; } });
|
|
@@ -174,7 +174,7 @@ export interface CompatTransactionClient {
|
|
|
174
174
|
* breaks atomicity. Optional only so a test stub can omit it: when it is
|
|
175
175
|
* absent the raw methods throw instead of escaping the transaction.
|
|
176
176
|
*/
|
|
177
|
-
rawQuery?(text: string, params?: readonly unknown[]): Promise<{
|
|
177
|
+
rawQuery?(text: string, params?: readonly unknown[], action?: string): Promise<{
|
|
178
178
|
rows: unknown[];
|
|
179
179
|
rowCount: number | null;
|
|
180
180
|
}>;
|
|
@@ -2104,17 +2104,17 @@ function makeRawSurface(exec, ph) {
|
|
|
2104
2104
|
return {
|
|
2105
2105
|
$queryRaw: async (strings, ...values) => {
|
|
2106
2106
|
const { text, params } = flattenTemplate(strings, values, ph);
|
|
2107
|
-
return (await exec(text, params)).rows;
|
|
2107
|
+
return (await exec(text, params, '$queryRaw')).rows;
|
|
2108
2108
|
},
|
|
2109
2109
|
$queryRawUnsafe: async (sql, ...params) => {
|
|
2110
|
-
return (await exec(sql, params)).rows;
|
|
2110
|
+
return (await exec(sql, params, '$queryRawUnsafe')).rows;
|
|
2111
2111
|
},
|
|
2112
2112
|
$executeRaw: async (strings, ...values) => {
|
|
2113
2113
|
const { text, params } = flattenTemplate(strings, values, ph);
|
|
2114
|
-
return (await exec(text, params)).rowCount ?? 0;
|
|
2114
|
+
return (await exec(text, params, '$executeRaw')).rowCount ?? 0;
|
|
2115
2115
|
},
|
|
2116
2116
|
$executeRawUnsafe: async (sql, ...params) => {
|
|
2117
|
-
return (await exec(sql, params)).rowCount ?? 0;
|
|
2117
|
+
return (await exec(sql, params, '$executeRawUnsafe')).rowCount ?? 0;
|
|
2118
2118
|
},
|
|
2119
2119
|
};
|
|
2120
2120
|
}
|
|
@@ -2371,8 +2371,12 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2371
2371
|
baseDelegates.set(prismaModel, makeDelegate(ctx, mm, () => db.table(mm.table), (fn) => db.$transaction((tx) => fn((n) => tx.table(n)))));
|
|
2372
2372
|
}
|
|
2373
2373
|
const ph = placeholderOf(db);
|
|
2374
|
-
|
|
2374
|
+
// Prefer the client's own raw seam, which emits a `$on('query')` event; a
|
|
2375
|
+
// client without one (an older core, a test stub) runs on the pool as before.
|
|
2376
|
+
const runRaw = async (text, params, action) => {
|
|
2375
2377
|
try {
|
|
2378
|
+
if (typeof db.rawQuery === 'function')
|
|
2379
|
+
return await db.rawQuery(text, params, action);
|
|
2376
2380
|
return await poolOf(db).query(text, params);
|
|
2377
2381
|
}
|
|
2378
2382
|
catch (err) {
|
|
@@ -2387,14 +2391,14 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2387
2391
|
* `wrapPgError` is idempotent (it returns an already-typed TurbineError
|
|
2388
2392
|
* untouched), so the error shape matches the pool path exactly.
|
|
2389
2393
|
*/
|
|
2390
|
-
const txRunRaw = (tx) => async (text, params) => {
|
|
2394
|
+
const txRunRaw = (tx) => async (text, params, action) => {
|
|
2391
2395
|
if (typeof tx.rawQuery !== 'function') {
|
|
2392
2396
|
throw decorate(new errors_js_1.ValidationError('prisma-compat: raw SQL inside $transaction needs a transaction client that can execute it ' +
|
|
2393
2397
|
'(core TransactionClient.rawQuery). Refusing to run the statement on a pool connection, which would ' +
|
|
2394
2398
|
'silently place it outside the transaction.'), ctx.options.prismaErrorCodes);
|
|
2395
2399
|
}
|
|
2396
2400
|
try {
|
|
2397
|
-
return await tx.rawQuery(text, params);
|
|
2401
|
+
return await tx.rawQuery(text, params, action);
|
|
2398
2402
|
}
|
|
2399
2403
|
catch (err) {
|
|
2400
2404
|
throw decorate((0, errors_js_1.wrapPgError)(err), ctx.options.prismaErrorCodes);
|
|
@@ -93,6 +93,21 @@ export interface QueryEvent {
|
|
|
93
93
|
* observability can see exactly which queries the auto default re-planned.
|
|
94
94
|
*/
|
|
95
95
|
strategy?: 'auto-batched';
|
|
96
|
+
/**
|
|
97
|
+
* The label of the innermost `db.$tag(label, fn)` scope the query ran in.
|
|
98
|
+
* Absent outside any scope. Lets a listener attribute a query to the feature
|
|
99
|
+
* or code path that issued it, which `model` + `action` cannot.
|
|
100
|
+
*/
|
|
101
|
+
tag?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Set on statements that ran as part of a batch rather than on their own:
|
|
104
|
+
* `'pipeline'` for `db.pipeline(...)`, `'transaction'` for the array form
|
|
105
|
+
* `$transaction([...])`. A pipeline sends every statement in one round trip,
|
|
106
|
+
* so its per-statement `duration` is the batch's wall time divided evenly
|
|
107
|
+
* across the statements (the sum is exact, the split is not). Transaction
|
|
108
|
+
* batch statements are timed individually. Absent for everything else.
|
|
109
|
+
*/
|
|
110
|
+
batch?: 'pipeline' | 'transaction';
|
|
96
111
|
}
|
|
97
112
|
export type QueryEventListener = (event: QueryEvent) => void;
|
|
98
113
|
/** Options passed from TurbineClient to QueryInterface */
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, `$on('query')` event metadata: the raw-statement model name
|
|
3
|
+
* and per-scope query tags for attribution.
|
|
4
|
+
*
|
|
5
|
+
* A tag is a caller-chosen label (`'checkout'`, `'nightly-report'`) that rides
|
|
6
|
+
* on every {@link QueryEvent} emitted while a `db.$tag(label, fn)` callback is
|
|
7
|
+
* running, including queries issued from inside awaited helpers, transactions
|
|
8
|
+
* and pipelines. It exists so telemetry can answer "which feature issued this
|
|
9
|
+
* query", which model + action alone cannot: `orders.findMany` is called from
|
|
10
|
+
* a dozen places.
|
|
11
|
+
*
|
|
12
|
+
* The tag is scoped by async context rather than threaded through every query
|
|
13
|
+
* argument type. A per-query option would have to be added to every args type,
|
|
14
|
+
* the runtime option surface, and the prisma-compat unknown-option check, and
|
|
15
|
+
* it would still miss raw SQL and pipelines. A scope reaches every emitter with
|
|
16
|
+
* one read at the single emit seam in client.ts, and it costs nothing when no
|
|
17
|
+
* `$on('query')` listener is registered, because the store is only read on the
|
|
18
|
+
* emit path.
|
|
19
|
+
*
|
|
20
|
+
* ONE scope per realm. The store hangs off `globalThis` under a
|
|
21
|
+
* `Symbol.for(...)` key for the same reason the warn-once registry does: the
|
|
22
|
+
* package ships ESM and CJS, and a mixed graph loading both copies must still
|
|
23
|
+
* see the tag a caller set through either copy.
|
|
24
|
+
*
|
|
25
|
+
* The tag never reaches SQL. It is metadata on the in-process event only; what
|
|
26
|
+
* a listener does with it (log it, forward it to a collector) is the
|
|
27
|
+
* listener's business.
|
|
28
|
+
*/
|
|
29
|
+
import type { QueryEvent } from './query/deferred.js';
|
|
30
|
+
/**
|
|
31
|
+
* The `model` reported for statements that are not tied to one table: `raw`,
|
|
32
|
+
* `sql`, the transaction-scoped `raw` / `rawQuery`, and prisma-compat's
|
|
33
|
+
* `$queryRaw` / `$executeRaw` family. `action` names the entry point that ran
|
|
34
|
+
* it. The `$` prefix cannot collide with a generated table accessor.
|
|
35
|
+
*/
|
|
36
|
+
export declare const RAW_QUERY_MODEL = "$raw";
|
|
37
|
+
/** Longest tag accepted. Keeps a tag a label, not a payload. */
|
|
38
|
+
export declare const MAX_QUERY_TAG_LENGTH = 128;
|
|
39
|
+
/** The tag in force for the code currently running, or `undefined`. */
|
|
40
|
+
export declare function currentQueryTag(): string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Run `fn` with `tag` attached to every query event it causes. An inner scope
|
|
43
|
+
* replaces an outer one for its own duration. Throws {@link ValidationError}
|
|
44
|
+
* (E003) for a tag that is not a non-empty string of at most
|
|
45
|
+
* {@link MAX_QUERY_TAG_LENGTH} characters, before `fn` runs.
|
|
46
|
+
*/
|
|
47
|
+
export declare function runWithQueryTag<R>(tag: string, fn: () => R): R;
|
|
48
|
+
/** A client's event sink, as QueryInterfaceOptions carries it. */
|
|
49
|
+
type QueryEventSink = ((event: QueryEvent) => void) | undefined;
|
|
50
|
+
/** Rows a driver result reports: the affected count, else the returned rows. */
|
|
51
|
+
export declare function resultRowCount(result: {
|
|
52
|
+
rowCount?: number | null;
|
|
53
|
+
rows?: unknown[];
|
|
54
|
+
} | undefined): number;
|
|
55
|
+
/**
|
|
56
|
+
* Emit one query event for a statement that ran outside a QueryInterface (raw
|
|
57
|
+
* SQL, a pipeline slot, a transaction-batch slot). Never throws: a listener
|
|
58
|
+
* problem must not turn a successful statement into a failed one.
|
|
59
|
+
*/
|
|
60
|
+
export declare function emitStatementEvent(sink: QueryEventSink, event: Omit<QueryEvent, 'timestamp'>): void;
|
|
61
|
+
/**
|
|
62
|
+
* Split a DeferredQuery tag (`'<table>.<action>'`) into the event's model and
|
|
63
|
+
* action. A tag without one is reported as a raw statement.
|
|
64
|
+
*/
|
|
65
|
+
export declare function deferredIdentity(tag: string): {
|
|
66
|
+
model: string;
|
|
67
|
+
action: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Run one statement and report it. `identity` is the event's model and action
|
|
71
|
+
* (plus `batch` for a batch slot); for raw SQL the model is
|
|
72
|
+
* {@link RAW_QUERY_MODEL} and the action names the entry point (`'raw'`,
|
|
73
|
+
* `'sql'`, `'rawQuery'`, `'$queryRaw'` ...). `wrap` turns a driver error into
|
|
74
|
+
* what the caller sees, and the event carries that same error.
|
|
75
|
+
*/
|
|
76
|
+
export declare function runReportedStatement<R extends {
|
|
77
|
+
rowCount?: number | null;
|
|
78
|
+
rows?: unknown[];
|
|
79
|
+
}>(sink: QueryEventSink, identity: Pick<QueryEvent, 'model' | 'action' | 'batch'>, sql: string, params: unknown[], run: () => Promise<R>, wrap: (err: unknown) => unknown): Promise<R>;
|
|
80
|
+
/** The identity of a raw statement run through `action`. */
|
|
81
|
+
export declare const rawIdentity: (action: string) => Pick<QueryEvent, "model" | "action">;
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm, `$on('query')` event metadata: the raw-statement model name
|
|
4
|
+
* and per-scope query tags for attribution.
|
|
5
|
+
*
|
|
6
|
+
* A tag is a caller-chosen label (`'checkout'`, `'nightly-report'`) that rides
|
|
7
|
+
* on every {@link QueryEvent} emitted while a `db.$tag(label, fn)` callback is
|
|
8
|
+
* running, including queries issued from inside awaited helpers, transactions
|
|
9
|
+
* and pipelines. It exists so telemetry can answer "which feature issued this
|
|
10
|
+
* query", which model + action alone cannot: `orders.findMany` is called from
|
|
11
|
+
* a dozen places.
|
|
12
|
+
*
|
|
13
|
+
* The tag is scoped by async context rather than threaded through every query
|
|
14
|
+
* argument type. A per-query option would have to be added to every args type,
|
|
15
|
+
* the runtime option surface, and the prisma-compat unknown-option check, and
|
|
16
|
+
* it would still miss raw SQL and pipelines. A scope reaches every emitter with
|
|
17
|
+
* one read at the single emit seam in client.ts, and it costs nothing when no
|
|
18
|
+
* `$on('query')` listener is registered, because the store is only read on the
|
|
19
|
+
* emit path.
|
|
20
|
+
*
|
|
21
|
+
* ONE scope per realm. The store hangs off `globalThis` under a
|
|
22
|
+
* `Symbol.for(...)` key for the same reason the warn-once registry does: the
|
|
23
|
+
* package ships ESM and CJS, and a mixed graph loading both copies must still
|
|
24
|
+
* see the tag a caller set through either copy.
|
|
25
|
+
*
|
|
26
|
+
* The tag never reaches SQL. It is metadata on the in-process event only; what
|
|
27
|
+
* a listener does with it (log it, forward it to a collector) is the
|
|
28
|
+
* listener's business.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.rawIdentity = exports.MAX_QUERY_TAG_LENGTH = exports.RAW_QUERY_MODEL = void 0;
|
|
32
|
+
exports.currentQueryTag = currentQueryTag;
|
|
33
|
+
exports.runWithQueryTag = runWithQueryTag;
|
|
34
|
+
exports.resultRowCount = resultRowCount;
|
|
35
|
+
exports.emitStatementEvent = emitStatementEvent;
|
|
36
|
+
exports.deferredIdentity = deferredIdentity;
|
|
37
|
+
exports.runReportedStatement = runReportedStatement;
|
|
38
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
39
|
+
const errors_js_1 = require("./errors.js");
|
|
40
|
+
/**
|
|
41
|
+
* The `model` reported for statements that are not tied to one table: `raw`,
|
|
42
|
+
* `sql`, the transaction-scoped `raw` / `rawQuery`, and prisma-compat's
|
|
43
|
+
* `$queryRaw` / `$executeRaw` family. `action` names the entry point that ran
|
|
44
|
+
* it. The `$` prefix cannot collide with a generated table accessor.
|
|
45
|
+
*/
|
|
46
|
+
exports.RAW_QUERY_MODEL = '$raw';
|
|
47
|
+
/** Longest tag accepted. Keeps a tag a label, not a payload. */
|
|
48
|
+
exports.MAX_QUERY_TAG_LENGTH = 128;
|
|
49
|
+
const SCOPE_KEY = Symbol.for('turbine.queryTag.scope');
|
|
50
|
+
function scope() {
|
|
51
|
+
const g = globalThis;
|
|
52
|
+
let store = g[SCOPE_KEY];
|
|
53
|
+
if (!store) {
|
|
54
|
+
store = new node_async_hooks_1.AsyncLocalStorage();
|
|
55
|
+
g[SCOPE_KEY] = store;
|
|
56
|
+
}
|
|
57
|
+
return store;
|
|
58
|
+
}
|
|
59
|
+
/** The tag in force for the code currently running, or `undefined`. */
|
|
60
|
+
function currentQueryTag() {
|
|
61
|
+
return scope().getStore();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Run `fn` with `tag` attached to every query event it causes. An inner scope
|
|
65
|
+
* replaces an outer one for its own duration. Throws {@link ValidationError}
|
|
66
|
+
* (E003) for a tag that is not a non-empty string of at most
|
|
67
|
+
* {@link MAX_QUERY_TAG_LENGTH} characters, before `fn` runs.
|
|
68
|
+
*/
|
|
69
|
+
function runWithQueryTag(tag, fn) {
|
|
70
|
+
if (typeof tag !== 'string' || !tag.trim() || tag.length > exports.MAX_QUERY_TAG_LENGTH) {
|
|
71
|
+
throw new errors_js_1.ValidationError(`$tag() needs a label of 1-${exports.MAX_QUERY_TAG_LENGTH} characters.`);
|
|
72
|
+
}
|
|
73
|
+
return scope().run(tag, fn);
|
|
74
|
+
}
|
|
75
|
+
/** Rows a driver result reports: the affected count, else the returned rows. */
|
|
76
|
+
function resultRowCount(result) {
|
|
77
|
+
return typeof result?.rowCount === 'number' ? result.rowCount : (result?.rows?.length ?? 0);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Emit one query event for a statement that ran outside a QueryInterface (raw
|
|
81
|
+
* SQL, a pipeline slot, a transaction-batch slot). Never throws: a listener
|
|
82
|
+
* problem must not turn a successful statement into a failed one.
|
|
83
|
+
*/
|
|
84
|
+
function emitStatementEvent(sink, event) {
|
|
85
|
+
try {
|
|
86
|
+
sink?.({ ...event, timestamp: new Date() });
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Listener errors must never crash a query.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Split a DeferredQuery tag (`'<table>.<action>'`) into the event's model and
|
|
94
|
+
* action. A tag without one is reported as a raw statement.
|
|
95
|
+
*/
|
|
96
|
+
function deferredIdentity(tag) {
|
|
97
|
+
const at = tag.lastIndexOf('.');
|
|
98
|
+
return at > 0 ? { model: tag.slice(0, at), action: tag.slice(at + 1) } : { model: exports.RAW_QUERY_MODEL, action: tag };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Run one statement and report it. `identity` is the event's model and action
|
|
102
|
+
* (plus `batch` for a batch slot); for raw SQL the model is
|
|
103
|
+
* {@link RAW_QUERY_MODEL} and the action names the entry point (`'raw'`,
|
|
104
|
+
* `'sql'`, `'rawQuery'`, `'$queryRaw'` ...). `wrap` turns a driver error into
|
|
105
|
+
* what the caller sees, and the event carries that same error.
|
|
106
|
+
*/
|
|
107
|
+
async function runReportedStatement(sink, identity, sql, params, run, wrap) {
|
|
108
|
+
const start = performance.now();
|
|
109
|
+
let result;
|
|
110
|
+
let error;
|
|
111
|
+
try {
|
|
112
|
+
result = await run();
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
error = wrap(err);
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
if (sink) {
|
|
121
|
+
emitStatementEvent(sink, {
|
|
122
|
+
...identity,
|
|
123
|
+
sql,
|
|
124
|
+
params,
|
|
125
|
+
duration: performance.now() - start,
|
|
126
|
+
rows: resultRowCount(result),
|
|
127
|
+
...(error === undefined ? {} : { error: error instanceof Error ? error : new Error(String(error)) }),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** The identity of a raw statement run through `action`. */
|
|
133
|
+
const rawIdentity = (action) => ({
|
|
134
|
+
model: exports.RAW_QUERY_MODEL,
|
|
135
|
+
action,
|
|
136
|
+
});
|
|
137
|
+
exports.rawIdentity = rawIdentity;
|