From 00508e8f9bfd28ef0c4847e90420e2bb138a7a85 Mon Sep 17 00:00:00 2001 From: englefly Date: Mon, 2 Dec 2024 00:08:40 +0800 Subject: [PATCH] join not finish --- .../rules/rewrite/PushDownEncodeSlot.java | 303 +++++++++++++----- .../doris/nereids/trees/plans/Plan.java | 1 - .../compress_materialize.out | 18 ++ .../compress_materialize/pushdown_encode.out | 12 + .../compress_materialize.groovy | 53 --- .../pushdown_encode.groovy | 108 +++++++ 6 files changed, 352 insertions(+), 143 deletions(-) create mode 100644 regression-test/data/nereids_p0/compress_materialize/pushdown_encode.out create mode 100644 regression-test/suites/nereids_p0/compress_materialize/pushdown_encode.groovy diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownEncodeSlot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownEncodeSlot.java index 750a18ead7cdb63..6370ae0e1a932a0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownEncodeSlot.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownEncodeSlot.java @@ -17,9 +17,14 @@ package org.apache.doris.nereids.rules.rewrite; +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import org.apache.commons.logging.Log; import org.apache.doris.nereids.rules.Rule; import org.apache.doris.nereids.rules.RuleType; import org.apache.doris.nereids.trees.expressions.Alias; +import org.apache.doris.nereids.trees.expressions.EqualPredicate; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Slot; @@ -27,16 +32,22 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.EncodeString; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation; +import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; import org.apache.doris.nereids.trees.plans.logical.LogicalProject; +import org.apache.doris.nereids.trees.plans.logical.LogicalRelation; import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; -import org.apache.doris.nereids.util.PlanUtils; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import org.apache.doris.nereids.types.coercion.CharacterType; +import org.apache.hadoop.util.Lists; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -63,7 +74,7 @@ * --> any(A, B) */ public class PushDownEncodeSlot extends OneRewriteRuleFactory { - + private static final Logger LOG = LogManager.getLogger(PushDownEncodeSlot.class); @Override public Rule build() { return logicalProject() @@ -82,18 +93,23 @@ private boolean containsEncode(Expression expr) { return expr instanceof EncodeString && expr.child(0) instanceof SlotReference; } - private List collectEncodeAlias(LogicalProject project) { - List encodeAlias = new ArrayList<>(); + private List collectEncodeAliases(LogicalProject project) { + List encodeAliases = new ArrayList<>(); project.getProjects().forEach(e -> { - if (e instanceof Alias && e.child(0) instanceof EncodeString) { - encodeAlias.add((Alias) e); + if (e instanceof Alias + && e.child(0) instanceof EncodeString + && e.child(0).child(0) instanceof SlotReference) { + encodeAliases.add((Alias) e); } }); - return encodeAlias; + return encodeAliases; } - private LogicalProject rewriteRootProject(LogicalProject project, + private LogicalProject rewriteRootProject(LogicalProject project, List encodeAlias) { + if (encodeAlias.isEmpty()) { + return project; + } List projections = project.getProjects().stream().map( e -> encodeAlias.contains(e) ? e.toSlot() : e) .collect(Collectors.toList()); @@ -102,93 +118,149 @@ private LogicalProject rewriteRootProject(LogicalProject pushDownEncodeSlot(LogicalProject project) { - List encodeAlias = collectEncodeAlias(project); - project = rewriteRootProject(project, encodeAlias); + List encodeAliases = collectEncodeAliases(project); + PushDownContext ctx = new PushDownContext(project, encodeAliases); + if (ctx.notPushed.size() == encodeAliases.size()) { + return project; + } Plan child = project.child(); - Plan newChild = child.accept(EncodeSlotPushDownVisitor.INSTANCE, encodeAlias); - if (child.equals(newChild)) { + PushDownContext childContext = new PushDownContext(child, ctx.toBePushedToChild.get(child)); + Plan newChild = child.accept(EncodeSlotPushDownVisitor.INSTANCE, childContext); + List pushed = ctx.toBePused; + if (!child.equals(newChild)) { + if (newChild instanceof LogicalProject) { + pushed.removeAll(childContext.notPushed); + newChild = ((LogicalProject) newChild).child(); + } project = (LogicalProject) project.withChildren(newChild); + project = rewriteRootProject(project, pushed); } return project; } - /** - * push down encode slot - */ - public static class EncodeSlotPushDownVisitor extends PlanVisitor> { - public static EncodeSlotPushDownVisitor INSTANCE = new EncodeSlotPushDownVisitor(); + public static class PushDownContext { + public Plan plan; - public LogicalProject replaceProjectsEncodeSlot(LogicalProject project, - Map replaceMap) { - List newProjections = new ArrayList<>(); - boolean changed = false; - for (NamedExpression expr : project.getProjects()) { - if (replaceMap.containsKey(expr) && replaceMap.get(expr) instanceof NamedExpression) { - newProjections.add((NamedExpression) replaceMap.get(expr)); - changed = true; - } else { - newProjections.add(expr); + public List encodeAliases; + // encode_as_int(slot1) as slot2 + // replaceMap: + // slot1 -> slot2 + Map replaceMap = new HashMap<>(); + // child plan -> aliases in encodeAliases which can be pushed down to child plan + Map> toBePushedToChild = new HashMap<>(); + List toBePused = new ArrayList<>(); + // the aliases that cannot be pushed down to any child plan + // for example: + // encode(A+B) as x, where plan is a join, and A, B comes from join's left and right child respectively + List notPushed = new ArrayList<>(); + public PushDownContext(Plan plan, List encodeAliases) { + this.plan = plan; + this.encodeAliases = encodeAliases; + prepare(); + } + + public static boolean canBothSidesEncode(EqualPredicate equal) { + return equal.left().getDataType() instanceof CharacterType + && ((CharacterType) equal.left().getDataType()).getLen() < 15 + && ((CharacterType) equal.right().getDataType()).getLen() < 15 + && equal.left() instanceof SlotReference && equal.right() instanceof SlotReference; + } + + // init replaceMap/toBePushed/notPushed + private void prepare() { + List> childrenPassThroughSlots = + plan.children().stream().map(n -> getPassThroughSlots(n)).collect(Collectors.toList()); + if (plan instanceof LogicalJoin) { + LogicalJoin join = (LogicalJoin) plan; + BiMap equalSlots = HashBiMap.create(); + for (Expression e : join.getHashJoinConjuncts()) { + EqualPredicate equal = (EqualPredicate) e; + if (canBothSidesEncode(equal)) { + equalSlots.put((SlotReference) equal.left(), (SlotReference) equal.right()); + } + } + List expandedOtherHands = expandEncodeAliasForJoin(equalSlots); + + for (SlotReference otherHand : expandedOtherHands) { + if (join.left().getOutputSet().contains(otherHand)) { + childrenPassThroughSlots.get(0).add(otherHand); + } else { + childrenPassThroughSlots.get(1).add(otherHand); + } } } - if (changed) { - return project.withProjects(newProjections); + for (Alias alias : encodeAliases) { + EncodeString encode = (EncodeString) alias.child(); + Expression strExpr = encode.child(); + boolean pushed = false; + Preconditions.checkArgument(strExpr instanceof SlotReference, + "expect encode_as_xxx(slot), but " + alias); + + for (int i = 0; i < childrenPassThroughSlots.size(); i++) { + if (childrenPassThroughSlots.get(i).contains(strExpr)) { + toBePushedToChild.putIfAbsent(plan.child(i), new ArrayList<>()); + toBePushedToChild.get(plan.child(i)).add(alias); + toBePused.add(alias); + replaceMap.put(alias.child().child(0), (SlotReference) alias.toSlot()); + pushed = true; + break; + } + } + if (!pushed) { + notPushed.add(alias); + } } - return project; } - @Override - public Plan visit(Plan plan, List encodeAlias) { - // replaceMap: - // encode_as_int(slot1) -> slot2 - // slot1 -> slot2 - Map replaceMap = new HashMap<>(); - List> byPassSlots = plan.children().stream() - .map(this::getPassThroughSlots) - .collect(Collectors.toList()); - Map> toBePushed = new HashMap<>(); - for (Alias alias : encodeAlias) { - EncodeString encode = (EncodeString) alias.child(); - Expression strExpr = encode.child(); - if (strExpr instanceof SlotReference) { - for (int i = 0; i < byPassSlots.size(); i++) { - if (byPassSlots.get(i).contains(strExpr)) { - toBePushed.putIfAbsent(plan.child(i), new ArrayList<>()); - toBePushed.get(plan.child(i)).add(alias); - replaceMap.put(alias, alias.toSlot()); - replaceMap.put(alias.child().child(0), alias.toSlot()); - break; + public List expandEncodeAliasForJoin(BiMap equalSlots) { + List expandedOtherHand = new ArrayList<>(); + List expanded = new ArrayList<>(); + for (Alias alias : encodeAliases) { + if (alias.child().child(0) instanceof SlotReference) { + SlotReference slot = (SlotReference) alias.child().child(0); + if (equalSlots.containsKey(slot)) { + Alias encodeOtherHand = (Alias) alias.withChildren(alias.child().withChildren(equalSlots.get(slot))); + if (!encodeAliases.contains(encodeOtherHand)) { + expanded.add(encodeOtherHand); + expandedOtherHand.add(equalSlots.get(slot)); } } } } - // rewrite plan according to encode expression - // for example: project(encode_as_int(slot1) as slot2) - // 1. rewrite project's expressions: project(slot2), - // 2. push "encode_as_int(slot1) as slot2" down to project.child() - // rewrite expressions - if (plan instanceof LogicalProject) { - plan = replaceProjectsEncodeSlot((LogicalProject)plan, replaceMap); + encodeAliases.addAll(expanded); + return expandedOtherHand; + } + + public static Set getPassThroughSlots(Plan plan) { + if (plan instanceof LogicalRelation) { + return new HashSet<>(); + } + Set outputSlots = Sets.newHashSet(plan.getOutputSet()); + Set keySlots = Sets.newHashSet(); + for (Expression e : plan.getExpressions()) { + if (!(e instanceof SlotReference)) { + keySlots.addAll(e.getInputSlots()); + } } - // rewrite children + outputSlots.removeAll(keySlots); + return outputSlots; + } + } + + + /** + * push down encode slot + */ + public static class EncodeSlotPushDownVisitor extends PlanVisitor { + public static EncodeSlotPushDownVisitor INSTANCE = new EncodeSlotPushDownVisitor(); + + public Plan visitChildren(Plan plan, PushDownContext ctx) { ImmutableList.Builder newChildren = ImmutableList.builderWithExpectedSize(plan.arity()); boolean hasNewChildren = false; for (Plan child : plan.children()) { Plan newChild; - if (toBePushed.containsKey(child)) { - if (child instanceof LogicalProject && child.child(0) instanceof LogicalCatalogRelation) { - LogicalProject project = (LogicalProject) child; - List projections = - PlanUtils.mergeProjections(project.getProjects(), toBePushed.get(child)); - newChild = project.withProjects(projections); - } else if (child instanceof LogicalCatalogRelation) { - List newProjections = new ArrayList<>(); - newProjections.addAll(child.getOutput()); - newProjections.addAll(toBePushed.get(child)); - newChild = new LogicalProject<>(newProjections, child); - hasNewChildren = true; - } else { - newChild = child.accept(this, toBePushed.get(child)); - } + if (ctx.toBePushedToChild.containsKey(child)) { + newChild = child.accept(this, new PushDownContext(child, ctx.toBePushedToChild.get(child))); if (!hasNewChildren && newChild != child) { hasNewChildren = true; } @@ -197,28 +269,81 @@ public Plan visit(Plan plan, List encodeAlias) { } newChildren.add(newChild); } - if (hasNewChildren) { plan = plan.withChildren(newChildren.build()); } return plan; } - private Set getPassThroughSlots(Plan plan) { - Set outputSlots = Sets.newHashSet(plan.getOutputSet()); - Set keySlots = Sets.newHashSet(); - if (plan instanceof LogicalProject) { - for (NamedExpression e : ((LogicalProject) plan).getProjects()) { - if (!(e instanceof SlotReference)) { - keySlots.addAll(e.getInputSlots()); - } + private Plan projectNotPushedAlias(Plan plan, List notPushedAlias) { + if (!notPushedAlias.isEmpty()) { + // project encode expressions if they are not pushed down + // project(encode) + // +--> plan + List projections = + notPushedAlias.stream().map(e -> (NamedExpression) e).collect(Collectors.toList()); + projections.addAll(plan.getOutput()); + plan = new LogicalProject<>(projections, plan); + } + return plan; + } + + @Override + public Plan visit(Plan plan, PushDownContext ctx) { + plan = visitChildren(plan, ctx); + plan = projectNotPushedAlias(plan, ctx.notPushed); + return plan; + } + + @Override + public LogicalProject visitLogicalProject( + LogicalProject project, PushDownContext ctx) { + project = (LogicalProject) visitChildren(project, ctx); + /* + * push down "encode(v1) as v2 + * project(v1, ...) + * +--->any(v1) + * => + * project(v2, ...) + * +--->any(v1) + * and push down "encode(v1) as v2" to any(v1) + */ + List projections = Lists.newArrayListWithCapacity(project.getProjects().size()); + for (NamedExpression e : project.getProjects()) { + if (ctx.toBePused.contains(e)) { + projections.add(e.toSlot()); + } else { + projections.add(e); } - } else { - keySlots = plan.getInputSlots(); } - outputSlots.removeAll(keySlots); - return outputSlots; + return project.withProjects(projections); } - } + @Override + public Plan visitLogicalJoin(LogicalJoin join, PushDownContext ctx) { + join = (LogicalJoin) visitChildren(join, ctx); + // TODO: rewrite join condition + List newConjuncts = Lists.newArrayListWithCapacity(join.getHashJoinConjuncts().size()); + boolean changed = false; + for (Expression e : join.getHashJoinConjuncts()) { + EqualPredicate equal = (EqualPredicate) e; + if (PushDownContext.canBothSidesEncode(equal)) { + SlotReference newLeft = ctx.replaceMap.get(equal.left()); + SlotReference newRight = ctx.replaceMap.get(equal.right()); + Preconditions.checkArgument(newLeft != null, + "PushDownEncodeSlot replaceMap is not valid, " + equal.left() + " is not found" ); + Preconditions.checkArgument(newRight != null, + "PushDownEncodeSlot replaceMap is not valid, " + equal.right() + " is not found" ); + equal = (EqualPredicate) equal.withChildren(newLeft, newRight); + changed = true; + } + newConjuncts.add(equal); + } + if (changed) { + join = join.withJoinConjuncts(newConjuncts, join.getOtherJoinConjuncts(), join.getJoinReorderContext()); + } + Plan plan = projectNotPushedAlias(join, ctx.notPushed); + return plan; + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java index 26122e68a9783f5..60af0d18666f2bc 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java @@ -36,7 +36,6 @@ import com.google.common.collect.Sets; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; diff --git a/regression-test/data/nereids_p0/compress_materialize/compress_materialize.out b/regression-test/data/nereids_p0/compress_materialize/compress_materialize.out index eee04795628144d..8922b2155aec2b1 100644 --- a/regression-test/data/nereids_p0/compress_materialize/compress_materialize.out +++ b/regression-test/data/nereids_p0/compress_materialize/compress_materialize.out @@ -53,3 +53,21 @@ a 1 中 8 bb 3 +-- !explain_sort_agg -- +cost = 19.563333333333333 +PhysicalResultSink[294] ( outputExprs=[v1#1] ) ++--PhysicalProject[289]@5 ( stats=1, projects=[v1#1] ) + +--PhysicalQuickSort[284]@4 ( stats=1, orderKeys=[encode_as_bigint(v1)#4 asc null first], phase=MERGE_SORT ) + +--PhysicalDistribute[279]@7 ( stats=1, distributionSpec=DistributionSpecGather ) + +--PhysicalQuickSort[274]@7 ( stats=1, orderKeys=[encode_as_bigint(v1)#4 asc null first], phase=LOCAL_SORT ) + +--PhysicalProject[269]@3 ( stats=1, projects=[decode_as_varchar(encode_as_bigint(v1)#3) AS `v1`#1, encode_as_bigint(decode_as_varchar(encode_as_bigint(v1)#3)) AS `encode_as_bigint(decode_as_varchar(encode_as_bigint(v1)))`#4], multi_proj=l0([encode_as_bigint(v1)#3, decode_as_varchar(encode_as_bigint(v1)#3) AS `decode_as_varchar(encode_as_bigint(v1))`#5])l1([decode_as_varchar(encode_as_bigint(v1))#5 AS `v1`#1, encode_as_bigint(decode_as_varchar(encode_as_bigint(v1))#5) AS `encode_as_bigint(decode_as_varchar(encode_as_bigint(v1)))`#4]) ) + +--PhysicalHashAggregate[264]@2 ( stats=1, aggPhase=GLOBAL, aggMode=BUFFER_TO_RESULT, maybeUseStreaming=false, groupByExpr=[encode_as_bigint(v1)#3], outputExpr=[encode_as_bigint(v1)#3], partitionExpr=Optional[[encode_as_bigint(v1)#3]], topnFilter=false, topnPushDown=false ) + +--PhysicalDistribute[259]@8 ( stats=1, distributionSpec=DistributionSpecHash ( orderedShuffledColumns=[3], shuffleType=EXECUTION_BUCKETED, tableId=-1, selectedIndexId=-1, partitionIds=[], equivalenceExprIds=[[3]], exprIdToEquivalenceSet={3=0} ) ) + +--PhysicalHashAggregate[254]@8 ( stats=1, aggPhase=LOCAL, aggMode=INPUT_TO_BUFFER, maybeUseStreaming=true, groupByExpr=[encode_as_bigint(v1)#3], outputExpr=[encode_as_bigint(v1)#3], partitionExpr=Optional[[encode_as_bigint(v1)#3]], topnFilter=false, topnPushDown=false ) + +--PhysicalProject[249]@1 ( stats=1, projects=[encode_as_bigint(v1#1) AS `encode_as_bigint(v1)`#3] ) + +--PhysicalOlapScan[t1]@0 ( stats=1 ) + +-- !exec_sort_agg -- +a +b + diff --git a/regression-test/data/nereids_p0/compress_materialize/pushdown_encode.out b/regression-test/data/nereids_p0/compress_materialize/pushdown_encode.out new file mode 100644 index 000000000000000..5374797c15c5a34 --- /dev/null +++ b/regression-test/data/nereids_p0/compress_materialize/pushdown_encode.out @@ -0,0 +1,12 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !exec_sort_agg -- +a +b + +-- !exec_sort_filter -- +a +b + +-- !exec_agg_join -- +b + diff --git a/regression-test/suites/nereids_p0/compress_materialize/compress_materialize.groovy b/regression-test/suites/nereids_p0/compress_materialize/compress_materialize.groovy index 6da6174279e879e..3599220209c503b 100644 --- a/regression-test/suites/nereids_p0/compress_materialize/compress_materialize.groovy +++ b/regression-test/suites/nereids_p0/compress_materialize/compress_materialize.groovy @@ -193,58 +193,5 @@ suite("compress_materialize") { qt_sort "select * from compressSort order by k desc, v"; qt_sort "select * from compressSort order by k desc nulls last"; qt_sort "select * from compressSort order by k desc nulls last, v limit 3"; - - // push down encode slot - sql """ - drop table if exists t1; - CREATE TABLE t1 ( - `k1` int NOT NULL, - `v1` char(5) NOT NULL - ) ENGINE=OLAP - DUPLICATE KEY(`k1`) - DISTRIBUTED BY HASH(`k1`) BUCKETS 3 - PROPERTIES ( - "replication_allocation" = "tag.location.default: 1" - ); - - insert into t1 values (1, "a"), (2, "b"); - - drop table if exists t2; - CREATE TABLE t2 ( - `k2` int NOT NULL, - `v2` char(5) NOT NULL - ) ENGINE=OLAP - DUPLICATE KEY(`k2`) - DISTRIBUTED BY HASH(`k2`) BUCKETS 3 - PROPERTIES ( - "replication_allocation" = "tag.location.default: 1" - ); - - insert into t2 values (3, "c"), (4, "d"); - """ - - qt_explain_sort_agg """ - explain physical plan - select v1 - from (select sum(k1) as k, v1 from t1 group by v1) t - order by v1; - """ - - qt_exec_sort_agg """ - select v1 - from (select sum(k1) as k, v1 from t1 group by v1) t - order by v1; - """ - - qt_sort_proj - """ - explain physical plan - select v1 - from t1 - where k1 > 0 - order by v1; - """ - - // TODO: select k, v as x from (select k1 as k, v1 as v from t1) A union all (select k2 as k, v2 as v from t2) order by x; -- 下推 union } diff --git a/regression-test/suites/nereids_p0/compress_materialize/pushdown_encode.groovy b/regression-test/suites/nereids_p0/compress_materialize/pushdown_encode.groovy new file mode 100644 index 000000000000000..c29a98f4e3a266d --- /dev/null +++ b/regression-test/suites/nereids_p0/compress_materialize/pushdown_encode.groovy @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +suite("pushdown_encode") { +// push down encode slot + sql """ + drop table if exists t1; + CREATE TABLE t1 ( + `k1` int NOT NULL, + `v1` char(5) NOT NULL + ) ENGINE=OLAP + DUPLICATE KEY(`k1`) + DISTRIBUTED BY HASH(`k1`) BUCKETS 3 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + + insert into t1 values (1, "a"), (2, "b"); + + drop table if exists t2; + CREATE TABLE t2 ( + `k2` int NOT NULL, + `v2` char(5) NOT NULL + ) ENGINE=OLAP + DUPLICATE KEY(`k2`) + DISTRIBUTED BY HASH(`k2`) BUCKETS 3 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + + insert into t2 values (3, "c"), (4, "d"), (2, "b"); + """ + + explain{ + sql """ + physical plan + select v1 + from (select sum(k1) as k, v1 from t1 group by v1) t + order by v1; + """ + contains("orderKeys=[encode_as_bigint(v1)#4 asc null first]") + contains("projects=[decode_as_varchar(encode_as_bigint(v1)#3) AS `v1`#1, encode_as_bigint(decode_as_varchar(encode_as_bigint(v1)#3)) AS `encode_as_bigint(decode_as_varchar(encode_as_bigint(v1)))`#4]") + contains("groupByExpr=[encode_as_bigint(v1)#3]") + contains("projects=[encode_as_bigint(v1#1) AS `encode_as_bigint(v1)`#3]") + } + + qt_exec_sort_agg """ + select v1 + from (select sum(k1) as k, v1 from t1 group by v1) t + order by v1; + """ + + explain{ + sql """ + physical plan + select v1 + from t1 + where k1 > 0 + order by v1; + """ + contains("orderKeys=[encode_as_bigint(v1)#2 asc null first]") + contains("projects=[v1#1, encode_as_bigint(v1#1) AS `encode_as_bigint(v1)`#2]") + } + + qt_exec_sort_filter """ + select v1 + from t1 + where k1 > 0 + order by v1; + """ + + explain{ + sql """ + physical plan + select v1 + from t1 join t2 on v1=v2 + group by v1; + """ + contains("groupByExpr=[encode_as_bigint(v1)#4]") + contains("projects=[encode_as_bigint(v1#1) AS `encode_as_bigint(v1)`#4]") + } + + + qt_exec_agg_join""" + select v1 + from t1 join t2 on v1=v2 + group by v1; + """ + // TODO: select k, v as x from (select k1 as k, v1 as v from t1) A union all (select k2 as k, v2 as v from t2) order by x; -- 下推 union + + // not pushed + // project(encode(A)) + // +-->join(A=B+1) +} \ No newline at end of file