diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 898b7cb032846cb85e4cc8767ed6090b35f41e1a..f619edd221c005a8d8e707afa5271072b032f74a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,4 +20,6 @@ ADD_SUBDIRECTORY(tsdb) ADD_SUBDIRECTORY(wal) ADD_SUBDIRECTORY(cq) ADD_SUBDIRECTORY(dnode) +ADD_SUBDIRECTORY(connector/odbc) ADD_SUBDIRECTORY(connector/jdbc) + diff --git a/src/connector/odbc/CMakeLists.txt b/src/connector/odbc/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..32e5c7ed690abc1d2ae7b8faf50cc7b46e76a27b --- /dev/null +++ b/src/connector/odbc/CMakeLists.txt @@ -0,0 +1,17 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(TDengine) + +AUX_SOURCE_DIRECTORY(src SRC) + +# generate dynamic library (*.so) +ADD_LIBRARY(todbc SHARED ${SRC}) +SET_TARGET_PROPERTIES(todbc PROPERTIES CLEAN_DIRECT_OUTPUT 1) +SET_TARGET_PROPERTIES(todbc PROPERTIES VERSION ${TD_VER_NUMBER} SOVERSION 1) +TARGET_LINK_LIBRARIES(todbc taos) + +install(CODE "execute_process(COMMAND sudo odbcinst -i -d -f ${CMAKE_CURRENT_SOURCE_DIR}/src/template.ini + COMMAND odbcinst -i -s -f ${CMAKE_CURRENT_SOURCE_DIR}/src/template.dsn + COMMAND echo odbc install done)") + +ADD_SUBDIRECTORY(tests) + diff --git a/src/connector/odbc/src/odbc_install.sh b/src/connector/odbc/src/odbc_install.sh new file mode 100644 index 0000000000000000000000000000000000000000..3ed8078a19d5f35bf1e1df88ced2f2c5795024af --- /dev/null +++ b/src/connector/odbc/src/odbc_install.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +odbcinst -u -d -n TAOS && +odbcinst -i -d -f "$(dirname "$0")/template.ini" && +odbcinst -i -s -f "$(dirname "$0")/template.dsn" && +echo yes + diff --git a/src/connector/odbc/src/template.dsn b/src/connector/odbc/src/template.dsn new file mode 100644 index 0000000000000000000000000000000000000000..3a90205464aef8a3bbc2e738b997977cfdf3f575 --- /dev/null +++ b/src/connector/odbc/src/template.dsn @@ -0,0 +1,5 @@ +[TAOS_DSN] +Description=Connection to TAOS +Driver=TAOS +Server=localhost:1234 + diff --git a/src/connector/odbc/src/template.ini b/src/connector/odbc/src/template.ini new file mode 100644 index 0000000000000000000000000000000000000000..6e0e23154ac5d27e6a9ea38546fce6518fa3e1cc --- /dev/null +++ b/src/connector/odbc/src/template.ini @@ -0,0 +1,4 @@ +[TAOS] +Description = taos odbc driver +Driver = /home/xxh/Documents/TDengine/debug/build/lib/libtodbc.so + diff --git a/src/connector/odbc/src/todbc.c b/src/connector/odbc/src/todbc.c new file mode 100644 index 0000000000000000000000000000000000000000..fc102cbcd2a2c1a73e5130460795938f0feacfe4 --- /dev/null +++ b/src/connector/odbc/src/todbc.c @@ -0,0 +1,426 @@ +/* + * 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 . + */ + +#include "taos.h" + +#include "os.h" + +#include +#include + +#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 GET_REF(obj) atomic_load_64(&obj->refcount) +#define INC_REF(obj) atomic_add_fetch_64(&obj->refcount, 1) +#define DEC_REF(obj) atomic_sub_fetch_64(&obj->refcount, 1) + +#define LOCK(obj) pthread_mutex_lock(&obj->lock); +#define UNLOCK(obj) pthread_mutex_unlock(&obj->lock); + + +typedef struct env_s env_t; +typedef struct conn_s conn_t; +typedef struct sql_s sql_t; + + + +struct env_s { + unsigned int destroying:1; +}; + +struct conn_s { + env_t *env; + + TAOS *taos; +}; + +struct sql_s { + conn_t *conn; + + TAOS_RES *rs; + TAOS_ROW row; +}; + +static pthread_once_t init_once = PTHREAD_ONCE_INIT; +static void init_routine(void); + +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); + +SQLRETURN SQL_API SQLAllocEnv(SQLHENV *EnvironmentHandle) { + pthread_once(&init_once, init_routine); + + env_t *env = (env_t*)calloc(1, sizeof(*env)); + if (!env) return SQL_ERROR; + + *EnvironmentHandle = env; + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFreeEnv(SQLHENV EnvironmentHandle) { + env_t *env = (env_t*)EnvironmentHandle; + if (!env) return SQL_ERROR; + + DASSERT(!env->destroying); + + env->destroying = 1; + DASSERT(env->destroying == 1); + + free(env); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLAllocConnect(SQLHENV EnvironmentHandle, + SQLHDBC *ConnectionHandle) { + env_t *env = (env_t*)EnvironmentHandle; + if (!env) return SQL_ERROR; + + conn_t *conn = NULL; + do { + conn = (conn_t*)calloc(1, sizeof(*conn)); + if (!conn) break; + + conn->env = env; + *ConnectionHandle = conn; + } while (0); + + return conn ? SQL_SUCCESS : SQL_ERROR; +} + +SQLRETURN SQL_API SQLFreeConnect(SQLHDBC ConnectionHandle) { + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + do { + if (conn->taos) { + taos_close(conn->taos); + conn->taos = NULL; + } + conn->env = NULL; + free(conn); + } while (0); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, + SQLCHAR *ServerName, SQLSMALLINT NameLength1, + SQLCHAR *UserName, SQLSMALLINT NameLength2, + SQLCHAR *Authentication, SQLSMALLINT NameLength3) { + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + if (conn->taos) return SQL_ERROR; + + conn->taos = taos_connect("localhost", (const char*)UserName, (const char*)Authentication, NULL, 0); + + return conn->taos ? SQL_SUCCESS : SQL_ERROR; +} + +SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) { + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + if (conn->taos) { + taos_close(conn->taos); + conn->taos = NULL; + } + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLAllocStmt(SQLHDBC ConnectionHandle, + SQLHSTMT *StatementHandle) { + conn_t *conn = (conn_t*)ConnectionHandle; + if (!conn) return SQL_ERROR; + + sql_t *sql = (sql_t*)calloc(1, sizeof(*sql)); + if (!sql) return SQL_ERROR; + + sql->conn = conn; + *StatementHandle = sql; + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, + SQLUSMALLINT Option) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + } + sql->conn = NULL; + free(sql); + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + + if (sql->rs) { + taos_free_result(sql->rs); + sql->rs = NULL; + sql->row = NULL; + } + sql->rs = taos_query(sql->conn->taos, (const char*)StatementText); + if (sql->rs) { + sql->row = taos_fetch_row(sql->rs); + } + + return sql->row ? SQL_SUCCESS : SQL_NO_DATA; +} + +SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + + int fields = taos_field_count(sql->rs); + if (ColumnCount) { + *ColumnCount = fields; + } + + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, + SQLLEN *RowCount) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + int rows = taos_affected_rows(sql->rs); + if (RowCount) { + *RowCount = rows; + } + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLColAttribute(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength, SQLLEN *NumericAttribute ) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + int nfields = taos_field_count(sql->rs); + TAOS_FIELD *fields = taos_fetch_fields(sql->rs); + + if (nfields==0 || fields==NULL) return SQL_ERROR; + if (ColumnNumber>nfields) return SQL_ERROR; + + TAOS_FIELD *field = fields + ColumnNumber-1; + + switch (FieldIdentifier) { + case SQL_COLUMN_DISPLAY_SIZE: { + *NumericAttribute = do_field_display_size(field); + } break; + case SQL_COLUMN_LABEL: { + strncpy(CharacterAttribute, field->name, field->bytes); + } break; + default: { + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, + SQLPOINTER TargetValue, SQLLEN BufferLength, + SQLLEN *StrLen_or_Ind) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + if (!sql->row) return SQL_ERROR; + int nfields = taos_field_count(sql->rs); + TAOS_FIELD *fields = taos_fetch_fields(sql->rs); + + if (nfields==0 || fields==NULL) return SQL_ERROR; + if (ColumnNumber>nfields) return SQL_ERROR; + + TAOS_FIELD *field = fields + ColumnNumber-1; + + switch (TargetType) { + case SQL_CHAR: { + do_convert(TargetValue, BufferLength, StrLen_or_Ind, field, sql->row[ColumnNumber-1]); + *StrLen_or_Ind = SQL_NTS; + } break; + default: { + return SQL_ERROR; + } break; + } + return SQL_SUCCESS; +} + +SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) { + sql_t *sql = (sql_t*)StatementHandle; + if (!sql) return SQL_ERROR; + if (!sql->conn) return SQL_ERROR; + if (!sql->conn->taos) return SQL_ERROR; + if (!sql->rs) return SQL_ERROR; + sql->row = taos_fetch_row(sql->rs); + return sql->row ? SQL_SUCCESS : SQL_NO_DATA; +} + +SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength) { + return SQL_ERROR; +} + +SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) { + return SQL_ERROR; +} + +static void init_routine(void) { + taos_init(); +} + +static int do_field_display_size(TAOS_FIELD *field) { + switch (field->type) { + case TSDB_DATA_TYPE_TINYINT: + return 5; + break; + + case TSDB_DATA_TYPE_SMALLINT: + return 7; + break; + + case TSDB_DATA_TYPE_INT: + return 12; + break; + + case TSDB_DATA_TYPE_BIGINT: + return 22; + break; + + case TSDB_DATA_TYPE_FLOAT: { + return 12; + } break; + + case TSDB_DATA_TYPE_DOUBLE: { + return 12; + } break; + + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + return 3*(field->bytes - VARSTR_HEADER_SIZE) + 2; + } break; + + case TSDB_DATA_TYPE_TIMESTAMP: + return 22; + break; + + case TSDB_DATA_TYPE_BOOL: + return 7; + default: + break; + } + + 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'; + } 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; + } +} + diff --git a/src/connector/odbc/tests/CMakeLists.txt b/src/connector/odbc/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ac57a5647fce8bd036e133936284f3f4c847d8c8 --- /dev/null +++ b/src/connector/odbc/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +PROJECT(TDengine) + +IF (TD_LINUX) + AUX_SOURCE_DIRECTORY(. SRC) + ADD_EXECUTABLE(tcodbc main.c) + TARGET_LINK_LIBRARIES(tcodbc odbc) +ENDIF () diff --git a/src/connector/odbc/tests/main.c b/src/connector/odbc/tests/main.c new file mode 100644 index 0000000000000000000000000000000000000000..3924fe9077f60d1bbc8fcc8a7bbd272bae05062a --- /dev/null +++ b/src/connector/odbc/tests/main.c @@ -0,0 +1,20 @@ +#include + +#include + + +int main(void) { + SQLRETURN r; + SQLHENV env = {0}; + SQLHDBC conn = {0}; + r = SQLAllocEnv(&env); + if (r!=SQL_SUCCESS) return 1; + do { + r = SQLAllocConnect(env, &conn); + if (r!=SQL_SUCCESS) break; + SQLFreeConnect(conn); + } while (0); + SQLFreeEnv(env); + return r ? 1 : 0; +} + diff --git a/src/os/inc/osDef.h b/src/os/inc/osDef.h index 81c70a58fdf01e405d2e665b5a017a4696d76e91..d718bef6da42f59c5ee4ea7bfaad098b47062984 100644 --- a/src/os/inc/osDef.h +++ b/src/os/inc/osDef.h @@ -27,7 +27,7 @@ extern "C" { #define FD_VALID(x) ((x) > STDERR_FILENO) #define FD_INITIALIZER ((int32_t)-1) -#define WCHAR wchar_t +// #define WCHAR wchar_t #define POINTER_SHIFT(p, b) ((void *)((char *)(p) + (b))) #define POINTER_DISTANCE(p1, p2) ((char *)(p1) - (char *)(p2))