提交 12ad3514 编写于 作者: F freemine

odbc driver

上级 25e7ff1e
......@@ -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);
}
STableDataBlocks* pBlock = NULL;
assert(pCmd->numOfParams == pBlock->numOfParams);
if (idx < 0 || idx >= pBlock->numOfParams) return -1;
int32_t ret =
tscGetDataBlockFromList(pCmd->pTableBlockHashList, pTableMeta->id.uid, TSDB_PAYLOAD_SIZE, sizeof(SSubmitBlk),
pTableMeta->tableInfo.rowSize, &pTableMetaInfo->name, pTableMeta, &pBlock, NULL);
if (ret != 0) {
// todo handle error
}
if (idx<0 || idx>=pBlock->numOfParams) {
tscError("param %d: out of range", idx);
abort();
}
SParamInfo* param = pBlock->params + idx;
SParamInfo* param = &pBlock->params[idx];
if (type) *type = param->type;
if (bytes) *bytes = param->bytes;
......
!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 ()
# ODBC 驱动 #
- **TAOS ODBC驱动持续更新中**
- **目前导出的ODBC函数包括(注: 有些不常用的函数只是导出,但并未实现)**:
SQLAllocEnv
SQLFreeEnv
SQLAllocConnect
SQLFreeConnect
SQLGetEnvAttr
SQLSetEnvAttr
SQLGetConnectAttr
SQLGetConnectOption
SQLGetInfo
SQLConnect
SQLDisconnect
SQLAllocStmt
SQLAllocHandle
SQLFreeHandle
SQLFreeStmt
SQLExecDirect
SQLNumResultCols
SQLRowCount
SQLColAttribute
SQLGetData
SQLFetch
SQLPrepare
SQLExecute
SQLParamData
SQLPutData
SQLGetDiagRec
SQLBindParameter
SQLDescribeParam
SQLDriverConnect
SQLSetConnectAttr
SQLDescribeCol
SQLBindCol
SQLNumParams
SQLSetStmtAttr
SQLBindParam
SQLCancel
SQLCancelHandle
SQLCloseCursor
SQLColumns
SQLCopyDesc
SQLDataSources
SQLEndTran
SQLFetchScroll
SQLGetCursorName
SQLGetDescField
SQLGetDescRec
SQLGetStmtAttr
SQLGetStmtOption
SQLGetTypeInfo
SQLSetConnectOption
SQLSetCursorName
SQLSetDescField
SQLSetDescRec
SQLSetParam
SQLSetStmtOption
SQLSpecialColumns
SQLStatistics
SQLTables
SQLTransact
`
- **国际化。可以通过在ODBC连接串中指定针对SQLCHAR/SQLWCHAR/taos_charset/system-locale的字符集来解决常见的环境匹配问题**.
- **现有的ODBC客户端工具可以籍此驱动同TAOS数据源互联,包括主流linux/macosx/windows平台**
- **现有的支持ODBC的编程语言可以籍此驱动同TAOS数据源互联, 例如: c/nodejs/python/rust/go已经在上述三个主流平台测试通过, 熟悉其他语言的同学可以发现这基本上是开箱即用**
- **持续更新中**...
# 编译和测试使用
**Note**: 下述所有步骤都在TDengine项目的根目录下进行
**Note**: 请先确保src/connector/odbc如下所示,被包含在src/CMakeLists.txt源文件中
```
...
ADD_SUBDIRECTORY(dnode)
ADD_SUBDIRECTORY(connector/odbc)
ADD_SUBDIRECTORY(connector/jdbc)
```
# Linux下的编译, 以Ubuntu为例
```
sudo apt install unixodbc unixodbc-dev flex
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
```
# MacOSX下的编译, 以Catalina为例,依赖homebrew进行第三方工具安装[https://brew.sh/]
```
brew install unixodbc
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
```
# Windows下的编译, 以Windows 10为例
- 安装windows的`flex`工具. 目前我们使用[https://github.com/lexxmark/winflexbison](url). 安装完成后请确保win_flex.exe所在目录记录于`PATH`环境变量中.
- 安装Microsoft Visual Studio工具, 以VS2019为例
- `"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"`
- `rmdir /s /q debug`
- `cmake -G "NMake Makefiles" -B debug`
- `cmake --build debug`
- `cmake --install debug`
- 以管理员身份打开`命令提示符`
- 安装ODBC驱动: 在上述打开的提示符下执行 `odbcconf /A {INSTALLDRIVER "TAOS | Driver=C:/TDengine/driver/todbc.dll | ConnectFunctions=YYN | DriverODBCVer=03.00"}`
- 新增一个数据源DSN: 执行 `odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN | Server=<host>:<port>"}`
上述步骤出现失败的话,可以参看这些链接:
1. win flex的安装: https://github.com/lexxmark/winflexbison/releases
2. PATH环境变量: https://jingyan.baidu.com/article/8ebacdf02d3c2949f65cd5d0.html
3. 管理员身份: https://blog.csdn.net/weixin_41083002/article/details/81019893
4. 安装odbc驱动/数据源: https://docs.microsoft.com/en-us/sql/odbc/odbcconf-exe?view=sql-server-ver15
# 测试使用
强烈建议您在linux上编译运行taosd服务端,因为当前TAOS还没有windows侧的服务端移植.
**Note1**: <>符号所括起的内容请按您当前的系统填写
**Note2**: `.stmts` 文件存放的是测试用sql语句, 注意其格式为`UTF-8`(不带BOM导引头)
## 按官方文档在linux侧启动taosd,确保选用'UTF-8'作为其字符集
## 在linux下创建数据
```
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/samples/create_data.stmts
--<或指定特殊的ODBC连接字符串 -->
./debug/build/bin/tcodbc -C 'DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>' --sts ./src/connector/odbc/samples/create_data.stmts
```
## 在windows下检索数据
```
.\debug\build\bin\tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>;enc_char=UTF-8" --sts .\src\connector\odbc\samples\query_data.stmts
```
## 在MacOSX下检索数据
```
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;UID=<uid>;PWD=<pwd>;Server=<fqdn>:<port>" --sts ./src/connector/odbc/samples/query_data.stmts
```
## 代码示例
- src/connector/odbc/examples/c
- src/connector/odbc/examples/js
- src/connector/odbc/examples/py
- src/connector/odbc/examples/rust
- src/connector/odbc/examples/go
在linux或MacOSX上, 可以通过修改运行如下脚本来尝试各种测试:
**Note**: 不要忘记替换<host>:<port>
**Note**: 你需要在你的平台上安装nodejs/python/rust/go
**Note**: 你还需要安装对应语言的ODBC包:
-- node-odbc for nodejs: https://www.npmjs.com/package/odbc
-- pyodbc for python: https://pypi.org/project/pyodbc/
-- rust-odbc for rust: https://docs.rs/odbc/0.17.0/odbc/
-- go-odbc for go: https://github.com/alexbrainman/odbc
```
echo c &&
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --sts src/connector/odbc/samples/create_data.stmts &&
echo nodejs &&
./src/connector/odbc/examples/js/odbc.js -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
echo python &&
python3 src/connector/odbc/examples/py/odbc.py -C 'DSN=TAOS_DSN;Server=<host>:<port>' &&
echo rust &&
pushd src/connector/odbc/examples/rust/main && DSN='DSN=TAOS_DSN;Server=<host>:<port>' cargo run && popd &&
echo go &&
DSN='DSN=TAOS_DSN;Server=<host>:<port>' go run src/connector/odbc/examples/go/odbc.go &&
```
## 您可以对比测试一下prepared-batch-insert是否会带来速度的提升:
**注** src/connector/odbc/examples/c/main.c是tcodbc的源代码
```
./debug/build/bin/tcodbc -C "DSN=TAOS_DSN;Server=<host>:<port>" --insert --batch_size 200 --batchs 10000
```
# ODBC Driver #
- **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
```
PROJECT(TDengine)
ADD_SUBDIRECTORY(c)
PROJECT(TDengine)
IF (TD_LINUX)
# AUX_SOURCE_DIRECTORY(. SRC)
ADD_EXECUTABLE(tcodbc main.c)
ADD_EXECUTABLE(tcodbc main.c ../../src/todbc_log.c)
IF (TD_LINUX OR TD_DARWIN)
TARGET_LINK_LIBRARIES(tcodbc odbc)
ADD_EXECUTABLE(tconv tconv.c)
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")
# 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)
ADD_EXECUTABLE(tms main.cpp)
TARGET_LINK_LIBRARIES(tms odbc32)
ENDIF ()
/*******************************************************************************
/* ODBCSQL: a sample program that implements an ODBC command line interpreter.
/*
/* USAGE: ODBCSQL DSN=<dsn name> or
/* ODBCSQL FILEDSN=<file dsn> or
/* ODBCSQL DRIVER={driver name}
/*
/*
/* Copyright(c) Microsoft Corporation. This is a WDAC sample program and
/* is not suitable for use in production environments.
/*
/******************************************************************************/
/* Modules:
/* Main Main driver loop, executes queries.
/* DisplayResults Display the results of the query if any
/* AllocateBindings Bind column data
/* DisplayTitles Print column titles
/* SetConsole Set console display mode
/* HandleError Show ODBC error messages
/******************************************************************************/
#define _UNICODE
#define UNICODE
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <stdlib.h>
#include <sal.h>
/*******************************************/
/* Macro to call ODBC functions and */
/* report an error on failure. */
/* Takes handle, handle type, and stmt */
/*******************************************/
#define TRYODBC(h, ht, x) { RETCODE rc = x;\
if (rc != SQL_SUCCESS) \
{ \
HandleDiagnosticRecord (h, ht, rc); \
} \
if (rc == SQL_ERROR) \
{ \
fwprintf(stderr, L"Error in " L#x L"\n"); \
goto Exit; \
} \
}
/******************************************/
/* Structure to store information about */
/* a column.
/******************************************/
typedef struct STR_BINDING {
SQLSMALLINT cDisplaySize; /* size to display */
WCHAR *wszBuffer; /* display buffer */
SQLLEN indPtr; /* size or null */
BOOL fChar; /* character col? */
struct STR_BINDING *sNext; /* linked list */
} BINDING;
/******************************************/
/* Forward references */
/******************************************/
void HandleDiagnosticRecord (SQLHANDLE hHandle,
SQLSMALLINT hType,
RETCODE RetCode);
void DisplayResults(HSTMT hStmt,
SQLSMALLINT cCols);
void AllocateBindings(HSTMT hStmt,
SQLSMALLINT cCols,
BINDING** ppBinding,
SQLSMALLINT* pDisplay);
void DisplayTitles(HSTMT hStmt,
DWORD cDisplaySize,
BINDING* pBinding);
void SetConsole(DWORD cDisplaySize,
BOOL fInvert);
/*****************************************/
/* Some constants */
/*****************************************/
#define DISPLAY_MAX 50 // Arbitrary limit on column width to display
#define DISPLAY_FORMAT_EXTRA 3 // Per column extra display bytes (| <data> )
#define DISPLAY_FORMAT L"%c %*.*s "
#define DISPLAY_FORMAT_C L"%c %-*.*s "
#define NULL_SIZE 6 // <NULL>
#define SQL_QUERY_SIZE 1000 // Max. Num characters for SQL Query passed in.
#define PIPE L'|'
SHORT gHeight = 80; // Users screen height
int __cdecl wmain(int argc, _In_reads_(argc) WCHAR **argv)
{
SQLHENV hEnv = NULL;
SQLHDBC hDbc = NULL;
SQLHSTMT hStmt = NULL;
WCHAR* pwszConnStr;
WCHAR wszInput[SQL_QUERY_SIZE];
// Allocate an environment
if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv) == SQL_ERROR)
{
fwprintf(stderr, L"Unable to allocate an environment handle\n");
exit(-1);
}
// Register this as an application that expects 3.x behavior,
// you must register something if you use AllocHandle
TRYODBC(hEnv,
SQL_HANDLE_ENV,
SQLSetEnvAttr(hEnv,
SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3,
0));
// Allocate a connection
TRYODBC(hEnv,
SQL_HANDLE_ENV,
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
if (argc > 1)
{
pwszConnStr = *++argv;
}
else
{
pwszConnStr = L"";
}
// Connect to the driver. Use the connection string if supplied
// on the input, otherwise let the driver manager prompt for input.
TRYODBC(hDbc,
SQL_HANDLE_DBC,
SQLDriverConnect(hDbc,
GetDesktopWindow(),
pwszConnStr,
SQL_NTS,
NULL,
0,
NULL,
SQL_DRIVER_COMPLETE));
fwprintf(stderr, L"Connected!\n");
TRYODBC(hDbc,
SQL_HANDLE_DBC,
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
wprintf(L"Enter SQL commands, type (control)Z to exit\nSQL COMMAND>");
// Loop to get input and execute queries
while(_fgetts(wszInput, SQL_QUERY_SIZE-1, stdin))
{
RETCODE RetCode;
SQLSMALLINT sNumResults;
// Execute the query
if (!(*wszInput))
{
wprintf(L"SQL COMMAND>");
continue;
}
RetCode = SQLExecDirect(hStmt,wszInput, SQL_NTS);
switch(RetCode)
{
case SQL_SUCCESS_WITH_INFO:
{
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
// fall through
}
case SQL_SUCCESS:
{
// If this is a row-returning query, display
// results
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLNumResultCols(hStmt,&sNumResults));
if (sNumResults > 0)
{
DisplayResults(hStmt,sNumResults);
}
else
{
SQLLEN cRowCount;
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLRowCount(hStmt,&cRowCount));
if (cRowCount >= 0)
{
wprintf(L"%Id %s affected\n",
cRowCount,
cRowCount == 1 ? L"row" : L"rows");
}
}
break;
}
case SQL_ERROR:
{
HandleDiagnosticRecord(hStmt, SQL_HANDLE_STMT, RetCode);
break;
}
default:
fwprintf(stderr, L"Unexpected return code %hd!\n", RetCode);
}
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLFreeStmt(hStmt, SQL_CLOSE));
wprintf(L"SQL COMMAND>");
}
Exit:
// Free ODBC handles and exit
if (hStmt)
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
if (hDbc)
{
SQLDisconnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
}
if (hEnv)
{
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
}
wprintf(L"\nDisconnected.");
return 0;
}
/************************************************************************
/* DisplayResults: display results of a select query
/*
/* Parameters:
/* hStmt ODBC statement handle
/* cCols Count of columns
/************************************************************************/
void DisplayResults(HSTMT hStmt,
SQLSMALLINT cCols)
{
BINDING *pFirstBinding, *pThisBinding;
SQLSMALLINT cDisplaySize;
RETCODE RetCode = SQL_SUCCESS;
int iCount = 0;
// Allocate memory for each column
AllocateBindings(hStmt, cCols, &pFirstBinding, &cDisplaySize);
// Set the display mode and write the titles
DisplayTitles(hStmt, cDisplaySize+1, pFirstBinding);
// Fetch and display the data
bool fNoData = false;
do {
// Fetch a row
if (iCount++ >= gHeight - 2)
{
int nInputChar;
bool fEnterReceived = false;
while(!fEnterReceived)
{
wprintf(L" ");
SetConsole(cDisplaySize+2, TRUE);
wprintf(L" Press ENTER to continue, Q to quit (height:%hd)", gHeight);
SetConsole(cDisplaySize+2, FALSE);
nInputChar = _getch();
wprintf(L"\n");
if ((nInputChar == 'Q') || (nInputChar == 'q'))
{
goto Exit;
}
else if ('\r' == nInputChar)
{
fEnterReceived = true;
}
// else loop back to display prompt again
}
iCount = 1;
DisplayTitles(hStmt, cDisplaySize+1, pFirstBinding);
}
TRYODBC(hStmt, SQL_HANDLE_STMT, RetCode = SQLFetch(hStmt));
if (RetCode == SQL_NO_DATA_FOUND)
{
fNoData = true;
}
else
{
// Display the data. Ignore truncations
for (pThisBinding = pFirstBinding;
pThisBinding;
pThisBinding = pThisBinding->sNext)
{
if (pThisBinding->indPtr != SQL_NULL_DATA)
{
wprintf(pThisBinding->fChar ? DISPLAY_FORMAT_C:DISPLAY_FORMAT,
PIPE,
pThisBinding->cDisplaySize,
pThisBinding->cDisplaySize,
pThisBinding->wszBuffer);
}
else
{
wprintf(DISPLAY_FORMAT_C,
PIPE,
pThisBinding->cDisplaySize,
pThisBinding->cDisplaySize,
L"<NULL>");
}
}
wprintf(L" %c\n",PIPE);
}
} while (!fNoData);
SetConsole(cDisplaySize+2, TRUE);
wprintf(L"%*.*s", cDisplaySize+2, cDisplaySize+2, L" ");
SetConsole(cDisplaySize+2, FALSE);
wprintf(L"\n");
Exit:
// Clean up the allocated buffers
while (pFirstBinding)
{
pThisBinding = pFirstBinding->sNext;
free(pFirstBinding->wszBuffer);
free(pFirstBinding);
pFirstBinding = pThisBinding;
}
}
/************************************************************************
/* AllocateBindings: Get column information and allocate bindings
/* for each column.
/*
/* Parameters:
/* hStmt Statement handle
/* cCols Number of columns in the result set
/* *lppBinding Binding pointer (returned)
/* lpDisplay Display size of one line
/************************************************************************/
void AllocateBindings(HSTMT hStmt,
SQLSMALLINT cCols,
BINDING **ppBinding,
SQLSMALLINT *pDisplay)
{
SQLSMALLINT iCol;
BINDING *pThisBinding, *pLastBinding = NULL;
SQLLEN cchDisplay, ssType;
SQLSMALLINT cchColumnNameLength;
*pDisplay = 0;
for (iCol = 1; iCol <= cCols; iCol++)
{
pThisBinding = (BINDING *)(malloc(sizeof(BINDING)));
if (!(pThisBinding))
{
fwprintf(stderr, L"Out of memory!\n");
exit(-100);
}
if (iCol == 1)
{
*ppBinding = pThisBinding;
}
else
{
pLastBinding->sNext = pThisBinding;
}
pLastBinding = pThisBinding;
// Figure out the display length of the column (we will
// bind to char since we are only displaying data, in general
// you should bind to the appropriate C type if you are going
// to manipulate data since it is much faster...)
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLColAttribute(hStmt,
iCol,
SQL_DESC_DISPLAY_SIZE,
NULL,
0,
NULL,
&cchDisplay));
// Figure out if this is a character or numeric column; this is
// used to determine if we want to display the data left- or right-
// aligned.
// SQL_DESC_CONCISE_TYPE maps to the 1.x SQL_COLUMN_TYPE.
// This is what you must use if you want to work
// against a 2.x driver.
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLColAttribute(hStmt,
iCol,
SQL_DESC_CONCISE_TYPE,
NULL,
0,
NULL,
&ssType));
pThisBinding->fChar = (ssType == SQL_CHAR ||
ssType == SQL_VARCHAR ||
ssType == SQL_LONGVARCHAR);
pThisBinding->sNext = NULL;
// Arbitrary limit on display size
if (cchDisplay > DISPLAY_MAX)
cchDisplay = DISPLAY_MAX;
// Allocate a buffer big enough to hold the text representation
// of the data. Add one character for the null terminator
pThisBinding->wszBuffer = (WCHAR *)malloc((cchDisplay+1) * sizeof(WCHAR));
if (!(pThisBinding->wszBuffer))
{
fwprintf(stderr, L"Out of memory!\n");
exit(-100);
}
// Map this buffer to the driver's buffer. At Fetch time,
// the driver will fill in this data. Note that the size is
// count of bytes (for Unicode). All ODBC functions that take
// SQLPOINTER use count of bytes; all functions that take only
// strings use count of characters.
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLBindCol(hStmt,
iCol,
SQL_C_TCHAR,
(SQLPOINTER) pThisBinding->wszBuffer,
(cchDisplay + 1) * sizeof(WCHAR),
&pThisBinding->indPtr));
// Now set the display size that we will use to display
// the data. Figure out the length of the column name
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLColAttribute(hStmt,
iCol,
SQL_DESC_NAME,
NULL,
0,
&cchColumnNameLength,
NULL));
pThisBinding->cDisplaySize = max((SQLSMALLINT)cchDisplay, cchColumnNameLength);
if (pThisBinding->cDisplaySize < NULL_SIZE)
pThisBinding->cDisplaySize = NULL_SIZE;
*pDisplay += pThisBinding->cDisplaySize + DISPLAY_FORMAT_EXTRA;
}
return;
Exit:
exit(-1);
return;
}
/************************************************************************
/* DisplayTitles: print the titles of all the columns and set the
/* shell window's width
/*
/* Parameters:
/* hStmt Statement handle
/* cDisplaySize Total display size
/* pBinding list of binding information
/************************************************************************/
void DisplayTitles(HSTMT hStmt,
DWORD cDisplaySize,
BINDING *pBinding)
{
WCHAR wszTitle[DISPLAY_MAX];
SQLSMALLINT iCol = 1;
SetConsole(cDisplaySize+2, TRUE);
for (; pBinding; pBinding = pBinding->sNext)
{
TRYODBC(hStmt,
SQL_HANDLE_STMT,
SQLColAttribute(hStmt,
iCol++,
SQL_DESC_NAME,
wszTitle,
sizeof(wszTitle), // Note count of bytes!
NULL,
NULL));
wprintf(DISPLAY_FORMAT_C,
PIPE,
pBinding->cDisplaySize,
pBinding->cDisplaySize,
wszTitle);
}
Exit:
wprintf(L" %c", PIPE);
SetConsole(cDisplaySize+2, FALSE);
wprintf(L"\n");
}
/************************************************************************
/* SetConsole: sets console display size and video mode
/*
/* Parameters
/* siDisplaySize Console display size
/* fInvert Invert video?
/************************************************************************/
void SetConsole(DWORD dwDisplaySize,
BOOL fInvert)
{
HANDLE hConsole;
CONSOLE_SCREEN_BUFFER_INFO csbInfo;
// Reset the console screen buffer size if necessary
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole != INVALID_HANDLE_VALUE)
{
if (GetConsoleScreenBufferInfo(hConsole, &csbInfo))
{
if (csbInfo.dwSize.X < (SHORT) dwDisplaySize)
{
csbInfo.dwSize.X = (SHORT) dwDisplaySize;
SetConsoleScreenBufferSize(hConsole, csbInfo.dwSize);
}
gHeight = csbInfo.dwSize.Y;
}
if (fInvert)
{
SetConsoleTextAttribute(hConsole, (WORD)(csbInfo.wAttributes | BACKGROUND_BLUE));
}
else
{
SetConsoleTextAttribute(hConsole, (WORD)(csbInfo.wAttributes & ~(BACKGROUND_BLUE)));
}
}
}
/************************************************************************
/* HandleDiagnosticRecord : display error/warning information
/*
/* Parameters:
/* hHandle ODBC handle
/* hType Type of handle (HANDLE_STMT, HANDLE_ENV, HANDLE_DBC)
/* RetCode Return code of failing command
/************************************************************************/
void HandleDiagnosticRecord (SQLHANDLE hHandle,
SQLSMALLINT hType,
RETCODE RetCode)
{
SQLSMALLINT iRec = 0;
SQLINTEGER iError;
WCHAR wszMessage[1000];
WCHAR wszState[SQL_SQLSTATE_SIZE+1];
if (RetCode == SQL_INVALID_HANDLE)
{
fwprintf(stderr, L"Invalid handle!\n");
return;
}
while (SQLGetDiagRec(hType,
hHandle,
++iRec,
wszState,
&iError,
wszMessage,
(SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
(SQLSMALLINT *)NULL) == SQL_SUCCESS)
{
// Hide data truncated..
if (wcsncmp(wszState, L"01004", 5))
{
fwprintf(stderr, L"[%5.5s] %s (%d)\n", wszState, wszMessage, iError);
}
}
}
package main
import (
"context"
"database/sql"
"flag"
"log"
"os"
"os/signal"
"time"
_ "github.com/alexbrainman/odbc"
)
var pool *sql.DB // Database connection pool.
func main() {
id := flag.Int64("id", 32768, "person ID to find")
dsn := flag.String("dsn", os.Getenv("DSN"), "connection data source name")
flag.Parse()
if len(*dsn) == 0 {
log.Fatal("missing dsn flag")
}
if *id == 0 {
log.Fatal("missing person ID")
}
var err error
// Opening a driver typically will not attempt to connect to the database.
pool, err = sql.Open("odbc", *dsn)
if err != nil {
// This will not be a connection error, but a DSN parse error or
// another initialization error.
log.Fatal("unable to use data source name", err)
}
defer pool.Close()
pool.SetConnMaxLifetime(0)
pool.SetMaxIdleConns(3)
pool.SetMaxOpenConns(3)
ctx, stop := context.WithCancel(context.Background())
defer stop()
appSignal := make(chan os.Signal, 3)
signal.Notify(appSignal, os.Interrupt)
go func() {
select {
case <-appSignal:
stop()
}
}()
Ping(ctx)
Query(ctx, *id)
}
// Ping the database to verify DSN provided by the user is valid and the
// server accessible. If the ping fails exit the program with an error.
func Ping(ctx context.Context) {
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
if err := pool.PingContext(ctx); err != nil {
log.Fatalf("unable to connect to database: %v", err)
}
}
// Query the database for the information requested and prints the results.
// If the query fails exit the program with an error.
func Query(ctx context.Context, id int64) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
var name string
err := pool.QueryRowContext(ctx, "select name from m.t").Scan(&name)
if err != nil {
log.Fatal("unable to execute search query", err)
}
log.Println("name=", name)
}
#!/usr/bin/env node
const odbc = require('odbc');
const path = require('path');
function usage() {
var arg = path.basename(process.argv[1]);
console.error(`usage:`);
console.error(`${arg} --DSN <DSN> --UID <uid> --PWD <pwd> --Server <host:port>`);
console.error(`${arg} -C <conn_str>`);
console.error(` conn_str eg: 'DSN={TAOS_DSN};UID=root;PWD=taosdata;Server=host:port'`);
}
var cfg = { };
if (process.argv.length==2) {
usage();
process.exit(0);
}
var i;
for (i=2; i<process.argv.length; ++i) {
var arg = process.argv[i];
if (arg=='-h') {
usage();
process.exit(0);
}
if (arg=="--DSN") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <dns> after --DSN but got nothing`);
usage(process.argv[1]);
process.exit(1);
}
arg = process.argv[i];
cfg.dsn = arg;
continue;
}
if (arg=="--UID") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <uid> after --UID but got nothing`);
usage(process.argv[1]);
process.exit(1);
}
arg = process.argv[i];
cfg.uid = arg;
continue;
}
if (arg=="--PWD") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <pwd> after --PWD but got nothing`);
usage(process.argv[1]);
process.exit(1);
}
arg = process.argv[i];
cfg.pwd = arg;
continue;
}
if (arg=="--Server") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <host:port> after --Server but got nothing`);
usage(process.argv[1]);
process.exit(1);
}
arg = process.argv[i];
cfg.server = arg;
continue;
}
if (arg=="-C") {
++i;
if (i>=process.argv.length) {
console.error(`expecting <conn_str> after -C but got nothing`);
console.error(` conn_str eg: 'DSN={TAOS_DSN};UID=root;PWD=taosdata;Server=host:port'`);
process.exit(1);
}
arg = process.argv[i];
cfg.conn_str = arg;
continue;
}
console.error(`unknown argument: [${arg}]`);
usage(process.argv[1]);
process.exit(1);
}
var connectionString = cfg.conn_str;
if (!cfg.conn_str) {
connectionString = `DSN={${cfg.dsn}}; UID=${cfg.uid}; PWD=${cfg.pwd}; Server=${cfg.server}`;
}
(async function () {
const connStr = connectionString;
try {
console.log(`connecting [${connStr}]...`);
const connection = await odbc.connect(connStr);
await connection.query('create database if not exists m');
await connection.query('use m');
await connection.query('drop table if exists t');
await connection.query('create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), name nchar(3))');
await connection.query('insert into t values("2020-01-02 12:34:56.781", 1, 127, 32767, 32768, 32769, 123.456, 789.987, "hello", "我和你")');
console.log('.........');
result = await connection.query('select * from t');
console.log(result[0]);
statement = await connection.createStatement();
await statement.prepare('INSERT INTO t (ts, v1) VALUES(?, ?)');
await statement.bind(['2020-02-02 11:22:33.449', 89]);
result = await statement.execute();
console.log(result);
result = await connection.query('select * from t');
console.log(result[0]);
console.log(result[1]);
} catch (e) {
console.log('error:', e);
}
})();
{
"dependencies": {
"odbc": "^2.3.6"
}
}
package.cpath = package.cpath .. ";/usr/local/lib/lib?.dylib"
-- load driver
local driver = require "luasql.odbc"
-- create environment object
env = assert (driver.odbc())
-- connect to data source
con = assert (env:connect("TAOS_DSN", "root", "taosdata"))
-- reset our table
-- res = con:execute"DROP TABLE people"
-- res = assert (con:execute[[
-- CREATE TABLE people(
-- name varchar(50),
-- email varchar(50)
-- )
-- ]])
-- -- add a few elements
-- list = {
-- { name="Jose das Couves", email="jose@couves.com", },
-- { name="Manoel Joaquim", email="manoel.joaquim@cafundo.com", },
-- { name="Maria das Dores", email="maria@dores.com", },
-- }
-- for i, p in pairs (list) do
-- res = assert (con:execute(string.format([[
-- INSERT INTO people
-- VALUES ('%s', '%s')]], p.name, p.email)
-- ))
-- end
-- -- retrieve a cursor
-- cur = assert (con:execute"SELECT name, email from people")
-- -- print all rows, the rows will be indexed by field names
-- row = cur:fetch ({}, "a")
-- while row do
-- print(string.format("Name: %s, E-mail: %s", row.name, row.email))
-- -- reusing the table of results
-- row = cur:fetch (row, "a")
-- end
cur = assert(con:execute"select * from m.t")
row = cur:fetch({}, "a")
while row do
print(string.format("Name: %s", row.name))
row = cur:fetch(row, "a")
end
-- close everything
cur:close() -- already closed because all the result set was consumed
con:close()
env:close()
import pyodbc
import argparse
import sys
parser = argparse.ArgumentParser(description='Access TDengine via ODBC.')
parser.add_argument('--DSN', help='DSN to use')
parser.add_argument('--UID', help='UID to use')
parser.add_argument('--PWD', help='PWD to use')
parser.add_argument('--Server', help='Server to use')
parser.add_argument('-C', metavar='CONNSTR', help='Connection string to use')
args = parser.parse_args()
a = 'DSN=%s'%args.DSN if args.DSN else None
b = 'UID=%s'%args.UID if args.UID else None
c = 'PWD=%s'%args.PWD if args.PWD else None
d = 'Server=%s'%args.Server if args.Server else None
conn_str = ';'.join(filter(None, [a,b,c,d])) if args.DSN else None
conn_str = conn_str if conn_str else args.C
if not conn_str:
parser.print_help(file=sys.stderr)
exit()
print('connecting: [%s]' % conn_str)
cnxn = pyodbc.connect(conn_str, autocommit=True)
cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
cursor = cnxn.cursor()
cursor.execute("drop database if exists db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create database db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.mt (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(10), blob nchar(10))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.mt values('2020-10-13 06:44:00.123', 1, 127, 32767, 2147483647, 32769, 123.456, 789.987, 'hello', 'helloworld')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00.234", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd129")
##cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", 1502535178128, 9223372036854775807, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo".encode('utf-8'), "wo哈rlxd123");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("""
INSERT INTO db.mt (ts,b,v1,v2,v4,v8,f4,f8,bin,blob) values (?,?,?,?,?,?,?,?,?,?)
""",
"2020-12-12 00:00:00",
'true',
'-127',
'-32767',
'-2147483647',
'-9223372036854775807',
'-1.23e10',
'-11.23e6',
'abcdefghij'.encode('utf-8'),
"人啊大发测试及abc")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("drop database if exists db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create database db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(4), blob nchar(4))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hell', 'w我你z')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.v (ts timestamp, v1 tinyint, v2 smallint, name nchar(10), ts2 timestamp)")
cursor.close()
params = [ ('2020-10-16 00:00:00.123', 19, '2111-01-02 01:02:03.123'),
('2020-10-16 00:00:01', 41, '2111-01-02 01:02:03.423'),
('2020-10-16 00:00:02', 57, '2111-01-02 01:02:03.153'),
('2020-10-16 00:00:03.009', 26, '2111-01-02 01:02:03.623') ]
cursor = cnxn.cursor()
cursor.fast_executemany = True
print('py:...................')
cursor.executemany("insert into db.v (ts, v1, ts2) values (?, ?, ?)", params)
print('py:...................')
cursor.close()
## cursor = cnxn.cursor()
## cursor.execute("SELECT * from db.v where v1 > ?", 4)
## row = cursor.fetchone()
## while row:
## print(row)
## row = cursor.fetchone()
## cursor.close()
##
## cursor = cnxn.cursor()
## cursor.execute("SELECT * from db.v where v1 > ?", '5')
## row = cursor.fetchone()
## while row:
## print(row)
## row = cursor.fetchone()
## cursor.close()
[package]
name = "main"
version = "0.1.0"
authors = ["freemine <freemine@yeah.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
odbc = "0.17.0"
env_logger = "0.8.2"
extern crate odbc;
// Use this crate and set environmet variable RUST_LOG=odbc to see ODBC warnings
extern crate env_logger;
use odbc::*;
use odbc_safe::AutocommitOn;
use std::env;
fn main() {
env_logger::init();
let conn_str = env::var("DSN").unwrap();
match connect(&conn_str) {
Ok(()) => println!("Success"),
Err(diag) => println!("Error: {}", diag),
}
}
fn connect(conn_str: &str) -> std::result::Result<(), DiagnosticRecord> {
let env = create_environment_v3().map_err(|e| e.unwrap())?;
let conn = env.connect_with_connection_string(conn_str)?;
execute_statement(&conn)
}
fn execute_statement<'env>(conn: &Connection<'env, AutocommitOn>) -> Result<()> {
let stmt = Statement::with_parent(conn)?;
match stmt.exec_direct("select * from m.t")? {
Data(mut stmt) => {
let cols = stmt.num_result_cols()?;
println!("cols: {}", cols);
while let Some(mut cursor) = stmt.fetch()? {
for i in 1..(cols + 1) {
match cursor.get_data::<&str>(i as u16)? {
Some(val) => print!(" {}", val),
None => print!(" NULL"),
}
}
println!("");
}
}
NoData(_) => println!("Query executed, no data returned"),
}
Ok(())
}
#P: positive sample
#N: negative sample
P:drop database if exists m;
P: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));
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 ()
此差异已折叠。
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _base_h_
#define _base_h_
#include "todbc_buf.h"
#include "todbc_iconv.h"
#include "todbc_log.h"
#include "taos.h"
#include "taoserror.h"
#include <sql.h>
#include <sqlext.h>
typedef struct errs_s errs_t;
typedef struct env_s env_t;
typedef struct conn_s conn_t;
typedef struct stmt_s stmt_t;
typedef struct param_s param_t;
typedef struct field_s field_t;
typedef struct rs_s rs_t;
typedef struct col_s col_t;
#define GET_REF(obj) atomic_load_64(&obj->refcount)
#define INC_REF(obj) atomic_add_fetch_64(&obj->refcount, 1)
#define DEC_REF(obj) atomic_sub_fetch_64(&obj->refcount, 1)
// public
#ifdef __GNUC__
__attribute__((format(printf, 6, 7)))
#endif
SQLRETURN errs_append(errs_t *errs, const char sql_state[6], const char *file, int line, const char *func, const char *fmt, ...);
int errs_count(errs_t *errs);
// 0/-1: ok/no-error
int errs_fetch(errs_t *errs, int idx, const char **sql_state, const char **err_str);
void errs_clear(SQLSMALLINT HandleType, SQLHANDLE InputHandle);
// err: if <>0, will generate strerror
#define SET_ERR(errs, sql_state, fmt, ...) \
errs_append(errs, sql_state, __FILE__, __LINE__, __func__, "%s" fmt "", "", ##__VA_ARGS__)
#define SET_OOM(errs, fmt, ...) SET_ERR(errs, "TD001", "OOM:" fmt, ##__VA_ARGS__)
#define SET_NIY(errs, fmt, ...) SET_ERR(errs, "TDC00", "NIY:" fmt, ##__VA_ARGS__)
#define SET_GENERAL(errs, fmt, ...) SET_ERR(errs, "TD000", "GEN:" fmt, ##__VA_ARGS__)
// public
errs_t* env_get_errs(env_t *env);
void env_clr_errs(env_t *env);
void env_inc_ref(env_t *env);
void env_dec_ref(env_t *env);
// public
errs_t* conn_get_errs(conn_t *conn);
void conn_clr_errs(conn_t *conn);
// public
errs_t* stmt_get_errs(stmt_t *stmt);
void stmt_clr_errs(stmt_t *stmt);
#endif // _base_h_
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(TDengine)
aux_source_directory(. SRC)
add_library(todbc_base STATIC ${SRC})
if (TD_DARWIN)
target_include_directories(todbc_base PRIVATE /usr/local/include)
endif ()
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "conn.h"
#include "env.h"
#include "../todbc_tls.h"
static void init_encodes(conn_t *conn, env_t *env) {
OILE(conn, "");
OILE(env, "");
const char *enc_charset = env->enc_charset;
snprintf(conn->enc_src, sizeof(conn->enc_src), "%s", UTF8_ENC); // compile-time constant
snprintf(conn->enc_wchar, sizeof(conn->enc_wchar), "%s", UTF16_ENC); // compile-time constant
snprintf(conn->enc_char, sizeof(conn->enc_char), "%s", enc_charset); // runtime default
snprintf(conn->enc_db, sizeof(conn->enc_db), "%s", enc_charset); // runtime default
snprintf(conn->enc_locale, sizeof(conn->enc_locale), "%s", enc_charset); // runtime default
OD("enc_src:[%s]; enc_wchar:[%s]; enc_char:[%s]; enc_db:[%s]; enc_locale:[%s]",
conn->enc_src, conn->enc_wchar, conn->enc_char, conn->enc_db, conn->enc_locale);
}
static int conn_init(conn_t *conn, env_t *env) {
OILE(conn, "");
OILE(env, "");
errs_t *errs = &env->errs;
OILE(conn->env==NULL, "");
int r = errs_init(&conn->errs);
if (r) return -1;
init_encodes(conn, env);
if (SQL_SUCCESS!=conn_check_charset(conn, errs)) {
return -1;
}
conn->env = env;
env_inc_ref(env);
return 0;
};
static void conn_release(conn_t *conn) {
if (!conn) return;
env_t *env = conn->env;
if (!env) return;
conn->env = NULL;
env_dec_ref(env);
}
conn_t* conn_new(env_t *env) {
conn_t *conn = (conn_t*)calloc(1, sizeof(*conn));
if (!conn) return NULL;
if (conn_init(conn, env)) {
OILE(conn->env==NULL, "");
free(conn);
return NULL;
}
return conn;
}
void conn_free(conn_t *conn) {
if (!conn) return;
// clean ext stuff
if (conn->ext.free_conn) {
conn->ext.free_conn(conn);
}
// clean conn stuffs
conn_release(conn);
free(conn);
}
static SQLRETURN do_check_charset(errs_t *errs, const char *enc_charset, todbc_enc_t *enc) {
*enc = todbc_tls_iconv_enc(enc_charset);
if (enc->enc[0]=='\0') {
if (errs) {
SET_GENERAL(errs, "unknown charset [%s]", enc_charset);
}
return SQL_ERROR;
}
return SQL_SUCCESS;
}
SQLRETURN conn_check_charset(conn_t *conn, errs_t *errs) {
OILE(conn, "");
todbc_enc_t enc;
SQLRETURN r;
r = do_check_charset(errs, conn->enc_char, &enc);
if (r!=SQL_SUCCESS) return r;
r = do_check_charset(errs, conn->enc_db, &enc);
if (r!=SQL_SUCCESS) return r;
r = do_check_charset(errs, conn->enc_locale, &enc);
if (r!=SQL_SUCCESS) return r;
r = do_check_charset(errs, conn->enc_src, &enc);
if (r!=SQL_SUCCESS) return r;
r = do_check_charset(errs, conn->enc_wchar, &enc);
if (r!=SQL_SUCCESS) return r;
if (enc.variable_char_size!=-1) {
OE("does not support [%s] for WCHAR", conn->enc_wchar);
if (errs) {
SET_GENERAL(errs, "does not support [%s] for WCHAR", conn->enc_wchar);
}
return SQL_ERROR;
}
if (enc.char_size<=0) {
if (errs) {
SET_GENERAL(errs, "unknown [%s] for WCHAR", conn->enc_wchar);
}
return SQL_ERROR;
}
conn->wchar_size = (size_t)enc.char_size;
return SQL_SUCCESS;
}
int conn_add_stmt(conn_t *conn, stmt_t *stmt) {
OILE(conn, "");
OILE(stmt, "");
OILE(stmt->owner==NULL, "");
OILE(stmt->next==NULL, "");
OILE(stmt->prev==NULL, "");
OILE(conn->ext.init_stmt, "");
if (conn->ext.init_stmt(stmt)) {
return -1;
}
stmts_t *owner = &conn->stmts;
stmt->owner = owner;
stmt->prev = owner->tail;
if (owner->tail) owner->tail->next = stmt;
else owner->head = stmt;
owner->tail = stmt;
++owner->count;
owner->conn = conn;
return 0;
}
void conn_del_stmt(conn_t *conn, stmt_t *stmt) {
OILE(conn, "");
OILE(stmt, "");
OILE(stmt->owner, "");
OILE(stmt->owner==&conn->stmts, "");
OILE(stmt->owner->conn==conn, "");
stmts_t *owner = stmt->owner;
stmt_t *next = stmt->next;
stmt_t *prev = stmt->prev;
if (next) next->prev = prev;
else owner->tail = prev;
if (prev) prev->next = next;
else owner->head = next;
--owner->count;
stmt->next = NULL;
stmt->prev = NULL;
stmt->owner = NULL;
}
SQLRETURN conn_connect(conn_t *conn) {
OILE(conn, "");
OILE(conn->ext.connect, "");
return conn->ext.connect(conn);
}
void conn_disconnect(conn_t *conn) {
OILE(conn, "");
OILE(conn->ext.disconnect, "");
conn->ext.disconnect(conn);
}
// public
errs_t* conn_get_errs(conn_t *conn) {
OILE(conn, "");
return &conn->errs;
}
void conn_clr_errs(conn_t *conn) {
if (!conn) return;
errs_reclaim(&conn->errs);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _conn_h_
#define _conn_h_
#include "../base.h"
#include "stmt.h"
#include "../todbc_flex.h"
typedef struct conn_ext_s conn_ext_t;
struct conn_ext_s {
void *ext;
void (*free_conn)(conn_t *conn);
int (*init_stmt)(stmt_t *stmt);
SQLRETURN (*connect)(conn_t *conn);
void (*disconnect)(conn_t *conn);
};
struct conn_s {
env_t *env;
char enc_src[64]; // c source file encoding
char enc_char[64]; // SQL_CHAR encoding
char enc_wchar[64]; // SQL_WCHAR encoding
char enc_db[64]; // taos client encoding
// use this for system i/o, such as reading from stdin, writing to stdout/stderr
char enc_locale[64]; // default: current localee
size_t wchar_size; // shall be fix-length
conn_val_t val;
stmts_t stmts;
errs_t errs;
conn_ext_t ext;
unsigned int connect:2;
};
#define CONN_SET_CONNECTING(conn) (conn->connect=0x01)
#define CONN_SET_CONNECTED(conn) (conn->connect=0x02)
#define CONN_SET_NORM(conn) (conn->connect=0x00)
#define CONN_IS_CONNECTING(conn) (conn->connect==0x01)
#define CONN_IS_CONNECTED(conn) (conn->connect==0x02)
#define CONN_IS_NORM(conn) (conn->connect==0x00)
conn_t* conn_new(env_t *env);
void conn_free(conn_t *conn);
SQLRETURN conn_check_charset(conn_t *conn, errs_t *errs);
int conn_add_stmt(conn_t *conn, stmt_t *stmt);
void conn_del_stmt(conn_t *conn, stmt_t *stmt);
SQLRETURN conn_connect(conn_t *conn);
void conn_disconnect(conn_t *conn);
#endif // _conn_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "env.h"
#include <pthread.h>
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static char default_charset[64] = {0};
static void init_routine(void) {
OD("compiled with ODBCVER:[0x%04x]", ODBCVER);
const char *charset = NULL;
setlocale(LC_ALL, "");
const char *locale = setlocale(LC_CTYPE, NULL);
if (locale) {
const char *dot = strrchr(locale, '.');
if (dot) charset = dot + 1;
}
if (!charset) {
#ifdef _MSC_VER
charset = "CP936";
#else
charset = "UTF-8";
#endif
OD("failed to find original locale, fall back to [%s]", charset);
} else {
#ifdef _MSC_VER
char buf[64];
snprintf(buf, sizeof(buf), "CP%s", charset);
charset = buf;
#endif
OD("system default charset: [%s]", charset);
}
snprintf(default_charset, sizeof(default_charset), "%s", charset);
}
static void env_release(env_t *env) {
if (!env) return;
OILE(env->refcount==0, "");
env_clr_errs(env);
}
int env_init(env_t *env) {
OILE(env, "");
OILE(env->refcount==0, "");
pthread_once(&init_once, init_routine);
int r = errs_init(&env->errs);
if (r) return -1;
snprintf(env->enc_charset, sizeof(env->enc_charset), "%s", default_charset);
env->odbc_ver = SQL_OV_ODBC3;
env->refcount = 1;
return 0;
}
// public
errs_t* env_get_errs(env_t *env) {
OILE(env, "");
return &env->errs;
}
void env_clr_errs(env_t *env) {
if (!env) return;
errs_reclaim(&env->errs);
}
void env_inc_ref(env_t *env) {
OILE(env, "");
int64_t rc = INC_REF(env);
OILE(rc>=2, "");
}
void env_dec_ref(env_t *env) {
OILE(env, "");
int64_t rc = DEC_REF(env);
if (rc>0) return;
OILE(rc==0, "");
env_release(env);
free(env);
}
env_t* env_create(void) {
env_t *env = (env_t*)calloc(1, sizeof(*env));
if (!env) return NULL;
if (env_init(env)) {
free(env);
return NULL;
}
return env;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _env_h_
#define _env_h_
#include "../base.h"
#include "err.h"
struct env_s {
int64_t refcount;
char enc_charset[64]; // default charset from system locale
int32_t odbc_ver; // default SQL_OV_ODBC3
errs_t errs;
void (*env_free)(env_t* env);
};
int env_init(env_t *env);
env_t* env_create(void);
#endif // _env_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "err.h"
#include "env.h"
#include "conn.h"
#include "stmt.h"
struct err_s {
char sql_state[6];
char *err_str; // no ownership
};
static void errs_clr(errs_t *errs) {
if (!errs) return;
errs->count = 0;
errs->errs = NULL;
}
int errs_init(errs_t *errs) {
OILE(errs && errs->cache==NULL, "");
OILE(errs->count==0 && errs->errs==NULL, "");
errs->cache = todbc_buf_create();
return errs->cache ? 0 : -1;
}
void errs_reclaim(errs_t *errs) {
if (!errs) return;
if (!errs->cache) return;
errs_clr(errs);
todbc_buf_reclaim(errs->cache);
}
void errs_release(errs_t *errs) {
if (!errs) return;
if (!errs->cache) return;
errs_clr(errs);
todbc_buf_free(errs->cache);
errs->cache = NULL;
}
// public
#ifdef __GNUC__
__attribute__((format(printf, 6, 7)))
#endif
SQLRETURN errs_append(errs_t *errs, const char sql_state[6], const char *file, int line, const char *func, const char *fmt, ...)
{
OILE(errs, "");
OILE(errs->cache, "");
todbc_buf_t *cache = errs->cache;
const char *name = basename((char*)file);
char *buf = NULL;
size_t blen = 0;
while (1) {
char *p = buf;
size_t bytes = blen;
int count = 0;
int n = 0;
va_list ap;
va_start(ap, fmt);
if (bytes<0) bytes = 0;
n = vsnprintf(p, bytes, fmt, ap);
va_end(ap);
OILE(n>=0, "");
count += n;
if (p) p += n;
if (bytes) bytes -= (size_t)n;
if (bytes<0) bytes = 0;
n = snprintf(p, bytes, "@%s[%d]%s()\n", name, line, func);
OILE(n>=0, "");
count += n;
if (p) break;
buf = todbc_buf_alloc(cache, (size_t)count + 1);
if (!buf) return SQL_ERROR;
blen = (size_t)count;
}
size_t bytes = (size_t)(errs->count + 1) * sizeof(err_t);
err_t *es = (err_t*)todbc_buf_realloc(cache, errs->errs, bytes);
if (!es) return SQL_ERROR;
errs->errs = es;
errs->count += 1;
err_t *err = errs->errs + errs->count - 1;
snprintf(err->sql_state, sizeof(err->sql_state), "%s", sql_state);
err->err_str = buf;
return SQL_SUCCESS;
}
int errs_count(errs_t *errs) {
OILE(errs, "");
OILE(errs->cache, "");
return errs->count;
}
// 0/-1: ok/no-error
int errs_fetch(errs_t *errs, int idx, const char **sql_state, const char **err_str) {
OILE(errs, "");
OILE(errs->cache, "");
if (errs->count<=0) return -1;
if (idx<0 || idx>=errs->count) return -1;
err_t *err = errs->errs + idx;
if (sql_state) *sql_state = err->sql_state;
if (err_str) *err_str = err->err_str;
return 0;
}
void errs_clear(SQLSMALLINT HandleType, SQLHANDLE InputHandle) {
errs_t *errs = NULL;
if (InputHandle==NULL) return;
switch (HandleType)
{
case SQL_HANDLE_ENV:
{
env_t *env = (env_t*)InputHandle;
errs = &env->errs;
} break;
case SQL_HANDLE_DBC:
{
conn_t *conn = (conn_t*)InputHandle;
errs = &conn->errs;
} break;
case SQL_HANDLE_STMT:
{
stmt_t *stmt = (stmt_t*)InputHandle;
errs = &stmt->errs;
} break;
default:
{
ONIY(0, "");
} break;
}
if (!errs) return;
errs_reclaim(errs);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _err_h_
#define _err_h_
#include "../base.h"
typedef struct err_s err_t;
struct errs_s {
todbc_buf_t *cache;
int count;
err_t *errs;
};
int errs_init(errs_t *errs);
void errs_reclaim(errs_t *errs);
void errs_release(errs_t *errs);
#endif // _err_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "field.h"
static void fieldset_clr_fields(fieldset_t *fieldset) {
if (!fieldset) return;
fieldset->fields = NULL;
fieldset->n_fields = 0;
}
static void fieldset_clr_bindings(fieldset_t *fieldset) {
if (!fieldset) return;
fieldset->bindings = NULL;
fieldset->n_bindings = 0;
}
void fieldset_init_fields(fieldset_t *fieldset) {
if (!fieldset) return;
if (fieldset->fields_cache) return;
fieldset->fields_cache = todbc_buf_create();
}
void fieldset_init_bindings(fieldset_t *fieldset) {
if (!fieldset) return;
if (fieldset->bindings_cache) return;
fieldset->bindings_cache = todbc_buf_create();
}
void fieldset_reclaim_fields(fieldset_t *fieldset) {
if (!fieldset) return;
if (!fieldset->fields_cache) return;
todbc_buf_reclaim(fieldset->fields_cache);
fieldset_clr_fields(fieldset);
}
void fieldset_reclaim_bindings(fieldset_t *fieldset) {
if (!fieldset) return;
if (!fieldset->bindings_cache) return;
todbc_buf_reclaim(fieldset->bindings_cache);
fieldset_clr_bindings(fieldset);
}
void fieldset_release(fieldset_t *fieldset) {
if (!fieldset) return;
fieldset_reclaim_fields(fieldset);
fieldset_reclaim_bindings(fieldset);
if (fieldset->fields_cache) {
todbc_buf_free(fieldset->fields_cache);
fieldset->fields_cache = NULL;
}
if (fieldset->bindings_cache) {
todbc_buf_free(fieldset->bindings_cache);
fieldset->bindings_cache = NULL;
}
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _field_h_
#define _field_h_
#include "../base.h"
typedef struct fieldset_s fieldset_t;
typedef struct col_binding_s col_binding_t;
typedef struct field_arg_s field_arg_t;
// SQLDescribeCol
struct field_arg_s {
SQLUSMALLINT ColumnNumber;
SQLCHAR *ColumnName;
SQLSMALLINT BufferLength;
SQLSMALLINT *NameLength;
SQLSMALLINT *DataType; // sql data type
SQLULEN *ColumnSize;
SQLSMALLINT *DecimalDigits;
SQLSMALLINT *Nullable;
};
struct field_s {
SQLUSMALLINT ColumnNumber;
SQLCHAR ColumnName[64]; // hard-coded
SQLSMALLINT DataType; // sql data type
SQLULEN ColumnSize;
SQLSMALLINT DecimalDigits;
SQLSMALLINT Nullable;
};
// SQLBindCol; SQLGetData
struct col_binding_s {
SQLUSMALLINT ColumnNumber;
SQLSMALLINT TargetType; // sql c data type
SQLPOINTER TargetValue;
SQLLEN BufferLength;
SQLLEN *StrLen_or_IndPtr;
};
struct fieldset_s {
todbc_buf_t *fields_cache;
field_t *fields;
int n_fields;
todbc_buf_t *bindings_cache;
col_binding_t *bindings;
int n_bindings;
};
void fieldset_init_fields(fieldset_t *fieldset);
void fieldset_init_bindings(fieldset_t *fieldset);
void fieldset_reclaim_fields(fieldset_t *fieldset);
void fieldset_reclaim_bindings(fieldset_t *fieldset);
void fieldset_release(fieldset_t *fields);
#endif // _field_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "null_conn.h"
#include "env.h"
// null_conn
static void null_conn_free(conn_t *conn) {
OILE(conn, "");
null_conn_t *null_conn = (null_conn_t*)conn->ext.ext;
OILE(null_conn, "");
conn->ext.ext = NULL;
conn->ext.free_conn = NULL;
free(null_conn);
}
static SQLRETURN null_conn_connect(conn_t *conn) {
OILE(conn, "");
null_conn_t *null_conn = (null_conn_t*)conn->ext.ext;
OILE(null_conn && null_conn->conn==conn, "");
OILE(CONN_IS_NORM(conn), "");
return SQL_SUCCESS;
}
static void null_conn_disconnect(conn_t *conn) {
OILE(conn, "");
OILE(CONN_IS_CONNECTED(conn), "");
}
static void null_conn_free_stmt(stmt_t *stmt) {
OILE(stmt, "");
OILE(stmt->owner, "");
}
static SQLRETURN null_conn_exec_direct(stmt_t *stmt) {
OILE(stmt, "");
OILE(stmt->owner, "");
OILE(!STMT_IS_EXECUTED(stmt), "");
STMT_SET_EXECUTED(stmt);
return SQL_SUCCESS;
}
static SQLRETURN null_conn_prepare(stmt_t *stmt) {
OILE(stmt, "");
OILE(stmt->owner, "");
return SQL_SUCCESS;
}
static SQLRETURN null_conn_proc_param(stmt_t *stmt) {
OILE(stmt, "");
OILE(stmt->owner, "");
return SQL_SUCCESS;
}
static SQLRETURN null_conn_execute(stmt_t *stmt) {
OILE(stmt, "");
OILE(stmt->owner, "");
OILE(!STMT_IS_EXECUTED(stmt), "");
STMT_SET_EXECUTED(stmt);
return SQL_SUCCESS;
}
static int null_conn_init_stmt(stmt_t *stmt) {
OILE(stmt, "");
OILE(stmt->ext.ext==NULL, "");
OILE(stmt->ext.free_stmt==NULL, "");
OILE(stmt->owner==NULL, "");
stmt->ext.ext = NULL;
stmt->ext.free_stmt = null_conn_free_stmt;
stmt->ext.exec_direct = null_conn_exec_direct;
stmt->ext.prepare = null_conn_prepare;
stmt->ext.proc_param = null_conn_proc_param;
stmt->ext.execute = null_conn_execute;
return 0;
}
int conn_init_null_conn(conn_t *conn) {
OILE(conn, "");
OILE(conn->ext.ext==NULL, "");
OILE(conn->ext.free_conn==NULL, "");
null_conn_t *null_conn = (null_conn_t*)calloc(1, sizeof(*null_conn));
if (!null_conn) return -1;
null_conn->conn = conn;
conn->ext.ext = null_conn;
conn->ext.free_conn = null_conn_free;
conn->ext.connect = null_conn_connect;
conn->ext.disconnect = null_conn_disconnect;
conn->ext.init_stmt = null_conn_init_stmt;
return 0;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _null_conn_h_
#define _null_conn_h_
#include "../base.h"
#include "conn.h"
typedef struct null_conn_s null_conn_t;
struct null_conn_s {
conn_t *conn;
};
int conn_init_null_conn(conn_t *conn);
#endif // _null_conn_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "param.h"
static void paramset_clr_params(paramset_t *paramset) {
if (!paramset) return;
paramset->params = NULL;
paramset->n_params = 0;
paramset->i_row = 0;
paramset->i_col = 0;
}
static void paramset_clr_bindings(paramset_t *paramset) {
if (!paramset) return;
paramset->bindings = NULL;
paramset->n_bindings = 0;
}
void paramset_reclaim_params(paramset_t *paramset) {
if (!paramset) return;
if (!paramset->params_cache) return;
todbc_buf_reclaim(paramset->params_cache);
paramset_clr_params(paramset);
}
void paramset_reclaim_bindings(paramset_t *paramset) {
if (!paramset) return;
if (!paramset->bindings_cache) return;
todbc_buf_reclaim(paramset->bindings_cache);
paramset_clr_bindings(paramset);
}
void paramset_init_params_cache(paramset_t *paramset) {
if (!paramset) return;
if (paramset->params_cache) return;
OILE(paramset->params==NULL, "");
OILE(paramset->n_params==0, "");
paramset->params_cache = todbc_buf_create();
}
void paramset_init_bindings_cache(paramset_t *paramset) {
if (!paramset) return;
if (paramset->bindings_cache) return;
OILE(paramset->bindings==NULL, "");
OILE(paramset->n_bindings==0, "");
paramset->bindings_cache = todbc_buf_create();
}
void paramset_release(paramset_t *paramset) {
if (!paramset) return;
paramset_reclaim_params(paramset);
paramset_reclaim_bindings(paramset);
if (paramset->params_cache) {
todbc_buf_free(paramset->params_cache);
paramset->params_cache = NULL;
}
if (paramset->bindings_cache) {
todbc_buf_free(paramset->bindings_cache);
paramset->bindings_cache = NULL;
}
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _param_h_
#define _param_h_
#include "../base.h"
typedef struct paramset_s paramset_t;
typedef struct param_binding_s param_binding_t;
typedef struct param_val_s param_val_t;
// SQLDescribeParam
struct param_s {
SQLUSMALLINT ParameterNumber;
SQLSMALLINT DataType; // sql data type
SQLULEN ParameterSize;
SQLSMALLINT DecimalDigits;
SQLSMALLINT Nullable;
};
// SQLBindParameter
struct param_binding_s {
SQLUSMALLINT ParameterNumber;
SQLSMALLINT InputOutputType;
SQLSMALLINT ValueType; // sql c data type
SQLSMALLINT ParameterType; // sql data type
SQLULEN ColumnSize;
SQLSMALLINT DecimalDigits;
SQLPOINTER ParameterValuePtr;
SQLLEN BufferLength;
SQLLEN *StrLen_or_IndPtr;
};
struct paramset_s {
todbc_buf_t *params_cache;
param_t *params;
int n_params;
todbc_buf_t *bindings_cache;
param_binding_t *bindings;
int n_bindings;
int i_row;
int i_col;
};
void paramset_reclaim_params(paramset_t *paramset);
void paramset_reclaim_bindings(paramset_t *paramset);
void paramset_init_params_cache(paramset_t *paramset);
void paramset_init_bindings_cache(paramset_t *paramset);
void paramset_release(paramset_t *paramset);
#endif // _param_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rs.h"
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _rs_h_
#define _rs_h_
#include "../base.h"
struct rs_s {
int affected_rows;
};
#endif // _rs_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stmt.h"
#include "conn.h"
#include "param.h"
static int static_app_row; // SQL_ATTR_APP_ROW_DESC
static int static_app_param; // SQL_ATTR_APP_PARAM_DESC
static int static_imp_row; // SQL_ATTR_IMP_ROW_DESC
static int static_imp_param; // SQL_ATTR_IMP_PARAM_DESC
static int stmt_init_descs(stmt_t *stmt) {
OILE(stmt, "");
descs_t *descs = &stmt->descs;
descs->app_row = &static_app_row; // SQL_ATTR_APP_ROW_DESC
descs->app_param = &static_app_param; // SQL_ATTR_APP_PARAM_DESC
descs->imp_row = &static_imp_row; // SQL_ATTR_IMP_ROW_DESC
descs->imp_param = &static_imp_param; // SQL_ATTR_IMP_PARAM_DESC
return 0;
}
static int stmt_init_sql(stmt_t *stmt) {
OILE(stmt, "");
sql_t *sql = &stmt->sql;
OILE(sql->cache==NULL, "");
OILE(sql->txt.buf==NULL, "");
OILE(sql->txt.bytes==0, "");
OILE(sql->txt.total_bytes==0, "");
sql->cache = todbc_buf_create();
if (!sql->cache) return -1;
return 0;
}
static void stmt_release_sql(stmt_t *stmt) {
OILE(stmt, "");
sql_t *sql = &stmt->sql;
if (!sql->cache) return;
todbc_buf_free(sql->cache);
sql->cache = NULL;
sql->txt.buf = NULL;
sql->txt.bytes = 0;
sql->txt.total_bytes = 0;
}
static int stmt_init(stmt_t *stmt, conn_t *conn) {
OILE(stmt, "");
OILE(conn, "");
OILE(stmt->owner==NULL, "");
stmt->typeinfo = SQL_UNKNOWN_TYPE;
int r = errs_init(&stmt->errs);
if (r) return -1;
r = stmt_init_descs(stmt);
if (r) return -1;
r = stmt_init_sql(stmt);
if (r) return -1;
stmt->attr.bind_type = SQL_PARAM_BIND_BY_COLUMN;
r = conn_add_stmt(conn, stmt);
OILE(r==0, "");
OILE(stmt->owner && stmt->owner->conn==conn, "");
return 0;
}
static void stmt_release(stmt_t *stmt) {
if (!stmt) return;
OILE(stmt->owner, "");
conn_t *conn = stmt->owner->conn;
conn_del_stmt(conn, stmt);
paramset_release(&stmt->paramset);
fieldset_release(&stmt->fieldset);
stmt_release_sql(stmt);
errs_release(&stmt->errs);
}
static SQLRETURN do_process_param(stmt_t *stmt) {
OILE(stmt->ext.proc_param, "");
return stmt->ext.proc_param(stmt);
}
static SQLRETURN do_process_param_row(stmt_t *stmt) {
paramset_t *paramset = &stmt->paramset;
int n_params = paramset->n_params;
SQLRETURN r = SQL_SUCCESS;
paramset->i_col = 0;
for (; paramset->i_col<n_params; ++paramset->i_col) {
r = do_process_param(stmt);
if (r!=SQL_SUCCESS) return r;
}
return SQL_SUCCESS;
}
stmt_t* stmt_new(conn_t *conn) {
stmt_t *stmt = (stmt_t*)calloc(1, sizeof(*stmt));
if (!stmt) return NULL;
if (stmt_init(stmt, conn)) {
free(stmt);
return NULL;
}
return stmt;
}
void stmt_free(stmt_t *stmt) {
if (!stmt) return;
// clean ext stuff
if (stmt->ext.free_stmt) {
stmt->ext.free_stmt(stmt);
stmt->ext.free_stmt = NULL;
}
// clean stmt stuff
stmt_release(stmt);
free(stmt);
}
conn_t* stmt_get_conn(stmt_t *stmt) {
if (!stmt) return NULL;
if (!stmt->owner) return NULL;
return stmt->owner->conn;
}
void stmt_reclaim_params(stmt_t *stmt) {
if (!stmt) return;
paramset_reclaim_params(&stmt->paramset);
}
void stmt_reclaim_param_bindings(stmt_t *stmt) {
if (!stmt) return;
paramset_reclaim_bindings(&stmt->paramset);
}
void stmt_reclaim_field_bindings(stmt_t *stmt) {
if (!stmt) return;
fieldset_reclaim_bindings(&stmt->fieldset);
}
void stmt_close_rs(stmt_t *stmt) {
if (!stmt) return;
OILE(stmt->ext.close_rs, "");
stmt->ext.close_rs(stmt);
stmt->typeinfo = SQL_UNKNOWN_TYPE;
stmt->eof = 0;
fieldset_reclaim_fields(&stmt->fieldset);
// for the performance
// we don't reclaim field-binds here
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindcol-function?view=sql-server-ver15
STMT_SET_NORM(stmt);
}
void stmt_reset_params(stmt_t *stmt) {
if (!stmt) return;
stmt->attr.paramset_size = 0;
stmt_reclaim_param_bindings(stmt);
}
static SQLRETURN stmt_set_sql(stmt_t *stmt, todbc_string_t *txt) {
OILE(stmt, "");
OILE(txt, "");
errs_t *errs = &stmt->errs;
sql_t *sql = &stmt->sql;
OILE(txt!=&sql->txt, "");
todbc_buf_t *cache = sql->cache;
OILE(sql->cache, "");
conn_t *conn = stmt_get_conn(stmt);
OILE(conn, "");
const char *enc = conn->enc_db;
todbc_buf_reclaim(cache);
sql->txt = todbc_string_conv_to(txt, enc, cache);
if (!sql->txt.buf) {
SET_OOM(errs, "");
return SQL_ERROR;
}
return SQL_SUCCESS;
}
static SQLRETURN do_stmt_prepare(stmt_t *stmt) {
OILE(stmt->ext.prepare, "");
return stmt->ext.prepare(stmt);
}
static void do_stmt_clear_param_vals(stmt_t *stmt) {
OILE(stmt->ext.clear_param_vals, "");
stmt->ext.clear_param_vals(stmt);
}
static void do_stmt_clear_params(stmt_t *stmt) {
stmt->prepared = 0;
paramset_t *paramset = &stmt->paramset;
paramset_reclaim_params(paramset);
do_stmt_clear_param_vals(stmt);
}
SQLRETURN stmt_exec_direct(stmt_t *stmt, todbc_string_t *txt) {
OILE(stmt, "");
OILE(txt, "");
SQLRETURN r = stmt_set_sql(stmt, txt);
if (r!=SQL_SUCCESS) return r;
do_stmt_clear_params(stmt);
if (stmt->ext.exec_direct) {
r = stmt->ext.exec_direct(stmt);
} else {
r = do_stmt_prepare(stmt);
if (r!=SQL_SUCCESS) return r;
stmt->prepared = 1;
OILE(0, "");
}
return r;
}
SQLRETURN stmt_prepare(stmt_t *stmt, todbc_string_t *txt) {
OILE(stmt, "");
OILE(txt, "");
SQLRETURN r = stmt_set_sql(stmt, txt);
if (r!=SQL_SUCCESS) return r;
do_stmt_clear_params(stmt);
r = do_stmt_prepare(stmt);
if (r!=SQL_SUCCESS) return r;
stmt->prepared = 1;
return SQL_SUCCESS;
}
SQLRETURN stmt_bind_param(stmt_t *stmt, param_binding_t *arg) {
OILE(stmt, "");
OILE(arg, "");
OILE(arg->ParameterNumber>0, "");
int idx = arg->ParameterNumber - 1;
errs_t *errs = &stmt->errs;
paramset_t *paramset = &stmt->paramset;
paramset_init_bindings_cache(paramset);
if (!paramset->bindings_cache) {
SET_OOM(errs, "failed to alloc cache for param binds");
return SQL_ERROR;
}
todbc_buf_t *cache = paramset->bindings_cache;
OILE(cache, "");
param_binding_t *bindings = paramset->bindings;
if (idx>=paramset->n_bindings) {
size_t num = (size_t)(idx + 1);
// align
const size_t block = 10;
num = (num + block-1) / block * block;
bindings = (param_binding_t*)todbc_buf_realloc(cache, bindings, num * sizeof(*bindings));
if (!bindings) {
SET_OOM(errs, "failed to realloc buf for param binds");
return SQL_ERROR;
}
paramset->bindings = bindings;
paramset->n_bindings = idx + 1;
}
OILE(paramset->bindings, "");
OILE(idx<paramset->n_bindings, "");
param_binding_t *binding = bindings + idx;
*binding = *arg;
if (paramset->n_bindings>paramset->n_params) return SQL_SUCCESS;
OILE(stmt->ext.set_param_conv, "");
return stmt->ext.set_param_conv(stmt, idx);
}
SQLRETURN stmt_execute(stmt_t *stmt) {
OILE(stmt, "");
OILE(!STMT_IS_EXECUTING(stmt), "");
SQLRETURN r = SQL_SUCCESS;
if (stmt->prepared==0) {
do_stmt_clear_params(stmt);
r = do_stmt_prepare(stmt);
if (r!=SQL_SUCCESS) return r;
stmt->prepared = 1;
}
OILE(stmt->prepared==1, "");
errs_t *errs = &stmt->errs;
paramset_t *paramset = &stmt->paramset;
int n_params = paramset->n_params;
int n_bindings = paramset->n_bindings;
if (n_params>n_bindings) {
SET_GENERAL(errs, "parameters need to be bound first");
return SQL_ERROR;
}
if (n_params>0) {
int paramset_size = (int)stmt->attr.paramset_size;
if (paramset_size==0) paramset_size = 1;
stmt->attr.paramset_size = (SQLULEN)paramset_size;
paramset->i_row = 0;
for (; paramset->i_row<paramset_size; ++paramset->i_row) {
r = do_process_param_row(stmt);
if (r) return r;
OILE(stmt->ext.param_row_processed, "");
r = stmt->ext.param_row_processed(stmt);
if (r) return r;
}
}
OILE(stmt->ext.execute, "");
return stmt->ext.execute(stmt);
}
SQLRETURN stmt_fetch(stmt_t *stmt) {
OILE(stmt, "");
OILE(STMT_IS_EXECUTED(stmt), "");
if (stmt->eof) return SQL_NO_DATA;
OILE(stmt->ext.fetch, "");
SQLRETURN r = stmt->ext.fetch(stmt);
if (r!=SQL_SUCCESS) return r;
fieldset_t *fieldset = &stmt->fieldset;
if (fieldset->n_bindings==0) return SQL_SUCCESS;
OILE(fieldset->n_bindings>0, "");
for (size_t i=0; i<fieldset->n_bindings; ++i) {
OILE(fieldset->bindings, "");
col_binding_t *binding = fieldset->bindings + i;
if (binding->ColumnNumber!=i+1) {
OILE(binding->ColumnNumber==0, "");
continue;
}
OILE(stmt->ext.get_data, "");
r = stmt->ext.get_data(stmt, binding);
if (r!=SQL_SUCCESS) return r;
}
return SQL_SUCCESS;
}
SQLRETURN stmt_get_data(stmt_t *stmt, col_binding_t *binding) {
OILE(stmt, "");
OILE(STMT_IS_EXECUTED(stmt), "");
OILE(stmt->eof==0, "");
OILE(stmt->ext.get_data, "");
return stmt->ext.get_data(stmt, binding);
}
SQLRETURN stmt_bind_col(stmt_t *stmt, col_binding_t *binding) {
OILE(stmt, "");
// shall we check execute state?
errs_t *errs = &stmt->errs;
fieldset_t *fieldset = &stmt->fieldset;
todbc_buf_t *cache = fieldset->bindings_cache;
if (cache==NULL) {
fieldset_init_bindings(fieldset);
cache = fieldset->bindings_cache;
if (!cache) {
SET_OOM(errs, "");
return SQL_ERROR;
}
}
OILE(cache, "");
col_binding_t *bindings = fieldset->bindings;
OILE(binding->ColumnNumber>0, "");
if (binding->ColumnNumber>=fieldset->n_bindings) {
size_t num = (size_t)binding->ColumnNumber;
const size_t block = 10;
size_t align = (num+block-1)/block*block;
size_t total = align * sizeof(*bindings);
bindings = (col_binding_t*)todbc_buf_realloc(cache, bindings, total);
if (!bindings) {
SET_OOM(errs, "");
return SQL_ERROR;
}
for (size_t i = (size_t)fieldset->n_bindings; i<num; ++i) {
bindings[i].ColumnNumber = 0; // not set yet
}
fieldset->bindings = bindings;
fieldset->n_bindings = (int)num;
}
OILE(bindings, "");
OILE(binding->ColumnNumber<=fieldset->n_bindings, "");
bindings[binding->ColumnNumber-1] = *binding;
return SQL_SUCCESS;
}
// public
errs_t* stmt_get_errs(stmt_t *stmt) {
OILE(stmt, "");
return &stmt->errs;
}
void stmt_clr_errs(stmt_t *stmt) {
if (!stmt) return;
errs_reclaim(&stmt->errs);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _stmt_h_
#define _stmt_h_
#include "../base.h"
#include "err.h"
#include "field.h"
#include "param.h"
#include "rs.h"
typedef struct descs_s descs_t;
typedef struct stmts_s stmts_t;
typedef struct stmt_ext_s stmt_ext_t;
typedef struct stmt_attr_s stmt_attr_t;
typedef struct sql_s sql_t;
struct descs_s {
void *app_row; // SQL_ATTR_APP_ROW_DESC
void *app_param; // SQL_ATTR_APP_PARAM_DESC
void *imp_row; // SQL_ATTR_IMP_ROW_DESC
void *imp_param; // SQL_ATTR_IMP_PARAM_DESC
};
struct stmt_ext_s {
void *ext;
void (*free_stmt)(stmt_t *stmt);
void (*clear_params)(stmt_t *stmt);
void (*clear_param_vals)(stmt_t *stmt);
SQLRETURN (*get_param)(stmt_t *stmt, param_t *arg);
SQLRETURN (*set_param_conv)(stmt_t *stmt, int idx);
SQLRETURN (*exec_direct)(stmt_t *stmt);
SQLRETURN (*prepare)(stmt_t *stmt);
SQLRETURN (*proc_param)(stmt_t *stmt);
SQLRETURN (*param_row_processed)(stmt_t *stmt);
SQLRETURN (*execute)(stmt_t *stmt);
SQLRETURN (*get_affected_rows)(stmt_t *stmt, SQLLEN *RowCount);
SQLRETURN (*get_fields_count)(stmt_t *stmt, SQLSMALLINT *ColumnCount);
SQLRETURN (*get_field)(stmt_t *stmt, field_arg_t *arg);
SQLRETURN (*fetch)(stmt_t *stmt);
SQLRETURN (*get_data)(stmt_t *stmt, col_binding_t *col);
void (*close_rs)(stmt_t *stmt);
};
struct stmt_attr_s {
// SQL_ATTR_PARAM_BIND_TYPE: SQL_PARAM_BIND_BY_COLUMN or row size
SQLULEN bind_type; // default: SQL_PARAM_BIND_BY_COLUMN
// SQL_ATTR_PARAM_BIND_OFFSET_PTR
SQLULEN *bind_offset_ptr; // default: NULL
// SQL_ATTR_PARAMSET_SIZE
SQLULEN paramset_size; // default: 0
};
struct sql_s {
todbc_buf_t *cache;
todbc_string_t txt;
};
struct stmt_s {
stmts_t *owner;
stmt_t *next;
stmt_t *prev;
SQLSMALLINT typeinfo;
descs_t descs;
sql_t sql;
stmt_attr_t attr;
paramset_t paramset;
fieldset_t fieldset;
int affected_rows;
rs_t rs;
errs_t errs;
stmt_ext_t ext;
unsigned int prepared:1;
unsigned int execute:2;
unsigned int eof:1;
};
struct stmts_s {
conn_t *conn;
int count;
stmt_t *head;
stmt_t *tail;
};
#define STMT_TYPEINFO(stmt) (stmt->typeinfo!=SQL_UNKNOWN_TYPE)
#define STMT_SET_EXECUTING(stmt) (stmt->execute=0x01)
#define STMT_SET_EXECUTED(stmt) (stmt->execute=0x02)
#define STMT_SET_NORM(stmt) (stmt->execute=0x00)
#define STMT_IS_EXECUTING(stmt) (stmt->execute==0x01)
#define STMT_IS_EXECUTED(stmt) (stmt->execute==0x02)
#define STMT_IS_NORM(stmt) (stmt->execute==0x00)
stmt_t* stmt_new(conn_t *conn);
void stmt_free(stmt_t *stmt);
conn_t* stmt_get_conn(stmt_t *stmt);
void stmt_reclaim_params(stmt_t *stmt);
void stmt_reclaim_param_binds(stmt_t *stmt);
void stmt_reclaim_field_binds(stmt_t *stmt);
void stmt_close_rs(stmt_t *stmt);
void stmt_reset_params(stmt_t *stmt);
SQLRETURN stmt_exec_direct(stmt_t *stmt, todbc_string_t *txt);
SQLRETURN stmt_prepare(stmt_t *stmt, todbc_string_t *txt);
SQLRETURN stmt_bind_param(stmt_t *stmt, param_binding_t *arg);
SQLRETURN stmt_execute(stmt_t *stmt);
SQLRETURN stmt_fetch(stmt_t *stmt);
SQLRETURN stmt_get_data(stmt_t *stmt, col_binding_t *binding);
SQLRETURN stmt_bind_col(stmt_t *stmt, col_binding_t *binding);
#endif // _stmt_h_
此差异已折叠。
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _tsdb_impl_h_
#define _tsdb_impl_h_
#include "../base.h"
#include "conn.h"
#define DEFAULT_SERVER "localhost:6030"
int conn_init_tsdb_conn(conn_t *conn);
#endif // _tsdb_impl_h_
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _col_h_
#define _col_h_
#include "base.h"
struct col_s {
};
#endif // _col_h_
@echo off
REM install driver
odbcconf /A {INSTALLDRIVER "TAOS|Driver=C:\TDengine\driver\todbc.dll|ConnectFunctions=YYN|DriverODBCVer=03.00|FileUsage=0|SQLLevel=0"}
REM config user dsn
odbcconf /A {CONFIGDSN "TAOS" "DSN=TAOS_DSN"}
......@@ -2,6 +2,13 @@
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"
此差异已折叠。
......@@ -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
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "todbc_buf.h"
#include "todbc_list.h"
#include "todbc_log.h"
#include "todbc_tls.h"
#include <stdlib.h>
#include <string.h>
#define BLOCK_SIZE (1024*16)
// alignment for holding whatever struct would be
#define ALIGN(size) ((size==0?1:size) + sizeof(size_t) - 1) / sizeof(size_t) * sizeof(size_t);
typedef struct buf_rec_s buf_rec_t;
typedef struct buf_block_s buf_block_t;
struct buf_rec_s {
size_t size; // payload size couting from ptr[0]
buf_block_t *owner;
char ptr[0]; // place-holder
};
#define PTR_OFFSET() ((size_t)(((buf_rec_t*)0)->ptr))
struct buf_block_s {
todbc_buf_t *owner;
size_t cap; // total payload space available for allocation
size_t ptr; // beginning of free space
char base[0]; // place-holder
};
#define BASE_OFFSET() ((size_t)(((buf_block_t*)0)->base))
struct todbc_buf_s {
char buf[BLOCK_SIZE];
buf_block_t *block; // mapped to &buf[0]
todbc_list_t *list; // dynamic allocated blocks list
};
#define BASE_STATIC(block) (block->owner->buf + BASE_OFFSET()==block->base)
typedef struct arg_s arg_t;
struct arg_s {
todbc_buf_t *buf;
// input
char *arg_ptr;
size_t arg_size;
buf_rec_t *arg_rec;
size_t arg_align;
// output
buf_rec_t *rec;
};
static buf_rec_t* ptr_rec(todbc_buf_t *buf, void *ptr) {
char *p = (char*)ptr;
char *begin = p-PTR_OFFSET();
buf_rec_t *rec = (buf_rec_t*)begin;
OILE(rec, "");
OILE(rec->size, "");
OILE((rec->size)%sizeof(void*)==0, "");
buf_block_t *block = rec->owner;
OILE(block, "");
OILE(block->owner==buf, "");
OILE(block->base, "");
char *end = begin + sizeof(buf_rec_t) + rec->size;
OILE(begin>=block->base, "");
OILE(end<=block->base+block->ptr, "");
return rec;
}
static buf_block_t* buf_block_create(size_t align);
static buf_rec_t* buf_block_realloc(buf_block_t *block, arg_t *arg);
static void buf_block_free(buf_block_t *block);
static void buf_block_reclaim(buf_block_t *block);
#define ALLOC_LIST() do { \
if (buf->list) break; \
todbc_list_conf_t conf = {0}; \
conf.arg = buf; \
conf.val_free = do_free_buf_block; \
buf->list = todbc_list_create(conf); \
} while (0)
todbc_buf_t* todbc_buf_create(void) {
todbc_buf_t *buf = (todbc_buf_t*)calloc(1, sizeof(*buf));
if (!buf) return NULL;
buf_block_t *block = (buf_block_t*)(buf->buf);
block->owner = buf;
block->cap = sizeof(buf->buf) - sizeof(buf_block_t);
block->ptr = 0;
buf->block = block;
OILE(BASE_STATIC(buf->block), "");
return buf;
}
void todbc_buf_free(todbc_buf_t *buf) {
if (!buf) return;
todbc_list_free(buf->list);
buf->list = NULL;
free(buf);
}
void* todbc_buf_alloc(todbc_buf_t *buf, size_t size) {
return todbc_buf_realloc(buf, NULL, size);
}
void* todbc_buf_calloc(todbc_buf_t *buf, size_t count, size_t size) {
size_t align = ALIGN(size);
size_t total = count * align;
if (total > INT64_MAX) return NULL;
void *ptr = todbc_buf_realloc(buf, NULL, total);
if (!ptr) return NULL;
memset(ptr, 0, total);
return ptr;
}
static void do_free_buf_block(todbc_list_t *list, void *val, void *arg);
static int alloc_space_by(todbc_list_t *list, void *val, void *arg);
void* todbc_buf_realloc(todbc_buf_t *buf, void *ptr, size_t size) {
OILE(buf, "");
OILE(sizeof(buf_block_t)%sizeof(void*)==0, "");
OILE(sizeof(buf_rec_t)%sizeof(void*)==0, "");
size_t align = ALIGN(size);
size_t req_size = align + sizeof(buf_rec_t);
buf_rec_t *rec = NULL;
buf_block_t *block = NULL;
if (ptr) {
rec = ptr_rec(buf, ptr);
if (align<=rec->size) return ptr;
block = rec->owner;
char *tail_rec = rec->ptr + rec->size;
char *tail_block = block->base + block->ptr;
if (tail_rec==tail_block) {
char *end_rec = rec->ptr + align;
char *end_block = block->base + block->cap;
if (end_rec<=end_block) {
rec->size = align;
block->ptr = (size_t)(end_rec - block->base);
return ptr;
}
} else {
size_t remain = block->cap - block->ptr;
if (req_size<=remain) {
char *new_ptr = block->base + block->ptr;
block->ptr += req_size;
buf_rec_t *new_rec = (buf_rec_t*)new_ptr;
new_rec->size = align;
new_rec->owner = block;
memcpy(new_rec->ptr, ptr, rec->size);
return new_rec->ptr;
}
}
}
arg_t arg = {0};
arg.buf = buf;
arg.arg_ptr = ptr;
arg.arg_size = size;
arg.arg_rec = rec;
arg.arg_align = align;
if (block!=buf->block) {
buf_rec_t *new_rec = buf_block_realloc(buf->block, &arg);
if (new_rec) return new_rec->ptr;
}
ALLOC_LIST();
if (!buf->list) return NULL;
int r = todbc_list_traverse(buf->list, alloc_space_by, &arg);
if (r) {
OILE(arg.rec, "");
OILE(arg.rec->ptr, "");
return arg.rec->ptr;
}
block = buf_block_create(arg.arg_align);
if (!block) return NULL;
OILE(block->owner==NULL, "");
r = todbc_list_pushfront(buf->list, block);
OILE(r==0, "");
block->owner = buf;
buf_rec_t *p = buf_block_realloc(block, &arg);
OILE(p, "");
return p->ptr;
}
static int do_reclaim(todbc_list_t *list, void *val, void *arg);
void todbc_buf_reclaim(todbc_buf_t *buf) {
if (!buf) return;
buf_block_reclaim(buf->block);
if (!buf->list) return;
todbc_list_traverse(buf->list, do_reclaim, buf);
}
static int do_reclaim(todbc_list_t *list, void *val, void *arg) {
todbc_buf_t *buf = (todbc_buf_t*)arg;
buf_block_t *block = (buf_block_t*)val;
OILE(list, "");
OILE(block, "");
OILE(block->owner==buf, "");
buf_block_reclaim(block);
return 0;
}
static void buf_block_reclaim(buf_block_t *block) {
block->ptr = 0;
}
static void buf_block_free(buf_block_t *block) {
if (!block) return;
buf_block_reclaim(block);
if (BASE_STATIC(block)) return;
block->owner = NULL;
block->cap = 0;
block->ptr = 0;
free(block);
}
static void do_free_buf_block(todbc_list_t *list, void *val, void *arg) {
todbc_buf_t *buf = (todbc_buf_t*)arg;
OILE(buf && buf->list==list, "");
buf_block_t *block = (buf_block_t*)val;
OILE(block, "");
OILE(buf==block->owner, "");
buf_block_free(block);
}
static int alloc_space_by(todbc_list_t *list, void *val, void *arg) {
buf_block_t *block = (buf_block_t*)val;
arg_t *targ = (arg_t*)arg;
buf_rec_t *rec = targ->arg_rec;
if (rec && rec->owner == block) return 0;
buf_rec_t *new_rec = buf_block_realloc(block, targ);
if (!new_rec) return 0;
return 1;
}
static buf_block_t* buf_block_create(size_t size) {
size_t align = ALIGN(size);
size_t req_size = sizeof(buf_block_t) + sizeof(buf_rec_t) + align;
req_size = (req_size + BLOCK_SIZE - 1) / BLOCK_SIZE * BLOCK_SIZE;
buf_block_t *block = (buf_block_t*)malloc(req_size);
if (!block) return NULL;
block->owner = NULL;
block->cap = req_size - sizeof(buf_block_t);
block->ptr = 0;
return block;
}
static buf_rec_t* buf_block_realloc(buf_block_t *block, arg_t *arg) {
OILE(block, "");
OILE(arg, "");
OILE(block->base, "");
OILE(block->cap >= block->ptr, "");
char *ptr = arg->arg_ptr;
buf_rec_t *rec = arg->arg_rec;
OILE(rec==NULL || rec->owner!=block, "");
size_t align = arg->arg_align;
size_t req_size = sizeof(buf_rec_t) + align;
OILE(req_size>0, "");
size_t remain = block->cap - block->ptr;
if (req_size > remain) {
return NULL;
}
char *p = block->base + block->ptr;
buf_rec_t *new_rec = (buf_rec_t*)p;
new_rec->size = align;
new_rec->owner = block;
block->ptr += req_size;
if (ptr) {
memcpy(new_rec->ptr, ptr, arg->arg_rec->size);
}
arg->rec = new_rec;
return new_rec;
}
// test
void todbc_buf_test_random(size_t iterates, size_t size, int raw) {
size_t n_reallocs = 0;
todbc_buf_t *cache = NULL;
OD("use %s", raw ? "realloc" : "todbc_buf_t");
if (!raw) {
cache = todbc_buf_create();
OILE(cache, "");
}
srand((unsigned)time(0));
char *buf = NULL;
size_t blen = 0;
while (iterates-- > 0) {
char *p = NULL;
size_t i = 0;
size_t len = (size_t)rand()%size;
if (raw) {
p = realloc(buf, len);
} else {
p = todbc_buf_realloc(cache, buf, len);
}
OILE(p, "");
for (i=blen; i<len; ++i) {
p[i] = '\0';
}
buf = p;
blen = len;
}
if (!raw) {
todbc_buf_free(cache);
}
OD("n_reallocs: %zd", n_reallocs);
}
void todbc_buf_test(size_t iterates, size_t size, int raw) {
size_t n_reallocs = 0;
todbc_buf_t *cache = NULL;
OD("use %s", raw ? "realloc" : "todbc_buf_t");
if (!raw) {
cache = todbc_buf_create();
OILE(cache, "");
}
while (iterates-- > 0) {
size_t i = 0;
char *buf = NULL;
while (i<size) {
char *p = NULL;
if (raw) {
p = realloc(buf, i);
} else {
p = todbc_buf_realloc(cache, buf, i);
}
OILE(p, "");
if (p!=buf) {
// OD("buf/p:%zd[%p/%p]", i, buf, p);
buf = p;
++n_reallocs;
}
if (i) p[i-1] = '\0';
++i;
}
// OD("buf :%zd[%p]", i, buf);
}
if (!raw) {
todbc_buf_free(cache);
}
OD("n_reallocs: %zd", n_reallocs);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_buf_h_
#define _todbc_buf_h_
#include <stdint.h>
#include <stddef.h>
// non-thread-safe
typedef struct todbc_buf_s todbc_buf_t;
todbc_buf_t* todbc_buf_create(void);
void todbc_buf_free(todbc_buf_t *buf);
void* todbc_buf_alloc(todbc_buf_t *buf, size_t size);
void* todbc_buf_calloc(todbc_buf_t *buf, size_t count, size_t size);
void* todbc_buf_realloc(todbc_buf_t *buf, void *ptr, size_t size);
char* todbc_buf_strdup(todbc_buf_t *buf, const char *str);
void todbc_buf_reclaim(todbc_buf_t *buf);
void todbc_buf_test_random(size_t iterates, size_t size, int raw);
void todbc_buf_test(size_t iterates, size_t size, int raw);
#endif // _todbc_buf_h_
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -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_
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册