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]