@prisma/orm-family-sql 8.0.0-rc.1-dev.46 → 8.0.0-rc.10-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -6
- package/dist/{ast-Lbl4VwqP.mjs → ast-5tg0fGPQ.mjs} +2 -58
- package/dist/ast-5tg0fGPQ.mjs.map +1 -0
- package/dist/{ast-HFcfQZr-.d.mts → ast-DuLukI6f.d.mts} +4 -36
- package/dist/ast-DuLukI6f.d.mts.map +1 -0
- package/dist/{authored-check-naming-Hf9990DV.mjs → authored-check-naming-BEvfamnI.mjs} +2 -2
- package/dist/{authored-check-naming-Hf9990DV.mjs.map → authored-check-naming-BEvfamnI.mjs.map} +1 -1
- package/dist/{authoring-type-constructors-Blqoa2Ua-DqvNCOb2.mjs → authoring-type-constructors-BD-XuavC-CJV-TxFh.mjs} +10 -4
- package/dist/authoring-type-constructors-BD-XuavC-CJV-TxFh.mjs.map +1 -0
- package/dist/builder.d.mts +3 -3
- package/dist/builder__runtime.d.mts +26 -9
- package/dist/builder__runtime.d.mts.map +1 -1
- package/dist/builder__runtime.mjs +18 -9
- package/dist/builder__runtime.mjs.map +1 -1
- package/dist/builder__types.d.mts +3 -3
- package/dist/{codec-descriptor-registry-N0dhtKaH.mjs → codec-descriptor-registry-ECZ4ohtG.mjs} +2 -2
- package/dist/{codec-descriptor-registry-N0dhtKaH.mjs.map → codec-descriptor-registry-ECZ4ohtG.mjs.map} +1 -1
- package/dist/{config-types-gAm54C_v.mjs → config-types-BNJgLUUQ.mjs} +2 -2
- package/dist/{config-types-gAm54C_v.mjs.map → config-types-BNJgLUUQ.mjs.map} +1 -1
- package/dist/{config-types-D9fdKG3g.d.mts → config-types-Bd3ud-mX.d.mts} +3 -3
- package/dist/{config-types-D9fdKG3g.d.mts.map → config-types-Bd3ud-mX.d.mts.map} +1 -1
- package/dist/{contract-builder-D4a5dT7j.mjs → contract-builder-BOmEsrRi.mjs} +24 -5
- package/dist/contract-builder-BOmEsrRi.mjs.map +1 -0
- package/dist/{contract-builder-CG_tGDbB.d.mts → contract-builder-De_sioHE.d.mts} +15 -3
- package/dist/contract-builder-De_sioHE.d.mts.map +1 -0
- package/dist/contract-emitter.mjs +1 -1
- package/dist/contract-psl.d.mts +1 -1
- package/dist/contract-psl.mjs +1 -1
- package/dist/contract-psl__attribute-specs.d.mts +77 -0
- package/dist/contract-psl__attribute-specs.d.mts.map +1 -0
- package/dist/contract-psl__attribute-specs.mjs +2 -0
- package/dist/contract-psl__provider.d.mts +1 -1
- package/dist/contract-psl__provider.mjs +3 -3
- package/dist/contract-psl__provider.mjs.map +1 -1
- package/dist/contract-ts.d.mts +3 -3
- package/dist/contract-ts.mjs +3 -3
- package/dist/contract-ts__config-types.d.mts +1 -1
- package/dist/contract-ts__config-types.mjs +1 -1
- package/dist/contract-ts__contract-builder.d.mts +2 -2
- package/dist/contract-ts__contract-builder.mjs +2 -2
- package/dist/contract.d.mts +3 -3
- package/dist/contract.mjs +6 -6
- package/dist/contract__authored-check-naming.mjs +1 -1
- package/dist/contract__foreign-key-materialization.mjs +1 -1
- package/dist/contract__index-naming.mjs +1 -1
- package/dist/contract__types.d.mts +2 -2
- package/dist/contract__types.mjs +2 -2
- package/dist/contract__validators.d.mts +2 -2
- package/dist/contract__validators.mjs +2 -2
- package/dist/{control-D3cB5YQp.mjs → control-BIIMOKMy.mjs} +11 -9
- package/dist/control-BIIMOKMy.mjs.map +1 -0
- package/dist/{control-adapter-C99J7aEF-B7eQd7vD.d.mts → control-adapter-C99J7aEF-GODO43Ai.d.mts} +5 -5
- package/dist/{control-adapter-C99J7aEF-B7eQd7vD.d.mts.map → control-adapter-C99J7aEF-GODO43Ai.d.mts.map} +1 -1
- package/dist/control-adapter-DzTFiMaG.d.mts +1 -0
- package/dist/{control-DTLJ1WtZ.d.mts → control-b-6t8eI_.d.mts} +138 -19
- package/dist/control-b-6t8eI_.d.mts.map +1 -0
- package/dist/{db-D18PrXKW-DvTv4JFn.d.mts → db-BhQdL6Za-DubRGJvv.d.mts} +86 -62
- package/dist/{db-D18PrXKW-DvTv4JFn.d.mts.map → db-BhQdL6Za-DubRGJvv.d.mts.map} +1 -1
- package/dist/{ddl-types-CXx4vPz6-BsgrnSUY.d.mts → ddl-types-CXx4vPz6-Dko-GTTj.d.mts} +3 -3
- package/dist/{ddl-types-CXx4vPz6-BsgrnSUY.d.mts.map → ddl-types-CXx4vPz6-Dko-GTTj.d.mts.map} +1 -1
- package/dist/{derived-checks-DFMRmiAu-DGfB8aS4.mjs → derived-checks-DGjY5t7S-B3txNm7X.mjs} +48 -15
- package/dist/derived-checks-DGjY5t7S-B3txNm7X.mjs.map +1 -0
- package/dist/{derived-checks-DPdD01O0-DGqOFjs6.d.mts → derived-checks-DPdD01O0-DNBs1ZDA.d.mts} +2 -2
- package/dist/{derived-checks-DPdD01O0-DGqOFjs6.d.mts.map → derived-checks-DPdD01O0-DNBs1ZDA.d.mts.map} +1 -1
- package/dist/{diff-DbTNVm2f.d.mts → diff-CN1ou7vM.d.mts} +5 -5
- package/dist/{diff-DbTNVm2f.d.mts.map → diff-CN1ou7vM.d.mts.map} +1 -1
- package/dist/{dist-ZK6oeI5e.mjs → dist-D5__KJs-.mjs} +2 -1
- package/dist/dist-D5__KJs-.mjs.map +1 -0
- package/dist/{expression-CX5mTA-z.mjs → expression-B2Ox7zKM.mjs} +6 -2
- package/dist/expression-B2Ox7zKM.mjs.map +1 -0
- package/dist/{expression-FJK6Ar-d.d.mts → expression-mku9I7Ue.d.mts} +16 -7
- package/dist/{expression-FJK6Ar-d.d.mts.map → expression-mku9I7Ue.d.mts.map} +1 -1
- package/dist/family.d.mts +10 -10
- package/dist/family.mjs +8 -8
- package/dist/family__control-adapter.d.mts +2 -2
- package/dist/family__control.d.mts +4 -4
- package/dist/family__control.mjs +4 -4
- package/dist/family__diff.d.mts +2 -2
- package/dist/family__diff.mjs +1 -1
- package/dist/family__ir.d.mts +1 -1
- package/dist/family__ir.mjs +1 -1
- package/dist/family__migration.d.mts +1 -1
- package/dist/family__pack.d.mts +72 -0
- package/dist/family__pack.d.mts.map +1 -1
- package/dist/family__pack.mjs +4 -2
- package/dist/family__pack.mjs.map +1 -1
- package/dist/family__psl-infer.mjs +1 -1
- package/dist/family__runtime.d.mts +1 -1
- package/dist/family__runtime.mjs +1 -1
- package/dist/family__verify.mjs +1 -1
- package/dist/{foreign-key-materialization-Cu7-7LHl.mjs → foreign-key-materialization-0TrwfShD.mjs} +2 -2
- package/dist/{foreign-key-materialization-Cu7-7LHl.mjs.map → foreign-key-materialization-0TrwfShD.mjs.map} +1 -1
- package/dist/{index-C5I8D-MB.d.mts → index-CLAshwKN.d.mts} +2 -2
- package/dist/{index-C5I8D-MB.d.mts.map → index-CLAshwKN.d.mts.map} +1 -1
- package/dist/{index-C-vQOMY_.d.mts → index-DzBzeTAy.d.mts} +10 -10
- package/dist/{index-C-vQOMY_.d.mts.map → index-DzBzeTAy.d.mts.map} +1 -1
- package/dist/{index-naming-DREt6js7-CRH27yUv.mjs → index-naming-fQMLqyC4-4FzPVtCR.mjs} +4 -4
- package/dist/index-naming-fQMLqyC4-4FzPVtCR.mjs.map +1 -0
- package/dist/{interpreter-DyiUUAWh-DYdjIpiE.mjs → interpreter-CZ_iVQV4-CJ6COgSE.mjs} +123 -302
- package/dist/interpreter-CZ_iVQV4-CJ6COgSE.mjs.map +1 -0
- package/dist/ir-I-xciGUH.mjs.map +1 -1
- package/dist/{ir-Bghjt-FG.d.mts → ir-R9GE8gsm.d.mts} +2 -2
- package/dist/{ir-Bghjt-FG.d.mts.map → ir-R9GE8gsm.d.mts.map} +1 -1
- package/dist/{middleware-AhTz1BOW-th-Suxuf.d.mts → middleware-AhTz1BOW-CtQWS4mb.d.mts} +2 -2
- package/dist/{middleware-AhTz1BOW-th-Suxuf.d.mts.map → middleware-AhTz1BOW-CtQWS4mb.d.mts.map} +1 -1
- package/dist/middleware-Cev-ZFyS.d.mts +1 -0
- package/dist/{migration-CjDRZNep.d.mts → migration-Bhce5i_m.d.mts} +3 -3
- package/dist/{migration-CjDRZNep.d.mts.map → migration-Bhce5i_m.d.mts.map} +1 -1
- package/dist/operations.d.mts +1 -1
- package/dist/orm-client.d.mts +131 -95
- package/dist/orm-client.d.mts.map +1 -1
- package/dist/orm-client.mjs +1021 -942
- package/dist/orm-client.mjs.map +1 -1
- package/dist/plan-C7VP7jOh.d.mts +2 -0
- package/dist/{plan-CDxduYaN-pLIyyJaB.d.mts → plan-CDxduYaN-B2kMLIQQ.d.mts} +3 -3
- package/dist/{plan-CDxduYaN-pLIyyJaB.d.mts.map → plan-CDxduYaN-B2kMLIQQ.d.mts.map} +1 -1
- package/dist/{prepared-statement-9QpkE6JG-C430V3MO.d.mts → prepared-statement-9QpkE6JG-B9xqBu5T.d.mts} +16 -16
- package/dist/{prepared-statement-9QpkE6JG-C430V3MO.d.mts.map → prepared-statement-9QpkE6JG-B9xqBu5T.d.mts.map} +1 -1
- package/dist/{psl-infer-Dk5kgKre.mjs → psl-infer-TBwKfKgU.mjs} +2 -2
- package/dist/{psl-infer-Dk5kgKre.mjs.map → psl-infer-TBwKfKgU.mjs.map} +1 -1
- package/dist/query-lane-context-CGdMIEuv.d.mts +1 -0
- package/dist/{query-lane-context-Qa-eddrE-kuZ8Z86-.d.mts → query-lane-context-Qa-eddrE-BQV6J9nV.d.mts} +4 -4
- package/dist/{query-lane-context-Qa-eddrE-kuZ8Z86-.d.mts.map → query-lane-context-Qa-eddrE-BQV6J9nV.d.mts.map} +1 -1
- package/dist/relational-core.d.mts +12 -12
- package/dist/relational-core.mjs +3 -3
- package/dist/relational-core__aggregate-descriptor-registry.d.mts +1 -1
- package/dist/relational-core__ast.d.mts +4 -4
- package/dist/relational-core__ast.mjs +2 -2
- package/dist/relational-core__codec-descriptor-registry.d.mts +3 -3
- package/dist/relational-core__codec-descriptor-registry.mjs +1 -1
- package/dist/relational-core__contract-free.d.mts +3 -3
- package/dist/relational-core__expression.d.mts +1 -1
- package/dist/relational-core__expression.mjs +1 -1
- package/dist/relational-core__middleware.d.mts +2 -2
- package/dist/relational-core__plan.d.mts +3 -3
- package/dist/relational-core__query-lane-context.d.mts +2 -2
- package/dist/relational-core__types.d.mts +2 -2
- package/dist/{runtime-DpKF2Yu2.d.mts → runtime-CzCx0TKe.d.mts} +3 -3
- package/dist/{runtime-DpKF2Yu2.d.mts.map → runtime-CzCx0TKe.d.mts.map} +1 -1
- package/dist/{runtime-tIHpGCcG.mjs → runtime-PknWErjr.mjs} +2 -2
- package/dist/{runtime-tIHpGCcG.mjs.map → runtime-PknWErjr.mjs.map} +1 -1
- package/dist/runtime.d.mts +4 -4
- package/dist/runtime.mjs +2 -2
- package/dist/runtime__internal__prepared-query.d.mts +1 -1
- package/dist/{schema-differ-DpG_gnur-DFAtGezx.d.mts → schema-differ-DpG_gnur-BICVtTHY.d.mts} +2 -2
- package/dist/{schema-differ-DpG_gnur-DFAtGezx.d.mts.map → schema-differ-DpG_gnur-BICVtTHY.d.mts.map} +1 -1
- package/dist/schema-ir.mjs +1 -1
- package/dist/schema-ir__types.mjs +1 -1
- package/dist/{schema-verify-C_PEdlAr-Dxxjm6EM.mjs → schema-verify-C_PEdlAr-CmXrFuW1.mjs} +2 -2
- package/dist/{schema-verify-C_PEdlAr-Dxxjm6EM.mjs.map → schema-verify-C_PEdlAr-CmXrFuW1.mjs.map} +1 -1
- package/dist/sql-attribute-specs-CG0yitvD-CG-0eW98.mjs +275 -0
- package/dist/sql-attribute-specs-CG0yitvD-CG-0eW98.mjs.map +1 -0
- package/dist/{sql-contract-serializer-2oBWuxTe-DKZTf2n8.mjs → sql-contract-serializer-Bxu4a5oQ-D0lKpb4a.mjs} +24 -5
- package/dist/sql-contract-serializer-Bxu4a5oQ-D0lKpb4a.mjs.map +1 -0
- package/dist/{sql-execution-plan--jKyLpe5-CMGhgO0M.d.mts → sql-execution-plan--jKyLpe5-Bcz9QM2s.d.mts} +2 -2
- package/dist/{sql-execution-plan--jKyLpe5-CMGhgO0M.d.mts.map → sql-execution-plan--jKyLpe5-Bcz9QM2s.d.mts.map} +1 -1
- package/dist/{timestamp-now-generator-DRXygu32-DN9Qb1uI.mjs → timestamp-now-generator-BZnBo7IC-DbN5Apqe.mjs} +27 -19
- package/dist/timestamp-now-generator-BZnBo7IC-DbN5Apqe.mjs.map +1 -0
- package/dist/{types-4YenawkP-CwaaZSid.mjs → types-BvupgHcG-c8QDupnu.mjs} +13 -6
- package/dist/types-BvupgHcG-c8QDupnu.mjs.map +1 -0
- package/dist/{types-UcHa2cTe.d.mts → types-C36_3E6_.d.mts} +3 -2
- package/dist/types-C36_3E6_.d.mts.map +1 -0
- package/dist/{types-CyxGtOOh-BggSa6Fe.d.mts → types-CyxGtOOh-BJnoWm0f.d.mts} +8 -8
- package/dist/{types-CyxGtOOh-BggSa6Fe.d.mts.map → types-CyxGtOOh-BJnoWm0f.d.mts.map} +1 -1
- package/dist/{types-CUsJrvEl.mjs → types-D0B65hip.mjs} +3 -2
- package/dist/types-D0B65hip.mjs.map +1 -0
- package/dist/types-DvsoMNLs.d.mts +1 -0
- package/dist/{types-IqZMLrFC-DcH8v_u3.d.mts → types-IqZMLrFC-BIRftuU7.d.mts} +4 -4
- package/dist/{types-IqZMLrFC-DcH8v_u3.d.mts.map → types-IqZMLrFC-BIRftuU7.d.mts.map} +1 -1
- package/dist/{types-v3qxLLxo-DCeKv7yY.d.mts → types-v3qxLLxo-Clw_yBAh.d.mts} +2 -2
- package/dist/{types-v3qxLLxo-DCeKv7yY.d.mts.map → types-v3qxLLxo-Clw_yBAh.d.mts.map} +1 -1
- package/dist/{validators-Belo8QqM.d.mts → validators-CbqMxQfW.d.mts} +23 -4
- package/dist/validators-CbqMxQfW.d.mts.map +1 -0
- package/dist/{validators-CrWD6bSP.mjs → validators-DD98VrpC.mjs} +0 -0
- package/dist/validators-DD98VrpC.mjs.map +1 -0
- package/dist/{verify-u0UTdZgD-DSAaz9rW.mjs → verify-ChiDeTk8-HEewdjrM.mjs} +3 -3
- package/dist/verify-ChiDeTk8-HEewdjrM.mjs.map +1 -0
- package/package.json +20 -21
- package/dist/ast-HFcfQZr-.d.mts.map +0 -1
- package/dist/ast-Lbl4VwqP.mjs.map +0 -1
- package/dist/authoring-type-constructors-Blqoa2Ua-DqvNCOb2.mjs.map +0 -1
- package/dist/contract-builder-CG_tGDbB.d.mts.map +0 -1
- package/dist/contract-builder-D4a5dT7j.mjs.map +0 -1
- package/dist/control-D3cB5YQp.mjs.map +0 -1
- package/dist/control-DTLJ1WtZ.d.mts.map +0 -1
- package/dist/control-adapter-MZJQiwcE.d.mts +0 -1
- package/dist/derived-checks-DFMRmiAu-DGfB8aS4.mjs.map +0 -1
- package/dist/dist-ZK6oeI5e.mjs.map +0 -1
- package/dist/expression-CX5mTA-z.mjs.map +0 -1
- package/dist/index-naming-DREt6js7-CRH27yUv.mjs.map +0 -1
- package/dist/interpreter-DyiUUAWh-DYdjIpiE.mjs.map +0 -1
- package/dist/lane-query-builder.d.mts +0 -236
- package/dist/lane-query-builder.d.mts.map +0 -1
- package/dist/lane-query-builder.mjs +0 -67
- package/dist/lane-query-builder.mjs.map +0 -1
- package/dist/middleware--TtoxUiW.d.mts +0 -1
- package/dist/plan-BHLdb0Z7.d.mts +0 -2
- package/dist/query-lane-context-XMvk7fmY.d.mts +0 -1
- package/dist/sql-contract-serializer-2oBWuxTe-DKZTf2n8.mjs.map +0 -1
- package/dist/timestamp-now-generator-DRXygu32-DN9Qb1uI.mjs.map +0 -1
- package/dist/types-4YenawkP-CwaaZSid.mjs.map +0 -1
- package/dist/types-B0msOnBM.d.mts +0 -1
- package/dist/types-CUsJrvEl.mjs.map +0 -1
- package/dist/types-UcHa2cTe.d.mts.map +0 -1
- package/dist/validators-Belo8QqM.d.mts.map +0 -1
- package/dist/validators-CrWD6bSP.mjs.map +0 -1
- package/dist/verify-u0UTdZgD-DSAaz9rW.mjs.map +0 -1
package/dist/orm-client.mjs
CHANGED
|
@@ -2,15 +2,15 @@ import { t as resolveStorageTable } from "./resolve-storage-table-DZsfnzeW.mjs";
|
|
|
2
2
|
import { i as isUniqueConstraintViolation } from "./dist-x6jvtkKM.mjs";
|
|
3
3
|
import { C as LiteralExpr, D as OrExpr, F as SubqueryExpr, I as TableSource, L as UpdateAst, O as OrderByItem, P as SelectAst, R as WindowFuncExpr, S as ListExpression, T as NullCheckExpr, U as isWhereExpr, V as isAggregateFn, _ as InsertAst, b as JsonArrayAggExpr, c as DeleteAst, f as EqColJoinOn, j as ProjectionItem, k as ParamRef, l as DerivedTableSource, n as AndExpr, o as ColumnRef, p as ExistsExpr, r as BinaryExpr, s as DefaultValueExpr, t as AggregateExpr, v as InsertOnConflict, w as NotExpr, x as JsonObjectExpr, y as JoinAst } from "./types-Dt4e_jC--COeP8xzN.mjs";
|
|
4
4
|
import { t as collectOrderedParamRefs } from "./util-DQQgv2j1-D6INZaRG.mjs";
|
|
5
|
-
import { n as JsonDocumentProjection, r as NativeJsonValueProjection, t as CodecJsonValueProjection } from "./ast-
|
|
6
|
-
import { n as codecRefForStorageColumn } from "./codec-descriptor-registry-
|
|
5
|
+
import { n as JsonDocumentProjection, r as NativeJsonValueProjection, t as CodecJsonValueProjection } from "./ast-5tg0fGPQ.mjs";
|
|
6
|
+
import { n as codecRefForStorageColumn } from "./codec-descriptor-registry-ECZ4ohtG.mjs";
|
|
7
7
|
import { structuredError } from "@prisma/orm-framework/utils/structured-error";
|
|
8
8
|
import { InternalError } from "@prisma/orm-framework/utils/internal-error";
|
|
9
9
|
import { domainModelsAtDefaultNamespace } from "@prisma/orm-framework/contract/types";
|
|
10
10
|
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
11
|
+
import { AsyncIterableResult, createMetaBuilder, isRuntimeError, runtimeError } from "@prisma/orm-framework/components/runtime";
|
|
11
12
|
import { blindCast, castAs } from "@prisma/orm-framework/utils/casts";
|
|
12
13
|
import { assertDefined, invariant } from "@prisma/orm-framework/utils/assertions";
|
|
13
|
-
import { AsyncIterableResult, createMetaBuilder, isRuntimeError, runtimeError } from "@prisma/orm-framework/components/runtime";
|
|
14
14
|
//#region ../../../3-extensions/sql-orm-client/dist/index.mjs
|
|
15
15
|
const operationNamesByRegistry = /* @__PURE__ */ new WeakMap();
|
|
16
16
|
/**
|
|
@@ -139,9 +139,6 @@ function resolveFieldToColumn(contract, namespaceId, modelName, fieldName) {
|
|
|
139
139
|
* base-table field map. Base fields are intentionally absent so callers can
|
|
140
140
|
* gate variant qualification strictly to variant-owned fields.
|
|
141
141
|
*
|
|
142
|
-
* `baseModelName` is a default-namespace model name, consistent with the rest
|
|
143
|
-
* of this module; namespace context is bound downstream at table resolution.
|
|
144
|
-
*
|
|
145
142
|
* Uncached on purpose: `resolvePolymorphismInfo` already memoizes the variant
|
|
146
143
|
* lookup, and the remaining work is one pass over the variant's field→column
|
|
147
144
|
* map, so a second cache layer would buy nothing.
|
|
@@ -190,8 +187,7 @@ function getColumnToFieldMap(contract, namespaceId, modelName) {
|
|
|
190
187
|
}
|
|
191
188
|
const completeColumnToFieldCache = /* @__PURE__ */ new WeakMap();
|
|
192
189
|
/**
|
|
193
|
-
* Like getColumnToFieldMap but
|
|
194
|
-
* name). getColumnToFieldMap only returns explicit remaps; this returns ALL column→field entries.
|
|
190
|
+
* Like getColumnToFieldMap, but a field without a storage `column` maps under its own name.
|
|
195
191
|
*/
|
|
196
192
|
function getCompleteColumnToFieldMap(contract, namespaceId, modelName) {
|
|
197
193
|
let perContract = completeColumnToFieldCache.get(contract);
|
|
@@ -346,6 +342,13 @@ function assertReturningCapability(contract, action) {
|
|
|
346
342
|
action
|
|
347
343
|
} });
|
|
348
344
|
}
|
|
345
|
+
function assertDistinctOnCapability(contract, methodName) {
|
|
346
|
+
if (contract.capabilities["postgres"]?.["distinctOn"] === true) return;
|
|
347
|
+
throw ormError("ORM.CAPABILITY_MISSING", `${methodName}() requires capability postgres.distinctOn`, { meta: {
|
|
348
|
+
capability: "postgres.distinctOn",
|
|
349
|
+
method: methodName
|
|
350
|
+
} });
|
|
351
|
+
}
|
|
349
352
|
function hasContractCapability(contract, capability) {
|
|
350
353
|
const capabilities = contract.capabilities;
|
|
351
354
|
const value = capabilities[capability];
|
|
@@ -537,16 +540,6 @@ function mapResultRows(result, mapper) {
|
|
|
537
540
|
};
|
|
538
541
|
return new AsyncIterableResult(generator());
|
|
539
542
|
}
|
|
540
|
-
async function acquireRuntimeScope(runtime) {
|
|
541
|
-
if (typeof runtime.connection !== "function") return { scope: runtime };
|
|
542
|
-
const connection = await runtime.connection();
|
|
543
|
-
const release = connection.release;
|
|
544
|
-
if (typeof release === "function") return {
|
|
545
|
-
scope: connection,
|
|
546
|
-
release: () => release.call(connection) ?? Promise.resolve()
|
|
547
|
-
};
|
|
548
|
-
return { scope: connection };
|
|
549
|
-
}
|
|
550
543
|
function deriveParamsFromAst(ast) {
|
|
551
544
|
return { params: collectOrderedParamRefs(ast).map((p) => p.kind === "param-ref" ? p.value : void 0) };
|
|
552
545
|
}
|
|
@@ -591,8 +584,8 @@ function buildOrmQueryPlan(contract, ast, params, annotations) {
|
|
|
591
584
|
* terminals where annotations arrive at the call site, not via state.
|
|
592
585
|
*
|
|
593
586
|
* Returns the input plan unchanged when `annotations` is undefined
|
|
594
|
-
* or empty. Reserved
|
|
595
|
-
*
|
|
587
|
+
* or empty. Reserved namespaces already on the input plan (`codecs`, target keys such as `pg`)
|
|
588
|
+
* win over caller-supplied entries under the same key —
|
|
596
589
|
* see the reserved-namespace policy on `defineAnnotation`.
|
|
597
590
|
*/
|
|
598
591
|
function mergeAnnotations(plan, annotations) {
|
|
@@ -611,202 +604,626 @@ function mergeAnnotations(plan, annotations) {
|
|
|
611
604
|
})
|
|
612
605
|
});
|
|
613
606
|
}
|
|
614
|
-
function
|
|
615
|
-
|
|
616
|
-
if (filters.length === 1) return filters[0];
|
|
617
|
-
return AndExpr.of(filters);
|
|
618
|
-
}
|
|
619
|
-
function toAggregateProjection(contract, aggregates, namespaceId, tableName, selector) {
|
|
620
|
-
const { codec, input: inputCodec, lower } = resolveAggregate({
|
|
621
|
-
aggregates,
|
|
622
|
-
contract,
|
|
623
|
-
namespaceId,
|
|
624
|
-
tableName,
|
|
625
|
-
fn: selector.fn,
|
|
626
|
-
column: selector.column
|
|
627
|
-
});
|
|
628
|
-
const inputExpr = selector.column === void 0 ? void 0 : ColumnRef.of(tableName, selector.column);
|
|
629
|
-
return {
|
|
630
|
-
expr: lower !== void 0 ? lower({
|
|
631
|
-
expr: inputExpr,
|
|
632
|
-
inputCodec
|
|
633
|
-
}) : plainAggregateExpr(selector.fn, inputExpr),
|
|
634
|
-
codec
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
function validateGroupedComparable(value) {
|
|
638
|
-
switch (value.kind) {
|
|
639
|
-
case "param-ref": throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "ParamRef is not supported in grouped having expressions", { meta: { kind: value.kind } });
|
|
640
|
-
case "literal":
|
|
641
|
-
case "column-ref":
|
|
642
|
-
case "identifier-ref":
|
|
643
|
-
case "aggregate":
|
|
644
|
-
case "operation": return value;
|
|
645
|
-
case "function-call":
|
|
646
|
-
case "cast":
|
|
647
|
-
case "case": throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", `Unsupported comparable kind in grouped having: "${value.kind}"`, { meta: { kind: value.kind } });
|
|
648
|
-
case "list":
|
|
649
|
-
if (value.values.some((entry) => entry.kind === "param-ref")) throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "ParamRef is not supported in grouped having expressions", { meta: { kind: "list" } });
|
|
650
|
-
return value;
|
|
651
|
-
default: throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", `Unsupported comparable kind in grouped having: "${value.kind}"`, { meta: { kind: value.kind } });
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
function validateGroupedMetricExpr(expr) {
|
|
655
|
-
if (expr.kind !== "aggregate") throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "groupBy().having() only supports aggregate metric expressions", { meta: { kind: expr.kind } });
|
|
656
|
-
return expr;
|
|
607
|
+
function namespaceCoordinateForSource(source) {
|
|
608
|
+
return source.kind === "table-source" ? source.namespaceId : void 0;
|
|
657
609
|
}
|
|
658
|
-
function
|
|
659
|
-
|
|
610
|
+
function bindWhereExpr(contract, expr, namespaceId) {
|
|
611
|
+
return bindWhereExprNode(contract, expr, namespaceId);
|
|
660
612
|
}
|
|
661
|
-
function
|
|
613
|
+
function bindWhereExprNode(contract, expr, namespaceId) {
|
|
662
614
|
return expr.accept({
|
|
663
|
-
columnRef
|
|
664
|
-
|
|
665
|
-
subquery: rejectHavingExpr,
|
|
666
|
-
operation: rejectHavingExpr,
|
|
667
|
-
aggregate: rejectHavingExpr,
|
|
668
|
-
windowFunc: rejectHavingExpr,
|
|
669
|
-
functionCall: rejectHavingExpr,
|
|
670
|
-
cast: rejectHavingExpr,
|
|
671
|
-
case: rejectHavingExpr,
|
|
672
|
-
jsonObject: rejectHavingExpr,
|
|
673
|
-
jsonArrayAgg: rejectHavingExpr,
|
|
674
|
-
literal: rejectHavingExpr,
|
|
675
|
-
param() {
|
|
676
|
-
throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "ParamRef is not supported in grouped having expressions", { meta: { kind: "param-ref" } });
|
|
615
|
+
columnRef(expr) {
|
|
616
|
+
return bindExpression(contract, expr);
|
|
677
617
|
},
|
|
678
|
-
|
|
679
|
-
|
|
618
|
+
identifierRef(expr) {
|
|
619
|
+
return expr;
|
|
620
|
+
},
|
|
621
|
+
subquery(expr) {
|
|
622
|
+
return bindExpression(contract, expr);
|
|
623
|
+
},
|
|
624
|
+
operation(expr) {
|
|
625
|
+
return bindExpression(contract, expr);
|
|
626
|
+
},
|
|
627
|
+
aggregate(expr) {
|
|
628
|
+
return bindExpression(contract, expr);
|
|
629
|
+
},
|
|
630
|
+
windowFunc(expr) {
|
|
631
|
+
return bindExpression(contract, expr);
|
|
632
|
+
},
|
|
633
|
+
functionCall(expr) {
|
|
634
|
+
return bindExpression(contract, expr);
|
|
635
|
+
},
|
|
636
|
+
cast(expr) {
|
|
637
|
+
return bindExpression(contract, expr);
|
|
638
|
+
},
|
|
639
|
+
case(expr) {
|
|
640
|
+
return bindExpression(contract, expr);
|
|
641
|
+
},
|
|
642
|
+
jsonObject(expr) {
|
|
643
|
+
return bindExpression(contract, expr);
|
|
644
|
+
},
|
|
645
|
+
jsonArrayAgg(expr) {
|
|
646
|
+
return bindExpression(contract, expr);
|
|
647
|
+
},
|
|
648
|
+
literal(expr) {
|
|
649
|
+
return expr;
|
|
650
|
+
},
|
|
651
|
+
param(expr) {
|
|
652
|
+
return expr;
|
|
653
|
+
},
|
|
654
|
+
preparedParam(expr) {
|
|
655
|
+
return expr;
|
|
656
|
+
},
|
|
657
|
+
list(expr) {
|
|
658
|
+
return bindExpression(contract, expr);
|
|
659
|
+
},
|
|
660
|
+
binary(expr) {
|
|
661
|
+
const left = bindExpression(contract, expr.left);
|
|
662
|
+
const bindingColumn = left.kind === "column-ref" ? left : void 0;
|
|
663
|
+
return new BinaryExpr(expr.op, left, bindComparable(contract, expr.right, bindingColumn, namespaceId));
|
|
680
664
|
},
|
|
681
|
-
list: rejectHavingExpr,
|
|
682
665
|
and(expr) {
|
|
683
|
-
return AndExpr.of(expr.exprs.map((
|
|
666
|
+
return AndExpr.of(expr.exprs.map((part) => bindWhereExprNode(contract, part, namespaceId)));
|
|
684
667
|
},
|
|
685
668
|
or(expr) {
|
|
686
|
-
return OrExpr.of(expr.exprs.map((
|
|
669
|
+
return OrExpr.of(expr.exprs.map((part) => bindWhereExprNode(contract, part, namespaceId)));
|
|
670
|
+
},
|
|
671
|
+
exists(expr) {
|
|
672
|
+
return expr.notExists ? ExistsExpr.notExists(bindSelectAst(contract, expr.subquery)) : ExistsExpr.exists(bindSelectAst(contract, expr.subquery));
|
|
687
673
|
},
|
|
688
|
-
exists: rejectHavingExpr,
|
|
689
674
|
nullCheck(expr) {
|
|
690
|
-
return
|
|
675
|
+
return expr.isNull ? NullCheckExpr.isNull(bindExpression(contract, expr.expr)) : NullCheckExpr.isNotNull(bindExpression(contract, expr.expr));
|
|
691
676
|
},
|
|
692
677
|
not(expr) {
|
|
693
|
-
return new NotExpr(
|
|
694
|
-
},
|
|
695
|
-
binary(expr) {
|
|
696
|
-
return new BinaryExpr(expr.op, validateGroupedMetricExpr(expr.left), validateGroupedComparable(expr.right));
|
|
678
|
+
return new NotExpr(bindWhereExprNode(contract, expr.expr, namespaceId));
|
|
697
679
|
},
|
|
698
|
-
rawExpr
|
|
680
|
+
rawExpr(expr) {
|
|
681
|
+
return expr;
|
|
682
|
+
}
|
|
699
683
|
});
|
|
700
684
|
}
|
|
701
|
-
function
|
|
702
|
-
|
|
703
|
-
if (
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
tableName
|
|
707
|
-
} });
|
|
708
|
-
const projection = entries.map(([alias, selector]) => {
|
|
709
|
-
const { expr, codec } = toAggregateProjection(contract, aggregates, namespaceId, tableName, selector);
|
|
710
|
-
return ProjectionItem.of(alias, expr, codec);
|
|
711
|
-
});
|
|
712
|
-
let ast = SelectAst.from(tableSourceForContract(contract, namespaceId, tableName)).withProjection(projection);
|
|
713
|
-
const where = combineWhereExprs(filters);
|
|
714
|
-
if (where) ast = ast.withWhere(where);
|
|
715
|
-
const { params } = deriveParamsFromAst(ast);
|
|
716
|
-
return buildOrmQueryPlan(contract, ast, params);
|
|
685
|
+
function bindComparable(contract, comparable, bindingColumn, namespaceId) {
|
|
686
|
+
if (comparable.kind === "param-ref" || bindingColumn === void 0) return comparable.kind === "param-ref" ? comparable : comparable.kind === "literal" || comparable.kind === "list" ? comparable : bindExpression(contract, comparable);
|
|
687
|
+
if (comparable.kind === "literal") return createParamRef(contract, bindingColumn, comparable.value, namespaceId);
|
|
688
|
+
if (comparable.kind === "list") return ListExpression.of(comparable.values.map((value) => value.kind === "literal" ? createParamRef(contract, bindingColumn, value.value, namespaceId) : value));
|
|
689
|
+
return bindExpression(contract, comparable);
|
|
717
690
|
}
|
|
718
|
-
function
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
tableName
|
|
722
|
-
|
|
723
|
-
const entries = Object.entries(aggregateSpec);
|
|
724
|
-
if (entries.length === 0) throw ormError("ORM.AGGREGATE_SELECTOR_MISSING", "groupBy().aggregate() requires at least one aggregation selector", { meta: {
|
|
725
|
-
method: "groupBy.aggregate",
|
|
726
|
-
namespaceId,
|
|
727
|
-
tableName
|
|
691
|
+
function createParamRef(contract, columnRef, value, namespaceId) {
|
|
692
|
+
const resolved = resolveStorageTable(contract.storage, columnRef.table, namespaceId);
|
|
693
|
+
if (resolved === void 0 || !resolved.table.columns[columnRef.column]) throw ormError("ORM.COLUMN_UNKNOWN", `Unknown column "${columnRef.column}" in table "${columnRef.table}"`, { meta: {
|
|
694
|
+
tableName: columnRef.table,
|
|
695
|
+
column: columnRef.column
|
|
728
696
|
} });
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
return ProjectionItem.of(alias, expr, codec);
|
|
732
|
-
})];
|
|
733
|
-
let ast = SelectAst.from(tableSourceForContract(contract, namespaceId, tableName)).withProjection(projection).withGroupBy(groupByColumns.map((column) => ColumnRef.of(tableName, column)));
|
|
734
|
-
const where = combineWhereExprs(filters);
|
|
735
|
-
if (where) ast = ast.withWhere(where);
|
|
736
|
-
if (havingExpr) ast = ast.withHaving(validateGroupedHavingExpr(havingExpr));
|
|
737
|
-
const { params } = deriveParamsFromAst(ast);
|
|
738
|
-
return buildOrmQueryPlan(contract, ast, params);
|
|
739
|
-
}
|
|
740
|
-
function buildReturningColumns(contract, namespaceId, tableName, returningColumns) {
|
|
741
|
-
return (returningColumns && returningColumns.length > 0 ? [...returningColumns] : resolveTableColumns(contract, namespaceId, tableName)).map((column) => ProjectionItem.of(column, ColumnRef.of(tableName, column), codecRefForStorageColumn(contract.storage, namespaceId, tableName, column)));
|
|
697
|
+
const codec = codecRefForStorageColumn(contract.storage, resolved.namespaceId, columnRef.table, columnRef.column);
|
|
698
|
+
return ParamRef.of(value, codec ? { codec } : void 0);
|
|
742
699
|
}
|
|
743
|
-
function
|
|
744
|
-
|
|
745
|
-
const table = storageTableForContract(contract, namespaceId, tableName);
|
|
746
|
-
for (const [column, value] of Object.entries(values)) {
|
|
747
|
-
if (!table.columns[column]) throw ormError("ORM.COLUMN_UNKNOWN", `Unknown column "${column}" in table "${tableName}"`, { meta: {
|
|
748
|
-
namespaceId,
|
|
749
|
-
tableName,
|
|
750
|
-
column
|
|
751
|
-
} });
|
|
752
|
-
const codec = codecRefForStorageColumn(contract.storage, namespaceId, tableName, column);
|
|
753
|
-
assignments[column] = ParamRef.of(value, {
|
|
754
|
-
name: column,
|
|
755
|
-
...ifDefined("codec", codec)
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
return { assignments };
|
|
700
|
+
function createExpressionBinder(contract) {
|
|
701
|
+
return { select: (ast) => bindSelectAst(contract, ast) };
|
|
759
702
|
}
|
|
760
|
-
function
|
|
761
|
-
|
|
762
|
-
const orderedColumns = [];
|
|
763
|
-
const seenColumns = /* @__PURE__ */ new Set();
|
|
764
|
-
for (const row of rows) for (const column of Object.keys(row)) {
|
|
765
|
-
if (seenColumns.has(column)) continue;
|
|
766
|
-
seenColumns.add(column);
|
|
767
|
-
orderedColumns.push(column);
|
|
768
|
-
}
|
|
769
|
-
const table = storageTableForContract(contract, namespaceId, tableName);
|
|
770
|
-
return { rows: rows.map((row) => {
|
|
771
|
-
if (orderedColumns.length === 0) return {};
|
|
772
|
-
const normalizedRow = {};
|
|
773
|
-
for (const column of orderedColumns) {
|
|
774
|
-
if (Object.hasOwn(row, column)) {
|
|
775
|
-
if (!table.columns[column]) throw ormError("ORM.COLUMN_UNKNOWN", `Unknown column "${column}" in table "${tableName}"`, { meta: {
|
|
776
|
-
namespaceId,
|
|
777
|
-
tableName,
|
|
778
|
-
column
|
|
779
|
-
} });
|
|
780
|
-
const codec = codecRefForStorageColumn(contract.storage, namespaceId, tableName, column);
|
|
781
|
-
normalizedRow[column] = ParamRef.of(row[column], {
|
|
782
|
-
name: column,
|
|
783
|
-
...ifDefined("codec", codec)
|
|
784
|
-
});
|
|
785
|
-
continue;
|
|
786
|
-
}
|
|
787
|
-
normalizedRow[column] = new DefaultValueExpr();
|
|
788
|
-
}
|
|
789
|
-
return normalizedRow;
|
|
790
|
-
}) };
|
|
703
|
+
function bindExpression(contract, expr) {
|
|
704
|
+
return expr.rewrite(createExpressionBinder(contract));
|
|
791
705
|
}
|
|
792
|
-
function
|
|
793
|
-
|
|
794
|
-
const ast = InsertAst.into(tableSourceForContract(contract, namespaceId, tableName)).withRows(normalizedRows).withReturning(buildReturningColumns(contract, namespaceId, tableName, returningColumns));
|
|
795
|
-
const { params } = deriveParamsFromAst(ast);
|
|
796
|
-
return buildOrmQueryPlan(contract, ast, params);
|
|
706
|
+
function bindProjectionExpr(contract, expr) {
|
|
707
|
+
return expr.kind === "literal" ? expr : bindExpression(contract, expr);
|
|
797
708
|
}
|
|
798
|
-
function
|
|
799
|
-
|
|
800
|
-
const ast = InsertAst.into(tableSourceForContract(contract, namespaceId, tableName)).withRows(normalizedRows);
|
|
801
|
-
const { params } = deriveParamsFromAst(ast);
|
|
802
|
-
return buildOrmQueryPlan(contract, ast, params);
|
|
709
|
+
function bindOrderByItem(contract, orderItem) {
|
|
710
|
+
return new OrderByItem(bindExpression(contract, orderItem.expr), orderItem.dir);
|
|
803
711
|
}
|
|
804
|
-
function
|
|
712
|
+
function bindJoin(contract, join) {
|
|
713
|
+
const namespaceId = namespaceCoordinateForSource(join.source);
|
|
714
|
+
return new JoinAst(join.joinType, bindFromSource(contract, join.source), join.on.kind === "eq-col-join-on" ? join.on : bindWhereExprNode(contract, join.on, namespaceId), join.lateral);
|
|
715
|
+
}
|
|
716
|
+
function bindFromSource(contract, source) {
|
|
717
|
+
if (source.kind === "table-source") return source;
|
|
718
|
+
if (source.kind === "derived-table-source") return DerivedTableSource.as(source.alias, bindSelectAst(contract, source.query));
|
|
719
|
+
return source;
|
|
720
|
+
}
|
|
721
|
+
function bindSelectAst(contract, ast) {
|
|
722
|
+
const namespaceId = ast.from !== void 0 ? namespaceCoordinateForSource(ast.from) : void 0;
|
|
723
|
+
return new SelectAst({
|
|
724
|
+
...ast.from !== void 0 ? { from: bindFromSource(contract, ast.from) } : {},
|
|
725
|
+
joins: ast.joins?.map((join) => bindJoin(contract, join)),
|
|
726
|
+
projection: ast.projection.map((projection) => new ProjectionItem(projection.alias, bindProjectionExpr(contract, projection.expr), projection.codec)),
|
|
727
|
+
where: ast.where ? bindWhereExprNode(contract, ast.where, namespaceId) : void 0,
|
|
728
|
+
orderBy: ast.orderBy?.map((orderItem) => bindOrderByItem(contract, orderItem)),
|
|
729
|
+
distinct: ast.distinct,
|
|
730
|
+
distinctOn: ast.distinctOn?.map((expr) => bindExpression(contract, expr)),
|
|
731
|
+
groupBy: ast.groupBy?.map((expr) => bindExpression(contract, expr)),
|
|
732
|
+
having: ast.having ? bindWhereExprNode(contract, ast.having, namespaceId) : void 0,
|
|
733
|
+
limit: ast.limit,
|
|
734
|
+
offset: ast.offset,
|
|
735
|
+
selectAllIntent: ast.selectAllIntent
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
function combineWhereExprs(filters) {
|
|
739
|
+
if (filters.length === 0) return;
|
|
740
|
+
if (filters.length === 1) return filters[0];
|
|
741
|
+
return AndExpr.of(filters);
|
|
742
|
+
}
|
|
743
|
+
function createBoundaryExpr(tableName, entry) {
|
|
744
|
+
return new BinaryExpr(entry.direction === "asc" ? "gt" : "lt", ColumnRef.of(tableName, entry.column), LiteralExpr.of(entry.value));
|
|
745
|
+
}
|
|
746
|
+
function buildLexicographicCursorWhere(tableName, entries) {
|
|
747
|
+
const branches = entries.map((entry, index) => {
|
|
748
|
+
const branchExprs = [];
|
|
749
|
+
for (const prefixEntry of entries.slice(0, index)) branchExprs.push(BinaryExpr.eq(ColumnRef.of(tableName, prefixEntry.column), LiteralExpr.of(prefixEntry.value)));
|
|
750
|
+
branchExprs.push(createBoundaryExpr(tableName, entry));
|
|
751
|
+
if (branchExprs.length === 1) {
|
|
752
|
+
const branch = branchExprs[0];
|
|
753
|
+
assertDefined(branch, "cursor branch contains its boundary expression");
|
|
754
|
+
return branch;
|
|
755
|
+
}
|
|
756
|
+
return AndExpr.of(branchExprs);
|
|
757
|
+
});
|
|
758
|
+
if (branches.length === 1) {
|
|
759
|
+
const branch = branches[0];
|
|
760
|
+
assertDefined(branch, "cursor expression contains its single branch");
|
|
761
|
+
return branch;
|
|
762
|
+
}
|
|
763
|
+
return OrExpr.of(branches);
|
|
764
|
+
}
|
|
765
|
+
function buildCursorWhere(tableName, orderBy, cursor) {
|
|
766
|
+
if (!cursor || !orderBy || orderBy.length === 0) return;
|
|
767
|
+
const entries = [];
|
|
768
|
+
for (const order of orderBy) {
|
|
769
|
+
if (order.expr.kind !== "column-ref") continue;
|
|
770
|
+
const column = order.expr.column;
|
|
771
|
+
const value = cursor[column];
|
|
772
|
+
if (value === void 0) throw ormError("ORM.CURSOR_VALUE_MISSING", `Missing cursor value for orderBy column "${column}"`, { meta: { column } });
|
|
773
|
+
entries.push({
|
|
774
|
+
column,
|
|
775
|
+
direction: order.dir,
|
|
776
|
+
value
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
const firstEntry = entries[0];
|
|
780
|
+
if (entries.length === 1 && firstEntry !== void 0) return createBoundaryExpr(tableName, firstEntry);
|
|
781
|
+
return buildLexicographicCursorWhere(tableName, entries);
|
|
782
|
+
}
|
|
783
|
+
function createTableRefRemapper$1(fromTable, toTable) {
|
|
784
|
+
return {
|
|
785
|
+
columnRef: (col) => col.table === fromTable ? ColumnRef.of(toTable, col.column) : col,
|
|
786
|
+
tableSource: (source) => {
|
|
787
|
+
if (source.alias === fromTable) return TableSource.named(source.name, toTable, source.namespaceId);
|
|
788
|
+
if (!source.alias && source.name === fromTable) return TableSource.named(source.name, toTable, source.namespaceId);
|
|
789
|
+
return source;
|
|
790
|
+
},
|
|
791
|
+
eqColJoinOn: (on) => EqColJoinOn.of(on.left.table === fromTable ? ColumnRef.of(toTable, on.left.column) : on.left, on.right.table === fromTable ? ColumnRef.of(toTable, on.right.column) : on.right)
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
function buildStateWhere(contract, tableName, state, options) {
|
|
795
|
+
const filterTableName = options?.filterTableName;
|
|
796
|
+
const cursorWhere = buildCursorWhere(filterTableName ?? tableName, state.orderBy, state.cursor);
|
|
797
|
+
const boundFilters = state.filters.map((filter) => bindWhereExpr(contract, filter, options?.namespaceId));
|
|
798
|
+
const remappedFilters = filterTableName && filterTableName !== tableName ? boundFilters.map((filter) => filter.rewrite(createTableRefRemapper$1(filterTableName, tableName))) : boundFilters;
|
|
799
|
+
const boundCursorWhere = cursorWhere ? bindWhereExpr(contract, cursorWhere, options?.namespaceId) : void 0;
|
|
800
|
+
const remappedCursorWhere = boundCursorWhere && filterTableName && filterTableName !== tableName ? boundCursorWhere.rewrite(createTableRefRemapper$1(filterTableName, tableName)) : boundCursorWhere;
|
|
801
|
+
return combineWhereExprs(remappedCursorWhere ? [...remappedFilters, remappedCursorWhere] : remappedFilters);
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Wrap a base SELECT in a `ROW_NUMBER() OVER (PARTITION BY … ORDER BY …) = 1`
|
|
805
|
+
* filter, implementing Prisma-style `.distinct(cols)` semantics: one
|
|
806
|
+
* representative row per `(distinctColumnRefs)` group is kept; the rest
|
|
807
|
+
* are dropped.
|
|
808
|
+
*
|
|
809
|
+
* Picking which row survives in each partition is governed by
|
|
810
|
+
* `rankingOrderBy`. When the caller's `orderBy` doesn't fully order rows
|
|
811
|
+
* within a partition (e.g. user wrote `.distinct('title')` with no
|
|
812
|
+
* `orderBy`, or ties in their ordering), the choice is
|
|
813
|
+
* implementation-defined — matching Prisma's documented nested-distinct
|
|
814
|
+
* behaviour. Callers that want determinism should pass an `orderBy` that
|
|
815
|
+
* is total within each partition.
|
|
816
|
+
*
|
|
817
|
+
* The wrapper forwards every column of `base.projection` through the
|
|
818
|
+
* derived alias, so the wrapper's projection is byte-identical in alias
|
|
819
|
+
* names — making this transparent to any outer query (`json_agg`,
|
|
820
|
+
* correlated subquery, top-level SELECT) that consumes the SELECT.
|
|
821
|
+
*/
|
|
822
|
+
function wrapWithRowNumberDedup(options) {
|
|
823
|
+
const { base, distinctColumnRefs, rankingOrderBy, rankedAlias } = options;
|
|
824
|
+
const rnAlias = "__prisma_distinct_rn";
|
|
825
|
+
const effectiveOrderBy = rankingOrderBy.length > 0 ? rankingOrderBy : distinctColumnRefs.map((expr) => OrderByItem.asc(expr));
|
|
826
|
+
const inner = base.withProjection([...base.projection, ProjectionItem.of(rnAlias, WindowFuncExpr.rowNumber({
|
|
827
|
+
partitionBy: distinctColumnRefs,
|
|
828
|
+
orderBy: effectiveOrderBy
|
|
829
|
+
}))]);
|
|
830
|
+
return SelectAst.from(DerivedTableSource.as(rankedAlias, inner)).withProjection(base.projection.map((item) => ProjectionItem.of(item.alias, ColumnRef.of(rankedAlias, item.alias), item.codec))).withWhere(BinaryExpr.eq(ColumnRef.of(rankedAlias, rnAlias), LiteralExpr.of(1)));
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* FROM source + WHERE for `state.distinct`: wraps in a `ROW_NUMBER`-ranked
|
|
834
|
+
* derived table aliased back to `tableName`, so callers need no rewriting.
|
|
835
|
+
*/
|
|
836
|
+
function buildDedupedTableSource(contract, namespaceId, tableName, state, where, wrapProjection, joins) {
|
|
837
|
+
if (!hasEntries(state.distinct)) return {
|
|
838
|
+
source: tableSourceForContract(contract, namespaceId, tableName),
|
|
839
|
+
where
|
|
840
|
+
};
|
|
841
|
+
const distinctColumnRefs = state.distinct.map((column) => ColumnRef.of(tableName, column));
|
|
842
|
+
const rankingOrderBy = hasEntries(state.orderBy) ? state.orderBy : distinctColumnRefs.map((expr) => OrderByItem.asc(expr));
|
|
843
|
+
let inner = SelectAst.from(tableSourceForContract(contract, namespaceId, tableName)).withProjection([...wrapProjection, ProjectionItem.of("__prisma_distinct_rn", WindowFuncExpr.rowNumber({
|
|
844
|
+
partitionBy: distinctColumnRefs,
|
|
845
|
+
orderBy: rankingOrderBy
|
|
846
|
+
}))]);
|
|
847
|
+
if (joins && joins.length > 0) inner = inner.withJoins(joins);
|
|
848
|
+
if (where) inner = inner.withWhere(where);
|
|
849
|
+
return {
|
|
850
|
+
source: DerivedTableSource.as(tableName, inner),
|
|
851
|
+
where: BinaryExpr.eq(ColumnRef.of(tableName, "__prisma_distinct_rn"), LiteralExpr.of(1))
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
function buildMtiJoins(contract, namespaceId, polyInfo, variantName, selectedColumnsByTable) {
|
|
855
|
+
const joins = [];
|
|
856
|
+
const projection = [];
|
|
857
|
+
const pkColumn = resolvePrimaryKeyColumn(contract, namespaceId, polyInfo.baseTable);
|
|
858
|
+
const variantsToJoin = variantName ? polyInfo.mtiVariants.filter((v) => v.modelName === variantName) : polyInfo.mtiVariants;
|
|
859
|
+
for (const variant of variantsToJoin) {
|
|
860
|
+
const joinType = variantName ? "inner" : "left";
|
|
861
|
+
const joinOn = EqColJoinOn.of(ColumnRef.of(polyInfo.baseTable, pkColumn), ColumnRef.of(variant.table, pkColumn));
|
|
862
|
+
const join = joinType === "inner" ? JoinAst.inner(tableSourceForContract(contract, namespaceId, variant.table), joinOn) : JoinAst.left(tableSourceForContract(contract, namespaceId, variant.table), joinOn);
|
|
863
|
+
joins.push(join);
|
|
864
|
+
const variantColumns = resolveTableColumns(contract, namespaceId, variant.table);
|
|
865
|
+
const selectedVariantColumns = selectedColumnsByTable?.get(variant.table);
|
|
866
|
+
for (const col of variantColumns) {
|
|
867
|
+
if (col === pkColumn) continue;
|
|
868
|
+
if (selectedColumnsByTable !== void 0 && selectedVariantColumns?.has(col) !== true) continue;
|
|
869
|
+
const alias = `${variant.table}__${col}`;
|
|
870
|
+
projection.push(ProjectionItem.of(alias, ColumnRef.of(variant.table, col), codecRefForStorageColumn(contract.storage, namespaceId, variant.table, col)));
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return {
|
|
874
|
+
joins,
|
|
875
|
+
projection
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
function hasEntries(value) {
|
|
879
|
+
return value !== void 0 && value.length > 0;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* The rows an aggregate reduces over, one SELECT aliased to `tableName` — an
|
|
883
|
+
* aggregate has no outer level of its own, so where/joins/distinct/orderBy/
|
|
884
|
+
* limit/offset all have to live in this one wrap.
|
|
885
|
+
*/
|
|
886
|
+
function buildAggregateInput(contract, namespaceId, tableName, state, modelName, projection) {
|
|
887
|
+
const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
|
|
888
|
+
const variantJoins = polyInfo && polyInfo.mtiVariants.length > 0 ? buildMtiJoins(contract, namespaceId, polyInfo, state.variantName, void 0).joins : [];
|
|
889
|
+
const { source, where: effectiveWhere } = buildDedupedTableSource(contract, namespaceId, tableName, state, buildStateWhere(contract, tableName, state, { namespaceId }), projection, variantJoins);
|
|
890
|
+
let inner = SelectAst.from(source).withProjection(projection);
|
|
891
|
+
if (!hasEntries(state.distinct) && variantJoins.length > 0) inner = inner.withJoins(variantJoins);
|
|
892
|
+
if (effectiveWhere) inner = inner.withWhere(effectiveWhere);
|
|
893
|
+
if (hasEntries(state.distinctOn)) inner = inner.withDistinctOn(state.distinctOn.map((column) => ColumnRef.of(tableName, column)));
|
|
894
|
+
if (hasEntries(state.orderBy)) inner = inner.withOrderBy(state.orderBy);
|
|
895
|
+
if (state.limit !== void 0) inner = inner.withLimit(state.limit);
|
|
896
|
+
if (state.offset !== void 0) inner = inner.withOffset(state.offset);
|
|
897
|
+
return { source: DerivedTableSource.as(tableName, inner) };
|
|
898
|
+
}
|
|
899
|
+
function emptyState() {
|
|
900
|
+
return {
|
|
901
|
+
filters: [],
|
|
902
|
+
includes: [],
|
|
903
|
+
orderBy: void 0,
|
|
904
|
+
cursor: void 0,
|
|
905
|
+
distinct: void 0,
|
|
906
|
+
distinctOn: void 0,
|
|
907
|
+
selectedFields: void 0,
|
|
908
|
+
limit: void 0,
|
|
909
|
+
offset: void 0,
|
|
910
|
+
variantName: void 0,
|
|
911
|
+
annotations: /* @__PURE__ */ new Map()
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
function emptyGroupPagingState() {
|
|
915
|
+
return {
|
|
916
|
+
orderBy: [],
|
|
917
|
+
limit: void 0,
|
|
918
|
+
offset: void 0
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
function param(codec, value) {
|
|
922
|
+
if (codec === void 0) return ParamRef.of(value);
|
|
923
|
+
return ParamRef.of(value, { codec });
|
|
924
|
+
}
|
|
925
|
+
function paramList(codec, values) {
|
|
926
|
+
return ListExpression.of(values.map((value) => param(codec, value)));
|
|
927
|
+
}
|
|
928
|
+
function scalarComparisonMethod(op) {
|
|
929
|
+
return ((left, codec) => (value) => {
|
|
930
|
+
if (value === null && (op === "eq" || op === "neq")) return op === "eq" ? NullCheckExpr.isNull(left) : NullCheckExpr.isNotNull(left);
|
|
931
|
+
return new BinaryExpr(op, left, param(codec, value));
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
function listComparisonMethod(op) {
|
|
935
|
+
return ((left, codec) => (values) => new BinaryExpr(op, left, paramList(codec, values)));
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Declares trait requirements and runtime factory for each comparison method.
|
|
939
|
+
*
|
|
940
|
+
* - `traits: []` means "no trait required" — always available
|
|
941
|
+
* - Multi-trait: `traits: ['equality', 'order']` means BOTH traits are required
|
|
942
|
+
*/
|
|
943
|
+
const COMPARISON_METHODS_META = {
|
|
944
|
+
eq: {
|
|
945
|
+
traits: ["equality"],
|
|
946
|
+
create: scalarComparisonMethod("eq")
|
|
947
|
+
},
|
|
948
|
+
neq: {
|
|
949
|
+
traits: ["equality"],
|
|
950
|
+
create: scalarComparisonMethod("neq")
|
|
951
|
+
},
|
|
952
|
+
in: {
|
|
953
|
+
traits: ["equality"],
|
|
954
|
+
create: listComparisonMethod("in")
|
|
955
|
+
},
|
|
956
|
+
notIn: {
|
|
957
|
+
traits: ["equality"],
|
|
958
|
+
create: listComparisonMethod("notIn")
|
|
959
|
+
},
|
|
960
|
+
gt: {
|
|
961
|
+
traits: ["order"],
|
|
962
|
+
create: scalarComparisonMethod("gt")
|
|
963
|
+
},
|
|
964
|
+
lt: {
|
|
965
|
+
traits: ["order"],
|
|
966
|
+
create: scalarComparisonMethod("lt")
|
|
967
|
+
},
|
|
968
|
+
gte: {
|
|
969
|
+
traits: ["order"],
|
|
970
|
+
create: scalarComparisonMethod("gte")
|
|
971
|
+
},
|
|
972
|
+
lte: {
|
|
973
|
+
traits: ["order"],
|
|
974
|
+
create: scalarComparisonMethod("lte")
|
|
975
|
+
},
|
|
976
|
+
like: {
|
|
977
|
+
traits: ["textual"],
|
|
978
|
+
create: scalarComparisonMethod("like")
|
|
979
|
+
},
|
|
980
|
+
asc: {
|
|
981
|
+
traits: ["order"],
|
|
982
|
+
create: (left) => () => OrderByItem.asc(left)
|
|
983
|
+
},
|
|
984
|
+
desc: {
|
|
985
|
+
traits: ["order"],
|
|
986
|
+
create: (left) => () => OrderByItem.desc(left)
|
|
987
|
+
},
|
|
988
|
+
isNull: {
|
|
989
|
+
traits: [],
|
|
990
|
+
create: (left) => () => NullCheckExpr.isNull(left)
|
|
991
|
+
},
|
|
992
|
+
isNotNull: {
|
|
993
|
+
traits: [],
|
|
994
|
+
create: (left) => () => NullCheckExpr.isNotNull(left)
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
function toAggregateProjection(contract, aggregates, namespaceId, tableName, selector) {
|
|
998
|
+
const { codec, input: inputCodec, lower } = resolveAggregate({
|
|
999
|
+
aggregates,
|
|
1000
|
+
contract,
|
|
1001
|
+
namespaceId,
|
|
1002
|
+
tableName,
|
|
1003
|
+
fn: selector.fn,
|
|
1004
|
+
column: selector.column
|
|
1005
|
+
});
|
|
1006
|
+
const inputExpr = selector.column === void 0 ? void 0 : ColumnRef.of(tableName, selector.column);
|
|
1007
|
+
return {
|
|
1008
|
+
expr: lower !== void 0 ? lower({
|
|
1009
|
+
expr: inputExpr,
|
|
1010
|
+
inputCodec
|
|
1011
|
+
}) : plainAggregateExpr(selector.fn, inputExpr),
|
|
1012
|
+
codec
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function validateGroupedComparable(value) {
|
|
1016
|
+
switch (value.kind) {
|
|
1017
|
+
case "param-ref": throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "ParamRef is not supported in grouped having expressions", { meta: { kind: value.kind } });
|
|
1018
|
+
case "literal":
|
|
1019
|
+
case "column-ref":
|
|
1020
|
+
case "identifier-ref":
|
|
1021
|
+
case "aggregate":
|
|
1022
|
+
case "operation": return value;
|
|
1023
|
+
case "function-call":
|
|
1024
|
+
case "cast":
|
|
1025
|
+
case "case": throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", `Unsupported comparable kind in grouped having: "${value.kind}"`, { meta: { kind: value.kind } });
|
|
1026
|
+
case "list":
|
|
1027
|
+
if (value.values.some((entry) => entry.kind === "param-ref")) throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "ParamRef is not supported in grouped having expressions", { meta: { kind: "list" } });
|
|
1028
|
+
return value;
|
|
1029
|
+
default: throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", `Unsupported comparable kind in grouped having: "${value.kind}"`, { meta: { kind: value.kind } });
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
function validateGroupedMetricExpr(expr) {
|
|
1033
|
+
if (expr.kind !== "aggregate") throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "groupBy().having() only supports aggregate metric expressions", { meta: { kind: expr.kind } });
|
|
1034
|
+
return expr;
|
|
1035
|
+
}
|
|
1036
|
+
function rejectHavingExpr(expr) {
|
|
1037
|
+
throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", `Unsupported grouped having expression kind "${expr.kind}"`, { meta: { kind: expr.kind } });
|
|
1038
|
+
}
|
|
1039
|
+
function validateGroupedHavingExpr(expr) {
|
|
1040
|
+
return expr.accept({
|
|
1041
|
+
columnRef: rejectHavingExpr,
|
|
1042
|
+
identifierRef: rejectHavingExpr,
|
|
1043
|
+
subquery: rejectHavingExpr,
|
|
1044
|
+
operation: rejectHavingExpr,
|
|
1045
|
+
aggregate: rejectHavingExpr,
|
|
1046
|
+
windowFunc: rejectHavingExpr,
|
|
1047
|
+
functionCall: rejectHavingExpr,
|
|
1048
|
+
cast: rejectHavingExpr,
|
|
1049
|
+
case: rejectHavingExpr,
|
|
1050
|
+
jsonObject: rejectHavingExpr,
|
|
1051
|
+
jsonArrayAgg: rejectHavingExpr,
|
|
1052
|
+
literal: rejectHavingExpr,
|
|
1053
|
+
param() {
|
|
1054
|
+
throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "ParamRef is not supported in grouped having expressions", { meta: { kind: "param-ref" } });
|
|
1055
|
+
},
|
|
1056
|
+
preparedParam() {
|
|
1057
|
+
throw ormError("ORM.HAVING_EXPRESSION_UNSUPPORTED", "PreparedParamRef is not supported in grouped having expressions", { meta: { kind: "prepared-param-ref" } });
|
|
1058
|
+
},
|
|
1059
|
+
list: rejectHavingExpr,
|
|
1060
|
+
and(expr) {
|
|
1061
|
+
return AndExpr.of(expr.exprs.map((child) => validateGroupedHavingExpr(child)));
|
|
1062
|
+
},
|
|
1063
|
+
or(expr) {
|
|
1064
|
+
return OrExpr.of(expr.exprs.map((child) => validateGroupedHavingExpr(child)));
|
|
1065
|
+
},
|
|
1066
|
+
exists: rejectHavingExpr,
|
|
1067
|
+
nullCheck(expr) {
|
|
1068
|
+
return new NullCheckExpr(validateGroupedMetricExpr(expr.expr), expr.isNull);
|
|
1069
|
+
},
|
|
1070
|
+
not(expr) {
|
|
1071
|
+
return new NotExpr(validateGroupedHavingExpr(expr.expr));
|
|
1072
|
+
},
|
|
1073
|
+
binary(expr) {
|
|
1074
|
+
return new BinaryExpr(expr.op, validateGroupedMetricExpr(expr.left), validateGroupedComparable(expr.right));
|
|
1075
|
+
},
|
|
1076
|
+
rawExpr: rejectHavingExpr
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
function aggregateInputColumns(tableName, entries, orderBy, groupByColumns = []) {
|
|
1080
|
+
const columns = new Set(groupByColumns);
|
|
1081
|
+
for (const [, selector] of entries) if (selector.column !== void 0) columns.add(selector.column);
|
|
1082
|
+
for (const item of orderBy ?? []) if (item.expr.kind === "column-ref") columns.add(item.expr.column);
|
|
1083
|
+
if (columns.size === 0) return [ProjectionItem.of("__row", LiteralExpr.of(1))];
|
|
1084
|
+
return Array.from(columns, (column) => ProjectionItem.of(column, ColumnRef.of(tableName, column)));
|
|
1085
|
+
}
|
|
1086
|
+
function compileAggregate(contract, aggregates, namespaceId, tableName, state, aggregateSpec, modelName) {
|
|
1087
|
+
const entries = Object.entries(aggregateSpec);
|
|
1088
|
+
if (entries.length === 0) throw ormError("ORM.AGGREGATE_SELECTOR_MISSING", "aggregate() requires at least one aggregation selector", { meta: {
|
|
1089
|
+
method: "aggregate",
|
|
1090
|
+
namespaceId,
|
|
1091
|
+
tableName
|
|
1092
|
+
} });
|
|
1093
|
+
if (state.distinctOn !== void 0 && state.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
|
|
1094
|
+
const hasPagination = state.limit !== void 0 || state.offset !== void 0;
|
|
1095
|
+
const hasDistinct = state.distinct !== void 0 && state.distinct.length > 0 || state.distinctOn !== void 0 && state.distinctOn.length > 0;
|
|
1096
|
+
if (hasPagination || hasDistinct) {
|
|
1097
|
+
const { source } = buildAggregateInput(contract, namespaceId, tableName, state, modelName, aggregateInputColumns(tableName, entries, state.orderBy));
|
|
1098
|
+
const projection = entries.map(([alias, selector]) => {
|
|
1099
|
+
const { expr, codec } = toAggregateProjection(contract, aggregates, namespaceId, tableName, selector);
|
|
1100
|
+
return ProjectionItem.of(alias, expr, codec);
|
|
1101
|
+
});
|
|
1102
|
+
const ast = SelectAst.from(source).withProjection(projection);
|
|
1103
|
+
const { params } = deriveParamsFromAst(ast);
|
|
1104
|
+
return buildOrmQueryPlan(contract, ast, params);
|
|
1105
|
+
}
|
|
1106
|
+
const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
|
|
1107
|
+
const variantJoins = polyInfo && polyInfo.mtiVariants.length > 0 ? buildMtiJoins(contract, namespaceId, polyInfo, state.variantName, void 0).joins : [];
|
|
1108
|
+
const where = buildStateWhere(contract, tableName, state, { namespaceId });
|
|
1109
|
+
const projection = entries.map(([alias, selector]) => {
|
|
1110
|
+
const { expr, codec } = toAggregateProjection(contract, aggregates, namespaceId, tableName, selector);
|
|
1111
|
+
return ProjectionItem.of(alias, expr, codec);
|
|
1112
|
+
});
|
|
1113
|
+
let ast = SelectAst.from(tableSourceForContract(contract, namespaceId, tableName)).withProjection(projection);
|
|
1114
|
+
if (variantJoins.length > 0) ast = ast.withJoins(variantJoins);
|
|
1115
|
+
if (where) ast = ast.withWhere(where);
|
|
1116
|
+
const { params } = deriveParamsFromAst(ast);
|
|
1117
|
+
return buildOrmQueryPlan(contract, ast, params);
|
|
1118
|
+
}
|
|
1119
|
+
function compileGroupedAggregate(contract, aggregates, namespaceId, tableName, preGroupState, groupByColumns, aggregateSpec, havingExpr, modelName, postGroup = emptyGroupPagingState()) {
|
|
1120
|
+
if (groupByColumns.length === 0) throw ormError("ORM.GROUP_BY_FIELD_MISSING", "groupBy() requires at least one field", { meta: {
|
|
1121
|
+
namespaceId,
|
|
1122
|
+
tableName
|
|
1123
|
+
} });
|
|
1124
|
+
const entries = Object.entries(aggregateSpec);
|
|
1125
|
+
if (entries.length === 0) throw ormError("ORM.AGGREGATE_SELECTOR_MISSING", "groupBy().aggregate() requires at least one aggregation selector", { meta: {
|
|
1126
|
+
method: "groupBy.aggregate",
|
|
1127
|
+
namespaceId,
|
|
1128
|
+
tableName
|
|
1129
|
+
} });
|
|
1130
|
+
if (preGroupState.distinctOn !== void 0 && preGroupState.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
|
|
1131
|
+
const projection = [...groupByColumns.map((column) => ProjectionItem.of(column, ColumnRef.of(tableName, column), codecRefForStorageColumn(contract.storage, namespaceId, tableName, column))), ...entries.map(([alias, selector]) => {
|
|
1132
|
+
const { expr, codec } = toAggregateProjection(contract, aggregates, namespaceId, tableName, selector);
|
|
1133
|
+
return ProjectionItem.of(alias, expr, codec);
|
|
1134
|
+
})];
|
|
1135
|
+
const hasPagination = preGroupState.limit !== void 0 || preGroupState.offset !== void 0;
|
|
1136
|
+
const hasDistinct = preGroupState.distinct !== void 0 && preGroupState.distinct.length > 0 || preGroupState.distinctOn !== void 0 && preGroupState.distinctOn.length > 0;
|
|
1137
|
+
const needsInputSelect = hasPagination || hasDistinct;
|
|
1138
|
+
let ast;
|
|
1139
|
+
if (needsInputSelect) {
|
|
1140
|
+
const { source } = buildAggregateInput(contract, namespaceId, tableName, preGroupState, modelName, aggregateInputColumns(tableName, entries, preGroupState.orderBy, groupByColumns));
|
|
1141
|
+
ast = SelectAst.from(source).withProjection(projection).withGroupBy(groupByColumns.map((column) => ColumnRef.of(tableName, column)));
|
|
1142
|
+
} else {
|
|
1143
|
+
const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
|
|
1144
|
+
const variantJoins = polyInfo && polyInfo.mtiVariants.length > 0 ? buildMtiJoins(contract, namespaceId, polyInfo, preGroupState.variantName, void 0).joins : [];
|
|
1145
|
+
ast = SelectAst.from(tableSourceForContract(contract, namespaceId, tableName)).withProjection(projection).withGroupBy(groupByColumns.map((column) => ColumnRef.of(tableName, column)));
|
|
1146
|
+
if (variantJoins.length > 0) ast = ast.withJoins(variantJoins);
|
|
1147
|
+
const where = buildStateWhere(contract, tableName, preGroupState, { namespaceId });
|
|
1148
|
+
if (where) ast = ast.withWhere(where);
|
|
1149
|
+
}
|
|
1150
|
+
if (havingExpr) ast = ast.withHaving(validateGroupedHavingExpr(havingExpr));
|
|
1151
|
+
if (postGroup.orderBy.length > 0) ast = ast.withOrderBy(postGroup.orderBy);
|
|
1152
|
+
if (postGroup.limit !== void 0) ast = ast.withLimit(postGroup.limit);
|
|
1153
|
+
if (postGroup.offset !== void 0) ast = ast.withOffset(postGroup.offset);
|
|
1154
|
+
const { params } = deriveParamsFromAst(ast);
|
|
1155
|
+
return buildOrmQueryPlan(contract, ast, params);
|
|
1156
|
+
}
|
|
1157
|
+
function buildReturningColumns(contract, namespaceId, tableName, returningColumns) {
|
|
1158
|
+
return (returningColumns && returningColumns.length > 0 ? [...returningColumns] : resolveTableColumns(contract, namespaceId, tableName)).map((column) => ProjectionItem.of(column, ColumnRef.of(tableName, column), codecRefForStorageColumn(contract.storage, namespaceId, tableName, column)));
|
|
1159
|
+
}
|
|
1160
|
+
function toParamAssignments(contract, namespaceId, tableName, values) {
|
|
1161
|
+
const assignments = {};
|
|
1162
|
+
const table = storageTableForContract(contract, namespaceId, tableName);
|
|
1163
|
+
for (const [column, value] of Object.entries(values)) {
|
|
1164
|
+
if (!table.columns[column]) throw ormError("ORM.COLUMN_UNKNOWN", `Unknown column "${column}" in table "${tableName}"`, { meta: {
|
|
1165
|
+
namespaceId,
|
|
1166
|
+
tableName,
|
|
1167
|
+
column
|
|
1168
|
+
} });
|
|
1169
|
+
const codec = codecRefForStorageColumn(contract.storage, namespaceId, tableName, column);
|
|
1170
|
+
assignments[column] = ParamRef.of(value, {
|
|
1171
|
+
name: column,
|
|
1172
|
+
...ifDefined("codec", codec)
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
return { assignments };
|
|
1176
|
+
}
|
|
1177
|
+
function normalizeInsertRows(contract, namespaceId, tableName, rows) {
|
|
1178
|
+
if (rows.length === 0) throw new InternalError("normalizeInsertRows requires at least one row");
|
|
1179
|
+
const orderedColumns = [];
|
|
1180
|
+
const seenColumns = /* @__PURE__ */ new Set();
|
|
1181
|
+
for (const row of rows) for (const column of Object.keys(row)) {
|
|
1182
|
+
if (seenColumns.has(column)) continue;
|
|
1183
|
+
seenColumns.add(column);
|
|
1184
|
+
orderedColumns.push(column);
|
|
1185
|
+
}
|
|
1186
|
+
const table = storageTableForContract(contract, namespaceId, tableName);
|
|
1187
|
+
return { rows: rows.map((row) => {
|
|
1188
|
+
if (orderedColumns.length === 0) return {};
|
|
1189
|
+
const normalizedRow = {};
|
|
1190
|
+
for (const column of orderedColumns) {
|
|
1191
|
+
if (Object.hasOwn(row, column)) {
|
|
1192
|
+
if (!table.columns[column]) throw ormError("ORM.COLUMN_UNKNOWN", `Unknown column "${column}" in table "${tableName}"`, { meta: {
|
|
1193
|
+
namespaceId,
|
|
1194
|
+
tableName,
|
|
1195
|
+
column
|
|
1196
|
+
} });
|
|
1197
|
+
const codec = codecRefForStorageColumn(contract.storage, namespaceId, tableName, column);
|
|
1198
|
+
normalizedRow[column] = ParamRef.of(row[column], {
|
|
1199
|
+
name: column,
|
|
1200
|
+
...ifDefined("codec", codec)
|
|
1201
|
+
});
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
normalizedRow[column] = new DefaultValueExpr();
|
|
1205
|
+
}
|
|
1206
|
+
return normalizedRow;
|
|
1207
|
+
}) };
|
|
1208
|
+
}
|
|
1209
|
+
function compileInsertReturning(contract, namespaceId, tableName, rows, returningColumns) {
|
|
1210
|
+
const { rows: normalizedRows } = normalizeInsertRows(contract, namespaceId, tableName, rows);
|
|
1211
|
+
const ast = InsertAst.into(tableSourceForContract(contract, namespaceId, tableName)).withRows(normalizedRows).withReturning(buildReturningColumns(contract, namespaceId, tableName, returningColumns));
|
|
1212
|
+
const { params } = deriveParamsFromAst(ast);
|
|
1213
|
+
return buildOrmQueryPlan(contract, ast, params);
|
|
1214
|
+
}
|
|
1215
|
+
function compileInsertCount(contract, namespaceId, tableName, rows) {
|
|
1216
|
+
const { rows: normalizedRows } = normalizeInsertRows(contract, namespaceId, tableName, rows);
|
|
1217
|
+
const ast = InsertAst.into(tableSourceForContract(contract, namespaceId, tableName)).withRows(normalizedRows);
|
|
1218
|
+
const { params } = deriveParamsFromAst(ast);
|
|
1219
|
+
return buildOrmQueryPlan(contract, ast, params);
|
|
1220
|
+
}
|
|
1221
|
+
function stripUndefinedValues(row) {
|
|
805
1222
|
const result = {};
|
|
806
1223
|
for (const [key, value] of Object.entries(row)) if (value !== void 0) result[key] = value;
|
|
807
1224
|
return result;
|
|
808
1225
|
}
|
|
809
|
-
function createTableRefRemapper
|
|
1226
|
+
function createTableRefRemapper(fromTable, toTable) {
|
|
810
1227
|
return {
|
|
811
1228
|
columnRef: (col) => col.table === fromTable ? ColumnRef.of(toTable, col.column) : col,
|
|
812
1229
|
tableSource: (source) => {
|
|
@@ -824,7 +1241,7 @@ function buildCountMutationWhere(contract, namespaceId, tableName, filters, vari
|
|
|
824
1241
|
if (!polyInfo || !variant || variant.strategy !== "mti") return combineWhereExprs(filters);
|
|
825
1242
|
const pkColumn = resolvePrimaryKeyColumn(contract, namespaceId, tableName);
|
|
826
1243
|
const baseTableRef = `${tableName}__write_filter`;
|
|
827
|
-
const remapper = createTableRefRemapper
|
|
1244
|
+
const remapper = createTableRefRemapper(tableName, baseTableRef);
|
|
828
1245
|
const innerFilters = filters.map((filter) => filter.rewrite(remapper));
|
|
829
1246
|
const where = combineWhereExprs([BinaryExpr.eq(ColumnRef.of(baseTableRef, pkColumn), ColumnRef.of(tableName, pkColumn)), ...innerFilters]);
|
|
830
1247
|
const joinOn = EqColJoinOn.of(ColumnRef.of(baseTableRef, pkColumn), ColumnRef.of(variant.table, pkColumn));
|
|
@@ -917,137 +1334,6 @@ function augmentSelectionForJoinColumns(selectedFields, requiredColumns) {
|
|
|
917
1334
|
hiddenColumns
|
|
918
1335
|
};
|
|
919
1336
|
}
|
|
920
|
-
function namespaceCoordinateForSource(source) {
|
|
921
|
-
return source.kind === "table-source" ? source.namespaceId : void 0;
|
|
922
|
-
}
|
|
923
|
-
function bindWhereExpr(contract, expr, namespaceId) {
|
|
924
|
-
return bindWhereExprNode(contract, expr, namespaceId);
|
|
925
|
-
}
|
|
926
|
-
function bindWhereExprNode(contract, expr, namespaceId) {
|
|
927
|
-
return expr.accept({
|
|
928
|
-
columnRef(expr) {
|
|
929
|
-
return bindExpression(contract, expr);
|
|
930
|
-
},
|
|
931
|
-
identifierRef(expr) {
|
|
932
|
-
return expr;
|
|
933
|
-
},
|
|
934
|
-
subquery(expr) {
|
|
935
|
-
return bindExpression(contract, expr);
|
|
936
|
-
},
|
|
937
|
-
operation(expr) {
|
|
938
|
-
return bindExpression(contract, expr);
|
|
939
|
-
},
|
|
940
|
-
aggregate(expr) {
|
|
941
|
-
return bindExpression(contract, expr);
|
|
942
|
-
},
|
|
943
|
-
windowFunc(expr) {
|
|
944
|
-
return bindExpression(contract, expr);
|
|
945
|
-
},
|
|
946
|
-
functionCall(expr) {
|
|
947
|
-
return bindExpression(contract, expr);
|
|
948
|
-
},
|
|
949
|
-
cast(expr) {
|
|
950
|
-
return bindExpression(contract, expr);
|
|
951
|
-
},
|
|
952
|
-
case(expr) {
|
|
953
|
-
return bindExpression(contract, expr);
|
|
954
|
-
},
|
|
955
|
-
jsonObject(expr) {
|
|
956
|
-
return bindExpression(contract, expr);
|
|
957
|
-
},
|
|
958
|
-
jsonArrayAgg(expr) {
|
|
959
|
-
return bindExpression(contract, expr);
|
|
960
|
-
},
|
|
961
|
-
literal(expr) {
|
|
962
|
-
return expr;
|
|
963
|
-
},
|
|
964
|
-
param(expr) {
|
|
965
|
-
return expr;
|
|
966
|
-
},
|
|
967
|
-
preparedParam(expr) {
|
|
968
|
-
return expr;
|
|
969
|
-
},
|
|
970
|
-
list(expr) {
|
|
971
|
-
return bindExpression(contract, expr);
|
|
972
|
-
},
|
|
973
|
-
binary(expr) {
|
|
974
|
-
const left = bindExpression(contract, expr.left);
|
|
975
|
-
const bindingColumn = left.kind === "column-ref" ? left : void 0;
|
|
976
|
-
return new BinaryExpr(expr.op, left, bindComparable(contract, expr.right, bindingColumn, namespaceId));
|
|
977
|
-
},
|
|
978
|
-
and(expr) {
|
|
979
|
-
return AndExpr.of(expr.exprs.map((part) => bindWhereExprNode(contract, part, namespaceId)));
|
|
980
|
-
},
|
|
981
|
-
or(expr) {
|
|
982
|
-
return OrExpr.of(expr.exprs.map((part) => bindWhereExprNode(contract, part, namespaceId)));
|
|
983
|
-
},
|
|
984
|
-
exists(expr) {
|
|
985
|
-
return expr.notExists ? ExistsExpr.notExists(bindSelectAst(contract, expr.subquery)) : ExistsExpr.exists(bindSelectAst(contract, expr.subquery));
|
|
986
|
-
},
|
|
987
|
-
nullCheck(expr) {
|
|
988
|
-
return expr.isNull ? NullCheckExpr.isNull(bindExpression(contract, expr.expr)) : NullCheckExpr.isNotNull(bindExpression(contract, expr.expr));
|
|
989
|
-
},
|
|
990
|
-
not(expr) {
|
|
991
|
-
return new NotExpr(bindWhereExprNode(contract, expr.expr, namespaceId));
|
|
992
|
-
},
|
|
993
|
-
rawExpr(expr) {
|
|
994
|
-
return expr;
|
|
995
|
-
}
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
function bindComparable(contract, comparable, bindingColumn, namespaceId) {
|
|
999
|
-
if (comparable.kind === "param-ref" || bindingColumn === void 0) return comparable.kind === "param-ref" ? comparable : comparable.kind === "literal" || comparable.kind === "list" ? comparable : bindExpression(contract, comparable);
|
|
1000
|
-
if (comparable.kind === "literal") return createParamRef(contract, bindingColumn, comparable.value, namespaceId);
|
|
1001
|
-
if (comparable.kind === "list") return ListExpression.of(comparable.values.map((value) => value.kind === "literal" ? createParamRef(contract, bindingColumn, value.value, namespaceId) : value));
|
|
1002
|
-
return bindExpression(contract, comparable);
|
|
1003
|
-
}
|
|
1004
|
-
function createParamRef(contract, columnRef, value, namespaceId) {
|
|
1005
|
-
const resolved = resolveStorageTable(contract.storage, columnRef.table, namespaceId);
|
|
1006
|
-
if (resolved === void 0 || !resolved.table.columns[columnRef.column]) throw ormError("ORM.COLUMN_UNKNOWN", `Unknown column "${columnRef.column}" in table "${columnRef.table}"`, { meta: {
|
|
1007
|
-
tableName: columnRef.table,
|
|
1008
|
-
column: columnRef.column
|
|
1009
|
-
} });
|
|
1010
|
-
const codec = codecRefForStorageColumn(contract.storage, resolved.namespaceId, columnRef.table, columnRef.column);
|
|
1011
|
-
return ParamRef.of(value, codec ? { codec } : void 0);
|
|
1012
|
-
}
|
|
1013
|
-
function createExpressionBinder(contract) {
|
|
1014
|
-
return { select: (ast) => bindSelectAst(contract, ast) };
|
|
1015
|
-
}
|
|
1016
|
-
function bindExpression(contract, expr) {
|
|
1017
|
-
return expr.rewrite(createExpressionBinder(contract));
|
|
1018
|
-
}
|
|
1019
|
-
function bindProjectionExpr(contract, expr) {
|
|
1020
|
-
return expr.kind === "literal" ? expr : bindExpression(contract, expr);
|
|
1021
|
-
}
|
|
1022
|
-
function bindOrderByItem(contract, orderItem) {
|
|
1023
|
-
return new OrderByItem(bindExpression(contract, orderItem.expr), orderItem.dir);
|
|
1024
|
-
}
|
|
1025
|
-
function bindJoin(contract, join) {
|
|
1026
|
-
const namespaceId = namespaceCoordinateForSource(join.source);
|
|
1027
|
-
return new JoinAst(join.joinType, bindFromSource(contract, join.source), join.on.kind === "eq-col-join-on" ? join.on : bindWhereExprNode(contract, join.on, namespaceId), join.lateral);
|
|
1028
|
-
}
|
|
1029
|
-
function bindFromSource(contract, source) {
|
|
1030
|
-
if (source.kind === "table-source") return source;
|
|
1031
|
-
if (source.kind === "derived-table-source") return DerivedTableSource.as(source.alias, bindSelectAst(contract, source.query));
|
|
1032
|
-
return source;
|
|
1033
|
-
}
|
|
1034
|
-
function bindSelectAst(contract, ast) {
|
|
1035
|
-
const namespaceId = ast.from !== void 0 ? namespaceCoordinateForSource(ast.from) : void 0;
|
|
1036
|
-
return new SelectAst({
|
|
1037
|
-
...ast.from !== void 0 ? { from: bindFromSource(contract, ast.from) } : {},
|
|
1038
|
-
joins: ast.joins?.map((join) => bindJoin(contract, join)),
|
|
1039
|
-
projection: ast.projection.map((projection) => new ProjectionItem(projection.alias, bindProjectionExpr(contract, projection.expr), projection.codec)),
|
|
1040
|
-
where: ast.where ? bindWhereExprNode(contract, ast.where, namespaceId) : void 0,
|
|
1041
|
-
orderBy: ast.orderBy?.map((orderItem) => bindOrderByItem(contract, orderItem)),
|
|
1042
|
-
distinct: ast.distinct,
|
|
1043
|
-
distinctOn: ast.distinctOn?.map((expr) => bindExpression(contract, expr)),
|
|
1044
|
-
groupBy: ast.groupBy?.map((expr) => bindExpression(contract, expr)),
|
|
1045
|
-
having: ast.having ? bindWhereExprNode(contract, ast.having, namespaceId) : void 0,
|
|
1046
|
-
limit: ast.limit,
|
|
1047
|
-
offset: ast.offset,
|
|
1048
|
-
selectAllIntent: ast.selectAllIntent
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
1337
|
/**
|
|
1052
1338
|
* The rule for which JSON projection variant an include entry carries. Every
|
|
1053
1339
|
* site that puts a value into a `json_build_object` or a `json_agg` goes
|
|
@@ -1098,98 +1384,38 @@ function resolvePolymorphicProjectionSelection(contract, namespaceId, modelName,
|
|
|
1098
1384
|
columnToField: getCompleteColumnToFieldMap(contract, namespaceId, variant.modelName)
|
|
1099
1385
|
}));
|
|
1100
1386
|
const baseSelectedFields = [];
|
|
1101
|
-
const selectedMtiColumnsByTable = /* @__PURE__ */ new Map();
|
|
1102
|
-
let hasVariantOwnedSelection = false;
|
|
1103
|
-
for (const selectedField of state.selectedFields) {
|
|
1104
|
-
const baseColumn = baseFieldToColumn[selectedField] ?? (baseTableColumns.has(selectedField) ? selectedField : void 0);
|
|
1105
|
-
if (baseColumn !== void 0) appendUnique(baseSelectedFields, baseColumn);
|
|
1106
|
-
let matchedVariantField = false;
|
|
1107
|
-
for (const { variant, columnToField } of variantFieldMaps) for (const [column, field] of Object.entries(columnToField)) {
|
|
1108
|
-
if (selectedField !== field && selectedField !== column) continue;
|
|
1109
|
-
matchedVariantField = true;
|
|
1110
|
-
hasVariantOwnedSelection = true;
|
|
1111
|
-
if (variant.strategy === "sti") {
|
|
1112
|
-
appendUnique(baseSelectedFields, column);
|
|
1113
|
-
continue;
|
|
1114
|
-
}
|
|
1115
|
-
let selectedColumns = selectedMtiColumnsByTable.get(variant.table);
|
|
1116
|
-
if (selectedColumns === void 0) {
|
|
1117
|
-
selectedColumns = /* @__PURE__ */ new Set();
|
|
1118
|
-
selectedMtiColumnsByTable.set(variant.table, selectedColumns);
|
|
1119
|
-
}
|
|
1120
|
-
selectedColumns.add(column);
|
|
1121
|
-
}
|
|
1122
|
-
if (baseColumn === void 0 && !matchedVariantField) appendUnique(baseSelectedFields, selectedField);
|
|
1123
|
-
}
|
|
1124
|
-
return {
|
|
1125
|
-
baseSelectedFields,
|
|
1126
|
-
selectedMtiColumnsByTable,
|
|
1127
|
-
needsHiddenDiscriminator: state.variantName === void 0 && hasVariantOwnedSelection && !baseSelectedFields.includes(polyInfo.discriminatorColumn)
|
|
1128
|
-
};
|
|
1129
|
-
}
|
|
1130
|
-
function buildHiddenDiscriminatorProjection(contract, namespaceId, polyInfo, tableRef, needed) {
|
|
1131
|
-
if (!needed) return [];
|
|
1132
|
-
return [ProjectionItem.of(POLYMORPHIC_DISCRIMINATOR_ALIAS, ColumnRef.of(tableRef, polyInfo.discriminatorColumn), codecRefForStorageColumn(contract.storage, namespaceId, polyInfo.baseTable, polyInfo.discriminatorColumn))];
|
|
1133
|
-
}
|
|
1134
|
-
function createBoundaryExpr(tableName, entry) {
|
|
1135
|
-
return new BinaryExpr(entry.direction === "asc" ? "gt" : "lt", ColumnRef.of(tableName, entry.column), LiteralExpr.of(entry.value));
|
|
1136
|
-
}
|
|
1137
|
-
function buildLexicographicCursorWhere(tableName, entries) {
|
|
1138
|
-
const branches = entries.map((entry, index) => {
|
|
1139
|
-
const branchExprs = [];
|
|
1140
|
-
for (const prefixEntry of entries.slice(0, index)) branchExprs.push(BinaryExpr.eq(ColumnRef.of(tableName, prefixEntry.column), LiteralExpr.of(prefixEntry.value)));
|
|
1141
|
-
branchExprs.push(createBoundaryExpr(tableName, entry));
|
|
1142
|
-
if (branchExprs.length === 1) {
|
|
1143
|
-
const branch = branchExprs[0];
|
|
1144
|
-
assertDefined(branch, "cursor branch contains its boundary expression");
|
|
1145
|
-
return branch;
|
|
1146
|
-
}
|
|
1147
|
-
return AndExpr.of(branchExprs);
|
|
1148
|
-
});
|
|
1149
|
-
if (branches.length === 1) {
|
|
1150
|
-
const branch = branches[0];
|
|
1151
|
-
assertDefined(branch, "cursor expression contains its single branch");
|
|
1152
|
-
return branch;
|
|
1153
|
-
}
|
|
1154
|
-
return OrExpr.of(branches);
|
|
1155
|
-
}
|
|
1156
|
-
function buildCursorWhere(tableName, orderBy, cursor) {
|
|
1157
|
-
if (!cursor || !orderBy || orderBy.length === 0) return;
|
|
1158
|
-
const entries = [];
|
|
1159
|
-
for (const order of orderBy) {
|
|
1160
|
-
if (order.expr.kind !== "column-ref") continue;
|
|
1161
|
-
const column = order.expr.column;
|
|
1162
|
-
const value = cursor[column];
|
|
1163
|
-
if (value === void 0) throw ormError("ORM.CURSOR_VALUE_MISSING", `Missing cursor value for orderBy column "${column}"`, { meta: { column } });
|
|
1164
|
-
entries.push({
|
|
1165
|
-
column,
|
|
1166
|
-
direction: order.dir,
|
|
1167
|
-
value
|
|
1168
|
-
});
|
|
1387
|
+
const selectedMtiColumnsByTable = /* @__PURE__ */ new Map();
|
|
1388
|
+
let hasVariantOwnedSelection = false;
|
|
1389
|
+
for (const selectedField of state.selectedFields) {
|
|
1390
|
+
const baseColumn = baseFieldToColumn[selectedField] ?? (baseTableColumns.has(selectedField) ? selectedField : void 0);
|
|
1391
|
+
if (baseColumn !== void 0) appendUnique(baseSelectedFields, baseColumn);
|
|
1392
|
+
let matchedVariantField = false;
|
|
1393
|
+
for (const { variant, columnToField } of variantFieldMaps) for (const [column, field] of Object.entries(columnToField)) {
|
|
1394
|
+
if (selectedField !== field && selectedField !== column) continue;
|
|
1395
|
+
matchedVariantField = true;
|
|
1396
|
+
hasVariantOwnedSelection = true;
|
|
1397
|
+
if (variant.strategy === "sti") {
|
|
1398
|
+
appendUnique(baseSelectedFields, column);
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
let selectedColumns = selectedMtiColumnsByTable.get(variant.table);
|
|
1402
|
+
if (selectedColumns === void 0) {
|
|
1403
|
+
selectedColumns = /* @__PURE__ */ new Set();
|
|
1404
|
+
selectedMtiColumnsByTable.set(variant.table, selectedColumns);
|
|
1405
|
+
}
|
|
1406
|
+
selectedColumns.add(column);
|
|
1407
|
+
}
|
|
1408
|
+
if (baseColumn === void 0 && !matchedVariantField) appendUnique(baseSelectedFields, selectedField);
|
|
1169
1409
|
}
|
|
1170
|
-
const firstEntry = entries[0];
|
|
1171
|
-
if (entries.length === 1 && firstEntry !== void 0) return createBoundaryExpr(tableName, firstEntry);
|
|
1172
|
-
return buildLexicographicCursorWhere(tableName, entries);
|
|
1173
|
-
}
|
|
1174
|
-
function createTableRefRemapper(fromTable, toTable) {
|
|
1175
1410
|
return {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
if (!source.alias && source.name === fromTable) return TableSource.named(source.name, toTable, source.namespaceId);
|
|
1180
|
-
return source;
|
|
1181
|
-
},
|
|
1182
|
-
eqColJoinOn: (on) => EqColJoinOn.of(on.left.table === fromTable ? ColumnRef.of(toTable, on.left.column) : on.left, on.right.table === fromTable ? ColumnRef.of(toTable, on.right.column) : on.right)
|
|
1411
|
+
baseSelectedFields,
|
|
1412
|
+
selectedMtiColumnsByTable,
|
|
1413
|
+
needsHiddenDiscriminator: state.variantName === void 0 && hasVariantOwnedSelection && !baseSelectedFields.includes(polyInfo.discriminatorColumn)
|
|
1183
1414
|
};
|
|
1184
1415
|
}
|
|
1185
|
-
function
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
const boundFilters = state.filters.map((filter) => bindWhereExpr(contract, filter, options?.namespaceId));
|
|
1189
|
-
const remappedFilters = filterTableName && filterTableName !== tableName ? boundFilters.map((filter) => filter.rewrite(createTableRefRemapper(filterTableName, tableName))) : boundFilters;
|
|
1190
|
-
const boundCursorWhere = cursorWhere ? bindWhereExpr(contract, cursorWhere, options?.namespaceId) : void 0;
|
|
1191
|
-
const remappedCursorWhere = boundCursorWhere && filterTableName && filterTableName !== tableName ? boundCursorWhere.rewrite(createTableRefRemapper(filterTableName, tableName)) : boundCursorWhere;
|
|
1192
|
-
return combineWhereExprs(remappedCursorWhere ? [...remappedFilters, remappedCursorWhere] : remappedFilters);
|
|
1416
|
+
function buildHiddenDiscriminatorProjection(contract, namespaceId, polyInfo, tableRef, needed) {
|
|
1417
|
+
if (!needed) return [];
|
|
1418
|
+
return [ProjectionItem.of(POLYMORPHIC_DISCRIMINATOR_ALIAS, ColumnRef.of(tableRef, polyInfo.discriminatorColumn), codecRefForStorageColumn(contract.storage, namespaceId, polyInfo.baseTable, polyInfo.discriminatorColumn))];
|
|
1193
1419
|
}
|
|
1194
1420
|
function buildIncludeOrderArtifacts(relationName, rowAlias, childOrderBy) {
|
|
1195
1421
|
if (!childOrderBy || childOrderBy.length === 0) return {
|
|
@@ -1208,35 +1434,6 @@ function buildIncludeOrderArtifacts(relationName, rowAlias, childOrderBy) {
|
|
|
1208
1434
|
})
|
|
1209
1435
|
};
|
|
1210
1436
|
}
|
|
1211
|
-
/**
|
|
1212
|
-
* Wrap a base SELECT in a `ROW_NUMBER() OVER (PARTITION BY … ORDER BY …) = 1`
|
|
1213
|
-
* filter, implementing Prisma-style `.distinct(cols)` semantics: one
|
|
1214
|
-
* representative row per `(distinctColumnRefs)` group is kept; the rest
|
|
1215
|
-
* are dropped.
|
|
1216
|
-
*
|
|
1217
|
-
* Picking which row survives in each partition is governed by
|
|
1218
|
-
* `rankingOrderBy`. When the caller's `orderBy` doesn't fully order rows
|
|
1219
|
-
* within a partition (e.g. user wrote `.distinct('title')` with no
|
|
1220
|
-
* `orderBy`, or ties in their ordering), the choice is
|
|
1221
|
-
* implementation-defined — matching Prisma's documented nested-distinct
|
|
1222
|
-
* behaviour. Callers that want determinism should pass an `orderBy` that
|
|
1223
|
-
* is total within each partition.
|
|
1224
|
-
*
|
|
1225
|
-
* The wrapper forwards every column of `base.projection` through the
|
|
1226
|
-
* derived alias, so the wrapper's projection is byte-identical in alias
|
|
1227
|
-
* names — making this transparent to any outer query (`json_agg`,
|
|
1228
|
-
* correlated subquery, top-level SELECT) that consumes the SELECT.
|
|
1229
|
-
*/
|
|
1230
|
-
function wrapWithRowNumberDedup(options) {
|
|
1231
|
-
const { base, distinctColumnRefs, rankingOrderBy, rankedAlias } = options;
|
|
1232
|
-
const rnAlias = "__prisma_distinct_rn";
|
|
1233
|
-
const effectiveOrderBy = rankingOrderBy.length > 0 ? rankingOrderBy : distinctColumnRefs.map((expr) => OrderByItem.asc(expr));
|
|
1234
|
-
const inner = base.withProjection([...base.projection, ProjectionItem.of(rnAlias, WindowFuncExpr.rowNumber({
|
|
1235
|
-
partitionBy: distinctColumnRefs,
|
|
1236
|
-
orderBy: effectiveOrderBy
|
|
1237
|
-
}))]);
|
|
1238
|
-
return SelectAst.from(DerivedTableSource.as(rankedAlias, inner)).withProjection(base.projection.map((item) => ProjectionItem.of(item.alias, ColumnRef.of(rankedAlias, item.alias), item.codec))).withWhere(BinaryExpr.eq(ColumnRef.of(rankedAlias, rnAlias), LiteralExpr.of(1)));
|
|
1239
|
-
}
|
|
1240
1437
|
function localColumnsForRowInclude(include) {
|
|
1241
1438
|
return include.through?.parentLocalColumns ?? [include.localColumn];
|
|
1242
1439
|
}
|
|
@@ -1304,7 +1501,7 @@ function buildChildPolymorphismJoinsAndProjection(contract, include, childTableA
|
|
|
1304
1501
|
hiddenProjection,
|
|
1305
1502
|
baseSelectedFields: selection.baseSelectedFields
|
|
1306
1503
|
};
|
|
1307
|
-
const remapper = createTableRefRemapper(polyInfo.baseTable, childTableRef);
|
|
1504
|
+
const remapper = createTableRefRemapper$1(polyInfo.baseTable, childTableRef);
|
|
1308
1505
|
return {
|
|
1309
1506
|
joins: joins.map((join) => join.rewrite(remapper)),
|
|
1310
1507
|
projection,
|
|
@@ -1368,12 +1565,13 @@ function buildManyToManyJunctionArtifacts(parentLocalRefs, childTableRef, throug
|
|
|
1368
1565
|
}
|
|
1369
1566
|
function buildIncludeChildRowsSelect(contract, aggregates, parentSource, include) {
|
|
1370
1567
|
const childState = include.nested;
|
|
1568
|
+
if (childState.distinctOn !== void 0 && childState.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
|
|
1371
1569
|
const parentLocalRefs = resolveParentLocalRefs(parentSource, include, localColumnsForRowInclude(include));
|
|
1372
1570
|
const childSource = resolveChildTableSource(include, parentLocalRefs);
|
|
1373
1571
|
const childTableAlias = childSource.alias;
|
|
1374
1572
|
const childTableRef = childSource.tableRef;
|
|
1375
1573
|
const rowsAlias = `${include.relationName}__rows`;
|
|
1376
|
-
const remappedChildOrderBy = childTableAlias && childState.orderBy ? childState.orderBy.map((item) => item.rewrite(createTableRefRemapper(include.relatedTableName, childTableRef))) : childState.orderBy;
|
|
1574
|
+
const remappedChildOrderBy = childTableAlias && childState.orderBy ? childState.orderBy.map((item) => item.rewrite(createTableRefRemapper$1(include.relatedTableName, childTableRef))) : childState.orderBy;
|
|
1377
1575
|
const { childOrderBy, hiddenOrderProjection, aggregateOrderBy } = buildIncludeOrderArtifacts(include.relationName, rowsAlias, remappedChildOrderBy);
|
|
1378
1576
|
const childWhere = buildStateWhere(contract, childTableRef, childState, {
|
|
1379
1577
|
filterTableName: include.relatedTableName,
|
|
@@ -1503,8 +1701,8 @@ function buildDistinctNonLeafChildRowsSelect(options) {
|
|
|
1503
1701
|
};
|
|
1504
1702
|
}
|
|
1505
1703
|
/**
|
|
1506
|
-
* Build the inner SELECT for a scalar include reducer (
|
|
1507
|
-
*
|
|
1704
|
+
* Build the inner SELECT for a scalar include reducer (any operation the aggregate registry
|
|
1705
|
+
* contributes).
|
|
1508
1706
|
*
|
|
1509
1707
|
* Emits one row containing `json_build_object('value', AGG(...))`
|
|
1510
1708
|
* over the child relation correlated to the parent via the FK. The
|
|
@@ -1513,18 +1711,18 @@ function buildDistinctNonLeafChildRowsSelect(options) {
|
|
|
1513
1711
|
* pulls `.value` out).
|
|
1514
1712
|
*
|
|
1515
1713
|
* The refine state's pipeline composes through to the aggregate's
|
|
1516
|
-
* input set: `where` / `orderBy` / `
|
|
1714
|
+
* input set: `where` / `orderBy` / `limit` / `offset` / `distinct` shape
|
|
1517
1715
|
* the rows the aggregate sees, matching the natural compositional
|
|
1518
1716
|
* semantic of
|
|
1519
1717
|
*
|
|
1520
|
-
* `db.User.include('posts', p => p.where(W).
|
|
1718
|
+
* `db.User.include('posts', p => p.where(W).limit(N).count()) // ≤ N`
|
|
1521
1719
|
*
|
|
1522
|
-
* When `
|
|
1720
|
+
* When `limit` / `offset` / `distinct` is set, the aggregate's input
|
|
1523
1721
|
* cannot just be the bare correlated table — a top-level `LIMIT` on
|
|
1524
1722
|
* the aggregating SELECT only trims the (already one-row) output, not
|
|
1525
1723
|
* the rows being aggregated. We therefore wrap the source in a
|
|
1526
1724
|
* derived SELECT that materialises the shaped row set, then
|
|
1527
|
-
* aggregate over that. `orderBy` alone (no `
|
|
1725
|
+
* aggregate over that. `orderBy` alone (no `limit` / `offset` /
|
|
1528
1726
|
* `distinct`) is dropped at the SQL level since reordering does not
|
|
1529
1727
|
* change which rows are aggregated.
|
|
1530
1728
|
*/
|
|
@@ -1542,6 +1740,7 @@ function buildIncludeChildScalarSelect(contract, aggregates, parentSource, inclu
|
|
|
1542
1740
|
const childTableAlias = childSource.alias;
|
|
1543
1741
|
const childTableRef = childSource.tableRef;
|
|
1544
1742
|
const state = scalar.state;
|
|
1743
|
+
if (state.distinctOn !== void 0 && state.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
|
|
1545
1744
|
const childWhere = buildStateWhere(contract, childTableRef, state, {
|
|
1546
1745
|
filterTableName: include.relatedTableName,
|
|
1547
1746
|
namespaceId: include.relatedNamespaceId
|
|
@@ -1558,7 +1757,7 @@ function buildIncludeChildScalarSelect(contract, aggregates, parentSource, inclu
|
|
|
1558
1757
|
const joinExpr = BinaryExpr.eq(ColumnRef.of(childTableRef, include.targetColumn), parentLocalRef);
|
|
1559
1758
|
whereExpr = childWhere ? AndExpr.of([joinExpr, childWhere]) : joinExpr;
|
|
1560
1759
|
}
|
|
1561
|
-
const remappedOrderBy = childTableAlias && state.orderBy ? state.orderBy.map((item) => item.rewrite(createTableRefRemapper(include.relatedTableName, childTableRef))) : state.orderBy;
|
|
1760
|
+
const remappedOrderBy = childTableAlias && state.orderBy ? state.orderBy.map((item) => item.rewrite(createTableRefRemapper$1(include.relatedTableName, childTableRef))) : state.orderBy;
|
|
1562
1761
|
const hasPagination = state.limit !== void 0 || state.offset !== void 0;
|
|
1563
1762
|
const hasDistinct = state.distinct !== void 0 && state.distinct.length > 0 || state.distinctOn !== void 0 && state.distinctOn.length > 0;
|
|
1564
1763
|
if (!(hasPagination || hasDistinct)) {
|
|
@@ -1615,7 +1814,7 @@ function buildIncludeAggregateExpr(scalar, childTableRef, lower, inputCodec) {
|
|
|
1615
1814
|
* Row branches reuse the standalone row-include builder; scalar
|
|
1616
1815
|
* branches reuse `buildIncludeChildScalarSelect` — the `{value: ...}`
|
|
1617
1816
|
* envelope survives into the combined JSON and the decoder unwraps
|
|
1618
|
-
* it per scalar branch. Distinct/
|
|
1817
|
+
* it per scalar branch. Distinct/limit/offset semantics inside a row
|
|
1619
1818
|
* branch fan out naturally because the row builder is invoked with
|
|
1620
1819
|
* a synthetic IncludeExpr whose `nested` is the branch's state.
|
|
1621
1820
|
*/
|
|
@@ -1676,13 +1875,11 @@ function buildCorrelatedIncludeProjection(contract, aggregates, parentSource, in
|
|
|
1676
1875
|
}
|
|
1677
1876
|
function buildSelectAst(contract, tableName, state, options) {
|
|
1678
1877
|
const namespaceId = options.namespaceId;
|
|
1878
|
+
if (state.distinctOn !== void 0 && state.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
|
|
1679
1879
|
const projection = [...buildProjection(contract, namespaceId, tableName, state.selectedFields, tableName), ...options.includeProjection ?? []];
|
|
1680
|
-
const where = options.where ?? buildStateWhere(contract, tableName, state, { namespaceId });
|
|
1681
|
-
const usesRowNumberDistinct = state.distinct !== void 0 && state.distinct.length > 0;
|
|
1682
|
-
const fromSource = usesRowNumberDistinct ? DerivedTableSource.as(tableName, buildTopLevelDistinctRankedInner(contract, namespaceId, tableName, state, where)) : tableSourceForContract(contract, namespaceId, tableName);
|
|
1880
|
+
const { source: fromSource, where: effectiveWhere } = buildDedupedTableSource(contract, namespaceId, tableName, state, options.where ?? buildStateWhere(contract, tableName, state, { namespaceId }), resolveTableColumns(contract, namespaceId, tableName).map((column) => ProjectionItem.of(column, ColumnRef.of(tableName, column))));
|
|
1683
1881
|
let ast = SelectAst.from(fromSource).withProjection(projection);
|
|
1684
|
-
if (
|
|
1685
|
-
else if (where) ast = ast.withWhere(where);
|
|
1882
|
+
if (effectiveWhere) ast = ast.withWhere(effectiveWhere);
|
|
1686
1883
|
if (state.orderBy) ast = ast.withOrderBy(state.orderBy);
|
|
1687
1884
|
if (state.selectedFields === void 0) ast = ast.withSelectAllIntent({ table: tableName });
|
|
1688
1885
|
if (state.distinctOn && state.distinctOn.length > 0) ast = ast.withDistinctOn(state.distinctOn.map((column) => ColumnRef.of(tableName, column)));
|
|
@@ -1690,197 +1887,70 @@ function buildSelectAst(contract, tableName, state, options) {
|
|
|
1690
1887
|
if (state.offset !== void 0) ast = ast.withOffset(state.offset);
|
|
1691
1888
|
if (options.joins && options.joins.length > 0) ast = ast.withJoins(options.joins);
|
|
1692
1889
|
return ast;
|
|
1693
|
-
}
|
|
1694
|
-
function
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
const
|
|
1698
|
-
const
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
|
|
1733
|
-
const selection = polyInfo && modelName ? resolvePolymorphicProjectionSelection(contract, namespaceId, modelName, polyInfo, state) : void 0;
|
|
1734
|
-
const projectionState = selection ? {
|
|
1735
|
-
...state,
|
|
1736
|
-
selectedFields: selection.baseSelectedFields
|
|
1737
|
-
} : state;
|
|
1738
|
-
const mtiArtifacts = polyInfo && polyInfo.mtiVariants.length > 0 ? buildMtiJoins(contract, namespaceId, polyInfo, state.variantName, selection?.selectedMtiColumnsByTable) : void 0;
|
|
1739
|
-
const hiddenProjection = polyInfo && selection ? buildHiddenDiscriminatorProjection(contract, namespaceId, polyInfo, tableName, selection.needsHiddenDiscriminator) : [];
|
|
1740
|
-
const ast = buildSelectAst(contract, tableName, {
|
|
1741
|
-
...projectionState,
|
|
1742
|
-
includes: []
|
|
1743
|
-
}, {
|
|
1744
|
-
joins: mtiArtifacts?.joins ?? [],
|
|
1745
|
-
includeProjection: [...mtiArtifacts?.projection ?? [], ...hiddenProjection],
|
|
1746
|
-
namespaceId
|
|
1747
|
-
});
|
|
1748
|
-
const { params } = deriveParamsFromAst(ast);
|
|
1749
|
-
return buildOrmQueryPlan(contract, ast, params, state.annotations);
|
|
1750
|
-
}
|
|
1751
|
-
function compileSelectWithIncludes(contract, aggregates, namespaceId, tableName, state, modelName) {
|
|
1752
|
-
const includeJoins = [];
|
|
1753
|
-
const includeProjection = [];
|
|
1754
|
-
const topLevelWhere = buildStateWhere(contract, tableName, state, { namespaceId });
|
|
1755
|
-
const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
|
|
1756
|
-
const selection = polyInfo && modelName ? resolvePolymorphicProjectionSelection(contract, namespaceId, modelName, polyInfo, state) : void 0;
|
|
1757
|
-
const projectionState = selection ? {
|
|
1758
|
-
...state,
|
|
1759
|
-
selectedFields: selection.baseSelectedFields
|
|
1760
|
-
} : state;
|
|
1761
|
-
if (polyInfo && selection) {
|
|
1762
|
-
if (polyInfo.mtiVariants.length > 0) {
|
|
1763
|
-
const mtiArtifacts = buildMtiJoins(contract, namespaceId, polyInfo, state.variantName, selection.selectedMtiColumnsByTable);
|
|
1764
|
-
includeJoins.push(...mtiArtifacts.joins);
|
|
1765
|
-
includeProjection.push(...mtiArtifacts.projection);
|
|
1766
|
-
}
|
|
1767
|
-
includeProjection.push(...buildHiddenDiscriminatorProjection(contract, namespaceId, polyInfo, tableName, selection.needsHiddenDiscriminator));
|
|
1768
|
-
}
|
|
1769
|
-
const parentSource = {
|
|
1770
|
-
baseTableName: tableName,
|
|
1771
|
-
tableRef: tableName,
|
|
1772
|
-
variantColumnsProjected: false
|
|
1773
|
-
};
|
|
1774
|
-
for (const include of state.includes) {
|
|
1775
|
-
const artifact = buildCorrelatedIncludeProjection(contract, aggregates, parentSource, include);
|
|
1776
|
-
includeProjection.push(artifact.projection);
|
|
1777
|
-
}
|
|
1778
|
-
const ast = buildSelectAst(contract, tableName, {
|
|
1779
|
-
...projectionState,
|
|
1780
|
-
includes: []
|
|
1781
|
-
}, {
|
|
1782
|
-
joins: includeJoins,
|
|
1783
|
-
includeProjection,
|
|
1784
|
-
namespaceId,
|
|
1785
|
-
...ifDefined("where", topLevelWhere)
|
|
1786
|
-
});
|
|
1787
|
-
const { params } = deriveParamsFromAst(ast);
|
|
1788
|
-
return buildOrmQueryPlan(contract, ast, params, state.annotations);
|
|
1789
|
-
}
|
|
1790
|
-
function queryPlanRows(scope, plan) {
|
|
1791
|
-
return scope.query(plan);
|
|
1792
|
-
}
|
|
1793
|
-
function emptyState() {
|
|
1794
|
-
return {
|
|
1795
|
-
filters: [],
|
|
1796
|
-
includes: [],
|
|
1797
|
-
orderBy: void 0,
|
|
1798
|
-
cursor: void 0,
|
|
1799
|
-
distinct: void 0,
|
|
1800
|
-
distinctOn: void 0,
|
|
1801
|
-
selectedFields: void 0,
|
|
1802
|
-
limit: void 0,
|
|
1803
|
-
offset: void 0,
|
|
1804
|
-
variantName: void 0,
|
|
1805
|
-
annotations: /* @__PURE__ */ new Map()
|
|
1806
|
-
};
|
|
1807
|
-
}
|
|
1808
|
-
function param(codec, value) {
|
|
1809
|
-
if (codec === void 0) return ParamRef.of(value);
|
|
1810
|
-
return ParamRef.of(value, { codec });
|
|
1811
|
-
}
|
|
1812
|
-
function paramList(codec, values) {
|
|
1813
|
-
return ListExpression.of(values.map((value) => param(codec, value)));
|
|
1814
|
-
}
|
|
1815
|
-
function scalarComparisonMethod(op) {
|
|
1816
|
-
return ((left, codec) => (value) => {
|
|
1817
|
-
if (value === null && (op === "eq" || op === "neq")) return op === "eq" ? NullCheckExpr.isNull(left) : NullCheckExpr.isNotNull(left);
|
|
1818
|
-
return new BinaryExpr(op, left, param(codec, value));
|
|
1819
|
-
});
|
|
1820
|
-
}
|
|
1821
|
-
function listComparisonMethod(op) {
|
|
1822
|
-
return ((left, codec) => (values) => new BinaryExpr(op, left, paramList(codec, values)));
|
|
1823
|
-
}
|
|
1824
|
-
/**
|
|
1825
|
-
* Declares trait requirements and runtime factory for each comparison method.
|
|
1826
|
-
*
|
|
1827
|
-
* - `traits: []` means "no trait required" — always available
|
|
1828
|
-
* - Multi-trait: `traits: ['equality', 'order']` means BOTH traits are required
|
|
1829
|
-
*/
|
|
1830
|
-
const COMPARISON_METHODS_META = {
|
|
1831
|
-
eq: {
|
|
1832
|
-
traits: ["equality"],
|
|
1833
|
-
create: scalarComparisonMethod("eq")
|
|
1834
|
-
},
|
|
1835
|
-
neq: {
|
|
1836
|
-
traits: ["equality"],
|
|
1837
|
-
create: scalarComparisonMethod("neq")
|
|
1838
|
-
},
|
|
1839
|
-
in: {
|
|
1840
|
-
traits: ["equality"],
|
|
1841
|
-
create: listComparisonMethod("in")
|
|
1842
|
-
},
|
|
1843
|
-
notIn: {
|
|
1844
|
-
traits: ["equality"],
|
|
1845
|
-
create: listComparisonMethod("notIn")
|
|
1846
|
-
},
|
|
1847
|
-
gt: {
|
|
1848
|
-
traits: ["order"],
|
|
1849
|
-
create: scalarComparisonMethod("gt")
|
|
1850
|
-
},
|
|
1851
|
-
lt: {
|
|
1852
|
-
traits: ["order"],
|
|
1853
|
-
create: scalarComparisonMethod("lt")
|
|
1854
|
-
},
|
|
1855
|
-
gte: {
|
|
1856
|
-
traits: ["order"],
|
|
1857
|
-
create: scalarComparisonMethod("gte")
|
|
1858
|
-
},
|
|
1859
|
-
lte: {
|
|
1860
|
-
traits: ["order"],
|
|
1861
|
-
create: scalarComparisonMethod("lte")
|
|
1862
|
-
},
|
|
1863
|
-
like: {
|
|
1864
|
-
traits: ["textual"],
|
|
1865
|
-
create: scalarComparisonMethod("like")
|
|
1866
|
-
},
|
|
1867
|
-
asc: {
|
|
1868
|
-
traits: ["order"],
|
|
1869
|
-
create: (left) => () => OrderByItem.asc(left)
|
|
1870
|
-
},
|
|
1871
|
-
desc: {
|
|
1872
|
-
traits: ["order"],
|
|
1873
|
-
create: (left) => () => OrderByItem.desc(left)
|
|
1874
|
-
},
|
|
1875
|
-
isNull: {
|
|
1876
|
-
traits: [],
|
|
1877
|
-
create: (left) => () => NullCheckExpr.isNull(left)
|
|
1878
|
-
},
|
|
1879
|
-
isNotNull: {
|
|
1880
|
-
traits: [],
|
|
1881
|
-
create: (left) => () => NullCheckExpr.isNotNull(left)
|
|
1890
|
+
}
|
|
1891
|
+
function compileSelect(contract, namespaceId, tableName, state, modelName) {
|
|
1892
|
+
if (state.distinctOn !== void 0 && state.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
|
|
1893
|
+
const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
|
|
1894
|
+
const selection = polyInfo && modelName ? resolvePolymorphicProjectionSelection(contract, namespaceId, modelName, polyInfo, state) : void 0;
|
|
1895
|
+
const projectionState = selection ? {
|
|
1896
|
+
...state,
|
|
1897
|
+
selectedFields: selection.baseSelectedFields
|
|
1898
|
+
} : state;
|
|
1899
|
+
const mtiArtifacts = polyInfo && polyInfo.mtiVariants.length > 0 ? buildMtiJoins(contract, namespaceId, polyInfo, state.variantName, selection?.selectedMtiColumnsByTable) : void 0;
|
|
1900
|
+
const hiddenProjection = polyInfo && selection ? buildHiddenDiscriminatorProjection(contract, namespaceId, polyInfo, tableName, selection.needsHiddenDiscriminator) : [];
|
|
1901
|
+
const ast = buildSelectAst(contract, tableName, {
|
|
1902
|
+
...projectionState,
|
|
1903
|
+
includes: []
|
|
1904
|
+
}, {
|
|
1905
|
+
joins: mtiArtifacts?.joins ?? [],
|
|
1906
|
+
includeProjection: [...mtiArtifacts?.projection ?? [], ...hiddenProjection],
|
|
1907
|
+
namespaceId
|
|
1908
|
+
});
|
|
1909
|
+
const { params } = deriveParamsFromAst(ast);
|
|
1910
|
+
return buildOrmQueryPlan(contract, ast, params, state.annotations);
|
|
1911
|
+
}
|
|
1912
|
+
function compileSelectWithIncludes(contract, aggregates, namespaceId, tableName, state, modelName) {
|
|
1913
|
+
const includeJoins = [];
|
|
1914
|
+
const includeProjection = [];
|
|
1915
|
+
const topLevelWhere = buildStateWhere(contract, tableName, state, { namespaceId });
|
|
1916
|
+
const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
|
|
1917
|
+
const selection = polyInfo && modelName ? resolvePolymorphicProjectionSelection(contract, namespaceId, modelName, polyInfo, state) : void 0;
|
|
1918
|
+
const projectionState = selection ? {
|
|
1919
|
+
...state,
|
|
1920
|
+
selectedFields: selection.baseSelectedFields
|
|
1921
|
+
} : state;
|
|
1922
|
+
if (polyInfo && selection) {
|
|
1923
|
+
if (polyInfo.mtiVariants.length > 0) {
|
|
1924
|
+
const mtiArtifacts = buildMtiJoins(contract, namespaceId, polyInfo, state.variantName, selection.selectedMtiColumnsByTable);
|
|
1925
|
+
includeJoins.push(...mtiArtifacts.joins);
|
|
1926
|
+
includeProjection.push(...mtiArtifacts.projection);
|
|
1927
|
+
}
|
|
1928
|
+
includeProjection.push(...buildHiddenDiscriminatorProjection(contract, namespaceId, polyInfo, tableName, selection.needsHiddenDiscriminator));
|
|
1882
1929
|
}
|
|
1883
|
-
|
|
1930
|
+
const parentSource = {
|
|
1931
|
+
baseTableName: tableName,
|
|
1932
|
+
tableRef: tableName,
|
|
1933
|
+
variantColumnsProjected: false
|
|
1934
|
+
};
|
|
1935
|
+
for (const include of state.includes) {
|
|
1936
|
+
const artifact = buildCorrelatedIncludeProjection(contract, aggregates, parentSource, include);
|
|
1937
|
+
includeProjection.push(artifact.projection);
|
|
1938
|
+
}
|
|
1939
|
+
const ast = buildSelectAst(contract, tableName, {
|
|
1940
|
+
...projectionState,
|
|
1941
|
+
includes: []
|
|
1942
|
+
}, {
|
|
1943
|
+
joins: includeJoins,
|
|
1944
|
+
includeProjection,
|
|
1945
|
+
namespaceId,
|
|
1946
|
+
...ifDefined("where", topLevelWhere)
|
|
1947
|
+
});
|
|
1948
|
+
const { params } = deriveParamsFromAst(ast);
|
|
1949
|
+
return buildOrmQueryPlan(contract, ast, params, state.annotations);
|
|
1950
|
+
}
|
|
1951
|
+
function queryPlanRows(scope, plan) {
|
|
1952
|
+
return scope.query(plan);
|
|
1953
|
+
}
|
|
1884
1954
|
function dispatchCollectionRows(options) {
|
|
1885
1955
|
const { context, runtime, state, tableName, modelName, namespaceId } = options;
|
|
1886
1956
|
const { contract } = context;
|
|
@@ -1892,22 +1962,18 @@ function dispatchWithIncludes(options) {
|
|
|
1892
1962
|
const { context, runtime, state, tableName, modelName, namespaceId } = options;
|
|
1893
1963
|
const { contract } = context;
|
|
1894
1964
|
const generator = async function* () {
|
|
1895
|
-
const
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
for (const row of parentRows) yield blindCast(row.mapped);
|
|
1908
|
-
} finally {
|
|
1909
|
-
if (release) await release();
|
|
1910
|
-
}
|
|
1965
|
+
const compiled = compileSelectWithIncludes(contract, context.aggregateDescriptors, namespaceId, tableName, state, modelName);
|
|
1966
|
+
const parentRowsRaw = await queryPlanRows(runtime, compiled).toArray();
|
|
1967
|
+
if (parentRowsRaw.length === 0) return;
|
|
1968
|
+
const polyInfo = resolvePolymorphismInfo(contract, namespaceId, modelName);
|
|
1969
|
+
const parentRows = parentRowsRaw.map((row) => {
|
|
1970
|
+
return {
|
|
1971
|
+
raw: row,
|
|
1972
|
+
mapped: polyInfo ? mapPolymorphicRow(contract, namespaceId, modelName, polyInfo, row, state.variantName) : mapStorageRowToModelFields(contract, namespaceId, modelName, row)
|
|
1973
|
+
};
|
|
1974
|
+
});
|
|
1975
|
+
for (const parent of parentRows) for (const include of state.includes) parent.mapped[include.relationName] = await decodeIncludePayload(contract, context, include, parent.raw[include.relationName]);
|
|
1976
|
+
for (const row of parentRows) yield blindCast(row.mapped);
|
|
1911
1977
|
};
|
|
1912
1978
|
return new AsyncIterableResult(generator());
|
|
1913
1979
|
}
|
|
@@ -1981,11 +2047,9 @@ function buildIdentityInFilter(contract, namespaceId, tableName, identityColumns
|
|
|
1981
2047
|
*
|
|
1982
2048
|
* Scalar leaves arrive wrapped in a `{ value: <primitive> }` JSON
|
|
1983
2049
|
* envelope (see `buildIncludeChildScalarSelect`); the branch below
|
|
1984
|
-
* unwraps that envelope and passes the value straight through. The
|
|
1985
|
-
*
|
|
1986
|
-
*
|
|
1987
|
-
* `MAX` over an empty input set are SQL `NULL`, which surfaces as
|
|
1988
|
-
* `null` in TS — the documented contract for those reducers.
|
|
2050
|
+
* unwraps that envelope and passes the value straight through. The empty-relation value reads off
|
|
2051
|
+
* the operation's declared row via `emptyAggregateResult`: `null` where the row is nullable,
|
|
2052
|
+
* otherwise the row's declared empty value (so `count()` is `0`).
|
|
1989
2053
|
*
|
|
1990
2054
|
* Combine descriptors arrive as a JSON object keyed by branch name;
|
|
1991
2055
|
* each branch is dispatched to the row or scalar decoder per its
|
|
@@ -2100,10 +2164,7 @@ function wrapIncludedDecodeFailure(error, ref, codecId) {
|
|
|
2100
2164
|
* On a parent with zero matching child rows the correlated subquery
|
|
2101
2165
|
* still produces one row (aggregates collapse the empty input to a
|
|
2102
2166
|
* single row), so the combine envelope here is always present in the
|
|
2103
|
-
* read path.
|
|
2104
|
-
* mutation read-back's `assignEmptyMutationIncludes` writes the empty
|
|
2105
|
-
* per-branch shape directly to `parent.mapped[relationName]` for any
|
|
2106
|
-
* parent absent from the read-back result and never enters the decoder,
|
|
2167
|
+
* read path. Mutation read-back goes through the same read dispatch,
|
|
2107
2168
|
* so a missing or non-object envelope here is always a planner/decoder
|
|
2108
2169
|
* bug — `parseCombineEnvelope` throws loudly rather than papering over
|
|
2109
2170
|
* it with an empty shape.
|
|
@@ -2143,8 +2204,7 @@ function describeEnvelopeShape(value) {
|
|
|
2143
2204
|
*
|
|
2144
2205
|
* Contract: the envelope is always either
|
|
2145
2206
|
* - a `{ value: <primitive> }` JSON object (the SQL path), or
|
|
2146
|
-
* - `null` / `undefined` (the
|
|
2147
|
-
* short-circuit, for a parent absent from the read-back result).
|
|
2207
|
+
* - `null` / `undefined` (no row reached the decoder), answered with `emptyAggregateResult`.
|
|
2148
2208
|
*
|
|
2149
2209
|
* Any other shape — array, primitive, string that JSON-parses to
|
|
2150
2210
|
* non-object — indicates a planner / decoder bug, so we throw
|
|
@@ -2255,224 +2315,78 @@ function dispatchSplitMutationRows(options) {
|
|
|
2255
2315
|
const mapped = mapStorageRow(rawRow);
|
|
2256
2316
|
if (hiddenColumns.length > 0) stripHiddenMappedFields(contract, namespaceId, modelName, mapped, hiddenColumns);
|
|
2257
2317
|
yield mapRow(mapped);
|
|
2258
|
-
}
|
|
2259
|
-
};
|
|
2260
|
-
return new AsyncIterableResult(generator());
|
|
2261
|
-
}
|
|
2262
|
-
async function executeMutationReturningSingleRow(options) {
|
|
2263
|
-
const { context, runtime, compiled, tableName, modelName, namespaceId, variantName, includes, selectedFields, hiddenColumns, mapRow, operation, onMissingRowMessage } = options;
|
|
2264
|
-
const { contract } = context;
|
|
2265
|
-
const mapStorageRow = createMutationRowMapper(contract, namespaceId, modelName, variantName);
|
|
2266
|
-
if (includes.length === 0) {
|
|
2267
|
-
const first = (await queryPlanRows(runtime, compiled).toArray())[0];
|
|
2268
|
-
if (!first) return null;
|
|
2269
|
-
const mapped = mapStorageRow(first);
|
|
2270
|
-
if (hiddenColumns.length > 0) stripHiddenMappedFields(contract, namespaceId, modelName, mapped, hiddenColumns);
|
|
2271
|
-
return mapRow(mapped);
|
|
2272
|
-
}
|
|
2273
|
-
const identityRows = await queryPlanRows(runtime, compiled).toArray();
|
|
2274
|
-
if (identityRows.length === 0) return null;
|
|
2275
|
-
for await (const row of reloadMutationRowsByIdentities({
|
|
2276
|
-
context,
|
|
2277
|
-
runtime,
|
|
2278
|
-
tableName,
|
|
2279
|
-
modelName,
|
|
2280
|
-
namespaceId,
|
|
2281
|
-
identityRows,
|
|
2282
|
-
selectedFields,
|
|
2283
|
-
includes
|
|
2284
|
-
})) return row;
|
|
2285
|
-
throw ormError("ORM.MUTATION_ROW_MISSING", onMissingRowMessage, { meta: {
|
|
2286
|
-
operation,
|
|
2287
|
-
model: modelName,
|
|
2288
|
-
tableName
|
|
2289
|
-
} });
|
|
2290
|
-
}
|
|
2291
|
-
function and(...exprs) {
|
|
2292
|
-
return AndExpr.of(exprs);
|
|
2293
|
-
}
|
|
2294
|
-
function or(...exprs) {
|
|
2295
|
-
return OrExpr.of(exprs);
|
|
2296
|
-
}
|
|
2297
|
-
function not(expr) {
|
|
2298
|
-
return expr.not();
|
|
2299
|
-
}
|
|
2300
|
-
function all() {
|
|
2301
|
-
return AndExpr.true();
|
|
2302
|
-
}
|
|
2303
|
-
function shorthandToWhereExpr(context, namespaceId, modelName, filters) {
|
|
2304
|
-
const contract = context.contract;
|
|
2305
|
-
const tableName = resolveModelTableName(contract, namespaceId, modelName);
|
|
2306
|
-
const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
|
|
2307
|
-
const exprs = [];
|
|
2308
|
-
for (const [fieldName, value] of Object.entries(filters)) {
|
|
2309
|
-
if (value === void 0) continue;
|
|
2310
|
-
const columnName = fieldToColumn[fieldName] ?? fieldName;
|
|
2311
|
-
const left = ColumnRef.of(tableName, columnName);
|
|
2312
|
-
if (value === null) {
|
|
2313
|
-
exprs.push(NullCheckExpr.isNull(left));
|
|
2314
|
-
continue;
|
|
2315
|
-
}
|
|
2316
|
-
assertFieldHasEqualityTrait(context, namespaceId, modelName, fieldName);
|
|
2317
|
-
exprs.push(BinaryExpr.eq(left, LiteralExpr.of(value)));
|
|
2318
|
-
}
|
|
2319
|
-
if (exprs.length === 0) return;
|
|
2320
|
-
return exprs.length === 1 ? exprs[0] : and(...exprs);
|
|
2321
|
-
}
|
|
2322
|
-
function assertFieldHasEqualityTrait(context, namespaceId, modelName, fieldName) {
|
|
2323
|
-
const fieldType = modelOf(context.contract, namespaceId, modelName)?.fields?.[fieldName]?.type;
|
|
2324
|
-
const codecId = fieldType?.kind === "scalar" ? fieldType.codecId : void 0;
|
|
2325
|
-
if (!(codecId ? context.codecDescriptors.descriptorFor(codecId)?.traits ?? [] : []).includes("equality")) throw ormError("ORM.FILTER_UNSUPPORTED", `Shorthand filter on "${modelName}.${fieldName}": field does not support equality comparisons`, { meta: {
|
|
2326
|
-
model: modelName,
|
|
2327
|
-
field: fieldName,
|
|
2328
|
-
trait: "equality"
|
|
2329
|
-
} });
|
|
2330
|
-
}
|
|
2331
|
-
var GroupedCollection = class GroupedCollection {
|
|
2332
|
-
ctx;
|
|
2333
|
-
contract;
|
|
2334
|
-
modelName;
|
|
2335
|
-
tableName;
|
|
2336
|
-
namespaceId;
|
|
2337
|
-
baseFilters;
|
|
2338
|
-
groupByFields;
|
|
2339
|
-
groupByColumns;
|
|
2340
|
-
havingFilters;
|
|
2341
|
-
constructor(ctx, modelName, options) {
|
|
2342
|
-
this.ctx = ctx;
|
|
2343
|
-
this.contract = ctx.context.contract;
|
|
2344
|
-
this.modelName = modelName;
|
|
2345
|
-
this.tableName = options.tableName;
|
|
2346
|
-
this.namespaceId = options.namespaceId;
|
|
2347
|
-
this.baseFilters = options.baseFilters;
|
|
2348
|
-
this.groupByFields = options.groupByFields;
|
|
2349
|
-
this.groupByColumns = options.groupByColumns;
|
|
2350
|
-
this.havingFilters = options.havingFilters;
|
|
2351
|
-
}
|
|
2352
|
-
having(predicate) {
|
|
2353
|
-
const havingExpr = predicate(createHavingBuilder(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.modelName, this.tableName));
|
|
2354
|
-
return new GroupedCollection(this.ctx, this.modelName, {
|
|
2355
|
-
tableName: this.tableName,
|
|
2356
|
-
namespaceId: this.namespaceId,
|
|
2357
|
-
baseFilters: this.baseFilters,
|
|
2358
|
-
groupByFields: this.groupByFields,
|
|
2359
|
-
groupByColumns: this.groupByColumns,
|
|
2360
|
-
havingFilters: [...this.havingFilters, havingExpr]
|
|
2361
|
-
});
|
|
2362
|
-
}
|
|
2363
|
-
/**
|
|
2364
|
-
* Read terminal: run a grouped aggregate query.
|
|
2365
|
-
*
|
|
2366
|
-
* Accepts an optional `configure` callback that receives a
|
|
2367
|
-
* `MetaBuilder<'read'>` for attaching typed annotations.
|
|
2368
|
-
* Annotations are merged into the compiled plan's `meta.annotations`.
|
|
2369
|
-
*/
|
|
2370
|
-
async aggregate(fn, configure) {
|
|
2371
|
-
const aggregateSpec = fn(createAggregateBuilder(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.modelName));
|
|
2372
|
-
const aggregateEntries = Object.entries(aggregateSpec);
|
|
2373
|
-
if (aggregateEntries.length === 0) throw ormError("ORM.AGGREGATE_SELECTOR_MISSING", "groupBy().aggregate() requires at least one aggregation selector", { meta: {
|
|
2374
|
-
method: "groupBy.aggregate",
|
|
2375
|
-
model: this.modelName
|
|
2376
|
-
} });
|
|
2377
|
-
for (const [alias, selector] of aggregateEntries) if (!isAggregateSelector(selector)) throw ormError("ORM.AGGREGATE_SELECTOR_INVALID", `groupBy().aggregate() selector "${alias}" is invalid`, { meta: {
|
|
2378
|
-
method: "groupBy.aggregate",
|
|
2379
|
-
model: this.modelName,
|
|
2380
|
-
alias
|
|
2381
|
-
} });
|
|
2382
|
-
let annotationsMap;
|
|
2383
|
-
if (configure !== void 0) {
|
|
2384
|
-
const meta = createMetaBuilder("read", "groupBy.aggregate");
|
|
2385
|
-
configure(meta);
|
|
2386
|
-
if (meta.annotations.size > 0) annotationsMap = meta.annotations;
|
|
2387
|
-
}
|
|
2388
|
-
const compiled = mergeAnnotations(compileGroupedAggregate(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.tableName, this.baseFilters, this.groupByColumns, aggregateSpec, combineWhereExprs(this.havingFilters)), annotationsMap);
|
|
2389
|
-
return (await queryPlanRows(this.ctx.runtime, compiled).toArray()).map((row) => {
|
|
2390
|
-
const mapped = mapStorageRowToModelFields(this.contract, this.namespaceId, this.modelName, row);
|
|
2391
|
-
for (const [alias] of aggregateEntries) mapped[alias] = row[alias];
|
|
2392
|
-
return mapped;
|
|
2393
|
-
});
|
|
2394
|
-
}
|
|
2395
|
-
};
|
|
2396
|
-
/**
|
|
2397
|
-
* The having metric methods, one per operation the registry contributes —
|
|
2398
|
-
* the runtime mirror of the contract's emitted aggregate map, which is what
|
|
2399
|
-
* types the surface as {@link HavingBuilder}. HAVING compares the value
|
|
2400
|
-
* inside the database, so only an operation's plain `AggregateExpr` form is
|
|
2401
|
-
* sound here: an operation outside the SQL aggregate alphabet exists only in
|
|
2402
|
-
* its descriptor-lowered form — a rendering for the driver boundary — and is
|
|
2403
|
-
* refused. The typed surface already excludes it; the runtime refusal covers
|
|
2404
|
-
* dynamic invocation.
|
|
2405
|
-
*/
|
|
2406
|
-
function createHavingBuilder(contract, aggregates, namespaceId, modelName, tableName) {
|
|
2407
|
-
const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
|
|
2408
|
-
const builder = {};
|
|
2409
|
-
for (const operation of aggregateOperationNames(aggregates)) builder[operation] = (field) => {
|
|
2410
|
-
if (!isAggregateFn(operation)) throw ormError("ORM.AGGREGATE_PROJECTION_ONLY", `Aggregate operation '${operation}' is projection-only: it has no plain SQL form for HAVING, ORDER BY, or comparison positions.`, {
|
|
2411
|
-
why: "An operation outside the SQL aggregate alphabet reaches SQL only through its descriptor's lowering hook — a rendering for the driver boundary. HAVING and ORDER BY compare the value inside the database, where that rendering would change SQL semantics.",
|
|
2412
|
-
fix: `Project '${operation}' in a select and filter or order on the projected value, or use an operation from the SQL aggregate alphabet.`,
|
|
2413
|
-
meta: { operation }
|
|
2414
|
-
});
|
|
2415
|
-
return createHavingComparisonMethods(new AggregateExpr(operation, field === void 0 ? void 0 : ColumnRef.of(tableName, fieldToColumn[field] ?? field)));
|
|
2416
|
-
};
|
|
2417
|
-
return blindCast(builder);
|
|
2418
|
-
}
|
|
2419
|
-
function createHavingComparisonMethods(metric) {
|
|
2420
|
-
const buildBinaryExpr = (op, value) => new BinaryExpr(op, metric, LiteralExpr.of(value));
|
|
2421
|
-
return {
|
|
2422
|
-
eq(value) {
|
|
2423
|
-
return buildBinaryExpr("eq", value);
|
|
2424
|
-
},
|
|
2425
|
-
neq(value) {
|
|
2426
|
-
return buildBinaryExpr("neq", value);
|
|
2427
|
-
},
|
|
2428
|
-
gt(value) {
|
|
2429
|
-
return buildBinaryExpr("gt", value);
|
|
2430
|
-
},
|
|
2431
|
-
lt(value) {
|
|
2432
|
-
return buildBinaryExpr("lt", value);
|
|
2433
|
-
},
|
|
2434
|
-
gte(value) {
|
|
2435
|
-
return buildBinaryExpr("gte", value);
|
|
2436
|
-
},
|
|
2437
|
-
lte(value) {
|
|
2438
|
-
return buildBinaryExpr("lte", value);
|
|
2439
|
-
}
|
|
2440
|
-
};
|
|
2441
|
-
}
|
|
2442
|
-
function createIncludeScalar(fn, state, column) {
|
|
2443
|
-
return {
|
|
2444
|
-
kind: "includeScalar",
|
|
2445
|
-
fn,
|
|
2446
|
-
state,
|
|
2447
|
-
...column !== void 0 ? { column } : {}
|
|
2318
|
+
}
|
|
2448
2319
|
};
|
|
2320
|
+
return new AsyncIterableResult(generator());
|
|
2449
2321
|
}
|
|
2450
|
-
function
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2322
|
+
async function executeMutationReturningSingleRow(options) {
|
|
2323
|
+
const { context, runtime, compiled, tableName, modelName, namespaceId, variantName, includes, selectedFields, hiddenColumns, mapRow, operation, onMissingRowMessage } = options;
|
|
2324
|
+
const { contract } = context;
|
|
2325
|
+
const mapStorageRow = createMutationRowMapper(contract, namespaceId, modelName, variantName);
|
|
2326
|
+
if (includes.length === 0) {
|
|
2327
|
+
const first = (await queryPlanRows(runtime, compiled).toArray())[0];
|
|
2328
|
+
if (!first) return null;
|
|
2329
|
+
const mapped = mapStorageRow(first);
|
|
2330
|
+
if (hiddenColumns.length > 0) stripHiddenMappedFields(contract, namespaceId, modelName, mapped, hiddenColumns);
|
|
2331
|
+
return mapRow(mapped);
|
|
2332
|
+
}
|
|
2333
|
+
const identityRows = await queryPlanRows(runtime, compiled).toArray();
|
|
2334
|
+
if (identityRows.length === 0) return null;
|
|
2335
|
+
for await (const row of reloadMutationRowsByIdentities({
|
|
2336
|
+
context,
|
|
2337
|
+
runtime,
|
|
2338
|
+
tableName,
|
|
2339
|
+
modelName,
|
|
2340
|
+
namespaceId,
|
|
2341
|
+
identityRows,
|
|
2342
|
+
selectedFields,
|
|
2343
|
+
includes
|
|
2344
|
+
})) return row;
|
|
2345
|
+
throw ormError("ORM.MUTATION_ROW_MISSING", onMissingRowMessage, { meta: {
|
|
2346
|
+
operation,
|
|
2347
|
+
model: modelName,
|
|
2348
|
+
tableName
|
|
2349
|
+
} });
|
|
2455
2350
|
}
|
|
2456
|
-
function
|
|
2457
|
-
|
|
2458
|
-
const candidate = value;
|
|
2459
|
-
return candidate.kind === "includeScalar" && typeof candidate.fn === "string" && isCollectionState(candidate.state);
|
|
2351
|
+
function and(...exprs) {
|
|
2352
|
+
return AndExpr.of(exprs);
|
|
2460
2353
|
}
|
|
2461
|
-
function
|
|
2462
|
-
|
|
2463
|
-
const candidate = value;
|
|
2464
|
-
if (candidate.kind !== "includeCombine") return false;
|
|
2465
|
-
if (typeof candidate.branches !== "object" || candidate.branches === null) return false;
|
|
2466
|
-
return true;
|
|
2354
|
+
function or(...exprs) {
|
|
2355
|
+
return OrExpr.of(exprs);
|
|
2467
2356
|
}
|
|
2468
|
-
function
|
|
2469
|
-
|
|
2470
|
-
return isCollectionState(value.state);
|
|
2357
|
+
function not(expr) {
|
|
2358
|
+
return expr.not();
|
|
2471
2359
|
}
|
|
2472
|
-
function
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2360
|
+
function all() {
|
|
2361
|
+
return AndExpr.true();
|
|
2362
|
+
}
|
|
2363
|
+
function shorthandToWhereExpr(context, namespaceId, modelName, filters) {
|
|
2364
|
+
const contract = context.contract;
|
|
2365
|
+
const tableName = resolveModelTableName(contract, namespaceId, modelName);
|
|
2366
|
+
const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
|
|
2367
|
+
const exprs = [];
|
|
2368
|
+
for (const [fieldName, value] of Object.entries(filters)) {
|
|
2369
|
+
if (value === void 0) continue;
|
|
2370
|
+
const columnName = fieldToColumn[fieldName] ?? fieldName;
|
|
2371
|
+
const left = ColumnRef.of(tableName, columnName);
|
|
2372
|
+
if (value === null) {
|
|
2373
|
+
exprs.push(NullCheckExpr.isNull(left));
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
assertFieldHasEqualityTrait(context, namespaceId, modelName, fieldName);
|
|
2377
|
+
exprs.push(BinaryExpr.eq(left, LiteralExpr.of(value)));
|
|
2378
|
+
}
|
|
2379
|
+
if (exprs.length === 0) return;
|
|
2380
|
+
return exprs.length === 1 ? exprs[0] : and(...exprs);
|
|
2381
|
+
}
|
|
2382
|
+
function assertFieldHasEqualityTrait(context, namespaceId, modelName, fieldName) {
|
|
2383
|
+
const fieldType = modelOf(context.contract, namespaceId, modelName)?.fields?.[fieldName]?.type;
|
|
2384
|
+
const codecId = fieldType?.kind === "scalar" ? fieldType.codecId : void 0;
|
|
2385
|
+
if (!(codecId ? context.codecDescriptors.descriptorFor(codecId)?.traits ?? [] : []).includes("equality")) throw ormError("ORM.FILTER_UNSUPPORTED", `Shorthand filter on "${modelName}.${fieldName}": field does not support equality comparisons`, { meta: {
|
|
2386
|
+
model: modelName,
|
|
2387
|
+
field: fieldName,
|
|
2388
|
+
trait: "equality"
|
|
2389
|
+
} });
|
|
2476
2390
|
}
|
|
2477
2391
|
function hasThrough$1(relation) {
|
|
2478
2392
|
return relation.through !== void 0;
|
|
@@ -2786,6 +2700,200 @@ function firstTargetColumn(contract, relation) {
|
|
|
2786
2700
|
if (!firstField) return;
|
|
2787
2701
|
return resolveFieldToColumn(contract, relation.toNamespace, relation.to, firstField);
|
|
2788
2702
|
}
|
|
2703
|
+
var GroupedCollection = class GroupedCollection {
|
|
2704
|
+
ctx;
|
|
2705
|
+
contract;
|
|
2706
|
+
modelName;
|
|
2707
|
+
tableName;
|
|
2708
|
+
namespaceId;
|
|
2709
|
+
preGroupState;
|
|
2710
|
+
groupByFields;
|
|
2711
|
+
groupByColumns;
|
|
2712
|
+
havingFilters;
|
|
2713
|
+
postGroup;
|
|
2714
|
+
constructor(ctx, modelName, options) {
|
|
2715
|
+
this.ctx = ctx;
|
|
2716
|
+
this.contract = ctx.context.contract;
|
|
2717
|
+
this.modelName = modelName;
|
|
2718
|
+
this.tableName = options.tableName;
|
|
2719
|
+
this.namespaceId = blindCast(options.namespaceId);
|
|
2720
|
+
this.preGroupState = options.preGroupState;
|
|
2721
|
+
this.groupByFields = options.groupByFields;
|
|
2722
|
+
this.groupByColumns = options.groupByColumns;
|
|
2723
|
+
this.havingFilters = options.havingFilters;
|
|
2724
|
+
this.postGroup = options.postGroup;
|
|
2725
|
+
}
|
|
2726
|
+
#clone(overrides) {
|
|
2727
|
+
return blindCast(new GroupedCollection(this.ctx, this.modelName, {
|
|
2728
|
+
tableName: this.tableName,
|
|
2729
|
+
namespaceId: this.namespaceId,
|
|
2730
|
+
preGroupState: this.preGroupState,
|
|
2731
|
+
groupByFields: this.groupByFields,
|
|
2732
|
+
groupByColumns: this.groupByColumns,
|
|
2733
|
+
havingFilters: this.havingFilters,
|
|
2734
|
+
postGroup: this.postGroup,
|
|
2735
|
+
...overrides
|
|
2736
|
+
}));
|
|
2737
|
+
}
|
|
2738
|
+
having(predicate) {
|
|
2739
|
+
const havingExpr = predicate(createHavingBuilder(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.modelName, this.tableName));
|
|
2740
|
+
return this.#clone({ havingFilters: [...this.havingFilters, havingExpr] });
|
|
2741
|
+
}
|
|
2742
|
+
/**
|
|
2743
|
+
* Append an `ORDER BY` clause on the grouped rows themselves — orders by
|
|
2744
|
+
* group key. Ordering by an aggregate alias needs a builder surface over
|
|
2745
|
+
* the aliases and isn't supported here. Unlocks post-group `limit(...)` /
|
|
2746
|
+
* `offset(...)`, which page a group order that would otherwise be undefined.
|
|
2747
|
+
*/
|
|
2748
|
+
orderBy(selection) {
|
|
2749
|
+
const selectors = Array.isArray(selection) ? selection : [selection];
|
|
2750
|
+
if (selectors.length === 0) throw ormError("ORM.ARGUMENT_INVALID", `orderBy() for model "${this.modelName}" requires at least one selector`, { meta: {
|
|
2751
|
+
method: "orderBy",
|
|
2752
|
+
model: this.modelName
|
|
2753
|
+
} });
|
|
2754
|
+
const accessor = createModelAccessor(this.ctx.context, this.namespaceId, this.modelName);
|
|
2755
|
+
const nextOrders = selectors.map((selector) => selector(accessor));
|
|
2756
|
+
return this.#clone({ postGroup: {
|
|
2757
|
+
...this.postGroup,
|
|
2758
|
+
orderBy: [...this.postGroup.orderBy, ...nextOrders]
|
|
2759
|
+
} });
|
|
2760
|
+
}
|
|
2761
|
+
/**
|
|
2762
|
+
* Apply `LIMIT n` to the grouped rows. Replaces any previous post-group
|
|
2763
|
+
* limit. Requires a prior `orderBy(...)` — a database may return groups in
|
|
2764
|
+
* any order, so "the first n groups" is undefined without one.
|
|
2765
|
+
*/
|
|
2766
|
+
limit(n) {
|
|
2767
|
+
return this.#clone({ postGroup: {
|
|
2768
|
+
...this.postGroup,
|
|
2769
|
+
limit: n
|
|
2770
|
+
} });
|
|
2771
|
+
}
|
|
2772
|
+
/**
|
|
2773
|
+
* Apply `OFFSET n` to the grouped rows. Replaces any previous post-group
|
|
2774
|
+
* offset. Requires a prior `orderBy(...)`, same as `limit(...)` — both
|
|
2775
|
+
* pagination methods need a deterministic group order.
|
|
2776
|
+
*/
|
|
2777
|
+
offset(n) {
|
|
2778
|
+
return this.#clone({ postGroup: {
|
|
2779
|
+
...this.postGroup,
|
|
2780
|
+
offset: n
|
|
2781
|
+
} });
|
|
2782
|
+
}
|
|
2783
|
+
/**
|
|
2784
|
+
* Read terminal: run a grouped aggregate query.
|
|
2785
|
+
*
|
|
2786
|
+
* Accepts an optional `configure` callback that receives a
|
|
2787
|
+
* `MetaBuilder<'read'>` for attaching typed annotations.
|
|
2788
|
+
* Annotations are merged into the compiled plan's `meta.annotations`.
|
|
2789
|
+
*/
|
|
2790
|
+
async aggregate(fn, configure) {
|
|
2791
|
+
const aggregateSpec = fn(createAggregateBuilder(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.modelName));
|
|
2792
|
+
const aggregateEntries = Object.entries(aggregateSpec);
|
|
2793
|
+
if (aggregateEntries.length === 0) throw ormError("ORM.AGGREGATE_SELECTOR_MISSING", "groupBy().aggregate() requires at least one aggregation selector", { meta: {
|
|
2794
|
+
method: "groupBy.aggregate",
|
|
2795
|
+
model: this.modelName
|
|
2796
|
+
} });
|
|
2797
|
+
for (const [alias, selector] of aggregateEntries) if (!isAggregateSelector(selector)) throw ormError("ORM.AGGREGATE_SELECTOR_INVALID", `groupBy().aggregate() selector "${alias}" is invalid`, { meta: {
|
|
2798
|
+
method: "groupBy.aggregate",
|
|
2799
|
+
model: this.modelName,
|
|
2800
|
+
alias
|
|
2801
|
+
} });
|
|
2802
|
+
let annotationsMap;
|
|
2803
|
+
if (configure !== void 0) {
|
|
2804
|
+
const meta = createMetaBuilder("read", "groupBy.aggregate");
|
|
2805
|
+
configure(meta);
|
|
2806
|
+
if (meta.annotations.size > 0) annotationsMap = meta.annotations;
|
|
2807
|
+
}
|
|
2808
|
+
const compiled = mergeAnnotations(compileGroupedAggregate(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.tableName, this.preGroupState, this.groupByColumns, aggregateSpec, combineWhereExprs(this.havingFilters), this.modelName, this.postGroup), annotationsMap);
|
|
2809
|
+
return (await queryPlanRows(this.ctx.runtime, compiled).toArray()).map((row) => {
|
|
2810
|
+
const mapped = mapStorageRowToModelFields(this.contract, this.namespaceId, this.modelName, row);
|
|
2811
|
+
for (const [alias] of aggregateEntries) mapped[alias] = row[alias];
|
|
2812
|
+
return blindCast(mapped);
|
|
2813
|
+
});
|
|
2814
|
+
}
|
|
2815
|
+
};
|
|
2816
|
+
/**
|
|
2817
|
+
* The having metric methods, one per operation the registry contributes —
|
|
2818
|
+
* the runtime mirror of the contract's emitted aggregate map, which is what
|
|
2819
|
+
* types the surface as {@link HavingBuilder}. HAVING compares the value
|
|
2820
|
+
* inside the database, so only an operation's plain `AggregateExpr` form is
|
|
2821
|
+
* sound here: an operation outside the SQL aggregate alphabet exists only in
|
|
2822
|
+
* its descriptor-lowered form — a rendering for the driver boundary — and is
|
|
2823
|
+
* refused. The typed surface already excludes it; the runtime refusal covers
|
|
2824
|
+
* dynamic invocation.
|
|
2825
|
+
*/
|
|
2826
|
+
function createHavingBuilder(contract, aggregates, namespaceId, modelName, tableName) {
|
|
2827
|
+
const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
|
|
2828
|
+
const builder = {};
|
|
2829
|
+
for (const operation of aggregateOperationNames(aggregates)) builder[operation] = (field) => {
|
|
2830
|
+
if (!isAggregateFn(operation)) throw ormError("ORM.AGGREGATE_PROJECTION_ONLY", `Aggregate operation '${operation}' is projection-only: it has no plain SQL form for HAVING, ORDER BY, or comparison positions.`, {
|
|
2831
|
+
why: "An operation outside the SQL aggregate alphabet reaches SQL only through its descriptor's lowering hook — a rendering for the driver boundary. HAVING and ORDER BY compare the value inside the database, where that rendering would change SQL semantics.",
|
|
2832
|
+
fix: `Project '${operation}' in a select and filter or order on the projected value, or use an operation from the SQL aggregate alphabet.`,
|
|
2833
|
+
meta: { operation }
|
|
2834
|
+
});
|
|
2835
|
+
return createHavingComparisonMethods(new AggregateExpr(operation, field === void 0 ? void 0 : ColumnRef.of(tableName, fieldToColumn[field] ?? field)));
|
|
2836
|
+
};
|
|
2837
|
+
return blindCast(builder);
|
|
2838
|
+
}
|
|
2839
|
+
function createHavingComparisonMethods(metric) {
|
|
2840
|
+
const buildBinaryExpr = (op, value) => new BinaryExpr(op, metric, LiteralExpr.of(value));
|
|
2841
|
+
return {
|
|
2842
|
+
eq(value) {
|
|
2843
|
+
return buildBinaryExpr("eq", value);
|
|
2844
|
+
},
|
|
2845
|
+
neq(value) {
|
|
2846
|
+
return buildBinaryExpr("neq", value);
|
|
2847
|
+
},
|
|
2848
|
+
gt(value) {
|
|
2849
|
+
return buildBinaryExpr("gt", value);
|
|
2850
|
+
},
|
|
2851
|
+
lt(value) {
|
|
2852
|
+
return buildBinaryExpr("lt", value);
|
|
2853
|
+
},
|
|
2854
|
+
gte(value) {
|
|
2855
|
+
return buildBinaryExpr("gte", value);
|
|
2856
|
+
},
|
|
2857
|
+
lte(value) {
|
|
2858
|
+
return buildBinaryExpr("lte", value);
|
|
2859
|
+
}
|
|
2860
|
+
};
|
|
2861
|
+
}
|
|
2862
|
+
function createIncludeScalar(fn, state, column) {
|
|
2863
|
+
return {
|
|
2864
|
+
kind: "includeScalar",
|
|
2865
|
+
fn,
|
|
2866
|
+
state,
|
|
2867
|
+
...column !== void 0 ? { column } : {}
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
function createIncludeCombine(branches) {
|
|
2871
|
+
return {
|
|
2872
|
+
kind: "includeCombine",
|
|
2873
|
+
branches
|
|
2874
|
+
};
|
|
2875
|
+
}
|
|
2876
|
+
function isIncludeScalar(value) {
|
|
2877
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2878
|
+
const candidate = value;
|
|
2879
|
+
return candidate.kind === "includeScalar" && typeof candidate.fn === "string" && isCollectionState(candidate.state);
|
|
2880
|
+
}
|
|
2881
|
+
function isIncludeCombine(value) {
|
|
2882
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2883
|
+
const candidate = value;
|
|
2884
|
+
if (candidate.kind !== "includeCombine") return false;
|
|
2885
|
+
if (typeof candidate.branches !== "object" || candidate.branches === null) return false;
|
|
2886
|
+
return true;
|
|
2887
|
+
}
|
|
2888
|
+
function isCollectionStateCarrier(value) {
|
|
2889
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2890
|
+
return isCollectionState(value.state);
|
|
2891
|
+
}
|
|
2892
|
+
function isCollectionState(value) {
|
|
2893
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2894
|
+
const candidate = value;
|
|
2895
|
+
return Array.isArray(candidate.filters) && Array.isArray(candidate.includes);
|
|
2896
|
+
}
|
|
2789
2897
|
function createRelationMutator() {
|
|
2790
2898
|
return {
|
|
2791
2899
|
create(data) {
|
|
@@ -3463,34 +3571,6 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3463
3571
|
variantName
|
|
3464
3572
|
});
|
|
3465
3573
|
}
|
|
3466
|
-
/**
|
|
3467
|
-
* Eagerly load a related model. The relation appears on every
|
|
3468
|
-
* returned row under its declared name; to-one relations are mapped
|
|
3469
|
-
* to a single object (or `null`), to-many relations to an array.
|
|
3470
|
-
*
|
|
3471
|
-
* An optional refinement callback receives a child collection that
|
|
3472
|
-
* can be further constrained, projected, ordered, paginated, or
|
|
3473
|
-
* reduced to scalars via `count()`/`sum()`/etc. or to multiple
|
|
3474
|
-
* sub-aggregates via `combine()`.
|
|
3475
|
-
*
|
|
3476
|
-
* ```typescript
|
|
3477
|
-
* // Simple include — every user comes back with its posts array:
|
|
3478
|
-
* const users = await db.orm.User.include('posts').all();
|
|
3479
|
-
*
|
|
3480
|
-
* // Refine the related collection:
|
|
3481
|
-
* const withRecent = await db.orm.User.include('posts', (posts) =>
|
|
3482
|
-
* posts.where({ published: true }).orderBy((p) => p.createdAt.desc()).take(5),
|
|
3483
|
-
* ).all();
|
|
3484
|
-
*
|
|
3485
|
-
* // Reduce a to-many relation to a scalar value:
|
|
3486
|
-
* const withCounts = await db.orm.User.include('posts', (posts) => posts.count()).all();
|
|
3487
|
-
*
|
|
3488
|
-
* // Multiple sub-views via combine():
|
|
3489
|
-
* const overview = await db.orm.User.include('posts', (posts) =>
|
|
3490
|
-
* posts.combine({ recent: posts.take(3), total: posts.count() }),
|
|
3491
|
-
* ).all();
|
|
3492
|
-
* ```
|
|
3493
|
-
*/
|
|
3494
3574
|
include(relationName, refineFn) {
|
|
3495
3575
|
const relation = resolveIncludeRelation(this.contract, this.namespaceId, this.modelName, relationName, this.state.variantName);
|
|
3496
3576
|
let nestedState = emptyState();
|
|
@@ -3594,10 +3674,11 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3594
3674
|
return new GroupedCollection(this.ctx, this.modelName, {
|
|
3595
3675
|
tableName: this.tableName,
|
|
3596
3676
|
namespaceId: this.namespaceId,
|
|
3597
|
-
|
|
3677
|
+
preGroupState: this.state,
|
|
3598
3678
|
groupByFields: [...fields],
|
|
3599
3679
|
groupByColumns,
|
|
3600
|
-
havingFilters: []
|
|
3680
|
+
havingFilters: [],
|
|
3681
|
+
postGroup: emptyGroupPagingState()
|
|
3601
3682
|
});
|
|
3602
3683
|
}
|
|
3603
3684
|
/**
|
|
@@ -3610,7 +3691,7 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3610
3691
|
* ```typescript
|
|
3611
3692
|
* const users = await db.orm.User.include('posts', (posts) =>
|
|
3612
3693
|
* posts.combine({
|
|
3613
|
-
* recent: posts.where({ published: true }).
|
|
3694
|
+
* recent: posts.where({ published: true }).limit(3),
|
|
3614
3695
|
* total: posts.count(),
|
|
3615
3696
|
* averageViews: posts.avg('views'),
|
|
3616
3697
|
* }),
|
|
@@ -3652,14 +3733,14 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3652
3733
|
* ```typescript
|
|
3653
3734
|
* const page1 = await db.orm.Post
|
|
3654
3735
|
* .orderBy((p) => p.createdAt.desc())
|
|
3655
|
-
* .
|
|
3736
|
+
* .limit(20)
|
|
3656
3737
|
* .all();
|
|
3657
3738
|
*
|
|
3658
3739
|
* const last = page1[page1.length - 1]!;
|
|
3659
3740
|
* const page2 = await db.orm.Post
|
|
3660
3741
|
* .orderBy((p) => p.createdAt.desc())
|
|
3661
3742
|
* .cursor({ createdAt: last.createdAt })
|
|
3662
|
-
* .
|
|
3743
|
+
* .limit(20)
|
|
3663
3744
|
* .all();
|
|
3664
3745
|
* ```
|
|
3665
3746
|
*/
|
|
@@ -3689,6 +3770,8 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3689
3770
|
* prior `orderBy(...)`; replaces any previous `distinct(...)` /
|
|
3690
3771
|
* `distinctOn(...)` selection.
|
|
3691
3772
|
*
|
|
3773
|
+
* Requires the `postgres.distinctOn` capability.
|
|
3774
|
+
*
|
|
3692
3775
|
* ```typescript
|
|
3693
3776
|
* // Latest post per user:
|
|
3694
3777
|
* const latestPerUser = await db.orm.Post
|
|
@@ -3698,6 +3781,7 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3698
3781
|
* ```
|
|
3699
3782
|
*/
|
|
3700
3783
|
distinctOn(...fields) {
|
|
3784
|
+
assertDistinctOnCapability(this.contract, "distinctOn");
|
|
3701
3785
|
const distinctOnFields = mapFieldsToColumns(this.contract, this.namespaceId, this.modelName, fields);
|
|
3702
3786
|
return this.#clone({
|
|
3703
3787
|
distinct: void 0,
|
|
@@ -3708,10 +3792,10 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3708
3792
|
* Apply `LIMIT n`. Replaces any previous limit set on this collection.
|
|
3709
3793
|
*
|
|
3710
3794
|
* ```typescript
|
|
3711
|
-
* const firstTen = await db.orm.User.orderBy((u) => u.id.asc()).
|
|
3795
|
+
* const firstTen = await db.orm.User.orderBy((u) => u.id.asc()).limit(10).all();
|
|
3712
3796
|
* ```
|
|
3713
3797
|
*/
|
|
3714
|
-
|
|
3798
|
+
limit(n) {
|
|
3715
3799
|
return this.#clone({ limit: n });
|
|
3716
3800
|
}
|
|
3717
3801
|
/**
|
|
@@ -3720,12 +3804,12 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3720
3804
|
* ```typescript
|
|
3721
3805
|
* const page2 = await db.orm.User
|
|
3722
3806
|
* .orderBy((u) => u.id.asc())
|
|
3723
|
-
* .
|
|
3724
|
-
* .
|
|
3807
|
+
* .offset(10)
|
|
3808
|
+
* .limit(10)
|
|
3725
3809
|
* .all();
|
|
3726
3810
|
* ```
|
|
3727
3811
|
*/
|
|
3728
|
-
|
|
3812
|
+
offset(n) {
|
|
3729
3813
|
return this.#clone({ offset: n });
|
|
3730
3814
|
}
|
|
3731
3815
|
/**
|
|
@@ -3738,15 +3822,9 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3738
3822
|
* whichever fits the caller. A single result can only be consumed
|
|
3739
3823
|
* once.
|
|
3740
3824
|
*
|
|
3741
|
-
*
|
|
3742
|
-
*
|
|
3743
|
-
*
|
|
3744
|
-
* underlying database, and — for queries with `include(...)` —
|
|
3745
|
-
* targets whose SQL dialect supports neither lateral joins nor
|
|
3746
|
-
* correlated subqueries (so child rows cannot be stitched in a
|
|
3747
|
-
* single streaming query). These are implementation details below
|
|
3748
|
-
* the public API; the iteration shape itself is genuinely
|
|
3749
|
-
* streaming whenever the driver and plan allow it.
|
|
3825
|
+
* Queries without `include(...)` stream rows as the driver yields them (drivers that cannot
|
|
3826
|
+
* expose a cursor buffer internally first). Queries with `include(...)` read the whole parent
|
|
3827
|
+
* result set into memory before yielding the first row.
|
|
3750
3828
|
*
|
|
3751
3829
|
* ```typescript
|
|
3752
3830
|
* // Thenable — collect to an array:
|
|
@@ -3773,7 +3851,7 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3773
3851
|
return this.#withAnnotationsFromMeta(configure, "all").#dispatch();
|
|
3774
3852
|
}
|
|
3775
3853
|
async first(filter, configure) {
|
|
3776
|
-
return (await (filter === void 0 ? this : typeof filter === "function" ? this.where(filter) : this.where(filter)).
|
|
3854
|
+
return (await (filter === void 0 ? this : typeof filter === "function" ? this.where(filter) : this.where(filter)).limit(1).#withAnnotationsFromMeta(configure, "first").#dispatch().toArray())[0] ?? null;
|
|
3777
3855
|
}
|
|
3778
3856
|
/**
|
|
3779
3857
|
* Read terminal: run an aggregate query (count, sum, avg, min, max)
|
|
@@ -3809,7 +3887,7 @@ var CollectionImpl = class CollectionImpl {
|
|
|
3809
3887
|
alias
|
|
3810
3888
|
} });
|
|
3811
3889
|
const annotationsMap = this.#collectAnnotationsFromMeta(configure, "read", "aggregate");
|
|
3812
|
-
const compiled = mergeAnnotations(compileAggregate(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.tableName, this.state
|
|
3890
|
+
const compiled = mergeAnnotations(compileAggregate(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.tableName, this.state, aggregateSpec, this.modelName), annotationsMap);
|
|
3813
3891
|
const row = (await queryPlanRows(this.ctx.runtime, compiled).toArray())[0] ?? {};
|
|
3814
3892
|
const result = {};
|
|
3815
3893
|
for (const [alias, selector] of entries) result[alias] = row[alias] ?? this.#emptyAggregateValue(selector);
|
|
@@ -4137,12 +4215,13 @@ var CollectionImpl = class CollectionImpl {
|
|
|
4137
4215
|
* Requires a prior `.where(...)` — calling `update(...)` on an
|
|
4138
4216
|
* unfiltered collection is a type error.
|
|
4139
4217
|
*
|
|
4140
|
-
* Related rows can be created or
|
|
4141
|
-
*
|
|
4142
|
-
*
|
|
4143
|
-
* `
|
|
4144
|
-
*
|
|
4145
|
-
*
|
|
4218
|
+
* Related rows can be created, linked, or unlinked through relation callbacks on any relation:
|
|
4219
|
+
* to-one (1:1, N:1), to-many (1:N), and many-to-many (N:M, written through the junction table).
|
|
4220
|
+
* The callback receives a mutator exposing `create(...)`, `connect(...)`, and `disconnect(...)`.
|
|
4221
|
+
* A to-one `disconnect()` clears the foreign key; a to-many `disconnect()` with no criteria
|
|
4222
|
+
* unlinks every related row; an N:M `disconnect()` requires criteria. N:M `create`/`connect` are
|
|
4223
|
+
* unavailable when the junction has required columns the relation API cannot populate. Nested
|
|
4224
|
+
* updates against existing related rows are not supported through this API.
|
|
4146
4225
|
*
|
|
4147
4226
|
* ```typescript
|
|
4148
4227
|
* // Update one row by id:
|
|
@@ -4164,9 +4243,9 @@ var CollectionImpl = class CollectionImpl {
|
|
|
4164
4243
|
*
|
|
4165
4244
|
* Note: when the input contains nested-mutation callbacks, the
|
|
4166
4245
|
* operation is executed as a graph of internal queries via
|
|
4167
|
-
* `withMutationScope`. In that path
|
|
4168
|
-
*
|
|
4169
|
-
*
|
|
4246
|
+
* `withMutationScope`. In that path the `configure` callback still runs, so `meta.annotate`
|
|
4247
|
+
* validation applies, but the recorded annotations are discarded: neither the nested
|
|
4248
|
+
* statements nor the read-back query carry them.
|
|
4170
4249
|
*/
|
|
4171
4250
|
async update(data, configure) {
|
|
4172
4251
|
assertReturningCapability(this.contract, "update()");
|
|
@@ -4573,7 +4652,7 @@ var CollectionImpl = class CollectionImpl {
|
|
|
4573
4652
|
* compiled plan is post-wrapped via `mergeAnnotations` instead.
|
|
4574
4653
|
* Read terminals `all` and `first` populate `state.annotations`
|
|
4575
4654
|
* via `#withAnnotationsFromMeta` instead; `aggregate` uses this
|
|
4576
|
-
* post-wrap path because
|
|
4655
|
+
* post-wrap path because `compileAggregate` does not forward `state.annotations` into the plan.
|
|
4577
4656
|
* The meta builder's `annotate` method enforces applicability at the
|
|
4578
4657
|
* type level and at runtime.
|
|
4579
4658
|
*/
|