Merge branch 'develop' into test/testcase
This commit is contained in:
commit
885012b1a0
|
@ -171,6 +171,8 @@ matrix:
|
|||
- build-essential
|
||||
- cmake
|
||||
- binutils-2.26
|
||||
- unixodbc
|
||||
- unixodbc-dev
|
||||
env:
|
||||
- DESC="trusty/gcc-4.8/bintuils-2.26 build"
|
||||
|
||||
|
@ -198,6 +200,8 @@ matrix:
|
|||
packages:
|
||||
- build-essential
|
||||
- cmake
|
||||
- unixodbc
|
||||
- unixodbc-dev
|
||||
|
||||
before_script:
|
||||
- export TZ=Asia/Harbin
|
||||
|
@ -252,6 +256,8 @@ matrix:
|
|||
packages:
|
||||
- build-essential
|
||||
- cmake
|
||||
- unixodbc
|
||||
- unixodbc-dev
|
||||
env:
|
||||
- DESC="arm64 xenial build"
|
||||
|
||||
|
@ -280,6 +286,7 @@ matrix:
|
|||
addons:
|
||||
homebrew:
|
||||
- cmake
|
||||
- unixodbc
|
||||
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
|
|
|
@ -17,6 +17,7 @@ SET(TD_MQTT FALSE)
|
|||
SET(TD_TSDB_PLUGINS FALSE)
|
||||
SET(TD_STORAGE FALSE)
|
||||
SET(TD_TOPIC FALSE)
|
||||
SET(TD_MODULE FALSE)
|
||||
|
||||
SET(TD_COVER FALSE)
|
||||
SET(TD_MEM_CHECK FALSE)
|
||||
|
|
|
@ -261,7 +261,13 @@ TDengine 的测试框架和所有测试例全部开源。
|
|||
点击 [这里](tests/How-To-Run-Test-And-How-To-Add-New-Test-Case.md),了解如何运行测试例和添加新的测试例。
|
||||
|
||||
# 成为社区贡献者
|
||||
|
||||
点击 [这里](https://www.taosdata.com/cn/contributor/),了解如何成为 TDengine 的贡献者。
|
||||
|
||||
# 加入技术交流群
|
||||
|
||||
TDengine 官方社群「物联网大数据群」对外开放,欢迎您加入讨论。搜索微信号 "tdengine",加小T为好友,即可入群。
|
||||
|
||||
# [谁在使用TDengine](https://github.com/taosdata/TDengine/issues/2432)
|
||||
|
||||
欢迎所有 TDengine 用户及贡献者在 [这里](https://github.com/taosdata/TDengine/issues/2432) 分享您在当前工作中开发/使用 TDengine 的故事。
|
||||
|
|
|
@ -31,7 +31,7 @@ For user manual, system design and architecture, engineering blogs, refer to [TD
|
|||
# Building
|
||||
At the moment, TDengine only supports building and running on Linux systems. You can choose to [install from packages](https://www.taosdata.com/en/getting-started/#Install-from-Package) or from the source code. This quick guide is for installation from the source only.
|
||||
|
||||
To build TDengine, use [CMake](https://cmake.org/) 3.5 or higher versions in the project directory.
|
||||
To build TDengine, use [CMake](https://cmake.org/) 2.8.12.x or higher versions in the project directory.
|
||||
|
||||
## Install tools
|
||||
|
||||
|
@ -250,3 +250,6 @@ Please follow the [contribution guidelines](CONTRIBUTING.md) to contribute to th
|
|||
|
||||
Add WeChat “tdengine” to join the group,you can communicate with other users.
|
||||
|
||||
# [User List](https://github.com/taosdata/TDengine/issues/2432)
|
||||
|
||||
If you are using TDengine and feel it helps or you'd like to do some contributions, please add your company to [user list](https://github.com/taosdata/TDengine/issues/2432) and let us know your needs.
|
||||
|
|
|
@ -29,6 +29,10 @@ IF (TD_TOPIC)
|
|||
ADD_DEFINITIONS(-D_TOPIC)
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_MODULE)
|
||||
ADD_DEFINITIONS(-D_MODULE)
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_GODLL)
|
||||
ADD_DEFINITIONS(-D_TD_GO_DLL_)
|
||||
ENDIF ()
|
||||
|
|
|
@ -17,6 +17,14 @@ ELSEIF (${TOPIC} MATCHES "false")
|
|||
MESSAGE(STATUS "Build without topic plugins")
|
||||
ENDIF ()
|
||||
|
||||
IF (${TD_MODULE} MATCHES "true")
|
||||
SET(TD_MODULE TRUE)
|
||||
MESSAGE(STATUS "Build with module plugins")
|
||||
ELSEIF (${TOPIC} MATCHES "false")
|
||||
SET(TD_MODULE FALSE)
|
||||
MESSAGE(STATUS "Build without module plugins")
|
||||
ENDIF ()
|
||||
|
||||
IF (${COVER} MATCHES "true")
|
||||
SET(TD_COVER TRUE)
|
||||
MESSAGE(STATUS "Build with test coverage")
|
||||
|
|
|
@ -32,7 +32,7 @@ ELSEIF (TD_WINDOWS)
|
|||
#INSTALL(TARGETS taos RUNTIME DESTINATION driver)
|
||||
#INSTALL(TARGETS shell RUNTIME DESTINATION .)
|
||||
IF (TD_MVN_INSTALLED)
|
||||
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos-jdbcdriver-2.0.22-dist.jar DESTINATION connector/jdbc)
|
||||
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos-jdbcdriver-2.0.25-dist.jar DESTINATION connector/jdbc)
|
||||
ENDIF ()
|
||||
ELSEIF (TD_DARWIN)
|
||||
SET(TD_MAKE_INSTALL_SH "${TD_COMMUNITY_DIR}/packaging/tools/make_install.sh")
|
||||
|
|
|
@ -4,7 +4,7 @@ PROJECT(TDengine)
|
|||
IF (DEFINED VERNUMBER)
|
||||
SET(TD_VER_NUMBER ${VERNUMBER})
|
||||
ELSE ()
|
||||
SET(TD_VER_NUMBER "2.0.17.0")
|
||||
SET(TD_VER_NUMBER "2.0.19.0")
|
||||
ENDIF ()
|
||||
|
||||
IF (DEFINED VERCOMPATIBLE)
|
||||
|
|
|
@ -120,7 +120,7 @@ TDengine是一个高效的存储、查询、分析时序大数据的平台,专
|
|||
* [TDengine性能对比测试工具](https://www.taosdata.com/blog/2020/01/18/1166.html)
|
||||
* [IDEA数据库管理工具可视化使用TDengine](https://www.taosdata.com/blog/2020/08/27/1767.html)
|
||||
* [基于eletron开发的跨平台TDengine图形化管理工具](https://github.com/skye0207/TDengineGUI)
|
||||
* [DataX,支持TDengine的离线数据采集/同步工具](https://github.com/alibaba/DataX)
|
||||
* [DataX,支持TDengine的离线数据采集/同步工具](https://github.com/wgzhao/DataX)(文档:[读取插件](https://github.com/wgzhao/DataX/blob/master/docs/src/main/sphinx/reader/tdenginereader.md)、[写入插件](https://github.com/wgzhao/DataX/blob/master/docs/src/main/sphinx/writer/tdenginewriter.md))
|
||||
|
||||
## TDengine与其他数据库的对比测试
|
||||
|
||||
|
|
|
@ -109,10 +109,10 @@ taos> source <filename>;
|
|||
|
||||
### Shell小技巧
|
||||
|
||||
- 可以使用上下光标键查看已经历史输入的命令
|
||||
- 修改用户密码。在shell中使用alter user命令
|
||||
- 可以使用上下光标键查看历史输入的指令
|
||||
- 修改用户密码。在 shell 中使用 alter user 指令
|
||||
- ctrl+c 中止正在进行中的查询
|
||||
- 执行`RESET QUERY CACHE`清空本地缓存的表的schema
|
||||
- 执行 `RESET QUERY CACHE` 清空本地缓存的表 schema
|
||||
|
||||
|
||||
## <a class="anchor" id="demo"></a>TDengine 极速体验
|
||||
|
@ -179,19 +179,20 @@ taos> select avg(f1), max(f2), min(f3) from test.t10 interval(10s);
|
|||
|
||||
### TDengine服务器支持的平台列表
|
||||
|
||||
| | **CentOS** **6/7/8** | **Ubuntu** **16/18/20** | **Other Linux** | **统信****UOS** | **银河****/****中标麒麟** | **凝思** **V60/V80** |
|
||||
| -------------- | --------------------- | ------------------------ | --------------- | --------------- | ------------------------- | --------------------- |
|
||||
| X64 | ● | ● | | ○ | ● | ● |
|
||||
| 树莓派ARM32 | | ● | ● | | | |
|
||||
| 龙芯MIPS64 | | | ● | | | |
|
||||
| 鲲鹏 ARM64 | | ○ | ○ | | ● | |
|
||||
| 申威 Alpha64 | | | ○ | ● | | |
|
||||
| 飞腾ARM64 | | ○优麒麟 | | | | |
|
||||
| 海光X64 | ● | ● | ● | ○ | ● | ● |
|
||||
| 瑞芯微ARM64/32 | | | ○ | | | |
|
||||
| 全志ARM64/32 | | | ○ | | | |
|
||||
| 炬力ARM64/32 | | | ○ | | | |
|
||||
| TI ARM32 | | | ○ | | | |
|
||||
| | **CentOS 6/7/8** | **Ubuntu 16/18/20** | **Other Linux** | **统信 UOS** | **银河/中标麒麟** | **凝思 V60/V80** | **华为 EulerOS** |
|
||||
| -------------- | --------------------- | ------------------------ | --------------- | --------------- | ------------------------- | --------------------- | --------------------- |
|
||||
| X64 | ● | ● | | ○ | ● | ● | ● |
|
||||
| 树莓派 ARM32 | | ● | ● | | | | |
|
||||
| 龙芯 MIPS64 | | | ● | | | | |
|
||||
| 鲲鹏 ARM64 | | ○ | ○ | | ● | | |
|
||||
| 申威 Alpha64 | | | ○ | ● | | | |
|
||||
| 飞腾 ARM64 | | ○ 优麒麟 | | | | | |
|
||||
| 海光 X64 | ● | ● | ● | ○ | ● | ● | |
|
||||
| 瑞芯微 ARM64/32 | | | ○ | | | | |
|
||||
| 全志 ARM64/32 | | | ○ | | | | |
|
||||
| 炬力 ARM64/32 | | | ○ | | | | |
|
||||
| TI ARM32 | | | ○ | | | | |
|
||||
| 华为云 ARM64 | | | | | | | ● |
|
||||
|
||||
注: ● 表示经过官方测试验证, ○ 表示非官方测试验证。
|
||||
|
||||
|
@ -203,7 +204,7 @@ taos> select avg(f1), max(f2), min(f3) from test.t10 interval(10s);
|
|||
|
||||
对照矩阵如下:
|
||||
|
||||
| **CPU** | **X64 64bit** | | | **X86 32bit** | **ARM64** | **ARM32** | **MIPS ** **龙芯** | **Alpha ** **申威** | **X64 ** **海光** |
|
||||
| **CPU** | **X64 64bit** | | | **X86 32bit** | **ARM64** | **ARM32** | **MIPS 龙芯** | **Alpha 申威** | **X64 海光** |
|
||||
| ----------- | --------------- | --------- | --------- | --------------- | --------- | --------- | ------------------- | -------------------- | ------------------ |
|
||||
| **OS** | **Linux** | **Win64** | **Win32** | **Win32** | **Linux** | **Linux** | **Linux** | **Linux** | **Linux** |
|
||||
| **C/C++** | ● | ● | ● | ○ | ● | ● | ● | ● | ● |
|
||||
|
@ -211,7 +212,7 @@ taos> select avg(f1), max(f2), min(f3) from test.t10 interval(10s);
|
|||
| **Python** | ● | ● | ● | ○ | ● | ● | ● | -- | ● |
|
||||
| **Go** | ● | ● | ● | ○ | ● | ● | ○ | -- | -- |
|
||||
| **NodeJs** | ● | ● | ○ | ○ | ● | ● | ○ | -- | -- |
|
||||
| **C#** | ○ | ● | ● | ○ | ○ | ○ | ○ | -- | -- |
|
||||
| **C#** | ● | ● | ○ | ○ | ○ | ○ | ○ | -- | -- |
|
||||
| **RESTful** | ● | ● | ● | ● | ● | ● | ● | ● | ● |
|
||||
|
||||
注: ● 表示经过官方测试验证, ○ 表示非官方测试验证。
|
||||
|
|
|
@ -1,27 +1,16 @@
|
|||
# 高级功能
|
||||
|
||||
## <a class="anchor" id="continuous-query"></a>连续查询(Continuous Query)
|
||||
## <a class="anchor" id="continuous-query"></a>连续查询(Continuous Query)
|
||||
|
||||
连续查询是TDengine定期自动执行的查询,采用滑动窗口的方式进行计算,是一种简化的时间驱动的流式计算。
|
||||
针对库中的表或超级表,TDengine可提供定期自动执行的连续查询,
|
||||
用户可让TDengine推送查询的结果,也可以将结果再写回到TDengine中。
|
||||
每次执行的查询是一个时间窗口,时间窗口随着时间流动向前滑动。
|
||||
在定义连续查询的时候需要指定时间窗口(time window, 参数interval)大小和每次前向增量时间(forward sliding times, 参数sliding)。
|
||||
连续查询是TDengine定期自动执行的查询,采用滑动窗口的方式进行计算,是一种简化的时间驱动的流式计算。针对库中的表或超级表,TDengine可提供定期自动执行的连续查询,用户可让TDengine推送查询的结果,也可以将结果再写回到TDengine中。每次执行的查询是一个时间窗口,时间窗口随着时间流动向前滑动。在定义连续查询的时候需要指定时间窗口(time window, 参数interval)大小和每次前向增量时间(forward sliding times, 参数sliding)。
|
||||
|
||||
TDengine的连续查询采用时间驱动模式,可以直接使用TAOS SQL进行定义,不需要额外的操作。
|
||||
使用连续查询,可以方便快捷地按照时间窗口生成结果,从而对原始采集数据进行降采样(down sampling)。
|
||||
用户通过TAOS SQL定义连续查询以后,TDengine自动在最后的一个完整的时间周期末端拉起查询,
|
||||
并将计算获得的结果推送给用户或者写回TDengine。
|
||||
TDengine的连续查询采用时间驱动模式,可以直接使用TAOS SQL进行定义,不需要额外的操作。使用连续查询,可以方便快捷地按照时间窗口生成结果,从而对原始采集数据进行降采样(down sampling)。用户通过TAOS SQL定义连续查询以后,TDengine自动在最后的一个完整的时间周期末端拉起查询,并将计算获得的结果推送给用户或者写回TDengine。
|
||||
|
||||
TDengine提供的连续查询与普通流计算中的时间窗口计算具有以下区别:
|
||||
|
||||
- 不同于流计算的实时反馈计算结果,连续查询只在时间窗口关闭以后才开始计算。
|
||||
例如时间周期是1天,那么当天的结果只会在23:59:59以后才会生成。
|
||||
- 如果有历史记录写入到已经计算完成的时间区间,连续查询并不会重新进行计算,
|
||||
也不会重新将结果推送给用户。对于写回TDengine的模式,也不会更新已经存在的计算结果。
|
||||
- 使用连续查询推送结果的模式,服务端并不缓存客户端计算状态,也不提供Exactly-Once的语意保证。
|
||||
如果用户的应用端崩溃,再次拉起的连续查询将只会从再次拉起的时间开始重新计算最近的一个完整的时间窗口。
|
||||
如果使用写回模式,TDengine可确保数据写回的有效性和连续性。
|
||||
- 不同于流计算的实时反馈计算结果,连续查询只在时间窗口关闭以后才开始计算。例如时间周期是1天,那么当天的结果只会在23:59:59以后才会生成。
|
||||
- 如果有历史记录写入到已经计算完成的时间区间,连续查询并不会重新进行计算,也不会重新将结果推送给用户。对于写回TDengine的模式,也不会更新已经存在的计算结果。
|
||||
- 使用连续查询推送结果的模式,服务端并不缓存客户端计算状态,也不提供Exactly-Once的语意保证。如果用户的应用端崩溃,再次拉起的连续查询将只会从再次拉起的时间开始重新计算最近的一个完整的时间窗口。如果使用写回模式,TDengine可确保数据写回的有效性和连续性。
|
||||
|
||||
### 使用连续查询
|
||||
|
||||
|
@ -40,23 +29,19 @@ create table D1002 using meters tags ("Beijing.Haidian", 2);
|
|||
select avg(voltage) from meters interval(1m) sliding(30s);
|
||||
```
|
||||
|
||||
每次执行这条语句,都会重新计算所有数据。
|
||||
如果需要每隔30秒执行一次来增量计算最近一分钟的数据,
|
||||
可以把上面的语句改进成下面的样子,每次使用不同的 `startTime` 并定期执行:
|
||||
每次执行这条语句,都会重新计算所有数据。 如果需要每隔30秒执行一次来增量计算最近一分钟的数据,可以把上面的语句改进成下面的样子,每次使用不同的 `startTime` 并定期执行:
|
||||
|
||||
```sql
|
||||
select avg(voltage) from meters where ts > {startTime} interval(1m) sliding(30s);
|
||||
```
|
||||
|
||||
这样做没有问题,但TDengine提供了更简单的方法,
|
||||
只要在最初的查询语句前面加上 `create table {tableName} as ` 就可以了, 例如:
|
||||
这样做没有问题,但TDengine提供了更简单的方法,只要在最初的查询语句前面加上 `create table {tableName} as ` 就可以了, 例如:
|
||||
|
||||
```sql
|
||||
create table avg_vol as select avg(voltage) from meters interval(1m) sliding(30s);
|
||||
```
|
||||
|
||||
会自动创建一个名为 `avg_vol` 的新表,然后每隔30秒,TDengine会增量执行 `as` 后面的 SQL 语句,
|
||||
并将查询结果写入这个表中,用户程序后续只要从 `avg_vol` 中查询数据即可。 例如:
|
||||
会自动创建一个名为 `avg_vol` 的新表,然后每隔30秒,TDengine会增量执行 `as` 后面的 SQL 语句,并将查询结果写入这个表中,用户程序后续只要从 `avg_vol` 中查询数据即可。 例如:
|
||||
|
||||
```mysql
|
||||
taos> select * from avg_vol;
|
||||
|
@ -70,43 +55,27 @@ taos> select * from avg_vol;
|
|||
|
||||
需要注意,查询时间窗口的最小值是10毫秒,没有时间窗口范围的上限。
|
||||
|
||||
此外,TDengine还支持用户指定连续查询的起止时间。
|
||||
如果不输入开始时间,连续查询将从第一条原始数据所在的时间窗口开始;
|
||||
如果没有输入结束时间,连续查询将永久运行;
|
||||
如果用户指定了结束时间,连续查询在系统时间达到指定的时间以后停止运行。
|
||||
比如使用下面的SQL创建的连续查询将运行一小时,之后会自动停止。
|
||||
此外,TDengine还支持用户指定连续查询的起止时间。如果不输入开始时间,连续查询将从第一条原始数据所在的时间窗口开始;如果没有输入结束时间,连续查询将永久运行;如果用户指定了结束时间,连续查询在系统时间达到指定的时间以后停止运行。比如使用下面的SQL创建的连续查询将运行一小时,之后会自动停止。
|
||||
|
||||
```mysql
|
||||
create table avg_vol as select avg(voltage) from meters where ts > now and ts <= now + 1h interval(1m) sliding(30s);
|
||||
```
|
||||
|
||||
需要说明的是,上面例子中的 `now` 是指创建连续查询的时间,而不是查询执行的时间,否则,查询就无法自动停止了。
|
||||
另外,为了尽量避免原始数据延迟写入导致的问题,TDengine中连续查询的计算有一定的延迟。
|
||||
也就是说,一个时间窗口过去后,TDengine并不会立即计算这个窗口的数据,
|
||||
所以要稍等一会(一般不会超过1分钟)才能查到计算结果。
|
||||
需要说明的是,上面例子中的 `now` 是指创建连续查询的时间,而不是查询执行的时间,否则,查询就无法自动停止了。另外,为了尽量避免原始数据延迟写入导致的问题,TDengine中连续查询的计算有一定的延迟。也就是说,一个时间窗口过去后,TDengine并不会立即计算这个窗口的数据,所以要稍等一会(一般不会超过1分钟)才能查到计算结果。
|
||||
|
||||
|
||||
### 管理连续查询
|
||||
|
||||
用户可在控制台中通过 `show streams` 命令来查看系统中全部运行的连续查询,
|
||||
并可以通过 `kill stream` 命令杀掉对应的连续查询。
|
||||
后续版本会提供更细粒度和便捷的连续查询管理命令。
|
||||
用户可在控制台中通过 `show streams` 命令来查看系统中全部运行的连续查询,并可以通过 `kill stream` 命令杀掉对应的连续查询。后续版本会提供更细粒度和便捷的连续查询管理命令。
|
||||
|
||||
|
||||
## <a class="anchor" id="subscribe"></a>数据订阅(Publisher/Subscriber)
|
||||
## <a class="anchor" id="subscribe"></a>数据订阅(Publisher/Subscriber)
|
||||
|
||||
基于数据天然的时间序列特性,TDengine的数据写入(insert)与消息系统的数据发布(pub)逻辑上一致,
|
||||
均可视为系统中插入一条带时间戳的新记录。
|
||||
同时,TDengine在内部严格按照数据时间序列单调递增的方式保存数据。
|
||||
本质上来说,TDengine中里每一张表均可视为一个标准的消息队列。
|
||||
基于数据天然的时间序列特性,TDengine的数据写入(insert)与消息系统的数据发布(pub)逻辑上一致,均可视为系统中插入一条带时间戳的新记录。同时,TDengine在内部严格按照数据时间序列单调递增的方式保存数据。本质上来说,TDengine中里每一张表均可视为一个标准的消息队列。
|
||||
|
||||
TDengine内嵌支持轻量级的消息订阅与推送服务。
|
||||
使用系统提供的API,用户可使用普通查询语句订阅数据库中的一张或多张表。
|
||||
订阅的逻辑和操作状态的维护均是由客户端完成,客户端定时轮询服务器是否有新的记录到达,
|
||||
有新的记录到达就会将结果反馈到客户。
|
||||
TDengine内嵌支持轻量级的消息订阅与推送服务。使用系统提供的API,用户可使用普通查询语句订阅数据库中的一张或多张表。订阅的逻辑和操作状态的维护均是由客户端完成,客户端定时轮询服务器是否有新的记录到达,有新的记录到达就会将结果反馈到客户。
|
||||
|
||||
TDengine的订阅与推送服务的状态是客户端维持,TDengine服务器并不维持。
|
||||
因此如果应用重启,从哪个时间点开始获取最新数据,由应用决定。
|
||||
TDengine的订阅与推送服务的状态是客户端维持,TDengine服务器并不维持。因此如果应用重启,从哪个时间点开始获取最新数据,由应用决定。
|
||||
|
||||
TDengine的API中,与订阅相关的主要有以下三个:
|
||||
|
||||
|
@ -116,12 +85,9 @@ taos_consume
|
|||
taos_unsubscribe
|
||||
```
|
||||
|
||||
这些API的文档请见 [C/C++ Connector](https://www.taosdata.com/cn/documentation/connector/),
|
||||
下面仍以智能电表场景为例介绍一下它们的具体用法(超级表和子表结构请参考上一节“连续查询”),
|
||||
完整的示例代码可以在 [这里](https://github.com/taosdata/TDengine/blob/master/tests/examples/c/subscribe.c) 找到。
|
||||
这些API的文档请见 [C/C++ Connector](https://www.taosdata.com/cn/documentation/connector#c-cpp),下面仍以智能电表场景为例介绍一下它们的具体用法(超级表和子表结构请参考上一节“连续查询”),完整的示例代码可以在 [这里](https://github.com/taosdata/TDengine/blob/master/tests/examples/c/subscribe.c) 找到。
|
||||
|
||||
如果我们希望当某个电表的电流超过一定限制(比如10A)后能得到通知并进行一些处理, 有两种方法:
|
||||
一是分别对每张子表进行查询,每次查询后记录最后一条数据的时间戳,后续只查询这个时间戳之后的数据:
|
||||
如果我们希望当某个电表的电流超过一定限制(比如10A)后能得到通知并进行一些处理, 有两种方法:一是分别对每张子表进行查询,每次查询后记录最后一条数据的时间戳,后续只查询这个时间戳之后的数据:
|
||||
|
||||
```sql
|
||||
select * from D1001 where ts > {last_timestamp1} and current > 10;
|
||||
|
@ -129,8 +95,7 @@ select * from D1002 where ts > {last_timestamp2} and current > 10;
|
|||
...
|
||||
```
|
||||
|
||||
这确实可行,但随着电表数量的增加,查询数量也会增加,客户端和服务端的性能都会受到影响,
|
||||
当电表数增长到一定的程度,系统就无法承受了。
|
||||
这确实可行,但随着电表数量的增加,查询数量也会增加,客户端和服务端的性能都会受到影响,当电表数增长到一定的程度,系统就无法承受了。
|
||||
|
||||
另一种方法是对超级表进行查询。这样,无论有多少电表,都只需一次查询:
|
||||
|
||||
|
@ -138,12 +103,7 @@ select * from D1002 where ts > {last_timestamp2} and current > 10;
|
|||
select * from meters where ts > {last_timestamp} and current > 10;
|
||||
```
|
||||
|
||||
但是,如何选择 `last_timestamp` 就成了一个新的问题。
|
||||
因为,一方面数据的产生时间(也就是数据时间戳)和数据入库的时间一般并不相同,有时偏差还很大;
|
||||
另一方面,不同电表的数据到达TDengine的时间也会有差异。
|
||||
所以,如果我们在查询中使用最慢的那台电表的数据的时间戳作为 `last_timestamp`,
|
||||
就可能重复读入其它电表的数据;
|
||||
如果使用最快的电表的时间戳,其它电表的数据就可能被漏掉。
|
||||
但是,如何选择 `last_timestamp` 就成了一个新的问题。因为,一方面数据的产生时间(也就是数据时间戳)和数据入库的时间一般并不相同,有时偏差还很大;另一方面,不同电表的数据到达TDengine的时间也会有差异。所以,如果我们在查询中使用最慢的那台电表的数据的时间戳作为 `last_timestamp`,就可能重复读入其它电表的数据;如果使用最快的电表的时间戳,其它电表的数据就可能被漏掉。
|
||||
|
||||
TDengine的订阅功能为上面这个问题提供了一个彻底的解决方案。
|
||||
|
||||
|
@ -160,47 +120,29 @@ if (async) {
|
|||
}
|
||||
```
|
||||
|
||||
TDengine中的订阅既可以是同步的,也可以是异步的,
|
||||
上面的代码会根据从命令行获取的参数`async`的值来决定使用哪种方式。
|
||||
这里,同步的意思是用户程序要直接调用`taos_consume`来拉取数据,
|
||||
而异步则由API在内部的另一个线程中调用`taos_consume`,
|
||||
然后把拉取到的数据交给回调函数`subscribe_callback`去处理。
|
||||
TDengine中的订阅既可以是同步的,也可以是异步的,上面的代码会根据从命令行获取的参数`async`的值来决定使用哪种方式。这里,同步的意思是用户程序要直接调用`taos_consume`来拉取数据,而异步则由API在内部的另一个线程中调用`taos_consume`,然后把拉取到的数据交给回调函数`subscribe_callback`去处理。
|
||||
|
||||
参数`taos`是一个已经建立好的数据库连接,在同步模式下无特殊要求。
|
||||
但在异步模式下,需要注意它不会被其它线程使用,否则可能导致不可预计的错误,
|
||||
因为回调函数在API的内部线程中被调用,而TDengine的部分API不是线程安全的。
|
||||
参数`taos`是一个已经建立好的数据库连接,在同步模式下无特殊要求。但在异步模式下,需要注意它不会被其它线程使用,否则可能导致不可预计的错误,因为回调函数在API的内部线程中被调用,而TDengine的部分API不是线程安全的。
|
||||
|
||||
参数`sql`是查询语句,可以在其中使用where子句指定过滤条件。
|
||||
在我们的例子中,如果只想订阅电流超过10A时的数据,可以这样写:
|
||||
参数`sql`是查询语句,可以在其中使用where子句指定过滤条件。在我们的例子中,如果只想订阅电流超过10A时的数据,可以这样写:
|
||||
|
||||
```sql
|
||||
select * from meters where current > 10;
|
||||
```
|
||||
|
||||
注意,这里没有指定起始时间,所以会读到所有时间的数据。
|
||||
如果只想从一天前的数据开始订阅,而不需要更早的历史数据,可以再加上一个时间条件:
|
||||
注意,这里没有指定起始时间,所以会读到所有时间的数据。如果只想从一天前的数据开始订阅,而不需要更早的历史数据,可以再加上一个时间条件:
|
||||
|
||||
```sql
|
||||
select * from meters where ts > now - 1d and current > 10;
|
||||
```
|
||||
|
||||
订阅的`topic`实际上是它的名字,因为订阅功能是在客户端API中实现的,
|
||||
所以没必要保证它全局唯一,但需要它在一台客户端机器上唯一。
|
||||
订阅的`topic`实际上是它的名字,因为订阅功能是在客户端API中实现的,所以没必要保证它全局唯一,但需要它在一台客户端机器上唯一。
|
||||
|
||||
如果名`topic`的订阅不存在,参数`restart`没有意义;
|
||||
但如果用户程序创建这个订阅后退出,当它再次启动并重新使用这个`topic`时,
|
||||
`restart`就会被用于决定是从头开始读取数据,还是接续上次的位置进行读取。
|
||||
本例中,如果`restart`是 **true**(非零值),用户程序肯定会读到所有数据。
|
||||
但如果这个订阅之前就存在了,并且已经读取了一部分数据,
|
||||
且`restart`是 **false**(**0**),用户程序就不会读到之前已经读取的数据了。
|
||||
如果名`topic`的订阅不存在,参数`restart`没有意义;但如果用户程序创建这个订阅后退出,当它再次启动并重新使用这个`topic`时,`restart`就会被用于决定是从头开始读取数据,还是接续上次的位置进行读取。本例中,如果`restart`是 **true**(非零值),用户程序肯定会读到所有数据。但如果这个订阅之前就存在了,并且已经读取了一部分数据,且`restart`是 **false**(**0**),用户程序就不会读到之前已经读取的数据了。
|
||||
|
||||
`taos_subscribe`的最后一个参数是以毫秒为单位的轮询周期。
|
||||
在同步模式下,如果前后两次调用`taos_consume`的时间间隔小于此时间,
|
||||
`taos_consume`会阻塞,直到间隔超过此时间。
|
||||
异步模式下,这个时间是两次调用回调函数的最小时间间隔。
|
||||
`taos_subscribe`的最后一个参数是以毫秒为单位的轮询周期。在同步模式下,如果前后两次调用`taos_consume`的时间间隔小于此时间,`taos_consume`会阻塞,直到间隔超过此时间。异步模式下,这个时间是两次调用回调函数的最小时间间隔。
|
||||
|
||||
`taos_subscribe`的倒数第二个参数用于用户程序向回调函数传递附加参数,
|
||||
订阅API不对其做任何处理,只原样传递给回调函数。此参数在同步模式下无意义。
|
||||
`taos_subscribe`的倒数第二个参数用于用户程序向回调函数传递附加参数,订阅API不对其做任何处理,只原样传递给回调函数。此参数在同步模式下无意义。
|
||||
|
||||
订阅创建以后,就可以消费其数据了,同步模式下,示例代码是下面的 else 部分:
|
||||
|
||||
|
@ -219,9 +161,7 @@ if (async) {
|
|||
}
|
||||
```
|
||||
|
||||
这里是一个 **while** 循环,用户每按一次回车键就调用一次`taos_consume`,
|
||||
而`taos_consume`的返回值是查询到的结果集,与`taos_use_result`完全相同,
|
||||
例子中使用这个结果集的代码是函数`print_result`:
|
||||
这里是一个 **while** 循环,用户每按一次回车键就调用一次`taos_consume`,而`taos_consume`的返回值是查询到的结果集,与`taos_use_result`完全相同,例子中使用这个结果集的代码是函数`print_result`:
|
||||
|
||||
```c
|
||||
void print_result(TAOS_RES* res, int blockFetch) {
|
||||
|
@ -247,8 +187,7 @@ void print_result(TAOS_RES* res, int blockFetch) {
|
|||
}
|
||||
```
|
||||
|
||||
其中的 `taos_print_row` 用于处理订阅到数据,在我们的例子中,它会打印出所有符合条件的记录。
|
||||
而异步模式下,消费订阅到的数据则显得更为简单:
|
||||
其中的 `taos_print_row` 用于处理订阅到数据,在我们的例子中,它会打印出所有符合条件的记录。而异步模式下,消费订阅到的数据则显得更为简单:
|
||||
|
||||
```c
|
||||
void subscribe_callback(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code) {
|
||||
|
@ -262,11 +201,7 @@ void subscribe_callback(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code) {
|
|||
taos_unsubscribe(tsub, keep);
|
||||
```
|
||||
|
||||
其第二个参数,用于决定是否在客户端保留订阅的进度信息。
|
||||
如果这个参数是**false**(**0**),那无论下次调用`taos_subscribe`的时的`restart`参数是什么,
|
||||
订阅都只能重新开始。
|
||||
另外,进度信息的保存位置是 *{DataDir}/subscribe/* 这个目录下,
|
||||
每个订阅有一个与其`topic`同名的文件,删掉某个文件,同样会导致下次创建其对应的订阅时只能重新开始。
|
||||
其第二个参数,用于决定是否在客户端保留订阅的进度信息。如果这个参数是**false**(**0**),那无论下次调用`taos_subscribe`时的`restart`参数是什么,订阅都只能重新开始。另外,进度信息的保存位置是 *{DataDir}/subscribe/* 这个目录下,每个订阅有一个与其`topic`同名的文件,删掉某个文件,同样会导致下次创建其对应的订阅时只能重新开始。
|
||||
|
||||
代码介绍完毕,我们来看一下实际的运行效果。假设:
|
||||
|
||||
|
@ -289,12 +224,11 @@ $ taos
|
|||
> insert into D1001 values(now, 12, 220, 1);
|
||||
```
|
||||
|
||||
这时,因为电流超过了10A,您应该可以看到示例程序将它输出到了屏幕上。
|
||||
您可以继续插入一些数据观察示例程序的输出。
|
||||
这时,因为电流超过了10A,您应该可以看到示例程序将它输出到了屏幕上。您可以继续插入一些数据观察示例程序的输出。
|
||||
|
||||
### Java 使用数据订阅功能
|
||||
|
||||
订阅功能也提供了 Java 开发接口,相关说明请见 [Java Connector](https://www.taosdata.com/cn/documentation/connector/)。需要注意的是,目前 Java 接口没有提供异步订阅模式,但用户程序可以通过创建 `TimerTask` 等方式达到同样的效果。
|
||||
订阅功能也提供了 Java 开发接口,相关说明请见 [Java Connector](https://www.taosdata.com/cn/documentation/connector/java#subscribe)。需要注意的是,目前 Java 接口没有提供异步订阅模式,但用户程序可以通过创建 `TimerTask` 等方式达到同样的效果。
|
||||
|
||||
下面以一个示例程序介绍其具体使用方法。它所完成的功能与前面介绍的 C 语言示例基本相同,也是订阅数据库中所有电流超过 10A 的记录。
|
||||
|
||||
|
@ -404,7 +338,7 @@ ts: 1597466400000 current: 12.4 voltage: 220 phase: 1 location: Beijing.Chaoyang
|
|||
```
|
||||
|
||||
|
||||
## <a class="anchor" id="cache"></a>缓存(Cache)
|
||||
## <a class="anchor" id="cache"></a>缓存(Cache)
|
||||
|
||||
TDengine采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Use,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心最近产生的数据,即当前状态。TDengine充分利用了这一特性,将最近到达的(当前状态)数据保存在缓存中。
|
||||
|
||||
|
@ -423,7 +357,7 @@ select last_row(voltage) from meters where location='Beijing.Chaoyang';
|
|||
该SQL语句将获取所有位于北京朝阳区的电表最后记录的电压值。
|
||||
|
||||
|
||||
## <a class="anchor" id="alert"></a>报警监测(Alert)
|
||||
## <a class="anchor" id="alert"></a>报警监测(Alert)
|
||||
|
||||
在 TDengine 的应用场景中,报警监测是一个常见需求,从概念上说,它要求程序从最近一段时间的数据中筛选出符合一定条件的数据,并基于这些数据根据定义好的公式计算出一个结果,当这个结果符合某个条件且持续一定时间后,以某种形式通知用户。
|
||||
|
||||
|
|
|
@ -285,7 +285,7 @@ JDBC连接器可能报错的错误码包括3种:JDBC driver本身的报错(
|
|||
* https://github.com/taosdata/TDengine/blob/develop/src/connector/jdbc/src/main/java/com/taosdata/jdbc/TSDBErrorNumbers.java
|
||||
* https://github.com/taosdata/TDengine/blob/develop/src/inc/taoserror.h
|
||||
|
||||
### 订阅
|
||||
### <a class="anchor" id="subscribe"></a>订阅
|
||||
|
||||
#### 创建
|
||||
|
||||
|
@ -451,7 +451,8 @@ Query OK, 1 row(s) in set (0.000141s)
|
|||
|
||||
| taos-jdbcdriver 版本 | TDengine 版本 | JDK 版本 |
|
||||
| -------------------- | ----------------- | -------- |
|
||||
| 2.0.12 及以上 | 2.0.8.0 及以上 | 1.8.x |
|
||||
| 2.0.22 | 2.0.18.0 及以上 | 1.8.x |
|
||||
| 2.0.12 - 2.0.21 | 2.0.8.0 - 2.0.17.0 | 1.8.x |
|
||||
| 2.0.4 - 2.0.11 | 2.0.0.0 - 2.0.7.x | 1.8.x |
|
||||
| 1.0.3 | 1.6.1.x 及以上 | 1.8.x |
|
||||
| 1.0.2 | 1.6.1.x 及以上 | 1.8.x |
|
||||
|
@ -470,9 +471,11 @@ TDengine 目前支持时间戳、数字、字符、布尔类型,与 Java 对
|
|||
| BIGINT | java.lang.Long |
|
||||
| FLOAT | java.lang.Float |
|
||||
| DOUBLE | java.lang.Double |
|
||||
| SMALLINT, TINYINT | java.lang.Short |
|
||||
| SMALLINT | java.lang.Short |
|
||||
| TINYINT | java.lang.Byte |
|
||||
| BOOL | java.lang.Boolean |
|
||||
| BINARY, NCHAR | java.lang.String |
|
||||
| BINARY | byte array |
|
||||
| NCHAR | java.lang.String |
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ TDengine提供了丰富的应用程序开发接口,其中包括C/C++、Java、
|
|||
| **Python** | ● | ● | ● | ○ | ● | ● | ○ | -- | ○ |
|
||||
| **Go** | ● | ● | ● | ○ | ● | ● | ○ | -- | -- |
|
||||
| **NodeJs** | ● | ● | ○ | ○ | ● | ● | ○ | -- | -- |
|
||||
| **C#** | ○ | ● | ● | ○ | ○ | ○ | ○ | -- | -- |
|
||||
| **C#** | ● | ● | ○ | ○ | ○ | ○ | ○ | -- | -- |
|
||||
| **RESTful** | ● | ● | ● | ● | ● | ● | ○ | ○ | ○ |
|
||||
|
||||
其中 ● 表示经过官方测试验证, ○ 表示非官方测试验证。
|
||||
|
@ -23,7 +23,7 @@ TDengine提供了丰富的应用程序开发接口,其中包括C/C++、Java、
|
|||
|
||||
* 在没有安装TDengine服务端软件的系统中使用连接器(除RESTful外)访问 TDengine 数据库,需要安装相应版本的客户端安装包来使应用驱动(Linux系统中文件名为libtaos.so,Windows系统中为taos.dll)被安装在系统中,否则会产生无法找到相应库文件的错误。
|
||||
* 所有执行 SQL 语句的 API,例如 C/C++ Connector 中的 `tao_query`、`taos_query_a`、`taos_subscribe` 等,以及其它语言中与它们对应的API,每次都只能执行一条 SQL 语句,如果实际参数中包含了多条语句,它们的行为是未定义的。
|
||||
* 升级到TDengine到2.0.8.0版本的用户,必须更新JDBC连接TDengine必须升级taos-jdbcdriver到2.0.12及以上。
|
||||
* 升级到TDengine到2.0.8.0版本的用户,必须更新JDBC连接TDengine必须升级taos-jdbcdriver到2.0.12及以上。详细的版本依赖关系请参见 [taos-jdbcdriver 文档](https://www.taosdata.com/cn/documentation/connector/java#version)。
|
||||
* 无论选用何种编程语言的连接器,2.0 及以上版本的 TDengine 推荐数据库应用的每个线程都建立一个独立的连接,或基于线程建立连接池,以避免连接内的“USE statement”状态量在线程之间相互干扰(但连接的查询和写入操作都是线程安全的)。
|
||||
|
||||
## <a class="anchor" id="driver"></a>安装连接器驱动步骤
|
||||
|
|
|
@ -113,6 +113,7 @@ taos>
|
|||
|
||||
- 任何已经加入集群在线的数据节点,都可以作为后续待加入节点的 firstEP。
|
||||
- firstEp 这个参数仅仅在该数据节点首次加入集群时有作用,加入集群后,该数据节点会保存最新的 mnode 的 End Point 列表,不再依赖这个参数。
|
||||
- 接下来,配置文件中的 firstEp 参数就主要在客户端连接的时候使用了,例如 taos shell 如果不加参数,会默认连接由 firstEp 指定的节点。
|
||||
- 两个没有配置 firstEp 参数的数据节点 dnode 启动后,会独立运行起来。这个时候,无法将其中一个数据节点加入到另外一个数据节点,形成集群。**无法将两个独立的集群合并成为新的集群**。
|
||||
|
||||
## <a class="anchor" id="management"></a>数据节点管理
|
||||
|
|
|
@ -6,19 +6,27 @@
|
|||
|
||||
### 内存需求
|
||||
|
||||
每个 DB 可以创建固定数目的 vgroup,默认与 CPU 核数相同,可通过 maxVgroupsPerDb 配置;vgroup 中的每个副本会是一个 vnode;每个 vnode 会占用固定大小的内存(大小与数据库的配置参数 blocks 和 cache 有关);每个 Table 会占用与标签总长度有关的内存;此外,系统会有一些固定的内存开销。因此,每个 DB 需要的系统内存可通过如下公式计算:
|
||||
每个 Database 可以创建固定数目的 vgroup,默认与 CPU 核数相同,可通过 maxVgroupsPerDb 配置;vgroup 中的每个副本会是一个 vnode;每个 vnode 会占用固定大小的内存(大小与数据库的配置参数 blocks 和 cache 有关);每个 Table 会占用与标签总长度有关的内存;此外,系统会有一些固定的内存开销。因此,每个 DB 需要的系统内存可通过如下公式计算:
|
||||
|
||||
```
|
||||
Memory Size = maxVgroupsPerDb * (blocks * cache + 10MB) + numOfTables * (tagSizePerTable + 0.5KB)
|
||||
Database Memory Size = maxVgroupsPerDb * (blocks * cache + 10MB) + numOfTables * (tagSizePerTable + 0.5KB)
|
||||
```
|
||||
|
||||
示例:假设是 4 核机器,cache 是缺省大小 16M, blocks 是缺省值 6,假设有 10 万张表,标签总长度是 256 字节,则总的内存需求为:4 \* (16 \* 6 + 10) + 100000 \* (0.25 + 0.5) / 1000 = 499M。
|
||||
示例:假设是 4 核机器,cache 是缺省大小 16M, blocks 是缺省值 6,并且一个 DB 中有 10 万张表,标签总长度是 256 字节,则这个 DB 总的内存需求为:4 \* (16 \* 6 + 10) + 100000 \* (0.25 + 0.5) / 1000 = 499M。
|
||||
|
||||
注意:从这个公式计算得到的内存容量,应理解为系统的“必要需求”,而不是“内存总数”。在实际运行的生产系统中,由于操作系统缓存、资源管理调度等方面的需要,内存规划应当在计算结果的基础上保留一定冗余,以维持系统状态和系统性能的稳定性。
|
||||
在实际的系统运维中,我们通常会更关心 TDengine 服务进程(taosd)会占用的内存量。
|
||||
```
|
||||
taosd 内存总量 = vnode 内存 + mnode 内存 + 查询内存
|
||||
```
|
||||
|
||||
实际运行的系统往往会根据数据特点的不同,将数据存放在不同的 DB 里。因此做规划时,也需要考虑。
|
||||
其中:
|
||||
1. “vnode 内存”指的是集群中所有的 Database 存储分摊到当前 taosd 节点上所占用的内存资源。可以按上文“Database Memory Size”计算公式估算每个 DB 的内存占用量进行加总,再按集群中总共的 TDengine 节点数做平均(如果设置为多副本,则还需要乘以对应的副本倍数)。
|
||||
2. “mnode 内存”指的是集群中管理节点所占用的资源。如果一个 taosd 节点上分布有 mnode 管理节点,则内存消耗还需要增加“0.2KB * 集群中数据表总数”。
|
||||
3. “查询内存”指的是服务端处理查询请求时所需要占用的内存。单条查询语句至少会占用“0.2KB * 查询涉及的数据表总数”的内存量。
|
||||
|
||||
如果内存充裕,可以加大 Blocks 的配置,这样更多数据将保存在内存里,提高查询速度。
|
||||
注意:以上内存估算方法,主要讲解了系统的“必须内存需求”,而不是“内存总数上限”。在实际运行的生产环境中,由于操作系统缓存、资源管理调度等方面的原因,内存规划应当在估算结果的基础上保留一定冗余,以维持系统状态和系统性能的稳定性。并且,生产环境通常会配置系统资源的监控工具,以便及时发现硬件资源的紧缺情况。
|
||||
|
||||
最后,如果内存充裕,可以考虑加大 Blocks 的配置,这样更多数据将保存在内存里,提高查询速度。
|
||||
|
||||
### CPU 需求
|
||||
|
||||
|
@ -112,17 +120,17 @@ taosd -C
|
|||
不同应用场景的数据往往具有不同的数据特征,比如保留天数、副本数、采集频次、记录大小、采集点的数量、压缩等都可完全不同。为获得在存储上的最高效率,TDengine提供如下存储相关的系统配置参数:
|
||||
|
||||
- days:一个数据文件存储数据的时间跨度,单位为天,默认值:10。
|
||||
- keep:数据库中数据保留的天数,单位为天,默认值:3650。
|
||||
- keep:数据库中数据保留的天数,单位为天,默认值:3650。(可通过 alter database 修改)
|
||||
- minRows:文件块中记录的最小条数,单位为条,默认值:100。
|
||||
- maxRows:文件块中记录的最大条数,单位为条,默认值:4096。
|
||||
- comp:文件压缩标志位,0:关闭;1:一阶段压缩;2:两阶段压缩。默认值:2。
|
||||
- comp:文件压缩标志位,0:关闭;1:一阶段压缩;2:两阶段压缩。默认值:2。(可通过 alter database 修改)
|
||||
- walLevel:WAL级别。1:写wal,但不执行fsync;2:写wal, 而且执行fsync。默认值:1。
|
||||
- fsync:当wal设置为2时,执行fsync的周期。设置为0,表示每次写入,立即执行fsync。单位为毫秒,默认值:3000。
|
||||
- cache:内存块的大小,单位为兆字节(MB),默认值:16。
|
||||
- blocks:每个VNODE(TSDB)中有多少cache大小的内存块。因此一个VNODE的用的内存大小粗略为(cache * blocks)。单位为块,默认值:4。
|
||||
- replica:副本个数,取值范围:1-3。单位为个,默认值:1
|
||||
- precision:时间戳精度标识,ms表示毫秒,us表示微秒。默认值:ms
|
||||
- cacheLast:是否在内存中缓存子表 last_row,0:关闭;1:开启。默认值:0。(从 2.0.11 版本开始支持此参数)
|
||||
- blocks:每个VNODE(TSDB)中有多少cache大小的内存块。因此一个VNODE的用的内存大小粗略为(cache * blocks)。单位为块,默认值:4。(可通过 alter database 修改)
|
||||
- replica:副本个数,取值范围:1-3。单位为个,默认值:1。(可通过 alter database 修改)
|
||||
- precision:时间戳精度标识,ms表示毫秒,us表示微秒。默认值:ms。
|
||||
- cacheLast:是否在内存中缓存子表 last_row,0:关闭;1:开启。默认值:0。(可通过 alter database 修改)(从 2.0.11 版本开始支持此参数)
|
||||
|
||||
对于一个应用场景,可能有多种数据特征的数据并存,最佳的设计是将具有相同数据特征的表放在一个库里,这样一个应用有多个库,而每个库可以配置不同的存储参数,从而保证系统有最优的性能。TDengine允许应用在创建库时指定上述存储参数,如果指定,该参数就将覆盖对应的系统配置参数。举例,有下述SQL:
|
||||
|
||||
|
|
|
@ -31,19 +31,19 @@ taos> DESCRIBE meters;
|
|||
|
||||
使用 TDengine,最重要的是时间戳。创建并插入记录、查询历史记录的时候,均需要指定时间戳。时间戳有如下规则:
|
||||
|
||||
- 时间格式为```YYYY-MM-DD HH:mm:ss.MS```, 默认时间分辨率为毫秒。比如:```2017-08-12 18:25:58.128```
|
||||
- 内部函数now是服务器的当前时间
|
||||
- 插入记录时,如果时间戳为now,插入数据时使用服务器当前时间
|
||||
- Epoch Time: 时间戳也可以是一个长整数,表示从1970-01-01 08:00:00.000开始的毫秒数
|
||||
- 时间可以加减,比如 now-2h,表明查询时刻向前推2个小时(最近2小时)。 数字后面的时间单位可以是 a(毫秒)、s(秒)、 m(分)、h(小时)、d(天)、w(周)。 比如select * from t1 where ts > now-2w and ts <= now-1w, 表示查询两周前整整一周的数据。 在指定降频操作(down sampling)的时间窗口(interval)时,时间单位还可以使用 n(自然月) 和 y(自然年)。
|
||||
- 时间格式为 ```YYYY-MM-DD HH:mm:ss.MS```,默认时间分辨率为毫秒。比如:```2017-08-12 18:25:58.128```
|
||||
- 内部函数 now 是客户端的当前时间
|
||||
- 插入记录时,如果时间戳为 now,插入数据时使用提交这条记录的客户端的当前时间
|
||||
- Epoch Time:时间戳也可以是一个长整数,表示从 1970-01-01 08:00:00.000 开始的毫秒数
|
||||
- 时间可以加减,比如 now-2h,表明查询时刻向前推 2 个小时(最近 2 小时)。数字后面的时间单位可以是 u(微秒)、a(毫秒)、s(秒)、m(分)、h(小时)、d(天)、w(周)。 比如 `select * from t1 where ts > now-2w and ts <= now-1w`,表示查询两周前整整一周的数据。在指定降频操作(down sampling)的时间窗口(interval)时,时间单位还可以使用 n(自然月) 和 y(自然年)。
|
||||
|
||||
TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMicrosecond就可支持微秒。
|
||||
TDengine 缺省的时间戳是毫秒精度,但通过修改配置参数 enableMicrosecond 就可以支持微秒。
|
||||
|
||||
在TDengine中,普通表的数据模型中可使用以下 10 种数据类型。
|
||||
|
||||
| | 类型 | Bytes | 说明 |
|
||||
| ---- | :-------: | ------ | ------------------------------------------------------------ |
|
||||
| 1 | TIMESTAMP | 8 | 时间戳。缺省精度毫秒,可支持微秒。从格林威治时间 1970-01-01 00:00:00.000 (UTC/GMT) 开始,计时不能早于该时间。 |
|
||||
| 1 | TIMESTAMP | 8 | 时间戳。缺省精度毫秒,可支持微秒。从格林威治时间 1970-01-01 00:00:00.000 (UTC/GMT) 开始,计时不能早于该时间。(从 2.0.18 版本开始,已经去除了这一时间范围限制) |
|
||||
| 2 | INT | 4 | 整型,范围 [-2^31+1, 2^31-1], -2^31 用作 NULL |
|
||||
| 3 | BIGINT | 8 | 长整型,范围 [-2^63+1, 2^63-1], -2^63 用于 NULL |
|
||||
| 4 | FLOAT | 4 | 浮点型,有效位数 6-7,范围 [-3.4E38, 3.4E38] |
|
||||
|
@ -249,7 +249,7 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic
|
|||
|
||||
3) TAGS 列名不能为预留关键字;
|
||||
|
||||
4) TAGS 最多允许128个,至少1个,总长度不超过16k个字符。
|
||||
4) TAGS 最多允许 128 个,至少 1 个,总长度不超过 16 KB。
|
||||
|
||||
- **删除超级表**
|
||||
|
||||
|
|
|
@ -156,3 +156,13 @@ ALTER LOCAL RESETLOG;
|
|||
```
|
||||
|
||||
其含义是,清空本机所有由客户端生成的日志文件。
|
||||
|
||||
## <a class="anchor" id="timezone"></a>18. 时间戳的时区信息是怎样处理的?
|
||||
|
||||
TDengine 中时间戳的时区总是由客户端进行处理,而与服务端无关。具体来说,客户端会对 SQL 语句中的时间戳进行时区转换,转为 UTC 时区(即 Unix 时间戳——Unix Timestamp)再交由服务端进行写入和查询;在读取数据时,服务端也是采用 UTC 时区提供原始数据,客户端收到后再根据本地设置,把时间戳转换为本地系统所要求的时区进行显示。
|
||||
|
||||
客户端在处理时间戳字符串时,会采取如下逻辑:
|
||||
1. 在未做特殊设置的情况下,客户端默认使用所在操作系统的时区设置。
|
||||
2. 如果在 taos.cfg 中设置了 timezone 参数,则客户端会以这个配置文件中的设置为准。
|
||||
3. 如果在 C/C++/Java/Python 等各种编程语言的 Connector Driver 中,在建立数据库连接时显式指定了 timezone,那么会以这个指定的时区设置为准。例如 Java Connector 的 JDBC URL 中就有 timezone 参数。
|
||||
4. 在书写 SQL 语句时,也可以直接使用 Unix 时间戳(例如 `1554984068000`)或带有时区的时间戳字符串,也即以 RFC 3339 格式(例如 `2013-04-12T15:52:01.123+08:00`)或 ISO-8601 格式(例如 `2013-04-12T15:52:01.123+0800`)来书写时间戳,此时这些时间戳的取值将不再受其他时区设置的影响。
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
# number of threads per CPU core
|
||||
# numOfThreadsPerCore 1.0
|
||||
|
||||
# number of threads to commit cache data
|
||||
# numOfCommitThreads 4
|
||||
|
||||
# the proportion of total CPU cores available for query processing
|
||||
# 2.0: the query threads will be set to double of the CPU cores.
|
||||
# 1.0: all CPU cores are available for query processing [default].
|
||||
|
|
|
@ -120,7 +120,7 @@ function clean_service_on_systemd() {
|
|||
|
||||
if [ "$verMode" == "cluster" ]; then
|
||||
nginx_service_config="${service_config_dir}/${nginx_service_name}.service"
|
||||
if [ -d ${bin_dir}/web ]; then
|
||||
if [ -d ${install_nginxd_dir} ]; then
|
||||
if systemctl is-active --quiet ${nginx_service_name}; then
|
||||
echo "Nginx for TDengine is running, stopping it..."
|
||||
${csudo} systemctl stop ${nginx_service_name} &> /dev/null || echo &> /dev/null
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: tdengine
|
||||
base: core18
|
||||
version: '2.0.17.0'
|
||||
version: '2.0.19.0'
|
||||
icon: snap/gui/t-dengine.svg
|
||||
summary: an open-source big data platform designed and optimized for IoT.
|
||||
description: |
|
||||
|
@ -72,7 +72,7 @@ parts:
|
|||
- usr/bin/taosd
|
||||
- usr/bin/taos
|
||||
- usr/bin/taosdemo
|
||||
- usr/lib/libtaos.so.2.0.17.0
|
||||
- usr/lib/libtaos.so.2.0.19.0
|
||||
- usr/lib/libtaos.so.1
|
||||
- usr/lib/libtaos.so
|
||||
|
||||
|
|
|
@ -19,6 +19,6 @@ ADD_SUBDIRECTORY(tsdb)
|
|||
ADD_SUBDIRECTORY(wal)
|
||||
ADD_SUBDIRECTORY(cq)
|
||||
ADD_SUBDIRECTORY(dnode)
|
||||
#ADD_SUBDIRECTORY(connector/odbc)
|
||||
ADD_SUBDIRECTORY(connector/odbc)
|
||||
ADD_SUBDIRECTORY(connector/jdbc)
|
||||
|
||||
|
|
|
@ -36,19 +36,6 @@ extern "C" {
|
|||
#define UTIL_TABLE_IS_NORMAL_TABLE(metaInfo)\
|
||||
(!(UTIL_TABLE_IS_SUPER_TABLE(metaInfo) || UTIL_TABLE_IS_CHILD_TABLE(metaInfo)))
|
||||
|
||||
|
||||
typedef struct SParsedColElem {
|
||||
int16_t colIndex;
|
||||
uint16_t offset;
|
||||
} SParsedColElem;
|
||||
|
||||
typedef struct SParsedDataColInfo {
|
||||
int16_t numOfCols;
|
||||
int16_t numOfAssignedCols;
|
||||
SParsedColElem elems[TSDB_MAX_COLUMNS];
|
||||
bool hasVal[TSDB_MAX_COLUMNS];
|
||||
} SParsedDataColInfo;
|
||||
|
||||
#pragma pack(push,1)
|
||||
// this struct is transfered as binary, padding two bytes to avoid
|
||||
// an 'uid' whose low bytes is 0xff being recoginized as NULL,
|
||||
|
@ -83,6 +70,22 @@ typedef struct SJoinSupporter {
|
|||
SArray* pVgroupTables;
|
||||
} SJoinSupporter;
|
||||
|
||||
|
||||
typedef struct SMergeCtx {
|
||||
SJoinSupporter* p;
|
||||
int32_t idx;
|
||||
SArray* res;
|
||||
int8_t compared;
|
||||
}SMergeCtx;
|
||||
|
||||
typedef struct SMergeTsCtx {
|
||||
SJoinSupporter* p;
|
||||
STSBuf* res;
|
||||
int64_t numOfInput;
|
||||
int8_t compared;
|
||||
}SMergeTsCtx;
|
||||
|
||||
|
||||
typedef struct SVgroupTableInfo {
|
||||
SVgroupInfo vgInfo;
|
||||
SArray* itemList; //SArray<STableIdInfo>
|
||||
|
@ -102,6 +105,8 @@ int32_t tscCreateDataBlock(size_t initialSize, int32_t rowSize, int32_t startOff
|
|||
void tscDestroyDataBlock(STableDataBlocks* pDataBlock, bool removeMeta);
|
||||
void tscSortRemoveDataBlockDupRows(STableDataBlocks* dataBuf);
|
||||
|
||||
void tscDestroyBoundColumnInfo(SParsedDataColInfo* pColInfo);
|
||||
|
||||
SParamInfo* tscAddParamToDataBlock(STableDataBlocks* pDataBlock, char type, uint8_t timePrec, int16_t bytes,
|
||||
uint32_t offset);
|
||||
|
||||
|
@ -124,6 +129,8 @@ bool tscIsPointInterpQuery(SQueryInfo* pQueryInfo);
|
|||
bool tscIsTWAQuery(SQueryInfo* pQueryInfo);
|
||||
bool tscIsSecondStageQuery(SQueryInfo* pQueryInfo);
|
||||
bool tscGroupbyColumn(SQueryInfo* pQueryInfo);
|
||||
bool tscIsTopbotQuery(SQueryInfo* pQueryInfo);
|
||||
int32_t tscGetTopbotQueryParam(SQueryInfo* pQueryInfo);
|
||||
|
||||
bool tscNonOrderedProjectionQueryOnSTable(SQueryInfo *pQueryInfo, int32_t tableIndex);
|
||||
bool tscOrderedProjectionQueryOnSTable(SQueryInfo* pQueryInfo, int32_t tableIndex);
|
||||
|
@ -183,6 +190,7 @@ int32_t tscSqlExprCopy(SArray* dst, const SArray* src, uint64_t uid, bool deep
|
|||
void tscSqlExprInfoDestroy(SArray* pExprInfo);
|
||||
|
||||
SColumn* tscColumnClone(const SColumn* src);
|
||||
bool tscColumnExists(SArray* pColumnList, SColumnIndex* pColIndex);
|
||||
SColumn* tscColumnListInsert(SArray* pColList, SColumnIndex* colIndex);
|
||||
SArray* tscColumnListClone(const SArray* src, int16_t tableIndex);
|
||||
void tscColumnListDestroy(SArray* pColList);
|
||||
|
|
|
@ -142,15 +142,15 @@ typedef struct SCond {
|
|||
} SCond;
|
||||
|
||||
typedef struct SJoinNode {
|
||||
char tableName[TSDB_TABLE_FNAME_LEN];
|
||||
uint64_t uid;
|
||||
int16_t tagColId;
|
||||
SArray* tsJoin;
|
||||
SArray* tagJoin;
|
||||
} SJoinNode;
|
||||
|
||||
typedef struct SJoinInfo {
|
||||
bool hasJoin;
|
||||
SJoinNode left;
|
||||
SJoinNode right;
|
||||
SJoinNode* joinTables[TSDB_MAX_JOIN_TABLE_NUM];
|
||||
} SJoinInfo;
|
||||
|
||||
typedef struct STagCond {
|
||||
|
@ -175,6 +175,19 @@ typedef struct SParamInfo {
|
|||
uint32_t offset;
|
||||
} SParamInfo;
|
||||
|
||||
|
||||
typedef struct SBoundColumn {
|
||||
bool hasVal; // denote if current column has bound or not
|
||||
int32_t offset; // all column offset value
|
||||
} SBoundColumn;
|
||||
|
||||
typedef struct SParsedDataColInfo {
|
||||
int16_t numOfCols;
|
||||
int16_t numOfBound;
|
||||
int32_t *boundedColumns;
|
||||
SBoundColumn *cols;
|
||||
} SParsedDataColInfo;
|
||||
|
||||
typedef struct STableDataBlocks {
|
||||
SName tableName;
|
||||
int8_t tsSource; // where does the UNIX timestamp come from, server or client
|
||||
|
@ -189,6 +202,8 @@ typedef struct STableDataBlocks {
|
|||
STableMeta *pTableMeta; // the tableMeta of current table, the table meta will be used during submit, keep a ref to avoid to be removed from cache
|
||||
char *pData;
|
||||
|
||||
SParsedDataColInfo boundColumnInfo;
|
||||
|
||||
// for parameter ('?') binding
|
||||
uint32_t numOfAllocedParams;
|
||||
uint32_t numOfParams;
|
||||
|
@ -284,7 +299,7 @@ typedef struct {
|
|||
char * pRsp;
|
||||
int32_t rspType;
|
||||
int32_t rspLen;
|
||||
uint64_t qhandle;
|
||||
uint64_t qId;
|
||||
int64_t useconds;
|
||||
int64_t offset; // offset value from vnode during projection query of stable
|
||||
int32_t row;
|
||||
|
@ -367,7 +382,7 @@ typedef struct SSqlObj {
|
|||
int64_t svgroupRid;
|
||||
|
||||
int64_t squeryLock;
|
||||
|
||||
int32_t retryReason; // previous error code
|
||||
struct SSqlObj *prev, *next;
|
||||
int64_t self;
|
||||
} SSqlObj;
|
||||
|
@ -425,6 +440,7 @@ void tscRestoreFuncForSTableQuery(SQueryInfo *pQueryInfo);
|
|||
|
||||
int32_t tscCreateResPointerInfo(SSqlRes *pRes, SQueryInfo *pQueryInfo);
|
||||
void tscSetResRawPtr(SSqlRes* pRes, SQueryInfo* pQueryInfo);
|
||||
void destroyTableNameList(SSqlCmd* pCmd);
|
||||
|
||||
void tscResetSqlCmd(SSqlCmd *pCmd, bool removeMeta);
|
||||
|
||||
|
@ -439,6 +455,8 @@ void tscFreeSqlResult(SSqlObj *pSql);
|
|||
* @param pObj
|
||||
*/
|
||||
void tscFreeSqlObj(SSqlObj *pSql);
|
||||
void tscFreeSubobj(SSqlObj* pSql);
|
||||
|
||||
void tscFreeRegisteredSqlObj(void *pSql);
|
||||
|
||||
void tscCloseTscObj(void *pObj);
|
||||
|
@ -460,6 +478,7 @@ char* tscGetSqlStr(SSqlObj* pSql);
|
|||
bool tscIsQueryWithLimit(SSqlObj* pSql);
|
||||
|
||||
bool tscHasReachLimitation(SQueryInfo *pQueryInfo, SSqlRes *pRes);
|
||||
void tscSetBoundColumnInfo(SParsedDataColInfo *pColInfo, SSchema *pSchema, int32_t numOfCols);
|
||||
|
||||
char *tscGetErrorMsgPayload(SSqlCmd *pCmd);
|
||||
|
||||
|
|
|
@ -160,8 +160,8 @@ static void tscProcessAsyncRetrieveImpl(void *param, TAOS_RES *tres, int numOfRo
|
|||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
SSqlRes *pRes = &pSql->res;
|
||||
|
||||
if ((pRes->qhandle == 0 || numOfRows != 0) && pCmd->command < TSDB_SQL_LOCAL) {
|
||||
if (pRes->qhandle == 0 && numOfRows != 0) {
|
||||
if ((pRes->qId == 0 || numOfRows != 0) && pCmd->command < TSDB_SQL_LOCAL) {
|
||||
if (pRes->qId == 0 && numOfRows != 0) {
|
||||
tscError("qhandle is NULL");
|
||||
} else {
|
||||
pRes->code = numOfRows;
|
||||
|
@ -208,7 +208,7 @@ void taos_fetch_rows_a(TAOS_RES *taosa, __async_cb_func_t fp, void *param) {
|
|||
pSql->fetchFp = fp;
|
||||
pSql->fp = tscAsyncFetchRowsProxy;
|
||||
|
||||
if (pRes->qhandle == 0) {
|
||||
if (pRes->qId == 0) {
|
||||
tscError("qhandle is NULL");
|
||||
pRes->code = TSDB_CODE_TSC_INVALID_QHANDLE;
|
||||
pSql->param = param;
|
||||
|
@ -310,10 +310,51 @@ void tscAsyncResultOnError(SSqlObj* pSql) {
|
|||
taosScheduleTask(tscQhandle, &schedMsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int tscSendMsgToServer(SSqlObj *pSql);
|
||||
|
||||
static int32_t updateMetaBeforeRetryQuery(SSqlObj* pSql, STableMetaInfo* pTableMetaInfo, SQueryInfo* pQueryInfo) {
|
||||
// handle the invalid table error code for super table.
|
||||
// update the pExpr info, colList info, number of table columns
|
||||
// TODO Re-parse this sql and issue the corresponding subquery as an alternative for this case.
|
||||
if (pSql->retryReason == TSDB_CODE_TDB_INVALID_TABLE_ID) {
|
||||
int32_t numOfExprs = (int32_t) tscSqlExprNumOfExprs(pQueryInfo);
|
||||
int32_t numOfCols = tscGetNumOfColumns(pTableMetaInfo->pTableMeta);
|
||||
int32_t numOfTags = tscGetNumOfTags(pTableMetaInfo->pTableMeta);
|
||||
|
||||
SSchema *pSchema = tscGetTableSchema(pTableMetaInfo->pTableMeta);
|
||||
for (int32_t i = 0; i < numOfExprs; ++i) {
|
||||
SSqlExpr *pExpr = tscSqlExprGet(pQueryInfo, i);
|
||||
pExpr->uid = pTableMetaInfo->pTableMeta->id.uid;
|
||||
|
||||
if (pExpr->colInfo.colIndex >= 0) {
|
||||
int32_t index = pExpr->colInfo.colIndex;
|
||||
|
||||
if ((TSDB_COL_IS_NORMAL_COL(pExpr->colInfo.flag) && index >= numOfCols) ||
|
||||
(TSDB_COL_IS_TAG(pExpr->colInfo.flag) && (index < numOfCols || index >= (numOfCols + numOfTags)))) {
|
||||
return pSql->retryReason;
|
||||
}
|
||||
|
||||
if ((pSchema[pExpr->colInfo.colIndex].colId != pExpr->colInfo.colId) &&
|
||||
strcasecmp(pExpr->colInfo.name, pSchema[pExpr->colInfo.colIndex].name) != 0) {
|
||||
return pSql->retryReason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate the table columns information
|
||||
for (int32_t i = 0; i < taosArrayGetSize(pQueryInfo->colList); ++i) {
|
||||
SColumn *pCol = taosArrayGetP(pQueryInfo->colList, i);
|
||||
if (pCol->colIndex.columnIndex >= numOfCols) {
|
||||
return pSql->retryReason;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) {
|
||||
SSqlObj* pSql = (SSqlObj*)taosAcquireRef(tscObjRef, (int64_t)param);
|
||||
if (pSql == NULL) return;
|
||||
|
@ -340,6 +381,7 @@ void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) {
|
|||
tscDebug("%p update local table meta, continue to process sql and send the corresponding query", pSql);
|
||||
|
||||
STableMetaInfo *pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||
|
||||
code = tscGetTableMeta(pSql, pTableMetaInfo);
|
||||
assert(code == TSDB_CODE_TSC_ACTION_IN_PROGRESS || code == TSDB_CODE_SUCCESS);
|
||||
|
||||
|
@ -349,6 +391,10 @@ void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) {
|
|||
}
|
||||
|
||||
assert((tscGetNumOfTags(pTableMetaInfo->pTableMeta) != 0));
|
||||
code = updateMetaBeforeRetryQuery(pSql, pTableMetaInfo, pQueryInfo);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _error;
|
||||
}
|
||||
|
||||
// tscProcessSql can add error into async res
|
||||
tscProcessSql(pSql);
|
||||
|
@ -459,10 +505,7 @@ void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) {
|
|||
return;
|
||||
|
||||
_error:
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
pSql->res.code = code;
|
||||
pRes->code = code;
|
||||
tscAsyncResultOnError(pSql);
|
||||
}
|
||||
|
||||
taosReleaseRef(tscObjRef, pSql->self);
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ TAOS_ROW tscFetchRow(void *param) {
|
|||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
SSqlRes *pRes = &pSql->res;
|
||||
|
||||
if (pRes->qhandle == 0 ||
|
||||
if (pRes->qId == 0 ||
|
||||
pCmd->command == TSDB_SQL_RETRIEVE_EMPTY_RESULT ||
|
||||
pCmd->command == TSDB_SQL_INSERT) {
|
||||
return NULL;
|
||||
|
@ -905,7 +905,7 @@ int tscProcessLocalCmd(SSqlObj *pSql) {
|
|||
* set the qhandle to be 1 in order to pass the qhandle check, and to call partial release function to
|
||||
* free allocated resources and remove the SqlObj from sql query linked list
|
||||
*/
|
||||
pRes->qhandle = 0x1;
|
||||
pRes->qId = 0x1;
|
||||
pRes->numOfRows = 0;
|
||||
} else if (pCmd->command == TSDB_SQL_SHOW_CREATE_TABLE) {
|
||||
pRes->code = tscProcessShowCreateTable(pSql);
|
||||
|
|
|
@ -338,11 +338,20 @@ void tscCreateLocalMerger(tExtMemBuffer **pMemBuffer, int32_t numOfBuffer, tOrde
|
|||
pReducer->resColModel->capacity = pReducer->nResultBufSize;
|
||||
pReducer->finalModel = pFFModel;
|
||||
|
||||
int32_t expandFactor = 1;
|
||||
if (finalmodel->rowSize > 0) {
|
||||
bool topBotQuery = tscIsTopbotQuery(pQueryInfo);
|
||||
if (topBotQuery) {
|
||||
expandFactor = tscGetTopbotQueryParam(pQueryInfo);
|
||||
pReducer->resColModel->capacity /= (finalmodel->rowSize * expandFactor);
|
||||
pReducer->resColModel->capacity *= expandFactor;
|
||||
} else {
|
||||
pReducer->resColModel->capacity /= finalmodel->rowSize;
|
||||
}
|
||||
}
|
||||
|
||||
assert(finalmodel->rowSize > 0 && finalmodel->rowSize <= pReducer->rowSize);
|
||||
|
||||
pReducer->pFinalRes = calloc(1, pReducer->rowSize * pReducer->resColModel->capacity);
|
||||
|
||||
if (pReducer->pTempBuffer == NULL || pReducer->discardData == NULL || pReducer->pResultBuf == NULL ||
|
||||
|
@ -1150,9 +1159,10 @@ static void fillMultiRowsOfTagsVal(SQueryInfo *pQueryInfo, int32_t numOfRes, SLo
|
|||
memset(buf, 0, (size_t)maxBufSize);
|
||||
memcpy(buf, pCtx->pOutput, (size_t)pCtx->outputBytes);
|
||||
|
||||
char* next = pCtx->pOutput;
|
||||
for (int32_t i = 0; i < inc; ++i) {
|
||||
pCtx->pOutput += pCtx->outputBytes;
|
||||
memcpy(pCtx->pOutput, buf, (size_t)pCtx->outputBytes);
|
||||
next += pCtx->outputBytes;
|
||||
memcpy(next, buf, (size_t)pCtx->outputBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1440,6 +1450,11 @@ int32_t tscDoLocalMerge(SSqlObj *pSql) {
|
|||
SQueryInfo *pQueryInfo = tscGetQueryInfoDetail(pCmd, pCmd->clauseIndex);
|
||||
tFilePage *tmpBuffer = pLocalMerge->pTempBuffer;
|
||||
|
||||
int32_t remain = 1;
|
||||
if (tscIsTopbotQuery(pQueryInfo)) {
|
||||
remain = tscGetTopbotQueryParam(pQueryInfo);
|
||||
}
|
||||
|
||||
if (doHandleLastRemainData(pSql)) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
@ -1528,7 +1543,7 @@ int32_t tscDoLocalMerge(SSqlObj *pSql) {
|
|||
* if the previous group does NOT generate any result (pResBuf->num == 0),
|
||||
* continue to process results instead of return results.
|
||||
*/
|
||||
if ((!sameGroup && pResBuf->num > 0) || (pResBuf->num == pLocalMerge->resColModel->capacity)) {
|
||||
if ((!sameGroup && pResBuf->num > 0) || (pResBuf->num + remain >= pLocalMerge->resColModel->capacity)) {
|
||||
// does not belong to the same group
|
||||
bool notSkipped = genFinalResults(pSql, pLocalMerge, !sameGroup);
|
||||
|
||||
|
@ -1607,7 +1622,7 @@ void tscInitResObjForLocalQuery(SSqlObj *pObj, int32_t numOfRes, int32_t rowLen)
|
|||
tscDestroyLocalMerger(pObj);
|
||||
}
|
||||
|
||||
pRes->qhandle = 1; // hack to pass the safety check in fetch_row function
|
||||
pRes->qId = 1; // hack to pass the safety check in fetch_row function
|
||||
pRes->numOfRows = 0;
|
||||
pRes->row = 0;
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ enum {
|
|||
};
|
||||
|
||||
static int32_t tscAllocateMemIfNeed(STableDataBlocks *pDataBlock, int32_t rowSize, int32_t * numOfRows);
|
||||
static int32_t parseBoundColumns(SSqlCmd* pCmd, SParsedDataColInfo* pColInfo, SSchema* pSchema, char* str, char** end);
|
||||
|
||||
static int32_t tscToDouble(SStrToken *pToken, double *value, char **endPtr) {
|
||||
errno = 0;
|
||||
|
@ -94,12 +95,12 @@ int tsParseTime(SStrToken *pToken, int64_t *time, char **next, char *error, int1
|
|||
*/
|
||||
SStrToken valueToken;
|
||||
index = 0;
|
||||
sToken = tStrGetToken(pTokenEnd, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(pTokenEnd, &index, false);
|
||||
pTokenEnd += index;
|
||||
|
||||
if (sToken.type == TK_MINUS || sToken.type == TK_PLUS) {
|
||||
index = 0;
|
||||
valueToken = tStrGetToken(pTokenEnd, &index, false, 0, NULL);
|
||||
valueToken = tStrGetToken(pTokenEnd, &index, false);
|
||||
pTokenEnd += index;
|
||||
|
||||
if (valueToken.n < 2) {
|
||||
|
@ -117,7 +118,7 @@ int tsParseTime(SStrToken *pToken, int64_t *time, char **next, char *error, int1
|
|||
if (sToken.type == TK_PLUS) {
|
||||
useconds += interval;
|
||||
} else {
|
||||
useconds = (useconds >= interval) ? useconds - interval : 0;
|
||||
useconds = useconds - interval;
|
||||
}
|
||||
|
||||
*next = pTokenEnd;
|
||||
|
@ -127,12 +128,11 @@ int tsParseTime(SStrToken *pToken, int64_t *time, char **next, char *error, int1
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
// todo extract the null value check
|
||||
static bool isNullStr(SStrToken* pToken) {
|
||||
return (pToken->type == TK_NULL) || ((pToken->type == TK_STRING) && (pToken->n != 0) &&
|
||||
(strncasecmp(TSDB_DATA_NULL_STR_L, pToken->z, pToken->n) == 0));
|
||||
}
|
||||
int32_t tsParseOneColumnData(SSchema *pSchema, SStrToken *pToken, char *payload, char *msg, char **str, bool primaryKey,
|
||||
int32_t tsParseOneColumn(SSchema *pSchema, SStrToken *pToken, char *payload, char *msg, char **str, bool primaryKey,
|
||||
int16_t timePrec) {
|
||||
int64_t iv;
|
||||
int32_t ret;
|
||||
|
@ -417,29 +417,32 @@ static int32_t tsCheckTimestamp(STableDataBlocks *pDataBlocks, const char *start
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
int tsParseOneRowData(char **str, STableDataBlocks *pDataBlocks, SSchema schema[], SParsedDataColInfo *spd, SSqlCmd* pCmd,
|
||||
int16_t timePrec, int32_t *code, char *tmpTokenBuf) {
|
||||
int tsParseOneRow(char **str, STableDataBlocks *pDataBlocks, SSqlCmd *pCmd, int16_t timePrec, int32_t *len,
|
||||
char *tmpTokenBuf) {
|
||||
int32_t index = 0;
|
||||
SStrToken sToken = {0};
|
||||
char *payload = pDataBlocks->pData + pDataBlocks->size;
|
||||
|
||||
SParsedDataColInfo *spd = &pDataBlocks->boundColumnInfo;
|
||||
SSchema *schema = tscGetTableSchema(pDataBlocks->pTableMeta);
|
||||
|
||||
// 1. set the parsed value from sql string
|
||||
int32_t rowSize = 0;
|
||||
for (int i = 0; i < spd->numOfAssignedCols; ++i) {
|
||||
for (int i = 0; i < spd->numOfBound; ++i) {
|
||||
// the start position in data block buffer of current value in sql
|
||||
char * start = payload + spd->elems[i].offset;
|
||||
int16_t colIndex = spd->elems[i].colIndex;
|
||||
SSchema *pSchema = schema + colIndex;
|
||||
int32_t colIndex = spd->boundedColumns[i];
|
||||
|
||||
char *start = payload + spd->cols[colIndex].offset;
|
||||
SSchema *pSchema = &schema[colIndex];
|
||||
rowSize += pSchema->bytes;
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(*str, &index, true, 0, NULL);
|
||||
sToken = tStrGetToken(*str, &index, true);
|
||||
*str += index;
|
||||
|
||||
if (sToken.type == TK_QUESTION) {
|
||||
if (pCmd->insertType != TSDB_QUERY_TYPE_STMT_INSERT) {
|
||||
*code = tscSQLSyntaxErrMsg(pCmd->payload, "? only allowed in binding insertion", *str);
|
||||
return -1;
|
||||
return tscSQLSyntaxErrMsg(pCmd->payload, "? only allowed in binding insertion", *str);
|
||||
}
|
||||
|
||||
uint32_t offset = (uint32_t)(start - pDataBlocks->pData);
|
||||
|
@ -448,15 +451,13 @@ int tsParseOneRowData(char **str, STableDataBlocks *pDataBlocks, SSchema schema[
|
|||
}
|
||||
|
||||
strcpy(pCmd->payload, "client out of memory");
|
||||
*code = TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
return -1;
|
||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
int16_t type = sToken.type;
|
||||
if ((type != TK_NOW && type != TK_INTEGER && type != TK_STRING && type != TK_FLOAT && type != TK_BOOL &&
|
||||
type != TK_NULL && type != TK_HEX && type != TK_OCT && type != TK_BIN) || (sToken.n == 0) || (type == TK_RP)) {
|
||||
*code = tscSQLSyntaxErrMsg(pCmd->payload, "invalid data or symbol", sToken.z);
|
||||
return -1;
|
||||
return tscSQLSyntaxErrMsg(pCmd->payload, "invalid data or symbol", sToken.z);
|
||||
}
|
||||
|
||||
// Remove quotation marks
|
||||
|
@ -485,26 +486,23 @@ int tsParseOneRowData(char **str, STableDataBlocks *pDataBlocks, SSchema schema[
|
|||
}
|
||||
|
||||
bool isPrimaryKey = (colIndex == PRIMARYKEY_TIMESTAMP_COL_INDEX);
|
||||
int32_t ret = tsParseOneColumnData(pSchema, &sToken, start, pCmd->payload, str, isPrimaryKey, timePrec);
|
||||
int32_t ret = tsParseOneColumn(pSchema, &sToken, start, pCmd->payload, str, isPrimaryKey, timePrec);
|
||||
if (ret != TSDB_CODE_SUCCESS) {
|
||||
*code = TSDB_CODE_TSC_SQL_SYNTAX_ERROR;
|
||||
return -1; // NOTE: here 0 mean error!
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (isPrimaryKey && tsCheckTimestamp(pDataBlocks, start) != TSDB_CODE_SUCCESS) {
|
||||
tscInvalidSQLErrMsg(pCmd->payload, "client time/server time can not be mixed up", sToken.z);
|
||||
*code = TSDB_CODE_TSC_INVALID_TIME_STAMP;
|
||||
return -1;
|
||||
return TSDB_CODE_TSC_INVALID_TIME_STAMP;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. set the null value for the columns that do not assign values
|
||||
if (spd->numOfAssignedCols < spd->numOfCols) {
|
||||
if (spd->numOfBound < spd->numOfCols) {
|
||||
char *ptr = payload;
|
||||
|
||||
for (int32_t i = 0; i < spd->numOfCols; ++i) {
|
||||
|
||||
if (!spd->hasVal[i]) { // current column do not have any value to insert, set it to null
|
||||
if (!spd->cols[i].hasVal) { // current column do not have any value to insert, set it to null
|
||||
if (schema[i].type == TSDB_DATA_TYPE_BINARY) {
|
||||
varDataSetLen(ptr, sizeof(int8_t));
|
||||
*(uint8_t*) varDataVal(ptr) = TSDB_DATA_BINARY_NULL;
|
||||
|
@ -522,7 +520,8 @@ int tsParseOneRowData(char **str, STableDataBlocks *pDataBlocks, SSchema schema[
|
|||
rowSize = (int32_t)(ptr - payload);
|
||||
}
|
||||
|
||||
return rowSize;
|
||||
*len = rowSize;
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t rowDataCompar(const void *lhs, const void *rhs) {
|
||||
|
@ -536,80 +535,79 @@ static int32_t rowDataCompar(const void *lhs, const void *rhs) {
|
|||
}
|
||||
}
|
||||
|
||||
int tsParseValues(char **str, STableDataBlocks *pDataBlock, STableMeta *pTableMeta, int maxRows,
|
||||
SParsedDataColInfo *spd, SSqlCmd* pCmd, int32_t *code, char *tmpTokenBuf) {
|
||||
int32_t tsParseValues(char **str, STableDataBlocks *pDataBlock, int maxRows, SSqlCmd* pCmd, int32_t* numOfRows, char *tmpTokenBuf) {
|
||||
int32_t index = 0;
|
||||
int32_t code = 0;
|
||||
|
||||
(*numOfRows) = 0;
|
||||
|
||||
SStrToken sToken;
|
||||
|
||||
int32_t numOfRows = 0;
|
||||
|
||||
SSchema *pSchema = tscGetTableSchema(pTableMeta);
|
||||
STableMeta* pTableMeta = pDataBlock->pTableMeta;
|
||||
STableComInfo tinfo = tscGetTableInfo(pTableMeta);
|
||||
|
||||
int32_t precision = tinfo.precision;
|
||||
|
||||
if (spd->hasVal[0] == false) {
|
||||
*code = tscInvalidSQLErrMsg(pCmd->payload, "primary timestamp column can not be null", *str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
index = 0;
|
||||
sToken = tStrGetToken(*str, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(*str, &index, false);
|
||||
if (sToken.n == 0 || sToken.type != TK_LP) break;
|
||||
|
||||
*str += index;
|
||||
if (numOfRows >= maxRows || pDataBlock->size + tinfo.rowSize >= pDataBlock->nAllocSize) {
|
||||
if ((*numOfRows) >= maxRows || pDataBlock->size + tinfo.rowSize >= pDataBlock->nAllocSize) {
|
||||
int32_t tSize;
|
||||
*code = tscAllocateMemIfNeed(pDataBlock, tinfo.rowSize, &tSize);
|
||||
if (*code != TSDB_CODE_SUCCESS) { //TODO pass the correct error code to client
|
||||
code = tscAllocateMemIfNeed(pDataBlock, tinfo.rowSize, &tSize);
|
||||
if (code != TSDB_CODE_SUCCESS) { //TODO pass the correct error code to client
|
||||
strcpy(pCmd->payload, "client out of memory");
|
||||
return -1;
|
||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
ASSERT(tSize > maxRows);
|
||||
maxRows = tSize;
|
||||
}
|
||||
|
||||
int32_t len = tsParseOneRowData(str, pDataBlock, pSchema, spd, pCmd, precision, code, tmpTokenBuf);
|
||||
if (len <= 0) { // error message has been set in tsParseOneRowData
|
||||
return -1;
|
||||
int32_t len = 0;
|
||||
code = tsParseOneRow(str, pDataBlock, pCmd, precision, &len, tmpTokenBuf);
|
||||
if (code != TSDB_CODE_SUCCESS) { // error message has been set in tsParseOneRow, return directly
|
||||
return TSDB_CODE_TSC_SQL_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
pDataBlock->size += len;
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(*str, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(*str, &index, false);
|
||||
*str += index;
|
||||
if (sToken.n == 0 || sToken.type != TK_RP) {
|
||||
tscSQLSyntaxErrMsg(pCmd->payload, ") expected", *str);
|
||||
*code = TSDB_CODE_TSC_SQL_SYNTAX_ERROR;
|
||||
code = TSDB_CODE_TSC_SQL_SYNTAX_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
numOfRows++;
|
||||
(*numOfRows)++;
|
||||
}
|
||||
|
||||
if (numOfRows <= 0) {
|
||||
if ((*numOfRows) <= 0) {
|
||||
strcpy(pCmd->payload, "no any data points");
|
||||
*code = TSDB_CODE_TSC_SQL_SYNTAX_ERROR;
|
||||
return -1;
|
||||
return TSDB_CODE_TSC_SQL_SYNTAX_ERROR;
|
||||
} else {
|
||||
return numOfRows;
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
static void tscSetAssignedColumnInfo(SParsedDataColInfo *spd, SSchema *pSchema, int32_t numOfCols) {
|
||||
spd->numOfCols = numOfCols;
|
||||
spd->numOfAssignedCols = numOfCols;
|
||||
void tscSetBoundColumnInfo(SParsedDataColInfo *pColInfo, SSchema *pSchema, int32_t numOfCols) {
|
||||
pColInfo->numOfCols = numOfCols;
|
||||
pColInfo->numOfBound = numOfCols;
|
||||
|
||||
for (int32_t i = 0; i < numOfCols; ++i) {
|
||||
spd->hasVal[i] = true;
|
||||
spd->elems[i].colIndex = i;
|
||||
pColInfo->boundedColumns = calloc(pColInfo->numOfCols, sizeof(int32_t));
|
||||
pColInfo->cols = calloc(pColInfo->numOfCols, sizeof(SBoundColumn));
|
||||
|
||||
for (int32_t i = 0; i < pColInfo->numOfCols; ++i) {
|
||||
if (i > 0) {
|
||||
spd->elems[i].offset = spd->elems[i - 1].offset + pSchema[i - 1].bytes;
|
||||
pColInfo->cols[i].offset = pSchema[i - 1].bytes + pColInfo->cols[i - 1].offset;
|
||||
}
|
||||
|
||||
pColInfo->cols[i].hasVal = true;
|
||||
pColInfo->boundedColumns[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -697,33 +695,26 @@ void tscSortRemoveDataBlockDupRows(STableDataBlocks *dataBuf) {
|
|||
}
|
||||
}
|
||||
|
||||
static int32_t doParseInsertStatement(SSqlCmd* pCmd, char **str, SParsedDataColInfo *spd, int32_t *totalNum) {
|
||||
STableMetaInfo *pTableMetaInfo = tscGetTableMetaInfoFromCmd(pCmd, pCmd->clauseIndex, 0);
|
||||
STableMeta *pTableMeta = pTableMetaInfo->pTableMeta;
|
||||
STableComInfo tinfo = tscGetTableInfo(pTableMeta);
|
||||
|
||||
STableDataBlocks *dataBuf = NULL;
|
||||
int32_t ret = tscGetDataBlockFromList(pCmd->pTableBlockHashList, pTableMeta->id.uid, TSDB_DEFAULT_PAYLOAD_SIZE,
|
||||
sizeof(SSubmitBlk), tinfo.rowSize, &pTableMetaInfo->name, pTableMeta, &dataBuf, NULL);
|
||||
if (ret != TSDB_CODE_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
static int32_t doParseInsertStatement(SSqlCmd* pCmd, char **str, STableDataBlocks* dataBuf, int32_t *totalNum) {
|
||||
STableComInfo tinfo = tscGetTableInfo(dataBuf->pTableMeta);
|
||||
|
||||
int32_t maxNumOfRows;
|
||||
ret = tscAllocateMemIfNeed(dataBuf, tinfo.rowSize, &maxNumOfRows);
|
||||
if (TSDB_CODE_SUCCESS != ret) {
|
||||
int32_t code = tscAllocateMemIfNeed(dataBuf, tinfo.rowSize, &maxNumOfRows);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
int32_t code = TSDB_CODE_TSC_INVALID_SQL;
|
||||
code = TSDB_CODE_TSC_INVALID_SQL;
|
||||
char *tmpTokenBuf = calloc(1, 16*1024); // used for deleting Escape character: \\, \', \"
|
||||
if (NULL == tmpTokenBuf) {
|
||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
int32_t numOfRows = tsParseValues(str, dataBuf, pTableMeta, maxNumOfRows, spd, pCmd, &code, tmpTokenBuf);
|
||||
int32_t numOfRows = 0;
|
||||
code = tsParseValues(str, dataBuf, maxNumOfRows, pCmd, &numOfRows, tmpTokenBuf);
|
||||
|
||||
free(tmpTokenBuf);
|
||||
if (numOfRows <= 0) {
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
||||
|
@ -736,20 +727,18 @@ static int32_t doParseInsertStatement(SSqlCmd* pCmd, char **str, SParsedDataColI
|
|||
}
|
||||
|
||||
SSubmitBlk *pBlocks = (SSubmitBlk *)(dataBuf->pData);
|
||||
code = tsSetBlockInfo(pBlocks, pTableMeta, numOfRows);
|
||||
code = tsSetBlockInfo(pBlocks, dataBuf->pTableMeta, numOfRows);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
tscInvalidSQLErrMsg(pCmd->payload, "too many rows in sql, total number of rows should be less than 32767", *str);
|
||||
return code;
|
||||
}
|
||||
|
||||
dataBuf->vgId = pTableMeta->vgId;
|
||||
dataBuf->numOfTables = 1;
|
||||
|
||||
*totalNum += numOfRows;
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
||||
static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql, char** boundColumn) {
|
||||
int32_t index = 0;
|
||||
SStrToken sToken = {0};
|
||||
SStrToken tableToken = {0};
|
||||
|
@ -767,38 +756,37 @@ static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
|||
|
||||
// get the token of specified table
|
||||
index = 0;
|
||||
tableToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
tableToken = tStrGetToken(sql, &index, false);
|
||||
sql += index;
|
||||
|
||||
char *cstart = NULL;
|
||||
char *cend = NULL;
|
||||
|
||||
// skip possibly exists column list
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(sql, &index, false);
|
||||
sql += index;
|
||||
|
||||
int32_t numOfColList = 0;
|
||||
bool createTable = false;
|
||||
|
||||
// Bind table columns list in string, skip it and continue
|
||||
if (sToken.type == TK_LP) {
|
||||
cstart = &sToken.z[0];
|
||||
index = 0;
|
||||
*boundColumn = &sToken.z[0];
|
||||
|
||||
while (1) {
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false);
|
||||
|
||||
if (sToken.type == TK_RP) {
|
||||
cend = &sToken.z[0];
|
||||
break;
|
||||
}
|
||||
|
||||
sql += index;
|
||||
++numOfColList;
|
||||
}
|
||||
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(sql, &index, false);
|
||||
sql += index;
|
||||
}
|
||||
|
||||
if (numOfColList == 0 && cstart != NULL) {
|
||||
if (numOfColList == 0 && (*boundColumn) != NULL) {
|
||||
return TSDB_CODE_TSC_INVALID_SQL;
|
||||
}
|
||||
|
||||
|
@ -806,7 +794,7 @@ static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
|||
|
||||
if (sToken.type == TK_USING) { // create table if not exists according to the super table
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(sql, &index, false);
|
||||
sql += index;
|
||||
|
||||
//the source super table is moved to the secondary position of the pTableMetaInfo list
|
||||
|
@ -835,82 +823,42 @@ static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
|||
SSchema *pTagSchema = tscGetTableTagSchema(pSTableMetaInfo->pTableMeta);
|
||||
STableComInfo tinfo = tscGetTableInfo(pSTableMetaInfo->pTableMeta);
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
sql += index;
|
||||
|
||||
SParsedDataColInfo spd = {0};
|
||||
|
||||
uint8_t numOfTags = tscGetNumOfTags(pSTableMetaInfo->pTableMeta);
|
||||
spd.numOfCols = numOfTags;
|
||||
|
||||
// if specify some tags column
|
||||
if (sToken.type != TK_LP) {
|
||||
tscSetAssignedColumnInfo(&spd, pTagSchema, numOfTags);
|
||||
} else {
|
||||
/* insert into tablename (col1, col2,..., coln) using superTableName (tagName1, tagName2, ..., tagNamen)
|
||||
* tags(tagVal1, tagVal2, ..., tagValn) values(v1, v2,... vn); */
|
||||
int16_t offset[TSDB_MAX_COLUMNS] = {0};
|
||||
for (int32_t t = 1; t < numOfTags; ++t) {
|
||||
offset[t] = offset[t - 1] + pTagSchema[t - 1].bytes;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
sql += index;
|
||||
|
||||
if (TK_STRING == sToken.type) {
|
||||
strdequote(sToken.z);
|
||||
sToken.n = (uint32_t)strtrim(sToken.z);
|
||||
}
|
||||
|
||||
if (sToken.type == TK_RP) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool findColumnIndex = false;
|
||||
|
||||
// todo speedup by using hash list
|
||||
for (int32_t t = 0; t < numOfTags; ++t) {
|
||||
if (strncmp(sToken.z, pTagSchema[t].name, sToken.n) == 0 && strlen(pTagSchema[t].name) == sToken.n) {
|
||||
SParsedColElem *pElem = &spd.elems[spd.numOfAssignedCols++];
|
||||
pElem->offset = offset[t];
|
||||
pElem->colIndex = t;
|
||||
|
||||
if (spd.hasVal[t] == true) {
|
||||
return tscInvalidSQLErrMsg(pCmd->payload, "duplicated tag name", sToken.z);
|
||||
}
|
||||
|
||||
spd.hasVal[t] = true;
|
||||
findColumnIndex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!findColumnIndex) {
|
||||
return tscInvalidSQLErrMsg(pCmd->payload, "invalid tag name", sToken.z);
|
||||
}
|
||||
}
|
||||
|
||||
if (spd.numOfAssignedCols == 0 || spd.numOfAssignedCols > numOfTags) {
|
||||
return tscInvalidSQLErrMsg(pCmd->payload, "tag name expected", sToken.z);
|
||||
}
|
||||
tscSetBoundColumnInfo(&spd, pTagSchema, tscGetNumOfTags(pSTableMetaInfo->pTableMeta));
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
sql += index;
|
||||
}
|
||||
|
||||
if (sToken.type != TK_TAGS) {
|
||||
sToken = tStrGetToken(sql, &index, false);
|
||||
if (sToken.type != TK_TAGS && sToken.type != TK_LP) {
|
||||
return tscInvalidSQLErrMsg(pCmd->payload, "keyword TAGS expected", sToken.z);
|
||||
}
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
// parse the bound tags column
|
||||
if (sToken.type == TK_LP) {
|
||||
/*
|
||||
* insert into tablename (col1, col2,..., coln) using superTableName (tagName1, tagName2, ..., tagNamen)
|
||||
* tags(tagVal1, tagVal2, ..., tagValn) values(v1, v2,... vn);
|
||||
*/
|
||||
char* end = NULL;
|
||||
code = parseBoundColumns(pCmd, &spd, pTagSchema, sql, &end);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
||||
sql = end;
|
||||
|
||||
index = 0; // keywords of "TAGS"
|
||||
sToken = tStrGetToken(sql, &index, false);
|
||||
sql += index;
|
||||
} else {
|
||||
sql += index;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false);
|
||||
sql += index;
|
||||
|
||||
if (sToken.type != TK_LP) {
|
||||
return tscInvalidSQLErrMsg(pCmd->payload, NULL, sToken.z);
|
||||
return tscInvalidSQLErrMsg(pCmd->payload, "( is expected", sToken.z);
|
||||
}
|
||||
|
||||
SKVRowBuilder kvRowBuilder = {0};
|
||||
|
@ -918,13 +866,11 @@ static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
|||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
uint32_t ignoreTokenTypes = TK_LP;
|
||||
uint32_t numOfIgnoreToken = 1;
|
||||
for (int i = 0; i < spd.numOfAssignedCols; ++i) {
|
||||
SSchema* pSchema = pTagSchema + spd.elems[i].colIndex;
|
||||
for (int i = 0; i < spd.numOfBound; ++i) {
|
||||
SSchema* pSchema = &pTagSchema[spd.boundedColumns[i]];
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, true, numOfIgnoreToken, &ignoreTokenTypes);
|
||||
sToken = tStrGetToken(sql, &index, true);
|
||||
sql += index;
|
||||
|
||||
if (TK_ILLEGAL == sToken.type) {
|
||||
|
@ -943,7 +889,7 @@ static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
|||
}
|
||||
|
||||
char tagVal[TSDB_MAX_TAGS_LEN];
|
||||
code = tsParseOneColumnData(pSchema, &sToken, tagVal, pCmd->payload, &sql, false, tinfo.precision);
|
||||
code = tsParseOneColumn(pSchema, &sToken, tagVal, pCmd->payload, &sql, false, tinfo.precision);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
tdDestroyKVRowBuilder(&kvRowBuilder);
|
||||
return code;
|
||||
|
@ -952,6 +898,8 @@ static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
|||
tdAddColToKVRow(&kvRowBuilder, pSchema->colId, pSchema->type, tagVal);
|
||||
}
|
||||
|
||||
tscDestroyBoundColumnInfo(&spd);
|
||||
|
||||
SKVRow row = tdGetKVRowFromBuilder(&kvRowBuilder);
|
||||
tdDestroyKVRowBuilder(&kvRowBuilder);
|
||||
if (row == NULL) {
|
||||
|
@ -974,7 +922,7 @@ static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
|||
pCmd->tagData.data = pTag;
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(sql, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(sql, &index, false);
|
||||
sql += index;
|
||||
if (sToken.n == 0 || sToken.type != TK_RP) {
|
||||
return tscSQLSyntaxErrMsg(pCmd->payload, ") expected", sToken.z);
|
||||
|
@ -989,33 +937,21 @@ static int32_t tscCheckIfCreateTable(char **sqlstr, SSqlObj *pSql) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
createTable = true;
|
||||
code = tscGetTableMetaEx(pSql, pTableMetaInfo, true);
|
||||
if (TSDB_CODE_TSC_ACTION_IN_PROGRESS == code) {
|
||||
return code;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (cstart != NULL) {
|
||||
sql = cstart;
|
||||
} else {
|
||||
sql = sToken.z;
|
||||
}
|
||||
code = tscGetTableMetaEx(pSql, pTableMetaInfo, false);
|
||||
|
||||
code = tscGetTableMetaEx(pSql, pTableMetaInfo, false);
|
||||
if (pCmd->curSql == NULL) {
|
||||
assert(code == TSDB_CODE_TSC_ACTION_IN_PROGRESS);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t len = (int32_t)(cend - cstart + 1);
|
||||
if (cstart != NULL && createTable == true) {
|
||||
/* move the column list to start position of the next accessed points */
|
||||
memmove(sql - len, cstart, len);
|
||||
*sqlstr = sql - len;
|
||||
} else {
|
||||
*sqlstr = sql;
|
||||
}
|
||||
|
||||
if (*sqlstr == NULL) {
|
||||
code = TSDB_CODE_TSC_INVALID_SQL;
|
||||
|
@ -1043,6 +979,76 @@ static int32_t validateDataSource(SSqlCmd *pCmd, int8_t type, const char *sql) {
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t parseBoundColumns(SSqlCmd* pCmd, SParsedDataColInfo* pColInfo, SSchema* pSchema,
|
||||
char* str, char **end) {
|
||||
pColInfo->numOfBound = 0;
|
||||
|
||||
memset(pColInfo->boundedColumns, 0, sizeof(int32_t) * pColInfo->numOfCols);
|
||||
for(int32_t i = 0; i < pColInfo->numOfCols; ++i) {
|
||||
pColInfo->cols[i].hasVal = false;
|
||||
}
|
||||
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
|
||||
int32_t index = 0;
|
||||
SStrToken sToken = tStrGetToken(str, &index, false);
|
||||
str += index;
|
||||
|
||||
if (sToken.type != TK_LP) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "( is expected", sToken.z);
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
index = 0;
|
||||
sToken = tStrGetToken(str, &index, false);
|
||||
str += index;
|
||||
|
||||
if (TK_STRING == sToken.type) {
|
||||
tscDequoteAndTrimToken(&sToken);
|
||||
}
|
||||
|
||||
if (sToken.type == TK_RP) {
|
||||
if (end != NULL) { // set the end position
|
||||
*end = str;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
bool findColumnIndex = false;
|
||||
|
||||
// todo speedup by using hash list
|
||||
for (int32_t t = 0; t < pColInfo->numOfCols; ++t) {
|
||||
if (strncmp(sToken.z, pSchema[t].name, sToken.n) == 0 && strlen(pSchema[t].name) == sToken.n) {
|
||||
if (pColInfo->cols[t].hasVal == true) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "duplicated column name", sToken.z);
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
pColInfo->cols[t].hasVal = true;
|
||||
pColInfo->boundedColumns[pColInfo->numOfBound] = t;
|
||||
pColInfo->numOfBound += 1;
|
||||
findColumnIndex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!findColumnIndex) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "invalid column/tag name", sToken.z);
|
||||
goto _clean;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&pColInfo->boundedColumns[pColInfo->numOfBound], 0 , sizeof(int32_t) * (pColInfo->numOfCols - pColInfo->numOfBound));
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
||||
_clean:
|
||||
pCmd->curSql = NULL;
|
||||
pCmd->parseFinished = 1;
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse insert sql
|
||||
* @param pSql
|
||||
|
@ -1083,7 +1089,7 @@ int tsParseInsertSql(SSqlObj *pSql) {
|
|||
|
||||
while (1) {
|
||||
int32_t index = 0;
|
||||
SStrToken sToken = tStrGetToken(str, &index, false, 0, NULL);
|
||||
SStrToken sToken = tStrGetToken(str, &index, false);
|
||||
|
||||
// no data in the sql string anymore.
|
||||
if (sToken.n == 0) {
|
||||
|
@ -1121,7 +1127,8 @@ int tsParseInsertSql(SSqlObj *pSql) {
|
|||
goto _clean;
|
||||
}
|
||||
|
||||
if ((code = tscCheckIfCreateTable(&str, pSql)) != TSDB_CODE_SUCCESS) {
|
||||
char *bindedColumns = NULL;
|
||||
if ((code = tscCheckIfCreateTable(&str, pSql, &bindedColumns)) != TSDB_CODE_SUCCESS) {
|
||||
/*
|
||||
* After retrieving the table meta from server, the sql string will be parsed from the paused position.
|
||||
* And during the getTableMetaCallback function, the sql string will be parsed from the paused position.
|
||||
|
@ -1141,41 +1148,22 @@ int tsParseInsertSql(SSqlObj *pSql) {
|
|||
}
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(str, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(str, &index, false);
|
||||
str += index;
|
||||
|
||||
if (sToken.n == 0) {
|
||||
if (sToken.n == 0 || (sToken.type != TK_FILE && sToken.type != TK_VALUES)) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "keyword VALUES or FILE required", sToken.z);
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
STableComInfo tinfo = tscGetTableInfo(pTableMetaInfo->pTableMeta);
|
||||
|
||||
if (sToken.type == TK_VALUES) {
|
||||
SParsedDataColInfo spd = {.numOfCols = tinfo.numOfColumns};
|
||||
|
||||
SSchema *pSchema = tscGetTableSchema(pTableMetaInfo->pTableMeta);
|
||||
tscSetAssignedColumnInfo(&spd, pSchema, tinfo.numOfColumns);
|
||||
|
||||
if (validateDataSource(pCmd, DATA_FROM_SQL_STRING, sToken.z) != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
/*
|
||||
* app here insert data in different vnodes, so we need to set the following
|
||||
* data in another submit procedure using async insert routines
|
||||
*/
|
||||
code = doParseInsertStatement(pCmd, &str, &spd, &totalNum);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
} else if (sToken.type == TK_FILE) {
|
||||
if (sToken.type == TK_FILE) {
|
||||
if (validateDataSource(pCmd, DATA_FROM_DATA_FILE, sToken.z) != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(str, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(str, &index, false);
|
||||
if (sToken.type != TK_STRING && sToken.type != TK_ID) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "file path is required following keyword FILE", sToken.z);
|
||||
goto _clean;
|
||||
|
@ -1199,83 +1187,63 @@ int tsParseInsertSql(SSqlObj *pSql) {
|
|||
tstrncpy(pCmd->payload, full_path.we_wordv[0], pCmd->allocSize);
|
||||
wordfree(&full_path);
|
||||
|
||||
} else if (sToken.type == TK_LP) {
|
||||
/* insert into tablename(col1, col2,..., coln) values(v1, v2,... vn); */
|
||||
STableMeta *pTableMeta = tscGetTableMetaInfoFromCmd(pCmd, pCmd->clauseIndex, 0)->pTableMeta;
|
||||
SSchema * pSchema = tscGetTableSchema(pTableMeta);
|
||||
} else {
|
||||
if (bindedColumns == NULL) {
|
||||
STableMeta *pTableMeta = pTableMetaInfo->pTableMeta;
|
||||
|
||||
if (validateDataSource(pCmd, DATA_FROM_SQL_STRING, sToken.z) != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
SParsedDataColInfo spd = {0};
|
||||
spd.numOfCols = tinfo.numOfColumns;
|
||||
|
||||
int16_t offset[TSDB_MAX_COLUMNS] = {0};
|
||||
for (int32_t t = 1; t < tinfo.numOfColumns; ++t) {
|
||||
offset[t] = offset[t - 1] + pSchema[t - 1].bytes;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
index = 0;
|
||||
sToken = tStrGetToken(str, &index, false, 0, NULL);
|
||||
str += index;
|
||||
|
||||
if (TK_STRING == sToken.type) {
|
||||
tscDequoteAndTrimToken(&sToken);
|
||||
}
|
||||
|
||||
if (sToken.type == TK_RP) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool findColumnIndex = false;
|
||||
|
||||
// todo speedup by using hash list
|
||||
for (int32_t t = 0; t < tinfo.numOfColumns; ++t) {
|
||||
if (strncmp(sToken.z, pSchema[t].name, sToken.n) == 0 && strlen(pSchema[t].name) == sToken.n) {
|
||||
SParsedColElem *pElem = &spd.elems[spd.numOfAssignedCols++];
|
||||
pElem->offset = offset[t];
|
||||
pElem->colIndex = t;
|
||||
|
||||
if (spd.hasVal[t] == true) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "duplicated column name", sToken.z);
|
||||
STableDataBlocks *dataBuf = NULL;
|
||||
int32_t ret = tscGetDataBlockFromList(pCmd->pTableBlockHashList, pTableMeta->id.uid, TSDB_DEFAULT_PAYLOAD_SIZE,
|
||||
sizeof(SSubmitBlk), tinfo.rowSize, &pTableMetaInfo->name, pTableMeta,
|
||||
&dataBuf, NULL);
|
||||
if (ret != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
spd.hasVal[t] = true;
|
||||
findColumnIndex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!findColumnIndex) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "invalid column name", sToken.z);
|
||||
code = doParseInsertStatement(pCmd, &str, dataBuf, &totalNum);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
}
|
||||
} else { // bindedColumns != NULL
|
||||
// insert into tablename(col1, col2,..., coln) values(v1, v2,... vn);
|
||||
STableMeta *pTableMeta = tscGetTableMetaInfoFromCmd(pCmd, pCmd->clauseIndex, 0)->pTableMeta;
|
||||
|
||||
if (spd.numOfAssignedCols == 0 || spd.numOfAssignedCols > tinfo.numOfColumns) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "column name expected", sToken.z);
|
||||
if (validateDataSource(pCmd, DATA_FROM_SQL_STRING, sToken.z) != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
sToken = tStrGetToken(str, &index, false, 0, NULL);
|
||||
str += index;
|
||||
STableDataBlocks *dataBuf = NULL;
|
||||
int32_t ret = tscGetDataBlockFromList(pCmd->pTableBlockHashList, pTableMeta->id.uid, TSDB_DEFAULT_PAYLOAD_SIZE,
|
||||
sizeof(SSubmitBlk), tinfo.rowSize, &pTableMetaInfo->name, pTableMeta,
|
||||
&dataBuf, NULL);
|
||||
if (ret != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
SSchema *pSchema = tscGetTableSchema(pTableMeta);
|
||||
code = parseBoundColumns(pCmd, &dataBuf->boundColumnInfo, pSchema, bindedColumns, NULL);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
if (dataBuf->boundColumnInfo.cols[0].hasVal == false) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "primary timestamp column can not be null", NULL);
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
if (sToken.type != TK_VALUES) {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "keyword VALUES is expected", sToken.z);
|
||||
goto _clean;
|
||||
}
|
||||
|
||||
code = doParseInsertStatement(pCmd, &str, &spd, &totalNum);
|
||||
code = doParseInsertStatement(pCmd, &str, dataBuf, &totalNum);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
goto _clean;
|
||||
}
|
||||
} else {
|
||||
code = tscInvalidSQLErrMsg(pCmd->payload, "keyword VALUES or FILE are required", sToken.z);
|
||||
goto _clean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1307,7 +1275,7 @@ int tsInsertInitialCheck(SSqlObj *pSql) {
|
|||
int32_t index = 0;
|
||||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
|
||||
SStrToken sToken = tStrGetToken(pSql->sqlstr, &index, false, 0, NULL);
|
||||
SStrToken sToken = tStrGetToken(pSql->sqlstr, &index, false);
|
||||
assert(sToken.type == TK_INSERT || sToken.type == TK_IMPORT);
|
||||
|
||||
pCmd->count = 0;
|
||||
|
@ -1317,7 +1285,7 @@ int tsInsertInitialCheck(SSqlObj *pSql) {
|
|||
|
||||
TSDB_QUERY_SET_TYPE(pQueryInfo->type, TSDB_QUERY_TYPE_INSERT | pCmd->insertType);
|
||||
|
||||
sToken = tStrGetToken(pSql->sqlstr, &index, false, 0, NULL);
|
||||
sToken = tStrGetToken(pSql->sqlstr, &index, false);
|
||||
if (sToken.type != TK_INTO) {
|
||||
return tscInvalidSQLErrMsg(pCmd->payload, "keyword INTO is expected", sToken.z);
|
||||
}
|
||||
|
@ -1450,13 +1418,10 @@ static void parseFileSendDataBlock(void *param, TAOS_RES *tres, int32_t numOfRow
|
|||
|
||||
STableMetaInfo *pTableMetaInfo = tscGetTableMetaInfoFromCmd(pCmd, pCmd->clauseIndex, 0);
|
||||
STableMeta * pTableMeta = pTableMetaInfo->pTableMeta;
|
||||
SSchema * pSchema = tscGetTableSchema(pTableMeta);
|
||||
STableComInfo tinfo = tscGetTableInfo(pTableMeta);
|
||||
|
||||
SParsedDataColInfo spd = {.numOfCols = tinfo.numOfColumns};
|
||||
tscSetAssignedColumnInfo(&spd, pSchema, tinfo.numOfColumns);
|
||||
destroyTableNameList(pCmd);
|
||||
|
||||
tfree(pCmd->pTableNameList);
|
||||
pCmd->pDataBlocks = tscDestroyBlockArrayList(pCmd->pDataBlocks);
|
||||
|
||||
if (pCmd->pTableBlockHashList == NULL) {
|
||||
|
@ -1495,8 +1460,9 @@ static void parseFileSendDataBlock(void *param, TAOS_RES *tres, int32_t numOfRow
|
|||
char *lineptr = line;
|
||||
strtolower(line, line);
|
||||
|
||||
int32_t len = tsParseOneRowData(&lineptr, pTableDataBlock, pSchema, &spd, pCmd, tinfo.precision, &code, tokenBuf);
|
||||
if (len <= 0 || pTableDataBlock->numOfParams > 0) {
|
||||
int32_t len = 0;
|
||||
code = tsParseOneRow(&lineptr, pTableDataBlock, pCmd, tinfo.precision, &len, tokenBuf);
|
||||
if (code != TSDB_CODE_SUCCESS || pTableDataBlock->numOfParams > 0) {
|
||||
pSql->res.code = code;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -261,7 +261,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) {
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
if (1) {
|
||||
if (0) {
|
||||
// allow user bind param data with different type
|
||||
union {
|
||||
int8_t v1;
|
||||
|
@ -903,7 +903,7 @@ int taos_stmt_prepare(TAOS_STMT* stmt, const char* sql, unsigned long length) {
|
|||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
pRes->qhandle = 0;
|
||||
pRes->qId = 0;
|
||||
pRes->numOfRows = 1;
|
||||
|
||||
strtolower(pSql->sqlstr, sql);
|
||||
|
@ -1057,14 +1057,28 @@ int taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes) {
|
|||
}
|
||||
|
||||
if (pStmt->isInsert) {
|
||||
SSqlObj* pSql = pStmt->pSql;
|
||||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
STableDataBlocks* pBlock = taosArrayGetP(pCmd->pDataBlocks, 0);
|
||||
SSqlCmd* pCmd = &pStmt->pSql->cmd;
|
||||
STableMetaInfo* pTableMetaInfo = tscGetTableMetaInfoFromCmd(pCmd, 0, 0);
|
||||
STableMeta* pTableMeta = pTableMetaInfo->pTableMeta;
|
||||
if (pCmd->pTableBlockHashList == NULL) {
|
||||
pCmd->pTableBlockHashList = taosHashInit(16, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), true, false);
|
||||
}
|
||||
|
||||
assert(pCmd->numOfParams == pBlock->numOfParams);
|
||||
if (idx < 0 || idx >= pBlock->numOfParams) return -1;
|
||||
STableDataBlocks* pBlock = NULL;
|
||||
|
||||
SParamInfo* param = pBlock->params + idx;
|
||||
int32_t ret =
|
||||
tscGetDataBlockFromList(pCmd->pTableBlockHashList, pTableMeta->id.uid, TSDB_PAYLOAD_SIZE, sizeof(SSubmitBlk),
|
||||
pTableMeta->tableInfo.rowSize, &pTableMetaInfo->name, pTableMeta, &pBlock, NULL);
|
||||
if (ret != 0) {
|
||||
// todo handle error
|
||||
}
|
||||
|
||||
if (idx<0 || idx>=pBlock->numOfParams) {
|
||||
tscError("param %d: out of range", idx);
|
||||
abort();
|
||||
}
|
||||
|
||||
SParamInfo* param = &pBlock->params[idx];
|
||||
if (type) *type = param->type;
|
||||
if (bytes) *bytes = param->bytes;
|
||||
|
||||
|
|
|
@ -249,8 +249,8 @@ int tscBuildQueryStreamDesc(void *pMsg, STscObj *pObj) {
|
|||
pQdesc->stime = htobe64(pSql->stime);
|
||||
pQdesc->queryId = htonl(pSql->queryId);
|
||||
//pQdesc->useconds = htobe64(pSql->res.useconds);
|
||||
pQdesc->useconds = htobe64(now - pSql->stime);
|
||||
pQdesc->qHandle = htobe64(pSql->res.qhandle);
|
||||
pQdesc->useconds = htobe64(now - pSql->stime); // use local time instead of sever rsp elapsed time
|
||||
pQdesc->qHandle = htobe64(pSql->res.qId);
|
||||
|
||||
pHeartbeat->numOfQueries++;
|
||||
pQdesc++;
|
||||
|
|
|
@ -670,7 +670,18 @@ int32_t tscToSQLCmd(SSqlObj* pSql, struct SSqlInfo* pInfo) {
|
|||
if ((code = setKillInfo(pSql, pInfo, pInfo->type)) != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TSDB_SQL_SYNC_DB_REPLICA: {
|
||||
const char* msg1 = "invalid db name";
|
||||
SStrToken* pzName = taosArrayGet(pInfo->pMiscInfo->a, 0);
|
||||
|
||||
assert(taosArrayGetSize(pInfo->pMiscInfo->a) == 1);
|
||||
code = tNameSetDbName(&pTableMetaInfo->name, getAccountId(pSql), pzName);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -966,12 +977,18 @@ int32_t parseSlidingClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SStrToken* pSl
|
|||
int32_t tscSetTableFullName(STableMetaInfo* pTableMetaInfo, SStrToken* pTableName, SSqlObj* pSql) {
|
||||
const char* msg1 = "name too long";
|
||||
const char* msg2 = "acctId too long";
|
||||
const char* msg3 = "no acctId";
|
||||
|
||||
SSqlCmd* pCmd = &pSql->cmd;
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
|
||||
if (hasSpecifyDB(pTableName)) { // db has been specified in sql string so we ignore current db path
|
||||
code = tNameSetAcctId(&pTableMetaInfo->name, getAccountId(pSql));
|
||||
char* acctId = getAccountId(pSql);
|
||||
if (acctId == NULL || strlen(acctId) <= 0) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg3);
|
||||
}
|
||||
|
||||
code = tNameSetAcctId(&pTableMetaInfo->name, acctId);
|
||||
if (code != 0) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
|
||||
}
|
||||
|
@ -3276,7 +3293,8 @@ static int32_t extractColumnFilterInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SC
|
|||
}
|
||||
|
||||
if (pSchema->type == TSDB_DATA_TYPE_BOOL) {
|
||||
if (pExpr->tokenId != TK_EQ && pExpr->tokenId != TK_NE) {
|
||||
int32_t t = pExpr->tokenId;
|
||||
if (t != TK_EQ && t != TK_NE && t != TK_NOTNULL && t != TK_ISNULL) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg3);
|
||||
}
|
||||
}
|
||||
|
@ -3342,23 +3360,25 @@ static int32_t getColumnQueryCondInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSq
|
|||
}
|
||||
}
|
||||
|
||||
static int32_t getJoinCondInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSqlExpr* pExpr) {
|
||||
const char* msg1 = "invalid join query condition";
|
||||
const char* msg2 = "invalid table name in join query";
|
||||
static int32_t checkAndSetJoinCondInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSqlExpr* pExpr) {
|
||||
int32_t code = 0;
|
||||
const char* msg1 = "timestamp required for join tables";
|
||||
const char* msg3 = "type of join columns must be identical";
|
||||
const char* msg4 = "invalid column name in join condition";
|
||||
const char* msg5 = "only support one join tag for each table";
|
||||
|
||||
if (pExpr == NULL) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
if (!tSqlExprIsParentOfLeaf(pExpr)) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
|
||||
code = checkAndSetJoinCondInfo(pCmd, pQueryInfo, pExpr->pLeft);
|
||||
if (code) {
|
||||
return code;
|
||||
}
|
||||
|
||||
STagCond* pTagCond = &pQueryInfo->tagCond;
|
||||
SJoinNode* pLeft = &pTagCond->joinInfo.left;
|
||||
SJoinNode* pRight = &pTagCond->joinInfo.right;
|
||||
return checkAndSetJoinCondInfo(pCmd, pQueryInfo, pExpr->pRight);
|
||||
}
|
||||
|
||||
SColumnIndex index = COLUMN_INDEX_INITIALIZER;
|
||||
if (getColumnIndexByName(pCmd, &pExpr->pLeft->colInfo, pQueryInfo, &index) != TSDB_CODE_SUCCESS) {
|
||||
|
@ -3368,14 +3388,29 @@ static int32_t getJoinCondInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSqlExpr*
|
|||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, index.tableIndex);
|
||||
SSchema* pTagSchema1 = tscGetTableColumnSchema(pTableMetaInfo->pTableMeta, index.columnIndex);
|
||||
|
||||
pLeft->uid = pTableMetaInfo->pTableMeta->id.uid;
|
||||
pLeft->tagColId = pTagSchema1->colId;
|
||||
assert(index.tableIndex >= 0 && index.tableIndex < TSDB_MAX_JOIN_TABLE_NUM);
|
||||
|
||||
int32_t code = tNameExtractFullName(&pTableMetaInfo->name, pLeft->tableName);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
|
||||
SJoinNode **leftNode = &pQueryInfo->tagCond.joinInfo.joinTables[index.tableIndex];
|
||||
if (*leftNode == NULL) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
|
||||
}
|
||||
|
||||
(*leftNode)->uid = pTableMetaInfo->pTableMeta->id.uid;
|
||||
(*leftNode)->tagColId = pTagSchema1->colId;
|
||||
|
||||
if (UTIL_TABLE_IS_SUPER_TABLE(pTableMetaInfo)) {
|
||||
index.columnIndex = index.columnIndex - tscGetNumOfColumns(pTableMetaInfo->pTableMeta);
|
||||
if (!tscColumnExists(pTableMetaInfo->tagColList, &index)) {
|
||||
tscColumnListInsert(pTableMetaInfo->tagColList, &index);
|
||||
if (taosArrayGetSize(pTableMetaInfo->tagColList) > 1) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t leftIdx = index.tableIndex;
|
||||
|
||||
|
||||
index = (SColumnIndex)COLUMN_INDEX_INITIALIZER;
|
||||
if (getColumnIndexByName(pCmd, &pExpr->pRight->colInfo, pQueryInfo, &index) != TSDB_CODE_SUCCESS) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg4);
|
||||
|
@ -3384,20 +3419,55 @@ static int32_t getJoinCondInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSqlExpr*
|
|||
pTableMetaInfo = tscGetMetaInfo(pQueryInfo, index.tableIndex);
|
||||
SSchema* pTagSchema2 = tscGetTableColumnSchema(pTableMetaInfo->pTableMeta, index.columnIndex);
|
||||
|
||||
pRight->uid = pTableMetaInfo->pTableMeta->id.uid;
|
||||
pRight->tagColId = pTagSchema2->colId;
|
||||
assert(index.tableIndex >= 0 && index.tableIndex < TSDB_MAX_JOIN_TABLE_NUM);
|
||||
|
||||
code = tNameExtractFullName(&pTableMetaInfo->name, pRight->tableName);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
|
||||
SJoinNode **rightNode = &pQueryInfo->tagCond.joinInfo.joinTables[index.tableIndex];
|
||||
if (*rightNode == NULL) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
|
||||
}
|
||||
|
||||
(*rightNode)->uid = pTableMetaInfo->pTableMeta->id.uid;
|
||||
(*rightNode)->tagColId = pTagSchema2->colId;
|
||||
|
||||
if (UTIL_TABLE_IS_SUPER_TABLE(pTableMetaInfo)) {
|
||||
index.columnIndex = index.columnIndex - tscGetNumOfColumns(pTableMetaInfo->pTableMeta);
|
||||
if (!tscColumnExists(pTableMetaInfo->tagColList, &index)) {
|
||||
tscColumnListInsert(pTableMetaInfo->tagColList, &index);
|
||||
if (taosArrayGetSize(pTableMetaInfo->tagColList) > 1) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t rightIdx = index.tableIndex;
|
||||
|
||||
if (pTagSchema1->type != pTagSchema2->type) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg3);
|
||||
}
|
||||
|
||||
pTagCond->joinInfo.hasJoin = true;
|
||||
if ((*leftNode)->tagJoin == NULL) {
|
||||
(*leftNode)->tagJoin = taosArrayInit(2, sizeof(int16_t));
|
||||
}
|
||||
|
||||
if ((*rightNode)->tagJoin == NULL) {
|
||||
(*rightNode)->tagJoin = taosArrayInit(2, sizeof(int16_t));
|
||||
}
|
||||
|
||||
taosArrayPush((*leftNode)->tagJoin, &rightIdx);
|
||||
taosArrayPush((*rightNode)->tagJoin, &leftIdx);
|
||||
|
||||
pQueryInfo->tagCond.joinInfo.hasJoin = true;
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
static int32_t getJoinCondInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSqlExpr* pExpr) {
|
||||
if (pExpr == NULL) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
return checkAndSetJoinCondInfo(pCmd, pQueryInfo, pExpr);
|
||||
}
|
||||
|
||||
static int32_t validateSQLExpr(SSqlCmd* pCmd, tSqlExpr* pExpr, SQueryInfo* pQueryInfo, SColumnList* pList,
|
||||
|
@ -3424,7 +3494,8 @@ static int32_t validateSQLExpr(SSqlCmd* pCmd, tSqlExpr* pExpr, SQueryInfo* pQuer
|
|||
}
|
||||
|
||||
pList->ids[pList->num++] = index;
|
||||
} else if (pExpr->tokenId == TK_FLOAT && (isnan(pExpr->value.dKey) || isinf(pExpr->value.dKey))) {
|
||||
} else if ((pExpr->tokenId == TK_FLOAT && (isnan(pExpr->value.dKey) || isinf(pExpr->value.dKey))) ||
|
||||
pExpr->tokenId == TK_NULL) {
|
||||
return TSDB_CODE_TSC_INVALID_SQL;
|
||||
} else if (pExpr->type == SQL_NODE_SQLFUNCTION) {
|
||||
if (*type == NON_ARITHMEIC_EXPR) {
|
||||
|
@ -3658,16 +3729,48 @@ static int32_t setExprToCond(tSqlExpr** parent, tSqlExpr* pExpr, const char* msg
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t validateNullExpr(tSqlExpr* pExpr, char* msgBuf) {
|
||||
const char* msg = "only support is [not] null";
|
||||
|
||||
tSqlExpr* pRight = pExpr->pRight;
|
||||
if (pRight->tokenId == TK_NULL && (!(pExpr->tokenId == TK_ISNULL || pExpr->tokenId == TK_NOTNULL))) {
|
||||
return invalidSqlErrMsg(msgBuf, msg);
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
// check for like expression
|
||||
static int32_t validateLikeExpr(tSqlExpr* pExpr, STableMeta* pTableMeta, int32_t index, char* msgBuf) {
|
||||
const char* msg1 = "wildcard string should be less than 20 characters";
|
||||
const char* msg2 = "illegal column name";
|
||||
|
||||
tSqlExpr* pLeft = pExpr->pLeft;
|
||||
tSqlExpr* pRight = pExpr->pRight;
|
||||
|
||||
if (pExpr->tokenId == TK_LIKE) {
|
||||
if (pRight->value.nLen > TSDB_PATTERN_STRING_MAX_LEN) {
|
||||
return invalidSqlErrMsg(msgBuf, msg1);
|
||||
}
|
||||
|
||||
SSchema* pSchema = tscGetTableSchema(pTableMeta);
|
||||
if ((!isTablenameToken(&pLeft->colInfo)) && !IS_VAR_DATA_TYPE(pSchema[index].type)) {
|
||||
return invalidSqlErrMsg(msgBuf, msg2);
|
||||
}
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t handleExprInQueryCond(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSqlExpr** pExpr, SCondExpr* pCondExpr,
|
||||
int32_t* type, int32_t parentOptr) {
|
||||
const char* msg1 = "table query cannot use tags filter";
|
||||
const char* msg2 = "illegal column name";
|
||||
const char* msg3 = "only one query time range allowed";
|
||||
const char* msg4 = "only one join condition allowed";
|
||||
const char* msg4 = "too many join tables";
|
||||
const char* msg5 = "not support ordinary column join";
|
||||
const char* msg6 = "only one query condition on tbname allowed";
|
||||
const char* msg7 = "only in/like allowed in filter table name";
|
||||
const char* msg8 = "wildcard string should be less than 20 characters";
|
||||
|
||||
tSqlExpr* pLeft = (*pExpr)->pLeft;
|
||||
tSqlExpr* pRight = (*pExpr)->pRight;
|
||||
|
@ -3684,6 +3787,18 @@ static int32_t handleExprInQueryCond(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSql
|
|||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, index.tableIndex);
|
||||
STableMeta* pTableMeta = pTableMetaInfo->pTableMeta;
|
||||
|
||||
// validate the null expression
|
||||
int32_t code = validateNullExpr(*pExpr, tscGetErrorMsgPayload(pCmd));
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
||||
// validate the like expression
|
||||
code = validateLikeExpr(*pExpr, pTableMeta, index.columnIndex, tscGetErrorMsgPayload(pCmd));
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
||||
if (index.columnIndex == PRIMARYKEY_TIMESTAMP_COL_INDEX) { // query on time range
|
||||
if (!validateJoinExprNode(pCmd, pQueryInfo, *pExpr, &index)) {
|
||||
return TSDB_CODE_TSC_INVALID_SQL;
|
||||
|
@ -3694,6 +3809,46 @@ static int32_t handleExprInQueryCond(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSql
|
|||
TSDB_QUERY_SET_TYPE(pQueryInfo->type, TSDB_QUERY_TYPE_JOIN_QUERY);
|
||||
pCondExpr->tsJoin = true;
|
||||
|
||||
assert(index.tableIndex >= 0 && index.tableIndex < TSDB_MAX_JOIN_TABLE_NUM);
|
||||
SJoinNode **leftNode = &pQueryInfo->tagCond.joinInfo.joinTables[index.tableIndex];
|
||||
if (*leftNode == NULL) {
|
||||
*leftNode = calloc(1, sizeof(SJoinNode));
|
||||
if (*leftNode == NULL) {
|
||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t leftIdx = index.tableIndex;
|
||||
|
||||
if (getColumnIndexByName(pCmd, &pRight->colInfo, pQueryInfo, &index) != TSDB_CODE_SUCCESS) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
|
||||
}
|
||||
|
||||
if (index.tableIndex < 0 || index.tableIndex >= TSDB_MAX_JOIN_TABLE_NUM) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg4);
|
||||
}
|
||||
|
||||
SJoinNode **rightNode = &pQueryInfo->tagCond.joinInfo.joinTables[index.tableIndex];
|
||||
if (*rightNode == NULL) {
|
||||
*rightNode = calloc(1, sizeof(SJoinNode));
|
||||
if (*rightNode == NULL) {
|
||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t rightIdx = index.tableIndex;
|
||||
|
||||
if ((*leftNode)->tsJoin == NULL) {
|
||||
(*leftNode)->tsJoin = taosArrayInit(2, sizeof(int16_t));
|
||||
}
|
||||
|
||||
if ((*rightNode)->tsJoin == NULL) {
|
||||
(*rightNode)->tsJoin = taosArrayInit(2, sizeof(int16_t));
|
||||
}
|
||||
|
||||
taosArrayPush((*leftNode)->tsJoin, &rightIdx);
|
||||
taosArrayPush((*rightNode)->tsJoin, &leftIdx);
|
||||
|
||||
/*
|
||||
* to release expression, e.g., m1.ts = m2.ts,
|
||||
* since this expression is used to set the join query type
|
||||
|
@ -3711,20 +3866,6 @@ static int32_t handleExprInQueryCond(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSql
|
|||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
|
||||
}
|
||||
|
||||
// check for like expression
|
||||
if ((*pExpr)->tokenId == TK_LIKE) {
|
||||
if (pRight->value.nLen > TSDB_PATTERN_STRING_MAX_LEN) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg8);
|
||||
}
|
||||
|
||||
SSchema* pSchema = tscGetTableSchema(pTableMetaInfo->pTableMeta);
|
||||
|
||||
if ((!isTablenameToken(&pLeft->colInfo)) && pSchema[index.columnIndex].type != TSDB_DATA_TYPE_BINARY &&
|
||||
pSchema[index.columnIndex].type != TSDB_DATA_TYPE_NCHAR) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
|
||||
}
|
||||
}
|
||||
|
||||
// in case of in operator, keep it in a seprate attribute
|
||||
if (index.columnIndex == TSDB_TBNAME_COLUMN_INDEX) {
|
||||
if (!validTableNameOptr(*pExpr)) {
|
||||
|
@ -3751,10 +3892,6 @@ static int32_t handleExprInQueryCond(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSql
|
|||
return TSDB_CODE_TSC_INVALID_SQL;
|
||||
}
|
||||
|
||||
if (pCondExpr->pJoinExpr != NULL) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg4);
|
||||
}
|
||||
|
||||
pQueryInfo->type |= TSDB_QUERY_TYPE_JOIN_QUERY;
|
||||
ret = setExprToCond(&pCondExpr->pJoinExpr, *pExpr, NULL, parentOptr, pQueryInfo->msg);
|
||||
*pExpr = NULL;
|
||||
|
@ -3982,6 +4119,7 @@ static bool validateFilterExpr(SQueryInfo* pQueryInfo) {
|
|||
static int32_t getTimeRangeFromExpr(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSqlExpr* pExpr) {
|
||||
const char* msg0 = "invalid timestamp";
|
||||
const char* msg1 = "only one time stamp window allowed";
|
||||
int32_t code = 0;
|
||||
|
||||
if (pExpr == NULL) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -3992,7 +4130,10 @@ static int32_t getTimeRangeFromExpr(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSqlE
|
|||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
|
||||
}
|
||||
|
||||
getTimeRangeFromExpr(pCmd, pQueryInfo, pExpr->pLeft);
|
||||
code = getTimeRangeFromExpr(pCmd, pQueryInfo, pExpr->pLeft);
|
||||
if (code) {
|
||||
return code;
|
||||
}
|
||||
|
||||
return getTimeRangeFromExpr(pCmd, pQueryInfo, pExpr->pRight);
|
||||
} else {
|
||||
|
@ -4074,6 +4215,7 @@ static void cleanQueryExpr(SCondExpr* pCondExpr) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static void doAddJoinTagsColumnsIntoTagList(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SCondExpr* pCondExpr) {
|
||||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||
if (QUERY_IS_JOIN_QUERY(pQueryInfo->type) && UTIL_TABLE_IS_SUPER_TABLE(pTableMetaInfo)) {
|
||||
|
@ -4096,6 +4238,7 @@ static void doAddJoinTagsColumnsIntoTagList(SSqlCmd* pCmd, SQueryInfo* pQueryInf
|
|||
tscColumnListInsert(pTableMetaInfo->tagColList, &index);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static int32_t validateTagCondExpr(SSqlCmd* pCmd, tExprNode *p) {
|
||||
const char *msg1 = "invalid tag operator";
|
||||
|
@ -4239,6 +4382,102 @@ static int32_t getTagQueryCondExpr(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SCondE
|
|||
return ret;
|
||||
}
|
||||
|
||||
int32_t validateJoinNodes(SQueryInfo* pQueryInfo, SSqlObj* pSql) {
|
||||
const char* msg1 = "timestamp required for join tables";
|
||||
const char* msg2 = "tag required for join stables";
|
||||
|
||||
for (int32_t i = 0; i < pQueryInfo->numOfTables; ++i) {
|
||||
SJoinNode *node = pQueryInfo->tagCond.joinInfo.joinTables[i];
|
||||
|
||||
if (node == NULL || node->tsJoin == NULL || taosArrayGetSize(node->tsJoin) <= 0) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(&pSql->cmd), msg1);
|
||||
}
|
||||
}
|
||||
|
||||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||
if (UTIL_TABLE_IS_SUPER_TABLE(pTableMetaInfo)) {
|
||||
for (int32_t i = 0; i < pQueryInfo->numOfTables; ++i) {
|
||||
SJoinNode *node = pQueryInfo->tagCond.joinInfo.joinTables[i];
|
||||
|
||||
if (node == NULL || node->tagJoin == NULL || taosArrayGetSize(node->tagJoin) <= 0) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(&pSql->cmd), msg2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void mergeJoinNodesImpl(int8_t* r, int8_t* p, int16_t* tidx, SJoinNode** nodes, int32_t type) {
|
||||
SJoinNode *node = nodes[*tidx];
|
||||
SArray* arr = (type == 0) ? node->tsJoin : node->tagJoin;
|
||||
size_t size = taosArrayGetSize(arr);
|
||||
|
||||
p[*tidx] = 1;
|
||||
|
||||
for (int32_t j = 0; j < size; j++) {
|
||||
int16_t* idx = taosArrayGet(arr, j);
|
||||
r[*idx] = 1;
|
||||
if (p[*idx] == 0) {
|
||||
mergeJoinNodesImpl(r, p, idx, nodes, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t mergeJoinNodes(SQueryInfo* pQueryInfo, SSqlObj* pSql) {
|
||||
const char* msg1 = "not all join tables have same timestamp";
|
||||
const char* msg2 = "not all join tables have same tag";
|
||||
|
||||
int8_t r[TSDB_MAX_JOIN_TABLE_NUM] = {0};
|
||||
int8_t p[TSDB_MAX_JOIN_TABLE_NUM] = {0};
|
||||
|
||||
for (int16_t i = 0; i < pQueryInfo->numOfTables; ++i) {
|
||||
mergeJoinNodesImpl(r, p, &i, pQueryInfo->tagCond.joinInfo.joinTables, 0);
|
||||
|
||||
taosArrayClear(pQueryInfo->tagCond.joinInfo.joinTables[i]->tsJoin);
|
||||
|
||||
for (int32_t j = 0; j < TSDB_MAX_JOIN_TABLE_NUM; ++j) {
|
||||
if (r[j]) {
|
||||
taosArrayPush(pQueryInfo->tagCond.joinInfo.joinTables[i]->tsJoin, &j);
|
||||
}
|
||||
}
|
||||
|
||||
memset(r, 0, sizeof(r));
|
||||
memset(p, 0, sizeof(p));
|
||||
}
|
||||
|
||||
if (taosArrayGetSize(pQueryInfo->tagCond.joinInfo.joinTables[0]->tsJoin) != pQueryInfo->numOfTables) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(&pSql->cmd), msg1);
|
||||
}
|
||||
|
||||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||
if (UTIL_TABLE_IS_SUPER_TABLE(pTableMetaInfo)) {
|
||||
for (int16_t i = 0; i < pQueryInfo->numOfTables; ++i) {
|
||||
mergeJoinNodesImpl(r, p, &i, pQueryInfo->tagCond.joinInfo.joinTables, 1);
|
||||
|
||||
taosArrayClear(pQueryInfo->tagCond.joinInfo.joinTables[i]->tagJoin);
|
||||
|
||||
for (int32_t j = 0; j < TSDB_MAX_JOIN_TABLE_NUM; ++j) {
|
||||
if (r[j]) {
|
||||
taosArrayPush(pQueryInfo->tagCond.joinInfo.joinTables[i]->tagJoin, &j);
|
||||
}
|
||||
}
|
||||
|
||||
memset(r, 0, sizeof(r));
|
||||
memset(p, 0, sizeof(p));
|
||||
}
|
||||
|
||||
if (taosArrayGetSize(pQueryInfo->tagCond.joinInfo.joinTables[0]->tagJoin) != pQueryInfo->numOfTables) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(&pSql->cmd), msg2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int32_t parseWhereClause(SQueryInfo* pQueryInfo, tSqlExpr** pExpr, SSqlObj* pSql) {
|
||||
if (pExpr == NULL) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -4284,17 +4523,17 @@ int32_t parseWhereClause(SQueryInfo* pQueryInfo, tSqlExpr** pExpr, SSqlObj* pSql
|
|||
|
||||
// 4. get the table name query condition
|
||||
if ((ret = getTablenameCond(&pSql->cmd, pQueryInfo, condExpr.pTableCond, &sb)) != TSDB_CODE_SUCCESS) {
|
||||
return ret;
|
||||
goto PARSE_WHERE_EXIT;
|
||||
}
|
||||
|
||||
// 5. other column query condition
|
||||
if ((ret = getColumnQueryCondInfo(&pSql->cmd, pQueryInfo, condExpr.pColumnCond, TK_AND)) != TSDB_CODE_SUCCESS) {
|
||||
return ret;
|
||||
goto PARSE_WHERE_EXIT;
|
||||
}
|
||||
|
||||
// 6. join condition
|
||||
if ((ret = getJoinCondInfo(&pSql->cmd, pQueryInfo, condExpr.pJoinExpr)) != TSDB_CODE_SUCCESS) {
|
||||
return ret;
|
||||
goto PARSE_WHERE_EXIT;
|
||||
}
|
||||
|
||||
// 7. query condition for table name
|
||||
|
@ -4302,12 +4541,29 @@ int32_t parseWhereClause(SQueryInfo* pQueryInfo, tSqlExpr** pExpr, SSqlObj* pSql
|
|||
|
||||
ret = setTableCondForSTableQuery(&pSql->cmd, pQueryInfo, getAccountId(pSql), condExpr.pTableCond, condExpr.tableCondIndex, &sb);
|
||||
taosStringBuilderDestroy(&sb);
|
||||
|
||||
if (!validateFilterExpr(pQueryInfo)) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(&pSql->cmd), msg2);
|
||||
if (ret) {
|
||||
goto PARSE_WHERE_EXIT;
|
||||
}
|
||||
|
||||
doAddJoinTagsColumnsIntoTagList(&pSql->cmd, pQueryInfo, &condExpr);
|
||||
if (!validateFilterExpr(pQueryInfo)) {
|
||||
ret = invalidSqlErrMsg(tscGetErrorMsgPayload(&pSql->cmd), msg2);
|
||||
goto PARSE_WHERE_EXIT;
|
||||
}
|
||||
|
||||
//doAddJoinTagsColumnsIntoTagList(&pSql->cmd, pQueryInfo, &condExpr);
|
||||
if (condExpr.tsJoin) {
|
||||
ret = validateJoinNodes(pQueryInfo, pSql);
|
||||
if (ret) {
|
||||
goto PARSE_WHERE_EXIT;
|
||||
}
|
||||
|
||||
ret = mergeJoinNodes(pQueryInfo, pSql);
|
||||
if (ret) {
|
||||
goto PARSE_WHERE_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
PARSE_WHERE_EXIT:
|
||||
|
||||
cleanQueryExpr(&condExpr);
|
||||
return ret;
|
||||
|
@ -6520,7 +6776,6 @@ int32_t doValidateSqlNode(SSqlObj* pSql, SQuerySqlNode* pQuerySqlNode, int32_t i
|
|||
const char* msg1 = "point interpolation query needs timestamp";
|
||||
const char* msg2 = "fill only available for interval query";
|
||||
const char* msg3 = "start(end) time of query range required or time range too large";
|
||||
const char* msg4 = "illegal number of tables in from clause";
|
||||
const char* msg5 = "too many columns in selection clause";
|
||||
const char* msg6 = "too many tables in from clause";
|
||||
const char* msg7 = "invalid table alias name";
|
||||
|
@ -6557,14 +6812,11 @@ int32_t doValidateSqlNode(SSqlObj* pSql, SQuerySqlNode* pQuerySqlNode, int32_t i
|
|||
}
|
||||
|
||||
size_t fromSize = taosArrayGetSize(pQuerySqlNode->from->tableList);
|
||||
if (fromSize > TSDB_MAX_JOIN_TABLE_NUM) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg4);
|
||||
if (fromSize > TSDB_MAX_JOIN_TABLE_NUM * 2) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg6);
|
||||
}
|
||||
|
||||
pQueryInfo->command = TSDB_SQL_SELECT;
|
||||
if (fromSize > 2) {
|
||||
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg6);
|
||||
}
|
||||
|
||||
// set all query tables, which are maybe more than one.
|
||||
for (int32_t i = 0; i < fromSize; ++i) {
|
||||
|
|
|
@ -350,8 +350,8 @@ void tscProcessMsgFromServer(SRpcMsg *rpcMsg, SRpcEpSet *pEpSet) {
|
|||
taosMsleep(duration);
|
||||
}
|
||||
|
||||
pSql->retryReason = rpcMsg->code;
|
||||
rpcMsg->code = tscRenewTableMeta(pSql, 0);
|
||||
|
||||
// if there is an error occurring, proceed to the following error handling procedure.
|
||||
if (rpcMsg->code == TSDB_CODE_TSC_ACTION_IN_PROGRESS) {
|
||||
taosReleaseRef(tscObjRef, handle);
|
||||
|
@ -508,7 +508,7 @@ int tscBuildFetchMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
|||
|
||||
SQueryInfo *pQueryInfo = tscGetQueryInfoDetail(&pSql->cmd, pSql->cmd.clauseIndex);
|
||||
pRetrieveMsg->free = htons(pQueryInfo->type);
|
||||
pRetrieveMsg->qhandle = htobe64(pSql->res.qhandle);
|
||||
pRetrieveMsg->qId = htobe64(pSql->res.qId);
|
||||
|
||||
// todo valid the vgroupId at the client side
|
||||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||
|
@ -520,7 +520,7 @@ int tscBuildFetchMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
|||
assert(pVgroupInfo->vgroups[vgIndex].vgId > 0 && vgIndex < pTableMetaInfo->vgroupList->numOfVgroups);
|
||||
|
||||
pRetrieveMsg->header.vgId = htonl(pVgroupInfo->vgroups[vgIndex].vgId);
|
||||
tscDebug("%p build fetch msg from vgId:%d, vgIndex:%d, qhandle:%" PRIX64, pSql, pVgroupInfo->vgroups[vgIndex].vgId, vgIndex, pSql->res.qhandle);
|
||||
tscDebug("%p build fetch msg from vgId:%d, vgIndex:%d, qhandle:%" PRIX64, pSql, pVgroupInfo->vgroups[vgIndex].vgId, vgIndex, pSql->res.qId);
|
||||
} else {
|
||||
int32_t numOfVgroups = (int32_t)taosArrayGetSize(pTableMetaInfo->pVgroupTables);
|
||||
assert(vgIndex >= 0 && vgIndex < numOfVgroups);
|
||||
|
@ -528,12 +528,12 @@ int tscBuildFetchMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
|||
SVgroupTableInfo* pTableIdList = taosArrayGet(pTableMetaInfo->pVgroupTables, vgIndex);
|
||||
|
||||
pRetrieveMsg->header.vgId = htonl(pTableIdList->vgInfo.vgId);
|
||||
tscDebug("%p build fetch msg from vgId:%d, vgIndex:%d, qhandle:%" PRIX64, pSql, pTableIdList->vgInfo.vgId, vgIndex, pSql->res.qhandle);
|
||||
tscDebug("%p build fetch msg from vgId:%d, vgIndex:%d, qId:%" PRIu64, pSql, pTableIdList->vgInfo.vgId, vgIndex, pSql->res.qId);
|
||||
}
|
||||
} else {
|
||||
STableMeta* pTableMeta = pTableMetaInfo->pTableMeta;
|
||||
pRetrieveMsg->header.vgId = htonl(pTableMeta->vgId);
|
||||
tscDebug("%p build fetch msg from only one vgroup, vgId:%d, qhandle:%" PRIX64, pSql, pTableMeta->vgId, pSql->res.qhandle);
|
||||
tscDebug("%p build fetch msg from only one vgroup, vgId:%d, qId:%" PRIu64, pSql, pTableMeta->vgId, pSql->res.qId);
|
||||
}
|
||||
|
||||
pSql->cmd.payloadLen = sizeof(SRetrieveTableMsg);
|
||||
|
@ -613,7 +613,7 @@ static int32_t tscEstimateQueryMsgSize(SSqlObj *pSql, int32_t clauseIndex) {
|
|||
tableSerialize + sqlLen + 4096 + pQueryInfo->bufLen;
|
||||
}
|
||||
|
||||
static char *doSerializeTableInfo(SQueryTableMsg* pQueryMsg, SSqlObj *pSql, char *pMsg) {
|
||||
static char *doSerializeTableInfo(SQueryTableMsg* pQueryMsg, SSqlObj *pSql, char *pMsg, int32_t *succeed) {
|
||||
STableMetaInfo *pTableMetaInfo = tscGetTableMetaInfoFromCmd(&pSql->cmd, pSql->cmd.clauseIndex, 0);
|
||||
TSKEY dfltKey = htobe64(pQueryMsg->window.skey);
|
||||
|
||||
|
@ -626,9 +626,14 @@ static char *doSerializeTableInfo(SQueryTableMsg* pQueryMsg, SSqlObj *pSql, char
|
|||
assert(index >= 0);
|
||||
|
||||
SVgroupInfo* pVgroupInfo = NULL;
|
||||
if (pTableMetaInfo->vgroupList->numOfVgroups > 0) {
|
||||
if (pTableMetaInfo->vgroupList && pTableMetaInfo->vgroupList->numOfVgroups > 0) {
|
||||
assert(index < pTableMetaInfo->vgroupList->numOfVgroups);
|
||||
pVgroupInfo = &pTableMetaInfo->vgroupList->vgroups[index];
|
||||
} else {
|
||||
tscError("%p No vgroup info found", pSql);
|
||||
|
||||
*succeed = 0;
|
||||
return pMsg;
|
||||
}
|
||||
|
||||
vgId = pVgroupInfo->vgId;
|
||||
|
@ -948,8 +953,13 @@ int tscBuildQueryMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
|||
pQueryMsg->secondStageOutput = 0;
|
||||
}
|
||||
|
||||
int32_t succeed = 1;
|
||||
|
||||
// serialize the table info (sid, uid, tags)
|
||||
pMsg = doSerializeTableInfo(pQueryMsg, pSql, pMsg);
|
||||
pMsg = doSerializeTableInfo(pQueryMsg, pSql, pMsg, &succeed);
|
||||
if (succeed == 0) {
|
||||
return TSDB_CODE_TSC_APP_ERROR;
|
||||
}
|
||||
|
||||
SSqlGroupbyExpr *pGroupbyExpr = &pQueryInfo->groupbyExpr;
|
||||
if (pGroupbyExpr->numOfGroupCols > 0) {
|
||||
|
@ -1284,6 +1294,23 @@ int32_t tscBuildUseDbMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t tscBuildSyncDbReplicaMsg(SSqlObj* pSql, SSqlInfo *pInfo) {
|
||||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
pCmd->payloadLen = sizeof(SSyncDbMsg);
|
||||
|
||||
if (TSDB_CODE_SUCCESS != tscAllocPayload(pCmd, pCmd->payloadLen)) {
|
||||
tscError("%p failed to malloc for query msg", pSql);
|
||||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SSyncDbMsg *pSyncMsg = (SSyncDbMsg *)pCmd->payload;
|
||||
STableMetaInfo *pTableMetaInfo = tscGetTableMetaInfoFromCmd(pCmd, pCmd->clauseIndex, 0);
|
||||
tNameExtractFullName(&pTableMetaInfo->name, pSyncMsg->db);
|
||||
pCmd->msgType = TSDB_MSG_TYPE_CM_SYNC_DB;
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t tscBuildShowMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
||||
STscObj *pObj = pSql->pTscObj;
|
||||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
|
@ -1556,7 +1583,7 @@ int tscBuildRetrieveFromMgmtMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
|
|||
|
||||
SQueryInfo *pQueryInfo = tscGetQueryInfoDetail(pCmd, 0);
|
||||
SRetrieveTableMsg *pRetrieveMsg = (SRetrieveTableMsg*)pCmd->payload;
|
||||
pRetrieveMsg->qhandle = htobe64(pSql->res.qhandle);
|
||||
pRetrieveMsg->qId = htobe64(pSql->res.qId);
|
||||
pRetrieveMsg->free = htons(pQueryInfo->type);
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -2064,6 +2091,10 @@ int tscProcessSTableVgroupRsp(SSqlObj *pSql) {
|
|||
assert(pInfo->vgroupList != NULL);
|
||||
|
||||
pInfo->vgroupList->numOfVgroups = pVgroupMsg->numOfVgroups;
|
||||
if (pInfo->vgroupList->numOfVgroups <= 0) {
|
||||
//tfree(pInfo->vgroupList);
|
||||
tscError("%p empty vgroup info", pSql);
|
||||
} else {
|
||||
for (int32_t j = 0; j < pInfo->vgroupList->numOfVgroups; ++j) {
|
||||
//just init, no need to lock
|
||||
SVgroupInfo *pVgroups = &pInfo->vgroupList->vgroups[j];
|
||||
|
@ -2079,6 +2110,7 @@ int tscProcessSTableVgroupRsp(SSqlObj *pSql) {
|
|||
pVgroups->epAddr[k].fqdn = strndup(vmsg->epAddr[k].fqdn, tListLen(vmsg->epAddr[k].fqdn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pMsg += size;
|
||||
}
|
||||
|
@ -2102,7 +2134,7 @@ int tscProcessShowRsp(SSqlObj *pSql) {
|
|||
|
||||
pShow = (SShowRsp *)pRes->pRsp;
|
||||
pShow->qhandle = htobe64(pShow->qhandle);
|
||||
pRes->qhandle = pShow->qhandle;
|
||||
pRes->qId = pShow->qhandle;
|
||||
|
||||
tscResetForNextRetrieve(pRes);
|
||||
pMetaMsg = &(pShow->tableMeta);
|
||||
|
@ -2284,11 +2316,12 @@ int tscProcessQueryRsp(SSqlObj *pSql) {
|
|||
SSqlRes *pRes = &pSql->res;
|
||||
|
||||
SQueryTableRsp *pQuery = (SQueryTableRsp *)pRes->pRsp;
|
||||
pQuery->qhandle = htobe64(pQuery->qhandle);
|
||||
pRes->qhandle = pQuery->qhandle;
|
||||
pQuery->qId = htobe64(pQuery->qId);
|
||||
pRes->qId = pQuery->qId;
|
||||
|
||||
pRes->data = NULL;
|
||||
tscResetForNextRetrieve(pRes);
|
||||
tscDebug("%p query rsp received, qId:%"PRIu64, pSql, pRes->qId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2346,7 +2379,8 @@ int tscProcessRetrieveRspFromNode(SSqlObj *pSql) {
|
|||
}
|
||||
|
||||
pRes->row = 0;
|
||||
tscDebug("%p numOfRows:%d, offset:%" PRId64 ", complete:%d", pSql, pRes->numOfRows, pRes->offset, pRes->completed);
|
||||
tscDebug("%p numOfRows:%d, offset:%" PRId64 ", complete:%d, qId:%"PRIu64, pSql, pRes->numOfRows, pRes->offset,
|
||||
pRes->completed, pRes->qId);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2559,6 +2593,7 @@ void tscInitMsgsFp() {
|
|||
tscBuildMsg[TSDB_SQL_DROP_USER] = tscBuildDropUserAcctMsg;
|
||||
tscBuildMsg[TSDB_SQL_DROP_ACCT] = tscBuildDropUserAcctMsg;
|
||||
tscBuildMsg[TSDB_SQL_DROP_DB] = tscBuildDropDbMsg;
|
||||
tscBuildMsg[TSDB_SQL_SYNC_DB_REPLICA] = tscBuildSyncDbReplicaMsg;
|
||||
tscBuildMsg[TSDB_SQL_DROP_TABLE] = tscBuildDropTableMsg;
|
||||
tscBuildMsg[TSDB_SQL_ALTER_USER] = tscBuildUserMsg;
|
||||
tscBuildMsg[TSDB_SQL_CREATE_DNODE] = tscBuildCreateDnodeMsg;
|
||||
|
@ -2612,7 +2647,6 @@ void tscInitMsgsFp() {
|
|||
tscProcessMsgRsp[TSDB_SQL_SHOW_CREATE_TABLE] = tscProcessShowCreateRsp;
|
||||
tscProcessMsgRsp[TSDB_SQL_SHOW_CREATE_DATABASE] = tscProcessShowCreateRsp;
|
||||
|
||||
|
||||
tscKeepConn[TSDB_SQL_SHOW] = 1;
|
||||
tscKeepConn[TSDB_SQL_RETRIEVE] = 1;
|
||||
tscKeepConn[TSDB_SQL_SELECT] = 1;
|
||||
|
|
|
@ -476,7 +476,7 @@ TAOS_ROW taos_fetch_row(TAOS_RES *res) {
|
|||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
SSqlRes *pRes = &pSql->res;
|
||||
|
||||
if (pRes->qhandle == 0 ||
|
||||
if (pRes->qId == 0 ||
|
||||
pRes->code == TSDB_CODE_TSC_QUERY_CANCELLED ||
|
||||
pCmd->command == TSDB_SQL_RETRIEVE_EMPTY_RESULT ||
|
||||
pCmd->command == TSDB_SQL_INSERT) {
|
||||
|
@ -508,7 +508,7 @@ int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows) {
|
|||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
SSqlRes *pRes = &pSql->res;
|
||||
|
||||
if (pRes->qhandle == 0 ||
|
||||
if (pRes->qId == 0 ||
|
||||
pRes->code == TSDB_CODE_TSC_QUERY_CANCELLED ||
|
||||
pCmd->command == TSDB_SQL_RETRIEVE_EMPTY_RESULT ||
|
||||
pCmd->command == TSDB_SQL_INSERT) {
|
||||
|
@ -554,7 +554,7 @@ static bool tscKillQueryInDnode(SSqlObj* pSql) {
|
|||
SSqlCmd* pCmd = &pSql->cmd;
|
||||
SSqlRes* pRes = &pSql->res;
|
||||
|
||||
if (pRes == NULL || pRes->qhandle == 0) {
|
||||
if (pRes == NULL || pRes->qId == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1050,7 +1050,7 @@ int taos_load_table_info(TAOS *taos, const char *tableNameList) {
|
|||
* If qhandle is NOT set 0, the function of taos_free_result() will send message to server by calling tscProcessSql()
|
||||
* to free connection, which may cause segment fault, when the parse phrase is not even successfully executed.
|
||||
*/
|
||||
pRes->qhandle = 0;
|
||||
pRes->qId = 0;
|
||||
free(str);
|
||||
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
|
|
|
@ -299,6 +299,7 @@ static void tscProcessStreamRetrieveResult(void *param, TAOS_RES *res, int numOf
|
|||
tfree(pTableMetaInfo->pTableMeta);
|
||||
|
||||
tscFreeSqlResult(pSql);
|
||||
tscFreeSubobj(pSql);
|
||||
tfree(pSql->pSubs);
|
||||
pSql->subState.numOfSub = 0;
|
||||
pTableMetaInfo->vgroupList = tscVgroupInfoClear(pTableMetaInfo->vgroupList);
|
||||
|
|
|
@ -149,7 +149,7 @@ static SSub* tscCreateSubscription(STscObj* pObj, const char* topic, const char*
|
|||
}
|
||||
|
||||
strtolower(pSql->sqlstr, pSql->sqlstr);
|
||||
pRes->qhandle = 0;
|
||||
pRes->qId = 0;
|
||||
pRes->numOfRows = 1;
|
||||
|
||||
code = tscAllocPayload(pCmd, TSDB_DEFAULT_PAYLOAD_SIZE);
|
||||
|
@ -448,7 +448,7 @@ SSqlObj* recreateSqlObj(SSub* pSub) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
pRes->qhandle = 0;
|
||||
pRes->qId = 0;
|
||||
pRes->numOfRows = 1;
|
||||
|
||||
int code = tscAllocPayload(pCmd, TSDB_DEFAULT_PAYLOAD_SIZE);
|
||||
|
@ -546,7 +546,7 @@ TAOS_RES *taos_consume(TAOS_SUB *tsub) {
|
|||
uint32_t type = pQueryInfo->type;
|
||||
tscFreeSqlResult(pSql);
|
||||
pRes->numOfRows = 1;
|
||||
pRes->qhandle = 0;
|
||||
pRes->qId = 0;
|
||||
pSql->cmd.command = TSDB_SQL_SELECT;
|
||||
pQueryInfo->type = type;
|
||||
|
||||
|
|
|
@ -46,6 +46,13 @@ static int32_t tsCompare(int32_t order, int64_t left, int64_t right) {
|
|||
}
|
||||
|
||||
static void skipRemainValue(STSBuf* pTSBuf, tVariant* tag1) {
|
||||
STSElem el1 = tsBufGetElem(pTSBuf);
|
||||
|
||||
int32_t res = tVariantCompare(el1.tag, tag1);
|
||||
if (res != 0) { // it is a record with new tag
|
||||
return;
|
||||
}
|
||||
|
||||
while (tsBufNextPos(pTSBuf)) {
|
||||
STSElem el1 = tsBufGetElem(pTSBuf);
|
||||
|
||||
|
@ -118,123 +125,233 @@ static bool subAndCheckDone(SSqlObj *pSql, SSqlObj *pParentSql, int idx) {
|
|||
|
||||
|
||||
|
||||
static int64_t doTSBlockIntersect(SSqlObj* pSql, SJoinSupporter* pSupporter1, SJoinSupporter* pSupporter2, STimeWindow * win) {
|
||||
static int64_t doTSBlockIntersect(SSqlObj* pSql, STimeWindow * win) {
|
||||
SQueryInfo* pQueryInfo = tscGetQueryInfoDetail(&pSql->cmd, pSql->cmd.clauseIndex);
|
||||
|
||||
STSBuf* output1 = tsBufCreate(true, pQueryInfo->order.order);
|
||||
STSBuf* output2 = tsBufCreate(true, pQueryInfo->order.order);
|
||||
|
||||
win->skey = INT64_MAX;
|
||||
win->ekey = INT64_MIN;
|
||||
|
||||
SLimitVal* pLimit = &pQueryInfo->limit;
|
||||
int32_t order = pQueryInfo->order.order;
|
||||
int32_t joinNum = pSql->subState.numOfSub;
|
||||
SMergeTsCtx ctxlist[TSDB_MAX_JOIN_TABLE_NUM] = {{0}};
|
||||
SMergeTsCtx* ctxStack[TSDB_MAX_JOIN_TABLE_NUM] = {0};
|
||||
int32_t slot = 0;
|
||||
size_t tableNum = 0;
|
||||
int16_t* tableMIdx = 0;
|
||||
int32_t equalNum = 0;
|
||||
int32_t stackidx = 0;
|
||||
SMergeTsCtx* ctx = NULL;
|
||||
SMergeTsCtx* pctx = NULL;
|
||||
SMergeTsCtx* mainCtx = NULL;
|
||||
STSElem cur;
|
||||
STSElem prev;
|
||||
SArray* tsCond = NULL;
|
||||
int32_t mergeDone = 0;
|
||||
|
||||
SQueryInfo* pSubQueryInfo1 = tscGetQueryInfoDetail(&pSql->pSubs[0]->cmd, 0);
|
||||
SQueryInfo* pSubQueryInfo2 = tscGetQueryInfoDetail(&pSql->pSubs[1]->cmd, 0);
|
||||
for (int32_t i = 0; i < joinNum; ++i) {
|
||||
STSBuf* output = tsBufCreate(true, pQueryInfo->order.order);
|
||||
SQueryInfo* pSubQueryInfo = tscGetQueryInfoDetail(&pSql->pSubs[i]->cmd, 0);
|
||||
|
||||
pSubQueryInfo1->tsBuf = output1;
|
||||
pSubQueryInfo2->tsBuf = output2;
|
||||
pSubQueryInfo->tsBuf = output;
|
||||
|
||||
TSKEY st = taosGetTimestampUs();
|
||||
SJoinSupporter* pSupporter = pSql->pSubs[i]->param;
|
||||
|
||||
// no result generated, return directly
|
||||
if (pSupporter1->pTSBuf == NULL || pSupporter2->pTSBuf == NULL) {
|
||||
if (pSupporter->pTSBuf == NULL) {
|
||||
tscDebug("%p at least one ts-comp is empty, 0 for secondary query after ts blocks intersecting", pSql);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tsBufResetPos(pSupporter1->pTSBuf);
|
||||
tsBufResetPos(pSupporter2->pTSBuf);
|
||||
|
||||
if (!tsBufNextPos(pSupporter1->pTSBuf)) {
|
||||
tsBufFlush(output1);
|
||||
tsBufFlush(output2);
|
||||
tsBufResetPos(pSupporter->pTSBuf);
|
||||
|
||||
if (!tsBufNextPos(pSupporter->pTSBuf)) {
|
||||
tscDebug("%p input1 is empty, 0 for secondary query after ts blocks intersecting", pSql);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!tsBufNextPos(pSupporter2->pTSBuf)) {
|
||||
tsBufFlush(output1);
|
||||
tsBufFlush(output2);
|
||||
tscDebug("%p sub:%p table idx:%d, input group number:%d", pSql, pSql->pSubs[i], i, pSupporter->pTSBuf->numOfGroups);
|
||||
|
||||
tscDebug("%p input2 is empty, 0 for secondary query after ts blocks intersecting", pSql);
|
||||
return 0;
|
||||
ctxlist[i].p = pSupporter;
|
||||
ctxlist[i].res = output;
|
||||
}
|
||||
|
||||
int64_t numOfInput1 = 1;
|
||||
int64_t numOfInput2 = 1;
|
||||
TSKEY st = taosGetTimestampUs();
|
||||
|
||||
for (int16_t tidx = 0; tidx < joinNum; tidx++) {
|
||||
pctx = &ctxlist[tidx];
|
||||
if (pctx->compared) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(pctx->numOfInput == 0);
|
||||
|
||||
tsCond = pQueryInfo->tagCond.joinInfo.joinTables[tidx]->tsJoin;
|
||||
|
||||
tableNum = taosArrayGetSize(tsCond);
|
||||
assert(tableNum >= 2);
|
||||
|
||||
for (int32_t i = 0; i < tableNum; ++i) {
|
||||
tableMIdx = taosArrayGet(tsCond, i);
|
||||
SMergeTsCtx* tctx = &ctxlist[*tableMIdx];
|
||||
tctx->compared = 1;
|
||||
}
|
||||
|
||||
tableMIdx = taosArrayGet(tsCond, 0);
|
||||
pctx = &ctxlist[*tableMIdx];
|
||||
|
||||
mainCtx = pctx;
|
||||
|
||||
while (1) {
|
||||
STSElem elem = tsBufGetElem(pSupporter1->pTSBuf);
|
||||
pctx = mainCtx;
|
||||
|
||||
// no data in pSupporter1 anymore, jump out of loop
|
||||
if (!tsBufIsValidElem(&elem)) {
|
||||
prev = tsBufGetElem(pctx->p->pTSBuf);
|
||||
|
||||
ctxStack[stackidx++] = pctx;
|
||||
|
||||
if (!tsBufIsValidElem(&prev)) {
|
||||
break;
|
||||
}
|
||||
|
||||
tVariant tag = {0};
|
||||
tVariantAssign(&tag, prev.tag);
|
||||
|
||||
int32_t skipped = 0;
|
||||
|
||||
for (int32_t i = 1; i < tableNum; ++i) {
|
||||
SMergeTsCtx* tctx = &ctxlist[i];
|
||||
|
||||
// find the data in supporter2 with the same tag value
|
||||
STSElem e2 = tsBufFindElemStartPosByTag(pSupporter2->pTSBuf, elem.tag);
|
||||
STSElem e2 = tsBufFindElemStartPosByTag(tctx->p->pTSBuf, &tag);
|
||||
|
||||
/**
|
||||
* there are elements in pSupporter2 with the same tag, continue
|
||||
*/
|
||||
tVariant tag1 = {0};
|
||||
tVariantAssign(&tag1, elem.tag);
|
||||
if (!tsBufIsValidElem(&e2)) {
|
||||
skipRemainValue(pctx->p->pTSBuf, &tag);
|
||||
skipped = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (skipped) {
|
||||
slot = 0;
|
||||
stackidx = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
tableMIdx = taosArrayGet(tsCond, ++slot);
|
||||
equalNum = 1;
|
||||
|
||||
if (tsBufIsValidElem(&e2)) {
|
||||
while (1) {
|
||||
STSElem elem1 = tsBufGetElem(pSupporter1->pTSBuf);
|
||||
STSElem elem2 = tsBufGetElem(pSupporter2->pTSBuf);
|
||||
ctx = &ctxlist[*tableMIdx];
|
||||
|
||||
prev = tsBufGetElem(pctx->p->pTSBuf);
|
||||
cur = tsBufGetElem(ctx->p->pTSBuf);
|
||||
|
||||
// data with current are exhausted
|
||||
if (!tsBufIsValidElem(&elem1) || tVariantCompare(elem1.tag, &tag1) != 0) {
|
||||
if (!tsBufIsValidElem(&prev) || tVariantCompare(prev.tag, &tag) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tsBufIsValidElem(&elem2) || tVariantCompare(elem2.tag, &tag1) != 0) { // ignore all records with the same tag
|
||||
skipRemainValue(pSupporter1->pTSBuf, &tag1);
|
||||
if (!tsBufIsValidElem(&cur) || tVariantCompare(cur.tag, &tag) != 0) { // ignore all records with the same tag
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* in case of stable query, limit/offset is not applied here. the limit/offset is applied to the
|
||||
* final results which is acquired after the secondary merge of in the client.
|
||||
*/
|
||||
int32_t re = tsCompare(order, elem1.ts, elem2.ts);
|
||||
if (re < 0) {
|
||||
tsBufNextPos(pSupporter1->pTSBuf);
|
||||
numOfInput1++;
|
||||
} else if (re > 0) {
|
||||
tsBufNextPos(pSupporter2->pTSBuf);
|
||||
numOfInput2++;
|
||||
} else {
|
||||
ctxStack[stackidx++] = ctx;
|
||||
|
||||
int32_t ret = tsCompare(order, prev.ts, cur.ts);
|
||||
if (ret == 0) {
|
||||
if (++equalNum < tableNum) {
|
||||
pctx = ctx;
|
||||
|
||||
if (++slot >= tableNum) {
|
||||
slot = 0;
|
||||
}
|
||||
|
||||
tableMIdx = taosArrayGet(tsCond, slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(stackidx == tableNum);
|
||||
|
||||
if (pLimit->offset == 0 || pQueryInfo->interval.interval > 0 || QUERY_IS_STABLE_QUERY(pQueryInfo->type)) {
|
||||
if (win->skey > elem1.ts) {
|
||||
win->skey = elem1.ts;
|
||||
if (win->skey > prev.ts) {
|
||||
win->skey = prev.ts;
|
||||
}
|
||||
|
||||
if (win->ekey < elem1.ts) {
|
||||
win->ekey = elem1.ts;
|
||||
if (win->ekey < prev.ts) {
|
||||
win->ekey = prev.ts;
|
||||
}
|
||||
|
||||
tsBufAppend(output1, elem1.id, elem1.tag, (const char*)&elem1.ts, sizeof(elem1.ts));
|
||||
tsBufAppend(output2, elem2.id, elem2.tag, (const char*)&elem2.ts, sizeof(elem2.ts));
|
||||
for (int32_t i = 0; i < stackidx; ++i) {
|
||||
SMergeTsCtx* tctx = ctxStack[i];
|
||||
prev = tsBufGetElem(tctx->p->pTSBuf);
|
||||
|
||||
tsBufAppend(tctx->res, prev.id, prev.tag, (const char*)&prev.ts, sizeof(prev.ts));
|
||||
}
|
||||
} else {
|
||||
pLimit->offset -= 1;//offset apply to projection?
|
||||
}
|
||||
|
||||
tsBufNextPos(pSupporter1->pTSBuf);
|
||||
numOfInput1++;
|
||||
for (int32_t i = 0; i < stackidx; ++i) {
|
||||
SMergeTsCtx* tctx = ctxStack[i];
|
||||
|
||||
tsBufNextPos(pSupporter2->pTSBuf);
|
||||
numOfInput2++;
|
||||
if (!tsBufNextPos(tctx->p->pTSBuf) && tctx == mainCtx) {
|
||||
mergeDone = 1;
|
||||
}
|
||||
tctx->numOfInput++;
|
||||
}
|
||||
} else { // no data in pSupporter2, ignore current data in pSupporter2
|
||||
skipRemainValue(pSupporter1->pTSBuf, &tag1);
|
||||
|
||||
if (mergeDone) {
|
||||
break;
|
||||
}
|
||||
|
||||
stackidx = 0;
|
||||
equalNum = 1;
|
||||
|
||||
ctxStack[stackidx++] = pctx;
|
||||
} else if (ret > 0) {
|
||||
if (!tsBufNextPos(ctx->p->pTSBuf) && ctx == mainCtx) {
|
||||
mergeDone = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->numOfInput++;
|
||||
stackidx--;
|
||||
} else {
|
||||
stackidx--;
|
||||
|
||||
for (int32_t i = 0; i < stackidx; ++i) {
|
||||
SMergeTsCtx* tctx = ctxStack[i];
|
||||
|
||||
if (!tsBufNextPos(tctx->p->pTSBuf) && tctx == mainCtx) {
|
||||
mergeDone = 1;
|
||||
}
|
||||
tctx->numOfInput++;
|
||||
}
|
||||
|
||||
if (mergeDone) {
|
||||
break;
|
||||
}
|
||||
|
||||
stackidx = 0;
|
||||
equalNum = 1;
|
||||
|
||||
ctxStack[stackidx++] = pctx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mergeDone) {
|
||||
break;
|
||||
}
|
||||
|
||||
slot = 0;
|
||||
stackidx = 0;
|
||||
|
||||
skipRemainValue(mainCtx->p->pTSBuf, &tag);
|
||||
}
|
||||
|
||||
stackidx = 0;
|
||||
slot = 0;
|
||||
mergeDone = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -242,28 +359,32 @@ static int64_t doTSBlockIntersect(SSqlObj* pSql, SJoinSupporter* pSupporter1, SJ
|
|||
* 1. only one element
|
||||
* 2. only one element for each tag.
|
||||
*/
|
||||
if (output1->tsOrder == -1) {
|
||||
output1->tsOrder = TSDB_ORDER_ASC;
|
||||
output2->tsOrder = TSDB_ORDER_ASC;
|
||||
if (ctxlist[0].res->tsOrder == -1) {
|
||||
for (int32_t i = 0; i < joinNum; ++i) {
|
||||
ctxlist[i].res->tsOrder = TSDB_ORDER_ASC;
|
||||
}
|
||||
}
|
||||
|
||||
tsBufFlush(output1);
|
||||
tsBufFlush(output2);
|
||||
for (int32_t i = 0; i < joinNum; ++i) {
|
||||
tsBufFlush(ctxlist[i].res);
|
||||
|
||||
tsBufDestroy(pSupporter1->pTSBuf);
|
||||
pSupporter1->pTSBuf = NULL;
|
||||
tsBufDestroy(pSupporter2->pTSBuf);
|
||||
pSupporter2->pTSBuf = NULL;
|
||||
tsBufDestroy(ctxlist[i].p->pTSBuf);
|
||||
ctxlist[i].p->pTSBuf = NULL;
|
||||
}
|
||||
|
||||
TSKEY et = taosGetTimestampUs();
|
||||
tscDebug("%p input1:%" PRId64 ", input2:%" PRId64 ", final:%" PRId64 " in %d vnodes for secondary query after ts blocks "
|
||||
"intersecting, skey:%" PRId64 ", ekey:%" PRId64 ", numOfVnode:%d, elapsed time:%" PRId64 " us",
|
||||
pSql, numOfInput1, numOfInput2, output1->numOfTotal, output1->numOfGroups, win->skey, win->ekey,
|
||||
tsBufGetNumOfGroup(output1), et - st);
|
||||
|
||||
return output1->numOfTotal;
|
||||
for (int32_t i = 0; i < joinNum; ++i) {
|
||||
tscDebug("%p sub:%p tblidx:%d, input:%" PRId64 ", final:%" PRId64 " in %d vnodes for secondary query after ts blocks "
|
||||
"intersecting, skey:%" PRId64 ", ekey:%" PRId64 ", numOfVnode:%d, elapsed time:%" PRId64 " us",
|
||||
pSql, pSql->pSubs[i], i, ctxlist[i].numOfInput, ctxlist[i].res->numOfTotal, ctxlist[i].res->numOfGroups, win->skey, win->ekey,
|
||||
tsBufGetNumOfGroup(ctxlist[i].res), et - st);
|
||||
}
|
||||
|
||||
return ctxlist[0].res->numOfTotal;
|
||||
}
|
||||
|
||||
|
||||
// todo handle failed to create sub query
|
||||
SJoinSupporter* tscCreateJoinSupporter(SSqlObj* pSql, int32_t index) {
|
||||
SJoinSupporter* pSupporter = calloc(1, sizeof(SJoinSupporter));
|
||||
|
@ -768,76 +889,218 @@ static bool checkForDuplicateTagVal(SSchema* pColSchema, SJoinSupporter* p1, SSq
|
|||
return true;
|
||||
}
|
||||
|
||||
static int32_t getIntersectionOfTableTuple(SQueryInfo* pQueryInfo, SSqlObj* pParentSql, SArray** s1, SArray** s2) {
|
||||
SJoinSupporter* p1 = pParentSql->pSubs[0]->param;
|
||||
SJoinSupporter* p2 = pParentSql->pSubs[1]->param;
|
||||
|
||||
tscDebug("%p all subquery retrieve <tid, tags> complete, do tags match, %d, %d", pParentSql, p1->num, p2->num);
|
||||
|
||||
// sort according to the tag value
|
||||
qsort(p1->pIdTagList, p1->num, p1->tagSize, tagValCompar);
|
||||
qsort(p2->pIdTagList, p2->num, p2->tagSize, tagValCompar);
|
||||
|
||||
static int32_t getIntersectionOfTableTuple(SQueryInfo* pQueryInfo, SSqlObj* pParentSql, SArray* resList) {
|
||||
int16_t joinNum = pParentSql->subState.numOfSub;
|
||||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||
int16_t tagColId = tscGetJoinTagColIdByUid(&pQueryInfo->tagCond, pTableMetaInfo->pTableMeta->id.uid);
|
||||
SJoinSupporter* p0 = pParentSql->pSubs[0]->param;
|
||||
SMergeCtx ctxlist[TSDB_MAX_JOIN_TABLE_NUM] = {{0}};
|
||||
SMergeCtx* ctxStack[TSDB_MAX_JOIN_TABLE_NUM] = {0};
|
||||
|
||||
// int16_t for padding
|
||||
int32_t size = p0->tagSize - sizeof(int16_t);
|
||||
|
||||
SSchema* pColSchema = tscGetColumnSchemaById(pTableMetaInfo->pTableMeta, tagColId);
|
||||
|
||||
// int16_t for padding
|
||||
int32_t size = p1->tagSize - sizeof(int16_t);
|
||||
*s1 = taosArrayInit(p1->num, size);
|
||||
*s2 = taosArrayInit(p2->num, size);
|
||||
tscDebug("%p all subquery retrieve <tid, tags> complete, do tags match", pParentSql);
|
||||
|
||||
if (!(checkForDuplicateTagVal(pColSchema, p1, pParentSql) && checkForDuplicateTagVal(pColSchema, p2, pParentSql))) {
|
||||
for (int32_t i = 0; i < joinNum; i++) {
|
||||
SJoinSupporter* p = pParentSql->pSubs[i]->param;
|
||||
|
||||
ctxlist[i].p = p;
|
||||
ctxlist[i].res = taosArrayInit(p->num, size);
|
||||
|
||||
tscDebug("Join %d - num:%d", i, p->num);
|
||||
|
||||
// sort according to the tag valu
|
||||
qsort(p->pIdTagList, p->num, p->tagSize, tagValCompar);
|
||||
|
||||
if (!checkForDuplicateTagVal(pColSchema, p, pParentSql)) {
|
||||
for (int32_t j = 0; j <= i; j++) {
|
||||
taosArrayDestroy(ctxlist[j].res);
|
||||
}
|
||||
return TSDB_CODE_QRY_DUP_JOIN_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t i = 0, j = 0;
|
||||
while(i < p1->num && j < p2->num) {
|
||||
STidTags* pp1 = (STidTags*) varDataVal(p1->pIdTagList + i * p1->tagSize);
|
||||
STidTags* pp2 = (STidTags*) varDataVal(p2->pIdTagList + j * p2->tagSize);
|
||||
assert(pp1->tid != 0 && pp2->tid != 0);
|
||||
int32_t slot = 0;
|
||||
size_t tableNum = 0;
|
||||
int16_t* tableMIdx = 0;
|
||||
int32_t equalNum = 0;
|
||||
int32_t stackidx = 0;
|
||||
int32_t mergeDone = 0;
|
||||
SMergeCtx* ctx = NULL;
|
||||
SMergeCtx* pctx = NULL;
|
||||
STidTags* cur = NULL;
|
||||
STidTags* prev = NULL;
|
||||
SArray* tagCond = NULL;
|
||||
|
||||
int32_t ret = doCompare(pp1->tag, pp2->tag, pColSchema->type, pColSchema->bytes);
|
||||
for (int16_t tidx = 0; tidx < joinNum; tidx++) {
|
||||
pctx = &ctxlist[tidx];
|
||||
if (pctx->compared) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(pctx->idx == 0 && taosArrayGetSize(pctx->res) == 0);
|
||||
|
||||
tagCond = pQueryInfo->tagCond.joinInfo.joinTables[tidx]->tagJoin;
|
||||
|
||||
tableNum = taosArrayGetSize(tagCond);
|
||||
assert(tableNum >= 2);
|
||||
|
||||
for (int32_t i = 0; i < tableNum; ++i) {
|
||||
tableMIdx = taosArrayGet(tagCond, i);
|
||||
SMergeCtx* tctx = &ctxlist[*tableMIdx];
|
||||
tctx->compared = 1;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < tableNum; ++i) {
|
||||
tableMIdx = taosArrayGet(tagCond, i);
|
||||
SMergeCtx* tctx = &ctxlist[*tableMIdx];
|
||||
if (tctx->p->num <= 0 || tctx->p->pIdTagList == NULL) {
|
||||
mergeDone = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mergeDone) {
|
||||
mergeDone = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
tableMIdx = taosArrayGet(tagCond, slot);
|
||||
|
||||
pctx = &ctxlist[*tableMIdx];
|
||||
|
||||
prev = (STidTags*) varDataVal(pctx->p->pIdTagList + pctx->idx * pctx->p->tagSize);
|
||||
|
||||
ctxStack[stackidx++] = pctx;
|
||||
|
||||
tableMIdx = taosArrayGet(tagCond, ++slot);
|
||||
|
||||
equalNum = 1;
|
||||
|
||||
while (1) {
|
||||
ctx = &ctxlist[*tableMIdx];
|
||||
|
||||
cur = (STidTags*) varDataVal(ctx->p->pIdTagList + ctx->idx * ctx->p->tagSize);
|
||||
|
||||
assert(cur->tid != 0 && prev->tid != 0);
|
||||
|
||||
ctxStack[stackidx++] = ctx;
|
||||
|
||||
int32_t ret = doCompare(prev->tag, cur->tag, pColSchema->type, pColSchema->bytes);
|
||||
if (ret == 0) {
|
||||
tscDebug("%p tag matched, vgId:%d, val:%d, tid:%d, uid:%"PRIu64", tid:%d, uid:%"PRIu64, pParentSql, pp1->vgId,
|
||||
*(int*) pp1->tag, pp1->tid, pp1->uid, pp2->tid, pp2->uid);
|
||||
if (++equalNum < tableNum) {
|
||||
prev = cur;
|
||||
pctx = ctx;
|
||||
|
||||
taosArrayPush(*s1, pp1);
|
||||
taosArrayPush(*s2, pp2);
|
||||
j++;
|
||||
i++;
|
||||
if (++slot >= tableNum) {
|
||||
slot = 0;
|
||||
}
|
||||
|
||||
tableMIdx = taosArrayGet(tagCond, slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
tscDebug("%p tag matched, vgId:%d, val:%d, tid:%d, uid:%"PRIu64", tid:%d, uid:%"PRIu64, pParentSql, prev->vgId,
|
||||
*(int*) prev->tag, prev->tid, prev->uid, cur->tid, cur->uid);
|
||||
|
||||
assert(stackidx == tableNum);
|
||||
|
||||
for (int32_t i = 0; i < stackidx; ++i) {
|
||||
SMergeCtx* tctx = ctxStack[i];
|
||||
prev = (STidTags*) varDataVal(tctx->p->pIdTagList + tctx->idx * tctx->p->tagSize);
|
||||
|
||||
taosArrayPush(tctx->res, prev);
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < stackidx; ++i) {
|
||||
SMergeCtx* tctx = ctxStack[i];
|
||||
|
||||
if (++tctx->idx >= tctx->p->num) {
|
||||
mergeDone = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mergeDone) {
|
||||
break;
|
||||
}
|
||||
|
||||
stackidx = 0;
|
||||
equalNum = 1;
|
||||
|
||||
prev = (STidTags*) varDataVal(pctx->p->pIdTagList + pctx->idx * pctx->p->tagSize);
|
||||
|
||||
ctxStack[stackidx++] = pctx;
|
||||
} else if (ret > 0) {
|
||||
j++;
|
||||
stackidx--;
|
||||
|
||||
if (++ctx->idx >= ctx->p->num) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
i++;
|
||||
stackidx--;
|
||||
|
||||
for (int32_t i = 0; i < stackidx; ++i) {
|
||||
SMergeCtx* tctx = ctxStack[i];
|
||||
if (++tctx->idx >= tctx->p->num) {
|
||||
mergeDone = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mergeDone) {
|
||||
break;
|
||||
}
|
||||
|
||||
stackidx = 0;
|
||||
equalNum = 1;
|
||||
|
||||
prev = (STidTags*) varDataVal(pctx->p->pIdTagList + pctx->idx * pctx->p->tagSize);
|
||||
ctxStack[stackidx++] = pctx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
slot = 0;
|
||||
mergeDone = 0;
|
||||
stackidx = 0;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < joinNum; ++i) {
|
||||
// reorganize the tid-tag value according to both the vgroup id and tag values
|
||||
// sort according to the tag value
|
||||
size_t t1 = taosArrayGetSize(*s1);
|
||||
size_t t2 = taosArrayGetSize(*s2);
|
||||
size_t num = taosArrayGetSize(ctxlist[i].res);
|
||||
|
||||
qsort((*s1)->pData, t1, size, tidTagsCompar);
|
||||
qsort((*s2)->pData, t2, size, tidTagsCompar);
|
||||
qsort((ctxlist[i].res)->pData, num, size, tidTagsCompar);
|
||||
|
||||
#if 0
|
||||
for(int32_t k = 0; k < t1; ++k) {
|
||||
STidTags* p = (*s1)->pData + size * k;
|
||||
printf("%d, tag:%s\n", p->vgId, ((tstr*)(p->tag))->data);
|
||||
taosArrayPush(resList, &ctxlist[i].res);
|
||||
|
||||
tscDebug("%p tags match complete, result num: %"PRIzu, pParentSql, num);
|
||||
}
|
||||
|
||||
for(int32_t k = 0; k < t1; ++k) {
|
||||
STidTags* p = (*s2)->pData + size * k;
|
||||
printf("%d, tag:%s\n", p->vgId, ((tstr*)(p->tag))->data);
|
||||
}
|
||||
#endif
|
||||
|
||||
tscDebug("%p tags match complete, result: %"PRIzu", %"PRIzu, pParentSql, t1, t2);
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
bool emptyTagList(SArray* resList, int32_t size) {
|
||||
size_t rsize = taosArrayGetSize(resList);
|
||||
if (rsize != size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
SArray** s = taosArrayGet(resList, i);
|
||||
if (taosArrayGetSize(*s) <= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void tidTagRetrieveCallback(void* param, TAOS_RES* tres, int32_t numOfRows) {
|
||||
SJoinSupporter* pSupporter = (SJoinSupporter*)param;
|
||||
|
||||
|
@ -939,19 +1202,19 @@ static void tidTagRetrieveCallback(void* param, TAOS_RES* tres, int32_t numOfRow
|
|||
return;
|
||||
}
|
||||
|
||||
SArray *s1 = NULL, *s2 = NULL;
|
||||
int32_t code = getIntersectionOfTableTuple(pQueryInfo, pParentSql, &s1, &s2);
|
||||
SArray* resList = taosArrayInit(pParentSql->subState.numOfSub, sizeof(SArray *));
|
||||
|
||||
int32_t code = getIntersectionOfTableTuple(pQueryInfo, pParentSql, resList);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
freeJoinSubqueryObj(pParentSql);
|
||||
pParentSql->res.code = code;
|
||||
tscAsyncResultOnError(pParentSql);
|
||||
|
||||
taosArrayDestroy(s1);
|
||||
taosArrayDestroy(s2);
|
||||
taosArrayDestroy(resList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (taosArrayGetSize(s1) == 0 || taosArrayGetSize(s2) == 0) { // no results,return.
|
||||
if (emptyTagList(resList, pParentSql->subState.numOfSub)) { // no results,return.
|
||||
assert(pParentSql->fp != tscJoinQueryCallback);
|
||||
|
||||
tscDebug("%p tag intersect does not generated qualified tables for join, free all sub SqlObj and quit", pParentSql);
|
||||
|
@ -963,37 +1226,34 @@ static void tidTagRetrieveCallback(void* param, TAOS_RES* tres, int32_t numOfRow
|
|||
|
||||
(*pParentSql->fp)(pParentSql->param, pParentSql, 0);
|
||||
} else {
|
||||
for (int32_t m = 0; m < pParentSql->subState.numOfSub; ++m) {
|
||||
// proceed to for ts_comp query
|
||||
SSqlCmd* pSubCmd1 = &pParentSql->pSubs[0]->cmd;
|
||||
SSqlCmd* pSubCmd2 = &pParentSql->pSubs[1]->cmd;
|
||||
SSqlCmd* pSubCmd = &pParentSql->pSubs[m]->cmd;
|
||||
SArray** s = taosArrayGet(resList, m);
|
||||
|
||||
SQueryInfo* pQueryInfo1 = tscGetQueryInfoDetail(pSubCmd1, 0);
|
||||
STableMetaInfo* pTableMetaInfo1 = tscGetMetaInfo(pQueryInfo1, 0);
|
||||
tscBuildVgroupTableInfo(pParentSql, pTableMetaInfo1, s1);
|
||||
SQueryInfo* pQueryInfo = tscGetQueryInfoDetail(pSubCmd, 0);
|
||||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||
tscBuildVgroupTableInfo(pParentSql, pTableMetaInfo, *s);
|
||||
|
||||
SQueryInfo* pQueryInfo2 = tscGetQueryInfoDetail(pSubCmd2, 0);
|
||||
STableMetaInfo* pTableMetaInfo2 = tscGetMetaInfo(pQueryInfo2, 0);
|
||||
tscBuildVgroupTableInfo(pParentSql, pTableMetaInfo2, s2);
|
||||
|
||||
SSqlObj* psub1 = pParentSql->pSubs[0];
|
||||
((SJoinSupporter*)psub1->param)->pVgroupTables = tscVgroupTableInfoDup(pTableMetaInfo1->pVgroupTables);
|
||||
|
||||
SSqlObj* psub2 = pParentSql->pSubs[1];
|
||||
((SJoinSupporter*)psub2->param)->pVgroupTables = tscVgroupTableInfoDup(pTableMetaInfo2->pVgroupTables);
|
||||
|
||||
pParentSql->subState.numOfSub = 2;
|
||||
SSqlObj* psub = pParentSql->pSubs[m];
|
||||
((SJoinSupporter*)psub->param)->pVgroupTables = tscVgroupTableInfoDup(pTableMetaInfo->pVgroupTables);
|
||||
|
||||
memset(pParentSql->subState.states, 0, sizeof(pParentSql->subState.states[0]) * pParentSql->subState.numOfSub);
|
||||
tscDebug("%p reset all sub states to 0", pParentSql);
|
||||
|
||||
for (int32_t m = 0; m < pParentSql->subState.numOfSub; ++m) {
|
||||
SSqlObj* sub = pParentSql->pSubs[m];
|
||||
issueTsCompQuery(sub, sub->param, pParentSql);
|
||||
issueTsCompQuery(psub, psub->param, pParentSql);
|
||||
}
|
||||
}
|
||||
|
||||
taosArrayDestroy(s1);
|
||||
taosArrayDestroy(s2);
|
||||
size_t rsize = taosArrayGetSize(resList);
|
||||
for (int32_t i = 0; i < rsize; ++i) {
|
||||
SArray** s = taosArrayGet(resList, i);
|
||||
if (*s) {
|
||||
taosArrayDestroy(*s);
|
||||
}
|
||||
}
|
||||
|
||||
taosArrayDestroy(resList);
|
||||
}
|
||||
|
||||
static void tsCompRetrieveCallback(void* param, TAOS_RES* tres, int32_t numOfRows) {
|
||||
|
@ -1124,12 +1384,8 @@ static void tsCompRetrieveCallback(void* param, TAOS_RES* tres, int32_t numOfRow
|
|||
|
||||
tscDebug("%p all subquery retrieve ts complete, do ts block intersect", pParentSql);
|
||||
|
||||
// proceeds to launched secondary query to retrieve final data
|
||||
SJoinSupporter* p1 = pParentSql->pSubs[0]->param;
|
||||
SJoinSupporter* p2 = pParentSql->pSubs[1]->param;
|
||||
|
||||
STimeWindow win = TSWINDOW_INITIALIZER;
|
||||
int64_t num = doTSBlockIntersect(pParentSql, p1, p2, &win);
|
||||
int64_t num = doTSBlockIntersect(pParentSql, &win);
|
||||
if (num <= 0) { // no result during ts intersect
|
||||
tscDebug("%p no results generated in ts intersection, free all sub SqlObj and quit", pParentSql);
|
||||
freeJoinSubqueryObj(pParentSql);
|
||||
|
@ -1584,7 +1840,7 @@ int32_t tscCreateJoinSubquery(SSqlObj *pSql, int16_t tableIndex, SJoinSupporter
|
|||
SSqlCmd * pCmd = &pSql->cmd;
|
||||
SQueryInfo *pQueryInfo = tscGetQueryInfoDetail(pCmd, pCmd->clauseIndex);
|
||||
|
||||
pSql->res.qhandle = 0x1;
|
||||
pSql->res.qId = 0x1;
|
||||
assert(pSql->res.numOfRows == 0);
|
||||
|
||||
if (pSql->pSubs == NULL) {
|
||||
|
@ -1639,6 +1895,8 @@ int32_t tscCreateJoinSubquery(SSqlObj *pSql, int16_t tableIndex, SJoinSupporter
|
|||
pNewQueryInfo->limit.limit = -1;
|
||||
pNewQueryInfo->limit.offset = 0;
|
||||
|
||||
pNewQueryInfo->order.orderColId = INT32_MIN;
|
||||
|
||||
// backup the data and clear it in the sqlcmd object
|
||||
memset(&pNewQueryInfo->groupbyExpr, 0, sizeof(SSqlGroupbyExpr));
|
||||
|
||||
|
@ -2182,7 +2440,7 @@ int32_t tscHandleMasterSTableQuery(SSqlObj *pSql) {
|
|||
SColumnModel *pModel = NULL;
|
||||
SColumnModel *pFinalModel = NULL;
|
||||
|
||||
pRes->qhandle = 0x1; // hack the qhandle check
|
||||
pRes->qId = 0x1; // hack the qhandle check
|
||||
|
||||
const uint32_t nBufferSize = (1u << 16u); // 64KB
|
||||
|
||||
|
@ -2730,7 +2988,7 @@ void tscRetrieveDataRes(void *param, TAOS_RES *tres, int code) {
|
|||
tscDebug("%p sub:%p query complete, ep:%s, vgId:%d, orderOfSub:%d, retrieve data", trsupport->pParentSql, pSql,
|
||||
pVgroup->epAddr[0].fqdn, pVgroup->vgId, trsupport->subqueryIndex);
|
||||
|
||||
if (pSql->res.qhandle == 0) { // qhandle is NULL, code is TSDB_CODE_SUCCESS means no results generated from this vnode
|
||||
if (pSql->res.qId == 0) { // qhandle is NULL, code is TSDB_CODE_SUCCESS means no results generated from this vnode
|
||||
tscRetrieveFromDnodeCallBack(param, pSql, 0);
|
||||
} else {
|
||||
taos_fetch_rows_a(tres, tscRetrieveFromDnodeCallBack, param);
|
||||
|
|
|
@ -271,6 +271,41 @@ bool tscIsTWAQuery(SQueryInfo* pQueryInfo) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool tscIsTopbotQuery(SQueryInfo* pQueryInfo) {
|
||||
size_t numOfExprs = tscSqlExprNumOfExprs(pQueryInfo);
|
||||
for (int32_t i = 0; i < numOfExprs; ++i) {
|
||||
SSqlExpr* pExpr = tscSqlExprGet(pQueryInfo, i);
|
||||
if (pExpr == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t functionId = pExpr->functionId;
|
||||
if (functionId == TSDB_FUNC_TOP || functionId == TSDB_FUNC_BOTTOM) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t tscGetTopbotQueryParam(SQueryInfo* pQueryInfo) {
|
||||
size_t numOfExprs = tscSqlExprNumOfExprs(pQueryInfo);
|
||||
for (int32_t i = 0; i < numOfExprs; ++i) {
|
||||
SSqlExpr* pExpr = tscSqlExprGet(pQueryInfo, i);
|
||||
if (pExpr == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t functionId = pExpr->functionId;
|
||||
if (functionId == TSDB_FUNC_TOP || functionId == TSDB_FUNC_BOTTOM) {
|
||||
return (int32_t) pExpr->param[0].i64;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void tscClearInterpInfo(SQueryInfo* pQueryInfo) {
|
||||
if (!tscIsPointInterpQuery(pQueryInfo)) {
|
||||
return;
|
||||
|
@ -309,7 +344,7 @@ void tscSetResRawPtr(SSqlRes* pRes, SQueryInfo* pQueryInfo) {
|
|||
|
||||
int32_t offset = 0;
|
||||
|
||||
for (int32_t i = 0; i < pRes->numOfCols; ++i) {
|
||||
for (int32_t i = 0; i < pQueryInfo->fieldsInfo.numOfOutput; ++i) {
|
||||
SInternalField* pInfo = (SInternalField*)TARRAY_GET_ELEM(pQueryInfo->fieldsInfo.internalField, i);
|
||||
|
||||
pRes->urow[i] = pRes->data + offset * pRes->numOfRows;
|
||||
|
@ -415,6 +450,20 @@ void tscFreeQueryInfo(SSqlCmd* pCmd, bool removeMeta) {
|
|||
tfree(pCmd->pQueryInfo);
|
||||
}
|
||||
|
||||
void destroyTableNameList(SSqlCmd* pCmd) {
|
||||
if (pCmd->numOfTables == 0) {
|
||||
assert(pCmd->pTableNameList == NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
for(int32_t i = 0; i < pCmd->numOfTables; ++i) {
|
||||
tfree(pCmd->pTableNameList[i]);
|
||||
}
|
||||
|
||||
pCmd->numOfTables = 0;
|
||||
tfree(pCmd->pTableNameList);
|
||||
}
|
||||
|
||||
void tscResetSqlCmd(SSqlCmd* pCmd, bool removeMeta) {
|
||||
pCmd->command = 0;
|
||||
pCmd->numOfCols = 0;
|
||||
|
@ -424,14 +473,7 @@ void tscResetSqlCmd(SSqlCmd* pCmd, bool removeMeta) {
|
|||
pCmd->parseFinished = 0;
|
||||
pCmd->autoCreated = 0;
|
||||
|
||||
for(int32_t i = 0; i < pCmd->numOfTables; ++i) {
|
||||
if (pCmd->pTableNameList && pCmd->pTableNameList[i]) {
|
||||
tfree(pCmd->pTableNameList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pCmd->numOfTables = 0;
|
||||
tfree(pCmd->pTableNameList);
|
||||
destroyTableNameList(pCmd);
|
||||
|
||||
pCmd->pTableBlockHashList = tscDestroyBlockHashTable(pCmd->pTableBlockHashList, removeMeta);
|
||||
pCmd->pDataBlocks = tscDestroyBlockArrayList(pCmd->pDataBlocks);
|
||||
|
@ -447,7 +489,7 @@ void tscFreeSqlResult(SSqlObj* pSql) {
|
|||
memset(&pSql->res, 0, sizeof(SSqlRes));
|
||||
}
|
||||
|
||||
static void tscFreeSubobj(SSqlObj* pSql) {
|
||||
void tscFreeSubobj(SSqlObj* pSql) {
|
||||
if (pSql->subState.numOfSub == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -548,6 +590,11 @@ void tscFreeSqlObj(SSqlObj* pSql) {
|
|||
free(pSql);
|
||||
}
|
||||
|
||||
void tscDestroyBoundColumnInfo(SParsedDataColInfo* pColInfo) {
|
||||
tfree(pColInfo->boundedColumns);
|
||||
tfree(pColInfo->cols);
|
||||
}
|
||||
|
||||
void tscDestroyDataBlock(STableDataBlocks* pDataBlock, bool removeMeta) {
|
||||
if (pDataBlock == NULL) {
|
||||
return;
|
||||
|
@ -568,6 +615,7 @@ void tscDestroyDataBlock(STableDataBlocks* pDataBlock, bool removeMeta) {
|
|||
taosHashRemove(tscTableMetaInfo, name, strnlen(name, TSDB_TABLE_FNAME_LEN));
|
||||
}
|
||||
|
||||
tscDestroyBoundColumnInfo(&pDataBlock->boundColumnInfo);
|
||||
tfree(pDataBlock);
|
||||
}
|
||||
|
||||
|
@ -678,7 +726,7 @@ int32_t tscCopyDataBlockToPayload(SSqlObj* pSql, STableDataBlocks* pDataBlock) {
|
|||
* @param dataBlocks
|
||||
* @return
|
||||
*/
|
||||
int32_t tscCreateDataBlock(size_t initialSize, int32_t rowSize, int32_t startOffset, SName* name,
|
||||
int32_t tscCreateDataBlock(size_t defaultSize, int32_t rowSize, int32_t startOffset, SName* name,
|
||||
STableMeta* pTableMeta, STableDataBlocks** dataBlocks) {
|
||||
STableDataBlocks* dataBuf = (STableDataBlocks*)calloc(1, sizeof(STableDataBlocks));
|
||||
if (dataBuf == NULL) {
|
||||
|
@ -686,8 +734,10 @@ int32_t tscCreateDataBlock(size_t initialSize, int32_t rowSize, int32_t startOff
|
|||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
dataBuf->nAllocSize = (uint32_t)initialSize;
|
||||
dataBuf->headerSize = startOffset; // the header size will always be the startOffset value, reserved for the subumit block header
|
||||
dataBuf->nAllocSize = (uint32_t)defaultSize;
|
||||
dataBuf->headerSize = startOffset;
|
||||
|
||||
// the header size will always be the startOffset value, reserved for the subumit block header
|
||||
if (dataBuf->nAllocSize <= dataBuf->headerSize) {
|
||||
dataBuf->nAllocSize = dataBuf->headerSize * 2;
|
||||
}
|
||||
|
@ -699,25 +749,31 @@ int32_t tscCreateDataBlock(size_t initialSize, int32_t rowSize, int32_t startOff
|
|||
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
//Here we keep the tableMeta to avoid it to be remove by other threads.
|
||||
dataBuf->pTableMeta = tscTableMetaDup(pTableMeta);
|
||||
|
||||
SParsedDataColInfo* pColInfo = &dataBuf->boundColumnInfo;
|
||||
SSchema* pSchema = tscGetTableSchema(dataBuf->pTableMeta);
|
||||
tscSetBoundColumnInfo(pColInfo, pSchema, dataBuf->pTableMeta->tableInfo.numOfColumns);
|
||||
|
||||
dataBuf->ordered = true;
|
||||
dataBuf->prevTS = INT64_MIN;
|
||||
|
||||
dataBuf->rowSize = rowSize;
|
||||
dataBuf->size = startOffset;
|
||||
dataBuf->tsSource = -1;
|
||||
dataBuf->vgId = dataBuf->pTableMeta->vgId;
|
||||
|
||||
tNameAssign(&dataBuf->tableName, name);
|
||||
|
||||
//Here we keep the tableMeta to avoid it to be remove by other threads.
|
||||
dataBuf->pTableMeta = tscTableMetaDup(pTableMeta);
|
||||
assert(initialSize > 0 && pTableMeta != NULL && dataBuf->pTableMeta != NULL);
|
||||
assert(defaultSize > 0 && pTableMeta != NULL && dataBuf->pTableMeta != NULL);
|
||||
|
||||
*dataBlocks = dataBuf;
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t tscGetDataBlockFromList(SHashObj* pHashList, int64_t id, int32_t size, int32_t startOffset, int32_t rowSize,
|
||||
SName* name, STableMeta* pTableMeta, STableDataBlocks** dataBlocks, SArray* pBlockList) {
|
||||
SName* name, STableMeta* pTableMeta, STableDataBlocks** dataBlocks,
|
||||
SArray* pBlockList) {
|
||||
*dataBlocks = NULL;
|
||||
STableDataBlocks** t1 = (STableDataBlocks**)taosHashGet(pHashList, (const char*)&id, sizeof(id));
|
||||
if (t1 != NULL) {
|
||||
|
@ -826,6 +882,8 @@ static void extractTableNameList(SSqlCmd* pCmd, bool freeBlockMap) {
|
|||
int32_t i = 0;
|
||||
while(p1) {
|
||||
STableDataBlocks* pBlocks = *p1;
|
||||
tfree(pCmd->pTableNameList[i]);
|
||||
|
||||
pCmd->pTableNameList[i++] = tNameDup(&pBlocks->tableName);
|
||||
p1 = taosHashIterate(pCmd->pTableBlockHashList, p1);
|
||||
}
|
||||
|
@ -942,7 +1000,7 @@ bool tscIsInsertData(char* sqlstr) {
|
|||
int32_t index = 0;
|
||||
|
||||
do {
|
||||
SStrToken t0 = tStrGetToken(sqlstr, &index, false, 0, NULL);
|
||||
SStrToken t0 = tStrGetToken(sqlstr, &index, false);
|
||||
if (t0.type != TK_LP) {
|
||||
return t0.type == TK_INSERT || t0.type == TK_IMPORT;
|
||||
}
|
||||
|
@ -1279,6 +1337,34 @@ int32_t tscSqlExprCopy(SArray* dst, const SArray* src, uint64_t uid, bool deepco
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool tscColumnExists(SArray* pColumnList, SColumnIndex* pColIndex) {
|
||||
// ignore the tbname columnIndex to be inserted into source list
|
||||
if (pColIndex->columnIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t numOfCols = taosArrayGetSize(pColumnList);
|
||||
int16_t col = pColIndex->columnIndex;
|
||||
|
||||
int32_t i = 0;
|
||||
while (i < numOfCols) {
|
||||
SColumn* pCol = taosArrayGetP(pColumnList, i);
|
||||
if ((pCol->colIndex.columnIndex != col) || (pCol->colIndex.tableIndex != pColIndex->tableIndex)) {
|
||||
++i;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= numOfCols || numOfCols == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SColumn* tscColumnListInsert(SArray* pColumnList, SColumnIndex* pColIndex) {
|
||||
// ignore the tbname columnIndex to be inserted into source list
|
||||
if (pColIndex->columnIndex < 0) {
|
||||
|
@ -1583,7 +1669,25 @@ int32_t tscTagCondCopy(STagCond* dest, const STagCond* src) {
|
|||
dest->tbnameCond.uid = src->tbnameCond.uid;
|
||||
dest->tbnameCond.len = src->tbnameCond.len;
|
||||
|
||||
memcpy(&dest->joinInfo, &src->joinInfo, sizeof(SJoinInfo));
|
||||
dest->joinInfo.hasJoin = src->joinInfo.hasJoin;
|
||||
|
||||
for (int32_t i = 0; i < TSDB_MAX_JOIN_TABLE_NUM; ++i) {
|
||||
if (src->joinInfo.joinTables[i]) {
|
||||
dest->joinInfo.joinTables[i] = calloc(1, sizeof(SJoinNode));
|
||||
|
||||
memcpy(dest->joinInfo.joinTables[i], src->joinInfo.joinTables[i], sizeof(SJoinNode));
|
||||
|
||||
if (src->joinInfo.joinTables[i]->tsJoin) {
|
||||
dest->joinInfo.joinTables[i]->tsJoin = taosArrayDup(src->joinInfo.joinTables[i]->tsJoin);
|
||||
}
|
||||
|
||||
if (src->joinInfo.joinTables[i]->tagJoin) {
|
||||
dest->joinInfo.joinTables[i]->tagJoin = taosArrayDup(src->joinInfo.joinTables[i]->tagJoin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dest->relType = src->relType;
|
||||
|
||||
if (src->pCond == NULL) {
|
||||
|
@ -1629,6 +1733,23 @@ void tscTagCondRelease(STagCond* pTagCond) {
|
|||
taosArrayDestroy(pTagCond->pCond);
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < TSDB_MAX_JOIN_TABLE_NUM; ++i) {
|
||||
SJoinNode *node = pTagCond->joinInfo.joinTables[i];
|
||||
if (node == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node->tsJoin != NULL) {
|
||||
taosArrayDestroy(node->tsJoin);
|
||||
}
|
||||
|
||||
if (node->tagJoin != NULL) {
|
||||
taosArrayDestroy(node->tagJoin);
|
||||
}
|
||||
|
||||
tfree(node);
|
||||
}
|
||||
|
||||
memset(pTagCond, 0, sizeof(STagCond));
|
||||
}
|
||||
|
||||
|
@ -2318,15 +2439,20 @@ void tscDoQuery(SSqlObj* pSql) {
|
|||
}
|
||||
|
||||
int16_t tscGetJoinTagColIdByUid(STagCond* pTagCond, uint64_t uid) {
|
||||
if (pTagCond->joinInfo.left.uid == uid) {
|
||||
return pTagCond->joinInfo.left.tagColId;
|
||||
} else if (pTagCond->joinInfo.right.uid == uid) {
|
||||
return pTagCond->joinInfo.right.tagColId;
|
||||
} else {
|
||||
int32_t i = 0;
|
||||
while (i < TSDB_MAX_JOIN_TABLE_NUM) {
|
||||
SJoinNode* node = pTagCond->joinInfo.joinTables[i];
|
||||
if (node && node->uid == uid) {
|
||||
return node->tagColId;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int16_t tscGetTagColIndexById(STableMeta* pTableMeta, int16_t colId) {
|
||||
int32_t numOfTags = tscGetNumOfTags(pTableMeta);
|
||||
|
|
|
@ -51,6 +51,7 @@ enum {
|
|||
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_ALTER_ACCT, "alter-acct" )
|
||||
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_ALTER_TABLE, "alter-table" )
|
||||
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_ALTER_DB, "alter-db" )
|
||||
TSDB_DEFINE_SQL_TYPE(TSDB_SQL_SYNC_DB_REPLICA, "sync db-replica")
|
||||
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_CREATE_MNODE, "create-mnode" )
|
||||
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_DROP_MNODE, "drop-mnode" )
|
||||
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_CREATE_DNODE, "create-dnode" )
|
||||
|
|
|
@ -51,7 +51,7 @@ int32_t tsMaxShellConns = 50000;
|
|||
int32_t tsMaxConnections = 5000;
|
||||
int32_t tsShellActivityTimer = 3; // second
|
||||
float tsNumOfThreadsPerCore = 1.0f;
|
||||
int32_t tsNumOfCommitThreads = 1;
|
||||
int32_t tsNumOfCommitThreads = 4;
|
||||
float tsRatioOfQueryCores = 1.0f;
|
||||
int8_t tsDaylight = 0;
|
||||
char tsTimezone[TSDB_TIMEZONE_LEN] = {0};
|
||||
|
@ -71,7 +71,7 @@ int32_t tsMaxBinaryDisplayWidth = 30;
|
|||
int32_t tsCompressMsgSize = -1;
|
||||
|
||||
// client
|
||||
int32_t tsMaxSQLStringLen = TSDB_MAX_SQL_LEN;
|
||||
int32_t tsMaxSQLStringLen = TSDB_MAX_ALLOWED_SQL_LEN;
|
||||
int8_t tsTscEnableRecordSql = 0;
|
||||
|
||||
// the maximum number of results for projection query on super table that are returned from
|
||||
|
|
|
@ -8,7 +8,7 @@ IF (TD_MVN_INSTALLED)
|
|||
ADD_CUSTOM_COMMAND(OUTPUT ${JDBC_CMD_NAME}
|
||||
POST_BUILD
|
||||
COMMAND mvn -Dmaven.test.skip=true install -f ${CMAKE_CURRENT_SOURCE_DIR}/pom.xml
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/target/taos-jdbcdriver-2.0.22-dist.jar ${LIBRARY_OUTPUT_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/target/taos-jdbcdriver-2.0.25-dist.jar ${LIBRARY_OUTPUT_PATH}
|
||||
COMMAND mvn -Dmaven.test.skip=true clean -f ${CMAKE_CURRENT_SOURCE_DIR}/pom.xml
|
||||
COMMENT "build jdbc driver")
|
||||
ADD_CUSTOM_TARGET(${JDBC_TARGET_NAME} ALL WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} DEPENDS ${JDBC_CMD_NAME})
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
<version>2.0.22</version>
|
||||
<version>2.0.25</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>JDBCDriver</name>
|
||||
|
@ -37,17 +37,6 @@
|
|||
</developers>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
@ -61,21 +50,20 @@
|
|||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.58</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>29.0-jre</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
<version>2.0.22</version>
|
||||
<version>2.0.25</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>JDBCDriver</name>
|
||||
<url>https://github.com/taosdata/TDengine/tree/master/src/connector/jdbc</url>
|
||||
|
@ -43,7 +43,6 @@
|
|||
<version>4.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- for restful -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
|
@ -55,10 +54,22 @@
|
|||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.58</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>29.0-jre</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.md</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
|
|
@ -30,9 +30,12 @@ public abstract class AbstractConnection extends WrapperImpl implements Connecti
|
|||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED);
|
||||
// do nothing
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setAutoCommit(boolean autoCommit) throws SQLException {
|
||||
if (isClosed())
|
||||
|
@ -448,7 +451,6 @@ public abstract class AbstractConnection extends WrapperImpl implements Connecti
|
|||
if (isClosed)
|
||||
throw (SQLClientInfoException) TSDBError.createSQLException(TSDBErrorNumbers.ERROR_SQLCLIENT_EXCEPTION_ON_CONNECTION_CLOSED);
|
||||
|
||||
|
||||
for (Enumeration<Object> enumer = properties.keys(); enumer.hasMoreElements(); ) {
|
||||
String name = (String) enumer.nextElement();
|
||||
clientInfoProps.put(name, properties.getProperty(name));
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
package com.taosdata.jdbc;
|
||||
|
||||
import java.sql.ParameterMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
|
||||
public abstract class AbstractParameterMetaData extends WrapperImpl implements ParameterMetaData {
|
||||
|
||||
private final Object[] parameters;
|
||||
|
||||
public AbstractParameterMetaData(Object[] parameters) {
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParameterCount() throws SQLException {
|
||||
return parameters == null ? 0 : parameters.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int isNullable(int param) throws SQLException {
|
||||
return ParameterMetaData.parameterNullableUnknown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSigned(int param) throws SQLException {
|
||||
if (param < 1 && param >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
if (parameters[param - 1] instanceof Byte)
|
||||
return true;
|
||||
if (parameters[param - 1] instanceof Short)
|
||||
return true;
|
||||
if (parameters[param - 1] instanceof Integer)
|
||||
return true;
|
||||
if (parameters[param - 1] instanceof Long)
|
||||
return true;
|
||||
if (parameters[param - 1] instanceof Float)
|
||||
return true;
|
||||
if (parameters[param - 1] instanceof Double)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPrecision(int param) throws SQLException {
|
||||
if (param < 1 && param >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
if (parameters[param - 1] instanceof String)
|
||||
return ((String) parameters[param - 1]).length();
|
||||
if (parameters[param - 1] instanceof byte[])
|
||||
return ((byte[]) parameters[param - 1]).length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScale(int param) throws SQLException {
|
||||
if (param < 1 && param >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParameterType(int param) throws SQLException {
|
||||
if (param < 1 && param >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
if (parameters[param - 1] instanceof Timestamp)
|
||||
return Types.TIMESTAMP;
|
||||
if (parameters[param - 1] instanceof Byte)
|
||||
return Types.TINYINT;
|
||||
if (parameters[param - 1] instanceof Short)
|
||||
return Types.SMALLINT;
|
||||
if (parameters[param - 1] instanceof Integer)
|
||||
return Types.INTEGER;
|
||||
if (parameters[param - 1] instanceof Long)
|
||||
return Types.BIGINT;
|
||||
if (parameters[param - 1] instanceof Float)
|
||||
return Types.FLOAT;
|
||||
if (parameters[param - 1] instanceof Double)
|
||||
return Types.DOUBLE;
|
||||
if (parameters[param - 1] instanceof String)
|
||||
return Types.NCHAR;
|
||||
if (parameters[param - 1] instanceof byte[])
|
||||
return Types.BINARY;
|
||||
if (parameters[param - 1] instanceof Boolean)
|
||||
return Types.BOOLEAN;
|
||||
return Types.OTHER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameterTypeName(int param) throws SQLException {
|
||||
if (param < 1 && param >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
if (parameters[param - 1] instanceof Timestamp)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.TIMESTAMP);
|
||||
if (parameters[param - 1] instanceof Byte)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.TINYINT);
|
||||
if (parameters[param - 1] instanceof Short)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.SMALLINT);
|
||||
if (parameters[param - 1] instanceof Integer)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.INTEGER);
|
||||
if (parameters[param - 1] instanceof Long)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.BIGINT);
|
||||
if (parameters[param - 1] instanceof Float)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.FLOAT);
|
||||
if (parameters[param - 1] instanceof Double)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.DOUBLE);
|
||||
if (parameters[param - 1] instanceof String)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.NCHAR);
|
||||
if (parameters[param - 1] instanceof byte[])
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.BINARY);
|
||||
if (parameters[param - 1] instanceof Boolean)
|
||||
return TSDBConstants.jdbcType2TaosTypeName(Types.BOOLEAN);
|
||||
|
||||
return parameters[param - 1].getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameterClassName(int param) throws SQLException {
|
||||
if (param < 1 && param >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
return parameters[param - 1].getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParameterMode(int param) throws SQLException {
|
||||
if (param < 1 && param >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
return ParameterMetaData.parameterModeUnknown;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,15 @@ import java.util.Map;
|
|||
public abstract class AbstractResultSet extends WrapperImpl implements ResultSet {
|
||||
private int fetchSize;
|
||||
|
||||
protected void checkAvailability(int columnIndex, int bounds) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex < 1)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " < 1");
|
||||
if (columnIndex > bounds)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean next() throws SQLException;
|
||||
|
||||
|
@ -46,38 +55,20 @@ public abstract class AbstractResultSet extends WrapperImpl implements ResultSet
|
|||
@Override
|
||||
public abstract double getDouble(int columnIndex) throws SQLException;
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
return getBigDecimal(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
}
|
||||
public abstract byte[] getBytes(int columnIndex) throws SQLException;
|
||||
|
||||
@Override
|
||||
public Date getDate(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
|
||||
}
|
||||
public abstract Date getDate(int columnIndex) throws SQLException;
|
||||
|
||||
@Override
|
||||
public Time getTime(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
}
|
||||
public abstract Time getTime(int columnIndex) throws SQLException;
|
||||
|
||||
@Override
|
||||
public abstract Timestamp getTimestamp(int columnIndex) throws SQLException;
|
||||
|
@ -147,9 +138,10 @@ public abstract class AbstractResultSet extends WrapperImpl implements ResultSet
|
|||
return getDouble(findColumn(columnLabel));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException {
|
||||
return getBigDecimal(findColumn(columnLabel));
|
||||
return getBigDecimal(findColumn(columnLabel), scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -214,12 +206,7 @@ public abstract class AbstractResultSet extends WrapperImpl implements ResultSet
|
|||
public abstract ResultSetMetaData getMetaData() throws SQLException;
|
||||
|
||||
@Override
|
||||
public Object getObject(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
}
|
||||
public abstract Object getObject(int columnIndex) throws SQLException;
|
||||
|
||||
@Override
|
||||
public Object getObject(String columnLabel) throws SQLException {
|
||||
|
@ -243,12 +230,7 @@ public abstract class AbstractResultSet extends WrapperImpl implements ResultSet
|
|||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
}
|
||||
public abstract BigDecimal getBigDecimal(int columnIndex) throws SQLException;
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
|
||||
|
@ -718,9 +700,7 @@ public abstract class AbstractResultSet extends WrapperImpl implements ResultSet
|
|||
|
||||
@Override
|
||||
public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
return getObject(findColumn(columnLabel), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -760,9 +740,7 @@ public abstract class AbstractResultSet extends WrapperImpl implements ResultSet
|
|||
|
||||
@Override
|
||||
public Date getDate(String columnLabel, Calendar cal) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
return getDate(findColumn(columnLabel), cal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -774,23 +752,15 @@ public abstract class AbstractResultSet extends WrapperImpl implements ResultSet
|
|||
|
||||
@Override
|
||||
public Time getTime(String columnLabel, Calendar cal) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
return getTime(findColumn(columnLabel), cal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
}
|
||||
public abstract Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException;
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
return getTimestamp(findColumn(columnLabel), cal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1198,9 +1168,7 @@ public abstract class AbstractResultSet extends WrapperImpl implements ResultSet
|
|||
|
||||
@Override
|
||||
public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
return getObject(findColumn(columnLabel), type);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ public abstract class AbstractStatement extends WrapperImpl implements Statement
|
|||
protected List<String> batchedArgs;
|
||||
private int fetchSize;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public abstract ResultSet executeQuery(String sql) throws SQLException;
|
||||
|
||||
|
|
|
@ -1,452 +0,0 @@
|
|||
package com.taosdata.jdbc;
|
||||
|
||||
import com.taosdata.jdbc.bean.TSDBPreparedParam;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* this class is used to precompile the sql of tdengine insert or import ops
|
||||
*/
|
||||
public class SavedPreparedStatement {
|
||||
|
||||
private TSDBPreparedStatement tsdbPreparedStatement;
|
||||
|
||||
/**
|
||||
* sql param List
|
||||
*/
|
||||
private List<TSDBPreparedParam> sqlParamList;
|
||||
|
||||
/**
|
||||
* init param according the sql
|
||||
*/
|
||||
private TSDBPreparedParam initPreparedParam;
|
||||
|
||||
/**
|
||||
* is table name dynamic in the prepared sql
|
||||
*/
|
||||
private boolean isTableNameDynamic;
|
||||
|
||||
/**
|
||||
* insert or import sql template pattern, the template are the following:
|
||||
* <p>
|
||||
* insert/import into tableName [(field1, field2, ...)] [using stables tags(?, ?, ...) ] values(?, ?, ...) (?, ?, ...)
|
||||
* <p>
|
||||
* we split it to three part:
|
||||
* 1. prefix, insert/import
|
||||
* 2. middle, tableName [(field1, field2, ...)] [using stables tags(?, ?, ...) ]
|
||||
* 3. valueList, the content after values, for example (?, ?, ...) (?, ?, ...)
|
||||
*/
|
||||
private Pattern sqlPattern = Pattern.compile("(?s)(?i)^\\s*(INSERT|IMPORT)\\s+INTO\\s+((?<tablename>\\S+)\\s*(\\(.*\\))?\\s+(USING\\s+(?<stableName>\\S+)\\s+TAGS\\s*\\((?<tagValue>.+)\\))?)\\s*VALUES\\s*(?<valueList>\\(.*\\)).*");
|
||||
|
||||
/**
|
||||
* the raw sql template
|
||||
*/
|
||||
private String sql;
|
||||
|
||||
/**
|
||||
* the prefix part of sql
|
||||
*/
|
||||
private String prefix;
|
||||
|
||||
/**
|
||||
* the middle part of sql
|
||||
*/
|
||||
private String middle;
|
||||
|
||||
private int middleParamSize;
|
||||
|
||||
/**
|
||||
* the valueList part of sql
|
||||
*/
|
||||
private String valueList;
|
||||
|
||||
private int valueListSize;
|
||||
|
||||
/**
|
||||
* default param value
|
||||
*/
|
||||
private static final String DEFAULT_VALUE = "NULL";
|
||||
|
||||
private static final String PLACEHOLDER = "?";
|
||||
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* is the parameter add to batch list
|
||||
*/
|
||||
private boolean isAddBatch;
|
||||
|
||||
public SavedPreparedStatement(String sql, TSDBPreparedStatement tsdbPreparedStatement) throws SQLException {
|
||||
this.sql = sql;
|
||||
this.tsdbPreparedStatement = tsdbPreparedStatement;
|
||||
this.sqlParamList = new ArrayList<>();
|
||||
|
||||
parsePreparedParam(this.sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the init param according the sql param
|
||||
*
|
||||
* @param sql
|
||||
*/
|
||||
private void parsePreparedParam(String sql) throws SQLException {
|
||||
|
||||
Matcher matcher = sqlPattern.matcher(sql);
|
||||
|
||||
if (matcher.find()) {
|
||||
|
||||
tableName = matcher.group("tablename");
|
||||
|
||||
if (tableName != null && PLACEHOLDER.equals(tableName)) {
|
||||
// the table name is dynamic
|
||||
this.isTableNameDynamic = true;
|
||||
}
|
||||
|
||||
prefix = matcher.group(1);
|
||||
middle = matcher.group(2);
|
||||
valueList = matcher.group("valueList");
|
||||
|
||||
if (middle != null && !"".equals(middle)) {
|
||||
middleParamSize = parsePlaceholder(middle);
|
||||
}
|
||||
|
||||
if (valueList != null && !"".equals(valueList)) {
|
||||
valueListSize = parsePlaceholder(valueList);
|
||||
}
|
||||
|
||||
initPreparedParam = initDefaultParam(tableName, middleParamSize, valueListSize);
|
||||
|
||||
} else {
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_SQL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private TSDBPreparedParam initDefaultParam(String tableName, int middleParamSize, int valueListSize) {
|
||||
|
||||
TSDBPreparedParam tsdbPreparedParam = new TSDBPreparedParam(tableName);
|
||||
|
||||
tsdbPreparedParam.setMiddleParamList(getDefaultParamList(middleParamSize));
|
||||
|
||||
tsdbPreparedParam.setValueList(getDefaultParamList(valueListSize));
|
||||
|
||||
return tsdbPreparedParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate the default param value list
|
||||
*
|
||||
* @param paramSize
|
||||
* @return
|
||||
*/
|
||||
private List<Object> getDefaultParamList(int paramSize) {
|
||||
|
||||
List<Object> paramList = new ArrayList<>(paramSize);
|
||||
if (paramSize > 0) {
|
||||
for (int i = 0; i < paramSize; i++) {
|
||||
paramList.add(i, DEFAULT_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return paramList;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate the placeholder num
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
private int parsePlaceholder(String value) {
|
||||
|
||||
Pattern pattern = Pattern.compile("[?]");
|
||||
|
||||
Matcher matcher = pattern.matcher(value);
|
||||
|
||||
int result = 0;
|
||||
while (matcher.find()) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* set current row params
|
||||
*
|
||||
* @param parameterIndex the first parameter is 1, the second is 2, ...
|
||||
* @param x the parameter value
|
||||
*/
|
||||
public void setParam(int parameterIndex, Object x) throws SQLException {
|
||||
|
||||
int paramSize = this.middleParamSize + this.valueListSize;
|
||||
|
||||
String errorMsg = String.format("the parameterIndex %s out of the range [1, %s]", parameterIndex, paramSize);
|
||||
|
||||
if (parameterIndex < 1 || parameterIndex > paramSize) {
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE,errorMsg);
|
||||
}
|
||||
|
||||
this.isAddBatch = false; //set isAddBatch to false
|
||||
|
||||
if (x == null) {
|
||||
x = DEFAULT_VALUE; // set default null string
|
||||
}
|
||||
|
||||
parameterIndex = parameterIndex - 1; // start from 0 in param list
|
||||
|
||||
if (this.middleParamSize > 0 && parameterIndex >= 0 && parameterIndex < this.middleParamSize) {
|
||||
|
||||
this.initPreparedParam.setMiddleParam(parameterIndex, x);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.valueListSize > 0 && parameterIndex >= this.middleParamSize && parameterIndex < paramSize) {
|
||||
|
||||
this.initPreparedParam.setValueParam(parameterIndex - this.middleParamSize, x);
|
||||
return;
|
||||
}
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE,errorMsg);
|
||||
}
|
||||
|
||||
public void addBatch() {
|
||||
|
||||
addCurrentRowParamToList();
|
||||
this.initPreparedParam = initDefaultParam(tableName, middleParamSize, valueListSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* add current param to batch list
|
||||
*/
|
||||
private void addCurrentRowParamToList() {
|
||||
|
||||
if (initPreparedParam != null && (this.middleParamSize > 0 || this.valueListSize > 0)) {
|
||||
this.sqlParamList.add(initPreparedParam); // add current param to batch list
|
||||
}
|
||||
this.isAddBatch = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* execute the sql with batch sql
|
||||
*
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public int[] executeBatch() throws SQLException {
|
||||
|
||||
int result = executeBatchInternal();
|
||||
|
||||
return new int[]{result};
|
||||
}
|
||||
|
||||
|
||||
public int executeBatchInternal() throws SQLException {
|
||||
|
||||
if (!isAddBatch) {
|
||||
addCurrentRowParamToList(); // add current param to batch list
|
||||
}
|
||||
|
||||
//1. generate batch sql
|
||||
String sql = generateExecuteSql();
|
||||
//2. execute batch sql
|
||||
int result = executeSql(sql);
|
||||
|
||||
//3. clear batch param list
|
||||
this.sqlParamList.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate the batch sql
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String generateExecuteSql() {
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.append(prefix);
|
||||
stringBuilder.append(" into ");
|
||||
|
||||
if (!isTableNameDynamic) {
|
||||
// tablename will not need to be replaced
|
||||
String middleValue = replaceMiddleListParam(middle, sqlParamList);
|
||||
stringBuilder.append(middleValue);
|
||||
stringBuilder.append(" values");
|
||||
|
||||
stringBuilder.append(replaceValueListParam(valueList, sqlParamList));
|
||||
|
||||
} else {
|
||||
// need to replace tablename
|
||||
|
||||
if (sqlParamList.size() > 0) {
|
||||
|
||||
TSDBPreparedParam firstPreparedParam = sqlParamList.get(0);
|
||||
|
||||
//replace middle part and value part of first row
|
||||
String firstRow = replaceMiddleAndValuePart(firstPreparedParam);
|
||||
stringBuilder.append(firstRow);
|
||||
|
||||
//the first param in the middleParamList is the tableName
|
||||
String lastTableName = firstPreparedParam.getMiddleParamList().get(0).toString();
|
||||
|
||||
if (sqlParamList.size() > 1) {
|
||||
|
||||
for (int i = 1; i < sqlParamList.size(); i++) {
|
||||
TSDBPreparedParam currentParam = sqlParamList.get(i);
|
||||
String currentTableName = currentParam.getMiddleParamList().get(0).toString();
|
||||
if (lastTableName.equalsIgnoreCase(currentTableName)) {
|
||||
// tablename is same with the last row ,so only need to append the part of value
|
||||
|
||||
String values = replaceTemplateParam(valueList, currentParam.getValueList());
|
||||
stringBuilder.append(values);
|
||||
|
||||
} else {
|
||||
// tablename difference with the last row
|
||||
//need to replace middle part and value part
|
||||
String row = replaceMiddleAndValuePart(currentParam);
|
||||
stringBuilder.append(row);
|
||||
lastTableName = currentTableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
stringBuilder.append(middle);
|
||||
stringBuilder.append(" values");
|
||||
stringBuilder.append(valueList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* replace the middle and value part
|
||||
*
|
||||
* @param tsdbPreparedParam
|
||||
* @return
|
||||
*/
|
||||
private String replaceMiddleAndValuePart(TSDBPreparedParam tsdbPreparedParam) {
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder(" ");
|
||||
|
||||
String middlePart = replaceTemplateParam(middle, tsdbPreparedParam.getMiddleParamList());
|
||||
|
||||
stringBuilder.append(middlePart);
|
||||
stringBuilder.append(" values ");
|
||||
|
||||
String valuePart = replaceTemplateParam(valueList, tsdbPreparedParam.getValueList());
|
||||
stringBuilder.append(valuePart);
|
||||
stringBuilder.append(" ");
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* replace the placeholder of the middle part of sql template with TSDBPreparedParam list
|
||||
*
|
||||
* @param template
|
||||
* @param sqlParamList
|
||||
* @return
|
||||
*/
|
||||
private String replaceMiddleListParam(String template, List<TSDBPreparedParam> sqlParamList) {
|
||||
|
||||
if (sqlParamList.size() > 0) {
|
||||
|
||||
//becase once the subTableName is static then will be ignore the tag which after the first setTag
|
||||
return replaceTemplateParam(template, sqlParamList.get(0).getMiddleParamList());
|
||||
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* replace the placeholder of the template with TSDBPreparedParam list
|
||||
*
|
||||
* @param template
|
||||
* @param sqlParamList
|
||||
* @return
|
||||
*/
|
||||
private String replaceValueListParam(String template, List<TSDBPreparedParam> sqlParamList) {
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
if (sqlParamList.size() > 0) {
|
||||
|
||||
for (TSDBPreparedParam tsdbPreparedParam : sqlParamList) {
|
||||
|
||||
String tmp = replaceTemplateParam(template, tsdbPreparedParam.getValueList());
|
||||
|
||||
stringBuilder.append(tmp);
|
||||
}
|
||||
|
||||
} else {
|
||||
stringBuilder.append(template);
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* replace the placeholder of the template with paramList
|
||||
*
|
||||
* @param template
|
||||
* @param paramList
|
||||
* @return
|
||||
*/
|
||||
private String replaceTemplateParam(String template, List<Object> paramList) {
|
||||
|
||||
if (paramList.size() > 0) {
|
||||
|
||||
String tmp = template;
|
||||
|
||||
for (int i = 0; i < paramList.size(); ++i) {
|
||||
|
||||
String paraStr = getParamString(paramList.get(i));
|
||||
|
||||
tmp = tmp.replaceFirst("[" + PLACEHOLDER + "]", paraStr);
|
||||
|
||||
}
|
||||
|
||||
return tmp;
|
||||
|
||||
} else {
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get the string of param object
|
||||
*
|
||||
* @param paramObj
|
||||
* @return
|
||||
*/
|
||||
private String getParamString(Object paramObj) {
|
||||
|
||||
String paraStr = paramObj.toString();
|
||||
if (paramObj instanceof Timestamp || (paramObj instanceof String && !DEFAULT_VALUE.equalsIgnoreCase(paraStr))) {
|
||||
paraStr = "'" + paraStr + "'";
|
||||
}
|
||||
return paraStr;
|
||||
}
|
||||
|
||||
|
||||
private int executeSql(String sql) throws SQLException {
|
||||
|
||||
return tsdbPreparedStatement.executeUpdate(sql);
|
||||
}
|
||||
|
||||
}
|
|
@ -27,10 +27,6 @@ public class TSDBConnection extends AbstractConnection {
|
|||
return this.batchFetch;
|
||||
}
|
||||
|
||||
public void setBatchFetch(Boolean batchFetch) {
|
||||
this.batchFetch = batchFetch;
|
||||
}
|
||||
|
||||
public TSDBConnection(Properties info, TSDBDatabaseMetaData meta) throws SQLException {
|
||||
this.databaseMetaData = meta;
|
||||
connect(info.getProperty(TSDBDriver.PROPERTY_KEY_HOST),
|
||||
|
@ -61,9 +57,7 @@ public class TSDBConnection extends AbstractConnection {
|
|||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED);
|
||||
}
|
||||
|
||||
TSDBStatement statement = new TSDBStatement(this, this.connector);
|
||||
statement.setConnection(this);
|
||||
return statement;
|
||||
return new TSDBStatement(this, this.connector);
|
||||
}
|
||||
|
||||
public TSDBSubscribe subscribe(String topic, String sql, boolean restart) throws SQLException {
|
||||
|
@ -79,10 +73,8 @@ public class TSDBConnection extends AbstractConnection {
|
|||
}
|
||||
|
||||
public PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||
if (isClosed()) {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED);
|
||||
}
|
||||
|
||||
return new TSDBPreparedStatement(this, this.connector, sql);
|
||||
}
|
||||
|
||||
|
@ -104,11 +96,4 @@ public class TSDBConnection extends AbstractConnection {
|
|||
return this.databaseMetaData;
|
||||
}
|
||||
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
if (isClosed()) {
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED);
|
||||
}
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
}
|
||||
|
||||
}
|
|
@ -29,45 +29,25 @@ public class TSDBJNIConnector {
|
|||
private static volatile Boolean isInitialized = false;
|
||||
|
||||
private TaosInfo taosInfo = TaosInfo.getInstance();
|
||||
// Connection pointer used in C
|
||||
private long taos = TSDBConstants.JNI_NULL_POINTER;
|
||||
// result set status in current connection
|
||||
private boolean isResultsetClosed = true;
|
||||
private int affectedRows = -1;
|
||||
|
||||
static {
|
||||
System.loadLibrary("taos");
|
||||
System.out.println("java.library.path:" + System.getProperty("java.library.path"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection pointer used in C
|
||||
*/
|
||||
private long taos = TSDBConstants.JNI_NULL_POINTER;
|
||||
|
||||
/**
|
||||
* Result set pointer for the current connection
|
||||
*/
|
||||
// private long taosResultSetPointer = TSDBConstants.JNI_NULL_POINTER;
|
||||
|
||||
/**
|
||||
* result set status in current connection
|
||||
*/
|
||||
private boolean isResultsetClosed = true;
|
||||
private int affectedRows = -1;
|
||||
|
||||
/**
|
||||
* Whether the connection is closed
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return this.taos == TSDBConstants.JNI_NULL_POINTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of last result set in current connection
|
||||
*/
|
||||
public boolean isResultsetClosed() {
|
||||
return this.isResultsetClosed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize static variables in JNI to optimize performance
|
||||
*/
|
||||
public static void init(String configDir, String locale, String charset, String timezone) throws SQLWarning {
|
||||
synchronized (isInitialized) {
|
||||
if (!isInitialized) {
|
||||
|
@ -93,11 +73,6 @@ public class TSDBJNIConnector {
|
|||
|
||||
public static native String getTsCharset();
|
||||
|
||||
/**
|
||||
* Get connection pointer
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
public boolean connect(String host, int port, String dbName, String user, String password) throws SQLException {
|
||||
if (this.taos != TSDBConstants.JNI_NULL_POINTER) {
|
||||
// this.closeConnectionImp(this.taos);
|
||||
|
@ -185,13 +160,6 @@ public class TSDBJNIConnector {
|
|||
|
||||
private native String getErrMsgImp(long pSql);
|
||||
|
||||
/**
|
||||
* Get resultset pointer
|
||||
* Each connection should have a single open result set at a time
|
||||
*/
|
||||
// public long getResultSet() {
|
||||
// return taosResultSetPointer;
|
||||
// }
|
||||
private native long getResultSetImp(long connection, long pSql);
|
||||
|
||||
public boolean isUpdateQuery(long pSql) {
|
||||
|
@ -231,6 +199,7 @@ public class TSDBJNIConnector {
|
|||
// }
|
||||
// return resCode;
|
||||
// }
|
||||
|
||||
private native int freeResultSetImp(long connection, long result);
|
||||
|
||||
/**
|
||||
|
@ -323,8 +292,7 @@ public class TSDBJNIConnector {
|
|||
* Validate if a <I>create table</I> sql statement is correct without actually creating that table
|
||||
*/
|
||||
public boolean validateCreateTableSql(String sql) {
|
||||
long connection = taos;
|
||||
int res = validateCreateTableSqlImp(connection, sql.getBytes());
|
||||
int res = validateCreateTableSqlImp(taos, sql.getBytes());
|
||||
return res != 0 ? false : true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package com.taosdata.jdbc;
|
||||
|
||||
public class TSDBParameterMetaData extends AbstractParameterMetaData {
|
||||
|
||||
public TSDBParameterMetaData(Object[] parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ import java.io.InputStream;
|
|||
import java.io.Reader;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
|
@ -30,36 +31,49 @@ import java.util.regex.Pattern;
|
|||
* compatibility needs.
|
||||
*/
|
||||
public class TSDBPreparedStatement extends TSDBStatement implements PreparedStatement {
|
||||
protected String rawSql;
|
||||
protected String sql;
|
||||
protected ArrayList<Object> parameters = new ArrayList<>();
|
||||
|
||||
private String rawSql;
|
||||
private String sql;
|
||||
// private ArrayList<Object> parameters = new ArrayList<>();
|
||||
private Object[] parameters;
|
||||
private boolean isPrepared;
|
||||
|
||||
//start with insert or import and is case-insensitive
|
||||
private static Pattern savePattern = Pattern.compile("(?i)^\\s*(insert|import)");
|
||||
|
||||
// is insert or import
|
||||
private boolean isSaved;
|
||||
|
||||
private SavedPreparedStatement savedPreparedStatement;
|
||||
private ParameterMetaData parameterMetaData;
|
||||
// private SavedPreparedStatement savedPreparedStatement;
|
||||
private volatile TSDBParameterMetaData parameterMetaData;
|
||||
|
||||
TSDBPreparedStatement(TSDBConnection connection, TSDBJNIConnector connecter, String sql) {
|
||||
super(connection, connecter);
|
||||
init(sql);
|
||||
|
||||
if (sql.contains("?")) {
|
||||
int parameterCnt = 0;
|
||||
for (int i = 0; i < sql.length(); i++) {
|
||||
if ('?' == sql.charAt(i)) {
|
||||
parameterCnt++;
|
||||
}
|
||||
}
|
||||
parameters = new Object[parameterCnt];
|
||||
this.isPrepared = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void init(String sql) {
|
||||
this.rawSql = sql;
|
||||
preprocessSql();
|
||||
// this.isSaved = isSavedSql(this.rawSql);
|
||||
// if (this.isSaved) {
|
||||
// try {
|
||||
// this.savedPreparedStatement = new SavedPreparedStatement(this.rawSql, this);
|
||||
// } catch (SQLException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
|
||||
this.isSaved = isSavedSql(this.rawSql);
|
||||
if (this.isSaved) {
|
||||
try {
|
||||
this.savedPreparedStatement = new SavedPreparedStatement(this.rawSql, this);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,19 +89,11 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
|
|||
|
||||
@Override
|
||||
public int[] executeBatch() throws SQLException {
|
||||
if (isSaved) {
|
||||
return this.savedPreparedStatement.executeBatch();
|
||||
} else {
|
||||
// if (isSaved) {
|
||||
// return this.savedPreparedStatement.executeBatch();
|
||||
// } else {
|
||||
return super.executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<Object> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public void setParameters(ArrayList<Object> parameters) {
|
||||
this.parameters = parameters;
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -151,41 +157,73 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
|
|||
*
|
||||
* @return a string of the native sql statement for TSDB
|
||||
*/
|
||||
private String getNativeSql() {
|
||||
this.sql = this.rawSql;
|
||||
for (int i = 0; i < parameters.size(); ++i) {
|
||||
Object para = parameters.get(i);
|
||||
// private String getNativeSql(String rawSql) {
|
||||
// for (int i = 0; i < parameters.length; i++) {
|
||||
// Object para = parameters[i];
|
||||
// if (para != null) {
|
||||
// String paraStr = para.toString();
|
||||
// if (para instanceof Timestamp || para instanceof String) {
|
||||
// paraStr = "'" + paraStr + "'";
|
||||
// }
|
||||
// this.sql = this.sql.replaceFirst("[?]", paraStr);
|
||||
// } else {
|
||||
// this.sql = this.sql.replaceFirst("[?]", "NULL");
|
||||
// }
|
||||
// }
|
||||
// parameters = new Object[parameters.length];
|
||||
// return sql;
|
||||
// }
|
||||
|
||||
private String getNativeSql(String rawSql) throws SQLException {
|
||||
String sql = rawSql;
|
||||
for (int i = 0; i < parameters.length; ++i) {
|
||||
Object para = parameters[i];
|
||||
if (para != null) {
|
||||
String paraStr = para.toString();
|
||||
if (para instanceof Timestamp || para instanceof String) {
|
||||
String paraStr;
|
||||
if (para instanceof byte[]) {
|
||||
paraStr = new String((byte[]) para, Charset.forName("UTF-8"));
|
||||
} else {
|
||||
paraStr = para.toString();
|
||||
}
|
||||
// if para is timestamp or String or byte[] need to translate ' character
|
||||
if (para instanceof Timestamp || para instanceof String || para instanceof byte[]) {
|
||||
paraStr = paraStr.replaceAll("'", "\\\\\\\\'");
|
||||
paraStr = "'" + paraStr + "'";
|
||||
}
|
||||
this.sql = this.sql.replaceFirst("[?]", paraStr);
|
||||
sql = sql.replaceFirst("[?]", paraStr);
|
||||
} else {
|
||||
this.sql = this.sql.replaceFirst("[?]", "NULL");
|
||||
sql = sql.replaceFirst("[?]", "NULL");
|
||||
}
|
||||
}
|
||||
parameters.clear();
|
||||
clearParameters();
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet executeQuery() throws SQLException {
|
||||
if (isSaved) {
|
||||
this.savedPreparedStatement.executeBatchInternal();
|
||||
return null;
|
||||
} else {
|
||||
return super.executeQuery(getNativeSql());
|
||||
}
|
||||
// if (isSaved) {
|
||||
// this.savedPreparedStatement.executeBatchInternal();
|
||||
// return null;
|
||||
// } else {
|
||||
|
||||
if (!isPrepared)
|
||||
return executeQuery(this.rawSql);
|
||||
|
||||
final String sql = getNativeSql(this.rawSql);
|
||||
return executeQuery(sql);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate() throws SQLException {
|
||||
if (isSaved) {
|
||||
return this.savedPreparedStatement.executeBatchInternal();
|
||||
} else {
|
||||
return super.executeUpdate(getNativeSql());
|
||||
}
|
||||
// if (isSaved) {
|
||||
// return this.savedPreparedStatement.executeBatchInternal();
|
||||
// } else {
|
||||
if (!isPrepared)
|
||||
return executeUpdate(this.rawSql);
|
||||
String sql = getNativeSql(this.rawSql);
|
||||
return executeUpdate(sql);
|
||||
// }
|
||||
}
|
||||
|
||||
private boolean isSupportedSQLType(int sqlType) {
|
||||
|
@ -201,35 +239,6 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
|
|||
case Types.BINARY:
|
||||
case Types.NCHAR:
|
||||
return true;
|
||||
case Types.ARRAY:
|
||||
case Types.BIT:
|
||||
case Types.BLOB:
|
||||
case Types.CHAR:
|
||||
case Types.CLOB:
|
||||
case Types.DATALINK:
|
||||
case Types.DATE:
|
||||
case Types.DECIMAL:
|
||||
case Types.DISTINCT:
|
||||
case Types.JAVA_OBJECT:
|
||||
case Types.LONGNVARCHAR:
|
||||
case Types.LONGVARBINARY:
|
||||
case Types.LONGVARCHAR:
|
||||
case Types.NCLOB:
|
||||
case Types.NULL:
|
||||
case Types.NUMERIC:
|
||||
case Types.NVARCHAR:
|
||||
case Types.OTHER:
|
||||
case Types.REAL:
|
||||
case Types.REF:
|
||||
case Types.REF_CURSOR:
|
||||
case Types.ROWID:
|
||||
case Types.SQLXML:
|
||||
case Types.STRUCT:
|
||||
case Types.TIME:
|
||||
case Types.TIME_WITH_TIMEZONE:
|
||||
case Types.TIMESTAMP_WITH_TIMEZONE:
|
||||
case Types.VARBINARY:
|
||||
case Types.VARCHAR:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -259,7 +268,7 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
|
|||
public void setByte(int parameterIndex, byte x) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
setObject(parameterIndex,x);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -315,7 +324,8 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
|
|||
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
|
||||
setObject(parameterIndex,x);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -365,45 +375,63 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
|
|||
public void clearParameters() throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
parameters.clear();
|
||||
|
||||
// parameters.clear();
|
||||
parameters = new Object[parameters.length];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(int parameterIndex, Object x) throws SQLException {
|
||||
if (isSaved) {
|
||||
this.savedPreparedStatement.setParam(parameterIndex, x);
|
||||
} else {
|
||||
parameters.add(x);
|
||||
}
|
||||
// if (isSaved) {
|
||||
// this.savedPreparedStatement.setParam(parameterIndex, x);
|
||||
// } else {
|
||||
if (parameterIndex < 1 && parameterIndex >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
parameters[parameterIndex - 1] = x;
|
||||
// parameters.add(x);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute() throws SQLException {
|
||||
if (isSaved) {
|
||||
int result = this.savedPreparedStatement.executeBatchInternal();
|
||||
return result > 0;
|
||||
} else {
|
||||
return super.execute(getNativeSql());
|
||||
}
|
||||
// if (isSaved) {
|
||||
// int result = this.savedPreparedStatement.executeBatchInternal();
|
||||
// return result > 0;
|
||||
// } else {
|
||||
if (!isPrepared)
|
||||
return execute(this.rawSql);
|
||||
|
||||
final String sql = getNativeSql(this.rawSql);
|
||||
|
||||
return execute(sql);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBatch() throws SQLException {
|
||||
if (isSaved) {
|
||||
this.savedPreparedStatement.addBatch();
|
||||
} else {
|
||||
// if (isSaved) {
|
||||
// this.savedPreparedStatement.addBatch();
|
||||
// } else {
|
||||
if (this.batchedArgs == null) {
|
||||
batchedArgs = new ArrayList<>();
|
||||
}
|
||||
super.addBatch(getNativeSql());
|
||||
|
||||
if (!isPrepared) {
|
||||
addBatch(this.rawSql);
|
||||
} else {
|
||||
String sql = this.getConnection().nativeSQL(this.rawSql);
|
||||
addBatch(sql);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -491,9 +519,11 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
|
|||
public ParameterMetaData getParameterMetaData() throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
//TODO: parameterMetaData not supported
|
||||
// return null;
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
|
||||
if (parameterMetaData == null) {
|
||||
this.parameterMetaData = new TSDBParameterMetaData(parameters);
|
||||
}
|
||||
return this.parameterMetaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,9 +14,14 @@
|
|||
*****************************************************************************/
|
||||
package com.taosdata.jdbc;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.primitives.Shorts;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
public class TSDBResultSet extends AbstractResultSet implements ResultSet {
|
||||
|
@ -120,158 +125,189 @@ public class TSDBResultSet extends AbstractResultSet implements ResultSet {
|
|||
}
|
||||
|
||||
public String getString(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
String res = null;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return this.blockData.getString(colIndex);
|
||||
return this.blockData.getString(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
res = this.rowData.getString(colIndex, this.columnMetaDataList.get(colIndex).getColType());
|
||||
res = this.rowData.getString(columnIndex - 1, this.columnMetaDataList.get(columnIndex - 1).getColType());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public boolean getBoolean(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
boolean res = false;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return this.blockData.getBoolean(colIndex);
|
||||
return this.blockData.getBoolean(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
res = this.rowData.getBoolean(colIndex, this.columnMetaDataList.get(colIndex).getColType());
|
||||
res = this.rowData.getBoolean(columnIndex - 1, this.columnMetaDataList.get(columnIndex - 1).getColType());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public byte getByte(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
byte res = 0;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return (byte) this.blockData.getInt(colIndex);
|
||||
return (byte) this.blockData.getInt(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
res = (byte) this.rowData.getInt(colIndex, this.columnMetaDataList.get(colIndex).getColType());
|
||||
res = (byte) this.rowData.getInt(columnIndex - 1, this.columnMetaDataList.get(columnIndex - 1).getColType());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public short getShort(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
short res = 0;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return (short) this.blockData.getInt(colIndex);
|
||||
return (short) this.blockData.getInt(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
res = (short) this.rowData.getInt(colIndex, this.columnMetaDataList.get(colIndex).getColType());
|
||||
res = (short) this.rowData.getInt(columnIndex - 1, this.columnMetaDataList.get(columnIndex - 1).getColType());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public int getInt(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
int res = 0;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return this.blockData.getInt(colIndex);
|
||||
return this.blockData.getInt(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
res = this.rowData.getInt(colIndex, this.columnMetaDataList.get(colIndex).getColType());
|
||||
res = this.rowData.getInt(columnIndex - 1, this.columnMetaDataList.get(columnIndex - 1).getColType());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public long getLong(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
long res = 0L;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return this.blockData.getLong(colIndex);
|
||||
return this.blockData.getLong(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
res = this.rowData.getLong(colIndex, this.columnMetaDataList.get(colIndex).getColType());
|
||||
res = this.rowData.getLong(columnIndex - 1, this.columnMetaDataList.get(columnIndex - 1).getColType());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public float getFloat(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
float res = 0;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return (float) this.blockData.getDouble(colIndex);
|
||||
return (float) this.blockData.getDouble(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull)
|
||||
res = this.rowData.getFloat(colIndex, this.columnMetaDataList.get(colIndex).getColType());
|
||||
res = this.rowData.getFloat(columnIndex - 1, this.columnMetaDataList.get(columnIndex - 1).getColType());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public double getDouble(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
double res = 0;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return this.blockData.getDouble(colIndex);
|
||||
return this.blockData.getDouble(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
res = this.rowData.getDouble(colIndex, this.columnMetaDataList.get(colIndex).getColType());
|
||||
res = this.rowData.getDouble(columnIndex - 1, this.columnMetaDataList.get(columnIndex - 1).getColType());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
|
||||
return new BigDecimal(getLong(columnIndex));
|
||||
public byte[] getBytes(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
Object value = this.rowData.get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
int colType = this.columnMetaDataList.get(columnIndex - 1).getColType();
|
||||
switch (colType) {
|
||||
case TSDBConstants.TSDB_DATA_TYPE_BIGINT:
|
||||
return Longs.toByteArray((Long) value);
|
||||
case TSDBConstants.TSDB_DATA_TYPE_INT:
|
||||
return Ints.toByteArray((int) value);
|
||||
case TSDBConstants.TSDB_DATA_TYPE_SMALLINT:
|
||||
return Shorts.toByteArray((Short) value);
|
||||
case TSDBConstants.TSDB_DATA_TYPE_TINYINT:
|
||||
return new byte[]{(byte) value};
|
||||
}
|
||||
return value.toString().getBytes();
|
||||
}
|
||||
|
||||
public byte[] getBytes(int columnIndex) throws SQLException {
|
||||
return getString(columnIndex).getBytes();
|
||||
@Override
|
||||
public Date getDate(int columnIndex) throws SQLException {
|
||||
Timestamp timestamp = getTimestamp(columnIndex);
|
||||
return timestamp == null ? null : new Date(timestamp.getTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Time getTime(int columnIndex) throws SQLException {
|
||||
Timestamp timestamp = getTimestamp(columnIndex);
|
||||
return timestamp == null ? null : new Time(timestamp.getTime());
|
||||
}
|
||||
|
||||
public Timestamp getTimestamp(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
Timestamp res = null;
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
|
||||
if (this.getBatchFetch())
|
||||
return this.blockData.getTimestamp(columnIndex);
|
||||
return this.blockData.getTimestamp(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
res = this.rowData.getTimestamp(colIndex);
|
||||
res = this.rowData.getTimestamp(columnIndex - 1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public ResultSetMetaData getMetaData() throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
|
||||
return new TSDBResultSetMetaData(this.columnMetaDataList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int columnIndex) throws SQLException {
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
checkAvailability(columnIndex, this.columnMetaDataList.size());
|
||||
|
||||
Object res = null;
|
||||
if (this.getBatchFetch())
|
||||
return this.blockData.get(colIndex);
|
||||
return this.blockData.get(columnIndex - 1);
|
||||
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
return this.rowData.get(colIndex);
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
if (!lastWasNull) {
|
||||
int colType = this.columnMetaDataList.get(columnIndex - 1).getColType();
|
||||
if (colType == TSDBConstants.TSDB_DATA_TYPE_BINARY)
|
||||
res = ((String) this.rowData.get(columnIndex - 1)).getBytes();
|
||||
else
|
||||
res = this.rowData.get(columnIndex - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(String columnLabel) throws SQLException {
|
||||
return this.getObject(this.findColumn(columnLabel));
|
||||
return res;
|
||||
}
|
||||
|
||||
public int findColumn(String columnLabel) throws SQLException {
|
||||
|
@ -285,15 +321,32 @@ public class TSDBResultSet extends AbstractResultSet implements ResultSet {
|
|||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
if (this.getBatchFetch())
|
||||
return new BigDecimal(this.blockData.getLong(columnIndex - 1));
|
||||
|
||||
if (!this.getBatchFetch()) {
|
||||
this.lastWasNull = this.rowData.wasNull(colIndex);
|
||||
return new BigDecimal(this.rowData.getLong(colIndex, this.columnMetaDataList.get(colIndex).getColType()));
|
||||
} else {
|
||||
return new BigDecimal(this.blockData.getLong(colIndex));
|
||||
this.lastWasNull = this.rowData.wasNull(columnIndex - 1);
|
||||
BigDecimal res = null;
|
||||
if (!lastWasNull) {
|
||||
int colType = this.columnMetaDataList.get(columnIndex - 1).getColType();
|
||||
switch (colType) {
|
||||
case TSDBConstants.TSDB_DATA_TYPE_TINYINT:
|
||||
case TSDBConstants.TSDB_DATA_TYPE_SMALLINT:
|
||||
case TSDBConstants.TSDB_DATA_TYPE_INT:
|
||||
case TSDBConstants.TSDB_DATA_TYPE_BIGINT:
|
||||
res = new BigDecimal(Long.valueOf(this.rowData.get(columnIndex - 1).toString()));
|
||||
break;
|
||||
case TSDBConstants.TSDB_DATA_TYPE_FLOAT:
|
||||
case TSDBConstants.TSDB_DATA_TYPE_DOUBLE:
|
||||
res = new BigDecimal(Double.valueOf(this.rowData.get(columnIndex - 1).toString()));
|
||||
break;
|
||||
case TSDBConstants.TSDB_DATA_TYPE_TIMESTAMP:
|
||||
return new BigDecimal(((Timestamp) this.rowData.get(columnIndex - 1)).getTime());
|
||||
default:
|
||||
res = new BigDecimal(this.rowData.get(columnIndex - 1).toString());
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeforeFirst() throws SQLException {
|
||||
|
@ -398,6 +451,12 @@ public class TSDBResultSet extends AbstractResultSet implements ResultSet {
|
|||
return this.statement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
|
||||
//TODO:did not use the specified timezone in cal
|
||||
return getTimestamp(columnIndex);
|
||||
}
|
||||
|
||||
public boolean isClosed() throws SQLException {
|
||||
if (isClosed)
|
||||
return true;
|
||||
|
@ -408,17 +467,7 @@ public class TSDBResultSet extends AbstractResultSet implements ResultSet {
|
|||
}
|
||||
|
||||
public String getNString(int columnIndex) throws SQLException {
|
||||
int colIndex = getTrueColumnIndex(columnIndex);
|
||||
return (String) rowData.get(colIndex);
|
||||
return getString(columnIndex);
|
||||
}
|
||||
|
||||
private int getTrueColumnIndex(int columnIndex) throws SQLException {
|
||||
if (columnIndex < 1)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "columnIndex(" + columnIndex + "): < 1");
|
||||
|
||||
int numOfCols = this.columnMetaDataList.size();
|
||||
if (columnIndex > numOfCols)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "columnIndex: " + columnIndex);
|
||||
return columnIndex - 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.util.List;
|
|||
|
||||
public class TSDBResultSetMetaData extends WrapperImpl implements ResultSetMetaData {
|
||||
|
||||
List<ColumnMetaData> colMetaDataList = null;
|
||||
List<ColumnMetaData> colMetaDataList;
|
||||
|
||||
public TSDBResultSetMetaData(List<ColumnMetaData> metaDataList) {
|
||||
this.colMetaDataList = metaDataList;
|
||||
|
@ -52,6 +52,9 @@ public class TSDBResultSetMetaData extends WrapperImpl implements ResultSetMetaD
|
|||
}
|
||||
|
||||
public int isNullable(int column) throws SQLException {
|
||||
if (column < 1 && column >= colMetaDataList.size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
if (column == 1) {
|
||||
return columnNoNulls;
|
||||
}
|
||||
|
@ -59,6 +62,9 @@ public class TSDBResultSetMetaData extends WrapperImpl implements ResultSetMetaD
|
|||
}
|
||||
|
||||
public boolean isSigned(int column) throws SQLException {
|
||||
if (column < 1 && column >= colMetaDataList.size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
ColumnMetaData meta = this.colMetaDataList.get(column - 1);
|
||||
switch (meta.getColType()) {
|
||||
case TSDBConstants.TSDB_DATA_TYPE_TINYINT:
|
||||
|
@ -74,22 +80,37 @@ public class TSDBResultSetMetaData extends WrapperImpl implements ResultSetMetaD
|
|||
}
|
||||
|
||||
public int getColumnDisplaySize(int column) throws SQLException {
|
||||
if (column < 1 && column >= colMetaDataList.size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
return colMetaDataList.get(column - 1).getColSize();
|
||||
}
|
||||
|
||||
public String getColumnLabel(int column) throws SQLException {
|
||||
if (column < 1 && column >= colMetaDataList.size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
return colMetaDataList.get(column - 1).getColName();
|
||||
}
|
||||
|
||||
public String getColumnName(int column) throws SQLException {
|
||||
if (column < 1 && column >= colMetaDataList.size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
return colMetaDataList.get(column - 1).getColName();
|
||||
}
|
||||
|
||||
public String getSchemaName(int column) throws SQLException {
|
||||
if (column < 1 && column >= colMetaDataList.size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
}
|
||||
|
||||
public int getPrecision(int column) throws SQLException {
|
||||
if (column < 1 && column >= colMetaDataList.size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
ColumnMetaData columnMetaData = this.colMetaDataList.get(column - 1);
|
||||
switch (columnMetaData.getColType()) {
|
||||
case TSDBConstants.TSDB_DATA_TYPE_FLOAT:
|
||||
|
@ -105,6 +126,9 @@ public class TSDBResultSetMetaData extends WrapperImpl implements ResultSetMetaD
|
|||
}
|
||||
|
||||
public int getScale(int column) throws SQLException {
|
||||
if (column < 1 && column >= colMetaDataList.size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
ColumnMetaData meta = this.colMetaDataList.get(column - 1);
|
||||
switch (meta.getColType()) {
|
||||
case TSDBConstants.TSDB_DATA_TYPE_FLOAT:
|
||||
|
|
|
@ -21,18 +21,13 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
|
||||
public class TSDBResultSetRowData {
|
||||
private ArrayList<Object> data = null;
|
||||
private ArrayList<Object> data;
|
||||
private int colSize = 0;
|
||||
|
||||
public TSDBResultSetRowData(int colSize) {
|
||||
this.setColSize(colSize);
|
||||
}
|
||||
|
||||
public TSDBResultSetRowData() {
|
||||
this.data = new ArrayList<>();
|
||||
this.setColSize(0);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (this.data != null) {
|
||||
this.data.clear();
|
||||
|
@ -71,9 +66,9 @@ public class TSDBResultSetRowData {
|
|||
case TSDBConstants.TSDB_DATA_TYPE_TIMESTAMP:
|
||||
case TSDBConstants.TSDB_DATA_TYPE_BIGINT:
|
||||
return ((Long) obj) == 1L ? Boolean.TRUE : Boolean.FALSE;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
public void setByte(int col, byte value) {
|
||||
|
@ -198,7 +193,7 @@ public class TSDBResultSetRowData {
|
|||
data.set(col, value);
|
||||
}
|
||||
|
||||
public float getFloat(int col, int srcType) throws SQLException {
|
||||
public float getFloat(int col, int srcType) {
|
||||
Object obj = data.get(col);
|
||||
|
||||
switch (srcType) {
|
||||
|
@ -226,7 +221,7 @@ public class TSDBResultSetRowData {
|
|||
data.set(col, value);
|
||||
}
|
||||
|
||||
public double getDouble(int col, int srcType) throws SQLException {
|
||||
public double getDouble(int col, int srcType) {
|
||||
Object obj = data.get(col);
|
||||
|
||||
switch (srcType) {
|
||||
|
@ -267,9 +262,8 @@ public class TSDBResultSetRowData {
|
|||
*
|
||||
* @param col column index
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public String getString(int col, int srcType) throws SQLException {
|
||||
public String getString(int col, int srcType) {
|
||||
switch (srcType) {
|
||||
case TSDBConstants.TSDB_DATA_TYPE_BINARY:
|
||||
case TSDBConstants.TSDB_DATA_TYPE_NCHAR:
|
||||
|
@ -305,11 +299,11 @@ public class TSDBResultSetRowData {
|
|||
}
|
||||
|
||||
public void setTimestamp(int col, long ts) {
|
||||
data.set(col, ts);
|
||||
data.set(col, new Timestamp(ts));
|
||||
}
|
||||
|
||||
public Timestamp getTimestamp(int col) {
|
||||
return new Timestamp((Long) data.get(col));
|
||||
return (Timestamp) data.get(col);
|
||||
}
|
||||
|
||||
public Object get(int col) {
|
||||
|
@ -320,7 +314,7 @@ public class TSDBResultSetRowData {
|
|||
return colSize;
|
||||
}
|
||||
|
||||
public void setColSize(int colSize) {
|
||||
private void setColSize(int colSize) {
|
||||
this.colSize = colSize;
|
||||
this.clear();
|
||||
}
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
package com.taosdata.jdbc.bean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* tdengine batch insert or import param object
|
||||
*/
|
||||
public class TSDBPreparedParam {
|
||||
|
||||
/**
|
||||
* tableName, if sTable Name is not null, and this is sub table name.
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* sub middle param list
|
||||
*/
|
||||
private List<Object> middleParamList;
|
||||
|
||||
/**
|
||||
* value list
|
||||
*/
|
||||
private List<Object> valueList;
|
||||
|
||||
public TSDBPreparedParam(String tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public void setTableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public List<Object> getMiddleParamList() {
|
||||
return middleParamList;
|
||||
}
|
||||
|
||||
public void setMiddleParamList(List<Object> middleParamList) {
|
||||
this.middleParamList = middleParamList;
|
||||
}
|
||||
|
||||
public void setMiddleParam(int parameterIndex, Object x) {
|
||||
this.middleParamList.set(parameterIndex, x);
|
||||
}
|
||||
|
||||
public List<Object> getValueList() {
|
||||
return valueList;
|
||||
}
|
||||
|
||||
public void setValueList(List<Object> valueList) {
|
||||
this.valueList = valueList;
|
||||
}
|
||||
|
||||
|
||||
public void setValueParam(int parameterIndex, Object x) {
|
||||
this.valueList.set(parameterIndex, x);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,14 @@
|
|||
package com.taosdata.jdbc.rs;
|
||||
|
||||
import com.taosdata.jdbc.*;
|
||||
import com.taosdata.jdbc.AbstractConnection;
|
||||
import com.taosdata.jdbc.TSDBDriver;
|
||||
import com.taosdata.jdbc.TSDBError;
|
||||
import com.taosdata.jdbc.TSDBErrorNumbers;
|
||||
|
||||
import java.sql.*;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
|
||||
public class RestfulConnection extends AbstractConnection {
|
||||
|
@ -55,7 +61,6 @@ public class RestfulConnection extends AbstractConnection {
|
|||
public DatabaseMetaData getMetaData() throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED);
|
||||
;
|
||||
|
||||
return this.metadata;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public class RestfulDriver extends AbstractDriver {
|
|||
|
||||
static {
|
||||
try {
|
||||
DriverManager.registerDriver(new RestfulDriver());
|
||||
java.sql.DriverManager.registerDriver(new RestfulDriver());
|
||||
} catch (SQLException e) {
|
||||
throw TSDBError.createRuntimeException(TSDBErrorNumbers.ERROR_URL_NOT_SET, e);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.taosdata.jdbc.rs;
|
||||
|
||||
import com.taosdata.jdbc.AbstractParameterMetaData;
|
||||
|
||||
public class RestfulParameterMetaData extends AbstractParameterMetaData {
|
||||
|
||||
RestfulParameterMetaData(Object[] parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import java.io.InputStream;
|
|||
import java.io.Reader;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.sql.*;
|
||||
import java.util.Calendar;
|
||||
|
||||
|
@ -30,7 +31,9 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar
|
|||
parameters = new Object[parameterCnt];
|
||||
this.isPrepared = true;
|
||||
}
|
||||
//TODO: build parameterMetaData
|
||||
|
||||
// build parameterMetaData
|
||||
this.parameterMetaData = new RestfulParameterMetaData(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,8 +63,15 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar
|
|||
for (int i = 0; i < parameters.length; ++i) {
|
||||
Object para = parameters[i];
|
||||
if (para != null) {
|
||||
String paraStr = para.toString();
|
||||
if (para instanceof Timestamp || para instanceof String) {
|
||||
String paraStr;
|
||||
if (para instanceof byte[]) {
|
||||
paraStr = new String((byte[]) para, Charset.forName("UTF-8"));
|
||||
} else {
|
||||
paraStr = para.toString();
|
||||
}
|
||||
// if para is timestamp or String or byte[] need to translate ' character
|
||||
if (para instanceof Timestamp || para instanceof String || para instanceof byte[]) {
|
||||
paraStr = paraStr.replaceAll("'", "\\\\\\\\'");
|
||||
paraStr = "'" + paraStr + "'";
|
||||
}
|
||||
sql = sql.replaceFirst("[?]", paraStr);
|
||||
|
@ -92,7 +102,7 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar
|
|||
public void setByte(int parameterIndex, byte x) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
setObject(parameterIndex, x);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,7 +163,7 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar
|
|||
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
setObject(parameterIndex, x);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -210,19 +220,16 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar
|
|||
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD);
|
||||
|
||||
setObject(parameterIndex,x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(int parameterIndex, Object x) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
|
||||
|
||||
if (parameterIndex < 1 && parameterIndex >= parameters.length)
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE);
|
||||
|
||||
parameters[parameterIndex - 1] = x;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,13 +2,18 @@ package com.taosdata.jdbc.rs;
|
|||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.primitives.Shorts;
|
||||
import com.taosdata.jdbc.AbstractResultSet;
|
||||
import com.taosdata.jdbc.TSDBConstants;
|
||||
import com.taosdata.jdbc.TSDBError;
|
||||
import com.taosdata.jdbc.TSDBErrorNumbers;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
||||
private volatile boolean isClosed;
|
||||
|
@ -16,7 +21,6 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
|
||||
private final String database;
|
||||
private final Statement statement;
|
||||
// private final JSONObject resultJson;
|
||||
// data
|
||||
private final ArrayList<ArrayList<Object>> resultSet;
|
||||
// meta
|
||||
|
@ -32,7 +36,6 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
public RestfulResultSet(String database, Statement statement, JSONObject resultJson) throws SQLException {
|
||||
this.database = database;
|
||||
this.statement = statement;
|
||||
// this.resultJson = resultJson;
|
||||
|
||||
// column metadata
|
||||
JSONArray columnMeta = resultJson.getJSONArray("column_meta");
|
||||
|
@ -73,7 +76,7 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
case TSDBConstants.TSDB_DATA_TYPE_INT:
|
||||
return row.getInteger(colIndex);
|
||||
case TSDBConstants.TSDB_DATA_TYPE_BIGINT:
|
||||
return row.getBigInteger(colIndex);
|
||||
return row.getLong(colIndex);
|
||||
case TSDBConstants.TSDB_DATA_TYPE_FLOAT:
|
||||
return row.getFloat(colIndex);
|
||||
case TSDBConstants.TSDB_DATA_TYPE_DOUBLE:
|
||||
|
@ -81,9 +84,11 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
case TSDBConstants.TSDB_DATA_TYPE_TIMESTAMP:
|
||||
return new Timestamp(row.getDate(colIndex).getTime());
|
||||
case TSDBConstants.TSDB_DATA_TYPE_BINARY:
|
||||
return row.getString(colIndex) == null ? null : row.getString(colIndex).getBytes();
|
||||
case TSDBConstants.TSDB_DATA_TYPE_NCHAR:
|
||||
return row.getString(colIndex) == null ? null : row.getString(colIndex);
|
||||
default:
|
||||
return row.getString(colIndex);
|
||||
return row.get(colIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,37 +135,33 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
|
||||
@Override
|
||||
public String getString(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex > resultSet.get(pos).size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size());
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex);
|
||||
return value == null ? null : value.toString();
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return null;
|
||||
if (value instanceof byte[])
|
||||
return new String((byte[]) value);
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex > resultSet.get(pos).size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size());
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
int result = getInt(columnIndex);
|
||||
return result == 0 ? false : true;
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return false;
|
||||
if (value instanceof Boolean)
|
||||
return (boolean) value;
|
||||
return Boolean.valueOf(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex > resultSet.get(pos).size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size());
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return 0;
|
||||
long valueAsLong = Long.parseLong(value.toString());
|
||||
|
@ -179,13 +180,9 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
|
||||
@Override
|
||||
public short getShort(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex > resultSet.get(pos).size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size());
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return 0;
|
||||
long valueAsLong = Long.parseLong(value.toString());
|
||||
|
@ -198,13 +195,9 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
|
||||
@Override
|
||||
public int getInt(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex > resultSet.get(pos).size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size());
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return 0;
|
||||
long valueAsLong = Long.parseLong(value.toString());
|
||||
|
@ -217,13 +210,9 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
|
||||
@Override
|
||||
public long getLong(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex > resultSet.get(pos).size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size());
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex);
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return 0;
|
||||
|
||||
|
@ -240,64 +229,99 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
|
||||
@Override
|
||||
public float getFloat(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex > resultSet.get(pos).size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size());
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
return Float.parseFloat(resultSet.get(pos).get(columnIndex).toString());
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return 0;
|
||||
if (value instanceof Float || value instanceof Double)
|
||||
return (float) value;
|
||||
return Float.parseFloat(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
if (columnIndex > resultSet.get(pos).size())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size());
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
return Double.parseDouble(resultSet.get(pos).get(columnIndex).toString());
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return 0;
|
||||
if (value instanceof Double || value instanceof Float)
|
||||
return (double) value;
|
||||
return Double.parseDouble(value.toString());
|
||||
}
|
||||
|
||||
private int getTrueColumnIndex(int columnIndex) throws SQLException {
|
||||
if (columnIndex < 1) {
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE
|
||||
, "Column Index out of range, " + columnIndex + " < 1");
|
||||
@Override
|
||||
public byte[] getBytes(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return null;
|
||||
if (value instanceof byte[])
|
||||
return (byte[]) value;
|
||||
if (value instanceof String)
|
||||
return ((String) value).getBytes();
|
||||
if (value instanceof Long)
|
||||
return Longs.toByteArray((long) value);
|
||||
if (value instanceof Integer)
|
||||
return Ints.toByteArray((int) value);
|
||||
if (value instanceof Short)
|
||||
return Shorts.toByteArray((short) value);
|
||||
if (value instanceof Byte)
|
||||
return new byte[]{(byte) value};
|
||||
|
||||
return value.toString().getBytes();
|
||||
}
|
||||
|
||||
int numOfCols = resultSet.get(pos).size();
|
||||
if (columnIndex > numOfCols) {
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE
|
||||
, "Column Index out of range, " + columnIndex + " > " + numOfCols);
|
||||
@Override
|
||||
public Date getDate(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return null;
|
||||
if (value instanceof Timestamp)
|
||||
return new Date(((Timestamp) value).getTime());
|
||||
return Date.valueOf(value.toString());
|
||||
}
|
||||
|
||||
return columnIndex - 1;
|
||||
@Override
|
||||
public Time getTime(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return null;
|
||||
if (value instanceof Timestamp)
|
||||
return new Time(((Timestamp) value).getTime());
|
||||
return Time.valueOf(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(int columnIndex) throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
columnIndex = getTrueColumnIndex(columnIndex);
|
||||
String strDate = resultSet.get(pos).get(columnIndex).toString();
|
||||
// strDate = strDate.substring(1, strDate.length() - 1);
|
||||
return Timestamp.valueOf(strDate);
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return null;
|
||||
if (value instanceof Timestamp)
|
||||
return (Timestamp) value;
|
||||
return Timestamp.valueOf(value.toString());
|
||||
}
|
||||
|
||||
/*************************************************************************************************************/
|
||||
@Override
|
||||
public ResultSetMetaData getMetaData() throws SQLException {
|
||||
if (isClosed())
|
||||
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED);
|
||||
|
||||
return this.metaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(String columnLabel) throws SQLException {
|
||||
return getObject(findColumn(columnLabel));
|
||||
public Object getObject(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
return resultSet.get(pos).get(columnIndex - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -311,6 +335,23 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
return columnIndex + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
|
||||
checkAvailability(columnIndex, resultSet.get(pos).size());
|
||||
|
||||
Object value = resultSet.get(pos).get(columnIndex - 1);
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
if (value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte)
|
||||
return new BigDecimal(Long.valueOf(value.toString()));
|
||||
if (value instanceof Double || value instanceof Float)
|
||||
return new BigDecimal(Double.valueOf(value.toString()));
|
||||
if (value instanceof Timestamp)
|
||||
return new BigDecimal(((Timestamp) value).getTime());
|
||||
return new BigDecimal(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeforeFirst() throws SQLException {
|
||||
if (isClosed())
|
||||
|
@ -471,6 +512,12 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
|
|||
return this.statement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
|
||||
//TODO:did not use the specified timezone in cal
|
||||
return getTimestamp(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
return isClosed;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package com.taosdata.jdbc.utils;
|
||||
|
||||
public class OSUtils {
|
||||
private static final String OS = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
public static boolean isWindows() {
|
||||
return OS.indexOf("win") >= 0;
|
||||
}
|
||||
|
||||
public static boolean isMac() {
|
||||
return OS.indexOf("mac") >= 0;
|
||||
}
|
||||
|
||||
public static boolean isLinux() {
|
||||
return OS.indexOf("nux") >= 0;
|
||||
}
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
com.taosdata.jdbc.TSDBDriver
|
||||
com.taosdata.jdbc.rs.RestfulDriver
|
||||
|
|
|
@ -1,266 +0,0 @@
|
|||
package com.taosdata.jdbc;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.sql.rowset.serial.SerialBlob;
|
||||
import javax.sql.rowset.serial.SerialClob;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.sql.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ResultSetTest {
|
||||
static Connection connection;
|
||||
static Statement statement;
|
||||
static String dbName = "test";
|
||||
static String tName = "t0";
|
||||
static String host = "localhost";
|
||||
static ResultSet resSet;
|
||||
|
||||
@BeforeClass
|
||||
public static void createDatabaseAndTable() {
|
||||
try {
|
||||
Class.forName("com.taosdata.jdbc.TSDBDriver");
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
|
||||
properties.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
|
||||
properties.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
|
||||
connection = DriverManager.getConnection("jdbc:TAOS://" + host + ":0/", properties);
|
||||
statement = connection.createStatement();
|
||||
statement.executeUpdate("drop database if exists " + dbName);
|
||||
statement.executeUpdate("create database if not exists " + dbName);
|
||||
statement.execute("use " + dbName);
|
||||
statement.executeUpdate("create table if not exists " + dbName + "." + tName + " (ts timestamp, k1 int, k2 bigint, k3 float, k4 double, k5 binary(30), k6 smallint, k7 bool, k8 nchar(20))");
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResultSet() {
|
||||
String sql;
|
||||
long ts = 1496732686000l;
|
||||
int v1 = 2147483600;
|
||||
long v2 = ts + 1000;
|
||||
float v3 = 3.1415926f;
|
||||
double v4 = 3.1415926535897;
|
||||
String v5 = "涛思数据,强~!";
|
||||
short v6 = 12;
|
||||
boolean v7 = false;
|
||||
String v8 = "TDengine is powerful";
|
||||
sql = "insert into " + dbName + "." + tName + " values (" + ts + "," + v1 + "," + v2 + "," + v3 + "," + v4
|
||||
+ ",\"" + v5 + "\"," + v6 + "," + v7 + ",\"" + v8 + "\")";
|
||||
try {
|
||||
statement.executeUpdate(sql);
|
||||
assertEquals(1, statement.getUpdateCount());
|
||||
} catch (SQLException e) {
|
||||
assert false : "insert error " + e.getMessage();
|
||||
}
|
||||
try {
|
||||
statement.execute("select * from " + dbName + "." + tName + " where ts = " + ts);
|
||||
resSet = statement.getResultSet();
|
||||
System.out.println(((TSDBResultSet) resSet).getRowData());
|
||||
while (resSet.next()) {
|
||||
assertEquals(ts, resSet.getLong(1));
|
||||
assertEquals(ts, resSet.getLong("ts"));
|
||||
System.out.println(resSet.getTimestamp(1));
|
||||
assertEquals(v1, resSet.getInt(2));
|
||||
assertEquals(v1, resSet.getInt("k1"));
|
||||
assertEquals(v2, resSet.getLong(3));
|
||||
assertEquals(v2, resSet.getLong("k2"));
|
||||
assertEquals(v3, resSet.getFloat(4), 7);
|
||||
assertEquals(v3, resSet.getFloat("k3"), 7);
|
||||
assertEquals(v4, resSet.getDouble(5), 13);
|
||||
assertEquals(v4, resSet.getDouble("k4"), 13);
|
||||
assertEquals(v5, resSet.getString(6));
|
||||
assertEquals(v5, resSet.getString("k5"));
|
||||
assertEquals(v6, resSet.getShort(7));
|
||||
assertEquals(v6, resSet.getShort("k6"));
|
||||
assertEquals(v7, resSet.getBoolean(8));
|
||||
assertEquals(v7, resSet.getBoolean("k7"));
|
||||
assertEquals(v8, resSet.getString(9));
|
||||
assertEquals(v8, resSet.getString("k8"));
|
||||
resSet.getBytes(9);
|
||||
resSet.getObject(6);
|
||||
resSet.getObject("k8");
|
||||
}
|
||||
if (!resSet.isClosed()) {
|
||||
resSet.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
assert false : "insert error " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = SQLException.class)
|
||||
public void testUnsupport() throws SQLException, UnsupportedEncodingException {
|
||||
statement.execute("show databases");
|
||||
resSet = statement.getResultSet();
|
||||
Assert.assertNotNull(resSet.unwrap(TSDBResultSet.class));
|
||||
Assert.assertTrue(resSet.isWrapperFor(TSDBResultSet.class));
|
||||
resSet.getUnicodeStream(null);
|
||||
resSet.getBinaryStream(null);
|
||||
resSet.getAsciiStream("");
|
||||
resSet.getUnicodeStream(null);
|
||||
resSet.getBinaryStream(null);
|
||||
resSet.getWarnings();
|
||||
resSet.clearWarnings();
|
||||
resSet.getCursorName();
|
||||
resSet.getCharacterStream(null);
|
||||
resSet.getCharacterStream(null);
|
||||
resSet.isBeforeFirst();
|
||||
resSet.isAfterLast();
|
||||
resSet.isFirst();
|
||||
resSet.isLast();
|
||||
resSet.beforeFirst();
|
||||
resSet.afterLast();
|
||||
resSet.first();
|
||||
resSet.last();
|
||||
resSet.getRow();
|
||||
resSet.absolute(1);
|
||||
resSet.relative(1);
|
||||
resSet.previous();
|
||||
resSet.setFetchDirection(0);
|
||||
resSet.getFetchDirection();
|
||||
resSet.setFetchSize(0);
|
||||
resSet.getFetchSize();
|
||||
resSet.getConcurrency();
|
||||
resSet.rowUpdated();
|
||||
resSet.rowInserted();
|
||||
resSet.rowDeleted();
|
||||
resSet.updateNull(null);
|
||||
resSet.updateBoolean(0, true);
|
||||
resSet.updateByte(0, (byte) 2);
|
||||
resSet.updateShort(0, (short) 1);
|
||||
resSet.updateInt(0, 0);
|
||||
resSet.updateLong(0, 0l);
|
||||
resSet.updateFloat(0, 3.14f);
|
||||
resSet.updateDouble(0, 3.1415);
|
||||
resSet.updateBigDecimal(null, null);
|
||||
resSet.updateString(null, null);
|
||||
resSet.updateBytes(null, null);
|
||||
resSet.updateDate(null, null);
|
||||
resSet.updateTime(null, null);
|
||||
resSet.updateTimestamp(null, null);
|
||||
resSet.updateAsciiStream(null, null);
|
||||
resSet.updateBinaryStream(null, null);
|
||||
resSet.updateCharacterStream(null, null);
|
||||
resSet.updateObject(null, null);
|
||||
resSet.updateObject(null, null);
|
||||
resSet.updateNull(null);
|
||||
resSet.updateBoolean("", false);
|
||||
resSet.updateByte("", (byte) 1);
|
||||
resSet.updateShort("", (short) 1);
|
||||
resSet.updateInt("", 0);
|
||||
resSet.updateLong("", 0l);
|
||||
resSet.updateFloat("", 3.14f);
|
||||
resSet.updateDouble("", 3.1415);
|
||||
resSet.updateBigDecimal(null, null);
|
||||
resSet.updateString(null, null);
|
||||
resSet.updateBytes(null, null);
|
||||
resSet.updateDate(null, null);
|
||||
resSet.updateTime(null, null);
|
||||
resSet.updateTimestamp(null, null);
|
||||
resSet.updateAsciiStream(null, null);
|
||||
resSet.updateBinaryStream(null, null);
|
||||
resSet.updateCharacterStream(null, null);
|
||||
resSet.updateObject(null, null);
|
||||
resSet.updateObject(null, null);
|
||||
resSet.insertRow();
|
||||
resSet.updateRow();
|
||||
resSet.deleteRow();
|
||||
resSet.refreshRow();
|
||||
resSet.cancelRowUpdates();
|
||||
resSet.moveToInsertRow();
|
||||
resSet.moveToCurrentRow();
|
||||
resSet.getStatement();
|
||||
resSet.getObject(0, new HashMap<>());
|
||||
resSet.getRef(null);
|
||||
resSet.getBlob(null);
|
||||
resSet.getClob(null);
|
||||
resSet.getArray(null);
|
||||
resSet.getObject("", new HashMap<>());
|
||||
resSet.getRef(null);
|
||||
resSet.getBlob(null);
|
||||
resSet.getClob(null);
|
||||
resSet.getArray(null);
|
||||
resSet.getDate(null, null);
|
||||
resSet.getDate(null, null);
|
||||
resSet.getTime(null, null);
|
||||
resSet.getTime(null, null);
|
||||
resSet.getTimestamp(null, null);
|
||||
resSet.getTimestamp(null, null);
|
||||
resSet.getURL(null);
|
||||
resSet.getURL(null);
|
||||
resSet.updateRef(null, null);
|
||||
resSet.updateRef(null, null);
|
||||
resSet.updateBlob(0, new SerialBlob("".getBytes("UTF8")));
|
||||
resSet.updateBlob("", new SerialBlob("".getBytes("UTF8")));
|
||||
resSet.updateClob("", new SerialClob("".toCharArray()));
|
||||
resSet.updateClob(0, new SerialClob("".toCharArray()));
|
||||
resSet.updateArray(null, null);
|
||||
resSet.updateArray(null, null);
|
||||
resSet.getRowId(null);
|
||||
resSet.getRowId(null);
|
||||
resSet.updateRowId(null, null);
|
||||
resSet.updateRowId(null, null);
|
||||
resSet.getHoldability();
|
||||
resSet.updateNString(null, null);
|
||||
resSet.updateNString(null, null);
|
||||
resSet.getNClob(null);
|
||||
resSet.getNClob(null);
|
||||
resSet.getSQLXML(null);
|
||||
resSet.getSQLXML(null);
|
||||
resSet.updateSQLXML(null, null);
|
||||
resSet.updateSQLXML(null, null);
|
||||
resSet.getNCharacterStream(null);
|
||||
resSet.getNCharacterStream(null);
|
||||
resSet.updateNCharacterStream(null, null);
|
||||
resSet.updateNCharacterStream(null, null);
|
||||
resSet.updateAsciiStream(null, null);
|
||||
resSet.updateBinaryStream(null, null);
|
||||
resSet.updateCharacterStream(null, null);
|
||||
resSet.updateAsciiStream(null, null);
|
||||
resSet.updateBinaryStream(null, null);
|
||||
resSet.updateCharacterStream(null, null);
|
||||
resSet.updateNCharacterStream(null, null);
|
||||
resSet.updateNCharacterStream(null, null);
|
||||
resSet.updateAsciiStream(null, null);
|
||||
resSet.updateBinaryStream(null, null);
|
||||
resSet.updateCharacterStream(null, null);
|
||||
resSet.updateAsciiStream(null, null);
|
||||
resSet.updateBinaryStream(null, null);
|
||||
resSet.updateCharacterStream(null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatch() throws SQLException {
|
||||
String[] sqls = new String[]{"insert into test.t0 values (1496732686001,2147483600,1496732687000,3.1415925,3.1415926535897," +
|
||||
"'涛思数据,强~',12,0,'TDengine is powerful')", "insert into test.t0 values (1496732686002,2147483600,1496732687000,3.1415925,3.1415926535897," +
|
||||
"'涛思数据,强~',12,1,'TDengine is powerful')"};
|
||||
for (String sql : sqls) {
|
||||
statement.addBatch(sql);
|
||||
}
|
||||
int[] res = statement.executeBatch();
|
||||
assertEquals(res.length, 2);
|
||||
statement.clearBatch();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void close() {
|
||||
try {
|
||||
statement.executeUpdate("drop database " + dbName);
|
||||
if (statement != null)
|
||||
statement.close();
|
||||
if (connection != null)
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -233,7 +233,7 @@ public class TSDBConnectionTest {
|
|||
int status = rs.getInt("server_status()");
|
||||
Assert.assertEquals(1, status);
|
||||
|
||||
conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
|
||||
conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
package com.taosdata.jdbc;
|
||||
|
||||
import com.taosdata.jdbc.rs.RestfulParameterMetaData;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class TSDBParameterMetaDataTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
private static Connection conn;
|
||||
private static final String sql_insert = "insert into t1 values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static PreparedStatement pstmt_insert;
|
||||
private static final String sql_select = "select * from t1 where ts > ? and ts <= ? and f1 >= ?";
|
||||
private static PreparedStatement pstmt_select;
|
||||
private static ParameterMetaData parameterMetaData_insert;
|
||||
private static ParameterMetaData parameterMetaData_select;
|
||||
|
||||
@Test
|
||||
public void getParameterCount() throws SQLException {
|
||||
Assert.assertEquals(10, parameterMetaData_insert.getParameterCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNullable() throws SQLException {
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(1));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(2));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(3));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(4));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(5));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(6));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(7));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(8));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(9));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSigned() throws SQLException {
|
||||
Assert.assertEquals(false, parameterMetaData_insert.isSigned(1));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(2));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(3));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(4));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(5));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(6));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(7));
|
||||
Assert.assertEquals(false, parameterMetaData_insert.isSigned(8));
|
||||
Assert.assertEquals(false, parameterMetaData_insert.isSigned(9));
|
||||
Assert.assertEquals(false, parameterMetaData_insert.isSigned(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPrecision() throws SQLException {
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(1));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(2));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(3));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(4));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(5));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(6));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(7));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(8));
|
||||
Assert.assertEquals(5, parameterMetaData_insert.getPrecision(9));
|
||||
Assert.assertEquals(5, parameterMetaData_insert.getPrecision(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getScale() throws SQLException {
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(1));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(2));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(3));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(4));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(5));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(6));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(7));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(8));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(9));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterType() throws SQLException {
|
||||
Assert.assertEquals(Types.TIMESTAMP, parameterMetaData_insert.getParameterType(1));
|
||||
Assert.assertEquals(Types.INTEGER, parameterMetaData_insert.getParameterType(2));
|
||||
Assert.assertEquals(Types.BIGINT, parameterMetaData_insert.getParameterType(3));
|
||||
Assert.assertEquals(Types.FLOAT, parameterMetaData_insert.getParameterType(4));
|
||||
Assert.assertEquals(Types.DOUBLE, parameterMetaData_insert.getParameterType(5));
|
||||
Assert.assertEquals(Types.SMALLINT, parameterMetaData_insert.getParameterType(6));
|
||||
Assert.assertEquals(Types.TINYINT, parameterMetaData_insert.getParameterType(7));
|
||||
Assert.assertEquals(Types.BOOLEAN, parameterMetaData_insert.getParameterType(8));
|
||||
Assert.assertEquals(Types.BINARY, parameterMetaData_insert.getParameterType(9));
|
||||
Assert.assertEquals(Types.NCHAR, parameterMetaData_insert.getParameterType(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterTypeName() throws SQLException {
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.TIMESTAMP), parameterMetaData_insert.getParameterTypeName(1));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.INTEGER), parameterMetaData_insert.getParameterTypeName(2));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.BIGINT), parameterMetaData_insert.getParameterTypeName(3));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.FLOAT), parameterMetaData_insert.getParameterTypeName(4));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.DOUBLE), parameterMetaData_insert.getParameterTypeName(5));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.SMALLINT), parameterMetaData_insert.getParameterTypeName(6));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.TINYINT), parameterMetaData_insert.getParameterTypeName(7));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.BOOLEAN), parameterMetaData_insert.getParameterTypeName(8));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.BINARY), parameterMetaData_insert.getParameterTypeName(9));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.NCHAR), parameterMetaData_insert.getParameterTypeName(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterClassName() throws SQLException {
|
||||
Assert.assertEquals(Timestamp.class.getName(), parameterMetaData_insert.getParameterClassName(1));
|
||||
Assert.assertEquals(Integer.class.getName(), parameterMetaData_insert.getParameterClassName(2));
|
||||
Assert.assertEquals(Long.class.getName(), parameterMetaData_insert.getParameterClassName(3));
|
||||
Assert.assertEquals(Float.class.getName(), parameterMetaData_insert.getParameterClassName(4));
|
||||
Assert.assertEquals(Double.class.getName(), parameterMetaData_insert.getParameterClassName(5));
|
||||
Assert.assertEquals(Short.class.getName(), parameterMetaData_insert.getParameterClassName(6));
|
||||
Assert.assertEquals(Byte.class.getName(), parameterMetaData_insert.getParameterClassName(7));
|
||||
Assert.assertEquals(Boolean.class.getName(), parameterMetaData_insert.getParameterClassName(8));
|
||||
Assert.assertEquals(byte[].class.getName(), parameterMetaData_insert.getParameterClassName(9));
|
||||
Assert.assertEquals(String.class.getName(), parameterMetaData_insert.getParameterClassName(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterMode() throws SQLException {
|
||||
for (int i = 1; i <= parameterMetaData_insert.getParameterCount(); i++) {
|
||||
int parameterMode = parameterMetaData_insert.getParameterMode(i);
|
||||
Assert.assertEquals(ParameterMetaData.parameterModeUnknown, parameterMode);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unwrap() throws SQLException {
|
||||
TSDBParameterMetaData unwrap = parameterMetaData_insert.unwrap(TSDBParameterMetaData.class);
|
||||
Assert.assertNotNull(unwrap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isWrapperFor() throws SQLException {
|
||||
Assert.assertTrue(parameterMetaData_insert.isWrapperFor(TSDBParameterMetaData.class));
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
try {
|
||||
Class.forName("com.taosdata.jdbc.TSDBDriver");
|
||||
conn = DriverManager.getConnection("jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata");
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("drop database if exists test_pstmt");
|
||||
stmt.execute("create database if not exists test_pstmt");
|
||||
stmt.execute("use test_pstmt");
|
||||
stmt.execute("create table weather(ts timestamp, f1 int, f2 bigint, f3 float, f4 double, f5 smallint, f6 tinyint, f7 bool, f8 binary(64), f9 nchar(64)) tags(loc nchar(64))");
|
||||
stmt.execute("create table t1 using weather tags('beijing')");
|
||||
}
|
||||
pstmt_insert = conn.prepareStatement(sql_insert);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(2, 111);
|
||||
pstmt_insert.setObject(3, Long.MAX_VALUE);
|
||||
pstmt_insert.setObject(4, 3.14159265354f);
|
||||
pstmt_insert.setObject(5, Double.MAX_VALUE);
|
||||
pstmt_insert.setObject(6, Short.MAX_VALUE);
|
||||
pstmt_insert.setObject(7, Byte.MAX_VALUE);
|
||||
pstmt_insert.setObject(8, true);
|
||||
pstmt_insert.setObject(9, "hello".getBytes());
|
||||
pstmt_insert.setObject(10, "Hello");
|
||||
parameterMetaData_insert = pstmt_insert.getParameterMetaData();
|
||||
|
||||
pstmt_select = conn.prepareStatement(sql_select);
|
||||
pstmt_select.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_select.setTimestamp(2, new Timestamp(System.currentTimeMillis() + 10000));
|
||||
pstmt_select.setInt(3, 0);
|
||||
parameterMetaData_select = pstmt_select.getParameterMetaData();
|
||||
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
try {
|
||||
if (pstmt_insert != null)
|
||||
pstmt_insert.close();
|
||||
if (pstmt_select != null)
|
||||
pstmt_select.close();
|
||||
if (conn != null)
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,14 +5,16 @@ import org.junit.Assert;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.sql.*;
|
||||
|
||||
public class TSDBPreparedStatementTest {
|
||||
private static final String host = "127.0.0.1";
|
||||
private static Connection conn;
|
||||
private static final String sql_insert = "insert into t1 values(?, ?)";
|
||||
private static final String sql_insert = "insert into t1 values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static PreparedStatement pstmt_insert;
|
||||
private static final String sql_select = "select * from t1 where ts > ? and ts <= ? and temperature >= ?";
|
||||
private static final String sql_select = "select * from t1 where ts > ? and ts <= ? and f1 >= ?";
|
||||
private static PreparedStatement pstmt_select;
|
||||
|
||||
@Test
|
||||
|
@ -21,7 +23,7 @@ public class TSDBPreparedStatementTest {
|
|||
long start = end - 1000 * 60 * 60;
|
||||
pstmt_select.setTimestamp(1, new Timestamp(start));
|
||||
pstmt_select.setTimestamp(2, new Timestamp(end));
|
||||
pstmt_select.setFloat(3, 0);
|
||||
pstmt_select.setInt(3, 0);
|
||||
|
||||
ResultSet rs = pstmt_select.executeQuery();
|
||||
Assert.assertNotNull(rs);
|
||||
|
@ -37,48 +39,73 @@ public class TSDBPreparedStatementTest {
|
|||
@Test
|
||||
public void executeUpdate() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setFloat(2, 3.14f);
|
||||
pstmt_insert.setFloat(4, 3.14f);
|
||||
int result = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setNull() throws SQLException {
|
||||
pstmt_insert.setNull(2, Types.FLOAT);
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setNull(2, Types.INTEGER);
|
||||
int result = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setBoolean() throws SQLException {
|
||||
pstmt_insert.setBoolean(2, true);
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setBoolean(8, true);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
@Test
|
||||
public void setByte() throws SQLException {
|
||||
pstmt_insert.setByte(1, (byte) 0x001);
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setByte(7, (byte) 0x001);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setShort() {
|
||||
|
||||
public void setShort() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setShort(6, (short) 2);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInt() {
|
||||
|
||||
public void setInt() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setInt(2, 10086);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setLong() {
|
||||
|
||||
public void setLong() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setLong(3, Long.MAX_VALUE);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setFloat() {
|
||||
|
||||
public void setFloat() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setFloat(4, 3.14f);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDouble() {
|
||||
public void setDouble() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setDouble(5, 3.14444);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -87,12 +114,56 @@ public class TSDBPreparedStatementTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void setString() {
|
||||
public void setString() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setString(10, "aaaa");
|
||||
boolean execute = pstmt_insert.execute();
|
||||
Assert.assertFalse(execute);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setString(10, new Person("john", 33, true).toString());
|
||||
Assert.assertFalse(pstmt_insert.execute());
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setString(10, new Person("john", 33, true).toString().replaceAll("'", "\""));
|
||||
Assert.assertFalse(pstmt_insert.execute());
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void setBytes() throws SQLException {
|
||||
pstmt_insert.setBytes(1, new byte[]{});
|
||||
class Person implements Serializable {
|
||||
String name;
|
||||
int age;
|
||||
boolean sex;
|
||||
|
||||
public Person(String name, int age, boolean sex) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"name='" + name + '\'' +
|
||||
", age=" + age +
|
||||
", sex=" + sex +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setBytes() throws SQLException, IOException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
|
||||
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
// ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
// oos.writeObject(new Person("john", 33, true));
|
||||
// oos.flush();
|
||||
// byte[] bytes = baos.toByteArray();
|
||||
// pstmt_insert.setBytes(9, bytes);
|
||||
|
||||
pstmt_insert.setBytes(9, new Person("john", 33, true).toString().getBytes());
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -106,8 +177,10 @@ public class TSDBPreparedStatementTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void setTimestamp() {
|
||||
//TODO
|
||||
public void setTimestamp() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -121,24 +194,69 @@ public class TSDBPreparedStatementTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void clearParameters() {
|
||||
//TODO
|
||||
public void clearParameters() throws SQLException {
|
||||
pstmt_insert.clearParameters();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setObject() throws SQLException {
|
||||
pstmt_insert.setObject(1, System.currentTimeMillis());
|
||||
//TODO
|
||||
pstmt_insert.setObject(1, new Timestamp(System.currentTimeMillis()));
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(2, 111);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(3, Long.MAX_VALUE);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(4, 3.14159265354f);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(5, Double.MAX_VALUE);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(6, Short.MAX_VALUE);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(7, Byte.MAX_VALUE);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(8, true);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(9, "hello".getBytes());
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(10, "Hello");
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void execute() {
|
||||
//TODO
|
||||
}
|
||||
public void execute() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
@Test
|
||||
public void addBatch() {
|
||||
//TODO:
|
||||
executeQuery();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -176,11 +294,11 @@ public class TSDBPreparedStatementTest {
|
|||
pstmt_insert.setURL(1, null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
@Test
|
||||
public void getParameterMetaData() throws SQLException {
|
||||
ParameterMetaData parameterMetaData = pstmt_insert.getParameterMetaData();
|
||||
// Assert.assertNotNull(parameterMetaData);
|
||||
//TODO:
|
||||
Assert.assertNotNull(parameterMetaData);
|
||||
//TODO: modify the test case
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -215,10 +333,10 @@ public class TSDBPreparedStatementTest {
|
|||
Class.forName("com.taosdata.jdbc.TSDBDriver");
|
||||
conn = DriverManager.getConnection("jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata");
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("drop database if exists test_pstmt");
|
||||
stmt.execute("create database if not exists test_pstmt");
|
||||
stmt.execute("use test_pstmt");
|
||||
stmt.execute("create table weather(ts timestamp, temperature float) tags(loc nchar(64))");
|
||||
stmt.execute("drop database if exists test_pstmt_jni");
|
||||
stmt.execute("create database if not exists test_pstmt_jni");
|
||||
stmt.execute("use test_pstmt_jni");
|
||||
stmt.execute("create table weather(ts timestamp, f1 int, f2 bigint, f3 float, f4 double, f5 smallint, f6 tinyint, f7 bool, f8 binary(64), f9 nchar(64)) tags(loc nchar(64))");
|
||||
stmt.execute("create table t1 using weather tags('beijing')");
|
||||
}
|
||||
pstmt_insert = conn.prepareStatement(sql_insert);
|
||||
|
@ -231,7 +349,10 @@ public class TSDBPreparedStatementTest {
|
|||
@AfterClass
|
||||
public static void afterClass() {
|
||||
try {
|
||||
|
||||
if (pstmt_insert != null)
|
||||
pstmt_insert.close();
|
||||
if (pstmt_select != null)
|
||||
pstmt_select.close();
|
||||
if (conn != null)
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
|
|
|
@ -0,0 +1,678 @@
|
|||
package com.taosdata.jdbc;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.primitives.Shorts;
|
||||
import com.taosdata.jdbc.rs.RestfulResultSet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.*;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
public class TSDBResultSetTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
private static Connection conn;
|
||||
private static Statement stmt;
|
||||
private static ResultSet rs;
|
||||
|
||||
@Test
|
||||
public void wasNull() throws SQLException {
|
||||
Assert.assertFalse(rs.wasNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getString() throws SQLException {
|
||||
String f10 = rs.getString("f10");
|
||||
Assert.assertEquals("涛思数据", f10);
|
||||
f10 = rs.getString(10);
|
||||
Assert.assertEquals("涛思数据", f10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBoolean() throws SQLException {
|
||||
Boolean f9 = rs.getBoolean("f9");
|
||||
Assert.assertEquals(true, f9);
|
||||
f9 = rs.getBoolean(9);
|
||||
Assert.assertEquals(true, f9);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getByte() throws SQLException {
|
||||
byte f8 = rs.getByte("f8");
|
||||
Assert.assertEquals(10, f8);
|
||||
f8 = rs.getByte(8);
|
||||
Assert.assertEquals(10, f8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getShort() throws SQLException {
|
||||
short f7 = rs.getShort("f7");
|
||||
Assert.assertEquals(10, f7);
|
||||
f7 = rs.getShort(7);
|
||||
Assert.assertEquals(10, f7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInt() throws SQLException {
|
||||
int f2 = rs.getInt("f2");
|
||||
Assert.assertEquals(1, f2);
|
||||
f2 = rs.getInt(2);
|
||||
Assert.assertEquals(1, f2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLong() throws SQLException {
|
||||
long f3 = rs.getLong("f3");
|
||||
Assert.assertEquals(100, f3);
|
||||
f3 = rs.getLong(3);
|
||||
Assert.assertEquals(100, f3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFloat() throws SQLException {
|
||||
float f4 = rs.getFloat("f4");
|
||||
Assert.assertEquals(3.1415f, f4, 0f);
|
||||
f4 = rs.getFloat(4);
|
||||
Assert.assertEquals(3.1415f, f4, 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDouble() throws SQLException {
|
||||
double f5 = rs.getDouble("f5");
|
||||
Assert.assertEquals(3.1415926, f5, 0.0);
|
||||
f5 = rs.getDouble(5);
|
||||
Assert.assertEquals(3.1415926, f5, 0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBigDecimal() throws SQLException {
|
||||
BigDecimal f1 = rs.getBigDecimal("f1");
|
||||
Assert.assertEquals(1609430400000l, f1.longValue());
|
||||
|
||||
BigDecimal f2 = rs.getBigDecimal("f2");
|
||||
Assert.assertEquals(1, f2.intValue());
|
||||
|
||||
BigDecimal f3 = rs.getBigDecimal("f3");
|
||||
Assert.assertEquals(100l, f3.longValue());
|
||||
|
||||
BigDecimal f4 = rs.getBigDecimal("f4");
|
||||
Assert.assertEquals(3.1415f, f4.floatValue(), 0.00000f);
|
||||
|
||||
BigDecimal f5 = rs.getBigDecimal("f5");
|
||||
Assert.assertEquals(3.1415926, f5.doubleValue(), 0.0000000);
|
||||
|
||||
BigDecimal f7 = rs.getBigDecimal("f7");
|
||||
Assert.assertEquals(10, f7.intValue());
|
||||
|
||||
BigDecimal f8 = rs.getBigDecimal("f8");
|
||||
Assert.assertEquals(10, f8.intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBytes() throws SQLException {
|
||||
byte[] f1 = rs.getBytes("f1");
|
||||
Assert.assertEquals("2021-01-01 00:00:00.0", new String(f1));
|
||||
|
||||
byte[] f2 = rs.getBytes("f2");
|
||||
Assert.assertEquals(1, Ints.fromByteArray(f2));
|
||||
|
||||
byte[] f3 = rs.getBytes("f3");
|
||||
Assert.assertEquals(100l, Longs.fromByteArray(f3));
|
||||
|
||||
byte[] f4 = rs.getBytes("f4");
|
||||
Assert.assertEquals(3.1415f, Float.valueOf(new String(f4)), 0.000000f);
|
||||
|
||||
byte[] f5 = rs.getBytes("f5");
|
||||
Assert.assertEquals(3.1415926, Double.valueOf(new String(f5)), 0.000000f);
|
||||
|
||||
byte[] f6 = rs.getBytes("f6");
|
||||
Assert.assertEquals("abc", new String(f6));
|
||||
|
||||
byte[] f7 = rs.getBytes("f7");
|
||||
Assert.assertEquals((short) 10, Shorts.fromByteArray(f7));
|
||||
|
||||
byte[] f8 = rs.getBytes("f8");
|
||||
Assert.assertEquals(1, f8.length);
|
||||
Assert.assertEquals((byte) 10, f8[0]);
|
||||
|
||||
byte[] f9 = rs.getBytes("f9");
|
||||
Assert.assertEquals("true", new String(f9));
|
||||
|
||||
byte[] f10 = rs.getBytes("f10");
|
||||
Assert.assertEquals("涛思数据", new String(f10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDate() throws SQLException, ParseException {
|
||||
Date f1 = rs.getDate("f1");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Assert.assertEquals(sdf.parse("2021-01-01"), f1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTime() throws SQLException {
|
||||
Time f1 = rs.getTime("f1");
|
||||
Assert.assertEquals("00:00:00", f1.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTimestamp() throws SQLException {
|
||||
Timestamp f1 = rs.getTimestamp("f1");
|
||||
Assert.assertEquals("2021-01-01 00:00:00.0", f1.toString());
|
||||
f1 = rs.getTimestamp(1);
|
||||
Assert.assertEquals("2021-01-01 00:00:00.0", f1.toString());
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getAsciiStream() throws SQLException {
|
||||
rs.getAsciiStream("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getUnicodeStream() throws SQLException {
|
||||
rs.getUnicodeStream("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getBinaryStream() throws SQLException {
|
||||
rs.getBinaryStream("f1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWarnings() throws SQLException {
|
||||
Assert.assertNull(rs.getWarnings());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearWarnings() throws SQLException {
|
||||
rs.clearWarnings();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getCursorName() throws SQLException {
|
||||
rs.getCursorName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMetaData() throws SQLException {
|
||||
ResultSetMetaData meta = rs.getMetaData();
|
||||
Assert.assertNotNull(meta);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getObject() throws SQLException, ParseException {
|
||||
Object f1 = rs.getObject("f1");
|
||||
Assert.assertEquals(Timestamp.class, f1.getClass());
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.sss");
|
||||
java.util.Date date = sdf.parse("2021-01-01 00:00:00.000");
|
||||
Assert.assertEquals(new Timestamp(date.getTime()), f1);
|
||||
|
||||
Object f2 = rs.getObject("f2");
|
||||
Assert.assertEquals(Integer.class, f2.getClass());
|
||||
Assert.assertEquals(1, f2);
|
||||
|
||||
Object f3 = rs.getObject("f3");
|
||||
Assert.assertEquals(Long.class, f3.getClass());
|
||||
Assert.assertEquals(100l, f3);
|
||||
|
||||
Object f4 = rs.getObject("f4");
|
||||
Assert.assertEquals(Float.class, f4.getClass());
|
||||
Assert.assertEquals(3.1415f, f4);
|
||||
|
||||
Object f5 = rs.getObject("f5");
|
||||
Assert.assertEquals(Double.class, f5.getClass());
|
||||
Assert.assertEquals(3.1415926, f5);
|
||||
|
||||
Object f6 = rs.getObject("f6");
|
||||
Assert.assertEquals(byte[].class, f6.getClass());
|
||||
Assert.assertEquals("abc", new String((byte[]) f6));
|
||||
|
||||
Object f7 = rs.getObject("f7");
|
||||
Assert.assertEquals(Short.class, f7.getClass());
|
||||
Assert.assertEquals((short) 10, f7);
|
||||
|
||||
Object f8 = rs.getObject("f8");
|
||||
Assert.assertEquals(Byte.class, f8.getClass());
|
||||
Assert.assertEquals((byte) 10, f8);
|
||||
|
||||
Object f9 = rs.getObject("f9");
|
||||
Assert.assertEquals(Boolean.class, f9.getClass());
|
||||
Assert.assertEquals(true, f9);
|
||||
|
||||
Object f10 = rs.getObject("f10");
|
||||
Assert.assertEquals(String.class, f10.getClass());
|
||||
Assert.assertEquals("涛思数据", f10);
|
||||
}
|
||||
|
||||
@Test(expected = SQLException.class)
|
||||
public void findColumn() throws SQLException {
|
||||
int columnIndex = rs.findColumn("f1");
|
||||
Assert.assertEquals(1, columnIndex);
|
||||
columnIndex = rs.findColumn("f2");
|
||||
Assert.assertEquals(2, columnIndex);
|
||||
columnIndex = rs.findColumn("f3");
|
||||
Assert.assertEquals(3, columnIndex);
|
||||
columnIndex = rs.findColumn("f4");
|
||||
Assert.assertEquals(4, columnIndex);
|
||||
columnIndex = rs.findColumn("f5");
|
||||
Assert.assertEquals(5, columnIndex);
|
||||
columnIndex = rs.findColumn("f6");
|
||||
Assert.assertEquals(6, columnIndex);
|
||||
columnIndex = rs.findColumn("f7");
|
||||
Assert.assertEquals(7, columnIndex);
|
||||
columnIndex = rs.findColumn("f8");
|
||||
Assert.assertEquals(8, columnIndex);
|
||||
columnIndex = rs.findColumn("f9");
|
||||
Assert.assertEquals(9, columnIndex);
|
||||
columnIndex = rs.findColumn("f10");
|
||||
Assert.assertEquals(10, columnIndex);
|
||||
|
||||
rs.findColumn("f11");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getCharacterStream() throws SQLException {
|
||||
rs.getCharacterStream(1);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void isBeforeFirst() throws SQLException {
|
||||
rs.isBeforeFirst();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void isAfterLast() throws SQLException {
|
||||
rs.isAfterLast();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void isFirst() throws SQLException {
|
||||
rs.isFirst();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void isLast() throws SQLException {
|
||||
rs.isLast();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void beforeFirst() throws SQLException {
|
||||
rs.beforeFirst();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void afterLast() throws SQLException {
|
||||
rs.afterLast();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void first() throws SQLException {
|
||||
rs.first();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void last() throws SQLException {
|
||||
rs.last();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getRow() throws SQLException {
|
||||
int row = rs.getRow();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void absolute() throws SQLException {
|
||||
rs.absolute(-1);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void relative() throws SQLException {
|
||||
rs.relative(-1);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void previous() throws SQLException {
|
||||
rs.previous();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setFetchDirection() throws SQLException {
|
||||
rs.setFetchDirection(ResultSet.FETCH_FORWARD);
|
||||
Assert.assertEquals(ResultSet.FETCH_FORWARD, rs.getFetchDirection());
|
||||
rs.setFetchDirection(ResultSet.FETCH_UNKNOWN);
|
||||
Assert.assertEquals(ResultSet.FETCH_FORWARD, rs.getFetchDirection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFetchDirection() throws SQLException {
|
||||
Assert.assertEquals(ResultSet.FETCH_FORWARD, rs.getFetchDirection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setFetchSize() throws SQLException {
|
||||
rs.setFetchSize(0);
|
||||
Assert.assertEquals(0, rs.getFetchSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFetchSize() throws SQLException {
|
||||
Assert.assertEquals(0, rs.getFetchSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getType() throws SQLException {
|
||||
Assert.assertEquals(ResultSet.TYPE_FORWARD_ONLY, rs.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConcurrency() throws SQLException {
|
||||
Assert.assertEquals(ResultSet.CONCUR_READ_ONLY, rs.getConcurrency());
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void rowUpdated() throws SQLException {
|
||||
rs.rowUpdated();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void rowInserted() throws SQLException {
|
||||
rs.rowInserted();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void rowDeleted() throws SQLException {
|
||||
rs.rowDeleted();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateNull() throws SQLException {
|
||||
rs.updateNull("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateBoolean() throws SQLException {
|
||||
rs.updateBoolean(1, false);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateByte() throws SQLException {
|
||||
rs.updateByte(1, new Byte("0"));
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateShort() throws SQLException {
|
||||
rs.updateShort(1, new Short("0"));
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateInt() throws SQLException {
|
||||
rs.updateInt(1, 1);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateLong() throws SQLException {
|
||||
rs.updateLong(1, 1l);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateFloat() throws SQLException {
|
||||
rs.updateFloat(1, 1f);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateDouble() throws SQLException {
|
||||
rs.updateDouble(1, 1.0);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateBigDecimal() throws SQLException {
|
||||
rs.updateBigDecimal(1, new BigDecimal(1));
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateString() throws SQLException {
|
||||
rs.updateString(1, "abc");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateBytes() throws SQLException {
|
||||
rs.updateBytes(1, new byte[]{});
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateDate() throws SQLException {
|
||||
rs.updateDate(1, new Date(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateTime() throws SQLException {
|
||||
rs.updateTime(1, new Time(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateTimestamp() throws SQLException {
|
||||
rs.updateTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateAsciiStream() throws SQLException {
|
||||
rs.updateAsciiStream(1, null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateBinaryStream() throws SQLException {
|
||||
rs.updateBinaryStream(1, null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateCharacterStream() throws SQLException {
|
||||
rs.updateCharacterStream(1, null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateObject() throws SQLException {
|
||||
rs.updateObject(1, null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void insertRow() throws SQLException {
|
||||
rs.insertRow();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateRow() throws SQLException {
|
||||
rs.updateRow();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void deleteRow() throws SQLException {
|
||||
rs.deleteRow();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void refreshRow() throws SQLException {
|
||||
rs.refreshRow();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void cancelRowUpdates() throws SQLException {
|
||||
rs.cancelRowUpdates();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void moveToInsertRow() throws SQLException {
|
||||
rs.moveToInsertRow();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStatement() throws SQLException {
|
||||
Statement stmt = rs.getStatement();
|
||||
Assert.assertNotNull(stmt);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void moveToCurrentRow() throws SQLException {
|
||||
rs.moveToCurrentRow();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getRef() throws SQLException {
|
||||
rs.getRef(1);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getBlob() throws SQLException {
|
||||
rs.getBlob("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getClob() throws SQLException {
|
||||
rs.getClob("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getArray() throws SQLException {
|
||||
rs.getArray("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getURL() throws SQLException {
|
||||
rs.getURL("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateRef() throws SQLException {
|
||||
rs.updateRef("f1", null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateBlob() throws SQLException {
|
||||
rs.updateBlob(1, (InputStream) null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateClob() throws SQLException {
|
||||
rs.updateClob(1, (Reader) null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateArray() throws SQLException {
|
||||
rs.updateArray(1, null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getRowId() throws SQLException {
|
||||
rs.getRowId("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateRowId() throws SQLException {
|
||||
rs.updateRowId(1, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHoldability() throws SQLException {
|
||||
Assert.assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, rs.getHoldability());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isClosed() throws SQLException {
|
||||
Assert.assertFalse(rs.isClosed());
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateNString() throws SQLException {
|
||||
rs.updateNString(1, null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateNClob() throws SQLException {
|
||||
rs.updateNClob(1, (Reader) null);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getNClob() throws SQLException {
|
||||
rs.getNClob("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getSQLXML() throws SQLException {
|
||||
rs.getSQLXML("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateSQLXML() throws SQLException {
|
||||
rs.updateSQLXML(1, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNString() throws SQLException {
|
||||
String f10 = rs.getNString("f10");
|
||||
Assert.assertEquals("涛思数据", f10);
|
||||
f10 = rs.getNString(10);
|
||||
Assert.assertEquals("涛思数据", f10);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getNCharacterStream() throws SQLException {
|
||||
rs.getNCharacterStream("f1");
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void updateNCharacterStream() throws SQLException {
|
||||
rs.updateNCharacterStream(1, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unwrap() throws SQLException {
|
||||
TSDBResultSet unwrap = rs.unwrap(TSDBResultSet.class);
|
||||
Assert.assertNotNull(unwrap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isWrapperFor() throws SQLException {
|
||||
Assert.assertTrue(rs.isWrapperFor(TSDBResultSet.class));
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
try {
|
||||
Class.forName("com.taosdata.jdbc.TSDBDriver");
|
||||
conn = DriverManager.getConnection("jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata");
|
||||
stmt = conn.createStatement();
|
||||
stmt.execute("create database if not exists restful_test");
|
||||
stmt.execute("use restful_test");
|
||||
stmt.execute("drop table if exists weather");
|
||||
stmt.execute("create table if not exists weather(f1 timestamp, f2 int, f3 bigint, f4 float, f5 double, f6 binary(64), f7 smallint, f8 tinyint, f9 bool, f10 nchar(64))");
|
||||
stmt.execute("insert into restful_test.weather values('2021-01-01 00:00:00.000', 1, 100, 3.1415, 3.1415926, 'abc', 10, 10, true, '涛思数据')");
|
||||
rs = stmt.executeQuery("select * from restful_test.weather");
|
||||
rs.next();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
try {
|
||||
if (rs != null)
|
||||
rs.close();
|
||||
if (stmt != null)
|
||||
stmt.close();
|
||||
if (conn != null)
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.taosdata.jdbc.cases;
|
||||
|
||||
import com.taosdata.jdbc.TSDBDriver;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
public class DriverAutoloadTest {
|
||||
|
||||
private Properties properties;
|
||||
private String host = "127.0.0.1";
|
||||
|
||||
@Test
|
||||
public void testRestful() throws SQLException {
|
||||
final String url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata";
|
||||
Connection conn = DriverManager.getConnection(url, properties);
|
||||
Assert.assertNotNull(conn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJni() throws SQLException {
|
||||
final String url = "jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata";
|
||||
Connection conn = DriverManager.getConnection(url, properties);
|
||||
Assert.assertNotNull(conn);
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
properties = new Properties();
|
||||
properties.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
|
||||
properties.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
|
||||
properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,6 @@ import java.util.Random;
|
|||
public class InsertDbwithoutUseDbTest {
|
||||
|
||||
private static String host = "127.0.0.1";
|
||||
// private static String host = "master";
|
||||
private static Properties properties;
|
||||
private static Random random = new Random(System.currentTimeMillis());
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package com.taosdata.jdbc.cases;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class NullValueInResultSetForJdbcRestfulTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
Connection conn;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery("select * from weather");
|
||||
ResultSetMetaData meta = rs.getMetaData();
|
||||
while (rs.next()) {
|
||||
for (int i = 1; i <= meta.getColumnCount(); i++) {
|
||||
Object value = rs.getObject(i);
|
||||
System.out.print(meta.getColumnLabel(i) + ": " + value + "\t");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() throws SQLException {
|
||||
final String url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata";
|
||||
conn = DriverManager.getConnection(url);
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("drop database if exists test_null");
|
||||
stmt.execute("create database if not exists test_null");
|
||||
stmt.execute("use test_null");
|
||||
stmt.execute("create table weather(ts timestamp, f1 int, f2 bigint, f3 float, f4 double, f5 smallint, f6 tinyint, f7 bool, f8 binary(64), f9 nchar(64))");
|
||||
stmt.executeUpdate("insert into weather(ts, f1) values(now+1s, 1)");
|
||||
stmt.executeUpdate("insert into weather(ts, f2) values(now+2s, 2)");
|
||||
stmt.executeUpdate("insert into weather(ts, f3) values(now+3s, 3.0)");
|
||||
stmt.executeUpdate("insert into weather(ts, f4) values(now+4s, 4.0)");
|
||||
stmt.executeUpdate("insert into weather(ts, f5) values(now+5s, 5)");
|
||||
stmt.executeUpdate("insert into weather(ts, f6) values(now+6s, 6)");
|
||||
stmt.executeUpdate("insert into weather(ts, f7) values(now+7s, true)");
|
||||
stmt.executeUpdate("insert into weather(ts, f8) values(now+8s, 'hello')");
|
||||
stmt.executeUpdate("insert into weather(ts, f9) values(now+9s, '涛思数据')");
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
try {
|
||||
if (conn != null)
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,8 +10,8 @@ import java.util.Properties;
|
|||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class UnsignedNumberRestfulTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
private static Connection conn;
|
||||
|
||||
@Test
|
||||
|
|
|
@ -8,7 +8,6 @@ import java.sql.*;
|
|||
public class AuthenticationTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
private static final String user = "root";
|
||||
private static final String password = "taos?data";
|
||||
private Connection conn;
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.Properties;
|
|||
public class RestfulConnectionTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
|
||||
private static Connection conn;
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.sql.*;
|
|||
import java.util.Properties;
|
||||
|
||||
public class RestfulDatabaseMetaDataTest {
|
||||
// private static final String host = "master";
|
||||
private static final String host = "127.0.0.1";
|
||||
private static final String url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata";
|
||||
private static Connection connection;
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.util.Random;
|
|||
public class RestfulJDBCTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
private static Connection connection;
|
||||
private Random random = new Random(System.currentTimeMillis());
|
||||
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
package com.taosdata.jdbc.rs;
|
||||
|
||||
import com.taosdata.jdbc.TSDBConstants;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class RestfulParameterMetaDataTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
private static Connection conn;
|
||||
private static final String sql_insert = "insert into t1 values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static PreparedStatement pstmt_insert;
|
||||
private static final String sql_select = "select * from t1 where ts > ? and ts <= ? and f1 >= ?";
|
||||
private static PreparedStatement pstmt_select;
|
||||
private static ParameterMetaData parameterMetaData_insert;
|
||||
private static ParameterMetaData parameterMetaData_select;
|
||||
|
||||
@Test
|
||||
public void getParameterCount() throws SQLException {
|
||||
Assert.assertEquals(10, parameterMetaData_insert.getParameterCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNullable() throws SQLException {
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(1));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(2));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(3));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(4));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(5));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(6));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(7));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(8));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(9));
|
||||
Assert.assertEquals(ParameterMetaData.parameterNullableUnknown, parameterMetaData_insert.isNullable(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSigned() throws SQLException {
|
||||
Assert.assertEquals(false, parameterMetaData_insert.isSigned(1));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(2));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(3));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(4));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(5));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(6));
|
||||
Assert.assertEquals(true, parameterMetaData_insert.isSigned(7));
|
||||
Assert.assertEquals(false, parameterMetaData_insert.isSigned(8));
|
||||
Assert.assertEquals(false, parameterMetaData_insert.isSigned(9));
|
||||
Assert.assertEquals(false, parameterMetaData_insert.isSigned(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPrecision() throws SQLException {
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(1));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(2));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(3));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(4));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(5));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(6));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(7));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getPrecision(8));
|
||||
Assert.assertEquals(5, parameterMetaData_insert.getPrecision(9));
|
||||
Assert.assertEquals(5, parameterMetaData_insert.getPrecision(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getScale() throws SQLException {
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(1));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(2));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(3));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(4));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(5));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(6));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(7));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(8));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(9));
|
||||
Assert.assertEquals(0, parameterMetaData_insert.getScale(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterType() throws SQLException {
|
||||
Assert.assertEquals(Types.TIMESTAMP, parameterMetaData_insert.getParameterType(1));
|
||||
Assert.assertEquals(Types.INTEGER, parameterMetaData_insert.getParameterType(2));
|
||||
Assert.assertEquals(Types.BIGINT, parameterMetaData_insert.getParameterType(3));
|
||||
Assert.assertEquals(Types.FLOAT, parameterMetaData_insert.getParameterType(4));
|
||||
Assert.assertEquals(Types.DOUBLE, parameterMetaData_insert.getParameterType(5));
|
||||
Assert.assertEquals(Types.SMALLINT, parameterMetaData_insert.getParameterType(6));
|
||||
Assert.assertEquals(Types.TINYINT, parameterMetaData_insert.getParameterType(7));
|
||||
Assert.assertEquals(Types.BOOLEAN, parameterMetaData_insert.getParameterType(8));
|
||||
Assert.assertEquals(Types.BINARY, parameterMetaData_insert.getParameterType(9));
|
||||
Assert.assertEquals(Types.NCHAR, parameterMetaData_insert.getParameterType(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterTypeName() throws SQLException {
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.TIMESTAMP), parameterMetaData_insert.getParameterTypeName(1));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.INTEGER), parameterMetaData_insert.getParameterTypeName(2));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.BIGINT), parameterMetaData_insert.getParameterTypeName(3));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.FLOAT), parameterMetaData_insert.getParameterTypeName(4));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.DOUBLE), parameterMetaData_insert.getParameterTypeName(5));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.SMALLINT), parameterMetaData_insert.getParameterTypeName(6));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.TINYINT), parameterMetaData_insert.getParameterTypeName(7));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.BOOLEAN), parameterMetaData_insert.getParameterTypeName(8));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.BINARY), parameterMetaData_insert.getParameterTypeName(9));
|
||||
Assert.assertEquals(TSDBConstants.jdbcType2TaosTypeName(Types.NCHAR), parameterMetaData_insert.getParameterTypeName(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterClassName() throws SQLException {
|
||||
Assert.assertEquals(Timestamp.class.getName(), parameterMetaData_insert.getParameterClassName(1));
|
||||
Assert.assertEquals(Integer.class.getName(), parameterMetaData_insert.getParameterClassName(2));
|
||||
Assert.assertEquals(Long.class.getName(), parameterMetaData_insert.getParameterClassName(3));
|
||||
Assert.assertEquals(Float.class.getName(), parameterMetaData_insert.getParameterClassName(4));
|
||||
Assert.assertEquals(Double.class.getName(), parameterMetaData_insert.getParameterClassName(5));
|
||||
Assert.assertEquals(Short.class.getName(), parameterMetaData_insert.getParameterClassName(6));
|
||||
Assert.assertEquals(Byte.class.getName(), parameterMetaData_insert.getParameterClassName(7));
|
||||
Assert.assertEquals(Boolean.class.getName(), parameterMetaData_insert.getParameterClassName(8));
|
||||
Assert.assertEquals(byte[].class.getName(), parameterMetaData_insert.getParameterClassName(9));
|
||||
Assert.assertEquals(String.class.getName(), parameterMetaData_insert.getParameterClassName(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterMode() throws SQLException {
|
||||
for (int i = 1; i <= parameterMetaData_insert.getParameterCount(); i++) {
|
||||
int parameterMode = parameterMetaData_insert.getParameterMode(i);
|
||||
Assert.assertEquals(ParameterMetaData.parameterModeUnknown, parameterMode);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unwrap() throws SQLException {
|
||||
RestfulParameterMetaData unwrap = parameterMetaData_insert.unwrap(RestfulParameterMetaData.class);
|
||||
Assert.assertNotNull(unwrap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isWrapperFor() throws SQLException {
|
||||
Assert.assertTrue(parameterMetaData_insert.isWrapperFor(RestfulParameterMetaData.class));
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
try {
|
||||
Class.forName("com.taosdata.jdbc.rs.RestfulDriver");
|
||||
conn = DriverManager.getConnection("jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata");
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("drop database if exists test_pstmt");
|
||||
stmt.execute("create database if not exists test_pstmt");
|
||||
stmt.execute("use test_pstmt");
|
||||
stmt.execute("create table weather(ts timestamp, f1 int, f2 bigint, f3 float, f4 double, f5 smallint, f6 tinyint, f7 bool, f8 binary(64), f9 nchar(64)) tags(loc nchar(64))");
|
||||
stmt.execute("create table t1 using weather tags('beijing')");
|
||||
}
|
||||
pstmt_insert = conn.prepareStatement(sql_insert);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(2, 111);
|
||||
pstmt_insert.setObject(3, Long.MAX_VALUE);
|
||||
pstmt_insert.setObject(4, 3.14159265354f);
|
||||
pstmt_insert.setObject(5, Double.MAX_VALUE);
|
||||
pstmt_insert.setObject(6, Short.MAX_VALUE);
|
||||
pstmt_insert.setObject(7, Byte.MAX_VALUE);
|
||||
pstmt_insert.setObject(8, true);
|
||||
pstmt_insert.setObject(9, "hello".getBytes());
|
||||
pstmt_insert.setObject(10, "Hello");
|
||||
parameterMetaData_insert = pstmt_insert.getParameterMetaData();
|
||||
|
||||
pstmt_select = conn.prepareStatement(sql_select);
|
||||
pstmt_select.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_select.setTimestamp(2, new Timestamp(System.currentTimeMillis() + 10000));
|
||||
pstmt_select.setInt(3, 0);
|
||||
parameterMetaData_select = pstmt_select.getParameterMetaData();
|
||||
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
try {
|
||||
if (pstmt_insert != null)
|
||||
pstmt_insert.close();
|
||||
if (pstmt_select != null)
|
||||
pstmt_select.close();
|
||||
if (conn != null)
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,11 +5,12 @@ import org.junit.Assert;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.sql.*;
|
||||
|
||||
public class RestfulPreparedStatementTest {
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
private static Connection conn;
|
||||
private static final String sql_insert = "insert into t1 values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static PreparedStatement pstmt_insert;
|
||||
|
@ -38,48 +39,73 @@ public class RestfulPreparedStatementTest {
|
|||
@Test
|
||||
public void executeUpdate() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setFloat(2, 3.14f);
|
||||
pstmt_insert.setFloat(4, 3.14f);
|
||||
int result = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setNull() throws SQLException {
|
||||
pstmt_insert.setNull(2, Types.FLOAT);
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setNull(2, Types.INTEGER);
|
||||
int result = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setBoolean() throws SQLException {
|
||||
pstmt_insert.setBoolean(2, true);
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setBoolean(8, true);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
@Test
|
||||
public void setByte() throws SQLException {
|
||||
pstmt_insert.setByte(1, (byte) 0x001);
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setByte(7, (byte) 0x001);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setShort() {
|
||||
|
||||
public void setShort() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setShort(6, (short) 2);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setInt() {
|
||||
|
||||
public void setInt() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setInt(2, 10086);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setLong() {
|
||||
|
||||
public void setLong() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setLong(3, Long.MAX_VALUE);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setFloat() {
|
||||
|
||||
public void setFloat() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setFloat(4, 3.14f);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDouble() {
|
||||
public void setDouble() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setDouble(5, 3.14444);
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -88,12 +114,56 @@ public class RestfulPreparedStatementTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void setString() {
|
||||
public void setString() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setString(10, "aaaa");
|
||||
boolean execute = pstmt_insert.execute();
|
||||
Assert.assertFalse(execute);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setString(10, new Person("john", 33, true).toString());
|
||||
Assert.assertFalse(pstmt_insert.execute());
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setString(10, new Person("john", 33, true).toString().replaceAll("'", "\""));
|
||||
Assert.assertFalse(pstmt_insert.execute());
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void setBytes() throws SQLException {
|
||||
pstmt_insert.setBytes(1, new byte[]{});
|
||||
class Person implements Serializable {
|
||||
String name;
|
||||
int age;
|
||||
boolean sex;
|
||||
|
||||
public Person(String name, int age, boolean sex) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"name='" + name + '\'' +
|
||||
", age=" + age +
|
||||
", sex=" + sex +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setBytes() throws SQLException, IOException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
|
||||
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
// ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
// oos.writeObject(new Person("john", 33, true));
|
||||
// oos.flush();
|
||||
// byte[] bytes = baos.toByteArray();
|
||||
// pstmt_insert.setBytes(9, bytes);
|
||||
|
||||
pstmt_insert.setBytes(9, new Person("john", 33, true).toString().getBytes());
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -107,8 +177,10 @@ public class RestfulPreparedStatementTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void setTimestamp() {
|
||||
//TODO
|
||||
public void setTimestamp() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -122,24 +194,69 @@ public class RestfulPreparedStatementTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void clearParameters() {
|
||||
//TODO
|
||||
public void clearParameters() throws SQLException {
|
||||
pstmt_insert.clearParameters();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setObject() throws SQLException {
|
||||
pstmt_insert.setObject(1, System.currentTimeMillis());
|
||||
//TODO
|
||||
pstmt_insert.setObject(1, new Timestamp(System.currentTimeMillis()));
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(2, 111);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(3, Long.MAX_VALUE);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(4, 3.14159265354f);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(5, Double.MAX_VALUE);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(6, Short.MAX_VALUE);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(7, Byte.MAX_VALUE);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(8, true);
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(9, "hello".getBytes());
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
pstmt_insert.setObject(10, "Hello");
|
||||
ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void execute() {
|
||||
//TODO
|
||||
}
|
||||
public void execute() throws SQLException {
|
||||
pstmt_insert.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
||||
int ret = pstmt_insert.executeUpdate();
|
||||
Assert.assertEquals(1, ret);
|
||||
|
||||
@Test
|
||||
public void addBatch() {
|
||||
//TODO:
|
||||
executeQuery();
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
@ -180,8 +297,8 @@ public class RestfulPreparedStatementTest {
|
|||
@Test
|
||||
public void getParameterMetaData() throws SQLException {
|
||||
ParameterMetaData parameterMetaData = pstmt_insert.getParameterMetaData();
|
||||
Assert.assertNull(parameterMetaData);
|
||||
//TODO:
|
||||
Assert.assertNotNull(parameterMetaData);
|
||||
//TODO: modify the test case
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.sql.*;
|
|||
public class RestfulResultSetMetaDataTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
|
||||
private static Connection conn;
|
||||
private static Statement stmt;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package com.taosdata.jdbc.rs;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.primitives.Shorts;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -9,11 +12,12 @@ import java.io.InputStream;
|
|||
import java.io.Reader;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.*;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
public class RestfulResultSetTest {
|
||||
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
|
||||
private static Connection conn;
|
||||
private static Statement stmt;
|
||||
|
@ -88,24 +92,75 @@ public class RestfulResultSetTest {
|
|||
Assert.assertEquals(3.1415926, f5, 0.0);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
@Test
|
||||
public void getBigDecimal() throws SQLException {
|
||||
rs.getBigDecimal("f1");
|
||||
BigDecimal f1 = rs.getBigDecimal("f1");
|
||||
Assert.assertEquals(1609430400000l, f1.longValue());
|
||||
|
||||
BigDecimal f2 = rs.getBigDecimal("f2");
|
||||
Assert.assertEquals(1, f2.intValue());
|
||||
|
||||
BigDecimal f3 = rs.getBigDecimal("f3");
|
||||
Assert.assertEquals(100l, f3.longValue());
|
||||
|
||||
BigDecimal f4 = rs.getBigDecimal("f4");
|
||||
Assert.assertEquals(3.1415f, f4.floatValue(), 0.00000f);
|
||||
|
||||
BigDecimal f5 = rs.getBigDecimal("f5");
|
||||
Assert.assertEquals(3.1415926, f5.doubleValue(), 0.0000000);
|
||||
|
||||
BigDecimal f7 = rs.getBigDecimal("f7");
|
||||
Assert.assertEquals(10, f7.intValue());
|
||||
|
||||
BigDecimal f8 = rs.getBigDecimal("f8");
|
||||
Assert.assertEquals(10, f8.intValue());
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
@Test
|
||||
public void getBytes() throws SQLException {
|
||||
rs.getBytes("f1");
|
||||
byte[] f1 = rs.getBytes("f1");
|
||||
Assert.assertEquals("2021-01-01 00:00:00.0", new String(f1));
|
||||
|
||||
byte[] f2 = rs.getBytes("f2");
|
||||
Assert.assertEquals(1, Ints.fromByteArray(f2));
|
||||
|
||||
byte[] f3 = rs.getBytes("f3");
|
||||
Assert.assertEquals(100l, Longs.fromByteArray(f3));
|
||||
|
||||
byte[] f4 = rs.getBytes("f4");
|
||||
Assert.assertEquals(3.1415f, Float.valueOf(new String(f4)), 0.000000f);
|
||||
|
||||
byte[] f5 = rs.getBytes("f5");
|
||||
Assert.assertEquals(3.1415926, Double.valueOf(new String(f5)), 0.000000f);
|
||||
|
||||
byte[] f6 = rs.getBytes("f6");
|
||||
Assert.assertEquals("abc", new String(f6));
|
||||
|
||||
byte[] f7 = rs.getBytes("f7");
|
||||
Assert.assertEquals((short) 10, Shorts.fromByteArray(f7));
|
||||
|
||||
byte[] f8 = rs.getBytes("f8");
|
||||
Assert.assertEquals(1, f8.length);
|
||||
Assert.assertEquals((byte) 10, f8[0]);
|
||||
|
||||
byte[] f9 = rs.getBytes("f9");
|
||||
Assert.assertEquals("true", new String(f9));
|
||||
|
||||
byte[] f10 = rs.getBytes("f10");
|
||||
Assert.assertEquals("涛思数据", new String(f10));
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getDate() throws SQLException {
|
||||
rs.getDate("f1");
|
||||
@Test
|
||||
public void getDate() throws SQLException, ParseException {
|
||||
Date f1 = rs.getDate("f1");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Assert.assertEquals(sdf.parse("2021-01-01"), f1);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
@Test
|
||||
public void getTime() throws SQLException {
|
||||
rs.getTime("f1");
|
||||
Time f1 = rs.getTime("f1");
|
||||
Assert.assertEquals("00:00:00", f1.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -152,9 +207,49 @@ public class RestfulResultSetTest {
|
|||
Assert.assertNotNull(meta);
|
||||
}
|
||||
|
||||
@Test(expected = SQLFeatureNotSupportedException.class)
|
||||
public void getObject() throws SQLException {
|
||||
rs.getObject("f1");
|
||||
@Test
|
||||
public void getObject() throws SQLException, ParseException {
|
||||
Object f1 = rs.getObject("f1");
|
||||
Assert.assertEquals(Timestamp.class, f1.getClass());
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.sss");
|
||||
java.util.Date date = sdf.parse("2021-01-01 00:00:00.000");
|
||||
Assert.assertEquals(new Timestamp(date.getTime()), f1);
|
||||
|
||||
Object f2 = rs.getObject("f2");
|
||||
Assert.assertEquals(Integer.class, f2.getClass());
|
||||
Assert.assertEquals(1, f2);
|
||||
|
||||
Object f3 = rs.getObject("f3");
|
||||
Assert.assertEquals(Long.class, f3.getClass());
|
||||
Assert.assertEquals(100l, f3);
|
||||
|
||||
Object f4 = rs.getObject("f4");
|
||||
Assert.assertEquals(Float.class, f4.getClass());
|
||||
Assert.assertEquals(3.1415f, f4);
|
||||
|
||||
Object f5 = rs.getObject("f5");
|
||||
Assert.assertEquals(Double.class, f5.getClass());
|
||||
Assert.assertEquals(3.1415926, f5);
|
||||
|
||||
Object f6 = rs.getObject("f6");
|
||||
Assert.assertEquals(byte[].class, f6.getClass());
|
||||
Assert.assertEquals("abc", new String((byte[]) f6));
|
||||
|
||||
Object f7 = rs.getObject("f7");
|
||||
Assert.assertEquals(Short.class, f7.getClass());
|
||||
Assert.assertEquals((short) 10, f7);
|
||||
|
||||
Object f8 = rs.getObject("f8");
|
||||
Assert.assertEquals(Byte.class, f8.getClass());
|
||||
Assert.assertEquals((byte) 10, f8);
|
||||
|
||||
Object f9 = rs.getObject("f9");
|
||||
Assert.assertEquals(Boolean.class, f9.getClass());
|
||||
Assert.assertEquals(true, f9);
|
||||
|
||||
Object f10 = rs.getObject("f10");
|
||||
Assert.assertEquals(String.class, f10.getClass());
|
||||
Assert.assertEquals("涛思数据", f10);
|
||||
}
|
||||
|
||||
@Test(expected = SQLException.class)
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.UUID;
|
|||
|
||||
public class RestfulStatementTest {
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
private static Connection conn;
|
||||
private static Statement stmt;
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.sql.*;
|
|||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class SQLTest {
|
||||
private static final String host = "127.0.0.1";
|
||||
// private static final String host = "master";
|
||||
private static Connection connection;
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package com.taosdata.jdbc.utils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class OSUtilsTest {
|
||||
|
||||
private String OS;
|
||||
|
||||
@Test
|
||||
public void inWindows() {
|
||||
Assert.assertEquals(OS.indexOf("win") >= 0, OSUtils.isWindows());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMac() {
|
||||
Assert.assertEquals(OS.indexOf("mac") >= 0, OSUtils.isMac());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLinux() {
|
||||
Assert.assertEquals(OS.indexOf("nux") >= 0, OSUtils.isLinux());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
OS = System.getProperty("os.name").toLowerCase();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
!c/
|
||||
node_modules/
|
||||
package-lock.json
|
|
@ -15,7 +15,7 @@ IF (TD_LINUX_64)
|
|||
message(STATUS "unixodbc/unixodbc-dev are installed, and odbc connector will be built")
|
||||
find_package(FLEX)
|
||||
if(NOT FLEX_FOUND)
|
||||
message(FATAL_ERROR "you need to install flex first")
|
||||
message(WARNING "you need to install flex first")
|
||||
else ()
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0.0)
|
||||
message(WARNING "gcc 4.8.0 will complain too much about flex-generated code, we just bypass building ODBC driver in such case")
|
||||
|
@ -24,7 +24,7 @@ IF (TD_LINUX_64)
|
|||
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wconversion")
|
||||
ADD_SUBDIRECTORY(src)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
endif ()
|
||||
endif()
|
||||
endif()
|
||||
|
@ -33,10 +33,44 @@ IF (TD_LINUX_64)
|
|||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_DARWIN)
|
||||
find_program(HAVE_ODBCINST NAMES odbcinst)
|
||||
IF (HAVE_ODBCINST)
|
||||
include(CheckSymbolExists)
|
||||
# shall we revert CMAKE_REQUIRED_LIBRARIES and how?
|
||||
set(CMAKE_REQUIRED_LIBRARIES odbc)
|
||||
set(CMAKE_REQUIRED_INCLUDES /usr/local/include)
|
||||
set(CMAKE_REQUIRED_LINK_OPTIONS -L/usr/local/lib)
|
||||
check_symbol_exists(SQLExecute "sql.h" HAVE_ODBC_DEV)
|
||||
if(NOT (HAVE_ODBC_DEV))
|
||||
unset(HAVE_ODBC_DEV CACHE)
|
||||
message(WARNING "unixodbc-dev is not installed yet, you may install it with homebrew by typing: brew install unixodbc")
|
||||
else ()
|
||||
message(STATUS "unixodbc/unixodbc-dev are installed, and odbc connector will be built")
|
||||
find_package(FLEX)
|
||||
if(NOT FLEX_FOUND)
|
||||
message(WARNING "you need to install flex first")
|
||||
else ()
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0.0)
|
||||
message(WARNING "gcc 4.8.0 will complain too much about flex-generated code, we just bypass building ODBC driver in such case")
|
||||
else ()
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wconversion")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wconversion")
|
||||
ADD_SUBDIRECTORY(src)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
endif ()
|
||||
endif()
|
||||
endif()
|
||||
ELSE ()
|
||||
message(WARNING "unixodbc is not installed yet, you may install it under ubuntu by typing: brew install unixodbc")
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_WINDOWS_64)
|
||||
find_package(ODBC)
|
||||
if (NOT ODBC_FOUND)
|
||||
message(FATAL_ERROR "you need to install ODBC first")
|
||||
message(WARNING "you need to install ODBC first")
|
||||
else ()
|
||||
message(STATUS "ODBC_INCLUDE_DIRS: ${ODBC_INCLUDE_DIRS}")
|
||||
message(STATUS "ODBC_LIBRARIES: ${ODBC_LIBRARIES}")
|
||||
|
@ -50,6 +84,7 @@ IF (TD_WINDOWS_64)
|
|||
else ()
|
||||
ADD_SUBDIRECTORY(src)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
endif()
|
||||
ENDIF ()
|
||||
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
|
||||
# ODBC 驱动 #
|
||||
|
||||
- **TAOS ODBC驱动持续更新中**
|
||||
|
||||
- **目前导出的ODBC函数包括(注: 有些不常用的函数只是导出,但并未实现)**:
|
||||
SQLAllocEnv
|
||||
SQLFreeEnv
|
||||
SQLAllocConnect
|
||||
SQLFreeConnect
|
||||
SQLGetEnvAttr
|
||||
SQLSetEnvAttr
|
||||
SQLGetConnectAttr
|
||||
SQLGetConnectOption
|
||||
SQLGetInfo
|
||||
SQLConnect
|
||||
SQLDisconnect
|
||||
SQLAllocStmt
|
||||
SQLAllocHandle
|
||||
SQLFreeHandle
|
||||
SQLFreeStmt
|
||||
SQLExecDirect
|
||||
SQLNumResultCols
|
||||
SQLRowCount
|
||||
SQLColAttribute
|
||||
SQLGetData
|
||||
SQLFetch
|
||||
SQLPrepare
|
||||
SQLExecute
|
||||
SQLParamData
|
||||
SQLPutData
|
||||
SQLGetDiagRec
|
||||
SQLBindParameter
|
||||
SQLDescribeParam
|
||||
SQLDriverConnect
|
||||
SQLSetConnectAttr
|
||||
SQLDescribeCol
|
||||
SQLBindCol
|
||||
SQLNumParams
|
||||
SQLSetStmtAttr
|
||||
SQLBindParam
|
||||
SQLCancel
|
||||
SQLCancelHandle
|
||||
SQLCloseCursor
|
||||
SQLColumns
|
||||
SQLCopyDesc
|
||||
SQLDataSources
|
||||
SQLEndTran
|
||||
SQLFetchScroll
|
||||
SQLGetCursorName
|
||||
SQLGetDescField
|
||||
SQLGetDescRec
|
||||
SQLGetStmtAttr
|
||||
SQLGetStmtOption
|
||||
SQLGetTypeInfo
|
||||
SQLSetConnectOption
|
||||
SQLSetCursorName
|
||||
SQLSetDescField
|
||||
SQLSetDescRec
|
||||
SQLSetParam
|
||||
SQLSetStmtOption
|
||||
SQLSpecialColumns
|
||||
SQLStatistics
|
||||
SQLTables
|
||||
SQLTransact
|
||||
|
||||
`
|
||||
|
||||
- **国际化。可以通过在ODBC连接串中指定针对SQLCHAR/SQLWCHAR/taos_charset/system-locale的字符集来解决常见的环境匹配问题**.
|
||||
|
||||
- **现有的ODBC客户端工具可以籍此驱动同TAOS数据源互联,包括主流linux/macosx/windows平台**
|
||||
|
||||
- **现有的支持ODBC的编程语言可以籍此驱动同TAOS数据源互联, 例如: c/nodejs/python/rust/go已经在上述三个主流平台测试通过, 熟悉其他语言的同学可以发现这基本上是开箱即用**
|
||||
|
||||
- **持续更新中**...
|
||||
|
||||
# 编译和测试使用
|
||||
**Note**: 下述所有步骤都在TDengine项目的根目录下进行
|
||||
**Note**: 请先确保src/connector/odbc如下所示,被包含在src/CMakeLists.txt源文件中
|
||||
```
|
||||
...
|
||||
ADD_SUBDIRECTORY(dnode)
|
||||
ADD_SUBDIRECTORY(connector/odbc)
|
||||
ADD_SUBDIRECTORY(connector/jdbc)
|
||||
```
|
||||
|
||||
# Linux下的编译, 以Ubuntu为例
|
||||
```
|
||||
sudo apt install unixodbc unixodbc-dev flex
|
||||
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
|
||||
```
|
||||
# MacOSX下的编译, 以Catalina为例,依赖homebrew进行第三方工具安装[https://brew.sh/]
|
||||
```
|
||||
brew install unixodbc
|
||||
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
|
||||
```
|
||||
# Windows下的编译, 以Windows 10为例
|
||||
- 安装windows的`flex`工具. 目前我们使用[https://github.com/lexxmark/winflexbison](url). 安装完成后请确保win_flex.exe所在目录记录于`PATH`环境变量中.
|
||||
- 安装Microsoft Visual Studio工具, 以VS2019为例
|
||||
- `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"`
|
||||
- `rmdir /s /q debug`
|
||||
- `cmake -G "NMake Makefiles" -B debug`
|
||||
- `cmake --build debug`
|
||||
- `cmake --install debug`
|
||||
- 以管理员身份打开`命令提示符`
|
||||
- 安装ODBC驱动: 在上述打开的提示符下执行 `odbcconf /A {INSTALLDRIVER "TAOS | Driver=C:/TDengine/driver/todbc.dll | ConnectFunctions=YYN | DriverODBCVer=03.00"}`
|
||||
- 新增一个数据源DSN: 执行 `odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN | Server=<host>:<port>"}`
|
||||
上述步骤出现失败的话,可以参看这些链接:
|
||||
1. win flex的安装: https://github.com/lexxmark/winflexbison/releases
|
||||
2. PATH环境变量: https://jingyan.baidu.com/article/8ebacdf02d3c2949f65cd5d0.html
|
||||
3. 管理员身份: https://blog.csdn.net/weixin_41083002/article/details/81019893
|
||||
4. 安装odbc驱动/数据源: https://docs.microsoft.com/en-us/sql/odbc/odbcconf-exe?view=sql-server-ver15
|
||||
|
||||
# 测试使用
|
||||
强烈建议您在linux上编译运行taosd服务端,因为当前TAOS还没有windows侧的服务端移植.
|
||||
**Note1**: <>符号所括起的内容请按您当前的系统填写
|
||||
**Note2**: `.stmts` 文件存放的是测试用sql语句, 注意其格式为`UTF-8`(不带BOM导引头)
|
||||
## 按官方文档在linux侧启动taosd,确保选用'UTF-8'作为其字符集
|
||||
## 在linux下创建数据
|
||||
```
|
||||
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/samples/create_data.stmts
|
||||
--<或指定特殊的ODBC连接字符串 -->
|
||||
./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>' --sts ./src/connector/odbc/samples/create_data.stmts
|
||||
```
|
||||
## 在windows下检索数据
|
||||
```
|
||||
.\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;enc_char=UTF-8" --sts .\src\connector\odbc\samples\query_data.stmts
|
||||
```
|
||||
## 在MacOSX下检索数据
|
||||
```
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>" --sts ./src/connector/odbc/samples/query_data.stmts
|
||||
```
|
||||
|
||||
## 代码示例
|
||||
- src/connector/odbc/examples/c
|
||||
- src/connector/odbc/examples/js
|
||||
- src/connector/odbc/examples/py
|
||||
- src/connector/odbc/examples/rust
|
||||
- src/connector/odbc/examples/go
|
||||
|
||||
在linux或MacOSX上, 可以通过修改运行如下脚本来尝试各种测试:
|
||||
**Note**: 不要忘记替换<host>:<port>
|
||||
**Note**: 你需要在你的平台上安装nodejs/python/rust/go
|
||||
**Note**: 你还需要安装对应语言的ODBC包:
|
||||
-- node-odbc for nodejs: https://www.npmjs.com/package/odbc
|
||||
-- pyodbc for python: https://pypi.org/project/pyodbc/
|
||||
-- rust-odbc for rust: https://docs.rs/odbc/0.17.0/odbc/
|
||||
-- go-odbc for go: https://github.com/alexbrainman/odbc
|
||||
|
||||
```
|
||||
echo c &&
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --sts src/connector/odbc/samples/create_data.stmts &&
|
||||
echo nodejs &&
|
||||
./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
|
||||
echo python &&
|
||||
python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
|
||||
echo rust &&
|
||||
pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=<host>:<port>' cargo run && popd &&
|
||||
echo go &&
|
||||
DSN='DSN=TAOS_DSN;Server=<host>:<port>' go run src/connector/odbc/examples/go/odbc.go &&
|
||||
```
|
||||
|
||||
## 您可以对比测试一下prepared-batch-insert是否会带来速度的提升:
|
||||
**注** src/connector/odbc/examples/c/main.c是tcodbc的源代码
|
||||
```
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --insert --batch_size 200 --batchs 10000
|
||||
```
|
||||
|
||||
|
|
@ -1,20 +1,25 @@
|
|||
|
||||
# ODBC Driver #
|
||||
|
||||
- **very initial implementation of ODBC driver for TAOS
|
||||
- **on-going implementation of ODBC driver for TAOS**
|
||||
|
||||
- **currently partially supported ODBC functions are: `
|
||||
- **currently exported ODBC functions are**:
|
||||
SQLAllocEnv
|
||||
SQLFreeEnv
|
||||
SQLAllocConnect
|
||||
SQLFreeConnect
|
||||
SQLGetEnvAttr
|
||||
SQLSetEnvAttr
|
||||
SQLGetConnectAttr
|
||||
SQLGetConnectOption
|
||||
SQLGetInfo
|
||||
SQLConnect
|
||||
SQLDisconnect
|
||||
SQLAllocStmt
|
||||
SQLAllocHandle
|
||||
SQLFreeHandle
|
||||
SQLFreeStmt
|
||||
SQLExecDirect
|
||||
SQLExecDirectW
|
||||
SQLNumResultCols
|
||||
SQLRowCount
|
||||
SQLColAttribute
|
||||
|
@ -22,29 +27,62 @@ SQLGetData
|
|||
SQLFetch
|
||||
SQLPrepare
|
||||
SQLExecute
|
||||
SQLGetDiagField
|
||||
SQLParamData
|
||||
SQLPutData
|
||||
SQLGetDiagRec
|
||||
SQLBindParameter
|
||||
SQLDescribeParam
|
||||
SQLDriverConnect
|
||||
SQLSetConnectAttr
|
||||
SQLDescribeCol
|
||||
SQLBindCol
|
||||
SQLNumParams
|
||||
SQLSetStmtAttr
|
||||
ConfigDSN
|
||||
SQLBindParam
|
||||
SQLCancel
|
||||
SQLCancelHandle
|
||||
SQLCloseCursor
|
||||
SQLColumns
|
||||
SQLCopyDesc
|
||||
SQLDataSources
|
||||
SQLEndTran
|
||||
SQLFetchScroll
|
||||
SQLGetCursorName
|
||||
SQLGetDescField
|
||||
SQLGetDescRec
|
||||
SQLGetStmtAttr
|
||||
SQLGetStmtOption
|
||||
SQLGetTypeInfo
|
||||
SQLSetConnectOption
|
||||
SQLSetCursorName
|
||||
SQLSetDescField
|
||||
SQLSetDescRec
|
||||
SQLSetParam
|
||||
SQLSetStmtOption
|
||||
SQLSpecialColumns
|
||||
SQLStatistics
|
||||
SQLTables
|
||||
SQLTransact
|
||||
|
||||
`
|
||||
|
||||
- **internationalized, you can specify different charset/code page for easy going. eg.: insert `utf-8.zh_cn` characters into database located in linux machine, while query them out in `gb2312/gb18030/...` code page in your chinese windows machine, or vice-versa. and much fun, insert `gb2312/gb18030/...` characters into database located in linux box from
|
||||
your japanese windows box, and query them out in your local chinese windows machine.
|
||||
- **internationalized, you can specify charset for SQLCHAR/SQLWCHAR/taos_charset/system-locale to coordinate with the environment**.
|
||||
|
||||
- **enable ODBC-aware software to communicate with TAOS.
|
||||
- **enable ODBC-aware software to communicate with TAOS, no matter what platform it's running on, currently we support linux/macosx/windows**
|
||||
|
||||
- **enable any language with ODBC-bindings/ODBC-plugings to communicate with TAOS
|
||||
- **enable any language with ODBC-bindings/ODBC-plugings to communicate with TAOS, currently c/nodejs/python/rust/go are all passed in our test environment, we believe other languages with ODBC-bindings/plugins are available-out-of-box**
|
||||
|
||||
- **still going on...
|
||||
- **still going on**...
|
||||
|
||||
# Building and Testing
|
||||
**Note**: all `work` is done in TDengine's project directory
|
||||
|
||||
**Note**: please make sure src/connector/odbc is included in src/CMakeLists.txt
|
||||
```
|
||||
...
|
||||
ADD_SUBDIRECTORY(dnode)
|
||||
ADD_SUBDIRECTORY(connector/odbc)
|
||||
ADD_SUBDIRECTORY(connector/jdbc)
|
||||
```
|
||||
|
||||
# Building under Linux, use Ubuntu as example
|
||||
```
|
||||
|
@ -53,36 +91,68 @@ rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug &
|
|||
```
|
||||
# Building under Windows, use Windows 10 as example
|
||||
- install windows `flex` port. We use [https://github.com/lexxmark/winflexbison](url) at the moment. Please be noted to append `<path_to_win_flex.exe>` to your `PATH`.
|
||||
- install Microsoft Visual Studio, take VS2015 as example here
|
||||
- `"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64`
|
||||
- install Microsoft Visual Studio, take VS2019 as example here
|
||||
- `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"`
|
||||
- `rmdir /s /q debug`
|
||||
- `cmake -G "NMake Makefiles" -B debug`
|
||||
- `cmake --build debug`
|
||||
- `cmake --install debug`
|
||||
- open your `Command Prompt` with Administrator's priviledge
|
||||
- remove previously installed TAOS ODBC driver: run `C:\TDengine\todbcinst -u -f -n TAOS`
|
||||
- install TAOS ODBC driver that was just built: run `C:\TDengine\todbcinst -i -n TAOS -p C:\TDengine\driver`
|
||||
- add a new user dsn: run `odbcconf CONFIGDSN TAOS "DSN=TAOS_DSN|Server=<fqdn>:<port>`
|
||||
- install TAOS ODBC driver that was just built: run `odbcconf /A {INSTALLDRIVER "TAOS | Driver=C:/TDengine/driver/todbc.dll | ConnectFunctions=YYN | DriverODBCVer=03.00"}`
|
||||
- add a new user dsn: run `odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN | Server=host:port"}`
|
||||
|
||||
# Test
|
||||
we highly suggest that you build both in linux(ubuntu) and windows(windows 10) platform, because currently TAOS only has it's server-side port on linux platform.
|
||||
we highly suggest that you build both in linux(ubuntu) and windows(windows 10) platform, because currently TAOS has not server-side port on windows platform.
|
||||
**Note1**: content within <> shall be modified to match your environment
|
||||
**Note2**: `.stmts` source files are all encoded in `UTF-8`
|
||||
## start taosd in linux, suppose charset is `UTF-8` as default
|
||||
```
|
||||
taosd -c ./debug/test/cfg
|
||||
```
|
||||
## start taosd in linux, suppose charset is `UTF-8` as default, please follow TAOS doc for starting up
|
||||
## create data in linux
|
||||
```
|
||||
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/tests/create_data.stmts
|
||||
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/samples/create_data.stmts
|
||||
--<or with driver connection string -->
|
||||
./debug/build/bin/tcodbc --dcs 'Driver=TAOS;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;client_enc=UTF-8' ./src/connector/odbc/tests/create_data.stmts
|
||||
./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>' --sts ./src/connector/odbc/samples/create_data.stmts
|
||||
```
|
||||
## query data in windows
|
||||
```
|
||||
.\debug\build\bin\tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts .\src\connector\odbc\tests\query_data.stmts
|
||||
--<or with driver connection string -->
|
||||
.\debug\build\bin\tcodbc --dcs "Driver=TAOS;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;client_enc=UTF-8" .\src\connector\odbc\tests\query_data.stmts
|
||||
.\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;enc_char=UTF-8" --sts .\src\connector\odbc\samples\query_data.stmts
|
||||
```
|
||||
## query data in MacOSX
|
||||
```
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>" --sts ./src/connector/odbc/samples/query_data.stmts
|
||||
```
|
||||
|
||||
## code examples
|
||||
- src/connector/odbc/examples/c
|
||||
- src/connector/odbc/examples/js
|
||||
- src/connector/odbc/examples/py
|
||||
- src/connector/odbc/examples/rust
|
||||
- src/connector/odbc/examples/go
|
||||
|
||||
on linux/MacOSX, here after are script-snippet for you to play with:
|
||||
**Note**: don't forget to replace <host>:<port> with whatever on your environment
|
||||
**Note**: you need to install node/python3/rust/go on you machine
|
||||
**Note**: you also need to install odbc-bindings/odbc-pluggins on those language platform, such as:
|
||||
-- node-odbc for nodejs: https://www.npmjs.com/package/odbc
|
||||
-- pyodbc for python: https://pypi.org/project/pyodbc/
|
||||
-- rust-odbc for rust: https://docs.rs/odbc/0.17.0/odbc/
|
||||
-- go-odbc for go: https://github.com/alexbrainman/odbc
|
||||
|
||||
```
|
||||
echo c &&
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --sts src/connector/odbc/samples/create_data.stmts &&
|
||||
echo nodejs &&
|
||||
./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
|
||||
echo python &&
|
||||
python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
|
||||
echo rust &&
|
||||
pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=<host>:<port>' cargo run && popd &&
|
||||
echo go &&
|
||||
DSN='DSN=TAOS_DSN;Server=<host>:<port>' go run src/connector/odbc/examples/go/odbc.go &&
|
||||
```
|
||||
|
||||
## see how fast prepared-statment could bring up with:
|
||||
```
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --insert --batch_size 200 --batchs 10000
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
PROJECT(TDengine)
|
||||
|
||||
ADD_SUBDIRECTORY(c)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
PROJECT(TDengine)
|
||||
|
||||
ADD_EXECUTABLE(tcodbc main.c ../../src/todbc_log.c)
|
||||
|
||||
IF (TD_LINUX OR TD_DARWIN)
|
||||
TARGET_LINK_LIBRARIES(tcodbc taos odbc)
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_DARWIN)
|
||||
target_include_directories(tcodbc PRIVATE /usr/local/include)
|
||||
target_link_directories(tcodbc PUBLIC /usr/local/lib)
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_WINDOWS_64)
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /GL")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL")
|
||||
TARGET_LINK_LIBRARIES(tcodbc taos_static odbc32 odbccp32 user32 legacy_stdio_definitions os)
|
||||
|
||||
ADD_EXECUTABLE(tms main.cpp)
|
||||
TARGET_LINK_LIBRARIES(tms odbc32)
|
||||
ENDIF ()
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,654 @@
|
|||
/*******************************************************************************
|
||||
/* ODBCSQL: a sample program that implements an ODBC command line interpreter.
|
||||
/*
|
||||
/* USAGE: ODBCSQL DSN=<dsn name> or
|
||||
/* ODBCSQL FILEDSN=<file dsn> or
|
||||
/* ODBCSQL DRIVER={driver name}
|
||||
/*
|
||||
/*
|
||||
/* Copyright(c) Microsoft Corporation. This is a WDAC sample program and
|
||||
/* is not suitable for use in production environments.
|
||||
/*
|
||||
/******************************************************************************/
|
||||
/* Modules:
|
||||
/* Main Main driver loop, executes queries.
|
||||
/* DisplayResults Display the results of the query if any
|
||||
/* AllocateBindings Bind column data
|
||||
/* DisplayTitles Print column titles
|
||||
/* SetConsole Set console display mode
|
||||
/* HandleError Show ODBC error messages
|
||||
/******************************************************************************/
|
||||
|
||||
#define _UNICODE
|
||||
#define UNICODE
|
||||
|
||||
#include <windows.h>
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include <stdio.h>
|
||||
#include <conio.h>
|
||||
#include <tchar.h>
|
||||
#include <stdlib.h>
|
||||
#include <sal.h>
|
||||
|
||||
|
||||
/*******************************************/
|
||||
/* Macro to call ODBC functions and */
|
||||
/* report an error on failure. */
|
||||
/* Takes handle, handle type, and stmt */
|
||||
/*******************************************/
|
||||
|
||||
#define TRYODBC(h, ht, x) { RETCODE rc = x;\
|
||||
if (rc != SQL_SUCCESS) \
|
||||
{ \
|
||||
HandleDiagnosticRecord (h, ht, rc); \
|
||||
} \
|
||||
if (rc == SQL_ERROR) \
|
||||
{ \
|
||||
fwprintf(stderr, L"Error in " L#x L"\n"); \
|
||||
goto Exit; \
|
||||
} \
|
||||
}
|
||||
/******************************************/
|
||||
/* Structure to store information about */
|
||||
/* a column.
|
||||
/******************************************/
|
||||
|
||||
typedef struct STR_BINDING {
|
||||
SQLSMALLINT cDisplaySize; /* size to display */
|
||||
WCHAR *wszBuffer; /* display buffer */
|
||||
SQLLEN indPtr; /* size or null */
|
||||
BOOL fChar; /* character col? */
|
||||
struct STR_BINDING *sNext; /* linked list */
|
||||
} BINDING;
|
||||
|
||||
|
||||
|
||||
/******************************************/
|
||||
/* Forward references */
|
||||
/******************************************/
|
||||
|
||||
void HandleDiagnosticRecord (SQLHANDLE hHandle,
|
||||
SQLSMALLINT hType,
|
||||
RETCODE RetCode);
|
||||
|
||||
void DisplayResults(HSTMT hStmt,
|
||||
SQLSMALLINT cCols);
|
||||
|
||||
void AllocateBindings(HSTMT hStmt,
|
||||
SQLSMALLINT cCols,
|
||||
BINDING** ppBinding,
|
||||
SQLSMALLINT* pDisplay);
|
||||
|
||||
|
||||
void DisplayTitles(HSTMT hStmt,
|
||||
DWORD cDisplaySize,
|
||||
BINDING* pBinding);
|
||||
|
||||
void SetConsole(DWORD cDisplaySize,
|
||||
BOOL fInvert);
|
||||
|
||||
/*****************************************/
|
||||
/* Some constants */
|
||||
/*****************************************/
|
||||
|
||||
|
||||
#define DISPLAY_MAX 50 // Arbitrary limit on column width to display
|
||||
#define DISPLAY_FORMAT_EXTRA 3 // Per column extra display bytes (| <data> )
|
||||
#define DISPLAY_FORMAT L"%c %*.*s "
|
||||
#define DISPLAY_FORMAT_C L"%c %-*.*s "
|
||||
#define NULL_SIZE 6 // <NULL>
|
||||
#define SQL_QUERY_SIZE 1000 // Max. Num characters for SQL Query passed in.
|
||||
|
||||
#define PIPE L'|'
|
||||
|
||||
SHORT gHeight = 80; // Users screen height
|
||||
|
||||
int __cdecl wmain(int argc, _In_reads_(argc) WCHAR **argv)
|
||||
{
|
||||
SQLHENV hEnv = NULL;
|
||||
SQLHDBC hDbc = NULL;
|
||||
SQLHSTMT hStmt = NULL;
|
||||
WCHAR* pwszConnStr;
|
||||
WCHAR wszInput[SQL_QUERY_SIZE];
|
||||
|
||||
// Allocate an environment
|
||||
|
||||
if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv) == SQL_ERROR)
|
||||
{
|
||||
fwprintf(stderr, L"Unable to allocate an environment handle\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Register this as an application that expects 3.x behavior,
|
||||
// you must register something if you use AllocHandle
|
||||
|
||||
TRYODBC(hEnv,
|
||||
SQL_HANDLE_ENV,
|
||||
SQLSetEnvAttr(hEnv,
|
||||
SQL_ATTR_ODBC_VERSION,
|
||||
(SQLPOINTER)SQL_OV_ODBC3,
|
||||
0));
|
||||
|
||||
// Allocate a connection
|
||||
TRYODBC(hEnv,
|
||||
SQL_HANDLE_ENV,
|
||||
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
pwszConnStr = *++argv;
|
||||
}
|
||||
else
|
||||
{
|
||||
pwszConnStr = L"";
|
||||
}
|
||||
|
||||
// Connect to the driver. Use the connection string if supplied
|
||||
// on the input, otherwise let the driver manager prompt for input.
|
||||
|
||||
TRYODBC(hDbc,
|
||||
SQL_HANDLE_DBC,
|
||||
SQLDriverConnect(hDbc,
|
||||
GetDesktopWindow(),
|
||||
pwszConnStr,
|
||||
SQL_NTS,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
SQL_DRIVER_COMPLETE));
|
||||
|
||||
fwprintf(stderr, L"Connected!\n");
|
||||
|
||||
TRYODBC(hDbc,
|
||||
SQL_HANDLE_DBC,
|
||||
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
|
||||
|
||||
wprintf(L"Enter SQL commands, type (control)Z to exit\nSQL COMMAND>");
|
||||
|
||||
// Loop to get input and execute queries
|
||||
|
||||
while(_fgetts(wszInput, SQL_QUERY_SIZE-1, stdin))
|
||||
{
|
||||
RETCODE RetCode;
|
||||
SQLSMALLINT sNumResults;
|
||||
|
||||
// Execute the query
|
||||
|
||||
if (!(*wszInput))
|
||||
{
|
||||
wprintf(L"SQL COMMAND>");
|
||||
continue;
|
||||
}
|
||||
RetCode = SQLExecDirect(hStmt,wszInput, SQL_NTS);
|
||||
|
||||
switch(RetCode)
|
||||
{
|
||||
case SQL_SUCCESS_WITH_INFO:
|
||||
{
|
||||
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
|
||||
// fall through
|
||||
}
|
||||
case SQL_SUCCESS:
|
||||
{
|
||||
// If this is a row-returning query, display
|
||||
// results
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLNumResultCols(hStmt,&sNumResults));
|
||||
|
||||
if (sNumResults > 0)
|
||||
{
|
||||
DisplayResults(hStmt,sNumResults);
|
||||
}
|
||||
else
|
||||
{
|
||||
SQLLEN cRowCount;
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLRowCount(hStmt,&cRowCount));
|
||||
|
||||
if (cRowCount >= 0)
|
||||
{
|
||||
wprintf(L"%Id %s affected\n",
|
||||
cRowCount,
|
||||
cRowCount == 1 ? L"row" : L"rows");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SQL_ERROR:
|
||||
{
|
||||
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
fwprintf(stderr, L"Unexpected return code %hd!\n", RetCode);
|
||||
|
||||
}
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLFreeStmt(hStmt, SQL_CLOSE));
|
||||
|
||||
wprintf(L"SQL COMMAND>");
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
// Free ODBC handles and exit
|
||||
|
||||
if (hStmt)
|
||||
{
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
||||
}
|
||||
|
||||
if (hDbc)
|
||||
{
|
||||
SQLDisconnect(hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
|
||||
}
|
||||
|
||||
if (hEnv)
|
||||
{
|
||||
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
|
||||
}
|
||||
|
||||
wprintf(L"\nDisconnected.");
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
/* DisplayResults: display results of a select query
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt ODBC statement handle
|
||||
/* cCols Count of columns
|
||||
/************************************************************************/
|
||||
|
||||
void DisplayResults(HSTMT hStmt,
|
||||
SQLSMALLINT cCols)
|
||||
{
|
||||
BINDING *pFirstBinding, *pThisBinding;
|
||||
SQLSMALLINT cDisplaySize;
|
||||
RETCODE RetCode = SQL_SUCCESS;
|
||||
int iCount = 0;
|
||||
|
||||
// Allocate memory for each column
|
||||
|
||||
AllocateBindings(hStmt, cCols, &pFirstBinding, &cDisplaySize);
|
||||
|
||||
// Set the display mode and write the titles
|
||||
|
||||
DisplayTitles(hStmt, cDisplaySize+1, pFirstBinding);
|
||||
|
||||
|
||||
// Fetch and display the data
|
||||
|
||||
bool fNoData = false;
|
||||
|
||||
do {
|
||||
// Fetch a row
|
||||
|
||||
if (iCount++ >= gHeight - 2)
|
||||
{
|
||||
int nInputChar;
|
||||
bool fEnterReceived = false;
|
||||
|
||||
while(!fEnterReceived)
|
||||
{
|
||||
wprintf(L" ");
|
||||
SetConsole(cDisplaySize+2, TRUE);
|
||||
wprintf(L" Press ENTER to continue, Q to quit (height:%hd)", gHeight);
|
||||
SetConsole(cDisplaySize+2, FALSE);
|
||||
|
||||
nInputChar = _getch();
|
||||
wprintf(L"\n");
|
||||
if ((nInputChar == 'Q') || (nInputChar == 'q'))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
else if ('\r' == nInputChar)
|
||||
{
|
||||
fEnterReceived = true;
|
||||
}
|
||||
// else loop back to display prompt again
|
||||
}
|
||||
|
||||
iCount = 1;
|
||||
DisplayTitles(hStmt, cDisplaySize+1, pFirstBinding);
|
||||
}
|
||||
|
||||
TRYODBC(hStmt, SQL_HANDLE_STMT, RetCode = SQLFetch(hStmt));
|
||||
|
||||
if (RetCode == SQL_NO_DATA_FOUND)
|
||||
{
|
||||
fNoData = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Display the data. Ignore truncations
|
||||
|
||||
for (pThisBinding = pFirstBinding;
|
||||
pThisBinding;
|
||||
pThisBinding = pThisBinding->sNext)
|
||||
{
|
||||
if (pThisBinding->indPtr != SQL_NULL_DATA)
|
||||
{
|
||||
wprintf(pThisBinding->fChar ? DISPLAY_FORMAT_C:DISPLAY_FORMAT,
|
||||
PIPE,
|
||||
pThisBinding->cDisplaySize,
|
||||
pThisBinding->cDisplaySize,
|
||||
pThisBinding->wszBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
wprintf(DISPLAY_FORMAT_C,
|
||||
PIPE,
|
||||
pThisBinding->cDisplaySize,
|
||||
pThisBinding->cDisplaySize,
|
||||
L"<NULL>");
|
||||
}
|
||||
}
|
||||
wprintf(L" %c\n",PIPE);
|
||||
}
|
||||
} while (!fNoData);
|
||||
|
||||
SetConsole(cDisplaySize+2, TRUE);
|
||||
wprintf(L"%*.*s", cDisplaySize+2, cDisplaySize+2, L" ");
|
||||
SetConsole(cDisplaySize+2, FALSE);
|
||||
wprintf(L"\n");
|
||||
|
||||
Exit:
|
||||
// Clean up the allocated buffers
|
||||
|
||||
while (pFirstBinding)
|
||||
{
|
||||
pThisBinding = pFirstBinding->sNext;
|
||||
free(pFirstBinding->wszBuffer);
|
||||
free(pFirstBinding);
|
||||
pFirstBinding = pThisBinding;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
/* AllocateBindings: Get column information and allocate bindings
|
||||
/* for each column.
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt Statement handle
|
||||
/* cCols Number of columns in the result set
|
||||
/* *lppBinding Binding pointer (returned)
|
||||
/* lpDisplay Display size of one line
|
||||
/************************************************************************/
|
||||
|
||||
void AllocateBindings(HSTMT hStmt,
|
||||
SQLSMALLINT cCols,
|
||||
BINDING **ppBinding,
|
||||
SQLSMALLINT *pDisplay)
|
||||
{
|
||||
SQLSMALLINT iCol;
|
||||
BINDING *pThisBinding, *pLastBinding = NULL;
|
||||
SQLLEN cchDisplay, ssType;
|
||||
SQLSMALLINT cchColumnNameLength;
|
||||
|
||||
*pDisplay = 0;
|
||||
|
||||
for (iCol = 1; iCol <= cCols; iCol++)
|
||||
{
|
||||
pThisBinding = (BINDING *)(malloc(sizeof(BINDING)));
|
||||
if (!(pThisBinding))
|
||||
{
|
||||
fwprintf(stderr, L"Out of memory!\n");
|
||||
exit(-100);
|
||||
}
|
||||
|
||||
if (iCol == 1)
|
||||
{
|
||||
*ppBinding = pThisBinding;
|
||||
}
|
||||
else
|
||||
{
|
||||
pLastBinding->sNext = pThisBinding;
|
||||
}
|
||||
pLastBinding = pThisBinding;
|
||||
|
||||
|
||||
// Figure out the display length of the column (we will
|
||||
// bind to char since we are only displaying data, in general
|
||||
// you should bind to the appropriate C type if you are going
|
||||
// to manipulate data since it is much faster...)
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLColAttribute(hStmt,
|
||||
iCol,
|
||||
SQL_DESC_DISPLAY_SIZE,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&cchDisplay));
|
||||
|
||||
|
||||
// Figure out if this is a character or numeric column; this is
|
||||
// used to determine if we want to display the data left- or right-
|
||||
// aligned.
|
||||
|
||||
// SQL_DESC_CONCISE_TYPE maps to the 1.x SQL_COLUMN_TYPE.
|
||||
// This is what you must use if you want to work
|
||||
// against a 2.x driver.
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLColAttribute(hStmt,
|
||||
iCol,
|
||||
SQL_DESC_CONCISE_TYPE,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&ssType));
|
||||
|
||||
pThisBinding->fChar = (ssType == SQL_CHAR ||
|
||||
ssType == SQL_VARCHAR ||
|
||||
ssType == SQL_LONGVARCHAR);
|
||||
|
||||
pThisBinding->sNext = NULL;
|
||||
|
||||
// Arbitrary limit on display size
|
||||
if (cchDisplay > DISPLAY_MAX)
|
||||
cchDisplay = DISPLAY_MAX;
|
||||
|
||||
// Allocate a buffer big enough to hold the text representation
|
||||
// of the data. Add one character for the null terminator
|
||||
|
||||
pThisBinding->wszBuffer = (WCHAR *)malloc((cchDisplay+1) * sizeof(WCHAR));
|
||||
|
||||
if (!(pThisBinding->wszBuffer))
|
||||
{
|
||||
fwprintf(stderr, L"Out of memory!\n");
|
||||
exit(-100);
|
||||
}
|
||||
|
||||
// Map this buffer to the driver's buffer. At Fetch time,
|
||||
// the driver will fill in this data. Note that the size is
|
||||
// count of bytes (for Unicode). All ODBC functions that take
|
||||
// SQLPOINTER use count of bytes; all functions that take only
|
||||
// strings use count of characters.
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLBindCol(hStmt,
|
||||
iCol,
|
||||
SQL_C_TCHAR,
|
||||
(SQLPOINTER) pThisBinding->wszBuffer,
|
||||
(cchDisplay + 1) * sizeof(WCHAR),
|
||||
&pThisBinding->indPtr));
|
||||
|
||||
|
||||
// Now set the display size that we will use to display
|
||||
// the data. Figure out the length of the column name
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLColAttribute(hStmt,
|
||||
iCol,
|
||||
SQL_DESC_NAME,
|
||||
NULL,
|
||||
0,
|
||||
&cchColumnNameLength,
|
||||
NULL));
|
||||
|
||||
pThisBinding->cDisplaySize = max((SQLSMALLINT)cchDisplay, cchColumnNameLength);
|
||||
if (pThisBinding->cDisplaySize < NULL_SIZE)
|
||||
pThisBinding->cDisplaySize = NULL_SIZE;
|
||||
|
||||
*pDisplay += pThisBinding->cDisplaySize + DISPLAY_FORMAT_EXTRA;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
Exit:
|
||||
|
||||
exit(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
/* DisplayTitles: print the titles of all the columns and set the
|
||||
/* shell window's width
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt Statement handle
|
||||
/* cDisplaySize Total display size
|
||||
/* pBinding list of binding information
|
||||
/************************************************************************/
|
||||
|
||||
void DisplayTitles(HSTMT hStmt,
|
||||
DWORD cDisplaySize,
|
||||
BINDING *pBinding)
|
||||
{
|
||||
WCHAR wszTitle[DISPLAY_MAX];
|
||||
SQLSMALLINT iCol = 1;
|
||||
|
||||
SetConsole(cDisplaySize+2, TRUE);
|
||||
|
||||
for (; pBinding; pBinding = pBinding->sNext)
|
||||
{
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLColAttribute(hStmt,
|
||||
iCol++,
|
||||
SQL_DESC_NAME,
|
||||
wszTitle,
|
||||
sizeof(wszTitle), // Note count of bytes!
|
||||
NULL,
|
||||
NULL));
|
||||
|
||||
wprintf(DISPLAY_FORMAT_C,
|
||||
PIPE,
|
||||
pBinding->cDisplaySize,
|
||||
pBinding->cDisplaySize,
|
||||
wszTitle);
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
wprintf(L" %c", PIPE);
|
||||
SetConsole(cDisplaySize+2, FALSE);
|
||||
wprintf(L"\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
/* SetConsole: sets console display size and video mode
|
||||
/*
|
||||
/* Parameters
|
||||
/* siDisplaySize Console display size
|
||||
/* fInvert Invert video?
|
||||
/************************************************************************/
|
||||
|
||||
void SetConsole(DWORD dwDisplaySize,
|
||||
BOOL fInvert)
|
||||
{
|
||||
HANDLE hConsole;
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbInfo;
|
||||
|
||||
// Reset the console screen buffer size if necessary
|
||||
|
||||
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (hConsole != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (GetConsoleScreenBufferInfo(hConsole, &csbInfo))
|
||||
{
|
||||
if (csbInfo.dwSize.X < (SHORT) dwDisplaySize)
|
||||
{
|
||||
csbInfo.dwSize.X = (SHORT) dwDisplaySize;
|
||||
SetConsoleScreenBufferSize(hConsole, csbInfo.dwSize);
|
||||
}
|
||||
|
||||
gHeight = csbInfo.dwSize.Y;
|
||||
}
|
||||
|
||||
if (fInvert)
|
||||
{
|
||||
SetConsoleTextAttribute(hConsole, (WORD)(csbInfo.wAttributes | BACKGROUND_BLUE));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetConsoleTextAttribute(hConsole, (WORD)(csbInfo.wAttributes & ~(BACKGROUND_BLUE)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
/* HandleDiagnosticRecord : display error/warning information
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hHandle ODBC handle
|
||||
/* hType Type of handle (HANDLE_STMT, HANDLE_ENV, HANDLE_DBC)
|
||||
/* RetCode Return code of failing command
|
||||
/************************************************************************/
|
||||
|
||||
void HandleDiagnosticRecord (SQLHANDLE hHandle,
|
||||
SQLSMALLINT hType,
|
||||
RETCODE RetCode)
|
||||
{
|
||||
SQLSMALLINT iRec = 0;
|
||||
SQLINTEGER iError;
|
||||
WCHAR wszMessage[1000];
|
||||
WCHAR wszState[SQL_SQLSTATE_SIZE+1];
|
||||
|
||||
|
||||
if (RetCode == SQL_INVALID_HANDLE)
|
||||
{
|
||||
fwprintf(stderr, L"Invalid handle!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (SQLGetDiagRec(hType,
|
||||
hHandle,
|
||||
++iRec,
|
||||
wszState,
|
||||
&iError,
|
||||
wszMessage,
|
||||
(SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
|
||||
(SQLSMALLINT *)NULL) == SQL_SUCCESS)
|
||||
{
|
||||
// Hide data truncated..
|
||||
if (wcsncmp(wszState, L"01004", 5))
|
||||
{
|
||||
fwprintf(stderr, L"[%5.5s] %s (%d)\n", wszState, wszMessage, iError);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
_ "github.com/alexbrainman/odbc"
|
||||
)
|
||||
|
||||
var pool *sql.DB // Database connection pool.
|
||||
|
||||
func main() {
|
||||
id := flag.Int64("id", 32768, "person ID to find")
|
||||
dsn := flag.String("dsn", os.Getenv("DSN"), "connection data source name")
|
||||
flag.Parse()
|
||||
|
||||
if len(*dsn) == 0 {
|
||||
log.Fatal("missing dsn flag")
|
||||
}
|
||||
if *id == 0 {
|
||||
log.Fatal("missing person ID")
|
||||
}
|
||||
var err error
|
||||
|
||||
// Opening a driver typically will not attempt to connect to the database.
|
||||
pool, err = sql.Open("odbc", *dsn)
|
||||
if err != nil {
|
||||
// This will not be a connection error, but a DSN parse error or
|
||||
// another initialization error.
|
||||
log.Fatal("unable to use data source name", err)
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
pool.SetConnMaxLifetime(0)
|
||||
pool.SetMaxIdleConns(3)
|
||||
pool.SetMaxOpenConns(3)
|
||||
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
|
||||
appSignal := make(chan os.Signal, 3)
|
||||
signal.Notify(appSignal, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-appSignal:
|
||||
stop()
|
||||
}
|
||||
}()
|
||||
|
||||
Ping(ctx)
|
||||
|
||||
Query(ctx, *id)
|
||||
}
|
||||
|
||||
// Ping the database to verify DSN provided by the user is valid and the
|
||||
// server accessible. If the ping fails exit the program with an error.
|
||||
func Ping(ctx context.Context) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := pool.PingContext(ctx); err != nil {
|
||||
log.Fatalf("unable to connect to database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Query the database for the information requested and prints the results.
|
||||
// If the query fails exit the program with an error.
|
||||
func Query(ctx context.Context, id int64) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var name string
|
||||
err := pool.QueryRowContext(ctx, "select name from m.t").Scan(&name)
|
||||
if err != nil {
|
||||
log.Fatal("unable to execute search query", err)
|
||||
}
|
||||
log.Println("name=", name)
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const odbc = require('odbc');
|
||||
const path = require('path');
|
||||
|
||||
function usage() {
|
||||
var arg = path.basename(process.argv[1]);
|
||||
console.error(`usage:`);
|
||||
console.error(`${arg} --DSN <DSN> --UID <uid> --PWD <pwd> --Server <host:port>`);
|
||||
console.error(`${arg} -C <conn_str>`);
|
||||
console.error(` conn_str eg: 'DSN={TAOS_DSN};UID=root;PWD=taosdata;Server=host:port'`);
|
||||
}
|
||||
|
||||
var cfg = { };
|
||||
|
||||
if (process.argv.length==2) {
|
||||
usage();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var i;
|
||||
for (i=2; i<process.argv.length; ++i) {
|
||||
var arg = process.argv[i];
|
||||
if (arg=='-h') {
|
||||
usage();
|
||||
process.exit(0);
|
||||
}
|
||||
if (arg=="--DSN") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <dns> after --DSN but got nothing`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.dsn = arg;
|
||||
continue;
|
||||
}
|
||||
if (arg=="--UID") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <uid> after --UID but got nothing`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.uid = arg;
|
||||
continue;
|
||||
}
|
||||
if (arg=="--PWD") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <pwd> after --PWD but got nothing`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.pwd = arg;
|
||||
continue;
|
||||
}
|
||||
if (arg=="--Server") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <host:port> after --Server but got nothing`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.server = arg;
|
||||
continue;
|
||||
}
|
||||
if (arg=="-C") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <conn_str> after -C but got nothing`);
|
||||
console.error(` conn_str eg: 'DSN={TAOS_DSN};UID=root;PWD=taosdata;Server=host:port'`);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.conn_str = arg;
|
||||
continue;
|
||||
}
|
||||
console.error(`unknown argument: [${arg}]`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var connectionString = cfg.conn_str;
|
||||
|
||||
if (!cfg.conn_str) {
|
||||
connectionString = `DSN={${cfg.dsn}}; UID=${cfg.uid}; PWD=${cfg.pwd}; Server=${cfg.server}`;
|
||||
}
|
||||
|
||||
(async function () {
|
||||
const connStr = connectionString;
|
||||
try {
|
||||
console.log(`connecting [${connStr}]...`);
|
||||
const connection = await odbc.connect(connStr);
|
||||
await connection.query('create database if not exists m');
|
||||
await connection.query('use m');
|
||||
await connection.query('drop table if exists t');
|
||||
await connection.query('create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), name nchar(3))');
|
||||
await connection.query('insert into t values("2020-01-02 12:34:56.781", 1, 127, 32767, 32768, 32769, 123.456, 789.987, "hello", "我和你")');
|
||||
console.log('.........');
|
||||
result = await connection.query('select * from t');
|
||||
console.log(result[0]);
|
||||
|
||||
|
||||
statement = await connection.createStatement();
|
||||
await statement.prepare('INSERT INTO t (ts, v1) VALUES(?, ?)');
|
||||
await statement.bind(['2020-02-02 11:22:33.449', 89]);
|
||||
result = await statement.execute();
|
||||
console.log(result);
|
||||
|
||||
result = await connection.query('select * from t');
|
||||
console.log(result[0]);
|
||||
console.log(result[1]);
|
||||
} catch (e) {
|
||||
console.log('error:', e);
|
||||
}
|
||||
})();
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"odbc": "^2.3.6"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package.cpath = package.cpath .. ";/usr/local/lib/lib?.dylib"
|
||||
-- load driver
|
||||
local driver = require "luasql.odbc"
|
||||
-- create environment object
|
||||
env = assert (driver.odbc())
|
||||
-- connect to data source
|
||||
con = assert (env:connect("TAOS_DSN", "root", "taosdata"))
|
||||
-- reset our table
|
||||
-- res = con:execute"DROP TABLE people"
|
||||
-- res = assert (con:execute[[
|
||||
-- CREATE TABLE people(
|
||||
-- name varchar(50),
|
||||
-- email varchar(50)
|
||||
-- )
|
||||
-- ]])
|
||||
-- -- add a few elements
|
||||
-- list = {
|
||||
-- { name="Jose das Couves", email="jose@couves.com", },
|
||||
-- { name="Manoel Joaquim", email="manoel.joaquim@cafundo.com", },
|
||||
-- { name="Maria das Dores", email="maria@dores.com", },
|
||||
-- }
|
||||
-- for i, p in pairs (list) do
|
||||
-- res = assert (con:execute(string.format([[
|
||||
-- INSERT INTO people
|
||||
-- VALUES ('%s', '%s')]], p.name, p.email)
|
||||
-- ))
|
||||
-- end
|
||||
-- -- retrieve a cursor
|
||||
-- cur = assert (con:execute"SELECT name, email from people")
|
||||
-- -- print all rows, the rows will be indexed by field names
|
||||
-- row = cur:fetch ({}, "a")
|
||||
-- while row do
|
||||
-- print(string.format("Name: %s, E-mail: %s", row.name, row.email))
|
||||
-- -- reusing the table of results
|
||||
-- row = cur:fetch (row, "a")
|
||||
-- end
|
||||
cur = assert(con:execute"select * from m.t")
|
||||
row = cur:fetch({}, "a")
|
||||
while row do
|
||||
print(string.format("Name: %s", row.name))
|
||||
row = cur:fetch(row, "a")
|
||||
end
|
||||
|
||||
-- close everything
|
||||
cur:close() -- already closed because all the result set was consumed
|
||||
con:close()
|
||||
env:close()
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
import pyodbc
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser(description='Access TDengine via ODBC.')
|
||||
parser.add_argument('--DSN', help='DSN to use')
|
||||
parser.add_argument('--UID', help='UID to use')
|
||||
parser.add_argument('--PWD', help='PWD to use')
|
||||
parser.add_argument('--Server', help='Server to use')
|
||||
parser.add_argument('-C', metavar='CONNSTR', help='Connection string to use')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
a = 'DSN=%s'%args.DSN if args.DSN else None
|
||||
b = 'UID=%s'%args.UID if args.UID else None
|
||||
c = 'PWD=%s'%args.PWD if args.PWD else None
|
||||
d = 'Server=%s'%args.Server if args.Server else None
|
||||
conn_str = ';'.join(filter(None, [a,b,c,d])) if args.DSN else None
|
||||
conn_str = conn_str if conn_str else args.C
|
||||
if not conn_str:
|
||||
parser.print_help(file=sys.stderr)
|
||||
exit()
|
||||
|
||||
print('connecting: [%s]' % conn_str)
|
||||
cnxn = pyodbc.connect(conn_str, autocommit=True)
|
||||
cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("drop database if exists db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create database db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.mt (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), blob nchar(10))");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.mt values('2020-10-13 06:44:00.123', 1, 127, 32767, 2147483647, 32769, 123.456, 789.987, 'hello', 'helloworld')")
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00.234", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd129")
|
||||
##cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", 1502535178128, 9223372036854775807, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd123");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT INTO db.mt (ts,b,v1,v2,v4,v8,f4,f8,bin,blob) values (?,?,?,?,?,?,?,?,?,?)
|
||||
""",
|
||||
"2020-12-12 00:00:00",
|
||||
'true',
|
||||
'-127',
|
||||
'-32767',
|
||||
'-2147483647',
|
||||
'-9223372036854775807',
|
||||
'-1.23e10',
|
||||
'-11.23e6',
|
||||
'abcdefghij'.encode('utf-8'),
|
||||
"人啊大发测试及abc")
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("drop database if exists db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create database db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(4), blob nchar(4))");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hell', 'w我你z')")
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.v (ts timestamp, v1 tinyint, v2 smallint, name nchar(10), ts2 timestamp)")
|
||||
cursor.close()
|
||||
|
||||
params = [ ('2020-10-16 00:00:00.123', 19, '2111-01-02 01:02:03.123'),
|
||||
('2020-10-16 00:00:01', 41, '2111-01-02 01:02:03.423'),
|
||||
('2020-10-16 00:00:02', 57, '2111-01-02 01:02:03.153'),
|
||||
('2020-10-16 00:00:03.009', 26, '2111-01-02 01:02:03.623') ]
|
||||
cursor = cnxn.cursor()
|
||||
cursor.fast_executemany = True
|
||||
print('py:...................')
|
||||
cursor.executemany("insert into db.v (ts, v1, ts2) values (?, ?, ?)", params)
|
||||
print('py:...................')
|
||||
cursor.close()
|
||||
|
||||
## cursor = cnxn.cursor()
|
||||
## cursor.execute("SELECT * from db.v where v1 > ?", 4)
|
||||
## row = cursor.fetchone()
|
||||
## while row:
|
||||
## print(row)
|
||||
## row = cursor.fetchone()
|
||||
## cursor.close()
|
||||
##
|
||||
## cursor = cnxn.cursor()
|
||||
## cursor.execute("SELECT * from db.v where v1 > ?", '5')
|
||||
## row = cursor.fetchone()
|
||||
## while row:
|
||||
## print(row)
|
||||
## row = cursor.fetchone()
|
||||
## cursor.close()
|
||||
|
|
@ -0,0 +1 @@
|
|||
Cargo.lock
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "main"
|
||||
version = "0.1.0"
|
||||
authors = ["freemine <freemine@yeah.net>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
odbc = "0.17.0"
|
||||
env_logger = "0.8.2"
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
extern crate odbc;
|
||||
// Use this crate and set environmet variable RUST_LOG=odbc to see ODBC warnings
|
||||
extern crate env_logger;
|
||||
use odbc::*;
|
||||
use odbc_safe::AutocommitOn;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
|
||||
env_logger::init();
|
||||
|
||||
let conn_str = env::var("DSN").unwrap();
|
||||
match connect(&conn_str) {
|
||||
Ok(()) => println!("Success"),
|
||||
Err(diag) => println!("Error: {}", diag),
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(conn_str: &str) -> std::result::Result<(), DiagnosticRecord> {
|
||||
|
||||
let env = create_environment_v3().map_err(|e| e.unwrap())?;
|
||||
|
||||
let conn = env.connect_with_connection_string(conn_str)?;
|
||||
execute_statement(&conn)
|
||||
}
|
||||
|
||||
fn execute_statement<'env>(conn: &Connection<'env, AutocommitOn>) -> Result<()> {
|
||||
let stmt = Statement::with_parent(conn)?;
|
||||
|
||||
match stmt.exec_direct("select * from m.t")? {
|
||||
Data(mut stmt) => {
|
||||
let cols = stmt.num_result_cols()?;
|
||||
println!("cols: {}", cols);
|
||||
while let Some(mut cursor) = stmt.fetch()? {
|
||||
for i in 1..(cols + 1) {
|
||||
match cursor.get_data::<&str>(i as u16)? {
|
||||
Some(val) => print!(" {}", val),
|
||||
None => print!(" NULL"),
|
||||
}
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
NoData(_) => println!("Query executed, no data returned"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,12 +1,17 @@
|
|||
#P: positive sample
|
||||
#N: negative sample
|
||||
|
||||
P:drop database if exists m;
|
||||
P:create database m;
|
||||
P:use m;
|
||||
|
||||
P:drop table if exists t;
|
||||
P:create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(3), name nchar(1));
|
||||
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00', 0, 1);
|
||||
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.001', 1, 2);
|
||||
P:create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(3), name nchar(1), ts2 nchar(148));
|
||||
#P:insert into t (ts, blob, name) values('2020-10-10 00:00:00', 0, 1);
|
||||
#P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.001', 1, 2);
|
||||
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.002', '你', '好');
|
||||
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.003', 'abc', 'd');
|
||||
P:select * from t;
|
||||
P:create table v (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(3), name nchar(1), ts2 nchar(23));
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue