From 52f023c0282bc7127c02912ee9fa5356d3025630 Mon Sep 17 00:00:00 2001 From: freemine Date: Tue, 6 Oct 2020 22:53:15 +0800 Subject: [PATCH 01/23] init odbc --- src/CMakeLists.txt | 2 + src/connector/odbc/CMakeLists.txt | 17 + src/connector/odbc/src/odbc_install.sh | 7 + src/connector/odbc/src/template.dsn | 5 + src/connector/odbc/src/template.ini | 4 + src/connector/odbc/src/todbc.c | 426 ++++++++++++++++++++++++ src/connector/odbc/tests/CMakeLists.txt | 7 + src/connector/odbc/tests/main.c | 20 ++ src/os/inc/osDef.h | 2 +- 9 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 src/connector/odbc/CMakeLists.txt create mode 100644 src/connector/odbc/src/odbc_install.sh create mode 100644 src/connector/odbc/src/template.dsn create mode 100644 src/connector/odbc/src/template.ini create mode 100644 src/connector/odbc/src/todbc.c create mode 100644 src/connector/odbc/tests/CMakeLists.txt create mode 100644 src/connector/odbc/tests/main.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 898b7cb032..f619edd221 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/connector/odbc/CMakeLists.txt b/src/connector/odbc/CMakeLists.txt new file mode 100644 index 0000000000..32e5c7ed69 --- /dev/null +++ b/src/connector/odbc/CMakeLists.txt @@ -0,0 +1,17 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(TDengine) + +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 sudo odbcinst -i -d -f ${CMAKE_CURRENT_SOURCE_DIR}/src/template.ini + COMMAND odbcinst -i -s -f ${CMAKE_CURRENT_SOURCE_DIR}/src/template.dsn + COMMAND echo odbc install done)") + +ADD_SUBDIRECTORY(tests) + diff --git a/src/connector/odbc/src/odbc_install.sh b/src/connector/odbc/src/odbc_install.sh new file mode 100644 index 0000000000..3ed8078a19 --- /dev/null +++ b/src/connector/odbc/src/odbc_install.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +odbcinst -u -d -n TAOS && +odbcinst -i -d -f "$(dirname "$0")/template.ini" && +odbcinst -i -s -f "$(dirname "$0")/template.dsn" && +echo yes + diff --git a/src/connector/odbc/src/template.dsn b/src/connector/odbc/src/template.dsn new file mode 100644 index 0000000000..3a90205464 --- /dev/null +++ b/src/connector/odbc/src/template.dsn @@ -0,0 +1,5 @@ +[TAOS_DSN] +Description=Connection to TAOS +Driver=TAOS +Server=localhost:1234 + diff --git a/src/connector/odbc/src/template.ini b/src/connector/odbc/src/template.ini new file mode 100644 index 0000000000..6e0e23154a --- /dev/null +++ b/src/connector/odbc/src/template.ini @@ -0,0 +1,4 @@ +[TAOS] +Description = taos odbc driver +Driver = /home/xxh/Documents/TDengine/debug/build/lib/libtodbc.so + diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c new file mode 100644 index 0000000000..fc102cbcd2 --- /dev/null +++ b/src/connector/odbc/src/todbc.c @@ -0,0 +1,426 @@ +/* + * 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 "taos.h" + +#include "os.h" + +#include +#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 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); + + +typedef struct env_s env_t; +typedef struct conn_s conn_t; +typedef struct sql_s sql_t; + + + +struct env_s { + unsigned int destroying:1; +}; + +struct conn_s { + env_t *env; + + TAOS *taos; +}; + +struct sql_s { + conn_t *conn; + + TAOS_RES *rs; + TAOS_ROW row; +}; + +static pthread_once_t init_once = PTHREAD_ONCE_INIT; +static void init_routine(void); + +static int do_field_display_size(TAOS_FIELD *field); +static void do_convert(SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind, TAOS_FIELD *field, void *row); + +SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) { + pthread_once(&init_once, init_routine); + + env_t *env = (env_t*)calloc(1, sizeof(*env)); + if (!env) return SQL_ERROR; + + *EnvironmentHandle = env; + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) { + env_t *env = (env_t*)EnvironmentHandle; + if (!env) return SQL_ERROR; + + DASSERT(!env->destroying); + + env->destroying = 1; + DASSERT(env->destroying == 1); + + free(env); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, + SQLHDBC *ConnectionHandle) { + env_t *env = (env_t*)EnvironmentHandle; + if (!env) return SQL_ERROR; + + conn_t *conn = NULL; + do { + conn = (conn_t*)calloc(1, sizeof(*conn)); + if (!conn) break; + + conn->env = env; + *ConnectionHandle = conn; + } while (0); + + return conn ? SQL_SUCCESS : SQL_ERROR; +} + +SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) { + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + do { + if (conn->taos) { + taos_close(conn->taos); + conn->taos = NULL; + } + conn->env = NULL; + free(conn); + } while (0); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLConnect(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) return SQL_ERROR; + + conn->taos = taos_connect("localhost", (const char*)UserName, (const char*)Authentication, NULL, 0); + + return conn->taos ? SQL_SUCCESS : SQL_ERROR; +} + +SQLRETURN SQL_API SQLDisconnect(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 SQLAllocStmt(SQLHDBC ConnectionHandle, + SQLHSTMT *StatementHandle) { + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); + if (!sql) return SQL_ERROR; + + sql->conn = conn; + *StatementHandle = sql; + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, + SQLUSMALLINT Option) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + } + sql->conn = NULL; + free(sql); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + sql->row = NULL; + } + sql->rs = taos_query(sql->conn->taos, (const char*)StatementText); + if (sql->rs) { + sql->row = taos_fetch_row(sql->rs); + } + + return sql->row ? SQL_SUCCESS : SQL_NO_DATA; +} + +SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + + int fields = taos_field_count(sql->rs); + if (ColumnCount) { + *ColumnCount = fields; + } + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, + SQLLEN *RowCount) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + int rows = taos_affected_rows(sql->rs); + if (RowCount) { + *RowCount = rows; + } + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLColAttribute(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; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + int nfields = taos_field_count(sql->rs); + TAOS_FIELD *fields = taos_fetch_fields(sql->rs); + + if (nfields==0 || fields==NULL) return SQL_ERROR; + if (ColumnNumber>nfields) 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: { + strncpy(CharacterAttribute, field->name, field->bytes); + } break; + default: { + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + if (!sql->row) return SQL_ERROR; + int nfields = taos_field_count(sql->rs); + TAOS_FIELD *fields = taos_fetch_fields(sql->rs); + + if (nfields==0 || fields==NULL) return SQL_ERROR; + if (ColumnNumber>nfields) return SQL_ERROR; + + TAOS_FIELD *field = fields + ColumnNumber-1; + + switch (TargetType) { + case SQL_CHAR: { + do_convert(TargetValue, BufferLength, StrLen_or_Ind, field, sql->row[ColumnNumber-1]); + *StrLen_or_Ind = SQL_NTS; + } break; + default: { + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + sql->row = taos_fetch_row(sql->rs); + return sql->row ? SQL_SUCCESS : SQL_NO_DATA; +} + +SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) { + return SQL_ERROR; +} + +SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) { + return SQL_ERROR; +} + +static void init_routine(void) { + 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 12; + } 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 22; + break; + + case TSDB_DATA_TYPE_BOOL: + return 7; + default: + break; + } + + return 10; +} + +static void do_convert(SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind, TAOS_FIELD *field, void *row) { + switch (field->type) { + case TSDB_DATA_TYPE_TINYINT: + snprintf((char*)TargetValue, BufferLength, "%d", *((int8_t *)row)); + break; + + case TSDB_DATA_TYPE_SMALLINT: + snprintf((char*)TargetValue, BufferLength, "%d", *((int16_t *)row)); + break; + + case TSDB_DATA_TYPE_INT: + snprintf((char*)TargetValue, BufferLength, "%d", *((int32_t *)row)); + break; + + case TSDB_DATA_TYPE_BIGINT: + snprintf((char*)TargetValue, BufferLength, "%" PRId64, *((int64_t *)row)); + break; + + case TSDB_DATA_TYPE_FLOAT: { + float fv = 0; + fv = GET_FLOAT_VAL(row); + snprintf((char*)TargetValue, BufferLength, "%f", fv); + } break; + + case TSDB_DATA_TYPE_DOUBLE: { + double dv = 0; + dv = GET_DOUBLE_VAL(row); + snprintf((char*)TargetValue, BufferLength, "%lf", dv); + } break; + + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + size_t xlen = 0; + char *p = (char*)TargetValue; + size_t n = BufferLength; + for (xlen = 0; xlen < field->bytes - VARSTR_HEADER_SIZE; xlen++) { + char c = ((char *)row)[xlen]; + if (c == 0) break; + int v = snprintf(p, n, "%c", c); + p += v; + n -= v; + if (n<=0) break; + } + if (n>0) *p = '\0'; + ((char*)TargetValue)[BufferLength-1] = '\0'; + } break; + + case TSDB_DATA_TYPE_TIMESTAMP: + snprintf((char*)TargetValue, BufferLength, "%" PRId64, *((int64_t *)row)); + break; + + case TSDB_DATA_TYPE_BOOL: + snprintf((char*)TargetValue, BufferLength, "%d", *((int8_t *)row)); + default: + break; + } +} + diff --git a/src/connector/odbc/tests/CMakeLists.txt b/src/connector/odbc/tests/CMakeLists.txt new file mode 100644 index 0000000000..ac57a5647f --- /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 0000000000..3924fe9077 --- /dev/null +++ b/src/connector/odbc/tests/main.c @@ -0,0 +1,20 @@ +#include + +#include + + +int main(void) { + SQLRETURN r; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + r = SQLAllocEnv(&env); + if (r!=SQL_SUCCESS) return 1; + do { + r = SQLAllocConnect(env, &conn); + if (r!=SQL_SUCCESS) break; + SQLFreeConnect(conn); + } while (0); + SQLFreeEnv(env); + return r ? 1 : 0; +} + diff --git a/src/os/inc/osDef.h b/src/os/inc/osDef.h index 81c70a58fd..d718bef6da 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)) From ec04ef7b79a87e6d0a5f33ea8a3dac1fb5188e1e Mon Sep 17 00:00:00 2001 From: freemine Date: Wed, 7 Oct 2020 08:08:34 +0800 Subject: [PATCH 02/23] delay fetch --- src/connector/odbc/src/todbc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index fc102cbcd2..fdacb2128f 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -201,11 +201,8 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, sql->row = NULL; } sql->rs = taos_query(sql->conn->taos, (const char*)StatementText); - if (sql->rs) { - sql->row = taos_fetch_row(sql->rs); - } - return sql->row ? SQL_SUCCESS : SQL_NO_DATA; + return sql->rs ? SQL_SUCCESS : SQL_NO_DATA; } SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, From 36e5dac0eba732328ec9d8a223cabb0abf162bd9 Mon Sep 17 00:00:00 2001 From: freemine Date: Wed, 7 Oct 2020 08:38:07 +0800 Subject: [PATCH 03/23] add refcount --- src/connector/odbc/src/todbc.c | 57 +++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index fdacb2128f..dea549f5e5 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -53,16 +53,19 @@ typedef struct sql_s sql_t; struct env_s { + uint64_t refcount; unsigned int destroying:1; }; struct conn_s { + uint64_t refcount; env_t *env; TAOS *taos; }; struct sql_s { + uint64_t refcount; conn_t *conn; TAOS_RES *rs; @@ -81,6 +84,8 @@ SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) { env_t *env = (env_t*)calloc(1, sizeof(*env)); if (!env) return SQL_ERROR; + DASSERT(INC_REF(env)>0); + *EnvironmentHandle = env; return SQL_SUCCESS; @@ -90,11 +95,15 @@ SQLRETURN SQL_API SQLFreeEnv(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(env); return SQL_SUCCESS; @@ -105,6 +114,8 @@ SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, 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)); @@ -112,20 +123,34 @@ SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, conn->env = env; *ConnectionHandle = conn; + + DASSERT(INC_REF(conn)>0); + + return SQL_SUCCESS; } while (0); - return conn ? SQL_SUCCESS : SQL_ERROR; + DASSERT(DEC_REF(env)>0); + + return SQL_ERROR; } SQLRETURN SQL_API SQLFreeConnect(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(conn); } while (0); @@ -142,8 +167,9 @@ SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, if (conn->taos) return SQL_ERROR; + // TODO: data-race conn->taos = taos_connect("localhost", (const char*)UserName, (const char*)Authentication, NULL, 0); - + return conn->taos ? SQL_SUCCESS : SQL_ERROR; } @@ -164,13 +190,23 @@ SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, conn_t *conn = (conn_t*)ConnectionHandle; if (!conn) return SQL_ERROR; - sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); - if (!sql) return SQL_ERROR; + DASSERT(INC_REF(conn)>1); - sql->conn = conn; - *StatementHandle = sql; + do { + sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); + if (!sql) break; - return SQL_SUCCESS; + 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 SQLFreeStmt(SQLHSTMT StatementHandle, @@ -178,11 +214,18 @@ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; + DASSERT(GET_REF(sql)==1); + if (sql->rs) { taos_free_result(sql->rs); sql->rs = NULL; } + + DASSERT(DEC_REF(sql->conn)>0); + DASSERT(DEC_REF(sql)==0); + sql->conn = NULL; + free(sql); return SQL_SUCCESS; From dc2df3a09e6e62e5839036158be842988d67b565 Mon Sep 17 00:00:00 2001 From: freemine Date: Wed, 7 Oct 2020 17:42:54 +0800 Subject: [PATCH 04/23] add Error support --- src/CMakeLists.txt | 2 +- src/connector/odbc/src/todbc.c | 242 +++++++++++++++++++++++++++++++-- 2 files changed, 235 insertions(+), 9 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f619edd221..2ed56d4f81 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,5 +21,5 @@ ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(dnode) ADD_SUBDIRECTORY(connector/odbc) -ADD_SUBDIRECTORY(connector/jdbc) +# ADD_SUBDIRECTORY(connector/jdbc) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index dea549f5e5..4b2c767f51 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -16,6 +16,7 @@ #include "taos.h" #include "os.h" +#include "taoserror.h" #include #include @@ -45,16 +46,73 @@ do { \ #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, "[%x]%s: " err_fmt "", eno, estr, ##__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, "[%x]%s: " err_fmt "", eno, estr, ##__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); \ +} 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 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) 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; +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 { @@ -62,14 +120,19 @@ struct conn_s { env_t *env; TAOS *taos; + + taos_error_t err; }; struct sql_s { uint64_t refcount; conn_t *conn; + TAOS_STMT *stmt; TAOS_RES *rs; TAOS_ROW row; + + taos_error_t err; }; static pthread_once_t init_once = PTHREAD_ONCE_INIT; @@ -88,6 +151,7 @@ SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) { *EnvironmentHandle = env; + CLR_ERROR(env); return SQL_SUCCESS; } @@ -104,6 +168,7 @@ SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) { DASSERT(DEC_REF(env)==0); + FREE_ERROR(env); free(env); return SQL_SUCCESS; @@ -119,7 +184,10 @@ SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, conn_t *conn = NULL; do { conn = (conn_t*)calloc(1, sizeof(*conn)); - if (!conn) break; + if (!conn) { + SET_ERROR(env, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to alloc connection handle"); + break; + } conn->env = env; *ConnectionHandle = conn; @@ -152,6 +220,7 @@ SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) { DASSERT(DEC_REF(conn)==0); conn->env = NULL; + FREE_ERROR(conn); free(conn); } while (0); @@ -161,14 +230,36 @@ SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) { SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, SQLCHAR *ServerName, SQLSMALLINT NameLength1, SQLCHAR *UserName, SQLSMALLINT NameLength2, - SQLCHAR *Authentication, SQLSMALLINT NameLength3) { + SQLCHAR *Authentication, SQLSMALLINT NameLength3) { conn_t *conn = (conn_t*)ConnectionHandle; if (!conn) return SQL_ERROR; - if (conn->taos) return SQL_ERROR; + if (conn->taos) { + SET_ERROR(conn, "HY000", TSDB_CODE_TSC_APP_ERROR, "connection still in use"); + return SQL_ERROR; + } - // TODO: data-race - conn->taos = taos_connect("localhost", (const char*)UserName, (const char*)Authentication, NULL, 0); + const char *serverName = SDUP(ServerName, NameLength1); + const char *userName = SDUP(UserName, NameLength2); + const char *auth = SDUP(Authentication, NameLength3); + + do { + if (!serverName || !userName || !auth) { + SET_ERROR(conn, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to connect to database"); + 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 database"); + break; + } + } while (0); + + SFRE(serverName, ServerName, NameLength1); + SFRE(userName, UserName, NameLength2); + SFRE(auth, Authentication, NameLength3); return conn->taos ? SQL_SUCCESS : SQL_ERROR; } @@ -194,7 +285,10 @@ SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, do { sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); - if (!sql) break; + if (!sql) { + SET_ERROR(conn, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to alloc statement handle"); + break; + } sql->conn = conn; DASSERT(INC_REF(sql)>0); @@ -214,6 +308,12 @@ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; + if (Option != SQL_DROP) { + D("Option: [%d][%x]", Option, Option); + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "failed to free statement"); + return SQL_ERROR; + } + DASSERT(GET_REF(sql)==1); if (sql->rs) { @@ -221,11 +321,17 @@ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, sql->rs = NULL; } + if (sql->stmt) { + taos_stmt_close(sql->stmt); + sql->stmt = NULL; + } + DASSERT(DEC_REF(sql->conn)>0); DASSERT(DEC_REF(sql)==0); sql->conn = NULL; + FREE_ERROR(sql); free(sql); return SQL_SUCCESS; @@ -243,7 +349,27 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, sql->rs = NULL; sql->row = NULL; } - sql->rs = taos_query(sql->conn->taos, (const char*)StatementText); + + if (sql->stmt) { + taos_stmt_close(sql->stmt); + sql->stmt = NULL; + } + + const char *stxt = SDUP(StatementText, TextLength); + + do { + if (!stxt) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to query"); + break; + } + sql->rs = taos_query(sql->conn->taos, stxt); + if (!sql->rs) { + SET_ERROR(sql, "HY000", terrno, "failed to query"); + break; + } + } while (0); + + SFRE(stxt, StatementText, TextLength); return sql->rs ? SQL_SUCCESS : SQL_NO_DATA; } @@ -351,13 +477,113 @@ SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) { SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { - return SQL_ERROR; + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + + 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; + } + + do { + sql->stmt = taos_stmt_init(sql->conn->taos); + if (!sql->stmt) { + SET_ERROR(sql, "HY000", terrno, "failed to initialize statement internally"); + break; + } + + int r = taos_stmt_prepare(sql->stmt, (const char *)StatementText, TextLength); + if (r) { + SET_ERROR(sql, "HY000", r, "failed to prepare a statement"); + taos_stmt_close(sql->stmt); + sql->stmt = NULL; + break; + } + } while (0); + + return sql->stmt ? SQL_SUCCESS : SQL_ERROR; } SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->stmt) return SQL_ERROR; + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + sql->row = NULL; + } + + int r = 0; + + r = taos_stmt_execute(sql->stmt); + if (r) { + SET_ERROR(sql, "HY000", r, "failed to execute statement"); + return SQL_ERROR; + } + + sql->rs = taos_stmt_use_result(sql->stmt); + if (!sql->rs) { + SET_ERROR(sql, "HY000", r, "failed to fetch result"); + return SQL_ERROR; + } + + return sql->rs ? SQL_SUCCESS : SQL_ERROR; +} + +SQLRETURN SQL_API SQLGetDiagField(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 SQLGetDiagRec(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; + } + + return SQL_ERROR; +} + + + + static void init_routine(void) { taos_init(); } From 12eafdd8f31fa807a918d871721cf186afe0fa15 Mon Sep 17 00:00:00 2001 From: freemine Date: Thu, 8 Oct 2020 12:11:20 +0800 Subject: [PATCH 05/23] add prepare/bind/execute support --- src/connector/odbc/src/todbc.c | 608 ++++++++++++++++++++++++++++++-- src/connector/odbc/tests/main.c | 183 +++++++++- 2 files changed, 764 insertions(+), 27 deletions(-) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 4b2c767f51..58659a2346 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -46,18 +46,18 @@ do { \ #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, "[%x]%s: " err_fmt "", eno, estr, ##__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, "[%x]%s: " err_fmt "", eno, estr, ##__VA_ARGS__); \ - snprintf((char*)obj->err.sql_state, sizeof(obj->err.sql_state), "%s", sqlstate); \ +#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, "@[%d][%x]%s: " err_fmt "", __LINE__, eno, estr, ##__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, "@[%d][%x]%s: " err_fmt "", __LINE__, eno, estr, ##__VA_ARGS__); \ + snprintf((char*)obj->err.sql_state, sizeof(obj->err.sql_state), "%s", sqlstate); \ } while (0) #define CLR_ERROR(obj) \ @@ -96,10 +96,29 @@ do { \ } \ } 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) + 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; + SQLPOINTER ParameterValue; + SQLLEN *StrLen_or_Ind; + unsigned int valid; +}; struct taos_error_s { char *err_str; @@ -129,6 +148,9 @@ struct sql_s { conn_t *conn; TAOS_STMT *stmt; + TAOS_BIND *binds; + param_bind_t *params; + int n_params; TAOS_RES *rs; TAOS_ROW row; @@ -309,7 +331,6 @@ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, if (!sql) return SQL_ERROR; if (Option != SQL_DROP) { - D("Option: [%d][%x]", Option, Option); SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "failed to free statement"); return SQL_ERROR; } @@ -326,6 +347,17 @@ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, sql->stmt = NULL; } + if (sql->binds) { + free(sql->binds); + sql->binds = 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); @@ -355,23 +387,32 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, sql->stmt = NULL; } + if (sql->binds) { + free(sql->binds); + sql->binds = 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, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to query"); break; } sql->rs = taos_query(sql->conn->taos, stxt); - if (!sql->rs) { - SET_ERROR(sql, "HY000", terrno, "failed to query"); - break; - } + CHK_RS(r, sql, "failed to query"); } while (0); SFRE(stxt, StatementText, TextLength); - return sql->rs ? SQL_SUCCESS : SQL_NO_DATA; + return r; } SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, @@ -455,8 +496,12 @@ SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, switch (TargetType) { case SQL_CHAR: { - do_convert(TargetValue, BufferLength, StrLen_or_Ind, field, sql->row[ColumnNumber-1]); - *StrLen_or_Ind = SQL_NTS; + if (sql->row[ColumnNumber-1]) { + do_convert(TargetValue, BufferLength, StrLen_or_Ind, field, sql->row[ColumnNumber-1]); + *StrLen_or_Ind = SQL_NTS; + } else { + *StrLen_or_Ind = SQL_NULL_DATA; + } } break; default: { return SQL_ERROR; @@ -493,6 +538,16 @@ SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, sql->stmt = NULL; } + if (sql->binds) { + free(sql->binds); + sql->binds = NULL; + } + if (sql->params) { + free(sql->params); + sql->params = NULL; + } + sql->n_params = 0; + do { sql->stmt = taos_stmt_init(sql->conn->taos); if (!sql->stmt) { @@ -527,19 +582,83 @@ SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) { int r = 0; + for (int i=0; in_params; ++i) { + param_bind_t *pb = sql->params + i; + if (!pb->valid) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "default parameter [@%d] not supported yet", i+1); + return SQL_ERROR; + } + TAOS_BIND *b = sql->binds + i; + int yes = 1; + int no = 0; + if (pb->StrLen_or_Ind && *pb->StrLen_or_Ind == SQL_NULL_DATA) { + b->is_null = &yes; + } else { + b->is_null = &no; + switch (b->buffer_type) { + case TSDB_DATA_TYPE_BOOL: + case TSDB_DATA_TYPE_TINYINT: + case TSDB_DATA_TYPE_SMALLINT: + case TSDB_DATA_TYPE_INT: + case TSDB_DATA_TYPE_BIGINT: + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + case TSDB_DATA_TYPE_TIMESTAMP: { + b->length = &b->buffer_length; + b->buffer = pb->ParameterValue; + } break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + if (!pb->StrLen_or_Ind) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "value [@%d] bad StrLen_or_Ind", i+1); + return SQL_ERROR; + } + size_t n = *pb->StrLen_or_Ind; + if (n == SQL_NTS) { + n = strlen(pb->ParameterValue); + } else if (n < 0 || n > b->buffer_length) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "value [@%d] bad StrLen_or_Ind", i+1); + return SQL_ERROR; + } + + b->buffer_length = n; + b->length = &b->buffer_length; + b->buffer = pb->ParameterValue; + } break; + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "value [@%d] not supported yet", i+1); + return SQL_ERROR; + } break; + } + } + } + + if (sql->n_params > 0) { + r = taos_stmt_bind_param(sql->stmt, sql->binds); + if (r) { + SET_ERROR(sql, "HY000", r, "failed to bind parameters"); + return SQL_ERROR; + } + + r = taos_stmt_add_batch(sql->stmt); + if (r) { + SET_ERROR(sql, "HY000", r, "failed to add batch"); + return SQL_ERROR; + } + } + r = taos_stmt_execute(sql->stmt); if (r) { SET_ERROR(sql, "HY000", r, "failed to execute statement"); return SQL_ERROR; } - sql->rs = taos_stmt_use_result(sql->stmt); - if (!sql->rs) { - SET_ERROR(sql, "HY000", r, "failed to fetch result"); - return SQL_ERROR; - } + SQLRETURN ret = SQL_ERROR; - return sql->rs ? SQL_SUCCESS : SQL_ERROR; + sql->rs = taos_stmt_use_result(sql->stmt); + CHK_RS(ret, sql, "failed to use result"); + + return ret; } SQLRETURN SQL_API SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, @@ -581,6 +700,443 @@ SQLRETURN SQL_API SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, return SQL_ERROR; } +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) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->stmt) return SQL_ERROR; + + if (fParamType != SQL_PARAM_INPUT) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "non-input parameter [@%d] not supported yet", ParameterNumber); + return SQL_ERROR; + } + switch (ParameterType) { + case SQL_BIT: { // TSDB_DATA_TYPE_BOOL + if (ValueType!=SQL_C_BIT) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + // LengthPrecision ignored; + // ParameterScale ignored; + // if (LengthPrecision != sizeof(v.b)) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + // return SQL_ERROR; + // } + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_BOOL; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_TINYINT: { // TSDB_DATA_TYPE_TINYINT + if (ValueType!=SQL_C_TINYINT) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + // LengthPrecision ignored; + // ParameterScale ignored; + // if (LengthPrecision != sizeof(v.v1)) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + // return SQL_ERROR; + // } + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_TINYINT; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_SMALLINT: { // TSDB_DATA_TYPE_SMALLINT + if (ValueType!=SQL_C_SHORT) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + // LengthPrecision ignored; + // ParameterScale ignored; + // if (LengthPrecision != sizeof(v.v2)) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + // return SQL_ERROR; + // } + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_SMALLINT; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_INTEGER: { // TSDB_DATA_TYPE_INT + if (ValueType!=SQL_C_LONG) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + // LengthPrecision ignored; + // ParameterScale ignored; + // if (LengthPrecision != sizeof(v.v4)) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + // return SQL_ERROR; + // } + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_INT; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_BIGINT: { // TSDB_DATA_TYPE_BIGINT + if (ValueType!=SQL_C_SBIGINT) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + // LengthPrecision ignored; + // ParameterScale ignored; + // if (LengthPrecision != sizeof(v.v8)) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + // return SQL_ERROR; + // } + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_BIGINT; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_FLOAT: { // TSDB_DATA_TYPE_FLOAT + if (ValueType!=SQL_C_FLOAT) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + // LengthPrecision ignored; + // ParameterScale ignored; + // if (LengthPrecision != sizeof(v.f4)) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + // return SQL_ERROR; + // } + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_FLOAT; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_DOUBLE: { // TSDB_DATA_TYPE_DOUBLE + if (ValueType!=SQL_C_DOUBLE) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + // LengthPrecision ignored; + // ParameterScale ignored; + // if (LengthPrecision != sizeof(v.f8)) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + // return SQL_ERROR; + // } + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_DOUBLE; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_TIMESTAMP: { // TSDB_DATA_TYPE_TIMESTAMP + if (ValueType!=SQL_C_SBIGINT) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + // LengthPrecision ignored; + // ParameterScale ignored; + // if (LengthPrecision != sizeof(v.v8)) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + // return SQL_ERROR; + // } + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_VARBINARY: { // TSDB_DATA_TYPE_BINARY + if (ValueType!=SQL_C_BINARY) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + if (LengthPrecision <=0) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + return SQL_ERROR; + } + // ParameterScale ignored; + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_BINARY; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + case SQL_VARCHAR: { // TSDB_DATA_TYPE_NCHAR + if (ValueType!=SQL_C_CHAR) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); + return SQL_ERROR; + } + if (LengthPrecision <=0) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); + return SQL_ERROR; + } + // ParameterScale ignored; + // if (ParameterScale != 0) { + // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); + // return SQL_ERROR; + // } + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + b->buffer_type = TSDB_DATA_TYPE_NCHAR; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + pb->ParameterValue = ParameterValue; + pb->StrLen_or_Ind = StrLen_or_Ind; + pb->valid = 1; + } break; + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "xdoes not support parameter type[%x]", ParameterType); + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; +} diff --git a/src/connector/odbc/tests/main.c b/src/connector/odbc/tests/main.c index 3924fe9077..1680e22620 100644 --- a/src/connector/odbc/tests/main.c +++ b/src/connector/odbc/tests/main.c @@ -1,9 +1,134 @@ #include +#include #include +#include +#include "os.h" -int main(void) { +// 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" +}; + +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)); + if (r) break; + 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; +} + +static int do_insert(SQLHSTMT stmt, data_t data) { + SQLRETURN r = 0; + SQLLEN lbin; + SQLLEN lblob; + + const char *statement = "insert into t values (?, ?, ?, ?, ?, ?, ?, ?, ?,?)"; + int ignore = 0; + + do { + fprintf(stderr, "prepare [%s]\n", statement); + r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement)); + if (r) break; + + fprintf(stderr, "bind 1 [%s]\n", statement); + r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_TIMESTAMP, ignore, ignore, &data.ts, ignore, NULL); + if (r) break; + + fprintf(stderr, "bind 2 [%s]\n", statement); + r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, ignore, ignore, &data.b, ignore, NULL); + if (r) break; + + fprintf(stderr, "bind 3 [%s]\n", statement); + r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, ignore, ignore, &data.v1, ignore, NULL); + if (r) break; + + fprintf(stderr, "bind 4 [%s]\n", statement); + r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, ignore, ignore, &data.v2, ignore, NULL); + if (r) break; + + fprintf(stderr, "bind 5 [%s]\n", statement); + r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, ignore, ignore, &data.v4, ignore, NULL); + if (r) break; + + fprintf(stderr, "bind 6 [%s]\n", statement); + r = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, ignore, ignore, &data.v8, ignore, NULL); + if (r) break; + + fprintf(stderr, "bind 7 [%s]\n", statement); + r = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, ignore, ignore, &data.f4, ignore, NULL); + if (r) break; + + fprintf(stderr, "bind 8 [%s]\n", statement); + SQLLEN l8 = SQL_NULL_DATA; + r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignore, ignore, &data.f8, ignore, &l8); + 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, ignore, &data.bin, ignore, &lbin); + 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, ignore, &data.blob, ignore, &lblob); + if (r) break; + + fprintf(stderr, "execute [%s]\n", statement); + r = SQLExecute(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}; @@ -12,6 +137,62 @@ int main(void) { do { r = SQLAllocConnect(env, &conn); if (r!=SQL_SUCCESS) break; + do { + r = SQLConnect(conn, (SQLCHAR*)dsn, strlen(dsn), + (SQLCHAR*)uid, strlen(uid), + (SQLCHAR*)pwd, strlen(pwd)); + 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 Date: Thu, 8 Oct 2020 12:19:33 +0800 Subject: [PATCH 06/23] remove obsoletes, ignore->ignored for better understanding --- src/connector/odbc/src/todbc.c | 72 --------------------------------- src/connector/odbc/tests/main.c | 22 +++++----- 2 files changed, 11 insertions(+), 83 deletions(-) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 58659a2346..1a14d4b3b1 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -729,14 +729,6 @@ SQLRETURN SQL_API SQLBindParameter( } // LengthPrecision ignored; // ParameterScale ignored; - // if (LengthPrecision != sizeof(v.b)) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - // return SQL_ERROR; - // } - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -770,14 +762,6 @@ SQLRETURN SQL_API SQLBindParameter( } // LengthPrecision ignored; // ParameterScale ignored; - // if (LengthPrecision != sizeof(v.v1)) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - // return SQL_ERROR; - // } - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -811,14 +795,6 @@ SQLRETURN SQL_API SQLBindParameter( } // LengthPrecision ignored; // ParameterScale ignored; - // if (LengthPrecision != sizeof(v.v2)) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - // return SQL_ERROR; - // } - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -852,14 +828,6 @@ SQLRETURN SQL_API SQLBindParameter( } // LengthPrecision ignored; // ParameterScale ignored; - // if (LengthPrecision != sizeof(v.v4)) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - // return SQL_ERROR; - // } - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -893,14 +861,6 @@ SQLRETURN SQL_API SQLBindParameter( } // LengthPrecision ignored; // ParameterScale ignored; - // if (LengthPrecision != sizeof(v.v8)) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - // return SQL_ERROR; - // } - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -934,14 +894,6 @@ SQLRETURN SQL_API SQLBindParameter( } // LengthPrecision ignored; // ParameterScale ignored; - // if (LengthPrecision != sizeof(v.f4)) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - // return SQL_ERROR; - // } - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -975,14 +927,6 @@ SQLRETURN SQL_API SQLBindParameter( } // LengthPrecision ignored; // ParameterScale ignored; - // if (LengthPrecision != sizeof(v.f8)) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - // return SQL_ERROR; - // } - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -1016,14 +960,6 @@ SQLRETURN SQL_API SQLBindParameter( } // LengthPrecision ignored; // ParameterScale ignored; - // if (LengthPrecision != sizeof(v.v8)) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - // return SQL_ERROR; - // } - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -1060,10 +996,6 @@ SQLRETURN SQL_API SQLBindParameter( return SQL_ERROR; } // ParameterScale ignored; - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { @@ -1100,10 +1032,6 @@ SQLRETURN SQL_API SQLBindParameter( return SQL_ERROR; } // ParameterScale ignored; - // if (ParameterScale != 0) { - // SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] scale not supported yet", ParameterNumber); - // return SQL_ERROR; - // } param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); if (!ar || !binds) { diff --git a/src/connector/odbc/tests/main.c b/src/connector/odbc/tests/main.c index 1680e22620..980194f27a 100644 --- a/src/connector/odbc/tests/main.c +++ b/src/connector/odbc/tests/main.c @@ -58,7 +58,7 @@ static int do_insert(SQLHSTMT stmt, data_t data) { SQLLEN lblob; const char *statement = "insert into t values (?, ?, ?, ?, ?, ?, ?, ?, ?,?)"; - int ignore = 0; + int ignored = 0; do { fprintf(stderr, "prepare [%s]\n", statement); @@ -66,46 +66,46 @@ static int do_insert(SQLHSTMT stmt, data_t data) { if (r) break; fprintf(stderr, "bind 1 [%s]\n", statement); - r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_TIMESTAMP, ignore, ignore, &data.ts, ignore, NULL); + r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_TIMESTAMP, ignored, ignored, &data.ts, ignored, NULL); if (r) break; fprintf(stderr, "bind 2 [%s]\n", statement); - r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, ignore, ignore, &data.b, ignore, NULL); + r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, ignored, ignored, &data.b, ignored, NULL); if (r) break; fprintf(stderr, "bind 3 [%s]\n", statement); - r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, ignore, ignore, &data.v1, ignore, NULL); + r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, ignored, ignored, &data.v1, ignored, NULL); if (r) break; fprintf(stderr, "bind 4 [%s]\n", statement); - r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, ignore, ignore, &data.v2, ignore, NULL); + r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, ignored, ignored, &data.v2, ignored, NULL); if (r) break; fprintf(stderr, "bind 5 [%s]\n", statement); - r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, ignore, ignore, &data.v4, ignore, NULL); + r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, ignored, ignored, &data.v4, ignored, NULL); if (r) break; fprintf(stderr, "bind 6 [%s]\n", statement); - r = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, ignore, ignore, &data.v8, ignore, NULL); + r = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, ignored, ignored, &data.v8, ignored, NULL); if (r) break; fprintf(stderr, "bind 7 [%s]\n", statement); - r = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, ignore, ignore, &data.f4, ignore, NULL); + r = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, ignored, ignored, &data.f4, ignored, NULL); if (r) break; fprintf(stderr, "bind 8 [%s]\n", statement); SQLLEN l8 = SQL_NULL_DATA; - r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignore, ignore, &data.f8, ignore, &l8); + r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignored, ignored, &data.f8, ignored, &l8); 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, ignore, &data.bin, ignore, &lbin); + r = SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, sizeof(data.bin)-1, ignored, &data.bin, ignored, &lbin); 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, ignore, &data.blob, ignore, &lblob); + r = SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(data.blob)-1, ignored, &data.blob, ignored, &lblob); if (r) break; fprintf(stderr, "execute [%s]\n", statement); From ac16f2031427f2dac7d44a661f420dcc5881019a Mon Sep 17 00:00:00 2001 From: freemine Date: Thu, 8 Oct 2020 15:47:55 +0800 Subject: [PATCH 07/23] 1. adjust CMakeLists.txt to enabling checking unixodbc existence 2. use install.sh to install odbcinst/odbc record in system 3. add packages (unixodbc/unixodbc-dev) to install in .travis.yml --- .travis.yml | 2 ++ src/CMakeLists.txt | 2 +- src/connector/odbc/CMakeLists.txt | 36 +++++++++++++++++++++-------- src/connector/odbc/src/install.sh | 25 ++++++++++++++++++++ src/connector/odbc/src/template.dsn | 5 ---- src/connector/odbc/src/template.ini | 4 ---- 6 files changed, 54 insertions(+), 20 deletions(-) create mode 100755 src/connector/odbc/src/install.sh delete mode 100644 src/connector/odbc/src/template.dsn delete mode 100644 src/connector/odbc/src/template.ini diff --git a/.travis.yml b/.travis.yml index 6e49709c85..3256710434 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 2ed56d4f81..f619edd221 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,5 +21,5 @@ ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(dnode) ADD_SUBDIRECTORY(connector/odbc) -# ADD_SUBDIRECTORY(connector/jdbc) +ADD_SUBDIRECTORY(connector/jdbc) diff --git a/src/connector/odbc/CMakeLists.txt b/src/connector/odbc/CMakeLists.txt index 32e5c7ed69..393a1bd50b 100644 --- a/src/connector/odbc/CMakeLists.txt +++ b/src/connector/odbc/CMakeLists.txt @@ -1,17 +1,33 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(TDengine) -AUX_SOURCE_DIRECTORY(src SRC) +IF (TD_LINUX_64) + find_program(HAVE_ODBCINST NAMES odbcinst) -# 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) + 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) -install(CODE "execute_process(COMMAND sudo odbcinst -i -d -f ${CMAKE_CURRENT_SOURCE_DIR}/src/template.ini - COMMAND odbcinst -i -s -f ${CMAKE_CURRENT_SOURCE_DIR}/src/template.dsn - COMMAND echo odbc install done)") + # 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) -ADD_SUBDIRECTORY(tests) + 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-dev") + ENDIF () +ENDIF () diff --git a/src/connector/odbc/src/install.sh b/src/connector/odbc/src/install.sh new file mode 100755 index 0000000000..b8c04677c7 --- /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" < Date: Thu, 8 Oct 2020 16:01:05 +0800 Subject: [PATCH 08/23] remove trailing spaces --- src/connector/odbc/src/todbc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 1a14d4b3b1..dda79f209c 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -180,7 +180,7 @@ SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) { SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) { env_t *env = (env_t*)EnvironmentHandle; if (!env) return SQL_ERROR; - + DASSERT(GET_REF(env)==1); DASSERT(!env->destroying); @@ -255,7 +255,7 @@ SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, SQLCHAR *Authentication, SQLSMALLINT NameLength3) { conn_t *conn = (conn_t*)ConnectionHandle; if (!conn) return SQL_ERROR; - + if (conn->taos) { SET_ERROR(conn, "HY000", TSDB_CODE_TSC_APP_ERROR, "connection still in use"); return SQL_ERROR; From 75786385a2e8a8f7b74d5f261405d5de22fe03be Mon Sep 17 00:00:00 2001 From: freemine Date: Thu, 8 Oct 2020 17:05:07 +0800 Subject: [PATCH 09/23] just add one more blank line to check if travis's happy with that, ref #add odbc connector #3765 --- src/connector/odbc/src/todbc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index dda79f209c..64de488f8b 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -26,6 +26,7 @@ #include #include + #define D(fmt, ...) \ fprintf(stderr, \ "%s[%d]:%s() " fmt "\n", \ From 045175682a1efe89f1401ec4843289a1e4a0194a Mon Sep 17 00:00:00 2001 From: freemine Date: Thu, 8 Oct 2020 22:36:13 +0800 Subject: [PATCH 10/23] 1. typo correction in CMakeLists.txt 2. fprint error message when SQLxxx failed in tcodbc --- src/connector/odbc/CMakeLists.txt | 2 +- src/connector/odbc/src/odbc_install.sh | 7 ------ src/connector/odbc/src/todbc.c | 24 ++++++++++----------- src/connector/odbc/tests/main.c | 30 ++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 20 deletions(-) delete mode 100644 src/connector/odbc/src/odbc_install.sh diff --git a/src/connector/odbc/CMakeLists.txt b/src/connector/odbc/CMakeLists.txt index 393a1bd50b..58e7b6acf1 100644 --- a/src/connector/odbc/CMakeLists.txt +++ b/src/connector/odbc/CMakeLists.txt @@ -27,7 +27,7 @@ IF (TD_LINUX_64) ADD_SUBDIRECTORY(tests) endif() ELSE () - message(WARNING "unixodbc is not installed yet, you may install it under ubuntu by typing: sudo apt install unixodbc-dev") + 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/odbc_install.sh b/src/connector/odbc/src/odbc_install.sh deleted file mode 100644 index 3ed8078a19..0000000000 --- a/src/connector/odbc/src/odbc_install.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -odbcinst -u -d -n TAOS && -odbcinst -i -d -f "$(dirname "$0")/template.ini" && -odbcinst -i -s -f "$(dirname "$0")/template.dsn" && -echo yes - diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 64de488f8b..2c4c0f90bf 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -47,18 +47,18 @@ do { \ #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, "@[%d][%x]%s: " err_fmt "", __LINE__, eno, estr, ##__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, "@[%d][%x]%s: " err_fmt "", __LINE__, eno, estr, ##__VA_ARGS__); \ - snprintf((char*)obj->err.sql_state, sizeof(obj->err.sql_state), "%s", sqlstate); \ +#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) \ diff --git a/src/connector/odbc/tests/main.c b/src/connector/odbc/tests/main.c index 980194f27a..aa4ce7a05a 100644 --- a/src/connector/odbc/tests/main.c +++ b/src/connector/odbc/tests/main.c @@ -37,14 +37,30 @@ static const char *pro_stmts[] = { // "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); @@ -63,53 +79,65 @@ static int do_insert(SQLHSTMT stmt, data_t data) { 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); SQLLEN l8 = SQL_NULL_DATA; r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignored, ignored, &data.f8, ignored, &l8); + 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; @@ -136,11 +164,13 @@ int main(int argc, char *argv[]) { 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}; From f1f5c0c4796ec51a57b90a376fca7c429f7311ae Mon Sep 17 00:00:00 2001 From: freemine Date: Sat, 10 Oct 2020 22:12:15 +0800 Subject: [PATCH 11/23] 1. add SQLDriverConnect to let ODBC-aware software to communicate with such as pyodbc 2. SQLDescribeCol 3. more DATA-TYPE-convertions in SQLGetData --- src/CMakeLists.txt | 2 +- src/connector/odbc/src/todbc.c | 2278 +++++++++++++++++++++++++------ src/connector/odbc/tests/main.c | 4 +- src/inc/taoserror.h | 12 +- 4 files changed, 1860 insertions(+), 436 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f619edd221..2ed56d4f81 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,5 +21,5 @@ ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(dnode) ADD_SUBDIRECTORY(connector/odbc) -ADD_SUBDIRECTORY(connector/jdbc) +# ADD_SUBDIRECTORY(connector/jdbc) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 2c4c0f90bf..6eaaba762f 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -25,6 +25,7 @@ #include #include #include +#include #define D(fmt, ...) \ @@ -87,6 +88,16 @@ do { \ obj->err.sql_state[0] = '\0'; \ } while (0) +#define SET_UNSUPPORT_ERROR(obj, sqlstate, err_fmt, ...) \ +do { \ + SET_ERROR(obj, sqlstate, TSDB_CODE_COM_OPS_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 { \ @@ -108,6 +119,25 @@ do { \ 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) + typedef struct env_s env_t; typedef struct conn_s conn_t; typedef struct sql_s sql_t; @@ -116,8 +146,13 @@ 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; + SQLLEN *StrLen_or_Ind; + unsigned int valid; }; @@ -158,13 +193,23 @@ struct sql_s { taos_error_t err; }; +typedef struct c_target_s c_target_t; +struct c_target_s { + SQLUSMALLINT col; + SQLSMALLINT ct; // c type: SQL_C_XXX + char *ptr; + SQLLEN len; + SQLLEN *soi; +}; + static pthread_once_t init_once = PTHREAD_ONCE_INIT; static void init_routine(void); static int do_field_display_size(TAOS_FIELD *field); static void do_convert(SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind, TAOS_FIELD *field, void *row); -SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) { +static SQLRETURN doSQLAllocEnv(SQLHENV *EnvironmentHandle) +{ pthread_once(&init_once, init_routine); env_t *env = (env_t*)calloc(1, sizeof(*env)); @@ -178,7 +223,15 @@ SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) { return SQL_SUCCESS; } -SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) { +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; @@ -197,8 +250,16 @@ SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) { return SQL_SUCCESS; } -SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, - SQLHDBC *ConnectionHandle) { +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; @@ -208,7 +269,7 @@ SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, do { conn = (conn_t*)calloc(1, sizeof(*conn)); if (!conn) { - SET_ERROR(env, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to alloc connection handle"); + SET_ERROR(env, "HY000", TSDB_CODE_ODBC_OOM, "failed to alloc connection handle"); break; } @@ -225,7 +286,16 @@ SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, return SQL_ERROR; } -SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) { +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; @@ -250,10 +320,18 @@ SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) { return SQL_SUCCESS; } -SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, +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) { + SQLCHAR *Authentication, SQLSMALLINT NameLength3) +{ conn_t *conn = (conn_t*)ConnectionHandle; if (!conn) return SQL_ERROR; @@ -267,15 +345,16 @@ SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, const char *auth = SDUP(Authentication, NameLength3); do { - if (!serverName || !userName || !auth) { - SET_ERROR(conn, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to connect to database"); + if ((ServerName && !serverName) || (UserName && !userName) || (Authentication && !auth)) { + SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources"); 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 database"); + SET_ERROR(conn, "HY000", terrno, "failed to connect to data source"); break; } } while (0); @@ -287,7 +366,20 @@ SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, return conn->taos ? SQL_SUCCESS : SQL_ERROR; } -SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) { +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; @@ -299,8 +391,16 @@ SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) { return SQL_SUCCESS; } -SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, - SQLHSTMT *StatementHandle) { +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; @@ -309,7 +409,7 @@ SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, do { sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); if (!sql) { - SET_ERROR(conn, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to alloc statement handle"); + SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_OOM, "failed to alloc statement handle"); break; } @@ -326,13 +426,23 @@ SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, return SQL_ERROR; } -SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, - SQLUSMALLINT Option) { +SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, + SQLHSTMT *StatementHandle) +{ + SQLRETURN r; + r = doSQLAllocStmt(ConnectionHandle, StatementHandle); + 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_COM_OPS_NOT_SUPPORT, "failed to free statement"); + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "free statement with Option[%x] not supported yet", Option); return SQL_ERROR; } @@ -370,12 +480,29 @@ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, return SQL_SUCCESS; } -SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, - SQLCHAR *StatementText, SQLINTEGER TextLength) { +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; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } if (sql->rs) { taos_free_result(sql->rs); @@ -404,7 +531,7 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, SQLRETURN r = SQL_ERROR; do { if (!stxt) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to query"); + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources"); break; } sql->rs = taos_query(sql->conn->taos, stxt); @@ -416,13 +543,34 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, return r; } -SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, - SQLSMALLINT *ColumnCount) { +SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) +{ + SQLRETURN r; + r = doSQLExecDirect(StatementHandle, StatementText, TextLength); + return r; +} + +static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount) +{ sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; - if (!sql->rs) return SQL_ERROR; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + return SQL_ERROR; + } int fields = taos_field_count(sql->rs); if (ColumnCount) { @@ -432,13 +580,35 @@ SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, return SQL_SUCCESS; } -SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, - SQLLEN *RowCount) { +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; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; - if (!sql->rs) return SQL_ERROR; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + return SQL_ERROR; + } + int rows = taos_affected_rows(sql->rs); if (RowCount) { *RowCount = rows; @@ -446,20 +616,60 @@ SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, return SQL_SUCCESS; } -SQLRETURN SQL_API SQLColAttribute(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, - SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, - SQLSMALLINT *StringLength, SQLLEN *NumericAttribute ) { +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 ) +{ + D("......"); sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; - if (!sql->rs) return SQL_ERROR; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + return SQL_ERROR; + } + int nfields = taos_field_count(sql->rs); TAOS_FIELD *fields = taos_fetch_fields(sql->rs); - if (nfields==0 || fields==NULL) return SQL_ERROR; - if (ColumnNumber>nfields) return SQL_ERROR; + if (nfields==0 || fields==NULL) { + SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "no fields in result set"); + return SQL_ERROR; + } + + if (ColumnNumber<0) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] underflow", ColumnNumber); + return SQL_ERROR; + } + + if (ColumnNumber==0) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "ColumnNumber[0] not supported"); + return SQL_ERROR; + } + + if (ColumnNumber>nfields) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] overflow", ColumnNumber); + return SQL_ERROR; + } TAOS_FIELD *field = fields + ColumnNumber-1; @@ -468,65 +678,454 @@ SQLRETURN SQL_API SQLColAttribute(SQLHSTMT StatementHandle, *NumericAttribute = do_field_display_size(field); } break; case SQL_COLUMN_LABEL: { - strncpy(CharacterAttribute, field->name, field->bytes); + 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, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, + "ColumnNumber[%d] FieldIdentifier[%d] not supported yet", + ColumnNumber, FieldIdentifier); return SQL_ERROR; } break; } return SQL_SUCCESS; } -SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, +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, TIMESTAMP_STRUCT *ts); +static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, TIMESTAMP_STRUCT *ts); +static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, TIMESTAMP_STRUCT *ts); +static SQLRETURN conv_tsdb_ts_to_c_ts(sql_t *sql, c_target_t *target, TAOS_FIELD *field, 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) { + SQLLEN *StrLen_or_Ind) +{ sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; - if (!sql->rs) return SQL_ERROR; - if (!sql->row) return SQL_ERROR; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + return SQL_ERROR; + } + + if (!sql->row) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no rows cached or not ready"); + return SQL_ERROR; + } + + DASSERT(TargetValue); + int nfields = taos_field_count(sql->rs); TAOS_FIELD *fields = taos_fetch_fields(sql->rs); - if (nfields==0 || fields==NULL) return SQL_ERROR; - if (ColumnNumber>nfields) return SQL_ERROR; + if (nfields==0 || fields==NULL) { + SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "no fields in result set"); + return SQL_ERROR; + } + + if (ColumnNumber<0) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] underflow", ColumnNumber); + return SQL_ERROR; + } + + if (ColumnNumber==0) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "ColumnNumber[0] not supported"); + return SQL_ERROR; + } + + if (ColumnNumber>nfields) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] overflow", ColumnNumber); + return SQL_ERROR; + } TAOS_FIELD *field = fields + ColumnNumber-1; + void *row = sql->row[ColumnNumber-1]; - switch (TargetType) { - case SQL_CHAR: { - if (sql->row[ColumnNumber-1]) { - do_convert(TargetValue, BufferLength, StrLen_or_Ind, field, sql->row[ColumnNumber-1]); - *StrLen_or_Ind = SQL_NTS; - } else { - *StrLen_or_Ind = SQL_NULL_DATA; + 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; + + SQLRETURN r = SQL_ERROR; + + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + return SQL_ERROR; + } + } + } break; + case TSDB_DATA_TYPE_TIMESTAMP: { + 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; + 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_TIMESTAMP: return conv_tsdb_ts_to_c_ts(sql, &target, field, &ts); + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + return SQL_ERROR; + } } } break; default: { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "field [@%d] type [%d] not supported yet", ColumnNumber, field->type); return SQL_ERROR; } break; } + + if (1) return r; + + switch (TargetType) { + case SQL_C_CHAR: { + do_convert(TargetValue, BufferLength, StrLen_or_Ind, field, row); + } break; + case SQL_C_TIMESTAMP: { + TIMESTAMP_STRUCT ts = {0}; + DASSERT(BufferLength == sizeof(ts)); + 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 = 0; + + memcpy(TargetValue, &ts, sizeof(ts)); + } break; + case SQL_C_LONG: { + int32_t v = *(int32_t*)row; + DASSERT(BufferLength == sizeof(v)); + memcpy(TargetValue, &v, sizeof(v)); + } break; + case SQL_C_SBIGINT: { + int64_t v = *(int64_t*)row; + DASSERT(BufferLength == sizeof(v)); + memcpy(TargetValue, &v, sizeof(v)); + } break; + case SQL_C_FLOAT: { + float v = *(float*)row; + DASSERT(BufferLength == sizeof(v)); + memcpy(TargetValue, &v, sizeof(v)); + } break; + case SQL_C_DOUBLE: { + double v = *(double*)row; + DASSERT(BufferLength == sizeof(v)); + memcpy(TargetValue, &v, sizeof(v)); + } break; + case SQL_C_BINARY: { + if (StrLen_or_Ind) { + if (field->type == TSDB_DATA_TYPE_NCHAR) { + *StrLen_or_Ind = strnlen((const char*)row, field->bytes); + } else { + *StrLen_or_Ind = field->bytes; + } + } + size_t n = field->bytes; + if (n>BufferLength) n = BufferLength; + memcpy(TargetValue, (const char*)row, n); + } break; + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, + "ColumnNumber[%d] TargetType[%d] BufferLength[%ld] not supported yet", + ColumnNumber, TargetType, BufferLength); + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; } -SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) { +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; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; - if (!sql->rs) return SQL_ERROR; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + return SQL_ERROR; + } + sql->row = taos_fetch_row(sql->rs); return sql->row ? SQL_SUCCESS : SQL_NO_DATA; } -SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, - SQLCHAR *StatementText, SQLINTEGER TextLength) { +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; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } if (sql->rs) { taos_free_result(sql->rs); @@ -568,12 +1167,33 @@ SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, return sql->stmt ? SQL_SUCCESS : SQL_ERROR; } -SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) { +SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) +{ + SQLRETURN r; + r = doSQLPrepare(StatementHandle, StatementText, TextLength); + return r; +} + +static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) +{ sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; - if (!sql->stmt) return SQL_ERROR; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } + + if (!sql->stmt) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_SQL, "no statement cached or not ready"); + return SQL_ERROR; + } if (sql->rs) { taos_free_result(sql->rs); @@ -635,20 +1255,20 @@ SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) { } if (sql->n_params > 0) { - r = taos_stmt_bind_param(sql->stmt, sql->binds); + PROFILE(r = taos_stmt_bind_param(sql->stmt, sql->binds)); if (r) { SET_ERROR(sql, "HY000", r, "failed to bind parameters"); return SQL_ERROR; } - r = taos_stmt_add_batch(sql->stmt); + PROFILE(r = taos_stmt_add_batch(sql->stmt)); if (r) { SET_ERROR(sql, "HY000", r, "failed to add batch"); return SQL_ERROR; } } - r = taos_stmt_execute(sql->stmt); + PROFILE(r = taos_stmt_execute(sql->stmt)); if (r) { SET_ERROR(sql, "HY000", r, "failed to execute statement"); return SQL_ERROR; @@ -656,25 +1276,48 @@ SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) { SQLRETURN ret = SQL_ERROR; - sql->rs = taos_stmt_use_result(sql->stmt); + PROFILE(sql->rs = taos_stmt_use_result(sql->stmt)); CHK_RS(ret, sql, "failed to use result"); return ret; } -SQLRETURN SQL_API SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, +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) { + SQLSMALLINT *StringLength) +{ // if this function is not exported, isql will never call SQLGetDiagRec return SQL_ERROR; } -SQLRETURN SQL_API SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, +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) { + SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) +{ if (RecNumber>1) return SQL_NO_DATA; + switch (HandleType) { case SQL_HANDLE_ENV: { env_t *env = (env_t*)Handle; @@ -698,9 +1341,204 @@ SQLRETURN SQL_API SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, } 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; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } + + if (!sql->stmt) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_SQL, "no statement cached or not ready"); + return SQL_ERROR; + } + + if (fParamType != SQL_PARAM_INPUT) { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "non-input parameter [@%d] not supported yet", ParameterNumber); + return SQL_ERROR; + } + + int buffer_type = 0; + + // ref: https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/converting-data-from-c-to-sql-data-types?view=sql-server-ver15 + switch (ValueType) { + case SQL_C_BIT: { + switch (ParameterType) { + case SQL_BIT: buffer_type = TSDB_DATA_TYPE_BOOL; break; + case SQL_TINYINT: buffer_type = TSDB_DATA_TYPE_TINYINT; break; + case SQL_SMALLINT: buffer_type = TSDB_DATA_TYPE_SMALLINT; break; + case SQL_INTEGER: buffer_type = TSDB_DATA_TYPE_INT; break; + case SQL_BIGINT: buffer_type = TSDB_DATA_TYPE_BIGINT; break; + case SQL_FLOAT: buffer_type = TSDB_DATA_TYPE_FLOAT; break; + case SQL_DOUBLE: buffer_type = TSDB_DATA_TYPE_DOUBLE; break; + case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, + "parameter[@%d] no conversion from [%d] to [%d]", + ParameterNumber, ValueType, ParameterType); + return SQL_ERROR; + } break; + } + } break; + case SQL_C_TINYINT: + case SQL_C_SHORT: + case SQL_C_LONG: + case SQL_C_SBIGINT: + case SQL_C_FLOAT: + case SQL_C_DOUBLE: + case SQL_C_NUMERIC: { + switch (ParameterType) { + case SQL_BIT: buffer_type = TSDB_DATA_TYPE_BOOL; break; + case SQL_TINYINT: buffer_type = TSDB_DATA_TYPE_TINYINT; break; + case SQL_SMALLINT: buffer_type = TSDB_DATA_TYPE_SMALLINT; break; + case SQL_INTEGER: buffer_type = TSDB_DATA_TYPE_INT; break; + case SQL_BIGINT: buffer_type = TSDB_DATA_TYPE_BIGINT; break; + case SQL_FLOAT: buffer_type = TSDB_DATA_TYPE_FLOAT; break; + case SQL_DOUBLE: buffer_type = TSDB_DATA_TYPE_DOUBLE; break; + case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; + case SQL_TIMESTAMP: buffer_type = TSDB_DATA_TYPE_TIMESTAMP; break; // extention to taos + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, + "parameter[@%d] no conversion from [%d] to [%d]", + ParameterNumber, ValueType, ParameterType); + return SQL_ERROR; + } break; + } + } break; + case SQL_C_DATE: + case SQL_C_TIME: + case SQL_C_TIMESTAMP: { + switch (ParameterType) { + case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; + case SQL_TIMESTAMP: buffer_type = TSDB_DATA_TYPE_TIMESTAMP; break; // extention to taos + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, + "parameter[@%d] no conversion from [%d] to [%d]", + ParameterNumber, ValueType, ParameterType); + return SQL_ERROR; + } break; + } + } break; + case SQL_C_CHAR: { + switch (ParameterType) { + case SQL_BIT: buffer_type = TSDB_DATA_TYPE_BOOL; break; + case SQL_TINYINT: buffer_type = TSDB_DATA_TYPE_TINYINT; break; + case SQL_SMALLINT: buffer_type = TSDB_DATA_TYPE_SMALLINT; break; + case SQL_INTEGER: buffer_type = TSDB_DATA_TYPE_INT; break; + case SQL_BIGINT: buffer_type = TSDB_DATA_TYPE_BIGINT; break; + case SQL_FLOAT: buffer_type = TSDB_DATA_TYPE_FLOAT; break; + case SQL_DOUBLE: buffer_type = TSDB_DATA_TYPE_DOUBLE; break; + case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; + case SQL_VARBINARY: buffer_type = TSDB_DATA_TYPE_BINARY; break; + case SQL_TIMESTAMP: buffer_type = TSDB_DATA_TYPE_TIMESTAMP; break; // extention to taos + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, + "parameter[@%d] no conversion from [%d] to [%d]", + ParameterNumber, ValueType, ParameterType); + return SQL_ERROR; + } break; + } + } break; + case SQL_C_BINARY: { + switch (ParameterType) { + case SQL_BIT: buffer_type = TSDB_DATA_TYPE_BOOL; break; + case SQL_TINYINT: buffer_type = TSDB_DATA_TYPE_TINYINT; break; + case SQL_SMALLINT: buffer_type = TSDB_DATA_TYPE_SMALLINT; break; + case SQL_INTEGER: buffer_type = TSDB_DATA_TYPE_INT; break; + case SQL_BIGINT: buffer_type = TSDB_DATA_TYPE_BIGINT; break; + case SQL_FLOAT: buffer_type = TSDB_DATA_TYPE_FLOAT; break; + case SQL_DOUBLE: buffer_type = TSDB_DATA_TYPE_DOUBLE; break; + case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; + case SQL_VARBINARY: buffer_type = TSDB_DATA_TYPE_BINARY; break; + case SQL_TIMESTAMP: buffer_type = TSDB_DATA_TYPE_TIMESTAMP; break; // extention to taos + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, + "parameter[@%d] no conversion from [%d] to [%d]", + ParameterNumber, ValueType, ParameterType); + return SQL_ERROR; + } break; + } + } break; + default: { + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, + "parameter[@%d] no conversion from [%d] to [%d]", + ParameterNumber, ValueType, ParameterType); + return SQL_ERROR; + } break; + } + + param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); + TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); + if (!ar || !binds) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources for Parameter[%d]", ParameterNumber); + if (ar) sql->params = ar; + if (binds) sql->binds = binds; + return SQL_ERROR; + } + sql->params = ar; + sql->binds = binds; + if (sql->n_paramsn_params = ParameterNumber; + } + + param_bind_t *pb = ar + ParameterNumber - 1; + TAOS_BIND *b = binds + ParameterNumber - 1; + + b->buffer_type = buffer_type; + b->buffer_length = LengthPrecision; + b->buffer = NULL; + b->length = NULL; + b->is_null = NULL; + b->is_unsigned = 0; + b->error = NULL; + + 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, @@ -711,362 +1549,252 @@ SQLRETURN SQL_API SQLBindParameter( 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; - if (!sql->conn) return SQL_ERROR; - if (!sql->conn->taos) return SQL_ERROR; - if (!sql->stmt) return SQL_ERROR; + SQLLEN *StrLen_or_Ind) +{ + SQLRETURN r; + r = doSQLBindParameter(StatementHandle, ParameterNumber, fParamType, ValueType, ParameterType, + LengthPrecision, ParameterScale, ParameterValue, cbValueMax, StrLen_or_Ind); + return r; +} - if (fParamType != SQL_PARAM_INPUT) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "non-input parameter [@%d] not supported yet", ParameterNumber); +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, "HY000", TSDB_CODE_TSC_APP_ERROR, "option[%d] other than SQL_DRIVER_NOPROMPT not supported yet", fDriverCompletion); return SQL_ERROR; } - switch (ParameterType) { - case SQL_BIT: { // TSDB_DATA_TYPE_BOOL - if (ValueType!=SQL_C_BIT) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - // LengthPrecision ignored; - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_BOOL; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_TINYINT: { // TSDB_DATA_TYPE_TINYINT - if (ValueType!=SQL_C_TINYINT) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - // LengthPrecision ignored; - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_TINYINT; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_SMALLINT: { // TSDB_DATA_TYPE_SMALLINT - if (ValueType!=SQL_C_SHORT) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - // LengthPrecision ignored; - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_SMALLINT; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_INTEGER: { // TSDB_DATA_TYPE_INT - if (ValueType!=SQL_C_LONG) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - // LengthPrecision ignored; - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_INT; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_BIGINT: { // TSDB_DATA_TYPE_BIGINT - if (ValueType!=SQL_C_SBIGINT) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - // LengthPrecision ignored; - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_BIGINT; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_FLOAT: { // TSDB_DATA_TYPE_FLOAT - if (ValueType!=SQL_C_FLOAT) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - // LengthPrecision ignored; - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_FLOAT; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_DOUBLE: { // TSDB_DATA_TYPE_DOUBLE - if (ValueType!=SQL_C_DOUBLE) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - // LengthPrecision ignored; - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_DOUBLE; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_TIMESTAMP: { // TSDB_DATA_TYPE_TIMESTAMP - if (ValueType!=SQL_C_SBIGINT) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - // LengthPrecision ignored; - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_TIMESTAMP; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_VARBINARY: { // TSDB_DATA_TYPE_BINARY - if (ValueType!=SQL_C_BINARY) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - if (LengthPrecision <=0) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - return SQL_ERROR; - } - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_BINARY; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - case SQL_VARCHAR: { // TSDB_DATA_TYPE_NCHAR - if (ValueType!=SQL_C_CHAR) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching value type", ParameterNumber); - return SQL_ERROR; - } - if (LengthPrecision <=0) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "parameter [@%d] not matching length precision", ParameterNumber); - return SQL_ERROR; - } - // ParameterScale ignored; - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OUT_OF_MEMORY, "failed to bind parameter [@%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; - return SQL_ERROR; - } - sql->params = ar; - sql->binds = binds; - if (sql->n_paramsn_params = ParameterNumber; - } - param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - b->buffer_type = TSDB_DATA_TYPE_NCHAR; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; - pb->ParameterValue = ParameterValue; - pb->StrLen_or_Ind = StrLen_or_Ind; - pb->valid = 1; - } break; - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "xdoes not support parameter type[%x]", ParameterType); - return SQL_ERROR; - } break; + + if (conn->taos) { + SET_ERROR(conn, "HY000", TSDB_CODE_TSC_APP_ERROR, "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, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources"); + 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_RPC_NETWORK_UNAVAIL, "unrecognized connection string: [%s]", (const char*)szConnStrIn); + break; + } + + D("DSN:[%s];UID:[%s];PWD:[%s]", serverName, userName, auth); + + // 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, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "Attribute other than SQL_ATTR_AUTOCOMMIT not supported yet"); + return SQL_ERROR; + } + if (Value != (SQLPOINTER)SQL_AUTOCOMMIT_ON) { + SET_ERROR(conn, "HY000", TSDB_CODE_COM_OPS_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; + + if (!sql->conn) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + return SQL_ERROR; + } + + if (!sql->conn->taos) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); + return SQL_ERROR; + } + + if (!sql->rs) { + SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + return SQL_ERROR; + } + + TAOS_FIELD *fields = taos_fetch_fields(sql->rs); + if (!fields) { + SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "fields not ready or unavailable"); + return SQL_ERROR; + } + int nfields = taos_field_count(sql->rs); + if (ColumnNumber<1 || ColumnNumber>nfields) { + SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "ColumnNumber not in valid range"); + 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 (DataType) { + switch (field->type) { + case TSDB_DATA_TYPE_BOOL: { + *DataType = SQL_C_TINYINT; + } break; + + case TSDB_DATA_TYPE_TINYINT: { + *DataType = SQL_C_TINYINT; + } break; + + case TSDB_DATA_TYPE_SMALLINT: { + *DataType = SQL_C_SHORT; + } break; + + case TSDB_DATA_TYPE_INT: { + *DataType = SQL_C_LONG; + } break; + + case TSDB_DATA_TYPE_BIGINT: { + *DataType = SQL_BIGINT; + } break; + + case TSDB_DATA_TYPE_FLOAT: { + *DataType = SQL_C_FLOAT; + } break; + + case TSDB_DATA_TYPE_DOUBLE: { + *DataType = SQL_C_DOUBLE; + } break; + + case TSDB_DATA_TYPE_TIMESTAMP: { + *DataType = SQL_C_TIMESTAMP; + } break; + + case TSDB_DATA_TYPE_NCHAR: { + *DataType = SQL_C_CHAR; // unicode ? + } break; + + case TSDB_DATA_TYPE_BINARY: { + *DataType = SQL_C_BINARY; + } break; + + default: + SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "unknown TSDB_DATA_TYPE [%x]", field->type); + return SQL_ERROR; + break; + } + } + if (ColumnSize) { + *ColumnSize = field->bytes; + } + if (DecimalDigits) { + if (field->type == TSDB_DATA_TYPE_TIMESTAMP) { + *DecimalDigits = 3; + } else { + *DecimalDigits = 0; + } + } + 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 void init_routine(void) { @@ -1096,7 +1824,7 @@ static int do_field_display_size(TAOS_FIELD *field) { } break; case TSDB_DATA_TYPE_DOUBLE: { - return 12; + return 20; } break; case TSDB_DATA_TYPE_BINARY: @@ -1105,7 +1833,7 @@ static int do_field_display_size(TAOS_FIELD *field) { } break; case TSDB_DATA_TYPE_TIMESTAMP: - return 22; + return 26; break; case TSDB_DATA_TYPE_BOOL: @@ -1162,6 +1890,7 @@ static void do_convert(SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrL } if (n>0) *p = '\0'; ((char*)TargetValue)[BufferLength-1] = '\0'; + *StrLen_or_Ind = BufferLength - n; } break; case TSDB_DATA_TYPE_TIMESTAMP: @@ -1175,3 +1904,692 @@ static void do_convert(SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrL } } +// 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) { + 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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), "%ld", v8); + DASSERT(nsoi = n; + if (target->ptr) 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), "%ld", v8); + DASSERT(nsoi = n; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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, 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 % 1000; + if (target->ptr) 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, 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; + + if (target->ptr) { + snprintf(target->ptr, target->len, "%s.%03d", buf, ts->fraction); + } + + 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, 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; + + if (target->ptr) memcpy(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_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, TIMESTAMP_STRUCT *ts) +{ + if (target->ptr) memcpy(target->ptr, ts, sizeof(*ts)); + 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) +{ + size_t n = field->bytes; + *target->soi = n; + if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n)); + if (n <= target->len) return SQL_SUCCESS; + + SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_BINARY -> SQL_C_CHAR"); + 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) +{ + size_t n = field->bytes; + *target->soi = n; + if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n)); + if (n <= target->len) return SQL_SUCCESS; + + SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_BINARY -> SQL_C_CHAR"); + 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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; + if (target->ptr) 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), "%ld", 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; + if (target->ptr) 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; + if (target->ptr) 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) +{ + size_t n = strlen(str); + *target->soi = n; + if (target->ptr) strncpy(target->ptr, str, (n < target->len ? n+1 : target->len)); + + if (n < target->len) return SQL_SUCCESS; + + SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_CHAR"); + return SQL_SUCCESS_WITH_INFO; +} + +static SQLRETURN conv_tsdb_str_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str) +{ + size_t n = strlen(str); + *target->soi = n; + if (target->ptr) memcpy(target->ptr, str, (n < target->len ? n : target->len)); + + if (n <= target->len) return SQL_SUCCESS; + + SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_BINARY"); + return SQL_SUCCESS_WITH_INFO; +} + diff --git a/src/connector/odbc/tests/main.c b/src/connector/odbc/tests/main.c index aa4ce7a05a..1d403002a8 100644 --- a/src/connector/odbc/tests/main.c +++ b/src/connector/odbc/tests/main.c @@ -118,8 +118,7 @@ static int do_insert(SQLHSTMT stmt, data_t data) { if (r) break; fprintf(stderr, "bind 8 [%s]\n", statement); - SQLLEN l8 = SQL_NULL_DATA; - r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignored, ignored, &data.f8, ignored, &l8); + 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; @@ -196,6 +195,7 @@ int main(int argc, char *argv[]) { memset(data.blob, 0, sizeof(data.blob)); snprintf(data.bin, sizeof(data.bin), "hello"); snprintf(data.blob, sizeof(data.blob), "world"); + snprintf(data.blob, sizeof(data.blob), "wo人rld"); SQLHSTMT stmt = {0}; r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt); if (r!=SQL_SUCCESS) break; diff --git a/src/inc/taoserror.h b/src/inc/taoserror.h index b5d22ea80c..17aa1fbfd5 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 @@ -180,7 +180,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") @@ -350,6 +350,12 @@ 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") + #ifdef TAOS_ERROR_C }; #endif From 77703ac80981c64a201a332c0ba80f0c9d20749c Mon Sep 17 00:00:00 2001 From: freemine Date: Sat, 10 Oct 2020 22:17:28 +0800 Subject: [PATCH 12/23] re-enable jdbc --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2ed56d4f81..f619edd221 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,5 +21,5 @@ ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(dnode) ADD_SUBDIRECTORY(connector/odbc) -# ADD_SUBDIRECTORY(connector/jdbc) +ADD_SUBDIRECTORY(connector/jdbc) From e88979de90d2def7906a738ebe1aead344b75e98 Mon Sep 17 00:00:00 2001 From: freemine Date: Sun, 11 Oct 2020 06:39:36 +0800 Subject: [PATCH 13/23] add python test code with pyodbc --- src/connector/odbc/tests/odbc.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/connector/odbc/tests/odbc.py diff --git a/src/connector/odbc/tests/odbc.py b/src/connector/odbc/tests/odbc.py new file mode 100644 index 0000000000..b12292b238 --- /dev/null +++ b/src/connector/odbc/tests/odbc.py @@ -0,0 +1,12 @@ +import pyodbc +cnxn = pyodbc.connect('DSN=TAOS_DSN;UID=root;PWD=taosdata', autocommit=True) +cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8') +#cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8') +#cnxn.setencoding(encoding='utf-8') +cursor = cnxn.cursor() +cursor.execute("SELECT * from db.t") +row = cursor.fetchone() +while row: + print(row) + row = cursor.fetchone() + From 8d325f16930045b995403c11b03abbb690d41a7b Mon Sep 17 00:00:00 2001 From: freemine Date: Tue, 13 Oct 2020 15:20:56 +0800 Subject: [PATCH 14/23] 1. support SQLWCHAR 2. more precise error message, according ODBC doc --- src/client/src/tscPrepare.c | 56 ++ src/connector/odbc/src/todbc.c | 1238 ++++++++++++++++----------- src/connector/odbc/src/todbc_util.c | 226 +++++ src/connector/odbc/src/todbc_util.h | 52 ++ src/connector/odbc/tests/main.c | 2 +- src/connector/odbc/tests/odbc.py | 44 + src/inc/taos.h | 17 + src/inc/taoserror.h | 10 + 8 files changed, 1127 insertions(+), 518 deletions(-) create mode 100644 src/connector/odbc/src/todbc_util.c create mode 100644 src/connector/odbc/src/todbc_util.h diff --git a/src/client/src/tscPrepare.c b/src/client/src/tscPrepare.c index b3bb4566be..2f42f8ac56 100644 --- a/src/client/src/tscPrepare.c +++ b/src/client/src/tscPrepare.c @@ -637,3 +637,59 @@ TAOS_RES *taos_stmt_use_result(TAOS_STMT* stmt) { pStmt->pSql = NULL; return result; } + +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; + } + + SSqlObj* pSql = pStmt->pSql; + SSqlCmd *pCmd = &pSql->cmd; + + *nums = pCmd->numOfParams; + + 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; + } + + 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; +} + +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/src/todbc.c b/src/connector/odbc/src/todbc.c index 6eaaba762f..9576a981fb 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -13,34 +13,22 @@ * 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 -#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 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) @@ -76,6 +64,7 @@ do { 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) \ @@ -90,7 +79,7 @@ do { \ #define SET_UNSUPPORT_ERROR(obj, sqlstate, err_fmt, ...) \ do { \ - SET_ERROR(obj, sqlstate, TSDB_CODE_COM_OPS_NOT_SUPPORT, err_fmt, ##__VA_ARGS__); \ + SET_ERROR(obj, sqlstate, TSDB_CODE_ODBC_NOT_SUPPORT, err_fmt, ##__VA_ARGS__); \ } while (0) \ #define SET_HANDLE_INVALID(obj, sqlstate, err_fmt, ...) \ @@ -108,6 +97,22 @@ do { \ } \ } 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; \ @@ -184,7 +189,6 @@ struct sql_s { conn_t *conn; TAOS_STMT *stmt; - TAOS_BIND *binds; param_bind_t *params; int n_params; TAOS_RES *rs; @@ -205,8 +209,9 @@ struct c_target_s { static pthread_once_t init_once = PTHREAD_ONCE_INIT; static void init_routine(void); + + static int do_field_display_size(TAOS_FIELD *field); -static void do_convert(SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind, TAOS_FIELD *field, void *row); static SQLRETURN doSQLAllocEnv(SQLHENV *EnvironmentHandle) { @@ -269,7 +274,7 @@ static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle, do { conn = (conn_t*)calloc(1, sizeof(*conn)); if (!conn) { - SET_ERROR(env, "HY000", TSDB_CODE_ODBC_OOM, "failed to alloc connection handle"); + SET_ERROR(env, "HY001", TSDB_CODE_ODBC_OOM, ""); break; } @@ -336,7 +341,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, if (!conn) return SQL_ERROR; if (conn->taos) { - SET_ERROR(conn, "HY000", TSDB_CODE_TSC_APP_ERROR, "connection still in use"); + SET_ERROR(conn, "08002", TSDB_CODE_ODBC_CONNECTION_BUSY, "connection still in use"); return SQL_ERROR; } @@ -346,7 +351,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, do { if ((ServerName && !serverName) || (UserName && !userName) || (Authentication && !auth)) { - SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources"); + SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, ""); break; } @@ -354,7 +359,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, // 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"); + SET_ERROR(conn, "08001", terrno, "failed to connect to data source"); break; } } while (0); @@ -409,7 +414,7 @@ static SQLRETURN doSQLAllocStmt(SQLHDBC ConnectionHandle, do { sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); if (!sql) { - SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_OOM, "failed to alloc statement handle"); + SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, ""); break; } @@ -442,7 +447,7 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, if (Option == SQL_CLOSE) return SQL_SUCCESS; if (Option != SQL_DROP) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "free statement with Option[%x] not supported yet", Option); + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "free statement with Option[%x] not supported yet", Option); return SQL_ERROR; } @@ -458,10 +463,6 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, sql->stmt = NULL; } - if (sql->binds) { - free(sql->binds); - sql->binds = NULL; - } if (sql->params) { free(sql->params); sql->params = NULL; @@ -494,15 +495,8 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (sql->rs) { taos_free_result(sql->rs); @@ -515,11 +509,6 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, sql->stmt = NULL; } - if (sql->binds) { - free(sql->binds); - sql->binds = NULL; - } - if (sql->params) { free(sql->params); sql->params = NULL; @@ -531,11 +520,11 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, SQLRETURN r = SQL_ERROR; do { if (!stxt) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources"); + SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); break; } sql->rs = taos_query(sql->conn->taos, stxt); - CHK_RS(r, sql, "failed to query"); + CHK_RS(r, sql, "failed to execute"); } while (0); SFRE(stxt, StatementText, TextLength); @@ -551,24 +540,24 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, 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; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); return SQL_ERROR; } @@ -594,18 +583,11 @@ static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle, sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); return SQL_ERROR; } @@ -629,22 +611,14 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle, SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength, SQLLEN *NumericAttribute ) { - D("......"); sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); return SQL_ERROR; } @@ -652,22 +626,12 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle, TAOS_FIELD *fields = taos_fetch_fields(sql->rs); if (nfields==0 || fields==NULL) { - SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "no fields in result set"); + SET_ERROR(sql, "07005", TSDB_CODE_ODBC_NO_FIELDS, ""); return SQL_ERROR; } - if (ColumnNumber<0) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] underflow", ColumnNumber); - return SQL_ERROR; - } - - if (ColumnNumber==0) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "ColumnNumber[0] not supported"); - return SQL_ERROR; - } - - if (ColumnNumber>nfields) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] overflow", ColumnNumber); + if (ColumnNumber<=0 || ColumnNumber>nfields) { + SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber); return SQL_ERROR; } @@ -685,9 +649,9 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle, *NumericAttribute = SQL_FALSE; } break; default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, - "ColumnNumber[%d] FieldIdentifier[%d] not supported yet", - ColumnNumber, FieldIdentifier); + 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; } @@ -772,23 +736,16 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); return SQL_ERROR; } if (!sql->row) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no rows cached or not ready"); + SET_ERROR(sql, "24000", TSDB_CODE_ODBC_INVALID_CURSOR, ""); return SQL_ERROR; } @@ -797,23 +754,8 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, int nfields = taos_field_count(sql->rs); TAOS_FIELD *fields = taos_fetch_fields(sql->rs); - if (nfields==0 || fields==NULL) { - SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "no fields in result set"); - return SQL_ERROR; - } - - if (ColumnNumber<0) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] underflow", ColumnNumber); - return SQL_ERROR; - } - - if (ColumnNumber==0) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "ColumnNumber[0] not supported"); - return SQL_ERROR; - } - - if (ColumnNumber>nfields) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] overflow", ColumnNumber); + if (ColumnNumber<=0 || ColumnNumber>nfields) { + SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber); return SQL_ERROR; } @@ -834,8 +776,6 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, target.len = BufferLength; target.soi = StrLen_or_Ind; - SQLRETURN r = SQL_ERROR; - switch (field->type) { case TSDB_DATA_TYPE_BOOL: { int8_t v = *(int8_t*)row; @@ -851,7 +791,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -868,7 +810,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -884,7 +828,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -899,7 +845,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -913,7 +861,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -926,7 +876,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -938,7 +890,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -962,7 +916,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, case SQL_C_BINARY: return conv_tsdb_ts_to_c_bin(sql, &target, field, &ts); case SQL_C_TIMESTAMP: return conv_tsdb_ts_to_c_ts(sql, &target, field, &ts); default: { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -973,7 +929,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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; } } @@ -991,81 +949,21 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "from TSDB_DATA_TYPE [%d] to SQL_C_TYPE [%d] not supported", field->type, target.ct); + 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, "HY000", TSDB_CODE_ODBC_CONV_NOT_SUPPORT, "field [@%d] type [%d] not supported yet", ColumnNumber, field->type); + 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; } - - if (1) return r; - - switch (TargetType) { - case SQL_C_CHAR: { - do_convert(TargetValue, BufferLength, StrLen_or_Ind, field, row); - } break; - case SQL_C_TIMESTAMP: { - TIMESTAMP_STRUCT ts = {0}; - DASSERT(BufferLength == sizeof(ts)); - 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 = 0; - - memcpy(TargetValue, &ts, sizeof(ts)); - } break; - case SQL_C_LONG: { - int32_t v = *(int32_t*)row; - DASSERT(BufferLength == sizeof(v)); - memcpy(TargetValue, &v, sizeof(v)); - } break; - case SQL_C_SBIGINT: { - int64_t v = *(int64_t*)row; - DASSERT(BufferLength == sizeof(v)); - memcpy(TargetValue, &v, sizeof(v)); - } break; - case SQL_C_FLOAT: { - float v = *(float*)row; - DASSERT(BufferLength == sizeof(v)); - memcpy(TargetValue, &v, sizeof(v)); - } break; - case SQL_C_DOUBLE: { - double v = *(double*)row; - DASSERT(BufferLength == sizeof(v)); - memcpy(TargetValue, &v, sizeof(v)); - } break; - case SQL_C_BINARY: { - if (StrLen_or_Ind) { - if (field->type == TSDB_DATA_TYPE_NCHAR) { - *StrLen_or_Ind = strnlen((const char*)row, field->bytes); - } else { - *StrLen_or_Ind = field->bytes; - } - } - size_t n = field->bytes; - if (n>BufferLength) n = BufferLength; - memcpy(TargetValue, (const char*)row, n); - } break; - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, - "ColumnNumber[%d] TargetType[%d] BufferLength[%ld] not supported yet", - ColumnNumber, TargetType, BufferLength); - return SQL_ERROR; - } break; - } - - return SQL_SUCCESS; } SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, @@ -1085,18 +983,11 @@ static SQLRETURN doSQLFetch(SQLHSTMT StatementHandle) sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); return SQL_ERROR; } @@ -1117,15 +1008,8 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (sql->rs) { taos_free_result(sql->rs); @@ -1138,10 +1022,6 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, sql->stmt = NULL; } - if (sql->binds) { - free(sql->binds); - sql->binds = NULL; - } if (sql->params) { free(sql->params); sql->params = NULL; @@ -1151,13 +1031,13 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, do { sql->stmt = taos_stmt_init(sql->conn->taos); if (!sql->stmt) { - SET_ERROR(sql, "HY000", terrno, "failed to initialize statement internally"); + SET_ERROR(sql, "HY001", terrno, "failed to initialize TAOS statement internally"); break; } int r = taos_stmt_prepare(sql->stmt, (const char *)StatementText, TextLength); if (r) { - SET_ERROR(sql, "HY000", r, "failed to prepare a statement"); + SET_ERROR(sql, "HY000", r, "failed to prepare a TAOS statement"); taos_stmt_close(sql->stmt); sql->stmt = NULL; break; @@ -1175,89 +1055,525 @@ SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, return r; } -static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) +static const int yes = 1; +static const int no = 0; + +static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, TAOS_BIND *bind) { - sql_t *sql = (sql_t*)StatementHandle; - if (!sql) return SQL_ERROR; - - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); + if (!param->valid) { + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "parameter [@%d] not bound yet", idx+1); + return SQL_ERROR; + } + if (param->StrLen_or_Ind && *param->StrLen_or_Ind == SQL_NULL_DATA) { + bind->is_null = (int*)&yes; + return SQL_SUCCESS; + } + bind->is_null = (int*)&no; + int type = 0; + int bytes = 0; + 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; } - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } - - if (!sql->stmt) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_SQL, "no statement cached or not ready"); - return SQL_ERROR; - } - - if (sql->rs) { - taos_free_result(sql->rs); - sql->rs = NULL; - sql->row = NULL; - } - - int r = 0; - - for (int i=0; in_params; ++i) { - param_bind_t *pb = sql->params + i; - if (!pb->valid) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "default parameter [@%d] not supported yet", i+1); - return SQL_ERROR; - } - TAOS_BIND *b = sql->binds + i; - int yes = 1; - int no = 0; - if (pb->StrLen_or_Ind && *pb->StrLen_or_Ind == SQL_NULL_DATA) { - b->is_null = &yes; - } else { - b->is_null = &no; - switch (b->buffer_type) { - case TSDB_DATA_TYPE_BOOL: - case TSDB_DATA_TYPE_TINYINT: - case TSDB_DATA_TYPE_SMALLINT: - case TSDB_DATA_TYPE_INT: - case TSDB_DATA_TYPE_BIGINT: - case TSDB_DATA_TYPE_FLOAT: - case TSDB_DATA_TYPE_DOUBLE: - case TSDB_DATA_TYPE_TIMESTAMP: { - b->length = &b->buffer_length; - b->buffer = pb->ParameterValue; + // 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 (param->ValueType) { + case SQL_C_LONG: { + bind->u.b = *(int32_t*)param->ParameterValue; } break; - case TSDB_DATA_TYPE_BINARY: - case TSDB_DATA_TYPE_NCHAR: { - if (!pb->StrLen_or_Ind) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "value [@%d] bad StrLen_or_Ind", i+1); - return SQL_ERROR; - } - size_t n = *pb->StrLen_or_Ind; - if (n == SQL_NTS) { - n = strlen(pb->ParameterValue); - } else if (n < 0 || n > b->buffer_length) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "value [@%d] bad StrLen_or_Ind", i+1); - return SQL_ERROR; - } - - b->buffer_length = n; - b->length = &b->buffer_length; - b->buffer = pb->ParameterValue; + case SQL_C_BIT: { + bind->u.b = *(int8_t*)param->ParameterValue; } 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, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "value [@%d] not supported yet", i+1); + 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_TINYINT: { + bind->u.v1 = *(int8_t*)param->ParameterValue; + } break; + case SQL_C_SHORT: { + bind->u.v1 = *(int16_t*)param->ParameterValue; + } break; + case SQL_C_LONG: { + bind->u.v1 = *(int32_t*)param->ParameterValue; + } break; + case SQL_C_SBIGINT: { + bind->u.v1 = *(int64_t*)param->ParameterValue; + } 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_LONG: { + bind->u.v2 = *(int32_t*)param->ParameterValue; + } break; + case SQL_C_SHORT: { + bind->u.v2 = *(int16_t*)param->ParameterValue; + } 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_LONG: { + bind->u.v4 = *(int32_t*)param->ParameterValue; + } 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_SBIGINT: { + bind->u.v8 = *(int64_t*)param->ParameterValue; + } break; + case SQL_C_LONG: { + bind->u.v8 = *(int32_t*)param->ParameterValue; + } 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_DOUBLE: { + bind->u.f4 = *(double*)param->ParameterValue; + } break; + case SQL_C_FLOAT: { + bind->u.f4 = *(float*)param->ParameterValue; + } 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_DOUBLE: { + bind->u.f8 = *(double*)param->ParameterValue; + } 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_WCHAR: { + DASSERT(param->StrLen_or_Ind); + DASSERT(*param->StrLen_or_Ind != SQL_NTS); + size_t bytes = 0; + SQLCHAR *utf8 = wchars_to_chars(param->ParameterValue, *param->StrLen_or_Ind/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*)param->ParameterValue; + if (*param->StrLen_or_Ind == SQL_NTS) { + bind->buffer_length = strlen((const char*)param->ParameterValue); + } else { + bind->buffer_length = *param->StrLen_or_Ind; + } + 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_WCHAR: { + DASSERT(param->StrLen_or_Ind); + DASSERT(*param->StrLen_or_Ind != SQL_NTS); + size_t bytes = 0; + SQLCHAR *utf8 = wchars_to_chars(param->ParameterValue, *param->StrLen_or_Ind/2, &bytes); + struct tm tm = {0}; + strptime((const char*)utf8, "%Y-%m-%d %H:%M:%S", &tm); + int64_t t = (int64_t)mktime(&tm); + t *= 1000; + bind->u.v8 = t; + free(utf8); + } break; + case SQL_C_SBIGINT: { + int64_t t = *(int64_t*)param->ParameterValue; + 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(param->ValueType), param->ValueType, param->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 (param->ValueType) { + case SQL_C_WCHAR: { + DASSERT(param->StrLen_or_Ind); + DASSERT(*param->StrLen_or_Ind != SQL_NTS); + size_t bytes = 0; + SQLCHAR *utf8 = wchars_to_chars(param->ParameterValue, *param->StrLen_or_Ind/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*)param->ParameterValue; + if (*param->StrLen_or_Ind == SQL_NTS) { + bind->buffer_length = strlen((const char*)param->ParameterValue); + } else { + bind->buffer_length = *param->StrLen_or_Ind; + } + 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(param->ValueType), param->ValueType, param->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(param->ValueType), param->ValueType, param->ValueType, + taos_data_type(type), type, type, idx+1); + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; +} + +static SQLRETURN do_execute(sql_t *sql, TAOS_BIND *binds) +{ + SQLRETURN r = SQL_SUCCESS; + for (int i=0; in_params; ++i) { + r = do_bind_param_value(sql, i, sql->params+i, binds+i); + if (r==SQL_SUCCESS) continue; + return r; } if (sql->n_params > 0) { - PROFILE(r = taos_stmt_bind_param(sql->stmt, sql->binds)); + PROFILE(r = taos_stmt_bind_param(sql->stmt, binds)); if (r) { - SET_ERROR(sql, "HY000", r, "failed to bind parameters"); + SET_ERROR(sql, "HY000", r, "failed to bind parameters[%d in total]", sql->n_params); return SQL_ERROR; } @@ -1274,12 +1590,54 @@ static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) return SQL_ERROR; } - SQLRETURN ret = SQL_ERROR; - PROFILE(sql->rs = taos_stmt_use_result(sql->stmt)); - CHK_RS(ret, sql, "failed to use result"); + CHK_RS(r, sql, "failed to use result"); - return ret; + 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; + } + + 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_execute(sql, 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); + } + + return r; } SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) @@ -1373,159 +1731,30 @@ static SQLRETURN doSQLBindParameter( sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (!sql->stmt) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_SQL, "no statement cached or not ready"); + SET_ERROR(sql, "HY010", TSDB_CODE_ODBC_STATEMENT_NOT_READY, ""); return SQL_ERROR; } if (fParamType != SQL_PARAM_INPUT) { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "non-input parameter [@%d] not supported yet", ParameterNumber); + SET_ERROR(sql, "HY105", TSDB_CODE_ODBC_NOT_SUPPORT, "non-input parameter [@%d] not supported yet", ParameterNumber); return SQL_ERROR; } - int buffer_type = 0; - - // ref: https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/converting-data-from-c-to-sql-data-types?view=sql-server-ver15 - switch (ValueType) { - case SQL_C_BIT: { - switch (ParameterType) { - case SQL_BIT: buffer_type = TSDB_DATA_TYPE_BOOL; break; - case SQL_TINYINT: buffer_type = TSDB_DATA_TYPE_TINYINT; break; - case SQL_SMALLINT: buffer_type = TSDB_DATA_TYPE_SMALLINT; break; - case SQL_INTEGER: buffer_type = TSDB_DATA_TYPE_INT; break; - case SQL_BIGINT: buffer_type = TSDB_DATA_TYPE_BIGINT; break; - case SQL_FLOAT: buffer_type = TSDB_DATA_TYPE_FLOAT; break; - case SQL_DOUBLE: buffer_type = TSDB_DATA_TYPE_DOUBLE; break; - case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, - "parameter[@%d] no conversion from [%d] to [%d]", - ParameterNumber, ValueType, ParameterType); - return SQL_ERROR; - } break; - } - } break; - case SQL_C_TINYINT: - case SQL_C_SHORT: - case SQL_C_LONG: - case SQL_C_SBIGINT: - case SQL_C_FLOAT: - case SQL_C_DOUBLE: - case SQL_C_NUMERIC: { - switch (ParameterType) { - case SQL_BIT: buffer_type = TSDB_DATA_TYPE_BOOL; break; - case SQL_TINYINT: buffer_type = TSDB_DATA_TYPE_TINYINT; break; - case SQL_SMALLINT: buffer_type = TSDB_DATA_TYPE_SMALLINT; break; - case SQL_INTEGER: buffer_type = TSDB_DATA_TYPE_INT; break; - case SQL_BIGINT: buffer_type = TSDB_DATA_TYPE_BIGINT; break; - case SQL_FLOAT: buffer_type = TSDB_DATA_TYPE_FLOAT; break; - case SQL_DOUBLE: buffer_type = TSDB_DATA_TYPE_DOUBLE; break; - case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; - case SQL_TIMESTAMP: buffer_type = TSDB_DATA_TYPE_TIMESTAMP; break; // extention to taos - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, - "parameter[@%d] no conversion from [%d] to [%d]", - ParameterNumber, ValueType, ParameterType); - return SQL_ERROR; - } break; - } - } break; - case SQL_C_DATE: - case SQL_C_TIME: - case SQL_C_TIMESTAMP: { - switch (ParameterType) { - case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; - case SQL_TIMESTAMP: buffer_type = TSDB_DATA_TYPE_TIMESTAMP; break; // extention to taos - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, - "parameter[@%d] no conversion from [%d] to [%d]", - ParameterNumber, ValueType, ParameterType); - return SQL_ERROR; - } break; - } - } break; - case SQL_C_CHAR: { - switch (ParameterType) { - case SQL_BIT: buffer_type = TSDB_DATA_TYPE_BOOL; break; - case SQL_TINYINT: buffer_type = TSDB_DATA_TYPE_TINYINT; break; - case SQL_SMALLINT: buffer_type = TSDB_DATA_TYPE_SMALLINT; break; - case SQL_INTEGER: buffer_type = TSDB_DATA_TYPE_INT; break; - case SQL_BIGINT: buffer_type = TSDB_DATA_TYPE_BIGINT; break; - case SQL_FLOAT: buffer_type = TSDB_DATA_TYPE_FLOAT; break; - case SQL_DOUBLE: buffer_type = TSDB_DATA_TYPE_DOUBLE; break; - case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; - case SQL_VARBINARY: buffer_type = TSDB_DATA_TYPE_BINARY; break; - case SQL_TIMESTAMP: buffer_type = TSDB_DATA_TYPE_TIMESTAMP; break; // extention to taos - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, - "parameter[@%d] no conversion from [%d] to [%d]", - ParameterNumber, ValueType, ParameterType); - return SQL_ERROR; - } break; - } - } break; - case SQL_C_BINARY: { - switch (ParameterType) { - case SQL_BIT: buffer_type = TSDB_DATA_TYPE_BOOL; break; - case SQL_TINYINT: buffer_type = TSDB_DATA_TYPE_TINYINT; break; - case SQL_SMALLINT: buffer_type = TSDB_DATA_TYPE_SMALLINT; break; - case SQL_INTEGER: buffer_type = TSDB_DATA_TYPE_INT; break; - case SQL_BIGINT: buffer_type = TSDB_DATA_TYPE_BIGINT; break; - case SQL_FLOAT: buffer_type = TSDB_DATA_TYPE_FLOAT; break; - case SQL_DOUBLE: buffer_type = TSDB_DATA_TYPE_DOUBLE; break; - case SQL_VARCHAR: buffer_type = TSDB_DATA_TYPE_NCHAR; break; - case SQL_VARBINARY: buffer_type = TSDB_DATA_TYPE_BINARY; break; - case SQL_TIMESTAMP: buffer_type = TSDB_DATA_TYPE_TIMESTAMP; break; // extention to taos - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, - "parameter[@%d] no conversion from [%d] to [%d]", - ParameterNumber, ValueType, ParameterType); - return SQL_ERROR; - } break; - } - } break; - default: { - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, - "parameter[@%d] no conversion from [%d] to [%d]", - ParameterNumber, ValueType, ParameterType); - return SQL_ERROR; - } break; - } - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - TAOS_BIND *binds = (TAOS_BIND*)(sql->n_params>=ParameterNumber ? sql->binds : realloc(sql->binds, ParameterNumber * sizeof(*binds))); - if (!ar || !binds) { - SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources for Parameter[%d]", ParameterNumber); - if (ar) sql->params = ar; - if (binds) sql->binds = binds; + if (!ar) { + SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); return SQL_ERROR; } sql->params = ar; - sql->binds = binds; if (sql->n_paramsn_params = ParameterNumber; } param_bind_t *pb = ar + ParameterNumber - 1; - TAOS_BIND *b = binds + ParameterNumber - 1; - - b->buffer_type = buffer_type; - b->buffer_length = LengthPrecision; - b->buffer = NULL; - b->length = NULL; - b->is_null = NULL; - b->is_unsigned = 0; - b->error = NULL; pb->ParameterNumber = ParameterNumber; pb->ValueType = ValueType; @@ -1571,12 +1800,12 @@ static SQLRETURN doSQLDriverConnect( if (!conn) return SQL_ERROR; if (fDriverCompletion!=SQL_DRIVER_NOPROMPT) { - SET_ERROR(conn, "HY000", TSDB_CODE_TSC_APP_ERROR, "option[%d] other than SQL_DRIVER_NOPROMPT not supported yet", fDriverCompletion); + 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, "HY000", TSDB_CODE_TSC_APP_ERROR, "connection still in use"); + SET_ERROR(conn, "08002", TSDB_CODE_ODBC_CONNECTION_BUSY, "connection still in use"); return SQL_ERROR; } @@ -1591,18 +1820,16 @@ static SQLRETURN doSQLDriverConnect( do { if (szConnStrIn && !connStr) { - SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources"); + 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_RPC_NETWORK_UNAVAIL, "unrecognized connection string: [%s]", (const char*)szConnStrIn); + SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_BAD_CONNSTR, "unrecognized connection string: [%s]", (const char*)szConnStrIn); break; } - D("DSN:[%s];UID:[%s];PWD:[%s]", serverName, userName, auth); - // TODO: data-race // TODO: shall receive ip/port from odbc.ini conn->taos = taos_connect("localhost", userName, auth, NULL, 0); @@ -1652,11 +1879,11 @@ static SQLRETURN doSQLSetConnectAttr(SQLHDBC ConnectionHandle, if (!conn) return SQL_ERROR; if (Attribute != SQL_ATTR_AUTOCOMMIT) { - SET_ERROR(conn, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "Attribute other than SQL_ATTR_AUTOCOMMIT not supported yet"); + 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, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "Attribute Value other than SQL_AUTOCOMMIT_ON not supported yet[%p]", Value); + SET_ERROR(conn, "HYC00", TSDB_CODE_ODBC_NOT_SUPPORT, "Attribute Value other than SQL_AUTOCOMMIT_ON not supported yet[%p]", Value); return SQL_ERROR; } @@ -1681,29 +1908,19 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, sql_t *sql = (sql_t*)StatementHandle; if (!sql) return SQL_ERROR; - if (!sql->conn) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); - return SQL_ERROR; - } - - if (!sql->conn->taos) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); - return SQL_ERROR; - } + CHK_CONN(sql); + CHK_CONN_TAOS(sql); if (!sql->rs) { - SET_ERROR(sql, "HY000", TSDB_CODE_TSC_QUERY_CACHE_ERASED, "no result set cached or not ready"); + SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NO_RESULT, ""); return SQL_ERROR; } - TAOS_FIELD *fields = taos_fetch_fields(sql->rs); - if (!fields) { - SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "fields not ready or unavailable"); - return SQL_ERROR; - } int nfields = taos_field_count(sql->rs); - if (ColumnNumber<1 || ColumnNumber>nfields) { - SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "ColumnNumber not in valid range"); + 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; } @@ -1759,7 +1976,8 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, } break; default: - SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "unknown TSDB_DATA_TYPE [%x]", field->type); + 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; } @@ -1795,9 +2013,52 @@ SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT StatementHandle, 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 params = 0; + int 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 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(); } @@ -1845,65 +2106,6 @@ static int do_field_display_size(TAOS_FIELD *field) { return 10; } -static void do_convert(SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind, TAOS_FIELD *field, void *row) { - switch (field->type) { - case TSDB_DATA_TYPE_TINYINT: - snprintf((char*)TargetValue, BufferLength, "%d", *((int8_t *)row)); - break; - - case TSDB_DATA_TYPE_SMALLINT: - snprintf((char*)TargetValue, BufferLength, "%d", *((int16_t *)row)); - break; - - case TSDB_DATA_TYPE_INT: - snprintf((char*)TargetValue, BufferLength, "%d", *((int32_t *)row)); - break; - - case TSDB_DATA_TYPE_BIGINT: - snprintf((char*)TargetValue, BufferLength, "%" PRId64, *((int64_t *)row)); - break; - - case TSDB_DATA_TYPE_FLOAT: { - float fv = 0; - fv = GET_FLOAT_VAL(row); - snprintf((char*)TargetValue, BufferLength, "%f", fv); - } break; - - case TSDB_DATA_TYPE_DOUBLE: { - double dv = 0; - dv = GET_DOUBLE_VAL(row); - snprintf((char*)TargetValue, BufferLength, "%lf", dv); - } break; - - case TSDB_DATA_TYPE_BINARY: - case TSDB_DATA_TYPE_NCHAR: { - size_t xlen = 0; - char *p = (char*)TargetValue; - size_t n = BufferLength; - for (xlen = 0; xlen < field->bytes - VARSTR_HEADER_SIZE; xlen++) { - char c = ((char *)row)[xlen]; - if (c == 0) break; - int v = snprintf(p, n, "%c", c); - p += v; - n -= v; - if (n<=0) break; - } - if (n>0) *p = '\0'; - ((char*)TargetValue)[BufferLength-1] = '\0'; - *StrLen_or_Ind = BufferLength - n; - } break; - - case TSDB_DATA_TYPE_TIMESTAMP: - snprintf((char*)TargetValue, BufferLength, "%" PRId64, *((int64_t *)row)); - break; - - case TSDB_DATA_TYPE_BOOL: - snprintf((char*)TargetValue, BufferLength, "%d", *((int8_t *)row)); - default: - break; - } -} - // 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) { @@ -2353,6 +2555,7 @@ static SQLRETURN conv_tsdb_ts_to_c_ts(sql_t *sql, c_target_t *target, TAOS_FIELD static SQLRETURN conv_tsdb_bin_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const unsigned char *bin) { size_t n = field->bytes; + n = strlen((const char*)bin); *target->soi = n; if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n)); if (n <= target->len) return SQL_SUCCESS; @@ -2364,6 +2567,7 @@ static SQLRETURN conv_tsdb_bin_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIE static SQLRETURN conv_tsdb_bin_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const unsigned char *bin) { size_t n = field->bytes; + n = strlen((const char*)bin); *target->soi = n; if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n)); if (n <= target->len) return SQL_SUCCESS; diff --git a/src/connector/odbc/src/todbc_util.c b/src/connector/odbc/src/todbc_util.c new file mode 100644 index 0000000000..9bbe8e6987 --- /dev/null +++ b/src/connector/odbc/src/todbc_util.c @@ -0,0 +1,226 @@ +/* + * 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 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 0000000000..0400da59fe --- /dev/null +++ b/src/connector/odbc/src/todbc_util.h @@ -0,0 +1,52 @@ +/* + * 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) + +const char* sql_sql_type(int type); +const char* sql_c_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/main.c b/src/connector/odbc/tests/main.c index 1d403002a8..1ac9b71369 100644 --- a/src/connector/odbc/tests/main.c +++ b/src/connector/odbc/tests/main.c @@ -193,7 +193,7 @@ int main(int argc, char *argv[]) { data.f8 = 9999999.999999; memset(data.bin, 0, sizeof(data.bin)); memset(data.blob, 0, sizeof(data.blob)); - snprintf(data.bin, sizeof(data.bin), "hello"); + snprintf(data.bin, sizeof(data.bin), "hel我lo"); snprintf(data.blob, sizeof(data.blob), "world"); snprintf(data.blob, sizeof(data.blob), "wo人rld"); SQLHSTMT stmt = {0}; diff --git a/src/connector/odbc/tests/odbc.py b/src/connector/odbc/tests/odbc.py index b12292b238..c2f2f33d2b 100644 --- a/src/connector/odbc/tests/odbc.py +++ b/src/connector/odbc/tests/odbc.py @@ -9,4 +9,48 @@ row = cursor.fetchone() while row: print(row) row = cursor.fetchone() +cursor.close() + +#cursor = cnxn.cursor() +#cursor.execute(""" +#INSERT INTO db.t values (?,?,?,?,?,?,?,?,?,?) +#""", +#"2020-12-12 00:00:00", +#1, +#27, +#32767, +#147483647, +#223372036854775807, +#23.456, +#899.999999, +#"foo", +#"bar") + +cursor = cnxn.cursor() +cursor.execute("drop database if exists db"); +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("create database db"); +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))"); +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')") +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("insert into db.t values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld"); +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("SELECT * from db.t") +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 7e8f174b7c..e4fba58228 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,25 @@ 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_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 17aa1fbfd5..1d8c50280e 100644 --- a/src/inc/taoserror.h +++ b/src/inc/taoserror.h @@ -355,6 +355,16 @@ TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_OOM, 0, 0x2101, "out of mem 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") + #ifdef TAOS_ERROR_C }; From 7c04c9b3803a55f7e406bda93e388058b2b39ec7 Mon Sep 17 00:00:00 2001 From: freemine Date: Tue, 13 Oct 2020 22:31:59 +0800 Subject: [PATCH 15/23] 1. bugFix: nchar/binary field bytes 2. report error for parameterised-select --- src/client/src/tscPrepare.c | 49 ++++-- src/connector/odbc/src/todbc.c | 232 ++++++++++++++++------------ src/connector/odbc/src/todbc_util.h | 9 ++ src/connector/odbc/tests/odbc.py | 33 +++- src/inc/taos.h | 1 + src/inc/taoserror.h | 1 + 6 files changed, 203 insertions(+), 122 deletions(-) diff --git a/src/client/src/tscPrepare.c b/src/client/src/tscPrepare.c index 2f42f8ac56..5630919ddb 100644 --- a/src/client/src/tscPrepare.c +++ b/src/client/src/tscPrepare.c @@ -638,6 +638,19 @@ TAOS_RES *taos_stmt_use_result(TAOS_STMT* stmt) { 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; @@ -646,12 +659,14 @@ int taos_stmt_num_params(TAOS_STMT *stmt, int *nums) { return TSDB_CODE_TSC_DISCONNECTED; } - SSqlObj* pSql = pStmt->pSql; - SSqlCmd *pCmd = &pSql->cmd; - - *nums = pCmd->numOfParams; - - return TSDB_CODE_SUCCESS; + if (pStmt->isInsert) { + SSqlObj* pSql = pStmt->pSql; + SSqlCmd *pCmd = &pSql->cmd; + *nums = pCmd->numOfParams; + return TSDB_CODE_SUCCESS; + } else { + return TSDB_CODE_TSC_APP_ERROR; + } } int taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes) { @@ -662,18 +677,22 @@ int taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes) { return TSDB_CODE_TSC_DISCONNECTED; } - SSqlObj* pSql = pStmt->pSql; - SSqlCmd *pCmd = &pSql->cmd; - STableDataBlocks* pBlock = taosArrayGetP(pCmd->pDataBlocks, 0); + 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; + 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; + SParamInfo* param = pBlock->params + idx; + if (type) *type = param->type; + if (bytes) *bytes = param->bytes; - return TSDB_CODE_SUCCESS; + return TSDB_CODE_SUCCESS; + } else { + return TSDB_CODE_TSC_APP_ERROR; + } } const char *taos_data_type(int type) { diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 9576a981fb..3d30b56662 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -759,6 +759,11 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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]; @@ -1563,33 +1568,34 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T static SQLRETURN do_execute(sql_t *sql, TAOS_BIND *binds) { - SQLRETURN r = SQL_SUCCESS; + int tr = TSDB_CODE_SUCCESS; for (int i=0; in_params; ++i) { - r = do_bind_param_value(sql, i, sql->params+i, binds+i); + SQLRETURN r = do_bind_param_value(sql, i, sql->params+i, binds+i); if (r==SQL_SUCCESS) continue; return r; } if (sql->n_params > 0) { - PROFILE(r = taos_stmt_bind_param(sql->stmt, binds)); - if (r) { - SET_ERROR(sql, "HY000", r, "failed to bind parameters[%d in total]", sql->n_params); + 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; } - PROFILE(r = taos_stmt_add_batch(sql->stmt)); - if (r) { - SET_ERROR(sql, "HY000", r, "failed to add batch"); - return SQL_ERROR; - } + // PROFILE(r = taos_stmt_add_batch(sql->stmt)); + // if (r) { + // SET_ERROR(sql, "HY000", r, "failed to add batch"); + // return SQL_ERROR; + // } } - PROFILE(r = taos_stmt_execute(sql->stmt)); - if (r) { - SET_ERROR(sql, "HY000", r, "failed to execute statement"); + PROFILE(tr = taos_stmt_execute(sql->stmt)); + if (tr) { + SET_ERROR(sql, "HY000", tr, "failed to execute statement"); return SQL_ERROR; } + SQLRETURN r = SQL_SUCCESS; PROFILE(sql->rs = taos_stmt_use_result(sql->stmt)); CHK_RS(r, sql, "failed to use result"); @@ -1933,6 +1939,10 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, if (NameLength) { *NameLength = strnlen(field->name, sizeof(field->name)); } + if (ColumnSize) { + *ColumnSize = field->bytes; + } + if (DataType) { switch (field->type) { case TSDB_DATA_TYPE_BOOL: { @@ -1969,10 +1979,12 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, case TSDB_DATA_TYPE_NCHAR: { *DataType = SQL_C_CHAR; // unicode ? + if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE; } break; case TSDB_DATA_TYPE_BINARY: { *DataType = SQL_C_BINARY; + if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE; } break; default: @@ -1982,9 +1994,6 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, break; } } - if (ColumnSize) { - *ColumnSize = field->bytes; - } if (DecimalDigits) { if (field->type == TSDB_DATA_TYPE_TIMESTAMP) { *DecimalDigits = 3; @@ -2026,8 +2035,19 @@ static SQLRETURN doSQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar) 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; - int r = taos_stmt_num_params(sql->stmt, ¶ms); + r = taos_stmt_num_params(sql->stmt, ¶ms); if (r) { SET_ERROR(sql, "HY000", terrno, "fetch num of statement params failed"); return SQL_ERROR; @@ -2110,49 +2130,49 @@ static int do_field_display_size(TAOS_FIELD *field) { 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); return SQL_SUCCESS; } @@ -2160,11 +2180,9 @@ static SQLRETURN conv_tsdb_bool_to_c_char(sql_t *sql, c_target_t *target, TAOS_F { DASSERT(target->len>0); *target->soi = 1; - if (target->ptr) { - target->ptr[0] = '0' + b; - if (target->len>1) { - target->ptr[1] = '\0'; - } + target->ptr[0] = '0' + b; + if (target->len>1) { + target->ptr[1] = '\0'; } return SQL_SUCCESS; @@ -2181,42 +2199,42 @@ static SQLRETURN conv_tsdb_bool_to_c_binary(sql_t *sql, c_target_t *target, TAOS 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); return SQL_SUCCESS; } @@ -2226,7 +2244,7 @@ static SQLRETURN conv_tsdb_v1_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIE int n = snprintf(buf, sizeof(buf), "%d", v1); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1)); + 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; @@ -2238,7 +2256,7 @@ static SQLRETURN conv_tsdb_v1_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F int n = snprintf(buf, sizeof(buf), "%d", v1); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>target->len ? target->len : 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; @@ -2247,35 +2265,35 @@ static SQLRETURN conv_tsdb_v1_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); return SQL_SUCCESS; } @@ -2285,7 +2303,7 @@ static SQLRETURN conv_tsdb_v2_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIE int n = snprintf(buf, sizeof(buf), "%d", v2); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1)); + 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; @@ -2297,7 +2315,7 @@ static SQLRETURN conv_tsdb_v2_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F int n = snprintf(buf, sizeof(buf), "%d", v2); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>target->len ? target->len : 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; @@ -2307,28 +2325,28 @@ static SQLRETURN conv_tsdb_v2_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); return SQL_SUCCESS; } @@ -2338,7 +2356,7 @@ static SQLRETURN conv_tsdb_v4_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIE int n = snprintf(buf, sizeof(buf), "%d", v4); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1)); + 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; @@ -2350,7 +2368,7 @@ static SQLRETURN conv_tsdb_v4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F int n = snprintf(buf, sizeof(buf), "%d", v4); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>target->len ? target->len : 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; @@ -2360,21 +2378,21 @@ static SQLRETURN conv_tsdb_v4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); return SQL_SUCCESS; } @@ -2384,7 +2402,7 @@ static SQLRETURN conv_tsdb_v8_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIE int n = snprintf(buf, sizeof(buf), "%ld", v8); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1)); + 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; @@ -2396,7 +2414,7 @@ static SQLRETURN conv_tsdb_v8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F int n = snprintf(buf, sizeof(buf), "%ld", v8); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>target->len ? target->len : 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; @@ -2406,14 +2424,14 @@ static SQLRETURN conv_tsdb_v8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F static SQLRETURN conv_tsdb_f4_to_c_float(sql_t *sql, c_target_t *target, TAOS_FIELD *field, float f4) { float v = f4; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + 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; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); return SQL_SUCCESS; } @@ -2423,7 +2441,7 @@ static SQLRETURN conv_tsdb_f4_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIE int n = snprintf(buf, sizeof(buf), "%g", f4); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1)); + 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; @@ -2435,7 +2453,7 @@ static SQLRETURN conv_tsdb_f4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F int n = snprintf(buf, sizeof(buf), "%g", f4); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>target->len ? target->len : 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; @@ -2445,7 +2463,7 @@ static SQLRETURN conv_tsdb_f4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F static SQLRETURN conv_tsdb_f8_to_c_double(sql_t *sql, c_target_t *target, TAOS_FIELD *field, double f8) { double v = f8; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); return SQL_SUCCESS; } @@ -2455,7 +2473,7 @@ static SQLRETURN conv_tsdb_f8_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIE int n = snprintf(buf, sizeof(buf), "%.6f", f8); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1)); + 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; @@ -2467,7 +2485,7 @@ static SQLRETURN conv_tsdb_f8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F int n = snprintf(buf, sizeof(buf), "%g", f8); DASSERT(nsoi = n; - if (target->ptr) strncpy(target->ptr, buf, (n>target->len ? target->len : 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; @@ -2488,7 +2506,7 @@ static SQLRETURN conv_tsdb_ts_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIELD int64_t v = (int64_t)t; v *= 1000; v += ts->fraction % 1000; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); return SQL_SUCCESS; } @@ -2508,9 +2526,7 @@ static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIEL *target->soi = n; - if (target->ptr) { - snprintf(target->ptr, target->len, "%s.%03d", buf, ts->fraction); - } + snprintf(target->ptr, target->len, "%s.%03d", buf, ts->fraction); if (n <= target->len) { return SQL_SUCCESS; @@ -2536,7 +2552,7 @@ static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIEL *target->soi = n; - if (target->ptr) memcpy(target->ptr, buf, (n>target->len ? target->len : n)); + memcpy(target->ptr, buf, (n>target->len ? target->len : n)); if (n <= target->len) { return SQL_SUCCESS; @@ -2548,31 +2564,55 @@ static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIEL static SQLRETURN conv_tsdb_ts_to_c_ts(sql_t *sql, c_target_t *target, TAOS_FIELD *field, TIMESTAMP_STRUCT *ts) { - if (target->ptr) memcpy(target->ptr, ts, sizeof(*ts)); + memcpy(target->ptr, ts, sizeof(*ts)); 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) { - size_t n = field->bytes; - n = strlen((const char*)bin); - *target->soi = n; - if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n)); - if (n <= target->len) return SQL_SUCCESS; + 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); - SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_BINARY -> SQL_C_CHAR"); + 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) { - size_t n = field->bytes; - n = strlen((const char*)bin); - *target->soi = n; - if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n)); - if (n <= target->len) return SQL_SUCCESS; + 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); - SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_UNDEF, "TSDB_DATA_TYPE_BINARY -> SQL_C_CHAR"); + 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; } @@ -2583,7 +2623,7 @@ static SQLRETURN conv_tsdb_str_to_c_bit(sql_t *sql, c_target_t *target, TAOS_FIE int n = sscanf(str, "%lf%n", &f8, &bytes); int8_t v = f8; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); *target->soi = 1; @@ -2622,7 +2662,7 @@ static SQLRETURN conv_tsdb_str_to_c_v1(sql_t *sql, c_target_t *target, TAOS_FIEL int n = sscanf(str, "%lf%n", &f8, &bytes); int8_t v = f8; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); *target->soi = 1; @@ -2652,7 +2692,7 @@ static SQLRETURN conv_tsdb_str_to_c_v2(sql_t *sql, c_target_t *target, TAOS_FIEL int n = sscanf(str, "%lf%n", &f8, &bytes); int16_t v = f8; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); *target->soi = 2; @@ -2682,7 +2722,7 @@ static SQLRETURN conv_tsdb_str_to_c_v4(sql_t *sql, c_target_t *target, TAOS_FIEL int n = sscanf(str, "%lf%n", &f8, &bytes); int32_t v = f8; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); *target->soi = 4; @@ -2712,7 +2752,7 @@ static SQLRETURN conv_tsdb_str_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIEL int n = sscanf(str, "%lf%n", &f8, &bytes); int64_t v = f8; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); *target->soi = 8; @@ -2742,7 +2782,7 @@ static SQLRETURN conv_tsdb_str_to_c_f4(sql_t *sql, c_target_t *target, TAOS_FIEL int n = sscanf(str, "%lf%n", &f8, &bytes); float v = f8; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); *target->soi = 4; @@ -2761,7 +2801,7 @@ static SQLRETURN conv_tsdb_str_to_c_f8(sql_t *sql, c_target_t *target, TAOS_FIEL int n = sscanf(str, "%lf%n", &f8, &bytes); float v = f8; - if (target->ptr) memcpy(target->ptr, &v, sizeof(v)); + memcpy(target->ptr, &v, sizeof(v)); *target->soi = 8; @@ -2775,25 +2815,11 @@ static SQLRETURN conv_tsdb_str_to_c_f8(sql_t *sql, c_target_t *target, TAOS_FIEL static SQLRETURN conv_tsdb_str_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, const char *str) { - size_t n = strlen(str); - *target->soi = n; - if (target->ptr) strncpy(target->ptr, str, (n < target->len ? n+1 : target->len)); - - if (n < target->len) return SQL_SUCCESS; - - SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_CHAR"); - return SQL_SUCCESS_WITH_INFO; + 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) { - size_t n = strlen(str); - *target->soi = n; - if (target->ptr) memcpy(target->ptr, str, (n < target->len ? n : target->len)); - - if (n <= target->len) return SQL_SUCCESS; - - SET_ERROR(sql, "01004", TSDB_CODE_ODBC_CONV_TRUNC, "TSDB_DATA_TYPE_NCHAR -> SQL_C_BINARY"); - return SQL_SUCCESS_WITH_INFO; + return conv_tsdb_bin_to_c_bin(sql, target, field, (const unsigned char*)str); } diff --git a/src/connector/odbc/src/todbc_util.h b/src/connector/odbc/src/todbc_util.h index 0400da59fe..a1521ef1cf 100644 --- a/src/connector/odbc/src/todbc_util.h +++ b/src/connector/odbc/src/todbc_util.h @@ -35,6 +35,15 @@ do { \ 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); diff --git a/src/connector/odbc/tests/odbc.py b/src/connector/odbc/tests/odbc.py index c2f2f33d2b..b1169679e3 100644 --- a/src/connector/odbc/tests/odbc.py +++ b/src/connector/odbc/tests/odbc.py @@ -3,10 +3,11 @@ cnxn = pyodbc.connect('DSN=TAOS_DSN;UID=root;PWD=taosdata', autocommit=True) cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8') #cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8') #cnxn.setencoding(encoding='utf-8') + cursor = cnxn.cursor() cursor.execute("SELECT * from db.t") -row = cursor.fetchone() -while row: +row = cursor.fetchone() +while row: print(row) row = cursor.fetchone() cursor.close() @@ -48,8 +49,32 @@ cursor.close() cursor = cnxn.cursor() cursor.execute("SELECT * from db.t") -row = cursor.fetchone() -while row: +row = cursor.fetchone() +while row: + print(row) + row = cursor.fetchone() +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("drop database if exists db"); +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("create database db"); +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("create table db.t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(4), blob nchar(4))"); +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hell', 'worl')") +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("SELECT * from db.t") +row = cursor.fetchone() +while row: print(row) row = cursor.fetchone() cursor.close() diff --git a/src/inc/taos.h b/src/inc/taos.h index e4fba58228..2d72b1739d 100644 --- a/src/inc/taos.h +++ b/src/inc/taos.h @@ -96,6 +96,7 @@ typedef struct 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); diff --git a/src/inc/taoserror.h b/src/inc/taoserror.h index 0e05a0829f..3bf8444d67 100644 --- a/src/inc/taoserror.h +++ b/src/inc/taoserror.h @@ -366,6 +366,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_INVALID_CURSOR, 0, 0x2110, "invalid cu 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 From 1c52e6598e723024fe5e782797aa44ae6434929d Mon Sep 17 00:00:00 2001 From: freemine Date: Sat, 17 Oct 2020 06:11:21 +0800 Subject: [PATCH 16/23] 1. add batch-insert 2. allow buffer-type conversion within taos --- src/client/src/tscPrepare.c | 417 +++++++++++++- src/connector/odbc/src/todbc.c | 825 +++++++++++++++++++++++++--- src/connector/odbc/src/todbc_util.c | 12 + src/connector/odbc/src/todbc_util.h | 2 + src/connector/odbc/tests/odbc.py | 36 +- 5 files changed, 1189 insertions(+), 103 deletions(-) diff --git a/src/client/src/tscPrepare.c b/src/client/src/tscPrepare.c index 5630919ddb..d6dd19db77 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,387 @@ 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 = *(int16_t*)bind->buffer; + if (u.v1==0 || u.v1==1) break; + } break; + case TSDB_DATA_TYPE_INT: { + u.v1 = *(int32_t*)bind->buffer; + if (u.v1==0 || u.v1==1) break; + } break; + case TSDB_DATA_TYPE_BIGINT: { + u.v1 = *(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 = 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 = 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 = 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, "%ld%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.v1 = 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 = v; + } break; + case TSDB_DATA_TYPE_INT: { + int32_t v = *(int32_t*)bind->buffer; + u.v2 = 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 = 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, "%ld%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.v2 = 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 = 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, "%ld%n", &v, &n); + if (r==1 && n==strlen((const char*)bind->buffer)) { + u.v4 = 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 = *(int32_t*)bind->buffer; + // shall we check equality? + } break; + case TSDB_DATA_TYPE_BIGINT: { + u.f4 = *(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, "%ld%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 = *(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.v8 = 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? + if (taosParseTime(bind->buffer, &u.v8, *bind->length, 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 +680,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 +739,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 +786,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 +828,7 @@ static int insertStmtExecute(STscStmt* stmt) { pRes->numOfRows = 0; pRes->numOfTotal = 0; pRes->numOfClauseTotal = 0; - + pRes->qhandle = 0; pSql->cmd.insertType = 0; @@ -508,35 +889,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 +929,7 @@ int taos_stmt_prepare(TAOS_STMT* stmt, const char* sql, unsigned long length) { tsem_wait(&pSql->rspSem); return pSql->res.code; } - + return code; } @@ -665,7 +1046,9 @@ int taos_stmt_num_params(TAOS_STMT *stmt, int *nums) { *nums = pCmd->numOfParams; return TSDB_CODE_SUCCESS; } else { - return TSDB_CODE_TSC_APP_ERROR; + SNormalStmt* normal = &pStmt->normal; + *nums = normal->numParams; + return TSDB_CODE_SUCCESS; } } diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 3d30b56662..45f005a7ed 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -156,7 +156,7 @@ struct param_bind_s { SQLULEN LengthPrecision; SQLSMALLINT ParameterScale; SQLPOINTER ParameterValue; - SQLLEN *StrLen_or_Ind; + SQLLEN *StrLen_or_Ind; unsigned int valid; }; @@ -191,10 +191,17 @@ struct sql_s { 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; @@ -209,6 +216,37 @@ struct c_target_s { static pthread_once_t init_once = PTHREAD_ONCE_INIT; static void init_routine(void); +// conversions + +const char* tsdb_int64_to_bit(int64_t src, int8_t *dst); +const char* tsdb_int64_to_tinyint(int64_t src, int8_t *dst); +const char* tsdb_int64_to_smallint(int64_t src, int16_t *dst); +const char* tsdb_int64_to_int(int64_t src, int32_t *dst); +const char* tsdb_int64_to_bigint(int64_t src, int64_t *dst); +const char* tsdb_int64_to_ts(int64_t src, int64_t *dst); +const char* tsdb_int64_to_float(int64_t src, float *dst); +const char* tsdb_int64_to_double(int64_t src, double *dst); +const char* tsdb_int64_to_char(int64_t src, char *dst, size_t dlen); + +const char* tsdb_double_to_bit(double src, int precision, int8_t *dst); +const char* tsdb_double_to_tinyint(double src, int precision, int8_t *dst); +const char* tsdb_double_to_smallint(double src, int precision, int16_t *dst); +const char* tsdb_double_to_int(double src, int precision, int32_t *dst); +const char* tsdb_double_to_bigint(double src, int precision, int64_t *dst); +const char* tsdb_double_to_ts(double src, int precision, int64_t *dst); +const char* tsdb_double_to_float(double src, int precision, float *dst); +const char* tsdb_double_to_double(double src, int precision, double *dst); +const char* tsdb_double_to_char(double src, int precision, char *dst, size_t dlen); + +const char* tsdb_chars_to_bit(const char *src, int8_t *dst); +const char* tsdb_chars_to_tinyint(const char *src, int8_t *dst); +const char* tsdb_chars_to_smallint(const char *src, int16_t *dst); +const char* tsdb_chars_to_int(const char *src, int32_t *dst); +const char* tsdb_chars_to_bigint(const char *src, int64_t *dst); +const char* tsdb_chars_to_ts(const char *src, int64_t *dst); +const char* tsdb_chars_to_float(const char *src, float *dst); +const char* tsdb_chars_to_double(const char *src, double *dst); +const char* tsdb_chars_to_char(const char *src, char *dst, size_t dlen); static int do_field_display_size(TAOS_FIELD *field); @@ -439,6 +477,40 @@ SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, 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) { @@ -556,6 +628,13 @@ static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle, 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; @@ -586,6 +665,11 @@ static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle, 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; @@ -919,6 +1003,7 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, 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, @@ -1032,6 +1117,7 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, sql->params = NULL; } sql->n_params = 0; + sql->is_insert = 0; do { sql->stmt = taos_stmt_init(sql->conn->taos); @@ -1040,12 +1126,51 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, break; } - int r = taos_stmt_prepare(sql->stmt, (const char *)StatementText, TextLength); - if (r) { - SET_ERROR(sql, "HY000", r, "failed to prepare a TAOS statement"); + 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; - break; + sql->is_prepared = 0; + sql->is_insert = 0; + sql->is_executed = 0; } } while (0); @@ -1063,23 +1188,76 @@ SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, static const int yes = 1; static const int no = 0; -static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, TAOS_BIND *bind) +static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bind_t *param, TAOS_BIND *bind) { if (!param->valid) { SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_NOT_SUPPORT, "parameter [@%d] not bound yet", idx+1); return SQL_ERROR; } - if (param->StrLen_or_Ind && *param->StrLen_or_Ind == SQL_NULL_DATA) { + + 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; - 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; + 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 @@ -1089,12 +1267,12 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T bind->buffer_length = sizeof(bind->u.b); bind->buffer = &bind->u.b; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_LONG: { - bind->u.b = *(int32_t*)param->ParameterValue; + bind->u.b = *(int32_t*)paramValue; } break; case SQL_C_BIT: { - bind->u.b = *(int8_t*)param->ParameterValue; + bind->u.b = *(int8_t*)paramValue; } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1122,7 +1300,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1133,18 +1311,18 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T bind->buffer_length = sizeof(bind->u.v1); bind->buffer = &bind->u.v1; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_TINYINT: { - bind->u.v1 = *(int8_t*)param->ParameterValue; + bind->u.v1 = *(int8_t*)paramValue; } break; case SQL_C_SHORT: { - bind->u.v1 = *(int16_t*)param->ParameterValue; + bind->u.v1 = *(int16_t*)paramValue; } break; case SQL_C_LONG: { - bind->u.v1 = *(int32_t*)param->ParameterValue; + bind->u.v1 = *(int32_t*)paramValue; } break; case SQL_C_SBIGINT: { - bind->u.v1 = *(int64_t*)param->ParameterValue; + bind->u.v1 = *(int64_t*)paramValue; } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1170,7 +1348,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1181,12 +1359,12 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T bind->buffer_length = sizeof(bind->u.v2); bind->buffer = &bind->u.v2; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_LONG: { - bind->u.v2 = *(int32_t*)param->ParameterValue; + bind->u.v2 = *(int32_t*)paramValue; } break; case SQL_C_SHORT: { - bind->u.v2 = *(int16_t*)param->ParameterValue; + bind->u.v2 = *(int16_t*)paramValue; } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1214,7 +1392,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1225,9 +1403,9 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T bind->buffer_length = sizeof(bind->u.v4); bind->buffer = &bind->u.v4; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_LONG: { - bind->u.v4 = *(int32_t*)param->ParameterValue; + bind->u.v4 = *(int32_t*)paramValue; } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1256,7 +1434,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1267,12 +1445,12 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T bind->buffer_length = sizeof(bind->u.v8); bind->buffer = &bind->u.v8; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_SBIGINT: { - bind->u.v8 = *(int64_t*)param->ParameterValue; + bind->u.v8 = *(int64_t*)paramValue; } break; case SQL_C_LONG: { - bind->u.v8 = *(int32_t*)param->ParameterValue; + bind->u.v8 = *(int32_t*)paramValue; } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1300,7 +1478,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1311,12 +1489,12 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T bind->buffer_length = sizeof(bind->u.f4); bind->buffer = &bind->u.f4; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_DOUBLE: { - bind->u.f4 = *(double*)param->ParameterValue; + bind->u.f4 = *(double*)paramValue; } break; case SQL_C_FLOAT: { - bind->u.f4 = *(float*)param->ParameterValue; + bind->u.f4 = *(float*)paramValue; } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1344,7 +1522,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1355,9 +1533,9 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T bind->buffer_length = sizeof(bind->u.f8); bind->buffer = &bind->u.f8; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_DOUBLE: { - bind->u.f8 = *(double*)param->ParameterValue; + bind->u.f8 = *(double*)paramValue; } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1386,7 +1564,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1395,23 +1573,23 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T case TSDB_DATA_TYPE_BINARY: { bind->buffer_type = type; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_WCHAR: { - DASSERT(param->StrLen_or_Ind); - DASSERT(*param->StrLen_or_Ind != SQL_NTS); + DASSERT(soi); + DASSERT(*soi != SQL_NTS); size_t bytes = 0; - SQLCHAR *utf8 = wchars_to_chars(param->ParameterValue, *param->StrLen_or_Ind/2, &bytes); + 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*)param->ParameterValue; - if (*param->StrLen_or_Ind == SQL_NTS) { - bind->buffer_length = strlen((const char*)param->ParameterValue); + bind->u.bin = (unsigned char*)paramValue; + if (*soi == SQL_NTS) { + bind->buffer_length = strlen((const char*)paramValue); } else { - bind->buffer_length = *param->StrLen_or_Ind; + bind->buffer_length = *soi; } bind->buffer = bind->u.bin; } break; @@ -1441,7 +1619,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1452,12 +1630,12 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T bind->buffer_length = sizeof(bind->u.v8); bind->buffer = &bind->u.v8; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_WCHAR: { - DASSERT(param->StrLen_or_Ind); - DASSERT(*param->StrLen_or_Ind != SQL_NTS); + DASSERT(soi); + DASSERT(*soi != SQL_NTS); size_t bytes = 0; - SQLCHAR *utf8 = wchars_to_chars(param->ParameterValue, *param->StrLen_or_Ind/2, &bytes); + SQLCHAR *utf8 = wchars_to_chars(paramValue, *soi/2, &bytes); struct tm tm = {0}; strptime((const char*)utf8, "%Y-%m-%d %H:%M:%S", &tm); int64_t t = (int64_t)mktime(&tm); @@ -1466,7 +1644,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T free(utf8); } break; case SQL_C_SBIGINT: { - int64_t t = *(int64_t*)param->ParameterValue; + int64_t t = *(int64_t*)paramValue; bind->u.v8 = t; } break; case SQL_C_SHORT: @@ -1494,7 +1672,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1503,23 +1681,23 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T case TSDB_DATA_TYPE_NCHAR: { bind->buffer_type = type; bind->length = &bind->buffer_length; - switch (param->ValueType) { + switch (valueType) { case SQL_C_WCHAR: { - DASSERT(param->StrLen_or_Ind); - DASSERT(*param->StrLen_or_Ind != SQL_NTS); + DASSERT(soi); + DASSERT(*soi != SQL_NTS); size_t bytes = 0; - SQLCHAR *utf8 = wchars_to_chars(param->ParameterValue, *param->StrLen_or_Ind/2, &bytes); + 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*)param->ParameterValue; - if (*param->StrLen_or_Ind == SQL_NTS) { - bind->buffer_length = strlen((const char*)param->ParameterValue); + bind->u.nchar = (char*)paramValue; + if (*soi == SQL_NTS) { + bind->buffer_length = strlen((const char*)paramValue); } else { - bind->buffer_length = *param->StrLen_or_Ind; + bind->buffer_length = *soi; } bind->buffer = bind->u.nchar; } break; @@ -1549,7 +1727,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1558,7 +1736,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T 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(param->ValueType), param->ValueType, param->ValueType, + sql_c_type(valueType), valueType, valueType, taos_data_type(type), type, type, idx+1); return SQL_ERROR; } break; @@ -1566,27 +1744,61 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, T return SQL_SUCCESS; } -static SQLRETURN do_execute(sql_t *sql, TAOS_BIND *binds) +static SQLRETURN do_bind_batch(sql_t *sql, int idx_row, TAOS_BIND *binds) { - int tr = TSDB_CODE_SUCCESS; - for (int i=0; in_params; ++i) { - SQLRETURN r = do_bind_param_value(sql, i, sql->params+i, binds+i); + 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; } - // PROFILE(r = taos_stmt_add_batch(sql->stmt)); - // if (r) { - // SET_ERROR(sql, "HY000", r, "failed to add batch"); - // 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, TAOS_BIND *x_binds) +{ + 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)); @@ -1595,6 +1807,9 @@ static SQLRETURN do_execute(sql_t *sql, TAOS_BIND *binds) 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"); @@ -1745,22 +1960,37 @@ static SQLRETURN doSQLBindParameter( 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; } - param_bind_t *ar = (param_bind_t*)(sql->n_params>=ParameterNumber ? sql->params : realloc(sql->params, ParameterNumber * sizeof(*ar))); - if (!ar) { - SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, ""); + 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; } - sql->params = ar; - if (sql->n_paramsn_params = ParameterNumber; + + 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; } - param_bind_t *pb = ar + ParameterNumber - 1; + 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; @@ -2041,10 +2271,10 @@ static SQLRETURN doSQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar) 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; - } + // 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); @@ -2065,6 +2295,67 @@ SQLRETURN SQL_API SQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *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; +} @@ -2823,3 +3114,367 @@ static SQLRETURN conv_tsdb_str_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIE 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 SQL_SUCCESS; + return "22003"; +} + +const char* tsdb_int64_to_smallint(int64_t src, int16_t *dst) +{ + *dst = src; + if (src>=SHRT_MIN || src<=SHRT_MAX) return SQL_SUCCESS; + return "22003"; +} + +const char* tsdb_int64_to_int(int64_t src, int32_t *dst) +{ + *dst = src; + if (src>=LONG_MIN || src<=LONG_MAX) return SQL_SUCCESS; + 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), "%ld", 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, "%ld%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, "%ld%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, "%ld%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, "%ld%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, "%ld%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, "%ld%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 index 9bbe8e6987..b6b45d8120 100644 --- a/src/connector/odbc/src/todbc_util.c +++ b/src/connector/odbc/src/todbc_util.c @@ -99,6 +99,18 @@ const char* sql_c_type(int type) { } } +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, diff --git a/src/connector/odbc/src/todbc_util.h b/src/connector/odbc/src/todbc_util.h index a1521ef1cf..43264975b4 100644 --- a/src/connector/odbc/src/todbc_util.h +++ b/src/connector/odbc/src/todbc_util.h @@ -47,6 +47,8 @@ do { \ 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, diff --git a/src/connector/odbc/tests/odbc.py b/src/connector/odbc/tests/odbc.py index b1169679e3..61ad4f4379 100644 --- a/src/connector/odbc/tests/odbc.py +++ b/src/connector/odbc/tests/odbc.py @@ -43,10 +43,13 @@ cursor = cnxn.cursor() cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')") cursor.close() +print("hhhhhhhhhhhhhhhhhhhhhh") + cursor = cnxn.cursor() -cursor.execute("insert into db.t values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld"); +cursor.execute("insert into db.t values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 229, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld"); cursor.close() +print("hhhhhhhhhhhhhhhhhhhhhh") cursor = cnxn.cursor() cursor.execute("SELECT * from db.t") row = cursor.fetchone() @@ -79,3 +82,34 @@ while row: row = cursor.fetchone() cursor.close() +cursor = cnxn.cursor() +cursor.execute("create table db.v (ts timestamp, v1 tinyint)") +cursor.close() + +params = [ ('A', 1), ('B', 2), ('C', 3) ] +params = [ ('A', 1), ('B', 2), ('C', 3) ] +params = [ ('2020-10-16 00:00:00', 1), + ('2020-10-16 00:00:01', 4), + ('2020-10-16 00:00:02', 5), + ('2020-10-16 00:00:03', 6) ] +cursor = cnxn.cursor() +cursor.fast_executemany = True +cursor.executemany("insert into db.v values (?, ?)", params) +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("SELECT * from db.v") +row = cursor.fetchone() +while row: + print(row) + row = cursor.fetchone() +cursor.close() + +cursor = cnxn.cursor() +cursor.execute("SELECT * from db.v where v1 > ?", 4) +row = cursor.fetchone() +while row: + print(row) + row = cursor.fetchone() +cursor.close() + From 2436485e9b30102f66af6502fa022edfd21cff9f Mon Sep 17 00:00:00 2001 From: freemine Date: Sat, 17 Oct 2020 06:25:17 +0800 Subject: [PATCH 17/23] test case: with user-provided type other than table field type --- src/connector/odbc/tests/odbc.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/connector/odbc/tests/odbc.py b/src/connector/odbc/tests/odbc.py index 61ad4f4379..02ac975776 100644 --- a/src/connector/odbc/tests/odbc.py +++ b/src/connector/odbc/tests/odbc.py @@ -113,3 +113,11 @@ while 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() + From a6c918bf4208302ae7efd5515c1dbd75ce4e5855 Mon Sep 17 00:00:00 2001 From: freemine Date: Sat, 17 Oct 2020 06:50:28 +0800 Subject: [PATCH 18/23] 1. msvc: C4244, conversion-loss-of-data 2. msvc: C4477, "%ld" => "%" PRId64 "" --- src/client/src/tscPrepare.c | 41 +++++++++++++++++----------------- src/connector/odbc/src/todbc.c | 22 +++++++++--------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/client/src/tscPrepare.c b/src/client/src/tscPrepare.c index d6dd19db77..fe609da9a5 100644 --- a/src/client/src/tscPrepare.c +++ b/src/client/src/tscPrepare.c @@ -290,15 +290,15 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { if (u.v1==0 || u.v1==1) break; } break; case TSDB_DATA_TYPE_SMALLINT: { - u.v1 = *(int16_t*)bind->buffer; + u.v1 = (int8_t)*(int16_t*)bind->buffer; if (u.v1==0 || u.v1==1) break; } break; case TSDB_DATA_TYPE_INT: { - u.v1 = *(int32_t*)bind->buffer; + u.v1 = (int8_t)*(int32_t*)bind->buffer; if (u.v1==0 || u.v1==1) break; } break; case TSDB_DATA_TYPE_BIGINT: { - u.v1 = *(int64_t*)bind->buffer; + u.v1 = (int8_t)*(int64_t*)bind->buffer; if (u.v1==0 || u.v1==1) break; } break; case TSDB_DATA_TYPE_BINARY: @@ -334,19 +334,19 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { } break; case TSDB_DATA_TYPE_SMALLINT: { int16_t v = *(int16_t*)bind->buffer; - u.v1 = v; + 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 = v; + 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 = v; + u.v1 = (int8_t)v; if (v >= SCHAR_MIN && v <= SCHAR_MAX) break; return TSDB_CODE_TSC_INVALID_VALUE; } break; @@ -354,9 +354,9 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { case TSDB_DATA_TYPE_NCHAR: { int64_t v; int n,r; - r = sscanf((const char*)bind->buffer, "%ld%n", &v, &n); + r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); if (r==1 && n==strlen((const char*)bind->buffer)) { - u.v1 = v; + u.v1 = (int8_t)v; if (v >= SCHAR_MIN && v <= SCHAR_MAX) break; } return TSDB_CODE_TSC_INVALID_VALUE; @@ -377,17 +377,17 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { case TSDB_DATA_TYPE_TINYINT: case TSDB_DATA_TYPE_SMALLINT: { int v = *(int16_t*)bind->buffer; - u.v2 = v; + u.v2 = (int16_t)v; } break; case TSDB_DATA_TYPE_INT: { int32_t v = *(int32_t*)bind->buffer; - u.v2 = v; + 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 = v; + u.v2 = (int16_t)v; if (v >= SHRT_MIN && v <= SHRT_MAX) break; return TSDB_CODE_TSC_INVALID_VALUE; } break; @@ -395,9 +395,9 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { case TSDB_DATA_TYPE_NCHAR: { int64_t v; int n,r; - r = sscanf((const char*)bind->buffer, "%ld%n", &v, &n); + r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); if (r==1 && n==strlen((const char*)bind->buffer)) { - u.v2 = v; + u.v2 = (int16_t)v; if (v >= SHRT_MIN && v <= SHRT_MAX) break; } return TSDB_CODE_TSC_INVALID_VALUE; @@ -430,7 +430,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { case TSDB_DATA_TYPE_NCHAR: { int64_t v; int n,r; - r = sscanf((const char*)bind->buffer, "%ld%n", &v, &n); + r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); if (r==1 && n==strlen((const char*)bind->buffer)) { u.v4 = v; if (v >= INT_MIN && v <= INT_MAX) break; @@ -457,11 +457,11 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { u.f4 = *(int16_t*)bind->buffer; } break; case TSDB_DATA_TYPE_INT: { - u.f4 = *(int32_t*)bind->buffer; + u.f4 = (float)*(int32_t*)bind->buffer; // shall we check equality? } break; case TSDB_DATA_TYPE_BIGINT: { - u.f4 = *(int64_t*)bind->buffer; + u.f4 = (float)*(int64_t*)bind->buffer; // shall we check equality? } break; case TSDB_DATA_TYPE_FLOAT: { @@ -509,7 +509,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { case TSDB_DATA_TYPE_NCHAR: { int64_t v; int n,r; - r = sscanf((const char*)bind->buffer, "%ld%n", &v, &n); + r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); if (r==1 && n==strlen((const char*)bind->buffer)) { u.v8 = v; break; @@ -539,7 +539,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { u.f8 = *(int32_t*)bind->buffer; } break; case TSDB_DATA_TYPE_BIGINT: { - u.f8 = *(int64_t*)bind->buffer; + u.f8 = (double)*(int64_t*)bind->buffer; } break; case TSDB_DATA_TYPE_FLOAT: { u.f8 = *(float*)bind->buffer; @@ -553,7 +553,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { int n,r; r = sscanf((const char*)bind->buffer, "%lf%n", &v, &n); if (r==1 && n==strlen((const char*)bind->buffer)) { - u.v8 = v; + u.f8 = v; break; } return TSDB_CODE_TSC_INVALID_VALUE; @@ -574,7 +574,8 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { case TSDB_DATA_TYPE_BINARY: case TSDB_DATA_TYPE_NCHAR: { // is this the correct way to call taosParseTime? - if (taosParseTime(bind->buffer, &u.v8, *bind->length, 3, tsDaylight) == TSDB_CODE_SUCCESS) { + 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; diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 45f005a7ed..518c98b41f 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -2690,7 +2690,7 @@ static SQLRETURN conv_tsdb_v8_to_c_double(sql_t *sql, c_target_t *target, TAOS_F 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), "%ld", v8); + int n = snprintf(buf, sizeof(buf), "%" PRId64 "", v8); DASSERT(nsoi = n; strncpy(target->ptr, buf, (n>=target->len ? target->len : n+1)); @@ -2702,7 +2702,7 @@ static SQLRETURN conv_tsdb_v8_to_c_char(sql_t *sql, c_target_t *target, TAOS_FIE 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), "%ld", v8); + int n = snprintf(buf, sizeof(buf), "%" PRId64 "", v8); DASSERT(nsoi = n; strncpy(target->ptr, buf, (n>target->len ? target->len : n)); @@ -3053,7 +3053,7 @@ static SQLRETURN conv_tsdb_str_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIEL } char buf[64]; - snprintf(buf, sizeof(buf), "%ld", v); + snprintf(buf, sizeof(buf), "%" PRId64 "", v); if (strcmp(buf, str)==0) return SQL_SUCCESS; @@ -3156,7 +3156,7 @@ const char* tsdb_int64_to_ts(int64_t src, int64_t *dst) *dst = src; char buf[4096]; - int n = snprintf(buf, sizeof(buf), "%ld", src); + int n = snprintf(buf, sizeof(buf), "%" PRId64 "", src); DASSERT(n>=0); DASSERT(n Date: Sat, 17 Oct 2020 07:17:45 +0800 Subject: [PATCH 19/23] msvc: C4244 --- src/client/src/tscPrepare.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/src/tscPrepare.c b/src/client/src/tscPrepare.c index fe609da9a5..a579eacdea 100644 --- a/src/client/src/tscPrepare.c +++ b/src/client/src/tscPrepare.c @@ -422,7 +422,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { } break; case TSDB_DATA_TYPE_BIGINT: { int64_t v = *(int64_t*)bind->buffer; - u.v4 = v; + u.v4 = (int32_t)v; if (v >= INT_MIN && v <= INT_MAX) break; return TSDB_CODE_TSC_INVALID_VALUE; } break; From 273b8e1732ebfbf2ef1e4f124e6864a67ecaaf0a Mon Sep 17 00:00:00 2001 From: freemine Date: Sat, 17 Oct 2020 07:37:21 +0800 Subject: [PATCH 20/23] msvc: C4244 --- src/client/src/tscPrepare.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/src/tscPrepare.c b/src/client/src/tscPrepare.c index a579eacdea..1739e4348c 100644 --- a/src/client/src/tscPrepare.c +++ b/src/client/src/tscPrepare.c @@ -432,7 +432,7 @@ static int doBindParam(char* data, SParamInfo* param, TAOS_BIND* bind) { int n,r; r = sscanf((const char*)bind->buffer, "%" PRId64 "%n", &v, &n); if (r==1 && n==strlen((const char*)bind->buffer)) { - u.v4 = v; + u.v4 = (int32_t)v; if (v >= INT_MIN && v <= INT_MAX) break; } return TSDB_CODE_TSC_INVALID_VALUE; From b0ff31e1a276c131960205364beff44d91fe70a4 Mon Sep 17 00:00:00 2001 From: freemine Date: Sun, 18 Oct 2020 14:18:15 +0800 Subject: [PATCH 21/23] remove obsolete code --- src/CMakeLists.txt | 2 +- src/connector/odbc/src/todbc.c | 24 ++---------------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f619edd221..b2c6e70f68 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,5 +21,5 @@ ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(dnode) ADD_SUBDIRECTORY(connector/odbc) -ADD_SUBDIRECTORY(connector/jdbc) +#ADD_SUBDIRECTORY(connector/jdbc) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 518c98b41f..46d10fd980 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -1771,7 +1771,7 @@ static SQLRETURN do_bind_batch(sql_t *sql, int idx_row, TAOS_BIND *binds) return SQL_SUCCESS; } -static SQLRETURN do_execute(sql_t *sql, TAOS_BIND *x_binds) +static SQLRETURN do_execute(sql_t *sql) { int tr = TSDB_CODE_SUCCESS; if (sql->n_rows==0) sql->n_rows = 1; @@ -1836,27 +1836,7 @@ static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) sql->row = NULL; } - 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_execute(sql, 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); - } + SQLRETURN r = do_execute(sql); return r; } From 4d7a69d1905400b826ec053ae4c8f9d20909ce65 Mon Sep 17 00:00:00 2001 From: freemine Date: Sun, 18 Oct 2020 20:22:47 +0800 Subject: [PATCH 22/23] 1. support fraction in timestamp 2. trying to use tsdb_xxx_to_yyy --- src/connector/odbc/src/todbc.c | 124 ++++++++++++++++++------------- src/connector/odbc/tests/odbc.py | 7 +- 2 files changed, 74 insertions(+), 57 deletions(-) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 46d10fd980..953999c434 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -143,6 +143,19 @@ do { \ 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; @@ -541,7 +554,6 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, } sql->n_params = 0; - DASSERT(DEC_REF(sql->conn)>0); DASSERT(DEC_REF(sql)==0); @@ -796,10 +808,10 @@ static SQLRETURN conv_tsdb_f4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F 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, TIMESTAMP_STRUCT *ts); -static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIELD *field, TIMESTAMP_STRUCT *ts); -static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, TIMESTAMP_STRUCT *ts); -static SQLRETURN conv_tsdb_ts_to_c_ts(sql_t *sql, c_target_t *target, TAOS_FIELD *field, TIMESTAMP_STRUCT *ts); +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); @@ -987,7 +999,7 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, } } break; case TSDB_DATA_TYPE_TIMESTAMP: { - TIMESTAMP_STRUCT ts = {0}; + SQL_TIMESTAMP_STRUCT ts = {0}; int64_t v = *(int64_t*)row; time_t t = v/1000; struct tm tm = {0}; @@ -998,7 +1010,7 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ts.hour = tm.tm_hour; ts.minute = tm.tm_min; ts.second = tm.tm_sec; - ts.fraction = v%1000; + 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); @@ -1269,10 +1281,10 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin bind->length = &bind->buffer_length; switch (valueType) { case SQL_C_LONG: { - bind->u.b = *(int32_t*)paramValue; + CHK_CONV(tsdb_int64_to_bit(*(int32_t*)paramValue, &bind->u.b)); } break; case SQL_C_BIT: { - bind->u.b = *(int8_t*)paramValue; + CHK_CONV(tsdb_int64_to_bit(*(int8_t*)paramValue, &bind->u.b)); } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1313,16 +1325,16 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin bind->length = &bind->buffer_length; switch (valueType) { case SQL_C_TINYINT: { - bind->u.v1 = *(int8_t*)paramValue; + CHK_CONV(tsdb_int64_to_tinyint(*(int8_t*)paramValue, &bind->u.v1)); } break; case SQL_C_SHORT: { - bind->u.v1 = *(int16_t*)paramValue; + CHK_CONV(tsdb_int64_to_tinyint(*(int16_t*)paramValue, &bind->u.v1)); } break; case SQL_C_LONG: { - bind->u.v1 = *(int32_t*)paramValue; + CHK_CONV(tsdb_int64_to_tinyint(*(int32_t*)paramValue, &bind->u.v1)); } break; case SQL_C_SBIGINT: { - bind->u.v1 = *(int64_t*)paramValue; + CHK_CONV(tsdb_int64_to_tinyint(*(int64_t*)paramValue, &bind->u.v1)); } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1361,10 +1373,10 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin bind->length = &bind->buffer_length; switch (valueType) { case SQL_C_LONG: { - bind->u.v2 = *(int32_t*)paramValue; + CHK_CONV(tsdb_int64_to_smallint(*(int32_t*)paramValue, &bind->u.v2)); } break; case SQL_C_SHORT: { - bind->u.v2 = *(int16_t*)paramValue; + CHK_CONV(tsdb_int64_to_smallint(*(int16_t*)paramValue, &bind->u.v2)); } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1405,7 +1417,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin bind->length = &bind->buffer_length; switch (valueType) { case SQL_C_LONG: { - bind->u.v4 = *(int32_t*)paramValue; + CHK_CONV(tsdb_int64_to_int(*(int32_t*)paramValue, &bind->u.v4)); } break; case SQL_C_CHAR: case SQL_C_WCHAR: @@ -1635,13 +1647,20 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin 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); - struct tm tm = {0}; - strptime((const char*)utf8, "%Y-%m-%d %H:%M:%S", &tm); - int64_t t = (int64_t)mktime(&tm); - t *= 1000; + // 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; @@ -2152,23 +2171,24 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, if (ColumnSize) { *ColumnSize = field->bytes; } + if (DecimalDigits) *DecimalDigits = 0; if (DataType) { switch (field->type) { case TSDB_DATA_TYPE_BOOL: { - *DataType = SQL_C_TINYINT; + *DataType = SQL_TINYINT; } break; case TSDB_DATA_TYPE_TINYINT: { - *DataType = SQL_C_TINYINT; + *DataType = SQL_TINYINT; } break; case TSDB_DATA_TYPE_SMALLINT: { - *DataType = SQL_C_SHORT; + *DataType = SQL_SMALLINT; } break; case TSDB_DATA_TYPE_INT: { - *DataType = SQL_C_LONG; + *DataType = SQL_INTEGER; } break; case TSDB_DATA_TYPE_BIGINT: { @@ -2176,24 +2196,29 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, } break; case TSDB_DATA_TYPE_FLOAT: { - *DataType = SQL_C_FLOAT; + *DataType = SQL_FLOAT; } break; case TSDB_DATA_TYPE_DOUBLE: { - *DataType = SQL_C_DOUBLE; + *DataType = SQL_DOUBLE; } break; case TSDB_DATA_TYPE_TIMESTAMP: { - *DataType = SQL_C_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_C_CHAR; // unicode ? + *DataType = SQL_CHAR; // unicode ? if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE; } break; case TSDB_DATA_TYPE_BINARY: { - *DataType = SQL_C_BINARY; + *DataType = SQL_BINARY; if (ColumnSize) *ColumnSize -= VARSTR_HEADER_SIZE; } break; @@ -2204,13 +2229,6 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, break; } } - if (DecimalDigits) { - if (field->type == TSDB_DATA_TYPE_TIMESTAMP) { - *DecimalDigits = 3; - } else { - *DecimalDigits = 0; - } - } if (Nullable) { *Nullable = SQL_NULLABLE_UNKNOWN; } @@ -2340,10 +2358,6 @@ SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT StatementHandle, - - - - static void init_routine(void) { if (0) { string_conv(NULL, NULL, NULL, 0, NULL, 0, NULL, NULL); @@ -2763,7 +2777,7 @@ static SQLRETURN conv_tsdb_f8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F } -static SQLRETURN conv_tsdb_ts_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIELD *field, TIMESTAMP_STRUCT *ts) +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; @@ -2776,12 +2790,12 @@ static SQLRETURN conv_tsdb_ts_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIELD DASSERT(sizeof(t) == sizeof(int64_t)); int64_t v = (int64_t)t; v *= 1000; - v += ts->fraction % 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, 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) { struct tm tm = {0}; tm.tm_sec = ts->second; @@ -2797,7 +2811,10 @@ static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIEL *target->soi = n; - snprintf(target->ptr, target->len, "%s.%03d", buf, ts->fraction); + 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; @@ -2807,7 +2824,7 @@ static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIEL return SQL_SUCCESS_WITH_INFO; } -static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIELD *field, 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) { struct tm tm = {0}; tm.tm_sec = ts->second; @@ -2821,9 +2838,10 @@ static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIEL int n = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm); DASSERT(n < sizeof(buf)); - *target->soi = n; - - memcpy(target->ptr, buf, (n>target->len ? target->len : 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; @@ -2833,9 +2851,11 @@ static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIEL return SQL_SUCCESS_WITH_INFO; } -static SQLRETURN conv_tsdb_ts_to_c_ts(sql_t *sql, c_target_t *target, TAOS_FIELD *field, 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) { + DASSERT(target->len == sizeof(*ts)); memcpy(target->ptr, ts, sizeof(*ts)); + *target->soi = target->len; return SQL_SUCCESS; } @@ -3107,21 +3127,21 @@ const char* tsdb_int64_to_bit(int64_t src, int8_t *dst) const char* tsdb_int64_to_tinyint(int64_t src, int8_t *dst) { *dst = src; - if (src>=SCHAR_MIN || src<=SCHAR_MAX) return SQL_SUCCESS; + 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 SQL_SUCCESS; + 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 SQL_SUCCESS; + if (src>=LONG_MIN && src<=LONG_MAX) return NULL; return "22003"; } diff --git a/src/connector/odbc/tests/odbc.py b/src/connector/odbc/tests/odbc.py index 02ac975776..d2de8f39c6 100644 --- a/src/connector/odbc/tests/odbc.py +++ b/src/connector/odbc/tests/odbc.py @@ -43,13 +43,10 @@ cursor = cnxn.cursor() cursor.execute("insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')") cursor.close() -print("hhhhhhhhhhhhhhhhhhhhhh") - cursor = cnxn.cursor() -cursor.execute("insert into db.t values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 229, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld"); +cursor.execute("insert into db.t values(?,?,?,?,?,?,?,?,?,?)", "2020-10-13 07:06:00", 0, 127, 32767, 32768, 32769, 123.456, 789.987, "hel后lo", "wo哈rld"); cursor.close() -print("hhhhhhhhhhhhhhhhhhhhhh") cursor = cnxn.cursor() cursor.execute("SELECT * from db.t") row = cursor.fetchone() @@ -91,7 +88,7 @@ params = [ ('A', 1), ('B', 2), ('C', 3) ] params = [ ('2020-10-16 00:00:00', 1), ('2020-10-16 00:00:01', 4), ('2020-10-16 00:00:02', 5), - ('2020-10-16 00:00:03', 6) ] + ('2020-10-16 00:00:03.009', 6) ] cursor = cnxn.cursor() cursor.fast_executemany = True cursor.executemany("insert into db.v values (?, ?)", params) From 196106ade51a6078c5217995fbab8d031bf22dbc Mon Sep 17 00:00:00 2001 From: freemine Date: Sun, 18 Oct 2020 20:26:14 +0800 Subject: [PATCH 23/23] reenable jdbc --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b2c6e70f68..f619edd221 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,5 +21,5 @@ ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(dnode) ADD_SUBDIRECTORY(connector/odbc) -#ADD_SUBDIRECTORY(connector/jdbc) +ADD_SUBDIRECTORY(connector/jdbc)