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;
+ }
+ }
+}