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 5544b558650edb6..c55ed5957ba20c7 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 @@ -53,6 +53,8 @@ * Resolve having clause to the aggregation/repeat. * need Top to Down to traverse plan, * because we need to process FILL_UP_SORT_HAVING_AGGREGATE before FILL_UP_HAVING_AGGREGATE. + * be aware that when filling up the missing slots, we should exclude outer query's correlated slots. + * because these correlated slots belong to outer query, so should not try to find them in child node. */ public class FillUpMissingSlots implements AnalysisRuleFactory { @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubExprAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubExprAnalyzer.java index 3c9ee822ce85017..103b02cba50eaab 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubExprAnalyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubExprAnalyzer.java @@ -32,6 +32,7 @@ import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; import org.apache.doris.nereids.trees.plans.logical.LogicalLimit; @@ -39,6 +40,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; import org.apache.doris.nereids.trees.plans.logical.LogicalProject; import org.apache.doris.nereids.trees.plans.logical.LogicalSort; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; import org.apache.doris.nereids.util.ExpressionUtils; import com.google.common.collect.ImmutableList; @@ -122,13 +124,15 @@ public Expression visitScalarSubquery(ScalarSubquery scalar, T context) { AnalyzedResult analyzedResult = analyzeSubquery(scalar); boolean isCorrelated = analyzedResult.isCorrelated(); LogicalPlan analyzedSubqueryPlan = analyzedResult.logicalPlan; + checkOutputColumn(analyzedSubqueryPlan); if (isCorrelated) { if (analyzedSubqueryPlan instanceof LogicalLimit) { - if (ScalarSubquery.findTopLevelScalarAgg(analyzedResult.logicalPlan) == null) { + LogicalLimit limit = (LogicalLimit) analyzedSubqueryPlan; + if (limit.getOffset() == 0 && limit.getLimit() == 1) { + analyzedSubqueryPlan = (LogicalPlan) analyzedSubqueryPlan.child(0); + } else { throw new AnalysisException("limit is not supported in correlated subquery " + analyzedResult.getLogicalPlan()); - } else { - analyzedSubqueryPlan = (LogicalPlan) analyzedSubqueryPlan.child(0); } } if (analyzedSubqueryPlan instanceof LogicalSort) { @@ -136,13 +140,13 @@ public Expression visitScalarSubquery(ScalarSubquery scalar, T context) { analyzedResult = new AnalyzedResult((LogicalPlan) analyzedSubqueryPlan.child(0), analyzedResult.correlatedSlots); } + CorrelatedSlotsValidator validator = + new CorrelatedSlotsValidator(ImmutableSet.copyOf(analyzedResult.correlatedSlots)); + List nodeInfoList = new ArrayList<>(16); + Set topAgg = new HashSet<>(); + validateSubquery(analyzedResult.logicalPlan, validator, nodeInfoList, topAgg); } - checkOutputColumn(analyzedResult.getLogicalPlan()); - checkHasNoGroupBy(analyzedResult); - checkNoCorrelatedSlotsInAgg(analyzedResult); - checkNoCorrelatedSlotsUnderJoin(analyzedResult); - LogicalPlan subqueryPlan = analyzedResult.getLogicalPlan(); if (analyzedResult.getLogicalPlan() instanceof LogicalProject) { LogicalProject project = (LogicalProject) analyzedResult.getLogicalPlan(); if (project.child() instanceof LogicalOneRowRelation @@ -155,10 +159,6 @@ public Expression visitScalarSubquery(ScalarSubquery scalar, T context) { return alias.child(); } } else if (isCorrelated) { - if (ExpressionUtils.containsWindowExpression(project.getProjects())) { - throw new AnalysisException("window function is not supported in correlated subquery's output " - + analyzedResult.getLogicalPlan()); - } Set correlatedSlots = new HashSet<>(analyzedResult.getCorrelatedSlots()); if (!Sets.intersection(ExpressionUtils.getInputSlotSet(project.getProjects()), correlatedSlots).isEmpty()) { @@ -169,7 +169,7 @@ public Expression visitScalarSubquery(ScalarSubquery scalar, T context) { } } - return new ScalarSubquery(subqueryPlan, analyzedResult.getCorrelatedSlots()); + return new ScalarSubquery(analyzedResult.getLogicalPlan(), analyzedResult.getCorrelatedSlots()); } private void checkOutputColumn(LogicalPlan plan) { @@ -179,16 +179,6 @@ private void checkOutputColumn(LogicalPlan plan) { } } - private void checkHasNoGroupBy(AnalyzedResult analyzedResult) { - if (!analyzedResult.isCorrelated()) { - return; - } - if (analyzedResult.hasGroupBy()) { - throw new AnalysisException("Unsupported correlated subquery with grouping and/or aggregation " - + analyzedResult.getLogicalPlan()); - } - } - private void checkNoCorrelatedSlotsUnderAgg(AnalyzedResult analyzedResult) { if (analyzedResult.hasCorrelatedSlotsUnderAgg()) { throw new AnalysisException( @@ -197,22 +187,6 @@ private void checkNoCorrelatedSlotsUnderAgg(AnalyzedResult analyzedResult) { } } - private void checkNoCorrelatedSlotsUnderJoin(AnalyzedResult analyzedResult) { - if (analyzedResult.hasCorrelatedSlotsUnderJoin()) { - throw new AnalysisException( - String.format("Unsupported accesss outer join's column under join operator : %s", - analyzedResult.getCorrelatedSlots())); - } - } - - private void checkNoCorrelatedSlotsInAgg(AnalyzedResult analyzedResult) { - if (analyzedResult.hasCorrelatedSlotsInAgg()) { - throw new AnalysisException(String.format( - "outer query's column is not supported in subquery's aggregation operator : %s", - analyzedResult.getCorrelatedSlots())); - } - } - private void checkRootIsLimit(AnalyzedResult analyzedResult) { if (!analyzedResult.isCorrelated()) { return; @@ -271,61 +245,14 @@ public boolean isCorrelated() { return !correlatedSlots.isEmpty(); } - public boolean hasAgg() { - return logicalPlan.anyMatch(LogicalAggregate.class::isInstance); - } - - public boolean hasGroupBy() { - if (hasAgg()) { - return !((LogicalAggregate) - ((ImmutableSet) logicalPlan.collect(LogicalAggregate.class::isInstance)).asList().get(0)) - .getGroupByExpressions().isEmpty(); - } - return false; - } - public boolean hasCorrelatedSlotsUnderAgg() { return correlatedSlots.isEmpty() ? false - : findCorrelatedSlotsUnderNode(logicalPlan, + : hasCorrelatedSlotsUnderNode(logicalPlan, ImmutableSet.copyOf(correlatedSlots), LogicalAggregate.class); } - public boolean hasCorrelatedSlotsUnderJoin() { - return correlatedSlots.isEmpty() ? false - : findCorrelatedSlotsUnderNode(logicalPlan, - ImmutableSet.copyOf(correlatedSlots), LogicalJoin.class); - } - - public boolean hasCorrelatedSlotsInAgg() { - return correlatedSlots.isEmpty() ? false - : findCorrelatedSlotsInNode(logicalPlan, ImmutableSet.copyOf(correlatedSlots), - LogicalAggregate.class); - } - - private static boolean findCorrelatedSlotsInNode(Plan rootPlan, - ImmutableSet slots, Class clazz) { - ArrayDeque planQueue = new ArrayDeque<>(); - planQueue.add(rootPlan); - while (!planQueue.isEmpty()) { - Plan plan = planQueue.poll(); - if (plan.getClass().equals(clazz)) { - if (!Sets - .intersection(slots, - ExpressionUtils.getInputSlotSet(plan.getExpressions())) - .isEmpty()) { - return true; - } - } else { - for (Plan child : plan.children()) { - planQueue.add(child); - } - } - } - return false; - } - - private static boolean findCorrelatedSlotsUnderNode(Plan rootPlan, - ImmutableSet slots, Class clazz) { + private static boolean hasCorrelatedSlotsUnderNode(Plan rootPlan, + ImmutableSet slots, Class clazz) { ArrayDeque planQueue = new ArrayDeque<>(); planQueue.add(rootPlan); while (!planQueue.isEmpty()) { @@ -355,4 +282,171 @@ public boolean rootIsLimitZero() { return logicalPlan instanceof LogicalLimit && ((LogicalLimit) logicalPlan).getLimit() == 0; } } + + private static class PlanNodeCorrelatedInfo { + private PlanType planType; + private boolean containCorrelatedSlots; + private boolean hasGroupBy; + private LogicalAggregate aggregate; + + public PlanNodeCorrelatedInfo(PlanType planType, boolean containCorrelatedSlots) { + this(planType, containCorrelatedSlots, null); + } + + public PlanNodeCorrelatedInfo(PlanType planType, boolean containCorrelatedSlots, + LogicalAggregate aggregate) { + this.planType = planType; + this.containCorrelatedSlots = containCorrelatedSlots; + this.aggregate = aggregate; + this.hasGroupBy = aggregate != null ? !aggregate.getGroupByExpressions().isEmpty() : false; + } + } + + private static class CorrelatedSlotsValidator + extends PlanVisitor { + private final ImmutableSet correlatedSlots; + + public CorrelatedSlotsValidator(ImmutableSet correlatedSlots) { + this.correlatedSlots = correlatedSlots; + } + + @Override + public PlanNodeCorrelatedInfo visit(Plan plan, Void context) { + return new PlanNodeCorrelatedInfo(plan.getType(), findCorrelatedSlots(plan)); + } + + public PlanNodeCorrelatedInfo visitLogicalProject(LogicalProject plan, Void context) { + boolean containCorrelatedSlots = findCorrelatedSlots(plan); + if (containCorrelatedSlots) { + throw new AnalysisException( + String.format("access outer query's column in project is not supported", + correlatedSlots)); + } else { + PlanType planType = ExpressionUtils.containsWindowExpression( + ((LogicalProject) plan).getProjects()) ? PlanType.LOGICAL_WINDOW : plan.getType(); + return new PlanNodeCorrelatedInfo(planType, false); + } + } + + public PlanNodeCorrelatedInfo visitLogicalAggregate(LogicalAggregate plan, Void context) { + boolean containCorrelatedSlots = findCorrelatedSlots(plan); + if (containCorrelatedSlots) { + throw new AnalysisException( + String.format("access outer query's column in aggregate is not supported", + correlatedSlots, plan)); + } else { + return new PlanNodeCorrelatedInfo(plan.getType(), false, plan); + } + } + + public PlanNodeCorrelatedInfo visitLogicalJoin(LogicalJoin plan, Void context) { + boolean containCorrelatedSlots = findCorrelatedSlots(plan); + if (containCorrelatedSlots) { + throw new AnalysisException( + String.format("access outer query's column in join is not supported", + correlatedSlots, plan)); + } else { + return new PlanNodeCorrelatedInfo(plan.getType(), false); + } + } + + public PlanNodeCorrelatedInfo visitLogicalSort(LogicalSort plan, Void context) { + boolean containCorrelatedSlots = findCorrelatedSlots(plan); + if (containCorrelatedSlots) { + throw new AnalysisException( + String.format("access outer query's column in order by is not supported", + correlatedSlots, plan)); + } else { + return new PlanNodeCorrelatedInfo(plan.getType(), false); + } + } + + private boolean findCorrelatedSlots(Plan plan) { + return plan.getExpressions().stream().anyMatch(expression -> !Sets + .intersection(correlatedSlots, expression.getInputSlots()).isEmpty()); + } + } + + private LogicalAggregate validateNodeInfoList(List nodeInfoList) { + LogicalAggregate topAggregate = null; + int size = nodeInfoList.size(); + if (size > 0) { + List correlatedNodes = new ArrayList<>(4); + boolean checkNodeTypeAfterCorrelatedNode = false; + boolean checkAfterAggNode = false; + for (int i = size - 1; i >= 0; --i) { + PlanNodeCorrelatedInfo nodeInfo = nodeInfoList.get(i); + if (checkNodeTypeAfterCorrelatedNode) { + switch (nodeInfo.planType) { + case LOGICAL_LIMIT: + throw new AnalysisException( + "limit is not supported in correlated subquery"); + case LOGICAL_GENERATE: + throw new AnalysisException( + "access outer query's column before lateral view is not supported"); + case LOGICAL_AGGREGATE: + if (checkAfterAggNode) { + throw new AnalysisException( + "access outer query's column before two agg nodes is not supported"); + } + if (nodeInfo.hasGroupBy) { + // TODO support later + throw new AnalysisException( + "access outer query's column before agg with group by is not supported"); + } + checkAfterAggNode = true; + topAggregate = nodeInfo.aggregate; + break; + case LOGICAL_WINDOW: + throw new AnalysisException( + "access outer query's column before window function is not supported"); + case LOGICAL_JOIN: + throw new AnalysisException( + "access outer query's column before join is not supported"); + case LOGICAL_SORT: + // allow any sort node, the sort node will be removed by ELIMINATE_ORDER_BY_UNDER_SUBQUERY + break; + case LOGICAL_PROJECT: + // allow any project node + break; + case LOGICAL_SUBQUERY_ALIAS: + // allow any subquery alias + break; + default: + if (checkAfterAggNode) { + throw new AnalysisException( + "only project, sort and subquery alias node is allowed after agg node"); + } + break; + } + } + if (nodeInfo.containCorrelatedSlots) { + correlatedNodes.add(nodeInfo); + checkNodeTypeAfterCorrelatedNode = true; + } + } + + // only support 1 correlated node for now + if (correlatedNodes.size() > 1) { + throw new AnalysisException( + "access outer query's column in two places is not supported"); + } + } + return topAggregate; + } + + private void validateSubquery(Plan plan, CorrelatedSlotsValidator validator, + List nodeInfoList, Set topAgg) { + nodeInfoList.add(plan.accept(validator, null)); + for (Plan child : plan.children()) { + validateSubquery(child, validator, nodeInfoList, topAgg); + } + if (plan.children().isEmpty()) { + LogicalAggregate topAggNode = validateNodeInfoList(nodeInfoList); + if (topAggNode != null) { + topAgg.add(topAggNode); + } + } + nodeInfoList.remove(nodeInfoList.size() - 1); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java index b7bb8c88fea4d3a..178debe7db83a55 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java @@ -21,12 +21,15 @@ import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; -import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; +import org.apache.doris.nereids.trees.plans.logical.LogicalProject; +import org.apache.doris.nereids.trees.plans.logical.LogicalSort; +import org.apache.doris.nereids.trees.plans.logical.LogicalSubQueryAlias; import org.apache.doris.nereids.types.DataType; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import java.util.List; import java.util.Objects; @@ -51,7 +54,7 @@ public ScalarSubquery(LogicalPlan subquery, List correlateSlots, Optional< super(Objects.requireNonNull(subquery, "subquery can not be null"), Objects.requireNonNull(correlateSlots, "correlateSlots can not be null"), typeCoercionExpr); - hasTopLevelScalarAgg = findTopLevelScalarAgg(subquery) != null; + hasTopLevelScalarAgg = findTopLevelScalarAgg(subquery, ImmutableSet.copyOf(correlateSlots)) != null; } public boolean hasTopLevelScalarAgg() { @@ -62,11 +65,12 @@ public boolean hasTopLevelScalarAgg() { * getTopLevelScalarAggFunction */ public Optional getTopLevelScalarAggFunction() { - Plan plan = findTopLevelScalarAgg(queryPlan); + Plan plan = findTopLevelScalarAgg(queryPlan, ImmutableSet.copyOf(correlateSlots)); if (plan != null) { LogicalAggregate aggregate = (LogicalAggregate) plan; Preconditions.checkState(aggregate.getAggregateFunctions().size() == 1, - "agg is not a scalar agg, it's output is ", aggregate.getOutputExpressions()); + "in scalar subquery, should only return 1 column 1 row, " + + "but we found multiple columns ", aggregate.getOutputExpressions()); return Optional.of((NamedExpression) aggregate.getOutputExpressions().get(0)); } else { return Optional.empty(); @@ -107,25 +111,28 @@ public ScalarSubquery withSubquery(LogicalPlan subquery) { } /** - * findTopLevelScalarAgg - */ - public static Plan findTopLevelScalarAgg(Plan plan) { + * for correlated subquery, we define top level scalar agg as if it meets the both 2 conditions: + * 1. The agg or its child contains correlated slots + * 2. only project, sort and subquery alias node can be agg's parent + */ + public static Plan findTopLevelScalarAgg(Plan plan, ImmutableSet slots) { if (plan instanceof LogicalAggregate) { - if (((LogicalAggregate) plan).getGroupByExpressions().isEmpty()) { + if (((LogicalAggregate) plan).getGroupByExpressions().isEmpty() && plan.containsSlots(slots)) { return plan; } else { return null; } - } else if (plan instanceof LogicalJoin) { - return null; - } else { + } else if (plan instanceof LogicalProject || plan instanceof LogicalSubQueryAlias + || plan instanceof LogicalSort) { for (Plan child : plan.children()) { - Plan result = findTopLevelScalarAgg(child); + Plan result = findTopLevelScalarAgg(child, slots); if (result != null) { return result; } } return null; + } else { + return null; } } } diff --git a/regression-test/data/nereids_p0/subquery/correlated_scalar_subquery.out b/regression-test/data/nereids_p0/subquery/correlated_scalar_subquery.out index 4a38b67f59d68d4..9414a5c9f61bcae 100644 --- a/regression-test/data/nereids_p0/subquery/correlated_scalar_subquery.out +++ b/regression-test/data/nereids_p0/subquery/correlated_scalar_subquery.out @@ -7,6 +7,73 @@ 1 1 +-- !select_where3 -- + +-- !select_where4 -- +1 +1 +2 +2 +3 + +-- !select_where5 -- + +-- !select_where6 -- +2 +2 + +-- !select_where7 -- +\N +\N +2 +2 +3 +3 +20 +22 +24 + +-- !select_where8 -- +\N +\N +1 +1 +2 +2 +3 +3 +20 +22 +24 + +-- !select_where9 -- +\N +\N +1 +1 +2 +2 +3 +3 +20 +22 +24 + +-- !select_where10 -- +\N +\N +1 +1 +2 +2 +3 +3 +20 +22 +24 + +-- !select_where11 -- + -- !select_project1 -- \N \N 1 \N diff --git a/regression-test/suites/nereids_p0/subquery/correlated_scalar_subquery.groovy b/regression-test/suites/nereids_p0/subquery/correlated_scalar_subquery.groovy index 05d6c3baedb5f2c..ff32078a8a24bac 100644 --- a/regression-test/suites/nereids_p0/subquery/correlated_scalar_subquery.groovy +++ b/regression-test/suites/nereids_p0/subquery/correlated_scalar_subquery.groovy @@ -74,8 +74,17 @@ suite("correlated_scalar_subquery") { insert into correlated_scalar_t3 values (1,null),(null,1),(1,9), (1,8), (null,7), (2,6), (3,7), (3,9),(null,null),(5,1); """ - qt_select_where1 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select c1 from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 and correlated_scalar_t2.c2 < 4);""" - qt_select_where2 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select any_value(c1) from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 and correlated_scalar_t2.c2 < 4);""" + qt_select_where1 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select c1 from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 and correlated_scalar_t2.c2 < 4) order by c1;""" + qt_select_where2 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select any_value(c1) from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 and correlated_scalar_t2.c2 < 4) order by c1;""" + qt_select_where3 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select e1 from (select 1 k1) as t lateral view explode_numbers(5) tmp1 as e1 where correlated_scalar_t1.c1 = e1 and correlated_scalar_t1.c2 = e1 order by e1) order by c1;""" + qt_select_where4 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select col from (select c1 col from correlated_scalar_t2 group by c1 ) tt where correlated_scalar_t1.c1 = tt.col) order by c1;""" + qt_select_where5 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select col from (select max(c1) over() col from correlated_scalar_t2 ) tt where correlated_scalar_t1.c1 = tt.col) order by c1;""" + qt_select_where6 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select min(correlated_scalar_t2.c1) from correlated_scalar_t2 join correlated_scalar_t3 on correlated_scalar_t2.c1 = correlated_scalar_t3.c2 where correlated_scalar_t2.c2 = correlated_scalar_t1.c1) order by c1;""" + qt_select_where7 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select x from (select count(c1)x from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 order by count(c1))tt) order by c1;""" + qt_select_where8 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select count(col) from (select c1 col from correlated_scalar_t2 group by c1 ) tt where correlated_scalar_t1.c1 = tt.col) order by c1;""" + qt_select_where9 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select count(col) from (select max(c1) over() col from correlated_scalar_t2) tt where correlated_scalar_t1.c1 = tt.col) order by c1;""" + qt_select_where10 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select count(correlated_scalar_t2.c1) from correlated_scalar_t2 join correlated_scalar_t3 on correlated_scalar_t2.c1 = correlated_scalar_t3.c2 where correlated_scalar_t2.c2 = correlated_scalar_t1.c1) order by c1;""" + qt_select_where11 """select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select count(c1) from correlated_scalar_t2 having correlated_scalar_t1.c1 = count(c1)) order by c1;""" qt_select_project1 """select c1, sum((select c1 from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 and correlated_scalar_t2.c2 > 7)) from correlated_scalar_t1 group by c1 order by c1;""" qt_select_project2 """select c1, sum((select any_value(c1) from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 and correlated_scalar_t2.c2 > 7)) from correlated_scalar_t1 group by c1 order by c1;""" @@ -109,29 +118,106 @@ suite("correlated_scalar_subquery") { test { sql """ - select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select correlated_scalar_t1.c1 from correlated_scalar_t2 where correlated_scalar_t2.c2 < 4 and correlated_scalar_t1.c1 = correlated_scalar_t2.c1 ); + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select c1 from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 limit 1); """ - exception "outer query's column is not supported in subquery's output" + exception "limit is not supported in correlated subquery" } test { sql """ - select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select any_value(correlated_scalar_t2.c1 +correlated_scalar_t1.c1 ) from correlated_scalar_t2 where correlated_scalar_t2.c2 < 4 and correlated_scalar_t1.c1 = correlated_scalar_t2.c1); + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select e1 from (select k1 from (select 1 k1 ) as t where correlated_scalar_t1.c1 = k1 ) tt lateral view explode_numbers(5) tmp1 as e1 order by e1); """ - exception "outer query's column is not supported in subquery's aggregation operator" + exception "access outer query's column before lateral view is not supported" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select e1 from (select 1 k1) as t lateral view explode_numbers(5) tmp1 as e1 where correlated_scalar_t1.c1 = e1 having correlated_scalar_t1.c2 = e1 order by e1); + """ + exception "access outer query's column in two places is not supported" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select e1 from (select 1 k1) as t lateral view explode_numbers(5) tmp1 as e1 where correlated_scalar_t1.c1 = e1 or correlated_scalar_t1.c2 = e1 order by e1); + """ + exception "Unsupported correlated subquery with correlated predicate" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select correlated_scalar_t1.c1 from correlated_scalar_t2); + """ + exception "access outer query's column in project is not supported" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select max(c1) over() from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 order by c1); + """ + exception "access outer query's column before window function is not supported" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select max(correlated_scalar_t1.c1) over() from correlated_scalar_t2 order by c1); + """ + exception "access outer query's column in project is not supported" } test { sql """ select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select min(correlated_scalar_t2.c1) from correlated_scalar_t2 join (select correlated_scalar_t3.c1 from correlated_scalar_t3 where correlated_scalar_t1.c1 = correlated_scalar_t3.c2 ) tt on correlated_scalar_t2.c2 > tt.c1); - """ - exception "Unsupported accesss outer join's column under join operator" + """ + exception "access outer query's column before join is not supported" } test { sql """ - select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select min(correlated_scalar_t2.c1) from correlated_scalar_t2 join correlated_scalar_t3 on correlated_scalar_t1.c1 = correlated_scalar_t3.c2 ); - """ - exception "Unsupported accesss outer join's column under join operator" + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select correlated_scalar_t2.c1 from correlated_scalar_t2 join correlated_scalar_t3 on correlated_scalar_t1.c1 = correlated_scalar_t3.c2 ); + """ + exception "access outer query's column in join is not supported" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select c1 from correlated_scalar_t2 order by correlated_scalar_t1.c1); + """ + exception "Unknown column" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select c1 from (select c1 from correlated_scalar_t2 order by correlated_scalar_t1.c1)tt ); + """ + exception "Unknown column" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select count(c1) from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 group by c2); + """ + exception "access outer query's column before agg with group by is not supported" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select count(c1) from correlated_scalar_t2 where correlated_scalar_t1.c1 = correlated_scalar_t2.c1 having count(c1) > 10); + """ + exception "only project, sort and subquery alias node is allowed after agg node" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select count(correlated_scalar_t1.c1) from correlated_scalar_t2); + """ + exception "access outer query's column in aggregate is not supported" + } + + test { + sql """ + select c1 from correlated_scalar_t1 where correlated_scalar_t1.c2 > (select count(col) from (select max(c1) col from correlated_scalar_t2 where correlated_scalar_t1.c1 = c1) tt ); + """ + exception "access outer query's column before two agg nodes is not supported" } } \ No newline at end of file