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

Add validity checks on correct battype before doing the BUNappend() to avoid 
possible server crashes.
Moved the switch for determining the C-targetType, targetValuePtr and 
bufferLength (needed by SQLGetData()) to outside the fetching rows loop.
It is now done before fetching the data, and the values are stored in the 
rescol_t array for fast access.


diffs (truncated from 826 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
@@ -56,6 +56,9 @@ typedef struct {
        SQLSMALLINT decimalDigits;      /* ODBC dec. digits, contains scale for 
decimals */
        int battype;                    /* MonetDB atom type, used to create 
the BAT */
        BAT * bat;                      /* MonetDB BAT */
+       SQLSMALLINT targetType;         /* needed for SQLGetData */
+       SQLPOINTER * targetValuePtr;    /* needed for SQLGetData */
+       SQLLEN bufferLength;            /* needed for SQLGetData */
 } rescol_t;
 
 /* map ODBC SQL datatype to MonetDB SQL datatype */
@@ -426,8 +429,8 @@ fraction2msec(SQLUINTEGER fraction, SQLS
                case 9: msec = fraction / 1000000; break;
        }
 
-       // millisec value should be no larger than 1000
-       while (msec > 1000) {
+       // millisec value should be no larger than 999
+       while (msec > 999) {
                msec = msec / 10;
        }
        return msec;
@@ -674,9 +677,32 @@ odbc_query(int caller, mvc *sql, sql_sub
                        goto finish;
                }
 
+               /* allocate buffers for each of the fixed size atom types. */
+               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 = 0;        // for hugeint and decimals with 
precision > 18
+#endif
+               flt flt_val = 0;
+               dbl dbl_val = 0;
+               DATE_STRUCT date_val;
+               TIME_STRUCT time_val;
+               TIMESTAMP_STRUCT ts_val;
+               SQL_INTERVAL_STRUCT itv_val;
+               SQLGUID guid_val;
+               union {
+                       uuid uuid_val;
+                       uint8_t u[UUID_SIZE];
+               } u_val;
+
+               bool hasStrCols = false;
                SQLULEN largestStringSize = 0;
                bool hasBlobCols = false;
                SQLULEN largestBlobSize = 0;
+
                /* make bats with right atom type */
                for (SQLUSMALLINT col = 0; col < (SQLUSMALLINT) nr_cols; col++) 
{
                        char cname[MAX_COL_NAME_LEN +1];
@@ -703,44 +729,191 @@ odbc_query(int caller, mvc *sql, sql_sub
                        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;
-//                                     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;
                        colmetadata[col].decimalDigits = decimalDigits;
+                       colmetadata[col].bufferLength = 0;
+
                        battype = getBatType(getArgType(mb, pci, col));
                        colmetadata[col].battype = battype;
                        if (battype == TYPE_str) {
+                               hasStrCols = true;
+                               if (dataType == SQL_DECIMAL || dataType == 
SQL_NUMERIC) {
+                                       /* read it as string */
+                                       if (columnSize < 38) {
+                                               columnSize = 38;
+                                       }
+                                       /* add 3 for: sign, possible leading 0 
and decimal separator */
+                                       columnSize += 3;
+                                       colmetadata[col].columnSize = 
columnSize;
+                               }
                                if (columnSize > largestStringSize) {
                                        largestStringSize = columnSize;
                                }
                        } else
+                       if (battype == TYPE_hge) {
+                               if (dataType == SQL_HUGEINT) {
+                                       /* read it as string */
+                                       hasStrCols = true;
+                                       if (columnSize < 50) {
+                                               columnSize = 50;
+                                               colmetadata[col].columnSize = 
columnSize;
+                                       }
+                                       if (columnSize > largestStringSize) {
+                                               largestStringSize = columnSize;
+                                       }
+                                       colmetadata[col].bufferLength = 
largestStringSize;
+                               }
+                       } else
                        if (battype == TYPE_blob) {
                                hasBlobCols = true;
                                if (columnSize > largestBlobSize) {
                                        largestBlobSize = columnSize;
                                }
                        }
+
+                       /* mapping based on 
https://learn.microsoft.com/en-us/sql/odbc/reference/appendixes/c-data-types */
+                       switch(dataType) {
+                               case SQL_CHAR:
+                               case SQL_VARCHAR:
+                               case SQL_LONGVARCHAR:
+                               case SQL_WCHAR:
+                               case SQL_WVARCHAR:
+                               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
+                                       break;
+                               case SQL_BIT:
+                                       colmetadata[col].targetType = SQL_C_BIT;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &bit_val;
+                                       break;
+                               case SQL_TINYINT:
+                                       colmetadata[col].targetType = 
SQL_C_STINYINT;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &bte_val;
+                                       break;
+                               case SQL_SMALLINT:
+                                       colmetadata[col].targetType = 
SQL_C_SSHORT;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &sht_val;
+                                       break;
+                               case SQL_INTEGER:
+                                       colmetadata[col].targetType = 
SQL_C_SLONG;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &int_val;
+                                       break;
+                               case SQL_BIGINT:
+                                       colmetadata[col].targetType = 
SQL_C_SBIGINT;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &lng_val;
+                                       break;
+                               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
+                                       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
+                                       break;
+                               case SQL_REAL:
+                                       colmetadata[col].targetType = 
SQL_C_FLOAT;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &flt_val;
+                                       break;
+                               case SQL_FLOAT:
+                                       /* use same logic as used in 
map_rescol_type() for SQL_FLOAT and SQL_DECIMAL */
+                                       if (colmetadata[col].battype == 
TYPE_flt) {
+                                               colmetadata[col].dataType = 
SQL_REAL;
+                                               colmetadata[col].targetType = 
SQL_C_FLOAT;
+                                               colmetadata[col].targetValuePtr 
= (SQLPOINTER *) &flt_val;
+                                       } else {
+                                               colmetadata[col].dataType = 
SQL_DOUBLE;
+                                               colmetadata[col].targetType = 
SQL_C_DOUBLE;
+                                               colmetadata[col].targetValuePtr 
= (SQLPOINTER *) &dbl_val;
+                                       }
+                                       break;
+                               case SQL_DOUBLE:
+                                       colmetadata[col].targetType = 
SQL_C_DOUBLE;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &dbl_val;
+                                       break;
+                               case SQL_TYPE_DATE:
+                                       colmetadata[col].targetType = 
SQL_C_TYPE_DATE;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &date_val;
+                                       break;
+                               case SQL_TYPE_TIME:
+                                       colmetadata[col].targetType = 
SQL_C_TYPE_TIME;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &time_val;
+                                       break;
+                               case SQL_DATETIME:
+                               case SQL_TYPE_TIMESTAMP:
+                                       colmetadata[col].targetType = 
SQL_C_TYPE_TIMESTAMP;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &ts_val;
+                                       break;
+                               case SQL_INTERVAL_YEAR:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_YEAR;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_YEAR_TO_MONTH:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_YEAR_TO_MONTH;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_MONTH:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_MONTH;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_DAY:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_DAY;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_HOUR:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_HOUR;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_MINUTE:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_MINUTE;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_SECOND:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_SECOND;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_DAY_TO_HOUR:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_DAY_TO_HOUR;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_DAY_TO_MINUTE:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_DAY_TO_MINUTE;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_DAY_TO_SECOND:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_DAY_TO_SECOND;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_HOUR_TO_MINUTE:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_HOUR_TO_MINUTE;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_HOUR_TO_SECOND:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_HOUR_TO_SECOND;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_INTERVAL_MINUTE_TO_SECOND:
+                                       colmetadata[col].targetType = 
SQL_C_INTERVAL_MINUTE_TO_SECOND;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &itv_val;
+                                       break;
+                               case SQL_GUID:
+                                       colmetadata[col].targetType = 
SQL_C_GUID;
+                                       colmetadata[col].targetValuePtr = 
(SQLPOINTER *) &guid_val;
+                                       colmetadata[col].bufferLength = 
(SQLLEN) sizeof(SQLGUID);
+                                       break;
+                               case SQL_BINARY:
+                               case SQL_VARBINARY:
+                               case SQL_LONGVARBINARY:
+                                       colmetadata[col].targetType = 
SQL_C_BINARY;
+                                       // colmetadata[col].targetValuePtr = 
(SQLPOINTER *) bin_data;  // will be done after allocation
+                                       break;
+                       }
+
                        if (trace_enabled)
                                printf("ResCol %u, name: %s, type %d (%s), size 
%u, decdigits %d, battype %d\n",
                                        col+1, cname, dataType, 
nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits, battype);
@@ -764,31 +937,11 @@ odbc_query(int caller, mvc *sql, sql_sub
                        }
                }
 
-               /* allocate buffers for all the fixed size atom types. */
-               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 = 0;        // for hugeint and decimals with 
precision > 18
-#endif
-               flt flt_val = 0;
-               dbl dbl_val = 0;
-               DATE_STRUCT date_val;
-               TIME_STRUCT time_val;
-               TIMESTAMP_STRUCT ts_val;
-               SQL_INTERVAL_STRUCT itv_val;
-               SQLGUID guid_val;
-               union {
-                       uuid uuid_val;
-                       uint8_t u[UUID_SIZE];
-               } u_val;
-
-               /* allocate target buffers for the variable sized atom types; 
TYPE_str, TYPE_blob. */
+               /* allocate large enough read buffers for storing string (and 
binary blob) data */
                char * str_val = NULL;          // TODO: change to wchar
                uint8_t * bin_data = NULL;
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to