diff --git a/.travis.yml b/.travis.yml index d814a465e67468fc05c2d03b62c092a9c5130e22..0617d759768251fcf09aa2316f3556f20c1718d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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} diff --git a/Jenkinsfile b/Jenkinsfile index e7603b578bf2c7718e67fbd9fda1674db49c07c0..d96eeaa724b975f7b22ed4351d86e98648cf1473 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -32,25 +32,24 @@ def abort_previous(){ milestone(buildNumber) } def pre_test(){ - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - sh ''' - sudo rmtaos - ''' - } + + sh ''' + sudo rmtaos || echo "taosd has not installed" + ''' sh ''' cd ${WKC} git checkout develop git reset --hard HEAD~10 >/dev/null - git pull + git pull >/dev/null git fetch origin +refs/pull/${CHANGE_ID}/merge git checkout -qf FETCH_HEAD - git --no-pager diff --name-only FETCH_HEAD $(git merge-base FETCH_HEAD develop)|grep -v -E '.*md|//src//connector|Jenkinsfile' || exit 0 + git --no-pager diff --name-only FETCH_HEAD $(git merge-base FETCH_HEAD develop)|grep -v -E '.*md|//src//connector|Jenkinsfile' find ${WKC}/tests/pytest -name \'*\'.sql -exec rm -rf {} \\; cd ${WK} git reset --hard HEAD~10 - git checkout develop - git pull + git checkout develop + git pull >/dev/null cd ${WK} export TZ=Asia/Harbin date diff --git a/README-CN.md b/README-CN.md new file mode 100644 index 0000000000000000000000000000000000000000..9601cde3af526800a407c8bf9af8694e01e5641e --- /dev/null +++ b/README-CN.md @@ -0,0 +1,267 @@ +[![Build Status](https://travis-ci.org/taosdata/TDengine.svg?branch=master)](https://travis-ci.org/taosdata/TDengine) +[![Build status](https://ci.appveyor.com/api/projects/status/kf3pwh2or5afsgl9/branch/master?svg=true)](https://ci.appveyor.com/project/sangshuduo/tdengine-2n8ge/branch/master) +[![Coverage Status](https://coveralls.io/repos/github/taosdata/TDengine/badge.svg?branch=develop)](https://coveralls.io/github/taosdata/TDengine?branch=develop) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4201/badge)](https://bestpractices.coreinfrastructure.org/projects/4201) +[![tdengine](https://snapcraft.io//tdengine/badge.svg)](https://snapcraft.io/tdengine) + +[![TDengine](TDenginelogo.png)](https://www.taosdata.com) + +简体中文 | [English](./README.md) + +# TDengine 简介 + +TDengine是涛思数据专为物联网、车联网、工业互联网、IT运维等设计和优化的大数据平台。除核心的快10倍以上的时序数据库功能外,还提供缓存、数据订阅、流式计算等功能,最大程度减少研发和运维的复杂度,且核心代码,包括集群功能全部开源(开源协议,AGPL v3.0)。 + +- 10 倍以上性能提升。定义了创新的数据存储结构,单核每秒就能处理至少2万次请求,插入数百万个数据点,读出一千万以上数据点,比现有通用数据库快了十倍以上。 +- 硬件或云服务成本降至1/5。由于超强性能,计算资源不到通用大数据方案的1/5;通过列式存储和先进的压缩算法,存储空间不到通用数据库的1/10。 +- 全栈时序数据处理引擎。将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成Kafka/Redis/HBase/Spark等软件,大幅降低应用开发和维护成本。 +- 强大的分析功能。无论是十年前还是一秒钟前的数据,指定时间范围即可查询。数据可在时间轴上或多个设备上进行聚合。即席查询可通过Shell/Python/R/Matlab随时进行。 +- 与第三方工具无缝连接。不用一行代码,即可与Telegraf, Grafana, EMQ X, Prometheus, Matlab, R集成。后续还将支持MQTT, OPC, Hadoop,Spark等, BI工具也将无缝连接。 +- 零运维成本、零学习成本。安装、集群一秒搞定,无需分库分表,实时备份。标准SQL,支持JDBC,RESTful,支持Python/Java/C/C++/Go/Node.JS, 与MySQL相似,零学习成本。 + +# 文档 + +TDengine是一个高效的存储、查询、分析时序大数据的平台,专为物联网、车联网、工业互联网、运维监测等优化而设计。您可以像使用关系型数据库MySQL一样来使用它,但建议您在使用前仔细阅读一遍下面的文档,特别是 [数据模型](https://www.taosdata.com/cn/documentation/architecture) 与 [数据建模](https://www.taosdata.com/cn/documentation/model)。除本文档之外,欢迎 [下载产品白皮书](https://www.taosdata.com/downloads/TDengine%20White%20Paper.pdf)。 + +# 生成 + +TDengine目前2.0版服务器仅能在Linux系统上安装和运行,后续会支持Windows、macOS等系统。客户端可以在Windows或Linux上安装和运行。任何OS的应用也可以选择RESTful接口连接服务器taosd。CPU支持X64/ARM64/MIPS64/Alpha64,后续会支持ARM32、RISC-V等CPU架构。用户可根据需求选择通过[源码](https://www.taosdata.com/cn/getting-started/#通过源码安装)或者[安装包](https://www.taosdata.com/cn/getting-started/#通过安装包安装)来安装。本快速指南仅适用于通过源码安装。 + +## 安装工具 + +### Ubuntu 16.04 及以上版本 & Debian: + +```bash +sudo apt-get install -y gcc cmake build-essential git +``` + +### Ubuntu 14.04: + +```bash +sudo apt-get install -y gcc cmake3 build-essential git binutils-2.26 +export PATH=/usr/lib/binutils-2.26/bin:$PATH +``` + +编译或打包 JDBC 驱动源码,需安装 Java JDK 8 或以上版本和 Apache Maven 2.7 或以上版本。 + +安装 OpenJDK 8: + +```bash +sudo apt-get install -y openjdk-8-jdk +``` + +安装 Apache Maven: + +```bash +sudo apt-get install -y maven +``` + +### CentOS 7: + +```bash +sudo yum install -y gcc gcc-c++ make cmake git +``` + +安装 OpenJDK 8: + +```bash +sudo yum install -y java-1.8.0-openjdk +``` + +安装 Apache Maven: + +```bash +sudo yum install -y maven +``` + +### CentOS 8 & Fedora: + +```bash +sudo dnf install -y gcc gcc-c++ make cmake epel-release git +``` + +安装 OpenJDK 8: + +```bash +sudo dnf install -y java-1.8.0-openjdk +``` + +安装 Apache Maven: + +```bash +sudo dnf install -y maven +``` + +## 获取源码 + +首先,你需要从 GitHub 克隆源码: + +```bash +git clone https://github.com/taosdata/TDengine.git +cd TDengine +``` + +Go 连接器和 Grafana 插件在其他独立仓库,如果安装它们的话,需要在 TDengine 目录下通过此命令安装: + +```bash +git submodule update --init --recursive +``` + +## 生成 TDengine + +### Linux 系统 + +```bash +mkdir debug && cd debug +cmake .. && cmake --build . +``` + +在X86-64、X86、arm64 和 arm32 平台上,TDengine 生成脚本可以自动检测机器架构。也可以手动配置 CPUTYPE 参数来指定 CPU 类型,如 aarch64 或 aarch32 等。 + +aarch64: + +```bash +cmake .. -DCPUTYPE=aarch64 && cmake --build . +``` + +aarch32: + +```bash +cmake .. -DCPUTYPE=aarch32 && cmake --build . +``` + +### Windows 系统 + +如果你使用的是 Visual Studio 2013 版本: + +打开 cmd.exe,执行 vcvarsall.bat 时,为 64 位操作系统指定“x86_amd64”,为 32 位操作系统指定“x86”。 + +```bash +mkdir debug && cd debug +"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" < x86_amd64 | x86 > +cmake .. -G "NMake Makefiles" +nmake +``` + +如果你使用的是 Visual Studio 2019 或 2017 版本: + +打开cmd.exe,执行 vcvarsall.bat 时,为 64 位操作系统指定“x64”,为 32 位操作系统指定“x86”。 + +```bash +mkdir debug && cd debug +"c:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" < x64 | x86 > +cmake .. -G "NMake Makefiles" +nmake +``` + +你也可以从开始菜单中找到"Visual Studio < 2019 | 2017 >"菜单项,根据你的系统选择"x64 Native Tools Command Prompt for VS < 2019 | 2017 >"或"x86 Native Tools Command Prompt for VS < 2019 | 2017 >",打开命令行窗口,执行: + +```bash +mkdir debug && cd debug +cmake .. -G "NMake Makefiles" +nmake +``` + +### Mac OS X 系统 + +安装 Xcode 命令行工具和 cmake. 在 Catalina 和 Big Sur 操作系统上,需要安装 XCode 11.4+ 版本。 + +```bash +mkdir debug && cd debug +cmake .. && cmake --build . +``` + +# 安装 + +如果你不想安装,可以直接在shell中运行。生成完成后,安装 TDengine: +```bash +make install +``` + +用户可以在[文件目录结构](https://www.taosdata.com/cn/documentation/administrator#directories)中了解更多在操作系统中生成的目录或文件。 + +安装成功后,在终端中启动 TDengine 服务: + +```bash +taosd +``` + +用户可以使用 TDengine Shell 来连接 TDengine 服务,在终端中,输入: + +```bash +taos +``` + +如果 TDengine Shell 连接服务成功,将会打印出欢迎消息和版本信息。如果失败,则会打印出错误消息。 + +## 快速运行 + +TDengine 生成后,在终端执行以下命令: + +```bash +./build/bin/taosd -c test/cfg +``` + +在另一个终端,使用 TDengine shell 连接服务器: + +```bash +./build/bin/taos -c test/cfg +``` + +"-c test/cfg"指定系统配置文件所在目录。 + +# 体验 TDengine + +在TDengine终端中,用户可以通过SQL命令来创建/删除数据库、表等,并进行插入查询操作。 + +```bash +create database demo; +use demo; +create table t (ts timestamp, speed int); +insert into t values ('2019-07-15 00:00:00', 10); +insert into t values ('2019-07-15 01:00:00', 20); +select * from t; + ts | speed | +=================================== + 19-07-15 00:00:00.000| 10| + 19-07-15 01:00:00.000| 20| +Query OK, 2 row(s) in set (0.001700s) +``` + +# 应用开发 + +## 官方连接器 + +TDengine 提供了丰富的应用程序开发接口,其中包括C/C++、Java、Python、Go、Node.js、C# 、RESTful 等,便于用户快速开发应用: + +- Java + +- C/C++ + +- Python + +- Go + +- RESTful API + +- Node.js + +## 第三方连接器 + +TDengine 社区生态中也有一些非常友好的第三方连接器,可以通过以下链接访问它们的源码。 + +- [Rust Connector](https://github.com/taosdata/TDengine/tree/master/tests/examples/rust) +- [.Net Core Connector](https://github.com/maikebing/Maikebing.EntityFrameworkCore.Taos) +- [Lua Connector](https://github.com/taosdata/TDengine/tree/develop/tests/examples/lua) + +# 运行和添加测试例 + +TDengine 的测试框架和所有测试例全部开源。 + +点击[这里](tests/How-To-Run-Test-And-How-To-Add-New-Test-Case.md),了解如何运行测试例和添加新的测试例。 + +# 成为社区贡献者 +点击[这里](https://www.taosdata.com/cn/contributor/),了解如何成为 TDengine 的贡献者。 + +#加入技术交流群 +TDengine官方社群「物联网大数据群」对外开放,欢迎您加入讨论。搜索微信号 "tdengine",加小T为好友,即可入群。 diff --git a/README.md b/README.md index 489b6d0a4e12db06c9f2c1274e7a87f22cf76655..79c140c741c1164633e5a2953db9c2d86569e01f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ [![TDengine](TDenginelogo.png)](https://www.taosdata.com) +English | [简体中文](./README-CN.md) + # What is TDengine? TDengine is an open-sourced big data platform under [GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html), designed and optimized for the Internet of Things (IoT), Connected Cars, Industrial IoT, and IT Infrastructure and Application Monitoring. Besides the 10x faster time-series database, it provides caching, stream computing, message queuing and other functionalities to reduce the complexity and cost of development and operation. @@ -29,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 @@ -160,39 +162,42 @@ mkdir debug && cd debug cmake .. && cmake --build . ``` -# Quick Run - -# Quick Run -To quickly start a TDengine server after building, run the command below in terminal: -```bash -./build/bin/taosd -c test/cfg -``` -In another terminal, use the TDengine shell to connect the server: -```bash -./build/bin/taos -c test/cfg -``` -option "-c test/cfg" specifies the system configuration file directory. - # Installing + After building successfully, TDengine can be installed by: ```bash -make install +sudo make install ``` -Users can find more information about directories installed on the system in the [directory and files](https://www.taosdata.com/en/documentation/administrator/#Directory-and-Files) section. It should be noted that installing from source code does not configure service management for TDengine. + +Users can find more information about directories installed on the system in the [directory and files](https://www.taosdata.com/en/documentation/administrator/#Directory-and-Files) section. Since version 2.0, installing from source code will also configure service management for TDengine. Users can also choose to [install from packages](https://www.taosdata.com/en/getting-started/#Install-from-Package) for it. To start the service after installation, in a terminal, use: -```cmd -taosd +```bash +sudo systemctl start taosd ``` Then users can use the [TDengine shell](https://www.taosdata.com/en/getting-started/#TDengine-Shell) to connect the TDengine server. In a terminal, use: -```cmd +```bash taos ``` If TDengine shell connects the server successfully, welcome messages and version info are printed. Otherwise, an error message is shown. +## Quick Run + +If you don't want to run TDengine as a service, you can run it in current shell. For example, to quickly start a TDengine server after building, run the command below in terminal: +```bash +./build/bin/taosd -c test/cfg +``` + +In another terminal, use the TDengine shell to connect the server: +```bash +./build/bin/taos -c test/cfg +``` + +option "-c test/cfg" specifies the system configuration file directory. + # Try TDengine It is easy to run SQL commands from TDengine shell which is the same as other SQL databases. ```sql diff --git a/cmake/version.inc b/cmake/version.inc index 05fbef5b87a85168f43f3445acc3e5567c92fbd9..b1e09c9532ef8193b32c99180d35946cc6ef5c7e 100755 --- a/cmake/version.inc +++ b/cmake/version.inc @@ -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.18.0") ENDIF () IF (DEFINED VERCOMPATIBLE) diff --git a/documentation20/cn/03.architecture/docs.md b/documentation20/cn/03.architecture/docs.md index 26d8bc55c29d283511080f420663c5fa32bf43fe..87553fa8ad9760ecdb6d1667823d336189542331 100644 --- a/documentation20/cn/03.architecture/docs.md +++ b/documentation20/cn/03.architecture/docs.md @@ -145,7 +145,7 @@ TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。 在TDengine的设计里,**表用来代表一个具体的数据采集点,超级表用来代表一组相同类型的数据采集点集合**。当为某个具体数据采集点创建表时,用户使用超级表的定义做模板,同时指定该具体采集点(表)的标签值。与传统的关系型数据库相比,表(一个数据采集点)是带有静态标签的,而且这些标签可以事后增加、删除、修改。**一张超级表包含有多张表,这些表具有相同的时序数据schema,但带有不同的标签值**。 -当对多个具有相同数据类型的数据采集点进行聚合操作时,TDengine将先把满足标签过滤条件的表从超级表的中查找出来,然后再扫描这些表的时序数据,进行聚合操作,这样能将需要扫描的数据集大幅减少,从而大幅提高聚合计算的性能。 +当对多个具有相同数据类型的数据采集点进行聚合操作时,TDengine会先把满足标签过滤条件的表从超级表中找出来,然后再扫描这些表的时序数据,进行聚合操作,这样需要扫描的数据集会大幅减少,从而显著提高聚合计算的性能。 ## 集群与基本逻辑单元 diff --git a/documentation20/cn/08.connector/01.java/docs.md b/documentation20/cn/08.connector/01.java/docs.md index c39f6ffb4c98e97c21369b5767eff38f0fa9fe3c..3ed7343579ef7dc9c774367a8d9b41e2dabb7561 100644 --- a/documentation20/cn/08.connector/01.java/docs.md +++ b/documentation20/cn/08.connector/01.java/docs.md @@ -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 | diff --git a/documentation20/cn/11.administrator/docs.md b/documentation20/cn/11.administrator/docs.md index 52a0da6551a332b9b578d3830d1bb8135eb30692..027828d9033c138a727d5f0e9056118681484b10 100644 --- a/documentation20/cn/11.administrator/docs.md +++ b/documentation20/cn/11.administrator/docs.md @@ -14,6 +14,8 @@ Memory Size = maxVgroupsPerDb * (blocks * cache + 10MB) + numOfTables * (tagSize 示例:假设是 4 核机器,cache 是缺省大小 16M, blocks 是缺省值 6,假设有 10 万张表,标签总长度是 256 字节,则总的内存需求为:4 \* (16 \* 6 + 10) + 100000 \* (0.25 + 0.5) / 1000 = 499M。 +注意:从这个公式计算得到的内存容量,应理解为系统的“必要需求”,而不是“内存总数”。在实际运行的生产系统中,由于操作系统缓存、资源管理调度等方面的需要,内存规划应当在计算结果的基础上保留一定冗余,以维持系统状态和系统性能的稳定性。 + 实际运行的系统往往会根据数据特点的不同,将数据存放在不同的 DB 里。因此做规划时,也需要考虑。 如果内存充裕,可以加大 Blocks 的配置,这样更多数据将保存在内存里,提高查询速度。 diff --git a/documentation20/cn/12.taos-sql/docs.md b/documentation20/cn/12.taos-sql/docs.md index c89dbda7a02c243e62d6bd81b6c0d78b14f67d2c..b4fa2b160aa8ec04ba3a45db0fbbca17022cbd23 100644 --- a/documentation20/cn/12.taos-sql/docs.md +++ b/documentation20/cn/12.taos-sql/docs.md @@ -125,7 +125,7 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic ```mysql ALTER DATABASE db_name CACHELAST 0; ``` - CACHELAST 参数控制是否在内存中缓存数据子表的 last_row。缺省值为 0,取值范围 [0, 1]。其中 0 表示不启用、1 表示启用。(从 2.0.11 版本开始支持) + CACHELAST 参数控制是否在内存中缓存数据子表的 last_row。缺省值为 0,取值范围 [0, 1]。其中 0 表示不启用、1 表示启用。(从 2.0.11 版本开始支持,修改后需要重启服务器生效。) **Tips**: 以上所有参数修改后都可以用show databases来确认是否修改成功。 @@ -249,7 +249,7 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic 3) TAGS 列名不能为预留关键字; - 4) TAGS 最多允许128个,至少1个,总长度不超过16k个字符。 + 4) TAGS 最多允许 128 个,至少 1 个,总长度不超过 16 KB。 - **删除超级表** @@ -331,7 +331,8 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic ```mysql INSERT INTO tb_name VALUES (field1_value1, ...) (field1_value2, ...) ...; ``` - 向表tb_name中插入多条记录 + 向表tb_name中插入多条记录 + **注意**:在使用“插入多条记录”方式写入数据时,不能把第一列的时间戳取值都设为now,否则会导致语句中的多条记录使用相同的时间戳,于是就可能出现相互覆盖以致这些数据行无法全部被正确保存。 - **按指定的列插入多条记录** ```mysql diff --git a/documentation20/cn/13.faq/docs.md b/documentation20/cn/13.faq/docs.md index d3169d507ac69d6d40eec698edf76a69a929bda2..e2285b29e244641566661cf102c7b17616a6780a 100644 --- a/documentation20/cn/13.faq/docs.md +++ b/documentation20/cn/13.faq/docs.md @@ -16,13 +16,13 @@ ## 1. TDengine2.0之前的版本升级到2.0及以上的版本应该注意什么?☆☆☆ -2.0版本在之前版本的基础上,进行了完全的重构,配置文件和数据文件是不兼容的。在升级之前务必进行如下操作: +2.0版在之前版本的基础上,进行了完全的重构,配置文件和数据文件是不兼容的。在升级之前务必进行如下操作: -1. 删除配置文件,执行 sudo rm -rf /etc/taos/taos.cfg -2. 删除日志文件,执行 sudo rm -rf /var/log/taos/ -3. 确保数据已经不再需要的前提下,删除数据文件,执行 sudo rm -rf /var/lib/taos/ -4. 安装最新稳定版本的TDengine -5. 如果数据需要迁移数据或者数据文件损坏,请联系涛思数据官方技术支持团队,进行协助解决 +1. 删除配置文件,执行 `sudo rm -rf /etc/taos/taos.cfg` +2. 删除日志文件,执行 `sudo rm -rf /var/log/taos/` +3. 确保数据已经不再需要的前提下,删除数据文件,执行 `sudo rm -rf /var/lib/taos/` +4. 安装最新稳定版本的 TDengine +5. 如果需要迁移数据或者数据文件损坏,请联系涛思数据官方技术支持团队,进行协助解决 ## 2. Windows平台下JDBCDriver找不到动态链接库,怎么办? diff --git a/packaging/tools/remove.sh b/packaging/tools/remove.sh index 2f2660d44635c86df3b51d2b86e37b3399869a88..e63889aff1a6eceebfc9624576270200f6e79fa7 100755 --- a/packaging/tools/remove.sh +++ b/packaging/tools/remove.sh @@ -213,10 +213,10 @@ fi if echo $osinfo | grep -qwi "ubuntu" ; then # echo "this is ubuntu system" - ${csudo} rm -f /var/lib/dpkg/info/tdengine* || : + ${csudo} dpkg --force-all -P tdengine || : elif echo $osinfo | grep -qwi "debian" ; then # echo "this is debian system" - ${csudo} rm -f /var/lib/dpkg/info/tdengine* || : + ${csudo} dpkg --force-all -P tdengine || : elif echo $osinfo | grep -qwi "centos" ; then # echo "this is centos system" ${csudo} rpm -e --noscripts tdengine || : diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index c7ed621815c6f4ba5553210ccac18929fb550631..88628b4db6cfafa2a9815489313ced1345f2b600 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,6 +1,6 @@ name: tdengine base: core18 -version: '2.0.17.0' +version: '2.0.18.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.18.0 - usr/lib/libtaos.so.1 - usr/lib/libtaos.so diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0f2cc0a48f906b40d7be5185ae5f081c2ed4418..8cc5cee3b51675b2d42ad62c442b2b030e802cbb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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) diff --git a/src/balance/src/bnMain.c b/src/balance/src/bnMain.c index 236b22afafb9ed6ee13acfaf6b831520f2b4d2f6..3055f77e81021c4877d97147033fc34f4c525df9 100644 --- a/src/balance/src/bnMain.c +++ b/src/balance/src/bnMain.c @@ -425,7 +425,7 @@ static bool bnMonitorVgroups() { while (1) { pIter = mnodeGetNextVgroup(pIter, &pVgroup); - if (pVgroup == NULL) break; + if (pVgroup == NULL || pVgroup->pDb == NULL) break; int32_t dbReplica = pVgroup->pDb->cfg.replications; int32_t vgReplica = pVgroup->numOfVnodes; @@ -721,4 +721,4 @@ int32_t bnAlterDnode(struct SDnodeObj *pSrcDnode, int32_t vnodeId, int32_t dnode mnodeDecDnodeRef(pDestDnode); return code; -} \ No newline at end of file +} diff --git a/src/client/src/tscAsync.c b/src/client/src/tscAsync.c index 0eeab22a0c5d13a60b8d9ea8479581e79561aa18..e34675a61bf52dfff1863950966c26d8e207c225 100644 --- a/src/client/src/tscAsync.c +++ b/src/client/src/tscAsync.c @@ -282,7 +282,7 @@ void tscQueueAsyncError(void(*fp), void *param, int32_t code) { } static void tscAsyncResultCallback(SSchedMsg *pMsg) { - SSqlObj* pSql = pMsg->ahandle; + SSqlObj* pSql = (SSqlObj*)taosAcquireRef(tscObjRef, (int64_t)pMsg->ahandle); if (pSql == NULL || pSql->signature != pSql) { tscDebug("%p SqlObj is freed, not add into queue async res", pSql); return; @@ -293,23 +293,26 @@ static void tscAsyncResultCallback(SSchedMsg *pMsg) { SSqlRes *pRes = &pSql->res; if (pSql->fp == NULL || pSql->fetchFp == NULL){ + taosReleaseRef(tscObjRef, pSql->self); return; } pSql->fp = pSql->fetchFp; (*pSql->fp)(pSql->param, pSql, pRes->code); + taosReleaseRef(tscObjRef, pSql->self); } void tscAsyncResultOnError(SSqlObj* pSql) { SSchedMsg schedMsg = {0}; schedMsg.fp = tscAsyncResultCallback; - schedMsg.ahandle = pSql; + schedMsg.ahandle = (void *)pSql->self; schedMsg.thandle = (void *)1; schedMsg.msg = 0; taosScheduleTask(tscQhandle, &schedMsg); } + int tscSendMsgToServer(SSqlObj *pSql); void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) { diff --git a/src/client/src/tscPrepare.c b/src/client/src/tscPrepare.c index 0142c25f519bb6455475decc4b0591a7aba848f7..296dc3926c08b64b707695d9d87a878eadfe821c 100644 --- a/src/client/src/tscPrepare.c +++ b/src/client/src/tscPrepare.c @@ -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; @@ -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); + } + + STableDataBlocks* pBlock = NULL; - assert(pCmd->numOfParams == pBlock->numOfParams); - if (idx < 0 || idx >= pBlock->numOfParams) return -1; + 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; + SParamInfo* param = &pBlock->params[idx]; if (type) *type = param->type; if (bytes) *bytes = param->bytes; diff --git a/src/client/src/tscSQLParser.c b/src/client/src/tscSQLParser.c index 0b1c062c0e3f7b1d864fb1dcffd6140507d16524..cfe89813f0ccbb4eaae976330af5e155c744b47f 100644 --- a/src/client/src/tscSQLParser.c +++ b/src/client/src/tscSQLParser.c @@ -2356,13 +2356,13 @@ int32_t addExprAndResultField(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, int32_t col // todo REFACTOR // set the first column ts for top/bottom query - SColumnIndex index1 = {0, PRIMARYKEY_TIMESTAMP_COL_INDEX}; + SColumnIndex index1 = {index.tableIndex, PRIMARYKEY_TIMESTAMP_COL_INDEX}; pExpr = tscSqlExprAppend(pQueryInfo, TSDB_FUNC_TS, &index1, TSDB_DATA_TYPE_TIMESTAMP, TSDB_KEYSIZE, getNewResColId(pQueryInfo), TSDB_KEYSIZE, false); tstrncpy(pExpr->aliasName, aAggs[TSDB_FUNC_TS].name, sizeof(pExpr->aliasName)); const int32_t TS_COLUMN_INDEX = PRIMARYKEY_TIMESTAMP_COL_INDEX; - SColumnList ids = getColumnList(1, 0, TS_COLUMN_INDEX); + SColumnList ids = getColumnList(1, index.tableIndex, TS_COLUMN_INDEX); insertResultField(pQueryInfo, TS_COLUMN_INDEX, &ids, TSDB_KEYSIZE, TSDB_DATA_TYPE_TIMESTAMP, aAggs[TSDB_FUNC_TS].name, pExpr); @@ -2375,7 +2375,7 @@ int32_t addExprAndResultField(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, int32_t col memset(pExpr->aliasName, 0, tListLen(pExpr->aliasName)); getColumnName(pItem, pExpr->aliasName, sizeof(pExpr->aliasName) - 1); - SColumnList ids = getColumnList(1, 0, index.columnIndex); + SColumnList ids = getColumnList(1, index.tableIndex, index.columnIndex); if (finalResult) { insertResultField(pQueryInfo, colIndex, &ids, resultSize, resultType, pExpr->aliasName, pExpr); } else { diff --git a/src/client/src/tscServer.c b/src/client/src/tscServer.c index 652d34eee5938c6ce5204c7b3d52f17a7ee9db5d..e870f98e7fae98668ecafd906dff7b31aed12795 100644 --- a/src/client/src/tscServer.c +++ b/src/client/src/tscServer.c @@ -523,7 +523,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", pSql, pVgroupInfo->vgroups[vgIndex].vgId, vgIndex); + tscDebug("%p build fetch msg from vgId:%d, vgIndex:%d, qhandle:%" PRIX64, pSql, pVgroupInfo->vgroups[vgIndex].vgId, vgIndex, pSql->res.qhandle); } else { int32_t numOfVgroups = (int32_t)taosArrayGetSize(pTableMetaInfo->pVgroupTables); assert(vgIndex >= 0 && vgIndex < numOfVgroups); @@ -531,12 +531,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", pSql, pTableIdList->vgInfo.vgId, vgIndex); + tscDebug("%p build fetch msg from vgId:%d, vgIndex:%d, qhandle:%" PRIX64, pSql, pTableIdList->vgInfo.vgId, vgIndex, pSql->res.qhandle); } } else { STableMeta* pTableMeta = pTableMetaInfo->pTableMeta; pRetrieveMsg->header.vgId = htonl(pTableMeta->vgId); - tscDebug("%p build fetch msg from only one vgroup, vgId:%d", pSql, pTableMeta->vgId); + tscDebug("%p build fetch msg from only one vgroup, vgId:%d, qhandle:%" PRIX64, pSql, pTableMeta->vgId, pSql->res.qhandle); } pSql->cmd.payloadLen = sizeof(SRetrieveTableMsg); diff --git a/src/common/inc/tglobal.h b/src/common/inc/tglobal.h index df1a622101afa032001ed499495656186b40754f..3f96466cc00fc3250860e79bc3dd85e6fd0368d7 100644 --- a/src/common/inc/tglobal.h +++ b/src/common/inc/tglobal.h @@ -163,6 +163,7 @@ extern float tsTotalDataDirGB; extern float tsAvailLogDirGB; extern float tsAvailTmpDirectorySpace; extern float tsAvailDataDirGB; +extern float tsUsedDataDirGB; extern float tsMinimalLogDirGB; extern float tsReservedTmpDirectorySpace; extern float tsMinimalDataDirGB; diff --git a/src/common/src/tglobal.c b/src/common/src/tglobal.c index 9e405fdfe115e36cf0a1d64143a30ea9f19b1373..4a5df9361b7a9bb8d6fe852db273f53cab6bf879 100644 --- a/src/common/src/tglobal.c +++ b/src/common/src/tglobal.c @@ -210,6 +210,7 @@ float tsTotalTmpDirGB = 0; float tsTotalDataDirGB = 0; float tsAvailTmpDirectorySpace = 0; float tsAvailDataDirGB = 0; +float tsUsedDataDirGB = 0; float tsReservedTmpDirectorySpace = 1.0f; float tsMinimalDataDirGB = 1.0f; int32_t tsTotalMemoryMB = 0; diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java index 30081a63c3e8a77208b29ddbf1fc34079d4c2818..0cbb40dbb69f9e3b6b8fb2cc5bfd6ddac6ea4aef 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java @@ -136,7 +136,8 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet { throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE, "Column Index out of range, " + columnIndex + " > " + resultSet.get(pos).size()); columnIndex = getTrueColumnIndex(columnIndex); - return resultSet.get(pos).get(columnIndex).toString(); + Object value = resultSet.get(pos).get(columnIndex); + return value == null ? null : value.toString(); } @Override diff --git a/src/connector/odbc/.gitignore b/src/connector/odbc/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..bfef9b2efaaf8cf6071c42687a1c4c02688a9eee --- /dev/null +++ b/src/connector/odbc/.gitignore @@ -0,0 +1,3 @@ +!c/ +node_modules/ +package-lock.json diff --git a/src/connector/odbc/CMakeLists.txt b/src/connector/odbc/CMakeLists.txt index 0d8c07041aa741793b7a1b8db20c3a3b470cf193..5a93ac3f7e2934fd8383c5a18f22c24845793f1a 100644 --- a/src/connector/odbc/CMakeLists.txt +++ b/src/connector/odbc/CMakeLists.txt @@ -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 () + diff --git a/src/connector/odbc/README.cn.md b/src/connector/odbc/README.cn.md new file mode 100644 index 0000000000000000000000000000000000000000..bf114356a6329a6299599734e81b2cafc8769132 --- /dev/null +++ b/src/connector/odbc/README.cn.md @@ -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=:"}` +上述步骤出现失败的话,可以参看这些链接: +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 --pwd --sts ./src/connector/odbc/samples/create_data.stmts +--<或指定特殊的ODBC连接字符串 --> +./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=;PWD=;Server=:' --sts ./src/connector/odbc/samples/create_data.stmts +``` +## 在windows下检索数据 +``` +.\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=;PWD=;Server=:;enc_char=UTF-8" --sts .\src\connector\odbc\samples\query_data.stmts +``` +## 在MacOSX下检索数据 +``` +./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;UID=;PWD=;Server=:" --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**: 不要忘记替换: +**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=:" --sts src/connector/odbc/samples/create_data.stmts && +echo nodejs && +./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=:' && +echo python && +python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=:' && +echo rust && +pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=:' cargo run && popd && +echo go && +DSN='DSN=TAOS_DSN;Server=:' 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=:" --insert --batch_size 200 --batchs 10000 +``` + + diff --git a/src/connector/odbc/README.md b/src/connector/odbc/README.md index e026884a0766772ac315acd3d0cac6535fb77557..670c7b9d9572ae066c51b2b43f593bed161fae40 100644 --- a/src/connector/odbc/README.md +++ b/src/connector/odbc/README.md @@ -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 `` 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=:` +- 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 --pwd --sts ./src/connector/odbc/tests/create_data.stmts +./debug/build/bin/tcodbc --dsn TAOS_DSN --uid --pwd --sts ./src/connector/odbc/samples/create_data.stmts -- -./debug/build/bin/tcodbc --dcs 'Driver=TAOS;UID=;PWD=;Server=:;client_enc=UTF-8' ./src/connector/odbc/tests/create_data.stmts +./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=;PWD=;Server=:' --sts ./src/connector/odbc/samples/create_data.stmts ``` ## query data in windows ``` -.\debug\build\bin\tcodbc --dsn TAOS_DSN --uid --pwd --sts .\src\connector\odbc\tests\query_data.stmts --- -.\debug\build\bin\tcodbc --dcs "Driver=TAOS;UID=;PWD=;Server=:;client_enc=UTF-8" .\src\connector\odbc\tests\query_data.stmts +.\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=;PWD=;Server=:;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=;PWD=;Server=:" --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 : 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=:" --sts src/connector/odbc/samples/create_data.stmts && +echo nodejs && +./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=:' && +echo python && +python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=:' && +echo rust && +pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=:' cargo run && popd && +echo go && +DSN='DSN=TAOS_DSN;Server=:' 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=:" --insert --batch_size 200 --batchs 10000 ``` diff --git a/src/connector/odbc/examples/CMakeLists.txt b/src/connector/odbc/examples/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..71f00a04e3013bc35137629555bdaa037850f0b1 --- /dev/null +++ b/src/connector/odbc/examples/CMakeLists.txt @@ -0,0 +1,4 @@ +PROJECT(TDengine) + +ADD_SUBDIRECTORY(c) + diff --git a/src/connector/odbc/examples/c/CMakeLists.txt b/src/connector/odbc/examples/c/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7821f894d072e129be563805a10901e403e3cf2a --- /dev/null +++ b/src/connector/odbc/examples/c/CMakeLists.txt @@ -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 () + diff --git a/src/connector/odbc/examples/c/main.c b/src/connector/odbc/examples/c/main.c new file mode 100644 index 0000000000000000000000000000000000000000..e36c75688e3440a62b66fa5fc2f8b13b83f55237 --- /dev/null +++ b/src/connector/odbc/examples/c/main.c @@ -0,0 +1,1060 @@ +#include "../../src/todbc_log.h" + +#ifdef _MSC_VER +#include +#include +#include "os.h" +#endif +#include +#include +#include + +#include "taos.h" +#include "taoserror.h" + +#include +#include + +#define CHK_TEST(statement) \ +do { \ + D("testing: %s", #statement); \ + int r = (statement); \ + if (r) { \ + D("testing failed: %s", #statement); \ + return 1; \ + } \ +} while (0); + +typedef struct { + int batch_size; + int batchs; + int keep_stmt_among_batchs; + int use_odbc; + int use_taos_query; + int use_taos_stmt; +} insert_arg_t; + +typedef struct db_column_s db_column_t; +struct db_column_s { + SQLSMALLINT nameLength; + char name[4096]; // seems enough + SQLSMALLINT dataType; + SQLULEN columnSize; + SQLSMALLINT decimalDigits; + SQLSMALLINT nullable; +}; + +static db_column_t *columns = NULL; + +typedef struct data_s data_t; +struct data_s { + int64_t ts; + int8_t b; + int8_t v1; + int16_t v2; + int32_t v4; + int64_t v8; + float f4; + double f8; + char bin[40+1]; + char blob[40+1]; // why 80? ref: tests/examples/c/apitest.c +}; + +#define CHK_RESULT(r, ht, h, fmt, ...) \ +do { \ + if (r==0) break; \ + SQLSMALLINT i_0381 = 1; \ + while (1) { \ + SQLCHAR ss[10]; \ + SQLINTEGER ne = 0; \ + SQLCHAR es[4096]; \ + SQLSMALLINT n = 0; \ + ss[0] = '\0'; \ + es[0] = '\0'; \ + SQLRETURN ret = SQLGetDiagRec(ht, h, i_0381, ss, &ne, es, sizeof(es), &n); \ + if (ret) break; \ + D("[%s]%s: " fmt "", ss, es, ##__VA_ARGS__); \ + ++i_0381; \ + } \ +} while (0) + +static int open_connect(const char *dsn, const char *uid, const char *pwd, SQLHENV *pEnv, SQLHDBC *pConn) { + SQLRETURN r; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + r = SQLAllocEnv(&env); + if (r!=SQL_SUCCESS) return 1; + do { + r = SQLAllocConnect(env, &conn); + CHK_RESULT(r, SQL_HANDLE_ENV, env, ""); + if (r!=SQL_SUCCESS) break; + do { + r = SQLConnect(conn, (SQLCHAR*)dsn, (SQLSMALLINT)(dsn ? strlen(dsn) : 0), + (SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0), + (SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0)); + CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); + if (r==SQL_SUCCESS) { + *pEnv = env; + *pConn = conn; + return 0; + } + } while (0); + SQLFreeConnect(conn); + } while (0); + SQLFreeEnv(env); + + return 1; +} + +static int open_driver_connect(const char *connstr, SQLHENV *pEnv, SQLHDBC *pConn) { + SQLRETURN r; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + r = SQLAllocEnv(&env); + if (r!=SQL_SUCCESS) return 1; + do { + r = SQLAllocConnect(env, &conn); + CHK_RESULT(r, SQL_HANDLE_ENV, env, ""); + if (r!=SQL_SUCCESS) break; + do { + SQLCHAR buf[4096]; + SQLSMALLINT blen = 0; + SQLHDBC ConnectionHandle = conn; + SQLHWND WindowHandle = NULL; + SQLCHAR * InConnectionString = (SQLCHAR*)connstr; + SQLSMALLINT StringLength1 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0); + SQLCHAR * OutConnectionString = buf; + SQLSMALLINT BufferLength = sizeof(buf); + SQLSMALLINT * StringLength2Ptr = &blen; + SQLUSMALLINT DriverCompletion = SQL_DRIVER_NOPROMPT; + r = SQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString, + StringLength1, OutConnectionString, BufferLength, + StringLength2Ptr, DriverCompletion); + CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); + if (r==SQL_SUCCESS) { + *pEnv = env; + *pConn = conn; + return 0; + } + } while (0); + SQLFreeConnect(conn); + } while (0); + SQLFreeEnv(env); + + return 1; +} + +static SQLRETURN traverse_cols(SQLHSTMT stmt, SQLSMALLINT cols) { + SQLRETURN r = SQL_ERROR; + for (SQLSMALLINT i=0; i0) fprintf(stdout, "\n"); + return r; + } + } + if (soi==SQL_NULL_DATA) { + fprintf(stdout, "%snull", i==0?"":","); + } else { + fprintf(stdout, "%s\"%s\"", i==0?"":",", buf); + } + } + fprintf(stdout, "\n"); + } + } while (0); + return r; +} + +int test_statements(const char *dsn, const char *uid, const char *pwd, const char **statements) { + SQLRETURN r = SQL_SUCCESS; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + int n = open_connect(dsn, uid, pwd, &env, &conn); + if (n) return 1; + do { + SQLHSTMT stmt = {0}; + r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt); + if (r!=SQL_SUCCESS) break; + const char **p = statements; + while (*p) { + if (do_statement(stmt, *p)) { + r = SQL_ERROR; + break; + } + ++p; + } + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + } while (0); + SQLDisconnect(conn); + SQLFreeConnect(conn); + SQLFreeEnv(env); + return r ? 1 : 0; +} + +int test_driver_connect(const char *connstr) { + SQLRETURN r = SQL_SUCCESS; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + int n = open_driver_connect(connstr, &env, &conn); + if (n) return 1; + SQLDisconnect(conn); + SQLFreeConnect(conn); + SQLFreeEnv(env); + return r ? 1 : 0; +} + +int create_statement(SQLHENV env, SQLHDBC conn, SQLHSTMT *pStmt) { + SQLHSTMT stmt = {0}; + SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt); + CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); + if (r==SQL_SUCCESS) { + *pStmt = stmt; + return 0; + } + if (r==SQL_SUCCESS_WITH_INFO) { + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + } + return 1; +} + +int do_statements(SQLHSTMT stmt, const char **statements) { + const char **p = statements; + while (p && *p) { + CHK_TEST(do_statement(stmt, *p)); + ++p; + } + return 0; +} + +int tests_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt) { + const char *statements[] = { + "drop database if exists m", + "create database m", + "use m", + // "create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(1), name nchar(1))", + "create table t (ts timestamp, b bool)", + "insert into t values('2020-10-10 00:00:00', 0)", + "insert into t values('2020-10-10 00:00:00.001', 1)", + NULL + }; + CHK_TEST(do_statements(stmt, statements)); + return 0; +} + +int tests(SQLHENV env, SQLHDBC conn) { + SQLHSTMT stmt = {0}; + CHK_TEST(create_statement(env, conn, &stmt)); + int r = tests_stmt(env, conn, stmt); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return r ? 1 : 0; +} + +int test_env(void) { + SQLRETURN r; + SQLHENV env = {0}; + r = SQLAllocEnv(&env); + if (r!=SQL_SUCCESS) return 1; + SQLFreeEnv(env); + return 0; +} + +static int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls) { + FILE *f = fopen(sqls, "rb"); + if (!f) { + D("failed to open file [%s]", sqls); + return -1; + } + + int r = 0; + while (!feof(f)) { + char *line = NULL; + size_t len = 0; + + ssize_t n = 0; +#ifdef _MSC_VER + n = taosGetlineImp(&line, &len, f); +#else + n = getline(&line, &len, f); +#endif + if (n==-1) break; + + const char *p = NULL; + do { + if (line[0] == '#') break; + if (n>0 && line[n-1] == '\n') line[n-1]='\0'; + if (n>0 && line[n-1] == '\r') line[n-1]='\0'; + if (n>1 && line[n-2] == '\r') line[n-2]='\0'; + p = line; + while (isspace(*p)) ++p; + + if (*p==0) break; + + int positive = 1; + if (strncmp(p, "N:", 2)==0) { + // negative sample + positive = 0; + p += 2; + } else if (strncmp(p, "P:", 2)==0) { + // positive sample + p += 2; + } + + D("statement: [%s]", p); + r = do_statement(stmt, p); + + if (positive && r==0) break; + if (!positive && r) { r = 0; break; } + if (positive) return r; + D("expecting negative result, but got positive"); + return -1; + } while (0); + + free(line); + + if (r) break; + } + + fclose(f); + return r ? 1 : 0; +} + +static int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) { + SQLHSTMT stmt = {0}; + CHK_TEST(create_statement(env, conn, &stmt)); + int r = test_sqls_in_stmt(env, conn, stmt, sqls); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return r ? 1 : 0; +} + +static int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *connstr, const char *sqls) { + int r = 0; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + if (dsn) { + CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn)); + } else { + CHK_TEST(open_driver_connect(connstr, &env, &conn)); + } + + if (sqls) { + r = test_sqls_in_conn(env, conn, sqls); + } + SQLDisconnect(conn); + SQLFreeConnect(conn); + SQLFreeEnv(env); + return r ? 1 : 0; +} + +typedef struct record_s record_t; +struct record_s { + int dummy; + char ts[64]; + SQLLEN ts_len; + int32_t v1; + SQLLEN v1_len; + char ts2[64]; + SQLLEN ts2_len; +}; + +static int do_prepare_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt) { + SQLRETURN r = SQL_SUCCESS; + do { + const char *sql = "insert into m.v (ts, v1, ts2) values (?, ?, ?)"; + r = SQLPrepare(stmt, (SQLCHAR*)sql, SQL_NTS); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + + record_t records[] = { + {0, "2020-01-03 11:22:33.345", SQL_NTS, 1, sizeof(int32_t), "2020-01-02 11:22:33.455", SQL_NTS}, + {0, "2020-01-03 11:22:34.346", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS}, + {0, "2020-01-04 11:22:34.345", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS}, + {0, "2020-01-05 11:22:34.345", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS}, + }; + + record_t *base = (record_t*)0; + + r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(base->ts)-1, 0, base->ts, sizeof(base->ts), &(base->ts_len)); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &base->v1, 0, &(base->v1_len)); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(base->ts2)-1, 0, base->ts2, sizeof(base->ts2), &(base->ts2_len)); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + + SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)sizeof(*base), 0); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + + SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)(sizeof(records)/sizeof(records[0])), 0); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + + record_t *record = NULL; + + SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &record, 0); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + + record = records; + + r = SQLExecute(stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + } while (0); + + return r ? -1 : 0; +} + +static int do_prepare_in_conn(SQLHENV env, SQLHDBC conn) { + SQLHSTMT stmt = {0}; + CHK_TEST(create_statement(env, conn, &stmt)); + int r = do_prepare_in_stmt(env, conn, stmt); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return r ? 1 : 0; +} + +static int do_prepare(const char *dsn, const char *uid, const char *pwd, const char *connstr) { + int r = 0; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + if (dsn) { + CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn)); + } else { + CHK_TEST(open_driver_connect(connstr, &env, &conn)); + } + + r = do_prepare_in_conn(env, conn); + + SQLDisconnect(conn); + SQLFreeConnect(conn); + SQLFreeEnv(env); + return r ? 1 : 0; +} + +typedef struct { + int dummy; + int64_t ts; + SQLLEN ts_len; + int8_t v1; + SQLLEN v1_len; + int16_t v2; + SQLLEN v2_len; + int32_t v4; + SQLLEN v4_len; + int64_t v8; + SQLLEN v8_len; +} test_v_t; + +static int do_insert_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, int64_t *ts, insert_arg_t *arg) { + SQLRETURN r = SQL_SUCCESS; + int batch_size = arg->batch_size; + test_v_t *recs = NULL; + do { + const char *sql = "insert into test.v (ts, v1, v2, v4, v8) values (?, ?, ?, ?, ?)"; + r = SQLPrepare(stmt, (SQLCHAR*)sql, SQL_NTS); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + + test_v_t *base = NULL; + + r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &base->ts, 0, &base->ts_len); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, 0, 0, &base->v1, 0, &base->v1_len); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, 0, 0, &base->v2, 0, &base->v2_len); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &base->v4, 0, &base->v4_len); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &base->v8, 0, &base->v8_len); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + + SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)sizeof(*base), 0); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + + base = NULL; + + SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &base, 0); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + + size_t n_recs = (size_t)batch_size; + recs = (test_v_t*)calloc(n_recs, sizeof(*recs)); + OILE(recs, ""); + + SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)n_recs, 0); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + + base = recs; + + for (int batch=0; batchbatchs; ++batch) { + for (int i=0; idummy = 0; + rec->ts = *ts + i; + rec->ts_len = sizeof(rec->ts); + rec->v1 = (int8_t)rand(); + rec->v1_len = sizeof(rec->v1); + rec->v2 = (int16_t)rand(); + rec->v2_len = sizeof(rec->v2); + rec->v4 = rand(); + rec->v4_len = sizeof(rec->v4); + rec->v8 = rand(); + rec->v8_len = sizeof(rec->v8); + } + + *ts += (int64_t)n_recs; + + r = SQLExecute(stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + if (r) break; + } + } while (0); + + free(recs); + return r ? -1 : 0; +} + +static int do_insert_in_conn(SQLHENV env, SQLHDBC conn, insert_arg_t *arg) { + SQLHSTMT stmt = {0}; + int64_t ts = 1502535178128; + int r = 0; + CHK_TEST(create_statement(env, conn, &stmt)); + for (int i=0; i<1 && ibatchs; ++i) { + r = do_insert_in_stmt(env, conn, stmt, &ts, arg); + if (r) break; + if (!arg->keep_stmt_among_batchs) { + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + r = create_statement(env, conn, &stmt); + if (r) break; + } + } + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return r ? 1 : 0; +} + +static int do_insert_batch(const char *dsn, const char *uid, const char *pwd, const char *connstr, insert_arg_t *arg, const char *sqls[]) { + int r = 0; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + if (dsn) { + CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn)); + } else { + CHK_TEST(open_driver_connect(connstr, &env, &conn)); + } + + SQLHSTMT stmt = {0}; + CHK_TEST(create_statement(env, conn, &stmt)); + CHK_TEST(do_statements(stmt, sqls)); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + + OD("................"); + r = do_insert_in_conn(env, conn, arg); + OD("................"); + + SQLDisconnect(conn); + SQLFreeConnect(conn); + SQLFreeEnv(env); + return r ? 1 : 0; +} + +static int inited = 0; +static void init_once(void) { + if (inited) return; + + int r = taos_init(); + if (r) OILE(0, ""); + inited = 1; +} + +static int do_sqls(TAOS *taos, const char *sqls[]) { + for (int i=0; sqls[i]; ++i) { + OD("[%s]", sqls[i]); + TAOS_RES *res = taos_query(taos, sqls[i]); + if (!res) { + int e = terrno; + OD("taos_query [%s] failed: [%d]%s", sqls[i], e, tstrerror(e)); + return -1; + } + int e = taos_errno(res); + if (e) { + OD("taos_query [%s] failed: [%d]%s", sqls[i], e, tstrerror(e)); + } + taos_stop_query(res); + if (e) return -1; + } + return 0; +} + +static int do_taos_query(TAOS *taos, insert_arg_t *arg) { + char **sqls = (char**)calloc((size_t)arg->batchs, sizeof(*sqls)); + if (!sqls) { + OILE(0, "out of memory"); + } + + int64_t ts = 1502535178128; + for (int i=0; ibatchs; ++i) { + size_t bytes = 100 * (size_t)arg->batch_size; + sqls[i] = (char*)malloc(bytes); + OILE(sqls[i], ""); + char *p = sqls[i]; + size_t count = 0; + + while (1) { + int n = 0; + n = snprintf(p, bytes, "insert into test.v values"); + OILE(n>0, ""); + if (p) p += n; + OILE(bytes>n, ""); + if (bytes>=n) bytes -= (size_t)n; + else bytes = 0; + count += (size_t)n; + + for (int j=0; jbatch_size; ++j) { + int8_t v1 = (int8_t)rand(); if (v1==INT8_MIN) v1++; + int16_t v2 = (int16_t)rand(); if (v2==INT16_MIN) v2++; + int32_t v4 = (int32_t)rand(); if (v4==INT32_MIN) v4++; + int64_t v8 = (int64_t)rand(); if (v8==INT64_MIN) v8++; + n = snprintf(p, bytes, " (%" PRId64 ", %d, %d, %d, %" PRId64 ")", ts + i*arg->batch_size + j, (int)v1, (int)v2, v4, v8); + OILE(n>0, ""); + if (p) p += n; + OILE(bytes>n, ""); + if (bytes>=n) bytes -= (size_t)n; + else bytes = 0; + count += (size_t)n; + } + + if (p) break; + OILE(0, ""); + } + } + + OD(".............."); + for (int i=0; ibatchs; ++i) { + TAOS_RES *res = taos_query(taos, sqls[i]); + if (!res) { + int e = terrno; + OD("taos_query [%s] failed: [%d]%s", sqls[i], e, tstrerror(e)); + return -1; + } + int e = taos_errno(res); + if (e) { + OD("taos_query [%s] failed: [%d]%s", sqls[i], e, tstrerror(e)); + } + taos_stop_query(res); + if (e) return -1; + } + OD(".............."); + + for (int i=0; ibatchs; ++i) { + free(sqls[i]); + } + free(sqls); + + return 0; +} + +static int do_taos_stmt(TAOS *taos, insert_arg_t *arg) { + TAOS_STMT *stmt = taos_stmt_init(taos); + OILE(stmt, ""); + const char *sql = "insert into test.v values (?,?,?,?,?)"; + int r = 0; + do { + r = taos_stmt_prepare(stmt, sql, (unsigned long)strlen(sql)); + if (r) { + OD("taos_stmt_prepare [%s] failed: [%d]%s", sql, r, tstrerror(r)); + break; + } + int64_t ts = 1502535178128; + TAOS_BIND *bindings = (TAOS_BIND*)calloc(5, sizeof(*bindings)); + TAOS_BIND *b_ts = bindings + 0; + TAOS_BIND *b_v1 = bindings + 1; + TAOS_BIND *b_v2 = bindings + 2; + TAOS_BIND *b_v4 = bindings + 3; + TAOS_BIND *b_v8 = bindings + 4; + b_ts->buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + b_ts->buffer_length = sizeof(b_ts->u.ts); + b_ts->length = &b_ts->buffer_length; + b_ts->buffer = &b_ts->u.ts; + b_ts->is_null = NULL; + + b_v1->buffer_type = TSDB_DATA_TYPE_TINYINT; + b_v1->buffer_length = sizeof(b_v1->u.v1); + b_v1->length = &b_v1->buffer_length; + b_v1->buffer = &b_v1->u.v1; + b_v1->is_null = NULL; + + b_v2->buffer_type = TSDB_DATA_TYPE_SMALLINT; + b_v2->buffer_length = sizeof(b_v2->u.v2); + b_v2->length = &b_v2->buffer_length; + b_v2->buffer = &b_v2->u.v2; + b_v2->is_null = NULL; + + b_v4->buffer_type = TSDB_DATA_TYPE_INT; + b_v4->buffer_length = sizeof(b_v4->u.v4); + b_v4->length = &b_v4->buffer_length; + b_v4->buffer = &b_v4->u.v4; + b_v4->is_null = NULL; + + b_v8->buffer_type = TSDB_DATA_TYPE_BIGINT; + b_v8->buffer_length = sizeof(b_v8->u.v8); + b_v8->length = &b_v8->buffer_length; + b_v8->buffer = &b_v8->u.v8; + b_v8->is_null = NULL; + + OILE(bindings, ""); + OD("................"); + for (int i=0; ibatchs; ++i) { + for (int j=0; jbatch_size; ++j) { + b_ts->u.ts = ts + i*arg->batch_size + j; + b_v1->u.v1 = (int8_t)rand(); + b_v2->u.v2 = (int16_t)rand(); + b_v4->u.v4 = (int32_t)rand(); + b_v8->u.v8 = (int64_t)rand(); + r = taos_stmt_bind_param(stmt, bindings); + if (r) { + OD("taos_stmt_bind_param failed: [%d]%s", r, tstrerror(r)); + break; + } + r = taos_stmt_add_batch(stmt); + if (r) { + OD("taos_stmt_add_batch failed: [%d]%s", r, tstrerror(r)); + break; + } + } + + if (r) break; + + r = taos_stmt_execute(stmt); + if (r) { + OD("taos_stmt_execute failed: [%d]%s", r, tstrerror(r)); + break; + } + } + OD("................"); + + free(bindings); + + if (r) break; + } while (0); + taos_stmt_close(stmt); + return r ? -1 : 0; +} + +static int do_insert_batch_taos(const char *dsn, const char *uid, const char *pwd, const char *connstr, insert_arg_t *arg, const char *sqls[]) { + int r = 0; + + init_once(); + + int port = 0; + char *ip = NULL; + const char *p = strchr(connstr, ':'); + if (p) { + ip = strndup(connstr, (size_t)(p-connstr)); + ++p; + sscanf(p, "%d", &port); + } else { + ip = strdup(connstr); + port = 6030; + } + if (!ip) { + OD("bad ip/port:[%s]", connstr); + return -1; + } + + TAOS *taos = NULL; + do { + taos = taos_connect(ip, uid, pwd, NULL, (uint16_t)port); + if (!taos) { + int e = terrno; + OD("taos_connect [%s/%d] failed:[%d]%s", ip, port, e, tstrerror(e)); + break; + } + r = do_sqls(taos, sqls); + if (r) break; + if (arg->use_taos_query) { + r = do_taos_query(taos, arg); + } else if (arg->use_taos_stmt) { + r = do_taos_stmt(taos, arg); + } else { + OILE(0, ""); + } + } while (0); + + if (taos) taos_close(taos); + free(ip); + + return r ? 1 : 0; +} + +static int do_debug_col_name_max_len(const char *dsn, const char *uid, const char *pwd, const char *connstr) { + SQLRETURN r; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + r = SQLAllocEnv(&env); + if (r!=SQL_SUCCESS) { + D("SQLAllocEnv failed"); + return 1; + }; + do { + r = SQLAllocConnect(env, &conn); + CHK_RESULT(r, SQL_HANDLE_ENV, env, ""); + if (r!=SQL_SUCCESS) break; + do { + if (dsn) { + r = SQLConnect(conn, (SQLCHAR*)dsn, (SQLSMALLINT)(dsn ? strlen(dsn) : 0), + (SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0), + (SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0)); + } else { + SQLCHAR buf[4096]; + SQLSMALLINT blen = 0; + SQLHDBC ConnectionHandle = conn; + SQLHWND WindowHandle = NULL; + SQLCHAR * InConnectionString = (SQLCHAR*)connstr; + SQLSMALLINT StringLength1 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0); + SQLCHAR * OutConnectionString = buf; + SQLSMALLINT BufferLength = sizeof(buf); + SQLSMALLINT * StringLength2Ptr = &blen; + SQLUSMALLINT DriverCompletion = SQL_DRIVER_NOPROMPT; + r = SQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString, + StringLength1, OutConnectionString, BufferLength, + StringLength2Ptr, DriverCompletion); + } + CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); + if (r!=SQL_SUCCESS) break; + D("connected"); + if (1) { + SQLSMALLINT maxColumnNameLength = 0; + SQLSMALLINT len = 0; + r = SQLGetInfo(conn, SQL_MAX_COLUMN_NAME_LEN, &maxColumnNameLength, sizeof(SQLSMALLINT), &len); + CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); + if (r!=SQL_SUCCESS) break; + D("maxColumnNameLength: %d", maxColumnNameLength); + } + } while (0); + SQLFreeConnect(conn); + conn = NULL; + } while (0); + SQLFreeEnv(env); + env = NULL; + + return (r==SQL_SUCCESS) ? 0 : 1; +} + +void usage(const char *arg0) { + fprintf(stdout, "%s usage:\n", arg0); + fprintf(stdout, "%s [--dsn ] [--uid ] [--pwd ] [-C ] [--sts ]\n", arg0); + fprintf(stdout, " --dsn : DSN\n"); + fprintf(stdout, " --uid : UID\n"); + fprintf(stdout, " --pwd : PWD\n"); + fprintf(stdout, " -C : driver connection string\n"); + fprintf(stdout, " --sts : file where statements store\n"); +} + +int main(int argc, char *argv[]) { + srand((unsigned)time(0)); + const char *conn_str = NULL; + const char *dsn = NULL; + const char *uid = NULL; + const char *pwd = NULL; + const char *sts = NULL; // statements file + int debug_col_name_max_len = 0; + int prepare = 0; + int insert = 0; + insert_arg_t insert_arg = { + .batch_size = 100, + .batchs = 100, + .keep_stmt_among_batchs = 0 + }; + for (size_t i=1; i=argc) { + D(" expected but got nothing"); + return 1; + } + sscanf(argv[i], "%d", &insert_arg.batch_size); + if (insert_arg.batch_size<=0) { + D(" invalid"); + return 1; + } + continue; + } + if (strcmp(arg, "--batchs")==0) { + ++i; + if (i>=argc) { + D(" expected but got nothing"); + return 1; + } + sscanf(argv[i], "%d", &insert_arg.batchs); + if (insert_arg.batchs<=0) { + D(" invalid"); + return 1; + } + continue; + } + if (strcmp(arg, "--keep_stmt_among_batchs")==0) { + insert_arg.keep_stmt_among_batchs = 1; + continue; + } + if (strcmp(arg, "--dsn")==0) { + ++i; + if (i>=argc) { + D(" expected but got nothing"); + return 1; + } + if (conn_str) { + D("-C has already been specified"); + return 1; + } + dsn = argv[i]; + continue; + } + if (strcmp(arg, "--uid")==0) { + ++i; + if (i>=argc) { + D(" expected but got nothing"); + return 1; + } + uid = argv[i]; + continue; + } + if (strcmp(arg, "--pwd")==0) { + ++i; + if (i>=argc) { + D(" expected but got nothing"); + return 1; + } + pwd = argv[i]; + continue; + } + if (strcmp(arg, "-C")==0) { + ++i; + if (i>=argc) { + D(" expected but got nothing"); + return 1; + } + if (dsn || uid || pwd) { + D("either of --dsn/--uid/--pwd has already been specified"); + return 1; + } + conn_str = argv[i]; + continue; + } + if (strcmp(arg, "--sts")==0) { + ++i; + if (i>=argc) { + D(" expected but got nothing"); + return 1; + } + sts = argv[i]; + continue; + } + if (strcmp(arg, "-p")==0) { + prepare = 1; + continue; + } + } + if (debug_col_name_max_len) { + int r = do_debug_col_name_max_len(dsn, uid, pwd, conn_str); + if (r) return 1; + } + if (insert) { + const char *sqls[] = { + "drop database if exists test", + "create database test", + "create table test.v (ts timestamp, v1 tinyint, v2 smallint, v4 int, v8 bigint)", + NULL + }; + int r = 0; + if (insert_arg.use_odbc) { + r = do_insert_batch(dsn, uid, pwd, conn_str, &insert_arg, sqls); + } else { + r = do_insert_batch_taos(dsn, uid, pwd, conn_str, &insert_arg, sqls); + } + if (r) return 1; + } + if (sts) { + int r = test_sqls(dsn, uid, pwd, conn_str, sts); + if (r) return 1; + } + if (prepare) { + int r = do_prepare(dsn, uid, pwd, conn_str); + if (r) return 1; + } + D("Done!"); + return 0; +} + diff --git a/src/connector/odbc/examples/c/main.cpp b/src/connector/odbc/examples/c/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b719534f986fd0076de7dc3351ce273a2847f919 --- /dev/null +++ b/src/connector/odbc/examples/c/main.cpp @@ -0,0 +1,654 @@ +/******************************************************************************* +/* ODBCSQL: a sample program that implements an ODBC command line interpreter. +/* +/* USAGE: ODBCSQL DSN= or +/* ODBCSQL FILEDSN= 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 +#include +#include +#include +#include +#include +#include +#include + + +/*******************************************/ +/* 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 (| ) +#define DISPLAY_FORMAT L"%c %*.*s " +#define DISPLAY_FORMAT_C L"%c %-*.*s " +#define NULL_SIZE 6 // +#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""); + } + } + 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); + } + } +} diff --git a/src/connector/odbc/examples/go/odbc.go b/src/connector/odbc/examples/go/odbc.go new file mode 100644 index 0000000000000000000000000000000000000000..4d9c760c4e87a4a899051edc74692ecca8a19d15 --- /dev/null +++ b/src/connector/odbc/examples/go/odbc.go @@ -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) +} + diff --git a/src/connector/odbc/examples/js/.gitignore b/src/connector/odbc/examples/js/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/connector/odbc/examples/js/odbc.js b/src/connector/odbc/examples/js/odbc.js new file mode 100755 index 0000000000000000000000000000000000000000..78a4365c11ddec66666bc54755c9429c34f53af3 --- /dev/null +++ b/src/connector/odbc/examples/js/odbc.js @@ -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 --UID --PWD --Server `); + console.error(`${arg} -C `); + 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) { + console.error(`expecting 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 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 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 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 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); + } +})(); + diff --git a/src/connector/odbc/examples/js/package.json b/src/connector/odbc/examples/js/package.json new file mode 100644 index 0000000000000000000000000000000000000000..28a04dc32f0d1fc9f1c06de174241b10606f8639 --- /dev/null +++ b/src/connector/odbc/examples/js/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "odbc": "^2.3.6" + } +} diff --git a/src/connector/odbc/examples/lua/odbc.lua b/src/connector/odbc/examples/lua/odbc.lua new file mode 100644 index 0000000000000000000000000000000000000000..e3388358cdab53d7513650192c6a272fadd0e185 --- /dev/null +++ b/src/connector/odbc/examples/lua/odbc.lua @@ -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() + diff --git a/src/connector/odbc/examples/py/odbc.py b/src/connector/odbc/examples/py/odbc.py new file mode 100644 index 0000000000000000000000000000000000000000..e6a4bc73aef3e19bc56e817325acd62d21156d67 --- /dev/null +++ b/src/connector/odbc/examples/py/odbc.py @@ -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() + diff --git a/src/connector/odbc/examples/rust/.gitignore b/src/connector/odbc/examples/rust/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..03314f77b5aa454994bf08c40943f4012964eb0a --- /dev/null +++ b/src/connector/odbc/examples/rust/.gitignore @@ -0,0 +1 @@ +Cargo.lock diff --git a/src/connector/odbc/examples/rust/main/Cargo.toml b/src/connector/odbc/examples/rust/main/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..171dcf52b2bfcf3bb56fa49c4e228871311e90aa --- /dev/null +++ b/src/connector/odbc/examples/rust/main/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "main" +version = "0.1.0" +authors = ["freemine "] +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" + diff --git a/src/connector/odbc/examples/rust/main/src/main.rs b/src/connector/odbc/examples/rust/main/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..112f0d309b51a0a75a7e580f764c697a2215b9da --- /dev/null +++ b/src/connector/odbc/examples/rust/main/src/main.rs @@ -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(()) +} + diff --git a/src/connector/odbc/tests/create_data.stmts b/src/connector/odbc/samples/create_data.stmts similarity index 51% rename from src/connector/odbc/tests/create_data.stmts rename to src/connector/odbc/samples/create_data.stmts index 549cb583d8322906b4bdaffafde8eb510cb91c90..3d41c0db6447ed8a45390c2a50d73697992dbf52 100644 --- a/src/connector/odbc/tests/create_data.stmts +++ b/src/connector/odbc/samples/create_data.stmts @@ -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)); + diff --git a/src/connector/odbc/tests/query_data.stmts b/src/connector/odbc/samples/query_data.stmts similarity index 100% rename from src/connector/odbc/tests/query_data.stmts rename to src/connector/odbc/samples/query_data.stmts diff --git a/src/connector/odbc/tests/select.stmts b/src/connector/odbc/samples/select.stmts similarity index 100% rename from src/connector/odbc/tests/select.stmts rename to src/connector/odbc/samples/select.stmts diff --git a/src/connector/odbc/tests/simples.stmts b/src/connector/odbc/samples/simples.stmts similarity index 100% rename from src/connector/odbc/tests/simples.stmts rename to src/connector/odbc/samples/simples.stmts diff --git a/src/connector/odbc/src/CMakeLists.txt b/src/connector/odbc/src/CMakeLists.txt index 2699e1bc90e162c80d27d690e1f7163747616526..f0e50415e2e4f14e1c247b834e1e52a2c2fd2868 100644 --- a/src/connector/odbc/src/CMakeLists.txt +++ b/src/connector/odbc/src/CMakeLists.txt @@ -1,6 +1,8 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(TDengine) +add_subdirectory(base) + IF (TD_LINUX_64) FLEX_TARGET(todbcFlexScanner todbc_scanner.l @@ -15,12 +17,35 @@ IF (TD_LINUX_64) ADD_LIBRARY(todbc SHARED ${SRC} ${todbc_flex_scanner_src}) SET_TARGET_PROPERTIES(todbc PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(todbc PROPERTIES VERSION ${TD_VER_NUMBER} SOVERSION 1) - TARGET_LINK_LIBRARIES(todbc taos odbcinst) + TARGET_LINK_LIBRARIES(todbc todbc_base taos odbcinst) target_include_directories(todbc PUBLIC .) install(CODE "execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/install.sh ${CMAKE_BINARY_DIR})") ENDIF () +IF (TD_DARWIN) + FLEX_TARGET(todbcFlexScanner + todbc_scanner.l + ${CMAKE_CURRENT_BINARY_DIR}/todbc_scanner.c + ) + set(todbc_flex_scanner_src + ${FLEX_todbcFlexScanner_OUTPUTS} + ) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/todbc_scanner.c PROPERTIES COMPILE_OPTIONS "-Wno-conversion") + AUX_SOURCE_DIRECTORY(. SRC) + + # generate dynamic library (*.dylib) + ADD_LIBRARY(todbc SHARED ${SRC} ${todbc_flex_scanner_src}) + SET_TARGET_PROPERTIES(todbc PROPERTIES CLEAN_DIRECT_OUTPUT 1) + SET_TARGET_PROPERTIES(todbc PROPERTIES VERSION ${TD_VER_NUMBER} SOVERSION 1) + TARGET_LINK_LIBRARIES(todbc todbc_base taos odbcinst) + target_include_directories(todbc PUBLIC .) + target_include_directories(todbc PRIVATE /usr/local/include) + target_link_directories(todbc PUBLIC /usr/local/lib) + + install(CODE "execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/install.sh ${CMAKE_BINARY_DIR})") +ENDIF () + IF (TD_WINDOWS_64) FLEX_TARGET(todbcFlexScanner todbc_scanner.l @@ -37,7 +62,7 @@ IF (TD_WINDOWS_64) ${todbc_flex_scanner_src} ${CMAKE_CURRENT_BINARY_DIR}/todbc.rc todbc.def) - TARGET_LINK_LIBRARIES(todbc taos_static odbccp32 legacy_stdio_definitions) + TARGET_LINK_LIBRARIES(todbc todbc_base taos_static odbccp32 legacy_stdio_definitions) target_include_directories(todbc PUBLIC .) target_compile_definitions(todbc PRIVATE "todbc_EXPORT") @@ -52,3 +77,4 @@ IF (TD_WINDOWS_64) INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.exp DESTINATION driver) INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.dll DESTINATION driver) ENDIF () + diff --git a/src/connector/odbc/src/base.c b/src/connector/odbc/src/base.c new file mode 100644 index 0000000000000000000000000000000000000000..54b55b48edcc63c444e05e2b3a234ac98025506e --- /dev/null +++ b/src/connector/odbc/src/base.c @@ -0,0 +1,2713 @@ +#include "base.h" + +#include "base/env.h" +#include "base/null_conn.h" +#include "base/tsdb_impl.h" + +#include "todbc_flex.h" +#include "todbc_tls.h" +#include "todbc_util.h" + +#ifdef _MSC_VER +#include +#endif // _MSC_VER + +#define PROFILING 0 +#define LOGGING 0 + +#define PROFILE(r_0911, statement) \ +do { \ + if (!PROFILING) { \ + if (LOGGING) D(""); \ + statement; \ + if (LOGGING) D("r=%zx", (size_t)r_0911); \ + break; \ + } \ + if (LOGGING) D(""); \ + struct timeval tv0, tv1; \ + gettimeofday(&tv0, NULL); \ + statement; \ + gettimeofday(&tv1, NULL); \ + double delta = difftime(tv1.tv_sec, tv0.tv_sec); \ + delta *= 1000000; \ + delta += (double)(tv1.tv_usec-tv0.tv_usec); \ + delta /= 1000000; \ + D("%s: elapsed: [%.6f]s", #statement, delta); \ + if (LOGGING) D("r=%zx", (size_t)r_0911); \ +} while (0) + +#define P(fmt,...) do { \ + if (LOGGING) { \ + D(fmt, ##__VA_ARGS__); \ + } \ +} while (0) + + + +static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle, SQLHDBC *ConnectionHandle); + +SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, SQLHDBC *ConnectionHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_ENV, EnvironmentHandle); + PROFILE(r, r = doSQLAllocConnect(EnvironmentHandle, ConnectionHandle)); + todbc_tls_buf_reclaim(); + return r; +} + + +static SQLRETURN doSQLAllocEnv(SQLHENV *EnvironmentHandle); + +SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) +{ + SQLRETURN r; + PROFILE(r, r = doSQLAllocEnv(EnvironmentHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle); + +SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle) +{ + SQLRETURN r; + // HandleType is NOT the type of InputHandle + PROFILE(r, r = doSQLAllocHandle(HandleType, InputHandle, OutputHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLAllocStmt(SQLHDBC ConnectionHandle, SQLHSTMT *StatementHandle); + +SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, SQLHSTMT *StatementHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = doSQLAllocStmt(ConnectionHandle, StatementHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLBindCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, + SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr); + +SQLRETURN SQL_API SQLBindCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, + SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLBindCol(StatementHandle, ColumnNumber, TargetType, + TargetValue, BufferLength, StrLen_or_IndPtr)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLBindParam(SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, SQLULEN LengthPrecision, + SQLSMALLINT ParameterScale, SQLPOINTER ParameterValue, + SQLLEN *StrLen_or_IndPtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLCancel(SQLHSTMT StatementHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLCancelHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle) +{ + SQLRETURN r; + errs_clear(HandleType, InputHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLCloseCursor(SQLHSTMT StatementHandle); + +SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT StatementHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLCloseCursor(StatementHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLColAttribute (SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength, SQLLEN *NumericAttribute) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLColumns(SQLHSTMT StatementHandle, + SQLCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLCHAR *TableName, SQLSMALLINT NameLength3, + SQLCHAR *ColumnName, SQLSMALLINT NameLength4) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLCompleteAsync(SQLSMALLINT HandleType, SQLHANDLE Handle, RETCODE* AsyncRetCodePtr) +{ + SQLRETURN r; + errs_clear(HandleType, Handle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, SQLSMALLINT NameLength1, + SQLCHAR *UserName, SQLSMALLINT NameLength2, + SQLCHAR *Authentication, SQLSMALLINT NameLength3); + +SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, SQLSMALLINT NameLength1, + SQLCHAR *UserName, SQLSMALLINT NameLength2, + SQLCHAR *Authentication, SQLSMALLINT NameLength3) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = doSQLConnect(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2, Authentication, NameLength3)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLCopyDesc(SQLHDESC SourceDescHandle, SQLHDESC TargetDescHandle) +{ + SQLRETURN r; + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLDataSources(SQLHENV EnvironmentHandle, + SQLUSMALLINT Direction, SQLCHAR *ServerName, SQLSMALLINT BufferLength1, SQLSMALLINT *NameLength1Ptr, + SQLCHAR *Description, SQLSMALLINT BufferLength2, + SQLSMALLINT *NameLength2Ptr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_ENV, EnvironmentHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, SQLSMALLINT BufferLength, + SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable); + +SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, SQLSMALLINT BufferLength, + SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLDescribeCol(StatementHandle, ColumnNumber, ColumnName, BufferLength, + NameLength, DataType, ColumnSize, DecimalDigits, Nullable)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLDisconnect(SQLHDBC ConnectionHandle); + +SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = doSQLDisconnect(ConnectionHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) +{ + SQLRETURN r; + errs_clear(HandleType, Handle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLError(SQLHENV EnvironmentHandle, + SQLHDBC ConnectionHandle, SQLHSTMT StatementHandle, + SQLCHAR *Sqlstate, SQLINTEGER *NativeError, + SQLCHAR *MessageText, SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength); + +SQLRETURN SQL_API not_support_SQLError(SQLHENV EnvironmentHandle, + SQLHDBC ConnectionHandle, SQLHSTMT StatementHandle, + SQLCHAR *Sqlstate, SQLINTEGER *NativeError, + SQLCHAR *MessageText, SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength) +{ + SQLRETURN r; + PROFILE(r, r = doSQLError(EnvironmentHandle, ConnectionHandle, StatementHandle, + Sqlstate, NativeError, MessageText, BufferLength, TextLength)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR* StatementText, SQLINTEGER TextLength); + +SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR* StatementText, SQLINTEGER TextLength) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLExecDirect(StatementHandle, StatementText, TextLength)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle); + +SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLExecute(StatementHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLFetch(SQLHSTMT StatementHandle); + +SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLFetch(StatementHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLFetchScroll(SQLHSTMT StatementHandle, SQLSMALLINT FetchOrientation, SQLLEN FetchOffset) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLFreeConnect(SQLHDBC ConnectionHandle); + +SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = doSQLFreeConnect(ConnectionHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLFreeEnv(SQLHENV EnvironmentHandle); + +SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_ENV, EnvironmentHandle); + PROFILE(r, r = doSQLFreeEnv(EnvironmentHandle)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle); + +SQLRETURN SQL_API SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) +{ + SQLRETURN r; + errs_clear(HandleType, Handle); + PROFILE(r, r = doSQLFreeHandle(HandleType, Handle)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, SQLUSMALLINT Option); + +SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, SQLUSMALLINT Option) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLFreeStmt(StatementHandle, Option)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLGetConnectAttr(SQLHDBC ConnectionHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLGetConnectOption(SQLHDBC ConnectionHandle, SQLUSMALLINT Option, SQLPOINTER Value) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLGetCursorName(SQLHSTMT StatementHandle, SQLCHAR *CursorName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLengthPtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, SQLLEN BufferLength, + SQLLEN *StrLen_or_IndPtr); + +SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, SQLLEN BufferLength, + SQLLEN *StrLen_or_IndPtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLGetData(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_IndPtr)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLGetDescField(SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, + SQLPOINTER Value, SQLINTEGER BufferLength, + SQLINTEGER *StringLength) +{ + SQLRETURN r; + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLGetDescRec(SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, SQLCHAR *Name, + SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, + SQLSMALLINT *TypePtr, SQLSMALLINT *SubTypePtr, + SQLLEN *LengthPtr, SQLSMALLINT *PrecisionPtr, + SQLSMALLINT *ScalePtr, SQLSMALLINT *NullablePtr) +{ + SQLRETURN r; + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, + SQLPOINTER DiagInfo, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength); + +SQLRETURN SQL_API SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, + SQLPOINTER DiagInfo, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength) +{ + SQLRETURN r; + PROFILE(r, r = doSQLGetDiagField(HandleType, Handle, RecNumber, DiagIdentifier, + DiagInfo, BufferLength, StringLength)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, SQLINTEGER *NativeError, SQLCHAR* MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength); + +SQLRETURN SQL_API SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, SQLINTEGER *NativeError, SQLCHAR* MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) +{ + SQLRETURN r; + PROFILE(r, r = doSQLGetDiagRec(HandleType, Handle, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLGetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER BufferLength, SQLINTEGER *StringLength); + +SQLRETURN SQL_API SQLGetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER BufferLength, SQLINTEGER *StringLength) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_ENV, EnvironmentHandle); + PROFILE(r, r = doSQLGetEnvAttr(EnvironmentHandle, Attribute, Value, BufferLength, StringLength)); + todbc_tls_buf_reclaim(); + return r; +} + +// SQLRETURN SQL_API SQLGetFunctions(SQLHDBC ConnectionHandle, SQLUSMALLINT FunctionId, SQLUSMALLINT *Supported) +// { +// SQLRETURN r; +// errs_clear(SQL_HANDLE_DBC, ConnectionHandle); +// PROFILE(r, r = SQL_ERROR); +// todbc_tls_buf_reclaim(); +// return r; +// } + +static SQLRETURN doSQLGetInfo(SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr); + +SQLRETURN SQL_API SQLGetInfo(SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = doSQLGetInfo(ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLGetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER BufferLength, SQLINTEGER *StringLength); + +SQLRETURN SQL_API SQLGetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER BufferLength, SQLINTEGER *StringLength) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLGetStmtAttr(StatementHandle, Attribute, Value, BufferLength, StringLength)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLGetStmtOption(SQLHSTMT StatementHandle, + SQLUSMALLINT Option, SQLPOINTER Value) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLGetTypeInfo(SQLHSTMT StatementHandle, SQLSMALLINT DataType); + +SQLRETURN SQL_API SQLGetTypeInfo(SQLHSTMT StatementHandle, SQLSMALLINT DataType) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLGetTypeInfo(StatementHandle, DataType)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle, SQLSMALLINT *ColumnCount); + +SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, SQLSMALLINT *ColumnCount) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLNumResultCols(StatementHandle, ColumnCount)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLParamData(SQLHSTMT StatementHandle, SQLPOINTER *Value) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, SQLCHAR* StatementText, SQLINTEGER TextLength); + +SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR* StatementText, SQLINTEGER TextLength) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLPrepare(StatementHandle, StatementText, TextLength)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER Data, SQLLEN StrLen_or_Ind) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle, SQLLEN* RowCount); + +SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, SQLLEN* RowCount) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLRowCount(StatementHandle, RowCount)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLSetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLSetConnectOption(SQLHDBC ConnectionHandle, SQLUSMALLINT Option, SQLULEN Value) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLSetCursorName(SQLHSTMT StatementHandle, SQLCHAR* CursorName, SQLSMALLINT NameLength) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLSetDescField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, + SQLPOINTER Value, SQLINTEGER BufferLength) +{ + SQLRETURN r; + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLSetDescRec(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT Type, + SQLSMALLINT SubType, SQLLEN Length, SQLSMALLINT Precision, SQLSMALLINT Scale, + SQLPOINTER Data, SQLLEN *StringLength, SQLLEN *Indicator) +{ + SQLRETURN r; + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLSetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength); + +SQLRETURN SQL_API SQLSetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_ENV, EnvironmentHandle); + PROFILE(r, r = doSQLSetEnvAttr(EnvironmentHandle, Attribute, Value, StringLength)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLSetParam(SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, SQLULEN LengthPrecision, SQLSMALLINT ParameterScale, SQLPOINTER ParameterValue, + SQLLEN *StrLen_or_IndPtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLSetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength); + +SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLSetStmtAttr(StatementHandle, Attribute, Value, StringLength)); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLSetStmtOption(SQLHSTMT StatementHandle, SQLUSMALLINT Option, SQLULEN Value) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLSpecialColumns(SQLHSTMT StatementHandle, SQLUSMALLINT IdentifierType, + SQLCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLCHAR *TableName, SQLSMALLINT NameLength3, + SQLUSMALLINT Scope, SQLUSMALLINT Nullable) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLStatistics(SQLHSTMT StatementHandle, + SQLCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLCHAR *TableName, SQLSMALLINT NameLength3, + SQLUSMALLINT Unique, SQLUSMALLINT Reserved) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLTables(SQLHSTMT StatementHandle, + SQLCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLCHAR *TableName, SQLSMALLINT NameLength3, + SQLCHAR *TableType, SQLSMALLINT NameLength4) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + +SQLRETURN SQL_API SQLTransact(SQLHENV EnvironmentHandle, SQLHDBC ConnectionHandle, SQLUSMALLINT CompletionType) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_ENV, EnvironmentHandle); + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = SQL_ERROR); + todbc_tls_buf_reclaim(); + return r; +} + + + + +static SQLRETURN doSQLDriverConnect( + SQLHDBC ConnectionHandle, + SQLHWND WindowHandle, + SQLCHAR *InConnectionString, + SQLSMALLINT StringLength1, + SQLCHAR *OutConnectionString, + SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength2Ptr, + SQLUSMALLINT DriverCompletion); + +SQLRETURN SQL_API SQLDriverConnect( + SQLHDBC ConnectionHandle, + SQLHWND WindowHandle, + SQLCHAR *InConnectionString, + SQLSMALLINT StringLength1, + SQLCHAR *OutConnectionString, + SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength2Ptr, + SQLUSMALLINT DriverCompletion) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_DBC, ConnectionHandle); + PROFILE(r, r = doSQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString, StringLength1, + OutConnectionString, BufferLength, StringLength2Ptr, DriverCompletion)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLBindParameter( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT InputOutputType, + SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, + SQLULEN ColumnSize, + SQLSMALLINT DecimalDigits, + SQLPOINTER ParameterValuePtr, + SQLLEN BufferLength, + SQLLEN * StrLen_or_IndPtr); + +SQLRETURN SQL_API SQLBindParameter( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT InputOutputType, + SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, + SQLULEN ColumnSize, + SQLSMALLINT DecimalDigits, + SQLPOINTER ParameterValuePtr, + SQLLEN BufferLength, + SQLLEN * StrLen_or_IndPtr) +{ + SQLRETURN r; + P("ParameterNumber:[%d]; InputOutputType:[%d]%s; ValueType:[%d]%s; ParameterType:[%d]%s; " + "ColumnSize:[%ld]; DecimalDigits:[%d]; ParameterValuePtr:[%p]; BufferLength:[%ld]; StrLen_or_IndPtr:[%p]", + ParameterNumber, InputOutputType, sql_input_output_type(InputOutputType), + ValueType, sql_c_type(ValueType), + ParameterType, sql_sql_type(ParameterType), + ColumnSize, DecimalDigits, ParameterValuePtr, BufferLength, StrLen_or_IndPtr); + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLBindParameter(StatementHandle, ParameterNumber, InputOutputType, ValueType, ParameterType, + ColumnSize, DecimalDigits, ParameterValuePtr, BufferLength, StrLen_or_IndPtr)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLNumParams(SQLHSTMT StatementHandle, SQLSMALLINT *ParameterCountPtr); + +SQLRETURN SQL_API SQLNumParams(SQLHSTMT StatementHandle, SQLSMALLINT *ParameterCountPtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLNumParams(StatementHandle, ParameterCountPtr)); + todbc_tls_buf_reclaim(); + return r; +} + +static SQLRETURN doSQLDescribeParam( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT * DataTypePtr, + SQLULEN * ParameterSizePtr, + SQLSMALLINT * DecimalDigitsPtr, + SQLSMALLINT * NullablePtr); + +SQLRETURN SQL_API SQLDescribeParam( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT * DataTypePtr, + SQLULEN * ParameterSizePtr, + SQLSMALLINT * DecimalDigitsPtr, + SQLSMALLINT * NullablePtr) +{ + SQLRETURN r; + errs_clear(SQL_HANDLE_STMT, StatementHandle); + PROFILE(r, r = doSQLDescribeParam(StatementHandle, ParameterNumber, DataTypePtr, + ParameterSizePtr, DecimalDigitsPtr, NullablePtr)); + todbc_tls_buf_reclaim(); + return r; +} + + + + + +SQLRETURN SQL_API SQLBrowseConnect( + SQLHDBC hdbc, + SQLCHAR *szConnStrIn, + SQLSMALLINT cchConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cchConnStrOutMax, + SQLSMALLINT *pcchConnStrOut); + +SQLRETURN SQL_API SQLBulkOperations( + SQLHSTMT StatementHandle, + SQLSMALLINT Operation); + +SQLRETURN SQL_API SQLColAttributes( + SQLHSTMT hstmt, + SQLUSMALLINT icol, + SQLUSMALLINT fDescType, + SQLPOINTER rgbDesc, + SQLSMALLINT cbDescMax, + SQLSMALLINT *pcbDesc, + SQLLEN * pfDesc); + +SQLRETURN SQL_API SQLColumnPrivileges( + SQLHSTMT hstmt, + SQLCHAR *szCatalogName, + SQLSMALLINT cchCatalogName, + SQLCHAR *szSchemaName, + SQLSMALLINT cchSchemaName, + SQLCHAR *szTableName, + SQLSMALLINT cchTableName, + SQLCHAR *szColumnName, + SQLSMALLINT cchColumnName); + +SQLRETURN SQL_API SQLExtendedFetch( + SQLHSTMT hstmt, + SQLUSMALLINT fFetchType, + SQLLEN irow, + SQLULEN *pcrow, + SQLUSMALLINT *rgfRowStatus); + +SQLRETURN SQL_API SQLForeignKeys( + SQLHSTMT hstmt, + SQLCHAR *szPkCatalogName, + SQLSMALLINT cchPkCatalogName, + SQLCHAR *szPkSchemaName, + SQLSMALLINT cchPkSchemaName, + SQLCHAR *szPkTableName, + SQLSMALLINT cchPkTableName, + SQLCHAR *szFkCatalogName, + SQLSMALLINT cchFkCatalogName, + SQLCHAR *szFkSchemaName, + SQLSMALLINT cchFkSchemaName, + SQLCHAR *szFkTableName, + SQLSMALLINT cchFkTableName); + +SQLRETURN SQL_API SQLMoreResults( + SQLHSTMT hstmt); + +SQLRETURN SQL_API SQLNativeSql +( + SQLHDBC hdbc, + SQLCHAR* szSqlStrIn, + SQLINTEGER cchSqlStrIn, + SQLCHAR* szSqlStr, + SQLINTEGER cchSqlStrMax, + SQLINTEGER *pcbSqlStr +); + +SQLRETURN SQL_API SQLParamOptions( + SQLHSTMT hstmt, + SQLULEN crow, + SQLULEN *pirow); + +SQLRETURN SQL_API SQLPrimaryKeys( + SQLHSTMT hstmt, + SQLCHAR *szCatalogName, + SQLSMALLINT cchCatalogName, + SQLCHAR *szSchemaName, + SQLSMALLINT cchSchemaName, + SQLCHAR *szTableName, + SQLSMALLINT cchTableName); + +SQLRETURN SQL_API SQLProcedureColumns( + SQLHSTMT hstmt, + SQLCHAR *szCatalogName, + SQLSMALLINT cchCatalogName, + SQLCHAR *szSchemaName, + SQLSMALLINT cchSchemaName, + SQLCHAR *szProcName, + SQLSMALLINT cchProcName, + SQLCHAR *szColumnName, + SQLSMALLINT cchColumnName); + +SQLRETURN SQL_API SQLProcedures( + SQLHSTMT hstmt, + SQLCHAR *szCatalogName, + SQLSMALLINT cchCatalogName, + SQLCHAR *szSchemaName, + SQLSMALLINT cchSchemaName, + SQLCHAR *szProcName, + SQLSMALLINT cchProcName); + + + +SQLRETURN SQL_API SQLSetPos( + SQLHSTMT hstmt, + SQLSETPOSIROW irow, + SQLUSMALLINT fOption, + SQLUSMALLINT fLock); + +SQLRETURN SQL_API SQLTablePrivileges( + SQLHSTMT hstmt, + SQLCHAR *szCatalogName, + SQLSMALLINT cchCatalogName, + SQLCHAR *szSchemaName, + SQLSMALLINT cchSchemaName, + SQLCHAR *szTableName, + SQLSMALLINT cchTableName); + +SQLRETURN SQL_API SQLDrivers( + SQLHENV henv, + SQLUSMALLINT fDirection, + SQLCHAR *szDriverDesc, + SQLSMALLINT cchDriverDescMax, + SQLSMALLINT *pcchDriverDesc, + SQLCHAR *szDriverAttributes, + SQLSMALLINT cchDrvrAttrMax, + SQLSMALLINT *pcchDrvrAttr); + +SQLRETURN SQL_API SQLAllocHandleStd( + SQLSMALLINT fHandleType, + SQLHANDLE hInput, + SQLHANDLE *phOutput); +SQLRETURN SQL_API SQLSetScrollOptions( + SQLHSTMT hstmt, + SQLUSMALLINT fConcurrency, + SQLLEN crowKeyset, + SQLUSMALLINT crowRowset); + + + + +static SQLRETURN doSQLAllocEnv(SQLHENV *EnvironmentHandle) +{ + env_t *env = (env_t*)calloc(1, sizeof(*env)); + OILE(env, ""); + + int r = env_init(env); + if (r) return SQL_ERROR; + + *EnvironmentHandle = env; + + return SQL_SUCCESS; +} + +static SQLRETURN doSQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle) +{ + P("HandleType:[%d]%s", HandleType, sql_handle_type(HandleType)); + switch (HandleType) + { + case SQL_HANDLE_ENV: + { + return doSQLAllocEnv(OutputHandle); + } break; + case SQL_HANDLE_DBC: + { + errs_clear(SQL_HANDLE_ENV, InputHandle); + return doSQLAllocConnect(InputHandle, OutputHandle); + } break; + case SQL_HANDLE_STMT: + { + errs_clear(SQL_HANDLE_DBC, InputHandle); + return doSQLAllocStmt(InputHandle, OutputHandle); + } break; + default: + { + ONIY(0, ""); + } break; + } +} + +static SQLRETURN doSQLSetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) +{ + P("Attribute:[%d]%s", Attribute, sql_env_attr_type(Attribute)); + env_t *env = (env_t*)EnvironmentHandle; + OILE(env, ""); + + switch (Attribute) + { + case SQL_ATTR_ODBC_VERSION: + { + int32_t ver = (int32_t)(size_t)Value; + P("client odbc ver:[%d]", ver); + if (ver < env->odbc_ver) { + // fall back to lower version + env->odbc_ver = ver; + } + return SQL_SUCCESS; + } break; + default: + { + ONIY(0, ""); + } break; + } +} + +static SQLRETURN doSQLGetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER BufferLength, SQLINTEGER *StringLength) +{ + P("Attribute:[%d]%s; Value:[%p]; BufferLength:[%d]; StringLength:[%p][%d]", + Attribute, sql_env_attr_type(Attribute), Value, BufferLength, + StringLength, StringLength ? *StringLength : 0); + env_t *env = (env_t*)EnvironmentHandle; + OILE(env, ""); + + switch (Attribute) + { + case SQL_ATTR_ODBC_VERSION: + { + *(int32_t*)Value = env->odbc_ver; + P("odbc ver:[%d]", env->odbc_ver); + return SQL_SUCCESS; + } break; + default: + { + ONIY(0, ""); + } break; + } +} + +static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle, SQLHDBC *ConnectionHandle) +{ + env_t *env = (env_t*)EnvironmentHandle; + OILE(env, ""); + errs_t *errs = env_get_errs(env); + + conn_t *conn = conn_new(env); + if (!conn) { + SET_OOM(errs, "alloc conn failed"); + return SQL_ERROR; + } + + do { + *ConnectionHandle = conn; + + return SQL_SUCCESS; + } while (0); + + conn_free(conn); + + return SQL_ERROR; +} + +static SQLRETURN doSQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) +{ + switch (HandleType) + { + case SQL_HANDLE_ENV: + { + return doSQLFreeEnv(Handle); + } break; + case SQL_HANDLE_DBC: + { + return doSQLFreeConnect(Handle); + } break; + case SQL_HANDLE_STMT: + { + stmt_t *stmt = (stmt_t*)Handle; + stmt_free(stmt); + return SQL_SUCCESS; + } break; + default: + { + ONIY(0, "HandleType:[%d]%s", HandleType, sql_handle_type(HandleType)); + } break; + } +} + +static SQLRETURN do_connect(conn_t *conn) { + errs_t *errs = &conn->errs; + + OD("enc:src/char/wchar/db/locale:[%s/%s/%s/%s/%s]", + conn->enc_src, conn->enc_char, conn->enc_wchar, conn->enc_db, conn->enc_locale); + + SQLRETURN r; + + r = conn_check_charset(conn, errs); + if (r!=SQL_SUCCESS) return r; + + if (0) { + // test with null_conn + if (conn_init_null_conn(conn)) { + SET_GENERAL(errs, "failed to init null conn for test"); + return SQL_ERROR; + } + } else { + if (conn_init_tsdb_conn(conn)) { + SET_GENERAL(errs, "failed to init taos conn for test"); + return SQL_ERROR; + } + } + + return conn_connect(conn); +} + +static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, SQLSMALLINT NameLength1, + SQLCHAR *UserName, SQLSMALLINT NameLength2, + SQLCHAR *Authentication, SQLSMALLINT NameLength3) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + OILE(conn, ""); + ONIY(ServerName, ""); + + errs_t *errs = &conn->errs; + + const char *enc_to = conn->enc_locale; + const char *enc_from = conn->enc_char; + + SQLRETURN ok = SQL_ERROR; + + conn_val_t *val = &conn->val; + conn_val_reset(val); + + do { + if (ServerName) { + size_t slen = (size_t)NameLength1; + todbc_string_t dsn = todbc_tls_conv(NULL, enc_to, enc_from, (const unsigned char*)ServerName, &slen); + if (!dsn.buf) { + SET_OOM(errs, "alloc buf failed"); + return SQL_ERROR; + } + val->dsn = strdup((const char*)dsn.buf); + if (!val->dsn) { + SET_OOM(errs, "strdup failed"); + return SQL_ERROR; + } + } + if (UserName) { + size_t slen = (size_t)NameLength2; + todbc_string_t uid = todbc_tls_conv(NULL, enc_to, enc_from, (const unsigned char*)UserName, &slen); + if (!uid.buf) { + SET_OOM(errs, "alloc buf failed"); + return SQL_ERROR; + } + val->uid = strdup((const char*)uid.buf); + if (!val->uid) { + SET_OOM(errs, "strdup failed"); + return SQL_ERROR; + } + } + if (Authentication) { + size_t slen = (size_t)NameLength3; + todbc_string_t pwd = todbc_tls_conv(NULL, enc_to, enc_from, (const unsigned char*)Authentication, &slen); + if (!pwd.buf) { + SET_OOM(errs, "alloc buf failed"); + return SQL_ERROR; + } + val->pwd = strdup((const char*)pwd.buf); + if (!val->pwd) { + SET_OOM(errs, "strdup failed"); + return SQL_ERROR; + } + } + + OD("................."); + ok = do_connect(conn); + OD("................."); + if (ok!=SQL_SUCCESS) break; + + CONN_SET_CONNECTED(conn); + return SQL_SUCCESS; + } while (0); + + conn_val_reset(val); + + return ok; +} + +static SQLRETURN doSQLDriverConnect( + SQLHDBC ConnectionHandle, + SQLHWND WindowHandle, + SQLCHAR *InConnectionString, + SQLSMALLINT StringLength1, + SQLCHAR *OutConnectionString, + SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength2Ptr, + SQLUSMALLINT DriverCompletion) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + OILE(conn, ""); + ONIY(InConnectionString, ""); + + errs_t *errs = &conn->errs; + +#ifndef _MSC_VER + if (DriverCompletion!=SQL_DRIVER_NOPROMPT) { + SET_NIY(errs, "option[%d] other than SQL_DRIVER_NOPROMPT not supported yet", DriverCompletion); + return SQL_ERROR; + } +#endif + + const char *enc_to = conn->enc_locale; + const char *enc_from = conn->enc_char; + + size_t slen = (size_t)StringLength1; + todbc_string_t ts = todbc_tls_conv(NULL, enc_to, enc_from, (const unsigned char*)InConnectionString, &slen); + const char *connStr = (const char*)ts.buf; + if (!connStr) { + SET_OOM(errs, "alloc buf failed"); + return SQL_ERROR; + } + + SQLRETURN ok = SQL_ERROR; + + conn_val_t *val = &conn->val; + conn_val_reset(val); + + do { + // TO_DO: genralize + int n = todbc_parse_conn_string(connStr, val); + if (n) { + SET_GENERAL(errs, "unrecognized connection string:[%s]", connStr); + break; + } + + todbc_enc_t enc; + if (val->enc_char) { + enc = todbc_tls_iconv_enc(val->enc_char); + if (enc.enc[0]=='\0') { + SET_GENERAL(errs, "unrecognized charset:[%s]", val->enc_char); + break; + } + snprintf(conn->enc_char, sizeof(conn->enc_char), "%s", val->enc_char); + } + if (val->enc_wchar) { + enc = todbc_tls_iconv_enc(val->enc_wchar); + if (enc.enc[0]=='\0') { + SET_GENERAL(errs, "unrecognized charset:[%s]", val->enc_wchar); + break; + } + snprintf(conn->enc_wchar, sizeof(conn->enc_wchar), "%s", val->enc_wchar); + } + if (val->enc_db) { + enc = todbc_tls_iconv_enc(val->enc_db); + if (enc.enc[0]=='\0') { + SET_GENERAL(errs, "unrecognized charset:[%s]", val->enc_db); + break; + } + snprintf(conn->enc_db, sizeof(conn->enc_db), "%s", val->enc_db); + } + if (val->enc_local) { + enc = todbc_tls_iconv_enc(val->enc_local); + if (enc.enc[0]=='\0') { + SET_GENERAL(errs, "unrecognized charset:[%s]", val->enc_local); + break; + } + snprintf(conn->enc_locale, sizeof(conn->enc_locale), "%s", val->enc_local); + } + + ok = do_connect(conn); + if (ok!=SQL_SUCCESS) break; + ok = SQL_ERROR; + + n = 0; + if (OutConnectionString) { + n = snprintf((char*)OutConnectionString, (size_t)BufferLength, "%s", connStr); + } + if (StringLength2Ptr) { + *StringLength2Ptr = (SQLSMALLINT)n; + } + + CONN_SET_CONNECTED(conn); + return SQL_SUCCESS; + } while (0); + + conn_val_reset(val); + + return ok; +} + +static SQLRETURN doSQLGetInfo(SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, SQLPOINTER InfoValue, + SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr) +{ + P("InfoType:[%d]%s; BufferLength:[%d]; StringLengthPtr:[%p]%d", + InfoType, sql_info_type(InfoType), BufferLength, + StringLengthPtr, StringLengthPtr ? *StringLengthPtr : 0); + + switch (InfoType) + { + case SQL_DRIVER_ODBC_VER: + { + // how to sync with env->odbc_ver ? + const char *v = "03.00"; + int n = snprintf((char*)InfoValue, (size_t)BufferLength, "%s", v); + OILE(n>0, ""); + *StringLengthPtr = (SQLSMALLINT)n; + } break; + case SQL_DESCRIBE_PARAMETER: + { + const char *v = "Y"; + int n = snprintf((char*)InfoValue, (size_t)BufferLength, "%s", v); + OILE(n>0, ""); + *StringLengthPtr = (SQLSMALLINT)n; + } break; + case SQL_NEED_LONG_DATA_LEN: + { + const char *v = "Y"; + int n = snprintf((char*)InfoValue, (size_t)BufferLength, "%s", v); + OILE(n>0, ""); + *StringLengthPtr = (SQLSMALLINT)n; + } break; + case SQL_MAX_COLUMN_NAME_LEN: + { + SQLUSMALLINT v = 64; + OILE(BufferLength==sizeof(v), ""); + *(SQLUSMALLINT*)InfoValue = v; + if (StringLengthPtr) *StringLengthPtr = sizeof(v); + } break; + case SQL_TXN_ISOLATION_OPTION: + { + SQLUINTEGER v = SQL_TXN_READ_UNCOMMITTED; + OILE(BufferLength==sizeof(v), ""); + *(SQLUINTEGER*)InfoValue = v; + if (StringLengthPtr) *StringLengthPtr = sizeof(v); + } break; + case SQL_CURSOR_COMMIT_BEHAVIOR: + { + SQLUSMALLINT v = SQL_CB_PRESERVE; + OILE(BufferLength==sizeof(v), ""); + *(SQLUSMALLINT*)InfoValue = v; + if (StringLengthPtr) *StringLengthPtr = sizeof(v); + } break; + case SQL_CURSOR_ROLLBACK_BEHAVIOR: + { + SQLUSMALLINT v = SQL_CB_PRESERVE; + OILE(BufferLength==sizeof(v), ""); + *(SQLUSMALLINT*)InfoValue = v; + if (StringLengthPtr) *StringLengthPtr = sizeof(v); + } break; + case SQL_GETDATA_EXTENSIONS: + { + SQLUINTEGER v = SQL_GD_ANY_COLUMN; + OILE(BufferLength==sizeof(v), ""); + *(SQLUINTEGER*)InfoValue = v; + if (StringLengthPtr) *StringLengthPtr = sizeof(v); + } break; + case SQL_DTC_TRANSITION_COST: + { + SQLUINTEGER v = SQL_DTC_ENLIST_EXPENSIVE | SQL_DTC_UNENLIST_EXPENSIVE; + OILE(BufferLength==sizeof(v), ""); + *(SQLUINTEGER*)InfoValue = v; + if (StringLengthPtr) *StringLengthPtr = sizeof(v); + } break; + case SQL_MAX_CONCURRENT_ACTIVITIES: + { + SQLUSMALLINT v = 10240; + OILE(BufferLength==sizeof(v), ""); + *(SQLUSMALLINT*)InfoValue = v; + if (StringLengthPtr) *StringLengthPtr = sizeof(v); + } break; + default: + { + ONIY(0, ""); + // return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_fill_error(errs_t *errs, const char *enc_to, const char *enc_from, + SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, SQLINTEGER *NativeError, + SQLCHAR* MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) +{ + if (errs_count(errs)<=0) return SQL_NO_DATA; + + const char *sql_state = NULL; + const char *err_str = NULL; + int r = errs_fetch(errs, RecNumber-1, &sql_state, &err_str); + if (r) return SQL_NO_DATA; + OILE(sql_state && err_str, ""); + const unsigned char *src = (const unsigned char*)err_str; + size_t slen = strlen(err_str); + unsigned char *dst = (unsigned char*)MessageText; + size_t dlen = (size_t)BufferLength; + // OILE(dst, ""); + if (!MessageText) { + OILE(TextLength, ""); + *TextLength = 4096; + return SQL_SUCCESS; + } + todbc_string_t s = todbc_tls_write(enc_to, enc_from, src, &slen, dst, dlen); + *NativeError = 0; + *TextLength = (SQLSMALLINT)s.bytes; + snprintf((char*)Sqlstate, 6, "%s", sql_state); + return SQL_SUCCESS; +} + +static SQLRETURN doSQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, + SQLCHAR *Sqlstate, SQLINTEGER *NativeError, SQLCHAR* MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) +{ + OILE(RecNumber>0, ""); + OILE(Sqlstate, ""); + // OILE(NativeError, ""); + OILE(TextLength, ""); + switch (HandleType) + { + // case SQL_HANDLE_ENV: + // { + // env_t *env = (env_t*)Handle; + // FILL_ERROR(env); + // return SQL_SUCCESS; + // } break; + case SQL_HANDLE_DBC: + { + conn_t *conn = (conn_t*)Handle; + OILE(conn, ""); + errs_t *errs = conn_get_errs(conn); + OILE(errs, ""); + const char *enc_to = conn->enc_char; + const char *enc_from = conn->enc_src; + return do_fill_error(errs, enc_to, enc_from, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength); + } break; + case SQL_HANDLE_STMT: + { + stmt_t *stmt = (stmt_t*)Handle; + OILE(stmt, ""); + if (!stmt->owner) return SQL_NO_DATA; + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + errs_t *errs = stmt_get_errs(stmt); + OILE(errs, ""); + const char *enc_to = conn->enc_char; + const char *enc_from = conn->enc_src; + return do_fill_error(errs, enc_to, enc_from, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength); + } break; + default: + { + ONIY(0, "HandleType:[%d]%s", HandleType, sql_handle_type(HandleType)); + // return SQL_ERROR; + } break; + } +} + +static SQLRETURN doSQLAllocStmt(SQLHDBC ConnectionHandle, SQLHSTMT *StatementHandle) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + OILE(conn, ""); + + errs_t *errs = &conn->errs; + + stmt_t *stmt = stmt_new(conn); + if (!stmt) { + SET_OOM(errs, "alloc stmt failed"); + return SQL_ERROR; + } + + do { + if (!StatementHandle) break; + + *StatementHandle = stmt; + return SQL_SUCCESS; + } while (0); + + stmt_free(stmt); + OILE(0, ""); +} + +static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, SQLUSMALLINT Option) +{ + P("Option:[%d]%s", Option, sql_freestmt_option_type(Option)); + + switch (Option) + { + case SQL_CLOSE: + { + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + if (STMT_IS_NORM(stmt)) break; + return doSQLCloseCursor(StatementHandle); + } break; + // case SQL_UNBIND: + // { + // } break; + case SQL_RESET_PARAMS: + { + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + stmt_reset_params(stmt); + } break; + default: + { + ONIY(0, ""); + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN doSQLGetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER BufferLength, SQLINTEGER *StringLength) +{ + P("Attribute:[%d]%s; BufferLength:[%d]; StringLength:[%p][%d]", + Attribute, sql_stmt_attr_type(Attribute), BufferLength, + StringLength, StringLength ? *StringLength : 0); + stmt_t *stmt = (stmt_t*)StatementHandle; + descs_t *descs = &stmt->descs; + + switch (Attribute) + { + case SQL_ATTR_APP_ROW_DESC: + { + OILE(BufferLength==sizeof(descs->app_row) || BufferLength==SQL_IS_POINTER, ""); + *(void**)Value = descs->app_row; + } break; + case SQL_ATTR_APP_PARAM_DESC: + { + OILE(BufferLength==sizeof(descs->app_param) || BufferLength==SQL_IS_POINTER, ""); + *(void**)Value = descs->app_param; + } break; + case SQL_ATTR_IMP_ROW_DESC: + { + OILE(BufferLength==sizeof(descs->imp_row) || BufferLength==SQL_IS_POINTER, ""); + *(void**)Value = descs->imp_row; + } break; + case SQL_ATTR_IMP_PARAM_DESC: + { + OILE(BufferLength==sizeof(descs->imp_param) || BufferLength==SQL_IS_POINTER, ""); + *(void**)Value = descs->imp_param; + } break; + default: + { + ONIY(0, ""); + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR* StatementText, SQLINTEGER TextLength) +{ + P("TextLength:[%d]%s", TextLength, sql_soi_type(TextLength)); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(!STMT_IS_EXECUTING(stmt), ""); + stmt_close_rs(stmt); + OILE(STMT_IS_NORM(stmt), ""); + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + errs_t *errs = &stmt->errs; + + const char *enc = conn->enc_char; + + OILE(StatementText, ""); + OILE(TextLength>=0 || TextLength==SQL_NTS, ""); + size_t slen = (TextLength>=0) ? (size_t)TextLength : (size_t)-1; + todbc_string_t sql = todbc_string_init(enc, StatementText, slen); + if (!sql.buf) { + SET_OOM(errs, ""); + return SQL_ERROR; + } + + return stmt_exec_direct(stmt, &sql); + + + // stmt_t *stmt = (stmt_t*)StatementHandle; + // rs_t *rs = &stmt->rs; + // rs_close(rs); + // OILE(rs->stmt==NULL); + + // if (1) { + // // taos_stmt_prepare fails to prepare a non-param-stmt-statement + // // thus we fall-back to use taos_query + // OILE(stmt->owner); + // conn_t *conn = stmt->owner->conn; + // OILE(conn); + + // OILE(rs->stmt==NULL); + // paramset_t *paramset = &stmt->paramset; + // paramset_close(paramset); + + // SQLRETURN e = stmt_set_statement(stmt, StatementText, TextLength); + // if (e!=SQL_SUCCESS) return e; + + // taos_rs_t *taos_rs = &rs->taos_rs; + + // const char *buf = (const char*)stmt->statement_db.buf; + // taos_rs->rs = taos_query(conn->taos, buf); + + // if (!taos_rs->rs) { + // int err = terrno; + // SET_ERROR(stmt, "HY000", "failed to execute statement:[%d]%s", err, tstrerror(err)); + // // keep executing/executed state unchanged + // return SQL_ERROR; + // } + + // taos_rs->owner = rs; + // taos_rs->rs_is_opened_by_taos_query = 1; + + // SET_EXECUTED(stmt); + // return stmt_after_exec(stmt); + // } else { + // SQLRETURN r = doSQLPrepare(StatementHandle, StatementText, TextLength); + // if (r!=SQL_SUCCESS) return r; + + // return doSQLExecute(StatementHandle); + // } +} + +static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle, SQLLEN* RowCount) +{ + OILE(RowCount, ""); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(STMT_IS_EXECUTED(stmt), ""); + + OILE(stmt->ext.get_affected_rows, ""); + SQLRETURN r = stmt->ext.get_affected_rows(stmt, RowCount); + if (r!=SQL_SUCCESS) return r; + + P("RowCount:[%ld]", *RowCount); + + return SQL_SUCCESS; +} + +static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle, SQLSMALLINT *ColumnCount) +{ + OILE(ColumnCount, ""); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(STMT_IS_EXECUTED(stmt), ""); + + OILE(stmt->ext.get_fields_count, ""); + SQLRETURN r = stmt->ext.get_fields_count(stmt, ColumnCount); + if (r!=SQL_SUCCESS) return r; + + P("ColumnCount:[%d]", *ColumnCount); + + return SQL_SUCCESS; +} + +static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, SQLSMALLINT BufferLength, + SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable) +{ + P("ColumnNumber:[%d]; BufferLength:[%d]", ColumnNumber, BufferLength); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(STMT_IS_EXECUTED(stmt), ""); + + field_arg_t field_arg = {0}; + field_arg.ColumnNumber = ColumnNumber; + field_arg.ColumnName = ColumnName; + field_arg.BufferLength = BufferLength; + field_arg.NameLength = NameLength; + field_arg.DataType = DataType; + field_arg.ColumnSize = ColumnSize; + field_arg.DecimalDigits = DecimalDigits; + field_arg.Nullable = Nullable; + + OILE(stmt->ext.get_field, ""); + return stmt->ext.get_field(stmt, &field_arg); + + // sql_t *sql = (sql_t*)StatementHandle; + // rs_t *rs = &sql->rs; + // OILE(rs->sql==sql); + // OILE(BufferLength>=0); + // OILE(NameLength); + // OILE(DataType); + // OILE(ColumnSize); + // OILE(DecimalDigits); + // OILE(Nullable); + + // OILE(ColumnNumber>0 && ColumnNumber<=rs->n_fields); + // field_t *field = rs->fields + (ColumnNumber-1); + + // int n = snprintf((char*)ColumnName, (size_t)BufferLength, "%s", field->name); + // *NameLength = (SQLSMALLINT)n; + // *DataType = (SQLSMALLINT)field->data_type; + // *ColumnSize = (SQLUSMALLINT)field->col_size; + // *DecimalDigits = (SQLSMALLINT)field->decimal_digits; + // *Nullable = (SQLSMALLINT)field->nullable; + + + // P("ColumnNumber:[%d]; DataType:[%d]%s; Name:[%s]; ColumnSize:[%ld]; DecimalDigits:[%d]; Nullable:[%d]%s", + // ColumnNumber, *DataType, sql_sql_type(*DataType), field->name, + // *ColumnSize, *DecimalDigits, *Nullable, sql_nullable_type(*Nullable)); + + // return SQL_SUCCESS; +} + +static SQLRETURN doSQLDisconnect(SQLHDBC ConnectionHandle) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + OILE(conn, ""); + OILE(CONN_IS_CONNECTED(conn), ""); + + conn_disconnect(conn); + + CONN_SET_NORM(conn); + return SQL_SUCCESS; + + + // OILE(conn->taos); + + // // conn/stmts which ever to close first? + // // as for ODBC, it's suggested to close connection first, then release all dangling statements + // // but we are not sure if doing so will result in memory leakage for taos + // // thus we choose to close dangling statements before shutdown connection + // conn_release_sqls(conn); + + // taos_close(conn->taos); + // conn->taos = 0; + + // return SQL_SUCCESS; +} + +static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, SQLCHAR* StatementText, SQLINTEGER TextLength) +{ + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(!STMT_IS_EXECUTING(stmt), ""); + stmt_close_rs(stmt); + OILE(STMT_IS_NORM(stmt), ""); + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + errs_t *errs = &stmt->errs; + + const char *enc = conn->enc_char; + + OILE(StatementText, ""); + OILE(TextLength>=0 || TextLength==SQL_NTS, ""); + size_t slen = (TextLength>=0) ? (size_t)TextLength : (size_t)-1; + todbc_string_t sql = todbc_string_init(enc, StatementText, slen); + if (!sql.buf) { + SET_OOM(errs, ""); + return SQL_ERROR; + } + + if (1) { + todbc_string_t l = todbc_string_conv_to(&sql, conn->enc_src, NULL); + P("prepare:[%s]", (const char*)l.buf); + } + + return stmt_prepare(stmt, &sql); + + // sql_t *sql = (sql_t*)StatementHandle; + // rs_t *rs = &sql->rs; + + // OILE(rs->taos_rs.rs_is_opened_by_taos_query==0); + + // OILE(sql->owner); + // conn_t *conn = sql->owner->conn; + // OILE(conn); + + // OILE(rs->sql==NULL); + // paramset_t *paramset = &sql->paramset; + // paramset_close(paramset); + + // SQLRETURN r = sql_set_statement(sql, StatementText, TextLength); + // if (r!=SQL_SUCCESS) return r; + // + // int n_params = 0; + // r = sql_prepare(sql, &n_params); + // if (r!=SQL_SUCCESS) return r; + + // paramset_init_params(paramset, n_params); + // if (!paramset->params_cache || !paramset->params || !paramset->taos_binds) { + // sql_close_taos_stmt(sql); + // SET_ERROR(sql, "HY001", ""); + // return SQL_ERROR; + // } + + // // for (int i=0; iparams + i; + + // // } + + // paramset->sql = sql; + // paramset->n_params = n_params; + + // return sql_after_prepare(sql); +} + +static SQLRETURN doSQLNumParams(SQLHSTMT StatementHandle, SQLSMALLINT *ParameterCountPtr) +{ + OILE(ParameterCountPtr, ""); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(stmt->prepared, ""); + + *ParameterCountPtr = (SQLSMALLINT)stmt->paramset.n_params; + + P("ParameterCount:[%d]", *ParameterCountPtr); + + return SQL_SUCCESS; +} + +static SQLRETURN doSQLBindParameter( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT InputOutputType, + SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, + SQLULEN ColumnSize, + SQLSMALLINT DecimalDigits, + SQLPOINTER ParameterValuePtr, + SQLLEN BufferLength, + SQLLEN * StrLen_or_IndPtr) +{ + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(!STMT_IS_EXECUTING(stmt), ""); + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + param_binding_t arg = {0}; + arg.ParameterNumber = ParameterNumber; + arg.InputOutputType = InputOutputType; + arg.ValueType = ValueType; // sql c data type + arg.ParameterType = ParameterType; // sql data type + arg.ColumnSize = ColumnSize; + arg.DecimalDigits = DecimalDigits; + arg.ParameterValuePtr = ParameterValuePtr; + arg.BufferLength = BufferLength; + arg.StrLen_or_IndPtr = StrLen_or_IndPtr; + + return stmt_bind_param(stmt, &arg); + + // sql_t *sql = (sql_t*)StatementHandle; + // rs_t *rs = &sql->rs; + // OILE(rs->sql==NULL); + // OILE(IS_PREPARED(sql)); + // paramset_t *paramset = &sql->paramset; + // OILE(ParameterNumber>0); + + // OILE(StrLen_or_IndPtr); + + // paramset_realloc_bps(paramset, ParameterNumber); + // if (!paramset->bps_cache || !paramset->bps || paramset->n_bpsbps + (ParameterNumber-1); + // bp->ParameterNumber = ParameterNumber; + // bp->InputOutputType = InputOutputType; + // bp->ValueType = ValueType; + // bp->ParameterType = ParameterType; + // bp->ColumnSize = ColumnSize; + // bp->DecimalDigits = DecimalDigits; + // bp->ParameterValuePtr = ParameterValuePtr; + // bp->BufferLength = BufferLength; + // bp->StrLen_or_IndPtr = StrLen_or_IndPtr; + + // return SQL_SUCCESS; +} + +static SQLRETURN doSQLSetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) +{ + P("Attribute:[%d]%s; Value:[%p]; StringLength:[%d]%s", + Attribute, sql_stmt_attr_type(Attribute), Value, + StringLength, sql_soi_type(StringLength)); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(!STMT_IS_EXECUTING(stmt), ""); + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + stmt_attr_t *attr = &stmt->attr; + + errs_t *errs = &stmt->errs; + + switch (Attribute) + { + case SQL_ATTR_PARAM_BIND_TYPE: + { + SQLULEN v = (SQLULEN)Value; + ONIY(v!=SQL_PARAM_BIND_BY_COLUMN, ""); + attr->bind_type = v; + P("%s:[%ld]", sql_stmt_attr_type(Attribute), v); + } break; + case SQL_ATTR_PARAMSET_SIZE: + { + SQLULEN v = (SQLULEN)Value; + ONIY(v!=0, ""); + attr->paramset_size = v; + P("%s:[%ld]", sql_stmt_attr_type(Attribute), v); + } break; + case SQL_ATTR_PARAM_BIND_OFFSET_PTR: + { + SQLULEN *v = (SQLULEN*)Value; + attr->bind_offset_ptr = v; + P("%s:[%p]", sql_stmt_attr_type(Attribute), v); + } break; + default: + { + P("Attribute:[%d]%s not implemented yet", Attribute, sql_stmt_attr_type(Attribute)); + SET_NIY(errs, "Attribute:[%d]%s", Attribute, sql_stmt_attr_type(Attribute)); + // ONSP(0, "Attribute:[%d]%s", Attribute, sql_stmt_attr_type(Attribute)); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) +{ + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(!STMT_IS_EXECUTING(stmt), ""); + stmt_close_rs(stmt); + OILE(STMT_IS_NORM(stmt), ""); + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + return stmt_execute(stmt); + + // sql_t *sql = (sql_t*)StatementHandle; + // OILE(sql); + // OILE(IS_PREPARED(sql)); + // OILE(!IS_EXECUTING(sql)); + + // rs_t *rs = &sql->rs; + // OILE(rs->taos_rs.rs_is_opened_by_taos_query==0); + // rs_close(rs); + // OILE(rs->sql==NULL); + + // SET_EXECUTING(sql); + + // paramset_t *paramset = &sql->paramset; + // OILE(paramset); + + // // fetch parameters + // OILE(paramset->n_bps==paramset->n_params); + // if (paramset->paramset_size==0) { + // // nodejs workaround + // paramset->paramset_size = 1; + // } + // for (unsigned int i=0; iparamset_size; ++i) { + // for (unsigned int j=0; jn_bps && jn_params; ++j) { + // SQLRETURN r = sql_process_param(sql, i, j); + // if (r!=SQL_SUCCESS) return r; + // } + // OILE(paramset->taos_binds); + // int tr = taos_stmt_bind_param(sql->stmt, paramset->taos_binds); + // if (tr) { + // SET_ERROR(sql, "HY000", "failed to bind parameters[%d in total][%d]%s", paramset->n_params, tr, tstrerror(tr)); + // // keep executing/executed state unchanged + // return SQL_ERROR; + // } + + // tr = taos_stmt_add_batch(sql->stmt); + // if (tr) { + // SET_ERROR(sql, "HY000", "failed to add batch:[%d]%s", tr, tstrerror(tr)); + // // keep executing/executed state unchanged + // return SQL_ERROR; + // } + // } + + // if (1) { + // int r = taos_stmt_execute(sql->stmt); + // if (r) { + // SET_ERROR(sql, "HY000", "failed to execute statement:[%d]%s", r, tstrerror(r)); + // // keep executing/executed state unchanged + // return SQL_ERROR; + // } + + // taos_rs_t *taos_rs = &rs->taos_rs; + // OILE(taos_rs->owner==NULL); + // taos_rs->owner = rs; + // taos_rs->rs = taos_stmt_use_result(sql->stmt); + // } + + // SET_EXECUTED(sql); + + // return sql_after_exec(sql); +} + +static SQLRETURN doSQLFetch(SQLHSTMT StatementHandle) +{ + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(STMT_IS_EXECUTED(stmt), ""); + + if (STMT_TYPEINFO(stmt)) { + return SQL_SUCCESS; + } + + return stmt_fetch(stmt); + // sql_t *sql = (sql_t*)StatementHandle; + // OILE(sql); + // rs_t *rs = &sql->rs; + // OILE(rs->n_rows==-1 || rs->curr_rown_rows); + // row_t *row = &rs->row; + // taos_rs_t *taos_rs = &rs->taos_rs; + // OA(rs->n_fields==taos_rs->n_fields, "%d/%d", rs->n_fields, taos_rs->n_fields); + + // if (rs->eof) return SQL_NO_DATA; + + // if (rs->n_rows!=-1 && rs->curr_row + 1 >= rs->n_rows) { + // row_release(row); + // taos_rs->row = NULL; + // rs->eof = 1; + // return SQL_NO_DATA; + // } + + // row_release(row); + // taos_rs->row = NULL; + // ++rs->curr_row; + // row->valid = 1; + // taos_rs->row = taos_fetch_row(taos_rs->rs); + // D("row:[%p]", taos_rs->row); + // if (!taos_rs->row) { + // row->valid = 0; + // rs->eof = 1; + // D("..."); + // return SQL_NO_DATA; + // } + + // // column bound? + // if (!rs->bcs) return SQL_SUCCESS; + // for (int i=0; in_fields; ++i) { + // SQLRETURN r = sql_get_data(sql, (unsigned int)i); + // if (r!=SQL_SUCCESS) return r; + // } + + // return SQL_SUCCESS; +} + +static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, SQLLEN BufferLength, + SQLLEN *StrLen_or_IndPtr) +{ + P("ColumnNumber:[%d]; TargetType:[%d]%s; BufferLength:[%ld]; StrLen_or_IndPtr:[%p]", + ColumnNumber, + TargetType, sql_c_type(TargetType), + BufferLength, StrLen_or_IndPtr); + + OILE(TargetValue, ""); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(STMT_IS_EXECUTED(stmt), ""); + + if (STMT_TYPEINFO(stmt)) { + ONIY(ColumnNumber==3, ""); + ONIY(TargetType==SQL_C_LONG, ""); + int32_t v = 255; + switch (stmt->typeinfo) { + case SQL_VARCHAR: + case SQL_WVARCHAR: + case SQL_VARBINARY: + break; + case SQL_TIMESTAMP: + case SQL_TYPE_TIMESTAMP: + v = 23; + break; + default: + { + OILE(0, ""); + } break; + } + *(int32_t*)TargetValue = v; + return SQL_SUCCESS; + } + + errs_t *errs = &stmt->errs; + + OILE(ColumnNumber>=0, ""); + if (ColumnNumber==0) { + SET_ERR(errs, "07009", "invalid ColumnNumber[%d]", ColumnNumber); + return SQL_ERROR; + } + + col_binding_t binding = { + .ColumnNumber = ColumnNumber, + .TargetType = TargetType, + .TargetValue = TargetValue, + .BufferLength = BufferLength, + .StrLen_or_IndPtr = StrLen_or_IndPtr + }; + + return stmt_get_data(stmt, &binding); + + // ONIY(TargetValue); + + // sql_t *sql = (sql_t*)StatementHandle; + // OILE(sql); + // rs_t *rs = &sql->rs; + // OILE(rs->sql==sql); + + // if (rs->eof) return SQL_NO_DATA; + // OILE(rs->curr_row>=0); + // OILE(rs->n_rows==-1 || rs->curr_rown_rows); + + // field_t *fields = rs->fields; + // if (!fields) return SQL_NO_DATA; + + // row_t *row = &rs->row; + // OILE(row->valid); + + // col_t *cols = row->cols; + + // if (cols==cols_timestamp || cols==cols_varchar || cols==cols_wvarchar || cols==cols_varbinary) { + // ONIY(ColumnNumber==3); + // ONIY(ColumnNumber<=row->n_cols); + // col_t *col = cols + (ColumnNumber-1); + // OILE(col->valid); + // ONIY(col->c_type==TargetType); + // OILE(col->c_type==SQL_C_LONG); + // int32_t v = col->u.c_long; + // ONIY(BufferLength>=sizeof(v)); + // ONIY(StrLen_or_IndPtr==0); + // *(int32_t*)TargetValue = v; + // return SQL_SUCCESS; + // } + + // OILE(StrLen_or_IndPtr); + // *StrLen_or_IndPtr = SQL_NULL_DATA; + + // return SQL_SUCCESS; +} + +static SQLRETURN doSQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, + SQLPOINTER DiagInfo, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength) +{ + P("HandleType:[%d]%s; RecNumber:[%d]; DiagIdentifier:[%d]%s; BufferLength:[%d]", + HandleType, sql_handle_type(HandleType), RecNumber, + DiagIdentifier, sql_diag_identifier(DiagIdentifier), + BufferLength); + OILE(0, ""); + // terror_t *err = NULL; + // switch (HandleType) + // { + // case SQL_HANDLE_ENV: + // { + // env_t *env = (env_t*)Handle; + // err = &env->err; + // } break; + // case SQL_HANDLE_DBC: + // { + // conn_t *conn = (conn_t*)Handle; + // err = &conn->err; + // } break; + // case SQL_HANDLE_STMT: + // { + // sql_t *sql = (sql_t*)Handle; + // err = &sql->err; + // } break; + // default: + // { + // OILE(0); + // return SQL_ERROR; + // } break; + // } + + // OILE(err); + // return SQL_NO_DATA; +} + +static SQLRETURN doSQLGetTypeInfo(SQLHSTMT StatementHandle, SQLSMALLINT DataType) +{ + P("DataType:[%d]%s", DataType, sql_sql_type(DataType)); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(!STMT_IS_EXECUTING(stmt), ""); + stmt_close_rs(stmt); + OILE(STMT_IS_NORM(stmt), ""); + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + switch (DataType) + { + case SQL_VARCHAR: + case SQL_WVARCHAR: + case SQL_VARBINARY: + case SQL_TIMESTAMP: + case SQL_TYPE_TIMESTAMP: + { + stmt->typeinfo = DataType; + } break; + default: + { + ONIY(0, ""); + return SQL_ERROR; + } break; + } + + STMT_SET_EXECUTED(stmt); + + return SQL_SUCCESS; +} + +static SQLRETURN doSQLDescribeParam( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT * DataTypePtr, + SQLULEN * ParameterSizePtr, + SQLSMALLINT * DecimalDigitsPtr, + SQLSMALLINT * NullablePtr) +{ + // SQLRETURN r; + + OILE(ParameterNumber>0, ""); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(stmt->prepared, ""); + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + errs_t *errs = &stmt->errs; + + // const char *enc = conn->enc_char; + + paramset_t *paramset = &stmt->paramset; + param_t *params = paramset->params; + int n_params = paramset->n_params; + + if (ParameterNumber>n_params) { + SET_ERR(errs, "07009", "invalid ParameterNumber/#params [%d/%d]", ParameterNumber, n_params); + return SQL_ERROR; + } + + param_t *param = params + (ParameterNumber-1); + if (DataTypePtr) *DataTypePtr = param->DataType; + if (ParameterSizePtr) *ParameterSizePtr = param->ParameterSize; + if (DecimalDigitsPtr) *DecimalDigitsPtr = param->DecimalDigits; + if (NullablePtr) *NullablePtr = param->Nullable; + + return SQL_SUCCESS; + + // sql_t *sql = (sql_t*)StatementHandle; + // rs_t *rs = &sql->rs; + // OILE(rs->sql==NULL); + // OILE(IS_PREPARED(sql)); + // OILE(DataTypePtr); + // OILE(ParameterSizePtr); + // OILE(DecimalDigitsPtr); + + // paramset_t *paramset = &sql->paramset; + // OILE(paramset->sql); + // OILE(ParameterNumber>0 && ParameterNumber<=paramset->n_params); + + // *DataTypePtr = SQL_C_CHAR; + // *ParameterSizePtr = 23; + // *DecimalDigitsPtr = 0; + // if (NullablePtr) *NullablePtr = SQL_NULLABLE; + + // param_t *param = paramset->params + (ParameterNumber-1); + // int taos_type = param->taos_type; + // switch (taos_type) + // { + // case TSDB_DATA_TYPE_TIMESTAMP: + // { + // *DataTypePtr = SQL_CHAR; + // *ParameterSizePtr = 23; + // *DecimalDigitsPtr = 0; + // if (NullablePtr) *NullablePtr = SQL_NULLABLE; + // } break; + // case TSDB_DATA_TYPE_TINYINT: + // { + // *DataTypePtr = SQL_TINYINT; + // *ParameterSizePtr = 1; + // *DecimalDigitsPtr = 0; + // if (NullablePtr) *NullablePtr = SQL_NULLABLE; + // } break; + // default: + // { + // OA(0, "taos param:[%d][%d]%s", ParameterNumber, taos_type, taos_data_type(taos_type)); + // return SQL_ERROR; + // } break; + // } + + // P("ParameterNumber:[%d]; DataTypePtr:[%p]%s; ParameterSizePtr:[%p]%ld; DecimalDigitsPtr:[%p]%d; NullablePtr:[%p]%d", + // ParameterNumber, + // DataTypePtr, DataTypePtr ? sql_sql_type(*DataTypePtr) : "UNKNOWN", + // ParameterSizePtr, ParameterSizePtr ? *ParameterSizePtr : 0, + // DecimalDigitsPtr, DecimalDigitsPtr ? *DecimalDigitsPtr : 0, + // NullablePtr, NullablePtr ? *NullablePtr : 0); + // return SQL_SUCCESS; +} + +static SQLRETURN doSQLFreeConnect(SQLHDBC ConnectionHandle) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + OILE(conn->stmts.count==0, ""); + conn_free(conn); + return SQL_SUCCESS; +} + +static SQLRETURN doSQLFreeEnv(SQLHENV EnvironmentHandle) +{ + env_t *env = (env_t*)EnvironmentHandle; + env_dec_ref(env); + return SQL_SUCCESS; +} + +static SQLRETURN doSQLBindCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, + SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr) +{ + P("ColumnNumber:[%d]; TargetType:[%d]%s; BufferLength:[%ld]", + ColumnNumber, TargetType, sql_c_type(TargetType), BufferLength); + + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + OILE(STMT_IS_EXECUTED(stmt), ""); + + errs_t *errs = &stmt->errs; + + OILE(ColumnNumber>=0, ""); + if (ColumnNumber==0) { + SET_ERR(errs, "07009", "invalid ColumnNumber[%d]", ColumnNumber); + return SQL_ERROR; + } + + col_binding_t binding = { + .ColumnNumber = ColumnNumber, + .TargetType = TargetType, + .TargetValue = TargetValue, + .BufferLength = BufferLength, + .StrLen_or_IndPtr = StrLen_or_IndPtr + }; + + return stmt_bind_col(stmt, &binding); + + + // OILE(ColumnNumber>0); + // sql_t *sql = (sql_t*)StatementHandle; + // OILE(!IS_EXECUTING(sql)); + + // rs_t *rs = &sql->rs; + // OILE(rs->fields); + // OILE(rs->n_fields>=ColumnNumber); + + // if (!rs->bcs_cache) { + // rs->bcs_cache = todbc_buf_create(); + // if (!rs->bcs_cache) { + // SET_ERROR(sql, "HY001", ""); + // return SQL_ERROR; + // } + // OILE(rs->bcs==NULL); + // rs->bcs = (bc_t*)todbc_buf_calloc(rs->bcs_cache, (size_t)rs->n_fields, sizeof(*rs->bcs)); + // if (!rs->bcs) { + // SET_ERROR(sql, "HY001", ""); + // return SQL_ERROR; + // } + // } + // OILE(rs->bcs); + + // bc_t *bc = rs->bcs + (ColumnNumber-1); + // bc->ColumnNumber = ColumnNumber; + // bc->TargetType = TargetType; + // bc->TargetValue = TargetValue; + // bc->BufferLength = BufferLength; + // bc->StrLen_or_IndPtr = StrLen_or_IndPtr; + + // return SQL_SUCCESS; +} + +static SQLRETURN doSQLCloseCursor(SQLHSTMT StatementHandle) +{ + stmt_t *stmt = (stmt_t*)StatementHandle; + OILE(stmt, ""); + errs_t *errs = &stmt->errs; + + if (STMT_IS_NORM(stmt)) { + OW("no cursor was opened previously"); + SET_ERR(errs, "24000", ""); + return SQL_ERROR; + } + + OILE(STMT_IS_EXECUTED(stmt), ""); + stmt_close_rs(stmt); + + return SQL_SUCCESS; + + // sql_t *sql = (sql_t*)StatementHandle; + // OILE(!IS_EXECUTING(sql)); + + // rs_t *rs = &sql->rs; + // if (!rs->sql) { + // SET_ERROR(sql, "24000", ""); + // OW("no cursor was opened previously"); + // return SQL_ERROR; + // } + + // OILE(rs->sql==sql); + // rs_close(rs); + + // return SQL_SUCCESS; +} + +static SQLRETURN doSQLError(SQLHENV EnvironmentHandle, + SQLHDBC ConnectionHandle, SQLHSTMT StatementHandle, + SQLCHAR *Sqlstate, SQLINTEGER *NativeError, + SQLCHAR *MessageText, SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength) +{ + env_t *env = (env_t*)EnvironmentHandle; + conn_t *conn = (conn_t*)ConnectionHandle; + stmt_t *stmt = (stmt_t*)StatementHandle; + OD("env/conn/stmt:[%p/%p/%p]", env, conn, stmt); + + SQLSMALLINT HandleType; + SQLHANDLE Handle; + if (stmt) { + HandleType = SQL_HANDLE_STMT; + Handle = StatementHandle; + } else if (conn) { + HandleType = SQL_HANDLE_DBC; + Handle = ConnectionHandle; + } else if (env) { + HandleType = SQL_HANDLE_ENV; + Handle = EnvironmentHandle; + } else { + return SQL_NO_DATA; + } + + return doSQLGetDiagRec(HandleType, Handle, 1, Sqlstate, NativeError, MessageText, BufferLength, TextLength); +} + + +#ifdef _MSC_VER + +#define POST_INSTALLER_ERROR(hwndParent, code, fmt, ...) \ +do { \ + char buf[4096]; \ + snprintf(buf, sizeof(buf), "%s[%d]%s():" fmt "", \ + basename((char*)__FILE__), __LINE__, __func__, \ + ##__VA_ARGS__); \ + SQLPostInstallerError(code, buf); \ + if (hwndParent) { \ + MessageBox(hwndParent, buf, "Error", MB_OK|MB_ICONEXCLAMATION); \ + } \ +} while (0) + +typedef struct kv_s kv_t; +struct kv_s { + char *line; + size_t val; +}; + +static BOOL get_driver_dll_path(HWND hwndParent, char *buf, size_t len) +{ + HMODULE hm = NULL; + + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR) &ConfigDSN, &hm) == 0) + { + int ret = GetLastError(); + POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "GetModuleHandle failed, error = %d\n", ret); + return FALSE; + } + if (GetModuleFileName(hm, buf, (DWORD)len) == 0) + { + int ret = GetLastError(); + POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "GetModuleFileName failed, error = %d\n", ret); + return FALSE; + } + return TRUE; +} + +static BOOL doDSNAdd(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes) +{ + BOOL r = TRUE; + + kv_t *kvs = NULL; + + kv_t dsn = {0}; + char *line = NULL; + + do { + char driver_dll[MAX_PATH + 1]; + r = get_driver_dll_path(hwndParent, driver_dll, sizeof(driver_dll)); + if (!r) break; + + dsn.line = strdup("DSN=TAOS_DEMO"); + if (!dsn.line) { r = FALSE; break; } + + const char *p = lpszAttributes; + int ikvs = 0; + while (p && *p) { + line = strdup(p); + if (!line) { r = FALSE; break; } + char *v = strchr(line, '='); + if (!v) { r = FALSE; break; } + + if (strstr(line, "DSN")==line) { + if (dsn.line) { + free(dsn.line); + dsn.line = NULL; + dsn.val = 0; + } + dsn.line = line; + line = NULL; + } else { + kv_t *t = (kv_t*)realloc(kvs, (ikvs+1)*sizeof(*t)); + if (!t) { r = FALSE; free(line); break; } + t[ikvs].line = line; + *v = '\0'; + if (v) t[ikvs].val = v - line + 1; + line = NULL; + + kvs = t; + ++ikvs; + } + + p += strlen(p) + 1; + } + + if (hwndParent) { + MessageBox(hwndParent, "Please use odbcconf to add DSN for TAOS ODBC Driver", "Warning!", MB_OK|MB_ICONEXCLAMATION); + } + if (!r) break; + + char *v = NULL; + v = strchr(dsn.line, '='); + if (!v) { r = FALSE; break; } + *v = '\0'; + dsn.val = v - dsn.line + 1; + + if ((!dsn.line)) { + if (!r) POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "lack of either DSN or Driver"); + } else { + if (r) r = SQLWritePrivateProfileString("ODBC Data Sources", dsn.line+dsn.val, lpszDriver, "Odbc.ini"); + if (r) r = SQLWritePrivateProfileString(dsn.line+dsn.val, "Driver", driver_dll, "Odbc.ini"); + } + + for (int i=0; r && i + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _base_h_ +#define _base_h_ + +#include "todbc_buf.h" +#include "todbc_iconv.h" +#include "todbc_log.h" + +#include "taos.h" +#include "taoserror.h" + +#include +#include + +typedef struct errs_s errs_t; +typedef struct env_s env_t; +typedef struct conn_s conn_t; +typedef struct stmt_s stmt_t; +typedef struct param_s param_t; +typedef struct field_s field_t; +typedef struct rs_s rs_t; +typedef struct col_s col_t; + +#define GET_REF(obj) atomic_load_64(&obj->refcount) +#define INC_REF(obj) atomic_add_fetch_64(&obj->refcount, 1) +#define DEC_REF(obj) atomic_sub_fetch_64(&obj->refcount, 1) + +// public +#ifdef __GNUC__ + __attribute__((format(printf, 6, 7))) +#endif +SQLRETURN errs_append(errs_t *errs, const char sql_state[6], const char *file, int line, const char *func, const char *fmt, ...); +int errs_count(errs_t *errs); +// 0/-1: ok/no-error +int errs_fetch(errs_t *errs, int idx, const char **sql_state, const char **err_str); +void errs_clear(SQLSMALLINT HandleType, SQLHANDLE InputHandle); + +// err: if <>0, will generate strerror +#define SET_ERR(errs, sql_state, fmt, ...) \ + errs_append(errs, sql_state, __FILE__, __LINE__, __func__, "%s" fmt "", "", ##__VA_ARGS__) + +#define SET_OOM(errs, fmt, ...) SET_ERR(errs, "TD001", "OOM:" fmt, ##__VA_ARGS__) +#define SET_NIY(errs, fmt, ...) SET_ERR(errs, "TDC00", "NIY:" fmt, ##__VA_ARGS__) +#define SET_GENERAL(errs, fmt, ...) SET_ERR(errs, "TD000", "GEN:" fmt, ##__VA_ARGS__) + + +// public +errs_t* env_get_errs(env_t *env); +void env_clr_errs(env_t *env); +void env_inc_ref(env_t *env); +void env_dec_ref(env_t *env); + +// public +errs_t* conn_get_errs(conn_t *conn); +void conn_clr_errs(conn_t *conn); + +// public +errs_t* stmt_get_errs(stmt_t *stmt); +void stmt_clr_errs(stmt_t *stmt); + +#endif // _base_h_ + + diff --git a/src/connector/odbc/src/base/CMakeLists.txt b/src/connector/odbc/src/base/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fa13f3e07737bb6c2a36e5296a49a9f282346e3b --- /dev/null +++ b/src/connector/odbc/src/base/CMakeLists.txt @@ -0,0 +1,10 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(TDengine) + +aux_source_directory(. SRC) +add_library(todbc_base STATIC ${SRC}) + +if (TD_DARWIN) + target_include_directories(todbc_base PRIVATE /usr/local/include) +endif () + diff --git a/src/connector/odbc/src/base/conn.c b/src/connector/odbc/src/base/conn.c new file mode 100644 index 0000000000000000000000000000000000000000..6e0554d88552330e2589eceb419a06d2135b54ce --- /dev/null +++ b/src/connector/odbc/src/base/conn.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "conn.h" + +#include "env.h" + +#include "../todbc_tls.h" + +static void init_encodes(conn_t *conn, env_t *env) { + OILE(conn, ""); + OILE(env, ""); + const char *enc_charset = env->enc_charset; + + snprintf(conn->enc_src, sizeof(conn->enc_src), "%s", UTF8_ENC); // compile-time constant + snprintf(conn->enc_wchar, sizeof(conn->enc_wchar), "%s", UTF16_ENC); // compile-time constant + snprintf(conn->enc_char, sizeof(conn->enc_char), "%s", enc_charset); // runtime default + snprintf(conn->enc_db, sizeof(conn->enc_db), "%s", enc_charset); // runtime default + snprintf(conn->enc_locale, sizeof(conn->enc_locale), "%s", enc_charset); // runtime default + + OD("enc_src:[%s]; enc_wchar:[%s]; enc_char:[%s]; enc_db:[%s]; enc_locale:[%s]", + conn->enc_src, conn->enc_wchar, conn->enc_char, conn->enc_db, conn->enc_locale); +} + +static int conn_init(conn_t *conn, env_t *env) { + OILE(conn, ""); + OILE(env, ""); + errs_t *errs = &env->errs; + + OILE(conn->env==NULL, ""); + + int r = errs_init(&conn->errs); + if (r) return -1; + + init_encodes(conn, env); + if (SQL_SUCCESS!=conn_check_charset(conn, errs)) { + return -1; + } + + conn->env = env; + env_inc_ref(env); + + return 0; +}; + +static void conn_release(conn_t *conn) { + if (!conn) return; + env_t *env = conn->env; + if (!env) return; + + conn->env = NULL; + env_dec_ref(env); +} + +conn_t* conn_new(env_t *env) { + conn_t *conn = (conn_t*)calloc(1, sizeof(*conn)); + if (!conn) return NULL; + + if (conn_init(conn, env)) { + OILE(conn->env==NULL, ""); + free(conn); + return NULL; + } + + return conn; +} + +void conn_free(conn_t *conn) { + if (!conn) return; + + // clean ext stuff + if (conn->ext.free_conn) { + conn->ext.free_conn(conn); + } + + // clean conn stuffs + conn_release(conn); + + free(conn); +} + +static SQLRETURN do_check_charset(errs_t *errs, const char *enc_charset, todbc_enc_t *enc) { + *enc = todbc_tls_iconv_enc(enc_charset); + if (enc->enc[0]=='\0') { + if (errs) { + SET_GENERAL(errs, "unknown charset [%s]", enc_charset); + } + return SQL_ERROR; + } + + return SQL_SUCCESS; +} + +SQLRETURN conn_check_charset(conn_t *conn, errs_t *errs) { + OILE(conn, ""); + + todbc_enc_t enc; + + SQLRETURN r; + r = do_check_charset(errs, conn->enc_char, &enc); + if (r!=SQL_SUCCESS) return r; + r = do_check_charset(errs, conn->enc_db, &enc); + if (r!=SQL_SUCCESS) return r; + r = do_check_charset(errs, conn->enc_locale, &enc); + if (r!=SQL_SUCCESS) return r; + r = do_check_charset(errs, conn->enc_src, &enc); + if (r!=SQL_SUCCESS) return r; + + r = do_check_charset(errs, conn->enc_wchar, &enc); + if (r!=SQL_SUCCESS) return r; + + if (enc.variable_char_size!=-1) { + OE("does not support [%s] for WCHAR", conn->enc_wchar); + if (errs) { + SET_GENERAL(errs, "does not support [%s] for WCHAR", conn->enc_wchar); + } + return SQL_ERROR; + } + if (enc.char_size<=0) { + if (errs) { + SET_GENERAL(errs, "unknown [%s] for WCHAR", conn->enc_wchar); + } + return SQL_ERROR; + } + + conn->wchar_size = (size_t)enc.char_size; + + return SQL_SUCCESS; +} + +int conn_add_stmt(conn_t *conn, stmt_t *stmt) { + OILE(conn, ""); + OILE(stmt, ""); + OILE(stmt->owner==NULL, ""); + OILE(stmt->next==NULL, ""); + OILE(stmt->prev==NULL, ""); + OILE(conn->ext.init_stmt, ""); + + if (conn->ext.init_stmt(stmt)) { + return -1; + } + + stmts_t *owner = &conn->stmts; + + stmt->owner = owner; + + stmt->prev = owner->tail; + if (owner->tail) owner->tail->next = stmt; + else owner->head = stmt; + owner->tail = stmt; + + ++owner->count; + owner->conn = conn; + + return 0; +} + +void conn_del_stmt(conn_t *conn, stmt_t *stmt) { + OILE(conn, ""); + OILE(stmt, ""); + OILE(stmt->owner, ""); + OILE(stmt->owner==&conn->stmts, ""); + OILE(stmt->owner->conn==conn, ""); + + stmts_t *owner = stmt->owner; + + stmt_t *next = stmt->next; + stmt_t *prev = stmt->prev; + + if (next) next->prev = prev; + else owner->tail = prev; + + if (prev) prev->next = next; + else owner->head = next; + + --owner->count; + + stmt->next = NULL; + stmt->prev = NULL; + stmt->owner = NULL; +} + +SQLRETURN conn_connect(conn_t *conn) { + OILE(conn, ""); + OILE(conn->ext.connect, ""); + return conn->ext.connect(conn); +} + +void conn_disconnect(conn_t *conn) { + OILE(conn, ""); + OILE(conn->ext.disconnect, ""); + conn->ext.disconnect(conn); +} + +// public +errs_t* conn_get_errs(conn_t *conn) { + OILE(conn, ""); + + return &conn->errs; +} + +void conn_clr_errs(conn_t *conn) { + if (!conn) return; + + errs_reclaim(&conn->errs); +} + + + + + diff --git a/src/connector/odbc/src/base/conn.h b/src/connector/odbc/src/base/conn.h new file mode 100644 index 0000000000000000000000000000000000000000..d82cbc37d4bb6a5a4c0f82e13396fa0f5342147b --- /dev/null +++ b/src/connector/odbc/src/base/conn.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _conn_h_ +#define _conn_h_ + +#include "../base.h" + +#include "stmt.h" + + +#include "../todbc_flex.h" + +typedef struct conn_ext_s conn_ext_t; +struct conn_ext_s { + void *ext; + void (*free_conn)(conn_t *conn); + int (*init_stmt)(stmt_t *stmt); + + SQLRETURN (*connect)(conn_t *conn); + void (*disconnect)(conn_t *conn); +}; + +struct conn_s { + env_t *env; + + char enc_src[64]; // c source file encoding + char enc_char[64]; // SQL_CHAR encoding + char enc_wchar[64]; // SQL_WCHAR encoding + char enc_db[64]; // taos client encoding + // use this for system i/o, such as reading from stdin, writing to stdout/stderr + char enc_locale[64]; // default: current localee + + size_t wchar_size; // shall be fix-length + + conn_val_t val; + + stmts_t stmts; + + errs_t errs; + + conn_ext_t ext; + + unsigned int connect:2; +}; + +#define CONN_SET_CONNECTING(conn) (conn->connect=0x01) +#define CONN_SET_CONNECTED(conn) (conn->connect=0x02) +#define CONN_SET_NORM(conn) (conn->connect=0x00) + +#define CONN_IS_CONNECTING(conn) (conn->connect==0x01) +#define CONN_IS_CONNECTED(conn) (conn->connect==0x02) +#define CONN_IS_NORM(conn) (conn->connect==0x00) + +conn_t* conn_new(env_t *env); +void conn_free(conn_t *conn); + +SQLRETURN conn_check_charset(conn_t *conn, errs_t *errs); + +int conn_add_stmt(conn_t *conn, stmt_t *stmt); +void conn_del_stmt(conn_t *conn, stmt_t *stmt); + +SQLRETURN conn_connect(conn_t *conn); +void conn_disconnect(conn_t *conn); + + +#endif // _conn_h_ + + diff --git a/src/connector/odbc/src/base/env.c b/src/connector/odbc/src/base/env.c new file mode 100644 index 0000000000000000000000000000000000000000..75b5b8360209f221b69b66e34c58cb7da439affe --- /dev/null +++ b/src/connector/odbc/src/base/env.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "env.h" + +#include + +static pthread_once_t init_once = PTHREAD_ONCE_INIT; +static char default_charset[64] = {0}; + +static void init_routine(void) { + OD("compiled with ODBCVER:[0x%04x]", ODBCVER); + + const char *charset = NULL; + setlocale(LC_ALL, ""); + const char *locale = setlocale(LC_CTYPE, NULL); + if (locale) { + const char *dot = strrchr(locale, '.'); + if (dot) charset = dot + 1; + } + if (!charset) { +#ifdef _MSC_VER + charset = "CP936"; +#else + charset = "UTF-8"; +#endif + OD("failed to find original locale, fall back to [%s]", charset); + } else { +#ifdef _MSC_VER + char buf[64]; + snprintf(buf, sizeof(buf), "CP%s", charset); + charset = buf; +#endif + OD("system default charset: [%s]", charset); + } + + snprintf(default_charset, sizeof(default_charset), "%s", charset); +} + + +static void env_release(env_t *env) { + if (!env) return; + OILE(env->refcount==0, ""); + + env_clr_errs(env); +} + +int env_init(env_t *env) { + OILE(env, ""); + OILE(env->refcount==0, ""); + + pthread_once(&init_once, init_routine); + + int r = errs_init(&env->errs); + if (r) return -1; + + snprintf(env->enc_charset, sizeof(env->enc_charset), "%s", default_charset); + env->odbc_ver = SQL_OV_ODBC3; + + env->refcount = 1; + + return 0; +} + +// public +errs_t* env_get_errs(env_t *env) { + OILE(env, ""); + + return &env->errs; +} + +void env_clr_errs(env_t *env) { + if (!env) return; + + errs_reclaim(&env->errs); +} + +void env_inc_ref(env_t *env) { + OILE(env, ""); + int64_t rc = INC_REF(env); + OILE(rc>=2, ""); +} + +void env_dec_ref(env_t *env) { + OILE(env, ""); + int64_t rc = DEC_REF(env); + if (rc>0) return; + OILE(rc==0, ""); + + env_release(env); + free(env); +} + +env_t* env_create(void) { + env_t *env = (env_t*)calloc(1, sizeof(*env)); + if (!env) return NULL; + + if (env_init(env)) { + free(env); + return NULL; + } + + return env; +} + diff --git a/src/connector/odbc/src/base/env.h b/src/connector/odbc/src/base/env.h new file mode 100644 index 0000000000000000000000000000000000000000..b4f420ad64d541fac92c8a4105e1d89b1cd0e9a0 --- /dev/null +++ b/src/connector/odbc/src/base/env.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _env_h_ +#define _env_h_ + +#include "../base.h" + +#include "err.h" + +struct env_s { + int64_t refcount; + + char enc_charset[64]; // default charset from system locale + int32_t odbc_ver; // default SQL_OV_ODBC3 + + errs_t errs; + + void (*env_free)(env_t* env); +}; + +int env_init(env_t *env); + +env_t* env_create(void); + + + + +#endif // _env_h_ + + diff --git a/src/connector/odbc/src/base/err.c b/src/connector/odbc/src/base/err.c new file mode 100644 index 0000000000000000000000000000000000000000..c1547288076fa5c4e53099560ae211ce14f61b65 --- /dev/null +++ b/src/connector/odbc/src/base/err.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "err.h" + +#include "env.h" +#include "conn.h" +#include "stmt.h" + +struct err_s { + char sql_state[6]; + char *err_str; // no ownership +}; + +static void errs_clr(errs_t *errs) { + if (!errs) return; + + errs->count = 0; + errs->errs = NULL; +} + +int errs_init(errs_t *errs) { + OILE(errs && errs->cache==NULL, ""); + OILE(errs->count==0 && errs->errs==NULL, ""); + + errs->cache = todbc_buf_create(); + + return errs->cache ? 0 : -1; +} + +void errs_reclaim(errs_t *errs) { + if (!errs) return; + if (!errs->cache) return; + + errs_clr(errs); + + todbc_buf_reclaim(errs->cache); +} + +void errs_release(errs_t *errs) { + if (!errs) return; + if (!errs->cache) return; + + errs_clr(errs); + + todbc_buf_free(errs->cache); + errs->cache = NULL; +} + +// public +#ifdef __GNUC__ + __attribute__((format(printf, 6, 7))) +#endif +SQLRETURN errs_append(errs_t *errs, const char sql_state[6], const char *file, int line, const char *func, const char *fmt, ...) +{ + OILE(errs, ""); + OILE(errs->cache, ""); + todbc_buf_t *cache = errs->cache; + + const char *name = basename((char*)file); + + char *buf = NULL; + size_t blen = 0; + while (1) { + char *p = buf; + size_t bytes = blen; + + int count = 0; + int n = 0; + + va_list ap; + va_start(ap, fmt); + if (bytes<0) bytes = 0; + n = vsnprintf(p, bytes, fmt, ap); + va_end(ap); + + OILE(n>=0, ""); + + count += n; + if (p) p += n; + if (bytes) bytes -= (size_t)n; + + if (bytes<0) bytes = 0; + n = snprintf(p, bytes, "@%s[%d]%s()\n", name, line, func); + + OILE(n>=0, ""); + count += n; + + if (p) break; + + buf = todbc_buf_alloc(cache, (size_t)count + 1); + if (!buf) return SQL_ERROR; + blen = (size_t)count; + } + + size_t bytes = (size_t)(errs->count + 1) * sizeof(err_t); + + err_t *es = (err_t*)todbc_buf_realloc(cache, errs->errs, bytes); + if (!es) return SQL_ERROR; + errs->errs = es; + errs->count += 1; + + err_t *err = errs->errs + errs->count - 1; + snprintf(err->sql_state, sizeof(err->sql_state), "%s", sql_state); + err->err_str = buf; + + + return SQL_SUCCESS; +} + +int errs_count(errs_t *errs) { + OILE(errs, ""); + OILE(errs->cache, ""); + + return errs->count; +} + +// 0/-1: ok/no-error +int errs_fetch(errs_t *errs, int idx, const char **sql_state, const char **err_str) { + OILE(errs, ""); + OILE(errs->cache, ""); + + if (errs->count<=0) return -1; + if (idx<0 || idx>=errs->count) return -1; + + err_t *err = errs->errs + idx; + + if (sql_state) *sql_state = err->sql_state; + if (err_str) *err_str = err->err_str; + + return 0; +} + +void errs_clear(SQLSMALLINT HandleType, SQLHANDLE InputHandle) { + errs_t *errs = NULL; + + if (InputHandle==NULL) return; + + switch (HandleType) + { + case SQL_HANDLE_ENV: + { + env_t *env = (env_t*)InputHandle; + errs = &env->errs; + } break; + case SQL_HANDLE_DBC: + { + conn_t *conn = (conn_t*)InputHandle; + errs = &conn->errs; + } break; + case SQL_HANDLE_STMT: + { + stmt_t *stmt = (stmt_t*)InputHandle; + errs = &stmt->errs; + } break; + default: + { + ONIY(0, ""); + } break; + } + + if (!errs) return; + errs_reclaim(errs); +} + diff --git a/src/connector/odbc/src/base/err.h b/src/connector/odbc/src/base/err.h new file mode 100644 index 0000000000000000000000000000000000000000..ae8f9a8085e90acd4f614cdccbfe04f4709ae510 --- /dev/null +++ b/src/connector/odbc/src/base/err.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _err_h_ +#define _err_h_ + +#include "../base.h" + +typedef struct err_s err_t; + +struct errs_s { + todbc_buf_t *cache; + + int count; + err_t *errs; +}; + +int errs_init(errs_t *errs); +void errs_reclaim(errs_t *errs); +void errs_release(errs_t *errs); + +#endif // _err_h_ + diff --git a/src/connector/odbc/src/base/field.c b/src/connector/odbc/src/base/field.c new file mode 100644 index 0000000000000000000000000000000000000000..41a466069fb8f40252c923ac78e3bdfcd864c425 --- /dev/null +++ b/src/connector/odbc/src/base/field.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "field.h" + +static void fieldset_clr_fields(fieldset_t *fieldset) { + if (!fieldset) return; + + fieldset->fields = NULL; + fieldset->n_fields = 0; +} + +static void fieldset_clr_bindings(fieldset_t *fieldset) { + if (!fieldset) return; + + fieldset->bindings = NULL; + fieldset->n_bindings = 0; +} + +void fieldset_init_fields(fieldset_t *fieldset) { + if (!fieldset) return; + if (fieldset->fields_cache) return; + fieldset->fields_cache = todbc_buf_create(); +} + +void fieldset_init_bindings(fieldset_t *fieldset) { + if (!fieldset) return; + if (fieldset->bindings_cache) return; + fieldset->bindings_cache = todbc_buf_create(); +} + +void fieldset_reclaim_fields(fieldset_t *fieldset) { + if (!fieldset) return; + + if (!fieldset->fields_cache) return; + + todbc_buf_reclaim(fieldset->fields_cache); + fieldset_clr_fields(fieldset); +} + +void fieldset_reclaim_bindings(fieldset_t *fieldset) { + if (!fieldset) return; + + if (!fieldset->bindings_cache) return; + + todbc_buf_reclaim(fieldset->bindings_cache); + fieldset_clr_bindings(fieldset); +} + +void fieldset_release(fieldset_t *fieldset) { + if (!fieldset) return; + + fieldset_reclaim_fields(fieldset); + fieldset_reclaim_bindings(fieldset); + + if (fieldset->fields_cache) { + todbc_buf_free(fieldset->fields_cache); + fieldset->fields_cache = NULL; + } + + if (fieldset->bindings_cache) { + todbc_buf_free(fieldset->bindings_cache); + fieldset->bindings_cache = NULL; + } +} + diff --git a/src/connector/odbc/src/base/field.h b/src/connector/odbc/src/base/field.h new file mode 100644 index 0000000000000000000000000000000000000000..667dc65ff66e8095e97804ad412adbbd88ed8969 --- /dev/null +++ b/src/connector/odbc/src/base/field.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _field_h_ +#define _field_h_ + +#include "../base.h" + +typedef struct fieldset_s fieldset_t; +typedef struct col_binding_s col_binding_t; +typedef struct field_arg_s field_arg_t; + +// SQLDescribeCol +struct field_arg_s { + SQLUSMALLINT ColumnNumber; + SQLCHAR *ColumnName; + SQLSMALLINT BufferLength; + SQLSMALLINT *NameLength; + SQLSMALLINT *DataType; // sql data type + SQLULEN *ColumnSize; + SQLSMALLINT *DecimalDigits; + SQLSMALLINT *Nullable; +}; + +struct field_s { + SQLUSMALLINT ColumnNumber; + SQLCHAR ColumnName[64]; // hard-coded + SQLSMALLINT DataType; // sql data type + SQLULEN ColumnSize; + SQLSMALLINT DecimalDigits; + SQLSMALLINT Nullable; +}; + +// SQLBindCol; SQLGetData +struct col_binding_s { + SQLUSMALLINT ColumnNumber; + SQLSMALLINT TargetType; // sql c data type + SQLPOINTER TargetValue; + SQLLEN BufferLength; + SQLLEN *StrLen_or_IndPtr; +}; + + +struct fieldset_s { + todbc_buf_t *fields_cache; + field_t *fields; + int n_fields; + + todbc_buf_t *bindings_cache; + col_binding_t *bindings; + int n_bindings; +}; + +void fieldset_init_fields(fieldset_t *fieldset); +void fieldset_init_bindings(fieldset_t *fieldset); + +void fieldset_reclaim_fields(fieldset_t *fieldset); +void fieldset_reclaim_bindings(fieldset_t *fieldset); + +void fieldset_release(fieldset_t *fields); + +#endif // _field_h_ + + + diff --git a/src/connector/odbc/src/base/null_conn.c b/src/connector/odbc/src/base/null_conn.c new file mode 100644 index 0000000000000000000000000000000000000000..29fe86991ac43095bd843a8df961ee2c443ade11 --- /dev/null +++ b/src/connector/odbc/src/base/null_conn.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "null_conn.h" + +#include "env.h" + +// null_conn + +static void null_conn_free(conn_t *conn) { + OILE(conn, ""); + null_conn_t *null_conn = (null_conn_t*)conn->ext.ext; + OILE(null_conn, ""); + + conn->ext.ext = NULL; + conn->ext.free_conn = NULL; + + free(null_conn); +} + +static SQLRETURN null_conn_connect(conn_t *conn) { + OILE(conn, ""); + null_conn_t *null_conn = (null_conn_t*)conn->ext.ext; + OILE(null_conn && null_conn->conn==conn, ""); + OILE(CONN_IS_NORM(conn), ""); + return SQL_SUCCESS; +} + +static void null_conn_disconnect(conn_t *conn) { + OILE(conn, ""); + OILE(CONN_IS_CONNECTED(conn), ""); +} + +static void null_conn_free_stmt(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); +} + +static SQLRETURN null_conn_exec_direct(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + OILE(!STMT_IS_EXECUTED(stmt), ""); + STMT_SET_EXECUTED(stmt); + return SQL_SUCCESS; +} + +static SQLRETURN null_conn_prepare(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + return SQL_SUCCESS; +} + +static SQLRETURN null_conn_proc_param(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + return SQL_SUCCESS; +} + +static SQLRETURN null_conn_execute(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + OILE(!STMT_IS_EXECUTED(stmt), ""); + STMT_SET_EXECUTED(stmt); + return SQL_SUCCESS; +} + +static int null_conn_init_stmt(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->ext.ext==NULL, ""); + OILE(stmt->ext.free_stmt==NULL, ""); + + OILE(stmt->owner==NULL, ""); + + stmt->ext.ext = NULL; + stmt->ext.free_stmt = null_conn_free_stmt; + stmt->ext.exec_direct = null_conn_exec_direct; + stmt->ext.prepare = null_conn_prepare; + stmt->ext.proc_param = null_conn_proc_param; + stmt->ext.execute = null_conn_execute; + + return 0; +} + +int conn_init_null_conn(conn_t *conn) { + OILE(conn, ""); + OILE(conn->ext.ext==NULL, ""); + OILE(conn->ext.free_conn==NULL, ""); + + null_conn_t *null_conn = (null_conn_t*)calloc(1, sizeof(*null_conn)); + if (!null_conn) return -1; + + null_conn->conn = conn; + conn->ext.ext = null_conn; + conn->ext.free_conn = null_conn_free; + conn->ext.connect = null_conn_connect; + conn->ext.disconnect = null_conn_disconnect; + conn->ext.init_stmt = null_conn_init_stmt; + + return 0; +} + + + diff --git a/src/connector/odbc/src/base/null_conn.h b/src/connector/odbc/src/base/null_conn.h new file mode 100644 index 0000000000000000000000000000000000000000..9940cda5848a9e9dbccf866853e154b7e5868d6b --- /dev/null +++ b/src/connector/odbc/src/base/null_conn.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _null_conn_h_ +#define _null_conn_h_ + +#include "../base.h" + +#include "conn.h" + +typedef struct null_conn_s null_conn_t; +struct null_conn_s { + conn_t *conn; +}; + +int conn_init_null_conn(conn_t *conn); + + +#endif // _null_conn_h_ + + diff --git a/src/connector/odbc/src/base/param.c b/src/connector/odbc/src/base/param.c new file mode 100644 index 0000000000000000000000000000000000000000..59b2871f6ed9324e07c5fb23f20b6bcde734644d --- /dev/null +++ b/src/connector/odbc/src/base/param.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "param.h" + +static void paramset_clr_params(paramset_t *paramset) { + if (!paramset) return; + + paramset->params = NULL; + paramset->n_params = 0; + + paramset->i_row = 0; + paramset->i_col = 0; +} + +static void paramset_clr_bindings(paramset_t *paramset) { + if (!paramset) return; + + paramset->bindings = NULL; + paramset->n_bindings = 0; +} + +void paramset_reclaim_params(paramset_t *paramset) { + if (!paramset) return; + + if (!paramset->params_cache) return; + + todbc_buf_reclaim(paramset->params_cache); + paramset_clr_params(paramset); +} + +void paramset_reclaim_bindings(paramset_t *paramset) { + if (!paramset) return; + + if (!paramset->bindings_cache) return; + + todbc_buf_reclaim(paramset->bindings_cache); + paramset_clr_bindings(paramset); +} + +void paramset_init_params_cache(paramset_t *paramset) { + if (!paramset) return; + if (paramset->params_cache) return; + + OILE(paramset->params==NULL, ""); + OILE(paramset->n_params==0, ""); + + paramset->params_cache = todbc_buf_create(); +} + +void paramset_init_bindings_cache(paramset_t *paramset) { + if (!paramset) return; + if (paramset->bindings_cache) return; + + OILE(paramset->bindings==NULL, ""); + OILE(paramset->n_bindings==0, ""); + + paramset->bindings_cache = todbc_buf_create(); +} + +void paramset_release(paramset_t *paramset) { + if (!paramset) return; + + paramset_reclaim_params(paramset); + paramset_reclaim_bindings(paramset); + + if (paramset->params_cache) { + todbc_buf_free(paramset->params_cache); + paramset->params_cache = NULL; + } + + if (paramset->bindings_cache) { + todbc_buf_free(paramset->bindings_cache); + paramset->bindings_cache = NULL; + } +} + diff --git a/src/connector/odbc/src/base/param.h b/src/connector/odbc/src/base/param.h new file mode 100644 index 0000000000000000000000000000000000000000..6175add47b706e927329c78111232aa9617f9b08 --- /dev/null +++ b/src/connector/odbc/src/base/param.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _param_h_ +#define _param_h_ + +#include "../base.h" + +typedef struct paramset_s paramset_t; +typedef struct param_binding_s param_binding_t; +typedef struct param_val_s param_val_t; + +// SQLDescribeParam +struct param_s { + SQLUSMALLINT ParameterNumber; + SQLSMALLINT DataType; // sql data type + SQLULEN ParameterSize; + SQLSMALLINT DecimalDigits; + SQLSMALLINT Nullable; +}; + +// SQLBindParameter +struct param_binding_s { + SQLUSMALLINT ParameterNumber; + SQLSMALLINT InputOutputType; + SQLSMALLINT ValueType; // sql c data type + SQLSMALLINT ParameterType; // sql data type + SQLULEN ColumnSize; + SQLSMALLINT DecimalDigits; + SQLPOINTER ParameterValuePtr; + SQLLEN BufferLength; + SQLLEN *StrLen_or_IndPtr; +}; + +struct paramset_s { + todbc_buf_t *params_cache; + param_t *params; + int n_params; + + todbc_buf_t *bindings_cache; + param_binding_t *bindings; + int n_bindings; + + int i_row; + int i_col; +}; + +void paramset_reclaim_params(paramset_t *paramset); +void paramset_reclaim_bindings(paramset_t *paramset); + +void paramset_init_params_cache(paramset_t *paramset); +void paramset_init_bindings_cache(paramset_t *paramset); + +void paramset_release(paramset_t *paramset); + +#endif // _param_h_ + + + diff --git a/src/connector/odbc/src/base/rs.c b/src/connector/odbc/src/base/rs.c new file mode 100644 index 0000000000000000000000000000000000000000..068cdec4fd00d8e00ee5976ad2a54feb1da10f12 --- /dev/null +++ b/src/connector/odbc/src/base/rs.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "rs.h" + + diff --git a/src/connector/odbc/src/base/rs.h b/src/connector/odbc/src/base/rs.h new file mode 100644 index 0000000000000000000000000000000000000000..6a7e0a2b3ac73047456260bce915278fe473fb5b --- /dev/null +++ b/src/connector/odbc/src/base/rs.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _rs_h_ +#define _rs_h_ + +#include "../base.h" + + +struct rs_s { + int affected_rows; +}; + + + +#endif // _rs_h_ + diff --git a/src/connector/odbc/src/base/stmt.c b/src/connector/odbc/src/base/stmt.c new file mode 100644 index 0000000000000000000000000000000000000000..2818b46ed2dbb11594b6c6cb63c61198c558e05f --- /dev/null +++ b/src/connector/odbc/src/base/stmt.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "stmt.h" + +#include "conn.h" +#include "param.h" + +static int static_app_row; // SQL_ATTR_APP_ROW_DESC +static int static_app_param; // SQL_ATTR_APP_PARAM_DESC +static int static_imp_row; // SQL_ATTR_IMP_ROW_DESC +static int static_imp_param; // SQL_ATTR_IMP_PARAM_DESC + +static int stmt_init_descs(stmt_t *stmt) { + OILE(stmt, ""); + descs_t *descs = &stmt->descs; + + descs->app_row = &static_app_row; // SQL_ATTR_APP_ROW_DESC + descs->app_param = &static_app_param; // SQL_ATTR_APP_PARAM_DESC + descs->imp_row = &static_imp_row; // SQL_ATTR_IMP_ROW_DESC + descs->imp_param = &static_imp_param; // SQL_ATTR_IMP_PARAM_DESC + + return 0; +} + +static int stmt_init_sql(stmt_t *stmt) { + OILE(stmt, ""); + sql_t *sql = &stmt->sql; + OILE(sql->cache==NULL, ""); + OILE(sql->txt.buf==NULL, ""); + OILE(sql->txt.bytes==0, ""); + OILE(sql->txt.total_bytes==0, ""); + + sql->cache = todbc_buf_create(); + if (!sql->cache) return -1; + + return 0; +} + +static void stmt_release_sql(stmt_t *stmt) { + OILE(stmt, ""); + sql_t *sql = &stmt->sql; + if (!sql->cache) return; + todbc_buf_free(sql->cache); + sql->cache = NULL; + sql->txt.buf = NULL; + sql->txt.bytes = 0; + sql->txt.total_bytes = 0; +} + +static int stmt_init(stmt_t *stmt, conn_t *conn) { + OILE(stmt, ""); + OILE(conn, ""); + OILE(stmt->owner==NULL, ""); + + stmt->typeinfo = SQL_UNKNOWN_TYPE; + + int r = errs_init(&stmt->errs); + if (r) return -1; + + r = stmt_init_descs(stmt); + if (r) return -1; + + r = stmt_init_sql(stmt); + if (r) return -1; + + stmt->attr.bind_type = SQL_PARAM_BIND_BY_COLUMN; + + r = conn_add_stmt(conn, stmt); + OILE(r==0, ""); + OILE(stmt->owner && stmt->owner->conn==conn, ""); + + return 0; +} + +static void stmt_release(stmt_t *stmt) { + if (!stmt) return; + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + + conn_del_stmt(conn, stmt); + + paramset_release(&stmt->paramset); + fieldset_release(&stmt->fieldset); + stmt_release_sql(stmt); + errs_release(&stmt->errs); +} + +static SQLRETURN do_process_param(stmt_t *stmt) { + OILE(stmt->ext.proc_param, ""); + return stmt->ext.proc_param(stmt); +} + +static SQLRETURN do_process_param_row(stmt_t *stmt) { + paramset_t *paramset = &stmt->paramset; + int n_params = paramset->n_params; + + SQLRETURN r = SQL_SUCCESS; + + paramset->i_col = 0; + for (; paramset->i_coli_col) { + r = do_process_param(stmt); + if (r!=SQL_SUCCESS) return r; + } + + return SQL_SUCCESS; +} + +stmt_t* stmt_new(conn_t *conn) { + stmt_t *stmt = (stmt_t*)calloc(1, sizeof(*stmt)); + if (!stmt) return NULL; + + if (stmt_init(stmt, conn)) { + free(stmt); + return NULL; + } + + return stmt; +} + +void stmt_free(stmt_t *stmt) { + if (!stmt) return; + + // clean ext stuff + if (stmt->ext.free_stmt) { + stmt->ext.free_stmt(stmt); + stmt->ext.free_stmt = NULL; + } + + // clean stmt stuff + stmt_release(stmt); + + free(stmt); +} + +conn_t* stmt_get_conn(stmt_t *stmt) { + if (!stmt) return NULL; + if (!stmt->owner) return NULL; + + return stmt->owner->conn; +} + +void stmt_reclaim_params(stmt_t *stmt) { + if (!stmt) return; + paramset_reclaim_params(&stmt->paramset); +} + +void stmt_reclaim_param_bindings(stmt_t *stmt) { + if (!stmt) return; + paramset_reclaim_bindings(&stmt->paramset); +} + +void stmt_reclaim_field_bindings(stmt_t *stmt) { + if (!stmt) return; + fieldset_reclaim_bindings(&stmt->fieldset); +} + +void stmt_close_rs(stmt_t *stmt) { + if (!stmt) return; + + OILE(stmt->ext.close_rs, ""); + stmt->ext.close_rs(stmt); + + stmt->typeinfo = SQL_UNKNOWN_TYPE; + + stmt->eof = 0; + + fieldset_reclaim_fields(&stmt->fieldset); + // for the performance + // we don't reclaim field-binds here + // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindcol-function?view=sql-server-ver15 + STMT_SET_NORM(stmt); +} + +void stmt_reset_params(stmt_t *stmt) { + if (!stmt) return; + + stmt->attr.paramset_size = 0; + stmt_reclaim_param_bindings(stmt); +} + +static SQLRETURN stmt_set_sql(stmt_t *stmt, todbc_string_t *txt) { + OILE(stmt, ""); + OILE(txt, ""); + errs_t *errs = &stmt->errs; + sql_t *sql = &stmt->sql; + + OILE(txt!=&sql->txt, ""); + + todbc_buf_t *cache = sql->cache; + OILE(sql->cache, ""); + + conn_t *conn = stmt_get_conn(stmt); + OILE(conn, ""); + const char *enc = conn->enc_db; + + todbc_buf_reclaim(cache); + + sql->txt = todbc_string_conv_to(txt, enc, cache); + if (!sql->txt.buf) { + SET_OOM(errs, ""); + return SQL_ERROR; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_stmt_prepare(stmt_t *stmt) { + OILE(stmt->ext.prepare, ""); + return stmt->ext.prepare(stmt); +} + +static void do_stmt_clear_param_vals(stmt_t *stmt) { + OILE(stmt->ext.clear_param_vals, ""); + stmt->ext.clear_param_vals(stmt); +} + +static void do_stmt_clear_params(stmt_t *stmt) { + stmt->prepared = 0; + paramset_t *paramset = &stmt->paramset; + paramset_reclaim_params(paramset); + do_stmt_clear_param_vals(stmt); +} + +SQLRETURN stmt_exec_direct(stmt_t *stmt, todbc_string_t *txt) { + OILE(stmt, ""); + OILE(txt, ""); + + SQLRETURN r = stmt_set_sql(stmt, txt); + if (r!=SQL_SUCCESS) return r; + + do_stmt_clear_params(stmt); + + if (stmt->ext.exec_direct) { + r = stmt->ext.exec_direct(stmt); + } else { + r = do_stmt_prepare(stmt); + if (r!=SQL_SUCCESS) return r; + stmt->prepared = 1; + OILE(0, ""); + } + + return r; +} + +SQLRETURN stmt_prepare(stmt_t *stmt, todbc_string_t *txt) { + OILE(stmt, ""); + OILE(txt, ""); + + SQLRETURN r = stmt_set_sql(stmt, txt); + if (r!=SQL_SUCCESS) return r; + + do_stmt_clear_params(stmt); + + r = do_stmt_prepare(stmt); + if (r!=SQL_SUCCESS) return r; + stmt->prepared = 1; + + return SQL_SUCCESS; +} + +SQLRETURN stmt_bind_param(stmt_t *stmt, param_binding_t *arg) { + OILE(stmt, ""); + OILE(arg, ""); + OILE(arg->ParameterNumber>0, ""); + int idx = arg->ParameterNumber - 1; + + errs_t *errs = &stmt->errs; + paramset_t *paramset = &stmt->paramset; + + paramset_init_bindings_cache(paramset); + if (!paramset->bindings_cache) { + SET_OOM(errs, "failed to alloc cache for param binds"); + return SQL_ERROR; + } + + todbc_buf_t *cache = paramset->bindings_cache; + OILE(cache, ""); + + param_binding_t *bindings = paramset->bindings; + if (idx>=paramset->n_bindings) { + size_t num = (size_t)(idx + 1); + // align + const size_t block = 10; + num = (num + block-1) / block * block; + bindings = (param_binding_t*)todbc_buf_realloc(cache, bindings, num * sizeof(*bindings)); + if (!bindings) { + SET_OOM(errs, "failed to realloc buf for param binds"); + return SQL_ERROR; + } + paramset->bindings = bindings; + paramset->n_bindings = idx + 1; + } + OILE(paramset->bindings, ""); + OILE(idxn_bindings, ""); + + param_binding_t *binding = bindings + idx; + *binding = *arg; + + if (paramset->n_bindings>paramset->n_params) return SQL_SUCCESS; + + OILE(stmt->ext.set_param_conv, ""); + return stmt->ext.set_param_conv(stmt, idx); +} + +SQLRETURN stmt_execute(stmt_t *stmt) { + OILE(stmt, ""); + OILE(!STMT_IS_EXECUTING(stmt), ""); + + SQLRETURN r = SQL_SUCCESS; + + if (stmt->prepared==0) { + do_stmt_clear_params(stmt); + + r = do_stmt_prepare(stmt); + if (r!=SQL_SUCCESS) return r; + stmt->prepared = 1; + } + OILE(stmt->prepared==1, ""); + + + errs_t *errs = &stmt->errs; + + paramset_t *paramset = &stmt->paramset; + int n_params = paramset->n_params; + int n_bindings = paramset->n_bindings; + + if (n_params>n_bindings) { + SET_GENERAL(errs, "parameters need to be bound first"); + return SQL_ERROR; + } + + if (n_params>0) { + int paramset_size = (int)stmt->attr.paramset_size; + if (paramset_size==0) paramset_size = 1; + stmt->attr.paramset_size = (SQLULEN)paramset_size; + + paramset->i_row = 0; + for (; paramset->i_rowi_row) { + r = do_process_param_row(stmt); + if (r) return r; + + OILE(stmt->ext.param_row_processed, ""); + r = stmt->ext.param_row_processed(stmt); + if (r) return r; + } + } + + OILE(stmt->ext.execute, ""); + return stmt->ext.execute(stmt); +} + +SQLRETURN stmt_fetch(stmt_t *stmt) { + OILE(stmt, ""); + OILE(STMT_IS_EXECUTED(stmt), ""); + + if (stmt->eof) return SQL_NO_DATA; + + OILE(stmt->ext.fetch, ""); + SQLRETURN r = stmt->ext.fetch(stmt); + if (r!=SQL_SUCCESS) return r; + + fieldset_t *fieldset = &stmt->fieldset; + if (fieldset->n_bindings==0) return SQL_SUCCESS; + OILE(fieldset->n_bindings>0, ""); + + for (size_t i=0; in_bindings; ++i) { + OILE(fieldset->bindings, ""); + col_binding_t *binding = fieldset->bindings + i; + if (binding->ColumnNumber!=i+1) { + OILE(binding->ColumnNumber==0, ""); + continue; + } + OILE(stmt->ext.get_data, ""); + r = stmt->ext.get_data(stmt, binding); + if (r!=SQL_SUCCESS) return r; + } + + return SQL_SUCCESS; +} + +SQLRETURN stmt_get_data(stmt_t *stmt, col_binding_t *binding) { + OILE(stmt, ""); + OILE(STMT_IS_EXECUTED(stmt), ""); + + OILE(stmt->eof==0, ""); + + OILE(stmt->ext.get_data, ""); + return stmt->ext.get_data(stmt, binding); +} + +SQLRETURN stmt_bind_col(stmt_t *stmt, col_binding_t *binding) { + OILE(stmt, ""); + // shall we check execute state? + + errs_t *errs = &stmt->errs; + + fieldset_t *fieldset = &stmt->fieldset; + + todbc_buf_t *cache = fieldset->bindings_cache; + if (cache==NULL) { + fieldset_init_bindings(fieldset); + cache = fieldset->bindings_cache; + if (!cache) { + SET_OOM(errs, ""); + return SQL_ERROR; + } + } + OILE(cache, ""); + + col_binding_t *bindings = fieldset->bindings; + + OILE(binding->ColumnNumber>0, ""); + if (binding->ColumnNumber>=fieldset->n_bindings) { + size_t num = (size_t)binding->ColumnNumber; + const size_t block = 10; + size_t align = (num+block-1)/block*block; + size_t total = align * sizeof(*bindings); + bindings = (col_binding_t*)todbc_buf_realloc(cache, bindings, total); + if (!bindings) { + SET_OOM(errs, ""); + return SQL_ERROR; + } + for (size_t i = (size_t)fieldset->n_bindings; ibindings = bindings; + fieldset->n_bindings = (int)num; + } + OILE(bindings, ""); + OILE(binding->ColumnNumber<=fieldset->n_bindings, ""); + bindings[binding->ColumnNumber-1] = *binding; + + return SQL_SUCCESS; +} + + + + +// public +errs_t* stmt_get_errs(stmt_t *stmt) { + OILE(stmt, ""); + + return &stmt->errs; +} + +void stmt_clr_errs(stmt_t *stmt) { + if (!stmt) return; + + errs_reclaim(&stmt->errs); +} + diff --git a/src/connector/odbc/src/base/stmt.h b/src/connector/odbc/src/base/stmt.h new file mode 100644 index 0000000000000000000000000000000000000000..905cb6a00323446736f130642d0e250e4ba24dd9 --- /dev/null +++ b/src/connector/odbc/src/base/stmt.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _stmt_h_ +#define _stmt_h_ + +#include "../base.h" + +#include "err.h" +#include "field.h" +#include "param.h" +#include "rs.h" + +typedef struct descs_s descs_t; +typedef struct stmts_s stmts_t; +typedef struct stmt_ext_s stmt_ext_t; +typedef struct stmt_attr_s stmt_attr_t; +typedef struct sql_s sql_t; + +struct descs_s { + void *app_row; // SQL_ATTR_APP_ROW_DESC + void *app_param; // SQL_ATTR_APP_PARAM_DESC + void *imp_row; // SQL_ATTR_IMP_ROW_DESC + void *imp_param; // SQL_ATTR_IMP_PARAM_DESC +}; + +struct stmt_ext_s { + void *ext; + void (*free_stmt)(stmt_t *stmt); + + void (*clear_params)(stmt_t *stmt); + void (*clear_param_vals)(stmt_t *stmt); + + SQLRETURN (*get_param)(stmt_t *stmt, param_t *arg); + SQLRETURN (*set_param_conv)(stmt_t *stmt, int idx); + + SQLRETURN (*exec_direct)(stmt_t *stmt); + SQLRETURN (*prepare)(stmt_t *stmt); + SQLRETURN (*proc_param)(stmt_t *stmt); + SQLRETURN (*param_row_processed)(stmt_t *stmt); + SQLRETURN (*execute)(stmt_t *stmt); + SQLRETURN (*get_affected_rows)(stmt_t *stmt, SQLLEN *RowCount); + SQLRETURN (*get_fields_count)(stmt_t *stmt, SQLSMALLINT *ColumnCount); + SQLRETURN (*get_field)(stmt_t *stmt, field_arg_t *arg); + SQLRETURN (*fetch)(stmt_t *stmt); + SQLRETURN (*get_data)(stmt_t *stmt, col_binding_t *col); + void (*close_rs)(stmt_t *stmt); +}; + +struct stmt_attr_s { + // SQL_ATTR_PARAM_BIND_TYPE: SQL_PARAM_BIND_BY_COLUMN or row size + SQLULEN bind_type; // default: SQL_PARAM_BIND_BY_COLUMN + // SQL_ATTR_PARAM_BIND_OFFSET_PTR + SQLULEN *bind_offset_ptr; // default: NULL + // SQL_ATTR_PARAMSET_SIZE + SQLULEN paramset_size; // default: 0 +}; + +struct sql_s { + todbc_buf_t *cache; + todbc_string_t txt; +}; + +struct stmt_s { + stmts_t *owner; + stmt_t *next; + stmt_t *prev; + + SQLSMALLINT typeinfo; + + descs_t descs; + + sql_t sql; + + stmt_attr_t attr; + + paramset_t paramset; + fieldset_t fieldset; + + int affected_rows; + rs_t rs; + + errs_t errs; + + stmt_ext_t ext; + + unsigned int prepared:1; + unsigned int execute:2; + unsigned int eof:1; +}; + +struct stmts_s { + conn_t *conn; + + int count; + stmt_t *head; + stmt_t *tail; +}; + +#define STMT_TYPEINFO(stmt) (stmt->typeinfo!=SQL_UNKNOWN_TYPE) + +#define STMT_SET_EXECUTING(stmt) (stmt->execute=0x01) +#define STMT_SET_EXECUTED(stmt) (stmt->execute=0x02) +#define STMT_SET_NORM(stmt) (stmt->execute=0x00) + +#define STMT_IS_EXECUTING(stmt) (stmt->execute==0x01) +#define STMT_IS_EXECUTED(stmt) (stmt->execute==0x02) +#define STMT_IS_NORM(stmt) (stmt->execute==0x00) + +stmt_t* stmt_new(conn_t *conn); +void stmt_free(stmt_t *stmt); + +conn_t* stmt_get_conn(stmt_t *stmt); + +void stmt_reclaim_params(stmt_t *stmt); +void stmt_reclaim_param_binds(stmt_t *stmt); +void stmt_reclaim_field_binds(stmt_t *stmt); +void stmt_close_rs(stmt_t *stmt); +void stmt_reset_params(stmt_t *stmt); +SQLRETURN stmt_exec_direct(stmt_t *stmt, todbc_string_t *txt); +SQLRETURN stmt_prepare(stmt_t *stmt, todbc_string_t *txt); +SQLRETURN stmt_bind_param(stmt_t *stmt, param_binding_t *arg); +SQLRETURN stmt_execute(stmt_t *stmt); +SQLRETURN stmt_fetch(stmt_t *stmt); +SQLRETURN stmt_get_data(stmt_t *stmt, col_binding_t *binding); +SQLRETURN stmt_bind_col(stmt_t *stmt, col_binding_t *binding); + +#endif // _stmt_h_ + + + diff --git a/src/connector/odbc/src/base/tsdb_impl.c b/src/connector/odbc/src/base/tsdb_impl.c new file mode 100644 index 0000000000000000000000000000000000000000..d913aebd7df55ceef337d48131f7bab634219e1b --- /dev/null +++ b/src/connector/odbc/src/base/tsdb_impl.c @@ -0,0 +1,2598 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "tsdb_impl.h" + +#include "env.h" +#include "../todbc_tls.h" +#include "../todbc_util.h" + +#include "ttype.h" + + +#include + +// tsdb_conn + +typedef struct tsdb_conn_s tsdb_conn_t; +typedef struct tsdb_stmt_s tsdb_stmt_t; +typedef struct tsdb_param_s tsdb_param_t; +typedef struct tsdb_param_val_s tsdb_param_val_t; +typedef struct tsdb_param_conv_arg_s tsdb_param_conv_arg_t; + +struct tsdb_conn_s { + conn_t *conn; + + char svr_info[64]; + char cli_info[64]; + + TAOS *taos; +}; + +struct tsdb_param_s { + int tsdb_type; // TSDB_DATA_TYPE_xxx + int tsdb_bytes; + + SQLRETURN (*conv)(stmt_t *stmt, tsdb_param_conv_arg_t *arg); +}; + +struct tsdb_param_val_s { + SQLUSMALLINT ParameterNumber; + int is_null; +}; + +struct tsdb_stmt_s { + stmt_t *stmt; + + TAOS_STMT *tsdb_stmt; // prepared-statement + TAOS_RES *tsdb_res; + + tsdb_param_t *tsdb_params; + + todbc_buf_t *tsdb_param_vals_cache; + tsdb_param_val_t *tsdb_param_vals; + TAOS_BIND *taos_binds; + + TAOS_FIELD *tsdb_fields; + TAOS_ROW tsdb_curr; + + unsigned int by_query:1; +}; + +struct tsdb_param_conv_arg_s { + conn_t *conn; + todbc_buf_t *cache; + int idx; + SQLPOINTER val; + SQLLEN soi; + tsdb_param_t *tsdb_param; + tsdb_param_val_t *tsdb_param_val; + TAOS_BIND *taos_bind; +}; + +static void tsdb_stmt_init_param_vals_cache(tsdb_stmt_t *tsdb_stmt) { + OILE(tsdb_stmt, ""); + if (tsdb_stmt->tsdb_param_vals_cache) return; + tsdb_stmt->tsdb_param_vals_cache = todbc_buf_create(); +} + +static void tsdb_stmt_reclaim_param_vals(tsdb_stmt_t *tsdb_stmt) { + if (!tsdb_stmt) return; + if (tsdb_stmt->tsdb_param_vals_cache) { + tsdb_stmt->tsdb_param_vals = NULL; + tsdb_stmt->taos_binds = NULL; + todbc_buf_reclaim(tsdb_stmt->tsdb_param_vals_cache); + } + OILE(tsdb_stmt->tsdb_param_vals==NULL, ""); + OILE(tsdb_stmt->taos_binds==NULL, ""); +} + +static void tsdb_stmt_cleanup_param_vals(tsdb_stmt_t *tsdb_stmt) { + if (!tsdb_stmt) return; + tsdb_stmt_reclaim_param_vals(tsdb_stmt); + + if (tsdb_stmt->tsdb_param_vals_cache) { + todbc_buf_free(tsdb_stmt->tsdb_param_vals_cache); + } +} + +static void tsdb_stmt_calloc_param_vals(tsdb_stmt_t *tsdb_stmt) { + OILE(tsdb_stmt, ""); + stmt_t *stmt = tsdb_stmt->stmt; + OILE(stmt, ""); + paramset_t *paramset = &stmt->paramset; + int n_params = paramset->n_params; + OILE(n_params>0, ""); + todbc_buf_t *cache = tsdb_stmt->tsdb_param_vals_cache; + OILE(cache, ""); + OILE(tsdb_stmt->tsdb_param_vals==NULL, ""); + OILE(tsdb_stmt->taos_binds==NULL, ""); + tsdb_stmt->tsdb_param_vals = (tsdb_param_val_t*)todbc_buf_calloc(cache, (size_t)n_params, sizeof(*tsdb_stmt->tsdb_param_vals)); + tsdb_stmt->taos_binds = (TAOS_BIND*)todbc_buf_calloc(cache, (size_t)n_params, sizeof(*tsdb_stmt->taos_binds)); +} + +static SQLRETURN tsdb_stmt_init_stmt(tsdb_stmt_t *tsdb_stmt) { + OILE(tsdb_stmt && tsdb_stmt->stmt, ""); + errs_t *errs = &tsdb_stmt->stmt->errs; + + if (tsdb_stmt->tsdb_stmt) return SQL_SUCCESS; + OILE(tsdb_stmt->stmt->owner, ""); + conn_t *conn = tsdb_stmt->stmt->owner->conn; + OILE(conn, ""); + tsdb_conn_t *tsdb_conn = (tsdb_conn_t*)conn->ext.ext; + OILE(tsdb_conn && tsdb_conn->taos, ""); + tsdb_stmt->tsdb_stmt = taos_stmt_init(tsdb_conn->taos); + if (!tsdb_stmt->tsdb_stmt) { + SET_GENERAL(errs, "failed to init taos stmt"); + return SQL_ERROR; + } + + return SQL_SUCCESS; +} + +static void tsdb_stmt_close_stmt(tsdb_stmt_t *tsdb_stmt) { + if (!tsdb_stmt) return; + if (!tsdb_stmt->tsdb_stmt) return; + + int r = taos_stmt_close(tsdb_stmt->tsdb_stmt); + tsdb_stmt->tsdb_stmt= NULL; + tsdb_stmt->stmt->prepared = 0; + if (r) OD("[%d]%s", r, tstrerror(r)); +} + +static void tsdb_stmt_close_rs(tsdb_stmt_t *tsdb_stmt) { + if (!tsdb_stmt) return; + if (!tsdb_stmt->tsdb_res) return; + + tsdb_stmt->tsdb_curr = NULL; + + if (tsdb_stmt->by_query) { + taos_stop_query(tsdb_stmt->tsdb_res); + } else { + OILE(tsdb_stmt->tsdb_stmt==NULL, ""); + taos_free_result(tsdb_stmt->tsdb_res); + } + tsdb_stmt->tsdb_res = NULL; +} + +static void tsdb_conn_free(conn_t *conn) { + OILE(conn, ""); + + tsdb_conn_t *tsdb_conn = (tsdb_conn_t*)conn->ext.ext; + OILE(tsdb_conn, ""); + OILE(tsdb_conn->taos==NULL, ""); + + conn->ext.ext = NULL; + conn->ext.free_conn = NULL; + + free(tsdb_conn); +} + +static SQLRETURN tsdb_conn_connect(conn_t *conn) { + OILE(conn, ""); + OILE(CONN_IS_NORM(conn), ""); + errs_t *errs = &conn->errs; + tsdb_conn_t *tsdb_conn = (tsdb_conn_t*)conn->ext.ext; + OILE(tsdb_conn, ""); + OILE(tsdb_conn->conn==conn, ""); + + conn_val_t *val = &conn->val; + const char *dsn = val->dsn; + const char *uid = val->uid; + const char *pwd = val->pwd; + const char *db = val->db; + const char *svr = val->server; + + OILE(dsn, ""); + + int use_default = 0; + char server[4096]; server[0] = '\0'; + if (!svr || !svr[0]) { + int n = SQLGetPrivateProfileString(dsn, "Server", "", server, sizeof(server)-1, "Odbc.ini"); + if (n<=0) { + snprintf(server, sizeof(server), DEFAULT_SERVER); + n = (int)strlen(server); + use_default = 1; + } else { + server[n] = '\0'; + } + svr = server; + + if (!svr || !svr[0]) { + SET_GENERAL(errs, "please specify Server entry in connection string or odbc.ini or windows registry for DSN:[%s]", dsn); + return SQL_ERROR; + } + } + + char *ip = NULL; + int port = 0; + char *p = strchr(svr, ':'); + if (p) { + ip = todbc_tls_strndup(svr, (size_t)(p-svr)); + port = atoi(p+1); + } + + tsdb_conn->taos = taos_connect(ip, uid, pwd, db, (uint16_t)port); + if (!tsdb_conn->taos) { + int e = terrno; + const char * es = tstrerror(e); + if (use_default) { + SET_GENERAL(errs, "no Server entry in odbc.ini or windows registry for DSN[%s], fallback to svr[%s:%d] db[%s]", dsn, ip, port, db); + } + SET_GENERAL(errs, "connect to DSN[%s] svr[%s:%d] db[%s] failed", dsn, ip, port, db); + SET_GENERAL(errs, "[%x]%s", e, es); + return SQL_ERROR; + } + + const char *svr_info = taos_get_server_info(tsdb_conn->taos); + const char *cli_info = taos_get_client_info(tsdb_conn->taos); + snprintf(tsdb_conn->svr_info, sizeof(tsdb_conn->svr_info), "%s", svr_info); + snprintf(tsdb_conn->cli_info, sizeof(tsdb_conn->cli_info), "%s", cli_info); + + return SQL_SUCCESS; +} + +static void tsdb_conn_disconnect(conn_t *conn) { + OILE(conn, ""); + OILE(CONN_IS_CONNECTED(conn), ""); + + tsdb_conn_t *tsdb_conn = (tsdb_conn_t*)conn->ext.ext; + OILE(tsdb_conn, ""); + OILE(tsdb_conn->conn==conn, ""); + + TAOS *taos = tsdb_conn->taos; + + if (!taos) return; + taos_close(taos); + taos = NULL; + tsdb_conn->taos = NULL; +} + +static void tsdb_conn_free_stmt(stmt_t *stmt) { + OILE(stmt, ""); + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt && tsdb_stmt->stmt==stmt, ""); + + tsdb_stmt_close_rs(tsdb_stmt); + tsdb_stmt_close_stmt(tsdb_stmt); + tsdb_stmt_cleanup_param_vals(tsdb_stmt); + + tsdb_stmt->tsdb_params = NULL; + + stmt_ext_t ext = {0}; + stmt->ext = ext; + + free(tsdb_stmt); +} + +static void tsdb_conn_clear_param_vals(stmt_t *stmt) { + OILE(stmt, ""); + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt && tsdb_stmt->stmt==stmt, ""); + + tsdb_stmt_reclaim_param_vals(tsdb_stmt); +} + +static SQLRETURN tsdb_conn_exec_direct(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + OILE(!STMT_IS_EXECUTED(stmt), ""); + + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + tsdb_conn_t *tsdb_conn = (tsdb_conn_t*)conn->ext.ext; + OILE(tsdb_conn && tsdb_conn->conn==conn, ""); + TAOS *taos = tsdb_conn->taos; + OILE(taos, ""); + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt && tsdb_stmt->stmt==stmt, ""); + OILE(tsdb_stmt->tsdb_stmt==NULL, ""); + OILE(tsdb_stmt->tsdb_res==NULL, ""); + + errs_t *errs = &stmt->errs; + + tsdb_stmt_reclaim_param_vals(tsdb_stmt); + + const char *txt = (const char*)stmt->sql.txt.buf; + TAOS_RES *tsdb_res = taos_query(taos, txt); + OILE(tsdb_res, ""); + int r = taos_errno(tsdb_res); + if (r) { + SET_GENERAL(errs, "taos query failed:[%d]%s", r, tstrerror(r)); + taos_stop_query(tsdb_res); + STMT_SET_NORM(stmt); + return SQL_ERROR; + } + + tsdb_stmt->tsdb_res = tsdb_res; + tsdb_stmt->by_query = 1; + + STMT_SET_EXECUTED(stmt); + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_int64_to_tsdb_timestamp(stmt_t *stmt, const int64_t v, tsdb_param_conv_arg_t *arg) { + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + taos_bind->u.ts = v; + taos_bind->buffer_length = sizeof(taos_bind->u.ts); + taos_bind->buffer = &taos_bind->u.ts; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_int64_to_tsdb_tinyint(stmt_t *stmt, const int64_t v, tsdb_param_conv_arg_t *arg) { + errs_t *errs = &stmt->errs; + + if (vINT8_MAX) { + SET_GENERAL(errs, "integer overflow for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_TINYINT; + taos_bind->u.v1 = (int8_t)v; + taos_bind->buffer_length = sizeof(taos_bind->u.v1); + taos_bind->buffer = &taos_bind->u.v1; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_int64_to_tsdb_smallint(stmt_t *stmt, const int64_t v, tsdb_param_conv_arg_t *arg) { + errs_t *errs = &stmt->errs; + + if (vINT16_MAX) { + SET_GENERAL(errs, "integer overflow for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_SMALLINT; + taos_bind->u.v2 = (int16_t)v; + taos_bind->buffer_length = sizeof(taos_bind->u.v2); + taos_bind->buffer = &taos_bind->u.v2; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_int64_to_tsdb_int(stmt_t *stmt, const int64_t v, tsdb_param_conv_arg_t *arg) { + errs_t *errs = &stmt->errs; + + if (vINT32_MAX) { + SET_GENERAL(errs, "integer overflow for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_INT; + taos_bind->u.v4 = (int32_t)v; + taos_bind->buffer_length = sizeof(taos_bind->u.v4); + taos_bind->buffer = &taos_bind->u.v4; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_int64_to_tsdb_bigint(stmt_t *stmt, const int64_t v, tsdb_param_conv_arg_t *arg) { + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_BIGINT; + taos_bind->u.v8 = v; + taos_bind->buffer_length = sizeof(taos_bind->u.v8); + taos_bind->buffer = &taos_bind->u.v8; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_double_to_tsdb_float(stmt_t *stmt, const double v, tsdb_param_conv_arg_t *arg) { + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_FLOAT; + taos_bind->u.f4 = (float)v; + taos_bind->buffer_length = sizeof(taos_bind->u.f4); + taos_bind->buffer = &taos_bind->u.f4; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_double_to_tsdb_double(stmt_t *stmt, const double v, tsdb_param_conv_arg_t *arg) { + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_DOUBLE; + taos_bind->u.f8 = v; + taos_bind->buffer_length = sizeof(taos_bind->u.f8); + taos_bind->buffer = &taos_bind->u.f8; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + + +static SQLRETURN do_conv_sql_string_to_int64(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg, int64_t *v) { + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt.buf; + int bytes = 0; + int64_t i64 = 0; + sscanf(buf, "%" PRId64 " %n", &i64, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to integer for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + *v = i64; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_double(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg, double *v) { + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt.buf; + int bytes = 0; + double dbl = 0.0; + sscanf(buf, "%lf%n", &dbl, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to double for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + *v = dbl; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_timestamp(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + char *buf = (char*)txt.buf; + int32_t bytes = (int32_t)txt.bytes; + + int64_t ts = 0; + int r = taosParseTime(buf, &ts, bytes, TSDB_TIME_PRECISION_MILLI, 0); + if (r) { + SET_GENERAL(errs, "failed to parse as timestamp for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + taos_bind->u.ts = ts; + taos_bind->buffer_length = sizeof(taos_bind->u.ts); + taos_bind->buffer = &taos_bind->u.ts; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_nchar(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_db; + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt_db = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt_db.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt_db.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + char *buf = (char*)txt_db.buf; + int32_t bytes = (int32_t)txt_db.bytes; + if (bytes > arg->tsdb_param->tsdb_bytes) { + SET_OOM(errs, "failed to convert from [%s->%s] for param [%d], string too long [%d/%d]", + enc_from, enc_to, arg->idx+1, bytes, arg->tsdb_param->tsdb_bytes); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_NCHAR; + taos_bind->u.nchar = buf; + taos_bind->buffer_length = (uintptr_t)((size_t)bytes); + taos_bind->buffer = taos_bind->u.nchar; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_bool(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + int64_t v = 0; + + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt_db = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt_db.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt_db.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt_db.buf; + int bytes = 0; + if (strcasecmp(buf, "true")==0) { + v = 1; + } else if (strcasecmp(buf, "false")==0) { + v = 0; + } else { + sscanf(buf, "%" PRId64 " %n", &v, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to integer for param [%d]", arg->idx+1); + return SQL_ERROR; + } + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_BOOL; + taos_bind->u.b = v ? 1 : 0; + taos_bind->buffer_length = sizeof(taos_bind->u.b); + taos_bind->buffer = &taos_bind->u.b; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_tinyint(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + int64_t v = 0; + + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt_db = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt_db.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt_db.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt_db.buf; + int bytes = 0; + sscanf(buf, "%" PRId64 " %n", &v, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to integer for param [%d]", arg->idx+1); + return SQL_ERROR; + } + if (vINT8_MAX) { + SET_GENERAL(errs, "failed to convert to tinyint for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_TINYINT; + taos_bind->u.v1 = (int8_t)v; + taos_bind->buffer_length = sizeof(taos_bind->u.v1); + taos_bind->buffer = &taos_bind->u.v1; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_smallint(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + int64_t v = 0; + + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt_db = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt_db.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt_db.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt_db.buf; + int bytes = 0; + sscanf(buf, "%" PRId64 " %n", &v, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to integer for param [%d]", arg->idx+1); + return SQL_ERROR; + } + if (vINT16_MAX) { + SET_GENERAL(errs, "failed to convert to smallint for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_SMALLINT; + taos_bind->u.v2 = (int16_t)v; + taos_bind->buffer_length = sizeof(taos_bind->u.v2); + taos_bind->buffer = &taos_bind->u.v2; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_int(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + int64_t v = 0; + + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt_db = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt_db.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt_db.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt_db.buf; + int bytes = 0; + sscanf(buf, "%" PRId64 " %n", &v, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to integer for param [%d]", arg->idx+1); + return SQL_ERROR; + } + if (vINT32_MAX) { + SET_GENERAL(errs, "failed to convert to int for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_INT; + taos_bind->u.v4 = (int32_t)v; + taos_bind->buffer_length = sizeof(taos_bind->u.v4); + taos_bind->buffer = &taos_bind->u.v4; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_bigint(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + int64_t v = 0; + + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt_db = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt_db.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt_db.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt_db.buf; + int bytes = 0; + sscanf(buf, "%" PRId64 " %n", &v, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to integer for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_BIGINT; + taos_bind->u.v8 = (int64_t)v; + taos_bind->buffer_length = sizeof(taos_bind->u.v8); + taos_bind->buffer = &taos_bind->u.v8; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_float(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + double v = 0; + + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt_db = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt_db.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt_db.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt_db.buf; + int bytes = 0; + sscanf(buf, "%lf %n", &v, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to integer for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_FLOAT; + taos_bind->u.f4 = (float)v; + taos_bind->buffer_length = sizeof(taos_bind->u.f4); + taos_bind->buffer = &taos_bind->u.f4; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_string_to_tsdb_double(stmt_t *stmt, const char *enc_from, tsdb_param_conv_arg_t *arg) { + double v = 0; + + todbc_buf_t *cache = arg->cache; + const char *enc_to = arg->conn->enc_src; // ?locale or src?, windows iconv!!! + const unsigned char *src = (const unsigned char*)arg->val; + size_t slen = (size_t)arg->soi; + + errs_t *errs = &stmt->errs; + + todbc_string_t txt_db = todbc_tls_conv(cache, enc_to, enc_from, src, &slen); + if (!txt_db.buf) { + SET_OOM(errs, "failed to alloc space to convert from [%s->%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + if (txt_db.bytes%s] for param [%d]", enc_from, enc_to, arg->idx+1); + return SQL_ERROR; + } + + const char *buf = (const char*)txt_db.buf; + int bytes = 0; + sscanf(buf, "%lf %n", &v, &bytes); + if (strlen(buf)!=bytes) { + SET_GENERAL(errs, "failed to convert to integer for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_DOUBLE; + taos_bind->u.f8 = v; + taos_bind->buffer_length = sizeof(taos_bind->u.f8); + taos_bind->buffer = &taos_bind->u.f8; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_timestamp(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + return do_conv_sql_string_to_tsdb_timestamp(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_nchar(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + return do_conv_sql_string_to_tsdb_nchar(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_bool(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + return do_conv_sql_string_to_tsdb_bool(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_tinyint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + int64_t v = 0; + SQLRETURN r = do_conv_sql_string_to_int64(stmt, enc_from, arg, &v); + if (r!=SQL_SUCCESS) return r; + return do_conv_int64_to_tsdb_tinyint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_smallint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + int64_t v = 0; + SQLRETURN r = do_conv_sql_string_to_int64(stmt, enc_from, arg, &v); + if (r!=SQL_SUCCESS) return r; + return do_conv_int64_to_tsdb_smallint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_int(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + int64_t v = 0; + SQLRETURN r = do_conv_sql_string_to_int64(stmt, enc_from, arg, &v); + if (r!=SQL_SUCCESS) return r; + return do_conv_int64_to_tsdb_int(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_bigint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + int64_t v = 0; + SQLRETURN r = do_conv_sql_string_to_int64(stmt, enc_from, arg, &v); + if (r!=SQL_SUCCESS) return r; + return do_conv_int64_to_tsdb_bigint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_float(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + double v = 0; + SQLRETURN r = do_conv_sql_string_to_double(stmt, enc_from, arg, &v); + if (r!=SQL_SUCCESS) return r; + return do_conv_double_to_tsdb_float(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_wchar_to_tsdb_double(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_wchar; + double v = 0; + SQLRETURN r = do_conv_sql_string_to_double(stmt, enc_from, arg, &v); + if (r!=SQL_SUCCESS) return r; + return do_conv_double_to_tsdb_double(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_timestamp(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_timestamp(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_nchar(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_nchar(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_bool(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_bool(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_tinyint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_tinyint(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_smallint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_smallint(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_int(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_int(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_bigint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_bigint(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_float(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_float(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_sql_char_to_tsdb_double(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + const char *enc_from = arg->conn->enc_char; + return do_conv_sql_string_to_tsdb_double(stmt, enc_from, arg); +} + +static SQLRETURN do_conv_int64_to_tsdb_bool(stmt_t *stmt, const int64_t v, tsdb_param_conv_arg_t *arg) { + errs_t *errs = &stmt->errs; + + if (v!=1 && v!=0) { + SET_GENERAL(errs, "integer overflow for param [%d]", arg->idx+1); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_BOOL; + taos_bind->u.b = v ? 1 : 0; + taos_bind->buffer_length = sizeof(taos_bind->u.b); + taos_bind->buffer = &taos_bind->u.b; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_sql_sbigint_to_tsdb_bigint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int64_t v = *(int64_t*)arg->val; + + return do_conv_int64_to_tsdb_bigint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_sbigint_to_tsdb_tinyint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int64_t v = *(int64_t*)arg->val; + + return do_conv_int64_to_tsdb_tinyint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_sbigint_to_tsdb_int(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int64_t v = *(int64_t*)arg->val; + + return do_conv_int64_to_tsdb_int(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_sbigint_to_tsdb_timestamp(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int64_t v = *(int64_t*)arg->val; + + return do_conv_int64_to_tsdb_timestamp(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_long_to_tsdb_bool(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int32_t v = *(int32_t*)arg->val; + + return do_conv_int64_to_tsdb_bool(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_long_to_tsdb_tinyint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int32_t v = *(int32_t*)arg->val; + + return do_conv_int64_to_tsdb_tinyint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_long_to_tsdb_smallint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int32_t v = *(int32_t*)arg->val; + + return do_conv_int64_to_tsdb_smallint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_long_to_tsdb_int(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int32_t v = *(int32_t*)arg->val; + + return do_conv_int64_to_tsdb_int(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_long_to_tsdb_bigint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int32_t v = *(int32_t*)arg->val; + + return do_conv_int64_to_tsdb_bigint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_tinyint_to_tsdb_bool(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int8_t v = *(int8_t*)arg->val; + + return do_conv_int64_to_tsdb_bool(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_tinyint_to_tsdb_tinyint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int8_t v = *(int8_t*)arg->val; + + return do_conv_int64_to_tsdb_tinyint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_tinyint_to_tsdb_smallint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int8_t v = *(int8_t*)arg->val; + + return do_conv_int64_to_tsdb_smallint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_tinyint_to_tsdb_int(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int8_t v = *(int8_t*)arg->val; + + return do_conv_int64_to_tsdb_int(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_tinyint_to_tsdb_bigint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int8_t v = *(int8_t*)arg->val; + + return do_conv_int64_to_tsdb_bigint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_short_to_tsdb_bool(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int16_t v = *(int16_t*)arg->val; + + return do_conv_int64_to_tsdb_bool(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_short_to_tsdb_tinyint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int16_t v = *(int16_t*)arg->val; + + return do_conv_int64_to_tsdb_tinyint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_short_to_tsdb_smallint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int16_t v = *(int16_t*)arg->val; + + return do_conv_int64_to_tsdb_smallint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_short_to_tsdb_int(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int16_t v = *(int16_t*)arg->val; + + return do_conv_int64_to_tsdb_int(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_short_to_tsdb_bigint(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + int16_t v = *(int16_t*)arg->val; + + return do_conv_int64_to_tsdb_bigint(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_double_to_tsdb_float(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + double v = *(double*)arg->val; + + return do_conv_double_to_tsdb_float(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_double_to_tsdb_double(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + double v = *(double*)arg->val; + + return do_conv_double_to_tsdb_double(stmt, v, arg); +} + +static SQLRETURN do_conv_sql_binary_to_tsdb_binary(stmt_t *stmt, tsdb_param_conv_arg_t *arg) { + unsigned char *buf = (unsigned char*)arg->val; + size_t len = (size_t)arg->soi; + OILE(len>0, ""); + + errs_t *errs = &stmt->errs; + + if (len > arg->tsdb_param->tsdb_bytes) { + SET_OOM(errs, "failed to convert binary for param [%d], binary too long [%zd/%d]", arg->idx+1, len, arg->tsdb_param->tsdb_bytes); + return SQL_ERROR; + } + + tsdb_param_val_t *tsdb_param_val = arg->tsdb_param_val; + TAOS_BIND *taos_bind = arg->taos_bind; + + taos_bind->buffer_type = TSDB_DATA_TYPE_BINARY; + taos_bind->u.bin = buf; + taos_bind->buffer_length = len; + taos_bind->buffer = taos_bind->u.bin; + taos_bind->length = &taos_bind->buffer_length; + taos_bind->is_null = &tsdb_param_val->is_null; + + return SQL_SUCCESS; +} + +static SQLRETURN do_set_param_wchar_conv_func(stmt_t *stmt, int idx, param_binding_t *binding, tsdb_param_t *tsdb_param) { + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + int tsdb_type = tsdb_param->tsdb_type; + + switch (tsdb_type) + { + case TSDB_DATA_TYPE_TIMESTAMP: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_timestamp; + } break; + case TSDB_DATA_TYPE_NCHAR: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_nchar; + } break; + case TSDB_DATA_TYPE_BOOL: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_bool; + } break; + case TSDB_DATA_TYPE_TINYINT: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_tinyint; + } break; + case TSDB_DATA_TYPE_SMALLINT: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_smallint; + } break; + case TSDB_DATA_TYPE_INT: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_int; + } break; + case TSDB_DATA_TYPE_BIGINT: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_bigint; + } break; + case TSDB_DATA_TYPE_FLOAT: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_float; + } break; + case TSDB_DATA_TYPE_DOUBLE: + { + tsdb_param->conv = do_conv_sql_wchar_to_tsdb_double; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for param [%d]", + valueType, sql_c_type(valueType), + tsdb_type, taos_data_type(tsdb_type), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_set_param_char_conv_func(stmt_t *stmt, int idx, param_binding_t *binding, tsdb_param_t *tsdb_param) { + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + int tsdb_type = tsdb_param->tsdb_type; + + switch (tsdb_type) + { + case TSDB_DATA_TYPE_TIMESTAMP: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_timestamp; + } break; + case TSDB_DATA_TYPE_NCHAR: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_nchar; + } break; + case TSDB_DATA_TYPE_BOOL: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_bool; + } break; + case TSDB_DATA_TYPE_TINYINT: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_tinyint; + } break; + case TSDB_DATA_TYPE_SMALLINT: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_smallint; + } break; + case TSDB_DATA_TYPE_INT: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_int; + } break; + case TSDB_DATA_TYPE_BIGINT: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_bigint; + } break; + case TSDB_DATA_TYPE_FLOAT: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_float; + } break; + case TSDB_DATA_TYPE_DOUBLE: + { + tsdb_param->conv = do_conv_sql_char_to_tsdb_double; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for param [%d]", + valueType, sql_c_type(valueType), + tsdb_type, taos_data_type(tsdb_type), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_set_param_sbigint_conv_func(stmt_t *stmt, int idx, param_binding_t *binding, tsdb_param_t *tsdb_param) { + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + int tsdb_type = tsdb_param->tsdb_type; + + switch (tsdb_type) + { + case TSDB_DATA_TYPE_BIGINT: + { + tsdb_param->conv = do_conv_sql_sbigint_to_tsdb_bigint; + } break; + case TSDB_DATA_TYPE_TINYINT: + { + tsdb_param->conv = do_conv_sql_sbigint_to_tsdb_tinyint; + } break; + case TSDB_DATA_TYPE_INT: + { + tsdb_param->conv = do_conv_sql_sbigint_to_tsdb_int; + } break; + case TSDB_DATA_TYPE_TIMESTAMP: + { + tsdb_param->conv = do_conv_sql_sbigint_to_tsdb_timestamp; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for param [%d]", + valueType, sql_c_type(valueType), + tsdb_type, taos_data_type(tsdb_type), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_set_param_long_conv_func(stmt_t *stmt, int idx, param_binding_t *binding, tsdb_param_t *tsdb_param) { + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + int tsdb_type = tsdb_param->tsdb_type; + + switch (tsdb_type) + { + case TSDB_DATA_TYPE_BOOL: + { + tsdb_param->conv = do_conv_sql_long_to_tsdb_bool; + } break; + case TSDB_DATA_TYPE_TINYINT: + { + tsdb_param->conv = do_conv_sql_long_to_tsdb_tinyint; + } break; + case TSDB_DATA_TYPE_SMALLINT: + { + tsdb_param->conv = do_conv_sql_long_to_tsdb_smallint; + } break; + case TSDB_DATA_TYPE_INT: + { + tsdb_param->conv = do_conv_sql_long_to_tsdb_int; + } break; + case TSDB_DATA_TYPE_BIGINT: + { + tsdb_param->conv = do_conv_sql_long_to_tsdb_bigint; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for param [%d]", + valueType, sql_c_type(valueType), + tsdb_type, taos_data_type(tsdb_type), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_set_param_tinyint_conv_func(stmt_t *stmt, int idx, param_binding_t *binding, tsdb_param_t *tsdb_param) { + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + int tsdb_type = tsdb_param->tsdb_type; + + switch (tsdb_type) + { + case TSDB_DATA_TYPE_BOOL: + { + tsdb_param->conv = do_conv_sql_tinyint_to_tsdb_bool; + } break; + case TSDB_DATA_TYPE_TINYINT: + { + tsdb_param->conv = do_conv_sql_tinyint_to_tsdb_tinyint; + } break; + case TSDB_DATA_TYPE_SMALLINT: + { + tsdb_param->conv = do_conv_sql_tinyint_to_tsdb_smallint; + } break; + case TSDB_DATA_TYPE_INT: + { + tsdb_param->conv = do_conv_sql_tinyint_to_tsdb_int; + } break; + case TSDB_DATA_TYPE_BIGINT: + { + tsdb_param->conv = do_conv_sql_tinyint_to_tsdb_bigint; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for param [%d]", + valueType, sql_c_type(valueType), + tsdb_type, taos_data_type(tsdb_type), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_set_param_short_conv_func(stmt_t *stmt, int idx, param_binding_t *binding, tsdb_param_t *tsdb_param) { + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + int tsdb_type = tsdb_param->tsdb_type; + + switch (tsdb_type) + { + case TSDB_DATA_TYPE_BOOL: + { + tsdb_param->conv = do_conv_sql_short_to_tsdb_bool; + } break; + case TSDB_DATA_TYPE_TINYINT: + { + tsdb_param->conv = do_conv_sql_short_to_tsdb_tinyint; + } break; + case TSDB_DATA_TYPE_SMALLINT: + { + tsdb_param->conv = do_conv_sql_short_to_tsdb_smallint; + } break; + case TSDB_DATA_TYPE_INT: + { + tsdb_param->conv = do_conv_sql_short_to_tsdb_int; + } break; + case TSDB_DATA_TYPE_BIGINT: + { + tsdb_param->conv = do_conv_sql_short_to_tsdb_bigint; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for param [%d]", + valueType, sql_c_type(valueType), + tsdb_type, taos_data_type(tsdb_type), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_set_param_double_conv_func(stmt_t *stmt, int idx, param_binding_t *binding, tsdb_param_t *tsdb_param) { + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + int tsdb_type = tsdb_param->tsdb_type; + + switch (tsdb_type) + { + case TSDB_DATA_TYPE_FLOAT: + { + tsdb_param->conv = do_conv_sql_double_to_tsdb_float; + } break; + case TSDB_DATA_TYPE_DOUBLE: + { + tsdb_param->conv = do_conv_sql_double_to_tsdb_double; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for param [%d]", + valueType, sql_c_type(valueType), + tsdb_type, taos_data_type(tsdb_type), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_set_param_binary_conv_func(stmt_t *stmt, int idx, param_binding_t *binding, tsdb_param_t *tsdb_param) { + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + int tsdb_type = tsdb_param->tsdb_type; + + switch (tsdb_type) + { + case TSDB_DATA_TYPE_BINARY: + { + tsdb_param->conv = do_conv_sql_binary_to_tsdb_binary; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for param [%d]", + valueType, sql_c_type(valueType), + tsdb_type, taos_data_type(tsdb_type), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + + +static SQLRETURN do_set_param_conv_func(stmt_t *stmt, int idx, param_binding_t *binding) { + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + tsdb_param_t *tsdb_params = tsdb_stmt->tsdb_params; + tsdb_param_t *tsdb_param = tsdb_params + idx; + + errs_t *errs = &stmt->errs; + + SQLSMALLINT valueType = binding->ValueType; + + switch (valueType) + { + case SQL_C_CHAR: + { + return do_set_param_char_conv_func(stmt, idx, binding, tsdb_param); + } break; + case SQL_C_WCHAR: + { + return do_set_param_wchar_conv_func(stmt, idx, binding, tsdb_param); + } break; + case SQL_C_SBIGINT: + { + return do_set_param_sbigint_conv_func(stmt, idx, binding, tsdb_param); + } break; + case SQL_C_LONG: + { + return do_set_param_long_conv_func(stmt, idx, binding, tsdb_param); + } break; + case SQL_C_TINYINT: + { + return do_set_param_tinyint_conv_func(stmt, idx, binding, tsdb_param); + } break; + case SQL_C_SHORT: + { + return do_set_param_short_conv_func(stmt, idx, binding, tsdb_param); + } break; + case SQL_C_DOUBLE: + { + return do_set_param_double_conv_func(stmt, idx, binding, tsdb_param); + } break; + case SQL_C_BINARY: + { + return do_set_param_binary_conv_func(stmt, idx, binding, tsdb_param); + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s for param [%d]", + valueType, sql_c_type(valueType), idx+1); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN tsdb_conn_set_param_conv(stmt_t *stmt, int idx) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt && tsdb_stmt->stmt==stmt, ""); + OILE(tsdb_stmt->tsdb_res==NULL, ""); + OILE(idx>=0, ""); + + paramset_t *paramset = &stmt->paramset; + param_t *params = paramset->params; + if (!params || idx>=paramset->n_params) return SQL_SUCCESS; + param_binding_t *bindings = paramset->bindings; + if (!bindings || idx>=paramset->n_bindings) return SQL_SUCCESS; + tsdb_param_t *tsdb_params = tsdb_stmt->tsdb_params; + OILE(tsdb_params, ""); + + param_binding_t *binding = bindings + idx; + + return do_set_param_conv_func(stmt, idx, binding); +} + +static SQLRETURN do_fill_param(stmt_t *stmt, int idx) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt && tsdb_stmt->stmt==stmt, ""); + OILE(tsdb_stmt->tsdb_res==NULL, ""); + + paramset_t *paramset = &stmt->paramset; + param_t *params = paramset->params; + tsdb_param_t *tsdb_params = tsdb_stmt->tsdb_params; + OILE(params, ""); + OILE(tsdb_params, ""); + OILE(idx>=0, ""); + OILE(idxn_params, ""); + param_t *param = params + idx; + tsdb_param_t *tsdb_param = tsdb_params + idx; + + errs_t *errs = &stmt->errs; + + int tsdb_type = 0; + int tsdb_bytes = 0; + int r = taos_stmt_get_param(tsdb_stmt->tsdb_stmt, idx, &tsdb_type, &tsdb_bytes); + if (r) { + SET_GENERAL(errs, "failed to get param[%d]", idx+1); + return SQL_ERROR; + } + + // https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size-decimal-digits-transfer-octet-length-and-display-size?view=sql-server-ver15 + param->DecimalDigits = 0; + param->Nullable = SQL_NULLABLE; + tsdb_param->tsdb_type = tsdb_type; + tsdb_param->tsdb_bytes = tsdb_bytes; + switch (tsdb_type) + { + case TSDB_DATA_TYPE_TIMESTAMP: + { + param->DataType = SQL_CHAR; + param->ParameterSize = 23; + } break; + case TSDB_DATA_TYPE_NCHAR: + { + size_t bytes = ((size_t)tsdb_bytes - VARSTR_HEADER_SIZE); + size_t chars = bytes / TSDB_NCHAR_SIZE; + tsdb_param->tsdb_bytes = (int)bytes; + param->DataType = SQL_WCHAR; + param->ParameterSize = (SQLULEN)chars; + } break; + case TSDB_DATA_TYPE_BINARY: + { + size_t bytes = ((size_t)tsdb_bytes - VARSTR_HEADER_SIZE); + tsdb_param->tsdb_bytes = (int)bytes; + param->DataType = SQL_BINARY; + param->ParameterSize = (SQLULEN)bytes; + } break; + case TSDB_DATA_TYPE_BOOL: + { + param->DataType = SQL_BIT; + param->ParameterSize = 1; + } break; + case TSDB_DATA_TYPE_TINYINT: + { + param->DataType = SQL_TINYINT; + param->ParameterSize = 3; + } break; + case TSDB_DATA_TYPE_SMALLINT: + { + param->DataType = SQL_SMALLINT; + param->ParameterSize = 5; + } break; + case TSDB_DATA_TYPE_INT: + { + param->DataType = SQL_INTEGER; + param->ParameterSize = 10; + } break; + case TSDB_DATA_TYPE_BIGINT: + { + param->DataType = SQL_BIGINT; + param->ParameterSize = 20; + } break; + case TSDB_DATA_TYPE_FLOAT: + { + param->DataType = SQL_FLOAT; + param->ParameterSize = 15; + } break; + case TSDB_DATA_TYPE_DOUBLE: + { + param->DataType = SQL_DOUBLE; + param->ParameterSize = 15; + } break; + default: + { + SET_GENERAL(errs, "failed to map param[%d] type[%d]%s to SQL DATA TYPE", + idx+1, tsdb_type, taos_data_type(tsdb_type)); + return SQL_ERROR; + } break; + } + + param->ParameterNumber = (SQLUSMALLINT)(idx + 1); + + param_binding_t *bindings = paramset->bindings; + if (!bindings) return SQL_SUCCESS; + if (idx>=paramset->n_bindings) return SQL_SUCCESS; + + param_binding_t *binding = bindings + idx; + + return do_set_param_conv_func(stmt, idx, binding); +} + +static SQLRETURN tsdb_conn_prepare(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + OILE(stmt->prepared==0, ""); + OILE(STMT_IS_NORM(stmt), ""); + errs_t *errs = &stmt->errs; + + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + tsdb_conn_t *tsdb_conn = (tsdb_conn_t*)conn->ext.ext; + OILE(tsdb_conn && tsdb_conn->conn==conn, ""); + TAOS *taos = tsdb_conn->taos; + OILE(taos, ""); + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt && tsdb_stmt->stmt==stmt, ""); + OILE(tsdb_stmt->tsdb_res==NULL, ""); + // OILE(tsdb_stmt->tsdb_stmt==NULL, ""); + + tsdb_stmt->tsdb_params = NULL; + + if (!tsdb_stmt->tsdb_stmt) { + SQLRETURN r = tsdb_stmt_init_stmt(tsdb_stmt); + if (r!=SQL_SUCCESS) return r; + } + OILE(tsdb_stmt->tsdb_stmt, ""); + + tsdb_stmt_reclaim_param_vals(tsdb_stmt); + + do { + const char *txt = (const char*)stmt->sql.txt.buf; + size_t len = stmt->sql.txt.bytes; + OILE(txt, ""); + OILE(len>0, ""); + int r = taos_stmt_prepare(tsdb_stmt->tsdb_stmt, txt, (unsigned int)len); + if (r) { + SET_GENERAL(errs, "failed to prepare taos stmt:[%d]%s", r, tstrerror(r)); + break; + } + + int nums = 0; + r = taos_stmt_num_params(tsdb_stmt->tsdb_stmt, &nums); + if (r) { + SET_GENERAL(errs, "failed to prepare taos stmt:[%d]%s", r, tstrerror(r)); + break; + } + + paramset_t *paramset = &stmt->paramset; + OILE(paramset->params==NULL, ""); + OILE(paramset->n_params==0, ""); + OILE(tsdb_stmt->tsdb_params==NULL, ""); + + if (nums>0) { + paramset_init_params_cache(paramset); + tsdb_stmt_init_param_vals_cache(tsdb_stmt); + + if (!tsdb_stmt->tsdb_param_vals_cache) { + SET_OOM(errs, "failed to alloc val cache for params"); + return SQL_ERROR; + } + + todbc_buf_t *cache = stmt->paramset.params_cache; + if (!cache) { + SET_OOM(errs, "failed to alloc cache buffer for params"); + return SQL_ERROR; + } + OILE(cache, ""); + + param_t *params = (param_t*)todbc_buf_calloc(cache, (size_t)nums, sizeof(*params)); + if (!params) { + SET_OOM(errs, "failed to alloc buffer for params"); + return SQL_ERROR; + } + + tsdb_param_t *tsdb_params = (tsdb_param_t*)todbc_buf_calloc(cache, (size_t)nums, sizeof(*tsdb_params)); + if (!tsdb_params) { + SET_OOM(errs, "failed to alloc buffer for tsdb params"); + return SQL_ERROR; + } + + paramset->params = params; + paramset->n_params = nums; + tsdb_stmt->tsdb_params = tsdb_params; + + for (int i=0; iowner, ""); + + errs_t *errs = &stmt->errs; + + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + const char *enc_to = conn->enc_char; + const char *enc_from = conn->enc_src; // UTF8 + const unsigned char *src = (const unsigned char*)buf; + size_t slen = strlen(buf); + unsigned char *dst = (unsigned char*)col->TargetValue; + size_t dlen = (size_t)col->BufferLength; + todbc_string_t s = todbc_tls_write(enc_to, enc_from, src, &slen, dst, dlen); + if (!s.buf) { + SET_OOM(errs, "failed to convert timestamp"); + return SQL_ERROR; + } + OILE(s.bytes==s.total_bytes, ""); + if (col->StrLen_or_IndPtr) { + *col->StrLen_or_IndPtr = (SQLLEN)s.bytes; + } + for(size_t i=s.bytes; ierrs; + + switch (binding->TargetType) + { + case SQL_C_CHAR: + { + size_t len = (size_t)binding->BufferLength; + OILE(len>0, ""); + if (bytesTargetValue) { + memcpy(binding->TargetValue, val, len); + } + if (binding->StrLen_or_IndPtr) { + *binding->StrLen_or_IndPtr = (SQLLEN)len; + } + // do we really need this? + size_t dlen = (size_t)binding->BufferLength; + unsigned char *dst = (unsigned char*)binding->TargetValue; + for(size_t i=len; iBufferLength; + OILE(len>0, ""); + if (bytesTargetValue) { + memcpy(binding->TargetValue, val, len); + } + if (binding->StrLen_or_IndPtr) { + *binding->StrLen_or_IndPtr = (SQLLEN)len; + } + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for binding[%d]", + field->type, taos_data_type(field->type), + binding->TargetType, sql_c_type(binding->TargetType), + binding->ColumnNumber); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_nchar_to_sql_c(stmt_t *stmt, TAOS_FIELD *field, void *val, size_t bytes, col_binding_t *binding) { + errs_t *errs = &stmt->errs; + + OILE(stmt->owner, ""); + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + switch (binding->TargetType) + { + case SQL_C_CHAR: + { + const char *enc_to = conn->enc_char; + const char *enc_from = conn->enc_db; + const unsigned char *src = (const unsigned char*)val; + size_t slen = bytes; + unsigned char *dst = (unsigned char*)binding->TargetValue; + size_t dlen = (size_t)binding->BufferLength; + todbc_string_t s = todbc_tls_write(enc_to, enc_from, src, &slen, dst, dlen); + if (!s.buf) { + SET_OOM(errs, "failed to convert nchar"); + return SQL_ERROR; + } + OILE(s.bytes==s.total_bytes, ""); + if (binding->StrLen_or_IndPtr) { + *binding->StrLen_or_IndPtr = (SQLLEN)s.bytes; // com-on, it's NOT character-size + } + for(size_t i=s.bytes; ienc_wchar; + const char *enc_from = conn->enc_db; + const unsigned char *src = (const unsigned char*)val; + size_t slen = bytes; + unsigned char *dst = (unsigned char*)binding->TargetValue; + size_t dlen = (size_t)binding->BufferLength; + todbc_string_t s = todbc_tls_write(enc_to, enc_from, src, &slen, dst, dlen); + if (!s.buf) { + SET_OOM(errs, "failed to convert nchar"); + return SQL_ERROR; + } + OILE(s.bytes==s.total_bytes, ""); + if (binding->StrLen_or_IndPtr) { + *binding->StrLen_or_IndPtr = (SQLLEN)s.bytes; // com-on, it's NOT character-size + } + for(size_t i=s.bytes; itype, taos_data_type(field->type), + binding->TargetType, sql_c_type(binding->TargetType), + binding->ColumnNumber); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_int64_to_sql_c(stmt_t *stmt, TAOS_FIELD *field, int64_t val, col_binding_t *binding) { + errs_t *errs = &stmt->errs; + + char buf[128]; + + switch (binding->TargetType) + { + case SQL_C_CHAR: + { + snprintf(buf, sizeof(buf), "%" PRId64 "", val); + return do_conv_utf8_to_sql_c_char(stmt, buf, binding); + } break; + case SQL_C_UTINYINT: + { + if (val>UINT8_MAX || val<0) { + SET_GENERAL(errs, ""); + return SQL_ERROR; + } + if (binding->TargetValue) { + *(uint8_t*)binding->TargetValue = (uint8_t)val; + } + } break; + case SQL_C_USHORT: + { + if (val>UINT16_MAX || val<0) { + SET_GENERAL(errs, ""); + return SQL_ERROR; + } + if (binding->TargetValue) { + *(uint16_t*)binding->TargetValue = (uint16_t)val; + } + } break; + case SQL_C_SLONG: + { + if (val>INT32_MAX || valTargetValue) { + *(int32_t*)binding->TargetValue = (int32_t)val; + } + } break; + case SQL_C_UBIGINT: + { + if (binding->TargetValue) { + *(uint64_t*)binding->TargetValue = (uint64_t)val; + } + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for binding[%d]", + field->type, taos_data_type(field->type), + binding->TargetType, sql_c_type(binding->TargetType), + binding->ColumnNumber); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_timestamp_to_utf8(stmt_t *stmt, int64_t v, char *buf, size_t len) { + errs_t *errs = &stmt->errs; + + // microsecond precision, based on test + time_t secs = v / 1000; + int msecs = (int)(v % 1000); + + struct tm vtm = {0}; + if (&vtm!=localtime_r(&secs, &vtm)) { + SET_ERR(errs, "22007", "invalid timestamp"); + return SQL_ERROR; // ? SQL_SUCCESS_WITH_INFO + } + + char *p = buf; + size_t bytes = len; + + OILE(bytes>0, ""); + size_t n = strftime(p, bytes, "%Y-%m-%d %H:%M:%S", &vtm); + if (n==0) { + SET_GENERAL(errs, "failed to convert timestamp"); + return SQL_ERROR; // ? SQL_SUCCESS_WITH_INFO + } + p += n; + bytes -= n; + + OILE(bytes>0, ""); + int m = snprintf(p, bytes, ".%03d", msecs); + if (m>=bytes) { + SET_GENERAL(errs, "failed to convert timestamp"); + return SQL_ERROR; // ? SQL_SUCCESS_WITH_INFO + } + p += (size_t)m; + bytes -= (size_t)m; + + OILE(bytes>=0, ""); + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_timestamp_to_sql_c(stmt_t *stmt, TAOS_FIELD *field, int64_t val, col_binding_t *col) { + errs_t *errs = &stmt->errs; + + OILE(stmt, ""); + OILE(stmt->owner, ""); + + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + + SQLRETURN r; + char buf[128]; + + switch (col->TargetType) + { + case SQL_C_CHAR: + { + r = do_conv_timestamp_to_utf8(stmt, val, buf, sizeof(buf)); + if (r!=SQL_SUCCESS) return r; + return do_conv_utf8_to_sql_c_char(stmt, buf, col); + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for col[%d]", + field->type, taos_data_type(field->type), + col->TargetType, sql_c_type(col->TargetType), + col->ColumnNumber); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN do_conv_double_to_sql_c(stmt_t *stmt, TAOS_FIELD *field, double val, col_binding_t *binding) { + errs_t *errs = &stmt->errs; + + char buf[256]; + + switch (binding->TargetType) + { + case SQL_C_DOUBLE: + { + if (binding->TargetValue) { + *(double*)binding->TargetValue = val; + } + } break; + case SQL_C_FLOAT: + { + // shall we check overflow/underflow here? + if (binding->TargetValue) { + *(float*)binding->TargetValue = (float)val; + } + } break; + case SQL_C_CHAR: + { + snprintf(buf, sizeof(buf), "%lf", val); + return do_conv_utf8_to_sql_c_char(stmt, buf, binding); + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s to [%d] %s for binding[%d]", + field->type, taos_data_type(field->type), + binding->TargetType, sql_c_type(binding->TargetType), + binding->ColumnNumber); + return SQL_ERROR; + } break; + } + + return SQL_SUCCESS; +} + +static SQLRETURN tsdb_conn_proc_param(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt && tsdb_stmt->stmt==stmt, ""); + + paramset_t *paramset = &stmt->paramset; + param_t *params = paramset->params; + int n_params = paramset->n_params; + param_binding_t *bindings = paramset->bindings; + int n_bindings = paramset->n_bindings; + tsdb_param_t *tsdb_params = tsdb_stmt->tsdb_params; + int i_row = paramset->i_row; + int i_col = paramset->i_col; + + OILE(params && n_params>0, ""); + OILE(bindings && n_bindings>=0, ""); + OILE(tsdb_params, ""); + OILE(n_params==n_bindings, ""); + OILE(i_row>=0, ""); + OILE(i_col>=0 && i_colattr.bind_type; // default: SQL_PARAM_BIND_BY_COLUMN + // SQL_ATTR_PARAM_BIND_OFFSET_PTR + SQLULEN *bind_offset_ptr = stmt->attr.bind_offset_ptr; // default: NULL + // SQL_ATTR_PARAMSET_SIZE + SQLULEN paramset_size = stmt->attr.paramset_size; // default: 0 + + // OILE(bind_type && bind_type!=SQL_PARAM_BIND_BY_COLUMN, "[%ld]", bind_type); + // OILE(bind_offset_ptr, ""); + OILE(paramset_size>0, ""); + + OILE(i_rowParameterNumber>0 && param->ParameterNumber<=n_params, ""); + tsdb_param_t *tsdb_param = tsdb_params + i_col; + OILE(tsdb_param->conv, ""); + + tsdb_param_val_t *tsdb_param_vals = tsdb_stmt->tsdb_param_vals; + if (!tsdb_param_vals) { + errs_t *errs = &stmt->errs; + tsdb_stmt_calloc_param_vals(tsdb_stmt); + if (tsdb_stmt->tsdb_param_vals==NULL) { + SET_OOM(errs, "failed to alloc tsdb param vals for tsdb params"); + return SQL_ERROR; + } + if (tsdb_stmt->taos_binds==NULL) { + SET_OOM(errs, "failed to alloc taos binds for tsdb params"); + return SQL_ERROR; + } + tsdb_param_vals = tsdb_stmt->tsdb_param_vals; + } + OILE(tsdb_param_vals, ""); + TAOS_BIND *taos_binds = tsdb_stmt->taos_binds; + OILE(taos_binds, ""); + + + tsdb_param_val_t *tsdb_param_val = tsdb_param_vals + i_col; + tsdb_param_val->ParameterNumber = (SQLUSMALLINT)(i_col + 1); + tsdb_param_val->is_null = 1; + TAOS_BIND *taos_bind = taos_binds + i_col; + + param_binding_t *binding = bindings + (size_t)i_col; + OILE(binding->ParameterNumber==i_col+1, ""); + if (binding->ParameterValuePtr==NULL) return SQL_SUCCESS; + + SQLPOINTER val = binding->ParameterValuePtr; + SQLLEN *soip = binding->StrLen_or_IndPtr; + OILE(soip, ""); + + size_t offset = (size_t)i_row * (size_t)bind_type; + size_t bind_offset = 0; + if (bind_offset_ptr) bind_offset = *bind_offset_ptr; + + val = (SQLPOINTER)(((char*)val) + offset + bind_offset); + soip = (SQLLEN*)(((char*)soip) + offset + bind_offset); + + SQLLEN soi = *soip; + + if (soi == SQL_NULL_DATA) return SQL_SUCCESS; + + OILE(soi>=0 || soi==SQL_NTS, ""); + + tsdb_param_val->is_null = 0; + + conn_t *conn = stmt->owner->conn; + OILE(conn, ""); + todbc_buf_t *cache = tsdb_stmt->tsdb_param_vals_cache; + OILE(cache, ""); + + tsdb_param_conv_arg_t arg = { + .conn = conn, + .cache = cache, + .idx = i_col, + .val = val, + .soi = soi, + .tsdb_param = tsdb_param, + .tsdb_param_val = tsdb_param_val, + .taos_bind = taos_bind + }; + return tsdb_param->conv(stmt, &arg); +} + +static SQLRETURN tsdb_conn_param_row_processed(stmt_t *stmt) { + paramset_t *paramset = &stmt->paramset; + OILE(paramset->n_params>0, ""); + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt, ""); + TAOS_BIND *taos_binds = tsdb_stmt->taos_binds; + OILE(taos_binds, ""); + OILE(tsdb_stmt->tsdb_stmt, ""); + + errs_t *errs = &stmt->errs; + + if (1) { + int r = taos_stmt_bind_param(tsdb_stmt->tsdb_stmt, taos_binds); + if (r) { + SET_GENERAL(errs, "failed to bind params:[%d]%s", r, tstrerror(r)); + // keep executing/executed state unchanged + return SQL_ERROR; + } + + r = taos_stmt_add_batch(tsdb_stmt->tsdb_stmt); + if (r) { + SET_GENERAL(errs, "failed to add batch params:[%d]%s", r, tstrerror(r)); + // keep executing/executed state unchanged + return SQL_ERROR; + } + } + + return SQL_SUCCESS; +} + +static SQLRETURN tsdb_conn_execute(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + OILE(!STMT_IS_EXECUTED(stmt), ""); + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt, ""); + if (!tsdb_stmt->tsdb_stmt) { + SQLRETURN r = tsdb_stmt_init_stmt(tsdb_stmt); + if (r!=SQL_SUCCESS) return r; + OILE(0, ""); + } + OILE(tsdb_stmt->tsdb_stmt, ""); + + errs_t * errs = &stmt->errs; + + if (1) { + int r = 0; + + r = taos_stmt_execute(tsdb_stmt->tsdb_stmt); + if (r) { + SET_GENERAL(errs, "failed to execute:[%d]%s", r, tstrerror(r)); + // keep executing/executed state unchanged + return SQL_ERROR; + } + + tsdb_stmt->by_query = 0; + } + + STMT_SET_EXECUTED(stmt); + return SQL_SUCCESS; +} + +static void do_fetch_tsdb_res(tsdb_stmt_t *tsdb_stmt) { + if (!tsdb_stmt->tsdb_res) { + OILE(tsdb_stmt->by_query==0, ""); + OILE(tsdb_stmt->tsdb_stmt, ""); + tsdb_stmt->tsdb_res = taos_stmt_use_result(tsdb_stmt->tsdb_stmt); + OILE(tsdb_stmt->tsdb_res, ""); + // currently, TAOS_STMT does NOT co-exist with TAOS_RES + tsdb_stmt_close_stmt(tsdb_stmt); + } +} + +SQLRETURN tsdb_conn_get_affected_rows(stmt_t *stmt, SQLLEN *RowCount) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt, ""); + + do_fetch_tsdb_res(tsdb_stmt); + + int rows = taos_affected_rows(tsdb_stmt->tsdb_res); + OILE(RowCount, ""); + *RowCount = rows; + + return SQL_SUCCESS; +} + +static void do_fetch_tsdb_fields(tsdb_stmt_t *tsdb_stmt) { + if (!tsdb_stmt->tsdb_fields) { + tsdb_stmt->tsdb_fields = taos_fetch_fields(tsdb_stmt->tsdb_res); + } +} + +SQLRETURN tsdb_conn_get_fields_count(stmt_t *stmt, SQLSMALLINT *ColumnCount) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt, ""); + + do_fetch_tsdb_res(tsdb_stmt); + OILE(tsdb_stmt->tsdb_res, ""); + + int n_fields = taos_num_fields(tsdb_stmt->tsdb_res); + OILE(ColumnCount, ""); + *ColumnCount = (SQLSMALLINT)n_fields; + + return SQL_SUCCESS; +} + +SQLRETURN tsdb_conn_get_field(stmt_t *stmt, field_arg_t *arg) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + + errs_t *errs = &stmt->errs; + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt, ""); + + do_fetch_tsdb_res(tsdb_stmt); + OILE(tsdb_stmt->tsdb_res, ""); + + int n_fields = taos_num_fields(tsdb_stmt->tsdb_res); + OILE(arg->ColumnNumber>0, ""); + OILE(arg->ColumnNumber<=n_fields, ""); + do_fetch_tsdb_fields(tsdb_stmt); + OILE(tsdb_stmt->tsdb_fields, ""); + + TAOS_FIELD *field = tsdb_stmt->tsdb_fields + (arg->ColumnNumber-1); + int len = 0; + // charset ? + len = snprintf((char*)arg->ColumnName, (size_t)arg->BufferLength, "%s", field->name); + if (arg->NameLength) *arg->NameLength = (SQLSMALLINT)len; + if (arg->DecimalDigits) *arg->DecimalDigits = 0; + if (arg->Nullable) *arg->Nullable = SQL_NULLABLE; + + SQLSMALLINT DataType; + SQLULEN ColumnSize; + + // https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size-decimal-digits-transfer-octet-length-and-display-size?view=sql-server-ver15 + switch (field->type) { + case TSDB_DATA_TYPE_TIMESTAMP: + { + DataType = SQL_CHAR; + ColumnSize = 23; + } break; + case TSDB_DATA_TYPE_NCHAR: + { + DataType = SQL_WCHAR; + ColumnSize = (SQLULEN)field->bytes; + } break; + case TSDB_DATA_TYPE_BINARY: + { + DataType = SQL_BINARY; + ColumnSize = (SQLULEN)field->bytes; + } break; + case TSDB_DATA_TYPE_BOOL: + { + DataType = SQL_BIT; + ColumnSize = 1; + } break; + case TSDB_DATA_TYPE_TINYINT: + { + DataType = SQL_TINYINT; + ColumnSize = 3; + } break; + case TSDB_DATA_TYPE_SMALLINT: + { + DataType = SQL_SMALLINT; + ColumnSize = 5; + } break; + case TSDB_DATA_TYPE_INT: + { + DataType = SQL_INTEGER; + ColumnSize = 10; + } break; + case TSDB_DATA_TYPE_BIGINT: + { + DataType = SQL_BIGINT; + ColumnSize = 20; + } break; + case TSDB_DATA_TYPE_FLOAT: + { + DataType = SQL_FLOAT; + ColumnSize = 15; + } break; + case TSDB_DATA_TYPE_DOUBLE: + { + DataType = SQL_DOUBLE; + ColumnSize = 15; + } break; + default: + { + SET_GENERAL(errs, "failed to map field[%d] type[%d]%s to SQL DATA TYPE", + arg->ColumnNumber, field->type, taos_data_type(field->type)); + return SQL_ERROR; + } break; + } + + if (arg->DataType) *arg->DataType = DataType; + if (arg->ColumnSize) *arg->ColumnSize = ColumnSize; + + return SQL_SUCCESS; +} + +SQLRETURN tsdb_conn_fetch(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt, ""); + + OILE(stmt->eof==0, ""); + + do_fetch_tsdb_res(tsdb_stmt); + OILE(tsdb_stmt->tsdb_res, ""); + + tsdb_stmt->tsdb_curr = taos_fetch_row(tsdb_stmt->tsdb_res); + if (!tsdb_stmt->tsdb_curr) { + stmt->eof = 1; + return SQL_NO_DATA; + } + + return SQL_SUCCESS; +} + +static SQLRETURN tsdb_conn_get_data(stmt_t *stmt, col_binding_t *col) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt, ""); + + OILE(stmt->eof==0, ""); + OILE(tsdb_stmt->tsdb_curr, ""); + OILE(tsdb_stmt->tsdb_res, ""); + + int n_fields = taos_num_fields(tsdb_stmt->tsdb_res); + int idx = (int)(col->ColumnNumber-1); + OILE(idx>=0, ""); + OILE(idxtsdb_fields, ""); + + TAOS_FIELD *field = tsdb_stmt->tsdb_fields + idx; + + OILE(col->StrLen_or_IndPtr, ""); + void *val = tsdb_stmt->tsdb_curr[idx]; + if (!val) { + *col->StrLen_or_IndPtr = SQL_NULL_DATA; + return SQL_SUCCESS; + } + + errs_t *errs = &stmt->errs; + + int64_t i64; + double dbl; + int is_dbl = 0; + + switch (field->type) + { + case TSDB_DATA_TYPE_TINYINT: + { + i64 = *(int8_t*)val; + } break; + case TSDB_DATA_TYPE_SMALLINT: + { + i64 = *(int16_t*)val; + } break; + case TSDB_DATA_TYPE_INT: + { + i64 = *(int32_t*)val; + } break; + case TSDB_DATA_TYPE_BIGINT: + { + i64 = *(int64_t*)val; + } break; + case TSDB_DATA_TYPE_FLOAT: + { + dbl = GET_FLOAT_VAL(val); + is_dbl = 1; + } break; + case TSDB_DATA_TYPE_DOUBLE: + { + dbl = GET_DOUBLE_VAL(val); + is_dbl = 1; + } break; + case TSDB_DATA_TYPE_BINARY: + { + size_t bytes = (size_t)varDataLen((char*)val - VARSTR_HEADER_SIZE); + return do_conv_binary_to_sql_c(stmt, field, val, bytes, col); + } break; + case TSDB_DATA_TYPE_NCHAR: + { + size_t bytes = (size_t)varDataLen((char*)val - VARSTR_HEADER_SIZE); + return do_conv_nchar_to_sql_c(stmt, field, val, bytes, col); + } break; + case TSDB_DATA_TYPE_TIMESTAMP: + { + i64 = *(int64_t*)val; + return do_conv_timestamp_to_sql_c(stmt, field, i64, col); + break; + } + case TSDB_DATA_TYPE_BOOL: + { + i64 = *(int8_t*)val; + } break; + default: + { + SET_GENERAL(errs, "not convertion from [%d]%s for col[%d]", + field->type, taos_data_type(field->type), col->ColumnNumber); + return SQL_ERROR; + } break; + } + if (is_dbl) return do_conv_double_to_sql_c(stmt, field, dbl, col); + else return do_conv_int64_to_sql_c(stmt, field, i64, col); +} + +static void tsdb_conn_close_rs(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->owner, ""); + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)stmt->ext.ext; + OILE(tsdb_stmt && tsdb_stmt->stmt==stmt, ""); + + tsdb_stmt_close_rs(tsdb_stmt); +} + +static int tsdb_conn_init_stmt(stmt_t *stmt) { + OILE(stmt, ""); + OILE(stmt->ext.ext==NULL, ""); + OILE(stmt->ext.free_stmt==NULL, ""); + + OILE(stmt->owner==NULL, ""); + + tsdb_stmt_t *tsdb_stmt = (tsdb_stmt_t*)calloc(1, sizeof(*tsdb_stmt)); + if (!tsdb_stmt) return -1; + + stmt_ext_t *ext = &stmt->ext; + + tsdb_stmt->stmt = stmt; + ext->ext = tsdb_stmt; + ext->free_stmt = tsdb_conn_free_stmt; + ext->clear_param_vals = tsdb_conn_clear_param_vals; + ext->exec_direct = tsdb_conn_exec_direct; + ext->prepare = tsdb_conn_prepare; + ext->set_param_conv = tsdb_conn_set_param_conv; + ext->proc_param = tsdb_conn_proc_param; + ext->param_row_processed = tsdb_conn_param_row_processed; + ext->execute = tsdb_conn_execute; + ext->get_affected_rows = tsdb_conn_get_affected_rows; + ext->get_fields_count = tsdb_conn_get_fields_count; + ext->get_field = tsdb_conn_get_field; + ext->fetch = tsdb_conn_fetch; + ext->get_data = tsdb_conn_get_data; + ext->close_rs = tsdb_conn_close_rs; + + return 0; +} + +static pthread_once_t init_once = PTHREAD_ONCE_INIT; +static int inited = 0; +// static char tsdb_svr_info[128] = ""; +// static char tsdb_cli_info[128] = ""; + +static void init_routine(void) { + int r = taos_init(); + if (r) { + OW("taos init failed: [%d]%s", r, tstrerror(r)); + return; + } + OI("taos inited"); + inited = 1; +} + +int conn_init_tsdb_conn(conn_t *conn) { + OILE(conn, ""); + OILE(conn->ext.ext==NULL, ""); + OILE(conn->ext.free_conn==NULL, ""); + + pthread_once(&init_once, init_routine); + if (!inited) return -1; + + tsdb_conn_t *tsdb_conn = (tsdb_conn_t*)calloc(1, sizeof(*tsdb_conn)); + if (!tsdb_conn) return -1; + + tsdb_conn->conn = conn; + conn->ext.ext = tsdb_conn; + conn->ext.free_conn = tsdb_conn_free; + conn->ext.connect = tsdb_conn_connect; + conn->ext.disconnect = tsdb_conn_disconnect; + conn->ext.init_stmt = tsdb_conn_init_stmt; + + return 0; +} + + diff --git a/src/connector/odbc/src/base/tsdb_impl.h b/src/connector/odbc/src/base/tsdb_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..f217a4be410db8f69b82836e33c905481aa91ec8 --- /dev/null +++ b/src/connector/odbc/src/base/tsdb_impl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _tsdb_impl_h_ +#define _tsdb_impl_h_ + +#include "../base.h" + +#include "conn.h" + +#define DEFAULT_SERVER "localhost:6030" + +int conn_init_tsdb_conn(conn_t *conn); + + +#endif // _tsdb_impl_h_ + + diff --git a/src/connector/odbc/src/col.h b/src/connector/odbc/src/col.h new file mode 100644 index 0000000000000000000000000000000000000000..f7cdc240813c5d96a6df0491132671e05fdfe374 --- /dev/null +++ b/src/connector/odbc/src/col.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _col_h_ +#define _col_h_ + +#include "base.h" + +struct col_s { +}; + + +#endif // _col_h_ + + + diff --git a/src/connector/odbc/src/install.cmd b/src/connector/odbc/src/install.cmd new file mode 100644 index 0000000000000000000000000000000000000000..4da8a2d9764686884d13318339ac5d1c3c8a8d15 --- /dev/null +++ b/src/connector/odbc/src/install.cmd @@ -0,0 +1,6 @@ +@echo off +REM install driver +odbcconf /A {INSTALLDRIVER "TAOS|Driver=C:\TDengine\driver\todbc.dll|ConnectFunctions=YYN|DriverODBCVer=03.00|FileUsage=0|SQLLevel=0"} +REM config user dsn +odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN"} + diff --git a/src/connector/odbc/src/install.sh b/src/connector/odbc/src/install.sh index 02f31de70ed76e150fbef5d388cbd8a3e9ba73b3..c08ac9208e56a8b4ead270d48825e13cd113a078 100755 --- a/src/connector/odbc/src/install.sh +++ b/src/connector/odbc/src/install.sh @@ -2,6 +2,13 @@ set -u +EXT="so" +[[ `uname` == 'Darwin' ]] && EXT="dylib" + +SUDO="sudo" +[[ `uname` == 'Darwin' ]] && SUDO="" + + BLD_DIR="$1" rm -f "${BLD_DIR}/template.ini" @@ -10,18 +17,30 @@ rm -f "${BLD_DIR}/template.dsn" cat > "${BLD_DIR}/template.ini" < "${BLD_DIR}/template.dsn" < - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -// #define _BSD_SOURCE -#define _XOPEN_SOURCE -#define _DEFAULT_SOURCE -#define _GNU_SOURCE - -#include "todbc_log.h" -#include "todbc_flex.h" - -#include "taos.h" - -#include "tglobal.h" -#include "taoserror.h" -#include "todbc_util.h" -#include "todbc_conv.h" - -#include "os.h" - -#include -#include - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - -#define UTF8_ENC "UTF-8" -#define UTF16_ENC "UCS-2LE" -#define UNICODE_ENC "UCS-4LE" -#define GB18030_ENC "GB18030" - -#define GET_REF(obj) atomic_load_64(&obj->refcount) -#define INC_REF(obj) atomic_add_fetch_64(&obj->refcount, 1) -#define DEC_REF(obj) atomic_sub_fetch_64(&obj->refcount, 1) - -#define LOCK(obj) pthread_mutex_lock(&obj->lock); -#define UNLOCK(obj) pthread_mutex_unlock(&obj->lock); - -#define SET_ERROR(obj, sqlstate, eno, err_fmt, ...) \ -do { \ - obj->err.err_no = eno; \ - const char* estr = tstrerror(eno); \ - if (!estr) estr = "Unknown error"; \ - int n = snprintf(NULL, 0, "[TSDB:%x]%s: @%s[%d]" err_fmt "", \ - eno, estr, \ - basename((char*)__FILE__), __LINE__, \ - ##__VA_ARGS__); \ - if (n<0) break; \ - char *err_str = (char*)realloc(obj->err.err_str, (size_t)n+1); \ - if (!err_str) break; \ - obj->err.err_str = err_str; \ - snprintf(obj->err.err_str, (size_t)n+1, "[TSDB:%x]%s: @%s[%d]" err_fmt "", \ - eno, estr, \ - basename((char*)__FILE__), __LINE__, \ - ##__VA_ARGS__); \ - snprintf((char*)obj->err.sql_state, sizeof(obj->err.sql_state), "%s", sqlstate); \ -} while (0) - - -#define CLR_ERROR(obj) \ -do { \ - obj->err.err_no = TSDB_CODE_SUCCESS; \ - if (obj->err.err_str) obj->err.err_str[0] = '\0'; \ - obj->err.sql_state[0] = '\0'; \ -} while (0) - -#define FILL_ERROR(obj) \ -do { \ - size_t n = sizeof(obj->err.sql_state); \ - if (Sqlstate) strncpy((char*)Sqlstate, (char*)obj->err.sql_state, n); \ - if (NativeError) *NativeError = obj->err.err_no; \ - snprintf((char*)MessageText, (size_t)BufferLength, "%s", obj->err.err_str); \ - if (TextLength && obj->err.err_str) *TextLength = (SQLSMALLINT)utf8_chars(obj->err.err_str); \ -} while (0) - -#define FREE_ERROR(obj) \ -do { \ - obj->err.err_no = TSDB_CODE_SUCCESS; \ - if (obj->err.err_str) { \ - free(obj->err.err_str); \ - obj->err.err_str = NULL; \ - } \ - obj->err.sql_state[0] = '\0'; \ -} while (0) - -#define SET_UNSUPPORT_ERROR(obj, sqlstate, err_fmt, ...) \ -do { \ - SET_ERROR(obj, sqlstate, TSDB_CODE_ODBC_NOT_SUPPORT, err_fmt, ##__VA_ARGS__); \ -} while (0) \ - -#define SET_HANDLE_INVALID(obj, sqlstate, err_fmt, ...) \ -do { \ - SET_ERROR(obj, sqlstate, TSDB_CODE_QRY_INVALID_QHANDLE, err_fmt, ##__VA_ARGS__); \ -} while (0); - -#define SDUP(s,n) (s ? (s[(size_t)n] ? (const char*)strndup((const char*)s,(size_t)n) : (const char*)s) : strdup("")) -#define SFRE(x,s,n) \ -do { \ - if (x==(const char*)s) break; \ - if (x) { \ - free((char*)x); \ - x = NULL; \ - } \ -} while (0) - -#define CHK_CONN(obj) \ -do { \ - if (!obj->conn) { \ - SET_ERROR(obj, "HY000", TSDB_CODE_ODBC_INVALID_HANDLE, "connection closed or not ready"); \ - return SQL_ERROR; \ - } \ -} while (0); - -#define CHK_CONN_TAOS(obj) \ -do { \ - if (!obj->conn->taos) { \ - SET_ERROR(obj, "HY000", TSDB_CODE_ODBC_INVALID_HANDLE, "connection to data source closed or not ready"); \ - return SQL_ERROR; \ - } \ -} while (0); - -#define CHK_RS(r_091c, sql_091c, fmt_091c, ...) \ -do { \ - r_091c = SQL_ERROR; \ - int e = sql_091c->rs ? taos_errno(sql_091c->rs) : terrno; \ - if (e != TSDB_CODE_SUCCESS) { \ - SET_ERROR(sql_091c, "HY000", e, fmt_091c, ##__VA_ARGS__); \ - break; \ - } \ - r_091c = SQL_SUCCESS; \ -} while (0) - -#define NORM_STR_LENGTH(obj, ptr, len) \ -do { \ - if ((len) < 0 && (len)!=SQL_NTS) { \ - SET_ERROR((obj), "HY090", TSDB_CODE_ODBC_BAD_ARG, ""); \ - return SQL_ERROR; \ - } \ - if (len==SQL_NTS) len = (ptr) ? (SQLSMALLINT)strlen((const char*)(ptr)) : 0; \ -} while (0) - -#define PROFILING 0 - -#define PROFILE(statement) \ -do { \ - if (!PROFILING) { \ - statement; \ - break; \ - } \ - struct timeval tv0, tv1; \ - gettimeofday(&tv0, NULL); \ - statement; \ - gettimeofday(&tv1, NULL); \ - double delta = difftime(tv1.tv_sec, tv0.tv_sec); \ - delta *= 1000000; \ - delta += (double)(tv1.tv_usec-tv0.tv_usec); \ - delta /= 1000000; \ - D("%s: elapsed: [%.6f]s", #statement, delta); \ -} while (0) - -#define CHK_CONV(todb, statement) \ -do { \ - TSDB_CONV_CODE code_0c80 = (statement); \ - switch (code_0c80) { \ - case TSDB_CONV_OK: return SQL_SUCCESS; \ - case TSDB_CONV_OOM: \ - case TSDB_CONV_NOT_AVAIL: { \ - SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_OOR: { \ - SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_OOR, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_CHAR_NOT_NUM: \ - case TSDB_CONV_CHAR_NOT_TS: { \ - SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_CHAR_NOT_NUM, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_NOT_VALID_TS: { \ - SET_ERROR(sql, "22007", TSDB_CODE_ODBC_CONV_NOT_VALID_TS, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_TRUNC_FRACTION: { \ - SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC_FRAC, ""); \ - return todb ? SQL_ERROR : SQL_SUCCESS_WITH_INFO; \ - } break; \ - case TSDB_CONV_TRUNC: { \ - SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_TRUNC, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_SRC_TOO_LARGE: { \ - SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_SRC_TOO_LARGE, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_SRC_BAD_SEQ: { \ - SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_SRC_BAD_SEQ, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_SRC_INCOMPLETE: { \ - SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_SRC_INCOMPLETE, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_SRC_GENERAL: { \ - SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_SRC_GENERAL, ""); \ - return SQL_ERROR; \ - } break; \ - case TSDB_CONV_BAD_CHAR: { \ - SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_TRUNC, ""); \ - return SQL_ERROR; \ - } break; \ - default: { \ - DASSERTX(0, "internal logic error: %d", code_0c80); \ - return SQL_ERROR; /* never reached here */ \ - } break; \ - } \ -} while (0) - -typedef struct env_s env_t; -typedef struct conn_s conn_t; -typedef struct sql_s sql_t; -typedef struct taos_error_s taos_error_t; -typedef struct param_bind_s param_bind_t; - -struct param_bind_s { - SQLUSMALLINT ParameterNumber; - SQLSMALLINT ValueType; - SQLSMALLINT ParameterType; - SQLULEN LengthPrecision; - SQLSMALLINT ParameterScale; - SQLPOINTER ParameterValue; - SQLLEN *StrLen_or_Ind; - - unsigned int valid; -}; - -struct taos_error_s { - char *err_str; - int err_no; - - SQLCHAR sql_state[6]; -}; - -struct env_s { - uint64_t refcount; - unsigned int destroying:1; - - char env_locale[64]; - char env_charset[64]; - - taos_error_t err; -}; - -struct conn_s { - uint64_t refcount; - env_t *env; - - char client_enc[64]; // ODBC client that communicates with this driver - char server_enc[64]; // taos dynamic library that's loaded by this driver - - tsdb_conv_t *client_to_server; - tsdb_conv_t *server_to_client; - tsdb_conv_t *utf8_to_client; - tsdb_conv_t *utf16_to_utf8; - tsdb_conv_t *utf16_to_server; - tsdb_conv_t *client_to_utf8; - - TAOS *taos; - - taos_error_t err; -}; - -struct sql_s { - uint64_t refcount; - conn_t *conn; - - TAOS_STMT *stmt; - param_bind_t *params; - int n_params; - size_t rowlen; - size_t n_rows; - size_t ptr_offset; - - TAOS_RES *rs; - TAOS_ROW row; - - taos_error_t err; - unsigned int is_prepared:1; - unsigned int is_insert:1; - unsigned int is_executed:1; -}; - -typedef struct c_target_s c_target_t; -struct c_target_s { - SQLUSMALLINT col; - SQLSMALLINT ct; // c type: SQL_C_XXX - char *ptr; - SQLLEN len; - SQLLEN *soi; -}; - -static pthread_once_t init_once = PTHREAD_ONCE_INIT; -static void init_routine(void); - -static size_t do_field_display_size(TAOS_FIELD *field); - -static tsdb_conv_t* tsdb_conn_client_to_server(conn_t *conn) { - if (!conn->client_to_server) { - conn->client_to_server = tsdb_conv_open(conn->client_enc, conn->server_enc); - } - return conn->client_to_server; -} - -static tsdb_conv_t* tsdb_conn_server_to_client(conn_t *conn) { - if (!conn->server_to_client) { - conn->server_to_client = tsdb_conv_open(conn->server_enc, conn->client_enc); - } - return conn->server_to_client; -} - -static tsdb_conv_t* tsdb_conn_utf8_to_client(conn_t *conn) { - if (!conn->utf8_to_client) { - conn->utf8_to_client = tsdb_conv_open(UTF8_ENC, conn->client_enc); - } - return conn->utf8_to_client; -} - -static tsdb_conv_t* tsdb_conn_utf16_to_utf8(conn_t *conn) { - if (!conn->utf16_to_utf8) { - conn->utf16_to_utf8 = tsdb_conv_open(UTF16_ENC, UTF8_ENC); - } - return conn->utf16_to_utf8; -} - -static tsdb_conv_t* tsdb_conn_utf16_to_server(conn_t *conn) { - if (!conn->utf16_to_server) { - conn->utf16_to_server = tsdb_conv_open(UTF16_ENC, conn->server_enc); - } - return conn->utf16_to_server; -} - -static tsdb_conv_t* tsdb_conn_client_to_utf8(conn_t *conn) { - if (!conn->client_to_utf8) { - conn->client_to_utf8 = tsdb_conv_open(conn->client_enc, UTF8_ENC); - } - return conn->client_to_utf8; -} - -static void tsdb_conn_close_convs(conn_t *conn) { - if (conn->client_to_server) { - tsdb_conv_close(conn->client_to_server); - conn->client_to_server = NULL; - } - if (conn->server_to_client) { - tsdb_conv_close(conn->server_to_client); - conn->server_to_client = NULL; - } - if (conn->utf8_to_client) { - tsdb_conv_close(conn->utf8_to_client); - conn->utf8_to_client = NULL; - } - if (conn->utf16_to_utf8) { - tsdb_conv_close(conn->utf16_to_utf8); - conn->utf16_to_utf8 = NULL; - } - if (conn->utf16_to_server) { - tsdb_conv_close(conn->utf16_to_server); - conn->utf16_to_server = NULL; - } - if (conn->client_to_utf8) { - tsdb_conv_close(conn->client_to_utf8); - conn->client_to_utf8 = NULL; - } -} - -#define SFREE(buffer, v, src) \ -do { \ - const char *v_096a = (const char*)(v); \ - const char *src_6a = (const char*)(src); \ - if (v_096a && v_096a!=src_6a && !is_owned_by_stack_buffer((buffer), v_096a)) { \ - free((char*)v_096a); \ - } \ -} while (0) - -static SQLRETURN doSQLAllocEnv(SQLHENV *EnvironmentHandle) -{ - pthread_once(&init_once, init_routine); - - env_t *env = (env_t*)calloc(1, sizeof(*env)); - if (!env) return SQL_INVALID_HANDLE; - - DASSERT(INC_REF(env)>0); - - snprintf(env->env_locale, sizeof(env->env_locale), "%s", tsLocale); - snprintf(env->env_charset, sizeof(env->env_charset), "%s", tsCharset); - - *EnvironmentHandle = env; - - CLR_ERROR(env); - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) -{ - SQLRETURN r; - r = doSQLAllocEnv(EnvironmentHandle); - return r; -} - -static SQLRETURN doSQLFreeEnv(SQLHENV EnvironmentHandle) -{ - env_t *env = (env_t*)EnvironmentHandle; - if (!env) return SQL_INVALID_HANDLE; - - DASSERT(GET_REF(env)==1); - - DASSERT(!env->destroying); - - env->destroying = 1; - DASSERT(env->destroying == 1); - - DASSERT(DEC_REF(env)==0); - - FREE_ERROR(env); - free(env); - - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) -{ - SQLRETURN r; - r = doSQLFreeEnv(EnvironmentHandle); - return r; -} - -static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle, - SQLHDBC *ConnectionHandle) -{ - env_t *env = (env_t*)EnvironmentHandle; - if (!env) return SQL_INVALID_HANDLE; - - if (!ConnectionHandle) { - SET_ERROR(env, "HY009", TSDB_CODE_ODBC_BAD_ARG, "ConnectionHandle [%p] not valid", ConnectionHandle); - return SQL_ERROR; - } - - DASSERT(INC_REF(env)>1); - - conn_t *conn = NULL; - do { - conn = (conn_t*)calloc(1, sizeof(*conn)); - if (!conn) { - SET_ERROR(env, "HY001", TSDB_CODE_ODBC_OOM, ""); - break; - } - - conn->env = env; - - snprintf(conn->client_enc, sizeof(conn->client_enc), "%s", conn->env->env_charset); - snprintf(conn->server_enc, sizeof(conn->server_enc), "%s", conn->env->env_charset); - - *ConnectionHandle = conn; - - DASSERT(INC_REF(conn)>0); - - return SQL_SUCCESS; - } while (0); - - DASSERT(DEC_REF(env)>0); - - return SQL_ERROR; -} - -SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, - SQLHDBC *ConnectionHandle) -{ - SQLRETURN r; - r = doSQLAllocConnect(EnvironmentHandle, ConnectionHandle); - return r; -} - -static SQLRETURN doSQLFreeConnect(SQLHDBC ConnectionHandle) -{ - conn_t *conn = (conn_t*)ConnectionHandle; - if (!conn) return SQL_INVALID_HANDLE; - - DASSERT(GET_REF(conn)==1); - - DASSERT(conn->env); - - do { - if (conn->taos) { - taos_close(conn->taos); - conn->taos = NULL; - } - - DASSERT(DEC_REF(conn->env)>0); - DASSERT(DEC_REF(conn)==0); - - conn->env = NULL; - FREE_ERROR(conn); - tsdb_conn_close_convs(conn); - free(conn); - } while (0); - - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) -{ - SQLRETURN r; - r = doSQLFreeConnect(ConnectionHandle); - return r; -} - -static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, - SQLCHAR *ServerName, SQLSMALLINT NameLength1, - SQLCHAR *UserName, SQLSMALLINT NameLength2, - SQLCHAR *Authentication, SQLSMALLINT NameLength3) -{ - stack_buffer_t buffer; buffer.next = 0; - - conn_t *conn = (conn_t*)ConnectionHandle; - if (!conn) return SQL_ERROR; - - if (conn->taos) { - SET_ERROR(conn, "08002", TSDB_CODE_ODBC_CONNECTION_BUSY, "connection still in use"); - return SQL_ERROR; - } - - NORM_STR_LENGTH(conn, ServerName, NameLength1); - NORM_STR_LENGTH(conn, UserName, NameLength2); - NORM_STR_LENGTH(conn, Authentication, NameLength3); - - if (NameLength1>SQL_MAX_DSN_LENGTH) { - SET_ERROR(conn, "HY090", TSDB_CODE_ODBC_BAD_ARG, ""); - return SQL_ERROR; - } - - tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn); - const char *dsn = NULL; - const char *uid = NULL; - const char *pwd = NULL; - const char *svr = NULL; - char server[4096]; server[0] = '\0'; - - do { - tsdb_conv(client_to_server, &buffer, (const char*)ServerName, (size_t)NameLength1, &dsn, NULL); - tsdb_conv(client_to_server, &buffer, (const char*)UserName, (size_t)NameLength2, &uid, NULL); - tsdb_conv(client_to_server, &buffer, (const char*)Authentication, (size_t)NameLength3, &pwd, NULL); - int n = SQLGetPrivateProfileString(dsn, "Server", "", server, sizeof(server)-1, "Odbc.ini"); - if (n<=0) { - snprintf(server, sizeof(server), "localhost:6030"); // all 7-bit ascii - } - tsdb_conv(client_to_server, &buffer, (const char*)server, (size_t)strlen(server), &svr, NULL); - - if ((!dsn) || (!uid) || (!pwd) || (!svr)) { - SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, ""); - break; - } - - char *ip = NULL; - int port = 0; - char *p = strchr(svr, ':'); - if (p) { - ip = strndup(svr, (size_t)(p-svr)); - port = atoi(p+1); - } - - // TODO: data-race - // TODO: shall receive ip/port from odbc.ini - conn->taos = taos_connect(ip, uid, pwd, NULL, (uint16_t)port); - if (!conn->taos) { - SET_ERROR(conn, "08001", terrno, "failed to connect to data source for DSN[%s] @[%s:%d]", dsn, ip, port); - break; - } - } while (0); - - tsdb_conv_free(client_to_server, dsn, &buffer, (const char*)ServerName); - tsdb_conv_free(client_to_server, uid, &buffer, (const char*)UserName); - tsdb_conv_free(client_to_server, pwd, &buffer, (const char*)Authentication); - tsdb_conv_free(client_to_server, svr, &buffer, (const char*)server); - - return conn->taos ? SQL_SUCCESS : SQL_ERROR; -} - -SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, - SQLCHAR *ServerName, SQLSMALLINT NameLength1, - SQLCHAR *UserName, SQLSMALLINT NameLength2, - SQLCHAR *Authentication, SQLSMALLINT NameLength3) -{ - SQLRETURN r; - r = doSQLConnect(ConnectionHandle, ServerName, NameLength1, - UserName, NameLength2, - Authentication, NameLength3); - return r; -} - -static SQLRETURN doSQLDisconnect(SQLHDBC ConnectionHandle) -{ - conn_t *conn = (conn_t*)ConnectionHandle; - if (!conn) return SQL_INVALID_HANDLE; - - if (conn->taos) { - taos_close(conn->taos); - conn->taos = NULL; - } - - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) -{ - SQLRETURN r; - r = doSQLDisconnect(ConnectionHandle); - return r; -} - -static SQLRETURN doSQLAllocStmt(SQLHDBC ConnectionHandle, - SQLHSTMT *StatementHandle) -{ - conn_t *conn = (conn_t*)ConnectionHandle; - if (!conn) return SQL_INVALID_HANDLE; - - if (!StatementHandle) { - SET_ERROR(conn, "HY009", TSDB_CODE_ODBC_BAD_ARG, "StatementHandle [%p] not valid", StatementHandle); - return SQL_ERROR; - } - - DASSERT(INC_REF(conn)>1); - - do { - sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); - if (!sql) { - SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, ""); - break; - } - - sql->conn = conn; - DASSERT(INC_REF(sql)>0); - - *StatementHandle = sql; - - return SQL_SUCCESS; - } while (0); - - DASSERT(DEC_REF(conn)>0); - - return SQL_ERROR; -} - -SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, - SQLHSTMT *StatementHandle) -{ - SQLRETURN r; - r = doSQLAllocStmt(ConnectionHandle, StatementHandle); - return r; -} - -static SQLRETURN doSQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle) -{ - switch (HandleType) { - case SQL_HANDLE_ENV: { - SQLHENV env = {0}; - if (!OutputHandle) return SQL_ERROR; - SQLRETURN r = doSQLAllocEnv(&env); - if (r==SQL_SUCCESS) *OutputHandle = env; - return r; - } break; - case SQL_HANDLE_DBC: { - SQLRETURN r = doSQLAllocConnect(InputHandle, OutputHandle); - return r; - } break; - case SQL_HANDLE_STMT: { - SQLRETURN r = doSQLAllocStmt(InputHandle, OutputHandle); - return r; - } break; - default: { - return SQL_ERROR; - } break; - } -} - -SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle) -{ - SQLRETURN r; - r = doSQLAllocHandle(HandleType, InputHandle, OutputHandle); - return r; -} - -static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, - SQLUSMALLINT Option) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - switch (Option) { - case SQL_CLOSE: return SQL_SUCCESS; - case SQL_DROP: break; - case SQL_UNBIND: - case SQL_RESET_PARAMS: { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "free statement with Option[%x] not supported yet", Option); - return SQL_ERROR; - } break; - default: { - SET_ERROR(sql, "HY092", TSDB_CODE_ODBC_OUT_OF_RANGE, "free statement with Option[%x] not supported yet", Option); - return SQL_ERROR; - } break; - } - - DASSERT(GET_REF(sql)==1); - - if (sql->rs) { - taos_free_result(sql->rs); - sql->rs = NULL; - } - - if (sql->stmt) { - taos_stmt_close(sql->stmt); - sql->stmt = NULL; - } - - if (sql->params) { - free(sql->params); - sql->params = NULL; - } - sql->n_params = 0; - - DASSERT(DEC_REF(sql->conn)>0); - DASSERT(DEC_REF(sql)==0); - - sql->conn = NULL; - - FREE_ERROR(sql); - - free(sql); - - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, - SQLUSMALLINT Option) -{ - SQLRETURN r; - r = doSQLFreeStmt(StatementHandle, Option); - return r; -} - -static SQLRETURN do_exec_direct(sql_t *sql, TSDB_CONV_CODE code, const char *statement) { - if (code) CHK_CONV(1, code); - DASSERT(code==TSDB_CONV_OK); - - SQLRETURN r = SQL_ERROR; - do { - sql->rs = taos_query(sql->conn->taos, statement); - CHK_RS(r, sql, "failed to execute"); - } while (0); - - return r; -} - -static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, - SQLCHAR *StatementText, SQLINTEGER TextLength) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - conn_t *conn = sql->conn; - - NORM_STR_LENGTH(sql, StatementText, TextLength); - - if (sql->rs) { - taos_free_result(sql->rs); - sql->rs = NULL; - sql->row = NULL; - } - - if (sql->stmt) { - taos_stmt_close(sql->stmt); - sql->stmt = NULL; - } - - if (sql->params) { - free(sql->params); - sql->params = NULL; - } - sql->n_params = 0; - - SQLRETURN r = SQL_SUCCESS; - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn); - const char *stxt = NULL; - do { - TSDB_CONV_CODE code = tsdb_conv(client_to_server, &buffer, (const char*)StatementText, (size_t)TextLength, &stxt, NULL); - r = do_exec_direct(sql, code, stxt); - } while (0); - tsdb_conv_free(client_to_server, stxt, &buffer, (const char*)StatementText); - - return r; -} - -SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, - SQLCHAR *StatementText, SQLINTEGER TextLength) -{ - SQLRETURN r; - r = doSQLExecDirect(StatementHandle, StatementText, TextLength); - return r; -} - -static SQLRETURN doSQLExecDirectW(SQLHSTMT hstmt, SQLWCHAR *szSqlStr, SQLINTEGER cbSqlStr) -{ - sql_t *sql = (sql_t*)hstmt; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - conn_t *conn = sql->conn; - - if (!szSqlStr) { - SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "szSqlStr [%p] not allowed", szSqlStr); - return SQL_ERROR; - } - if (cbSqlStr < 0) { - SET_ERROR(sql, "HY090", TSDB_CODE_ODBC_BAD_ARG, ""); - return SQL_ERROR; - } - - SQLRETURN r = SQL_SUCCESS; - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_server = tsdb_conn_utf16_to_server(conn); - const char *stxt = NULL; - do { - size_t slen = (size_t)cbSqlStr * sizeof(*szSqlStr); - TSDB_CONV_CODE code = tsdb_conv(utf16_to_server, &buffer, (const char*)szSqlStr, slen, &stxt, NULL); - r = do_exec_direct(sql, code, stxt); - } while (0); - tsdb_conv_free(utf16_to_server, stxt, &buffer, (const char*)szSqlStr); - - return r; -} - -SQLRETURN SQL_API SQLExecDirectW(SQLHSTMT hstmt, SQLWCHAR *szSqlStr, SQLINTEGER cbSqlStr) -{ - SQLRETURN r = doSQLExecDirectW(hstmt, szSqlStr, cbSqlStr); - return r; -} - -static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle, - SQLSMALLINT *ColumnCount) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - if (sql->is_insert) { - if (ColumnCount) { - *ColumnCount = 0; - } - return SQL_SUCCESS; - } - - if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); - return SQL_ERROR; - } - - int fields = taos_field_count(sql->rs); - if (ColumnCount) { - *ColumnCount = (SQLSMALLINT)fields; - } - - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, - SQLSMALLINT *ColumnCount) -{ - SQLRETURN r; - r = doSQLNumResultCols(StatementHandle, ColumnCount); - return r; -} - -static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle, - SQLLEN *RowCount) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_ERROR; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - // ref: https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlrowcount-function?view=sql-server-ver15 - // Summary - // SQLRowCount returns the number of rows affected by an UPDATE, INSERT, or DELETE statement; - // an SQL_ADD, SQL_UPDATE_BY_BOOKMARK, or SQL_DELETE_BY_BOOKMARK operation in SQLBulkOperations; - // or an SQL_UPDATE or SQL_DELETE operation in SQLSetPos. - - // how to fetch affected rows from taos? - // taos_affected_rows? - - if (1) { - SET_ERROR(sql, "IM001", TSDB_CODE_ODBC_NOT_SUPPORT, ""); - // if (RowCount) *RowCount = 0; - return SQL_SUCCESS_WITH_INFO; - } - - if (!sql->is_insert) { - if (RowCount) *RowCount = 0; - return SQL_SUCCESS; - } - - if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); - return SQL_ERROR; - } - - int rows = taos_affected_rows(sql->rs); - if (RowCount) { - *RowCount = rows; - } - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, - SQLLEN *RowCount) -{ - SQLRETURN r; - r = doSQLRowCount(StatementHandle, RowCount); - return r; -} - -static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, - SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, - SQLSMALLINT *StringLength, SQLLEN *NumericAttribute ) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_ERROR; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); - return SQL_ERROR; - } - - int nfields = taos_field_count(sql->rs); - TAOS_FIELD *fields = taos_fetch_fields(sql->rs); - - if (nfields==0 || fields==NULL) { - SET_ERROR(sql, "07005", TSDB_CODE_ODBC_NO_FIELDS, ""); - return SQL_ERROR; - } - - if (ColumnNumber<=0 || ColumnNumber>nfields) { - SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber); - return SQL_ERROR; - } - - TAOS_FIELD *field = fields + ColumnNumber-1; - - switch (FieldIdentifier) { - case SQL_COLUMN_DISPLAY_SIZE: { - *NumericAttribute = (SQLLEN)do_field_display_size(field); - } break; - case SQL_COLUMN_LABEL: { - // todo: check BufferLength - size_t n = sizeof(field->name); - strncpy(CharacterAttribute, field->name, (n>BufferLength ? (size_t)BufferLength : n)); - } break; - case SQL_COLUMN_UNSIGNED: { - *NumericAttribute = SQL_FALSE; - } break; - default: { - SET_ERROR(sql, "HY091", TSDB_CODE_ODBC_OUT_OF_RANGE, - "FieldIdentifier[%d/0x%x] for Column [%d] not supported yet", - FieldIdentifier, FieldIdentifier, ColumnNumber); - return SQL_ERROR; - } break; - } - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLColAttribute(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, - SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, - SQLSMALLINT *StringLength, SQLLEN *NumericAttribute ) -{ - SQLRETURN r; - r = doSQLColAttribute(StatementHandle, ColumnNumber, FieldIdentifier, - CharacterAttribute, BufferLength, - StringLength, NumericAttribute); - return r; -} - -static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, - SQLPOINTER TargetValue, SQLLEN BufferLength, - SQLLEN *StrLen_or_Ind) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - conn_t *conn = sql->conn; - - if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); - return SQL_ERROR; - } - - if (!sql->row) { - SET_ERROR(sql, "24000", TSDB_CODE_ODBC_INVALID_CURSOR, ""); - return SQL_ERROR; - } - - int nfields = taos_field_count(sql->rs); - TAOS_FIELD *fields = taos_fetch_fields(sql->rs); - - if (ColumnNumber<=0 || ColumnNumber>nfields) { - SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber); - return SQL_ERROR; - } - - if (TargetValue == NULL) { - SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "NULL TargetValue not allowed for col [%d]", ColumnNumber); - return SQL_ERROR; - } - if (BufferLength<0) { - SET_ERROR(sql, "HY090", TSDB_CODE_ODBC_BAD_ARG, ""); - return SQL_ERROR; - } - - TAOS_FIELD *field = fields + ColumnNumber-1; - void *row = sql->row[ColumnNumber-1]; - - if (!row) { - if (!StrLen_or_Ind) { - SET_ERROR(sql, "22002", TSDB_CODE_ODBC_BAD_ARG, "NULL StrLen_or_Ind not allowed for col [%d]", ColumnNumber); - return SQL_ERROR; - } - *StrLen_or_Ind = SQL_NULL_DATA; - return SQL_SUCCESS; - } - - c_target_t target = {0}; - target.col = ColumnNumber; - target.ct = TargetType; - target.ptr = TargetValue; - target.len = BufferLength; - target.soi = StrLen_or_Ind; - - switch (field->type) { - case TSDB_DATA_TYPE_BOOL: - case TSDB_DATA_TYPE_TINYINT: - case TSDB_DATA_TYPE_SMALLINT: - case TSDB_DATA_TYPE_INT: - case TSDB_DATA_TYPE_BIGINT: { - int64_t v; - switch (field->type) { - case TSDB_DATA_TYPE_BOOL: v = *(int8_t*)row; if (v) v = 1; break; - case TSDB_DATA_TYPE_TINYINT: v = *(int8_t*)row; break; - case TSDB_DATA_TYPE_SMALLINT: v = *(int16_t*)row; break; - case TSDB_DATA_TYPE_INT: v = *(int32_t*)row; break; - case TSDB_DATA_TYPE_BIGINT: // fall through - default: v = *(int64_t*)row; break; - } - switch (target.ct) { - case SQL_C_BIT: { - CHK_CONV(0, tsdb_int64_to_bit(v, TargetValue)); - } break; - case SQL_C_TINYINT: { - CHK_CONV(0, tsdb_int64_to_tinyint(v, TargetValue)); - } break; - case SQL_C_SHORT: { - CHK_CONV(0, tsdb_int64_to_smallint(v, TargetValue)); - } break; - case SQL_C_LONG: { - CHK_CONV(0, tsdb_int64_to_int(v, TargetValue)); - } break; - case SQL_C_SBIGINT: { - CHK_CONV(0, tsdb_int64_to_bigint(v, TargetValue)); - } break; - case SQL_C_FLOAT: { - CHK_CONV(0, tsdb_int64_to_float(v, TargetValue)); - } break; - case SQL_C_DOUBLE: { - CHK_CONV(0, tsdb_int64_to_double(v, TargetValue)); - } break; - case SQL_C_CHAR: { - tsdb_conv_t *utf8_to_client = tsdb_conn_utf8_to_client(conn); - size_t len = (size_t)BufferLength; - TSDB_CONV_CODE code = tsdb_conv_write_int64(utf8_to_client, v, (char*)TargetValue, &len); - if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len; - CHK_CONV(0, code); - } break; - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, - "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", - taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); - return SQL_ERROR; - } - } - } break; - case TSDB_DATA_TYPE_FLOAT: { - float v = *(float*)row; - switch (target.ct) { - case SQL_C_FLOAT: { - *(float*)TargetValue = v; - return SQL_SUCCESS; - } break; - case SQL_C_DOUBLE: { - *(double*)TargetValue = v; - return SQL_SUCCESS; - } break; - case SQL_C_CHAR: { - tsdb_conv_t *utf8_to_client = tsdb_conn_utf8_to_client(conn); - size_t len = (size_t)BufferLength; - TSDB_CONV_CODE code = tsdb_conv_write_double(utf8_to_client, v, (char*)TargetValue, &len); - if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len; - CHK_CONV(0, code); - } break; - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, - "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", - taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); - return SQL_ERROR; - } - } - } break; - case TSDB_DATA_TYPE_DOUBLE: { - double v = *(double*)row; - switch (target.ct) { - case SQL_C_DOUBLE: { - *(double*)TargetValue = v; - return SQL_SUCCESS; - } break; - case SQL_C_CHAR: { - tsdb_conv_t *utf8_to_client = tsdb_conn_utf8_to_client(conn); - size_t len = (size_t)BufferLength; - TSDB_CONV_CODE code = tsdb_conv_write_double(utf8_to_client, v, (char*)TargetValue, &len); - if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len; - CHK_CONV(0, code); - } break; - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, - "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", - taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); - return SQL_ERROR; - } - } - } break; - case TSDB_DATA_TYPE_TIMESTAMP: { - SQL_TIMESTAMP_STRUCT ts = {0}; - int64_t v = *(int64_t*)row; - time_t t = v/1000; - struct tm vtm = {0}; - localtime_r(&t, &vtm); - ts.year = (SQLSMALLINT)(vtm.tm_year + 1900); - ts.month = (SQLUSMALLINT)(vtm.tm_mon + 1); - ts.day = (SQLUSMALLINT)(vtm.tm_mday); - ts.hour = (SQLUSMALLINT)(vtm.tm_hour); - ts.minute = (SQLUSMALLINT)(vtm.tm_min); - ts.second = (SQLUSMALLINT)(vtm.tm_sec); - ts.fraction = (SQLUINTEGER)(v%1000 * 1000000); - switch (target.ct) { - case SQL_C_SBIGINT: { - *(int64_t*)TargetValue = v; - return SQL_SUCCESS; - } break; - case SQL_C_CHAR: { - tsdb_conv_t *utf8_to_client = tsdb_conn_utf8_to_client(conn); - size_t len = (size_t)BufferLength; - TSDB_CONV_CODE code = tsdb_conv_write_timestamp(utf8_to_client, ts, (char*)TargetValue, &len); - if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len; - CHK_CONV(0, code); - } break; - case SQL_C_TYPE_TIMESTAMP: { - *(SQL_TIMESTAMP_STRUCT*)TargetValue = ts; - return SQL_SUCCESS; - } break; - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, - "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", - taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); - return SQL_ERROR; - } - } - } break; - case TSDB_DATA_TYPE_BINARY: { - size_t field_bytes = (size_t)field->bytes; - field_bytes -= VARSTR_HEADER_SIZE; - switch (target.ct) { - case SQL_C_CHAR: { - // taos cares nothing about what would be stored in 'binary' as most sql implementations do - // but the client requires to fetch it as a SQL_C_CHAR - // thus, we first try to decode binary to client charset - // if failed, we then do hex-serialization - - tsdb_conv_t *server_to_client = tsdb_conn_server_to_client(conn); - size_t slen = strnlen((const char*)row, field_bytes); - size_t len = (size_t)BufferLength; - TSDB_CONV_CODE code = tsdb_conv_write(server_to_client, - (const char*)row, &slen, - (char*)TargetValue, &len); - if (code==TSDB_CONV_OK) { - if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)((size_t)BufferLength - len); - CHK_CONV(0, code); - // code never reached here - } - - // todo: hex-serialization - const char *bad = ""; - int n = snprintf((char*)TargetValue, (size_t)BufferLength, "%s", bad); - // what if n < 0 ? - if (StrLen_or_Ind) *StrLen_or_Ind = n; - CHK_CONV(0, n>=BufferLength ? TSDB_CONV_TRUNC : TSDB_CONV_OK); - } break; - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, - "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", - taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); - return SQL_ERROR; - } - } - } break; - case TSDB_DATA_TYPE_NCHAR: { - size_t field_bytes = (size_t)field->bytes; - field_bytes -= VARSTR_HEADER_SIZE; - switch (target.ct) { - case SQL_C_CHAR: { - tsdb_conv_t *server_to_client = tsdb_conn_server_to_client(conn); - size_t slen = strnlen((const char*)row, field_bytes); - size_t len = (size_t)BufferLength; - TSDB_CONV_CODE code = tsdb_conv_write(server_to_client, - (const char*)row, &slen, - (char*)TargetValue, &len); - if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)((size_t)BufferLength - len); - CHK_CONV(0, code); - } break; - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, - "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", - taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); - return SQL_ERROR; - } - } - } break; - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for col [%d]", - taos_data_type(field->type), field->type, field->type, - sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); - return SQL_ERROR; - } break; - } -} - -SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, - SQLPOINTER TargetValue, SQLLEN BufferLength, - SQLLEN *StrLen_or_Ind) -{ - SQLRETURN r; - r = doSQLGetData(StatementHandle, ColumnNumber, TargetType, - TargetValue, BufferLength, - StrLen_or_Ind); - return r; -} - -static SQLRETURN doSQLFetch(SQLHSTMT StatementHandle) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); - return SQL_ERROR; - } - - sql->row = taos_fetch_row(sql->rs); - return sql->row ? SQL_SUCCESS : SQL_NO_DATA; -} - -SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) -{ - SQLRETURN r; - r = doSQLFetch(StatementHandle); - return r; -} - -static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, - SQLCHAR *StatementText, SQLINTEGER TextLength) -{ - stack_buffer_t buffer; buffer.next = 0; - - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - conn_t *conn = sql->conn; - - NORM_STR_LENGTH(sql, StatementText, TextLength); - - if (sql->rs) { - taos_free_result(sql->rs); - sql->rs = NULL; - sql->row = NULL; - } - - if (sql->stmt) { - taos_stmt_close(sql->stmt); - sql->stmt = NULL; - } - - if (sql->params) { - free(sql->params); - sql->params = NULL; - } - sql->n_params = 0; - sql->is_insert = 0; - - do { - sql->stmt = taos_stmt_init(sql->conn->taos); - if (!sql->stmt) { - SET_ERROR(sql, "HY001", terrno, "failed to initialize TAOS statement internally"); - break; - } - - tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn); - const char *stxt = NULL; - int ok = 0; - do { - tsdb_conv(client_to_server, &buffer, (const char*)StatementText, (size_t)TextLength, &stxt, NULL); - if ((!stxt)) { - SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); - break; - } - - int r = taos_stmt_prepare(sql->stmt, stxt, (unsigned long)strlen(stxt)); - if (r) { - SET_ERROR(sql, "HY000", r, "failed to prepare a TAOS statement"); - break; - } - sql->is_prepared = 1; - - int is_insert = 0; - r = taos_stmt_is_insert(sql->stmt, &is_insert); - if (r) { - SET_ERROR(sql, "HY000", r, "failed to determine if a prepared-statement is of insert"); - break; - } - sql->is_insert = is_insert ? 1 : 0; - - int params = 0; - r = taos_stmt_num_params(sql->stmt, ¶ms); - if (r) { - SET_ERROR(sql, "HY000", terrno, "fetch num of statement params failed"); - break; - } - DASSERT(params>=0); - - if (params>0) { - param_bind_t *ar = (param_bind_t*)calloc(1, ((size_t)params) * sizeof(*ar)); - if (!ar) { - SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); - break; - } - sql->params = ar; - } - - sql->n_params = params; - - ok = 1; - } while (0); - - tsdb_conv_free(client_to_server, stxt, &buffer, (const char*)StatementText); - - if (!ok) { - taos_stmt_close(sql->stmt); - sql->stmt = NULL; - sql->is_prepared = 0; - sql->is_insert = 0; - sql->is_executed = 0; - } - } while (0); - - return sql->stmt ? SQL_SUCCESS : SQL_ERROR; -} - -SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, - SQLCHAR *StatementText, SQLINTEGER TextLength) -{ - SQLRETURN r; - r = doSQLPrepare(StatementHandle, StatementText, TextLength); - return r; -} - -static const int yes = 1; -static const int no = 0; - -static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bind_t *param, TAOS_BIND *bind) -{ - if (!param->valid) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "parameter [@%d] not bound yet", idx+1); - return SQL_ERROR; - } - if (param->ParameterValue==NULL) { - SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "ParameterValue [@%p] not allowed", param->ParameterValue); - return SQL_ERROR; - } - if (param->StrLen_or_Ind==NULL) { - SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "StrLen_or_Ind [@%p] not allowed", param->StrLen_or_Ind); - return SQL_ERROR; - } - - conn_t *conn = sql->conn; - - unsigned char *paramValue = param->ParameterValue; - SQLSMALLINT valueType = param->ValueType; - SQLLEN *soi = param->StrLen_or_Ind; - - size_t offset = ((size_t)idx_row) * sql->rowlen + sql->ptr_offset; - - paramValue += offset; - soi = (SQLLEN*)((char*)soi + offset); - - - if (*soi == SQL_NULL_DATA) { - bind->is_null = (int*)&yes; - return SQL_SUCCESS; - } - bind->is_null = (int*)&no; - int tsdb_type = 0; // taos internal data tsdb_type to be bound to - int tsdb_bytes = 0; // we don't rely much on 'tsdb_bytes' here, we delay until taos to check it internally - if (sql->is_insert) { - int r = taos_stmt_get_param(sql->stmt, idx, &tsdb_type, &tsdb_bytes); - if (r) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OUT_OF_RANGE, "parameter [@%d] not valid", idx+1); - return SQL_ERROR; - } - } else { - // we don't have correspondent data type from taos api - // we have to give a good guess here - switch (valueType) { - case SQL_C_BIT: { - tsdb_type = TSDB_DATA_TYPE_BOOL; - } break; - case SQL_C_STINYINT: - case SQL_C_TINYINT: { - tsdb_type = TSDB_DATA_TYPE_TINYINT; - } break; - case SQL_C_SSHORT: - case SQL_C_SHORT: { - tsdb_type = TSDB_DATA_TYPE_SMALLINT; - } break; - case SQL_C_SLONG: - case SQL_C_LONG: { - tsdb_type = TSDB_DATA_TYPE_INT; - } break; - case SQL_C_SBIGINT: { - tsdb_type = TSDB_DATA_TYPE_BIGINT; - } break; - case SQL_C_FLOAT: { - tsdb_type = TSDB_DATA_TYPE_FLOAT; - } break; - case SQL_C_DOUBLE: { - tsdb_type = TSDB_DATA_TYPE_DOUBLE; - } break; - case SQL_C_TIMESTAMP: { - tsdb_type = TSDB_DATA_TYPE_TIMESTAMP; - } break; - case SQL_C_CHAR: { - tsdb_type = TSDB_DATA_TYPE_BINARY; - tsdb_bytes = SQL_NTS; - } break; - case SQL_C_WCHAR: { - tsdb_type = TSDB_DATA_TYPE_NCHAR; - tsdb_bytes = SQL_NTS; - } break; - case SQL_C_USHORT: - case SQL_C_ULONG: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - idx+1); - return SQL_ERROR; - } break; - } - } - - // ref: https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/converting-data-from-c-to-sql-data-types?view=sql-server-ver15 - switch (tsdb_type) { - case TSDB_DATA_TYPE_BOOL: { - bind->buffer_type = tsdb_type; - bind->buffer_length = sizeof(bind->u.b); - bind->buffer = &bind->u.b; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_BIT: { - CHK_CONV(1, tsdb_int64_to_bit(*(int8_t*)paramValue, &bind->u.b)); - } break; - case SQL_C_TINYINT: - case SQL_C_STINYINT: { - CHK_CONV(1, tsdb_int64_to_bit(*(int8_t*)paramValue, &bind->u.b)); - } break; - case SQL_C_SHORT: - case SQL_C_SSHORT: { - CHK_CONV(1, tsdb_int64_to_bit(*(int16_t*)paramValue, &bind->u.b)); - } break; - case SQL_C_LONG: - case SQL_C_SLONG: { - CHK_CONV(1, tsdb_int64_to_bit(*(int32_t*)paramValue, &bind->u.b)); - } break; - case SQL_C_SBIGINT: { - CHK_CONV(1, tsdb_int64_to_bit(*(int64_t*)paramValue, &bind->u.b)); - } break; - case SQL_C_FLOAT: { - CHK_CONV(1, tsdb_double_to_bit(*(float*)paramValue, &bind->u.b)); - } break; - case SQL_C_DOUBLE: { - CHK_CONV(1, tsdb_double_to_bit(*(double*)paramValue, &bind->u.b)); - } break; - case SQL_C_CHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - CHK_CONV(1, tsdb_conv_chars_to_bit(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.b)); - } break; - case SQL_C_WCHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_bit(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.b)); - } break; - case SQL_C_USHORT: - case SQL_C_ULONG: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_TINYINT: { - bind->buffer_type = tsdb_type; - bind->buffer_length = sizeof(bind->u.v1); - bind->buffer = &bind->u.v1; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_BIT: { - CHK_CONV(1, tsdb_int64_to_tinyint(*(int8_t*)paramValue, &bind->u.v1)); - } break; - case SQL_C_STINYINT: - case SQL_C_TINYINT: { - CHK_CONV(1, tsdb_int64_to_tinyint(*(int8_t*)paramValue, &bind->u.v1)); - } break; - case SQL_C_SSHORT: - case SQL_C_SHORT: { - CHK_CONV(1, tsdb_int64_to_tinyint(*(int16_t*)paramValue, &bind->u.v1)); - } break; - case SQL_C_SLONG: - case SQL_C_LONG: { - CHK_CONV(1, tsdb_int64_to_tinyint(*(int32_t*)paramValue, &bind->u.v1)); - } break; - case SQL_C_SBIGINT: { - CHK_CONV(1, tsdb_int64_to_tinyint(*(int64_t*)paramValue, &bind->u.v1)); - } break; - case SQL_C_CHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - CHK_CONV(1, tsdb_conv_chars_to_tinyint(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v1)); - // CHK_CONV(1, tsdb_chars_to_tinyint((const char *)paramValue, (size_t)*soi, &bind->u.v1)); - } break; - case SQL_C_WCHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_tinyint(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v1)); - } break; - case SQL_C_USHORT: - case SQL_C_ULONG: - case SQL_C_FLOAT: - case SQL_C_DOUBLE: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_SMALLINT: { - bind->buffer_type = tsdb_type; - bind->buffer_length = sizeof(bind->u.v2); - bind->buffer = &bind->u.v2; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_BIT: { - CHK_CONV(1, tsdb_int64_to_smallint(*(int8_t*)paramValue, &bind->u.v2)); - } break; - case SQL_C_STINYINT: - case SQL_C_TINYINT: { - CHK_CONV(1, tsdb_int64_to_smallint(*(int8_t*)paramValue, &bind->u.v2)); - } break; - case SQL_C_SSHORT: - case SQL_C_SHORT: { - CHK_CONV(1, tsdb_int64_to_smallint(*(int16_t*)paramValue, &bind->u.v2)); - } break; - case SQL_C_SLONG: - case SQL_C_LONG: { - CHK_CONV(1, tsdb_int64_to_smallint(*(int32_t*)paramValue, &bind->u.v2)); - } break; - case SQL_C_SBIGINT: { - CHK_CONV(1, tsdb_int64_to_smallint(*(int64_t*)paramValue, &bind->u.v2)); - } break; - case SQL_C_CHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - CHK_CONV(1, tsdb_conv_chars_to_smallint(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v2)); - // CHK_CONV(1, tsdb_chars_to_smallint((const char*)paramValue, (size_t)*soi, &bind->u.v2)); - } break; - case SQL_C_WCHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_smallint(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v2)); - } break; - case SQL_C_USHORT: - case SQL_C_ULONG: - case SQL_C_FLOAT: - case SQL_C_DOUBLE: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_INT: { - bind->buffer_type = tsdb_type; - bind->buffer_length = sizeof(bind->u.v4); - bind->buffer = &bind->u.v4; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_BIT: { - CHK_CONV(1, tsdb_int64_to_int(*(int8_t*)paramValue, &bind->u.v4)); - } break; - case SQL_C_STINYINT: - case SQL_C_TINYINT: { - CHK_CONV(1, tsdb_int64_to_int(*(int8_t*)paramValue, &bind->u.v4)); - } break; - case SQL_C_SSHORT: - case SQL_C_SHORT: { - CHK_CONV(1, tsdb_int64_to_int(*(int16_t*)paramValue, &bind->u.v4)); - } break; - case SQL_C_SLONG: - case SQL_C_LONG: { - CHK_CONV(1, tsdb_int64_to_int(*(int32_t*)paramValue, &bind->u.v4)); - } break; - case SQL_C_SBIGINT: { - CHK_CONV(1, tsdb_int64_to_int(*(int64_t*)paramValue, &bind->u.v4)); - } break; - case SQL_C_CHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - CHK_CONV(1, tsdb_conv_chars_to_int(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v4)); - // CHK_CONV(1, tsdb_chars_to_int((const char*)paramValue, (size_t)*soi, &bind->u.v4)); - } break; - case SQL_C_WCHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_int(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v4)); - } break; - case SQL_C_USHORT: - case SQL_C_ULONG: - case SQL_C_FLOAT: - case SQL_C_DOUBLE: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_BIGINT: { - bind->buffer_type = tsdb_type; - bind->buffer_length = sizeof(bind->u.v8); - bind->buffer = &bind->u.v8; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_BIT: { - CHK_CONV(1, tsdb_int64_to_bigint(*(int8_t*)paramValue, &bind->u.v8)); - } break; - case SQL_C_STINYINT: - case SQL_C_TINYINT: { - CHK_CONV(1, tsdb_int64_to_bigint(*(int8_t*)paramValue, &bind->u.v8)); - } break; - case SQL_C_SSHORT: - case SQL_C_SHORT: { - CHK_CONV(1, tsdb_int64_to_bigint(*(int16_t*)paramValue, &bind->u.v8)); - } break; - case SQL_C_SLONG: - case SQL_C_LONG: { - CHK_CONV(1, tsdb_int64_to_bigint(*(int32_t*)paramValue, &bind->u.v8)); - } break; - case SQL_C_SBIGINT: { - CHK_CONV(1, tsdb_int64_to_bigint(*(int64_t*)paramValue, &bind->u.v8)); - } break; - case SQL_C_CHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - CHK_CONV(1, tsdb_conv_chars_to_bigint(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v8)); - // CHK_CONV(1, tsdb_chars_to_bigint((const char*)paramValue, (size_t)*soi, &bind->u.v8)); - } break; - case SQL_C_WCHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_bigint(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v8)); - } break; - case SQL_C_USHORT: - case SQL_C_ULONG: - case SQL_C_FLOAT: - case SQL_C_DOUBLE: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_FLOAT: { - bind->buffer_type = tsdb_type; - bind->buffer_length = sizeof(bind->u.f4); - bind->buffer = &bind->u.f4; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_BIT: { - CHK_CONV(1, tsdb_int64_to_float(*(int8_t*)paramValue, &bind->u.f4)); - } break; - case SQL_C_STINYINT: - case SQL_C_TINYINT: { - CHK_CONV(1, tsdb_int64_to_float(*(int8_t*)paramValue, &bind->u.f4)); - } break; - case SQL_C_SSHORT: - case SQL_C_SHORT: { - CHK_CONV(1, tsdb_int64_to_float(*(int16_t*)paramValue, &bind->u.f4)); - } break; - case SQL_C_SLONG: - case SQL_C_LONG: { - CHK_CONV(1, tsdb_int64_to_float(*(int32_t*)paramValue, &bind->u.f4)); - } break; - case SQL_C_SBIGINT: { - CHK_CONV(1, tsdb_int64_to_float(*(int64_t*)paramValue, &bind->u.f4)); - } break; - case SQL_C_FLOAT: { - bind->u.f4 = *(float*)paramValue; - } break; - case SQL_C_DOUBLE: { - bind->u.f4 = (float)*(double*)paramValue; - } break; - case SQL_C_CHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - CHK_CONV(1, tsdb_conv_chars_to_float(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.f4)); - } break; - case SQL_C_WCHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_float(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.f4)); - } break; - case SQL_C_USHORT: - case SQL_C_ULONG: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_DOUBLE: { - bind->buffer_type = tsdb_type; - bind->buffer_length = sizeof(bind->u.f8); - bind->buffer = &bind->u.f8; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_BIT: { - CHK_CONV(1, tsdb_int64_to_double(*(int8_t*)paramValue, &bind->u.f8)); - } break; - case SQL_C_STINYINT: - case SQL_C_TINYINT: { - CHK_CONV(1, tsdb_int64_to_double(*(int8_t*)paramValue, &bind->u.f8)); - } break; - case SQL_C_SSHORT: - case SQL_C_SHORT: { - CHK_CONV(1, tsdb_int64_to_double(*(int16_t*)paramValue, &bind->u.f8)); - } break; - case SQL_C_SLONG: - case SQL_C_LONG: { - CHK_CONV(1, tsdb_int64_to_double(*(int32_t*)paramValue, &bind->u.f8)); - } break; - case SQL_C_SBIGINT: { - CHK_CONV(1, tsdb_int64_to_double(*(int64_t*)paramValue, &bind->u.f8)); - } break; - case SQL_C_FLOAT: { - bind->u.f8 = *(float*)paramValue; - } break; - case SQL_C_DOUBLE: { - bind->u.f8 = *(double*)paramValue; - } break; - case SQL_C_CHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - CHK_CONV(1, tsdb_conv_chars_to_double(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.f8)); - // CHK_CONV(1, tsdb_chars_to_double((const char*)paramValue, (size_t)*soi, &bind->u.f8)); - } break; - case SQL_C_WCHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_double(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.f8)); - } break; - case SQL_C_USHORT: - case SQL_C_ULONG: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_TIMESTAMP: { - bind->buffer_type = tsdb_type; - bind->buffer_length = sizeof(bind->u.v8); - bind->buffer = &bind->u.v8; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_CHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_timestamp_ts(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v8)); - } break; - case SQL_C_WCHAR: { - stack_buffer_t buffer; buffer.next = 0; - tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn); - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - CHK_CONV(1, tsdb_conv_chars_to_timestamp_ts(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v8)); - } break; - case SQL_C_SBIGINT: { - int64_t t = *(int64_t*)paramValue; - bind->u.v8 = t; - } break; - case SQL_C_TYPE_TIMESTAMP: { - SQL_TIMESTAMP_STRUCT ts = *(SQL_TIMESTAMP_STRUCT*)paramValue; - struct tm vtm = {0}; - vtm.tm_year = ts.year - 1900; - vtm.tm_mon = ts.month - 1; - vtm.tm_mday = ts.day; - vtm.tm_hour = ts.hour; - vtm.tm_min = ts.minute; - vtm.tm_sec = ts.second; - int64_t t = (int64_t) mktime(&vtm); - if (t==-1) { - CHK_CONV(1, TSDB_CONV_NOT_VALID_TS); - // code never reached here - } - bind->u.ts = t * 1000 + ts.fraction / 1000000; - } break; - case SQL_C_SHORT: - case SQL_C_SSHORT: - case SQL_C_USHORT: - case SQL_C_LONG: - case SQL_C_SLONG: - case SQL_C_ULONG: - case SQL_C_FLOAT: - case SQL_C_DOUBLE: - case SQL_C_BIT: - case SQL_C_TINYINT: - case SQL_C_STINYINT: - case SQL_C_UTINYINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_BINARY: { - bind->buffer_type = tsdb_type; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_WCHAR: { - // taos cares nothing about what would be stored in 'binary' as most sql implementations do - // thus, we just copy it as is - // it's caller's responsibility to maintain data-consistency - // if he/she is going to use 'binary' to store characters - // taos might extend it's sql syntax to let user specify - // what charset is to be used for specific 'binary' field when - // table is to be created - // in such way, 'binary' would be 'internationalized' - // but actually speaking, normally, 'char' field is a better - // one for this purpose - size_t slen = (size_t)*soi; - DASSERT(slen != SQL_NTS); - bind->u.bin = (unsigned char*)malloc(slen + 1); // add null-terminator, just for case of use - if (!bind->u.bin) { - CHK_CONV(1, TSDB_CONV_OOM); - // code never reached here - } - memcpy(bind->u.bin, paramValue, slen); - bind->buffer_length = slen; - bind->buffer = bind->u.bin; - CHK_CONV(1, TSDB_CONV_OK); - - // tsdb_conv_t *utf16_to_server = tsdb_conn_utf16_to_server(conn); - // size_t slen = (size_t)*soi; - // DASSERT(slen != SQL_NTS); - // const char *buf = NULL; - // size_t blen = 0; - // TSDB_CONV_CODE code = tsdb_conv(utf16_to_server, NULL, (const char *)paramValue, slen, &buf, &blen); - // if (code==TSDB_CONV_OK) { - // if (buf!=(const char*)paramValue) { - // bind->allocated = 1; - // } - // bind->u.bin = (unsigned char*)buf; - // bind->buffer_length = blen; - // bind->buffer = bind->u.bin; - // } - // CHK_CONV(1, code); - } break; - case SQL_C_CHAR: { - // taos cares nothing about what would be stored in 'binary' as most sql implementations do - // thus, we just copy it as is - // it's caller's responsibility to maintain data-consistency - // if he/she is going to use 'binary' to store characters - // taos might extend it's sql syntax to let user specify - // what charset is to be used for specific 'binary' field when - // table is to be created - // in such way, 'binary' would be 'internationalized' - // but actually speaking, normally, 'char' field is a better - // one for this purpose - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - // we can not use strndup, because ODBC client might pass in a buffer without null-terminated - bind->u.bin = (unsigned char*)malloc(slen + 1); // add null-terminator, just for case of use - if (!bind->u.bin) { - CHK_CONV(1, TSDB_CONV_OOM); - // code never reached here - } - memcpy(bind->u.bin, paramValue, slen); - bind->buffer_length = slen; - bind->buffer = bind->u.bin; - CHK_CONV(1, TSDB_CONV_OK); - // code never reached here - - // tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn); - // size_t slen = (size_t)*soi; - // if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - // const char *buf = NULL; - // size_t blen = 0; - // TSDB_CONV_CODE code = tsdb_conv(client_to_server, NULL, (const char *)paramValue, slen, &buf, &blen); - // if (code==TSDB_CONV_OK) { - // if (buf!=(const char*)paramValue) { - // bind->allocated = 1; - // } - // bind->u.bin = (unsigned char*)buf; - // bind->buffer_length = blen; - // bind->buffer = bind->u.bin; - // } - // CHK_CONV(1, code); - } break; - case SQL_C_SHORT: - case SQL_C_SSHORT: - case SQL_C_USHORT: - case SQL_C_LONG: - case SQL_C_SLONG: - case SQL_C_ULONG: - case SQL_C_FLOAT: - case SQL_C_DOUBLE: - case SQL_C_BIT: - case SQL_C_TINYINT: - case SQL_C_STINYINT: - case SQL_C_UTINYINT: - case SQL_C_SBIGINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: // we don't provide auto-converstion - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - case TSDB_DATA_TYPE_NCHAR: { - bind->buffer_type = tsdb_type; - bind->length = &bind->buffer_length; - switch (valueType) { - case SQL_C_WCHAR: { - tsdb_conv_t *utf16_to_server = tsdb_conn_utf16_to_server(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - const char *buf = NULL; - size_t blen = 0; - TSDB_CONV_CODE code = tsdb_conv(utf16_to_server, NULL, (const char *)paramValue, slen, &buf, &blen); - if (code==TSDB_CONV_OK) { - if (buf!=(const char*)paramValue) { - bind->allocated = 1; - } - bind->u.nchar = (char*)buf; - bind->buffer_length = blen; - bind->buffer = bind->u.nchar; - } - CHK_CONV(1, code); - } break; - case SQL_C_CHAR: { - tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn); - size_t slen = (size_t)*soi; - if (slen==SQL_NTS) slen = strlen((const char*)paramValue); - const char *buf = NULL; - size_t blen = 0; - TSDB_CONV_CODE code = tsdb_conv(client_to_server, NULL, (const char *)paramValue, slen, &buf, &blen); - if (code==TSDB_CONV_OK) { - if (buf!=(const char*)paramValue) { - bind->allocated = 1; - } - bind->u.bin = (unsigned char*)buf; - bind->buffer_length = blen; - bind->buffer = bind->u.bin; - } - CHK_CONV(1, code); - } break; - case SQL_C_SHORT: - case SQL_C_SSHORT: - case SQL_C_USHORT: - case SQL_C_LONG: - case SQL_C_SLONG: - case SQL_C_ULONG: - case SQL_C_FLOAT: - case SQL_C_DOUBLE: - case SQL_C_BIT: - case SQL_C_TINYINT: - case SQL_C_STINYINT: - case SQL_C_UTINYINT: - case SQL_C_SBIGINT: - case SQL_C_UBIGINT: - case SQL_C_BINARY: - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: - case SQL_C_TYPE_DATE: - case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: // we don't provide auto-converstion - case SQL_C_NUMERIC: - case SQL_C_GUID: - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - } break; - default: { - SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, - "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", - sql_c_type(valueType), valueType, valueType, - taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); - return SQL_ERROR; - } break; - } - return SQL_SUCCESS; -} - -static SQLRETURN do_bind_batch(sql_t *sql, int idx_row, TAOS_BIND *binds) -{ - for (int j=0; jn_params; ++j) { - SQLRETURN r = do_bind_param_value(sql, idx_row, j, sql->params+j, binds+j); - if (r==SQL_SUCCESS) continue; - return r; - } - if (sql->n_params > 0) { - int tr = 0; - PROFILE(tr = taos_stmt_bind_param(sql->stmt, binds)); - if (tr) { - SET_ERROR(sql, "HY000", tr, "failed to bind parameters[%d in total]", sql->n_params); - return SQL_ERROR; - } - - if (sql->is_insert) { - int r = 0; - PROFILE(r = taos_stmt_add_batch(sql->stmt)); - if (r) { - SET_ERROR(sql, "HY000", r, "failed to add batch"); - return SQL_ERROR; - } - } - } - return SQL_SUCCESS; -} - -static SQLRETURN do_execute(sql_t *sql) -{ - int tr = TSDB_CODE_SUCCESS; - if (sql->n_rows==0) sql->n_rows = 1; - for (int i=0; in_rows; ++i) { - TAOS_BIND *binds = NULL; - if (sql->n_params>0) { - binds = (TAOS_BIND*)calloc((size_t)sql->n_params, sizeof(*binds)); - if (!binds) { - SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); - return SQL_ERROR; - } - } - - SQLRETURN r = do_bind_batch(sql, i, binds); - - if (binds) { - for (int i = 0; in_params; ++i) { - TAOS_BIND *bind = binds + i; - if (bind->allocated) { - free(bind->u.nchar); - bind->u.nchar = NULL; - } - } - free(binds); - } - - if (r) return r; - } - - PROFILE(tr = taos_stmt_execute(sql->stmt)); - if (tr) { - SET_ERROR(sql, "HY000", tr, "failed to execute statement"); - return SQL_ERROR; - } - - sql->is_executed = 1; - // if (sql->is_insert) return SQL_SUCCESS; - - SQLRETURN r = SQL_SUCCESS; - PROFILE(sql->rs = taos_stmt_use_result(sql->stmt)); - CHK_RS(r, sql, "failed to use result"); - - return r; -} - -static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_ERROR; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - if (!sql->stmt) { - SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); - return SQL_ERROR; - } - - if (sql->rs) { - taos_free_result(sql->rs); - sql->rs = NULL; - sql->row = NULL; - } - - SQLRETURN r = do_execute(sql); - - return r; -} - -SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) -{ - SQLRETURN r; - PROFILE(r = doSQLExecute(StatementHandle)); - return r; -} - -static SQLRETURN doSQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, - SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, - SQLPOINTER DiagInfo, SQLSMALLINT BufferLength, - SQLSMALLINT *StringLength) -{ - switch (DiagIdentifier) { - case SQL_DIAG_CLASS_ORIGIN: { - *StringLength = 0; - } break; - } - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, - SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, - SQLPOINTER DiagInfo, SQLSMALLINT BufferLength, - SQLSMALLINT *StringLength) -{ - SQLRETURN r; - r = doSQLGetDiagField(HandleType, Handle, - RecNumber, DiagIdentifier, - DiagInfo, BufferLength, - StringLength); - return r; -} - -static SQLRETURN doSQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, - SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, - SQLINTEGER *NativeError, SQLCHAR *MessageText, - SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) -{ - if (RecNumber>1) return SQL_NO_DATA; - - switch (HandleType) { - case SQL_HANDLE_ENV: { - env_t *env = (env_t*)Handle; - if (!env) break; - FILL_ERROR(env); - return SQL_SUCCESS; - } break; - case SQL_HANDLE_DBC: { - conn_t *conn = (conn_t*)Handle; - if (!conn) break; - FILL_ERROR(conn); - return SQL_SUCCESS; - } break; - case SQL_HANDLE_STMT: { - sql_t *sql = (sql_t*)Handle; - if (!sql) break; - FILL_ERROR(sql); - return SQL_SUCCESS; - } break; - default: { - } break; - } - - // how to return error? - return SQL_ERROR; -} - -SQLRETURN SQL_API SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, - SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, - SQLINTEGER *NativeError, SQLCHAR *MessageText, - SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) -{ - SQLRETURN r; - r = doSQLGetDiagRec(HandleType, Handle, - RecNumber, Sqlstate, - NativeError, MessageText, - BufferLength, TextLength); - return r; -} - -static SQLRETURN doSQLBindParameter( - SQLHSTMT StatementHandle, - SQLUSMALLINT ParameterNumber, - SQLSMALLINT fParamType, - SQLSMALLINT ValueType, - SQLSMALLINT ParameterType, - SQLULEN LengthPrecision, - SQLSMALLINT ParameterScale, - SQLPOINTER ParameterValue, - SQLLEN cbValueMax, // ignore for now, since only SQL_PARAM_INPUT is supported now - SQLLEN *StrLen_or_Ind) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - if (!sql->stmt) { - SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); - return SQL_ERROR; - } - - if (ParameterNumber<=0 || ParameterNumber>sql->n_params) { - SET_ERROR(sql, "07009", TSDB_CODE_ODBC_BAD_ARG, - "parameter [@%d] invalid", ParameterNumber); - return SQL_ERROR; - } - - if (fParamType != SQL_PARAM_INPUT) { - SET_ERROR(sql, "HY105", TSDB_CODE_ODBC_NOT_SUPPORT, "non-input parameter [@%d] not supported yet", ParameterNumber); - return SQL_ERROR; - } - - if (ValueType == SQL_C_DEFAULT) { - SET_ERROR(sql, "HY003", TSDB_CODE_ODBC_NOT_SUPPORT, "default value for parameter [@%d] not supported yet", ParameterNumber); - return SQL_ERROR; - } - - if (!is_valid_sql_c_type(ValueType)) { - SET_ERROR(sql, "HY003", TSDB_CODE_ODBC_NOT_SUPPORT, - "SQL_C_TYPE [%s/%d/0x%x] for parameter [@%d] unknown", - sql_c_type(ValueType), ValueType, ValueType, ParameterNumber); - return SQL_ERROR; - } - - if (!is_valid_sql_sql_type(ParameterType)) { - SET_ERROR(sql, "HY004", TSDB_CODE_ODBC_NOT_SUPPORT, - "SQL_TYPE [%s/%d/0x%x] for parameter [@%d] unknown", - sql_c_type(ParameterType), ParameterType, ParameterType, ParameterNumber); - return SQL_ERROR; - } - - if (ParameterValue==NULL) { - SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "ParameterValue [@%p] not allowed", ParameterValue); - return SQL_ERROR; - } - - if (StrLen_or_Ind==NULL) { - SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "StrLen_or_Ind [@%p] not allowed", StrLen_or_Ind); - return SQL_ERROR; - } - - param_bind_t *pb = sql->params + ParameterNumber - 1; - - pb->ParameterNumber = ParameterNumber; - pb->ValueType = ValueType; - pb->ParameterType = ParameterType; - pb->LengthPrecision = LengthPrecision; - pb->ParameterScale = ParameterScale; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - - pb->valid = 1; - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLBindParameter( - SQLHSTMT StatementHandle, - SQLUSMALLINT ParameterNumber, - SQLSMALLINT fParamType, - SQLSMALLINT ValueType, - SQLSMALLINT ParameterType, - SQLULEN LengthPrecision, - SQLSMALLINT ParameterScale, - SQLPOINTER ParameterValue, - SQLLEN cbValueMax, // ignore for now, since only SQL_PARAM_INPUT is supported now - SQLLEN *StrLen_or_Ind) -{ - SQLRETURN r; - r = doSQLBindParameter(StatementHandle, ParameterNumber, fParamType, ValueType, ParameterType, - LengthPrecision, ParameterScale, ParameterValue, cbValueMax, StrLen_or_Ind); - return r; -} - -static SQLRETURN doSQLDriverConnect( - SQLHDBC hdbc, - SQLHWND hwnd, - SQLCHAR *szConnStrIn, - SQLSMALLINT cbConnStrIn, - SQLCHAR *szConnStrOut, - SQLSMALLINT cbConnStrOutMax, - SQLSMALLINT *pcbConnStrOut, - SQLUSMALLINT fDriverCompletion) -{ - conn_t *conn = (conn_t*)hdbc; - if (!conn) return SQL_INVALID_HANDLE; - - if (conn->taos) { - SET_ERROR(conn, "08002", TSDB_CODE_ODBC_CONNECTION_BUSY, "connection still in use"); - return SQL_ERROR; - } - - if (fDriverCompletion!=SQL_DRIVER_NOPROMPT) { - SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "option[%d] other than SQL_DRIVER_NOPROMPT not supported yet", fDriverCompletion); - return SQL_ERROR; - } - - NORM_STR_LENGTH(conn, szConnStrIn, cbConnStrIn); - - // DSN=; UID=; PWD= - - const char *connStr = SDUP(szConnStrIn, cbConnStrIn); - - conn_val_t val = {0}; - - do { - if (szConnStrIn && !connStr) { - SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, ""); - break; - } - - int n = todbc_parse_conn_string((const char *)connStr, &val); - if (n) { - SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_BAD_CONNSTR, "unrecognized connection string: [%s]", (const char*)szConnStrIn); - break; - } - char *ip = NULL; - int port = 0; - if (val.server) { - char *p = strchr(val.server, ':'); - if (p) { - ip = strndup(val.server, (size_t)(p-val.server)); - port = atoi(p+1); - } - } - - if ((val.cli_enc && strcmp(val.cli_enc, conn->client_enc)) || - (val.svr_enc && strcmp(val.svr_enc, conn->server_enc)) ) - { - tsdb_conn_close_convs(conn); - if (val.cli_enc) { - snprintf(conn->client_enc, sizeof(conn->client_enc), "%s", val.cli_enc); - } - if (val.svr_enc) { - snprintf(conn->server_enc, sizeof(conn->server_enc), "%s", val.svr_enc); - } - } - - // TODO: data-race - // TODO: shall receive ip/port from odbc.ini - // shall we support non-ansi uid/pwd/db etc? - conn->taos = taos_connect(ip ? ip : "localhost", val.uid, val.pwd, val.db, (uint16_t)port); - free(ip); ip = NULL; - if (!conn->taos) { - SET_ERROR(conn, "HY000", terrno, "failed to connect to data source"); - break; - } - - if (szConnStrOut) { - snprintf((char*)szConnStrOut, (size_t)cbConnStrOutMax, "%s", connStr); - } - if (pcbConnStrOut) { - *pcbConnStrOut = cbConnStrIn; - } - } while (0); - - conn_val_reset(&val); - - SFRE(connStr, szConnStrIn, cbConnStrIn); - - return conn->taos ? SQL_SUCCESS : SQL_ERROR; -} - -SQLRETURN SQL_API SQLDriverConnect( - SQLHDBC hdbc, - SQLHWND hwnd, - SQLCHAR *szConnStrIn, - SQLSMALLINT cbConnStrIn, - SQLCHAR *szConnStrOut, - SQLSMALLINT cbConnStrOutMax, - SQLSMALLINT *pcbConnStrOut, - SQLUSMALLINT fDriverCompletion) -{ - SQLRETURN r; - r = doSQLDriverConnect(hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion); - return r; -} - -static SQLRETURN doSQLSetConnectAttr(SQLHDBC ConnectionHandle, - SQLINTEGER Attribute, SQLPOINTER Value, - SQLINTEGER StringLength) -{ - conn_t *conn = (conn_t*)ConnectionHandle; - if (!conn) return SQL_INVALID_HANDLE; - - if (Attribute != SQL_ATTR_AUTOCOMMIT) { - SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "Attribute other than SQL_ATTR_AUTOCOMMIT not supported yet"); - return SQL_ERROR; - } - if (Value != (SQLPOINTER)SQL_AUTOCOMMIT_ON) { - SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "Attribute Value other than SQL_AUTOCOMMIT_ON not supported yet[%p]", Value); - return SQL_ERROR; - } - - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLSetConnectAttr(SQLHDBC ConnectionHandle, - SQLINTEGER Attribute, SQLPOINTER Value, - SQLINTEGER StringLength) -{ - SQLRETURN r; - r = doSQLSetConnectAttr(ConnectionHandle, Attribute, Value, StringLength); - return r; -} - -static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, - SQLSMALLINT BufferLength, SQLSMALLINT *NameLength, - SQLSMALLINT *DataType, SQLULEN *ColumnSize, - SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); - return SQL_ERROR; - } - - int nfields = taos_field_count(sql->rs); - TAOS_FIELD *fields = taos_fetch_fields(sql->rs); - - if (ColumnNumber<=0 || ColumnNumber>nfields) { - SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber); - return SQL_ERROR; - } - if (BufferLength<0) { - SET_ERROR(sql, "HY090", TSDB_CODE_ODBC_BAD_ARG, ""); - return SQL_ERROR; - } - - TAOS_FIELD *field = fields + ColumnNumber - 1; - if (ColumnName) { - size_t n = sizeof(field->name); - if (n>BufferLength) n = (size_t)BufferLength; - strncpy((char*)ColumnName, field->name, n); - } - if (NameLength) { - *NameLength = (SQLSMALLINT)strnlen(field->name, sizeof(field->name)); - } - if (ColumnSize) *ColumnSize = (SQLULEN)field->bytes; - if (DecimalDigits) *DecimalDigits = 0; - - if (DataType) { - switch (field->type) { - case TSDB_DATA_TYPE_BOOL: { - *DataType = SQL_TINYINT; - } break; - - case TSDB_DATA_TYPE_TINYINT: { - *DataType = SQL_TINYINT; - } break; - - case TSDB_DATA_TYPE_SMALLINT: { - *DataType = SQL_SMALLINT; - } break; - - case TSDB_DATA_TYPE_INT: { - *DataType = SQL_INTEGER; - } break; - - case TSDB_DATA_TYPE_BIGINT: { - *DataType = SQL_BIGINT; - } break; - - case TSDB_DATA_TYPE_FLOAT: { - *DataType = SQL_FLOAT; - } break; - - case TSDB_DATA_TYPE_DOUBLE: { - *DataType = SQL_DOUBLE; - } break; - - case TSDB_DATA_TYPE_TIMESTAMP: { - *DataType = SQL_TIMESTAMP; - if (ColumnSize) *ColumnSize = sizeof(SQL_TIMESTAMP_STRUCT); - if (DecimalDigits) *DecimalDigits = 0; - } break; - - case TSDB_DATA_TYPE_NCHAR: { - *DataType = SQL_CHAR; // unicode ? - if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE; - } break; - - case TSDB_DATA_TYPE_BINARY: { - *DataType = SQL_CHAR; - if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE; - } break; - - default: - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, - "unknown [%s[%d/0x%x]]", taos_data_type(field->type), field->type, field->type); - return SQL_ERROR; - break; - } - } - if (Nullable) { - *Nullable = SQL_NULLABLE_UNKNOWN; - } - - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, - SQLSMALLINT BufferLength, SQLSMALLINT *NameLength, - SQLSMALLINT *DataType, SQLULEN *ColumnSize, - SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable) -{ - SQLRETURN r; - r = doSQLDescribeCol(StatementHandle, ColumnNumber, ColumnName, - BufferLength, NameLength, - DataType, ColumnSize, - DecimalDigits, Nullable); - return r; -} - -static SQLRETURN doSQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar) -{ - sql_t *sql = (sql_t*)hstmt; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - if (!sql->stmt) { - SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); - return SQL_ERROR; - } - - int insert = 0; - int r = taos_stmt_is_insert(sql->stmt, &insert); - if (r) { - SET_ERROR(sql, "HY000", terrno, ""); - return SQL_ERROR; - } - // if (!insert) { - // SET_ERROR(sql, "HY000", terrno, "taos does not provide count of parameters for statement other than insert"); - // return SQL_ERROR; - // } - - int params = 0; - r = taos_stmt_num_params(sql->stmt, ¶ms); - if (r) { - SET_ERROR(sql, "HY000", terrno, "fetch num of statement params failed"); - return SQL_ERROR; - } - - if (pcpar) *pcpar = (SQLSMALLINT)params; - - return SQL_SUCCESS; -} - -SQLRETURN SQL_API SQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar) -{ - SQLRETURN r; - r = doSQLNumParams(hstmt, pcpar); - return r; -} - -static SQLRETURN doSQLSetStmtAttr(SQLHSTMT StatementHandle, - SQLINTEGER Attribute, SQLPOINTER Value, - SQLINTEGER StringLength) -{ - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_INVALID_HANDLE; - - CHK_CONN(sql); - CHK_CONN_TAOS(sql); - - if (!sql->stmt) { - SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); - return SQL_ERROR; - } - - if (sql->is_executed) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "change attr after executing statement not supported yet"); - return SQL_ERROR; - } - - switch (Attribute) { - case SQL_ATTR_PARAM_BIND_TYPE: { - SQLULEN val = (SQLULEN)Value; - if (val==SQL_BIND_BY_COLUMN) { - sql->rowlen = 0; - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "SQL_ATTR_PARAM_BIND_TYPE/SQL_BIND_BY_COLUMN"); - return SQL_ERROR; - } - sql->rowlen = val; - return SQL_SUCCESS; - } break; - case SQL_ATTR_PARAMSET_SIZE: { - SQLULEN val = (SQLULEN)Value; - DASSERT(val>0); - sql->n_rows = val; - return SQL_SUCCESS; - } break; - case SQL_ATTR_PARAM_BIND_OFFSET_PTR: { - if (Value) { - SQLULEN val = *(SQLULEN*)Value; - sql->ptr_offset = val; - } else { - sql->ptr_offset = 0; - } - return SQL_SUCCESS; - } break; - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "Attribute:%d", Attribute); - } break; - } - return SQL_ERROR; -} - -SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT StatementHandle, - SQLINTEGER Attribute, SQLPOINTER Value, - SQLINTEGER StringLength) -{ - SQLRETURN r; - r = doSQLSetStmtAttr(StatementHandle, Attribute, Value, StringLength); - return r; -} - -#ifdef _MSC_VER - -#define POST_INSTALLER_ERROR(hwndParent, code, fmt, ...) \ -do { \ - char buf[4096]; \ - snprintf(buf, sizeof(buf), "%s[%d]%s():" fmt "", \ - basename((char*)__FILE__), __LINE__, __func__, \ - ##__VA_ARGS__); \ - SQLPostInstallerError(code, buf); \ - if (hwndParent) { \ - MessageBox(hwndParent, buf, "Error", MB_OK|MB_ICONEXCLAMATION); \ - } \ -} while (0) - -typedef struct kv_s kv_t; -struct kv_s { - char *line; - size_t val; -}; - -static BOOL get_driver_dll_path(HWND hwndParent, char *buf, size_t len) -{ - HMODULE hm = NULL; - - if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - (LPCSTR) &ConfigDSN, &hm) == 0) - { - int ret = GetLastError(); - POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "GetModuleHandle failed, error = %d\n", ret); - return FALSE; - } - if (GetModuleFileName(hm, buf, (DWORD)len) == 0) - { - int ret = GetLastError(); - POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "GetModuleFileName failed, error = %d\n", ret); - return FALSE; - } - return TRUE; -} - -static BOOL doDSNAdd(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes) -{ - BOOL r = TRUE; - - kv_t *kvs = NULL; - - kv_t dsn = {0}; - char *line = NULL; - - do { - char driver_dll[MAX_PATH + 1]; - r = get_driver_dll_path(hwndParent, driver_dll, sizeof(driver_dll)); - if (!r) break; - - dsn.line = strdup("DSN=TAOS_DEMO"); - if (!dsn.line) { r = FALSE; break; } - - const char *p = lpszAttributes; - int ikvs = 0; - while (p && *p) { - line = strdup(p); - if (!line) { r = FALSE; break; } - char *v = strchr(line, '='); - if (!v) { r = FALSE; break; } - - if (strstr(line, "DSN")==line) { - if (dsn.line) { - free(dsn.line); - dsn.line = NULL; - dsn.val = 0; - } - dsn.line = line; - line = NULL; - } else { - kv_t *t = (kv_t*)realloc(kvs, (ikvs+1)*sizeof(*t)); - if (!t) { r = FALSE; free(line); break; } - t[ikvs].line = line; - *v = '\0'; - if (v) t[ikvs].val = v - line + 1; - line = NULL; - - kvs = t; - ++ikvs; - } - - p += strlen(p) + 1; - } - - if (hwndParent) { - MessageBox(hwndParent, "Please use odbcconf to add DSN for TAOS ODBC Driver", "Warning!", MB_OK|MB_ICONEXCLAMATION); - } - if (!r) break; - - char *v = NULL; - v = strchr(dsn.line, '='); - if (!v) { r = FALSE; break; } - *v = '\0'; - dsn.val = v - dsn.line + 1; - - if ((!dsn.line)) { - if (!r) POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "lack of either DSN or Driver"); - } else { - if (r) r = SQLWritePrivateProfileString("ODBC Data Sources", dsn.line+dsn.val, lpszDriver, "Odbc.ini"); - if (r) r = SQLWritePrivateProfileString(dsn.line+dsn.val, "Driver", driver_dll, "Odbc.ini"); - } - - for (int i=0; r && itype) { - case TSDB_DATA_TYPE_TINYINT: - return 5; - break; - - case TSDB_DATA_TYPE_SMALLINT: - return 7; - break; - - case TSDB_DATA_TYPE_INT: - return 12; - break; - - case TSDB_DATA_TYPE_BIGINT: - return 22; - break; - - case TSDB_DATA_TYPE_FLOAT: { - return 12; - } break; - - case TSDB_DATA_TYPE_DOUBLE: { - return 20; - } break; - - case TSDB_DATA_TYPE_BINARY: - case TSDB_DATA_TYPE_NCHAR: { - return 3*((size_t)field->bytes - VARSTR_HEADER_SIZE) + 2; - } break; - - case TSDB_DATA_TYPE_TIMESTAMP: - return 26; - break; - - case TSDB_DATA_TYPE_BOOL: - return 7; - default: - break; - } - - return 10; -} - - - diff --git a/src/connector/odbc/src/todbc.def b/src/connector/odbc/src/todbc.def index 1e080f01983ec2b38d657004291008f6da6198dc..2485e5df11490bafd5752e7fc3670ce09d47095a 100644 --- a/src/connector/odbc/src/todbc.def +++ b/src/connector/odbc/src/todbc.def @@ -3,13 +3,19 @@ SQLAllocEnv SQLFreeEnv SQLAllocConnect SQLFreeConnect +SQLGetEnvAttr +SQLSetEnvAttr +SQLGetConnectAttr +SQLGetConnectOption +SQLGetInfo SQLConnect SQLDisconnect SQLAllocStmt SQLAllocHandle +SQLFreeHandle SQLFreeStmt SQLExecDirect -SQLExecDirectW +;SQLExecDirectW SQLNumResultCols SQLRowCount SQLColAttribute @@ -17,14 +23,45 @@ SQLGetData SQLFetch SQLPrepare SQLExecute -SQLGetDiagField +SQLParamData +SQLPutData +;SQLGetDiagField SQLGetDiagRec SQLBindParameter +SQLDescribeParam SQLDriverConnect SQLSetConnectAttr SQLDescribeCol +SQLBindCol SQLNumParams SQLSetStmtAttr +SQLBindParam +SQLCancel +SQLCancelHandle +SQLCloseCursor +SQLColumns +SQLCopyDesc +SQLDataSources +SQLEndTran +;SQLError +SQLFetchScroll +SQLGetCursorName +SQLGetDescField +SQLGetDescRec +;SQLGetFunctions +SQLGetStmtAttr +SQLGetStmtOption +SQLGetTypeInfo +SQLSetConnectOption +SQLSetCursorName +SQLSetDescField +SQLSetDescRec +SQLSetParam +SQLSetStmtOption +SQLSpecialColumns +SQLStatistics +SQLTables +SQLTransact ConfigDSN ConfigTranslator ConfigDriver diff --git a/src/connector/odbc/src/todbc_buf.c b/src/connector/odbc/src/todbc_buf.c new file mode 100644 index 0000000000000000000000000000000000000000..1fce74299c53977359437dd1e43eba1f57eab936 --- /dev/null +++ b/src/connector/odbc/src/todbc_buf.c @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "todbc_buf.h" + +#include "todbc_list.h" +#include "todbc_log.h" +#include "todbc_tls.h" + +#include +#include + +#define BLOCK_SIZE (1024*16) + +// alignment for holding whatever struct would be +#define ALIGN(size) ((size==0?1:size) + sizeof(size_t) - 1) / sizeof(size_t) * sizeof(size_t); + +typedef struct buf_rec_s buf_rec_t; +typedef struct buf_block_s buf_block_t; + +struct buf_rec_s { + size_t size; // payload size couting from ptr[0] + buf_block_t *owner; + char ptr[0]; // place-holder +}; + +#define PTR_OFFSET() ((size_t)(((buf_rec_t*)0)->ptr)) + +struct buf_block_s { + todbc_buf_t *owner; + size_t cap; // total payload space available for allocation + size_t ptr; // beginning of free space + char base[0]; // place-holder +}; + +#define BASE_OFFSET() ((size_t)(((buf_block_t*)0)->base)) + +struct todbc_buf_s { + char buf[BLOCK_SIZE]; + buf_block_t *block; // mapped to &buf[0] + todbc_list_t *list; // dynamic allocated blocks list +}; + +#define BASE_STATIC(block) (block->owner->buf + BASE_OFFSET()==block->base) + + +typedef struct arg_s arg_t; +struct arg_s { + todbc_buf_t *buf; + + // input + char *arg_ptr; + size_t arg_size; + buf_rec_t *arg_rec; + size_t arg_align; + + // output + buf_rec_t *rec; +}; + +static buf_rec_t* ptr_rec(todbc_buf_t *buf, void *ptr) { + char *p = (char*)ptr; + char *begin = p-PTR_OFFSET(); + buf_rec_t *rec = (buf_rec_t*)begin; + OILE(rec, ""); + OILE(rec->size, ""); + OILE((rec->size)%sizeof(void*)==0, ""); + buf_block_t *block = rec->owner; + OILE(block, ""); + OILE(block->owner==buf, ""); + OILE(block->base, ""); + + char *end = begin + sizeof(buf_rec_t) + rec->size; + OILE(begin>=block->base, ""); + OILE(end<=block->base+block->ptr, ""); + + return rec; +} + +static buf_block_t* buf_block_create(size_t align); +static buf_rec_t* buf_block_realloc(buf_block_t *block, arg_t *arg); +static void buf_block_free(buf_block_t *block); +static void buf_block_reclaim(buf_block_t *block); + +#define ALLOC_LIST() do { \ + if (buf->list) break; \ + todbc_list_conf_t conf = {0}; \ + conf.arg = buf; \ + conf.val_free = do_free_buf_block; \ + buf->list = todbc_list_create(conf); \ +} while (0) + + +todbc_buf_t* todbc_buf_create(void) { + todbc_buf_t *buf = (todbc_buf_t*)calloc(1, sizeof(*buf)); + if (!buf) return NULL; + + buf_block_t *block = (buf_block_t*)(buf->buf); + + block->owner = buf; + block->cap = sizeof(buf->buf) - sizeof(buf_block_t); + block->ptr = 0; + + buf->block = block; + + OILE(BASE_STATIC(buf->block), ""); + + return buf; +} + +void todbc_buf_free(todbc_buf_t *buf) { + if (!buf) return; + + todbc_list_free(buf->list); + buf->list = NULL; + + free(buf); +} + +void* todbc_buf_alloc(todbc_buf_t *buf, size_t size) { + return todbc_buf_realloc(buf, NULL, size); +} + +void* todbc_buf_calloc(todbc_buf_t *buf, size_t count, size_t size) { + size_t align = ALIGN(size); + size_t total = count * align; + if (total > INT64_MAX) return NULL; + void *ptr = todbc_buf_realloc(buf, NULL, total); + if (!ptr) return NULL; + memset(ptr, 0, total); + return ptr; +} + +static void do_free_buf_block(todbc_list_t *list, void *val, void *arg); + +static int alloc_space_by(todbc_list_t *list, void *val, void *arg); + +void* todbc_buf_realloc(todbc_buf_t *buf, void *ptr, size_t size) { + OILE(buf, ""); + OILE(sizeof(buf_block_t)%sizeof(void*)==0, ""); + OILE(sizeof(buf_rec_t)%sizeof(void*)==0, ""); + + size_t align = ALIGN(size); + size_t req_size = align + sizeof(buf_rec_t); + + buf_rec_t *rec = NULL; + buf_block_t *block = NULL; + if (ptr) { + rec = ptr_rec(buf, ptr); + if (align<=rec->size) return ptr; + + block = rec->owner; + char *tail_rec = rec->ptr + rec->size; + char *tail_block = block->base + block->ptr; + if (tail_rec==tail_block) { + char *end_rec = rec->ptr + align; + char *end_block = block->base + block->cap; + if (end_rec<=end_block) { + rec->size = align; + block->ptr = (size_t)(end_rec - block->base); + return ptr; + } + } else { + size_t remain = block->cap - block->ptr; + if (req_size<=remain) { + char *new_ptr = block->base + block->ptr; + block->ptr += req_size; + buf_rec_t *new_rec = (buf_rec_t*)new_ptr; + new_rec->size = align; + new_rec->owner = block; + memcpy(new_rec->ptr, ptr, rec->size); + return new_rec->ptr; + } + } + } + + arg_t arg = {0}; + arg.buf = buf; + arg.arg_ptr = ptr; + arg.arg_size = size; + arg.arg_rec = rec; + arg.arg_align = align; + + if (block!=buf->block) { + buf_rec_t *new_rec = buf_block_realloc(buf->block, &arg); + if (new_rec) return new_rec->ptr; + } + + ALLOC_LIST(); + if (!buf->list) return NULL; + + int r = todbc_list_traverse(buf->list, alloc_space_by, &arg); + if (r) { + OILE(arg.rec, ""); + OILE(arg.rec->ptr, ""); + return arg.rec->ptr; + } + + block = buf_block_create(arg.arg_align); + if (!block) return NULL; + OILE(block->owner==NULL, ""); + r = todbc_list_pushfront(buf->list, block); + OILE(r==0, ""); + block->owner = buf; + buf_rec_t *p = buf_block_realloc(block, &arg); + OILE(p, ""); + + return p->ptr; +} + +static int do_reclaim(todbc_list_t *list, void *val, void *arg); + +void todbc_buf_reclaim(todbc_buf_t *buf) { + if (!buf) return; + + buf_block_reclaim(buf->block); + + if (!buf->list) return; + + todbc_list_traverse(buf->list, do_reclaim, buf); +} + +static int do_reclaim(todbc_list_t *list, void *val, void *arg) { + todbc_buf_t *buf = (todbc_buf_t*)arg; + buf_block_t *block = (buf_block_t*)val; + OILE(list, ""); + OILE(block, ""); + OILE(block->owner==buf, ""); + buf_block_reclaim(block); + return 0; +} + +static void buf_block_reclaim(buf_block_t *block) { + block->ptr = 0; +} + +static void buf_block_free(buf_block_t *block) { + if (!block) return; + + buf_block_reclaim(block); + + if (BASE_STATIC(block)) return; + + block->owner = NULL; + block->cap = 0; + block->ptr = 0; + + free(block); +} + +static void do_free_buf_block(todbc_list_t *list, void *val, void *arg) { + todbc_buf_t *buf = (todbc_buf_t*)arg; + OILE(buf && buf->list==list, ""); + buf_block_t *block = (buf_block_t*)val; + OILE(block, ""); + OILE(buf==block->owner, ""); + + buf_block_free(block); +} + +static int alloc_space_by(todbc_list_t *list, void *val, void *arg) { + buf_block_t *block = (buf_block_t*)val; + arg_t *targ = (arg_t*)arg; + + buf_rec_t *rec = targ->arg_rec; + if (rec && rec->owner == block) return 0; + + buf_rec_t *new_rec = buf_block_realloc(block, targ); + if (!new_rec) return 0; + return 1; +} + +static buf_block_t* buf_block_create(size_t size) { + size_t align = ALIGN(size); + size_t req_size = sizeof(buf_block_t) + sizeof(buf_rec_t) + align; + req_size = (req_size + BLOCK_SIZE - 1) / BLOCK_SIZE * BLOCK_SIZE; + + buf_block_t *block = (buf_block_t*)malloc(req_size); + if (!block) return NULL; + + block->owner = NULL; + block->cap = req_size - sizeof(buf_block_t); + block->ptr = 0; + + return block; +} + +static buf_rec_t* buf_block_realloc(buf_block_t *block, arg_t *arg) { + OILE(block, ""); + OILE(arg, ""); + OILE(block->base, ""); + OILE(block->cap >= block->ptr, ""); + + char *ptr = arg->arg_ptr; + buf_rec_t *rec = arg->arg_rec; + OILE(rec==NULL || rec->owner!=block, ""); + + size_t align = arg->arg_align; + size_t req_size = sizeof(buf_rec_t) + align; + + OILE(req_size>0, ""); + + size_t remain = block->cap - block->ptr; + if (req_size > remain) { + return NULL; + } + + char *p = block->base + block->ptr; + buf_rec_t *new_rec = (buf_rec_t*)p; + new_rec->size = align; + new_rec->owner = block; + + block->ptr += req_size; + + if (ptr) { + memcpy(new_rec->ptr, ptr, arg->arg_rec->size); + } + + arg->rec = new_rec; + + return new_rec; +} + + + + + + +// test + +void todbc_buf_test_random(size_t iterates, size_t size, int raw) { + size_t n_reallocs = 0; + todbc_buf_t *cache = NULL; + OD("use %s", raw ? "realloc" : "todbc_buf_t"); + if (!raw) { + cache = todbc_buf_create(); + OILE(cache, ""); + } + srand((unsigned)time(0)); + char *buf = NULL; + size_t blen = 0; + while (iterates-- > 0) { + char *p = NULL; + size_t i = 0; + size_t len = (size_t)rand()%size; + if (raw) { + p = realloc(buf, len); + } else { + p = todbc_buf_realloc(cache, buf, len); + } + OILE(p, ""); + for (i=blen; i 0) { + size_t i = 0; + char *buf = NULL; + while (i + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _todbc_buf_h_ +#define _todbc_buf_h_ + +#include +#include + +// non-thread-safe +typedef struct todbc_buf_s todbc_buf_t; + +todbc_buf_t* todbc_buf_create(void); +void todbc_buf_free(todbc_buf_t *buf); + +void* todbc_buf_alloc(todbc_buf_t *buf, size_t size); +void* todbc_buf_calloc(todbc_buf_t *buf, size_t count, size_t size); +void* todbc_buf_realloc(todbc_buf_t *buf, void *ptr, size_t size); +char* todbc_buf_strdup(todbc_buf_t *buf, const char *str); +void todbc_buf_reclaim(todbc_buf_t *buf); + +void todbc_buf_test_random(size_t iterates, size_t size, int raw); +void todbc_buf_test(size_t iterates, size_t size, int raw); + +#endif // _todbc_buf_h_ + diff --git a/src/connector/odbc/src/todbc_conv.c b/src/connector/odbc/src/todbc_conv.c deleted file mode 100644 index 9c0f19764c2b456c63b6d0a01402e37868a0a366..0000000000000000000000000000000000000000 --- a/src/connector/odbc/src/todbc_conv.c +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "todbc_conv.h" - -#include "todbc_log.h" - -#include -#include -#include -#include -#include - -const char* tsdb_conv_code_str(TSDB_CONV_CODE code) { - switch (code) { - case TSDB_CONV_OK: return "TSDB_CONV_OK"; - case TSDB_CONV_NOT_AVAIL: return "TSDB_CONV_NOT_AVAIL"; - case TSDB_CONV_OOM: return "TSDB_CONV_OOM"; - case TSDB_CONV_OOR: return "TSDB_CONV_OOR"; - case TSDB_CONV_TRUNC_FRACTION: return "TSDB_CONV_TRUNC_FRACTION"; - case TSDB_CONV_TRUNC: return "TSDB_CONV_TRUNC"; - case TSDB_CONV_CHAR_NOT_NUM: return "TSDB_CONV_CHAR_NOT_NUM"; - case TSDB_CONV_CHAR_NOT_TS: return "TSDB_CONV_CHAR_NOT_TS"; - case TSDB_CONV_NOT_VALID_TS: return "TSDB_CONV_NOT_VALID_TS"; - case TSDB_CONV_GENERAL: return "TSDB_CONV_GENERAL"; - case TSDB_CONV_SRC_TOO_LARGE: return "TSDB_CONV_SRC_TOO_LARGE"; - case TSDB_CONV_SRC_BAD_SEQ: return "TSDB_CONV_SRC_BAD_SEQ"; - case TSDB_CONV_SRC_INCOMPLETE: return "TSDB_CONV_SRC_INCOMPLETE"; - case TSDB_CONV_SRC_GENERAL: return "TSDB_CONV_SRC_GENERAL"; - case TSDB_CONV_BAD_CHAR: return "TSDB_CONV_BAD_CHAR"; - default: return "UNKNOWN"; - }; -} - -// src: int -TSDB_CONV_CODE tsdb_int64_to_bit(int64_t src, int8_t *dst) { - *dst = (int8_t)src; - if (src==0 || src==1) return TSDB_CONV_OK; - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_int64_to_tinyint(int64_t src, int8_t *dst) { - *dst = (int8_t)src; - if (src == *dst) return TSDB_CONV_OK; - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_int64_to_smallint(int64_t src, int16_t *dst) { - *dst = (int16_t)src; - if (src == *dst) return TSDB_CONV_OK; - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_int64_to_int(int64_t src, int32_t *dst) { - *dst = (int32_t)src; - if (src == *dst) return TSDB_CONV_OK; - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_int64_to_bigint(int64_t src, int64_t *dst) { - *dst = src; - return TSDB_CONV_OK; -} - -TSDB_CONV_CODE tsdb_int64_to_ts(int64_t src, int64_t *dst) { - *dst = src; - - time_t t = (time_t)(src / 1000); - struct tm tm = {0}; - if (localtime_r(&t, &tm)) return TSDB_CONV_OK; - - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_int64_to_float(int64_t src, float *dst) { - *dst = (float)src; - - int64_t v = (int64_t)*dst; - if (v==src) return TSDB_CONV_OK; - - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_int64_to_double(int64_t src, double *dst) { - *dst = (double)src; - - int64_t v = (int64_t)*dst; - if (v==src) return TSDB_CONV_OK; - - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_int64_to_char(int64_t src, char *dst, size_t dlen) { - int n = snprintf(dst, dlen, "%" PRId64 "", src); - DASSERT(n>=0); - - if (n=2) return TSDB_CONV_OOR; - if (src == *dst) return TSDB_CONV_OK; - - int64_t v = (int64_t)src; - if (v == *dst) return TSDB_CONV_TRUNC_FRACTION; - - return TSDB_CONV_TRUNC; -} - -TSDB_CONV_CODE tsdb_double_to_tinyint(double src, int8_t *dst) { - *dst = (int8_t)src; - - if (srcSCHAR_MAX) return TSDB_CONV_OOR; - if (src == *dst) return TSDB_CONV_OK; - - int64_t v = (int64_t)src; - if (v == *dst) return TSDB_CONV_TRUNC_FRACTION; - - return TSDB_CONV_TRUNC; -} - -TSDB_CONV_CODE tsdb_double_to_smallint(double src, int16_t *dst) { - *dst = (int16_t)src; - - if (srcSHRT_MAX) return TSDB_CONV_OOR; - if (src == *dst) return TSDB_CONV_OK; - - int64_t v = (int64_t)src; - if (v == *dst) return TSDB_CONV_TRUNC_FRACTION; - - return TSDB_CONV_TRUNC; -} - -TSDB_CONV_CODE tsdb_double_to_int(double src, int32_t *dst) { - *dst = (int32_t)src; - - if (srcLONG_MAX) return TSDB_CONV_OOR; - if (src == *dst) return TSDB_CONV_OK; - - int64_t v = (int64_t)src; - if (v == *dst) return TSDB_CONV_TRUNC_FRACTION; - - return TSDB_CONV_TRUNC; -} - -TSDB_CONV_CODE tsdb_double_to_bigint(double src, int64_t *dst) { - *dst = (int64_t)src; - - if (srcLLONG_MAX) return TSDB_CONV_OOR; - if (src == *dst) return TSDB_CONV_OK; - - int64_t v = (int64_t)src; - if (v == *dst) return TSDB_CONV_TRUNC_FRACTION; - - return TSDB_CONV_TRUNC; -} - -TSDB_CONV_CODE tsdb_double_to_ts(double src, int64_t *dst) { - TSDB_CONV_CODE code = tsdb_double_to_bigint(src, dst); - - if (code==TSDB_CONV_OK || code==TSDB_CONV_TRUNC_FRACTION) { - int64_t v = (int64_t)src; - time_t t = (time_t)(v / 1000); - struct tm tm = {0}; - if (localtime_r(&t, &tm)) return TSDB_CONV_OK; - - return TSDB_CONV_OOR; - } - - return code; -} - -TSDB_CONV_CODE tsdb_double_to_char(double src, char *dst, size_t dlen) { - int n = snprintf(dst, dlen, "%lg", src); - DASSERT(n>=0); - - if (n=0); - if (n=19) return TSDB_CONV_TRUNC_FRACTION; - - return TSDB_CONV_TRUNC; -} - -// src: chars -TSDB_CONV_CODE tsdb_chars_to_bit(const char *src, size_t smax, int8_t *dst) { - if (strcmp(src, "0")==0) { - *dst = 0; - return TSDB_CONV_OK; - } - - if (strcmp(src, "1")==0) { - *dst = 1; - return TSDB_CONV_OK; - } - - double v; - int bytes; - int n = sscanf(src, "%lg%n", &v, &bytes); - - if (n!=1) return TSDB_CONV_CHAR_NOT_NUM; - if (bytes!=strlen(src)) return TSDB_CONV_CHAR_NOT_NUM; - - if (v<0 || v>=2) return TSDB_CONV_OOR; - - return TSDB_CONV_TRUNC_FRACTION; -} - -TSDB_CONV_CODE tsdb_chars_to_tinyint(const char *src, size_t smax, int8_t *dst) { - int64_t v; - TSDB_CONV_CODE code = tsdb_chars_to_bigint(src, smax, &v); - if (code!=TSDB_CONV_OK) return code; - - *dst = (int8_t)v; - - if (v==*dst) return TSDB_CONV_OK; - - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_chars_to_smallint(const char *src, size_t smax, int16_t *dst) { - int64_t v; - TSDB_CONV_CODE code = tsdb_chars_to_bigint(src, smax, &v); - if (code!=TSDB_CONV_OK) return code; - - *dst = (int16_t)v; - - if (v==*dst) return TSDB_CONV_OK; - - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_chars_to_int(const char *src, size_t smax, int32_t *dst) { - int64_t v; - TSDB_CONV_CODE code = tsdb_chars_to_bigint(src, smax, &v); - if (code!=TSDB_CONV_OK) return code; - - *dst = (int32_t)v; - - if (v==*dst) return TSDB_CONV_OK; - - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_chars_to_bigint(const char *src, size_t smax, int64_t *dst) { - int bytes; - int n = sscanf(src, "%" PRId64 "%n", dst, &bytes); - - if (n!=1) return TSDB_CONV_CHAR_NOT_NUM; - if (bytes==strlen(src)) { - return TSDB_CONV_OK; - } - - double v; - n = sscanf(src, "%lg%n", &v, &bytes); - if (n!=1) return TSDB_CONV_CHAR_NOT_NUM; - if (bytes==strlen(src)) { - return TSDB_CONV_TRUNC_FRACTION; - } - - return TSDB_CONV_OK; -} - -TSDB_CONV_CODE tsdb_chars_to_ts(const char *src, size_t smax, int64_t *dst) { - int64_t v; - TSDB_CONV_CODE code = tsdb_chars_to_bigint(src, smax, &v); - if (code!=TSDB_CONV_OK) return code; - - *dst = v; - - if (v==*dst) { - time_t t = (time_t)(v / 1000); - struct tm tm = {0}; - if (localtime_r(&t, &tm)) return TSDB_CONV_OK; - } - - return TSDB_CONV_OOR; -} - -TSDB_CONV_CODE tsdb_chars_to_float(const char *src, size_t smax, float *dst) { - int bytes; - int n = sscanf(src, "%g%n", dst, &bytes); - - if (n==1 && bytes==strlen(src)) { - return TSDB_CONV_OK; - } - - return TSDB_CONV_CHAR_NOT_NUM; -} - -TSDB_CONV_CODE tsdb_chars_to_double(const char *src, size_t smax, double *dst) { - int bytes; - int n = sscanf(src, "%lg%n", dst, &bytes); - - if (n==1 && bytes==strlen(src)) { - return TSDB_CONV_OK; - } - - return TSDB_CONV_CHAR_NOT_NUM; -} - -TSDB_CONV_CODE tsdb_chars_to_timestamp(const char *src, size_t smax, SQL_TIMESTAMP_STRUCT *dst) { - int64_t v = 0; - // why cast to 'char*' ? - int r = taosParseTime((char*)src, &v, (int32_t)smax, TSDB_TIME_PRECISION_MILLI, 0); - - if (r) { - return TSDB_CONV_CHAR_NOT_TS; - } - - time_t t = v/1000; - struct tm vtm = {0}; - localtime_r(&t, &vtm); - dst->year = (SQLSMALLINT)(vtm.tm_year + 1900); - dst->month = (SQLUSMALLINT)(vtm.tm_mon + 1); - dst->day = (SQLUSMALLINT)(vtm.tm_mday); - dst->hour = (SQLUSMALLINT)(vtm.tm_hour); - dst->minute = (SQLUSMALLINT)(vtm.tm_min); - dst->second = (SQLUSMALLINT)(vtm.tm_sec); - dst->fraction = (SQLUINTEGER)(v%1000 * 1000000); - - return TSDB_CONV_OK; -} - -TSDB_CONV_CODE tsdb_chars_to_timestamp_ts(const char *src, size_t smax, int64_t *dst) { - // why cast to 'char*' ? - int r = taosParseTime((char*)src, dst, (int32_t)smax, TSDB_TIME_PRECISION_MILLI, 0); - - if (r) { - return TSDB_CONV_CHAR_NOT_TS; - } - - return TSDB_CONV_OK; -} - -TSDB_CONV_CODE tsdb_chars_to_char(const char *src, size_t smax, char *dst, size_t dmax) { - int n = snprintf(dst, dmax, "%s", src); - DASSERT(n>=0); - if (nnext + bytes; - if (next>sizeof(buffer->buf)) return NULL; - - char *p = buffer->buf + buffer->next; - buffer->next = next; - return p; -} - -int is_owned_by_stack_buffer(stack_buffer_t *buffer, const char *ptr) { - if (!buffer) return 0; - if (ptr>=buffer->buf && ptrbuf+buffer->next) return 1; - return 0; -} - - -struct tsdb_conv_s { - iconv_t cnv; - unsigned int direct:1; -}; - -static tsdb_conv_t no_conversion = {0}; -static pthread_once_t once = PTHREAD_ONCE_INIT; -static void once_init(void) { - no_conversion.cnv = (iconv_t)-1; - no_conversion.direct = 1; -} - -tsdb_conv_t* tsdb_conv_direct() { // get a non-conversion-converter - pthread_once(&once, once_init); - return &no_conversion; -} - -tsdb_conv_t* tsdb_conv_open(const char *from_enc, const char *to_enc) { - pthread_once(&once, once_init); - tsdb_conv_t *cnv = (tsdb_conv_t*)calloc(1, sizeof(*cnv)); - if (!cnv) return NULL; - if (strcmp(from_enc, to_enc)==0 && 0) { - cnv->cnv = (iconv_t)-1; - cnv->direct = 1; - return cnv; - } - cnv->cnv = iconv_open(to_enc, from_enc); - if (cnv->cnv == (iconv_t)-1) { - free(cnv); - return NULL; - } - cnv->direct = 0; - return cnv; -} - -void tsdb_conv_close(tsdb_conv_t *cnv) { - if (!cnv) return; - if (cnv == &no_conversion) return; - if (!cnv->direct) { - if (cnv->cnv != (iconv_t)-1) { - iconv_close(cnv->cnv); - } - } - cnv->cnv = (iconv_t)-1; - cnv->direct = 0; - free(cnv); -} - -TSDB_CONV_CODE tsdb_conv_write(tsdb_conv_t *cnv, const char *src, size_t *slen, char *dst, size_t *dlen) { - if (!cnv) return TSDB_CONV_NOT_AVAIL; - if (cnv->direct) { - size_t n = (*slen > *dlen) ? *dlen : *slen; - memcpy(dst, src, n); - *slen -= n; - *dlen -= n; - if (*dlen) dst[n] = '\0'; - return TSDB_CONV_OK; - } - if (!cnv->cnv) return TSDB_CONV_NOT_AVAIL; - size_t r = iconv(cnv->cnv, (char**)&src, slen, &dst, dlen); - if (r==(size_t)-1) return TSDB_CONV_BAD_CHAR; - if (*slen) return TSDB_CONV_TRUNC; - if (*dlen) *dst = '\0'; - return TSDB_CONV_OK; -} - -TSDB_CONV_CODE tsdb_conv_write_int64(tsdb_conv_t *cnv, int64_t val, char *dst, size_t *dlen) { - char utf8[64]; - int n = snprintf(utf8, sizeof(utf8), "%" PRId64 "", val); - DASSERT(n>=0); - DASSERT(n=0); - DASSERT(n=0); - DASSERT(ndirect) { - if (src[slen]=='\0') { // access violation? - *dst = src; - if (dlen) *dlen = slen; - return TSDB_CONV_OK; - } - blen = slen + 1; - } else { - blen = (slen + 1) * 4; - } - - buf = stack_buffer_alloc(buffer, blen); - if (!buf) { - buf = (char*)malloc(blen); - if (!buf) return TSDB_CONV_OOM; - } - - if (cnv->direct) { - size_t n = slen; - DASSERT(blen > n); - memcpy(buf, src, n); - buf[n] = '\0'; - *dst = buf; - if (dlen) *dlen = n; - return TSDB_CONV_OK; - } - - const char *orig_s = src; - char *orig_d = buf; - size_t orig_blen = blen; - - TSDB_CONV_CODE code; - size_t r = iconv(cnv->cnv, (char**)&src, &slen, &buf, &blen); - do { - if (r==(size_t)-1) { - switch(errno) { - case E2BIG: { - code = TSDB_CONV_SRC_TOO_LARGE; - } break; - case EILSEQ: { - code = TSDB_CONV_SRC_BAD_SEQ; - } break; - case EINVAL: { - code = TSDB_CONV_SRC_INCOMPLETE; - } break; - default: { - code = TSDB_CONV_SRC_GENERAL; - } break; - } - break; - } - if (slen) { - code = TSDB_CONV_TRUNC; - break; - } - DASSERT(blen); - *buf = '\0'; - *dst = orig_d; - if (dlen) *dlen = orig_blen - blen; - return TSDB_CONV_OK; - } while (0); - - if (orig_d!=(char*)orig_s && !is_owned_by_stack_buffer(buffer, orig_d)) free(orig_d); - return code; -} - -void tsdb_conv_free(tsdb_conv_t *cnv, const char *ptr, stack_buffer_t *buffer, const char *src) { - if (ptr!=src && !is_owned_by_stack_buffer(buffer, ptr)) free((char*)ptr); -} - diff --git a/src/connector/odbc/src/todbc_conv.h b/src/connector/odbc/src/todbc_conv.h deleted file mode 100644 index 2941f3e4961d38ed1e72bfd3d1184d1ea8de251b..0000000000000000000000000000000000000000 --- a/src/connector/odbc/src/todbc_conv.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#ifndef _todbc_conv_h_ -#define _todbc_conv_h_ - -#include "os.h" -#include -#include - - -typedef enum { - TSDB_CONV_OK = 0, - TSDB_CONV_NOT_AVAIL, - TSDB_CONV_OOM, - TSDB_CONV_OOR, - TSDB_CONV_TRUNC_FRACTION, - TSDB_CONV_TRUNC, - TSDB_CONV_CHAR_NOT_NUM, - TSDB_CONV_CHAR_NOT_TS, - TSDB_CONV_NOT_VALID_TS, - TSDB_CONV_GENERAL, - TSDB_CONV_BAD_CHAR, - TSDB_CONV_SRC_TOO_LARGE, - TSDB_CONV_SRC_BAD_SEQ, - TSDB_CONV_SRC_INCOMPLETE, - TSDB_CONV_SRC_GENERAL, -} TSDB_CONV_CODE; - -const char* tsdb_conv_code_str(TSDB_CONV_CODE code); - -typedef struct stack_buffer_s stack_buffer_t; -struct stack_buffer_s { - char buf[1024*16]; - size_t next; -}; - -char* stack_buffer_alloc(stack_buffer_t *buffer, size_t bytes); -int is_owned_by_stack_buffer(stack_buffer_t *buffer, const char *ptr); - -typedef struct tsdb_conv_s tsdb_conv_t; -tsdb_conv_t* tsdb_conv_direct(); // get a non-conversion-converter -tsdb_conv_t* tsdb_conv_open(const char *from_enc, const char *to_enc); -void tsdb_conv_close(tsdb_conv_t *cnv); - -TSDB_CONV_CODE tsdb_conv_write(tsdb_conv_t *cnv, const char *src, size_t *slen, char *dst, size_t *dlen); -TSDB_CONV_CODE tsdb_conv_write_int64(tsdb_conv_t *cnv, int64_t val, char *dst, size_t *dlen); -TSDB_CONV_CODE tsdb_conv_write_double(tsdb_conv_t *cnv, double val, char *dst, size_t *dlen); -TSDB_CONV_CODE tsdb_conv_write_timestamp(tsdb_conv_t *cnv, SQL_TIMESTAMP_STRUCT val, char *dst, size_t *dlen); - -TSDB_CONV_CODE tsdb_conv_chars_to_bit(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int8_t *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_tinyint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int8_t *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_smallint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int16_t *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_int(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int32_t *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_bigint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_ts(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_float(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, float *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_double(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, double *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_timestamp(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, SQL_TIMESTAMP_STRUCT *dst); -TSDB_CONV_CODE tsdb_conv_chars_to_timestamp_ts(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst); -TSDB_CONV_CODE tsdb_conv(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, const char **dst, size_t *dlen); -void tsdb_conv_free(tsdb_conv_t *cnv, const char *ptr, stack_buffer_t *buffer, const char *src); - - -TSDB_CONV_CODE tsdb_int64_to_bit(int64_t src, int8_t *dst); -TSDB_CONV_CODE tsdb_int64_to_tinyint(int64_t src, int8_t *dst); -TSDB_CONV_CODE tsdb_int64_to_smallint(int64_t src, int16_t *dst); -TSDB_CONV_CODE tsdb_int64_to_int(int64_t src, int32_t *dst); -TSDB_CONV_CODE tsdb_int64_to_bigint(int64_t src, int64_t *dst); -TSDB_CONV_CODE tsdb_int64_to_ts(int64_t src, int64_t *dst); -TSDB_CONV_CODE tsdb_int64_to_float(int64_t src, float *dst); -TSDB_CONV_CODE tsdb_int64_to_double(int64_t src, double *dst); -TSDB_CONV_CODE tsdb_int64_to_char(int64_t src, char *dst, size_t dlen); - -TSDB_CONV_CODE tsdb_double_to_bit(double src, int8_t *dst); -TSDB_CONV_CODE tsdb_double_to_tinyint(double src, int8_t *dst); -TSDB_CONV_CODE tsdb_double_to_smallint(double src, int16_t *dst); -TSDB_CONV_CODE tsdb_double_to_int(double src, int32_t *dst); -TSDB_CONV_CODE tsdb_double_to_bigint(double src, int64_t *dst); -TSDB_CONV_CODE tsdb_double_to_ts(double src, int64_t *dst); -TSDB_CONV_CODE tsdb_double_to_char(double src, char *dst, size_t dlen); - -TSDB_CONV_CODE tsdb_timestamp_to_char(SQL_TIMESTAMP_STRUCT src, char *dst, size_t dlen); - -TSDB_CONV_CODE tsdb_chars_to_bit(const char *src, size_t smax, int8_t *dst); -TSDB_CONV_CODE tsdb_chars_to_tinyint(const char *src, size_t smax, int8_t *dst); -TSDB_CONV_CODE tsdb_chars_to_smallint(const char *src, size_t smax, int16_t *dst); -TSDB_CONV_CODE tsdb_chars_to_int(const char *src, size_t smax, int32_t *dst); -TSDB_CONV_CODE tsdb_chars_to_bigint(const char *src, size_t smax, int64_t *dst); -TSDB_CONV_CODE tsdb_chars_to_ts(const char *src, size_t smax, int64_t *dst); -TSDB_CONV_CODE tsdb_chars_to_float(const char *src, size_t smax, float *dst); -TSDB_CONV_CODE tsdb_chars_to_double(const char *src, size_t smax, double *dst); -TSDB_CONV_CODE tsdb_chars_to_timestamp(const char *src, size_t smax, SQL_TIMESTAMP_STRUCT *dst); -TSDB_CONV_CODE tsdb_chars_to_char(const char *src, size_t smax, char *dst, size_t dmax); - -#endif // _todbc_conv_h_ - diff --git a/src/connector/odbc/src/todbc_flex.h b/src/connector/odbc/src/todbc_flex.h index a13f1f4d2ebd8bbca73d9ff224bb3ed20ed43174..762ffba0bec7bcfd47f45ad4475d08f20dc2375a 100644 --- a/src/connector/odbc/src/todbc_flex.h +++ b/src/connector/odbc/src/todbc_flex.h @@ -16,16 +16,40 @@ #ifndef _TODBC_FLEX_H_ #define _TODBC_FLEX_H_ +#include +#include + +// TSDB predefined field types +// TINYINT SMALLINT INT BIGINT FLOAT DOUBLE BOOL TIMESTAMP BINARY NCHAR + +typedef struct map_tsdb_type_s map_tsdb_type_t; +struct map_tsdb_type_s { + SQLSMALLINT tsdb_tinyint; + SQLSMALLINT tsdb_smallint; + SQLSMALLINT tsdb_int; + SQLSMALLINT tsdb_bigint; + SQLULEN tsdb_bigint_size; + SQLSMALLINT tsdb_float; + SQLSMALLINT tsdb_double; + SQLSMALLINT tsdb_bool; + SQLSMALLINT tsdb_timestamp; + SQLSMALLINT tsdb_binary; + SQLSMALLINT tsdb_nchar; +}; + typedef struct conn_val_s conn_val_t; struct conn_val_s { - char *key; - char *dsn; - char *uid; - char *pwd; - char *db; - char *server; - char *svr_enc; - char *cli_enc; + char *dsn; + char *uid; + char *pwd; + char *db; + char *server; + char *enc_local; + char *enc_char; + char *enc_wchar; + char *enc_db; + + map_tsdb_type_t tsdb_map; }; diff --git a/src/connector/odbc/src/todbc_hash.c b/src/connector/odbc/src/todbc_hash.c new file mode 100644 index 0000000000000000000000000000000000000000..1f64a490fd2adf5688e320c14d22db8c279c9f98 --- /dev/null +++ b/src/connector/odbc/src/todbc_hash.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "todbc_hash.h" + +#include "todbc_list.h" +#include "todbc_log.h" + +#include + +typedef struct todbc_hash_slot_s todbc_hash_slot_t; + +struct todbc_hash_s { + todbc_hash_conf_t conf; + + todbc_hash_slot_t *slots; + size_t n_slots; +}; + +struct todbc_hash_slot_s { + todbc_list_t *list; +}; + +todbc_hash_t* todbc_hash_create(todbc_hash_conf_t conf) { + if (!conf.key_hash) return NULL; + if (!conf.key_comp) return NULL; + if (!conf.val_free) return NULL; + + todbc_hash_t *hash = (todbc_hash_t*)calloc(1, sizeof(*hash)); + if (!hash) return NULL; + + hash->conf = conf; + if (hash->conf.slots==0) { + hash->conf.slots = 33; + } + + hash->slots = (todbc_hash_slot_t*)calloc(hash->conf.slots, sizeof(*hash->slots)); + do { + if (!hash->slots) break; + hash->n_slots = hash->conf.slots; + return hash; + } while (0); + + todbc_hash_free(hash); + return NULL; +} + +void todbc_hash_free(todbc_hash_t *hash) { + if (!hash) return; + for (int i=0; in_slots; ++i) { + todbc_hash_slot_t *slot = hash->slots + i; + if (!slot->list) continue; + todbc_list_free(slot->list); + slot->list = NULL; + } + free(hash->slots); + hash->n_slots = 0; +} + +typedef struct kv_s kv_t; +struct kv_s { + todbc_hash_t *hash; + void *key; + void *val; +}; + +static void do_val_free(todbc_list_t *list, void *val, void *arg) { + todbc_hash_t *hash = (todbc_hash_t*)arg; + DASSERT(list); + DASSERT(hash); + DASSERT(hash->conf.val_free); + hash->conf.val_free(hash, val, hash->conf.arg); +} + +int todbc_hash_put(todbc_hash_t *hash, void *key, void *val) { + if (!hash) return -1; + if (!hash->slots) return -1; + if (!key) return -1; + if (!val) return -1; + + void *old = NULL; + int found = 0; + int r = todbc_hash_get(hash, key, &old, &found); + if (r) return r; + if (found) return -1; + + unsigned long hash_val = hash->conf.key_hash(hash, key); + todbc_hash_slot_t *slot = hash->slots + (hash_val % hash->n_slots); + if (!slot->list) { + todbc_list_conf_t conf = {0}; + conf.arg = hash; + conf.val_free = do_val_free; + + slot->list = todbc_list_create(conf); + if (!slot->list) return -1; + } + + r = todbc_list_pushback(slot->list, val); + + return r; +} + +static int do_comp(todbc_list_t *list, void *old, void *val, void *arg) { + kv_t *kv = (kv_t*)arg; + DASSERT(kv); + DASSERT(kv->hash); + DASSERT(kv->key); + DASSERT(kv->hash->conf.key_comp); + DASSERT(kv->key == val); + int r = kv->hash->conf.key_comp(kv->hash, kv->key, old); + if (r==0) { + kv->val = old; + } + return r; +} + +int todbc_hash_get(todbc_hash_t *hash, void *key, void **val, int *found) { + if (!hash) return -1; + if (!hash->slots) return -1; + if (!key) return -1; + if (!val) return -1; + if (!found) return -1; + + *found = 0; + + unsigned long hash_val = hash->conf.key_hash(hash, key); + todbc_hash_slot_t *slot = hash->slots + (hash_val % hash->n_slots); + if (slot->list) { + kv_t kv = {0}; + kv.hash = hash; + kv.key = key; + kv.val = NULL; + int r = todbc_list_find(slot->list, key, found, do_comp, &kv); + if (*found) { + DASSERT(r==0); + DASSERT(kv.val); + *val = kv.val; + } + return r; + } + + return 0; +} + +int todbc_hash_del(todbc_hash_t *hash, void *key) { + return -1; +} + +typedef struct arg_s arg_t; +struct arg_s { + todbc_hash_t *hash; + void *arg; + int (*iterate)(todbc_hash_t *hash, void *val, void *arg); +}; + +static int do_iterate(todbc_list_t *list, void *val, void *arg); + +int todbc_hash_traverse(todbc_hash_t *hash, int (*iterate)(todbc_hash_t *hash, void *val, void *arg), void *arg) { + if (!hash) return -1; + if (!iterate) return -1; + + for (int i=0; in_slots; ++i) { + todbc_hash_slot_t *slot = hash->slots + i; + if (!slot->list) continue; + arg_t targ = {0}; + targ.hash = hash; + targ.arg = arg; + targ.iterate = iterate; + int r = todbc_list_traverse(slot->list, do_iterate, &targ); + if (r) return r; + } + + return 0; +} + +static int do_iterate(todbc_list_t *list, void *val, void *arg) { + arg_t *targ = (arg_t*)arg; + DASSERT(targ); + DASSERT(targ->iterate); + DASSERT(targ->hash); + return targ->iterate(targ->hash, val, targ->arg); +} + diff --git a/src/connector/odbc/src/todbc_hash.h b/src/connector/odbc/src/todbc_hash.h new file mode 100644 index 0000000000000000000000000000000000000000..9172c8b7f9c79b84f643d25bb317593675e15521 --- /dev/null +++ b/src/connector/odbc/src/todbc_hash.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _todbc_hash_h_ +#define _todbc_hash_h_ + + +#include +#include + +// non-thread-safe + +typedef struct todbc_hash_conf_s todbc_hash_conf_t; +typedef struct todbc_hash_s todbc_hash_t; + +typedef void (*hash_val_free_f)(todbc_hash_t *hash, void *val, void *arg); +typedef unsigned long (*hash_key_hash_f)(todbc_hash_t *hash, void *key); +typedef int (*hash_key_comp_f)(todbc_hash_t *hash, void *key, void *val); + +struct todbc_hash_conf_s { + void *arg; + hash_val_free_f val_free; + + hash_key_hash_f key_hash; + hash_key_comp_f key_comp; + + size_t slots; +}; + +todbc_hash_t* todbc_hash_create(todbc_hash_conf_t conf); +void todbc_hash_free(todbc_hash_t *hash); + +// fail if key exists +int todbc_hash_put(todbc_hash_t *hash, void *key, void *val); +int todbc_hash_get(todbc_hash_t *hash, void *key, void **val, int *found); +int todbc_hash_del(todbc_hash_t *hash, void *key); +typedef int (*hash_iterate_f)(todbc_hash_t *hash, void *val, void *arg); +int todbc_hash_traverse(todbc_hash_t *hash, hash_iterate_f iterate, void *arg); + +#endif // _todbc_hash_h_ + diff --git a/src/connector/odbc/src/todbc_iconv.c b/src/connector/odbc/src/todbc_iconv.c new file mode 100644 index 0000000000000000000000000000000000000000..968cc870f81d420484de3361a54e8185901c3f14 --- /dev/null +++ b/src/connector/odbc/src/todbc_iconv.c @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "todbc_iconv.h" + +#include "todbc_hash.h" +#include "todbc_log.h" +#include "todbc_string.h" +#include "todbc_tls.h" + +#define invalid_iconv() ((iconv_t)-1) + +struct todbc_iconvset_s { + todbc_hash_t *iconv_hash; + + todbc_hash_t *enc_hash; +}; + +typedef struct todbc_iconv_key_s todbc_iconv_key_t; +struct todbc_iconv_key_s { + const char *enc_to; + const char *enc_from; +}; + +struct todbc_iconv_s { + char enc_to[64]; + char enc_from[64]; + todbc_iconv_key_t key; + iconv_t cnv; + + todbc_enc_t from; + todbc_enc_t to; + + unsigned int direct:1; +}; + +static void todbc_iconv_free(todbc_iconv_t *val); + +todbc_iconvset_t* todbc_iconvset_create(void) { + todbc_iconvset_t *cnvset = (todbc_iconvset_t*)calloc(1, sizeof(*cnvset)); + if (!cnvset) return NULL; + + return cnvset; +} + +void todbc_iconvset_free(todbc_iconvset_t *cnvset) { + if (!cnvset) return; + if (cnvset->iconv_hash) { + todbc_hash_free(cnvset->iconv_hash); + cnvset->iconv_hash = NULL; + } + if (cnvset->enc_hash) { + todbc_hash_free(cnvset->enc_hash); + cnvset->enc_hash = NULL; + } + free(cnvset); +} + +static void do_iconv_hash_val_free(todbc_hash_t *hash, void *val, void *arg); +static unsigned long do_iconv_hash_key_hash(todbc_hash_t *hash, void *key); +static int do_iconv_hash_key_comp(todbc_hash_t *hash, void *key, void *val); + +static void do_enc_hash_val_free(todbc_hash_t *hash, void *val, void *arg); +static unsigned long do_enc_hash_key_hash(todbc_hash_t *hash, void *key); +static int do_enc_hash_key_comp(todbc_hash_t *hash, void *key, void *val); + +#define CHK(x, y, n) if (strcasecmp(x, y)==0) return n + +#define SET_SIZES(cs, enc, a,b) do { \ + if (strcasecmp(enc->enc, cs)==0) { \ + enc->char_size = a; \ + enc->variable_char_size = b; \ + return; \ + } \ +} while (0) + +static void do_set_sizes(todbc_enc_t *enc) { + if (!enc) return; + + SET_SIZES("ISO-10646-UCS-2", enc, 2, -1); + SET_SIZES("UCS-2", enc, 2, -1); + SET_SIZES("CSUNICODE", enc, 2, -1); + SET_SIZES("UCS-2BE", enc, 2, -1); + SET_SIZES("UNICODE-1-1", enc, 2, -1); + SET_SIZES("UNICODEBIG", enc, 2, -1); + SET_SIZES("CSUNICODE11", enc, 2, -1); + SET_SIZES("UCS-2LE", enc, 2, -1); + SET_SIZES("UNICODELITTLE", enc, 2, -1); + SET_SIZES("UTF-16", enc, 2, -1); + SET_SIZES("UTF-16BE", enc, 2, -1); + SET_SIZES("UTF-16LE", enc, 2, -1); + SET_SIZES("UCS-2-INTERNAL", enc, 2, -1); + SET_SIZES("UCS-2-SWAPPED", enc, 2, -1); + SET_SIZES("ISO-10646-UCS-4", enc, 4, -1); + SET_SIZES("UCS-4", enc, 4, -1); + SET_SIZES("CSUCS4", enc, 4, -1); + SET_SIZES("UCS-4BE", enc, 4, -1); + SET_SIZES("UCS-4LE", enc, 4, -1); + SET_SIZES("UTF-32", enc, 4, -1); + SET_SIZES("UTF-32BE", enc, 4, -1); + SET_SIZES("UTF-32LE", enc, 4, -1); + SET_SIZES("UCS-4-INTERNAL", enc, 4, -1); + SET_SIZES("UCS-4-SWAPPED", enc, 4, -1); + + SET_SIZES("UTF-8", enc, -1, 3); + SET_SIZES("UTF8", enc, -1, 3); + SET_SIZES("UTF-8-MAC", enc, -1, 3); + SET_SIZES("UTF8-MAC", enc, -1, 3); + + SET_SIZES("CN-GB", enc, 2, -1); + SET_SIZES("EUC-CN", enc, 2, -1); + SET_SIZES("EUCCN", enc, 2, -1); + SET_SIZES("GB2312", enc, 2, -1); + SET_SIZES("CSGB2312", enc, 2, -1); + SET_SIZES("GBK", enc, 2, -1); + SET_SIZES("CP936", enc, 2, -1); + SET_SIZES("MS936", enc, 2, -1); + SET_SIZES("WINDOWS-936", enc, 2, -1); + SET_SIZES("GB18030", enc, 2, -1); + + // add more setup here after + + enc->char_size = -1; + enc->variable_char_size = -1; +} + +static int do_get_null_size(const char *enc, int *null_size); + +// static int do_get_unicode_char_size(const char *enc) { +// if (!enc) return -1; +// +// CHK("ISO-10646-UCS-2", enc, 2); +// CHK("UCS-2", enc, 2); +// CHK("CSUNICODE", enc, 2); +// CHK("UCS-2BE", enc, 2); +// CHK("UNICODE-1-1", enc, 2); +// CHK("UNICODEBIG", enc, 2); +// CHK("CSUNICODE11", enc, 2); +// CHK("UCS-2LE", enc, 2); +// CHK("UNICODELITTLE", enc, 2); +// CHK("UTF-16", enc, 2); +// CHK("UTF-16BE", enc, 2); +// CHK("UTF-16LE", enc, 2); +// CHK("UCS-2-INTERNAL", enc, 2); +// CHK("UCS-2-SWAPPED", enc, 2); +// CHK("ISO-10646-UCS-4", enc, 4); +// CHK("UCS-4", enc, 4); +// CHK("CSUCS4", enc, 4); +// CHK("UCS-4BE", enc, 4); +// CHK("UCS-4LE", enc, 4); +// CHK("UTF-32", enc, 4); +// CHK("UTF-32BE", enc, 4); +// CHK("UTF-32LE", enc, 4); +// CHK("UCS-4-INTERNAL", enc, 4); +// CHK("UCS-4-SWAPPED", enc, 4); +// +// return -1; +// } + +todbc_enc_t todbc_iconvset_enc(todbc_iconvset_t *cnvset, const char *enc) { + do { + if (!cnvset) break; + if (!enc) break; + + if (!cnvset->enc_hash) { + todbc_hash_conf_t conf = {0}; + conf.arg = cnvset; + conf.val_free = do_enc_hash_val_free; + conf.key_hash = do_enc_hash_key_hash; + conf.key_comp = do_enc_hash_key_comp; + conf.slots = 7; + cnvset->enc_hash = todbc_hash_create(conf); + if (!cnvset->enc_hash) break; + } + + void *old = NULL; + int found = 0; + int r = todbc_hash_get(cnvset->enc_hash, (void*)enc, &old, &found); + if (r) { + DASSERT(found==0); + DASSERT(old==NULL); + break; + } + + if (found) { + DASSERT(old); + todbc_enc_t *val = (todbc_enc_t*)old; + return *val; + } + + todbc_enc_t *val = (todbc_enc_t*)calloc(1, sizeof(*val)); + if (!val) break; + do { + if (snprintf(val->enc, sizeof(val->enc), "%s", enc)>=sizeof(val->enc)) { + break; + } + do_set_sizes(val); + + if (do_get_null_size(val->enc, &val->null_size)) break; + + return *val; + } while (0); + + free(val); + } while (0); + + todbc_enc_t v = {0}; + v.char_size = -1; + v.null_size = -1; + + return v; +} + +todbc_iconv_t* todbc_iconvset_get(todbc_iconvset_t *cnvset, const char *enc_to, const char *enc_from) { + if (!cnvset) return NULL; + if (!enc_to) return NULL; + if (!enc_from) return NULL; + todbc_iconv_key_t key; + key.enc_to = enc_to; + key.enc_from = enc_from; + + if (!cnvset->iconv_hash) { + todbc_hash_conf_t conf = {0}; + conf.arg = cnvset; + conf.val_free = do_iconv_hash_val_free; + conf.key_hash = do_iconv_hash_key_hash; + conf.key_comp = do_iconv_hash_key_comp; + conf.slots = 7; + cnvset->iconv_hash = todbc_hash_create(conf); + if (!cnvset->iconv_hash) return NULL; + } + + void *old = NULL; + int found = 0; + int r = todbc_hash_get(cnvset->iconv_hash, &key, &old, &found); + if (r) { + DASSERT(found==0); + DASSERT(old==NULL); + return NULL; + } + + if (found) { + DASSERT(old); + todbc_iconv_t *val = (todbc_iconv_t*)old; + // D("found [%p] for [%s->%s]", val, enc_from, enc_to); + return val; + } + + todbc_iconv_t *val = (todbc_iconv_t*)calloc(1, sizeof(*val)); + if (!val) return NULL; + do { + if (snprintf(val->enc_to, sizeof(val->enc_to), "%s", enc_to)>=sizeof(val->enc_to)) { + break; + } + if (snprintf(val->enc_from, sizeof(val->enc_from), "%s", enc_from)>=sizeof(val->enc_from)) { + break; + } + val->key.enc_to = val->enc_to; + val->key.enc_from = val->enc_from; + if (strcasecmp(enc_to, enc_from)==0) { + val->direct = 1; + } + + val->from = todbc_tls_iconv_enc(enc_from); + val->to = todbc_tls_iconv_enc(enc_to); + + val->cnv = iconv_open(enc_to, enc_from); + if (val->cnv==invalid_iconv()) break; + + r = todbc_hash_put(cnvset->iconv_hash, &key, val); + + if (r) break; + + // D("created [%p] for [%s->%s]", val, enc_from, enc_to); + return val; + } while (0); + + todbc_iconv_free(val); + return NULL; +} + +iconv_t todbc_iconv_get(todbc_iconv_t *cnv) { + if (!cnv) return invalid_iconv(); + return cnv->cnv; +} + +// static int todbc_legal_chars_by_cnv(iconv_t cnv, const unsigned char *str, todbc_bytes_t *bc); + +int todbc_iconv_get_legal_chars(todbc_iconv_t *cnv, const unsigned char *str, todbc_bytes_t *bc) { + DASSERT(0); + // if (!cnv) return -1; + // if (bc->inbytes==0 || bc->inbytes > INT64_MAX) { + // DASSERT(bc->chars<=INT64_MAX); + // if (bc->chars > 0) { + // DASSERT(cnv->from_char_size==2 || cnv->from_char_size==4); + // bc->inbytes = ((size_t)cnv->from_char_size) * bc->chars; + // bc->chars = 0; + // } + // } else { + // DASSERT(bc->chars==0); + // } + // return todbc_legal_chars_by_cnv(todbc_iconv_get(cnv), str, bc); +} + +// static int todbc_legal_chars_by_block(iconv_t cnv, const unsigned char *str, todbc_bytes_t *bc); + +// static int todbc_legal_chars_by_cnv(iconv_t cnv, const unsigned char *str, todbc_bytes_t *bc) { +// size_t max_bytes = bc->inbytes; +// if (max_bytes > INT64_MAX) max_bytes = (size_t)-1; +// +// if (max_bytes != (size_t)-1) { +// return todbc_legal_chars_by_block(cnv, str, bc); +// } +// +// memset(bc, 0, sizeof(*bc)); +// +// size_t nbytes = 0; +// size_t ch_bytes = 0; +// +// char buf[16]; +// char *inbuf = (char*)str; +// char *outbuf; +// size_t outbytes; +// size_t inbytes; +// +// size_t n = 0; +// +// int r = 0; +// inbytes = 1; +// while (1) { +// if (nbytes==max_bytes) break; +// outbytes = sizeof(buf); +// outbuf = buf; +// +// ch_bytes = inbytes; +// n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes); +// nbytes += 1; +// if (n==(size_t)-1) { +// int err = errno; +// if (err!=EINVAL) { +// E("......."); +// r = -1; +// break; +// } +// inbytes = ch_bytes + 1; +// continue; +// } +// DASSERT(inbytes==0); +// n = sizeof(buf) - outbytes; +// +// DASSERT(n==2); +// if (buf[0]=='\0' && buf[1]=='\0') { +// ch_bytes = 0; +// break; +// } +// +// bc->inbytes += ch_bytes; +// bc->chars += 1; +// bc->outbytes += 2; +// +// ch_bytes = 0; +// inbytes = 1; +// } +// +// outbytes = sizeof(buf); +// outbuf = buf; +// n = iconv(cnv, NULL, NULL, &outbuf, &outbytes); +// +// if (r) return -1; +// if (n==(size_t)-1) return -1; +// if (outbytes!=sizeof(buf)) return -1; +// if (outbuf!=buf) return -1; +// if (ch_bytes) return -1; +// return 0; +// } + +unsigned char* todbc_iconv_conv(todbc_iconv_t *cnv, todbc_buf_t *buf, const unsigned char *src, todbc_bytes_t *bc) { + if (!buf) { + buf = todbc_tls_buf(); + if (!buf) return NULL; + } + if (bc==NULL) { + todbc_bytes_t x = {0}; + x.inbytes = (size_t)-1; + return todbc_iconv_conv(cnv, buf, src, &x); + } + + DASSERT(buf); + DASSERT(cnv); + DASSERT(src); + + todbc_string_t s = todbc_string_init(cnv->from.enc, src, bc->inbytes); + // D("total_bytes/bytes: %d/%zu/%zu", s.total_bytes, s.bytes); + DASSERT(s.buf==src); + todbc_string_t t = todbc_string_conv_to(&s, cnv->to.enc, NULL); + DASSERT(t.buf); + // bc->outbytes not counting size of null-terminator + bc->outbytes = t.bytes; + return (unsigned char*)t.buf; +} + +size_t todbc_iconv_est_bytes(todbc_iconv_t *cnv, size_t inbytes) { + DASSERT(cnv); + DASSERT(inbytes<=INT64_MAX); + const todbc_enc_t *from = &cnv->from; + const todbc_enc_t *to = &cnv->to; + + size_t outbytes = inbytes; + do { + if (from == to) break; + + size_t inchars = inbytes; + if (from->char_size > 1) { + inchars = (inbytes + (size_t)from->char_size - 1) / (size_t)from->char_size; + } + outbytes = inchars; + size_t char_size = MAX_CHARACTER_SIZE; + if (to->char_size > 0) { + char_size = (size_t)to->char_size; + } else if (to->variable_char_size > 0) { + char_size = (size_t)to->variable_char_size; + } + outbytes *= char_size; + } while (0); + + size_t nullbytes = MAX_CHARACTER_SIZE; + if (to->null_size > 0) { + nullbytes = (size_t)to->null_size; + } + // D("%s->%s: %zu->%zu", from->enc, to->enc, inbytes, outbytes); + outbytes += nullbytes; + // D("%s->%s: %zu->%zu", from->enc, to->enc, inbytes, outbytes); + + return outbytes; +} + +size_t todbc_iconv_bytes(todbc_iconv_t *cnv, size_t inchars) { + DASSERT(cnv); + if (inchars >= INT64_MAX) return (size_t)-1; + const todbc_enc_t *from = &cnv->from; + if (from->char_size > 0) { + return inchars * (size_t)from->char_size; + } + return (size_t)-1; +} + +todbc_enc_t todbc_iconv_from(todbc_iconv_t *cnv) { + DASSERT(cnv); + return cnv->from; +} + +todbc_enc_t todbc_iconv_to(todbc_iconv_t *cnv) { + DASSERT(cnv); + return cnv->to; +} + +int todbc_iconv_raw(todbc_iconv_t *cnv, const unsigned char *src, size_t *slen, unsigned char *dst, size_t *dlen) { + DASSERT(cnv); + DASSERT(src && dst); + DASSERT(slen && *slen < INT64_MAX); + DASSERT(dlen && *dlen < INT64_MAX); + + const int null_bytes = todbc_iconv_to(cnv).null_size; + + if (*dlen<=null_bytes) { + D("target buffer too small to hold even null-terminator"); + *dlen = 0; // while slen does not change + return -1; + } + + char *inbuf = (char*)src; + size_t inbytes = *slen; + char *outbuf = (char*)dst; + size_t outbytes = *dlen; + + size_t n = iconv(cnv->cnv, &inbuf, &inbytes, &outbuf, &outbytes); + int e = 0; + if (n==(size_t)-1) { + e = errno; + D("iconv failed: [%s->%s]:[%d]%s", cnv->from.enc, cnv->to.enc, e, strerror(e)); + } else { + DASSERT(n==0); + } + + const size_t inremain = inbytes; + const size_t outremain = outbytes; + *slen = inremain; + *dlen = outremain; + + // writing null-terminator to make dest a real string + DASSERT(outbytes <= INT64_MAX); + if (outbytes < null_bytes) { + D("target buffer too small to hold null-terminator"); + return -1; + } else { + for (int i=0; icnv, NULL, NULL, NULL, NULL); + + return 0; +} + +todbc_string_t todbc_iconv_conv2(todbc_iconv_t *cnv, todbc_buf_t *buf, const unsigned char *src, size_t *slen) { + const todbc_string_t nul = {0}; + if (!buf) { + buf = todbc_tls_buf(); + if (!buf) return nul; + } + DASSERT(cnv); + DASSERT(src); + + size_t inbytes = (size_t)-1; + if (slen && *slen <= INT64_MAX) inbytes = *slen; + + todbc_string_t in = todbc_string_init(todbc_iconv_from(cnv).enc, src, inbytes); + if (in.buf!=src) return nul; + if (in.bytes > INT64_MAX) return nul; + inbytes = in.bytes; + + size_t outblock = todbc_iconv_est_bytes(cnv, in.bytes); + if (outblock > INT64_MAX) return nul; + + unsigned char *out = todbc_buf_alloc(buf, outblock); + if (!out) return nul; + + const unsigned char *inbuf = src; + unsigned char *outbuf = out; + + + size_t outbytes = outblock; + int r = todbc_iconv_raw(cnv, inbuf, &inbytes, outbuf, &outbytes); + if (slen) *slen = inbytes; + if (r) return nul; + todbc_string_t s = {0}; + s.buf = outbuf; + s.bytes = outblock - outbytes; + s.total_bytes = s.bytes; + return s; +} + +// static int todbc_legal_chars_by_block(iconv_t cnv, const unsigned char *str, todbc_bytes_t *bc) { +// int r = 0; +// size_t max_bytes = bc->inbytes; +// char buf[1024*16]; +// memset(bc, 0, sizeof(*bc)); +// char *inbuf = (char*)str; +// while (bc->inbytesinbytes; +// size_t outbytes = sizeof(buf); +// char *outbuf = buf; +// size_t n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes); +// int err = 0; +// if (n==(size_t)-1) { +// err = errno; +// if (err!=E2BIG && err!=EINVAL) r = -1; +// } else { +// DASSERT(n==0); +// } +// if (inbytes == max_bytes - bc->inbytes) { +// r = -1; +// } +// bc->inbytes += max_bytes - bc->inbytes - inbytes; +// bc->outbytes += sizeof(buf) - outbytes; +// if (r) break; +// } +// bc->chars = bc->outbytes / 2; +// iconv(cnv, NULL, NULL, NULL, NULL); +// return r ? -1 : 0; +// } + +static void todbc_iconv_free(todbc_iconv_t *val) { + if (!val) return; + if (val->cnv!=invalid_iconv()) { + iconv_close(val->cnv); + val->cnv = invalid_iconv(); + } + free(val); +} + +// http://www.cse.yorku.ca/~oz/hash.html +static const unsigned long hash_seed = 5381; +static unsigned long hashing(unsigned long hash, const unsigned char *str); + +static void do_enc_hash_val_free(todbc_hash_t *hash, void *val, void *arg) { + todbc_iconvset_t *cnv = (todbc_iconvset_t*)arg; + todbc_enc_t *v = (todbc_enc_t*)val; + DASSERT(hash); + DASSERT(cnv); + DASSERT(v); + free(v); +} + +static unsigned long do_enc_hash_key_hash(todbc_hash_t *hash, void *key) { + const char *k = (const char*)key; + DASSERT(k); + unsigned long h = hash_seed; + h = hashing(h, (const unsigned char*)k); + return h; +} + +static int do_enc_hash_key_comp(todbc_hash_t *hash, void *key, void *val) { + const char *k = (const char*)key; + todbc_enc_t *v = (todbc_enc_t*)val; + if (strcasecmp(k, v->enc)) return 1; + return 0; +} + +static int do_get_null_size(const char *enc, int *null_size) { + iconv_t cnv = iconv_open(enc, UTF8_ENC); + if (cnv==(iconv_t)-1) return -1; + + char src[] = ""; + char dst[64]; + + char *inbuf = src; + size_t inbytes = 1; + char *outbuf = dst; + size_t outbytes = sizeof(dst); + + size_t n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes); + DASSERT(n==0); + DASSERT(inbytes==0); + + int size = (int)(sizeof(dst) - outbytes); + + iconv_close(cnv); + + if (null_size) *null_size = size; + + return 0; +} + +static void do_iconv_hash_val_free(todbc_hash_t *hash, void *val, void *arg) { + todbc_iconvset_t *cnv = (todbc_iconvset_t*)arg; + todbc_iconv_t *v = (todbc_iconv_t*)val; + DASSERT(hash); + DASSERT(cnv); + DASSERT(v); + todbc_iconv_free(v); +} + +static unsigned long do_iconv_hash_key_hash(todbc_hash_t *hash, void *key) { + todbc_iconv_key_t *k = (todbc_iconv_key_t*)key; + DASSERT(k); + unsigned long h = hash_seed; + h = hashing(h, (const unsigned char*)k->enc_to); + h = hashing(h, (const unsigned char*)k->enc_from); + return h; +} + +static int do_iconv_hash_key_comp(todbc_hash_t *hash, void *key, void *val) { + todbc_iconv_key_t *k = (todbc_iconv_key_t*)key; + todbc_iconv_t *v = (todbc_iconv_t*)val; + if (strcasecmp(k->enc_to, v->key.enc_to)) return 1; + if (strcasecmp(k->enc_from, v->key.enc_from)) return 1; + return 0; +} + +static unsigned long hashing(unsigned long hash, const unsigned char *str) { + unsigned long c; + while ((c = *str++)!=0) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + + return hash; +} + diff --git a/src/connector/odbc/src/todbc_iconv.h b/src/connector/odbc/src/todbc_iconv.h new file mode 100644 index 0000000000000000000000000000000000000000..269900f8f1fc77da2bdf6dd4e298ed765e5e7e9e --- /dev/null +++ b/src/connector/odbc/src/todbc_iconv.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _todbc_iconv_h_ +#define _todbc_iconv_h_ + +#include "todbc_buf.h" +#include "todbc_string.h" + +#include + +// non-thread-safe + +#define ASCII_ENC "ASCII" +#define UTF8_ENC "UTF-8" +#define UTF16_ENC "UCS-2LE" +#define UNICODE_ENC "UCS-4LE" +#define GB18030_ENC "GB18030" + +#define MAX_CHARACTER_SIZE 6 + +typedef enum { + TSDB_CONV_OK = 0, + TSDB_CONV_NOT_AVAIL, + TSDB_CONV_OOM, + TSDB_CONV_OOR, + TSDB_CONV_TRUNC_FRACTION, + TSDB_CONV_TRUNC, + TSDB_CONV_CHAR_NOT_NUM, + TSDB_CONV_CHAR_NOT_TS, + TSDB_CONV_NOT_VALID_TS, + TSDB_CONV_GENERAL, + TSDB_CONV_BAD_CHAR, + TSDB_CONV_SRC_TOO_LARGE, + TSDB_CONV_SRC_BAD_SEQ, + TSDB_CONV_SRC_INCOMPLETE, + TSDB_CONV_SRC_GENERAL, +} TSDB_CONV_CODE; + +typedef struct todbc_iconvset_s todbc_iconvset_t; +typedef struct todbc_iconv_s todbc_iconv_t; +typedef struct todbc_enc_s todbc_enc_t; +struct todbc_enc_s { + int char_size; // character size at most + int null_size; // size for null terminator + int variable_char_size; // such as 3 for UTF8 + char enc[64]; // move here to satisfy todbc_enc_t enc = {0}; +}; + +todbc_iconvset_t* todbc_iconvset_create(void); +void todbc_iconvset_free(todbc_iconvset_t *cnvset); +todbc_iconv_t* todbc_iconvset_get(todbc_iconvset_t *cnvset, const char *enc_to, const char *enc_from); +todbc_enc_t todbc_iconvset_enc(todbc_iconvset_t *cnvset, const char *enc); + +typedef struct todbc_bytes_s todbc_bytes_t; +typedef struct todbc_err_s todbc_err_t; + +struct todbc_err_s { + unsigned int einval:1; // EINVAL + unsigned int eilseq:1; // EILSEQ + unsigned int etoobig:1; // E2BIG + unsigned int eoom:1; // ENOMEM +}; + +struct todbc_bytes_s { + size_t inbytes, outbytes; + size_t chars; + + todbc_err_t err; +}; + +typedef struct todbc_iconv_arg_s todbc_iconv_arg_t; +struct todbc_iconv_arg_s { + const unsigned char *inbuf; + size_t inbytes; // -1: not set + unsigned char *outbuf; + size_t outbytes; // -1: not set + + size_t chars; // -1: not set +}; + +iconv_t todbc_iconv_get(todbc_iconv_t *cnv); +int todbc_iconv_get_legal_chars(todbc_iconv_t *cnv, const unsigned char *str, todbc_bytes_t *bc); +// non-thread-safe +// use todbc_buf_t as mem-allocator, if NULL, fall-back to thread-local version +unsigned char* todbc_iconv_conv(todbc_iconv_t *cnv, todbc_buf_t *buf, const unsigned char *src, todbc_bytes_t *bc); +// null-terminator-inclusive +size_t todbc_iconv_est_bytes(todbc_iconv_t *cnv, size_t inbytes); +// if inchars>=0 && enc_from has fixed-char-size, returns inchars * char_size +// otherwise -1 +size_t todbc_iconv_bytes(todbc_iconv_t *cnv, size_t inchars); +todbc_enc_t todbc_iconv_from(todbc_iconv_t *cnv); +todbc_enc_t todbc_iconv_to(todbc_iconv_t *cnv); + +// at return, *slen/*dlen stores the remaining # +int todbc_iconv_raw(todbc_iconv_t *cnv, const unsigned char *src, size_t *slen, unsigned char *dst, size_t *dlen); +// use todbc_buf_t as mem-allocator, if NULL, fall-back to thread-local version +// at return, *slen stores the remaining # +todbc_string_t todbc_iconv_conv2(todbc_iconv_t *cnv, todbc_buf_t *buf, const unsigned char *src, size_t *slen); + +#endif // _todbc_iconv_h_ + diff --git a/src/connector/odbc/src/todbc_list.c b/src/connector/odbc/src/todbc_list.c new file mode 100644 index 0000000000000000000000000000000000000000..447ce4826c4d6417af3c7d983a9278c8c6adf32c --- /dev/null +++ b/src/connector/odbc/src/todbc_list.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "todbc_list.h" + +#include "todbc_log.h" + +#include + +struct todbc_list_s { + todbc_list_conf_t conf; + + todbc_list_node_t *head; + todbc_list_node_t *tail; + size_t count; +}; + +struct todbc_list_node_s { + void *val; + + todbc_list_t *list; + todbc_list_node_t *next; + todbc_list_node_t *prev; +}; + +static void do_remove(todbc_list_t *list, todbc_list_node_t *node); + +static void do_pushback(todbc_list_t *list, todbc_list_node_t *node); +static void do_pushfront(todbc_list_t *list, todbc_list_node_t *node); + +todbc_list_t* todbc_list_create(todbc_list_conf_t conf) { + todbc_list_t *list = (todbc_list_t*)calloc(1, sizeof(*list)); + if (!list) return NULL; + + list->conf = conf; + + return list; +} + +void todbc_list_free(todbc_list_t *list) { + if (!list) return; + + while (list->head) { + void *val = list->head->val; + do_remove(list, list->head); + if (!list->conf.val_free) continue; + list->conf.val_free(list, val, list->conf.arg); + } + + DASSERT(list->count == 0); +} + +size_t todbc_list_count(todbc_list_t *list) { + if (!list) return 0; + return list->count; +} + +int todbc_list_pushback(todbc_list_t *list, void *val) { + if (!list) return -1; + + todbc_list_node_t *node = (todbc_list_node_t*)calloc(1, sizeof(*node)); + if (!node) return -1; + node->val = val; + + do_pushback(list, node); + + return 0; +} + +int todbc_list_pushfront(todbc_list_t *list, void *val) { + if (!list) return -1; + + todbc_list_node_t *node = (todbc_list_node_t*)calloc(1, sizeof(*node)); + if (!node) return -1; + node->val = val; + + do_pushfront(list, node); + + return 0; +} + +int todbc_list_popback(todbc_list_t *list, void **val, int *found) { + if (!list) return -1; + if (!found) return -1; + + *found = 0; + + todbc_list_node_t *node = list->tail; + if (!node) return 0; + + if (val) *val = node->val; + do_remove(list, node); + + *found = 1; + + return 0; +} + +int todbc_list_popfront(todbc_list_t *list, void **val, int *found) { + if (!list) return -1; + if (!found) return -1; + + *found = 0; + + todbc_list_node_t *node = list->head; + if (!node) return 0; + + if (val) *val = node->val; + do_remove(list, node); + + *found = 1; + + return 0; +} + +int todbc_list_pop(todbc_list_t *list, void *val, int *found) { + if (!list) return -1; + if (!found) return -1; + + *found = 0; + + todbc_list_node_t *node = list->head; + while (node) { + if (node->val == val) break; + node = node->next; + } + + if (!node) return 0; + + do_remove(list, node); + + *found = 1; + + return 0; +} + +int todbc_list_traverse(todbc_list_t *list, list_iterate_f iterate, void *arg) { + if (!list) return -1; + if (!iterate) return -1; + + todbc_list_node_t *node = list->head; + while (node) { + int r = iterate(list, node->val, arg); + if (r) return r; + node = node->next; + } + + return 0; +} + +typedef struct comp_s comp_t; +struct comp_s { + list_comp_f comp; + void *arg; + int found; + void *val; +}; + +static int do_comp(todbc_list_t *list, void *val, void *arg); + +int todbc_list_find(todbc_list_t *list, void *val, int *found, list_comp_f comp, void *arg) { + if (!list) return -1; + if (!found) return -1; + if (!comp) return -1; + + *found = 0; + + comp_t sarg = {0}; + sarg.comp = comp; + sarg.arg = arg; + sarg.val = val; + todbc_list_traverse(list, do_comp, &sarg); + if (sarg.found) { + *found = 1; + } + return 0; +} + +static int do_comp(todbc_list_t *list, void *val, void *arg) { + comp_t *sarg = (comp_t*)arg; + + int r = sarg->comp(list, val, sarg->val, sarg->arg); + if (r==0) { + sarg->found = 1; + } + + return r; +} + +static void do_remove(todbc_list_t *list, todbc_list_node_t *node) { + DASSERT(node); + DASSERT(list); + DASSERT(list == node->list); + + todbc_list_node_t *prev = node->prev; + todbc_list_node_t *next = node->next; + if (prev) prev->next = next; + else list->head = next; + if (next) next->prev = prev; + else list->tail = prev; + node->prev = NULL; + node->next = NULL; + node->list = NULL; + node->val = NULL; + + list->count -= 1; + DASSERT(list->count <= INT64_MAX); + + free(node); +} + +static void do_pushback(todbc_list_t *list, todbc_list_node_t *node) { + DASSERT(list); + DASSERT(node); + DASSERT(node->list == NULL); + DASSERT(node->prev == NULL); + DASSERT(node->next == NULL); + + node->list = list; + node->prev = list->tail; + if (list->tail) list->tail->next = node; + else list->head = node; + list->tail = node; + + list->count += 1; + DASSERT(list->count > 0); +} + +static void do_pushfront(todbc_list_t *list, todbc_list_node_t *node) { + DASSERT(node); + DASSERT(node->list == NULL); + DASSERT(node->prev == NULL); + DASSERT(node->next == NULL); + + node->list = list; + node->next = list->head; + if (list->head) list->head->prev = node; + else list->tail = node; + list->head = node; + + list->count += 1; + DASSERT(list->count > 0); +} + diff --git a/src/connector/odbc/src/todbc_list.h b/src/connector/odbc/src/todbc_list.h new file mode 100644 index 0000000000000000000000000000000000000000..d84ffd88a0e8d06cea2b7382da8cba5b881813a1 --- /dev/null +++ b/src/connector/odbc/src/todbc_list.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _todbc_list_h_ +#define _todbc_list_h_ + +#include +#include + +// non-thread-safe + +typedef struct todbc_list_s todbc_list_t; +typedef struct todbc_list_node_s todbc_list_node_t; + +typedef struct todbc_list_conf_s todbc_list_conf_t; + +typedef void (*list_val_free_f)(todbc_list_t *list, void *val, void *arg); + +struct todbc_list_conf_s { + void *arg; + list_val_free_f val_free; +}; + +todbc_list_t* todbc_list_create(todbc_list_conf_t conf); +void todbc_list_free(todbc_list_t *list); + +size_t todbc_list_count(todbc_list_t *list); + +int todbc_list_pushback(todbc_list_t *list, void *val); +int todbc_list_pushfront(todbc_list_t *list, void *val); +int todbc_list_popback(todbc_list_t *list, void **val, int *found); +int todbc_list_popfront(todbc_list_t *list, void **val, int *found); +int todbc_list_pop(todbc_list_t *list, void *val, int *found); +typedef int (*list_iterate_f)(todbc_list_t *list, void *val, void *arg); +int todbc_list_traverse(todbc_list_t *list, list_iterate_f iterate, void *arg); +typedef int (*list_comp_f)(todbc_list_t *list, void *old, void *val, void *arg); +int todbc_list_find(todbc_list_t *list, void *val, int *found, list_comp_f comp, void *arg); + + +#endif // _todbc_list_h_ + diff --git a/src/connector/odbc/src/todbc_log.c b/src/connector/odbc/src/todbc_log.c new file mode 100644 index 0000000000000000000000000000000000000000..c83ad2d62df3ce08cb70bd035898ec9e33b893fe --- /dev/null +++ b/src/connector/odbc/src/todbc_log.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "todbc_log.h" + +uint64_t todbc_get_threadid(void) { + uint64_t tid = 0; +#ifdef __APPLE__ + pthread_threadid_np(NULL, &tid); +#elif defined(__linux__) + tid = (uint64_t)syscall(__NR_gettid); +#elif defined(_MSC_VER) + tid = GetCurrentThreadId(); +#else +#error you have to check the target API for thread id +#endif + return tid; +} + +#ifdef _MSC_VER +static __declspec(thread) uint64_t thread_id = 0; +#else +static __thread uint64_t thread_id = 0; +#endif + +#ifdef __GNUC__ + __attribute__((format(printf, 6, 7))) +#endif +void todbc_log(const char *file, int line, const char *func, const char tag, int err, const char *fmt, ...) { + struct tm stm = {0}; + struct timeval tv; + + if (thread_id==0) { + thread_id = todbc_get_threadid(); + } + + gettimeofday(&tv, NULL); + time_t tt = tv.tv_sec; + localtime_r(&tt, &stm); + + char buf[4096]; + size_t bytes = sizeof(buf); + + char *p = buf; + int n = 0; + + n = snprintf(p, bytes, "%C%02d:%02d:%02d.%06d[%" PRIx64 "]%s[%d]%s()", + tag, stm.tm_hour, stm.tm_min, stm.tm_sec, (int)tv.tv_usec, + thread_id, basename((char*)file), line, func); + if (n>0) { + bytes -= (size_t)n; + p += n; + } + + if (bytes>0) { + if (tag=='E' && err) { + n = snprintf(p, bytes, "[%d]%s", err, strerror(err)); + if (n>0) { + bytes -= (size_t)n; + p += n; + } + } + } + + if (bytes>0) { + n = snprintf(p, bytes, ": "); + if (n>0) { + bytes -= (size_t)n; + p += n; + } + } + + if (bytes>0) { + va_list ap; + va_start(ap, fmt); + n = vsnprintf(p, bytes, fmt, ap); + va_end(ap); + } + + fprintf(stderr, "%s\n", buf); +} + diff --git a/src/connector/odbc/src/todbc_log.h b/src/connector/odbc/src/todbc_log.h index 391a690cccb0954736cac76af3354cc8a39754a8..30ef8436e1e02535f9a7a3eaefc456a792bd1744 100644 --- a/src/connector/odbc/src/todbc_log.h +++ b/src/connector/odbc/src/todbc_log.h @@ -18,25 +18,35 @@ #include "os.h" -#define D(fmt, ...) \ - fprintf(stderr, \ - "%s[%d]:%s() " fmt "\n", \ - basename((char*)__FILE__), __LINE__, __func__, \ - ##__VA_ARGS__) - -#define DASSERT(statement) \ -do { \ - if (statement) break; \ - D("Assertion failure: %s", #statement); \ - abort(); \ -} while (0) +#ifdef __GNUC__ + __attribute__((format(printf, 6, 7))) +#endif +void todbc_log(const char *file, int line, const char *func, const char tag, int err, const char *fmt, ...); -#define DASSERTX(statement, fmt, ...) \ -do { \ - if (statement) break; \ - D("Assertion failure: %s, " fmt "", #statement, ##__VA_ARGS__); \ - abort(); \ +#define OL(tag, err, fmt, ...) todbc_log(__FILE__, __LINE__, __func__, tag, err, "%s" fmt "", "", ##__VA_ARGS__) +#define OD(fmt, ...) OL('D', 0, fmt, ##__VA_ARGS__) +#define OE(fmt, ...) OL('E', errno, fmt, ##__VA_ARGS__) +#define OW(fmt, ...) OL('W', 0, fmt, ##__VA_ARGS__) +#define OI(fmt, ...) OL('I', 0, fmt, ##__VA_ARGS__) +#define OV(fmt, ...) OL('V', 0, fmt, ##__VA_ARGS__) +#define OA(statement, fmt, ...) do { \ + if (statement) break; \ + OL('A', 0, "Assertion failure:[%s]; " fmt "", #statement, ##__VA_ARGS__); \ + abort(); \ } while (0) +#define OILE(statement, fmt, ...) OA(statement, "internal logic error: [" fmt "]", ##__VA_ARGS__) +#define ONIY(statement, fmt, ...) OA(statement, "not implemented yet: [" fmt "]", ##__VA_ARGS__) +#define ONSP(statement, fmt, ...) OA(statement, "not support yet: [" fmt "]", ##__VA_ARGS__) + +#define D(fmt, ...) OD(fmt, ##__VA_ARGS__) +#define E(fmt, ...) OE(fmt, ##__VA_ARGS__) +#define DASSERT(statement) OA(statement, "") +#define DASSERTX(statement, fmt, ...) OA(statement, fmt, ##__VA_ARGS__) + + +uint64_t todbc_get_threadid(void); + + #endif // _todbc_log_h_ diff --git a/src/connector/odbc/src/todbc_scanner.l b/src/connector/odbc/src/todbc_scanner.l index f8c6a15d92442ee7f7b9041f017b41a4ed590314..b36c894f73cd7d8eb3b1642eb8b9c3bb433f2a5b 100644 --- a/src/connector/odbc/src/todbc_scanner.l +++ b/src/connector/odbc/src/todbc_scanner.l @@ -1,11 +1,18 @@ %{ +#ifdef _MSC_VER +#include +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#define basename PathFindFileNameA +#else +#include +#endif + #include "todbc_flex.h" #include +#include -#ifdef _MSC_VER -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#endif +static int process_map(const char *cfg, map_tsdb_type_t *tsdb_map, int type); #define PUSH_STATE(state) yy_push_state(state, yyscanner) #define POP_STATE() yy_pop_state(yyscanner) @@ -28,50 +35,73 @@ do { \ while (yyleng) unput(yytext[yyleng-1]); \ } while (0) -#define set_key() \ -do { \ - free(yyextra->key); \ - yyextra->key = strdup(yytext); \ +#define set_val() \ +do { \ + int r = 0; \ + int curr; TOP_STATE(curr); \ + POP_STATE(); \ + int state; TOP_STATE(state); \ + switch(state) { \ + case DSN: { \ + free(yyextra->dsn); \ + yyextra->dsn = strdup(yytext); \ + } break; \ + case UID: { \ + free(yyextra->uid); \ + yyextra->uid = strdup(yytext); \ + } break; \ + case PWD: { \ + free(yyextra->pwd); \ + yyextra->pwd = strdup(yytext); \ + } break; \ + case SERVER: { \ + free(yyextra->server); \ + yyextra->server = strdup(yytext); \ + } break; \ + case DB: { \ + free(yyextra->db); \ + yyextra->db = strdup(yytext); \ + } break; \ + case ENC_CHAR: { \ + free(yyextra->enc_char); \ + yyextra->enc_char = strdup(yytext); \ + } break; \ + case ENC_WCHAR: { \ + free(yyextra->enc_wchar); \ + yyextra->enc_wchar = strdup(yytext); \ + } break; \ + case ENC_DB: { \ + free(yyextra->enc_db); \ + yyextra->enc_db = strdup(yytext); \ + } break; \ + case ENC_LOCAL: { \ + free(yyextra->enc_local); \ + yyextra->enc_local = strdup(yytext); \ + } break; \ + case TSDB_FLOAT: { \ + if (process_map(yytext, &yyextra->tsdb_map, TSDB_FLOAT)) { \ + r = -1; \ + } \ + } break; \ + case TSDB_BIGINT: { \ + if (process_map(yytext, &yyextra->tsdb_map, TSDB_BIGINT)) { \ + r = -1; \ + } \ + } break; \ + case KEY: { \ + } break; \ + default: { \ + r = -1; \ + } break; \ + } \ + PUSH_STATE(curr); \ + if (r) return r; \ } while (0) -#define set_val() \ -do { \ - if (!yyextra->key) break; \ - if (strcasecmp(yyextra->key, "DSN")==0) { \ - free(yyextra->dsn); \ - yyextra->dsn = strdup(yytext); \ - break; \ - } \ - if (strcasecmp(yyextra->key, "UID")==0) { \ - free(yyextra->uid); \ - yyextra->uid = strdup(yytext); \ - break; \ - } \ - if (strcasecmp(yyextra->key, "PWD")==0) { \ - free(yyextra->pwd); \ - yyextra->pwd = strdup(yytext); \ - break; \ - } \ - if (strcasecmp(yyextra->key, "DB")==0) { \ - free(yyextra->db); \ - yyextra->pwd = strdup(yytext); \ - break; \ - } \ - if (strcasecmp(yyextra->key, "Server")==0) { \ - free(yyextra->server); \ - yyextra->server = strdup(yytext); \ - break; \ - } \ - if (strcasecmp(yyextra->key, "SERVER_ENC")==0) { \ - free(yyextra->svr_enc); \ - yyextra->svr_enc = strdup(yytext); \ - break; \ - } \ - if (strcasecmp(yyextra->key, "CLIENT_ENC")==0) { \ - free(yyextra->cli_enc); \ - yyextra->cli_enc = strdup(yytext); \ - break; \ - } \ +#define FAIL() \ +do { \ + /*fprintf(stderr, "==%s[%d]%s()==\n", basename(__FILE__), __LINE__, __func__);*/ \ + return -1; \ } while (0) %} @@ -89,57 +119,85 @@ do { \ %option warn %option perf-report %option 8bit +%option case-insensitive +%x DSN UID PWD SERVER DB +%x ENC_CHAR ENC_WCHAR ENC_DB ENC_LOCAL +%x TSDB_FLOAT TSDB_BIGINT %x KEY EQ BRACE1 BRACE2 VAL %% <> { int state; TOP_STATE(state); if (state == INITIAL) yyterminate(); if (state == VAL) yyterminate(); - return -1; } + FAIL(); } [[:space:]]+ { } -[[:alnum:]_]+ { set_key(); PUSH_STATE(KEY); } -.|\n { return -1; } +"DSN" { PUSH_STATE(DSN); } +"UID" { PUSH_STATE(UID); } +"PWD" { PUSH_STATE(PWD); } +"Server" { PUSH_STATE(SERVER); } +"DB" { PUSH_STATE(DB); } +"ENC_CHAR" { PUSH_STATE(ENC_CHAR); } +"ENC_WCHAR" { PUSH_STATE(ENC_WCHAR); } +"ENC_DB" { PUSH_STATE(ENC_DB); } +"ENC_LOCAL" { PUSH_STATE(ENC_LOCAL); } +"map.float" { PUSH_STATE(TSDB_FLOAT); } +"map.bigint" { PUSH_STATE(TSDB_BIGINT); } +[[:alnum:]_]+ { PUSH_STATE(KEY); } +.|\n { FAIL(); } -[[:space:]]+ { } -[=] { CHG_STATE(EQ); } -.|\n { return -1; } +[[:space:]]+ { } +[=] { PUSH_STATE(EQ); } +.|\n { FAIL(); } [[:space:]]+ { } -[^][{}(),;?*=!@/\\\n[:space:]]+ { set_val(); CHG_STATE(VAL); } [{] { CHG_STATE(BRACE1); } -.|\n { return -1; } +[^][{}(),;?*=!@/\\\n[:space:]]+ { set_val(); POP_STATE(); CHG_STATE(VAL); } +.|\n { FAIL(); } [^{}\n]+ { set_val(); CHG_STATE(BRACE2); } -.|\n { return -1; } +.|\n { FAIL(); } [[:space:]]+ { } -[}] { CHG_STATE(VAL); } -.|\n { return -1; } +[}] { POP_STATE(); CHG_STATE(VAL); } +.|\n { FAIL(); } [;] { POP_STATE(); } -.|\n { return -1; } +.|\n { FAIL(); } %% +static char* get_val_by_key_from_odbc_ini(const char *dsn, const char *key); + +static void conn_val_init(conn_val_t *val); + int todbc_parse_conn_string(const char *conn, conn_val_t *val) { yyscan_t arg = {0}; yylex_init(&arg); yyset_debug(0, arg); yyset_extra(val, arg); + + conn_val_init(val); + yy_scan_string(conn, arg); int ret =yylex(arg); yylex_destroy(arg); - if (val->key) free(val->key); val->key = NULL; - if (ret) { + if (ret || !val->dsn) { conn_val_reset(val); + } else { + if (!val->uid) { + val->uid = get_val_by_key_from_odbc_ini(val->dsn, "UID"); + } + if (!val->pwd) { + val->pwd = get_val_by_key_from_odbc_ini(val->dsn, "PWD"); + } + if (!val->server) { + val->server = get_val_by_key_from_odbc_ini(val->dsn, "Server"); + } } return ret ? -1 : 0; } void conn_val_reset(conn_val_t *val) { - if (val->key) { - free(val->key); val->key = NULL; - } if (val->dsn) { free(val->dsn); val->dsn = NULL; } @@ -155,11 +213,68 @@ void conn_val_reset(conn_val_t *val) { if (val->server) { free(val->server); val->server = NULL; } - if (val->svr_enc) { - free(val->svr_enc); val->svr_enc = NULL; + if (val->enc_local) { + free(val->enc_local); val->enc_local = NULL; + } + if (val->enc_db) { + free(val->enc_db); val->enc_db = NULL; + } + if (val->enc_char) { + free(val->enc_char); val->enc_char = NULL; } - if (val->cli_enc) { - free(val->cli_enc); val->cli_enc = NULL; + if (val->enc_wchar) { + free(val->enc_wchar); val->enc_wchar = NULL; + } +} + +static char* get_val_by_key_from_odbc_ini(const char *dsn, const char *key) { + char Val[4096]; + Val[0] = '\0'; + int n = SQLGetPrivateProfileString(dsn, key, "", Val, sizeof(Val), "odbc.ini"); + if (n<=0) return NULL; + if (Val[0]=='\0') return NULL; + return strdup(Val); +} + +static int process_map(const char *cfg, map_tsdb_type_t *tsdb_map, int type) { + switch (type) { + case TSDB_FLOAT: { + if (strcmp(cfg, "SQL_DOUBLE")==0) { + tsdb_map->tsdb_float = SQL_DOUBLE; + return 0; + } + } break; + case TSDB_BIGINT: { + if (strcmp(cfg, "SQL_C_SBIGINT")==0) { + tsdb_map->tsdb_bigint = SQL_C_SBIGINT; + return 0; + } + if (strcmp(cfg, "SQL_C_UBIGINT")==0) { + tsdb_map->tsdb_bigint = SQL_C_UBIGINT; + return 0; + } + if (strcmp(cfg, "SQL_CHAR")==0) { + tsdb_map->tsdb_bigint = SQL_CHAR; + return 0; + } + } break; + default: { + } break; } + return -1; +} + +static void conn_val_init(conn_val_t *val) { + if (!val) return; + val->tsdb_map.tsdb_tinyint = SQL_TINYINT; + val->tsdb_map.tsdb_smallint = SQL_SMALLINT; + val->tsdb_map.tsdb_int = SQL_INTEGER; + val->tsdb_map.tsdb_bigint = SQL_BIGINT; + val->tsdb_map.tsdb_float = SQL_REAL; + val->tsdb_map.tsdb_double = SQL_DOUBLE; + val->tsdb_map.tsdb_bool = SQL_TINYINT; + val->tsdb_map.tsdb_timestamp = SQL_CHAR; + val->tsdb_map.tsdb_binary = SQL_BINARY; + val->tsdb_map.tsdb_nchar = SQL_WCHAR; } diff --git a/src/connector/odbc/src/todbc_string.c b/src/connector/odbc/src/todbc_string.c new file mode 100644 index 0000000000000000000000000000000000000000..96e9a6d8c3658b08e9e57071aaf4d573e3ca81cb --- /dev/null +++ b/src/connector/odbc/src/todbc_string.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "todbc_string.h" + +#include "todbc_log.h" +#include "todbc_tls.h" + +#include + +static int do_calc_bytes(todbc_string_t *str); + +todbc_string_t todbc_string_init(const char *enc, const unsigned char *src, const size_t bytes) { + DASSERT(enc); + DASSERT(src); + + todbc_string_t s = {0}; + todbc_string_t *str = &s; + + todbc_iconv_t *cnv = todbc_tls_iconv_get(enc, UTF8_ENC); + if (!cnv) return s; + + if (snprintf(str->enc, sizeof(str->enc), "%s", enc)>=sizeof(str->enc)) { + return s; + } + str->buf = src; + str->total_bytes = bytes; // need to recalc + str->bytes = 0; + + if (do_calc_bytes(str)) { + str->buf = NULL; + str->total_bytes = 0; + str->bytes = 0; + } + + return s; +} + +todbc_string_t todbc_string_copy(todbc_string_t *str, const char *enc, unsigned char *dst, const size_t target_bytes) { + todbc_string_t val = {0}; + DASSERT(str); + DASSERT(dst); + DASSERT(str->buf); + DASSERT(str->bytes<=INT64_MAX); + DASSERT(str->total_bytes<=INT64_MAX); + + if (snprintf(val.enc, sizeof(val.enc), "%s", enc)>=sizeof(val.enc)) { + return val; + } + + todbc_iconv_t *icnv = todbc_tls_iconv_get(enc, str->enc); + if (!icnv) return val; + iconv_t cnv = todbc_iconv_get(icnv); + if (cnv==(iconv_t)-1) return val; + + val.buf = dst; + val.total_bytes = target_bytes; + + const int null_bytes = todbc_iconv_to(icnv).null_size; + + if (target_bytes<=null_bytes) return val; + size_t estsize = todbc_iconv_est_bytes(icnv, str->bytes); + if (estsize>INT64_MAX) return val; + + // smaller is better!!! + const size_t outblock = (estsize > target_bytes) ? estsize = target_bytes : estsize; + + char *inbuf = (char*)str->buf; + size_t inbytes = str->bytes; // not counting null-terminator + char *outbuf = (char*)dst; + size_t outbytes = outblock; + + int r = todbc_iconv_raw(icnv, (const unsigned char*)inbuf, &inbytes, (unsigned char*)outbuf, &outbytes); + if (r) { + DASSERT(outbytes > 0); + val.bytes = outblock - outbytes; + val.total_bytes = outblock; + return val; + } else { + val.bytes = outblock - outbytes; + val.total_bytes = val.bytes; + if (inbytes > 0) { + val.total_bytes += 1; // to indicate truncation + } + return val; + } +} + +todbc_string_t todbc_copy(const char *from_enc, const unsigned char *src, size_t *inbytes, const char *to_enc, unsigned char *dst, const size_t dlen) { + DASSERT(from_enc); + DASSERT(src); + DASSERT(inbytes); + DASSERT(to_enc); + DASSERT(dst); + DASSERT(dlen <= INT64_MAX); + + todbc_string_t s_from = todbc_string_init(from_enc, src, *inbytes); + DASSERT(s_from.buf == src); + + return todbc_string_copy(&s_from, to_enc, dst, dlen); +} + +todbc_string_t todbc_string_conv_to(todbc_string_t *str, const char *enc, todbc_buf_t *buf) { + DASSERT(str); + DASSERT(str->buf); + DASSERT(str->bytes<=INT64_MAX); + DASSERT(str->total_bytes<=INT64_MAX); + + todbc_string_t nul = {0}; + + todbc_iconv_t *icnv = todbc_tls_iconv_get(enc, str->enc); + if (!icnv) return nul; + + size_t estsize = todbc_iconv_est_bytes(icnv, str->bytes); + if (estsize>INT64_MAX) return nul; + char *out = NULL; + if (!buf) out = (char*)todbc_tls_buf_alloc(estsize); + else out = (char*)todbc_buf_alloc(buf, estsize); + if (!out) return nul; + + return todbc_string_copy(str, enc, (unsigned char*)out, estsize); +} + +static int do_calc_bytes(todbc_string_t *str) { + iconv_t cnv = todbc_tls_iconv(UTF8_ENC, str->enc); + if (cnv == (iconv_t)-1) return -1; + + size_t total_bytes = 0; + + char buf[1024*16]; + + char *inbuf = (char*)str->buf; + + while (1) { + size_t outblock = sizeof(buf); + + size_t inblock = outblock; + size_t remain = (size_t)-1; + if (str->total_bytes <= INT64_MAX) { + remain = str->total_bytes - total_bytes; + if (remain==0) break; + if (inblock > remain) inblock = remain; + } + + size_t inbytes = inblock; + char *outbuf = buf; + size_t outbytes = outblock; + + size_t n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes); + total_bytes += inblock - inbytes; + + int e = 0; + if (n==(size_t)-1) { + e = errno; + if (str->total_bytes<=INT64_MAX) { + D("iconv failed @[%zu], inbytes[%zd->%zd], outbytes[%zd->%zd]: [%d]%s", + (inblock-inbytes), inblock, inbytes, outblock, outbytes, e, strerror(e)); + } + DASSERT(e==EILSEQ || e==E2BIG || e==EINVAL); + } + if (n>0 && n<=INT64_MAX) { + D("iconv found non-reversible seq"); + } + + size_t outlen = outblock - outbytes; + size_t utf8len = strnlen(buf, outlen); + if (utf8len < outlen) { + // null-terminator found + // revert + inbuf -= inblock - inbytes; + total_bytes -= inblock - inbytes; + + if (utf8len==0) break; + + inbytes = inblock; + outbuf = buf; + outbytes = utf8len; + + n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes); + total_bytes += inblock - inbytes; + DASSERT(n==(size_t)-1); + e = errno; + DASSERT(e==E2BIG); + + break; + } + + if (e==EILSEQ) break; + if (e==EINVAL) { + if (inbytes == remain) { + // this is the last stuff + break; + } + } + } + + if (str->total_bytes > INT64_MAX) { + str->total_bytes = total_bytes; + } + str->bytes = total_bytes; + + iconv(cnv, NULL, NULL, NULL, NULL); + + return 0; +} + diff --git a/src/connector/odbc/src/todbc_string.h b/src/connector/odbc/src/todbc_string.h new file mode 100644 index 0000000000000000000000000000000000000000..eed3356847a16ab383da63eaa8c503312273136a --- /dev/null +++ b/src/connector/odbc/src/todbc_string.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _todbc_string_h_ +#define _todbc_string_h_ + +#include +#include + +#include "todbc_buf.h" + +// non-thread-safe + +typedef struct todbc_string_s todbc_string_t; +struct todbc_string_s { + // null if init failed because of internal resources shortage + const unsigned char *buf; // null-terminator inclusive + size_t total_bytes; // not counting null-terminator + + // <= total_bytes + // truncated if < total_bytes + size_t bytes; // not counting null-terminator + + // move here to satisfy todbc_string_t dummy = {0}; + char enc[64]; +}; + + +// does not copy internally +// bytes: not characters, <0 means bytes unknown +todbc_string_t todbc_string_init(const char *enc, const unsigned char *src, const size_t bytes); +// conv and copy to dst not more than target_bytes (null-terminator-inclusive) +// return'd val->buf == dst, total_bytes + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "todbc_tls.h" + +#include "todbc_buf.h" +#include "todbc_iconv.h" +#include "todbc_log.h" + + +typedef struct todbc_tls_s todbc_tls_t; + +struct todbc_tls_s { + todbc_buf_t *buf; + todbc_iconvset_t *cnvset; +}; + + +static void todbc_tls_free(todbc_tls_t *value); + +static pthread_key_t key_this; +static pthread_once_t key_once = PTHREAD_ONCE_INIT; +static int key_err = 0; + + +static void key_init(void); +static void key_destructor(void *arg); + + +static void key_init(void) { + key_err = pthread_key_create(&key_this, key_destructor); + if (key_err) { + D("thread local initialization failed: [%d]%s", key_err, strerror(key_err)); + } +} + +static todbc_tls_t* todbc_tls_create(void); + +static todbc_tls_t* key_value(void) { + pthread_once(&key_once, key_init); + if (key_err) return NULL; + + int err = 0; + + todbc_tls_t *value = pthread_getspecific(key_this); + if (value) return value; + + value = todbc_tls_create(); + if (!value) return NULL; + + do { + err = pthread_setspecific(key_this, value); + if (err) { + D("thread local setup failed: [%d]%s", err, strerror(err)); + break; + } + + return value; + } while (0); + + todbc_tls_free(value); + + return NULL; +} + +static void key_destructor(void *arg) { + todbc_tls_t *value = (todbc_tls_t*)arg; + todbc_tls_free(value); +} + +static todbc_tls_t* todbc_tls_create(void) { + int err = 0; + todbc_tls_t *value = (todbc_tls_t*)calloc(1, sizeof(*value)); + if (!value) { + err = errno; + D("thread local creation failed: [%d]%s", err, strerror(err)); + return NULL; + } + do { + return value; + } while (0); + + todbc_tls_free(value); + return NULL; +} + +static void todbc_tls_free(todbc_tls_t *value) { + if (value->cnvset) { + todbc_iconvset_free(value->cnvset); + value->cnvset = NULL; + } + + if (value->buf) { + todbc_buf_free(value->buf); + value->buf = NULL; + } + + free(value); +} + +static todbc_iconvset_t* do_get_iconvset(void); + +// iconv +int todbc_legal_chars(const char *enc, const unsigned char *str, todbc_bytes_t *bc) { + todbc_iconvset_t *icnv = do_get_iconvset(); + if (!icnv) return -1; + todbc_iconv_t *cnv = todbc_iconvset_get(icnv, UTF16_ENC, enc); + if (!cnv) return -1; + return todbc_iconv_get_legal_chars(cnv, str, bc); +} + +todbc_iconv_t* todbc_tls_iconv_get(const char *to_enc, const char *from_enc) { + todbc_iconvset_t *cnvset = do_get_iconvset(); + if (!cnvset) return NULL; + todbc_iconv_t *cnv = todbc_iconvset_get(cnvset, to_enc, from_enc); + return cnv; +} + +iconv_t todbc_tls_iconv(const char *to_enc, const char *from_enc) { + todbc_iconv_t *icnv = todbc_tls_iconv_get(to_enc, from_enc); + if (!icnv) return (iconv_t)-1; + return todbc_iconv_get(icnv); +} + +todbc_enc_t todbc_tls_iconv_enc(const char *enc) { + do { + todbc_iconvset_t *cnvset = do_get_iconvset(); + if (!cnvset) break; + return todbc_iconvset_enc(cnvset, enc); + } while (0); + + todbc_enc_t v = {0}; + v.char_size = -1; + v.null_size = -1; + + return v; +} + +todbc_string_t todbc_tls_conv(todbc_buf_t *buf, const char *enc_to, const char *enc_from, const unsigned char *src, size_t *slen) { + todbc_iconv_t *cnv = todbc_tls_iconv_get(enc_to, enc_from); + if (!cnv) { + todbc_string_t nul = {0}; + return nul; + } + return todbc_iconv_conv2(cnv, buf, src, slen); +} + +todbc_string_t todbc_tls_write(const char *enc_to, const char *enc_from, + const unsigned char *src, size_t *slen, unsigned char *dst, size_t dlen) +{ + todbc_iconv_t *cnv = todbc_tls_iconv_get(enc_to, enc_from); + if (!cnv) { + todbc_string_t nul = {0}; + return nul; + } + todbc_string_t s = {0}; + s.buf = dst; + s.total_bytes = dlen; + size_t inbytes = *slen; + size_t outbytes = dlen; + todbc_iconv_raw(cnv, src, &inbytes, dst, &outbytes); + s.bytes = dlen - outbytes; + s.total_bytes = s.bytes; + if (inbytes) { + s.total_bytes += 1; + } + *slen = inbytes; + + return s; +} + +char* todbc_tls_strndup(const char *src, size_t n) { + todbc_buf_t *buf = todbc_tls_buf(); + if (!buf) return NULL; + n = strnlen(src, n); + char *d = todbc_buf_alloc(buf, (n+1)); + if (!d) return NULL; + snprintf(d, n+1, "%s", src); + return d; +} + +static todbc_iconvset_t* do_get_iconvset(void) { + todbc_tls_t *tls = key_value(); + if (!tls) return NULL; + if (!tls->cnvset) { + tls->cnvset = todbc_iconvset_create(); + } + return tls->cnvset; +} + +// tls_buf +void* todbc_tls_buf_alloc(size_t size) { + todbc_tls_t *tls = key_value(); + if (!tls) return NULL; + if (!tls->buf) { + tls->buf = todbc_buf_create(); + if (!tls->buf) return NULL; + } + return todbc_buf_alloc(tls->buf, size); +} + +void* todbc_tls_buf_calloc(size_t count, size_t size) { + todbc_tls_t *tls = key_value(); + if (!tls) return NULL; + if (!tls->buf) { + tls->buf = todbc_buf_create(); + if (!tls->buf) return NULL; + } + return todbc_buf_calloc(tls->buf, count, size); +} + +void* todbc_tls_buf_realloc(void *ptr, size_t size) { + todbc_tls_t *tls = key_value(); + if (!tls) return NULL; + if (!tls->buf) { + tls->buf = todbc_buf_create(); + if (!tls->buf) return NULL; + } + return todbc_buf_realloc(tls->buf, ptr, size); +} + +void todbc_tls_buf_reclaim(void) { + todbc_tls_t *tls = key_value(); + if (!tls) return; + if (!tls->buf) return; + + todbc_buf_reclaim(tls->buf); +} + +todbc_buf_t* todbc_tls_buf(void) { + todbc_tls_t *tls = key_value(); + if (!tls) return NULL; + if (!tls->buf) { + tls->buf = todbc_buf_create(); + } + return tls->buf; +} + diff --git a/src/connector/odbc/src/todbc_tls.h b/src/connector/odbc/src/todbc_tls.h new file mode 100644 index 0000000000000000000000000000000000000000..e636f6ae6c9c9051fe4a77d5e9d1e082e2dcfdb0 --- /dev/null +++ b/src/connector/odbc/src/todbc_tls.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _todbc_tls_h_ +#define _todbc_tls_h_ + +// !!! functions exported in this header file are all non-thread-safe !!! + +#include "taos.h" + +#include "todbc_buf.h" +#include "todbc_iconv.h" +#include "todbc_string.h" + +// thread local buffers +// non-thread-safe +// returned-buf are all thread-local-accessible until todbc_tls_buf_reclaim +void* todbc_tls_buf_alloc(size_t size); +void* todbc_tls_buf_calloc(size_t count, size_t size); +void* todbc_tls_buf_realloc(void *ptr, size_t size); +// reclaim all above thread-local-buf(s) +void todbc_tls_buf_reclaim(void); + +// return local-thread-buf +todbc_buf_t* todbc_tls_buf(void); + +// thread local iconv +// non-thread-safe +todbc_iconv_t* todbc_tls_iconv_get(const char *to_enc, const char *from_enc); +iconv_t todbc_tls_iconv(const char *to_enc, const char *from_enc); + +todbc_enc_t todbc_tls_iconv_enc(const char *enc); + +// non-thread-safe +int todbc_legal_chars(const char *enc, const unsigned char *str, todbc_bytes_t *bc); + +// at return, *slen stores the remaining # +todbc_string_t todbc_tls_conv(todbc_buf_t *buf, const char *enc_to, const char *enc_from, const unsigned char *src, size_t *slen); +todbc_string_t todbc_tls_write(const char *enc_to, const char *enc_from, const unsigned char *src, size_t *slen, unsigned char *dst, size_t dlen); + +char* todbc_tls_strndup(const char *src, size_t n); + +#endif // _todbc_tls_h_ + diff --git a/src/connector/odbc/src/todbc_util.c b/src/connector/odbc/src/todbc_util.c index 9c130b4f2f2e6d0f5d4a19e11ab323d42b800195..d0b5abe09321c0b65b711b4e45e23e038c3dff88 100644 --- a/src/connector/odbc/src/todbc_util.c +++ b/src/connector/odbc/src/todbc_util.c @@ -15,86 +15,576 @@ #include "todbc_util.h" #include "todbc_log.h" + #include #include +#define SQL_CASE(type) case type: return #type + const char* sql_sql_type(int type) { switch (type) { - case SQL_BIT: return "SQL_BIT"; - case SQL_TINYINT: return "SQL_TINYINT"; - case SQL_SMALLINT: return "SQL_SMALLINT"; - case SQL_INTEGER: return "SQL_INTEGER"; - case SQL_BIGINT: return "SQL_BIGINT"; - case SQL_FLOAT: return "SQL_FLOAT"; - case SQL_DOUBLE: return "SQL_DOUBLE"; - case SQL_DECIMAL: return "SQL_DECIMAL"; - case SQL_NUMERIC: return "SQL_NUMERIC"; - case SQL_REAL: return "SQL_REAL"; - case SQL_CHAR: return "SQL_CHAR"; - case SQL_VARCHAR: return "SQL_VARCHAR"; - case SQL_LONGVARCHAR: return "SQL_LONGVARCHAR"; - case SQL_WCHAR: return "SQL_WCHAR"; - case SQL_WVARCHAR: return "SQL_WVARCHAR"; - case SQL_WLONGVARCHAR: return "SQL_WLONGVARCHAR"; - case SQL_BINARY: return "SQL_BINARY"; - case SQL_VARBINARY: return "SQL_VARBINARY"; - case SQL_LONGVARBINARY: return "SQL_LONGVARBINARY"; - case SQL_DATE: return "SQL_DATE"; - case SQL_TIME: return "SQL_TIME"; - case SQL_TIMESTAMP: return "SQL_TIMESTAMP"; - case SQL_TYPE_DATE: return "SQL_TYPE_DATE"; - case SQL_TYPE_TIME: return "SQL_TYPE_TIME"; - case SQL_TYPE_TIMESTAMP: return "SQL_TYPE_TIMESTAMP"; - case SQL_INTERVAL_MONTH: return "SQL_INTERVAL_MONTH"; - case SQL_INTERVAL_YEAR: return "SQL_INTERVAL_YEAR"; - case SQL_INTERVAL_YEAR_TO_MONTH: return "SQL_INTERVAL_YEAR_TO_MONTH"; - case SQL_INTERVAL_DAY: return "SQL_INTERVAL_DAY"; - case SQL_INTERVAL_HOUR: return "SQL_INTERVAL_HOUR"; - case SQL_INTERVAL_MINUTE: return "SQL_INTERVAL_MINUTE"; - case SQL_INTERVAL_SECOND: return "SQL_INTERVAL_SECOND"; - case SQL_INTERVAL_DAY_TO_HOUR: return "SQL_INTERVAL_DAY_TO_HOUR"; - case SQL_INTERVAL_DAY_TO_MINUTE: return "SQL_INTERVAL_DAY_TO_MINUTE"; - case SQL_INTERVAL_DAY_TO_SECOND: return "SQL_INTERVAL_DAY_TO_SECOND"; - case SQL_INTERVAL_HOUR_TO_MINUTE: return "SQL_INTERVAL_HOUR_TO_MINUTE"; - case SQL_INTERVAL_HOUR_TO_SECOND: return "SQL_INTERVAL_HOUR_TO_SECOND"; - case SQL_INTERVAL_MINUTE_TO_SECOND: return "SQL_INTERVAL_MINUTE_TO_SECOND"; - case SQL_GUID: return "SQL_GUID"; + SQL_CASE(SQL_BIT); + SQL_CASE(SQL_TINYINT); + SQL_CASE(SQL_SMALLINT); + SQL_CASE(SQL_INTEGER); + SQL_CASE(SQL_BIGINT); + SQL_CASE(SQL_FLOAT); + SQL_CASE(SQL_DOUBLE); + SQL_CASE(SQL_DECIMAL); + SQL_CASE(SQL_NUMERIC); + SQL_CASE(SQL_REAL); + SQL_CASE(SQL_CHAR); + SQL_CASE(SQL_VARCHAR); + SQL_CASE(SQL_LONGVARCHAR); + SQL_CASE(SQL_WCHAR); + SQL_CASE(SQL_WVARCHAR); + SQL_CASE(SQL_WLONGVARCHAR); + SQL_CASE(SQL_BINARY); + SQL_CASE(SQL_VARBINARY); + SQL_CASE(SQL_LONGVARBINARY); + SQL_CASE(SQL_DATE); + SQL_CASE(SQL_TIME); + SQL_CASE(SQL_TIMESTAMP); + SQL_CASE(SQL_TYPE_DATE); + SQL_CASE(SQL_TYPE_TIME); + SQL_CASE(SQL_TYPE_TIMESTAMP); + SQL_CASE(SQL_INTERVAL_MONTH); + SQL_CASE(SQL_INTERVAL_YEAR); + SQL_CASE(SQL_INTERVAL_YEAR_TO_MONTH); + SQL_CASE(SQL_INTERVAL_DAY); + SQL_CASE(SQL_INTERVAL_HOUR); + SQL_CASE(SQL_INTERVAL_MINUTE); + SQL_CASE(SQL_INTERVAL_SECOND); + SQL_CASE(SQL_INTERVAL_DAY_TO_HOUR); + SQL_CASE(SQL_INTERVAL_DAY_TO_MINUTE); + SQL_CASE(SQL_INTERVAL_DAY_TO_SECOND); + SQL_CASE(SQL_INTERVAL_HOUR_TO_MINUTE); + SQL_CASE(SQL_INTERVAL_HOUR_TO_SECOND); + SQL_CASE(SQL_INTERVAL_MINUTE_TO_SECOND); + SQL_CASE(SQL_GUID); + SQL_CASE(SQL_ALL_TYPES); default: return "UNKNOWN"; } } const char* sql_c_type(int type) { switch (type) { - case SQL_C_CHAR: return "SQL_C_CHAR"; - case SQL_C_WCHAR: return "SQL_C_WCHAR"; - case SQL_C_SHORT: return "SQL_C_SHORT"; - case SQL_C_SSHORT: return "SQL_C_SSHORT"; - case SQL_C_USHORT: return "SQL_C_USHORT"; - case SQL_C_LONG: return "SQL_C_LONG"; - case SQL_C_SLONG: return "SQL_C_SLONG"; - case SQL_C_ULONG: return "SQL_C_ULONG"; - case SQL_C_FLOAT: return "SQL_C_FLOAT"; - case SQL_C_DOUBLE: return "SQL_C_DOUBLE"; - case SQL_C_BIT: return "SQL_C_BIT"; - case SQL_C_TINYINT: return "SQL_C_TINYINT"; - case SQL_C_STINYINT: return "SQL_C_STINYINT"; - case SQL_C_UTINYINT: return "SQL_C_UTINYINT"; - case SQL_C_SBIGINT: return "SQL_C_SBIGINT"; - case SQL_C_UBIGINT: return "SQL_C_UBIGINT"; - case SQL_C_BINARY: return "SQL_C_BINARY"; - case SQL_C_DATE: return "SQL_C_DATE"; - case SQL_C_TIME: return "SQL_C_TIME"; - case SQL_C_TIMESTAMP: return "SQL_C_TIMESTAMP"; - case SQL_C_TYPE_DATE: return "SQL_C_TYPE_DATE"; - case SQL_C_TYPE_TIME: return "SQL_C_TYPE_TIME"; - case SQL_C_TYPE_TIMESTAMP: return "SQL_C_TYPE_TIMESTAMP"; - case SQL_C_NUMERIC: return "SQL_C_NUMERIC"; - case SQL_C_GUID: return "SQL_C_GUID"; + SQL_CASE(SQL_C_CHAR); + SQL_CASE(SQL_C_WCHAR); + SQL_CASE(SQL_C_SHORT); + SQL_CASE(SQL_C_SSHORT); + SQL_CASE(SQL_C_USHORT); + SQL_CASE(SQL_C_LONG); + SQL_CASE(SQL_C_SLONG); + SQL_CASE(SQL_C_ULONG); + SQL_CASE(SQL_C_FLOAT); + SQL_CASE(SQL_C_DOUBLE); + SQL_CASE(SQL_C_BIT); + SQL_CASE(SQL_C_TINYINT); + SQL_CASE(SQL_C_STINYINT); + SQL_CASE(SQL_C_UTINYINT); + SQL_CASE(SQL_C_SBIGINT); + SQL_CASE(SQL_C_UBIGINT); + SQL_CASE(SQL_C_BINARY); + SQL_CASE(SQL_C_DATE); + SQL_CASE(SQL_C_TIME); + SQL_CASE(SQL_C_TIMESTAMP); + SQL_CASE(SQL_C_TYPE_DATE); + SQL_CASE(SQL_C_TYPE_TIME); + SQL_CASE(SQL_C_TYPE_TIMESTAMP); + SQL_CASE(SQL_C_NUMERIC); + SQL_CASE(SQL_C_GUID); + default: return "UNKNOWN"; + } +} + +// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetdiagfield-function?view=sql-server-ver15 +const char* sql_diag_identifier(int type) { + switch (type) { + // header fields + SQL_CASE(SQL_DIAG_CURSOR_ROW_COUNT); + SQL_CASE(SQL_DIAG_DYNAMIC_FUNCTION); + SQL_CASE(SQL_DIAG_DYNAMIC_FUNCTION_CODE); + SQL_CASE(SQL_DIAG_NUMBER); + SQL_CASE(SQL_DIAG_RETURNCODE); + SQL_CASE(SQL_DIAG_ROW_COUNT); + // record fields + SQL_CASE(SQL_DIAG_CLASS_ORIGIN); + SQL_CASE(SQL_DIAG_COLUMN_NUMBER); + SQL_CASE(SQL_DIAG_CONNECTION_NAME); + SQL_CASE(SQL_DIAG_MESSAGE_TEXT); + SQL_CASE(SQL_DIAG_NATIVE); + SQL_CASE(SQL_DIAG_ROW_NUMBER); + SQL_CASE(SQL_DIAG_SERVER_NAME); + SQL_CASE(SQL_DIAG_SQLSTATE); + SQL_CASE(SQL_DIAG_SUBCLASS_ORIGIN); + default: return "UNKNOWN"; + } +} + +const char* sql_handle_type(int type) { + switch(type) { + SQL_CASE(SQL_HANDLE_ENV); + SQL_CASE(SQL_HANDLE_DBC); + SQL_CASE(SQL_HANDLE_STMT); + SQL_CASE(SQL_HANDLE_DESC); + // SQL_CASE(SQL_HANDLE_DBC_INFO_TOKEN); + default: return "UNKNOWN"; + } +} + +const char* sql_env_attr_type(int type) { + switch(type) { + SQL_CASE(SQL_ATTR_OUTPUT_NTS); + SQL_CASE(SQL_ATTR_ODBC_VERSION); + SQL_CASE(SQL_ATTR_CONNECTION_POOLING); + SQL_CASE(SQL_ATTR_CP_MATCH); + default: return "UNKNOWN"; + } +} + +// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetconnectattr-function?view=sql-server-ver15 +const char* sql_conn_attr_type(int type) { + switch(type) { + SQL_CASE(SQL_ATTR_ACCESS_MODE); + // ODBC 3.8 + // SQL_CASE(SQL_ATTR_ASYNC_DBC_EVENT); + SQL_CASE(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE); + // ODBC 3.8 + // SQL_CASE(SQL_ATTR_ASYNC_DBC_PCALLBACK); + // ODBC 3.8 + // SQL_CASE(SQL_ATTR_ASYNC_DBC_PCONTEXT); + SQL_CASE(SQL_ATTR_ASYNC_ENABLE); + SQL_CASE(SQL_ATTR_AUTO_IPD); + SQL_CASE(SQL_ATTR_AUTOCOMMIT); + SQL_CASE(SQL_ATTR_CONNECTION_DEAD); + SQL_CASE(SQL_ATTR_CONNECTION_TIMEOUT); + SQL_CASE(SQL_ATTR_CURRENT_CATALOG); + // ODBC 3.8 + // SQL_CASE(SQL_ATTR_DBC_INFO_TOKEN); + SQL_CASE(SQL_ATTR_ENLIST_IN_DTC); + SQL_CASE(SQL_ATTR_LOGIN_TIMEOUT); + SQL_CASE(SQL_ATTR_METADATA_ID); + SQL_CASE(SQL_ATTR_ODBC_CURSORS); + SQL_CASE(SQL_ATTR_PACKET_SIZE); + SQL_CASE(SQL_ATTR_QUIET_MODE); + SQL_CASE(SQL_ATTR_TRACE); + SQL_CASE(SQL_ATTR_TRACEFILE); + SQL_CASE(SQL_ATTR_TRANSLATE_LIB); + SQL_CASE(SQL_ATTR_TRANSLATE_OPTION); + SQL_CASE(SQL_ATTR_TXN_ISOLATION); + default: return "UNKNOWN"; + } +} + +// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetinfo-function?view=sql-server-ver15 +const char* sql_info_type(int type) { + switch(type) { + SQL_CASE(SQL_ACTIVE_ENVIRONMENTS); + SQL_CASE(SQL_ASYNC_DBC_FUNCTIONS); + SQL_CASE(SQL_ASYNC_MODE); + SQL_CASE(SQL_ASYNC_NOTIFICATION); + SQL_CASE(SQL_BATCH_ROW_COUNT); + SQL_CASE(SQL_BATCH_SUPPORT); + SQL_CASE(SQL_DATA_SOURCE_NAME); + SQL_CASE(SQL_DRIVER_AWARE_POOLING_SUPPORTED); + SQL_CASE(SQL_DRIVER_HDBC); + SQL_CASE(SQL_DRIVER_HDESC); + SQL_CASE(SQL_DRIVER_HENV); + SQL_CASE(SQL_DRIVER_HLIB); + SQL_CASE(SQL_DRIVER_HSTMT); + SQL_CASE(SQL_DRIVER_NAME); + SQL_CASE(SQL_DRIVER_ODBC_VER); + SQL_CASE(SQL_DRIVER_VER); + SQL_CASE(SQL_DYNAMIC_CURSOR_ATTRIBUTES1); + SQL_CASE(SQL_DYNAMIC_CURSOR_ATTRIBUTES2); + SQL_CASE(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1); + SQL_CASE(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2); + SQL_CASE(SQL_FILE_USAGE); + SQL_CASE(SQL_GETDATA_EXTENSIONS); + SQL_CASE(SQL_INFO_SCHEMA_VIEWS); + SQL_CASE(SQL_KEYSET_CURSOR_ATTRIBUTES1); + SQL_CASE(SQL_KEYSET_CURSOR_ATTRIBUTES2); + SQL_CASE(SQL_MAX_ASYNC_CONCURRENT_STATEMENTS); + SQL_CASE(SQL_MAX_CONCURRENT_ACTIVITIES); + SQL_CASE(SQL_MAX_DRIVER_CONNECTIONS); + SQL_CASE(SQL_ODBC_INTERFACE_CONFORMANCE); + // SQL_CASE(SQL_ODBC_STANDARD_CLI_CONFORMANCE); + SQL_CASE(SQL_ODBC_VER); + SQL_CASE(SQL_PARAM_ARRAY_ROW_COUNTS); + SQL_CASE(SQL_PARAM_ARRAY_SELECTS); + SQL_CASE(SQL_ROW_UPDATES); + SQL_CASE(SQL_SEARCH_PATTERN_ESCAPE); + SQL_CASE(SQL_SERVER_NAME); + SQL_CASE(SQL_STATIC_CURSOR_ATTRIBUTES1); + SQL_CASE(SQL_STATIC_CURSOR_ATTRIBUTES2); + + SQL_CASE(SQL_DATABASE_NAME); + SQL_CASE(SQL_DBMS_NAME); + SQL_CASE(SQL_DBMS_VER); + + SQL_CASE(SQL_ACCESSIBLE_PROCEDURES); + SQL_CASE(SQL_ACCESSIBLE_TABLES); + SQL_CASE(SQL_BOOKMARK_PERSISTENCE); + SQL_CASE(SQL_CATALOG_TERM); + SQL_CASE(SQL_COLLATION_SEQ); + SQL_CASE(SQL_CONCAT_NULL_BEHAVIOR); + SQL_CASE(SQL_CURSOR_COMMIT_BEHAVIOR); + SQL_CASE(SQL_CURSOR_ROLLBACK_BEHAVIOR); + SQL_CASE(SQL_CURSOR_SENSITIVITY); + SQL_CASE(SQL_DATA_SOURCE_READ_ONLY); + SQL_CASE(SQL_DEFAULT_TXN_ISOLATION); + SQL_CASE(SQL_DESCRIBE_PARAMETER); + SQL_CASE(SQL_MULT_RESULT_SETS); + SQL_CASE(SQL_MULTIPLE_ACTIVE_TXN); + SQL_CASE(SQL_NEED_LONG_DATA_LEN); + SQL_CASE(SQL_NULL_COLLATION); + SQL_CASE(SQL_PROCEDURE_TERM); + SQL_CASE(SQL_SCHEMA_TERM); + SQL_CASE(SQL_SCROLL_OPTIONS); + SQL_CASE(SQL_TABLE_TERM); + SQL_CASE(SQL_TXN_CAPABLE); + SQL_CASE(SQL_TXN_ISOLATION_OPTION); + SQL_CASE(SQL_USER_NAME); + + SQL_CASE(SQL_AGGREGATE_FUNCTIONS); + SQL_CASE(SQL_ALTER_DOMAIN); + // SQL_CASE(SQL_ALTER_SCHEMA); + SQL_CASE(SQL_ALTER_TABLE); + // SQL_CASE(SQL_ANSI_SQL_DATETIME_LITERALS); + SQL_CASE(SQL_CATALOG_LOCATION); + SQL_CASE(SQL_CATALOG_NAME); + SQL_CASE(SQL_CATALOG_NAME_SEPARATOR); + SQL_CASE(SQL_CATALOG_USAGE); + SQL_CASE(SQL_COLUMN_ALIAS); + SQL_CASE(SQL_CORRELATION_NAME); + SQL_CASE(SQL_CREATE_ASSERTION); + SQL_CASE(SQL_CREATE_CHARACTER_SET); + SQL_CASE(SQL_CREATE_COLLATION); + SQL_CASE(SQL_CREATE_DOMAIN); + SQL_CASE(SQL_CREATE_SCHEMA); + SQL_CASE(SQL_CREATE_TABLE); + SQL_CASE(SQL_CREATE_TRANSLATION); + SQL_CASE(SQL_DDL_INDEX); + SQL_CASE(SQL_DROP_ASSERTION); + SQL_CASE(SQL_DROP_CHARACTER_SET); + SQL_CASE(SQL_DROP_COLLATION); + SQL_CASE(SQL_DROP_DOMAIN); + SQL_CASE(SQL_DROP_SCHEMA); + + SQL_CASE(SQL_DROP_TABLE); + SQL_CASE(SQL_DROP_TRANSLATION); + SQL_CASE(SQL_DROP_VIEW); + SQL_CASE(SQL_EXPRESSIONS_IN_ORDERBY); + SQL_CASE(SQL_GROUP_BY); + SQL_CASE(SQL_IDENTIFIER_CASE); + SQL_CASE(SQL_IDENTIFIER_QUOTE_CHAR); + SQL_CASE(SQL_INDEX_KEYWORDS); + SQL_CASE(SQL_INSERT_STATEMENT); + SQL_CASE(SQL_INTEGRITY); + SQL_CASE(SQL_KEYWORDS); + SQL_CASE(SQL_LIKE_ESCAPE_CLAUSE); + SQL_CASE(SQL_NON_NULLABLE_COLUMNS); + SQL_CASE(SQL_OJ_CAPABILITIES); + SQL_CASE(SQL_ORDER_BY_COLUMNS_IN_SELECT); + SQL_CASE(SQL_OUTER_JOINS); + SQL_CASE(SQL_PROCEDURES); + SQL_CASE(SQL_QUOTED_IDENTIFIER_CASE); + SQL_CASE(SQL_SCHEMA_USAGE); + SQL_CASE(SQL_SPECIAL_CHARACTERS); + SQL_CASE(SQL_SQL_CONFORMANCE); + SQL_CASE(SQL_SUBQUERIES); + SQL_CASE(SQL_UNION); + + SQL_CASE(SQL_MAX_BINARY_LITERAL_LEN); + SQL_CASE(SQL_MAX_CATALOG_NAME_LEN); + SQL_CASE(SQL_MAX_CHAR_LITERAL_LEN); + SQL_CASE(SQL_MAX_COLUMN_NAME_LEN); + SQL_CASE(SQL_MAX_COLUMNS_IN_GROUP_BY); + SQL_CASE(SQL_MAX_COLUMNS_IN_INDEX); + SQL_CASE(SQL_MAX_COLUMNS_IN_ORDER_BY); + SQL_CASE(SQL_MAX_COLUMNS_IN_SELECT); + SQL_CASE(SQL_MAX_COLUMNS_IN_TABLE); + SQL_CASE(SQL_MAX_CURSOR_NAME_LEN); + + SQL_CASE(SQL_MAX_IDENTIFIER_LEN); + SQL_CASE(SQL_MAX_INDEX_SIZE); + SQL_CASE(SQL_MAX_PROCEDURE_NAME_LEN); + SQL_CASE(SQL_MAX_ROW_SIZE); + SQL_CASE(SQL_MAX_ROW_SIZE_INCLUDES_LONG); + SQL_CASE(SQL_MAX_SCHEMA_NAME_LEN); + SQL_CASE(SQL_MAX_STATEMENT_LEN); + SQL_CASE(SQL_MAX_TABLE_NAME_LEN); + SQL_CASE(SQL_MAX_TABLES_IN_SELECT); + SQL_CASE(SQL_MAX_USER_NAME_LEN); + + SQL_CASE(SQL_CONVERT_FUNCTIONS); + SQL_CASE(SQL_NUMERIC_FUNCTIONS); + SQL_CASE(SQL_STRING_FUNCTIONS); + SQL_CASE(SQL_SYSTEM_FUNCTIONS); + + SQL_CASE(SQL_TIMEDATE_ADD_INTERVALS); + SQL_CASE(SQL_TIMEDATE_DIFF_INTERVALS); + SQL_CASE(SQL_TIMEDATE_FUNCTIONS); + + SQL_CASE(SQL_CONVERT_BIGINT); + SQL_CASE(SQL_CONVERT_BINARY); + SQL_CASE(SQL_CONVERT_BIT); + SQL_CASE(SQL_CONVERT_CHAR); + SQL_CASE(SQL_CONVERT_DATE); + SQL_CASE(SQL_CONVERT_DECIMAL); + SQL_CASE(SQL_CONVERT_DOUBLE); + SQL_CASE(SQL_CONVERT_FLOAT); + SQL_CASE(SQL_CONVERT_INTEGER); + SQL_CASE(SQL_CONVERT_INTERVAL_DAY_TIME); + SQL_CASE(SQL_CONVERT_INTERVAL_YEAR_MONTH); + + SQL_CASE(SQL_CONVERT_LONGVARBINARY); + SQL_CASE(SQL_CONVERT_LONGVARCHAR); + SQL_CASE(SQL_CONVERT_NUMERIC); + SQL_CASE(SQL_CONVERT_REAL); + SQL_CASE(SQL_CONVERT_SMALLINT); + SQL_CASE(SQL_CONVERT_TIME); + SQL_CASE(SQL_CONVERT_TIMESTAMP); + SQL_CASE(SQL_CONVERT_TINYINT); + SQL_CASE(SQL_CONVERT_VARBINARY); + SQL_CASE(SQL_CONVERT_VARCHAR); + + SQL_CASE(SQL_DM_VER); + + SQL_CASE(SQL_XOPEN_CLI_YEAR); + + SQL_CASE(SQL_DTC_TRANSITION_COST); + + default: return "UNKNOWN"; + } +} + +// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlcolattribute-function?view=sql-server-ver15 +const char* sql_field_identifier(int type) { + switch (type) { + SQL_CASE(SQL_DESC_AUTO_UNIQUE_VALUE); + SQL_CASE(SQL_DESC_BASE_COLUMN_NAME); + SQL_CASE(SQL_DESC_BASE_TABLE_NAME); + SQL_CASE(SQL_DESC_CASE_SENSITIVE); + SQL_CASE(SQL_DESC_CATALOG_NAME); + SQL_CASE(SQL_DESC_CONCISE_TYPE); + SQL_CASE(SQL_DESC_COUNT); + SQL_CASE(SQL_DESC_DISPLAY_SIZE); + SQL_CASE(SQL_DESC_FIXED_PREC_SCALE); + SQL_CASE(SQL_DESC_LABEL); + SQL_CASE(SQL_DESC_LENGTH); + SQL_CASE(SQL_DESC_LITERAL_PREFIX); + SQL_CASE(SQL_DESC_LITERAL_SUFFIX); + SQL_CASE(SQL_DESC_LOCAL_TYPE_NAME); + SQL_CASE(SQL_DESC_NAME); + SQL_CASE(SQL_DESC_NULLABLE); + SQL_CASE(SQL_DESC_NUM_PREC_RADIX); + SQL_CASE(SQL_DESC_OCTET_LENGTH); + SQL_CASE(SQL_DESC_PRECISION); + SQL_CASE(SQL_DESC_SCALE); + SQL_CASE(SQL_DESC_SCHEMA_NAME); + SQL_CASE(SQL_DESC_SEARCHABLE); + SQL_CASE(SQL_DESC_TABLE_NAME); + SQL_CASE(SQL_DESC_TYPE); + SQL_CASE(SQL_DESC_TYPE_NAME); + SQL_CASE(SQL_DESC_UNNAMED); + SQL_CASE(SQL_DESC_UNSIGNED); + SQL_CASE(SQL_DESC_UPDATABLE); default: return "UNKNOWN"; } } +// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function?view=sql-server-ver15 +const char* sql_input_output_type(int type) { + switch (type) { + SQL_CASE(SQL_PARAM_INPUT); + SQL_CASE(SQL_PARAM_OUTPUT); + SQL_CASE(SQL_PARAM_OUTPUT_STREAM); + SQL_CASE(SQL_PARAM_INPUT_OUTPUT); + SQL_CASE(SQL_PARAM_INPUT_OUTPUT_STREAM); + default: return "UNKNOWN"; + } +} + +// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function?view=sql-server-ver15 +const char* sql_stmt_attr_type(int type) { + switch (type) { + SQL_CASE(SQL_ATTR_APP_PARAM_DESC); + SQL_CASE(SQL_ATTR_APP_ROW_DESC); + SQL_CASE(SQL_ATTR_ASYNC_ENABLE); + SQL_CASE(SQL_ATTR_ASYNC_STMT_EVENT); + // ODBC 3.8 + // SQL_CASE(SQL_ATTR_ASYNC_STMT_PCALLBACK); + // ODBC 3.8 + // SQL_CASE(SQL_ATTR_ASYNC_STMT_PCONTEXT); + SQL_CASE(SQL_ATTR_CONCURRENCY); + SQL_CASE(SQL_ATTR_CURSOR_SCROLLABLE); + SQL_CASE(SQL_ATTR_CURSOR_SENSITIVITY); + SQL_CASE(SQL_ATTR_CURSOR_TYPE); + SQL_CASE(SQL_ATTR_ENABLE_AUTO_IPD); + SQL_CASE(SQL_ATTR_FETCH_BOOKMARK_PTR); + SQL_CASE(SQL_ATTR_IMP_PARAM_DESC); + SQL_CASE(SQL_ATTR_IMP_ROW_DESC); + SQL_CASE(SQL_ATTR_KEYSET_SIZE); + SQL_CASE(SQL_ATTR_MAX_LENGTH); + SQL_CASE(SQL_ATTR_MAX_ROWS); + SQL_CASE(SQL_ATTR_METADATA_ID); + SQL_CASE(SQL_ATTR_NOSCAN); + SQL_CASE(SQL_ATTR_PARAM_BIND_OFFSET_PTR); + SQL_CASE(SQL_ATTR_PARAM_BIND_TYPE); + SQL_CASE(SQL_ATTR_PARAM_OPERATION_PTR); + SQL_CASE(SQL_ATTR_PARAM_STATUS_PTR); + SQL_CASE(SQL_ATTR_PARAMS_PROCESSED_PTR); + SQL_CASE(SQL_ATTR_PARAMSET_SIZE); + SQL_CASE(SQL_ATTR_QUERY_TIMEOUT); + SQL_CASE(SQL_ATTR_RETRIEVE_DATA); + SQL_CASE(SQL_ATTR_ROW_ARRAY_SIZE); + SQL_CASE(SQL_ATTR_ROW_BIND_OFFSET_PTR); + SQL_CASE(SQL_ATTR_ROW_BIND_TYPE); + SQL_CASE(SQL_ATTR_ROW_NUMBER); + SQL_CASE(SQL_ATTR_ROW_OPERATION_PTR); + SQL_CASE(SQL_ATTR_ROW_STATUS_PTR); + SQL_CASE(SQL_ATTR_ROWS_FETCHED_PTR); + SQL_CASE(SQL_ATTR_SIMULATE_CURSOR); + SQL_CASE(SQL_ATTR_USE_BOOKMARKS); + default: return "UNKNOWN"; + } +} + +const char* sql_function_type(int type) { + switch (type) { + // + SQL_CASE(SQL_API_ALL_FUNCTIONS); + SQL_CASE(SQL_API_ODBC3_ALL_FUNCTIONS); + + // ISO 92 standards-compliance + SQL_CASE(SQL_API_SQLALLOCHANDLE); + SQL_CASE(SQL_API_SQLGETDESCFIELD); + SQL_CASE(SQL_API_SQLBINDCOL); + SQL_CASE(SQL_API_SQLGETDESCREC); + SQL_CASE(SQL_API_SQLCANCEL); + SQL_CASE(SQL_API_SQLGETDIAGFIELD); + SQL_CASE(SQL_API_SQLCLOSECURSOR); + SQL_CASE(SQL_API_SQLGETDIAGREC); + SQL_CASE(SQL_API_SQLCOLATTRIBUTE); + SQL_CASE(SQL_API_SQLGETENVATTR); + SQL_CASE(SQL_API_SQLCONNECT); + SQL_CASE(SQL_API_SQLGETFUNCTIONS); + SQL_CASE(SQL_API_SQLCOPYDESC); + SQL_CASE(SQL_API_SQLGETINFO); + SQL_CASE(SQL_API_SQLDATASOURCES); + SQL_CASE(SQL_API_SQLGETSTMTATTR); + SQL_CASE(SQL_API_SQLDESCRIBECOL); + SQL_CASE(SQL_API_SQLGETTYPEINFO); + SQL_CASE(SQL_API_SQLDISCONNECT); + SQL_CASE(SQL_API_SQLNUMRESULTCOLS); + SQL_CASE(SQL_API_SQLDRIVERS); + SQL_CASE(SQL_API_SQLPARAMDATA); + SQL_CASE(SQL_API_SQLENDTRAN); + SQL_CASE(SQL_API_SQLPREPARE); + SQL_CASE(SQL_API_SQLEXECDIRECT); + SQL_CASE(SQL_API_SQLPUTDATA); + SQL_CASE(SQL_API_SQLEXECUTE); + SQL_CASE(SQL_API_SQLROWCOUNT); + SQL_CASE(SQL_API_SQLFETCH); + SQL_CASE(SQL_API_SQLSETCONNECTATTR); + SQL_CASE(SQL_API_SQLFETCHSCROLL); + SQL_CASE(SQL_API_SQLSETCURSORNAME); + SQL_CASE(SQL_API_SQLFREEHANDLE); + SQL_CASE(SQL_API_SQLSETDESCFIELD); + SQL_CASE(SQL_API_SQLFREESTMT); + SQL_CASE(SQL_API_SQLSETDESCREC); + SQL_CASE(SQL_API_SQLGETCONNECTATTR); + SQL_CASE(SQL_API_SQLSETENVATTR); + SQL_CASE(SQL_API_SQLGETCURSORNAME); + SQL_CASE(SQL_API_SQLSETSTMTATTR); + SQL_CASE(SQL_API_SQLGETDATA); + + // Open Group standards-compliance); + SQL_CASE(SQL_API_SQLCOLUMNS); + SQL_CASE(SQL_API_SQLSTATISTICS); + SQL_CASE(SQL_API_SQLSPECIALCOLUMNS); + SQL_CASE(SQL_API_SQLTABLES); + + // ODBC standards-compliance); + SQL_CASE(SQL_API_SQLBINDPARAMETER); + SQL_CASE(SQL_API_SQLNATIVESQL); + SQL_CASE(SQL_API_SQLBROWSECONNECT); + SQL_CASE(SQL_API_SQLNUMPARAMS); + SQL_CASE(SQL_API_SQLBULKOPERATIONS); + SQL_CASE(SQL_API_SQLPRIMARYKEYS); + SQL_CASE(SQL_API_SQLCOLUMNPRIVILEGES); + SQL_CASE(SQL_API_SQLPROCEDURECOLUMNS); + SQL_CASE(SQL_API_SQLDESCRIBEPARAM); + SQL_CASE(SQL_API_SQLPROCEDURES); + SQL_CASE(SQL_API_SQLDRIVERCONNECT); + SQL_CASE(SQL_API_SQLSETPOS); + SQL_CASE(SQL_API_SQLFOREIGNKEYS); + SQL_CASE(SQL_API_SQLTABLEPRIVILEGES); + SQL_CASE(SQL_API_SQLMORERESULTS); + + SQL_CASE(SQL_API_SQLALLOCCONNECT); + SQL_CASE(SQL_API_SQLALLOCENV); + SQL_CASE(SQL_API_SQLALLOCSTMT); + SQL_CASE(SQL_API_SQLBINDPARAM); + SQL_CASE(SQL_API_SQLERROR); + SQL_CASE(SQL_API_SQLFREECONNECT); + SQL_CASE(SQL_API_SQLFREEENV); + SQL_CASE(SQL_API_SQLGETCONNECTOPTION); + SQL_CASE(SQL_API_SQLGETSTMTOPTION); + SQL_CASE(SQL_API_SQLSETCONNECTOPTION); + SQL_CASE(SQL_API_SQLSETPARAM); + SQL_CASE(SQL_API_SQLSETSTMTOPTION); + SQL_CASE(SQL_API_SQLTRANSACT); + SQL_CASE(SQL_API_SQLCANCELHANDLE); + + default: return "UNKNOWN"; + } +} + +const char* sql_freestmt_option_type(int type) { + switch (type) { + SQL_CASE(SQL_CLOSE); + SQL_CASE(SQL_DROP); + SQL_CASE(SQL_UNBIND); + SQL_CASE(SQL_RESET_PARAMS); + default: return "UNKNOWN"; + } +} + +const char* sql_soi_type(int soi) { + switch (soi) { + SQL_CASE(SQL_NTS); + SQL_CASE(SQL_NULL_DATA); + SQL_CASE(SQL_DEFAULT_PARAM); + SQL_CASE(SQL_DATA_AT_EXEC); + default: { + if (soi >= 0) return ""; + return "SQL_LEN_DATA_AT_EXEC(?)"; + } break; + } +} + +const char* sql_nullable_type(int type) { + switch (type) { + SQL_CASE(SQL_NO_NULLS); + SQL_CASE(SQL_NULLABLE); + SQL_CASE(SQL_NULLABLE_UNKNOWN); + default: { + return "UNKNOWN"; + } break; + } +} + + + + + + + + int is_valid_sql_c_type(int type) { const char *ctype = sql_c_type(type); if (strcmp(ctype, "UNKNOWN")==0) return 0; @@ -127,3 +617,46 @@ int utf8_chars(const char *src) return (int)chars; } +static int do_charset_chars(iconv_t cnv, const unsigned char *src) +{ + int chars = 0; + char *ps = (char*)src; + char buf[16]; + size_t sn = 1; + while (1) { + char *ds = buf; + size_t dn = sizeof(buf); + size_t n = iconv(cnv, &ps, &sn, &ds, &dn); + if (n==(size_t)-1) { + int e = errno; + switch (e) { + case EILSEQ: return -1; + case E2BIG: return -1; + case EINVAL: sn += 1; continue; + default: return -1; + } + } + if (sn) return -1; + if (n>0) return -1; + int i=0; + for (i=0; i<(sizeof(buf)-dn); ++i) { + if (buf[i]) break; + } + if (i>=(sizeof(buf)-dn)) break; + chars += (int)1; + sn = 1; + } + return chars; +} + +int charset_chars(const char *charset, const unsigned char *src) { + iconv_t cnv = iconv_open(charset, charset); + if (cnv==(iconv_t)-1) return -1; + + int chars = do_charset_chars(cnv, src); + + iconv_close(cnv); + + return chars; +} + diff --git a/src/connector/odbc/src/todbc_util.h b/src/connector/odbc/src/todbc_util.h index ead0d7348973409c85741cc4d676e40f6f140447..a340c32546e0b40ba05eda190c03944d1cea4d55 100644 --- a/src/connector/odbc/src/todbc_util.h +++ b/src/connector/odbc/src/todbc_util.h @@ -23,11 +23,24 @@ const char* sql_sql_type(int type); const char* sql_c_type(int type); +const char* sql_handle_type(int type); +const char* sql_env_attr_type(int type); +const char* sql_conn_attr_type(int type); +const char* sql_info_type(int type); +const char* sql_field_identifier(int type); +const char* sql_diag_identifier(int type); +const char* sql_input_output_type(int type); +const char* sql_stmt_attr_type(int type); +const char* sql_function_type(int type); +const char* sql_freestmt_option_type(int type); +const char* sql_soi_type(int soi); +const char* sql_nullable_type(int type); int is_valid_sql_c_type(int type); int is_valid_sql_sql_type(int type); int utf8_chars(const char *src); +int charset_chars(const char *charset, const unsigned char *src); #endif // _TODBC_UTIL_H_ diff --git a/src/connector/odbc/tests/CMakeLists.txt b/src/connector/odbc/tests/CMakeLists.txt deleted file mode 100644 index 1cc6acaf4bf34aa2158cc1f4fa0836d6e51f3a41..0000000000000000000000000000000000000000 --- a/src/connector/odbc/tests/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -PROJECT(TDengine) - -IF (TD_LINUX) - # AUX_SOURCE_DIRECTORY(. SRC) - ADD_EXECUTABLE(tcodbc main.c) - TARGET_LINK_LIBRARIES(tcodbc odbc) - ADD_EXECUTABLE(tconv tconv.c) -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") - # AUX_SOURCE_DIRECTORY(. SRC) - ADD_EXECUTABLE(tcodbc main.c) - TARGET_LINK_LIBRARIES(tcodbc odbc32 odbccp32 user32 legacy_stdio_definitions os) - ADD_EXECUTABLE(tconv tconv.c) - TARGET_LINK_LIBRARIES(tconv tutil) -ENDIF () diff --git a/src/connector/odbc/tests/main.c b/src/connector/odbc/tests/main.c deleted file mode 100644 index 417de00d55f64249a9194b77fecbeb458c560cc7..0000000000000000000000000000000000000000 --- a/src/connector/odbc/tests/main.c +++ /dev/null @@ -1,673 +0,0 @@ -#include "../src/todbc_log.h" - -#ifdef _MSC_VER -#include -#include -#include "os.h" -#endif -#include -#include -#include - -#include -#include - -#define CHK_TEST(statement) \ -do { \ - D("testing: %s", #statement); \ - int r = (statement); \ - if (r) { \ - D("testing failed: %s", #statement); \ - return 1; \ - } \ -} while (0); - -typedef struct db_column_s db_column_t; -struct db_column_s { - SQLSMALLINT nameLength; - char name[4096]; // seems enough - SQLSMALLINT dataType; - SQLULEN columnSize; - SQLSMALLINT decimalDigits; - SQLSMALLINT nullable; -}; - -static db_column_t *columns = NULL; - -typedef struct data_s data_t; -struct data_s { - int64_t ts; - int8_t b; - int8_t v1; - int16_t v2; - int32_t v4; - int64_t v8; - float f4; - double f8; - char bin[40+1]; - char blob[40+1]; // why 80? ref: tests/examples/c/apitest.c -}; - -static const char *pre_stmts[] = { - "create database db", - "use db", - "create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))" -}; - -static const char *pro_stmts[] = { - // "insert into t values ('2019-07-15 00:00:00', 1)", - // "insert into t values ('2019-07-15 01:00:00', 2)", - "select * from t" - // "drop database db" -}; - -#define CHK_RESULT(r, ht, h, fmt, ...) \ -do { \ - if (r==0) break; \ - SQLCHAR ss[10]; \ - SQLINTEGER ne = 0; \ - SQLCHAR es[4096]; \ - SQLSMALLINT n = 0; \ - ss[0] = '\0'; \ - es[0] = '\0'; \ - SQLRETURN ret = SQLGetDiagRec(ht, h, 1, ss, &ne, es, sizeof(es), &n); \ - if (ret) break; \ - D("[%s]%s: " fmt "", ss, es, ##__VA_ARGS__); \ -} while (0) - -static int open_connect(const char *dsn, const char *uid, const char *pwd, SQLHENV *pEnv, SQLHDBC *pConn) { - SQLRETURN r; - SQLHENV env = {0}; - SQLHDBC conn = {0}; - r = SQLAllocEnv(&env); - if (r!=SQL_SUCCESS) return 1; - do { - r = SQLAllocConnect(env, &conn); - CHK_RESULT(r, SQL_HANDLE_ENV, env, ""); - if (r!=SQL_SUCCESS) break; - do { - r = SQLConnect(conn, (SQLCHAR*)dsn, (SQLSMALLINT)(dsn ? strlen(dsn) : 0), - (SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0), - (SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0)); - CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); - if (r==SQL_SUCCESS) { - *pEnv = env; - *pConn = conn; - return 0; - } - } while (0); - SQLFreeConnect(conn); - } while (0); - SQLFreeEnv(env); - - return 1; -} - -static int open_driver_connect(const char *connstr, SQLHENV *pEnv, SQLHDBC *pConn) { - SQLRETURN r; - SQLHENV env = {0}; - SQLHDBC conn = {0}; - r = SQLAllocEnv(&env); - if (r!=SQL_SUCCESS) return 1; - do { - r = SQLAllocConnect(env, &conn); - CHK_RESULT(r, SQL_HANDLE_ENV, env, ""); - if (r!=SQL_SUCCESS) break; - do { - SQLCHAR buf[4096]; - SQLSMALLINT blen = 0; - SQLHDBC ConnectionHandle = conn; - SQLHWND WindowHandle = NULL; - SQLCHAR * InConnectionString = (SQLCHAR*)connstr; - SQLSMALLINT StringLength1 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0); - SQLCHAR * OutConnectionString = buf; - SQLSMALLINT BufferLength = sizeof(buf); - SQLSMALLINT * StringLength2Ptr = &blen; - SQLUSMALLINT DriverCompletion = SQL_DRIVER_NOPROMPT; - r = SQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString, - StringLength1, OutConnectionString, BufferLength, - StringLength2Ptr, DriverCompletion); - CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); - if (r==SQL_SUCCESS) { - *pEnv = env; - *pConn = conn; - return 0; - } - } while (0); - SQLFreeConnect(conn); - } while (0); - SQLFreeEnv(env); - - return 1; -} - -static SQLRETURN traverse_cols(SQLHSTMT stmt, SQLSMALLINT cols) { - SQLRETURN r = SQL_ERROR; - for (SQLSMALLINT i=0; i0) fprintf(stdout, "\n"); - return r; - } - } - if (soi==SQL_NULL_DATA) { - fprintf(stdout, "%snull", i==0?"":","); - } else { - fprintf(stdout, "%s\"%s\"", i==0?"":",", buf); - } - } - fprintf(stdout, "\n"); - } - - // r = SQLFetch(stmt); - // if (r==SQL_NO_DATA) { - // D(".........."); - // r = SQL_SUCCESS; - // break; - // } - // CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); - // if (r) break; - // r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement)); - // CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); - // if (r) break; - // r = SQLExecute(stmt); - // CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - // if (r) break; - } while (0); - return r; -} - -static int do_insert(SQLHSTMT stmt, data_t data) { - SQLRETURN r = 0; - SQLLEN lbin; - SQLLEN lblob; - - const char *statement = "insert into t values (?, ?, ?, ?, ?, ?, ?, ?, ?,?)"; - #define ignored 0 - - do { - r = SQLPrepare(stmt, (SQLCHAR*)statement, (SQLINTEGER)strlen(statement)); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_TIMESTAMP, ignored, ignored, &data.ts, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, ignored, ignored, &data.b, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, ignored, ignored, &data.v1, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, ignored, ignored, &data.v2, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, ignored, ignored, &data.v4, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, ignored, ignored, &data.v8, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, ignored, ignored, &data.f4, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignored, ignored, &data.f8, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - lbin = SQL_NTS; - r = SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, sizeof(data.bin)-1, ignored, &data.bin, ignored, &lbin); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - lblob = SQL_NTS; - r = SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(data.blob)-1, ignored, &data.blob, ignored, &lblob); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - r = SQLExecute(stmt); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); - if (r) break; - - // ts += 1; - // v = 2; - // r = SQLExecute(stmt); - // if (r) break; - } while (0); - - #undef ignored - return r; -} - -static int test1(const char *dsn, const char *uid, const char *pwd) { - SQLHENV env = {0}; - SQLHDBC conn = {0}; - int n = open_connect(dsn, uid, pwd, &env, &conn); - if (n) return 1; - - int ok = 0; - do { - SQLRETURN r = SQL_SUCCESS; - SQLHSTMT stmt = {0}; - r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt); - if (r!=SQL_SUCCESS) break; - do { - if (do_statement(stmt, "drop database if exists db")) { - break; - } - for (size_t i=0; i0 && line[n-1] == '\n') line[n-1]='\0'; - if (n>0 && line[n-1] == '\r') line[n-1]='\0'; - if (n>1 && line[n-2] == '\r') line[n-2]='\0'; - p = line; - while (isspace(*p)) ++p; - - if (*p==0) break; - - int positive = 1; - if (strncmp(p, "N:", 2)==0) { - positive = 0; - p += 2; - } else if (strncmp(p, "P:", 2)==0) { - p += 2; - } - - D("statement: [%s]", p); - r = do_statement(stmt, p); - - if (positive && r==0) break; - if (!positive && r) { r = 0; break; } - if (positive) return r; - D("expecting negative result, but got positive"); - return -1; - } while (0); - - free(line); - - if (r) break; - } - - fclose(f); - return r ? 1 : 0; -} - -int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) { - SQLHSTMT stmt = {0}; - CHK_TEST(create_statement(env, conn, &stmt)); - int r = test_sqls_in_stmt(env, conn, stmt, sqls); - SQLFreeHandle(SQL_HANDLE_STMT, stmt); - return r ? 1 : 0; -} - -int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *connstr, const char *sqls) { - int r = 0; - SQLHENV env = {0}; - SQLHDBC conn = {0}; - if (dsn) { - CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn)); - } else { - CHK_TEST(open_driver_connect(connstr, &env, &conn)); - } - if (sqls) { - r = test_sqls_in_conn(env, conn, sqls); - } - SQLDisconnect(conn); - SQLFreeConnect(conn); - SQLFreeEnv(env); - return r ? 1 : 0; -} - -void usage(const char *arg0) { - fprintf(stdout, "%s usage:\n", arg0); - fprintf(stdout, "%s [--dsn ] [--uid ] [--pwd ] [--dcs ] [--sts ]\n", arg0); - fprintf(stdout, " --dsn : DSN\n"); - fprintf(stdout, " --uid : UID\n"); - fprintf(stdout, " --pwd : PWD\n"); - fprintf(stdout, " --dcs : driver connection string\n"); - fprintf(stdout, " --sts : file where statements store\n"); -} - -int main(int argc, char *argv[]) { - // if (argc==1) { - // CHK_TEST(test_env()); - // CHK_TEST(test1("TAOS_DSN", "root", "taoxsdata")); - // D("Done!"); - // return 0; - // } - - const char *dsn = NULL; - const char *uid = NULL; - const char *pwd = NULL; - const char *dcs = NULL; // driver connection string - const char *sts = NULL; // statements file - for (size_t i=1; i=argc) { - D(" expected but got nothing"); - return 1; - } - if (dcs) { - D("--dcs has already been specified"); - return 1; - } - dsn = argv[i]; - continue; - } - if (strcmp(arg, "--uid")==0) { - ++i; - if (i>=argc) { - D(" expected but got nothing"); - return 1; - } - uid = argv[i]; - continue; - } - if (strcmp(arg, "--pwd")==0) { - ++i; - if (i>=argc) { - D(" expected but got nothing"); - return 1; - } - pwd = argv[i]; - continue; - } - if (strcmp(arg, "--dcs")==0) { - ++i; - if (i>=argc) { - D(" expected but got nothing"); - return 1; - } - if (dsn || uid || pwd) { - D("either of --dsn/--uid/--pwd has already been specified"); - return 1; - } - dcs = argv[i]; - continue; - } - if (strcmp(arg, "--sts")==0) { - ++i; - if (i>=argc) { - D(" expected but got nothing"); - return 1; - } - sts = argv[i]; - continue; - } - } - CHK_TEST(test_sqls(dsn, uid, pwd, dcs, sts)); - D("Done!"); - return 0; - - if (0) { - const char *dsn = (argc>1) ? argv[1] : NULL; - const char *uid = (argc>2) ? argv[2] : NULL; - const char *pwd = (argc>3) ? argv[3] : NULL; - const char *connstr = (argc>4) ? argv[4] : NULL; - const char *sqls = (argc>5) ? argv[5] : NULL; - - dsn = NULL; - uid = NULL; - pwd = NULL; - connstr = argv[1]; - sqls = argv[2]; - if (0) { - CHK_TEST(test_env()); - - CHK_TEST(test1(dsn, uid, pwd)); - - const char *statements[] = { - "drop database if exists m", - "create database m", - "use m", - "drop database m", - NULL - }; - CHK_TEST(test_statements(dsn, uid, pwd, statements)); - - if (connstr) - CHK_TEST(test_driver_connect(connstr)); - - if (connstr) { - SQLHENV env = {0}; - SQLHDBC conn = {0}; - CHK_TEST(open_driver_connect(connstr, &env, &conn)); - int r = tests(env, conn); - SQLDisconnect(conn); - SQLFreeConnect(conn); - SQLFreeEnv(env); - if (r) return 1; - } - } - - if ((dsn || connstr) && 1) { - CHK_TEST(test_sqls(dsn, uid, pwd, connstr, sqls)); - } - - D("Done!"); - return 0; - } -} - diff --git a/src/connector/odbc/tests/odbc.py b/src/connector/odbc/tests/odbc.py deleted file mode 100644 index c137905775e567f6163846690886850cb77a684a..0000000000000000000000000000000000000000 --- a/src/connector/odbc/tests/odbc.py +++ /dev/null @@ -1,131 +0,0 @@ -import pyodbc -# cnxn = pyodbc.connect('DSN={TAOS_DSN};UID={ root };PWD={ taosdata };HOST={ localhost:6030 }', autocommit=True) -cnxn = pyodbc.connect('DSN={TAOS_DSN}; UID=root;PWD=taosdata; HOST=localhost:6030', autocommit=True) -cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8') -#cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8') -#cnxn.setencoding(encoding='utf-8') - -#cursor = cnxn.cursor() -#cursor.execute("SELECT * from db.t") -#row = cursor.fetchone() -#while row: -# print(row) -# row = cursor.fetchone() -#cursor.close() - -#cursor = cnxn.cursor() -#cursor.execute(""" -#INSERT INTO db.t values (?,?,?,?,?,?,?,?,?,?) -#""", -#"2020-12-12 00:00:00", -#1, -#27, -#32767, -#147483647, -#223372036854775807, -#23.456, -#899.999999, -#"foo", -#"bar") - -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(40), blob nchar(10))"); -cursor.close() - -cursor = cnxn.cursor() -cursor.execute("insert into db.mt values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')") -cursor.close() - -cursor = cnxn.cursor() -cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld"); -cursor.close() - -cursor = cnxn.cursor() -cursor.execute("SELECT * from db.mt") -row = cursor.fetchone() -while row: - print(row) - row = cursor.fetchone() -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', 'worl')") -cursor.close() - -cursor = cnxn.cursor() -cursor.execute("SELECT * from db.t") -row = cursor.fetchone() -while row: - print(row) - row = cursor.fetchone() -cursor.close() - -cursor = cnxn.cursor() -cursor.execute("create table db.v (ts timestamp, v1 tinyint)") -cursor.close() - -params = [ ('A', 1), ('B', 2), ('C', 3) ] -params = [ ('A', 1), ('B', 2), ('C', 3) ] -params = [ ('2020-10-16 00:00:00', 1), - ('2020-10-16 00:00:01', 4), - ('2020-10-16 00:00:02', 5), - ('2020-10-16 00:00:03.009', 6) ] -cursor = cnxn.cursor() -cursor.fast_executemany = True -cursor.executemany("insert into db.v values (?, ?)", params) -cursor.close() - -cursor = cnxn.cursor() -cursor.execute("SELECT * from db.v") -row = cursor.fetchone() -while row: - print(row) - row = cursor.fetchone() -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() - -cursor = cnxn.cursor() -cursor.execute("create table db.f (ts timestamp, v1 float)") -cursor.close() - -params = [ ('2020-10-20 00:00:10', '123.3') ] -cursor = cnxn.cursor() -cursor.fast_executemany = True -cursor.executemany("insert into db.f values (?, ?)", params) -cursor.close() - diff --git a/src/connector/odbc/tools/CMakeLists.txt b/src/connector/odbc/tools/CMakeLists.txt index a0aafb1f3ca36bb0b6fb108e1948214b2dd14a6f..e543d245c8c3ef19f541832b3f2c8889860db2f9 100644 --- a/src/connector/odbc/tools/CMakeLists.txt +++ b/src/connector/odbc/tools/CMakeLists.txt @@ -1,12 +1,23 @@ PROJECT(TDengine) -IF (TD_LINUX) - ADD_EXECUTABLE(todbcinst main.c) +ADD_EXECUTABLE(todbcinst main.c) +ADD_EXECUTABLE(tconv tconv.c) + +IF (TD_LINUX OR TD_DARWIN) TARGET_LINK_LIBRARIES(todbcinst odbc odbcinst) ENDIF () +IF (TD_DARWIN) + target_include_directories(todbcinst PRIVATE /usr/local/include) + target_link_directories(todbcinst PUBLIC /usr/local/lib) + target_include_directories(tconv PRIVATE /usr/local/include) + target_link_directories(tconv PUBLIC /usr/local/lib) + TARGET_LINK_LIBRARIES(tconv iconv) +ENDIF () + IF (TD_WINDOWS_64) - ADD_EXECUTABLE(todbcinst main.c) TARGET_LINK_LIBRARIES(todbcinst odbc32 odbccp32 user32 legacy_stdio_definitions os) + TARGET_LINK_LIBRARIES(tconv taos) INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/todbcinst.exe DESTINATION .) ENDIF () + diff --git a/src/connector/odbc/tests/tconv.c b/src/connector/odbc/tools/tconv.c similarity index 100% rename from src/connector/odbc/tests/tconv.c rename to src/connector/odbc/tools/tconv.c diff --git a/src/dnode/src/dnodeVWrite.c b/src/dnode/src/dnodeVWrite.c index a3ff459396a974eae26be3561e418b5836b90f4b..87b31e4604882d750e4a43fde4a6d818cc862e98 100644 --- a/src/dnode/src/dnodeVWrite.c +++ b/src/dnode/src/dnodeVWrite.c @@ -222,7 +222,7 @@ static void *dnodeProcessVWriteQueue(void *wparam) { dnodeSendRpcVWriteRsp(pVnode, pWrite, pWrite->code); } else { if (qtype == TAOS_QTYPE_FWD) { - vnodeConfirmForward(pVnode, pWrite->pHead.version, 0); + vnodeConfirmForward(pVnode, pWrite->pHead.version, 0, pWrite->pHead.msgType != TSDB_MSG_TYPE_SUBMIT); } if (pWrite->rspRet.rsp) { rpcFreeCont(pWrite->rspRet.rsp); diff --git a/src/inc/query.h b/src/inc/query.h index 7342221cb9de1b632ad0f398f2f3a8d27621747a..77a12ebfc5c069bef42dcb6a339c650528df8987 100644 --- a/src/inc/query.h +++ b/src/inc/query.h @@ -28,7 +28,7 @@ typedef void* qinfo_t; * @param qinfo * @return */ -int32_t qCreateQueryInfo(void* tsdb, int32_t vgId, SQueryTableMsg* pQueryTableMsg, qinfo_t* qinfo); +int32_t qCreateQueryInfo(void* tsdb, int32_t vgId, SQueryTableMsg* pQueryTableMsg, qinfo_t* qinfo, uint64_t *qId); /** @@ -88,9 +88,10 @@ void* qOpenQueryMgmt(int32_t vgId); void qQueryMgmtNotifyClosed(void* pExecutor); void qQueryMgmtReOpen(void *pExecutor); void qCleanupQueryMgmt(void* pExecutor); -void** qRegisterQInfo(void* pMgmt, uint64_t qInfo); +void** qRegisterQInfo(void* pMgmt, uint64_t qId, uint64_t qInfo); void** qAcquireQInfo(void* pMgmt, uint64_t key); void** qReleaseQInfo(void* pMgmt, void* pQInfo, bool freeHandle); +bool checkQIdEqual(void *qHandle, uint64_t qId); #ifdef __cplusplus } diff --git a/src/inc/taoserror.h b/src/inc/taoserror.h index e069d0295ac5b08c0ff38cc09e90eb5b56817e7b..8daccee1f255e5956bb8308bac5d8d05d1088642 100644 --- a/src/inc/taoserror.h +++ b/src/inc/taoserror.h @@ -187,6 +187,8 @@ int32_t* taosGetErrno(); #define TSDB_CODE_MND_INVALID_TOPIC TAOS_DEF_ERROR_CODE(0, 0x0392) //"Invalid topic name) #define TSDB_CODE_MND_INVALID_TOPIC_OPTION TAOS_DEF_ERROR_CODE(0, 0x0393) //"Invalid topic option) +#define TSDB_CODE_MND_INVALID_TOPIC_PARTITONS TAOS_DEF_ERROR_CODE(0, 0x0394) //"Invalid topic partitons num, valid range: [1, 1000]) +#define TSDB_CODE_MND_TOPIC_ALREADY_EXIST TAOS_DEF_ERROR_CODE(0, 0x0395) //"Topic already exists) // dnode #define TSDB_CODE_DND_MSG_NOT_PROCESSED TAOS_DEF_ERROR_CODE(0, 0x0400) //"Message not processed") diff --git a/src/inc/tfs.h b/src/inc/tfs.h index 76e9b17a621f9842479396ada65bb10ac147366c..4ed21bc6e1ddb4d9011d701ad99e6f2a71f54b0e 100644 --- a/src/inc/tfs.h +++ b/src/inc/tfs.h @@ -35,6 +35,7 @@ typedef struct { // FS APIs ==================================== typedef struct { int64_t tsize; + int64_t used; int64_t avail; } SFSMeta; @@ -90,4 +91,4 @@ void tfsClosedir(TDIR *tdir); } #endif -#endif \ No newline at end of file +#endif diff --git a/src/inc/tp.h b/src/inc/tp.h index b0b787bf689dafb016416cb82c1ebeb049d7c104..1d6570898ebfcf0137fd2d502dc39de9378714b9 100644 --- a/src/inc/tp.h +++ b/src/inc/tp.h @@ -22,7 +22,7 @@ extern "C" { int32_t tpInit(); void tpCleanUp(); -void tpUpdateTs(int32_t *seq, void *pMsg); +void tpUpdateTs(int32_t vgId, int64_t *seq, void *pMsg); #ifdef __cplusplus } diff --git a/src/inc/tsdb.h b/src/inc/tsdb.h index 09444bb8e4b2c6cd45ce087a3444338d140a90ae..493bdbe5ded9f6235687d376625b8f6fc9861c8f 100644 --- a/src/inc/tsdb.h +++ b/src/inc/tsdb.h @@ -25,6 +25,8 @@ #include "tdataformat.h" #include "tname.h" #include "hash.h" +#include "tlockfree.h" +#include "tlist.h" #ifdef __cplusplus extern "C" { @@ -172,10 +174,32 @@ typedef struct STsdbQueryCond { int32_t type; // data block load type: } STsdbQueryCond; +typedef struct STableData STableData; +typedef struct { + T_REF_DECLARE() + SRWLatch latch; + TSKEY keyFirst; + TSKEY keyLast; + int64_t numOfRows; + int32_t maxTables; + STableData **tData; + SList * actList; + SList * extraBuffList; + SList * bufBlockList; + int64_t pointsAdd; // TODO + int64_t storageAdd; // TODO +} SMemTable; + +typedef struct { + SMemTable* mem; + SMemTable* imem; + SMemTable mtable; + SMemTable* omem; +} SMemSnapshot; + typedef struct SMemRef { - int32_t ref; - void * mem; - void * imem; + int32_t ref; + SMemSnapshot snapshot; } SMemRef; typedef struct SDataBlockInfo { diff --git a/src/inc/tsync.h b/src/inc/tsync.h index 379c877b266b6026ea9d9ee55f76ecb24fca1a44..99dfd3a6a30b1c7cf36e94a7d2485181f966f5fc 100644 --- a/src/inc/tsync.h +++ b/src/inc/tsync.h @@ -79,6 +79,9 @@ typedef void (*FStopSyncFile)(int32_t vgId, uint64_t fversion); // get file version typedef int32_t (*FGetVersion)(int32_t vgId, uint64_t *fver, uint64_t *vver); +// reset version +typedef int32_t (*FResetVersion)(int32_t vgId, uint64_t fver); + typedef int32_t (*FSendFile)(void *tsdb, SOCKET socketFd); typedef int32_t (*FRecvFile)(void *tsdb, SOCKET socketFd); @@ -96,6 +99,7 @@ typedef struct { FStartSyncFile startSyncFileFp; FStopSyncFile stopSyncFileFp; FGetVersion getVersionFp; + FResetVersion resetVersionFp; FSendFile sendFileFp; FRecvFile recvFileFp; } SSyncInfo; @@ -108,8 +112,8 @@ void syncCleanUp(); int64_t syncStart(const SSyncInfo *); void syncStop(int64_t rid); int32_t syncReconfig(int64_t rid, const SSyncCfg *); -int32_t syncForwardToPeer(int64_t rid, void *pHead, void *mhandle, int32_t qtype); -void syncConfirmForward(int64_t rid, uint64_t version, int32_t code); +int32_t syncForwardToPeer(int64_t rid, void *pHead, void *mhandle, int32_t qtype, bool force); +void syncConfirmForward(int64_t rid, uint64_t version, int32_t code, bool force); void syncRecover(int64_t rid); // recover from other nodes: int32_t syncGetNodesRole(int64_t rid, SNodesRole *); diff --git a/src/inc/twal.h b/src/inc/twal.h index 1645de77aacf020048dc21cd47907163089f269f..bce398d6f95518379c1cd4c0d95a4f6324aab4d7 100644 --- a/src/inc/twal.h +++ b/src/inc/twal.h @@ -65,6 +65,7 @@ void walFsync(twalh, bool forceFsync); int32_t walRestore(twalh, void *pVnode, FWalWrite writeFp); int32_t walGetWalFile(twalh, char *fileName, int64_t *fileId); uint64_t walGetVersion(twalh); +void walResetVersion(twalh, uint64_t newVer); #ifdef __cplusplus } diff --git a/src/inc/vnode.h b/src/inc/vnode.h index 7c9ebd8a0b9d96090134bd23c2061c857dfcd5f1..dddec83da87f6cf7f25212dd1aa39373e6b613f7 100644 --- a/src/inc/vnode.h +++ b/src/inc/vnode.h @@ -78,7 +78,7 @@ void vnodeFreeFromWQueue(void *pVnode, SVWriteMsg *pWrite); int32_t vnodeProcessWrite(void *pVnode, void *pHead, int32_t qtype, void *pRspRet); // vnodeSync -void vnodeConfirmForward(void *pVnode, uint64_t version, int32_t code); +void vnodeConfirmForward(void *pVnode, uint64_t version, int32_t code, bool force); // vnodeRead int32_t vnodeWriteToRQueue(void *pVnode, void *pCont, int32_t contLen, int8_t qtype, void *rparam); diff --git a/src/kit/shell/CMakeLists.txt b/src/kit/shell/CMakeLists.txt index b6babc5bc53aa254e0372dbbfd235bdd4cef878a..d36c1e3fccc4ee7c5eae359f975e2ac1faa6c135 100644 --- a/src/kit/shell/CMakeLists.txt +++ b/src/kit/shell/CMakeLists.txt @@ -36,6 +36,7 @@ ELSEIF (TD_DARWIN) LIST(APPEND SRC ./src/shellDarwin.c) LIST(APPEND SRC ./src/shellCommand.c) LIST(APPEND SRC ./src/shellImport.c) + LIST(APPEND SRC ./src/shellCheck.c) ADD_EXECUTABLE(shell ${SRC}) # linking with dylib TARGET_LINK_LIBRARIES(shell taos) diff --git a/src/kit/shell/inc/shell.h b/src/kit/shell/inc/shell.h index 50bceb1a7169373d1c832cadf03d6d601cace264..019f3e5d92d453a9df97ab0b4bb2c7614bf2210d 100644 --- a/src/kit/shell/inc/shell.h +++ b/src/kit/shell/inc/shell.h @@ -51,6 +51,7 @@ typedef struct SShellArguments { char file[TSDB_FILENAME_LEN]; char dir[TSDB_FILENAME_LEN]; int threadNum; + int check; char* commands; int abort; int port; @@ -71,6 +72,7 @@ void read_history(); void write_history(); void source_file(TAOS* con, char* fptr); void source_dir(TAOS* con, SShellArguments* args); +void shellCheck(TAOS* con, SShellArguments* args); void get_history_path(char* history); void cleanup_handler(void* arg); void exitShell(); diff --git a/src/kit/shell/src/shellCheck.c b/src/kit/shell/src/shellCheck.c new file mode 100644 index 0000000000000000000000000000000000000000..b88244ea018291fbdc98165a8665949f618e3291 --- /dev/null +++ b/src/kit/shell/src/shellCheck.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE +#define _XOPEN_SOURCE +#define _DEFAULT_SOURCE + +#include "os.h" +#include "shell.h" +#include "shellCommand.h" +#include "tglobal.h" +#include "tutil.h" + +#define SHELL_SQL_LEN 1024 +static int32_t tbNum = 0; +static int32_t tbMallocNum = 0; +static char ** tbNames = NULL; +static int32_t checkedNum = 0; +static int32_t errorNum = 0; + +typedef struct { + pthread_t threadID; + int threadIndex; + int totalThreads; + void * taos; + char * db; +} ShellThreadObj; + +static int32_t shellUseDb(TAOS *con, char *db) { + if (db == NULL) { + fprintf(stdout, "no dbname input\n"); + return -1; + } + + char sql[SHELL_SQL_LEN] = {0}; + snprintf(sql, SHELL_SQL_LEN, "use %s", db); + + TAOS_RES *pSql = taos_query(con, sql); + int32_t code = taos_errno(pSql); + if (code != 0) { + fprintf(stdout, "failed to execute sql:%s since %s", sql, taos_errstr(pSql)); + } + + taos_free_result(pSql); + return code; +} + +static int32_t shellShowTables(TAOS *con, char *db) { + char sql[SHELL_SQL_LEN] = {0}; + snprintf(sql, SHELL_SQL_LEN, "show %s.tables", db); + + TAOS_RES *pSql = taos_query(con, sql); + int32_t code = taos_errno(pSql); + + if (code != 0) { + fprintf(stdout, "failed to execute sql:%s since %s\n", sql, taos_errstr(pSql)); + } else { + TAOS_ROW row; + while ((row = taos_fetch_row(pSql))) { + int32_t tbIndex = tbNum++; + if (tbMallocNum < tbNum) { + tbMallocNum = (tbMallocNum * 2 + 1); + tbNames = realloc(tbNames, tbMallocNum * sizeof(char *)); + if (tbNames == NULL) { + fprintf(stdout, "failed to malloc tablenames, num:%d\n", tbMallocNum); + code = TSDB_CODE_TSC_OUT_OF_MEMORY; + break; + } + } + + tbNames[tbIndex] = malloc(TSDB_TABLE_NAME_LEN); + strncpy(tbNames[tbIndex], (const char *)row[0], TSDB_TABLE_NAME_LEN); + if (tbIndex % 100000 == 0 && tbIndex != 0) { + fprintf(stdout, "%d tablenames fetched\n", tbIndex); + } + } + } + + taos_free_result(pSql); + + fprintf(stdout, "total %d tablenames fetched, over\n", tbNum); + return code; +} + +static void shellFreeTbnames() { + for (int32_t i = 0; i < tbNum; ++i) { + free(tbNames[i]); + } + free(tbNames); +} + +static void *shellCheckThreadFp(void *arg) { + ShellThreadObj *pThread = (ShellThreadObj *)arg; + + int32_t interval = tbNum / pThread->totalThreads + 1; + int32_t start = pThread->threadIndex * interval; + int32_t end = (pThread->threadIndex + 1) * interval; + + if (end > tbNum) end = tbNum + 1; + + char file[32] = {0}; + snprintf(file, 32, "tb%d.txt", pThread->threadIndex); + + FILE *fp = fopen(file, "w"); + if (!fp) { + fprintf(stdout, "failed to open %s, reason:%s", file, strerror(errno)); + return NULL; + } + + char sql[SHELL_SQL_LEN]; + for (int32_t t = start; t < end; ++t) { + char *tbname = tbNames[t]; + if (tbname == NULL) break; + + snprintf(sql, SHELL_SQL_LEN, "select * from %s limit 1", tbname); + + TAOS_RES *pSql = taos_query(pThread->taos, sql); + int32_t code = taos_errno(pSql); + if (code != 0) { + int32_t len = snprintf(sql, SHELL_SQL_LEN, "drop table %s.%s;\n", pThread->db, tbname); + fwrite(sql, 1, len, fp); + atomic_add_fetch_32(&errorNum, 1); + } + + int32_t cnum = atomic_add_fetch_32(&checkedNum, 1); + if (cnum % 5000 == 0 && cnum != 0) { + fprintf(stdout, "%d tables checked\n", cnum); + } + + taos_free_result(pSql); + } + + fsync(fileno(fp)); + fclose(fp); + + return NULL; +} + +static void shellRunCheckThreads(TAOS *con, SShellArguments *args) { + pthread_attr_t thattr; + ShellThreadObj *threadObj = (ShellThreadObj *)calloc(args->threadNum, sizeof(ShellThreadObj)); + for (int t = 0; t < args->threadNum; ++t) { + ShellThreadObj *pThread = threadObj + t; + pThread->threadIndex = t; + pThread->totalThreads = args->threadNum; + pThread->taos = con; + pThread->db = args->database; + + pthread_attr_init(&thattr); + pthread_attr_setdetachstate(&thattr, PTHREAD_CREATE_JOINABLE); + + if (pthread_create(&(pThread->threadID), &thattr, shellCheckThreadFp, (void *)pThread) != 0) { + fprintf(stderr, "ERROR: thread:%d failed to start\n", pThread->threadIndex); + exit(0); + } + } + + for (int t = 0; t < args->threadNum; ++t) { + pthread_join(threadObj[t].threadID, NULL); + } + + for (int t = 0; t < args->threadNum; ++t) { + taos_close(threadObj[t].taos); + } + free(threadObj); +} + +void shellCheck(TAOS *con, SShellArguments *args) { + int64_t start = taosGetTimestampMs(); + + if (shellUseDb(con, args->database) != 0) { + shellFreeTbnames(); + return; + } + + if (shellShowTables(con, args->database) != 0) { + shellFreeTbnames(); + return; + } + + fprintf(stdout, "total %d tables will be checked by %d threads\n", tbNum, args->threadNum); + shellRunCheckThreads(con, args); + + int64_t end = taosGetTimestampMs(); + fprintf(stdout, "total %d tables checked, failed:%d, time spent %.2f seconds\n", checkedNum, errorNum, + (end - start) / 1000.0); +} diff --git a/src/kit/shell/src/shellEngine.c b/src/kit/shell/src/shellEngine.c index e86a1fce60a3ea6eda5ddccd309df9f89a32e6a6..b9529aac8ec572083406f0d902d48f424ef4547b 100644 --- a/src/kit/shell/src/shellEngine.c +++ b/src/kit/shell/src/shellEngine.c @@ -121,6 +121,12 @@ TAOS *shellInit(SShellArguments *args) { taos_close(con); exit(EXIT_SUCCESS); } + + if (args->check != 0) { + shellCheck(con, args); + taos_close(con); + exit(EXIT_SUCCESS); + } #endif return con; diff --git a/src/kit/shell/src/shellLinux.c b/src/kit/shell/src/shellLinux.c index 07b21531a7f1cf9fc6346a27373fe9de8895f2b2..3f6b3da9bf4b1f80f58a06613e8571ca16892c46 100644 --- a/src/kit/shell/src/shellLinux.c +++ b/src/kit/shell/src/shellLinux.c @@ -45,6 +45,7 @@ static struct argp_option options[] = { {"file", 'f', "FILE", 0, "Script to run without enter the shell."}, {"directory", 'D', "DIRECTORY", 0, "Use multi-thread to import all SQL files in the directory separately."}, {"thread", 'T', "THREADNUM", 0, "Number of threads when using multi-thread to import data."}, + {"check", 'k', "CHECK", 0, "Check tables."}, {"database", 'd', "DATABASE", 0, "Database to use when connecting to the server."}, {"timezone", 't', "TIMEZONE", 0, "Time zone of the shell, default is local."}, {"netrole", 'n', "NETROLE", 0, "Net role when network connectivity test, default is startup, options: client|server|rpc|startup|sync."}, @@ -130,6 +131,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { return -1; } break; + case 'k': + arguments->check = atoi(arg); + break; case 'd': arguments->database = arg; break; diff --git a/src/kit/taosdemo/insert-interlace.json b/src/kit/taosdemo/insert-interlace.json new file mode 100644 index 0000000000000000000000000000000000000000..0f54f008fb04d1873c5ee41d57a28e777d44ffea --- /dev/null +++ b/src/kit/taosdemo/insert-interlace.json @@ -0,0 +1,57 @@ +{ + "filetype": "insert", + "cfgdir": "/etc/taos", + "host": "127.0.0.1", + "port": 6030, + "user": "root", + "password": "taosdata", + "thread_count": 4, + "thread_count_create_tbl": 4, + "result_file": "./insert_res.txt", + "confirm_parameter_prompt": "no", + "insert_interval": 1000, + "num_of_records_per_req": 100, + "max_sql_len": 1024000, + "databases": [{ + "dbinfo": { + "name": "db", + "drop": "yes", + "replica": 1, + "days": 10, + "cache": 16, + "blocks": 8, + "precision": "ms", + "keep": 365, + "minRows": 100, + "maxRows": 4096, + "comp":2, + "walLevel":1, + "cachelast":0, + "quorum":1, + "fsync":3000, + "update": 0 + }, + "super_tables": [{ + "name": "stb", + "child_table_exists":"no", + "childtable_count": 100, + "childtable_prefix": "stb_", + "auto_create_table": "no", + "data_source": "rand", + "insert_mode": "taosc", + "insert_rows": 1000, + "multi_thread_write_one_tbl": "no", + "rows_per_tbl": 20, + "max_sql_len": 1024000, + "disorder_ratio": 0, + "disorder_range": 1000, + "timestamp_step": 1, + "start_timestamp": "2020-10-01 00:00:00.000", + "sample_format": "csv", + "sample_file": "./sample.csv", + "tags_file": "", + "columns": [{"type": "INT"}], + "tags": [{"type": "TINYINT", "count":1}] + }] + }] +} diff --git a/src/kit/taosdemo/insert.json b/src/kit/taosdemo/insert.json index 208b5fcb746aed052cdb8abefe64f5dadaa7d2d3..e6b1895043108dc1b36b504431da068ac7b302de 100644 --- a/src/kit/taosdemo/insert.json +++ b/src/kit/taosdemo/insert.json @@ -41,8 +41,7 @@ "insert_mode": "taosc", "insert_rows": 100000, "multi_thread_write_one_tbl": "no", - "number_of_tbl_in_one_sql": 0, - "rows_per_tbl": 100, + "rows_per_tbl": 0, "max_sql_len": 1024000, "disorder_ratio": 0, "disorder_range": 1000, diff --git a/src/kit/taosdemo/taosdemo.c b/src/kit/taosdemo/taosdemo.c index ce88ba5befd2bf6641c102fa32a245a58b796fed..65341966030d6ec9c0a07a30af8aa6381c378b45 100644 --- a/src/kit/taosdemo/taosdemo.c +++ b/src/kit/taosdemo/taosdemo.c @@ -23,7 +23,6 @@ #ifdef LINUX #include - #include #include #ifndef _ALPINE #include @@ -39,11 +38,11 @@ #include #include #else - #include #include #include #endif +#include #include #include "cJSON.h" @@ -61,9 +60,12 @@ extern char configDir[]; #define QUERY_JSON_NAME "query.json" #define SUBSCRIBE_JSON_NAME "subscribe.json" -#define INSERT_MODE 0 -#define QUERY_MODE 1 -#define SUBSCRIBE_MODE 2 +enum TEST_MODE { + INSERT_TEST, // 0 + QUERY_TEST, // 1 + SUBSCRIBE_TEST, // 2 + INVAID_TEST +}; #define MAX_SQL_SIZE 65536 #define BUFFER_SIZE (65536*2) @@ -90,7 +92,7 @@ extern char configDir[]; #define MAX_DATABASE_COUNT 256 #define INPUT_BUF_LEN 256 -#define DEFAULT_TIMESTAMP_STEP 10 +#define DEFAULT_TIMESTAMP_STEP 1 typedef enum CREATE_SUB_TALBE_MOD_EN { PRE_CREATE_SUBTBL, @@ -99,8 +101,8 @@ typedef enum CREATE_SUB_TALBE_MOD_EN { } CREATE_SUB_TALBE_MOD_EN; typedef enum TALBE_EXISTS_EN { - TBL_ALREADY_EXISTS, TBL_NO_EXISTS, + TBL_ALREADY_EXISTS, TBL_EXISTS_BUTT } TALBE_EXISTS_EN; @@ -110,6 +112,12 @@ enum MODE { MODE_BUT }; +typedef enum enum_INSERT_MODE { + PROGRESSIVE_INSERT_MODE, + INTERLACE_INSERT_MODE, + INVALID_INSERT_MODE +} INSERT_MODE; + enum QUERY_TYPE { NO_INSERT_TYPE, INSERT_TYPE, @@ -149,7 +157,8 @@ enum _show_stables_index { TSDB_SHOW_STABLES_TID_INDEX, TSDB_SHOW_STABLES_VGID_INDEX, TSDB_MAX_SHOW_STABLES -}; +}; + enum _describe_table_index { TSDB_DESCRIBE_METRIC_FIELD_INDEX, TSDB_DESCRIBE_METRIC_TYPE_INDEX, @@ -182,6 +191,7 @@ typedef struct SArguments_S { bool answer_yes; bool debug_print; bool verbose_print; + bool performance_print; char * output_file; int mode; char * datatype[MAX_NUM_DATATYPE + 1]; @@ -189,6 +199,7 @@ typedef struct SArguments_S { int num_of_CPR; int num_of_threads; int insert_interval; + int rows_per_tbl; int num_of_RPR; int max_sql_len; int num_of_tables; @@ -198,6 +209,8 @@ typedef struct SArguments_S { int disorderRange; int method_of_delete; char ** arg_list; + int64_t totalInsertRows; + int64_t totalAffectedRows; } SArguments; typedef struct SColumn_S { @@ -221,7 +234,6 @@ typedef struct SSuperTable_S { int childTblOffset; int multiThreadWriteOneTbl; // 0: no, 1: yes - int numberOfTblInOneSql; // 0/1: one table, > 1: number of tbl int rowsPerTbl; // int disorderRatio; // 0: no disorder, >0: x% int disorderRange; // ms or us by database precision @@ -381,8 +393,9 @@ typedef struct SThreadInfo_S { char db_name[MAX_DB_NAME_SIZE+1]; char fp[4096]; char tb_prefix[MAX_TB_NAME_SIZE]; - int start_table_id; - int end_table_id; + int start_table_from; + int end_table_to; + int ntables; int data_of_rate; uint64_t start_time; char* cols; @@ -396,6 +409,8 @@ typedef struct SThreadInfo_S { uint64_t et; int64_t lastTs; + // sample data + int samplePos; // statistics int64_t totalInsertRows; int64_t totalAffectedRows; @@ -426,7 +441,7 @@ typedef unsigned __int32 uint32_t; static HANDLE g_stdoutHandle; static DWORD g_consoleMode; -void setupForAnsiEscape(void) { +static void setupForAnsiEscape(void) { DWORD mode = 0; g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); @@ -448,7 +463,7 @@ void setupForAnsiEscape(void) { } } -void resetAfterAnsiEscape(void) { +static void resetAfterAnsiEscape(void) { // Reset colors printf("\x1b[0m"); @@ -458,7 +473,7 @@ void resetAfterAnsiEscape(void) { } } -int taosRandom() +static int taosRandom() { int number; rand_s(&number); @@ -466,14 +481,14 @@ int taosRandom() return number; } #else -void setupForAnsiEscape(void) {} +static void setupForAnsiEscape(void) {} -void resetAfterAnsiEscape(void) { +static void resetAfterAnsiEscape(void) { // Reset colors printf("\x1b[0m"); } -int taosRandom() +static int taosRandom() { return random(); } @@ -508,10 +523,11 @@ SArguments g_args = { 1, // replica "t", // tb_prefix NULL, // sqlFile - false, // use_metric - false, // insert_only + true, // use_metric + true, // insert_only false, // debug_print false, // verbose_print + false, // performance statistic print false, // answer_yes; "./output.txt", // output_file 0, // mode : sync or async @@ -531,6 +547,7 @@ SArguments g_args = { 10, // num_of_CPR 10, // num_of_connections/thread 0, // insert_interval + 0, // rows_per_tbl; 100, // num_of_RPR TSDB_PAYLOAD_SIZE, // max_sql_len 10000, // num_of_tables @@ -552,12 +569,24 @@ static FILE * g_fpOfInsertResult = NULL; #define debugPrint(fmt, ...) \ do { if (g_args.debug_print || g_args.verbose_print) \ fprintf(stderr, "DEBG: "fmt, __VA_ARGS__); } while(0) + #define verbosePrint(fmt, ...) \ - do { if (g_args.verbose_print) fprintf(stderr, "VERB: "fmt, __VA_ARGS__); } while(0) + do { if (g_args.verbose_print) \ + fprintf(stderr, "VERB: "fmt, __VA_ARGS__); } while(0) + +#define performancePrint(fmt, ...) \ + do { if (g_args.performance_print) \ + fprintf(stderr, "VERB: "fmt, __VA_ARGS__); } while(0) + +#define errorPrint(fmt, ...) \ + do { fprintf(stderr, "ERROR: "fmt, __VA_ARGS__); } while(0) + /////////////////////////////////////////////////// -void printHelp() { +static void ERROR_EXIT(const char *msg) { perror(msg); exit(-1); } + +static void printHelp() { char indent[10] = " "; printf("%s%s%s%s\n", indent, "-f", indent, "The meta file to the execution procedure. Default is './meta.json'."); @@ -585,7 +614,7 @@ void printHelp() { printf("%s%s%s%s\n", indent, "-m", indent, "Table prefix name. Default is 't'."); printf("%s%s%s%s\n", indent, "-s", indent, "The select sql file."); - printf("%s%s%s%s\n", indent, "-M", indent, "Use metric flag."); + printf("%s%s%s%s\n", indent, "-N", indent, "Use normal table flag."); printf("%s%s%s%s\n", indent, "-o", indent, "Direct output to the named file. Default is './output.txt'."); printf("%s%s%s%s\n", indent, "-q", indent, @@ -619,7 +648,7 @@ void printHelp() { */ } -void parse_args(int argc, char *argv[], SArguments *arguments) { +static void parse_args(int argc, char *argv[], SArguments *arguments) { char **sptr; wordexp_t full_path; @@ -629,7 +658,7 @@ void parse_args(int argc, char *argv[], SArguments *arguments) { } else if (strcmp(argv[i], "-c") == 0) { char *configPath = argv[++i]; if (wordexp(configPath, &full_path, 0) != 0) { - fprintf(stderr, "Invalid path %s\n", configPath); + errorPrint( "Invalid path %s\n", configPath); return; } taos_options(TSDB_OPTION_CONFIGDIR, full_path.we_wordv[0]); @@ -652,6 +681,8 @@ void parse_args(int argc, char *argv[], SArguments *arguments) { arguments->num_of_threads = atoi(argv[++i]); } else if (strcmp(argv[i], "-i") == 0) { arguments->insert_interval = atoi(argv[++i]); + } else if (strcmp(argv[i], "-B") == 0) { + arguments->rows_per_tbl = atoi(argv[++i]); } else if (strcmp(argv[i], "-r") == 0) { arguments->num_of_RPR = atoi(argv[++i]); } else if (strcmp(argv[i], "-t") == 0) { @@ -676,8 +707,8 @@ void parse_args(int argc, char *argv[], SArguments *arguments) { && strcasecmp(argv[i], "DOUBLE") && strcasecmp(argv[i], "BINARY") && strcasecmp(argv[i], "NCHAR")) { - fprintf(stderr, "Invalid data_type!\n"); printHelp(); + ERROR_EXIT( "Invalid data_type!\n"); exit(EXIT_FAILURE); } sptr[0] = argv[i]; @@ -697,8 +728,8 @@ void parse_args(int argc, char *argv[], SArguments *arguments) { && strcasecmp(token, "DOUBLE") && strcasecmp(token, "BINARY") && strcasecmp(token, "NCHAR")) { - fprintf(stderr, "Invalid data_type!\n"); printHelp(); + ERROR_EXIT("Invalid data_type!\n"); exit(EXIT_FAILURE); } sptr[index++] = token; @@ -711,16 +742,18 @@ void parse_args(int argc, char *argv[], SArguments *arguments) { arguments->len_of_binary = atoi(argv[++i]); } else if (strcmp(argv[i], "-m") == 0) { arguments->tb_prefix = argv[++i]; - } else if (strcmp(argv[i], "-M") == 0) { - arguments->use_metric = true; + } else if (strcmp(argv[i], "-N") == 0) { + arguments->use_metric = false; } else if (strcmp(argv[i], "-x") == 0) { - arguments->insert_only = true; + arguments->insert_only = false; } else if (strcmp(argv[i], "-y") == 0) { arguments->answer_yes = true; } else if (strcmp(argv[i], "-g") == 0) { arguments->debug_print = true; } else if (strcmp(argv[i], "-gg") == 0) { arguments->verbose_print = true; + } else if (strcmp(argv[i], "-pp") == 0) { + arguments->performance_print = true; } else if (strcmp(argv[i], "-c") == 0) { strcpy(configDir, argv[++i]); } else if (strcmp(argv[i], "-O") == 0) { @@ -753,8 +786,8 @@ void parse_args(int argc, char *argv[], SArguments *arguments) { printHelp(); exit(0); } else { - fprintf(stderr, "wrong options\n"); printHelp(); + ERROR_EXIT("ERROR: wrong options\n"); exit(EXIT_FAILURE); } } @@ -808,13 +841,13 @@ static bool getInfoFromJsonFile(char* file); //static int generateOneRowDataForStb(SSuperTable* stbInfo); //static int getDataIntoMemForStb(SSuperTable* stbInfo); static void init_rand_data(); -void tmfclose(FILE *fp) { +static void tmfclose(FILE *fp) { if (NULL != fp) { fclose(fp); } } -void tmfree(char *buf) { +static void tmfree(char *buf) { if (NULL != buf) { free(buf); } @@ -840,7 +873,7 @@ static int queryDbExec(TAOS *taos, char *command, int type) { if (code != 0) { debugPrint("%s() LN%d - command: %s\n", __func__, __LINE__, command); - fprintf(stderr, "Failed to run %s, reason: %s\n", command, taos_errstr(res)); + errorPrint( "Failed to run %s, reason: %s\n", command, taos_errstr(res)); taos_free_result(res); //taos_close(taos); return -1; @@ -866,13 +899,13 @@ static void getResult(TAOS_RES *res, char* resultFileName) { if (resultFileName[0] != 0) { fp = fopen(resultFileName, "at"); if (fp == NULL) { - fprintf(stderr, "failed to open result file: %s, result will not save to file\n", resultFileName); + errorPrint("%s() LN%d, failed to open result file: %s, result will not save to file\n", __func__, __LINE__, resultFileName); } } char* databuf = (char*) calloc(1, 100*1024*1024); if (databuf == NULL) { - fprintf(stderr, "failed to malloc, warning: save result to file slowly!\n"); + errorPrint("%s() LN%d, failed to malloc, warning: save result to file slowly!\n", __func__, __LINE__); if (fp) fclose(fp); return ; @@ -913,7 +946,7 @@ static void selectAndGetResult(TAOS *taos, char *command, char* resultFileName) taos_free_result(res); } -double getCurrentTime() { +static double getCurrentTime() { struct timeval tv; if (gettimeofday(&tv, NULL) != 0) { perror("Failed to get current time in ms"); @@ -967,12 +1000,12 @@ static float rand_float(){ } static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; -void rand_string(char *str, int size) { +static void rand_string(char *str, int size) { str[0] = 0; if (size > 0) { //--size; int n; - for (n = 0; n < size; n++) { + for (n = 0; n < size - 1; n++) { int key = rand_tinyint() % (int)(sizeof(charset) - 1); str[n] = charset[key]; } @@ -1126,8 +1159,6 @@ static int printfInsertMeta() { }else { printf(" multiThreadWriteOneTbl: \033[33myes\033[0m\n"); } - printf(" numberOfTblInOneSql: \033[33m%d\033[0m\n", - g_Dbs.db[i].superTbls[j].numberOfTblInOneSql); printf(" rowsPerTbl: \033[33m%d\033[0m\n", g_Dbs.db[i].superTbls[j].rowsPerTbl); printf(" disorderRange: \033[33m%d\033[0m\n", @@ -1287,7 +1318,6 @@ static void printfInsertMetaToFile(FILE* fp) { }else { fprintf(fp, " multiThreadWriteOneTbl: yes\n"); } - fprintf(fp, " numberOfTblInOneSql: %d\n", g_Dbs.db[i].superTbls[j].numberOfTblInOneSql); fprintf(fp, " rowsPerTbl: %d\n", g_Dbs.db[i].superTbls[j].rowsPerTbl); fprintf(fp, " disorderRange: %d\n", g_Dbs.db[i].superTbls[j].disorderRange); fprintf(fp, " disorderRatio: %d\n", g_Dbs.db[i].superTbls[j].disorderRatio); @@ -1302,33 +1332,45 @@ static void printfInsertMetaToFile(FILE* fp) { fprintf(fp, " columnCount: %d\n ", g_Dbs.db[i].superTbls[j].columnCount); for (int k = 0; k < g_Dbs.db[i].superTbls[j].columnCount; k++) { //printf("dataType:%s, dataLen:%d\t", g_Dbs.db[i].superTbls[j].columns[k].dataType, g_Dbs.db[i].superTbls[j].columns[k].dataLen); - if ((0 == strncasecmp(g_Dbs.db[i].superTbls[j].columns[k].dataType, "binary", 6)) || (0 == strncasecmp(g_Dbs.db[i].superTbls[j].columns[k].dataType, "nchar", 5))) { - fprintf(fp, "column[%d]:%s(%d) ", k, g_Dbs.db[i].superTbls[j].columns[k].dataType, g_Dbs.db[i].superTbls[j].columns[k].dataLen); + if ((0 == strncasecmp( + g_Dbs.db[i].superTbls[j].columns[k].dataType, + "binary", strlen("binary"))) + || (0 == strncasecmp(g_Dbs.db[i].superTbls[j].columns[k].dataType, + "nchar", strlen("nchar")))) { + fprintf(fp, "column[%d]:%s(%d) ", k, + g_Dbs.db[i].superTbls[j].columns[k].dataType, + g_Dbs.db[i].superTbls[j].columns[k].dataLen); } else { fprintf(fp, "column[%d]:%s ", k, g_Dbs.db[i].superTbls[j].columns[k].dataType); } } fprintf(fp, "\n"); - - fprintf(fp, " tagCount: %d\n ", g_Dbs.db[i].superTbls[j].tagCount); + + fprintf(fp, " tagCount: %d\n ", + g_Dbs.db[i].superTbls[j].tagCount); for (int k = 0; k < g_Dbs.db[i].superTbls[j].tagCount; k++) { //printf("dataType:%s, dataLen:%d\t", g_Dbs.db[i].superTbls[j].tags[k].dataType, g_Dbs.db[i].superTbls[j].tags[k].dataLen); - if ((0 == strncasecmp(g_Dbs.db[i].superTbls[j].tags[k].dataType, "binary", 6)) || (0 == strncasecmp(g_Dbs.db[i].superTbls[j].tags[k].dataType, "nchar", 5))) { - fprintf(fp, "tag[%d]:%s(%d) ", k, g_Dbs.db[i].superTbls[j].tags[k].dataType, g_Dbs.db[i].superTbls[j].tags[k].dataLen); + if ((0 == strncasecmp(g_Dbs.db[i].superTbls[j].tags[k].dataType, + "binary", strlen("binary"))) + || (0 == strncasecmp(g_Dbs.db[i].superTbls[j].tags[k].dataType, + "nchar", strlen("nchar")))) { + fprintf(fp, "tag[%d]:%s(%d) ", k, g_Dbs.db[i].superTbls[j].tags[k].dataType, + g_Dbs.db[i].superTbls[j].tags[k].dataLen); } else { fprintf(fp, "tag[%d]:%s ", k, g_Dbs.db[i].superTbls[j].tags[k].dataType); - } + } } fprintf(fp, "\n"); } fprintf(fp, "\n"); } - SHOW_PARSE_RESULT_END_TO_FILE(fp); + SHOW_PARSE_RESULT_END_TO_FILE(fp); } static void printfQueryMeta() { - SHOW_PARSE_RESULT_START(); - printf("host: \033[33m%s:%u\033[0m\n", g_queryInfo.host, g_queryInfo.port); + SHOW_PARSE_RESULT_START(); + printf("host: \033[33m%s:%u\033[0m\n", + g_queryInfo.host, g_queryInfo.port); printf("user: \033[33m%s\033[0m\n", g_queryInfo.user); printf("password: \033[33m%s\033[0m\n", g_queryInfo.password); printf("database name: \033[33m%s\033[0m\n", g_queryInfo.dbName); @@ -1339,7 +1381,7 @@ static void printfQueryMeta() { printf("concurrent: \033[33m%d\033[0m\n", g_queryInfo.superQueryInfo.concurrent); printf("sqlCount: \033[33m%d\033[0m\n", g_queryInfo.superQueryInfo.sqlCount); - if (SUBSCRIBE_MODE == g_args.test_mode) { + if (SUBSCRIBE_TEST == g_args.test_mode) { printf("mod: \033[33m%d\033[0m\n", g_queryInfo.superQueryInfo.subscribeMode); printf("interval: \033[33m%d\033[0m\n", g_queryInfo.superQueryInfo.subscribeInterval); printf("restart: \033[33m%d\033[0m\n", g_queryInfo.superQueryInfo.subscribeRestart); @@ -1356,7 +1398,7 @@ static void printfQueryMeta() { printf("childTblCount: \033[33m%d\033[0m\n", g_queryInfo.subQueryInfo.childTblCount); printf("stable name: \033[33m%s\033[0m\n", g_queryInfo.subQueryInfo.sTblName); - if (SUBSCRIBE_MODE == g_args.test_mode) { + if (SUBSCRIBE_TEST == g_args.test_mode) { printf("mod: \033[33m%d\033[0m\n", g_queryInfo.subQueryInfo.subscribeMode); printf("interval: \033[33m%d\033[0m\n", g_queryInfo.subQueryInfo.subscribeInterval); printf("restart: \033[33m%d\033[0m\n", g_queryInfo.subQueryInfo.subscribeRestart); @@ -1457,7 +1499,7 @@ static int xDumpResultToFile(const char* fname, TAOS_RES* tres) { FILE* fp = fopen(fname, "at"); if (fp == NULL) { - fprintf(stderr, "ERROR: failed to open file: %s\n", fname); + errorPrint("%s() LN%d, failed to open file: %s\n", __func__, __LINE__, fname); return -1; } @@ -1502,7 +1544,7 @@ static int getDbFromServer(TAOS * taos, SDbInfo** dbInfos) { int32_t code = taos_errno(res); if (code != 0) { - fprintf(stderr, "failed to run , reason: %s\n", taos_errstr(res)); + errorPrint( "failed to run , reason: %s\n", taos_errstr(res)); return -1; } @@ -1514,19 +1556,23 @@ static int getDbFromServer(TAOS * taos, SDbInfo** dbInfos) { dbInfos[count] = (SDbInfo *)calloc(1, sizeof(SDbInfo)); if (dbInfos[count] == NULL) { - fprintf(stderr, "failed to allocate memory for some dbInfo[%d]\n", count); + errorPrint( "failed to allocate memory for some dbInfo[%d]\n", count); return -1; } - tstrncpy(dbInfos[count]->name, (char *)row[TSDB_SHOW_DB_NAME_INDEX], fields[TSDB_SHOW_DB_NAME_INDEX].bytes); - xFormatTimestamp(dbInfos[count]->create_time, *(int64_t*)row[TSDB_SHOW_DB_CREATED_TIME_INDEX], TSDB_TIME_PRECISION_MILLI); + tstrncpy(dbInfos[count]->name, (char *)row[TSDB_SHOW_DB_NAME_INDEX], + fields[TSDB_SHOW_DB_NAME_INDEX].bytes); + xFormatTimestamp(dbInfos[count]->create_time, + *(int64_t*)row[TSDB_SHOW_DB_CREATED_TIME_INDEX], + TSDB_TIME_PRECISION_MILLI); dbInfos[count]->ntables = *((int32_t *)row[TSDB_SHOW_DB_NTABLES_INDEX]); dbInfos[count]->vgroups = *((int32_t *)row[TSDB_SHOW_DB_VGROUPS_INDEX]); dbInfos[count]->replica = *((int16_t *)row[TSDB_SHOW_DB_REPLICA_INDEX]); dbInfos[count]->quorum = *((int16_t *)row[TSDB_SHOW_DB_QUORUM_INDEX]); dbInfos[count]->days = *((int16_t *)row[TSDB_SHOW_DB_DAYS_INDEX]); - tstrncpy(dbInfos[count]->keeplist, (char *)row[TSDB_SHOW_DB_KEEP_INDEX], fields[TSDB_SHOW_DB_KEEP_INDEX].bytes); + tstrncpy(dbInfos[count]->keeplist, (char *)row[TSDB_SHOW_DB_KEEP_INDEX], + fields[TSDB_SHOW_DB_KEEP_INDEX].bytes); dbInfos[count]->cache = *((int32_t *)row[TSDB_SHOW_DB_CACHE_INDEX]); dbInfos[count]->blocks = *((int32_t *)row[TSDB_SHOW_DB_BLOCKS_INDEX]); dbInfos[count]->minrows = *((int32_t *)row[TSDB_SHOW_DB_MINROWS_INDEX]); @@ -1540,11 +1586,12 @@ static int getDbFromServer(TAOS * taos, SDbInfo** dbInfos) { (char *)row[TSDB_SHOW_DB_PRECISION_INDEX], fields[TSDB_SHOW_DB_PRECISION_INDEX].bytes); dbInfos[count]->update = *((int8_t *)row[TSDB_SHOW_DB_UPDATE_INDEX]); - tstrncpy(dbInfos[count]->status, (char *)row[TSDB_SHOW_DB_STATUS_INDEX], fields[TSDB_SHOW_DB_STATUS_INDEX].bytes); - + tstrncpy(dbInfos[count]->status, (char *)row[TSDB_SHOW_DB_STATUS_INDEX], + fields[TSDB_SHOW_DB_STATUS_INDEX].bytes); + count++; if (count > MAX_DATABASE_COUNT) { - fprintf(stderr, "The database count overflow than %d\n", MAX_DATABASE_COUNT); + errorPrint( "The database count overflow than %d\n", MAX_DATABASE_COUNT); break; } } @@ -1558,7 +1605,7 @@ static void printfDbInfoForQueryToFile(char* filename, SDbInfo* dbInfos, int ind FILE *fp = fopen(filename, "at"); if (fp == NULL) { - fprintf(stderr, "failed to open file: %s\n", filename); + errorPrint( "failed to open file: %s\n", filename); return; } @@ -1596,8 +1643,10 @@ static void printfQuerySystemInfo(TAOS * taos) { struct tm* lt; time(&t); lt = localtime(&t); - snprintf(filename, MAX_QUERY_SQL_LENGTH, "querySystemInfo-%d-%d-%d %d:%d:%d", lt->tm_year+1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); - + snprintf(filename, MAX_QUERY_SQL_LENGTH, "querySystemInfo-%d-%d-%d %d:%d:%d", + lt->tm_year+1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min, + lt->tm_sec); + // show variables res = taos_query(taos, "show variables;"); //getResult(res, filename); @@ -1607,12 +1656,12 @@ static void printfQuerySystemInfo(TAOS * taos) { res = taos_query(taos, "show dnodes;"); xDumpResultToFile(filename, res); //getResult(res, filename); - + // show databases res = taos_query(taos, "show databases;"); SDbInfo** dbInfos = (SDbInfo **)calloc(MAX_DATABASE_COUNT, sizeof(SDbInfo *)); if (dbInfos == NULL) { - fprintf(stderr, "failed to allocate memory\n"); + errorPrint("%s() LN%d, failed to allocate memory\n", __func__, __LINE__); return; } int dbCount = getDbFromServer(taos, dbInfos); @@ -1624,7 +1673,7 @@ static void printfQuerySystemInfo(TAOS * taos) { for (int i = 0; i < dbCount; i++) { // printf database info printfDbInfoForQueryToFile(filename, dbInfos[i], i); - + // show db.vgroups snprintf(buffer, MAX_QUERY_SQL_LENGTH, "show %s.vgroups;", dbInfos[i]->name); res = taos_query(taos, buffer); @@ -1642,9 +1691,7 @@ static void printfQuerySystemInfo(TAOS * taos) { } -void ERROR_EXIT(const char *msg) { perror(msg); exit(-1); } - -int postProceSql(char* host, uint16_t port, char* sqlstr) +static int postProceSql(char* host, uint16_t port, char* sqlstr) { char *req_fmt = "POST %s HTTP/1.1\r\nHost: %s:%d\r\nAccept: */*\r\nAuthorization: Basic %s\r\nContent-Length: %d\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n%s"; @@ -1691,9 +1738,9 @@ int postProceSql(char* host, uint16_t port, char* sqlstr) sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { #ifdef WINDOWS - fprintf(stderr, "Could not create socket : %d" , WSAGetLastError()); + errorPrint( "Could not create socket : %d" , WSAGetLastError()); #endif - debugPrint("%s() LN%d sockfd=%d\n", __func__, __LINE__, sockfd); + debugPrint("%s() LN%d, sockfd=%d\n", __func__, __LINE__, sockfd); free(request_buf); ERROR_EXIT("ERROR opening socket"); } @@ -1813,7 +1860,7 @@ int postProceSql(char* host, uint16_t port, char* sqlstr) static char* getTagValueFromTagSample(SSuperTable* stbInfo, int tagUsePos) { char* dataBuf = (char*)calloc(TSDB_MAX_SQL_LEN+1, 1); if (NULL == dataBuf) { - printf("calloc failed! size:%d\n", TSDB_MAX_SQL_LEN+1); + errorPrint("%s() LN%d, calloc failed! size:%d\n", __func__, __LINE__, TSDB_MAX_SQL_LEN+1); return NULL; } @@ -2005,7 +2052,8 @@ static int getChildNameOfSuperTableWithLimitAndOffset(TAOS * taos, //printf("==== sub table name: %s\n", pTblName); count++; if (count >= childTblCount - 1) { - char *tmp = realloc(childTblName, (size_t)childTblCount*1.5*TSDB_TABLE_NAME_LEN+1); + char *tmp = realloc(childTblName, + (size_t)childTblCount*1.5*TSDB_TABLE_NAME_LEN+1); if (tmp != NULL) { childTblName = tmp; childTblCount = (int)(childTblCount*1.5); @@ -2013,14 +2061,15 @@ static int getChildNameOfSuperTableWithLimitAndOffset(TAOS * taos, (size_t)((childTblCount-count)*TSDB_TABLE_NAME_LEN)); } else { // exit, if allocate more memory failed - printf("realloc fail for save child table name of %s.%s\n", dbName, sTblName); + errorPrint("%s() LN%d, realloc fail for save child table name of %s.%s\n", + __func__, __LINE__, dbName, sTblName); tmfree(childTblName); taos_free_result(res); taos_close(taos); exit(-1); } } - pTblName = childTblName + count * TSDB_TABLE_NAME_LEN; + pTblName = childTblName + count * TSDB_TABLE_NAME_LEN; } *childTblCountOfSuperTbl = count; @@ -2101,12 +2150,13 @@ static int getSuperTableFromServer(TAOS * taos, char* dbName, calcRowLen(superTbls); +/* if (TBL_ALREADY_EXISTS == superTbls->childTblExists) { //get all child table name use cmd: select tbname from superTblName; int childTblCount = 10000; superTbls->childTblName = (char*)calloc(1, childTblCount * TSDB_TABLE_NAME_LEN); if (superTbls->childTblName == NULL) { - fprintf(stderr, "alloc memory failed!"); + errorPrint("%s() LN%d, alloc memory failed!\n", __func__, __LINE__); return -1; } getAllChildNameOfSuperTable(taos, dbName, @@ -2114,6 +2164,7 @@ static int getSuperTableFromServer(TAOS * taos, char* dbName, &superTbls->childTblName, &superTbls->childTblCount); } + */ return 0; } @@ -2245,7 +2296,7 @@ static int createSuperTable(TAOS * taos, char* dbName, SSuperTable* superTbls, verbosePrint("%s() LN%d: %s\n", __func__, __LINE__, command); if (0 != queryDbExec(taos, command, NO_INSERT_TYPE)) { - fprintf(stderr, "create supertable %s failed!\n\n", + errorPrint( "create supertable %s failed!\n\n", superTbls->sTblName); return -1; } @@ -2259,7 +2310,7 @@ static int createDatabases() { int ret = 0; taos = taos_connect(g_Dbs.host, g_Dbs.user, g_Dbs.password, NULL, g_Dbs.port); if (taos == NULL) { - fprintf(stderr, "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); + errorPrint( "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); return -1; } char command[BUFFER_SIZE] = "\0"; @@ -2335,7 +2386,8 @@ static int createDatabases() { " fsync %d", g_Dbs.db[i].dbCfg.fsync); } if ((0 == strncasecmp(g_Dbs.db[i].dbCfg.precision, "ms", strlen("ms"))) - || (0 == strncasecmp(g_Dbs.db[i].dbCfg.precision, "us", strlen("us")))) { + || (0 == strncasecmp(g_Dbs.db[i].dbCfg.precision, + "us", strlen("us")))) { dataLen += snprintf(command + dataLen, BUFFER_SIZE - dataLen, " precision \'%s\';", g_Dbs.db[i].dbCfg.precision); } @@ -2343,22 +2395,29 @@ static int createDatabases() { debugPrint("%s() %d command: %s\n", __func__, __LINE__, command); if (0 != queryDbExec(taos, command, NO_INSERT_TYPE)) { taos_close(taos); - fprintf(stderr, "\ncreate database %s failed!\n\n", g_Dbs.db[i].dbName); + errorPrint( "\ncreate database %s failed!\n\n", g_Dbs.db[i].dbName); return -1; } printf("\ncreate database %s success!\n\n", g_Dbs.db[i].dbName); - debugPrint("%s() %d supertbl count:%d\n", __func__, __LINE__, g_Dbs.db[i].superTblCount); + debugPrint("%s() %d supertbl count:%d\n", + __func__, __LINE__, g_Dbs.db[i].superTblCount); for (int j = 0; j < g_Dbs.db[i].superTblCount; j++) { // describe super table, if exists - sprintf(command, "describe %s.%s;", g_Dbs.db[i].dbName, g_Dbs.db[i].superTbls[j].sTblName); + sprintf(command, "describe %s.%s;", g_Dbs.db[i].dbName, + g_Dbs.db[i].superTbls[j].sTblName); verbosePrint("%s() %d command: %s\n", __func__, __LINE__, command); if (0 != queryDbExec(taos, command, NO_INSERT_TYPE)) { g_Dbs.db[i].superTbls[j].superTblExists = TBL_NO_EXISTS; - ret = createSuperTable(taos, g_Dbs.db[i].dbName, &g_Dbs.db[i].superTbls[j], g_Dbs.use_metric); + ret = createSuperTable(taos, g_Dbs.db[i].dbName, + &g_Dbs.db[i].superTbls[j], g_Dbs.use_metric); } else { g_Dbs.db[i].superTbls[j].superTblExists = TBL_ALREADY_EXISTS; - ret = getSuperTableFromServer(taos, g_Dbs.db[i].dbName, &g_Dbs.db[i].superTbls[j]); + + if (g_Dbs.db[i].superTbls[j].childTblExists != TBL_ALREADY_EXISTS) { + ret = getSuperTableFromServer(taos, g_Dbs.db[i].dbName, + &g_Dbs.db[i].superTbls[j]); + } } if (0 != ret) { @@ -2388,14 +2447,18 @@ static void* createTable(void *sarg) char *buffer = calloc(buff_len, 1); if (buffer == NULL) { - fprintf(stderr, "Memory allocated failed!"); + errorPrint("%s() LN%d, Memory allocated failed!\n", __func__, __LINE__); exit(-1); } int len = 0; int batchNum = 0; - //printf("Creating table from %d to %d\n", winfo->start_table_id, winfo->end_table_id); - for (int i = winfo->start_table_id; i <= winfo->end_table_id; i++) { + + verbosePrint("%s() LN%d: Creating table from %d to %d\n", + __func__, __LINE__, + winfo->start_table_from, winfo->end_table_to); + + for (int i = winfo->start_table_from; i <= winfo->end_table_to; i++) { if (0 == g_Dbs.use_metric) { snprintf(buffer, buff_len, "create table if not exists %s.%s%d %s;", @@ -2442,7 +2505,7 @@ static void* createTable(void *sarg) len = 0; verbosePrint("%s() LN%d %s\n", __func__, __LINE__, buffer); if (0 != queryDbExec(winfo->taos, buffer, NO_INSERT_TYPE)){ - fprintf(stderr, "queryDbExec() failed. buffer:\n%s\n", buffer); + errorPrint( "queryDbExec() failed. buffer:\n%s\n", buffer); free(buffer); return NULL; } @@ -2450,7 +2513,7 @@ static void* createTable(void *sarg) int64_t currentPrintTime = taosGetTimestampMs(); if (currentPrintTime - lastPrintTime > 30*1000) { printf("thread[%d] already create %d - %d tables\n", - winfo->threadID, winfo->start_table_id, i); + winfo->threadID, winfo->start_table_from, i); lastPrintTime = currentPrintTime; } } @@ -2458,7 +2521,7 @@ static void* createTable(void *sarg) if (0 != len) { verbosePrint("%s() %d buffer: %s\n", __func__, __LINE__, buffer); if (0 != queryDbExec(winfo->taos, buffer, NO_INSERT_TYPE)) { - fprintf(stderr, "queryDbExec() failed. buffer:\n%s\n", buffer); + errorPrint( "queryDbExec() failed. buffer:\n%s\n", buffer); } } @@ -2466,7 +2529,7 @@ static void* createTable(void *sarg) return NULL; } -int startMultiThreadCreateChildTable( +static int startMultiThreadCreateChildTable( char* cols, int threads, int startFrom, int ntables, char* db_name, SSuperTable* superTblInfo) { pthread_t *pids = malloc(threads * sizeof(pthread_t)); @@ -2490,7 +2553,6 @@ int startMultiThreadCreateChildTable( int b = 0; b = ntables % threads; - int last = startFrom; for (int i = 0; i < threads; i++) { threadInfo *t_info = infos + i; t_info->threadID = i; @@ -2504,20 +2566,22 @@ int startMultiThreadCreateChildTable( db_name, g_Dbs.port); if (t_info->taos == NULL) { - fprintf(stderr, "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); + errorPrint( "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); free(pids); free(infos); return -1; } - t_info->start_table_id = last; - t_info->end_table_id = i < b ? last + a : last + a - 1; - last = t_info->end_table_id + 1; - t_info->use_metric = 1; + + t_info->start_table_from = startFrom; + t_info->ntables = iend_table_to = i < b ? startFrom + a : startFrom + a - 1; + startFrom = t_info->end_table_to + 1; + t_info->use_metric = true; t_info->cols = cols; t_info->minDelay = INT16_MAX; pthread_create(pids + i, NULL, createTable, t_info); } - + for (int i = 0; i < threads; i++) { pthread_join(pids[i], NULL); } @@ -2528,12 +2592,11 @@ int startMultiThreadCreateChildTable( } free(pids); - free(infos); + free(infos); return 0; } - static void createChildTables() { char tblColsBuf[MAX_SQL_SIZE]; int len; @@ -2569,10 +2632,10 @@ static void createChildTables() { if ((strncasecmp(g_args.datatype[j], "BINARY", strlen("BINARY")) == 0) || (strncasecmp(g_args.datatype[j], "NCHAR", strlen("NCHAR")) == 0)) { - len = snprintf(tblColsBuf + len, MAX_SQL_SIZE, + len = snprintf(tblColsBuf + len, MAX_SQL_SIZE - len, ", COL%d %s(60)", j, g_args.datatype[j]); } else { - len = snprintf(tblColsBuf + len, MAX_SQL_SIZE, + len = snprintf(tblColsBuf + len, MAX_SQL_SIZE - len, ", COL%d %s", j, g_args.datatype[j]); } len = strlen(tblColsBuf); @@ -2598,7 +2661,7 @@ static void createChildTables() { /* Read 10000 lines at most. If more than 10000 lines, continue to read after using */ -int readTagFromCsvFileToMem(SSuperTable * superTblInfo) { +static int readTagFromCsvFileToMem(SSuperTable * superTblInfo) { size_t n = 0; ssize_t readLen = 0; char * line = NULL; @@ -2668,7 +2731,6 @@ int readSampleFromJsonFileToMem(SSuperTable * superTblInfo) { return 0; } - /* Read 10000 lines at most. If more than 10000 lines, continue to read after using */ @@ -2681,7 +2743,7 @@ static int readSampleFromCsvFileToMem( FILE* fp = fopen(superTblInfo->sampleFile, "r"); if (fp == NULL) { - fprintf(stderr, "Failed to open sample file: %s, reason:%s\n", + errorPrint( "Failed to open sample file: %s, reason:%s\n", superTblInfo->sampleFile, strerror(errno)); return -1; } @@ -2693,7 +2755,7 @@ static int readSampleFromCsvFileToMem( readLen = tgetline(&line, &n, fp); if (-1 == readLen) { if(0 != fseek(fp, 0, SEEK_SET)) { - fprintf(stderr, "Failed to fseek file: %s, reason:%s\n", + errorPrint( "Failed to fseek file: %s, reason:%s\n", superTblInfo->sampleFile, strerror(errno)); fclose(fp); return -1; @@ -2715,6 +2777,9 @@ static int readSampleFromCsvFileToMem( continue; } + verbosePrint("readLen=%ld stb->lenOfOneRow=%d getRows=%d\n", (long)readLen, + superTblInfo->lenOfOneRow, getRows); + memcpy(superTblInfo->sampleDataBuf + getRows * superTblInfo->lenOfOneRow, line, readLen); getRows++; @@ -2729,23 +2794,10 @@ static int readSampleFromCsvFileToMem( return 0; } -/* -void readSampleFromFileToMem(SSuperTable * supterTblInfo) { - int ret; - if (0 == strncasecmp(supterTblInfo->sampleFormat, "csv", 3)) { - ret = readSampleFromCsvFileToMem(supterTblInfo); - } else if (0 == strncasecmp(supterTblInfo->sampleFormat, "json", 4)) { - ret = readSampleFromJsonFileToMem(supterTblInfo); - } - - if (0 != ret) { - exit(-1); - } -} -*/ -static bool getColumnAndTagTypeFromInsertJsonFile(cJSON* stbInfo, SSuperTable* superTbls) { +static bool getColumnAndTagTypeFromInsertJsonFile( + cJSON* stbInfo, SSuperTable* superTbls) { bool ret = false; - + // columns cJSON *columns = cJSON_GetObjectItem(stbInfo, "columns"); if (columns && columns->type != cJSON_Array) { @@ -2756,11 +2808,11 @@ static bool getColumnAndTagTypeFromInsertJsonFile(cJSON* stbInfo, SSuperTable* s superTbls->tagCount = 0; return true; } - + int columnSize = cJSON_GetArraySize(columns); if (columnSize > MAX_COLUMN_COUNT) { - printf("ERROR: failed to read json, column size overflow, max column size is %d\n", - MAX_COLUMN_COUNT); + errorPrint("%s() LN%d, failed to read json, column size overflow, max column size is %d\n", + __func__, __LINE__, MAX_COLUMN_COUNT); goto PARSE_OVER; } @@ -2768,7 +2820,7 @@ static bool getColumnAndTagTypeFromInsertJsonFile(cJSON* stbInfo, SSuperTable* s int index = 0; StrColumn columnCase; - //superTbls->columnCount = columnSize; + //superTbls->columnCount = columnSize; for (int k = 0; k < columnSize; ++k) { cJSON* column = cJSON_GetArrayItem(columns, k); if (column == NULL) continue; @@ -2776,9 +2828,9 @@ static bool getColumnAndTagTypeFromInsertJsonFile(cJSON* stbInfo, SSuperTable* s count = 1; cJSON* countObj = cJSON_GetObjectItem(column, "count"); if (countObj && countObj->type == cJSON_Number) { - count = countObj->valueint; + count = countObj->valueint; } else if (countObj && countObj->type != cJSON_Number) { - printf("ERROR: failed to read json, column count not found\n"); + errorPrint("%s() LN%d, failed to read json, column count not found\n", __func__, __LINE__); goto PARSE_OVER; } else { count = 1; @@ -2788,54 +2840,55 @@ static bool getColumnAndTagTypeFromInsertJsonFile(cJSON* stbInfo, SSuperTable* s memset(&columnCase, 0, sizeof(StrColumn)); cJSON *dataType = cJSON_GetObjectItem(column, "type"); if (!dataType || dataType->type != cJSON_String || dataType->valuestring == NULL) { - printf("ERROR: failed to read json, column type not found\n"); + errorPrint("%s() LN%d: failed to read json, column type not found\n", __func__, __LINE__); goto PARSE_OVER; } //tstrncpy(superTbls->columns[k].dataType, dataType->valuestring, MAX_TB_NAME_SIZE); tstrncpy(columnCase.dataType, dataType->valuestring, MAX_TB_NAME_SIZE); - + cJSON* dataLen = cJSON_GetObjectItem(column, "len"); if (dataLen && dataLen->type == cJSON_Number) { - columnCase.dataLen = dataLen->valueint; + columnCase.dataLen = dataLen->valueint; } else if (dataLen && dataLen->type != cJSON_Number) { - printf("ERROR: failed to read json, column len not found\n"); + debugPrint("%s() LN%d: failed to read json, column len not found\n", __func__, __LINE__); goto PARSE_OVER; } else { columnCase.dataLen = 8; } - + for (int n = 0; n < count; ++n) { - tstrncpy(superTbls->columns[index].dataType, columnCase.dataType, MAX_TB_NAME_SIZE); - superTbls->columns[index].dataLen = columnCase.dataLen; + tstrncpy(superTbls->columns[index].dataType, + columnCase.dataType, MAX_TB_NAME_SIZE); + superTbls->columns[index].dataLen = columnCase.dataLen; index++; } - } - superTbls->columnCount = index; - + } + superTbls->columnCount = index; + count = 1; index = 0; // tags cJSON *tags = cJSON_GetObjectItem(stbInfo, "tags"); if (!tags || tags->type != cJSON_Array) { - printf("ERROR: failed to read json, tags not found\n"); + debugPrint("%s() LN%d, failed to read json, tags not found\n", __func__, __LINE__); goto PARSE_OVER; } int tagSize = cJSON_GetArraySize(tags); if (tagSize > MAX_TAG_COUNT) { - printf("ERROR: failed to read json, tags size overflow, max tag size is %d\n", MAX_TAG_COUNT); + debugPrint("%s() LN%d, failed to read json, tags size overflow, max tag size is %d\n", __func__, __LINE__, MAX_TAG_COUNT); goto PARSE_OVER; } - - //superTbls->tagCount = tagSize; + + //superTbls->tagCount = tagSize; for (int k = 0; k < tagSize; ++k) { cJSON* tag = cJSON_GetArrayItem(tags, k); if (tag == NULL) continue; - + count = 1; cJSON* countObj = cJSON_GetObjectItem(tag, "count"); if (countObj && countObj->type == cJSON_Number) { - count = countObj->valueint; + count = countObj->valueint; } else if (countObj && countObj->type != cJSON_Number) { printf("ERROR: failed to read json, column count not found\n"); goto PARSE_OVER; @@ -2851,23 +2904,23 @@ static bool getColumnAndTagTypeFromInsertJsonFile(cJSON* stbInfo, SSuperTable* s goto PARSE_OVER; } tstrncpy(columnCase.dataType, dataType->valuestring, MAX_TB_NAME_SIZE); - + cJSON* dataLen = cJSON_GetObjectItem(tag, "len"); if (dataLen && dataLen->type == cJSON_Number) { - columnCase.dataLen = dataLen->valueint; + columnCase.dataLen = dataLen->valueint; } else if (dataLen && dataLen->type != cJSON_Number) { printf("ERROR: failed to read json, column len not found\n"); goto PARSE_OVER; } else { columnCase.dataLen = 0; - } - + } + for (int n = 0; n < count; ++n) { tstrncpy(superTbls->tags[index].dataType, columnCase.dataType, MAX_TB_NAME_SIZE); - superTbls->tags[index].dataLen = columnCase.dataLen; + superTbls->tags[index].dataLen = columnCase.dataLen; index++; } - } + } superTbls->tagCount = index; ret = true; @@ -2945,37 +2998,47 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { goto PARSE_OVER; } - cJSON* gInsertInterval = cJSON_GetObjectItem(root, "insert_interval"); - if (gInsertInterval && gInsertInterval->type == cJSON_Number) { - g_args.insert_interval = gInsertInterval->valueint; - } else if (!gInsertInterval) { - g_args.insert_interval = 0; - } else { - fprintf(stderr, "ERROR: failed to read json, insert_interval input mistake\n"); - goto PARSE_OVER; - } - - cJSON* maxSqlLen = cJSON_GetObjectItem(root, "max_sql_len"); - if (maxSqlLen && maxSqlLen->type == cJSON_Number) { - g_args.max_sql_len = maxSqlLen->valueint; - } else if (!maxSqlLen) { - g_args.max_sql_len = TSDB_PAYLOAD_SIZE; - } else { - fprintf(stderr, "ERROR: failed to read json, max_sql_len input mistake\n"); - goto PARSE_OVER; - } - + cJSON* gInsertInterval = cJSON_GetObjectItem(root, "insert_interval"); + if (gInsertInterval && gInsertInterval->type == cJSON_Number) { + g_args.insert_interval = gInsertInterval->valueint; + } else if (!gInsertInterval) { + g_args.insert_interval = 0; + } else { + errorPrint("%s() LN%d, failed to read json, insert_interval input mistake\n", __func__, __LINE__); + goto PARSE_OVER; + } - cJSON* numRecPerReq = cJSON_GetObjectItem(root, "num_of_records_per_req"); - if (numRecPerReq && numRecPerReq->type == cJSON_Number) { - g_args.num_of_RPR = numRecPerReq->valueint; - } else if (!numRecPerReq) { - g_args.num_of_RPR = 100; - } else { - printf("ERROR: failed to read json, num_of_records_per_req not found\n"); - goto PARSE_OVER; - } + cJSON* rowsPerTbl = cJSON_GetObjectItem(root, "rows_per_tbl"); + if (rowsPerTbl && rowsPerTbl->type == cJSON_Number) { + g_args.rows_per_tbl = rowsPerTbl->valueint; + } else if (!rowsPerTbl) { + g_args.rows_per_tbl = 0; // 0 means progressive mode, > 0 mean interlace mode. max value is less or equ num_of_records_per_req + } else { + errorPrint("%s() LN%d, failed to read json, rows_per_tbl input mistake\n", __func__, __LINE__); + goto PARSE_OVER; + } + + cJSON* maxSqlLen = cJSON_GetObjectItem(root, "max_sql_len"); + if (maxSqlLen && maxSqlLen->type == cJSON_Number) { + g_args.max_sql_len = maxSqlLen->valueint; + } else if (!maxSqlLen) { + g_args.max_sql_len = TSDB_PAYLOAD_SIZE; + } else { + errorPrint("%s() LN%d, failed to read json, max_sql_len input mistake\n", __func__, __LINE__); + goto PARSE_OVER; + } + + cJSON* numRecPerReq = cJSON_GetObjectItem(root, "num_of_records_per_req"); + if (numRecPerReq && numRecPerReq->type == cJSON_Number) { + g_args.num_of_RPR = numRecPerReq->valueint; + } else if (!numRecPerReq) { + g_args.num_of_RPR = 100; + } else { + errorPrint("%s() LN%d, failed to read json, num_of_records_per_req not found\n", __func__, __LINE__); + goto PARSE_OVER; + } + cJSON *answerPrompt = cJSON_GetObjectItem(root, "confirm_parameter_prompt"); // yes, no, if (answerPrompt && answerPrompt->type == cJSON_String @@ -3002,7 +3065,9 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { int dbSize = cJSON_GetArraySize(dbs); if (dbSize > MAX_DB_COUNT) { - printf("ERROR: failed to read json, databases size overflow, max database is %d\n", MAX_DB_COUNT); + errorPrint( + "ERROR: failed to read json, databases size overflow, max database is %d\n", + MAX_DB_COUNT); goto PARSE_OVER; } @@ -3040,8 +3105,10 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { } cJSON *precision = cJSON_GetObjectItem(dbinfo, "precision"); - if (precision && precision->type == cJSON_String && precision->valuestring != NULL) { - tstrncpy(g_Dbs.db[i].dbCfg.precision, precision->valuestring, MAX_DB_NAME_SIZE); + if (precision && precision->type == cJSON_String + && precision->valuestring != NULL) { + tstrncpy(g_Dbs.db[i].dbCfg.precision, precision->valuestring, + MAX_DB_NAME_SIZE); } else if (!precision) { //tstrncpy(g_Dbs.db[i].dbCfg.precision, "ms", MAX_DB_NAME_SIZE); memset(g_Dbs.db[i].dbCfg.precision, 0, MAX_DB_NAME_SIZE); @@ -3199,7 +3266,9 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { int stbSize = cJSON_GetArraySize(stables); if (stbSize > MAX_SUPER_TABLE_COUNT) { - printf("ERROR: failed to read json, databases size overflow, max database is %d\n", MAX_SUPER_TABLE_COUNT); + errorPrint( + "ERROR: failed to read json, databases size overflow, max database is %d\n", + MAX_SUPER_TABLE_COUNT); goto PARSE_OVER; } @@ -3265,13 +3334,13 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { } else if (!childTblExists) { g_Dbs.db[i].superTbls[j].childTblExists = TBL_NO_EXISTS; } else { - printf("ERROR: failed to read json, child_table_exists not found\n"); + errorPrint("%s() LN%d, failed to read json, child_table_exists not found\n", __func__, __LINE__); goto PARSE_OVER; } cJSON* count = cJSON_GetObjectItem(stbInfo, "childtable_count"); if (!count || count->type != cJSON_Number || 0 >= count->valueint) { - printf("ERROR: failed to read json, childtable_count not found\n"); + errorPrint("%s() LN%d, failed to read json, childtable_count not found\n", __func__, __LINE__); goto PARSE_OVER; } g_Dbs.db[i].superTbls[j].childTblCount = count->valueint; @@ -3284,7 +3353,7 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { } else if (!dataSource) { tstrncpy(g_Dbs.db[i].superTbls[j].dataSource, "rand", MAX_DB_NAME_SIZE); } else { - printf("ERROR: failed to read json, data_source not found\n"); + errorPrint("%s() LN%d, failed to read json, data_source not found\n", __func__, __LINE__); goto PARSE_OVER; } @@ -3324,9 +3393,11 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { cJSON *ts = cJSON_GetObjectItem(stbInfo, "start_timestamp"); if (ts && ts->type == cJSON_String && ts->valuestring != NULL) { - tstrncpy(g_Dbs.db[i].superTbls[j].startTimestamp, ts->valuestring, MAX_DB_NAME_SIZE); + tstrncpy(g_Dbs.db[i].superTbls[j].startTimestamp, + ts->valuestring, MAX_DB_NAME_SIZE); } else if (!ts) { - tstrncpy(g_Dbs.db[i].superTbls[j].startTimestamp, "now", MAX_DB_NAME_SIZE); + tstrncpy(g_Dbs.db[i].superTbls[j].startTimestamp, + "now", MAX_DB_NAME_SIZE); } else { printf("ERROR: failed to read json, start_timestamp not found\n"); goto PARSE_OVER; @@ -3427,23 +3498,13 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { goto PARSE_OVER; } - cJSON* numberOfTblInOneSql = cJSON_GetObjectItem(stbInfo, "number_of_tbl_in_one_sql"); - if (numberOfTblInOneSql && numberOfTblInOneSql->type == cJSON_Number) { - g_Dbs.db[i].superTbls[j].numberOfTblInOneSql = numberOfTblInOneSql->valueint; - } else if (!numberOfTblInOneSql) { - g_Dbs.db[i].superTbls[j].numberOfTblInOneSql = 0; - } else { - printf("ERROR: failed to read json, numberOfTblInOneSql not found\n"); - goto PARSE_OVER; - } - cJSON* rowsPerTbl = cJSON_GetObjectItem(stbInfo, "rows_per_tbl"); if (rowsPerTbl && rowsPerTbl->type == cJSON_Number) { g_Dbs.db[i].superTbls[j].rowsPerTbl = rowsPerTbl->valueint; } else if (!rowsPerTbl) { - g_Dbs.db[i].superTbls[j].rowsPerTbl = 1; + g_Dbs.db[i].superTbls[j].rowsPerTbl = 0; // 0 means progressive mode, > 0 mean interlace mode. max value is less or equ num_of_records_per_req } else { - printf("ERROR: failed to read json, rowsPerTbl not found\n"); + errorPrint("%s() LN%d, failed to read json, rowsPerTbl input mistake\n", __func__, __LINE__); goto PARSE_OVER; } @@ -3473,7 +3534,7 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { } else if (!insertRows) { g_Dbs.db[i].superTbls[j].insertRows = 0x7FFFFFFFFFFFFFFF; } else { - fprintf(stderr, "failed to read json, insert_rows input mistake"); + errorPrint("%s() LN%d, failed to read json, insert_rows input mistake\n", __func__, __LINE__); goto PARSE_OVER; } @@ -3485,16 +3546,18 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { __func__, __LINE__, g_args.insert_interval); g_Dbs.db[i].superTbls[j].insertInterval = g_args.insert_interval; } else { - fprintf(stderr, "failed to read json, insert_interval input mistake"); + errorPrint("%s() LN%d, failed to read json, insert_interval input mistake\n", __func__, __LINE__); goto PARSE_OVER; } - if (NO_CREATE_SUBTBL == g_Dbs.db[i].superTbls[j].autoCreateTable +/* CBD if (NO_CREATE_SUBTBL == g_Dbs.db[i].superTbls[j].autoCreateTable || (TBL_ALREADY_EXISTS == g_Dbs.db[i].superTbls[j].childTblExists)) { continue; } + */ - int retVal = getColumnAndTagTypeFromInsertJsonFile(stbInfo, &g_Dbs.db[i].superTbls[j]); + int retVal = getColumnAndTagTypeFromInsertJsonFile( + stbInfo, &g_Dbs.db[i].superTbls[j]); if (false == retVal) { goto PARSE_OVER; } @@ -3776,7 +3839,9 @@ static bool getMetaFromQueryJsonFile(cJSON* root) { } cJSON* subkeepProgress = cJSON_GetObjectItem(subQuery, "keepProgress"); - if (subkeepProgress && subkeepProgress->type == cJSON_String && subkeepProgress->valuestring != NULL) { + if (subkeepProgress && + subkeepProgress->type == cJSON_String + && subkeepProgress->valuestring != NULL) { if (0 == strcmp("yes", subkeepProgress->valuestring)) { g_queryInfo.subQueryInfo.subscribeKeepProgress = 1; } else if (0 == strcmp("no", subkeepProgress->valuestring)) { @@ -3867,30 +3932,30 @@ static bool getInfoFromJsonFile(char* file) { cJSON* filetype = cJSON_GetObjectItem(root, "filetype"); if (filetype && filetype->type == cJSON_String && filetype->valuestring != NULL) { if (0 == strcasecmp("insert", filetype->valuestring)) { - g_args.test_mode = INSERT_MODE; + g_args.test_mode = INSERT_TEST; } else if (0 == strcasecmp("query", filetype->valuestring)) { - g_args.test_mode = QUERY_MODE; + g_args.test_mode = QUERY_TEST; } else if (0 == strcasecmp("subscribe", filetype->valuestring)) { - g_args.test_mode = SUBSCRIBE_MODE; + g_args.test_mode = SUBSCRIBE_TEST; } else { printf("ERROR: failed to read json, filetype not support\n"); goto PARSE_OVER; } } else if (!filetype) { - g_args.test_mode = INSERT_MODE; + g_args.test_mode = INSERT_TEST; } else { printf("ERROR: failed to read json, filetype not found\n"); goto PARSE_OVER; } - if (INSERT_MODE == g_args.test_mode) { + if (INSERT_TEST == g_args.test_mode) { ret = getMetaFromInsertJsonFile(root); - } else if (QUERY_MODE == g_args.test_mode) { + } else if (QUERY_TEST == g_args.test_mode) { ret = getMetaFromQueryJsonFile(root); - } else if (SUBSCRIBE_MODE == g_args.test_mode) { + } else if (SUBSCRIBE_TEST == g_args.test_mode) { ret = getMetaFromQueryJsonFile(root); } else { - printf("ERROR: input json file type error! please input correct file type: insert or query or subscribe\n"); + errorPrint("%s() LN%d, input json file type error! please input correct file type: insert or query or subscribe\n", __func__, __LINE__); goto PARSE_OVER; } @@ -3901,13 +3966,9 @@ PARSE_OVER: return ret; } -void prepareSampleData() { +static void prepareSampleData() { for (int i = 0; i < g_Dbs.dbCount; i++) { for (int j = 0; j < g_Dbs.db[i].superTblCount; j++) { - //if (0 == strncasecmp(g_Dbs.db[i].superTbls[j].dataSource, "sample", 6)) { - // readSampleFromFileToMem(&g_Dbs.db[i].superTbls[j]); - //} - if (g_Dbs.db[i].superTbls[j].tagsFile[0] != 0) { (void)readTagFromCsvFileToMem(&g_Dbs.db[i].superTbls[j]); } @@ -3915,7 +3976,7 @@ void prepareSampleData() { } } -void postFreeResource() { +static void postFreeResource() { tmfclose(g_fpOfInsertResult); for (int i = 0; i < g_Dbs.dbCount; i++) { for (int j = 0; j < g_Dbs.db[i].superTblCount; j++) { @@ -3942,16 +4003,18 @@ void postFreeResource() { static int getRowDataFromSample(char* dataBuf, int maxLen, int64_t timestamp, SSuperTable* superTblInfo, int* sampleUsePos) { if ((*sampleUsePos) == MAX_SAMPLES_ONCE_FROM_FILE) { - int ret = readSampleFromCsvFileToMem(superTblInfo); +/* int ret = readSampleFromCsvFileToMem(superTblInfo); if (0 != ret) { tmfree(superTblInfo->sampleDataBuf); superTblInfo->sampleDataBuf = NULL; return -1; } +*/ *sampleUsePos = 0; } int dataLen = 0; + dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "(%" PRId64 ", ", timestamp); dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, @@ -3967,29 +4030,40 @@ static int generateRowData(char* dataBuf, int maxLen, int64_t timestamp, SSuper int dataLen = 0; dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "(%" PRId64 ", ", timestamp); for (int i = 0; i < stbInfo->columnCount; i++) { - if ((0 == strncasecmp(stbInfo->columns[i].dataType, "binary", 6)) || (0 == strncasecmp(stbInfo->columns[i].dataType, "nchar", 5))) { + if ((0 == strncasecmp(stbInfo->columns[i].dataType, "binary", 6)) + || (0 == strncasecmp(stbInfo->columns[i].dataType, "nchar", 5))) { if (stbInfo->columns[i].dataLen > TSDB_MAX_BINARY_LEN) { - printf("binary or nchar length overflow, max size:%u\n", (uint32_t)TSDB_MAX_BINARY_LEN); + errorPrint( "binary or nchar length overflow, max size:%u\n", + (uint32_t)TSDB_MAX_BINARY_LEN); return (-1); } - + char* buf = (char*)calloc(stbInfo->columns[i].dataLen+1, 1); if (NULL == buf) { - printf("calloc failed! size:%d\n", stbInfo->columns[i].dataLen); + errorPrint( "calloc failed! size:%d\n", stbInfo->columns[i].dataLen); return (-1); } rand_string(buf, stbInfo->columns[i].dataLen); dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "\'%s\', ", buf); tmfree(buf); - } else if (0 == strncasecmp(stbInfo->columns[i].dataType, "int", 3)) { - dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "%d, ", rand_int()); - } else if (0 == strncasecmp(stbInfo->columns[i].dataType, "bigint", 6)) { - dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "%"PRId64", ", rand_bigint()); - } else if (0 == strncasecmp(stbInfo->columns[i].dataType, "float", 5)) { - dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "%f, ", rand_float()); - } else if (0 == strncasecmp(stbInfo->columns[i].dataType, "double", 6)) { - dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "%f, ", rand_double()); - } else if (0 == strncasecmp(stbInfo->columns[i].dataType, "smallint", 8)) { + } else if (0 == strncasecmp(stbInfo->columns[i].dataType, + "int", 3)) { + dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, + "%d, ", rand_int()); + } else if (0 == strncasecmp(stbInfo->columns[i].dataType, + "bigint", 6)) { + dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, + "%"PRId64", ", rand_bigint()); + } else if (0 == strncasecmp(stbInfo->columns[i].dataType, + "float", 5)) { + dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, + "%f, ", rand_float()); + } else if (0 == strncasecmp(stbInfo->columns[i].dataType, + "double", 6)) { + dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, + "%f, ", rand_double()); + } else if (0 == strncasecmp(stbInfo->columns[i].dataType, + "smallint", 8)) { dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "%d, ", rand_smallint()); } else if (0 == strncasecmp(stbInfo->columns[i].dataType, "tinyint", 7)) { dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "%d, ", rand_tinyint()); @@ -3998,7 +4072,7 @@ static int generateRowData(char* dataBuf, int maxLen, int64_t timestamp, SSuper } else if (0 == strncasecmp(stbInfo->columns[i].dataType, "timestamp", 9)) { dataLen += snprintf(dataBuf + dataLen, maxLen - dataLen, "%"PRId64", ", rand_bigint()); } else { - printf("No support data type: %s\n", stbInfo->columns[i].dataType); + errorPrint( "No support data type: %s\n", stbInfo->columns[i].dataType); return (-1); } } @@ -4009,256 +4083,7 @@ static int generateRowData(char* dataBuf, int maxLen, int64_t timestamp, SSuper return dataLen; } -static void syncWriteForNumberOfTblInOneSql( - threadInfo *winfo, char* sampleDataBuf) { - SSuperTable* superTblInfo = winfo->superTblInfo; - - int samplePos = 0; - - //printf("========threadID[%d], table rang: %d - %d \n", winfo->threadID, winfo->start_table_id, winfo->end_table_id); - int64_t lastPrintTime = taosGetTimestampMs(); - - char* buffer = calloc(superTblInfo->maxSqlLen+1, 1); - if (NULL == buffer) { - printf("========calloc size[ %d ] fail!\n", superTblInfo->maxSqlLen); - return; - } - - int32_t numberOfTblInOneSql = superTblInfo->numberOfTblInOneSql; - int32_t tbls = winfo->end_table_id - winfo->start_table_id + 1; - if (numberOfTblInOneSql > tbls) { - numberOfTblInOneSql = tbls; - } - - uint64_t time_counter = winfo->start_time; - int sampleUsePos; - - int insert_interval = superTblInfo?superTblInfo->insertInterval:g_args.insert_interval; - int64_t st = 0; - int64_t et = 0xffffffff; - - int64_t insertRows = (superTblInfo)?superTblInfo->insertRows:g_args.num_of_DPT; - for (int i = 0; i < insertRows;) { - int32_t tbl_id = 0; - for (int tableSeq = winfo->start_table_id; tableSeq <= winfo->end_table_id; ) { - int64_t start_time = 0; - int inserted = i; - - for (int k = 0; k < g_args.num_of_RPR;) { - int len = 0; - memset(buffer, 0, superTblInfo->maxSqlLen); - char *pstr = buffer; - - int32_t end_tbl_id = tableSeq + numberOfTblInOneSql; - if (end_tbl_id > winfo->end_table_id) { - end_tbl_id = winfo->end_table_id+1; - } - - for (tbl_id = tableSeq ; tbl_id < end_tbl_id; tbl_id++) { - sampleUsePos = samplePos; - if (AUTO_CREATE_SUBTBL == superTblInfo->autoCreateTable) { - char* tagsValBuf = NULL; - if (0 == superTblInfo->tagSource) { - tagsValBuf = generateTagVaulesForStb(superTblInfo); - } else { - tagsValBuf = getTagValueFromTagSample( - superTblInfo, tbl_id % superTblInfo->tagSampleCount); - } - if (NULL == tagsValBuf) { - goto free_and_statistics; - } - - if (0 == len) { - len += snprintf(pstr + len, - superTblInfo->maxSqlLen - len, - "insert into %s.%s%d using %s.%s tags %s values ", - winfo->db_name, - superTblInfo->childTblPrefix, - tbl_id, - winfo->db_name, - superTblInfo->sTblName, - tagsValBuf); - } else { - len += snprintf(pstr + len, - superTblInfo->maxSqlLen - len, - " %s.%s%d using %s.%s tags %s values ", - winfo->db_name, - superTblInfo->childTblPrefix, - tbl_id, - winfo->db_name, - superTblInfo->sTblName, - tagsValBuf); - } - tmfree(tagsValBuf); - } else if (TBL_ALREADY_EXISTS == superTblInfo->childTblExists) { - if (0 == len) { - len += snprintf(pstr + len, - superTblInfo->maxSqlLen - len, - "insert into %s.%s values ", - winfo->db_name, - superTblInfo->childTblName + tbl_id * TSDB_TABLE_NAME_LEN); - } else { - len += snprintf(pstr + len, - superTblInfo->maxSqlLen - len, - " %s.%s values ", - winfo->db_name, - superTblInfo->childTblName + tbl_id * TSDB_TABLE_NAME_LEN); - } - } else { // pre-create child table - if (0 == len) { - len += snprintf(pstr + len, - superTblInfo->maxSqlLen - len, - "insert into %s.%s%d values ", - winfo->db_name, - superTblInfo->childTblPrefix, - tbl_id); - } else { - len += snprintf(pstr + len, - superTblInfo->maxSqlLen - len, - " %s.%s%d values ", - winfo->db_name, - superTblInfo->childTblPrefix, - tbl_id); - } - } - - start_time = time_counter; - for (int j = 0; j < superTblInfo->rowsPerTbl;) { - int retLen = 0; - if (0 == strncasecmp(superTblInfo->dataSource, - "sample", strlen("sample"))) { - retLen = getRowDataFromSample(pstr + len, - superTblInfo->maxSqlLen - len, - start_time += superTblInfo->timeStampStep, - superTblInfo, - &sampleUsePos); - if (retLen < 0) { - goto free_and_statistics; - } - } else if (0 == strncasecmp( - superTblInfo->dataSource, "rand", strlen("rand"))) { - int rand_num = rand_tinyint() % 100; - if (0 != superTblInfo->disorderRatio - && rand_num < superTblInfo->disorderRatio) { - int64_t d = start_time - taosRandom() % superTblInfo->disorderRange; - retLen = generateRowData(pstr + len, - superTblInfo->maxSqlLen - len, - d, - superTblInfo); - } else { - retLen = generateRowData(pstr + len, - superTblInfo->maxSqlLen - len, - start_time += superTblInfo->timeStampStep, - superTblInfo); - } - if (retLen < 0) { - goto free_and_statistics; - } - } - len += retLen; - //inserted++; - j++; - winfo->totalInsertRows++; - - if (inserted >= superTblInfo->insertRows || - (superTblInfo->maxSqlLen - len) < (superTblInfo->lenOfOneRow + 128)) { - tableSeq = tbl_id + 1; - printf("config rowsPerTbl and numberOfTblInOneSql not match with max_sql_lenth, please reconfig![lenOfOneRow:%d]\n", - superTblInfo->lenOfOneRow); - goto send_to_server; - } - } - } - - tableSeq = tbl_id; - inserted += superTblInfo->rowsPerTbl; - -send_to_server: - if (insert_interval) { - st = taosGetTimestampUs(); - - if (insert_interval > ((et - st)/1000)) { - int sleep_time = insert_interval - (et -st); - printf("sleep: %d ms insert interval\n", sleep_time); - taosMsleep(sleep_time); // ms - } - } - - if (0 == strncasecmp(superTblInfo->insertMode, - "taosc", - strlen("taosc"))) { - //printf("multi table===== sql: %s \n\n", buffer); - //int64_t t1 = taosGetTimestampMs(); - int64_t startTs; - int64_t endTs; - startTs = taosGetTimestampUs(); - - debugPrint("%s() LN%d buff: %s\n", __func__, __LINE__, buffer); - int affectedRows = queryDbExec( - winfo->taos, buffer, INSERT_TYPE); - - if (0 < affectedRows) { - endTs = taosGetTimestampUs(); - int64_t delay = endTs - startTs; - if (delay > winfo->maxDelay) winfo->maxDelay = delay; - if (delay < winfo->minDelay) winfo->minDelay = delay; - winfo->cntDelay++; - winfo->totalDelay += delay; - winfo->avgDelay = (double)winfo->totalDelay / winfo->cntDelay; - winfo->totalAffectedRows += affectedRows; - } else { - fprintf(stderr, "queryDbExec() buffer:\n%s\naffected rows is %d", buffer, affectedRows); - goto free_and_statistics; - } - - int64_t currentPrintTime = taosGetTimestampMs(); - if (currentPrintTime - lastPrintTime > 30*1000) { - printf("thread[%d] has currently inserted rows: %"PRId64 ", affected rows: %"PRId64 "\n", - winfo->threadID, - winfo->totalInsertRows, - winfo->totalAffectedRows); - lastPrintTime = currentPrintTime; - } - //int64_t t2 = taosGetTimestampMs(); - //printf("taosc insert sql return, Spent %.4f seconds \n", (double)(t2 - t1)/1000.0); - } else { - //int64_t t1 = taosGetTimestampMs(); - int retCode = postProceSql(g_Dbs.host, g_Dbs.port, buffer); - //int64_t t2 = taosGetTimestampMs(); - //printf("http insert sql return, Spent %ld ms \n", t2 - t1); - - if (0 != retCode) { - printf("========restful return fail, threadID[%d]\n", winfo->threadID); - goto free_and_statistics; - } - } - if (insert_interval) { - et = taosGetTimestampUs(); - } - - break; - } - - if (tableSeq > winfo->end_table_id) { - if (0 == strncasecmp(superTblInfo->dataSource, "sample", strlen("sample"))) { - samplePos = sampleUsePos; - } - i = inserted; - time_counter = start_time; - } - } - - //printf("========loop %d childTables duration:%"PRId64 "========inserted rows:%d\n", winfo->end_table_id - winfo->start_table_id, et - st, i); - } - -free_and_statistics: - tmfree(buffer); - printf("====thread[%d] completed total inserted rows: %"PRId64 ", affected rows: %"PRId64 "====\n", - winfo->threadID, winfo->totalInsertRows, winfo->totalAffectedRows); - return; -} - -int32_t generateData(char *res, char **data_type, +static int32_t generateData(char *res, char **data_type, int num_of_cols, int64_t timestamp, int lenOfBinary) { memset(res, 0, MAX_DATA_SIZE); char *pstr = res; @@ -4319,66 +4144,82 @@ int32_t generateData(char *res, char **data_type, static int prepareSampleDataForSTable(SSuperTable *superTblInfo) { char* sampleDataBuf = NULL; - // each thread read sample data from csv file - if (0 == strncasecmp(superTblInfo->dataSource, - "sample", - strlen("sample"))) { - sampleDataBuf = calloc( + sampleDataBuf = calloc( superTblInfo->lenOfOneRow * MAX_SAMPLES_ONCE_FROM_FILE, 1); - if (sampleDataBuf == NULL) { - fprintf(stderr, "Failed to calloc %d Bytes, reason:%s\n", + if (sampleDataBuf == NULL) { + errorPrint("%s() LN%d, Failed to calloc %d Bytes, reason:%s\n", + __func__, __LINE__, superTblInfo->lenOfOneRow * MAX_SAMPLES_ONCE_FROM_FILE, strerror(errno)); return -1; - } + } - superTblInfo->sampleDataBuf = sampleDataBuf; - int ret = readSampleFromCsvFileToMem(superTblInfo); - if (0 != ret) { + superTblInfo->sampleDataBuf = sampleDataBuf; + int ret = readSampleFromCsvFileToMem(superTblInfo); + + if (0 != ret) { + errorPrint("%s() LN%d, read sample from csv file failed.\n", __func__, __LINE__); tmfree(sampleDataBuf); superTblInfo->sampleDataBuf = NULL; return -1; - } } return 0; } -static int execInsert(threadInfo *winfo, char *buffer, int k) +static int execInsert(threadInfo *pThreadInfo, char *buffer, int k) { int affectedRows; - SSuperTable* superTblInfo = winfo->superTblInfo; + SSuperTable* superTblInfo = pThreadInfo->superTblInfo; + verbosePrint("[%d] %s() LN%d %s\n", pThreadInfo->threadID, + __func__, __LINE__, buffer); if (superTblInfo) { if (0 == strncasecmp(superTblInfo->insertMode, "taosc", strlen("taosc"))) { - verbosePrint("%s() LN%d %s\n", __func__, __LINE__, buffer); - affectedRows = queryDbExec(winfo->taos, buffer, INSERT_TYPE); + affectedRows = queryDbExec(pThreadInfo->taos, buffer, INSERT_TYPE); } else { - verbosePrint("%s() LN%d %s\n", __func__, __LINE__, buffer); - int retCode = postProceSql(g_Dbs.host, g_Dbs.port, buffer); - - if (0 != retCode) { + if (0 != postProceSql(g_Dbs.host, g_Dbs.port, buffer)) { affectedRows = -1; - printf("========restful return fail, threadID[%d]\n", winfo->threadID); + printf("========restful return fail, threadID[%d]\n", pThreadInfo->threadID); } else { affectedRows = k; } } } else { - verbosePrint("%s() LN%d %s\n", __func__, __LINE__, buffer); - affectedRows = queryDbExec(winfo->taos, buffer, 1); + affectedRows = queryDbExec(pThreadInfo->taos, buffer, 1); } return affectedRows; } -static int generateDataBuffer(int32_t tableSeq, - threadInfo *pThreadInfo, char *buffer, - int64_t insertRows, - int64_t startFrom, int64_t startTime, int *pSampleUsePos) +static void getTableName(char *pTblName, threadInfo* pThreadInfo, int tableSeq) { SSuperTable* superTblInfo = pThreadInfo->superTblInfo; + if (superTblInfo) { + if ((superTblInfo->childTblOffset >= 0) + && (superTblInfo->childTblLimit > 0)) { + snprintf(pTblName, TSDB_TABLE_NAME_LEN, "%s", + superTblInfo->childTblName + (tableSeq - superTblInfo->childTblOffset) * TSDB_TABLE_NAME_LEN); + } else { + + verbosePrint("[%d] %s() LN%d: from=%d count=%d seq=%d\n", + pThreadInfo->threadID, __func__, __LINE__, + pThreadInfo->start_table_from, + pThreadInfo->ntables, tableSeq); + snprintf(pTblName, TSDB_TABLE_NAME_LEN, "%s", + superTblInfo->childTblName + tableSeq * TSDB_TABLE_NAME_LEN); + } + } else { + snprintf(pTblName, TSDB_TABLE_NAME_LEN, "%s%d", + g_args.tb_prefix, tableSeq); + } +} +static int generateDataTail(char *tableName, int32_t tableSeq, + threadInfo* pThreadInfo, SSuperTable* superTblInfo, + int batch, char* buffer, int64_t insertRows, + int64_t startFrom, uint64_t startTime, int *pSamplePos, int *dataLen) { + int len = 0; int ncols_per_record = 1; // count first col ts if (superTblInfo == NULL) { @@ -4389,166 +4230,389 @@ static int generateDataBuffer(int32_t tableSeq, } } - assert(buffer != NULL); + verbosePrint("%s() LN%d batch=%d\n", __func__, __LINE__, batch); - char *pChildTblName; - int childTblCount; + int k = 0; + for (k = 0; k < batch;) { + if (superTblInfo) { + int retLen = 0; - pChildTblName = calloc(TSDB_TABLE_NAME_LEN, 1); - if (NULL == pChildTblName) { - fprintf(stderr, "failed to alloc memory %d\n", TSDB_TABLE_NAME_LEN); - return -1; - } + if (0 == strncasecmp(superTblInfo->dataSource, + "sample", strlen("sample"))) { + retLen = getRowDataFromSample( + buffer + len, + superTblInfo->maxSqlLen - len, + startTime + superTblInfo->timeStampStep * k, + superTblInfo, + pSamplePos); + } else if (0 == strncasecmp(superTblInfo->dataSource, + "rand", strlen("rand"))) { + int rand_num = rand_tinyint() % 100; + if (0 != superTblInfo->disorderRatio + && rand_num < superTblInfo->disorderRatio) { + int64_t d = startTime + + superTblInfo->timeStampStep * k + - taosRandom() % superTblInfo->disorderRange; + retLen = generateRowData( + buffer + len, + superTblInfo->maxSqlLen - len, + d, + superTblInfo); + } else { + retLen = generateRowData( + buffer + len, + superTblInfo->maxSqlLen - len, + startTime + superTblInfo->timeStampStep * k, + superTblInfo); + } + } - if (superTblInfo && (superTblInfo->childTblOffset > 0)) { - // select tbname from stb limit 1 offset tableSeq - getChildNameOfSuperTableWithLimitAndOffset(pThreadInfo->taos, - pThreadInfo->db_name, superTblInfo->sTblName, - &pChildTblName, &childTblCount, - 1, tableSeq); - } else { - snprintf(pChildTblName, TSDB_TABLE_NAME_LEN, "%s%d", - superTblInfo?superTblInfo->childTblPrefix:g_args.tb_prefix, tableSeq); - } + if (retLen < 0) { + return -1; + } - memset(buffer, 0, superTblInfo?superTblInfo->maxSqlLen:g_args.max_sql_len); + len += retLen; - char *pstr = buffer; + if (len >= (superTblInfo->maxSqlLen - 256)) { // reserve for overwrite + k++; + break; + } + } else { + int rand_num = taosRandom() % 100; + char data[MAX_DATA_SIZE]; + char **data_type = g_args.datatype; + int lenOfBinary = g_args.len_of_binary; - if (superTblInfo) { - if (AUTO_CREATE_SUBTBL == superTblInfo->autoCreateTable) { - char* tagsValBuf = NULL; - if (0 == superTblInfo->tagSource) { - tagsValBuf = generateTagVaulesForStb(superTblInfo); + if ((g_args.disorderRatio != 0) + && (rand_num < g_args.disorderRange)) { + + int64_t d = startTime + DEFAULT_TIMESTAMP_STEP * k + - taosRandom() % 1000000 + rand_num; + len = generateData(data, data_type, + ncols_per_record, d, lenOfBinary); } else { - tagsValBuf = getTagValueFromTagSample( - superTblInfo, - tableSeq % superTblInfo->tagSampleCount); + len = generateData(data, data_type, + ncols_per_record, + startTime + DEFAULT_TIMESTAMP_STEP * k, + lenOfBinary); } - if (NULL == tagsValBuf) { - free(pChildTblName); - fprintf(stderr, "tag buf failed to allocate memory\n"); + + buffer += sprintf(buffer, " %s", data); + if (strlen(buffer) >= (g_args.max_sql_len - 256)) { // too long + k++; + break; + } + } + + verbosePrint("%s() LN%d len=%d k=%d \nbuffer=%s\n", + __func__, __LINE__, len, k, buffer); + + k++; + startFrom ++; + + if (startFrom >= insertRows) { + break; + } + } + + *dataLen = len; + return k; +} + +static int generateSQLHead(char *tableName, int32_t tableSeq, + threadInfo* pThreadInfo, SSuperTable* superTblInfo, char *buffer) +{ + int len; + if (superTblInfo) { + if (AUTO_CREATE_SUBTBL == superTblInfo->autoCreateTable) { + char* tagsValBuf = NULL; + if (0 == superTblInfo->tagSource) { + tagsValBuf = generateTagVaulesForStb(superTblInfo); + } else { + tagsValBuf = getTagValueFromTagSample( + superTblInfo, + tableSeq % superTblInfo->tagSampleCount); + } + if (NULL == tagsValBuf) { + errorPrint("%s() LN%d, tag buf failed to allocate memory\n", __func__, __LINE__); return -1; } - pstr += snprintf(pstr, + len = snprintf(buffer, superTblInfo->maxSqlLen, "insert into %s.%s using %s.%s tags %s values", pThreadInfo->db_name, - pChildTblName, + tableName, pThreadInfo->db_name, superTblInfo->sTblName, tagsValBuf); tmfree(tagsValBuf); } else if (TBL_ALREADY_EXISTS == superTblInfo->childTblExists) { - pstr += snprintf(pstr, + len = snprintf(buffer, superTblInfo->maxSqlLen, "insert into %s.%s values", pThreadInfo->db_name, - superTblInfo->childTblName + tableSeq * TSDB_TABLE_NAME_LEN); + tableName); } else { - pstr += snprintf(pstr, - (superTblInfo?superTblInfo->maxSqlLen:g_args.max_sql_len), + len = snprintf(buffer, + superTblInfo->maxSqlLen, "insert into %s.%s values", pThreadInfo->db_name, - pChildTblName); + tableName); } } else { - pstr += snprintf(pstr, + len = snprintf(buffer, g_args.max_sql_len, "insert into %s.%s values", pThreadInfo->db_name, - pChildTblName); + tableName); } + return len; +} + +static int generateDataBuffer(char *pTblName, + int32_t tableSeq, + threadInfo *pThreadInfo, char *buffer, + int64_t insertRows, + int64_t startFrom, int64_t startTime, int *pSamplePos) +{ + SSuperTable* superTblInfo = pThreadInfo->superTblInfo; + + int ncols_per_record = 1; // count first col ts + + if (superTblInfo == NULL) { + int datatypeSeq = 0; + while(g_args.datatype[datatypeSeq]) { + datatypeSeq ++; + ncols_per_record ++; + } + } + + assert(buffer != NULL); + + memset(buffer, 0, superTblInfo?superTblInfo->maxSqlLen:g_args.max_sql_len); + + char *pstr = buffer; + + int headLen = generateSQLHead(pTblName, tableSeq, pThreadInfo, superTblInfo, + buffer); + pstr += headLen; + int k; - int len = 0; + int dataLen; + k = generateDataTail(pTblName, tableSeq, pThreadInfo, superTblInfo, + g_args.num_of_RPR, pstr, insertRows, startFrom, + startTime, + pSamplePos, &dataLen); + return k; +} - verbosePrint("%s() LN%d num_of_RPR=%d\n", __func__, __LINE__, g_args.num_of_RPR); - for (k = 0; k < g_args.num_of_RPR;) { - if (superTblInfo) { - int retLen = 0; +static void* syncWriteInterlace(threadInfo *pThreadInfo) { + debugPrint("[%d] %s() LN%d: ### interlace write\n", + pThreadInfo->threadID, __func__, __LINE__); - if (0 == strncasecmp(superTblInfo->dataSource, - "sample", strlen("sample"))) { - retLen = getRowDataFromSample( - pstr + len, - superTblInfo->maxSqlLen - len, - startTime + superTblInfo->timeStampStep * startFrom, - superTblInfo, - pSampleUsePos); - } else if (0 == strncasecmp(superTblInfo->dataSource, - "rand", strlen("rand"))) { - int rand_num = rand_tinyint() % 100; - if (0 != superTblInfo->disorderRatio - && rand_num < superTblInfo->disorderRatio) { - int64_t d = startTime - taosRandom() % superTblInfo->disorderRange; - retLen = generateRowData( - pstr + len, - superTblInfo->maxSqlLen - len, - d, - superTblInfo); - //printf("disorder rows, rand_num:%d, last ts:%"PRId64" current ts:%"PRId64"\n", rand_num, start_time, d); - } else { - retLen = generateRowData( - pstr + len, - superTblInfo->maxSqlLen - len, - startTime + superTblInfo->timeStampStep * startFrom, - superTblInfo); - } + SSuperTable* superTblInfo = pThreadInfo->superTblInfo; - if (retLen < 0) { - free(pChildTblName); - return -1; - } + char* buffer = calloc(superTblInfo?superTblInfo->maxSqlLen:g_args.max_sql_len, 1); + if (NULL == buffer) { + errorPrint( "Failed to alloc %d Bytes, reason:%s\n", + superTblInfo?superTblInfo->maxSqlLen:g_args.max_sql_len, + strerror(errno)); + return NULL; + } + + int insertMode; + char tableName[TSDB_TABLE_NAME_LEN]; - len += retLen; + int rowsPerTbl = superTblInfo?superTblInfo->rowsPerTbl:g_args.rows_per_tbl; - if (len >= (superTblInfo->maxSqlLen - 256)) { // reserve for overwrite - k++; - break; - } - } - } else { - int rand_num = taosRandom() % 100; - char data[MAX_DATA_SIZE]; - char **data_type = g_args.datatype; - int lenOfBinary = g_args.len_of_binary; + if (rowsPerTbl > 0) { + insertMode = INTERLACE_INSERT_MODE; + } else { + insertMode = PROGRESSIVE_INSERT_MODE; + } - if ((g_args.disorderRatio != 0) - && (rand_num < g_args.disorderRange)) { - - int64_t d = startTime - taosRandom() % 1000000 + rand_num; - len = generateData(data, data_type, - ncols_per_record, d, lenOfBinary); + // rows per table need be less than insert batch + if (rowsPerTbl > g_args.num_of_RPR) + rowsPerTbl = g_args.num_of_RPR; + + pThreadInfo->totalInsertRows = 0; + pThreadInfo->totalAffectedRows = 0; + + int64_t insertRows = (superTblInfo)?superTblInfo->insertRows:g_args.num_of_DPT; + int insert_interval = superTblInfo?superTblInfo->insertInterval:g_args.insert_interval; + uint64_t st = 0; + uint64_t et = 0xffffffff; + + int64_t lastPrintTime = taosGetTimestampMs(); + int64_t startTs = taosGetTimestampUs(); + int64_t endTs; + + int tableSeq = pThreadInfo->start_table_from; + + debugPrint("[%d] %s() LN%d: start_table_from=%d ntables=%d insertRows=%"PRId64"\n", + pThreadInfo->threadID, __func__, __LINE__, pThreadInfo->start_table_from, + pThreadInfo->ntables, insertRows); + + int64_t startTime = pThreadInfo->start_time; + + int batchPerTblTimes; + int batchPerTbl; + + assert(pThreadInfo->ntables > 0); + + if (rowsPerTbl > g_args.num_of_RPR) + rowsPerTbl = g_args.num_of_RPR; + + batchPerTbl = rowsPerTbl; + if ((rowsPerTbl > 0) && (pThreadInfo->ntables > 1)) { + batchPerTblTimes = + (g_args.num_of_RPR / (rowsPerTbl * pThreadInfo->ntables)) + 1; + } else { + batchPerTblTimes = 1; + } + + int generatedRecPerTbl = 0; + bool flagSleep = true; + int sleepTimeTotal = 0; + while(pThreadInfo->totalInsertRows < pThreadInfo->ntables * insertRows) { + if ((flagSleep) && (insert_interval)) { + st = taosGetTimestampUs(); + flagSleep = false; + } + // generate data + memset(buffer, 0, superTblInfo?superTblInfo->maxSqlLen:g_args.max_sql_len); + + char *pstr = buffer; + int recOfBatch = 0; + + for (int i = 0; i < batchPerTblTimes; i ++) { + getTableName(tableName, pThreadInfo, tableSeq); + + int headLen; + if (i == 0) { + headLen = generateSQLHead(tableName, tableSeq, pThreadInfo, + superTblInfo, pstr); } else { - len = generateData(data, data_type, - ncols_per_record, - startTime + DEFAULT_TIMESTAMP_STEP * startFrom, - lenOfBinary); + headLen = snprintf(pstr, TSDB_TABLE_NAME_LEN, "%s.%s values", + pThreadInfo->db_name, + tableName); } - pstr += sprintf(pstr, " %s", data); - //assert(len + pstr - buffer < BUFFER_SIZE); - if (len + pstr - buffer >= (g_args.max_sql_len - 256)) { // too long - k++; - break; + // generate data buffer + verbosePrint("[%d] %s() LN%d i=%d buffer:\n%s\n", + pThreadInfo->threadID, __func__, __LINE__, i, buffer); + + pstr += headLen; + int dataLen = 0; + + verbosePrint("[%d] %s() LN%d i=%d batchPerTblTimes=%d batchPerTbl = %d\n", + pThreadInfo->threadID, __func__, __LINE__, + i, batchPerTblTimes, batchPerTbl); + generateDataTail( + tableName, tableSeq, pThreadInfo, superTblInfo, + batchPerTbl, pstr, insertRows, 0, + startTime, + &(pThreadInfo->samplePos), &dataLen); + + pstr += dataLen; + recOfBatch += batchPerTbl; + pThreadInfo->totalInsertRows += batchPerTbl; + + verbosePrint("[%d] %s() LN%d batchPerTbl=%d recOfBatch=%d\n", + pThreadInfo->threadID, __func__, __LINE__, + batchPerTbl, recOfBatch); + + tableSeq ++; + if (insertMode == INTERLACE_INSERT_MODE) { + if (tableSeq == pThreadInfo->start_table_from + pThreadInfo->ntables) { + // turn to first table + startTime += batchPerTbl * superTblInfo->timeStampStep; + tableSeq = pThreadInfo->start_table_from; + generatedRecPerTbl += batchPerTbl; + flagSleep = true; + if (generatedRecPerTbl >= insertRows) + break; + + if (pThreadInfo->ntables * batchPerTbl < g_args.num_of_RPR) + break; + } } + + int remainRows = insertRows - generatedRecPerTbl; + if ((remainRows > 0) && (batchPerTbl > remainRows)) + batchPerTbl = remainRows; + + verbosePrint("[%d] %s() LN%d generatedRecPerTbl=%d insertRows=%"PRId64"\n", + pThreadInfo->threadID, __func__, __LINE__, + generatedRecPerTbl, insertRows); + + if ((g_args.num_of_RPR - recOfBatch) < batchPerTbl) + break; } - verbosePrint("%s() LN%d len=%d k=%d \nbuffer=%p\n", __func__, __LINE__, len, k, buffer); + verbosePrint("[%d] %s() LN%d recOfBatch=%d totalInsertRows=%"PRId64"\n", + pThreadInfo->threadID, __func__, __LINE__, recOfBatch, + pThreadInfo->totalInsertRows); + verbosePrint("[%d] %s() LN%d, buffer=%s\n", + pThreadInfo->threadID, __func__, __LINE__, buffer); - k++; - startFrom ++; + startTs = taosGetTimestampUs(); - if (startFrom >= insertRows) - break; + int affectedRows = execInsert(pThreadInfo, buffer, recOfBatch); + + endTs = taosGetTimestampUs(); + int64_t delay = endTs - startTs; + performancePrint("%s() LN%d, insert execution time is %10.6fms\n", + __func__, __LINE__, delay/1000.0); + + if (delay > pThreadInfo->maxDelay) pThreadInfo->maxDelay = delay; + if (delay < pThreadInfo->minDelay) pThreadInfo->minDelay = delay; + pThreadInfo->cntDelay++; + pThreadInfo->totalDelay += delay; + + verbosePrint("[%d] %s() LN%d affectedRows=%d\n", pThreadInfo->threadID, + __func__, __LINE__, affectedRows); + if ((affectedRows < 0) || (recOfBatch != affectedRows)) { + errorPrint("[%d] %s() LN%d execInsert insert %d, affected rows: %d\n%s\n", + pThreadInfo->threadID, __func__, __LINE__, + recOfBatch, affectedRows, buffer); + goto free_and_statistics_interlace; + } + + pThreadInfo->totalAffectedRows += affectedRows; + + int64_t currentPrintTime = taosGetTimestampMs(); + if (currentPrintTime - lastPrintTime > 30*1000) { + printf("thread[%d] has currently inserted rows: %"PRId64 ", affected rows: %"PRId64 "\n", + pThreadInfo->threadID, + pThreadInfo->totalInsertRows, + pThreadInfo->totalAffectedRows); + lastPrintTime = currentPrintTime; + } + + if ((insert_interval) && flagSleep) { + et = taosGetTimestampUs(); + + if (insert_interval > ((et - st)/1000) ) { + int sleepTime = insert_interval - (et -st)/1000; + performancePrint("%s() LN%d sleep: %d ms for insert interval\n", + __func__, __LINE__, sleepTime); + taosMsleep(sleepTime); // ms + sleepTimeTotal += insert_interval; + } + } } - if (pChildTblName) - free(pChildTblName); +free_and_statistics_interlace: + tmfree(buffer); - return k; + printf("====thread[%d] completed total inserted rows: %"PRId64 ", total affected rows: %"PRId64 "====\n", + pThreadInfo->threadID, + pThreadInfo->totalInsertRows, + pThreadInfo->totalAffectedRows); + return NULL; } // sync insertion @@ -4559,50 +4623,36 @@ static int generateDataBuffer(int32_t tableSeq, 2 taosinsertdata , 1 thread: 10 tables * 20000 rows/s */ -static void* syncWrite(void *sarg) { +static void* syncWriteProgressive(threadInfo *pThreadInfo) { + debugPrint("%s() LN%d: ### progressive write\n", __func__, __LINE__); - threadInfo *winfo = (threadInfo *)sarg; - SSuperTable* superTblInfo = winfo->superTblInfo; + SSuperTable* superTblInfo = pThreadInfo->superTblInfo; char* buffer = calloc(superTblInfo?superTblInfo->maxSqlLen:g_args.max_sql_len, 1); if (NULL == buffer) { - fprintf(stderr, "Failed to alloc %d Bytes, reason:%s\n", + errorPrint( "Failed to alloc %d Bytes, reason:%s\n", superTblInfo?superTblInfo->maxSqlLen:g_args.max_sql_len, strerror(errno)); return NULL; } - - if (superTblInfo) { - if (0 != prepareSampleDataForSTable(superTblInfo)) - return NULL; - - if (superTblInfo->numberOfTblInOneSql > 0) { - syncWriteForNumberOfTblInOneSql(winfo, superTblInfo->sampleDataBuf); - tmfree(superTblInfo->sampleDataBuf); - superTblInfo->sampleDataBuf = NULL; - return NULL; - } - } - int samplePos = 0; - int64_t lastPrintTime = taosGetTimestampMs(); int64_t startTs = taosGetTimestampUs(); int64_t endTs; - int insert_interval = superTblInfo?superTblInfo->insertInterval: - g_args.insert_interval; + int timeStampStep = superTblInfo?superTblInfo->timeStampStep:DEFAULT_TIMESTAMP_STEP; + int insert_interval = superTblInfo?superTblInfo->insertInterval:g_args.insert_interval; uint64_t st = 0; uint64_t et = 0xffffffff; - winfo->totalInsertRows = 0; - winfo->totalAffectedRows = 0; + pThreadInfo->totalInsertRows = 0; + pThreadInfo->totalAffectedRows = 0; - int sampleUsePos; + pThreadInfo->samplePos = 0; - for (uint32_t tableSeq = winfo->start_table_id; tableSeq <= winfo->end_table_id; + for (uint32_t tableSeq = pThreadInfo->start_table_from; tableSeq <= pThreadInfo->end_table_to; tableSeq ++) { - int64_t start_time = winfo->start_time; + int64_t start_time = pThreadInfo->start_time; int64_t insertRows = (superTblInfo)?superTblInfo->insertRows:g_args.num_of_DPT; verbosePrint("%s() LN%d insertRows=%"PRId64"\n", __func__, __LINE__, insertRows); @@ -4612,36 +4662,49 @@ static void* syncWrite(void *sarg) { st = taosGetTimestampUs(); } - sampleUsePos = samplePos; + char tableName[TSDB_TABLE_NAME_LEN]; + getTableName(tableName, pThreadInfo, tableSeq); + verbosePrint("%s() LN%d: tid=%d seq=%d tableName=%s\n", + __func__, __LINE__, + pThreadInfo->threadID, tableSeq, tableName); - int generated = generateDataBuffer(tableSeq, winfo, buffer, insertRows, - i, start_time, &sampleUsePos); + int generated = generateDataBuffer( + tableName, tableSeq, pThreadInfo, buffer, insertRows, + i, start_time, + &(pThreadInfo->samplePos)); if (generated > 0) i += generated; else goto free_and_statistics_2; - int affectedRows = execInsert(winfo, buffer, generated); - if (affectedRows < 0) - goto free_and_statistics_2; + start_time += generated * timeStampStep; + pThreadInfo->totalInsertRows += generated; + startTs = taosGetTimestampUs(); - winfo->totalInsertRows += generated; - winfo->totalAffectedRows += affectedRows; + int affectedRows = execInsert(pThreadInfo, buffer, generated); endTs = taosGetTimestampUs(); int64_t delay = endTs - startTs; - if (delay > winfo->maxDelay) winfo->maxDelay = delay; - if (delay < winfo->minDelay) winfo->minDelay = delay; - winfo->cntDelay++; - winfo->totalDelay += delay; + performancePrint("%s() LN%d, insert execution time is %10.6fms\n", + __func__, __LINE__, delay/1000.0); + + if (delay > pThreadInfo->maxDelay) pThreadInfo->maxDelay = delay; + if (delay < pThreadInfo->minDelay) pThreadInfo->minDelay = delay; + pThreadInfo->cntDelay++; + pThreadInfo->totalDelay += delay; + + if (affectedRows < 0) + goto free_and_statistics_2; + + pThreadInfo->totalAffectedRows += affectedRows; int64_t currentPrintTime = taosGetTimestampMs(); if (currentPrintTime - lastPrintTime > 30*1000) { printf("thread[%d] has currently inserted rows: %"PRId64 ", affected rows: %"PRId64 "\n", - winfo->threadID, - winfo->totalInsertRows, - winfo->totalAffectedRows); + pThreadInfo->threadID, + pThreadInfo->totalInsertRows, + pThreadInfo->totalAffectedRows); lastPrintTime = currentPrintTime; } @@ -4653,34 +4716,48 @@ static void* syncWrite(void *sarg) { if (insert_interval > ((et - st)/1000) ) { int sleep_time = insert_interval - (et -st)/1000; - verbosePrint("%s() LN%d sleep: %d ms for insert interval\n", __func__, __LINE__, sleep_time); + performancePrint("%s() LN%d sleep: %d ms for insert interval\n", + __func__, __LINE__, sleep_time); taosMsleep(sleep_time); // ms } } } // num_of_DPT - if ((tableSeq == winfo->end_table_id) && superTblInfo && + if ((tableSeq == pThreadInfo->ntables - 1) && superTblInfo && (0 == strncasecmp( superTblInfo->dataSource, "sample", strlen("sample")))) { - samplePos = sampleUsePos; + printf("%s() LN%d samplePos=%d\n", + __func__, __LINE__, pThreadInfo->samplePos); } } // tableSeq free_and_statistics_2: tmfree(buffer); - if (superTblInfo) { - tmfree(superTblInfo->sampleDataBuf); - superTblInfo->sampleDataBuf = NULL; - } printf("====thread[%d] completed total inserted rows: %"PRId64 ", total affected rows: %"PRId64 "====\n", - winfo->threadID, - winfo->totalInsertRows, - winfo->totalAffectedRows); + pThreadInfo->threadID, + pThreadInfo->totalInsertRows, + pThreadInfo->totalAffectedRows); return NULL; } -void callBack(void *param, TAOS_RES *res, int code) { +static void* syncWrite(void *sarg) { + + threadInfo *winfo = (threadInfo *)sarg; + SSuperTable* superTblInfo = winfo->superTblInfo; + + int rowsPerTbl = superTblInfo?superTblInfo->rowsPerTbl:g_args.rows_per_tbl; + + if (rowsPerTbl > 0) { + // interlace mode + return syncWriteInterlace(winfo); + } else { + // progressive mode + return syncWriteProgressive(winfo); + } +} + +static void callBack(void *param, TAOS_RES *res, int code) { threadInfo* winfo = (threadInfo*)param; SSuperTable* superTblInfo = winfo->superTblInfo; @@ -4695,13 +4772,14 @@ void callBack(void *param, TAOS_RES *res, int code) { char *buffer = calloc(1, winfo->superTblInfo->maxSqlLen); char *data = calloc(1, MAX_DATA_SIZE); char *pstr = buffer; - pstr += sprintf(pstr, "insert into %s.%s%d values", winfo->db_name, winfo->tb_prefix, winfo->start_table_id); + pstr += sprintf(pstr, "insert into %s.%s%d values", winfo->db_name, winfo->tb_prefix, + winfo->start_table_from); // if (winfo->counter >= winfo->superTblInfo->insertRows) { if (winfo->counter >= g_args.num_of_RPR) { - winfo->start_table_id++; + winfo->start_table_from++; winfo->counter = 0; } - if (winfo->start_table_id > winfo->end_table_id) { + if (winfo->start_table_from > winfo->end_table_to) { tsem_post(&winfo->lock_sem); free(buffer); free(data); @@ -4738,7 +4816,7 @@ void callBack(void *param, TAOS_RES *res, int code) { taos_free_result(res); } -void *asyncWrite(void *sarg) { +static void *asyncWrite(void *sarg) { threadInfo *winfo = (threadInfo *)sarg; SSuperTable* superTblInfo = winfo->superTblInfo; @@ -4810,7 +4888,7 @@ static void startMultiThreadInsertData(int threads, char* db_name, } else if (0 == strncasecmp(precision, "us", 2)) { timePrec = TSDB_TIME_PRECISION_MICRO; } else { - fprintf(stderr, "No support precision: %s\n", precision); + errorPrint( "No support precision: %s\n", precision); exit(-1); } } @@ -4820,14 +4898,13 @@ static void startMultiThreadInsertData(int threads, char* db_name, if (0 == strncasecmp(superTblInfo->startTimestamp, "now", 3)) { start_time = taosGetTimestamp(timePrec); } else { - if (TSDB_CODE_SUCCESS != taosParseTime( - superTblInfo->startTimestamp, - &start_time, - strlen(superTblInfo->startTimestamp), - timePrec, 0)) { - printf("ERROR to parse time!\n"); - exit(-1); - } + if (TSDB_CODE_SUCCESS != taosParseTime( + superTblInfo->startTimestamp, + &start_time, + strlen(superTblInfo->startTimestamp), + timePrec, 0)) { + ERROR_EXIT("failed to parse time!\n"); + } } } else { start_time = 1500000000000; @@ -4835,12 +4912,69 @@ static void startMultiThreadInsertData(int threads, char* db_name, double start = getCurrentTime(); - int last; + int startFrom; if ((superTblInfo) && (superTblInfo->childTblOffset >= 0)) - last = superTblInfo->childTblOffset; + startFrom = superTblInfo->childTblOffset; else - last = 0; + startFrom = 0; + + // read sample data from file first + if ((superTblInfo) && (0 == strncasecmp(superTblInfo->dataSource, + "sample", strlen("sample")))) { + if (0 != prepareSampleDataForSTable(superTblInfo)) { + errorPrint("%s() LN%d, prepare sample data for stable failed!\n", __func__, __LINE__); + exit(-1); + } + } + + // read sample data from file first + if ((superTblInfo) && (0 == strncasecmp(superTblInfo->dataSource, + "sample", strlen("sample")))) { + if (0 != prepareSampleDataForSTable(superTblInfo)) { + errorPrint("%s() LN%d, prepare sample data for stable failed!\n", __func__, __LINE__); + exit(-1); + } + } + + TAOS* taos = taos_connect( + g_Dbs.host, g_Dbs.user, + g_Dbs.password, db_name, g_Dbs.port); + if (NULL == taos) { + errorPrint("%s() LN%d, connect to server fail , reason: %s\n", + __func__, __LINE__, taos_errstr(NULL)); + exit(-1); + } + + if (superTblInfo) { + + int limit, offset; + if (superTblInfo && (superTblInfo->childTblOffset >= 0) + && (superTblInfo->childTblLimit > 0)) { + limit = superTblInfo->childTblLimit; + offset = superTblInfo->childTblOffset; + } else { + limit = superTblInfo->childTblCount; + offset = 0; + } + + superTblInfo->childTblName = (char*)calloc(1, + limit * TSDB_TABLE_NAME_LEN); + if (superTblInfo->childTblName == NULL) { + errorPrint("%s() LN%d, alloc memory failed!\n", __func__, __LINE__); + taos_close(taos); + exit(-1); + } + + int childTblCount; + getChildNameOfSuperTableWithLimitAndOffset( + taos, + db_name, superTblInfo->sTblName, + &superTblInfo->childTblName, &childTblCount, + limit, + offset); + } + taos_close(taos); for (int i = 0; i < threads; i++) { threadInfo *t_info = infos + i; @@ -4858,7 +4992,7 @@ static void startMultiThreadInsertData(int threads, char* db_name, g_Dbs.host, g_Dbs.user, g_Dbs.password, db_name, g_Dbs.port); if (NULL == t_info->taos) { - printf("connect to server fail from insert sub thread, reason: %s\n", + errorPrint( "connect to server fail from insert sub thread, reason: %s\n", taos_errstr(NULL)); exit(-1); } @@ -4868,12 +5002,13 @@ static void startMultiThreadInsertData(int threads, char* db_name, if ((NULL == superTblInfo) || (0 == superTblInfo->multiThreadWriteOneTbl)) { - t_info->start_table_id = last; - t_info->end_table_id = i < b ? last + a : last + a - 1; - last = t_info->end_table_id + 1; + t_info->start_table_from = startFrom; + t_info->ntables = iend_table_to = i < b ? startFrom + a : startFrom + a - 1; + startFrom = t_info->end_table_to + 1; } else { - t_info->start_table_id = 0; - t_info->end_table_id = superTblInfo->childTblCount - 1; + t_info->start_table_from = 0; + t_info->ntables = superTblInfo->childTblCount; t_info->start_time = t_info->start_time + rand_int() % 10000 - rand_tinyint(); } @@ -4901,9 +5036,16 @@ static void startMultiThreadInsertData(int threads, char* db_name, tsem_destroy(&(t_info->lock_sem)); taos_close(t_info->taos); + debugPrint("%s() LN%d, [%d] totalInsert=%"PRId64" totalAffected=%"PRId64"\n", + __func__, __LINE__, + t_info->threadID, t_info->totalInsertRows, + t_info->totalAffectedRows); if (superTblInfo) { superTblInfo->totalAffectedRows += t_info->totalAffectedRows; superTblInfo->totalInsertRows += t_info->totalInsertRows; + } else { + g_args.totalAffectedRows += t_info->totalAffectedRows; + g_args.totalInsertRows += t_info->totalInsertRows; } totalDelay += t_info->totalDelay; @@ -4931,6 +5073,18 @@ static void startMultiThreadInsertData(int threads, char* db_name, superTblInfo->totalAffectedRows, threads, db_name, superTblInfo->sTblName, superTblInfo->totalInsertRows/ t); + } else { + printf("Spent %.4f seconds to insert rows: %"PRId64", affected rows: %"PRId64" with %d thread(s) into %s %2.f records/second\n\n", + t, g_args.totalInsertRows, + g_args.totalAffectedRows, + threads, db_name, + g_args.totalInsertRows / t); + fprintf(g_fpOfInsertResult, + "Spent %.4f seconds to insert rows: %"PRId64", affected rows: %"PRId64" with %d thread(s) into %s %2.f records/second\n\n", + t, g_args.totalInsertRows, + g_args.totalAffectedRows, + threads, db_name, + g_args.totalInsertRows / t); } printf("insert delay, avg: %10.6fms, max: %10.6fms, min: %10.6fms\n\n", @@ -4944,7 +5098,7 @@ static void startMultiThreadInsertData(int threads, char* db_name, free(infos); } -void *readTable(void *sarg) { +static void *readTable(void *sarg) { #if 1 threadInfo *rinfo = (threadInfo *)sarg; TAOS *taos = rinfo->taos; @@ -4953,7 +5107,7 @@ void *readTable(void *sarg) { char *tb_prefix = rinfo->tb_prefix; FILE *fp = fopen(rinfo->fp, "a"); if (NULL == fp) { - printf("fopen %s fail, reason:%s.\n", rinfo->fp, strerror(errno)); + errorPrint( "fopen %s fail, reason:%s.\n", rinfo->fp, strerror(errno)); return NULL; } @@ -4965,7 +5119,7 @@ void *readTable(void *sarg) { num_of_DPT = g_args.num_of_DPT; // } - int num_of_tables = rinfo->end_table_id - rinfo->start_table_id + 1; + int num_of_tables = rinfo->ntables; // rinfo->end_table_to - rinfo->start_table_from + 1; int totalData = num_of_DPT * num_of_tables; bool do_aggreFunc = g_Dbs.do_aggreFunc; @@ -4987,7 +5141,7 @@ void *readTable(void *sarg) { int32_t code = taos_errno(pSql); if (code != 0) { - fprintf(stderr, "Failed to query:%s\n", taos_errstr(pSql)); + errorPrint( "Failed to query:%s\n", taos_errstr(pSql)); taos_free_result(pSql); taos_close(taos); fclose(fp); @@ -5015,7 +5169,7 @@ void *readTable(void *sarg) { return NULL; } -void *readMetric(void *sarg) { +static void *readMetric(void *sarg) { #if 1 threadInfo *rinfo = (threadInfo *)sarg; TAOS *taos = rinfo->taos; @@ -5027,7 +5181,7 @@ void *readMetric(void *sarg) { } int num_of_DPT = rinfo->superTblInfo->insertRows; - int num_of_tables = rinfo->end_table_id - rinfo->start_table_id + 1; + int num_of_tables = rinfo->ntables; // rinfo->end_table_to - rinfo->start_table_from + 1; int totalData = num_of_DPT * num_of_tables; bool do_aggreFunc = g_Dbs.do_aggreFunc; @@ -5063,7 +5217,7 @@ void *readMetric(void *sarg) { int32_t code = taos_errno(pSql); if (code != 0) { - fprintf(stderr, "Failed to query:%s\n", taos_errstr(pSql)); + errorPrint( "Failed to query:%s\n", taos_errstr(pSql)); taos_free_result(pSql); taos_close(taos); fclose(fp); @@ -5075,7 +5229,8 @@ void *readMetric(void *sarg) { } t = getCurrentTime() - t; - fprintf(fp, "| Speed: %12.2f(per s) | Latency: %.4f(ms) |\n", num_of_tables * num_of_DPT / t, t * 1000); + fprintf(fp, "| Speed: %12.2f(per s) | Latency: %.4f(ms) |\n", + num_of_tables * num_of_DPT / t, t * 1000); printf("select %10s took %.6f second(s)\n\n", aggreFunc[j], t); taos_free_result(pSql); @@ -5100,7 +5255,7 @@ static int insertTestProcess() { debugPrint("%d result file: %s\n", __LINE__, g_Dbs.resultFile); g_fpOfInsertResult = fopen(g_Dbs.resultFile, "a"); if (NULL == g_fpOfInsertResult) { - fprintf(stderr, "Failed to open %s for save result\n", g_Dbs.resultFile); + errorPrint( "Failed to open %s for save result\n", g_Dbs.resultFile); return -1; } @@ -5177,7 +5332,7 @@ static int insertTestProcess() { return 0; } -void *superQueryProcess(void *sarg) { +static void *superQueryProcess(void *sarg) { threadInfo *winfo = (threadInfo *)sarg; //char sqlStr[MAX_TB_NAME_SIZE*2]; @@ -5189,7 +5344,7 @@ void *superQueryProcess(void *sarg) { while (1) { if (g_queryInfo.superQueryInfo.rate && (et - st) < (int64_t)g_queryInfo.superQueryInfo.rate*1000) { taosMsleep(g_queryInfo.superQueryInfo.rate*1000 - (et - st)); // ms - //printf("========sleep duration:%"PRId64 "========inserted rows:%d, table range:%d - %d\n", (1000 - (et - st)), i, winfo->start_table_id, winfo->end_table_id); + //printf("========sleep duration:%"PRId64 "========inserted rows:%d, table range:%d - %d\n", (1000 - (et - st)), i, winfo->start_table_from, winfo->end_table_to); } st = taosGetTimestampUs(); @@ -5253,13 +5408,14 @@ static void *subQueryProcess(void *sarg) { int64_t st = 0; int64_t et = (int64_t)g_queryInfo.subQueryInfo.rate*1000; while (1) { - if (g_queryInfo.subQueryInfo.rate && (et - st) < (int64_t)g_queryInfo.subQueryInfo.rate*1000) { + if (g_queryInfo.subQueryInfo.rate + && (et - st) < (int64_t)g_queryInfo.subQueryInfo.rate*1000) { taosMsleep(g_queryInfo.subQueryInfo.rate*1000 - (et - st)); // ms - //printf("========sleep duration:%"PRId64 "========inserted rows:%d, table range:%d - %d\n", (1000 - (et - st)), i, winfo->start_table_id, winfo->end_table_id); + //printf("========sleep duration:%"PRId64 "========inserted rows:%d, table range:%d - %d\n", (1000 - (et - st)), i, winfo->start_table_from, winfo->end_table_to); } st = taosGetTimestampUs(); - for (int i = winfo->start_table_id; i <= winfo->end_table_id; i++) { + for (int i = winfo->start_table_from; i <= winfo->end_table_to; i++) { for (int j = 0; j < g_queryInfo.subQueryInfo.sqlCount; j++) { memset(sqlstr,0,sizeof(sqlstr)); replaceSubTblName(g_queryInfo.subQueryInfo.sql[j], sqlstr, i); @@ -5275,8 +5431,8 @@ static void *subQueryProcess(void *sarg) { et = taosGetTimestampUs(); printf("####thread[%"PRId64"] complete all sqls to allocate all sub-tables[%d - %d] once queries duration:%.4fs\n\n", taosGetSelfPthreadId(), - winfo->start_table_id, - winfo->end_table_id, + winfo->start_table_from, + winfo->end_table_to, (double)(et - st)/1000000.0); } return NULL; @@ -5290,7 +5446,7 @@ static int queryTestProcess() { NULL, g_queryInfo.port); if (taos == NULL) { - fprintf(stderr, "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); + errorPrint( "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); exit(-1); } @@ -5314,31 +5470,42 @@ static int queryTestProcess() { pthread_t *pids = NULL; threadInfo *infos = NULL; //==== create sub threads for query from specify table - if (g_queryInfo.superQueryInfo.sqlCount > 0 && g_queryInfo.superQueryInfo.concurrent > 0) { + if (g_queryInfo.superQueryInfo.sqlCount > 0 + && g_queryInfo.superQueryInfo.concurrent > 0) { pids = malloc(g_queryInfo.superQueryInfo.concurrent * sizeof(pthread_t)); + if (NULL == pids) { + taos_close(taos); + ERROR_EXIT("memory allocation failed\n"); + } infos = malloc(g_queryInfo.superQueryInfo.concurrent * sizeof(threadInfo)); - if ((NULL == pids) || (NULL == infos)) { - printf("malloc failed for create threads\n"); + if (NULL == infos) { taos_close(taos); - exit(-1); + free(pids); + ERROR_EXIT("memory allocation failed for create threads\n"); } for (int i = 0; i < g_queryInfo.superQueryInfo.concurrent; i++) { threadInfo *t_info = infos + i; t_info->threadID = i; - + if (0 == strncasecmp(g_queryInfo.queryMode, "taosc", 5)) { t_info->taos = taos; char sqlStr[MAX_TB_NAME_SIZE*2]; sprintf(sqlStr, "use %s", g_queryInfo.dbName); verbosePrint("%s() %d sqlStr: %s\n", __func__, __LINE__, sqlStr); - (void)queryDbExec(t_info->taos, sqlStr, NO_INSERT_TYPE); + if (0 != queryDbExec(t_info->taos, sqlStr, NO_INSERT_TYPE)) { + free(infos); + free(pids); + errorPrint( "use database %s failed!\n\n", + g_queryInfo.dbName); + return -1; + } } else { t_info->taos = NULL; } - + pthread_create(pids + i, NULL, superQueryProcess, t_info); } }else { @@ -5351,11 +5518,21 @@ static int queryTestProcess() { if ((g_queryInfo.subQueryInfo.sqlCount > 0) && (g_queryInfo.subQueryInfo.threadCnt > 0)) { pidsOfSub = malloc(g_queryInfo.subQueryInfo.threadCnt * sizeof(pthread_t)); + if (NULL == pidsOfSub) { + taos_close(taos); + free(infos); + free(pids); + + ERROR_EXIT("memory allocation failed for create threads\n"); + } + infosOfSub = malloc(g_queryInfo.subQueryInfo.threadCnt * sizeof(threadInfo)); - if ((NULL == pidsOfSub) || (NULL == infosOfSub)) { - printf("malloc failed for create threads\n"); + if (NULL == infosOfSub) { taos_close(taos); - exit(-1); + free(pidsOfSub); + free(infos); + free(pids); + ERROR_EXIT("memory allocation failed for create threads\n"); } int ntables = g_queryInfo.subQueryInfo.childTblCount; @@ -5372,75 +5549,77 @@ static int queryTestProcess() { b = ntables % threads; } - int last = 0; + int startFrom = 0; for (int i = 0; i < threads; i++) { threadInfo *t_info = infosOfSub + i; t_info->threadID = i; - t_info->start_table_id = last; - t_info->end_table_id = i < b ? last + a : last + a - 1; - last = t_info->end_table_id + 1; + t_info->start_table_from = startFrom; + t_info->ntables = iend_table_to = i < b ? startFrom + a : startFrom + a - 1; + startFrom = t_info->end_table_to + 1; t_info->taos = taos; pthread_create(pidsOfSub + i, NULL, subQueryProcess, t_info); } g_queryInfo.subQueryInfo.threadCnt = threads; - }else { + } else { g_queryInfo.subQueryInfo.threadCnt = 0; } - + for (int i = 0; i < g_queryInfo.superQueryInfo.concurrent; i++) { pthread_join(pids[i], NULL); } tmfree((char*)pids); - tmfree((char*)infos); - + tmfree((char*)infos); + for (int i = 0; i < g_queryInfo.subQueryInfo.threadCnt; i++) { pthread_join(pidsOfSub[i], NULL); } tmfree((char*)pidsOfSub); - tmfree((char*)infosOfSub); - + tmfree((char*)infosOfSub); + taos_close(taos); return 0; } -static void subscribe_callback(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code) { +static void subscribe_callback(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code) { if (res == NULL || taos_errno(res) != 0) { - printf("failed to subscribe result, code:%d, reason:%s\n", code, taos_errstr(res)); + errorPrint("%s() LN%d, failed to subscribe result, code:%d, reason:%s\n", + __func__, __LINE__, code, taos_errstr(res)); return; } - + getResult(res, (char*)param); taos_free_result(res); } static TAOS_SUB* subscribeImpl(TAOS *taos, char *sql, char* topic, char* resultFileName) { - TAOS_SUB* tsub = NULL; + TAOS_SUB* tsub = NULL; if (g_queryInfo.superQueryInfo.subscribeMode) { - tsub = taos_subscribe(taos, - g_queryInfo.superQueryInfo.subscribeRestart, - topic, sql, subscribe_callback, (void*)resultFileName, + tsub = taos_subscribe(taos, + g_queryInfo.superQueryInfo.subscribeRestart, + topic, sql, subscribe_callback, (void*)resultFileName, g_queryInfo.superQueryInfo.subscribeInterval); } else { - tsub = taos_subscribe(taos, - g_queryInfo.superQueryInfo.subscribeRestart, + tsub = taos_subscribe(taos, + g_queryInfo.superQueryInfo.subscribeRestart, topic, sql, NULL, NULL, 0); } if (tsub == NULL) { printf("failed to create subscription. topic:%s, sql:%s\n", topic, sql); return NULL; - } + } return tsub; } -void *subSubscribeProcess(void *sarg) { - threadInfo *winfo = (threadInfo *)sarg; +static void *subSubscribeProcess(void *sarg) { + threadInfo *winfo = (threadInfo *)sarg; char subSqlstr[1024]; char sqlStr[MAX_TB_NAME_SIZE*2]; @@ -5449,13 +5628,13 @@ void *subSubscribeProcess(void *sarg) { if (0 != queryDbExec(winfo->taos, sqlStr, NO_INSERT_TYPE)){ return NULL; } - + //int64_t st = 0; //int64_t et = 0; do { //if (g_queryInfo.superQueryInfo.rate && (et - st) < g_queryInfo.superQueryInfo.rate*1000) { // taosMsleep(g_queryInfo.superQueryInfo.rate*1000 - (et - st)); // ms - // //printf("========sleep duration:%"PRId64 "========inserted rows:%d, table range:%d - %d\n", (1000 - (et - st)), i, winfo->start_table_id, winfo->end_table_id); + // //printf("========sleep duration:%"PRId64 "========inserted rows:%d, table range:%d - %d\n", (1000 - (et - st)), i, winfo->start_table_from, winfo->end_table_to); //} //st = taosGetTimestampMs(); @@ -5484,13 +5663,13 @@ void *subSubscribeProcess(void *sarg) { if (1 == g_queryInfo.subQueryInfo.subscribeMode) { continue; } - + res = taos_consume(g_queryInfo.subQueryInfo.tsub[i]); if (res) { char tmpFile[MAX_FILE_NAME_LEN*2] = {0}; if (g_queryInfo.subQueryInfo.result[i][0] != 0) { - sprintf(tmpFile, "%s-%d", - g_queryInfo.subQueryInfo.result[i], + sprintf(tmpFile, "%s-%d", + g_queryInfo.subQueryInfo.result[i], winfo->threadID); } getResult(res, tmpFile); @@ -5498,16 +5677,16 @@ void *subSubscribeProcess(void *sarg) { } } taos_free_result(res); - + for (int i = 0; i < g_queryInfo.subQueryInfo.sqlCount; i++) { - taos_unsubscribe(g_queryInfo.subQueryInfo.tsub[i], + taos_unsubscribe(g_queryInfo.subQueryInfo.tsub[i], g_queryInfo.subQueryInfo.subscribeKeepProgress); } return NULL; } -void *superSubscribeProcess(void *sarg) { - threadInfo *winfo = (threadInfo *)sarg; +static void *superSubscribeProcess(void *sarg) { + threadInfo *winfo = (threadInfo *)sarg; char sqlStr[MAX_TB_NAME_SIZE*2]; sprintf(sqlStr, "use %s", g_queryInfo.dbName); @@ -5515,13 +5694,13 @@ void *superSubscribeProcess(void *sarg) { if (0 != queryDbExec(winfo->taos, sqlStr, NO_INSERT_TYPE)) { return NULL; } - + //int64_t st = 0; //int64_t et = 0; do { //if (g_queryInfo.superQueryInfo.rate && (et - st) < g_queryInfo.superQueryInfo.rate*1000) { // taosMsleep(g_queryInfo.superQueryInfo.rate*1000 - (et - st)); // ms - // //printf("========sleep duration:%"PRId64 "========inserted rows:%d, table range:%d - %d\n", (1000 - (et - st)), i, winfo->start_table_id, winfo->end_table_id); + // //printf("========sleep duration:%"PRId64 "========inserted rows:%d, table range:%d - %d\n", (1000 - (et - st)), i, winfo->start_table_from, winfo->end_table_to); //} //st = taosGetTimestampMs(); @@ -5530,13 +5709,13 @@ void *superSubscribeProcess(void *sarg) { sprintf(topic, "taosdemo-subscribe-%d", i); char tmpFile[MAX_FILE_NAME_LEN*2] = {0}; if (g_queryInfo.subQueryInfo.result[i][0] != 0) { - sprintf(tmpFile, "%s-%d", + sprintf(tmpFile, "%s-%d", g_queryInfo.superQueryInfo.result[i], winfo->threadID); } - g_queryInfo.superQueryInfo.tsub[i] = - subscribeImpl(winfo->taos, - g_queryInfo.superQueryInfo.sql[i], - topic, tmpFile); + g_queryInfo.superQueryInfo.tsub[i] = + subscribeImpl(winfo->taos, + g_queryInfo.superQueryInfo.sql[i], + topic, tmpFile); if (NULL == g_queryInfo.superQueryInfo.tsub[i]) { return NULL; } @@ -5552,12 +5731,12 @@ void *superSubscribeProcess(void *sarg) { if (1 == g_queryInfo.superQueryInfo.subscribeMode) { continue; } - + res = taos_consume(g_queryInfo.superQueryInfo.tsub[i]); if (res) { char tmpFile[MAX_FILE_NAME_LEN*2] = {0}; if (g_queryInfo.superQueryInfo.result[i][0] != 0) { - sprintf(tmpFile, "%s-%d", + sprintf(tmpFile, "%s-%d", g_queryInfo.superQueryInfo.result[i], winfo->threadID); } getResult(res, tmpFile); @@ -5567,7 +5746,7 @@ void *superSubscribeProcess(void *sarg) { taos_free_result(res); for (int i = 0; i < g_queryInfo.superQueryInfo.sqlCount; i++) { - taos_unsubscribe(g_queryInfo.superQueryInfo.tsub[i], + taos_unsubscribe(g_queryInfo.superQueryInfo.tsub[i], g_queryInfo.superQueryInfo.subscribeKeepProgress); } return NULL; @@ -5588,7 +5767,7 @@ static int subscribeTestProcess() { g_queryInfo.dbName, g_queryInfo.port); if (taos == NULL) { - fprintf(stderr, "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); + errorPrint( "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); exit(-1); } @@ -5603,22 +5782,27 @@ static int subscribeTestProcess() { pthread_t *pids = NULL; threadInfo *infos = NULL; //==== create sub threads for query from super table - if (g_queryInfo.superQueryInfo.sqlCount > 0 - && g_queryInfo.superQueryInfo.concurrent > 0) { - pids = malloc(g_queryInfo.superQueryInfo.concurrent * sizeof(pthread_t)); - infos = malloc(g_queryInfo.superQueryInfo.concurrent * sizeof(threadInfo)); - if ((NULL == pids) || (NULL == infos)) { + if ((g_queryInfo.superQueryInfo.sqlCount <= 0) || + (g_queryInfo.superQueryInfo.concurrent <= 0)) { + errorPrint("%s() LN%d, query sqlCount %d or concurrent %d is not correct.\n", + __func__, __LINE__, g_queryInfo.superQueryInfo.sqlCount, + g_queryInfo.superQueryInfo.concurrent); + exit(-1); + } + + pids = malloc(g_queryInfo.superQueryInfo.concurrent * sizeof(pthread_t)); + infos = malloc(g_queryInfo.superQueryInfo.concurrent * sizeof(threadInfo)); + if ((NULL == pids) || (NULL == infos)) { printf("malloc failed for create threads\n"); taos_close(taos); exit(-1); - } + } - for (int i = 0; i < g_queryInfo.superQueryInfo.concurrent; i++) { + for (int i = 0; i < g_queryInfo.superQueryInfo.concurrent; i++) { threadInfo *t_info = infos + i; t_info->threadID = i; t_info->taos = taos; pthread_create(pids + i, NULL, superSubscribeProcess, t_info); - } } //==== create sub threads for query from sub table @@ -5650,13 +5834,15 @@ static int subscribeTestProcess() { b = ntables % threads; } - int last = 0; + int startFrom = 0; for (int i = 0; i < threads; i++) { threadInfo *t_info = infosOfSub + i; t_info->threadID = i; - t_info->start_table_id = last; - t_info->end_table_id = i < b ? last + a : last + a - 1; + t_info->start_table_from = startFrom; + t_info->ntables = iend_table_to = i < b ? startFrom + a : startFrom + a - 1; + startFrom = t_info->end_table_to + 1; t_info->taos = taos; pthread_create(pidsOfSub + i, NULL, subSubscribeProcess, t_info); } @@ -5680,7 +5866,7 @@ static int subscribeTestProcess() { return 0; } -void initOfInsertMeta() { +static void initOfInsertMeta() { memset(&g_Dbs, 0, sizeof(SDbs)); // set default values @@ -5692,7 +5878,7 @@ void initOfInsertMeta() { g_Dbs.use_metric = true; } -void initOfQueryMeta() { +static void initOfQueryMeta() { memset(&g_queryInfo, 0, sizeof(SQueryMetaInfo)); // set default values @@ -5702,7 +5888,7 @@ void initOfQueryMeta() { tstrncpy(g_queryInfo.password, TSDB_DEFAULT_PASS, MAX_DB_NAME_SIZE); } -void setParaFromArg(){ +static void setParaFromArg(){ if (g_args.host) { strcpy(g_Dbs.host, g_args.host); } else { @@ -5843,7 +6029,7 @@ static int isCommentLine(char *line) { return regexMatch(line, "^\\s*#.*", REG_EXTENDED); } -void querySqlFile(TAOS* taos, char* sqlFile) +static void querySqlFile(TAOS* taos, char* sqlFile) { FILE *fp = fopen(sqlFile, "r"); if (fp == NULL) { @@ -5876,7 +6062,6 @@ void querySqlFile(TAOS* taos, char* sqlFile) memcpy(cmd + cmd_len, line, read_len); verbosePrint("%s() LN%d cmd: %s\n", __func__, __LINE__, cmd); - queryDbExec(taos, cmd, NO_INSERT_TYPE); if (0 != queryDbExec(taos, cmd, NO_INSERT_TYPE)) { printf("queryDbExec %s failed!\n", cmd); tmfree(cmd); @@ -5898,14 +6083,14 @@ void querySqlFile(TAOS* taos, char* sqlFile) } static void testMetaFile() { - if (INSERT_MODE == g_args.test_mode) { + if (INSERT_TEST == g_args.test_mode) { if (g_Dbs.cfgDir[0]) taos_options(TSDB_OPTION_CONFIGDIR, g_Dbs.cfgDir); insertTestProcess(); - } else if (QUERY_MODE == g_args.test_mode) { + } else if (QUERY_TEST == g_args.test_mode) { if (g_queryInfo.cfgDir[0]) taos_options(TSDB_OPTION_CONFIGDIR, g_queryInfo.cfgDir); queryTestProcess(); - } else if (SUBSCRIBE_MODE == g_args.test_mode) { + } else if (SUBSCRIBE_TEST == g_args.test_mode) { if (g_queryInfo.cfgDir[0]) taos_options(TSDB_OPTION_CONFIGDIR, g_queryInfo.cfgDir); subscribeTestProcess(); @@ -5922,16 +6107,18 @@ static void queryResult() { pthread_t read_id; threadInfo *rInfo = malloc(sizeof(threadInfo)); rInfo->start_time = 1500000000000; // 2017-07-14 10:40:00.000 - rInfo->start_table_id = 0; + rInfo->start_table_from = 0; //rInfo->do_aggreFunc = g_Dbs.do_aggreFunc; if (g_args.use_metric) { - rInfo->end_table_id = g_Dbs.db[0].superTbls[0].childTblCount - 1; + rInfo->ntables = g_Dbs.db[0].superTbls[0].childTblCount; + rInfo->end_table_to = g_Dbs.db[0].superTbls[0].childTblCount - 1; rInfo->superTblInfo = &g_Dbs.db[0].superTbls[0]; strcpy(rInfo->tb_prefix, g_Dbs.db[0].superTbls[0].childTblPrefix); } else { - rInfo->end_table_id = g_args.num_of_tables -1; + rInfo->ntables = g_args.num_of_tables; + rInfo->end_table_to = g_args.num_of_tables -1; strcpy(rInfo->tb_prefix, g_args.tb_prefix); } @@ -5942,7 +6129,7 @@ static void queryResult() { g_Dbs.db[0].dbName, g_Dbs.port); if (rInfo->taos == NULL) { - fprintf(stderr, "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); + errorPrint( "Failed to connect to TDengine, reason:%s\n", taos_errstr(NULL)); free(rInfo); exit(-1); } @@ -5962,13 +6149,13 @@ static void queryResult() { static void testCmdLine() { - g_args.test_mode = INSERT_MODE; - insertTestProcess(); + g_args.test_mode = INSERT_TEST; + insertTestProcess(); - if (g_Dbs.insert_only) - return; - else - queryResult(); + if (g_Dbs.insert_only) + return; + else + queryResult(); } int main(int argc, char *argv[]) { @@ -5978,7 +6165,7 @@ int main(int argc, char *argv[]) { if (g_args.metaFile) { initOfInsertMeta(); - initOfQueryMeta(); + initOfQueryMeta(); if (false == getInfoFromJsonFile(g_args.metaFile)) { printf("Failed to read %s\n", g_args.metaFile); @@ -5992,12 +6179,12 @@ int main(int argc, char *argv[]) { if (NULL != g_args.sqlFile) { TAOS* qtaos = taos_connect( - g_Dbs.host, - g_Dbs.user, - g_Dbs.password, - g_Dbs.db[0].dbName, + g_Dbs.host, + g_Dbs.user, + g_Dbs.password, + g_Dbs.db[0].dbName, g_Dbs.port); - querySqlFile(qtaos, g_args.sqlFile); + querySqlFile(qtaos, g_args.sqlFile); taos_close(qtaos); } else { diff --git a/src/mnode/inc/mnodeVgroup.h b/src/mnode/inc/mnodeVgroup.h index 2067ad04ccf963701bffcf134c8ae478547f1abb..e052cdb83cbbffa0ceb1119bc49da52a23e02e2b 100644 --- a/src/mnode/inc/mnodeVgroup.h +++ b/src/mnode/inc/mnodeVgroup.h @@ -44,7 +44,7 @@ void mnodeDropVgroup(SVgObj *pVgroup, void *ahandle); void mnodeAlterVgroup(SVgObj *pVgroup, void *ahandle); int32_t mnodeGetAvailableVgroup(struct SMnodeMsg *pMsg, SVgObj **pVgroup, int32_t *sid); -void mnodeAddTableIntoVgroup(SVgObj *pVgroup, SCTableObj *pTable); +int32_t mnodeAddTableIntoVgroup(SVgObj *pVgroup, SCTableObj *pTable, bool needCheck); void mnodeRemoveTableFromVgroup(SVgObj *pVgroup, SCTableObj *pTable); void mnodeSendDropVnodeMsg(int32_t vgId, SRpcEpSet *epSet, void *ahandle); void mnodeSendCreateVgroupMsg(SVgObj *pVgroup, void *ahandle); diff --git a/src/mnode/src/mnodeDb.c b/src/mnode/src/mnodeDb.c index fcc88db740e664c3e3510744f78f79069b89bfb7..b1c88ca718305c4087f291e1b1703543fd8dba3c 100644 --- a/src/mnode/src/mnodeDb.c +++ b/src/mnode/src/mnodeDb.c @@ -55,7 +55,7 @@ int32_t mnodeProcessAlterDbMsg(SMnodeMsg *pMsg); #ifndef _TOPIC int32_t tpInit() { return 0; } void tpCleanUp() {} -void tpUpdateTs(int32_t *seq, void *pMsg) {} +void tpUpdateTs(int32_t vgId, int64_t *seq, void *pMsg) {} #endif static void mnodeDestroyDb(SDbObj *pDb) { diff --git a/src/mnode/src/mnodeSdb.c b/src/mnode/src/mnodeSdb.c index fe1f70cb50e4dd074962d42ea475095c37667c38..381cb11952dbdd639408bda1efdae258a9a94a30 100644 --- a/src/mnode/src/mnodeSdb.c +++ b/src/mnode/src/mnodeSdb.c @@ -35,7 +35,7 @@ #include "mnodeSdb.h" #define SDB_TABLE_LEN 12 -#define MAX_QUEUED_MSG_NUM 10000 +#define MAX_QUEUED_MSG_NUM 100000 typedef enum { SDB_ACTION_INSERT = 0, @@ -552,7 +552,7 @@ static int32_t sdbInsertHash(SSdbTable *pTable, SSdbRow *pRow) { int32_t code = (*pTable->fpInsert)(pRow); if (code != TSDB_CODE_SUCCESS) { - sdbError("vgId:1, sdb:%s, failed to insert key:%s to hash, remove it", pTable->name, + sdbError("vgId:1, sdb:%s, failed to perform insert action for key:%s, remove it", pTable->name, sdbGetRowStr(pTable, pRow->pObj)); sdbDeleteHash(pTable, pRow); } @@ -680,7 +680,7 @@ static int32_t sdbProcessWrite(void *wparam, void *hparam, int32_t qtype, void * if (pRow != NULL) { // forward to peers pRow->processedCount = 0; - int32_t syncCode = syncForwardToPeer(tsSdbMgmt.sync, pHead, pRow, TAOS_QTYPE_RPC); + int32_t syncCode = syncForwardToPeer(tsSdbMgmt.sync, pHead, pRow, TAOS_QTYPE_RPC, false); if (syncCode <= 0) pRow->processedCount = 1; if (syncCode < 0) { @@ -700,7 +700,7 @@ static int32_t sdbProcessWrite(void *wparam, void *hparam, int32_t qtype, void * actStr[action], sdbGetKeyStr(pTable, pHead->cont), pHead->version); // even it is WAL/FWD, it shall be called to update version in sync - syncForwardToPeer(tsSdbMgmt.sync, pHead, pRow, TAOS_QTYPE_RPC); + syncForwardToPeer(tsSdbMgmt.sync, pHead, pRow, TAOS_QTYPE_RPC, false); // from wal or forward msg, row not created, should add into hash if (action == SDB_ACTION_INSERT) { @@ -1119,7 +1119,7 @@ static void *sdbWorkerFp(void *pWorker) { sdbConfirmForward(1, pRow, pRow->code); } else { if (qtype == TAOS_QTYPE_FWD) { - syncConfirmForward(tsSdbMgmt.sync, pRow->pHead.version, pRow->code); + syncConfirmForward(tsSdbMgmt.sync, pRow->pHead.version, pRow->code, false); } sdbFreeFromQueue(pRow); } diff --git a/src/mnode/src/mnodeTable.c b/src/mnode/src/mnodeTable.c index 1378847d0b472bb65621fc88951d4fbd6e7d0790..39eca8819d8ace50b6fa82fadb1d4464247568d6 100644 --- a/src/mnode/src/mnodeTable.c +++ b/src/mnode/src/mnodeTable.c @@ -108,10 +108,12 @@ static int32_t mnodeChildTableActionDestroy(SSdbRow *pRow) { static int32_t mnodeChildTableActionInsert(SSdbRow *pRow) { SCTableObj *pTable = pRow->pObj; + int32_t code = 0; SVgObj *pVgroup = mnodeGetVgroup(pTable->vgId); if (pVgroup == NULL) { mError("ctable:%s, not in vgId:%d", pTable->info.tableId, pTable->vgId); + code = -1; } SDbObj *pDb = NULL; @@ -119,6 +121,7 @@ static int32_t mnodeChildTableActionInsert(SSdbRow *pRow) { pDb = mnodeGetDb(pVgroup->dbName); if (pDb == NULL) { mError("ctable:%s, vgId:%d not in db:%s", pTable->info.tableId, pVgroup->vgId, pVgroup->dbName); + code = -1; } } @@ -127,6 +130,7 @@ static int32_t mnodeChildTableActionInsert(SSdbRow *pRow) { pAcct = mnodeGetAcct(pDb->acct); if (pAcct == NULL) { mError("ctable:%s, acct:%s not exists", pTable->info.tableId, pDb->acct); + code = -1; } } @@ -139,6 +143,7 @@ static int32_t mnodeChildTableActionInsert(SSdbRow *pRow) { if (pAcct) pAcct->acctInfo.numOfTimeSeries += (pTable->superTable->numOfColumns - 1); } else { mError("table:%s:%p, correspond stable not found suid:%" PRIu64, pTable->info.tableId, pTable, pTable->suid); + code = -1; } } else { grantAdd(TSDB_GRANT_TIMESERIES, pTable->numOfColumns - 1); @@ -146,18 +151,31 @@ static int32_t mnodeChildTableActionInsert(SSdbRow *pRow) { } if (pDb) mnodeAddTableIntoDb(pDb); - if (pVgroup) mnodeAddTableIntoVgroup(pVgroup, pTable); + if (pVgroup) { + if (mnodeAddTableIntoVgroup(pVgroup, pTable, pRow->pMsg == NULL) != 0) { + mError("table:%s, vgId:%d tid:%d, failed to perform insert action, uid:%" PRIu64 " suid:%" PRIu64, + pTable->info.tableId, pTable->vgId, pTable->tid, pTable->uid, pTable->suid); + code = -1; + } + } mnodeDecVgroupRef(pVgroup); mnodeDecDbRef(pDb); mnodeDecAcctRef(pAcct); - return TSDB_CODE_SUCCESS; + if (code == 0) { + mTrace("table:%s, vgId:%d tid:%d, perform insert action, uid:%" PRIu64 " suid:%" PRIu64, pTable->info.tableId, + pTable->vgId, pTable->tid, pTable->uid, pTable->suid); + } + + return code; } static int32_t mnodeChildTableActionDelete(SSdbRow *pRow) { SCTableObj *pTable = pRow->pObj; if (pTable->vgId == 0) { + mError("table:%s, vgId:%d tid:%d, failed to perform delete action, uid:%" PRIu64 " suid:%" PRIu64, + pTable->info.tableId, pTable->vgId, pTable->tid, pTable->uid, pTable->suid); return TSDB_CODE_MND_VGROUP_NOT_EXIST; } @@ -188,6 +206,8 @@ static int32_t mnodeChildTableActionDelete(SSdbRow *pRow) { mnodeDecDbRef(pDb); mnodeDecAcctRef(pAcct); + mTrace("table:%s, vgId:%d tid:%d, perform delete action, uid:%" PRIu64 " suid:%" PRIu64, pTable->info.tableId, + pTable->vgId, pTable->tid, pTable->uid, pTable->suid); return TSDB_CODE_SUCCESS; } @@ -399,13 +419,13 @@ static void mnodeAddTableIntoStable(SSTableObj *pStable, SCTableObj *pCtable) { if (pStable->vgHash == NULL) { pStable->vgHash = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), true, HASH_ENTRY_LOCK); - mDebug("table:%s, create hash:%p", pStable->info.tableId, pStable->vgHash); + mDebug("stable:%s, create vgId hash:%p", pStable->info.tableId, pStable->vgHash); } if (pStable->vgHash != NULL) { if (taosHashGet(pStable->vgHash, &pCtable->vgId, sizeof(pCtable->vgId)) == NULL) { taosHashPut(pStable->vgHash, &pCtable->vgId, sizeof(pCtable->vgId), &pCtable->vgId, sizeof(pCtable->vgId)); - mDebug("table:%s, vgId:%d is put into stable hash:%p, sizeOfVgList:%d", pStable->info.tableId, pCtable->vgId, + mDebug("stable:%s, vgId:%d is put into stable vgId hash:%p, sizeOfVgList:%d", pStable->info.tableId, pCtable->vgId, pStable->vgHash, taosHashGetSize(pStable->vgHash)); } } @@ -443,19 +463,21 @@ static int32_t mnodeSuperTableActionDestroy(SSdbRow *pRow) { static int32_t mnodeSuperTableActionInsert(SSdbRow *pRow) { SSTableObj *pStable = pRow->pObj; - SDbObj *pDb = mnodeGetDbByTableName(pStable->info.tableId); + SDbObj * pDb = mnodeGetDbByTableName(pStable->info.tableId); if (pDb != NULL && pDb->status == TSDB_DB_STATUS_READY) { mnodeAddSuperTableIntoDb(pDb); } mnodeDecDbRef(pDb); taosHashPut(tsSTableUidHash, &pStable->uid, sizeof(int64_t), &pStable, sizeof(int64_t)); + + mTrace("stable:%s, perform insert action, uid:%" PRIu64, pStable->info.tableId, pStable->uid); return TSDB_CODE_SUCCESS; } static int32_t mnodeSuperTableActionDelete(SSdbRow *pRow) { SSTableObj *pStable = pRow->pObj; - SDbObj *pDb = mnodeGetDbByTableName(pStable->info.tableId); + SDbObj * pDb = mnodeGetDbByTableName(pStable->info.tableId); if (pDb != NULL) { mnodeRemoveSuperTableFromDb(pDb); mnodeDropAllChildTablesInStable((SSTableObj *)pStable); @@ -463,6 +485,8 @@ static int32_t mnodeSuperTableActionDelete(SSdbRow *pRow) { mnodeDecDbRef(pDb); taosHashRemove(tsSTableUidHash, &pStable->uid, sizeof(int64_t)); + + mTrace("stable:%s, perform delete action, uid:%" PRIu64, pStable->info.tableId, pStable->uid); return TSDB_CODE_SUCCESS; } @@ -1888,15 +1912,14 @@ static int32_t mnodeDoCreateChildTableCb(SMnodeMsg *pMsg, int32_t code) { pMsg->rpcMsg.handle); if (pMsg->pBatchMasterMsg) { - ++pMsg->pBatchMasterMsg->successed; - if (pMsg->pBatchMasterMsg->successed + pMsg->pBatchMasterMsg->received - >= pMsg->pBatchMasterMsg->expected) { - dnodeSendRpcMWriteRsp(pMsg->pBatchMasterMsg, code); - } + ++pMsg->pBatchMasterMsg->successed; + if (pMsg->pBatchMasterMsg->successed + pMsg->pBatchMasterMsg->received >= pMsg->pBatchMasterMsg->expected) { + dnodeSendRpcMWriteRsp(pMsg->pBatchMasterMsg, code); + } - mnodeDestroySubMsg(pMsg); + mnodeDestroySubMsg(pMsg); - return TSDB_CODE_MND_ACTION_IN_PROGRESS; + return TSDB_CODE_MND_ACTION_IN_PROGRESS; } dnodeSendRpcMWriteRsp(pMsg, TSDB_CODE_SUCCESS); @@ -1911,9 +1934,8 @@ static int32_t mnodeDoCreateChildTableCb(SMnodeMsg *pMsg, int32_t code) { if (pMsg->pBatchMasterMsg) { ++pMsg->pBatchMasterMsg->received; pMsg->pBatchMasterMsg->code = code; - if (pMsg->pBatchMasterMsg->successed + pMsg->pBatchMasterMsg->received - >= pMsg->pBatchMasterMsg->expected) { - dnodeSendRpcMWriteRsp(pMsg->pBatchMasterMsg, code); + if (pMsg->pBatchMasterMsg->successed + pMsg->pBatchMasterMsg->received >= pMsg->pBatchMasterMsg->expected) { + dnodeSendRpcMWriteRsp(pMsg->pBatchMasterMsg, code); } mnodeDestroySubMsg(pMsg); @@ -2662,9 +2684,8 @@ static void mnodeProcessCreateChildTableRsp(SRpcMsg *rpcMsg) { if (pMsg->pBatchMasterMsg) { ++pMsg->pBatchMasterMsg->successed; - if (pMsg->pBatchMasterMsg->successed + pMsg->pBatchMasterMsg->received - >= pMsg->pBatchMasterMsg->expected) { - dnodeSendRpcMWriteRsp(pMsg->pBatchMasterMsg, rpcMsg->code); + if (pMsg->pBatchMasterMsg->successed + pMsg->pBatchMasterMsg->received >= pMsg->pBatchMasterMsg->expected) { + dnodeSendRpcMWriteRsp(pMsg->pBatchMasterMsg, rpcMsg->code); } mnodeDestroySubMsg(pMsg); diff --git a/src/mnode/src/mnodeVgroup.c b/src/mnode/src/mnodeVgroup.c index 008a655597f0934a1281ca0e5ccedd84d33bc1f9..fdbf7ae398e10d754b82d01a002f79130071d3c7 100644 --- a/src/mnode/src/mnodeVgroup.c +++ b/src/mnode/src/mnodeVgroup.c @@ -443,6 +443,7 @@ int32_t mnodeGetAvailableVgroup(SMnodeMsg *pMsg, SVgObj **ppVgroup, int32_t *pSi mDebug("msg:%p, app:%p db:%s, no enough sid in vgId:%d", pMsg, pMsg->rpcMsg.ahandle, pDb->name, pVgroup->vgId); continue; } + mTrace("vgId:%d, alloc tid:%d", pVgroup->vgId, sid); *pSid = sid; *ppVgroup = pVgroup; @@ -507,6 +508,7 @@ int32_t mnodeGetAvailableVgroup(SMnodeMsg *pMsg, SVgObj **ppVgroup, int32_t *pSi pDb->vgListIndex = 0; pthread_mutex_unlock(&pDb->mutex); + mTrace("vgId:%d, alloc tid:%d", pVgroup->vgId, sid); return TSDB_CODE_SUCCESS; } @@ -832,26 +834,37 @@ static int32_t mnodeRetrieveVgroups(SShowObj *pShow, char *data, int32_t rows, v return numOfRows; } -void mnodeAddTableIntoVgroup(SVgObj *pVgroup, SCTableObj *pTable) { +int32_t mnodeAddTableIntoVgroup(SVgObj *pVgroup, SCTableObj *pTable, bool needCheck) { int32_t idPoolSize = taosIdPoolMaxSize(pVgroup->idPool); if (pTable->tid > idPoolSize) { mnodeAllocVgroupIdPool(pVgroup); } if (pTable->tid >= 1) { - taosIdPoolMarkStatus(pVgroup->idPool, pTable->tid); - pVgroup->numOfTables++; - // The create vgroup message may be received later than the create table message - // and the writing order in sdb is therefore uncertain - // which will cause the reference count of the vgroup to be incorrect when restarting - // mnodeIncVgroupRef(pVgroup); + if (taosIdPoolMarkStatus(pVgroup->idPool, pTable->tid) || !needCheck) { + pVgroup->numOfTables++; + mTrace("table:%s, vgId:%d tid:%d, mark tid used, uid:%" PRIu64, pTable->info.tableId, pTable->vgId, pTable->tid, + pTable->uid); + // The create vgroup message may be received later than the create table message + // and the writing order in sdb is therefore uncertain + // which will cause the reference count of the vgroup to be incorrect when restarting + // mnodeIncVgroupRef(pVgroup); + } else { + mError("table:%s, vgId:%d tid:%d, failed to mark tid, uid:%" PRIu64, pTable->info.tableId, pTable->vgId, + pTable->tid, pTable->uid); + return -1; + } } + + return 0; } void mnodeRemoveTableFromVgroup(SVgObj *pVgroup, SCTableObj *pTable) { if (pTable->tid >= 1) { taosFreeId(pVgroup->idPool, pTable->tid); pVgroup->numOfTables--; + mTrace("table:%s, vgId:%d tid:%d, put tid back uid:%" PRIu64, pTable->info.tableId, pTable->vgId, pTable->tid, + pTable->uid); // The create vgroup message may be received later than the create table message // and the writing order in sdb is therefore uncertain // which will cause the reference count of the vgroup to be incorrect when restarting diff --git a/src/os/inc/osSysinfo.h b/src/os/inc/osSysinfo.h index 25c9c97b1e888d9f4371eea67a16efea728294a4..895b5dd499196703f954c5621241cd2d1e2fabec 100644 --- a/src/os/inc/osSysinfo.h +++ b/src/os/inc/osSysinfo.h @@ -23,6 +23,7 @@ extern "C" { // TAOS_OS_FUNC_SYSINFO typedef struct { int64_t tsize; + int64_t used; int64_t avail; } SysDiskSize; diff --git a/src/os/src/darwin/darwinSysInfo.c b/src/os/src/darwin/darwinSysInfo.c index 6af6285f56aa33200379aa52f94e0ab39de80644..55c07766b307f57fbc8fd76798ac880efef8a4de 100644 --- a/src/os/src/darwin/darwinSysInfo.c +++ b/src/os/src/darwin/darwinSysInfo.c @@ -138,6 +138,8 @@ void taosPrintOsInfo() { // uInfo(" os streamMax: %" PRId64, tsStreamMax); uInfo(" os numOfCores: %d", tsNumOfCores); uInfo(" os totalDisk: %f(GB)", tsTotalDataDirGB); + uInfo(" os usedDisk: %f(GB)", tsUsedDataDirGB); + uInfo(" os availDisk: %f(GB)", tsAvailDataDirGB); uInfo(" os totalMemory: %d(MB)", tsTotalMemoryMB); struct utsname buf; @@ -222,6 +224,7 @@ int32_t taosGetDiskSize(char *dataDir, SysDiskSize *diskSize) { } else { diskSize->tsize = info.f_blocks * info.f_frsize; diskSize->avail = info.f_bavail * info.f_frsize; + diskSize->used = (info.f_blocks - info.f_bfree) * info.f_frsize; return 0; } } diff --git a/src/os/src/detail/osSysinfo.c b/src/os/src/detail/osSysinfo.c index f12ec93bf7725d013fa2f53d148de783e854d3bf..c0d46878a821db3490ccf54f8e303e37e52f84aa 100644 --- a/src/os/src/detail/osSysinfo.c +++ b/src/os/src/detail/osSysinfo.c @@ -326,6 +326,7 @@ int32_t taosGetDiskSize(char *dataDir, SysDiskSize *diskSize) { } else { diskSize->tsize = info.f_blocks * info.f_frsize; diskSize->avail = info.f_bavail * info.f_frsize; + diskSize->used = (info.f_blocks - info.f_bfree) * info.f_frsize; return 0; } } @@ -506,6 +507,8 @@ void taosPrintOsInfo() { uInfo(" os streamMax: %" PRId64, tsStreamMax); uInfo(" os numOfCores: %d", tsNumOfCores); uInfo(" os totalDisk: %f(GB)", tsTotalDataDirGB); + uInfo(" os usedDisk: %f(GB)", tsUsedDataDirGB); + uInfo(" os availDisk: %f(GB)", tsAvailDataDirGB); uInfo(" os totalMemory: %d(MB)", tsTotalMemoryMB); struct utsname buf; diff --git a/src/os/src/windows/wSysinfo.c b/src/os/src/windows/wSysinfo.c index 48fb3c13a8fe80332cfc26f893d6a21f1cdd902e..8a81e3079a17e013372ebd7de0facb6b49a99c7b 100644 --- a/src/os/src/windows/wSysinfo.c +++ b/src/os/src/windows/wSysinfo.c @@ -136,7 +136,8 @@ int32_t taosGetDiskSize(char *dataDir, SysDiskSize *diskSize) { (PULARGE_INTEGER)&i64FreeBytes); if (fResult) { diskSize->tsize = (int64_t)(i64TotalBytes); - diskSize->avail = (int64_t)(i64FreeBytes); + diskSize->avail = (int64_t)(i64FreeBytesToCaller); + diskSize->used = (int64_t)(i64TotalBytes - i64FreeBytes); return 0; } else { uError("failed to get disk size, dataDir:%s errno:%s", tsDataDir, strerror(errno)); @@ -205,6 +206,8 @@ void taosGetSystemInfo() { void taosPrintOsInfo() { uInfo(" os numOfCores: %d", tsNumOfCores); uInfo(" os totalDisk: %f(GB)", tsTotalDataDirGB); + uInfo(" os usedDisk: %f(GB)", tsUsedDataDirGB); + uInfo(" os availDisk: %f(GB)", tsAvailDataDirGB); uInfo(" os totalMemory: %d(MB)", tsTotalMemoryMB); uInfo("=================================="); } diff --git a/src/plugins/http/src/httpParser.c b/src/plugins/http/src/httpParser.c index 4ce54a8ee630579499fe498e5d8c155bb5916eea..18cea56cfe6e0d1e8a4f833a920e5473536e1c7e 100644 --- a/src/plugins/http/src/httpParser.c +++ b/src/plugins/http/src/httpParser.c @@ -110,7 +110,7 @@ static void httpCleanupString(HttpString *str) { static int32_t httpAppendString(HttpString *str, const char *s, int32_t len) { if (str->size == 0) { str->pos = 0; - str->size = 64; + str->size = len + 1; str->str = malloc(str->size); } else if (str->pos + len + 1 >= str->size) { str->size += len; @@ -715,10 +715,12 @@ static int32_t httpParserOnVersion(HttpParser *parser, HTTP_PARSER_STATE state, if (parser->method) { ok = httpOnRequestLine(parser, parser->method, parser->target, parser->version); + /* if (parser->target) { free(parser->target); parser->target = NULL; } + */ } httpClearString(&parser->str); diff --git a/src/plugins/monitor/src/monMain.c b/src/plugins/monitor/src/monMain.c index ac80ad62509a755b5b86b65593a90a2730120df8..94af8e3ecd6623f791f6ad797176a59ca2605c2b 100644 --- a/src/plugins/monitor/src/monMain.c +++ b/src/plugins/monitor/src/monMain.c @@ -292,7 +292,7 @@ static int32_t monBuildCpuSql(char *sql) { // unit is GB static int32_t monBuildDiskSql(char *sql) { - return sprintf(sql, ", %f, %d", (tsTotalDataDirGB - tsAvailDataDirGB), (int32_t)tsTotalDataDirGB); + return sprintf(sql, ", %f, %d", tsUsedDataDirGB, (int32_t)tsTotalDataDirGB); } // unit is Kb diff --git a/src/query/inc/qExecutor.h b/src/query/inc/qExecutor.h index 3615c32a905a87f7a5ed604bcd2e86baa3a5ee77..5ff574ec673119d3837ef57d119c778e6cd8c285 100644 --- a/src/query/inc/qExecutor.h +++ b/src/query/inc/qExecutor.h @@ -308,6 +308,7 @@ enum { typedef struct SQInfo { void* signature; + uint64_t qId; int32_t code; // error code to returned to client int64_t owner; // if it is in execution @@ -429,7 +430,7 @@ int32_t createIndirectQueryFuncExprFromMsg(SQueryTableMsg *pQueryMsg, int32_t nu SSqlGroupbyExpr *createGroupbyExprFromMsg(SQueryTableMsg *pQueryMsg, SColIndex *pColIndex, int32_t *code); SQInfo *createQInfoImpl(SQueryTableMsg *pQueryMsg, SSqlGroupbyExpr *pGroupbyExpr, SExprInfo *pExprs, - SExprInfo *pSecExprs, STableGroupInfo *pTableGroupInfo, SColumnInfo* pTagCols, bool stableQuery, char* sql); + SExprInfo *pSecExprs, STableGroupInfo *pTableGroupInfo, SColumnInfo* pTagCols, bool stableQuery, char* sql, uint64_t *qId); int32_t initQInfo(SQueryTableMsg *pQueryMsg, void *tsdb, int32_t vgId, SQInfo *pQInfo, SQueryParam* param, bool isSTable); void freeColumnFilterInfo(SColumnFilterInfo* pFilter, int32_t numOfFilters); diff --git a/src/query/src/qExecutor.c b/src/query/src/qExecutor.c index 8a1c86c389d25d6361d03490de3f508eb4f85cd2..d1d184aa751d9bf079b30f5c344fd3b327562056 100644 --- a/src/query/src/qExecutor.c +++ b/src/query/src/qExecutor.c @@ -98,6 +98,9 @@ static UNUSED_FUNC void* u_realloc(void* p, size_t __size) { #define GET_NUM_OF_TABLEGROUP(q) taosArrayGetSize((q)->tableqinfoGroupInfo.pGroupList) #define QUERY_IS_INTERVAL_QUERY(_q) ((_q)->interval.interval > 0) + +uint64_t queryHandleId = 0; + int32_t getMaximumIdleDurationSec() { return tsShellActivityTimer * 2; } @@ -1837,7 +1840,7 @@ static void doFreeQueryHandle(SQueryRuntimeEnv* pRuntimeEnv) { pRuntimeEnv->pQueryHandle = NULL; SMemRef* pMemRef = &pQuery->memRef; - assert(pMemRef->ref == 0 && pMemRef->imem == NULL && pMemRef->mem == NULL); + assert(pMemRef->ref == 0 && pMemRef->snapshot.imem == NULL && pMemRef->snapshot.mem == NULL); } static void teardownQueryRuntimeEnv(SQueryRuntimeEnv *pRuntimeEnv) { @@ -2747,7 +2750,10 @@ static void doSetTagValueInParam(void* pTable, int32_t tagColId, tVariant *tag, } if (type == TSDB_DATA_TYPE_BINARY || type == TSDB_DATA_TYPE_NCHAR) { - tVariantCreateFromBinary(tag, varDataVal(val), varDataLen(val), type); + int32_t maxLen = bytes - VARSTR_HEADER_SIZE; + int32_t len = (varDataLen(val) > maxLen)? maxLen:varDataLen(val); + tVariantCreateFromBinary(tag, varDataVal(val), len, type); + //tVariantCreateFromBinary(tag, varDataVal(val), varDataLen(val), type); } else { tVariantCreateFromBinary(tag, val, bytes, type); } @@ -6116,9 +6122,13 @@ void setResultBufSize(SQuery* pQuery, SRspResultInfo* pResultInfo) { pResultInfo->total = 0; } +FORCE_INLINE bool checkQIdEqual(void *qHandle, uint64_t qId) { + return ((SQInfo *)qHandle)->qId == qId; +} + SQInfo* createQInfoImpl(SQueryTableMsg* pQueryMsg, SSqlGroupbyExpr* pGroupbyExpr, SExprInfo* pExprs, SExprInfo* pSecExprs, STableGroupInfo* pTableGroupInfo, SColumnInfo* pTagCols, bool stableQuery, - char* sql) { + char* sql, uint64_t *qId) { int16_t numOfCols = pQueryMsg->numOfCols; int16_t numOfOutput = pQueryMsg->numOfOutput; @@ -6259,7 +6269,9 @@ SQInfo* createQInfoImpl(SQueryTableMsg* pQueryMsg, SSqlGroupbyExpr* pGroupbyExpr // todo refactor pQInfo->query.queryBlockDist = (numOfOutput == 1 && pExprs[0].base.colInfo.colId == TSDB_BLOCK_DIST_COLUMN_INDEX); - qDebug("qmsg:%p QInfo:%p created", pQueryMsg, pQInfo); + pQInfo->qId = atomic_add_fetch_64(&queryHandleId, 1); + *qId = pQInfo->qId; + qDebug("qmsg:%p QInfo:%" PRIu64 "-%p created", pQueryMsg, pQInfo->qId, pQInfo); return pQInfo; _cleanup_qinfo: @@ -6549,8 +6561,15 @@ static void doSetTagValueToResultBuf(char* output, const char* val, int16_t type return; } - if (type == TSDB_DATA_TYPE_BINARY || type == TSDB_DATA_TYPE_NCHAR) { - memcpy(output, val, varDataTLen(val)); + if (IS_VAR_DATA_TYPE(type)) { + // Binary data overflows for sort of unknown reasons. Let trim the overflow data + if (varDataTLen(val) > bytes) { + int32_t len = bytes - VARSTR_HEADER_SIZE; // remain available space + memcpy(varDataVal(output), varDataVal(val), len); + varDataSetLen(output, len); + } else { + varDataCopy(output, val); + } } else { memcpy(output, val, bytes); } diff --git a/src/query/src/queryMain.c b/src/query/src/queryMain.c index d35d4a5ff859899a6c9521ca0918788307675317..bba6a10df333d792fa0467548199a411ccd02e43 100644 --- a/src/query/src/queryMain.c +++ b/src/query/src/queryMain.c @@ -68,7 +68,7 @@ void freeParam(SQueryParam *param) { tfree(param->prevResult); } -int32_t qCreateQueryInfo(void* tsdb, int32_t vgId, SQueryTableMsg* pQueryMsg, qinfo_t* pQInfo) { +int32_t qCreateQueryInfo(void* tsdb, int32_t vgId, SQueryTableMsg* pQueryMsg, qinfo_t* pQInfo, uint64_t *qId) { assert(pQueryMsg != NULL && tsdb != NULL); int32_t code = TSDB_CODE_SUCCESS; @@ -158,7 +158,7 @@ int32_t qCreateQueryInfo(void* tsdb, int32_t vgId, SQueryTableMsg* pQueryMsg, qi goto _over; } - (*pQInfo) = createQInfoImpl(pQueryMsg, param.pGroupbyExpr, param.pExprs, param.pSecExprs, &tableGroupInfo, param.pTagColumnInfo, isSTableQuery, param.sql); + (*pQInfo) = createQInfoImpl(pQueryMsg, param.pGroupbyExpr, param.pExprs, param.pSecExprs, &tableGroupInfo, param.pTagColumnInfo, isSTableQuery, param.sql, qId); param.sql = NULL; param.pExprs = NULL; @@ -430,7 +430,7 @@ void qQueryMgmtNotifyClosed(void* pQMgmt) { } SQueryMgmt* pQueryMgmt = pQMgmt; - qDebug("vgId:%d, set querymgmt closed, wait for all queries cancelled", pQueryMgmt->vgId); + qInfo("vgId:%d, set querymgmt closed, wait for all queries cancelled", pQueryMgmt->vgId); pthread_mutex_lock(&pQueryMgmt->lock); pQueryMgmt->closed = true; @@ -445,7 +445,7 @@ void qQueryMgmtReOpen(void *pQMgmt) { } SQueryMgmt *pQueryMgmt = pQMgmt; - qDebug("vgId:%d, set querymgmt reopen", pQueryMgmt->vgId); + qInfo("vgId:%d, set querymgmt reopen", pQueryMgmt->vgId); pthread_mutex_lock(&pQueryMgmt->lock); pQueryMgmt->closed = false; @@ -472,7 +472,7 @@ void qCleanupQueryMgmt(void* pQMgmt) { qDebug("vgId:%d, queryMgmt cleanup completed", vgId); } -void** qRegisterQInfo(void* pMgmt, uint64_t qInfo) { +void** qRegisterQInfo(void* pMgmt, uint64_t qId, uint64_t qInfo) { if (pMgmt == NULL) { terrno = TSDB_CODE_VND_INVALID_VGROUP_ID; return NULL; @@ -492,8 +492,7 @@ void** qRegisterQInfo(void* pMgmt, uint64_t qInfo) { terrno = TSDB_CODE_VND_INVALID_VGROUP_ID; return NULL; } else { - TSDB_CACHE_PTR_TYPE handleVal = (TSDB_CACHE_PTR_TYPE) qInfo; - void** handle = taosCachePut(pQueryMgmt->qinfoPool, &handleVal, sizeof(TSDB_CACHE_PTR_TYPE), &qInfo, sizeof(TSDB_CACHE_PTR_TYPE), + void** handle = taosCachePut(pQueryMgmt->qinfoPool, &qId, sizeof(qId), &qInfo, sizeof(TSDB_CACHE_PTR_TYPE), (getMaximumIdleDurationSec()*1000)); pthread_mutex_unlock(&pQueryMgmt->lock); diff --git a/src/sync/inc/syncInt.h b/src/sync/inc/syncInt.h index 91613ae35107f0a667798e730f6021687e3ced5e..2b87938474d9385e5d093dcf524be777eb22db27 100644 --- a/src/sync/inc/syncInt.h +++ b/src/sync/inc/syncInt.h @@ -117,6 +117,7 @@ typedef struct SSyncNode { FStartSyncFile startSyncFileFp; FStopSyncFile stopSyncFileFp; FGetVersion getVersionFp; + FResetVersion resetVersionFp; FSendFile sendFileFp; FRecvFile recvFileFp; pthread_mutex_t mutex; diff --git a/src/sync/src/syncMain.c b/src/sync/src/syncMain.c index 956ccdc0730e345d2e0ece3bcf45e4d0e9b3c14d..264bbf6b925b2244e38915326f7b9eaf9e8831ad 100644 --- a/src/sync/src/syncMain.c +++ b/src/sync/src/syncMain.c @@ -56,7 +56,7 @@ static void syncMonitorNodeRole(void *param, void *tmrId); static void syncProcessFwdAck(SSyncNode *pNode, SFwdInfo *pFwdInfo, int32_t code); static int32_t syncSaveFwdInfo(SSyncNode *pNode, uint64_t version, void *mhandle); static void syncRestartPeer(SSyncPeer *pPeer); -static int32_t syncForwardToPeerImpl(SSyncNode *pNode, void *data, void *mhandle, int32_t qtyp); +static int32_t syncForwardToPeerImpl(SSyncNode *pNode, void *data, void *mhandle, int32_t qtype, bool force); static SSyncPeer *syncAddPeer(SSyncNode *pNode, const SNodeInfo *pInfo); static void syncStartCheckPeerConn(SSyncPeer *pPeer); @@ -182,6 +182,7 @@ int64_t syncStart(const SSyncInfo *pInfo) { pNode->startSyncFileFp = pInfo->startSyncFileFp; pNode->stopSyncFileFp = pInfo->stopSyncFileFp; pNode->getVersionFp = pInfo->getVersionFp; + pNode->resetVersionFp = pInfo->resetVersionFp; pNode->sendFileFp = pInfo->sendFileFp; pNode->recvFileFp = pInfo->recvFileFp; @@ -377,24 +378,24 @@ int32_t syncReconfig(int64_t rid, const SSyncCfg *pNewCfg) { return 0; } -int32_t syncForwardToPeer(int64_t rid, void *data, void *mhandle, int32_t qtype) { +int32_t syncForwardToPeer(int64_t rid, void *data, void *mhandle, int32_t qtype, bool force) { if (rid <= 0) return 0; SSyncNode *pNode = syncAcquireNode(rid); if (pNode == NULL) return 0; - int32_t code = syncForwardToPeerImpl(pNode, data, mhandle, qtype); + int32_t code = syncForwardToPeerImpl(pNode, data, mhandle, qtype, force); syncReleaseNode(pNode); return code; } -void syncConfirmForward(int64_t rid, uint64_t version, int32_t code) { +void syncConfirmForward(int64_t rid, uint64_t version, int32_t code, bool force) { SSyncNode *pNode = syncAcquireNode(rid); if (pNode == NULL) return; SSyncPeer *pPeer = pNode->pMaster; - if (pPeer && pNode->quorum > 1) { + if (pPeer && (pNode->quorum > 1 || force)) { SFwdRsp rsp; syncBuildSyncFwdRsp(&rsp, pNode->vgId, version, code); @@ -1413,7 +1414,7 @@ static void syncMonitorFwdInfos(void *param, void *tmrId) { syncReleaseNode(pNode); } -static int32_t syncForwardToPeerImpl(SSyncNode *pNode, void *data, void *mhandle, int32_t qtype) { +static int32_t syncForwardToPeerImpl(SSyncNode *pNode, void *data, void *mhandle, int32_t qtype, bool force) { SSyncPeer *pPeer; SSyncHead *pSyncHead; SWalHead * pWalHead = data; @@ -1457,7 +1458,7 @@ static int32_t syncForwardToPeerImpl(SSyncNode *pNode, void *data, void *mhandle if (pPeer == NULL || pPeer->peerFd < 0) continue; if (pPeer->role != TAOS_SYNC_ROLE_SLAVE && pPeer->sstatus != TAOS_SYNC_STATUS_CACHE) continue; - if (pNode->quorum > 1 && code == 0) { + if ((pNode->quorum > 1 || force) && code == 0) { code = syncSaveFwdInfo(pNode, pWalHead->version, mhandle); if (code >= 0) code = 1; } diff --git a/src/sync/src/syncRestore.c b/src/sync/src/syncRestore.c index c0d66316cd5b802ddcaddf1015d4ceca1aa3b2c5..22d0a2758131f3880adc63f3777528c8e3d06ff9 100644 --- a/src/sync/src/syncRestore.c +++ b/src/sync/src/syncRestore.c @@ -238,6 +238,7 @@ static int32_t syncRestoreDataStepByStep(SSyncPeer *pPeer) { (*pNode->stopSyncFileFp)(pNode->vgId, fversion); nodeVersion = fversion; + if (pNode->resetVersionFp) (*pNode->resetVersionFp)(pNode->vgId, fversion); sInfo("%s, start to restore wal, fver:%" PRIu64, pPeer->id, nodeVersion); uint64_t wver = 0; diff --git a/src/sync/src/syncRetrieve.c b/src/sync/src/syncRetrieve.c index be4073760dfa551878301bc3bd4f2405b12b77cf..505ba68c41f2c2373dba3322b6ed31ba0ac853f1 100644 --- a/src/sync/src/syncRetrieve.c +++ b/src/sync/src/syncRetrieve.c @@ -29,7 +29,7 @@ static int32_t syncGetWalVersion(SSyncNode *pNode, SSyncPeer *pPeer) { uint64_t fver, wver; int32_t code = (*pNode->getVersionFp)(pNode->vgId, &fver, &wver); if (code != 0) { - sDebug("%s, vnode is commiting while retrieve, last wver:%" PRIu64, pPeer->id, pPeer->lastWalVer); + sInfo("%s, vnode is commiting while retrieve, last wver:%" PRIu64, pPeer->id, pPeer->lastWalVer); return -1; } @@ -41,12 +41,12 @@ static bool syncIsWalModified(SSyncNode *pNode, SSyncPeer *pPeer) { uint64_t fver, wver; int32_t code = (*pNode->getVersionFp)(pNode->vgId, &fver, &wver); if (code != 0) { - sDebug("%s, vnode is commiting while retrieve, last wver:%" PRIu64, pPeer->id, pPeer->lastWalVer); + sInfo("%s, vnode is commiting while retrieve, last wver:%" PRIu64, pPeer->id, pPeer->lastWalVer); return true; } if (wver != pPeer->lastWalVer) { - sDebug("%s, wal is modified while retrieve, wver:%" PRIu64 ", last:%" PRIu64, pPeer->id, wver, pPeer->lastWalVer); + sInfo("%s, wal is modified while retrieve, wver:%" PRIu64 ", last:%" PRIu64, pPeer->id, wver, pPeer->lastWalVer); return true; } @@ -57,7 +57,7 @@ static int32_t syncGetFileVersion(SSyncNode *pNode, SSyncPeer *pPeer) { uint64_t fver, wver; int32_t code = (*pNode->getVersionFp)(pNode->vgId, &fver, &wver); if (code != 0) { - sDebug("%s, vnode is commiting while get fver for retrieve, last fver:%" PRIu64, pPeer->id, pPeer->lastFileVer); + sInfo("%s, vnode is commiting while get fver for retrieve, last fver:%" PRIu64, pPeer->id, pPeer->lastFileVer); return -1; } @@ -69,13 +69,13 @@ static bool syncAreFilesModified(SSyncNode *pNode, SSyncPeer *pPeer) { uint64_t fver, wver; int32_t code = (*pNode->getVersionFp)(pNode->vgId, &fver, &wver); if (code != 0) { - sDebug("%s, vnode is commiting while retrieve, last fver:%" PRIu64, pPeer->id, pPeer->lastFileVer); + sInfo("%s, vnode is commiting while retrieve, last fver:%" PRIu64, pPeer->id, pPeer->lastFileVer); pPeer->fileChanged = 1; return true; } if (fver != pPeer->lastFileVer) { - sDebug("%s, files are modified while retrieve, fver:%" PRIu64 ", last:%" PRIu64, pPeer->id, fver, pPeer->lastFileVer); + sInfo("%s, files are modified while retrieve, fver:%" PRIu64 ", last:%" PRIu64, pPeer->id, fver, pPeer->lastFileVer); pPeer->fileChanged = 1; return true; } @@ -143,13 +143,13 @@ static int32_t syncReadOneWalRecord(int32_t sfd, SWalHead *pHead) { } if (ret == 0) { - sDebug("sfd:%d, read to the end of file, ret:%d", sfd, ret); + sInfo("sfd:%d, read to the end of file, ret:%d", sfd, ret); return 0; } if (ret != sizeof(SWalHead)) { // file is not at end yet, it shall be reloaded - sDebug("sfd:%d, a partial wal head is read out, ret:%d", sfd, ret); + sInfo("sfd:%d, a partial wal head is read out, ret:%d", sfd, ret); return 0; } @@ -163,7 +163,7 @@ static int32_t syncReadOneWalRecord(int32_t sfd, SWalHead *pHead) { if (ret != pHead->len) { // file is not at end yet, it shall be reloaded - sDebug("sfd:%d, a partial wal conetnt is read out, ret:%d", sfd, ret); + sInfo("sfd:%d, a partial wal conetnt is read out, ret:%d", sfd, ret); return 0; } @@ -184,7 +184,7 @@ static int64_t syncRetrieveLastWal(SSyncPeer *pPeer, char *name, uint64_t fversi return -1; } - sDebug("%s, retrieve last wal:%s, offset:%" PRId64 " fver:%" PRIu64, pPeer->id, name, offset, fversion); + sInfo("%s, retrieve last wal:%s, offset:%" PRId64 " fver:%" PRIu64, pPeer->id, name, offset, fversion); SWalHead *pHead = malloc(SYNC_MAX_SIZE); int64_t bytes = 0; @@ -198,7 +198,7 @@ static int64_t syncRetrieveLastWal(SSyncPeer *pPeer, char *name, uint64_t fversi if (code == 0) { code = bytes; - sDebug("%s, read to the end of wal, bytes:%" PRId64, pPeer->id, bytes); + sInfo("%s, read to the end of wal, bytes:%" PRId64, pPeer->id, bytes); break; } @@ -217,7 +217,7 @@ static int64_t syncRetrieveLastWal(SSyncPeer *pPeer, char *name, uint64_t fversi if (pHead->version >= fversion && fversion > 0) { code = 0; - sDebug("%s, retrieve wal finished, hver:%" PRIu64 " fver:%" PRIu64, pPeer->id, pHead->version, fversion); + sInfo("%s, retrieve wal finished, hver:%" PRIu64 " fver:%" PRIu64, pPeer->id, pHead->version, fversion); break; } } @@ -237,7 +237,7 @@ static int64_t syncProcessLastWal(SSyncPeer *pPeer, char *wname, int64_t index) // get full path to wal file snprintf(fname, sizeof(fname), "%s/%s", pNode->path, wname); - sDebug("%s, start to retrieve last wal:%s", pPeer->id, fname); + sInfo("%s, start to retrieve last wal:%s", pPeer->id, fname); while (1) { if (syncAreFilesModified(pNode, pPeer)) return -1; @@ -245,7 +245,7 @@ static int64_t syncProcessLastWal(SSyncPeer *pPeer, char *wname, int64_t index) int64_t bytes = syncRetrieveLastWal(pPeer, fname, fversion, offset); if (bytes < 0) { - sDebug("%s, failed to retrieve last wal, bytes:%" PRId64, pPeer->id, bytes); + sInfo("%s, failed to retrieve last wal, bytes:%" PRId64, pPeer->id, bytes); return bytes; } @@ -257,13 +257,13 @@ static int64_t syncProcessLastWal(SSyncPeer *pPeer, char *wname, int64_t index) if (fversion == 0) { pPeer->sstatus = TAOS_SYNC_STATUS_CACHE; // start to forward pkt fversion = nodeVersion; // must read data to fversion - sDebug("%s, set sstatus:%s and fver:%" PRIu64, pPeer->id, syncStatus[pPeer->sstatus], fversion); + sInfo("%s, set sstatus:%s and fver:%" PRIu64, pPeer->id, syncStatus[pPeer->sstatus], fversion); } } // if all data up to fversion is read out, it is over if (pPeer->sversion >= fversion && fversion > 0) { - sDebug("%s, data up to fver:%" PRIu64 " has been read out, bytes:%" PRId64 " sver:%" PRIu64, pPeer->id, fversion, bytes, + sInfo("%s, data up to fver:%" PRIu64 " has been read out, bytes:%" PRId64 " sver:%" PRIu64, pPeer->id, fversion, bytes, pPeer->sversion); return 0; } @@ -277,7 +277,7 @@ static int64_t syncProcessLastWal(SSyncPeer *pPeer, char *wname, int64_t index) // if bytes > 0, file is updated, or fversion is not reached but file still open, read again once = 1; offset += bytes; - sDebug("%s, continue retrieve last wal, bytes:%" PRId64 " offset:%" PRId64 " sver:%" PRIu64 " fver:%" PRIu64, pPeer->id, + sInfo("%s, continue retrieve last wal, bytes:%" PRId64 " offset:%" PRId64 " sver:%" PRIu64 " fver:%" PRIu64, pPeer->id, bytes, offset, pPeer->sversion, fversion); } @@ -303,7 +303,7 @@ static int64_t syncRetrieveWal(SSyncPeer *pPeer) { if (wname[0] == 0) { // no wal file code = 0; - sDebug("%s, no wal file anymore", pPeer->id); + sInfo("%s, no wal file anymore", pPeer->id); break; } @@ -320,12 +320,12 @@ static int64_t syncRetrieveWal(SSyncPeer *pPeer) { struct stat fstat; if (stat(fname, &fstat) < 0) { code = -1; - sDebug("%s, failed to stat wal:%s for retrieve since %s, code:0x%" PRIx64, pPeer->id, fname, strerror(errno), code); + sInfo("%s, failed to stat wal:%s for retrieve since %s, code:0x%" PRIx64, pPeer->id, fname, strerror(errno), code); break; } size = fstat.st_size; - sDebug("%s, retrieve wal:%s size:%d", pPeer->id, fname, size); + sInfo("%s, retrieve wal:%s size:%d", pPeer->id, fname, size); int32_t sfd = open(fname, O_RDONLY | O_BINARY); if (sfd < 0) { @@ -334,7 +334,7 @@ static int64_t syncRetrieveWal(SSyncPeer *pPeer) { break; } - code = (int32_t)taosSendFile(pPeer->syncFd, sfd, NULL, size); + code = taosSendFile(pPeer->syncFd, sfd, NULL, size); close(sfd); if (code < 0) { sError("%s, failed to send wal:%s for retrieve since %s, code:0x%" PRIx64, pPeer->id, fname, strerror(errno), code); @@ -374,7 +374,7 @@ static int32_t syncRetrieveFirstPkt(SSyncPeer *pPeer) { sError("%s, failed to send sync-data msg since %s, tranId:%u", pPeer->id, strerror(errno), msg.tranId); return -1; } - sDebug("%s, send sync-data msg to peer, tranId:%u", pPeer->id, msg.tranId); + sInfo("%s, send sync-data msg to peer, tranId:%u", pPeer->id, msg.tranId); SSyncRsp rsp; if (taosReadMsg(pPeer->syncFd, &rsp, sizeof(SSyncRsp)) != sizeof(SSyncRsp)) { @@ -382,7 +382,7 @@ static int32_t syncRetrieveFirstPkt(SSyncPeer *pPeer) { return -1; } - sDebug("%s, recv sync-data rsp from peer, tranId:%u rsp-tranId:%u", pPeer->id, msg.tranId, rsp.tranId); + sInfo("%s, recv sync-data rsp from peer, tranId:%u rsp-tranId:%u", pPeer->id, msg.tranId, rsp.tranId); return 0; } diff --git a/src/tfs/inc/tfsint.h b/src/tfs/inc/tfsint.h index fa4cd597237a957064e1cca74fe089051db450c6..619ef6df73444934a949955af41c85f359660116 100644 --- a/src/tfs/inc/tfsint.h +++ b/src/tfs/inc/tfsint.h @@ -41,6 +41,7 @@ extern int fsDebugFlag; // tdisk.c ====================================================== typedef struct { int64_t size; + int64_t used; int64_t free; } SDiskMeta; @@ -56,6 +57,7 @@ typedef struct SDisk { #define DISK_DIR(pd) ((pd)->dir) #define DISK_META(pd) ((pd)->dmeta) #define DISK_SIZE(pd) ((pd)->dmeta.size) +#define DISK_USED_SIZE(pd) ((pd)->dmeta.used) #define DISK_FREE_SIZE(pd) ((pd)->dmeta.free) SDisk *tfsNewDisk(int level, int id, const char *dir); @@ -65,6 +67,7 @@ int tfsUpdateDiskInfo(SDisk *pDisk); // ttier.c ====================================================== typedef struct { int64_t size; + int64_t used; int64_t free; int16_t nAvailDisks; // # of Available disks } STierMeta; @@ -96,4 +99,4 @@ void tfsPosNextId(STier *pTier); } #endif -#endif \ No newline at end of file +#endif diff --git a/src/tfs/src/tdisk.c b/src/tfs/src/tdisk.c index 37798d3a886b443a20703747ad9a99e26c1502b7..22601e48c3607fca53c91ea00b17551c791302bd 100644 --- a/src/tfs/src/tdisk.c +++ b/src/tfs/src/tdisk.c @@ -27,7 +27,7 @@ SDisk *tfsNewDisk(int level, int id, const char *dir) { pDisk->level = level; pDisk->id = id; - strncpy(pDisk->dir, dir, TSDB_FILENAME_LEN); + tstrncpy(pDisk->dir, dir, TSDB_FILENAME_LEN); return pDisk; } @@ -52,6 +52,7 @@ int tfsUpdateDiskInfo(SDisk *pDisk) { } pDisk->dmeta.size = diskSize.tsize; + pDisk->dmeta.used = diskSize.used; pDisk->dmeta.free = diskSize.avail; return code; diff --git a/src/tfs/src/tfs.c b/src/tfs/src/tfs.c index 7b7c9b6127db0f1c8cabab35254ba4a128e05f05..f78535b8ed5bd17cb232fbcbc73f9831db099547 100644 --- a/src/tfs/src/tfs.c +++ b/src/tfs/src/tfs.c @@ -134,6 +134,7 @@ void tfsUpdateInfo(SFSMeta *pFSMeta) { tfsUpdateTierInfo(pTier, &tierMeta); pFSMeta->tsize += tierMeta.size; pFSMeta->avail += tierMeta.free; + pFSMeta->used += tierMeta.used; } tfsLock(); @@ -186,7 +187,7 @@ void tfsInitFile(TFILE *pf, int level, int id, const char *bname) { pf->level = level; pf->id = id; - strncpy(pf->rname, bname, TSDB_FILENAME_LEN); + tstrncpy(pf->rname, bname, TSDB_FILENAME_LEN); char tmpName[TMPNAME_LEN] = {0}; snprintf(tmpName, TMPNAME_LEN, "%s/%s", DISK_DIR(pDisk), bname); @@ -229,15 +230,15 @@ void *tfsDecodeFile(void *buf, TFILE *pf) { void tfsbasename(const TFILE *pf, char *dest) { char tname[TSDB_FILENAME_LEN] = "\0"; - strncpy(tname, pf->aname, TSDB_FILENAME_LEN); - strncpy(dest, basename(tname), TSDB_FILENAME_LEN); + tstrncpy(tname, pf->aname, TSDB_FILENAME_LEN); + tstrncpy(dest, basename(tname), TSDB_FILENAME_LEN); } void tfsdirname(const TFILE *pf, char *dest) { char tname[TSDB_FILENAME_LEN] = "\0"; - strncpy(tname, pf->aname, TSDB_FILENAME_LEN); - strncpy(dest, dirname(tname), TSDB_FILENAME_LEN); + tstrncpy(tname, pf->aname, TSDB_FILENAME_LEN); + tstrncpy(dest, dirname(tname), TSDB_FILENAME_LEN); } // DIR APIs ==================================== @@ -343,7 +344,7 @@ TDIR *tfsOpendir(const char *rname) { } tfsInitDiskIter(&(tdir->iter)); - strncpy(tdir->dirname, rname, TSDB_FILENAME_LEN); + tstrncpy(tdir->dirname, rname, TSDB_FILENAME_LEN); if (tfsOpendirImpl(tdir) < 0) { free(tdir); @@ -585,6 +586,7 @@ void taosGetDisk() { if (tscEmbedded) { tfsUpdateInfo(&fsMeta); tsTotalDataDirGB = (float)(fsMeta.tsize / unit); + tsUsedDataDirGB = (float)(fsMeta.used / unit); tsAvailDataDirGB = (float)(fsMeta.avail / unit); } diff --git a/src/tfs/src/ttier.c b/src/tfs/src/ttier.c index 2dce0c31949a145bdafd111a63e96c2b83a7140c..3b19797acf67f36a31c30dccefc0c985512f510b 100644 --- a/src/tfs/src/ttier.c +++ b/src/tfs/src/ttier.c @@ -100,6 +100,7 @@ void tfsUpdateTierInfo(STier *pTier, STierMeta *pTierMeta) { continue; } pTierMeta->size += DISK_SIZE(DISK_AT_TIER(pTier, id)); + pTierMeta->used += DISK_USED_SIZE(DISK_AT_TIER(pTier, id)); pTierMeta->free += DISK_FREE_SIZE(DISK_AT_TIER(pTier, id)); pTierMeta->nAvailDisks++; } @@ -166,4 +167,4 @@ void tfsPosNextId(STier *pTier) { } pTier->nextid = nextid; -} \ No newline at end of file +} diff --git a/src/tsdb/inc/tsdbFile.h b/src/tsdb/inc/tsdbFile.h index f1e2422e45b195e3874518132051ded67a776449..792efcdb2e0cc0245203d91e80369205cab74d56 100644 --- a/src/tsdb/inc/tsdbFile.h +++ b/src/tsdb/inc/tsdbFile.h @@ -334,7 +334,7 @@ static FORCE_INLINE int tsdbOpenDFileSet(SDFileSet* pSet, int flags) { static FORCE_INLINE void tsdbRemoveDFileSet(SDFileSet* pSet) { for (TSDB_FILE_T ftype = 0; ftype < TSDB_FILE_MAX; ftype++) { - tsdbRemoveDFile(TSDB_DFILE_IN_SET(pSet, ftype)); + (void)tsdbRemoveDFile(TSDB_DFILE_IN_SET(pSet, ftype)); } } diff --git a/src/tsdb/inc/tsdbMemTable.h b/src/tsdb/inc/tsdbMemTable.h index bd64ed4a5238b3b8b60716d8732d59d27218c639..6046274af40b855ac25dba6e220bf3bcfdc1dcca 100644 --- a/src/tsdb/inc/tsdbMemTable.h +++ b/src/tsdb/inc/tsdbMemTable.h @@ -31,29 +31,14 @@ typedef struct { SSkipListIterator *pIter; } SCommitIter; -typedef struct { +struct STableData { uint64_t uid; TSKEY keyFirst; TSKEY keyLast; int64_t numOfRows; SSkipList* pData; T_REF_DECLARE() -} STableData; - -typedef struct { - T_REF_DECLARE() - SRWLatch latch; - TSKEY keyFirst; - TSKEY keyLast; - int64_t numOfRows; - int32_t maxTables; - STableData** tData; - SList* actList; - SList* extraBuffList; - SList* bufBlockList; - int64_t pointsAdd; // TODO - int64_t storageAdd; // TODO -} SMemTable; +}; enum { TSDB_UPDATE_META, TSDB_DROP_META }; @@ -77,8 +62,8 @@ typedef struct { int tsdbRefMemTable(STsdbRepo* pRepo, SMemTable* pMemTable); int tsdbUnRefMemTable(STsdbRepo* pRepo, SMemTable* pMemTable); -int tsdbTakeMemSnapshot(STsdbRepo* pRepo, SMemTable** pMem, SMemTable** pIMem, SArray* pATable); -void tsdbUnTakeMemSnapShot(STsdbRepo* pRepo, SMemTable* pMem, SMemTable* pIMem); +int tsdbTakeMemSnapshot(STsdbRepo* pRepo, SMemSnapshot* pSnapshot, SArray* pATable); +void tsdbUnTakeMemSnapShot(STsdbRepo* pRepo, SMemSnapshot* pSnapshot); void* tsdbAllocBytes(STsdbRepo* pRepo, int bytes); int tsdbAsyncCommit(STsdbRepo* pRepo); int tsdbLoadDataFromCache(STable* pTable, SSkipListIterator* pIter, TSKEY maxKey, int maxRowsToRead, SDataCols* pCols, diff --git a/src/tsdb/src/tsdbCommit.c b/src/tsdb/src/tsdbCommit.c index a1ed0796a6e628a55ebe81fd87396ada990b7e73..02dd2d1ca40a04dd1102f884bae528916b522272 100644 --- a/src/tsdb/src/tsdbCommit.c +++ b/src/tsdb/src/tsdbCommit.c @@ -164,7 +164,7 @@ static int tsdbCommitMeta(STsdbRepo *pRepo) { tsdbError("vgId:%d failed to update META record, uid %" PRIu64 " since %s", REPO_ID(pRepo), pAct->uid, tstrerror(terrno)); tsdbCloseMFile(&mf); - tsdbApplyMFileChange(&mf, pOMFile); + (void)tsdbApplyMFileChange(&mf, pOMFile); // TODO: need to reload metaCache return -1; } @@ -304,7 +304,7 @@ static int tsdbCommitTSData(STsdbRepo *pRepo) { SDFileSet *pSet = NULL; int fid; - memset(&commith, 0, sizeof(SMemTable *)); + memset(&commith, 0, sizeof(commith)); if (pMem->numOfRows <= 0) { // No memory data, just apply retention on each file on disk @@ -399,9 +399,9 @@ static void tsdbEndCommit(STsdbRepo *pRepo, int eno) { if (pRepo->appH.notifyStatus) pRepo->appH.notifyStatus(pRepo->appH.appH, TSDB_STATUS_COMMIT_OVER, eno); SMemTable *pIMem = pRepo->imem; - tsdbLockRepo(pRepo); + (void)tsdbLockRepo(pRepo); pRepo->imem = NULL; - tsdbUnlockRepo(pRepo); + (void)tsdbUnlockRepo(pRepo); tsdbUnRefMemTable(pRepo, pIMem); tsem_post(&(pRepo->readyToCommit)); } @@ -1136,12 +1136,12 @@ static int tsdbMoveBlock(SCommitH *pCommith, int bidx) { } static int tsdbCommitAddBlock(SCommitH *pCommith, const SBlock *pSupBlock, const SBlock *pSubBlocks, int nSubBlocks) { - if (taosArrayPush(pCommith->aSupBlk, pSupBlock) < 0) { + if (taosArrayPush(pCommith->aSupBlk, pSupBlock) == NULL) { terrno = TSDB_CODE_TDB_OUT_OF_MEMORY; return -1; } - if (pSubBlocks && taosArrayPushBatch(pCommith->aSubBlk, pSubBlocks, nSubBlocks) < 0) { + if (pSubBlocks && taosArrayPushBatch(pCommith->aSubBlk, pSubBlocks, nSubBlocks) == NULL) { terrno = TSDB_CODE_TDB_OUT_OF_MEMORY; return -1; } @@ -1379,7 +1379,7 @@ static int tsdbSetAndOpenCommitFile(SCommitH *pCommith, SDFileSet *pSet, int fid tstrerror(terrno)); tsdbCloseDFileSet(pWSet); - tsdbRemoveDFile(pWHeadf); + (void)tsdbRemoveDFile(pWHeadf); if (pCommith->isRFileSet) { tsdbCloseAndUnsetFSet(&(pCommith->readh)); return -1; diff --git a/src/tsdb/src/tsdbFS.c b/src/tsdb/src/tsdbFS.c index cbff4fbeaa6907c83b5836d6039cbfc23a62168b..a9e74cb229df24800bd920186113669164ca7dc7 100644 --- a/src/tsdb/src/tsdbFS.c +++ b/src/tsdb/src/tsdbFS.c @@ -380,7 +380,7 @@ static int tsdbSaveFSStatus(SFSStatus *pStatus, int vid) { if (taosWrite(fd, pBuf, fsheader.len) < fsheader.len) { terrno = TAOS_SYSTEM_ERROR(errno); close(fd); - remove(tfname); + (void)remove(tfname); taosTZfree(pBuf); return -1; } @@ -413,7 +413,7 @@ static void tsdbApplyFSTxnOnDisk(SFSStatus *pFrom, SFSStatus *pTo) { sizeTo = taosArrayGetSize(pTo->df); // Apply meta file change - tsdbApplyMFileChange(pFrom->pmf, pTo->pmf); + (void)tsdbApplyMFileChange(pFrom->pmf, pTo->pmf); // Apply SDFileSet change if (ifrom >= sizeFrom) { @@ -853,7 +853,7 @@ static int tsdbScanRootDir(STsdbRepo *pRepo) { continue; } - tfsremove(pf); + (void)tfsremove(pf); tsdbDebug("vgId:%d invalid file %s is removed", REPO_ID(pRepo), TFILE_NAME(pf)); } @@ -879,7 +879,7 @@ static int tsdbScanDataDir(STsdbRepo *pRepo) { tfsbasename(pf, bname); if (!tsdbIsTFileInFS(pfs, pf)) { - tfsremove(pf); + (void)tfsremove(pf); tsdbDebug("vgId:%d invalid file %s is removed", REPO_ID(pRepo), TFILE_NAME(pf)); } } @@ -939,7 +939,7 @@ static int tsdbRestoreMeta(STsdbRepo *pRepo) { if (strcmp(bname, tsdbTxnFname[TSDB_TXN_TEMP_FILE]) == 0) { // Skip current.t file tsdbInfo("vgId:%d file %s exists, remove it", REPO_ID(pRepo), TFILE_NAME(pf)); - tfsremove(pf); + (void)tfsremove(pf); continue; } @@ -1045,7 +1045,7 @@ static int tsdbRestoreDFileSet(STsdbRepo *pRepo) { int code = regexec(®ex, bname, 0, NULL, 0); if (code == 0) { - if (taosArrayPush(fArray, (void *)pf) < 0) { + if (taosArrayPush(fArray, (void *)pf) == NULL) { terrno = TSDB_CODE_TDB_OUT_OF_MEMORY; tfsClosedir(tdir); taosArrayDestroy(fArray); @@ -1055,7 +1055,7 @@ static int tsdbRestoreDFileSet(STsdbRepo *pRepo) { } else if (code == REG_NOMATCH) { // Not match tsdbInfo("vgId:%d invalid file %s exists, remove it", REPO_ID(pRepo), TFILE_NAME(pf)); - tfsremove(pf); + (void)tfsremove(pf); continue; } else { // Has other error diff --git a/src/tsdb/src/tsdbFile.c b/src/tsdb/src/tsdbFile.c index 8124a0e3b5984eecee1f0934a385be56d5bd3b61..ac33096aaeb2536a70b5226ce1b5bdb8856903b8 100644 --- a/src/tsdb/src/tsdbFile.c +++ b/src/tsdb/src/tsdbFile.c @@ -523,7 +523,7 @@ static int tsdbApplyDFileChange(SDFile *from, SDFile *to) { tsdbRollBackDFile(to); } } else { - tsdbRemoveDFile(from); + (void)tsdbRemoveDFile(from); } } } diff --git a/src/tsdb/src/tsdbMemTable.c b/src/tsdb/src/tsdbMemTable.c index 6818f2ed14c278bd5d203d0570f52772c159dcde..20ec426018a39e554fb03e9bb11399dbce1f3fcc 100644 --- a/src/tsdb/src/tsdbMemTable.c +++ b/src/tsdb/src/tsdbMemTable.c @@ -124,88 +124,80 @@ int tsdbUnRefMemTable(STsdbRepo *pRepo, SMemTable *pMemTable) { return 0; } -int tsdbTakeMemSnapshot(STsdbRepo *pRepo, SMemTable **pMem, SMemTable **pIMem, SArray *pATable) { - SMemTable *tmem; +int tsdbTakeMemSnapshot(STsdbRepo *pRepo, SMemSnapshot *pSnapshot, SArray *pATable) { + memset(pSnapshot, 0, sizeof(*pSnapshot)); - // Get snap object if (tsdbLockRepo(pRepo) < 0) return -1; - tmem = pRepo->mem; - *pIMem = pRepo->imem; - tsdbRefMemTable(pRepo, tmem); - tsdbRefMemTable(pRepo, *pIMem); + pSnapshot->omem = pRepo->mem; + pSnapshot->imem = pRepo->imem; + tsdbRefMemTable(pRepo, pRepo->mem); + tsdbRefMemTable(pRepo, pRepo->imem); if (tsdbUnlockRepo(pRepo) < 0) return -1; - // Copy mem objects and ref needed STableData - if (tmem) { - taosRLockLatch(&(tmem->latch)); + if (pSnapshot->omem) { + taosRLockLatch(&(pSnapshot->omem->latch)); - *pMem = (SMemTable *)calloc(1, sizeof(**pMem)); - if (*pMem == NULL) { - terrno = TSDB_CODE_TDB_OUT_OF_MEMORY; - taosRUnLockLatch(&(tmem->latch)); - tsdbUnRefMemTable(pRepo, tmem); - tsdbUnRefMemTable(pRepo, *pIMem); - *pMem = NULL; - *pIMem = NULL; - return -1; - } + pSnapshot->mem = &(pSnapshot->mtable); - (*pMem)->tData = (STableData **)calloc(tmem->maxTables, sizeof(STableData *)); - if ((*pMem)->tData == NULL) { + pSnapshot->mem->tData = (STableData **)calloc(pSnapshot->omem->maxTables, sizeof(STableData *)); + if (pSnapshot->mem->tData == NULL) { terrno = TSDB_CODE_TDB_OUT_OF_MEMORY; - taosRUnLockLatch(&(tmem->latch)); - free(*pMem); - tsdbUnRefMemTable(pRepo, tmem); - tsdbUnRefMemTable(pRepo, *pIMem); - *pMem = NULL; - *pIMem = NULL; + taosRUnLockLatch(&(pSnapshot->omem->latch)); + tsdbUnRefMemTable(pRepo, pSnapshot->omem); + tsdbUnRefMemTable(pRepo, pSnapshot->imem); + pSnapshot->mem = NULL; + pSnapshot->imem = NULL; + pSnapshot->omem = NULL; return -1; } - (*pMem)->keyFirst = tmem->keyFirst; - (*pMem)->keyLast = tmem->keyLast; - (*pMem)->numOfRows = tmem->numOfRows; - (*pMem)->maxTables = tmem->maxTables; + pSnapshot->mem->keyFirst = pSnapshot->omem->keyFirst; + pSnapshot->mem->keyLast = pSnapshot->omem->keyLast; + pSnapshot->mem->numOfRows = pSnapshot->omem->numOfRows; + pSnapshot->mem->maxTables = pSnapshot->omem->maxTables; for (size_t i = 0; i < taosArrayGetSize(pATable); i++) { STable * pTable = *(STable **)taosArrayGet(pATable, i); int32_t tid = TABLE_TID(pTable); - STableData *pTableData = (tid < tmem->maxTables) ? tmem->tData[tid] : NULL; + STableData *pTableData = (tid < pSnapshot->omem->maxTables) ? pSnapshot->omem->tData[tid] : NULL; if ((pTableData == NULL) || (TABLE_UID(pTable) != pTableData->uid)) continue; - (*pMem)->tData[tid] = tmem->tData[tid]; - T_REF_INC(tmem->tData[tid]); + pSnapshot->mem->tData[tid] = pTableData; + T_REF_INC(pTableData); } - taosRUnLockLatch(&(tmem->latch)); + taosRUnLockLatch(&(pSnapshot->omem->latch)); } - tsdbUnRefMemTable(pRepo, tmem); - - tsdbDebug("vgId:%d take memory snapshot, pMem %p pIMem %p", REPO_ID(pRepo), *pMem, *pIMem); + tsdbDebug("vgId:%d take memory snapshot, pMem %p pIMem %p", REPO_ID(pRepo), pSnapshot->omem, pSnapshot->imem); return 0; } -void tsdbUnTakeMemSnapShot(STsdbRepo *pRepo, SMemTable *pMem, SMemTable *pIMem) { - tsdbDebug("vgId:%d untake memory snapshot, pMem %p pIMem %p", REPO_ID(pRepo), pMem, pIMem); +void tsdbUnTakeMemSnapShot(STsdbRepo *pRepo, SMemSnapshot *pSnapshot) { + tsdbDebug("vgId:%d untake memory snapshot, pMem %p pIMem %p", REPO_ID(pRepo), pSnapshot->omem, pSnapshot->imem); - if (pMem != NULL) { - for (size_t i = 0; i < pMem->maxTables; i++) { - STableData *pTableData = pMem->tData[i]; + if (pSnapshot->mem) { + ASSERT(pSnapshot->omem != NULL); + + for (size_t i = 0; i < pSnapshot->mem->maxTables; i++) { + STableData *pTableData = pSnapshot->mem->tData[i]; if (pTableData) { tsdbFreeTableData(pTableData); } } - free(pMem->tData); - free(pMem); - } + tfree(pSnapshot->mem->tData); - if (pIMem != NULL) { - tsdbUnRefMemTable(pRepo, pIMem); + tsdbUnRefMemTable(pRepo, pSnapshot->omem); } + + tsdbUnRefMemTable(pRepo, pSnapshot->imem); + + pSnapshot->mem = NULL; + pSnapshot->imem = NULL; + pSnapshot->omem = NULL; } void *tsdbAllocBytes(STsdbRepo *pRepo, int bytes) { diff --git a/src/tsdb/src/tsdbMeta.c b/src/tsdb/src/tsdbMeta.c index cdde2bc91f430273a65aa3ae2ba09bc42771a024..824f69608e8c9d2d00e3219d54b484304827e8ae 100644 --- a/src/tsdb/src/tsdbMeta.c +++ b/src/tsdb/src/tsdbMeta.c @@ -213,9 +213,9 @@ void *tsdbGetTableTagVal(const void* pTable, int32_t colId, int16_t type, int16_ char *val = tdGetKVRowValOfCol(((STable*)pTable)->tagVal, colId); assert(type == pCol->type && bytes == pCol->bytes); - if (val != NULL && IS_VAR_DATA_TYPE(type)) { - assert(varDataLen(val) < pCol->bytes); - } + // if (val != NULL && IS_VAR_DATA_TYPE(type)) { + // assert(varDataLen(val) < pCol->bytes); + // } return val; } diff --git a/src/tsdb/src/tsdbRead.c b/src/tsdb/src/tsdbRead.c index 7b7c244ba8eb93730adc27714cb55b0ee75b6e35..648b6d3617a53b004d93add220d007fa228f7076 100644 --- a/src/tsdb/src/tsdbRead.c +++ b/src/tsdb/src/tsdbRead.c @@ -194,7 +194,7 @@ static void tsdbMayTakeMemSnapshot(STsdbQueryHandle* pQueryHandle, SArray* psTab SMemRef* pMemRef = pQueryHandle->pMemRef; if (pQueryHandle->pMemRef->ref++ == 0) { - tsdbTakeMemSnapshot(pQueryHandle->pTsdb, (SMemTable**)&(pMemRef->mem), (SMemTable**)&(pMemRef->imem), psTable); + tsdbTakeMemSnapshot(pQueryHandle->pTsdb, &(pMemRef->snapshot), psTable); } taosArrayDestroy(psTable); @@ -208,9 +208,7 @@ static void tsdbMayUnTakeMemSnapshot(STsdbQueryHandle* pQueryHandle) { } if (--pMemRef->ref == 0) { - tsdbUnTakeMemSnapShot(pQueryHandle->pTsdb, pMemRef->mem, pMemRef->imem); - pMemRef->mem = NULL; - pMemRef->imem = NULL; + tsdbUnTakeMemSnapShot(pQueryHandle->pTsdb, &(pMemRef->snapshot)); } pQueryHandle->pMemRef = NULL; @@ -229,10 +227,10 @@ int64_t tsdbGetNumOfRowsInMemTable(TsdbQueryHandleT* pHandle) { if (pMemRef == NULL) { return rows; } STableData* pMem = NULL; - STableData* pIMem = NULL; + STableData* pIMem = NULL; - SMemTable *pMemT = (SMemTable *)(pMemRef->mem); - SMemTable *pIMemT = (SMemTable *)(pMemRef->imem); + SMemTable* pMemT = pMemRef->snapshot.mem; + SMemTable* pIMemT = pMemRef->snapshot.imem; if (pMemT && pCheckInfo->tableId.tid < pMemT->maxTables) { pMem = pMemT->tData[pCheckInfo->tableId.tid]; @@ -605,7 +603,7 @@ static bool initTableMemIterator(STsdbQueryHandle* pHandle, STableCheckInfo* pCh int32_t order = pHandle->order; // no data in buffer, abort - if (pHandle->pMemRef->mem == NULL && pHandle->pMemRef->imem == NULL) { + if (pHandle->pMemRef->snapshot.mem == NULL && pHandle->pMemRef->snapshot.imem == NULL) { return false; } @@ -614,8 +612,8 @@ static bool initTableMemIterator(STsdbQueryHandle* pHandle, STableCheckInfo* pCh STableData* pMem = NULL; STableData* pIMem = NULL; - SMemTable* pMemT = pHandle->pMemRef->mem; - SMemTable* pIMemT = pHandle->pMemRef->imem; + SMemTable* pMemT = pHandle->pMemRef->snapshot.mem; + SMemTable* pIMemT = pHandle->pMemRef->snapshot.imem; if (pMemT && pCheckInfo->tableId.tid < pMemT->maxTables) { pMem = pMemT->tData[pCheckInfo->tableId.tid]; @@ -844,6 +842,10 @@ static int32_t getFileIdFromKey(TSKEY key, int32_t daysPerFile, int32_t precisio return INT32_MIN; } + if (key < 0) { + key -= (daysPerFile * tsMsPerDay[precision]); + } + int64_t fid = (int64_t)(key / (daysPerFile * tsMsPerDay[precision])); // set the starting fileId if (fid < 0L && llabs(fid) > INT32_MAX) { // data value overflow for INT32 fid = INT32_MIN; diff --git a/src/tsdb/src/tsdbReadImpl.c b/src/tsdb/src/tsdbReadImpl.c index 572706d45e6a085f44ffd0a089dc28256255e33e..7212ae1636400b0ce45ef27306356f658fb3f90d 100644 --- a/src/tsdb/src/tsdbReadImpl.c +++ b/src/tsdb/src/tsdbReadImpl.c @@ -139,7 +139,7 @@ int tsdbLoadBlockIdx(SReadH *pReadh) { ptr = tsdbDecodeSBlockIdx(ptr, &blkIdx); ASSERT(ptr != NULL); - if (taosArrayPush(pReadh->aBlkIdx, (void *)(&blkIdx)) < 0) { + if (taosArrayPush(pReadh->aBlkIdx, (void *)(&blkIdx)) == NULL) { terrno = TSDB_CODE_TDB_OUT_OF_MEMORY; return -1; } diff --git a/src/tsdb/src/tsdbSync.c b/src/tsdb/src/tsdbSync.c index 6b8483e4a66df75db529b05db9a29e800e01427a..cef561aebea002649e8458057882094a7e42d9b4 100644 --- a/src/tsdb/src/tsdbSync.c +++ b/src/tsdb/src/tsdbSync.c @@ -152,14 +152,14 @@ static int32_t tsdbSyncSendMeta(SSyncH *pSynch) { return -1; } - int32_t writeLen = (int32_t)mf.info.size; - tsdbInfo("vgId:%d, metafile:%s will be sent, size:%d", REPO_ID(pRepo), mf.f.aname, writeLen); + int64_t writeLen = mf.info.size; + tsdbInfo("vgId:%d, metafile:%s will be sent, size:%" PRId64, REPO_ID(pRepo), mf.f.aname, writeLen); - int32_t ret = (int32_t)taosSendFile(pSynch->socketFd, TSDB_FILE_FD(&mf), 0, writeLen); + int64_t ret = taosSendFile(pSynch->socketFd, TSDB_FILE_FD(&mf), 0, writeLen); if (ret != writeLen) { terrno = TAOS_SYSTEM_ERROR(errno); - tsdbError("vgId:%d, failed to send metafile since %s, ret:%d writeLen:%d", REPO_ID(pRepo), tstrerror(terrno), ret, - writeLen); + tsdbError("vgId:%d, failed to send metafile since %s, ret:%" PRId64 " writeLen:%" PRId64, REPO_ID(pRepo), + tstrerror(terrno), ret, writeLen); tsdbCloseMFile(&mf); return -1; } @@ -217,18 +217,18 @@ static int32_t tsdbSyncRecvMeta(SSyncH *pSynch) { tsdbInfo("vgId:%d, metafile:%s is created", REPO_ID(pRepo), mf.f.aname); - int32_t readLen = (int32_t)pSynch->pmf->info.size; - int32_t ret = taosCopyFds(pSynch->socketFd, TSDB_FILE_FD(&mf), readLen); + int64_t readLen = pSynch->pmf->info.size; + int64_t ret = taosCopyFds(pSynch->socketFd, TSDB_FILE_FD(&mf), readLen); if (ret != readLen) { terrno = TAOS_SYSTEM_ERROR(errno); - tsdbError("vgId:%d, failed to recv metafile since %s, ret:%d readLen:%d", REPO_ID(pRepo), tstrerror(terrno), ret, - readLen); + tsdbError("vgId:%d, failed to recv metafile since %s, ret:%" PRId64 " readLen:%" PRId64, REPO_ID(pRepo), + tstrerror(terrno), ret, readLen); tsdbCloseMFile(&mf); tsdbRemoveMFile(&mf); return -1; } - tsdbInfo("vgId:%d, metafile is received, size:%d", REPO_ID(pRepo), readLen); + tsdbInfo("vgId:%d, metafile is received, size:%" PRId64, REPO_ID(pRepo), readLen); mf.info = pSynch->pmf->info; tsdbCloseMFile(&mf); @@ -463,12 +463,12 @@ static int32_t tsdbSyncRecvDFileSetArray(SSyncH *pSynch) { tsdbInfo("vgId:%d, file:%s will be received, osize:%" PRIu64 " rsize:%" PRIu64, REPO_ID(pRepo), pDFile->f.aname, pDFile->info.size, pRDFile->info.size); - int32_t writeLen = (int32_t)pRDFile->info.size; - int32_t ret = taosCopyFds(pSynch->socketFd, pDFile->fd, writeLen); + int64_t writeLen = pRDFile->info.size; + int64_t ret = taosCopyFds(pSynch->socketFd, pDFile->fd, writeLen); if (ret != writeLen) { terrno = TAOS_SYSTEM_ERROR(errno); - tsdbError("vgId:%d, failed to recv file:%s since %s, ret:%d writeLen:%d", REPO_ID(pRepo), pDFile->f.aname, - tstrerror(terrno), ret, writeLen); + tsdbError("vgId:%d, failed to recv file:%s since %s, ret:%" PRId64 " writeLen:%" PRId64, REPO_ID(pRepo), + pDFile->f.aname, tstrerror(terrno), ret, writeLen); tsdbCloseDFileSet(&fset); tsdbRemoveDFileSet(&fset); return -1; @@ -476,7 +476,7 @@ static int32_t tsdbSyncRecvDFileSetArray(SSyncH *pSynch) { // Update new file info pDFile->info = pRDFile->info; - tsdbInfo("vgId:%d, file:%s is received, size:%d", REPO_ID(pRepo), pDFile->f.aname, writeLen); + tsdbInfo("vgId:%d, file:%s is received, size:%" PRId64, REPO_ID(pRepo), pDFile->f.aname, writeLen); } tsdbCloseDFileSet(&fset); @@ -575,14 +575,14 @@ static int32_t tsdbSyncSendDFileSet(SSyncH *pSynch, SDFileSet *pSet) { return -1; } - int32_t writeLen = (int32_t)df.info.size; - tsdbInfo("vgId:%d, file:%s will be sent, size:%d", REPO_ID(pRepo), df.f.aname, writeLen); + int64_t writeLen = df.info.size; + tsdbInfo("vgId:%d, file:%s will be sent, size:%" PRId64, REPO_ID(pRepo), df.f.aname, writeLen); - int32_t ret = (int32_t)taosSendFile(pSynch->socketFd, TSDB_FILE_FD(&df), 0, writeLen); + int64_t ret = taosSendFile(pSynch->socketFd, TSDB_FILE_FD(&df), 0, writeLen); if (ret != writeLen) { terrno = TAOS_SYSTEM_ERROR(errno); - tsdbError("vgId:%d, failed to send file:%s since %s, ret:%d writeLen:%d", REPO_ID(pRepo), df.f.aname, - tstrerror(terrno), ret, writeLen); + tsdbError("vgId:%d, failed to send file:%s since %s, ret:%" PRId64 " writeLen:%" PRId64, REPO_ID(pRepo), + df.f.aname, tstrerror(terrno), ret, writeLen); tsdbCloseDFile(&df); return -1; } @@ -677,13 +677,13 @@ static int32_t tsdbRecvDFileSetInfo(SSyncH *pSynch) { static int tsdbReload(STsdbRepo *pRepo, bool isMfChanged) { // TODO: may need to stop and restart stream - if (isMfChanged) { - tsdbCloseMeta(pRepo); - tsdbFreeMeta(pRepo->tsdbMeta); - pRepo->tsdbMeta = tsdbNewMeta(REPO_CFG(pRepo)); - tsdbOpenMeta(pRepo); - tsdbLoadMetaCache(pRepo, true); - } + // if (isMfChanged) { + tsdbCloseMeta(pRepo); + tsdbFreeMeta(pRepo->tsdbMeta); + pRepo->tsdbMeta = tsdbNewMeta(REPO_CFG(pRepo)); + tsdbOpenMeta(pRepo); + tsdbLoadMetaCache(pRepo, true); + // } tsdbUnRefMemTable(pRepo, pRepo->mem); tsdbUnRefMemTable(pRepo, pRepo->imem); diff --git a/src/util/inc/tchecksum.h b/src/util/inc/tchecksum.h index 495aaf33e8fca3594c45f1cfe6fc6acc59fa59d5..12ca3a54432187ea5af0ac24208e81d5028359bb 100644 --- a/src/util/inc/tchecksum.h +++ b/src/util/inc/tchecksum.h @@ -47,7 +47,6 @@ static FORCE_INLINE int taosCalcChecksumAppend(TSCKSUM csi, uint8_t *stream, uin } static FORCE_INLINE int taosCheckChecksum(const uint8_t *stream, uint32_t ssize, TSCKSUM checksum) { - if (ssize < 0) return 0; return (checksum == (*crc32c)(0, stream, (size_t)ssize)); } diff --git a/src/util/inc/tidpool.h b/src/util/inc/tidpool.h index bf352516310a1356f37359ab3052814d726ab8f0..e4439439ced6522e26c8db4a560c50f5b0cb8a16 100644 --- a/src/util/inc/tidpool.h +++ b/src/util/inc/tidpool.h @@ -34,7 +34,7 @@ void taosIdPoolCleanUp(void *handle); int taosIdPoolNumOfUsed(void *handle); -void taosIdPoolMarkStatus(void *handle, int id); +bool taosIdPoolMarkStatus(void *handle, int id); #ifdef __cplusplus } diff --git a/src/util/inc/tsocket.h b/src/util/inc/tsocket.h index 35b591b61e007dcf6d4edc39810a60454f37c07a..b4f55169594589b83c299694723cb234bd21ed7d 100644 --- a/src/util/inc/tsocket.h +++ b/src/util/inc/tsocket.h @@ -28,7 +28,7 @@ int32_t taosReadn(SOCKET sock, char *buffer, int32_t len); int32_t taosWriteMsg(SOCKET fd, void *ptr, int32_t nbytes); int32_t taosReadMsg(SOCKET fd, void *ptr, int32_t nbytes); int32_t taosNonblockwrite(SOCKET fd, char *ptr, int32_t nbytes); -int32_t taosCopyFds(SOCKET sfd, int32_t dfd, int64_t len); +int64_t taosCopyFds(SOCKET sfd, int32_t dfd, int64_t len); int32_t taosSetNonblocking(SOCKET sock, int32_t on); SOCKET taosOpenUdpSocket(uint32_t localIp, uint16_t localPort); diff --git a/src/util/src/terror.c b/src/util/src/terror.c index 221e6183909682680b68c7a0debb2a90eca34c37..918ccc493543c785313df67c7f2a8eced6ba8fb7 100644 --- a/src/util/src/terror.c +++ b/src/util/src/terror.c @@ -199,6 +199,8 @@ TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_DB_OPTION_KEEP, "Invalid database opti TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_TOPIC, "Invalid topic name") TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_TOPIC_OPTION, "Invalid topic option") +TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_TOPIC_PARTITONS, "Invalid topic partitons num, valid range: [1, 1000]") +TAOS_DEFINE_ERROR(TSDB_CODE_MND_TOPIC_ALREADY_EXIST, "Topic already exists") // dnode TAOS_DEFINE_ERROR(TSDB_CODE_DND_MSG_NOT_PROCESSED, "Message not processed") diff --git a/src/util/src/tidpool.c b/src/util/src/tidpool.c index 53d81bb5422e5c1b3b7384a97186a93940d26408..bd3f7745430a20bd0ba4250436e43e6524d1b431 100644 --- a/src/util/src/tidpool.c +++ b/src/util/src/tidpool.c @@ -104,10 +104,16 @@ void taosIdPoolCleanUp(void *handle) { int taosIdPoolNumOfUsed(void *handle) { id_pool_t *pIdPool = handle; - return pIdPool->maxId - pIdPool->numOfFree; + + pthread_mutex_lock(&pIdPool->mutex); + int ret = pIdPool->maxId - pIdPool->numOfFree; + pthread_mutex_unlock(&pIdPool->mutex); + + return ret; } -void taosIdPoolMarkStatus(void *handle, int id) { +bool taosIdPoolMarkStatus(void *handle, int id) { + bool ret = false; id_pool_t *pIdPool = handle; pthread_mutex_lock(&pIdPool->mutex); @@ -115,9 +121,13 @@ void taosIdPoolMarkStatus(void *handle, int id) { if (!pIdPool->freeList[slot]) { pIdPool->freeList[slot] = true; pIdPool->numOfFree--; + ret = true; + } else { + ret = false; } pthread_mutex_unlock(&pIdPool->mutex); + return ret; } int taosUpdateIdPool(id_pool_t *handle, int maxId) { @@ -147,6 +157,11 @@ int taosUpdateIdPool(id_pool_t *handle, int maxId) { } int taosIdPoolMaxSize(void *handle) { - id_pool_t *pIdPool = (id_pool_t*)handle; - return pIdPool->maxId; + id_pool_t *pIdPool = (id_pool_t *)handle; + + pthread_mutex_lock(&pIdPool->mutex); + int ret = pIdPool->maxId; + pthread_mutex_unlock(&pIdPool->mutex); + + return ret; } \ No newline at end of file diff --git a/src/util/src/tsocket.c b/src/util/src/tsocket.c index e07587686774cb0fa6c7682daca59b400a9451ec..b33cdb8b807f545943e3676a96098818e54b31dd 100644 --- a/src/util/src/tsocket.c +++ b/src/util/src/tsocket.c @@ -465,36 +465,36 @@ void tinet_ntoa(char *ipstr, uint32_t ip) { #define COPY_SIZE 32768 // sendfile shall be used -int32_t taosCopyFds(SOCKET sfd, int32_t dfd, int64_t len) { +int64_t taosCopyFds(SOCKET sfd, int32_t dfd, int64_t len) { int64_t leftLen; - int32_t readLen, writeLen; + int64_t readLen, writeLen; char temp[COPY_SIZE]; leftLen = len; while (leftLen > 0) { if (leftLen < COPY_SIZE) - readLen = (int32_t)leftLen; + readLen = leftLen; else readLen = COPY_SIZE; // 4K - int32_t retLen = taosReadMsg(sfd, temp, (int32_t)readLen); + int64_t retLen = taosReadMsg(sfd, temp, (int32_t)readLen); if (readLen != retLen) { - uError("read error, readLen:%d retLen:%d len:%" PRId64 " leftLen:%" PRId64 ", reason:%s", readLen, retLen, len, - leftLen, strerror(errno)); + uError("read error, readLen:%" PRId64 " retLen:%" PRId64 " len:%" PRId64 " leftLen:%" PRId64 ", reason:%s", + readLen, retLen, len, leftLen, strerror(errno)); return -1; } - writeLen = taosWriteMsg(dfd, temp, readLen); + writeLen = taosWriteMsg(dfd, temp, (int32_t)readLen); if (readLen != writeLen) { - uError("copy error, readLen:%d writeLen:%d len:%" PRId64 " leftLen:%" PRId64 ", reason:%s", readLen, writeLen, - len, leftLen, strerror(errno)); + uError("copy error, readLen:%" PRId64 " writeLen:%" PRId64 " len:%" PRId64 " leftLen:%" PRId64 ", reason:%s", + readLen, writeLen, len, leftLen, strerror(errno)); return -1; } leftLen -= readLen; } - return (int32_t)len; + return len; } diff --git a/src/vnode/inc/vnodeInt.h b/src/vnode/inc/vnodeInt.h index 91ddf5076b0540613cdac7047f0a1ea0cdd21c0b..4aa07196a79513c476072b58d62787bd7a5a966d 100644 --- a/src/vnode/inc/vnodeInt.h +++ b/src/vnode/inc/vnodeInt.h @@ -40,7 +40,7 @@ typedef struct { int32_t queuedWMsg; int32_t queuedRMsg; int32_t flowctrlLevel; - int32_t sequence; // for topic + int64_t sequence; // for topic int8_t status; int8_t role; int8_t accessState; diff --git a/src/vnode/inc/vnodeSync.h b/src/vnode/inc/vnodeSync.h index c9ac25c2274d81cd08c52a77cd3cc76a27c7a0d5..75d7ffbabda514f3d280c712fbd531aedbc3e0d7 100644 --- a/src/vnode/inc/vnodeSync.h +++ b/src/vnode/inc/vnodeSync.h @@ -30,8 +30,9 @@ void vnodeStopSyncFile(int32_t vgId, uint64_t fversion); void vnodeConfirmForard(int32_t vgId, void *wparam, int32_t code); int32_t vnodeWriteToCache(int32_t vgId, void *wparam, int32_t qtype, void *rparam); int32_t vnodeGetVersion(int32_t vgId, uint64_t *fver, uint64_t *wver); +int32_t vnodeResetVersion(int32_t vgId, uint64_t fver); -void vnodeConfirmForward(void *pVnode, uint64_t version, int32_t code); +void vnodeConfirmForward(void *pVnode, uint64_t version, int32_t code, bool force); #ifdef __cplusplus } diff --git a/src/vnode/src/vnodeMain.c b/src/vnode/src/vnodeMain.c index ac9536d243dbd2613d42c8b9a5b054ce862df061..ded39e67cc37d7733f4e7aaa9998b4578e05c122 100644 --- a/src/vnode/src/vnodeMain.c +++ b/src/vnode/src/vnodeMain.c @@ -110,31 +110,78 @@ int32_t vnodeDrop(int32_t vgId) { } static int32_t vnodeAlterImp(SVnodeObj *pVnode, SCreateVnodeMsg *pVnodeCfg) { + STsdbCfg tsdbCfg = pVnode->tsdbCfg; + SSyncCfg syncCfg = pVnode->syncCfg; + int32_t dbCfgVersion = pVnode->dbCfgVersion; + int32_t vgCfgVersion = pVnode->vgCfgVersion; + int32_t code = vnodeWriteCfg(pVnodeCfg); if (code != TSDB_CODE_SUCCESS) { - return code; + pVnode->dbCfgVersion = dbCfgVersion; + pVnode->vgCfgVersion = vgCfgVersion; + pVnode->syncCfg = syncCfg; + pVnode->tsdbCfg = tsdbCfg; + return code; } code = vnodeReadCfg(pVnode); if (code != TSDB_CODE_SUCCESS) { - return code; + pVnode->dbCfgVersion = dbCfgVersion; + pVnode->vgCfgVersion = vgCfgVersion; + pVnode->syncCfg = syncCfg; + pVnode->tsdbCfg = tsdbCfg; + return code; } code = walAlter(pVnode->wal, &pVnode->walCfg); if (code != TSDB_CODE_SUCCESS) { + pVnode->dbCfgVersion = dbCfgVersion; + pVnode->vgCfgVersion = vgCfgVersion; + pVnode->syncCfg = syncCfg; + pVnode->tsdbCfg = tsdbCfg; return code; } - code = syncReconfig(pVnode->sync, &pVnode->syncCfg); - if (code != TSDB_CODE_SUCCESS) { - return code; - } + bool tsdbCfgChanged = (memcmp(&tsdbCfg, &pVnode->tsdbCfg, sizeof(STsdbCfg)) != 0); + bool syncCfgChanged = (memcmp(&syncCfg, &pVnode->syncCfg, sizeof(SSyncCfg)) != 0); - if (pVnode->tsdb) { - code = tsdbConfigRepo(pVnode->tsdb, &pVnode->tsdbCfg); + vDebug("vgId:%d, tsdbchanged:%d syncchanged:%d while alter vnode", pVnode->vgId, tsdbCfgChanged, syncCfgChanged); + + if (/*tsdbCfgChanged || */syncCfgChanged) { + // vnode in non-ready state and still needs to return success instead of TSDB_CODE_VND_INVALID_STATUS + // dbCfgVersion can be corrected by status msg + if (!vnodeSetUpdatingStatus(pVnode)) { + vDebug("vgId:%d, vnode is not ready, do alter operation later", pVnode->vgId); + pVnode->dbCfgVersion = dbCfgVersion; + pVnode->vgCfgVersion = vgCfgVersion; + pVnode->syncCfg = syncCfg; + pVnode->tsdbCfg = tsdbCfg; + return TSDB_CODE_SUCCESS; + } + + code = syncReconfig(pVnode->sync, &pVnode->syncCfg); if (code != TSDB_CODE_SUCCESS) { + pVnode->dbCfgVersion = dbCfgVersion; + pVnode->vgCfgVersion = vgCfgVersion; + pVnode->syncCfg = syncCfg; + pVnode->tsdbCfg = tsdbCfg; + vnodeSetReadyStatus(pVnode); return code; } + + if (pVnode->tsdb) { + code = tsdbConfigRepo(pVnode->tsdb, &pVnode->tsdbCfg); + if (code != TSDB_CODE_SUCCESS) { + pVnode->dbCfgVersion = dbCfgVersion; + pVnode->vgCfgVersion = vgCfgVersion; + pVnode->syncCfg = syncCfg; + pVnode->tsdbCfg = tsdbCfg; + vnodeSetReadyStatus(pVnode); + return code; + } + } + + vnodeSetReadyStatus(pVnode); } return 0; @@ -142,21 +189,16 @@ static int32_t vnodeAlterImp(SVnodeObj *pVnode, SCreateVnodeMsg *pVnodeCfg) { int32_t vnodeAlter(void *vparam, SCreateVnodeMsg *pVnodeCfg) { SVnodeObj *pVnode = vparam; - if (pVnode->dbCfgVersion == pVnodeCfg->cfg.dbCfgVersion && pVnode->vgCfgVersion == pVnodeCfg->cfg.vgCfgVersion) { - vDebug("vgId:%d, dbCfgVersion:%d and vgCfgVersion:%d not change", pVnode->vgId, pVnode->dbCfgVersion, - pVnode->vgCfgVersion); - return TSDB_CODE_SUCCESS; - } - // vnode in non-ready state and still needs to return success instead of TSDB_CODE_VND_INVALID_STATUS - // dbCfgVersion can be corrected by status msg - if (!vnodeSetUpdatingStatus(pVnode)) { - vDebug("vgId:%d, vnode is not ready, do alter operation later", pVnode->vgId); + vDebug("vgId:%d, current dbCfgVersion:%d vgCfgVersion:%d, input dbCfgVersion:%d vgCfgVersion:%d", pVnode->vgId, + pVnode->dbCfgVersion, pVnode->vgCfgVersion, pVnodeCfg->cfg.dbCfgVersion, pVnodeCfg->cfg.vgCfgVersion); + + if (pVnode->dbCfgVersion == pVnodeCfg->cfg.dbCfgVersion && pVnode->vgCfgVersion == pVnodeCfg->cfg.vgCfgVersion) { + vDebug("vgId:%d, cfg not change", pVnode->vgId); return TSDB_CODE_SUCCESS; } int32_t code = vnodeAlterImp(pVnode, pVnodeCfg); - vnodeSetReadyStatus(pVnode); if (code != 0) { vError("vgId:%d, failed to alter vnode, code:0x%x", pVnode->vgId, code); @@ -305,6 +347,7 @@ int32_t vnodeOpen(int32_t vgId) { syncInfo.startSyncFileFp = vnodeStartSyncFile; syncInfo.stopSyncFileFp = vnodeStopSyncFile; syncInfo.getVersionFp = vnodeGetVersion; + syncInfo.resetVersionFp = vnodeResetVersion; syncInfo.sendFileFp = tsdbSyncSend; syncInfo.recvFileFp = tsdbSyncRecv; syncInfo.pTsdb = pVnode->tsdb; @@ -439,7 +482,7 @@ static int32_t vnodeProcessTsdbStatus(void *arg, int32_t status, int32_t eno) { if (status == TSDB_STATUS_COMMIT_START) { pVnode->isCommiting = 1; pVnode->cversion = pVnode->version; - vDebug("vgId:%d, start commit, fver:%" PRIu64 " vver:%" PRIu64, pVnode->vgId, pVnode->fversion, pVnode->version); + vInfo("vgId:%d, start commit, fver:%" PRIu64 " vver:%" PRIu64, pVnode->vgId, pVnode->fversion, pVnode->version); if (!vnodeInInitStatus(pVnode)) { return walRenew(pVnode->wal); } @@ -450,7 +493,7 @@ static int32_t vnodeProcessTsdbStatus(void *arg, int32_t status, int32_t eno) { pVnode->isCommiting = 0; pVnode->isFull = 0; pVnode->fversion = pVnode->cversion; - vDebug("vgId:%d, commit over, fver:%" PRIu64 " vver:%" PRIu64, pVnode->vgId, pVnode->fversion, pVnode->version); + vInfo("vgId:%d, commit over, fver:%" PRIu64 " vver:%" PRIu64, pVnode->vgId, pVnode->fversion, pVnode->version); if (!vnodeInInitStatus(pVnode)) { walRemoveOneOldFile(pVnode->wal); } diff --git a/src/vnode/src/vnodeMgmt.c b/src/vnode/src/vnodeMgmt.c index 8469ab12c1ef5833e73058c167afb2777ac76ba9..71d9bc07f50b6a8cba06d68b9b06b24f4dadcf83 100644 --- a/src/vnode/src/vnodeMgmt.c +++ b/src/vnode/src/vnodeMgmt.c @@ -128,7 +128,7 @@ static void vnodeBuildVloadMsg(SVnodeObj *pVnode, SStatusMsg *pStatus) { int64_t compStorage = 0; int64_t pointsWritten = 0; - if (!vnodeInReadyStatus(pVnode)) return; + if (vnodeInClosingStatus(pVnode)) return; if (pStatus->openVnodes >= TSDB_MAX_VNODES) return; if (pVnode->tsdb) { @@ -194,4 +194,4 @@ void vnodeSetAccess(SVgroupAccess *pAccess, int32_t numOfVnodes) { vnodeRelease(pVnode); } } -} \ No newline at end of file +} diff --git a/src/vnode/src/vnodeRead.c b/src/vnode/src/vnodeRead.c index 8b26f4a8e22e00286a833f88efdf31e1e84b58e9..ef68499b889d33544e601eea9ddd8cd5efe3f534 100644 --- a/src/vnode/src/vnodeRead.c +++ b/src/vnode/src/vnodeRead.c @@ -247,7 +247,8 @@ static int32_t vnodeProcessQueryMsg(SVnodeObj *pVnode, SVReadMsg *pRead) { if (contLen != 0) { qinfo_t pQInfo = NULL; - code = qCreateQueryInfo(pVnode->tsdb, pVnode->vgId, pQueryTableMsg, &pQInfo); + uint64_t qId = 0; + code = qCreateQueryInfo(pVnode->tsdb, pVnode->vgId, pQueryTableMsg, &pQInfo, &qId); SQueryTableRsp *pRsp = (SQueryTableRsp *)rpcMallocCont(sizeof(SQueryTableRsp)); pRsp->code = code; @@ -259,22 +260,22 @@ static int32_t vnodeProcessQueryMsg(SVnodeObj *pVnode, SVReadMsg *pRead) { // current connect is broken if (code == TSDB_CODE_SUCCESS) { - handle = qRegisterQInfo(pVnode->qMgmt, (uint64_t)pQInfo); + handle = qRegisterQInfo(pVnode->qMgmt, qId, (uint64_t)pQInfo); if (handle == NULL) { // failed to register qhandle pRsp->code = terrno; terrno = 0; - vError("vgId:%d, QInfo:%p register qhandle failed, return to app, code:%s", pVnode->vgId, (void *)pQInfo, + vError("vgId:%d, QInfo:%"PRIu64 "-%p register qhandle failed, return to app, code:%s", pVnode->vgId, qId, (void *)pQInfo, tstrerror(pRsp->code)); qDestroyQueryInfo(pQInfo); // destroy it directly return pRsp->code; } else { assert(*handle == pQInfo); - pRsp->qhandle = htobe64((uint64_t)pQInfo); + pRsp->qhandle = htobe64(qId); } if (handle != NULL && vnodeNotifyCurrentQhandle(pRead->rpcHandle, *handle, pVnode->vgId) != TSDB_CODE_SUCCESS) { - vError("vgId:%d, QInfo:%p, query discarded since link is broken, %p", pVnode->vgId, *handle, + vError("vgId:%d, QInfo:%"PRIu64 "-%p, query discarded since link is broken, %p", pVnode->vgId, qId, *handle, pRead->rpcHandle); pRsp->code = TSDB_CODE_RPC_NETWORK_UNAVAIL; qReleaseQInfo(pVnode->qMgmt, (void **)&handle, true); @@ -285,7 +286,7 @@ static int32_t vnodeProcessQueryMsg(SVnodeObj *pVnode, SVReadMsg *pRead) { } if (handle != NULL) { - vTrace("vgId:%d, QInfo:%p, dnode query msg disposed, create qhandle and returns to app", vgId, *handle); + vTrace("vgId:%d, QInfo:%"PRIu64 "-%p, dnode query msg disposed, create qhandle and returns to app", vgId, qId, *handle); code = vnodePutItemIntoReadQueue(pVnode, handle, pRead->rpcHandle); if (code != TSDB_CODE_SUCCESS) { pRsp->code = code; @@ -349,7 +350,7 @@ static int32_t vnodeProcessFetchMsg(SVnodeObj *pVnode, SVReadMsg *pRead) { pRetrieve->free = htons(pRetrieve->free); pRetrieve->qhandle = htobe64(pRetrieve->qhandle); - vTrace("vgId:%d, QInfo:%p, retrieve msg is disposed, free:%d, conn:%p", pVnode->vgId, (void *)pRetrieve->qhandle, + vTrace("vgId:%d, QInfo:%" PRIu64 ", retrieve msg is disposed, free:%d, conn:%p", pVnode->vgId, pRetrieve->qhandle, pRetrieve->free, pRead->rpcHandle); memset(pRet, 0, sizeof(SRspRet)); @@ -360,19 +361,19 @@ static int32_t vnodeProcessFetchMsg(SVnodeObj *pVnode, SVReadMsg *pRead) { if (handle == NULL) { code = terrno; terrno = TSDB_CODE_SUCCESS; - } else if ((*handle) != (void *)pRetrieve->qhandle) { + } else if (!checkQIdEqual(*handle, pRetrieve->qhandle)) { code = TSDB_CODE_QRY_INVALID_QHANDLE; } if (code != TSDB_CODE_SUCCESS) { - vError("vgId:%d, invalid handle in retrieving result, code:%s, QInfo:%p", pVnode->vgId, tstrerror(code), (void *)pRetrieve->qhandle); + vError("vgId:%d, invalid handle in retrieving result, code:%s, QInfo:%" PRIu64, pVnode->vgId, tstrerror(code), pRetrieve->qhandle); vnodeBuildNoResultQueryRsp(pRet); return code; } // kill current query and free corresponding resources. if (pRetrieve->free == 1) { - vDebug("vgId:%d, QInfo:%p, retrieve msg received to kill query and free qhandle", pVnode->vgId, *handle); + vWarn("vgId:%d, QInfo:%"PRIu64 "-%p, retrieve msg received to kill query and free qhandle", pVnode->vgId, pRetrieve->qhandle, *handle); qKillQuery(*handle); qReleaseQInfo(pVnode->qMgmt, (void **)&handle, true); @@ -383,7 +384,7 @@ static int32_t vnodeProcessFetchMsg(SVnodeObj *pVnode, SVReadMsg *pRead) { // register the qhandle to connect to quit query immediate if connection is broken if (vnodeNotifyCurrentQhandle(pRead->rpcHandle, *handle, pVnode->vgId) != TSDB_CODE_SUCCESS) { - vError("vgId:%d, QInfo:%p, retrieve discarded since link is broken, %p", pVnode->vgId, *handle, pRead->rpcHandle); + vError("vgId:%d, QInfo:%"PRIu64 "-%p, retrieve discarded since link is broken, %p", pVnode->vgId, pRetrieve->qhandle, *handle, pRead->rpcHandle); code = TSDB_CODE_RPC_NETWORK_UNAVAIL; qKillQuery(*handle); qReleaseQInfo(pVnode->qMgmt, (void **)&handle, true); @@ -441,4 +442,4 @@ void vnodeWaitReadCompleted(SVnodeObj *pVnode) { vTrace("vgId:%d, queued rmsg num:%d", pVnode->vgId, pVnode->queuedRMsg); taosMsleep(10); } -} \ No newline at end of file +} diff --git a/src/vnode/src/vnodeStatus.c b/src/vnode/src/vnodeStatus.c index 68898435303daa8e0ac60a84cd54f4794515103e..c482d1fd1a44497b2ba3ff8482d4d1f66bb11ff5 100644 --- a/src/vnode/src/vnodeStatus.c +++ b/src/vnode/src/vnodeStatus.c @@ -109,6 +109,8 @@ bool vnodeSetResetStatus(SVnodeObj* pVnode) { taosMsleep(1); } + vInfo("vgId:%d, set to reset status", pVnode->vgId); + // release local resources only after cutting off outside connections qQueryMgmtNotifyClosed(pVnode->qMgmt); vnodeWaitReadCompleted(pVnode); @@ -153,6 +155,18 @@ bool vnodeInReadyOrUpdatingStatus(SVnodeObj* pVnode) { return in; } +bool vnodeInClosingStatus(SVnodeObj* pVnode) { + bool in = false; + pthread_mutex_lock(&pVnode->statusMutex); + + if (pVnode->status == TAOS_VN_STATUS_CLOSING) { + in = true; + } + + pthread_mutex_unlock(&pVnode->statusMutex); + return in; +} + bool vnodeInResetStatus(SVnodeObj* pVnode) { bool in = false; pthread_mutex_lock(&pVnode->statusMutex); diff --git a/src/vnode/src/vnodeSync.c b/src/vnode/src/vnodeSync.c index 627783c391d45b37830cb2b6d851fa6dd3261819..929dd1592610b03a41027781d66470b13756174a 100644 --- a/src/vnode/src/vnodeSync.c +++ b/src/vnode/src/vnodeSync.c @@ -91,7 +91,7 @@ void vnodeStartSyncFile(int32_t vgId) { return; } - vDebug("vgId:%d, datafile will be synced", vgId); + vInfo("vgId:%d, datafile will be synced", vgId); vnodeSetResetStatus(pVnode); vnodeRelease(pVnode); @@ -147,7 +147,7 @@ int32_t vnodeGetVersion(int32_t vgId, uint64_t *fver, uint64_t *wver) { int32_t code = 0; if (pVnode->isCommiting) { - vDebug("vgId:%d, vnode is commiting while get version", vgId); + vInfo("vgId:%d, vnode is commiting while get version", vgId); code = -1; } else { *fver = pVnode->fversion; @@ -158,7 +158,23 @@ int32_t vnodeGetVersion(int32_t vgId, uint64_t *fver, uint64_t *wver) { return code; } -void vnodeConfirmForward(void *vparam, uint64_t version, int32_t code) { - SVnodeObj *pVnode = vparam; - syncConfirmForward(pVnode->sync, version, code); +int32_t vnodeResetVersion(int32_t vgId, uint64_t fver) { + SVnodeObj *pVnode = vnodeAcquire(vgId); + if (pVnode == NULL) { + vError("vgId:%d, vnode not found while reset version", vgId); + return -1; + } + + pVnode->fversion = fver; + pVnode->version = fver; + walResetVersion(pVnode->wal, fver); + vInfo("vgId:%d, version reset to %" PRIu64, vgId, fver); + + vnodeRelease(pVnode); + return 0; } + +void vnodeConfirmForward(void *vparam, uint64_t version, int32_t code, bool force) { + SVnodeObj *pVnode = vparam; + syncConfirmForward(pVnode->sync, version, code, force); +} \ No newline at end of file diff --git a/src/vnode/src/vnodeWrite.c b/src/vnode/src/vnodeWrite.c index a1d4f50010f1cd3825146dbc2506d53ad3e17c09..e318978a11186a86dff79a5aaea234b9719ce979 100644 --- a/src/vnode/src/vnodeWrite.c +++ b/src/vnode/src/vnodeWrite.c @@ -24,7 +24,7 @@ #include "dnode.h" #include "vnodeStatus.h" -#define MAX_QUEUED_MSG_NUM 10000 +#define MAX_QUEUED_MSG_NUM 100000 extern void * tsDnodeTmr; static int32_t (*vnodeProcessWriteMsgFp[TSDB_MSG_TYPE_MAX])(SVnodeObj *, void *pCont, SRspRet *); @@ -89,7 +89,8 @@ int32_t vnodeProcessWrite(void *vparam, void *wparam, int32_t qtype, void *rpara // forward to peers, even it is WAL/FWD, it shall be called to update version in sync int32_t syncCode = 0; - syncCode = syncForwardToPeer(pVnode->sync, pHead, pWrite, qtype); + bool force = (pWrite == NULL ? false : pWrite->pHead.msgType != TSDB_MSG_TYPE_SUBMIT); + syncCode = syncForwardToPeer(pVnode->sync, pHead, pWrite, qtype, force); if (syncCode < 0) return syncCode; // write into WAL @@ -141,7 +142,7 @@ static int32_t vnodeProcessSubmitMsg(SVnodeObj *pVnode, void *pCont, SRspRet *pR vTrace("vgId:%d, submit msg is processed", pVnode->vgId); if (pVnode->dbType == TSDB_DB_TYPE_TOPIC && pVnode->role == TAOS_SYNC_ROLE_MASTER) { - tpUpdateTs(&pVnode->sequence, pCont); + tpUpdateTs(pVnode->vgId, &pVnode->sequence, pCont); } // save insert result into item diff --git a/src/wal/src/walWrite.c b/src/wal/src/walWrite.c index aeb49830299eb0dcddfbd39a7a838fdc5d45b081..4368ddd7d35c444c0b6e32fb5897801bba6e615d 100644 --- a/src/wal/src/walWrite.c +++ b/src/wal/src/walWrite.c @@ -446,3 +446,16 @@ uint64_t walGetVersion(twalh param) { return pWal->version; } + +// Wal version in slave (dnode1) must be reset. +// Because after the data file is recovered from peer (dnode2), the new file version in dnode1 may become smaller than origin. +// Some new wal record cannot be written to the wal file in dnode1 for wal version not reset, then fversion and the record in wal file may inconsistent, +// At this time, if dnode2 down, dnode1 switched to master. After dnode2 start and restore data from dnode1, data loss will occur + +void walResetVersion(twalh param, uint64_t newVer) { + SWal *pWal = param; + if (pWal == 0) return; + wInfo("vgId:%d, version reset from %" PRIu64 " to %" PRIu64, pWal->vgId, pWal->version, newVer); + + pWal->version = newVer; +} \ No newline at end of file diff --git a/tests/pytest/cluster/clusterEnvSetup/Dockerfile b/tests/pytest/cluster/clusterEnvSetup/Dockerfile index c9c4d79be981e45609e040bf5835e275fc446260..437dbc65e6430deb20faa16fc78ddc07005c15ac 100644 --- a/tests/pytest/cluster/clusterEnvSetup/Dockerfile +++ b/tests/pytest/cluster/clusterEnvSetup/Dockerfile @@ -28,6 +28,8 @@ RUN ulimit -c unlimited COPY --from=builder /root/bin/taosd /usr/bin COPY --from=builder /root/bin/tarbitrator /usr/bin +COPY --from=builder /root/bin/taosdemo /usr/bin +COPY --from=builder /root/bin/taosdump /usr/bin COPY --from=builder /root/bin/taos /usr/bin COPY --from=builder /root/cfg/taos.cfg /etc/taos/ COPY --from=builder /root/lib/libtaos.so.* /usr/lib/libtaos.so.1 diff --git a/tests/pytest/cluster/clusterEnvSetup/basic.py b/tests/pytest/cluster/clusterEnvSetup/basic.py index d9b8e9ce4a7bc144839334332268ac0f09f78f0d..dc7e07fd5cea3473270afa6cb2ddbfba6c2402e4 100644 --- a/tests/pytest/cluster/clusterEnvSetup/basic.py +++ b/tests/pytest/cluster/clusterEnvSetup/basic.py @@ -18,13 +18,15 @@ import argparse class BuildDockerCluser: - def __init__(self, hostName, user, password, configDir, numOfNodes, clusterVersion): + def __init__(self, hostName, user, password, configDir, numOfNodes, clusterVersion, dockerDir, removeFlag): self.hostName = hostName self.user = user self.password = password self.configDir = configDir self.numOfNodes = numOfNodes - self.clusterVersion = clusterVersion + self.clusterVersion = clusterVersion + self.dockerDir = dockerDir + self.removeFlag = removeFlag def getConnection(self): self.conn = taos.connect( @@ -42,14 +44,17 @@ class BuildDockerCluser: print("start arbitrator") os.system("docker exec -d $(docker ps|grep tdnode1|awk '{print $1}') tarbitrator") - def run(self): - if self.numOfNodes < 2 or self.numOfNodes > 5: - print("the number of nodes must be between 2 and 5") - exit(0) - os.system("./buildClusterEnv.sh -n %d -v %s" % (self.numOfNodes, self.clusterVersion)) + def run(self): + if self.numOfNodes < 2 or self.numOfNodes > 10: + print("the number of nodes must be between 2 and 10") + exit(0) + print("remove Flag value %s" % self.removeFlag) + if self.removeFlag == False: + os.system("./cleanClusterEnv.sh -d %s" % self.dockerDir) + os.system("./buildClusterEnv.sh -n %d -v %s -d %s" % (self.numOfNodes, self.clusterVersion, self.dockerDir)) self.getConnection() self.createDondes() - self.startArbitrator() + self.startArbitrator() parser = argparse.ArgumentParser() parser.add_argument( @@ -91,10 +96,24 @@ parser.add_argument( '-v', '--version', action='store', - default='2.0.14.1', + default='2.0.18.1', type=str, - help='the version of the cluster to be build, Default is 2.0.14.1') + help='the version of the cluster to be build, Default is 2.0.17.1') +parser.add_argument( + '-d', + '--docker-dir', + action='store', + default='/data', + type=str, + help='the data dir for docker, default is /data') +parser.add_argument( + '--flag', + action='store_true', + help='remove docker containers flag, default: True') args = parser.parse_args() -cluster = BuildDockerCluser(args.host, args.user, args.password, args.config_dir, args.num_of_nodes, args.version) -cluster.run() \ No newline at end of file +cluster = BuildDockerCluser(args.host, args.user, args.password, args.config_dir, args.num_of_nodes, args.version, args.docker_dir, args.flag) +cluster.run() + +# usage 1: python3 basic.py -n 2 --flag (flag is True) +# usage 2: python3 basic.py -n 2 (flag should be False when it is not specified) \ No newline at end of file diff --git a/tests/pytest/cluster/clusterEnvSetup/buildClusterEnv.sh b/tests/pytest/cluster/clusterEnvSetup/buildClusterEnv.sh index 968cdd1c1c81b9f6dba68bc2cca542038ada8606..0057a970ca096d15e17792805e469dd3180b7d83 100755 --- a/tests/pytest/cluster/clusterEnvSetup/buildClusterEnv.sh +++ b/tests/pytest/cluster/clusterEnvSetup/buildClusterEnv.sh @@ -1,18 +1,20 @@ #!/bin/bash echo "Executing buildClusterEnv.sh" -DOCKER_DIR=/data CURR_DIR=`pwd` +IN_TDINTERNAL="community" -if [ $# != 4 ]; then +if [ $# != 6 ]; then echo "argument list need input : " echo " -n numOfNodes" - echo " -v version" + echo " -v version" + echo " -d docker dir" exit 1 fi NUM_OF_NODES= VERSION= -while getopts "n:v:" arg +DOCKER_DIR= +while getopts "n:v:d:" arg do case $arg in n) @@ -20,6 +22,9 @@ do ;; v) VERSION=$OPTARG + ;; + d) + DOCKER_DIR=$OPTARG ;; ?) echo "unkonwn argument" @@ -28,32 +33,31 @@ do done function addTaoscfg { - for i in {1..5} + for((i=1;i<=$NUM_OF_NODES;i++)) do - touch /data/node$i/cfg/taos.cfg - echo 'firstEp tdnode1:6030' > /data/node$i/cfg/taos.cfg - echo 'fqdn tdnode'$i >> /data/node$i/cfg/taos.cfg - echo 'arbitrator tdnode1:6042' >> /data/node$i/cfg/taos.cfg + touch $DOCKER_DIR/node$i/cfg/taos.cfg + echo 'firstEp tdnode1:6030' > $DOCKER_DIR/node$i/cfg/taos.cfg + echo 'fqdn tdnode'$i >> $DOCKER_DIR/node$i/cfg/taos.cfg + echo 'arbitrator tdnode1:6042' >> $DOCKER_DIR/node$i/cfg/taos.cfg done } function createDIR { - for i in {1..5} + for((i=1;i<=$NUM_OF_NODES;i++)) do - mkdir -p /data/node$i/data - mkdir -p /data/node$i/log - mkdir -p /data/node$i/cfg - mkdir -p /data/node$i/core + mkdir -p $DOCKER_DIR/node$i/data + mkdir -p $DOCKER_DIR/node$i/log + mkdir -p $DOCKER_DIR/node$i/cfg + mkdir -p $DOCKER_DIR/node$i/core done } -function cleanEnv { - for i in {1..5} - do - echo /data/node$i/data/* - rm -rf /data/node$i/data/* - echo /data/node$i/log/* - rm -rf /data/node$i/log/* +function cleanEnv { + echo "Clean up docker environment" + for((i=1;i<=$NUM_OF_NODES;i++)) + do + rm -rf $DOCKER_DIR/node$i/data/* + rm -rf $DOCKER_DIR/node$i/log/* done } @@ -65,23 +69,48 @@ function prepareBuild { fi if [ ! -e $DOCKER_DIR/TDengine-server-$VERSION-Linux-x64.tar.gz ] || [ ! -e $DOCKER_DIR/TDengine-arbitrator-$VERSION-Linux-x64.tar.gz ]; then - cd $CURR_DIR/../../../../packaging + cd $CURR_DIR/../../../../packaging + echo $CURR_DIR + echo $IN_TDINTERNAL echo "generating TDeninger packages" - ./release.sh -v edge -n $VERSION >> /dev/null - - if [ ! -e $CURR_DIR/../../../../release/TDengine-server-$VERSION-Linux-x64.tar.gz ]; then - echo "no TDengine install package found" - exit 1 + if [[ "$CURR_DIR" == *"$IN_TDINTERNAL"* ]]; then + pwd + ./release.sh -v cluster -n $VERSION >> /dev/null 2>&1 + else + pwd + ./release.sh -v edge -n $VERSION >> /dev/null 2>&1 fi - if [ ! -e $CURR_DIR/../../../../release/TDengine-arbitrator-$VERSION-Linux-x64.tar.gz ]; then - echo "no arbitrator install package found" - exit 1 + if [[ "$CURR_DIR" == *"$IN_TDINTERNAL"* ]]; then + if [ ! -e $CURR_DIR/../../../../release/TDengine-enterprise-server-$VERSION-Linux-x64.tar.gz ]; then + echo "no TDengine install package found" + exit 1 + fi + + if [ ! -e $CURR_DIR/../../../../release/TDengine-enterprise-arbitrator-$VERSION-Linux-x64.tar.gz ]; then + echo "no arbitrator install package found" + exit 1 + fi + else + if [ ! -e $CURR_DIR/../../../../release/TDengine-server-$VERSION-Linux-x64.tar.gz ]; then + echo "no TDengine install package found" + exit 1 + fi + + if [ ! -e $CURR_DIR/../../../../release/TDengine-arbitrator-$VERSION-Linux-x64.tar.gz ]; then + echo "no arbitrator install package found" + exit 1 + fi fi cd $CURR_DIR/../../../../release - mv TDengine-server-$VERSION-Linux-x64.tar.gz $DOCKER_DIR - mv TDengine-arbitrator-$VERSION-Linux-x64.tar.gz $DOCKER_DIR + if [[ "$CURR_DIR" == *"$IN_TDINTERNAL"* ]]; then + mv TDengine-enterprise-server-$VERSION-Linux-x64.tar.gz $DOCKER_DIR + mv TDengine-enterprise-arbitrator-$VERSION-Linux-x64.tar.gz $DOCKER_DIR + else + mv TDengine-server-$VERSION-Linux-x64.tar.gz $DOCKER_DIR + mv TDengine-arbitrator-$VERSION-Linux-x64.tar.gz $DOCKER_DIR + fi fi rm -rf $DOCKER_DIR/*.yml @@ -96,23 +125,31 @@ function clusterUp { cd $DOCKER_DIR - if [ $NUM_OF_NODES -eq 2 ]; then - echo "create 2 dnodes" - PACKAGE=TDengine-server-$VERSION-Linux-x64.tar.gz DIR=TDengine-server-$VERSION DIR2=TDengine-arbitrator-$VERSION VERSION=$VERSION docker-compose up -d - fi - - if [ $NUM_OF_NODES -eq 3 ]; then - PACKAGE=TDengine-server-$VERSION-Linux-x64.tar.gz DIR=TDengine-server-$VERSION DIR2=TDengine-arbitrator-$VERSION VERSION=$VERSION docker-compose -f docker-compose.yml -f node3.yml up -d - fi - - if [ $NUM_OF_NODES -eq 4 ]; then - PACKAGE=TDengine-server-$VERSION-Linux-x64.tar.gz DIR=TDengine-server-$VERSION DIR2=TDengine-arbitrator-$VERSION VERSION=$VERSION docker-compose -f docker-compose.yml -f node3.yml -f node4.yml up -d + if [[ "$CURR_DIR" == *"$IN_TDINTERNAL"* ]]; then + docker_run="PACKAGE=TDengine-enterprise-server-$VERSION-Linux-x64.tar.gz TARBITRATORPKG=TDengine-enterprise-arbitrator-$VERSION-Linux-x64.tar.gz DIR=TDengine-enterprise-server-$VERSION DIR2=TDengine-enterprise-arbitrator-$VERSION VERSION=$VERSION DATADIR=$DOCKER_DIR docker-compose -f docker-compose.yml " + else + docker_run="PACKAGE=TDengine-server-$VERSION-Linux-x64.tar.gz TARBITRATORPKG=TDengine-arbitrator-$VERSION-Linux-x64.tar.gz DIR=TDengine-server-$VERSION DIR2=TDengine-arbitrator-$VERSION VERSION=$VERSION DATADIR=$DOCKER_DIR docker-compose -f docker-compose.yml " fi - if [ $NUM_OF_NODES -eq 5 ]; then - PACKAGE=TDengine-server-$VERSION-Linux-x64.tar.gz DIR=TDengine-server-$VERSION DIR2=TDengine-arbitrator-$VERSION VERSION=$VERSION docker-compose -f docker-compose.yml -f node3.yml -f node4.yml -f node5.yml up -d + if [ $NUM_OF_NODES -ge 2 ];then + echo "create $NUM_OF_NODES dnodes" + for((i=3;i<=$NUM_OF_NODES;i++)) + do + if [ ! -f node$i.yml ];then + echo "node$i.yml not exist" + cp node3.yml node$i.yml + sed -i "s/td2.0-node3/td2.0-node$i/g" node$i.yml + sed -i "s/'tdnode3'/'tdnode$i'/g" node$i.yml + sed -i "s#/node3/#/node$i/#g" node$i.yml + sed -i "s#hostname: tdnode3#hostname: tdnode$i#g" node$i.yml + sed -i "s#ipv4_address: 172.27.0.9#ipv4_address: 172.27.0.`expr $i + 6`#g" node$i.yml + fi + docker_run=$docker_run" -f node$i.yml " + done + docker_run=$docker_run" up -d" fi - + echo $docker_run |sh + echo "docker compose finish" } diff --git a/tests/pytest/cluster/clusterEnvSetup/cleanClusterEnv.sh b/tests/pytest/cluster/clusterEnvSetup/cleanClusterEnv.sh new file mode 100755 index 0000000000000000000000000000000000000000..675cae5fef850947c2c53899ef1863dc00b55422 --- /dev/null +++ b/tests/pytest/cluster/clusterEnvSetup/cleanClusterEnv.sh @@ -0,0 +1,39 @@ +#!/bin/bash +echo "Executing cleanClusterEnv.sh" +CURR_DIR=`pwd` + +if [ $# != 2 ]; then + echo "argument list need input : " + echo " -d docker dir" + exit 1 +fi + +DOCKER_DIR= +while getopts "d:" arg +do + case $arg in + d) + DOCKER_DIR=$OPTARG + ;; + ?) + echo "unkonwn argument" + ;; + esac +done + +function removeDockerContainers { + cd $DOCKER_DIR + docker-compose down --remove-orphans +} + +function cleanEnv { + echo "Clean up docker environment" + for i in {1..10} + do + rm -rf $DOCKER_DIR/node$i/data/* + rm -rf $DOCKER_DIR/node$i/log/* + done +} + +removeDockerContainers +cleanEnv \ No newline at end of file diff --git a/tests/pytest/cluster/clusterEnvSetup/docker-compose.yml b/tests/pytest/cluster/clusterEnvSetup/docker-compose.yml index cb35abd9a1497c92dee10e1e6fb95027fb21710c..d241062a2dd50fa82f934d9e915cddeb74505248 100644 --- a/tests/pytest/cluster/clusterEnvSetup/docker-compose.yml +++ b/tests/pytest/cluster/clusterEnvSetup/docker-compose.yml @@ -9,6 +9,7 @@ services: - TARBITRATORPKG=${TARBITRATORPKG} - EXTRACTDIR=${DIR} - EXTRACTDIR2=${DIR2} + - DATADIR=${DATADIR} image: 'tdengine:${VERSION}' container_name: 'tdnode1' cap_add: @@ -29,22 +30,27 @@ services: - "tdnode3:172.27.0.9" - "tdnode4:172.27.0.10" - "tdnode5:172.27.0.11" + - "tdnode6:172.27.0.12" + - "tdnode7:172.27.0.13" + - "tdnode8:172.27.0.14" + - "tdnode9:172.27.0.15" + - "tdnode10:172.27.0.16" volumes: # bind data directory - type: bind - source: /data/node1/data + source: ${DATADIR}/node1/data target: /var/lib/taos # bind log directory - type: bind - source: /data/node1/log + source: ${DATADIR}/node1/log target: /var/log/taos # bind configuration - type: bind - source: /data/node1/cfg + source: ${DATADIR}/node1/cfg target: /etc/taos # bind core dump path - type: bind - source: /data/node1/core + source: ${DATADIR}/node1/core target: /coredump - type: bind source: /data @@ -60,7 +66,10 @@ services: context: . args: - PACKAGE=${PACKAGE} + - TARBITRATORPKG=${TARBITRATORPKG} - EXTRACTDIR=${DIR} + - EXTRACTDIR2=${DIR2} + - DATADIR=${DATADIR} image: 'tdengine:${VERSION}' container_name: 'tdnode2' cap_add: @@ -84,22 +93,22 @@ services: volumes: # bind data directory - type: bind - source: /data/node2/data + source: ${DATADIR}/node2/data target: /var/lib/taos # bind log directory - type: bind - source: /data/node2/log + source: ${DATADIR}/node2/log target: /var/log/taos # bind configuration - type: bind - source: /data/node2/cfg + source: ${DATADIR}/node2/cfg target: /etc/taos # bind core dump path - type: bind - source: /data/node2/core + source: ${DATADIR}/node2/core target: /coredump - type: bind - source: /data + source: ${DATADIR} target: /root hostname: tdnode2 networks: diff --git a/tests/pytest/cluster/clusterEnvSetup/node3.yml b/tests/pytest/cluster/clusterEnvSetup/node3.yml index 4f4f3a6f991f0ffff51265fee4c5a3b8941b5d85..18f1b37c1c029f50cc6e66e662ec2a42bc475c5e 100644 --- a/tests/pytest/cluster/clusterEnvSetup/node3.yml +++ b/tests/pytest/cluster/clusterEnvSetup/node3.yml @@ -6,7 +6,10 @@ services: context: . args: - PACKAGE=${PACKAGE} + - TARBITRATORPKG=${TARBITRATORPKG} - EXTRACTDIR=${DIR} + - EXTRACTDIR2=${DIR2} + - DATADIR=${DATADIR} image: 'tdengine:${VERSION}' container_name: 'tdnode3' cap_add: @@ -23,29 +26,34 @@ services: sysctl -p && exec my-main-application" extra_hosts: - - "tdnode1:172.27.0.7" - "tdnode2:172.27.0.8" + - "tdnode3:172.27.0.9" - "tdnode4:172.27.0.10" - "tdnode5:172.27.0.11" + - "tdnode6:172.27.0.12" + - "tdnode7:172.27.0.13" + - "tdnode8:172.27.0.14" + - "tdnode9:172.27.0.15" + - "tdnode10:172.27.0.16" volumes: # bind data directory - type: bind - source: /data/node3/data + source: ${DATADIR}/node3/data target: /var/lib/taos # bind log directory - type: bind - source: /data/node3/log + source: ${DATADIR}/node3/log target: /var/log/taos # bind configuration - type: bind - source: /data/node3/cfg + source: ${DATADIR}/node3/cfg target: /etc/taos # bind core dump path - type: bind - source: /data/node3/core + source: ${DATADIR}/node3/core target: /coredump - type: bind - source: /data + source: ${DATADIR} target: /root hostname: tdnode3 networks: diff --git a/tests/pytest/cluster/clusterEnvSetup/node4.yml b/tests/pytest/cluster/clusterEnvSetup/node4.yml index c82a174cb883b14c885de7c5e8f19d98263b22b7..f542c22c4537dd6dc2c529dff39ba93c5c0ec3a4 100644 --- a/tests/pytest/cluster/clusterEnvSetup/node4.yml +++ b/tests/pytest/cluster/clusterEnvSetup/node4.yml @@ -6,7 +6,10 @@ services: context: . args: - PACKAGE=${PACKAGE} + - TARBITRATORPKG=${TARBITRATORPKG} - EXTRACTDIR=${DIR} + - EXTRACTDIR2=${DIR2} + - DATADIR=${DATADIR} image: 'tdengine:${VERSION}' container_name: 'tdnode4' cap_add: @@ -27,26 +30,31 @@ services: - "tdnode3:172.27.0.9" - "tdnode4:172.27.0.10" - "tdnode5:172.27.0.11" + - "tdnode6:172.27.0.12" + - "tdnode7:172.27.0.13" + - "tdnode8:172.27.0.14" + - "tdnode9:172.27.0.15" + - "tdnode10:172.27.0.16" volumes: # bind data directory - type: bind - source: /data/node4/data + source: ${DATADIR}/node4/data target: /var/lib/taos # bind log directory - type: bind - source: /data/node4/log + source: ${DATADIR}/node4/log target: /var/log/taos # bind configuration - type: bind - source: /data/node4/cfg + source: ${DATADIR}/node4/cfg target: /etc/taos - # bind core dump path + # bind core dump path - type: bind - source: /data/node4/core + source: ${DATADIR}/node4/core target: /coredump - type: bind - source: /data - target: /root + source: ${DATADIR} + target: /root hostname: tdnode4 networks: taos_update_net: diff --git a/tests/pytest/cluster/clusterEnvSetup/node5.yml b/tests/pytest/cluster/clusterEnvSetup/node5.yml index 2e37e47512430ac99244f6b2f0e2d309a2145edc..dd5941ac769273031c98bca3248adcf633240088 100644 --- a/tests/pytest/cluster/clusterEnvSetup/node5.yml +++ b/tests/pytest/cluster/clusterEnvSetup/node5.yml @@ -6,7 +6,10 @@ services: context: . args: - PACKAGE=${PACKAGE} + - TARBITRATORPKG=${TARBITRATORPKG} - EXTRACTDIR=${DIR} + - EXTRACTDIR2=${DIR2} + - DATADIR=${DATADIR} image: 'tdengine:${VERSION}' container_name: 'tdnode5' cap_add: @@ -27,25 +30,30 @@ services: - "tdnode3:172.27.0.9" - "tdnode4:172.27.0.10" - "tdnode5:172.27.0.11" + - "tdnode6:172.27.0.12" + - "tdnode7:172.27.0.13" + - "tdnode8:172.27.0.14" + - "tdnode9:172.27.0.15" + - "tdnode10:172.27.0.16" volumes: # bind data directory - type: bind - source: /data/node5/data + source: ${DATADIR}/node5/data target: /var/lib/taos # bind log directory - type: bind - source: /data/node5/log + source: ${DATADIR}/node5/log target: /var/log/taos # bind configuration - type: bind - source: /data/node5/cfg + source: ${DATADIR}/node5/cfg target: /etc/taos # bind core dump path - type: bind - source: /data/node5/core + source: ${DATADIR}/node5/core target: /coredump - type: bind - source: /data + source: ${DATADIR} target: /root hostname: tdnode5 networks: diff --git a/tests/pytest/fulltest.sh b/tests/pytest/fulltest.sh index d830d96865a3dfac2a0244c796a01516fba0369c..5037f2c399bdc1f1a291dac774e1fde1ab2cb6ed 100755 --- a/tests/pytest/fulltest.sh +++ b/tests/pytest/fulltest.sh @@ -1,18 +1,15 @@ #!/bin/bash ulimit -c unlimited +#======================p1-start=============== python3 ./test.py -f insert/basic.py python3 ./test.py -f insert/int.py -python3 ./test.py -f insert/unsignedInt.py python3 ./test.py -f insert/float.py python3 ./test.py -f insert/bigint.py -python3 ./test.py -f insert/unsignedBigint.py python3 ./test.py -f insert/bool.py python3 ./test.py -f insert/double.py python3 ./test.py -f insert/smallint.py -python3 ./test.py -f insert/unsignedSmallint.py python3 ./test.py -f insert/tinyint.py -python3 ./test.py -f insert/unsignedTinyint.py python3 ./test.py -f insert/date.py python3 ./test.py -f insert/binary.py python3 ./test.py -f insert/nchar.py @@ -23,10 +20,8 @@ python3 ./test.py -f insert/randomNullCommit.py python3 insert/retentionpolicy.py python3 ./test.py -f insert/alterTableAndInsert.py python3 ./test.py -f insert/insertIntoTwoTables.py -#python3 ./test.py -f insert/before_1970.py -python3 ./test.py -f insert/metadataUpdate.py +python3 ./test.py -f insert/before_1970.py python3 bug2265.py -python3 ./test.py -f insert/boundary2.py #table python3 ./test.py -f table/alter_wal0.py @@ -71,7 +66,7 @@ python3 ./test.py -f tag_lite/int.py python3 ./test.py -f tag_lite/set.py python3 ./test.py -f tag_lite/smallint.py python3 ./test.py -f tag_lite/tinyint.py -python3 ./test.py -f tag_lite/alter_tag.py + #python3 ./test.py -f dbmgmt/database-name-boundary.py python3 ./test.py -f import_merge/importBlock1HO.py @@ -142,6 +137,26 @@ python3 ./test.py -f import_merge/importTPORestart.py python3 ./test.py -f import_merge/importTRestart.py python3 ./test.py -f import_merge/importInsertThenImport.py python3 ./test.py -f import_merge/importCSV.py +#======================p1-end=============== +#======================p2-start=============== +# update +python3 ./test.py -f update/allow_update.py +python3 ./test.py -f update/allow_update-0.py +python3 ./test.py -f update/append_commit_data.py +python3 ./test.py -f update/append_commit_last-0.py +python3 ./test.py -f update/append_commit_last.py +python3 ./test.py -f update/merge_commit_data.py + +python3 ./test.py -f update/merge_commit_data2.py +python3 ./test.py -f update/merge_commit_data2_update0.py +python3 ./test.py -f update/merge_commit_last-0.py +python3 ./test.py -f update/merge_commit_last.py +python3 ./test.py -f update/bug_td2279.py + +#======================p2-end=============== +#======================p3-start=============== + + # user python3 ./test.py -f user/user_create.py python3 ./test.py -f user/pass_len.py @@ -155,7 +170,6 @@ python3 ./test.py -f query/filterCombo.py python3 ./test.py -f query/queryNormal.py python3 ./test.py -f query/queryError.py python3 ./test.py -f query/filterAllIntTypes.py -python3 ./test.py -f query/filterAllUnsignedIntTypes.py python3 ./test.py -f query/filterFloatAndDouble.py python3 ./test.py -f query/filterOtherTypes.py python3 ./test.py -f query/querySort.py @@ -182,8 +196,6 @@ python3 ./test.py -f query/bug2119.py python3 ./test.py -f query/isNullTest.py python3 ./test.py -f query/queryWithTaosdKilled.py python3 ./test.py -f query/floatCompare.py -python3 ./test.py -f query/queryGroupbySort.py -python3 ./test.py -f query/queryBetweenAnd.py #stream python3 ./test.py -f stream/metric_1.py @@ -199,8 +211,6 @@ python3 ./test.py -f stream/table_n.py #alter table python3 ./test.py -f alter/alter_table_crash.py -python3 ./test.py -f alter/alter_table.py -python3 ./test.py -f alter/alter_debugFlag.py # client python3 ./test.py -f client/client.py @@ -213,6 +223,38 @@ python3 testCompress.py python3 testNoCompress.py python3 testMinTablesPerVnode.py + +python3 queryCount.py +python3 ./test.py -f query/queryGroupbyWithInterval.py +python3 client/twoClients.py +python3 test.py -f query/queryInterval.py +python3 test.py -f query/queryFillTest.py + +# tools +python3 test.py -f tools/taosdemoTest.py +python3 test.py -f tools/taosdemoTestWithoutMetric.py +python3 test.py -f tools/taosdemoTestWithJson.py +python3 test.py -f tools/taosdemoTestLimitOffset.py +python3 test.py -f tools/taosdumpTest.py +python3 test.py -f tools/taosdemoTest2.py +python3 test.py -f tools/taosdemoTestSampleData.py +python3 test.py -f tools/taosdemoTestInterlace.py + +# subscribe +python3 test.py -f subscribe/singlemeter.py +#python3 test.py -f subscribe/stability.py +python3 test.py -f subscribe/supertable.py + + +#======================p3-end=============== +#======================p4-start=============== + +python3 ./test.py -f update/merge_commit_data-0.py +# wal +python3 ./test.py -f wal/addOldWalTest.py + +# function +python3 ./test.py -f functions/all_null_value.py # functions python3 ./test.py -f functions/function_avg.py -r 1 python3 ./test.py -f functions/function_bottom.py -r 1 @@ -232,46 +274,24 @@ python3 ./test.py -f functions/function_sum.py -r 1 python3 ./test.py -f functions/function_top.py -r 1 python3 ./test.py -f functions/function_twa.py -r 1 python3 ./test.py -f functions/function_twa_test2.py -python3 ./test.py -f functions/all_null_value.py -python3 ./test.py -f functions/function_percentile2.py -python3 queryCount.py -python3 ./test.py -f query/queryGroupbyWithInterval.py -python3 client/twoClients.py -python3 ./test.py -f query/queryInterval.py -python3 ./test.py -f query/queryFillTest.py -python3 ./test.py -f query/last_row_cache.py +python3 ./test.py -f functions/function_stddev_td2555.py +python3 ./test.py -f insert/metadataUpdate.py python3 ./test.py -f query/last_cache.py +python3 ./test.py -f query/last_row_cache.py +python3 ./test.py -f account/account_create.py +python3 ./test.py -f alter/alter_table.py +python3 ./test.py -f query/queryGroupbySort.py -# tools -python3 test.py -f tools/taosdumpTest.py -python3 test.py -f tools/lowaTest.py -python3 test.py -f tools/taosdemoTest.py -python3 test.py -f tools/taosdemoTest2.py -python3 test.py -f tools/taosdemoTestWithoutMetric.py -python3 test.py -f tools/taosdemoTestLimitOffset.py - -# subscribe -python3 test.py -f subscribe/singlemeter.py -#python3 test.py -f subscribe/stability.py -python3 test.py -f subscribe/supertable.py - - -# update -python3 ./test.py -f update/allow_update.py -python3 ./test.py -f update/allow_update-0.py -python3 ./test.py -f update/append_commit_data.py -python3 ./test.py -f update/append_commit_last-0.py -python3 ./test.py -f update/append_commit_last.py -python3 ./test.py -f update/merge_commit_data.py -python3 ./test.py -f update/merge_commit_data-0.py -python3 ./test.py -f update/merge_commit_data2.py -python3 ./test.py -f update/merge_commit_data2_update0.py -python3 ./test.py -f update/merge_commit_last-0.py -python3 ./test.py -f update/merge_commit_last.py -python3 ./test.py -f update/bug_td2279.py +python3 ./test.py -f insert/unsignedInt.py +python3 ./test.py -f insert/unsignedBigint.py +python3 ./test.py -f insert/unsignedSmallint.py +python3 ./test.py -f insert/unsignedTinyint.py +python3 ./test.py -f query/filterAllUnsignedIntTypes.py -# wal -python3 ./test.py -f wal/addOldWalTest.py +python3 ./test.py -f functions/function_percentile2.py +python3 ./test.py -f insert/boundary2.py +python3 ./test.py -f alter/alter_debugFlag.py +python3 ./test.py -f query/queryBetweenAnd.py +python3 ./test.py -f tag_lite/alter_tag.py -# account -python3 ./test.py -f account/account_create.py +#======================p4-end=============== diff --git a/tests/pytest/functions/function_operations.py b/tests/pytest/functions/function_operations.py index e703147b6722f848263f170b89ce9f972f7a2435..162aa3eb658a60615c9f20de0c363f9066096f0c 100644 --- a/tests/pytest/functions/function_operations.py +++ b/tests/pytest/functions/function_operations.py @@ -92,14 +92,16 @@ class TDTestCase: col_list = [ 'col1' , 'col2' , 'col3' , 'col4' , 'col5' , 'col6' , 'col7' , 'col8' , 'col9' , 'col11' , 'col12' , 'col13' , 'col14' , '1' , '1.1' , 'NULL' ] op_list = [ '+' , '-' , '*' , '/' , '%' ] err_list = [ 'col7' , 'col8' , 'col9' , 'NULL' ] + order_lsit = [ ' order by ts ', ' order by ts desc ', ' order by ts asc '] for i in col_list : for j in col_list : for k in op_list : - sql = " select %s %s %s from test1 " % ( i , k , j ) - if i in err_list or j in err_list: - tdSql.error(sql) - else: - tdSql.query(sql) + for l in order_lsit : + sql = " select %s %s %s from test1 %s" % ( i , k , j , l ) + if i in err_list or j in err_list: + tdSql.error(sql) + else: + tdSql.query(sql) def stop(self): tdSql.close() tdLog.success("%s successfully executed" % __file__) diff --git a/tests/pytest/functions/function_stddev.py b/tests/pytest/functions/function_stddev.py index afc59ac83d7156253225bf6c111e7b5f0e6f9dcb..3ff2b82bf6b326ed4d07a5a51027c9e266c2fd72 100644 --- a/tests/pytest/functions/function_stddev.py +++ b/tests/pytest/functions/function_stddev.py @@ -118,7 +118,11 @@ class TDTestCase: if i == 1 or i == 5 or i == 6 or i == 7 or i == 9 or i == 8 :continue tdSql.query('select stddev(c%d),stddev(c%d) from s group by c%d' %( i+1 , i+1 , i+1 ) ) - + #add for TD-3318 + tdSql.execute('create table t1(ts timestamp, k int, b binary(12));') + tdSql.execute("insert into t1 values(now, 1, 'abc');") + tdLog.info("select stddev(k) from t1 where b <> 'abc' interval(1s);") + tdSql.query("select stddev(k) from t1 where b <> 'abc' interval(1s);") def stop(self): diff --git a/tests/pytest/handle_crash_gen_val_log.sh b/tests/pytest/handle_crash_gen_val_log.sh index 502c859dad85a8fcceee7e0efaaa46ab9bded02a..55c10639d7af941eda21cf33cbf7050c95fe3539 100755 --- a/tests/pytest/handle_crash_gen_val_log.sh +++ b/tests/pytest/handle_crash_gen_val_log.sh @@ -16,7 +16,7 @@ TOP_DIR=`pwd` TAOSD_DIR=`find . -name "taosd"|grep -v community|head -n1` nohup $TAOSD_DIR >/dev/null & cd - -./crash_gen.sh --valgrind -p -t 10 -s 500 -b 4 +./crash_gen.sh --valgrind -p -t 10 -s 1000 -b 4 pidof taosd|xargs kill -9 grep 'start to execute\|ERROR SUMMARY' valgrind.err|grep -v 'grep'|uniq|tee crash_gen_mem_err.log diff --git a/tests/pytest/pytest_1.sh b/tests/pytest/pytest_1.sh deleted file mode 100755 index e6638cbb171ab3d8dc38c13f5767ff2f09c522bb..0000000000000000000000000000000000000000 --- a/tests/pytest/pytest_1.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/bash -ulimit -c unlimited - -python3 ./test.py -f insert/basic.py -python3 ./test.py -f insert/int.py -python3 ./test.py -f insert/float.py -python3 ./test.py -f insert/bigint.py -python3 ./test.py -f insert/bool.py -python3 ./test.py -f insert/double.py -python3 ./test.py -f insert/smallint.py -python3 ./test.py -f insert/tinyint.py -python3 ./test.py -f insert/date.py -python3 ./test.py -f insert/binary.py -python3 ./test.py -f insert/nchar.py -#python3 ./test.py -f insert/nchar-boundary.py -python3 ./test.py -f insert/nchar-unicode.py -python3 ./test.py -f insert/multi.py -python3 ./test.py -f insert/randomNullCommit.py -python3 insert/retentionpolicy.py -python3 ./test.py -f insert/alterTableAndInsert.py -python3 ./test.py -f insert/insertIntoTwoTables.py -python3 ./test.py -f insert/before_1970.py -python3 bug2265.py - -#table -python3 ./test.py -f table/alter_wal0.py -python3 ./test.py -f table/column_name.py -python3 ./test.py -f table/column_num.py -python3 ./test.py -f table/db_table.py -python3 ./test.py -f table/create_sensitive.py -#python3 ./test.py -f table/tablename-boundary.py -python3 ./test.py -f table/max_table_length.py -python3 ./test.py -f table/alter_column.py -python3 ./test.py -f table/boundary.py -python3 ./test.py -f table/create.py -python3 ./test.py -f table/del_stable.py - - -# tag -python3 ./test.py -f tag_lite/filter.py -python3 ./test.py -f tag_lite/create-tags-boundary.py -python3 ./test.py -f tag_lite/3.py -python3 ./test.py -f tag_lite/4.py -python3 ./test.py -f tag_lite/5.py -python3 ./test.py -f tag_lite/6.py -python3 ./test.py -f tag_lite/add.py -python3 ./test.py -f tag_lite/bigint.py -python3 ./test.py -f tag_lite/binary_binary.py -python3 ./test.py -f tag_lite/binary.py -python3 ./test.py -f tag_lite/bool_binary.py -python3 ./test.py -f tag_lite/bool_int.py -python3 ./test.py -f tag_lite/bool.py -python3 ./test.py -f tag_lite/change.py -python3 ./test.py -f tag_lite/column.py -python3 ./test.py -f tag_lite/commit.py -python3 ./test.py -f tag_lite/create.py -python3 ./test.py -f tag_lite/datatype.py -python3 ./test.py -f tag_lite/datatype-without-alter.py -python3 ./test.py -f tag_lite/delete.py -python3 ./test.py -f tag_lite/double.py -python3 ./test.py -f tag_lite/float.py -python3 ./test.py -f tag_lite/int_binary.py -python3 ./test.py -f tag_lite/int_float.py -python3 ./test.py -f tag_lite/int.py -python3 ./test.py -f tag_lite/set.py -python3 ./test.py -f tag_lite/smallint.py -python3 ./test.py -f tag_lite/tinyint.py - -#python3 ./test.py -f dbmgmt/database-name-boundary.py - -python3 ./test.py -f import_merge/importBlock1HO.py -python3 ./test.py -f import_merge/importBlock1HPO.py -python3 ./test.py -f import_merge/importBlock1H.py -python3 ./test.py -f import_merge/importBlock1S.py -python3 ./test.py -f import_merge/importBlock1Sub.py -python3 ./test.py -f import_merge/importBlock1TO.py -python3 ./test.py -f import_merge/importBlock1TPO.py -python3 ./test.py -f import_merge/importBlock1T.py -python3 ./test.py -f import_merge/importBlock2HO.py -python3 ./test.py -f import_merge/importBlock2HPO.py -python3 ./test.py -f import_merge/importBlock2H.py -python3 ./test.py -f import_merge/importBlock2S.py -python3 ./test.py -f import_merge/importBlock2Sub.py -python3 ./test.py -f import_merge/importBlock2TO.py -python3 ./test.py -f import_merge/importBlock2TPO.py -python3 ./test.py -f import_merge/importBlock2T.py -python3 ./test.py -f import_merge/importBlockbetween.py -python3 ./test.py -f import_merge/importCacheFileHO.py -python3 ./test.py -f import_merge/importCacheFileHPO.py -python3 ./test.py -f import_merge/importCacheFileH.py -python3 ./test.py -f import_merge/importCacheFileS.py -python3 ./test.py -f import_merge/importCacheFileSub.py -python3 ./test.py -f import_merge/importCacheFileTO.py -python3 ./test.py -f import_merge/importCacheFileTPO.py -python3 ./test.py -f import_merge/importCacheFileT.py -python3 ./test.py -f import_merge/importDataH2.py -python3 ./test.py -f import_merge/importDataHO2.py -python3 ./test.py -f import_merge/importDataHO.py -python3 ./test.py -f import_merge/importDataHPO.py -python3 ./test.py -f import_merge/importDataLastHO.py -python3 ./test.py -f import_merge/importDataLastHPO.py -python3 ./test.py -f import_merge/importDataLastH.py -python3 ./test.py -f import_merge/importDataLastS.py -python3 ./test.py -f import_merge/importDataLastSub.py -python3 ./test.py -f import_merge/importDataLastTO.py -python3 ./test.py -f import_merge/importDataLastTPO.py -python3 ./test.py -f import_merge/importDataLastT.py -python3 ./test.py -f import_merge/importDataS.py -python3 ./test.py -f import_merge/importDataSub.py -python3 ./test.py -f import_merge/importDataTO.py -python3 ./test.py -f import_merge/importDataTPO.py -python3 ./test.py -f import_merge/importDataT.py -python3 ./test.py -f import_merge/importHeadOverlap.py -python3 ./test.py -f import_merge/importHeadPartOverlap.py -python3 ./test.py -f import_merge/importHead.py -python3 ./test.py -f import_merge/importHORestart.py -python3 ./test.py -f import_merge/importHPORestart.py -python3 ./test.py -f import_merge/importHRestart.py -python3 ./test.py -f import_merge/importLastHO.py -python3 ./test.py -f import_merge/importLastHPO.py -python3 ./test.py -f import_merge/importLastH.py -python3 ./test.py -f import_merge/importLastS.py -python3 ./test.py -f import_merge/importLastSub.py -python3 ./test.py -f import_merge/importLastTO.py -python3 ./test.py -f import_merge/importLastTPO.py -python3 ./test.py -f import_merge/importLastT.py -python3 ./test.py -f import_merge/importSpan.py -python3 ./test.py -f import_merge/importSRestart.py -python3 ./test.py -f import_merge/importSubRestart.py -python3 ./test.py -f import_merge/importTailOverlap.py -python3 ./test.py -f import_merge/importTailPartOverlap.py -python3 ./test.py -f import_merge/importTail.py -python3 ./test.py -f import_merge/importToCommit.py -python3 ./test.py -f import_merge/importTORestart.py -python3 ./test.py -f import_merge/importTPORestart.py -python3 ./test.py -f import_merge/importTRestart.py -python3 ./test.py -f import_merge/importInsertThenImport.py -python3 ./test.py -f import_merge/importCSV.py \ No newline at end of file diff --git a/tests/pytest/pytest_2.sh b/tests/pytest/pytest_2.sh deleted file mode 100755 index d152ed85fb9e138a6b9d62574bcc8a5119973448..0000000000000000000000000000000000000000 --- a/tests/pytest/pytest_2.sh +++ /dev/null @@ -1,16 +0,0 @@ - - -# update -python3 ./test.py -f update/allow_update.py -python3 ./test.py -f update/allow_update-0.py -python3 ./test.py -f update/append_commit_data.py -python3 ./test.py -f update/append_commit_last-0.py -python3 ./test.py -f update/append_commit_last.py -python3 ./test.py -f update/merge_commit_data.py - -python3 ./test.py -f update/merge_commit_data2.py -python3 ./test.py -f update/merge_commit_data2_update0.py -python3 ./test.py -f update/merge_commit_last-0.py -python3 ./test.py -f update/merge_commit_last.py -python3 ./test.py -f update/bug_td2279.py - diff --git a/tests/pytest/pytest_3.sh b/tests/pytest/pytest_3.sh deleted file mode 100755 index f2462154bc60f845600d6f819edf01925cb86938..0000000000000000000000000000000000000000 --- a/tests/pytest/pytest_3.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash -ulimit -c unlimited - - -python3 ./test.py -f insert/randomNullCommit.py - -# user -python3 ./test.py -f user/user_create.py -python3 ./test.py -f user/pass_len.py - -# stable -python3 ./test.py -f stable/query_after_reset.py - -#query -python3 ./test.py -f query/filter.py -python3 ./test.py -f query/filterCombo.py -python3 ./test.py -f query/queryNormal.py -python3 ./test.py -f query/queryError.py -python3 ./test.py -f query/filterAllIntTypes.py -python3 ./test.py -f query/filterFloatAndDouble.py -python3 ./test.py -f query/filterOtherTypes.py -python3 ./test.py -f query/querySort.py -python3 ./test.py -f query/queryJoin.py -python3 ./test.py -f query/select_last_crash.py -python3 ./test.py -f query/queryNullValueTest.py -python3 ./test.py -f query/queryInsertValue.py -python3 ./test.py -f query/queryConnection.py -python3 ./test.py -f query/queryCountCSVData.py -python3 ./test.py -f query/natualInterval.py -python3 ./test.py -f query/bug1471.py -#python3 ./test.py -f query/dataLossTest.py -python3 ./test.py -f query/bug1874.py -python3 ./test.py -f query/bug1875.py -python3 ./test.py -f query/bug1876.py -python3 ./test.py -f query/bug2218.py -python3 ./test.py -f query/bug2117.py -python3 ./test.py -f query/bug2118.py -python3 ./test.py -f query/bug2143.py -python3 ./test.py -f query/sliding.py -python3 ./test.py -f query/unionAllTest.py -python3 ./test.py -f query/bug2281.py -python3 ./test.py -f query/bug2119.py -python3 ./test.py -f query/isNullTest.py -python3 ./test.py -f query/queryWithTaosdKilled.py -python3 ./test.py -f query/floatCompare.py - -#stream -python3 ./test.py -f stream/metric_1.py -python3 ./test.py -f stream/metric_n.py -python3 ./test.py -f stream/new.py -python3 ./test.py -f stream/stream1.py -python3 ./test.py -f stream/stream2.py -#python3 ./test.py -f stream/parser.py -python3 ./test.py -f stream/history.py -python3 ./test.py -f stream/sys.py -python3 ./test.py -f stream/table_1.py -python3 ./test.py -f stream/table_n.py - -#alter table -python3 ./test.py -f alter/alter_table_crash.py - -# client -python3 ./test.py -f client/client.py -python3 ./test.py -f client/version.py -python3 ./test.py -f client/alterDatabase.py -python3 ./test.py -f client/noConnectionErrorTest.py - -# Misc -python3 testCompress.py -python3 testNoCompress.py -python3 testMinTablesPerVnode.py - - -python3 queryCount.py -python3 ./test.py -f query/queryGroupbyWithInterval.py -python3 client/twoClients.py -python3 test.py -f query/queryInterval.py -python3 test.py -f query/queryFillTest.py - -# tools -python3 test.py -f tools/lowaTest.py -python3 test.py -f tools/taosdemoTest.py -python3 test.py -f tools/taosdemoTestWithoutMetric.py -python3 test.py -f tools/taosdemoTestLimitOffset.py -python3 test.py -f tools/taosdumpTest.py -#python3 test.py -f tools/taosdemoTest2.py - -# subscribe -python3 test.py -f subscribe/singlemeter.py -#python3 test.py -f subscribe/stability.py -python3 test.py -f subscribe/supertable.py - diff --git a/tests/pytest/pytest_4.sh b/tests/pytest/pytest_4.sh deleted file mode 100755 index dada90c709a967df37693d7398692c6d7fc12039..0000000000000000000000000000000000000000 --- a/tests/pytest/pytest_4.sh +++ /dev/null @@ -1,39 +0,0 @@ -python3 ./test.py -f update/merge_commit_data-0.py -# wal -python3 ./test.py -f wal/addOldWalTest.py - -# function -python3 ./test.py -f functions/all_null_value.py -# functions -python3 ./test.py -f functions/function_avg.py -r 1 -python3 ./test.py -f functions/function_bottom.py -r 1 -python3 ./test.py -f functions/function_count.py -r 1 -python3 ./test.py -f functions/function_diff.py -r 1 -python3 ./test.py -f functions/function_first.py -r 1 -python3 ./test.py -f functions/function_last.py -r 1 -python3 ./test.py -f functions/function_last_row.py -r 1 -python3 ./test.py -f functions/function_leastsquares.py -r 1 -python3 ./test.py -f functions/function_max.py -r 1 -python3 ./test.py -f functions/function_min.py -r 1 -python3 ./test.py -f functions/function_operations.py -r 1 -python3 ./test.py -f functions/function_percentile.py -r 1 -python3 ./test.py -f functions/function_spread.py -r 1 -python3 ./test.py -f functions/function_stddev.py -r 1 -python3 ./test.py -f functions/function_sum.py -r 1 -python3 ./test.py -f functions/function_top.py -r 1 -python3 ./test.py -f functions/function_twa.py -r 1 -python3 ./test.py -f functions/function_twa_test2.py -python3 ./test.py -f functions/function_stddev_td2555.py -python3 ./test.py -f insert/metadataUpdate.py -python3 ./test.py -f tools/taosdemoTest2.py -python3 ./test.py -f query/last_cache.py -python3 ./test.py -f query/last_row_cache.py -python3 ./test.py -f account/account_create.py -python3 ./test.py -f alter/alter_table.py -python3 ./test.py -f query/queryGroupbySort.py - -python3 ./test.py -f insert/unsignedInt.py -python3 ./test.py -f insert/unsignedBigint.py -python3 ./test.py -f insert/unsignedSmallint.py -python3 ./test.py -f insert/unsignedTinyint.py -python3 ./test.py -f query/filterAllUnsignedIntTypes.py \ No newline at end of file diff --git a/tests/pytest/query/filter.py b/tests/pytest/query/filter.py index f107985f150a05cdd38a7fa1453d59d35c8a903a..6d2ffbc8b10992628fec381e60fd11a68c69fe27 100644 --- a/tests/pytest/query/filter.py +++ b/tests/pytest/query/filter.py @@ -28,18 +28,18 @@ class TDTestCase: print("==============step1") tdSql.execute( - "create table if not exists st (ts timestamp, tagtype int, name nchar(16)) tags(dev nchar(50))") + "create table if not exists st (ts timestamp, tagtype int, name nchar(16), col4 binary(16)) tags(dev nchar(50), tag2 binary(16))") tdSql.execute( - 'CREATE TABLE if not exists dev_001 using st tags("dev_01")') + 'CREATE TABLE if not exists dev_001 using st tags("dev_01", "tag_01")') tdSql.execute( - 'CREATE TABLE if not exists dev_002 using st tags("dev_02")') + 'CREATE TABLE if not exists dev_002 using st tags("dev_02", "tag_02")') print("==============step2") tdSql.execute( - """INSERT INTO dev_001(ts, tagtype, name) VALUES('2020-05-13 10:00:00.000', 1, 'first'),('2020-05-13 10:00:00.001', 2, 'second'), - ('2020-05-13 10:00:00.002', 3, 'third') dev_002 VALUES('2020-05-13 10:00:00.003', 1, 'first'), ('2020-05-13 10:00:00.004', 2, 'second'), - ('2020-05-13 10:00:00.005', 3, 'third')""") + """INSERT INTO dev_001 VALUES('2020-05-13 10:00:00.000', 1, 'first', 'binary1'),('2020-05-13 10:00:00.001', 2, 'second', 'binary2'), + ('2020-05-13 10:00:00.002', 3, 'third' , 'binary3') dev_002 VALUES('2020-05-13 10:00:00.003', 1, 'first', 'binary4'), ('2020-05-13 10:00:00.004', 2, 'second', 'binary5'), + ('2020-05-13 10:00:00.005', 3, 'third', 'binary6')""") # > for timestamp type tdSql.query("select * from db.st where ts > '2020-05-13 10:00:00.002'") @@ -85,6 +85,12 @@ class TDTestCase: tdSql.query("select * from db.st where name = 'first'") tdSql.checkRows(2) + tdSql.query("select * from db.st where col4 = 1231231") + tdSql.checkRows(0) + + tdSql.query("select * from db.st where name = 1231231") + tdSql.checkRows(0) + # <> for timestamp type tdSql.query("select * from db.st where ts <> '2020-05-13 10:00:00.002'") # tdSql.checkRows(4) @@ -105,6 +111,13 @@ class TDTestCase: tdSql.query("select * from db.st where name like '_econd'") tdSql.checkRows(2) + # for tag + tdSql.query("select * from db.st where dev=1") + tdSql.checkRows(0) + + tdSql.query("select * from db.st where tag2=1") + tdSql.checkRows(0) + def stop(self): tdSql.close() tdLog.success("%s successfully executed" % __file__) diff --git a/tests/pytest/tools/insert-interlace.json b/tests/pytest/tools/insert-interlace.json new file mode 100644 index 0000000000000000000000000000000000000000..a2ff2c001cf5aad24d2ecd7d9766f5ee62d6f3a5 --- /dev/null +++ b/tests/pytest/tools/insert-interlace.json @@ -0,0 +1,58 @@ +{ + "filetype": "insert", + "cfgdir": "/etc/taos", + "host": "127.0.0.1", + "port": 6030, + "user": "root", + "password": "taosdata", + "thread_count": 4, + "thread_count_create_tbl": 4, + "result_file": "./insert_res.txt", + "confirm_parameter_prompt": "no", + "insert_interval": 5000, + "rows_per_tbl": 50, + "num_of_records_per_req": 100, + "max_sql_len": 1024000, + "databases": [{ + "dbinfo": { + "name": "db", + "drop": "yes", + "replica": 1, + "days": 10, + "cache": 16, + "blocks": 8, + "precision": "ms", + "keep": 365, + "minRows": 100, + "maxRows": 4096, + "comp":2, + "walLevel":1, + "cachelast":0, + "quorum":1, + "fsync":3000, + "update": 0 + }, + "super_tables": [{ + "name": "stb", + "child_table_exists":"no", + "childtable_count": 9, + "childtable_prefix": "stb_", + "auto_create_table": "no", + "data_source": "rand", + "insert_mode": "taosc", + "insert_rows": 250, + "multi_thread_write_one_tbl": "no", + "rows_per_tbl": 80, + "max_sql_len": 1024000, + "disorder_ratio": 0, + "disorder_range": 1000, + "timestamp_step": 1, + "start_timestamp": "2020-10-01 00:00:00.000", + "sample_format": "csv", + "sample_file": "./sample.csv", + "tags_file": "", + "columns": [{"type": "INT"}], + "tags": [{"type": "INT", "count":1}] + }] + }] +} diff --git a/tests/pytest/tools/insert-tblimit-tboffset.json b/tests/pytest/tools/insert-tblimit-tboffset.json index 5f63660347141e419043da7632b507cf38be4315..f3d3e864baaa5429248f57a6dd1ea6b5bc314bfb 100644 --- a/tests/pytest/tools/insert-tblimit-tboffset.json +++ b/tests/pytest/tools/insert-tblimit-tboffset.json @@ -44,7 +44,6 @@ "childtable_offset": 33, "multi_thread_write_one_tbl": "no", "number_of_tbl_in_one_sql": 0, - "rows_per_tbl": 100, "max_sql_len": 1024000, "disorder_ratio": 0, "disorder_range": 1000, diff --git a/tests/pytest/tools/insert-tblimit-tboffset0.json b/tests/pytest/tools/insert-tblimit-tboffset0.json index 302744cab933297d8455aed9c45210385f1ec56f..7dcb2e052745e545d1693d97ce28350d00745e55 100644 --- a/tests/pytest/tools/insert-tblimit-tboffset0.json +++ b/tests/pytest/tools/insert-tblimit-tboffset0.json @@ -44,7 +44,6 @@ "childtable_offset": 0, "multi_thread_write_one_tbl": "no", "number_of_tbl_in_one_sql": 0, - "rows_per_tbl": 100, "max_sql_len": 1024000, "disorder_ratio": 0, "disorder_range": 1000, diff --git a/tests/pytest/tools/insert-tblimit1-tboffset.json b/tests/pytest/tools/insert-tblimit1-tboffset.json new file mode 100644 index 0000000000000000000000000000000000000000..a33dc22d5d5e2c88c9b6220fbe53390dfcd4f1ce --- /dev/null +++ b/tests/pytest/tools/insert-tblimit1-tboffset.json @@ -0,0 +1,59 @@ +{ + "filetype": "insert", + "cfgdir": "/etc/taos", + "host": "127.0.0.1", + "port": 6030, + "user": "root", + "password": "taosdata", + "thread_count": 4, + "thread_count_create_tbl": 4, + "result_file": "./insert_res.txt", + "confirm_parameter_prompt": "no", + "insert_interval": 0, + "num_of_records_per_req": 100, + "max_sql_len": 1024000, + "databases": [{ + "dbinfo": { + "name": "db", + "drop": "yes", + "replica": 1, + "days": 10, + "cache": 16, + "blocks": 8, + "precision": "ms", + "keep": 365, + "minRows": 100, + "maxRows": 4096, + "comp":2, + "walLevel":1, + "cachelast":0, + "quorum":1, + "fsync":3000, + "update": 0 + }, + "super_tables": [{ + "name": "stb", + "child_table_exists":"no", + "childtable_count": 100, + "childtable_prefix": "stb_", + "auto_create_table": "no", + "data_source": "rand", + "insert_mode": "taosc", + "insert_rows": 1000, + "childtable_limit": 1, + "childtable_offset": 50, + "multi_thread_write_one_tbl": "no", + "number_of_tbl_in_one_sql": 0, + "max_sql_len": 1024000, + "disorder_ratio": 0, + "disorder_range": 1000, + "timestamp_step": 1, + "start_timestamp": "2020-10-01 00:00:00.000", + "sample_format": "csv", + "sample_file": "./sample.csv", + "tags_file": "", + "columns": [{"type": "INT"}], + "tags": [{"type": "TINYINT", "count":1}] + }] + }] +} diff --git a/tests/pytest/tools/insert.json b/tests/pytest/tools/insert.json index c3fa78076b2a25f73ebc50f6a35bcc5afddb246d..996b91ed06f283fdcd968df9cafc4f58583cbb8d 100644 --- a/tests/pytest/tools/insert.json +++ b/tests/pytest/tools/insert.json @@ -1,50 +1,45 @@ { "filetype":"insert", "cfgdir": "/etc/taos", - "host": "127.0.0.1", - "port": 6030, - "user": "root", - "password": "taosdata", - "thread_count": 1, - "databases": [{ - "dbinfo": { - "name": "db01", - "replica": 1, - "days": 10, - "cache": 16, - "blocks": 8, - "precision": "ms", + "host": "127.0.0.1", + "port": 6030, + "user": "root", + "password": "taosdata", + "thread_count": 2, + "num_of_records_per_req": 10, + "thread_count_create_tbl": 4, + "databases": [{ + "dbinfo": { + "name": "db01", + "drop": "yes", + "replica": 1, + "days": 10, + "cache": 16, + "blocks": 8, + "precision": "ms", "update": 0, - "maxtablesPerVnode": 1000 - }, - "super_tables": [{ - "name": "stb01", - "childtable_count": 100, - "childtable_prefix": "stb01_", - "auto_create_table": "no", - "data_source": "rand", - "insert_mode": "taosc", - "insert_rate": 0, - "insert_rows": 1000, - "timestamp_step": 1000, - "start_timestamp": "2020-10-01 00:00:00.000", - "sample_format": "csv", - "sample_file": "/home/data/sample.csv", - "tags_file": "", - "columns": [{ - "type": "SMALLINT" - }, { - "type": "BOOL" - }, { - "type": "BINARY", - "len": 6 - }], - "tags": [{ - "type": "INT" - },{ - "type": "BINARY", - "len": 4 - }] - }] - }] + "maxtablesPerVnode": 1000 + }, + "super_tables": [{ + "name": "stb01", + "childtable_count": 3, + "childtable_prefix": "stb01_", + "auto_create_table": "no", + "data_source": "rand", + "insert_mode": "taosc", + "insert_rate": 0, + "insert_rows": 20, + "timestamp_step": 1000, + "start_timestamp": "2020-10-01 00:00:00.000", + "sample_format": "csv", + "sample_file": "/home/data/sample.csv", + "tags_file": "", + "columns": [{ + "type": "INT" + }], + "tags": [{ + "type": "INT" + }] + }] + }] } diff --git a/tests/pytest/tools/sampledata.csv b/tests/pytest/tools/sampledata.csv new file mode 100644 index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d --- /dev/null +++ b/tests/pytest/tools/sampledata.csv @@ -0,0 +1,3 @@ +1 +2 +3 diff --git a/tests/pytest/tools/taosdemo-sampledata.json b/tests/pytest/tools/taosdemo-sampledata.json new file mode 100644 index 0000000000000000000000000000000000000000..473c977773a097fd040b3821c3df806da8ef9c02 --- /dev/null +++ b/tests/pytest/tools/taosdemo-sampledata.json @@ -0,0 +1,39 @@ +{ + "filetype": "insert", + "cfgdir": "/etc/taos", + "host": "127.0.0.1", + "port": 6030, + "user": "root", + "password": "taosdata", + "thread_count": 10, + "confirm_parameter_prompt": "no", + "databases": [{ + "dbinfo": { + "name": "db", + "drop": "yes" + }, + "super_tables": [{ + "name": "stb", + "child_table_exists":"no", + "childtable_count": 20, + "childtable_limit": 10, + "childtable_offset": 0, + "childtable_prefix": "t_", + "auto_create_table": "no", + "data_source": "sample", + "insert_mode": "taosc", + "insert_rate": 0, + "insert_rows": 20, + "multi_thread_write_one_tbl": "no", + "number_of_tbl_in_one_sql": 0, + "max_sql_len": 1048000, + "timestamp_step": 1000, + "start_timestamp": "2020-1-1 00:00:00", + "sample_format": "csv", + "sample_file": "./tools/sampledata.csv", + "columns": [{"type": "INT"}], + "tags": [{"type": "INT", "count":1}] + }] + }] + +} diff --git a/tests/pytest/tools/taosdemoTest.py b/tests/pytest/tools/taosdemoTest.py index c450570d2446a236ad8fa98c0817d2e31bcae795..ff5921be604f9fe911f1aa8b84efe230baf20e07 100644 --- a/tests/pytest/tools/taosdemoTest.py +++ b/tests/pytest/tools/taosdemoTest.py @@ -51,7 +51,7 @@ class TDTestCase: else: tdLog.info("taosd found in %s" % buildPath) binPath = buildPath + "/build/bin/" - os.system("%staosdemo -y -M -t %d -n %d -x" % + os.system("%staosdemo -y -t %d -n %d" % (binPath, self.numberOfTables, self.numberOfRecords)) tdSql.execute("use test") @@ -63,7 +63,7 @@ class TDTestCase: tdSql.checkRows(2) tdSql.query( - "select apercentile(col1, 1) from test.meters interval(10s)") + "select apercentile(col1, 1) from test.meters interval(100s)") tdSql.checkRows(1) tdSql.error("select loc, count(loc) from test.meters") diff --git a/tests/pytest/tools/taosdemoTest2.py b/tests/pytest/tools/taosdemoTest2.py index 75a79d0585e766718151ca9b0e3e195c03732e16..92f6fa12657c4976bd975ffaa5358a510ae8f3f0 100644 --- a/tests/pytest/tools/taosdemoTest2.py +++ b/tests/pytest/tools/taosdemoTest2.py @@ -31,7 +31,7 @@ class TDTestCase: def insertDataAndAlterTable(self, threadID): if(threadID == 0): - os.system("taosdemo -M -y -t %d -n %d -x" % + os.system("taosdemo -y -t %d -n %d" % (self.numberOfTables, self.numberOfRecords)) if(threadID == 1): time.sleep(2) diff --git a/tests/pytest/tools/taosdemoTestInterlace.py b/tests/pytest/tools/taosdemoTestInterlace.py new file mode 100644 index 0000000000000000000000000000000000000000..953bfff90fd7f114ad1c78425a80e1eaae75c43e --- /dev/null +++ b/tests/pytest/tools/taosdemoTestInterlace.py @@ -0,0 +1,78 @@ +################################################################### +# Copyright (c) 2016 by TAOS Technologies, Inc. +# All rights reserved. +# +# This file is proprietary and confidential to TAOS Technologies. +# No part of this file may be reproduced, stored, transmitted, +# disclosed or used in any form or by any means other than as +# expressly provided by the written permission from Jianhui Tao +# +################################################################### + +# -*- coding: utf-8 -*- + +import sys +import os +from util.log import * +from util.cases import * +from util.sql import * +from util.dnodes import * +import subprocess + + +class TDTestCase: + def init(self, conn, logSql): + tdLog.debug("start to execute %s" % __file__) + tdSql.init(conn.cursor(), logSql) + + self.numberOfTables = 10000 + self.numberOfRecords = 100 + + def getBuildPath(self): + selfPath = os.path.dirname(os.path.realpath(__file__)) + + if ("community" in selfPath): + projPath = selfPath[:selfPath.find("community")] + else: + projPath = selfPath[:selfPath.find("tests")] + + for root, dirs, files in os.walk(projPath): + if ("taosd" in files): + rootRealPath = os.path.dirname(os.path.realpath(root)) + if ("packaging" not in rootRealPath): + buildPath = root[:len(root) - len("/build/bin")] + break + return buildPath + + def run(self): + tdSql.prepare() + buildPath = self.getBuildPath() + if (buildPath == ""): + tdLog.exit("taosd not found!") + else: + tdLog.info("taosd found in %s" % buildPath) + binPath = buildPath + "/build/bin/" + taosdemoCmd = "%staosdemo -f tools/insert-interlace.json -pp 2>&1 | grep sleep | wc -l" % binPath + sleepTimes = subprocess.check_output( + taosdemoCmd, shell=True).decode("utf-8") + print("sleep times: %d" % int(sleepTimes)) + + if (int(sleepTimes) != 16): + caller = inspect.getframeinfo(inspect.stack()[0][0]) + tdLog.exit( + "%s(%d) failed: expected sleep times 16, actual %d" % + (caller.filename, caller.lineno, int(sleepTimes))) + + tdSql.execute("use db") + tdSql.query("select count(tbname) from db.stb") + tdSql.checkData(0, 0, 9) + tdSql.query("select count(*) from db.stb") + tdSql.checkData(0, 0, 2250) + + def stop(self): + tdSql.close() + tdLog.success("%s successfully executed" % __file__) + + +tdCases.addWindows(__file__, TDTestCase()) +tdCases.addLinux(__file__, TDTestCase()) diff --git a/tests/pytest/tools/taosdemoTestLimitOffset.py b/tests/pytest/tools/taosdemoTestLimitOffset.py index 6dbe5a70282975670b6b1813b429e5a07336c6db..bce41e1c75817c2939d4c1104419771483a9a689 100644 --- a/tests/pytest/tools/taosdemoTestLimitOffset.py +++ b/tests/pytest/tools/taosdemoTestLimitOffset.py @@ -59,6 +59,24 @@ class TDTestCase: tdSql.query("select count(*) from db.stb") tdSql.checkData(0, 0, 33000) + os.system("%staosdemo -f tools/insert-tblimit-tboffset0.json" % binPath) + + tdSql.execute("reset query cache") + tdSql.execute("use db") + tdSql.query("select count(tbname) from db.stb") + tdSql.checkData(0, 0, 100) + tdSql.query("select count(*) from db.stb") + tdSql.checkData(0, 0, 20000) + + os.system("%staosdemo -f tools/insert-tblimit1-tboffset.json" % binPath) + + tdSql.execute("reset query cache") + tdSql.execute("use db") + tdSql.query("select count(tbname) from db.stb") + tdSql.checkData(0, 0, 100) + tdSql.query("select count(*) from db.stb") + tdSql.checkData(0, 0, 1000) + def stop(self): tdSql.close() tdLog.success("%s successfully executed" % __file__) diff --git a/tests/pytest/tools/taosdemoTestSampleData.py b/tests/pytest/tools/taosdemoTestSampleData.py new file mode 100644 index 0000000000000000000000000000000000000000..893c53984d87aa729111a840831c6f5343087085 --- /dev/null +++ b/tests/pytest/tools/taosdemoTestSampleData.py @@ -0,0 +1,68 @@ +################################################################### +# Copyright (c) 2016 by TAOS Technologies, Inc. +# All rights reserved. +# +# This file is proprietary and confidential to TAOS Technologies. +# No part of this file may be reproduced, stored, transmitted, +# disclosed or used in any form or by any means other than as +# expressly provided by the written permission from Jianhui Tao +# +################################################################### + +# -*- coding: utf-8 -*- + +import sys +import os +from util.log import * +from util.cases import * +from util.sql import * +from util.dnodes import * + + +class TDTestCase: + def init(self, conn, logSql): + tdLog.debug("start to execute %s" % __file__) + tdSql.init(conn.cursor(), logSql) + + self.numberOfTables = 10000 + self.numberOfRecords = 100 + + def getBuildPath(self): + selfPath = os.path.dirname(os.path.realpath(__file__)) + + if ("community" in selfPath): + projPath = selfPath[:selfPath.find("community")] + else: + projPath = selfPath[:selfPath.find("tests")] + + for root, dirs, files in os.walk(projPath): + if ("taosd" in files): + rootRealPath = os.path.dirname(os.path.realpath(root)) + if ("packaging" not in rootRealPath): + buildPath = root[:len(root)-len("/build/bin")] + break + return buildPath + + def run(self): + tdSql.prepare() + buildPath = self.getBuildPath() + if (buildPath == ""): + tdLog.exit("taosd not found!") + else: + tdLog.info("taosd found in %s" % buildPath) + binPath = buildPath+ "/build/bin/" + os.system("%staosdemo -f tools/taosdemo-sampledata.json" % binPath) + + tdSql.execute("use db") + tdSql.query("select count(tbname) from db.stb") + tdSql.checkData(0, 0, 20) + tdSql.query("select count(*) from db.stb") + tdSql.checkData(0, 0, 200) + + def stop(self): + tdSql.close() + tdLog.success("%s successfully executed" % __file__) + + +tdCases.addWindows(__file__, TDTestCase()) +tdCases.addLinux(__file__, TDTestCase()) diff --git a/tests/pytest/tools/lowaTest.py b/tests/pytest/tools/taosdemoTestWithJson.py similarity index 94% rename from tests/pytest/tools/lowaTest.py rename to tests/pytest/tools/taosdemoTestWithJson.py index ad8b5925bd99b9c5918421eb277cea6e5ed100a7..f57af9ce5c7ce890a0a93c6081702d93954af138 100644 --- a/tests/pytest/tools/lowaTest.py +++ b/tests/pytest/tools/taosdemoTestWithJson.py @@ -24,9 +24,6 @@ class TDTestCase: tdLog.debug("start to execute %s" % __file__) tdSql.init(conn.cursor(), logSql) - self.numberOfTables = 10000 - self.numberOfRecords = 100 - def getBuildPath(self): selfPath = os.path.dirname(os.path.realpath(__file__)) @@ -55,7 +52,7 @@ class TDTestCase: tdSql.execute("use db01") tdSql.query("select count(*) from stb01") - tdSql.checkData(0, 0, 100000) + tdSql.checkData(0, 0, 60) def stop(self): tdSql.close() diff --git a/tests/pytest/tools/taosdemoTestWithoutMetric.py b/tests/pytest/tools/taosdemoTestWithoutMetric.py index 647d6a37cb74e613950ccf72e9f2d99d85435c1a..9687600563d8fed68c6f9c67643759a3dcfa9703 100644 --- a/tests/pytest/tools/taosdemoTestWithoutMetric.py +++ b/tests/pytest/tools/taosdemoTestWithoutMetric.py @@ -50,7 +50,7 @@ class TDTestCase: else: tdLog.info("taosd found in %s" % buildPath) binPath = buildPath + "/build/bin/" - os.system("%staosdemo -y -t %d -n %d -x" % + os.system("%staosdemo -N -y -t %d -n %d" % (binPath, self.numberOfTables, self.numberOfRecords)) tdSql.query("show databases") diff --git a/tests/script/general/db/topic1.sim b/tests/script/general/db/topic1.sim index cdfb8ea2ec33a5322ab34de6d2688ef0dc1fc64f..e17a4996f569450116a8050cc4e32905463cea4d 100644 --- a/tests/script/general/db/topic1.sim +++ b/tests/script/general/db/topic1.sim @@ -167,7 +167,7 @@ sql_error show t1.stables; sql_error show t1.tables; sql_error create topic t1 partitions -1; -sql_error create topic t1 partitions 0; +#sql_error create topic t1 partitions 0; sql_error create topic t1 partitions 10001; print =============step3 create with db para @@ -718,7 +718,7 @@ sql_error alter database t1 partitions 1000 sql_error alter database t1 partitions 10000 sql_error alter topic t1 partitions -1 -sql_error alter topic t1 partitions 0 +#sql_error alter topic t1 partitions 0 sql_error alter database t1 partitions 10000 sql alter topic t1 partitions 1 @@ -842,4 +842,41 @@ if $rows != 10 then return -1 endi +sql drop topic t1 + +print ============== create same name topic +sql create database d2 +sql create topic t2 + +sql_error create topic d2 +sql_error create topic if not exists d2 +sql_error create topic t2 +sql create topic if not exists t2 +sql_error create topic t2 partitions 5; +sql_error create topic t2 partitions 6; +sql_error create topic t2 partitions 3; + +sql_error alter topic t3 partitions 1 +sql_error alter topic d2 partitions 1 +#sql_error alter topic t2 partitions 0 +sql_error alter topic t2 partitions 10000 + +sql_error drop topic d2 +sql_error drop topic d3 +sql drop database d2 +sql drop database t2 + +print ============== create partitons 0 +sql create topic t2 partitions 0 +sql show t2.stables; +if $rows != 0 then + return -1 +endi +sql show t2.tables; +if $rows != 0 then + return -1 +endi + +sql drop topic t2 + system sh/exec.sh -n dnode1 -s stop -x SIGINT diff --git a/tests/script/issue/TD-3300.sim b/tests/script/issue/TD-3300.sim new file mode 100644 index 0000000000000000000000000000000000000000..0745ceb8490567b508397b3e99d4748dc41c8971 --- /dev/null +++ b/tests/script/issue/TD-3300.sim @@ -0,0 +1,556 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/deploy.sh -n dnode2 -i 2 +system sh/deploy.sh -n dnode3 -i 3 +system sh/deploy.sh -n dnode4 -i 4 + +system sh/cfg.sh -n dnode1 -c numOfMnodes -v 1 +system sh/cfg.sh -n dnode2 -c numOfMnodes -v 1 +system sh/cfg.sh -n dnode3 -c numOfMnodes -v 1 +system sh/cfg.sh -n dnode4 -c numOfMnodes -v 1 + +system sh/cfg.sh -n dnode1 -c role -v 1 +system sh/cfg.sh -n dnode2 -c role -v 2 +system sh/cfg.sh -n dnode3 -c role -v 2 +system sh/cfg.sh -n dnode4 -c role -v 2 + +system sh/cfg.sh -n dnode1 -c arbitrator -v $arbitrator +system sh/cfg.sh -n dnode2 -c arbitrator -v $arbitrator +system sh/cfg.sh -n dnode3 -c arbitrator -v $arbitrator +system sh/cfg.sh -n dnode4 -c arbitrator -v $arbitrator + +print ============== step0: start tarbitrator +system sh/exec_tarbitrator.sh -s start + +print ============== step1: start dnode1, only deploy mnode +system sh/exec.sh -n dnode1 -s start +sql connect + +print ============== step2: start dnode2/dnode3 +system sh/exec.sh -n dnode2 -s start +system sh/exec.sh -n dnode3 -s start +sql create dnode $hostname2 +sql create dnode $hostname3 + +$x = 0 +step2: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi + +sql show dnodes +print dnode1 $data4_1 +print dnode2 $data4_2 +print dnode3 $data4_3 + +if $data4_1 != ready then + goto step2 +endi +if $data4_2 != ready then + goto step2 +endi +if $data4_3 != ready then + goto step2 +endi + +sleep 1000 + +print ============== step3 +sql create database db replica 2 +sql use db + +sql create table stb (ts timestamp, c1 int, c2 int) tags(t1 int) +sql create table t1 using stb tags(1) +sql insert into t1 values(1577980800000, 1, 5) +sql insert into t1 values(1577980800001, 2, 4) +sql insert into t1 values(1577980800002, 3, 3) +sql insert into t1 values(1577980800003, 4, 2) +sql insert into t1 values(1577980800004, 5, 1) + +sql show db.vgroups +if $data04 != 3 then + return -1 +endi +if $data06 != 2 then + return -1 +endi +if $data05 != master then + return -1 +endi +if $data07 != slave then + return -1 +endi + +sql select * from t1 +if $rows != 5 then + return -1 +endi + +system sh/exec.sh -n dnode2 -s stop -x SIGKILL +system sh/exec.sh -n dnode3 -s stop -x SIGKILL + +print ============== step4 +system sh/exec.sh -n dnode2 -s start +system sh/exec.sh -n dnode3 -s start + +$x = 0 +step4: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi + +sql show dnodes +print dnode1 $data4_1 +print dnode2 $data4_2 +print dnode3 $data4_3 + +if $data4_1 != ready then + goto step4 +endi +if $data4_2 != ready then + goto step4 +endi +if $data4_3 != ready then + goto step4 +endi + +sql show db.vgroups +if $data04 != 3 then + goto step4 +endi +if $data06 != 2 then + goto step4 +endi +if $data05 != master then + goto step4 +endi +if $data07 != slave then + goto step4 +endi + +sql create table t2 using stb tags(1) +sql insert into t2 values(1577980800000, 1, 5) +sql insert into t2 values(1577980800001, 2, 4) +sql insert into t2 values(1577980800002, 3, 3) +sql insert into t2 values(1577980800003, 4, 2) +sql insert into t2 values(1577980800004, 5, 1) + +sql select * from t2 +if $rows != 5 then + return -1 +endi + +print ============== step5 +system sh/exec.sh -n dnode3 -s stop -x SIGKILL + +$x = 0 +step5: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi + +sql show dnodes +print dnode1 $data4_1 +print dnode2 $data4_2 +print dnode3 $data4_3 + +if $data4_1 != ready then + goto step5 +endi +if $data4_2 != ready then + goto step5 +endi +if $data4_3 != offline then + goto step5 +endi + +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi + +sql show db.vgroups +if $data04 != 3 then + goto step5 +endi +if $data06 != 2 then + goto step5 +endi +if $data05 != offline then + goto step5 +endi +if $data07 != master then + goto step5 +endi + +print ============== step6 +sql create table t3 using stb tags(1) +sql insert into t3 values(1577980800000, 1, 5) +sql insert into t3 values(1577980800001, 2, 4) +sql insert into t3 values(1577980800002, 3, 3) +sql insert into t3 values(1577980800003, 4, 2) +sql insert into t3 values(1577980800004, 5, 1) +sql insert into t3 values(1577980800010, 11, 5) +sql insert into t3 values(1577980800011, 12, 4) +sql insert into t3 values(1577980800012, 13, 3) +sql insert into t3 values(1577980800013, 14, 2) +sql insert into t3 values(1577980800014, 15, 1) + +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi +sql select * from t3 +if $rows != 10 then + return -1 +endi + +system sh/exec.sh -n dnode3 -s start + +$x = 0 +step6: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi + +sql show dnodes +print dnode1 $data4_1 +print dnode2 $data4_2 +print dnode3 $data4_3 + +if $data4_1 != ready then + goto step6 +endi +if $data4_2 != ready then + goto step6 +endi +if $data4_3 != ready then + goto step6 +endi + +sql show db.vgroups +if $data04 != 3 then + goto step6 +endi +if $data06 != 2 then + goto step6 +endi +if $data05 != slave then + goto step6 +endi +if $data07 != master then + goto step6 +endi + +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi +sql select * from t3 +if $rows != 10 then + return -1 +endi + +print ============== step7 +sql create table t4 using stb tags(1) +sql insert into t4 values(1577980800000, 1, 5) +sql insert into t4 values(1577980800001, 2, 4) +sql insert into t4 values(1577980800002, 3, 3) +sql insert into t4 values(1577980800003, 4, 2) +sql insert into t4 values(1577980800004, 5, 1) +sql insert into t4 values(1577980800010, 11, 5) +sql insert into t4 values(1577980800011, 12, 4) +sql insert into t4 values(1577980800012, 13, 3) +sql insert into t4 values(1577980800013, 14, 2) +sql insert into t4 values(1577980800014, 15, 1) +sql insert into t4 values(1577980800020, 21, 5) +sql insert into t4 values(1577980800021, 22, 4) +sql insert into t4 values(1577980800022, 23, 3) +sql insert into t4 values(1577980800023, 24, 2) +sql insert into t4 values(1577980800024, 25, 1) + +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi +sql select * from t3 +if $rows != 10 then + return -1 +endi +sql select * from t4 +if $rows != 15 then + return -1 +endi + +system sh/exec.sh -n dnode2 -s stop -x SIGKILL +$x = 0 +step7: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi + +sql show dnodes +print dnode1 $data4_1 +print dnode2 $data4_2 +print dnode3 $data4_3 + +if $data4_1 != ready then + goto step7 +endi +if $data4_2 != offline then + goto step7 +endi +if $data4_3 != ready then + goto step7 +endi + +sql show db.vgroups +if $data04 != 3 then + goto step7 +endi +if $data06 != 2 then + goto step7 +endi +if $data05 != master then + goto step7 +endi +if $data07 != offline then + goto step7 +endi + +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi +sql select * from t3 +if $rows != 10 then + return -1 +endi +sql select * from t4 +if $rows != 15 then + return -1 +endi + +print ============== step8 +sql create table t5 using stb tags(1) +sql insert into t5 values(1577980800000, 1, 5) +sql insert into t5 values(1577980800001, 2, 4) +sql insert into t5 values(1577980800002, 3, 3) +sql insert into t5 values(1577980800003, 4, 2) +sql insert into t5 values(1577980800004, 5, 1) +sql insert into t5 values(1577980800010, 11, 5) + +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi +sql select * from t3 +if $rows != 10 then + return -1 +endi +sql select * from t4 +if $rows != 15 then + return -1 +endi +sql select * from t5 +if $rows != 6 then + return -1 +endi + +system sh/exec.sh -n dnode2 -s start +$x = 0 +step8: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi + +sql show dnodes +print dnode1 $data4_1 +print dnode2 $data4_2 +print dnode3 $data4_3 + +if $data4_1 != ready then + goto step8 +endi +if $data4_2 != ready then + goto step8 +endi +if $data4_3 != ready then + goto step8 +endi + +sql show db.vgroups +if $data04 != 3 then + goto step8 +endi +if $data06 != 2 then + goto step8 +endi +if $data05 != master then + goto step8 +endi +if $data07 != slave then + goto step8 +endi + +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi +sql select * from t3 +if $rows != 10 then + return -1 +endi +sql select * from t4 +if $rows != 15 then + return -1 +endi +sql select * from t5 +if $rows != 6 then + return -1 +endi + +print ============== step9 +sql create table t6 using stb tags(1) +sql insert into t6 values(1577980800000, 1, 5) +sql insert into t6 values(1577980800001, 2, 4) +sql insert into t6 values(1577980800002, 3, 3) +sql insert into t6 values(1577980800003, 4, 2) +sql insert into t6 values(1577980800004, 5, 1) +sql insert into t6 values(1577980800010, 11, 5) +sql insert into t6 values(1577980800011, 12, 4) + +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi +sql select * from t3 +if $rows != 10 then + return -1 +endi +sql select * from t4 +if $rows != 15 then + return -1 +endi +sql select * from t5 +if $rows != 6 then + return -1 +endi +sql select * from t6 +if $rows != 7 then + return -1 +endi + +system sh/exec.sh -n dnode3 -s stop -x SIGKILL +$x = 0 +step9: + $x = $x + 1 + sleep 1000 + if $x == 10 then + return -1 + endi + +sql show dnodes +print dnode1 $data4_1 +print dnode2 $data4_2 +print dnode3 $data4_3 + +if $data4_1 != ready then + goto step9 +endi +if $data4_2 != ready then + goto step9 +endi +if $data4_3 != offline then + goto step9 +endi + +print ============== 2 +sql show db.vgroups + +if $data04 != 3 then + goto step7 +endi +if $data06 != 2 then + goto step7 +endi +if $data05 != offline then + goto step7 +endi +if $data07 != master then + goto step7 +endi + +print ============== 3 +sql select * from t1 +if $rows != 5 then + return -1 +endi +sql select * from t2 +if $rows != 5 then + return -1 +endi +sql select * from t3 +if $rows != 10 then + return -1 +endi +sql select * from t4 +if $rows != 15 then + return -1 +endi +sql select * from t5 +if $rows != 6 then + return -1 +endi +sql select * from t6 +if $rows != 7 then + return -1 +endi + +system sh/exec.sh -n dnode1 -s stop +system sh/exec.sh -n dnode2 -s stop +system sh/exec.sh -n dnode3 -s stop diff --git a/tests/script/jenkins/basic.txt b/tests/script/jenkins/basic.txt index f6e2267f6d923bdb99ee337a887adf7e91d242ea..ce770aee56c9ddb7b3a6b866e4317d09c463c8d6 100644 --- a/tests/script/jenkins/basic.txt +++ b/tests/script/jenkins/basic.txt @@ -1,76 +1,7 @@ cd ../../../debug; cmake .. cd ../../../debug; make -./test.sh -f issue/TD-2677.sim -./test.sh -f issue/TD-2680.sim -./test.sh -f issue/TD-2713.sim - -./test.sh -f general/alter/cached_schema_after_alter.sim -./test.sh -f general/alter/count.sim -./test.sh -f general/alter/dnode.sim -./test.sh -f general/alter/import.sim -./test.sh -f general/alter/insert1.sim -./test.sh -f general/alter/insert2.sim -./test.sh -f general/alter/metrics.sim -./test.sh -f general/alter/table.sim - -./test.sh -f general/cache/new_metrics.sim -./test.sh -f general/cache/restart_metrics.sim -./test.sh -f general/cache/restart_table.sim - -./test.sh -f general/connection/connection.sim - -./test.sh -f general/column/commit.sim -./test.sh -f general/column/metrics.sim -./test.sh -f general/column/table.sim - -./test.sh -f general/compress/commitlog.sim -./test.sh -f general/compress/compress.sim -./test.sh -f general/compress/compress2.sim -./test.sh -f general/compress/uncompress.sim - -./test.sh -f general/compute/avg.sim -./test.sh -f general/compute/bottom.sim -./test.sh -f general/compute/count.sim -./test.sh -f general/compute/diff.sim -./test.sh -f general/compute/diff2.sim -./test.sh -f general/compute/first.sim -./test.sh -f general/compute/interval.sim -./test.sh -f general/compute/last.sim -./test.sh -f general/compute/leastsquare.sim -./test.sh -f general/compute/max.sim -./test.sh -f general/compute/min.sim -./test.sh -f general/compute/null.sim -./test.sh -f general/compute/percentile.sim -./test.sh -f general/compute/stddev.sim -./test.sh -f general/compute/sum.sim -./test.sh -f general/compute/top.sim - -./test.sh -f general/db/alter_option.sim -./test.sh -f general/db/alter_tables_d2.sim -./test.sh -f general/db/alter_tables_v1.sim -./test.sh -f general/db/alter_tables_v4.sim -./test.sh -f general/db/alter_vgroups.sim -./test.sh -f general/db/basic.sim -./test.sh -f general/db/basic1.sim -./test.sh -f general/db/basic2.sim -./test.sh -f general/db/basic3.sim -./test.sh -f general/db/basic4.sim -./test.sh -f general/db/basic5.sim -./test.sh -f general/db/delete_reuse1.sim -./test.sh -f general/db/delete_reuse2.sim -./test.sh -f general/db/delete_reusevnode.sim -./test.sh -f general/db/delete_reusevnode2.sim -./test.sh -f general/db/delete_writing1.sim -./test.sh -f general/db/delete_writing2.sim -./test.sh -f general/db/delete.sim -./test.sh -f general/db/len.sim -./test.sh -f general/db/repeat.sim -./test.sh -f general/db/tables.sim -./test.sh -f general/db/vnodes.sim -./test.sh -f general/db/topic1.sim -./test.sh -f general/db/topic2.sim -./test.sh -f general/db/nosuchfile.sim +#======================b1-start=============== ./test.sh -f general/field/2.sim ./test.sh -f general/field/3.sim @@ -96,11 +27,6 @@ cd ../../../debug; make ./test.sh -f general/http/grafana_bug.sim ./test.sh -f general/http/grafana.sim -./test.sh -f general/import/basic.sim -./test.sh -f general/import/commit.sim -./test.sh -f general/import/large.sim -./test.sh -f general/import/replica1.sim - ./test.sh -f general/insert/basic.sim ./test.sh -f general/insert/insert_drop.sim ./test.sh -f general/insert/query_block1_memory.sim @@ -160,49 +86,12 @@ cd ../../../debug; make ./test.sh -f general/parser/repeatAlter.sim ./test.sh -f general/parser/union.sim ./test.sh -f general/parser/topbot.sim +./test.sh -f general/db/nosuchfile.sim ./test.sh -f general/parser/function.sim -./test.sh -f general/parser/select_distinct_tag.sim -./test.sh -f general/parser/join_manyblocks.sim -./test.sh -f general/parser/stableOp.sim -./test.sh -f general/parser/timestamp.sim -./test.sh -f general/parser/sliding.sim +./test.sh -f unique/cluster/vgroup100.sim -./test.sh -f general/stable/disk.sim -./test.sh -f general/stable/dnode3.sim -./test.sh -f general/stable/metrics.sim -./test.sh -f general/stable/refcount.sim -./test.sh -f general/stable/show.sim -./test.sh -f general/stable/values.sim -./test.sh -f general/stable/vnode3.sim - -./test.sh -f general/table/autocreate.sim -./test.sh -f general/table/basic1.sim -./test.sh -f general/table/basic2.sim -./test.sh -f general/table/basic3.sim -./test.sh -f general/table/bigint.sim -./test.sh -f general/table/binary.sim -./test.sh -f general/table/bool.sim -./test.sh -f general/table/column_name.sim -./test.sh -f general/table/column_num.sim -./test.sh -f general/table/column_value.sim -./test.sh -f general/table/column2.sim -./test.sh -f general/table/date.sim -./test.sh -f general/table/db.table.sim -./test.sh -f general/table/delete_reuse1.sim -./test.sh -f general/table/delete_reuse2.sim -./test.sh -f general/table/delete_writing.sim -./test.sh -f general/table/describe.sim -./test.sh -f general/table/double.sim -./test.sh -f general/table/fill.sim -./test.sh -f general/table/float.sim -./test.sh -f general/table/int.sim -./test.sh -f general/table/limit.sim -./test.sh -f general/table/smallint.sim -./test.sh -f general/table/table_len.sim -./test.sh -f general/table/table.sim -./test.sh -f general/table/tinyint.sim -./test.sh -f general/table/vgroup.sim -./test.sh -f general/table/createmulti.sim +#======================b1-end=============== +#======================b2-start=============== ./test.sh -f general/tag/3.sim ./test.sh -f general/tag/4.sim @@ -229,6 +118,9 @@ cd ../../../debug; make ./test.sh -f general/tag/set.sim ./test.sh -f general/tag/smallint.sim ./test.sh -f general/tag/tinyint.sim +./test.sh -f general/wal/sync.sim +./test.sh -f general/wal/kill.sim +./test.sh -f general/wal/maxtables.sim ./test.sh -f general/user/authority.sim ./test.sh -f general/user/monitor.sim @@ -249,10 +141,6 @@ cd ../../../debug; make ./test.sh -f general/vector/table_query.sim ./test.sh -f general/vector/table_time.sim -./test.sh -f general/wal/sync.sim -./test.sh -f general/wal/kill.sim -./test.sh -f general/wal/maxtables.sim - ./test.sh -f unique/account/account_create.sim ./test.sh -f unique/account/account_delete.sim ./test.sh -f unique/account/account_len.sim @@ -265,91 +153,15 @@ cd ../../../debug; make ./test.sh -f unique/account/user_create.sim ./test.sh -f unique/account/user_len.sim -./test.sh -f unique/big/balance.sim ./test.sh -f unique/big/maxvnodes.sim ./test.sh -f unique/big/tcp.sim ./test.sh -f unique/cluster/alter.sim -./test.sh -f unique/cluster/balance1.sim -./test.sh -f unique/cluster/balance2.sim -./test.sh -f unique/cluster/balance3.sim ./test.sh -f unique/cluster/cache.sim -./test.sh -f unique/cluster/vgroup100.sim - -./test.sh -f unique/column/replica3.sim - -./test.sh -f unique/db/commit.sim -./test.sh -f unique/db/delete.sim -./test.sh -f unique/db/delete_part.sim -./test.sh -f unique/db/replica_add12.sim -./test.sh -f unique/db/replica_add13.sim -./test.sh -f unique/db/replica_add23.sim -./test.sh -f unique/db/replica_reduce21.sim -./test.sh -f unique/db/replica_reduce32.sim -./test.sh -f unique/db/replica_reduce31.sim -./test.sh -f unique/db/replica_part.sim - -./test.sh -f unique/dnode/alternativeRole.sim -./test.sh -f unique/dnode/monitor.sim -./test.sh -f unique/dnode/monitor_bug.sim -./test.sh -f unique/dnode/simple.sim -./test.sh -f unique/dnode/balance1.sim -./test.sh -f unique/dnode/balance2.sim -./test.sh -f unique/dnode/balance3.sim -./test.sh -f unique/dnode/balancex.sim -./test.sh -f unique/dnode/data1.sim -./test.sh -f unique/dnode/m2.sim -./test.sh -f unique/dnode/m3.sim -./test.sh -f unique/dnode/lossdata.sim -./test.sh -f unique/dnode/offline1.sim -./test.sh -f unique/dnode/offline2.sim -./test.sh -f unique/dnode/offline3.sim -./test.sh -f unique/dnode/reason.sim -./test.sh -f unique/dnode/remove1.sim -./test.sh -f unique/dnode/remove2.sim -./test.sh -f unique/dnode/vnode_clean.sim - -./test.sh -f unique/http/admin.sim -./test.sh -f unique/http/opentsdb.sim -./test.sh -f unique/import/replica2.sim -./test.sh -f unique/import/replica3.sim -./test.sh -f unique/stable/balance_replica1.sim -./test.sh -f unique/stable/dnode2_stop.sim -./test.sh -f unique/stable/dnode2.sim -./test.sh -f unique/stable/dnode3.sim -./test.sh -f unique/stable/replica2_dnode4.sim -./test.sh -f unique/stable/replica2_vnode3.sim -./test.sh -f unique/stable/replica3_dnode6.sim -./test.sh -f unique/stable/replica3_vnode3.sim - -./test.sh -f unique/mnode/mgmt20.sim -./test.sh -f unique/mnode/mgmt21.sim -./test.sh -f unique/mnode/mgmt22.sim -./test.sh -f unique/mnode/mgmt23.sim -./test.sh -f unique/mnode/mgmt24.sim -./test.sh -f unique/mnode/mgmt25.sim -./test.sh -f unique/mnode/mgmt26.sim -./test.sh -f unique/mnode/mgmt30.sim -./test.sh -f unique/mnode/mgmt33.sim -./test.sh -f unique/mnode/mgmt34.sim -./test.sh -f unique/mnode/mgmtr2.sim - -./test.sh -f unique/vnode/many.sim -./test.sh -f unique/vnode/replica2_basic2.sim -./test.sh -f unique/vnode/replica2_repeat.sim -./test.sh -f unique/vnode/replica3_basic.sim -./test.sh -f unique/vnode/replica3_repeat.sim -./test.sh -f unique/vnode/replica3_vgroup.sim - -./test.sh -f general/stream/metrics_del.sim -./test.sh -f general/stream/metrics_replica1_vnoden.sim -./test.sh -f general/stream/restart_stream.sim -./test.sh -f general/stream/stream_3.sim -./test.sh -f general/stream/stream_restart.sim -./test.sh -f general/stream/table_del.sim -./test.sh -f general/stream/table_replica1_vnoden.sim +#======================b2-end=============== +#======================b3-start=============== ./test.sh -f unique/arbitrator/check_cluster_cfg_para.sim #./test.sh -f unique/arbitrator/dn2_mn1_cache_file_sync.sim @@ -371,7 +183,7 @@ cd ../../../debug; make ./test.sh -f unique/arbitrator/dn3_mn1_r3_vnode_delDir.sim ./test.sh -f unique/arbitrator/dn3_mn1_vnode_nomaster.sim ./test.sh -f unique/arbitrator/dn3_mn2_killDnode.sim -./test.sh -f unique/arbitrator/insert_duplicationTs.sim + ./test.sh -f unique/arbitrator/offline_replica2_alterTable_online.sim ./test.sh -f unique/arbitrator/offline_replica2_alterTag_online.sim ./test.sh -f unique/arbitrator/offline_replica2_createTable_online.sim @@ -398,7 +210,220 @@ cd ../../../debug; make ./test.sh -f unique/migrate/mn2_vn2_repl2_rmMnodeVnodeDir_stopAll_starAll.sim ./test.sh -f unique/migrate/mn2_vn2_repl2_rmVnodeDir.sim +./test.sh -f unique/stable/balance_replica1.sim +./test.sh -f unique/stable/dnode2_stop.sim +./test.sh -f unique/stable/dnode2.sim +./test.sh -f unique/stable/dnode3.sim +./test.sh -f unique/stable/replica2_dnode4.sim +./test.sh -f unique/stable/replica2_vnode3.sim +./test.sh -f unique/stable/replica3_dnode6.sim +./test.sh -f unique/stable/replica3_vnode3.sim + +#======================b3-end=============== +#======================b4-start=============== + +./test.sh -f unique/http/admin.sim +./test.sh -f unique/http/opentsdb.sim + +./test.sh -f unique/import/replica2.sim +./test.sh -f unique/import/replica3.sim + +./test.sh -f general/alter/cached_schema_after_alter.sim +./test.sh -f general/alter/count.sim +./test.sh -f general/alter/dnode.sim +./test.sh -f general/alter/import.sim +./test.sh -f general/alter/insert1.sim +./test.sh -f general/alter/insert2.sim +./test.sh -f general/alter/metrics.sim +./test.sh -f general/alter/table.sim + +./test.sh -f general/cache/new_metrics.sim +./test.sh -f general/cache/restart_metrics.sim +./test.sh -f general/cache/restart_table.sim + +./test.sh -f general/connection/connection.sim + +./test.sh -f general/column/commit.sim +./test.sh -f general/column/metrics.sim +./test.sh -f general/column/table.sim + +./test.sh -f general/compress/commitlog.sim +./test.sh -f general/compress/compress.sim +./test.sh -f general/compress/compress2.sim +./test.sh -f general/compress/uncompress.sim + +./test.sh -f general/stable/disk.sim +./test.sh -f general/stable/dnode3.sim +./test.sh -f general/stable/metrics.sim +./test.sh -f general/stable/refcount.sim +./test.sh -f general/stable/show.sim +./test.sh -f general/stable/values.sim +./test.sh -f general/stable/vnode3.sim + +./test.sh -f unique/column/replica3.sim +./test.sh -f issue/TD-2713.sim +./test.sh -f general/parser/select_distinct_tag.sim +./test.sh -f unique/mnode/mgmt30.sim +./test.sh -f issue/TD-2677.sim +./test.sh -f issue/TD-2680.sim +./test.sh -f unique/dnode/lossdata.sim + +#======================b4-end=============== +#======================b5-start=============== + +./test.sh -f unique/dnode/alternativeRole.sim +./test.sh -f unique/dnode/balance1.sim +./test.sh -f unique/dnode/balance2.sim +./test.sh -f unique/dnode/balance3.sim +./test.sh -f unique/dnode/balancex.sim +./test.sh -f unique/dnode/offline1.sim +./test.sh -f unique/dnode/offline2.sim + +./test.sh -f general/stream/metrics_del.sim +./test.sh -f general/stream/metrics_replica1_vnoden.sim +./test.sh -f general/stream/restart_stream.sim +./test.sh -f general/stream/stream_3.sim +./test.sh -f general/stream/stream_restart.sim +./test.sh -f general/stream/table_del.sim +./test.sh -f general/stream/table_replica1_vnoden.sim + ./test.sh -f general/connection/test_old_data.sim ./test.sh -f unique/dnode/datatrans_3node.sim ./test.sh -f unique/dnode/datatrans_3node_2.sim +./test.sh -f general/db/alter_tables_d2.sim +./test.sh -f general/db/alter_tables_v1.sim +./test.sh -f general/db/alter_tables_v4.sim + +#======================b5-end=============== +#======================b6-start=============== + +./test.sh -f unique/dnode/reason.sim +./test.sh -f unique/dnode/remove1.sim +./test.sh -f unique/dnode/remove2.sim +./test.sh -f unique/dnode/vnode_clean.sim + +./test.sh -f unique/db/commit.sim +./test.sh -f unique/db/delete.sim +./test.sh -f unique/db/delete_part.sim +./test.sh -f unique/db/replica_add12.sim +./test.sh -f unique/db/replica_add13.sim +./test.sh -f unique/db/replica_add23.sim +./test.sh -f unique/db/replica_reduce21.sim +./test.sh -f unique/db/replica_reduce32.sim +./test.sh -f unique/db/replica_reduce31.sim +./test.sh -f unique/db/replica_part.sim + +./test.sh -f unique/vnode/many.sim +./test.sh -f unique/vnode/replica2_basic2.sim +./test.sh -f unique/vnode/replica2_repeat.sim +./test.sh -f unique/vnode/replica3_basic.sim +./test.sh -f unique/vnode/replica3_repeat.sim +./test.sh -f unique/vnode/replica3_vgroup.sim + +./test.sh -f unique/dnode/monitor.sim +./test.sh -f unique/dnode/monitor_bug.sim +./test.sh -f unique/dnode/simple.sim +./test.sh -f unique/dnode/data1.sim +./test.sh -f unique/dnode/m2.sim +./test.sh -f unique/dnode/m3.sim +./test.sh -f unique/dnode/offline3.sim +./test.sh -f general/wal/kill.sim +./test.sh -f general/wal/maxtables.sim + +./test.sh -f general/import/basic.sim +./test.sh -f general/import/commit.sim +./test.sh -f general/import/large.sim +./test.sh -f general/import/replica1.sim +./test.sh -f unique/cluster/balance1.sim +./test.sh -f unique/cluster/balance2.sim +./test.sh -f unique/cluster/balance3.sim + +#======================b6-end=============== +#======================b7-start=============== + +./test.sh -f general/compute/avg.sim +./test.sh -f general/compute/bottom.sim +./test.sh -f general/compute/count.sim +./test.sh -f general/compute/diff.sim +./test.sh -f general/compute/diff2.sim +./test.sh -f general/compute/first.sim +./test.sh -f general/compute/interval.sim +./test.sh -f general/compute/last.sim +./test.sh -f general/compute/leastsquare.sim +./test.sh -f general/compute/max.sim +./test.sh -f general/compute/min.sim +./test.sh -f general/compute/null.sim +./test.sh -f general/compute/percentile.sim +./test.sh -f general/compute/stddev.sim +./test.sh -f general/compute/sum.sim +./test.sh -f general/compute/top.sim + +./test.sh -f general/db/alter_option.sim +./test.sh -f general/db/alter_vgroups.sim +./test.sh -f general/db/basic.sim +./test.sh -f general/db/basic1.sim +./test.sh -f general/db/basic2.sim +./test.sh -f general/db/basic3.sim +./test.sh -f general/db/basic4.sim +./test.sh -f general/db/basic5.sim +./test.sh -f general/db/delete_reuse1.sim +./test.sh -f general/db/delete_reuse2.sim +./test.sh -f general/db/delete_reusevnode.sim +./test.sh -f general/db/delete_reusevnode2.sim +./test.sh -f general/db/delete_writing1.sim +./test.sh -f general/db/delete_writing2.sim +./test.sh -f general/db/delete.sim +./test.sh -f general/db/len.sim +./test.sh -f general/db/repeat.sim +./test.sh -f general/db/tables.sim +./test.sh -f general/db/vnodes.sim +./test.sh -f general/db/topic1.sim +./test.sh -f general/db/topic2.sim +./test.sh -f general/table/autocreate.sim +./test.sh -f general/table/basic1.sim +./test.sh -f general/table/basic2.sim +./test.sh -f general/table/basic3.sim +./test.sh -f general/table/bigint.sim +./test.sh -f general/table/binary.sim +./test.sh -f general/table/bool.sim +./test.sh -f general/table/column_name.sim +./test.sh -f general/table/column_num.sim +./test.sh -f general/table/column_value.sim +./test.sh -f general/table/column2.sim +./test.sh -f general/table/date.sim +./test.sh -f general/table/db.table.sim +./test.sh -f general/table/delete_reuse1.sim +./test.sh -f general/table/delete_reuse2.sim +./test.sh -f general/table/delete_writing.sim +./test.sh -f general/table/describe.sim +./test.sh -f general/table/double.sim +./test.sh -f general/table/fill.sim +./test.sh -f general/table/float.sim +./test.sh -f general/table/int.sim +./test.sh -f general/table/limit.sim +./test.sh -f general/table/smallint.sim +./test.sh -f general/table/table_len.sim +./test.sh -f general/table/table.sim +./test.sh -f general/table/tinyint.sim +./test.sh -f general/table/vgroup.sim +./test.sh -f general/table/createmulti.sim + +./test.sh -f unique/mnode/mgmt20.sim +./test.sh -f unique/mnode/mgmt21.sim +./test.sh -f unique/mnode/mgmt22.sim +./test.sh -f unique/mnode/mgmt23.sim +./test.sh -f unique/mnode/mgmt24.sim +./test.sh -f unique/mnode/mgmt25.sim +./test.sh -f unique/mnode/mgmt26.sim +./test.sh -f unique/mnode/mgmt33.sim +./test.sh -f unique/mnode/mgmt34.sim +./test.sh -f unique/mnode/mgmtr2.sim + +./test.sh -f unique/arbitrator/insert_duplicationTs.sim +./test.sh -f general/parser/join_manyblocks.sim +./test.sh -f general/parser/stableOp.sim +./test.sh -f general/parser/timestamp.sim +./test.sh -f general/parser/sliding.sim +./test.sh -f unique/big/balance.sim +#======================b7-end=============== diff --git a/tests/script/jenkins/basic_1.txt b/tests/script/jenkins/basic_1.txt deleted file mode 100644 index ae72e76b2f6c444e84fdaee3818200983796ace3..0000000000000000000000000000000000000000 --- a/tests/script/jenkins/basic_1.txt +++ /dev/null @@ -1,85 +0,0 @@ -./test.sh -f general/field/2.sim -./test.sh -f general/field/3.sim -./test.sh -f general/field/4.sim -./test.sh -f general/field/5.sim -./test.sh -f general/field/6.sim -./test.sh -f general/field/bigint.sim -./test.sh -f general/field/binary.sim -./test.sh -f general/field/bool.sim -./test.sh -f general/field/single.sim -./test.sh -f general/field/smallint.sim -./test.sh -f general/field/tinyint.sim - -./test.sh -f general/http/autocreate.sim -./test.sh -f general/http/chunked.sim -./test.sh -f general/http/gzip.sim -./test.sh -f general/http/restful.sim -./test.sh -f general/http/restful_insert.sim -./test.sh -f general/http/restful_limit.sim -./test.sh -f general/http/restful_full.sim -./test.sh -f general/http/prepare.sim -./test.sh -f general/http/telegraf.sim -./test.sh -f general/http/grafana_bug.sim -./test.sh -f general/http/grafana.sim - -./test.sh -f general/insert/basic.sim -./test.sh -f general/insert/insert_drop.sim -./test.sh -f general/insert/query_block1_memory.sim -./test.sh -f general/insert/query_block2_memory.sim -./test.sh -f general/insert/query_block1_file.sim -./test.sh -f general/insert/query_block2_file.sim -./test.sh -f general/insert/query_file_memory.sim -./test.sh -f general/insert/query_multi_file.sim -./test.sh -f general/insert/tcp.sim - -./test.sh -f general/parser/alter.sim -./test.sh -f general/parser/alter1.sim -./test.sh -f general/parser/alter_stable.sim -./test.sh -f general/parser/auto_create_tb.sim -./test.sh -f general/parser/auto_create_tb_drop_tb.sim -./test.sh -f general/parser/col_arithmetic_operation.sim -./test.sh -f general/parser/columnValue.sim -./test.sh -f general/parser/commit.sim -./test.sh -f general/parser/create_db.sim -./test.sh -f general/parser/create_mt.sim -./test.sh -f general/parser/create_tb.sim -./test.sh -f general/parser/dbtbnameValidate.sim -./test.sh -f general/parser/import_commit1.sim -./test.sh -f general/parser/import_commit2.sim -./test.sh -f general/parser/import_commit3.sim -./test.sh -f general/parser/insert_tb.sim -./test.sh -f general/parser/first_last.sim -./test.sh -f general/parser/lastrow.sim -./test.sh -f general/parser/nchar.sim -./test.sh -f general/parser/null_char.sim -./test.sh -f general/parser/single_row_in_tb.sim -./test.sh -f general/parser/select_from_cache_disk.sim -./test.sh -f general/parser/mixed_blocks.sim -./test.sh -f general/parser/selectResNum.sim -./test.sh -f general/parser/limit.sim -./test.sh -f general/parser/limit1.sim -./test.sh -f general/parser/limit1_tblocks100.sim -./test.sh -f general/parser/select_across_vnodes.sim -./test.sh -f general/parser/slimit1.sim -./test.sh -f general/parser/tbnameIn.sim -./test.sh -f general/parser/projection_limit_offset.sim -./test.sh -f general/parser/limit2.sim -./test.sh -f general/parser/fill.sim -./test.sh -f general/parser/fill_stb.sim -./test.sh -f general/parser/where.sim -./test.sh -f general/parser/slimit.sim -./test.sh -f general/parser/select_with_tags.sim -./test.sh -f general/parser/interp.sim -./test.sh -f general/parser/tags_dynamically_specifiy.sim -./test.sh -f general/parser/groupby.sim -./test.sh -f general/parser/set_tag_vals.sim -./test.sh -f general/parser/tags_filter.sim -./test.sh -f general/parser/slimit_alter_tags.sim -./test.sh -f general/parser/join.sim -./test.sh -f general/parser/join_multivnode.sim -./test.sh -f general/parser/binary_escapeCharacter.sim -./test.sh -f general/parser/repeatAlter.sim -./test.sh -f general/parser/union.sim -./test.sh -f general/parser/topbot.sim -./test.sh -f general/db/nosuchfile.sim -./test.sh -f general/parser/function.sim \ No newline at end of file diff --git a/tests/script/jenkins/basic_2.txt b/tests/script/jenkins/basic_2.txt deleted file mode 100644 index 5a2a6f4062e9f35f9ef77222383ecfc800ea7574..0000000000000000000000000000000000000000 --- a/tests/script/jenkins/basic_2.txt +++ /dev/null @@ -1,74 +0,0 @@ -cd ../../../debug; cmake .. -cd ../../../debug; make - -./test.sh -f general/tag/3.sim -./test.sh -f general/tag/4.sim -./test.sh -f general/tag/5.sim -./test.sh -f general/tag/6.sim -./test.sh -f general/tag/add.sim -./test.sh -f general/tag/bigint.sim -./test.sh -f general/tag/binary_binary.sim -./test.sh -f general/tag/binary.sim -./test.sh -f general/tag/bool_binary.sim -./test.sh -f general/tag/bool_int.sim -./test.sh -f general/tag/bool.sim -./test.sh -f general/tag/change.sim -./test.sh -f general/tag/column.sim -./test.sh -f general/tag/commit.sim -./test.sh -f general/tag/create.sim -./test.sh -f general/tag/delete.sim -./test.sh -f general/tag/double.sim -./test.sh -f general/tag/filter.sim -./test.sh -f general/tag/float.sim -./test.sh -f general/tag/int_binary.sim -./test.sh -f general/tag/int_float.sim -./test.sh -f general/tag/int.sim -./test.sh -f general/tag/set.sim -./test.sh -f general/tag/smallint.sim -./test.sh -f general/tag/tinyint.sim -./test.sh -f general/wal/sync.sim -./test.sh -f general/wal/kill.sim -./test.sh -f general/wal/maxtables.sim - -./test.sh -f general/user/authority.sim -./test.sh -f general/user/monitor.sim -./test.sh -f general/user/pass_alter.sim -./test.sh -f general/user/pass_len.sim -./test.sh -f general/user/user_create.sim -./test.sh -f general/user/user_len.sim - -./test.sh -f general/vector/metrics_field.sim -./test.sh -f general/vector/metrics_mix.sim -./test.sh -f general/vector/metrics_query.sim -./test.sh -f general/vector/metrics_tag.sim -./test.sh -f general/vector/metrics_time.sim -./test.sh -f general/vector/multi.sim -./test.sh -f general/vector/single.sim -./test.sh -f general/vector/table_field.sim -./test.sh -f general/vector/table_mix.sim -./test.sh -f general/vector/table_query.sim -./test.sh -f general/vector/table_time.sim - -./test.sh -f unique/account/account_create.sim -./test.sh -f unique/account/account_delete.sim -./test.sh -f unique/account/account_len.sim -./test.sh -f unique/account/authority.sim -./test.sh -f unique/account/basic.sim -./test.sh -f unique/account/paras.sim -./test.sh -f unique/account/pass_alter.sim -./test.sh -f unique/account/pass_len.sim -./test.sh -f unique/account/usage.sim -./test.sh -f unique/account/user_create.sim -./test.sh -f unique/account/user_len.sim - -./test.sh -f unique/big/balance.sim -./test.sh -f unique/big/maxvnodes.sim -./test.sh -f unique/big/tcp.sim - -./test.sh -f unique/cluster/alter.sim -./test.sh -f unique/cluster/balance1.sim -./test.sh -f unique/cluster/balance2.sim -./test.sh -f unique/cluster/balance3.sim -./test.sh -f unique/cluster/cache.sim -./test.sh -f unique/cluster/vgroup100.sim - diff --git a/tests/script/jenkins/basic_3.txt b/tests/script/jenkins/basic_3.txt deleted file mode 100644 index f53b1b763a45659ce498f863a3843d27ef4b233d..0000000000000000000000000000000000000000 --- a/tests/script/jenkins/basic_3.txt +++ /dev/null @@ -1,58 +0,0 @@ - -./test.sh -f unique/arbitrator/check_cluster_cfg_para.sim -#./test.sh -f unique/arbitrator/dn2_mn1_cache_file_sync.sim -./test.sh -f unique/arbitrator/dn3_mn1_full_createTableFail.sim -./test.sh -f unique/arbitrator/dn3_mn1_multiCreateDropTable.sim -#./test.sh -f unique/arbitrator/dn3_mn1_nw_disable_timeout_autoDropDnode.sim -#./test.sh -f unique/arbitrator/dn3_mn1_replica2_wal1_AddDelDnode.sim -./test.sh -f unique/arbitrator/dn3_mn1_replica_change_dropDnod.sim -./test.sh -f unique/arbitrator/dn3_mn1_replica_change.sim -#./test.sh -f unique/arbitrator/dn3_mn1_stopDnode_timeout.sim -# lower the priority while file corruption -#./test.sh -f unique/arbitrator/dn3_mn1_vnode_change.sim -#./test.sh -f unique/arbitrator/dn3_mn1_vnode_corruptFile_offline.sim -#./test.sh -f unique/arbitrator/dn3_mn1_vnode_corruptFile_online.sim -#./test.sh -f unique/arbitrator/dn3_mn1_vnode_createErrData_online.sim -./test.sh -f unique/arbitrator/dn3_mn1_vnode_noCorruptFile_offline.sim -./test.sh -f unique/arbitrator/dn3_mn1_vnode_delDir.sim -./test.sh -f unique/arbitrator/dn3_mn1_r2_vnode_delDir.sim -./test.sh -f unique/arbitrator/dn3_mn1_r3_vnode_delDir.sim -./test.sh -f unique/arbitrator/dn3_mn1_vnode_nomaster.sim -./test.sh -f unique/arbitrator/dn3_mn2_killDnode.sim - -./test.sh -f unique/arbitrator/offline_replica2_alterTable_online.sim -./test.sh -f unique/arbitrator/offline_replica2_alterTag_online.sim -./test.sh -f unique/arbitrator/offline_replica2_createTable_online.sim -./test.sh -f unique/arbitrator/offline_replica2_dropDb_online.sim -./test.sh -f unique/arbitrator/offline_replica2_dropTable_online.sim -./test.sh -f unique/arbitrator/offline_replica3_alterTable_online.sim -./test.sh -f unique/arbitrator/offline_replica3_alterTag_online.sim -./test.sh -f unique/arbitrator/offline_replica3_createTable_online.sim -./test.sh -f unique/arbitrator/offline_replica3_dropDb_online.sim -./test.sh -f unique/arbitrator/offline_replica3_dropTable_online.sim -./test.sh -f unique/arbitrator/replica_changeWithArbitrator.sim -./test.sh -f unique/arbitrator/sync_replica2_alterTable_add.sim -./test.sh -f unique/arbitrator/sync_replica2_alterTable_drop.sim - -./test.sh -f unique/arbitrator/sync_replica2_dropDb.sim -./test.sh -f unique/arbitrator/sync_replica2_dropTable.sim -./test.sh -f unique/arbitrator/sync_replica3_alterTable_add.sim -./test.sh -f unique/arbitrator/sync_replica3_alterTable_drop.sim -./test.sh -f unique/arbitrator/sync_replica3_dropDb.sim -./test.sh -f unique/arbitrator/sync_replica3_dropTable.sim - -./test.sh -f unique/migrate/mn2_vn2_repl2_rmMnodeDir.sim -./test.sh -f unique/migrate/mn2_vn2_repl2_rmMnodeVnodeDir.sim -./test.sh -f unique/migrate/mn2_vn2_repl2_rmMnodeVnodeDir_stopAll_starAll.sim -./test.sh -f unique/migrate/mn2_vn2_repl2_rmVnodeDir.sim - -./test.sh -f unique/stable/balance_replica1.sim -./test.sh -f unique/stable/dnode2_stop.sim -./test.sh -f unique/stable/dnode2.sim -./test.sh -f unique/stable/dnode3.sim -./test.sh -f unique/stable/replica2_dnode4.sim -./test.sh -f unique/stable/replica2_vnode3.sim -./test.sh -f unique/stable/replica3_dnode6.sim -./test.sh -f unique/stable/replica3_vnode3.sim - - diff --git a/tests/script/jenkins/basic_4.txt b/tests/script/jenkins/basic_4.txt deleted file mode 100644 index 5a7d23df719f737d0c5a3c85acfd3875b87872ab..0000000000000000000000000000000000000000 --- a/tests/script/jenkins/basic_4.txt +++ /dev/null @@ -1,46 +0,0 @@ - -./test.sh -f unique/http/admin.sim -./test.sh -f unique/http/opentsdb.sim - -./test.sh -f unique/import/replica2.sim -./test.sh -f unique/import/replica3.sim - -./test.sh -f general/alter/cached_schema_after_alter.sim -./test.sh -f general/alter/count.sim -./test.sh -f general/alter/dnode.sim -./test.sh -f general/alter/import.sim -./test.sh -f general/alter/insert1.sim -./test.sh -f general/alter/insert2.sim -./test.sh -f general/alter/metrics.sim -./test.sh -f general/alter/table.sim - -./test.sh -f general/cache/new_metrics.sim -./test.sh -f general/cache/restart_metrics.sim -./test.sh -f general/cache/restart_table.sim - -./test.sh -f general/connection/connection.sim - -./test.sh -f general/column/commit.sim -./test.sh -f general/column/metrics.sim -./test.sh -f general/column/table.sim - -./test.sh -f general/compress/commitlog.sim -./test.sh -f general/compress/compress.sim -./test.sh -f general/compress/compress2.sim -./test.sh -f general/compress/uncompress.sim - -./test.sh -f general/stable/disk.sim -./test.sh -f general/stable/dnode3.sim -./test.sh -f general/stable/metrics.sim -./test.sh -f general/stable/refcount.sim -./test.sh -f general/stable/show.sim -./test.sh -f general/stable/values.sim -./test.sh -f general/stable/vnode3.sim - -./test.sh -f unique/column/replica3.sim -./test.sh -f issue/TD-2713.sim -./test.sh -f general/parser/select_distinct_tag.sim -./test.sh -f unique/mnode/mgmt30.sim -./test.sh -f issue/TD-2677.sim -./test.sh -f issue/TD-2680.sim -./test.sh -f unique/dnode/lossdata.sim \ No newline at end of file diff --git a/tests/script/jenkins/basic_5.txt b/tests/script/jenkins/basic_5.txt deleted file mode 100644 index f89be9499e7a672a3c72646614552a43d1537463..0000000000000000000000000000000000000000 --- a/tests/script/jenkins/basic_5.txt +++ /dev/null @@ -1,19 +0,0 @@ -./test.sh -f unique/dnode/alternativeRole.sim -./test.sh -f unique/dnode/balance1.sim -./test.sh -f unique/dnode/balance2.sim -./test.sh -f unique/dnode/balance3.sim -./test.sh -f unique/dnode/balancex.sim -./test.sh -f unique/dnode/offline1.sim -./test.sh -f unique/dnode/offline2.sim - -./test.sh -f general/stream/metrics_del.sim -./test.sh -f general/stream/metrics_replica1_vnoden.sim -./test.sh -f general/stream/restart_stream.sim -./test.sh -f general/stream/stream_3.sim -./test.sh -f general/stream/stream_restart.sim -./test.sh -f general/stream/table_del.sim -./test.sh -f general/stream/table_replica1_vnoden.sim - -./test.sh -f general/connection/test_old_data.sim -./test.sh -f unique/dnode/datatrans_3node.sim -./test.sh -f unique/dnode/datatrans_3node_2.sim \ No newline at end of file diff --git a/tests/script/jenkins/basic_6.txt b/tests/script/jenkins/basic_6.txt deleted file mode 100644 index 9156360a9f548ba17d9b96d297e839e6b74aaa55..0000000000000000000000000000000000000000 --- a/tests/script/jenkins/basic_6.txt +++ /dev/null @@ -1,37 +0,0 @@ -./test.sh -f unique/dnode/reason.sim -./test.sh -f unique/dnode/remove1.sim -./test.sh -f unique/dnode/remove2.sim -./test.sh -f unique/dnode/vnode_clean.sim - -./test.sh -f unique/db/commit.sim -./test.sh -f unique/db/delete.sim -./test.sh -f unique/db/delete_part.sim -./test.sh -f unique/db/replica_add12.sim -./test.sh -f unique/db/replica_add13.sim -./test.sh -f unique/db/replica_add23.sim -./test.sh -f unique/db/replica_reduce21.sim -./test.sh -f unique/db/replica_reduce32.sim -./test.sh -f unique/db/replica_reduce31.sim -./test.sh -f unique/db/replica_part.sim - -./test.sh -f unique/vnode/many.sim -./test.sh -f unique/vnode/replica2_basic2.sim -./test.sh -f unique/vnode/replica2_repeat.sim -./test.sh -f unique/vnode/replica3_basic.sim -./test.sh -f unique/vnode/replica3_repeat.sim -./test.sh -f unique/vnode/replica3_vgroup.sim - -./test.sh -f unique/dnode/monitor.sim -./test.sh -f unique/dnode/monitor_bug.sim -./test.sh -f unique/dnode/simple.sim -./test.sh -f unique/dnode/data1.sim -./test.sh -f unique/dnode/m2.sim -./test.sh -f unique/dnode/m3.sim -./test.sh -f unique/dnode/offline3.sim -./test.sh -f general/wal/kill.sim -./test.sh -f general/wal/maxtables.sim - -./test.sh -f general/import/basic.sim -./test.sh -f general/import/commit.sim -./test.sh -f general/import/large.sim -./test.sh -f general/import/replica1.sim \ No newline at end of file diff --git a/tests/script/jenkins/basic_7.txt b/tests/script/jenkins/basic_7.txt deleted file mode 100644 index d951e0a48d33f457ae8aa53dc0577f6187e4ddee..0000000000000000000000000000000000000000 --- a/tests/script/jenkins/basic_7.txt +++ /dev/null @@ -1,83 +0,0 @@ - -./test.sh -f general/compute/avg.sim -./test.sh -f general/compute/bottom.sim -./test.sh -f general/compute/count.sim -./test.sh -f general/compute/diff.sim -./test.sh -f general/compute/diff2.sim -./test.sh -f general/compute/first.sim -./test.sh -f general/compute/interval.sim -./test.sh -f general/compute/last.sim -./test.sh -f general/compute/leastsquare.sim -./test.sh -f general/compute/max.sim -./test.sh -f general/compute/min.sim -./test.sh -f general/compute/null.sim -./test.sh -f general/compute/percentile.sim -./test.sh -f general/compute/stddev.sim -./test.sh -f general/compute/sum.sim -./test.sh -f general/compute/top.sim - -./test.sh -f general/db/alter_option.sim -./test.sh -f general/db/alter_tables_d2.sim -./test.sh -f general/db/alter_tables_v1.sim -./test.sh -f general/db/alter_tables_v4.sim -./test.sh -f general/db/alter_vgroups.sim -./test.sh -f general/db/basic.sim -./test.sh -f general/db/basic1.sim -./test.sh -f general/db/basic2.sim -./test.sh -f general/db/basic3.sim -./test.sh -f general/db/basic4.sim -./test.sh -f general/db/basic5.sim -./test.sh -f general/db/delete_reuse1.sim -./test.sh -f general/db/delete_reuse2.sim -./test.sh -f general/db/delete_reusevnode.sim -./test.sh -f general/db/delete_reusevnode2.sim -./test.sh -f general/db/delete_writing1.sim -./test.sh -f general/db/delete_writing2.sim -./test.sh -f general/db/delete.sim -./test.sh -f general/db/len.sim -./test.sh -f general/db/repeat.sim -./test.sh -f general/db/tables.sim -./test.sh -f general/db/vnodes.sim -./test.sh -f general/db/topic1.sim -./test.sh -f general/db/topic2.sim -./test.sh -f general/table/autocreate.sim -./test.sh -f general/table/basic1.sim -./test.sh -f general/table/basic2.sim -./test.sh -f general/table/basic3.sim -./test.sh -f general/table/bigint.sim -./test.sh -f general/table/binary.sim -./test.sh -f general/table/bool.sim -./test.sh -f general/table/column_name.sim -./test.sh -f general/table/column_num.sim -./test.sh -f general/table/column_value.sim -./test.sh -f general/table/column2.sim -./test.sh -f general/table/date.sim -./test.sh -f general/table/db.table.sim -./test.sh -f general/table/delete_reuse1.sim -./test.sh -f general/table/delete_reuse2.sim -./test.sh -f general/table/delete_writing.sim -./test.sh -f general/table/describe.sim -./test.sh -f general/table/double.sim -./test.sh -f general/table/fill.sim -./test.sh -f general/table/float.sim -./test.sh -f general/table/int.sim -./test.sh -f general/table/limit.sim -./test.sh -f general/table/smallint.sim -./test.sh -f general/table/table_len.sim -./test.sh -f general/table/table.sim -./test.sh -f general/table/tinyint.sim -./test.sh -f general/table/vgroup.sim -./test.sh -f general/table/createmulti.sim - -./test.sh -f unique/mnode/mgmt20.sim -./test.sh -f unique/mnode/mgmt21.sim -./test.sh -f unique/mnode/mgmt22.sim -./test.sh -f unique/mnode/mgmt23.sim -./test.sh -f unique/mnode/mgmt24.sim -./test.sh -f unique/mnode/mgmt25.sim -./test.sh -f unique/mnode/mgmt26.sim -./test.sh -f unique/mnode/mgmt33.sim -./test.sh -f unique/mnode/mgmt34.sim -./test.sh -f unique/mnode/mgmtr2.sim - -./test.sh -f unique/arbitrator/insert_duplicationTs.sim \ No newline at end of file diff --git a/tests/test-all.sh b/tests/test-all.sh index db9d6523a0eced09407f958e0adee9a4a8087bf1..b5e23c1506ed6d7deb7e9acaf7d3f3c182861ba6 100755 --- a/tests/test-all.sh +++ b/tests/test-all.sh @@ -24,13 +24,19 @@ function stopTaosd { function dohavecore(){ corefile=`find $corepath -mmin 1` + core_file=`echo $corefile|cut -d " " -f2` + echo $core_file + proc=`echo $corefile|cut -d "_" -f3` if [ -n "$corefile" ];then - echo 'taosd or taos has generated core' - if [[ $1 == 1 ]];then - exit 8 - fi + echo 'taosd or taos has generated core' + tar -zcPf $corepath'taos_'`date "+%Y_%m_%d_%H_%M_%S"`.tar.gz /usr/local/taos/ + if [[ $1 == 1 ]];then + echo '\n'|gdb /usr/local/taos/bin/$proc $core_file -ex "bt 10" -ex quit + exit 8 + fi fi } + function runSimCaseOneByOne { while read -r line; do if [[ $line =~ ^./test.sh* ]] || [[ $line =~ ^run* ]]; then @@ -61,7 +67,11 @@ function runSimCaseOneByOne { done < $1 } function runSimCaseOneByOnefq { - while read -r line; do + + start=`sed -n "/$1-start/=" jenkins/basic.txt` + end=`sed -n "/$1-end/=" jenkins/basic.txt` + for ((i=$start;i<=$end;i++)) ; do + line=`sed -n "$i"p jenkins/basic.txt` if [[ $line =~ ^./test.sh* ]] || [[ $line =~ ^run* ]]; then case=`echo $line | grep sim$ |awk '{print $NF}'` @@ -69,32 +79,36 @@ function runSimCaseOneByOnefq { date +%F\ %T | tee -a out.log if [[ "$tests_dir" == *"$IN_TDINTERNAL"* ]]; then echo -n $case - ./test.sh -f $case > /dev/null 2>&1 && \ + ./test.sh -f $case > ../../../sim/case.log 2>&1 && \ ( grep -q 'script.*'$case'.*failed.*, err.*lineNum' ../../../sim/tsim/log/taoslog0.0 && echo -e "${RED} failed${NC}" | tee -a out.log || echo -e "${GREEN} success${NC}" | tee -a out.log )|| \ ( grep -q 'script.*success.*m$' ../../../sim/tsim/log/taoslog0.0 && echo -e "${GREEN} success${NC}" | tee -a out.log ) || \ - echo -e "${RED} failed${NC}" | tee -a out.log + ( echo -e "${RED} failed${NC}" | tee -a out.log && echo '=====================log=====================' && cat ../../../sim/case.log ) else echo -n $case - ./test.sh -f $case > /dev/null 2>&1 && \ + ./test.sh -f $case > ../../sim/case.log 2>&1 && \ ( grep -q 'script.*'$case'.*failed.*, err.*lineNum' ../../sim/tsim/log/taoslog0.0 && echo -e "${RED} failed${NC}" | tee -a out.log || echo -e "${GREEN} success${NC}" | tee -a out.log )|| \ ( grep -q 'script.*success.*m$' ../../sim/tsim/log/taoslog0.0 && echo -e "${GREEN} success${NC}" | tee -a out.log ) || \ - echo -e "${RED} failed${NC}" | tee -a out.log + ( echo -e "${RED} failed${NC}" | tee -a out.log && echo '=====================log=====================' && cat ../../sim/case.log ) fi out_log=`tail -1 out.log ` if [[ $out_log =~ 'failed' ]];then if [[ "$tests_dir" == *"$IN_TDINTERNAL"* ]]; then cp -r ../../../sim ~/sim_`date "+%Y_%m_%d_%H:%M:%S"` + rm -rf ../../../sim/case.log else cp -r ../../sim ~/sim_`date "+%Y_%m_%d_%H:%M:%S" ` + rm -rf ../../sim/case.log fi exit 8 fi end_time=`date +%s` echo execution time of $case was `expr $end_time - $start_time`s. | tee -a out.log - dohavecore 1 + dohavecore $2 fi - done < $1 + done + rm -rf ../../../sim/case.log + rm -rf ../../sim/case.log } function runPyCaseOneByOne { @@ -126,8 +140,17 @@ function runPyCaseOneByOne { fi done < $1 } -function runPyCaseOneByOnefq { - while read -r line; do +function runPyCaseOneByOnefq() { + cd $tests_dir/pytest + if [[ $1 =~ full ]] ; then + start=1 + end=`sed -n '$=' fulltest.sh` + else + start=`sed -n "/$1-start/=" fulltest.sh` + end=`sed -n "/$1-end/=" fulltest.sh` + fi + for ((i=$start;i<=$end;i++)) ; do + line=`sed -n "$i"p fulltest.sh` if [[ $line =~ ^python.* ]]; then if [[ $line != *sleep* ]]; then @@ -139,22 +162,26 @@ function runPyCaseOneByOnefq { start_time=`date +%s` date +%F\ %T | tee -a pytest-out.log echo -n $case - $line > /dev/null 2>&1 && \ + $line > ../../sim/case.log 2>&1 && \ echo -e "${GREEN} success${NC}" | tee -a pytest-out.log || \ - echo -e "${RED} failed${NC}" | tee -a pytest-out.log + echo -e "${RED} failed${NC}" | tee -a pytest-out.log end_time=`date +%s` out_log=`tail -1 pytest-out.log ` if [[ $out_log =~ 'failed' ]];then cp -r ../../sim ~/sim_`date "+%Y_%m_%d_%H:%M:%S" ` + echo '=====================log=====================' + cat ../../sim/case.log + rm -rf ../../sim/case.log exit 8 fi echo execution time of $case was `expr $end_time - $start_time`s. | tee -a pytest-out.log else $line > /dev/null 2>&1 fi - dohavecore 1 + dohavecore $2 fi - done < $1 + done + rm -rf ../../sim/case.log } totalFailed=0 @@ -176,38 +203,38 @@ if [ "$2" != "jdbc" ] && [ "$2" != "python" ] && [ "$2" != "unit" ]; then runSimCaseOneByOne jenkins/basic.txt elif [ "$1" == "b1" ]; then echo "### run TSIM b1 test ###" - runSimCaseOneByOne jenkins/basic_1.txt - runSimCaseOneByOne jenkins/basic_4.txt - runSimCaseOneByOne jenkins/basic_5.txt - runSimCaseOneByOne jenkins/basic_6.txt - runSimCaseOneByOne jenkins/basic_7.txt + runSimCaseOneByOnefq b1 0 + runSimCaseOneByOnefq b4 0 + runSimCaseOneByOnefq b5 0 + runSimCaseOneByOnefq b6 0 + runSimCaseOneByOnefq b7 0 elif [ "$1" == "b2" ]; then echo "### run TSIM b2 test ###" - runSimCaseOneByOne jenkins/basic_2.txt + runSimCaseOneByOnefq b2 0 elif [ "$1" == "b3" ]; then echo "### run TSIM b3 test ###" - runSimCaseOneByOne jenkins/basic_3.txt + runSimCaseOneByOnefq b3 0 elif [ "$1" == "b1fq" ]; then echo "### run TSIM b1 test ###" - runSimCaseOneByOnefq jenkins/basic_1.txt + runSimCaseOneByOnefq b1 1 elif [ "$1" == "b2fq" ]; then echo "### run TSIM b2 test ###" - runSimCaseOneByOnefq jenkins/basic_2.txt + runSimCaseOneByOnefq b2 1 elif [ "$1" == "b3fq" ]; then echo "### run TSIM b3 test ###" - runSimCaseOneByOnefq jenkins/basic_3.txt + runSimCaseOneByOnefq b3 1 elif [ "$1" == "b4fq" ]; then echo "### run TSIM b4 test ###" - runSimCaseOneByOnefq jenkins/basic_4.txt + runSimCaseOneByOnefq b4 1 elif [ "$1" == "b5fq" ]; then echo "### run TSIM b5 test ###" - runSimCaseOneByOnefq jenkins/basic_5.txt + runSimCaseOneByOnefq b5 1 elif [ "$1" == "b6fq" ]; then echo "### run TSIM b6 test ###" - runSimCaseOneByOnefq jenkins/basic_6.txt + runSimCaseOneByOnefq b6 1 elif [ "$1" == "b7fq" ]; then echo "### run TSIM b7 test ###" - runSimCaseOneByOnefq jenkins/basic_7.txt + runSimCaseOneByOnefq b7 1 elif [ "$1" == "smoke" ] || [ -z "$1" ]; then echo "### run TSIM smoke test ###" runSimCaseOneByOne basicSuite.sim @@ -268,19 +295,19 @@ if [ "$2" != "sim" ] && [ "$2" != "jdbc" ] && [ "$2" != "unit" ]; then runPyCaseOneByOne fulltest.sh elif [ "$1" == "pytestfq" ]; then echo "### run Python full test ###" - runPyCaseOneByOnefq fulltest.sh + runPyCaseOneByOnefq full 0 elif [ "$1" == "p1" ]; then echo "### run Python_1 test ###" - runPyCaseOneByOnefq pytest_1.sh + runPyCaseOneByOnefq p1 1 elif [ "$1" == "p2" ]; then echo "### run Python_2 test ###" - runPyCaseOneByOnefq pytest_2.sh + runPyCaseOneByOnefq p2 1 elif [ "$1" == "p3" ]; then echo "### run Python_3 test ###" - runPyCaseOneByOnefq pytest_3.sh + runPyCaseOneByOnefq p3 1 elif [ "$1" == "p4" ]; then echo "### run Python_4 test ###" - runPyCaseOneByOnefq pytest_4.sh + runPyCaseOneByOnefq p4 1 elif [ "$1" == "b2" ] || [ "$1" == "b3" ]; then exit $(($totalFailed + $totalPyFailed)) elif [ "$1" == "smoke" ] || [ -z "$1" ]; then