odbc driver
This commit is contained in:
parent
25e7ff1e6b
commit
12ad3514ef
|
@ -171,6 +171,8 @@ matrix:
|
|||
- build-essential
|
||||
- cmake
|
||||
- binutils-2.26
|
||||
- unixodbc
|
||||
- unixodbc-dev
|
||||
env:
|
||||
- DESC="trusty/gcc-4.8/bintuils-2.26 build"
|
||||
|
||||
|
@ -198,6 +200,8 @@ matrix:
|
|||
packages:
|
||||
- build-essential
|
||||
- cmake
|
||||
- unixodbc
|
||||
- unixodbc-dev
|
||||
|
||||
before_script:
|
||||
- export TZ=Asia/Harbin
|
||||
|
@ -252,6 +256,8 @@ matrix:
|
|||
packages:
|
||||
- build-essential
|
||||
- cmake
|
||||
- unixodbc
|
||||
- unixodbc-dev
|
||||
env:
|
||||
- DESC="arm64 xenial build"
|
||||
|
||||
|
@ -280,6 +286,7 @@ matrix:
|
|||
addons:
|
||||
homebrew:
|
||||
- cmake
|
||||
- unixodbc
|
||||
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
|
|
|
@ -19,6 +19,6 @@ ADD_SUBDIRECTORY(tsdb)
|
|||
ADD_SUBDIRECTORY(wal)
|
||||
ADD_SUBDIRECTORY(cq)
|
||||
ADD_SUBDIRECTORY(dnode)
|
||||
#ADD_SUBDIRECTORY(connector/odbc)
|
||||
ADD_SUBDIRECTORY(connector/odbc)
|
||||
ADD_SUBDIRECTORY(connector/jdbc)
|
||||
|
||||
|
|
|
@ -261,7 +261,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) {
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
if (1) {
|
||||
if (0) {
|
||||
// allow user bind param data with different type
|
||||
union {
|
||||
int8_t v1;
|
||||
|
@ -1057,14 +1057,28 @@ int taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes) {
|
|||
}
|
||||
|
||||
if (pStmt->isInsert) {
|
||||
SSqlObj* pSql = pStmt->pSql;
|
||||
SSqlCmd *pCmd = &pSql->cmd;
|
||||
STableDataBlocks* pBlock = taosArrayGetP(pCmd->pDataBlocks, 0);
|
||||
SSqlCmd* pCmd = &pStmt->pSql->cmd;
|
||||
STableMetaInfo* pTableMetaInfo = tscGetTableMetaInfoFromCmd(pCmd, 0, 0);
|
||||
STableMeta* pTableMeta = pTableMetaInfo->pTableMeta;
|
||||
if (pCmd->pTableBlockHashList == NULL) {
|
||||
pCmd->pTableBlockHashList = taosHashInit(16, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), true, false);
|
||||
}
|
||||
|
||||
assert(pCmd->numOfParams == pBlock->numOfParams);
|
||||
if (idx < 0 || idx >= pBlock->numOfParams) return -1;
|
||||
STableDataBlocks* pBlock = NULL;
|
||||
|
||||
SParamInfo* param = pBlock->params + idx;
|
||||
int32_t ret =
|
||||
tscGetDataBlockFromList(pCmd->pTableBlockHashList, pTableMeta->id.uid, TSDB_PAYLOAD_SIZE, sizeof(SSubmitBlk),
|
||||
pTableMeta->tableInfo.rowSize, &pTableMetaInfo->name, pTableMeta, &pBlock, NULL);
|
||||
if (ret != 0) {
|
||||
// todo handle error
|
||||
}
|
||||
|
||||
if (idx<0 || idx>=pBlock->numOfParams) {
|
||||
tscError("param %d: out of range", idx);
|
||||
abort();
|
||||
}
|
||||
|
||||
SParamInfo* param = &pBlock->params[idx];
|
||||
if (type) *type = param->type;
|
||||
if (bytes) *bytes = param->bytes;
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
!c/
|
||||
node_modules/
|
||||
package-lock.json
|
|
@ -15,7 +15,7 @@ IF (TD_LINUX_64)
|
|||
message(STATUS "unixodbc/unixodbc-dev are installed, and odbc connector will be built")
|
||||
find_package(FLEX)
|
||||
if(NOT FLEX_FOUND)
|
||||
message(FATAL_ERROR "you need to install flex first")
|
||||
message(WARNING "you need to install flex first")
|
||||
else ()
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0.0)
|
||||
message(WARNING "gcc 4.8.0 will complain too much about flex-generated code, we just bypass building ODBC driver in such case")
|
||||
|
@ -24,7 +24,7 @@ IF (TD_LINUX_64)
|
|||
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wconversion")
|
||||
ADD_SUBDIRECTORY(src)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
endif ()
|
||||
endif()
|
||||
endif()
|
||||
|
@ -33,10 +33,44 @@ IF (TD_LINUX_64)
|
|||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_DARWIN)
|
||||
find_program(HAVE_ODBCINST NAMES odbcinst)
|
||||
IF (HAVE_ODBCINST)
|
||||
include(CheckSymbolExists)
|
||||
# shall we revert CMAKE_REQUIRED_LIBRARIES and how?
|
||||
set(CMAKE_REQUIRED_LIBRARIES odbc)
|
||||
set(CMAKE_REQUIRED_INCLUDES /usr/local/include)
|
||||
set(CMAKE_REQUIRED_LINK_OPTIONS -L/usr/local/lib)
|
||||
check_symbol_exists(SQLExecute "sql.h" HAVE_ODBC_DEV)
|
||||
if(NOT (HAVE_ODBC_DEV))
|
||||
unset(HAVE_ODBC_DEV CACHE)
|
||||
message(WARNING "unixodbc-dev is not installed yet, you may install it with homebrew by typing: brew install unixodbc")
|
||||
else ()
|
||||
message(STATUS "unixodbc/unixodbc-dev are installed, and odbc connector will be built")
|
||||
find_package(FLEX)
|
||||
if(NOT FLEX_FOUND)
|
||||
message(WARNING "you need to install flex first")
|
||||
else ()
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0.0)
|
||||
message(WARNING "gcc 4.8.0 will complain too much about flex-generated code, we just bypass building ODBC driver in such case")
|
||||
else ()
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wconversion")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wconversion")
|
||||
ADD_SUBDIRECTORY(src)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
endif ()
|
||||
endif()
|
||||
endif()
|
||||
ELSE ()
|
||||
message(WARNING "unixodbc is not installed yet, you may install it under ubuntu by typing: brew install unixodbc")
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_WINDOWS_64)
|
||||
find_package(ODBC)
|
||||
if (NOT ODBC_FOUND)
|
||||
message(FATAL_ERROR "you need to install ODBC first")
|
||||
message(WARNING "you need to install ODBC first")
|
||||
else ()
|
||||
message(STATUS "ODBC_INCLUDE_DIRS: ${ODBC_INCLUDE_DIRS}")
|
||||
message(STATUS "ODBC_LIBRARIES: ${ODBC_LIBRARIES}")
|
||||
|
@ -50,6 +84,7 @@ IF (TD_WINDOWS_64)
|
|||
else ()
|
||||
ADD_SUBDIRECTORY(src)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
endif()
|
||||
ENDIF ()
|
||||
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
|
||||
# ODBC 驱动 #
|
||||
|
||||
- **TAOS ODBC驱动持续更新中**
|
||||
|
||||
- **目前导出的ODBC函数包括(注: 有些不常用的函数只是导出,但并未实现)**:
|
||||
SQLAllocEnv
|
||||
SQLFreeEnv
|
||||
SQLAllocConnect
|
||||
SQLFreeConnect
|
||||
SQLGetEnvAttr
|
||||
SQLSetEnvAttr
|
||||
SQLGetConnectAttr
|
||||
SQLGetConnectOption
|
||||
SQLGetInfo
|
||||
SQLConnect
|
||||
SQLDisconnect
|
||||
SQLAllocStmt
|
||||
SQLAllocHandle
|
||||
SQLFreeHandle
|
||||
SQLFreeStmt
|
||||
SQLExecDirect
|
||||
SQLNumResultCols
|
||||
SQLRowCount
|
||||
SQLColAttribute
|
||||
SQLGetData
|
||||
SQLFetch
|
||||
SQLPrepare
|
||||
SQLExecute
|
||||
SQLParamData
|
||||
SQLPutData
|
||||
SQLGetDiagRec
|
||||
SQLBindParameter
|
||||
SQLDescribeParam
|
||||
SQLDriverConnect
|
||||
SQLSetConnectAttr
|
||||
SQLDescribeCol
|
||||
SQLBindCol
|
||||
SQLNumParams
|
||||
SQLSetStmtAttr
|
||||
SQLBindParam
|
||||
SQLCancel
|
||||
SQLCancelHandle
|
||||
SQLCloseCursor
|
||||
SQLColumns
|
||||
SQLCopyDesc
|
||||
SQLDataSources
|
||||
SQLEndTran
|
||||
SQLFetchScroll
|
||||
SQLGetCursorName
|
||||
SQLGetDescField
|
||||
SQLGetDescRec
|
||||
SQLGetStmtAttr
|
||||
SQLGetStmtOption
|
||||
SQLGetTypeInfo
|
||||
SQLSetConnectOption
|
||||
SQLSetCursorName
|
||||
SQLSetDescField
|
||||
SQLSetDescRec
|
||||
SQLSetParam
|
||||
SQLSetStmtOption
|
||||
SQLSpecialColumns
|
||||
SQLStatistics
|
||||
SQLTables
|
||||
SQLTransact
|
||||
|
||||
`
|
||||
|
||||
- **国际化。可以通过在ODBC连接串中指定针对SQLCHAR/SQLWCHAR/taos_charset/system-locale的字符集来解决常见的环境匹配问题**.
|
||||
|
||||
- **现有的ODBC客户端工具可以籍此驱动同TAOS数据源互联,包括主流linux/macosx/windows平台**
|
||||
|
||||
- **现有的支持ODBC的编程语言可以籍此驱动同TAOS数据源互联, 例如: c/nodejs/python/rust/go已经在上述三个主流平台测试通过, 熟悉其他语言的同学可以发现这基本上是开箱即用**
|
||||
|
||||
- **持续更新中**...
|
||||
|
||||
# 编译和测试使用
|
||||
**Note**: 下述所有步骤都在TDengine项目的根目录下进行
|
||||
**Note**: 请先确保src/connector/odbc如下所示,被包含在src/CMakeLists.txt源文件中
|
||||
```
|
||||
...
|
||||
ADD_SUBDIRECTORY(dnode)
|
||||
ADD_SUBDIRECTORY(connector/odbc)
|
||||
ADD_SUBDIRECTORY(connector/jdbc)
|
||||
```
|
||||
|
||||
# Linux下的编译, 以Ubuntu为例
|
||||
```
|
||||
sudo apt install unixodbc unixodbc-dev flex
|
||||
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
|
||||
```
|
||||
# MacOSX下的编译, 以Catalina为例,依赖homebrew进行第三方工具安装[https://brew.sh/]
|
||||
```
|
||||
brew install unixodbc
|
||||
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
|
||||
```
|
||||
# Windows下的编译, 以Windows 10为例
|
||||
- 安装windows的`flex`工具. 目前我们使用[https://github.com/lexxmark/winflexbison](url). 安装完成后请确保win_flex.exe所在目录记录于`PATH`环境变量中.
|
||||
- 安装Microsoft Visual Studio工具, 以VS2019为例
|
||||
- `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"`
|
||||
- `rmdir /s /q debug`
|
||||
- `cmake -G "NMake Makefiles" -B debug`
|
||||
- `cmake --build debug`
|
||||
- `cmake --install debug`
|
||||
- 以管理员身份打开`命令提示符`
|
||||
- 安装ODBC驱动: 在上述打开的提示符下执行 `odbcconf /A {INSTALLDRIVER "TAOS | Driver=C:/TDengine/driver/todbc.dll | ConnectFunctions=YYN | DriverODBCVer=03.00"}`
|
||||
- 新增一个数据源DSN: 执行 `odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN | Server=<host>:<port>"}`
|
||||
上述步骤出现失败的话,可以参看这些链接:
|
||||
1. win flex的安装: https://github.com/lexxmark/winflexbison/releases
|
||||
2. PATH环境变量: https://jingyan.baidu.com/article/8ebacdf02d3c2949f65cd5d0.html
|
||||
3. 管理员身份: https://blog.csdn.net/weixin_41083002/article/details/81019893
|
||||
4. 安装odbc驱动/数据源: https://docs.microsoft.com/en-us/sql/odbc/odbcconf-exe?view=sql-server-ver15
|
||||
|
||||
# 测试使用
|
||||
强烈建议您在linux上编译运行taosd服务端,因为当前TAOS还没有windows侧的服务端移植.
|
||||
**Note1**: <>符号所括起的内容请按您当前的系统填写
|
||||
**Note2**: `.stmts` 文件存放的是测试用sql语句, 注意其格式为`UTF-8`(不带BOM导引头)
|
||||
## 按官方文档在linux侧启动taosd,确保选用'UTF-8'作为其字符集
|
||||
## 在linux下创建数据
|
||||
```
|
||||
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/samples/create_data.stmts
|
||||
--<或指定特殊的ODBC连接字符串 -->
|
||||
./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>' --sts ./src/connector/odbc/samples/create_data.stmts
|
||||
```
|
||||
## 在windows下检索数据
|
||||
```
|
||||
.\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;enc_char=UTF-8" --sts .\src\connector\odbc\samples\query_data.stmts
|
||||
```
|
||||
## 在MacOSX下检索数据
|
||||
```
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>" --sts ./src/connector/odbc/samples/query_data.stmts
|
||||
```
|
||||
|
||||
## 代码示例
|
||||
- src/connector/odbc/examples/c
|
||||
- src/connector/odbc/examples/js
|
||||
- src/connector/odbc/examples/py
|
||||
- src/connector/odbc/examples/rust
|
||||
- src/connector/odbc/examples/go
|
||||
|
||||
在linux或MacOSX上, 可以通过修改运行如下脚本来尝试各种测试:
|
||||
**Note**: 不要忘记替换<host>:<port>
|
||||
**Note**: 你需要在你的平台上安装nodejs/python/rust/go
|
||||
**Note**: 你还需要安装对应语言的ODBC包:
|
||||
-- node-odbc for nodejs: https://www.npmjs.com/package/odbc
|
||||
-- pyodbc for python: https://pypi.org/project/pyodbc/
|
||||
-- rust-odbc for rust: https://docs.rs/odbc/0.17.0/odbc/
|
||||
-- go-odbc for go: https://github.com/alexbrainman/odbc
|
||||
|
||||
```
|
||||
echo c &&
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --sts src/connector/odbc/samples/create_data.stmts &&
|
||||
echo nodejs &&
|
||||
./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
|
||||
echo python &&
|
||||
python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
|
||||
echo rust &&
|
||||
pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=<host>:<port>' cargo run && popd &&
|
||||
echo go &&
|
||||
DSN='DSN=TAOS_DSN;Server=<host>:<port>' go run src/connector/odbc/examples/go/odbc.go &&
|
||||
```
|
||||
|
||||
## 您可以对比测试一下prepared-batch-insert是否会带来速度的提升:
|
||||
**注** src/connector/odbc/examples/c/main.c是tcodbc的源代码
|
||||
```
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --insert --batch_size 200 --batchs 10000
|
||||
```
|
||||
|
||||
|
|
@ -1,20 +1,25 @@
|
|||
|
||||
# ODBC Driver #
|
||||
|
||||
- **very initial implementation of ODBC driver for TAOS
|
||||
- **on-going implementation of ODBC driver for TAOS**
|
||||
|
||||
- **currently partially supported ODBC functions are: `
|
||||
- **currently exported ODBC functions are**:
|
||||
SQLAllocEnv
|
||||
SQLFreeEnv
|
||||
SQLAllocConnect
|
||||
SQLFreeConnect
|
||||
SQLGetEnvAttr
|
||||
SQLSetEnvAttr
|
||||
SQLGetConnectAttr
|
||||
SQLGetConnectOption
|
||||
SQLGetInfo
|
||||
SQLConnect
|
||||
SQLDisconnect
|
||||
SQLAllocStmt
|
||||
SQLAllocHandle
|
||||
SQLFreeHandle
|
||||
SQLFreeStmt
|
||||
SQLExecDirect
|
||||
SQLExecDirectW
|
||||
SQLNumResultCols
|
||||
SQLRowCount
|
||||
SQLColAttribute
|
||||
|
@ -22,29 +27,62 @@ SQLGetData
|
|||
SQLFetch
|
||||
SQLPrepare
|
||||
SQLExecute
|
||||
SQLGetDiagField
|
||||
SQLParamData
|
||||
SQLPutData
|
||||
SQLGetDiagRec
|
||||
SQLBindParameter
|
||||
SQLDescribeParam
|
||||
SQLDriverConnect
|
||||
SQLSetConnectAttr
|
||||
SQLDescribeCol
|
||||
SQLBindCol
|
||||
SQLNumParams
|
||||
SQLSetStmtAttr
|
||||
ConfigDSN
|
||||
SQLBindParam
|
||||
SQLCancel
|
||||
SQLCancelHandle
|
||||
SQLCloseCursor
|
||||
SQLColumns
|
||||
SQLCopyDesc
|
||||
SQLDataSources
|
||||
SQLEndTran
|
||||
SQLFetchScroll
|
||||
SQLGetCursorName
|
||||
SQLGetDescField
|
||||
SQLGetDescRec
|
||||
SQLGetStmtAttr
|
||||
SQLGetStmtOption
|
||||
SQLGetTypeInfo
|
||||
SQLSetConnectOption
|
||||
SQLSetCursorName
|
||||
SQLSetDescField
|
||||
SQLSetDescRec
|
||||
SQLSetParam
|
||||
SQLSetStmtOption
|
||||
SQLSpecialColumns
|
||||
SQLStatistics
|
||||
SQLTables
|
||||
SQLTransact
|
||||
|
||||
`
|
||||
|
||||
- **internationalized, you can specify different charset/code page for easy going. eg.: insert `utf-8.zh_cn` characters into database located in linux machine, while query them out in `gb2312/gb18030/...` code page in your chinese windows machine, or vice-versa. and much fun, insert `gb2312/gb18030/...` characters into database located in linux box from
|
||||
your japanese windows box, and query them out in your local chinese windows machine.
|
||||
- **internationalized, you can specify charset for SQLCHAR/SQLWCHAR/taos_charset/system-locale to coordinate with the environment**.
|
||||
|
||||
- **enable ODBC-aware software to communicate with TAOS.
|
||||
- **enable ODBC-aware software to communicate with TAOS, no matter what platform it's running on, currently we support linux/macosx/windows**
|
||||
|
||||
- **enable any language with ODBC-bindings/ODBC-plugings to communicate with TAOS
|
||||
- **enable any language with ODBC-bindings/ODBC-plugings to communicate with TAOS, currently c/nodejs/python/rust/go are all passed in our test environment, we believe other languages with ODBC-bindings/plugins are available-out-of-box**
|
||||
|
||||
- **still going on...
|
||||
- **still going on**...
|
||||
|
||||
# Building and Testing
|
||||
**Note**: all `work` is done in TDengine's project directory
|
||||
|
||||
**Note**: please make sure src/connector/odbc is included in src/CMakeLists.txt
|
||||
```
|
||||
...
|
||||
ADD_SUBDIRECTORY(dnode)
|
||||
ADD_SUBDIRECTORY(connector/odbc)
|
||||
ADD_SUBDIRECTORY(connector/jdbc)
|
||||
```
|
||||
|
||||
# Building under Linux, use Ubuntu as example
|
||||
```
|
||||
|
@ -53,36 +91,68 @@ rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug &
|
|||
```
|
||||
# Building under Windows, use Windows 10 as example
|
||||
- install windows `flex` port. We use [https://github.com/lexxmark/winflexbison](url) at the moment. Please be noted to append `<path_to_win_flex.exe>` to your `PATH`.
|
||||
- install Microsoft Visual Studio, take VS2015 as example here
|
||||
- `"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64`
|
||||
- install Microsoft Visual Studio, take VS2019 as example here
|
||||
- `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"`
|
||||
- `rmdir /s /q debug`
|
||||
- `cmake -G "NMake Makefiles" -B debug`
|
||||
- `cmake --build debug`
|
||||
- `cmake --install debug`
|
||||
- open your `Command Prompt` with Administrator's priviledge
|
||||
- remove previously installed TAOS ODBC driver: run `C:\TDengine\todbcinst -u -f -n TAOS`
|
||||
- install TAOS ODBC driver that was just built: run `C:\TDengine\todbcinst -i -n TAOS -p C:\TDengine\driver`
|
||||
- add a new user dsn: run `odbcconf CONFIGDSN TAOS "DSN=TAOS_DSN|Server=<fqdn>:<port>`
|
||||
- install TAOS ODBC driver that was just built: run `odbcconf /A {INSTALLDRIVER "TAOS | Driver=C:/TDengine/driver/todbc.dll | ConnectFunctions=YYN | DriverODBCVer=03.00"}`
|
||||
- add a new user dsn: run `odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN | Server=host:port"}`
|
||||
|
||||
# Test
|
||||
we highly suggest that you build both in linux(ubuntu) and windows(windows 10) platform, because currently TAOS only has it's server-side port on linux platform.
|
||||
we highly suggest that you build both in linux(ubuntu) and windows(windows 10) platform, because currently TAOS has not server-side port on windows platform.
|
||||
**Note1**: content within <> shall be modified to match your environment
|
||||
**Note2**: `.stmts` source files are all encoded in `UTF-8`
|
||||
## start taosd in linux, suppose charset is `UTF-8` as default
|
||||
```
|
||||
taosd -c ./debug/test/cfg
|
||||
```
|
||||
## start taosd in linux, suppose charset is `UTF-8` as default, please follow TAOS doc for starting up
|
||||
## create data in linux
|
||||
```
|
||||
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/tests/create_data.stmts
|
||||
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/samples/create_data.stmts
|
||||
--<or with driver connection string -->
|
||||
./debug/build/bin/tcodbc --dcs 'Driver=TAOS;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;client_enc=UTF-8' ./src/connector/odbc/tests/create_data.stmts
|
||||
./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>' --sts ./src/connector/odbc/samples/create_data.stmts
|
||||
```
|
||||
## query data in windows
|
||||
```
|
||||
.\debug\build\bin\tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts .\src\connector\odbc\tests\query_data.stmts
|
||||
--<or with driver connection string -->
|
||||
.\debug\build\bin\tcodbc --dcs "Driver=TAOS;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;client_enc=UTF-8" .\src\connector\odbc\tests\query_data.stmts
|
||||
.\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;enc_char=UTF-8" --sts .\src\connector\odbc\samples\query_data.stmts
|
||||
```
|
||||
## query data in MacOSX
|
||||
```
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>" --sts ./src/connector/odbc/samples/query_data.stmts
|
||||
```
|
||||
|
||||
## code examples
|
||||
- src/connector/odbc/examples/c
|
||||
- src/connector/odbc/examples/js
|
||||
- src/connector/odbc/examples/py
|
||||
- src/connector/odbc/examples/rust
|
||||
- src/connector/odbc/examples/go
|
||||
|
||||
on linux/MacOSX, here after are script-snippet for you to play with:
|
||||
**Note**: don't forget to replace <host>:<port> with whatever on your environment
|
||||
**Note**: you need to install node/python3/rust/go on you machine
|
||||
**Note**: you also need to install odbc-bindings/odbc-pluggins on those language platform, such as:
|
||||
-- node-odbc for nodejs: https://www.npmjs.com/package/odbc
|
||||
-- pyodbc for python: https://pypi.org/project/pyodbc/
|
||||
-- rust-odbc for rust: https://docs.rs/odbc/0.17.0/odbc/
|
||||
-- go-odbc for go: https://github.com/alexbrainman/odbc
|
||||
|
||||
```
|
||||
echo c &&
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --sts src/connector/odbc/samples/create_data.stmts &&
|
||||
echo nodejs &&
|
||||
./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
|
||||
echo python &&
|
||||
python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
|
||||
echo rust &&
|
||||
pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=<host>:<port>' cargo run && popd &&
|
||||
echo go &&
|
||||
DSN='DSN=TAOS_DSN;Server=<host>:<port>' go run src/connector/odbc/examples/go/odbc.go &&
|
||||
```
|
||||
|
||||
## see how fast prepared-statment could bring up with:
|
||||
```
|
||||
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --insert --batch_size 200 --batchs 10000
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
PROJECT(TDengine)
|
||||
|
||||
ADD_SUBDIRECTORY(c)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
PROJECT(TDengine)
|
||||
|
||||
ADD_EXECUTABLE(tcodbc main.c ../../src/todbc_log.c)
|
||||
|
||||
IF (TD_LINUX OR TD_DARWIN)
|
||||
TARGET_LINK_LIBRARIES(tcodbc 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 odbc32 odbccp32 user32 legacy_stdio_definitions os)
|
||||
|
||||
ADD_EXECUTABLE(tms main.cpp)
|
||||
TARGET_LINK_LIBRARIES(tms odbc32)
|
||||
ENDIF ()
|
||||
|
|
@ -0,0 +1,808 @@
|
|||
#include "../../src/todbc_log.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include "os.h"
|
||||
#endif
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include <odbcinst.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CHK_TEST(statement) \
|
||||
do { \
|
||||
D("testing: %s", #statement); \
|
||||
int r = (statement); \
|
||||
if (r) { \
|
||||
D("testing failed: %s", #statement); \
|
||||
return 1; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
typedef struct {
|
||||
int batch_size;
|
||||
int batchs;
|
||||
int keep_stmt_among_batchs;
|
||||
} insert_arg_t;
|
||||
|
||||
typedef struct db_column_s db_column_t;
|
||||
struct db_column_s {
|
||||
SQLSMALLINT nameLength;
|
||||
char name[4096]; // seems enough
|
||||
SQLSMALLINT dataType;
|
||||
SQLULEN columnSize;
|
||||
SQLSMALLINT decimalDigits;
|
||||
SQLSMALLINT nullable;
|
||||
};
|
||||
|
||||
static db_column_t *columns = NULL;
|
||||
|
||||
typedef struct data_s data_t;
|
||||
struct data_s {
|
||||
int64_t ts;
|
||||
int8_t b;
|
||||
int8_t v1;
|
||||
int16_t v2;
|
||||
int32_t v4;
|
||||
int64_t v8;
|
||||
float f4;
|
||||
double f8;
|
||||
char bin[40+1];
|
||||
char blob[40+1]; // why 80? ref: tests/examples/c/apitest.c
|
||||
};
|
||||
|
||||
#define CHK_RESULT(r, ht, h, fmt, ...) \
|
||||
do { \
|
||||
if (r==0) break; \
|
||||
SQLSMALLINT i_0381 = 1; \
|
||||
while (1) { \
|
||||
SQLCHAR ss[10]; \
|
||||
SQLINTEGER ne = 0; \
|
||||
SQLCHAR es[4096]; \
|
||||
SQLSMALLINT n = 0; \
|
||||
ss[0] = '\0'; \
|
||||
es[0] = '\0'; \
|
||||
SQLRETURN ret = SQLGetDiagRec(ht, h, i_0381, ss, &ne, es, sizeof(es), &n); \
|
||||
if (ret) break; \
|
||||
D("[%s]%s: " fmt "", ss, es, ##__VA_ARGS__); \
|
||||
++i_0381; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int open_connect(const char *dsn, const char *uid, const char *pwd, SQLHENV *pEnv, SQLHDBC *pConn) {
|
||||
SQLRETURN r;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
r = SQLAllocEnv(&env);
|
||||
if (r!=SQL_SUCCESS) return 1;
|
||||
do {
|
||||
r = SQLAllocConnect(env, &conn);
|
||||
CHK_RESULT(r, SQL_HANDLE_ENV, env, "");
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
do {
|
||||
r = SQLConnect(conn, (SQLCHAR*)dsn, (SQLSMALLINT)(dsn ? strlen(dsn) : 0),
|
||||
(SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0),
|
||||
(SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0));
|
||||
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
||||
if (r==SQL_SUCCESS) {
|
||||
*pEnv = env;
|
||||
*pConn = conn;
|
||||
return 0;
|
||||
}
|
||||
} while (0);
|
||||
SQLFreeConnect(conn);
|
||||
} while (0);
|
||||
SQLFreeEnv(env);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int open_driver_connect(const char *connstr, SQLHENV *pEnv, SQLHDBC *pConn) {
|
||||
SQLRETURN r;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
r = SQLAllocEnv(&env);
|
||||
if (r!=SQL_SUCCESS) return 1;
|
||||
do {
|
||||
r = SQLAllocConnect(env, &conn);
|
||||
CHK_RESULT(r, SQL_HANDLE_ENV, env, "");
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
do {
|
||||
SQLCHAR buf[4096];
|
||||
SQLSMALLINT blen = 0;
|
||||
SQLHDBC ConnectionHandle = conn;
|
||||
SQLHWND WindowHandle = NULL;
|
||||
SQLCHAR * InConnectionString = (SQLCHAR*)connstr;
|
||||
SQLSMALLINT StringLength1 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0);
|
||||
SQLCHAR * OutConnectionString = buf;
|
||||
SQLSMALLINT BufferLength = sizeof(buf);
|
||||
SQLSMALLINT * StringLength2Ptr = &blen;
|
||||
SQLUSMALLINT DriverCompletion = SQL_DRIVER_NOPROMPT;
|
||||
r = SQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString,
|
||||
StringLength1, OutConnectionString, BufferLength,
|
||||
StringLength2Ptr, DriverCompletion);
|
||||
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
||||
if (r==SQL_SUCCESS) {
|
||||
*pEnv = env;
|
||||
*pConn = conn;
|
||||
return 0;
|
||||
}
|
||||
} while (0);
|
||||
SQLFreeConnect(conn);
|
||||
} while (0);
|
||||
SQLFreeEnv(env);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQLRETURN traverse_cols(SQLHSTMT stmt, SQLSMALLINT cols) {
|
||||
SQLRETURN r = SQL_ERROR;
|
||||
for (SQLSMALLINT i=0; i<cols; ++i) {
|
||||
db_column_t column = {0};
|
||||
r = SQLDescribeCol(stmt, (SQLUSMALLINT)(i+1), (SQLCHAR*)column.name,
|
||||
(SQLSMALLINT)sizeof(column.name), &column.nameLength,
|
||||
&column.dataType, &column.columnSize,
|
||||
&column.decimalDigits, &column.nullable);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
D("col%02d:[%s]%d,type:[%d],colSize:[%zu],decimalDigits:[%d],nullable:[%d]",
|
||||
i+1, column.name, column.nameLength, column.dataType, column.columnSize,
|
||||
column.decimalDigits, column.nullable);
|
||||
db_column_t *col = (db_column_t*)realloc(columns, (size_t)(i+1)*sizeof(*col));
|
||||
if (!col) {
|
||||
D("out of memory");
|
||||
return SQL_ERROR;
|
||||
}
|
||||
col[i] = column;
|
||||
columns = col;
|
||||
}
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
static int do_statement(SQLHSTMT stmt, const char *statement) {
|
||||
SQLRETURN r = 0;
|
||||
do {
|
||||
r = SQLExecDirect(stmt, (SQLCHAR*)statement, SQL_NTS);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: [%s]", statement);
|
||||
if (r) break;
|
||||
SQLSMALLINT cols = 0;
|
||||
r = SQLNumResultCols(stmt, &cols);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
if (cols <= 0) break;
|
||||
r = traverse_cols(stmt, cols);
|
||||
char buf[4096];
|
||||
while (1) {
|
||||
SQLRETURN r = SQLFetch(stmt);
|
||||
if (r==SQL_NO_DATA) break;
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
for (size_t i=0; i<cols; ++i) {
|
||||
SQLLEN soi = 0;
|
||||
r = SQLGetData(stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, buf, sizeof(buf), &soi);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "i/cols:[%ld/%d]", i,cols);
|
||||
if (r) {
|
||||
if (r!=SQL_SUCCESS_WITH_INFO) {
|
||||
if (i>0) fprintf(stdout, "\n");
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if (soi==SQL_NULL_DATA) {
|
||||
fprintf(stdout, "%snull", i==0?"":",");
|
||||
} else {
|
||||
fprintf(stdout, "%s\"%s\"", i==0?"":",", buf);
|
||||
}
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
} while (0);
|
||||
return r;
|
||||
}
|
||||
|
||||
int test_statements(const char *dsn, const char *uid, const char *pwd, const char **statements) {
|
||||
SQLRETURN r = SQL_SUCCESS;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
int n = open_connect(dsn, uid, pwd, &env, &conn);
|
||||
if (n) return 1;
|
||||
do {
|
||||
SQLHSTMT stmt = {0};
|
||||
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
const char **p = statements;
|
||||
while (*p) {
|
||||
if (do_statement(stmt, *p)) {
|
||||
r = SQL_ERROR;
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
} while (0);
|
||||
SQLDisconnect(conn);
|
||||
SQLFreeConnect(conn);
|
||||
SQLFreeEnv(env);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
int test_driver_connect(const char *connstr) {
|
||||
SQLRETURN r = SQL_SUCCESS;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
int n = open_driver_connect(connstr, &env, &conn);
|
||||
if (n) return 1;
|
||||
SQLDisconnect(conn);
|
||||
SQLFreeConnect(conn);
|
||||
SQLFreeEnv(env);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
int create_statement(SQLHENV env, SQLHDBC conn, SQLHSTMT *pStmt) {
|
||||
SQLHSTMT stmt = {0};
|
||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
|
||||
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
||||
if (r==SQL_SUCCESS) {
|
||||
*pStmt = stmt;
|
||||
return 0;
|
||||
}
|
||||
if (r==SQL_SUCCESS_WITH_INFO) {
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int do_statements(SQLHSTMT stmt, const char **statements) {
|
||||
const char **p = statements;
|
||||
while (p && *p) {
|
||||
CHK_TEST(do_statement(stmt, *p));
|
||||
++p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tests_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt) {
|
||||
const char *statements[] = {
|
||||
"drop database if exists m",
|
||||
"create database m",
|
||||
"use m",
|
||||
// "create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(1), name nchar(1))",
|
||||
"create table t (ts timestamp, b bool)",
|
||||
"insert into t values('2020-10-10 00:00:00', 0)",
|
||||
"insert into t values('2020-10-10 00:00:00.001', 1)",
|
||||
NULL
|
||||
};
|
||||
CHK_TEST(do_statements(stmt, statements));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tests(SQLHENV env, SQLHDBC conn) {
|
||||
SQLHSTMT stmt = {0};
|
||||
CHK_TEST(create_statement(env, conn, &stmt));
|
||||
int r = tests_stmt(env, conn, stmt);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
int test_env(void) {
|
||||
SQLRETURN r;
|
||||
SQLHENV env = {0};
|
||||
r = SQLAllocEnv(&env);
|
||||
if (r!=SQL_SUCCESS) return 1;
|
||||
SQLFreeEnv(env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls) {
|
||||
FILE *f = fopen(sqls, "rb");
|
||||
if (!f) {
|
||||
D("failed to open file [%s]", sqls);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int r = 0;
|
||||
while (!feof(f)) {
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
ssize_t n = 0;
|
||||
#ifdef _MSC_VER
|
||||
n = taosGetlineImp(&line, &len, f);
|
||||
#else
|
||||
n = getline(&line, &len, f);
|
||||
#endif
|
||||
if (n==-1) break;
|
||||
|
||||
const char *p = NULL;
|
||||
do {
|
||||
if (line[0] == '#') break;
|
||||
if (n>0 && line[n-1] == '\n') line[n-1]='\0';
|
||||
if (n>0 && line[n-1] == '\r') line[n-1]='\0';
|
||||
if (n>1 && line[n-2] == '\r') line[n-2]='\0';
|
||||
p = line;
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
if (*p==0) break;
|
||||
|
||||
int positive = 1;
|
||||
if (strncmp(p, "N:", 2)==0) {
|
||||
// negative sample
|
||||
positive = 0;
|
||||
p += 2;
|
||||
} else if (strncmp(p, "P:", 2)==0) {
|
||||
// positive sample
|
||||
p += 2;
|
||||
}
|
||||
|
||||
D("statement: [%s]", p);
|
||||
r = do_statement(stmt, p);
|
||||
|
||||
if (positive && r==0) break;
|
||||
if (!positive && r) { r = 0; break; }
|
||||
if (positive) return r;
|
||||
D("expecting negative result, but got positive");
|
||||
return -1;
|
||||
} while (0);
|
||||
|
||||
free(line);
|
||||
|
||||
if (r) break;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
static int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) {
|
||||
SQLHSTMT stmt = {0};
|
||||
CHK_TEST(create_statement(env, conn, &stmt));
|
||||
int r = test_sqls_in_stmt(env, conn, stmt, sqls);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
static int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *connstr, const char *sqls) {
|
||||
int r = 0;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
if (dsn) {
|
||||
CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn));
|
||||
} else {
|
||||
CHK_TEST(open_driver_connect(connstr, &env, &conn));
|
||||
}
|
||||
|
||||
if (sqls) {
|
||||
r = test_sqls_in_conn(env, conn, sqls);
|
||||
}
|
||||
SQLDisconnect(conn);
|
||||
SQLFreeConnect(conn);
|
||||
SQLFreeEnv(env);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
typedef struct record_s record_t;
|
||||
struct record_s {
|
||||
int dummy;
|
||||
char ts[64];
|
||||
SQLLEN ts_len;
|
||||
int32_t v1;
|
||||
SQLLEN v1_len;
|
||||
char ts2[64];
|
||||
SQLLEN ts2_len;
|
||||
};
|
||||
|
||||
static int do_prepare_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt) {
|
||||
SQLRETURN r = SQL_SUCCESS;
|
||||
do {
|
||||
const char *sql = "insert into m.v (ts, v1, ts2) values (?, ?, ?)";
|
||||
r = SQLPrepare(stmt, (SQLCHAR*)sql, SQL_NTS);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
|
||||
record_t records[] = {
|
||||
{0, "2020-01-03 11:22:33.345", SQL_NTS, 1, sizeof(int32_t), "2020-01-02 11:22:33.455", SQL_NTS},
|
||||
{0, "2020-01-03 11:22:34.346", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS},
|
||||
{0, "2020-01-04 11:22:34.345", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS},
|
||||
{0, "2020-01-05 11:22:34.345", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS},
|
||||
};
|
||||
|
||||
record_t *base = (record_t*)0;
|
||||
|
||||
r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(base->ts)-1, 0, base->ts, sizeof(base->ts), &(base->ts_len));
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &base->v1, 0, &(base->v1_len));
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(base->ts2)-1, 0, base->ts2, sizeof(base->ts2), &(base->ts2_len));
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
|
||||
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)sizeof(*base), 0);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
|
||||
SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)(sizeof(records)/sizeof(records[0])), 0);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
|
||||
record_t *record = NULL;
|
||||
|
||||
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &record, 0);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
|
||||
record = records;
|
||||
|
||||
r = SQLExecute(stmt);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
} while (0);
|
||||
|
||||
return r ? -1 : 0;
|
||||
}
|
||||
|
||||
static int do_prepare_in_conn(SQLHENV env, SQLHDBC conn) {
|
||||
SQLHSTMT stmt = {0};
|
||||
CHK_TEST(create_statement(env, conn, &stmt));
|
||||
int r = do_prepare_in_stmt(env, conn, stmt);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
static int do_prepare(const char *dsn, const char *uid, const char *pwd, const char *connstr) {
|
||||
int r = 0;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
if (dsn) {
|
||||
CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn));
|
||||
} else {
|
||||
CHK_TEST(open_driver_connect(connstr, &env, &conn));
|
||||
}
|
||||
|
||||
r = do_prepare_in_conn(env, conn);
|
||||
|
||||
SQLDisconnect(conn);
|
||||
SQLFreeConnect(conn);
|
||||
SQLFreeEnv(env);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int dummy;
|
||||
int64_t ts;
|
||||
SQLLEN ts_len;
|
||||
int8_t v1;
|
||||
SQLLEN v1_len;
|
||||
int16_t v2;
|
||||
SQLLEN v2_len;
|
||||
int32_t v4;
|
||||
SQLLEN v4_len;
|
||||
int64_t v8;
|
||||
SQLLEN v8_len;
|
||||
} test_v_t;
|
||||
|
||||
static int do_insert_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, int64_t *ts, insert_arg_t *arg) {
|
||||
SQLRETURN r = SQL_SUCCESS;
|
||||
int batch_size = arg->batch_size;
|
||||
test_v_t *recs = NULL;
|
||||
do {
|
||||
const char *sql = "insert into test.v (ts, v1, v2, v4, v8) values (?, ?, ?, ?, ?)";
|
||||
r = SQLPrepare(stmt, (SQLCHAR*)sql, SQL_NTS);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
|
||||
test_v_t *base = NULL;
|
||||
|
||||
r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &base->ts, 0, &base->ts_len);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, 0, 0, &base->v1, 0, &base->v1_len);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, 0, 0, &base->v2, 0, &base->v2_len);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &base->v4, 0, &base->v4_len);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &base->v8, 0, &base->v8_len);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
|
||||
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)sizeof(*base), 0);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
|
||||
base = NULL;
|
||||
|
||||
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &base, 0);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
|
||||
size_t n_recs = (size_t)batch_size;
|
||||
recs = (test_v_t*)calloc(n_recs, sizeof(*recs));
|
||||
OILE(recs, "");
|
||||
|
||||
SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)n_recs, 0);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
|
||||
base = recs;
|
||||
|
||||
for (int batch=0; 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) {
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
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 do_debug_col_name_max_len(const char *dsn, const char *uid, const char *pwd, const char *connstr) {
|
||||
SQLRETURN r;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
r = SQLAllocEnv(&env);
|
||||
if (r!=SQL_SUCCESS) {
|
||||
D("SQLAllocEnv failed");
|
||||
return 1;
|
||||
};
|
||||
do {
|
||||
r = SQLAllocConnect(env, &conn);
|
||||
CHK_RESULT(r, SQL_HANDLE_ENV, env, "");
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
do {
|
||||
if (dsn) {
|
||||
r = SQLConnect(conn, (SQLCHAR*)dsn, (SQLSMALLINT)(dsn ? strlen(dsn) : 0),
|
||||
(SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0),
|
||||
(SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0));
|
||||
} else {
|
||||
SQLCHAR buf[4096];
|
||||
SQLSMALLINT blen = 0;
|
||||
SQLHDBC ConnectionHandle = conn;
|
||||
SQLHWND WindowHandle = NULL;
|
||||
SQLCHAR * InConnectionString = (SQLCHAR*)connstr;
|
||||
SQLSMALLINT StringLength1 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0);
|
||||
SQLCHAR * OutConnectionString = buf;
|
||||
SQLSMALLINT BufferLength = sizeof(buf);
|
||||
SQLSMALLINT * StringLength2Ptr = &blen;
|
||||
SQLUSMALLINT DriverCompletion = SQL_DRIVER_NOPROMPT;
|
||||
r = SQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString,
|
||||
StringLength1, OutConnectionString, BufferLength,
|
||||
StringLength2Ptr, DriverCompletion);
|
||||
}
|
||||
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
D("connected");
|
||||
if (1) {
|
||||
SQLSMALLINT maxColumnNameLength = 0;
|
||||
SQLSMALLINT len = 0;
|
||||
r = SQLGetInfo(conn, SQL_MAX_COLUMN_NAME_LEN, &maxColumnNameLength, sizeof(SQLSMALLINT), &len);
|
||||
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
D("maxColumnNameLength: %d", maxColumnNameLength);
|
||||
}
|
||||
} while (0);
|
||||
SQLFreeConnect(conn);
|
||||
conn = NULL;
|
||||
} while (0);
|
||||
SQLFreeEnv(env);
|
||||
env = NULL;
|
||||
|
||||
return (r==SQL_SUCCESS) ? 0 : 1;
|
||||
}
|
||||
|
||||
void usage(const char *arg0) {
|
||||
fprintf(stdout, "%s usage:\n", arg0);
|
||||
fprintf(stdout, "%s [--dsn <dsn>] [--uid <uid>] [--pwd <pwd>] [-C <conn_str>] [--sts <sts>]\n", arg0);
|
||||
fprintf(stdout, " --dsn <dsn>: DSN\n");
|
||||
fprintf(stdout, " --uid <uid>: UID\n");
|
||||
fprintf(stdout, " --pwd <pwd>: PWD\n");
|
||||
fprintf(stdout, " -C <conn_str>: driver connection string\n");
|
||||
fprintf(stdout, " --sts <sts>: file where statements store\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
srand((unsigned)time(0));
|
||||
const char *conn_str = NULL;
|
||||
const char *dsn = NULL;
|
||||
const char *uid = NULL;
|
||||
const char *pwd = NULL;
|
||||
const char *sts = NULL; // statements file
|
||||
int debug_col_name_max_len = 0;
|
||||
int prepare = 0;
|
||||
int insert = 0;
|
||||
insert_arg_t insert_arg = {
|
||||
.batch_size = 100,
|
||||
.batchs = 100,
|
||||
.keep_stmt_among_batchs = 0
|
||||
};
|
||||
for (size_t i=1; i<argc; ++i) {
|
||||
const char *arg = argv[i];
|
||||
if (strcmp(arg, "-h")==0) {
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(arg, "-d")==0) {
|
||||
debug_col_name_max_len = 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) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<dsn> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
if (conn_str) {
|
||||
D("-C has already been specified");
|
||||
return 1;
|
||||
}
|
||||
dsn = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--uid")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<uid> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
uid = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--pwd")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<pwd> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
pwd = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "-C")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<connection string> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
if (dsn || uid || pwd) {
|
||||
D("either of --dsn/--uid/--pwd has already been specified");
|
||||
return 1;
|
||||
}
|
||||
conn_str = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--sts")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<sts> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
sts = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "-p")==0) {
|
||||
prepare = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (debug_col_name_max_len) {
|
||||
int r = do_debug_col_name_max_len(dsn, uid, pwd, conn_str);
|
||||
if (r) return 1;
|
||||
}
|
||||
if (insert) {
|
||||
int r = do_insert_batch(dsn, uid, pwd, conn_str, &insert_arg);
|
||||
if (r) return 1;
|
||||
}
|
||||
if (sts) {
|
||||
int r = test_sqls(dsn, uid, pwd, conn_str, sts);
|
||||
if (r) return 1;
|
||||
}
|
||||
if (prepare) {
|
||||
int r = do_prepare(dsn, uid, pwd, conn_str);
|
||||
if (r) return 1;
|
||||
}
|
||||
D("Done!");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,654 @@
|
|||
/*******************************************************************************
|
||||
/* ODBCSQL: a sample program that implements an ODBC command line interpreter.
|
||||
/*
|
||||
/* USAGE: ODBCSQL DSN=<dsn name> or
|
||||
/* ODBCSQL FILEDSN=<file dsn> or
|
||||
/* ODBCSQL DRIVER={driver name}
|
||||
/*
|
||||
/*
|
||||
/* Copyright(c) Microsoft Corporation. This is a WDAC sample program and
|
||||
/* is not suitable for use in production environments.
|
||||
/*
|
||||
/******************************************************************************/
|
||||
/* Modules:
|
||||
/* Main Main driver loop, executes queries.
|
||||
/* DisplayResults Display the results of the query if any
|
||||
/* AllocateBindings Bind column data
|
||||
/* DisplayTitles Print column titles
|
||||
/* SetConsole Set console display mode
|
||||
/* HandleError Show ODBC error messages
|
||||
/******************************************************************************/
|
||||
|
||||
#define _UNICODE
|
||||
#define UNICODE
|
||||
|
||||
#include <windows.h>
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include <stdio.h>
|
||||
#include <conio.h>
|
||||
#include <tchar.h>
|
||||
#include <stdlib.h>
|
||||
#include <sal.h>
|
||||
|
||||
|
||||
/*******************************************/
|
||||
/* Macro to call ODBC functions and */
|
||||
/* report an error on failure. */
|
||||
/* Takes handle, handle type, and stmt */
|
||||
/*******************************************/
|
||||
|
||||
#define TRYODBC(h, ht, x) { RETCODE rc = x;\
|
||||
if (rc != SQL_SUCCESS) \
|
||||
{ \
|
||||
HandleDiagnosticRecord (h, ht, rc); \
|
||||
} \
|
||||
if (rc == SQL_ERROR) \
|
||||
{ \
|
||||
fwprintf(stderr, L"Error in " L#x L"\n"); \
|
||||
goto Exit; \
|
||||
} \
|
||||
}
|
||||
/******************************************/
|
||||
/* Structure to store information about */
|
||||
/* a column.
|
||||
/******************************************/
|
||||
|
||||
typedef struct STR_BINDING {
|
||||
SQLSMALLINT cDisplaySize; /* size to display */
|
||||
WCHAR *wszBuffer; /* display buffer */
|
||||
SQLLEN indPtr; /* size or null */
|
||||
BOOL fChar; /* character col? */
|
||||
struct STR_BINDING *sNext; /* linked list */
|
||||
} BINDING;
|
||||
|
||||
|
||||
|
||||
/******************************************/
|
||||
/* Forward references */
|
||||
/******************************************/
|
||||
|
||||
void HandleDiagnosticRecord (SQLHANDLE hHandle,
|
||||
SQLSMALLINT hType,
|
||||
RETCODE RetCode);
|
||||
|
||||
void DisplayResults(HSTMT hStmt,
|
||||
SQLSMALLINT cCols);
|
||||
|
||||
void AllocateBindings(HSTMT hStmt,
|
||||
SQLSMALLINT cCols,
|
||||
BINDING** ppBinding,
|
||||
SQLSMALLINT* pDisplay);
|
||||
|
||||
|
||||
void DisplayTitles(HSTMT hStmt,
|
||||
DWORD cDisplaySize,
|
||||
BINDING* pBinding);
|
||||
|
||||
void SetConsole(DWORD cDisplaySize,
|
||||
BOOL fInvert);
|
||||
|
||||
/*****************************************/
|
||||
/* Some constants */
|
||||
/*****************************************/
|
||||
|
||||
|
||||
#define DISPLAY_MAX 50 // Arbitrary limit on column width to display
|
||||
#define DISPLAY_FORMAT_EXTRA 3 // Per column extra display bytes (| <data> )
|
||||
#define DISPLAY_FORMAT L"%c %*.*s "
|
||||
#define DISPLAY_FORMAT_C L"%c %-*.*s "
|
||||
#define NULL_SIZE 6 // <NULL>
|
||||
#define SQL_QUERY_SIZE 1000 // Max. Num characters for SQL Query passed in.
|
||||
|
||||
#define PIPE L'|'
|
||||
|
||||
SHORT gHeight = 80; // Users screen height
|
||||
|
||||
int __cdecl wmain(int argc, _In_reads_(argc) WCHAR **argv)
|
||||
{
|
||||
SQLHENV hEnv = NULL;
|
||||
SQLHDBC hDbc = NULL;
|
||||
SQLHSTMT hStmt = NULL;
|
||||
WCHAR* pwszConnStr;
|
||||
WCHAR wszInput[SQL_QUERY_SIZE];
|
||||
|
||||
// Allocate an environment
|
||||
|
||||
if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv) == SQL_ERROR)
|
||||
{
|
||||
fwprintf(stderr, L"Unable to allocate an environment handle\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Register this as an application that expects 3.x behavior,
|
||||
// you must register something if you use AllocHandle
|
||||
|
||||
TRYODBC(hEnv,
|
||||
SQL_HANDLE_ENV,
|
||||
SQLSetEnvAttr(hEnv,
|
||||
SQL_ATTR_ODBC_VERSION,
|
||||
(SQLPOINTER)SQL_OV_ODBC3,
|
||||
0));
|
||||
|
||||
// Allocate a connection
|
||||
TRYODBC(hEnv,
|
||||
SQL_HANDLE_ENV,
|
||||
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
pwszConnStr = *++argv;
|
||||
}
|
||||
else
|
||||
{
|
||||
pwszConnStr = L"";
|
||||
}
|
||||
|
||||
// Connect to the driver. Use the connection string if supplied
|
||||
// on the input, otherwise let the driver manager prompt for input.
|
||||
|
||||
TRYODBC(hDbc,
|
||||
SQL_HANDLE_DBC,
|
||||
SQLDriverConnect(hDbc,
|
||||
GetDesktopWindow(),
|
||||
pwszConnStr,
|
||||
SQL_NTS,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
SQL_DRIVER_COMPLETE));
|
||||
|
||||
fwprintf(stderr, L"Connected!\n");
|
||||
|
||||
TRYODBC(hDbc,
|
||||
SQL_HANDLE_DBC,
|
||||
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
|
||||
|
||||
wprintf(L"Enter SQL commands, type (control)Z to exit\nSQL COMMAND>");
|
||||
|
||||
// Loop to get input and execute queries
|
||||
|
||||
while(_fgetts(wszInput, SQL_QUERY_SIZE-1, stdin))
|
||||
{
|
||||
RETCODE RetCode;
|
||||
SQLSMALLINT sNumResults;
|
||||
|
||||
// Execute the query
|
||||
|
||||
if (!(*wszInput))
|
||||
{
|
||||
wprintf(L"SQL COMMAND>");
|
||||
continue;
|
||||
}
|
||||
RetCode = SQLExecDirect(hStmt,wszInput, SQL_NTS);
|
||||
|
||||
switch(RetCode)
|
||||
{
|
||||
case SQL_SUCCESS_WITH_INFO:
|
||||
{
|
||||
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
|
||||
// fall through
|
||||
}
|
||||
case SQL_SUCCESS:
|
||||
{
|
||||
// If this is a row-returning query, display
|
||||
// results
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLNumResultCols(hStmt,&sNumResults));
|
||||
|
||||
if (sNumResults > 0)
|
||||
{
|
||||
DisplayResults(hStmt,sNumResults);
|
||||
}
|
||||
else
|
||||
{
|
||||
SQLLEN cRowCount;
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLRowCount(hStmt,&cRowCount));
|
||||
|
||||
if (cRowCount >= 0)
|
||||
{
|
||||
wprintf(L"%Id %s affected\n",
|
||||
cRowCount,
|
||||
cRowCount == 1 ? L"row" : L"rows");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SQL_ERROR:
|
||||
{
|
||||
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
fwprintf(stderr, L"Unexpected return code %hd!\n", RetCode);
|
||||
|
||||
}
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLFreeStmt(hStmt, SQL_CLOSE));
|
||||
|
||||
wprintf(L"SQL COMMAND>");
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
// Free ODBC handles and exit
|
||||
|
||||
if (hStmt)
|
||||
{
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
||||
}
|
||||
|
||||
if (hDbc)
|
||||
{
|
||||
SQLDisconnect(hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
|
||||
}
|
||||
|
||||
if (hEnv)
|
||||
{
|
||||
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
|
||||
}
|
||||
|
||||
wprintf(L"\nDisconnected.");
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
/* DisplayResults: display results of a select query
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt ODBC statement handle
|
||||
/* cCols Count of columns
|
||||
/************************************************************************/
|
||||
|
||||
void DisplayResults(HSTMT hStmt,
|
||||
SQLSMALLINT cCols)
|
||||
{
|
||||
BINDING *pFirstBinding, *pThisBinding;
|
||||
SQLSMALLINT cDisplaySize;
|
||||
RETCODE RetCode = SQL_SUCCESS;
|
||||
int iCount = 0;
|
||||
|
||||
// Allocate memory for each column
|
||||
|
||||
AllocateBindings(hStmt, cCols, &pFirstBinding, &cDisplaySize);
|
||||
|
||||
// Set the display mode and write the titles
|
||||
|
||||
DisplayTitles(hStmt, cDisplaySize+1, pFirstBinding);
|
||||
|
||||
|
||||
// Fetch and display the data
|
||||
|
||||
bool fNoData = false;
|
||||
|
||||
do {
|
||||
// Fetch a row
|
||||
|
||||
if (iCount++ >= gHeight - 2)
|
||||
{
|
||||
int nInputChar;
|
||||
bool fEnterReceived = false;
|
||||
|
||||
while(!fEnterReceived)
|
||||
{
|
||||
wprintf(L" ");
|
||||
SetConsole(cDisplaySize+2, TRUE);
|
||||
wprintf(L" Press ENTER to continue, Q to quit (height:%hd)", gHeight);
|
||||
SetConsole(cDisplaySize+2, FALSE);
|
||||
|
||||
nInputChar = _getch();
|
||||
wprintf(L"\n");
|
||||
if ((nInputChar == 'Q') || (nInputChar == 'q'))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
else if ('\r' == nInputChar)
|
||||
{
|
||||
fEnterReceived = true;
|
||||
}
|
||||
// else loop back to display prompt again
|
||||
}
|
||||
|
||||
iCount = 1;
|
||||
DisplayTitles(hStmt, cDisplaySize+1, pFirstBinding);
|
||||
}
|
||||
|
||||
TRYODBC(hStmt, SQL_HANDLE_STMT, RetCode = SQLFetch(hStmt));
|
||||
|
||||
if (RetCode == SQL_NO_DATA_FOUND)
|
||||
{
|
||||
fNoData = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Display the data. Ignore truncations
|
||||
|
||||
for (pThisBinding = pFirstBinding;
|
||||
pThisBinding;
|
||||
pThisBinding = pThisBinding->sNext)
|
||||
{
|
||||
if (pThisBinding->indPtr != SQL_NULL_DATA)
|
||||
{
|
||||
wprintf(pThisBinding->fChar ? DISPLAY_FORMAT_C:DISPLAY_FORMAT,
|
||||
PIPE,
|
||||
pThisBinding->cDisplaySize,
|
||||
pThisBinding->cDisplaySize,
|
||||
pThisBinding->wszBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
wprintf(DISPLAY_FORMAT_C,
|
||||
PIPE,
|
||||
pThisBinding->cDisplaySize,
|
||||
pThisBinding->cDisplaySize,
|
||||
L"<NULL>");
|
||||
}
|
||||
}
|
||||
wprintf(L" %c\n",PIPE);
|
||||
}
|
||||
} while (!fNoData);
|
||||
|
||||
SetConsole(cDisplaySize+2, TRUE);
|
||||
wprintf(L"%*.*s", cDisplaySize+2, cDisplaySize+2, L" ");
|
||||
SetConsole(cDisplaySize+2, FALSE);
|
||||
wprintf(L"\n");
|
||||
|
||||
Exit:
|
||||
// Clean up the allocated buffers
|
||||
|
||||
while (pFirstBinding)
|
||||
{
|
||||
pThisBinding = pFirstBinding->sNext;
|
||||
free(pFirstBinding->wszBuffer);
|
||||
free(pFirstBinding);
|
||||
pFirstBinding = pThisBinding;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
/* AllocateBindings: Get column information and allocate bindings
|
||||
/* for each column.
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt Statement handle
|
||||
/* cCols Number of columns in the result set
|
||||
/* *lppBinding Binding pointer (returned)
|
||||
/* lpDisplay Display size of one line
|
||||
/************************************************************************/
|
||||
|
||||
void AllocateBindings(HSTMT hStmt,
|
||||
SQLSMALLINT cCols,
|
||||
BINDING **ppBinding,
|
||||
SQLSMALLINT *pDisplay)
|
||||
{
|
||||
SQLSMALLINT iCol;
|
||||
BINDING *pThisBinding, *pLastBinding = NULL;
|
||||
SQLLEN cchDisplay, ssType;
|
||||
SQLSMALLINT cchColumnNameLength;
|
||||
|
||||
*pDisplay = 0;
|
||||
|
||||
for (iCol = 1; iCol <= cCols; iCol++)
|
||||
{
|
||||
pThisBinding = (BINDING *)(malloc(sizeof(BINDING)));
|
||||
if (!(pThisBinding))
|
||||
{
|
||||
fwprintf(stderr, L"Out of memory!\n");
|
||||
exit(-100);
|
||||
}
|
||||
|
||||
if (iCol == 1)
|
||||
{
|
||||
*ppBinding = pThisBinding;
|
||||
}
|
||||
else
|
||||
{
|
||||
pLastBinding->sNext = pThisBinding;
|
||||
}
|
||||
pLastBinding = pThisBinding;
|
||||
|
||||
|
||||
// Figure out the display length of the column (we will
|
||||
// bind to char since we are only displaying data, in general
|
||||
// you should bind to the appropriate C type if you are going
|
||||
// to manipulate data since it is much faster...)
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLColAttribute(hStmt,
|
||||
iCol,
|
||||
SQL_DESC_DISPLAY_SIZE,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&cchDisplay));
|
||||
|
||||
|
||||
// Figure out if this is a character or numeric column; this is
|
||||
// used to determine if we want to display the data left- or right-
|
||||
// aligned.
|
||||
|
||||
// SQL_DESC_CONCISE_TYPE maps to the 1.x SQL_COLUMN_TYPE.
|
||||
// This is what you must use if you want to work
|
||||
// against a 2.x driver.
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLColAttribute(hStmt,
|
||||
iCol,
|
||||
SQL_DESC_CONCISE_TYPE,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&ssType));
|
||||
|
||||
pThisBinding->fChar = (ssType == SQL_CHAR ||
|
||||
ssType == SQL_VARCHAR ||
|
||||
ssType == SQL_LONGVARCHAR);
|
||||
|
||||
pThisBinding->sNext = NULL;
|
||||
|
||||
// Arbitrary limit on display size
|
||||
if (cchDisplay > DISPLAY_MAX)
|
||||
cchDisplay = DISPLAY_MAX;
|
||||
|
||||
// Allocate a buffer big enough to hold the text representation
|
||||
// of the data. Add one character for the null terminator
|
||||
|
||||
pThisBinding->wszBuffer = (WCHAR *)malloc((cchDisplay+1) * sizeof(WCHAR));
|
||||
|
||||
if (!(pThisBinding->wszBuffer))
|
||||
{
|
||||
fwprintf(stderr, L"Out of memory!\n");
|
||||
exit(-100);
|
||||
}
|
||||
|
||||
// Map this buffer to the driver's buffer. At Fetch time,
|
||||
// the driver will fill in this data. Note that the size is
|
||||
// count of bytes (for Unicode). All ODBC functions that take
|
||||
// SQLPOINTER use count of bytes; all functions that take only
|
||||
// strings use count of characters.
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLBindCol(hStmt,
|
||||
iCol,
|
||||
SQL_C_TCHAR,
|
||||
(SQLPOINTER) pThisBinding->wszBuffer,
|
||||
(cchDisplay + 1) * sizeof(WCHAR),
|
||||
&pThisBinding->indPtr));
|
||||
|
||||
|
||||
// Now set the display size that we will use to display
|
||||
// the data. Figure out the length of the column name
|
||||
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLColAttribute(hStmt,
|
||||
iCol,
|
||||
SQL_DESC_NAME,
|
||||
NULL,
|
||||
0,
|
||||
&cchColumnNameLength,
|
||||
NULL));
|
||||
|
||||
pThisBinding->cDisplaySize = max((SQLSMALLINT)cchDisplay, cchColumnNameLength);
|
||||
if (pThisBinding->cDisplaySize < NULL_SIZE)
|
||||
pThisBinding->cDisplaySize = NULL_SIZE;
|
||||
|
||||
*pDisplay += pThisBinding->cDisplaySize + DISPLAY_FORMAT_EXTRA;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
Exit:
|
||||
|
||||
exit(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
/* DisplayTitles: print the titles of all the columns and set the
|
||||
/* shell window's width
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt Statement handle
|
||||
/* cDisplaySize Total display size
|
||||
/* pBinding list of binding information
|
||||
/************************************************************************/
|
||||
|
||||
void DisplayTitles(HSTMT hStmt,
|
||||
DWORD cDisplaySize,
|
||||
BINDING *pBinding)
|
||||
{
|
||||
WCHAR wszTitle[DISPLAY_MAX];
|
||||
SQLSMALLINT iCol = 1;
|
||||
|
||||
SetConsole(cDisplaySize+2, TRUE);
|
||||
|
||||
for (; pBinding; pBinding = pBinding->sNext)
|
||||
{
|
||||
TRYODBC(hStmt,
|
||||
SQL_HANDLE_STMT,
|
||||
SQLColAttribute(hStmt,
|
||||
iCol++,
|
||||
SQL_DESC_NAME,
|
||||
wszTitle,
|
||||
sizeof(wszTitle), // Note count of bytes!
|
||||
NULL,
|
||||
NULL));
|
||||
|
||||
wprintf(DISPLAY_FORMAT_C,
|
||||
PIPE,
|
||||
pBinding->cDisplaySize,
|
||||
pBinding->cDisplaySize,
|
||||
wszTitle);
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
wprintf(L" %c", PIPE);
|
||||
SetConsole(cDisplaySize+2, FALSE);
|
||||
wprintf(L"\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
/* SetConsole: sets console display size and video mode
|
||||
/*
|
||||
/* Parameters
|
||||
/* siDisplaySize Console display size
|
||||
/* fInvert Invert video?
|
||||
/************************************************************************/
|
||||
|
||||
void SetConsole(DWORD dwDisplaySize,
|
||||
BOOL fInvert)
|
||||
{
|
||||
HANDLE hConsole;
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbInfo;
|
||||
|
||||
// Reset the console screen buffer size if necessary
|
||||
|
||||
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (hConsole != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (GetConsoleScreenBufferInfo(hConsole, &csbInfo))
|
||||
{
|
||||
if (csbInfo.dwSize.X < (SHORT) dwDisplaySize)
|
||||
{
|
||||
csbInfo.dwSize.X = (SHORT) dwDisplaySize;
|
||||
SetConsoleScreenBufferSize(hConsole, csbInfo.dwSize);
|
||||
}
|
||||
|
||||
gHeight = csbInfo.dwSize.Y;
|
||||
}
|
||||
|
||||
if (fInvert)
|
||||
{
|
||||
SetConsoleTextAttribute(hConsole, (WORD)(csbInfo.wAttributes | BACKGROUND_BLUE));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetConsoleTextAttribute(hConsole, (WORD)(csbInfo.wAttributes & ~(BACKGROUND_BLUE)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
/* HandleDiagnosticRecord : display error/warning information
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hHandle ODBC handle
|
||||
/* hType Type of handle (HANDLE_STMT, HANDLE_ENV, HANDLE_DBC)
|
||||
/* RetCode Return code of failing command
|
||||
/************************************************************************/
|
||||
|
||||
void HandleDiagnosticRecord (SQLHANDLE hHandle,
|
||||
SQLSMALLINT hType,
|
||||
RETCODE RetCode)
|
||||
{
|
||||
SQLSMALLINT iRec = 0;
|
||||
SQLINTEGER iError;
|
||||
WCHAR wszMessage[1000];
|
||||
WCHAR wszState[SQL_SQLSTATE_SIZE+1];
|
||||
|
||||
|
||||
if (RetCode == SQL_INVALID_HANDLE)
|
||||
{
|
||||
fwprintf(stderr, L"Invalid handle!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (SQLGetDiagRec(hType,
|
||||
hHandle,
|
||||
++iRec,
|
||||
wszState,
|
||||
&iError,
|
||||
wszMessage,
|
||||
(SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
|
||||
(SQLSMALLINT *)NULL) == SQL_SUCCESS)
|
||||
{
|
||||
// Hide data truncated..
|
||||
if (wcsncmp(wszState, L"01004", 5))
|
||||
{
|
||||
fwprintf(stderr, L"[%5.5s] %s (%d)\n", wszState, wszMessage, iError);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
_ "github.com/alexbrainman/odbc"
|
||||
)
|
||||
|
||||
var pool *sql.DB // Database connection pool.
|
||||
|
||||
func main() {
|
||||
id := flag.Int64("id", 32768, "person ID to find")
|
||||
dsn := flag.String("dsn", os.Getenv("DSN"), "connection data source name")
|
||||
flag.Parse()
|
||||
|
||||
if len(*dsn) == 0 {
|
||||
log.Fatal("missing dsn flag")
|
||||
}
|
||||
if *id == 0 {
|
||||
log.Fatal("missing person ID")
|
||||
}
|
||||
var err error
|
||||
|
||||
// Opening a driver typically will not attempt to connect to the database.
|
||||
pool, err = sql.Open("odbc", *dsn)
|
||||
if err != nil {
|
||||
// This will not be a connection error, but a DSN parse error or
|
||||
// another initialization error.
|
||||
log.Fatal("unable to use data source name", err)
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
pool.SetConnMaxLifetime(0)
|
||||
pool.SetMaxIdleConns(3)
|
||||
pool.SetMaxOpenConns(3)
|
||||
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
|
||||
appSignal := make(chan os.Signal, 3)
|
||||
signal.Notify(appSignal, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-appSignal:
|
||||
stop()
|
||||
}
|
||||
}()
|
||||
|
||||
Ping(ctx)
|
||||
|
||||
Query(ctx, *id)
|
||||
}
|
||||
|
||||
// Ping the database to verify DSN provided by the user is valid and the
|
||||
// server accessible. If the ping fails exit the program with an error.
|
||||
func Ping(ctx context.Context) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := pool.PingContext(ctx); err != nil {
|
||||
log.Fatalf("unable to connect to database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Query the database for the information requested and prints the results.
|
||||
// If the query fails exit the program with an error.
|
||||
func Query(ctx context.Context, id int64) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var name string
|
||||
err := pool.QueryRowContext(ctx, "select name from m.t").Scan(&name)
|
||||
if err != nil {
|
||||
log.Fatal("unable to execute search query", err)
|
||||
}
|
||||
log.Println("name=", name)
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const odbc = require('odbc');
|
||||
const path = require('path');
|
||||
|
||||
function usage() {
|
||||
var arg = path.basename(process.argv[1]);
|
||||
console.error(`usage:`);
|
||||
console.error(`${arg} --DSN <DSN> --UID <uid> --PWD <pwd> --Server <host:port>`);
|
||||
console.error(`${arg} -C <conn_str>`);
|
||||
console.error(` conn_str eg: 'DSN={TAOS_DSN};UID=root;PWD=taosdata;Server=host:port'`);
|
||||
}
|
||||
|
||||
var cfg = { };
|
||||
|
||||
if (process.argv.length==2) {
|
||||
usage();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var i;
|
||||
for (i=2; i<process.argv.length; ++i) {
|
||||
var arg = process.argv[i];
|
||||
if (arg=='-h') {
|
||||
usage();
|
||||
process.exit(0);
|
||||
}
|
||||
if (arg=="--DSN") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <dns> after --DSN but got nothing`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.dsn = arg;
|
||||
continue;
|
||||
}
|
||||
if (arg=="--UID") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <uid> after --UID but got nothing`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.uid = arg;
|
||||
continue;
|
||||
}
|
||||
if (arg=="--PWD") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <pwd> after --PWD but got nothing`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.pwd = arg;
|
||||
continue;
|
||||
}
|
||||
if (arg=="--Server") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <host:port> after --Server but got nothing`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.server = arg;
|
||||
continue;
|
||||
}
|
||||
if (arg=="-C") {
|
||||
++i;
|
||||
if (i>=process.argv.length) {
|
||||
console.error(`expecting <conn_str> after -C but got nothing`);
|
||||
console.error(` conn_str eg: 'DSN={TAOS_DSN};UID=root;PWD=taosdata;Server=host:port'`);
|
||||
process.exit(1);
|
||||
}
|
||||
arg = process.argv[i];
|
||||
cfg.conn_str = arg;
|
||||
continue;
|
||||
}
|
||||
console.error(`unknown argument: [${arg}]`);
|
||||
usage(process.argv[1]);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var connectionString = cfg.conn_str;
|
||||
|
||||
if (!cfg.conn_str) {
|
||||
connectionString = `DSN={${cfg.dsn}}; UID=${cfg.uid}; PWD=${cfg.pwd}; Server=${cfg.server}`;
|
||||
}
|
||||
|
||||
(async function () {
|
||||
const connStr = connectionString;
|
||||
try {
|
||||
console.log(`connecting [${connStr}]...`);
|
||||
const connection = await odbc.connect(connStr);
|
||||
await connection.query('create database if not exists m');
|
||||
await connection.query('use m');
|
||||
await connection.query('drop table if exists t');
|
||||
await connection.query('create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), name nchar(3))');
|
||||
await connection.query('insert into t values("2020-01-02 12:34:56.781", 1, 127, 32767, 32768, 32769, 123.456, 789.987, "hello", "我和你")');
|
||||
console.log('.........');
|
||||
result = await connection.query('select * from t');
|
||||
console.log(result[0]);
|
||||
|
||||
|
||||
statement = await connection.createStatement();
|
||||
await statement.prepare('INSERT INTO t (ts, v1) VALUES(?, ?)');
|
||||
await statement.bind(['2020-02-02 11:22:33.449', 89]);
|
||||
result = await statement.execute();
|
||||
console.log(result);
|
||||
|
||||
result = await connection.query('select * from t');
|
||||
console.log(result[0]);
|
||||
console.log(result[1]);
|
||||
} catch (e) {
|
||||
console.log('error:', e);
|
||||
}
|
||||
})();
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"odbc": "^2.3.6"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package.cpath = package.cpath .. ";/usr/local/lib/lib?.dylib"
|
||||
-- load driver
|
||||
local driver = require "luasql.odbc"
|
||||
-- create environment object
|
||||
env = assert (driver.odbc())
|
||||
-- connect to data source
|
||||
con = assert (env:connect("TAOS_DSN", "root", "taosdata"))
|
||||
-- reset our table
|
||||
-- res = con:execute"DROP TABLE people"
|
||||
-- res = assert (con:execute[[
|
||||
-- CREATE TABLE people(
|
||||
-- name varchar(50),
|
||||
-- email varchar(50)
|
||||
-- )
|
||||
-- ]])
|
||||
-- -- add a few elements
|
||||
-- list = {
|
||||
-- { name="Jose das Couves", email="jose@couves.com", },
|
||||
-- { name="Manoel Joaquim", email="manoel.joaquim@cafundo.com", },
|
||||
-- { name="Maria das Dores", email="maria@dores.com", },
|
||||
-- }
|
||||
-- for i, p in pairs (list) do
|
||||
-- res = assert (con:execute(string.format([[
|
||||
-- INSERT INTO people
|
||||
-- VALUES ('%s', '%s')]], p.name, p.email)
|
||||
-- ))
|
||||
-- end
|
||||
-- -- retrieve a cursor
|
||||
-- cur = assert (con:execute"SELECT name, email from people")
|
||||
-- -- print all rows, the rows will be indexed by field names
|
||||
-- row = cur:fetch ({}, "a")
|
||||
-- while row do
|
||||
-- print(string.format("Name: %s, E-mail: %s", row.name, row.email))
|
||||
-- -- reusing the table of results
|
||||
-- row = cur:fetch (row, "a")
|
||||
-- end
|
||||
cur = assert(con:execute"select * from m.t")
|
||||
row = cur:fetch({}, "a")
|
||||
while row do
|
||||
print(string.format("Name: %s", row.name))
|
||||
row = cur:fetch(row, "a")
|
||||
end
|
||||
|
||||
-- close everything
|
||||
cur:close() -- already closed because all the result set was consumed
|
||||
con:close()
|
||||
env:close()
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
import pyodbc
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser(description='Access TDengine via ODBC.')
|
||||
parser.add_argument('--DSN', help='DSN to use')
|
||||
parser.add_argument('--UID', help='UID to use')
|
||||
parser.add_argument('--PWD', help='PWD to use')
|
||||
parser.add_argument('--Server', help='Server to use')
|
||||
parser.add_argument('-C', metavar='CONNSTR', help='Connection string to use')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
a = 'DSN=%s'%args.DSN if args.DSN else None
|
||||
b = 'UID=%s'%args.UID if args.UID else None
|
||||
c = 'PWD=%s'%args.PWD if args.PWD else None
|
||||
d = 'Server=%s'%args.Server if args.Server else None
|
||||
conn_str = ';'.join(filter(None, [a,b,c,d])) if args.DSN else None
|
||||
conn_str = conn_str if conn_str else args.C
|
||||
if not conn_str:
|
||||
parser.print_help(file=sys.stderr)
|
||||
exit()
|
||||
|
||||
print('connecting: [%s]' % conn_str)
|
||||
cnxn = pyodbc.connect(conn_str, autocommit=True)
|
||||
cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("drop database if exists db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create database db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.mt (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), blob nchar(10))");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.mt values('2020-10-13 06:44:00.123', 1, 127, 32767, 2147483647, 32769, 123.456, 789.987, 'hello', 'helloworld')")
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00.234", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd129")
|
||||
##cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", 1502535178128, 9223372036854775807, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd123");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT INTO db.mt (ts,b,v1,v2,v4,v8,f4,f8,bin,blob) values (?,?,?,?,?,?,?,?,?,?)
|
||||
""",
|
||||
"2020-12-12 00:00:00",
|
||||
'true',
|
||||
'-127',
|
||||
'-32767',
|
||||
'-2147483647',
|
||||
'-9223372036854775807',
|
||||
'-1.23e10',
|
||||
'-11.23e6',
|
||||
'abcdefghij'.encode('utf-8'),
|
||||
"人啊大发测试及abc")
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("drop database if exists db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create database db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(4), blob nchar(4))");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hell', 'w我你z')")
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.v (ts timestamp, v1 tinyint, v2 smallint, name nchar(10), ts2 timestamp)")
|
||||
cursor.close()
|
||||
|
||||
params = [ ('2020-10-16 00:00:00.123', 19, '2111-01-02 01:02:03.123'),
|
||||
('2020-10-16 00:00:01', 41, '2111-01-02 01:02:03.423'),
|
||||
('2020-10-16 00:00:02', 57, '2111-01-02 01:02:03.153'),
|
||||
('2020-10-16 00:00:03.009', 26, '2111-01-02 01:02:03.623') ]
|
||||
cursor = cnxn.cursor()
|
||||
cursor.fast_executemany = True
|
||||
print('py:...................')
|
||||
cursor.executemany("insert into db.v (ts, v1, ts2) values (?, ?, ?)", params)
|
||||
print('py:...................')
|
||||
cursor.close()
|
||||
|
||||
## cursor = cnxn.cursor()
|
||||
## cursor.execute("SELECT * from db.v where v1 > ?", 4)
|
||||
## row = cursor.fetchone()
|
||||
## while row:
|
||||
## print(row)
|
||||
## row = cursor.fetchone()
|
||||
## cursor.close()
|
||||
##
|
||||
## cursor = cnxn.cursor()
|
||||
## cursor.execute("SELECT * from db.v where v1 > ?", '5')
|
||||
## row = cursor.fetchone()
|
||||
## while row:
|
||||
## print(row)
|
||||
## row = cursor.fetchone()
|
||||
## cursor.close()
|
||||
|
|
@ -0,0 +1 @@
|
|||
Cargo.lock
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "main"
|
||||
version = "0.1.0"
|
||||
authors = ["freemine <freemine@yeah.net>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
odbc = "0.17.0"
|
||||
env_logger = "0.8.2"
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
extern crate odbc;
|
||||
// Use this crate and set environmet variable RUST_LOG=odbc to see ODBC warnings
|
||||
extern crate env_logger;
|
||||
use odbc::*;
|
||||
use odbc_safe::AutocommitOn;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
|
||||
env_logger::init();
|
||||
|
||||
let conn_str = env::var("DSN").unwrap();
|
||||
match connect(&conn_str) {
|
||||
Ok(()) => println!("Success"),
|
||||
Err(diag) => println!("Error: {}", diag),
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(conn_str: &str) -> std::result::Result<(), DiagnosticRecord> {
|
||||
|
||||
let env = create_environment_v3().map_err(|e| e.unwrap())?;
|
||||
|
||||
let conn = env.connect_with_connection_string(conn_str)?;
|
||||
execute_statement(&conn)
|
||||
}
|
||||
|
||||
fn execute_statement<'env>(conn: &Connection<'env, AutocommitOn>) -> Result<()> {
|
||||
let stmt = Statement::with_parent(conn)?;
|
||||
|
||||
match stmt.exec_direct("select * from m.t")? {
|
||||
Data(mut stmt) => {
|
||||
let cols = stmt.num_result_cols()?;
|
||||
println!("cols: {}", cols);
|
||||
while let Some(mut cursor) = stmt.fetch()? {
|
||||
for i in 1..(cols + 1) {
|
||||
match cursor.get_data::<&str>(i as u16)? {
|
||||
Some(val) => print!(" {}", val),
|
||||
None => print!(" NULL"),
|
||||
}
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
NoData(_) => println!("Query executed, no data returned"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,12 +1,17 @@
|
|||
#P: positive sample
|
||||
#N: negative sample
|
||||
|
||||
P:drop database if exists m;
|
||||
P:create database m;
|
||||
P:use m;
|
||||
|
||||
P:drop table if exists t;
|
||||
P:create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(3), name nchar(1));
|
||||
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00', 0, 1);
|
||||
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.001', 1, 2);
|
||||
P:create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(3), name nchar(1), ts2 nchar(148));
|
||||
#P:insert into t (ts, blob, name) values('2020-10-10 00:00:00', 0, 1);
|
||||
#P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.001', 1, 2);
|
||||
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.002', '你', '好');
|
||||
P:insert into t (ts, blob, name) values('2020-10-10 00:00:00.003', 'abc', 'd');
|
||||
P:select * from t;
|
||||
P:create table v (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(3), name nchar(1), ts2 nchar(23));
|
||||
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
PROJECT(TDengine)
|
||||
|
||||
add_subdirectory(base)
|
||||
|
||||
IF (TD_LINUX_64)
|
||||
FLEX_TARGET(todbcFlexScanner
|
||||
todbc_scanner.l
|
||||
|
@ -15,12 +17,35 @@ IF (TD_LINUX_64)
|
|||
ADD_LIBRARY(todbc SHARED ${SRC} ${todbc_flex_scanner_src})
|
||||
SET_TARGET_PROPERTIES(todbc PROPERTIES CLEAN_DIRECT_OUTPUT 1)
|
||||
SET_TARGET_PROPERTIES(todbc PROPERTIES VERSION ${TD_VER_NUMBER} SOVERSION 1)
|
||||
TARGET_LINK_LIBRARIES(todbc taos odbcinst)
|
||||
TARGET_LINK_LIBRARIES(todbc todbc_base taos odbcinst)
|
||||
target_include_directories(todbc PUBLIC .)
|
||||
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/install.sh ${CMAKE_BINARY_DIR})")
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_DARWIN)
|
||||
FLEX_TARGET(todbcFlexScanner
|
||||
todbc_scanner.l
|
||||
${CMAKE_CURRENT_BINARY_DIR}/todbc_scanner.c
|
||||
)
|
||||
set(todbc_flex_scanner_src
|
||||
${FLEX_todbcFlexScanner_OUTPUTS}
|
||||
)
|
||||
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/todbc_scanner.c PROPERTIES COMPILE_OPTIONS "-Wno-conversion")
|
||||
AUX_SOURCE_DIRECTORY(. SRC)
|
||||
|
||||
# generate dynamic library (*.dylib)
|
||||
ADD_LIBRARY(todbc SHARED ${SRC} ${todbc_flex_scanner_src})
|
||||
SET_TARGET_PROPERTIES(todbc PROPERTIES CLEAN_DIRECT_OUTPUT 1)
|
||||
SET_TARGET_PROPERTIES(todbc PROPERTIES VERSION ${TD_VER_NUMBER} SOVERSION 1)
|
||||
TARGET_LINK_LIBRARIES(todbc todbc_base taos odbcinst)
|
||||
target_include_directories(todbc PUBLIC .)
|
||||
target_include_directories(todbc PRIVATE /usr/local/include)
|
||||
target_link_directories(todbc PUBLIC /usr/local/lib)
|
||||
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/install.sh ${CMAKE_BINARY_DIR})")
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_WINDOWS_64)
|
||||
FLEX_TARGET(todbcFlexScanner
|
||||
todbc_scanner.l
|
||||
|
@ -37,7 +62,7 @@ IF (TD_WINDOWS_64)
|
|||
${todbc_flex_scanner_src}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/todbc.rc
|
||||
todbc.def)
|
||||
TARGET_LINK_LIBRARIES(todbc taos_static odbccp32 legacy_stdio_definitions)
|
||||
TARGET_LINK_LIBRARIES(todbc todbc_base taos_static odbccp32 legacy_stdio_definitions)
|
||||
target_include_directories(todbc PUBLIC .)
|
||||
target_compile_definitions(todbc PRIVATE "todbc_EXPORT")
|
||||
|
||||
|
@ -52,3 +77,4 @@ IF (TD_WINDOWS_64)
|
|||
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.exp DESTINATION driver)
|
||||
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.dll DESTINATION driver)
|
||||
ENDIF ()
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
PROJECT(TDengine)
|
||||
|
||||
aux_source_directory(. SRC)
|
||||
add_library(todbc_base STATIC ${SRC})
|
||||
|
||||
if (TD_DARWIN)
|
||||
target_include_directories(todbc_base PRIVATE /usr/local/include)
|
||||
endif ()
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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_
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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_
|
||||
|
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@echo off
|
||||
REM install driver
|
||||
odbcconf /A {INSTALLDRIVER "TAOS|Driver=C:\TDengine\driver\todbc.dll|ConnectFunctions=YYN|DriverODBCVer=03.00|FileUsage=0|SQLLevel=0"}
|
||||
REM config user dsn
|
||||
odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN"}
|
||||
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
set -u
|
||||
|
||||
EXT="so"
|
||||
[[ `uname` == 'Darwin' ]] && EXT="dylib"
|
||||
|
||||
SUDO="sudo"
|
||||
[[ `uname` == 'Darwin' ]] && SUDO=""
|
||||
|
||||
|
||||
BLD_DIR="$1"
|
||||
|
||||
rm -f "${BLD_DIR}/template.ini"
|
||||
|
@ -10,18 +17,30 @@ rm -f "${BLD_DIR}/template.dsn"
|
|||
cat > "${BLD_DIR}/template.ini" <<EOF
|
||||
[TAOS]
|
||||
Description=taos odbc driver
|
||||
Driver=${BLD_DIR}/build/lib/libtodbc.so
|
||||
Driver=${BLD_DIR}/build/lib/libtodbc.${EXT}
|
||||
EOF
|
||||
|
||||
cat > "${BLD_DIR}/template.dsn" <<EOF
|
||||
[TAOS_DSN]
|
||||
Description=Connection to 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
|
||||
|
||||
# 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" &&
|
||||
echo "odbc install done"
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,13 +3,19 @@ SQLAllocEnv
|
|||
SQLFreeEnv
|
||||
SQLAllocConnect
|
||||
SQLFreeConnect
|
||||
SQLGetEnvAttr
|
||||
SQLSetEnvAttr
|
||||
SQLGetConnectAttr
|
||||
SQLGetConnectOption
|
||||
SQLGetInfo
|
||||
SQLConnect
|
||||
SQLDisconnect
|
||||
SQLAllocStmt
|
||||
SQLAllocHandle
|
||||
SQLFreeHandle
|
||||
SQLFreeStmt
|
||||
SQLExecDirect
|
||||
SQLExecDirectW
|
||||
;SQLExecDirectW
|
||||
SQLNumResultCols
|
||||
SQLRowCount
|
||||
SQLColAttribute
|
||||
|
@ -17,14 +23,45 @@ SQLGetData
|
|||
SQLFetch
|
||||
SQLPrepare
|
||||
SQLExecute
|
||||
SQLGetDiagField
|
||||
SQLParamData
|
||||
SQLPutData
|
||||
;SQLGetDiagField
|
||||
SQLGetDiagRec
|
||||
SQLBindParameter
|
||||
SQLDescribeParam
|
||||
SQLDriverConnect
|
||||
SQLSetConnectAttr
|
||||
SQLDescribeCol
|
||||
SQLBindCol
|
||||
SQLNumParams
|
||||
SQLSetStmtAttr
|
||||
SQLBindParam
|
||||
SQLCancel
|
||||
SQLCancelHandle
|
||||
SQLCloseCursor
|
||||
SQLColumns
|
||||
SQLCopyDesc
|
||||
SQLDataSources
|
||||
SQLEndTran
|
||||
;SQLError
|
||||
SQLFetchScroll
|
||||
SQLGetCursorName
|
||||
SQLGetDescField
|
||||
SQLGetDescRec
|
||||
;SQLGetFunctions
|
||||
SQLGetStmtAttr
|
||||
SQLGetStmtOption
|
||||
SQLGetTypeInfo
|
||||
SQLSetConnectOption
|
||||
SQLSetCursorName
|
||||
SQLSetDescField
|
||||
SQLSetDescRec
|
||||
SQLSetParam
|
||||
SQLSetStmtOption
|
||||
SQLSpecialColumns
|
||||
SQLStatistics
|
||||
SQLTables
|
||||
SQLTransact
|
||||
ConfigDSN
|
||||
ConfigTranslator
|
||||
ConfigDriver
|
||||
|
|
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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_
|
||||
|
|
@ -1,660 +0,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/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
@ -1,109 +0,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_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 @@
|
|||
#ifndef _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;
|
||||
struct conn_val_s {
|
||||
char *key;
|
||||
char *dsn;
|
||||
char *uid;
|
||||
char *pwd;
|
||||
char *db;
|
||||
char *server;
|
||||
char *svr_enc;
|
||||
char *cli_enc;
|
||||
char *dsn;
|
||||
char *uid;
|
||||
char *pwd;
|
||||
char *db;
|
||||
char *server;
|
||||
char *enc_local;
|
||||
char *enc_char;
|
||||
char *enc_wchar;
|
||||
char *enc_db;
|
||||
|
||||
map_tsdb_type_t tsdb_map;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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_
|
||||
|
|
@ -0,0 +1,682 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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 {
|
||||
char enc[64];
|
||||
int char_size; // character size at most
|
||||
int null_size; // size for null terminator
|
||||
int variable_char_size; // such as 3 for UTF8
|
||||
};
|
||||
|
||||
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_
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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_
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 @@
|
|||
|
||||
#include "os.h"
|
||||
|
||||
#define D(fmt, ...) \
|
||||
fprintf(stderr, \
|
||||
"%s[%d]:%s() " fmt "\n", \
|
||||
basename((char*)__FILE__), __LINE__, __func__, \
|
||||
##__VA_ARGS__)
|
||||
#ifdef __GNUC__
|
||||
__attribute__((format(printf, 6, 7)))
|
||||
#endif
|
||||
void todbc_log(const char *file, int line, const char *func, const char tag, int err, const char *fmt, ...);
|
||||
|
||||
#define DASSERT(statement) \
|
||||
do { \
|
||||
if (statement) break; \
|
||||
D("Assertion failure: %s", #statement); \
|
||||
abort(); \
|
||||
#define OL(tag, err, fmt, ...) todbc_log(__FILE__, __LINE__, __func__, tag, err, "%s" fmt "", "", ##__VA_ARGS__)
|
||||
#define OD(fmt, ...) OL('D', 0, fmt, ##__VA_ARGS__)
|
||||
#define OE(fmt, ...) OL('E', errno, fmt, ##__VA_ARGS__)
|
||||
#define OW(fmt, ...) OL('W', 0, fmt, ##__VA_ARGS__)
|
||||
#define OI(fmt, ...) OL('I', 0, fmt, ##__VA_ARGS__)
|
||||
#define OV(fmt, ...) OL('V', 0, fmt, ##__VA_ARGS__)
|
||||
#define OA(statement, fmt, ...) do { \
|
||||
if (statement) break; \
|
||||
OL('A', 0, "Assertion failure:[%s]; " fmt "", #statement, ##__VA_ARGS__); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
#define OILE(statement, fmt, ...) OA(statement, "internal logic error: [" fmt "]", ##__VA_ARGS__)
|
||||
#define ONIY(statement, fmt, ...) OA(statement, "not implemented yet: [" fmt "]", ##__VA_ARGS__)
|
||||
#define ONSP(statement, fmt, ...) OA(statement, "not support yet: [" fmt "]", ##__VA_ARGS__)
|
||||
|
||||
#define D(fmt, ...) OD(fmt, ##__VA_ARGS__)
|
||||
#define E(fmt, ...) OE(fmt, ##__VA_ARGS__)
|
||||
#define DASSERT(statement) OA(statement, "")
|
||||
#define DASSERTX(statement, fmt, ...) OA(statement, fmt, ##__VA_ARGS__)
|
||||
|
||||
|
||||
uint64_t todbc_get_threadid(void);
|
||||
|
||||
|
||||
#define DASSERTX(statement, fmt, ...) \
|
||||
do { \
|
||||
if (statement) break; \
|
||||
D("Assertion failure: %s, " fmt "", #statement, ##__VA_ARGS__); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
|
||||
#endif // _todbc_log_h_
|
||||
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
%{
|
||||
#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 <stdio.h>
|
||||
#include <odbcinst.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strncasecmp _strnicmp
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
static int process_map(const char *cfg, map_tsdb_type_t *tsdb_map, int type);
|
||||
|
||||
#define PUSH_STATE(state) yy_push_state(state, yyscanner)
|
||||
#define POP_STATE() yy_pop_state(yyscanner)
|
||||
|
@ -28,50 +35,73 @@ do { \
|
|||
while (yyleng) unput(yytext[yyleng-1]); \
|
||||
} while (0)
|
||||
|
||||
#define set_key() \
|
||||
do { \
|
||||
free(yyextra->key); \
|
||||
yyextra->key = strdup(yytext); \
|
||||
#define set_val() \
|
||||
do { \
|
||||
int r = 0; \
|
||||
int curr; TOP_STATE(curr); \
|
||||
POP_STATE(); \
|
||||
int state; TOP_STATE(state); \
|
||||
switch(state) { \
|
||||
case DSN: { \
|
||||
free(yyextra->dsn); \
|
||||
yyextra->dsn = strdup(yytext); \
|
||||
} break; \
|
||||
case UID: { \
|
||||
free(yyextra->uid); \
|
||||
yyextra->uid = strdup(yytext); \
|
||||
} break; \
|
||||
case PWD: { \
|
||||
free(yyextra->pwd); \
|
||||
yyextra->pwd = strdup(yytext); \
|
||||
} break; \
|
||||
case SERVER: { \
|
||||
free(yyextra->server); \
|
||||
yyextra->server = strdup(yytext); \
|
||||
} break; \
|
||||
case DB: { \
|
||||
free(yyextra->db); \
|
||||
yyextra->db = strdup(yytext); \
|
||||
} break; \
|
||||
case ENC_CHAR: { \
|
||||
free(yyextra->enc_char); \
|
||||
yyextra->enc_char = strdup(yytext); \
|
||||
} break; \
|
||||
case ENC_WCHAR: { \
|
||||
free(yyextra->enc_wchar); \
|
||||
yyextra->enc_wchar = strdup(yytext); \
|
||||
} break; \
|
||||
case ENC_DB: { \
|
||||
free(yyextra->enc_db); \
|
||||
yyextra->enc_db = strdup(yytext); \
|
||||
} break; \
|
||||
case ENC_LOCAL: { \
|
||||
free(yyextra->enc_local); \
|
||||
yyextra->enc_local = strdup(yytext); \
|
||||
} break; \
|
||||
case TSDB_FLOAT: { \
|
||||
if (process_map(yytext, &yyextra->tsdb_map, TSDB_FLOAT)) { \
|
||||
r = -1; \
|
||||
} \
|
||||
} break; \
|
||||
case TSDB_BIGINT: { \
|
||||
if (process_map(yytext, &yyextra->tsdb_map, TSDB_BIGINT)) { \
|
||||
r = -1; \
|
||||
} \
|
||||
} break; \
|
||||
case KEY: { \
|
||||
} break; \
|
||||
default: { \
|
||||
r = -1; \
|
||||
} break; \
|
||||
} \
|
||||
PUSH_STATE(curr); \
|
||||
if (r) return r; \
|
||||
} while (0)
|
||||
|
||||
#define set_val() \
|
||||
do { \
|
||||
if (!yyextra->key) break; \
|
||||
if (strcasecmp(yyextra->key, "DSN")==0) { \
|
||||
free(yyextra->dsn); \
|
||||
yyextra->dsn = strdup(yytext); \
|
||||
break; \
|
||||
} \
|
||||
if (strcasecmp(yyextra->key, "UID")==0) { \
|
||||
free(yyextra->uid); \
|
||||
yyextra->uid = strdup(yytext); \
|
||||
break; \
|
||||
} \
|
||||
if (strcasecmp(yyextra->key, "PWD")==0) { \
|
||||
free(yyextra->pwd); \
|
||||
yyextra->pwd = strdup(yytext); \
|
||||
break; \
|
||||
} \
|
||||
if (strcasecmp(yyextra->key, "DB")==0) { \
|
||||
free(yyextra->db); \
|
||||
yyextra->pwd = strdup(yytext); \
|
||||
break; \
|
||||
} \
|
||||
if (strcasecmp(yyextra->key, "Server")==0) { \
|
||||
free(yyextra->server); \
|
||||
yyextra->server = strdup(yytext); \
|
||||
break; \
|
||||
} \
|
||||
if (strcasecmp(yyextra->key, "SERVER_ENC")==0) { \
|
||||
free(yyextra->svr_enc); \
|
||||
yyextra->svr_enc = strdup(yytext); \
|
||||
break; \
|
||||
} \
|
||||
if (strcasecmp(yyextra->key, "CLIENT_ENC")==0) { \
|
||||
free(yyextra->cli_enc); \
|
||||
yyextra->cli_enc = strdup(yytext); \
|
||||
break; \
|
||||
} \
|
||||
#define FAIL() \
|
||||
do { \
|
||||
/*fprintf(stderr, "==%s[%d]%s()==\n", basename(__FILE__), __LINE__, __func__);*/ \
|
||||
return -1; \
|
||||
} while (0)
|
||||
|
||||
%}
|
||||
|
@ -89,57 +119,85 @@ do { \
|
|||
%option warn
|
||||
%option perf-report
|
||||
%option 8bit
|
||||
%option case-insensitive
|
||||
|
||||
%x DSN UID PWD SERVER DB
|
||||
%x ENC_CHAR ENC_WCHAR ENC_DB ENC_LOCAL
|
||||
%x TSDB_FLOAT TSDB_BIGINT
|
||||
%x KEY EQ BRACE1 BRACE2 VAL
|
||||
|
||||
%%
|
||||
<<EOF>> { int state; TOP_STATE(state);
|
||||
if (state == INITIAL) yyterminate();
|
||||
if (state == VAL) yyterminate();
|
||||
return -1; }
|
||||
FAIL(); }
|
||||
[[:space:]]+ { }
|
||||
[[:alnum:]_]+ { set_key(); PUSH_STATE(KEY); }
|
||||
.|\n { return -1; }
|
||||
"DSN" { PUSH_STATE(DSN); }
|
||||
"UID" { PUSH_STATE(UID); }
|
||||
"PWD" { PUSH_STATE(PWD); }
|
||||
"Server" { PUSH_STATE(SERVER); }
|
||||
"DB" { PUSH_STATE(DB); }
|
||||
"ENC_CHAR" { PUSH_STATE(ENC_CHAR); }
|
||||
"ENC_WCHAR" { PUSH_STATE(ENC_WCHAR); }
|
||||
"ENC_DB" { PUSH_STATE(ENC_DB); }
|
||||
"ENC_LOCAL" { PUSH_STATE(ENC_LOCAL); }
|
||||
"map.float" { PUSH_STATE(TSDB_FLOAT); }
|
||||
"map.bigint" { PUSH_STATE(TSDB_BIGINT); }
|
||||
[[:alnum:]_]+ { PUSH_STATE(KEY); }
|
||||
.|\n { FAIL(); }
|
||||
|
||||
<KEY>[[:space:]]+ { }
|
||||
<KEY>[=] { CHG_STATE(EQ); }
|
||||
<KEY>.|\n { return -1; }
|
||||
<DSN,UID,PWD,SERVER,DB,ENC_CHAR,ENC_WCHAR,ENC_DB,ENC_LOCAL,TSDB_FLOAT,TSDB_BIGINT,KEY>[[:space:]]+ { }
|
||||
<DSN,UID,PWD,SERVER,DB,ENC_CHAR,ENC_WCHAR,ENC_DB,ENC_LOCAL,TSDB_FLOAT,TSDB_BIGINT,KEY>[=] { PUSH_STATE(EQ); }
|
||||
<DSN,UID,PWD,SERVER,DB,ENC_CHAR,ENC_WCHAR,ENC_DB,ENC_LOCAL,TSDB_FLOAT,TSDB_BIGINT,KEY>.|\n { FAIL(); }
|
||||
|
||||
<EQ>[[:space:]]+ { }
|
||||
<EQ>[^][{}(),;?*=!@/\\\n[:space:]]+ { set_val(); CHG_STATE(VAL); }
|
||||
<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 { return -1; }
|
||||
<BRACE1>.|\n { FAIL(); }
|
||||
|
||||
<BRACE2>[[:space:]]+ { }
|
||||
<BRACE2>[}] { CHG_STATE(VAL); }
|
||||
<BRACE2>.|\n { return -1; }
|
||||
<BRACE2>[}] { POP_STATE(); CHG_STATE(VAL); }
|
||||
<BRACE2>.|\n { FAIL(); }
|
||||
|
||||
<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) {
|
||||
yyscan_t arg = {0};
|
||||
yylex_init(&arg);
|
||||
yyset_debug(0, arg);
|
||||
yyset_extra(val, arg);
|
||||
|
||||
conn_val_init(val);
|
||||
|
||||
yy_scan_string(conn, arg);
|
||||
int ret =yylex(arg);
|
||||
yylex_destroy(arg);
|
||||
if (val->key) free(val->key); val->key = NULL;
|
||||
if (ret) {
|
||||
if (ret || !val->dsn) {
|
||||
conn_val_reset(val);
|
||||
} else {
|
||||
if (!val->uid) {
|
||||
val->uid = get_val_by_key_from_odbc_ini(val->dsn, "UID");
|
||||
}
|
||||
if (!val->pwd) {
|
||||
val->pwd = get_val_by_key_from_odbc_ini(val->dsn, "PWD");
|
||||
}
|
||||
if (!val->server) {
|
||||
val->server = get_val_by_key_from_odbc_ini(val->dsn, "Server");
|
||||
}
|
||||
}
|
||||
return ret ? -1 : 0;
|
||||
}
|
||||
|
||||
void conn_val_reset(conn_val_t *val) {
|
||||
if (val->key) {
|
||||
free(val->key); val->key = NULL;
|
||||
}
|
||||
if (val->dsn) {
|
||||
free(val->dsn); val->dsn = NULL;
|
||||
}
|
||||
|
@ -155,11 +213,68 @@ void conn_val_reset(conn_val_t *val) {
|
|||
if (val->server) {
|
||||
free(val->server); val->server = NULL;
|
||||
}
|
||||
if (val->svr_enc) {
|
||||
free(val->svr_enc); val->svr_enc = NULL;
|
||||
if (val->enc_local) {
|
||||
free(val->enc_local); val->enc_local = NULL;
|
||||
}
|
||||
if (val->cli_enc) {
|
||||
free(val->cli_enc); val->cli_enc = 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->enc_wchar) {
|
||||
free(val->enc_wchar); val->enc_wchar = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static char* get_val_by_key_from_odbc_ini(const char *dsn, const char *key) {
|
||||
char Val[4096];
|
||||
Val[0] = '\0';
|
||||
int n = SQLGetPrivateProfileString(dsn, key, "", Val, sizeof(Val), "odbc.ini");
|
||||
if (n<=0) return NULL;
|
||||
if (Val[0]=='\0') return NULL;
|
||||
return strdup(Val);
|
||||
}
|
||||
|
||||
static int process_map(const char *cfg, map_tsdb_type_t *tsdb_map, int type) {
|
||||
switch (type) {
|
||||
case TSDB_FLOAT: {
|
||||
if (strcmp(cfg, "SQL_DOUBLE")==0) {
|
||||
tsdb_map->tsdb_float = SQL_DOUBLE;
|
||||
return 0;
|
||||
}
|
||||
} break;
|
||||
case TSDB_BIGINT: {
|
||||
if (strcmp(cfg, "SQL_C_SBIGINT")==0) {
|
||||
tsdb_map->tsdb_bigint = SQL_C_SBIGINT;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(cfg, "SQL_C_UBIGINT")==0) {
|
||||
tsdb_map->tsdb_bigint = SQL_C_UBIGINT;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(cfg, "SQL_CHAR")==0) {
|
||||
tsdb_map->tsdb_bigint = SQL_CHAR;
|
||||
return 0;
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void conn_val_init(conn_val_t *val) {
|
||||
if (!val) return;
|
||||
val->tsdb_map.tsdb_tinyint = SQL_TINYINT;
|
||||
val->tsdb_map.tsdb_smallint = SQL_SMALLINT;
|
||||
val->tsdb_map.tsdb_int = SQL_INTEGER;
|
||||
val->tsdb_map.tsdb_bigint = SQL_BIGINT;
|
||||
val->tsdb_map.tsdb_float = SQL_REAL;
|
||||
val->tsdb_map.tsdb_double = SQL_DOUBLE;
|
||||
val->tsdb_map.tsdb_bool = SQL_TINYINT;
|
||||
val->tsdb_map.tsdb_timestamp = SQL_CHAR;
|
||||
val->tsdb_map.tsdb_binary = SQL_BINARY;
|
||||
val->tsdb_map.tsdb_nchar = SQL_WCHAR;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 {
|
||||
char enc[64];
|
||||
// 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
|
||||
};
|
||||
|
||||
|
||||
// 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_
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 @@
|
|||
|
||||
#include "todbc_util.h"
|
||||
#include "todbc_log.h"
|
||||
|
||||
#include <iconv.h>
|
||||
#include <sqlext.h>
|
||||
|
||||
|
||||
#define SQL_CASE(type) case type: return #type
|
||||
|
||||
const char* sql_sql_type(int type) {
|
||||
switch (type) {
|
||||
case SQL_BIT: return "SQL_BIT";
|
||||
case SQL_TINYINT: return "SQL_TINYINT";
|
||||
case SQL_SMALLINT: return "SQL_SMALLINT";
|
||||
case SQL_INTEGER: return "SQL_INTEGER";
|
||||
case SQL_BIGINT: return "SQL_BIGINT";
|
||||
case SQL_FLOAT: return "SQL_FLOAT";
|
||||
case SQL_DOUBLE: return "SQL_DOUBLE";
|
||||
case SQL_DECIMAL: return "SQL_DECIMAL";
|
||||
case SQL_NUMERIC: return "SQL_NUMERIC";
|
||||
case SQL_REAL: return "SQL_REAL";
|
||||
case SQL_CHAR: return "SQL_CHAR";
|
||||
case SQL_VARCHAR: return "SQL_VARCHAR";
|
||||
case SQL_LONGVARCHAR: return "SQL_LONGVARCHAR";
|
||||
case SQL_WCHAR: return "SQL_WCHAR";
|
||||
case SQL_WVARCHAR: return "SQL_WVARCHAR";
|
||||
case SQL_WLONGVARCHAR: return "SQL_WLONGVARCHAR";
|
||||
case SQL_BINARY: return "SQL_BINARY";
|
||||
case SQL_VARBINARY: return "SQL_VARBINARY";
|
||||
case SQL_LONGVARBINARY: return "SQL_LONGVARBINARY";
|
||||
case SQL_DATE: return "SQL_DATE";
|
||||
case SQL_TIME: return "SQL_TIME";
|
||||
case SQL_TIMESTAMP: return "SQL_TIMESTAMP";
|
||||
case SQL_TYPE_DATE: return "SQL_TYPE_DATE";
|
||||
case SQL_TYPE_TIME: return "SQL_TYPE_TIME";
|
||||
case SQL_TYPE_TIMESTAMP: return "SQL_TYPE_TIMESTAMP";
|
||||
case SQL_INTERVAL_MONTH: return "SQL_INTERVAL_MONTH";
|
||||
case SQL_INTERVAL_YEAR: return "SQL_INTERVAL_YEAR";
|
||||
case SQL_INTERVAL_YEAR_TO_MONTH: return "SQL_INTERVAL_YEAR_TO_MONTH";
|
||||
case SQL_INTERVAL_DAY: return "SQL_INTERVAL_DAY";
|
||||
case SQL_INTERVAL_HOUR: return "SQL_INTERVAL_HOUR";
|
||||
case SQL_INTERVAL_MINUTE: return "SQL_INTERVAL_MINUTE";
|
||||
case SQL_INTERVAL_SECOND: return "SQL_INTERVAL_SECOND";
|
||||
case SQL_INTERVAL_DAY_TO_HOUR: return "SQL_INTERVAL_DAY_TO_HOUR";
|
||||
case SQL_INTERVAL_DAY_TO_MINUTE: return "SQL_INTERVAL_DAY_TO_MINUTE";
|
||||
case SQL_INTERVAL_DAY_TO_SECOND: return "SQL_INTERVAL_DAY_TO_SECOND";
|
||||
case SQL_INTERVAL_HOUR_TO_MINUTE: return "SQL_INTERVAL_HOUR_TO_MINUTE";
|
||||
case SQL_INTERVAL_HOUR_TO_SECOND: return "SQL_INTERVAL_HOUR_TO_SECOND";
|
||||
case SQL_INTERVAL_MINUTE_TO_SECOND: return "SQL_INTERVAL_MINUTE_TO_SECOND";
|
||||
case SQL_GUID: return "SQL_GUID";
|
||||
SQL_CASE(SQL_BIT);
|
||||
SQL_CASE(SQL_TINYINT);
|
||||
SQL_CASE(SQL_SMALLINT);
|
||||
SQL_CASE(SQL_INTEGER);
|
||||
SQL_CASE(SQL_BIGINT);
|
||||
SQL_CASE(SQL_FLOAT);
|
||||
SQL_CASE(SQL_DOUBLE);
|
||||
SQL_CASE(SQL_DECIMAL);
|
||||
SQL_CASE(SQL_NUMERIC);
|
||||
SQL_CASE(SQL_REAL);
|
||||
SQL_CASE(SQL_CHAR);
|
||||
SQL_CASE(SQL_VARCHAR);
|
||||
SQL_CASE(SQL_LONGVARCHAR);
|
||||
SQL_CASE(SQL_WCHAR);
|
||||
SQL_CASE(SQL_WVARCHAR);
|
||||
SQL_CASE(SQL_WLONGVARCHAR);
|
||||
SQL_CASE(SQL_BINARY);
|
||||
SQL_CASE(SQL_VARBINARY);
|
||||
SQL_CASE(SQL_LONGVARBINARY);
|
||||
SQL_CASE(SQL_DATE);
|
||||
SQL_CASE(SQL_TIME);
|
||||
SQL_CASE(SQL_TIMESTAMP);
|
||||
SQL_CASE(SQL_TYPE_DATE);
|
||||
SQL_CASE(SQL_TYPE_TIME);
|
||||
SQL_CASE(SQL_TYPE_TIMESTAMP);
|
||||
SQL_CASE(SQL_INTERVAL_MONTH);
|
||||
SQL_CASE(SQL_INTERVAL_YEAR);
|
||||
SQL_CASE(SQL_INTERVAL_YEAR_TO_MONTH);
|
||||
SQL_CASE(SQL_INTERVAL_DAY);
|
||||
SQL_CASE(SQL_INTERVAL_HOUR);
|
||||
SQL_CASE(SQL_INTERVAL_MINUTE);
|
||||
SQL_CASE(SQL_INTERVAL_SECOND);
|
||||
SQL_CASE(SQL_INTERVAL_DAY_TO_HOUR);
|
||||
SQL_CASE(SQL_INTERVAL_DAY_TO_MINUTE);
|
||||
SQL_CASE(SQL_INTERVAL_DAY_TO_SECOND);
|
||||
SQL_CASE(SQL_INTERVAL_HOUR_TO_MINUTE);
|
||||
SQL_CASE(SQL_INTERVAL_HOUR_TO_SECOND);
|
||||
SQL_CASE(SQL_INTERVAL_MINUTE_TO_SECOND);
|
||||
SQL_CASE(SQL_GUID);
|
||||
SQL_CASE(SQL_ALL_TYPES);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char* sql_c_type(int type) {
|
||||
switch (type) {
|
||||
case SQL_C_CHAR: return "SQL_C_CHAR";
|
||||
case SQL_C_WCHAR: return "SQL_C_WCHAR";
|
||||
case SQL_C_SHORT: return "SQL_C_SHORT";
|
||||
case SQL_C_SSHORT: return "SQL_C_SSHORT";
|
||||
case SQL_C_USHORT: return "SQL_C_USHORT";
|
||||
case SQL_C_LONG: return "SQL_C_LONG";
|
||||
case SQL_C_SLONG: return "SQL_C_SLONG";
|
||||
case SQL_C_ULONG: return "SQL_C_ULONG";
|
||||
case SQL_C_FLOAT: return "SQL_C_FLOAT";
|
||||
case SQL_C_DOUBLE: return "SQL_C_DOUBLE";
|
||||
case SQL_C_BIT: return "SQL_C_BIT";
|
||||
case SQL_C_TINYINT: return "SQL_C_TINYINT";
|
||||
case SQL_C_STINYINT: return "SQL_C_STINYINT";
|
||||
case SQL_C_UTINYINT: return "SQL_C_UTINYINT";
|
||||
case SQL_C_SBIGINT: return "SQL_C_SBIGINT";
|
||||
case SQL_C_UBIGINT: return "SQL_C_UBIGINT";
|
||||
case SQL_C_BINARY: return "SQL_C_BINARY";
|
||||
case SQL_C_DATE: return "SQL_C_DATE";
|
||||
case SQL_C_TIME: return "SQL_C_TIME";
|
||||
case SQL_C_TIMESTAMP: return "SQL_C_TIMESTAMP";
|
||||
case SQL_C_TYPE_DATE: return "SQL_C_TYPE_DATE";
|
||||
case SQL_C_TYPE_TIME: return "SQL_C_TYPE_TIME";
|
||||
case SQL_C_TYPE_TIMESTAMP: return "SQL_C_TYPE_TIMESTAMP";
|
||||
case SQL_C_NUMERIC: return "SQL_C_NUMERIC";
|
||||
case SQL_C_GUID: return "SQL_C_GUID";
|
||||
SQL_CASE(SQL_C_CHAR);
|
||||
SQL_CASE(SQL_C_WCHAR);
|
||||
SQL_CASE(SQL_C_SHORT);
|
||||
SQL_CASE(SQL_C_SSHORT);
|
||||
SQL_CASE(SQL_C_USHORT);
|
||||
SQL_CASE(SQL_C_LONG);
|
||||
SQL_CASE(SQL_C_SLONG);
|
||||
SQL_CASE(SQL_C_ULONG);
|
||||
SQL_CASE(SQL_C_FLOAT);
|
||||
SQL_CASE(SQL_C_DOUBLE);
|
||||
SQL_CASE(SQL_C_BIT);
|
||||
SQL_CASE(SQL_C_TINYINT);
|
||||
SQL_CASE(SQL_C_STINYINT);
|
||||
SQL_CASE(SQL_C_UTINYINT);
|
||||
SQL_CASE(SQL_C_SBIGINT);
|
||||
SQL_CASE(SQL_C_UBIGINT);
|
||||
SQL_CASE(SQL_C_BINARY);
|
||||
SQL_CASE(SQL_C_DATE);
|
||||
SQL_CASE(SQL_C_TIME);
|
||||
SQL_CASE(SQL_C_TIMESTAMP);
|
||||
SQL_CASE(SQL_C_TYPE_DATE);
|
||||
SQL_CASE(SQL_C_TYPE_TIME);
|
||||
SQL_CASE(SQL_C_TYPE_TIMESTAMP);
|
||||
SQL_CASE(SQL_C_NUMERIC);
|
||||
SQL_CASE(SQL_C_GUID);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetdiagfield-function?view=sql-server-ver15
|
||||
const char* sql_diag_identifier(int type) {
|
||||
switch (type) {
|
||||
// header fields
|
||||
SQL_CASE(SQL_DIAG_CURSOR_ROW_COUNT);
|
||||
SQL_CASE(SQL_DIAG_DYNAMIC_FUNCTION);
|
||||
SQL_CASE(SQL_DIAG_DYNAMIC_FUNCTION_CODE);
|
||||
SQL_CASE(SQL_DIAG_NUMBER);
|
||||
SQL_CASE(SQL_DIAG_RETURNCODE);
|
||||
SQL_CASE(SQL_DIAG_ROW_COUNT);
|
||||
// record fields
|
||||
SQL_CASE(SQL_DIAG_CLASS_ORIGIN);
|
||||
SQL_CASE(SQL_DIAG_COLUMN_NUMBER);
|
||||
SQL_CASE(SQL_DIAG_CONNECTION_NAME);
|
||||
SQL_CASE(SQL_DIAG_MESSAGE_TEXT);
|
||||
SQL_CASE(SQL_DIAG_NATIVE);
|
||||
SQL_CASE(SQL_DIAG_ROW_NUMBER);
|
||||
SQL_CASE(SQL_DIAG_SERVER_NAME);
|
||||
SQL_CASE(SQL_DIAG_SQLSTATE);
|
||||
SQL_CASE(SQL_DIAG_SUBCLASS_ORIGIN);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char* sql_handle_type(int type) {
|
||||
switch(type) {
|
||||
SQL_CASE(SQL_HANDLE_ENV);
|
||||
SQL_CASE(SQL_HANDLE_DBC);
|
||||
SQL_CASE(SQL_HANDLE_STMT);
|
||||
SQL_CASE(SQL_HANDLE_DESC);
|
||||
// SQL_CASE(SQL_HANDLE_DBC_INFO_TOKEN);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char* sql_env_attr_type(int type) {
|
||||
switch(type) {
|
||||
SQL_CASE(SQL_ATTR_OUTPUT_NTS);
|
||||
SQL_CASE(SQL_ATTR_ODBC_VERSION);
|
||||
SQL_CASE(SQL_ATTR_CONNECTION_POOLING);
|
||||
SQL_CASE(SQL_ATTR_CP_MATCH);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetconnectattr-function?view=sql-server-ver15
|
||||
const char* sql_conn_attr_type(int type) {
|
||||
switch(type) {
|
||||
SQL_CASE(SQL_ATTR_ACCESS_MODE);
|
||||
// ODBC 3.8
|
||||
// SQL_CASE(SQL_ATTR_ASYNC_DBC_EVENT);
|
||||
SQL_CASE(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE);
|
||||
// ODBC 3.8
|
||||
// SQL_CASE(SQL_ATTR_ASYNC_DBC_PCALLBACK);
|
||||
// ODBC 3.8
|
||||
// SQL_CASE(SQL_ATTR_ASYNC_DBC_PCONTEXT);
|
||||
SQL_CASE(SQL_ATTR_ASYNC_ENABLE);
|
||||
SQL_CASE(SQL_ATTR_AUTO_IPD);
|
||||
SQL_CASE(SQL_ATTR_AUTOCOMMIT);
|
||||
SQL_CASE(SQL_ATTR_CONNECTION_DEAD);
|
||||
SQL_CASE(SQL_ATTR_CONNECTION_TIMEOUT);
|
||||
SQL_CASE(SQL_ATTR_CURRENT_CATALOG);
|
||||
// ODBC 3.8
|
||||
// SQL_CASE(SQL_ATTR_DBC_INFO_TOKEN);
|
||||
SQL_CASE(SQL_ATTR_ENLIST_IN_DTC);
|
||||
SQL_CASE(SQL_ATTR_LOGIN_TIMEOUT);
|
||||
SQL_CASE(SQL_ATTR_METADATA_ID);
|
||||
SQL_CASE(SQL_ATTR_ODBC_CURSORS);
|
||||
SQL_CASE(SQL_ATTR_PACKET_SIZE);
|
||||
SQL_CASE(SQL_ATTR_QUIET_MODE);
|
||||
SQL_CASE(SQL_ATTR_TRACE);
|
||||
SQL_CASE(SQL_ATTR_TRACEFILE);
|
||||
SQL_CASE(SQL_ATTR_TRANSLATE_LIB);
|
||||
SQL_CASE(SQL_ATTR_TRANSLATE_OPTION);
|
||||
SQL_CASE(SQL_ATTR_TXN_ISOLATION);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetinfo-function?view=sql-server-ver15
|
||||
const char* sql_info_type(int type) {
|
||||
switch(type) {
|
||||
SQL_CASE(SQL_ACTIVE_ENVIRONMENTS);
|
||||
SQL_CASE(SQL_ASYNC_DBC_FUNCTIONS);
|
||||
SQL_CASE(SQL_ASYNC_MODE);
|
||||
SQL_CASE(SQL_ASYNC_NOTIFICATION);
|
||||
SQL_CASE(SQL_BATCH_ROW_COUNT);
|
||||
SQL_CASE(SQL_BATCH_SUPPORT);
|
||||
SQL_CASE(SQL_DATA_SOURCE_NAME);
|
||||
SQL_CASE(SQL_DRIVER_AWARE_POOLING_SUPPORTED);
|
||||
SQL_CASE(SQL_DRIVER_HDBC);
|
||||
SQL_CASE(SQL_DRIVER_HDESC);
|
||||
SQL_CASE(SQL_DRIVER_HENV);
|
||||
SQL_CASE(SQL_DRIVER_HLIB);
|
||||
SQL_CASE(SQL_DRIVER_HSTMT);
|
||||
SQL_CASE(SQL_DRIVER_NAME);
|
||||
SQL_CASE(SQL_DRIVER_ODBC_VER);
|
||||
SQL_CASE(SQL_DRIVER_VER);
|
||||
SQL_CASE(SQL_DYNAMIC_CURSOR_ATTRIBUTES1);
|
||||
SQL_CASE(SQL_DYNAMIC_CURSOR_ATTRIBUTES2);
|
||||
SQL_CASE(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1);
|
||||
SQL_CASE(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2);
|
||||
SQL_CASE(SQL_FILE_USAGE);
|
||||
SQL_CASE(SQL_GETDATA_EXTENSIONS);
|
||||
SQL_CASE(SQL_INFO_SCHEMA_VIEWS);
|
||||
SQL_CASE(SQL_KEYSET_CURSOR_ATTRIBUTES1);
|
||||
SQL_CASE(SQL_KEYSET_CURSOR_ATTRIBUTES2);
|
||||
SQL_CASE(SQL_MAX_ASYNC_CONCURRENT_STATEMENTS);
|
||||
SQL_CASE(SQL_MAX_CONCURRENT_ACTIVITIES);
|
||||
SQL_CASE(SQL_MAX_DRIVER_CONNECTIONS);
|
||||
SQL_CASE(SQL_ODBC_INTERFACE_CONFORMANCE);
|
||||
// SQL_CASE(SQL_ODBC_STANDARD_CLI_CONFORMANCE);
|
||||
SQL_CASE(SQL_ODBC_VER);
|
||||
SQL_CASE(SQL_PARAM_ARRAY_ROW_COUNTS);
|
||||
SQL_CASE(SQL_PARAM_ARRAY_SELECTS);
|
||||
SQL_CASE(SQL_ROW_UPDATES);
|
||||
SQL_CASE(SQL_SEARCH_PATTERN_ESCAPE);
|
||||
SQL_CASE(SQL_SERVER_NAME);
|
||||
SQL_CASE(SQL_STATIC_CURSOR_ATTRIBUTES1);
|
||||
SQL_CASE(SQL_STATIC_CURSOR_ATTRIBUTES2);
|
||||
|
||||
SQL_CASE(SQL_DATABASE_NAME);
|
||||
SQL_CASE(SQL_DBMS_NAME);
|
||||
SQL_CASE(SQL_DBMS_VER);
|
||||
|
||||
SQL_CASE(SQL_ACCESSIBLE_PROCEDURES);
|
||||
SQL_CASE(SQL_ACCESSIBLE_TABLES);
|
||||
SQL_CASE(SQL_BOOKMARK_PERSISTENCE);
|
||||
SQL_CASE(SQL_CATALOG_TERM);
|
||||
SQL_CASE(SQL_COLLATION_SEQ);
|
||||
SQL_CASE(SQL_CONCAT_NULL_BEHAVIOR);
|
||||
SQL_CASE(SQL_CURSOR_COMMIT_BEHAVIOR);
|
||||
SQL_CASE(SQL_CURSOR_ROLLBACK_BEHAVIOR);
|
||||
SQL_CASE(SQL_CURSOR_SENSITIVITY);
|
||||
SQL_CASE(SQL_DATA_SOURCE_READ_ONLY);
|
||||
SQL_CASE(SQL_DEFAULT_TXN_ISOLATION);
|
||||
SQL_CASE(SQL_DESCRIBE_PARAMETER);
|
||||
SQL_CASE(SQL_MULT_RESULT_SETS);
|
||||
SQL_CASE(SQL_MULTIPLE_ACTIVE_TXN);
|
||||
SQL_CASE(SQL_NEED_LONG_DATA_LEN);
|
||||
SQL_CASE(SQL_NULL_COLLATION);
|
||||
SQL_CASE(SQL_PROCEDURE_TERM);
|
||||
SQL_CASE(SQL_SCHEMA_TERM);
|
||||
SQL_CASE(SQL_SCROLL_OPTIONS);
|
||||
SQL_CASE(SQL_TABLE_TERM);
|
||||
SQL_CASE(SQL_TXN_CAPABLE);
|
||||
SQL_CASE(SQL_TXN_ISOLATION_OPTION);
|
||||
SQL_CASE(SQL_USER_NAME);
|
||||
|
||||
SQL_CASE(SQL_AGGREGATE_FUNCTIONS);
|
||||
SQL_CASE(SQL_ALTER_DOMAIN);
|
||||
// SQL_CASE(SQL_ALTER_SCHEMA);
|
||||
SQL_CASE(SQL_ALTER_TABLE);
|
||||
// SQL_CASE(SQL_ANSI_SQL_DATETIME_LITERALS);
|
||||
SQL_CASE(SQL_CATALOG_LOCATION);
|
||||
SQL_CASE(SQL_CATALOG_NAME);
|
||||
SQL_CASE(SQL_CATALOG_NAME_SEPARATOR);
|
||||
SQL_CASE(SQL_CATALOG_USAGE);
|
||||
SQL_CASE(SQL_COLUMN_ALIAS);
|
||||
SQL_CASE(SQL_CORRELATION_NAME);
|
||||
SQL_CASE(SQL_CREATE_ASSERTION);
|
||||
SQL_CASE(SQL_CREATE_CHARACTER_SET);
|
||||
SQL_CASE(SQL_CREATE_COLLATION);
|
||||
SQL_CASE(SQL_CREATE_DOMAIN);
|
||||
SQL_CASE(SQL_CREATE_SCHEMA);
|
||||
SQL_CASE(SQL_CREATE_TABLE);
|
||||
SQL_CASE(SQL_CREATE_TRANSLATION);
|
||||
SQL_CASE(SQL_DDL_INDEX);
|
||||
SQL_CASE(SQL_DROP_ASSERTION);
|
||||
SQL_CASE(SQL_DROP_CHARACTER_SET);
|
||||
SQL_CASE(SQL_DROP_COLLATION);
|
||||
SQL_CASE(SQL_DROP_DOMAIN);
|
||||
SQL_CASE(SQL_DROP_SCHEMA);
|
||||
|
||||
SQL_CASE(SQL_DROP_TABLE);
|
||||
SQL_CASE(SQL_DROP_TRANSLATION);
|
||||
SQL_CASE(SQL_DROP_VIEW);
|
||||
SQL_CASE(SQL_EXPRESSIONS_IN_ORDERBY);
|
||||
SQL_CASE(SQL_GROUP_BY);
|
||||
SQL_CASE(SQL_IDENTIFIER_CASE);
|
||||
SQL_CASE(SQL_IDENTIFIER_QUOTE_CHAR);
|
||||
SQL_CASE(SQL_INDEX_KEYWORDS);
|
||||
SQL_CASE(SQL_INSERT_STATEMENT);
|
||||
SQL_CASE(SQL_INTEGRITY);
|
||||
SQL_CASE(SQL_KEYWORDS);
|
||||
SQL_CASE(SQL_LIKE_ESCAPE_CLAUSE);
|
||||
SQL_CASE(SQL_NON_NULLABLE_COLUMNS);
|
||||
SQL_CASE(SQL_OJ_CAPABILITIES);
|
||||
SQL_CASE(SQL_ORDER_BY_COLUMNS_IN_SELECT);
|
||||
SQL_CASE(SQL_OUTER_JOINS);
|
||||
SQL_CASE(SQL_PROCEDURES);
|
||||
SQL_CASE(SQL_QUOTED_IDENTIFIER_CASE);
|
||||
SQL_CASE(SQL_SCHEMA_USAGE);
|
||||
SQL_CASE(SQL_SPECIAL_CHARACTERS);
|
||||
SQL_CASE(SQL_SQL_CONFORMANCE);
|
||||
SQL_CASE(SQL_SUBQUERIES);
|
||||
SQL_CASE(SQL_UNION);
|
||||
|
||||
SQL_CASE(SQL_MAX_BINARY_LITERAL_LEN);
|
||||
SQL_CASE(SQL_MAX_CATALOG_NAME_LEN);
|
||||
SQL_CASE(SQL_MAX_CHAR_LITERAL_LEN);
|
||||
SQL_CASE(SQL_MAX_COLUMN_NAME_LEN);
|
||||
SQL_CASE(SQL_MAX_COLUMNS_IN_GROUP_BY);
|
||||
SQL_CASE(SQL_MAX_COLUMNS_IN_INDEX);
|
||||
SQL_CASE(SQL_MAX_COLUMNS_IN_ORDER_BY);
|
||||
SQL_CASE(SQL_MAX_COLUMNS_IN_SELECT);
|
||||
SQL_CASE(SQL_MAX_COLUMNS_IN_TABLE);
|
||||
SQL_CASE(SQL_MAX_CURSOR_NAME_LEN);
|
||||
|
||||
SQL_CASE(SQL_MAX_IDENTIFIER_LEN);
|
||||
SQL_CASE(SQL_MAX_INDEX_SIZE);
|
||||
SQL_CASE(SQL_MAX_PROCEDURE_NAME_LEN);
|
||||
SQL_CASE(SQL_MAX_ROW_SIZE);
|
||||
SQL_CASE(SQL_MAX_ROW_SIZE_INCLUDES_LONG);
|
||||
SQL_CASE(SQL_MAX_SCHEMA_NAME_LEN);
|
||||
SQL_CASE(SQL_MAX_STATEMENT_LEN);
|
||||
SQL_CASE(SQL_MAX_TABLE_NAME_LEN);
|
||||
SQL_CASE(SQL_MAX_TABLES_IN_SELECT);
|
||||
SQL_CASE(SQL_MAX_USER_NAME_LEN);
|
||||
|
||||
SQL_CASE(SQL_CONVERT_FUNCTIONS);
|
||||
SQL_CASE(SQL_NUMERIC_FUNCTIONS);
|
||||
SQL_CASE(SQL_STRING_FUNCTIONS);
|
||||
SQL_CASE(SQL_SYSTEM_FUNCTIONS);
|
||||
|
||||
SQL_CASE(SQL_TIMEDATE_ADD_INTERVALS);
|
||||
SQL_CASE(SQL_TIMEDATE_DIFF_INTERVALS);
|
||||
SQL_CASE(SQL_TIMEDATE_FUNCTIONS);
|
||||
|
||||
SQL_CASE(SQL_CONVERT_BIGINT);
|
||||
SQL_CASE(SQL_CONVERT_BINARY);
|
||||
SQL_CASE(SQL_CONVERT_BIT);
|
||||
SQL_CASE(SQL_CONVERT_CHAR);
|
||||
SQL_CASE(SQL_CONVERT_DATE);
|
||||
SQL_CASE(SQL_CONVERT_DECIMAL);
|
||||
SQL_CASE(SQL_CONVERT_DOUBLE);
|
||||
SQL_CASE(SQL_CONVERT_FLOAT);
|
||||
SQL_CASE(SQL_CONVERT_INTEGER);
|
||||
SQL_CASE(SQL_CONVERT_INTERVAL_DAY_TIME);
|
||||
SQL_CASE(SQL_CONVERT_INTERVAL_YEAR_MONTH);
|
||||
|
||||
SQL_CASE(SQL_CONVERT_LONGVARBINARY);
|
||||
SQL_CASE(SQL_CONVERT_LONGVARCHAR);
|
||||
SQL_CASE(SQL_CONVERT_NUMERIC);
|
||||
SQL_CASE(SQL_CONVERT_REAL);
|
||||
SQL_CASE(SQL_CONVERT_SMALLINT);
|
||||
SQL_CASE(SQL_CONVERT_TIME);
|
||||
SQL_CASE(SQL_CONVERT_TIMESTAMP);
|
||||
SQL_CASE(SQL_CONVERT_TINYINT);
|
||||
SQL_CASE(SQL_CONVERT_VARBINARY);
|
||||
SQL_CASE(SQL_CONVERT_VARCHAR);
|
||||
|
||||
SQL_CASE(SQL_DM_VER);
|
||||
|
||||
SQL_CASE(SQL_XOPEN_CLI_YEAR);
|
||||
|
||||
SQL_CASE(SQL_DTC_TRANSITION_COST);
|
||||
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlcolattribute-function?view=sql-server-ver15
|
||||
const char* sql_field_identifier(int type) {
|
||||
switch (type) {
|
||||
SQL_CASE(SQL_DESC_AUTO_UNIQUE_VALUE);
|
||||
SQL_CASE(SQL_DESC_BASE_COLUMN_NAME);
|
||||
SQL_CASE(SQL_DESC_BASE_TABLE_NAME);
|
||||
SQL_CASE(SQL_DESC_CASE_SENSITIVE);
|
||||
SQL_CASE(SQL_DESC_CATALOG_NAME);
|
||||
SQL_CASE(SQL_DESC_CONCISE_TYPE);
|
||||
SQL_CASE(SQL_DESC_COUNT);
|
||||
SQL_CASE(SQL_DESC_DISPLAY_SIZE);
|
||||
SQL_CASE(SQL_DESC_FIXED_PREC_SCALE);
|
||||
SQL_CASE(SQL_DESC_LABEL);
|
||||
SQL_CASE(SQL_DESC_LENGTH);
|
||||
SQL_CASE(SQL_DESC_LITERAL_PREFIX);
|
||||
SQL_CASE(SQL_DESC_LITERAL_SUFFIX);
|
||||
SQL_CASE(SQL_DESC_LOCAL_TYPE_NAME);
|
||||
SQL_CASE(SQL_DESC_NAME);
|
||||
SQL_CASE(SQL_DESC_NULLABLE);
|
||||
SQL_CASE(SQL_DESC_NUM_PREC_RADIX);
|
||||
SQL_CASE(SQL_DESC_OCTET_LENGTH);
|
||||
SQL_CASE(SQL_DESC_PRECISION);
|
||||
SQL_CASE(SQL_DESC_SCALE);
|
||||
SQL_CASE(SQL_DESC_SCHEMA_NAME);
|
||||
SQL_CASE(SQL_DESC_SEARCHABLE);
|
||||
SQL_CASE(SQL_DESC_TABLE_NAME);
|
||||
SQL_CASE(SQL_DESC_TYPE);
|
||||
SQL_CASE(SQL_DESC_TYPE_NAME);
|
||||
SQL_CASE(SQL_DESC_UNNAMED);
|
||||
SQL_CASE(SQL_DESC_UNSIGNED);
|
||||
SQL_CASE(SQL_DESC_UPDATABLE);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function?view=sql-server-ver15
|
||||
const char* sql_input_output_type(int type) {
|
||||
switch (type) {
|
||||
SQL_CASE(SQL_PARAM_INPUT);
|
||||
SQL_CASE(SQL_PARAM_OUTPUT);
|
||||
SQL_CASE(SQL_PARAM_OUTPUT_STREAM);
|
||||
SQL_CASE(SQL_PARAM_INPUT_OUTPUT);
|
||||
SQL_CASE(SQL_PARAM_INPUT_OUTPUT_STREAM);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function?view=sql-server-ver15
|
||||
const char* sql_stmt_attr_type(int type) {
|
||||
switch (type) {
|
||||
SQL_CASE(SQL_ATTR_APP_PARAM_DESC);
|
||||
SQL_CASE(SQL_ATTR_APP_ROW_DESC);
|
||||
SQL_CASE(SQL_ATTR_ASYNC_ENABLE);
|
||||
SQL_CASE(SQL_ATTR_ASYNC_STMT_EVENT);
|
||||
// ODBC 3.8
|
||||
// SQL_CASE(SQL_ATTR_ASYNC_STMT_PCALLBACK);
|
||||
// ODBC 3.8
|
||||
// SQL_CASE(SQL_ATTR_ASYNC_STMT_PCONTEXT);
|
||||
SQL_CASE(SQL_ATTR_CONCURRENCY);
|
||||
SQL_CASE(SQL_ATTR_CURSOR_SCROLLABLE);
|
||||
SQL_CASE(SQL_ATTR_CURSOR_SENSITIVITY);
|
||||
SQL_CASE(SQL_ATTR_CURSOR_TYPE);
|
||||
SQL_CASE(SQL_ATTR_ENABLE_AUTO_IPD);
|
||||
SQL_CASE(SQL_ATTR_FETCH_BOOKMARK_PTR);
|
||||
SQL_CASE(SQL_ATTR_IMP_PARAM_DESC);
|
||||
SQL_CASE(SQL_ATTR_IMP_ROW_DESC);
|
||||
SQL_CASE(SQL_ATTR_KEYSET_SIZE);
|
||||
SQL_CASE(SQL_ATTR_MAX_LENGTH);
|
||||
SQL_CASE(SQL_ATTR_MAX_ROWS);
|
||||
SQL_CASE(SQL_ATTR_METADATA_ID);
|
||||
SQL_CASE(SQL_ATTR_NOSCAN);
|
||||
SQL_CASE(SQL_ATTR_PARAM_BIND_OFFSET_PTR);
|
||||
SQL_CASE(SQL_ATTR_PARAM_BIND_TYPE);
|
||||
SQL_CASE(SQL_ATTR_PARAM_OPERATION_PTR);
|
||||
SQL_CASE(SQL_ATTR_PARAM_STATUS_PTR);
|
||||
SQL_CASE(SQL_ATTR_PARAMS_PROCESSED_PTR);
|
||||
SQL_CASE(SQL_ATTR_PARAMSET_SIZE);
|
||||
SQL_CASE(SQL_ATTR_QUERY_TIMEOUT);
|
||||
SQL_CASE(SQL_ATTR_RETRIEVE_DATA);
|
||||
SQL_CASE(SQL_ATTR_ROW_ARRAY_SIZE);
|
||||
SQL_CASE(SQL_ATTR_ROW_BIND_OFFSET_PTR);
|
||||
SQL_CASE(SQL_ATTR_ROW_BIND_TYPE);
|
||||
SQL_CASE(SQL_ATTR_ROW_NUMBER);
|
||||
SQL_CASE(SQL_ATTR_ROW_OPERATION_PTR);
|
||||
SQL_CASE(SQL_ATTR_ROW_STATUS_PTR);
|
||||
SQL_CASE(SQL_ATTR_ROWS_FETCHED_PTR);
|
||||
SQL_CASE(SQL_ATTR_SIMULATE_CURSOR);
|
||||
SQL_CASE(SQL_ATTR_USE_BOOKMARKS);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char* sql_function_type(int type) {
|
||||
switch (type) {
|
||||
//
|
||||
SQL_CASE(SQL_API_ALL_FUNCTIONS);
|
||||
SQL_CASE(SQL_API_ODBC3_ALL_FUNCTIONS);
|
||||
|
||||
// ISO 92 standards-compliance
|
||||
SQL_CASE(SQL_API_SQLALLOCHANDLE);
|
||||
SQL_CASE(SQL_API_SQLGETDESCFIELD);
|
||||
SQL_CASE(SQL_API_SQLBINDCOL);
|
||||
SQL_CASE(SQL_API_SQLGETDESCREC);
|
||||
SQL_CASE(SQL_API_SQLCANCEL);
|
||||
SQL_CASE(SQL_API_SQLGETDIAGFIELD);
|
||||
SQL_CASE(SQL_API_SQLCLOSECURSOR);
|
||||
SQL_CASE(SQL_API_SQLGETDIAGREC);
|
||||
SQL_CASE(SQL_API_SQLCOLATTRIBUTE);
|
||||
SQL_CASE(SQL_API_SQLGETENVATTR);
|
||||
SQL_CASE(SQL_API_SQLCONNECT);
|
||||
SQL_CASE(SQL_API_SQLGETFUNCTIONS);
|
||||
SQL_CASE(SQL_API_SQLCOPYDESC);
|
||||
SQL_CASE(SQL_API_SQLGETINFO);
|
||||
SQL_CASE(SQL_API_SQLDATASOURCES);
|
||||
SQL_CASE(SQL_API_SQLGETSTMTATTR);
|
||||
SQL_CASE(SQL_API_SQLDESCRIBECOL);
|
||||
SQL_CASE(SQL_API_SQLGETTYPEINFO);
|
||||
SQL_CASE(SQL_API_SQLDISCONNECT);
|
||||
SQL_CASE(SQL_API_SQLNUMRESULTCOLS);
|
||||
SQL_CASE(SQL_API_SQLDRIVERS);
|
||||
SQL_CASE(SQL_API_SQLPARAMDATA);
|
||||
SQL_CASE(SQL_API_SQLENDTRAN);
|
||||
SQL_CASE(SQL_API_SQLPREPARE);
|
||||
SQL_CASE(SQL_API_SQLEXECDIRECT);
|
||||
SQL_CASE(SQL_API_SQLPUTDATA);
|
||||
SQL_CASE(SQL_API_SQLEXECUTE);
|
||||
SQL_CASE(SQL_API_SQLROWCOUNT);
|
||||
SQL_CASE(SQL_API_SQLFETCH);
|
||||
SQL_CASE(SQL_API_SQLSETCONNECTATTR);
|
||||
SQL_CASE(SQL_API_SQLFETCHSCROLL);
|
||||
SQL_CASE(SQL_API_SQLSETCURSORNAME);
|
||||
SQL_CASE(SQL_API_SQLFREEHANDLE);
|
||||
SQL_CASE(SQL_API_SQLSETDESCFIELD);
|
||||
SQL_CASE(SQL_API_SQLFREESTMT);
|
||||
SQL_CASE(SQL_API_SQLSETDESCREC);
|
||||
SQL_CASE(SQL_API_SQLGETCONNECTATTR);
|
||||
SQL_CASE(SQL_API_SQLSETENVATTR);
|
||||
SQL_CASE(SQL_API_SQLGETCURSORNAME);
|
||||
SQL_CASE(SQL_API_SQLSETSTMTATTR);
|
||||
SQL_CASE(SQL_API_SQLGETDATA);
|
||||
|
||||
// Open Group standards-compliance);
|
||||
SQL_CASE(SQL_API_SQLCOLUMNS);
|
||||
SQL_CASE(SQL_API_SQLSTATISTICS);
|
||||
SQL_CASE(SQL_API_SQLSPECIALCOLUMNS);
|
||||
SQL_CASE(SQL_API_SQLTABLES);
|
||||
|
||||
// ODBC standards-compliance);
|
||||
SQL_CASE(SQL_API_SQLBINDPARAMETER);
|
||||
SQL_CASE(SQL_API_SQLNATIVESQL);
|
||||
SQL_CASE(SQL_API_SQLBROWSECONNECT);
|
||||
SQL_CASE(SQL_API_SQLNUMPARAMS);
|
||||
SQL_CASE(SQL_API_SQLBULKOPERATIONS);
|
||||
SQL_CASE(SQL_API_SQLPRIMARYKEYS);
|
||||
SQL_CASE(SQL_API_SQLCOLUMNPRIVILEGES);
|
||||
SQL_CASE(SQL_API_SQLPROCEDURECOLUMNS);
|
||||
SQL_CASE(SQL_API_SQLDESCRIBEPARAM);
|
||||
SQL_CASE(SQL_API_SQLPROCEDURES);
|
||||
SQL_CASE(SQL_API_SQLDRIVERCONNECT);
|
||||
SQL_CASE(SQL_API_SQLSETPOS);
|
||||
SQL_CASE(SQL_API_SQLFOREIGNKEYS);
|
||||
SQL_CASE(SQL_API_SQLTABLEPRIVILEGES);
|
||||
SQL_CASE(SQL_API_SQLMORERESULTS);
|
||||
|
||||
SQL_CASE(SQL_API_SQLALLOCCONNECT);
|
||||
SQL_CASE(SQL_API_SQLALLOCENV);
|
||||
SQL_CASE(SQL_API_SQLALLOCSTMT);
|
||||
SQL_CASE(SQL_API_SQLBINDPARAM);
|
||||
SQL_CASE(SQL_API_SQLERROR);
|
||||
SQL_CASE(SQL_API_SQLFREECONNECT);
|
||||
SQL_CASE(SQL_API_SQLFREEENV);
|
||||
SQL_CASE(SQL_API_SQLGETCONNECTOPTION);
|
||||
SQL_CASE(SQL_API_SQLGETSTMTOPTION);
|
||||
SQL_CASE(SQL_API_SQLSETCONNECTOPTION);
|
||||
SQL_CASE(SQL_API_SQLSETPARAM);
|
||||
SQL_CASE(SQL_API_SQLSETSTMTOPTION);
|
||||
SQL_CASE(SQL_API_SQLTRANSACT);
|
||||
SQL_CASE(SQL_API_SQLCANCELHANDLE);
|
||||
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char* sql_freestmt_option_type(int type) {
|
||||
switch (type) {
|
||||
SQL_CASE(SQL_CLOSE);
|
||||
SQL_CASE(SQL_DROP);
|
||||
SQL_CASE(SQL_UNBIND);
|
||||
SQL_CASE(SQL_RESET_PARAMS);
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char* sql_soi_type(int soi) {
|
||||
switch (soi) {
|
||||
SQL_CASE(SQL_NTS);
|
||||
SQL_CASE(SQL_NULL_DATA);
|
||||
SQL_CASE(SQL_DEFAULT_PARAM);
|
||||
SQL_CASE(SQL_DATA_AT_EXEC);
|
||||
default: {
|
||||
if (soi >= 0) return "";
|
||||
return "SQL_LEN_DATA_AT_EXEC(?)";
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
const char* sql_nullable_type(int type) {
|
||||
switch (type) {
|
||||
SQL_CASE(SQL_NO_NULLS);
|
||||
SQL_CASE(SQL_NULLABLE);
|
||||
SQL_CASE(SQL_NULLABLE_UNKNOWN);
|
||||
default: {
|
||||
return "UNKNOWN";
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int is_valid_sql_c_type(int type) {
|
||||
const char *ctype = sql_c_type(type);
|
||||
if (strcmp(ctype, "UNKNOWN")==0) return 0;
|
||||
|
@ -127,3 +617,46 @@ int utf8_chars(const char *src)
|
|||
return (int)chars;
|
||||
}
|
||||
|
||||
static int do_charset_chars(iconv_t cnv, const unsigned char *src)
|
||||
{
|
||||
int chars = 0;
|
||||
char *ps = (char*)src;
|
||||
char buf[16];
|
||||
size_t sn = 1;
|
||||
while (1) {
|
||||
char *ds = buf;
|
||||
size_t dn = sizeof(buf);
|
||||
size_t n = iconv(cnv, &ps, &sn, &ds, &dn);
|
||||
if (n==(size_t)-1) {
|
||||
int e = errno;
|
||||
switch (e) {
|
||||
case EILSEQ: return -1;
|
||||
case E2BIG: return -1;
|
||||
case EINVAL: sn += 1; continue;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
if (sn) return -1;
|
||||
if (n>0) return -1;
|
||||
int i=0;
|
||||
for (i=0; i<(sizeof(buf)-dn); ++i) {
|
||||
if (buf[i]) break;
|
||||
}
|
||||
if (i>=(sizeof(buf)-dn)) break;
|
||||
chars += (int)1;
|
||||
sn = 1;
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
int charset_chars(const char *charset, const unsigned char *src) {
|
||||
iconv_t cnv = iconv_open(charset, charset);
|
||||
if (cnv==(iconv_t)-1) return -1;
|
||||
|
||||
int chars = do_charset_chars(cnv, src);
|
||||
|
||||
iconv_close(cnv);
|
||||
|
||||
return chars;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,11 +23,24 @@
|
|||
|
||||
const char* sql_sql_type(int type);
|
||||
const char* sql_c_type(int type);
|
||||
const char* sql_handle_type(int type);
|
||||
const char* sql_env_attr_type(int type);
|
||||
const char* sql_conn_attr_type(int type);
|
||||
const char* sql_info_type(int type);
|
||||
const char* sql_field_identifier(int type);
|
||||
const char* sql_diag_identifier(int type);
|
||||
const char* sql_input_output_type(int type);
|
||||
const char* sql_stmt_attr_type(int type);
|
||||
const char* sql_function_type(int type);
|
||||
const char* sql_freestmt_option_type(int type);
|
||||
const char* sql_soi_type(int soi);
|
||||
const char* sql_nullable_type(int type);
|
||||
|
||||
int is_valid_sql_c_type(int type);
|
||||
int is_valid_sql_sql_type(int type);
|
||||
|
||||
int utf8_chars(const char *src);
|
||||
int charset_chars(const char *charset, const unsigned char *src);
|
||||
|
||||
#endif // _TODBC_UTIL_H_
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
PROJECT(TDengine)
|
||||
|
||||
IF (TD_LINUX)
|
||||
# AUX_SOURCE_DIRECTORY(. SRC)
|
||||
ADD_EXECUTABLE(tcodbc main.c)
|
||||
TARGET_LINK_LIBRARIES(tcodbc odbc)
|
||||
ADD_EXECUTABLE(tconv tconv.c)
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_WINDOWS_64)
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /GL")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL")
|
||||
# AUX_SOURCE_DIRECTORY(. SRC)
|
||||
ADD_EXECUTABLE(tcodbc main.c)
|
||||
TARGET_LINK_LIBRARIES(tcodbc odbc32 odbccp32 user32 legacy_stdio_definitions os)
|
||||
ADD_EXECUTABLE(tconv tconv.c)
|
||||
TARGET_LINK_LIBRARIES(tconv tutil)
|
||||
ENDIF ()
|
|
@ -1,673 +0,0 @@
|
|||
#include "../src/todbc_log.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include "os.h"
|
||||
#endif
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include <odbcinst.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CHK_TEST(statement) \
|
||||
do { \
|
||||
D("testing: %s", #statement); \
|
||||
int r = (statement); \
|
||||
if (r) { \
|
||||
D("testing failed: %s", #statement); \
|
||||
return 1; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
typedef struct db_column_s db_column_t;
|
||||
struct db_column_s {
|
||||
SQLSMALLINT nameLength;
|
||||
char name[4096]; // seems enough
|
||||
SQLSMALLINT dataType;
|
||||
SQLULEN columnSize;
|
||||
SQLSMALLINT decimalDigits;
|
||||
SQLSMALLINT nullable;
|
||||
};
|
||||
|
||||
static db_column_t *columns = NULL;
|
||||
|
||||
typedef struct data_s data_t;
|
||||
struct data_s {
|
||||
int64_t ts;
|
||||
int8_t b;
|
||||
int8_t v1;
|
||||
int16_t v2;
|
||||
int32_t v4;
|
||||
int64_t v8;
|
||||
float f4;
|
||||
double f8;
|
||||
char bin[40+1];
|
||||
char blob[40+1]; // why 80? ref: tests/examples/c/apitest.c
|
||||
};
|
||||
|
||||
static const char *pre_stmts[] = {
|
||||
"create database db",
|
||||
"use db",
|
||||
"create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))"
|
||||
};
|
||||
|
||||
static const char *pro_stmts[] = {
|
||||
// "insert into t values ('2019-07-15 00:00:00', 1)",
|
||||
// "insert into t values ('2019-07-15 01:00:00', 2)",
|
||||
"select * from t"
|
||||
// "drop database db"
|
||||
};
|
||||
|
||||
#define CHK_RESULT(r, ht, h, fmt, ...) \
|
||||
do { \
|
||||
if (r==0) break; \
|
||||
SQLCHAR ss[10]; \
|
||||
SQLINTEGER ne = 0; \
|
||||
SQLCHAR es[4096]; \
|
||||
SQLSMALLINT n = 0; \
|
||||
ss[0] = '\0'; \
|
||||
es[0] = '\0'; \
|
||||
SQLRETURN ret = SQLGetDiagRec(ht, h, 1, ss, &ne, es, sizeof(es), &n); \
|
||||
if (ret) break; \
|
||||
D("[%s]%s: " fmt "", ss, es, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static int open_connect(const char *dsn, const char *uid, const char *pwd, SQLHENV *pEnv, SQLHDBC *pConn) {
|
||||
SQLRETURN r;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
r = SQLAllocEnv(&env);
|
||||
if (r!=SQL_SUCCESS) return 1;
|
||||
do {
|
||||
r = SQLAllocConnect(env, &conn);
|
||||
CHK_RESULT(r, SQL_HANDLE_ENV, env, "");
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
do {
|
||||
r = SQLConnect(conn, (SQLCHAR*)dsn, (SQLSMALLINT)(dsn ? strlen(dsn) : 0),
|
||||
(SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0),
|
||||
(SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0));
|
||||
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
||||
if (r==SQL_SUCCESS) {
|
||||
*pEnv = env;
|
||||
*pConn = conn;
|
||||
return 0;
|
||||
}
|
||||
} while (0);
|
||||
SQLFreeConnect(conn);
|
||||
} while (0);
|
||||
SQLFreeEnv(env);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int open_driver_connect(const char *connstr, SQLHENV *pEnv, SQLHDBC *pConn) {
|
||||
SQLRETURN r;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
r = SQLAllocEnv(&env);
|
||||
if (r!=SQL_SUCCESS) return 1;
|
||||
do {
|
||||
r = SQLAllocConnect(env, &conn);
|
||||
CHK_RESULT(r, SQL_HANDLE_ENV, env, "");
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
do {
|
||||
SQLCHAR buf[4096];
|
||||
SQLSMALLINT blen = 0;
|
||||
SQLHDBC ConnectionHandle = conn;
|
||||
SQLHWND WindowHandle = NULL;
|
||||
SQLCHAR * InConnectionString = (SQLCHAR*)connstr;
|
||||
SQLSMALLINT StringLength1 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0);
|
||||
SQLCHAR * OutConnectionString = buf;
|
||||
SQLSMALLINT BufferLength = sizeof(buf);
|
||||
SQLSMALLINT * StringLength2Ptr = &blen;
|
||||
SQLUSMALLINT DriverCompletion = SQL_DRIVER_NOPROMPT;
|
||||
r = SQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString,
|
||||
StringLength1, OutConnectionString, BufferLength,
|
||||
StringLength2Ptr, DriverCompletion);
|
||||
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
||||
if (r==SQL_SUCCESS) {
|
||||
*pEnv = env;
|
||||
*pConn = conn;
|
||||
return 0;
|
||||
}
|
||||
} while (0);
|
||||
SQLFreeConnect(conn);
|
||||
} while (0);
|
||||
SQLFreeEnv(env);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQLRETURN traverse_cols(SQLHSTMT stmt, SQLSMALLINT cols) {
|
||||
SQLRETURN r = SQL_ERROR;
|
||||
for (SQLSMALLINT i=0; i<cols; ++i) {
|
||||
db_column_t column = {0};
|
||||
r = SQLDescribeCol(stmt, (SQLUSMALLINT)(i+1), (SQLCHAR*)column.name,
|
||||
(SQLSMALLINT)sizeof(column.name), &column.nameLength,
|
||||
&column.dataType, &column.columnSize,
|
||||
&column.decimalDigits, &column.nullable);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
D("col%02d:[%s]%d,type:[%d],colSize:[%"PRId64"],decimalDigits:[%d],nullable:[%d]",
|
||||
i+1, column.name, column.nameLength, column.dataType, column.columnSize,
|
||||
column.decimalDigits, column.nullable);
|
||||
db_column_t *col = (db_column_t*)realloc(columns, (size_t)(i+1)*sizeof(*col));
|
||||
if (!col) {
|
||||
D("out of memory");
|
||||
return SQL_ERROR;
|
||||
}
|
||||
col[i] = column;
|
||||
columns = col;
|
||||
}
|
||||
return SQL_SUCCESS;
|
||||
}
|
||||
|
||||
static int do_statement(SQLHSTMT stmt, const char *statement) {
|
||||
SQLRETURN r = 0;
|
||||
do {
|
||||
r = SQLExecDirect(stmt, (SQLCHAR*)statement, SQL_NTS);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: [%s]", statement);
|
||||
if (r) break;
|
||||
SQLSMALLINT cols = 0;
|
||||
r = SQLNumResultCols(stmt, &cols);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) break;
|
||||
if (cols <= 0) break;
|
||||
r = traverse_cols(stmt, cols);
|
||||
char buf[4096];
|
||||
while (1) {
|
||||
SQLRETURN r = SQLFetch(stmt);
|
||||
if (r==SQL_NO_DATA) break;
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
for (size_t i=0; i<cols; ++i) {
|
||||
SQLLEN soi = 0;
|
||||
r = SQLGetData(stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, buf, sizeof(buf), &soi);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
if (r) {
|
||||
if (r!=SQL_SUCCESS_WITH_INFO) {
|
||||
if (i>0) fprintf(stdout, "\n");
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if (soi==SQL_NULL_DATA) {
|
||||
fprintf(stdout, "%snull", i==0?"":",");
|
||||
} else {
|
||||
fprintf(stdout, "%s\"%s\"", i==0?"":",", buf);
|
||||
}
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
// r = SQLFetch(stmt);
|
||||
// if (r==SQL_NO_DATA) {
|
||||
// D("..........");
|
||||
// r = SQL_SUCCESS;
|
||||
// break;
|
||||
// }
|
||||
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
// if (r) break;
|
||||
// r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement));
|
||||
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
||||
// if (r) break;
|
||||
// r = SQLExecute(stmt);
|
||||
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
// if (r) break;
|
||||
} while (0);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int do_insert(SQLHSTMT stmt, data_t data) {
|
||||
SQLRETURN r = 0;
|
||||
SQLLEN lbin;
|
||||
SQLLEN lblob;
|
||||
|
||||
const char *statement = "insert into t values (?, ?, ?, ?, ?, ?, ?, ?, ?,?)";
|
||||
#define ignored 0
|
||||
|
||||
do {
|
||||
r = SQLPrepare(stmt, (SQLCHAR*)statement, (SQLINTEGER)strlen(statement));
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_TIMESTAMP, ignored, ignored, &data.ts, ignored, NULL);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, ignored, ignored, &data.b, ignored, NULL);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, ignored, ignored, &data.v1, ignored, NULL);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, ignored, ignored, &data.v2, ignored, NULL);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, ignored, ignored, &data.v4, ignored, NULL);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, ignored, ignored, &data.v8, ignored, NULL);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, ignored, ignored, &data.f4, ignored, NULL);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignored, ignored, &data.f8, ignored, NULL);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
lbin = SQL_NTS;
|
||||
r = SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, sizeof(data.bin)-1, ignored, &data.bin, ignored, &lbin);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
lblob = SQL_NTS;
|
||||
r = SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(data.blob)-1, ignored, &data.blob, ignored, &lblob);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
r = SQLExecute(stmt);
|
||||
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
|
||||
if (r) break;
|
||||
|
||||
// ts += 1;
|
||||
// v = 2;
|
||||
// r = SQLExecute(stmt);
|
||||
// if (r) break;
|
||||
} while (0);
|
||||
|
||||
#undef ignored
|
||||
return r;
|
||||
}
|
||||
|
||||
static int test1(const char *dsn, const char *uid, const char *pwd) {
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
int n = open_connect(dsn, uid, pwd, &env, &conn);
|
||||
if (n) return 1;
|
||||
|
||||
int ok = 0;
|
||||
do {
|
||||
SQLRETURN r = SQL_SUCCESS;
|
||||
SQLHSTMT stmt = {0};
|
||||
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
do {
|
||||
if (do_statement(stmt, "drop database if exists db")) {
|
||||
break;
|
||||
}
|
||||
for (size_t i=0; 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) {
|
||||
SQLRETURN r = SQL_SUCCESS;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
int n = open_connect(dsn, uid, pwd, &env, &conn);
|
||||
if (n) return 1;
|
||||
do {
|
||||
SQLHSTMT stmt = {0};
|
||||
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
|
||||
if (r!=SQL_SUCCESS) break;
|
||||
const char **p = statements;
|
||||
while (*p) {
|
||||
if (do_statement(stmt, *p)) {
|
||||
r = SQL_ERROR;
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
} while (0);
|
||||
SQLDisconnect(conn);
|
||||
SQLFreeConnect(conn);
|
||||
SQLFreeEnv(env);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
int test_driver_connect(const char *connstr) {
|
||||
SQLRETURN r = SQL_SUCCESS;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
int n = open_driver_connect(connstr, &env, &conn);
|
||||
if (n) return 1;
|
||||
SQLDisconnect(conn);
|
||||
SQLFreeConnect(conn);
|
||||
SQLFreeEnv(env);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
int create_statement(SQLHENV env, SQLHDBC conn, SQLHSTMT *pStmt) {
|
||||
SQLHSTMT stmt = {0};
|
||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
|
||||
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
||||
if (r==SQL_SUCCESS) {
|
||||
*pStmt = stmt;
|
||||
return 0;
|
||||
}
|
||||
if (r==SQL_SUCCESS_WITH_INFO) {
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int do_statements(SQLHSTMT stmt, const char **statements) {
|
||||
const char **p = statements;
|
||||
while (p && *p) {
|
||||
CHK_TEST(do_statement(stmt, *p));
|
||||
++p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tests_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt) {
|
||||
const char *statements[] = {
|
||||
"drop database if exists m",
|
||||
"create database m",
|
||||
"use m",
|
||||
// "create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(1), name nchar(1))",
|
||||
"create table t (ts timestamp, b bool)",
|
||||
"insert into t values('2020-10-10 00:00:00', 0)",
|
||||
"insert into t values('2020-10-10 00:00:00.001', 1)",
|
||||
NULL
|
||||
};
|
||||
CHK_TEST(do_statements(stmt, statements));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tests(SQLHENV env, SQLHDBC conn) {
|
||||
SQLHSTMT stmt = {0};
|
||||
CHK_TEST(create_statement(env, conn, &stmt));
|
||||
int r = tests_stmt(env, conn, stmt);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
int test_env(void) {
|
||||
SQLRETURN r;
|
||||
SQLHENV env = {0};
|
||||
r = SQLAllocEnv(&env);
|
||||
if (r!=SQL_SUCCESS) return 1;
|
||||
SQLFreeEnv(env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls) {
|
||||
FILE *f = fopen(sqls, "rb");
|
||||
if (!f) {
|
||||
D("failed to open file [%s]", sqls);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int r = 0;
|
||||
while (!feof(f)) {
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
ssize_t n = 0;
|
||||
#ifdef _MSC_VER
|
||||
n = taosGetline(&line, &len, f);
|
||||
#else
|
||||
n = getline(&line, &len, f);
|
||||
#endif
|
||||
if (n==-1) break;
|
||||
|
||||
const char *p = NULL;
|
||||
do {
|
||||
if (line[0] == '#') break;
|
||||
if (n>0 && line[n-1] == '\n') line[n-1]='\0';
|
||||
if (n>0 && line[n-1] == '\r') line[n-1]='\0';
|
||||
if (n>1 && line[n-2] == '\r') line[n-2]='\0';
|
||||
p = line;
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
if (*p==0) break;
|
||||
|
||||
int positive = 1;
|
||||
if (strncmp(p, "N:", 2)==0) {
|
||||
positive = 0;
|
||||
p += 2;
|
||||
} else if (strncmp(p, "P:", 2)==0) {
|
||||
p += 2;
|
||||
}
|
||||
|
||||
D("statement: [%s]", p);
|
||||
r = do_statement(stmt, p);
|
||||
|
||||
if (positive && r==0) break;
|
||||
if (!positive && r) { r = 0; break; }
|
||||
if (positive) return r;
|
||||
D("expecting negative result, but got positive");
|
||||
return -1;
|
||||
} while (0);
|
||||
|
||||
free(line);
|
||||
|
||||
if (r) break;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) {
|
||||
SQLHSTMT stmt = {0};
|
||||
CHK_TEST(create_statement(env, conn, &stmt));
|
||||
int r = test_sqls_in_stmt(env, conn, stmt, sqls);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *connstr, const char *sqls) {
|
||||
int r = 0;
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
if (dsn) {
|
||||
CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn));
|
||||
} else {
|
||||
CHK_TEST(open_driver_connect(connstr, &env, &conn));
|
||||
}
|
||||
if (sqls) {
|
||||
r = test_sqls_in_conn(env, conn, sqls);
|
||||
}
|
||||
SQLDisconnect(conn);
|
||||
SQLFreeConnect(conn);
|
||||
SQLFreeEnv(env);
|
||||
return r ? 1 : 0;
|
||||
}
|
||||
|
||||
void usage(const char *arg0) {
|
||||
fprintf(stdout, "%s usage:\n", arg0);
|
||||
fprintf(stdout, "%s [--dsn <dsn>] [--uid <uid>] [--pwd <pwd>] [--dcs <dcs>] [--sts <sts>]\n", arg0);
|
||||
fprintf(stdout, " --dsn <dsn>: DSN\n");
|
||||
fprintf(stdout, " --uid <uid>: UID\n");
|
||||
fprintf(stdout, " --pwd <pwd>: PWD\n");
|
||||
fprintf(stdout, " --dcs <dcs>: driver connection string\n");
|
||||
fprintf(stdout, " --sts <sts>: file where statements store\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// if (argc==1) {
|
||||
// CHK_TEST(test_env());
|
||||
// CHK_TEST(test1("TAOS_DSN", "root", "taoxsdata"));
|
||||
// D("Done!");
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
const char *dsn = NULL;
|
||||
const char *uid = NULL;
|
||||
const char *pwd = NULL;
|
||||
const char *dcs = NULL; // driver connection string
|
||||
const char *sts = NULL; // statements file
|
||||
for (size_t i=1; i<argc; ++i) {
|
||||
const char *arg = argv[i];
|
||||
if (strcmp(arg, "-h")==0) {
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(arg, "--dsn")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<dsn> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
if (dcs) {
|
||||
D("--dcs has already been specified");
|
||||
return 1;
|
||||
}
|
||||
dsn = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--uid")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<uid> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
uid = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--pwd")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<pwd> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
pwd = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--dcs")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<dcs> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
if (dsn || uid || pwd) {
|
||||
D("either of --dsn/--uid/--pwd has already been specified");
|
||||
return 1;
|
||||
}
|
||||
dcs = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--sts")==0) {
|
||||
++i;
|
||||
if (i>=argc) {
|
||||
D("<sts> expected but got nothing");
|
||||
return 1;
|
||||
}
|
||||
sts = argv[i];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
CHK_TEST(test_sqls(dsn, uid, pwd, dcs, sts));
|
||||
D("Done!");
|
||||
return 0;
|
||||
|
||||
if (0) {
|
||||
const char *dsn = (argc>1) ? argv[1] : NULL;
|
||||
const char *uid = (argc>2) ? argv[2] : NULL;
|
||||
const char *pwd = (argc>3) ? argv[3] : NULL;
|
||||
const char *connstr = (argc>4) ? argv[4] : NULL;
|
||||
const char *sqls = (argc>5) ? argv[5] : NULL;
|
||||
|
||||
dsn = NULL;
|
||||
uid = NULL;
|
||||
pwd = NULL;
|
||||
connstr = argv[1];
|
||||
sqls = argv[2];
|
||||
if (0) {
|
||||
CHK_TEST(test_env());
|
||||
|
||||
CHK_TEST(test1(dsn, uid, pwd));
|
||||
|
||||
const char *statements[] = {
|
||||
"drop database if exists m",
|
||||
"create database m",
|
||||
"use m",
|
||||
"drop database m",
|
||||
NULL
|
||||
};
|
||||
CHK_TEST(test_statements(dsn, uid, pwd, statements));
|
||||
|
||||
if (connstr)
|
||||
CHK_TEST(test_driver_connect(connstr));
|
||||
|
||||
if (connstr) {
|
||||
SQLHENV env = {0};
|
||||
SQLHDBC conn = {0};
|
||||
CHK_TEST(open_driver_connect(connstr, &env, &conn));
|
||||
int r = tests(env, conn);
|
||||
SQLDisconnect(conn);
|
||||
SQLFreeConnect(conn);
|
||||
SQLFreeEnv(env);
|
||||
if (r) return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dsn || connstr) && 1) {
|
||||
CHK_TEST(test_sqls(dsn, uid, pwd, connstr, sqls));
|
||||
}
|
||||
|
||||
D("Done!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
import pyodbc
|
||||
# cnxn = pyodbc.connect('DSN={TAOS_DSN};UID={ root };PWD={ taosdata };HOST={ localhost:6030 }', autocommit=True)
|
||||
cnxn = pyodbc.connect('DSN={TAOS_DSN}; UID=root;PWD=taosdata; HOST=localhost:6030', autocommit=True)
|
||||
cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
|
||||
#cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8')
|
||||
#cnxn.setencoding(encoding='utf-8')
|
||||
|
||||
#cursor = cnxn.cursor()
|
||||
#cursor.execute("SELECT * from db.t")
|
||||
#row = cursor.fetchone()
|
||||
#while row:
|
||||
# print(row)
|
||||
# row = cursor.fetchone()
|
||||
#cursor.close()
|
||||
|
||||
#cursor = cnxn.cursor()
|
||||
#cursor.execute("""
|
||||
#INSERT INTO db.t values (?,?,?,?,?,?,?,?,?,?)
|
||||
#""",
|
||||
#"2020-12-12 00:00:00",
|
||||
#1,
|
||||
#27,
|
||||
#32767,
|
||||
#147483647,
|
||||
#223372036854775807,
|
||||
#23.456,
|
||||
#899.999999,
|
||||
#"foo",
|
||||
#"bar")
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("drop database if exists db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create database db");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.mt (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.mt values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')")
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("SELECT * from db.mt")
|
||||
row = cursor.fetchone()
|
||||
while row:
|
||||
print(row)
|
||||
row = cursor.fetchone()
|
||||
cursor.close()
|
||||
|
||||
#cursor = cnxn.cursor()
|
||||
#cursor.execute("drop database if exists db");
|
||||
#cursor.close()
|
||||
#
|
||||
#cursor = cnxn.cursor()
|
||||
#cursor.execute("create database db");
|
||||
#cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(4), blob nchar(4))");
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hell', 'worl')")
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("SELECT * from db.t")
|
||||
row = cursor.fetchone()
|
||||
while row:
|
||||
print(row)
|
||||
row = cursor.fetchone()
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.v (ts timestamp, v1 tinyint)")
|
||||
cursor.close()
|
||||
|
||||
params = [ ('A', 1), ('B', 2), ('C', 3) ]
|
||||
params = [ ('A', 1), ('B', 2), ('C', 3) ]
|
||||
params = [ ('2020-10-16 00:00:00', 1),
|
||||
('2020-10-16 00:00:01', 4),
|
||||
('2020-10-16 00:00:02', 5),
|
||||
('2020-10-16 00:00:03.009', 6) ]
|
||||
cursor = cnxn.cursor()
|
||||
cursor.fast_executemany = True
|
||||
cursor.executemany("insert into db.v values (?, ?)", params)
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("SELECT * from db.v")
|
||||
row = cursor.fetchone()
|
||||
while row:
|
||||
print(row)
|
||||
row = cursor.fetchone()
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("SELECT * from db.v where v1 > ?", 4)
|
||||
row = cursor.fetchone()
|
||||
while row:
|
||||
print(row)
|
||||
row = cursor.fetchone()
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("SELECT * from db.v where v1 > ?", '5')
|
||||
row = cursor.fetchone()
|
||||
while row:
|
||||
print(row)
|
||||
row = cursor.fetchone()
|
||||
cursor.close()
|
||||
|
||||
cursor = cnxn.cursor()
|
||||
cursor.execute("create table db.f (ts timestamp, v1 float)")
|
||||
cursor.close()
|
||||
|
||||
params = [ ('2020-10-20 00:00:10', '123.3') ]
|
||||
cursor = cnxn.cursor()
|
||||
cursor.fast_executemany = True
|
||||
cursor.executemany("insert into db.f values (?, ?)", params)
|
||||
cursor.close()
|
||||
|
|
@ -1,12 +1,23 @@
|
|||
PROJECT(TDengine)
|
||||
|
||||
IF (TD_LINUX)
|
||||
ADD_EXECUTABLE(todbcinst main.c)
|
||||
ADD_EXECUTABLE(todbcinst main.c)
|
||||
ADD_EXECUTABLE(tconv tconv.c)
|
||||
|
||||
IF (TD_LINUX OR TD_DARWIN)
|
||||
TARGET_LINK_LIBRARIES(todbcinst odbc odbcinst)
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_DARWIN)
|
||||
target_include_directories(todbcinst PRIVATE /usr/local/include)
|
||||
target_link_directories(todbcinst PUBLIC /usr/local/lib)
|
||||
target_include_directories(tconv PRIVATE /usr/local/include)
|
||||
target_link_directories(tconv PUBLIC /usr/local/lib)
|
||||
TARGET_LINK_LIBRARIES(tconv iconv)
|
||||
ENDIF ()
|
||||
|
||||
IF (TD_WINDOWS_64)
|
||||
ADD_EXECUTABLE(todbcinst main.c)
|
||||
TARGET_LINK_LIBRARIES(todbcinst odbc32 odbccp32 user32 legacy_stdio_definitions os)
|
||||
TARGET_LINK_LIBRARIES(tconv taos)
|
||||
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/todbcinst.exe DESTINATION .)
|
||||
ENDIF ()
|
||||
|
||||
|
|
Loading…
Reference in New Issue