You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ro...@apache.org on 2022/03/03 00:03:35 UTC

[iotdb] branch master updated: [IOTDB-1807] Compatibility of Apache IoTDB with InfluxDB - Parse InfluxQL (#4535)

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

rong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 07fb542  [IOTDB-1807] Compatibility of Apache IoTDB with InfluxDB - Parse InfluxQL (#4535)
07fb542 is described below

commit 07fb54250bd61430b1381ff50005f417372b1b72
Author: Xieqijun <44...@users.noreply.github.com>
AuthorDate: Thu Mar 3 08:02:09 2022 +0800

    [IOTDB-1807] Compatibility of Apache IoTDB with InfluxDB - Parse InfluxQL (#4535)
    
    Co-authored-by: Timcross-WEI <ti...@qq.com>
---
 .../apache/iotdb/db/qp/sql/InfluxDBSqlParser.g4    | 129 +++++++++
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   |   2 +-
 .../db/qp/sql/{IoTDBSqlLexer.g4 => SqlLexer.g4}    |   2 +-
 .../apache/iotdb/cli/IoTDBSyntaxHighlighter.java   |   4 +-
 .../org/apache/iotdb/cli/utils/JlineUtils.java     |   6 +-
 docs/zh/UserGuide/API/InfluxDB-Protocol.md         |   2 +-
 .../influxdb/constant/InfluxSQLConstant.java       |  57 ++++
 .../iotdb/db/protocol/influxdb/dto/IoTDBPoint.java |  10 +-
 .../influxdb/operator/InfluxQueryOperator.java     |  38 +++
 .../influxdb/operator/InfluxSelectComponent.java   |  94 +++++++
 .../influxdb/sql/InfluxDBLogicalGenerator.java     |  66 +++++
 .../protocol/influxdb/sql/InfluxDBSqlVisitor.java  | 297 +++++++++++++++++++++
 .../iotdb/db/qp/constant/FilterConstant.java       |  20 +-
 .../apache/iotdb/db/qp/constant/SQLConstant.java   |   3 +-
 .../iotdb/db/qp/logical/crud/SelectComponent.java  |   4 +-
 .../iotdb/db/qp/strategy/LogicalGenerator.java     |   6 +-
 .../influxdb/sql/InfluxDBLogicalGeneratorTest.java |  68 +++++
 thrift-influxdb/README.md                          |   2 +-
 18 files changed, 778 insertions(+), 32 deletions(-)

diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/InfluxDBSqlParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/InfluxDBSqlParser.g4
new file mode 100644
index 0000000..2df0017
--- /dev/null
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/InfluxDBSqlParser.g4
@@ -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.
+ */
+
+parser grammar InfluxDBSqlParser;
+
+options { tokenVocab=SqlLexer; }
+
+singleStatement
+    : statement SEMI? EOF
+    ;
+
+
+statement
+   : selectClause fromClause whereClause? #selectStatement
+   ;
+
+selectClause
+   : SELECT resultColumn (COMMA resultColumn)*
+   ;
+
+resultColumn
+   : expression (AS identifier)?
+   ;
+
+expression
+   : LR_BRACKET unaryInBracket=expression RR_BRACKET
+   | (PLUS | MINUS) unaryAfterSign=expression
+   | leftExpression=expression (STAR | DIV | MOD) rightExpression=expression
+   | leftExpression=expression (PLUS | MINUS) rightExpression=expression
+   | functionName=nodeName LR_BRACKET expression (COMMA expression)* functionAttribute* RR_BRACKET
+   | nodeName
+   | constant
+   ;
+
+whereClause
+    : WHERE orExpression
+    ;
+
+orExpression
+    : andExpression (OPERATOR_OR andExpression)*
+    ;
+
+andExpression
+    : predicate (OPERATOR_AND predicate)*
+    ;
+
+predicate
+    : (TIME | TIMESTAMP | nodeName ) comparisonOperator constant
+    | OPERATOR_NOT? LR_BRACKET orExpression RR_BRACKET
+    ;
+
+fromClause
+    : FROM nodeName (COMMA nodeName)*
+    ;
+
+nodeName
+    : STAR
+    | ID
+    | QUTOED_ID
+    | QUTOED_ID_IN_NODE_NAME
+    ;
+
+// Identifier
+
+identifier
+    : ID
+    | QUTOED_ID
+    | QUTOED_ID_IN_NODE_NAME
+    | INTEGER_LITERAL
+    ;
+
+
+// Constant & Literal
+
+constant
+    : dateExpression
+    | (MINUS|PLUS)? realLiteral
+    | (MINUS|PLUS)? INTEGER_LITERAL
+    | STRING_LITERAL
+    | BOOLEAN_LITERAL
+    | NULL_LITERAL
+    | NAN_LITERAL
+    ;
+
+functionAttribute
+    : COMMA functionAttributeKey=STRING_LITERAL OPERATOR_EQ functionAttributeValue=STRING_LITERAL
+    ;
+
+comparisonOperator
+    : type = OPERATOR_GT
+    | type = OPERATOR_GTE
+    | type = OPERATOR_LT
+    | type = OPERATOR_LTE
+    | type = OPERATOR_EQ
+    | type = OPERATOR_NEQ
+    ;
+
+// Expression & Predicate
+
+dateExpression
+    : datetimeLiteral ((PLUS | MINUS) DURATION_LITERAL)*
+    ;
+
+realLiteral
+    : INTEGER_LITERAL DOT (INTEGER_LITERAL|EXPONENT_NUM_PART)?
+    | DOT (INTEGER_LITERAL|EXPONENT_NUM_PART)
+    | EXPONENT_NUM_PART
+    ;
+
+datetimeLiteral
+    : DATETIME_LITERAL
+    | NOW LR_BRACKET RR_BRACKET
+    ;
diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
index a218d15..73b4b0a 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
@@ -19,7 +19,7 @@
 
 parser grammar IoTDBSqlParser;
 
-options { tokenVocab=IoTDBSqlLexer; }
+options { tokenVocab=SqlLexer; }
 
 
 /**
diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlLexer.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
similarity index 99%
rename from antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlLexer.g4
rename to antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
index 459d01f..89a4603 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlLexer.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-lexer grammar IoTDBSqlLexer;
+lexer grammar SqlLexer;
 
 /**
  * 1. Whitespace
diff --git a/cli/src/main/java/org/apache/iotdb/cli/IoTDBSyntaxHighlighter.java b/cli/src/main/java/org/apache/iotdb/cli/IoTDBSyntaxHighlighter.java
index 82e9866..211fec7 100644
--- a/cli/src/main/java/org/apache/iotdb/cli/IoTDBSyntaxHighlighter.java
+++ b/cli/src/main/java/org/apache/iotdb/cli/IoTDBSyntaxHighlighter.java
@@ -20,7 +20,7 @@
 package org.apache.iotdb.cli;
 
 import org.apache.iotdb.cli.utils.JlineUtils;
-import org.apache.iotdb.db.qp.sql.IoTDBSqlLexer;
+import org.apache.iotdb.db.qp.sql.SqlLexer;
 
 import org.antlr.v4.runtime.CharStream;
 import org.antlr.v4.runtime.CharStreams;
@@ -43,7 +43,7 @@ public class IoTDBSyntaxHighlighter implements Highlighter {
   @Override
   public AttributedString highlight(LineReader reader, String buffer) {
     CharStream stream = CharStreams.fromString(buffer);
-    IoTDBSqlLexer tokenSource = new IoTDBSqlLexer(stream);
+    SqlLexer tokenSource = new SqlLexer(stream);
     tokenSource.removeErrorListeners();
     AttributedStringBuilder builder = new AttributedStringBuilder();
     while (true) {
diff --git a/cli/src/main/java/org/apache/iotdb/cli/utils/JlineUtils.java b/cli/src/main/java/org/apache/iotdb/cli/utils/JlineUtils.java
index b185398..b9ad88d 100644
--- a/cli/src/main/java/org/apache/iotdb/cli/utils/JlineUtils.java
+++ b/cli/src/main/java/org/apache/iotdb/cli/utils/JlineUtils.java
@@ -19,7 +19,7 @@
 
 package org.apache.iotdb.cli.utils;
 
-import org.apache.iotdb.db.qp.sql.IoTDBSqlLexer;
+import org.apache.iotdb.db.qp.sql.SqlLexer;
 
 import org.jline.reader.LineReader;
 import org.jline.reader.LineReader.Option;
@@ -44,8 +44,8 @@ public class JlineUtils {
 
   public static final Pattern SQL_KEYWORD_PATTERN = Pattern.compile("([A-Z_]+)");
   public static final Set<String> SQL_KEYWORDS =
-      IntStream.range(0, IoTDBSqlLexer.VOCABULARY.getMaxTokenType())
-          .mapToObj(IoTDBSqlLexer.VOCABULARY::getDisplayName)
+      IntStream.range(0, SqlLexer.VOCABULARY.getMaxTokenType())
+          .mapToObj(SqlLexer.VOCABULARY::getDisplayName)
           .filter(Objects::nonNull)
           .filter(w -> SQL_KEYWORD_PATTERN.matcher(w).matches())
           .collect(Collectors.toSet());
diff --git a/docs/zh/UserGuide/API/InfluxDB-Protocol.md b/docs/zh/UserGuide/API/InfluxDB-Protocol.md
index db9f83f..63f8816 100644
--- a/docs/zh/UserGuide/API/InfluxDB-Protocol.md
+++ b/docs/zh/UserGuide/API/InfluxDB-Protocol.md
@@ -230,7 +230,7 @@ time                address name phone sex socre
    ```
 
 
-4. 查询student中name=A或score>97,由于tag存储在路径中,因此没有办法用一次查询同时完成tag和field的*或*语义查询,因此需要多次查询进行或运算求并集,对应到IoTDB的查询为:
+4. 查询student中name=A或score>97,由于tag存储在路径中,因此没有办法用一次查询同时完成tag和field的**或**语义查询,因此需要多次查询进行或运算求并集,对应到IoTDB的查询为:
 
    ```sql
    select * from root.monitor.student.A 
diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/constant/InfluxSQLConstant.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/constant/InfluxSQLConstant.java
new file mode 100644
index 0000000..cbc77b7
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/constant/InfluxSQLConstant.java
@@ -0,0 +1,57 @@
+/*
+ * 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.protocol.influxdb.constant;
+
+import org.apache.iotdb.db.qp.constant.SQLConstant;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/** this class contains several constants used in SQL. */
+public class InfluxSQLConstant extends SQLConstant {
+
+  public static final String MAX = "max";
+  public static final String MIN = "min";
+  public static final String FIRST = "first";
+  public static final String MEAN = "mean";
+  public static final String MEDIAN = "median";
+  public static final String MODE = "mode";
+  public static final String SPREAD = "spread";
+  public static final String STDDEV = "stddev";
+  private static final Set<String> NATIVE_FUNCTION_NAMES =
+      new HashSet<>(
+          Arrays.asList(MIN, MAX, FIRST, LAST, MEAN, COUNT, MEDIAN, MODE, SPREAD, STDDEV, SUM));
+  private static final Set<String> NATIVE_SELECTOR_FUNCTION_NAMES =
+      new HashSet<>(Arrays.asList(MIN, MAX, FIRST, LAST));
+
+  public InfluxSQLConstant() throws InstantiationException {
+    // forbidding instantiation
+    super();
+  }
+
+  public static Set<String> getNativeFunctionNames() {
+    return NATIVE_FUNCTION_NAMES;
+  }
+
+  public static Set<String> getNativeSelectorFunctionNames() {
+    return NATIVE_SELECTOR_FUNCTION_NAMES;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/dto/IoTDBPoint.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/dto/IoTDBPoint.java
index 45fbdd9..3d90114 100644
--- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/dto/IoTDBPoint.java
+++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/dto/IoTDBPoint.java
@@ -82,21 +82,17 @@ public class IoTDBPoint {
         if (reflectField.getType().getName().equalsIgnoreCase("java.util.Map")
             && reflectField.getName().equalsIgnoreCase("fields")) {
           fields = (Map<String, Object>) reflectField.get(point);
-        }
-        if (reflectField.getType().getName().equalsIgnoreCase("java.util.Map")
+        } else if (reflectField.getType().getName().equalsIgnoreCase("java.util.Map")
             && reflectField.getName().equalsIgnoreCase("tags")) {
           tags = (Map<String, String>) reflectField.get(point);
-        }
-        if (reflectField.getType().getName().equalsIgnoreCase("java.lang.String")
+        } else if (reflectField.getType().getName().equalsIgnoreCase("java.lang.String")
             && reflectField.getName().equalsIgnoreCase("measurement")) {
           measurement = (String) reflectField.get(point);
-        }
-        if (reflectField.getType().getName().equalsIgnoreCase("java.lang.Number")
+        } else if (reflectField.getType().getName().equalsIgnoreCase("java.lang.Number")
             && reflectField.getName().equalsIgnoreCase("time")) {
           time = (Long) reflectField.get(point);
           time = TimeUnit.MILLISECONDS.convert(time, precision);
         }
-
       } catch (IllegalAccessException e) {
         throw new IllegalArgumentException(e.getMessage());
       }
diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxQueryOperator.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxQueryOperator.java
new file mode 100644
index 0000000..bdc15eb
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxQueryOperator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.protocol.influxdb.operator;
+
+import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
+
+public class InfluxQueryOperator extends QueryOperator {
+
+  protected InfluxSelectComponent influxSelectComponent;
+
+  public InfluxQueryOperator() {
+    super();
+  }
+
+  public InfluxSelectComponent getSelectComponent() {
+    return influxSelectComponent;
+  }
+
+  public void setSelectComponent(InfluxSelectComponent influxSelectComponent) {
+    this.influxSelectComponent = influxSelectComponent;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxSelectComponent.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxSelectComponent.java
new file mode 100644
index 0000000..4e98caa
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxSelectComponent.java
@@ -0,0 +1,94 @@
+/*
+ * 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.protocol.influxdb.operator;
+
+import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant;
+import org.apache.iotdb.db.query.expression.Expression;
+import org.apache.iotdb.db.query.expression.ResultColumn;
+import org.apache.iotdb.db.query.expression.unary.FunctionExpression;
+import org.apache.iotdb.db.query.expression.unary.TimeSeriesOperand;
+
+import java.time.ZoneId;
+
+/** this class maintains information from select clause. */
+public final class InfluxSelectComponent
+    extends org.apache.iotdb.db.qp.logical.crud.SelectComponent {
+
+  private boolean hasAggregationFunction = false;
+  private boolean hasSelectorFunction = false;
+  private boolean hasMoreSelectorFunction = false;
+  private boolean hasMoreFunction = false;
+  private boolean hasFunction = false;
+  private boolean hasCommonQuery = false;
+
+  public InfluxSelectComponent() {
+    super((ZoneId) null);
+  }
+
+  public void addResultColumn(ResultColumn resultColumn) {
+    Expression expression = resultColumn.getExpression();
+    if (expression instanceof FunctionExpression) {
+      String functionName = ((FunctionExpression) expression).getFunctionName();
+      if (InfluxSQLConstant.getNativeFunctionNames().contains(functionName.toLowerCase())) {
+        if (hasFunction) {
+          hasMoreFunction = true;
+        } else {
+          hasFunction = true;
+        }
+      }
+      if (InfluxSQLConstant.getNativeSelectorFunctionNames().contains(functionName.toLowerCase())) {
+        if (hasSelectorFunction) {
+          hasMoreSelectorFunction = true;
+        } else {
+          hasSelectorFunction = true;
+        }
+      } else {
+        hasAggregationFunction = true;
+      }
+    }
+    if (expression instanceof TimeSeriesOperand) {
+      hasCommonQuery = true;
+    }
+    resultColumns.add(resultColumn);
+  }
+
+  public boolean isHasAggregationFunction() {
+    return hasAggregationFunction;
+  }
+
+  public boolean isHasMoreFunction() {
+    return hasMoreFunction;
+  }
+
+  public boolean isHasCommonQuery() {
+    return hasCommonQuery;
+  }
+
+  public boolean isHasSelectorFunction() {
+    return hasSelectorFunction;
+  }
+
+  public boolean isHasMoreSelectorFunction() {
+    return hasMoreSelectorFunction;
+  }
+
+  public boolean isHasFunction() {
+    return hasFunction;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGenerator.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGenerator.java
new file mode 100644
index 0000000..361d105
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGenerator.java
@@ -0,0 +1,66 @@
+/*
+ * 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.protocol.influxdb.sql;
+
+import org.apache.iotdb.db.qp.logical.Operator;
+import org.apache.iotdb.db.qp.sql.InfluxDBSqlParser;
+import org.apache.iotdb.db.qp.sql.SqlLexer;
+import org.apache.iotdb.db.qp.strategy.SQLParseError;
+
+import org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.atn.PredictionMode;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
+import org.antlr.v4.runtime.tree.ParseTree;
+
+public class InfluxDBLogicalGenerator {
+
+  private InfluxDBLogicalGenerator() {}
+
+  public static Operator generate(String sql) throws ParseCancellationException {
+    InfluxDBSqlVisitor influxDBSqlVisitor = new InfluxDBSqlVisitor();
+    CharStream charStream1 = CharStreams.fromString(sql);
+    SqlLexer lexer1 = new SqlLexer(charStream1);
+    lexer1.removeErrorListeners();
+    lexer1.addErrorListener(SQLParseError.INSTANCE);
+    CommonTokenStream tokens1 = new CommonTokenStream(lexer1);
+    InfluxDBSqlParser parser1 = new InfluxDBSqlParser(tokens1);
+    parser1.getInterpreter().setPredictionMode(PredictionMode.SLL);
+    parser1.removeErrorListeners();
+    parser1.addErrorListener(SQLParseError.INSTANCE);
+    ParseTree tree;
+    try {
+      tree = parser1.singleStatement(); // STAGE 1
+    } catch (Exception ex) {
+      CharStream charStream2 = CharStreams.fromString(sql);
+      SqlLexer lexer2 = new SqlLexer(charStream2);
+      lexer2.removeErrorListeners();
+      lexer2.addErrorListener(SQLParseError.INSTANCE);
+      CommonTokenStream tokens2 = new CommonTokenStream(lexer2);
+      InfluxDBSqlParser parser2 = new InfluxDBSqlParser(tokens2);
+      parser2.getInterpreter().setPredictionMode(PredictionMode.LL);
+      parser2.removeErrorListeners();
+      parser2.addErrorListener(SQLParseError.INSTANCE);
+      tree = parser2.singleStatement(); // STAGE 2
+      // if we parse ok, it's LL not SLL
+    }
+    return influxDBSqlVisitor.visit(tree);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBSqlVisitor.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBSqlVisitor.java
new file mode 100644
index 0000000..72c4eea
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBSqlVisitor.java
@@ -0,0 +1,297 @@
+/*
+ * 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.protocol.influxdb.sql;
+
+import org.apache.iotdb.db.metadata.path.PartialPath;
+import org.apache.iotdb.db.protocol.influxdb.operator.*;
+import org.apache.iotdb.db.qp.constant.FilterConstant;
+import org.apache.iotdb.db.qp.constant.SQLConstant;
+import org.apache.iotdb.db.qp.logical.Operator;
+import org.apache.iotdb.db.qp.logical.crud.BasicFunctionOperator;
+import org.apache.iotdb.db.qp.logical.crud.FilterOperator;
+import org.apache.iotdb.db.qp.logical.crud.FromComponent;
+import org.apache.iotdb.db.qp.logical.crud.WhereComponent;
+import org.apache.iotdb.db.qp.sql.InfluxDBSqlParser;
+import org.apache.iotdb.db.qp.sql.InfluxDBSqlParserBaseVisitor;
+import org.apache.iotdb.db.qp.utils.DatetimeUtils;
+import org.apache.iotdb.db.query.expression.Expression;
+import org.apache.iotdb.db.query.expression.ResultColumn;
+import org.apache.iotdb.db.query.expression.binary.AdditionExpression;
+import org.apache.iotdb.db.query.expression.binary.DivisionExpression;
+import org.apache.iotdb.db.query.expression.binary.ModuloExpression;
+import org.apache.iotdb.db.query.expression.binary.MultiplicationExpression;
+import org.apache.iotdb.db.query.expression.binary.SubtractionExpression;
+import org.apache.iotdb.db.query.expression.unary.FunctionExpression;
+import org.apache.iotdb.db.query.expression.unary.NegationExpression;
+import org.apache.iotdb.db.query.expression.unary.TimeSeriesOperand;
+
+public class InfluxDBSqlVisitor extends InfluxDBSqlParserBaseVisitor<Operator> {
+
+  private InfluxQueryOperator queryOp;
+
+  @Override
+  public Operator visitSingleStatement(InfluxDBSqlParser.SingleStatementContext ctx) {
+    return visit(ctx.statement());
+  }
+
+  @Override
+  public Operator visitSelectStatement(InfluxDBSqlParser.SelectStatementContext ctx) {
+    queryOp = new InfluxQueryOperator();
+    parseSelectClause(ctx.selectClause());
+    parseFromClause(ctx.fromClause());
+    if (ctx.whereClause() != null) {
+      WhereComponent whereComponent = parseWhereClause(ctx.whereClause());
+      queryOp.setWhereComponent(whereComponent);
+    }
+    return queryOp;
+  }
+
+  public void parseSelectClause(InfluxDBSqlParser.SelectClauseContext ctx) {
+    InfluxSelectComponent influxSelectComponent = new InfluxSelectComponent();
+    for (InfluxDBSqlParser.ResultColumnContext resultColumnContext : ctx.resultColumn()) {
+      influxSelectComponent.addResultColumn(parseResultColumn(resultColumnContext));
+    }
+    queryOp.setSelectComponent(influxSelectComponent);
+  }
+
+  private void parseFromClause(InfluxDBSqlParser.FromClauseContext fromClause) {
+    FromComponent fromComponent = new FromComponent();
+
+    for (InfluxDBSqlParser.NodeNameContext nodeName : fromClause.nodeName()) {
+      fromComponent.addPrefixTablePath(new PartialPath(nodeName.getText(), false));
+    }
+    queryOp.setFromComponent(fromComponent);
+  }
+
+  private WhereComponent parseWhereClause(InfluxDBSqlParser.WhereClauseContext ctx) {
+    FilterOperator whereOp = new FilterOperator();
+    whereOp.addChildOperator(parseOrExpression(ctx.orExpression()));
+    return new WhereComponent(whereOp.getChildren().get(0));
+  }
+
+  private FilterOperator parseOrExpression(InfluxDBSqlParser.OrExpressionContext ctx) {
+    if (ctx.andExpression().size() == 1) {
+      return parseAndExpression(ctx.andExpression(0));
+    }
+    FilterOperator binaryOp = new FilterOperator(FilterConstant.FilterType.KW_OR);
+    if (ctx.andExpression().size() > 2) {
+      binaryOp.addChildOperator(parseAndExpression(ctx.andExpression(0)));
+      binaryOp.addChildOperator(parseAndExpression(ctx.andExpression(1)));
+      for (int i = 2; i < ctx.andExpression().size(); i++) {
+        FilterOperator operator = new FilterOperator(FilterConstant.FilterType.KW_OR);
+        operator.addChildOperator(binaryOp);
+        operator.addChildOperator(parseAndExpression(ctx.andExpression(i)));
+        binaryOp = operator;
+      }
+    } else {
+      for (InfluxDBSqlParser.AndExpressionContext andExpressionContext : ctx.andExpression()) {
+        binaryOp.addChildOperator(parseAndExpression(andExpressionContext));
+      }
+    }
+    return binaryOp;
+  }
+
+  private FilterOperator parseAndExpression(InfluxDBSqlParser.AndExpressionContext ctx) {
+    if (ctx.predicate().size() == 1) {
+      return parsePredicate(ctx.predicate(0));
+    }
+    FilterOperator binaryOp = new FilterOperator(FilterConstant.FilterType.KW_AND);
+    int size = ctx.predicate().size();
+    if (size > 2) {
+      binaryOp.addChildOperator(parsePredicate(ctx.predicate(0)));
+      binaryOp.addChildOperator(parsePredicate(ctx.predicate(1)));
+      for (int i = 2; i < size; i++) {
+        FilterOperator op = new FilterOperator(FilterConstant.FilterType.KW_AND);
+        op.addChildOperator(binaryOp);
+        op.addChildOperator(parsePredicate(ctx.predicate(i)));
+        binaryOp = op;
+      }
+    } else {
+      for (InfluxDBSqlParser.PredicateContext predicateContext : ctx.predicate()) {
+        binaryOp.addChildOperator(parsePredicate(predicateContext));
+      }
+    }
+    return binaryOp;
+  }
+
+  private FilterOperator parsePredicate(InfluxDBSqlParser.PredicateContext ctx) {
+    if (ctx.OPERATOR_NOT() != null) {
+      FilterOperator notOp = new FilterOperator(FilterConstant.FilterType.KW_NOT);
+      notOp.addChildOperator(parseOrExpression(ctx.orExpression()));
+      return notOp;
+    } else if (ctx.LR_BRACKET() != null && ctx.OPERATOR_NOT() == null) {
+      return parseOrExpression(ctx.orExpression());
+    } else {
+      String keyName = null;
+      if (ctx.TIME() != null || ctx.TIMESTAMP() != null) {
+        keyName = SQLConstant.RESERVED_TIME;
+      }
+      if (ctx.nodeName() != null) {
+        keyName = ctx.nodeName().getText();
+      }
+      if (keyName == null) {
+        throw new IllegalArgumentException("keyName is null, please check the sql");
+      }
+      return parseBasicFunctionOperator(ctx, keyName);
+    }
+  }
+
+  private ResultColumn parseResultColumn(
+      InfluxDBSqlParser.ResultColumnContext resultColumnContext) {
+    return new ResultColumn(
+        parseExpression(resultColumnContext.expression()),
+        resultColumnContext.AS() == null ? null : resultColumnContext.identifier().getText());
+  }
+
+  private Expression parseExpression(InfluxDBSqlParser.ExpressionContext context) {
+    // LR_BRACKET unaryInBracket=expression RR_BRACKET
+    if (context.unaryInBracket != null) {
+      return parseExpression(context.unaryInBracket);
+    }
+
+    // (PLUS | MINUS) unaryAfterSign=expression
+    if (context.unaryAfterSign != null) {
+      return context.MINUS() != null
+          ? new NegationExpression(parseExpression(context.expression(0)))
+          : parseExpression(context.expression(0));
+    }
+
+    // leftExpression=expression (STAR | DIV | MOD) rightExpression=expression
+    // leftExpression=expression (PLUS | MINUS) rightExpression=expression
+    if (context.leftExpression != null && context.rightExpression != null) {
+      Expression leftExpression = parseExpression(context.leftExpression);
+      Expression rightExpression = parseExpression(context.rightExpression);
+      if (context.STAR() != null) {
+        return new MultiplicationExpression(leftExpression, rightExpression);
+      }
+      if (context.DIV() != null) {
+        return new DivisionExpression(leftExpression, rightExpression);
+      }
+      if (context.MOD() != null) {
+        return new ModuloExpression(leftExpression, rightExpression);
+      }
+      if (context.PLUS() != null) {
+        return new AdditionExpression(leftExpression, rightExpression);
+      }
+      if (context.MINUS() != null) {
+        return new SubtractionExpression(leftExpression, rightExpression);
+      }
+    }
+
+    // functionName=nodeName LR_BRACKET expression (COMMA expression)* functionAttribute* RR_BRACKET
+    if (context.functionName != null) {
+      return parseFunctionExpression(context);
+    }
+
+    // nodeName || constant
+    if (context.nodeName() != null || context.constant() != null) {
+      return new TimeSeriesOperand(new PartialPath(context.nodeName().getText(), false));
+    }
+
+    throw new UnsupportedOperationException();
+  }
+
+  private Expression parseFunctionExpression(InfluxDBSqlParser.ExpressionContext functionClause) {
+
+    FunctionExpression functionExpression =
+        new FunctionExpression(functionClause.functionName.getText());
+
+    // expressions
+    for (InfluxDBSqlParser.ExpressionContext expression : functionClause.expression()) {
+      functionExpression.addExpression(parseExpression(expression));
+    }
+
+    // attributes
+    for (InfluxDBSqlParser.FunctionAttributeContext functionAttribute :
+        functionClause.functionAttribute()) {
+      functionExpression.addAttribute(
+          removeStringQuote(functionAttribute.functionAttributeKey.getText()),
+          removeStringQuote(functionAttribute.functionAttributeValue.getText()));
+    }
+
+    return functionExpression;
+  }
+
+  private static String removeStringQuote(String src) {
+    if (src.charAt(0) == '\'' && src.charAt(src.length() - 1) == '\'') {
+      return src.substring(1, src.length() - 1);
+    } else if (src.charAt(0) == '\"' && src.charAt(src.length() - 1) == '\"') {
+      return src.substring(1, src.length() - 1);
+    } else {
+      throw new IllegalArgumentException("error format for string with quote:" + src);
+    }
+  }
+
+  /**
+   * parse time expression, which is addition and subtraction expression of duration time, now() or
+   * DataTimeFormat time.
+   *
+   * <p>eg. now() + 1d - 2h
+   */
+  private static Long parseDateExpression(InfluxDBSqlParser.DateExpressionContext ctx) {
+    long time;
+    time = parseTimeFormat(ctx.getChild(0).getText());
+    for (int i = 1; i < ctx.getChildCount(); i = i + 2) {
+      if (ctx.getChild(i).getText().equals("+")) {
+        time += DatetimeUtils.convertDurationStrToLong(time, ctx.getChild(i + 1).getText());
+      } else {
+        time -= DatetimeUtils.convertDurationStrToLong(time, ctx.getChild(i + 1).getText());
+      }
+    }
+    return time;
+  }
+
+  /** function for parsing time format. */
+  public static long parseTimeFormat(String timestampStr) {
+    if (timestampStr == null || timestampStr.trim().equals("")) {
+      throw new IllegalArgumentException("input timestamp cannot be empty");
+    }
+    if (timestampStr.equalsIgnoreCase(SQLConstant.NOW_FUNC)) {
+      return DatetimeUtils.currentTime();
+    }
+    throw new IllegalArgumentException(
+        String.format(
+            "Input time format %s error. "
+                + "Input like yyyy-MM-dd HH:mm:ss, yyyy-MM-ddTHH:mm:ss or "
+                + "refer to user document for more info.",
+            timestampStr));
+  }
+
+  private static FilterOperator parseBasicFunctionOperator(
+      InfluxDBSqlParser.PredicateContext ctx, String pathName) {
+    BasicFunctionOperator basic;
+    if (ctx.constant().dateExpression() != null) {
+      if (!pathName.equals(SQLConstant.RESERVED_TIME)) {
+        throw new IllegalArgumentException("Date can only be used to time");
+      }
+      basic =
+          new BasicFunctionOperator(
+              FilterConstant.lexerToFilterType.get(ctx.comparisonOperator().type.getType()),
+              new PartialPath(pathName, false),
+              Long.toString(parseDateExpression(ctx.constant().dateExpression())));
+    } else {
+      basic =
+          new BasicFunctionOperator(
+              FilterConstant.lexerToFilterType.get(ctx.comparisonOperator().type.getType()),
+              new PartialPath(pathName, false),
+              ctx.constant().getText());
+    }
+    return basic;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/constant/FilterConstant.java b/server/src/main/java/org/apache/iotdb/db/qp/constant/FilterConstant.java
index dbc5e57..cd8cc49 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/constant/FilterConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/constant/FilterConstant.java
@@ -19,7 +19,7 @@
 
 package org.apache.iotdb.db.qp.constant;
 
-import org.apache.iotdb.db.qp.sql.IoTDBSqlLexer;
+import org.apache.iotdb.db.qp.sql.SqlLexer;
 
 import java.util.EnumMap;
 import java.util.HashMap;
@@ -50,15 +50,15 @@ public class FilterConstant {
   }
 
   static {
-    lexerToFilterType.put(IoTDBSqlLexer.OPERATOR_EQ, FilterType.EQUAL);
-    lexerToFilterType.put(IoTDBSqlLexer.OPERATOR_NEQ, FilterType.NOTEQUAL);
-    lexerToFilterType.put(IoTDBSqlLexer.OPERATOR_LTE, FilterType.LESSTHANOREQUALTO);
-    lexerToFilterType.put(IoTDBSqlLexer.OPERATOR_LT, FilterType.LESSTHAN);
-    lexerToFilterType.put(IoTDBSqlLexer.OPERATOR_GTE, FilterType.GREATERTHANOREQUALTO);
-    lexerToFilterType.put(IoTDBSqlLexer.OPERATOR_GT, FilterType.GREATERTHAN);
-    lexerToFilterType.put(IoTDBSqlLexer.OPERATOR_IN, FilterType.IN);
-    lexerToFilterType.put(IoTDBSqlLexer.REGEXP, FilterType.REGEXP);
-    lexerToFilterType.put(IoTDBSqlLexer.LIKE, FilterType.LIKE);
+    lexerToFilterType.put(SqlLexer.OPERATOR_EQ, FilterType.EQUAL);
+    lexerToFilterType.put(SqlLexer.OPERATOR_NEQ, FilterType.NOTEQUAL);
+    lexerToFilterType.put(SqlLexer.OPERATOR_LTE, FilterType.LESSTHANOREQUALTO);
+    lexerToFilterType.put(SqlLexer.OPERATOR_LT, FilterType.LESSTHAN);
+    lexerToFilterType.put(SqlLexer.OPERATOR_GTE, FilterType.GREATERTHANOREQUALTO);
+    lexerToFilterType.put(SqlLexer.OPERATOR_GT, FilterType.GREATERTHAN);
+    lexerToFilterType.put(SqlLexer.OPERATOR_IN, FilterType.IN);
+    lexerToFilterType.put(SqlLexer.REGEXP, FilterType.REGEXP);
+    lexerToFilterType.put(SqlLexer.LIKE, FilterType.LIKE);
   }
 
   static {
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java b/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
index a0b3168..cbae289 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
@@ -30,8 +30,9 @@ import java.util.Set;
 @SuppressWarnings("unused") // some fields are for future features
 public class SQLConstant {
 
-  private SQLConstant() {
+  public SQLConstant() throws InstantiationException {
     // forbidding instantiation
+    throw new InstantiationException();
   }
 
   private static final String[] SINGLE_ROOT_ARRAY = {"root", "**"};
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectComponent.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectComponent.java
index 9dac1c3..4b955eb 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectComponent.java
@@ -30,7 +30,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 /** this class maintains information from select clause. */
-public final class SelectComponent {
+public class SelectComponent {
 
   private final ZoneId zoneId;
 
@@ -38,7 +38,7 @@ public final class SelectComponent {
   private boolean hasTimeSeriesGeneratingFunction = false;
   private boolean hasUserDefinedAggregationFunction = false;
 
-  private List<ResultColumn> resultColumns = new ArrayList<>();
+  protected List<ResultColumn> resultColumns = new ArrayList<>();
 
   private List<PartialPath> pathsCache;
   private List<String> aggregationFunctionsCache;
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
index 27d38a4..340bc32 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
@@ -30,9 +30,9 @@ import org.apache.iotdb.db.qp.logical.crud.LastQueryOperator;
 import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
 import org.apache.iotdb.db.qp.logical.crud.SelectComponent;
 import org.apache.iotdb.db.qp.logical.crud.WhereComponent;
-import org.apache.iotdb.db.qp.sql.IoTDBSqlLexer;
 import org.apache.iotdb.db.qp.sql.IoTDBSqlParser;
 import org.apache.iotdb.db.qp.sql.IoTDBSqlVisitor;
+import org.apache.iotdb.db.qp.sql.SqlLexer;
 import org.apache.iotdb.db.query.expression.ResultColumn;
 import org.apache.iotdb.db.query.expression.unary.TimeSeriesOperand;
 import org.apache.iotdb.db.utils.TestOnly;
@@ -64,7 +64,7 @@ public class LogicalGenerator {
 
     CharStream charStream1 = CharStreams.fromString(sql);
 
-    IoTDBSqlLexer lexer1 = new IoTDBSqlLexer(charStream1);
+    SqlLexer lexer1 = new SqlLexer(charStream1);
     lexer1.removeErrorListeners();
     lexer1.addErrorListener(SQLParseError.INSTANCE);
 
@@ -84,7 +84,7 @@ public class LogicalGenerator {
     } catch (Exception ex) {
       CharStream charStream2 = CharStreams.fromString(sql);
 
-      IoTDBSqlLexer lexer2 = new IoTDBSqlLexer(charStream2);
+      SqlLexer lexer2 = new SqlLexer(charStream2);
       lexer2.removeErrorListeners();
       lexer2.addErrorListener(SQLParseError.INSTANCE);
 
diff --git a/server/src/test/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGeneratorTest.java b/server/src/test/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGeneratorTest.java
new file mode 100644
index 0000000..276b9d3
--- /dev/null
+++ b/server/src/test/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGeneratorTest.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.protocol.influxdb.sql;
+
+import org.apache.iotdb.db.protocol.influxdb.operator.*;
+import org.apache.iotdb.db.qp.logical.crud.BasicFunctionOperator;
+import org.apache.iotdb.db.qp.logical.crud.FilterOperator;
+import org.apache.iotdb.db.qp.logical.crud.WhereComponent;
+import org.apache.iotdb.db.query.expression.ResultColumn;
+import org.apache.iotdb.db.query.expression.unary.TimeSeriesOperand;
+
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class InfluxDBLogicalGeneratorTest {
+  @Test
+  public void testParserSql1() {
+    InfluxQueryOperator operator =
+        (InfluxQueryOperator) InfluxDBLogicalGenerator.generate("SELECT * FROM h2o_feet");
+    List<ResultColumn> resultColumnList = operator.getSelectComponent().getResultColumns();
+    assertEquals(resultColumnList.size(), 1);
+    TimeSeriesOperand timeSeriesOperand =
+        (TimeSeriesOperand) resultColumnList.get(0).getExpression();
+    assertEquals(timeSeriesOperand.getPath().getFullPath(), "*");
+    assertEquals(operator.getFromComponent().getPrefixPaths().get(0).getFullPath(), "h2o_feet");
+    assertNull(operator.getWhereComponent());
+  }
+
+  @Test
+  public void testParserSql2() {
+    InfluxQueryOperator operator =
+        (InfluxQueryOperator)
+            InfluxDBLogicalGenerator.generate("SELECT a,b,c FROM h2o_feet where a>1 and b<1");
+    List<ResultColumn> resultColumnList = operator.getSelectComponent().getResultColumns();
+    assertEquals(resultColumnList.size(), 3);
+    TimeSeriesOperand timeSeriesOperand =
+        (TimeSeriesOperand) resultColumnList.get(0).getExpression();
+    assertEquals(timeSeriesOperand.getPath().getFullPath(), "a");
+    WhereComponent whereComponent = operator.getWhereComponent();
+    FilterOperator filterOperator = (FilterOperator) whereComponent.getFilterOperator();
+    assertEquals(filterOperator.getFilterType().toString(), "KW_AND");
+    assertEquals(filterOperator.getChildren().size(), 2);
+    BasicFunctionOperator basicFunctionOperator =
+        (BasicFunctionOperator) filterOperator.getChildren().get(0);
+    assertEquals(basicFunctionOperator.getValue(), "1");
+    assertEquals(basicFunctionOperator.getSinglePath().getFullPath(), "a");
+    assertNotNull(basicFunctionOperator.getFilterType());
+  }
+}
diff --git a/thrift-influxdb/README.md b/thrift-influxdb/README.md
index 4801c89..af4fe2c 100644
--- a/thrift-influxdb/README.md
+++ b/thrift-influxdb/README.md
@@ -19,4 +19,4 @@
 
 -->
 
-This modules maintains all influxdb RPC interfaces among servers for the influxdb protocol module.
\ No newline at end of file
+This module maintains all influxdb RPC interfaces among servers for the influxdb protocol module.
\ No newline at end of file