Patch varcharmax-fix-v2.diff for ODBC related Bug #44278
Patch version 2017-11-18 11:30 UTC
Return to Bug #44278 |
Download this patch
Patch Revisions:
Developer: chris@ocproducts.com
diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c
index f432dcd..cdcfe41 100644
--- a/ext/odbc/php_odbc.c
+++ b/ext/odbc/php_odbc.c
@@ -968,11 +968,14 @@ int odbc_bindcols(odbc_result *result)
result->values[i].name, sizeof(result->values[i].name), &colnamelen, 0);
rc = PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)(i+1), SQL_COLUMN_TYPE,
NULL, 0, NULL, &result->values[i].coltype);
-
- /* Don't bind LONG / BINARY columns, so that fetch behaviour can
- * be controlled by odbc_binmode() / odbc_longreadlen()
+
+ /* Don't bind LONG / BINARY columns:
+ * a) so that fetch behaviour can be controlled by odbc_binmode()
+ * b) ... or odbc_longreadlen(),
+ * c) for performance reasons (user can do individual odbc_result calls)
+ * (ODBC implementation may impose SqlGetData due to SQL_NO_TOTAL length anyway, handled if it happens)
*/
-
+
switch(result->values[i].coltype) {
case SQL_BINARY:
case SQL_VARBINARY:
@@ -1024,23 +1027,36 @@ int odbc_bindcols(odbc_result *result)
NULL, 0, NULL, &displaysize);
}
- /* Workaround for drivers that report NVARCHAR(MAX) columns as SQL_WVARCHAR with size 0 (bug #69975) */
+ /* Workaround for drivers that report NVARCHAR(MAX) columns as SQL_WVARCHAR with size 0 [SQL_SS_LENGTH_UNLIMITED] (bug #69975) */
if (result->values[i].coltype == SQL_WVARCHAR && displaysize == 0) {
result->values[i].coltype = SQL_WLONGVARCHAR;
result->values[i].value = NULL;
break;
}
#endif
+ /* Workaround for drivers that report VARCHAR(MAX) columns as SQL_WVARCHAR with size 0 [SQL_SS_LENGTH_UNLIMITED] */
+ if (result->values[i].coltype == SQL_VARCHAR && displaysize == 0) {
+ result->values[i].coltype = SQL_LONGVARCHAR;
+ result->values[i].value = NULL;
+ break;
+ }
+
/* Workaround for Oracle ODBC Driver bug (#50162) when fetching TIMESTAMP column */
if (result->values[i].coltype == SQL_TIMESTAMP) {
displaysize += 3;
}
if (charextraalloc) {
- /* Since we don't know the exact # of bytes, allocate extra */
+ /* Since we don't know the exact # of bytes, allocate extra. displaysize is smart enough to set length based on VARCHAR max length though */
displaysize *= 4;
}
result->values[i].value = (char *)emalloc(displaysize + 1);
+
+ /* It is conceivable that SQLBindCol will never actually execute filling up of the values[i] struct,
+ due to long data and buggy ODBC implementation, so default it to say we need to do a long data call.
+ */
+ result->values[i].vallen = SQL_NO_TOTAL;
+
rc = SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value,
displaysize + 1, &result->values[i].vallen);
break;
@@ -1775,6 +1791,11 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type)
for(i = 0; i < result->numcols; i++) {
sql_c_type = SQL_C_CHAR;
+ if (result->values[i].vallen < 0) {
+ /* Illegal value for doing emalloc, likely a SQL_NO_TOTAL (-4), meaning we need to do SQLGetData rather than just SQLBindCol */
+ result->values[i].coltype = SQL_LONGVARCHAR; /* A bit of a hack, but safe and this will allow the correct processing branch to flow */
+ }
+
switch(result->values[i].coltype) {
case SQL_BINARY:
case SQL_VARBINARY:
@@ -1929,6 +1950,11 @@ PHP_FUNCTION(odbc_fetch_into)
for(i = 0; i < result->numcols; i++) {
sql_c_type = SQL_C_CHAR;
+ if (result->values[i].vallen < 0) {
+ /* Illegal value for doing emalloc, likely a SQL_NO_TOTAL (-4), meaning we need to do SQLGetData rather than just SQLBindCol */
+ result->values[i].coltype = SQL_LONGVARCHAR; /* A bit of a hack, but safe and this will allow the correct processing branch to flow */
+ }
+
switch(result->values[i].coltype) {
case SQL_BINARY:
case SQL_VARBINARY:
@@ -2155,6 +2181,11 @@ PHP_FUNCTION(odbc_result)
result->fetched++;
}
+ if (result->values[field_ind].vallen < 0) {
+ /* Illegal value for doing emalloc, likely a SQL_NO_TOTAL (-4), meaning we need to do SQLGetData rather than just SQLBindCol */
+ result->values[field_ind].coltype = SQL_LONGVARCHAR; /* A bit of a hack, but safe and this will allow the correct processing branch to flow */
+ }
+
switch(result->values[field_ind].coltype) {
case SQL_BINARY:
case SQL_VARBINARY:
@@ -2320,6 +2351,11 @@ PHP_FUNCTION(odbc_result_all)
result->fetched++;
php_printf("<tr>");
for(i = 0; i < result->numcols; i++) {
+ if (result->values[i].vallen < 0) {
+ /* Illegal value for doing emalloc, likely a SQL_NO_TOTAL (-4), meaning we need to do SQLGetData rather than just SQLBindCol */
+ result->values[i].coltype = SQL_LONGVARCHAR; /* A bit of a hack, but safe and this will allow the correct processing branch to flow */
+ }
+
sql_c_type = SQL_C_CHAR;
switch(result->values[i].coltype) {
case SQL_BINARY:
|