This is an automated email from the ASF dual-hosted git repository.
huajianlan 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 4cdf9f2a23 [Enhancement](Nereids) Refine nereids parser. (#11839)
4cdf9f2a23 is described below
commit 4cdf9f2a23d9a2660024e7b95996413a05343db1
Author: Shuo Wang <[email protected]>
AuthorDate: Wed Aug 17 20:17:26 2022 +0800
[Enhancement](Nereids) Refine nereids parser. (#11839)
1. Use ParseException in nereids parser.
2. Add check utils in the parser test.
3. Distinguish matchesFromRoot and matches when checking plans.
---
.../apache/doris/nereids/analyzer/UnboundSlot.java | 5 +
.../doris/nereids/parser/LogicalPlanBuilder.java | 51 ++++------
.../doris/nereids/parser/LimitClauseTest.java | 113 +++++++++------------
.../doris/nereids/parser/NereidsParserTest.java | 21 ++--
.../doris/nereids/parser/ParserTestBase.java | 35 +++++++
.../rules/rewrite/logical/ColumnPruningTest.java | 8 +-
.../trees/expressions/ExpressionParserTest.java | 38 ++++++-
.../doris/nereids/trees/expressions/ViewTest.java | 4 +-
.../doris/nereids/util/AnalyzeSubQueryTest.java | 6 +-
.../doris/nereids/util/ExceptionChecker.java | 48 +++++++++
.../doris/nereids/util/ExpressionParseChecker.java | 46 +++++++++
.../doris/nereids/util/GroupMatchingUtils.java | 43 ++++++++
.../apache/doris/nereids/util/ParseChecker.java | 29 ++++++
.../org/apache/doris/nereids/util/PlanChecker.java | 34 ++++++-
.../doris/nereids/util/PlanParseChecker.java | 64 ++++++++++++
15 files changed, 419 insertions(+), 126 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
index 60b662865b..045882396a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.util.Utils;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.List;
@@ -32,6 +33,10 @@ import java.util.Objects;
public class UnboundSlot extends Slot implements Unbound {
private final List<String> nameParts;
+ public UnboundSlot(String... nameParts) {
+ this(ImmutableList.copyOf(nameParts));
+ }
+
public UnboundSlot(List<String> nameParts) {
this.nameParts = Objects.requireNonNull(nameParts, "nameParts can not
be null");
}
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 215a993b42..9cb2b03d06 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
@@ -213,8 +213,12 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
@Override
public LogicalPlan
visitRegularQuerySpecification(RegularQuerySpecificationContext ctx) {
return ParserUtils.withOrigin(ctx, () -> {
- // TODO: support on row relation
- LogicalPlan relation =
withRelation(Optional.ofNullable(ctx.fromClause()));
+ // TODO: support one row relation
+ if (ctx.fromClause() == null) {
+ throw new ParseException("Unsupported one row relation", ctx);
+ }
+
+ LogicalPlan relation = visitFromClause(ctx.fromClause());
return withSelectQuerySpecification(
ctx, relation,
ctx.selectClause(),
@@ -326,8 +330,8 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
case DorisParser.NSEQ:
return new NullSafeEqual(left, right);
default:
- throw new IllegalStateException("Unsupported comparison
expression: "
- + operator.getSymbol().getText());
+ throw new ParseException("Unsupported comparison
expression: "
+ + operator.getSymbol().getText(), ctx);
}
});
}
@@ -356,7 +360,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
case DorisParser.OR:
return new Or(left, right);
default:
- throw new IllegalStateException("Unsupported logical
binary type: " + ctx.operator.getText());
+ throw new ParseException("Unsupported logical binary type:
" + ctx.operator.getText(), ctx);
}
});
}
@@ -387,7 +391,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
case DorisParser.MINUS:
//TODO: Add single operator subtraction
default:
- throw new IllegalStateException("Unsupported arithmetic
unary type: " + ctx.operator.getText());
+ throw new ParseException("Unsupported arithmetic unary
type: " + ctx.operator.getText(), ctx);
}
});
}
@@ -401,7 +405,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
int type = ctx.operator.getType();
if (left instanceof IntervalLiteral) {
if (type != DorisParser.PLUS) {
- throw new IllegalArgumentException("Only supported: " +
Operator.ADD);
+ throw new ParseException("Only supported: " +
Operator.ADD, ctx);
}
IntervalLiteral interval = (IntervalLiteral) left;
return new TimestampArithmetic(Operator.ADD, right,
interval.value(), interval.timeUnit(), true);
@@ -414,7 +418,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
} else if (type == DorisParser.MINUS) {
op = Operator.SUBTRACT;
} else {
- throw new IllegalArgumentException("Only supported: " +
Operator.ADD + " and " + Operator.SUBTRACT);
+ throw new ParseException("Only supported: " + Operator.ADD
+ " and " + Operator.SUBTRACT, ctx);
}
IntervalLiteral interval = (IntervalLiteral) right;
return new TimestampArithmetic(op, left, interval.value(),
interval.timeUnit(), false);
@@ -433,8 +437,8 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
case DorisParser.MINUS:
return new Subtract(left, right);
default:
- throw new IllegalStateException(
- "Unsupported arithmetic binary type: " +
ctx.operator.getText());
+ throw new ParseException(
+ "Unsupported arithmetic binary type: " +
ctx.operator.getText(), ctx);
}
});
});
@@ -537,7 +541,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
case "DATETIME":
return new DateTimeLiteral(value);
default:
- throw new IllegalStateException("Unsupported data type : " +
type);
+ throw new ParseException("Unsupported data type : " + type,
ctx);
}
}
@@ -552,7 +556,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
return new UnboundSlot(nameParts);
} else {
// todo: base is an expression, may be not a table name.
- throw new IllegalStateException("Unsupported dereference
expression: " + ctx.getText());
+ throw new ParseException("Unsupported dereference expression:
" + ctx.getText(), ctx);
}
});
}
@@ -614,7 +618,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
LogicalPlan right = plan(ctx.relationPrimary());
if (ctx.LATERAL() != null) {
if (!(right instanceof LogicalSubQueryAlias)) {
- throw new IllegalStateException("lateral join right table
should be sub-query");
+ throw new ParseException("lateral join right table should be
sub-query", ctx);
}
}
return right;
@@ -719,7 +723,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
private LogicalPlan withSort(LogicalPlan input,
Optional<SortClauseContext> sortCtx) {
return input.optionalMap(sortCtx, () -> {
List<OrderKey> orderKeys = visit(sortCtx.get().sortItem(),
OrderKey.class);
- return new LogicalSort(orderKeys, input);
+ return new LogicalSort<>(orderKeys, input);
});
}
@@ -732,7 +736,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
if (input instanceof LogicalSort) {
offset = Long.parseLong(offsetToken.getText());
} else {
- throw new IllegalStateException("OFFSET requires an ORDER
BY clause");
+ throw new ParseException("OFFSET requires an ORDER BY
clause", limitCtx.get());
}
}
return new LogicalLimit<>(limit, offset, input);
@@ -766,14 +770,6 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
});
}
- private LogicalPlan withRelation(Optional<FromClauseContext> ctx) {
- if (ctx.isPresent()) {
- return visitFromClause(ctx.get());
- } else {
- throw new IllegalStateException("Unsupported one row relation");
- }
- }
-
/**
* Join one more [[LogicalPlan]]s to the current logical plan.
*/
@@ -916,7 +912,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
}
break;
default:
- throw new IllegalStateException("Unsupported predicate
type: " + ctx.kind.getText());
+ throw new ParseException("Unsupported predicate type: " +
ctx.kind.getText(), ctx);
}
return ctx.NOT() != null ? new Not(outExpression) : outExpression;
});
@@ -925,14 +921,13 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
private List<NamedExpression>
getNamedExpressions(NamedExpressionSeqContext namedCtx) {
return ParserUtils.withOrigin(namedCtx, () -> {
List<Expression> expressions = visit(namedCtx.namedExpression(),
Expression.class);
- List<NamedExpression> namedExpressions =
expressions.stream().map(expression -> {
+ return expressions.stream().map(expression -> {
if (expression instanceof NamedExpression) {
return (NamedExpression) expression;
} else {
return new UnboundAlias(expression);
}
}).collect(ImmutableList.toImmutableList());
- return namedExpressions;
});
}
@@ -947,8 +942,6 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
}
public List<Expression> withInList(PredicateContext ctx) {
- List<Expression> expressions = ctx.expression().stream()
-
.map(this::getExpression).collect(ImmutableList.toImmutableList());
- return expressions;
+ return
ctx.expression().stream().map(this::getExpression).collect(ImmutableList.toImmutableList());
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/LimitClauseTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/LimitClauseTest.java
index 40ed482528..fe407eed1c 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/LimitClauseTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/LimitClauseTest.java
@@ -17,92 +17,73 @@
package org.apache.doris.nereids.parser;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
-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.exceptions.ParseException;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-public class LimitClauseTest {
+public class LimitClauseTest extends ParserTestBase {
@Test
public void testLimit() {
- NereidsParser nereidsParser = new NereidsParser();
- String sql = "SELECT b FROM test order by a limit 3 offset 100";
- LogicalPlan logicalPlan = nereidsParser.parseSingle(sql);
- Assertions.assertTrue(logicalPlan instanceof LogicalLimit);
- LogicalLimit limit = (LogicalLimit) logicalPlan;
- Assertions.assertEquals(3, limit.getLimit());
- Assertions.assertEquals(100, limit.getOffset());
- Assertions.assertEquals(1, limit.children().size());
- Assertions.assertTrue(limit.child(0) instanceof LogicalSort);
+ parsePlan("SELECT b FROM test order by a limit 3 offset 100")
+ .matchesFromRoot(
+ logicalLimit(
+ logicalSort()
+ ).when(limit -> limit.getLimit() == 3 &&
limit.getOffset() == 100)
+ );
- sql = "SELECT b FROM test order by a limit 100, 3";
- logicalPlan = nereidsParser.parseSingle(sql);
- Assertions.assertTrue(logicalPlan instanceof LogicalLimit);
- limit = (LogicalLimit) logicalPlan;
- Assertions.assertEquals(3, limit.getLimit());
- Assertions.assertEquals(100, limit.getOffset());
- Assertions.assertEquals(1, limit.children().size());
- Assertions.assertTrue(limit.child(0) instanceof LogicalSort);
+ parsePlan("SELECT b FROM test order by a limit 100, 3")
+ .matchesFromRoot(
+ logicalLimit(
+ logicalSort()
+ ).when(limit -> limit.getLimit() == 3 &&
limit.getOffset() == 100)
+ );
- sql = "SELECT b FROM test limit 3";
- logicalPlan = nereidsParser.parseSingle(sql);
- Assertions.assertTrue(logicalPlan instanceof LogicalLimit);
- limit = (LogicalLimit) logicalPlan;
- Assertions.assertEquals(3, limit.getLimit());
- Assertions.assertEquals(0, limit.getOffset());
- Assertions.assertEquals(1, limit.children().size());
- Assertions.assertTrue(limit.child(0) instanceof LogicalProject);
- sql = "SELECT b FROM test order by a limit 3";
- logicalPlan = nereidsParser.parseSingle(sql);
- Assertions.assertTrue(logicalPlan instanceof LogicalLimit);
- limit = (LogicalLimit) logicalPlan;
- Assertions.assertEquals(3, limit.getLimit());
- Assertions.assertEquals(0, limit.getOffset());
- Assertions.assertEquals(1, limit.children().size());
- Assertions.assertTrue(limit.child(0) instanceof LogicalSort);
+ parsePlan("SELECT b FROM test limit 3")
+ .matchesFromRoot(logicalLimit().when(limit -> limit.getLimit()
== 3 && limit.getOffset() == 0));
+
+
+ parsePlan("SELECT b FROM test order by a limit 3")
+ .matchesFromRoot(
+ logicalLimit(
+ logicalSort()
+ ).when(limit -> limit.getLimit() == 3 &&
limit.getOffset() == 0)
+ );
}
@Test
public void testLimitExceptionCase() {
- NereidsParser nereidsParser = new NereidsParser();
- IllegalStateException exception = Assertions.assertThrows(
- IllegalStateException.class,
- () -> {
- String sql = "SELECT b FROM test limit 3 offset 100";
- nereidsParser.parseSingle(sql);
- });
- Assertions.assertEquals("OFFSET requires an ORDER BY clause",
- exception.getMessage());
-
- exception = Assertions.assertThrows(
- IllegalStateException.class,
- () -> {
- String sql = "SELECT b FROM test limit 100, 3";
- nereidsParser.parseSingle(sql);
- });
- Assertions.assertEquals("OFFSET requires an ORDER BY clause",
- exception.getMessage());
+ parsePlan("SELECT b FROM test limit 3 offset 100")
+ .assertThrowsExactly(ParseException.class)
+ .assertMessageContains("\n"
+ + "OFFSET requires an ORDER BY clause(line 1, pos19)\n"
+ + "\n"
+ + "== SQL ==\n"
+ + "SELECT b FROM test limit 3 offset 100\n"
+ + "-------------------^^^");
+ parsePlan("SELECT b FROM test limit 100, 3")
+ .assertThrowsExactly(ParseException.class)
+ .assertMessageContains("\n"
+ + "OFFSET requires an ORDER BY clause(line 1, pos19)\n"
+ + "\n"
+ + "== SQL ==\n"
+ + "SELECT b FROM test limit 100, 3\n"
+ + "-------------------^^^");
}
@Test
public void testNoLimit() {
- NereidsParser nereidsParser = new NereidsParser();
- String sql = "select a from tbl order by x";
- LogicalPlan root = nereidsParser.parseSingle(sql);
- Assertions.assertTrue(root instanceof LogicalSort);
+ parsePlan("select a from tbl order by
x").matchesFromRoot(logicalSort());
}
-
@Test
public void testNoQueryOrganization() {
- NereidsParser nereidsParser = new NereidsParser();
- String sql = "select a from tbl";
- LogicalPlan root = nereidsParser.parseSingle(sql);
- Assertions.assertTrue(root instanceof LogicalProject);
+ parsePlan("select a from tbl")
+ .matchesFromRoot(
+ logicalProject(
+ unboundRelation()
+ )
+ );
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
index e5c1ffba03..208a45b09b 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
@@ -34,7 +34,7 @@ import org.junit.jupiter.api.Test;
import java.util.List;
-public class NereidsParserTest {
+public class NereidsParserTest extends ParserTestBase {
@Test
public void testParseMultiple() {
@@ -60,22 +60,17 @@ public class NereidsParserTest {
@Test
public void testErrorListener() {
- Exception exception = Assertions.assertThrows(ParseException.class, ()
-> {
- String sql = "select * from t1 where a = 1 illegal_symbol";
- NereidsParser nereidsParser = new NereidsParser();
- nereidsParser.parseSingle(sql);
- });
- Assertions.assertEquals("\nextraneous input 'illegal_symbol' expecting
{<EOF>, ';'}(line 1, pos29)\n",
- exception.getMessage());
+ parsePlan("select * from t1 where a = 1 illegal_symbol")
+ .assertThrowsExactly(ParseException.class)
+ .assertMessageEquals("\nextraneous input 'illegal_symbol'
expecting {<EOF>, ';'}(line 1, pos29)\n");
}
@Test
public void testPostProcessor() {
- String sql = "select `AD``D` from t1 where a = 1";
- NereidsParser nereidsParser = new NereidsParser();
- LogicalPlan logicalPlan = nereidsParser.parseSingle(sql);
- LogicalProject<Plan> logicalProject = (LogicalProject) logicalPlan;
- Assertions.assertEquals("AD`D",
logicalProject.getProjects().get(0).getName());
+ parsePlan("select `AD``D` from t1 where a = 1")
+ .matchesFromRoot(
+ logicalProject().when(p ->
"AD`D".equals(p.getProjects().get(0).getName()))
+ );
}
@Test
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/ParserTestBase.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/ParserTestBase.java
new file mode 100644
index 0000000000..cd35c5aef8
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/ParserTestBase.java
@@ -0,0 +1,35 @@
+// 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.
+
+package org.apache.doris.nereids.parser;
+
+import org.apache.doris.nereids.util.ExpressionParseChecker;
+import org.apache.doris.nereids.util.PatternMatchSupported;
+import org.apache.doris.nereids.util.PlanParseChecker;
+
+/**
+ * Base class to check SQL parsing result.
+ */
+public abstract class ParserTestBase implements PatternMatchSupported {
+ public PlanParseChecker parsePlan(String sql) {
+ return new PlanParseChecker(sql);
+ }
+
+ public ExpressionParseChecker parseExpression(String sql) {
+ return new ExpressionParseChecker(sql);
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ColumnPruningTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ColumnPruningTest.java
index 54b552a152..eb7777b368 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ColumnPruningTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ColumnPruningTest.java
@@ -60,7 +60,7 @@ public class ColumnPruningTest extends TestWithFeService
implements PatternMatch
.analyze("select id,name,grade from student left join score on
student.id = score.sid"
+ " where score.grade > 60")
.applyTopDown(new ColumnPruning())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalFilter(
logicalProject(
@@ -92,7 +92,7 @@ public class ColumnPruningTest extends TestWithFeService
implements PatternMatch
+ "from student left join score on student.id =
score.sid "
+ "where score.grade > 60")
.applyTopDown(new ColumnPruning())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalFilter(
logicalProject(
@@ -122,7 +122,7 @@ public class ColumnPruningTest extends TestWithFeService
implements PatternMatch
PlanChecker.from(connectContext)
.analyze("select id,name from student where age > 18")
.applyTopDown(new ColumnPruning())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalFilter(
logicalProject().when(p ->
getOutputQualifiedNames(p)
@@ -144,7 +144,7 @@ public class ColumnPruningTest extends TestWithFeService
implements PatternMatch
+ "on score.cid = course.cid "
+ "where score.grade > 60")
.applyTopDown(new ColumnPruning())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalFilter(
logicalProject(
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionParserTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionParserTest.java
index ece7f547e1..e6dd5de565 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionParserTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionParserTest.java
@@ -17,17 +17,34 @@
package org.apache.doris.nereids.trees.expressions;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.exceptions.ParseException;
import org.apache.doris.nereids.parser.NereidsParser;
+import org.apache.doris.nereids.parser.ParserTestBase;
import org.junit.jupiter.api.Test;
-public class ExpressionParserTest {
+public class ExpressionParserTest extends ParserTestBase {
private static final NereidsParser PARSER = new NereidsParser();
+ /**
+ * This method is deprecated.
+ * <p>
+ * Please use utility functions `parsePlan `in {@link ParserTestBase}
+ * to get {@link org.apache.doris.nereids.util.PlanParseChecker}.
+ */
+ @Deprecated
private void assertSql(String sql) {
PARSER.parseSingle(sql);
}
+ /**
+ * This method is deprecated.
+ * <p>
+ * Please use utility functions `parseExpression` in {@link ParserTestBase}
+ * to get {@link org.apache.doris.nereids.util.PlanParseChecker}.
+ */
+ @Deprecated
private void assertExpr(String expr) {
Expression expression = PARSER.parseExpression(expr);
System.out.println(expression.toSql());
@@ -41,12 +58,18 @@ public class ExpressionParserTest {
@Test
public void testExprBetweenPredicate() {
- String sql = "c BETWEEN a AND b";
- assertExpr(sql);
+ parseExpression("c BETWEEN a AND b")
+ .assertEquals(
+ new Between(
+ new UnboundSlot("c"),
+ new UnboundSlot("a"),
+ new UnboundSlot("b")
+ )
+ );
}
@Test
- public void testInPredicate() throws Exception {
+ public void testInPredicate() {
String in = "select * from test1 where d1 in (1, 2, 3)";
assertSql(in);
@@ -55,7 +78,7 @@ public class ExpressionParserTest {
}
@Test
- public void testSqlAnd() throws Exception {
+ public void testSqlAnd() {
String sql = "select * from test1 where a > 1 and b > 1";
assertSql(sql);
}
@@ -100,6 +123,11 @@ public class ExpressionParserTest {
String subtract = "3 - 2";
assertExpr(subtract);
+
+ parseExpression("3 += 2")
+ .assertThrowsExactly(ParseException.class)
+ .assertMessageContains("extraneous input '=' expecting {'(");
+
}
@Test
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ViewTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ViewTest.java
index 3391c14b2d..cdba3ef62d 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ViewTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ViewTest.java
@@ -114,7 +114,7 @@ public class ViewTest extends TestWithFeService implements
PatternMatchSupported
.analyze("SELECT * FROM V1")
.applyTopDown(new EliminateAliasNode())
.applyTopDown(new MergeConsecutiveProjects())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalOlapScan()
)
@@ -127,7 +127,7 @@ public class ViewTest extends TestWithFeService implements
PatternMatchSupported
.analyze("SELECT * FROM (SELECT * FROM V1 JOIN V2 ON V1.ID1 =
V2.ID2) X JOIN (SELECT * FROM V1 JOIN V3 ON V1.ID1 = V3.ID2) Y ON X.ID1 =
Y.ID3")
.applyTopDown(new EliminateAliasNode())
.applyTopDown(new MergeConsecutiveProjects())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalJoin(
logicalProject(
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/AnalyzeSubQueryTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/AnalyzeSubQueryTest.java
index 00e9de4266..7d5117d75b 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/AnalyzeSubQueryTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/AnalyzeSubQueryTest.java
@@ -103,7 +103,7 @@ public class AnalyzeSubQueryTest extends TestWithFeService
implements PatternMat
PlanChecker.from(connectContext)
.analyze(testSql.get(0))
.applyTopDown(new EliminateAliasNode())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalProject(
logicalOlapScan().when(o -> true)
@@ -123,7 +123,7 @@ public class AnalyzeSubQueryTest extends TestWithFeService
implements PatternMat
PlanChecker.from(connectContext)
.analyze(testSql.get(1))
.applyTopDown(new EliminateAliasNode())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalJoin(
logicalOlapScan(),
@@ -154,7 +154,7 @@ public class AnalyzeSubQueryTest extends TestWithFeService
implements PatternMat
PlanChecker.from(connectContext)
.analyze(testSql.get(5))
.applyTopDown(new EliminateAliasNode())
- .matches(
+ .matchesFromRoot(
logicalProject(
logicalJoin(
logicalOlapScan(),
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExceptionChecker.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExceptionChecker.java
new file mode 100644
index 0000000000..ebea43fee5
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExceptionChecker.java
@@ -0,0 +1,48 @@
+// 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.
+
+package org.apache.doris.nereids.util;
+
+import org.junit.jupiter.api.Assertions;
+
+import java.util.function.Function;
+
+/**
+ * Helper to check exception message.
+ */
+public class ExceptionChecker {
+ private final Throwable exception;
+
+ public ExceptionChecker(Throwable exception) {
+ this.exception = exception;
+ }
+
+ public ExceptionChecker assertMessageEquals(String message) {
+ Assertions.assertEquals(message, exception.getMessage());
+ return this;
+ }
+
+ public ExceptionChecker assertMessageContains(String context) {
+ Assertions.assertTrue(exception.getMessage().contains(context));
+ return this;
+ }
+
+ public ExceptionChecker assertWith(Function<Throwable, Boolean> asserter) {
+ Assertions.assertTrue(asserter.apply(exception));
+ return this;
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionParseChecker.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionParseChecker.java
new file mode 100644
index 0000000000..3ac6745547
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionParseChecker.java
@@ -0,0 +1,46 @@
+// 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.
+
+package org.apache.doris.nereids.util;
+
+import org.apache.doris.nereids.trees.expressions.Expression;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import org.junit.jupiter.api.Assertions;
+
+public class ExpressionParseChecker extends ParseChecker {
+ private final Supplier<Expression> parsedSupplier;
+
+ public ExpressionParseChecker(String sql) {
+ super(sql);
+ this.parsedSupplier = Suppliers.memoize(() ->
PARSER.parseExpression(sql));
+ }
+
+ public ExpressionParseChecker assertEquals(Expression expected) {
+ Assertions.assertEquals(expected, parsedSupplier.get());
+ return this;
+ }
+
+ public <T extends Throwable> ExceptionChecker assertThrows(Class<T>
expectedType) {
+ return new ExceptionChecker(Assertions.assertThrows(expectedType,
parsedSupplier::get));
+ }
+
+ public <T extends Throwable> ExceptionChecker assertThrowsExactly(Class<T>
expectedType) {
+ return new
ExceptionChecker(Assertions.assertThrowsExactly(expectedType,
parsedSupplier::get));
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/GroupMatchingUtils.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/GroupMatchingUtils.java
new file mode 100644
index 0000000000..5b6338aaf9
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/GroupMatchingUtils.java
@@ -0,0 +1,43 @@
+// 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.
+
+package org.apache.doris.nereids.util;
+
+import org.apache.doris.nereids.memo.Group;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.pattern.GroupExpressionMatching;
+import org.apache.doris.nereids.pattern.Pattern;
+import org.apache.doris.nereids.trees.plans.Plan;
+
+public class GroupMatchingUtils {
+
+ public static boolean topDownFindMatching(Group group, Pattern<? extends
Plan> pattern) {
+ GroupExpression logicalExpr = group.getLogicalExpression();
+ GroupExpressionMatching matchingResult = new
GroupExpressionMatching(pattern, logicalExpr);
+ if (matchingResult.iterator().hasNext()) {
+ return true;
+ } else {
+ for (Group childGroup : logicalExpr.children()) {
+ boolean checkResult = topDownFindMatching(childGroup, pattern);
+ if (checkResult) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ParseChecker.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ParseChecker.java
new file mode 100644
index 0000000000..432d55150f
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ParseChecker.java
@@ -0,0 +1,29 @@
+// 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.
+
+package org.apache.doris.nereids.util;
+
+import org.apache.doris.nereids.parser.NereidsParser;
+
+public abstract class ParseChecker {
+ protected static final NereidsParser PARSER = new NereidsParser();
+ protected final String sql;
+
+ public ParseChecker(String sql) {
+ this.sql = sql;
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
index fb8a9453cf..bd4b805540 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
@@ -25,8 +25,11 @@ import org.apache.doris.nereids.rules.RuleFactory;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.qe.ConnectContext;
+import com.google.common.base.Supplier;
import org.junit.jupiter.api.Assertions;
+import java.util.function.Consumer;
+
/**
* Utility to apply rules to plan and check output plan matches the expected
pattern.
*/
@@ -34,6 +37,7 @@ public class PlanChecker {
private ConnectContext connectContext;
private CascadesContext cascadesContext;
+ private Plan parsedPlan;
public PlanChecker(ConnectContext connectContext) {
this.connectContext = connectContext;
@@ -44,6 +48,18 @@ public class PlanChecker {
this.cascadesContext = cascadesContext;
}
+ public PlanChecker checkParse(String sql, Consumer<PlanParseChecker>
consumer) {
+ PlanParseChecker checker = new PlanParseChecker(sql);
+ consumer.accept(checker);
+ parsedPlan = checker.parsedSupplier.get();
+ return this;
+ }
+
+ public PlanChecker analyze() {
+ MemoTestUtils.createCascadesContext(connectContext, parsedPlan);
+ return this;
+ }
+
public PlanChecker analyze(String sql) {
this.cascadesContext =
MemoTestUtils.createCascadesContext(connectContext, sql);
this.cascadesContext.newAnalyzer().analyze();
@@ -66,12 +82,22 @@ public class PlanChecker {
return this;
}
+ public void matchesFromRoot(PatternDescriptor<? extends Plan> patternDesc)
{
+ Memo memo = cascadesContext.getMemo();
+ assertMatches(memo, () -> new
GroupExpressionMatching(patternDesc.pattern,
+ memo.getRoot().getLogicalExpression()).iterator().hasNext());
+ }
+
public void matches(PatternDescriptor<? extends Plan> patternDesc) {
Memo memo = cascadesContext.getMemo();
- GroupExpressionMatching matchResult = new
GroupExpressionMatching(patternDesc.pattern,
- memo.getRoot().getLogicalExpression());
- Assertions.assertTrue(matchResult.iterator().hasNext(), () ->
- "pattern not match, plan :\n" +
memo.getRoot().getLogicalExpression().getPlan().treeString() + "\n"
+ assertMatches(memo, () ->
GroupMatchingUtils.topDownFindMatching(memo.getRoot(), patternDesc.pattern));
+ }
+
+ private void assertMatches(Memo memo, Supplier<Boolean> asserter) {
+ Assertions.assertTrue(asserter.get(),
+ () -> "pattern not match, plan :\n"
+ +
memo.getRoot().getLogicalExpression().getPlan().treeString()
+ + "\n"
);
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanParseChecker.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanParseChecker.java
new file mode 100644
index 0000000000..55032a2864
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanParseChecker.java
@@ -0,0 +1,64 @@
+// 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.
+
+package org.apache.doris.nereids.util;
+
+import org.apache.doris.nereids.memo.Memo;
+import org.apache.doris.nereids.pattern.GroupExpressionMatching;
+import org.apache.doris.nereids.pattern.PatternDescriptor;
+import org.apache.doris.nereids.trees.plans.Plan;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import org.junit.jupiter.api.Assertions;
+
+public class PlanParseChecker extends ParseChecker {
+ final Supplier<Plan> parsedSupplier;
+
+ public PlanParseChecker(String sql) {
+ super(sql);
+ this.parsedSupplier = Suppliers.memoize(() -> PARSER.parseSingle(sql));
+ }
+
+ public PlanParseChecker matches(PatternDescriptor<? extends Plan>
patternDesc) {
+ assertMatches(() -> GroupMatchingUtils.topDownFindMatching(
+ new Memo(parsedSupplier.get()).getRoot(),
patternDesc.pattern));
+ return this;
+ }
+
+ public PlanParseChecker matchesFromRoot(PatternDescriptor<? extends Plan>
patternDesc) {
+ assertMatches(() -> new GroupExpressionMatching(patternDesc.pattern,
+ new
Memo(parsedSupplier.get()).getRoot().getLogicalExpression())
+ .iterator().hasNext());
+ return this;
+ }
+
+ public <T extends Throwable> ExceptionChecker assertThrows(Class<T>
expectedType) {
+ return new ExceptionChecker(Assertions.assertThrows(expectedType,
parsedSupplier::get));
+ }
+
+ public <T extends Throwable> ExceptionChecker assertThrowsExactly(Class<T>
expectedType) {
+ return new
ExceptionChecker(Assertions.assertThrowsExactly(expectedType,
parsedSupplier::get));
+ }
+
+ private void assertMatches(Supplier<Boolean> assertResultSupplier) {
+ Assertions.assertTrue(assertResultSupplier.get(),
+ () -> "pattern not match,\ninput SQL:\n" + sql
+ + "\n, parsed plan :\n" +
parsedSupplier.get().treeString() + "\n"
+ );
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]