LCOV - code coverage report
Current view: top level - sql/backends/monet5/vaults/odbc - odbc_loader.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 726 902 80.5 %
Date: 2025-03-26 20:06:40 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024, 2025 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : #include "gdk.h"      // COLnew(), BUNappend()
      15             : #include "gdk_time.h" // date_create(), daytime_create(), timestamp_create()
      16             : #include "mal_exception.h"
      17             : #include "mal_builder.h"
      18             : #include "mal_client.h"
      19             : #include "mutils.h"   /* utf8toutf16(), utf16toutf8() */
      20             : #include "rel_proto_loader.h"
      21             : #include "rel_exp.h"
      22             : // #include "sql_decimal.h"   /* decimal_from_str() */
      23             : 
      24             : #ifdef _MSC_VER
      25             : #include <WTypes.h>
      26             : #endif
      27             : #include <stdint.h>
      28             : #include <ctype.h>
      29             : #include <wchar.h>
      30             : 
      31             : /**** Define the ODBC Version our ODBC application complies with ****/
      32             : #define ODBCVER 0x0352          /* Important: this must be defined before include of sql.h and sqlext.h */
      33             : #include <sql.h>
      34             : #include <sqlext.h>
      35             : 
      36             : 
      37             : #define ODBC_RELATION 1
      38             : #define ODBC_LOADER   2
      39             : 
      40             : #define QUERY_MAX_COLUMNS 4096
      41             : #define MAX_COL_NAME_LEN  1023
      42             : #define MAX_TBL_NAME_LEN  1023
      43             : 
      44             : #ifdef HAVE_HGE
      45             : #define MAX_PREC  38
      46             : #else
      47             : #define MAX_PREC  18
      48             : #endif
      49             : 
      50             : /* MonetDB ODBC Driver defines in ODBCGlobal.h  SQL_HUGEINT 0x4000 */
      51             : #define SQL_HUGEINT     0x4000
      52             : 
      53             : typedef struct {
      54             :         SQLSMALLINT dataType;           /* ODBC datatype */
      55             :         SQLULEN columnSize;             /* ODBC colsize, contains precision for decimals */
      56             :         SQLSMALLINT decimalDigits;      /* ODBC dec. digits, contains scale for decimals */
      57             :         int battype;                    /* MonetDB atom type, used to create the BAT */
      58             :         BAT * bat;                      /* MonetDB BAT */
      59             :         SQLSMALLINT targetType;         /* needed for SQLGetData */
      60             :         SQLPOINTER * targetValuePtr;    /* needed for SQLGetData */
      61             :         SQLLEN bufferLength;            /* needed for SQLGetData */
      62             : } rescol_t;
      63             : 
      64             : /* map ODBC SQL datatype to MonetDB SQL datatype */
      65             : static sql_subtype *
      66         192 : map_rescol_type(SQLSMALLINT dataType, SQLULEN columnSize, SQLSMALLINT decimalDigits, mvc * sql)
      67             : {
      68         192 :         char * typenm;
      69         192 :         unsigned int interval_type = 0;
      70             : 
      71         192 :         switch (dataType) {
      72          45 :         case SQL_CHAR:
      73             :         case SQL_VARCHAR:
      74             :         case SQL_LONGVARCHAR:
      75             :         case SQL_WCHAR:
      76             :         case SQL_WVARCHAR:
      77             :         case SQL_WLONGVARCHAR:
      78             :         default:        /* all other ODBC types are also mapped to varchar for now */
      79             :                 /* all ODBC char datatypes are mapped to varchar. char and clob are internally not used anymore */
      80          45 :                 if (columnSize > (SQLULEN) INT_MAX)
      81             :                         columnSize = INT_MAX;
      82          45 :                 return sql_bind_subtype(sql->sa, "varchar", (unsigned int) columnSize, 0);
      83             : 
      84           3 :         case SQL_BINARY:
      85             :         case SQL_VARBINARY:
      86             :         case SQL_LONGVARBINARY:
      87           3 :                 if (columnSize > (SQLULEN) INT_MAX)
      88             :                         columnSize = INT_MAX;
      89           3 :                 return sql_bind_subtype(sql->sa, "blob", (unsigned int) columnSize, 0);
      90             : 
      91           9 :         case SQL_DECIMAL:
      92             :         case SQL_NUMERIC:
      93             :         {
      94             :                 /* columnSize contains the defined number of digits, so precision. */
      95             :                 /* decimalDigits contains the scale (which can be negative). */
      96           9 :                 if (columnSize > MAX_PREC || abs(decimalDigits) > MAX_PREC) {
      97             :                         /* too large precision/scale, not supported by MonetDB. Map this column to a string */
      98           0 :                         if (columnSize > (SQLULEN) INT_MAX)
      99             :                                 columnSize = INT_MAX;
     100           0 :                         return sql_bind_subtype(sql->sa, "varchar", (unsigned int) columnSize +3, 0);
     101             :                 }
     102             : 
     103           9 :                 return sql_bind_subtype(sql->sa, "varchar", (unsigned int) columnSize +3, 0);
     104             : //              unsigned int prec = MAX(1, columnSize); /* precision must be >= 1 */
     105             : //              unsigned int scale = MAX(0, decimalDigits); /* negative scales are not supported by MonetDB */
     106             : //              if (prec < scale)
     107             : //                      prec = scale;   /* make precision large enough to contain all decimal digits */
     108             : //              return sql_bind_subtype(sql->sa, "decimal", prec, scale);
     109             :         }
     110           3 :         case SQL_GUID:
     111             :         {
     112             :                 /* represents a uuid of length 36, such as: dbe7343c-1f11-4fa9-a9c8-a31cd26f92fe */
     113           3 :                 sql_subtype * tp = sql_bind_subtype(sql->sa, "uuid", 0, 0);        // this fails to return a valid pointer
     114           3 :                 if (tp != NULL)
     115             :                         return tp;
     116             :                 // try a different way
     117           3 :                 sql_schema *syss = mvc_bind_schema(sql, "sys");
     118           3 :                 if (syss) {
     119           3 :                         tp = SA_ZNEW(sql->sa, sql_subtype);
     120           3 :                         if (tp != NULL) {
     121           3 :                                 tp->digits = tp->scale = 0;
     122           3 :                                 tp->type = schema_bind_type(sql, syss, "uuid");
     123           3 :                                 if (tp->type != NULL)
     124             :                                         return tp;
     125             :                         }
     126             :                 }
     127             :                 /* fall back to map it to a char(36) result column type */
     128           0 :                 return sql_bind_subtype(sql->sa, "char", (unsigned int) UUID_STRLEN, 0);
     129             :         }
     130             : 
     131             :         case SQL_BIT:
     132             :                 typenm = "boolean";
     133             :                 break;
     134             : 
     135           4 :         case SQL_TINYINT:
     136           4 :                 typenm = "tinyint";
     137           4 :                 break;
     138           4 :         case SQL_SMALLINT:
     139           4 :                 typenm = "smallint";
     140           4 :                 break;
     141          31 :         case SQL_INTEGER:
     142          31 :                 typenm = "int";
     143          31 :                 break;
     144           4 :         case SQL_BIGINT:
     145           4 :                 typenm = "bigint";
     146           4 :                 break;
     147             : #ifdef HAVE_HGE
     148           4 :         case SQL_HUGEINT:
     149           4 :                 typenm = "hugeint";
     150           4 :                 break;
     151             : #endif
     152             : 
     153             :         case SQL_REAL:
     154           4 :                 typenm = "real";
     155             :                 break;
     156             :         case SQL_DOUBLE:
     157           8 :                 typenm = "double";
     158             :                 break;
     159           0 :         case SQL_FLOAT:
     160             :                 /* the precision of SQL_FLOAT can be either 24 or 53:
     161             :                    if it is 24, the SQL_FLOAT data type is the same as SQL_REAL;
     162             :                    if it is 53, the SQL_FLOAT data type is the same as SQL_DOUBLE. */
     163           0 :                 typenm = (columnSize == 7) ? "real" : "double";
     164             :                 break;
     165             : 
     166           4 :         case SQL_TYPE_DATE:
     167           4 :                 typenm = "date";
     168           4 :                 break;
     169           4 :         case SQL_TYPE_TIME:
     170             :                 /* decimalDigits contains the precision of fractions of a second */
     171           4 :                 typenm = "time";
     172           4 :                 break;
     173           6 :         case SQL_DATETIME:
     174             :         case SQL_TYPE_TIMESTAMP:
     175             :                 /* decimalDigits contains the precision of fractions of a second */
     176           6 :                 typenm = "timestamp";
     177           6 :                 break;
     178             : 
     179           4 :         case SQL_INTERVAL_YEAR:
     180           4 :                 typenm = "month_interval";
     181           4 :                 interval_type = 1;
     182           4 :                 break;
     183           4 :         case SQL_INTERVAL_YEAR_TO_MONTH:
     184           4 :                 typenm = "month_interval";
     185           4 :                 interval_type = 2;
     186           4 :                 break;
     187           4 :         case SQL_INTERVAL_MONTH:
     188           4 :                 typenm = "month_interval";
     189           4 :                 interval_type = 3;
     190           4 :                 break;
     191           6 :         case SQL_INTERVAL_DAY:
     192           6 :                 typenm = "day_interval";
     193           6 :                 interval_type = 4;
     194           6 :                 break;
     195           4 :         case SQL_INTERVAL_HOUR:
     196           4 :                 typenm = "sec_interval";
     197           4 :                 interval_type = 8;
     198           4 :                 break;
     199           4 :         case SQL_INTERVAL_MINUTE:
     200           4 :                 typenm = "sec_interval";
     201           4 :                 interval_type = 11;
     202           4 :                 break;
     203           4 :         case SQL_INTERVAL_SECOND:
     204           4 :                 typenm = "sec_interval";
     205           4 :                 interval_type = 13;
     206           4 :                 break;
     207           4 :         case SQL_INTERVAL_DAY_TO_HOUR:
     208           4 :                 typenm = "sec_interval";
     209           4 :                 interval_type = 5;
     210           4 :                 break;
     211           4 :         case SQL_INTERVAL_DAY_TO_MINUTE:
     212           4 :                 typenm = "sec_interval";
     213           4 :                 interval_type = 6;
     214           4 :                 break;
     215           4 :         case SQL_INTERVAL_DAY_TO_SECOND:
     216           4 :                 typenm = "sec_interval";
     217           4 :                 interval_type = 7;
     218           4 :                 break;
     219           4 :         case SQL_INTERVAL_HOUR_TO_MINUTE:
     220           4 :                 typenm = "sec_interval";
     221           4 :                 interval_type = 9;
     222           4 :                 break;
     223           4 :         case SQL_INTERVAL_HOUR_TO_SECOND:
     224           4 :                 typenm = "sec_interval";
     225           4 :                 interval_type = 10;
     226           4 :                 break;
     227           4 :         case SQL_INTERVAL_MINUTE_TO_SECOND:
     228           4 :                 typenm = "sec_interval";
     229           4 :                 interval_type = 12;
     230           4 :                 break;
     231             :         }
     232         132 :         return sql_bind_subtype(sql->sa, typenm, interval_type, 0);
     233             : }
     234             : 
     235             : /* return name for ODBC SQL datatype */
     236             : static char *
     237           0 : nameofSQLtype(SQLSMALLINT dataType)
     238             : {
     239             :         /* https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/sql-data-types */
     240           0 :         switch (dataType) {
     241             :         case SQL_CHAR:          return "CHAR";
     242           0 :         case SQL_VARCHAR:       return "VARCHAR";
     243           0 :         case SQL_LONGVARCHAR:   return "LONG VARCHAR";
     244           0 :         case SQL_WCHAR:         return "WCHAR";
     245           0 :         case SQL_WVARCHAR:      return "WVARCHAR";
     246           0 :         case SQL_WLONGVARCHAR:  return "WLONGVARCHAR";
     247           0 :         case SQL_DECIMAL:       return "DECIMAL";
     248           0 :         case SQL_NUMERIC:       return "NUMERIC";
     249           0 :         case SQL_SMALLINT:      return "SMALLINT";
     250           0 :         case SQL_INTEGER:       return "INTEGER";
     251           0 :         case SQL_REAL:          return "REAL";
     252           0 :         case SQL_FLOAT:         return "FLOAT";
     253           0 :         case SQL_DOUBLE:        return "DOUBLE";
     254           0 :         case SQL_BIT:           return "BIT";
     255           0 :         case SQL_TINYINT:       return "TINYINT";
     256           0 :         case SQL_BIGINT:        return "BIGINT";
     257           0 :         case SQL_BINARY:        return "BINARY";
     258           0 :         case SQL_VARBINARY:     return "VARBINARY";
     259           0 :         case SQL_LONGVARBINARY: return "LONG VARBINARY";
     260           0 :         case SQL_DATETIME:      return "DATETIME";
     261           0 :         case SQL_TYPE_DATE:     return "DATE";
     262           0 :         case SQL_TYPE_TIME:     return "TIME";
     263           0 :         case SQL_TYPE_TIMESTAMP:        return "TIMESTAMP";
     264           0 :         case SQL_INTERVAL_MONTH:        return "INTERVAL MONTH";
     265           0 :         case SQL_INTERVAL_YEAR:         return "INTERVAL YEAR";
     266           0 :         case SQL_INTERVAL_YEAR_TO_MONTH: return "INTERVAL YEAR TO MONTH";
     267           0 :         case SQL_INTERVAL_DAY:          return "INTERVAL DAY";
     268           0 :         case SQL_INTERVAL_HOUR:         return "INTERVAL HOUR";
     269           0 :         case SQL_INTERVAL_MINUTE:       return "INTERVAL MINUTE";
     270           0 :         case SQL_INTERVAL_SECOND:       return "INTERVAL SECOND";
     271           0 :         case SQL_INTERVAL_DAY_TO_HOUR:  return "INTERVAL DAY TO HOUR";
     272           0 :         case SQL_INTERVAL_DAY_TO_MINUTE:        return "INTERVAL DAY TO MINUTE";
     273           0 :         case SQL_INTERVAL_DAY_TO_SECOND:        return "INTERVAL DAY TO SECOND";
     274           0 :         case SQL_INTERVAL_HOUR_TO_MINUTE:       return "INTERVAL HOUR TO MINUTE";
     275           0 :         case SQL_INTERVAL_HOUR_TO_SECOND:       return "INTERVAL HOUR TO SECOND";
     276           0 :         case SQL_INTERVAL_MINUTE_TO_SECOND:     return "INTERVAL MINUTE TO SECOND";
     277           0 :         case SQL_GUID:          return "GUID";
     278           0 :         case SQL_HUGEINT:       return "HUGEINT";
     279           0 :         default:                return "Driver specific type";
     280             :         }
     281             : }
     282             : 
     283             : /* name of ODBC SQLRETURN codes */
     284             : static char *
     285           0 : nameOfRetCode(SQLRETURN code)
     286             : {
     287           0 :         switch (code) {
     288             :         case SQL_SUCCESS:               return "SQL_SUCCESS";
     289           0 :         case SQL_SUCCESS_WITH_INFO:     return "SQL_SUCCESS_WITH_INFO";
     290           0 :         case SQL_ERROR:                 return "SQL_ERROR";
     291           0 :         case SQL_INVALID_HANDLE:        return "SQL_INVALID_HANDLE";
     292           0 :         case SQL_STILL_EXECUTING:       return "SQL_STILL_EXECUTING";
     293           0 :         case SQL_NEED_DATA:             return "SQL_NEED_DATA";
     294           0 :         case SQL_NO_DATA:               return "SQL_NO_DATA";
     295           0 :         default:                return "SQLRETURN ??";
     296             :         }
     297             : }
     298             : 
     299             : #ifdef HAVE_HGE
     300             : static hge
     301           3 : str_to_hge(const char *s) {
     302           3 :         char c;
     303           3 :         char sign = '+';
     304           3 :         int i = 0;
     305           3 :         hge ret = 0;
     306             : 
     307           3 :         if (!s)
     308             :                 return 0;
     309             : 
     310           3 :         c = s[i];
     311           3 :         if (c == '-' || c == '+') {
     312           1 :                 sign = c;
     313           1 :                 c = s[++i];
     314             :         }
     315          82 :         while (c) {
     316          79 :                 if (c >= '0' && c <= '9') {
     317          79 :                         ret *= 10;
     318          79 :                         ret += (int) c - '0';
     319             :                 }
     320          79 :                 c = s[++i];
     321             :         }
     322           3 :         if (sign == '-')
     323           1 :                 ret = -ret;
     324             :         return ret;
     325             : }
     326             : #endif
     327             : 
     328             : /* an ODBC function call returned an error, get the error msg from the ODBC driver */
     329             : static char *
     330           5 : getErrMsg(SQLSMALLINT handleType, SQLHANDLE handle) {
     331           5 :         SQLRETURN ret;
     332           5 :         SQLCHAR state[SQL_SQLSTATE_SIZE +1];
     333           5 :         SQLINTEGER errnr;
     334           5 :         SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH] = { 0 };
     335           5 :         SQLSMALLINT msglen = SQL_MAX_MESSAGE_LENGTH -1;
     336             : 
     337           5 :         if (handle == SQL_NULL_HSTMT)
     338             :                 return NULL;
     339             : 
     340             :         // TODO use ODBC W function
     341           5 :         ret = SQLGetDiagRec(handleType, handle, 1, state, &errnr, msg, SQL_MAX_MESSAGE_LENGTH -1, &msglen);
     342           5 :         if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
     343           5 :                 const char format[] = "SQLSTATE %s, Error code %d, Message %s";
     344             :                 /* ignore msg when using MS Excel ODBC driver, which does not support setting connection timeout */
     345           5 :                 if ((strcmp("IM006", (char *)state) == 0)
     346           0 :                  && (strcmp("[Microsoft][ODBC Driver Manager] Driver's SQLSetConnectAttr failed", (char *)msg) == 0)) {
     347           5 :                         return NULL;
     348             :                 }
     349             : 
     350           5 :                 if (msglen <= 0) {
     351             :                         /* e.g SQL_NTS */
     352           0 :                         msglen = (SQLSMALLINT) strlen((char *)msg);
     353             :                 }
     354           5 :                 char * retmsg = (char *) GDKmalloc(sizeof(format) + SQL_SQLSTATE_SIZE + 10 + msglen);
     355           5 :                 if (retmsg != NULL) {
     356           5 :                         if (state[SQL_SQLSTATE_SIZE] != '\0')
     357           0 :                                 state[SQL_SQLSTATE_SIZE] = '\0';
     358           5 :                         sprintf(retmsg, format, (char *)state, errnr, (char *)msg);
     359           5 :                         return retmsg;
     360             :                 }
     361             :         }
     362             :         return NULL;
     363             : }
     364             : 
     365             : /* utility function to safely close all opened ODBC resources */
     366             : static void
     367         107 : odbc_cleanup(SQLHANDLE env, SQLHANDLE dbc, SQLHANDLE stmt) {
     368         107 :         SQLRETURN ret = SQL_SUCCESS;
     369             : 
     370         107 :         if (stmt != SQL_NULL_HSTMT) {
     371         103 :                 ret = SQLFreeStmt(stmt, SQL_CLOSE);
     372         103 :                 if (ret != SQL_INVALID_HANDLE)
     373         103 :                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
     374             :         }
     375         107 :         if (dbc != SQL_NULL_HDBC) {
     376         107 :                 ret = SQLDisconnect(dbc);
     377         107 :                 if (ret != SQL_INVALID_HANDLE)
     378         107 :                         SQLFreeHandle(SQL_HANDLE_DBC, dbc);
     379             :         }
     380         107 :         if (env != SQL_NULL_HENV) {
     381         107 :                 SQLFreeHandle(SQL_HANDLE_ENV, env);
     382             :         }
     383         107 : }
     384             : 
     385             : /* copied from monetdb5/modules/mal/tablet.c */
     386             : static BAT *
     387         192 : bat_create(int adt, BUN nr)
     388             : {
     389         192 :         BAT *b = COLnew(0, adt, nr, TRANSIENT);
     390             : 
     391             :         /* check for correct structures */
     392         192 :         if (b == NULL)
     393             :                 return NULL;
     394         192 :         if ((b = BATsetaccess(b, BAT_APPEND)) == NULL) {
     395             :                 return NULL;
     396             :         }
     397             : 
     398             :         /* disable all properties here */
     399         192 :         b->tsorted = false;
     400         192 :         b->trevsorted = false;
     401         192 :         b->tnosorted = 0;
     402         192 :         b->tnorevsorted = 0;
     403         192 :         b->tseqbase = oid_nil;
     404         192 :         b->tkey = false;
     405         192 :         b->tnokey[0] = 0;
     406         192 :         b->tnokey[1] = 0;
     407         192 :         return b;
     408             : }
     409             : 
     410             : /* convert interval.day_second.fraction values to millisec fractions as needed by MonetDB interval types.
     411             :  * we need the columns decimalDigits specification to adjust the fractions value to millisec.
     412             :  */
     413             : static SQLUINTEGER
     414          12 : fraction2msec(SQLUINTEGER fraction, SQLSMALLINT decimaldigits) {
     415          12 :         SQLUINTEGER msec = fraction;
     416          12 :         if (msec == 0)
     417             :                 return 0;
     418             : 
     419           8 :         switch (decimaldigits) {
     420           8 :                 case 6: msec = fraction / 1000; break;
     421             :                 case 3: msec = fraction; break;
     422           0 :                 case 0: msec = fraction * 1000; break;
     423           0 :                 case 1: msec = fraction * 100; break;
     424           0 :                 case 2: msec = fraction * 10; break;
     425           0 :                 case 4: msec = fraction / 10; break;
     426           0 :                 case 5: msec = fraction / 100; break;
     427           0 :                 case 7: msec = fraction / 10000; break;
     428           0 :                 case 8: msec = fraction / 100000; break;
     429           0 :                 case 9: msec = fraction / 1000000; break;
     430             :         }
     431             : 
     432             :         // millisec value should be no larger than 999
     433           8 :         while (msec > 999) {
     434           0 :                 msec = msec / 10;
     435             :         }
     436             :         return msec;
     437             : }
     438             : 
     439             : /*
     440             :  * odbc_query() contains the logic for both odbc_relation() and ODBCloader()
     441             :  * the caller argument is ODBC_RELATION when called from odbc_relation and ODBC_LOADER when called from ODBCloader
     442             :  */
     443             : static str
     444         118 : odbc_query(int caller, mvc *sql, sql_subfunc *f, char *url, list *res_exps, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
     445             : {
     446         118 :         if (sql == NULL)
     447             :                 return "Missing mvc value.";
     448         118 :         if (f == NULL)
     449             :                 return "Missing sql_subfunc value.";
     450             : 
     451             :         /* check received url and extract the ODBC connection string and the SQL query */
     452         118 :         if (!url || (url && strncasecmp("odbc:", url, 5) != 0))
     453             :                 return "Invalid URI. Must start with 'odbc:'.";
     454             : 
     455             :         // skip 'odbc:' prefix from url so we get a connection string including the query
     456         118 :         char * con_str = &url[5];
     457             :         /* the connection string must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='
     458             :            else the ODBC driver manager can't load the ODBC driver */
     459         118 :         if (con_str
     460         118 :           && (strncmp("DSN=", con_str, 4) != 0)
     461          46 :           && (strncmp("DRIVER=", con_str, 7) != 0)
     462          10 :           && (strncmp("FILEDSN=", con_str, 8) != 0))
     463             :                 return "Invalid ODBC connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.";
     464             : 
     465             :         // locate the 'QUERY=' part to extract the SQL query string to execute
     466         109 :         char * qry_str = strstr(con_str, "QUERY=");
     467         109 :         if (qry_str == NULL)
     468             :                 return "Incomplete ODBC URI string. Missing 'QUERY=' part to specify the SQL SELECT query to execute.";
     469             : 
     470         107 :         char * query = GDKstrdup(&qry_str[6]);      // we expect that QUERY= is at the end of the connection string
     471         107 :         if (query == NULL || *query == 0) {
     472           0 :                 GDKfree(query);
     473           0 :                 return "Incomplete ODBC URI string. Missing SQL SELECT query after 'QUERY='.";
     474             :         }
     475             : 
     476             :         // create a new ODBC connection string without the QUERY= part
     477         107 :         char * odbc_con_str = GDKstrndup(con_str, qry_str - con_str);
     478         107 :         if (odbc_con_str == NULL) {
     479           0 :                 GDKfree(query);
     480           0 :                 return "Missing ODBC connection string.";
     481             :         }
     482             : 
     483         107 :         TRC_INFO(LOADER, "\nExtracted ODBC connection string: %s\n  and SQL query: %s\n", odbc_con_str, query);
     484             : 
     485             :         /* now we can (try to) connect to the ODBC driver and execute the SQL query */
     486         107 :         SQLRETURN ret = SQL_INVALID_HANDLE;
     487         107 :         SQLHANDLE env = SQL_NULL_HENV;
     488         107 :         SQLHANDLE dbc = SQL_NULL_HDBC;
     489         107 :         SQLHANDLE stmt = SQL_NULL_HSTMT;
     490         107 :         char * errmsg = NULL;
     491             : 
     492         107 :         ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
     493         107 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     494           0 :                 errmsg = "Allocate ODBC ENV handle failed.";
     495           0 :                 goto finish;
     496             :         }
     497         107 :         ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (uintptr_t) SQL_OV_ODBC3, 0);
     498         107 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     499           0 :                 errmsg = "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION ODBC3) failed.";
     500           0 :                 goto finish;
     501             :         }
     502             : 
     503         107 :         ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
     504         107 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     505           0 :                 errmsg = "Allocate ODBC DBC handle failed.";
     506           0 :                 goto finish;
     507             :         }
     508             :         /* to avoid an endless blocking SQLDriverConnect() set a login timeout of 8s */
     509         107 :         ret = SQLSetConnectAttr(dbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) (uintptr_t) 8UL, 0);
     510         107 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     511           0 :                 errmsg = "SQLSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT 8 sec) failed.";
     512           0 :                 goto finish;
     513             :         }
     514             : 
     515         107 :         SQLSMALLINT len = 0;
     516         107 :         uint16_t * odbc_con_Wstr = utf8toutf16(odbc_con_str);
     517             : #define MAX_CONNECT_OUT_STR 2048
     518         107 :         if (odbc_con_Wstr != NULL) {
     519         107 :                 SQLWCHAR outstr[MAX_CONNECT_OUT_STR];
     520         107 :                 ret = SQLDriverConnectW(dbc, NULL, (SQLWCHAR *) odbc_con_Wstr, SQL_NTS, outstr, MAX_CONNECT_OUT_STR, &len, SQL_DRIVER_NOPROMPT);
     521             :                 /* we no longer need odbc_con_Wstr */
     522         107 :                 free(odbc_con_Wstr);
     523             :         } else {
     524           0 :                 SQLCHAR outstr[MAX_CONNECT_OUT_STR];
     525           0 :                 ret = SQLDriverConnect(dbc, NULL, (SQLCHAR *) odbc_con_str, SQL_NTS, outstr, MAX_CONNECT_OUT_STR, &len, SQL_DRIVER_NOPROMPT);
     526             :         }
     527         107 :         if (ret == SQL_SUCCESS_WITH_INFO && caller == ODBC_RELATION) {
     528             :                 /* show the info warning, but only once */
     529           0 :                 char * ODBCmsg = getErrMsg(SQL_HANDLE_DBC, dbc);
     530           0 :                 TRC_INFO(LOADER, "SQLDriverConnect(%s) returned %s ODBCmsg: %s\n", odbc_con_str, nameOfRetCode(ret), (ODBCmsg) ? ODBCmsg : "");
     531           0 :                 if (ODBCmsg)
     532           0 :                         GDKfree(ODBCmsg);
     533             :         } else {
     534         107 :                 TRC_DEBUG(LOADER, "SQLDriverConnect(%s) returned %s\n", odbc_con_str, nameOfRetCode(ret));
     535             :         }
     536         107 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     537           4 :                 errmsg = "SQLDriverConnect failed.";
     538           4 :                 goto finish;
     539             :         }
     540             :         /* we no longer need odbc_con_str */
     541         103 :         GDKfree(odbc_con_str);
     542         103 :         odbc_con_str = NULL;
     543             : 
     544         103 :         ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
     545         103 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     546           0 :                 errmsg = "Allocate ODBC STMT handle failed.";
     547           0 :                 goto finish;
     548             :         }
     549             : 
     550             : #ifdef HAVE_HGE
     551             :         {
     552         103 :                 char DBMSname[128];
     553         103 :                 ret = SQLGetInfo(dbc, SQL_DBMS_NAME, (SQLPOINTER) &DBMSname, 127, NULL);
     554         103 :                 if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
     555         103 :                         TRC_DEBUG(LOADER, "SQLGetInfo(dbc, SQL_DBMS_NAME) returned %s\n", DBMSname);
     556         103 :                         if (strcmp("MonetDB", DBMSname) == 0) {
     557             :                                 /* instruct the MonetDB ODBC driver to return SQL_HUGEINT as column datatype */
     558          70 :                                 ret = SQLGetTypeInfo(stmt, SQL_HUGEINT);
     559          70 :                                 TRC_DEBUG(LOADER, "SQLGetTypeInfo(stmt, SQL_HUGEINT) returned %s\n", nameOfRetCode(ret));
     560          70 :                                 if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
     561          70 :                                         ret = SQLCloseCursor(stmt);
     562             :                                 }
     563             :                         }
     564             :                 }
     565             :         }
     566             : #endif
     567             : 
     568         103 :         uint16_t * query_Wstr = utf8toutf16(query);
     569         103 :         if (query_Wstr != NULL) {
     570         103 :                 ret = SQLExecDirectW(stmt, (SQLWCHAR *) query_Wstr, SQL_NTS);
     571             :                 /* we no longer need query_Wstr */
     572         103 :                 free(query_Wstr);
     573             :         } else {
     574           0 :                 ret = SQLExecDirect(stmt, (SQLCHAR *) query, SQL_NTS);
     575             :         }
     576         103 :         if (ret == SQL_SUCCESS_WITH_INFO && caller == ODBC_RELATION) {
     577             :                 /* show the info warning, but only once */
     578           0 :                 char * ODBCmsg = getErrMsg(SQL_HANDLE_STMT, stmt);
     579           0 :                 TRC_INFO(LOADER, "SQLExecDirect(%s) returned %s ODBCmsg: %s\n", query, nameOfRetCode(ret), (ODBCmsg) ? ODBCmsg : "");
     580           0 :                 if (ODBCmsg)
     581           0 :                         GDKfree(ODBCmsg);
     582             :         } else {
     583         103 :                 TRC_DEBUG(LOADER, "SQLExecDirect(%s) returned %s\n", query, nameOfRetCode(ret));
     584             :         }
     585         103 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     586           1 :                 errmsg = "SQLExecDirect query failed.";
     587           1 :                 goto finish;
     588             :         }
     589             :         /* we no longer need query string */
     590         102 :         GDKfree(query);
     591         102 :         query = NULL;
     592             : 
     593         102 :         SQLSMALLINT nr_cols = 0;
     594         102 :         ret = SQLNumResultCols(stmt, &nr_cols);
     595         102 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     596           0 :                 errmsg = "SQLNumResultCols failed.";
     597           0 :                 goto finish;
     598             :         }
     599         102 :         if (nr_cols <= 0) {
     600           0 :                 errmsg = "ODBC query did not return a resultset.";
     601           0 :                 goto finish;
     602             :         }
     603         102 :         TRC_INFO(LOADER, "Query has %d result columns\n", nr_cols);
     604         102 :         if (nr_cols > QUERY_MAX_COLUMNS) {
     605             :                 /* limit the number of data columns, as we do not want to block or blow up the mserver */
     606           0 :                 nr_cols = QUERY_MAX_COLUMNS;
     607           0 :                 TRC_INFO(LOADER, "ODBC_loader limited Query result to first %d columns.\n", nr_cols);
     608             :         }
     609             : 
     610             :         /* when called from odbc_relation() */
     611         102 :         if (caller == ODBC_RELATION) {
     612          51 :                 char tname[MAX_TBL_NAME_LEN +1];
     613          51 :                 char cname[MAX_COL_NAME_LEN +1];
     614          51 :                 char * tblname;
     615          51 :                 char * colname;
     616          51 :                 SQLSMALLINT dataType = 0;
     617          51 :                 SQLULEN columnSize = 0;
     618          51 :                 SQLSMALLINT decimalDigits = 0;
     619          51 :                 sql_subtype * sql_mtype;
     620          51 :                 list * typelist = sa_list(sql->sa);
     621          51 :                 list * nameslist = sa_list(sql->sa);
     622          51 :                 strcpy(tname, "");
     623         243 :                 for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) nr_cols; col++) {
     624             :                         /* for each result column get name, datatype, size and decdigits */
     625             :                         // TODO use ODBC W function
     626         192 :                         ret = SQLDescribeCol(stmt, col, (SQLCHAR *) cname, (SQLSMALLINT) MAX_COL_NAME_LEN,
     627             :                                         NULL, &dataType, &columnSize, &decimalDigits, NULL);
     628         192 :                         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     629           0 :                                 errmsg = "SQLDescribeCol failed.";
     630           0 :                                 goto finish;
     631             :                         }
     632         192 :                         TRC_DEBUG(LOADER, "ResCol %u, name: %s, type %d (%s), size %u, decdigits %d\n",
     633             :                                         col, cname, dataType, nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits);
     634         192 :                         sql_mtype = map_rescol_type(dataType, columnSize, decimalDigits, sql);
     635         192 :                         if (sql_mtype == NULL)
     636           0 :                                 continue;       /* skip this column */
     637             : 
     638         192 :                         colname = sa_strdup(sql->sa, cname);
     639         192 :                         list_append(nameslist, colname);
     640         192 :                         list_append(typelist, sql_mtype);
     641             : 
     642         192 :                         if (res_exps) {
     643             :                                 /* also get the table name for this result column */
     644             :                                 // TODO use ODBC W function
     645         192 :                                 ret = SQLColAttribute(stmt, col, SQL_DESC_TABLE_NAME, (SQLPOINTER) tname, (SQLSMALLINT) MAX_TBL_NAME_LEN, NULL, NULL);
     646         192 :                                 if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     647           0 :                                         strcpy(tname, "");
     648             :                                 }
     649         192 :                                 tblname = sa_strdup(sql->sa, tname);
     650         192 :                                 sql_exp *ne = exp_column(sql->sa, tblname, colname, sql_mtype, CARD_MULTI, 1, 0, 0);
     651         192 :                                 set_basecol(ne);
     652         192 :                                 ne->alias.label = -(sql->nid++);
     653         192 :                                 list_append(res_exps, ne);
     654             :                         }
     655             :                 }
     656             : 
     657          51 :                 f->tname = sa_strdup(sql->sa, tname);
     658          51 :                 f->colnames = nameslist;
     659          51 :                 f->coltypes = typelist;
     660          51 :                 f->res = typelist;
     661          51 :                 goto finish;
     662             :         }
     663             : 
     664             :         /* when called from ODBCloader() */
     665          51 :         if (caller == ODBC_LOADER) {
     666          51 :                 rescol_t * colmetadata = (rescol_t *) GDKzalloc(nr_cols * sizeof(rescol_t));
     667          51 :                 if (colmetadata == NULL) {
     668           0 :                         errmsg = "GDKzalloc colmetadata[nr_cols] failed.";
     669           0 :                         goto finish;
     670             :                 }
     671             : 
     672             :                 /* allocate buffers for each of the fixed size atom types. */
     673          51 :                 bit bit_val = 0;
     674          51 :                 bte bte_val = 0;
     675          51 :                 sht sht_val = 0;
     676          51 :                 int int_val = 0;
     677          51 :                 lng lng_val = 0;
     678             : #ifdef HAVE_HGE
     679          51 :                 hge hge_val = 0;        // for hugeint and decimals with precision > 18
     680             : #endif
     681          51 :                 flt flt_val = 0;
     682          51 :                 dbl dbl_val = 0;
     683          51 :                 DATE_STRUCT date_val;
     684          51 :                 TIME_STRUCT time_val;
     685          51 :                 TIMESTAMP_STRUCT ts_val;
     686          51 :                 SQL_INTERVAL_STRUCT itv_val;
     687          51 :                 SQLGUID guid_val;
     688          51 :                 union {
     689             :                         uuid uuid_val;
     690             :                         uint8_t u[UUID_SIZE];
     691             :                 } u_val;
     692             : 
     693          51 :                 bool hasStrCols = false;
     694          51 :                 SQLULEN largestStringSize = 0;
     695          51 :                 bool hasBlobCols = false;
     696          51 :                 SQLULEN largestBlobSize = 0;
     697             : 
     698             :                 /* make bats with right atom type */
     699         243 :                 for (SQLUSMALLINT col = 0; col < (SQLUSMALLINT) nr_cols; col++) {
     700         192 :                         char cname[MAX_COL_NAME_LEN +1];
     701         192 :                         SQLSMALLINT dataType = 0;
     702         192 :                         SQLULEN columnSize = 0;
     703         192 :                         SQLSMALLINT decimalDigits = 0;
     704         192 :                         int battype = TYPE_str;
     705         192 :                         BAT * b = NULL;
     706             : 
     707             :                         /* for each result column get SQL datatype, size and decdigits */
     708             :                         // TODO use ODBC W function
     709         192 :                         ret = SQLDescribeCol(stmt, col+1, (SQLCHAR *) cname, (SQLSMALLINT) MAX_COL_NAME_LEN,
     710             :                                         NULL, &dataType, &columnSize, &decimalDigits, NULL);
     711         192 :                         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     712           0 :                                 errmsg = "SQLDescribeCol failed.";
     713             :                                 /* cleanup already created bats */
     714           0 :                                 while (col > 0) {
     715           0 :                                         col--;
     716           0 :                                         BBPreclaim(colmetadata[col].bat);
     717             :                                 }
     718           0 :                                 GDKfree(colmetadata);
     719           0 :                                 goto finish;
     720             :                         }
     721         192 :                         TRC_DEBUG(LOADER, "DescCol %u, name: %s, type %d (%s), size %u, decdigits %d\n",
     722             :                                         col+1, cname, dataType, nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits);
     723             : 
     724         192 :                         colmetadata[col].dataType = dataType;
     725         192 :                         colmetadata[col].columnSize = columnSize;
     726         192 :                         colmetadata[col].decimalDigits = decimalDigits;
     727         192 :                         colmetadata[col].bufferLength = 0;
     728             : 
     729         192 :                         battype = getBatType(getArgType(mb, pci, col));
     730         192 :                         colmetadata[col].battype = battype;
     731         192 :                         if (battype == TYPE_str) {
     732          54 :                                 hasStrCols = true;
     733          54 :                                 if (dataType == SQL_DECIMAL || dataType == SQL_NUMERIC) {
     734             :                                         /* read it as string */
     735           9 :                                         if (columnSize < 38) {
     736           7 :                                                 columnSize = 38;
     737             :                                         }
     738             :                                         /* add 3 for: sign, possible leading 0 and decimal separator */
     739           9 :                                         columnSize += 3;
     740           9 :                                         colmetadata[col].columnSize = columnSize;
     741             :                                 }
     742          54 :                                 if (columnSize > largestStringSize) {
     743             :                                         largestStringSize = columnSize;
     744             :                                 }
     745             :                         } else
     746             : #ifdef HAVE_HGE
     747         138 :                         if (battype == TYPE_hge) {
     748           4 :                                 if (dataType == SQL_HUGEINT) {
     749             :                                         /* read it as string */
     750           4 :                                         hasStrCols = true;
     751           4 :                                         if (columnSize < 50) {
     752           4 :                                                 columnSize = 50;
     753           4 :                                                 colmetadata[col].columnSize = columnSize;
     754             :                                         }
     755           4 :                                         if (columnSize > largestStringSize) {
     756             :                                                 largestStringSize = columnSize;
     757             :                                         }
     758           4 :                                         colmetadata[col].bufferLength = largestStringSize;
     759             :                                 }
     760             :                         } else
     761             : #endif
     762         134 :                         if (battype == TYPE_blob) {
     763           3 :                                 hasBlobCols = true;
     764           3 :                                 if (columnSize > largestBlobSize) {
     765             :                                         largestBlobSize = columnSize;
     766             :                                 }
     767             :                         }
     768             : 
     769             :                         /* mapping based on https://learn.microsoft.com/en-us/sql/odbc/reference/appendixes/c-data-types */
     770         192 :                         switch(dataType) {
     771          45 :                                 case SQL_CHAR:
     772             :                                 case SQL_VARCHAR:
     773             :                                 case SQL_LONGVARCHAR:
     774             :                                 case SQL_WCHAR:
     775             :                                 case SQL_WVARCHAR:
     776             :                                 case SQL_WLONGVARCHAR:
     777             :                                 default:
     778          45 :                                         colmetadata[col].targetType = SQL_C_CHAR;       // TODO later: SQL_C_WCHAR
     779             :                                         // colmetadata[col].targetValuePtr = (SQLPOINTER *) str_val;  // will be done after allocation
     780          45 :                                         break;
     781           5 :                                 case SQL_BIT:
     782           5 :                                         colmetadata[col].targetType = SQL_C_BIT;
     783           5 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &bit_val;
     784           5 :                                         break;
     785           4 :                                 case SQL_TINYINT:
     786           4 :                                         colmetadata[col].targetType = SQL_C_STINYINT;
     787           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &bte_val;
     788           4 :                                         break;
     789           4 :                                 case SQL_SMALLINT:
     790           4 :                                         colmetadata[col].targetType = SQL_C_SSHORT;
     791           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &sht_val;
     792           4 :                                         break;
     793          31 :                                 case SQL_INTEGER:
     794          31 :                                         colmetadata[col].targetType = SQL_C_SLONG;
     795          31 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &int_val;
     796          31 :                                         break;
     797           4 :                                 case SQL_BIGINT:
     798           4 :                                         colmetadata[col].targetType = SQL_C_SBIGINT;
     799           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &lng_val;
     800           4 :                                         break;
     801           4 :                                 case SQL_HUGEINT:
     802             :                                         /* read huge int data as string data as there is no SQL_C_SHUGEINT */
     803           4 :                                         colmetadata[col].targetType = SQL_C_CHAR;
     804             :                                         // colmetadata[col].targetValuePtr = (SQLPOINTER *) str_val;  // will be done after allocation
     805           4 :                                         break;
     806           9 :                                 case SQL_DECIMAL:
     807             :                                 case SQL_NUMERIC:
     808             :                                         /* read decimal data always as string data and convert it to the right internal decimal format and bat type */
     809           9 :                                         colmetadata[col].targetType = SQL_C_CHAR;
     810             :                                         // colmetadata[col].targetValuePtr = (SQLPOINTER *) str_val;  // will be done after allocation
     811           9 :                                         break;
     812           4 :                                 case SQL_REAL:
     813           4 :                                         colmetadata[col].targetType = SQL_C_FLOAT;
     814           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &flt_val;
     815           4 :                                         break;
     816           0 :                                 case SQL_FLOAT:
     817             :                                         /* use same logic as used in map_rescol_type() for SQL_FLOAT and SQL_DECIMAL */
     818           0 :                                         if (colmetadata[col].battype == TYPE_flt) {
     819           0 :                                                 colmetadata[col].dataType = SQL_REAL;
     820           0 :                                                 colmetadata[col].targetType = SQL_C_FLOAT;
     821           0 :                                                 colmetadata[col].targetValuePtr = (SQLPOINTER *) &flt_val;
     822             :                                         } else {
     823           0 :                                                 colmetadata[col].dataType = SQL_DOUBLE;
     824           0 :                                                 colmetadata[col].targetType = SQL_C_DOUBLE;
     825           0 :                                                 colmetadata[col].targetValuePtr = (SQLPOINTER *) &dbl_val;
     826             :                                         }
     827             :                                         break;
     828           8 :                                 case SQL_DOUBLE:
     829           8 :                                         colmetadata[col].targetType = SQL_C_DOUBLE;
     830           8 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &dbl_val;
     831           8 :                                         break;
     832           4 :                                 case SQL_TYPE_DATE:
     833           4 :                                         colmetadata[col].targetType = SQL_C_TYPE_DATE;
     834           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &date_val;
     835           4 :                                         break;
     836           4 :                                 case SQL_TYPE_TIME:
     837           4 :                                         colmetadata[col].targetType = SQL_C_TYPE_TIME;
     838           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &time_val;
     839           4 :                                         break;
     840           6 :                                 case SQL_DATETIME:
     841             :                                 case SQL_TYPE_TIMESTAMP:
     842           6 :                                         colmetadata[col].targetType = SQL_C_TYPE_TIMESTAMP;
     843           6 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &ts_val;
     844           6 :                                         break;
     845           4 :                                 case SQL_INTERVAL_YEAR:
     846           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_YEAR;
     847           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     848           4 :                                         break;
     849           4 :                                 case SQL_INTERVAL_YEAR_TO_MONTH:
     850           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_YEAR_TO_MONTH;
     851           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     852           4 :                                         break;
     853           4 :                                 case SQL_INTERVAL_MONTH:
     854           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_MONTH;
     855           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     856           4 :                                         break;
     857           6 :                                 case SQL_INTERVAL_DAY:
     858           6 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_DAY;
     859           6 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     860           6 :                                         break;
     861           4 :                                 case SQL_INTERVAL_HOUR:
     862           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_HOUR;
     863           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     864           4 :                                         break;
     865           4 :                                 case SQL_INTERVAL_MINUTE:
     866           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_MINUTE;
     867           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     868           4 :                                         break;
     869           4 :                                 case SQL_INTERVAL_SECOND:
     870           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_SECOND;
     871           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     872           4 :                                         break;
     873           4 :                                 case SQL_INTERVAL_DAY_TO_HOUR:
     874           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_DAY_TO_HOUR;
     875           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     876           4 :                                         break;
     877           4 :                                 case SQL_INTERVAL_DAY_TO_MINUTE:
     878           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_DAY_TO_MINUTE;
     879           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     880           4 :                                         break;
     881           4 :                                 case SQL_INTERVAL_DAY_TO_SECOND:
     882           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_DAY_TO_SECOND;
     883           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     884           4 :                                         break;
     885           4 :                                 case SQL_INTERVAL_HOUR_TO_MINUTE:
     886           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_HOUR_TO_MINUTE;
     887           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     888           4 :                                         break;
     889           4 :                                 case SQL_INTERVAL_HOUR_TO_SECOND:
     890           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_HOUR_TO_SECOND;
     891           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     892           4 :                                         break;
     893           4 :                                 case SQL_INTERVAL_MINUTE_TO_SECOND:
     894           4 :                                         colmetadata[col].targetType = SQL_C_INTERVAL_MINUTE_TO_SECOND;
     895           4 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
     896           4 :                                         break;
     897           3 :                                 case SQL_GUID:
     898           3 :                                         colmetadata[col].targetType = SQL_C_GUID;
     899           3 :                                         colmetadata[col].targetValuePtr = (SQLPOINTER *) &guid_val;
     900           3 :                                         colmetadata[col].bufferLength = (SQLLEN) sizeof(SQLGUID);
     901           3 :                                         break;
     902           3 :                                 case SQL_BINARY:
     903             :                                 case SQL_VARBINARY:
     904             :                                 case SQL_LONGVARBINARY:
     905           3 :                                         colmetadata[col].targetType = SQL_C_BINARY;
     906             :                                         // colmetadata[col].targetValuePtr = (SQLPOINTER *) bin_data;  // will be done after allocation
     907           3 :                                         break;
     908             :                         }
     909             : 
     910         192 :                         TRC_INFO(LOADER, "ResCol %u, name: %s, type %d (%s), size %u, decdigits %d, battype %d\n",
     911             :                                         col+1, cname, dataType, nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits, battype);
     912             : 
     913         192 :                         TRC_DEBUG(LOADER, "Before create BAT %d type %d\n", col+1, battype);
     914         192 :                         b = bat_create(battype, 0);
     915         192 :                         if (b) {
     916         192 :                                 colmetadata[col].bat = b;
     917         192 :                                 TRC_DEBUG(LOADER, "Created BAT %d\n", col+1);
     918             :                         } else {
     919           0 :                                 errmsg = "Failed to create bat.";
     920             :                                 /* cleanup already created bats */
     921           0 :                                 while (col > 0) {
     922           0 :                                         col--;
     923           0 :                                         BBPreclaim(colmetadata[col].bat);
     924             :                                 }
     925           0 :                                 GDKfree(colmetadata);
     926           0 :                                 goto finish;
     927             :                         }
     928             :                 }
     929             : 
     930             :                 /* allocate large enough read buffers for storing string (and binary blob) data */
     931          51 :                 char * str_val = NULL;          // TODO: change to wchar
     932          51 :                 uint8_t * bin_data = NULL;
     933             : 
     934          51 :                 if (largestStringSize == 0 && hasStrCols)       // no valid string length, use 65535 (64kB) as default
     935             :                         largestStringSize = 65535;
     936          51 :                 else if (largestStringSize < 1023)   // for very large decimals read as strings
     937             :                         largestStringSize = 1023;
     938          51 :                 else if (largestStringSize > 16777215)       // string length very large, limit to 16MB for now
     939             :                         largestStringSize = 16777215;
     940          51 :                 str_val = (char *)GDKmalloc((largestStringSize +1) * sizeof(char));     // +1 for the eos char
     941          51 :                 if (!str_val) {
     942           0 :                         errmsg = "Failed to alloc memory for largest rescol string buffer.";
     943           0 :                         goto finish_fetch;
     944             :                 }
     945          51 :                 TRC_DEBUG(LOADER, "Allocated str_val buffer of size %zu\n", (largestStringSize +1) * sizeof(char));
     946             : 
     947          51 :                 if (hasBlobCols) {
     948           1 :                         if (largestBlobSize == 0)       // no valid blob/binary data size, assume 1048576 (1MB) as default
     949             :                                 largestBlobSize = 1048576;
     950           0 :                         if (largestBlobSize > 16777216) // blob length very large, limit to 16MB for now
     951             :                                 largestBlobSize = 16777216;
     952           1 :                         bin_data = (uint8_t *)GDKmalloc(largestBlobSize * sizeof(uint8_t));
     953           1 :                         if (!bin_data) {
     954           0 :                                 errmsg = "Failed to alloc memory for largest rescol binary data buffer.";
     955           0 :                                 goto finish_fetch;
     956             :                         }
     957           1 :                         TRC_DEBUG(LOADER, "Allocated bin_data buffer of size %zu\n", largestBlobSize * sizeof(uint8_t));
     958             :                 }
     959             : 
     960             :                 /* after allocation of var sized buffers, update targetValuePtr and bufferLength for those columns */
     961         243 :                 for (SQLUSMALLINT col = 0; col < (SQLUSMALLINT) nr_cols; col++) {
     962         192 :                         switch (colmetadata[col].targetType) {
     963          58 :                         case SQL_C_CHAR:
     964          58 :                                 colmetadata[col].targetValuePtr = (SQLPOINTER *) str_val;
     965          58 :                                 colmetadata[col].bufferLength = largestStringSize;
     966          58 :                                 break;
     967           3 :                         case SQL_C_BINARY:
     968           3 :                                 colmetadata[col].targetValuePtr = (SQLPOINTER *) bin_data;
     969           3 :                                 colmetadata[col].bufferLength = largestBlobSize;
     970           3 :                                 break;
     971             : //      TODO            case SQL_C_WCHAR:
     972             : //                              colmetadata[col].targetValuePtr = (SQLPOINTER *) Wstr_val;
     973             : //                              colmetadata[col].bufferLength = largestWStringSize;
     974             :                         }
     975             :                 }
     976             : 
     977          51 :                 gdk_return gdkret = GDK_SUCCEED;
     978          51 :                 unsigned long row = 0;
     979          51 :                 ret = SQLFetch(stmt);   // TODO optimisation: use SQLExtendedFetch() to pull data array wise and use BUNappendmulti()
     980       15747 :                 while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
     981       15696 :                         row++;
     982       15696 :                         TRC_DEBUG(LOADER, "Fetched row %lu\n", row);
     983             : 
     984       82395 :                         for (SQLUSMALLINT col = 0; col < (SQLUSMALLINT) nr_cols; col++) {
     985       66699 :                                 SQLSMALLINT sqltype = colmetadata[col].dataType;
     986       66699 :                                 BAT * b = colmetadata[col].bat;
     987       66699 :                                 SQLSMALLINT targetType = colmetadata[col].targetType;
     988       66699 :                                 SQLPOINTER * targetValuePtr = colmetadata[col].targetValuePtr;
     989       66699 :                                 SQLLEN bufferLength = colmetadata[col].bufferLength;
     990       66699 :                                 SQLLEN strLen = 0;
     991             : 
     992       66699 :                                 TRC_DEBUG(LOADER, "Before SQLGetData(col %u C_type %d buflen %d\n", col+1, targetType, (int)bufferLength);
     993       66699 :                                 ret = SQLGetData(stmt, col+1, targetType, targetValuePtr, bufferLength, &strLen);
     994       66699 :                                 if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
     995           0 :                                         char * ODBCmsg = getErrMsg(SQL_HANDLE_STMT, stmt);
     996           0 :                                         TRC_DEBUG(LOADER, "Failed to get C_type %d data for col %u of row %lu. ODBCmsg: %s\n",
     997             :                                                         targetType, col+1, row, (ODBCmsg) ? ODBCmsg : "");
     998           0 :                                         if (ODBCmsg)
     999           0 :                                                 GDKfree(ODBCmsg);
    1000             : 
    1001             :                                         /* as all bats need to be the same length, append NULL value */
    1002           0 :                                         if (BUNappend(b, ATOMnilptr(b->ttype), false) != GDK_SUCCEED)
    1003           0 :                                                 TRC_ERROR(LOADER, "BUNappend(b, ATOMnilptr(b->ttype), false) failed after SQLGetData failed\n");
    1004             :                                 } else {
    1005       66699 :                                         if (strLen == SQL_NULL_DATA) {
    1006        1378 :                                                 TRC_DEBUG(LOADER, "Data row %lu col %u: NULL\n", row, col+1);
    1007        1378 :                                                 if (BUNappend(b, ATOMnilptr(b->ttype), false) != GDK_SUCCEED)
    1008           0 :                                                         TRC_ERROR(LOADER, "BUNappend(b, ATOMnilptr(b->ttype), false) failed for setting SQL_NULL_DATA\n");
    1009             :                                         } else {
    1010       65321 :                                                 switch(sqltype) {
    1011        9246 :                                                         case SQL_CHAR:
    1012             :                                                         case SQL_VARCHAR:
    1013             :                                                         case SQL_LONGVARCHAR:
    1014             :                                                         case SQL_WCHAR:
    1015             :                                                         case SQL_WVARCHAR:
    1016             :                                                         case SQL_WLONGVARCHAR:
    1017             :                                                         default:
    1018        9246 :                                                                 if (strLen != SQL_NTS && strLen >= 0) {
    1019             :                                                                         /* make sure it is a Nul Terminated String */
    1020        9246 :                                                                         if ((SQLULEN) strLen < largestStringSize) {
    1021        9246 :                                                                                 if (str_val[strLen] != '\0')
    1022           0 :                                                                                         str_val[strLen] = '\0';
    1023             :                                                                         } else {
    1024           0 :                                                                                 if (str_val[largestStringSize] != '\0')
    1025           0 :                                                                                         str_val[largestStringSize] = '\0';
    1026             :                                                                         }
    1027             :                                                                 }
    1028        9246 :                                                                 TRC_DEBUG(LOADER, "Data row %lu col %u: %s\n", row, col+1, str_val);
    1029        9246 :                                                                 switch (colmetadata[col].battype) {
    1030        9243 :                                                                         case TYPE_str:
    1031        9243 :                                                                                 gdkret = BUNappend(b, (void *) str_val, false);
    1032        9243 :                                                                                 break;
    1033             : #ifdef HAVE_HGE
    1034           3 :                                                                         case TYPE_hge:
    1035             :                                                                                 /* HUGEINT values are read as string */
    1036           3 :                                                                                 hge_val = str_to_hge(str_val);
    1037           3 :                                                                                 gdkret = BUNappend(b, (void *) &hge_val, false);
    1038           3 :                                                                                 break;
    1039             : #endif
    1040           0 :                                                                         default:
    1041           0 :                                                                                 gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1042           0 :                                                                                 break;
    1043             :                                                                 }
    1044             :                                                                 break;
    1045           4 :                                                         case SQL_BIT:
    1046           4 :                                                                 if (colmetadata[col].battype == TYPE_bit) {
    1047           4 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %x\n", row, col+1, bit_val);
    1048           4 :                                                                         gdkret = BUNappend(b, (void *) &bit_val, false);
    1049             :                                                                 } else {
    1050           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1051             :                                                                 }
    1052             :                                                                 break;
    1053           3 :                                                         case SQL_TINYINT:
    1054           3 :                                                                 if (colmetadata[col].battype == TYPE_bte) {
    1055           3 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %hd\n", row, col+1, (sht) bte_val);
    1056           3 :                                                                         gdkret = BUNappend(b, (void *) &bte_val, false);
    1057             :                                                                 } else {
    1058           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1059             :                                                                 }
    1060             :                                                                 break;
    1061           3 :                                                         case SQL_SMALLINT:
    1062           3 :                                                                 if (colmetadata[col].battype == TYPE_sht) {
    1063           3 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %hd\n", row, col+1, sht_val);
    1064           3 :                                                                         gdkret = BUNappend(b, (void *) &sht_val, false);
    1065             :                                                                 } else {
    1066           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1067             :                                                                 }
    1068             :                                                                 break;
    1069       49404 :                                                         case SQL_INTEGER:
    1070       49404 :                                                                 if (colmetadata[col].battype == TYPE_int) {
    1071       49404 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %d\n", row, col+1, int_val);
    1072       49404 :                                                                         gdkret = BUNappend(b, (void *) &int_val, false);
    1073             :                                                                 } else {
    1074           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1075             :                                                                 }
    1076             :                                                                 break;
    1077           3 :                                                         case SQL_BIGINT:
    1078           3 :                                                                 if (colmetadata[col].battype == TYPE_lng) {
    1079           3 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %" PRId64 "\n", row, col+1, lng_val);
    1080           3 :                                                                         gdkret = BUNappend(b, (void *) &lng_val, false);
    1081             :                                                                 } else {
    1082           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1083             :                                                                 }
    1084             :                                                                 break;
    1085           9 :                                                         case SQL_DECIMAL:
    1086             :                                                         case SQL_NUMERIC:
    1087           9 :                                                                 if (colmetadata[col].battype == TYPE_str) {
    1088           9 :                                                                         if (strLen != SQL_NTS && strLen >= 0) {
    1089             :                                                                                 /* make sure it is a Nul Terminated String */
    1090           9 :                                                                                 if ((SQLULEN) strLen < largestStringSize) {
    1091           9 :                                                                                         if (str_val[strLen] != '\0')
    1092           0 :                                                                                                 str_val[strLen] = '\0';
    1093             :                                                                                 } else {
    1094           0 :                                                                                         if (str_val[largestStringSize] != '\0')
    1095           0 :                                                                                                 str_val[largestStringSize] = '\0';
    1096             :                                                                                 }
    1097             :                                                                         }
    1098           9 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %s\n", row, col+1, str_val);
    1099           9 :                                                                         gdkret = BUNappend(b, (void *) str_val, false);
    1100             :                                                                 } else {
    1101           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1102             :                                                                 }
    1103             :                                                                 // TODO add support for battypes: bte, sht, int, lng, hge
    1104             :                                                                 // this requires conversion of string to the target bat type, with the scale (decimalDigits) semantics.
    1105             :                                                                 break;
    1106           3 :                                                         case SQL_REAL:
    1107           3 :                                                                 if (colmetadata[col].battype == TYPE_flt) {
    1108           3 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %f\n", row, col+1, flt_val);
    1109           3 :                                                                         gdkret = BUNappend(b, (void *) &flt_val, false);
    1110             :                                                                 } else {
    1111           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1112             :                                                                 }
    1113             :                                                                 break;
    1114        6169 :                                                         case SQL_DOUBLE:
    1115        6169 :                                                                 if (colmetadata[col].battype == TYPE_dbl) {
    1116        6169 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %f\n", row, col+1, dbl_val);
    1117        6169 :                                                                         gdkret = BUNappend(b, (void *) &dbl_val, false);
    1118             :                                                                 } else {
    1119           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1120             :                                                                 }
    1121             :                                                                 break;
    1122           0 :                                                         case SQL_FLOAT:
    1123           0 :                                                                 if (colmetadata[col].battype == TYPE_flt) {
    1124           0 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %f\n", row, col+1, flt_val);
    1125           0 :                                                                         gdkret = BUNappend(b, (void *) &flt_val, false);
    1126             :                                                                 } else {
    1127           0 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %f\n", row, col+1, dbl_val);
    1128           0 :                                                                         gdkret = BUNappend(b, (void *) &dbl_val, false);
    1129             :                                                                 }
    1130             :                                                                 break;
    1131           3 :                                                         case SQL_TYPE_DATE:
    1132           3 :                                                                 if (colmetadata[col].battype == TYPE_date) {
    1133           3 :                                                                         date mdate_val = date_create(date_val.year, date_val.month, date_val.day);
    1134           3 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: date(%04d-%02u-%02u)\n",
    1135             :                                                                                 row, col+1, date_val.year, date_val.month, date_val.day);
    1136           3 :                                                                         gdkret = BUNappend(b, (void *) &mdate_val, false);
    1137             :                                                                 } else {
    1138           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1139             :                                                                 }
    1140             :                                                                 break;
    1141           3 :                                                         case SQL_TYPE_TIME:
    1142           3 :                                                                 if (colmetadata[col].battype == TYPE_daytime) {
    1143           3 :                                                                         daytime daytime_val = daytime_create(time_val.hour, time_val.minute, time_val.second, 0);
    1144           3 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: daytime(%02u:%02u:%02u)\n",
    1145             :                                                                                 row, col+1, time_val.hour, time_val.minute, time_val.second);
    1146           3 :                                                                         gdkret = BUNappend(b, (void *) &daytime_val, false);
    1147             :                                                                 } else {
    1148           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1149             :                                                                 }
    1150             :                                                                 break;
    1151         426 :                                                         case SQL_DATETIME:
    1152             :                                                         case SQL_TYPE_TIMESTAMP:
    1153         426 :                                                                 if (colmetadata[col].battype == TYPE_timestamp) {
    1154         426 :                                                                         date mdate_val = date_create(ts_val.year, ts_val.month, ts_val.day);
    1155         426 :                                                                         daytime daytime_val = daytime_create(ts_val.hour, ts_val.minute, ts_val.second, ts_val.fraction);
    1156         426 :                                                                         timestamp timestamp_val = timestamp_create(mdate_val, daytime_val);
    1157         426 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: timestamp(%04d-%02u-%02u %02u:%02u:%02u.%06u)\n", row, col+1,
    1158             :                                                                                         ts_val.year, ts_val.month, ts_val.day, ts_val.hour, ts_val.minute, ts_val.second, ts_val.fraction);
    1159         426 :                                                                         gdkret = BUNappend(b, (void *) &timestamp_val, false);
    1160             :                                                                 } else {
    1161           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1162             :                                                                 }
    1163             :                                                                 break;
    1164           9 :                                                         case SQL_INTERVAL_YEAR:
    1165             :                                                         case SQL_INTERVAL_YEAR_TO_MONTH:
    1166             :                                                         case SQL_INTERVAL_MONTH:
    1167           9 :                                                                 if (colmetadata[col].battype == TYPE_int) {
    1168           9 :                                                                         switch (itv_val.interval_type) {
    1169           3 :                                                                         case SQL_IS_YEAR:
    1170           3 :                                                                                 int_val = (int) itv_val.intval.year_month.year *12;
    1171           3 :                                                                                 break;
    1172           3 :                                                                         case SQL_IS_YEAR_TO_MONTH:
    1173           3 :                                                                                 int_val = (int) (itv_val.intval.year_month.year *12)
    1174           3 :                                                                                         + itv_val.intval.year_month.month;
    1175           3 :                                                                                 break;
    1176           3 :                                                                         case SQL_IS_MONTH:
    1177           3 :                                                                                 int_val = (int) itv_val.intval.year_month.month;
    1178           3 :                                                                                 break;
    1179           0 :                                                                         default:
    1180           0 :                                                                                 int_val = 0;
    1181             :                                                                         }
    1182             : 
    1183           9 :                                                                         if (itv_val.interval_sign == SQL_TRUE)
    1184           3 :                                                                                 int_val = -int_val;
    1185           9 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %d\n", row, col+1, int_val);
    1186           9 :                                                                         gdkret = BUNappend(b, (void *) &int_val, false);
    1187             :                                                                 } else {
    1188           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1189             :                                                                 }
    1190             :                                                                 break;
    1191          32 :                                                         case SQL_INTERVAL_DAY:
    1192             :                                                         case SQL_INTERVAL_HOUR:
    1193             :                                                         case SQL_INTERVAL_MINUTE:
    1194             :                                                         case SQL_INTERVAL_SECOND:
    1195             :                                                         case SQL_INTERVAL_DAY_TO_HOUR:
    1196             :                                                         case SQL_INTERVAL_DAY_TO_MINUTE:
    1197             :                                                         case SQL_INTERVAL_DAY_TO_SECOND:
    1198             :                                                         case SQL_INTERVAL_HOUR_TO_MINUTE:
    1199             :                                                         case SQL_INTERVAL_HOUR_TO_SECOND:
    1200             :                                                         case SQL_INTERVAL_MINUTE_TO_SECOND:
    1201          32 :                                                                 if (colmetadata[col].battype == TYPE_lng) {
    1202          32 :                                                                         switch (itv_val.interval_type) {
    1203           5 :                                                                         case SQL_IS_DAY:
    1204           5 :                                                                                 lng_val = (lng) itv_val.intval.day_second.day * (24*60*60*1000);
    1205           5 :                                                                                 break;
    1206           3 :                                                                         case SQL_IS_HOUR:
    1207           3 :                                                                                 lng_val = (lng) itv_val.intval.day_second.hour * (60*60*1000);
    1208           3 :                                                                                 break;
    1209           3 :                                                                         case SQL_IS_MINUTE:
    1210           3 :                                                                                 lng_val = (lng) itv_val.intval.day_second.minute * (60*1000);
    1211           3 :                                                                                 break;
    1212           3 :                                                                         case SQL_IS_SECOND:
    1213           6 :                                                                                 lng_val = (lng) (itv_val.intval.day_second.second * 1000)
    1214           3 :                                                                                         + fraction2msec(itv_val.intval.day_second.fraction, colmetadata[col].decimalDigits);
    1215           3 :                                                                                 break;
    1216           3 :                                                                         case SQL_IS_DAY_TO_HOUR:
    1217           3 :                                                                                 lng_val = (lng) ((itv_val.intval.day_second.day *24)
    1218           3 :                                                                                         + itv_val.intval.day_second.hour) * (60*60*1000);
    1219           3 :                                                                                 break;
    1220           3 :                                                                         case SQL_IS_DAY_TO_MINUTE:
    1221           3 :                                                                                 lng_val = (lng) ((((itv_val.intval.day_second.day *24)
    1222           3 :                                                                                         + itv_val.intval.day_second.hour) *60)
    1223           3 :                                                                                         + itv_val.intval.day_second.minute) * (60*1000);
    1224           3 :                                                                                 break;
    1225           3 :                                                                         case SQL_IS_DAY_TO_SECOND:
    1226           6 :                                                                                 lng_val = (lng) (((((((itv_val.intval.day_second.day *24)
    1227           3 :                                                                                         + itv_val.intval.day_second.hour) *60)
    1228           3 :                                                                                         + itv_val.intval.day_second.minute) *60)
    1229           3 :                                                                                         + itv_val.intval.day_second.second) *1000)
    1230           3 :                                                                                         + fraction2msec(itv_val.intval.day_second.fraction, colmetadata[col].decimalDigits);
    1231           3 :                                                                                 break;
    1232           3 :                                                                         case SQL_IS_HOUR_TO_MINUTE:
    1233           3 :                                                                                 lng_val = (lng) ((itv_val.intval.day_second.hour *60)
    1234           3 :                                                                                         + itv_val.intval.day_second.minute) * (60*1000);
    1235           3 :                                                                                 break;
    1236           3 :                                                                         case SQL_IS_HOUR_TO_SECOND:
    1237           6 :                                                                                 lng_val = (lng) (((((itv_val.intval.day_second.hour *60)
    1238           3 :                                                                                         + itv_val.intval.day_second.minute) *60)
    1239           3 :                                                                                         + itv_val.intval.day_second.second) *1000)
    1240           3 :                                                                                         + fraction2msec(itv_val.intval.day_second.fraction, colmetadata[col].decimalDigits);
    1241           3 :                                                                                 break;
    1242           3 :                                                                         case SQL_IS_MINUTE_TO_SECOND:
    1243           6 :                                                                                 lng_val = (lng) (((itv_val.intval.day_second.minute *60)
    1244           3 :                                                                                         + itv_val.intval.day_second.second) *1000)
    1245           3 :                                                                                         + fraction2msec(itv_val.intval.day_second.fraction, colmetadata[col].decimalDigits);
    1246           3 :                                                                                 break;
    1247           0 :                                                                         default:
    1248           0 :                                                                                 lng_val = 0;
    1249           0 :                                                                                 break;
    1250             :                                                                         }
    1251             : 
    1252          32 :                                                                         if (itv_val.interval_sign == SQL_TRUE)
    1253          11 :                                                                                 lng_val = -lng_val;
    1254          32 :                                                                         TRC_DEBUG(LOADER, "Data row %lu col %u: %" PRId64 "\n", row, col+1, lng_val);
    1255          32 :                                                                         gdkret = BUNappend(b, (void *) &lng_val, false);
    1256             :                                                                 } else {
    1257           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1258             :                                                                 }
    1259             :                                                                 break;
    1260           2 :                                                         case SQL_GUID:
    1261           2 :                                                                 TRC_DEBUG(LOADER, "Data row %lu col %u: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", row, col+1,
    1262             :                                                                                 guid_val.Data1, guid_val.Data2, guid_val.Data3, guid_val.Data4[0], guid_val.Data4[1], guid_val.Data4[2],
    1263             :                                                                                 guid_val.Data4[3], guid_val.Data4[4], guid_val.Data4[5], guid_val.Data4[6], guid_val.Data4[7]);
    1264           2 :                                                                 if (colmetadata[col].battype == TYPE_uuid) {
    1265           2 :                                                                         u_val.u[0] = (guid_val.Data1 >> 24) & 0xFF;
    1266           2 :                                                                         u_val.u[1] = (guid_val.Data1 >> 16) & 0xFF;
    1267           2 :                                                                         u_val.u[2] = (guid_val.Data1 >> 8) & 0xFF;
    1268           2 :                                                                         u_val.u[3] = guid_val.Data1 & 0xFF;
    1269           2 :                                                                         u_val.u[4] = (guid_val.Data2 >> 8) & 0xFF;
    1270           2 :                                                                         u_val.u[5] = guid_val.Data2 & 0xFF;
    1271           2 :                                                                         u_val.u[6] = (guid_val.Data3 >> 8) & 0xFF;
    1272           2 :                                                                         u_val.u[7] = guid_val.Data3 & 0xFF;
    1273           2 :                                                                         memcpy(&u_val.u[8], &guid_val.Data4[0], 8);
    1274           2 :                                                                         gdkret = BUNappend(b, (void *) &u_val.uuid_val, false);
    1275             :                                                                 } else {
    1276           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1277             :                                                                 }
    1278             :                                                                 break;
    1279           2 :                                                         case SQL_BINARY:
    1280             :                                                         case SQL_VARBINARY:
    1281             :                                                         case SQL_LONGVARBINARY:
    1282           2 :                                                                 TRC_DEBUG(LOADER, "Data row %lu col %u: binary data[%d]\n", row, col+1, (int) strLen);
    1283           2 :                                                                 if (colmetadata[col].battype == TYPE_blob && strLen > 0) {
    1284             :                                                                         // convert bin_data to blob struct.
    1285           2 :                                                                         size_t bin_size = (size_t)strLen;
    1286           2 :                                                                         if (bin_size > (size_t)largestBlobSize)
    1287             :                                                                                 /* the data has been truncated */
    1288             :                                                                                 bin_size = (size_t)largestBlobSize;
    1289           2 :                                                                         blob * blb = (blob *) GDKmalloc(blobsize(bin_size));
    1290           2 :                                                                         if (blb) {
    1291           2 :                                                                                 blb->nitems = bin_size;
    1292           2 :                                                                                 memcpy(blb->data, bin_data, bin_size);
    1293           2 :                                                                                 gdkret = BUNappend(b, (void *) blb, false);
    1294           2 :                                                                                 GDKfree(blb);
    1295             :                                                                         } else {
    1296           0 :                                                                                 gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1297             :                                                                         }
    1298             :                                                                 } else {
    1299           0 :                                                                         gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
    1300             :                                                                 }
    1301             :                                                                 break;
    1302             :                                                 }
    1303       65321 :                                                 if (gdkret != GDK_SUCCEED)
    1304           0 :                                                         TRC_ERROR(LOADER, "BUNappend(b, val, false) failed!\n");
    1305             :                                         }
    1306             :                                 }
    1307             :                         }
    1308       15696 :                         ret = SQLFetch(stmt);   // get data of next row
    1309             :                 }
    1310             :                 /* the last SQLFetch() will return SQL_NO_DATA at end, treat it as success */
    1311          51 :                 if (ret == SQL_NO_DATA)
    1312          51 :                         ret = SQL_SUCCESS;      // we retrieved all rows
    1313          51 :                 TRC_INFO(LOADER, "Fetched %lu rows\n", row);
    1314             : 
    1315          51 :   finish_fetch:
    1316          51 :                 if (str_val)
    1317          51 :                         GDKfree(str_val);
    1318          51 :                 if (bin_data)
    1319           1 :                         GDKfree(bin_data);
    1320             : 
    1321             :                 /* pass bats to caller */
    1322             :                 if (colmetadata) {
    1323         243 :                         for (int col = 0; col < (int) nr_cols; col++) {
    1324         192 :                                 bat * rescol = getArgReference_bat(stk, pci, col);
    1325         192 :                                 BAT * b = colmetadata[col].bat;
    1326         192 :                                 if (rescol && b) {
    1327         192 :                                         *rescol = b->batCacheid;
    1328         192 :                                         BBPkeepref(b);
    1329             :                                 }
    1330         192 :                                 TRC_DEBUG(LOADER, "col %d pass bat %d\n", col, b->ttype);
    1331             :                         }
    1332             :                         /* free locally allocated memory */
    1333          51 :                         GDKfree(colmetadata);
    1334             :                 }
    1335             :         } /* end of: if (caller == ODBC_LOADER) */
    1336             : 
    1337           0 :   finish:
    1338         107 :         if (query)
    1339           5 :                 GDKfree(query);
    1340         107 :         if (odbc_con_str)
    1341           4 :                 GDKfree(odbc_con_str);
    1342             : 
    1343         107 :         TRC_DEBUG(LOADER, "caller %d at finish, ret %d (%s) errmsg %s\n", caller, ret, nameOfRetCode(ret), (errmsg) ? errmsg : "");
    1344             : 
    1345         107 :         if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
    1346             :                 /* an ODBC function call returned an error or warning, get the error msg from the ODBC driver */
    1347           5 :                 SQLSMALLINT handleType;
    1348           5 :                 SQLHANDLE handle;
    1349           5 :                 str retmsg;
    1350           5 :                 char * ODBCmsg;
    1351             : 
    1352             :                 /* get err message(s) from the right handle */
    1353           5 :                 if (stmt != SQL_NULL_HSTMT) {
    1354             :                         handleType = SQL_HANDLE_STMT;
    1355             :                         handle = stmt;
    1356             :                 } else
    1357           4 :                 if (dbc != SQL_NULL_HDBC) {
    1358             :                         handleType = SQL_HANDLE_DBC;
    1359             :                         handle = dbc;
    1360             :                 } else {
    1361           0 :                         handleType = SQL_HANDLE_ENV;
    1362           0 :                         handle = env;
    1363             :                 }
    1364           5 :                 ODBCmsg = getErrMsg(handleType, handle);
    1365           5 :                 if (errmsg != NULL) {
    1366           5 :                         retmsg = sa_message(sql->sa, "odbc_loader" " %s %s", errmsg, (ODBCmsg) ? ODBCmsg : "");
    1367             :                 } else {
    1368           0 :                         retmsg = sa_message(sql->sa, "odbc_loader" " %s", (ODBCmsg) ? ODBCmsg : "");
    1369             :                 }
    1370           5 :                 if (ODBCmsg)
    1371           5 :                         GDKfree(ODBCmsg);
    1372           5 :                 odbc_cleanup(env, dbc, stmt);
    1373           5 :                 return retmsg;
    1374             :         }
    1375             : 
    1376         102 :         odbc_cleanup(env, dbc, stmt);
    1377         102 :         TRC_DEBUG(LOADER, "after odbc_cleanup(%p, %p, %p) errmsg %s\n", env, dbc, stmt, (errmsg) ? errmsg : "");
    1378             :         return (errmsg != NULL) ? (str)errmsg : MAL_SUCCEED;
    1379             : }
    1380             : 
    1381             : /*
    1382             :  * returns an error string (static or via tmp sa_allocator allocated), NULL on success
    1383             :  *
    1384             :  * Extend the subfunc f with result columns, ie.
    1385             :         f->res = typelist;
    1386             :         f->coltypes = typelist;
    1387             :         f->colnames = nameslist; use tname if passed, for the relation name
    1388             :  * Fill the list res_exps, with one result expressions per resulting column.
    1389             :  */
    1390             : static str
    1391          67 : odbc_relation(mvc *sql, sql_subfunc *f, char *url, list *res_exps, char *aname)
    1392             : {
    1393          67 :         (void) aname;
    1394          67 :         return odbc_query(ODBC_RELATION, sql, f, url, res_exps, NULL, NULL, NULL);
    1395             : }
    1396             : 
    1397             : static void *
    1398          51 : odbc_load(void *BE, sql_subfunc *f, char *url, sql_exp *topn)
    1399             : {
    1400          51 :         backend *be = (backend*)BE;
    1401          51 :         if (!f)
    1402             :                 return NULL;
    1403             : 
    1404          51 :         (void)topn;
    1405             : 
    1406          51 :         InstrPtr q = newStmtArgs(be->mb, "odbc", "loader", list_length(f->coltypes) + 2);
    1407          51 :         int col = 0;
    1408          51 :         list *l = sa_list(be->mvc->sa);
    1409         243 :         for (node *n = f->coltypes->h, *nn = f->colnames->h; n && nn; col++, n = n->next, nn = nn->next) {
    1410         192 :                 const char *name = nn->data;
    1411         192 :                 sql_subtype *tp = n->data;
    1412         192 :                 if (tp) {
    1413         192 :                         int type = newBatType(tp->type->localtype);
    1414         192 :                         if (col)
    1415         141 :                                 q = pushReturn(be->mb, q, newTmpVariable(be->mb, type));
    1416             :                         else
    1417          51 :                                 getArg(q, 0) = newTmpVariable(be->mb, type);
    1418         192 :                         stmt *s = stmt_blackbox_result(be, q, col, tp);
    1419         192 :                         s = stmt_alias(be, s, col+1, f->tname, name);
    1420         192 :                         list_append(l, s);
    1421             :                 }
    1422             :         }
    1423          51 :         q = pushStr(be->mb, q, url);
    1424          51 :         q = pushPtr(be->mb, q, f);
    1425          51 :         pushInstruction(be->mb, q);
    1426          51 :         return stmt_list(be, l);
    1427             : }
    1428             : 
    1429             : static str
    1430          51 : ODBCloader(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1431             : {
    1432          51 :         backend *be = NULL;
    1433          51 :         str msg;
    1434          51 :         if ((msg = getBackendContext(cntxt, &be)) != NULL)
    1435             :                 return msg;
    1436          51 :         str uri = *getArgReference_str(stk, pci, pci->retc);
    1437          51 :         sql_subfunc *f = *(sql_subfunc**)getArgReference_ptr(stk, pci, pci->retc+1);
    1438             : 
    1439          51 :         return odbc_query(ODBC_LOADER, be->mvc, f, uri, NULL, mb, stk, pci);
    1440             :         //return MAL_SUCCEED;
    1441             : }
    1442             : 
    1443             : static str
    1444           1 : ODBCprelude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1445             : {
    1446           1 :         (void)cntxt; (void)mb; (void)stk; (void)pci;
    1447           1 :         pl_register("odbc", &odbc_relation, &odbc_load);
    1448           1 :         return MAL_SUCCEED;
    1449             : }
    1450             : 
    1451             : static str
    1452           1 : ODBCepilogue(void *ret)
    1453             : {
    1454           1 :         (void)ret;
    1455           1 :         pl_unregister("odbc");
    1456           1 :         return MAL_SUCCEED;
    1457             : }
    1458             : 
    1459             : #include "sql_scenario.h"
    1460             : #include "mel.h"
    1461             : 
    1462             : static mel_func odbc_init_funcs[] = {
    1463             :         pattern("odbc", "prelude", ODBCprelude, false, "", noargs),
    1464             :         command("odbc", "epilogue", ODBCepilogue, false, "", noargs),
    1465             :         pattern("odbc", "loader", ODBCloader, true, "Import a query result via the odbc uri", args(1,3, batvarargany("",0),arg("uri",str),arg("func",ptr))),
    1466             : { .imp=NULL }
    1467             : };
    1468             : 
    1469             : #include "mal_import.h"
    1470             : #ifdef _MSC_VER
    1471             : #undef read
    1472             : #pragma section(".CRT$XCU",read)
    1473             : #endif
    1474           1 : LIB_STARTUP_FUNC(init_odbc_mal)
    1475           1 : { mal_module("odbc", NULL, odbc_init_funcs); }

Generated by: LCOV version 1.14