1061 lines
31 KiB
C
1061 lines
31 KiB
C
#include "../../src/todbc_log.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#include <winsock2.h>
|
|
#include <windows.h>
|
|
#include "os.h"
|
|
#endif
|
|
#include <sql.h>
|
|
#include <sqlext.h>
|
|
#include <odbcinst.h>
|
|
|
|
#include "taos.h"
|
|
#include "taoserror.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define CHK_TEST(statement) \
|
|
do { \
|
|
D("testing: %s", #statement); \
|
|
int r = (statement); \
|
|
if (r) { \
|
|
D("testing failed: %s", #statement); \
|
|
return 1; \
|
|
} \
|
|
} while (0);
|
|
|
|
typedef struct {
|
|
int batch_size;
|
|
int batchs;
|
|
int keep_stmt_among_batchs;
|
|
int use_odbc;
|
|
int use_taos_query;
|
|
int use_taos_stmt;
|
|
} insert_arg_t;
|
|
|
|
typedef struct db_column_s db_column_t;
|
|
struct db_column_s {
|
|
SQLSMALLINT nameLength;
|
|
char name[4096]; // seems enough
|
|
SQLSMALLINT dataType;
|
|
SQLULEN columnSize;
|
|
SQLSMALLINT decimalDigits;
|
|
SQLSMALLINT nullable;
|
|
};
|
|
|
|
static db_column_t *columns = NULL;
|
|
|
|
typedef struct data_s data_t;
|
|
struct data_s {
|
|
int64_t ts;
|
|
int8_t b;
|
|
int8_t v1;
|
|
int16_t v2;
|
|
int32_t v4;
|
|
int64_t v8;
|
|
float f4;
|
|
double f8;
|
|
char bin[40+1];
|
|
char blob[40+1]; // why 80? ref: tests/examples/c/apitest.c
|
|
};
|
|
|
|
#define CHK_RESULT(r, ht, h, fmt, ...) \
|
|
do { \
|
|
if (r==0) break; \
|
|
SQLSMALLINT i_0381 = 1; \
|
|
while (1) { \
|
|
SQLCHAR ss[10]; \
|
|
SQLINTEGER ne = 0; \
|
|
SQLCHAR es[4096]; \
|
|
SQLSMALLINT n = 0; \
|
|
ss[0] = '\0'; \
|
|
es[0] = '\0'; \
|
|
SQLRETURN ret = SQLGetDiagRec(ht, h, i_0381, ss, &ne, es, sizeof(es), &n); \
|
|
if (ret) break; \
|
|
D("[%s]%s: " fmt "", ss, es, ##__VA_ARGS__); \
|
|
++i_0381; \
|
|
} \
|
|
} 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, (SQLSMALLINT)(dsn ? strlen(dsn) : 0),
|
|
(SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0),
|
|
(SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0));
|
|
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 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0);
|
|
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 SQLRETURN traverse_cols(SQLHSTMT stmt, SQLSMALLINT cols) {
|
|
SQLRETURN r = SQL_ERROR;
|
|
for (SQLSMALLINT i=0; i<cols; ++i) {
|
|
db_column_t column = {0};
|
|
r = SQLDescribeCol(stmt, (SQLUSMALLINT)(i+1), (SQLCHAR*)column.name,
|
|
(SQLSMALLINT)sizeof(column.name), &column.nameLength,
|
|
&column.dataType, &column.columnSize,
|
|
&column.decimalDigits, &column.nullable);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
D("col%02d:[%s]%d,type:[%d],colSize:[%zu],decimalDigits:[%d],nullable:[%d]",
|
|
i+1, column.name, column.nameLength, column.dataType, column.columnSize,
|
|
column.decimalDigits, column.nullable);
|
|
db_column_t *col = (db_column_t*)realloc(columns, (size_t)(i+1)*sizeof(*col));
|
|
if (!col) {
|
|
D("out of memory");
|
|
return SQL_ERROR;
|
|
}
|
|
col[i] = column;
|
|
columns = col;
|
|
}
|
|
return SQL_SUCCESS;
|
|
}
|
|
|
|
static int do_statement(SQLHSTMT stmt, const char *statement) {
|
|
SQLRETURN r = 0;
|
|
do {
|
|
r = SQLExecDirect(stmt, (SQLCHAR*)statement, SQL_NTS);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "statement: [%s]", statement);
|
|
if (r) break;
|
|
SQLSMALLINT cols = 0;
|
|
r = SQLNumResultCols(stmt, &cols);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
if (cols <= 0) break;
|
|
r = traverse_cols(stmt, cols);
|
|
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; i<cols; ++i) {
|
|
SQLLEN soi = 0;
|
|
r = SQLGetData(stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, buf, sizeof(buf), &soi);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "i/cols:[%ld/%d]", i,cols);
|
|
if (r) {
|
|
if (r!=SQL_SUCCESS_WITH_INFO) {
|
|
if (i>0) 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");
|
|
}
|
|
} while (0);
|
|
return r;
|
|
}
|
|
|
|
int test_statements(const char *dsn, const char *uid, const char *pwd, const char **statements) {
|
|
SQLRETURN r = SQL_SUCCESS;
|
|
SQLHENV env = {0};
|
|
SQLHDBC conn = {0};
|
|
int n = open_connect(dsn, uid, pwd, &env, &conn);
|
|
if (n) return 1;
|
|
do {
|
|
SQLHSTMT stmt = {0};
|
|
r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
|
|
if (r!=SQL_SUCCESS) break;
|
|
const char **p = statements;
|
|
while (*p) {
|
|
if (do_statement(stmt, *p)) {
|
|
r = SQL_ERROR;
|
|
break;
|
|
}
|
|
++p;
|
|
}
|
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
} while (0);
|
|
SQLDisconnect(conn);
|
|
SQLFreeConnect(conn);
|
|
SQLFreeEnv(env);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
int test_driver_connect(const char *connstr) {
|
|
SQLRETURN r = SQL_SUCCESS;
|
|
SQLHENV env = {0};
|
|
SQLHDBC conn = {0};
|
|
int n = open_driver_connect(connstr, &env, &conn);
|
|
if (n) return 1;
|
|
SQLDisconnect(conn);
|
|
SQLFreeConnect(conn);
|
|
SQLFreeEnv(env);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
int create_statement(SQLHENV env, SQLHDBC conn, SQLHSTMT *pStmt) {
|
|
SQLHSTMT stmt = {0};
|
|
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt);
|
|
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
|
if (r==SQL_SUCCESS) {
|
|
*pStmt = stmt;
|
|
return 0;
|
|
}
|
|
if (r==SQL_SUCCESS_WITH_INFO) {
|
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int do_statements(SQLHSTMT stmt, const char **statements) {
|
|
const char **p = statements;
|
|
while (p && *p) {
|
|
CHK_TEST(do_statement(stmt, *p));
|
|
++p;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int tests_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt) {
|
|
const char *statements[] = {
|
|
"drop database if exists m",
|
|
"create database m",
|
|
"use m",
|
|
// "create table t (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, blob binary(1), name nchar(1))",
|
|
"create table t (ts timestamp, b bool)",
|
|
"insert into t values('2020-10-10 00:00:00', 0)",
|
|
"insert into t values('2020-10-10 00:00:00.001', 1)",
|
|
NULL
|
|
};
|
|
CHK_TEST(do_statements(stmt, statements));
|
|
return 0;
|
|
}
|
|
|
|
int tests(SQLHENV env, SQLHDBC conn) {
|
|
SQLHSTMT stmt = {0};
|
|
CHK_TEST(create_statement(env, conn, &stmt));
|
|
int r = tests_stmt(env, conn, stmt);
|
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
int test_env(void) {
|
|
SQLRETURN r;
|
|
SQLHENV env = {0};
|
|
r = SQLAllocEnv(&env);
|
|
if (r!=SQL_SUCCESS) return 1;
|
|
SQLFreeEnv(env);
|
|
return 0;
|
|
}
|
|
|
|
static int test_sqls_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, const char *sqls) {
|
|
FILE *f = fopen(sqls, "rb");
|
|
if (!f) {
|
|
D("failed to open file [%s]", sqls);
|
|
return -1;
|
|
}
|
|
|
|
int r = 0;
|
|
while (!feof(f)) {
|
|
char *line = NULL;
|
|
size_t len = 0;
|
|
|
|
ssize_t n = 0;
|
|
#ifdef _MSC_VER
|
|
n = taosGetlineImp(&line, &len, f);
|
|
#else
|
|
n = getline(&line, &len, f);
|
|
#endif
|
|
if (n==-1) break;
|
|
|
|
const char *p = NULL;
|
|
do {
|
|
if (line[0] == '#') break;
|
|
if (n>0 && line[n-1] == '\n') line[n-1]='\0';
|
|
if (n>0 && line[n-1] == '\r') line[n-1]='\0';
|
|
if (n>1 && line[n-2] == '\r') line[n-2]='\0';
|
|
p = line;
|
|
while (isspace(*p)) ++p;
|
|
|
|
if (*p==0) break;
|
|
|
|
int positive = 1;
|
|
if (strncmp(p, "N:", 2)==0) {
|
|
// negative sample
|
|
positive = 0;
|
|
p += 2;
|
|
} else if (strncmp(p, "P:", 2)==0) {
|
|
// positive sample
|
|
p += 2;
|
|
}
|
|
|
|
D("statement: [%s]", p);
|
|
r = do_statement(stmt, p);
|
|
|
|
if (positive && r==0) break;
|
|
if (!positive && r) { r = 0; break; }
|
|
if (positive) return r;
|
|
D("expecting negative result, but got positive");
|
|
return -1;
|
|
} while (0);
|
|
|
|
free(line);
|
|
|
|
if (r) break;
|
|
}
|
|
|
|
fclose(f);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
static int test_sqls_in_conn(SQLHENV env, SQLHDBC conn, const char *sqls) {
|
|
SQLHSTMT stmt = {0};
|
|
CHK_TEST(create_statement(env, conn, &stmt));
|
|
int r = test_sqls_in_stmt(env, conn, stmt, sqls);
|
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
static int test_sqls(const char *dsn, const char *uid, const char *pwd, const char *connstr, const char *sqls) {
|
|
int r = 0;
|
|
SQLHENV env = {0};
|
|
SQLHDBC conn = {0};
|
|
if (dsn) {
|
|
CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn));
|
|
} else {
|
|
CHK_TEST(open_driver_connect(connstr, &env, &conn));
|
|
}
|
|
|
|
if (sqls) {
|
|
r = test_sqls_in_conn(env, conn, sqls);
|
|
}
|
|
SQLDisconnect(conn);
|
|
SQLFreeConnect(conn);
|
|
SQLFreeEnv(env);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
typedef struct record_s record_t;
|
|
struct record_s {
|
|
int dummy;
|
|
char ts[64];
|
|
SQLLEN ts_len;
|
|
int32_t v1;
|
|
SQLLEN v1_len;
|
|
char ts2[64];
|
|
SQLLEN ts2_len;
|
|
};
|
|
|
|
static int do_prepare_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt) {
|
|
SQLRETURN r = SQL_SUCCESS;
|
|
do {
|
|
const char *sql = "insert into m.v (ts, v1, ts2) values (?, ?, ?)";
|
|
r = SQLPrepare(stmt, (SQLCHAR*)sql, SQL_NTS);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
|
|
record_t records[] = {
|
|
{0, "2020-01-03 11:22:33.345", SQL_NTS, 1, sizeof(int32_t), "2020-01-02 11:22:33.455", SQL_NTS},
|
|
{0, "2020-01-03 11:22:34.346", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS},
|
|
{0, "2020-01-04 11:22:34.345", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS},
|
|
{0, "2020-01-05 11:22:34.345", SQL_NTS, 2, sizeof(int32_t), "2020-01-02 11:22:34.445", SQL_NTS},
|
|
};
|
|
|
|
record_t *base = (record_t*)0;
|
|
|
|
r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(base->ts)-1, 0, base->ts, sizeof(base->ts), &(base->ts_len));
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &base->v1, 0, &(base->v1_len));
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(base->ts2)-1, 0, base->ts2, sizeof(base->ts2), &(base->ts2_len));
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
|
|
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)sizeof(*base), 0);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
|
|
SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)(sizeof(records)/sizeof(records[0])), 0);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
|
|
record_t *record = NULL;
|
|
|
|
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &record, 0);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
|
|
record = records;
|
|
|
|
r = SQLExecute(stmt);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
} while (0);
|
|
|
|
return r ? -1 : 0;
|
|
}
|
|
|
|
static int do_prepare_in_conn(SQLHENV env, SQLHDBC conn) {
|
|
SQLHSTMT stmt = {0};
|
|
CHK_TEST(create_statement(env, conn, &stmt));
|
|
int r = do_prepare_in_stmt(env, conn, stmt);
|
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
static int do_prepare(const char *dsn, const char *uid, const char *pwd, const char *connstr) {
|
|
int r = 0;
|
|
SQLHENV env = {0};
|
|
SQLHDBC conn = {0};
|
|
if (dsn) {
|
|
CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn));
|
|
} else {
|
|
CHK_TEST(open_driver_connect(connstr, &env, &conn));
|
|
}
|
|
|
|
r = do_prepare_in_conn(env, conn);
|
|
|
|
SQLDisconnect(conn);
|
|
SQLFreeConnect(conn);
|
|
SQLFreeEnv(env);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
typedef struct {
|
|
int dummy;
|
|
int64_t ts;
|
|
SQLLEN ts_len;
|
|
int8_t v1;
|
|
SQLLEN v1_len;
|
|
int16_t v2;
|
|
SQLLEN v2_len;
|
|
int32_t v4;
|
|
SQLLEN v4_len;
|
|
int64_t v8;
|
|
SQLLEN v8_len;
|
|
} test_v_t;
|
|
|
|
static int do_insert_in_stmt(SQLHENV env, SQLHDBC conn, SQLHSTMT stmt, int64_t *ts, insert_arg_t *arg) {
|
|
SQLRETURN r = SQL_SUCCESS;
|
|
int batch_size = arg->batch_size;
|
|
test_v_t *recs = NULL;
|
|
do {
|
|
const char *sql = "insert into test.v (ts, v1, v2, v4, v8) values (?, ?, ?, ?, ?)";
|
|
r = SQLPrepare(stmt, (SQLCHAR*)sql, SQL_NTS);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
|
|
test_v_t *base = NULL;
|
|
|
|
r = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &base->ts, 0, &base->ts_len);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
r = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_TINYINT, 0, 0, &base->v1, 0, &base->v1_len);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
r = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, 0, 0, &base->v2, 0, &base->v2_len);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
r = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &base->v4, 0, &base->v4_len);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
r = SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &base->v8, 0, &base->v8_len);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
|
|
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)sizeof(*base), 0);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
|
|
base = NULL;
|
|
|
|
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &base, 0);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
|
|
size_t n_recs = (size_t)batch_size;
|
|
recs = (test_v_t*)calloc(n_recs, sizeof(*recs));
|
|
OILE(recs, "");
|
|
|
|
SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)n_recs, 0);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
|
|
base = recs;
|
|
|
|
for (int batch=0; batch<arg->batchs; ++batch) {
|
|
for (int i=0; i<n_recs; ++i) {
|
|
test_v_t *rec = recs + i;
|
|
rec->dummy = 0;
|
|
rec->ts = *ts + i;
|
|
rec->ts_len = sizeof(rec->ts);
|
|
rec->v1 = (int8_t)rand();
|
|
rec->v1_len = sizeof(rec->v1);
|
|
rec->v2 = (int16_t)rand();
|
|
rec->v2_len = sizeof(rec->v2);
|
|
rec->v4 = rand();
|
|
rec->v4_len = sizeof(rec->v4);
|
|
rec->v8 = rand();
|
|
rec->v8_len = sizeof(rec->v8);
|
|
}
|
|
|
|
*ts += (int64_t)n_recs;
|
|
|
|
r = SQLExecute(stmt);
|
|
CHK_RESULT(r, SQL_HANDLE_STMT, stmt, "");
|
|
if (r) break;
|
|
}
|
|
} while (0);
|
|
|
|
free(recs);
|
|
return r ? -1 : 0;
|
|
}
|
|
|
|
static int do_insert_in_conn(SQLHENV env, SQLHDBC conn, insert_arg_t *arg) {
|
|
SQLHSTMT stmt = {0};
|
|
int64_t ts = 1502535178128;
|
|
int r = 0;
|
|
CHK_TEST(create_statement(env, conn, &stmt));
|
|
for (int i=0; i<1 && i<arg->batchs; ++i) {
|
|
r = do_insert_in_stmt(env, conn, stmt, &ts, arg);
|
|
if (r) break;
|
|
if (!arg->keep_stmt_among_batchs) {
|
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
r = create_statement(env, conn, &stmt);
|
|
if (r) break;
|
|
}
|
|
}
|
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
static int do_insert_batch(const char *dsn, const char *uid, const char *pwd, const char *connstr, insert_arg_t *arg, const char *sqls[]) {
|
|
int r = 0;
|
|
SQLHENV env = {0};
|
|
SQLHDBC conn = {0};
|
|
if (dsn) {
|
|
CHK_TEST(open_connect(dsn, uid, pwd, &env, &conn));
|
|
} else {
|
|
CHK_TEST(open_driver_connect(connstr, &env, &conn));
|
|
}
|
|
|
|
SQLHSTMT stmt = {0};
|
|
CHK_TEST(create_statement(env, conn, &stmt));
|
|
CHK_TEST(do_statements(stmt, sqls));
|
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
|
|
OD("................");
|
|
r = do_insert_in_conn(env, conn, arg);
|
|
OD("................");
|
|
|
|
SQLDisconnect(conn);
|
|
SQLFreeConnect(conn);
|
|
SQLFreeEnv(env);
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
static int inited = 0;
|
|
static void init_once(void) {
|
|
if (inited) return;
|
|
|
|
int r = taos_init();
|
|
if (r) OILE(0, "");
|
|
inited = 1;
|
|
}
|
|
|
|
static int do_sqls(TAOS *taos, const char *sqls[]) {
|
|
for (int i=0; sqls[i]; ++i) {
|
|
OD("[%s]", sqls[i]);
|
|
TAOS_RES *res = taos_query(taos, sqls[i]);
|
|
if (!res) {
|
|
int e = terrno;
|
|
OD("taos_query [%s] failed: [%d]%s", sqls[i], e, tstrerror(e));
|
|
return -1;
|
|
}
|
|
int e = taos_errno(res);
|
|
if (e) {
|
|
OD("taos_query [%s] failed: [%d]%s", sqls[i], e, tstrerror(e));
|
|
}
|
|
taos_stop_query(res);
|
|
if (e) return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int do_taos_query(TAOS *taos, insert_arg_t *arg) {
|
|
char **sqls = (char**)calloc((size_t)arg->batchs, sizeof(*sqls));
|
|
if (!sqls) {
|
|
OILE(0, "out of memory");
|
|
}
|
|
|
|
int64_t ts = 1502535178128;
|
|
for (int i=0; i<arg->batchs; ++i) {
|
|
size_t bytes = 100 * (size_t)arg->batch_size;
|
|
sqls[i] = (char*)malloc(bytes);
|
|
OILE(sqls[i], "");
|
|
char *p = sqls[i];
|
|
size_t count = 0;
|
|
|
|
while (1) {
|
|
int n = 0;
|
|
n = snprintf(p, bytes, "insert into test.v values");
|
|
OILE(n>0, "");
|
|
if (p) p += n;
|
|
OILE(bytes>n, "");
|
|
if (bytes>=n) bytes -= (size_t)n;
|
|
else bytes = 0;
|
|
count += (size_t)n;
|
|
|
|
for (int j=0; j<arg->batch_size; ++j) {
|
|
int8_t v1 = (int8_t)rand(); if (v1==INT8_MIN) v1++;
|
|
int16_t v2 = (int16_t)rand(); if (v2==INT16_MIN) v2++;
|
|
int32_t v4 = (int32_t)rand(); if (v4==INT32_MIN) v4++;
|
|
int64_t v8 = (int64_t)rand(); if (v8==INT64_MIN) v8++;
|
|
n = snprintf(p, bytes, " (%" PRId64 ", %d, %d, %d, %" PRId64 ")", ts + i*arg->batch_size + j, (int)v1, (int)v2, v4, v8);
|
|
OILE(n>0, "");
|
|
if (p) p += n;
|
|
OILE(bytes>n, "");
|
|
if (bytes>=n) bytes -= (size_t)n;
|
|
else bytes = 0;
|
|
count += (size_t)n;
|
|
}
|
|
|
|
if (p) break;
|
|
OILE(0, "");
|
|
}
|
|
}
|
|
|
|
OD("..............");
|
|
for (int i=0; i<arg->batchs; ++i) {
|
|
TAOS_RES *res = taos_query(taos, sqls[i]);
|
|
if (!res) {
|
|
int e = terrno;
|
|
OD("taos_query [%s] failed: [%d]%s", sqls[i], e, tstrerror(e));
|
|
return -1;
|
|
}
|
|
int e = taos_errno(res);
|
|
if (e) {
|
|
OD("taos_query [%s] failed: [%d]%s", sqls[i], e, tstrerror(e));
|
|
}
|
|
taos_stop_query(res);
|
|
if (e) return -1;
|
|
}
|
|
OD("..............");
|
|
|
|
for (int i=0; i<arg->batchs; ++i) {
|
|
free(sqls[i]);
|
|
}
|
|
free(sqls);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_taos_stmt(TAOS *taos, insert_arg_t *arg) {
|
|
TAOS_STMT *stmt = taos_stmt_init(taos);
|
|
OILE(stmt, "");
|
|
const char *sql = "insert into test.v values (?,?,?,?,?)";
|
|
int r = 0;
|
|
do {
|
|
r = taos_stmt_prepare(stmt, sql, (unsigned long)strlen(sql));
|
|
if (r) {
|
|
OD("taos_stmt_prepare [%s] failed: [%d]%s", sql, r, tstrerror(r));
|
|
break;
|
|
}
|
|
int64_t ts = 1502535178128;
|
|
TAOS_BIND *bindings = (TAOS_BIND*)calloc(5, sizeof(*bindings));
|
|
TAOS_BIND *b_ts = bindings + 0;
|
|
TAOS_BIND *b_v1 = bindings + 1;
|
|
TAOS_BIND *b_v2 = bindings + 2;
|
|
TAOS_BIND *b_v4 = bindings + 3;
|
|
TAOS_BIND *b_v8 = bindings + 4;
|
|
b_ts->buffer_type = TSDB_DATA_TYPE_TIMESTAMP;
|
|
b_ts->buffer_length = sizeof(b_ts->u.ts);
|
|
b_ts->length = &b_ts->buffer_length;
|
|
b_ts->buffer = &b_ts->u.ts;
|
|
b_ts->is_null = NULL;
|
|
|
|
b_v1->buffer_type = TSDB_DATA_TYPE_TINYINT;
|
|
b_v1->buffer_length = sizeof(b_v1->u.v1);
|
|
b_v1->length = &b_v1->buffer_length;
|
|
b_v1->buffer = &b_v1->u.v1;
|
|
b_v1->is_null = NULL;
|
|
|
|
b_v2->buffer_type = TSDB_DATA_TYPE_SMALLINT;
|
|
b_v2->buffer_length = sizeof(b_v2->u.v2);
|
|
b_v2->length = &b_v2->buffer_length;
|
|
b_v2->buffer = &b_v2->u.v2;
|
|
b_v2->is_null = NULL;
|
|
|
|
b_v4->buffer_type = TSDB_DATA_TYPE_INT;
|
|
b_v4->buffer_length = sizeof(b_v4->u.v4);
|
|
b_v4->length = &b_v4->buffer_length;
|
|
b_v4->buffer = &b_v4->u.v4;
|
|
b_v4->is_null = NULL;
|
|
|
|
b_v8->buffer_type = TSDB_DATA_TYPE_BIGINT;
|
|
b_v8->buffer_length = sizeof(b_v8->u.v8);
|
|
b_v8->length = &b_v8->buffer_length;
|
|
b_v8->buffer = &b_v8->u.v8;
|
|
b_v8->is_null = NULL;
|
|
|
|
OILE(bindings, "");
|
|
OD("................");
|
|
for (int i=0; i<arg->batchs; ++i) {
|
|
for (int j=0; j<arg->batch_size; ++j) {
|
|
b_ts->u.ts = ts + i*arg->batch_size + j;
|
|
b_v1->u.v1 = (int8_t)rand();
|
|
b_v2->u.v2 = (int16_t)rand();
|
|
b_v4->u.v4 = (int32_t)rand();
|
|
b_v8->u.v8 = (int64_t)rand();
|
|
r = taos_stmt_bind_param(stmt, bindings);
|
|
if (r) {
|
|
OD("taos_stmt_bind_param failed: [%d]%s", r, tstrerror(r));
|
|
break;
|
|
}
|
|
r = taos_stmt_add_batch(stmt);
|
|
if (r) {
|
|
OD("taos_stmt_add_batch failed: [%d]%s", r, tstrerror(r));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (r) break;
|
|
|
|
r = taos_stmt_execute(stmt);
|
|
if (r) {
|
|
OD("taos_stmt_execute failed: [%d]%s", r, tstrerror(r));
|
|
break;
|
|
}
|
|
}
|
|
OD("................");
|
|
|
|
free(bindings);
|
|
|
|
if (r) break;
|
|
} while (0);
|
|
taos_stmt_close(stmt);
|
|
return r ? -1 : 0;
|
|
}
|
|
|
|
static int do_insert_batch_taos(const char *dsn, const char *uid, const char *pwd, const char *connstr, insert_arg_t *arg, const char *sqls[]) {
|
|
int r = 0;
|
|
|
|
init_once();
|
|
|
|
int port = 0;
|
|
char *ip = NULL;
|
|
const char *p = strchr(connstr, ':');
|
|
if (p) {
|
|
ip = strndup(connstr, (size_t)(p-connstr));
|
|
++p;
|
|
sscanf(p, "%d", &port);
|
|
} else {
|
|
ip = strdup(connstr);
|
|
port = 6030;
|
|
}
|
|
if (!ip) {
|
|
OD("bad ip/port:[%s]", connstr);
|
|
return -1;
|
|
}
|
|
|
|
TAOS *taos = NULL;
|
|
do {
|
|
taos = taos_connect(ip, uid, pwd, NULL, (uint16_t)port);
|
|
if (!taos) {
|
|
int e = terrno;
|
|
OD("taos_connect [%s/%d] failed:[%d]%s", ip, port, e, tstrerror(e));
|
|
break;
|
|
}
|
|
r = do_sqls(taos, sqls);
|
|
if (r) break;
|
|
if (arg->use_taos_query) {
|
|
r = do_taos_query(taos, arg);
|
|
} else if (arg->use_taos_stmt) {
|
|
r = do_taos_stmt(taos, arg);
|
|
} else {
|
|
OILE(0, "");
|
|
}
|
|
} while (0);
|
|
|
|
if (taos) taos_close(taos);
|
|
free(ip);
|
|
|
|
return r ? 1 : 0;
|
|
}
|
|
|
|
static int do_debug_col_name_max_len(const char *dsn, const char *uid, const char *pwd, const char *connstr) {
|
|
SQLRETURN r;
|
|
SQLHENV env = {0};
|
|
SQLHDBC conn = {0};
|
|
r = SQLAllocEnv(&env);
|
|
if (r!=SQL_SUCCESS) {
|
|
D("SQLAllocEnv failed");
|
|
return 1;
|
|
};
|
|
do {
|
|
r = SQLAllocConnect(env, &conn);
|
|
CHK_RESULT(r, SQL_HANDLE_ENV, env, "");
|
|
if (r!=SQL_SUCCESS) break;
|
|
do {
|
|
if (dsn) {
|
|
r = SQLConnect(conn, (SQLCHAR*)dsn, (SQLSMALLINT)(dsn ? strlen(dsn) : 0),
|
|
(SQLCHAR*)uid, (SQLSMALLINT)(uid ? strlen(uid) : 0),
|
|
(SQLCHAR*)pwd, (SQLSMALLINT)(pwd ? strlen(pwd) : 0));
|
|
} else {
|
|
SQLCHAR buf[4096];
|
|
SQLSMALLINT blen = 0;
|
|
SQLHDBC ConnectionHandle = conn;
|
|
SQLHWND WindowHandle = NULL;
|
|
SQLCHAR * InConnectionString = (SQLCHAR*)connstr;
|
|
SQLSMALLINT StringLength1 = (SQLSMALLINT)(connstr ? strlen(connstr) : 0);
|
|
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) break;
|
|
D("connected");
|
|
if (1) {
|
|
SQLSMALLINT maxColumnNameLength = 0;
|
|
SQLSMALLINT len = 0;
|
|
r = SQLGetInfo(conn, SQL_MAX_COLUMN_NAME_LEN, &maxColumnNameLength, sizeof(SQLSMALLINT), &len);
|
|
CHK_RESULT(r, SQL_HANDLE_DBC, conn, "");
|
|
if (r!=SQL_SUCCESS) break;
|
|
D("maxColumnNameLength: %d", maxColumnNameLength);
|
|
}
|
|
} while (0);
|
|
SQLFreeConnect(conn);
|
|
conn = NULL;
|
|
} while (0);
|
|
SQLFreeEnv(env);
|
|
env = NULL;
|
|
|
|
return (r==SQL_SUCCESS) ? 0 : 1;
|
|
}
|
|
|
|
void usage(const char *arg0) {
|
|
fprintf(stdout, "%s usage:\n", arg0);
|
|
fprintf(stdout, "%s [--dsn <dsn>] [--uid <uid>] [--pwd <pwd>] [-C <conn_str>] [--sts <sts>]\n", arg0);
|
|
fprintf(stdout, " --dsn <dsn>: DSN\n");
|
|
fprintf(stdout, " --uid <uid>: UID\n");
|
|
fprintf(stdout, " --pwd <pwd>: PWD\n");
|
|
fprintf(stdout, " -C <conn_str>: driver connection string\n");
|
|
fprintf(stdout, " --sts <sts>: file where statements store\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
srand((unsigned)time(0));
|
|
const char *conn_str = NULL;
|
|
const char *dsn = NULL;
|
|
const char *uid = NULL;
|
|
const char *pwd = NULL;
|
|
const char *sts = NULL; // statements file
|
|
int debug_col_name_max_len = 0;
|
|
int prepare = 0;
|
|
int insert = 0;
|
|
insert_arg_t insert_arg = {
|
|
.batch_size = 100,
|
|
.batchs = 100,
|
|
.keep_stmt_among_batchs = 0
|
|
};
|
|
for (size_t i=1; i<argc; ++i) {
|
|
const char *arg = argv[i];
|
|
if (strcmp(arg, "-h")==0) {
|
|
usage(argv[0]);
|
|
return 0;
|
|
}
|
|
if (strcmp(arg, "-d")==0) {
|
|
debug_col_name_max_len = 1;
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--odbc")==0) {
|
|
insert_arg.use_odbc = 1;
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--taos_query")==0) {
|
|
insert_arg.use_taos_query= 1;
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--taos_stmt")==0) {
|
|
insert_arg.use_taos_stmt= 1;
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--insert")==0) {
|
|
insert = 1;
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--batch_size")==0) {
|
|
++i;
|
|
if (i>=argc) {
|
|
D("<batch_size> expected but got nothing");
|
|
return 1;
|
|
}
|
|
sscanf(argv[i], "%d", &insert_arg.batch_size);
|
|
if (insert_arg.batch_size<=0) {
|
|
D("<batch_size> invalid");
|
|
return 1;
|
|
}
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--batchs")==0) {
|
|
++i;
|
|
if (i>=argc) {
|
|
D("<batchs> expected but got nothing");
|
|
return 1;
|
|
}
|
|
sscanf(argv[i], "%d", &insert_arg.batchs);
|
|
if (insert_arg.batchs<=0) {
|
|
D("<batchs> invalid");
|
|
return 1;
|
|
}
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--keep_stmt_among_batchs")==0) {
|
|
insert_arg.keep_stmt_among_batchs = 1;
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--dsn")==0) {
|
|
++i;
|
|
if (i>=argc) {
|
|
D("<dsn> expected but got nothing");
|
|
return 1;
|
|
}
|
|
if (conn_str) {
|
|
D("-C has already been specified");
|
|
return 1;
|
|
}
|
|
dsn = argv[i];
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--uid")==0) {
|
|
++i;
|
|
if (i>=argc) {
|
|
D("<uid> expected but got nothing");
|
|
return 1;
|
|
}
|
|
uid = argv[i];
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--pwd")==0) {
|
|
++i;
|
|
if (i>=argc) {
|
|
D("<pwd> expected but got nothing");
|
|
return 1;
|
|
}
|
|
pwd = argv[i];
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "-C")==0) {
|
|
++i;
|
|
if (i>=argc) {
|
|
D("<connection string> expected but got nothing");
|
|
return 1;
|
|
}
|
|
if (dsn || uid || pwd) {
|
|
D("either of --dsn/--uid/--pwd has already been specified");
|
|
return 1;
|
|
}
|
|
conn_str = argv[i];
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "--sts")==0) {
|
|
++i;
|
|
if (i>=argc) {
|
|
D("<sts> expected but got nothing");
|
|
return 1;
|
|
}
|
|
sts = argv[i];
|
|
continue;
|
|
}
|
|
if (strcmp(arg, "-p")==0) {
|
|
prepare = 1;
|
|
continue;
|
|
}
|
|
}
|
|
if (debug_col_name_max_len) {
|
|
int r = do_debug_col_name_max_len(dsn, uid, pwd, conn_str);
|
|
if (r) return 1;
|
|
}
|
|
if (insert) {
|
|
const char *sqls[] = {
|
|
"drop database if exists test",
|
|
"create database test",
|
|
"create table test.v (ts timestamp, v1 tinyint, v2 smallint, v4 int, v8 bigint)",
|
|
NULL
|
|
};
|
|
int r = 0;
|
|
if (insert_arg.use_odbc) {
|
|
r = do_insert_batch(dsn, uid, pwd, conn_str, &insert_arg, sqls);
|
|
} else {
|
|
r = do_insert_batch_taos(dsn, uid, pwd, conn_str, &insert_arg, sqls);
|
|
}
|
|
if (r) return 1;
|
|
}
|
|
if (sts) {
|
|
int r = test_sqls(dsn, uid, pwd, conn_str, sts);
|
|
if (r) return 1;
|
|
}
|
|
if (prepare) {
|
|
int r = do_prepare(dsn, uid, pwd, conn_str);
|
|
if (r) return 1;
|
|
}
|
|
D("Done!");
|
|
return 0;
|
|
}
|
|
|