@prisma-next/sql-runtime 0.6.0-dev.5 → 0.6.0-dev.7
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-BSTHn_rH.mjs → exports-BeaTZIiJ.mjs} +171 -174
- package/dist/exports-BeaTZIiJ.mjs.map +1 -0
- package/dist/{index-CTCvZOWI.d.mts → index-qGOr0ruJ.d.mts} +2 -2
- package/dist/index-qGOr0ruJ.d.mts.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/test/utils.d.mts +2 -2
- package/dist/test/utils.d.mts.map +1 -1
- package/dist/test/utils.mjs +61 -6
- package/dist/test/utils.mjs.map +1 -1
- package/package.json +11 -11
- package/src/codecs/ast-codec-resolver.ts +99 -0
- package/src/codecs/decoding.ts +4 -39
- package/src/codecs/encoding.ts +20 -46
- package/src/sql-context.ts +157 -124
- package/src/sql-runtime.ts +0 -5
- package/dist/exports-BSTHn_rH.mjs.map +0 -1
- package/dist/index-CTCvZOWI.d.mts.map +0 -1
- package/src/codecs/alias-resolver.ts +0 -37
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { AsyncIterableResult, RuntimeCore, checkAborted, checkMiddlewareCompatibility, isRuntimeError, raceAgainstAbort, runWithMiddleware, runtimeError } from "@prisma-next/framework-components/runtime";
|
|
2
2
|
import { type } from "arktype";
|
|
3
|
-
import { collectOrderedParamRefs, isQueryAst
|
|
3
|
+
import { collectOrderedParamRefs, isQueryAst } from "@prisma-next/sql-relational-core/ast";
|
|
4
4
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
5
5
|
import { checkContractComponentRequirements } from "@prisma-next/framework-components/components";
|
|
6
6
|
import { createExecutionStack } from "@prisma-next/framework-components/execution";
|
|
7
|
+
import { canonicalizeJson } from "@prisma-next/framework-components/utils";
|
|
7
8
|
import { createSqlOperationRegistry } from "@prisma-next/sql-operations";
|
|
8
9
|
import { buildCodecDescriptorRegistry } from "@prisma-next/sql-relational-core/codec-descriptor-registry";
|
|
9
10
|
import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
|
|
@@ -388,6 +389,46 @@ function lints(options) {
|
|
|
388
389
|
});
|
|
389
390
|
}
|
|
390
391
|
//#endregion
|
|
392
|
+
//#region src/codecs/ast-codec-resolver.ts
|
|
393
|
+
/**
|
|
394
|
+
* Build an {@link AstCodecResolver} bound to a descriptor registry and a per-call instance-context factory.
|
|
395
|
+
*
|
|
396
|
+
* The instance-context factory lets callers control `name` / `usedAt` for refs the AST supplies (e.g. AST-embedded migration ops where the materialisation site is the AST node, not a contract column). The contract-walk pre-population path constructs its own contexts and invokes the resolver with those refs to seed the cache.
|
|
397
|
+
*/
|
|
398
|
+
function createAstCodecResolver(descriptors, instanceContextFor) {
|
|
399
|
+
const cache = /* @__PURE__ */ new Map();
|
|
400
|
+
return { forCodecRef(ref) {
|
|
401
|
+
const key = `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
|
|
402
|
+
const cached = cache.get(key);
|
|
403
|
+
if (cached) return cached;
|
|
404
|
+
const descriptor = descriptors.descriptorFor(ref.codecId);
|
|
405
|
+
if (!descriptor) throw runtimeError("RUNTIME.CODEC_DESCRIPTOR_MISSING", `No codec descriptor registered for codecId '${ref.codecId}'.`, { codecId: ref.codecId });
|
|
406
|
+
const validated = validateTypeParams$1(descriptor.paramsSchema, descriptor.isParameterized && ref.typeParams === void 0 ? {
|
|
407
|
+
...ref,
|
|
408
|
+
typeParams: {}
|
|
409
|
+
} : ref);
|
|
410
|
+
const ctx = instanceContextFor(ref);
|
|
411
|
+
const codec = descriptor.factory(validated)(ctx);
|
|
412
|
+
cache.set(key, codec);
|
|
413
|
+
return codec;
|
|
414
|
+
} };
|
|
415
|
+
}
|
|
416
|
+
function validateTypeParams$1(paramsSchema, ref) {
|
|
417
|
+
const result = paramsSchema["~standard"].validate(ref.typeParams);
|
|
418
|
+
if (result instanceof Promise) throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `paramsSchema for codec '${ref.codecId}' returned a Promise; runtime validation requires a synchronous Standard Schema validator.`, {
|
|
419
|
+
codecId: ref.codecId,
|
|
420
|
+
typeParams: ref.typeParams
|
|
421
|
+
});
|
|
422
|
+
if ("issues" in result && result.issues) {
|
|
423
|
+
const messages = result.issues.map((issue) => issue.message).join("; ");
|
|
424
|
+
throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Invalid typeParams for codec '${ref.codecId}': ${messages}`, {
|
|
425
|
+
codecId: ref.codecId,
|
|
426
|
+
typeParams: ref.typeParams
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
return result.value;
|
|
430
|
+
}
|
|
431
|
+
//#endregion
|
|
391
432
|
//#region src/sql-context.ts
|
|
392
433
|
function createSqlExecutionStack(options) {
|
|
393
434
|
return createExecutionStack({
|
|
@@ -502,93 +543,124 @@ function validateColumnTypeParams(storage, codecDescriptors) {
|
|
|
502
543
|
});
|
|
503
544
|
}
|
|
504
545
|
}
|
|
505
|
-
function isResolvedCodec(candidate) {
|
|
506
|
-
return candidate !== null && typeof candidate === "object" && "id" in candidate && "decode" in candidate;
|
|
507
|
-
}
|
|
508
546
|
/**
|
|
509
|
-
*
|
|
547
|
+
* Build-time contract-integrity check: every `(table, column)` resolves to a {@link CodecRef} whose `codecId` is registered and whose `typeParams` presence matches the descriptor's `isParameterized` flag.
|
|
548
|
+
*
|
|
549
|
+
* Surfaces three classes of malformed contract that AST-bound codec resolution would otherwise mask silently:
|
|
510
550
|
*
|
|
511
|
-
* -
|
|
512
|
-
* -
|
|
513
|
-
* -
|
|
551
|
+
* - column references a codecId no contributor registered → `RUNTIME.CODEC_DESCRIPTOR_MISSING`.
|
|
552
|
+
* - parameterized codec, no `typeParams` (legacy "tolerate refs without params" shape) → `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH`.
|
|
553
|
+
* - non-parameterized codec, `typeParams` supplied → `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH`.
|
|
514
554
|
*
|
|
515
|
-
*
|
|
555
|
+
* Runs unconditionally from `createExecutionContext` so contract bugs fail fast at construction time instead of silently skipping affected columns in the codec registry's pre-population walk.
|
|
516
556
|
*/
|
|
517
|
-
function
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
if (descriptor
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
|
|
557
|
+
function assertColumnCodecIntegrity(storage, codecDescriptors) {
|
|
558
|
+
for (const [tableName, table] of Object.entries(storage.tables)) for (const columnName of Object.keys(table.columns)) {
|
|
559
|
+
const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
|
|
560
|
+
if (!ref) continue;
|
|
561
|
+
const descriptor = codecDescriptors.descriptorFor(ref.codecId);
|
|
562
|
+
if (!descriptor) throw runtimeError("RUNTIME.CODEC_DESCRIPTOR_MISSING", `Column '${tableName}.${columnName}' references codec '${ref.codecId}' but no contributor registered a codec descriptor for that codecId. Add the extension pack that owns the codec to the runtime stack.`, {
|
|
563
|
+
table: tableName,
|
|
564
|
+
column: columnName,
|
|
565
|
+
codecId: ref.codecId
|
|
566
|
+
});
|
|
567
|
+
if (descriptor.isParameterized && ref.typeParams === void 0) {
|
|
568
|
+
const probe = descriptor.paramsSchema["~standard"].validate({});
|
|
569
|
+
if (probe instanceof Promise) {
|
|
570
|
+
probe.catch(() => {});
|
|
571
|
+
throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Column '${tableName}.${columnName}' uses parameterized codec '${ref.codecId}' whose paramsSchema returned a Promise; paramsSchema must be a synchronous Standard Schema validator. Return a value/issues result directly instead of a Promise.`, {
|
|
572
|
+
table: tableName,
|
|
573
|
+
column: columnName,
|
|
574
|
+
codecId: ref.codecId
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
if ("issues" in probe && !!probe.issues) throw runtimeError("RUNTIME.CODEC_PARAMETERIZATION_MISMATCH", `Column '${tableName}.${columnName}' uses parameterized codec '${ref.codecId}' but no typeParams are supplied. Provide typeParams on the column, or use a typeRef pointing at a storage.types entry that carries them.`, {
|
|
578
|
+
table: tableName,
|
|
579
|
+
column: columnName,
|
|
580
|
+
codecId: ref.codecId,
|
|
581
|
+
expected: "parameterized",
|
|
582
|
+
actual: "no typeParams"
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
if (!descriptor.isParameterized && ref.typeParams !== void 0) throw runtimeError("RUNTIME.CODEC_PARAMETERIZATION_MISMATCH", `Column '${tableName}.${columnName}' supplies typeParams to non-parameterized codec '${ref.codecId}'. Remove the typeParams or switch to a parameterized codec id.`, {
|
|
586
|
+
table: tableName,
|
|
587
|
+
column: columnName,
|
|
588
|
+
codecId: ref.codecId,
|
|
589
|
+
expected: "non-parameterized",
|
|
590
|
+
actual: "has typeParams"
|
|
591
|
+
});
|
|
529
592
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Build a {@link ContractCodecRegistry} that resolves codecs exclusively through the `forCodecRef` content-keyed cache.
|
|
596
|
+
*
|
|
597
|
+
* One pre-population pass walks `storage.types` and `storage.tables[].columns[]` to seed the resolver's per-ref instance context with the *aggregated* `usedAt` set for each canonical `(codecId, typeParams)` key. The same codec materialised through `forColumn` or `forCodecRef` is therefore one instance with one `SqlCodecInstanceContext` — stateful codecs reading `usedAt` see the full column set regardless of which surface the caller used.
|
|
598
|
+
*
|
|
599
|
+
* Per-key instance-name policy:
|
|
600
|
+
*
|
|
601
|
+
* - typeRef-shared columns use the `storage.types[name]` name.
|
|
602
|
+
* - inline-`typeParams` columns use `<col:Table.column>` (the first column observed at that key; additional columns sharing the key extend `usedAt`).
|
|
603
|
+
* - non-parameterized codec ids use `<codec:codecId>`, aggregating every column on that codec id into one `usedAt` set.
|
|
604
|
+
* - ad-hoc refs the contract walk did not pre-populate (e.g. AST-supplied refs from deserialised migration ops) fall back to the canonical cache key `${codecId}:${canonicalizeJson(typeParams)}` — the only structurally honest identity for an ad-hoc ref, distinct per `(codecId, typeParams)`.
|
|
605
|
+
*
|
|
606
|
+
* Contract integrity is enforced upstream by {@link assertColumnCodecIntegrity}: every column must reference a registered `codecId` whose `descriptor.isParameterized` flag matches the presence of `typeParams` (via `codecRefForColumn`). The pre-population walk and `forColumn` therefore make no defensive checks — malformed columns fail fast at `createExecutionContext` construction with `RUNTIME.CODEC_DESCRIPTOR_MISSING` or `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH` rather than being silently skipped here.
|
|
607
|
+
*
|
|
608
|
+
* `forColumn(t, c)` is a thin delegate over `forCodecRef(codecRefForColumn(t, c))`; encode/decode hot paths read the resolver directly via `forCodecRef`. The only `undefined` `forColumn` returns is the legitimate "no such column in the contract" case.
|
|
609
|
+
*/
|
|
610
|
+
function buildContractCodecRegistry(contract, codecDescriptors) {
|
|
611
|
+
const refKeyOf = (ref) => `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
|
|
612
|
+
const usedAtByKey = /* @__PURE__ */ new Map();
|
|
613
|
+
const nameByKey = /* @__PURE__ */ new Map();
|
|
614
|
+
const typeRefSites = collectTypeRefSites(contract.storage);
|
|
615
|
+
for (const [typeName, typeInstance] of Object.entries(contract.storage.types ?? {})) {
|
|
616
|
+
const key = refKeyOf({
|
|
617
|
+
codecId: typeInstance.codecId,
|
|
618
|
+
typeParams: typeInstance.typeParams
|
|
619
|
+
});
|
|
620
|
+
const sites = typeRefSites.get(typeName) ?? [];
|
|
621
|
+
const existing = usedAtByKey.get(key);
|
|
622
|
+
if (existing) existing.push(...sites);
|
|
623
|
+
else {
|
|
624
|
+
usedAtByKey.set(key, [...sites]);
|
|
625
|
+
nameByKey.set(key, typeName);
|
|
626
|
+
}
|
|
541
627
|
}
|
|
542
628
|
for (const [tableName, table] of Object.entries(contract.storage.tables)) for (const [columnName, column] of Object.entries(table.columns)) {
|
|
543
|
-
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
});
|
|
558
|
-
const ctx = {
|
|
559
|
-
name: `<col:${tableName}.${columnName}>`,
|
|
560
|
-
usedAt: [{
|
|
561
|
-
table: tableName,
|
|
562
|
-
column: columnName
|
|
563
|
-
}]
|
|
564
|
-
};
|
|
565
|
-
resolvedCodec = parameterizedDescriptor.factory(validatedParams)(ctx);
|
|
566
|
-
}
|
|
567
|
-
} else if (!isParameterized) {
|
|
568
|
-
const ctx = {
|
|
569
|
-
name: `<col:${tableName}.${columnName}>`,
|
|
570
|
-
usedAt: [{
|
|
571
|
-
table: tableName,
|
|
572
|
-
column: columnName
|
|
573
|
-
}]
|
|
574
|
-
};
|
|
575
|
-
resolvedCodec = descriptor.factory.bind(descriptor)(void 0)(ctx);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
if (resolvedCodec) {
|
|
579
|
-
byColumn.set(columnKey, resolvedCodec);
|
|
580
|
-
const existing = byCodecId.get(column.codecId);
|
|
581
|
-
if (existing === void 0) byCodecId.set(column.codecId, resolvedCodec);
|
|
582
|
-
else if (existing !== resolvedCodec && parameterizedDescriptors.has(column.codecId)) ambiguousCodecIds.add(column.codecId);
|
|
629
|
+
if (column.typeRef !== void 0) continue;
|
|
630
|
+
const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
|
|
631
|
+
if (!ref) continue;
|
|
632
|
+
const key = refKeyOf(ref);
|
|
633
|
+
const site = {
|
|
634
|
+
table: tableName,
|
|
635
|
+
column: columnName
|
|
636
|
+
};
|
|
637
|
+
const existing = usedAtByKey.get(key);
|
|
638
|
+
if (existing) existing.push(site);
|
|
639
|
+
else {
|
|
640
|
+
usedAtByKey.set(key, [site]);
|
|
641
|
+
const name = ref.typeParams !== void 0 ? `<col:${tableName}.${columnName}>` : `<codec:${ref.codecId}>`;
|
|
642
|
+
nameByKey.set(key, name);
|
|
583
643
|
}
|
|
584
644
|
}
|
|
645
|
+
const resolver = createAstCodecResolver(codecDescriptors, (ref) => {
|
|
646
|
+
const key = refKeyOf(ref);
|
|
647
|
+
return {
|
|
648
|
+
name: nameByKey.get(key) ?? key,
|
|
649
|
+
usedAt: usedAtByKey.get(key) ?? []
|
|
650
|
+
};
|
|
651
|
+
});
|
|
652
|
+
for (const [tableName, table] of Object.entries(contract.storage.tables)) for (const columnName of Object.keys(table.columns)) {
|
|
653
|
+
const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
|
|
654
|
+
if (!ref) continue;
|
|
655
|
+
resolver.forCodecRef(ref);
|
|
656
|
+
}
|
|
585
657
|
return {
|
|
586
658
|
forColumn(table, column) {
|
|
587
|
-
|
|
659
|
+
const ref = codecDescriptors.codecRefForColumn(table, column);
|
|
660
|
+
return ref ? resolver.forCodecRef(ref) : void 0;
|
|
588
661
|
},
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
return byCodecId.get(codecId) ?? parameterizedRepresentatives.get(codecId);
|
|
662
|
+
forCodecRef(ref) {
|
|
663
|
+
return resolver.forCodecRef(ref);
|
|
592
664
|
}
|
|
593
665
|
};
|
|
594
666
|
}
|
|
@@ -683,14 +755,15 @@ function createExecutionContext(options) {
|
|
|
683
755
|
const ops = contributor.queryOperations?.() ?? {};
|
|
684
756
|
for (const [name, op] of Object.entries(ops)) queryOperationRegistry.register(name, op);
|
|
685
757
|
}
|
|
686
|
-
const codecDescriptors = buildCodecDescriptorRegistry(allCodecDescriptors);
|
|
758
|
+
const codecDescriptors = buildCodecDescriptorRegistry(allCodecDescriptors, contract.storage);
|
|
759
|
+
assertColumnCodecIntegrity(contract.storage, codecDescriptors);
|
|
687
760
|
const mutationDefaultGeneratorRegistry = collectMutationDefaultGenerators(contributors);
|
|
688
761
|
assertMutationDefaultGeneratorsAvailable(contract, mutationDefaultGeneratorRegistry);
|
|
689
762
|
if (parameterizedCodecDescriptors.size > 0) validateColumnTypeParams(contract.storage, parameterizedCodecDescriptors);
|
|
690
763
|
const types = initializeTypeHelpers(contract.storage, parameterizedCodecDescriptors);
|
|
691
764
|
return {
|
|
692
765
|
contract,
|
|
693
|
-
contractCodecs: buildContractCodecRegistry(contract, codecDescriptors
|
|
766
|
+
contractCodecs: buildContractCodecRegistry(contract, codecDescriptors),
|
|
694
767
|
codecDescriptors,
|
|
695
768
|
queryOperations: queryOperationRegistry,
|
|
696
769
|
types,
|
|
@@ -817,33 +890,6 @@ function writeContractMarker(input) {
|
|
|
817
890
|
};
|
|
818
891
|
}
|
|
819
892
|
//#endregion
|
|
820
|
-
//#region src/codecs/alias-resolver.ts
|
|
821
|
-
/**
|
|
822
|
-
* Build a map from query-local table aliases to their underlying source table names.
|
|
823
|
-
*
|
|
824
|
-
* Self-joins like `db.sql.post.as('p1').innerJoin(db.sql.post.as('p2'), …)` produce `ColumnRef`s whose `table` is the alias (`p1`, `p2`) — the SQL renderer needs the alias for `SELECT p1.id, …`. Codec dispatch keys `byColumn` by the underlying source table, so aliases must be resolved back to the source name for `forColumn(...)` to hit. Tables that already use their canonical name (no alias) are also entered so a single
|
|
825
|
-
* lookup works for both shapes.
|
|
826
|
-
*/
|
|
827
|
-
function buildAliasMap(ast) {
|
|
828
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
829
|
-
const recordSource = (source) => {
|
|
830
|
-
if (source.kind === "table-source") {
|
|
831
|
-
const key = source.alias ?? source.name;
|
|
832
|
-
aliases.set(key, source.name);
|
|
833
|
-
} else aliases.set(source.alias, source.alias);
|
|
834
|
-
};
|
|
835
|
-
if (ast.kind === "select") {
|
|
836
|
-
recordSource(ast.from);
|
|
837
|
-
for (const join of ast.joins ?? []) recordSource(join.source);
|
|
838
|
-
} else if (ast.kind === "raw-sql") {} else recordSource(ast.table);
|
|
839
|
-
return aliases;
|
|
840
|
-
}
|
|
841
|
-
function makeAliasResolver(ast) {
|
|
842
|
-
if (!ast) return (alias) => alias;
|
|
843
|
-
const map = buildAliasMap(ast);
|
|
844
|
-
return (alias) => map.get(alias) ?? alias;
|
|
845
|
-
}
|
|
846
|
-
//#endregion
|
|
847
893
|
//#region src/codecs/decoding.ts
|
|
848
894
|
const WIRE_PREVIEW_LIMIT = 100;
|
|
849
895
|
const EMPTY_INCLUDE_ALIASES = /* @__PURE__ */ new Set();
|
|
@@ -855,30 +901,8 @@ function projectionListFromAst(ast) {
|
|
|
855
901
|
if (ast.kind === "raw-sql") return;
|
|
856
902
|
return ast.returning;
|
|
857
903
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
*
|
|
861
|
-
* When a `(table, column)` ref is available — either implicit on a `column-ref` expression or carried explicitly via `item.refs` for column-bound non-`column-ref` projections — prefer `contractCodecs.forColumn(table, column)`: that returns the per-instance codec materialized from the descriptor's factory for that column, encoding any per-instance state (typeParams like vector length, schema validators, etc.).
|
|
862
|
-
*
|
|
863
|
-
* The wrong-instance risk for parameterized codecs is closed off structurally:
|
|
864
|
-
*
|
|
865
|
-
* 1. `buildContractCodecRegistry` pre-populates `byCodecId` with one canonical instance per non-parameterized descriptor; parameterized descriptors are intentionally absent. 2. `forCodecId` rejects ambiguous parameterized fallbacks (`ambiguousCodecIds`). 3. The non-ambiguous parameterized case stores the column-correct per-instance codec under `byCodecId`, so the fall-through still resolves to the right instance.
|
|
866
|
-
*
|
|
867
|
-
* The `forCodecId` fallback otherwise covers projections that are *not* column-bound (computed projections, raw SQL aliases) but still carry a `codecId` (ADR 205 stamps every `ProjectionItem` with the producer's codec id).
|
|
868
|
-
*
|
|
869
|
-
* Codec-registry-unification spec § AC-4 / AC-5.
|
|
870
|
-
*/
|
|
871
|
-
function resolveProjectionCodec(item, contractCodecs, aliasResolver) {
|
|
872
|
-
if (contractCodecs) {
|
|
873
|
-
if (item.expr.kind === "column-ref") {
|
|
874
|
-
const byColumn = contractCodecs.forColumn(aliasResolver(item.expr.table), item.expr.column);
|
|
875
|
-
if (byColumn && (item.codecId === void 0 || byColumn.id === item.codecId)) return byColumn;
|
|
876
|
-
} else if (item.refs) {
|
|
877
|
-
const byColumn = contractCodecs.forColumn(aliasResolver(item.refs.table), item.refs.column);
|
|
878
|
-
if (byColumn && (item.codecId === void 0 || byColumn.id === item.codecId)) return byColumn;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
if (item.codecId) return contractCodecs?.forCodecId(item.codecId);
|
|
904
|
+
function resolveProjectionCodec(item, contractCodecs) {
|
|
905
|
+
if (item.codec && contractCodecs) return contractCodecs.forCodecRef(item.codec);
|
|
882
906
|
}
|
|
883
907
|
function buildDecodeContext(plan, contractCodecs) {
|
|
884
908
|
if (!isAstBackedPlan(plan)) return {
|
|
@@ -898,19 +922,14 @@ function buildDecodeContext(plan, contractCodecs) {
|
|
|
898
922
|
const codecs = /* @__PURE__ */ new Map();
|
|
899
923
|
const columnRefs = /* @__PURE__ */ new Map();
|
|
900
924
|
const includeAliases = /* @__PURE__ */ new Set();
|
|
901
|
-
const aliasResolver = makeAliasResolver(plan.ast);
|
|
902
925
|
for (const item of projection) {
|
|
903
926
|
aliases.push(item.alias);
|
|
904
|
-
const codec = resolveProjectionCodec(item, contractCodecs
|
|
927
|
+
const codec = resolveProjectionCodec(item, contractCodecs);
|
|
905
928
|
if (codec) codecs.set(item.alias, codec);
|
|
906
929
|
if (item.expr.kind === "column-ref") columnRefs.set(item.alias, {
|
|
907
|
-
table:
|
|
930
|
+
table: item.expr.table,
|
|
908
931
|
column: item.expr.column
|
|
909
932
|
});
|
|
910
|
-
else if (item.refs) columnRefs.set(item.alias, {
|
|
911
|
-
table: aliasResolver(item.refs.table),
|
|
912
|
-
column: item.refs.column
|
|
913
|
-
});
|
|
914
933
|
else if (item.expr.kind === "subquery" || item.expr.kind === "json-array-agg") includeAliases.add(item.alias);
|
|
915
934
|
}
|
|
916
935
|
return {
|
|
@@ -1035,29 +1054,11 @@ async function decodeRow(row, plan, rowCtx, contractCodecs) {
|
|
|
1035
1054
|
//#endregion
|
|
1036
1055
|
//#region src/codecs/encoding.ts
|
|
1037
1056
|
const NO_METADATA = Object.freeze({
|
|
1038
|
-
|
|
1039
|
-
name: void 0
|
|
1040
|
-
refs: void 0
|
|
1057
|
+
codec: void 0,
|
|
1058
|
+
name: void 0
|
|
1041
1059
|
});
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
*
|
|
1045
|
-
* Column-aware dispatch: when `metadata.refs` is populated by a column-bound construction site, prefer `contractCodecs.forColumn(refs.table, refs.column)` — that returns the per-instance codec the contract walk materialized for the `(table, column)` pair, encoding the column's typeParams (e.g. `vector(1024)` vs. `vector(1536)`).
|
|
1046
|
-
*
|
|
1047
|
-
* On a column-lookup miss the resolver falls through to `forCodecId`. The wrong-instance risk for parameterized codecs is closed off structurally:
|
|
1048
|
-
*
|
|
1049
|
-
* 1. `buildContractCodecRegistry` pre-populates `byCodecId` with one canonical instance per non-parameterized descriptor; parameterized descriptors are intentionally absent from this pre-population. 2. `forCodecId` rejects ambiguous parameterized fallbacks (`ambiguousCodecIds`) — if the contract walk resolved more than one distinct instance under a single parameterized id, the call throws rather than binding to
|
|
1050
|
-
* whichever landed first. 3. For the non-ambiguous parameterized case (a single column with that id), `byCodecId` stores the column-correct per-instance codec, so the fall-through still resolves to the right instance.
|
|
1051
|
-
*
|
|
1052
|
-
* Refs-less fallback: ParamRefs constructed outside a column-bound site (literals, transient builder state) carry a non-parameterized `codecId` whose dispatch is ambiguity-free. The validator pass (`validateParamRefRefs`) already enforced refs on every parameterized ParamRef before encode runs.
|
|
1053
|
-
*/
|
|
1054
|
-
function resolveParamCodec(metadata, contractCodecs, aliasResolver) {
|
|
1055
|
-
if (!metadata.codecId) return void 0;
|
|
1056
|
-
if (metadata.refs && contractCodecs) {
|
|
1057
|
-
const byColumn = contractCodecs.forColumn(aliasResolver(metadata.refs.table), metadata.refs.column);
|
|
1058
|
-
if (byColumn && byColumn.id === metadata.codecId) return byColumn;
|
|
1059
|
-
}
|
|
1060
|
-
return contractCodecs?.forCodecId(metadata.codecId);
|
|
1060
|
+
function resolveParamCodec(metadata, contractCodecs) {
|
|
1061
|
+
if (metadata.codec && contractCodecs) return contractCodecs.forCodecRef(metadata.codec);
|
|
1061
1062
|
}
|
|
1062
1063
|
function paramLabel(metadata, paramIndex) {
|
|
1063
1064
|
return metadata.name ?? `param[${paramIndex}]`;
|
|
@@ -1072,13 +1073,14 @@ function wrapEncodeFailure(error, metadata, paramIndex, codecId) {
|
|
|
1072
1073
|
wrapped.cause = error;
|
|
1073
1074
|
throw wrapped;
|
|
1074
1075
|
}
|
|
1075
|
-
async function encodeParamValue(value, metadata, paramIndex, ctx, contractCodecs
|
|
1076
|
+
async function encodeParamValue(value, metadata, paramIndex, ctx, contractCodecs) {
|
|
1076
1077
|
if (value === null || value === void 0) return null;
|
|
1077
|
-
const codec = resolveParamCodec(metadata, contractCodecs
|
|
1078
|
+
const codec = resolveParamCodec(metadata, contractCodecs);
|
|
1078
1079
|
if (!codec) return value;
|
|
1079
1080
|
try {
|
|
1080
1081
|
return await codec.encode(value, ctx);
|
|
1081
1082
|
} catch (error) {
|
|
1083
|
+
if (isRuntimeError(error)) throw error;
|
|
1082
1084
|
wrapEncodeFailure(error, metadata, paramIndex, codec.id);
|
|
1083
1085
|
}
|
|
1084
1086
|
}
|
|
@@ -1102,15 +1104,13 @@ async function encodeParams(plan, ctx, contractCodecs) {
|
|
|
1102
1104
|
for (let i = 0; i < paramCount && i < refs.length; i++) {
|
|
1103
1105
|
const ref = refs[i];
|
|
1104
1106
|
if (ref) metadata[i] = {
|
|
1105
|
-
|
|
1106
|
-
name: ref.name
|
|
1107
|
-
refs: ref.refs
|
|
1107
|
+
codec: ref.codec,
|
|
1108
|
+
name: ref.name
|
|
1108
1109
|
};
|
|
1109
1110
|
}
|
|
1110
1111
|
}
|
|
1111
|
-
const aliasResolver = makeAliasResolver(plan.ast);
|
|
1112
1112
|
const tasks = new Array(paramCount);
|
|
1113
|
-
for (let i = 0; i < paramCount; i++) tasks[i] = encodeParamValue(plan.params[i], metadata[i] ?? NO_METADATA, i, ctx, contractCodecs
|
|
1113
|
+
for (let i = 0; i < paramCount; i++) tasks[i] = encodeParamValue(plan.params[i], metadata[i] ?? NO_METADATA, i, ctx, contractCodecs);
|
|
1114
1114
|
const settled = await raceAgainstAbort(Promise.all(tasks), signal, "encode");
|
|
1115
1115
|
return Object.freeze(settled);
|
|
1116
1116
|
}
|
|
@@ -1271,7 +1271,6 @@ var SqlRuntimeImpl = class extends RuntimeCore {
|
|
|
1271
1271
|
* `ctx: SqlCodecCallContext` is forwarded to `encodeParams` so per-query cancellation reaches every codec body during parameter encoding. The framework abstract typed this as `CodecCallContext`; the SQL family narrows it to the SQL-specific extension. SQL params do not populate `ctx.column` — encode-side column metadata is the middleware's domain.
|
|
1272
1272
|
*/
|
|
1273
1273
|
async lower(plan, ctx) {
|
|
1274
|
-
validateParamRefRefs(plan.ast, this.codecDescriptors);
|
|
1275
1274
|
const lowered = lowerSqlPlan(this.adapter, this.contract, plan);
|
|
1276
1275
|
return Object.freeze({
|
|
1277
1276
|
...lowered,
|
|
@@ -1318,13 +1317,11 @@ var SqlRuntimeImpl = class extends RuntimeCore {
|
|
|
1318
1317
|
const generator = async function* () {
|
|
1319
1318
|
checkAborted(codecCtx, "stream");
|
|
1320
1319
|
let exec;
|
|
1321
|
-
if (isExecutionPlan(plan)) {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
});
|
|
1327
|
-
} else exec = await self.lower(await self.runBeforeCompile(plan), codecCtx);
|
|
1320
|
+
if (isExecutionPlan(plan)) exec = Object.freeze({
|
|
1321
|
+
...plan,
|
|
1322
|
+
params: await encodeParams(plan, codecCtx, self.contractCodecs)
|
|
1323
|
+
});
|
|
1324
|
+
else exec = await self.lower(await self.runBeforeCompile(plan), codecCtx);
|
|
1328
1325
|
self.familyAdapter.validatePlan(exec, self.contract);
|
|
1329
1326
|
self._telemetry = null;
|
|
1330
1327
|
if (!self.startupVerified && self.verify.mode === "startup") await self.verifyMarker();
|
|
@@ -1513,4 +1510,4 @@ function createRuntime(options) {
|
|
|
1513
1510
|
//#endregion
|
|
1514
1511
|
export { ensureTableStatement as a, createExecutionContext as c, budgets as d, parseContractMarkerRow as f, validateContractCodecMappings as g, validateCodecRegistryCompleteness as h, ensureSchemaStatement as i, createSqlExecutionStack as l, extractCodecIds as m, withTransaction as n, readContractMarker as o, lowerSqlPlan as p, APP_SPACE_ID as r, writeContractMarker as s, createRuntime as t, lints as u };
|
|
1515
1512
|
|
|
1516
|
-
//# sourceMappingURL=exports-
|
|
1513
|
+
//# sourceMappingURL=exports-BeaTZIiJ.mjs.map
|