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

jackietien pushed a commit to branch ty/TableModelGrammar
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/ty/TableModelGrammar by this 
push:
     new 0a7255d1072 partial parser
0a7255d1072 is described below

commit 0a7255d1072a42e13e98f7312ff4b8db9f81b2be
Author: JackieTien97 <[email protected]>
AuthorDate: Thu Feb 22 18:12:10 2024 +0800

    partial parser
---
 iotdb-core/datanode/pom.xml                        |   5 +
 .../db/relational/grammar/sql/RelationalSql.g4     |   9 +
 .../iotdb/db/relational/sql/parser/AstBuilder.java | 469 +++++++++++----------
 .../iotdb/db/relational/sql/parser/SqlParser.java  | 233 +++++++++-
 .../iotdb/db/relational/sql/util/AstUtil.java      |  99 +++++
 .../iotdb/db/relational/sql/util/QueryUtil.java    | 208 +++++++++
 .../relational/sql/util/ReservedIdentifiers.java   |  48 +++
 7 files changed, 845 insertions(+), 226 deletions(-)

diff --git a/iotdb-core/datanode/pom.xml b/iotdb-core/datanode/pom.xml
index 66b05aef12e..290594ca011 100644
--- a/iotdb-core/datanode/pom.xml
+++ b/iotdb-core/datanode/pom.xml
@@ -84,6 +84,11 @@
             <artifactId>iotdb-relational-parser</artifactId>
             <version>1.3.1-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.iotdb</groupId>
+            <artifactId>iotdb-relational-parser</artifactId>
+            <version>1.3.1-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.iotdb</groupId>
             <artifactId>iotdb-thrift-commons</artifactId>
diff --git 
a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
 
b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
index d256df02c40..84e4fea8301 100644
--- 
a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
+++ 
b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
@@ -29,6 +29,15 @@ singleStatement
     : statement EOF
     ;
 
+
+standaloneExpression
+    : expression EOF
+    ;
+
+standaloneType
+    : type EOF
+    ;
+
 statement
     // Query Statement
     : queryStatement
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/AstBuilder.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/AstBuilder.java
index eeede0436b1..65ad32a3ef2 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/AstBuilder.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/AstBuilder.java
@@ -29,6 +29,8 @@ import org.apache.iotdb.db.relational.sql.tree.AllRows;
 import org.apache.iotdb.db.relational.sql.tree.ArithmeticBinaryExpression;
 import org.apache.iotdb.db.relational.sql.tree.ArithmeticUnaryExpression;
 import org.apache.iotdb.db.relational.sql.tree.BetweenPredicate;
+import org.apache.iotdb.db.relational.sql.tree.BinaryLiteral;
+import org.apache.iotdb.db.relational.sql.tree.BooleanLiteral;
 import org.apache.iotdb.db.relational.sql.tree.Cast;
 import org.apache.iotdb.db.relational.sql.tree.CoalesceExpression;
 import org.apache.iotdb.db.relational.sql.tree.ColumnDefinition;
@@ -41,9 +43,11 @@ import org.apache.iotdb.db.relational.sql.tree.CurrentTime;
 import org.apache.iotdb.db.relational.sql.tree.CurrentUser;
 import org.apache.iotdb.db.relational.sql.tree.DataType;
 import org.apache.iotdb.db.relational.sql.tree.DataTypeParameter;
+import org.apache.iotdb.db.relational.sql.tree.DecimalLiteral;
 import org.apache.iotdb.db.relational.sql.tree.Delete;
 import org.apache.iotdb.db.relational.sql.tree.DereferenceExpression;
 import org.apache.iotdb.db.relational.sql.tree.DescribeTable;
+import org.apache.iotdb.db.relational.sql.tree.DoubleLiteral;
 import org.apache.iotdb.db.relational.sql.tree.DropColumn;
 import org.apache.iotdb.db.relational.sql.tree.DropDB;
 import org.apache.iotdb.db.relational.sql.tree.DropIndex;
@@ -70,6 +74,7 @@ import org.apache.iotdb.db.relational.sql.tree.JoinOn;
 import org.apache.iotdb.db.relational.sql.tree.JoinUsing;
 import org.apache.iotdb.db.relational.sql.tree.LikePredicate;
 import org.apache.iotdb.db.relational.sql.tree.Limit;
+import org.apache.iotdb.db.relational.sql.tree.LogicalExpression;
 import org.apache.iotdb.db.relational.sql.tree.LongLiteral;
 import org.apache.iotdb.db.relational.sql.tree.NaturalJoin;
 import org.apache.iotdb.db.relational.sql.tree.Node;
@@ -77,6 +82,7 @@ import org.apache.iotdb.db.relational.sql.tree.NodeLocation;
 import org.apache.iotdb.db.relational.sql.tree.NotExpression;
 import org.apache.iotdb.db.relational.sql.tree.NullIfExpression;
 import org.apache.iotdb.db.relational.sql.tree.NullLiteral;
+import org.apache.iotdb.db.relational.sql.tree.NumericParameter;
 import org.apache.iotdb.db.relational.sql.tree.Offset;
 import org.apache.iotdb.db.relational.sql.tree.OrderBy;
 import org.apache.iotdb.db.relational.sql.tree.Parameter;
@@ -105,10 +111,12 @@ import 
org.apache.iotdb.db.relational.sql.tree.SubqueryExpression;
 import org.apache.iotdb.db.relational.sql.tree.Table;
 import org.apache.iotdb.db.relational.sql.tree.TableSubquery;
 import org.apache.iotdb.db.relational.sql.tree.Trim;
+import org.apache.iotdb.db.relational.sql.tree.TypeParameter;
 import org.apache.iotdb.db.relational.sql.tree.Union;
 import org.apache.iotdb.db.relational.sql.tree.Update;
 import org.apache.iotdb.db.relational.sql.tree.UpdateAssignment;
 import org.apache.iotdb.db.relational.sql.tree.Use;
+import org.apache.iotdb.db.relational.sql.tree.Values;
 import org.apache.iotdb.db.relational.sql.tree.WhenClause;
 import org.apache.iotdb.db.relational.sql.tree.With;
 import org.apache.iotdb.db.relational.sql.tree.WithQuery;
@@ -121,9 +129,13 @@ import org.antlr.v4.runtime.tree.TerminalNode;
 
 import javax.annotation.Nullable;
 
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
+import java.util.function.Function;
 
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static java.util.Objects.requireNonNull;
@@ -147,6 +159,16 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
     return visit(ctx.statement());
   }
 
+  @Override
+  public Node 
visitStandaloneExpression(RelationalSqlParser.StandaloneExpressionContext 
context) {
+    return visit(context.expression());
+  }
+
+  @Override
+  public Node visitStandaloneType(RelationalSqlParser.StandaloneTypeContext 
context) {
+    return visit(context.type());
+  }
+
   // ******************* statements **********************
   @Override
   public Node 
visitUseDatabaseStatement(RelationalSqlParser.UseDatabaseStatementContext ctx) {
@@ -326,6 +348,12 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
     }
   }
 
+  @Override
+  public Node 
visitUpdateAssignment(RelationalSqlParser.UpdateAssignmentContext ctx) {
+    return new UpdateAssignment(
+        (Identifier) visit(ctx.identifier()), (Expression) 
visit(ctx.expression()));
+  }
+
   @Override
   public Node 
visitCreateFunctionStatement(RelationalSqlParser.CreateFunctionStatementContext 
ctx) {
     return super.visitCreateFunctionStatement(ctx);
@@ -616,6 +644,32 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
         Optional.empty());
   }
 
+  @Override
+  public Node visitSelectSingle(RelationalSqlParser.SelectSingleContext ctx) {
+    if (ctx.identifier() != null) {
+      return new SingleColumn(
+          getLocation(ctx),
+          (Expression) visit(ctx.expression()),
+          (Identifier) visit(ctx.identifier()));
+    } else {
+      return new SingleColumn(getLocation(ctx), (Expression) 
visit(ctx.expression()));
+    }
+  }
+
+  @Override
+  public Node visitSelectAll(RelationalSqlParser.SelectAllContext ctx) {
+    List<Identifier> aliases = ImmutableList.of();
+    if (ctx.columnAliases() != null) {
+      aliases = visit(ctx.columnAliases().identifier(), Identifier.class);
+    }
+
+    if (ctx.primaryExpression() != null) {
+      return new AllColumns(getLocation(ctx), (Expression) 
visit(ctx.primaryExpression()), aliases);
+    } else {
+      return new AllColumns(getLocation(ctx), aliases);
+    }
+  }
+
   @Override
   public Node visitGroupBy(RelationalSqlParser.GroupByContext ctx) {
     return new GroupBy(
@@ -700,12 +754,12 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
 
   @Override
   public Node visitInlineTable(RelationalSqlParser.InlineTableContext ctx) {
-    return super.visitInlineTable(ctx);
+    return new Values(getLocation(ctx), visit(ctx.expression(), 
Expression.class));
   }
 
   @Override
   public Node visitSubquery(RelationalSqlParser.SubqueryContext ctx) {
-    return super.visitSubquery(ctx);
+    return new TableSubquery(getLocation(ctx), (Query) 
visit(ctx.queryNoWith()));
   }
 
   @Override
@@ -721,6 +775,19 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
             .orElse(SortItem.NullOrdering.UNDEFINED));
   }
 
+  @Override
+  public Node 
visitUnquotedIdentifier(RelationalSqlParser.UnquotedIdentifierContext ctx) {
+    return new Identifier(getLocation(ctx), ctx.getText(), false);
+  }
+
+  @Override
+  public Node 
visitQuotedIdentifier(RelationalSqlParser.QuotedIdentifierContext ctx) {
+    String token = ctx.getText();
+    String identifier = token.substring(1, token.length() - 1).replace("\"\"", 
"\"");
+
+    return new Identifier(getLocation(ctx), identifier, true);
+  }
+
   @Override
   public Node visitTimenGrouping(RelationalSqlParser.TimenGroupingContext ctx) 
{
     return super.visitTimenGrouping(ctx);
@@ -776,30 +843,69 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
     return super.visitKeepExpression(ctx);
   }
 
+  // ***************** boolean expressions ******************
   @Override
-  public Node visitSelectSingle(RelationalSqlParser.SelectSingleContext ctx) {
-    if (ctx.identifier() != null) {
-      return new SingleColumn(
-          getLocation(ctx),
-          (Expression) visit(ctx.expression()),
-          (Identifier) visit(ctx.identifier()));
-    } else {
-      return new SingleColumn(getLocation(ctx), (Expression) 
visit(ctx.expression()));
-    }
+  public Node visitLogicalNot(RelationalSqlParser.LogicalNotContext ctx) {
+    return new NotExpression(getLocation(ctx), (Expression) 
visit(ctx.booleanExpression()));
   }
 
   @Override
-  public Node visitSelectAll(RelationalSqlParser.SelectAllContext ctx) {
-    List<Identifier> aliases = ImmutableList.of();
-    if (ctx.columnAliases() != null) {
-      aliases = visit(ctx.columnAliases().identifier(), Identifier.class);
-    }
+  public Node visitOr(RelationalSqlParser.OrContext ctx) {
+    List<ParserRuleContext> terms =
+        flatten(
+            ctx,
+            element -> {
+              if (element instanceof RelationalSqlParser.OrContext) {
+                RelationalSqlParser.OrContext or = 
(RelationalSqlParser.OrContext) element;
+                return Optional.of(or.booleanExpression());
+              }
 
-    if (ctx.primaryExpression() != null) {
-      return new AllColumns(getLocation(ctx), (Expression) 
visit(ctx.primaryExpression()), aliases);
-    } else {
-      return new AllColumns(getLocation(ctx), aliases);
+              return Optional.empty();
+            });
+
+    return new LogicalExpression(
+        getLocation(ctx), LogicalExpression.Operator.OR, visit(terms, 
Expression.class));
+  }
+
+  @Override
+  public Node visitAnd(RelationalSqlParser.AndContext ctx) {
+    List<ParserRuleContext> terms =
+        flatten(
+            ctx,
+            element -> {
+              if (element instanceof RelationalSqlParser.AndContext) {
+                RelationalSqlParser.AndContext and = 
(RelationalSqlParser.AndContext) element;
+                return Optional.of(and.booleanExpression());
+              }
+
+              return Optional.empty();
+            });
+
+    return new LogicalExpression(
+        getLocation(ctx), LogicalExpression.Operator.AND, visit(terms, 
Expression.class));
+  }
+
+  private static List<ParserRuleContext> flatten(
+      ParserRuleContext root,
+      Function<ParserRuleContext, Optional<List<? extends ParserRuleContext>>> 
extractChildren) {
+    List<ParserRuleContext> result = new ArrayList<>();
+    Deque<ParserRuleContext> pending = new ArrayDeque<>();
+    pending.push(root);
+
+    while (!pending.isEmpty()) {
+      ParserRuleContext next = pending.pop();
+
+      Optional<List<? extends ParserRuleContext>> children = 
extractChildren.apply(next);
+      if (!children.isPresent()) {
+        result.add(next);
+      } else {
+        for (int i = children.get().size() - 1; i >= 0; i--) {
+          pending.push(children.get().get(i));
+        }
+      }
     }
+
+    return result;
   }
 
   // *************** from clause *****************
@@ -894,21 +1000,6 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
         (Expression) visit(ctx.right));
   }
 
-  @Override
-  public Node visitLogicalNot(RelationalSqlParser.LogicalNotContext ctx) {
-    return super.visitLogicalNot(ctx);
-  }
-
-  @Override
-  public Node visitOr(RelationalSqlParser.OrContext ctx) {
-    return super.visitOr(ctx);
-  }
-
-  @Override
-  public Node visitAnd(RelationalSqlParser.AndContext ctx) {
-    return super.visitAnd(ctx);
-  }
-
   @Override
   public Node 
visitQuantifiedComparison(RelationalSqlParser.QuantifiedComparisonContext ctx) {
     return new QuantifiedComparisonExpression(
@@ -1242,42 +1333,45 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
 
   @Override
   public Node 
visitBasicStringLiteral(RelationalSqlParser.BasicStringLiteralContext ctx) {
-    return super.visitBasicStringLiteral(ctx);
+    return new StringLiteral(getLocation(ctx), 
unquote(ctx.STRING().getText()));
   }
 
   @Override
   public Node 
visitUnicodeStringLiteral(RelationalSqlParser.UnicodeStringLiteralContext ctx) {
-    return super.visitUnicodeStringLiteral(ctx);
+    return new StringLiteral(getLocation(ctx), decodeUnicodeLiteral(ctx));
   }
 
   @Override
   public Node visitBinaryLiteral(RelationalSqlParser.BinaryLiteralContext ctx) 
{
-    return super.visitBinaryLiteral(ctx);
+    String raw = ctx.BINARY_LITERAL().getText();
+    return new BinaryLiteral(getLocation(ctx), unquote(raw.substring(1)));
   }
 
   @Override
-  public Node visitNumericLiteral(RelationalSqlParser.NumericLiteralContext 
ctx) {
-    return super.visitNumericLiteral(ctx);
+  public Node visitDecimalLiteral(RelationalSqlParser.DecimalLiteralContext 
ctx) {
+    return new DecimalLiteral(getLocation(ctx), ctx.getText());
   }
 
   @Override
-  public Node visitBooleanLiteral(RelationalSqlParser.BooleanLiteralContext 
ctx) {
-    return super.visitBooleanLiteral(ctx);
+  public Node visitDoubleLiteral(RelationalSqlParser.DoubleLiteralContext ctx) 
{
+    return new DoubleLiteral(getLocation(ctx), ctx.getText());
   }
 
   @Override
-  public Node visitStringLiteral(RelationalSqlParser.StringLiteralContext ctx) 
{
-    return super.visitStringLiteral(ctx);
+  public Node visitIntegerLiteral(RelationalSqlParser.IntegerLiteralContext 
ctx) {
+    return new LongLiteral(getLocation(ctx), ctx.getText());
   }
 
   @Override
-  public Node visitParameter(RelationalSqlParser.ParameterContext ctx) {
-    return super.visitParameter(ctx);
+  public Node visitBooleanLiteral(RelationalSqlParser.BooleanLiteralContext 
ctx) {
+    return new BooleanLiteral(getLocation(ctx), ctx.getText());
   }
 
   @Override
-  public Node 
visitTrimsSpecification(RelationalSqlParser.TrimsSpecificationContext ctx) {
-    return super.visitTrimsSpecification(ctx);
+  public Node visitParameter(RelationalSqlParser.ParameterContext ctx) {
+    Parameter parameter = new Parameter(getLocation(ctx), parameterPosition);
+    parameterPosition++;
+    return parameter;
   }
 
   @Override
@@ -1292,16 +1386,6 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
     return new Identifier(getLocation(ctx), s);
   }
 
-  @Override
-  public Node visitBooleanValue(RelationalSqlParser.BooleanValueContext ctx) {
-    return super.visitBooleanValue(ctx);
-  }
-
-  @Override
-  public Node visitInterval(RelationalSqlParser.IntervalContext ctx) {
-    return super.visitInterval(ctx);
-  }
-
   @Override
   public Node visitIntervalField(RelationalSqlParser.IntervalFieldContext ctx) 
{
     return super.visitIntervalField(ctx);
@@ -1312,6 +1396,7 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
     return super.visitTimeDuration(ctx);
   }
 
+  // ***************** arguments *****************
   @Override
   public Node visitGenericType(RelationalSqlParser.GenericTypeContext ctx) {
     List<DataTypeParameter> parameters =
@@ -1325,183 +1410,113 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
 
   @Override
   public Node visitTypeParameter(RelationalSqlParser.TypeParameterContext ctx) 
{
-    return super.visitTypeParameter(ctx);
-  }
-
-  @Override
-  public Node 
visitUpdateAssignment(RelationalSqlParser.UpdateAssignmentContext ctx) {
-    return super.visitUpdateAssignment(ctx);
-  }
-
-  @Override
-  public Node visitReturnStatement(RelationalSqlParser.ReturnStatementContext 
ctx) {
-    return super.visitReturnStatement(ctx);
-  }
-
-  @Override
-  public Node 
visitAssignmentStatement(RelationalSqlParser.AssignmentStatementContext ctx) {
-    return super.visitAssignmentStatement(ctx);
-  }
-
-  @Override
-  public Node 
visitSimpleCaseStatement(RelationalSqlParser.SimpleCaseStatementContext ctx) {
-    return super.visitSimpleCaseStatement(ctx);
-  }
-
-  @Override
-  public Node 
visitSearchedCaseStatement(RelationalSqlParser.SearchedCaseStatementContext 
ctx) {
-    return super.visitSearchedCaseStatement(ctx);
-  }
-
-  @Override
-  public Node visitIfStatement(RelationalSqlParser.IfStatementContext ctx) {
-    return super.visitIfStatement(ctx);
-  }
-
-  @Override
-  public Node 
visitIterateStatement(RelationalSqlParser.IterateStatementContext ctx) {
-    return super.visitIterateStatement(ctx);
-  }
-
-  @Override
-  public Node visitLeaveStatement(RelationalSqlParser.LeaveStatementContext 
ctx) {
-    return super.visitLeaveStatement(ctx);
-  }
-
-  @Override
-  public Node 
visitCompoundStatement(RelationalSqlParser.CompoundStatementContext ctx) {
-    return super.visitCompoundStatement(ctx);
-  }
-
-  @Override
-  public Node visitLoopStatement(RelationalSqlParser.LoopStatementContext ctx) 
{
-    return super.visitLoopStatement(ctx);
-  }
-
-  @Override
-  public Node visitWhileStatement(RelationalSqlParser.WhileStatementContext 
ctx) {
-    return super.visitWhileStatement(ctx);
-  }
-
-  @Override
-  public Node visitRepeatStatement(RelationalSqlParser.RepeatStatementContext 
ctx) {
-    return super.visitRepeatStatement(ctx);
-  }
-
-  @Override
-  public Node 
visitCaseStatementWhenClause(RelationalSqlParser.CaseStatementWhenClauseContext 
ctx) {
-    return super.visitCaseStatementWhenClause(ctx);
-  }
-
-  @Override
-  public Node visitElseIfClause(RelationalSqlParser.ElseIfClauseContext ctx) {
-    return super.visitElseIfClause(ctx);
-  }
-
-  @Override
-  public Node visitElseClause(RelationalSqlParser.ElseClauseContext ctx) {
-    return super.visitElseClause(ctx);
-  }
-
-  @Override
-  public Node 
visitVariableDeclaration(RelationalSqlParser.VariableDeclarationContext ctx) {
-    return super.visitVariableDeclaration(ctx);
-  }
-
-  @Override
-  public Node 
visitSqlStatementList(RelationalSqlParser.SqlStatementListContext ctx) {
-    return super.visitSqlStatementList(ctx);
-  }
-
-  @Override
-  public Node visitPrivilege(RelationalSqlParser.PrivilegeContext ctx) {
-    return super.visitPrivilege(ctx);
-  }
-
-  @Override
-  public Node visitQualifiedName(RelationalSqlParser.QualifiedNameContext ctx) 
{
-    return super.visitQualifiedName(ctx);
-  }
-
-  @Override
-  public Node 
visitSpecifiedPrincipal(RelationalSqlParser.SpecifiedPrincipalContext ctx) {
-    return super.visitSpecifiedPrincipal(ctx);
-  }
-
-  @Override
-  public Node 
visitCurrentUserGrantor(RelationalSqlParser.CurrentUserGrantorContext ctx) {
-    return super.visitCurrentUserGrantor(ctx);
-  }
-
-  @Override
-  public Node 
visitCurrentRoleGrantor(RelationalSqlParser.CurrentRoleGrantorContext ctx) {
-    return super.visitCurrentRoleGrantor(ctx);
-  }
-
-  @Override
-  public Node 
visitUnspecifiedPrincipal(RelationalSqlParser.UnspecifiedPrincipalContext ctx) {
-    return super.visitUnspecifiedPrincipal(ctx);
-  }
-
-  @Override
-  public Node visitUserPrincipal(RelationalSqlParser.UserPrincipalContext ctx) 
{
-    return super.visitUserPrincipal(ctx);
-  }
-
-  @Override
-  public Node visitRolePrincipal(RelationalSqlParser.RolePrincipalContext ctx) 
{
-    return super.visitRolePrincipal(ctx);
-  }
-
-  @Override
-  public Node visitRoles(RelationalSqlParser.RolesContext ctx) {
-    return super.visitRoles(ctx);
-  }
-
-  @Override
-  public Node 
visitUnquotedIdentifier(RelationalSqlParser.UnquotedIdentifierContext ctx) {
-    return new Identifier(getLocation(ctx), ctx.getText(), false);
-  }
-
-  @Override
-  public Node 
visitQuotedIdentifier(RelationalSqlParser.QuotedIdentifierContext ctx) {
-    String token = ctx.getText();
-    String identifier = token.substring(1, token.length() - 1).replace("\"\"", 
"\"");
-
-    return new Identifier(getLocation(ctx), identifier, true);
-  }
+    if (ctx.INTEGER_VALUE() != null) {
+      return new NumericParameter(getLocation(ctx), ctx.getText());
+    }
 
-  @Override
-  public Node visitDecimalLiteral(RelationalSqlParser.DecimalLiteralContext 
ctx) {
-    return super.visitDecimalLiteral(ctx);
+    return new TypeParameter((DataType) visit(ctx.type()));
   }
 
-  @Override
-  public Node visitDoubleLiteral(RelationalSqlParser.DoubleLiteralContext ctx) 
{
-    return super.visitDoubleLiteral(ctx);
-  }
+  // ***************** helpers *****************
 
-  @Override
-  public Node visitIntegerLiteral(RelationalSqlParser.IntegerLiteralContext 
ctx) {
-    return super.visitIntegerLiteral(ctx);
+  private enum UnicodeDecodeState {
+    EMPTY,
+    ESCAPED,
+    UNICODE_SEQUENCE
   }
 
-  @Override
-  public Node visitIdentifierUser(RelationalSqlParser.IdentifierUserContext 
ctx) {
-    return super.visitIdentifierUser(ctx);
-  }
+  private static String decodeUnicodeLiteral(
+      RelationalSqlParser.UnicodeStringLiteralContext context) {
+    char escape;
+    if (context.UESCAPE() != null) {
+      String escapeString = unquote(context.STRING().getText());
+      check(!escapeString.isEmpty(), "Empty Unicode escape character", 
context);
+      check(
+          escapeString.length() == 1, "Invalid Unicode escape character: " + 
escapeString, context);
+      escape = escapeString.charAt(0);
+      check(
+          isValidUnicodeEscape(escape),
+          "Invalid Unicode escape character: " + escapeString,
+          context);
+    } else {
+      escape = '\\';
+    }
 
-  @Override
-  public Node visitStringUser(RelationalSqlParser.StringUserContext ctx) {
-    return super.visitStringUser(ctx);
-  }
+    String rawContent = 
unquote(context.UNICODE_STRING().getText().substring(2));
+    StringBuilder unicodeStringBuilder = new StringBuilder();
+    StringBuilder escapedCharacterBuilder = new StringBuilder();
+    int charactersNeeded = 0;
+    UnicodeDecodeState state = UnicodeDecodeState.EMPTY;
+    for (int i = 0; i < rawContent.length(); i++) {
+      char ch = rawContent.charAt(i);
+      switch (state) {
+        case EMPTY:
+          if (ch == escape) {
+            state = UnicodeDecodeState.ESCAPED;
+          } else {
+            unicodeStringBuilder.append(ch);
+          }
+          break;
+        case ESCAPED:
+          if (ch == escape) {
+            unicodeStringBuilder.append(escape);
+            state = UnicodeDecodeState.EMPTY;
+          } else if (ch == '+') {
+            state = UnicodeDecodeState.UNICODE_SEQUENCE;
+            charactersNeeded = 6;
+          } else if (isHexDigit(ch)) {
+            state = UnicodeDecodeState.UNICODE_SEQUENCE;
+            charactersNeeded = 4;
+            escapedCharacterBuilder.append(ch);
+          } else {
+            throw parseError("Invalid hexadecimal digit: " + ch, context);
+          }
+          break;
+        case UNICODE_SEQUENCE:
+          check(isHexDigit(ch), "Incomplete escape sequence: " + 
escapedCharacterBuilder, context);
+          escapedCharacterBuilder.append(ch);
+          if (charactersNeeded == escapedCharacterBuilder.length()) {
+            String currentEscapedCode = escapedCharacterBuilder.toString();
+            escapedCharacterBuilder.setLength(0);
+            int codePoint = Integer.parseInt(currentEscapedCode, 16);
+            check(
+                Character.isValidCodePoint(codePoint),
+                "Invalid escaped character: " + currentEscapedCode,
+                context);
+            if (Character.isSupplementaryCodePoint(codePoint)) {
+              unicodeStringBuilder.appendCodePoint(codePoint);
+            } else {
+              char currentCodePoint = (char) codePoint;
+              if (Character.isSurrogate(currentCodePoint)) {
+                throw parseError(
+                    String.format(
+                        "Invalid escaped character: %s. Escaped character is a 
surrogate. Use '\\+123456' instead.",
+                        currentEscapedCode),
+                    context);
+              }
+              unicodeStringBuilder.append(currentCodePoint);
+            }
+            state = UnicodeDecodeState.EMPTY;
+            charactersNeeded = -1;
+          } else {
+            check(
+                charactersNeeded > escapedCharacterBuilder.length(),
+                "Unexpected escape sequence length: " + 
escapedCharacterBuilder.length(),
+                context);
+          }
+          break;
+        default:
+          throw new UnsupportedOperationException();
+      }
+    }
 
-  @Override
-  public Node visitNonReserved(RelationalSqlParser.NonReservedContext ctx) {
-    return super.visitNonReserved(ctx);
+    check(
+        state == UnicodeDecodeState.EMPTY,
+        "Incomplete escape sequence: " + escapedCharacterBuilder.toString(),
+        context);
+    return unicodeStringBuilder.toString();
   }
 
-  // ***************** helpers *****************
   private <T> Optional<T> visitIfPresent(ParserRuleContext context, Class<T> 
clazz) {
     return Optional.ofNullable(context).map(this::visit).map(clazz::cast);
   }
@@ -1510,6 +1525,10 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
     return 
contexts.stream().map(this::visit).map(clazz::cast).collect(toList());
   }
 
+  private static String unquote(String value) {
+    return value.substring(1, value.length() - 1).replace("''", "'");
+  }
+
   private QualifiedName 
getQualifiedName(RelationalSqlParser.QualifiedNameContext context) {
     return QualifiedName.of(visit(context.identifier(), Identifier.class));
   }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/SqlParser.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/SqlParser.java
index 933a39b2338..6a8e652fe63 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/SqlParser.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/SqlParser.java
@@ -19,4 +19,235 @@
 
 package org.apache.iotdb.db.relational.sql.parser;
 
-public class SqlParser {}
+import org.apache.iotdb.db.relational.grammar.sql.RelationalSqlBaseListener;
+import org.apache.iotdb.db.relational.grammar.sql.RelationalSqlLexer;
+import org.apache.iotdb.db.relational.grammar.sql.RelationalSqlParser;
+import org.apache.iotdb.db.relational.sql.tree.DataType;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.Node;
+import org.apache.iotdb.db.relational.sql.tree.NodeLocation;
+import org.apache.iotdb.db.relational.sql.tree.Statement;
+
+import org.antlr.v4.runtime.ANTLRErrorListener;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonToken;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.DefaultErrorStrategy;
+import org.antlr.v4.runtime.InputMismatchException;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.atn.PredictionMode;
+import org.antlr.v4.runtime.misc.Pair;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import static java.util.Objects.requireNonNull;
+
+public class SqlParser {
+  private static final ANTLRErrorListener LEXER_ERROR_LISTENER =
+      new BaseErrorListener() {
+        @Override
+        public void syntaxError(
+            Recognizer<?, ?> recognizer,
+            Object offendingSymbol,
+            int line,
+            int charPositionInLine,
+            String message,
+            RecognitionException e) {
+          throw new ParsingException(message, e, line, charPositionInLine + 1);
+        }
+      };
+  private static final BiConsumer<RelationalSqlLexer, RelationalSqlParser>
+      DEFAULT_PARSER_INITIALIZER = (RelationalSqlLexer lexer, 
RelationalSqlParser parser) -> {};
+
+  private static final ErrorHandler PARSER_ERROR_HANDLER =
+      ErrorHandler.builder()
+          .specialRule(RelationalSqlParser.RULE_expression, "<expression>")
+          .specialRule(RelationalSqlParser.RULE_booleanExpression, 
"<expression>")
+          .specialRule(RelationalSqlParser.RULE_valueExpression, 
"<expression>")
+          .specialRule(RelationalSqlParser.RULE_primaryExpression, 
"<expression>")
+          .specialRule(RelationalSqlParser.RULE_predicate, "<predicate>")
+          .specialRule(RelationalSqlParser.RULE_identifier, "<identifier>")
+          .specialRule(RelationalSqlParser.RULE_string, "<string>")
+          .specialRule(RelationalSqlParser.RULE_query, "<query>")
+          .specialRule(RelationalSqlParser.RULE_type, "<type>")
+          .specialToken(RelationalSqlParser.INTEGER_VALUE, "<integer>")
+          .build();
+
+  private final BiConsumer<RelationalSqlLexer, RelationalSqlParser> 
initializer;
+
+  public SqlParser() {
+    this(DEFAULT_PARSER_INITIALIZER);
+  }
+
+  public SqlParser(BiConsumer<RelationalSqlLexer, RelationalSqlParser> 
initializer) {
+    this.initializer = requireNonNull(initializer, "initializer is null");
+  }
+
+  public Statement createStatement(String sql) {
+    return (Statement) invokeParser("statement", sql, 
RelationalSqlParser::singleStatement);
+  }
+
+  public Statement createStatement(String sql, NodeLocation location) {
+    return (Statement)
+        invokeParser(
+            "statement", sql, Optional.ofNullable(location), 
RelationalSqlParser::singleStatement);
+  }
+
+  public Expression createExpression(String expression) {
+    return (Expression)
+        invokeParser("expression", expression, 
RelationalSqlParser::standaloneExpression);
+  }
+
+  public DataType createType(String expression) {
+    return (DataType) invokeParser("type", expression, 
RelationalSqlParser::standaloneType);
+  }
+
+  private Node invokeParser(
+      String name, String sql, Function<RelationalSqlParser, 
ParserRuleContext> parseFunction) {
+    return invokeParser(name, sql, Optional.empty(), parseFunction);
+  }
+
+  private Node invokeParser(
+      String name,
+      String sql,
+      Optional<NodeLocation> location,
+      Function<RelationalSqlParser, ParserRuleContext> parseFunction) {
+    try {
+      RelationalSqlLexer lexer = new 
RelationalSqlLexer(CharStreams.fromString(sql));
+      CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+      RelationalSqlParser parser = new RelationalSqlParser(tokenStream);
+      initializer.accept(lexer, parser);
+
+      // Override the default error strategy to not attempt inserting or 
deleting a token.
+      // Otherwise, it messes up error reporting
+      parser.setErrorHandler(
+          new DefaultErrorStrategy() {
+            @Override
+            public Token recoverInline(Parser recognizer) throws 
RecognitionException {
+              if (nextTokensContext == null) {
+                throw new InputMismatchException(recognizer);
+              }
+              throw new InputMismatchException(recognizer, nextTokensState, 
nextTokensContext);
+            }
+          });
+
+      parser.addParseListener(new 
PostProcessor(Arrays.asList(parser.getRuleNames()), parser));
+
+      lexer.removeErrorListeners();
+      lexer.addErrorListener(LEXER_ERROR_LISTENER);
+
+      parser.removeErrorListeners();
+      parser.addErrorListener(PARSER_ERROR_HANDLER);
+
+      ParserRuleContext tree;
+      try {
+        try {
+          // first, try parsing with potentially faster SLL mode
+          parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
+          tree = parseFunction.apply(parser);
+        } catch (ParsingException ex) {
+          // if we fail, parse with LL mode
+          tokenStream.seek(0); // rewind input stream
+          parser.reset();
+
+          parser.getInterpreter().setPredictionMode(PredictionMode.LL);
+          tree = parseFunction.apply(parser);
+        }
+      } catch (ParsingException e) {
+        location.ifPresent(
+            statementLocation -> {
+              int line = statementLocation.getLineNumber();
+              int column = statementLocation.getColumnNumber();
+              throw new ParsingException(
+                  e.getErrorMessage(),
+                  (RecognitionException) e.getCause(),
+                  e.getLineNumber() + line - 1,
+                  e.getColumnNumber() + (line == 1 ? column : 0));
+            });
+        throw e;
+      }
+
+      return new AstBuilder(location.orElse(null)).visit(tree);
+    } catch (StackOverflowError e) {
+      throw new ParsingException(name + " is too large (stack overflow while 
parsing)");
+    }
+  }
+
+  private static class PostProcessor extends RelationalSqlBaseListener {
+    private final List<String> ruleNames;
+    private final RelationalSqlParser parser;
+
+    public PostProcessor(List<String> ruleNames, RelationalSqlParser parser) {
+      this.ruleNames = ruleNames;
+      this.parser = parser;
+    }
+
+    @Override
+    public void 
exitQuotedIdentifier(RelationalSqlParser.QuotedIdentifierContext context) {
+      Token token = context.QUOTED_IDENTIFIER().getSymbol();
+      if (token.getText().length() == 2) { // empty identifier
+        throw new ParsingException(
+            "Zero-length delimited identifier not allowed",
+            null,
+            token.getLine(),
+            token.getCharPositionInLine() + 1);
+      }
+    }
+
+    @Override
+    public void 
exitBackQuotedIdentifier(RelationalSqlParser.BackQuotedIdentifierContext 
context) {
+      Token token = context.BACKQUOTED_IDENTIFIER().getSymbol();
+      throw new ParsingException(
+          "backquoted identifiers are not supported; use double quotes to 
quote identifiers",
+          null,
+          token.getLine(),
+          token.getCharPositionInLine() + 1);
+    }
+
+    @Override
+    public void exitDigitIdentifier(RelationalSqlParser.DigitIdentifierContext 
context) {
+      Token token = context.DIGIT_IDENTIFIER().getSymbol();
+      throw new ParsingException(
+          "identifiers must not start with a digit; surround the identifier 
with double quotes",
+          null,
+          token.getLine(),
+          token.getCharPositionInLine() + 1);
+    }
+
+    @Override
+    public void exitNonReserved(RelationalSqlParser.NonReservedContext 
context) {
+      // we can't modify the tree during rule enter/exit event handling unless 
we're dealing with a
+      // terminal.
+      // Otherwise, ANTLR gets confused and fires spurious notifications.
+      if (!(context.getChild(0) instanceof TerminalNode)) {
+        int rule = ((ParserRuleContext) context.getChild(0)).getRuleIndex();
+        throw new AssertionError(
+            "nonReserved can only contain tokens. Found nested rule: " + 
ruleNames.get(rule));
+      }
+
+      // replace nonReserved words with IDENT tokens
+      context.getParent().removeLastChild();
+
+      Token token = (Token) context.getChild(0).getPayload();
+      Token newToken =
+          new CommonToken(
+              new Pair<>(token.getTokenSource(), token.getInputStream()),
+              RelationalSqlLexer.IDENTIFIER,
+              token.getChannel(),
+              token.getStartIndex(),
+              token.getStopIndex());
+
+      
context.getParent().addChild(parser.createTerminalNode(context.getParent(), 
newToken));
+    }
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/AstUtil.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/AstUtil.java
new file mode 100644
index 00000000000..c067a8705cd
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/AstUtil.java
@@ -0,0 +1,99 @@
+/*
+ * 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.iotdb.db.relational.sql.util;
+
+import org.apache.iotdb.db.relational.sql.tree.Node;
+
+import com.google.common.graph.SuccessorsFunction;
+import com.google.common.graph.Traverser;
+
+import java.util.List;
+import java.util.OptionalInt;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import static com.google.common.collect.Streams.stream;
+import static java.util.Objects.requireNonNull;
+
+public final class AstUtil {
+
+  public static Stream<Node> preOrder(Node node) {
+    return stream(
+        Traverser.forTree((SuccessorsFunction<Node>) Node::getChildren)
+            .depthFirstPreOrder(requireNonNull(node, "node is null")));
+  }
+
+  /**
+   * Compares two AST trees recursively by applying the provided comparator to 
each pair of nodes.
+   *
+   * <p>The comparator can perform a hybrid shallow/deep comparison. If it 
returns true or false,
+   * the nodes and any subtrees are considered equal or different, 
respectively. If it returns null,
+   * the nodes are considered shallowly-equal and their children will be 
compared recursively.
+   */
+  public static boolean treeEqual(
+      Node left, Node right, BiFunction<Node, Node, Boolean> 
subtreeComparator) {
+    Boolean equal = subtreeComparator.apply(left, right);
+
+    if (equal != null) {
+      return equal;
+    }
+
+    List<? extends Node> leftChildren = left.getChildren();
+    List<? extends Node> rightChildren = right.getChildren();
+
+    if (leftChildren.size() != rightChildren.size()) {
+      return false;
+    }
+
+    for (int i = 0; i < leftChildren.size(); i++) {
+      if (!treeEqual(leftChildren.get(i), rightChildren.get(i), 
subtreeComparator)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Computes a hash of the given AST by applying the provided subtree hasher 
at each level.
+   *
+   * <p>If the hasher returns a non-empty {@link OptionalInt}, the value is 
treated as the hash for
+   * the subtree at that node. Otherwise, the hashes of its children are 
computed and combined.
+   */
+  public static int treeHash(Node node, Function<Node, OptionalInt> 
subtreeHasher) {
+    OptionalInt hash = subtreeHasher.apply(node);
+
+    if (hash.isPresent()) {
+      return hash.getAsInt();
+    }
+
+    List<? extends Node> children = node.getChildren();
+
+    int result = node.getClass().hashCode();
+    for (Node element : children) {
+      result = 31 * result + treeHash(element, subtreeHasher);
+    }
+
+    return result;
+  }
+
+  private AstUtil() {}
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/QueryUtil.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/QueryUtil.java
new file mode 100644
index 00000000000..add3ef4b889
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/QueryUtil.java
@@ -0,0 +1,208 @@
+/*
+ * 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.iotdb.db.relational.sql.util;
+
+import org.apache.iotdb.db.relational.sql.tree.AliasedRelation;
+import org.apache.iotdb.db.relational.sql.tree.CoalesceExpression;
+import org.apache.iotdb.db.relational.sql.tree.ComparisonExpression;
+import org.apache.iotdb.db.relational.sql.tree.DereferenceExpression;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.FunctionCall;
+import org.apache.iotdb.db.relational.sql.tree.GroupBy;
+import org.apache.iotdb.db.relational.sql.tree.Identifier;
+import org.apache.iotdb.db.relational.sql.tree.LogicalExpression;
+import org.apache.iotdb.db.relational.sql.tree.Node;
+import org.apache.iotdb.db.relational.sql.tree.Offset;
+import org.apache.iotdb.db.relational.sql.tree.OrderBy;
+import org.apache.iotdb.db.relational.sql.tree.QualifiedName;
+import org.apache.iotdb.db.relational.sql.tree.Query;
+import org.apache.iotdb.db.relational.sql.tree.QueryBody;
+import org.apache.iotdb.db.relational.sql.tree.QuerySpecification;
+import org.apache.iotdb.db.relational.sql.tree.Relation;
+import org.apache.iotdb.db.relational.sql.tree.SearchedCaseExpression;
+import org.apache.iotdb.db.relational.sql.tree.Select;
+import org.apache.iotdb.db.relational.sql.tree.SelectItem;
+import org.apache.iotdb.db.relational.sql.tree.SingleColumn;
+import org.apache.iotdb.db.relational.sql.tree.SortItem;
+import org.apache.iotdb.db.relational.sql.tree.StringLiteral;
+import org.apache.iotdb.db.relational.sql.tree.Table;
+import org.apache.iotdb.db.relational.sql.tree.TableSubquery;
+import org.apache.iotdb.db.relational.sql.tree.WhenClause;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static java.util.Arrays.asList;
+
+public final class QueryUtil {
+  private QueryUtil() {}
+
+  public static Identifier identifier(String name) {
+    return new Identifier(name);
+  }
+
+  public static Identifier quotedIdentifier(String name) {
+    return new Identifier(name, true);
+  }
+
+  public static Expression nameReference(String first, String... rest) {
+    return DereferenceExpression.from(QualifiedName.of(first, rest));
+  }
+
+  public static SelectItem unaliasedName(String name) {
+    return new SingleColumn(identifier(name));
+  }
+
+  public static SelectItem aliasedName(String name, String alias) {
+    return new SingleColumn(identifier(name), identifier(alias));
+  }
+
+  public static Select selectList(Expression... expressions) {
+    return selectList(asList(expressions));
+  }
+
+  public static Select selectList(List<Expression> expressions) {
+    ImmutableList.Builder<SelectItem> items = ImmutableList.builder();
+    for (Expression expression : expressions) {
+      items.add(new SingleColumn(expression));
+    }
+    return new Select(false, items.build());
+  }
+
+  public static Select selectList(SelectItem... items) {
+    return new Select(false, ImmutableList.copyOf(items));
+  }
+
+  public static Select selectAll(List<SelectItem> items) {
+    return new Select(false, items);
+  }
+
+  public static Table table(QualifiedName name) {
+    return new Table(name);
+  }
+
+  public static Relation subquery(Query query) {
+    return new TableSubquery(query);
+  }
+
+  public static SortItem ascending(String name) {
+    return new SortItem(
+        identifier(name), SortItem.Ordering.ASCENDING, 
SortItem.NullOrdering.UNDEFINED);
+  }
+
+  public static Expression logicalAnd(Expression left, Expression right) {
+    return LogicalExpression.and(left, right);
+  }
+
+  public static Expression equal(Expression left, Expression right) {
+    return new ComparisonExpression(ComparisonExpression.Operator.EQUAL, left, 
right);
+  }
+
+  public static Expression caseWhen(Expression operand, Expression result) {
+    return new SearchedCaseExpression(ImmutableList.of(new WhenClause(operand, 
result)));
+  }
+
+  public static Expression functionCall(String name, Expression... arguments) {
+    return new FunctionCall(QualifiedName.of(name), 
ImmutableList.copyOf(arguments));
+  }
+
+  public static Relation aliased(Relation relation, String alias) {
+    return new AliasedRelation(relation, identifier(alias), null);
+  }
+
+  public static Relation aliased(Relation relation, String alias, List<String> 
columnAliases) {
+    return new AliasedRelation(
+        relation,
+        identifier(alias),
+        
columnAliases.stream().map(QueryUtil::identifier).collect(Collectors.toList()));
+  }
+
+  public static SelectItem aliasedNullToEmpty(String column, String alias) {
+    return new SingleColumn(
+        new CoalesceExpression(identifier(column), new StringLiteral("")), 
identifier(alias));
+  }
+
+  public static OrderBy ordering(SortItem... items) {
+    return new OrderBy(ImmutableList.copyOf(items));
+  }
+
+  public static Query simpleQuery(Select select) {
+    return query(
+        new QuerySpecification(
+            select,
+            Optional.empty(),
+            Optional.empty(),
+            Optional.empty(),
+            Optional.empty(),
+            Optional.empty(),
+            Optional.empty(),
+            Optional.empty()));
+  }
+
+  public static Query simpleQuery(Select select, Relation from) {
+    return simpleQuery(select, from, Optional.empty(), Optional.empty());
+  }
+
+  public static Query simpleQuery(Select select, Relation from, OrderBy 
orderBy) {
+    return simpleQuery(select, from, Optional.empty(), Optional.of(orderBy));
+  }
+
+  public static Query simpleQuery(Select select, Relation from, Expression 
where) {
+    return simpleQuery(select, from, Optional.of(where), Optional.empty());
+  }
+
+  public static Query simpleQuery(Select select, Relation from, Expression 
where, OrderBy orderBy) {
+    return simpleQuery(select, from, Optional.of(where), Optional.of(orderBy));
+  }
+
+  public static Query simpleQuery(
+      Select select, Relation from, Optional<Expression> where, 
Optional<OrderBy> orderBy) {
+    return simpleQuery(
+        select,
+        from,
+        where,
+        Optional.empty(),
+        Optional.empty(),
+        orderBy,
+        Optional.empty(),
+        Optional.empty());
+  }
+
+  public static Query simpleQuery(
+      Select select,
+      Relation from,
+      Optional<Expression> where,
+      Optional<GroupBy> groupBy,
+      Optional<Expression> having,
+      Optional<OrderBy> orderBy,
+      Optional<Offset> offset,
+      Optional<Node> limit) {
+    return query(
+        new QuerySpecification(
+            select, Optional.of(from), where, groupBy, having, orderBy, 
offset, limit));
+  }
+
+  public static Query query(QueryBody body) {
+    return new Query(Optional.empty(), body, Optional.empty(), 
Optional.empty(), Optional.empty());
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/ReservedIdentifiers.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/ReservedIdentifiers.java
new file mode 100644
index 00000000000..751abb58d1d
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/ReservedIdentifiers.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.iotdb.db.relational.sql.util;
+
+import org.apache.iotdb.db.relational.sql.parser.ParsingException;
+import org.apache.iotdb.db.relational.sql.parser.SqlParser;
+import org.apache.iotdb.db.relational.sql.tree.Identifier;
+
+import java.util.Set;
+
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static 
org.apache.iotdb.db.relational.grammar.sql.RelationalSqlKeywords.sqlKeywords;
+
+public final class ReservedIdentifiers {
+  private static final SqlParser PARSER = new SqlParser();
+
+  public static Set<String> reservedIdentifiers() {
+    return sqlKeywords().stream()
+        .filter(ReservedIdentifiers::reserved)
+        .sorted()
+        .collect(toImmutableSet());
+  }
+
+  public static boolean reserved(String name) {
+    try {
+      return !(PARSER.createExpression(name) instanceof Identifier);
+    } catch (ParsingException ignored) {
+      return true;
+    }
+  }
+}

Reply via email to