@prisma/orm-family-sql 8.0.0-rc.4-dev.10 → 8.0.0-rc.4-dev.12

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.
@@ -346,6 +346,13 @@ function assertReturningCapability(contract, action) {
346
346
  action
347
347
  } });
348
348
  }
349
+ function assertDistinctOnCapability(contract, methodName) {
350
+ if (contract.capabilities["postgres"]?.["distinctOn"] === true) return;
351
+ throw ormError("ORM.CAPABILITY_MISSING", `${methodName}() requires capability postgres.distinctOn`, { meta: {
352
+ capability: "postgres.distinctOn",
353
+ method: methodName
354
+ } });
355
+ }
349
356
  function hasContractCapability(contract, capability) {
350
357
  const capabilities = contract.capabilities;
351
358
  const value = capabilities[capability];
@@ -611,11 +618,298 @@ function mergeAnnotations(plan, annotations) {
611
618
  })
612
619
  });
613
620
  }
621
+ function namespaceCoordinateForSource(source) {
622
+ return source.kind === "table-source" ? source.namespaceId : void 0;
623
+ }
624
+ function bindWhereExpr(contract, expr, namespaceId) {
625
+ return bindWhereExprNode(contract, expr, namespaceId);
626
+ }
627
+ function bindWhereExprNode(contract, expr, namespaceId) {
628
+ return expr.accept({
629
+ columnRef(expr) {
630
+ return bindExpression(contract, expr);
631
+ },
632
+ identifierRef(expr) {
633
+ return expr;
634
+ },
635
+ subquery(expr) {
636
+ return bindExpression(contract, expr);
637
+ },
638
+ operation(expr) {
639
+ return bindExpression(contract, expr);
640
+ },
641
+ aggregate(expr) {
642
+ return bindExpression(contract, expr);
643
+ },
644
+ windowFunc(expr) {
645
+ return bindExpression(contract, expr);
646
+ },
647
+ functionCall(expr) {
648
+ return bindExpression(contract, expr);
649
+ },
650
+ cast(expr) {
651
+ return bindExpression(contract, expr);
652
+ },
653
+ case(expr) {
654
+ return bindExpression(contract, expr);
655
+ },
656
+ jsonObject(expr) {
657
+ return bindExpression(contract, expr);
658
+ },
659
+ jsonArrayAgg(expr) {
660
+ return bindExpression(contract, expr);
661
+ },
662
+ literal(expr) {
663
+ return expr;
664
+ },
665
+ param(expr) {
666
+ return expr;
667
+ },
668
+ preparedParam(expr) {
669
+ return expr;
670
+ },
671
+ list(expr) {
672
+ return bindExpression(contract, expr);
673
+ },
674
+ binary(expr) {
675
+ const left = bindExpression(contract, expr.left);
676
+ const bindingColumn = left.kind === "column-ref" ? left : void 0;
677
+ return new BinaryExpr(expr.op, left, bindComparable(contract, expr.right, bindingColumn, namespaceId));
678
+ },
679
+ and(expr) {
680
+ return AndExpr.of(expr.exprs.map((part) => bindWhereExprNode(contract, part, namespaceId)));
681
+ },
682
+ or(expr) {
683
+ return OrExpr.of(expr.exprs.map((part) => bindWhereExprNode(contract, part, namespaceId)));
684
+ },
685
+ exists(expr) {
686
+ return expr.notExists ? ExistsExpr.notExists(bindSelectAst(contract, expr.subquery)) : ExistsExpr.exists(bindSelectAst(contract, expr.subquery));
687
+ },
688
+ nullCheck(expr) {
689
+ return expr.isNull ? NullCheckExpr.isNull(bindExpression(contract, expr.expr)) : NullCheckExpr.isNotNull(bindExpression(contract, expr.expr));
690
+ },
691
+ not(expr) {
692
+ return new NotExpr(bindWhereExprNode(contract, expr.expr, namespaceId));
693
+ },
694
+ rawExpr(expr) {
695
+ return expr;
696
+ }
697
+ });
698
+ }
699
+ function bindComparable(contract, comparable, bindingColumn, namespaceId) {
700
+ if (comparable.kind === "param-ref" || bindingColumn === void 0) return comparable.kind === "param-ref" ? comparable : comparable.kind === "literal" || comparable.kind === "list" ? comparable : bindExpression(contract, comparable);
701
+ if (comparable.kind === "literal") return createParamRef(contract, bindingColumn, comparable.value, namespaceId);
702
+ if (comparable.kind === "list") return ListExpression.of(comparable.values.map((value) => value.kind === "literal" ? createParamRef(contract, bindingColumn, value.value, namespaceId) : value));
703
+ return bindExpression(contract, comparable);
704
+ }
705
+ function createParamRef(contract, columnRef, value, namespaceId) {
706
+ const resolved = resolveStorageTable(contract.storage, columnRef.table, namespaceId);
707
+ if (resolved === void 0 || !resolved.table.columns[columnRef.column]) throw ormError("ORM.COLUMN_UNKNOWN", `Unknown column "${columnRef.column}" in table "${columnRef.table}"`, { meta: {
708
+ tableName: columnRef.table,
709
+ column: columnRef.column
710
+ } });
711
+ const codec = codecRefForStorageColumn(contract.storage, resolved.namespaceId, columnRef.table, columnRef.column);
712
+ return ParamRef.of(value, codec ? { codec } : void 0);
713
+ }
714
+ function createExpressionBinder(contract) {
715
+ return { select: (ast) => bindSelectAst(contract, ast) };
716
+ }
717
+ function bindExpression(contract, expr) {
718
+ return expr.rewrite(createExpressionBinder(contract));
719
+ }
720
+ function bindProjectionExpr(contract, expr) {
721
+ return expr.kind === "literal" ? expr : bindExpression(contract, expr);
722
+ }
723
+ function bindOrderByItem(contract, orderItem) {
724
+ return new OrderByItem(bindExpression(contract, orderItem.expr), orderItem.dir);
725
+ }
726
+ function bindJoin(contract, join) {
727
+ const namespaceId = namespaceCoordinateForSource(join.source);
728
+ 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);
729
+ }
730
+ function bindFromSource(contract, source) {
731
+ if (source.kind === "table-source") return source;
732
+ if (source.kind === "derived-table-source") return DerivedTableSource.as(source.alias, bindSelectAst(contract, source.query));
733
+ return source;
734
+ }
735
+ function bindSelectAst(contract, ast) {
736
+ const namespaceId = ast.from !== void 0 ? namespaceCoordinateForSource(ast.from) : void 0;
737
+ return new SelectAst({
738
+ ...ast.from !== void 0 ? { from: bindFromSource(contract, ast.from) } : {},
739
+ joins: ast.joins?.map((join) => bindJoin(contract, join)),
740
+ projection: ast.projection.map((projection) => new ProjectionItem(projection.alias, bindProjectionExpr(contract, projection.expr), projection.codec)),
741
+ where: ast.where ? bindWhereExprNode(contract, ast.where, namespaceId) : void 0,
742
+ orderBy: ast.orderBy?.map((orderItem) => bindOrderByItem(contract, orderItem)),
743
+ distinct: ast.distinct,
744
+ distinctOn: ast.distinctOn?.map((expr) => bindExpression(contract, expr)),
745
+ groupBy: ast.groupBy?.map((expr) => bindExpression(contract, expr)),
746
+ having: ast.having ? bindWhereExprNode(contract, ast.having, namespaceId) : void 0,
747
+ limit: ast.limit,
748
+ offset: ast.offset,
749
+ selectAllIntent: ast.selectAllIntent
750
+ });
751
+ }
614
752
  function combineWhereExprs(filters) {
615
753
  if (filters.length === 0) return;
616
754
  if (filters.length === 1) return filters[0];
617
755
  return AndExpr.of(filters);
618
756
  }
757
+ function createBoundaryExpr(tableName, entry) {
758
+ return new BinaryExpr(entry.direction === "asc" ? "gt" : "lt", ColumnRef.of(tableName, entry.column), LiteralExpr.of(entry.value));
759
+ }
760
+ function buildLexicographicCursorWhere(tableName, entries) {
761
+ const branches = entries.map((entry, index) => {
762
+ const branchExprs = [];
763
+ for (const prefixEntry of entries.slice(0, index)) branchExprs.push(BinaryExpr.eq(ColumnRef.of(tableName, prefixEntry.column), LiteralExpr.of(prefixEntry.value)));
764
+ branchExprs.push(createBoundaryExpr(tableName, entry));
765
+ if (branchExprs.length === 1) {
766
+ const branch = branchExprs[0];
767
+ assertDefined(branch, "cursor branch contains its boundary expression");
768
+ return branch;
769
+ }
770
+ return AndExpr.of(branchExprs);
771
+ });
772
+ if (branches.length === 1) {
773
+ const branch = branches[0];
774
+ assertDefined(branch, "cursor expression contains its single branch");
775
+ return branch;
776
+ }
777
+ return OrExpr.of(branches);
778
+ }
779
+ function buildCursorWhere(tableName, orderBy, cursor) {
780
+ if (!cursor || !orderBy || orderBy.length === 0) return;
781
+ const entries = [];
782
+ for (const order of orderBy) {
783
+ if (order.expr.kind !== "column-ref") continue;
784
+ const column = order.expr.column;
785
+ const value = cursor[column];
786
+ if (value === void 0) throw ormError("ORM.CURSOR_VALUE_MISSING", `Missing cursor value for orderBy column "${column}"`, { meta: { column } });
787
+ entries.push({
788
+ column,
789
+ direction: order.dir,
790
+ value
791
+ });
792
+ }
793
+ const firstEntry = entries[0];
794
+ if (entries.length === 1 && firstEntry !== void 0) return createBoundaryExpr(tableName, firstEntry);
795
+ return buildLexicographicCursorWhere(tableName, entries);
796
+ }
797
+ function createTableRefRemapper$1(fromTable, toTable) {
798
+ return {
799
+ columnRef: (col) => col.table === fromTable ? ColumnRef.of(toTable, col.column) : col,
800
+ tableSource: (source) => {
801
+ if (source.alias === fromTable) return TableSource.named(source.name, toTable, source.namespaceId);
802
+ if (!source.alias && source.name === fromTable) return TableSource.named(source.name, toTable, source.namespaceId);
803
+ return source;
804
+ },
805
+ 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)
806
+ };
807
+ }
808
+ function buildStateWhere(contract, tableName, state, options) {
809
+ const filterTableName = options?.filterTableName;
810
+ const cursorWhere = buildCursorWhere(filterTableName ?? tableName, state.orderBy, state.cursor);
811
+ const boundFilters = state.filters.map((filter) => bindWhereExpr(contract, filter, options?.namespaceId));
812
+ const remappedFilters = filterTableName && filterTableName !== tableName ? boundFilters.map((filter) => filter.rewrite(createTableRefRemapper$1(filterTableName, tableName))) : boundFilters;
813
+ const boundCursorWhere = cursorWhere ? bindWhereExpr(contract, cursorWhere, options?.namespaceId) : void 0;
814
+ const remappedCursorWhere = boundCursorWhere && filterTableName && filterTableName !== tableName ? boundCursorWhere.rewrite(createTableRefRemapper$1(filterTableName, tableName)) : boundCursorWhere;
815
+ return combineWhereExprs(remappedCursorWhere ? [...remappedFilters, remappedCursorWhere] : remappedFilters);
816
+ }
817
+ /**
818
+ * Wrap a base SELECT in a `ROW_NUMBER() OVER (PARTITION BY … ORDER BY …) = 1`
819
+ * filter, implementing Prisma-style `.distinct(cols)` semantics: one
820
+ * representative row per `(distinctColumnRefs)` group is kept; the rest
821
+ * are dropped.
822
+ *
823
+ * Picking which row survives in each partition is governed by
824
+ * `rankingOrderBy`. When the caller's `orderBy` doesn't fully order rows
825
+ * within a partition (e.g. user wrote `.distinct('title')` with no
826
+ * `orderBy`, or ties in their ordering), the choice is
827
+ * implementation-defined — matching Prisma's documented nested-distinct
828
+ * behaviour. Callers that want determinism should pass an `orderBy` that
829
+ * is total within each partition.
830
+ *
831
+ * The wrapper forwards every column of `base.projection` through the
832
+ * derived alias, so the wrapper's projection is byte-identical in alias
833
+ * names — making this transparent to any outer query (`json_agg`,
834
+ * correlated subquery, top-level SELECT) that consumes the SELECT.
835
+ */
836
+ function wrapWithRowNumberDedup(options) {
837
+ const { base, distinctColumnRefs, rankingOrderBy, rankedAlias } = options;
838
+ const rnAlias = "__prisma_distinct_rn";
839
+ const effectiveOrderBy = rankingOrderBy.length > 0 ? rankingOrderBy : distinctColumnRefs.map((expr) => OrderByItem.asc(expr));
840
+ const inner = base.withProjection([...base.projection, ProjectionItem.of(rnAlias, WindowFuncExpr.rowNumber({
841
+ partitionBy: distinctColumnRefs,
842
+ orderBy: effectiveOrderBy
843
+ }))]);
844
+ 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)));
845
+ }
846
+ /**
847
+ * FROM source + WHERE for `state.distinct`: wraps in a `ROW_NUMBER`-ranked
848
+ * derived table aliased back to `tableName`, so callers need no rewriting.
849
+ */
850
+ function buildDedupedTableSource(contract, namespaceId, tableName, state, where, wrapProjection, joins) {
851
+ if (!hasEntries(state.distinct)) return {
852
+ source: tableSourceForContract(contract, namespaceId, tableName),
853
+ where
854
+ };
855
+ const distinctColumnRefs = state.distinct.map((column) => ColumnRef.of(tableName, column));
856
+ const rankingOrderBy = hasEntries(state.orderBy) ? state.orderBy : distinctColumnRefs.map((expr) => OrderByItem.asc(expr));
857
+ let inner = SelectAst.from(tableSourceForContract(contract, namespaceId, tableName)).withProjection([...wrapProjection, ProjectionItem.of("__prisma_distinct_rn", WindowFuncExpr.rowNumber({
858
+ partitionBy: distinctColumnRefs,
859
+ orderBy: rankingOrderBy
860
+ }))]);
861
+ if (joins && joins.length > 0) inner = inner.withJoins(joins);
862
+ if (where) inner = inner.withWhere(where);
863
+ return {
864
+ source: DerivedTableSource.as(tableName, inner),
865
+ where: BinaryExpr.eq(ColumnRef.of(tableName, "__prisma_distinct_rn"), LiteralExpr.of(1))
866
+ };
867
+ }
868
+ function buildMtiJoins(contract, namespaceId, polyInfo, variantName, selectedColumnsByTable) {
869
+ const joins = [];
870
+ const projection = [];
871
+ const pkColumn = resolvePrimaryKeyColumn(contract, namespaceId, polyInfo.baseTable);
872
+ const variantsToJoin = variantName ? polyInfo.mtiVariants.filter((v) => v.modelName === variantName) : polyInfo.mtiVariants;
873
+ for (const variant of variantsToJoin) {
874
+ const joinType = variantName ? "inner" : "left";
875
+ const joinOn = EqColJoinOn.of(ColumnRef.of(polyInfo.baseTable, pkColumn), ColumnRef.of(variant.table, pkColumn));
876
+ const join = joinType === "inner" ? JoinAst.inner(tableSourceForContract(contract, namespaceId, variant.table), joinOn) : JoinAst.left(tableSourceForContract(contract, namespaceId, variant.table), joinOn);
877
+ joins.push(join);
878
+ const variantColumns = resolveTableColumns(contract, namespaceId, variant.table);
879
+ const selectedVariantColumns = selectedColumnsByTable?.get(variant.table);
880
+ for (const col of variantColumns) {
881
+ if (col === pkColumn) continue;
882
+ if (selectedColumnsByTable !== void 0 && selectedVariantColumns?.has(col) !== true) continue;
883
+ const alias = `${variant.table}__${col}`;
884
+ projection.push(ProjectionItem.of(alias, ColumnRef.of(variant.table, col), codecRefForStorageColumn(contract.storage, namespaceId, variant.table, col)));
885
+ }
886
+ }
887
+ return {
888
+ joins,
889
+ projection
890
+ };
891
+ }
892
+ function hasEntries(value) {
893
+ return value !== void 0 && value.length > 0;
894
+ }
895
+ /**
896
+ * The rows an aggregate reduces over, one SELECT aliased to `tableName` — an
897
+ * aggregate has no outer level of its own, so where/joins/distinct/orderBy/
898
+ * limit/offset all have to live in this one wrap.
899
+ */
900
+ function buildAggregateInput(contract, namespaceId, tableName, state, modelName, projection) {
901
+ const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
902
+ const variantJoins = polyInfo && polyInfo.mtiVariants.length > 0 ? buildMtiJoins(contract, namespaceId, polyInfo, state.variantName, void 0).joins : [];
903
+ const { source, where: effectiveWhere } = buildDedupedTableSource(contract, namespaceId, tableName, state, buildStateWhere(contract, tableName, state, { namespaceId }), projection, variantJoins);
904
+ let inner = SelectAst.from(source).withProjection(projection);
905
+ if (!hasEntries(state.distinct) && variantJoins.length > 0) inner = inner.withJoins(variantJoins);
906
+ if (effectiveWhere) inner = inner.withWhere(effectiveWhere);
907
+ if (hasEntries(state.distinctOn)) inner = inner.withDistinctOn(state.distinctOn.map((column) => ColumnRef.of(tableName, column)));
908
+ if (hasEntries(state.orderBy)) inner = inner.withOrderBy(state.orderBy);
909
+ if (state.limit !== void 0) inner = inner.withLimit(state.limit);
910
+ if (state.offset !== void 0) inner = inner.withOffset(state.offset);
911
+ return { source: DerivedTableSource.as(tableName, inner) };
912
+ }
619
913
  function toAggregateProjection(contract, aggregates, namespaceId, tableName, selector) {
620
914
  const { codec, input: inputCodec, lower } = resolveAggregate({
621
915
  aggregates,
@@ -698,19 +992,42 @@ function validateGroupedHavingExpr(expr) {
698
992
  rawExpr: rejectHavingExpr
699
993
  });
700
994
  }
701
- function compileAggregate(contract, aggregates, namespaceId, tableName, filters, aggregateSpec) {
995
+ function aggregateInputColumns(tableName, entries, orderBy) {
996
+ const columns = /* @__PURE__ */ new Set();
997
+ for (const [, selector] of entries) if (selector.column !== void 0) columns.add(selector.column);
998
+ for (const item of orderBy ?? []) if (item.expr.kind === "column-ref") columns.add(item.expr.column);
999
+ if (columns.size === 0) return [ProjectionItem.of("__row", LiteralExpr.of(1))];
1000
+ return Array.from(columns, (column) => ProjectionItem.of(column, ColumnRef.of(tableName, column)));
1001
+ }
1002
+ function compileAggregate(contract, aggregates, namespaceId, tableName, state, aggregateSpec, modelName) {
702
1003
  const entries = Object.entries(aggregateSpec);
703
1004
  if (entries.length === 0) throw ormError("ORM.AGGREGATE_SELECTOR_MISSING", "aggregate() requires at least one aggregation selector", { meta: {
704
1005
  method: "aggregate",
705
1006
  namespaceId,
706
1007
  tableName
707
1008
  } });
1009
+ if (state.distinctOn !== void 0 && state.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
1010
+ const hasPagination = state.limit !== void 0 || state.offset !== void 0;
1011
+ const hasDistinct = state.distinct !== void 0 && state.distinct.length > 0 || state.distinctOn !== void 0 && state.distinctOn.length > 0;
1012
+ if (hasPagination || hasDistinct) {
1013
+ const { source } = buildAggregateInput(contract, namespaceId, tableName, state, modelName, aggregateInputColumns(tableName, entries, state.orderBy));
1014
+ const projection = entries.map(([alias, selector]) => {
1015
+ const { expr, codec } = toAggregateProjection(contract, aggregates, namespaceId, tableName, selector);
1016
+ return ProjectionItem.of(alias, expr, codec);
1017
+ });
1018
+ const ast = SelectAst.from(source).withProjection(projection);
1019
+ const { params } = deriveParamsFromAst(ast);
1020
+ return buildOrmQueryPlan(contract, ast, params);
1021
+ }
1022
+ const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
1023
+ const variantJoins = polyInfo && polyInfo.mtiVariants.length > 0 ? buildMtiJoins(contract, namespaceId, polyInfo, state.variantName, void 0).joins : [];
1024
+ const where = buildStateWhere(contract, tableName, state, { namespaceId });
708
1025
  const projection = entries.map(([alias, selector]) => {
709
1026
  const { expr, codec } = toAggregateProjection(contract, aggregates, namespaceId, tableName, selector);
710
1027
  return ProjectionItem.of(alias, expr, codec);
711
1028
  });
712
1029
  let ast = SelectAst.from(tableSourceForContract(contract, namespaceId, tableName)).withProjection(projection);
713
- const where = combineWhereExprs(filters);
1030
+ if (variantJoins.length > 0) ast = ast.withJoins(variantJoins);
714
1031
  if (where) ast = ast.withWhere(where);
715
1032
  const { params } = deriveParamsFromAst(ast);
716
1033
  return buildOrmQueryPlan(contract, ast, params);
@@ -806,7 +1123,7 @@ function stripUndefinedValues(row) {
806
1123
  for (const [key, value] of Object.entries(row)) if (value !== void 0) result[key] = value;
807
1124
  return result;
808
1125
  }
809
- function createTableRefRemapper$1(fromTable, toTable) {
1126
+ function createTableRefRemapper(fromTable, toTable) {
810
1127
  return {
811
1128
  columnRef: (col) => col.table === fromTable ? ColumnRef.of(toTable, col.column) : col,
812
1129
  tableSource: (source) => {
@@ -824,7 +1141,7 @@ function buildCountMutationWhere(contract, namespaceId, tableName, filters, vari
824
1141
  if (!polyInfo || !variant || variant.strategy !== "mti") return combineWhereExprs(filters);
825
1142
  const pkColumn = resolvePrimaryKeyColumn(contract, namespaceId, tableName);
826
1143
  const baseTableRef = `${tableName}__write_filter`;
827
- const remapper = createTableRefRemapper$1(tableName, baseTableRef);
1144
+ const remapper = createTableRefRemapper(tableName, baseTableRef);
828
1145
  const innerFilters = filters.map((filter) => filter.rewrite(remapper));
829
1146
  const where = combineWhereExprs([BinaryExpr.eq(ColumnRef.of(baseTableRef, pkColumn), ColumnRef.of(tableName, pkColumn)), ...innerFilters]);
830
1147
  const joinOn = EqColJoinOn.of(ColumnRef.of(baseTableRef, pkColumn), ColumnRef.of(variant.table, pkColumn));
@@ -917,137 +1234,6 @@ function augmentSelectionForJoinColumns(selectedFields, requiredColumns) {
917
1234
  hiddenColumns
918
1235
  };
919
1236
  }
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
1237
  /**
1052
1238
  * The rule for which JSON projection variant an include entry carries. Every
1053
1239
  * site that puts a value into a `json_build_object` or a `json_agg` goes
@@ -1131,66 +1317,6 @@ function buildHiddenDiscriminatorProjection(contract, namespaceId, polyInfo, tab
1131
1317
  if (!needed) return [];
1132
1318
  return [ProjectionItem.of(POLYMORPHIC_DISCRIMINATOR_ALIAS, ColumnRef.of(tableRef, polyInfo.discriminatorColumn), codecRefForStorageColumn(contract.storage, namespaceId, polyInfo.baseTable, polyInfo.discriminatorColumn))];
1133
1319
  }
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
- });
1169
- }
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
- return {
1176
- columnRef: (col) => col.table === fromTable ? ColumnRef.of(toTable, col.column) : col,
1177
- tableSource: (source) => {
1178
- if (source.alias === fromTable) return TableSource.named(source.name, toTable, source.namespaceId);
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)
1183
- };
1184
- }
1185
- function buildStateWhere(contract, tableName, state, options) {
1186
- const filterTableName = options?.filterTableName;
1187
- const cursorWhere = buildCursorWhere(filterTableName ?? tableName, state.orderBy, state.cursor);
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);
1193
- }
1194
1320
  function buildIncludeOrderArtifacts(relationName, rowAlias, childOrderBy) {
1195
1321
  if (!childOrderBy || childOrderBy.length === 0) return {
1196
1322
  childOrderBy: void 0,
@@ -1208,35 +1334,6 @@ function buildIncludeOrderArtifacts(relationName, rowAlias, childOrderBy) {
1208
1334
  })
1209
1335
  };
1210
1336
  }
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
1337
  function localColumnsForRowInclude(include) {
1241
1338
  return include.through?.parentLocalColumns ?? [include.localColumn];
1242
1339
  }
@@ -1304,7 +1401,7 @@ function buildChildPolymorphismJoinsAndProjection(contract, include, childTableA
1304
1401
  hiddenProjection,
1305
1402
  baseSelectedFields: selection.baseSelectedFields
1306
1403
  };
1307
- const remapper = createTableRefRemapper(polyInfo.baseTable, childTableRef);
1404
+ const remapper = createTableRefRemapper$1(polyInfo.baseTable, childTableRef);
1308
1405
  return {
1309
1406
  joins: joins.map((join) => join.rewrite(remapper)),
1310
1407
  projection,
@@ -1368,12 +1465,13 @@ function buildManyToManyJunctionArtifacts(parentLocalRefs, childTableRef, throug
1368
1465
  }
1369
1466
  function buildIncludeChildRowsSelect(contract, aggregates, parentSource, include) {
1370
1467
  const childState = include.nested;
1468
+ if (childState.distinctOn !== void 0 && childState.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
1371
1469
  const parentLocalRefs = resolveParentLocalRefs(parentSource, include, localColumnsForRowInclude(include));
1372
1470
  const childSource = resolveChildTableSource(include, parentLocalRefs);
1373
1471
  const childTableAlias = childSource.alias;
1374
1472
  const childTableRef = childSource.tableRef;
1375
1473
  const rowsAlias = `${include.relationName}__rows`;
1376
- const remappedChildOrderBy = childTableAlias && childState.orderBy ? childState.orderBy.map((item) => item.rewrite(createTableRefRemapper(include.relatedTableName, childTableRef))) : childState.orderBy;
1474
+ const remappedChildOrderBy = childTableAlias && childState.orderBy ? childState.orderBy.map((item) => item.rewrite(createTableRefRemapper$1(include.relatedTableName, childTableRef))) : childState.orderBy;
1377
1475
  const { childOrderBy, hiddenOrderProjection, aggregateOrderBy } = buildIncludeOrderArtifacts(include.relationName, rowsAlias, remappedChildOrderBy);
1378
1476
  const childWhere = buildStateWhere(contract, childTableRef, childState, {
1379
1477
  filterTableName: include.relatedTableName,
@@ -1542,6 +1640,7 @@ function buildIncludeChildScalarSelect(contract, aggregates, parentSource, inclu
1542
1640
  const childTableAlias = childSource.alias;
1543
1641
  const childTableRef = childSource.tableRef;
1544
1642
  const state = scalar.state;
1643
+ if (state.distinctOn !== void 0 && state.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
1545
1644
  const childWhere = buildStateWhere(contract, childTableRef, state, {
1546
1645
  filterTableName: include.relatedTableName,
1547
1646
  namespaceId: include.relatedNamespaceId
@@ -1558,7 +1657,7 @@ function buildIncludeChildScalarSelect(contract, aggregates, parentSource, inclu
1558
1657
  const joinExpr = BinaryExpr.eq(ColumnRef.of(childTableRef, include.targetColumn), parentLocalRef);
1559
1658
  whereExpr = childWhere ? AndExpr.of([joinExpr, childWhere]) : joinExpr;
1560
1659
  }
1561
- const remappedOrderBy = childTableAlias && state.orderBy ? state.orderBy.map((item) => item.rewrite(createTableRefRemapper(include.relatedTableName, childTableRef))) : state.orderBy;
1660
+ const remappedOrderBy = childTableAlias && state.orderBy ? state.orderBy.map((item) => item.rewrite(createTableRefRemapper$1(include.relatedTableName, childTableRef))) : state.orderBy;
1562
1661
  const hasPagination = state.limit !== void 0 || state.offset !== void 0;
1563
1662
  const hasDistinct = state.distinct !== void 0 && state.distinct.length > 0 || state.distinctOn !== void 0 && state.distinctOn.length > 0;
1564
1663
  if (!(hasPagination || hasDistinct)) {
@@ -1676,13 +1775,11 @@ function buildCorrelatedIncludeProjection(contract, aggregates, parentSource, in
1676
1775
  }
1677
1776
  function buildSelectAst(contract, tableName, state, options) {
1678
1777
  const namespaceId = options.namespaceId;
1778
+ if (state.distinctOn !== void 0 && state.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
1679
1779
  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);
1780
+ 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
1781
  let ast = SelectAst.from(fromSource).withProjection(projection);
1684
- if (usesRowNumberDistinct) ast = ast.withWhere(BinaryExpr.eq(ColumnRef.of(tableName, "__prisma_distinct_rn"), LiteralExpr.of(1)));
1685
- else if (where) ast = ast.withWhere(where);
1782
+ if (effectiveWhere) ast = ast.withWhere(effectiveWhere);
1686
1783
  if (state.orderBy) ast = ast.withOrderBy(state.orderBy);
1687
1784
  if (state.selectedFields === void 0) ast = ast.withSelectAllIntent({ table: tableName });
1688
1785
  if (state.distinctOn && state.distinctOn.length > 0) ast = ast.withDistinctOn(state.distinctOn.map((column) => ColumnRef.of(tableName, column)));
@@ -1691,44 +1788,8 @@ function buildSelectAst(contract, tableName, state, options) {
1691
1788
  if (options.joins && options.joins.length > 0) ast = ast.withJoins(options.joins);
1692
1789
  return ast;
1693
1790
  }
1694
- function buildTopLevelDistinctRankedInner(contract, namespaceId, tableName, state, where) {
1695
- const distinctColumns = state.distinct;
1696
- if (distinctColumns === void 0 || distinctColumns.length === 0) throw new InternalError("buildTopLevelDistinctRankedInner called without `state.distinct`");
1697
- const allColsProjection = resolveTableColumns(contract, namespaceId, tableName).map((column) => ProjectionItem.of(column, ColumnRef.of(tableName, column)));
1698
- const distinctColumnRefs = distinctColumns.map((column) => ColumnRef.of(tableName, column));
1699
- const rankingOrderBy = state.orderBy && state.orderBy.length > 0 ? state.orderBy : distinctColumnRefs.map((expr) => OrderByItem.asc(expr));
1700
- let inner = SelectAst.from(tableSourceForContract(contract, namespaceId, tableName)).withProjection([...allColsProjection, ProjectionItem.of("__prisma_distinct_rn", WindowFuncExpr.rowNumber({
1701
- partitionBy: distinctColumnRefs,
1702
- orderBy: rankingOrderBy
1703
- }))]);
1704
- if (where) inner = inner.withWhere(where);
1705
- return inner;
1706
- }
1707
- function buildMtiJoins(contract, namespaceId, polyInfo, variantName, selectedColumnsByTable) {
1708
- const joins = [];
1709
- const projection = [];
1710
- const pkColumn = resolvePrimaryKeyColumn(contract, namespaceId, polyInfo.baseTable);
1711
- const variantsToJoin = variantName ? polyInfo.mtiVariants.filter((v) => v.modelName === variantName) : polyInfo.mtiVariants;
1712
- for (const variant of variantsToJoin) {
1713
- const joinType = variantName ? "inner" : "left";
1714
- const joinOn = EqColJoinOn.of(ColumnRef.of(polyInfo.baseTable, pkColumn), ColumnRef.of(variant.table, pkColumn));
1715
- const join = joinType === "inner" ? JoinAst.inner(tableSourceForContract(contract, namespaceId, variant.table), joinOn) : JoinAst.left(tableSourceForContract(contract, namespaceId, variant.table), joinOn);
1716
- joins.push(join);
1717
- const variantColumns = resolveTableColumns(contract, namespaceId, variant.table);
1718
- const selectedVariantColumns = selectedColumnsByTable?.get(variant.table);
1719
- for (const col of variantColumns) {
1720
- if (col === pkColumn) continue;
1721
- if (selectedColumnsByTable !== void 0 && selectedVariantColumns?.has(col) !== true) continue;
1722
- const alias = `${variant.table}__${col}`;
1723
- projection.push(ProjectionItem.of(alias, ColumnRef.of(variant.table, col), codecRefForStorageColumn(contract.storage, namespaceId, variant.table, col)));
1724
- }
1725
- }
1726
- return {
1727
- joins,
1728
- projection
1729
- };
1730
- }
1731
1791
  function compileSelect(contract, namespaceId, tableName, state, modelName) {
1792
+ if (state.distinctOn !== void 0 && state.distinctOn.length > 0) assertDistinctOnCapability(contract, "distinctOn");
1732
1793
  const polyInfo = modelName ? resolvePolymorphismInfo(contract, namespaceId, modelName) : void 0;
1733
1794
  const selection = polyInfo && modelName ? resolvePolymorphicProjectionSelection(contract, namespaceId, modelName, polyInfo, state) : void 0;
1734
1795
  const projectionState = selection ? {
@@ -3689,6 +3750,8 @@ var CollectionImpl = class CollectionImpl {
3689
3750
  * prior `orderBy(...)`; replaces any previous `distinct(...)` /
3690
3751
  * `distinctOn(...)` selection.
3691
3752
  *
3753
+ * Requires the `postgres.distinctOn` capability.
3754
+ *
3692
3755
  * ```typescript
3693
3756
  * // Latest post per user:
3694
3757
  * const latestPerUser = await db.orm.Post
@@ -3698,6 +3761,7 @@ var CollectionImpl = class CollectionImpl {
3698
3761
  * ```
3699
3762
  */
3700
3763
  distinctOn(...fields) {
3764
+ assertDistinctOnCapability(this.contract, "distinctOn");
3701
3765
  const distinctOnFields = mapFieldsToColumns(this.contract, this.namespaceId, this.modelName, fields);
3702
3766
  return this.#clone({
3703
3767
  distinct: void 0,
@@ -3809,7 +3873,7 @@ var CollectionImpl = class CollectionImpl {
3809
3873
  alias
3810
3874
  } });
3811
3875
  const annotationsMap = this.#collectAnnotationsFromMeta(configure, "read", "aggregate");
3812
- const compiled = mergeAnnotations(compileAggregate(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.tableName, this.state.filters, aggregateSpec), annotationsMap);
3876
+ const compiled = mergeAnnotations(compileAggregate(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.tableName, this.state, aggregateSpec, this.modelName), annotationsMap);
3813
3877
  const row = (await queryPlanRows(this.ctx.runtime, compiled).toArray())[0] ?? {};
3814
3878
  const result = {};
3815
3879
  for (const [alias, selector] of entries) result[alias] = row[alias] ?? this.#emptyAggregateValue(selector);