未验证 提交 ff464661 编写于 作者: S Shengliang Guan 提交者: GitHub

Merge pull request #5512 from taosdata/feature/linux

run ci for odbc
...@@ -171,6 +171,8 @@ matrix: ...@@ -171,6 +171,8 @@ matrix:
- build-essential - build-essential
- cmake - cmake
- binutils-2.26 - binutils-2.26
- unixodbc
- unixodbc-dev
env: env:
- DESC="trusty/gcc-4.8/bintuils-2.26 build" - DESC="trusty/gcc-4.8/bintuils-2.26 build"
...@@ -198,6 +200,8 @@ matrix: ...@@ -198,6 +200,8 @@ matrix:
packages: packages:
- build-essential - build-essential
- cmake - cmake
- unixodbc
- unixodbc-dev
before_script: before_script:
- export TZ=Asia/Harbin - export TZ=Asia/Harbin
...@@ -252,6 +256,8 @@ matrix: ...@@ -252,6 +256,8 @@ matrix:
packages: packages:
- build-essential - build-essential
- cmake - cmake
- unixodbc
- unixodbc-dev
env: env:
- DESC="arm64 xenial build" - DESC="arm64 xenial build"
...@@ -280,6 +286,7 @@ matrix: ...@@ -280,6 +286,7 @@ matrix:
addons: addons:
homebrew: homebrew:
- cmake - cmake
- unixodbc
script: script:
- cd ${TRAVIS_BUILD_DIR} - cd ${TRAVIS_BUILD_DIR}
......
...@@ -19,6 +19,6 @@ ADD_SUBDIRECTORY(tsdb) ...@@ -19,6 +19,6 @@ ADD_SUBDIRECTORY(tsdb)
ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(wal)
ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(cq)
ADD_SUBDIRECTORY(dnode) ADD_SUBDIRECTORY(dnode)
#ADD_SUBDIRECTORY(connector/odbc) ADD_SUBDIRECTORY(connector/odbc)
ADD_SUBDIRECTORY(connector/jdbc) ADD_SUBDIRECTORY(connector/jdbc)
...@@ -261,7 +261,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { ...@@ -261,7 +261,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) {
return TSDB_CODE_SUCCESS; return TSDB_CODE_SUCCESS;
} }
if (1) { if (0) {
// allow user bind param data with different type // allow user bind param data with different type
union { union {
int8_t v1; int8_t v1;
...@@ -1057,14 +1057,28 @@ int taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes) { ...@@ -1057,14 +1057,28 @@ int taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes) {
} }
if (pStmt->isInsert) { if (pStmt->isInsert) {
SSqlObj* pSql = pStmt->pSql; SSqlCmd* pCmd = &pStmt->pSql->cmd;
SSqlCmd *pCmd = &pSql->cmd; STableMetaInfo* pTableMetaInfo = tscGetTableMetaInfoFromCmd(pCmd, 0, 0);
STableDataBlocks* pBlock = taosArrayGetP(pCmd->pDataBlocks, 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); int32_t ret =
if (idx < 0 || idx >= pBlock->numOfParams) return -1; 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 (type) *type = param->type;
if (bytes) *bytes = param->bytes; if (bytes) *bytes = param->bytes;
......
!c/
node_modules/
package-lock.json
...@@ -15,7 +15,7 @@ IF (TD_LINUX_64) ...@@ -15,7 +15,7 @@ IF (TD_LINUX_64)
message(STATUS "unixodbc/unixodbc-dev are installed, and odbc connector will be built") message(STATUS "unixodbc/unixodbc-dev are installed, and odbc connector will be built")
find_package(FLEX) find_package(FLEX)
if(NOT FLEX_FOUND) if(NOT FLEX_FOUND)
message(FATAL_ERROR "you need to install flex first") message(WARNING "you need to install flex first")
else () else ()
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0.0) 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") 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) ...@@ -24,7 +24,7 @@ IF (TD_LINUX_64)
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wconversion") SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wconversion")
ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(tools) ADD_SUBDIRECTORY(tools)
ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(examples)
endif () endif ()
endif() endif()
endif() endif()
...@@ -33,10 +33,44 @@ IF (TD_LINUX_64) ...@@ -33,10 +33,44 @@ IF (TD_LINUX_64)
ENDIF () ENDIF ()
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) IF (TD_WINDOWS_64)
find_package(ODBC) find_package(ODBC)
if (NOT ODBC_FOUND) if (NOT ODBC_FOUND)
message(FATAL_ERROR "you need to install ODBC first") message(WARNING "you need to install ODBC first")
else () else ()
message(STATUS "ODBC_INCLUDE_DIRS: ${ODBC_INCLUDE_DIRS}") message(STATUS "ODBC_INCLUDE_DIRS: ${ODBC_INCLUDE_DIRS}")
message(STATUS "ODBC_LIBRARIES: ${ODBC_LIBRARIES}") message(STATUS "ODBC_LIBRARIES: ${ODBC_LIBRARIES}")
...@@ -50,6 +84,7 @@ IF (TD_WINDOWS_64) ...@@ -50,6 +84,7 @@ IF (TD_WINDOWS_64)
else () else ()
ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(tools) ADD_SUBDIRECTORY(tools)
ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(examples)
endif() endif()
ENDIF () ENDIF ()
# ODBC 驱动 #
- **TAOS ODBC驱动持续更新中**
- **目前导出的ODBC函数包括(注: 有些不常用的函数只是导出,但并未实现)**:
SQLAllocEnv
SQLFreeEnv
SQLAllocConnect
SQLFreeConnect
SQLGetEnvAttr
SQLSetEnvAttr
SQLGetConnectAttr
SQLGetConnectOption
SQLGetInfo
SQLConnect
SQLDisconnect
SQLAllocStmt
SQLAllocHandle
SQLFreeHandle
SQLFreeStmt
SQLExecDirect
SQLNumResultCols
SQLRowCount
SQLColAttribute
SQLGetData
SQLFetch
SQLPrepare
SQLExecute
SQLParamData
SQLPutData
SQLGetDiagRec
SQLBindParameter
SQLDescribeParam
SQLDriverConnect
SQLSetConnectAttr
SQLDescribeCol
SQLBindCol
SQLNumParams
SQLSetStmtAttr
SQLBindParam
SQLCancel
SQLCancelHandle
SQLCloseCursor
SQLColumns
SQLCopyDesc
SQLDataSources
SQLEndTran
SQLFetchScroll
SQLGetCursorName
SQLGetDescField
SQLGetDescRec
SQLGetStmtAttr
SQLGetStmtOption
SQLGetTypeInfo
SQLSetConnectOption
SQLSetCursorName
SQLSetDescField
SQLSetDescRec
SQLSetParam
SQLSetStmtOption
SQLSpecialColumns
SQLStatistics
SQLTables
SQLTransact
`
- **国际化。可以通过在ODBC连接串中指定针对SQLCHAR/SQLWCHAR/taos_charset/system-locale的字符集来解决常见的环境匹配问题**.
- **现有的ODBC客户端工具可以籍此驱动同TAOS数据源互联,包括主流linux/macosx/windows平台**
- **现有的支持ODBC的编程语言可以籍此驱动同TAOS数据源互联, 例如: c/nodejs/python/rust/go已经在上述三个主流平台测试通过, 熟悉其他语言的同学可以发现这基本上是开箱即用**
- **持续更新中**...
# 编译和测试使用
**Note**: 下述所有步骤都在TDengine项目的根目录下进行
**Note**: 请先确保src/connector/odbc如下所示,被包含在src/CMakeLists.txt源文件中
```
...
ADD_SUBDIRECTORY(dnode)
ADD_SUBDIRECTORY(connector/odbc)
ADD_SUBDIRECTORY(connector/jdbc)
```
# Linux下的编译, 以Ubuntu为例
```
sudo apt install unixodbc unixodbc-dev flex
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
```
# MacOSX下的编译, 以Catalina为例,依赖homebrew进行第三方工具安装[https://brew.sh/]
```
brew install unixodbc
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
```
# Windows下的编译, 以Windows 10为例
- 安装windows的`flex`工具. 目前我们使用[https://github.com/lexxmark/winflexbison](url). 安装完成后请确保win_flex.exe所在目录记录于`PATH`环境变量中.
- 安装Microsoft Visual Studio工具, 以VS2019为例
- `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"`
- `rmdir /s /q debug`
- `cmake -G "NMake Makefiles" -B debug`
- `cmake --build debug`
- `cmake --install debug`
- 以管理员身份打开`命令提示符`
- 安装ODBC驱动: 在上述打开的提示符下执行 `odbcconf /A {INSTALLDRIVER "TAOS | Driver=C:/TDengine/driver/todbc.dll | ConnectFunctions=YYN | DriverODBCVer=03.00"}`
- 新增一个数据源DSN: 执行 `odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN | Server=<host>:<port>"}`
上述步骤出现失败的话,可以参看这些链接:
1. win flex的安装: https://github.com/lexxmark/winflexbison/releases
2. PATH环境变量: https://jingyan.baidu.com/article/8ebacdf02d3c2949f65cd5d0.html
3. 管理员身份: https://blog.csdn.net/weixin_41083002/article/details/81019893
4. 安装odbc驱动/数据源: https://docs.microsoft.com/en-us/sql/odbc/odbcconf-exe?view=sql-server-ver15
# 测试使用
强烈建议您在linux上编译运行taosd服务端,因为当前TAOS还没有windows侧的服务端移植.
**Note1**: <>符号所括起的内容请按您当前的系统填写
**Note2**: `.stmts` 文件存放的是测试用sql语句, 注意其格式为`UTF-8`(不带BOM导引头)
## 按官方文档在linux侧启动taosd,确保选用'UTF-8'作为其字符集
## 在linux下创建数据
```
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/samples/create_data.stmts
--<或指定特殊的ODBC连接字符串 -->
./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>' --sts ./src/connector/odbc/samples/create_data.stmts
```
## 在windows下检索数据
```
.\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;enc_char=UTF-8" --sts .\src\connector\odbc\samples\query_data.stmts
```
## 在MacOSX下检索数据
```
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>" --sts ./src/connector/odbc/samples/query_data.stmts
```
## 代码示例
- src/connector/odbc/examples/c
- src/connector/odbc/examples/js
- src/connector/odbc/examples/py
- src/connector/odbc/examples/rust
- src/connector/odbc/examples/go
在linux或MacOSX上, 可以通过修改运行如下脚本来尝试各种测试:
**Note**: 不要忘记替换<host>:<port>
**Note**: 你需要在你的平台上安装nodejs/python/rust/go
**Note**: 你还需要安装对应语言的ODBC包:
-- node-odbc for nodejs: https://www.npmjs.com/package/odbc
-- pyodbc for python: https://pypi.org/project/pyodbc/
-- rust-odbc for rust: https://docs.rs/odbc/0.17.0/odbc/
-- go-odbc for go: https://github.com/alexbrainman/odbc
```
echo c &&
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --sts src/connector/odbc/samples/create_data.stmts &&
echo nodejs &&
./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
echo python &&
python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
echo rust &&
pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=<host>:<port>' cargo run && popd &&
echo go &&
DSN='DSN=TAOS_DSN;Server=<host>:<port>' go run src/connector/odbc/examples/go/odbc.go &&
```
## 您可以对比测试一下prepared-batch-insert是否会带来速度的提升:
**注** src/connector/odbc/examples/c/main.c是tcodbc的源代码
```
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --insert --batch_size 200 --batchs 10000
```
# ODBC Driver # # 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 SQLAllocEnv
SQLFreeEnv SQLFreeEnv
SQLAllocConnect SQLAllocConnect
SQLFreeConnect SQLFreeConnect
SQLGetEnvAttr
SQLSetEnvAttr
SQLGetConnectAttr
SQLGetConnectOption
SQLGetInfo
SQLConnect SQLConnect
SQLDisconnect SQLDisconnect
SQLAllocStmt SQLAllocStmt
SQLAllocHandle SQLAllocHandle
SQLFreeHandle
SQLFreeStmt SQLFreeStmt
SQLExecDirect SQLExecDirect
SQLExecDirectW
SQLNumResultCols SQLNumResultCols
SQLRowCount SQLRowCount
SQLColAttribute SQLColAttribute
...@@ -22,29 +27,62 @@ SQLGetData ...@@ -22,29 +27,62 @@ SQLGetData
SQLFetch SQLFetch
SQLPrepare SQLPrepare
SQLExecute SQLExecute
SQLGetDiagField SQLParamData
SQLPutData
SQLGetDiagRec SQLGetDiagRec
SQLBindParameter SQLBindParameter
SQLDescribeParam
SQLDriverConnect SQLDriverConnect
SQLSetConnectAttr SQLSetConnectAttr
SQLDescribeCol SQLDescribeCol
SQLBindCol
SQLNumParams SQLNumParams
SQLSetStmtAttr 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 - **internationalized, you can specify charset for SQLCHAR/SQLWCHAR/taos_charset/system-locale to coordinate with the environment**.
your japanese windows box, and query them out in your local chinese windows machine.
- **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 # Building and Testing
**Note**: all `work` is done in TDengine's project directory **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 # Building under Linux, use Ubuntu as example
``` ```
...@@ -53,36 +91,68 @@ rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug & ...@@ -53,36 +91,68 @@ rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug &
``` ```
# Building under Windows, use Windows 10 as example # Building under Windows, use Windows 10 as example
- install windows `flex` port. We use [https://github.com/lexxmark/winflexbison](url) at the moment. Please be noted to append `<path_to_win_flex.exe>` to your `PATH`. - install windows `flex` port. We use [https://github.com/lexxmark/winflexbison](url) at the moment. Please be noted to append `<path_to_win_flex.exe>` to your `PATH`.
- install Microsoft Visual Studio, take VS2015 as example here - install Microsoft Visual Studio, take VS2019 as example here
- `"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64` - `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"`
- `rmdir /s /q debug` - `rmdir /s /q debug`
- `cmake -G "NMake Makefiles" -B debug` - `cmake -G "NMake Makefiles" -B debug`
- `cmake --build debug` - `cmake --build debug`
- `cmake --install debug` - `cmake --install debug`
- open your `Command Prompt` with Administrator's priviledge - 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 `odbcconf /A {INSTALLDRIVER "TAOS | Driver=C:/TDengine/driver/todbc.dll | ConnectFunctions=YYN | DriverODBCVer=03.00"}`
- 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 /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN | Server=host:port"}`
- add a new user dsn: run `odbcconf CONFIGDSN TAOS "DSN=TAOS_DSN|Server=<fqdn>:<port>`
# Test # 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 **Note1**: content within <> shall be modified to match your environment
**Note2**: `.stmts` source files are all encoded in `UTF-8` **Note2**: `.stmts` source files are all encoded in `UTF-8`
## start taosd in linux, suppose charset is `UTF-8` as default ## start taosd in linux, suppose charset is `UTF-8` as default, please follow TAOS doc for starting up
```
taosd -c ./debug/test/cfg
```
## create data in linux ## create data in linux
``` ```
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/tests/create_data.stmts ./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/samples/create_data.stmts
--<or with driver connection string --> --<or with driver connection string -->
./debug/build/bin/tcodbc --dcs 'Driver=TAOS;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;client_enc=UTF-8' ./src/connector/odbc/tests/create_data.stmts ./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>' --sts ./src/connector/odbc/samples/create_data.stmts
``` ```
## query data in windows ## query data in windows
``` ```
.\debug\build\bin\tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts .\src\connector\odbc\tests\query_data.stmts .\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;enc_char=UTF-8" --sts .\src\connector\odbc\samples\query_data.stmts
--<or with driver connection string --> ```
.\debug\build\bin\tcodbc --dcs "Driver=TAOS;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;client_enc=UTF-8" .\src\connector\odbc\tests\query_data.stmts ## query data in MacOSX
```
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>" --sts ./src/connector/odbc/samples/query_data.stmts
```
## code examples
- src/connector/odbc/examples/c
- src/connector/odbc/examples/js
- src/connector/odbc/examples/py
- src/connector/odbc/examples/rust
- src/connector/odbc/examples/go
on linux/MacOSX, here after are script-snippet for you to play with:
**Note**: don't forget to replace <host>:<port> with whatever on your environment
**Note**: you need to install node/python3/rust/go on you machine
**Note**: you also need to install odbc-bindings/odbc-pluggins on those language platform, such as:
-- node-odbc for nodejs: https://www.npmjs.com/package/odbc
-- pyodbc for python: https://pypi.org/project/pyodbc/
-- rust-odbc for rust: https://docs.rs/odbc/0.17.0/odbc/
-- go-odbc for go: https://github.com/alexbrainman/odbc
```
echo c &&
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --sts src/connector/odbc/samples/create_data.stmts &&
echo nodejs &&
./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
echo python &&
python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
echo rust &&
pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=<host>:<port>' cargo run && popd &&
echo go &&
DSN='DSN=TAOS_DSN;Server=<host>:<port>' go run src/connector/odbc/examples/go/odbc.go &&
```
## see how fast prepared-statment could bring up with:
```
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --insert --batch_size 200 --batchs 10000
``` ```
PROJECT(TDengine)
ADD_SUBDIRECTORY(c)
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 ()
#include "../src/todbc_log.h" #include "../../src/todbc_log.h"
#ifdef _MSC_VER #ifdef _MSC_VER
#include <winsock2.h> #include <winsock2.h>
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
#include <sqlext.h> #include <sqlext.h>
#include <odbcinst.h> #include <odbcinst.h>
#include "taos.h"
#include "taoserror.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
...@@ -22,6 +25,15 @@ do { \ ...@@ -22,6 +25,15 @@ do { \
} \ } \
} while (0); } 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; typedef struct db_column_s db_column_t;
struct db_column_s { struct db_column_s {
SQLSMALLINT nameLength; SQLSMALLINT nameLength;
...@@ -48,31 +60,22 @@ struct data_s { ...@@ -48,31 +60,22 @@ struct data_s {
char blob[40+1]; // why 80? ref: tests/examples/c/apitest.c char blob[40+1]; // why 80? ref: tests/examples/c/apitest.c
}; };
static const char *pre_stmts[] = {
"create database db",
"use db",
"create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))"
};
static const char *pro_stmts[] = {
// "insert into t values ('2019-07-15 00:00:00', 1)",
// "insert into t values ('2019-07-15 01:00:00', 2)",
"select * from t"
// "drop database db"
};
#define CHK_RESULT(r, ht, h, fmt, ...) \ #define CHK_RESULT(r, ht, h, fmt, ...) \
do { \ do { \
if (r==0) break; \ if (r==0) break; \
SQLCHAR ss[10]; \ SQLSMALLINT i_0381 = 1; \
SQLINTEGER ne = 0; \ while (1) { \
SQLCHAR es[4096]; \ SQLCHAR ss[10]; \
SQLSMALLINT n = 0; \ SQLINTEGER ne = 0; \
ss[0] = '\0'; \ SQLCHAR es[4096]; \
es[0] = '\0'; \ SQLSMALLINT n = 0; \
SQLRETURN ret = SQLGetDiagRec(ht, h, 1, ss, &ne, es, sizeof(es), &n); \ ss[0] = '\0'; \
if (ret) break; \ es[0] = '\0'; \
D("[%s]%s: " fmt "", ss, es, ##__VA_ARGS__); \ 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) } while (0)
static int open_connect(const char *dsn, const char *uid, const char *pwd, SQLHENV *pEnv, SQLHDBC *pConn) { static int open_connect(const char *dsn, const char *uid, const char *pwd, SQLHENV *pEnv, SQLHDBC *pConn) {
...@@ -150,7 +153,7 @@ static SQLRETURN traverse_cols(SQLHSTMT stmt, SQLSMALLINT cols) { ...@@ -150,7 +153,7 @@ static SQLRETURN traverse_cols(SQLHSTMT stmt, SQLSMALLINT cols) {
&column.dataType, &column.columnSize, &column.dataType, &column.columnSize,
&column.decimalDigits, &column.nullable); &column.decimalDigits, &column.nullable);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
D("col%02d:[%s]%d,type:[%d],colSize:[%"PRId64"],decimalDigits:[%d],nullable:[%d]", D("col%02d:[%s]%d,type:[%d],colSize:[%zu],decimalDigits:[%d],nullable:[%d]",
i+1, column.name, column.nameLength, column.dataType, column.columnSize, i+1, column.name, column.nameLength, column.dataType, column.columnSize,
column.decimalDigits, column.nullable); column.decimalDigits, column.nullable);
db_column_t *col = (db_column_t*)realloc(columns, (size_t)(i+1)*sizeof(*col)); db_column_t *col = (db_column_t*)realloc(columns, (size_t)(i+1)*sizeof(*col));
...@@ -184,7 +187,7 @@ static int do_statement(SQLHSTMT stmt, const char *statement) { ...@@ -184,7 +187,7 @@ static int do_statement(SQLHSTMT stmt, const char *statement) {
for (size_t i=0; i<cols; ++i) { for (size_t i=0; i<cols; ++i) {
SQLLEN soi = 0; SQLLEN soi = 0;
r = SQLGetData(stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, buf, sizeof(buf), &soi); r = SQLGetData(stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, buf, sizeof(buf), &soi);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "i/cols:[%ld/%d]", i,cols);
if (r) { if (r) {
if (r!=SQL_SUCCESS_WITH_INFO) { if (r!=SQL_SUCCESS_WITH_INFO) {
if (i>0) fprintf(stdout, "\n"); if (i>0) fprintf(stdout, "\n");
...@@ -199,164 +202,10 @@ static int do_statement(SQLHSTMT stmt, const char *statement) { ...@@ -199,164 +202,10 @@ static int do_statement(SQLHSTMT stmt, const char *statement) {
} }
fprintf(stdout, "\n"); fprintf(stdout, "\n");
} }
// r = SQLFetch(stmt);
// if (r==SQL_NO_DATA) {
// D("..........");
// r = SQL_SUCCESS;
// break;
// }
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
// if (r) break;
// r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement));
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
// if (r) break;
// r = SQLExecute(stmt);
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
// if (r) break;
} while (0);
return r;
}
static int do_insert(SQLHSTMT stmt, data_t data) {
SQLRETURN r = 0;
SQLLEN lbin;
SQLLEN lblob;
const char *statement = "insert into t values (?, ?, ?, ?, ?, ?, ?, ?, ?,?)";
#define ignored 0
do {
r = SQLPrepare(stmt, (SQLCHAR*)statement, (SQLINTEGER)strlen(statement));
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_TIMESTAMP, ignored, ignored, &data.ts, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, ignored, ignored, &data.b, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, ignored, ignored, &data.v1, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, ignored, ignored, &data.v2, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, ignored, ignored, &data.v4, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, ignored, ignored, &data.v8, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, ignored, ignored, &data.f4, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignored, ignored, &data.f8, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
lbin = SQL_NTS;
r = SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, sizeof(data.bin)-1, ignored, &data.bin, ignored, &lbin);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
lblob = SQL_NTS;
r = SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(data.blob)-1, ignored, &data.blob, ignored, &lblob);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
r = SQLExecute(stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
// ts += 1;
// v = 2;
// r = SQLExecute(stmt);
// if (r) break;
} while (0); } while (0);
#undef ignored
return r; return r;
} }
static int test1(const char *dsn, const char *uid, const char *pwd) {
SQLHENV env = {0};
SQLHDBC conn = {0};
int n = open_connect(dsn, uid, pwd, &env, &conn);
if (n) return 1;
int ok = 0;
do {
SQLRETURN r = SQL_SUCCESS;
SQLHSTMT stmt = {0};
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
if (r!=SQL_SUCCESS) break;
do {
if (do_statement(stmt, "drop database if exists db")) {
break;
}
for (size_t i=0; i<sizeof(pre_stmts)/sizeof(pre_stmts[0]); ++i) {
n = do_statement(stmt, pre_stmts[i]);
if (n) break;
}
do {
data_t data = {0};
data.ts = 1591060628001;
data.b = 1;
data.v1 = 127;
data.v2 = 32767;
data.v4 = 2147483647;
data.v8 = 9223372036854775807;
data.f4 = 123.456f;
data.f8 = 9999999.999999;
memset(data.bin, 0, sizeof(data.bin));
memset(data.blob, 0, sizeof(data.blob));
snprintf(data.bin, sizeof(data.bin), "hel我lo");
snprintf(data.blob, sizeof(data.blob), "world");
snprintf(data.blob, sizeof(data.blob), "wo人rld");
SQLHSTMT stmt = {0};
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
if (r!=SQL_SUCCESS) break;
do {
n = do_insert(stmt, data);
if (n) break;
} while (0);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
// r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
// if (r!=SQL_SUCCESS) break;
// do {
// r = do_insert(stmt, ts++, v++);
// if (r!=SQL_SUCCESS) break;
// } while (0);
// SQLFreeHandle(SQL_HANDLE_STMT, stmt);
ok = 1;
} while (0);
if (!ok) break;
ok = 0;
for (size_t i=0; i<sizeof(pro_stmts)/sizeof(pro_stmts[0]); ++i) {
n = do_statement(stmt, pro_stmts[i]);
if (n) break;
}
ok = 1;
} while (0);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
} while (0);
SQLDisconnect(conn);
SQLFreeConnect(conn);
SQLFreeEnv(env);
return ok ? 0 : 1;
}
int test_statements(const char *dsn, const char *uid, const char *pwd, const char **statements) { int test_statements(const char *dsn, const char *uid, const char *pwd, const char **statements) {
SQLRETURN r = SQL_SUCCESS; SQLRETURN r = SQL_SUCCESS;
SQLHENV env = {0}; SQLHENV env = {0};
...@@ -450,7 +299,7 @@ int test_env(void) { ...@@ -450,7 +299,7 @@ int test_env(void) {
return 0; return 0;
} }
int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls) { static int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls) {
FILE *f = fopen(sqls, "rb"); FILE *f = fopen(sqls, "rb");
if (!f) { if (!f) {
D("failed to open file [%s]", sqls); D("failed to open file [%s]", sqls);
...@@ -464,7 +313,7 @@ int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls ...@@ -464,7 +313,7 @@ int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls
ssize_t n = 0; ssize_t n = 0;
#ifdef _MSC_VER #ifdef _MSC_VER
n = taosGetline(&line, &len, f); n = taosGetlineImp(&line, &len, f);
#else #else
n = getline(&line, &len, f); n = getline(&line, &len, f);
#endif #endif
...@@ -483,9 +332,11 @@ int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls ...@@ -483,9 +332,11 @@ int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls
int positive = 1; int positive = 1;
if (strncmp(p, "N:", 2)==0) { if (strncmp(p, "N:", 2)==0) {
// negative sample
positive = 0; positive = 0;
p += 2; p += 2;
} else if (strncmp(p, "P:", 2)==0) { } else if (strncmp(p, "P:", 2)==0) {
// positive sample
p += 2; p += 2;
} }
...@@ -508,7 +359,7 @@ int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls ...@@ -508,7 +359,7 @@ int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls
return r ? 1 : 0; return r ? 1 : 0;
} }
int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) { static int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) {
SQLHSTMT stmt = {0}; SQLHSTMT stmt = {0};
CHK_TEST(create_statement(env, conn, &stmt)); CHK_TEST(create_statement(env, conn, &stmt));
int r = test_sqls_in_stmt(env, conn, stmt, sqls); int r = test_sqls_in_stmt(env, conn, stmt, sqls);
...@@ -516,7 +367,7 @@ int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) { ...@@ -516,7 +367,7 @@ int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) {
return r ? 1 : 0; return r ? 1 : 0;
} }
int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *connstr, const char *sqls) { static int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *connstr, const char *sqls) {
int r = 0; int r = 0;
SQLHENV env = {0}; SQLHENV env = {0};
SQLHDBC conn = {0}; SQLHDBC conn = {0};
...@@ -525,6 +376,7 @@ int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *con ...@@ -525,6 +376,7 @@ int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *con
} else { } else {
CHK_TEST(open_driver_connect(connstr, &env, &conn)); CHK_TEST(open_driver_connect(connstr, &env, &conn));
} }
if (sqls) { if (sqls) {
r = test_sqls_in_conn(env, conn, sqls); r = test_sqls_in_conn(env, conn, sqls);
} }
...@@ -534,43 +386,597 @@ int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *con ...@@ -534,43 +386,597 @@ int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *con
return r ? 1 : 0; 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; batch<arg->batchs; ++batch) {
for (int i=0; i<n_recs; ++i) {
test_v_t *rec = recs + i;
rec->dummy = 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 && i<arg->batchs; ++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; i<arg->batchs; ++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; j<arg->batch_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; i<arg->batchs; ++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; i<arg->batchs; ++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; i<arg->batchs; ++i) {
for (int j=0; j<arg->batch_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) { void usage(const char *arg0) {
fprintf(stdout, "%s usage:\n", arg0); fprintf(stdout, "%s usage:\n", arg0);
fprintf(stdout, "%s [--dsn <dsn>] [--uid <uid>] [--pwd <pwd>] [--dcs <dcs>] [--sts <sts>]\n", arg0); fprintf(stdout, "%s [--dsn <dsn>] [--uid <uid>] [--pwd <pwd>] [-C <conn_str>] [--sts <sts>]\n", arg0);
fprintf(stdout, " --dsn <dsn>: DSN\n"); fprintf(stdout, " --dsn <dsn>: DSN\n");
fprintf(stdout, " --uid <uid>: UID\n"); fprintf(stdout, " --uid <uid>: UID\n");
fprintf(stdout, " --pwd <pwd>: PWD\n"); fprintf(stdout, " --pwd <pwd>: PWD\n");
fprintf(stdout, " --dcs <dcs>: driver connection string\n"); fprintf(stdout, " -C <conn_str>: driver connection string\n");
fprintf(stdout, " --sts <sts>: file where statements store\n"); fprintf(stdout, " --sts <sts>: file where statements store\n");
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
// if (argc==1) { srand((unsigned)time(0));
// CHK_TEST(test_env()); const char *conn_str = NULL;
// CHK_TEST(test1("TAOS_DSN", "root", "taoxsdata"));
// D("Done!");
// return 0;
// }
const char *dsn = NULL; const char *dsn = NULL;
const char *uid = NULL; const char *uid = NULL;
const char *pwd = NULL; const char *pwd = NULL;
const char *dcs = NULL; // driver connection string
const char *sts = NULL; // statements file 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; ++i) { for (size_t i=1; i<argc; ++i) {
const char *arg = argv[i]; const char *arg = argv[i];
if (strcmp(arg, "-h")==0) { if (strcmp(arg, "-h")==0) {
usage(argv[0]); usage(argv[0]);
return 0; return 0;
} }
if (strcmp(arg, "-d")==0) {
debug_col_name_max_len = 1;
continue;
}
if (strcmp(arg, "--odbc")==0) {
insert_arg.use_odbc = 1;
continue;
}
if (strcmp(arg, "--taos_query")==0) {
insert_arg.use_taos_query= 1;
continue;
}
if (strcmp(arg, "--taos_stmt")==0) {
insert_arg.use_taos_stmt= 1;
continue;
}
if (strcmp(arg, "--insert")==0) {
insert = 1;
continue;
}
if (strcmp(arg, "--batch_size")==0) {
++i;
if (i>=argc) {
D("<batch_size> expected but got nothing");
return 1;
}
sscanf(argv[i], "%d", &insert_arg.batch_size);
if (insert_arg.batch_size<=0) {
D("<batch_size> invalid");
return 1;
}
continue;
}
if (strcmp(arg, "--batchs")==0) {
++i;
if (i>=argc) {
D("<batchs> expected but got nothing");
return 1;
}
sscanf(argv[i], "%d", &insert_arg.batchs);
if (insert_arg.batchs<=0) {
D("<batchs> 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) { if (strcmp(arg, "--dsn")==0) {
++i; ++i;
if (i>=argc) { if (i>=argc) {
D("<dsn> expected but got nothing"); D("<dsn> expected but got nothing");
return 1; return 1;
} }
if (dcs) { if (conn_str) {
D("--dcs has already been specified"); D("-C has already been specified");
return 1; return 1;
} }
dsn = argv[i]; dsn = argv[i];
...@@ -594,17 +1000,17 @@ int main(int argc, char *argv[]) { ...@@ -594,17 +1000,17 @@ int main(int argc, char *argv[]) {
pwd = argv[i]; pwd = argv[i];
continue; continue;
} }
if (strcmp(arg, "--dcs")==0) { if (strcmp(arg, "-C")==0) {
++i; ++i;
if (i>=argc) { if (i>=argc) {
D("<dcs> expected but got nothing"); D("<connection string> expected but got nothing");
return 1; return 1;
} }
if (dsn || uid || pwd) { if (dsn || uid || pwd) {
D("either of --dsn/--uid/--pwd has already been specified"); D("either of --dsn/--uid/--pwd has already been specified");
return 1; return 1;
} }
dcs = argv[i]; conn_str = argv[i];
continue; continue;
} }
if (strcmp(arg, "--sts")==0) { if (strcmp(arg, "--sts")==0) {
...@@ -616,58 +1022,39 @@ int main(int argc, char *argv[]) { ...@@ -616,58 +1022,39 @@ int main(int argc, char *argv[]) {
sts = argv[i]; sts = argv[i];
continue; continue;
} }
} if (strcmp(arg, "-p")==0) {
CHK_TEST(test_sqls(dsn, uid, pwd, dcs, sts)); prepare = 1;
D("Done!"); continue;
return 0;
if (0) {
const char *dsn = (argc>1) ? argv[1] : NULL;
const char *uid = (argc>2) ? argv[2] : NULL;
const char *pwd = (argc>3) ? argv[3] : NULL;
const char *connstr = (argc>4) ? argv[4] : NULL;
const char *sqls = (argc>5) ? argv[5] : NULL;
dsn = NULL;
uid = NULL;
pwd = NULL;
connstr = argv[1];
sqls = argv[2];
if (0) {
CHK_TEST(test_env());
CHK_TEST(test1(dsn, uid, pwd));
const char *statements[] = {
"drop database if exists m",
"create database m",
"use m",
"drop database m",
NULL
};
CHK_TEST(test_statements(dsn, uid, pwd, statements));
if (connstr)
CHK_TEST(test_driver_connect(connstr));
if (connstr) {
SQLHENV env = {0};
SQLHDBC conn = {0};
CHK_TEST(open_driver_connect(connstr, &env, &conn));
int r = tests(env, conn);
SQLDisconnect(conn);
SQLFreeConnect(conn);
SQLFreeEnv(env);
if (r) return 1;
}
} }
}
if ((dsn || connstr) && 1) { if (debug_col_name_max_len) {
CHK_TEST(test_sqls(dsn, uid, pwd, connstr, sqls)); 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;
D("Done!"); }
return 0; 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;
} }
/*******************************************************************************
/* ODBCSQL: a sample program that implements an ODBC command line interpreter.
/*
/* USAGE: ODBCSQL DSN=<dsn name> or
/* ODBCSQL FILEDSN=<file dsn> or
/* ODBCSQL DRIVER={driver name}
/*
/*
/* Copyright(c) Microsoft Corporation. This is a WDAC sample program and
/* is not suitable for use in production environments.
/*
/******************************************************************************/
/* Modules:
/* Main Main driver loop, executes queries.
/* DisplayResults Display the results of the query if any
/* AllocateBindings Bind column data
/* DisplayTitles Print column titles
/* SetConsole Set console display mode
/* HandleError Show ODBC error messages
/******************************************************************************/
#define _UNICODE
#define UNICODE
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <stdlib.h>
#include <sal.h>
/*******************************************/
/* Macro to call ODBC functions and */
/* report an error on failure. */
/* Takes handle, handle type, and stmt */
/*******************************************/
#define TRYODBC(h, ht, x) { RETCODE rc = x;\
if (rc != SQL_SUCCESS) \
{ \
HandleDiagnosticRecord (h, ht, rc); \
} \
if (rc == SQL_ERROR) \
{ \
fwprintf(stderr, L"Error in " L#x L"\n"); \
goto Exit; \
} \
}
/******************************************/
/* Structure to store information about */
/* a column.
/******************************************/
typedef struct STR_BINDING {
SQLSMALLINT cDisplaySize; /* size to display */
WCHAR *wszBuffer; /* display buffer */
SQLLEN indPtr; /* size or null */
BOOL fChar; /* character col? */
struct STR_BINDING *sNext; /* linked list */
} BINDING;
/******************************************/
/* Forward references */
/******************************************/
void HandleDiagnosticRecord (SQLHANDLE hHandle,
SQLSMALLINT hType,
RETCODE RetCode);
void DisplayResults(HSTMT hStmt,
SQLSMALLINT cCols);
void AllocateBindings(HSTMT hStmt,
SQLSMALLINT cCols,
BINDING** ppBinding,
SQLSMALLINT* pDisplay);
void DisplayTitles(HSTMT hStmt,
DWORD cDisplaySize,
BINDING* pBinding);
void SetConsole(DWORD cDisplaySize,
BOOL fInvert);
/*****************************************/
/* Some constants */
/*****************************************/
#define DISPLAY_MAX 50 // Arbitrary limit on column width to display
#define DISPLAY_FORMAT_EXTRA 3 // Per column extra display bytes (| <data> )
#define DISPLAY_FORMAT L"%c %*.*s "
#define DISPLAY_FORMAT_C L"%c %-*.*s "
#define NULL_SIZE 6 // <NULL>
#define SQL_QUERY_SIZE 1000 // Max. Num characters for SQL Query passed in.
#define PIPE L'|'
SHORT gHeight = 80; // Users screen height
int __cdecl wmain(int argc, _In_reads_(argc) WCHAR **argv)
{
SQLHENV hEnv = NULL;
SQLHDBC hDbc = NULL;
SQLHSTMT hStmt = NULL;
WCHAR* pwszConnStr;
WCHAR wszInput[SQL_QUERY_SIZE];
// Allocate an environment
if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv) == SQL_ERROR)
{
fwprintf(stderr, L"Unable to allocate an environment handle\n");
exit(-1);
}
// Register this as an application that expects 3.x behavior,
// you must register something if you use AllocHandle
TRYODBC(hEnv,
SQL_HANDLE_ENV,
SQLSetEnvAttr(hEnv,
SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3,
0));
// Allocate a connection
TRYODBC(hEnv,
SQL_HANDLE_ENV,
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
if (argc > 1)
{
pwszConnStr = *++argv;
}
else
{
pwszConnStr = L"";
}
// Connect to the driver. Use the connection string if supplied
// on the input, otherwise let the driver manager prompt for input.
TRYODBC(hDbc,
SQL_HANDLE_DBC,
SQLDriverConnect(hDbc,
GetDesktopWindow(),
pwszConnStr,
SQL_NTS,
NULL,
0,
NULL,
SQL_DRIVER_COMPLETE));
fwprintf(stderr, L"Connected!\n");
TRYODBC(hDbc,
SQL_HANDLE_DBC,
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
wprintf(L"Enter SQL commands, type (control)Z to exit\nSQL COMMAND>");
// Loop to get input and execute queries
while(_fgetts(wszInput, SQL_QUERY_SIZE-1, stdin))
{
RETCODE RetCode;
SQLSMALLINT sNumResults;
// Execute the query
if (!(*wszInput))
{
wprintf(L"SQL COMMAND>");
continue;
}
RetCode = SQLExecDirect(hStmt,wszInput, SQL_NTS);
switch(RetCode)
{
case SQL_SUCCESS_WITH_INFO:
{
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
// fall through
}
case SQL_SUCCESS:
{
// If this is a row-returning query, display
// results
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLNumResultCols(hStmt,&sNumResults));
if (sNumResults > 0)
{
DisplayResults(hStmt,sNumResults);
}
else
{
SQLLEN cRowCount;
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLRowCount(hStmt,&cRowCount));
if (cRowCount >= 0)
{
wprintf(L"%Id %s affected\n",
cRowCount,
cRowCount == 1 ? L"row" : L"rows");
}
}
break;
}
case SQL_ERROR:
{
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
break;
}
default:
fwprintf(stderr, L"Unexpected return code %hd!\n", RetCode);
}
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLFreeStmt(hStmt, SQL_CLOSE));
wprintf(L"SQL COMMAND>");
}
Exit:
// Free ODBC handles and exit
if (hStmt)
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
if (hDbc)
{
SQLDisconnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
}
if (hEnv)
{
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
}
wprintf(L"\nDisconnected.");
return 0;
}
/************************************************************************
/* DisplayResults: display results of a select query
/*
/* Parameters:
/* hStmt ODBC statement handle
/* cCols Count of columns
/************************************************************************/
void DisplayResults(HSTMT hStmt,
SQLSMALLINT cCols)
{
BINDING *pFirstBinding, *pThisBinding;
SQLSMALLINT cDisplaySize;
RETCODE RetCode = SQL_SUCCESS;
int iCount = 0;
// Allocate memory for each column
AllocateBindings(hStmt, cCols, &pFirstBinding, &cDisplaySize);
// Set the display mode and write the titles
DisplayTitles(hStmt, cDisplaySize+1, pFirstBinding);
// Fetch and display the data
bool fNoData = false;
do {
// Fetch a row
if (iCount++ >= gHeight - 2)
{
int nInputChar;
bool fEnterReceived = false;
while(!fEnterReceived)
{
wprintf(L" ");
SetConsole(cDisplaySize+2, TRUE);
wprintf(L" Press ENTER to continue, Q to quit (height:%hd)", gHeight);
SetConsole(cDisplaySize+2, FALSE);
nInputChar = _getch();
wprintf(L"\n");
if ((nInputChar == 'Q') || (nInputChar == 'q'))
{
goto Exit;
}
else if ('\r' == nInputChar)
{
fEnterReceived = true;
}
// else loop back to display prompt again
}
iCount = 1;
DisplayTitles(hStmt, cDisplaySize+1, pFirstBinding);
}
TRYODBC(hStmt, SQL_HANDLE_STMT, RetCode = SQLFetch(hStmt));
if (RetCode == SQL_NO_DATA_FOUND)
{
fNoData = true;
}
else
{
// Display the data. Ignore truncations
for (pThisBinding = pFirstBinding;
pThisBinding;
pThisBinding = pThisBinding->sNext)
{
if (pThisBinding->indPtr != SQL_NULL_DATA)
{
wprintf(pThisBinding->fChar ? DISPLAY_FORMAT_C:DISPLAY_FORMAT,
PIPE,
pThisBinding->cDisplaySize,
pThisBinding->cDisplaySize,
pThisBinding->wszBuffer);
}
else
{
wprintf(DISPLAY_FORMAT_C,
PIPE,
pThisBinding->cDisplaySize,
pThisBinding->cDisplaySize,
L"<NULL>");
}
}
wprintf(L" %c\n",PIPE);
}
} while (!fNoData);
SetConsole(cDisplaySize+2, TRUE);
wprintf(L"%*.*s", cDisplaySize+2, cDisplaySize+2, L" ");
SetConsole(cDisplaySize+2, FALSE);
wprintf(L"\n");
Exit:
// Clean up the allocated buffers
while (pFirstBinding)
{
pThisBinding = pFirstBinding->sNext;
free(pFirstBinding->wszBuffer);
free(pFirstBinding);
pFirstBinding = pThisBinding;
}
}
/************************************************************************
/* AllocateBindings: Get column information and allocate bindings
/* for each column.
/*
/* Parameters:
/* hStmt Statement handle
/* cCols Number of columns in the result set
/* *lppBinding Binding pointer (returned)
/* lpDisplay Display size of one line
/************************************************************************/
void AllocateBindings(HSTMT hStmt,
SQLSMALLINT cCols,
BINDING **ppBinding,
SQLSMALLINT *pDisplay)
{
SQLSMALLINT iCol;
BINDING *pThisBinding, *pLastBinding = NULL;
SQLLEN cchDisplay, ssType;
SQLSMALLINT cchColumnNameLength;
*pDisplay = 0;
for (iCol = 1; iCol <= cCols; iCol++)
{
pThisBinding = (BINDING *)(malloc(sizeof(BINDING)));
if (!(pThisBinding))
{
fwprintf(stderr, L"Out of memory!\n");
exit(-100);
}
if (iCol == 1)
{
*ppBinding = pThisBinding;
}
else
{
pLastBinding->sNext = pThisBinding;
}
pLastBinding = pThisBinding;
// Figure out the display length of the column (we will
// bind to char since we are only displaying data, in general
// you should bind to the appropriate C type if you are going
// to manipulate data since it is much faster...)
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLColAttribute(hStmt,
iCol,
SQL_DESC_DISPLAY_SIZE,
NULL,
0,
NULL,
&cchDisplay));
// Figure out if this is a character or numeric column; this is
// used to determine if we want to display the data left- or right-
// aligned.
// SQL_DESC_CONCISE_TYPE maps to the 1.x SQL_COLUMN_TYPE.
// This is what you must use if you want to work
// against a 2.x driver.
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLColAttribute(hStmt,
iCol,
SQL_DESC_CONCISE_TYPE,
NULL,
0,
NULL,
&ssType));
pThisBinding->fChar = (ssType == SQL_CHAR ||
ssType == SQL_VARCHAR ||
ssType == SQL_LONGVARCHAR);
pThisBinding->sNext = NULL;
// Arbitrary limit on display size
if (cchDisplay > DISPLAY_MAX)
cchDisplay = DISPLAY_MAX;
// Allocate a buffer big enough to hold the text representation
// of the data. Add one character for the null terminator
pThisBinding->wszBuffer = (WCHAR *)malloc((cchDisplay+1) * sizeof(WCHAR));
if (!(pThisBinding->wszBuffer))
{
fwprintf(stderr, L"Out of memory!\n");
exit(-100);
}
// Map this buffer to the driver's buffer. At Fetch time,
// the driver will fill in this data. Note that the size is
// count of bytes (for Unicode). All ODBC functions that take
// SQLPOINTER use count of bytes; all functions that take only
// strings use count of characters.
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLBindCol(hStmt,
iCol,
SQL_C_TCHAR,
(SQLPOINTER) pThisBinding->wszBuffer,
(cchDisplay + 1) * sizeof(WCHAR),
&pThisBinding->indPtr));
// Now set the display size that we will use to display
// the data. Figure out the length of the column name
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLColAttribute(hStmt,
iCol,
SQL_DESC_NAME,
NULL,
0,
&cchColumnNameLength,
NULL));
pThisBinding->cDisplaySize = max((SQLSMALLINT)cchDisplay, cchColumnNameLength);
if (pThisBinding->cDisplaySize < NULL_SIZE)
pThisBinding->cDisplaySize = NULL_SIZE;
*pDisplay += pThisBinding->cDisplaySize + DISPLAY_FORMAT_EXTRA;
}
return;
Exit:
exit(-1);
return;
}
/************************************************************************
/* DisplayTitles: print the titles of all the columns and set the
/* shell window's width
/*
/* Parameters:
/* hStmt Statement handle
/* cDisplaySize Total display size
/* pBinding list of binding information
/************************************************************************/
void DisplayTitles(HSTMT hStmt,
DWORD cDisplaySize,
BINDING *pBinding)
{
WCHAR wszTitle[DISPLAY_MAX];
SQLSMALLINT iCol = 1;
SetConsole(cDisplaySize+2, TRUE);
for (; pBinding; pBinding = pBinding->sNext)
{
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLColAttribute(hStmt,
iCol++,
SQL_DESC_NAME,
wszTitle,
sizeof(wszTitle), // Note count of bytes!
NULL,
NULL));
wprintf(DISPLAY_FORMAT_C,
PIPE,
pBinding->cDisplaySize,
pBinding->cDisplaySize,
wszTitle);
}
Exit:
wprintf(L" %c", PIPE);
SetConsole(cDisplaySize+2, FALSE);
wprintf(L"\n");
}
/************************************************************************
/* SetConsole: sets console display size and video mode
/*
/* Parameters
/* siDisplaySize Console display size
/* fInvert Invert video?
/************************************************************************/
void SetConsole(DWORD dwDisplaySize,
BOOL fInvert)
{
HANDLE hConsole;
CONSOLE_SCREEN_BUFFER_INFO csbInfo;
// Reset the console screen buffer size if necessary
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole != INVALID_HANDLE_VALUE)
{
if (GetConsoleScreenBufferInfo(hConsole, &csbInfo))
{
if (csbInfo.dwSize.X < (SHORT) dwDisplaySize)
{
csbInfo.dwSize.X = (SHORT) dwDisplaySize;
SetConsoleScreenBufferSize(hConsole, csbInfo.dwSize);
}
gHeight = csbInfo.dwSize.Y;
}
if (fInvert)
{
SetConsoleTextAttribute(hConsole, (WORD)(csbInfo.wAttributes | BACKGROUND_BLUE));
}
else
{
SetConsoleTextAttribute(hConsole, (WORD)(csbInfo.wAttributes & ~(BACKGROUND_BLUE)));
}
}
}
/************************************************************************
/* HandleDiagnosticRecord : display error/warning information
/*
/* Parameters:
/* hHandle ODBC handle
/* hType Type of handle (HANDLE_STMT, HANDLE_ENV, HANDLE_DBC)
/* RetCode Return code of failing command
/************************************************************************/
void HandleDiagnosticRecord (SQLHANDLE hHandle,
SQLSMALLINT hType,
RETCODE RetCode)
{
SQLSMALLINT iRec = 0;
SQLINTEGER iError;
WCHAR wszMessage[1000];
WCHAR wszState[SQL_SQLSTATE_SIZE+1];
if (RetCode == SQL_INVALID_HANDLE)
{
fwprintf(stderr, L"Invalid handle!\n");
return;
}
while (SQLGetDiagRec(hType,
hHandle,
++iRec,
wszState,
&iError,
wszMessage,
(SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
(SQLSMALLINT *)NULL) == SQL_SUCCESS)
{
// Hide data truncated..
if (wcsncmp(wszState, L"01004", 5))
{
fwprintf(stderr, L"[%5.5s] %s (%d)\n", wszState, wszMessage, iError);
}
}
}
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)
}
#!/usr/bin/env node
const odbc = require('odbc');
const path = require('path');
function usage() {
var arg = path.basename(process.argv[1]);
console.error(`usage:`);
console.error(`${arg} --DSN <DSN> --UID <uid> --PWD <pwd> --Server <host:port>`);
console.error(`${arg} -C <conn_str>`);
console.error(` conn_str eg: 'DSN={TAOS_DSN};UID=root;PWD=taosdata;Server=host:port'`);
}
var cfg = { };
if (process.argv.length==2) {
usage();
process.exit(0);
}
var i;
for (i=2; i<process.argv.length; ++i) {
var arg = process.argv[i];
if (arg=='-h') {
usage();
process.exit(0);
}
if (arg=="--DSN") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <dns> after --DSN but got nothing`);
usage(process.argv[1]);
process.exit(1);
}
arg = process.argv[i];
cfg.dsn = arg;
continue;
}
if (arg=="--UID") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <uid> after --UID but got nothing`);
usage(process.argv[1]);
process.exit(1);
}
arg = process.argv[i];
cfg.uid = arg;
continue;
}
if (arg=="--PWD") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <pwd> after --PWD but got nothing`);
usage(process.argv[1]);
process.exit(1);
}
arg = process.argv[i];
cfg.pwd = arg;
continue;
}
if (arg=="--Server") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <host:port> after --Server but got nothing`);
usage(process.argv[1]);
process.exit(1);
}
arg = process.argv[i];
cfg.server = arg;
continue;
}
if (arg=="-C") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <conn_str> after -C but got nothing`);
console.error(` conn_str eg: 'DSN={TAOS_DSN};UID=root;PWD=taosdata;Server=host:port'`);
process.exit(1);
}
arg = process.argv[i];
cfg.conn_str = arg;
continue;
}
console.error(`unknown argument: [${arg}]`);
usage(process.argv[1]);
process.exit(1);
}
var connectionString = cfg.conn_str;
if (!cfg.conn_str) {
connectionString = `DSN={${cfg.dsn}}; UID=${cfg.uid}; PWD=${cfg.pwd}; Server=${cfg.server}`;
}
(async function () {
const connStr = connectionString;
try {
console.log(`connecting [${connStr}]...`);
const connection = await odbc.connect(connStr);
await connection.query('create database if not exists m');
await connection.query('use m');
await connection.query('drop table if exists t');
await connection.query('create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), name nchar(3))');
await connection.query('insert into t values("2020-01-02 12:34:56.781", 1, 127, 32767, 32768, 32769, 123.456, 789.987, "hello", "我和你")');
console.log('.........');
result = await connection.query('select * from t');
console.log(result[0]);
statement = await connection.createStatement();
await statement.prepare('INSERT INTO t (ts, v1) VALUES(?, ?)');
await statement.bind(['2020-02-02 11:22:33.449', 89]);
result = await statement.execute();
console.log(result);
result = await connection.query('select * from t');
console.log(result[0]);
console.log(result[1]);
} catch (e) {
console.log('error:', e);
}
})();
{
"dependencies": {
"odbc": "^2.3.6"
}
}
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()
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()
[package]
name = "main"
version = "0.1.0"
authors = ["freemine <freemine@yeah.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
odbc = "0.17.0"
env_logger = "0.8.2"
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(())
}
#P: positive sample
#N: negative sample
P:drop database if exists m; P:drop database if exists m;
P:create database m; P:create database m;
P:use m; P:use m;
P:drop table if exists t; 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: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', 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.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.002', '你', '好');
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.003', 'abc', 'd'); P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.003', 'abc', 'd');
P:select * from t; 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));
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(TDengine) PROJECT(TDengine)
add_subdirectory(base)
IF (TD_LINUX_64) IF (TD_LINUX_64)
FLEX_TARGET(todbcFlexScanner FLEX_TARGET(todbcFlexScanner
todbc_scanner.l todbc_scanner.l
...@@ -15,12 +17,35 @@ IF (TD_LINUX_64) ...@@ -15,12 +17,35 @@ IF (TD_LINUX_64)
ADD_LIBRARY(todbc SHARED ${SRC} ${todbc_flex_scanner_src}) ADD_LIBRARY(todbc SHARED ${SRC} ${todbc_flex_scanner_src})
SET_TARGET_PROPERTIES(todbc PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(todbc PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(todbc PROPERTIES VERSION ${TD_VER_NUMBER} SOVERSION 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 .) target_include_directories(todbc PUBLIC .)
install(CODE "execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/install.sh ${CMAKE_BINARY_DIR})") install(CODE "execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/install.sh ${CMAKE_BINARY_DIR})")
ENDIF () 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) IF (TD_WINDOWS_64)
FLEX_TARGET(todbcFlexScanner FLEX_TARGET(todbcFlexScanner
todbc_scanner.l todbc_scanner.l
...@@ -37,7 +62,7 @@ IF (TD_WINDOWS_64) ...@@ -37,7 +62,7 @@ IF (TD_WINDOWS_64)
${todbc_flex_scanner_src} ${todbc_flex_scanner_src}
${CMAKE_CURRENT_BINARY_DIR}/todbc.rc ${CMAKE_CURRENT_BINARY_DIR}/todbc.rc
todbc.def) 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_include_directories(todbc PUBLIC .)
target_compile_definitions(todbc PRIVATE "todbc_EXPORT") target_compile_definitions(todbc PRIVATE "todbc_EXPORT")
...@@ -52,3 +77,4 @@ IF (TD_WINDOWS_64) ...@@ -52,3 +77,4 @@ IF (TD_WINDOWS_64)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.exp DESTINATION driver) INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.exp DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.dll DESTINATION driver) INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.dll DESTINATION driver)
ENDIF () ENDIF ()
#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 <odbcinst.h>
#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; i<n_params; ++i) {
// // param_t *param = paramset->params + 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_bps<ParameterNumber) {
// SET_ERROR(sql, "HY001", "");
// return SQL_ERROR;
// }
// bp_t *bp = paramset->bps + (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; i<paramset->paramset_size; ++i) {
// for (unsigned int j=0; j<paramset->n_bps && j<paramset->n_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_row<rs->n_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; i<rs->n_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_row<rs->n_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<ikvs; ++i) {
const char *k = kvs[i].line;
const char *v = NULL;
if (kvs[i].val) v = kvs[i].line + kvs[i].val;
r = SQLWritePrivateProfileString(dsn.line+dsn.val, k, v, "Odbc.ini");
}
} while (0);
if (dsn.line) free(dsn.line);
if (line) free(line);
return r;
}
static BOOL doDSNConfig(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
const char *p = lpszAttributes;
while (p && *p) {
p += strlen(p) + 1;
}
return FALSE;
}
static BOOL doDSNRemove(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
BOOL r = TRUE;
kv_t dsn = {0};
char *line = NULL;
do {
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; }
*v = '\0';
if (strstr(line, "DSN")==line) {
if (dsn.line) {
free(dsn.line);
dsn.line = NULL;
dsn.val = 0;
}
dsn.line = line;
dsn.val = v - line + 1;
line = NULL;
break;
} else {
free(line);
line = NULL;
}
p += strlen(p) + 1;
}
if (!r) break;
if (!dsn.line) {
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "lack of DSN");
r = FALSE;
break;
}
r = SQLWritePrivateProfileString("ODBC Data Sources", dsn.line+dsn.val, NULL, "Odbc.ini");
if (!r) break;
char buf[8192];
r = SQLGetPrivateProfileString(dsn.line+dsn.val, NULL, "null", buf, sizeof(buf), "Odbc.ini");
if (!r) break;
int n = 0;
char *s = buf;
while (s && *s && n++<10) {
SQLWritePrivateProfileString(dsn.line+dsn.val, s, NULL, "Odbc.ini");
s += strlen(s) + 1;
}
} while (0);
if (dsn.line) free(dsn.line);
if (line) free(line);
return r;
}
static BOOL doConfigDSN(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
BOOL r = FALSE;
const char *sReq = NULL;
switch(fRequest) {
case ODBC_ADD_DSN: sReq = "ODBC_ADD_DSN"; break;
case ODBC_CONFIG_DSN: sReq = "ODBC_CONFIG_DSN"; break;
case ODBC_REMOVE_DSN: sReq = "ODBC_REMOVE_DSN"; break;
default: sReq = "UNKNOWN"; break;
}
switch(fRequest) {
case ODBC_ADD_DSN: {
r = doDSNAdd(hwndParent, lpszDriver, lpszAttributes);
} break;
case ODBC_CONFIG_DSN: {
r = doDSNConfig(hwndParent, lpszDriver, lpszAttributes);
} break;
case ODBC_REMOVE_DSN: {
r = doDSNRemove(hwndParent, lpszDriver, lpszAttributes);
} break;
default: {
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
r = FALSE;
} break;
}
return r;
}
BOOL INSTAPI ConfigDSN(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
BOOL r;
r = doConfigDSN(hwndParent, fRequest, lpszDriver, lpszAttributes);
return r;
}
BOOL INSTAPI ConfigTranslator(HWND hwndParent, DWORD *pvOption)
{
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
return FALSE;
}
BOOL INSTAPI ConfigDriver(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszArgs,
LPSTR lpszMsg, WORD cbMsgMax, WORD *pcbMsgOut)
{
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
return FALSE;
}
#endif // _MSC_VER
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 <sql.h>
#include <sqlext.h>
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_
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 ()
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "env.h"
#include <pthread.h>
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;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "rs.h"
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _rs_h_
#define _rs_h_
#include "../base.h"
struct rs_s {
int affected_rows;
};
#endif // _rs_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_col<n_params; ++paramset->i_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(idx<paramset->n_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_row<paramset_size; ++paramset->i_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; i<fieldset->n_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; i<num; ++i) {
bindings[i].ColumnNumber = 0; // not set yet
}
fieldset->bindings = 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);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "tsdb_impl.h"
#include "env.h"
#include "../todbc_tls.h"
#include "../todbc_util.h"
#include "ttype.h"
#include <odbcinst.h>
// 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 (v<INT8_MIN || v>INT8_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 (v<INT16_MIN || v>INT16_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 (v<INT32_MIN || v>INT32_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<txt.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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<txt.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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<txt.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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<txt_db.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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<txt_db.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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<txt_db.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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 (v<INT8_MIN || v>INT8_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<txt_db.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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 (v<INT16_MIN || v>INT16_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<txt_db.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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 (v<INT32_MIN || v>INT32_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<txt_db.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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<txt_db.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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<txt_db.total_bytes) {
SET_OOM(errs, "failed to convert from [%s->%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(idx<paramset->n_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; i<nums; ++i) {
SQLRETURN r = do_fill_param(stmt, i);
if (r) return r;
}
}
return SQL_SUCCESS;
} while (0);
tsdb_stmt_close_stmt(tsdb_stmt);
return SQL_ERROR;
}
static SQLRETURN do_conv_utf8_to_sql_c_char(stmt_t *stmt, const char *buf, col_binding_t *col) {
OILE(stmt, "");
OILE(stmt->owner, "");
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; i<dlen; ++i) {
dst[i] = '\0'; // in case, the app forgets to check StrLen_orInd
}
return SQL_SUCCESS;
}
static SQLRETURN do_conv_binary_to_sql_c(stmt_t *stmt, TAOS_FIELD *field, void *val, size_t bytes, col_binding_t *binding) {
errs_t *errs = &stmt->errs;
switch (binding->TargetType)
{
case SQL_C_CHAR:
{
size_t len = (size_t)binding->BufferLength;
OILE(len>0, "");
if (bytes<len) {
len = bytes;
}
if (binding->TargetValue) {
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; i<dlen; ++i) {
dst[i] = '\0'; // in case, the app forgets to check StrLen_orInd
}
} break;
case SQL_C_BINARY:
{
size_t len = (size_t)binding->BufferLength;
OILE(len>0, "");
if (bytes<len) {
len = bytes;
}
if (binding->TargetValue) {
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; i<dlen; ++i) {
dst[i] = '\0'; // in case, the app forgets to check StrLen_orInd
}
} break;
case SQL_C_WCHAR:
{
const char *enc_to = conn->enc_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; i<dlen; ++i) {
dst[i] = '\0'; // in case, the app forgets to check StrLen_orInd
}
} 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_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 || val<INT32_MIN) {
SET_GENERAL(errs, "");
return SQL_ERROR;
}
if (binding->TargetValue) {
*(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_col<n_params, "");
// SQL_ATTR_PARAM_BIND_TYPE: SQL_PARAM_BIND_BY_COLUMN or row size
SQLULEN bind_type = stmt->attr.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_row<paramset_size, "");
param_t *param = params + (size_t)i_col;
OILE(param->ParameterNumber>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(idx<n_fields, "");
do_fetch_tsdb_fields(tsdb_stmt);
OILE(tsdb_stmt->tsdb_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;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _col_h_
#define _col_h_
#include "base.h"
struct col_s {
};
#endif // _col_h_
@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"}
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
set -u set -u
EXT="so"
[[ `uname` == 'Darwin' ]] && EXT="dylib"
SUDO="sudo"
[[ `uname` == 'Darwin' ]] && SUDO=""
BLD_DIR="$1" BLD_DIR="$1"
rm -f "${BLD_DIR}/template.ini" rm -f "${BLD_DIR}/template.ini"
...@@ -10,18 +17,30 @@ rm -f "${BLD_DIR}/template.dsn" ...@@ -10,18 +17,30 @@ rm -f "${BLD_DIR}/template.dsn"
cat > "${BLD_DIR}/template.ini" <<EOF cat > "${BLD_DIR}/template.ini" <<EOF
[TAOS] [TAOS]
Description=taos odbc driver Description=taos odbc driver
Driver=${BLD_DIR}/build/lib/libtodbc.so Driver=${BLD_DIR}/build/lib/libtodbc.${EXT}
EOF EOF
cat > "${BLD_DIR}/template.dsn" <<EOF cat > "${BLD_DIR}/template.dsn" <<EOF
[TAOS_DSN] [TAOS_DSN]
Description=Connection to TAOS Description=Connection to TAOS
Driver=TAOS Driver=TAOS
Server=localhost:6030 // UID=
// PWD=
// Server=localhost:6030
// https://www.npmjs.com/package/odbc
// SQL_C_FLOAT not support yet for node odbc, thus could promote to SQL_DOUBLE
// workaround:
// map.float=SQL_DOUBLE
// pyodbc: https://github.com/mkleehammer/pyodbc
// bigint seems not working properly
// workaround:
// map.bigint=SQL_CHAR
EOF EOF
# better remove first ? # better remove first ?
sudo odbcinst -i -d -f "${BLD_DIR}/template.ini" && ${SUDO} odbcinst -i -d -f "${BLD_DIR}/template.ini" &&
odbcinst -i -s -f "${BLD_DIR}/template.dsn" && odbcinst -i -s -f "${BLD_DIR}/template.dsn" &&
echo "odbc install done" echo "odbc install done"
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
// #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 <odbcinst.h>
#include <sqlext.h>
#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 = "<bad-charset>";
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, &params);
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; j<sql->n_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; i<sql->n_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; i<sql->n_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=<dsn>; UID=<uid>; PWD=<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, &params);
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 && i<ikvs; ++i) {
const char *k = kvs[i].line;
const char *v = NULL;
if (kvs[i].val) v = kvs[i].line + kvs[i].val;
r = SQLWritePrivateProfileString(dsn.line+dsn.val, k, v, "Odbc.ini");
}
} while (0);
if (dsn.line) free(dsn.line);
if (line) free(line);
return r;
}
static BOOL doDSNConfig(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
const char *p = lpszAttributes;
while (p && *p) {
p += strlen(p) + 1;
}
return FALSE;
}
static BOOL doDSNRemove(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
BOOL r = TRUE;
kv_t dsn = {0};
char *line = NULL;
do {
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; }
*v = '\0';
if (strstr(line, "DSN")==line) {
if (dsn.line) {
free(dsn.line);
dsn.line = NULL;
dsn.val = 0;
}
dsn.line = line;
dsn.val = v - line + 1;
line = NULL;
break;
} else {
free(line);
line = NULL;
}
p += strlen(p) + 1;
}
if (!r) break;
if (!dsn.line) {
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "lack of DSN");
r = FALSE;
break;
}
r = SQLWritePrivateProfileString("ODBC Data Sources", dsn.line+dsn.val, NULL, "Odbc.ini");
if (!r) break;
char buf[8192];
r = SQLGetPrivateProfileString(dsn.line+dsn.val, NULL, "null", buf, sizeof(buf), "Odbc.ini");
if (!r) break;
int n = 0;
char *s = buf;
while (s && *s && n++<10) {
SQLWritePrivateProfileString(dsn.line+dsn.val, s, NULL, "Odbc.ini");
s += strlen(s) + 1;
}
} while (0);
if (dsn.line) free(dsn.line);
if (line) free(line);
return r;
}
static BOOL doConfigDSN(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
BOOL r = FALSE;
const char *sReq = NULL;
switch(fRequest) {
case ODBC_ADD_DSN: sReq = "ODBC_ADD_DSN"; break;
case ODBC_CONFIG_DSN: sReq = "ODBC_CONFIG_DSN"; break;
case ODBC_REMOVE_DSN: sReq = "ODBC_REMOVE_DSN"; break;
default: sReq = "UNKNOWN"; break;
}
switch(fRequest) {
case ODBC_ADD_DSN: {
r = doDSNAdd(hwndParent, lpszDriver, lpszAttributes);
} break;
case ODBC_CONFIG_DSN: {
r = doDSNConfig(hwndParent, lpszDriver, lpszAttributes);
} break;
case ODBC_REMOVE_DSN: {
r = doDSNRemove(hwndParent, lpszDriver, lpszAttributes);
} break;
default: {
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
r = FALSE;
} break;
}
return r;
}
BOOL INSTAPI ConfigDSN(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
BOOL r;
r = doConfigDSN(hwndParent, fRequest, lpszDriver, lpszAttributes);
return r;
}
BOOL INSTAPI ConfigTranslator(HWND hwndParent, DWORD *pvOption)
{
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
return FALSE;
}
BOOL INSTAPI ConfigDriver(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszArgs,
LPSTR lpszMsg, WORD cbMsgMax, WORD *pcbMsgOut)
{
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
return FALSE;
}
#endif // _MSC_VER
static void init_routine(void) {
taos_init();
}
static size_t do_field_display_size(TAOS_FIELD *field) {
switch (field->type) {
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;
}
...@@ -3,13 +3,19 @@ SQLAllocEnv ...@@ -3,13 +3,19 @@ SQLAllocEnv
SQLFreeEnv SQLFreeEnv
SQLAllocConnect SQLAllocConnect
SQLFreeConnect SQLFreeConnect
SQLGetEnvAttr
SQLSetEnvAttr
SQLGetConnectAttr
SQLGetConnectOption
SQLGetInfo
SQLConnect SQLConnect
SQLDisconnect SQLDisconnect
SQLAllocStmt SQLAllocStmt
SQLAllocHandle SQLAllocHandle
SQLFreeHandle
SQLFreeStmt SQLFreeStmt
SQLExecDirect SQLExecDirect
SQLExecDirectW ;SQLExecDirectW
SQLNumResultCols SQLNumResultCols
SQLRowCount SQLRowCount
SQLColAttribute SQLColAttribute
...@@ -17,14 +23,45 @@ SQLGetData ...@@ -17,14 +23,45 @@ SQLGetData
SQLFetch SQLFetch
SQLPrepare SQLPrepare
SQLExecute SQLExecute
SQLGetDiagField SQLParamData
SQLPutData
;SQLGetDiagField
SQLGetDiagRec SQLGetDiagRec
SQLBindParameter SQLBindParameter
SQLDescribeParam
SQLDriverConnect SQLDriverConnect
SQLSetConnectAttr SQLSetConnectAttr
SQLDescribeCol SQLDescribeCol
SQLBindCol
SQLNumParams SQLNumParams
SQLSetStmtAttr 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 ConfigDSN
ConfigTranslator ConfigTranslator
ConfigDriver ConfigDriver
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "todbc_buf.h"
#include "todbc_list.h"
#include "todbc_log.h"
#include "todbc_tls.h"
#include <stdlib.h>
#include <string.h>
#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<len; ++i) {
p[i] = '\0';
}
buf = p;
blen = len;
}
if (!raw) {
todbc_buf_free(cache);
}
OD("n_reallocs: %zd", n_reallocs);
}
void todbc_buf_test(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, "");
}
while (iterates-- > 0) {
size_t i = 0;
char *buf = NULL;
while (i<size) {
char *p = NULL;
if (raw) {
p = realloc(buf, i);
} else {
p = todbc_buf_realloc(cache, buf, i);
}
OILE(p, "");
if (p!=buf) {
// OD("buf/p:%zd[%p/%p]", i, buf, p);
buf = p;
++n_reallocs;
}
if (i) p[i-1] = '\0';
++i;
}
// OD("buf :%zd[%p]", i, buf);
}
if (!raw) {
todbc_buf_free(cache);
}
OD("n_reallocs: %zd", n_reallocs);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_buf_h_
#define _todbc_buf_h_
#include <stdint.h>
#include <stddef.h>
// 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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "todbc_conv.h"
#include "todbc_log.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
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<dlen) return TSDB_CONV_OK;
return TSDB_CONV_TRUNC;
}
// src: double
TSDB_CONV_CODE tsdb_double_to_bit(double src, int8_t *dst) {
*dst = (int8_t)src;
if (src<0 || src>=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 (src<SCHAR_MIN || src>SCHAR_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 (src<SHRT_MIN || src>SHRT_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 (src<LONG_MIN || src>LONG_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 (src<LLONG_MIN || src>LLONG_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<dlen) return TSDB_CONV_OK;
return TSDB_CONV_TRUNC;
}
// src: SQL_TIMESTAMP_STRUCT
TSDB_CONV_CODE tsdb_timestamp_to_char(SQL_TIMESTAMP_STRUCT src, char *dst, size_t dlen) {
int n = snprintf(dst, dlen, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
src.year, src.month, src.day,
src.hour, src.minute, src.second,
src.fraction / 1000000);
DASSERT(n>=0);
if (n<dlen) return TSDB_CONV_OK;
if (strlen(dst)>=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 (n<dmax) return TSDB_CONV_OK;
return TSDB_CONV_TRUNC;
}
char* stack_buffer_alloc(stack_buffer_t *buffer, size_t bytes) {
if (!buffer) return NULL;
// align-by-size_of-size_t-bytes
if (bytes==0) bytes = sizeof(size_t);
bytes = (bytes + sizeof(size_t) - 1) / sizeof(size_t) * sizeof(size_t);
size_t next = buffer->next + 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 && ptr<buffer->buf+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<sizeof(utf8));
size_t len = (size_t)n;
TSDB_CONV_CODE code = tsdb_conv_write(cnv, utf8, &len, dst, dlen);
*dlen = (size_t)n+1;
return code;
}
TSDB_CONV_CODE tsdb_conv_write_double(tsdb_conv_t *cnv, double val, char *dst, size_t *dlen) {
char utf8[256];
int n = snprintf(utf8, sizeof(utf8), "%g", val);
DASSERT(n>=0);
DASSERT(n<sizeof(utf8));
size_t len = (size_t)n;
TSDB_CONV_CODE code = tsdb_conv_write(cnv, utf8, &len, dst, dlen);
*dlen = (size_t)n+1;
return code;
}
TSDB_CONV_CODE tsdb_conv_write_timestamp(tsdb_conv_t *cnv, SQL_TIMESTAMP_STRUCT val, char *dst, size_t *dlen) {
char utf8[256];
int n = snprintf(utf8, sizeof(utf8), "%04d-%02d-%02d %02d:%02d:%02d.%03d",
val.year, val.month, val.day,
val.hour, val.minute, val.second,
val.fraction / 1000000);
DASSERT(n>=0);
DASSERT(n<sizeof(utf8));
size_t len = (size_t)n;
TSDB_CONV_CODE code = tsdb_conv_write(cnv, utf8, &len, dst, dlen);
*dlen = (size_t)n+1;
return code;
}
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) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_bit(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
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) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_tinyint(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
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) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_smallint(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
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) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_int(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
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) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_bigint(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
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) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_ts(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_float(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, float *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_float(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_double(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, double *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_double(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
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) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_timestamp(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
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) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_timestamp_ts(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
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) {
if (!cnv) return TSDB_CONV_NOT_AVAIL;
char *buf;
size_t blen;
if (cnv->direct) {
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);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_conv_h_
#define _todbc_conv_h_
#include "os.h"
#include <iconv.h>
#include <sql.h>
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_
...@@ -16,16 +16,40 @@ ...@@ -16,16 +16,40 @@
#ifndef _TODBC_FLEX_H_ #ifndef _TODBC_FLEX_H_
#define _TODBC_FLEX_H_ #define _TODBC_FLEX_H_
#include <sql.h>
#include <sqlext.h>
// 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; typedef struct conn_val_s conn_val_t;
struct conn_val_s { struct conn_val_s {
char *key; char *dsn;
char *dsn; char *uid;
char *uid; char *pwd;
char *pwd; char *db;
char *db; char *server;
char *server; char *enc_local;
char *svr_enc; char *enc_char;
char *cli_enc; char *enc_wchar;
char *enc_db;
map_tsdb_type_t tsdb_map;
}; };
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "todbc_hash.h"
#include "todbc_list.h"
#include "todbc_log.h"
#include <stdlib.h>
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; i<hash->n_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; i<hash->n_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);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_hash_h_
#define _todbc_hash_h_
#include <stdint.h>
#include <stddef.h>
// 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_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "todbc_iconv.h"
#include "todbc_hash.h"
#include "todbc_log.h"
#include "todbc_string.h"
#include "todbc_tls.h"
#define invalid_iconv() ((iconv_t)-1)
struct todbc_iconvset_s {
todbc_hash_t *iconv_hash;
todbc_hash_t *enc_hash;
};
typedef struct todbc_iconv_key_s todbc_iconv_key_t;
struct todbc_iconv_key_s {
const char *enc_to;
const char *enc_from;
};
struct todbc_iconv_s {
char enc_to[64];
char enc_from[64];
todbc_iconv_key_t key;
iconv_t cnv;
todbc_enc_t from;
todbc_enc_t to;
unsigned int direct:1;
};
static void todbc_iconv_free(todbc_iconv_t *val);
todbc_iconvset_t* todbc_iconvset_create(void) {
todbc_iconvset_t *cnvset = (todbc_iconvset_t*)calloc(1, sizeof(*cnvset));
if (!cnvset) return NULL;
return cnvset;
}
void todbc_iconvset_free(todbc_iconvset_t *cnvset) {
if (!cnvset) return;
if (cnvset->iconv_hash) {
todbc_hash_free(cnvset->iconv_hash);
cnvset->iconv_hash = NULL;
}
if (cnvset->enc_hash) {
todbc_hash_free(cnvset->enc_hash);
cnvset->enc_hash = NULL;
}
free(cnvset);
}
static void do_iconv_hash_val_free(todbc_hash_t *hash, void *val, void *arg);
static unsigned long do_iconv_hash_key_hash(todbc_hash_t *hash, void *key);
static int do_iconv_hash_key_comp(todbc_hash_t *hash, void *key, void *val);
static void do_enc_hash_val_free(todbc_hash_t *hash, void *val, void *arg);
static unsigned long do_enc_hash_key_hash(todbc_hash_t *hash, void *key);
static int do_enc_hash_key_comp(todbc_hash_t *hash, void *key, void *val);
#define CHK(x, y, n) if (strcasecmp(x, y)==0) return n
#define SET_SIZES(cs, enc, a,b) do { \
if (strcasecmp(enc->enc, cs)==0) { \
enc->char_size = a; \
enc->variable_char_size = b; \
return; \
} \
} while (0)
static void do_set_sizes(todbc_enc_t *enc) {
if (!enc) return;
SET_SIZES("ISO-10646-UCS-2", enc, 2, -1);
SET_SIZES("UCS-2", enc, 2, -1);
SET_SIZES("CSUNICODE", enc, 2, -1);
SET_SIZES("UCS-2BE", enc, 2, -1);
SET_SIZES("UNICODE-1-1", enc, 2, -1);
SET_SIZES("UNICODEBIG", enc, 2, -1);
SET_SIZES("CSUNICODE11", enc, 2, -1);
SET_SIZES("UCS-2LE", enc, 2, -1);
SET_SIZES("UNICODELITTLE", enc, 2, -1);
SET_SIZES("UTF-16", enc, 2, -1);
SET_SIZES("UTF-16BE", enc, 2, -1);
SET_SIZES("UTF-16LE", enc, 2, -1);
SET_SIZES("UCS-2-INTERNAL", enc, 2, -1);
SET_SIZES("UCS-2-SWAPPED", enc, 2, -1);
SET_SIZES("ISO-10646-UCS-4", enc, 4, -1);
SET_SIZES("UCS-4", enc, 4, -1);
SET_SIZES("CSUCS4", enc, 4, -1);
SET_SIZES("UCS-4BE", enc, 4, -1);
SET_SIZES("UCS-4LE", enc, 4, -1);
SET_SIZES("UTF-32", enc, 4, -1);
SET_SIZES("UTF-32BE", enc, 4, -1);
SET_SIZES("UTF-32LE", enc, 4, -1);
SET_SIZES("UCS-4-INTERNAL", enc, 4, -1);
SET_SIZES("UCS-4-SWAPPED", enc, 4, -1);
SET_SIZES("UTF-8", enc, -1, 3);
SET_SIZES("UTF8", enc, -1, 3);
SET_SIZES("UTF-8-MAC", enc, -1, 3);
SET_SIZES("UTF8-MAC", enc, -1, 3);
SET_SIZES("CN-GB", enc, 2, -1);
SET_SIZES("EUC-CN", enc, 2, -1);
SET_SIZES("EUCCN", enc, 2, -1);
SET_SIZES("GB2312", enc, 2, -1);
SET_SIZES("CSGB2312", enc, 2, -1);
SET_SIZES("GBK", enc, 2, -1);
SET_SIZES("CP936", enc, 2, -1);
SET_SIZES("MS936", enc, 2, -1);
SET_SIZES("WINDOWS-936", enc, 2, -1);
SET_SIZES("GB18030", enc, 2, -1);
// add more setup here after
enc->char_size = -1;
enc->variable_char_size = -1;
}
static int do_get_null_size(const char *enc, int *null_size);
// static int do_get_unicode_char_size(const char *enc) {
// if (!enc) return -1;
//
// CHK("ISO-10646-UCS-2", enc, 2);
// CHK("UCS-2", enc, 2);
// CHK("CSUNICODE", enc, 2);
// CHK("UCS-2BE", enc, 2);
// CHK("UNICODE-1-1", enc, 2);
// CHK("UNICODEBIG", enc, 2);
// CHK("CSUNICODE11", enc, 2);
// CHK("UCS-2LE", enc, 2);
// CHK("UNICODELITTLE", enc, 2);
// CHK("UTF-16", enc, 2);
// CHK("UTF-16BE", enc, 2);
// CHK("UTF-16LE", enc, 2);
// CHK("UCS-2-INTERNAL", enc, 2);
// CHK("UCS-2-SWAPPED", enc, 2);
// CHK("ISO-10646-UCS-4", enc, 4);
// CHK("UCS-4", enc, 4);
// CHK("CSUCS4", enc, 4);
// CHK("UCS-4BE", enc, 4);
// CHK("UCS-4LE", enc, 4);
// CHK("UTF-32", enc, 4);
// CHK("UTF-32BE", enc, 4);
// CHK("UTF-32LE", enc, 4);
// CHK("UCS-4-INTERNAL", enc, 4);
// CHK("UCS-4-SWAPPED", enc, 4);
//
// return -1;
// }
todbc_enc_t todbc_iconvset_enc(todbc_iconvset_t *cnvset, const char *enc) {
do {
if (!cnvset) break;
if (!enc) break;
if (!cnvset->enc_hash) {
todbc_hash_conf_t conf = {0};
conf.arg = cnvset;
conf.val_free = do_enc_hash_val_free;
conf.key_hash = do_enc_hash_key_hash;
conf.key_comp = do_enc_hash_key_comp;
conf.slots = 7;
cnvset->enc_hash = todbc_hash_create(conf);
if (!cnvset->enc_hash) break;
}
void *old = NULL;
int found = 0;
int r = todbc_hash_get(cnvset->enc_hash, (void*)enc, &old, &found);
if (r) {
DASSERT(found==0);
DASSERT(old==NULL);
break;
}
if (found) {
DASSERT(old);
todbc_enc_t *val = (todbc_enc_t*)old;
return *val;
}
todbc_enc_t *val = (todbc_enc_t*)calloc(1, sizeof(*val));
if (!val) break;
do {
if (snprintf(val->enc, sizeof(val->enc), "%s", enc)>=sizeof(val->enc)) {
break;
}
do_set_sizes(val);
if (do_get_null_size(val->enc, &val->null_size)) break;
return *val;
} while (0);
free(val);
} while (0);
todbc_enc_t v = {0};
v.char_size = -1;
v.null_size = -1;
return v;
}
todbc_iconv_t* todbc_iconvset_get(todbc_iconvset_t *cnvset, const char *enc_to, const char *enc_from) {
if (!cnvset) return NULL;
if (!enc_to) return NULL;
if (!enc_from) return NULL;
todbc_iconv_key_t key;
key.enc_to = enc_to;
key.enc_from = enc_from;
if (!cnvset->iconv_hash) {
todbc_hash_conf_t conf = {0};
conf.arg = cnvset;
conf.val_free = do_iconv_hash_val_free;
conf.key_hash = do_iconv_hash_key_hash;
conf.key_comp = do_iconv_hash_key_comp;
conf.slots = 7;
cnvset->iconv_hash = todbc_hash_create(conf);
if (!cnvset->iconv_hash) return NULL;
}
void *old = NULL;
int found = 0;
int r = todbc_hash_get(cnvset->iconv_hash, &key, &old, &found);
if (r) {
DASSERT(found==0);
DASSERT(old==NULL);
return NULL;
}
if (found) {
DASSERT(old);
todbc_iconv_t *val = (todbc_iconv_t*)old;
// D("found [%p] for [%s->%s]", val, enc_from, enc_to);
return val;
}
todbc_iconv_t *val = (todbc_iconv_t*)calloc(1, sizeof(*val));
if (!val) return NULL;
do {
if (snprintf(val->enc_to, sizeof(val->enc_to), "%s", enc_to)>=sizeof(val->enc_to)) {
break;
}
if (snprintf(val->enc_from, sizeof(val->enc_from), "%s", enc_from)>=sizeof(val->enc_from)) {
break;
}
val->key.enc_to = val->enc_to;
val->key.enc_from = val->enc_from;
if (strcasecmp(enc_to, enc_from)==0) {
val->direct = 1;
}
val->from = todbc_tls_iconv_enc(enc_from);
val->to = todbc_tls_iconv_enc(enc_to);
val->cnv = iconv_open(enc_to, enc_from);
if (val->cnv==invalid_iconv()) break;
r = todbc_hash_put(cnvset->iconv_hash, &key, val);
if (r) break;
// D("created [%p] for [%s->%s]", val, enc_from, enc_to);
return val;
} while (0);
todbc_iconv_free(val);
return NULL;
}
iconv_t todbc_iconv_get(todbc_iconv_t *cnv) {
if (!cnv) return invalid_iconv();
return cnv->cnv;
}
// static int todbc_legal_chars_by_cnv(iconv_t cnv, const unsigned char *str, todbc_bytes_t *bc);
int todbc_iconv_get_legal_chars(todbc_iconv_t *cnv, const unsigned char *str, todbc_bytes_t *bc) {
DASSERT(0);
// if (!cnv) return -1;
// if (bc->inbytes==0 || bc->inbytes > INT64_MAX) {
// DASSERT(bc->chars<=INT64_MAX);
// if (bc->chars > 0) {
// DASSERT(cnv->from_char_size==2 || cnv->from_char_size==4);
// bc->inbytes = ((size_t)cnv->from_char_size) * bc->chars;
// bc->chars = 0;
// }
// } else {
// DASSERT(bc->chars==0);
// }
// return todbc_legal_chars_by_cnv(todbc_iconv_get(cnv), str, bc);
}
// static int todbc_legal_chars_by_block(iconv_t cnv, const unsigned char *str, todbc_bytes_t *bc);
// static int todbc_legal_chars_by_cnv(iconv_t cnv, const unsigned char *str, todbc_bytes_t *bc) {
// size_t max_bytes = bc->inbytes;
// if (max_bytes > INT64_MAX) max_bytes = (size_t)-1;
//
// if (max_bytes != (size_t)-1) {
// return todbc_legal_chars_by_block(cnv, str, bc);
// }
//
// memset(bc, 0, sizeof(*bc));
//
// size_t nbytes = 0;
// size_t ch_bytes = 0;
//
// char buf[16];
// char *inbuf = (char*)str;
// char *outbuf;
// size_t outbytes;
// size_t inbytes;
//
// size_t n = 0;
//
// int r = 0;
// inbytes = 1;
// while (1) {
// if (nbytes==max_bytes) break;
// outbytes = sizeof(buf);
// outbuf = buf;
//
// ch_bytes = inbytes;
// n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes);
// nbytes += 1;
// if (n==(size_t)-1) {
// int err = errno;
// if (err!=EINVAL) {
// E(".......");
// r = -1;
// break;
// }
// inbytes = ch_bytes + 1;
// continue;
// }
// DASSERT(inbytes==0);
// n = sizeof(buf) - outbytes;
//
// DASSERT(n==2);
// if (buf[0]=='\0' && buf[1]=='\0') {
// ch_bytes = 0;
// break;
// }
//
// bc->inbytes += ch_bytes;
// bc->chars += 1;
// bc->outbytes += 2;
//
// ch_bytes = 0;
// inbytes = 1;
// }
//
// outbytes = sizeof(buf);
// outbuf = buf;
// n = iconv(cnv, NULL, NULL, &outbuf, &outbytes);
//
// if (r) return -1;
// if (n==(size_t)-1) return -1;
// if (outbytes!=sizeof(buf)) return -1;
// if (outbuf!=buf) return -1;
// if (ch_bytes) return -1;
// return 0;
// }
unsigned char* todbc_iconv_conv(todbc_iconv_t *cnv, todbc_buf_t *buf, const unsigned char *src, todbc_bytes_t *bc) {
if (!buf) {
buf = todbc_tls_buf();
if (!buf) return NULL;
}
if (bc==NULL) {
todbc_bytes_t x = {0};
x.inbytes = (size_t)-1;
return todbc_iconv_conv(cnv, buf, src, &x);
}
DASSERT(buf);
DASSERT(cnv);
DASSERT(src);
todbc_string_t s = todbc_string_init(cnv->from.enc, src, bc->inbytes);
// D("total_bytes/bytes: %d/%zu/%zu", s.total_bytes, s.bytes);
DASSERT(s.buf==src);
todbc_string_t t = todbc_string_conv_to(&s, cnv->to.enc, NULL);
DASSERT(t.buf);
// bc->outbytes not counting size of null-terminator
bc->outbytes = t.bytes;
return (unsigned char*)t.buf;
}
size_t todbc_iconv_est_bytes(todbc_iconv_t *cnv, size_t inbytes) {
DASSERT(cnv);
DASSERT(inbytes<=INT64_MAX);
const todbc_enc_t *from = &cnv->from;
const todbc_enc_t *to = &cnv->to;
size_t outbytes = inbytes;
do {
if (from == to) break;
size_t inchars = inbytes;
if (from->char_size > 1) {
inchars = (inbytes + (size_t)from->char_size - 1) / (size_t)from->char_size;
}
outbytes = inchars;
size_t char_size = MAX_CHARACTER_SIZE;
if (to->char_size > 0) {
char_size = (size_t)to->char_size;
} else if (to->variable_char_size > 0) {
char_size = (size_t)to->variable_char_size;
}
outbytes *= char_size;
} while (0);
size_t nullbytes = MAX_CHARACTER_SIZE;
if (to->null_size > 0) {
nullbytes = (size_t)to->null_size;
}
// D("%s->%s: %zu->%zu", from->enc, to->enc, inbytes, outbytes);
outbytes += nullbytes;
// D("%s->%s: %zu->%zu", from->enc, to->enc, inbytes, outbytes);
return outbytes;
}
size_t todbc_iconv_bytes(todbc_iconv_t *cnv, size_t inchars) {
DASSERT(cnv);
if (inchars >= INT64_MAX) return (size_t)-1;
const todbc_enc_t *from = &cnv->from;
if (from->char_size > 0) {
return inchars * (size_t)from->char_size;
}
return (size_t)-1;
}
todbc_enc_t todbc_iconv_from(todbc_iconv_t *cnv) {
DASSERT(cnv);
return cnv->from;
}
todbc_enc_t todbc_iconv_to(todbc_iconv_t *cnv) {
DASSERT(cnv);
return cnv->to;
}
int todbc_iconv_raw(todbc_iconv_t *cnv, const unsigned char *src, size_t *slen, unsigned char *dst, size_t *dlen) {
DASSERT(cnv);
DASSERT(src && dst);
DASSERT(slen && *slen < INT64_MAX);
DASSERT(dlen && *dlen < INT64_MAX);
const int null_bytes = todbc_iconv_to(cnv).null_size;
if (*dlen<=null_bytes) {
D("target buffer too small to hold even null-terminator");
*dlen = 0; // while slen does not change
return -1;
}
char *inbuf = (char*)src;
size_t inbytes = *slen;
char *outbuf = (char*)dst;
size_t outbytes = *dlen;
size_t n = iconv(cnv->cnv, &inbuf, &inbytes, &outbuf, &outbytes);
int e = 0;
if (n==(size_t)-1) {
e = errno;
D("iconv failed: [%s->%s]:[%d]%s", cnv->from.enc, cnv->to.enc, e, strerror(e));
} else {
DASSERT(n==0);
}
const size_t inremain = inbytes;
const size_t outremain = outbytes;
*slen = inremain;
*dlen = outremain;
// writing null-terminator to make dest a real string
DASSERT(outbytes <= INT64_MAX);
if (outbytes < null_bytes) {
D("target buffer too small to hold null-terminator");
return -1;
} else {
for (int i=0; i<null_bytes; ++i) {
outbuf[i] = '\0';
}
}
iconv(cnv->cnv, NULL, NULL, NULL, NULL);
return 0;
}
todbc_string_t todbc_iconv_conv2(todbc_iconv_t *cnv, todbc_buf_t *buf, const unsigned char *src, size_t *slen) {
const todbc_string_t nul = {0};
if (!buf) {
buf = todbc_tls_buf();
if (!buf) return nul;
}
DASSERT(cnv);
DASSERT(src);
size_t inbytes = (size_t)-1;
if (slen && *slen <= INT64_MAX) inbytes = *slen;
todbc_string_t in = todbc_string_init(todbc_iconv_from(cnv).enc, src, inbytes);
if (in.buf!=src) return nul;
if (in.bytes > INT64_MAX) return nul;
inbytes = in.bytes;
size_t outblock = todbc_iconv_est_bytes(cnv, in.bytes);
if (outblock > INT64_MAX) return nul;
unsigned char *out = todbc_buf_alloc(buf, outblock);
if (!out) return nul;
const unsigned char *inbuf = src;
unsigned char *outbuf = out;
size_t outbytes = outblock;
int r = todbc_iconv_raw(cnv, inbuf, &inbytes, outbuf, &outbytes);
if (slen) *slen = inbytes;
if (r) return nul;
todbc_string_t s = {0};
s.buf = outbuf;
s.bytes = outblock - outbytes;
s.total_bytes = s.bytes;
return s;
}
// static int todbc_legal_chars_by_block(iconv_t cnv, const unsigned char *str, todbc_bytes_t *bc) {
// int r = 0;
// size_t max_bytes = bc->inbytes;
// char buf[1024*16];
// memset(bc, 0, sizeof(*bc));
// char *inbuf = (char*)str;
// while (bc->inbytes<max_bytes) {
// size_t inbytes = max_bytes - bc->inbytes;
// size_t outbytes = sizeof(buf);
// char *outbuf = buf;
// size_t n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes);
// int err = 0;
// if (n==(size_t)-1) {
// err = errno;
// if (err!=E2BIG && err!=EINVAL) r = -1;
// } else {
// DASSERT(n==0);
// }
// if (inbytes == max_bytes - bc->inbytes) {
// r = -1;
// }
// bc->inbytes += max_bytes - bc->inbytes - inbytes;
// bc->outbytes += sizeof(buf) - outbytes;
// if (r) break;
// }
// bc->chars = bc->outbytes / 2;
// iconv(cnv, NULL, NULL, NULL, NULL);
// return r ? -1 : 0;
// }
static void todbc_iconv_free(todbc_iconv_t *val) {
if (!val) return;
if (val->cnv!=invalid_iconv()) {
iconv_close(val->cnv);
val->cnv = invalid_iconv();
}
free(val);
}
// http://www.cse.yorku.ca/~oz/hash.html
static const unsigned long hash_seed = 5381;
static unsigned long hashing(unsigned long hash, const unsigned char *str);
static void do_enc_hash_val_free(todbc_hash_t *hash, void *val, void *arg) {
todbc_iconvset_t *cnv = (todbc_iconvset_t*)arg;
todbc_enc_t *v = (todbc_enc_t*)val;
DASSERT(hash);
DASSERT(cnv);
DASSERT(v);
free(v);
}
static unsigned long do_enc_hash_key_hash(todbc_hash_t *hash, void *key) {
const char *k = (const char*)key;
DASSERT(k);
unsigned long h = hash_seed;
h = hashing(h, (const unsigned char*)k);
return h;
}
static int do_enc_hash_key_comp(todbc_hash_t *hash, void *key, void *val) {
const char *k = (const char*)key;
todbc_enc_t *v = (todbc_enc_t*)val;
if (strcasecmp(k, v->enc)) return 1;
return 0;
}
static int do_get_null_size(const char *enc, int *null_size) {
iconv_t cnv = iconv_open(enc, UTF8_ENC);
if (cnv==(iconv_t)-1) return -1;
char src[] = "";
char dst[64];
char *inbuf = src;
size_t inbytes = 1;
char *outbuf = dst;
size_t outbytes = sizeof(dst);
size_t n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes);
DASSERT(n==0);
DASSERT(inbytes==0);
int size = (int)(sizeof(dst) - outbytes);
iconv_close(cnv);
if (null_size) *null_size = size;
return 0;
}
static void do_iconv_hash_val_free(todbc_hash_t *hash, void *val, void *arg) {
todbc_iconvset_t *cnv = (todbc_iconvset_t*)arg;
todbc_iconv_t *v = (todbc_iconv_t*)val;
DASSERT(hash);
DASSERT(cnv);
DASSERT(v);
todbc_iconv_free(v);
}
static unsigned long do_iconv_hash_key_hash(todbc_hash_t *hash, void *key) {
todbc_iconv_key_t *k = (todbc_iconv_key_t*)key;
DASSERT(k);
unsigned long h = hash_seed;
h = hashing(h, (const unsigned char*)k->enc_to);
h = hashing(h, (const unsigned char*)k->enc_from);
return h;
}
static int do_iconv_hash_key_comp(todbc_hash_t *hash, void *key, void *val) {
todbc_iconv_key_t *k = (todbc_iconv_key_t*)key;
todbc_iconv_t *v = (todbc_iconv_t*)val;
if (strcasecmp(k->enc_to, v->key.enc_to)) return 1;
if (strcasecmp(k->enc_from, v->key.enc_from)) return 1;
return 0;
}
static unsigned long hashing(unsigned long hash, const unsigned char *str) {
unsigned long c;
while ((c = *str++)!=0) {
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_iconv_h_
#define _todbc_iconv_h_
#include "todbc_buf.h"
#include "todbc_string.h"
#include <iconv.h>
// non-thread-safe
#define ASCII_ENC "ASCII"
#define UTF8_ENC "UTF-8"
#define UTF16_ENC "UCS-2LE"
#define UNICODE_ENC "UCS-4LE"
#define GB18030_ENC "GB18030"
#define MAX_CHARACTER_SIZE 6
typedef enum {
TSDB_CONV_OK = 0,
TSDB_CONV_NOT_AVAIL,
TSDB_CONV_OOM,
TSDB_CONV_OOR,
TSDB_CONV_TRUNC_FRACTION,
TSDB_CONV_TRUNC,
TSDB_CONV_CHAR_NOT_NUM,
TSDB_CONV_CHAR_NOT_TS,
TSDB_CONV_NOT_VALID_TS,
TSDB_CONV_GENERAL,
TSDB_CONV_BAD_CHAR,
TSDB_CONV_SRC_TOO_LARGE,
TSDB_CONV_SRC_BAD_SEQ,
TSDB_CONV_SRC_INCOMPLETE,
TSDB_CONV_SRC_GENERAL,
} TSDB_CONV_CODE;
typedef struct todbc_iconvset_s todbc_iconvset_t;
typedef struct todbc_iconv_s todbc_iconv_t;
typedef struct todbc_enc_s todbc_enc_t;
struct todbc_enc_s {
int char_size; // character size at most
int null_size; // size for null terminator
int variable_char_size; // such as 3 for UTF8
char enc[64]; // move here to satisfy todbc_enc_t enc = {0};
};
todbc_iconvset_t* todbc_iconvset_create(void);
void todbc_iconvset_free(todbc_iconvset_t *cnvset);
todbc_iconv_t* todbc_iconvset_get(todbc_iconvset_t *cnvset, const char *enc_to, const char *enc_from);
todbc_enc_t todbc_iconvset_enc(todbc_iconvset_t *cnvset, const char *enc);
typedef struct todbc_bytes_s todbc_bytes_t;
typedef struct todbc_err_s todbc_err_t;
struct todbc_err_s {
unsigned int einval:1; // EINVAL
unsigned int eilseq:1; // EILSEQ
unsigned int etoobig:1; // E2BIG
unsigned int eoom:1; // ENOMEM
};
struct todbc_bytes_s {
size_t inbytes, outbytes;
size_t chars;
todbc_err_t err;
};
typedef struct todbc_iconv_arg_s todbc_iconv_arg_t;
struct todbc_iconv_arg_s {
const unsigned char *inbuf;
size_t inbytes; // -1: not set
unsigned char *outbuf;
size_t outbytes; // -1: not set
size_t chars; // -1: not set
};
iconv_t todbc_iconv_get(todbc_iconv_t *cnv);
int todbc_iconv_get_legal_chars(todbc_iconv_t *cnv, const unsigned char *str, todbc_bytes_t *bc);
// non-thread-safe
// use todbc_buf_t as mem-allocator, if NULL, fall-back to thread-local version
unsigned char* todbc_iconv_conv(todbc_iconv_t *cnv, todbc_buf_t *buf, const unsigned char *src, todbc_bytes_t *bc);
// null-terminator-inclusive
size_t todbc_iconv_est_bytes(todbc_iconv_t *cnv, size_t inbytes);
// if inchars>=0 && enc_from has fixed-char-size, returns inchars * char_size
// otherwise -1
size_t todbc_iconv_bytes(todbc_iconv_t *cnv, size_t inchars);
todbc_enc_t todbc_iconv_from(todbc_iconv_t *cnv);
todbc_enc_t todbc_iconv_to(todbc_iconv_t *cnv);
// at return, *slen/*dlen stores the remaining #
int todbc_iconv_raw(todbc_iconv_t *cnv, const unsigned char *src, size_t *slen, unsigned char *dst, size_t *dlen);
// use todbc_buf_t as mem-allocator, if NULL, fall-back to thread-local version
// at return, *slen stores the remaining #
todbc_string_t todbc_iconv_conv2(todbc_iconv_t *cnv, todbc_buf_t *buf, const unsigned char *src, size_t *slen);
#endif // _todbc_iconv_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "todbc_list.h"
#include "todbc_log.h"
#include <stdlib.h>
struct todbc_list_s {
todbc_list_conf_t conf;
todbc_list_node_t *head;
todbc_list_node_t *tail;
size_t count;
};
struct todbc_list_node_s {
void *val;
todbc_list_t *list;
todbc_list_node_t *next;
todbc_list_node_t *prev;
};
static void do_remove(todbc_list_t *list, todbc_list_node_t *node);
static void do_pushback(todbc_list_t *list, todbc_list_node_t *node);
static void do_pushfront(todbc_list_t *list, todbc_list_node_t *node);
todbc_list_t* todbc_list_create(todbc_list_conf_t conf) {
todbc_list_t *list = (todbc_list_t*)calloc(1, sizeof(*list));
if (!list) return NULL;
list->conf = conf;
return list;
}
void todbc_list_free(todbc_list_t *list) {
if (!list) return;
while (list->head) {
void *val = list->head->val;
do_remove(list, list->head);
if (!list->conf.val_free) continue;
list->conf.val_free(list, val, list->conf.arg);
}
DASSERT(list->count == 0);
}
size_t todbc_list_count(todbc_list_t *list) {
if (!list) return 0;
return list->count;
}
int todbc_list_pushback(todbc_list_t *list, void *val) {
if (!list) return -1;
todbc_list_node_t *node = (todbc_list_node_t*)calloc(1, sizeof(*node));
if (!node) return -1;
node->val = val;
do_pushback(list, node);
return 0;
}
int todbc_list_pushfront(todbc_list_t *list, void *val) {
if (!list) return -1;
todbc_list_node_t *node = (todbc_list_node_t*)calloc(1, sizeof(*node));
if (!node) return -1;
node->val = val;
do_pushfront(list, node);
return 0;
}
int todbc_list_popback(todbc_list_t *list, void **val, int *found) {
if (!list) return -1;
if (!found) return -1;
*found = 0;
todbc_list_node_t *node = list->tail;
if (!node) return 0;
if (val) *val = node->val;
do_remove(list, node);
*found = 1;
return 0;
}
int todbc_list_popfront(todbc_list_t *list, void **val, int *found) {
if (!list) return -1;
if (!found) return -1;
*found = 0;
todbc_list_node_t *node = list->head;
if (!node) return 0;
if (val) *val = node->val;
do_remove(list, node);
*found = 1;
return 0;
}
int todbc_list_pop(todbc_list_t *list, void *val, int *found) {
if (!list) return -1;
if (!found) return -1;
*found = 0;
todbc_list_node_t *node = list->head;
while (node) {
if (node->val == val) break;
node = node->next;
}
if (!node) return 0;
do_remove(list, node);
*found = 1;
return 0;
}
int todbc_list_traverse(todbc_list_t *list, list_iterate_f iterate, void *arg) {
if (!list) return -1;
if (!iterate) return -1;
todbc_list_node_t *node = list->head;
while (node) {
int r = iterate(list, node->val, arg);
if (r) return r;
node = node->next;
}
return 0;
}
typedef struct comp_s comp_t;
struct comp_s {
list_comp_f comp;
void *arg;
int found;
void *val;
};
static int do_comp(todbc_list_t *list, void *val, void *arg);
int todbc_list_find(todbc_list_t *list, void *val, int *found, list_comp_f comp, void *arg) {
if (!list) return -1;
if (!found) return -1;
if (!comp) return -1;
*found = 0;
comp_t sarg = {0};
sarg.comp = comp;
sarg.arg = arg;
sarg.val = val;
todbc_list_traverse(list, do_comp, &sarg);
if (sarg.found) {
*found = 1;
}
return 0;
}
static int do_comp(todbc_list_t *list, void *val, void *arg) {
comp_t *sarg = (comp_t*)arg;
int r = sarg->comp(list, val, sarg->val, sarg->arg);
if (r==0) {
sarg->found = 1;
}
return r;
}
static void do_remove(todbc_list_t *list, todbc_list_node_t *node) {
DASSERT(node);
DASSERT(list);
DASSERT(list == node->list);
todbc_list_node_t *prev = node->prev;
todbc_list_node_t *next = node->next;
if (prev) prev->next = next;
else list->head = next;
if (next) next->prev = prev;
else list->tail = prev;
node->prev = NULL;
node->next = NULL;
node->list = NULL;
node->val = NULL;
list->count -= 1;
DASSERT(list->count <= INT64_MAX);
free(node);
}
static void do_pushback(todbc_list_t *list, todbc_list_node_t *node) {
DASSERT(list);
DASSERT(node);
DASSERT(node->list == NULL);
DASSERT(node->prev == NULL);
DASSERT(node->next == NULL);
node->list = list;
node->prev = list->tail;
if (list->tail) list->tail->next = node;
else list->head = node;
list->tail = node;
list->count += 1;
DASSERT(list->count > 0);
}
static void do_pushfront(todbc_list_t *list, todbc_list_node_t *node) {
DASSERT(node);
DASSERT(node->list == NULL);
DASSERT(node->prev == NULL);
DASSERT(node->next == NULL);
node->list = list;
node->next = list->head;
if (list->head) list->head->prev = node;
else list->tail = node;
list->head = node;
list->count += 1;
DASSERT(list->count > 0);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_list_h_
#define _todbc_list_h_
#include <stdint.h>
#include <stddef.h>
// non-thread-safe
typedef struct todbc_list_s todbc_list_t;
typedef struct todbc_list_node_s todbc_list_node_t;
typedef struct todbc_list_conf_s todbc_list_conf_t;
typedef void (*list_val_free_f)(todbc_list_t *list, void *val, void *arg);
struct todbc_list_conf_s {
void *arg;
list_val_free_f val_free;
};
todbc_list_t* todbc_list_create(todbc_list_conf_t conf);
void todbc_list_free(todbc_list_t *list);
size_t todbc_list_count(todbc_list_t *list);
int todbc_list_pushback(todbc_list_t *list, void *val);
int todbc_list_pushfront(todbc_list_t *list, void *val);
int todbc_list_popback(todbc_list_t *list, void **val, int *found);
int todbc_list_popfront(todbc_list_t *list, void **val, int *found);
int todbc_list_pop(todbc_list_t *list, void *val, int *found);
typedef int (*list_iterate_f)(todbc_list_t *list, void *val, void *arg);
int todbc_list_traverse(todbc_list_t *list, list_iterate_f iterate, void *arg);
typedef int (*list_comp_f)(todbc_list_t *list, void *old, void *val, void *arg);
int todbc_list_find(todbc_list_t *list, void *val, int *found, list_comp_f comp, void *arg);
#endif // _todbc_list_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "todbc_log.h"
uint64_t todbc_get_threadid(void) {
uint64_t tid = 0;
#ifdef __APPLE__
pthread_threadid_np(NULL, &tid);
#elif defined(__linux__)
tid = (uint64_t)syscall(__NR_gettid);
#elif defined(_MSC_VER)
tid = GetCurrentThreadId();
#else
#error you have to check the target API for thread id
#endif
return tid;
}
#ifdef _MSC_VER
static __declspec(thread) uint64_t thread_id = 0;
#else
static __thread uint64_t thread_id = 0;
#endif
#ifdef __GNUC__
__attribute__((format(printf, 6, 7)))
#endif
void todbc_log(const char *file, int line, const char *func, const char tag, int err, const char *fmt, ...) {
struct tm stm = {0};
struct timeval tv;
if (thread_id==0) {
thread_id = todbc_get_threadid();
}
gettimeofday(&tv, NULL);
time_t tt = tv.tv_sec;
localtime_r(&tt, &stm);
char buf[4096];
size_t bytes = sizeof(buf);
char *p = buf;
int n = 0;
n = snprintf(p, bytes, "%C%02d:%02d:%02d.%06d[%" PRIx64 "]%s[%d]%s()",
tag, stm.tm_hour, stm.tm_min, stm.tm_sec, (int)tv.tv_usec,
thread_id, basename((char*)file), line, func);
if (n>0) {
bytes -= (size_t)n;
p += n;
}
if (bytes>0) {
if (tag=='E' && err) {
n = snprintf(p, bytes, "[%d]%s", err, strerror(err));
if (n>0) {
bytes -= (size_t)n;
p += n;
}
}
}
if (bytes>0) {
n = snprintf(p, bytes, ": ");
if (n>0) {
bytes -= (size_t)n;
p += n;
}
}
if (bytes>0) {
va_list ap;
va_start(ap, fmt);
n = vsnprintf(p, bytes, fmt, ap);
va_end(ap);
}
fprintf(stderr, "%s\n", buf);
}
...@@ -18,25 +18,35 @@ ...@@ -18,25 +18,35 @@
#include "os.h" #include "os.h"
#define D(fmt, ...) \ #ifdef __GNUC__
fprintf(stderr, \ __attribute__((format(printf, 6, 7)))
"%s[%d]:%s() " fmt "\n", \ #endif
basename((char*)__FILE__), __LINE__, __func__, \ void todbc_log(const char *file, int line, const char *func, const char tag, int err, const char *fmt, ...);
##__VA_ARGS__)
#define DASSERT(statement) \
do { \
if (statement) break; \
D("Assertion failure: %s", #statement); \
abort(); \
} while (0)
#define DASSERTX(statement, fmt, ...) \ #define OL(tag, err, fmt, ...) todbc_log(__FILE__, __LINE__, __func__, tag, err, "%s" fmt "", "", ##__VA_ARGS__)
do { \ #define OD(fmt, ...) OL('D', 0, fmt, ##__VA_ARGS__)
if (statement) break; \ #define OE(fmt, ...) OL('E', errno, fmt, ##__VA_ARGS__)
D("Assertion failure: %s, " fmt "", #statement, ##__VA_ARGS__); \ #define OW(fmt, ...) OL('W', 0, fmt, ##__VA_ARGS__)
abort(); \ #define OI(fmt, ...) OL('I', 0, fmt, ##__VA_ARGS__)
#define OV(fmt, ...) OL('V', 0, fmt, ##__VA_ARGS__)
#define OA(statement, fmt, ...) do { \
if (statement) break; \
OL('A', 0, "Assertion failure:[%s]; " fmt "", #statement, ##__VA_ARGS__); \
abort(); \
} while (0) } while (0)
#define OILE(statement, fmt, ...) OA(statement, "internal logic error: [" fmt "]", ##__VA_ARGS__)
#define ONIY(statement, fmt, ...) OA(statement, "not implemented yet: [" fmt "]", ##__VA_ARGS__)
#define ONSP(statement, fmt, ...) OA(statement, "not support yet: [" fmt "]", ##__VA_ARGS__)
#define D(fmt, ...) OD(fmt, ##__VA_ARGS__)
#define E(fmt, ...) OE(fmt, ##__VA_ARGS__)
#define DASSERT(statement) OA(statement, "")
#define DASSERTX(statement, fmt, ...) OA(statement, fmt, ##__VA_ARGS__)
uint64_t todbc_get_threadid(void);
#endif // _todbc_log_h_ #endif // _todbc_log_h_
%{ %{
#ifdef _MSC_VER
#include <windows.h>
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#define basename PathFindFileNameA
#else
#include <libgen.h>
#endif
#include "todbc_flex.h" #include "todbc_flex.h"
#include <stdio.h> #include <stdio.h>
#include <odbcinst.h>
#ifdef _MSC_VER static int process_map(const char *cfg, map_tsdb_type_t *tsdb_map, int type);
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#define PUSH_STATE(state) yy_push_state(state, yyscanner) #define PUSH_STATE(state) yy_push_state(state, yyscanner)
#define POP_STATE() yy_pop_state(yyscanner) #define POP_STATE() yy_pop_state(yyscanner)
...@@ -28,50 +35,73 @@ do { \ ...@@ -28,50 +35,73 @@ do { \
while (yyleng) unput(yytext[yyleng-1]); \ while (yyleng) unput(yytext[yyleng-1]); \
} while (0) } while (0)
#define set_key() \ #define set_val() \
do { \ do { \
free(yyextra->key); \ int r = 0; \
yyextra->key = strdup(yytext); \ int curr; TOP_STATE(curr); \
POP_STATE(); \
int state; TOP_STATE(state); \
switch(state) { \
case DSN: { \
free(yyextra->dsn); \
yyextra->dsn = strdup(yytext); \
} break; \
case UID: { \
free(yyextra->uid); \
yyextra->uid = strdup(yytext); \
} break; \
case PWD: { \
free(yyextra->pwd); \
yyextra->pwd = strdup(yytext); \
} break; \
case SERVER: { \
free(yyextra->server); \
yyextra->server = strdup(yytext); \
} break; \
case DB: { \
free(yyextra->db); \
yyextra->db = strdup(yytext); \
} break; \
case ENC_CHAR: { \
free(yyextra->enc_char); \
yyextra->enc_char = strdup(yytext); \
} break; \
case ENC_WCHAR: { \
free(yyextra->enc_wchar); \
yyextra->enc_wchar = strdup(yytext); \
} break; \
case ENC_DB: { \
free(yyextra->enc_db); \
yyextra->enc_db = strdup(yytext); \
} break; \
case ENC_LOCAL: { \
free(yyextra->enc_local); \
yyextra->enc_local = strdup(yytext); \
} break; \
case TSDB_FLOAT: { \
if (process_map(yytext, &yyextra->tsdb_map, TSDB_FLOAT)) { \
r = -1; \
} \
} break; \
case TSDB_BIGINT: { \
if (process_map(yytext, &yyextra->tsdb_map, TSDB_BIGINT)) { \
r = -1; \
} \
} break; \
case KEY: { \
} break; \
default: { \
r = -1; \
} break; \
} \
PUSH_STATE(curr); \
if (r) return r; \
} while (0) } while (0)
#define set_val() \ #define FAIL() \
do { \ do { \
if (!yyextra->key) break; \ /*fprintf(stderr, "==%s[%d]%s()==\n", basename(__FILE__), __LINE__, __func__);*/ \
if (strcasecmp(yyextra->key, "DSN")==0) { \ return -1; \
free(yyextra->dsn); \
yyextra->dsn = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "UID")==0) { \
free(yyextra->uid); \
yyextra->uid = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "PWD")==0) { \
free(yyextra->pwd); \
yyextra->pwd = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "DB")==0) { \
free(yyextra->db); \
yyextra->pwd = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "Server")==0) { \
free(yyextra->server); \
yyextra->server = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "SERVER_ENC")==0) { \
free(yyextra->svr_enc); \
yyextra->svr_enc = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "CLIENT_ENC")==0) { \
free(yyextra->cli_enc); \
yyextra->cli_enc = strdup(yytext); \
break; \
} \
} while (0) } while (0)
%} %}
...@@ -89,57 +119,85 @@ do { \ ...@@ -89,57 +119,85 @@ do { \
%option warn %option warn
%option perf-report %option perf-report
%option 8bit %option 8bit
%option case-insensitive
%x DSN UID PWD SERVER DB
%x ENC_CHAR ENC_WCHAR ENC_DB ENC_LOCAL
%x TSDB_FLOAT TSDB_BIGINT
%x KEY EQ BRACE1 BRACE2 VAL %x KEY EQ BRACE1 BRACE2 VAL
%% %%
<<EOF>> { int state; TOP_STATE(state); <<EOF>> { int state; TOP_STATE(state);
if (state == INITIAL) yyterminate(); if (state == INITIAL) yyterminate();
if (state == VAL) yyterminate(); if (state == VAL) yyterminate();
return -1; } FAIL(); }
[[:space:]]+ { } [[:space:]]+ { }
[[:alnum:]_]+ { set_key(); PUSH_STATE(KEY); } "DSN" { PUSH_STATE(DSN); }
.|\n { return -1; } "UID" { PUSH_STATE(UID); }
"PWD" { PUSH_STATE(PWD); }
"Server" { PUSH_STATE(SERVER); }
"DB" { PUSH_STATE(DB); }
"ENC_CHAR" { PUSH_STATE(ENC_CHAR); }
"ENC_WCHAR" { PUSH_STATE(ENC_WCHAR); }
"ENC_DB" { PUSH_STATE(ENC_DB); }
"ENC_LOCAL" { PUSH_STATE(ENC_LOCAL); }
"map.float" { PUSH_STATE(TSDB_FLOAT); }
"map.bigint" { PUSH_STATE(TSDB_BIGINT); }
[[:alnum:]_]+ { PUSH_STATE(KEY); }
.|\n { FAIL(); }
<KEY>[[:space:]]+ { } <DSN,UID,PWD,SERVER,DB,ENC_CHAR,ENC_WCHAR,ENC_DB,ENC_LOCAL,TSDB_FLOAT,TSDB_BIGINT,KEY>[[:space:]]+ { }
<KEY>[=] { CHG_STATE(EQ); } <DSN,UID,PWD,SERVER,DB,ENC_CHAR,ENC_WCHAR,ENC_DB,ENC_LOCAL,TSDB_FLOAT,TSDB_BIGINT,KEY>[=] { PUSH_STATE(EQ); }
<KEY>.|\n { return -1; } <DSN,UID,PWD,SERVER,DB,ENC_CHAR,ENC_WCHAR,ENC_DB,ENC_LOCAL,TSDB_FLOAT,TSDB_BIGINT,KEY>.|\n { FAIL(); }
<EQ>[[:space:]]+ { } <EQ>[[:space:]]+ { }
<EQ>[^][{}(),;?*=!@/\\\n[:space:]]+ { set_val(); CHG_STATE(VAL); }
<EQ>[{] { CHG_STATE(BRACE1); } <EQ>[{] { CHG_STATE(BRACE1); }
<EQ>.|\n { return -1; } <EQ>[^][{}(),;?*=!@/\\\n[:space:]]+ { set_val(); POP_STATE(); CHG_STATE(VAL); }
<EQ>.|\n { FAIL(); }
<BRACE1>[^{}\n]+ { set_val(); CHG_STATE(BRACE2); } <BRACE1>[^{}\n]+ { set_val(); CHG_STATE(BRACE2); }
<BRACE1>.|\n { return -1; } <BRACE1>.|\n { FAIL(); }
<BRACE2>[[:space:]]+ { } <BRACE2>[[:space:]]+ { }
<BRACE2>[}] { CHG_STATE(VAL); } <BRACE2>[}] { POP_STATE(); CHG_STATE(VAL); }
<BRACE2>.|\n { return -1; } <BRACE2>.|\n { FAIL(); }
<VAL>[;] { POP_STATE(); } <VAL>[;] { POP_STATE(); }
<VAL>.|\n { return -1; } <VAL>.|\n { FAIL(); }
%% %%
static char* get_val_by_key_from_odbc_ini(const char *dsn, const char *key);
static void conn_val_init(conn_val_t *val);
int todbc_parse_conn_string(const char *conn, conn_val_t *val) { int todbc_parse_conn_string(const char *conn, conn_val_t *val) {
yyscan_t arg = {0}; yyscan_t arg = {0};
yylex_init(&arg); yylex_init(&arg);
yyset_debug(0, arg); yyset_debug(0, arg);
yyset_extra(val, arg); yyset_extra(val, arg);
conn_val_init(val);
yy_scan_string(conn, arg); yy_scan_string(conn, arg);
int ret =yylex(arg); int ret =yylex(arg);
yylex_destroy(arg); yylex_destroy(arg);
if (val->key) free(val->key); val->key = NULL; if (ret || !val->dsn) {
if (ret) {
conn_val_reset(val); conn_val_reset(val);
} else {
if (!val->uid) {
val->uid = get_val_by_key_from_odbc_ini(val->dsn, "UID");
}
if (!val->pwd) {
val->pwd = get_val_by_key_from_odbc_ini(val->dsn, "PWD");
}
if (!val->server) {
val->server = get_val_by_key_from_odbc_ini(val->dsn, "Server");
}
} }
return ret ? -1 : 0; return ret ? -1 : 0;
} }
void conn_val_reset(conn_val_t *val) { void conn_val_reset(conn_val_t *val) {
if (val->key) {
free(val->key); val->key = NULL;
}
if (val->dsn) { if (val->dsn) {
free(val->dsn); val->dsn = NULL; free(val->dsn); val->dsn = NULL;
} }
...@@ -155,11 +213,68 @@ void conn_val_reset(conn_val_t *val) { ...@@ -155,11 +213,68 @@ void conn_val_reset(conn_val_t *val) {
if (val->server) { if (val->server) {
free(val->server); val->server = NULL; free(val->server); val->server = NULL;
} }
if (val->svr_enc) { if (val->enc_local) {
free(val->svr_enc); val->svr_enc = NULL; free(val->enc_local); val->enc_local = NULL;
}
if (val->enc_db) {
free(val->enc_db); val->enc_db = NULL;
}
if (val->enc_char) {
free(val->enc_char); val->enc_char = NULL;
} }
if (val->cli_enc) { if (val->enc_wchar) {
free(val->cli_enc); val->cli_enc = NULL; free(val->enc_wchar); val->enc_wchar = NULL;
}
}
static char* get_val_by_key_from_odbc_ini(const char *dsn, const char *key) {
char Val[4096];
Val[0] = '\0';
int n = SQLGetPrivateProfileString(dsn, key, "", Val, sizeof(Val), "odbc.ini");
if (n<=0) return NULL;
if (Val[0]=='\0') return NULL;
return strdup(Val);
}
static int process_map(const char *cfg, map_tsdb_type_t *tsdb_map, int type) {
switch (type) {
case TSDB_FLOAT: {
if (strcmp(cfg, "SQL_DOUBLE")==0) {
tsdb_map->tsdb_float = SQL_DOUBLE;
return 0;
}
} break;
case TSDB_BIGINT: {
if (strcmp(cfg, "SQL_C_SBIGINT")==0) {
tsdb_map->tsdb_bigint = SQL_C_SBIGINT;
return 0;
}
if (strcmp(cfg, "SQL_C_UBIGINT")==0) {
tsdb_map->tsdb_bigint = SQL_C_UBIGINT;
return 0;
}
if (strcmp(cfg, "SQL_CHAR")==0) {
tsdb_map->tsdb_bigint = SQL_CHAR;
return 0;
}
} break;
default: {
} break;
} }
return -1;
}
static void conn_val_init(conn_val_t *val) {
if (!val) return;
val->tsdb_map.tsdb_tinyint = SQL_TINYINT;
val->tsdb_map.tsdb_smallint = SQL_SMALLINT;
val->tsdb_map.tsdb_int = SQL_INTEGER;
val->tsdb_map.tsdb_bigint = SQL_BIGINT;
val->tsdb_map.tsdb_float = SQL_REAL;
val->tsdb_map.tsdb_double = SQL_DOUBLE;
val->tsdb_map.tsdb_bool = SQL_TINYINT;
val->tsdb_map.tsdb_timestamp = SQL_CHAR;
val->tsdb_map.tsdb_binary = SQL_BINARY;
val->tsdb_map.tsdb_nchar = SQL_WCHAR;
} }
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "todbc_string.h"
#include "todbc_log.h"
#include "todbc_tls.h"
#include <stdlib.h>
static int do_calc_bytes(todbc_string_t *str);
todbc_string_t todbc_string_init(const char *enc, const unsigned char *src, const size_t bytes) {
DASSERT(enc);
DASSERT(src);
todbc_string_t s = {0};
todbc_string_t *str = &s;
todbc_iconv_t *cnv = todbc_tls_iconv_get(enc, UTF8_ENC);
if (!cnv) return s;
if (snprintf(str->enc, sizeof(str->enc), "%s", enc)>=sizeof(str->enc)) {
return s;
}
str->buf = src;
str->total_bytes = bytes; // need to recalc
str->bytes = 0;
if (do_calc_bytes(str)) {
str->buf = NULL;
str->total_bytes = 0;
str->bytes = 0;
}
return s;
}
todbc_string_t todbc_string_copy(todbc_string_t *str, const char *enc, unsigned char *dst, const size_t target_bytes) {
todbc_string_t val = {0};
DASSERT(str);
DASSERT(dst);
DASSERT(str->buf);
DASSERT(str->bytes<=INT64_MAX);
DASSERT(str->total_bytes<=INT64_MAX);
if (snprintf(val.enc, sizeof(val.enc), "%s", enc)>=sizeof(val.enc)) {
return val;
}
todbc_iconv_t *icnv = todbc_tls_iconv_get(enc, str->enc);
if (!icnv) return val;
iconv_t cnv = todbc_iconv_get(icnv);
if (cnv==(iconv_t)-1) return val;
val.buf = dst;
val.total_bytes = target_bytes;
const int null_bytes = todbc_iconv_to(icnv).null_size;
if (target_bytes<=null_bytes) return val;
size_t estsize = todbc_iconv_est_bytes(icnv, str->bytes);
if (estsize>INT64_MAX) return val;
// smaller is better!!!
const size_t outblock = (estsize > target_bytes) ? estsize = target_bytes : estsize;
char *inbuf = (char*)str->buf;
size_t inbytes = str->bytes; // not counting null-terminator
char *outbuf = (char*)dst;
size_t outbytes = outblock;
int r = todbc_iconv_raw(icnv, (const unsigned char*)inbuf, &inbytes, (unsigned char*)outbuf, &outbytes);
if (r) {
DASSERT(outbytes > 0);
val.bytes = outblock - outbytes;
val.total_bytes = outblock;
return val;
} else {
val.bytes = outblock - outbytes;
val.total_bytes = val.bytes;
if (inbytes > 0) {
val.total_bytes += 1; // to indicate truncation
}
return val;
}
}
todbc_string_t todbc_copy(const char *from_enc, const unsigned char *src, size_t *inbytes, const char *to_enc, unsigned char *dst, const size_t dlen) {
DASSERT(from_enc);
DASSERT(src);
DASSERT(inbytes);
DASSERT(to_enc);
DASSERT(dst);
DASSERT(dlen <= INT64_MAX);
todbc_string_t s_from = todbc_string_init(from_enc, src, *inbytes);
DASSERT(s_from.buf == src);
return todbc_string_copy(&s_from, to_enc, dst, dlen);
}
todbc_string_t todbc_string_conv_to(todbc_string_t *str, const char *enc, todbc_buf_t *buf) {
DASSERT(str);
DASSERT(str->buf);
DASSERT(str->bytes<=INT64_MAX);
DASSERT(str->total_bytes<=INT64_MAX);
todbc_string_t nul = {0};
todbc_iconv_t *icnv = todbc_tls_iconv_get(enc, str->enc);
if (!icnv) return nul;
size_t estsize = todbc_iconv_est_bytes(icnv, str->bytes);
if (estsize>INT64_MAX) return nul;
char *out = NULL;
if (!buf) out = (char*)todbc_tls_buf_alloc(estsize);
else out = (char*)todbc_buf_alloc(buf, estsize);
if (!out) return nul;
return todbc_string_copy(str, enc, (unsigned char*)out, estsize);
}
static int do_calc_bytes(todbc_string_t *str) {
iconv_t cnv = todbc_tls_iconv(UTF8_ENC, str->enc);
if (cnv == (iconv_t)-1) return -1;
size_t total_bytes = 0;
char buf[1024*16];
char *inbuf = (char*)str->buf;
while (1) {
size_t outblock = sizeof(buf);
size_t inblock = outblock;
size_t remain = (size_t)-1;
if (str->total_bytes <= INT64_MAX) {
remain = str->total_bytes - total_bytes;
if (remain==0) break;
if (inblock > remain) inblock = remain;
}
size_t inbytes = inblock;
char *outbuf = buf;
size_t outbytes = outblock;
size_t n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes);
total_bytes += inblock - inbytes;
int e = 0;
if (n==(size_t)-1) {
e = errno;
if (str->total_bytes<=INT64_MAX) {
D("iconv failed @[%zu], inbytes[%zd->%zd], outbytes[%zd->%zd]: [%d]%s",
(inblock-inbytes), inblock, inbytes, outblock, outbytes, e, strerror(e));
}
DASSERT(e==EILSEQ || e==E2BIG || e==EINVAL);
}
if (n>0 && n<=INT64_MAX) {
D("iconv found non-reversible seq");
}
size_t outlen = outblock - outbytes;
size_t utf8len = strnlen(buf, outlen);
if (utf8len < outlen) {
// null-terminator found
// revert
inbuf -= inblock - inbytes;
total_bytes -= inblock - inbytes;
if (utf8len==0) break;
inbytes = inblock;
outbuf = buf;
outbytes = utf8len;
n = iconv(cnv, &inbuf, &inbytes, &outbuf, &outbytes);
total_bytes += inblock - inbytes;
DASSERT(n==(size_t)-1);
e = errno;
DASSERT(e==E2BIG);
break;
}
if (e==EILSEQ) break;
if (e==EINVAL) {
if (inbytes == remain) {
// this is the last stuff
break;
}
}
}
if (str->total_bytes > INT64_MAX) {
str->total_bytes = total_bytes;
}
str->bytes = total_bytes;
iconv(cnv, NULL, NULL, NULL, NULL);
return 0;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_string_h_
#define _todbc_string_h_
#include <stdint.h>
#include <stddef.h>
#include "todbc_buf.h"
// non-thread-safe
typedef struct todbc_string_s todbc_string_t;
struct todbc_string_s {
// null if init failed because of internal resources shortage
const unsigned char *buf; // null-terminator inclusive
size_t total_bytes; // not counting null-terminator
// <= total_bytes
// truncated if < total_bytes
size_t bytes; // not counting null-terminator
// move here to satisfy todbc_string_t dummy = {0};
char enc[64];
};
// does not copy internally
// bytes: not characters, <0 means bytes unknown
todbc_string_t todbc_string_init(const char *enc, const unsigned char *src, const size_t bytes);
// conv and copy to dst not more than target_bytes (null-terminator-inclusive)
// return'd val->buf == dst, total_bytes<target_bytes, truncated if bytes<total_bytes
todbc_string_t todbc_string_copy(todbc_string_t *str, const char *enc, unsigned char *dst, const size_t target_bytes);
todbc_string_t todbc_copy(const char *from_enc, const unsigned char *src, size_t *inbytes, const char *to_enc, unsigned char *dst, const size_t dlen);
// use todbc_buf_t as mem-allocator
todbc_string_t todbc_string_conv_to(todbc_string_t *str, const char *enc, todbc_buf_t *buf);
#endif // _todbc_string_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "todbc_tls.h"
#include "todbc_buf.h"
#include "todbc_iconv.h"
#include "todbc_log.h"
typedef struct todbc_tls_s todbc_tls_t;
struct todbc_tls_s {
todbc_buf_t *buf;
todbc_iconvset_t *cnvset;
};
static void todbc_tls_free(todbc_tls_t *value);
static pthread_key_t key_this;
static pthread_once_t key_once = PTHREAD_ONCE_INIT;
static int key_err = 0;
static void key_init(void);
static void key_destructor(void *arg);
static void key_init(void) {
key_err = pthread_key_create(&key_this, key_destructor);
if (key_err) {
D("thread local initialization failed: [%d]%s", key_err, strerror(key_err));
}
}
static todbc_tls_t* todbc_tls_create(void);
static todbc_tls_t* key_value(void) {
pthread_once(&key_once, key_init);
if (key_err) return NULL;
int err = 0;
todbc_tls_t *value = pthread_getspecific(key_this);
if (value) return value;
value = todbc_tls_create();
if (!value) return NULL;
do {
err = pthread_setspecific(key_this, value);
if (err) {
D("thread local setup failed: [%d]%s", err, strerror(err));
break;
}
return value;
} while (0);
todbc_tls_free(value);
return NULL;
}
static void key_destructor(void *arg) {
todbc_tls_t *value = (todbc_tls_t*)arg;
todbc_tls_free(value);
}
static todbc_tls_t* todbc_tls_create(void) {
int err = 0;
todbc_tls_t *value = (todbc_tls_t*)calloc(1, sizeof(*value));
if (!value) {
err = errno;
D("thread local creation failed: [%d]%s", err, strerror(err));
return NULL;
}
do {
return value;
} while (0);
todbc_tls_free(value);
return NULL;
}
static void todbc_tls_free(todbc_tls_t *value) {
if (value->cnvset) {
todbc_iconvset_free(value->cnvset);
value->cnvset = NULL;
}
if (value->buf) {
todbc_buf_free(value->buf);
value->buf = NULL;
}
free(value);
}
static todbc_iconvset_t* do_get_iconvset(void);
// iconv
int todbc_legal_chars(const char *enc, const unsigned char *str, todbc_bytes_t *bc) {
todbc_iconvset_t *icnv = do_get_iconvset();
if (!icnv) return -1;
todbc_iconv_t *cnv = todbc_iconvset_get(icnv, UTF16_ENC, enc);
if (!cnv) return -1;
return todbc_iconv_get_legal_chars(cnv, str, bc);
}
todbc_iconv_t* todbc_tls_iconv_get(const char *to_enc, const char *from_enc) {
todbc_iconvset_t *cnvset = do_get_iconvset();
if (!cnvset) return NULL;
todbc_iconv_t *cnv = todbc_iconvset_get(cnvset, to_enc, from_enc);
return cnv;
}
iconv_t todbc_tls_iconv(const char *to_enc, const char *from_enc) {
todbc_iconv_t *icnv = todbc_tls_iconv_get(to_enc, from_enc);
if (!icnv) return (iconv_t)-1;
return todbc_iconv_get(icnv);
}
todbc_enc_t todbc_tls_iconv_enc(const char *enc) {
do {
todbc_iconvset_t *cnvset = do_get_iconvset();
if (!cnvset) break;
return todbc_iconvset_enc(cnvset, enc);
} while (0);
todbc_enc_t v = {0};
v.char_size = -1;
v.null_size = -1;
return v;
}
todbc_string_t todbc_tls_conv(todbc_buf_t *buf, const char *enc_to, const char *enc_from, const unsigned char *src, size_t *slen) {
todbc_iconv_t *cnv = todbc_tls_iconv_get(enc_to, enc_from);
if (!cnv) {
todbc_string_t nul = {0};
return nul;
}
return todbc_iconv_conv2(cnv, buf, src, slen);
}
todbc_string_t todbc_tls_write(const char *enc_to, const char *enc_from,
const unsigned char *src, size_t *slen, unsigned char *dst, size_t dlen)
{
todbc_iconv_t *cnv = todbc_tls_iconv_get(enc_to, enc_from);
if (!cnv) {
todbc_string_t nul = {0};
return nul;
}
todbc_string_t s = {0};
s.buf = dst;
s.total_bytes = dlen;
size_t inbytes = *slen;
size_t outbytes = dlen;
todbc_iconv_raw(cnv, src, &inbytes, dst, &outbytes);
s.bytes = dlen - outbytes;
s.total_bytes = s.bytes;
if (inbytes) {
s.total_bytes += 1;
}
*slen = inbytes;
return s;
}
char* todbc_tls_strndup(const char *src, size_t n) {
todbc_buf_t *buf = todbc_tls_buf();
if (!buf) return NULL;
n = strnlen(src, n);
char *d = todbc_buf_alloc(buf, (n+1));
if (!d) return NULL;
snprintf(d, n+1, "%s", src);
return d;
}
static todbc_iconvset_t* do_get_iconvset(void) {
todbc_tls_t *tls = key_value();
if (!tls) return NULL;
if (!tls->cnvset) {
tls->cnvset = todbc_iconvset_create();
}
return tls->cnvset;
}
// tls_buf
void* todbc_tls_buf_alloc(size_t size) {
todbc_tls_t *tls = key_value();
if (!tls) return NULL;
if (!tls->buf) {
tls->buf = todbc_buf_create();
if (!tls->buf) return NULL;
}
return todbc_buf_alloc(tls->buf, size);
}
void* todbc_tls_buf_calloc(size_t count, size_t size) {
todbc_tls_t *tls = key_value();
if (!tls) return NULL;
if (!tls->buf) {
tls->buf = todbc_buf_create();
if (!tls->buf) return NULL;
}
return todbc_buf_calloc(tls->buf, count, size);
}
void* todbc_tls_buf_realloc(void *ptr, size_t size) {
todbc_tls_t *tls = key_value();
if (!tls) return NULL;
if (!tls->buf) {
tls->buf = todbc_buf_create();
if (!tls->buf) return NULL;
}
return todbc_buf_realloc(tls->buf, ptr, size);
}
void todbc_tls_buf_reclaim(void) {
todbc_tls_t *tls = key_value();
if (!tls) return;
if (!tls->buf) return;
todbc_buf_reclaim(tls->buf);
}
todbc_buf_t* todbc_tls_buf(void) {
todbc_tls_t *tls = key_value();
if (!tls) return NULL;
if (!tls->buf) {
tls->buf = todbc_buf_create();
}
return tls->buf;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_tls_h_
#define _todbc_tls_h_
// !!! functions exported in this header file are all non-thread-safe !!!
#include "taos.h"
#include "todbc_buf.h"
#include "todbc_iconv.h"
#include "todbc_string.h"
// thread local buffers
// non-thread-safe
// returned-buf are all thread-local-accessible until todbc_tls_buf_reclaim
void* todbc_tls_buf_alloc(size_t size);
void* todbc_tls_buf_calloc(size_t count, size_t size);
void* todbc_tls_buf_realloc(void *ptr, size_t size);
// reclaim all above thread-local-buf(s)
void todbc_tls_buf_reclaim(void);
// return local-thread-buf
todbc_buf_t* todbc_tls_buf(void);
// thread local iconv
// non-thread-safe
todbc_iconv_t* todbc_tls_iconv_get(const char *to_enc, const char *from_enc);
iconv_t todbc_tls_iconv(const char *to_enc, const char *from_enc);
todbc_enc_t todbc_tls_iconv_enc(const char *enc);
// non-thread-safe
int todbc_legal_chars(const char *enc, const unsigned char *str, todbc_bytes_t *bc);
// at return, *slen stores the remaining #
todbc_string_t todbc_tls_conv(todbc_buf_t *buf, const char *enc_to, const char *enc_from, const unsigned char *src, size_t *slen);
todbc_string_t todbc_tls_write(const char *enc_to, const char *enc_from, const unsigned char *src, size_t *slen, unsigned char *dst, size_t dlen);
char* todbc_tls_strndup(const char *src, size_t n);
#endif // _todbc_tls_h_
...@@ -15,86 +15,576 @@ ...@@ -15,86 +15,576 @@
#include "todbc_util.h" #include "todbc_util.h"
#include "todbc_log.h" #include "todbc_log.h"
#include <iconv.h> #include <iconv.h>
#include <sqlext.h> #include <sqlext.h>
#define SQL_CASE(type) case type: return #type
const char* sql_sql_type(int type) { const char* sql_sql_type(int type) {
switch (type) { switch (type) {
case SQL_BIT: return "SQL_BIT"; SQL_CASE(SQL_BIT);
case SQL_TINYINT: return "SQL_TINYINT"; SQL_CASE(SQL_TINYINT);
case SQL_SMALLINT: return "SQL_SMALLINT"; SQL_CASE(SQL_SMALLINT);
case SQL_INTEGER: return "SQL_INTEGER"; SQL_CASE(SQL_INTEGER);
case SQL_BIGINT: return "SQL_BIGINT"; SQL_CASE(SQL_BIGINT);
case SQL_FLOAT: return "SQL_FLOAT"; SQL_CASE(SQL_FLOAT);
case SQL_DOUBLE: return "SQL_DOUBLE"; SQL_CASE(SQL_DOUBLE);
case SQL_DECIMAL: return "SQL_DECIMAL"; SQL_CASE(SQL_DECIMAL);
case SQL_NUMERIC: return "SQL_NUMERIC"; SQL_CASE(SQL_NUMERIC);
case SQL_REAL: return "SQL_REAL"; SQL_CASE(SQL_REAL);
case SQL_CHAR: return "SQL_CHAR"; SQL_CASE(SQL_CHAR);
case SQL_VARCHAR: return "SQL_VARCHAR"; SQL_CASE(SQL_VARCHAR);
case SQL_LONGVARCHAR: return "SQL_LONGVARCHAR"; SQL_CASE(SQL_LONGVARCHAR);
case SQL_WCHAR: return "SQL_WCHAR"; SQL_CASE(SQL_WCHAR);
case SQL_WVARCHAR: return "SQL_WVARCHAR"; SQL_CASE(SQL_WVARCHAR);
case SQL_WLONGVARCHAR: return "SQL_WLONGVARCHAR"; SQL_CASE(SQL_WLONGVARCHAR);
case SQL_BINARY: return "SQL_BINARY"; SQL_CASE(SQL_BINARY);
case SQL_VARBINARY: return "SQL_VARBINARY"; SQL_CASE(SQL_VARBINARY);
case SQL_LONGVARBINARY: return "SQL_LONGVARBINARY"; SQL_CASE(SQL_LONGVARBINARY);
case SQL_DATE: return "SQL_DATE"; SQL_CASE(SQL_DATE);
case SQL_TIME: return "SQL_TIME"; SQL_CASE(SQL_TIME);
case SQL_TIMESTAMP: return "SQL_TIMESTAMP"; SQL_CASE(SQL_TIMESTAMP);
case SQL_TYPE_DATE: return "SQL_TYPE_DATE"; SQL_CASE(SQL_TYPE_DATE);
case SQL_TYPE_TIME: return "SQL_TYPE_TIME"; SQL_CASE(SQL_TYPE_TIME);
case SQL_TYPE_TIMESTAMP: return "SQL_TYPE_TIMESTAMP"; SQL_CASE(SQL_TYPE_TIMESTAMP);
case SQL_INTERVAL_MONTH: return "SQL_INTERVAL_MONTH"; SQL_CASE(SQL_INTERVAL_MONTH);
case SQL_INTERVAL_YEAR: return "SQL_INTERVAL_YEAR"; SQL_CASE(SQL_INTERVAL_YEAR);
case SQL_INTERVAL_YEAR_TO_MONTH: return "SQL_INTERVAL_YEAR_TO_MONTH"; SQL_CASE(SQL_INTERVAL_YEAR_TO_MONTH);
case SQL_INTERVAL_DAY: return "SQL_INTERVAL_DAY"; SQL_CASE(SQL_INTERVAL_DAY);
case SQL_INTERVAL_HOUR: return "SQL_INTERVAL_HOUR"; SQL_CASE(SQL_INTERVAL_HOUR);
case SQL_INTERVAL_MINUTE: return "SQL_INTERVAL_MINUTE"; SQL_CASE(SQL_INTERVAL_MINUTE);
case SQL_INTERVAL_SECOND: return "SQL_INTERVAL_SECOND"; SQL_CASE(SQL_INTERVAL_SECOND);
case SQL_INTERVAL_DAY_TO_HOUR: return "SQL_INTERVAL_DAY_TO_HOUR"; SQL_CASE(SQL_INTERVAL_DAY_TO_HOUR);
case SQL_INTERVAL_DAY_TO_MINUTE: return "SQL_INTERVAL_DAY_TO_MINUTE"; SQL_CASE(SQL_INTERVAL_DAY_TO_MINUTE);
case SQL_INTERVAL_DAY_TO_SECOND: return "SQL_INTERVAL_DAY_TO_SECOND"; SQL_CASE(SQL_INTERVAL_DAY_TO_SECOND);
case SQL_INTERVAL_HOUR_TO_MINUTE: return "SQL_INTERVAL_HOUR_TO_MINUTE"; SQL_CASE(SQL_INTERVAL_HOUR_TO_MINUTE);
case SQL_INTERVAL_HOUR_TO_SECOND: return "SQL_INTERVAL_HOUR_TO_SECOND"; SQL_CASE(SQL_INTERVAL_HOUR_TO_SECOND);
case SQL_INTERVAL_MINUTE_TO_SECOND: return "SQL_INTERVAL_MINUTE_TO_SECOND"; SQL_CASE(SQL_INTERVAL_MINUTE_TO_SECOND);
case SQL_GUID: return "SQL_GUID"; SQL_CASE(SQL_GUID);
SQL_CASE(SQL_ALL_TYPES);
default: return "UNKNOWN"; default: return "UNKNOWN";
} }
} }
const char* sql_c_type(int type) { const char* sql_c_type(int type) {
switch (type) { switch (type) {
case SQL_C_CHAR: return "SQL_C_CHAR"; SQL_CASE(SQL_C_CHAR);
case SQL_C_WCHAR: return "SQL_C_WCHAR"; SQL_CASE(SQL_C_WCHAR);
case SQL_C_SHORT: return "SQL_C_SHORT"; SQL_CASE(SQL_C_SHORT);
case SQL_C_SSHORT: return "SQL_C_SSHORT"; SQL_CASE(SQL_C_SSHORT);
case SQL_C_USHORT: return "SQL_C_USHORT"; SQL_CASE(SQL_C_USHORT);
case SQL_C_LONG: return "SQL_C_LONG"; SQL_CASE(SQL_C_LONG);
case SQL_C_SLONG: return "SQL_C_SLONG"; SQL_CASE(SQL_C_SLONG);
case SQL_C_ULONG: return "SQL_C_ULONG"; SQL_CASE(SQL_C_ULONG);
case SQL_C_FLOAT: return "SQL_C_FLOAT"; SQL_CASE(SQL_C_FLOAT);
case SQL_C_DOUBLE: return "SQL_C_DOUBLE"; SQL_CASE(SQL_C_DOUBLE);
case SQL_C_BIT: return "SQL_C_BIT"; SQL_CASE(SQL_C_BIT);
case SQL_C_TINYINT: return "SQL_C_TINYINT"; SQL_CASE(SQL_C_TINYINT);
case SQL_C_STINYINT: return "SQL_C_STINYINT"; SQL_CASE(SQL_C_STINYINT);
case SQL_C_UTINYINT: return "SQL_C_UTINYINT"; SQL_CASE(SQL_C_UTINYINT);
case SQL_C_SBIGINT: return "SQL_C_SBIGINT"; SQL_CASE(SQL_C_SBIGINT);
case SQL_C_UBIGINT: return "SQL_C_UBIGINT"; SQL_CASE(SQL_C_UBIGINT);
case SQL_C_BINARY: return "SQL_C_BINARY"; SQL_CASE(SQL_C_BINARY);
case SQL_C_DATE: return "SQL_C_DATE"; SQL_CASE(SQL_C_DATE);
case SQL_C_TIME: return "SQL_C_TIME"; SQL_CASE(SQL_C_TIME);
case SQL_C_TIMESTAMP: return "SQL_C_TIMESTAMP"; SQL_CASE(SQL_C_TIMESTAMP);
case SQL_C_TYPE_DATE: return "SQL_C_TYPE_DATE"; SQL_CASE(SQL_C_TYPE_DATE);
case SQL_C_TYPE_TIME: return "SQL_C_TYPE_TIME"; SQL_CASE(SQL_C_TYPE_TIME);
case SQL_C_TYPE_TIMESTAMP: return "SQL_C_TYPE_TIMESTAMP"; SQL_CASE(SQL_C_TYPE_TIMESTAMP);
case SQL_C_NUMERIC: return "SQL_C_NUMERIC"; SQL_CASE(SQL_C_NUMERIC);
case SQL_C_GUID: return "SQL_C_GUID"; SQL_CASE(SQL_C_GUID);
default: return "UNKNOWN";
}
}
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetdiagfield-function?view=sql-server-ver15
const char* sql_diag_identifier(int type) {
switch (type) {
// header fields
SQL_CASE(SQL_DIAG_CURSOR_ROW_COUNT);
SQL_CASE(SQL_DIAG_DYNAMIC_FUNCTION);
SQL_CASE(SQL_DIAG_DYNAMIC_FUNCTION_CODE);
SQL_CASE(SQL_DIAG_NUMBER);
SQL_CASE(SQL_DIAG_RETURNCODE);
SQL_CASE(SQL_DIAG_ROW_COUNT);
// record fields
SQL_CASE(SQL_DIAG_CLASS_ORIGIN);
SQL_CASE(SQL_DIAG_COLUMN_NUMBER);
SQL_CASE(SQL_DIAG_CONNECTION_NAME);
SQL_CASE(SQL_DIAG_MESSAGE_TEXT);
SQL_CASE(SQL_DIAG_NATIVE);
SQL_CASE(SQL_DIAG_ROW_NUMBER);
SQL_CASE(SQL_DIAG_SERVER_NAME);
SQL_CASE(SQL_DIAG_SQLSTATE);
SQL_CASE(SQL_DIAG_SUBCLASS_ORIGIN);
default: return "UNKNOWN";
}
}
const char* sql_handle_type(int type) {
switch(type) {
SQL_CASE(SQL_HANDLE_ENV);
SQL_CASE(SQL_HANDLE_DBC);
SQL_CASE(SQL_HANDLE_STMT);
SQL_CASE(SQL_HANDLE_DESC);
// SQL_CASE(SQL_HANDLE_DBC_INFO_TOKEN);
default: return "UNKNOWN";
}
}
const char* sql_env_attr_type(int type) {
switch(type) {
SQL_CASE(SQL_ATTR_OUTPUT_NTS);
SQL_CASE(SQL_ATTR_ODBC_VERSION);
SQL_CASE(SQL_ATTR_CONNECTION_POOLING);
SQL_CASE(SQL_ATTR_CP_MATCH);
default: return "UNKNOWN";
}
}
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetconnectattr-function?view=sql-server-ver15
const char* sql_conn_attr_type(int type) {
switch(type) {
SQL_CASE(SQL_ATTR_ACCESS_MODE);
// ODBC 3.8
// SQL_CASE(SQL_ATTR_ASYNC_DBC_EVENT);
SQL_CASE(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE);
// ODBC 3.8
// SQL_CASE(SQL_ATTR_ASYNC_DBC_PCALLBACK);
// ODBC 3.8
// SQL_CASE(SQL_ATTR_ASYNC_DBC_PCONTEXT);
SQL_CASE(SQL_ATTR_ASYNC_ENABLE);
SQL_CASE(SQL_ATTR_AUTO_IPD);
SQL_CASE(SQL_ATTR_AUTOCOMMIT);
SQL_CASE(SQL_ATTR_CONNECTION_DEAD);
SQL_CASE(SQL_ATTR_CONNECTION_TIMEOUT);
SQL_CASE(SQL_ATTR_CURRENT_CATALOG);
// ODBC 3.8
// SQL_CASE(SQL_ATTR_DBC_INFO_TOKEN);
SQL_CASE(SQL_ATTR_ENLIST_IN_DTC);
SQL_CASE(SQL_ATTR_LOGIN_TIMEOUT);
SQL_CASE(SQL_ATTR_METADATA_ID);
SQL_CASE(SQL_ATTR_ODBC_CURSORS);
SQL_CASE(SQL_ATTR_PACKET_SIZE);
SQL_CASE(SQL_ATTR_QUIET_MODE);
SQL_CASE(SQL_ATTR_TRACE);
SQL_CASE(SQL_ATTR_TRACEFILE);
SQL_CASE(SQL_ATTR_TRANSLATE_LIB);
SQL_CASE(SQL_ATTR_TRANSLATE_OPTION);
SQL_CASE(SQL_ATTR_TXN_ISOLATION);
default: return "UNKNOWN";
}
}
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetinfo-function?view=sql-server-ver15
const char* sql_info_type(int type) {
switch(type) {
SQL_CASE(SQL_ACTIVE_ENVIRONMENTS);
SQL_CASE(SQL_ASYNC_DBC_FUNCTIONS);
SQL_CASE(SQL_ASYNC_MODE);
SQL_CASE(SQL_ASYNC_NOTIFICATION);
SQL_CASE(SQL_BATCH_ROW_COUNT);
SQL_CASE(SQL_BATCH_SUPPORT);
SQL_CASE(SQL_DATA_SOURCE_NAME);
SQL_CASE(SQL_DRIVER_AWARE_POOLING_SUPPORTED);
SQL_CASE(SQL_DRIVER_HDBC);
SQL_CASE(SQL_DRIVER_HDESC);
SQL_CASE(SQL_DRIVER_HENV);
SQL_CASE(SQL_DRIVER_HLIB);
SQL_CASE(SQL_DRIVER_HSTMT);
SQL_CASE(SQL_DRIVER_NAME);
SQL_CASE(SQL_DRIVER_ODBC_VER);
SQL_CASE(SQL_DRIVER_VER);
SQL_CASE(SQL_DYNAMIC_CURSOR_ATTRIBUTES1);
SQL_CASE(SQL_DYNAMIC_CURSOR_ATTRIBUTES2);
SQL_CASE(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1);
SQL_CASE(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2);
SQL_CASE(SQL_FILE_USAGE);
SQL_CASE(SQL_GETDATA_EXTENSIONS);
SQL_CASE(SQL_INFO_SCHEMA_VIEWS);
SQL_CASE(SQL_KEYSET_CURSOR_ATTRIBUTES1);
SQL_CASE(SQL_KEYSET_CURSOR_ATTRIBUTES2);
SQL_CASE(SQL_MAX_ASYNC_CONCURRENT_STATEMENTS);
SQL_CASE(SQL_MAX_CONCURRENT_ACTIVITIES);
SQL_CASE(SQL_MAX_DRIVER_CONNECTIONS);
SQL_CASE(SQL_ODBC_INTERFACE_CONFORMANCE);
// SQL_CASE(SQL_ODBC_STANDARD_CLI_CONFORMANCE);
SQL_CASE(SQL_ODBC_VER);
SQL_CASE(SQL_PARAM_ARRAY_ROW_COUNTS);
SQL_CASE(SQL_PARAM_ARRAY_SELECTS);
SQL_CASE(SQL_ROW_UPDATES);
SQL_CASE(SQL_SEARCH_PATTERN_ESCAPE);
SQL_CASE(SQL_SERVER_NAME);
SQL_CASE(SQL_STATIC_CURSOR_ATTRIBUTES1);
SQL_CASE(SQL_STATIC_CURSOR_ATTRIBUTES2);
SQL_CASE(SQL_DATABASE_NAME);
SQL_CASE(SQL_DBMS_NAME);
SQL_CASE(SQL_DBMS_VER);
SQL_CASE(SQL_ACCESSIBLE_PROCEDURES);
SQL_CASE(SQL_ACCESSIBLE_TABLES);
SQL_CASE(SQL_BOOKMARK_PERSISTENCE);
SQL_CASE(SQL_CATALOG_TERM);
SQL_CASE(SQL_COLLATION_SEQ);
SQL_CASE(SQL_CONCAT_NULL_BEHAVIOR);
SQL_CASE(SQL_CURSOR_COMMIT_BEHAVIOR);
SQL_CASE(SQL_CURSOR_ROLLBACK_BEHAVIOR);
SQL_CASE(SQL_CURSOR_SENSITIVITY);
SQL_CASE(SQL_DATA_SOURCE_READ_ONLY);
SQL_CASE(SQL_DEFAULT_TXN_ISOLATION);
SQL_CASE(SQL_DESCRIBE_PARAMETER);
SQL_CASE(SQL_MULT_RESULT_SETS);
SQL_CASE(SQL_MULTIPLE_ACTIVE_TXN);
SQL_CASE(SQL_NEED_LONG_DATA_LEN);
SQL_CASE(SQL_NULL_COLLATION);
SQL_CASE(SQL_PROCEDURE_TERM);
SQL_CASE(SQL_SCHEMA_TERM);
SQL_CASE(SQL_SCROLL_OPTIONS);
SQL_CASE(SQL_TABLE_TERM);
SQL_CASE(SQL_TXN_CAPABLE);
SQL_CASE(SQL_TXN_ISOLATION_OPTION);
SQL_CASE(SQL_USER_NAME);
SQL_CASE(SQL_AGGREGATE_FUNCTIONS);
SQL_CASE(SQL_ALTER_DOMAIN);
// SQL_CASE(SQL_ALTER_SCHEMA);
SQL_CASE(SQL_ALTER_TABLE);
// SQL_CASE(SQL_ANSI_SQL_DATETIME_LITERALS);
SQL_CASE(SQL_CATALOG_LOCATION);
SQL_CASE(SQL_CATALOG_NAME);
SQL_CASE(SQL_CATALOG_NAME_SEPARATOR);
SQL_CASE(SQL_CATALOG_USAGE);
SQL_CASE(SQL_COLUMN_ALIAS);
SQL_CASE(SQL_CORRELATION_NAME);
SQL_CASE(SQL_CREATE_ASSERTION);
SQL_CASE(SQL_CREATE_CHARACTER_SET);
SQL_CASE(SQL_CREATE_COLLATION);
SQL_CASE(SQL_CREATE_DOMAIN);
SQL_CASE(SQL_CREATE_SCHEMA);
SQL_CASE(SQL_CREATE_TABLE);
SQL_CASE(SQL_CREATE_TRANSLATION);
SQL_CASE(SQL_DDL_INDEX);
SQL_CASE(SQL_DROP_ASSERTION);
SQL_CASE(SQL_DROP_CHARACTER_SET);
SQL_CASE(SQL_DROP_COLLATION);
SQL_CASE(SQL_DROP_DOMAIN);
SQL_CASE(SQL_DROP_SCHEMA);
SQL_CASE(SQL_DROP_TABLE);
SQL_CASE(SQL_DROP_TRANSLATION);
SQL_CASE(SQL_DROP_VIEW);
SQL_CASE(SQL_EXPRESSIONS_IN_ORDERBY);
SQL_CASE(SQL_GROUP_BY);
SQL_CASE(SQL_IDENTIFIER_CASE);
SQL_CASE(SQL_IDENTIFIER_QUOTE_CHAR);
SQL_CASE(SQL_INDEX_KEYWORDS);
SQL_CASE(SQL_INSERT_STATEMENT);
SQL_CASE(SQL_INTEGRITY);
SQL_CASE(SQL_KEYWORDS);
SQL_CASE(SQL_LIKE_ESCAPE_CLAUSE);
SQL_CASE(SQL_NON_NULLABLE_COLUMNS);
SQL_CASE(SQL_OJ_CAPABILITIES);
SQL_CASE(SQL_ORDER_BY_COLUMNS_IN_SELECT);
SQL_CASE(SQL_OUTER_JOINS);
SQL_CASE(SQL_PROCEDURES);
SQL_CASE(SQL_QUOTED_IDENTIFIER_CASE);
SQL_CASE(SQL_SCHEMA_USAGE);
SQL_CASE(SQL_SPECIAL_CHARACTERS);
SQL_CASE(SQL_SQL_CONFORMANCE);
SQL_CASE(SQL_SUBQUERIES);
SQL_CASE(SQL_UNION);
SQL_CASE(SQL_MAX_BINARY_LITERAL_LEN);
SQL_CASE(SQL_MAX_CATALOG_NAME_LEN);
SQL_CASE(SQL_MAX_CHAR_LITERAL_LEN);
SQL_CASE(SQL_MAX_COLUMN_NAME_LEN);
SQL_CASE(SQL_MAX_COLUMNS_IN_GROUP_BY);
SQL_CASE(SQL_MAX_COLUMNS_IN_INDEX);
SQL_CASE(SQL_MAX_COLUMNS_IN_ORDER_BY);
SQL_CASE(SQL_MAX_COLUMNS_IN_SELECT);
SQL_CASE(SQL_MAX_COLUMNS_IN_TABLE);
SQL_CASE(SQL_MAX_CURSOR_NAME_LEN);
SQL_CASE(SQL_MAX_IDENTIFIER_LEN);
SQL_CASE(SQL_MAX_INDEX_SIZE);
SQL_CASE(SQL_MAX_PROCEDURE_NAME_LEN);
SQL_CASE(SQL_MAX_ROW_SIZE);
SQL_CASE(SQL_MAX_ROW_SIZE_INCLUDES_LONG);
SQL_CASE(SQL_MAX_SCHEMA_NAME_LEN);
SQL_CASE(SQL_MAX_STATEMENT_LEN);
SQL_CASE(SQL_MAX_TABLE_NAME_LEN);
SQL_CASE(SQL_MAX_TABLES_IN_SELECT);
SQL_CASE(SQL_MAX_USER_NAME_LEN);
SQL_CASE(SQL_CONVERT_FUNCTIONS);
SQL_CASE(SQL_NUMERIC_FUNCTIONS);
SQL_CASE(SQL_STRING_FUNCTIONS);
SQL_CASE(SQL_SYSTEM_FUNCTIONS);
SQL_CASE(SQL_TIMEDATE_ADD_INTERVALS);
SQL_CASE(SQL_TIMEDATE_DIFF_INTERVALS);
SQL_CASE(SQL_TIMEDATE_FUNCTIONS);
SQL_CASE(SQL_CONVERT_BIGINT);
SQL_CASE(SQL_CONVERT_BINARY);
SQL_CASE(SQL_CONVERT_BIT);
SQL_CASE(SQL_CONVERT_CHAR);
SQL_CASE(SQL_CONVERT_DATE);
SQL_CASE(SQL_CONVERT_DECIMAL);
SQL_CASE(SQL_CONVERT_DOUBLE);
SQL_CASE(SQL_CONVERT_FLOAT);
SQL_CASE(SQL_CONVERT_INTEGER);
SQL_CASE(SQL_CONVERT_INTERVAL_DAY_TIME);
SQL_CASE(SQL_CONVERT_INTERVAL_YEAR_MONTH);
SQL_CASE(SQL_CONVERT_LONGVARBINARY);
SQL_CASE(SQL_CONVERT_LONGVARCHAR);
SQL_CASE(SQL_CONVERT_NUMERIC);
SQL_CASE(SQL_CONVERT_REAL);
SQL_CASE(SQL_CONVERT_SMALLINT);
SQL_CASE(SQL_CONVERT_TIME);
SQL_CASE(SQL_CONVERT_TIMESTAMP);
SQL_CASE(SQL_CONVERT_TINYINT);
SQL_CASE(SQL_CONVERT_VARBINARY);
SQL_CASE(SQL_CONVERT_VARCHAR);
SQL_CASE(SQL_DM_VER);
SQL_CASE(SQL_XOPEN_CLI_YEAR);
SQL_CASE(SQL_DTC_TRANSITION_COST);
default: return "UNKNOWN";
}
}
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlcolattribute-function?view=sql-server-ver15
const char* sql_field_identifier(int type) {
switch (type) {
SQL_CASE(SQL_DESC_AUTO_UNIQUE_VALUE);
SQL_CASE(SQL_DESC_BASE_COLUMN_NAME);
SQL_CASE(SQL_DESC_BASE_TABLE_NAME);
SQL_CASE(SQL_DESC_CASE_SENSITIVE);
SQL_CASE(SQL_DESC_CATALOG_NAME);
SQL_CASE(SQL_DESC_CONCISE_TYPE);
SQL_CASE(SQL_DESC_COUNT);
SQL_CASE(SQL_DESC_DISPLAY_SIZE);
SQL_CASE(SQL_DESC_FIXED_PREC_SCALE);
SQL_CASE(SQL_DESC_LABEL);
SQL_CASE(SQL_DESC_LENGTH);
SQL_CASE(SQL_DESC_LITERAL_PREFIX);
SQL_CASE(SQL_DESC_LITERAL_SUFFIX);
SQL_CASE(SQL_DESC_LOCAL_TYPE_NAME);
SQL_CASE(SQL_DESC_NAME);
SQL_CASE(SQL_DESC_NULLABLE);
SQL_CASE(SQL_DESC_NUM_PREC_RADIX);
SQL_CASE(SQL_DESC_OCTET_LENGTH);
SQL_CASE(SQL_DESC_PRECISION);
SQL_CASE(SQL_DESC_SCALE);
SQL_CASE(SQL_DESC_SCHEMA_NAME);
SQL_CASE(SQL_DESC_SEARCHABLE);
SQL_CASE(SQL_DESC_TABLE_NAME);
SQL_CASE(SQL_DESC_TYPE);
SQL_CASE(SQL_DESC_TYPE_NAME);
SQL_CASE(SQL_DESC_UNNAMED);
SQL_CASE(SQL_DESC_UNSIGNED);
SQL_CASE(SQL_DESC_UPDATABLE);
default: return "UNKNOWN"; default: return "UNKNOWN";
} }
} }
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function?view=sql-server-ver15
const char* sql_input_output_type(int type) {
switch (type) {
SQL_CASE(SQL_PARAM_INPUT);
SQL_CASE(SQL_PARAM_OUTPUT);
SQL_CASE(SQL_PARAM_OUTPUT_STREAM);
SQL_CASE(SQL_PARAM_INPUT_OUTPUT);
SQL_CASE(SQL_PARAM_INPUT_OUTPUT_STREAM);
default: return "UNKNOWN";
}
}
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function?view=sql-server-ver15
const char* sql_stmt_attr_type(int type) {
switch (type) {
SQL_CASE(SQL_ATTR_APP_PARAM_DESC);
SQL_CASE(SQL_ATTR_APP_ROW_DESC);
SQL_CASE(SQL_ATTR_ASYNC_ENABLE);
SQL_CASE(SQL_ATTR_ASYNC_STMT_EVENT);
// ODBC 3.8
// SQL_CASE(SQL_ATTR_ASYNC_STMT_PCALLBACK);
// ODBC 3.8
// SQL_CASE(SQL_ATTR_ASYNC_STMT_PCONTEXT);
SQL_CASE(SQL_ATTR_CONCURRENCY);
SQL_CASE(SQL_ATTR_CURSOR_SCROLLABLE);
SQL_CASE(SQL_ATTR_CURSOR_SENSITIVITY);
SQL_CASE(SQL_ATTR_CURSOR_TYPE);
SQL_CASE(SQL_ATTR_ENABLE_AUTO_IPD);
SQL_CASE(SQL_ATTR_FETCH_BOOKMARK_PTR);
SQL_CASE(SQL_ATTR_IMP_PARAM_DESC);
SQL_CASE(SQL_ATTR_IMP_ROW_DESC);
SQL_CASE(SQL_ATTR_KEYSET_SIZE);
SQL_CASE(SQL_ATTR_MAX_LENGTH);
SQL_CASE(SQL_ATTR_MAX_ROWS);
SQL_CASE(SQL_ATTR_METADATA_ID);
SQL_CASE(SQL_ATTR_NOSCAN);
SQL_CASE(SQL_ATTR_PARAM_BIND_OFFSET_PTR);
SQL_CASE(SQL_ATTR_PARAM_BIND_TYPE);
SQL_CASE(SQL_ATTR_PARAM_OPERATION_PTR);
SQL_CASE(SQL_ATTR_PARAM_STATUS_PTR);
SQL_CASE(SQL_ATTR_PARAMS_PROCESSED_PTR);
SQL_CASE(SQL_ATTR_PARAMSET_SIZE);
SQL_CASE(SQL_ATTR_QUERY_TIMEOUT);
SQL_CASE(SQL_ATTR_RETRIEVE_DATA);
SQL_CASE(SQL_ATTR_ROW_ARRAY_SIZE);
SQL_CASE(SQL_ATTR_ROW_BIND_OFFSET_PTR);
SQL_CASE(SQL_ATTR_ROW_BIND_TYPE);
SQL_CASE(SQL_ATTR_ROW_NUMBER);
SQL_CASE(SQL_ATTR_ROW_OPERATION_PTR);
SQL_CASE(SQL_ATTR_ROW_STATUS_PTR);
SQL_CASE(SQL_ATTR_ROWS_FETCHED_PTR);
SQL_CASE(SQL_ATTR_SIMULATE_CURSOR);
SQL_CASE(SQL_ATTR_USE_BOOKMARKS);
default: return "UNKNOWN";
}
}
const char* sql_function_type(int type) {
switch (type) {
//
SQL_CASE(SQL_API_ALL_FUNCTIONS);
SQL_CASE(SQL_API_ODBC3_ALL_FUNCTIONS);
// ISO 92 standards-compliance
SQL_CASE(SQL_API_SQLALLOCHANDLE);
SQL_CASE(SQL_API_SQLGETDESCFIELD);
SQL_CASE(SQL_API_SQLBINDCOL);
SQL_CASE(SQL_API_SQLGETDESCREC);
SQL_CASE(SQL_API_SQLCANCEL);
SQL_CASE(SQL_API_SQLGETDIAGFIELD);
SQL_CASE(SQL_API_SQLCLOSECURSOR);
SQL_CASE(SQL_API_SQLGETDIAGREC);
SQL_CASE(SQL_API_SQLCOLATTRIBUTE);
SQL_CASE(SQL_API_SQLGETENVATTR);
SQL_CASE(SQL_API_SQLCONNECT);
SQL_CASE(SQL_API_SQLGETFUNCTIONS);
SQL_CASE(SQL_API_SQLCOPYDESC);
SQL_CASE(SQL_API_SQLGETINFO);
SQL_CASE(SQL_API_SQLDATASOURCES);
SQL_CASE(SQL_API_SQLGETSTMTATTR);
SQL_CASE(SQL_API_SQLDESCRIBECOL);
SQL_CASE(SQL_API_SQLGETTYPEINFO);
SQL_CASE(SQL_API_SQLDISCONNECT);
SQL_CASE(SQL_API_SQLNUMRESULTCOLS);
SQL_CASE(SQL_API_SQLDRIVERS);
SQL_CASE(SQL_API_SQLPARAMDATA);
SQL_CASE(SQL_API_SQLENDTRAN);
SQL_CASE(SQL_API_SQLPREPARE);
SQL_CASE(SQL_API_SQLEXECDIRECT);
SQL_CASE(SQL_API_SQLPUTDATA);
SQL_CASE(SQL_API_SQLEXECUTE);
SQL_CASE(SQL_API_SQLROWCOUNT);
SQL_CASE(SQL_API_SQLFETCH);
SQL_CASE(SQL_API_SQLSETCONNECTATTR);
SQL_CASE(SQL_API_SQLFETCHSCROLL);
SQL_CASE(SQL_API_SQLSETCURSORNAME);
SQL_CASE(SQL_API_SQLFREEHANDLE);
SQL_CASE(SQL_API_SQLSETDESCFIELD);
SQL_CASE(SQL_API_SQLFREESTMT);
SQL_CASE(SQL_API_SQLSETDESCREC);
SQL_CASE(SQL_API_SQLGETCONNECTATTR);
SQL_CASE(SQL_API_SQLSETENVATTR);
SQL_CASE(SQL_API_SQLGETCURSORNAME);
SQL_CASE(SQL_API_SQLSETSTMTATTR);
SQL_CASE(SQL_API_SQLGETDATA);
// Open Group standards-compliance);
SQL_CASE(SQL_API_SQLCOLUMNS);
SQL_CASE(SQL_API_SQLSTATISTICS);
SQL_CASE(SQL_API_SQLSPECIALCOLUMNS);
SQL_CASE(SQL_API_SQLTABLES);
// ODBC standards-compliance);
SQL_CASE(SQL_API_SQLBINDPARAMETER);
SQL_CASE(SQL_API_SQLNATIVESQL);
SQL_CASE(SQL_API_SQLBROWSECONNECT);
SQL_CASE(SQL_API_SQLNUMPARAMS);
SQL_CASE(SQL_API_SQLBULKOPERATIONS);
SQL_CASE(SQL_API_SQLPRIMARYKEYS);
SQL_CASE(SQL_API_SQLCOLUMNPRIVILEGES);
SQL_CASE(SQL_API_SQLPROCEDURECOLUMNS);
SQL_CASE(SQL_API_SQLDESCRIBEPARAM);
SQL_CASE(SQL_API_SQLPROCEDURES);
SQL_CASE(SQL_API_SQLDRIVERCONNECT);
SQL_CASE(SQL_API_SQLSETPOS);
SQL_CASE(SQL_API_SQLFOREIGNKEYS);
SQL_CASE(SQL_API_SQLTABLEPRIVILEGES);
SQL_CASE(SQL_API_SQLMORERESULTS);
SQL_CASE(SQL_API_SQLALLOCCONNECT);
SQL_CASE(SQL_API_SQLALLOCENV);
SQL_CASE(SQL_API_SQLALLOCSTMT);
SQL_CASE(SQL_API_SQLBINDPARAM);
SQL_CASE(SQL_API_SQLERROR);
SQL_CASE(SQL_API_SQLFREECONNECT);
SQL_CASE(SQL_API_SQLFREEENV);
SQL_CASE(SQL_API_SQLGETCONNECTOPTION);
SQL_CASE(SQL_API_SQLGETSTMTOPTION);
SQL_CASE(SQL_API_SQLSETCONNECTOPTION);
SQL_CASE(SQL_API_SQLSETPARAM);
SQL_CASE(SQL_API_SQLSETSTMTOPTION);
SQL_CASE(SQL_API_SQLTRANSACT);
SQL_CASE(SQL_API_SQLCANCELHANDLE);
default: return "UNKNOWN";
}
}
const char* sql_freestmt_option_type(int type) {
switch (type) {
SQL_CASE(SQL_CLOSE);
SQL_CASE(SQL_DROP);
SQL_CASE(SQL_UNBIND);
SQL_CASE(SQL_RESET_PARAMS);
default: return "UNKNOWN";
}
}
const char* sql_soi_type(int soi) {
switch (soi) {
SQL_CASE(SQL_NTS);
SQL_CASE(SQL_NULL_DATA);
SQL_CASE(SQL_DEFAULT_PARAM);
SQL_CASE(SQL_DATA_AT_EXEC);
default: {
if (soi >= 0) return "";
return "SQL_LEN_DATA_AT_EXEC(?)";
} break;
}
}
const char* sql_nullable_type(int type) {
switch (type) {
SQL_CASE(SQL_NO_NULLS);
SQL_CASE(SQL_NULLABLE);
SQL_CASE(SQL_NULLABLE_UNKNOWN);
default: {
return "UNKNOWN";
} break;
}
}
int is_valid_sql_c_type(int type) { int is_valid_sql_c_type(int type) {
const char *ctype = sql_c_type(type); const char *ctype = sql_c_type(type);
if (strcmp(ctype, "UNKNOWN")==0) return 0; if (strcmp(ctype, "UNKNOWN")==0) return 0;
...@@ -127,3 +617,46 @@ int utf8_chars(const char *src) ...@@ -127,3 +617,46 @@ int utf8_chars(const char *src)
return (int)chars; return (int)chars;
} }
static int do_charset_chars(iconv_t cnv, const unsigned char *src)
{
int chars = 0;
char *ps = (char*)src;
char buf[16];
size_t sn = 1;
while (1) {
char *ds = buf;
size_t dn = sizeof(buf);
size_t n = iconv(cnv, &ps, &sn, &ds, &dn);
if (n==(size_t)-1) {
int e = errno;
switch (e) {
case EILSEQ: return -1;
case E2BIG: return -1;
case EINVAL: sn += 1; continue;
default: return -1;
}
}
if (sn) return -1;
if (n>0) return -1;
int i=0;
for (i=0; i<(sizeof(buf)-dn); ++i) {
if (buf[i]) break;
}
if (i>=(sizeof(buf)-dn)) break;
chars += (int)1;
sn = 1;
}
return chars;
}
int charset_chars(const char *charset, const unsigned char *src) {
iconv_t cnv = iconv_open(charset, charset);
if (cnv==(iconv_t)-1) return -1;
int chars = do_charset_chars(cnv, src);
iconv_close(cnv);
return chars;
}
...@@ -23,11 +23,24 @@ ...@@ -23,11 +23,24 @@
const char* sql_sql_type(int type); const char* sql_sql_type(int type);
const char* sql_c_type(int type); const char* sql_c_type(int type);
const char* sql_handle_type(int type);
const char* sql_env_attr_type(int type);
const char* sql_conn_attr_type(int type);
const char* sql_info_type(int type);
const char* sql_field_identifier(int type);
const char* sql_diag_identifier(int type);
const char* sql_input_output_type(int type);
const char* sql_stmt_attr_type(int type);
const char* sql_function_type(int type);
const char* sql_freestmt_option_type(int type);
const char* sql_soi_type(int soi);
const char* sql_nullable_type(int type);
int is_valid_sql_c_type(int type); int is_valid_sql_c_type(int type);
int is_valid_sql_sql_type(int type); int is_valid_sql_sql_type(int type);
int utf8_chars(const char *src); int utf8_chars(const char *src);
int charset_chars(const char *charset, const unsigned char *src);
#endif // _TODBC_UTIL_H_ #endif // _TODBC_UTIL_H_
PROJECT(TDengine)
IF (TD_LINUX)
# AUX_SOURCE_DIRECTORY(. SRC)
ADD_EXECUTABLE(tcodbc main.c)
TARGET_LINK_LIBRARIES(tcodbc odbc)
ADD_EXECUTABLE(tconv tconv.c)
ENDIF ()
IF (TD_WINDOWS_64)
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /GL")
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL")
# AUX_SOURCE_DIRECTORY(. SRC)
ADD_EXECUTABLE(tcodbc main.c)
TARGET_LINK_LIBRARIES(tcodbc odbc32 odbccp32 user32 legacy_stdio_definitions os)
ADD_EXECUTABLE(tconv tconv.c)
TARGET_LINK_LIBRARIES(tconv tutil)
ENDIF ()
import pyodbc
# cnxn = pyodbc.connect('DSN={TAOS_DSN};UID={ root };PWD={ taosdata };HOST={ localhost:6030 }', autocommit=True)
cnxn = pyodbc.connect('DSN={TAOS_DSN}; UID=root;PWD=taosdata; HOST=localhost:6030', autocommit=True)
cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
#cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8')
#cnxn.setencoding(encoding='utf-8')
#cursor = cnxn.cursor()
#cursor.execute("SELECT * from db.t")
#row = cursor.fetchone()
#while row:
# print(row)
# row = cursor.fetchone()
#cursor.close()
#cursor = cnxn.cursor()
#cursor.execute("""
#INSERT INTO db.t values (?,?,?,?,?,?,?,?,?,?)
#""",
#"2020-12-12 00:00:00",
#1,
#27,
#32767,
#147483647,
#223372036854775807,
#23.456,
#899.999999,
#"foo",
#"bar")
cursor = cnxn.cursor()
cursor.execute("drop database if exists db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create database db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.mt (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.mt values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("SELECT * from db.mt")
row = cursor.fetchone()
while row:
print(row)
row = cursor.fetchone()
cursor.close()
#cursor = cnxn.cursor()
#cursor.execute("drop database if exists db");
#cursor.close()
#
#cursor = cnxn.cursor()
#cursor.execute("create database db");
#cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(4), blob nchar(4))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hell', 'worl')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("SELECT * from db.t")
row = cursor.fetchone()
while row:
print(row)
row = cursor.fetchone()
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.v (ts timestamp, v1 tinyint)")
cursor.close()
params = [ ('A', 1), ('B', 2), ('C', 3) ]
params = [ ('A', 1), ('B', 2), ('C', 3) ]
params = [ ('2020-10-16 00:00:00', 1),
('2020-10-16 00:00:01', 4),
('2020-10-16 00:00:02', 5),
('2020-10-16 00:00:03.009', 6) ]
cursor = cnxn.cursor()
cursor.fast_executemany = True
cursor.executemany("insert into db.v values (?, ?)", params)
cursor.close()
cursor = cnxn.cursor()
cursor.execute("SELECT * from db.v")
row = cursor.fetchone()
while row:
print(row)
row = cursor.fetchone()
cursor.close()
cursor = cnxn.cursor()
cursor.execute("SELECT * from db.v where v1 > ?", 4)
row = cursor.fetchone()
while row:
print(row)
row = cursor.fetchone()
cursor.close()
cursor = cnxn.cursor()
cursor.execute("SELECT * from db.v where v1 > ?", '5')
row = cursor.fetchone()
while row:
print(row)
row = cursor.fetchone()
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.f (ts timestamp, v1 float)")
cursor.close()
params = [ ('2020-10-20 00:00:10', '123.3') ]
cursor = cnxn.cursor()
cursor.fast_executemany = True
cursor.executemany("insert into db.f values (?, ?)", params)
cursor.close()
PROJECT(TDengine) PROJECT(TDengine)
IF (TD_LINUX) ADD_EXECUTABLE(todbcinst main.c)
ADD_EXECUTABLE(todbcinst main.c) ADD_EXECUTABLE(tconv tconv.c)
IF (TD_LINUX OR TD_DARWIN)
TARGET_LINK_LIBRARIES(todbcinst odbc odbcinst) TARGET_LINK_LIBRARIES(todbcinst odbc odbcinst)
ENDIF () ENDIF ()
IF (TD_DARWIN)
target_include_directories(todbcinst PRIVATE /usr/local/include)
target_link_directories(todbcinst PUBLIC /usr/local/lib)
target_include_directories(tconv PRIVATE /usr/local/include)
target_link_directories(tconv PUBLIC /usr/local/lib)
TARGET_LINK_LIBRARIES(tconv iconv)
ENDIF ()
IF (TD_WINDOWS_64) IF (TD_WINDOWS_64)
ADD_EXECUTABLE(todbcinst main.c)
TARGET_LINK_LIBRARIES(todbcinst odbc32 odbccp32 user32 legacy_stdio_definitions os) TARGET_LINK_LIBRARIES(todbcinst odbc32 odbccp32 user32 legacy_stdio_definitions os)
TARGET_LINK_LIBRARIES(tconv taos)
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/todbcinst.exe DESTINATION .) INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/todbcinst.exe DESTINATION .)
ENDIF () ENDIF ()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册