This is an automated email from the ASF dual-hosted git repository.

morrysnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 10fd75e03d5 [fix](Nereids) fix bind having aggregate failed (#32490)
10fd75e03d5 is described below

commit 10fd75e03d5737a48e4106b1a838b56a3ea4d65b
Author: 924060929 <[email protected]>
AuthorDate: Fri Mar 22 11:53:46 2024 +0800

    [fix](Nereids) fix bind having aggregate failed (#32490)
    
    fix bind having aggregate failed, keep the behavior like mysql
---
 .../org/apache/doris/catalog/FunctionRegistry.java |  41 ++++--
 .../nereids/rules/analysis/BindExpression.java     | 135 +++++++++++++++++--
 .../functions/AggCombinerFunctionBuilder.java      |   5 +
 .../functions/BuiltinFunctionBuilder.java          |  12 +-
 .../expressions/functions/FunctionBuilder.java     |   2 +
 .../expressions/functions/udf/AliasUdfBuilder.java |   5 +
 .../expressions/functions/udf/JavaUdafBuilder.java |   5 +
 .../expressions/functions/udf/JavaUdfBuilder.java  |   5 +
 .../data/nereids_syntax_p0/bind_priority.out       |  40 ++++++
 .../suites/nereids_syntax_p0/bind_priority.groovy  | 143 ++++++++++++++++++++-
 10 files changed, 366 insertions(+), 27 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionRegistry.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionRegistry.java
index 5552b3d1d5c..56016ad071a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionRegistry.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionRegistry.java
@@ -53,14 +53,14 @@ public class FunctionRegistry {
     // to record the global alias function and other udf.
     private static final String GLOBAL_FUNCTION = "__GLOBAL_FUNCTION__";
 
-    private final Map<String, List<FunctionBuilder>> 
name2InternalBuiltinBuilders;
+    private final Map<String, List<FunctionBuilder>> name2BuiltinBuilders;
     private final Map<String, Map<String, List<FunctionBuilder>>> 
name2UdfBuilders;
 
     public FunctionRegistry() {
-        name2InternalBuiltinBuilders = new ConcurrentHashMap<>();
+        name2BuiltinBuilders = new ConcurrentHashMap<>();
         name2UdfBuilders = new ConcurrentHashMap<>();
-        registerBuiltinFunctions(name2InternalBuiltinBuilders);
-        afterRegisterBuiltinFunctions(name2InternalBuiltinBuilders);
+        registerBuiltinFunctions(name2BuiltinBuilders);
+        afterRegisterBuiltinFunctions(name2BuiltinBuilders);
     }
 
     // this function is used to test.
@@ -78,12 +78,33 @@ public class FunctionRegistry {
     }
 
     public Optional<List<FunctionBuilder>> tryGetBuiltinBuilders(String name) {
-        List<FunctionBuilder> builders = 
name2InternalBuiltinBuilders.get(name);
-        return name2InternalBuiltinBuilders.get(name) == null
+        List<FunctionBuilder> builders = name2BuiltinBuilders.get(name);
+        return name2BuiltinBuilders.get(name) == null
                 ? Optional.empty()
                 : Optional.of(ImmutableList.copyOf(builders));
     }
 
+    public boolean isAggregateFunction(String dbName, String name) {
+        name = name.toLowerCase();
+        Class<?> aggClass = 
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction.class;
+        if (StringUtils.isEmpty(dbName)) {
+            List<FunctionBuilder> functionBuilders = 
name2BuiltinBuilders.get(name);
+            for (FunctionBuilder functionBuilder : functionBuilders) {
+                if 
(aggClass.isAssignableFrom(functionBuilder.functionClass())) {
+                    return true;
+                }
+            }
+        }
+
+        List<FunctionBuilder> udfBuilders = findUdfBuilder(dbName, name);
+        for (FunctionBuilder udfBuilder : udfBuilders) {
+            if (aggClass.isAssignableFrom(udfBuilder.functionClass())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     // currently we only find function by name and arity and args' types.
     public FunctionBuilder findFunctionBuilder(String dbName, String name, 
List<?> arguments) {
         List<FunctionBuilder> functionBuilders = null;
@@ -92,11 +113,11 @@ public class FunctionRegistry {
 
         if (StringUtils.isEmpty(dbName)) {
             // search internal function only if dbName is empty
-            functionBuilders = 
name2InternalBuiltinBuilders.get(name.toLowerCase());
+            functionBuilders = name2BuiltinBuilders.get(name.toLowerCase());
             if (CollectionUtils.isEmpty(functionBuilders) && 
AggCombinerFunctionBuilder.isAggStateCombinator(name)) {
                 String nestedName = 
AggCombinerFunctionBuilder.getNestedName(name);
                 String combinatorSuffix = 
AggCombinerFunctionBuilder.getCombinatorSuffix(name);
-                functionBuilders = 
name2InternalBuiltinBuilders.get(nestedName.toLowerCase());
+                functionBuilders = 
name2BuiltinBuilders.get(nestedName.toLowerCase());
                 if (functionBuilders != null) {
                     List<FunctionBuilder> candidateBuilders = 
Lists.newArrayListWithCapacity(functionBuilders.size());
                     for (FunctionBuilder functionBuilder : functionBuilders) {
@@ -199,8 +220,8 @@ public class FunctionRegistry {
         }
         synchronized (name2UdfBuilders) {
             Map<String, List<FunctionBuilder>> builders = 
name2UdfBuilders.getOrDefault(dbName, ImmutableMap.of());
-            builders.getOrDefault(name, Lists.newArrayList()).removeIf(builder 
-> ((UdfBuilder) builder).getArgTypes()
-                    .equals(argTypes));
+            builders.getOrDefault(name, Lists.newArrayList())
+                    .removeIf(builder -> ((UdfBuilder) 
builder).getArgTypes().equals(argTypes));
         }
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
index a561507fb94..04ba599aa60 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
@@ -19,11 +19,13 @@ package org.apache.doris.nereids.rules.analysis;
 
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.FunctionRegistry;
+import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.NereidsPlanner;
 import org.apache.doris.nereids.StatementContext;
 import org.apache.doris.nereids.analyzer.MappingSlot;
 import org.apache.doris.nereids.analyzer.Scope;
+import org.apache.doris.nereids.analyzer.UnboundFunction;
 import org.apache.doris.nereids.analyzer.UnboundOneRowRelation;
 import org.apache.doris.nereids.analyzer.UnboundResultSink;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
@@ -351,12 +353,12 @@ public class BindExpression implements 
AnalysisRuleFactory {
         CascadesContext cascadesContext = ctx.cascadesContext;
 
         // bind slot by child.output first
-        Scope defaultScope = toScope(cascadesContext, childPlan.getOutput());
+        Scope childOutput = toScope(cascadesContext, childPlan.getOutput());
         // then bind slot by child.children.output
-        Supplier<Scope> backupScope = Suppliers.memoize(() ->
+        Supplier<Scope> childChildrenOutput = Suppliers.memoize(() ->
                 toScope(cascadesContext, 
PlanUtils.fastGetChildrenOutputs(childPlan.children()))
         );
-        return bindHavingByScopes(having, cascadesContext, defaultScope, 
backupScope);
+        return bindHavingByScopes(having, cascadesContext, childOutput, 
childChildrenOutput);
     }
 
     private LogicalHaving<Plan> bindHavingAggregate(
@@ -365,13 +367,115 @@ public class BindExpression implements 
AnalysisRuleFactory {
         Aggregate<Plan> aggregate = having.child();
         CascadesContext cascadesContext = ctx.cascadesContext;
 
-        // having(aggregate) should bind slot by aggregate.child.output first
-        Scope defaultScope = toScope(cascadesContext, 
PlanUtils.fastGetChildrenOutputs(aggregate.children()));
-        // then bind slot by aggregate.output
-        Supplier<Scope> backupScope = Suppliers.memoize(() ->
-                toScope(cascadesContext, aggregate.getOutput())
-        );
-        return bindHavingByScopes(ctx.root, ctx.cascadesContext, defaultScope, 
backupScope);
+        // keep same behavior as mysql
+        Supplier<CustomSlotBinderAnalyzer> bindByAggChild = 
Suppliers.memoize(() -> {
+            Scope aggChildOutputScope
+                    = toScope(cascadesContext, 
PlanUtils.fastGetChildrenOutputs(aggregate.children()));
+            return (analyzer, unboundSlot) -> 
analyzer.bindSlotByScope(unboundSlot, aggChildOutputScope);
+        });
+
+        Scope aggOutputScope = toScope(cascadesContext, aggregate.getOutput());
+        Supplier<CustomSlotBinderAnalyzer> 
bindByGroupByThenAggOutputThenAggChild = Suppliers.memoize(() -> {
+            List<Expression> groupByExprs = aggregate.getGroupByExpressions();
+            ImmutableList.Builder<Slot> groupBySlots
+                    = 
ImmutableList.builderWithExpectedSize(groupByExprs.size());
+            for (Expression groupBy : groupByExprs) {
+                if (groupBy instanceof Slot) {
+                    groupBySlots.add((Slot) groupBy);
+                }
+            }
+            Scope groupBySlotsScope = toScope(cascadesContext, 
groupBySlots.build());
+
+            Supplier<Pair<Scope, Scope>> separateAggOutputScopes = 
Suppliers.memoize(() -> {
+                ImmutableList.Builder<Slot> groupByOutputs = 
ImmutableList.builderWithExpectedSize(
+                        aggregate.getOutputExpressions().size());
+                ImmutableList.Builder<Slot> aggFunOutputs = 
ImmutableList.builderWithExpectedSize(
+                        aggregate.getOutputExpressions().size());
+                for (NamedExpression outputExpression : 
aggregate.getOutputExpressions()) {
+                    if 
(outputExpression.anyMatch(AggregateFunction.class::isInstance)) {
+                        aggFunOutputs.add(outputExpression.toSlot());
+                    } else {
+                        groupByOutputs.add(outputExpression.toSlot());
+                    }
+                }
+                Scope nonAggFunSlotsScope = toScope(cascadesContext, 
groupByOutputs.build());
+                Scope aggFuncSlotsScope = toScope(cascadesContext, 
aggFunOutputs.build());
+                return Pair.of(nonAggFunSlotsScope, aggFuncSlotsScope);
+            });
+
+            return (analyzer, unboundSlot) -> {
+                List<Slot> boundInGroupBy = 
analyzer.bindSlotByScope(unboundSlot, groupBySlotsScope);
+                if (boundInGroupBy.size() == 1) {
+                    return boundInGroupBy;
+                }
+
+                Pair<Scope, Scope> separateAggOutputScope = 
separateAggOutputScopes.get();
+                List<Slot> boundInNonAggFuncs = 
analyzer.bindSlotByScope(unboundSlot, separateAggOutputScope.first);
+                if (boundInNonAggFuncs.size() == 1) {
+                    return boundInNonAggFuncs;
+                }
+
+                List<Slot> boundInAggFuncs = 
analyzer.bindSlotByScope(unboundSlot, separateAggOutputScope.second);
+                if (boundInAggFuncs.size() == 1) {
+                    return boundInAggFuncs;
+                }
+
+                return bindByAggChild.get().bindSlot(analyzer, unboundSlot);
+            };
+        });
+
+        FunctionRegistry functionRegistry = 
cascadesContext.getConnectContext().getEnv().getFunctionRegistry();
+        ExpressionAnalyzer havingAnalyzer = new ExpressionAnalyzer(having, 
aggOutputScope, cascadesContext,
+                false, true) {
+            private boolean currentIsInAggregateFunction;
+
+            @Override
+            public Expression visitAggregateFunction(AggregateFunction 
aggregateFunction,
+                    ExpressionRewriteContext context) {
+                if (!currentIsInAggregateFunction) {
+                    currentIsInAggregateFunction = true;
+                    try {
+                        return super.visitAggregateFunction(aggregateFunction, 
context);
+                    } finally {
+                        currentIsInAggregateFunction = false;
+                    }
+                } else {
+                    return super.visitAggregateFunction(aggregateFunction, 
context);
+                }
+            }
+
+            @Override
+            public Expression visitUnboundFunction(UnboundFunction 
unboundFunction, ExpressionRewriteContext context) {
+                if (!currentIsInAggregateFunction && 
isAggregateFunction(unboundFunction, functionRegistry)) {
+                    currentIsInAggregateFunction = true;
+                    try {
+                        return super.visitUnboundFunction(unboundFunction, 
context);
+                    } finally {
+                        currentIsInAggregateFunction = false;
+                    }
+                } else {
+                    return super.visitUnboundFunction(unboundFunction, 
context);
+                }
+            }
+
+            @Override
+            protected List<? extends Expression> 
bindSlotByThisScope(UnboundSlot unboundSlot) {
+                if (currentIsInAggregateFunction) {
+                    return bindByAggChild.get().bindSlot(this, unboundSlot);
+                } else {
+                    return 
bindByGroupByThenAggOutputThenAggChild.get().bindSlot(this, unboundSlot);
+                }
+            }
+        };
+
+        Set<Expression> havingExprs = having.getConjuncts();
+        ImmutableSet.Builder<Expression> analyzedHaving = 
ImmutableSet.builderWithExpectedSize(havingExprs.size());
+        ExpressionRewriteContext rewriteContext = new 
ExpressionRewriteContext(cascadesContext);
+        for (Expression expression : havingExprs) {
+            analyzedHaving.add(havingAnalyzer.analyze(expression, 
rewriteContext));
+        }
+
+        return new LogicalHaving<>(analyzedHaving.build(), having.child());
     }
 
     private LogicalHaving<Plan> bindHavingByScopes(
@@ -764,6 +868,11 @@ public class BindExpression implements AnalysisRuleFactory 
{
         }
     }
 
+    private boolean isAggregateFunction(UnboundFunction unboundFunction, 
FunctionRegistry functionRegistry) {
+        return functionRegistry.isAggregateFunction(
+                    unboundFunction.getDbName(), unboundFunction.getName());
+    }
+
     private <E extends Expression> E checkBoundExceptLambda(E expression, Plan 
plan) {
         if (expression instanceof Lambda) {
             return expression;
@@ -797,6 +906,12 @@ public class BindExpression implements AnalysisRuleFactory 
{
             boolean enableExactMatch, boolean bindSlotInOuterScope) {
         List<Slot> childrenOutputs = 
PlanUtils.fastGetChildrenOutputs(children);
         Scope scope = toScope(cascadesContext, childrenOutputs);
+        return buildSimpleExprAnalyzer(currentPlan, cascadesContext, scope, 
enableExactMatch, bindSlotInOuterScope);
+    }
+
+    private SimpleExprAnalyzer buildSimpleExprAnalyzer(
+            Plan currentPlan, CascadesContext cascadesContext, Scope scope,
+            boolean enableExactMatch, boolean bindSlotInOuterScope) {
         ExpressionRewriteContext rewriteContext = new 
ExpressionRewriteContext(cascadesContext);
         ExpressionAnalyzer expressionAnalyzer = new 
ExpressionAnalyzer(currentPlan,
                 scope, cascadesContext, enableExactMatch, 
bindSlotInOuterScope);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java
index 3c514475eed..4ef642890ca 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java
@@ -56,6 +56,11 @@ public class AggCombinerFunctionBuilder extends 
FunctionBuilder {
         this.nestedBuilder = Objects.requireNonNull(nestedBuilder, 
"nestedBuilder can not be null");
     }
 
+    @Override
+    public Class<? extends BoundFunction> functionClass() {
+        return nestedBuilder.functionClass();
+    }
+
     @Override
     public boolean canApply(List<? extends Object> arguments) {
         if (combinatorSuffix.equals(STATE) || 
combinatorSuffix.equals(FOREACH)) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BuiltinFunctionBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BuiltinFunctionBuilder.java
index 74c4a918cf0..e2dab713332 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BuiltinFunctionBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BuiltinFunctionBuilder.java
@@ -42,13 +42,21 @@ public class BuiltinFunctionBuilder extends FunctionBuilder 
{
 
     // Concrete BoundFunction's constructor
     private final Constructor<BoundFunction> builderMethod;
+    private final Class<? extends BoundFunction> functionClass;
 
-    public BuiltinFunctionBuilder(Constructor<BoundFunction> builderMethod) {
+    public BuiltinFunctionBuilder(
+            Class<? extends BoundFunction> functionClass, 
Constructor<BoundFunction> builderMethod) {
+        this.functionClass = Objects.requireNonNull(functionClass, 
"functionClass can not be null");
         this.builderMethod = Objects.requireNonNull(builderMethod, 
"builderMethod can not be null");
         this.arity = builderMethod.getParameterCount();
         this.isVariableLength = arity > 0 && 
builderMethod.getParameterTypes()[arity - 1].isArray();
     }
 
+    @Override
+    public Class<? extends BoundFunction> functionClass() {
+        return functionClass;
+    }
+
     @Override
     public boolean canApply(List<? extends Object> arguments) {
         if (isVariableLength && arity > arguments.size() + 1) {
@@ -133,7 +141,7 @@ public class BuiltinFunctionBuilder extends FunctionBuilder 
{
                         + functionClass.getSimpleName());
         return Arrays.stream(functionClass.getConstructors())
                 .filter(constructor -> 
Modifier.isPublic(constructor.getModifiers()))
-                .map(constructor -> new 
BuiltinFunctionBuilder((Constructor<BoundFunction>) constructor))
+                .map(constructor -> new BuiltinFunctionBuilder(functionClass, 
(Constructor<BoundFunction>) constructor))
                 .collect(ImmutableList.toImmutableList());
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/FunctionBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/FunctionBuilder.java
index 0b3a50a239b..d1e69d3e307 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/FunctionBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/FunctionBuilder.java
@@ -27,6 +27,8 @@ import java.util.List;
  * This class used to build BoundFunction(Builtin or Combinator) by a list of 
Expressions.
  */
 public abstract class FunctionBuilder {
+    public abstract Class<? extends BoundFunction> functionClass();
+
     /** check whether arguments can apply to the constructor */
     public abstract boolean canApply(List<? extends Object> arguments);
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdfBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdfBuilder.java
index 197cb8b396d..733bd5fcae1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdfBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdfBuilder.java
@@ -50,6 +50,11 @@ public class AliasUdfBuilder extends UdfBuilder {
         return aliasUdf.getArgTypes();
     }
 
+    @Override
+    public Class<? extends BoundFunction> functionClass() {
+        return AliasUdf.class;
+    }
+
     @Override
     public boolean canApply(List<?> arguments) {
         if (arguments.size() != aliasUdf.arity()) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdafBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdafBuilder.java
index d0c0b067e94..89073b4eb77 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdafBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdafBuilder.java
@@ -49,6 +49,11 @@ public class JavaUdafBuilder extends UdfBuilder {
                 .collect(Collectors.toList())).get();
     }
 
+    @Override
+    public Class<? extends BoundFunction> functionClass() {
+        return JavaUdaf.class;
+    }
+
     @Override
     public boolean canApply(List<?> arguments) {
         if ((isVarArgs && arity > arguments.size() + 1) || (!isVarArgs && 
arguments.size() != arity)) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdfBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdfBuilder.java
index efbcbf9f483..a78f8cfe4b4 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdfBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdfBuilder.java
@@ -51,6 +51,11 @@ public class JavaUdfBuilder extends UdfBuilder {
                 .collect(Collectors.toList())).get();
     }
 
+    @Override
+    public Class<? extends BoundFunction> functionClass() {
+        return JavaUdf.class;
+    }
+
     @Override
     public boolean canApply(List<?> arguments) {
         if ((isVarArgs && arity > arguments.size() + 1) || (!isVarArgs && 
arguments.size() != arity)) {
diff --git a/regression-test/data/nereids_syntax_p0/bind_priority.out 
b/regression-test/data/nereids_syntax_p0/bind_priority.out
index 7cd1e3ea569..eb4002d960a 100644
--- a/regression-test/data/nereids_syntax_p0/bind_priority.out
+++ b/regression-test/data/nereids_syntax_p0/bind_priority.out
@@ -36,3 +36,43 @@ all  2
 4      5
 6      6
 
+-- !having_bind_child --
+1      10
+
+-- !having_bind_child2 --
+2      10
+
+-- !having_bind_child3 --
+2      10
+
+-- !having_bind_project --
+2      10
+
+-- !having_bind_project2 --
+
+-- !having_bind_project3 --
+
+-- !having_bind_project4 --
+2      11
+
+-- !having_bind_child4 --
+2      11
+
+-- !having_bind_child5 --
+2      11
+
+-- !having_bind_agg_fun --
+
+-- !having_bind_agg_fun --
+2      4
+3      3
+
+-- !having_bind_group_by --
+7      3
+
+-- !having_bind_group_by --
+7      3
+
+-- !having_bind_group_by --
+7      3
+
diff --git a/regression-test/suites/nereids_syntax_p0/bind_priority.groovy 
b/regression-test/suites/nereids_syntax_p0/bind_priority.groovy
index 072587ed28e..4e1740061b6 100644
--- a/regression-test/suites/nereids_syntax_p0/bind_priority.groovy
+++ b/regression-test/suites/nereids_syntax_p0/bind_priority.groovy
@@ -31,7 +31,7 @@ suite("bind_priority") {
     sql """
     insert into bind_priority_tbl values(1, 2),(3, 4)
     """
-    
+
     sql "SET enable_nereids_planner=true"
     sql "SET enable_fallback_to_original_planner=false"
 
@@ -100,17 +100,17 @@ suite("bind_priority") {
         );
     """
     sql "insert into bind_priority_tbl2 values(3,5),(2, 6),(1,4);"
-    
+
     qt_bind_order_to_project_alias """
             select bind_priority_tbl.b b, bind_priority_tbl2.b
-            from bind_priority_tbl join bind_priority_tbl2 on 
bind_priority_tbl.a=bind_priority_tbl2.a 
+            from bind_priority_tbl join bind_priority_tbl2 on 
bind_priority_tbl.a=bind_priority_tbl2.a
             order by b;
         """
 
 
     qt_bind_order_to_project_alias """
         select bind_priority_tbl.b, bind_priority_tbl2.b b
-        from bind_priority_tbl join bind_priority_tbl2 on 
bind_priority_tbl.a=bind_priority_tbl2.a 
+        from bind_priority_tbl join bind_priority_tbl2 on 
bind_priority_tbl.a=bind_priority_tbl2.a
         order by b;
         """
 
@@ -148,11 +148,144 @@ suite("bind_priority") {
               ) a
             ), tb2 as
             (
-              select * from tb1 
+              select * from tb1
             )
             select * from tb2 order by id;
             """)
 
         result([[1], [2], [3]])
     }
+
+    def testBindHaving = {
+        sql "drop table if exists test_bind_having_slots"
+
+        sql """create table test_bind_having_slots
+                (id int, age int)
+                distributed by hash(id)
+                properties('replication_num'='1');
+                """
+        sql "insert into test_bind_having_slots values(1, 10), (2, 20), (3, 
30);"
+
+        order_qt_having_bind_child """
+            select id, sum(age)
+            from test_bind_having_slots s
+            group by id
+            having id = 1; -- bind id from group by
+            """
+
+        order_qt_having_bind_child2 """
+            select id + 1 as id, sum(age)
+            from test_bind_having_slots s
+            group by id
+            having id = 1; -- bind id from group by
+            """
+
+        order_qt_having_bind_child3 """
+            select id + 1 as id, sum(age)
+            from test_bind_having_slots s
+            group by id
+            having id + 1 = 2; -- bind id from group by
+            """
+
+        order_qt_having_bind_project """
+            select id + 1 as id, sum(age)
+            from test_bind_having_slots s
+            group by id + 1
+            having id = 2; -- bind id from project
+            """
+
+        order_qt_having_bind_project2 """
+            select id + 1 as id, sum(age)
+            from test_bind_having_slots s
+            group by id + 1
+            having id + 1 = 2;  -- bind id from project
+            """
+
+        order_qt_having_bind_project3 """
+            select id + 1 as id, sum(age + 1) as age
+            from test_bind_having_slots s
+            group by id
+            having age = 10; -- bind age from project
+            """
+
+        order_qt_having_bind_project4 """
+            select id + 1 as id, sum(age + 1) as age
+            from test_bind_having_slots s
+            group by id
+            having age = 11; -- bind age from project
+            """
+
+        order_qt_having_bind_child4 """
+            select id + 1 as id, sum(age + 1) as age
+            from test_bind_having_slots s
+            group by id
+            having sum(age) = 10; -- bind age from s
+            """
+
+        order_qt_having_bind_child5 """
+            select id + 1 as id, sum(age + 1) as age
+            from test_bind_having_slots s
+            group by id
+            having sum(age + 1) = 11 -- bind age from s
+            """
+
+
+
+
+        sql "drop table if exists test_bind_having_slots2"
+        sql """create table test_bind_having_slots2
+                (id int)
+                distributed by hash(id)
+                properties('replication_num'='1');
+                """
+        sql "insert into test_bind_having_slots2 values(1), (2), (3), (2);"
+
+        order_qt_having_bind_agg_fun """
+               select id, abs(sum(id)) as id
+                from test_bind_having_slots2
+                group by id
+                having sum(id) + id  >= 7
+                """
+
+        order_qt_having_bind_agg_fun """
+               select id, abs(sum(id)) as id
+                from test_bind_having_slots2
+                group by id
+                having sum(id) + id  >= 6
+                """
+
+
+
+
+
+        sql "drop table if exists test_bind_having_slots3"
+
+        sql """CREATE TABLE `test_bind_having_slots3`(pk int, pk2 int)
+                    DUPLICATE KEY(`pk`)
+                    DISTRIBUTED BY HASH(`pk`) BUCKETS 10
+                    properties('replication_num'='1');
+                    """
+        sql "insert into test_bind_having_slots3 values(1, 1), (2, 2), (2, 2), 
(3, 3), (3, 3), (3, 3);"
+
+        order_qt_having_bind_group_by """
+                SELECT pk + 6 as ps, COUNT(pk )  *  3 as pk
+                FROM test_bind_having_slots3  tbl_alias1
+                GROUP by pk
+                HAVING  pk = 1
+                """
+
+        order_qt_having_bind_group_by """
+                SELECT pk + 6 as pk, COUNT(pk )  *  3 as pk
+                FROM test_bind_having_slots3  tbl_alias1
+                GROUP by pk + 6
+                HAVING  pk = 7
+                """
+
+        order_qt_having_bind_group_by """
+                SELECT pk + 6, COUNT(pk )  *  3 as pk
+                FROM test_bind_having_slots3  tbl_alias1
+                GROUP by pk + 6
+                HAVING  pk = 3
+                """
+    }()
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to