提交 8d325f16 编写于 作者: F freemine

1. support SQLWCHAR

2. more precise error message, according ODBC doc
上级 e88979de
...@@ -637,3 +637,59 @@ TAOS_RES *taos_stmt_use_result(TAOS_STMT* stmt) { ...@@ -637,3 +637,59 @@ TAOS_RES *taos_stmt_use_result(TAOS_STMT* stmt) {
pStmt->pSql = NULL; pStmt->pSql = NULL;
return result; 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";
}
}
...@@ -13,34 +13,22 @@ ...@@ -13,34 +13,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// #define _BSD_SOURCE
#define _XOPEN_SOURCE
#define _DEFAULT_SOURCE
#include "taos.h" #include "taos.h"
#include "os.h" #include "os.h"
#include "taoserror.h" #include "taoserror.h"
#include "todbc_util.h"
#include <sql.h> #include <sql.h>
#include <sqlext.h> #include <sqlext.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> #include <time.h>
#define D(fmt, ...) \
fprintf(stderr, \
"%s[%d]:%s() " fmt "\n", \
basename((char*)__FILE__), __LINE__, __func__, \
##__VA_ARGS__)
#define DASSERT(statement) \
do { \
if (statement) break; \
D("Assertion failure: %s", #statement); \
abort(); \
} while (0)
#define GET_REF(obj) atomic_load_64(&obj->refcount) #define GET_REF(obj) atomic_load_64(&obj->refcount)
#define INC_REF(obj) atomic_add_fetch_64(&obj->refcount, 1) #define INC_REF(obj) atomic_add_fetch_64(&obj->refcount, 1)
#define DEC_REF(obj) atomic_sub_fetch_64(&obj->refcount, 1) #define DEC_REF(obj) atomic_sub_fetch_64(&obj->refcount, 1)
...@@ -76,6 +64,7 @@ do { ...@@ -76,6 +64,7 @@ do {
if (NativeError) *NativeError = obj->err.err_no; \ if (NativeError) *NativeError = obj->err.err_no; \
snprintf((char*)MessageText, BufferLength, "%s", obj->err.err_str); \ 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 = strlen(obj->err.err_str); \
if (TextLength && obj->err.err_str) *TextLength = utf8_chars(obj->err.err_str); \
} while (0) } while (0)
#define FREE_ERROR(obj) \ #define FREE_ERROR(obj) \
...@@ -90,7 +79,7 @@ do { \ ...@@ -90,7 +79,7 @@ do { \
#define SET_UNSUPPORT_ERROR(obj, sqlstate, err_fmt, ...) \ #define SET_UNSUPPORT_ERROR(obj, sqlstate, err_fmt, ...) \
do { \ 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) \ } while (0) \
#define SET_HANDLE_INVALID(obj, sqlstate, err_fmt, ...) \ #define SET_HANDLE_INVALID(obj, sqlstate, err_fmt, ...) \
...@@ -108,6 +97,22 @@ do { \ ...@@ -108,6 +97,22 @@ do { \
} \ } \
} while (0) } 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, ...) \ #define CHK_RS(r_091c, sql_091c, fmt_091c, ...) \
do { \ do { \
r_091c = SQL_ERROR; \ r_091c = SQL_ERROR; \
...@@ -184,7 +189,6 @@ struct sql_s { ...@@ -184,7 +189,6 @@ struct sql_s {
conn_t *conn; conn_t *conn;
TAOS_STMT *stmt; TAOS_STMT *stmt;
TAOS_BIND *binds;
param_bind_t *params; param_bind_t *params;
int n_params; int n_params;
TAOS_RES *rs; TAOS_RES *rs;
...@@ -205,8 +209,9 @@ struct c_target_s { ...@@ -205,8 +209,9 @@ struct c_target_s {
static pthread_once_t init_once = PTHREAD_ONCE_INIT; static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static void init_routine(void); static void init_routine(void);
static int do_field_display_size(TAOS_FIELD *field); 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) static SQLRETURN doSQLAllocEnv(SQLHENV *EnvironmentHandle)
{ {
...@@ -269,7 +274,7 @@ static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle, ...@@ -269,7 +274,7 @@ static SQLRETURN doSQLAllocConnect(SQLHENV EnvironmentHandle,
do { do {
conn = (conn_t*)calloc(1, sizeof(*conn)); conn = (conn_t*)calloc(1, sizeof(*conn));
if (!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; break;
} }
...@@ -336,7 +341,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, ...@@ -336,7 +341,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle,
if (!conn) return SQL_ERROR; if (!conn) return SQL_ERROR;
if (conn->taos) { 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; return SQL_ERROR;
} }
...@@ -346,7 +351,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, ...@@ -346,7 +351,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle,
do { do {
if ((ServerName && !serverName) || (UserName && !userName) || (Authentication && !auth)) { 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; break;
} }
...@@ -354,7 +359,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, ...@@ -354,7 +359,7 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle,
// TODO: shall receive ip/port from odbc.ini // TODO: shall receive ip/port from odbc.ini
conn->taos = taos_connect("localhost", userName, auth, NULL, 0); conn->taos = taos_connect("localhost", userName, auth, NULL, 0);
if (!conn->taos) { 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; break;
} }
} while (0); } while (0);
...@@ -409,7 +414,7 @@ static SQLRETURN doSQLAllocStmt(SQLHDBC ConnectionHandle, ...@@ -409,7 +414,7 @@ static SQLRETURN doSQLAllocStmt(SQLHDBC ConnectionHandle,
do { do {
sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); sql_t *sql = (sql_t*)calloc(1, sizeof(*sql));
if (!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; break;
} }
...@@ -442,7 +447,7 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, ...@@ -442,7 +447,7 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle,
if (Option == SQL_CLOSE) return SQL_SUCCESS; if (Option == SQL_CLOSE) return SQL_SUCCESS;
if (Option != SQL_DROP) { 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; return SQL_ERROR;
} }
...@@ -458,10 +463,6 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle, ...@@ -458,10 +463,6 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle,
sql->stmt = NULL; sql->stmt = NULL;
} }
if (sql->binds) {
free(sql->binds);
sql->binds = NULL;
}
if (sql->params) { if (sql->params) {
free(sql->params); free(sql->params);
sql->params = NULL; sql->params = NULL;
...@@ -494,15 +495,8 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, ...@@ -494,15 +495,8 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle,
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { if (sql->rs) {
taos_free_result(sql->rs); taos_free_result(sql->rs);
...@@ -515,11 +509,6 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, ...@@ -515,11 +509,6 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle,
sql->stmt = NULL; sql->stmt = NULL;
} }
if (sql->binds) {
free(sql->binds);
sql->binds = NULL;
}
if (sql->params) { if (sql->params) {
free(sql->params); free(sql->params);
sql->params = NULL; sql->params = NULL;
...@@ -531,11 +520,11 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle, ...@@ -531,11 +520,11 @@ static SQLRETURN doSQLExecDirect(SQLHSTMT StatementHandle,
SQLRETURN r = SQL_ERROR; SQLRETURN r = SQL_ERROR;
do { do {
if (!stxt) { if (!stxt) {
SET_ERROR(sql, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources"); SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, "");
break; break;
} }
sql->rs = taos_query(sql->conn->taos, stxt); sql->rs = taos_query(sql->conn->taos, stxt);
CHK_RS(r, sql, "failed to query"); CHK_RS(r, sql, "failed to execute");
} while (0); } while (0);
SFRE(stxt, StatementText, TextLength); SFRE(stxt, StatementText, TextLength);
...@@ -551,24 +540,24 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, ...@@ -551,24 +540,24 @@ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle,
return r; 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, static SQLRETURN doSQLNumResultCols(SQLHSTMT StatementHandle,
SQLSMALLINT *ColumnCount) SQLSMALLINT *ColumnCount)
{ {
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { 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; return SQL_ERROR;
} }
...@@ -594,18 +583,11 @@ static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle, ...@@ -594,18 +583,11 @@ static SQLRETURN doSQLRowCount(SQLHSTMT StatementHandle,
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { 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; return SQL_ERROR;
} }
...@@ -629,22 +611,14 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle, ...@@ -629,22 +611,14 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle,
SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength,
SQLSMALLINT *StringLength, SQLLEN *NumericAttribute ) SQLSMALLINT *StringLength, SQLLEN *NumericAttribute )
{ {
D("......");
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { 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; return SQL_ERROR;
} }
...@@ -652,22 +626,12 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle, ...@@ -652,22 +626,12 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle,
TAOS_FIELD *fields = taos_fetch_fields(sql->rs); TAOS_FIELD *fields = taos_fetch_fields(sql->rs);
if (nfields==0 || fields==NULL) { 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; return SQL_ERROR;
} }
if (ColumnNumber==0) { if (ColumnNumber<=0 || ColumnNumber>nfields) {
SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "ColumnNumber[0] not supported"); SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber);
return SQL_ERROR;
}
if (ColumnNumber>nfields) {
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_APP_ERROR, "ColumnNumber[%d] overflow", ColumnNumber);
return SQL_ERROR; return SQL_ERROR;
} }
...@@ -685,9 +649,9 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle, ...@@ -685,9 +649,9 @@ static SQLRETURN doSQLColAttribute(SQLHSTMT StatementHandle,
*NumericAttribute = SQL_FALSE; *NumericAttribute = SQL_FALSE;
} break; } break;
default: { default: {
SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, SET_ERROR(sql, "HY091", TSDB_CODE_ODBC_OUT_OF_RANGE,
"ColumnNumber[%d] FieldIdentifier[%d] not supported yet", "FieldIdentifier[%d/0x%x] for Column [%d] not supported yet",
ColumnNumber, FieldIdentifier); FieldIdentifier, FieldIdentifier, ColumnNumber);
return SQL_ERROR; return SQL_ERROR;
} break; } break;
} }
...@@ -772,23 +736,16 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -772,23 +736,16 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { 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; return SQL_ERROR;
} }
if (!sql->row) { 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; return SQL_ERROR;
} }
...@@ -797,23 +754,8 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -797,23 +754,8 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
int nfields = taos_field_count(sql->rs); int nfields = taos_field_count(sql->rs);
TAOS_FIELD *fields = taos_fetch_fields(sql->rs); TAOS_FIELD *fields = taos_fetch_fields(sql->rs);
if (nfields==0 || fields==NULL) { if (ColumnNumber<=0 || ColumnNumber>nfields) {
SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "no fields in result set"); SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber);
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; return SQL_ERROR;
} }
...@@ -834,8 +776,6 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -834,8 +776,6 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
target.len = BufferLength; target.len = BufferLength;
target.soi = StrLen_or_Ind; target.soi = StrLen_or_Ind;
SQLRETURN r = SQL_ERROR;
switch (field->type) { switch (field->type) {
case TSDB_DATA_TYPE_BOOL: { case TSDB_DATA_TYPE_BOOL: {
int8_t v = *(int8_t*)row; int8_t v = *(int8_t*)row;
...@@ -851,7 +791,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_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); case SQL_C_BINARY: return conv_tsdb_bool_to_c_binary(sql, &target, field, v);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -868,7 +810,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_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); case SQL_C_BINARY: return conv_tsdb_v1_to_c_binary(sql, &target, field, v);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -884,7 +828,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_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); case SQL_C_BINARY: return conv_tsdb_v2_to_c_binary(sql, &target, field, v);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -899,7 +845,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_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); case SQL_C_BINARY: return conv_tsdb_v4_to_c_binary(sql, &target, field, v);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -913,7 +861,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_CHAR: return conv_tsdb_v8_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_v8_to_c_binary(sql, &target, field, v); case SQL_C_BINARY: return conv_tsdb_v8_to_c_binary(sql, &target, field, v);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -926,7 +876,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_CHAR: return conv_tsdb_f4_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_f4_to_c_binary(sql, &target, field, v); case SQL_C_BINARY: return conv_tsdb_f4_to_c_binary(sql, &target, field, v);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -938,7 +890,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_CHAR: return conv_tsdb_f8_to_c_char(sql, &target, field, v);
case SQL_C_BINARY: return conv_tsdb_f8_to_c_binary(sql, &target, field, v); case SQL_C_BINARY: return conv_tsdb_f8_to_c_binary(sql, &target, field, v);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -962,7 +916,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_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); case SQL_C_TIMESTAMP: return conv_tsdb_ts_to_c_ts(sql, &target, field, &ts);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -973,7 +929,9 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_CHAR: return conv_tsdb_bin_to_c_str(sql, &target, field, bin);
case SQL_C_BINARY: return conv_tsdb_bin_to_c_bin(sql, &target, field, bin); case SQL_C_BINARY: return conv_tsdb_bin_to_c_bin(sql, &target, field, bin);
default: { 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; return SQL_ERROR;
} }
} }
...@@ -991,81 +949,21 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle, ...@@ -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_CHAR: return conv_tsdb_str_to_c_str(sql, &target, field, str);
case SQL_C_BINARY: return conv_tsdb_str_to_c_bin(sql, &target, field, str); case SQL_C_BINARY: return conv_tsdb_str_to_c_bin(sql, &target, field, str);
default: { 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; return SQL_ERROR;
} }
} }
} break; } break;
default: { 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; return SQL_ERROR;
} break; } 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, SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle,
...@@ -1085,18 +983,11 @@ static SQLRETURN doSQLFetch(SQLHSTMT StatementHandle) ...@@ -1085,18 +983,11 @@ static SQLRETURN doSQLFetch(SQLHSTMT StatementHandle)
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { 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; return SQL_ERROR;
} }
...@@ -1117,15 +1008,8 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, ...@@ -1117,15 +1008,8 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle,
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { if (sql->rs) {
taos_free_result(sql->rs); taos_free_result(sql->rs);
...@@ -1138,10 +1022,6 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, ...@@ -1138,10 +1022,6 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle,
sql->stmt = NULL; sql->stmt = NULL;
} }
if (sql->binds) {
free(sql->binds);
sql->binds = NULL;
}
if (sql->params) { if (sql->params) {
free(sql->params); free(sql->params);
sql->params = NULL; sql->params = NULL;
...@@ -1151,13 +1031,13 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle, ...@@ -1151,13 +1031,13 @@ static SQLRETURN doSQLPrepare(SQLHSTMT StatementHandle,
do { do {
sql->stmt = taos_stmt_init(sql->conn->taos); sql->stmt = taos_stmt_init(sql->conn->taos);
if (!sql->stmt) { 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; break;
} }
int r = taos_stmt_prepare(sql->stmt, (const char *)StatementText, TextLength); int r = taos_stmt_prepare(sql->stmt, (const char *)StatementText, TextLength);
if (r) { 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); taos_stmt_close(sql->stmt);
sql->stmt = NULL; sql->stmt = NULL;
break; break;
...@@ -1175,89 +1055,525 @@ SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, ...@@ -1175,89 +1055,525 @@ SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle,
return r; return r;
} }
static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) static const int yes = 1;
{ static const int no = 0;
sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR;
if (!sql->conn) { static SQLRETURN do_bind_param_value(sql_t *sql, int idx, param_bind_t *param, TAOS_BIND *bind)
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; return SQL_ERROR;
} }
if (param->StrLen_or_Ind && *param->StrLen_or_Ind == SQL_NULL_DATA) {
if (!sql->conn->taos) { bind->is_null = (int*)&yes;
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection to data source yet"); 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; return SQL_ERROR;
} }
if (!sql->stmt) { // ref: https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/converting-data-from-c-to-sql-data-types?view=sql-server-ver15
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_SQL, "no statement cached or not ready"); 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 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, "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; return SQL_ERROR;
} break;
} }
} break;
if (sql->rs) { case TSDB_DATA_TYPE_TINYINT: {
taos_free_result(sql->rs); bind->buffer_type = type;
sql->rs = NULL; bind->buffer_length = sizeof(bind->u.v1);
sql->row = NULL; 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;
int r = 0; case TSDB_DATA_TYPE_SMALLINT: {
bind->buffer_type = type;
for (int i=0; i<sql->n_params; ++i) { bind->buffer_length = sizeof(bind->u.v2);
param_bind_t *pb = sql->params + i; bind->buffer = &bind->u.v2;
if (!pb->valid) { bind->length = &bind->buffer_length;
SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "default parameter [@%d] not supported yet", i+1); 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; return SQL_ERROR;
} break;
} }
TAOS_BIND *b = sql->binds + i; } break;
int yes = 1; case TSDB_DATA_TYPE_BINARY: {
int no = 0; bind->buffer_type = type;
if (pb->StrLen_or_Ind && *pb->StrLen_or_Ind == SQL_NULL_DATA) { bind->length = &bind->buffer_length;
b->is_null = &yes; 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 { } else {
b->is_null = &no; bind->buffer_length = *param->StrLen_or_Ind;
switch (b->buffer_type) { }
case TSDB_DATA_TYPE_BOOL: bind->buffer = bind->u.bin;
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; } break;
case TSDB_DATA_TYPE_BINARY: case SQL_C_CHAR:
case TSDB_DATA_TYPE_NCHAR: { case SQL_C_SHORT:
if (!pb->StrLen_or_Ind) { case SQL_C_SSHORT:
SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "value [@%d] bad StrLen_or_Ind", i+1); 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; return SQL_ERROR;
} break;
} }
size_t n = *pb->StrLen_or_Ind; } break;
if (n == SQL_NTS) { case TSDB_DATA_TYPE_TIMESTAMP: {
n = strlen(pb->ParameterValue); bind->buffer_type = type;
} else if (n < 0 || n > b->buffer_length) { bind->buffer_length = sizeof(bind->u.v8);
SET_ERROR(sql, "HY000", TSDB_CODE_COM_OPS_NOT_SUPPORT, "value [@%d] bad StrLen_or_Ind", i+1); 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; return SQL_ERROR;
} break;
} }
b->buffer_length = n;
b->length = &b->buffer_length;
b->buffer = pb->ParameterValue;
} 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: { 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; return SQL_ERROR;
} break; } 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; i<sql->n_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) { 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) { 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; return SQL_ERROR;
} }
...@@ -1274,12 +1590,54 @@ static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle) ...@@ -1274,12 +1590,54 @@ static SQLRETURN doSQLExecute(SQLHSTMT StatementHandle)
return SQL_ERROR; return SQL_ERROR;
} }
SQLRETURN ret = SQL_ERROR;
PROFILE(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"); 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; i<sql->n_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) SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle)
...@@ -1373,159 +1731,30 @@ static SQLRETURN doSQLBindParameter( ...@@ -1373,159 +1731,30 @@ static SQLRETURN doSQLBindParameter(
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { 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; return SQL_ERROR;
} }
if (fParamType != SQL_PARAM_INPUT) { 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; 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))); 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) {
if (!ar || !binds) { SET_ERROR(sql, "HY001", TSDB_CODE_ODBC_OOM, "");
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; return SQL_ERROR;
} }
sql->params = ar; sql->params = ar;
sql->binds = binds;
if (sql->n_params<ParameterNumber) { if (sql->n_params<ParameterNumber) {
sql->n_params = ParameterNumber; sql->n_params = ParameterNumber;
} }
param_bind_t *pb = ar + ParameterNumber - 1; 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->ParameterNumber = ParameterNumber;
pb->ValueType = ValueType; pb->ValueType = ValueType;
...@@ -1571,12 +1800,12 @@ static SQLRETURN doSQLDriverConnect( ...@@ -1571,12 +1800,12 @@ static SQLRETURN doSQLDriverConnect(
if (!conn) return SQL_ERROR; if (!conn) return SQL_ERROR;
if (fDriverCompletion!=SQL_DRIVER_NOPROMPT) { 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; return SQL_ERROR;
} }
if (conn->taos) { 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; return SQL_ERROR;
} }
...@@ -1591,18 +1820,16 @@ static SQLRETURN doSQLDriverConnect( ...@@ -1591,18 +1820,16 @@ static SQLRETURN doSQLDriverConnect(
do { do {
if (szConnStrIn && !connStr) { if (szConnStrIn && !connStr) {
SET_ERROR(conn, "HY000", TSDB_CODE_ODBC_OOM, "failed to allocate resources"); SET_ERROR(conn, "HY001", TSDB_CODE_ODBC_OOM, "");
break; break;
} }
int n = sscanf((const char*)connStr, "DSN=%m[^;]; UID=%m[^;]; PWD=%m[^;] %n", &serverName, &userName, &auth, &bytes); int n = sscanf((const char*)connStr, "DSN=%m[^;]; UID=%m[^;]; PWD=%m[^;] %n", &serverName, &userName, &auth, &bytes);
if (n<1) { 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; break;
} }
D("DSN:[%s];UID:[%s];PWD:[%s]", serverName, userName, auth);
// TODO: data-race // TODO: data-race
// TODO: shall receive ip/port from odbc.ini // TODO: shall receive ip/port from odbc.ini
conn->taos = taos_connect("localhost", userName, auth, NULL, 0); conn->taos = taos_connect("localhost", userName, auth, NULL, 0);
...@@ -1652,11 +1879,11 @@ static SQLRETURN doSQLSetConnectAttr(SQLHDBC ConnectionHandle, ...@@ -1652,11 +1879,11 @@ static SQLRETURN doSQLSetConnectAttr(SQLHDBC ConnectionHandle,
if (!conn) return SQL_ERROR; if (!conn) return SQL_ERROR;
if (Attribute != SQL_ATTR_AUTOCOMMIT) { 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; return SQL_ERROR;
} }
if (Value != (SQLPOINTER)SQL_AUTOCOMMIT_ON) { 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; return SQL_ERROR;
} }
...@@ -1681,29 +1908,19 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, ...@@ -1681,29 +1908,19 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
sql_t *sql = (sql_t*)StatementHandle; sql_t *sql = (sql_t*)StatementHandle;
if (!sql) return SQL_ERROR; if (!sql) return SQL_ERROR;
if (!sql->conn) { CHK_CONN(sql);
SET_ERROR(sql, "HY000", TSDB_CODE_TSC_INVALID_CONNECTION, "no connection yet"); CHK_CONN_TAOS(sql);
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) { 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; 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); int nfields = taos_field_count(sql->rs);
if (ColumnNumber<1 || ColumnNumber>nfields) { TAOS_FIELD *fields = taos_fetch_fields(sql->rs);
SET_ERROR(sql, "HY000", TSDB_CODE_MND_FIELD_NOT_EXIST, "ColumnNumber not in valid range");
if (ColumnNumber<=0 || ColumnNumber>nfields) {
SET_ERROR(sql, "07009", TSDB_CODE_ODBC_OUT_OF_RANGE, "invalid column number [%d]", ColumnNumber);
return SQL_ERROR; return SQL_ERROR;
} }
...@@ -1759,7 +1976,8 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle, ...@@ -1759,7 +1976,8 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
} break; } break;
default: 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; return SQL_ERROR;
break; break;
} }
...@@ -1795,9 +2013,52 @@ SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT StatementHandle, ...@@ -1795,9 +2013,52 @@ SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT StatementHandle,
return r; 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, &params);
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) { 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(); taos_init();
} }
...@@ -1845,65 +2106,6 @@ static int do_field_display_size(TAOS_FIELD *field) { ...@@ -1845,65 +2106,6 @@ static int do_field_display_size(TAOS_FIELD *field) {
return 10; 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 // 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) 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 ...@@ -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) 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; size_t n = field->bytes;
n = strlen((const char*)bin);
*target->soi = n; *target->soi = n;
if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n)); if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n));
if (n <= target->len) return SQL_SUCCESS; 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 ...@@ -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) 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; size_t n = field->bytes;
n = strlen((const char*)bin);
*target->soi = n; *target->soi = n;
if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n)); if (target->ptr) memcpy(target->ptr, bin, (n>target->len ? target->len : n));
if (n <= target->len) return SQL_SUCCESS; if (n <= target->len) return SQL_SUCCESS;
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "todbc_util.h"
#include "iconv.h"
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
#include <stdlib.h>
#include <string.h>
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;
}
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _TODBC_UTIL_H_
#define _TODBC_UTIL_H_
#include <libgen.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#define D(fmt, ...) \
fprintf(stderr, \
"%s[%d]:%s() " fmt "\n", \
basename((char*)__FILE__), __LINE__, __func__, \
##__VA_ARGS__)
#define DASSERT(statement) \
do { \
if (statement) break; \
D("Assertion failure: %s", #statement); \
abort(); \
} while (0)
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_
...@@ -193,7 +193,7 @@ int main(int argc, char *argv[]) { ...@@ -193,7 +193,7 @@ int main(int argc, char *argv[]) {
data.f8 = 9999999.999999; data.f8 = 9999999.999999;
memset(data.bin, 0, sizeof(data.bin)); memset(data.bin, 0, sizeof(data.bin));
memset(data.blob, 0, sizeof(data.blob)); memset(data.blob, 0, sizeof(data.blob));
snprintf(data.bin, sizeof(data.bin), "hello"); snprintf(data.bin, sizeof(data.bin), "hello");
snprintf(data.blob, sizeof(data.blob), "world"); snprintf(data.blob, sizeof(data.blob), "world");
snprintf(data.blob, sizeof(data.blob), "wo人rld"); snprintf(data.blob, sizeof(data.blob), "wo人rld");
SQLHSTMT stmt = {0}; SQLHSTMT stmt = {0};
......
...@@ -9,4 +9,48 @@ row = cursor.fetchone() ...@@ -9,4 +9,48 @@ row = cursor.fetchone()
while row: while row:
print(row) print(row)
row = cursor.fetchone() 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()
...@@ -69,6 +69,8 @@ DLL_EXPORT int taos_options(TSDB_OPTION option, const void *arg, ...); ...@@ -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 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); DLL_EXPORT void taos_close(TAOS *taos);
const char *taos_data_type(int type);
typedef struct TAOS_BIND { typedef struct TAOS_BIND {
int buffer_type; int buffer_type;
void * buffer; void * buffer;
...@@ -77,10 +79,25 @@ typedef struct TAOS_BIND { ...@@ -77,10 +79,25 @@ typedef struct TAOS_BIND {
int * is_null; int * is_null;
int is_unsigned; // unused int is_unsigned; // unused
int * error; // 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_BIND;
TAOS_STMT *taos_stmt_init(TAOS *taos); TAOS_STMT *taos_stmt_init(TAOS *taos);
int taos_stmt_prepare(TAOS_STMT *stmt, const char *sql, unsigned long length); 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_bind_param(TAOS_STMT *stmt, TAOS_BIND *bind);
int taos_stmt_add_batch(TAOS_STMT *stmt); int taos_stmt_add_batch(TAOS_STMT *stmt);
int taos_stmt_execute(TAOS_STMT *stmt); int taos_stmt_execute(TAOS_STMT *stmt);
......
...@@ -355,6 +355,16 @@ TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_OOM, 0, 0x2101, "out of mem ...@@ -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_UNDEF, 0, 0x2102, "convertion undefined")
TAOS_DEFINE_ERROR(TSDB_CODE_ODBC_CONV_TRUNC, 0, 0x2103, "convertion truncated") 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_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 #ifdef TAOS_ERROR_C
}; };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册