From 23f49cca86c91de5b004a93a714b5daa3d9513ef Mon Sep 17 00:00:00 2001 From: freemine Date: Sat, 31 Oct 2020 13:39:39 +0800 Subject: [PATCH] add more data-type-conversions --- src/connector/odbc/src/todbc.c | 140 ++++++++++++++++------------ src/connector/odbc/src/todbc_conv.c | 15 +++ src/connector/odbc/src/todbc_conv.h | 3 + src/connector/odbc/tests/odbc.py | 14 +-- src/inc/taoserror.h | 1 + 5 files changed, 107 insertions(+), 66 deletions(-) diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 5180247bd2..03d47dad4d 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -182,6 +182,10 @@ do { \ SET_ERROR(sql, "22018", TSDB_CODE_ODBC_CONV_CHAR_NOT_NUM, ""); \ return SQL_ERROR; \ } break; \ + case TSDB_CONV_NOT_VALID_TS: { \ + SET_ERROR(sql, "22007", TSDB_CODE_ODBC_CONV_NOT_VALID_TS, ""); \ + return SQL_ERROR; \ + } break; \ case TSDB_CONV_TRUNC_FRACTION: { \ SET_ERROR(sql, "01S07", TSDB_CODE_ODBC_CONV_TRUNC_FRAC, ""); \ return todb ? SQL_ERROR : SQL_SUCCESS_WITH_INFO; \ @@ -1213,8 +1217,7 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, if (StrLen_or_Ind) *StrLen_or_Ind = (SQLLEN)len; CHK_CONV(0, code); } break; - case SQL_C_TYPE_TIMESTAMP: - case SQL_C_TIMESTAMP: { + case SQL_C_TYPE_TIMESTAMP: { *(SQL_TIMESTAMP_STRUCT*)TargetValue = ts; return SQL_SUCCESS; } break; @@ -1460,41 +1463,60 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin return SQL_SUCCESS; } bind->is_null = (int*)&no; - int type = 0; - int bytes = 0; + int tsdb_type = 0; // taos internal data tsdb_type to be bound to + int tsdb_bytes = 0; // we don't rely much on 'tsdb_bytes' here, we delay until taos to check it internally if (sql->is_insert) { - int r = taos_stmt_get_param(sql->stmt, idx, &type, &bytes); + int r = taos_stmt_get_param(sql->stmt, idx, &tsdb_type, &tsdb_bytes); if (r) { SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OUT_OF_RANGE, "parameter [@%d] not valid", idx+1); return SQL_ERROR; } } else { + // we don't have correspondent data type from taos api + // we have to give a good guess here switch (valueType) { + case SQL_C_BIT: { + tsdb_type = TSDB_DATA_TYPE_BOOL; + } break; + case SQL_C_STINYINT: + case SQL_C_TINYINT: { + tsdb_type = TSDB_DATA_TYPE_TINYINT; + } break; + case SQL_C_SSHORT: + case SQL_C_SHORT: { + tsdb_type = TSDB_DATA_TYPE_SMALLINT; + } break; case SQL_C_SLONG: case SQL_C_LONG: { - type = TSDB_DATA_TYPE_INT; + tsdb_type = TSDB_DATA_TYPE_INT; + } break; + case SQL_C_SBIGINT: { + tsdb_type = TSDB_DATA_TYPE_BIGINT; + } break; + case SQL_C_FLOAT: { + tsdb_type = TSDB_DATA_TYPE_FLOAT; + } break; + case SQL_C_DOUBLE: { + tsdb_type = TSDB_DATA_TYPE_DOUBLE; + } break; + case SQL_C_TIMESTAMP: { + tsdb_type = TSDB_DATA_TYPE_TIMESTAMP; + } break; + case SQL_C_CHAR: { + tsdb_type = TSDB_DATA_TYPE_BINARY; + tsdb_bytes = SQL_NTS; } break; case SQL_C_WCHAR: { - type = TSDB_DATA_TYPE_NCHAR; - bytes = SQL_NTS; + tsdb_type = TSDB_DATA_TYPE_NCHAR; + tsdb_bytes = SQL_NTS; } break; - case SQL_C_CHAR: - case SQL_C_SHORT: - case SQL_C_SSHORT: case SQL_C_USHORT: case SQL_C_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: @@ -1511,9 +1533,9 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin } // ref: https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/converting-data-from-c-to-sql-data-types?view=sql-server-ver15 - switch (type) { + switch (tsdb_type) { case TSDB_DATA_TYPE_BOOL: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->buffer_length = sizeof(bind->u.b); bind->buffer = &bind->u.b; bind->length = &bind->buffer_length; @@ -1548,7 +1570,6 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin size_t slen = (size_t)*soi; if (slen==SQL_NTS) slen = strlen((const char*)paramValue); CHK_CONV(1, tsdb_conv_chars_to_bit(client_to_utf8, &buffer, (const char *)paramValue, slen, &bind->u.b)); - // CHK_CONV(1, tsdb_chars_to_bit((const char *)paramValue, (size_t)*soi, &bind->u.b)); } break; case SQL_C_WCHAR: { CHK_CONV(1, tsdb_wchars_to_bit(sql_get_w2c(sql), (const unsigned char*)paramValue, (size_t)*soi, &bind->u.b)); @@ -1570,13 +1591,13 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_TINYINT: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->buffer_length = sizeof(bind->u.v1); bind->buffer = &bind->u.v1; bind->length = &bind->buffer_length; @@ -1629,13 +1650,13 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_SMALLINT: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->buffer_length = sizeof(bind->u.v2); bind->buffer = &bind->u.v2; bind->length = &bind->buffer_length; @@ -1688,13 +1709,13 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_INT: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->buffer_length = sizeof(bind->u.v4); bind->buffer = &bind->u.v4; bind->length = &bind->buffer_length; @@ -1747,13 +1768,13 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_BIGINT: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->buffer_length = sizeof(bind->u.v8); bind->buffer = &bind->u.v8; bind->length = &bind->buffer_length; @@ -1806,13 +1827,13 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_FLOAT: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->buffer_length = sizeof(bind->u.f4); bind->buffer = &bind->u.f4; bind->length = &bind->buffer_length; @@ -1874,13 +1895,13 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_DOUBLE: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->buffer_length = sizeof(bind->u.f8); bind->buffer = &bind->u.f8; bind->length = &bind->buffer_length; @@ -1942,13 +1963,13 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_TIMESTAMP: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->buffer_length = sizeof(bind->u.v8); bind->buffer = &bind->u.v8; bind->length = &bind->buffer_length; @@ -1971,6 +1992,22 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin int64_t t = *(int64_t*)paramValue; bind->u.v8 = t; } break; + case SQL_C_TYPE_TIMESTAMP: { + SQL_TIMESTAMP_STRUCT ts = *(SQL_TIMESTAMP_STRUCT*)paramValue; + struct tm vtm = {0}; + vtm.tm_year = ts.year - 1900; + vtm.tm_mon = ts.month - 1; + vtm.tm_mday = ts.day; + vtm.tm_hour = ts.hour; + vtm.tm_min = ts.minute; + vtm.tm_sec = ts.second; + int64_t t = (int64_t) mktime(&vtm); + if (t==-1) { + CHK_CONV(1, TSDB_CONV_NOT_VALID_TS); + // code never reached here + } + bind->u.ts = t * 1000 + ts.fraction / 1000000; + } break; case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: @@ -1990,20 +2027,19 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin case SQL_C_TIMESTAMP: case SQL_C_TYPE_DATE: case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: case SQL_C_NUMERIC: case SQL_C_GUID: default: { SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_BINARY: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->length = &bind->buffer_length; switch (valueType) { case SQL_C_WCHAR: { @@ -2039,13 +2075,6 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin bind->buffer = bind->u.bin; } CHK_CONV(1, code); - // bind->u.bin = (unsigned char*)paramValue; - // if (*soi == SQL_NTS) { - // bind->buffer_length = strlen((const char*)paramValue); - // } else { - // bind->buffer_length = (uintptr_t)*soi; - // } - // bind->buffer = bind->u.bin; } break; case SQL_C_SHORT: case SQL_C_SSHORT: @@ -2055,32 +2084,32 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin 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_BIT: 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_TYPE_TIMESTAMP: // we don't provide auto-converstion case SQL_C_NUMERIC: case SQL_C_GUID: default: { SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } } break; case TSDB_DATA_TYPE_NCHAR: { - bind->buffer_type = type; + bind->buffer_type = tsdb_type; bind->length = &bind->buffer_length; switch (valueType) { case SQL_C_WCHAR: { @@ -2116,13 +2145,6 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin bind->buffer = bind->u.bin; } CHK_CONV(1, code); - // bind->u.nchar = (char*)paramValue; - // if (*soi == SQL_NTS) { - // bind->buffer_length = strlen((const char*)paramValue); - // } else { - // bind->buffer_length = (uintptr_t)*soi; - // } - // bind->buffer = bind->u.nchar; } break; case SQL_C_SHORT: case SQL_C_SSHORT: @@ -2144,14 +2166,14 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin case SQL_C_TIMESTAMP: case SQL_C_TYPE_DATE: case SQL_C_TYPE_TIME: - case SQL_C_TYPE_TIMESTAMP: + case SQL_C_TYPE_TIMESTAMP: // we don't provide auto-converstion case SQL_C_NUMERIC: case SQL_C_GUID: default: { SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } @@ -2160,7 +2182,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin SET_ERROR(sql, "HYC00", TSDB_CODE_ODBC_OUT_OF_RANGE, "no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", sql_c_type(valueType), valueType, valueType, - taos_data_type(type), type, type, idx+1); + taos_data_type(tsdb_type), tsdb_type, tsdb_type, idx+1); return SQL_ERROR; } break; } diff --git a/src/connector/odbc/src/todbc_conv.c b/src/connector/odbc/src/todbc_conv.c index aaca1f75f7..4f7171d149 100644 --- a/src/connector/odbc/src/todbc_conv.c +++ b/src/connector/odbc/src/todbc_conv.c @@ -62,6 +62,7 @@ const char* tsdb_conv_code_str(TSDB_CONV_CODE code) { case TSDB_CONV_TRUNC: return "TSDB_CONV_TRUNC"; case TSDB_CONV_CHAR_NOT_NUM: return "TSDB_CONV_CHAR_NOT_NUM"; case TSDB_CONV_CHAR_NOT_TS: return "TSDB_CONV_CHAR_NOT_TS"; + case TSDB_CONV_NOT_VALID_TS: return "TSDB_CONV_NOT_VALID_TS"; case TSDB_CONV_GENERAL: return "TSDB_CONV_GENERAL"; case TSDB_CONV_BAD_CHAR: return "TSDB_CONV_BAD_CHAR"; default: return "UNKNOWN"; @@ -592,7 +593,20 @@ struct tsdb_conv_s { unsigned int direct:1; }; +static tsdb_conv_t no_conversion = {0}; +static pthread_once_t once = PTHREAD_ONCE_INIT; +static void once_init(void) { + no_conversion.cnv = (iconv_t)-1; + no_conversion.direct = 1; +} + +tsdb_conv_t* tsdb_conv_direct() { // get a non-conversion-converter + pthread_once(&once, once_init); + return &no_conversion; +} + tsdb_conv_t* tsdb_conv_open(const char *from_enc, const char *to_enc) { + pthread_once(&once, once_init); tsdb_conv_t *cnv = (tsdb_conv_t*)calloc(1, sizeof(*cnv)); if (!cnv) return NULL; if (strcmp(from_enc, to_enc)==0) { @@ -611,6 +625,7 @@ tsdb_conv_t* tsdb_conv_open(const char *from_enc, const char *to_enc) { void tsdb_conv_close(tsdb_conv_t *cnv) { if (!cnv) return; + if (cnv == &no_conversion) return; if (!cnv->direct) { if (cnv->cnv != (iconv_t)-1) { iconv_close(cnv->cnv); diff --git a/src/connector/odbc/src/todbc_conv.h b/src/connector/odbc/src/todbc_conv.h index 529cc10e93..2075d22870 100644 --- a/src/connector/odbc/src/todbc_conv.h +++ b/src/connector/odbc/src/todbc_conv.h @@ -30,6 +30,7 @@ typedef enum { TSDB_CONV_TRUNC, TSDB_CONV_CHAR_NOT_NUM, TSDB_CONV_CHAR_NOT_TS, + TSDB_CONV_NOT_VALID_TS, TSDB_CONV_GENERAL, TSDB_CONV_BAD_CHAR, } TSDB_CONV_CODE; @@ -46,8 +47,10 @@ char* stack_buffer_alloc(stack_buffer_t *buffer, size_t bytes); int is_owned_by_stack_buffer(stack_buffer_t *buffer, const char *ptr); typedef struct tsdb_conv_s tsdb_conv_t; +tsdb_conv_t* tsdb_conv_direct(); // get a non-conversion-converter tsdb_conv_t* tsdb_conv_open(const char *from_enc, const char *to_enc); void tsdb_conv_close(tsdb_conv_t *cnv); + TSDB_CONV_CODE tsdb_conv_write(tsdb_conv_t *cnv, const char *src, size_t *slen, char *dst, size_t *dlen); TSDB_CONV_CODE tsdb_conv_write_int64(tsdb_conv_t *cnv, int64_t val, char *dst, size_t *dlen); TSDB_CONV_CODE tsdb_conv_write_double(tsdb_conv_t *cnv, double val, char *dst, size_t *dlen); diff --git a/src/connector/odbc/tests/odbc.py b/src/connector/odbc/tests/odbc.py index 01dd52852b..c137905775 100644 --- a/src/connector/odbc/tests/odbc.py +++ b/src/connector/odbc/tests/odbc.py @@ -5,13 +5,13 @@ cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8') #cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8') #cnxn.setencoding(encoding='utf-8') -cursor = cnxn.cursor() -cursor.execute("SELECT * from db.t") -row = cursor.fetchone() -while row: - print(row) - row = cursor.fetchone() -cursor.close() +#cursor = cnxn.cursor() +#cursor.execute("SELECT * from db.t") +#row = cursor.fetchone() +#while row: +# print(row) +# row = cursor.fetchone() +#cursor.close() #cursor = cnxn.cursor() #cursor.execute(""" diff --git a/src/inc/taoserror.h b/src/inc/taoserror.h index 5330ca0bad..d15d162d6c 100644 --- a/src/inc/taoserror.h +++ b/src/inc/taoserror.h @@ -379,6 +379,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_STATEMENT_NOT_READY, 0, 0x210d, "statement TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONNECTION_BUSY, 0, 0x210e, "connection still busy") TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_BAD_CONNSTR, 0, 0x210f, "bad connection string") TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_BAD_ARG, 0, 0x2110, "bad argument") +TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_NOT_VALID_TS, 0, 0x2111, "not a valid timestamp") #ifdef TAOS_ERROR_C -- GitLab