Changeset: 14dc205dddb4 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/14dc205dddb4
Modified Files:
        sql/backends/monet5/vaults/odbc/odbc_loader.c
Branch: Mar2025
Log Message:

Add setting SQL_ATTR_LOGIN_TIMEOUT before doing SQLDriverConnect().
Add SQLGetData() to retrieve data values.


diffs (283 lines):

diff --git a/sql/backends/monet5/vaults/odbc/odbc_loader.c 
b/sql/backends/monet5/vaults/odbc/odbc_loader.c
--- a/sql/backends/monet5/vaults/odbc/odbc_loader.c
+++ b/sql/backends/monet5/vaults/odbc/odbc_loader.c
@@ -29,28 +29,7 @@
 #include <sql.h>
 #include <sqlext.h>
 
-typedef struct odbc_loader_t {
-       SQLHANDLE env;
-       SQLHANDLE dbc;
-       SQLHANDLE stmt;
-       SQLSMALLINT nr_cols;
-} odbc_loader_t;
-
-static void
-odbc_cleanup(SQLHANDLE env, SQLHANDLE dbc, SQLHANDLE stmt) {
-       if (stmt != SQL_NULL_HSTMT) {
-               SQLFreeStmt(stmt, SQL_CLOSE);
-               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-       }
-       if (dbc != SQL_NULL_HDBC) {
-               SQLDisconnect(dbc);
-               SQLFreeHandle(SQL_HANDLE_DBC, dbc);
-       }
-       if (env != SQL_NULL_HENV) {
-               SQLFreeHandle(SQL_HANDLE_ENV, env);
-       }
-}
-
+/* map ODBC SQL datatype to MonetDB SQL datatype */
 static sql_subtype *
 map_rescol_type(SQLSMALLINT dataType, SQLULEN columnSize, SQLSMALLINT 
decimalDigits, mvc * sql)
 {
@@ -103,7 +82,9 @@ map_rescol_type(SQLSMALLINT dataType, SQ
                typenm = "double";
                break;
        case SQL_FLOAT:
-               /* the precision of SQL_FLOAT can be either 24 or 53: if it is 
24, the SQL_FLOAT data type is the same as SQL_REAL; if it is 53, the SQL_FLOAT 
data type is the same as SQL_DOUBLE. */
+               /* the precision of SQL_FLOAT can be either 24 or 53:
+                  if it is 24, the SQL_FLOAT data type is the same as SQL_REAL;
+                  if it is 53, the SQL_FLOAT data type is the same as 
SQL_DOUBLE. */
                typenm = (columnSize == 7) ? "real" : "double";
                break;
 
@@ -181,6 +162,7 @@ map_rescol_type(SQLSMALLINT dataType, SQ
        return sql_bind_subtype(sql->sa, typenm, interval_type, 0);
 }
 
+/* return name for ODBC SQL datatype */
 static char *
 nameofSQLtype(SQLSMALLINT dataType)
 {
@@ -228,14 +210,37 @@ nameofSQLtype(SQLSMALLINT dataType)
        }
 }
 
+/* utility function to nicely close all opened ODBC resources */
+static void
+odbc_cleanup(SQLHANDLE env, SQLHANDLE dbc, SQLHANDLE stmt) {
+       SQLRETURN ret = SQL_INVALID_HANDLE;
+
+       if (stmt != SQL_NULL_HSTMT) {
+               ret = SQLFreeStmt(stmt, SQL_CLOSE);
+               if (ret != SQL_INVALID_HANDLE)
+                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+       }
+       if (dbc != SQL_NULL_HDBC) {
+               ret = SQLDisconnect(dbc);
+               if (ret != SQL_INVALID_HANDLE)
+                       SQLFreeHandle(SQL_HANDLE_DBC, dbc);
+       }
+       if (env != SQL_NULL_HENV) {
+               SQLFreeHandle(SQL_HANDLE_ENV, env);
+       }
+}
+
+typedef struct odbc_loader_t {
+       SQLHANDLE env;
+       SQLHANDLE dbc;
+       SQLHANDLE stmt;
+       SQLSMALLINT nr_cols;
+} odbc_loader_t;
+
+
 /*
- * returns an error string (static or via tmp sa_allocator allocated), NULL on 
success
- *
- * Extend the subfunc f with result columns, ie.
-       f->res = typelist;
-       f->coltypes = typelist;
-       f->colnames = nameslist; use tname if passed, for the relation name
- * Fill the list res_exps, with one result expressions per resulting column.
+ * odbc_query() contains the logic for both odbc_relation() and odbc_load()
+ * the caller arg is 1 when called from odbc_relation and 2 when called from 
odbc_load
  */
 static str
 odbc_query(mvc *sql, sql_subfunc *f, char *url, list *res_exps, sql_exp *topn, 
int caller)
@@ -267,11 +272,11 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
        // create a new ODBC connection string without the QUERY= part
        char * odbc_con_str = GDKstrndup(con_str, qry_str - con_str);
 
+       // trace_enabled = true;
        if (trace_enabled)
-               printf("\nExtracted ODBC connection string: %s\nand SQL query: 
%s\n", odbc_con_str, query);
+               printf("\nExtracted ODBC connection string: %s\n  and SQL 
query: %s\n", odbc_con_str, query);
 
-
-       /* now we can try to connect to the ODBC driver and execute the SQL 
query */
+       /* now we can (try to) connect to the ODBC driver and execute the SQL 
query */
        SQLRETURN ret = SQL_INVALID_HANDLE;
        SQLHANDLE env = SQL_NULL_HENV;
        SQLHANDLE dbc = SQL_NULL_HDBC;
@@ -288,11 +293,18 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
                errmsg = "SQLSetEnvAttr (SQL_ATTR_ODBC_VERSION ODBC3) failed.";
                goto finish;
        }
+
        ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
                errmsg = "Allocate ODBC DBC handle failed.";
                goto finish;
        }
+       /* to avoid an endless blocking SQLDriverConnect() set a login timeout 
of 8s */
+       ret = SQLSetConnectAttr(dbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) 
(uintptr_t) 8UL, 0);
+       if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+               errmsg = "SQLSetConnectAttr (SQL_ATTR_LOGIN_TIMEOUT 8s) 
failed.";
+               goto finish;
+       }
 
        SQLSMALLINT len = 0;
        ret = SQLDriverConnect(dbc, NULL, (SQLCHAR *) odbc_con_str, SQL_NTS, 
NULL, 0, &len, SQL_DRIVER_NOPROMPT);
@@ -334,6 +346,8 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
        if (caller == 1) {
                char tname[1024];
                char cname[1024];
+               char * tblname;
+               char * colname;
                SQLSMALLINT dataType = 0;
                SQLULEN columnSize = 0;
                SQLSMALLINT decimalDigits = 0;
@@ -343,7 +357,7 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
                for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) nr_cols; 
col++) {
                        /* for each result column get name, datatype, size and 
decdigits */
                        ret = SQLDescribeCol(stmt, col, (SQLCHAR *) cname, 
(SQLSMALLINT) sizeof(cname),
-                               NULL, &dataType, &columnSize, &decimalDigits, 
NULL);
+                                       NULL, &dataType, &columnSize, 
&decimalDigits, NULL);
                        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) 
{
                                errmsg = "SQLDescribeCol failed.";
                                goto finish;
@@ -351,7 +365,8 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
                        if (trace_enabled)
                                printf("ResCol %d, name: %s, type %d (%s), size 
%d, decdigits %d\n",
                                        col, cname, (int)dataType, 
nameofSQLtype(dataType), (int)columnSize, (int)decimalDigits);
-                       list_append(nameslist, sa_strdup(sql->sa, cname));
+                       colname = sa_strdup(sql->sa, cname);
+                       list_append(nameslist, colname);
                        sql_mtype = map_rescol_type(dataType, columnSize, 
decimalDigits, sql);
                        list_append(typelist, sql_mtype);
 
@@ -360,16 +375,17 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
                        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) 
{
                                strcpy(tname, "");
                        }
-                       sql_exp *ne = exp_column(sql->sa, sa_strdup(sql->sa, 
tname), sa_strdup(sql->sa, cname), sql_mtype, CARD_MULTI, 1, 0, 0);
+                       tblname = sa_strdup(sql->sa, tname);
+                       sql_exp *ne = exp_column(sql->sa, tblname, colname, 
sql_mtype, CARD_MULTI, 1, 0, 0);
                        set_basecol(ne);
                        ne->alias.label = -(sql->nid++);
                        list_append(res_exps, ne);
                }
 
                f->tname = sa_strdup(sql->sa, tname);
-               f->res = typelist;
+               f->colnames = nameslist;
                f->coltypes = typelist;
-               f->colnames = nameslist;
+               f->res = typelist;
 
                odbc_loader_t *r = (odbc_loader_t *)sa_alloc(sql->sa, 
sizeof(odbc_loader_t));
                r->env = env;
@@ -381,9 +397,8 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
                goto finish;
        }
 
-       /* when called from odbc_load() we can now fetch the data */
+       /* when called from odbc_load() */
        if (caller == 2 && stmt != SQL_NULL_HSTMT) {
-               //odbc_loader_t *r = (odbc_loader_t *)f->sname;
                sql_table *t;
 
                if (trace_enabled)
@@ -397,38 +412,60 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
                        printf("After mvc_create_table()\n");
 
                node *n, *nn = f->colnames->h, *tn = f->coltypes->h;
-               for (n = f->res->h; n; n = n->next, nn = nn->next, tn = 
tn->next) {
+               int col = 1;
+               for (n = f->res->h; n && col <= nr_cols; col++, n = n->next, nn 
= nn->next, tn = tn->next) {
                        const char *name = nn->data;
                        sql_subtype *tp = tn->data;
                        sql_column *c = NULL;
 
                        if (trace_enabled)
-                               printf("Before mvc_create_column(%s)\n", name);
+                               printf("%d Before mvc_create_column(%s)\n", 
col, name);
                        if (!tp || mvc_create_column(&c, sql, t, name, tp) != 
LOG_OK) {
                                return NULL;
                        }
-               }
-
-               for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) nr_cols; 
col++) {
-                       // TODO for each result column create a buffer and bind 
it.
-                       // ret = SQLBindCol(stmt, 1, );
+                       if (trace_enabled)
+                               printf("After mvc_create_column()\n");
                }
 
                // repeat fetching data, adding data work table
                long rows = 0;
                ret = SQLFetch(stmt);
                while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
+                       SQLLEN buflen = 65535;
+                       char buf[65536];
+                       SQLLEN strLen;
+
                        rows++;
                        if (trace_enabled)
                                printf("Fetched row %ld\n", rows);
 
                        // TODO for each result column append to created 
transient table
                        for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) 
nr_cols; col++) {
-                               // ret = SQLGetData(stmt, col, ...);
-                               // copy buffer value to BUN and append
+                               buf[buflen] = '\0';
+                               strLen = SQL_NTS;
+
+                               ret = SQLGetData(stmt, col, SQL_C_CHAR, 
(SQLPOINTER *) buf, buflen, &strLen);
+                               if (buf[buflen] != '\0')
+                                       buf[buflen] = '\0';
+                               if (strLen != SQL_NTS && strLen > 0) {
+                                       if (strLen <= buflen)
+                                               if (buf[strLen] != '\0')
+                                                       buf[strLen] = '\0';
+                               }
+                               if (ret != SQL_SUCCESS && ret != 
SQL_SUCCESS_WITH_INFO) {
+                                       if (trace_enabled)
+                                               printf("Failed to get data for 
col %d of row %ld\n", col, rows);
+                               } else {
+                                       // TODO copy buffer value to BUN and 
append
+                                       if (trace_enabled)
+                                               printf("Got data for col %d of 
row %ld: %s\n", col, rows, buf);
+                               }
                        }
                        ret = SQLFetch(stmt);   // get data of next row
                }
+               /* the last SQLFetch() should have returned SQL_NO_DATA */
+               if (ret == SQL_NO_DATA)
+                       ret = SQL_SUCCESS;      // we retrieved all rows
        }
 
   finish:
@@ -440,7 +477,7 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
                SQLSMALLINT handleType;
                SQLHANDLE handle;
-               SQLCHAR state[6];
+               SQLCHAR state[SQL_SQLSTATE_SIZE +1];
                SQLINTEGER errnr;
                SQLCHAR msg[2048];
                SQLSMALLINT msglen;
@@ -457,9 +494,11 @@ odbc_query(mvc *sql, sql_subfunc *f, cha
                        handleType = SQL_HANDLE_ENV;
                        handle = env;
                }
-               ret = SQLGetDiagRec(handleType, handle, 1, state, &errnr, msg, 
sizeof(msg), &msglen);
+               ret = SQLGetDiagRec(handleType, handle, 1, state, &errnr, msg, 
(sizeof(msg) -1), &msglen);
                if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
                        str retmsg;
+                       if (state[SQL_SQLSTATE_SIZE] != '\0')
+                               state[SQL_SQLSTATE_SIZE] = '\0';
                        if (errmsg != NULL) {
                                retmsg = sa_message(sql->sa, "odbc_loader" " %s 
SQLstate %s, Errnr %d, Message %s", errmsg, (char*)state, (int)errnr, 
(char*)msg);
                        } else {
_______________________________________________
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org

Reply via email to