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