diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SortInfo.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SortInfo.java index 0dc41c037d..a4d5962bc4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SortInfo.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SortInfo.java @@ -139,6 +139,13 @@ public class SortInfo { return materializedOrderingExprs; } + public void addMaterializedOrderingExpr(Expr expr) { + if (materializedOrderingExprs == null) { + materializedOrderingExprs = Lists.newArrayList(); + } + materializedOrderingExprs.add(expr); + } + public List getSortTupleSlotExprs() { return sortTupleSlotExprs; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java index a505582f0b..14dbb80e14 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java @@ -160,6 +160,7 @@ import org.apache.doris.planner.external.HiveScanNode; import org.apache.doris.planner.external.hudi.HudiScanNode; import org.apache.doris.planner.external.iceberg.IcebergScanNode; import org.apache.doris.planner.external.paimon.PaimonScanNode; +import org.apache.doris.qe.ConnectContext; import org.apache.doris.tablefunction.TableValuedFunctionIf; import org.apache.doris.thrift.TColumn; import org.apache.doris.thrift.TFetchOption; @@ -1069,7 +1070,25 @@ public class PhysicalPlanTranslator extends DefaultPlanVisitor 0) { + scanNode.setSortLimit(sortNode.getLimit() + sortNode.getOffset()); + } else { + scanNode.setSortLimit(sortNode.getLimit()); + } + } } addPlanRoot(currentFragment, sortNode, topN); } else { @@ -1094,6 +1113,60 @@ public class PhysicalPlanTranslator extends DefaultPlanVisitor ConnectContext.get().getSessionVariable().topnOptLimitThreshold) { + return false; + } + + // Ensure all isAscOrder is same, ande length != 0. + // Can't be zorder. + if (sortNode.getSortInfo().getIsAscOrder().stream().distinct().count() != 1 + || olapTable.isZOrderSort()) { + return false; + } + + // Tablet's order by key only can be the front part of schema. + // Like: schema: a.b.c.d.e.f.g order by key: a.b.c (no a,b,d) + // Do **prefix match** to check if order by key can be pushed down. + // olap order by key: a.b.c.d + // sort key: (a) (a,b) (a,b,c) (a,b,c,d) is ok + // (a,c) (a,c,d), (a,c,b) (a,c,f) (a,b,c,d,e)is NOT ok + List sortExprs = sortNode.getSortInfo().getOrderingExprs(); + List nullsFirsts = sortNode.getSortInfo().getNullsFirst(); + List isAscOrders = sortNode.getSortInfo().getIsAscOrder(); + if (sortExprs.size() > olapTable.getDataSortInfo().getColNum()) { + return false; + } + for (int i = 0; i < sortExprs.size(); i++) { + // table key. + Column tableKey = olapTable.getFullSchema().get(i); + // sort slot. + Expr sortExpr = sortExprs.get(i); + if (sortExpr instanceof SlotRef) { + SlotRef slotRef = (SlotRef) sortExpr; + if (tableKey.equals(slotRef.getColumn())) { + // ORDER BY DESC NULLS FIRST can not be optimized to only read file tail, + // since NULLS is at file head but data is at tail + if (tableKey.isAllowNull() && nullsFirsts.get(i) && !isAscOrders.get(i)) { + return false; + } + } else { + return false; + } + } else { + return false; + } + } + + return true; + } + private SortNode translateSortNode(AbstractPhysicalSort sort, PlanNode childNode, PlanTranslatorContext context) { List oldOrderingExprList = Lists.newArrayList();