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

Add support for reading HUGEINT values when connected via the ODBC driver to a 
MonetDB server. This required that we call SQLGetTypeInfo(stmt, SQL_HUGEINT) 
first.
Corrected issue with retrieving decimals due to small string buffer. Also the 
function decimal_from_str() is not available on windows, so disabled it.


diffs (truncated from 334 to 300 lines):

diff --git a/sql/backends/monet5/vaults/odbc/odbc_loader.c 
b/sql/backends/monet5/vaults/odbc/odbc_loader.c
--- a/sql/backends/monet5/vaults/odbc/odbc_loader.c
+++ b/sql/backends/monet5/vaults/odbc/odbc_loader.c
@@ -19,7 +19,7 @@
 #include "mal_builder.h"
 #include "mal_client.h"
 // #include "mutils.h" /* utf8towchar(), wchartoutf8() */
-#include "sql_decimal.h"       /* decimal_from_str() */
+// #include "sql_decimal.h"    /* decimal_from_str() */
 
 #ifdef _MSC_VER
 #include <WTypes.h>
@@ -47,6 +47,9 @@
 #define MAX_PREC  18
 #endif
 
+/* MonetDB ODBC Driver defines in ODBCGlobal.h  SQL_HUGEINT 0x4000 */
+#define SQL_HUGEINT    0x4000
+
 typedef struct {
        SQLSMALLINT dataType;           /* ODBC datatype */
        SQLULEN columnSize;             /* ODBC colsize, contains precision for 
decimals */
@@ -91,14 +94,15 @@ map_rescol_type(SQLSMALLINT dataType, SQ
                        /* too large precision/scale, not supported by MonetDB. 
Map this column to a string */
                        if (columnSize > (SQLULEN) INT_MAX)
                                columnSize = INT_MAX;
-                       return sql_bind_subtype(sql->sa, "varchar", (unsigned 
int) columnSize, 0);
+                       return sql_bind_subtype(sql->sa, "varchar", (unsigned 
int) columnSize +3, 0);
                }
 
-               unsigned int prec = MIN(1, columnSize); /* precision must be >= 
1 */
-               unsigned int scale = MIN(0, decimalDigits); /* negative scales 
are not supported by MonetDB */
-               if (prec < scale)
-                       prec = scale;   /* make precision large enough to 
contain all decimal digits */
-               return sql_bind_subtype(sql->sa, "decimal", prec, scale);
+               return sql_bind_subtype(sql->sa, "varchar", (unsigned int) 
columnSize +3, 0);
+//             unsigned int prec = MAX(1, columnSize); /* precision must be >= 
1 */
+//             unsigned int scale = MAX(0, decimalDigits); /* negative scales 
are not supported by MonetDB */
+//             if (prec < scale)
+//                     prec = scale;   /* make precision large enough to 
contain all decimal digits */
+//             return sql_bind_subtype(sql->sa, "decimal", prec, scale);
        }
 
        case SQL_BIT:
@@ -117,6 +121,11 @@ map_rescol_type(SQLSMALLINT dataType, SQ
        case SQL_BIGINT:
                typenm = "bigint";
                break;
+#ifdef HAVE_HGE
+       case SQL_HUGEINT:
+               typenm = "hugeint";
+               break;
+#endif
 
        case SQL_REAL:
                typenm = "real";
@@ -248,11 +257,40 @@ nameofSQLtype(SQLSMALLINT dataType)
        case SQL_INTERVAL_HOUR_TO_SECOND:       return "INTERVAL HOUR TO 
SECOND";
        case SQL_INTERVAL_MINUTE_TO_SECOND:     return "INTERVAL MINUTE TO 
SECOND";
        case SQL_GUID:          return "GUID";
-/*     case SQL_HUGEINT:       return "HUGEINT";       0x4000 (defined in 
ODBCGlobal.h) */
+       case SQL_HUGEINT:       return "HUGEINT";
        default:                return "Driver specific type";
        }
 }
 
+#ifdef HAVE_HGE
+static hge
+str_to_hge(const char *s) {
+       char c;
+       char sign = '+';
+       int i = 0;
+       hge ret = 0;
+
+       if (!s)
+               return 0;
+
+       c = s[i];
+       if (c == '-' || c == '+') {
+               sign = c;
+               c = s[++i];
+       }
+       while (c) {
+               if (c >= '0' && c <= '9') {
+                       ret *= 10;
+                       ret += (int) c - '0';
+               }
+               c = s[++i];
+       }
+       if (sign == '-')
+               ret = -ret;
+       return ret;
+}
+#endif
+
 /* utility function to safely close all opened ODBC resources */
 static void
 odbc_cleanup(SQLHANDLE env, SQLHANDLE dbc, SQLHANDLE stmt) {
@@ -400,6 +438,26 @@ odbc_query(int caller, mvc *sql, sql_sub
                goto finish;
        }
 
+#ifdef HAVE_HGE
+       {
+               char name[1024];
+               ret = SQLGetInfo(dbc, SQL_DBMS_NAME, (SQLPOINTER) &name, 1023, 
NULL);
+               if (trace_enabled)
+                       printf("After SQLGetInfo(dbc, SQL_DBMS_NAME) returned 
%d\n", ret);
+               if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
+                       if (strcmp("MonetDB", name) == 0) {
+                               /* let the MonetDB driver enable returning 
HUGEINT as column datatype */
+                               ret = SQLGetTypeInfo(stmt, SQL_HUGEINT);
+                               if (trace_enabled)
+                                       printf("After SQLGetTypeInfo(stmt, 
SQL_HUGEINT) returned %d\n", ret);
+                               if (ret == SQL_SUCCESS || ret == 
SQL_SUCCESS_WITH_INFO) {
+                                       ret = SQLCloseCursor(stmt);
+                               }
+                       }
+               }
+       }
+#endif
+
 //     wchar_t * query_Wstr = utf8towchar(query);
 //     if (query_Wstr != NULL) {
 //             ret = SQLExecDirectW(stmt, (SQLWCHAR *) query_Wstr, SQL_NTS);
@@ -520,21 +578,30 @@ odbc_query(int caller, mvc *sql, sql_sub
                                GDKfree(colmetadata);
                                goto finish;
                        }
+                       if (trace_enabled)
+                               printf("DescCol %u, name: %s, type %d (%s), 
size %u, decdigits %d\n",
+                                       col+1, cname, dataType, 
nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits);
                        /* use same logic as used in map_rescol_type() for 
SQL_FLOAT and SQL_DECIMAL */
                        if (dataType == SQL_FLOAT) {
                                dataType = (columnSize == 7) ? SQL_REAL : 
SQL_DOUBLE;
                        } else
+                       if (dataType == SQL_HUGEINT) {
+                               dataType = SQL_VARCHAR; /* read it as string */
+                               columnSize = 50;
+                       } else
                        if (dataType == SQL_DECIMAL || dataType == SQL_NUMERIC) 
{
                                /* MonetDB has limits for the precision and 
scale */
                                if (columnSize > MAX_PREC || abs(decimalDigits) 
> MAX_PREC) {
                                        /* very large precision/scale, not 
supported by MonetDB. Map this column to a string */
                                        dataType = SQL_VARCHAR;
-                               } else {
-                                       columnSize = MIN(1, columnSize); /* 
precision must be >= 1 */
-                                       decimalDigits = MIN(0, decimalDigits); 
/* negative scales are not supported by MonetDB */
-                                       if ((int)columnSize < decimalDigits)
-                                               columnSize = decimalDigits;     
/* make precision large enough to contain all decimal digits */
+//                                     columnSize += 3;        /* add 3 for 
sign, leading 0 and decimal separator */
                                }
+//                             columnSize = MAX(1, columnSize); /* precision 
must be >= 1 */
+//                             decimalDigits = MAX(0, decimalDigits); /* 
negative scales are not supported by MonetDB */
+//                             if ((int)columnSize < decimalDigits)
+//                                     columnSize = decimalDigits;     /* make 
precision large enough to contain all decimal digits */
+                               dataType = SQL_VARCHAR; /* read it as string */
+                               columnSize += 3;        /* add 3 for sign, 
leading 0 and decimal separator */
                        }
                        colmetadata[col].dataType = dataType;
                        colmetadata[col].columnSize = columnSize;
@@ -545,7 +612,7 @@ odbc_query(int caller, mvc *sql, sql_sub
                                if (columnSize > largestStringSize) {
                                        largestStringSize = columnSize;
                                }
-                       }
+                       } else
                        if (battype == TYPE_blob) {
                                hasBlobCols = true;
                                if (columnSize > largestBlobSize) {
@@ -576,16 +643,16 @@ odbc_query(int caller, mvc *sql, sql_sub
                }
 
                /* allocate storage for all the fixed size atom types. */
-               bit bit_val;
-               bte bte_val;
-               sht sht_val;
-               int int_val;
-               lng lng_val;
+               bit bit_val = 0;
+               bte bte_val = 0;
+               sht sht_val = 0;
+               int int_val = 0;
+               lng lng_val = 0;
 #ifdef HAVE_HGE
-               hge hge_val;    // for decimals with precision > 18
+               hge hge_val = 0;        // for hugeint and decimals with 
precision > 18
 #endif
-               flt flt_val;
-               dbl dbl_val;
+               flt flt_val = 0;
+               dbl dbl_val = 0;
                DATE_STRUCT date_val;
                TIME_STRUCT time_val;
                TIMESTAMP_STRUCT ts_val;
@@ -668,6 +735,12 @@ odbc_query(int caller, mvc *sql, sql_sub
                                                targetType = SQL_C_SBIGINT;
                                                targetValuePtr = (SQLPOINTER *) 
&lng_val;
                                                break;
+                                       case SQL_HUGEINT:
+                                               /* read huge int data as string 
data as there is no SQL_C_SHUGEINT */
+                                               targetType = SQL_C_CHAR;
+                                               targetValuePtr = (SQLPOINTER *) 
str_val;
+                                               bufferLength = 
largestStringSize;
+                                               break;
                                        case SQL_DECIMAL:
                                        case SQL_NUMERIC:
                                                /* we read decimal data always 
as string data and convert it to the right internal decimal format and bat type 
*/
@@ -772,16 +845,32 @@ odbc_query(int caller, mvc *sql, sql_sub
                                                                }
                                                                if 
(trace_enabled)
                                                                        
printf("Data row %lu col %u: %s\n", row, col+1, str_val);
-                                                               gdkret = 
BUNappend(b, (void *) str_val, false);
+                                                               switch 
(colmetadata[col].battype) {
+                                                                       case 
TYPE_str:
+                                                                               
gdkret = BUNappend(b, (void *) str_val, false);
+                                                                               
break;
+#ifdef HAVE_HGE
+                                                                       case 
TYPE_hge:
+                                                                               
hge_val = str_to_hge(str_val);
+                                                                               
gdkret = BUNappend(b, (void *) &hge_val, false);
+                                                                               
break;
+#endif
+                                                                       default:
+                                                                               
if (BUNappend(b, ATOMnilptr(b->ttype), false) != GDK_SUCCEED) {
+                                                                               
        if (trace_enabled)
+                                                                               
                printf("BUNappend(b, ATOMnilptr(b->ttype), false) failed\n");
+                                                                               
}
+                                                                               
break;
+                                                               }
                                                                break;
                                                        case SQL_BIT:
                                                                if 
(trace_enabled)
-                                                                       
printf("Data row %lu col %u: %c\n", row, col+1, bit_val);
+                                                                       
printf("Data row %lu col %u: %x\n", row, col+1, bit_val);
                                                                gdkret = 
BUNappend(b, (void *) &bit_val, false);
                                                                break;
                                                        case SQL_TINYINT:
                                                                if 
(trace_enabled)
-                                                                       
printf("Data row %lu col %u: %c\n", row, col+1, bte_val);
+                                                                       
printf("Data row %lu col %u: %hd\n", row, col+1, (sht) bte_val);
                                                                gdkret = 
BUNappend(b, (void *) &bte_val, false);
                                                                break;
                                                        case SQL_SMALLINT:
@@ -799,6 +888,13 @@ odbc_query(int caller, mvc *sql, sql_sub
                                                                        
printf("Data row %lu col %u: %" PRId64 "\n", row, col+1, lng_val);
                                                                gdkret = 
BUNappend(b, (void *) &lng_val, false);
                                                                break;
+#ifdef HAVE_HGE
+//                                                     case SQL_HUGEINT:
+//                                                             if 
(trace_enabled)
+//                                                                     
printf("Data row %lu col %u: %" PRId128 "\n", row, col+1, hge_val);
+//                                                             gdkret = 
BUNappend(b, (void *) &hge_val, false);
+//                                                             break;
+#endif
                                                        case SQL_DECIMAL:
                                                        case SQL_NUMERIC:
                                                        {
@@ -817,75 +913,6 @@ odbc_query(int caller, mvc *sql, sql_sub
                                                                        if 
(trace_enabled)
                                                                                
printf("Data row %lu col %u: %s\n", row, col+1, str_val);
                                                                        gdkret 
= BUNappend(b, (void *) str_val, false);
-                                                                       break;
-                                                               }
-
-                                                               if 
(trace_enabled)
-                                                                       
printf("Data row %lu col %u: %s  convert to lng/hge using colSize %d digits 
%d\n",
-                                                                               
row, col+1, str_val, (int)colmetadata[col].columnSize, 
colmetadata[col].decimalDigits);
-
-                                                               /* convert 
str_val to lng_val or hge_val based on sql_bind_subtype(sql->sa, "decimal", 
columnSize, decimalDigits); */
-                                                               int digits;
-                                                               int scale;
-                                                               int has_errors;
-                                                               DEC_TPE res = 
decimal_from_str(str_val, &digits, &scale, &has_errors);
-                                                               /* or use: str 
str_2dec(TYPE *res, const str *val, const int *d, const int *sc)) */
-                                                               if (has_errors) 
{
-                                                                       if 
(trace_enabled)
-                                                                               
printf("decimal_from_str() failed to convert dec value: %s to lng/hge\n", 
str_val);
-                                                                       gdkret 
= BUNappend(b, ATOMnilptr(b->ttype), false);
-                                                                       break;
-                                                               }
-
-                                                               switch(battype) 
{
-#ifdef HAVE_HGE
-                                                                       case 
TYPE_hge:
-                                                                       {
-                                                                               
hge_val = (hge) res;
-//                                                                             
if (trace_enabled)
-//                                                                             
        printf("Data row %lu col %u: %" PRId128 "\n", row, col+1, hge_val);
-                                                                               
gdkret = BUNappend(b, (void *) &hge_val, false);
-                                                                               
break;
-                                                                       }
-#endif
-                                                                       case 
TYPE_lng:
-                                                                       {
-                                                                               
lng_val = (lng) res;
-                                                                               
if (trace_enabled)
-                                                                               
        printf("Data row %lu col %u: %" PRId64 "\n", row, col+1, lng_val);
-                                                                               
gdkret = BUNappend(b, (void *) &lng_val, false);
-                                                                               
break;
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to