diff --git a/documentation/webdocs/markdowndocs/connector-ch.md b/documentation/webdocs/markdowndocs/connector-ch.md
index a2a9161ce7..1e2e3b6989 100644
--- a/documentation/webdocs/markdowndocs/connector-ch.md
+++ b/documentation/webdocs/markdowndocs/connector-ch.md
@@ -188,58 +188,107 @@ TDengine提供时间驱动的实时流式计算API。可以每隔一指定的时
## Java Connector
-### JDBC接口
+TDengine 为了方便 Java 应用使用,提供了遵循 JDBC 标准(3.0)API 规范的 `taos-jdbcdriver` 实现。目前可以通过 [Sonatype Repository][1] 搜索并下载。
-如果用户使用Java开发企业级应用,可选用 TDengine 提供的 JDBC Driver 来调用服务。TDengine 提供的 JDBC Driver 是标准 JDBC 规范的子集,遵循 JDBC 标准 (3.0)API 规范,支持现有的各种 Java 开发框架。目前 TDengine 的 JDBC driver 已经发布到 Sonatype Maven Repository。因此用户开发时,需要在 pom.xml 文件中进行如下配置:
+由于 TDengine 是使用 c 语言开发的,使用 taos-jdbcdriver 驱动包时需要依赖系统对应的本地函数库。
+
+* libtaos.so
+ 在 linux 系统中成功安装 TDengine 后,依赖的本地函数库 libtaos.so 文件会被自动拷贝至 /usr/lib/libtaos.so,该目录包含在 Linux 自动扫描路径上,无需单独指定。
+
+* taos.dll
+ 在 windows 系统中安装完客户端之后,驱动包依赖的 taos.dll 文件会自动拷贝到系统默认搜索路径 C:/Windows/System32 下,同样无需要单独指定。
+
+> 注意:在 windows 环境开发时需要安装 TDengine 对应的 windows 版本客户端,由于目前没有提供 Linux 环境单独的客户端,需要安装 TDengine 才能使用。
+
+TDengine 的 JDBC 驱动实现尽可能的与关系型数据库驱动保持一致,但时序空间数据库与关系对象型数据库服务的对象和技术特征的差异导致 taos-jdbcdriver 并未完全实现 JDBC 标准规范。在使用时需要注意以下几点:
+
+* TDengine 不提供针对单条数据记录的删除和修改的操作,驱动中也没有支持相关方法。
+* 由于不支持删除和修改,所以也不支持事务操作。
+* 目前不支持表间的 union 操作。
+* 目前不支持嵌套查询(nested query),`对每个 Connection 的实例,至多只能有一个打开的 ResultSet 实例;如果在 ResultSet还没关闭的情况下执行了新的查询,TSDBJDBCDriver 则会自动关闭上一个 ResultSet`。
+
+
+## TAOS-JDBCDriver 版本以及支持的 TDengine 版本和 JDK 版本
+
+| taos-jdbcdriver 版本 | TDengine 版本 | JDK 版本 |
+| --- | --- | --- |
+| 1.0.3 | 1.6.1.x 及以上 | 1.8.x |
+| 1.0.2 | 1.6.1.x 及以上 | 1.8.x |
+| 1.0.1 | 1.6.1.x 及以上 | 1.8.x |
+
+## TDengine DataType 和 Java DataType
+
+TDengine 目前支持时间戳、数字、字符、布尔类型,与 Java 对应类型转换如下:
+
+| TDengine DataType | Java DataType |
+| --- | --- |
+| TIMESTAMP | java.sql.Timestamp |
+| INT | java.lang.Integer |
+| BIGINT | java.lang.Long |
+| FLOAT | java.lang.Float |
+| DOUBLE | java.lang.Double |
+| SMALLINT, TINYINT |java.lang.Short |
+| BOOL | java.lang.Boolean |
+| BINARY, NCHAR | java.lang.String |
+
+## 如何获取 TAOS-JDBCDriver
+
+### maven 仓库
+
+目前 taos-jdbcdriver 已经发布到 [Sonatype Repository][1] 仓库,且各大仓库都已同步。
+* [sonatype][8]
+* [mvnrepository][9]
+* [maven.aliyun][10]
+
+maven 项目中使用如下 pom.xml 配置即可:
```xml
-
-
-
- oss-sonatype
- oss-sonatype
- https://oss.sonatype.org/content/groups/public
-
-
-
com.taosdata.jdbc
taos-jdbcdriver
- 1.0.1
+ 1.0.3
-
```
-TDengine 的驱动程序包的在不同操作系统上依赖不同的本地函数库(均由C语言编写)。Linux系统上,依赖一个名为`libtaos.so` 的本地库,.so即"Shared Object"缩写。成功安装TDengine后,`libtaos.so` 文件会被自动拷贝至`/usr/local/lib/taos`目录下,该目录也包含在Linux上自动扫描路径上。Windows系统上,JDBC驱动程序依赖于一个名为`taos.dll` 的本地库,.dll是动态链接库"Dynamic Link Library"的缩写。Windows上成功安装客户端后,JDBC驱动程序包默认位于`C:/TDengine/driver/JDBC/`目录下;其依赖的动态链接库`taos.dll`文件位于`C:/TDengine/driver/C`目录下,`taos.dll` 会被自动拷贝至系统默认搜索路径`C:/Windows/System32`下。
+### 源码编译打包
-TDengine的JDBC Driver遵循标准JDBC规范,开发人员可以参考Oracle官方的JDBC相关文档来找到具体的接口和方法的定义与用法。TDengine的JDBC驱动在连接配置和支持的方法上与传统数据库驱动稍有不同。
+下载 [TDengine][3] 源码之后,进入 taos-jdbcdriver 源码目录 `src/connector/jdbc` 执行 `mvn clean package` 即可生成相应 jar 包。
-TDengine的JDBC URL规范格式为:
-`jdbc:TSDB://{host_ip}:{port}/{database_name}?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]`
+## 使用说明
-其中,`{}`中的内容必须,`[]`中为可选。配置参数说明如下:
-
-- user:登陆TDengine所用用户名;默认值root
-- password:用户登陆密码;默认值taosdata
-- charset:客户端使用的字符集;默认值为系统字符集
-- cfgdir:客户端配置文件目录路径;Linux OS上默认值`/etc/taos` ,Windows OS上默认值 `C:/TDengine/cfg`
-- locale:客户端语言环境;默认值系统当前locale
-- timezone:客户端使用的时区;默认值为系统当前时区
-
-以上所有参数均可在调用java.sql.DriverManager类创建连接时指定,示例如下:
+### 获取连接
+如下所示配置即可获取 TDengine Connection:
```java
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.util.Properties;
-import com.taosdata.jdbc.TSDBDriver;
+Class.forName("com.taosdata.jdbc.TSDBDriver");
+String jdbcUrl = "jdbc:TAOS://127.0.0.1:6030/log?user=root&password=taosdata";
+Connection conn = DriverManager.getConnection(jdbcUrl);
+```
+> 端口 6030 为默认连接端口,JDBC URL 中的 log 为系统本身的监控数据库。
+TDengine 的 JDBC URL 规范格式为:
+`jdbc:TSDB://{host_ip}:{port}/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]`
+
+其中,`{}` 中的内容必须,`[]` 中为可选。配置参数说明如下:
+
+* user:登录 TDengine 用户名,默认值 root。
+* password:用户登录密码,默认值 taosdata。
+* charset:客户端使用的字符集,默认值为系统字符集。
+* cfgdir:客户端配置文件目录路径,Linux OS 上默认值 /etc/taos ,Windows OS 上默认值 C:/TDengine/cfg。
+* locale:客户端语言环境,默认值系统当前 locale。
+* timezone:客户端使用的时区,默认值为系统当前时区。
+
+以上参数可以在 3 处配置,`优先级由高到低`分别如下:
+1. JDBC URL 参数
+ 如上所述,可以在 JDBC URL 的参数中指定。
+2. java.sql.DriverManager.getConnection(String jdbcUrl, Properties connProps)
+```java
public Connection getConn() throws Exception{
- Class.forName("com.taosdata.jdbc.TSDBDriver");
- String jdbcUrl = "jdbc:TAOS://127.0.0.1:0/db?user=root&password=taosdata";
+ Class.forName("com.taosdata.jdbc.TSDBDriver");
+ String jdbcUrl = "jdbc:TAOS://127.0.0.1:0/log?user=root&password=taosdata";
Properties connProps = new Properties();
connProps.setProperty(TSDBDriver.PROPERTY_KEY_USER, "root");
connProps.setProperty(TSDBDriver.PROPERTY_KEY_PASSWORD, "taosdata");
@@ -252,16 +301,203 @@ public Connection getConn() throws Exception{
}
```
-这些配置参数中除了cfgdir外,均可在客户端配置文件taos.cfg中进行配置。调用java.sql.DriverManager时声明的配置参数优先级最高,JDBC URL的优先级次之,配置文件的优先级最低。例如charset同时在配置文件taos.cfg中配置,也在JDBC URL中配置,则使用JDBC URL中的配置值。
+3. 客户端配置文件 taos.cfg
-此外,尽管TDengine的JDBC驱动实现尽可能的与关系型数据库驱动保持一致,但时序空间数据库与关系对象型数据库服务的对象和技术特征的差异导致TDengine的Java API并不能与标准完全相同。对于有大量关系型数据库开发经验而初次接触TDengine的开发者来说,有以下一些值的注意的地方:
+ linux 系统默认配置文件为 /var/lib/taos/taos.cfg,windows 系统默认配置文件路径为 C:\TDengine\cfg\taos.cfg。
+```properties
+# client default username
+# defaultUser root
-* TDengine不提供针对单条数据记录的删除和修改的操作,驱动中也没有支持相关方法
-* 目前TDengine不支持表间的join或union操作,因此也缺乏对该部分API的支持
-* TDengine支持批量写入,但是支持停留在SQL语句级别,而不是API级别,也就是说用户需要通过写特殊的SQL语句来实现批量
-* 目前TDengine不支持嵌套查询(nested query),对每个Connection的实例,至多只能有一个打开的ResultSet实例;如果在ResultSet还没关闭的情况下执行了新的查询,TSDBJDBCDriver则会自动关闭上一个ResultSet
+# client default password
+# defaultPass taosdata
-对于TDengine操作的报错信息,用户可使用JDBCDriver包里提供的枚举类TSDBError.java来获取error message和error code的列表。对于更多的具体操作的相关代码,请参考TDengine提供的使用示范项目`JDBCDemo`。
+# default system charset
+# charset UTF-8
+
+# system locale
+# locale en_US.UTF-8
+```
+> 更多详细配置请参考[客户端配置][13]
+
+### 创建数据库和表
+
+```java
+Statement stmt = conn.createStatement();
+
+// create database
+stmt.executeUpdate("create database if not exists db");
+
+// use database
+stmt.executeUpdate("use db");
+
+// create table
+stmt.executeUpdate("create table if not exists tb (ts timestamp, temperature int, humidity float)");
+```
+> 注意:如果不使用 `use db` 指定数据库,则后续对表的操作都需要增加数据库名称作为前缀,如 db.tb。
+
+### 插入数据
+
+```java
+// insert data
+int affectedRows = stmt.executeUpdate("insert into tb values(now, 23, 10.3) (now + 1s, 20, 9.3)");
+
+System.out.println("insert " + affectedRows + " rows.");
+```
+> now 为系统内部函数,默认为服务器当前时间。
+> `now + 1s` 代表服务器当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒), s(秒), m(分), h(小时), d(天),w(周), n(月), y(年)。
+
+### 查询数据
+
+```java
+// query data
+ResultSet resultSet = stmt.executeQuery("select * from tb");
+
+Timestamp ts = null;
+int temperature = 0;
+float humidity = 0;
+while(resultSet.next()){
+
+ ts = resultSet.getTimestamp(1);
+ temperature = resultSet.getInt(2);
+ humidity = resultSet.getFloat("humidity");
+
+ System.out.printf("%s, %d, %s\n", ts, temperature, humidity);
+}
+```
+> 查询和操作关系型数据库一致,使用下标获取返回字段内容时从 1 开始,建议使用字段名称获取。
+
+
+### 关闭资源
+
+```java
+resultSet.close();
+stmt.close();
+conn.close();
+```
+> `注意务必要将 connection 进行关闭`,否则会出现连接泄露。
+## 与连接池使用
+
+**HikariCP**
+
+* 引入相应 HikariCP maven 依赖:
+```xml
+
+ com.zaxxer
+ HikariCP
+ 3.4.1
+
+```
+
+* 使用示例如下:
+```java
+ public static void main(String[] args) throws SQLException {
+ HikariConfig config = new HikariConfig();
+ config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log");
+ config.setUsername("root");
+ config.setPassword("taosdata");
+
+ config.setMinimumIdle(3); //minimum number of idle connection
+ config.setMaximumPoolSize(10); //maximum number of connection in the pool
+ config.setConnectionTimeout(10000); //maximum wait milliseconds for get connection from pool
+ config.setIdleTimeout(60000); // max idle time for recycle idle connection
+ config.setConnectionTestQuery("describe log.dn"); //validation query
+ config.setValidationTimeout(3000); //validation query timeout
+
+ HikariDataSource ds = new HikariDataSource(config); //create datasource
+
+ Connection connection = ds.getConnection(); // get connection
+ Statement statement = connection.createStatement(); // get statement
+
+ //query or insert
+ // ...
+
+ connection.close(); // put back to conneciton pool
+}
+```
+> 通过 HikariDataSource.getConnection() 获取连接后,使用完成后需要调用 close() 方法,实际上它并不会关闭连接,只是放回连接池中。
+> 更多 HikariCP 使用问题请查看[官方说明][5]
+
+**Druid**
+
+* 引入相应 Druid maven 依赖:
+
+```xml
+
+ com.alibaba
+ druid
+ 1.1.20
+
+```
+
+* 使用示例如下:
+```java
+public static void main(String[] args) throws Exception {
+ Properties properties = new Properties();
+ properties.put("driverClassName","com.taosdata.jdbc.TSDBDriver");
+ properties.put("url","jdbc:TAOS://127.0.0.1:6030/log");
+ properties.put("username","root");
+ properties.put("password","taosdata");
+
+ properties.put("maxActive","10"); //maximum number of connection in the pool
+ properties.put("initialSize","3");//initial number of connection
+ properties.put("maxWait","10000");//maximum wait milliseconds for get connection from pool
+ properties.put("minIdle","3");//minimum number of connection in the pool
+
+ properties.put("timeBetweenEvictionRunsMillis","3000");// the interval milliseconds to test connection
+
+ properties.put("minEvictableIdleTimeMillis","60000");//the minimum milliseconds to keep idle
+ properties.put("maxEvictableIdleTimeMillis","90000");//the maximum milliseconds to keep idle
+
+ properties.put("validationQuery","describe log.dn"); //validation query
+ properties.put("testWhileIdle","true"); // test connection while idle
+ properties.put("testOnBorrow","false"); // don't need while testWhileIdle is true
+ properties.put("testOnReturn","false"); // don't need while testWhileIdle is true
+
+ //create druid datasource
+ DataSource ds = DruidDataSourceFactory.createDataSource(properties);
+ Connection connection = ds.getConnection(); // get connection
+ Statement statement = connection.createStatement(); // get statement
+
+ //query or insert
+ // ...
+
+ connection.close(); // put back to conneciton pool
+}
+```
+> 更多 druid 使用问题请查看[官方说明][6]
+
+**注意事项**
+* TDengine `v1.6.4.1` 版本开始提供了一个专门用于心跳检测的函数 `select server_status()`,所以在使用连接池时推荐使用 `select server_status()` 进行 Validation Query。
+
+如下所示,`select server_status()` 执行成功会返回 `1`。
+```shell
+taos> select server_status();
+server_status()|
+================
+1 |
+Query OK, 1 row(s) in set (0.000141s)
+```
+
+## 与框架使用
+
+* Spring JdbcTemplate 中使用 taos-jdbcdriver,可参考 [SpringJdbcTemplate][11]
+* Springboot + Mybatis 中使用,可参考 [springbootdemo][12]
+
+## 常见问题
+
+* java.lang.UnsatisfiedLinkError: no taos in java.library.path
+
+ **原因**:程序没有找到依赖的本地函数库 taos。
+
+ **解决方法**:windows 下可以将 C:\TDengine\driver\taos.dll 拷贝到 C:\Windows\System32\ 目录下,linux 下将建立如下软链 ` ln -s /usr/local/taos/driver/libtaos.so.x.x.x.x /usr/lib/libtaos.so` 即可。
+
+* java.lang.UnsatisfiedLinkError: taos.dll Can't load AMD 64 bit on a IA 32-bit platform
+
+ **原因**:目前 TDengine 只支持 64 位 JDK。
+
+ **解决方法**:重新安装 64 位 JDK。
+
+* 其它问题请参考 [Issues][7]
## Python Connector
@@ -811,30 +1047,30 @@ https://gitee.com/maikebing/Maikebing.EntityFrameworkCore.Taos
```
├── cfg
-│ └── taos.cfg
+├───└── taos.cfg
├── connector
-│ ├── go
-│ ├── grafana
-│ ├── jdbc
-│ └── python
+├───├── go
+├───├── grafana
+├───├── jdbc
+├───└── python
├── driver
-│ ├── taos.dll
-│ ├── taos.exp
-│ └── taos.lib
+├───├── taos.dll
+├───├── taos.exp
+├───└── taos.lib
├── examples
-│ ├── bash
-│ ├── c
-│ ├── C#
-│ ├── go
-│ ├── JDBC
-│ ├── lua
-│ ├── matlab
-│ ├── nodejs
-│ ├── python
-│ ├── R
-│ └── rust
+├───├── bash
+├───├── c
+├───├── C#
+├───├── go
+├───├── JDBC
+├───├── lua
+├───├── matlab
+├───├── nodejs
+├───├── python
+├───├── R
+├───└── rust
├── include
-│ └── taos.h
+├───└── taos.h
└── taos.exe
```
@@ -879,3 +1115,16 @@ TDengine在Window系统上提供的API与Linux系统是相同的, 应用程序
+ 将Windows开发包(taos.dll)放置到system32目录下。
+[1]: https://search.maven.org/artifact/com.taosdata.jdbc/taos-jdbcdriver
+[2]: https://mvnrepository.com/artifact/com.taosdata.jdbc/taos-jdbcdriver
+[3]: https://github.com/taosdata/TDengine
+[4]: https://www.taosdata.com/blog/2019/12/03/jdbcdriver%e6%89%be%e4%b8%8d%e5%88%b0%e5%8a%a8%e6%80%81%e9%93%be%e6%8e%a5%e5%ba%93/
+[5]: https://github.com/brettwooldridge/HikariCP
+[6]: https://github.com/alibaba/druid
+[7]: https://github.com/taosdata/TDengine/issues
+[8]: https://search.maven.org/artifact/com.taosdata.jdbc/taos-jdbcdriver
+[9]: https://mvnrepository.com/artifact/com.taosdata.jdbc/taos-jdbcdriver
+[10]: https://maven.aliyun.com/mvn/search
+[11]: https://github.com/taosdata/TDengine/tree/develop/tests/examples/JDBC/SpringJdbcTemplate
+[12]: https://github.com/taosdata/TDengine/tree/develop/tests/examples/JDBC/springbootdemo
+[13]: https://www.taosdata.com/cn/documentation/administrator/#%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%85%8D%E7%BD%AE
diff --git a/packaging/tools/install.sh b/packaging/tools/install.sh
index 019e614e0b..e1bcce401d 100755
--- a/packaging/tools/install.sh
+++ b/packaging/tools/install.sh
@@ -313,9 +313,9 @@ vercomp () {
function is_version_compatible() {
- curr_version=$(${bin_dir}/taosd -V | head -1 | cut -d ' ' -f 2)
+ curr_version=$(${bin_dir}/taosd -V | head -1 | cut -d ' ' -f 3)
- min_compatible_version=$(${script_dir}/bin/taosd -V | head -1 | cut -d ' ' -f 4)
+ min_compatible_version=$(${script_dir}/bin/taosd -V | head -1 | cut -d ' ' -f 5)
vercomp $curr_version $min_compatible_version
case $? in
diff --git a/src/client/src/tscAst.c b/src/client/src/tscAst.c
index 48e147a337..6e72fa8438 100644
--- a/src/client/src/tscAst.c
+++ b/src/client/src/tscAst.c
@@ -108,12 +108,12 @@ static tSQLSyntaxNode *tSQLSyntaxNodeCreate(SSchema *pSchema, int32_t numOfCols,
return NULL;
}
- int32_t i = 0;
size_t nodeSize = sizeof(tSQLSyntaxNode);
tSQLSyntaxNode *pNode = NULL;
if (pToken->type == TK_ID || pToken->type == TK_TBNAME) {
if (pToken->type == TK_ID) {
+ int32_t i = 0;
do {
size_t len = strlen(pSchema[i].name);
if (strncmp(pToken->z, pSchema[i].name, pToken->n) == 0 && pToken->n == len) break;
@@ -268,8 +268,8 @@ static tSQLSyntaxNode *createSyntaxTree(SSchema *pSchema, int32_t numOfCols, cha
}
// get the operator of expr
- uint8_t optr = getBinaryExprOptr(&t0);
- if (optr <= 0) {
+ uint8_t optr = getBinaryExprOptr(&t0);
+ if (optr == 0) {
pError("not support binary operator:%d", t0.type);
tSQLSyntaxNodeDestroy(pLeft, NULL);
return NULL;
@@ -323,10 +323,11 @@ static tSQLSyntaxNode *createSyntaxTree(SSchema *pSchema, int32_t numOfCols, cha
pn->colId = -1;
return pn;
} else {
- uint8_t localOptr = getBinaryExprOptr(&t0);
- if (localOptr <= 0) {
+ uint8_t localOptr = getBinaryExprOptr(&t0);
+ if (localOptr == 0) {
pError("not support binary operator:%d", t0.type);
return NULL;
+ free(pBinExpr)
}
return parseRemainStr(str, pBinExpr, pSchema, localOptr, numOfCols, i);
@@ -418,16 +419,17 @@ void tSQLBinaryExprToString(tSQLBinaryExpr *pExpr, char *dst, int32_t *len) {
if (pExpr == NULL) {
*dst = 0;
*len = 0;
+ return;
}
- int32_t lhs = tSQLBinaryExprToStringImpl(pExpr->pLeft, dst, pExpr->pLeft->nodeType);
+ int32_t lhs = tSQLBinaryExprToStringImpl(pExpr->pLeft, dst, pExpr->pLeft->nodeType);
dst += lhs;
*len = lhs;
- char *start = tSQLOptrToString(pExpr->nSQLBinaryOptr, dst);
+ char *start = tSQLOptrToString(pExpr->nSQLBinaryOptr, dst);
*len += (start - dst);
- *len += tSQLBinaryExprToStringImpl(pExpr->pRight, start, pExpr->pRight->nodeType);
+ *len += tSQLBinaryExprToStringImpl(pExpr->pRight, start, pExpr->pRight->nodeType);
}
static void UNUSED_FUNC destroySyntaxTree(tSQLSyntaxNode *pNode) { tSQLSyntaxNodeDestroy(pNode, NULL); }
@@ -650,7 +652,8 @@ void tSQLListTraverseOnResult(struct tSQLBinaryExpr *pExpr, bool (*fp)(tSkipList
// brutal force search
int64_t num = pResult->num;
for (int32_t i = 0, j = 0; i < pResult->num; ++i) {
- if (fp == NULL || (fp != NULL && fp(pResult->pRes[i], pExpr->info) == true)) {
+ //if (fp == NULL || (fp != NULL && fp(pResult->pRes[i], pExpr->info) == true)) {
+ if (fp == NULL || (fp(pResult->pRes[i], pExpr->info) == true)) {
pResult->pRes[j++] = pResult->pRes[i];
} else {
num--;
diff --git a/src/client/src/tscAsync.c b/src/client/src/tscAsync.c
index abf91e7c43..61db9602cf 100644
--- a/src/client/src/tscAsync.c
+++ b/src/client/src/tscAsync.c
@@ -157,7 +157,6 @@ static void tscProcessAsyncRetrieveImpl(void *param, TAOS_RES *tres, int numOfRo
SSqlObj *pSql = (SSqlObj *)tres;
if (pSql == NULL) { // error
tscError("sql object is NULL");
- tscQueueAsyncError(pSql->fetchFp, param);
return;
}
diff --git a/src/client/src/tscParseInsert.c b/src/client/src/tscParseInsert.c
index 58cfcda17e..48423417e7 100644
--- a/src/client/src/tscParseInsert.c
+++ b/src/client/src/tscParseInsert.c
@@ -392,7 +392,7 @@ static int32_t tsCheckTimestamp(STableDataBlocks *pDataBlocks, const char *start
}
int tsParseOneRowData(char **str, STableDataBlocks *pDataBlocks, SSchema schema[], SParsedDataColInfo *spd, char *error,
- int16_t timePrec) {
+ int16_t timePrec, int32_t *code, char* tmpTokenBuf) {
int32_t index = 0;
bool isPrevOptr;
SSQLToken sToken = {0};
@@ -418,6 +418,7 @@ int tsParseOneRowData(char **str, STableDataBlocks *pDataBlocks, SSchema schema[
}
strcpy(error, "client out of memory");
+ *code = TSDB_CODE_CLI_OUT_OF_MEMORY;
return -1;
}
@@ -425,23 +426,42 @@ int tsParseOneRowData(char **str, STableDataBlocks *pDataBlocks, SSchema schema[
(sToken.type != TK_FLOAT) && (sToken.type != TK_BOOL) && (sToken.type != TK_NULL)) ||
(sToken.n == 0) || (sToken.type == TK_RP)) {
tscInvalidSQLErrMsg(error, "invalid data or symbol", sToken.z);
+ *code = TSDB_CODE_INVALID_SQL;
return -1;
}
// Remove quotation marks
if (TK_STRING == sToken.type) {
- sToken.z++;
- sToken.n -= 2;
+ // delete escape character: \\, \', \"
+ char delim = sToken.z[0];
+ int32_t cnt = 0;
+ int32_t j = 0;
+ for (int32_t i = 1; i < sToken.n - 1; ++i) {
+ if (sToken.z[i] == delim || sToken.z[i] == '\\') {
+ if (sToken.z[i + 1] == delim) {
+ cnt++;
+ continue;
+ }
+ }
+
+ tmpTokenBuf[j] = sToken.z[i];
+ j++;
+ }
+ tmpTokenBuf[j] = 0;
+ sToken.z = tmpTokenBuf;
+ sToken.n -= 2 + cnt;
}
bool isPrimaryKey = (colIndex == PRIMARYKEY_TIMESTAMP_COL_INDEX);
int32_t ret = tsParseOneColumnData(pSchema, &sToken, start, error, str, isPrimaryKey, timePrec);
if (ret != TSDB_CODE_SUCCESS) {
+ *code = TSDB_CODE_INVALID_SQL;
return -1; // NOTE: here 0 mean error!
}
if (isPrimaryKey && tsCheckTimestamp(pDataBlocks, start) != TSDB_CODE_SUCCESS) {
tscInvalidSQLErrMsg(error, "client time/server time can not be mixed up", sToken.z);
+ *code = TSDB_CODE_INVALID_TIME_STAMP;
return -1;
}
}
@@ -476,7 +496,7 @@ static int32_t rowDataCompar(const void *lhs, const void *rhs) {
}
int tsParseValues(char **str, STableDataBlocks *pDataBlock, SMeterMeta *pMeterMeta, int maxRows,
- SParsedDataColInfo *spd, char *error) {
+ SParsedDataColInfo *spd, char *error, int32_t *code, char* tmpTokenBuf) {
int32_t index = 0;
SSQLToken sToken;
@@ -487,6 +507,7 @@ int tsParseValues(char **str, STableDataBlocks *pDataBlock, SMeterMeta *pMeterMe
if (spd->hasVal[0] == false) {
strcpy(error, "primary timestamp column can not be null");
+ *code = TSDB_CODE_INVALID_SQL;
return -1;
}
@@ -500,12 +521,13 @@ int tsParseValues(char **str, STableDataBlocks *pDataBlock, SMeterMeta *pMeterMe
int32_t tSize = tscAllocateMemIfNeed(pDataBlock, pMeterMeta->rowSize);
if (0 == tSize) {
strcpy(error, "client out of memory");
+ *code = TSDB_CODE_CLI_OUT_OF_MEMORY;
return -1;
}
maxRows += tSize;
}
- int32_t len = tsParseOneRowData(str, pDataBlock, pSchema, spd, error, precision);
+ int32_t len = tsParseOneRowData(str, pDataBlock, pSchema, spd, error, precision, code, tmpTokenBuf);
if (len <= 0) { // error message has been set in tsParseOneRowData
return -1;
}
@@ -517,6 +539,7 @@ int tsParseValues(char **str, STableDataBlocks *pDataBlock, SMeterMeta *pMeterMe
*str += index;
if (sToken.n == 0 || sToken.type != TK_RP) {
tscInvalidSQLErrMsg(error, ") expected", *str);
+ *code = TSDB_CODE_INVALID_SQL;
return -1;
}
@@ -525,6 +548,7 @@ int tsParseValues(char **str, STableDataBlocks *pDataBlock, SMeterMeta *pMeterMe
if (numOfRows <= 0) {
strcpy(error, "no any data points");
+ *code = TSDB_CODE_INVALID_SQL;
return -1;
} else {
return numOfRows;
@@ -636,10 +660,17 @@ static int32_t doParseInsertStatement(SSqlObj *pSql, void *pTableHashList, char
if (0 == maxNumOfRows) {
return TSDB_CODE_CLI_OUT_OF_MEMORY;
}
-
- int32_t numOfRows = tsParseValues(str, dataBuf, pMeterMeta, maxNumOfRows, spd, pCmd->payload);
+
+ int32_t code = TSDB_CODE_INVALID_SQL;
+ char* tmpTokenBuf = calloc(1, 4096); // used for deleting Escape character: \\, \', \"
+ if (NULL == tmpTokenBuf) {
+ return TSDB_CODE_CLI_OUT_OF_MEMORY;
+ }
+
+ int32_t numOfRows = tsParseValues(str, dataBuf, pMeterMeta, maxNumOfRows, spd, pCmd->payload, &code, tmpTokenBuf);
+ free(tmpTokenBuf);
if (numOfRows <= 0) {
- return TSDB_CODE_INVALID_SQL;
+ return code;
}
for (uint32_t i = 0; i < dataBuf->numOfParams; ++i) {
@@ -1173,7 +1204,7 @@ static int doPackSendDataBlock(SSqlObj *pSql, int32_t numOfRows, STableDataBlock
return TSDB_CODE_SUCCESS;
}
-static int tscInsertDataFromFile(SSqlObj *pSql, FILE *fp) {
+static int tscInsertDataFromFile(SSqlObj *pSql, FILE *fp, char *tmpTokenBuf) {
size_t readLen = 0;
char * line = NULL;
size_t n = 0;
@@ -1216,10 +1247,10 @@ static int tscInsertDataFromFile(SSqlObj *pSql, FILE *fp) {
maxRows += tSize;
}
- len = tsParseOneRowData(&lineptr, pTableDataBlock, pSchema, &spd, pCmd->payload, pMeterMeta->precision);
+ len = tsParseOneRowData(&lineptr, pTableDataBlock, pSchema, &spd, pCmd->payload, pMeterMeta->precision, &code, tmpTokenBuf);
if (len <= 0 || pTableDataBlock->numOfParams > 0) {
- pSql->res.code = TSDB_CODE_INVALID_SQL;
- return -1;
+ pSql->res.code = code;
+ return (-code);
}
pTableDataBlock->size += len;
@@ -1348,8 +1379,16 @@ void tscProcessMultiVnodesInsertForFile(SSqlObj *pSql) {
tscError("%p get meter meta failed, abort", pSql);
continue;
}
+
+ char* tmpTokenBuf = calloc(1, 4096); // used for deleting Escape character: \\, \', \"
+ if (NULL == tmpTokenBuf) {
+ tscError("%p calloc failed", pSql);
+ continue;
+ }
- int nrows = tscInsertDataFromFile(pSql, fp);
+ int nrows = tscInsertDataFromFile(pSql, fp, tmpTokenBuf);
+ free(tmpTokenBuf);
+
pCmd->pDataBlocks = tscDestroyBlockArrayList(pCmd->pDataBlocks);
if (nrows < 0) {
diff --git a/src/connector/jdbc/readme.md b/src/connector/jdbc/readme.md
new file mode 100644
index 0000000000..e81f078c15
--- /dev/null
+++ b/src/connector/jdbc/readme.md
@@ -0,0 +1,329 @@
+
+## TAOS-JDBCDriver 概述
+
+TDengine 为了方便 Java 应用使用,提供了遵循 JDBC 标准(3.0)API 规范的 `taos-jdbcdriver` 实现。目前可以通过 [Sonatype Repository][1] 搜索并下载。
+
+由于 TDengine 是使用 c 语言开发的,使用 taos-jdbcdriver 驱动包时需要依赖系统对应的本地函数库。
+
+* libtaos.so
+ 在 linux 系统中成功安装 TDengine 后,依赖的本地函数库 libtaos.so 文件会被自动拷贝至 /usr/lib/libtaos.so,该目录包含在 Linux 自动扫描路径上,无需单独指定。
+
+* taos.dll
+ 在 windows 系统中安装完客户端之后,驱动包依赖的 taos.dll 文件会自动拷贝到系统默认搜索路径 C:/Windows/System32 下,同样无需要单独指定。
+
+> 注意:在 windows 环境开发时需要安装 TDengine 对应的 windows 版本客户端,由于目前没有提供 Linux 环境单独的客户端,需要安装 TDengine 才能使用。
+
+TDengine 的 JDBC 驱动实现尽可能的与关系型数据库驱动保持一致,但时序空间数据库与关系对象型数据库服务的对象和技术特征的差异导致 taos-jdbcdriver 并未完全实现 JDBC 标准规范。在使用时需要注意以下几点:
+
+* TDengine 不提供针对单条数据记录的删除和修改的操作,驱动中也没有支持相关方法。
+* 由于不支持删除和修改,所以也不支持事务操作。
+* 目前不支持表间的 union 操作。
+* 目前不支持嵌套查询(nested query),`对每个 Connection 的实例,至多只能有一个打开的 ResultSet 实例;如果在 ResultSet还没关闭的情况下执行了新的查询,TSDBJDBCDriver 则会自动关闭上一个 ResultSet`。
+
+
+## TAOS-JDBCDriver 版本以及支持的 TDengine 版本和 JDK 版本
+
+| taos-jdbcdriver 版本 | TDengine 版本 | JDK 版本 |
+| --- | --- | --- |
+| 1.0.3 | 1.6.1.x 及以上 | 1.8.x |
+| 1.0.2 | 1.6.1.x 及以上 | 1.8.x |
+| 1.0.1 | 1.6.1.x 及以上 | 1.8.x |
+
+## TDengine DataType 和 Java DataType
+
+TDengine 目前支持时间戳、数字、字符、布尔类型,与 Java 对应类型转换如下:
+
+| TDengine DataType | Java DataType |
+| --- | --- |
+| TIMESTAMP | java.sql.Timestamp |
+| INT | java.lang.Integer |
+| BIGINT | java.lang.Long |
+| FLOAT | java.lang.Float |
+| DOUBLE | java.lang.Double |
+| SMALLINT, TINYINT |java.lang.Short |
+| BOOL | java.lang.Boolean |
+| BINARY, NCHAR | java.lang.String |
+
+## 如何获取 TAOS-JDBCDriver
+
+### maven 仓库
+
+目前 taos-jdbcdriver 已经发布到 [Sonatype Repository][1] 仓库,且各大仓库都已同步。
+* [sonatype][8]
+* [mvnrepository][9]
+* [maven.aliyun][10]
+
+maven 项目中使用如下 pom.xml 配置即可:
+
+```xml
+
+
+ com.taosdata.jdbc
+ taos-jdbcdriver
+ 1.0.3
+
+
+```
+
+### 源码编译打包
+
+下载 [TDengine][3] 源码之后,进入 taos-jdbcdriver 源码目录 `src/connector/jdbc` 执行 `mvn clean package` 即可生成相应 jar 包。
+
+
+## 使用说明
+
+### 获取连接
+
+如下所示配置即可获取 TDengine Connection:
+```java
+Class.forName("com.taosdata.jdbc.TSDBDriver");
+String jdbcUrl = "jdbc:TAOS://127.0.0.1:6030/log?user=root&password=taosdata";
+Connection conn = DriverManager.getConnection(jdbcUrl);
+```
+> 端口 6030 为默认连接端口,JDBC URL 中的 log 为系统本身的监控数据库。
+
+TDengine 的 JDBC URL 规范格式为:
+`jdbc:TSDB://{host_ip}:{port}/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]`
+
+其中,`{}` 中的内容必须,`[]` 中为可选。配置参数说明如下:
+
+* user:登录 TDengine 用户名,默认值 root。
+* password:用户登录密码,默认值 taosdata。
+* charset:客户端使用的字符集,默认值为系统字符集。
+* cfgdir:客户端配置文件目录路径,Linux OS 上默认值 /etc/taos ,Windows OS 上默认值 C:/TDengine/cfg。
+* locale:客户端语言环境,默认值系统当前 locale。
+* timezone:客户端使用的时区,默认值为系统当前时区。
+
+以上参数可以在 3 处配置,`优先级由高到低`分别如下:
+1. JDBC URL 参数
+ 如上所述,可以在 JDBC URL 的参数中指定。
+2. java.sql.DriverManager.getConnection(String jdbcUrl, Properties connProps)
+```java
+public Connection getConn() throws Exception{
+ Class.forName("com.taosdata.jdbc.TSDBDriver");
+ String jdbcUrl = "jdbc:TAOS://127.0.0.1:0/log?user=root&password=taosdata";
+ Properties connProps = new Properties();
+ connProps.setProperty(TSDBDriver.PROPERTY_KEY_USER, "root");
+ connProps.setProperty(TSDBDriver.PROPERTY_KEY_PASSWORD, "taosdata");
+ connProps.setProperty(TSDBDriver.PROPERTY_KEY_CONFIG_DIR, "/etc/taos");
+ connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
+ connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
+ connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
+ Connection conn = DriverManager.getConnection(jdbcUrl, connProps);
+ return conn;
+}
+```
+
+3. 客户端配置文件 taos.cfg
+
+ linux 系统默认配置文件为 /var/lib/taos/taos.cfg,windows 系统默认配置文件路径为 C:\TDengine\cfg\taos.cfg。
+```properties
+# client default username
+# defaultUser root
+
+# client default password
+# defaultPass taosdata
+
+# default system charset
+# charset UTF-8
+
+# system locale
+# locale en_US.UTF-8
+```
+> 更多详细配置请参考[客户端配置][13]
+
+### 创建数据库和表
+
+```java
+Statement stmt = conn.createStatement();
+
+// create database
+stmt.executeUpdate("create database if not exists db");
+
+// use database
+stmt.executeUpdate("use db");
+
+// create table
+stmt.executeUpdate("create table if not exists tb (ts timestamp, temperature int, humidity float)");
+```
+> 注意:如果不使用 `use db` 指定数据库,则后续对表的操作都需要增加数据库名称作为前缀,如 db.tb。
+
+### 插入数据
+
+```java
+// insert data
+int affectedRows = stmt.executeUpdate("insert into tb values(now, 23, 10.3) (now + 1s, 20, 9.3)");
+
+System.out.println("insert " + affectedRows + " rows.");
+```
+> now 为系统内部函数,默认为服务器当前时间。
+> `now + 1s` 代表服务器当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒), s(秒), m(分), h(小时), d(天),w(周), n(月), y(年)。
+
+### 查询数据
+
+```java
+// query data
+ResultSet resultSet = stmt.executeQuery("select * from tb");
+
+Timestamp ts = null;
+int temperature = 0;
+float humidity = 0;
+while(resultSet.next()){
+
+ ts = resultSet.getTimestamp(1);
+ temperature = resultSet.getInt(2);
+ humidity = resultSet.getFloat("humidity");
+
+ System.out.printf("%s, %d, %s\n", ts, temperature, humidity);
+}
+```
+> 查询和操作关系型数据库一致,使用下标获取返回字段内容时从 1 开始,建议使用字段名称获取。
+
+
+### 关闭资源
+
+```java
+resultSet.close();
+stmt.close();
+conn.close();
+```
+> `注意务必要将 connection 进行关闭`,否则会出现连接泄露。
+## 与连接池使用
+
+**HikariCP**
+
+* 引入相应 HikariCP maven 依赖:
+```xml
+
+ com.zaxxer
+ HikariCP
+ 3.4.1
+
+```
+
+* 使用示例如下:
+```java
+ public static void main(String[] args) throws SQLException {
+ HikariConfig config = new HikariConfig();
+ config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log");
+ config.setUsername("root");
+ config.setPassword("taosdata");
+
+ config.setMinimumIdle(3); //minimum number of idle connection
+ config.setMaximumPoolSize(10); //maximum number of connection in the pool
+ config.setConnectionTimeout(10000); //maximum wait milliseconds for get connection from pool
+ config.setIdleTimeout(60000); // max idle time for recycle idle connection
+ config.setConnectionTestQuery("describe log.dn"); //validation query
+ config.setValidationTimeout(3000); //validation query timeout
+
+ HikariDataSource ds = new HikariDataSource(config); //create datasource
+
+ Connection connection = ds.getConnection(); // get connection
+ Statement statement = connection.createStatement(); // get statement
+
+ //query or insert
+ // ...
+
+ connection.close(); // put back to conneciton pool
+}
+```
+> 通过 HikariDataSource.getConnection() 获取连接后,使用完成后需要调用 close() 方法,实际上它并不会关闭连接,只是放回连接池中。
+> 更多 HikariCP 使用问题请查看[官方说明][5]
+
+**Druid**
+
+* 引入相应 Druid maven 依赖:
+
+```xml
+
+ com.alibaba
+ druid
+ 1.1.20
+
+```
+
+* 使用示例如下:
+```java
+public static void main(String[] args) throws Exception {
+ Properties properties = new Properties();
+ properties.put("driverClassName","com.taosdata.jdbc.TSDBDriver");
+ properties.put("url","jdbc:TAOS://127.0.0.1:6030/log");
+ properties.put("username","root");
+ properties.put("password","taosdata");
+
+ properties.put("maxActive","10"); //maximum number of connection in the pool
+ properties.put("initialSize","3");//initial number of connection
+ properties.put("maxWait","10000");//maximum wait milliseconds for get connection from pool
+ properties.put("minIdle","3");//minimum number of connection in the pool
+
+ properties.put("timeBetweenEvictionRunsMillis","3000");// the interval milliseconds to test connection
+
+ properties.put("minEvictableIdleTimeMillis","60000");//the minimum milliseconds to keep idle
+ properties.put("maxEvictableIdleTimeMillis","90000");//the maximum milliseconds to keep idle
+
+ properties.put("validationQuery","describe log.dn"); //validation query
+ properties.put("testWhileIdle","true"); // test connection while idle
+ properties.put("testOnBorrow","false"); // don't need while testWhileIdle is true
+ properties.put("testOnReturn","false"); // don't need while testWhileIdle is true
+
+ //create druid datasource
+ DataSource ds = DruidDataSourceFactory.createDataSource(properties);
+ Connection connection = ds.getConnection(); // get connection
+ Statement statement = connection.createStatement(); // get statement
+
+ //query or insert
+ // ...
+
+ connection.close(); // put back to conneciton pool
+}
+```
+> 更多 druid 使用问题请查看[官方说明][6]
+
+**注意事项**
+* TDengine `v1.6.4.1` 版本开始提供了一个专门用于心跳检测的函数 `select server_status()`,所以在使用连接池时推荐使用 `select server_status()` 进行 Validation Query。
+
+如下所示,`select server_status()` 执行成功会返回 `1`。
+```shell
+taos> select server_status();
+server_status()|
+================
+1 |
+Query OK, 1 row(s) in set (0.000141s)
+```
+
+## 与框架使用
+
+* Spring JdbcTemplate 中使用 taos-jdbcdriver,可参考 [SpringJdbcTemplate][11]
+* Springboot + Mybatis 中使用,可参考 [springbootdemo][12]
+
+## 常见问题
+
+* java.lang.UnsatisfiedLinkError: no taos in java.library.path
+
+ **原因**:程序没有找到依赖的本地函数库 taos。
+
+ **解决方法**:windows 下可以将 C:\TDengine\driver\taos.dll 拷贝到 C:\Windows\System32\ 目录下,linux 下将建立如下软链 ` ln -s /usr/local/taos/driver/libtaos.so.x.x.x.x /usr/lib/libtaos.so` 即可。
+
+* java.lang.UnsatisfiedLinkError: taos.dll Can't load AMD 64 bit on a IA 32-bit platform
+
+ **原因**:目前 TDengine 只支持 64 位 JDK。
+
+ **解决方法**:重新安装 64 位 JDK。
+
+* 其它问题请参考 [Issues][7]
+
+
+
+[1]: https://search.maven.org/artifact/com.taosdata.jdbc/taos-jdbcdriver
+[2]: https://mvnrepository.com/artifact/com.taosdata.jdbc/taos-jdbcdriver
+[3]: https://github.com/taosdata/TDengine
+[4]: https://www.taosdata.com/blog/2019/12/03/jdbcdriver%e6%89%be%e4%b8%8d%e5%88%b0%e5%8a%a8%e6%80%81%e9%93%be%e6%8e%a5%e5%ba%93/
+[5]: https://github.com/brettwooldridge/HikariCP
+[6]: https://github.com/alibaba/druid
+[7]: https://github.com/taosdata/TDengine/issues
+[8]: https://search.maven.org/artifact/com.taosdata.jdbc/taos-jdbcdriver
+[9]: https://mvnrepository.com/artifact/com.taosdata.jdbc/taos-jdbcdriver
+[10]: https://maven.aliyun.com/mvn/search
+[11]: https://github.com/taosdata/TDengine/tree/develop/tests/examples/JDBC/SpringJdbcTemplate
+[12]: https://github.com/taosdata/TDengine/tree/develop/tests/examples/JDBC/springbootdemo
+[13]: https://www.taosdata.com/cn/documentation/administrator/#%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%85%8D%E7%BD%AE
\ No newline at end of file
diff --git a/src/kit/shell/src/shellEngine.c b/src/kit/shell/src/shellEngine.c
index ecda6912c3..c66f6f4be7 100644
--- a/src/kit/shell/src/shellEngine.c
+++ b/src/kit/shell/src/shellEngine.c
@@ -151,6 +151,8 @@ void shellReplaceCtrlChar(char *str) {
}
break;
default:
+ *pstr = *str;
+ pstr++;
break;
}
ctrlOn = false;
diff --git a/src/sdb/src/sdbEngine.c b/src/sdb/src/sdbEngine.c
index 5eb644a7be..7eec500199 100644
--- a/src/sdb/src/sdbEngine.c
+++ b/src/sdb/src/sdbEngine.c
@@ -171,6 +171,7 @@ int sdbInitTableByFile(SSdbTable *pTable) {
void * pMetaRow = NULL;
int total_size = 0;
int real_size = 0;
+ int maxAutoIndex = 0;
oldId = pTable->id;
if (sdbOpenSdbFile(pTable) < 0) return -1;
@@ -240,10 +241,18 @@ int sdbInitTableByFile(SSdbTable *pTable) {
rowMeta.rowSize = rowHead->rowSize;
rowMeta.row = (*(pTable->appTool))(SDB_TYPE_DECODE, NULL, rowHead->data, rowHead->rowSize, NULL);
(*sdbAddIndexFp[pTable->keyType])(pTable->iHandle, rowMeta.row, &rowMeta);
- if (pTable->keyType == SDB_KEYTYPE_AUTO) pTable->autoIndex++;
+ if (pTable->keyType == SDB_KEYTYPE_AUTO) {
+ pTable->autoIndex++;
+ maxAutoIndex = MAX(maxAutoIndex, *(int32_t*)rowHead->data);
+ }
pTable->numOfRows++;
}
} else { // already exists
+ if (pTable->keyType == SDB_KEYTYPE_AUTO) {
+ pTable->autoIndex++;
+ maxAutoIndex = MAX(maxAutoIndex, *(int32_t *) rowHead->data);
+ }
+
if (rowHead->id < 0) { // Delete the object
(*sdbDeleteIndexFp[pTable->keyType])(pTable->iHandle, rowHead->data);
(*(pTable->appTool))(SDB_TYPE_DESTROY, pMetaRow, NULL, 0, NULL);
@@ -260,6 +269,10 @@ int sdbInitTableByFile(SSdbTable *pTable) {
if (pTable->id < abs(rowHead->id)) pTable->id = abs(rowHead->id);
}
+ if (pTable->keyType == SDB_KEYTYPE_AUTO) {
+ pTable->autoIndex = maxAutoIndex;
+ }
+
sdbVersion += (pTable->id - oldId);
if (numOfDels > pTable->maxRows / 4) sdbSaveSnapShot(pTable);
diff --git a/src/system/detail/src/mgmtMeter.c b/src/system/detail/src/mgmtMeter.c
index 0862edf3eb..4e1d62e60e 100644
--- a/src/system/detail/src/mgmtMeter.c
+++ b/src/system/detail/src/mgmtMeter.c
@@ -233,6 +233,10 @@ void *mgmtMeterActionDelete(void *row, char *str, int size, int *ssize) {
pMeter = (STabObj *)row;
if (mgmtIsNormalMeter(pMeter)) {
+ if (pMeter->gid.vgId == 0) {
+ return NULL;
+ }
+
pVgroup = mgmtGetVgroup(pMeter->gid.vgId);
if (pVgroup == NULL) {
mError("id:%s not in vgroup:%d", pMeter->meterId, pMeter->gid.vgId);
@@ -426,6 +430,7 @@ void mgmtAddMeterStatisticToAcct(STabObj *pMeter, SAcctObj *pAcct) {
int mgmtInitMeters() {
void * pNode = NULL;
+ void * pLastNode = NULL;
SVgObj * pVgroup = NULL;
STabObj * pMeter = NULL;
STabObj * pMetric = NULL;
@@ -451,21 +456,47 @@ int mgmtInitMeters() {
pNode = NULL;
while (1) {
+ pLastNode = pNode;
pNode = sdbFetchRow(meterSdb, pNode, (void **)&pMeter);
if (pMeter == NULL) break;
pDb = mgmtGetDbByMeterId(pMeter->meterId);
if (pDb == NULL) {
- mError("failed to get db: %s", pMeter->meterId);
+ mError("meter:%s, failed to get db, discard it", pMeter->meterId, pMeter->gid.vgId, pMeter->gid.sid);
+ pMeter->gid.vgId = 0;
+ sdbDeleteRow(meterSdb, pMeter);
+ pNode = pLastNode;
continue;
}
if (mgmtIsNormalMeter(pMeter)) {
pVgroup = mgmtGetVgroup(pMeter->gid.vgId);
- if (pVgroup == NULL || pVgroup->meterList == NULL) {
- mError("failed to get vgroup:%i", pMeter->gid.vgId);
+
+ if (pVgroup == NULL) {
+ mError("meter:%s, failed to get vgroup:%d sid:%d, discard it", pMeter->meterId, pMeter->gid.vgId, pMeter->gid.sid);
+ pMeter->gid.vgId = 0;
+ sdbDeleteRow(meterSdb, pMeter);
+ pNode = pLastNode;
continue;
}
+
+ if (strcmp(pVgroup->dbName, pDb->name) != 0) {
+ mError("meter:%s, db:%s not match with vgroup:%d db:%s sid:%d, discard it",
+ pMeter->meterId, pDb->name, pMeter->gid.vgId, pVgroup->dbName, pMeter->gid.sid);
+ pMeter->gid.vgId = 0;
+ sdbDeleteRow(meterSdb, pMeter);
+ pNode = pLastNode;
+ continue;
+ }
+
+ if ( pVgroup->meterList == NULL) {
+ mError("meter:%s, vgroup:%d meterlist is null", pMeter->meterId, pMeter->gid.vgId);
+ pMeter->gid.vgId = 0;
+ sdbDeleteRow(meterSdb, pMeter);
+ pNode = pLastNode;
+ continue;
+ }
+
pVgroup->meterList[pMeter->gid.sid] = pMeter;
taosIdPoolMarkStatus(pVgroup->idPool, pMeter->gid.sid, 1);
diff --git a/src/system/detail/src/vnodeFile.c b/src/system/detail/src/vnodeFile.c
index f145f4a35a..1cb28e8399 100644
--- a/src/system/detail/src/vnodeFile.c
+++ b/src/system/detail/src/vnodeFile.c
@@ -1254,6 +1254,7 @@ int vnodeWriteBlockToFile(SMeterObj *pObj, SCompBlock *pCompBlock, SData *data[]
offset += (cdata[i]->len + sizeof(TSCKSUM));
} else {
+ data[i]->len = pObj->schema[i].bytes * points;
fields[i].len = data[i]->len;
taosCalcChecksumAppend(0, (uint8_t *)(data[i]->data), data[i]->len + sizeof(TSCKSUM));
offset += (data[i]->len + sizeof(TSCKSUM));
diff --git a/src/util/src/ttimer.c b/src/util/src/ttimer.c
index e278f5b9f8..e276710afa 100644
--- a/src/util/src/ttimer.c
+++ b/src/util/src/ttimer.c
@@ -254,13 +254,13 @@ static void processExpiredTimer(void* handle, void* arg) {
timer->executedBy = taosGetPthreadId();
uint8_t state = atomic_val_compare_exchange_8(&timer->state, TIMER_STATE_WAITING, TIMER_STATE_EXPIRED);
if (state == TIMER_STATE_WAITING) {
- const char* fmt = "%s timer[id=" PRIuPTR ", fp=%p, param=%p] execution start.";
+ const char* fmt = "%s timer[id=%" PRIuPTR ", fp=%p, param=%p] execution start.";
tmrTrace(fmt, timer->ctrl->label, timer->id, timer->fp, timer->param);
(*timer->fp)(timer->param, (tmr_h)timer->id);
atomic_store_8(&timer->state, TIMER_STATE_STOPPED);
- fmt = "%s timer[id=" PRIuPTR ", fp=%p, param=%p] execution end.";
+ fmt = "%s timer[id=%" PRIuPTR ", fp=%p, param=%p] execution end.";
tmrTrace(fmt, timer->ctrl->label, timer->id, timer->fp, timer->param);
}
removeTimer(timer->id);
@@ -268,7 +268,7 @@ static void processExpiredTimer(void* handle, void* arg) {
}
static void addToExpired(tmr_obj_t* head) {
- const char* fmt = "%s adding expired timer[id=" PRIuPTR ", fp=%p, param=%p] to queue.";
+ const char* fmt = "%s adding expired timer[id=%" PRIuPTR ", fp=%p, param=%p] to queue.";
while (head != NULL) {
uintptr_t id = head->id;
@@ -282,7 +282,7 @@ static void addToExpired(tmr_obj_t* head) {
schedMsg.thandle = NULL;
taosScheduleTask(tmrQhandle, &schedMsg);
- tmrTrace("timer[id=" PRIuPTR "] has been added to queue.", id);
+ tmrTrace("timer[id=%" PRIuPTR "] has been added to queue.", id);
head = next;
}
}
@@ -296,7 +296,7 @@ static uintptr_t doStartTimer(tmr_obj_t* timer, TAOS_TMR_CALLBACK fp, int msecon
timer->ctrl = ctrl;
addTimer(timer);
- const char* fmt = "%s timer[id=" PRIuPTR ", fp=%p, param=%p] started";
+ const char* fmt = "%s timer[id=%" PRIuPTR ", fp=%p, param=%p] started";
tmrTrace(fmt, ctrl->label, timer->id, timer->fp, timer->param);
if (mseconds == 0) {
@@ -389,7 +389,7 @@ static bool doStopTimer(tmr_obj_t* timer, uint8_t state) {
// we cannot guarantee the thread safety of the timr in all other cases.
reusable = true;
}
- const char* fmt = "%s timer[id=" PRIuPTR ", fp=%p, param=%p] is cancelled.";
+ const char* fmt = "%s timer[id=%" PRIuPTR ", fp=%p, param=%p] is cancelled.";
tmrTrace(fmt, timer->ctrl->label, timer->id, timer->fp, timer->param);
return reusable;
}
@@ -409,7 +409,7 @@ static bool doStopTimer(tmr_obj_t* timer, uint8_t state) {
// timer callback is executing in another thread, we SHOULD wait it to stop,
// BUT this may result in dead lock if current thread are holding a lock which
// the timer callback need to acquire. so, we HAVE TO return directly.
- const char* fmt = "%s timer[id=" PRIuPTR ", fp=%p, param=%p] is executing and cannot be stopped.";
+ const char* fmt = "%s timer[id=%" PRIuPTR ", fp=%p, param=%p] is executing and cannot be stopped.";
tmrTrace(fmt, timer->ctrl->label, timer->id, timer->fp, timer->param);
return false;
}
@@ -419,7 +419,7 @@ bool taosTmrStop(tmr_h timerId) {
tmr_obj_t* timer = findTimer(id);
if (timer == NULL) {
- tmrTrace("timer[id=" PRIuPTR "] does not exist", id);
+ tmrTrace("timer[id=%" PRIuPTR "] does not exist", id);
return false;
}
@@ -446,7 +446,7 @@ bool taosTmrReset(TAOS_TMR_CALLBACK fp, int mseconds, void* param, void* handle,
bool stopped = false;
tmr_obj_t* timer = findTimer(id);
if (timer == NULL) {
- tmrTrace("%s timer[id=" PRIuPTR "] does not exist", ctrl->label, id);
+ tmrTrace("%s timer[id=%" PRIuPTR "] does not exist", ctrl->label, id);
} else {
uint8_t state = atomic_val_compare_exchange_8(&timer->state, TIMER_STATE_WAITING, TIMER_STATE_CANCELED);
if (!doStopTimer(timer, state)) {
@@ -461,7 +461,7 @@ bool taosTmrReset(TAOS_TMR_CALLBACK fp, int mseconds, void* param, void* handle,
return stopped;
}
- tmrTrace("%s timer[id=" PRIuPTR "] is reused", ctrl->label, timer->id);
+ tmrTrace("%s timer[id=%" PRIuPTR "] is reused", ctrl->label, timer->id);
// wait until there's no other reference to this timer,
// so that we can reuse this timer safely.
diff --git a/src/util/src/ttokenizer.c b/src/util/src/ttokenizer.c
index af8174456c..f402963049 100644
--- a/src/util/src/ttokenizer.c
+++ b/src/util/src/ttokenizer.c
@@ -418,7 +418,12 @@ uint32_t tSQLGetToken(char* z, uint32_t* tokenType) {
int delim = z[0];
bool strEnd = false;
for (i = 1; z[i]; i++) {
- if (z[i] == delim) {
+ if (z[i] == '\\') {
+ i++;
+ continue;
+ }
+
+ if (z[i] == delim ) {
if (z[i + 1] == delim) {
i++;
} else {
@@ -427,6 +432,7 @@ uint32_t tSQLGetToken(char* z, uint32_t* tokenType) {
}
}
}
+
if (z[i]) i++;
if (strEnd) {
diff --git a/tests/examples/JDBC/readme.md b/tests/examples/JDBC/JDBCDemo/readme.md
similarity index 100%
rename from tests/examples/JDBC/readme.md
rename to tests/examples/JDBC/JDBCDemo/readme.md
diff --git a/tests/examples/JDBC/SpringJdbcTemplate/.gitignore b/tests/examples/JDBC/SpringJdbcTemplate/.gitignore
new file mode 100644
index 0000000000..175de5c653
--- /dev/null
+++ b/tests/examples/JDBC/SpringJdbcTemplate/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+.mvn/
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/tests/examples/JDBC/SpringJdbcTemplate/pom.xml b/tests/examples/JDBC/SpringJdbcTemplate/pom.xml
new file mode 100644
index 0000000000..45abc5354a
--- /dev/null
+++ b/tests/examples/JDBC/SpringJdbcTemplate/pom.xml
@@ -0,0 +1,85 @@
+
+
+
+ 4.0.0
+
+ com.taosdata.jdbc
+ SpringJdbcTemplate
+ 1.0-SNAPSHOT
+
+ SpringJdbcTemplate
+ http://www.taosdata.com
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+
+
+ org.springframework
+ spring-context
+ 4.3.2.RELEASE
+
+
+
+ org.springframework
+ spring-jdbc
+ 4.3.2.RELEASE
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+ com.taosdata.jdbc
+ taos-jdbcdriver
+ 1.0.3
+
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.1.0
+
+
+
+ com.taosdata.jdbc.App
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
diff --git a/tests/examples/JDBC/SpringJdbcTemplate/readme.md b/tests/examples/JDBC/SpringJdbcTemplate/readme.md
new file mode 100644
index 0000000000..1fe8809b50
--- /dev/null
+++ b/tests/examples/JDBC/SpringJdbcTemplate/readme.md
@@ -0,0 +1,34 @@
+
+## TDengine Spring JDBC Template Demo
+
+`Spring JDBC Template` 简化了原生 JDBC Connection 获取释放等操作,使得操作数据库更加方便。
+
+### 配置
+
+修改 `src/main/resources/applicationContext.xml` 文件中 TDengine 的配置信息:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### 打包运行
+
+进入 `TDengine/tests/examples/JDBC/SpringJdbcTemplate` 目录下,执行以下命令可以生成可执行 jar 包。
+```shell
+mvn clean package
+```
+打包成功之后,进入 `target/` 目录下,执行以下命令就可运行测试:
+```shell
+java -jar SpringJdbcTemplate-1.0-SNAPSHOT-jar-with-dependencies.jar
+```
\ No newline at end of file
diff --git a/tests/examples/JDBC/SpringJdbcTemplate/src/main/java/com/taosdata/jdbc/App.java b/tests/examples/JDBC/SpringJdbcTemplate/src/main/java/com/taosdata/jdbc/App.java
new file mode 100644
index 0000000000..3230af46a8
--- /dev/null
+++ b/tests/examples/JDBC/SpringJdbcTemplate/src/main/java/com/taosdata/jdbc/App.java
@@ -0,0 +1,44 @@
+package com.taosdata.jdbc;
+
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+import java.util.Map;
+
+public class App {
+
+ public static void main( String[] args ) {
+
+ ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
+
+ JdbcTemplate jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
+
+ // create database
+ jdbcTemplate.execute("create database if not exists db ");
+
+ // create table
+ jdbcTemplate.execute("create table if not exists db.tb (ts timestamp, temperature int, humidity float)");
+
+ String insertSql = "insert into db.tb values(now, 23, 10.3) (now + 1s, 20, 9.3)";
+
+ // insert rows
+ int affectedRows = jdbcTemplate.update(insertSql);
+
+ System.out.println("insert success " + affectedRows + " rows.");
+
+ // query for list
+ List