You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by su...@apache.org on 2020/12/13 14:46:44 UTC

[iotdb] branch master updated: [IOTDB-507] Add zeppelin-interpreter module (#2096)

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

sunzesong 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 6a5e527  [IOTDB-507] Add zeppelin-interpreter module  (#2096)
6a5e527 is described below

commit 6a5e527322b57992f73b6b709ec9e20b39f89839
Author: Duan Tianyang <48...@users.noreply.github.com>
AuthorDate: Sun Dec 13 22:46:25 2020 +0800

    [IOTDB-507] Add zeppelin-interpreter module  (#2096)
    
    [IOTDB-507] Add zeppelin-interpreter module  (#2096)
    
    Co-authored-by: Xiangdong Huang <hx...@qq.com>
    Co-authored-by: Jialin Qiao <qj...@mails.tsinghua.edu.cn>
    Co-authored-by: kr11 <30...@qq.com>
    Co-authored-by: kr11 <3095717866.com>
---
 .../java/org/apache/iotdb/cli/AbstractCli.java     | 110 +-----
 cli/src/main/java/org/apache/iotdb/cli/Cli.java    |   5 +-
 cli/src/main/java/org/apache/iotdb/cli/WinCli.java |   5 +-
 .../main/java/org/apache/iotdb/tool/ExportCsv.java |   4 +-
 .../java/org/apache/iotdb/cli/AbstractCliIT.java   |  35 +-
 pom.xml                                            |  12 +-
 .../main/java/org/apache/iotdb/rpc/RpcUtils.java   |  90 +++++
 zeppelin-interpreter/IoTDB-Zeppelin-Demo.zpln      | 377 +++++++++++++++++++++
 zeppelin-interpreter/README.md                     | 186 ++++++++++
 zeppelin-interpreter/pom.xml                       | 126 +++++++
 .../apache/zeppelin/iotdb/IoTDBInterpreter.java    | 213 ++++++++++++
 .../src/main/resources/interpreter-setting.json    |  70 ++++
 .../zeppelin/iotdb/IoTDBInterpreterTest.java       | 273 +++++++++++++++
 13 files changed, 1364 insertions(+), 142 deletions(-)

diff --git a/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java b/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java
index 2648199..61429d6 100644
--- a/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java
+++ b/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java
@@ -26,11 +26,7 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.text.SimpleDateFormat;
-import java.time.Instant;
 import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -44,6 +40,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.iotdb.exception.ArgsErrorException;
 import org.apache.iotdb.jdbc.IoTDBConnection;
 import org.apache.iotdb.jdbc.IoTDBJDBCResultSet;
+import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.service.rpc.thrift.ServerProperties;
 import org.apache.iotdb.tool.ImportCsv;
 
@@ -88,21 +85,17 @@ public abstract class AbstractCli {
   private static final String SHOW_METADATA_COMMAND = "show timeseries";
   static final int MAX_HELP_CONSOLE_WIDTH = 88;
   static final String TIMESTAMP_STR = "Time";
-  static final int ISO_DATETIME_LEN = 35;
   private static final String IMPORT_CMD = "import";
-  private static final String DEFAULT_TIME_FORMAT = "default";
-  private static String timeFormat = DEFAULT_TIME_FORMAT;
   static int maxPrintRowCount = 1000;
   private static int fetchSize = 1000;
-  static int maxTimeLength = ISO_DATETIME_LEN;
-  static int maxValueLength = 15;
-  static String TIMESTAMP_PRECISION = "ms";
+  static String timestampPrecision = "ms";
+  static String timeFormat = RpcUtils.DEFAULT_TIME_FORMAT;
+
   private static int lineCount = 0;
   private static final String SUCCESS_MESSAGE = "The statement is executed successfully.";
 
   private static boolean isReachEnd = false;
 
-  static String formatTime = "%" + maxTimeLength + "s|";
   static String host = "127.0.0.1";
   static String port = "6667";
   static String username;
@@ -130,9 +123,6 @@ public abstract class AbstractCli {
   }
 
 
-  private static String getTimestampPrecision() {
-    return TIMESTAMP_PRECISION;
-  }
 
   private static void printCount(int cnt) {
     if (cnt == 0) {
@@ -188,67 +178,6 @@ public abstract class AbstractCli {
     return options;
   }
 
-  @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning
-  public static String parseLongToDateWithPrecision(DateTimeFormatter formatter,
-      long timestamp, ZoneId zoneid, String timestampPrecision) {
-    if (timestampPrecision.equals("ms")) {
-      long integerofDate = timestamp / 1000;
-      StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000));
-      ZonedDateTime dateTime = ZonedDateTime
-          .ofInstant(Instant.ofEpochSecond(integerofDate), zoneid);
-      String datetime = dateTime.format(formatter);
-      int length = digits.length();
-      if (length != 3) {
-        for (int i = 0; i < 3 - length; i++) {
-          digits.insert(0, "0");
-        }
-      }
-      return datetime.substring(0, 19) + "." + digits + datetime.substring(19);
-    } else if (timestampPrecision.equals("us")) {
-      long integerofDate = timestamp / 1000_000;
-      StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000_000));
-      ZonedDateTime dateTime = ZonedDateTime
-          .ofInstant(Instant.ofEpochSecond(integerofDate), zoneid);
-      String datetime = dateTime.format(formatter);
-      int length = digits.length();
-      if (length != 6) {
-        for (int i = 0; i < 6 - length; i++) {
-          digits.insert(0, "0");
-        }
-      }
-      return datetime.substring(0, 19) + "." + digits + datetime.substring(19);
-    } else {
-      long integerofDate = timestamp / 1000_000_000L;
-      StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000_000_000L));
-      ZonedDateTime dateTime = ZonedDateTime
-          .ofInstant(Instant.ofEpochSecond(integerofDate), zoneid);
-      String datetime = dateTime.format(formatter);
-      int length = digits.length();
-      if (length != 9) {
-        for (int i = 0; i < 9 - length; i++) {
-          digits.insert(0, "0");
-        }
-      }
-      return datetime.substring(0, 19) + "." + digits + datetime.substring(19);
-    }
-  }
-
-  private static String formatDatetime(long timestamp, ZoneId zoneId) {
-    ZonedDateTime dateTime;
-    switch (timeFormat) {
-      case "long":
-      case "number":
-        return Long.toString(timestamp);
-      case DEFAULT_TIME_FORMAT:
-      case "iso8601":
-        return parseLongToDateWithPrecision(
-            DateTimeFormatter.ISO_OFFSET_DATE_TIME, timestamp, zoneId, getTimestampPrecision());
-      default:
-        dateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), zoneId);
-        return dateTime.format(DateTimeFormatter.ofPattern(timeFormat));
-    }
-  }
-
   static String checkRequiredArg(String arg, String name, CommandLine commandLine,
       boolean isRequired,
       String defaultValue) throws ArgsErrorException {
@@ -271,29 +200,6 @@ public abstract class AbstractCli {
     return str;
   }
 
-  static void setTimeFormat(String newTimeFormat) {
-    switch (newTimeFormat.trim().toLowerCase()) {
-      case "long":
-      case "number":
-        maxTimeLength = maxValueLength;
-        timeFormat = newTimeFormat.trim().toLowerCase();
-        break;
-      case DEFAULT_TIME_FORMAT:
-      case "iso8601":
-        maxTimeLength = ISO_DATETIME_LEN;
-        timeFormat = newTimeFormat.trim().toLowerCase();
-        break;
-      default:
-        // use java default SimpleDateFormat to check whether input time format is legal
-        // if illegal, it will throw an exception
-        new SimpleDateFormat(newTimeFormat.trim());
-        maxTimeLength = Math.max(TIMESTAMP_STR.length(), newTimeFormat.length());
-        timeFormat = newTimeFormat;
-        break;
-    }
-    formatTime = "%" + maxTimeLength + "s|";
-  }
-
   private static void setFetchSize(String fetchSizeString) {
     long tmp = Long.parseLong(fetchSizeString.trim());
     if (tmp > Integer.MAX_VALUE || tmp < 0) {
@@ -458,7 +364,7 @@ public abstract class AbstractCli {
       return;
     }
     try {
-      setTimeFormat(cmd.split("=")[1]);
+      timeFormat = RpcUtils.setTimeFormat(cmd.split("=")[1]);
     } catch (Exception e) {
       println(String.format("time display format error, %s", e.getMessage()));
       return;
@@ -632,7 +538,8 @@ public abstract class AbstractCli {
         for (int i = 1; i <= columnCount; i++) {
           String tmp;
           if (printTimestamp && i == 1) {
-            tmp = formatDatetime(resultSet.getLong(TIMESTAMP_STR), zoneId);
+            tmp = RpcUtils.formatDatetime(timeFormat, timestampPrecision,
+                resultSet.getLong(TIMESTAMP_STR), zoneId);
           } else {
             tmp = resultSet.getString(i);
           }
@@ -656,7 +563,8 @@ public abstract class AbstractCli {
             tmp = NULL;
           }
           if (i % 2 != 0 && !tmp.equals(NULL)) {
-            tmp = formatDatetime(Long.parseLong(tmp), zoneId);
+            tmp = RpcUtils.formatDatetime(timeFormat, timestampPrecision,
+                Long.parseLong(tmp), zoneId);
           }
           lists.get(i - 1).add(tmp);
           if (maxSizeList.get(i - 1) < tmp.length()) {
diff --git a/cli/src/main/java/org/apache/iotdb/cli/Cli.java b/cli/src/main/java/org/apache/iotdb/cli/Cli.java
index 4227092..907aedf 100644
--- a/cli/src/main/java/org/apache/iotdb/cli/Cli.java
+++ b/cli/src/main/java/org/apache/iotdb/cli/Cli.java
@@ -31,6 +31,7 @@ import org.apache.commons.cli.ParseException;
 import org.apache.iotdb.exception.ArgsErrorException;
 import org.apache.iotdb.jdbc.Config;
 import org.apache.iotdb.jdbc.IoTDBConnection;
+import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.thrift.TException;
 
 /**
@@ -84,7 +85,7 @@ public class Cli extends AbstractCli {
         Config.rpcThriftCompressionEnable = true;
       }
       if (commandLine.hasOption(ISO8601_ARGS)) {
-        setTimeFormat("long");
+        timeFormat = RpcUtils.setTimeFormat("long");
       }
       if (commandLine.hasOption(MAX_PRINT_ROW_COUNT_ARGS)) {
         setMaxDisplayNumber(commandLine.getOptionValue(MAX_PRINT_ROW_COUNT_ARGS));
@@ -142,7 +143,7 @@ public class Cli extends AbstractCli {
       String s;
       properties = connection.getServerProperties();
       AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      TIMESTAMP_PRECISION = properties.getTimestampPrecision();
+      timestampPrecision = properties.getTimestampPrecision();
 
       echoStarting();
       displayLogo(properties.getVersion());
diff --git a/cli/src/main/java/org/apache/iotdb/cli/WinCli.java b/cli/src/main/java/org/apache/iotdb/cli/WinCli.java
index 02a5d0b..53c7837 100644
--- a/cli/src/main/java/org/apache/iotdb/cli/WinCli.java
+++ b/cli/src/main/java/org/apache/iotdb/cli/WinCli.java
@@ -31,6 +31,7 @@ import org.apache.commons.cli.ParseException;
 import org.apache.iotdb.exception.ArgsErrorException;
 import org.apache.iotdb.jdbc.Config;
 import org.apache.iotdb.jdbc.IoTDBConnection;
+import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.thrift.TException;
 
 /**
@@ -92,7 +93,7 @@ public class WinCli extends AbstractCli {
         Config.rpcThriftCompressionEnable = true;
       }
       if (commandLine.hasOption(ISO8601_ARGS)) {
-        setTimeFormat("long");
+        timeFormat = RpcUtils.setTimeFormat("long");
       }
       if (commandLine.hasOption(MAX_PRINT_ROW_COUNT_ARGS)) {
         maxPrintRowCount = Integer.parseInt(commandLine.getOptionValue(MAX_PRINT_ROW_COUNT_ARGS));
@@ -148,7 +149,7 @@ public class WinCli extends AbstractCli {
         .getConnection(Config.IOTDB_URL_PREFIX + host + ":" + port + "/", username, password)) {
       properties = connection.getServerProperties();
       AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      TIMESTAMP_PRECISION = properties.getTimestampPrecision();
+      timestampPrecision = properties.getTimestampPrecision();
 
       echoStarting();
       displayLogo(properties.getVersion());
diff --git a/cli/src/main/java/org/apache/iotdb/tool/ExportCsv.java b/cli/src/main/java/org/apache/iotdb/tool/ExportCsv.java
index df08bdc..7cf5d78 100644
--- a/cli/src/main/java/org/apache/iotdb/tool/ExportCsv.java
+++ b/cli/src/main/java/org/apache/iotdb/tool/ExportCsv.java
@@ -37,9 +37,9 @@ import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
-import org.apache.iotdb.cli.AbstractCli;
 import org.apache.iotdb.exception.ArgsErrorException;
 import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.StatementExecutionException;
 import org.apache.iotdb.session.Session;
 import org.apache.iotdb.session.SessionDataSet;
@@ -283,7 +283,7 @@ public class ExportCsv extends AbstractCsvTool {
     String timestampPrecision = "ms";
     switch (timeFormat) {
       case "default":
-        String str = AbstractCli
+        String str = RpcUtils
             .parseLongToDateWithPrecision(DateTimeFormatter.ISO_OFFSET_DATE_TIME, time, zoneId,
                 timestampPrecision);
         bw.write(str + ",");
diff --git a/cli/src/test/java/org/apache/iotdb/cli/AbstractCliIT.java b/cli/src/test/java/org/apache/iotdb/cli/AbstractCliIT.java
index 342fe0c..0a4142c 100644
--- a/cli/src/test/java/org/apache/iotdb/cli/AbstractCliIT.java
+++ b/cli/src/test/java/org/apache/iotdb/cli/AbstractCliIT.java
@@ -31,6 +31,7 @@ import org.apache.iotdb.cli.AbstractCli.OperationResult;
 import org.apache.iotdb.exception.ArgsErrorException;
 import org.apache.iotdb.jdbc.IoTDBConnection;
 import org.apache.iotdb.jdbc.IoTDBDatabaseMetadata;
+import org.apache.iotdb.rpc.RpcUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -152,7 +153,6 @@ public class AbstractCliIT {
         .handleInputCmd(String.format("%s=xxx", AbstractCli.SET_TIMESTAMP_DISPLAY), connection));
     assertEquals(OperationResult.CONTINUE_OPER, AbstractCli
         .handleInputCmd(String.format("%s=default", AbstractCli.SET_TIMESTAMP_DISPLAY), connection));
-    testSetTimeFormat();
 
     assertEquals(OperationResult.CONTINUE_OPER, AbstractCli
         .handleInputCmd(String.format("%s=", AbstractCli.SET_MAX_DISPLAY_NUM), connection));
@@ -180,33 +180,6 @@ public class AbstractCliIT {
         .handleInputCmd(String.format("%s=111", AbstractCli.SET_FETCH_SIZE), connection));
   }
 
-  private void testSetTimeFormat() {
-    AbstractCli.setTimeFormat("long");
-    assertEquals(AbstractCli.maxTimeLength, AbstractCli.maxValueLength);
-    assertEquals(AbstractCli.formatTime, "%" + AbstractCli.maxTimeLength + "s|");
-
-    AbstractCli.setTimeFormat("number");
-    assertEquals(AbstractCli.maxTimeLength, AbstractCli.maxValueLength);
-    assertEquals(AbstractCli.formatTime, "%" + AbstractCli.maxTimeLength + "s|");
-
-    AbstractCli.setTimeFormat("default");
-    assertEquals(AbstractCli.ISO_DATETIME_LEN, AbstractCli.maxTimeLength);
-    assertEquals(AbstractCli.formatTime, "%" + AbstractCli.maxTimeLength + "s|");
-
-    AbstractCli.setTimeFormat("iso8601");
-    assertEquals(AbstractCli.ISO_DATETIME_LEN, AbstractCli.maxTimeLength);
-    assertEquals(AbstractCli.formatTime, "%" + AbstractCli.maxTimeLength + "s|");
-
-    AbstractCli.setTimeFormat("yyyy-MM-dd HH:mm:ssZZ");
-    assertEquals(AbstractCli.maxTimeLength, "yyyy-MM-dd HH:mm:ssZZ".length());
-    assertEquals(AbstractCli.formatTime, "%" + AbstractCli.maxTimeLength + "s|");
-
-    AbstractCli.setTimeFormat("dd");
-    assertEquals(AbstractCli.maxTimeLength, AbstractCli.TIMESTAMP_STR.length());
-    assertEquals(AbstractCli.formatTime, "%" + AbstractCli.maxTimeLength + "s|");
-
-  }
-
   private void testSetMaxDisplayNumber() {
     try {
       AbstractCli.setMaxDisplayNumber("10");
@@ -217,19 +190,19 @@ public class AbstractCliIT {
     try {
       AbstractCli.setMaxDisplayNumber("111111111111111");
       fail();
-    } catch (NumberFormatException e) {
+    } catch (NumberFormatException ignored) {
     }
 
     try {
       AbstractCli.setMaxDisplayNumber("-10");
       fail();
-    } catch (NumberFormatException e) {
+    } catch (NumberFormatException ignored) {
     }
 
     try {
       AbstractCli.setMaxDisplayNumber("0");
       fail();
-    } catch (NumberFormatException e) {
+    } catch (NumberFormatException ignored) {
     }
   }
 }
diff --git a/pom.xml b/pom.xml
index d81586f..41425ef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -98,6 +98,7 @@
         <module>hive-connector</module>
         <module>cluster</module>
         <module>cross-tests</module>
+        <module>zeppelin-interpreter</module>
         <module>client-py</module>
     </modules>
     <!-- Properties Management -->
@@ -128,6 +129,7 @@
         <common.lang.version>2.6</common.lang.version>
         <common.lang3.version>3.8.1</common.lang3.version>
         <common.logging.version>1.1.3</common.logging.version>
+        <org.slf4j.version>1.7.25</org.slf4j.version>
         <guava.version>24.1.1</guava.version>
         <jline.version>2.14.5</jline.version>
         <jetty.version>9.4.24.v20191120</jetty.version>
@@ -410,22 +412,22 @@
             <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>jcl-over-slf4j</artifactId>
-                <version>1.7.25</version>
+                <version>${org.slf4j.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>jul-to-slf4j</artifactId>
-                <version>1.7.25</version>
+                <version>${org.slf4j.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-api</artifactId>
-                <version>1.7.25</version>
+                <version>${org.slf4j.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-log4j12</artifactId>
-                <version>1.7.25</version>
+                <version>${org.slf4j.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.xerial.snappy</groupId>
@@ -574,6 +576,8 @@
                             <exclude>LICENSE-binary</exclude>
                             <!-- json does not support comments-->
                             <exclude>**/*.json</exclude>
+                            <!-- the zeppelin export file format-->
+                            <exclude>**/*.zpln</exclude>
                         </excludes>
                     </configuration>
                 </plugin>
diff --git a/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcUtils.java b/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcUtils.java
index 1a84a18..c9be448 100644
--- a/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcUtils.java
+++ b/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcUtils.java
@@ -19,6 +19,11 @@
 package org.apache.iotdb.rpc;
 
 import java.lang.reflect.Proxy;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -176,4 +181,89 @@ public class RpcUtils {
     return resp;
   }
 
+  public static final String DEFAULT_TIME_FORMAT = "default";
+  public static final String DEFAULT_TIMESTAMP_PRECISION = "ms";
+
+  public static String setTimeFormat(String newTimeFormat) {
+    String timeFormat;
+    switch (newTimeFormat.trim().toLowerCase()) {
+      case "long":
+      case "number":
+        timeFormat = newTimeFormat.trim().toLowerCase();
+        break;
+      case DEFAULT_TIME_FORMAT:
+      case "iso8601":
+        timeFormat = newTimeFormat.trim().toLowerCase();
+        break;
+      default:
+        // use java default SimpleDateFormat to check whether input time format is legal
+        // if illegal, it will throw an exception
+        new SimpleDateFormat(newTimeFormat.trim());
+        timeFormat = newTimeFormat;
+        break;
+    }
+    return timeFormat;
+  }
+
+  public static String formatDatetime(String timeFormat, String timePrecision, long timestamp,
+      ZoneId zoneId) {
+    ZonedDateTime dateTime;
+    switch (timeFormat) {
+      case "long":
+      case "number":
+        return Long.toString(timestamp);
+      case DEFAULT_TIME_FORMAT:
+      case "iso8601":
+        return parseLongToDateWithPrecision(
+            DateTimeFormatter.ISO_OFFSET_DATE_TIME, timestamp, zoneId, timePrecision);
+      default:
+        dateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), zoneId);
+        return dateTime.format(DateTimeFormatter.ofPattern(timeFormat));
+    }
+  }
+
+  @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning
+  public static String parseLongToDateWithPrecision(DateTimeFormatter formatter,
+      long timestamp, ZoneId zoneid, String timestampPrecision) {
+    if (timestampPrecision.equals("ms")) {
+      long integerofDate = timestamp / 1000;
+      StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000));
+      ZonedDateTime dateTime = ZonedDateTime
+          .ofInstant(Instant.ofEpochSecond(integerofDate), zoneid);
+      String datetime = dateTime.format(formatter);
+      int length = digits.length();
+      if (length != 3) {
+        for (int i = 0; i < 3 - length; i++) {
+          digits.insert(0, "0");
+        }
+      }
+      return datetime.substring(0, 19) + "." + digits + datetime.substring(19);
+    } else if (timestampPrecision.equals("us")) {
+      long integerofDate = timestamp / 1000_000;
+      StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000_000));
+      ZonedDateTime dateTime = ZonedDateTime
+          .ofInstant(Instant.ofEpochSecond(integerofDate), zoneid);
+      String datetime = dateTime.format(formatter);
+      int length = digits.length();
+      if (length != 6) {
+        for (int i = 0; i < 6 - length; i++) {
+          digits.insert(0, "0");
+        }
+      }
+      return datetime.substring(0, 19) + "." + digits + datetime.substring(19);
+    } else {
+      long integerofDate = timestamp / 1000_000_000L;
+      StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000_000_000L));
+      ZonedDateTime dateTime = ZonedDateTime
+          .ofInstant(Instant.ofEpochSecond(integerofDate), zoneid);
+      String datetime = dateTime.format(formatter);
+      int length = digits.length();
+      if (length != 9) {
+        for (int i = 0; i < 9 - length; i++) {
+          digits.insert(0, "0");
+        }
+      }
+      return datetime.substring(0, 19) + "." + digits + datetime.substring(19);
+    }
+  }
 }
diff --git a/zeppelin-interpreter/IoTDB-Zeppelin-Demo.zpln b/zeppelin-interpreter/IoTDB-Zeppelin-Demo.zpln
new file mode 100644
index 0000000..2428a9c
--- /dev/null
+++ b/zeppelin-interpreter/IoTDB-Zeppelin-Demo.zpln
@@ -0,0 +1,377 @@
+{
+  "paragraphs": [
+    {
+      "title": "Set IoTDB Schema",
+      "text": "%iotdb\n\nSET STORAGE GROUP TO root.ln.wf01.wt01;\nCREATE TIMESERIES root.ln.wf01.wt01.status WITH DATATYPE=BOOLEAN, ENCODING=PLAIN;\nCREATE TIMESERIES root.ln.wf01.wt01.temperature WITH DATATYPE=FLOAT, ENCODING=PLAIN;\nCREATE TIMESERIES root.ln.wf01.wt01.hardware WITH DATATYPE=INT32, ENCODING=PLAIN;\n\n",
+      "user": "anonymous",
+      "dateUpdated": "2020-11-24T17:01:35+0800",
+      "config": {
+        "colWidth": 6,
+        "fontSize": 9,
+        "enabled": true,
+        "results": {},
+        "editorSetting": {
+          "language": "sql",
+          "editOnDblClick": false,
+          "completionKey": "TAB"
+        },
+        "title": true,
+        "lineNumbers": true,
+        "editorMode": "ace/mode/sql"
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "TEXT",
+            "data": "Sql executed."
+          }
+        ]
+      },
+      "apps": [],
+      "runtimeInfos": {},
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1606199112249_2061310279",
+      "id": "paragraph_1606199112249_2061310279",
+      "dateCreated": "2020-11-24T14:25:12+0800",
+      "dateStarted": "2020-11-24T17:01:35+0800",
+      "dateFinished": "2020-11-24T17:01:35+0800",
+      "status": "FINISHED",
+      "focus": true,
+      "$$hashKey": "object:407"
+    },
+    {
+      "title": "Insert Time Series",
+      "text": "INSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)\nVALUES (1, 1.1, false, 4);\nINSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)\nVALUES (2, 3.2, true, 3);\nINSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)\nVALUES (3, 2.3, false, 2);\nINSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)\nVALUES (4, 4.4, false, 1);\nINSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardwar [...]
+      "user": "anonymous",
+      "dateUpdated": "2020-11-24T17:01:38+0800",
+      "config": {
+        "colWidth": 6,
+        "fontSize": 9,
+        "enabled": true,
+        "results": {},
+        "editorSetting": {
+          "language": "sql",
+          "editOnDblClick": false,
+          "completionKey": "TAB"
+        },
+        "title": true,
+        "lineNumbers": true,
+        "editorMode": "ace/mode/sql"
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "TEXT",
+            "data": "Sql executed."
+          }
+        ]
+      },
+      "apps": [],
+      "runtimeInfos": {},
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1606199645491_331600654",
+      "id": "paragraph_1606199645491_331600654",
+      "dateCreated": "2020-11-24T14:34:05+0800",
+      "dateStarted": "2020-11-24T17:01:38+0800",
+      "dateFinished": "2020-11-24T17:01:38+0800",
+      "status": "FINISHED",
+      "$$hashKey": "object:408"
+    },
+    {
+      "title": "Query: Table",
+      "text": "SELECT *\nFROM root.ln.wf01.wt01\nWHERE time >= 1\n\tAND time <= 6;",
+      "user": "anonymous",
+      "dateUpdated": "2020-11-24T17:01:40+0800",
+      "config": {
+        "colWidth": 6,
+        "fontSize": 9,
+        "enabled": true,
+        "results": {
+          "0": {
+            "graph": {
+              "mode": "table",
+              "height": 300,
+              "optionOpen": false,
+              "setting": {
+                "table": {
+                  "tableGridState": {
+                    "columns": [
+                      {
+                        "name": "Time0",
+                        "visible": true,
+                        "width": 150,
+                        "sort": {},
+                        "filters": [
+                          {}
+                        ],
+                        "pinned": ""
+                      },
+                      {
+                        "name": "root.ln.wf01.wt01.temperature1",
+                        "visible": true,
+                        "width": 290,
+                        "sort": {},
+                        "filters": [
+                          {}
+                        ],
+                        "pinned": ""
+                      },
+                      {
+                        "name": "root.ln.wf01.wt01.status2",
+                        "visible": true,
+                        "width": "*",
+                        "sort": {},
+                        "filters": [
+                          {}
+                        ],
+                        "pinned": ""
+                      },
+                      {
+                        "name": "root.ln.wf01.wt01.hardware3",
+                        "visible": true,
+                        "width": "*",
+                        "sort": {},
+                        "filters": [
+                          {}
+                        ],
+                        "pinned": ""
+                      }
+                    ],
+                    "scrollFocus": {},
+                    "selection": [],
+                    "grouping": {
+                      "grouping": [],
+                      "aggregations": [],
+                      "rowExpandedStates": {}
+                    },
+                    "treeView": {},
+                    "pagination": {
+                      "paginationCurrentPage": 1,
+                      "paginationPageSize": 250
+                    }
+                  },
+                  "tableColumnTypeState": {
+                    "names": {
+                      "Time": "string",
+                      "root.ln.wf01.wt01.temperature": "string",
+                      "root.ln.wf01.wt01.status": "string",
+                      "root.ln.wf01.wt01.hardware": "string"
+                    },
+                    "updated": false
+                  },
+                  "tableOptionSpecHash": "[{\"name\":\"useFilter\",\"valueType\":\"boolean\",\"defaultValue\":false,\"widget\":\"checkbox\",\"description\":\"Enable filter for columns\"},{\"name\":\"showPagination\",\"valueType\":\"boolean\",\"defaultValue\":false,\"widget\":\"checkbox\",\"description\":\"Enable pagination for better navigation\"},{\"name\":\"showAggregationFooter\",\"valueType\":\"boolean\",\"defaultValue\":false,\"widget\":\"checkbox\",\"description\":\"Enable a footer [...]
+                  "tableOptionValue": {
+                    "useFilter": false,
+                    "showPagination": false,
+                    "showAggregationFooter": false
+                  },
+                  "updated": false,
+                  "initialized": false
+                }
+              },
+              "commonSetting": {}
+            }
+          }
+        },
+        "editorSetting": {
+          "language": "sql",
+          "editOnDblClick": false,
+          "completionKey": "TAB"
+        },
+        "editorHide": false,
+        "title": true,
+        "lineNumbers": true,
+        "editorMode": "ace/mode/sql"
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "TABLE",
+            "data": "Time\troot.ln.wf01.wt01.temperature\troot.ln.wf01.wt01.status\troot.ln.wf01.wt01.hardware\n1\t1.1\tfalse\t4\n2\t3.2\ttrue\t3\n3\t2.3\tfalse\t2\n4\t4.4\tfalse\t1\n5\t3.5\tfalse\t0"
+          }
+        ]
+      },
+      "apps": [],
+      "runtimeInfos": {},
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1605787967187_651066788",
+      "id": "paragraph_1605787967187_651066788",
+      "dateCreated": "2020-11-19T20:12:47+0800",
+      "dateStarted": "2020-11-24T17:01:40+0800",
+      "dateFinished": "2020-11-24T17:01:40+0800",
+      "status": "FINISHED",
+      "$$hashKey": "object:409"
+    },
+    {
+      "title": "Query: Line Chart",
+      "text": "SELECT *\nFROM root.ln.wf01.wt01\nWHERE time >= 1\n\tAND time <= 6;",
+      "user": "anonymous",
+      "dateUpdated": "2020-11-24T17:01:42+0800",
+      "config": {
+        "colWidth": 6,
+        "fontSize": 9,
+        "enabled": true,
+        "results": {
+          "0": {
+            "graph": {
+              "mode": "lineChart",
+              "height": 300,
+              "optionOpen": false,
+              "setting": {
+                "table": {
+                  "tableGridState": {},
+                  "tableColumnTypeState": {
+                    "names": {
+                      "Time": "string",
+                      "root.ln.wf01.wt01.status": "string"
+                    },
+                    "updated": false
+                  },
+                  "tableOptionSpecHash": "[{\"name\":\"useFilter\",\"valueType\":\"boolean\",\"defaultValue\":false,\"widget\":\"checkbox\",\"description\":\"Enable filter for columns\"},{\"name\":\"showPagination\",\"valueType\":\"boolean\",\"defaultValue\":false,\"widget\":\"checkbox\",\"description\":\"Enable pagination for better navigation\"},{\"name\":\"showAggregationFooter\",\"valueType\":\"boolean\",\"defaultValue\":false,\"widget\":\"checkbox\",\"description\":\"Enable a footer [...]
+                  "tableOptionValue": {
+                    "useFilter": false,
+                    "showPagination": false,
+                    "showAggregationFooter": false
+                  },
+                  "updated": false,
+                  "initialized": false
+                },
+                "lineChart": {
+                  "rotate": {
+                    "degree": "-45"
+                  },
+                  "xLabelStatus": "default"
+                },
+                "stackedAreaChart": {
+                  "rotate": {
+                    "degree": "-45"
+                  },
+                  "xLabelStatus": "default"
+                },
+                "pieChart": {},
+                "multiBarChart": {
+                  "rotate": {
+                    "degree": "-45"
+                  },
+                  "xLabelStatus": "default"
+                }
+              },
+              "commonSetting": {},
+              "keys": [
+                {
+                  "name": "Time",
+                  "index": 0,
+                  "aggr": "sum"
+                }
+              ],
+              "groups": [],
+              "values": [
+                {
+                  "name": "root.ln.wf01.wt01.temperature",
+                  "index": 1,
+                  "aggr": "sum"
+                },
+                {
+                  "name": "root.ln.wf01.wt01.hardware",
+                  "index": 3,
+                  "aggr": "sum"
+                }
+              ]
+            },
+            "helium": {}
+          }
+        },
+        "editorSetting": {
+          "language": "sql",
+          "editOnDblClick": false,
+          "completionKey": "TAB"
+        },
+        "editorHide": false,
+        "title": true,
+        "lineNumbers": true,
+        "editorMode": "ace/mode/sql"
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "TABLE",
+            "data": "Time\troot.ln.wf01.wt01.temperature\troot.ln.wf01.wt01.status\troot.ln.wf01.wt01.hardware\n1\t1.1\tfalse\t4\n2\t3.2\ttrue\t3\n3\t2.3\tfalse\t2\n4\t4.4\tfalse\t1\n5\t3.5\tfalse\t0"
+          }
+        ]
+      },
+      "apps": [],
+      "runtimeInfos": {},
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1606200407155_1025230441",
+      "id": "paragraph_1606200407155_1025230441",
+      "dateCreated": "2020-11-24T14:46:47+0800",
+      "dateStarted": "2020-11-24T17:01:42+0800",
+      "dateFinished": "2020-11-24T17:01:42+0800",
+      "status": "FINISHED",
+      "$$hashKey": "object:410"
+    },
+    {
+      "text": "",
+      "user": "anonymous",
+      "dateUpdated": "2020-11-24T14:24:43+0800",
+      "config": {
+        "colWidth": 12,
+        "fontSize": 9,
+        "enabled": true,
+        "results": {},
+        "editorSetting": {
+          "language": "sql",
+          "editOnDblClick": false,
+          "completionKey": "TAB"
+        },
+        "editorMode": "ace/mode/sql"
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "apps": [],
+      "runtimeInfos": {},
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1605787989076_1398743633",
+      "id": "paragraph_1605787989076_1398743633",
+      "dateCreated": "2020-11-19T20:13:09+0800",
+      "status": "READY",
+      "$$hashKey": "object:411"
+    }
+  ],
+  "name": "IoTDB-Zeppelin-Demo",
+  "id": "2FRZ8D72H",
+  "defaultInterpreterGroup": "iotdb",
+  "version": "0.9.0-preview2",
+  "noteParams": {},
+  "noteForms": {},
+  "angularObjects": {},
+  "config": {
+    "isZeppelinNotebookCronEnable": false,
+    "looknfeel": "default",
+    "personalizedMode": "false"
+  },
+  "info": {},
+  "path": "/IoTDB-Zeppelin-Demo"
+}
\ No newline at end of file
diff --git a/zeppelin-interpreter/README.md b/zeppelin-interpreter/README.md
new file mode 100644
index 0000000..5df4d54
--- /dev/null
+++ b/zeppelin-interpreter/README.md
@@ -0,0 +1,186 @@
+<!--
+
+    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.
+
+-->
+
+
+
+# What's Zeppelin
+
+Zeppelin is a web-based notebook that enables interactive data analytics. You can connect to data sources and perform interactive operations with SQL, Scala, etc. The operations can be saved as documents, just like Jupyter. Zeppelin has already supported many data sources, including Spark, ElasticSearch, Cassandra, and InfluxDB. Now, we have enabled Zeppelin to operate IoTDB via SQL. 
+
+![image-20201124145203621](https://tva1.sinaimg.cn/large/0081Kckwly1gl09mzzfhzj314q0q70xo.jpg)
+
+
+
+# IoTDB zeppelin-interpreter
+
+## Environment
+
+To use IoTDB and Zeppelin, you need to have:
+
+1. Java >= 1.8.0_271 (Please make sure the environment path has been set. You can run `java -version` to confirm the current Java version.
+2. IoTDB >= 0.11.0-SNAPSHOT.
+3. Zeppelin >= 0.8.2
+
+You can install IoTDB according to [IoTDB Quick Start](http://iotdb.apache.org/UserGuide/V0.10.x/Get%20Started/QuickStart.html). IoTDB is placed at `$IoTDB_HOME`.
+
+You can download [Zeppelin](https://zeppelin.apache.org/download.html#) and unpack the binary package directly or [build Zeppelin from source](https://zeppelin.apache.org/docs/latest/setup/basics/how_to_build.html). Zeppelin is placed at `$Zeppelin_HOME`.
+
+
+
+## Build interpreter
+
+```shell
+cd $IoTDB_HOME
+mvn clean package -pl zeppelin-interpreter -am -DskipTests
+```
+
+The interpreter will be in the folder:
+
+```shell
+$IoTDB_HOME/zeppelin-interpreter/target/zeppelin-{version}-SNAPSHOT.jar
+```
+
+
+
+## Install interpreter
+
+Once you have built your interpreter, create a new folder under the Zeppelin interpreter directory and put the built interpreter into it. 
+
+```shell
+cd $IoTDB_HOME
+mkdir -p $Zeppelin_HOME/interpreter/iotdb
+cp $IoTDB_HOME/zeppelin-interpreter/target/zeppelin-{version}-SNAPSHOT.jar $Zeppelin_HOME/interpreter/iotdb
+```
+
+
+
+## Configure your interpreter
+
+To configure your interpreter you need to follow these steps:
+
+1. If it's the first start, create `conf/zeppelin-site.xml` by copying `conf/zeppelin-site.xml.template` to `conf/zeppelin-site.xml`.
+
+2. Append your interpreter class name to  `zeppelin.interpreters` property in `conf/zeppelin-site.xml`. If the property `zeppelin.interpreters` doesn't exist yet, add it directly.
+
+    ```xml
+    <configuration>
+      ...
+      <property>
+        <name>zeppelin.interpreters</name>
+        <value>org.apache.iotdb.zeppelin.IoTDBInterpreter</value>
+      </property>
+      ...
+    </configuration>
+    ```
+
+## Running Zeppelin and IoTDB
+
+Go to `$Zeppelin_HOME` and start Zeppelin by running: 
+
+```shell
+./bin/zeppelin-daemon.sh start
+```
+
+or in Windows:
+```shell
+./bin/zeppelin.cmd
+```
+
+Go to `$IoTDB_HOME` and [start IoTDB server](https://github.com/apache/iotdb#start-iotdb):
+
+```shell
+# Unix/OS X
+> nohup sbin/start-server.sh >/dev/null 2>&1 &
+or
+> nohup sbin/start-server.sh -c <conf_path> -rpc_port <rpc_port> >/dev/null 2>&1 &
+
+# Windows
+> sbin\start-server.bat -c <conf_path> -rpc_port <rpc_port>
+```
+
+## Zeppelin to IoTDB
+
+Wait for Zeppelin server to start, then visit http://localhost:8080/
+
+In the interpreter page: 
+
+1. Click the `Create new node` button
+2. Set the note name
+3. Configure your interpreter
+
+Now you are ready to use your interpreter.
+
+![image-20201123112330976](https://tva1.sinaimg.cn/large/0081Kckwly1gl09n7cpibj30sz0opagn.jpg)
+
+
+
+# Use IoTDB-Zeppelin
+
+We provide some simple SQL to show the use of Zeppelin-IoTDB-interpreter:
+
+```sql
+SET STORAGE GROUP TO root.ln.wf01.wt01;
+CREATE TIMESERIES root.ln.wf01.wt01.status WITH DATATYPE=BOOLEAN, ENCODING=PLAIN;
+CREATE TIMESERIES root.ln.wf01.wt01.temperature WITH DATATYPE=FLOAT, ENCODING=PLAIN;
+CREATE TIMESERIES root.ln.wf01.wt01.hardware WITH DATATYPE=INT32, ENCODING=PLAIN;
+
+INSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)
+VALUES (1, 1.1, false, 11);
+
+INSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)
+VALUES (2, 2.2, true, 22);
+
+INSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)
+VALUES (3, 3.3, false, 33);
+
+INSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)
+VALUES (4, 4.4, false, 44);
+
+INSERT INTO root.ln.wf01.wt01 (timestamp, temperature, status, hardware)
+VALUES (5, 5.5, false, 55);
+
+
+SELECT *
+FROM root.ln.wf01.wt01
+WHERE time >= 1
+	AND time <= 6;
+```
+
+The screenshot is as follows:
+
+![image-20201124142635276](https://tva1.sinaimg.cn/large/0081Kckwly1gl09nd3vimj30pj0nz0w3.jpg)
+
+You can also design more fantasy documents referring to [[1]](https://zeppelin.apache.org/docs/0.9.0-SNAPSHOT/usage/display_system/basic.html) and others.
+
+The above demo notebook can be found at `./IoTDB-Zeppelin-Demo.zpln`.
+
+
+
+# IoTDB-Zeppelin Configuration
+
+You can configure the connection parameters in [http://localhost:8080/#/interpreter](http://localhost:8080/#/interpreter) :
+
+![image-20201212141528183](https://tva1.sinaimg.cn/large/0081Kckwly1gll1pgn3k9j31kx0u045o.jpg)
+
+
+
+
+
diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml
new file mode 100644
index 0000000..6070469
--- /dev/null
+++ b/zeppelin-interpreter/pom.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.iotdb</groupId>
+        <artifactId>iotdb-parent</artifactId>
+        <version>0.12.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>zeppelin-iotdb</artifactId>
+    <packaging>jar</packaging>
+    <name>Zeppelin: Apache IoTDB interpreter</name>
+    <description>Zeppelin IoTDB support</description>
+    <properties>
+        <zeppelin.version>0.9.0-preview2</zeppelin.version>
+        <junit.version>4.11</junit.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.zeppelin</groupId>
+            <artifactId>zeppelin-interpreter</artifactId>
+            <version>${zeppelin.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.iotdb</groupId>
+            <artifactId>iotdb-session</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.iotdb</groupId>
+            <artifactId>iotdb-server</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-codec</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-transport-native-epoll</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-buffer</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-transport</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-handler</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-common</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.iotdb</groupId>
+            <artifactId>iotdb-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-jcl</artifactId>
+            <version>${org.slf4j.version}</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-enforcer-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <artifactId>maven-resources-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <artifactId>maven-shade-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/iotdb/IoTDBInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/iotdb/IoTDBInterpreter.java
new file mode 100644
index 0000000..7574d5c
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/iotdb/IoTDBInterpreter.java
@@ -0,0 +1,213 @@
+/*
+ * 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.zeppelin.iotdb;
+
+
+import static org.apache.iotdb.rpc.RpcUtils.setTimeFormat;
+
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.RpcUtils;
+import org.apache.iotdb.rpc.StatementExecutionException;
+import org.apache.iotdb.session.Session;
+import org.apache.iotdb.session.SessionDataSet;
+import org.apache.iotdb.tsfile.read.common.Field;
+import org.apache.iotdb.tsfile.read.common.RowRecord;
+import org.apache.zeppelin.interpreter.AbstractInterpreter;
+import org.apache.zeppelin.interpreter.InterpreterContext;
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.InterpreterResult.Code;
+import org.apache.zeppelin.interpreter.InterpreterResult.Type;
+import org.apache.zeppelin.interpreter.ZeppelinContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IoTDBInterpreter extends AbstractInterpreter {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(IoTDBInterpreter.class);
+
+  static final String IOTDB_HOST = "iotdb.host";
+  static final String IOTDB_PORT = "iotdb.port";
+  static final String IOTDB_USERNAME = "iotdb.username";
+  static final String IOTDB_PASSWORD = "iotdb.password";
+  static final String IOTDB_FETCH_SIZE = "iotdb.fetchSize";
+  static final String IOTDB_ZONE_ID = "iotdb.zoneId";
+  static final String IOTDB_ENABLE_RPC_COMPRESSION = "iotdb.enable.rpc.compression";
+  static final String IOTDB_TIME_DISPLAY_TYPE = "iotdb.time.display.type";
+  static final String SET_TIMESTAMP_DISPLAY = "set time_display_type";
+
+  private static final String NONE_VALUE = "none";
+  static final String DEFAULT_HOST = "127.0.0.1";
+  static final String DEFAULT_PORT = "6667";
+  static final String DEFAULT_FETCH_SIZE = "10000";
+  static final String DEFAULT_ENABLE_RPC_COMPRESSION = "false";
+  static final String DEFAULT_TIME_DISPLAY_TYPE = "long";
+
+  private static final char TAB = '\t';
+  private static final char NEWLINE = '\n';
+  private static final char WHITESPACE = ' ';
+  private static final String SEMICOLON = ";";
+  private static final String EQUAL_SIGN = "=";
+
+  private IoTDBConnectionException connectionException;
+  private Session session;
+  private String timeFormat;
+  private ZoneId zoneId;
+
+  public IoTDBInterpreter(Properties property) {
+    super(property);
+  }
+
+  @Override
+  public void open() {
+    try {
+      String host = getProperty(IOTDB_HOST, DEFAULT_HOST).trim();
+      int port = Integer.parseInt(getProperty(IOTDB_PORT, DEFAULT_PORT).trim());
+      String userName = properties.getProperty(IOTDB_USERNAME, NONE_VALUE).trim();
+      String passWord = properties.getProperty(IOTDB_PASSWORD, NONE_VALUE).trim();
+      int fetchSize = Integer
+          .parseInt(properties.getProperty(IOTDB_FETCH_SIZE, DEFAULT_FETCH_SIZE).trim());
+      String zoneStr = properties.getProperty(IOTDB_ZONE_ID);
+      this.zoneId = !NONE_VALUE.equalsIgnoreCase(zoneStr) && StringUtils.isNotBlank(zoneStr)
+          ? ZoneId.of(zoneStr.trim()) : ZoneId.systemDefault();
+      String timeDisplayType = properties.getProperty(IOTDB_TIME_DISPLAY_TYPE,
+          DEFAULT_TIME_DISPLAY_TYPE).trim();
+      this.timeFormat = setTimeFormat(timeDisplayType);
+      boolean enableRPCCompression = "true".equalsIgnoreCase(
+          properties.getProperty(IOTDB_ENABLE_RPC_COMPRESSION,
+              DEFAULT_ENABLE_RPC_COMPRESSION).trim());
+      session = new Session(host, port, userName, passWord, fetchSize, zoneId);
+      session.open(enableRPCCompression);
+    } catch (IoTDBConnectionException e) {
+      connectionException = e;
+    }
+  }
+
+  @Override
+  public void close() {
+    try {
+      if (session != null) {
+        session.close();
+      }
+    } catch (IoTDBConnectionException e) {
+      connectionException = e;
+    }
+  }
+
+  @Override
+  public FormType getFormType() {
+    return FormType.SIMPLE;
+  }
+
+  @Override
+  public ZeppelinContext getZeppelinContext() {
+    return null;
+  }
+
+  @Override
+  protected InterpreterResult internalInterpret(String st, InterpreterContext context) {
+    if (connectionException != null) {
+      return new InterpreterResult(Code.ERROR,
+          "IoTDBConnectionException: " + connectionException.getMessage());
+    }
+    try {
+      String[] scriptLines = parseMultiLinesSQL(st);
+      InterpreterResult interpreterResult = null;
+      for (String scriptLine : scriptLines) {
+        String lowercaseSc = scriptLine.toLowerCase();
+        if (lowercaseSc.startsWith(SET_TIMESTAMP_DISPLAY)) {
+          String[] values = scriptLine.split(EQUAL_SIGN);
+          if (values.length != 2) {
+            throw new StatementExecutionException(
+                String.format("Time display format error, please input like %s=ISO8601",
+                    SET_TIMESTAMP_DISPLAY));
+          }
+          String timeDisplayType = values[1].trim();
+          this.timeFormat = setTimeFormat(values[1]);
+          interpreterResult = new InterpreterResult(Code.SUCCESS, "Time display type has set to " +
+              timeDisplayType);
+        } else if (lowercaseSc.startsWith("select")) {
+          //Execute query
+          String msg;
+          msg = getResultWithCols(session, scriptLine);
+          interpreterResult = new InterpreterResult(Code.SUCCESS);
+          interpreterResult.add(Type.TABLE, msg);
+        } else {
+          //Execute non query
+          session.executeNonQueryStatement(scriptLine);
+          interpreterResult = new InterpreterResult(Code.SUCCESS, "Sql executed.");
+        }
+      }
+      return interpreterResult;
+    } catch (StatementExecutionException e) {
+      return new InterpreterResult(Code.ERROR, "StatementExecutionException: " + e.getMessage());
+    } catch (IoTDBConnectionException e) {
+      return new InterpreterResult(Code.ERROR, "IoTDBConnectionException: " + e.getMessage());
+    }
+  }
+
+  private String getResultWithCols(Session session, String sql)
+      throws StatementExecutionException, IoTDBConnectionException {
+    SessionDataSet sessionDataSet = session.executeQueryStatement(sql);
+    List<String> columnNames = sessionDataSet.getColumnNames();
+    StringBuilder stringBuilder = new StringBuilder();
+    for (String key : columnNames) {
+      stringBuilder.append(key);
+      stringBuilder.append(TAB);
+    }
+    stringBuilder.deleteCharAt(stringBuilder.length() - 1);
+    stringBuilder.append(NEWLINE);
+    while (sessionDataSet.hasNext()) {
+      RowRecord record = sessionDataSet.next();
+      stringBuilder.append(RpcUtils.formatDatetime(timeFormat, RpcUtils.DEFAULT_TIMESTAMP_PRECISION,
+          record.getTimestamp(), zoneId));
+      for (Field f : record.getFields()) {
+        stringBuilder.append(TAB);
+        stringBuilder.append(f);
+      }
+      stringBuilder.append(NEWLINE);
+    }
+    stringBuilder.deleteCharAt(stringBuilder.length() - 1);
+    return stringBuilder.toString();
+  }
+
+  @Override
+  public int getProgress(InterpreterContext context) {
+    return 0;
+  }
+
+  @Override
+  public void cancel(InterpreterContext context) {
+    try {
+      session.close();
+    } catch (IoTDBConnectionException e) {
+      LOGGER.error("Exception close failed", e);
+    }
+  }
+
+  static String[] parseMultiLinesSQL(String sql) {
+    String[] tmp = sql.replace(TAB, WHITESPACE).replace(NEWLINE, WHITESPACE).trim()
+        .split(SEMICOLON);
+    return Arrays.stream(tmp).map(String::trim).toArray(String[]::new);
+  }
+
+}
+
diff --git a/zeppelin-interpreter/src/main/resources/interpreter-setting.json b/zeppelin-interpreter/src/main/resources/interpreter-setting.json
new file mode 100644
index 0000000..296cd6f
--- /dev/null
+++ b/zeppelin-interpreter/src/main/resources/interpreter-setting.json
@@ -0,0 +1,70 @@
+[
+  {
+    "group": "iotdb",
+    "name": "iotdb",
+    "className": "org.apache.zeppelin.iotdb.IoTDBInterpreter",
+    "properties": {
+      "iotdb.host": {
+        "envName": null,
+        "propertyName": "iotdb.host",
+        "defaultValue": "127.0.0.1",
+        "description": "IoTDB server host to connect to, Default = 127.0.0.1",
+        "type": "string"
+      },
+      "iotdb.port": {
+        "envName": null,
+        "propertyName": "iotdb.port",
+        "defaultValue": "6667",
+        "description": "IoTDB server port to connect to, Default = 6667",
+        "type": "number"
+      },
+      "iotdb.username": {
+        "envName": null,
+        "propertyName": "iotdb.username",
+        "defaultValue": "root",
+        "description": "Username for authentication, Default = 'root'",
+        "type": "string"
+      },
+      "iotdb.password": {
+        "envName": null,
+        "propertyName": "iotdb.password",
+        "defaultValue": "root",
+        "description": "Password for authentication, Default = 'root'",
+        "type": "password"
+      },
+      "iotdb.fetchSize": {
+        "envName": null,
+        "propertyName": "iotdb.fetchSize",
+        "defaultValue": "10000",
+        "description": "query default fetch size. Default = 10000",
+        "type": "number"
+      },
+      "iotdb.zoneId": {
+        "envName": null,
+        "propertyName": "iotdb.zoneId",
+        "defaultValue": null,
+        "description": "time zone id of data point. Default = null",
+        "type": "string"
+      },
+      "iotdb.enable.rpc.compression": {
+        "envName": null,
+        "propertyName": "iotdb.enable.rpc.compression",
+        "defaultValue": false,
+        "description": "whether enable rpc compression. Default = null",
+        "type": "checkbox"
+      },
+      "iotdb.time.display.type": {
+        "envName": null,
+        "propertyName": "iotdb.time.display.type",
+        "defaultValue": "default",
+        "description": "the time format to display. Default = default",
+        "type": "string"
+      }
+    },
+    "editor": {
+      "language": "sql",
+      "editOnDblClick": false,
+      "completionKey": "TAB"
+    }
+  }
+]
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/iotdb/IoTDBInterpreterTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/iotdb/IoTDBInterpreterTest.java
new file mode 100644
index 0000000..2013c47
--- /dev/null
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/iotdb/IoTDBInterpreterTest.java
@@ -0,0 +1,273 @@
+/*
+ * 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.zeppelin.iotdb;
+
+
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.DEFAULT_ENABLE_RPC_COMPRESSION;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.DEFAULT_FETCH_SIZE;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.DEFAULT_HOST;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.DEFAULT_PORT;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.DEFAULT_TIME_DISPLAY_TYPE;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.IOTDB_ENABLE_RPC_COMPRESSION;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.IOTDB_FETCH_SIZE;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.IOTDB_HOST;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.IOTDB_PASSWORD;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.IOTDB_PORT;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.IOTDB_TIME_DISPLAY_TYPE;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.IOTDB_USERNAME;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.IOTDB_ZONE_ID;
+import static org.apache.zeppelin.iotdb.IoTDBInterpreter.SET_TIMESTAMP_DISPLAY;
+
+import java.io.IOException;
+import java.util.Properties;
+import org.apache.iotdb.db.exception.StorageEngineException;
+import org.apache.iotdb.db.utils.EnvironmentUtils;
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.InterpreterResult.Code;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class IoTDBInterpreterTest {
+
+  private IoTDBInterpreter interpreter;
+
+  @Before
+  public void open() {
+    EnvironmentUtils.closeStatMonitor();
+    EnvironmentUtils.envSetUp();
+    Properties properties = new Properties();
+    properties.put(IOTDB_HOST, DEFAULT_HOST);
+    properties.put(IOTDB_PORT, DEFAULT_PORT);
+    properties.put(IOTDB_USERNAME, "root");
+    properties.put(IOTDB_PASSWORD, "root");
+    properties.put(IOTDB_FETCH_SIZE, DEFAULT_FETCH_SIZE);
+    properties.put(IOTDB_ZONE_ID, "UTC");
+    properties.put(IOTDB_ENABLE_RPC_COMPRESSION, DEFAULT_ENABLE_RPC_COMPRESSION);
+    properties.put(IOTDB_TIME_DISPLAY_TYPE, DEFAULT_TIME_DISPLAY_TYPE);
+    interpreter = new IoTDBInterpreter(properties);
+    interpreter.open();
+    initInsert();
+  }
+
+  private void initInsert() {
+    interpreter.internalInterpret(
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (1, 1.1, false, 11)",
+        null);
+    interpreter.internalInterpret(
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (2, 2.2, true, 22)",
+        null);
+    interpreter.internalInterpret(
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (3, 3.3, false, 33)",
+        null);
+    interpreter.internalInterpret(
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (4, 4.4, false, 44)",
+        null);
+    interpreter.internalInterpret(
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (5, 5.5, false, 55)",
+        null);
+  }
+
+  @After
+  public void close() throws IOException, StorageEngineException {
+    interpreter.close();
+    EnvironmentUtils.cleanEnv();
+  }
+
+
+  @Test
+  public void testNonQuery() {
+    for (int i = 0; i < 100; i++) {
+      String script = String
+          .format("INSERT INTO root.test.wf02(timestamp,temperature) VALUES(%d,%f)", i,
+              Math.random() * 10);
+      InterpreterResult actual = interpreter.internalInterpret(script, null);
+      Assert.assertNotNull(actual);
+      Assert.assertEquals(Code.SUCCESS, actual.code());
+      Assert.assertEquals("Sql executed.", actual.message().get(0).getData());
+    }
+  }
+
+  @Test
+  public void testSelectColumnStatement() {
+    InterpreterResult actual = interpreter
+        .internalInterpret("select status from root.test.wf01.wt01", null);
+    String gt = "Time\troot.test.wf01.wt01.status\n"
+        + "1\tfalse\n"
+        + "2\ttrue\n"
+        + "3\tfalse\n"
+        + "4\tfalse\n"
+        + "5\tfalse";
+    Assert.assertNotNull(actual);
+    Assert.assertEquals(Code.SUCCESS, actual.code());
+    Assert.assertEquals(gt, actual.message().get(0).getData());
+  }
+
+  @Test
+  public void testSetTimeDisplay() {
+    String longGT = "Time\troot.test.wf01.wt01.status\n"
+        + "1\tfalse\n"
+        + "2\ttrue\n"
+        + "3\tfalse\n"
+        + "4\tfalse\n"
+        + "5\tfalse";
+    String isoGT = "Time\troot.test.wf01.wt01.status\n"
+        + "1970-01-01T00:00:00.001Z\tfalse\n"
+        + "1970-01-01T00:00:00.002Z\ttrue\n"
+        + "1970-01-01T00:00:00.003Z\tfalse\n"
+        + "1970-01-01T00:00:00.004Z\tfalse\n"
+        + "1970-01-01T00:00:00.005Z\tfalse";
+    String specialGT = "Time\troot.test.wf01.wt01.status\n"
+        + "1970-01-01 00:00:00.001\tfalse\n"
+        + "1970-01-01 00:00:00.002\ttrue\n"
+        + "1970-01-01 00:00:00.003\tfalse\n"
+        + "1970-01-01 00:00:00.004\tfalse\n"
+        + "1970-01-01 00:00:00.005\tfalse";
+    String specialGT2 = "Time\troot.test.wf01.wt01.status\n"
+        + "1970-01 00:00\tfalse\n"
+        + "1970-01 00:00\ttrue\n"
+        + "1970-01 00:00\tfalse\n"
+        + "1970-01 00:00\tfalse\n"
+        + "1970-01 00:00\tfalse";
+
+    testSetTimeDisplay("yyyy-MM-dd HH:mm:ss.SSS", specialGT);
+    testSetTimeDisplay("yyyy-dd mm:ss", specialGT2);
+    testSetTimeDisplay("iso8601", isoGT);
+    testSetTimeDisplay("default", isoGT);
+    testSetTimeDisplay("long", longGT);
+    testSetTimeDisplay("number", longGT);
+  }
+
+  private void testSetTimeDisplay(String timeDisplay, String gt) {
+    InterpreterResult actual = interpreter
+        .internalInterpret(SET_TIMESTAMP_DISPLAY + "=" + timeDisplay, null);
+    Assert.assertNotNull(actual);
+    Assert.assertEquals(Code.SUCCESS, actual.code());
+    Assert.assertEquals("Time display type has set to " + timeDisplay,
+        actual.message().get(0).getData());
+    actual = interpreter
+        .internalInterpret("select status from root.test.wf01.wt01", null);
+    Assert.assertNotNull(actual);
+    Assert.assertEquals(Code.SUCCESS, actual.code());
+    Assert.assertEquals(gt, actual.message().get(0).getData());
+  }
+
+  @Test
+  public void testSelectColumnStatementWithTimeFilter() {
+    InterpreterResult actual = interpreter
+        .internalInterpret("select * from root.test.wf01.wt01 where time > 2 and time < 6", null);
+    String gt =
+        "Time\troot.test.wf01.wt01.temperature\troot.test.wf01.wt01.status\troot.test.wf01.wt01.hardware\n"
+            + "3\t3.3\tfalse\t33.0\n"
+            + "4\t4.4\tfalse\t44.0\n"
+            + "5\t5.5\tfalse\t55.0";
+    Assert.assertNotNull(actual);
+    Assert.assertEquals(Code.SUCCESS, actual.code());
+    Assert.assertEquals(gt, actual.message().get(0).getData());
+  }
+
+  @Test
+  public void testException() {
+    InterpreterResult actual;
+    String wrongSql;
+
+    wrongSql = "select * from";
+    actual = interpreter.internalInterpret(wrongSql, null);
+    Assert.assertNotNull(actual);
+    Assert.assertEquals(Code.ERROR, actual.code());
+    Assert.assertEquals(
+        "StatementExecutionException: 401: meet error while parsing SQL to physical plan: {}line 1:13 missing ROOT at '<EOF>'",
+        actual.message().get(0).getData());
+
+    wrongSql = "select * from a";
+    actual = interpreter.internalInterpret(wrongSql, null);
+    Assert.assertNotNull(actual);
+    Assert.assertEquals(Code.ERROR, actual.code());
+    Assert.assertEquals(
+        "StatementExecutionException: 401: meet error while parsing SQL to physical plan: {}line 1:14 mismatched input 'a' expecting {FROM, ',', '.'}",
+        actual.message().get(0).getData());
+
+    wrongSql = "select * from root a";
+    Assert.assertNotNull(actual);
+    Assert.assertEquals(Code.ERROR, actual.code());
+    Assert.assertEquals(
+        "StatementExecutionException: 401: meet error while parsing SQL to physical plan: {}line 1:14 mismatched input 'a' expecting {FROM, ',', '.'}",
+        actual.message().get(0).getData());
+  }
+
+  @Test
+  public void TestMultiLines() {
+    String insert = "SET STORAGE GROUP TO root.test.wf01.wt01;\n"
+        + "CREATE TIMESERIES root.test.wf01.wt01.status WITH DATATYPE=BOOLEAN, ENCODING=PLAIN;\n"
+        + "CREATE TIMESERIES root.test.wf01.wt01.temperature WITH DATATYPE=FLOAT, ENCODING=PLAIN;\n"
+        + "CREATE TIMESERIES root.test.wf01.wt01.hardware WITH DATATYPE=INT32, ENCODING=PLAIN;\n"
+        + "\n"
+        + "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware)\n"
+        + "VALUES (1, 1.1, false, 11);\n"
+        + "\n"
+        + "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware)\n"
+        + "VALUES (2, 2.2, true, 22);\n"
+        + "\n"
+        + "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware)\n"
+        + "VALUES (3, 3.3, false, 33);\n"
+        + "\n"
+        + "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware)\n"
+        + "VALUES (4, 4.4, false, 44);\n"
+        + "\n"
+        + "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware)\n"
+        + "VALUES (5, 5.5, false, 55);\n"
+        + "\n"
+        + "\n";
+    String[] gt = new String[]{
+        "SET STORAGE GROUP TO root.test.wf01.wt01",
+        "CREATE TIMESERIES root.test.wf01.wt01.status WITH DATATYPE=BOOLEAN, ENCODING=PLAIN",
+        "CREATE TIMESERIES root.test.wf01.wt01.temperature WITH DATATYPE=FLOAT, ENCODING=PLAIN",
+        "CREATE TIMESERIES root.test.wf01.wt01.hardware WITH DATATYPE=INT32, ENCODING=PLAIN",
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (1, 1.1, false, 11)",
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (2, 2.2, true, 22)",
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (3, 3.3, false, 33)",
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (4, 4.4, false, 44)",
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (5, 5.5, false, 55)",
+    };
+    Assert.assertArrayEquals(gt, IoTDBInterpreter.parseMultiLinesSQL(insert));
+  }
+
+  @Test
+  public void TestMultiLines2() {
+    String query = "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware)\n"
+        + "VALUES (4, 4.4, false, 44);\n"
+        + "\n"
+        + "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware)\n"
+        + "VALUES (5, 5.5, false, 55);\n"
+        + "\n"
+        + "\n"
+        + "SELECT *\n"
+        + "FROM root.test.wf01.wt01\n"
+        + "WHERE time >= 1\n"
+        + "\tAND time <= 6;";
+
+    String[] gt = new String[]{
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (4, 4.4, false, 44)",
+        "INSERT INTO root.test.wf01.wt01 (timestamp, temperature, status, hardware) VALUES (5, 5.5, false, 55)",
+        "SELECT * FROM root.test.wf01.wt01 WHERE time >= 1  AND time <= 6",
+    };
+    Assert.assertArrayEquals(gt, IoTDBInterpreter.parseMultiLinesSQL(query));
+  }
+}
\ No newline at end of file