From 1c52e6598e723024fe5e782797aa44ae6434929d Mon Sep 17 00:00:00 2001 From: freemine Date: Sat, 17 Oct 2020 06:11:21 +0800 Subject: [PATCH] 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() + -- GitLab