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 b1278912731 partial
b1278912731 is described below

commit b1278912731a0795985fc36d2618029fd8435ba4
Author: JackieTien97 <[email protected]>
AuthorDate: Sun Feb 18 12:35:51 2024 +0800

    partial
---
 .../db/relational/sql/parser/ParsingException.java |  68 +++++++
 .../db/relational/sql/tree/AliasedRelation.java    | 110 ++++++++++++
 .../iotdb/db/relational/sql/tree/AstVisitor.java   |   1 +
 .../db/relational/sql/tree/BinaryLiteral.java      | 108 +++++++++++
 .../db/relational/sql/tree/BooleanLiteral.java     |  85 +++++++++
 .../db/relational/sql/tree/DecimalLiteral.java     |  77 ++++++++
 .../db/relational/sql/tree/DoubleLiteral.java      |  80 +++++++++
 .../iotdb/db/relational/sql/tree/Expression.java   |   2 +
 .../db/relational/sql/tree/GenericLiteral.java     | 102 +++++++++++
 .../tree/{Literal.java => IsNotNullPredicate.java} |  51 +++++-
 .../tree/{Literal.java => IsNullPredicate.java}    |  51 +++++-
 .../apache/iotdb/db/relational/sql/tree/Join.java  | 154 ++++++++++++++++
 .../sql/tree/{Literal.java => JoinCriteria.java}   |  23 +--
 .../sql/tree/{Literal.java => JoinOn.java}         |  46 ++++-
 .../sql/tree/{Literal.java => JoinUsing.java}      |  46 ++++-
 .../db/relational/sql/tree/LikePredicate.java      | 107 +++++++++++
 .../apache/iotdb/db/relational/sql/tree/Limit.java |  98 ++++++++++
 .../iotdb/db/relational/sql/tree/Literal.java      |   3 +-
 .../db/relational/sql/tree/LogicalExpression.java  | 114 ++++++++++++
 .../iotdb/db/relational/sql/tree/LongLiteral.java  | 109 ++++++++++++
 .../sql/tree/{Literal.java => NaturalJoin.java}    |  25 ++-
 .../apache/iotdb/db/relational/sql/tree/Node.java  |   2 +-
 .../sql/tree/{Literal.java => NotExpression.java}  |  51 +++++-
 .../sql/tree/{Literal.java => NullLiteral.java}    |  36 +++-
 .../iotdb/db/relational/sql/tree/Offset.java       |  95 ++++++++++
 .../iotdb/db/relational/sql/tree/OrderBy.java      |  91 ++++++++++
 .../sql/tree/{Literal.java => Parameter.java}      |  52 +++++-
 .../sql/tree/QuantifiedComparisonExpression.java   | 116 ++++++++++++
 .../apache/iotdb/db/relational/sql/tree/Query.java | 159 +++++++++++++++++
 .../sql/tree/{Literal.java => QueryBody.java}      |  13 +-
 .../db/relational/sql/tree/QuerySpecification.java | 198 +++++++++++++++++++++
 .../sql/tree/{Literal.java => Relation.java}       |  16 +-
 .../sql/tree/SearchedCaseExpression.java           | 107 +++++++++++
 .../relational/sql/tree/SimpleCaseExpression.java  | 120 +++++++++++++
 .../iotdb/db/relational/sql/tree/SortItem.java     | 118 ++++++++++++
 .../db/relational/sql/tree/StringLiteral.java      |  82 +++++++++
 .../tree/{Literal.java => SubqueryExpression.java} |  51 +++++-
 .../tree/{Literal.java => SymbolReference.java}    |  36 +++-
 .../apache/iotdb/db/relational/sql/tree/Trim.java  | 138 ++++++++++++++
 .../iotdb/db/relational/sql/tree/WhenClause.java   |  87 +++++++++
 .../apache/iotdb/db/relational/sql/tree/With.java  | 105 +++++++++++
 .../iotdb/db/relational/sql/tree/WithQuery.java    | 129 ++++++++++++++
 .../relational/sql/util/ExpressionFormatter.java   |   2 +
 43 files changed, 3150 insertions(+), 114 deletions(-)

diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/ParsingException.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/ParsingException.java
new file mode 100644
index 00000000000..281490f4dd3
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/parser/ParsingException.java
@@ -0,0 +1,68 @@
+/*
+ * 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.parser;
+
+import org.antlr.v4.runtime.RecognitionException;
+import org.apache.iotdb.db.relational.sql.tree.NodeLocation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class ParsingException extends RuntimeException {
+  private final int line;
+  private final int column;
+
+  public ParsingException(String message, RecognitionException cause, int 
line, int column) {
+    super(message, cause);
+    checkArgument(line > 0, "line must be > 0");
+    checkArgument(column > 0, "column must be > 0");
+
+    this.line = line;
+    this.column = column;
+  }
+
+  public ParsingException(String message) {
+    this(message, null, 1, 1);
+  }
+
+  public ParsingException(String message, RecognitionException cause) {
+    this(message, cause, 1, 1);
+  }
+
+  public ParsingException(String message, NodeLocation nodeLocation) {
+    this(message, null, nodeLocation.getLineNumber(), 
nodeLocation.getColumnNumber());
+  }
+
+  public int getLineNumber() {
+    return line;
+  }
+
+  public int getColumnNumber() {
+    return column;
+  }
+
+  public String getErrorMessage() {
+    return super.getMessage();
+  }
+
+  @Override
+  public String getMessage() {
+    return String.format("line %s:%s: %s", getLineNumber(), getColumnNumber(), 
getErrorMessage());
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/AliasedRelation.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/AliasedRelation.java
new file mode 100644
index 00000000000..231b9e229d9
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/AliasedRelation.java
@@ -0,0 +1,110 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static java.util.Objects.requireNonNull;
+
+public class AliasedRelation extends Relation {
+  private final Relation relation;
+  private final Identifier alias;
+  private final List<Identifier> columnNames;
+
+  public AliasedRelation(Relation relation, Identifier alias, List<Identifier> 
columnNames) {
+    super(null);
+    this.relation = requireNonNull(relation, "relation is null");
+    this.alias = requireNonNull(alias, "alias is null");
+    this.columnNames = columnNames;
+  }
+
+  public AliasedRelation(NodeLocation location, Relation relation, Identifier 
alias, List<Identifier> columnNames) {
+    super(requireNonNull(location, "location is null"));
+    this.relation = requireNonNull(relation, "relation is null");
+    this.alias = requireNonNull(alias, "alias is null");
+    this.columnNames = columnNames;
+  }
+
+  public Relation getRelation() {
+    return relation;
+  }
+
+  public Identifier getAlias() {
+    return alias;
+  }
+
+  public List<Identifier> getColumnNames() {
+    return columnNames;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitAliasedRelation(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    return ImmutableList.of(relation);
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("relation", relation)
+        .add("alias", alias)
+        .add("columnNames", columnNames)
+        .omitNullValues()
+        .toString();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    AliasedRelation that = (AliasedRelation) o;
+    return Objects.equals(relation, that.relation) &&
+        Objects.equals(alias, that.alias) &&
+        Objects.equals(columnNames, that.columnNames);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(relation, alias, columnNames);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    AliasedRelation otherRelation = (AliasedRelation) other;
+    return alias.equals(otherRelation.alias) && Objects.equals(columnNames, 
otherRelation.columnNames);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/AstVisitor.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/AstVisitor.java
index 607c3a1a93b..f6aca0f4d28 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/AstVisitor.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/AstVisitor.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.db.relational.sql.tree;
 
+
 import javax.annotation.Nullable;
 
 public abstract class AstVisitor<R, C> {
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/BinaryLiteral.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/BinaryLiteral.java
new file mode 100644
index 00000000000..26f79356d6c
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/BinaryLiteral.java
@@ -0,0 +1,108 @@
+/*
+ * 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.tree;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.io.BaseEncoding;
+import org.apache.iotdb.db.relational.sql.parser.ParsingException;
+
+import java.util.Arrays;
+
+import static java.util.Locale.ENGLISH;
+import static java.util.Objects.requireNonNull;
+
+public class BinaryLiteral extends Literal {
+  // the grammar could possibly include whitespace in the value it passes to us
+  private static final CharMatcher WHITESPACE_MATCHER = 
CharMatcher.whitespace();
+  private static final CharMatcher HEX_DIGIT_MATCHER = 
CharMatcher.inRange('A', 'F')
+      .or(CharMatcher.inRange('0', '9'))
+      .precomputed();
+
+  private final byte[] value;
+
+  public BinaryLiteral(String value) {
+    super(null);
+    requireNonNull(value, "value is null");
+    String hexString = 
WHITESPACE_MATCHER.removeFrom(value).toUpperCase(ENGLISH);
+    if (!HEX_DIGIT_MATCHER.matchesAllOf(hexString)) {
+      throw new ParsingException("Binary literal can only contain hexadecimal 
digits");
+    }
+    if (hexString.length() % 2 != 0) {
+      throw new ParsingException("Binary literal must contain an even number 
of digits");
+    }
+    this.value = BaseEncoding.base16().decode(hexString);
+  }
+
+  public BinaryLiteral(NodeLocation location, String value) {
+    super(requireNonNull(location, "location is null"));
+    requireNonNull(value, "value is null");
+    String hexString = 
WHITESPACE_MATCHER.removeFrom(value).toUpperCase(ENGLISH);
+    if (!HEX_DIGIT_MATCHER.matchesAllOf(hexString)) {
+      throw new ParsingException("Binary literal can only contain hexadecimal 
digits", location);
+    }
+    if (hexString.length() % 2 != 0) {
+      throw new ParsingException("Binary literal must contain an even number 
of digits", location);
+    }
+    this.value = BaseEncoding.base16().decode(hexString);
+  }
+
+  /**
+   * Return the valued as a hex-formatted string with upper-case characters
+   */
+  public String toHexString() {
+    return BaseEncoding.base16().encode(value);
+  }
+
+  public byte[] getValue() {
+    return value.clone();
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitBinaryLiteral(this, context);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    BinaryLiteral that = (BinaryLiteral) o;
+    return Arrays.equals(value, that.value);
+  }
+
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(value);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    return Arrays.equals(value, ((BinaryLiteral) other).value);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/BooleanLiteral.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/BooleanLiteral.java
new file mode 100644
index 00000000000..51ce7afece6
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/BooleanLiteral.java
@@ -0,0 +1,85 @@
+/*
+ * 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.tree;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Locale.ENGLISH;
+import static java.util.Objects.requireNonNull;
+
+public class BooleanLiteral extends Literal {
+
+  public static final BooleanLiteral TRUE_LITERAL = new BooleanLiteral("true");
+  public static final BooleanLiteral FALSE_LITERAL = new 
BooleanLiteral("false");
+
+  private final boolean value;
+
+  public BooleanLiteral(String value) {
+    super(null);
+    requireNonNull(value, "value is null");
+    checkArgument(value.toLowerCase(ENGLISH).equals("true") || 
value.toLowerCase(ENGLISH).equals("false"));
+
+    this.value = value.toLowerCase(ENGLISH).equals("true");
+  }
+
+  public BooleanLiteral(NodeLocation location, String value) {
+    super(requireNonNull(location, "location is null"));
+    requireNonNull(value, "value is null");
+    checkArgument(value.toLowerCase(ENGLISH).equals("true") || 
value.toLowerCase(ENGLISH).equals("false"));
+
+    this.value = value.toLowerCase(ENGLISH).equals("true");
+  }
+
+  public boolean getValue() {
+    return value;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitBooleanLiteral(this, context);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(value);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    BooleanLiteral other = (BooleanLiteral) obj;
+    return Objects.equals(this.value, other.value);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    return value == ((BooleanLiteral) other).value;
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/DecimalLiteral.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/DecimalLiteral.java
new file mode 100644
index 00000000000..21b217c207a
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/DecimalLiteral.java
@@ -0,0 +1,77 @@
+/*
+ * 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.tree;
+
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class DecimalLiteral extends Literal {
+
+  private final String value;
+
+  public DecimalLiteral(String value) {
+    super(null);
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public DecimalLiteral(NodeLocation location, String value) {
+    super(requireNonNull(location, "location is null"));
+
+    this.value = requireNonNull(value, "value is null");
+
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitDecimalLiteral(this, context);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    DecimalLiteral that = (DecimalLiteral) o;
+    return Objects.equals(value, that.value);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(value);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    DecimalLiteral otherLiteral = (DecimalLiteral) other;
+    return value.equals(otherLiteral.value);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/DoubleLiteral.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/DoubleLiteral.java
new file mode 100644
index 00000000000..7d911f32a32
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/DoubleLiteral.java
@@ -0,0 +1,80 @@
+/*
+ * 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+public class DoubleLiteral extends Literal {
+
+  private final double value;
+
+  public DoubleLiteral(String value) {
+    super(null);
+    this.value = Double.parseDouble(requireNonNull(value, "value is null"));
+  }
+
+  public DoubleLiteral(NodeLocation location, String value) {
+    super(requireNonNull(location, "location is null"));
+    this.value = Double.parseDouble(requireNonNull(value, "value is null"));
+  }
+
+  public double getValue() {
+    return value;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitDoubleLiteral(this, context);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    DoubleLiteral that = (DoubleLiteral) o;
+
+    if (Double.compare(that.value, value) != 0) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @SuppressWarnings("UnaryPlus")
+  @Override
+  public int hashCode() {
+    long temp = value != +0.0d ? Double.doubleToLongBits(value) : 0L;
+    return (int) (temp ^ (temp >>> 32));
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    return value == ((DoubleLiteral) other).value;
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Expression.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Expression.java
index 2e401eaf4cd..98aee549f2e 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Expression.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Expression.java
@@ -19,6 +19,8 @@
 
 package org.apache.iotdb.db.relational.sql.tree;
 
+import org.apache.iotdb.db.relational.sql.util.ExpressionFormatter;
+
 import javax.annotation.Nullable;
 
 public abstract class Expression extends Node {
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/GenericLiteral.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/GenericLiteral.java
new file mode 100644
index 00000000000..017c0272956
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/GenericLiteral.java
@@ -0,0 +1,102 @@
+/*
+ * 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.tree;
+
+import org.apache.iotdb.db.relational.sql.parser.ParsingException;
+
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class GenericLiteral extends Literal {
+
+  private final String type;
+  private final String value;
+
+  public GenericLiteral(String type, String value) {
+    super(null);
+    this.type = requireNonNull(type, "type is null");
+
+    if (type.equalsIgnoreCase("X")) {
+      // we explicitly disallow "X" as type name, so if the user arrived here,
+      // it must be because that he intended to give a binaryLiteral instead, 
but
+      // added whitespace between the X and quote
+      throw new ParsingException("Spaces are not allowed between 'X' and the 
starting quote of a binary literal");
+    }
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public GenericLiteral(NodeLocation location, String type, String value) {
+    super(requireNonNull(location, "location is null"));
+    this.type = requireNonNull(type, "type is null");
+
+    if (type.equalsIgnoreCase("X")) {
+      // we explicitly disallow "X" as type name, so if the user arrived here,
+      // it must be because that he intended to give a binaryLiteral instead, 
but
+      // added whitespace between the X and quote
+      throw new ParsingException("Spaces are not allowed between 'X' and the 
starting quote of a binary literal",
+          location);
+    }
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitGenericLiteral(this, context);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(value, type);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+
+    GenericLiteral other = (GenericLiteral) obj;
+    return Objects.equals(this.value, other.value) &&
+        Objects.equals(this.type, other.type);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    GenericLiteral otherLiteral = (GenericLiteral) other;
+
+    return value.equals(otherLiteral.value) && type.equals(otherLiteral.type);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/IsNotNullPredicate.java
similarity index 50%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/IsNotNullPredicate.java
index b61b0bbb9cd..8b8389982e0 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/IsNotNullPredicate.java
@@ -21,22 +21,59 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class IsNotNullPredicate extends Expression {
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  private final Expression value;
+
+  public IsNotNullPredicate(Expression value) {
+    super(null);
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public IsNotNullPredicate(NodeLocation location, Expression value) {
+    super(requireNonNull(location, "location is null"));
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public Expression getValue() {
+    return value;
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+    return visitor.visitIsNotNullPredicate(this, context);
   }
 
   @Override
   public List<Node> getChildren() {
-    return ImmutableList.of();
+    return ImmutableList.of(value);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    IsNotNullPredicate that = (IsNotNullPredicate) o;
+    return Objects.equals(value, that.value);
+  }
+
+  @Override
+  public int hashCode() {
+    return value.hashCode();
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/IsNullPredicate.java
similarity index 50%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/IsNullPredicate.java
index b61b0bbb9cd..81de5b31f01 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/IsNullPredicate.java
@@ -21,22 +21,59 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class IsNullPredicate extends Expression {
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  private final Expression value;
+
+  public IsNullPredicate(Expression value) {
+    super(null);
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public IsNullPredicate(NodeLocation location, Expression value) {
+    super(requireNonNull(location, "location is null"));
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public Expression getValue() {
+    return value;
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+    return visitor.visitIsNullPredicate(this, context);
   }
 
   @Override
   public List<Node> getChildren() {
-    return ImmutableList.of();
+    return ImmutableList.of(value);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    IsNullPredicate that = (IsNullPredicate) o;
+    return Objects.equals(value, that.value);
+  }
+
+  @Override
+  public int hashCode() {
+    return value.hashCode();
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Join.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Join.java
new file mode 100644
index 00000000000..cb1efad1f78
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Join.java
@@ -0,0 +1,154 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class Join extends Relation {
+
+  public enum Type {
+    CROSS, INNER, LEFT, RIGHT, FULL, IMPLICIT
+  }
+
+  private final Type type;
+  private final Relation left;
+  private final Relation right;
+
+  @Nullable
+  private final JoinCriteria criteria;
+
+  public Join(Type type, Relation left, Relation right) {
+    super(null);
+    this.criteria = null;
+    checkArgument((type == Type.CROSS) || (type == Type.IMPLICIT), "No join 
criteria specified");
+    this.type = type;
+    this.left = requireNonNull(left, "left is null");
+    this.right = requireNonNull(right, "right is null");
+  }
+
+  public Join(NodeLocation location, Type type, Relation left, Relation right) 
{
+    super(requireNonNull(location, "location is null"));
+    this.criteria = null;
+    checkArgument((type == Type.CROSS) || (type == Type.IMPLICIT), "No join 
criteria specified");
+    this.type = type;
+    this.left = requireNonNull(left, "left is null");
+    this.right = requireNonNull(right, "right is null");
+  }
+
+  public Join(Type type, Relation left, Relation right, JoinCriteria criteria) 
{
+    super(null);
+    this.criteria = requireNonNull(criteria, "criteria is null");
+    checkArgument(!((type == Type.CROSS) || (type == Type.IMPLICIT)), "%s join 
cannot have join criteria", type);
+    this.type = type;
+    this.left = requireNonNull(left, "left is null");
+    this.right = requireNonNull(right, "right is null");
+  }
+
+  public Join(NodeLocation location, Type type, Relation left, Relation right, 
JoinCriteria criteria) {
+    super(requireNonNull(location, "location is null"));
+    this.criteria = requireNonNull(criteria, "criteria is null");
+    checkArgument(!((type == Type.CROSS) || (type == Type.IMPLICIT)), "%s join 
cannot have join criteria", type);
+    this.type = type;
+    this.left = requireNonNull(left, "left is null");
+    this.right = requireNonNull(right, "right is null");
+  }
+
+  public Type getType() {
+    return type;
+  }
+
+  public Relation getLeft() {
+    return left;
+  }
+
+  public Relation getRight() {
+    return right;
+  }
+
+  public Optional<JoinCriteria> getCriteria() {
+    return Optional.ofNullable(criteria);
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitJoin(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    ImmutableList.Builder<Node> nodes = ImmutableList.builder();
+    nodes.add(left);
+    nodes.add(right);
+    if (criteria != null) {
+      nodes.addAll(criteria.getNodes());
+    }
+    return nodes.build();
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("type", type)
+        .add("left", left)
+        .add("right", right)
+        .add("criteria", criteria)
+        .omitNullValues()
+        .toString();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if ((o == null) || (getClass() != o.getClass())) {
+      return false;
+    }
+    Join join = (Join) o;
+    return (type == join.type) &&
+        Objects.equals(left, join.left) &&
+        Objects.equals(right, join.right) &&
+        Objects.equals(criteria, join.criteria);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(type, left, right, criteria);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    return type.equals(((Join) other).type);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinCriteria.java
similarity index 69%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinCriteria.java
index b61b0bbb9cd..155998839bc 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinCriteria.java
@@ -19,24 +19,19 @@
 
 package org.apache.iotdb.db.relational.sql.tree;
 
-import com.google.common.collect.ImmutableList;
-
-import javax.annotation.Nullable;
-
 import java.util.List;
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
-  }
+public abstract class JoinCriteria {
 
+  // Force subclasses to have a proper equals and hashcode implementation
   @Override
-  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
-  }
+  public abstract boolean equals(Object obj);
 
   @Override
-  public List<Node> getChildren() {
-    return ImmutableList.of();
-  }
+  public abstract int hashCode();
+
+  @Override
+  public abstract String toString();
+
+  public abstract List<Node> getNodes();
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinOn.java
similarity index 52%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinOn.java
index b61b0bbb9cd..7be2c93decb 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinOn.java
@@ -21,22 +21,50 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static java.util.Objects.requireNonNull;
+
+public class JoinOn extends JoinCriteria {
+
+  private final Expression expression;
+
+  public JoinOn(Expression expression) {
+    this.expression = requireNonNull(expression, "expression is null");
+  }
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  public Expression getExpression() {
+    return expression;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if ((obj == null) || (getClass() != obj.getClass())) {
+      return false;
+    }
+    JoinOn o = (JoinOn) obj;
+    return Objects.equals(expression, o.expression);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(expression);
   }
 
   @Override
-  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+  public String toString() {
+    return toStringHelper(this)
+        .addValue(expression)
+        .toString();
   }
 
   @Override
-  public List<Node> getChildren() {
-    return ImmutableList.of();
+  public List<Node> getNodes() {
+    return ImmutableList.of(expression);
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinUsing.java
similarity index 50%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinUsing.java
index b61b0bbb9cd..0e560d6d403 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/JoinUsing.java
@@ -21,22 +21,52 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class JoinUsing extends JoinCriteria {
+  private final List<Identifier> columns;
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  public JoinUsing(List<Identifier> columns) {
+    requireNonNull(columns, "columns is null");
+    checkArgument(!columns.isEmpty(), "columns is empty");
+    this.columns = ImmutableList.copyOf(columns);
+  }
+
+  public List<Identifier> getColumns() {
+    return columns;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    JoinUsing o = (JoinUsing) obj;
+    return Objects.equals(columns, o.columns);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(columns);
   }
 
   @Override
-  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+  public String toString() {
+    return toStringHelper(this)
+        .addValue(columns)
+        .toString();
   }
 
   @Override
-  public List<Node> getChildren() {
+  public List<Node> getNodes() {
     return ImmutableList.of();
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LikePredicate.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LikePredicate.java
new file mode 100644
index 00000000000..66fd2798278
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LikePredicate.java
@@ -0,0 +1,107 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+public class LikePredicate extends Expression {
+
+  private final Expression value;
+  private final Expression pattern;
+  @Nullable
+  private final Expression escape;
+
+  public LikePredicate(Expression value, Expression pattern, Expression 
escape) {
+    super(null);
+    this.value = requireNonNull(value, "value is null");
+    this.pattern = requireNonNull(pattern, "pattern is null");
+    this.escape = requireNonNull(escape, "escape is null");
+  }
+
+  public LikePredicate(NodeLocation location, Expression value, Expression 
pattern) {
+    super(requireNonNull(location, "location is null"));
+    this.value = requireNonNull(value, "value is null");
+    this.pattern = requireNonNull(pattern, "pattern is null");
+    this.escape = null;
+  }
+
+  public Expression getValue() {
+    return value;
+  }
+
+  public Expression getPattern() {
+    return pattern;
+  }
+
+  public Optional<Expression> getEscape() {
+    return Optional.ofNullable(escape);
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitLikePredicate(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    ImmutableList.Builder<Node> result = ImmutableList.<Node>builder()
+        .add(value)
+        .add(pattern);
+
+    if (escape != null) {
+      result.add(escape);
+    }
+
+    return result.build();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    LikePredicate that = (LikePredicate) o;
+    return Objects.equals(value, that.value) &&
+        Objects.equals(pattern, that.pattern) &&
+        Objects.equals(escape, that.escape);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(value, pattern, escape);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Limit.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Limit.java
new file mode 100644
index 00000000000..c554e03ba19
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Limit.java
@@ -0,0 +1,98 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class Limit extends Node {
+  private final Expression rowCount;
+
+  public Limit(Expression rowCount) {
+    super(null);
+    checkArgument(
+        rowCount instanceof AllRows ||
+            rowCount instanceof LongLiteral ||
+            rowCount instanceof Parameter,
+        "unexpected rowCount class: %s",
+        rowCount.getClass().getSimpleName());
+    this.rowCount = rowCount;
+  }
+
+  public Limit(NodeLocation location, Expression rowCount) {
+    super(requireNonNull(location, "location is null"));
+    checkArgument(
+        rowCount instanceof AllRows ||
+            rowCount instanceof LongLiteral ||
+            rowCount instanceof Parameter,
+        "unexpected rowCount class: %s",
+        rowCount.getClass().getSimpleName());
+    this.rowCount = rowCount;
+  }
+
+  public Expression getRowCount() {
+    return rowCount;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitLimit(this, context);
+  }
+
+  @Override
+  public List<? extends Node> getChildren() {
+    return ImmutableList.of(rowCount);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if ((obj == null) || (getClass() != obj.getClass())) {
+      return false;
+    }
+    Limit o = (Limit) obj;
+    return Objects.equals(rowCount, o.rowCount);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(rowCount);
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("limit", rowCount)
+        .toString();
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
index b61b0bbb9cd..5e6e582c725 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
@@ -21,9 +21,8 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import javax.annotation.Nullable;
 
 public abstract class Literal extends Expression {
   protected Literal(@Nullable NodeLocation location) {
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LogicalExpression.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LogicalExpression.java
new file mode 100644
index 00000000000..efda0e5c1ae
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LogicalExpression.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class LogicalExpression extends Expression {
+
+  public enum Operator {
+    AND, OR;
+
+    public Operator flip() {
+      if (this == AND) {
+        return OR;
+      } else if (this == OR) {
+        return AND;
+      } else {
+        throw new IllegalArgumentException("Unsupported logical expression 
type: " + this);
+      }
+    }
+  }
+
+  private final Operator operator;
+  private final List<Expression> terms;
+
+  public LogicalExpression(Operator operator, List<Expression> terms) {
+    super(null);
+    this.operator = requireNonNull(operator, "operator is null");
+    checkArgument(terms.size() >= 2, "Expected at least 2 terms");
+    this.terms = ImmutableList.copyOf(terms);
+  }
+
+  public LogicalExpression(NodeLocation location, Operator operator, 
List<Expression> terms) {
+    super(requireNonNull(location, "location is null"));
+    this.operator = requireNonNull(operator, "operator is null");
+    checkArgument(terms.size() >= 2, "Expected at least 2 terms");
+    this.terms = ImmutableList.copyOf(terms);
+  }
+
+  public Operator getOperator() {
+    return operator;
+  }
+
+  public List<Expression> getTerms() {
+    return terms;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitLogicalExpression(this, context);
+  }
+
+  @Override
+  public List<? extends Node> getChildren() {
+    return terms;
+  }
+
+  public static LogicalExpression and(Expression left, Expression right) {
+    return new LogicalExpression(Operator.AND, ImmutableList.of(left, right));
+  }
+
+  public static LogicalExpression or(Expression left, Expression right) {
+    return new LogicalExpression(Operator.OR, ImmutableList.of(left, right));
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    LogicalExpression that = (LogicalExpression) o;
+    return operator == that.operator && Objects.equals(terms, that.terms);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(operator, terms);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    return operator == ((LogicalExpression) other).operator;
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LongLiteral.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LongLiteral.java
new file mode 100644
index 00000000000..88472eb1f25
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/LongLiteral.java
@@ -0,0 +1,109 @@
+/*
+ * 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.tree;
+
+import org.apache.iotdb.db.relational.sql.parser.ParsingException;
+
+import static java.util.Objects.requireNonNull;
+
+public class LongLiteral extends Literal {
+
+  private final String value;
+  private final long parsedValue;
+
+  public LongLiteral(String value) {
+    super(null);
+    try {
+      this.value = value;
+      this.parsedValue = parse(value);
+    } catch (NumberFormatException e) {
+      throw new ParsingException("Invalid numeric literal: " + value);
+    }
+  }
+
+  public LongLiteral(NodeLocation location, String value) {
+    super(requireNonNull(location, "location is null"));
+    try {
+      this.value = value;
+      this.parsedValue = parse(value);
+    } catch (NumberFormatException e) {
+      throw new ParsingException("Invalid numeric literal: " + value, 
location);
+    }
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  public long getParsedValue() {
+    return parsedValue;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitLongLiteral(this, context);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    LongLiteral that = (LongLiteral) o;
+
+    if (parsedValue != that.parsedValue) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return (int) (parsedValue ^ (parsedValue >>> 32));
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    return parsedValue == ((LongLiteral) other).parsedValue;
+  }
+
+  private static long parse(String value) {
+    value = value.replace("_", "");
+
+    if (value.startsWith("0x") || value.startsWith("0X")) {
+      return Long.parseLong(value.substring(2), 16);
+    } else if (value.startsWith("0b") || value.startsWith("0B")) {
+      return Long.parseLong(value.substring(2), 2);
+    } else if (value.startsWith("0o") || value.startsWith("0O")) {
+      return Long.parseLong(value.substring(2), 8);
+    } else {
+      return Long.parseLong(value);
+    }
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NaturalJoin.java
similarity index 71%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NaturalJoin.java
index b61b0bbb9cd..3cb6d405077 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NaturalJoin.java
@@ -21,22 +21,31 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+public class NaturalJoin extends JoinCriteria {
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    return (obj != null) && (getClass() == obj.getClass());
+  }
+
+  @Override
+  public int hashCode() {
+    return getClass().hashCode();
   }
 
   @Override
-  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+  public String toString() {
+    return toStringHelper(this).toString();
   }
 
   @Override
-  public List<Node> getChildren() {
+  public List<Node> getNodes() {
     return ImmutableList.of();
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Node.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Node.java
index 96797b308f3..7c86b9bc0bd 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Node.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Node.java
@@ -61,7 +61,7 @@ public abstract class Node {
     throw new UnsupportedOperationException("not yet implemented: " + 
getClass().getName());
   }
 
-  static boolean sameClass(Node left, Node right) {
+  public static boolean sameClass(Node left, Node right) {
     if (left == right) {
       return true;
     }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NotExpression.java
similarity index 50%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NotExpression.java
index b61b0bbb9cd..dd425122c9a 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NotExpression.java
@@ -21,22 +21,59 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class NotExpression extends Expression {
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  private final Expression value;
+
+  public NotExpression(Expression value) {
+    super(null);
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public NotExpression(NodeLocation location, Expression value) {
+    super(requireNonNull(location, "location is null"));
+    this.value = requireNonNull(value, "value is null");
+  }
+
+  public Expression getValue() {
+    return value;
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+    return visitor.visitNotExpression(this, context);
   }
 
   @Override
   public List<Node> getChildren() {
-    return ImmutableList.of();
+    return ImmutableList.of(value);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    NotExpression that = (NotExpression) o;
+    return Objects.equals(value, that.value);
+  }
+
+  @Override
+  public int hashCode() {
+    return value.hashCode();
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NullLiteral.java
similarity index 60%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NullLiteral.java
index b61b0bbb9cd..167cd54ddc8 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/NullLiteral.java
@@ -19,24 +19,42 @@
 
 package org.apache.iotdb.db.relational.sql.tree;
 
-import com.google.common.collect.ImmutableList;
+import static java.util.Objects.requireNonNull;
 
-import javax.annotation.Nullable;
+public class NullLiteral extends Literal {
 
-import java.util.List;
+  public NullLiteral() {
+    super(null);
+  }
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  public NullLiteral(NodeLocation location) {
+    super(requireNonNull(location, "location is null"));
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+    return visitor.visitNullLiteral(this, context);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return getClass().hashCode();
   }
 
   @Override
-  public List<Node> getChildren() {
-    return ImmutableList.of();
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Offset.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Offset.java
new file mode 100644
index 00000000000..888e263bda4
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Offset.java
@@ -0,0 +1,95 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class Offset extends Node {
+
+  private final Expression rowCount;
+
+  public Offset(Expression rowCount) {
+    super(null);
+    checkArgument(rowCount instanceof LongLiteral || rowCount instanceof 
Parameter,
+        "unexpected rowCount class: %s",
+        rowCount.getClass().getSimpleName());
+    this.rowCount = rowCount;
+  }
+
+  public Offset(NodeLocation location, Expression rowCount) {
+    super(requireNonNull(location, "location is null"));
+
+    checkArgument(rowCount instanceof LongLiteral || rowCount instanceof 
Parameter,
+        "unexpected rowCount class: %s",
+        rowCount.getClass().getSimpleName());
+    this.rowCount = rowCount;
+  }
+
+  public Expression getRowCount() {
+    return rowCount;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitOffset(this, context);
+  }
+
+  @Override
+  public List<? extends Node> getChildren() {
+    return ImmutableList.of(rowCount);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if ((obj == null) || (getClass() != obj.getClass())) {
+      return false;
+    }
+    Offset o = (Offset) obj;
+    return Objects.equals(rowCount, o.rowCount);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(rowCount);
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("rowCount", rowCount)
+        .toString();
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/OrderBy.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/OrderBy.java
new file mode 100644
index 00000000000..952806d3e84
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/OrderBy.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class OrderBy extends Node {
+
+  private final List<SortItem> sortItems;
+
+  public OrderBy(List<SortItem> sortItems) {
+    super(null);
+    requireNonNull(sortItems, "sortItems is null");
+    checkArgument(!sortItems.isEmpty(), "sortItems should not be empty");
+    this.sortItems = ImmutableList.copyOf(sortItems);
+  }
+
+  public OrderBy(NodeLocation location, List<SortItem> sortItems) {
+    super(requireNonNull(location, "location is null"));
+    requireNonNull(sortItems, "sortItems is null");
+    checkArgument(!sortItems.isEmpty(), "sortItems should not be empty");
+    this.sortItems = ImmutableList.copyOf(sortItems);
+  }
+
+  public List<SortItem> getSortItems() {
+    return sortItems;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitOrderBy(this, context);
+  }
+
+  @Override
+  public List<? extends Node> getChildren() {
+    return sortItems;
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("sortItems", sortItems)
+        .toString();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if ((obj == null) || (getClass() != obj.getClass())) {
+      return false;
+    }
+    OrderBy o = (OrderBy) obj;
+    return Objects.equals(sortItems, o.sortItems);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(sortItems);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Parameter.java
similarity index 54%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Parameter.java
index b61b0bbb9cd..ce2dd025b35 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Parameter.java
@@ -21,22 +21,62 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class Parameter extends Expression {
+  private final int id;
+
+  public Parameter(int id) {
+    super(null);
+    this.id = id;
+  }
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  public Parameter(NodeLocation location, int id) {
+    super(requireNonNull(location, "location is null"));
+    this.id = id;
+  }
+
+  public int getId() {
+    return id;
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+    return visitor.visitParameter(this, context);
   }
 
   @Override
   public List<Node> getChildren() {
     return ImmutableList.of();
   }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    Parameter that = (Parameter) o;
+    return Objects.equals(id, that.id);
+  }
+
+  @Override
+  public int hashCode() {
+    return id;
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    return id == ((Parameter) other).id;
+  }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QuantifiedComparisonExpression.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QuantifiedComparisonExpression.java
new file mode 100644
index 00000000000..c6ec38a87d8
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QuantifiedComparisonExpression.java
@@ -0,0 +1,116 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class QuantifiedComparisonExpression extends Expression {
+
+  public enum Quantifier {
+    ALL,
+    ANY,
+    SOME,
+  }
+
+  private final ComparisonExpression.Operator operator;
+  private final Quantifier quantifier;
+  private final Expression value;
+  private final Expression subquery;
+
+  public QuantifiedComparisonExpression(ComparisonExpression.Operator 
operator, Quantifier quantifier, Expression value,
+                                        Expression subquery) {
+    super(null);
+    this.operator = requireNonNull(operator, "operator is null");
+    this.quantifier = requireNonNull(quantifier, "quantifier is null");
+    this.value = requireNonNull(value, "value is null");
+    this.subquery = requireNonNull(subquery, "subquery is null");
+  }
+
+  public QuantifiedComparisonExpression(NodeLocation location, 
ComparisonExpression.Operator operator,
+                                        Quantifier quantifier, Expression 
value, Expression subquery) {
+    super(requireNonNull(location, "location is null"));
+    this.operator = requireNonNull(operator, "operator is null");
+    this.quantifier = requireNonNull(quantifier, "quantifier is null");
+    this.value = requireNonNull(value, "value is null");
+    this.subquery = requireNonNull(subquery, "subquery is null");
+  }
+
+  public ComparisonExpression.Operator getOperator() {
+    return operator;
+  }
+
+  public Quantifier getQuantifier() {
+    return quantifier;
+  }
+
+  public Expression getValue() {
+    return value;
+  }
+
+  public Expression getSubquery() {
+    return subquery;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitQuantifiedComparisonExpression(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    return ImmutableList.of(value, subquery);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    QuantifiedComparisonExpression that = (QuantifiedComparisonExpression) o;
+    return operator == that.operator &&
+        quantifier == that.quantifier &&
+        Objects.equals(value, that.value) &&
+        Objects.equals(subquery, that.subquery);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(operator, quantifier, value, subquery);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    QuantifiedComparisonExpression otherNode = 
(QuantifiedComparisonExpression) other;
+    return operator == otherNode.operator && quantifier == 
otherNode.quantifier;
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Query.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Query.java
new file mode 100644
index 00000000000..016e2846c42
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Query.java
@@ -0,0 +1,159 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class Query extends Statement {
+
+  private final Optional<With> with;
+  private final QueryBody queryBody;
+  private final Optional<OrderBy> orderBy;
+  private final Optional<Offset> offset;
+  private final Optional<Node> limit;
+
+  public Query(
+      Optional<With> with,
+      QueryBody queryBody,
+      Optional<OrderBy> orderBy,
+      Optional<Offset> offset,
+      Optional<Node> limit) {
+    this(Optional.empty(), with, queryBody, orderBy, offset, limit);
+  }
+
+  public Query(
+      NodeLocation location,
+      Optional<With> with,
+      QueryBody queryBody,
+      Optional<OrderBy> orderBy,
+      Optional<Offset> offset,
+      Optional<Node> limit) {
+    this(Optional.of(location), with, queryBody, orderBy, offset, limit);
+  }
+
+  private Query(
+      Optional<NodeLocation> location,
+      Optional<With> with,
+      QueryBody queryBody,
+      Optional<OrderBy> orderBy,
+      Optional<Offset> offset,
+      Optional<Node> limit) {
+    super(location);
+    requireNonNull(with, "with is null");
+    requireNonNull(queryBody, "queryBody is null");
+    requireNonNull(orderBy, "orderBy is null");
+    requireNonNull(offset, "offset is null");
+    requireNonNull(limit, "limit is null");
+    checkArgument(!limit.isPresent() || limit.get() instanceof FetchFirst || 
limit.get() instanceof Limit,
+        "limit must be optional of either FetchFirst or Limit type");
+
+    this.with = with;
+    this.queryBody = queryBody;
+    this.orderBy = orderBy;
+    this.offset = offset;
+    this.limit = limit;
+  }
+
+  public Optional<With> getWith() {
+    return with;
+  }
+
+  public QueryBody getQueryBody() {
+    return queryBody;
+  }
+
+  public Optional<OrderBy> getOrderBy() {
+    return orderBy;
+  }
+
+  public Optional<Offset> getOffset() {
+    return offset;
+  }
+
+  public Optional<Node> getLimit() {
+    return limit;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitQuery(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    ImmutableList.Builder<Node> nodes = ImmutableList.builder();
+    nodes.addAll(functions);
+    with.ifPresent(nodes::add);
+    nodes.add(queryBody);
+    orderBy.ifPresent(nodes::add);
+    offset.ifPresent(nodes::add);
+    limit.ifPresent(nodes::add);
+    return nodes.build();
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("functions", functions.isEmpty() ? null : functions)
+        .add("with", with.orElse(null))
+        .add("queryBody", queryBody)
+        .add("orderBy", orderBy)
+        .add("offset", offset.orElse(null))
+        .add("limit", limit.orElse(null))
+        .omitNullValues()
+        .toString();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if ((obj == null) || (getClass() != obj.getClass())) {
+      return false;
+    }
+    Query o = (Query) obj;
+    return Objects.equals(functions, o.functions) &&
+        Objects.equals(with, o.with) &&
+        Objects.equals(queryBody, o.queryBody) &&
+        Objects.equals(orderBy, o.orderBy) &&
+        Objects.equals(offset, o.offset) &&
+        Objects.equals(limit, o.limit);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(functions, with, queryBody, orderBy, offset, limit);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QueryBody.java
similarity index 76%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QueryBody.java
index b61b0bbb9cd..4ccfd29f6f6 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QueryBody.java
@@ -19,24 +19,17 @@
 
 package org.apache.iotdb.db.relational.sql.tree;
 
-import com.google.common.collect.ImmutableList;
-
 import javax.annotation.Nullable;
 
-import java.util.List;
+public abstract class QueryBody extends Relation {
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
+  protected QueryBody(@Nullable NodeLocation location) {
     super(location);
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+    return visitor.visitQueryBody(this, context);
   }
 
-  @Override
-  public List<Node> getChildren() {
-    return ImmutableList.of();
-  }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QuerySpecification.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QuerySpecification.java
new file mode 100644
index 00000000000..55b91452064
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/QuerySpecification.java
@@ -0,0 +1,198 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class QuerySpecification extends QueryBody {
+
+  private final Select select;
+  private final Optional<Relation> from;
+  private final Optional<Expression> where;
+  private final Optional<GroupBy> groupBy;
+  private final Optional<Expression> having;
+  private final Optional<OrderBy> orderBy;
+  private final Optional<Offset> offset;
+  private final Optional<Node> limit;
+
+  public QuerySpecification(
+      Select select,
+      Optional<Relation> from,
+      Optional<Expression> where,
+      Optional<GroupBy> groupBy,
+      Optional<Expression> having,
+      Optional<OrderBy> orderBy,
+      Optional<Offset> offset,
+      Optional<Node> limit) {
+    this(Optional.empty(), select, from, where, groupBy, having, orderBy, 
offset, limit);
+  }
+
+  public QuerySpecification(
+      NodeLocation location,
+      Select select,
+      Optional<Relation> from,
+      Optional<Expression> where,
+      Optional<GroupBy> groupBy,
+      Optional<Expression> having,
+      Optional<OrderBy> orderBy,
+      Optional<Offset> offset,
+      Optional<Node> limit) {
+    this(Optional.of(location), select, from, where, groupBy, having, orderBy, 
offset, limit);
+  }
+
+  private QuerySpecification(
+      Optional<NodeLocation> location,
+      Select select,
+      Optional<Relation> from,
+      Optional<Expression> where,
+      Optional<GroupBy> groupBy,
+      Optional<Expression> having,
+      Optional<OrderBy> orderBy,
+      Optional<Offset> offset,
+      Optional<Limit> limit) {
+    super(location);
+    requireNonNull(select, "select is null");
+    requireNonNull(from, "from is null");
+    requireNonNull(where, "where is null");
+    requireNonNull(groupBy, "groupBy is null");
+    requireNonNull(having, "having is null");
+    requireNonNull(orderBy, "orderBy is null");
+    requireNonNull(offset, "offset is null");
+    requireNonNull(limit, "limit is null");
+
+    this.select = select;
+    this.from = from;
+    this.where = where;
+    this.groupBy = groupBy;
+    this.having = having;
+    this.orderBy = orderBy;
+    this.offset = offset;
+    this.limit = limit;
+  }
+
+  public Select getSelect() {
+    return select;
+  }
+
+  public Optional<Relation> getFrom() {
+    return from;
+  }
+
+  public Optional<Expression> getWhere() {
+    return where;
+  }
+
+  public Optional<GroupBy> getGroupBy() {
+    return groupBy;
+  }
+
+  public Optional<Expression> getHaving() {
+    return having;
+  }
+
+  public List<WindowDefinition> getWindows() {
+    return windows;
+  }
+
+  public Optional<OrderBy> getOrderBy() {
+    return orderBy;
+  }
+
+  public Optional<Offset> getOffset() {
+    return offset;
+  }
+
+  public Optional<Node> getLimit() {
+    return limit;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitQuerySpecification(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    ImmutableList.Builder<Node> nodes = ImmutableList.builder();
+    nodes.add(select);
+    from.ifPresent(nodes::add);
+    where.ifPresent(nodes::add);
+    groupBy.ifPresent(nodes::add);
+    having.ifPresent(nodes::add);
+    nodes.addAll(windows);
+    orderBy.ifPresent(nodes::add);
+    offset.ifPresent(nodes::add);
+    limit.ifPresent(nodes::add);
+    return nodes.build();
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("select", select)
+        .add("from", from)
+        .add("where", where.orElse(null))
+        .add("groupBy", groupBy)
+        .add("having", having.orElse(null))
+        .add("windows", windows.isEmpty() ? null : windows)
+        .add("orderBy", orderBy)
+        .add("offset", offset.orElse(null))
+        .add("limit", limit.orElse(null))
+        .toString();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if ((obj == null) || (getClass() != obj.getClass())) {
+      return false;
+    }
+    QuerySpecification o = (QuerySpecification) obj;
+    return Objects.equals(select, o.select) &&
+        Objects.equals(from, o.from) &&
+        Objects.equals(where, o.where) &&
+        Objects.equals(groupBy, o.groupBy) &&
+        Objects.equals(having, o.having) &&
+        Objects.equals(windows, o.windows) &&
+        Objects.equals(orderBy, o.orderBy) &&
+        Objects.equals(offset, o.offset) &&
+        Objects.equals(limit, o.limit);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(select, from, where, groupBy, having, windows, 
orderBy, offset, limit);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Relation.java
similarity index 73%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Relation.java
index b61b0bbb9cd..964a1b82efb 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Relation.java
@@ -19,24 +19,14 @@
 
 package org.apache.iotdb.db.relational.sql.tree;
 
-import com.google.common.collect.ImmutableList;
+public abstract class Relation extends Node {
 
-import javax.annotation.Nullable;
-
-import java.util.List;
-
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
+  protected Relation(NodeLocation location) {
     super(location);
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
-  }
-
-  @Override
-  public List<Node> getChildren() {
-    return ImmutableList.of();
+    return visitor.visitRelation(this, context);
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SearchedCaseExpression.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SearchedCaseExpression.java
new file mode 100644
index 00000000000..c047cdf94d1
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SearchedCaseExpression.java
@@ -0,0 +1,107 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+public class SearchedCaseExpression extends Expression {
+  private final List<WhenClause> whenClauses;
+  @Nullable
+  private final Expression defaultValue;
+
+  public SearchedCaseExpression(List<WhenClause> whenClauses) {
+    super(null);
+    this.whenClauses = ImmutableList.copyOf(requireNonNull(whenClauses, 
"whenClauses is null"));
+    this.defaultValue = null;
+  }
+
+  public SearchedCaseExpression(List<WhenClause> whenClauses, Expression 
defaultValue) {
+    super(null);
+    this.whenClauses = ImmutableList.copyOf(requireNonNull(whenClauses, 
"whenClauses is null"));
+    this.defaultValue = requireNonNull(defaultValue, "defaultValue is null");
+  }
+
+  public SearchedCaseExpression(NodeLocation location, List<WhenClause> 
whenClauses) {
+    super(requireNonNull(location, "location is null"));
+    this.whenClauses = ImmutableList.copyOf(requireNonNull(whenClauses, 
"whenClauses is null"));
+    this.defaultValue = null;
+  }
+
+  public SearchedCaseExpression(NodeLocation location, List<WhenClause> 
whenClauses, Expression defaultValue) {
+    super(requireNonNull(location, "location is null"));
+    this.whenClauses = ImmutableList.copyOf(requireNonNull(whenClauses, 
"whenClauses is null"));
+    this.defaultValue = requireNonNull(defaultValue, "defaultValue is null");
+  }
+
+  public List<WhenClause> getWhenClauses() {
+    return whenClauses;
+  }
+
+  public Optional<Expression> getDefaultValue() {
+    return Optional.ofNullable(defaultValue);
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitSearchedCaseExpression(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    ImmutableList.Builder<Node> nodes = ImmutableList.builder();
+    nodes.addAll(whenClauses);
+    if (defaultValue != null) {
+      nodes.add(defaultValue);
+    }
+    return nodes.build();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    SearchedCaseExpression that = (SearchedCaseExpression) o;
+    return Objects.equals(whenClauses, that.whenClauses) &&
+        Objects.equals(defaultValue, that.defaultValue);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(whenClauses, defaultValue);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SimpleCaseExpression.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SimpleCaseExpression.java
new file mode 100644
index 00000000000..48fd0024903
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SimpleCaseExpression.java
@@ -0,0 +1,120 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+public class SimpleCaseExpression extends Expression {
+
+  private final Expression operand;
+  private final List<WhenClause> whenClauses;
+  @Nullable
+  private final Expression defaultValue;
+
+  public SimpleCaseExpression(Expression operand, List<WhenClause> 
whenClauses) {
+    super(null);
+    this.operand = requireNonNull(operand, "operand is null");
+    this.whenClauses = ImmutableList.copyOf(requireNonNull(whenClauses, 
"whenClauses is null"));
+    this.defaultValue = null;
+  }
+
+  public SimpleCaseExpression(Expression operand, List<WhenClause> 
whenClauses, Expression defaultValue) {
+    super(null);
+    this.operand = requireNonNull(operand, "operand is null");
+    this.whenClauses = ImmutableList.copyOf(requireNonNull(whenClauses, 
"whenClauses is null"));
+    this.defaultValue = requireNonNull(defaultValue, "defaultValue is null");
+  }
+
+  public SimpleCaseExpression(NodeLocation location, Expression operand, 
List<WhenClause> whenClauses) {
+    super(requireNonNull(location, "location is null"));
+    this.operand = requireNonNull(operand, "operand is null");
+    this.whenClauses = ImmutableList.copyOf(requireNonNull(whenClauses, 
"whenClauses is null"));
+    this.defaultValue = null;
+  }
+
+  public SimpleCaseExpression(NodeLocation location, Expression operand, 
List<WhenClause> whenClauses,
+                              Expression defaultValue) {
+    super(requireNonNull(location, "location is null"));
+    this.operand = requireNonNull(operand, "operand is null");
+    this.whenClauses = ImmutableList.copyOf(requireNonNull(whenClauses, 
"whenClauses is null"));
+    this.defaultValue = requireNonNull(defaultValue, "defaultValue is null");
+  }
+
+  public Expression getOperand() {
+    return operand;
+  }
+
+  public List<WhenClause> getWhenClauses() {
+    return whenClauses;
+  }
+
+  public Optional<Expression> getDefaultValue() {
+    return Optional.ofNullable(defaultValue);
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitSimpleCaseExpression(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    ImmutableList.Builder<Node> nodes = ImmutableList.builder();
+    nodes.add(operand);
+    nodes.addAll(whenClauses);
+    if (defaultValue != null) {
+      nodes.add(defaultValue);
+    }
+    return nodes.build();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    SimpleCaseExpression that = (SimpleCaseExpression) o;
+    return Objects.equals(operand, that.operand) &&
+        Objects.equals(whenClauses, that.whenClauses) &&
+        Objects.equals(defaultValue, that.defaultValue);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(operand, whenClauses, defaultValue);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SortItem.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SortItem.java
new file mode 100644
index 00000000000..d7bafd583d9
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SortItem.java
@@ -0,0 +1,118 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static java.util.Objects.requireNonNull;
+
+public class SortItem extends Node {
+
+  public enum Ordering {
+    ASCENDING, DESCENDING
+  }
+
+  public enum NullOrdering {
+    FIRST, LAST, UNDEFINED
+  }
+
+  private final Expression sortKey;
+  private final Ordering ordering;
+  private final NullOrdering nullOrdering;
+
+  public SortItem(Expression sortKey, Ordering ordering, NullOrdering 
nullOrdering) {
+    super(null);
+    this.ordering = ordering;
+    this.sortKey = sortKey;
+    this.nullOrdering = nullOrdering;
+  }
+
+  public SortItem(NodeLocation location, Expression sortKey, Ordering 
ordering, NullOrdering nullOrdering) {
+    super(requireNonNull(location, "location is null"));
+    this.ordering = ordering;
+    this.sortKey = sortKey;
+    this.nullOrdering = nullOrdering;
+  }
+
+  public Expression getSortKey() {
+    return sortKey;
+  }
+
+  public Ordering getOrdering() {
+    return ordering;
+  }
+
+  public NullOrdering getNullOrdering() {
+    return nullOrdering;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitSortItem(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    return ImmutableList.of(sortKey);
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("sortKey", sortKey)
+        .add("ordering", ordering)
+        .add("nullOrdering", nullOrdering)
+        .toString();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    SortItem sortItem = (SortItem) o;
+    return Objects.equals(sortKey, sortItem.sortKey) &&
+        (ordering == sortItem.ordering) &&
+        (nullOrdering == sortItem.nullOrdering);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(sortKey, ordering, nullOrdering);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    SortItem otherItem = (SortItem) other;
+    return ordering == otherItem.ordering && nullOrdering == 
otherItem.nullOrdering;
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/StringLiteral.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/StringLiteral.java
new file mode 100644
index 00000000000..f5012f36995
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/StringLiteral.java
@@ -0,0 +1,82 @@
+/*
+ * 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.tree;
+
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class StringLiteral extends Literal {
+
+  private final String value;
+  private final int length;
+
+  public StringLiteral(String value) {
+    super(null);
+    this.value = requireNonNull(value, "value is null");
+    this.length = value.codePointCount(0, value.length());
+  }
+
+  public StringLiteral(NodeLocation location, String value) {
+    super(requireNonNull(location, "location is null"));
+    this.value = requireNonNull(value, "value is null");
+    this.length = value.codePointCount(0, value.length());
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  public int length() {
+    return length;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitStringLiteral(this, context);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    StringLiteral that = (StringLiteral) o;
+    return Objects.equals(value, that.value);
+  }
+
+  @Override
+  public int hashCode() {
+    return value.hashCode();
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    return Objects.equals(value, ((StringLiteral) other).value);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SubqueryExpression.java
similarity index 50%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SubqueryExpression.java
index b61b0bbb9cd..6b6d0a8b8b3 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SubqueryExpression.java
@@ -21,22 +21,59 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class SubqueryExpression extends Expression {
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  private final Query query;
+
+  public SubqueryExpression(Query query) {
+    super(null);
+    this.query = requireNonNull(query, "query is null");
+  }
+
+  public SubqueryExpression(NodeLocation location, Query query) {
+    super(requireNonNull(location, "location is null"));
+    this.query = requireNonNull(query, "query is null");
+  }
+
+  public Query getQuery() {
+    return query;
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+    return visitor.visitSubqueryExpression(this, context);
   }
 
   @Override
   public List<Node> getChildren() {
-    return ImmutableList.of();
+    return ImmutableList.of(query);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    SubqueryExpression that = (SubqueryExpression) o;
+    return Objects.equals(query, that.query);
+  }
+
+  @Override
+  public int hashCode() {
+    return query.hashCode();
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
   }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SymbolReference.java
similarity index 63%
copy from 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
copy to 
iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SymbolReference.java
index b61b0bbb9cd..3d93a0dd33e 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Literal.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/SymbolReference.java
@@ -21,22 +21,46 @@ package org.apache.iotdb.db.relational.sql.tree;
 
 import com.google.common.collect.ImmutableList;
 
-import javax.annotation.Nullable;
-
 import java.util.List;
+import java.util.Objects;
+
+public class SymbolReference extends Expression {
+
+  private final String name;
 
-public abstract class Literal extends Expression {
-  protected Literal(@Nullable NodeLocation location) {
-    super(location);
+  public SymbolReference(String name) {
+    super(null);
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
   }
 
   @Override
   public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitLiteral(this, context);
+    return visitor.visitSymbolReference(this, context);
   }
 
   @Override
   public List<Node> getChildren() {
     return ImmutableList.of();
   }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    SymbolReference that = (SymbolReference) o;
+    return Objects.equals(name, that.name);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(name);
+  }
 }
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Trim.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Trim.java
new file mode 100644
index 00000000000..56ce66db797
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/Trim.java
@@ -0,0 +1,138 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+public class Trim extends Expression {
+  private final Specification specification;
+  private final Expression trimSource;
+  @Nullable
+  private final Expression trimCharacter;
+
+  public Trim(Specification specification, Expression trimSource) {
+    super(null);
+    this.specification = requireNonNull(specification, "specification is 
null");
+    this.trimSource = requireNonNull(trimSource, "trimSource is null");
+    this.trimCharacter = null;
+  }
+
+  public Trim(Specification specification, Expression trimSource, Expression 
trimCharacter) {
+    super(null);
+    this.specification = requireNonNull(specification, "specification is 
null");
+    this.trimSource = requireNonNull(trimSource, "trimSource is null");
+    this.trimCharacter = requireNonNull(trimCharacter, "trimCharacter is 
null");
+  }
+
+  public Trim(NodeLocation location, Specification specification, Expression 
trimSource) {
+    super(requireNonNull(location, "location is null"));
+    this.specification = requireNonNull(specification, "specification is 
null");
+    this.trimSource = requireNonNull(trimSource, "trimSource is null");
+    this.trimCharacter = null;
+  }
+
+  public Trim(NodeLocation location, Specification specification, Expression 
trimSource, Expression trimCharacter) {
+    super(requireNonNull(location, "location is null"));
+    this.specification = requireNonNull(specification, "specification is 
null");
+    this.trimSource = requireNonNull(trimSource, "trimSource is null");
+    this.trimCharacter = requireNonNull(trimCharacter, "trimCharacter is 
null");
+  }
+
+  public Specification getSpecification() {
+    return specification;
+  }
+
+  public Expression getTrimSource() {
+    return trimSource;
+  }
+
+  public Optional<Expression> getTrimCharacter() {
+    return Optional.ofNullable(trimCharacter);
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitTrim(this, context);
+  }
+
+  @Override
+  public List<? extends Node> getChildren() {
+    ImmutableList.Builder<Node> nodes = ImmutableList.builder();
+    nodes.add(trimSource);
+    if (trimCharacter != null) {
+      nodes.add(trimCharacter);
+    }
+    return nodes.build();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    Trim that = (Trim) o;
+    return specification == that.specification &&
+        Objects.equals(trimSource, that.trimSource) &&
+        Objects.equals(trimCharacter, that.trimCharacter);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(specification, trimSource, trimCharacter);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    Trim otherTrim = (Trim) other;
+    return specification == otherTrim.specification;
+  }
+
+  public enum Specification {
+    BOTH("trim"),
+    LEADING("ltrim"),
+    TRAILING("rtrim");
+
+    private final String functionName;
+
+    Specification(String functionName) {
+      this.functionName = requireNonNull(functionName, "functionName is null");
+    }
+
+    public String getFunctionName() {
+      return functionName;
+    }
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/WhenClause.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/WhenClause.java
new file mode 100644
index 00000000000..d2668c4ae88
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/WhenClause.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+public class WhenClause extends Expression {
+
+  private final Expression operand;
+  private final Expression result;
+
+  public WhenClause(Expression operand, Expression result) {
+    super(null);
+    this.operand = requireNonNull(operand, "operand is null");
+    this.result = requireNonNull(result, "result is null");
+  }
+
+  public WhenClause(NodeLocation location, Expression operand, Expression 
result) {
+    super(requireNonNull(location, "location is null"));
+    this.operand = requireNonNull(operand, "operand is null");
+    this.result = requireNonNull(result, "result is null");
+  }
+
+  public Expression getOperand() {
+    return operand;
+  }
+
+  public Expression getResult() {
+    return result;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitWhenClause(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    return ImmutableList.of(operand, result);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    WhenClause that = (WhenClause) o;
+    return Objects.equals(operand, that.operand) &&
+        Objects.equals(result, that.result);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(operand, result);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/With.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/With.java
new file mode 100644
index 00000000000..9b68fee7b9d
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/With.java
@@ -0,0 +1,105 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class With extends Node {
+
+  private final boolean recursive;
+  private final List<WithQuery> queries;
+
+  public With(boolean recursive, List<WithQuery> queries) {
+    super(null);
+
+    requireNonNull(queries, "queries is null");
+    checkArgument(!queries.isEmpty(), "queries is empty");
+
+    this.recursive = recursive;
+    this.queries = ImmutableList.copyOf(queries);
+  }
+
+  public With(NodeLocation location, boolean recursive, List<WithQuery> 
queries) {
+    super(requireNonNull(location, "location is null"));
+
+    requireNonNull(queries, "queries is null");
+    checkArgument(!queries.isEmpty(), "queries is empty");
+
+    this.recursive = recursive;
+    this.queries = ImmutableList.copyOf(queries);
+  }
+
+  public boolean isRecursive() {
+    return recursive;
+  }
+
+  public List<WithQuery> getQueries() {
+    return queries;
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitWith(this, context);
+  }
+
+  @Override
+  public List<? extends Node> getChildren() {
+    return queries;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if ((obj == null) || (getClass() != obj.getClass())) {
+      return false;
+    }
+    With o = (With) obj;
+    return Objects.equals(recursive, o.recursive) &&
+        Objects.equals(queries, o.queries);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(recursive, queries);
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("recursive", recursive)
+        .add("queries", queries)
+        .toString();
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    return sameClass(this, other);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/WithQuery.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/WithQuery.java
new file mode 100644
index 00000000000..a92e19f9e20
--- /dev/null
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/tree/WithQuery.java
@@ -0,0 +1,129 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static java.util.Objects.requireNonNull;
+
+public class WithQuery extends Node {
+
+  private final Identifier name;
+  private final Query query;
+
+  @Nullable
+  private final List<Identifier> columnNames;
+
+  public WithQuery(Identifier name, Query query) {
+    super(null);
+    this.name = name;
+    this.query = requireNonNull(query, "query is null");
+    this.columnNames = null;
+  }
+
+  public WithQuery(Identifier name, Query query, List<Identifier> columnNames) 
{
+    super(null);
+    this.name = name;
+    this.query = requireNonNull(query, "query is null");
+    this.columnNames = requireNonNull(columnNames, "columnNames is null");
+  }
+
+  public WithQuery(NodeLocation location, Identifier name, Query query) {
+    super(requireNonNull(location, "location is null"));
+    this.name = name;
+    this.query = requireNonNull(query, "query is null");
+    this.columnNames = null;
+  }
+
+  public WithQuery(NodeLocation location, Identifier name, Query query, 
List<Identifier> columnNames) {
+    super(requireNonNull(location, "location is null"));
+    this.name = name;
+    this.query = requireNonNull(query, "query is null");
+    this.columnNames = requireNonNull(columnNames, "columnNames is null");
+  }
+
+  public Identifier getName() {
+    return name;
+  }
+
+  public Query getQuery() {
+    return query;
+  }
+
+  public Optional<List<Identifier>> getColumnNames() {
+    return Optional.ofNullable(columnNames);
+  }
+
+  @Override
+  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+    return visitor.visitWithQuery(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    return ImmutableList.of(query);
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("name", name)
+        .add("query", query)
+        .add("columnNames", columnNames)
+        .omitNullValues()
+        .toString();
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(name, query, columnNames);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if ((obj == null) || (getClass() != obj.getClass())) {
+      return false;
+    }
+    WithQuery o = (WithQuery) obj;
+    return Objects.equals(name, o.name) &&
+        Objects.equals(query, o.query) &&
+        Objects.equals(columnNames, o.columnNames);
+  }
+
+  @Override
+  public boolean shallowEquals(Node other) {
+    if (!sameClass(this, other)) {
+      return false;
+    }
+
+    WithQuery otherRelation = (WithQuery) other;
+    return name.equals(otherRelation.name) && Objects.equals(columnNames, 
otherRelation.columnNames);
+  }
+}
diff --git 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/ExpressionFormatter.java
 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/ExpressionFormatter.java
index de9ac00e13c..d2fe0cc7e5c 100644
--- 
a/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/ExpressionFormatter.java
+++ 
b/iotdb-core/relational-parser/src/main/java/org/apache/iotdb/db/relational/sql/util/ExpressionFormatter.java
@@ -21,6 +21,8 @@ package org.apache.iotdb.db.relational.sql.util;
 
 import org.apache.iotdb.db.relational.sql.tree.AstVisitor;
 import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.Literal;
+import org.apache.iotdb.db.relational.sql.tree.SymbolReference;
 
 import java.util.Optional;
 import java.util.function.Function;

Reply via email to