diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c index 7812a991ef4fafc5a32bcee2651aa2a99315f4ea..1ba5f1ac16b0765cf3d6cf66be480b68d71cb36f 100644 --- a/src/connector/odbc/src/todbc.c +++ b/src/connector/odbc/src/todbc.c @@ -42,12 +42,18 @@ do { obj->err.err_no = eno; \ const char* estr = tstrerror(eno); \ if (!estr) estr = "Unknown error"; \ - int n = snprintf(NULL, 0, "%s: @[%d][TSDB:%x]" err_fmt "", estr, __LINE__, eno, ##__VA_ARGS__); \ + int n = snprintf(NULL, 0, "[TSDB:%x]%s: @%s[%d]" err_fmt "", \ + eno, estr, \ + basename((char*)__FILE__), __LINE__, \ + ##__VA_ARGS__); \ if (n<0) break; \ char *err_str = (char*)realloc(obj->err.err_str, n+1); \ if (!err_str) break; \ obj->err.err_str = err_str; \ - snprintf(obj->err.err_str, n+1, "%s: @[%d][TSDB:%x]" err_fmt "", estr, __LINE__, eno, ##__VA_ARGS__); \ + snprintf(obj->err.err_str, n+1, "[TSDB:%x]%s: @%s[%d]" err_fmt "", \ + eno, estr, \ + basename((char*)__FILE__), __LINE__, \ + ##__VA_ARGS__); \ snprintf((char*)obj->err.sql_state, sizeof(obj->err.sql_state), "%s", sqlstate); \ } while (0) @@ -410,6 +416,19 @@ static SQLRETURN doSQLConnect(SQLHDBC ConnectionHandle, return SQL_ERROR; } + NameLength1 = (NameLength1==SQL_NTS) ? strlen((const char*)ServerName) : NameLength1; + NameLength2 = (NameLength2==SQL_NTS) ? strlen((const char*)UserName) : NameLength2; + NameLength3 = (NameLength3==SQL_NTS) ? strlen((const char*)Authentication) : NameLength3; + + if (NameLength1 < 0 || NameLength2 < 0 || NameLength3 < 0) { + SET_ERROR(conn, "HY090", TSDB_CODE_ODBC_BAD_ARG, ""); + return SQL_ERROR; + } + if (NameLength1>SQL_MAX_DSN_LENGTH) { + SET_ERROR(conn, "HY090", TSDB_CODE_ODBC_BAD_ARG, ""); + return SQL_ERROR; + } + const char *serverName = SDUP(ServerName, NameLength1); const char *userName = SDUP(UserName, NameLength2); const char *auth = SDUP(Authentication, NameLength3); diff --git a/src/connector/odbc/src/todbc_conv.c b/src/connector/odbc/src/todbc_conv.c index 0b8b35bfd5395726bbf78ec37c516238ab9c7818..bbb59c53adeb0216e559764c1986a555905f5874 100644 --- a/src/connector/odbc/src/todbc_conv.c +++ b/src/connector/odbc/src/todbc_conv.c @@ -52,6 +52,20 @@ static void buf_clean(buf_t *buf) { } } +const char* tsdb_conv_code_str(TSDB_CONV_CODE code) { + switch (code) { + case TSDB_CONV_OK: return "TSDB_CONV_OK"; + case TSDB_CONV_OOM: return "TSDB_CONV_OOM"; + case TSDB_CONV_OOR: return "TSDB_CONV_OOR"; + case TSDB_CONV_TRUNC_FRACTION: return "TSDB_CONV_TRUNC_FRACTION"; + case TSDB_CONV_TRUNC: return "TSDB_CONV_TRUNC"; + case TSDB_CONV_CHAR_NOT_NUM: return "TSDB_CONV_CHAR_NOT_NUM"; + case TSDB_CONV_GENERAL: return "TSDB_CONV_GENERAL"; + case TSDB_CONV_BAD_CHAR: return "TSDB_CONV_BAD_CHAR"; + default: return "UNKNOWN"; + }; +} + TSDB_CONV_CODE tsdb_iconv_conv(iconv_t cnv, const unsigned char *src, size_t *slen, unsigned char *dst, size_t *dlen) { if(cnv == (iconv_t)-1) return TSDB_CONV_GENERAL; diff --git a/src/connector/odbc/src/todbc_conv.h b/src/connector/odbc/src/todbc_conv.h index 8a247818f1e6c6491e3f62a38e8cd4f2f3fac7fd..4b326230f0d47b730472e4a92a9ddf62923de3dc 100644 --- a/src/connector/odbc/src/todbc_conv.h +++ b/src/connector/odbc/src/todbc_conv.h @@ -34,6 +34,8 @@ typedef enum { TSDB_CONV_BAD_CHAR, } TSDB_CONV_CODE; +const char* tsdb_conv_code_str(TSDB_CONV_CODE code); + TSDB_CONV_CODE tsdb_iconv_conv(iconv_t cnv, const unsigned char *src, size_t *slen, unsigned char *dst, size_t *dlen); diff --git a/src/connector/odbc/src/todbc_log.h b/src/connector/odbc/src/todbc_log.h new file mode 100644 index 0000000000000000000000000000000000000000..ff0409fef6ccc01fb2173905208833983c7833f9 --- /dev/null +++ b/src/connector/odbc/src/todbc_log.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _todbc_log_h_ +#define _todbc_log_h_ + +#include +#include +#include + +#define D(fmt, ...) \ + fprintf(stderr, \ + "%s[%d]:%s() " fmt "\n", \ + basename((char*)__FILE__), __LINE__, __func__, \ + ##__VA_ARGS__) + +#define DASSERT(statement) \ +do { \ + if (statement) break; \ + D("Assertion failure: %s", #statement); \ + abort(); \ +} while (0) + +#define DASSERTX(statement, fmt, ...) \ +do { \ + if (statement) break; \ + D("Assertion failure: %s, " fmt "", #statement, ##__VA_ARGS__); \ + abort(); \ +} while (0) + +#endif // _todbc_log_h_ + diff --git a/src/connector/odbc/src/todbc_util.h b/src/connector/odbc/src/todbc_util.h index a1c32768efee3629859bc1cb79986afa3a721184..5f58ff13cefed71ca0e988cd45683876aed256a6 100644 --- a/src/connector/odbc/src/todbc_util.h +++ b/src/connector/odbc/src/todbc_util.h @@ -16,32 +16,11 @@ #ifndef _TODBC_UTIL_H_ #define _TODBC_UTIL_H_ -#include +#include "todbc_log.h" + #include -#include -#include #include -#define D(fmt, ...) \ - fprintf(stderr, \ - "%s[%d]:%s() " fmt "\n", \ - basename((char*)__FILE__), __LINE__, __func__, \ - ##__VA_ARGS__) - -#define DASSERT(statement) \ -do { \ - if (statement) break; \ - D("Assertion failure: %s", #statement); \ - abort(); \ -} while (0) - -#define DASSERTX(statement, fmt, ...) \ -do { \ - if (statement) break; \ - D("Assertion failure: %s, " fmt "", #statement, ##__VA_ARGS__); \ - abort(); \ -} while (0) - const char* sql_sql_type(int type); const char* sql_c_type(int type); diff --git a/src/connector/odbc/tests/main.c b/src/connector/odbc/tests/main.c index 1ac9b71369e8a526b051008b9379fdda4b6a5e77..0d42da985de8da9d2bfa0d51a3ac13624e0e5a03 100644 --- a/src/connector/odbc/tests/main.c +++ b/src/connector/odbc/tests/main.c @@ -1,3 +1,4 @@ +#include #include #include @@ -5,11 +6,20 @@ #include #include "os.h" +#include "../src/todbc_log.h" // static const char *dsn = "TAOS_DSN"; // static const char *uid = "root"; // static const char *pwd = "taosdata"; +#define CHK_TEST(statement) \ +do { \ + D("testing: %s", #statement); \ + int r = (statement); \ + if (r) return 1; \ +} while (0); + + typedef struct data_s data_t; struct data_s { int64_t ts; @@ -37,7 +47,7 @@ static const char *pro_stmts[] = { // "drop database db" }; -#define CHK_RESULT(r, ht, h) \ +#define CHK_RESULT(r, ht, h, fmt, ...) \ do { \ if (r==0) break; \ SQLCHAR ss[10]; \ @@ -48,23 +58,124 @@ do { es[0] = '\0'; \ SQLRETURN ret = SQLGetDiagRec(ht, h, 1, ss, &ne, es, sizeof(es), &n); \ if (ret) break; \ - fprintf(stderr, "%s%s\n", ss, es); \ + D("[%s]%s: " fmt "", ss, es, ##__VA_ARGS__); \ } while (0) +static int open_connect(const char *dsn, const char *uid, const char *pwd, SQLHENV *pEnv, SQLHDBC *pConn) { + SQLRETURN r; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + r = SQLAllocEnv(&env); + if (r!=SQL_SUCCESS) return 1; + do { + r = SQLAllocConnect(env, &conn); + CHK_RESULT(r, SQL_HANDLE_ENV, env, ""); + if (r!=SQL_SUCCESS) break; + do { + r = SQLConnect(conn, (SQLCHAR*)dsn, strlen(dsn), + (SQLCHAR*)uid, strlen(uid), + (SQLCHAR*)pwd, strlen(pwd)); + CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); + if (r==SQL_SUCCESS) { + *pEnv = env; + *pConn = conn; + return 0; + } + } while (0); + SQLFreeConnect(conn); + } while (0); + SQLFreeEnv(env); + + return 1; +} + +static int open_driver_connect(const char *connstr, SQLHENV *pEnv, SQLHDBC *pConn) { + SQLRETURN r; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + r = SQLAllocEnv(&env); + if (r!=SQL_SUCCESS) return 1; + do { + r = SQLAllocConnect(env, &conn); + CHK_RESULT(r, SQL_HANDLE_ENV, env, ""); + if (r!=SQL_SUCCESS) break; + do { + SQLCHAR buf[4096]; + SQLSMALLINT blen = 0; + SQLHDBC ConnectionHandle = conn; + SQLHWND WindowHandle = NULL; + SQLCHAR * InConnectionString = (SQLCHAR*)connstr; + SQLSMALLINT StringLength1 = strlen(connstr); + SQLCHAR * OutConnectionString = buf; + SQLSMALLINT BufferLength = sizeof(buf); + SQLSMALLINT * StringLength2Ptr = &blen; + SQLUSMALLINT DriverCompletion = SQL_DRIVER_NOPROMPT; + r = SQLDriverConnect(ConnectionHandle, WindowHandle, InConnectionString, + StringLength1, OutConnectionString, BufferLength, + StringLength2Ptr, DriverCompletion); + CHK_RESULT(r, SQL_HANDLE_DBC, conn, ""); + if (r==SQL_SUCCESS) { + *pEnv = env; + *pConn = conn; + return 0; + } + } while (0); + SQLFreeConnect(conn); + } while (0); + SQLFreeEnv(env); + + return 1; +} static int do_statement(SQLHSTMT stmt, const char *statement) { SQLRETURN r = 0; do { - fprintf(stderr, "prepare [%s]\n", statement); - r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement)); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + r = SQLExecDirect(stmt, (SQLCHAR*)statement, SQL_NTS); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: [%s]", statement); if (r) break; - fprintf(stderr, "execute [%s]\n", statement); - r = SQLExecute(stmt); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + SQLSMALLINT cols = 0; + r = SQLNumResultCols(stmt, &cols); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); if (r) break; - fprintf(stderr, "done\n"); + if (cols <= 0) break; + char buf[4096]; + while (1) { + SQLRETURN r = SQLFetch(stmt); + if (r==SQL_NO_DATA) break; + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + for (size_t i=0; i0) fprintf(stdout, "\n"); + return r; + } + } + if (soi==SQL_NULL_DATA) { + fprintf(stdout, "%snull", i==0?"":","); + } else { + fprintf(stdout, "%s\"%s\"", i==0?"":",", buf); + } + } + fprintf(stdout, "\n"); + } + + // r = SQLFetch(stmt); + // if (r==SQL_NO_DATA) { + // D(".........."); + // r = SQL_SUCCESS; + // break; + // } + // CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + // if (r) break; + // r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement)); + // CHK_RESULT(r, SQL_HANDLE_STMT, stmt, ""); + // if (r) break; + // r = SQLExecute(stmt); + // CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); + // if (r) break; } while (0); - fprintf(stderr, "r: [%x][%d]\n", r, r); return r; } @@ -77,155 +188,344 @@ static int do_insert(SQLHSTMT stmt, data_t data) { int ignored = 0; do { - fprintf(stderr, "prepare [%s]\n", statement); r = SQLPrepare(stmt, (SQLCHAR*)statement, strlen(statement)); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 1 [%s]\n", statement); r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_TIMESTAMP, ignored, ignored, &data.ts, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 2 [%s]\n", statement); r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, ignored, ignored, &data.b, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 3 [%s]\n", statement); r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, ignored, ignored, &data.v1, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 4 [%s]\n", statement); r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, ignored, ignored, &data.v2, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 5 [%s]\n", statement); r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, ignored, ignored, &data.v4, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 6 [%s]\n", statement); r = SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, ignored, ignored, &data.v8, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 7 [%s]\n", statement); r = SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, ignored, ignored, &data.f4, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 8 [%s]\n", statement); r = SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, ignored, ignored, &data.f8, ignored, NULL); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 9 [%s]\n", statement); lbin = SQL_NTS; r = SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, sizeof(data.bin)-1, ignored, &data.bin, ignored, &lbin); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "bind 10 [%s]\n", statement); lblob = SQL_NTS; r = SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(data.blob)-1, ignored, &data.blob, ignored, &lblob); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; - fprintf(stderr, "execute [%s]\n", statement); r = SQLExecute(stmt); - CHK_RESULT(r, SQL_HANDLE_STMT, stmt); + CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: %s", statement); if (r) break; // ts += 1; // v = 2; - // fprintf(stderr, "execute [%s]\n", statement); // r = SQLExecute(stmt); // if (r) break; - - fprintf(stderr, "done\n"); } while (0); - fprintf(stderr, "r: [%x][%d]\n", r, r); return r; } -int main(int argc, char *argv[]) { - if (argc < 4) return 1; - const char *dsn = argv[1]; - const char *uid = argv[2]; - const char *pwd = argv[3]; - SQLRETURN r; +static int test1(const char *dsn, const char *uid, const char *pwd) { + SQLRETURN r = SQL_SUCCESS; SQLHENV env = {0}; SQLHDBC conn = {0}; - r = SQLAllocEnv(&env); - if (r!=SQL_SUCCESS) return 1; + int n = open_connect(dsn, uid, pwd, &env, &conn); + if (n) return 1; + do { - r = SQLAllocConnect(env, &conn); - CHK_RESULT(r, SQL_HANDLE_ENV, env); + SQLHSTMT stmt = {0}; + r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt); if (r!=SQL_SUCCESS) break; do { - r = SQLConnect(conn, (SQLCHAR*)dsn, strlen(dsn), - (SQLCHAR*)uid, strlen(uid), - (SQLCHAR*)pwd, strlen(pwd)); - CHK_RESULT(r, SQL_HANDLE_DBC, conn); - if (r!=SQL_SUCCESS) break; + if (do_statement(stmt, "drop database if exists db")) { + r = SQL_ERROR; + break; + } + for (size_t i=0; i1) ? argv[1] : NULL; + const char *uid = (argc>2) ? argv[2] : NULL; + const char *pwd = (argc>3) ? argv[3] : NULL; + const char *connstr = (argc>4) ? argv[4] : NULL; + const char *sqls = (argc>5) ? argv[5] : NULL; + + if (0) { + CHK_TEST(test_env()); + + CHK_TEST(test1(dsn, uid, pwd)); + + const char *statements[] = { + "drop database if exists m", + "create database m", + "use m", + "drop database m", + NULL + }; + CHK_TEST(test_statements(dsn, uid, pwd, statements)); + + if (connstr) + CHK_TEST(test_driver_connect(connstr)); + + if (connstr) { + SQLHENV env = {0}; + SQLHDBC conn = {0}; + CHK_TEST(open_driver_connect(connstr, &env, &conn)); + int r = tests(env, conn); + SQLDisconnect(conn); + SQLFreeConnect(conn); + SQLFreeEnv(env); + if (r) return 1; + } + } + + if ((dsn || connstr) && 1) { + CHK_TEST(test_sqls(dsn, uid, pwd, connstr, sqls)); + } + + return 0; +} +