diff --git a/.travis.yml b/.travis.yml index 5ce9873d1534f8aec1b8e44640cce145c1254330..eb69370418a9c83c7b8bfe5daa1d6ead19150243 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,8 @@ matrix: - python3-setuptools - valgrind - psmisc + - unixodbc + - unixodbc-dev before_script: - export TZ=Asia/Harbin diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 898b7cb032846cb85e4cc8767ed6090b35f41e1a..f619edd221c005a8d8e707afa5271072b032f74a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,4 +20,6 @@ ADD_SUBDIRECTORY(tsdb) ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(dnode) +ADD_SUBDIRECTORY(connector/odbc) ADD_SUBDIRECTORY(connector/jdbc) + diff --git a/src/client/src/tscPrepare.c b/src/client/src/tscPrepare.c index b3bb4566be10cdeab19bcd1436f59a9bae2f6e9f..1739e4348ca2d1e90e4cdc292a14c7dcc5dde2da 100644 --- a/src/client/src/tscPrepare.c +++ b/src/client/src/tscPrepare.c @@ -139,7 +139,7 @@ static int normalStmtBindParam(STscStmt* stmt, TAOS_BIND* bind) { return TSDB_CODE_TSC_INVALID_VALUE; } } - + return TSDB_CODE_SUCCESS; } @@ -213,7 +213,7 @@ static char* normalStmtBuildSql(STscStmt* stmt) { case TSDB_DATA_TYPE_NULL: taosStringBuilderAppendNull(&sb); break; - + case TSDB_DATA_TYPE_BOOL: case TSDB_DATA_TYPE_TINYINT: case TSDB_DATA_TYPE_SMALLINT: @@ -266,6 +266,388 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { return TSDB_CODE_SUCCESS; } + if (1) { + // allow user bind param data with different type + short size = 0; + union { + int8_t v1; + int16_t v2; + int32_t v4; + int64_t v8; + float f4; + double f8; + unsigned char buf[32*1024]; + } u; + switch (param->type) { + case TSDB_DATA_TYPE_BOOL: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_BOOL: { + u.v1 = *(int8_t*)bind->buffer; + if (u.v1==0 || u.v1==1) break; + } break; + case TSDB_DATA_TYPE_TINYINT: { + u.v1 = *(int8_t*)bind->buffer; + if (u.v1==0 || u.v1==1) break; + } break; + case TSDB_DATA_TYPE_SMALLINT: { + u.v1 = (int8_t)*(int16_t*)bind->buffer; + if (u.v1==0 || u.v1==1) break; + } break; + case TSDB_DATA_TYPE_INT: { + u.v1 = (int8_t)*(int32_t*)bind->buffer; + if (u.v1==0 || u.v1==1) break; + } break; + case TSDB_DATA_TYPE_BIGINT: { + u.v1 = (int8_t)*(int64_t*)bind->buffer; + if (u.v1==0 || u.v1==1) break; + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + // "0", "1" convertible + if (strncmp((const char*)bind->buffer, "0", *bind->length)==0) { + u.v1 = 0; + break; + } + if (strncmp((const char*)bind->buffer, "1", *bind->length)==0) { + u.v1 = 1; + break; + } + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + case TSDB_DATA_TYPE_TIMESTAMP: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + memcpy(data + param->offset, &u.v1, sizeof(u.v1)); + return TSDB_CODE_SUCCESS; + } break; + case TSDB_DATA_TYPE_TINYINT: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_BOOL: + case TSDB_DATA_TYPE_TINYINT: { + int8_t v = *(int8_t*)bind->buffer; + u.v1 = v; + if (v >= SCHAR_MIN && v <= SCHAR_MAX) break; + } break; + case TSDB_DATA_TYPE_SMALLINT: { + int16_t v = *(int16_t*)bind->buffer; + u.v1 = (int8_t)v; + if (v >= SCHAR_MIN && v <= SCHAR_MAX) break; + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_INT: { + int32_t v = *(int32_t*)bind->buffer; + u.v1 = (int8_t)v; + if (v >= SCHAR_MIN && v <= SCHAR_MAX) break; + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_BIGINT: { + int64_t v = *(int64_t*)bind->buffer; + u.v1 = (int8_t)v; + if (v >= SCHAR_MIN && v <= SCHAR_MAX) break; + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + int64_t v; + int n,r; + r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.v1 = (int8_t)v; + if (v >= SCHAR_MIN && v <= SCHAR_MAX) break; + } + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + case TSDB_DATA_TYPE_TIMESTAMP: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + memcpy(data + param->offset, &u.v1, sizeof(u.v1)); + return TSDB_CODE_SUCCESS; + } break; + case TSDB_DATA_TYPE_SMALLINT: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_BOOL: + case TSDB_DATA_TYPE_TINYINT: + case TSDB_DATA_TYPE_SMALLINT: { + int v = *(int16_t*)bind->buffer; + u.v2 = (int16_t)v; + } break; + case TSDB_DATA_TYPE_INT: { + int32_t v = *(int32_t*)bind->buffer; + u.v2 = (int16_t)v; + if (v >= SHRT_MIN && v <= SHRT_MAX) break; + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_BIGINT: { + int64_t v = *(int64_t*)bind->buffer; + u.v2 = (int16_t)v; + if (v >= SHRT_MIN && v <= SHRT_MAX) break; + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + int64_t v; + int n,r; + r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.v2 = (int16_t)v; + if (v >= SHRT_MIN && v <= SHRT_MAX) break; + } + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + case TSDB_DATA_TYPE_TIMESTAMP: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + memcpy(data + param->offset, &u.v2, sizeof(u.v2)); + return TSDB_CODE_SUCCESS; + } break; + case TSDB_DATA_TYPE_INT: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_BOOL: + case TSDB_DATA_TYPE_TINYINT: + case TSDB_DATA_TYPE_SMALLINT: + case TSDB_DATA_TYPE_INT: { + u.v4 = *(int32_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_BIGINT: { + int64_t v = *(int64_t*)bind->buffer; + u.v4 = (int32_t)v; + if (v >= INT_MIN && v <= INT_MAX) break; + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + int64_t v; + int n,r; + r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.v4 = (int32_t)v; + if (v >= INT_MIN && v <= INT_MAX) break; + } + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + case TSDB_DATA_TYPE_TIMESTAMP: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + memcpy(data + param->offset, &u.v2, sizeof(u.v2)); + return TSDB_CODE_SUCCESS; + } break; + case TSDB_DATA_TYPE_FLOAT: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_BOOL: + case TSDB_DATA_TYPE_TINYINT: { + u.f4 = *(int8_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_SMALLINT: { + u.f4 = *(int16_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_INT: { + u.f4 = (float)*(int32_t*)bind->buffer; + // shall we check equality? + } break; + case TSDB_DATA_TYPE_BIGINT: { + u.f4 = (float)*(int64_t*)bind->buffer; + // shall we check equality? + } break; + case TSDB_DATA_TYPE_FLOAT: { + u.f4 = *(float*)bind->buffer; + } break; + case TSDB_DATA_TYPE_DOUBLE: { + u.f4 = *(float*)bind->buffer; + // shall we check equality? + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + float v; + int n,r; + r = sscanf((const char*)bind->buffer, "%f%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.f4 = v; + break; + } + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_TIMESTAMP: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + memcpy(data + param->offset, &u.f4, sizeof(u.f4)); + return TSDB_CODE_SUCCESS; + } break; + case TSDB_DATA_TYPE_BIGINT: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_BOOL: + case TSDB_DATA_TYPE_TINYINT: { + u.v8 = *(int8_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_SMALLINT: { + u.v8 = *(int16_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_INT: { + u.v8 = *(int32_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_BIGINT: { + u.v8 = *(int64_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + int64_t v; + int n,r; + r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.v8 = v; + break; + } + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + case TSDB_DATA_TYPE_TIMESTAMP: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + memcpy(data + param->offset, &u.v8, sizeof(u.v8)); + return TSDB_CODE_SUCCESS; + } break; + case TSDB_DATA_TYPE_DOUBLE: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_BOOL: + case TSDB_DATA_TYPE_TINYINT: { + u.f8 = *(int8_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_SMALLINT: { + u.f8 = *(int16_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_INT: { + u.f8 = *(int32_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_BIGINT: { + u.f8 = (double)*(int64_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_FLOAT: { + u.f8 = *(float*)bind->buffer; + } break; + case TSDB_DATA_TYPE_DOUBLE: { + u.f8 = *(double*)bind->buffer; + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + double v; + int n,r; + r = sscanf((const char*)bind->buffer, "%lf%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.f8 = v; + break; + } + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + case TSDB_DATA_TYPE_TIMESTAMP: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } break; + memcpy(data + param->offset, &u.f8, sizeof(u.f8)); + return TSDB_CODE_SUCCESS; + } break; + case TSDB_DATA_TYPE_TIMESTAMP: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_TIMESTAMP: { + u.v8 = *(int64_t*)bind->buffer; + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + // is this the correct way to call taosParseTime? + int32_t len = (int32_t)*bind->length; + if (taosParseTime(bind->buffer, &u.v8, len, 3, tsDaylight) == TSDB_CODE_SUCCESS) { + break; + } + return TSDB_CODE_TSC_INVALID_VALUE; + } 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_FLOAT: + case TSDB_DATA_TYPE_BIGINT: + case TSDB_DATA_TYPE_DOUBLE: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } break; + memcpy(data + param->offset, &u.v8, sizeof(u.v8)); + return TSDB_CODE_SUCCESS; + } break; + case TSDB_DATA_TYPE_BINARY: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_BINARY: { + if ((*bind->length) > (uintptr_t)param->bytes) { + return TSDB_CODE_TSC_INVALID_VALUE; + } + size = (short)*bind->length; + STR_WITH_SIZE_TO_VARSTR(data + param->offset, bind->buffer, size); + return TSDB_CODE_SUCCESS; + } 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_FLOAT: + case TSDB_DATA_TYPE_BIGINT: + case TSDB_DATA_TYPE_DOUBLE: + case TSDB_DATA_TYPE_TIMESTAMP: + case TSDB_DATA_TYPE_NCHAR: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + } break; + case TSDB_DATA_TYPE_NCHAR: { + switch (bind->buffer_type) { + case TSDB_DATA_TYPE_NCHAR: { + size_t output = 0; + if (!taosMbsToUcs4(bind->buffer, *bind->length, varDataVal(data + param->offset), param->bytes - VARSTR_HEADER_SIZE, &output)) { + return TSDB_CODE_TSC_INVALID_VALUE; + } + varDataSetLen(data + param->offset, output); + return TSDB_CODE_SUCCESS; + } 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_FLOAT: + case TSDB_DATA_TYPE_BIGINT: + case TSDB_DATA_TYPE_DOUBLE: + case TSDB_DATA_TYPE_TIMESTAMP: + case TSDB_DATA_TYPE_BINARY: + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + } break; + default: { + return TSDB_CODE_TSC_INVALID_VALUE; + } break; + } + } + if (bind->buffer_type != param->type) { return TSDB_CODE_TSC_INVALID_VALUE; } @@ -299,12 +681,12 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { size = (short)*bind->length; STR_WITH_SIZE_TO_VARSTR(data + param->offset, bind->buffer, size); return TSDB_CODE_SUCCESS; - + case TSDB_DATA_TYPE_NCHAR: { size_t output = 0; if (!taosMbsToUcs4(bind->buffer, *bind->length, varDataVal(data + param->offset), param->bytes - VARSTR_HEADER_SIZE, &output)) { return TSDB_CODE_TSC_INVALID_VALUE; - } + } varDataSetLen(data + param->offset, output); return TSDB_CODE_SUCCESS; } @@ -358,7 +740,7 @@ static int insertStmtBindParam(STscStmt* stmt, TAOS_BIND* bind) { } // actual work of all data blocks is done, update block size and numOfRows. - // note we don't do this block by block during the binding process, because + // note we don't do this block by block during the binding process, because // we cannot recover if something goes wrong. pCmd->batchSize = binded * 2 + 1; @@ -405,7 +787,7 @@ static int insertStmtReset(STscStmt* pStmt) { } } pCmd->batchSize = 0; - + STableMetaInfo* pTableMetaInfo = tscGetTableMetaInfoFromCmd(pCmd, pCmd->clauseIndex, 0); pTableMetaInfo->vgroupIndex = 0; return TSDB_CODE_SUCCESS; @@ -447,7 +829,7 @@ static int insertStmtExecute(STscStmt* stmt) { pRes->numOfRows = 0; pRes->numOfTotal = 0; pRes->numOfClauseTotal = 0; - + pRes->qhandle = 0; pSql->cmd.insertType = 0; @@ -508,35 +890,35 @@ int taos_stmt_prepare(TAOS_STMT* stmt, const char* sql, unsigned long length) { SSqlObj* pSql = pStmt->pSql; size_t sqlLen = strlen(sql); - + SSqlCmd *pCmd = &pSql->cmd; SSqlRes *pRes = &pSql->res; pSql->param = (void*) pSql; pSql->fp = waitForQueryRsp; pSql->cmd.insertType = TSDB_QUERY_TYPE_STMT_INSERT; - + if (TSDB_CODE_SUCCESS != tscAllocPayload(pCmd, TSDB_DEFAULT_PAYLOAD_SIZE)) { tscError("%p failed to malloc payload buffer", pSql); return TSDB_CODE_TSC_OUT_OF_MEMORY; } - + pSql->sqlstr = realloc(pSql->sqlstr, sqlLen + 1); - + if (pSql->sqlstr == NULL) { tscError("%p failed to malloc sql string buffer", pSql); free(pCmd->payload); return TSDB_CODE_TSC_OUT_OF_MEMORY; } - + pRes->qhandle = 0; pRes->numOfRows = 1; - + strtolower(pSql->sqlstr, sql); tscDebugL("%p SQL: %s", pSql, pSql->sqlstr); - if (tscIsInsertData(pSql->sqlstr)) { + if (tscIsInsertData(pSql->sqlstr)) { pStmt->isInsert = true; - + pSql->cmd.numOfParams = 0; pSql->cmd.batchSize = 0; @@ -548,7 +930,7 @@ int taos_stmt_prepare(TAOS_STMT* stmt, const char* sql, unsigned long length) { tsem_wait(&pSql->rspSem); return pSql->res.code; } - + return code; } @@ -637,3 +1019,80 @@ TAOS_RES *taos_stmt_use_result(TAOS_STMT* stmt) { pStmt->pSql = NULL; return result; } + +int taos_stmt_is_insert(TAOS_STMT *stmt, int *insert) { + STscStmt* pStmt = (STscStmt*)stmt; + + if (stmt == NULL || pStmt->taos == NULL || pStmt->pSql == NULL) { + terrno = TSDB_CODE_TSC_DISCONNECTED; + return TSDB_CODE_TSC_DISCONNECTED; + } + + if (insert) *insert = pStmt->isInsert; + + return TSDB_CODE_SUCCESS; +} + +int taos_stmt_num_params(TAOS_STMT *stmt, int *nums) { + STscStmt* pStmt = (STscStmt*)stmt; + + if (stmt == NULL || pStmt->taos == NULL || pStmt->pSql == NULL) { + terrno = TSDB_CODE_TSC_DISCONNECTED; + return TSDB_CODE_TSC_DISCONNECTED; + } + + if (pStmt->isInsert) { + SSqlObj* pSql = pStmt->pSql; + SSqlCmd *pCmd = &pSql->cmd; + *nums = pCmd->numOfParams; + return TSDB_CODE_SUCCESS; + } else { + SNormalStmt* normal = &pStmt->normal; + *nums = normal->numParams; + return TSDB_CODE_SUCCESS; + } +} + +int taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes) { + STscStmt* pStmt = (STscStmt*)stmt; + + if (stmt == NULL || pStmt->taos == NULL || pStmt->pSql == NULL) { + terrno = TSDB_CODE_TSC_DISCONNECTED; + return TSDB_CODE_TSC_DISCONNECTED; + } + + if (pStmt->isInsert) { + SSqlObj* pSql = pStmt->pSql; + SSqlCmd *pCmd = &pSql->cmd; + STableDataBlocks* pBlock = taosArrayGetP(pCmd->pDataBlocks, 0); + + assert(pCmd->numOfParams == pBlock->numOfParams); + if (idx < 0 || idx >= pBlock->numOfParams) return -1; + + SParamInfo* param = pBlock->params + idx; + if (type) *type = param->type; + if (bytes) *bytes = param->bytes; + + return TSDB_CODE_SUCCESS; + } else { + return TSDB_CODE_TSC_APP_ERROR; + } +} + +const char *taos_data_type(int type) { + switch (type) { + case TSDB_DATA_TYPE_NULL: return "TSDB_DATA_TYPE_NULL"; + case TSDB_DATA_TYPE_BOOL: return "TSDB_DATA_TYPE_BOOL"; + case TSDB_DATA_TYPE_TINYINT: return "TSDB_DATA_TYPE_TINYINT"; + case TSDB_DATA_TYPE_SMALLINT: return "TSDB_DATA_TYPE_SMALLINT"; + case TSDB_DATA_TYPE_INT: return "TSDB_DATA_TYPE_INT"; + case TSDB_DATA_TYPE_BIGINT: return "TSDB_DATA_TYPE_BIGINT"; + case TSDB_DATA_TYPE_FLOAT: return "TSDB_DATA_TYPE_FLOAT"; + case TSDB_DATA_TYPE_DOUBLE: return "TSDB_DATA_TYPE_DOUBLE"; + case TSDB_DATA_TYPE_BINARY: return "TSDB_DATA_TYPE_BINARY"; + case TSDB_DATA_TYPE_TIMESTAMP: return "TSDB_DATA_TYPE_TIMESTAMP"; + case TSDB_DATA_TYPE_NCHAR: return "TSDB_DATA_TYPE_NCHAR"; + default: return "UNKNOWN"; + } +} + diff --git a/src/connector/odbc/CMakeLists.txt b/src/connector/odbc/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..58e7b6acf1f8424c8b8f72578a9ece027d1a9447 --- /dev/null +++ b/src/connector/odbc/CMakeLists.txt @@ -0,0 +1,33 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +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? + set(CMAKE_REQUIRED_LIBRARIES odbc) + check_symbol_exists(SQLExecute "sql.h" HAVE_ODBC_DEV) + if(NOT (HAVE_ODBC_DEV)) + unset(HAVE_ODBC_DEV CACHE) + message(WARNING "unixodbc-dev is not installed yet, you may install it 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) + endif() + ELSE () + message(WARNING "unixodbc is not installed yet, you may install it under ubuntu by typing: sudo apt install unixodbc") + ENDIF () +ENDIF () + diff --git a/src/connector/odbc/src/install.sh b/src/connector/odbc/src/install.sh new file mode 100755 index 0000000000000000000000000000000000000000..b8c04677c7199384f7bc0b66515eb04d0fe560fc --- /dev/null +++ b/src/connector/odbc/src/install.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -u + +BLD_DIR="$1" + +rm -f "${BLD_DIR}/template.ini" +rm -f "${BLD_DIR}/template.dsn" + +cat > "${BLD_DIR}/template.ini" < "${BLD_DIR}/template.dsn" < + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// #define _BSD_SOURCE +#define _XOPEN_SOURCE +#define _DEFAULT_SOURCE + +#include "taos.h" + +#include "os.h" +#include "taoserror.h" +#include "todbc_util.h" + +#include +#include + +#include + + +#define GET_REF(obj) atomic_load_64(&obj->refcount) +#define INC_REF(obj) atomic_add_fetch_64(&obj->refcount, 1) +#define DEC_REF(obj) atomic_sub_fetch_64(&obj->refcount, 1) + +#define LOCK(obj) pthread_mutex_lock(&obj->lock); +#define UNLOCK(obj) pthread_mutex_unlock(&obj->lock); + +#define SET_ERROR(obj, sqlstate, eno, err_fmt, ...) \ +do { \ + obj->err.err_no = eno; \ + const char* estr = tstrerror(eno); \ + if (!estr) estr = "Unknown error"; \ + int n = snprintf(NULL, 0, "%s: @[%d][TSDB:%x]" err_fmt "", estr, __LINE__, eno, ##__VA_ARGS__); \ + if (n<0) break; \ + char *err_str = (char*)realloc(obj->err.err_str, 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((char*)obj->err.sql_state, sizeof(obj->err.sql_state), "%s", sqlstate); \ +} while (0) + +#define CLR_ERROR(obj) \ +do { \ + obj->err.err_no = TSDB_CODE_SUCCESS; \ + if (obj->err.err_str) obj->err.err_str[0] = '\0'; \ + obj->err.sql_state[0] = '\0'; \ +} while (0) + +#define FILL_ERROR(obj) \ +do { \ + size_t n = sizeof(obj->err.sql_state); \ + if (Sqlstate) strncpy((char*)Sqlstate, (char*)obj->err.sql_state, n); \ + if (NativeError) *NativeError = obj->err.err_no; \ + snprintf((char*)MessageText, 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); \ +} while (0) + +#define FREE_ERROR(obj) \ +do { \ + obj->err.err_no = TSDB_CODE_SUCCESS; \ + if (obj->err.err_str) { \ + free(obj->err.err_str); \ + obj->err.err_str = NULL; \ + } \ + obj->err.sql_state[0] = '\0'; \ +} while (0) + +#define SET_UNSUPPORT_ERROR(obj, sqlstate, err_fmt, ...) \ +do { \ + SET_ERROR(obj, sqlstate, TSDB_CODE_ODBC_NOT_SUPPORT, err_fmt, ##__VA_ARGS__); \ +} while (0) \ + +#define SET_HANDLE_INVALID(obj, sqlstate, err_fmt, ...) \ +do { \ + SET_ERROR(obj, sqlstate, TSDB_CODE_QRY_INVALID_QHANDLE, err_fmt, ##__VA_ARGS__); \ +} while (0); + +#define SDUP(s,n) (s ? (s[n] ? (const char*)strndup((const char*)s,n) : (const char*)s) : strdup("")) +#define SFRE(x,s,n) \ +do { \ + if (x==(const char*)s) break; \ + if (x) { \ + free((char*)x); \ + x = NULL; \ + } \ +} while (0) + +#define CHK_CONN(obj) \ +do { \ + if (!obj->conn) { \ + SET_ERROR(obj, "HY000", TSDB_CODE_ODBC_INVALID_HANDLE, "connection closed or not ready"); \ + return SQL_ERROR; \ + } \ +} while (0); + +#define CHK_CONN_TAOS(obj) \ +do { \ + if (!obj->conn->taos) { \ + SET_ERROR(obj, "HY000", TSDB_CODE_ODBC_INVALID_HANDLE, "connection to data source closed or not ready"); \ + return SQL_ERROR; \ + } \ +} while (0); + +#define CHK_RS(r_091c, sql_091c, fmt_091c, ...) \ +do { \ + r_091c = SQL_ERROR; \ + int e = sql_091c->rs ? taos_errno(sql_091c->rs) : terrno; \ + if (e != TSDB_CODE_SUCCESS) { \ + SET_ERROR(sql_091c, "HY000", e, fmt_091c, ##__VA_ARGS__); \ + break; \ + } \ + r_091c = SQL_SUCCESS; \ +} while (0) + +#define PROFILING 0 + +#define PROFILE(statement) \ +do { \ + if (!PROFILING) { \ + statement; \ + break; \ + } \ + struct timeval tv0, tv1; \ + gettimeofday(&tv0, NULL); \ + statement; \ + gettimeofday(&tv1, NULL); \ + double delta = difftime(tv1.tv_sec, tv0.tv_sec); \ + delta *= 1000000; \ + delta += (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; \ + } \ +} while (0) + +typedef struct env_s env_t; +typedef struct conn_s conn_t; +typedef struct sql_s sql_t; +typedef struct taos_error_s taos_error_t; +typedef struct param_bind_s param_bind_t; + +struct param_bind_s { + SQLUSMALLINT ParameterNumber; + SQLSMALLINT ValueType; + SQLSMALLINT ParameterType; + SQLULEN LengthPrecision; + SQLSMALLINT ParameterScale; + SQLPOINTER ParameterValue; + SQLLEN *StrLen_or_Ind; + + unsigned int valid; +}; + +struct taos_error_s { + char *err_str; + int err_no; + + SQLCHAR sql_state[6]; +}; + +struct env_s { + uint64_t refcount; + unsigned int destroying:1; + + taos_error_t err; +}; + +struct conn_s { + uint64_t refcount; + env_t *env; + + TAOS *taos; + + taos_error_t err; +}; + +struct sql_s { + uint64_t refcount; + conn_t *conn; + + TAOS_STMT *stmt; + param_bind_t *params; + int n_params; + size_t rowlen; + size_t n_rows; + size_t ptr_offset; + + TAOS_RES *rs; + TAOS_ROW row; + + taos_error_t err; + unsigned int is_prepared:1; + unsigned int is_insert:1; + unsigned int is_executed:1; +}; + +typedef struct c_target_s c_target_t; +struct c_target_s { + SQLUSMALLINT col; + SQLSMALLINT ct; // c type: SQL_C_XXX + char *ptr; + SQLLEN len; + SQLLEN *soi; +}; + +static pthread_once_t init_once = PTHREAD_ONCE_INIT; +static void init_routine(void); + +// 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 SQLRETURN doSQLAllocEnv(SQLHENV *EnvironmentHandle) +{ + pthread_once(&init_once, init_routine); + + env_t *env = (env_t*)calloc(1, sizeof(*env)); + if (!env) return SQL_ERROR; + + DASSERT(INC_REF(env)>0); + + *EnvironmentHandle = env; + + CLR_ERROR(env); + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) +{ + SQLRETURN r; + r = doSQLAllocEnv(EnvironmentHandle); + return r; +} + +static SQLRETURN doSQLFreeEnv(SQLHENV EnvironmentHandle) +{ + env_t *env = (env_t*)EnvironmentHandle; + if (!env) return SQL_ERROR; + + DASSERT(GET_REF(env)==1); + + DASSERT(!env->destroying); + + env->destroying = 1; + DASSERT(env->destroying == 1); + + DASSERT(DEC_REF(env)==0); + + FREE_ERROR(env); + free(env); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) +{ + SQLRETURN r; + r = doSQLFreeEnv(EnvironmentHandle); + return r; +} + +static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle, + SQLHDBC *ConnectionHandle) +{ + env_t *env = (env_t*)EnvironmentHandle; + if (!env) return SQL_ERROR; + + DASSERT(INC_REF(env)>1); + + conn_t *conn = NULL; + do { + conn = (conn_t*)calloc(1, sizeof(*conn)); + if (!conn) { + SET_ERROR(env, "HY001", TSDB_CODE_ODBC_OOM, ""); + break; + } + + conn->env = env; + *ConnectionHandle = conn; + + DASSERT(INC_REF(conn)>0); + + return SQL_SUCCESS; + } while (0); + + DASSERT(DEC_REF(env)>0); + + return SQL_ERROR; +} + +SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, + SQLHDBC *ConnectionHandle) +{ + SQLRETURN r; + r = doSQLAllocConnect(EnvironmentHandle, ConnectionHandle); + return r; +} + +static SQLRETURN doSQLFreeConnect(SQLHDBC ConnectionHandle) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + DASSERT(GET_REF(conn)==1); + + DASSERT(conn->env); + + do { + if (conn->taos) { + taos_close(conn->taos); + conn->taos = NULL; + } + + DASSERT(DEC_REF(conn->env)>0); + DASSERT(DEC_REF(conn)==0); + + conn->env = NULL; + FREE_ERROR(conn); + free(conn); + } while (0); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) +{ + SQLRETURN r; + r = doSQLFreeConnect(ConnectionHandle); + return r; +} + +static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, SQLSMALLINT NameLength1, + SQLCHAR *UserName, SQLSMALLINT NameLength2, + SQLCHAR *Authentication, SQLSMALLINT NameLength3) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + if (conn->taos) { + SET_ERROR(conn, "08002", TSDB_CODE_ODBC_CONNECTION_BUSY, "connection still in use"); + return SQL_ERROR; + } + + const char *serverName = SDUP(ServerName, NameLength1); + const char *userName = SDUP(UserName, NameLength2); + const char *auth = SDUP(Authentication, NameLength3); + + do { + if ((ServerName && !serverName) || (UserName && !userName) || (Authentication && !auth)) { + SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, ""); + break; + } + + // TODO: data-race + // TODO: shall receive ip/port from odbc.ini + conn->taos = taos_connect("localhost", userName, auth, NULL, 0); + if (!conn->taos) { + SET_ERROR(conn, "08001", terrno, "failed to connect to data source"); + break; + } + } while (0); + + SFRE(serverName, ServerName, NameLength1); + SFRE(userName, UserName, NameLength2); + SFRE(auth, Authentication, NameLength3); + + return conn->taos ? SQL_SUCCESS : SQL_ERROR; +} + +SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, SQLSMALLINT NameLength1, + SQLCHAR *UserName, SQLSMALLINT NameLength2, + SQLCHAR *Authentication, SQLSMALLINT NameLength3) +{ + SQLRETURN r; + r = doSQLConnect(ConnectionHandle, ServerName, NameLength1, + UserName, NameLength2, + Authentication, NameLength3); + return r; +} + +static SQLRETURN doSQLDisconnect(SQLHDBC ConnectionHandle) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + if (conn->taos) { + taos_close(conn->taos); + conn->taos = NULL; + } + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) +{ + SQLRETURN r; + r = doSQLDisconnect(ConnectionHandle); + return r; +} + +static SQLRETURN doSQLAllocStmt(SQLHDBC ConnectionHandle, + SQLHSTMT *StatementHandle) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + DASSERT(INC_REF(conn)>1); + + do { + sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); + if (!sql) { + SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, ""); + break; + } + + sql->conn = conn; + DASSERT(INC_REF(sql)>0); + + *StatementHandle = sql; + + return SQL_SUCCESS; + } while (0); + + DASSERT(DEC_REF(conn)>0); + + return SQL_ERROR; +} + +SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, + SQLHSTMT *StatementHandle) +{ + SQLRETURN r; + r = doSQLAllocStmt(ConnectionHandle, StatementHandle); + return r; +} + +static SQLRETURN doSQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle) +{ + switch (HandleType) { + case SQL_HANDLE_ENV: { + SQLHENV env = {0}; + SQLRETURN r = doSQLAllocEnv(&env); + if (r==SQL_SUCCESS && OutputHandle) *OutputHandle = env; + return r; + } break; + case SQL_HANDLE_DBC: { + SQLHDBC dbc = {0}; + SQLRETURN r = doSQLAllocConnect(InputHandle, &dbc); + if (r==SQL_SUCCESS && OutputHandle) *OutputHandle = dbc; + return r; + } break; + case SQL_HANDLE_STMT: { + SQLHSTMT stmt = {0}; + SQLRETURN r = doSQLAllocStmt(InputHandle, &stmt); + if (r==SQL_SUCCESS && OutputHandle) *OutputHandle = stmt; + return r; + } break; + default: { + return SQL_ERROR; + } break; + } +} + +SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle) +{ + SQLRETURN r; + r = doSQLAllocHandle(HandleType, InputHandle, OutputHandle); + return r; +} + +static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, + SQLUSMALLINT Option) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_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; + } + + DASSERT(GET_REF(sql)==1); + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + } + + if (sql->stmt) { + taos_stmt_close(sql->stmt); + sql->stmt = NULL; + } + + if (sql->params) { + free(sql->params); + sql->params = NULL; + } + sql->n_params = 0; + + DASSERT(DEC_REF(sql->conn)>0); + DASSERT(DEC_REF(sql)==0); + + sql->conn = NULL; + + FREE_ERROR(sql); + free(sql); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, + SQLUSMALLINT Option) +{ + SQLRETURN r; + r = doSQLFreeStmt(StatementHandle, Option); + return r; +} + +static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + sql->row = NULL; + } + + if (sql->stmt) { + taos_stmt_close(sql->stmt); + sql->stmt = NULL; + } + + if (sql->params) { + free(sql->params); + sql->params = NULL; + } + sql->n_params = 0; + + const char *stxt = SDUP(StatementText, TextLength); + + SQLRETURN r = SQL_ERROR; + 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"); + } while (0); + + SFRE(stxt, StatementText, TextLength); + + return r; +} + +SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) +{ + SQLRETURN r; + r = doSQLExecDirect(StatementHandle, StatementText, TextLength); + 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); +} + +static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (sql->is_insert) { + if (ColumnCount) { + *ColumnCount = 0; + } + return SQL_SUCCESS; + } + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); + return SQL_ERROR; + } + + int fields = taos_field_count(sql->rs); + if (ColumnCount) { + *ColumnCount = fields; + } + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount) +{ + SQLRETURN r; + r = doSQLNumResultCols(StatementHandle, ColumnCount); + return r; +} + +static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle, + SQLLEN *RowCount) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (sql->is_insert) { + if (RowCount) *RowCount = 0; + return SQL_SUCCESS; + } + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); + return SQL_ERROR; + } + + int rows = taos_affected_rows(sql->rs); + if (RowCount) { + *RowCount = rows; + } + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, + SQLLEN *RowCount) +{ + SQLRETURN r; + r = doSQLRowCount(StatementHandle, RowCount); + return r; +} + +static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength, SQLLEN *NumericAttribute ) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); + return SQL_ERROR; + } + + int nfields = taos_field_count(sql->rs); + TAOS_FIELD *fields = taos_fetch_fields(sql->rs); + + if (nfields==0 || fields==NULL) { + SET_ERROR(sql, "07005", TSDB_CODE_ODBC_NO_FIELDS, ""); + return SQL_ERROR; + } + + if (ColumnNumber<=0 || ColumnNumber>nfields) { + SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber); + return SQL_ERROR; + } + + TAOS_FIELD *field = fields + ColumnNumber-1; + + switch (FieldIdentifier) { + case SQL_COLUMN_DISPLAY_SIZE: { + *NumericAttribute = do_field_display_size(field); + } break; + case SQL_COLUMN_LABEL: { + size_t n = sizeof(field->name); + strncpy(CharacterAttribute, field->name, (n>BufferLength ? BufferLength : n)); + } break; + case SQL_COLUMN_UNSIGNED: { + *NumericAttribute = SQL_FALSE; + } break; + default: { + SET_ERROR(sql, "HY091", TSDB_CODE_ODBC_OUT_OF_RANGE, + "FieldIdentifier[%d/0x%x] for Column [%d] not supported yet", + FieldIdentifier, FieldIdentifier, ColumnNumber); + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLColAttribute(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength, SQLLEN *NumericAttribute ) +{ + SQLRETURN r; + r = doSQLColAttribute(StatementHandle, ColumnNumber, FieldIdentifier, + CharacterAttribute, BufferLength, + StringLength, NumericAttribute); + return r; +} + +static SQLRETURN 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; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); + return SQL_ERROR; + } + + if (!sql->row) { + SET_ERROR(sql, "24000", TSDB_CODE_ODBC_INVALID_CURSOR, ""); + return SQL_ERROR; + } + + DASSERT(TargetValue); + + int nfields = taos_field_count(sql->rs); + TAOS_FIELD *fields = taos_fetch_fields(sql->rs); + + if (ColumnNumber<=0 || ColumnNumber>nfields) { + SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber); + return SQL_ERROR; + } + + if (TargetValue == NULL) { + SET_ERROR(sql, "HY009", TSDB_CODE_ODBC_BAD_ARG, "NULL TargetValue not allowed for col [%d]", ColumnNumber); + return SQL_ERROR; + } + + TAOS_FIELD *field = fields + ColumnNumber-1; + void *row = sql->row[ColumnNumber-1]; + + if (!row) { + if (StrLen_or_Ind) { + *StrLen_or_Ind = SQL_NULL_DATA; + } + return SQL_SUCCESS; + } + + c_target_t target = {0}; + target.col = ColumnNumber; + target.ct = TargetType; + target.ptr = TargetValue; + target.len = BufferLength; + target.soi = StrLen_or_Ind; + + switch (field->type) { + case TSDB_DATA_TYPE_BOOL: { + 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_BIGINT: { + int64_t v = *(int64_t*)row; + 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); + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, + "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", + taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); + return SQL_ERROR; + } + } + } break; + case TSDB_DATA_TYPE_FLOAT: { + float v = *(float*)row; + switch (target.ct) { + case SQL_C_FLOAT: 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); + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, + "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", + taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); + return SQL_ERROR; + } + } + } break; + case TSDB_DATA_TYPE_DOUBLE: { + double v = *(double*)row; + switch (target.ct) { + case SQL_C_DOUBLE: 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); + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, + "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", + taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); + return SQL_ERROR; + } + } + } break; + case TSDB_DATA_TYPE_TIMESTAMP: { + SQL_TIMESTAMP_STRUCT ts = {0}; + int64_t v = *(int64_t*)row; + time_t t = v/1000; + struct tm 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; + 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); + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, + "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", + taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); + return SQL_ERROR; + } + } + } break; + case TSDB_DATA_TYPE_BINARY: { + const unsigned char *bin = (const unsigned char *)row; + 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); + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, + "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", + taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); + return SQL_ERROR; + } + } + } break; + case TSDB_DATA_TYPE_NCHAR: { + const char *str = (const char *)row; + 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); + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, + "no convertion from [%s] to [%s[%d][0x%x]] for col [%d]", + taos_data_type(field->type), sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); + return SQL_ERROR; + } + } + } break; + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, + "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for col [%d]", + taos_data_type(field->type), field->type, field->type, + sql_c_type(target.ct), target.ct, target.ct, ColumnNumber); + return SQL_ERROR; + } break; + } +} + +SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind) +{ + SQLRETURN r; + r = doSQLGetData(StatementHandle, ColumnNumber, TargetType, + TargetValue, BufferLength, + StrLen_or_Ind); + return r; +} + +static SQLRETURN doSQLFetch(SQLHSTMT StatementHandle) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); + return SQL_ERROR; + } + + sql->row = taos_fetch_row(sql->rs); + return sql->row ? SQL_SUCCESS : SQL_NO_DATA; +} + +SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) +{ + SQLRETURN r; + r = doSQLFetch(StatementHandle); + return r; +} + +static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + sql->row = NULL; + } + + if (sql->stmt) { + taos_stmt_close(sql->stmt); + sql->stmt = NULL; + } + + if (sql->params) { + free(sql->params); + sql->params = NULL; + } + sql->n_params = 0; + sql->is_insert = 0; + + do { + sql->stmt = taos_stmt_init(sql->conn->taos); + if (!sql->stmt) { + SET_ERROR(sql, "HY001", terrno, "failed to initialize TAOS statement internally"); + break; + } + + int ok = 0; + do { + int r = taos_stmt_prepare(sql->stmt, (const char *)StatementText, TextLength); + if (r) { + SET_ERROR(sql, "HY000", r, "failed to prepare a TAOS statement"); + break; + } + sql->is_prepared = 1; + + int is_insert = 0; + r = taos_stmt_is_insert(sql->stmt, &is_insert); + if (r) { + SET_ERROR(sql, "HY000", r, "failed to determine if a prepared-statement is of insert"); + break; + } + sql->is_insert = is_insert ? 1 : 0; + + int params = 0; + r = taos_stmt_num_params(sql->stmt, ¶ms); + if (r) { + SET_ERROR(sql, "HY000", terrno, "fetch num of statement params failed"); + break; + } + DASSERT(params>=0); + + if (params>0) { + param_bind_t *ar = (param_bind_t*)calloc(1, params * sizeof(*ar)); + if (!ar) { + SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); + break; + } + sql->params = ar; + } + + sql->n_params = params; + + ok = 1; + } while (0); + + if (!ok) { + taos_stmt_close(sql->stmt); + sql->stmt = NULL; + sql->is_prepared = 0; + sql->is_insert = 0; + sql->is_executed = 0; + } + } while (0); + + return sql->stmt ? SQL_SUCCESS : SQL_ERROR; +} + +SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) +{ + SQLRETURN r; + r = doSQLPrepare(StatementHandle, StatementText, TextLength); + return r; +} + +static const int yes = 1; +static const int no = 0; + +static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bind_t *param, TAOS_BIND *bind) +{ + if (!param->valid) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "parameter [@%d] not bound yet", idx+1); + return SQL_ERROR; + } + + SQLPOINTER paramValue = param->ParameterValue; + SQLSMALLINT valueType = param->ValueType; + SQLLEN *soi = param->StrLen_or_Ind; + + size_t offset = idx_row * sql->rowlen + sql->ptr_offset; + + if (paramValue) paramValue += offset; + if (soi) soi = (SQLLEN*)((char*)soi + offset); + + + if (soi && *soi == SQL_NULL_DATA) { + bind->is_null = (int*)&yes; + return SQL_SUCCESS; + } + bind->is_null = (int*)&no; + int type = 0; + int bytes = 0; + if (sql->is_insert) { + int r = taos_stmt_get_param(sql->stmt, idx, &type, &bytes); + if (r) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OUT_OF_RANGE, "parameter [@%d] not valid", idx+1); + return SQL_ERROR; + } + } else { + switch (valueType) { + case SQL_C_LONG: { + type = TSDB_DATA_TYPE_INT; + } break; + case SQL_C_WCHAR: { + type = TSDB_DATA_TYPE_NCHAR; + 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: + case SQL_C_NUMERIC: + case SQL_C_GUID: + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, + "no convertion from [%s[%d/0x%x]] for parameter [%d]", + sql_c_type(valueType), valueType, valueType, + idx+1); + return SQL_ERROR; + } break; + } + } + + // ref: https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/converting-data-from-c-to-sql-data-types?view=sql-server-ver15 + switch (type) { + case TSDB_DATA_TYPE_BOOL: { + bind->buffer_type = 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)); + } 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_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); + return SQL_ERROR; + } break; + } + } break; + case TSDB_DATA_TYPE_TINYINT: { + bind->buffer_type = type; + bind->buffer_length = sizeof(bind->u.v1); + bind->buffer = &bind->u.v1; + bind->length = &bind->buffer_length; + switch (valueType) { + case SQL_C_TINYINT: { + CHK_CONV(tsdb_int64_to_tinyint(*(int8_t*)paramValue, &bind->u.v1)); + } break; + case SQL_C_SHORT: { + CHK_CONV(tsdb_int64_to_tinyint(*(int16_t*)paramValue, &bind->u.v1)); + } break; + case SQL_C_LONG: { + CHK_CONV(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)); + } 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: + 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); + return SQL_ERROR; + } break; + } + } break; + case TSDB_DATA_TYPE_SMALLINT: { + bind->buffer_type = 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)); + } break; + case SQL_C_SHORT: { + CHK_CONV(tsdb_int64_to_smallint(*(int16_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_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: + 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); + return SQL_ERROR; + } break; + } + } break; + case TSDB_DATA_TYPE_INT: { + bind->buffer_type = 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)); + } 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_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: + 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); + return SQL_ERROR; + } break; + } + } break; + case TSDB_DATA_TYPE_BIGINT: { + bind->buffer_type = 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; + } break; + case SQL_C_LONG: { + bind->u.v8 = *(int32_t*)paramValue; + } 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_BIT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: + case SQL_C_UTINYINT: + case SQL_C_UBIGINT: + case SQL_C_BINARY: + case SQL_C_DATE: + case SQL_C_TIME: + case SQL_C_TIMESTAMP: + case SQL_C_TYPE_DATE: + case SQL_C_TYPE_TIME: + case SQL_C_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); + return SQL_ERROR; + } break; + } + } break; + case TSDB_DATA_TYPE_FLOAT: { + bind->buffer_type = 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; + } 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_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: + 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); + return SQL_ERROR; + } break; + } + } break; + case TSDB_DATA_TYPE_DOUBLE: { + bind->buffer_type = type; + bind->buffer_length = sizeof(bind->u.f8); + bind->buffer = &bind->u.f8; + bind->length = &bind->buffer_length; + switch (valueType) { + 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_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: + 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); + return SQL_ERROR; + } break; + } + } break; + case TSDB_DATA_TYPE_BINARY: { + bind->buffer_type = 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.bin = utf8; + bind->buffer_length = bytes; + bind->buffer = bind->u.bin; + } 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; + } + bind->buffer = bind->u.bin; + } break; + case SQL_C_CHAR: + case SQL_C_SHORT: + case SQL_C_SSHORT: + case SQL_C_USHORT: + case SQL_C_LONG: + case SQL_C_SLONG: + case SQL_C_ULONG: + case SQL_C_FLOAT: + case SQL_C_DOUBLE: + case SQL_C_BIT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: + case SQL_C_UTINYINT: + case SQL_C_SBIGINT: + case SQL_C_UBIGINT: + case SQL_C_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); + 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; + 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; + } + } break; + case SQL_C_SBIGINT: { + int64_t t = *(int64_t*)paramValue; + bind->u.v8 = t; + } break; + case SQL_C_SHORT: + case SQL_C_SSHORT: + case SQL_C_USHORT: + case SQL_C_LONG: + case SQL_C_SLONG: + case SQL_C_ULONG: + case SQL_C_FLOAT: + case SQL_C_DOUBLE: + case SQL_C_BIT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: + case SQL_C_UTINYINT: + case SQL_C_UBIGINT: + case SQL_C_BINARY: + case SQL_C_DATE: + case SQL_C_TIME: + case SQL_C_TIMESTAMP: + case SQL_C_TYPE_DATE: + case SQL_C_TYPE_TIME: + case SQL_C_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); + return SQL_ERROR; + } break; + } + } break; + case TSDB_DATA_TYPE_NCHAR: { + bind->buffer_type = 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; + } 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; + } + bind->buffer = bind->u.nchar; + } break; + case SQL_C_SHORT: + case SQL_C_SSHORT: + case SQL_C_USHORT: + case SQL_C_LONG: + case SQL_C_SLONG: + case SQL_C_ULONG: + case SQL_C_FLOAT: + case SQL_C_DOUBLE: + case SQL_C_BIT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: + case SQL_C_UTINYINT: + case SQL_C_SBIGINT: + case SQL_C_UBIGINT: + case SQL_C_BINARY: + case SQL_C_DATE: + case SQL_C_TIME: + case SQL_C_TIMESTAMP: + case SQL_C_TYPE_DATE: + case SQL_C_TYPE_TIME: + case SQL_C_TYPE_TIMESTAMP: + 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); + return SQL_ERROR; + } break; + } + } break; + default: { + SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, + "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", + sql_c_type(valueType), valueType, valueType, + taos_data_type(type), type, type, idx+1); + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; +} + +static SQLRETURN do_bind_batch(sql_t *sql, int idx_row, TAOS_BIND *binds) +{ + for (int j=0; jn_params; ++j) { + SQLRETURN r = do_bind_param_value(sql, idx_row, j, sql->params+j, binds+j); + if (r==SQL_SUCCESS) continue; + return r; + } + if (sql->n_params > 0) { + int tr = 0; + PROFILE(tr = taos_stmt_bind_param(sql->stmt, binds)); + if (tr) { + SET_ERROR(sql, "HY000", tr, "failed to bind parameters[%d in total]", sql->n_params); + return SQL_ERROR; + } + + if (sql->is_insert) { + int r = 0; + PROFILE(r = taos_stmt_add_batch(sql->stmt)); + if (r) { + SET_ERROR(sql, "HY000", r, "failed to add batch"); + return SQL_ERROR; + } + } + } + return SQL_SUCCESS; +} + +static SQLRETURN do_execute(sql_t *sql) +{ + int tr = TSDB_CODE_SUCCESS; + if (sql->n_rows==0) sql->n_rows = 1; + for (int i=0; in_rows; ++i) { + TAOS_BIND *binds = NULL; + if (sql->n_params>0) { + binds = (TAOS_BIND*)calloc(sql->n_params, sizeof(*binds)); + if (!binds) { + SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); + return SQL_ERROR; + } + } + + SQLRETURN r = do_bind_batch(sql, i, binds); + + if (binds) { + for (int i = 0; in_params; ++i) { + TAOS_BIND *bind = binds + i; + if (bind->allocated) { + free(bind->u.nchar); + bind->u.nchar = NULL; + } + } + free(binds); + } + + if (r) return r; + } + + PROFILE(tr = taos_stmt_execute(sql->stmt)); + if (tr) { + SET_ERROR(sql, "HY000", tr, "failed to execute statement"); + return SQL_ERROR; + } + + sql->is_executed = 1; + if (sql->is_insert) return SQL_SUCCESS; + + SQLRETURN r = SQL_SUCCESS; + PROFILE(sql->rs = taos_stmt_use_result(sql->stmt)); + CHK_RS(r, sql, "failed to use result"); + + return r; +} + +static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (!sql->stmt) { + SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); + return SQL_ERROR; + } + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + sql->row = NULL; + } + + SQLRETURN r = do_execute(sql); + + return r; +} + +SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) +{ + SQLRETURN r; + PROFILE(r = doSQLExecute(StatementHandle)); + return r; +} + +static SQLRETURN doSQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, + SQLPOINTER DiagInfo, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength) +{ + // if this function is not exported, isql will never call SQLGetDiagRec + return SQL_ERROR; +} + +SQLRETURN SQL_API SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, + SQLPOINTER DiagInfo, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength) +{ + SQLRETURN r; + r = doSQLGetDiagField(HandleType, Handle, + RecNumber, DiagIdentifier, + DiagInfo, BufferLength, + StringLength); + return r; +} + +static SQLRETURN doSQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, SQLCHAR *MessageText, + SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) +{ + if (RecNumber>1) return SQL_NO_DATA; + + switch (HandleType) { + case SQL_HANDLE_ENV: { + env_t *env = (env_t*)Handle; + if (!env) break; + FILL_ERROR(env); + return SQL_SUCCESS; + } break; + case SQL_HANDLE_DBC: { + conn_t *conn = (conn_t*)Handle; + if (!conn) break; + FILL_ERROR(conn); + return SQL_SUCCESS; + } break; + case SQL_HANDLE_STMT: { + sql_t *sql = (sql_t*)Handle; + if (!sql) break; + FILL_ERROR(sql); + return SQL_SUCCESS; + } break; + default: { + } break; + } + + // how to return error? + return SQL_ERROR; +} + +SQLRETURN SQL_API SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, SQLCHAR *MessageText, + SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) +{ + SQLRETURN r; + r = doSQLGetDiagRec(HandleType, Handle, + RecNumber, Sqlstate, + NativeError, MessageText, + BufferLength, TextLength); + return r; +} + +static SQLRETURN doSQLBindParameter( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT fParamType, + SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, + SQLULEN LengthPrecision, + SQLSMALLINT ParameterScale, + SQLPOINTER ParameterValue, + SQLLEN cbValueMax, // ignore for now, since only SQL_PARAM_INPUT is supported now + SQLLEN *StrLen_or_Ind) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (!sql->stmt) { + SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); + return SQL_ERROR; + } + + if (ParameterNumber<=0 || ParameterNumber>sql->n_params) { + SET_ERROR(sql, "07009", TSDB_CODE_ODBC_BAD_ARG, + "parameter [@%d] invalid", ParameterNumber); + return SQL_ERROR; + } + + if (fParamType != SQL_PARAM_INPUT) { + SET_ERROR(sql, "HY105", TSDB_CODE_ODBC_NOT_SUPPORT, "non-input parameter [@%d] not supported yet", ParameterNumber); + return SQL_ERROR; + } + + if (ValueType == SQL_C_DEFAULT) { + SET_ERROR(sql, "HY003", TSDB_CODE_ODBC_NOT_SUPPORT, "default value for parameter [@%d] not supported yet", ParameterNumber); + return SQL_ERROR; + } + + if (!is_valid_sql_c_type(ValueType)) { + SET_ERROR(sql, "HY003", TSDB_CODE_ODBC_NOT_SUPPORT, + "SQL_C_TYPE [%s/%d/0x%x] for parameter [@%d] unknown", + sql_c_type(ValueType), ValueType, ValueType, ParameterNumber); + return SQL_ERROR; + } + + if (!is_valid_sql_sql_type(ParameterType)) { + SET_ERROR(sql, "HY004", TSDB_CODE_ODBC_NOT_SUPPORT, + "SQL_TYPE [%s/%d/0x%x] for parameter [@%d] unknown", + sql_c_type(ParameterType), ParameterType, ParameterType, ParameterNumber); + return SQL_ERROR; + } + + param_bind_t *pb = sql->params + ParameterNumber - 1; + + pb->ParameterNumber = ParameterNumber; + pb->ValueType = ValueType; + pb->ParameterType = ParameterType; + pb->LengthPrecision = LengthPrecision; + pb->ParameterScale = ParameterScale; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + + pb->valid = 1; + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLBindParameter( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT fParamType, + SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, + SQLULEN LengthPrecision, + SQLSMALLINT ParameterScale, + SQLPOINTER ParameterValue, + SQLLEN cbValueMax, // ignore for now, since only SQL_PARAM_INPUT is supported now + SQLLEN *StrLen_or_Ind) +{ + SQLRETURN r; + r = doSQLBindParameter(StatementHandle, ParameterNumber, fParamType, ValueType, ParameterType, + LengthPrecision, ParameterScale, ParameterValue, cbValueMax, StrLen_or_Ind); + return r; +} + +static SQLRETURN doSQLDriverConnect( + SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion) +{ + conn_t *conn = (conn_t*)hdbc; + if (!conn) return SQL_ERROR; + + if (fDriverCompletion!=SQL_DRIVER_NOPROMPT) { + SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "option[%d] other than SQL_DRIVER_NOPROMPT not supported yet", fDriverCompletion); + return SQL_ERROR; + } + + if (conn->taos) { + SET_ERROR(conn, "08002", TSDB_CODE_ODBC_CONNECTION_BUSY, "connection still in use"); + return SQL_ERROR; + } + + // DSN=; UID=; PWD= + + const char *connStr = SDUP(szConnStrIn, cbConnStrIn); + + char *serverName = NULL; + char *userName = NULL; + char *auth = NULL; + int bytes = 0; + + do { + if (szConnStrIn && !connStr) { + SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, ""); + break; + } + + int n = sscanf((const char*)connStr, "DSN=%m[^;]; UID=%m[^;]; PWD=%m[^;] %n", &serverName, &userName, &auth, &bytes); + if (n<1) { + SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_BAD_CONNSTR, "unrecognized connection string: [%s]", (const char*)szConnStrIn); + break; + } + + // TODO: data-race + // TODO: shall receive ip/port from odbc.ini + conn->taos = taos_connect("localhost", userName, auth, NULL, 0); + if (!conn->taos) { + SET_ERROR(conn, "HY000", terrno, "failed to connect to data source"); + break; + } + + if (szConnStrOut) { + snprintf((char*)szConnStrOut, cbConnStrOutMax, "%s", connStr); + } + if (pcbConnStrOut) { + *pcbConnStrOut = cbConnStrIn; + } + + } while (0); + + if (serverName) free(serverName); + if (userName) free(userName); + if (auth) free(auth); + + SFRE(connStr, szConnStrIn, cbConnStrIn); + + return conn->taos ? SQL_SUCCESS : SQL_ERROR; +} + +SQLRETURN SQL_API SQLDriverConnect( + SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion) +{ + SQLRETURN r; + r = doSQLDriverConnect(hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion); + return r; +} + +static SQLRETURN doSQLSetConnectAttr(SQLHDBC ConnectionHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER StringLength) +{ + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + if (Attribute != SQL_ATTR_AUTOCOMMIT) { + SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "Attribute other than SQL_ATTR_AUTOCOMMIT not supported yet"); + return SQL_ERROR; + } + if (Value != (SQLPOINTER)SQL_AUTOCOMMIT_ON) { + SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "Attribute Value other than SQL_AUTOCOMMIT_ON not supported yet[%p]", Value); + return SQL_ERROR; + } + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLSetConnectAttr(SQLHDBC ConnectionHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER StringLength) +{ + SQLRETURN r; + r = doSQLSetConnectAttr(ConnectionHandle, Attribute, Value, StringLength); + return r; +} + +static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, + SQLSMALLINT BufferLength, SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); + return SQL_ERROR; + } + + int nfields = taos_field_count(sql->rs); + TAOS_FIELD *fields = taos_fetch_fields(sql->rs); + + if (ColumnNumber<=0 || ColumnNumber>nfields) { + SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber); + return SQL_ERROR; + } + + TAOS_FIELD *field = fields + ColumnNumber - 1; + if (ColumnName) { + size_t n = sizeof(field->name); + if (n>BufferLength) n = BufferLength; + strncpy((char*)ColumnName, field->name, n); + } + if (NameLength) { + *NameLength = strnlen(field->name, sizeof(field->name)); + } + if (ColumnSize) { + *ColumnSize = field->bytes; + } + if (DecimalDigits) *DecimalDigits = 0; + + if (DataType) { + switch (field->type) { + case TSDB_DATA_TYPE_BOOL: { + *DataType = SQL_TINYINT; + } break; + + case TSDB_DATA_TYPE_TINYINT: { + *DataType = SQL_TINYINT; + } break; + + case TSDB_DATA_TYPE_SMALLINT: { + *DataType = SQL_SMALLINT; + } break; + + case TSDB_DATA_TYPE_INT: { + *DataType = SQL_INTEGER; + } break; + + case TSDB_DATA_TYPE_BIGINT: { + *DataType = SQL_BIGINT; + } break; + + case TSDB_DATA_TYPE_FLOAT: { + *DataType = SQL_FLOAT; + } break; + + case TSDB_DATA_TYPE_DOUBLE: { + *DataType = SQL_DOUBLE; + } break; + + case TSDB_DATA_TYPE_TIMESTAMP: { + // *DataType = SQL_TIMESTAMP; + // *ColumnSize = 30; + // *DecimalDigits = 3; + *DataType = SQL_TIMESTAMP; + *ColumnSize = sizeof(SQL_TIMESTAMP_STRUCT); + *DecimalDigits = 0; + } break; + + case TSDB_DATA_TYPE_NCHAR: { + *DataType = SQL_CHAR; // unicode ? + if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE; + } break; + + case TSDB_DATA_TYPE_BINARY: { + *DataType = SQL_BINARY; + if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE; + } break; + + default: + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, + "unknown [%s[%d/0x%x]]", taos_data_type(field->type), field->type, field->type); + return SQL_ERROR; + break; + } + } + if (Nullable) { + *Nullable = SQL_NULLABLE_UNKNOWN; + } + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, + SQLSMALLINT BufferLength, SQLSMALLINT *NameLength, + SQLSMALLINT *DataType, SQLULEN *ColumnSize, + SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable) +{ + SQLRETURN r; + r = doSQLDescribeCol(StatementHandle, ColumnNumber, ColumnName, + BufferLength, NameLength, + DataType, ColumnSize, + DecimalDigits, Nullable); + return r; +} + +static SQLRETURN doSQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar) +{ + sql_t *sql = (sql_t*)hstmt; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (!sql->stmt) { + SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); + return SQL_ERROR; + } + + int insert = 0; + int r = taos_stmt_is_insert(sql->stmt, &insert); + if (r) { + SET_ERROR(sql, "HY000", terrno, ""); + return SQL_ERROR; + } + // if (!insert) { + // SET_ERROR(sql, "HY000", terrno, "taos does not provide count of parameters for statement other than insert"); + // return SQL_ERROR; + // } + + int params = 0; + r = taos_stmt_num_params(sql->stmt, ¶ms); + if (r) { + SET_ERROR(sql, "HY000", terrno, "fetch num of statement params failed"); + return SQL_ERROR; + } + + if (pcpar) *pcpar = params; + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar) +{ + SQLRETURN r; + r = doSQLNumParams(hstmt, pcpar); + return r; +} + +static SQLRETURN doSQLSetStmtAttr(SQLHSTMT StatementHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER StringLength) +{ + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + CHK_CONN(sql); + CHK_CONN_TAOS(sql); + + if (!sql->stmt) { + SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); + return SQL_ERROR; + } + + if (sql->is_executed) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "change attr after executing statement not supported yet"); + return SQL_ERROR; + } + + switch (Attribute) { + case SQL_ATTR_PARAM_BIND_TYPE: { + SQLULEN val = (SQLULEN)Value; + if (val==SQL_BIND_BY_COLUMN) { + sql->rowlen = 0; + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "SQL_ATTR_PARAM_BIND_TYPE/SQL_BIND_BY_COLUMN"); + return SQL_ERROR; + } + sql->rowlen = val; + return SQL_SUCCESS; + } break; + case SQL_ATTR_PARAMSET_SIZE: { + SQLULEN val = (SQLULEN)Value; + DASSERT(val>0); + sql->n_rows = val; + return SQL_SUCCESS; + } break; + case SQL_ATTR_PARAM_BIND_OFFSET_PTR: { + if (Value) { + SQLULEN val = *(SQLULEN*)Value; + sql->ptr_offset = val; + } else { + sql->ptr_offset = 0; + } + return SQL_SUCCESS; + } break; + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "Attribute:%d", Attribute); + } break; + } + return SQL_ERROR; +} + +SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT StatementHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER StringLength) +{ + SQLRETURN r; + r = doSQLSetStmtAttr(StatementHandle, Attribute, Value, StringLength); + return r; +} + + + + +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); + } + taos_init(); +} + +static int do_field_display_size(TAOS_FIELD *field) { + switch (field->type) { + case TSDB_DATA_TYPE_TINYINT: + return 5; + break; + + case TSDB_DATA_TYPE_SMALLINT: + return 7; + break; + + case TSDB_DATA_TYPE_INT: + return 12; + break; + + case TSDB_DATA_TYPE_BIGINT: + return 22; + break; + + case TSDB_DATA_TYPE_FLOAT: { + return 12; + } break; + + case TSDB_DATA_TYPE_DOUBLE: { + return 20; + } break; + + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + return 3*(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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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(nsoi = 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 (nlen) 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; + + if (f8>INT8_MAX || f8 SQL_C_TINYINT"); + return SQL_SUCCESS_WITH_INFO; + } + + SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_TINYINT"); + return SQL_SUCCESS_WITH_INFO; +} + +static SQLRETURN conv_tsdb_str_to_c_v2(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); + + 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 SQL_C_SHORT"); + return SQL_SUCCESS_WITH_INFO; + } + + SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_SHORT"); + return SQL_SUCCESS_WITH_INFO; +} + +static SQLRETURN conv_tsdb_str_to_c_v4(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); + + 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; + + if (f8>INT32_MAX || f8 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; +} + +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); + + int64_t 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_SBIGINT"); + return SQL_SUCCESS_WITH_INFO; + } + + char buf[64]; + snprintf(buf, sizeof(buf), "%" PRId64 "", v); + + if (strcmp(buf, str)==0) return SQL_SUCCESS; + + if (f8>INT64_MAX || f8 SQL_C_SBIGINT"); + return SQL_SUCCESS_WITH_INFO; + } + + SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_SBIGINT"); + return SQL_SUCCESS_WITH_INFO; +} + +static SQLRETURN conv_tsdb_str_to_c_f4(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 = 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; + } + + 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=2) return "22003"; + + char buf[4096]; + int n = snprintf(buf, sizeof(buf), "%.*g", precision, src); + DASSERT(n>=0); + DASSERT(nSCHAR_MAX) return "22003"; + + char buf[4096]; + int n = snprintf(buf, sizeof(buf), "%.*g", precision, src); + DASSERT(n>=0); + DASSERT(nSHRT_MAX) return "22003"; + + char buf[4096]; + int n = snprintf(buf, sizeof(buf), "%.*g", precision, src); + DASSERT(n>=0); + DASSERT(nLONG_MAX) return "22003"; + + char buf[4096]; + int n = snprintf(buf, sizeof(buf), "%.*g", precision, src); + DASSERT(n>=0); + DASSERT(nLLONG_MAX) return "22003"; + + char buf[4096]; + int n = snprintf(buf, sizeof(buf), "%.*g", precision, src); + DASSERT(n>=0); + DASSERT(n=0); + DASSERT(n=0); + DASSERT(n2>=0); + DASSERT(n1=0); + if (n>=dlen) return "22001"; + + return NULL; +} + +const char* tsdb_chars_to_bit(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==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 (vSCHAR_MAX) return "22001"; + + return "22003"; + } + return "22018"; + } + + if (vSCHAR_MAX) return "22001"; + + return NULL; +} + +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"; + + + if (bytes!=strlen(src)) { + if (src[bytes-1]=='.') { + if (vSHRT_MAX) return "22001"; + + return "22003"; + } + return "22018"; + } + + if (vSHRT_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 (vLONG_MAX) return "22001"; + + return "22003"; + } + return "22018"; + } + + if (vLONG_MAX) return "22001"; + + return NULL; +} + +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"; + + + if (bytes!=strlen(src)) { + if (src[bytes-1]=='.') { + if (vLLONG_MAX) return "22001"; + + return "22003"; + } + return "22018"; + } + + if (vLLONG_MAX) return "22001"; + + return NULL; +} + +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"; + + + if (bytes!=strlen(src)) { + if (src[bytes-1]=='.') { + if (vLLONG_MAX) return "22001"; + + return "22003"; + } + return "22018"; + } + + if (vLLONG_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; +} + +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; +} + diff --git a/src/connector/odbc/src/todbc_util.c b/src/connector/odbc/src/todbc_util.c new file mode 100644 index 0000000000000000000000000000000000000000..b6b45d8120d28da31dcc1d323893984f9b93519b --- /dev/null +++ b/src/connector/odbc/src/todbc_util.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "todbc_util.h" + +#include "iconv.h" + +#include +#include +#include +#include +#include + +const char* sql_sql_type(int type) { + switch (type) { + case SQL_BIT: return "SQL_BIT"; + case SQL_TINYINT: return "SQL_TINYINT"; + case SQL_SMALLINT: return "SQL_SMALLINT"; + case SQL_INTEGER: return "SQL_INTEGER"; + case SQL_BIGINT: return "SQL_BIGINT"; + case SQL_FLOAT: return "SQL_FLOAT"; + case SQL_DOUBLE: return "SQL_DOUBLE"; + case SQL_DECIMAL: return "SQL_DECIMAL"; + case SQL_NUMERIC: return "SQL_NUMERIC"; + case SQL_REAL: return "SQL_REAL"; + case SQL_CHAR: return "SQL_CHAR"; + case SQL_VARCHAR: return "SQL_VARCHAR"; + case SQL_LONGVARCHAR: return "SQL_LONGVARCHAR"; + case SQL_WCHAR: return "SQL_WCHAR"; + case SQL_WVARCHAR: return "SQL_WVARCHAR"; + case SQL_WLONGVARCHAR: return "SQL_WLONGVARCHAR"; + case SQL_BINARY: return "SQL_BINARY"; + case SQL_VARBINARY: return "SQL_VARBINARY"; + case SQL_LONGVARBINARY: return "SQL_LONGVARBINARY"; + case SQL_DATE: return "SQL_DATE"; + case SQL_TIME: return "SQL_TIME"; + case SQL_TIMESTAMP: return "SQL_TIMESTAMP"; + case SQL_TYPE_DATE: return "SQL_TYPE_DATE"; + case SQL_TYPE_TIME: return "SQL_TYPE_TIME"; + case SQL_TYPE_TIMESTAMP: return "SQL_TYPE_TIMESTAMP"; + case SQL_INTERVAL_MONTH: return "SQL_INTERVAL_MONTH"; + case SQL_INTERVAL_YEAR: return "SQL_INTERVAL_YEAR"; + case SQL_INTERVAL_YEAR_TO_MONTH: return "SQL_INTERVAL_YEAR_TO_MONTH"; + case SQL_INTERVAL_DAY: return "SQL_INTERVAL_DAY"; + case SQL_INTERVAL_HOUR: return "SQL_INTERVAL_HOUR"; + case SQL_INTERVAL_MINUTE: return "SQL_INTERVAL_MINUTE"; + case SQL_INTERVAL_SECOND: return "SQL_INTERVAL_SECOND"; + case SQL_INTERVAL_DAY_TO_HOUR: return "SQL_INTERVAL_DAY_TO_HOUR"; + case SQL_INTERVAL_DAY_TO_MINUTE: return "SQL_INTERVAL_DAY_TO_MINUTE"; + case SQL_INTERVAL_DAY_TO_SECOND: return "SQL_INTERVAL_DAY_TO_SECOND"; + case SQL_INTERVAL_HOUR_TO_MINUTE: return "SQL_INTERVAL_HOUR_TO_MINUTE"; + case SQL_INTERVAL_HOUR_TO_SECOND: return "SQL_INTERVAL_HOUR_TO_SECOND"; + case SQL_INTERVAL_MINUTE_TO_SECOND: return "SQL_INTERVAL_MINUTE_TO_SECOND"; + case SQL_GUID: return "SQL_GUID"; + default: return "UNKNOWN"; + } +} + +const char* sql_c_type(int type) { + switch (type) { + case SQL_C_CHAR: return "SQL_C_CHAR"; + case SQL_C_WCHAR: return "SQL_C_WCHAR"; + case SQL_C_SHORT: return "SQL_C_SHORT"; + case SQL_C_SSHORT: return "SQL_C_SSHORT"; + case SQL_C_USHORT: return "SQL_C_USHORT"; + case SQL_C_LONG: return "SQL_C_LONG"; + case SQL_C_SLONG: return "SQL_C_SLONG"; + case SQL_C_ULONG: return "SQL_C_ULONG"; + case SQL_C_FLOAT: return "SQL_C_FLOAT"; + case SQL_C_DOUBLE: return "SQL_C_DOUBLE"; + case SQL_C_BIT: return "SQL_C_BIT"; + case SQL_C_TINYINT: return "SQL_C_TINYINT"; + case SQL_C_STINYINT: return "SQL_C_STINYINT"; + case SQL_C_UTINYINT: return "SQL_C_UTINYINT"; + case SQL_C_SBIGINT: return "SQL_C_SBIGINT"; + case SQL_C_UBIGINT: return "SQL_C_UBIGINT"; + case SQL_C_BINARY: return "SQL_C_BINARY"; + case SQL_C_DATE: return "SQL_C_DATE"; + case SQL_C_TIME: return "SQL_C_TIME"; + case SQL_C_TIMESTAMP: return "SQL_C_TIMESTAMP"; + case SQL_C_TYPE_DATE: return "SQL_C_TYPE_DATE"; + case SQL_C_TYPE_TIME: return "SQL_C_TYPE_TIME"; + case SQL_C_TYPE_TIMESTAMP: return "SQL_C_TYPE_TIMESTAMP"; + case SQL_C_NUMERIC: return "SQL_C_NUMERIC"; + case SQL_C_GUID: return "SQL_C_GUID"; + default: return "UNKNOWN"; + } +} + +int is_valid_sql_c_type(int type) { + const char *ctype = sql_c_type(type); + if (strcmp(ctype, "UNKNOWN")==0) return 0; + return 1; +} + +int is_valid_sql_sql_type(int type) { + const char *sqltype = sql_sql_type(type); + if (strcmp(sqltype, "UNKNOWN")==0) return 0; + 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"; + const char *tocode = "UCS-2LE"; + iconv_t conv = iconv_open(tocode, fromcode); + if (!conv) return -1; + + size_t slen = strlen(src); + char buf[4096]; + size_t dlen = sizeof(buf); + char *ps = (char*)src; + char *pd = buf; + iconv(conv, &ps, &slen, &pd, &dlen); + DASSERT(slen==0); + + 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; +} + diff --git a/src/connector/odbc/src/todbc_util.h b/src/connector/odbc/src/todbc_util.h new file mode 100644 index 0000000000000000000000000000000000000000..43264975b4e618bd495691e59fb9df59f6664e03 --- /dev/null +++ b/src/connector/odbc/src/todbc_util.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _TODBC_UTIL_H_ +#define _TODBC_UTIL_H_ + +#include +#include +#include +#include +#include + +#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) + + + +const char* sql_sql_type(int type); +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_ diff --git a/src/connector/odbc/tests/CMakeLists.txt b/src/connector/odbc/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ac57a5647fce8bd036e133936284f3f4c847d8c8 --- /dev/null +++ b/src/connector/odbc/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +PROJECT(TDengine) + +IF (TD_LINUX) + AUX_SOURCE_DIRECTORY(. SRC) + ADD_EXECUTABLE(tcodbc main.c) + TARGET_LINK_LIBRARIES(tcodbc odbc) +ENDIF () diff --git a/src/connector/odbc/tests/main.c b/src/connector/odbc/tests/main.c new file mode 100644 index 0000000000000000000000000000000000000000..1ac9b71369e8a526b051008b9379fdda4b6a5e77 --- /dev/null +++ b/src/connector/odbc/tests/main.c @@ -0,0 +1,231 @@ +#include +#include + +#include +#include + +#include "os.h" + +// static const char *dsn = "TAOS_DSN"; +// static const char *uid = "root"; +// static const char *pwd = "taosdata"; + +typedef struct data_s data_t; +struct data_s { + int64_t ts; + int8_t b; + int8_t v1; + int16_t v2; + int32_t v4; + int64_t v8; + float f4; + double f8; + char bin[40+1]; + char blob[40+1]; // why 80? ref: tests/examples/c/apitest.c +}; + +static const char *pre_stmts[] = { + "create database db", + "use db", + "create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))" +}; + +static const char *pro_stmts[] = { + // "insert into t values ('2019-07-15 00:00:00', 1)", + // "insert into t values ('2019-07-15 01:00:00', 2)", + "select * from t" + // "drop database db" +}; + +#define CHK_RESULT(r, ht, h) \ +do { \ + if (r==0) break; \ + SQLCHAR ss[10]; \ + SQLINTEGER ne = 0; \ + SQLCHAR es[4096]; \ + SQLSMALLINT n = 0; \ + ss[0] = '\0'; \ + es[0] = '\0'; \ + SQLRETURN ret = SQLGetDiagRec(ht, h, 1, ss, &ne, es, sizeof(es), &n); \ + if (ret) break; \ + fprintf(stderr, "%s%s\n", ss, es); \ +} while (0) + +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); + if (r) break; + fprintf(stderr, "execute [%s]\n", statement); + r = SQLExecute(stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + if (r) break; + fprintf(stderr, "done\n"); + } while (0); + fprintf(stderr, "r: [%x][%d]\n", r, r); + return r; +} + +static int do_insert(SQLHSTMT stmt, data_t data) { + SQLRETURN r = 0; + SQLLEN lbin; + SQLLEN lblob; + + const char *statement = "insert into t values (?, ?, ?, ?, ?, ?, ?, ?, ?,?)"; + int ignored = 0; + + do { + fprintf(stderr, "prepare [%s]\n", statement); + r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement)); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + if (r) break; + + fprintf(stderr, "execute [%s]\n", statement); + r = SQLExecute(stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + 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); + 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; + 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, strlen(dsn), + (SQLCHAR*)uid, strlen(uid), + (SQLCHAR*)pwd, strlen(pwd)); + CHK_RESULT(r, SQL_HANDLE_DBC, conn); + if (r!=SQL_SUCCESS) break; + do { + 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 ?", 4) +row = cursor.fetchone() +while row: + print(row) + row = cursor.fetchone() +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("SELECT * from db.v where v1 > ?", '5') +row = cursor.fetchone() +while row: + print(row) + row = cursor.fetchone() +cursor.close() + diff --git a/src/inc/taos.h b/src/inc/taos.h index 7e8f174b7c3737463f7e66dfdc6d5cd906791ee3..2d72b1739d1023945f2fff7c4af10d908475c7d1 100644 --- a/src/inc/taos.h +++ b/src/inc/taos.h @@ -69,6 +69,8 @@ DLL_EXPORT int taos_options(TSDB_OPTION option, const void *arg, ...); DLL_EXPORT TAOS *taos_connect(const char *ip, const char *user, const char *pass, const char *db, uint16_t port); DLL_EXPORT void taos_close(TAOS *taos); +const char *taos_data_type(int type); + typedef struct TAOS_BIND { int buffer_type; void * buffer; @@ -77,10 +79,26 @@ typedef struct TAOS_BIND { int * is_null; int is_unsigned; // unused int * error; // unused + union { + int64_t ts; + int8_t b; + int8_t v1; + int16_t v2; + int32_t v4; + int64_t v8; + float f4; + double f8; + unsigned char *bin; + char *nchar; + } u; + unsigned int allocated; } TAOS_BIND; TAOS_STMT *taos_stmt_init(TAOS *taos); int taos_stmt_prepare(TAOS_STMT *stmt, const char *sql, unsigned long length); +int taos_stmt_is_insert(TAOS_STMT *stmt, int *insert); +int taos_stmt_num_params(TAOS_STMT *stmt, int *nums); +int taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes); int taos_stmt_bind_param(TAOS_STMT *stmt, TAOS_BIND *bind); int taos_stmt_add_batch(TAOS_STMT *stmt); int taos_stmt_execute(TAOS_STMT *stmt); diff --git a/src/inc/taoserror.h b/src/inc/taoserror.h index 08621a81c6399d3ed542924af07f876d69c47ce4..bb111d2da0da75e6a3e3812ac21364f9a18fb6f3 100644 --- a/src/inc/taoserror.h +++ b/src/inc/taoserror.h @@ -28,7 +28,7 @@ extern "C" { #else #define TAOS_DEFINE_ERROR(name, mod, code, msg) static const int32_t name = (0x80000000 | ((mod)<<16) | (code)); #endif - + #define TAOS_SYSTEM_ERROR(code) (0x80ff0000 | (code)) #define TAOS_SUCCEEDED(err) ((err) >= 0) #define TAOS_FAILED(err) ((err) < 0) @@ -37,7 +37,7 @@ const char* tstrerror(int32_t err); int32_t* taosGetErrno(); #define terrno (*taosGetErrno()) - + #define TSDB_CODE_SUCCESS 0 #ifdef TAOS_ERROR_C @@ -182,7 +182,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_DND_OUT_OF_MEMORY, 0, 0x0401, "Dnode out TAOS_DEFINE_ERROR(TSDB_CODE_DND_NO_WRITE_ACCESS, 0, 0x0402, "No permission for disk files in dnode") TAOS_DEFINE_ERROR(TSDB_CODE_DND_INVALID_MSG_LEN, 0, 0x0403, "Invalid message length") -// vnode +// vnode TAOS_DEFINE_ERROR(TSDB_CODE_VND_ACTION_IN_PROGRESS, 0, 0x0500, "Action in progress") TAOS_DEFINE_ERROR(TSDB_CODE_VND_MSG_NOT_PROCESSED, 0, 0x0501, "Message not processed") TAOS_DEFINE_ERROR(TSDB_CODE_VND_ACTION_NEED_REPROCESSED, 0, 0x0502, "Action need to be reprocessed") @@ -354,6 +354,23 @@ TAOS_DEFINE_ERROR(TSDB_CODE_HTTP_OP_TAG_VALUE_TOO_LONG, 0, 0x11A4, "tag value TAOS_DEFINE_ERROR(TSDB_CODE_HTTP_OP_VALUE_NULL, 0, 0x11A5, "value not find") 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_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") + + #ifdef TAOS_ERROR_C }; #endif diff --git a/src/os/inc/osDef.h b/src/os/inc/osDef.h index 81c70a58fdf01e405d2e665b5a017a4696d76e91..d718bef6da42f59c5ee4ea7bfaad098b47062984 100644 --- a/src/os/inc/osDef.h +++ b/src/os/inc/osDef.h @@ -27,7 +27,7 @@ extern "C" { #define FD_VALID(x) ((x) > STDERR_FILENO) #define FD_INITIALIZER ((int32_t)-1) -#define WCHAR wchar_t +// #define WCHAR wchar_t #define POINTER_SHIFT(p, b) ((void *)((char *)(p) + (b))) #define POINTER_DISTANCE(p1, p2) ((char *)(p1) - (char *)(p2))