From bfa7f8df6dff46a901c19fb45149c1aba399e2a8 Mon Sep 17 00:00:00 2001 From: morrySnow <101034200+morrySnow@users.noreply.github.com> Date: Fri, 28 Jul 2023 09:48:17 +0800 Subject: [PATCH] [fix](Nereids) parse logical binary stack overflow (#22308) 1. not use recursive parse to avoid stack overflow 2. To create a balanced tree instead of left deep tree TODO: add expr_depth_limit to Nereids' parser --- .../nereids/parser/LogicalPlanBuilder.java | 60 +++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index 696ac16f6f..08736548b4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -30,6 +30,7 @@ import org.apache.doris.nereids.DorisParser.AliasedQueryContext; import org.apache.doris.nereids.DorisParser.ArithmeticBinaryContext; import org.apache.doris.nereids.DorisParser.ArithmeticUnaryContext; import org.apache.doris.nereids.DorisParser.BitOperationContext; +import org.apache.doris.nereids.DorisParser.BooleanExpressionContext; import org.apache.doris.nereids.DorisParser.BooleanLiteralContext; import org.apache.doris.nereids.DorisParser.BracketJoinHintContext; import org.apache.doris.nereids.DorisParser.BracketRelationHintContext; @@ -744,21 +745,60 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor { @Override public Expression visitLogicalBinary(LogicalBinaryContext ctx) { return ParserUtils.withOrigin(ctx, () -> { - Expression left = getExpression(ctx.left); - Expression right = getExpression(ctx.right); + // Code block copy from Spark + // sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala - switch (ctx.operator.getType()) { - case DorisParser.LOGICALAND: - case DorisParser.AND: - return new And(left, right); - case DorisParser.OR: - return new Or(left, right); - default: - throw new ParseException("Unsupported logical binary type: " + ctx.operator.getText(), ctx); + // Collect all similar left hand contexts. + List contexts = Lists.newArrayList(ctx.right); + BooleanExpressionContext current = ctx.left; + while (true) { + if (current instanceof LogicalBinaryContext + && ((LogicalBinaryContext) current).operator.getType() == ctx.operator.getType()) { + contexts.add(((LogicalBinaryContext) current).right); + current = ((LogicalBinaryContext) current).left; + } else { + contexts.add(current); + break; + } } + // Reverse the contexts to have them in the same sequence as in the SQL statement & turn them + // into expressions. + Collections.reverse(contexts); + List expressions = contexts.stream().map(this::getExpression).collect(Collectors.toList()); + // Create a balanced tree. + return reduceToExpressionTree(0, expressions.size() - 1, expressions, ctx); }); } + private Expression expressionCombiner(Expression left, Expression right, LogicalBinaryContext ctx) { + switch (ctx.operator.getType()) { + case DorisParser.LOGICALAND: + case DorisParser.AND: + return new And(left, right); + case DorisParser.OR: + return new Or(left, right); + default: + throw new ParseException("Unsupported logical binary type: " + ctx.operator.getText(), ctx); + } + } + + private Expression reduceToExpressionTree(int low, int high, + List expressions, LogicalBinaryContext ctx) { + switch (high - low) { + case 0: + return expressions.get(low); + case 1: + return expressionCombiner(expressions.get(low), expressions.get(high), ctx); + default: + int mid = low + (high - low) / 2; + return expressionCombiner( + reduceToExpressionTree(low, mid, expressions, ctx), + reduceToExpressionTree(mid + 1, high, expressions, ctx), + ctx + ); + } + } + /** * Create a predicated expression. A predicated expression is a normal expression with a * predicate attached to it, for example: