@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.
- package/dist/orm-client.d.mts +7 -1
- package/dist/orm-client.d.mts.map +1 -1
- package/dist/orm-client.mjs +334 -270
- package/dist/orm-client.mjs.map +1 -1
- package/package.json +18 -18
package/dist/orm-client.mjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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);
|