diff --git a/package.xml b/package.xml
index d95feb40..3f024f9d 100755
--- a/package.xml
+++ b/package.xml
@@ -180,6 +180,13 @@
datax
+
+ tdenginereader/target/datax/
+
+ **/*.*
+
+ datax
+
diff --git a/pom.xml b/pom.xml
index 1a9da81b..72241137 100644
--- a/pom.xml
+++ b/pom.xml
@@ -108,6 +108,7 @@
hbase20xsqlreader
hbase20xsqlwriter
kuduwriter
+ tdenginereader
diff --git a/tdenginereader/doc/tdenginereader.md b/tdenginereader/doc/tdenginereader.md
new file mode 100644
index 00000000..3c683a64
--- /dev/null
+++ b/tdenginereader/doc/tdenginereader.md
@@ -0,0 +1,162 @@
+# DataX TDengineReader
+
+## 1 快速介绍
+
+TDengineReader 插件实现了 TDengine 读取数据的功能。
+
+## 2 实现原理
+
+TDengineReader 通过TDengine的JDBC driver查询获取数据。
+
+## 3 功能说明
+
+### 3.1 配置样例
+
+```json
+{
+ "job": {
+ "content": [
+ {
+ "reader": {
+ "name": "tdenginereader",
+ "parameter": {
+ "user": "root",
+ "password": "taosdata",
+ "connection": [
+ {
+ "table": [
+ "meters"
+ ],
+ "jdbcUrl": "jdbc:TAOS-RS://192.168.56.105:6041/test?timestampFormat=TIMESTAMP"
+ }
+ ],
+ "column": [
+ "ts",
+ "current",
+ "voltage",
+ "phase"
+ ],
+ "beginDateTime": "2017-07-14 10:40:00",
+ "endDateTime": "2017-08-14 10:40:00",
+ "splitInterval": "1d"
+ }
+ },
+ "writer": {
+ "name": "streamwriter",
+ "parameter": {
+ "encoding": "UTF-8",
+ "print": true
+ }
+ }
+ }
+ ],
+ "setting": {
+ "speed": {
+ "channel": 1
+ }
+ }
+ }
+}
+```
+
+### 3.2 参数说明
+
+* **username**
+ * 描述:TDengine实例的用户名
+ * 必选:是
+ * 默认值:无
+* **password**
+ * 描述:TDengine实例的密码
+ * 必选:是
+ * 默认值:无
+* **table**
+ * 描述:所选取的需要同步的表。使用JSON的数组描述,因此支持多张表同时抽取。当配置为多张表时,用户自己需保证多张表是同一schema结构,
+ TDengineReader不予检查表是否同一逻辑表。注意,table必须包含在connection配置单元中。
+ * 必选:是
+ * 默认值:无
+* **jdbcUrl**
+ * 描述:TDengine数据库的JDBC连接信息。注意,jdbcUrl必须包含在connection配置单元中。JdbcUrl具体请参看TDengine官方文档。
+ * 必选:是
+ * 默认值:无
+* **beginDateTime**
+ * 描述:数据的开始时间,Job迁移从begineDateTime到endDateTime的数据,格式为yyyy-MM-dd HH:mm:ss
+ * 必选:是
+ * 默认值:无
+* **endDateTime**
+ * 描述:数据的结束时间,Job迁移从begineDateTime到endDateTime的数据,格式为yyyy-MM-dd HH:mm:ss
+ * 必选:是
+ * 默认值:无
+* **splitInterval**
+ * 描述:按照splitInterval来划分task, 每splitInterval创建一个task。例如,20d代表按照每20天的数据划分为1个task。
+ 可以配置的时间单位为:d(天), h(小时), m(分钟), s(秒)
+ * 必选:是
+ * 默认值:无
+
+### 3.3 类型转换
+| TDengine 数据类型 | DataX 内部类型 |
+| --------------- | ------------- |
+| TINYINT | Long |
+| SMALLINT | Long |
+| INTEGER | Long |
+| BIGINT | Long |
+| FLOAT | Double |
+| DOUBLE | Double |
+| BOOLEAN | Bool |
+| TIMESTAMP | Date |
+| BINARY | Bytes |
+| NCHAR | String |
+
+
+## 4 性能报告
+
+### 4.1 环境准备
+
+#### 4.1.1 数据特征
+
+建表语句:
+
+单行记录类似于:
+
+#### 4.1.2 机器参数
+
+* 执行DataX的机器参数为:
+ 1. cpu:
+ 2. mem:
+ 3. net: 千兆双网卡
+ 4. disc: DataX 数据不落磁盘,不统计此项
+
+* TDengine数据库机器参数为:
+ 1. cpu:
+ 2. mem:
+ 3. net: 千兆双网卡
+ 4. disc:
+
+#### 4.1.3 DataX jvm 参数
+
+ -Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError
+
+### 4.2 测试报告
+
+#### 4.2.1 单表测试报告
+
+| 通道数| DataX速度(Rec/s)|DataX流量(MB/s)| DataX机器网卡流出流量(MB/s)|DataX机器运行负载|DB网卡进入流量(MB/s)|DB运行负载|DB TPS|
+|--------| --------|--------|--------|--------|--------|--------|--------|
+|1| | | | | | | |
+|4| | | | | | | |
+|8| | | | | | | |
+|16| | | | | | | |
+|32| | | | | | | |
+
+说明:
+
+1. 这里的单表,主键类型为 bigint(20),自增。
+2. batchSize 和 通道个数,对性能影响较大。
+
+#### 4.2.4 性能测试小结
+
+1.
+2.
+
+## 5 约束限制
+
+## FAQ
\ No newline at end of file
diff --git a/tdenginereader/pom.xml b/tdenginereader/pom.xml
new file mode 100644
index 00000000..8c0f6645
--- /dev/null
+++ b/tdenginereader/pom.xml
@@ -0,0 +1,101 @@
+
+
+
+ datax-all
+ com.alibaba.datax
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ tdenginereader
+
+
+ 8
+ 8
+
+
+
+
+ com.alibaba.datax
+ datax-common
+ ${datax-project-version}
+
+
+ slf4j-log4j12
+ org.slf4j
+
+
+
+
+
+ com.taosdata.jdbc
+ taos-jdbcdriver
+ 2.0.34
+
+
+
+ junit
+ junit
+ ${junit-version}
+ test
+
+
+ com.alibaba.datax
+ plugin-rdbms-util
+ 0.0.1-SNAPSHOT
+ compile
+
+
+
+
+
+
+
+ maven-compiler-plugin
+
+ ${jdk-version}
+ ${jdk-version}
+ ${project-sourceEncoding}
+
+
+
+ maven-assembly-plugin
+
+
+ src/main/assembly/package.xml
+
+ datax
+
+
+
+ dwzip
+ package
+
+ single
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.12.4
+
+
+
+ **/*Test.java
+
+
+
+
+ true
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tdenginereader/src/main/assembly/package.xml b/tdenginereader/src/main/assembly/package.xml
new file mode 100755
index 00000000..b52f20fb
--- /dev/null
+++ b/tdenginereader/src/main/assembly/package.xml
@@ -0,0 +1,34 @@
+
+
+
+ dir
+
+ false
+
+
+ src/main/resources
+
+ plugin.json
+ plugin_job_template.json
+
+ plugin/reader/tdenginereader
+
+
+ target/
+
+ tdenginereader-0.0.1-SNAPSHOT.jar
+
+ plugin/reader/tdenginereader
+
+
+
+
+
+ false
+ plugin/reader/tdenginereader/libs
+ runtime
+
+
+
diff --git a/tdenginereader/src/main/java/com/alibaba/datax/plugin/reader/Key.java b/tdenginereader/src/main/java/com/alibaba/datax/plugin/reader/Key.java
new file mode 100644
index 00000000..eddf98eb
--- /dev/null
+++ b/tdenginereader/src/main/java/com/alibaba/datax/plugin/reader/Key.java
@@ -0,0 +1,19 @@
+package com.alibaba.datax.plugin.reader;
+
+public class Key {
+
+ public static final String JDBC_URL = "jdbcUrl";
+// public static final String HOST = "host";
+// public static final String PORT = "port";
+// public static final String DB = "db";
+ public static final String TABLE = "table";
+ public static final String USER = "user";
+ public static final String PASSWORD = "password";
+ public static final String CONNECTION = "connection";
+// public static final String SQL = "sql";
+ public static final String BEGIN_DATETIME = "beginDateTime";
+ public static final String END_DATETIME = "endDateTime";
+ public static final String SPLIT_INTERVAL = "splitInterval";
+ public static final String COLUMN = "column";
+ public static final String MANDATORY_ENCODING = "mandatoryEncoding";
+}
diff --git a/tdenginereader/src/main/java/com/alibaba/datax/plugin/reader/TDengineReader.java b/tdenginereader/src/main/java/com/alibaba/datax/plugin/reader/TDengineReader.java
new file mode 100644
index 00000000..dfdce7b4
--- /dev/null
+++ b/tdenginereader/src/main/java/com/alibaba/datax/plugin/reader/TDengineReader.java
@@ -0,0 +1,291 @@
+package com.alibaba.datax.plugin.reader;
+
+import com.alibaba.datax.common.constant.CommonConstant;
+import com.alibaba.datax.common.element.*;
+import com.alibaba.datax.common.exception.DataXException;
+import com.alibaba.datax.common.plugin.RecordSender;
+import com.alibaba.datax.common.spi.Reader;
+import com.alibaba.datax.common.util.Configuration;
+import com.alibaba.datax.plugin.rdbms.util.DataBaseType;
+import com.alibaba.fastjson.JSON;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+import java.sql.*;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class TDengineReader extends Reader {
+ private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+ public static class Job extends Reader.Job {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Job.class);
+ private Configuration originalConfig;
+
+ @Override
+ public void init() {
+ this.originalConfig = super.getPluginJobConf();
+ // check user
+ String user = this.originalConfig.getString(Key.USER);
+ if (StringUtils.isBlank(user))
+ throw DataXException.asDataXException(TDengineReaderErrorCode.REQUIRED_VALUE, "The parameter [" + Key.USER + "] is not set.");
+
+ // check password
+ String password = this.originalConfig.getString(Key.PASSWORD);
+ if (StringUtils.isBlank(password))
+ throw DataXException.asDataXException(TDengineReaderErrorCode.REQUIRED_VALUE, "The parameter [" + Key.PASSWORD + "] is not set.");
+
+ SimpleDateFormat format = new SimpleDateFormat(DATETIME_FORMAT);
+ // check beginDateTime
+ String beginDatetime = this.originalConfig.getString(Key.BEGIN_DATETIME);
+ if (StringUtils.isBlank(beginDatetime))
+ throw DataXException.asDataXException(TDengineReaderErrorCode.REQUIRED_VALUE, "The parameter [" + Key.BEGIN_DATETIME + "] is not set.");
+ Long start;
+ try {
+ start = format.parse(beginDatetime).getTime();
+ } catch (ParseException e) {
+ throw DataXException.asDataXException(TDengineReaderErrorCode.ILLEGAL_VALUE, "The parameter [" + Key.BEGIN_DATETIME + "] needs to conform to the [" + DATETIME_FORMAT + "] format.");
+ }
+
+ // check endDateTime
+ String endDatetime = this.originalConfig.getString(Key.END_DATETIME);
+ if (StringUtils.isBlank(endDatetime))
+ throw DataXException.asDataXException(TDengineReaderErrorCode.REQUIRED_VALUE, "The parameter [" + Key.END_DATETIME + "] is not set.");
+ Long end;
+ try {
+ end = format.parse(endDatetime).getTime();
+ } catch (ParseException e) {
+ throw DataXException.asDataXException(TDengineReaderErrorCode.ILLEGAL_VALUE, "The parameter [" + Key.END_DATETIME + "] needs to conform to the [" + DATETIME_FORMAT + "] format.");
+ }
+ if (start >= end)
+ throw DataXException.asDataXException(TDengineReaderErrorCode.ILLEGAL_VALUE, "The parameter [" + Key.BEGIN_DATETIME + "] should be less than the parameter [" + Key.END_DATETIME + "].");
+
+ // check splitInterval
+ String splitInterval = this.originalConfig.getString(Key.SPLIT_INTERVAL);
+ Long split;
+ if (StringUtils.isBlank(splitInterval))
+ throw DataXException.asDataXException(TDengineReaderErrorCode.REQUIRED_VALUE, "The parameter [" + Key.SPLIT_INTERVAL + "] is not set.");
+ try {
+ split = parseSplitInterval(splitInterval);
+ } catch (Exception e) {
+ throw DataXException.asDataXException(TDengineReaderErrorCode.ILLEGAL_VALUE, "The parameter [" + Key.SPLIT_INTERVAL + "] should be like: \"123d|h|m|s\", error: " + e.getMessage());
+ }
+
+ this.originalConfig.set(Key.BEGIN_DATETIME, start);
+ this.originalConfig.set(Key.END_DATETIME, end);
+ this.originalConfig.set(Key.SPLIT_INTERVAL, split);
+
+ // check connection
+ List