diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java index a9536031d231cf..30440b83ffe6ea 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java @@ -24,6 +24,7 @@ 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.ArrayItemReference.ArrayItemSlot; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Slot; @@ -207,6 +208,11 @@ static class Resolver { } public void resolve(Expression expression, ResolvePlanType planType) { + // ArrayItemSlot represents a lambda-local variable, not an input from the aggregate's child. + // It is bound by its ArrayItemReference and should not participate in GROUP BY validation. + if (expression instanceof ArrayItemSlot) { + return; + } Pair, Boolean> result = lookUp(expression); Optional found = result.first; boolean isFoundInOutputExpressions = result.second; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java index 922c6ba700c5c7..d3fab2e4856c31 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java @@ -355,6 +355,27 @@ void testJoinWithHaving() { ).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot(), sumA2.toSlot())))); } + @Test + void testHavingLambdaLocalSlots() { + String mapSql = "SELECT a1, COUNT(*) AS n FROM t1 GROUP BY a1 " + + "HAVING map_exists((k, v) -> v > 1, map(1, COUNT(*)))"; + Assertions.assertNotNull(PlanChecker.from(connectContext).analyze(mapSql).getPlan()); + + String arraySql = "SELECT a1, COUNT(*) AS n FROM t1 GROUP BY a1 " + + "HAVING array_match_any(array_map(x -> x > 1, array(COUNT(*))))"; + Assertions.assertNotNull(PlanChecker.from(connectContext).analyze(arraySql).getPlan()); + + ExceptionChecker.expectThrowsWithMsg( + AnalysisException.class, + "HAVING expression 'a2' must appear in the GROUP BY clause" + + " or be used in an aggregate function.", + () -> PlanChecker.from(connectContext).analyze( + "SELECT a1, COUNT(*) AS n FROM t1 GROUP BY a1 " + + "HAVING array_match_any(array_map(x -> x > 1, " + + "array(COUNT(*) + a2)))" + )); + } + @Test void testInvalidHaving() { ExceptionChecker.expectThrowsWithMsg( diff --git a/regression-test/data/nereids_rules_p0/fill_up_missing_slots/test_having_with_aggregate_function.out b/regression-test/data/nereids_rules_p0/fill_up_missing_slots/test_having_with_aggregate_function.out index b9581decc50a38..fcf06b6b3bffa3 100644 --- a/regression-test/data/nereids_rules_p0/fill_up_missing_slots/test_having_with_aggregate_function.out +++ b/regression-test/data/nereids_rules_p0/fill_up_missing_slots/test_having_with_aggregate_function.out @@ -4,3 +4,9 @@ -- !having_project_2 -- +-- !having_map_lambda_local_slots -- +1 2 + +-- !having_array_lambda_local_slots -- +1 2 + diff --git a/regression-test/suites/nereids_rules_p0/fill_up_missing_slots/test_having_with_aggregate_function.groovy b/regression-test/suites/nereids_rules_p0/fill_up_missing_slots/test_having_with_aggregate_function.groovy index 8fc24380561c4b..04a2ec51039ebd 100644 --- a/regression-test/suites/nereids_rules_p0/fill_up_missing_slots/test_having_with_aggregate_function.groovy +++ b/regression-test/suites/nereids_rules_p0/fill_up_missing_slots/test_having_with_aggregate_function.groovy @@ -34,6 +34,36 @@ suite("test_having_project") { SELECT 1 AS c1 FROM t HAVING count(1) > 0 """ + qt_having_map_lambda_local_slots """ + SELECT id, COUNT(*) AS n + FROM (SELECT 1 id UNION ALL SELECT 1 id) input + GROUP BY id + HAVING map_exists((k, v) -> v > 1, map(1, COUNT(*))) + ORDER BY id + """ + + qt_having_array_lambda_local_slots """ + SELECT id, COUNT(*) AS n + FROM (SELECT 1 id UNION ALL SELECT 1 id) input + GROUP BY id + HAVING array_match_any(array_map(x -> x > 1, array(COUNT(*)))) + ORDER BY id + """ + + test { + sql """ + SELECT id, COUNT(*) AS n + FROM ( + SELECT 1 id, 1 AS ungrouped_col + UNION ALL + SELECT 1 id, 2 AS ungrouped_col + ) input + GROUP BY id + HAVING array_match_any(array_map(x -> x > 1, array(COUNT(*) + ungrouped_col))) + """ + exception "HAVING expression 'ungrouped_col' must appear in the GROUP BY clause or be used in an aggregate function" + } + test { sql "SELECT 1 AS c1 FROM t HAVING count(1) > 0 OR c1 IS NOT NULL" exception "HAVING expression 'c1' must appear in the GROUP BY clause or be used in an aggregate function"