@prisma-next/sql-runtime 0.9.0-dev.1 → 0.9.0-dev.3
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/{exports-C2zFVOFS.mjs → exports-DHQOSWty.mjs} +231 -64
- package/dist/exports-DHQOSWty.mjs.map +1 -0
- package/dist/{index-BhG8bX80.d.mts → index-B2sP_QGE.d.mts} +101 -52
- package/dist/index-B2sP_QGE.d.mts.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/test/utils.d.mts +4 -3
- package/dist/test/utils.d.mts.map +1 -1
- package/dist/test/utils.mjs +15 -4
- package/dist/test/utils.mjs.map +1 -1
- package/package.json +12 -12
- package/src/codecs/decoding.ts +6 -25
- package/src/codecs/encoding.ts +29 -20
- package/src/exports/index.ts +10 -0
- package/src/lower-sql-plan.ts +16 -5
- package/src/middleware/budgets.ts +4 -1
- package/src/prepared/bind-site-params.ts +41 -0
- package/src/prepared/encode-prepared.ts +41 -0
- package/src/prepared/prepared-statement.ts +50 -0
- package/src/prepared/types.ts +65 -0
- package/src/sql-runtime.ts +289 -55
- package/dist/exports-C2zFVOFS.mjs.map +0 -1
- package/dist/index-BhG8bX80.d.mts.map +0 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { JsonValue, PlanMeta } from '@prisma-next/contract/types';
|
|
2
|
+
import type {
|
|
3
|
+
AsyncIterableResult,
|
|
4
|
+
RuntimeExecuteOptions,
|
|
5
|
+
} from '@prisma-next/framework-components/runtime';
|
|
6
|
+
import type { AnyQueryAst, LoweredParam } from '@prisma-next/sql-relational-core/ast';
|
|
7
|
+
import type {
|
|
8
|
+
CodecTypesBase,
|
|
9
|
+
CodecValue,
|
|
10
|
+
Expression,
|
|
11
|
+
} from '@prisma-next/sql-relational-core/expression';
|
|
12
|
+
import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
|
|
13
|
+
import type { RuntimeQueryable } from '../sql-runtime';
|
|
14
|
+
|
|
15
|
+
export type ParamSpec<CT extends CodecTypesBase = CodecTypesBase> =
|
|
16
|
+
| (keyof CT & string)
|
|
17
|
+
| {
|
|
18
|
+
readonly codecId: keyof CT & string;
|
|
19
|
+
readonly typeParams?: JsonValue;
|
|
20
|
+
readonly nullable?: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type Declaration<CT extends CodecTypesBase = CodecTypesBase> = Readonly<
|
|
24
|
+
Record<string, ParamSpec<CT>>
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
export type DeclaredCodecId<S> = S extends string
|
|
28
|
+
? S
|
|
29
|
+
: S extends { readonly codecId: infer C extends string }
|
|
30
|
+
? C
|
|
31
|
+
: never;
|
|
32
|
+
|
|
33
|
+
export type DeclaredNullable<S> = S extends { readonly nullable: true } ? true : false;
|
|
34
|
+
|
|
35
|
+
export type BindSiteParams<D> = {
|
|
36
|
+
readonly [K in keyof D]: Expression<{
|
|
37
|
+
codecId: DeclaredCodecId<D[K]>;
|
|
38
|
+
nullable: DeclaredNullable<D[K]>;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type ParamsFromDeclaration<D, CT extends CodecTypesBase> = {
|
|
43
|
+
readonly [K in keyof D]: CodecValue<DeclaredCodecId<D[K]>, DeclaredNullable<D[K]>, CT>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type PrepareCallback<D, Row> = (params: BindSiteParams<D>) => SqlQueryPlan<Row>;
|
|
47
|
+
|
|
48
|
+
export interface PreparedStatement<Params, Row> {
|
|
49
|
+
readonly sql: string;
|
|
50
|
+
readonly ast: AnyQueryAst;
|
|
51
|
+
readonly meta: PlanMeta;
|
|
52
|
+
readonly slots: readonly LoweredParam[];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Run this prepared statement against the given target. The target carries
|
|
56
|
+
* the execution scope (top-level runtime, an explicit connection, an active
|
|
57
|
+
* transaction). It is required and explicit — there is no implicit binding
|
|
58
|
+
* back to the runtime that produced this statement.
|
|
59
|
+
*/
|
|
60
|
+
execute(
|
|
61
|
+
target: RuntimeQueryable,
|
|
62
|
+
params: Params,
|
|
63
|
+
options?: RuntimeExecuteOptions,
|
|
64
|
+
): AsyncIterableResult<Row>;
|
|
65
|
+
}
|
package/src/sql-runtime.ts
CHANGED
|
@@ -21,11 +21,14 @@ import type {
|
|
|
21
21
|
AnyQueryAst,
|
|
22
22
|
ContractCodecRegistry,
|
|
23
23
|
LoweredStatement,
|
|
24
|
+
PreparedExecuteRequest,
|
|
24
25
|
SqlCodecCallContext,
|
|
25
26
|
SqlDriver,
|
|
26
27
|
SqlQueryable,
|
|
27
28
|
SqlTransaction,
|
|
28
29
|
} from '@prisma-next/sql-relational-core/ast';
|
|
30
|
+
import { collectOrderedParamRefs } from '@prisma-next/sql-relational-core/ast';
|
|
31
|
+
import type { CodecTypesBase } from '@prisma-next/sql-relational-core/expression';
|
|
29
32
|
import {
|
|
30
33
|
createSqlParamRefMutator,
|
|
31
34
|
type SqlParamRefMutator,
|
|
@@ -35,14 +38,26 @@ import type { SqlExecutionPlan, SqlQueryPlan } from '@prisma-next/sql-relational
|
|
|
35
38
|
import type { CodecDescriptorRegistry } from '@prisma-next/sql-relational-core/query-lane-context';
|
|
36
39
|
import type { RuntimeScope } from '@prisma-next/sql-relational-core/types';
|
|
37
40
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
38
|
-
import { decodeRow } from './codecs/decoding';
|
|
39
|
-
import { encodeParams } from './codecs/encoding';
|
|
41
|
+
import { buildDecodeContext, type DecodeContext, decodeRow } from './codecs/decoding';
|
|
42
|
+
import { deriveParamMetadata, encodeParams, encodeParamsWithMetadata } from './codecs/encoding';
|
|
40
43
|
import { validateCodecRegistryCompleteness } from './codecs/validation';
|
|
41
44
|
import { computeSqlContentHash } from './content-hash';
|
|
42
45
|
import { computeSqlFingerprint } from './fingerprint';
|
|
43
46
|
import { lowerSqlPlan } from './lower-sql-plan';
|
|
44
47
|
import { runBeforeCompileChain } from './middleware/before-compile-chain';
|
|
45
48
|
import type { SqlMiddleware, SqlMiddlewareContext } from './middleware/sql-middleware';
|
|
49
|
+
import { buildBindSiteParams } from './prepared/bind-site-params';
|
|
50
|
+
import { resolvePreparedSlotValues } from './prepared/encode-prepared';
|
|
51
|
+
import {
|
|
52
|
+
PreparedStatementImpl,
|
|
53
|
+
type PreparedStatementInternals,
|
|
54
|
+
} from './prepared/prepared-statement';
|
|
55
|
+
import type {
|
|
56
|
+
Declaration,
|
|
57
|
+
ParamsFromDeclaration,
|
|
58
|
+
PrepareCallback,
|
|
59
|
+
PreparedStatement,
|
|
60
|
+
} from './prepared/types';
|
|
46
61
|
import type {
|
|
47
62
|
RuntimeFamilyAdapter,
|
|
48
63
|
RuntimeTelemetryEvent,
|
|
@@ -91,6 +106,16 @@ export interface Runtime extends RuntimeQueryable {
|
|
|
91
106
|
connection(): Promise<RuntimeConnection>;
|
|
92
107
|
telemetry(): RuntimeTelemetryEvent | null;
|
|
93
108
|
close(): Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Build a reusable {@link PreparedStatement}. Throws
|
|
112
|
+
* `RUNTIME.PREPARE_UNUSED_PARAM` if any declared name is unreferenced
|
|
113
|
+
* by the callback's plan.
|
|
114
|
+
*/
|
|
115
|
+
prepare<D extends Declaration<CT>, Row, CT extends CodecTypesBase = CodecTypesBase>(
|
|
116
|
+
declaration: D,
|
|
117
|
+
callback: PrepareCallback<D, Row>,
|
|
118
|
+
): Promise<PreparedStatement<ParamsFromDeclaration<D, CT>, Row>>;
|
|
94
119
|
}
|
|
95
120
|
|
|
96
121
|
export interface RuntimeConnection extends RuntimeQueryable {
|
|
@@ -114,7 +139,19 @@ export interface RuntimeTransaction extends RuntimeQueryable {
|
|
|
114
139
|
rollback(): Promise<void>;
|
|
115
140
|
}
|
|
116
141
|
|
|
117
|
-
export interface RuntimeQueryable extends RuntimeScope {
|
|
142
|
+
export interface RuntimeQueryable extends RuntimeScope {
|
|
143
|
+
/**
|
|
144
|
+
* Run a prepared statement against this scope. Required for the explicit
|
|
145
|
+
* `PreparedStatement.execute(target, params)` API — every scope (top-level
|
|
146
|
+
* runtime, connection, transaction) routes prepared executions through the
|
|
147
|
+
* `SqlQueryable` it is backed by.
|
|
148
|
+
*/
|
|
149
|
+
executePrepared<Params, Row>(
|
|
150
|
+
ps: PreparedStatement<Params, Row>,
|
|
151
|
+
params: Params,
|
|
152
|
+
options?: RuntimeExecuteOptions,
|
|
153
|
+
): AsyncIterableResult<Row>;
|
|
154
|
+
}
|
|
118
155
|
|
|
119
156
|
export interface TransactionContext extends RuntimeQueryable {
|
|
120
157
|
readonly invalidated: boolean;
|
|
@@ -142,6 +179,7 @@ class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorag
|
|
|
142
179
|
private readonly codecDescriptors: CodecDescriptorRegistry;
|
|
143
180
|
private readonly sqlCtx: SqlMiddlewareContext;
|
|
144
181
|
private readonly verify: RuntimeVerifyOptions;
|
|
182
|
+
readonly #preparedStatementHandles = new WeakMap<object, unknown>();
|
|
145
183
|
private codecRegistryValidated: boolean;
|
|
146
184
|
private verified: boolean;
|
|
147
185
|
private startupVerified: boolean;
|
|
@@ -275,6 +313,86 @@ class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorag
|
|
|
275
313
|
return this.executeAgainstQueryable<Row>(plan, this.driver, options);
|
|
276
314
|
}
|
|
277
315
|
|
|
316
|
+
executePrepared<Params, Row>(
|
|
317
|
+
ps: PreparedStatement<Params, Row>,
|
|
318
|
+
params: Params,
|
|
319
|
+
options?: RuntimeExecuteOptions,
|
|
320
|
+
): AsyncIterableResult<Row> {
|
|
321
|
+
return this.executePreparedAgainstQueryable<Params, Row>(
|
|
322
|
+
ps as PreparedStatementImpl<Params, Row>,
|
|
323
|
+
params as Record<string, unknown>,
|
|
324
|
+
this.driver,
|
|
325
|
+
options,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private async *streamRows<Row>(
|
|
330
|
+
exec: SqlExecutionPlan,
|
|
331
|
+
decodeContext: DecodeContext,
|
|
332
|
+
driverCall: () => AsyncIterable<Record<string, unknown>>,
|
|
333
|
+
codecCtx: SqlCodecCallContext,
|
|
334
|
+
execMiddlewareCtx: RuntimeMiddlewareContext,
|
|
335
|
+
): AsyncGenerator<Row, void, unknown> {
|
|
336
|
+
this.familyAdapter.validatePlan(exec, this.contract);
|
|
337
|
+
this._telemetry = null;
|
|
338
|
+
|
|
339
|
+
if (!this.startupVerified && this.verify.mode === 'startup') {
|
|
340
|
+
await this.verifyMarker();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (!this.verified && this.verify.mode === 'onFirstUse') {
|
|
344
|
+
await this.verifyMarker();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const startedAt = Date.now();
|
|
348
|
+
let outcome: TelemetryOutcome | null = null;
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
if (this.verify.mode === 'always') {
|
|
352
|
+
await this.verifyMarker();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const stream = runWithMiddleware<SqlExecutionPlan, Record<string, unknown>>(
|
|
356
|
+
exec,
|
|
357
|
+
this.middleware,
|
|
358
|
+
execMiddlewareCtx,
|
|
359
|
+
driverCall,
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
// Manually drive the driver's async iterator so the between-row
|
|
363
|
+
// abort check fires *before* requesting the next row. With a
|
|
364
|
+
// `for await...of` loop the runtime would await `iterator.next()`
|
|
365
|
+
// first, leaving a window where one extra row is pulled through
|
|
366
|
+
// the driver after the signal aborted.
|
|
367
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
368
|
+
try {
|
|
369
|
+
while (true) {
|
|
370
|
+
checkAborted(codecCtx, 'stream');
|
|
371
|
+
const next = await iterator.next();
|
|
372
|
+
if (next.done) {
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
const decodedRow = await decodeRow(next.value, decodeContext, codecCtx);
|
|
376
|
+
yield decodedRow as Row;
|
|
377
|
+
}
|
|
378
|
+
} finally {
|
|
379
|
+
// Best-effort iterator cleanup so the driver can release its
|
|
380
|
+
// resources whether the stream finished normally, threw, or was
|
|
381
|
+
// abandoned by the consumer.
|
|
382
|
+
await iterator.return?.();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
outcome = 'success';
|
|
386
|
+
} catch (error) {
|
|
387
|
+
outcome = 'runtime-error';
|
|
388
|
+
throw error;
|
|
389
|
+
} finally {
|
|
390
|
+
if (outcome !== null) {
|
|
391
|
+
this.recordTelemetry(exec, outcome, Date.now() - startedAt);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
278
396
|
private executeAgainstQueryable<Row>(
|
|
279
397
|
plan: SqlExecutionPlan<unknown> | SqlQueryPlan<unknown>,
|
|
280
398
|
queryable: SqlQueryable,
|
|
@@ -350,65 +468,138 @@ class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorag
|
|
|
350
468
|
exec = await self.encodeDraftParams(draftWithMutations, codecCtx);
|
|
351
469
|
}
|
|
352
470
|
|
|
353
|
-
|
|
354
|
-
self._telemetry = null;
|
|
471
|
+
const decodeContext = buildDecodeContext(exec.ast, self.contractCodecs);
|
|
355
472
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
473
|
+
yield* self.streamRows<Row>(
|
|
474
|
+
exec,
|
|
475
|
+
decodeContext,
|
|
476
|
+
() => queryable.execute<Record<string, unknown>>({ sql: exec.sql, params: exec.params }),
|
|
477
|
+
codecCtx,
|
|
478
|
+
execMiddlewareCtx,
|
|
479
|
+
);
|
|
480
|
+
};
|
|
359
481
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
482
|
+
return new AsyncIterableResult(generator());
|
|
483
|
+
}
|
|
363
484
|
|
|
364
|
-
|
|
365
|
-
|
|
485
|
+
async prepare<D extends Declaration<CT>, Row, CT extends CodecTypesBase = CodecTypesBase>(
|
|
486
|
+
declaration: D,
|
|
487
|
+
callback: PrepareCallback<D, Row>,
|
|
488
|
+
): Promise<PreparedStatement<ParamsFromDeclaration<D, CT>, Row>> {
|
|
489
|
+
this.ensureCodecRegistryValidated();
|
|
366
490
|
|
|
367
|
-
|
|
368
|
-
if (self.verify.mode === 'always') {
|
|
369
|
-
await self.verifyMarker();
|
|
370
|
-
}
|
|
491
|
+
const bindSiteParams = buildBindSiteParams(declaration);
|
|
371
492
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
execMiddlewareCtx,
|
|
376
|
-
() =>
|
|
377
|
-
queryable.execute<Record<string, unknown>>({
|
|
378
|
-
sql: exec.sql,
|
|
379
|
-
// `beforeExecute` ran on the pre-encode draft (see
|
|
380
|
-
// generator setup above); `exec.params` already carries
|
|
381
|
-
// any mutator-driven replacements through `encodeParams`.
|
|
382
|
-
params: exec.params,
|
|
383
|
-
}),
|
|
384
|
-
);
|
|
493
|
+
const userPlan = callback(bindSiteParams);
|
|
494
|
+
const finalPlan = await this.runBeforeCompile(userPlan);
|
|
495
|
+
const orderedRefs = collectOrderedParamRefs(finalPlan.ast);
|
|
385
496
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// Best-effort iterator cleanup so the driver can release its resources whether the stream finished normally, threw, or was abandoned by the consumer.
|
|
400
|
-
await iterator.return?.();
|
|
401
|
-
}
|
|
497
|
+
// Type-level detection isn't achievable across chained-builder generics.
|
|
498
|
+
const referencedNames = new Set<string>();
|
|
499
|
+
for (const ref of orderedRefs) {
|
|
500
|
+
if (ref.kind === 'prepared-param-ref') referencedNames.add(ref.name);
|
|
501
|
+
}
|
|
502
|
+
const missing = Object.keys(declaration).filter((name) => !referencedNames.has(name));
|
|
503
|
+
if (missing.length > 0) {
|
|
504
|
+
throw runtimeError(
|
|
505
|
+
'RUNTIME.PREPARE_UNUSED_PARAM',
|
|
506
|
+
`Prepared statement declaration includes parameter${missing.length === 1 ? '' : 's'} not referenced by the callback's plan: ${missing.join(', ')}`,
|
|
507
|
+
{ unused: missing },
|
|
508
|
+
);
|
|
509
|
+
}
|
|
402
510
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
511
|
+
const lowered = this.adapter.lower(finalPlan.ast, {
|
|
512
|
+
contract: this.contract,
|
|
513
|
+
params: orderedRefs.map((r) => (r.kind === 'param-ref' ? r.value : undefined)),
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
const decodeContext = buildDecodeContext(finalPlan.ast, this.contractCodecs);
|
|
517
|
+
const paramMetadata = deriveParamMetadata(finalPlan.ast);
|
|
518
|
+
|
|
519
|
+
const internals: PreparedStatementInternals = Object.freeze({
|
|
520
|
+
sql: lowered.sql,
|
|
521
|
+
ast: finalPlan.ast,
|
|
522
|
+
meta: finalPlan.meta,
|
|
523
|
+
slots: lowered.params,
|
|
524
|
+
decodeContext,
|
|
525
|
+
paramMetadata,
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
return new PreparedStatementImpl<ParamsFromDeclaration<D, CT>, Row>(internals);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private executePreparedAgainstQueryable<P, Row>(
|
|
532
|
+
ps: PreparedStatementImpl<P, Row>,
|
|
533
|
+
userParams: Record<string, unknown>,
|
|
534
|
+
queryable: SqlQueryable,
|
|
535
|
+
options?: RuntimeExecuteOptions,
|
|
536
|
+
): AsyncIterableResult<Row> {
|
|
537
|
+
this.ensureCodecRegistryValidated();
|
|
538
|
+
|
|
539
|
+
const self = this;
|
|
540
|
+
const signal = options?.signal;
|
|
541
|
+
const scope = options?.scope ?? 'runtime';
|
|
542
|
+
const codecCtx: SqlCodecCallContext = signal === undefined ? {} : { signal };
|
|
543
|
+
const execMiddlewareCtx: RuntimeMiddlewareContext = {
|
|
544
|
+
...self.ctx,
|
|
545
|
+
...ifDefined('signal', signal),
|
|
546
|
+
...(scope !== 'runtime' ? { scope } : {}),
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
const generator = async function* (): AsyncGenerator<Row, void, unknown> {
|
|
550
|
+
checkAborted(codecCtx, 'stream');
|
|
551
|
+
|
|
552
|
+
// Resolve slot order to unencoded values so `beforeExecute`'s
|
|
553
|
+
// mutator sees pre-encode user values for prepared-param slots
|
|
554
|
+
// and can override them before encode runs.
|
|
555
|
+
const preEncodeValues = resolvePreparedSlotValues(ps, userParams);
|
|
556
|
+
const preEncodeExec: SqlExecutionPlan = {
|
|
557
|
+
sql: ps.sql,
|
|
558
|
+
params: preEncodeValues,
|
|
559
|
+
ast: ps.ast,
|
|
560
|
+
meta: ps.meta,
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const mutator: SqlParamRefMutatorInternal = createSqlParamRefMutator(preEncodeExec);
|
|
564
|
+
await runBeforeExecuteChain<SqlExecutionPlan, SqlParamRefMutator>(
|
|
565
|
+
preEncodeExec,
|
|
566
|
+
self.middleware,
|
|
567
|
+
execMiddlewareCtx,
|
|
568
|
+
mutator,
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
const encodedParams = await encodeParamsWithMetadata(
|
|
572
|
+
mutator.currentParams(),
|
|
573
|
+
ps.paramMetadata,
|
|
574
|
+
codecCtx,
|
|
575
|
+
self.contractCodecs,
|
|
576
|
+
);
|
|
577
|
+
const exec: SqlExecutionPlan = {
|
|
578
|
+
sql: ps.sql,
|
|
579
|
+
params: encodedParams,
|
|
580
|
+
ast: ps.ast,
|
|
581
|
+
meta: ps.meta,
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
const handles = self.#preparedStatementHandles;
|
|
585
|
+
const request: PreparedExecuteRequest = {
|
|
586
|
+
sql: exec.sql,
|
|
587
|
+
params: exec.params,
|
|
588
|
+
handle: {
|
|
589
|
+
get: () => handles.get(ps),
|
|
590
|
+
set: (value) => {
|
|
591
|
+
handles.set(ps, value);
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
yield* self.streamRows<Row>(
|
|
597
|
+
exec,
|
|
598
|
+
ps.decodeContext,
|
|
599
|
+
() => queryable.executePrepared<Record<string, unknown>>(request),
|
|
600
|
+
codecCtx,
|
|
601
|
+
execMiddlewareCtx,
|
|
602
|
+
);
|
|
412
603
|
};
|
|
413
604
|
|
|
414
605
|
return new AsyncIterableResult(generator());
|
|
@@ -438,6 +629,18 @@ class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorag
|
|
|
438
629
|
scope: 'connection',
|
|
439
630
|
});
|
|
440
631
|
},
|
|
632
|
+
executePrepared<Params, Row>(
|
|
633
|
+
ps: PreparedStatement<Params, Row>,
|
|
634
|
+
params: Params,
|
|
635
|
+
options?: RuntimeExecuteOptions,
|
|
636
|
+
): AsyncIterableResult<Row> {
|
|
637
|
+
return self.executePreparedAgainstQueryable<Params, Row>(
|
|
638
|
+
ps as PreparedStatementImpl<Params, Row>,
|
|
639
|
+
params as Record<string, unknown>,
|
|
640
|
+
driverConn,
|
|
641
|
+
{ ...options, scope: 'connection' },
|
|
642
|
+
);
|
|
643
|
+
},
|
|
441
644
|
};
|
|
442
645
|
|
|
443
646
|
return wrappedConnection;
|
|
@@ -461,6 +664,18 @@ class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorag
|
|
|
461
664
|
scope: 'transaction',
|
|
462
665
|
});
|
|
463
666
|
},
|
|
667
|
+
executePrepared<Params, Row>(
|
|
668
|
+
ps: PreparedStatement<Params, Row>,
|
|
669
|
+
params: Params,
|
|
670
|
+
options?: RuntimeExecuteOptions,
|
|
671
|
+
): AsyncIterableResult<Row> {
|
|
672
|
+
return self.executePreparedAgainstQueryable<Params, Row>(
|
|
673
|
+
ps as PreparedStatementImpl<Params, Row>,
|
|
674
|
+
params as Record<string, unknown>,
|
|
675
|
+
driverTx,
|
|
676
|
+
{ ...options, scope: 'transaction' },
|
|
677
|
+
);
|
|
678
|
+
},
|
|
464
679
|
};
|
|
465
680
|
}
|
|
466
681
|
|
|
@@ -580,6 +795,25 @@ export async function withTransaction<R>(
|
|
|
580
795
|
};
|
|
581
796
|
return new AsyncIterableResult(guarded());
|
|
582
797
|
},
|
|
798
|
+
executePrepared<Params, Row>(
|
|
799
|
+
ps: PreparedStatement<Params, Row>,
|
|
800
|
+
params: Params,
|
|
801
|
+
options?: RuntimeExecuteOptions,
|
|
802
|
+
): AsyncIterableResult<Row> {
|
|
803
|
+
if (invalidated) {
|
|
804
|
+
throw transactionClosedError();
|
|
805
|
+
}
|
|
806
|
+
const inner = transaction.executePrepared(ps, params, options);
|
|
807
|
+
const guarded = async function* (): AsyncGenerator<Row, void, unknown> {
|
|
808
|
+
for await (const row of inner) {
|
|
809
|
+
if (invalidated) {
|
|
810
|
+
throw transactionClosedError();
|
|
811
|
+
}
|
|
812
|
+
yield row;
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
return new AsyncIterableResult(guarded());
|
|
816
|
+
},
|
|
583
817
|
};
|
|
584
818
|
|
|
585
819
|
let connectionDisposed = false;
|