未验证 提交 b4e8f00a 编写于 作者: H huili 提交者: GitHub

Merge pull request #3981 from freemine/odbc

odbc connector, keep going
......@@ -9,7 +9,7 @@ ELSEIF (TD_WINDOWS)
ELSE ()
SET(CMAKE_INSTALL_PREFIX C:/TDengine)
ENDIF ()
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/go DESTINATION connector)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/nodejs DESTINATION connector)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/python DESTINATION connector)
......@@ -20,12 +20,12 @@ ELSEIF (TD_WINDOWS)
INSTALL(FILES ${TD_COMMUNITY_DIR}/src/inc/taoserror.h DESTINATION include)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.lib DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.exp DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.dll DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.dll DESTINATION driver)
IF (TD_POWER)
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/power.exe DESTINATION .)
ELSE ()
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/taos.exe DESTINATION .)
ELSE ()
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/taos.exe DESTINATION .)
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/taosdemo.exe DESTINATION .)
ENDIF ()
......
......@@ -268,7 +268,6 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) {
if (1) {
// allow user bind param data with different type
short size = 0;
union {
int8_t v1;
int16_t v2;
......@@ -600,7 +599,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) {
if ((*bind->length) > (uintptr_t)param->bytes) {
return TSDB_CODE_TSC_INVALID_VALUE;
}
size = (short)*bind->length;
short size = (short)*bind->length;
STR_WITH_SIZE_TO_VARSTR(data + param->offset, bind->buffer, size);
return TSDB_CODE_SUCCESS;
} break;
......
......@@ -3,7 +3,6 @@ PROJECT(TDengine)
IF (TD_LINUX_64)
find_program(HAVE_ODBCINST NAMES odbcinst)
IF (HAVE_ODBCINST)
include(CheckSymbolExists)
# shall we revert CMAKE_REQUIRED_LIBRARIES and how?
......@@ -14,20 +13,43 @@ IF (TD_LINUX_64)
message(WARNING "unixodbc-dev is not installed yet, you may install it under ubuntu by typing: sudo apt install unixodbc-dev")
else ()
message(STATUS "unixodbc/unixodbc-dev are installed, and odbc connector will be built")
AUX_SOURCE_DIRECTORY(src SRC)
# generate dynamic library (*.so)
ADD_LIBRARY(todbc SHARED ${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)
install(CODE "execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/install.sh ${CMAKE_BINARY_DIR})")
ADD_SUBDIRECTORY(tests)
find_package(FLEX)
if(NOT FLEX_FOUND)
message(FATAL_ERROR "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(tests)
endif ()
endif()
endif()
ELSE ()
message(WARNING "unixodbc is not installed yet, you may install it under ubuntu by typing: sudo apt install unixodbc")
ENDIF ()
ENDIF ()
IF (TD_WINDOWS_64)
find_package(ODBC)
if (NOT ODBC_FOUND)
message(FATAL_ERROR "you need to install ODBC first")
else ()
message(STATUS "ODBC_INCLUDE_DIRS: ${ODBC_INCLUDE_DIRS}")
message(STATUS "ODBC_LIBRARIES: ${ODBC_LIBRARIES}")
message(STATUS "ODBC_CONFIG: ${ODBC_CONFIG}")
endif ()
find_package(FLEX)
if(NOT FLEX_FOUND)
message(WARNING "you need to install flex first\n"
"you may go to: https://github.com/lexxmark/winflexbison\n"
"or download from: https://github.com/lexxmark/winflexbison/releases")
else ()
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(tools)
ADD_SUBDIRECTORY(tests)
endif()
ENDIF ()
# ODBC Driver #
- **very initial implementation of ODBC driver for TAOS
- **currently partially supported ODBC functions are: `
SQLAllocEnv
SQLFreeEnv
SQLAllocConnect
SQLFreeConnect
SQLConnect
SQLDisconnect
SQLAllocStmt
SQLAllocHandle
SQLFreeStmt
SQLExecDirect
SQLExecDirectW
SQLNumResultCols
SQLRowCount
SQLColAttribute
SQLGetData
SQLFetch
SQLPrepare
SQLExecute
SQLGetDiagField
SQLGetDiagRec
SQLBindParameter
SQLDriverConnect
SQLSetConnectAttr
SQLDescribeCol
SQLNumParams
SQLSetStmtAttr
ConfigDSN
`
- **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.
- **enable ODBC-aware software to communicate with TAOS.
- **enable any language with ODBC-bindings/ODBC-plugings to communicate with TAOS
- **still going on...
# Building and Testing
**Note**: all `work` is done in TDengine's project directory
# Building under Linux, use Ubuntu as example
```
sudo apt install unixodbc unixodbc-dev flex
rm -rf debug && cmake -B debug && cmake --build debug && cmake --install debug && echo yes
```
# 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`
- `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>`
# 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.
**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
```
## create data in linux
```
./debug/build/bin/tcodbc --dsn TAOS_DSN --uid <uid> --pwd <pwd> --sts ./src/connector/odbc/tests/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
```
## 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
```
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(TDengine)
IF (TD_LINUX_64)
FLEX_TARGET(todbcFlexScanner
todbc_scanner.l
${CMAKE_CURRENT_BINARY_DIR}/todbc_scanner.c
)
set(todbc_flex_scanner_src
${FLEX_todbcFlexScanner_OUTPUTS}
)
AUX_SOURCE_DIRECTORY(. SRC)
# generate dynamic library (*.so)
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_include_directories(todbc PUBLIC .)
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
${CMAKE_CURRENT_BINARY_DIR}/todbc_scanner.c
)
set(todbc_flex_scanner_src
${FLEX_todbcFlexScanner_OUTPUTS}
)
AUX_SOURCE_DIRECTORY(. SRC)
# generate dynamic library (*.dll)
ADD_LIBRARY(todbc SHARED
${SRC}
${todbc_flex_scanner_src}
${CMAKE_CURRENT_BINARY_DIR}/todbc.rc
todbc.def)
TARGET_LINK_LIBRARIES(todbc taos_static odbccp32 legacy_stdio_definitions)
target_include_directories(todbc PUBLIC .)
target_compile_definitions(todbc PRIVATE "todbc_EXPORT")
CONFIGURE_FILE("todbc.rc.in"
"${CMAKE_CURRENT_BINARY_DIR}/todbc.rc")
SET_TARGET_PROPERTIES(todbc PROPERTIES LINK_FLAGS
/DEF:todbc.def)
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /GL")
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL")
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.lib DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.exp DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/todbc.dll DESTINATION driver)
ENDIF ()
......@@ -9,16 +9,18 @@ rm -f "${BLD_DIR}/template.dsn"
cat > "${BLD_DIR}/template.ini" <<EOF
[TAOS]
Description = taos odbc driver
Driver = ${BLD_DIR}/build/lib/libtodbc.so
Description=taos odbc driver
Driver=${BLD_DIR}/build/lib/libtodbc.so
EOF
cat > "${BLD_DIR}/template.dsn" <<EOF
[TAOS_DSN]
Description=Connection to TAOS
Driver=TAOS
Server=localhost:6030
EOF
# better remove first ?
sudo odbcinst -i -d -f "${BLD_DIR}/template.ini" &&
odbcinst -i -s -f "${BLD_DIR}/template.dsn" &&
echo "odbc install done"
......
......@@ -18,17 +18,33 @@
#define _DEFAULT_SOURCE
#define _GNU_SOURCE
#include "todbc_log.h"
#include "todbc_flex.h"
#include "taos.h"
#include "os.h"
#include "tglobal.h"
#include "taoserror.h"
#include "todbc_util.h"
#include "todbc_conv.h"
#include "os.h"
#include <sql.h>
#include <odbcinst.h>
#include <sqlext.h>
#include <time.h>
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#define UTF8_ENC "UTF-8"
#define UTF16_ENC "UCS-2LE"
#define UNICODE_ENC "UCS-4LE"
#define GB18030_ENC "GB18030"
#define GET_REF(obj) atomic_load_64(&obj->refcount)
#define INC_REF(obj) atomic_add_fetch_64(&obj->refcount, 1)
......@@ -42,15 +58,22 @@ do {
obj->err.err_no = eno; \
const char* estr = tstrerror(eno); \
if (!estr) estr = "Unknown error"; \
int n = snprintf(NULL, 0, "%s: @[%d][TSDB:%x]" err_fmt "", estr, __LINE__, eno, ##__VA_ARGS__); \
int n = snprintf(NULL, 0, "[TSDB:%x]%s: @%s[%d]" err_fmt "", \
eno, estr, \
basename((char*)__FILE__), __LINE__, \
##__VA_ARGS__); \
if (n<0) break; \
char *err_str = (char*)realloc(obj->err.err_str, n+1); \
char *err_str = (char*)realloc(obj->err.err_str, (size_t)n+1); \
if (!err_str) break; \
obj->err.err_str = err_str; \
snprintf(obj->err.err_str, n+1, "%s: @[%d][TSDB:%x]" err_fmt "", estr, __LINE__, eno, ##__VA_ARGS__); \
snprintf(obj->err.err_str, (size_t)n+1, "[TSDB:%x]%s: @%s[%d]" err_fmt "", \
eno, estr, \
basename((char*)__FILE__), __LINE__, \
##__VA_ARGS__); \
snprintf((char*)obj->err.sql_state, sizeof(obj->err.sql_state), "%s", sqlstate); \
} while (0)
#define CLR_ERROR(obj) \
do { \
obj->err.err_no = TSDB_CODE_SUCCESS; \
......@@ -58,14 +81,13 @@ do {
obj->err.sql_state[0] = '\0'; \
} while (0)
#define FILL_ERROR(obj) \
do { \
size_t n = sizeof(obj->err.sql_state); \
if (Sqlstate) strncpy((char*)Sqlstate, (char*)obj->err.sql_state, n); \
if (NativeError) *NativeError = obj->err.err_no; \
snprintf((char*)MessageText, BufferLength, "%s", obj->err.err_str); \
if (TextLength && obj->err.err_str) *TextLength = strlen(obj->err.err_str); \
if (TextLength && obj->err.err_str) *TextLength = utf8_chars(obj->err.err_str); \
#define FILL_ERROR(obj) \
do { \
size_t n = sizeof(obj->err.sql_state); \
if (Sqlstate) strncpy((char*)Sqlstate, (char*)obj->err.sql_state, n); \
if (NativeError) *NativeError = obj->err.err_no; \
snprintf((char*)MessageText, (size_t)BufferLength, "%s", obj->err.err_str); \
if (TextLength && obj->err.err_str) *TextLength = (SQLSMALLINT)utf8_chars(obj->err.err_str); \
} while (0)
#define FREE_ERROR(obj) \
......@@ -88,7 +110,7 @@ do {
SET_ERROR(obj, sqlstate, TSDB_CODE_QRY_INVALID_QHANDLE, err_fmt, ##__VA_ARGS__); \
} while (0);
#define SDUP(s,n) (s ? (s[n] ? (const char*)strndup((const char*)s,n) : (const char*)s) : strdup(""))
#define SDUP(s,n) (s ? (s[(size_t)n] ? (const char*)strndup((const char*)s,(size_t)n) : (const char*)s) : strdup(""))
#define SFRE(x,s,n) \
do { \
if (x==(const char*)s) break; \
......@@ -125,6 +147,15 @@ do { \
r_091c = SQL_SUCCESS; \
} while (0)
#define NORM_STR_LENGTH(obj, ptr, len) \
do { \
if ((len) < 0 && (len)!=SQL_NTS) { \
SET_ERROR((obj), "HY090", TSDB_CODE_ODBC_BAD_ARG, ""); \
return SQL_ERROR; \
} \
if (len==SQL_NTS) len = (ptr) ? (SQLSMALLINT)strlen((const char*)(ptr)) : 0; \
} while (0)
#define PROFILING 0
#define PROFILE(statement) \
......@@ -139,22 +170,67 @@ do { \
gettimeofday(&tv1, NULL); \
double delta = difftime(tv1.tv_sec, tv0.tv_sec); \
delta *= 1000000; \
delta += (tv1.tv_usec-tv0.tv_usec); \
delta += (double)(tv1.tv_usec-tv0.tv_usec); \
delta /= 1000000; \
D("%s: elapsed: [%.6f]s", #statement, delta); \
} while (0)
#define CHK_CONV(statement) \
do { \
const char *sqlstate = statement; \
if (sqlstate) { \
SET_ERROR(sql, sqlstate, TSDB_CODE_ODBC_OUT_OF_RANGE, \
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", \
sql_c_type(valueType), valueType, valueType, \
taos_data_type(type), type, type, idx+1); \
return SQL_ERROR; \
} \
#define CHK_CONV(todb, statement) \
do { \
TSDB_CONV_CODE code_0c80 = (statement); \
switch (code_0c80) { \
case TSDB_CONV_OK: return SQL_SUCCESS; \
case TSDB_CONV_OOM: \
case TSDB_CONV_NOT_AVAIL: { \
SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_OOR: { \
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_OOR, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_CHAR_NOT_NUM: \
case TSDB_CONV_CHAR_NOT_TS: { \
SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_CHAR_NOT_NUM, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_NOT_VALID_TS: { \
SET_ERROR(sql, "22007", TSDB_CODE_ODBC_CONV_NOT_VALID_TS, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_TRUNC_FRACTION: { \
SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC_FRAC, ""); \
return todb ? SQL_ERROR : SQL_SUCCESS_WITH_INFO; \
} break; \
case TSDB_CONV_TRUNC: { \
SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_TRUNC, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_SRC_TOO_LARGE: { \
SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_SRC_TOO_LARGE, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_SRC_BAD_SEQ: { \
SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_SRC_BAD_SEQ, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_SRC_INCOMPLETE: { \
SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_SRC_INCOMPLETE, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_SRC_GENERAL: { \
SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_SRC_GENERAL, ""); \
return SQL_ERROR; \
} break; \
case TSDB_CONV_BAD_CHAR: { \
SET_ERROR(sql, "22001", TSDB_CODE_ODBC_CONV_TRUNC, ""); \
return SQL_ERROR; \
} break; \
default: { \
DASSERTX(0, "internal logic error: %d", code_0c80); \
return SQL_ERROR; /* never reached here */ \
} break; \
} \
} while (0)
typedef struct env_s env_t;
......@@ -186,6 +262,9 @@ struct env_s {
uint64_t refcount;
unsigned int destroying:1;
char env_locale[64];
char env_charset[64];
taos_error_t err;
};
......@@ -193,6 +272,16 @@ struct conn_s {
uint64_t refcount;
env_t *env;
char client_enc[64]; // ODBC client that communicates with this driver
char server_enc[64]; // taos dynamic library that's loaded by this driver
tsdb_conv_t *client_to_server;
tsdb_conv_t *server_to_client;
tsdb_conv_t *utf8_to_client;
tsdb_conv_t *utf16_to_utf8;
tsdb_conv_t *utf16_to_server;
tsdb_conv_t *client_to_utf8;
TAOS *taos;
taos_error_t err;
......@@ -230,50 +319,98 @@ struct c_target_s {
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static void init_routine(void);
// conversions
const char* tsdb_int64_to_bit(int64_t src, int8_t *dst);
const char* tsdb_int64_to_tinyint(int64_t src, int8_t *dst);
const char* tsdb_int64_to_smallint(int64_t src, int16_t *dst);
const char* tsdb_int64_to_int(int64_t src, int32_t *dst);
const char* tsdb_int64_to_bigint(int64_t src, int64_t *dst);
const char* tsdb_int64_to_ts(int64_t src, int64_t *dst);
const char* tsdb_int64_to_float(int64_t src, float *dst);
const char* tsdb_int64_to_double(int64_t src, double *dst);
const char* tsdb_int64_to_char(int64_t src, char *dst, size_t dlen);
const char* tsdb_double_to_bit(double src, int precision, int8_t *dst);
const char* tsdb_double_to_tinyint(double src, int precision, int8_t *dst);
const char* tsdb_double_to_smallint(double src, int precision, int16_t *dst);
const char* tsdb_double_to_int(double src, int precision, int32_t *dst);
const char* tsdb_double_to_bigint(double src, int precision, int64_t *dst);
const char* tsdb_double_to_ts(double src, int precision, int64_t *dst);
const char* tsdb_double_to_float(double src, int precision, float *dst);
const char* tsdb_double_to_double(double src, int precision, double *dst);
const char* tsdb_double_to_char(double src, int precision, char *dst, size_t dlen);
const char* tsdb_chars_to_bit(const char *src, int8_t *dst);
const char* tsdb_chars_to_tinyint(const char *src, int8_t *dst);
const char* tsdb_chars_to_smallint(const char *src, int16_t *dst);
const char* tsdb_chars_to_int(const char *src, int32_t *dst);
const char* tsdb_chars_to_bigint(const char *src, int64_t *dst);
const char* tsdb_chars_to_ts(const char *src, int64_t *dst);
const char* tsdb_chars_to_float(const char *src, float *dst);
const char* tsdb_chars_to_double(const char *src, double *dst);
const char* tsdb_chars_to_char(const char *src, char *dst, size_t dlen);
static int do_field_display_size(TAOS_FIELD *field);
static size_t do_field_display_size(TAOS_FIELD *field);
static tsdb_conv_t* tsdb_conn_client_to_server(conn_t *conn) {
if (!conn->client_to_server) {
conn->client_to_server = tsdb_conv_open(conn->client_enc, conn->server_enc);
}
return conn->client_to_server;
}
static tsdb_conv_t* tsdb_conn_server_to_client(conn_t *conn) {
if (!conn->server_to_client) {
conn->server_to_client = tsdb_conv_open(conn->server_enc, conn->client_enc);
}
return conn->server_to_client;
}
static tsdb_conv_t* tsdb_conn_utf8_to_client(conn_t *conn) {
if (!conn->utf8_to_client) {
conn->utf8_to_client = tsdb_conv_open(UTF8_ENC, conn->client_enc);
}
return conn->utf8_to_client;
}
static tsdb_conv_t* tsdb_conn_utf16_to_utf8(conn_t *conn) {
if (!conn->utf16_to_utf8) {
conn->utf16_to_utf8 = tsdb_conv_open(UTF16_ENC, UTF8_ENC);
}
return conn->utf16_to_utf8;
}
static tsdb_conv_t* tsdb_conn_utf16_to_server(conn_t *conn) {
if (!conn->utf16_to_server) {
conn->utf16_to_server = tsdb_conv_open(UTF16_ENC, conn->server_enc);
}
return conn->utf16_to_server;
}
static tsdb_conv_t* tsdb_conn_client_to_utf8(conn_t *conn) {
if (!conn->client_to_utf8) {
conn->client_to_utf8 = tsdb_conv_open(conn->client_enc, UTF8_ENC);
}
return conn->client_to_utf8;
}
static void tsdb_conn_close_convs(conn_t *conn) {
if (conn->client_to_server) {
tsdb_conv_close(conn->client_to_server);
conn->client_to_server = NULL;
}
if (conn->server_to_client) {
tsdb_conv_close(conn->server_to_client);
conn->server_to_client = NULL;
}
if (conn->utf8_to_client) {
tsdb_conv_close(conn->utf8_to_client);
conn->utf8_to_client = NULL;
}
if (conn->utf16_to_utf8) {
tsdb_conv_close(conn->utf16_to_utf8);
conn->utf16_to_utf8 = NULL;
}
if (conn->utf16_to_server) {
tsdb_conv_close(conn->utf16_to_server);
conn->utf16_to_server = NULL;
}
if (conn->client_to_utf8) {
tsdb_conv_close(conn->client_to_utf8);
conn->client_to_utf8 = NULL;
}
}
#define SFREE(buffer, v, src) \
do { \
const char *v_096a = (const char*)(v); \
const char *src_6a = (const char*)(src); \
if (v_096a && v_096a!=src_6a && !is_owned_by_stack_buffer((buffer), v_096a)) { \
free((char*)v_096a); \
} \
} while (0)
static SQLRETURN doSQLAllocEnv(SQLHENV *EnvironmentHandle)
{
pthread_once(&init_once, init_routine);
env_t *env = (env_t*)calloc(1, sizeof(*env));
if (!env) return SQL_ERROR;
if (!env) return SQL_INVALID_HANDLE;
DASSERT(INC_REF(env)>0);
snprintf(env->env_locale, sizeof(env->env_locale), "%s", tsLocale);
snprintf(env->env_charset, sizeof(env->env_charset), "%s", tsCharset);
*EnvironmentHandle = env;
CLR_ERROR(env);
......@@ -290,7 +427,7 @@ SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle)
static SQLRETURN doSQLFreeEnv(SQLHENV EnvironmentHandle)
{
env_t *env = (env_t*)EnvironmentHandle;
if (!env) return SQL_ERROR;
if (!env) return SQL_INVALID_HANDLE;
DASSERT(GET_REF(env)==1);
......@@ -318,7 +455,12 @@ static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle,
SQLHDBC *ConnectionHandle)
{
env_t *env = (env_t*)EnvironmentHandle;
if (!env) return SQL_ERROR;
if (!env) return SQL_INVALID_HANDLE;
if (!ConnectionHandle) {
SET_ERROR(env, "HY009", TSDB_CODE_ODBC_BAD_ARG, "ConnectionHandle [%p] not valid", ConnectionHandle);
return SQL_ERROR;
}
DASSERT(INC_REF(env)>1);
......@@ -331,6 +473,10 @@ static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle,
}
conn->env = env;
snprintf(conn->client_enc, sizeof(conn->client_enc), "%s", conn->env->env_charset);
snprintf(conn->server_enc, sizeof(conn->server_enc), "%s", conn->env->env_charset);
*ConnectionHandle = conn;
DASSERT(INC_REF(conn)>0);
......@@ -354,7 +500,7 @@ SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle,
static SQLRETURN doSQLFreeConnect(SQLHDBC ConnectionHandle)
{
conn_t *conn = (conn_t*)ConnectionHandle;
if (!conn) return SQL_ERROR;
if (!conn) return SQL_INVALID_HANDLE;
DASSERT(GET_REF(conn)==1);
......@@ -371,6 +517,7 @@ static SQLRETURN doSQLFreeConnect(SQLHDBC ConnectionHandle)
conn->env = NULL;
FREE_ERROR(conn);
tsdb_conn_close_convs(conn);
free(conn);
} while (0);
......@@ -389,6 +536,8 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle,
SQLCHAR *UserName, SQLSMALLINT NameLength2,
SQLCHAR *Authentication, SQLSMALLINT NameLength3)
{
stack_buffer_t buffer; buffer.next = 0;
conn_t *conn = (conn_t*)ConnectionHandle;
if (!conn) return SQL_ERROR;
......@@ -397,28 +546,58 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle,
return SQL_ERROR;
}
const char *serverName = SDUP(ServerName, NameLength1);
const char *userName = SDUP(UserName, NameLength2);
const char *auth = SDUP(Authentication, NameLength3);
NORM_STR_LENGTH(conn, ServerName, NameLength1);
NORM_STR_LENGTH(conn, UserName, NameLength2);
NORM_STR_LENGTH(conn, Authentication, NameLength3);
if (NameLength1>SQL_MAX_DSN_LENGTH) {
SET_ERROR(conn, "HY090", TSDB_CODE_ODBC_BAD_ARG, "");
return SQL_ERROR;
}
tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn);
const char *dsn = NULL;
const char *uid = NULL;
const char *pwd = NULL;
const char *svr = NULL;
char server[4096]; server[0] = '\0';
do {
if ((ServerName && !serverName) || (UserName && !userName) || (Authentication && !auth)) {
tsdb_conv(client_to_server, &buffer, (const char*)ServerName, (size_t)NameLength1, &dsn, NULL);
tsdb_conv(client_to_server, &buffer, (const char*)UserName, (size_t)NameLength2, &uid, NULL);
tsdb_conv(client_to_server, &buffer, (const char*)Authentication, (size_t)NameLength3, &pwd, NULL);
int n = SQLGetPrivateProfileString(dsn, "Server", "", server, sizeof(server)-1, "Odbc.ini");
if (n<=0) {
snprintf(server, sizeof(server), "localhost:6030"); // all 7-bit ascii
}
tsdb_conv(client_to_server, &buffer, (const char*)server, (size_t)strlen(server), &svr, NULL);
if ((!dsn) || (!uid) || (!pwd) || (!svr)) {
SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, "");
break;
}
char *ip = NULL;
int port = 0;
char *p = strchr(svr, ':');
if (p) {
ip = strndup(svr, (size_t)(p-svr));
port = atoi(p+1);
}
// TODO: data-race
// TODO: shall receive ip/port from odbc.ini
conn->taos = taos_connect("localhost", userName, auth, NULL, 0);
conn->taos = taos_connect(ip, uid, pwd, NULL, (uint16_t)port);
if (!conn->taos) {
SET_ERROR(conn, "08001", terrno, "failed to connect to data source");
SET_ERROR(conn, "08001", terrno, "failed to connect to data source for DSN[%s] @[%s:%d]", dsn, ip, port);
break;
}
} while (0);
SFRE(serverName, ServerName, NameLength1);
SFRE(userName, UserName, NameLength2);
SFRE(auth, Authentication, NameLength3);
tsdb_conv_free(client_to_server, dsn, &buffer, (const char*)ServerName);
tsdb_conv_free(client_to_server, uid, &buffer, (const char*)UserName);
tsdb_conv_free(client_to_server, pwd, &buffer, (const char*)Authentication);
tsdb_conv_free(client_to_server, svr, &buffer, (const char*)server);
return conn->taos ? SQL_SUCCESS : SQL_ERROR;
}
......@@ -438,7 +617,7 @@ SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle,
static SQLRETURN doSQLDisconnect(SQLHDBC ConnectionHandle)
{
conn_t *conn = (conn_t*)ConnectionHandle;
if (!conn) return SQL_ERROR;
if (!conn) return SQL_INVALID_HANDLE;
if (conn->taos) {
taos_close(conn->taos);
......@@ -456,10 +635,15 @@ SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle)
}
static SQLRETURN doSQLAllocStmt(SQLHDBC ConnectionHandle,
SQLHSTMT *StatementHandle)
SQLHSTMT *StatementHandle)
{
conn_t *conn = (conn_t*)ConnectionHandle;
if (!conn) return SQL_ERROR;
if (!conn) return SQL_INVALID_HANDLE;
if (!StatementHandle) {
SET_ERROR(conn, "HY009", TSDB_CODE_ODBC_BAD_ARG, "StatementHandle [%p] not valid", StatementHandle);
return SQL_ERROR;
}
DASSERT(INC_REF(conn)>1);
......@@ -496,20 +680,17 @@ static SQLRETURN doSQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle,
switch (HandleType) {
case SQL_HANDLE_ENV: {
SQLHENV env = {0};
if (!OutputHandle) return SQL_ERROR;
SQLRETURN r = doSQLAllocEnv(&env);
if (r==SQL_SUCCESS && OutputHandle) *OutputHandle = env;
if (r==SQL_SUCCESS) *OutputHandle = env;
return r;
} break;
case SQL_HANDLE_DBC: {
SQLHDBC dbc = {0};
SQLRETURN r = doSQLAllocConnect(InputHandle, &dbc);
if (r==SQL_SUCCESS && OutputHandle) *OutputHandle = dbc;
SQLRETURN r = doSQLAllocConnect(InputHandle, OutputHandle);
return r;
} break;
case SQL_HANDLE_STMT: {
SQLHSTMT stmt = {0};
SQLRETURN r = doSQLAllocStmt(InputHandle, &stmt);
if (r==SQL_SUCCESS && OutputHandle) *OutputHandle = stmt;
SQLRETURN r = doSQLAllocStmt(InputHandle, OutputHandle);
return r;
} break;
default: {
......@@ -529,12 +710,20 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle,
SQLUSMALLINT Option)
{
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (Option == SQL_CLOSE) return SQL_SUCCESS;
if (Option != SQL_DROP) {
SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "free statement with Option[%x] not supported yet", Option);
return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
switch (Option) {
case SQL_CLOSE: return SQL_SUCCESS;
case SQL_DROP: break;
case SQL_UNBIND:
case SQL_RESET_PARAMS: {
SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "free statement with Option[%x] not supported yet", Option);
return SQL_ERROR;
} break;
default: {
SET_ERROR(sql, "HY092", TSDB_CODE_ODBC_OUT_OF_RANGE, "free statement with Option[%x] not supported yet", Option);
return SQL_ERROR;
} break;
}
DASSERT(GET_REF(sql)==1);
......@@ -561,6 +750,7 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle,
sql->conn = NULL;
FREE_ERROR(sql);
free(sql);
return SQL_SUCCESS;
......@@ -574,15 +764,32 @@ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle,
return r;
}
static SQLRETURN do_exec_direct(sql_t *sql, TSDB_CONV_CODE code, const char *statement) {
if (code) CHK_CONV(1, code);
DASSERT(code==TSDB_CONV_OK);
SQLRETURN r = SQL_ERROR;
do {
sql->rs = taos_query(sql->conn->taos, statement);
CHK_RS(r, sql, "failed to execute");
} while (0);
return r;
}
static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle,
SQLCHAR *StatementText, SQLINTEGER TextLength)
{
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
conn_t *conn = sql->conn;
NORM_STR_LENGTH(sql, StatementText, TextLength);
if (sql->rs) {
taos_free_result(sql->rs);
sql->rs = NULL;
......@@ -600,19 +807,15 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle,
}
sql->n_params = 0;
const char *stxt = SDUP(StatementText, TextLength);
SQLRETURN r = SQL_ERROR;
SQLRETURN r = SQL_SUCCESS;
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn);
const char *stxt = NULL;
do {
if (!stxt) {
SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, "");
break;
}
sql->rs = taos_query(sql->conn->taos, stxt);
CHK_RS(r, sql, "failed to execute");
TSDB_CONV_CODE code = tsdb_conv(client_to_server, &buffer, (const char*)StatementText, (size_t)TextLength, &stxt, NULL);
r = do_exec_direct(sql, code, stxt);
} while (0);
SFRE(stxt, StatementText, TextLength);
tsdb_conv_free(client_to_server, stxt, &buffer, (const char*)StatementText);
return r;
}
......@@ -625,18 +828,50 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle,
return r;
}
static SQLRETURN doSQLExecDirectW(SQLHSTMT hstmt, SQLWCHAR *szSqlStr, SQLINTEGER cbSqlStr)
{
sql_t *sql = (sql_t*)hstmt;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
conn_t *conn = sql->conn;
if (!szSqlStr) {
SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "szSqlStr [%p] not allowed", szSqlStr);
return SQL_ERROR;
}
if (cbSqlStr < 0) {
SET_ERROR(sql, "HY090", TSDB_CODE_ODBC_BAD_ARG, "");
return SQL_ERROR;
}
SQLRETURN r = SQL_SUCCESS;
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_server = tsdb_conn_utf16_to_server(conn);
const char *stxt = NULL;
do {
size_t slen = (size_t)cbSqlStr * sizeof(*szSqlStr);
TSDB_CONV_CODE code = tsdb_conv(utf16_to_server, &buffer, (const char*)szSqlStr, slen, &stxt, NULL);
r = do_exec_direct(sql, code, stxt);
} while (0);
tsdb_conv_free(utf16_to_server, stxt, &buffer, (const char*)szSqlStr);
return r;
}
SQLRETURN SQL_API SQLExecDirectW(SQLHSTMT hstmt, SQLWCHAR *szSqlStr, SQLINTEGER cbSqlStr)
{
size_t bytes = 0;
SQLCHAR *utf8 = wchars_to_chars(szSqlStr, cbSqlStr, &bytes);
return SQLExecDirect(hstmt, utf8, bytes);
SQLRETURN r = doSQLExecDirectW(hstmt, szSqlStr, cbSqlStr);
return r;
}
static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle,
SQLSMALLINT *ColumnCount)
{
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
......@@ -655,7 +890,7 @@ static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle,
int fields = taos_field_count(sql->rs);
if (ColumnCount) {
*ColumnCount = fields;
*ColumnCount = (SQLSMALLINT)fields;
}
return SQL_SUCCESS;
......@@ -678,7 +913,22 @@ static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle,
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
if (sql->is_insert) {
// ref: https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlrowcount-function?view=sql-server-ver15
// Summary
// SQLRowCount returns the number of rows affected by an UPDATE, INSERT, or DELETE statement;
// an SQL_ADD, SQL_UPDATE_BY_BOOKMARK, or SQL_DELETE_BY_BOOKMARK operation in SQLBulkOperations;
// or an SQL_UPDATE or SQL_DELETE operation in SQLSetPos.
// how to fetch affected rows from taos?
// taos_affected_rows?
if (1) {
SET_ERROR(sql, "IM001", TSDB_CODE_ODBC_NOT_SUPPORT, "");
// if (RowCount) *RowCount = 0;
return SQL_SUCCESS_WITH_INFO;
}
if (!sql->is_insert) {
if (RowCount) *RowCount = 0;
return SQL_SUCCESS;
}
......@@ -736,11 +986,12 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle,
switch (FieldIdentifier) {
case SQL_COLUMN_DISPLAY_SIZE: {
*NumericAttribute = do_field_display_size(field);
*NumericAttribute = (SQLLEN)do_field_display_size(field);
} break;
case SQL_COLUMN_LABEL: {
// todo: check BufferLength
size_t n = sizeof(field->name);
strncpy(CharacterAttribute, field->name, (n>BufferLength ? BufferLength : n));
strncpy(CharacterAttribute, field->name, (n>BufferLength ? (size_t)BufferLength : n));
} break;
case SQL_COLUMN_UNSIGNED: {
*NumericAttribute = SQL_FALSE;
......@@ -767,75 +1018,19 @@ SQLRETURN SQL_API SQLColAttribute(SQLHSTMT StatementHandle,
return r;
}
static SQLRETURN conv_tsdb_bool_to_c_bit(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_bool_to_c_tinyint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_bool_to_c_short(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_bool_to_c_long(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_bool_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_bool_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_bool_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_bool_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_bool_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b);
static SQLRETURN conv_tsdb_v1_to_c_tinyint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1);
static SQLRETURN conv_tsdb_v1_to_c_short(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1);
static SQLRETURN conv_tsdb_v1_to_c_long(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1);
static SQLRETURN conv_tsdb_v1_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1);
static SQLRETURN conv_tsdb_v1_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1);
static SQLRETURN conv_tsdb_v1_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1);
static SQLRETURN conv_tsdb_v1_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1);
static SQLRETURN conv_tsdb_v1_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1);
static SQLRETURN conv_tsdb_v2_to_c_short(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2);
static SQLRETURN conv_tsdb_v2_to_c_long(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2);
static SQLRETURN conv_tsdb_v2_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2);
static SQLRETURN conv_tsdb_v2_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2);
static SQLRETURN conv_tsdb_v2_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2);
static SQLRETURN conv_tsdb_v2_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2);
static SQLRETURN conv_tsdb_v2_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2);
static SQLRETURN conv_tsdb_v4_to_c_long(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4);
static SQLRETURN conv_tsdb_v4_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4);
static SQLRETURN conv_tsdb_v4_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4);
static SQLRETURN conv_tsdb_v4_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4);
static SQLRETURN conv_tsdb_v4_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4);
static SQLRETURN conv_tsdb_v4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4);
static SQLRETURN conv_tsdb_v8_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8);
static SQLRETURN conv_tsdb_v8_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8);
static SQLRETURN conv_tsdb_v8_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8);
static SQLRETURN conv_tsdb_v8_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8);
static SQLRETURN conv_tsdb_v8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8);
static SQLRETURN conv_tsdb_f4_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4);
static SQLRETURN conv_tsdb_f4_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4);
static SQLRETURN conv_tsdb_f4_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4);
static SQLRETURN conv_tsdb_f4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4);
static SQLRETURN conv_tsdb_f8_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, double f8);
static SQLRETURN conv_tsdb_f8_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, double f8);
static SQLRETURN conv_tsdb_f8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, double f8);
static SQLRETURN conv_tsdb_ts_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIELD *field, SQL_TIMESTAMP_STRUCT *ts);
static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, SQL_TIMESTAMP_STRUCT *ts);
static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, SQL_TIMESTAMP_STRUCT *ts);
static SQLRETURN conv_tsdb_ts_to_c_ts(sql_t *sql, c_target_t *target, TAOS_FIELD *field, SQL_TIMESTAMP_STRUCT *ts);
static SQLRETURN conv_tsdb_bin_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const unsigned char *bin);
static SQLRETURN conv_tsdb_bin_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const unsigned char *bin);
static SQLRETURN conv_tsdb_str_to_c_bit(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN conv_tsdb_str_to_c_v1(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN conv_tsdb_str_to_c_v2(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN conv_tsdb_str_to_c_v4(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN conv_tsdb_str_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN conv_tsdb_str_to_c_f4(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN conv_tsdb_str_to_c_f8(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN conv_tsdb_str_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN conv_tsdb_str_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str);
static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,
SQLPOINTER TargetValue, SQLLEN BufferLength,
SQLLEN *StrLen_or_Ind)
{
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
conn_t *conn = sql->conn;
if (!sql->rs) {
SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, "");
return SQL_ERROR;
......@@ -846,8 +1041,6 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
return SQL_ERROR;
}
DASSERT(TargetValue);
int nfields = taos_field_count(sql->rs);
TAOS_FIELD *fields = taos_fetch_fields(sql->rs);
......@@ -860,14 +1053,20 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "NULL TargetValue not allowed for col [%d]", ColumnNumber);
return SQL_ERROR;
}
if (BufferLength<0) {
SET_ERROR(sql, "HY090", TSDB_CODE_ODBC_BAD_ARG, "");
return SQL_ERROR;
}
TAOS_FIELD *field = fields + ColumnNumber-1;
void *row = sql->row[ColumnNumber-1];
if (!row) {
if (StrLen_or_Ind) {
*StrLen_or_Ind = SQL_NULL_DATA;
if (!StrLen_or_Ind) {
SET_ERROR(sql, "22002", TSDB_CODE_ODBC_BAD_ARG, "NULL StrLen_or_Ind not allowed for col [%d]", ColumnNumber);
return SQL_ERROR;
}
*StrLen_or_Ind = SQL_NULL_DATA;
return SQL_SUCCESS;
}
......@@ -879,89 +1078,49 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
target.soi = StrLen_or_Ind;
switch (field->type) {
case TSDB_DATA_TYPE_BOOL: {
int8_t v = *(int8_t*)row;
if (v) v = 1;
switch (target.ct) {
case SQL_C_BIT: return conv_tsdb_bool_to_c_bit(sql, &target, field, v);
case SQL_C_TINYINT: return conv_tsdb_bool_to_c_tinyint(sql, &target, field, v);
case SQL_C_SHORT: return conv_tsdb_bool_to_c_short(sql, &target, field, v);
case SQL_C_LONG: return conv_tsdb_bool_to_c_long(sql, &target, field, v);
case SQL_C_SBIGINT: return conv_tsdb_bool_to_c_sbigint(sql, &target, field, v);
case SQL_C_FLOAT: return conv_tsdb_bool_to_c_float(sql, &target, field, v);
case SQL_C_DOUBLE: return conv_tsdb_bool_to_c_double(sql, &target, field, v);
case SQL_C_CHAR: return conv_tsdb_bool_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_bool_to_c_binary(sql, &target, field, v);
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber);
return SQL_ERROR;
}
}
} break;
case TSDB_DATA_TYPE_TINYINT: {
int8_t v = *(int8_t*)row;
switch (target.ct) {
case SQL_C_TINYINT: return conv_tsdb_v1_to_c_tinyint(sql, &target, field, v);
case SQL_C_SHORT: return conv_tsdb_v1_to_c_short(sql, &target, field, v);
case SQL_C_LONG: return conv_tsdb_v1_to_c_long(sql, &target, field, v);
case SQL_C_SBIGINT: return conv_tsdb_v1_to_c_sbigint(sql, &target, field, v);
case SQL_C_FLOAT: return conv_tsdb_v1_to_c_float(sql, &target, field, v);
case SQL_C_DOUBLE: return conv_tsdb_v1_to_c_double(sql, &target, field, v);
case SQL_C_CHAR: return conv_tsdb_v1_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_v1_to_c_binary(sql, &target, field, v);
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber);
return SQL_ERROR;
}
}
} break;
case TSDB_DATA_TYPE_SMALLINT: {
int16_t v = *(int16_t*)row;
switch (target.ct) {
case SQL_C_SHORT: return conv_tsdb_v2_to_c_short(sql, &target, field, v);
case SQL_C_LONG: return conv_tsdb_v2_to_c_long(sql, &target, field, v);
case SQL_C_SBIGINT: return conv_tsdb_v2_to_c_sbigint(sql, &target, field, v);
case SQL_C_FLOAT: return conv_tsdb_v2_to_c_float(sql, &target, field, v);
case SQL_C_DOUBLE: return conv_tsdb_v2_to_c_double(sql, &target, field, v);
case SQL_C_CHAR: return conv_tsdb_v2_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_v2_to_c_binary(sql, &target, field, v);
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber);
return SQL_ERROR;
}
}
} break;
case TSDB_DATA_TYPE_INT: {
int32_t v = *(int32_t*)row;
switch (target.ct) {
case SQL_C_LONG: return conv_tsdb_v4_to_c_long(sql, &target, field, v);
case SQL_C_SBIGINT: return conv_tsdb_v4_to_c_sbigint(sql, &target, field, v);
case SQL_C_FLOAT: return conv_tsdb_v4_to_c_float(sql, &target, field, v);
case SQL_C_DOUBLE: return conv_tsdb_v4_to_c_double(sql, &target, field, v);
case SQL_C_CHAR: return conv_tsdb_v4_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_v4_to_c_binary(sql, &target, field, v);
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber);
return SQL_ERROR;
}
}
} break;
case TSDB_DATA_TYPE_BOOL:
case TSDB_DATA_TYPE_TINYINT:
case TSDB_DATA_TYPE_SMALLINT:
case TSDB_DATA_TYPE_INT:
case TSDB_DATA_TYPE_BIGINT: {
int64_t v = *(int64_t*)row;
int64_t v;
switch (field->type) {
case TSDB_DATA_TYPE_BOOL: v = *(int8_t*)row; if (v) v = 1; break;
case TSDB_DATA_TYPE_TINYINT: v = *(int8_t*)row; break;
case TSDB_DATA_TYPE_SMALLINT: v = *(int16_t*)row; break;
case TSDB_DATA_TYPE_INT: v = *(int32_t*)row; break;
case TSDB_DATA_TYPE_BIGINT: // fall through
default: v = *(int64_t*)row; break;
}
switch (target.ct) {
case SQL_C_SBIGINT: return conv_tsdb_v8_to_c_sbigint(sql, &target, field, v);
case SQL_C_FLOAT: return conv_tsdb_v8_to_c_float(sql, &target, field, v);
case SQL_C_DOUBLE: return conv_tsdb_v8_to_c_double(sql, &target, field, v);
case SQL_C_CHAR: return conv_tsdb_v8_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_v8_to_c_binary(sql, &target, field, v);
case SQL_C_BIT: {
CHK_CONV(0, tsdb_int64_to_bit(v, TargetValue));
} break;
case SQL_C_TINYINT: {
CHK_CONV(0, tsdb_int64_to_tinyint(v, TargetValue));
} break;
case SQL_C_SHORT: {
CHK_CONV(0, tsdb_int64_to_smallint(v, TargetValue));
} break;
case SQL_C_LONG: {
CHK_CONV(0, tsdb_int64_to_int(v, TargetValue));
} break;
case SQL_C_SBIGINT: {
CHK_CONV(0, tsdb_int64_to_bigint(v, TargetValue));
} break;
case SQL_C_FLOAT: {
CHK_CONV(0, tsdb_int64_to_float(v, TargetValue));
} break;
case SQL_C_DOUBLE: {
CHK_CONV(0, tsdb_int64_to_double(v, TargetValue));
} break;
case SQL_C_CHAR: {
tsdb_conv_t *utf8_to_client = tsdb_conn_utf8_to_client(conn);
size_t len = (size_t)BufferLength;
TSDB_CONV_CODE code = tsdb_conv_write_int64(utf8_to_client, v, (char*)TargetValue, &len);
if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len;
CHK_CONV(0, code);
} break;
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
......@@ -973,10 +1132,21 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
case TSDB_DATA_TYPE_FLOAT: {
float v = *(float*)row;
switch (target.ct) {
case SQL_C_FLOAT: return conv_tsdb_f4_to_c_float(sql, &target, field, v);
case SQL_C_DOUBLE: return conv_tsdb_f4_to_c_double(sql, &target, field, v);
case SQL_C_CHAR: return conv_tsdb_f4_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_f4_to_c_binary(sql, &target, field, v);
case SQL_C_FLOAT: {
*(float*)TargetValue = v;
return SQL_SUCCESS;
} break;
case SQL_C_DOUBLE: {
*(double*)TargetValue = v;
return SQL_SUCCESS;
} break;
case SQL_C_CHAR: {
tsdb_conv_t *utf8_to_client = tsdb_conn_utf8_to_client(conn);
size_t len = (size_t)BufferLength;
TSDB_CONV_CODE code = tsdb_conv_write_double(utf8_to_client, v, (char*)TargetValue, &len);
if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len;
CHK_CONV(0, code);
} break;
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
......@@ -988,9 +1158,17 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
case TSDB_DATA_TYPE_DOUBLE: {
double v = *(double*)row;
switch (target.ct) {
case SQL_C_DOUBLE: return conv_tsdb_f8_to_c_double(sql, &target, field, v);
case SQL_C_CHAR: return conv_tsdb_f8_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_f8_to_c_binary(sql, &target, field, v);
case SQL_C_DOUBLE: {
*(double*)TargetValue = v;
return SQL_SUCCESS;
} break;
case SQL_C_CHAR: {
tsdb_conv_t *utf8_to_client = tsdb_conn_utf8_to_client(conn);
size_t len = (size_t)BufferLength;
TSDB_CONV_CODE code = tsdb_conv_write_double(utf8_to_client, v, (char*)TargetValue, &len);
if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len;
CHK_CONV(0, code);
} break;
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
......@@ -1003,21 +1181,31 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
SQL_TIMESTAMP_STRUCT ts = {0};
int64_t v = *(int64_t*)row;
time_t t = v/1000;
struct tm tm = {0};
localtime_r(&t, &tm);
ts.year = tm.tm_year + 1900;
ts.month = tm.tm_mon + 1;
ts.day = tm.tm_mday;
ts.hour = tm.tm_hour;
ts.minute = tm.tm_min;
ts.second = tm.tm_sec;
ts.fraction = v%1000 * 1000000;
struct tm vtm = {0};
localtime_r(&t, &vtm);
ts.year = (SQLSMALLINT)(vtm.tm_year + 1900);
ts.month = (SQLUSMALLINT)(vtm.tm_mon + 1);
ts.day = (SQLUSMALLINT)(vtm.tm_mday);
ts.hour = (SQLUSMALLINT)(vtm.tm_hour);
ts.minute = (SQLUSMALLINT)(vtm.tm_min);
ts.second = (SQLUSMALLINT)(vtm.tm_sec);
ts.fraction = (SQLUINTEGER)(v%1000 * 1000000);
switch (target.ct) {
case SQL_C_SBIGINT: return conv_tsdb_ts_to_c_v8(sql, &target, field, &ts);
case SQL_C_CHAR: return conv_tsdb_ts_to_c_str(sql, &target, field, &ts);
case SQL_C_BINARY: return conv_tsdb_ts_to_c_bin(sql, &target, field, &ts);
case SQL_C_TYPE_TIMESTAMP:
case SQL_C_TIMESTAMP: return conv_tsdb_ts_to_c_ts(sql, &target, field, &ts);
case SQL_C_SBIGINT: {
*(int64_t*)TargetValue = v;
return SQL_SUCCESS;
} break;
case SQL_C_CHAR: {
tsdb_conv_t *utf8_to_client = tsdb_conn_utf8_to_client(conn);
size_t len = (size_t)BufferLength;
TSDB_CONV_CODE code = tsdb_conv_write_timestamp(utf8_to_client, ts, (char*)TargetValue, &len);
if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len;
CHK_CONV(0, code);
} break;
case SQL_C_TYPE_TIMESTAMP: {
*(SQL_TIMESTAMP_STRUCT*)TargetValue = ts;
return SQL_SUCCESS;
} break;
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
......@@ -1027,10 +1215,34 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
}
} break;
case TSDB_DATA_TYPE_BINARY: {
const unsigned char *bin = (const unsigned char *)row;
size_t field_bytes = (size_t)field->bytes;
field_bytes -= VARSTR_HEADER_SIZE;
switch (target.ct) {
case SQL_C_CHAR: return conv_tsdb_bin_to_c_str(sql, &target, field, bin);
case SQL_C_BINARY: return conv_tsdb_bin_to_c_bin(sql, &target, field, bin);
case SQL_C_CHAR: {
// taos cares nothing about what would be stored in 'binary' as most sql implementations do
// but the client requires to fetch it as a SQL_C_CHAR
// thus, we first try to decode binary to client charset
// if failed, we then do hex-serialization
tsdb_conv_t *server_to_client = tsdb_conn_server_to_client(conn);
size_t slen = strnlen((const char*)row, field_bytes);
size_t len = (size_t)BufferLength;
TSDB_CONV_CODE code = tsdb_conv_write(server_to_client,
(const char*)row, &slen,
(char*)TargetValue, &len);
if (code==TSDB_CONV_OK) {
if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)((size_t)BufferLength - len);
CHK_CONV(0, code);
// code never reached here
}
// todo: hex-serialization
const char *bad = "<bad-charset>";
int n = snprintf((char*)TargetValue, (size_t)BufferLength, "%s", bad);
// what if n < 0 ?
if (StrLen_or_Ind) *StrLen_or_Ind = n;
CHK_CONV(0, n>=BufferLength ? TSDB_CONV_TRUNC : TSDB_CONV_OK);
} break;
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
......@@ -1040,17 +1252,19 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
}
} break;
case TSDB_DATA_TYPE_NCHAR: {
const char *str = (const char *)row;
size_t field_bytes = (size_t)field->bytes;
field_bytes -= VARSTR_HEADER_SIZE;
switch (target.ct) {
case SQL_C_BIT: return conv_tsdb_str_to_c_bit(sql, &target, field, str);
case SQL_C_TINYINT: return conv_tsdb_str_to_c_v1(sql, &target, field, str);
case SQL_C_SHORT: return conv_tsdb_str_to_c_v2(sql, &target, field, str);
case SQL_C_LONG: return conv_tsdb_str_to_c_v4(sql, &target, field, str);
case SQL_C_SBIGINT: return conv_tsdb_str_to_c_v8(sql, &target, field, str);
case SQL_C_FLOAT: return conv_tsdb_str_to_c_f4(sql, &target, field, str);
case SQL_C_DOUBLE: return conv_tsdb_str_to_c_f8(sql, &target, field, str);
case SQL_C_CHAR: return conv_tsdb_str_to_c_str(sql, &target, field, str);
case SQL_C_BINARY: return conv_tsdb_str_to_c_bin(sql, &target, field, str);
case SQL_C_CHAR: {
tsdb_conv_t *server_to_client = tsdb_conn_server_to_client(conn);
size_t slen = strnlen((const char*)row, field_bytes);
size_t len = (size_t)BufferLength;
TSDB_CONV_CODE code = tsdb_conv_write(server_to_client,
(const char*)row, &slen,
(char*)TargetValue, &len);
if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)((size_t)BufferLength - len);
CHK_CONV(0, code);
} break;
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT,
"no convertion from [%s] to [%s[%d][0x%x]] for col [%d]",
......@@ -1084,7 +1298,7 @@ SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle,
static SQLRETURN doSQLFetch(SQLHSTMT StatementHandle)
{
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
......@@ -1108,12 +1322,18 @@ SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle)
static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle,
SQLCHAR *StatementText, SQLINTEGER TextLength)
{
stack_buffer_t buffer; buffer.next = 0;
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
conn_t *conn = sql->conn;
NORM_STR_LENGTH(sql, StatementText, TextLength);
if (sql->rs) {
taos_free_result(sql->rs);
sql->rs = NULL;
......@@ -1139,9 +1359,17 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle,
break;
}
tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn);
const char *stxt = NULL;
int ok = 0;
do {
int r = taos_stmt_prepare(sql->stmt, (const char *)StatementText, TextLength);
tsdb_conv(client_to_server, &buffer, (const char*)StatementText, (size_t)TextLength, &stxt, NULL);
if ((!stxt)) {
SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, "");
break;
}
int r = taos_stmt_prepare(sql->stmt, stxt, (unsigned long)strlen(stxt));
if (r) {
SET_ERROR(sql, "HY000", r, "failed to prepare a TAOS statement");
break;
......@@ -1165,7 +1393,7 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle,
DASSERT(params>=0);
if (params>0) {
param_bind_t *ar = (param_bind_t*)calloc(1, params * sizeof(*ar));
param_bind_t *ar = (param_bind_t*)calloc(1, ((size_t)params) * sizeof(*ar));
if (!ar) {
SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, "");
break;
......@@ -1178,6 +1406,8 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle,
ok = 1;
} while (0);
tsdb_conv_free(client_to_server, stxt, &buffer, (const char*)StatementText);
if (!ok) {
taos_stmt_close(sql->stmt);
sql->stmt = NULL;
......@@ -1207,57 +1437,86 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "parameter [@%d] not bound yet", idx+1);
return SQL_ERROR;
}
if (param->ParameterValue==NULL) {
SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "ParameterValue [@%p] not allowed", param->ParameterValue);
return SQL_ERROR;
}
if (param->StrLen_or_Ind==NULL) {
SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "StrLen_or_Ind [@%p] not allowed", param->StrLen_or_Ind);
return SQL_ERROR;
}
SQLPOINTER paramValue = param->ParameterValue;
SQLSMALLINT valueType = param->ValueType;
SQLLEN *soi = param->StrLen_or_Ind;
conn_t *conn = sql->conn;
size_t offset = idx_row * sql->rowlen + sql->ptr_offset;
unsigned char *paramValue = param->ParameterValue;
SQLSMALLINT valueType = param->ValueType;
SQLLEN *soi = param->StrLen_or_Ind;
if (paramValue) paramValue += offset;
if (soi) soi = (SQLLEN*)((char*)soi + offset);
size_t offset = ((size_t)idx_row) * sql->rowlen + sql->ptr_offset;
paramValue += offset;
soi = (SQLLEN*)((char*)soi + offset);
if (soi && *soi == SQL_NULL_DATA) {
if (*soi == SQL_NULL_DATA) {
bind->is_null = (int*)&yes;
return SQL_SUCCESS;
}
bind->is_null = (int*)&no;
int type = 0;
int bytes = 0;
int tsdb_type = 0; // taos internal data tsdb_type to be bound to
int tsdb_bytes = 0; // we don't rely much on 'tsdb_bytes' here, we delay until taos to check it internally
if (sql->is_insert) {
int r = taos_stmt_get_param(sql->stmt, idx, &type, &bytes);
int r = taos_stmt_get_param(sql->stmt, idx, &tsdb_type, &tsdb_bytes);
if (r) {
SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OUT_OF_RANGE, "parameter [@%d] not valid", idx+1);
return SQL_ERROR;
}
} else {
// we don't have correspondent data type from taos api
// we have to give a good guess here
switch (valueType) {
case SQL_C_BIT: {
tsdb_type = TSDB_DATA_TYPE_BOOL;
} break;
case SQL_C_STINYINT:
case SQL_C_TINYINT: {
tsdb_type = TSDB_DATA_TYPE_TINYINT;
} break;
case SQL_C_SSHORT:
case SQL_C_SHORT: {
tsdb_type = TSDB_DATA_TYPE_SMALLINT;
} break;
case SQL_C_SLONG:
case SQL_C_LONG: {
type = TSDB_DATA_TYPE_INT;
tsdb_type = TSDB_DATA_TYPE_INT;
} break;
case SQL_C_SBIGINT: {
tsdb_type = TSDB_DATA_TYPE_BIGINT;
} break;
case SQL_C_FLOAT: {
tsdb_type = TSDB_DATA_TYPE_FLOAT;
} break;
case SQL_C_DOUBLE: {
tsdb_type = TSDB_DATA_TYPE_DOUBLE;
} break;
case SQL_C_TIMESTAMP: {
tsdb_type = TSDB_DATA_TYPE_TIMESTAMP;
} break;
case SQL_C_CHAR: {
tsdb_type = TSDB_DATA_TYPE_BINARY;
tsdb_bytes = SQL_NTS;
} break;
case SQL_C_WCHAR: {
type = TSDB_DATA_TYPE_NCHAR;
bytes = SQL_NTS;
tsdb_type = TSDB_DATA_TYPE_NCHAR;
tsdb_bytes = SQL_NTS;
} break;
case SQL_C_CHAR:
case SQL_C_SHORT:
case SQL_C_SSHORT:
case SQL_C_USHORT:
case SQL_C_SLONG:
case SQL_C_ULONG:
case SQL_C_FLOAT:
case SQL_C_DOUBLE:
case SQL_C_BIT:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
case SQL_C_TIME:
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_DATE:
case SQL_C_TYPE_TIME:
case SQL_C_TYPE_TIMESTAMP:
......@@ -1274,37 +1533,59 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
}
// ref: https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/converting-data-from-c-to-sql-data-types?view=sql-server-ver15
switch (type) {
switch (tsdb_type) {
case TSDB_DATA_TYPE_BOOL: {
bind->buffer_type = type;
bind->buffer_type = tsdb_type;
bind->buffer_length = sizeof(bind->u.b);
bind->buffer = &bind->u.b;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_LONG: {
CHK_CONV(tsdb_int64_to_bit(*(int32_t*)paramValue, &bind->u.b));
} break;
case SQL_C_BIT: {
CHK_CONV(tsdb_int64_to_bit(*(int8_t*)paramValue, &bind->u.b));
CHK_CONV(1, tsdb_int64_to_bit(*(int8_t*)paramValue, &bind->u.b));
} break;
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_SHORT:
case SQL_C_SSHORT:
case SQL_C_USHORT:
case SQL_C_SLONG:
case SQL_C_ULONG:
case SQL_C_FLOAT:
case SQL_C_DOUBLE:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
case SQL_C_TIME:
case SQL_C_TIMESTAMP:
case SQL_C_STINYINT: {
CHK_CONV(1, tsdb_int64_to_bit(*(int8_t*)paramValue, &bind->u.b));
} break;
case SQL_C_SHORT:
case SQL_C_SSHORT: {
CHK_CONV(1, tsdb_int64_to_bit(*(int16_t*)paramValue, &bind->u.b));
} break;
case SQL_C_LONG:
case SQL_C_SLONG: {
CHK_CONV(1, tsdb_int64_to_bit(*(int32_t*)paramValue, &bind->u.b));
} break;
case SQL_C_SBIGINT: {
CHK_CONV(1, tsdb_int64_to_bit(*(int64_t*)paramValue, &bind->u.b));
} break;
case SQL_C_FLOAT: {
CHK_CONV(1, tsdb_double_to_bit(*(float*)paramValue, &bind->u.b));
} break;
case SQL_C_DOUBLE: {
CHK_CONV(1, tsdb_double_to_bit(*(double*)paramValue, &bind->u.b));
} break;
case SQL_C_CHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
CHK_CONV(1, tsdb_conv_chars_to_bit(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.b));
} break;
case SQL_C_WCHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_bit(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.b));
} break;
case SQL_C_USHORT:
case SQL_C_ULONG:
case SQL_C_UTINYINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
case SQL_C_TIME:
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_DATE:
case SQL_C_TYPE_TIME:
case SQL_C_TYPE_TIMESTAMP:
......@@ -1314,39 +1595,54 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_TINYINT: {
bind->buffer_type = type;
bind->buffer_type = tsdb_type;
bind->buffer_length = sizeof(bind->u.v1);
bind->buffer = &bind->u.v1;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_BIT: {
CHK_CONV(1, tsdb_int64_to_tinyint(*(int8_t*)paramValue, &bind->u.v1));
} break;
case SQL_C_STINYINT:
case SQL_C_TINYINT: {
CHK_CONV(tsdb_int64_to_tinyint(*(int8_t*)paramValue, &bind->u.v1));
CHK_CONV(1, tsdb_int64_to_tinyint(*(int8_t*)paramValue, &bind->u.v1));
} break;
case SQL_C_SSHORT:
case SQL_C_SHORT: {
CHK_CONV(tsdb_int64_to_tinyint(*(int16_t*)paramValue, &bind->u.v1));
CHK_CONV(1, tsdb_int64_to_tinyint(*(int16_t*)paramValue, &bind->u.v1));
} break;
case SQL_C_SLONG:
case SQL_C_LONG: {
CHK_CONV(tsdb_int64_to_tinyint(*(int32_t*)paramValue, &bind->u.v1));
CHK_CONV(1, tsdb_int64_to_tinyint(*(int32_t*)paramValue, &bind->u.v1));
} break;
case SQL_C_SBIGINT: {
CHK_CONV(tsdb_int64_to_tinyint(*(int64_t*)paramValue, &bind->u.v1));
CHK_CONV(1, tsdb_int64_to_tinyint(*(int64_t*)paramValue, &bind->u.v1));
} break;
case SQL_C_CHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
CHK_CONV(1, tsdb_conv_chars_to_tinyint(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v1));
// CHK_CONV(1, tsdb_chars_to_tinyint((const char *)paramValue, (size_t)*soi, &bind->u.v1));
} break;
case SQL_C_WCHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_tinyint(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v1));
} break;
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_SSHORT:
case SQL_C_USHORT:
case SQL_C_SLONG:
case SQL_C_ULONG:
case SQL_C_FLOAT:
case SQL_C_DOUBLE:
case SQL_C_BIT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
......@@ -1362,36 +1658,55 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_SMALLINT: {
bind->buffer_type = type;
bind->buffer_type = tsdb_type;
bind->buffer_length = sizeof(bind->u.v2);
bind->buffer = &bind->u.v2;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_LONG: {
CHK_CONV(tsdb_int64_to_smallint(*(int32_t*)paramValue, &bind->u.v2));
case SQL_C_BIT: {
CHK_CONV(1, tsdb_int64_to_smallint(*(int8_t*)paramValue, &bind->u.v2));
} break;
case SQL_C_SHORT: {
CHK_CONV(tsdb_int64_to_smallint(*(int16_t*)paramValue, &bind->u.v2));
case SQL_C_STINYINT:
case SQL_C_TINYINT: {
CHK_CONV(1, tsdb_int64_to_smallint(*(int8_t*)paramValue, &bind->u.v2));
} break;
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_SSHORT:
case SQL_C_USHORT:
case SQL_C_SHORT: {
CHK_CONV(1, tsdb_int64_to_smallint(*(int16_t*)paramValue, &bind->u.v2));
} break;
case SQL_C_SLONG:
case SQL_C_LONG: {
CHK_CONV(1, tsdb_int64_to_smallint(*(int32_t*)paramValue, &bind->u.v2));
} break;
case SQL_C_SBIGINT: {
CHK_CONV(1, tsdb_int64_to_smallint(*(int64_t*)paramValue, &bind->u.v2));
} break;
case SQL_C_CHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
CHK_CONV(1, tsdb_conv_chars_to_smallint(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v2));
// CHK_CONV(1, tsdb_chars_to_smallint((const char*)paramValue, (size_t)*soi, &bind->u.v2));
} break;
case SQL_C_WCHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_smallint(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v2));
} break;
case SQL_C_USHORT:
case SQL_C_ULONG:
case SQL_C_FLOAT:
case SQL_C_DOUBLE:
case SQL_C_BIT:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
......@@ -1406,34 +1721,55 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_INT: {
bind->buffer_type = type;
bind->buffer_type = tsdb_type;
bind->buffer_length = sizeof(bind->u.v4);
bind->buffer = &bind->u.v4;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_LONG: {
CHK_CONV(tsdb_int64_to_int(*(int32_t*)paramValue, &bind->u.v4));
case SQL_C_BIT: {
CHK_CONV(1, tsdb_int64_to_int(*(int8_t*)paramValue, &bind->u.v4));
} break;
case SQL_C_STINYINT:
case SQL_C_TINYINT: {
CHK_CONV(1, tsdb_int64_to_int(*(int8_t*)paramValue, &bind->u.v4));
} break;
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_SHORT:
case SQL_C_SSHORT:
case SQL_C_USHORT:
case SQL_C_SHORT: {
CHK_CONV(1, tsdb_int64_to_int(*(int16_t*)paramValue, &bind->u.v4));
} break;
case SQL_C_SLONG:
case SQL_C_LONG: {
CHK_CONV(1, tsdb_int64_to_int(*(int32_t*)paramValue, &bind->u.v4));
} break;
case SQL_C_SBIGINT: {
CHK_CONV(1, tsdb_int64_to_int(*(int64_t*)paramValue, &bind->u.v4));
} break;
case SQL_C_CHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
CHK_CONV(1, tsdb_conv_chars_to_int(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v4));
// CHK_CONV(1, tsdb_chars_to_int((const char*)paramValue, (size_t)*soi, &bind->u.v4));
} break;
case SQL_C_WCHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_int(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v4));
} break;
case SQL_C_USHORT:
case SQL_C_ULONG:
case SQL_C_FLOAT:
case SQL_C_DOUBLE:
case SQL_C_BIT:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
......@@ -1448,35 +1784,54 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_BIGINT: {
bind->buffer_type = type;
bind->buffer_type = tsdb_type;
bind->buffer_length = sizeof(bind->u.v8);
bind->buffer = &bind->u.v8;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_SBIGINT: {
bind->u.v8 = *(int64_t*)paramValue;
case SQL_C_BIT: {
CHK_CONV(1, tsdb_int64_to_bigint(*(int8_t*)paramValue, &bind->u.v8));
} break;
case SQL_C_LONG: {
bind->u.v8 = *(int32_t*)paramValue;
case SQL_C_STINYINT:
case SQL_C_TINYINT: {
CHK_CONV(1, tsdb_int64_to_bigint(*(int8_t*)paramValue, &bind->u.v8));
} break;
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_SHORT:
case SQL_C_SSHORT:
case SQL_C_USHORT:
case SQL_C_SHORT: {
CHK_CONV(1, tsdb_int64_to_bigint(*(int16_t*)paramValue, &bind->u.v8));
} break;
case SQL_C_SLONG:
case SQL_C_LONG: {
CHK_CONV(1, tsdb_int64_to_bigint(*(int32_t*)paramValue, &bind->u.v8));
} break;
case SQL_C_SBIGINT: {
CHK_CONV(1, tsdb_int64_to_bigint(*(int64_t*)paramValue, &bind->u.v8));
} break;
case SQL_C_CHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
CHK_CONV(1, tsdb_conv_chars_to_bigint(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v8));
// CHK_CONV(1, tsdb_chars_to_bigint((const char*)paramValue, (size_t)*soi, &bind->u.v8));
} break;
case SQL_C_WCHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_bigint(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v8));
} break;
case SQL_C_USHORT:
case SQL_C_ULONG:
case SQL_C_FLOAT:
case SQL_C_DOUBLE:
case SQL_C_BIT:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
......@@ -1492,36 +1847,58 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_FLOAT: {
bind->buffer_type = type;
bind->buffer_type = tsdb_type;
bind->buffer_length = sizeof(bind->u.f4);
bind->buffer = &bind->u.f4;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_DOUBLE: {
bind->u.f4 = *(double*)paramValue;
case SQL_C_BIT: {
CHK_CONV(1, tsdb_int64_to_float(*(int8_t*)paramValue, &bind->u.f4));
} break;
case SQL_C_STINYINT:
case SQL_C_TINYINT: {
CHK_CONV(1, tsdb_int64_to_float(*(int8_t*)paramValue, &bind->u.f4));
} break;
case SQL_C_SSHORT:
case SQL_C_SHORT: {
CHK_CONV(1, tsdb_int64_to_float(*(int16_t*)paramValue, &bind->u.f4));
} break;
case SQL_C_SLONG:
case SQL_C_LONG: {
CHK_CONV(1, tsdb_int64_to_float(*(int32_t*)paramValue, &bind->u.f4));
} break;
case SQL_C_SBIGINT: {
CHK_CONV(1, tsdb_int64_to_float(*(int64_t*)paramValue, &bind->u.f4));
} break;
case SQL_C_FLOAT: {
bind->u.f4 = *(float*)paramValue;
} break;
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_SHORT:
case SQL_C_SSHORT:
case SQL_C_DOUBLE: {
bind->u.f4 = (float)*(double*)paramValue;
} break;
case SQL_C_CHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
CHK_CONV(1, tsdb_conv_chars_to_float(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.f4));
} break;
case SQL_C_WCHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_float(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.f4));
} break;
case SQL_C_USHORT:
case SQL_C_LONG:
case SQL_C_SLONG:
case SQL_C_ULONG:
case SQL_C_BIT:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
......@@ -1536,34 +1913,59 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_DOUBLE: {
bind->buffer_type = type;
bind->buffer_type = tsdb_type;
bind->buffer_length = sizeof(bind->u.f8);
bind->buffer = &bind->u.f8;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_BIT: {
CHK_CONV(1, tsdb_int64_to_double(*(int8_t*)paramValue, &bind->u.f8));
} break;
case SQL_C_STINYINT:
case SQL_C_TINYINT: {
CHK_CONV(1, tsdb_int64_to_double(*(int8_t*)paramValue, &bind->u.f8));
} break;
case SQL_C_SSHORT:
case SQL_C_SHORT: {
CHK_CONV(1, tsdb_int64_to_double(*(int16_t*)paramValue, &bind->u.f8));
} break;
case SQL_C_SLONG:
case SQL_C_LONG: {
CHK_CONV(1, tsdb_int64_to_double(*(int32_t*)paramValue, &bind->u.f8));
} break;
case SQL_C_SBIGINT: {
CHK_CONV(1, tsdb_int64_to_double(*(int64_t*)paramValue, &bind->u.f8));
} break;
case SQL_C_FLOAT: {
bind->u.f8 = *(float*)paramValue;
} break;
case SQL_C_DOUBLE: {
bind->u.f8 = *(double*)paramValue;
} break;
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_SHORT:
case SQL_C_SSHORT:
case SQL_C_CHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
CHK_CONV(1, tsdb_conv_chars_to_double(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.f8));
// CHK_CONV(1, tsdb_chars_to_double((const char*)paramValue, (size_t)*soi, &bind->u.f8));
} break;
case SQL_C_WCHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_double(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.f8));
} break;
case SQL_C_USHORT:
case SQL_C_LONG:
case SQL_C_SLONG:
case SQL_C_ULONG:
case SQL_C_FLOAT:
case SQL_C_BIT:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
......@@ -1578,35 +1980,51 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_BINARY: {
bind->buffer_type = type;
case TSDB_DATA_TYPE_TIMESTAMP: {
bind->buffer_type = tsdb_type;
bind->buffer_length = sizeof(bind->u.v8);
bind->buffer = &bind->u.v8;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_CHAR: {
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *client_to_utf8 = tsdb_conn_client_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_timestamp_ts(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v8));
} break;
case SQL_C_WCHAR: {
DASSERT(soi);
DASSERT(*soi != SQL_NTS);
size_t bytes = 0;
SQLCHAR *utf8 = wchars_to_chars(paramValue, *soi/2, &bytes);
bind->allocated = 1;
bind->u.bin = utf8;
bind->buffer_length = bytes;
bind->buffer = bind->u.bin;
stack_buffer_t buffer; buffer.next = 0;
tsdb_conv_t *utf16_to_utf8 = tsdb_conn_utf16_to_utf8(conn);
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
CHK_CONV(1, tsdb_conv_chars_to_timestamp_ts(utf16_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.v8));
} break;
case SQL_C_SBIGINT: {
int64_t t = *(int64_t*)paramValue;
bind->u.v8 = t;
} break;
case SQL_C_BINARY: {
bind->u.bin = (unsigned char*)paramValue;
if (*soi == SQL_NTS) {
bind->buffer_length = strlen((const char*)paramValue);
} else {
bind->buffer_length = *soi;
case SQL_C_TYPE_TIMESTAMP: {
SQL_TIMESTAMP_STRUCT ts = *(SQL_TIMESTAMP_STRUCT*)paramValue;
struct tm vtm = {0};
vtm.tm_year = ts.year - 1900;
vtm.tm_mon = ts.month - 1;
vtm.tm_mday = ts.day;
vtm.tm_hour = ts.hour;
vtm.tm_min = ts.minute;
vtm.tm_sec = ts.second;
int64_t t = (int64_t) mktime(&vtm);
if (t==-1) {
CHK_CONV(1, TSDB_CONV_NOT_VALID_TS);
// code never reached here
}
bind->buffer = bind->u.bin;
bind->u.ts = t * 1000 + ts.fraction / 1000000;
} break;
case SQL_C_CHAR:
case SQL_C_SHORT:
case SQL_C_SSHORT:
case SQL_C_USHORT:
......@@ -1619,53 +2037,107 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
case SQL_C_TIME:
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_DATE:
case SQL_C_TYPE_TIME:
case SQL_C_TYPE_TIMESTAMP:
case SQL_C_NUMERIC:
case SQL_C_GUID:
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_TIMESTAMP: {
bind->buffer_type = type;
bind->buffer_length = sizeof(bind->u.v8);
bind->buffer = &bind->u.v8;
case TSDB_DATA_TYPE_BINARY: {
bind->buffer_type = tsdb_type;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_WCHAR: {
DASSERT(soi);
DASSERT(*soi != SQL_NTS);
size_t bytes = 0;
int r = 0;
int64_t t = 0;
SQLCHAR *utf8 = wchars_to_chars(paramValue, *soi/2, &bytes);
// why cast utf8 to 'char*' ?
r = taosParseTime((char*)utf8, &t, strlen((const char*)utf8), TSDB_TIME_PRECISION_MILLI, 0);
bind->u.v8 = t;
free(utf8);
if (r) {
SET_ERROR(sql, "22007", TSDB_CODE_ODBC_OUT_OF_RANGE,
"convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d] failed",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
return SQL_ERROR;
// taos cares nothing about what would be stored in 'binary' as most sql implementations do
// thus, we just copy it as is
// it's caller's responsibility to maintain data-consistency
// if he/she is going to use 'binary' to store characters
// taos might extend it's sql syntax to let user specify
// what charset is to be used for specific 'binary' field when
// table is to be created
// in such way, 'binary' would be 'internationalized'
// but actually speaking, normally, 'char' field is a better
// one for this purpose
size_t slen = (size_t)*soi;
DASSERT(slen != SQL_NTS);
bind->u.bin = (unsigned char*)malloc(slen + 1); // add null-terminator, just for case of use
if (!bind->u.bin) {
CHK_CONV(1, TSDB_CONV_OOM);
// code never reached here
}
memcpy(bind->u.bin, paramValue, slen);
bind->buffer_length = slen;
bind->buffer = bind->u.bin;
CHK_CONV(1, TSDB_CONV_OK);
// tsdb_conv_t *utf16_to_server = tsdb_conn_utf16_to_server(conn);
// size_t slen = (size_t)*soi;
// DASSERT(slen != SQL_NTS);
// const char *buf = NULL;
// size_t blen = 0;
// TSDB_CONV_CODE code = tsdb_conv(utf16_to_server, NULL, (const char *)paramValue, slen, &buf, &blen);
// if (code==TSDB_CONV_OK) {
// if (buf!=(const char*)paramValue) {
// bind->allocated = 1;
// }
// bind->u.bin = (unsigned char*)buf;
// bind->buffer_length = blen;
// bind->buffer = bind->u.bin;
// }
// CHK_CONV(1, code);
} break;
case SQL_C_SBIGINT: {
int64_t t = *(int64_t*)paramValue;
bind->u.v8 = t;
case SQL_C_CHAR: {
// taos cares nothing about what would be stored in 'binary' as most sql implementations do
// thus, we just copy it as is
// it's caller's responsibility to maintain data-consistency
// if he/she is going to use 'binary' to store characters
// taos might extend it's sql syntax to let user specify
// what charset is to be used for specific 'binary' field when
// table is to be created
// in such way, 'binary' would be 'internationalized'
// but actually speaking, normally, 'char' field is a better
// one for this purpose
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
// we can not use strndup, because ODBC client might pass in a buffer without null-terminated
bind->u.bin = (unsigned char*)malloc(slen + 1); // add null-terminator, just for case of use
if (!bind->u.bin) {
CHK_CONV(1, TSDB_CONV_OOM);
// code never reached here
}
memcpy(bind->u.bin, paramValue, slen);
bind->buffer_length = slen;
bind->buffer = bind->u.bin;
CHK_CONV(1, TSDB_CONV_OK);
// code never reached here
// tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn);
// size_t slen = (size_t)*soi;
// if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
// const char *buf = NULL;
// size_t blen = 0;
// TSDB_CONV_CODE code = tsdb_conv(client_to_server, NULL, (const char *)paramValue, slen, &buf, &blen);
// if (code==TSDB_CONV_OK) {
// if (buf!=(const char*)paramValue) {
// bind->allocated = 1;
// }
// bind->u.bin = (unsigned char*)buf;
// bind->buffer_length = blen;
// bind->buffer = bind->u.bin;
// }
// CHK_CONV(1, code);
} break;
case SQL_C_SHORT:
case SQL_C_SSHORT:
......@@ -1679,6 +2151,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_BINARY:
case SQL_C_DATE:
......@@ -1686,40 +2159,55 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_DATE:
case SQL_C_TYPE_TIME:
case SQL_C_TYPE_TIMESTAMP:
case SQL_C_TYPE_TIMESTAMP: // we don't provide auto-converstion
case SQL_C_NUMERIC:
case SQL_C_GUID:
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
} break;
case TSDB_DATA_TYPE_NCHAR: {
bind->buffer_type = type;
bind->buffer_type = tsdb_type;
bind->length = &bind->buffer_length;
switch (valueType) {
case SQL_C_WCHAR: {
DASSERT(soi);
DASSERT(*soi != SQL_NTS);
size_t bytes = 0;
SQLCHAR *utf8 = wchars_to_chars(paramValue, *soi/2, &bytes);
bind->allocated = 1;
bind->u.nchar = (char*)utf8;
bind->buffer_length = bytes;
bind->buffer = bind->u.nchar;
tsdb_conv_t *utf16_to_server = tsdb_conn_utf16_to_server(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
const char *buf = NULL;
size_t blen = 0;
TSDB_CONV_CODE code = tsdb_conv(utf16_to_server, NULL, (const char *)paramValue, slen, &buf, &blen);
if (code==TSDB_CONV_OK) {
if (buf!=(const char*)paramValue) {
bind->allocated = 1;
}
bind->u.nchar = (char*)buf;
bind->buffer_length = blen;
bind->buffer = bind->u.nchar;
}
CHK_CONV(1, code);
} break;
case SQL_C_CHAR: {
bind->u.nchar = (char*)paramValue;
if (*soi == SQL_NTS) {
bind->buffer_length = strlen((const char*)paramValue);
} else {
bind->buffer_length = *soi;
tsdb_conv_t *client_to_server = tsdb_conn_client_to_server(conn);
size_t slen = (size_t)*soi;
if (slen==SQL_NTS) slen = strlen((const char*)paramValue);
const char *buf = NULL;
size_t blen = 0;
TSDB_CONV_CODE code = tsdb_conv(client_to_server, NULL, (const char *)paramValue, slen, &buf, &blen);
if (code==TSDB_CONV_OK) {
if (buf!=(const char*)paramValue) {
bind->allocated = 1;
}
bind->u.bin = (unsigned char*)buf;
bind->buffer_length = blen;
bind->buffer = bind->u.bin;
}
bind->buffer = bind->u.nchar;
CHK_CONV(1, code);
} break;
case SQL_C_SHORT:
case SQL_C_SSHORT:
......@@ -1741,14 +2229,14 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_DATE:
case SQL_C_TYPE_TIME:
case SQL_C_TYPE_TIMESTAMP:
case SQL_C_TYPE_TIMESTAMP: // we don't provide auto-converstion
case SQL_C_NUMERIC:
case SQL_C_GUID:
default: {
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
......@@ -1757,7 +2245,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE,
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]",
sql_c_type(valueType), valueType, valueType,
taos_data_type(type), type, type, idx+1);
taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1);
return SQL_ERROR;
} break;
}
......@@ -1798,7 +2286,7 @@ static SQLRETURN do_execute(sql_t *sql)
for (int i=0; i<sql->n_rows; ++i) {
TAOS_BIND *binds = NULL;
if (sql->n_params>0) {
binds = (TAOS_BIND*)calloc(sql->n_params, sizeof(*binds));
binds = (TAOS_BIND*)calloc((size_t)sql->n_params, sizeof(*binds));
if (!binds) {
SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, "");
return SQL_ERROR;
......@@ -1828,7 +2316,7 @@ static SQLRETURN do_execute(sql_t *sql)
}
sql->is_executed = 1;
if (sql->is_insert) return SQL_SUCCESS;
// if (sql->is_insert) return SQL_SUCCESS;
SQLRETURN r = SQL_SUCCESS;
PROFILE(sql->rs = taos_stmt_use_result(sql->stmt));
......@@ -1873,8 +2361,12 @@ static SQLRETURN doSQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
SQLPOINTER DiagInfo, SQLSMALLINT BufferLength,
SQLSMALLINT *StringLength)
{
// if this function is not exported, isql will never call SQLGetDiagRec
return SQL_ERROR;
switch (DiagIdentifier) {
case SQL_DIAG_CLASS_ORIGIN: {
*StringLength = 0;
} break;
}
return SQL_SUCCESS;
}
SQLRETURN SQL_API SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
......@@ -1950,7 +2442,7 @@ static SQLRETURN doSQLBindParameter(
SQLLEN *StrLen_or_Ind)
{
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
......@@ -1990,6 +2482,16 @@ static SQLRETURN doSQLBindParameter(
return SQL_ERROR;
}
if (ParameterValue==NULL) {
SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "ParameterValue [@%p] not allowed", ParameterValue);
return SQL_ERROR;
}
if (StrLen_or_Ind==NULL) {
SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "StrLen_or_Ind [@%p] not allowed", StrLen_or_Ind);
return SQL_ERROR;
}
param_bind_t *pb = sql->params + ParameterNumber - 1;
pb->ParameterNumber = ParameterNumber;
......@@ -2033,26 +2535,25 @@ static SQLRETURN doSQLDriverConnect(
SQLUSMALLINT fDriverCompletion)
{
conn_t *conn = (conn_t*)hdbc;
if (!conn) return SQL_ERROR;
if (!conn) return SQL_INVALID_HANDLE;
if (fDriverCompletion!=SQL_DRIVER_NOPROMPT) {
SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "option[%d] other than SQL_DRIVER_NOPROMPT not supported yet", fDriverCompletion);
if (conn->taos) {
SET_ERROR(conn, "08002", TSDB_CODE_ODBC_CONNECTION_BUSY, "connection still in use");
return SQL_ERROR;
}
if (conn->taos) {
SET_ERROR(conn, "08002", TSDB_CODE_ODBC_CONNECTION_BUSY, "connection still in use");
if (fDriverCompletion!=SQL_DRIVER_NOPROMPT) {
SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "option[%d] other than SQL_DRIVER_NOPROMPT not supported yet", fDriverCompletion);
return SQL_ERROR;
}
NORM_STR_LENGTH(conn, szConnStrIn, cbConnStrIn);
// DSN=<dsn>; UID=<uid>; PWD=<pwd>
const char *connStr = SDUP(szConnStrIn, cbConnStrIn);
char *serverName = NULL;
char *userName = NULL;
char *auth = NULL;
int bytes = 0;
conn_val_t val = {0};
do {
if (szConnStrIn && !connStr) {
......@@ -2060,32 +2561,52 @@ static SQLRETURN doSQLDriverConnect(
break;
}
int n = sscanf((const char*)connStr, "DSN=%m[^;]; UID=%m[^;]; PWD=%m[^;] %n", &serverName, &userName, &auth, &bytes);
if (n<1) {
int n = todbc_parse_conn_string((const char *)connStr, &val);
if (n) {
SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_BAD_CONNSTR, "unrecognized connection string: [%s]", (const char*)szConnStrIn);
break;
}
char *ip = NULL;
int port = 0;
if (val.server) {
char *p = strchr(val.server, ':');
if (p) {
ip = strndup(val.server, (size_t)(p-val.server));
port = atoi(p+1);
}
}
if ((val.cli_enc && strcmp(val.cli_enc, conn->client_enc)) ||
(val.svr_enc && strcmp(val.svr_enc, conn->server_enc)) )
{
tsdb_conn_close_convs(conn);
if (val.cli_enc) {
snprintf(conn->client_enc, sizeof(conn->client_enc), "%s", val.cli_enc);
}
if (val.svr_enc) {
snprintf(conn->server_enc, sizeof(conn->server_enc), "%s", val.svr_enc);
}
}
// TODO: data-race
// TODO: shall receive ip/port from odbc.ini
conn->taos = taos_connect("localhost", userName, auth, NULL, 0);
// shall we support non-ansi uid/pwd/db etc?
conn->taos = taos_connect(ip ? ip : "localhost", val.uid, val.pwd, val.db, (uint16_t)port);
free(ip); ip = NULL;
if (!conn->taos) {
SET_ERROR(conn, "HY000", terrno, "failed to connect to data source");
break;
}
if (szConnStrOut) {
snprintf((char*)szConnStrOut, cbConnStrOutMax, "%s", connStr);
snprintf((char*)szConnStrOut, (size_t)cbConnStrOutMax, "%s", connStr);
}
if (pcbConnStrOut) {
*pcbConnStrOut = cbConnStrIn;
}
} while (0);
if (serverName) free(serverName);
if (userName) free(userName);
if (auth) free(auth);
conn_val_reset(&val);
SFRE(connStr, szConnStrIn, cbConnStrIn);
......@@ -2112,7 +2633,7 @@ static SQLRETURN doSQLSetConnectAttr(SQLHDBC ConnectionHandle,
SQLINTEGER StringLength)
{
conn_t *conn = (conn_t*)ConnectionHandle;
if (!conn) return SQL_ERROR;
if (!conn) return SQL_INVALID_HANDLE;
if (Attribute != SQL_ATTR_AUTOCOMMIT) {
SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "Attribute other than SQL_ATTR_AUTOCOMMIT not supported yet");
......@@ -2142,7 +2663,7 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable)
{
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
......@@ -2159,19 +2680,21 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber);
return SQL_ERROR;
}
if (BufferLength<0) {
SET_ERROR(sql, "HY090", TSDB_CODE_ODBC_BAD_ARG, "");
return SQL_ERROR;
}
TAOS_FIELD *field = fields + ColumnNumber - 1;
if (ColumnName) {
size_t n = sizeof(field->name);
if (n>BufferLength) n = BufferLength;
if (n>BufferLength) n = (size_t)BufferLength;
strncpy((char*)ColumnName, field->name, n);
}
if (NameLength) {
*NameLength = strnlen(field->name, sizeof(field->name));
}
if (ColumnSize) {
*ColumnSize = field->bytes;
*NameLength = (SQLSMALLINT)strnlen(field->name, sizeof(field->name));
}
if (ColumnSize) *ColumnSize = (SQLULEN)field->bytes;
if (DecimalDigits) *DecimalDigits = 0;
if (DataType) {
......@@ -2205,12 +2728,9 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
} break;
case TSDB_DATA_TYPE_TIMESTAMP: {
// *DataType = SQL_TIMESTAMP;
// *ColumnSize = 30;
// *DecimalDigits = 3;
*DataType = SQL_TIMESTAMP;
*ColumnSize = sizeof(SQL_TIMESTAMP_STRUCT);
*DecimalDigits = 0;
if (ColumnSize) *ColumnSize = sizeof(SQL_TIMESTAMP_STRUCT);
if (DecimalDigits) *DecimalDigits = 0;
} break;
case TSDB_DATA_TYPE_NCHAR: {
......@@ -2219,7 +2739,7 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
} break;
case TSDB_DATA_TYPE_BINARY: {
*DataType = SQL_BINARY;
*DataType = SQL_CHAR;
if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE;
} break;
......@@ -2254,7 +2774,7 @@ SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT StatementHandle,
static SQLRETURN doSQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar)
{
sql_t *sql = (sql_t*)hstmt;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
......@@ -2282,7 +2802,7 @@ static SQLRETURN doSQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar)
return SQL_ERROR;
}
if (pcpar) *pcpar = params;
if (pcpar) *pcpar = (SQLSMALLINT)params;
return SQL_SUCCESS;
}
......@@ -2299,7 +2819,7 @@ static SQLRETURN doSQLSetStmtAttr(SQLHSTMT StatementHandle,
SQLINTEGER StringLength)
{
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql) return SQL_INVALID_HANDLE;
CHK_CONN(sql);
CHK_CONN_TAOS(sql);
......@@ -2356,1126 +2876,296 @@ SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT StatementHandle,
return r;
}
#ifdef _MSC_VER
#define POST_INSTALLER_ERROR(hwndParent, code, fmt, ...) \
do { \
char buf[4096]; \
snprintf(buf, sizeof(buf), "%s[%d]%s():" fmt "", \
basename((char*)__FILE__), __LINE__, __func__, \
##__VA_ARGS__); \
SQLPostInstallerError(code, buf); \
if (hwndParent) { \
MessageBox(hwndParent, buf, "Error", MB_OK|MB_ICONEXCLAMATION); \
} \
} while (0)
typedef struct kv_s kv_t;
struct kv_s {
char *line;
size_t val;
};
static BOOL get_driver_dll_path(HWND hwndParent, char *buf, size_t len)
{
HMODULE hm = NULL;
static void init_routine(void) {
if (0) {
string_conv(NULL, NULL, NULL, 0, NULL, 0, NULL, NULL);
utf8_to_ucs4le(NULL, NULL);
ucs4le_to_utf8(NULL, 0, NULL);
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCSTR) &ConfigDSN, &hm) == 0)
{
int ret = GetLastError();
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "GetModuleHandle failed, error = %d\n", ret);
return FALSE;
}
taos_init();
if (GetModuleFileName(hm, buf, (DWORD)len) == 0)
{
int ret = GetLastError();
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "GetModuleFileName failed, error = %d\n", ret);
return FALSE;
}
return TRUE;
}
static int do_field_display_size(TAOS_FIELD *field) {
switch (field->type) {
case TSDB_DATA_TYPE_TINYINT:
return 5;
break;
static BOOL doDSNAdd(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
BOOL r = TRUE;
case TSDB_DATA_TYPE_SMALLINT:
return 7;
break;
kv_t *kvs = NULL;
case TSDB_DATA_TYPE_INT:
return 12;
break;
kv_t dsn = {0};
char *line = NULL;
case TSDB_DATA_TYPE_BIGINT:
return 22;
break;
do {
char driver_dll[MAX_PATH + 1];
r = get_driver_dll_path(hwndParent, driver_dll, sizeof(driver_dll));
if (!r) break;
dsn.line = strdup("DSN=TAOS_DEMO");
if (!dsn.line) { r = FALSE; break; }
const char *p = lpszAttributes;
int ikvs = 0;
while (p && *p) {
line = strdup(p);
if (!line) { r = FALSE; break; }
char *v = strchr(line, '=');
if (!v) { r = FALSE; break; }
if (strstr(line, "DSN")==line) {
if (dsn.line) {
free(dsn.line);
dsn.line = NULL;
dsn.val = 0;
}
dsn.line = line;
line = NULL;
} else {
kv_t *t = (kv_t*)realloc(kvs, (ikvs+1)*sizeof(*t));
if (!t) { r = FALSE; free(line); break; }
t[ikvs].line = line;
*v = '\0';
if (v) t[ikvs].val = v - line + 1;
line = NULL;
kvs = t;
++ikvs;
}
case TSDB_DATA_TYPE_FLOAT: {
return 12;
} break;
p += strlen(p) + 1;
}
case TSDB_DATA_TYPE_DOUBLE: {
return 20;
} break;
if (hwndParent) {
MessageBox(hwndParent, "Please use odbcconf to add DSN for TAOS ODBC Driver", "Warning!", MB_OK|MB_ICONEXCLAMATION);
}
if (!r) break;
char *v = NULL;
v = strchr(dsn.line, '=');
if (!v) { r = FALSE; break; }
*v = '\0';
dsn.val = v - dsn.line + 1;
if ((!dsn.line)) {
if (!r) POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "lack of either DSN or Driver");
} else {
if (r) r = SQLWritePrivateProfileString("ODBC Data Sources", dsn.line+dsn.val, lpszDriver, "Odbc.ini");
if (r) r = SQLWritePrivateProfileString(dsn.line+dsn.val, "Driver", driver_dll, "Odbc.ini");
}
case TSDB_DATA_TYPE_BINARY:
case TSDB_DATA_TYPE_NCHAR: {
return 3*(field->bytes - VARSTR_HEADER_SIZE) + 2;
} break;
case TSDB_DATA_TYPE_TIMESTAMP:
return 26;
break;
case TSDB_DATA_TYPE_BOOL:
return 7;
default:
break;
}
return 10;
}
// convertion from TSDB_DATA_TYPE_XXX to SQL_C_XXX
static SQLRETURN conv_tsdb_bool_to_c_bit(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
int8_t v = b;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bool_to_c_tinyint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
int8_t v = b;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bool_to_c_short(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
int16_t v = b;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bool_to_c_long(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
int32_t v = b;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bool_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
int64_t v = b;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bool_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
float v = b;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bool_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
double v = b;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bool_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
DASSERT(target->len>0);
*target->soi = 1;
target->ptr[0] = '0' + b;
if (target->len>1) {
target->ptr[1] = '\0';
}
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bool_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t b)
{
DASSERT(target->len>0);
*target->soi = 1;
target->ptr[0] = '0' + b;
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v1_to_c_tinyint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1)
{
int8_t v = v1;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v1_to_c_short(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1)
{
int16_t v = v1;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v1_to_c_long(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1)
{
int32_t v = v1;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v1_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1)
{
int64_t v = v1;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v1_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1)
{
float v = v1;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v1_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1)
{
double v = v1;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v1_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%d", v1);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_TINYINT -> SQL_C_BIT");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_v1_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int8_t v1)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%d", v1);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>target->len ? target->len : n));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_TINYINT -> SQL_C_BIT");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_v2_to_c_short(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2)
{
int16_t v = v2;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v2_to_c_long(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2)
{
int32_t v = v2;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v2_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2)
{
int64_t v = v2;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v2_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2)
{
float v = v2;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v2_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2)
{
double v = v2;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v2_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%d", v2);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_SMALLINT -> SQL_C_CHAR");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_v2_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int16_t v2)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%d", v2);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>target->len ? target->len : n));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_SMALLINT -> SQL_C_CHAR");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_v4_to_c_long(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4)
{
int32_t v = v4;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v4_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4)
{
int64_t v = v4;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v4_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4)
{
float v = v4;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v4_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4)
{
double v = v4;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v4_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%d", v4);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_INTEGER -> SQL_C_CHAR");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_v4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int32_t v4)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%d", v4);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>target->len ? target->len : n));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_INTEGER -> SQL_C_BINARY");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_v8_to_c_sbigint(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8)
{
int64_t v = v8;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v8_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8)
{
float v = v8;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v8_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8)
{
double v = v8;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_v8_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%" PRId64 "", v8);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_BIGINT -> SQL_C_CHAR");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_v8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, int64_t v8)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%" PRId64 "", v8);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>target->len ? target->len : n));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_BIGINT -> SQL_C_BINARY");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_f4_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4)
{
float v = f4;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_f4_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4)
{
double v = f4;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_f4_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%g", f4);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_FLOAT -> SQL_C_CHAR");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_f4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%g", f4);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>target->len ? target->len : n));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_FLOAT -> SQL_C_BINARY");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_f8_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, double f8)
{
double v = f8;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_f8_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIELD *field, double f8)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%.6f", f8);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_DOUBLE -> SQL_C_CHAR");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_f8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_FIELD *field, double f8)
{
char buf[64];
int n = snprintf(buf, sizeof(buf), "%g", f8);
DASSERT(n<sizeof(buf));
*target->soi = n;
strncpy(target->ptr, buf, (n>target->len ? target->len : n));
if (n<=target->len) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_DOUBLE -> SQL_C_BINARY");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_ts_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIELD *field, SQL_TIMESTAMP_STRUCT *ts)
{
struct tm tm = {0};
tm.tm_sec = ts->second;
tm.tm_min = ts->minute;
tm.tm_hour = ts->hour;
tm.tm_mday = ts->day;
tm.tm_mon = ts->month - 1;
tm.tm_year = ts->year - 1900;
time_t t = mktime(&tm);
DASSERT(sizeof(t) == sizeof(int64_t));
int64_t v = (int64_t)t;
v *= 1000;
v += ts->fraction / 1000000;
memcpy(target->ptr, &v, sizeof(v));
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, SQL_TIMESTAMP_STRUCT *ts)
{
struct tm tm = {0};
tm.tm_sec = ts->second;
tm.tm_min = ts->minute;
tm.tm_hour = ts->hour;
tm.tm_mday = ts->day;
tm.tm_mon = ts->month - 1;
tm.tm_year = ts->year - 1900;
char buf[64];
int n = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
DASSERT(n < sizeof(buf));
*target->soi = n;
unsigned int fraction = ts->fraction;
fraction /= 1000000;
snprintf(target->ptr, target->len, "%s.%03d", buf, fraction);
if (target->soi) *target->soi = strlen((const char*)target->ptr);
if (n <= target->len) {
return SQL_SUCCESS;
}
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_TIMESTAMP -> SQL_C_CHAR");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, SQL_TIMESTAMP_STRUCT *ts)
{
struct tm tm = {0};
tm.tm_sec = ts->second;
tm.tm_min = ts->minute;
tm.tm_hour = ts->hour;
tm.tm_mday = ts->day;
tm.tm_mon = ts->month - 1;
tm.tm_year = ts->year - 1900;
char buf[64];
int n = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
DASSERT(n < sizeof(buf));
unsigned int fraction = ts->fraction;
fraction /= 1000000;
snprintf(target->ptr, target->len, "%s.%03d", buf, fraction);
if (target->soi) *target->soi = strlen((const char*)target->ptr);
if (n <= target->len) {
return SQL_SUCCESS;
}
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_TIMESTAMP -> SQL_C_BINARY");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_ts_to_c_ts(sql_t *sql, c_target_t *target, TAOS_FIELD *field, SQL_TIMESTAMP_STRUCT *ts)
{
DASSERT(target->len == sizeof(*ts));
memcpy(target->ptr, ts, sizeof(*ts));
*target->soi = target->len;
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_bin_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const unsigned char *bin)
{
if (target->len<1) {
SET_ERROR(sql, "HY090", TSDB_CODE_ODBC_BAD_ARG, "");
return SQL_ERROR;
}
size_t field_bytes = field->bytes - VARSTR_HEADER_SIZE;
size_t n = strnlen((const char*)bin, field_bytes);
if (n < target->len) {
memcpy(target->ptr, bin, n);
target->ptr[n] = '\0';
*target->soi = n;
return SQL_SUCCESS;
}
n = target->len - 1;
*target->soi = n;
if (n > 0) {
memcpy(target->ptr, bin, n-1);
target->ptr[n-1] = '\0';
}
SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_TRUNC, "");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_bin_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const unsigned char *bin)
{
if (target->len<1) {
SET_ERROR(sql, "HY090", TSDB_CODE_ODBC_BAD_ARG, "");
return SQL_ERROR;
}
size_t field_bytes = field->bytes - VARSTR_HEADER_SIZE;
size_t n = strnlen((const char*)bin, field_bytes);
if (n <= target->len) {
memcpy(target->ptr, bin, n);
if (n<target->len) target->ptr[n] = '\0';
*target->soi = n;
return SQL_SUCCESS;
}
n = target->len;
memcpy(target->ptr, bin, n);
*target->soi = n;
SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_TRUNC, "");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_str_to_c_bit(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
{
int bytes = 0;
double f8 = 0;
int n = sscanf(str, "%lf%n", &f8, &bytes);
int8_t v = f8;
memcpy(target->ptr, &v, sizeof(v));
*target->soi = 1;
if (n!=1 || bytes!=strlen(str)) {
SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_BIT");
return SQL_SUCCESS_WITH_INFO;
}
char buf[64];
snprintf(buf, sizeof(buf), "%d", v);
if (strcmp(buf, str)==0) {
if (v==0 || v==1) return SQL_SUCCESS;
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_BIT");
return SQL_SUCCESS_WITH_INFO;
}
if (f8>0 || f8<2) {
SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_BIT");
return SQL_SUCCESS_WITH_INFO;
}
if (f8<0 || f8>2) {
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_BIT");
return SQL_SUCCESS_WITH_INFO;
}
SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_BIT");
return SQL_SUCCESS_WITH_INFO;
}
static SQLRETURN conv_tsdb_str_to_c_v1(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
{
int bytes = 0;
double f8 = 0;
int n = sscanf(str, "%lf%n", &f8, &bytes);
int8_t v = f8;
memcpy(target->ptr, &v, sizeof(v));
*target->soi = 1;
if (n!=1 || bytes!=strlen(str)) {
SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_TINYINT");
return SQL_SUCCESS_WITH_INFO;
}
char buf[64];
snprintf(buf, sizeof(buf), "%d", v);
if (strcmp(buf, str)==0) return SQL_SUCCESS;
for (int i=0; r && i<ikvs; ++i) {
const char *k = kvs[i].line;
const char *v = NULL;
if (kvs[i].val) v = kvs[i].line + kvs[i].val;
r = SQLWritePrivateProfileString(dsn.line+dsn.val, k, v, "Odbc.ini");
}
} while (0);
if (f8>INT8_MAX || f8<INT8_MIN) {
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_TINYINT");
return SQL_SUCCESS_WITH_INFO;
}
if (dsn.line) free(dsn.line);
if (line) free(line);
SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_TINYINT");
return SQL_SUCCESS_WITH_INFO;
return r;
}
static SQLRETURN conv_tsdb_str_to_c_v2(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
static BOOL doDSNConfig(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
int bytes = 0;
double f8 = 0;
int n = sscanf(str, "%lf%n", &f8, &bytes);
int16_t v = f8;
memcpy(target->ptr, &v, sizeof(v));
*target->soi = 2;
if (n!=1 || bytes!=strlen(str)) {
SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_SHORT");
return SQL_SUCCESS_WITH_INFO;
}
char buf[64];
snprintf(buf, sizeof(buf), "%d", v);
if (strcmp(buf, str)==0) return SQL_SUCCESS;
if (f8>INT16_MAX || f8<INT16_MIN) {
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_SHORT");
return SQL_SUCCESS_WITH_INFO;
const char *p = lpszAttributes;
while (p && *p) {
p += strlen(p) + 1;
}
SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_SHORT");
return SQL_SUCCESS_WITH_INFO;
return FALSE;
}
static SQLRETURN conv_tsdb_str_to_c_v4(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
static BOOL doDSNRemove(HWND hwndParent, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
int bytes = 0;
double f8 = 0;
int n = sscanf(str, "%lf%n", &f8, &bytes);
int32_t v = f8;
memcpy(target->ptr, &v, sizeof(v));
*target->soi = 4;
if (n!=1 || bytes!=strlen(str)) {
SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_LONG");
return SQL_SUCCESS_WITH_INFO;
}
char buf[64];
snprintf(buf, sizeof(buf), "%d", v);
if (strcmp(buf, str)==0) return SQL_SUCCESS;
BOOL r = TRUE;
if (f8>INT32_MAX || f8<INT32_MIN) {
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_LONG");
return SQL_SUCCESS_WITH_INFO;
}
SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_LONG");
return SQL_SUCCESS_WITH_INFO;
}
kv_t dsn = {0};
char *line = NULL;
static SQLRETURN conv_tsdb_str_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
{
int bytes = 0;
double f8 = 0;
int n = sscanf(str, "%lf%n", &f8, &bytes);
do {
const char *p = lpszAttributes;
int ikvs = 0;
while (p && *p) {
line = strdup(p);
if (!line) { r = FALSE; break; }
char *v = strchr(line, '=');
if (!v) { r = FALSE; break; }
*v = '\0';
if (strstr(line, "DSN")==line) {
if (dsn.line) {
free(dsn.line);
dsn.line = NULL;
dsn.val = 0;
}
dsn.line = line;
dsn.val = v - line + 1;
line = NULL;
break;
} else {
free(line);
line = NULL;
}
int64_t v = f8;
memcpy(target->ptr, &v, sizeof(v));
p += strlen(p) + 1;
}
*target->soi = 8;
if (!r) break;
if (n!=1 || bytes!=strlen(str)) {
SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_SBIGINT");
return SQL_SUCCESS_WITH_INFO;
}
if (!dsn.line) {
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_REQUEST_FAILED, "lack of DSN");
r = FALSE;
break;
}
char buf[64];
snprintf(buf, sizeof(buf), "%" PRId64 "", v);
r = SQLWritePrivateProfileString("ODBC Data Sources", dsn.line+dsn.val, NULL, "Odbc.ini");
if (!r) break;
if (strcmp(buf, str)==0) return SQL_SUCCESS;
char buf[8192];
r = SQLGetPrivateProfileString(dsn.line+dsn.val, NULL, "null", buf, sizeof(buf), "Odbc.ini");
if (!r) break;
if (f8>INT64_MAX || f8<INT64_MIN) {
SET_ERROR(sql, "22003", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_SBIGINT");
return SQL_SUCCESS_WITH_INFO;
}
int n = 0;
char *s = buf;
while (s && *s && n++<10) {
SQLWritePrivateProfileString(dsn.line+dsn.val, s, NULL, "Odbc.ini");
s += strlen(s) + 1;
}
} while (0);
SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_SBIGINT");
return SQL_SUCCESS_WITH_INFO;
if (dsn.line) free(dsn.line);
if (line) free(line);
return r;
}
static SQLRETURN conv_tsdb_str_to_c_f4(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
static BOOL doConfigDSN(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
int bytes = 0;
double f8 = 0;
int n = sscanf(str, "%lf%n", &f8, &bytes);
float v = f8;
memcpy(target->ptr, &v, sizeof(v));
*target->soi = 4;
if (n!=1 || bytes!=strlen(str)) {
SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_FLOAT");
return SQL_SUCCESS_WITH_INFO;
BOOL r = FALSE;
const char *sReq = NULL;
switch(fRequest) {
case ODBC_ADD_DSN: sReq = "ODBC_ADD_DSN"; break;
case ODBC_CONFIG_DSN: sReq = "ODBC_CONFIG_DSN"; break;
case ODBC_REMOVE_DSN: sReq = "ODBC_REMOVE_DSN"; break;
default: sReq = "UNKNOWN"; break;
}
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_str_to_c_f8(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
{
int bytes = 0;
double f8 = 0;
int n = sscanf(str, "%lf%n", &f8, &bytes);
float v = f8;
memcpy(target->ptr, &v, sizeof(v));
*target->soi = 8;
if (n!=1 || bytes!=strlen(str)) {
SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_NCHAR -> SQL_C_DOUBLE");
return SQL_SUCCESS_WITH_INFO;
}
return SQL_SUCCESS;
}
static SQLRETURN conv_tsdb_str_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
{
return conv_tsdb_bin_to_c_str(sql, target, field, (const unsigned char*)str);
}
static SQLRETURN conv_tsdb_str_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str)
{
return conv_tsdb_bin_to_c_bin(sql, target, field, (const unsigned char*)str);
}
const char* tsdb_int64_to_bit(int64_t src, int8_t *dst)
{
*dst = src;
if (src==0 || src==1) return NULL;
return "22003";
}
const char* tsdb_int64_to_tinyint(int64_t src, int8_t *dst)
{
*dst = src;
if (src>=SCHAR_MIN && src<=SCHAR_MAX) return NULL;
return "22003";
}
const char* tsdb_int64_to_smallint(int64_t src, int16_t *dst)
{
*dst = src;
if (src>=SHRT_MIN && src<=SHRT_MAX) return NULL;
return "22003";
}
const char* tsdb_int64_to_int(int64_t src, int32_t *dst)
{
*dst = src;
if (src>=LONG_MIN && src<=LONG_MAX) return NULL;
return "22003";
}
const char* tsdb_int64_to_bigint(int64_t src, int64_t *dst)
{
*dst = src;
return NULL;
}
const char* tsdb_int64_to_ts(int64_t src, int64_t *dst)
{
*dst = src;
char buf[4096];
int n = snprintf(buf, sizeof(buf), "%" PRId64 "", src);
DASSERT(n>=0);
DASSERT(n<sizeof(buf));
int64_t secs = src / 1000;
struct tm tm = {0};
if (&tm != localtime_r(&secs, &tm)) return "22007";
return NULL;
}
const char* tsdb_int64_to_float(int64_t src, float *dst)
{
*dst = src;
return NULL;
}
const char* tsdb_int64_to_double(int64_t src, double *dst)
{
*dst = src;
return NULL;
}
const char* tsdb_int64_to_char(int64_t src, char *dst, size_t dlen)
{
int n = snprintf(dst, dlen, "%" PRId64 "", src);
if (n<dlen) return NULL;
return "22003";
}
const char* tsdb_double_to_bit(double src, int precision, int8_t *dst)
{
int64_t v = src;
*dst = v;
if (v<0 || v>=2) return "22003";
char buf[4096];
int n = snprintf(buf, sizeof(buf), "%.*g", precision, src);
DASSERT(n>=0);
DASSERT(n<sizeof(buf));
if (strcmp(buf, "0")==0 || strcmp(buf, "1")==1) {
return NULL;
switch(fRequest) {
case ODBC_ADD_DSN: {
r = doDSNAdd(hwndParent, lpszDriver, lpszAttributes);
} break;
case ODBC_CONFIG_DSN: {
r = doDSNConfig(hwndParent, lpszDriver, lpszAttributes);
} break;
case ODBC_REMOVE_DSN: {
r = doDSNRemove(hwndParent, lpszDriver, lpszAttributes);
} break;
default: {
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
r = FALSE;
} break;
}
return "22001";
}
const char* tsdb_double_to_tinyint(double src, int precision, int8_t *dst)
{
int64_t v = src;
*dst = v;
if (v<SCHAR_MIN || v>SCHAR_MAX) return "22003";
char buf[4096];
int n = snprintf(buf, sizeof(buf), "%.*g", precision, src);
DASSERT(n>=0);
DASSERT(n<sizeof(buf));
if (strchr(buf, '.')) return "01S07";
return NULL;
}
const char* tsdb_double_to_smallint(double src, int precision, int16_t *dst)
{
int64_t v = src;
*dst = v;
if (v<SHRT_MIN || v>SHRT_MAX) return "22003";
char buf[4096];
int n = snprintf(buf, sizeof(buf), "%.*g", precision, src);
DASSERT(n>=0);
DASSERT(n<sizeof(buf));
if (strchr(buf, '.')) return "01S07";
return NULL;
}
const char* tsdb_double_to_int(double src, int precision, int32_t *dst)
{
int64_t v = src;
*dst = v;
if (v<LONG_MIN || v>LONG_MAX) return "22003";
char buf[4096];
int n = snprintf(buf, sizeof(buf), "%.*g", precision, src);
DASSERT(n>=0);
DASSERT(n<sizeof(buf));
if (strchr(buf, '.')) return "01S07";
return NULL;
}
const char* tsdb_double_to_bigint(double src, int precision, int64_t *dst)
{
int64_t v = src;
*dst = v;
if (v<LLONG_MIN || v>LLONG_MAX) return "22003";
char buf[4096];
int n = snprintf(buf, sizeof(buf), "%.*g", precision, src);
DASSERT(n>=0);
DASSERT(n<sizeof(buf));
if (strchr(buf, '.')) return "01S07";
return NULL;
}
const char* tsdb_double_to_ts(double src, int precision, int64_t *dst)
{
int64_t v = src;
*dst = v;
char buf[4096];
int n = snprintf(buf, sizeof(buf), "%.*g", precision, src);
DASSERT(n>=0);
DASSERT(n<sizeof(buf));
if (strchr(buf, '.')) return "01S07";
int64_t secs = v / 1000;
struct tm tm = {0};
if (&tm != localtime_r(&secs, &tm)) return "22007";
return NULL;
}
const char* tsdb_double_to_float(double src, int precision, float *dst)
{
*dst = src;
char buf1[4096], buf2[4096];
int n1 = snprintf(buf1, sizeof(buf1), "%.*g", precision, src);
int n2 = snprintf(buf2, sizeof(buf2), "%.*g", precision, *dst);
DASSERT(n1>=0);
DASSERT(n2>=0);
DASSERT(n1<sizeof(buf1));
DASSERT(n2<sizeof(buf2));
if (strcmp(buf1, buf2)) return "01S07";
return NULL;
return r;
}
const char* tsdb_double_to_double(double src, int precision, double *dst)
BOOL INSTAPI ConfigDSN(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes)
{
*dst = src;
return NULL;
BOOL r;
r = doConfigDSN(hwndParent, fRequest, lpszDriver, lpszAttributes);
return r;
}
const char* tsdb_double_to_char(double src, int precision, char *dst, size_t dlen)
BOOL INSTAPI ConfigTranslator(HWND hwndParent, DWORD *pvOption)
{
int n = snprintf(dst, dlen, "%.*g", precision, src);
DASSERT(n>=0);
if (n>=dlen) return "22001";
return NULL;
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
return FALSE;
}
const char* tsdb_chars_to_bit(const char *src, int8_t *dst)
BOOL INSTAPI ConfigDriver(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszArgs,
LPSTR lpszMsg, WORD cbMsgMax, WORD *pcbMsgOut)
{
int bytes = 0;
int64_t v = 0;
int n = sscanf(src, "%" PRId64 "%n", &v, &bytes);
if (n!=1) return "22018";
if (bytes!=strlen(src)) {
if (src[bytes-1]=='.') {
if (v==0 || v==1) return "22001";
return "22003";
}
return "22018";
}
if (v==0 || v==1) return NULL;
return "22003";
}
const char* tsdb_chars_to_tinyint(const char *src, int8_t *dst)
{
int bytes = 0;
int64_t v = 0;
int n = sscanf(src, "%" PRId64 "%n", &v, &bytes);
if (n!=1) return "22018";
if (bytes!=strlen(src)) {
if (src[bytes-1]=='.') {
if (v<SCHAR_MIN || v>SCHAR_MAX) return "22001";
return "22003";
}
return "22018";
}
if (v<SCHAR_MIN || v>SCHAR_MAX) return "22001";
return NULL;
POST_INSTALLER_ERROR(hwndParent, ODBC_ERROR_GENERAL_ERR, "not implemented yet");
return FALSE;
}
const char* tsdb_chars_to_smallint(const char *src, int16_t *dst)
{
int bytes = 0;
int64_t v = 0;
int n = sscanf(src, "%" PRId64 "%n", &v, &bytes);
if (n!=1) return "22018";
#endif // _MSC_VER
if (bytes!=strlen(src)) {
if (src[bytes-1]=='.') {
if (v<SHRT_MIN || v>SHRT_MAX) return "22001";
return "22003";
}
return "22018";
}
if (v<SHRT_MIN || v>SHRT_MAX) return "22001";
return NULL;
}
const char* tsdb_chars_to_int(const char *src, int32_t *dst)
{
int bytes = 0;
int64_t v = 0;
int n = sscanf(src, "%" PRId64 "%n", &v, &bytes);
if (n!=1) return "22018";
if (bytes!=strlen(src)) {
if (src[bytes-1]=='.') {
if (v<LONG_MIN || v>LONG_MAX) return "22001";
return "22003";
}
return "22018";
}
if (v<LONG_MIN || v>LONG_MAX) return "22001";
return NULL;
static void init_routine(void) {
taos_init();
}
const char* tsdb_chars_to_bigint(const char *src, int64_t *dst)
{
int bytes = 0;
int64_t v = 0;
int n = sscanf(src, "%" PRId64 "%n", &v, &bytes);
if (n!=1) return "22018";
static size_t do_field_display_size(TAOS_FIELD *field) {
switch (field->type) {
case TSDB_DATA_TYPE_TINYINT:
return 5;
break;
if (bytes!=strlen(src)) {
if (src[bytes-1]=='.') {
if (v<LLONG_MIN || v>LLONG_MAX) return "22001";
case TSDB_DATA_TYPE_SMALLINT:
return 7;
break;
return "22003";
}
return "22018";
}
case TSDB_DATA_TYPE_INT:
return 12;
break;
if (v<LLONG_MIN || v>LLONG_MAX) return "22001";
case TSDB_DATA_TYPE_BIGINT:
return 22;
break;
return NULL;
}
case TSDB_DATA_TYPE_FLOAT: {
return 12;
} break;
const char* tsdb_chars_to_ts(const char *src, int64_t *dst)
{
int bytes = 0;
int64_t v = 0;
int n = sscanf(src, "%" PRId64 "%n", &v, &bytes);
if (n!=1) return "22018";
case TSDB_DATA_TYPE_DOUBLE: {
return 20;
} break;
case TSDB_DATA_TYPE_BINARY:
case TSDB_DATA_TYPE_NCHAR: {
return 3*((size_t)field->bytes - VARSTR_HEADER_SIZE) + 2;
} break;
if (bytes!=strlen(src)) {
if (src[bytes-1]=='.') {
if (v<LLONG_MIN || v>LLONG_MAX) return "22001";
case TSDB_DATA_TYPE_TIMESTAMP:
return 26;
break;
return "22003";
}
return "22018";
case TSDB_DATA_TYPE_BOOL:
return 7;
default:
break;
}
if (v<LLONG_MIN || v>LLONG_MAX) return "22001";
return NULL;
}
const char* tsdb_chars_to_float(const char *src, float *dst)
{
int bytes = 0;
int n = sscanf(src, "%f%n", dst, &bytes);
if (n!=1) return "22018";
if (bytes!=strlen(src)) return "22018";
return NULL;
}
const char* tsdb_chars_to_double(const char *src, double *dst)
{
int bytes = 0;
int n = sscanf(src, "%lf%n", dst, &bytes);
if (n!=1) return "22018";
if (bytes!=strlen(src)) return "22018";
return NULL;
return 10;
}
const char* tsdb_chars_to_char(const char *src, char *dst, size_t dlen)
{
int n = snprintf(dst, dlen, "%s", src);
if (n>=dlen) return "22001";
return NULL;
}
EXPORTS
SQLAllocEnv
SQLFreeEnv
SQLAllocConnect
SQLFreeConnect
SQLConnect
SQLDisconnect
SQLAllocStmt
SQLAllocHandle
SQLFreeStmt
SQLExecDirect
SQLExecDirectW
SQLNumResultCols
SQLRowCount
SQLColAttribute
SQLGetData
SQLFetch
SQLPrepare
SQLExecute
SQLGetDiagField
SQLGetDiagRec
SQLBindParameter
SQLDriverConnect
SQLSetConnectAttr
SQLDescribeCol
SQLNumParams
SQLSetStmtAttr
ConfigDSN
ConfigTranslator
ConfigDriver
1 VERSIONINFO
FILEVERSION ${TD_VER_NUMBER}
PRODUCTVERSION ${TD_VER_NUMBER}
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x0L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "ODBC Driver for TDengine"
VALUE "FileVersion", "${TD_VER_NUMBER}"
VALUE "InternalName", "todbc.dll(${TD_VER_CPUTYPE})"
VALUE "LegalCopyright", "Copyright (C) 2020 TAOS Data"
VALUE "OriginalFilename", ""
VALUE "ProductName", "todbc.dll(${TD_VER_CPUTYPE})"
VALUE "ProductVersion", "${TD_VER_NUMBER}"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
\ No newline at end of file
INSTALLDRIVER "TAOS ODBC|Driver=todbc.dll|FileUsage=0|ConnectFunctions=YYN"
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "todbc_conv.h"
#include "todbc_log.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
const char* tsdb_conv_code_str(TSDB_CONV_CODE code) {
switch (code) {
case TSDB_CONV_OK: return "TSDB_CONV_OK";
case TSDB_CONV_NOT_AVAIL: return "TSDB_CONV_NOT_AVAIL";
case TSDB_CONV_OOM: return "TSDB_CONV_OOM";
case TSDB_CONV_OOR: return "TSDB_CONV_OOR";
case TSDB_CONV_TRUNC_FRACTION: return "TSDB_CONV_TRUNC_FRACTION";
case TSDB_CONV_TRUNC: return "TSDB_CONV_TRUNC";
case TSDB_CONV_CHAR_NOT_NUM: return "TSDB_CONV_CHAR_NOT_NUM";
case TSDB_CONV_CHAR_NOT_TS: return "TSDB_CONV_CHAR_NOT_TS";
case TSDB_CONV_NOT_VALID_TS: return "TSDB_CONV_NOT_VALID_TS";
case TSDB_CONV_GENERAL: return "TSDB_CONV_GENERAL";
case TSDB_CONV_SRC_TOO_LARGE: return "TSDB_CONV_SRC_TOO_LARGE";
case TSDB_CONV_SRC_BAD_SEQ: return "TSDB_CONV_SRC_BAD_SEQ";
case TSDB_CONV_SRC_INCOMPLETE: return "TSDB_CONV_SRC_INCOMPLETE";
case TSDB_CONV_SRC_GENERAL: return "TSDB_CONV_SRC_GENERAL";
case TSDB_CONV_BAD_CHAR: return "TSDB_CONV_BAD_CHAR";
default: return "UNKNOWN";
};
}
// src: int
TSDB_CONV_CODE tsdb_int64_to_bit(int64_t src, int8_t *dst) {
*dst = (int8_t)src;
if (src==0 || src==1) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_int64_to_tinyint(int64_t src, int8_t *dst) {
*dst = (int8_t)src;
if (src == *dst) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_int64_to_smallint(int64_t src, int16_t *dst) {
*dst = (int16_t)src;
if (src == *dst) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_int64_to_int(int64_t src, int32_t *dst) {
*dst = (int32_t)src;
if (src == *dst) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_int64_to_bigint(int64_t src, int64_t *dst) {
*dst = src;
return TSDB_CONV_OK;
}
TSDB_CONV_CODE tsdb_int64_to_ts(int64_t src, int64_t *dst) {
*dst = src;
time_t t = (time_t)(src / 1000);
struct tm tm = {0};
if (localtime_r(&t, &tm)) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_int64_to_float(int64_t src, float *dst) {
*dst = (float)src;
int64_t v = (int64_t)*dst;
if (v==src) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_int64_to_double(int64_t src, double *dst) {
*dst = (double)src;
int64_t v = (int64_t)*dst;
if (v==src) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_int64_to_char(int64_t src, char *dst, size_t dlen) {
int n = snprintf(dst, dlen, "%" PRId64 "", src);
DASSERT(n>=0);
if (n<dlen) return TSDB_CONV_OK;
return TSDB_CONV_TRUNC;
}
// src: double
TSDB_CONV_CODE tsdb_double_to_bit(double src, int8_t *dst) {
*dst = (int8_t)src;
if (src<0 || src>=2) return TSDB_CONV_OOR;
if (src == *dst) return TSDB_CONV_OK;
int64_t v = (int64_t)src;
if (v == *dst) return TSDB_CONV_TRUNC_FRACTION;
return TSDB_CONV_TRUNC;
}
TSDB_CONV_CODE tsdb_double_to_tinyint(double src, int8_t *dst) {
*dst = (int8_t)src;
if (src<SCHAR_MIN || src>SCHAR_MAX) return TSDB_CONV_OOR;
if (src == *dst) return TSDB_CONV_OK;
int64_t v = (int64_t)src;
if (v == *dst) return TSDB_CONV_TRUNC_FRACTION;
return TSDB_CONV_TRUNC;
}
TSDB_CONV_CODE tsdb_double_to_smallint(double src, int16_t *dst) {
*dst = (int16_t)src;
if (src<SHRT_MIN || src>SHRT_MAX) return TSDB_CONV_OOR;
if (src == *dst) return TSDB_CONV_OK;
int64_t v = (int64_t)src;
if (v == *dst) return TSDB_CONV_TRUNC_FRACTION;
return TSDB_CONV_TRUNC;
}
TSDB_CONV_CODE tsdb_double_to_int(double src, int32_t *dst) {
*dst = (int32_t)src;
if (src<LONG_MIN || src>LONG_MAX) return TSDB_CONV_OOR;
if (src == *dst) return TSDB_CONV_OK;
int64_t v = (int64_t)src;
if (v == *dst) return TSDB_CONV_TRUNC_FRACTION;
return TSDB_CONV_TRUNC;
}
TSDB_CONV_CODE tsdb_double_to_bigint(double src, int64_t *dst) {
*dst = (int64_t)src;
if (src<LLONG_MIN || src>LLONG_MAX) return TSDB_CONV_OOR;
if (src == *dst) return TSDB_CONV_OK;
int64_t v = (int64_t)src;
if (v == *dst) return TSDB_CONV_TRUNC_FRACTION;
return TSDB_CONV_TRUNC;
}
TSDB_CONV_CODE tsdb_double_to_ts(double src, int64_t *dst) {
TSDB_CONV_CODE code = tsdb_double_to_bigint(src, dst);
if (code==TSDB_CONV_OK || code==TSDB_CONV_TRUNC_FRACTION) {
int64_t v = (int64_t)src;
time_t t = (time_t)(v / 1000);
struct tm tm = {0};
if (localtime_r(&t, &tm)) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
return code;
}
TSDB_CONV_CODE tsdb_double_to_char(double src, char *dst, size_t dlen) {
int n = snprintf(dst, dlen, "%lg", src);
DASSERT(n>=0);
if (n<dlen) return TSDB_CONV_OK;
return TSDB_CONV_TRUNC;
}
// src: SQL_TIMESTAMP_STRUCT
TSDB_CONV_CODE tsdb_timestamp_to_char(SQL_TIMESTAMP_STRUCT src, char *dst, size_t dlen) {
int n = snprintf(dst, dlen, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
src.year, src.month, src.day,
src.hour, src.minute, src.second,
src.fraction / 1000000);
DASSERT(n>=0);
if (n<dlen) return TSDB_CONV_OK;
if (strlen(dst)>=19) return TSDB_CONV_TRUNC_FRACTION;
return TSDB_CONV_TRUNC;
}
// src: chars
TSDB_CONV_CODE tsdb_chars_to_bit(const char *src, size_t smax, int8_t *dst) {
if (strcmp(src, "0")==0) {
*dst = 0;
return TSDB_CONV_OK;
}
if (strcmp(src, "1")==0) {
*dst = 1;
return TSDB_CONV_OK;
}
double v;
int bytes;
int n = sscanf(src, "%lg%n", &v, &bytes);
if (n!=1) return TSDB_CONV_CHAR_NOT_NUM;
if (bytes!=strlen(src)) return TSDB_CONV_CHAR_NOT_NUM;
if (v<0 || v>=2) return TSDB_CONV_OOR;
return TSDB_CONV_TRUNC_FRACTION;
}
TSDB_CONV_CODE tsdb_chars_to_tinyint(const char *src, size_t smax, int8_t *dst) {
int64_t v;
TSDB_CONV_CODE code = tsdb_chars_to_bigint(src, smax, &v);
if (code!=TSDB_CONV_OK) return code;
*dst = (int8_t)v;
if (v==*dst) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_chars_to_smallint(const char *src, size_t smax, int16_t *dst) {
int64_t v;
TSDB_CONV_CODE code = tsdb_chars_to_bigint(src, smax, &v);
if (code!=TSDB_CONV_OK) return code;
*dst = (int16_t)v;
if (v==*dst) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_chars_to_int(const char *src, size_t smax, int32_t *dst) {
int64_t v;
TSDB_CONV_CODE code = tsdb_chars_to_bigint(src, smax, &v);
if (code!=TSDB_CONV_OK) return code;
*dst = (int32_t)v;
if (v==*dst) return TSDB_CONV_OK;
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_chars_to_bigint(const char *src, size_t smax, int64_t *dst) {
int bytes;
int n = sscanf(src, "%" PRId64 "%n", dst, &bytes);
if (n!=1) return TSDB_CONV_CHAR_NOT_NUM;
if (bytes==strlen(src)) {
return TSDB_CONV_OK;
}
double v;
n = sscanf(src, "%lg%n", &v, &bytes);
if (n!=1) return TSDB_CONV_CHAR_NOT_NUM;
if (bytes==strlen(src)) {
return TSDB_CONV_TRUNC_FRACTION;
}
return TSDB_CONV_OK;
}
TSDB_CONV_CODE tsdb_chars_to_ts(const char *src, size_t smax, int64_t *dst) {
int64_t v;
TSDB_CONV_CODE code = tsdb_chars_to_bigint(src, smax, &v);
if (code!=TSDB_CONV_OK) return code;
*dst = v;
if (v==*dst) {
time_t t = (time_t)(v / 1000);
struct tm tm = {0};
if (localtime_r(&t, &tm)) return TSDB_CONV_OK;
}
return TSDB_CONV_OOR;
}
TSDB_CONV_CODE tsdb_chars_to_float(const char *src, size_t smax, float *dst) {
int bytes;
int n = sscanf(src, "%g%n", dst, &bytes);
if (n==1 && bytes==strlen(src)) {
return TSDB_CONV_OK;
}
return TSDB_CONV_CHAR_NOT_NUM;
}
TSDB_CONV_CODE tsdb_chars_to_double(const char *src, size_t smax, double *dst) {
int bytes;
int n = sscanf(src, "%lg%n", dst, &bytes);
if (n==1 && bytes==strlen(src)) {
return TSDB_CONV_OK;
}
return TSDB_CONV_CHAR_NOT_NUM;
}
TSDB_CONV_CODE tsdb_chars_to_timestamp(const char *src, size_t smax, SQL_TIMESTAMP_STRUCT *dst) {
int64_t v = 0;
// why cast to 'char*' ?
int r = taosParseTime((char*)src, &v, (int32_t)smax, TSDB_TIME_PRECISION_MILLI, 0);
if (r) {
return TSDB_CONV_CHAR_NOT_TS;
}
time_t t = v/1000;
struct tm vtm = {0};
localtime_r(&t, &vtm);
dst->year = (SQLSMALLINT)(vtm.tm_year + 1900);
dst->month = (SQLUSMALLINT)(vtm.tm_mon + 1);
dst->day = (SQLUSMALLINT)(vtm.tm_mday);
dst->hour = (SQLUSMALLINT)(vtm.tm_hour);
dst->minute = (SQLUSMALLINT)(vtm.tm_min);
dst->second = (SQLUSMALLINT)(vtm.tm_sec);
dst->fraction = (SQLUINTEGER)(v%1000 * 1000000);
return TSDB_CONV_OK;
}
TSDB_CONV_CODE tsdb_chars_to_timestamp_ts(const char *src, size_t smax, int64_t *dst) {
// why cast to 'char*' ?
int r = taosParseTime((char*)src, dst, (int32_t)smax, TSDB_TIME_PRECISION_MILLI, 0);
if (r) {
return TSDB_CONV_CHAR_NOT_TS;
}
return TSDB_CONV_OK;
}
TSDB_CONV_CODE tsdb_chars_to_char(const char *src, size_t smax, char *dst, size_t dmax) {
int n = snprintf(dst, dmax, "%s", src);
DASSERT(n>=0);
if (n<dmax) return TSDB_CONV_OK;
return TSDB_CONV_TRUNC;
}
char* stack_buffer_alloc(stack_buffer_t *buffer, size_t bytes) {
if (!buffer) return NULL;
// align-by-size_of-size_t-bytes
if (bytes==0) bytes = sizeof(size_t);
bytes = (bytes + sizeof(size_t) - 1) / sizeof(size_t) * sizeof(size_t);
size_t next = buffer->next + bytes;
if (next>sizeof(buffer->buf)) return NULL;
char *p = buffer->buf + buffer->next;
buffer->next = next;
return p;
}
int is_owned_by_stack_buffer(stack_buffer_t *buffer, const char *ptr) {
if (!buffer) return 0;
if (ptr>=buffer->buf && ptr<buffer->buf+buffer->next) return 1;
return 0;
}
struct tsdb_conv_s {
iconv_t cnv;
unsigned int direct:1;
};
static tsdb_conv_t no_conversion = {0};
static pthread_once_t once = PTHREAD_ONCE_INIT;
static void once_init(void) {
no_conversion.cnv = (iconv_t)-1;
no_conversion.direct = 1;
}
tsdb_conv_t* tsdb_conv_direct() { // get a non-conversion-converter
pthread_once(&once, once_init);
return &no_conversion;
}
tsdb_conv_t* tsdb_conv_open(const char *from_enc, const char *to_enc) {
pthread_once(&once, once_init);
tsdb_conv_t *cnv = (tsdb_conv_t*)calloc(1, sizeof(*cnv));
if (!cnv) return NULL;
if (strcmp(from_enc, to_enc)==0 && 0) {
cnv->cnv = (iconv_t)-1;
cnv->direct = 1;
return cnv;
}
cnv->cnv = iconv_open(to_enc, from_enc);
if (cnv->cnv == (iconv_t)-1) {
free(cnv);
return NULL;
}
cnv->direct = 0;
return cnv;
}
void tsdb_conv_close(tsdb_conv_t *cnv) {
if (!cnv) return;
if (cnv == &no_conversion) return;
if (!cnv->direct) {
if (cnv->cnv != (iconv_t)-1) {
iconv_close(cnv->cnv);
}
}
cnv->cnv = (iconv_t)-1;
cnv->direct = 0;
free(cnv);
}
TSDB_CONV_CODE tsdb_conv_write(tsdb_conv_t *cnv, const char *src, size_t *slen, char *dst, size_t *dlen) {
if (!cnv) return TSDB_CONV_NOT_AVAIL;
if (cnv->direct) {
size_t n = (*slen > *dlen) ? *dlen : *slen;
memcpy(dst, src, n);
*slen -= n;
*dlen -= n;
if (*dlen) dst[n] = '\0';
return TSDB_CONV_OK;
}
if (!cnv->cnv) return TSDB_CONV_NOT_AVAIL;
size_t r = iconv(cnv->cnv, (char**)&src, slen, &dst, dlen);
if (r==(size_t)-1) return TSDB_CONV_BAD_CHAR;
if (*slen) return TSDB_CONV_TRUNC;
if (*dlen) *dst = '\0';
return TSDB_CONV_OK;
}
TSDB_CONV_CODE tsdb_conv_write_int64(tsdb_conv_t *cnv, int64_t val, char *dst, size_t *dlen) {
char utf8[64];
int n = snprintf(utf8, sizeof(utf8), "%" PRId64 "", val);
DASSERT(n>=0);
DASSERT(n<sizeof(utf8));
size_t len = (size_t)n;
TSDB_CONV_CODE code = tsdb_conv_write(cnv, utf8, &len, dst, dlen);
*dlen = (size_t)n+1;
return code;
}
TSDB_CONV_CODE tsdb_conv_write_double(tsdb_conv_t *cnv, double val, char *dst, size_t *dlen) {
char utf8[256];
int n = snprintf(utf8, sizeof(utf8), "%g", val);
DASSERT(n>=0);
DASSERT(n<sizeof(utf8));
size_t len = (size_t)n;
TSDB_CONV_CODE code = tsdb_conv_write(cnv, utf8, &len, dst, dlen);
*dlen = (size_t)n+1;
return code;
}
TSDB_CONV_CODE tsdb_conv_write_timestamp(tsdb_conv_t *cnv, SQL_TIMESTAMP_STRUCT val, char *dst, size_t *dlen) {
char utf8[256];
int n = snprintf(utf8, sizeof(utf8), "%04d-%02d-%02d %02d:%02d:%02d.%03d",
val.year, val.month, val.day,
val.hour, val.minute, val.second,
val.fraction / 1000000);
DASSERT(n>=0);
DASSERT(n<sizeof(utf8));
size_t len = (size_t)n;
TSDB_CONV_CODE code = tsdb_conv_write(cnv, utf8, &len, dst, dlen);
*dlen = (size_t)n+1;
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_bit(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int8_t *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_bit(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_tinyint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int8_t *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_tinyint(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_smallint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int16_t *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_smallint(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_int(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int32_t *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_int(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_bigint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_bigint(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_ts(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_ts(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_float(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, float *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_float(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_double(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, double *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_double(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_timestamp(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, SQL_TIMESTAMP_STRUCT *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_timestamp(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv_chars_to_timestamp_ts(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst) {
const char *utf8 = NULL;
TSDB_CONV_CODE code = tsdb_conv(cnv, buffer, src, slen, &utf8, NULL);
if (code) return code;
code = tsdb_chars_to_timestamp_ts(utf8, sizeof(utf8), dst);
tsdb_conv_free(cnv, utf8, buffer, src);
return code;
}
TSDB_CONV_CODE tsdb_conv(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, const char **dst, size_t *dlen) {
if (!cnv) return TSDB_CONV_NOT_AVAIL;
char *buf;
size_t blen;
if (cnv->direct) {
if (src[slen]=='\0') { // access violation?
*dst = src;
if (dlen) *dlen = slen;
return TSDB_CONV_OK;
}
blen = slen + 1;
} else {
blen = (slen + 1) * 4;
}
buf = stack_buffer_alloc(buffer, blen);
if (!buf) {
buf = (char*)malloc(blen);
if (!buf) return TSDB_CONV_OOM;
}
if (cnv->direct) {
size_t n = slen;
DASSERT(blen > n);
memcpy(buf, src, n);
buf[n] = '\0';
*dst = buf;
if (dlen) *dlen = n;
return TSDB_CONV_OK;
}
const char *orig_s = src;
char *orig_d = buf;
size_t orig_blen = blen;
TSDB_CONV_CODE code;
size_t r = iconv(cnv->cnv, (char**)&src, &slen, &buf, &blen);
do {
if (r==(size_t)-1) {
switch(errno) {
case E2BIG: {
code = TSDB_CONV_SRC_TOO_LARGE;
} break;
case EILSEQ: {
code = TSDB_CONV_SRC_BAD_SEQ;
} break;
case EINVAL: {
code = TSDB_CONV_SRC_INCOMPLETE;
} break;
default: {
code = TSDB_CONV_SRC_GENERAL;
} break;
}
break;
}
if (slen) {
code = TSDB_CONV_TRUNC;
break;
}
DASSERT(blen);
*buf = '\0';
*dst = orig_d;
if (dlen) *dlen = orig_blen - blen;
return TSDB_CONV_OK;
} while (0);
if (orig_d!=(char*)orig_s && !is_owned_by_stack_buffer(buffer, orig_d)) free(orig_d);
return code;
}
void tsdb_conv_free(tsdb_conv_t *cnv, const char *ptr, stack_buffer_t *buffer, const char *src) {
if (ptr!=src && !is_owned_by_stack_buffer(buffer, ptr)) free((char*)ptr);
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _todbc_conv_h_
#define _todbc_conv_h_
#include "os.h"
#include <iconv.h>
#include <sql.h>
typedef enum {
TSDB_CONV_OK = 0,
TSDB_CONV_NOT_AVAIL,
TSDB_CONV_OOM,
TSDB_CONV_OOR,
TSDB_CONV_TRUNC_FRACTION,
TSDB_CONV_TRUNC,
TSDB_CONV_CHAR_NOT_NUM,
TSDB_CONV_CHAR_NOT_TS,
TSDB_CONV_NOT_VALID_TS,
TSDB_CONV_GENERAL,
TSDB_CONV_BAD_CHAR,
TSDB_CONV_SRC_TOO_LARGE,
TSDB_CONV_SRC_BAD_SEQ,
TSDB_CONV_SRC_INCOMPLETE,
TSDB_CONV_SRC_GENERAL,
} TSDB_CONV_CODE;
const char* tsdb_conv_code_str(TSDB_CONV_CODE code);
typedef struct stack_buffer_s stack_buffer_t;
struct stack_buffer_s {
char buf[1024*16];
size_t next;
};
char* stack_buffer_alloc(stack_buffer_t *buffer, size_t bytes);
int is_owned_by_stack_buffer(stack_buffer_t *buffer, const char *ptr);
typedef struct tsdb_conv_s tsdb_conv_t;
tsdb_conv_t* tsdb_conv_direct(); // get a non-conversion-converter
tsdb_conv_t* tsdb_conv_open(const char *from_enc, const char *to_enc);
void tsdb_conv_close(tsdb_conv_t *cnv);
TSDB_CONV_CODE tsdb_conv_write(tsdb_conv_t *cnv, const char *src, size_t *slen, char *dst, size_t *dlen);
TSDB_CONV_CODE tsdb_conv_write_int64(tsdb_conv_t *cnv, int64_t val, char *dst, size_t *dlen);
TSDB_CONV_CODE tsdb_conv_write_double(tsdb_conv_t *cnv, double val, char *dst, size_t *dlen);
TSDB_CONV_CODE tsdb_conv_write_timestamp(tsdb_conv_t *cnv, SQL_TIMESTAMP_STRUCT val, char *dst, size_t *dlen);
TSDB_CONV_CODE tsdb_conv_chars_to_bit(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int8_t *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_tinyint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int8_t *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_smallint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int16_t *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_int(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int32_t *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_bigint(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_ts(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_float(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, float *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_double(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, double *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_timestamp(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, SQL_TIMESTAMP_STRUCT *dst);
TSDB_CONV_CODE tsdb_conv_chars_to_timestamp_ts(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, int64_t *dst);
TSDB_CONV_CODE tsdb_conv(tsdb_conv_t *cnv, stack_buffer_t *buffer, const char *src, size_t slen, const char **dst, size_t *dlen);
void tsdb_conv_free(tsdb_conv_t *cnv, const char *ptr, stack_buffer_t *buffer, const char *src);
TSDB_CONV_CODE tsdb_int64_to_bit(int64_t src, int8_t *dst);
TSDB_CONV_CODE tsdb_int64_to_tinyint(int64_t src, int8_t *dst);
TSDB_CONV_CODE tsdb_int64_to_smallint(int64_t src, int16_t *dst);
TSDB_CONV_CODE tsdb_int64_to_int(int64_t src, int32_t *dst);
TSDB_CONV_CODE tsdb_int64_to_bigint(int64_t src, int64_t *dst);
TSDB_CONV_CODE tsdb_int64_to_ts(int64_t src, int64_t *dst);
TSDB_CONV_CODE tsdb_int64_to_float(int64_t src, float *dst);
TSDB_CONV_CODE tsdb_int64_to_double(int64_t src, double *dst);
TSDB_CONV_CODE tsdb_int64_to_char(int64_t src, char *dst, size_t dlen);
TSDB_CONV_CODE tsdb_double_to_bit(double src, int8_t *dst);
TSDB_CONV_CODE tsdb_double_to_tinyint(double src, int8_t *dst);
TSDB_CONV_CODE tsdb_double_to_smallint(double src, int16_t *dst);
TSDB_CONV_CODE tsdb_double_to_int(double src, int32_t *dst);
TSDB_CONV_CODE tsdb_double_to_bigint(double src, int64_t *dst);
TSDB_CONV_CODE tsdb_double_to_ts(double src, int64_t *dst);
TSDB_CONV_CODE tsdb_double_to_char(double src, char *dst, size_t dlen);
TSDB_CONV_CODE tsdb_timestamp_to_char(SQL_TIMESTAMP_STRUCT src, char *dst, size_t dlen);
TSDB_CONV_CODE tsdb_chars_to_bit(const char *src, size_t smax, int8_t *dst);
TSDB_CONV_CODE tsdb_chars_to_tinyint(const char *src, size_t smax, int8_t *dst);
TSDB_CONV_CODE tsdb_chars_to_smallint(const char *src, size_t smax, int16_t *dst);
TSDB_CONV_CODE tsdb_chars_to_int(const char *src, size_t smax, int32_t *dst);
TSDB_CONV_CODE tsdb_chars_to_bigint(const char *src, size_t smax, int64_t *dst);
TSDB_CONV_CODE tsdb_chars_to_ts(const char *src, size_t smax, int64_t *dst);
TSDB_CONV_CODE tsdb_chars_to_float(const char *src, size_t smax, float *dst);
TSDB_CONV_CODE tsdb_chars_to_double(const char *src, size_t smax, double *dst);
TSDB_CONV_CODE tsdb_chars_to_timestamp(const char *src, size_t smax, SQL_TIMESTAMP_STRUCT *dst);
TSDB_CONV_CODE tsdb_chars_to_char(const char *src, size_t smax, char *dst, size_t dmax);
#endif // _todbc_conv_h_
/*
* 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_FLEX_H_
#define _TODBC_FLEX_H_
typedef struct conn_val_s conn_val_t;
struct conn_val_s {
char *key;
char *dsn;
char *uid;
char *pwd;
char *db;
char *server;
char *svr_enc;
char *cli_enc;
};
void conn_val_reset(conn_val_t *val);
int todbc_parse_conn_string(const char *conn, conn_val_t *val);
#endif // _TODBC_FLEX_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 _todbc_log_h_
#define _todbc_log_h_
#include "os.h"
#define D(fmt, ...) \
fprintf(stderr, \
"%s[%d]:%s() " fmt "\n", \
basename((char*)__FILE__), __LINE__, __func__, \
##__VA_ARGS__)
#define DASSERT(statement) \
do { \
if (statement) break; \
D("Assertion failure: %s", #statement); \
abort(); \
} while (0)
#define DASSERTX(statement, fmt, ...) \
do { \
if (statement) break; \
D("Assertion failure: %s, " fmt "", #statement, ##__VA_ARGS__); \
abort(); \
} while (0)
#endif // _todbc_log_h_
%{
#include "todbc_flex.h"
#include <stdio.h>
#ifdef _MSC_VER
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#define PUSH_STATE(state) yy_push_state(state, yyscanner)
#define POP_STATE() yy_pop_state(yyscanner)
#define CHG_STATE(state) \
do { \
yy_pop_state(yyscanner); \
yy_push_state(state, yyscanner); \
} while (0)
#define TOP_STATE(top) \
do { \
yy_push_state(INITIAL, yyscanner); \
top = yy_top_state(yyscanner); \
yy_pop_state(yyscanner); \
} while (0)
#define UNPUT() \
do { \
while (yyleng) unput(yytext[yyleng-1]); \
} while (0)
#define set_key() \
do { \
free(yyextra->key); \
yyextra->key = strdup(yytext); \
} while (0)
#define set_val() \
do { \
if (!yyextra->key) break; \
if (strcasecmp(yyextra->key, "DSN")==0) { \
free(yyextra->dsn); \
yyextra->dsn = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "UID")==0) { \
free(yyextra->uid); \
yyextra->uid = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "PWD")==0) { \
free(yyextra->pwd); \
yyextra->pwd = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "DB")==0) { \
free(yyextra->db); \
yyextra->pwd = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "Server")==0) { \
free(yyextra->server); \
yyextra->server = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "SERVER_ENC")==0) { \
free(yyextra->svr_enc); \
yyextra->svr_enc = strdup(yytext); \
break; \
} \
if (strcasecmp(yyextra->key, "CLIENT_ENC")==0) { \
free(yyextra->cli_enc); \
yyextra->cli_enc = strdup(yytext); \
break; \
} \
} while (0)
%}
%option prefix="todbc_yy"
%option extra-type="conn_val_t *"
%option nounistd
%option never-interactive
%option reentrant
%option noyywrap
%option noinput nounput
%option debug verbose
%option stack
%option nodefault
%option warn
%option perf-report
%option 8bit
%x KEY EQ BRACE1 BRACE2 VAL
%%
<<EOF>> { int state; TOP_STATE(state);
if (state == INITIAL) yyterminate();
if (state == VAL) yyterminate();
return -1; }
[[:space:]]+ { }
[[:alnum:]_]+ { set_key(); PUSH_STATE(KEY); }
.|\n { return -1; }
<KEY>[[:space:]]+ { }
<KEY>[=] { CHG_STATE(EQ); }
<KEY>.|\n { return -1; }
<EQ>[[:space:]]+ { }
<EQ>[^][{}(),;?*=!@/\\\n[:space:]]+ { set_val(); CHG_STATE(VAL); }
<EQ>[{] { CHG_STATE(BRACE1); }
<EQ>.|\n { return -1; }
<BRACE1>[^{}\n]+ { set_val(); CHG_STATE(BRACE2); }
<BRACE1>.|\n { return -1; }
<BRACE2>[[:space:]]+ { }
<BRACE2>[}] { CHG_STATE(VAL); }
<BRACE2>.|\n { return -1; }
<VAL>[;] { POP_STATE(); }
<VAL>.|\n { return -1; }
%%
int todbc_parse_conn_string(const char *conn, conn_val_t *val) {
yyscan_t arg = {0};
yylex_init(&arg);
yyset_debug(0, arg);
yyset_extra(val, arg);
yy_scan_string(conn, arg);
int ret =yylex(arg);
yylex_destroy(arg);
if (val->key) free(val->key); val->key = NULL;
if (ret) {
conn_val_reset(val);
}
return ret ? -1 : 0;
}
void conn_val_reset(conn_val_t *val) {
if (val->key) {
free(val->key); val->key = NULL;
}
if (val->dsn) {
free(val->dsn); val->dsn = NULL;
}
if (val->uid) {
free(val->uid); val->uid = NULL;
}
if (val->pwd) {
free(val->pwd); val->pwd = NULL;
}
if (val->db) {
free(val->db); val->db = NULL;
}
if (val->server) {
free(val->server); val->server = NULL;
}
if (val->svr_enc) {
free(val->svr_enc); val->svr_enc = NULL;
}
if (val->cli_enc) {
free(val->cli_enc); val->cli_enc = NULL;
}
}
......@@ -14,14 +14,10 @@
*/
#include "todbc_util.h"
#include "iconv.h"
#include <sql.h>
#include <sqltypes.h>
#include "todbc_log.h"
#include <iconv.h>
#include <sqlext.h>
#include <stdlib.h>
#include <string.h>
const char* sql_sql_type(int type) {
switch (type) {
......@@ -111,39 +107,6 @@ int is_valid_sql_sql_type(int type) {
return 1;
}
int string_conv(const char *fromcode, const char *tocode,
const unsigned char *src, size_t sbytes,
unsigned char *dst, size_t dbytes,
size_t *consumed, size_t *generated)
{
if (consumed) *consumed = 0;
if (generated) *generated = 0;
if (dbytes <= 0) return -1;
dst[0] = '\0';
iconv_t conv = iconv_open(tocode, fromcode);
if (!conv) return -1;
int r = 0;
do {
char *s = (char*)src;
char *d = (char*)dst;
size_t sl = sbytes;
size_t dl = dbytes;
r = iconv(conv, &s, &sl, &d, &dl);
*d = '\0';
if (consumed) *consumed = sbytes - sl;
if (generated) *generated = dbytes - dl;
} while (0);
iconv_close(conv);
return r;
}
int utf8_chars(const char *src)
{
const char *fromcode = "UTF-8";
......@@ -161,78 +124,6 @@ int utf8_chars(const char *src)
size_t chars = (sizeof(buf) - dlen) / 2;
iconv_close(conv);
return chars;
}
unsigned char* utf8_to_ucs4le(const char *utf8, size_t *chars)
{
const char *tocode = "UCS-4LE";
const char *fromcode = "UTF-8";
iconv_t conv = iconv_open(tocode, fromcode);
if (!conv) return NULL;
unsigned char *ucs4le = NULL;
do {
size_t slen = strlen(utf8);
size_t dlen = slen * 4;
ucs4le = (unsigned char*)malloc(dlen+1);
if (!ucs4le) break;
char *src = (char*)utf8;
char *dst = (char*)ucs4le;
size_t s = slen;
size_t d = dlen;
iconv(conv, &src, &s, &dst, &d);
dst[0] = '\0';
if (chars) *chars = (dlen - d) / 4;
} while (0);
iconv_close(conv);
return ucs4le;
}
char* ucs4le_to_utf8(const unsigned char *ucs4le, size_t slen, size_t *chars)
{
const char *fromcode = "UCS-4LE";
const char *tocode = "UTF-8";
iconv_t conv = iconv_open(tocode, fromcode);
if (!conv) return NULL;
char *utf8 = NULL;
do {
size_t dlen = slen;
utf8 = (char*)malloc(dlen+1);
if (!utf8) break;
char *dst = utf8;
char *src = (char*)ucs4le;
size_t s = slen;
size_t d = dlen;
iconv(conv, &src, &s, &dst, &d);
dst[0] = '\0';
if (chars) *chars = (slen - s) / 4;
} while (0);
iconv_close(conv);
return utf8;
}
SQLCHAR* wchars_to_chars(const SQLWCHAR *wchars, size_t chs, size_t *bytes)
{
size_t dlen = chs * 4;
SQLCHAR *dst = (SQLCHAR*)malloc(dlen + 1);
if (!dst) return NULL;
string_conv("UCS-2LE", "UTF-8", (const unsigned char*)wchars, chs * sizeof(*wchars), dst, dlen + 1, NULL, bytes);
return dst;
return (int)chars;
}
......@@ -16,33 +16,10 @@
#ifndef _TODBC_UTIL_H_
#define _TODBC_UTIL_H_
#include <libgen.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#define D(fmt, ...) \
fprintf(stderr, \
"%s[%d]:%s() " fmt "\n", \
basename((char*)__FILE__), __LINE__, __func__, \
##__VA_ARGS__)
#define DASSERT(statement) \
do { \
if (statement) break; \
D("Assertion failure: %s", #statement); \
abort(); \
} while (0)
#define DASSERTX(statement, fmt, ...) \
do { \
if (statement) break; \
D("Assertion failure: %s, " fmt "", #statement, ##__VA_ARGS__); \
abort(); \
} while (0)
#include "os.h"
#include <sql.h>
#include <sqltypes.h>
const char* sql_sql_type(int type);
const char* sql_c_type(int type);
......@@ -50,14 +27,7 @@ const char* sql_c_type(int type);
int is_valid_sql_c_type(int type);
int is_valid_sql_sql_type(int type);
int string_conv(const char *fromcode, const char *tocode,
const unsigned char *src, size_t sbytes,
unsigned char *dst, size_t dbytes,
size_t *consumed, size_t *generated);
int utf8_chars(const char *src);
unsigned char* utf8_to_ucs4le(const char *utf8, size_t *chars);
char* ucs4le_to_utf8(const unsigned char *ucs4le, size_t slen, size_t *chars);
SQLCHAR* wchars_to_chars(const SQLWCHAR *wchars, size_t chs, size_t *bytes);
#endif // _TODBC_UTIL_H_
PROJECT(TDengine)
IF (TD_LINUX)
AUX_SOURCE_DIRECTORY(. SRC)
# AUX_SOURCE_DIRECTORY(. SRC)
ADD_EXECUTABLE(tcodbc main.c)
TARGET_LINK_LIBRARIES(tcodbc odbc)
ADD_EXECUTABLE(tconv tconv.c)
ENDIF ()
IF (TD_WINDOWS_64)
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /GL")
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL")
# AUX_SOURCE_DIRECTORY(. SRC)
ADD_EXECUTABLE(tcodbc main.c)
TARGET_LINK_LIBRARIES(tcodbc odbc32 odbccp32 user32 legacy_stdio_definitions os)
ADD_EXECUTABLE(tconv tconv.c)
TARGET_LINK_LIBRARIES(tconv tutil)
ENDIF ()
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: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;
#include "../src/todbc_log.h"
#ifdef _MSC_VER
#include <winsock2.h>
#include <windows.h>
#include "os.h"
#endif
#include <sql.h>
#include <sqlext.h>
#include <odbcinst.h>
#include <stdio.h>
#include <string.h>
#include "os.h"
#define CHK_TEST(statement) \
do { \
D("testing: %s", #statement); \
int r = (statement); \
if (r) { \
D("testing failed: %s", #statement); \
return 1; \
} \
} while (0);
typedef struct db_column_s db_column_t;
struct db_column_s {
SQLSMALLINT nameLength;
char name[4096]; // seems enough
SQLSMALLINT dataType;
SQLULEN columnSize;
SQLSMALLINT decimalDigits;
SQLSMALLINT nullable;
};
// static const char *dsn = "TAOS_DSN";
// static const char *uid = "root";
// static const char *pwd = "taosdata";
static db_column_t *columns = NULL;
typedef struct data_s data_t;
struct data_s {
......@@ -37,7 +61,7 @@ static const char *pro_stmts[] = {
// "drop database db"
};
#define CHK_RESULT(r, ht, h) \
#define CHK_RESULT(r, ht, h, fmt, ...) \
do { \
if (r==0) break; \
SQLCHAR ss[10]; \
......@@ -48,23 +72,149 @@ do {
es[0] = '\0'; \
SQLRETURN ret = SQLGetDiagRec(ht, h, 1, ss, &ne, es, sizeof(es), &n); \
if (ret) break; \
fprintf(stderr, "%s%s\n", ss, es); \
D("[%s]%s: " fmt "", ss, es, ##__VA_ARGS__); \
} while (0)
static int open_connect(const char *dsn, const char *uid, const char *pwd, SQLHENV *pEnv, SQLHDBC *pConn) {
SQLRETURN r;
SQLHENV env = {0};
SQLHDBC conn = {0};
r = SQLAllocEnv(&env);
if (r!=SQL_SUCCESS) return 1;
do {
r = SQLAllocConnect(env, &conn);
CHK_RESULT(r, SQL_HANDLE_ENV, env, "");
if (r!=SQL_SUCCESS) break;
do {
r = SQLConnect(conn, (SQLCHAR*)dsn, (SQLSMALLINT)(dsn ? strlen(dsn) : 0),
(SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0),
(SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0));
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
if (r==SQL_SUCCESS) {
*pEnv = env;
*pConn = conn;
return 0;
}
} while (0);
SQLFreeConnect(conn);
} while (0);
SQLFreeEnv(env);
return 1;
}
static int open_driver_connect(const char *connstr, SQLHENV *pEnv, SQLHDBC *pConn) {
SQLRETURN r;
SQLHENV env = {0};
SQLHDBC conn = {0};
r = SQLAllocEnv(&env);
if (r!=SQL_SUCCESS) return 1;
do {
r = SQLAllocConnect(env, &conn);
CHK_RESULT(r, SQL_HANDLE_ENV, env, "");
if (r!=SQL_SUCCESS) break;
do {
SQLCHAR buf[4096];
SQLSMALLINT blen = 0;
SQLHDBC ConnectionHandle = conn;
SQLHWND WindowHandle = NULL;
SQLCHAR * InConnectionString = (SQLCHAR*)connstr;
SQLSMALLINT StringLength1 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0);
SQLCHAR * OutConnectionString = buf;
SQLSMALLINT BufferLength = sizeof(buf);
SQLSMALLINT * StringLength2Ptr = &blen;
SQLUSMALLINT DriverCompletion = SQL_DRIVER_NOPROMPT;
r = SQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString,
StringLength1, OutConnectionString, BufferLength,
StringLength2Ptr, DriverCompletion);
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
if (r==SQL_SUCCESS) {
*pEnv = env;
*pConn = conn;
return 0;
}
} while (0);
SQLFreeConnect(conn);
} while (0);
SQLFreeEnv(env);
return 1;
}
static SQLRETURN traverse_cols(SQLHSTMT stmt, SQLSMALLINT cols) {
SQLRETURN r = SQL_ERROR;
for (SQLSMALLINT i=0; i<cols; ++i) {
db_column_t column = {0};
r = SQLDescribeCol(stmt, (SQLUSMALLINT)(i+1), (SQLCHAR*)column.name,
(SQLSMALLINT)sizeof(column.name), &column.nameLength,
&column.dataType, &column.columnSize,
&column.decimalDigits, &column.nullable);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
D("col%02d:[%s]%d,type:[%d],colSize:[%"PRId64"],decimalDigits:[%d],nullable:[%d]",
i+1, column.name, column.nameLength, column.dataType, column.columnSize,
column.decimalDigits, column.nullable);
db_column_t *col = (db_column_t*)realloc(columns, (size_t)(i+1)*sizeof(*col));
if (!col) {
D("out of memory");
return SQL_ERROR;
}
col[i] = column;
columns = col;
}
return SQL_SUCCESS;
}
static int do_statement(SQLHSTMT stmt, const char *statement) {
SQLRETURN r = 0;
do {
fprintf(stderr, "prepare [%s]\n", statement);
r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement));
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
r = SQLExecDirect(stmt, (SQLCHAR*)statement, SQL_NTS);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: [%s]", statement);
if (r) break;
fprintf(stderr, "execute [%s]\n", statement);
r = SQLExecute(stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
SQLSMALLINT cols = 0;
r = SQLNumResultCols(stmt, &cols);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
if (r) break;
fprintf(stderr, "done\n");
if (cols <= 0) break;
r = traverse_cols(stmt, cols);
char buf[4096];
while (1) {
SQLRETURN r = SQLFetch(stmt);
if (r==SQL_NO_DATA) break;
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
for (size_t i=0; i<cols; ++i) {
SQLLEN soi = 0;
r = SQLGetData(stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, buf, sizeof(buf), &soi);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
if (r) {
if (r!=SQL_SUCCESS_WITH_INFO) {
if (i>0) fprintf(stdout, "\n");
return r;
}
}
if (soi==SQL_NULL_DATA) {
fprintf(stdout, "%snull", i==0?"":",");
} else {
fprintf(stdout, "%s\"%s\"", i==0?"":",", buf);
}
}
fprintf(stdout, "\n");
}
// r = SQLFetch(stmt);
// if (r==SQL_NO_DATA) {
// D("..........");
// r = SQL_SUCCESS;
// break;
// }
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
// if (r) break;
// r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement));
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
// if (r) break;
// r = SQLExecute(stmt);
// CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
// if (r) break;
} while (0);
fprintf(stderr, "r: [%x][%d]\n", r, r);
return r;
}
......@@ -74,158 +224,450 @@ static int do_insert(SQLHSTMT stmt, data_t data) {
SQLLEN lblob;
const char *statement = "insert into t values (?, ?, ?, ?, ?, ?, ?, ?, ?,?)";
int ignored = 0;
#define ignored 0
do {
fprintf(stderr, "prepare [%s]\n", statement);
r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement));
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
r = SQLPrepare(stmt, (SQLCHAR*)statement, (SQLINTEGER)strlen(statement));
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 1 [%s]\n", statement);
r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_TIMESTAMP, ignored, ignored, &data.ts, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 2 [%s]\n", statement);
r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, ignored, ignored, &data.b, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 3 [%s]\n", statement);
r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, ignored, ignored, &data.v1, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 4 [%s]\n", statement);
r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, ignored, ignored, &data.v2, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 5 [%s]\n", statement);
r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, ignored, ignored, &data.v4, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 6 [%s]\n", statement);
r = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, ignored, ignored, &data.v8, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 7 [%s]\n", statement);
r = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, ignored, ignored, &data.f4, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 8 [%s]\n", statement);
r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignored, ignored, &data.f8, ignored, NULL);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 9 [%s]\n", statement);
lbin = SQL_NTS;
r = SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, sizeof(data.bin)-1, ignored, &data.bin, ignored, &lbin);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "bind 10 [%s]\n", statement);
lblob = SQL_NTS;
r = SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(data.blob)-1, ignored, &data.blob, ignored, &lblob);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
fprintf(stderr, "execute [%s]\n", statement);
r = SQLExecute(stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt);
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement);
if (r) break;
// ts += 1;
// v = 2;
// fprintf(stderr, "execute [%s]\n", statement);
// r = SQLExecute(stmt);
// if (r) break;
fprintf(stderr, "done\n");
} while (0);
fprintf(stderr, "r: [%x][%d]\n", r, r);
#undef ignored
return r;
}
int main(int argc, char *argv[]) {
if (argc < 4) return 1;
const char *dsn = argv[1];
const char *uid = argv[2];
const char *pwd = argv[3];
SQLRETURN r;
static int test1(const char *dsn, const char *uid, const char *pwd) {
SQLHENV env = {0};
SQLHDBC conn = {0};
r = SQLAllocEnv(&env);
if (r!=SQL_SUCCESS) return 1;
int n = open_connect(dsn, uid, pwd, &env, &conn);
if (n) return 1;
int ok = 0;
do {
r = SQLAllocConnect(env, &conn);
CHK_RESULT(r, SQL_HANDLE_ENV, env);
SQLRETURN r = SQL_SUCCESS;
SQLHSTMT stmt = {0};
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
if (r!=SQL_SUCCESS) break;
do {
r = SQLConnect(conn, (SQLCHAR*)dsn, strlen(dsn),
(SQLCHAR*)uid, strlen(uid),
(SQLCHAR*)pwd, strlen(pwd));
CHK_RESULT(r, SQL_HANDLE_DBC, conn);
if (r!=SQL_SUCCESS) break;
if (do_statement(stmt, "drop database if exists db")) {
break;
}
for (size_t i=0; i<sizeof(pre_stmts)/sizeof(pre_stmts[0]); ++i) {
n = do_statement(stmt, pre_stmts[i]);
if (n) break;
}
do {
data_t data = {0};
data.ts = 1591060628001;
data.b = 1;
data.v1 = 127;
data.v2 = 32767;
data.v4 = 2147483647;
data.v8 = 9223372036854775807;
data.f4 = 123.456f;
data.f8 = 9999999.999999;
memset(data.bin, 0, sizeof(data.bin));
memset(data.blob, 0, sizeof(data.blob));
snprintf(data.bin, sizeof(data.bin), "hel我lo");
snprintf(data.blob, sizeof(data.blob), "world");
snprintf(data.blob, sizeof(data.blob), "wo人rld");
SQLHSTMT stmt = {0};
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
if (r!=SQL_SUCCESS) break;
do {
do_statement(stmt, "drop database db");
for (size_t i=0; i<sizeof(pre_stmts)/sizeof(pre_stmts[0]); ++i) {
r = do_statement(stmt, pre_stmts[i]);
if (r!=SQL_SUCCESS) break;
}
do {
data_t data = {0};
data.ts = 1591060628001;
data.b = 1;
data.v1 = 127;
data.v2 = 32767;
data.v4 = 2147483647;
data.v8 = 9223372036854775807;
data.f4 = 123.456;
data.f8 = 9999999.999999;
memset(data.bin, 0, sizeof(data.bin));
memset(data.blob, 0, sizeof(data.blob));
snprintf(data.bin, sizeof(data.bin), "hel我lo");
snprintf(data.blob, sizeof(data.blob), "world");
snprintf(data.blob, sizeof(data.blob), "wo人rld");
SQLHSTMT stmt = {0};
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
if (r!=SQL_SUCCESS) break;
do {
r = do_insert(stmt, data);
if (r!=SQL_SUCCESS) break;
} while (0);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
// r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
// if (r!=SQL_SUCCESS) break;
// do {
// r = do_insert(stmt, ts++, v++);
// if (r!=SQL_SUCCESS) break;
// } while (0);
// SQLFreeHandle(SQL_HANDLE_STMT, stmt);
} while (0);
if (r!=SQL_SUCCESS) break;
for (size_t i=0; i<sizeof(pro_stmts)/sizeof(pro_stmts[0]); ++i) {
r = do_statement(stmt, pro_stmts[i]);
if (r!=SQL_SUCCESS) break;
}
n = do_insert(stmt, data);
if (n) break;
} while (0);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
// r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
// if (r!=SQL_SUCCESS) break;
// do {
// r = do_insert(stmt, ts++, v++);
// if (r!=SQL_SUCCESS) break;
// } while (0);
// SQLFreeHandle(SQL_HANDLE_STMT, stmt);
ok = 1;
} while (0);
SQLDisconnect(conn);
if (!ok) break;
ok = 0;
for (size_t i=0; i<sizeof(pro_stmts)/sizeof(pro_stmts[0]); ++i) {
n = do_statement(stmt, pro_stmts[i]);
if (n) break;
}
ok = 1;
} while (0);
SQLFreeConnect(conn);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
} while (0);
SQLDisconnect(conn);
SQLFreeConnect(conn);
SQLFreeEnv(env);
return ok ? 0 : 1;
}
int test_statements(const char *dsn, const char *uid, const char *pwd, const char **statements) {
SQLRETURN r = SQL_SUCCESS;
SQLHENV env = {0};
SQLHDBC conn = {0};
int n = open_connect(dsn, uid, pwd, &env, &conn);
if (n) return 1;
do {
SQLHSTMT stmt = {0};
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
if (r!=SQL_SUCCESS) break;
const char **p = statements;
while (*p) {
if (do_statement(stmt, *p)) {
r = SQL_ERROR;
break;
}
++p;
}
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
} while (0);
SQLDisconnect(conn);
SQLFreeConnect(conn);
SQLFreeEnv(env);
return r ? 1 : 0;
}
int test_driver_connect(const char *connstr) {
SQLRETURN r = SQL_SUCCESS;
SQLHENV env = {0};
SQLHDBC conn = {0};
int n = open_driver_connect(connstr, &env, &conn);
if (n) return 1;
SQLDisconnect(conn);
SQLFreeConnect(conn);
SQLFreeEnv(env);
return r ? 1 : 0;
}
int create_statement(SQLHENV env, SQLHDBC conn, SQLHSTMT *pStmt) {
SQLHSTMT stmt = {0};
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
if (r==SQL_SUCCESS) {
*pStmt = stmt;
return 0;
}
if (r==SQL_SUCCESS_WITH_INFO) {
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
}
return 1;
}
int do_statements(SQLHSTMT stmt, const char **statements) {
const char **p = statements;
while (p && *p) {
CHK_TEST(do_statement(stmt, *p));
++p;
}
return 0;
}
int tests_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt) {
const char *statements[] = {
"drop database if exists m",
"create database m",
"use m",
// "create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(1), name nchar(1))",
"create table t (ts timestamp, b bool)",
"insert into t values('2020-10-10 00:00:00', 0)",
"insert into t values('2020-10-10 00:00:00.001', 1)",
NULL
};
CHK_TEST(do_statements(stmt, statements));
return 0;
}
int tests(SQLHENV env, SQLHDBC conn) {
SQLHSTMT stmt = {0};
CHK_TEST(create_statement(env, conn, &stmt));
int r = tests_stmt(env, conn, stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
return r ? 1 : 0;
}
int test_env(void) {
SQLRETURN r;
SQLHENV env = {0};
r = SQLAllocEnv(&env);
if (r!=SQL_SUCCESS) return 1;
SQLFreeEnv(env);
return 0;
}
int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls) {
FILE *f = fopen(sqls, "rb");
if (!f) {
D("failed to open file [%s]", sqls);
return -1;
}
int r = 0;
while (!feof(f)) {
char *line = NULL;
size_t len = 0;
ssize_t n = 0;
#ifdef _MSC_VER
n = taosGetline(&line, &len, f);
#else
n = getline(&line, &len, f);
#endif
if (n==-1) break;
const char *p = NULL;
do {
if (line[0] == '#') break;
if (n>0 && line[n-1] == '\n') line[n-1]='\0';
if (n>0 && line[n-1] == '\r') line[n-1]='\0';
if (n>1 && line[n-2] == '\r') line[n-2]='\0';
p = line;
while (isspace(*p)) ++p;
if (*p==0) break;
int positive = 1;
if (strncmp(p, "N:", 2)==0) {
positive = 0;
p += 2;
} else if (strncmp(p, "P:", 2)==0) {
p += 2;
}
D("statement: [%s]", p);
r = do_statement(stmt, p);
if (positive && r==0) break;
if (!positive && r) { r = 0; break; }
if (positive) return r;
D("expecting negative result, but got positive");
return -1;
} while (0);
free(line);
if (r) break;
}
fclose(f);
return r ? 1 : 0;
}
int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) {
SQLHSTMT stmt = {0};
CHK_TEST(create_statement(env, conn, &stmt));
int r = test_sqls_in_stmt(env, conn, stmt, sqls);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
return r ? 1 : 0;
}
int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *connstr, const char *sqls) {
int r = 0;
SQLHENV env = {0};
SQLHDBC conn = {0};
if (dsn) {
CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn));
} else {
CHK_TEST(open_driver_connect(connstr, &env, &conn));
}
if (sqls) {
r = test_sqls_in_conn(env, conn, sqls);
}
SQLDisconnect(conn);
SQLFreeConnect(conn);
SQLFreeEnv(env);
return r ? 1 : 0;
}
void usage(const char *arg0) {
fprintf(stdout, "%s usage:\n", arg0);
fprintf(stdout, "%s [--dsn <dsn>] [--uid <uid>] [--pwd <pwd>] [--dcs <dcs>] [--sts <sts>]\n", arg0);
fprintf(stdout, " --dsn <dsn>: DSN\n");
fprintf(stdout, " --uid <uid>: UID\n");
fprintf(stdout, " --pwd <pwd>: PWD\n");
fprintf(stdout, " --dcs <dcs>: driver connection string\n");
fprintf(stdout, " --sts <sts>: file where statements store\n");
}
int main(int argc, char *argv[]) {
// if (argc==1) {
// CHK_TEST(test_env());
// CHK_TEST(test1("TAOS_DSN", "root", "taoxsdata"));
// D("Done!");
// return 0;
// }
const char *dsn = NULL;
const char *uid = NULL;
const char *pwd = NULL;
const char *dcs = NULL; // driver connection string
const char *sts = NULL; // statements file
for (size_t i=1; i<argc; ++i) {
const char *arg = argv[i];
if (strcmp(arg, "-h")==0) {
usage(argv[0]);
return 0;
}
if (strcmp(arg, "--dsn")==0) {
++i;
if (i>=argc) {
D("<dsn> expected but got nothing");
return 1;
}
if (dcs) {
D("--dcs has already been specified");
return 1;
}
dsn = argv[i];
continue;
}
if (strcmp(arg, "--uid")==0) {
++i;
if (i>=argc) {
D("<uid> expected but got nothing");
return 1;
}
uid = argv[i];
continue;
}
if (strcmp(arg, "--pwd")==0) {
++i;
if (i>=argc) {
D("<pwd> expected but got nothing");
return 1;
}
pwd = argv[i];
continue;
}
if (strcmp(arg, "--dcs")==0) {
++i;
if (i>=argc) {
D("<dcs> expected but got nothing");
return 1;
}
if (dsn || uid || pwd) {
D("either of --dsn/--uid/--pwd has already been specified");
return 1;
}
dcs = argv[i];
continue;
}
if (strcmp(arg, "--sts")==0) {
++i;
if (i>=argc) {
D("<sts> expected but got nothing");
return 1;
}
sts = argv[i];
continue;
}
}
CHK_TEST(test_sqls(dsn, uid, pwd, dcs, sts));
D("Done!");
return 0;
if (0) {
const char *dsn = (argc>1) ? argv[1] : NULL;
const char *uid = (argc>2) ? argv[2] : NULL;
const char *pwd = (argc>3) ? argv[3] : NULL;
const char *connstr = (argc>4) ? argv[4] : NULL;
const char *sqls = (argc>5) ? argv[5] : NULL;
dsn = NULL;
uid = NULL;
pwd = NULL;
connstr = argv[1];
sqls = argv[2];
if (0) {
CHK_TEST(test_env());
CHK_TEST(test1(dsn, uid, pwd));
const char *statements[] = {
"drop database if exists m",
"create database m",
"use m",
"drop database m",
NULL
};
CHK_TEST(test_statements(dsn, uid, pwd, statements));
if (connstr)
CHK_TEST(test_driver_connect(connstr));
if (connstr) {
SQLHENV env = {0};
SQLHDBC conn = {0};
CHK_TEST(open_driver_connect(connstr, &env, &conn));
int r = tests(env, conn);
SQLDisconnect(conn);
SQLFreeConnect(conn);
SQLFreeEnv(env);
if (r) return 1;
}
}
if ((dsn || connstr) && 1) {
CHK_TEST(test_sqls(dsn, uid, pwd, connstr, sqls));
}
D("Done!");
return 0;
}
}
import pyodbc
cnxn = pyodbc.connect('DSN=TAOS_DSN;UID=root;PWD=taosdata', autocommit=True)
# cnxn = pyodbc.connect('DSN={TAOS_DSN};UID={ root };PWD={ taosdata };HOST={ localhost:6030 }', autocommit=True)
cnxn = pyodbc.connect('DSN={TAOS_DSN}; UID=root;PWD=taosdata; HOST=localhost:6030', autocommit=True)
cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
#cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8')
#cnxn.setencoding(encoding='utf-8')
cursor = cnxn.cursor()
cursor.execute("SELECT * from db.t")
row = cursor.fetchone()
while row:
print(row)
row = cursor.fetchone()
cursor.close()
#cursor = cnxn.cursor()
#cursor.execute("SELECT * from db.t")
#row = cursor.fetchone()
#while row:
# print(row)
# row = cursor.fetchone()
#cursor.close()
#cursor = cnxn.cursor()
#cursor.execute("""
......@@ -36,32 +37,32 @@ 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(40), blob nchar(10))");
cursor.execute("create table db.mt (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')")
cursor.execute("insert into db.mt values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')")
cursor.close()
cursor = cnxn.cursor()
cursor.execute("insert into db.t values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld");
cursor.execute("insert into db.mt values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("SELECT * from db.t")
cursor.execute("SELECT * from db.mt")
row = cursor.fetchone()
while row:
print(row)
row = cursor.fetchone()
cursor.close()
cursor = cnxn.cursor()
cursor.execute("drop database if exists db");
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create database db");
cursor.close()
#cursor = cnxn.cursor()
#cursor.execute("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))");
......@@ -118,3 +119,13 @@ while row:
row = cursor.fetchone()
cursor.close()
cursor = cnxn.cursor()
cursor.execute("create table db.f (ts timestamp, v1 float)")
cursor.close()
params = [ ('2020-10-20 00:00:10', '123.3') ]
cursor = cnxn.cursor()
cursor.fast_executemany = True
cursor.executemany("insert into db.f values (?, ?)", params)
cursor.close()
P: select * from db.t;
P: select * from db.f;
P: select * from db.v;
P: select * from db.mt;
\ No newline at end of file
P:drop database if exists m;
P:create database m;
P:use m;
P:create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(1), name nchar(1));
P:insert into t (ts, b) values('2020-10-10 00:00:00', 0);
P:insert into t (ts, b) values('2020-10-10 00:00:00.001', 1);
P:insert into t (ts, b) values('2020-10-10 00:00:00.002', 10);
P:select * from t;
P:drop table t;
P:create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(1), name nchar(1));
P:insert into t (ts, v1) values('2020-10-10 00:00:00', 0);
P:insert into t (ts, v1) values('2020-10-10 00:00:00.001', 1);
P:insert into t (ts, v1) values('2020-10-10 00:00:00.002', 10);
P:select * from t;
P:drop table t;
P:create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(1), name nchar(1));
P:insert into t (ts, name) values('2020-10-10 00:00:00', 0);
P:insert into t (ts, name) values('2020-10-10 00:00:00.001', 1);
P:insert into t (ts, name) values('2020-10-10 00:00:00.002', '人');
P:insert into t (ts, name) values('2020-10-10 00:00:00.003', 'a');
P:select * from t;
P:drop table 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) values('2020-10-10 00:00:00', 0);
P:insert into t (ts, blob) values('2020-10-10 00:00:00.001', 1);
P:insert into t (ts, blob) values('2020-10-10 00:00:00.002', 'a');
P:insert into t (ts, blob) values('2020-10-10 00:00:00.003', 'b');
P:insert into t (ts, blob) values('2020-10-10 00:00:00.004', '人');
P:select * from t;
P:drop table 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));
N:insert into t (ts, blob) values('2020-10-10 00:00:00', '1234');
N:insert into t (ts, blob) values('2020-10-10 00:00:00.001', '0000');
N:insert into t (ts, blob) values('2020-10-10 00:00:00.002', '人a');
P:insert into t (ts, blob) values('2020-10-10 00:00:00.003', 'a');
P:insert into t (ts, blob) values('2020-10-10 00:00:00.004', 'b');
P:insert into t (ts, blob) values('2020-10-10 00:00:00.005', '人');
P:select * from t;
#include "../src/todbc_log.h"
#ifdef _MSC_VER
#include <winsock2.h>
#include <windows.h>
#endif
#include <iconv.h>
#include <stdio.h>
#include <string.h>
static void usage(const char *arg0);
static int do_conv(iconv_t cnv, FILE *fin, FILE *fout);
int main(int argc, char *argv[]) {
const char *from_enc = "UTF-8";
const char *to_enc = "UTF-8";
const char *dst_file = NULL;
const char *src = NULL;
#ifdef _MSC_VER
from_enc = "CP936";
to_enc = "CP936";
#endif
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (strcmp(arg, "-h") == 0) {
usage(argv[0]);
return 0;
} else if (strcmp(arg, "-f") == 0 ) {
i += 1;
if (i>=argc) {
fprintf(stderr, "expecing <from_enc>, but got nothing\n");
return 1;
}
from_enc = argv[i];
continue;
} else if (strcmp(arg, "-t") == 0 ) {
i += 1;
if (i>=argc) {
fprintf(stderr, "expecing <to_enc>, but got nothing\n");
return 1;
}
to_enc = argv[i];
continue;
} else if (strcmp(arg, "-o") == 0 ) {
i += 1;
if (i>=argc) {
fprintf(stderr, "expecing <dst_file>, but got nothing\n");
return 1;
}
dst_file = argv[i];
continue;
} else if (arg[0]=='-') {
fprintf(stderr, "unknown argument: [%s]\n", arg);
return 1;
} else {
if (src) {
fprintf(stderr, "does not allow multiple files\n");
return 1;
}
src = arg;
continue;
}
}
int r = -1;
FILE *fin = src ? fopen(src, "rb") : stdin;
FILE *fout = dst_file ? fopen(dst_file, "wb") : stdout;
iconv_t cnv = iconv_open(to_enc, from_enc);
do {
if (!fin) {
fprintf(stderr, "failed to open file [%s]\n", src);
break;
}
if (!fout) {
fprintf(stderr, "failed to open file [%s]\n", dst_file);
break;
}
#ifdef _MSC_VER
if (fout == stdout) {
r = _setmode(_fileno(fout), _O_BINARY);
if (r == -1) {
fprintf(stderr, "Cannot set binary mode for output stream: %d[%s]\n", errno, strerror(errno));
}
}
#endif
if (cnv == (iconv_t)-1) {
fprintf(stderr, "failed to open conv from [%s] to [%s]: [%s]\n", from_enc, to_enc, strerror(errno));
break;
}
r = do_conv(cnv, fin, fout);
iconv_close(cnv);
cnv = (iconv_t)-1;
} while (0);
if (fin && fin != stdin) fclose(fin);
if (fout && fout != stdout) fclose(fout);
return r ? 1 : 0;
}
static void usage(const char *arg0) {
fprintf(stderr, "%s -h | [-f <from_enc>] [-t <to_enc>] [-o <dst file>] [file]\n", arg0);
return;
}
#define IN_SIZE (64*1024)
#define OUT_SIZE (8*IN_SIZE)
static int do_conv(iconv_t cnv, FILE *fin, FILE *fout) {
int r = 0;
char src[IN_SIZE];
size_t slen = sizeof(src);
char dst[OUT_SIZE];
size_t dlen = sizeof(dst);
char *start = src;
while (!feof(fin)) {
slen = (size_t)(src + sizeof(src) - start);
size_t n = fread(start, 1, slen, fin);
if (n>0) {
char *ss = src;
size_t sl = n;
while (sl) {
char *dd = dst;
size_t dn = dlen;
size_t v = iconv(cnv, &ss, &sl, &dd, &dn);
if (v==(size_t)-1) {
int err = errno;
if (err == EILSEQ) {
fprintf(stderr, "failed to convert: [%s]\n", strerror(err));
r = -1;
break;
}
if (err == EINVAL) {
fprintf(stderr, "[%s]\n", strerror(errno));
size_t ava = (size_t)(src + sizeof(src) - ss);
memcpy(src, ss, ava);
start = ss;
} else {
fprintf(stderr, "internal logic error: [%s]\n", strerror(errno));
r = -1;
break;
}
}
n = fwrite(dst, 1, (size_t)(dd-dst), fout);
if (n<dd-dst) {
fprintf(stderr, "failed to write: [%s]\n", strerror(errno));
r = -1;
break;
}
}
if (r) break;
}
}
return r ? -1 : 0;
}
PROJECT(TDengine)
IF (TD_LINUX)
ADD_EXECUTABLE(todbcinst main.c)
TARGET_LINK_LIBRARIES(todbcinst odbc odbcinst)
ENDIF ()
IF (TD_WINDOWS_64)
ADD_EXECUTABLE(todbcinst main.c)
TARGET_LINK_LIBRARIES(todbcinst odbc32 odbccp32 user32 legacy_stdio_definitions os)
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/todbcinst.exe DESTINATION .)
ENDIF ()
#include "../src/todbc_log.h"
#ifdef _MSC_VER
#include <winsock2.h>
#include <windows.h>
#include "os.h"
#endif
#include <odbcinst.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
static void usage(const char *arg0);
static int do_install(int i, int argc, char *argv[]);
static int do_uninstall(int i, int argc, char *argv[]);
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (strcmp(arg, "-h") == 0) {
usage(argv[0]);
return 0;
} else if (strcmp(arg, "-i") == 0 ) {
i = do_install(i + 1, argc, argv);
if (i > 0) continue;
return i == 0 ? 0 : 1;
} else if (strcmp(arg, "-u") == 0 ) {
i = do_uninstall(i + 1, argc, argv);
if (i > 0) continue;
return i == 0 ? 0 : 1;
} else {
fprintf(stderr, "unknown argument: [%s]\n", arg);
return 1;
}
}
}
static void usage(const char *arg0) {
fprintf(stderr, "%s -h | -i -n [TaosDriverName] -p [TaosDriverPath] | -u [-f] -n [TaosDriverName]\n", arg0);
return;
}
static int do_install(int i, int argc, char *argv[]) {
const char* driverName = NULL;
#ifdef _MSC_VER
const char* driverFile = "todbc.dll";
#else
const char* driverFile = "libtodbc.so";
#endif
const char* driverPath = NULL;
for (; i < argc; ++i) {
const char *arg = argv[i];
if (strcmp(arg, "-n") == 0) {
i += 1;
if (i >= argc) {
fprintf(stderr, "expecting TaosDriverName, but got nothing\n");
return -1;
}
arg = argv[i];
if (strstr(arg, "TAOS") != arg) {
fprintf(stderr, "TaosDriverName shall begin with 'TAOS': [%s]\n", arg);
return -1;
}
driverName = arg;
} else if (strcmp(arg, "-p") == 0) {
i += 1;
if (i >= argc) {
fprintf(stderr, "expecting TaosDriverPath, but got nothing\n");
return -1;
}
driverPath = argv[i];
} else {
fprintf(stderr, "unknown argument: [%s]\n", arg);
return -1;
}
}
if (!driverName) {
fprintf(stderr, "TaosDriverName not specified\n");
return -1;
}
if (!driverPath) {
fprintf(stderr, "TaosDriverPath not specified\n");
return -1;
}
char buf[8192];
snprintf(buf, sizeof(buf), "%s%cDriver=%s%cFileUage=0%cConnectFunctions=YYN%c",
driverName, 0, driverFile, 0, 0, 0);
BOOL ok = 1;
DWORD usageCount = 1;
char installed[PATH_MAX + 1];
WORD len = 0;
ok = SQLInstallDriverEx(buf, driverPath, installed, sizeof(installed), &len, ODBC_INSTALL_INQUIRY, &usageCount);
if (!ok) {
fprintf(stderr, "failed to query TaosDriverName: [%s]\n", driverName);
return -1;
}
int r = 0;
#ifdef _MSC_VER
r = stricmp(driverPath, installed);
#else
r = strcasecmp(driverPath, installed);
#endif
if (r) {
fprintf(stderr, "previously installed TaosDriver [%s] has different target path [%s]\n"
"it shall be uninstalled before you can install it to different path [%s]\n",
driverName, installed, driverPath);
return -1;
}
ok = SQLInstallDriverEx(buf, driverPath, installed, sizeof(installed), &len, ODBC_INSTALL_COMPLETE, &usageCount);
if (!ok) {
fprintf(stderr, "failed to install TaosDriverName: [%s][%s]\n", driverName, driverPath);
return -1;
}
fprintf(stderr, "ODBC driver [%s] has been installed in [%s], and UsageCount is now [%d]\n",
driverName, driverPath, usageCount);
return argc;
}
static int do_uninstall(int i, int argc, char *argv[]) {
int forceful = 0;
const char* driverName = NULL;
for (; i < argc; ++i) {
const char *arg = argv[i];
if (strcmp(arg, "-f") == 0) {
forceful = 1;
} else if (strcmp(arg, "-n") == 0) {
i += 1;
if (i >= argc) {
fprintf(stderr, "expecting TaosDriverName, but got nothing\n");
return -1;
}
arg = argv[i];
if (strstr(arg, "TAOS") != arg) {
fprintf(stderr, "TaosDriverName shall begin with 'TAOS': [%s]\n", arg);
return -1;
}
driverName = arg;
} else {
fprintf(stderr, "unknown argument: [%s]\n", arg);
return -1;
}
}
if (!driverName) {
fprintf(stderr, "TaosDriverName not specified\n");
return -1;
}
BOOL ok = 1;
DWORD usageCount = 1;
do {
ok = SQLRemoveDriver(driverName, 0, &usageCount);
if (!ok) {
fprintf(stderr, "failed to remove driver [%s]\n", driverName);
return -1;
}
if (!forceful) {
fprintf(stderr, "UsageCount for ODBC driver [%s] is now: [%d]\n", driverName, usageCount);
return argc;
}
} while (usageCount > 0);
fprintf(stderr, "ODBC driver [%s] is now fully uninstalled\n", driverName);
return argc;
}
......@@ -24,9 +24,9 @@ extern "C" {
#include <stdbool.h>
#ifdef TAOS_ERROR_C
#define TAOS_DEFINE_ERROR(name, mod, code, msg) {.val = (0x80000000 | ((mod)<<16) | (code)), .str=(msg)},
#define TAOS_DEFINE_ERROR(name, mod, code, msg) {.val = (int32_t)((0x80000000 | ((mod)<<16) | (code))), .str=(msg)},
#else
#define TAOS_DEFINE_ERROR(name, mod, code, msg) static const int32_t name = (0x80000000 | ((mod)<<16) | (code));
#define TAOS_DEFINE_ERROR(name, mod, code, msg) static const int32_t name = (int32_t)((0x80000000 | ((mod)<<16) | (code)));
#endif
#define TAOS_SYSTEM_ERROR(code) (0x80ff0000 | (code))
......@@ -366,20 +366,28 @@ TAOS_DEFINE_ERROR(TSDB_CODE_HTTP_OP_VALUE_NULL, 0, 0x11A5, "value not
TAOS_DEFINE_ERROR(TSDB_CODE_HTTP_OP_VALUE_TYPE, 0, 0x11A6, "value type should be boolean, number or string")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_OOM, 0, 0x2101, "out of memory")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_OOM, 0, 0x2100, "out of memory")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_CHAR_NOT_NUM, 0, 0x2101, "convertion not a valid literal input")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_UNDEF, 0, 0x2102, "convertion undefined")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_TRUNC, 0, 0x2103, "convertion truncated")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_NOT_SUPPORT, 0, 0x2104, "convertion not supported")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_OUT_OF_RANGE, 0, 0x2105, "out of range")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_NOT_SUPPORT, 0, 0x2106, "not supported yet")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_INVALID_HANDLE, 0, 0x2107, "invalid handle")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_NO_RESULT, 0, 0x2108, "no result set")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_NO_FIELDS, 0, 0x2109, "no fields returned")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_INVALID_CURSOR, 0, 0x2110, "invalid cursor")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_STATEMENT_NOT_READY, 0, 0x2111, "statement not ready")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONNECTION_BUSY, 0, 0x2112, "connection still busy")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_BAD_CONNSTR, 0, 0x2113, "bad connection string")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_BAD_ARG, 0, 0x2114, "bad argument")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_TRUNC_FRAC, 0, 0x2103, "convertion fractional truncated")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_TRUNC, 0, 0x2104, "convertion truncated")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_NOT_SUPPORT, 0, 0x2105, "convertion not supported")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_OOR, 0, 0x2106, "convertion numeric value out of range")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_OUT_OF_RANGE, 0, 0x2107, "out of range")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_NOT_SUPPORT, 0, 0x2108, "not supported yet")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_INVALID_HANDLE, 0, 0x2109, "invalid handle")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_NO_RESULT, 0, 0x210a, "no result set")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_NO_FIELDS, 0, 0x210b, "no fields returned")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_INVALID_CURSOR, 0, 0x210c, "invalid cursor")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_STATEMENT_NOT_READY, 0, 0x210d, "statement not ready")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONNECTION_BUSY, 0, 0x210e, "connection still busy")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_BAD_CONNSTR, 0, 0x210f, "bad connection string")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_BAD_ARG, 0, 0x2110, "bad argument")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_NOT_VALID_TS, 0, 0x2111, "not a valid timestamp")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_SRC_TOO_LARGE, 0, 0x2112, "src too large")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_SRC_BAD_SEQ, 0, 0x2113, "src bad sequence")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_SRC_INCOMPLETE, 0, 0x2114, "src incomplete")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_SRC_GENERAL, 0, 0x2115, "src general")
#ifdef TAOS_ERROR_C
......
......@@ -38,14 +38,14 @@ int32_t taosGetTimestampSec();
static FORCE_INLINE int64_t taosGetTimestampMs() {
struct timeval systemTime;
gettimeofday(&systemTime, NULL);
return (int64_t)systemTime.tv_sec * 1000L + (uint64_t)systemTime.tv_usec / 1000;
return (int64_t)systemTime.tv_sec * 1000L + (int64_t)systemTime.tv_usec / 1000;
}
//@return timestamp in microsecond
static FORCE_INLINE int64_t taosGetTimestampUs() {
struct timeval systemTime;
gettimeofday(&systemTime, NULL);
return (int64_t)systemTime.tv_sec * 1000000L + (uint64_t)systemTime.tv_usec;
return (int64_t)systemTime.tv_sec * 1000000L + (int64_t)systemTime.tv_usec;
}
/*
......
......@@ -43,6 +43,7 @@
#include "msvcProcess.h"
#include "msvcDirect.h"
#include "msvcFcntl.h"
#include "msvcLibgen.h"
#include "msvcStdio.h"
#include "sys/msvcStat.h"
#include "sys/msvcTypes.h"
......
......@@ -31,7 +31,10 @@
#pragma comment(lib, "Mswsock.lib ")
#endif
#pragma warning(push)
#pragma warning(disable:4091)
#include <DbgHelp.h>
#pragma warning(pop)
static void taosGetSystemTimezone() {
// get and set default timezone
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册