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

Improve odbc_loader when reading decimals. They are now mapped to MonetDB bat 
storage types hge/lng/int/sht/bte when possible.


diffs (truncated from 421 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
@@ -80,37 +80,37 @@ map_rescol_type(SQLSMALLINT dataType, SQ
        case SQL_WVARCHAR:
        case SQL_WLONGVARCHAR:
        default:        /* all other ODBC types are also mapped to varchar for 
now */
+       {
                /* all ODBC char datatypes are mapped to varchar. char and clob 
are internally not used anymore */
                if (columnSize > (SQLULEN) INT_MAX)
                        columnSize = INT_MAX;
                return sql_bind_subtype(sql->sa, "varchar", (unsigned int) 
columnSize, 0);
+       }
 
        case SQL_BINARY:
        case SQL_VARBINARY:
        case SQL_LONGVARBINARY:
+       {
                if (columnSize > (SQLULEN) INT_MAX)
                        columnSize = INT_MAX;
                return sql_bind_subtype(sql->sa, "blob", (unsigned int) 
columnSize, 0);
+       }
 
        case SQL_DECIMAL:
        case SQL_NUMERIC:
        {
                /* columnSize contains the defined number of digits, so 
precision. */
                /* decimalDigits contains the scale (which can be negative). */
-               if (columnSize > MAX_PREC || abs(decimalDigits) > MAX_PREC) {
-                       /* too large precision/scale, not supported by MonetDB. 
Map this column to a string */
-                       if (columnSize > (SQLULEN) INT_MAX)
-                               columnSize = INT_MAX;
+               if (columnSize > MAX_PREC || columnSize < 1 || decimalDigits < 0
+                || decimalDigits > MAX_PREC || (int) decimalDigits > (int) 
columnSize) {
+                       /* too large precision/scale or negative scale are NOT 
supported by MonetDB. Map this column to a string bat type */
+                       if (columnSize > (SQLULEN) INT_MAX -3)
+                               columnSize = INT_MAX -3;
                        return sql_bind_subtype(sql->sa, "varchar", (unsigned 
int) columnSize +3, 0);
                }
+               return sql_bind_subtype(sql->sa, "decimal", (unsigned int) 
columnSize, (unsigned int) decimalDigits);
+       }
 
-               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_GUID:
        {
                /* represents a uuid of length 36, such as: 
dbe7343c-1f11-4fa9-a9c8-a31cd26f92fe */
@@ -300,35 +300,6 @@ nameOfRetCode(SQLRETURN code)
        }
 }
 
-#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
-
 /* an ODBC function call returned an error, get the error msg from the ODBC 
driver */
 static char *
 getErrMsg(SQLSMALLINT handleType, SQLHANDLE handle) {
@@ -411,6 +382,132 @@ bat_create(int adt, BUN nr)
        return b;
 }
 
+/* convert decimal values to lng as needed by MonetDB decimal storage type.
+ * we require the precision and scale specification of the column.
+ */
+static lng
+decstr_to_lng(const char *s, int prec, SQLSMALLINT scale) {
+       char c;
+       char sign = '+';
+       int i = 0;
+       int digits = 0;
+       SQLSMALLINT decdigits = -1;
+       int decsep = -1;
+       lng ret = 0;
+
+       if (!s)
+               return 0;
+
+       c = s[i];
+       if (c == '-' || c == '+') {
+               sign = c;
+               c = s[++i];
+       }
+       while (c) {
+               if (digits > prec || decdigits >= scale)
+                       break;  // we have read enough
+               if (c >= '0' && c <= '9') {
+                       ret *= 10;
+                       ret += (int) c - '0';
+                       digits++;
+                       if (decsep >= 0)
+                               decdigits++;
+               } else if (c == '.') {
+                       if (decsep < 0) {
+                               decsep = i;
+                               decdigits = 0;
+                       }
+               }
+               c = s[++i];
+       }
+       if (decdigits == -1)
+               decdigits = 0;
+       while (decdigits < scale) {
+               // align to the scale number of digits
+               ret *= 10;
+               decdigits++;
+       }
+       if (sign == '-')
+               ret = -ret;
+       return ret;
+}
+
+#ifdef HAVE_HGE
+static hge
+decstr_to_hge(const char *s, int prec, SQLSMALLINT scale) {
+       char c;
+       char sign = '+';
+       int i = 0;
+       int digits = 0;
+       SQLSMALLINT decdigits = -1;
+       int decsep = -1;
+       hge ret = 0;
+
+       if (!s)
+               return 0;
+
+       c = s[i];
+       if (c == '-' || c == '+') {
+               sign = c;
+               c = s[++i];
+       }
+       while (c) {
+               if (digits > prec || decdigits >= scale)
+                       break;  // we have read enough
+               if (c >= '0' && c <= '9') {
+                       ret *= 10;
+                       ret += (int) c - '0';
+                       digits++;
+                       if (decsep >= 0)
+                               decdigits++;
+               } else if (c == '.') {
+                       if (decsep < 0) {
+                               decsep = i;
+                               decdigits = 0;
+                       }
+               }
+               c = s[++i];
+       }
+       if (decdigits == -1)
+               decdigits = 0;
+       while (decdigits < scale) {
+               // align to the scale
+               ret *= 10;
+               decdigits++;
+       }
+       if (sign == '-')
+               ret = -ret;
+       return ret;
+}
+
+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
+
 /* convert interval.day_second.fraction values to millisec fractions as needed 
by MonetDB interval types.
  * we need the columns decimalDigits specification to adjust the fractions 
value to millisec.
  */
@@ -682,7 +779,7 @@ odbc_query(int caller, mvc *sql, sql_sub
                int int_val = 0;
                lng lng_val = 0;
 #ifdef HAVE_HGE
-               hge hge_val = 0;        // for hugeint and decimals with 
precision > 18
+               hge hge_val = 0;        // for hugeint and decimals with 
precision between 19 and 38
 #endif
                flt flt_val = 0;
                dbl dbl_val = 0;
@@ -782,7 +879,7 @@ odbc_query(int caller, mvc *sql, sql_sub
                                case SQL_WLONGVARCHAR:
                                default:
                                        colmetadata[col].targetType = 
SQL_C_CHAR;       // TODO later: SQL_C_WCHAR
-                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) str_val;  // will be done after allocation
+                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) str_val;  // will be done after str_val allocation
                                        break;
                                case SQL_BIT:
                                        colmetadata[col].targetType = SQL_C_BIT;
@@ -807,13 +904,13 @@ odbc_query(int caller, mvc *sql, sql_sub
                                case SQL_HUGEINT:
                                        /* read huge int data as string data as 
there is no SQL_C_SHUGEINT */
                                        colmetadata[col].targetType = 
SQL_C_CHAR;
-                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) str_val;  // will be done after allocation
+                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) str_val;  // will be done after str_val allocation
                                        break;
                                case SQL_DECIMAL:
                                case SQL_NUMERIC:
                                        /* read decimal data always as string 
data and convert it to the right internal decimal format and bat type */
                                        colmetadata[col].targetType = 
SQL_C_CHAR;
-                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) str_val;  // will be done after allocation
+                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) str_val;  // will be done after str_val allocation
                                        break;
                                case SQL_REAL:
                                        colmetadata[col].targetType = 
SQL_C_FLOAT;
@@ -909,7 +1006,7 @@ odbc_query(int caller, mvc *sql, sql_sub
                                case SQL_VARBINARY:
                                case SQL_LONGVARBINARY:
                                        colmetadata[col].targetType = 
SQL_C_BINARY;
-                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) bin_data;  // will be done after allocation
+                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) bin_data;  // will be done after bin_data allocation
                                        break;
                        }
 
@@ -1038,7 +1135,7 @@ odbc_query(int caller, mvc *sql, sql_sub
                                                                                
break;
 #ifdef HAVE_HGE
                                                                        case 
TYPE_hge:
-                                                                               
/* HUGEINT values are read as string */
+                                                                               
/* HUGEINT values are passed as string, need to convert to hge */
                                                                                
hge_val = str_to_hge(str_val);
                                                                                
gdkret = BUNappend(b, (void *) &hge_val, false);
                                                                                
break;
@@ -1090,24 +1187,52 @@ odbc_query(int caller, mvc *sql, sql_sub
                                                                break;
                                                        case SQL_DECIMAL:
                                                        case SQL_NUMERIC:
-                                                               if 
(colmetadata[col].battype == TYPE_str) {
-                                                                       if 
(strLen != SQL_NTS && strLen >= 0) {
-                                                                               
/* make sure it is a Nul Terminated String */
-                                                                               
if ((SQLULEN) strLen < largestStringSize) {
-                                                                               
        if (str_val[strLen] != '\0')
-                                                                               
                str_val[strLen] = '\0';
-                                                                               
} else {
-                                                                               
        if (str_val[largestStringSize] != '\0')
-                                                                               
                str_val[largestStringSize] = '\0';
-                                                                               
}
+                                                               /* decimal 
values are always read as string */
+                                                               if (strLen != 
SQL_NTS && strLen >= 0) {
+                                                                       /* make 
sure it is a Nul Terminated String */
+                                                                       if 
((SQLULEN) strLen < largestStringSize) {
+                                                                               
if (str_val[strLen] != '\0')
+                                                                               
        str_val[strLen] = '\0';
+                                                                       } else {
+                                                                               
if (str_val[largestStringSize] != '\0')
+                                                                               
        str_val[largestStringSize] = '\0';
                                                                        }
-                                                                       
TRC_DEBUG(LOADER, "Data row %lu col %u: %s\n", row, col+1, str_val);
-                                                                       gdkret 
= BUNappend(b, (void *) str_val, false);
-                                                               } else {
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to