summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers
diff options
context:
space:
mode:
authorChristian Ehrlicher <ch.ehrlicher@gmx.de>2023-02-03 22:20:28 +0100
committerChristian Ehrlicher <ch.ehrlicher@gmx.de>2023-02-07 22:27:37 +0100
commitefce30bb4309ee0ae6af35b8fe6f62ccfb67622c (patch)
tree9720e0e7f294d8faf41c74f9a566bb0c87e3098b /src/plugins/sqldrivers
parenta53fe60a925acfa160e6bb5f050b2114047cc07c (diff)
ODBC/SQL: Fix usage of SQLGetData
SQLGetData was called with a nullptr for the output buffer to determine the buffer length which is not allowed. Fix it by passing a valid buffer with a buffer size of 0. Fixes: QTBUG-70362 Fixes: QTBUG-110803 Change-Id: I4d547383100714901a4e2ca3b4777326cfab12fe Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/plugins/sqldrivers')
-rw-r--r--src/plugins/sqldrivers/odbc/qsql_odbc.cpp143
1 files changed, 49 insertions, 94 deletions
diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
index a90125af14..e73b573e42 100644
--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
@@ -370,114 +370,69 @@ static QMetaType qDecodeODBCType(SQLSMALLINT sqltype, bool isSigned = true)
return QMetaType(type);
}
-static QVariant qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool unicode)
+template <typename CT>
+static QVariant getStringDataImpl(SQLHANDLE hStmt, SQLUSMALLINT column, qsizetype colSize, SQLSMALLINT targetType)
{
QString fieldVal;
SQLRETURN r = SQL_ERROR;
SQLLEN lengthIndicator = 0;
-
- // NB! colSize must be a multiple of 2 for unicode enabled DBs
- if (colSize <= 0) {
- colSize = 256;
- } else if (colSize > 65536) { // limit buffer size to 64 KB
- colSize = 65536;
- } else {
- colSize++; // make sure there is room for more than the 0 termination
- }
- if (unicode) {
+ QVarLengthArray<CT> buf(colSize);
+ while (true) {
r = SQLGetData(hStmt,
- column+1,
- SQL_C_TCHAR,
- NULL,
- 0,
- &lengthIndicator);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
- colSize = int(lengthIndicator / sizeof(SQLTCHAR) + 1);
- QVarLengthArray<SQLTCHAR> buf(colSize);
- while (true) {
- r = SQLGetData(hStmt,
- column+1,
- SQL_C_TCHAR,
- (SQLPOINTER)buf.data(),
- colSize*sizeof(SQLTCHAR),
- &lengthIndicator);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
- if (lengthIndicator == SQL_NULL_DATA) {
- return {};
- }
- // starting with ODBC Native Client 2012, SQL_NO_TOTAL is returned
- // instead of the length (which sometimes was wrong in older versions)
- // see link for more info: http://msdn.microsoft.com/en-us/library/jj219209.aspx
- // if length indicator equals SQL_NO_TOTAL, indicating that
- // more data can be fetched, but size not known, collect data
- // and fetch next block
- if (lengthIndicator == SQL_NO_TOTAL) {
- fieldVal += fromSQLTCHAR(buf, colSize);
- continue;
- }
- // if SQL_SUCCESS_WITH_INFO is returned, indicating that
- // more data can be fetched, the length indicator does NOT
- // contain the number of bytes returned - it contains the
- // total number of bytes that CAN be fetched
- int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : int(lengthIndicator / sizeof(SQLTCHAR));
- fieldVal += fromSQLTCHAR(buf, rSize);
- if (lengthIndicator < SQLLEN(colSize*sizeof(SQLTCHAR))) {
- // workaround for Drivermanagers that don't return SQL_NO_DATA
- break;
- }
- } else if (r == SQL_NO_DATA) {
- break;
- } else {
- qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
+ column + 1,
+ targetType,
+ SQLPOINTER(buf.data()), SQLINTEGER(buf.size() * sizeof(CT)),
+ &lengthIndicator);
+ if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+ if (lengthIndicator == SQL_NULL_DATA) {
return {};
}
- }
- } else {
- r = SQLGetData(hStmt,
- column+1,
- SQL_C_CHAR,
- NULL,
- 0,
- &lengthIndicator);
- if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
- colSize = lengthIndicator + 1;
- QVarLengthArray<SQLCHAR> buf(colSize);
- while (true) {
- r = SQLGetData(hStmt,
- column+1,
- SQL_C_CHAR,
- (SQLPOINTER)buf.data(),
- colSize,
- &lengthIndicator);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
- if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
- return {};
- }
- // if SQL_SUCCESS_WITH_INFO is returned, indicating that
- // more data can be fetched, the length indicator does NOT
- // contain the number of bytes returned - it contains the
- // total number of bytes that CAN be fetched
- qsizetype rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator;
- // Remove any trailing \0 as some drivers misguidedly append one
- int realsize = qMin(rSize, buf.size());
- if (realsize > 0 && buf[realsize - 1] == 0)
- realsize--;
- fieldVal += QString::fromUtf8(reinterpret_cast<const char *>(buf.constData()), realsize);
- if (lengthIndicator < SQLLEN(colSize)) {
- // workaround for Drivermanagers that don't return SQL_NO_DATA
- break;
- }
- } else if (r == SQL_NO_DATA) {
+ // starting with ODBC Native Client 2012, SQL_NO_TOTAL is returned
+ // instead of the length (which sometimes was wrong in older versions)
+ // see link for more info: http://msdn.microsoft.com/en-us/library/jj219209.aspx
+ // if length indicator equals SQL_NO_TOTAL, indicating that
+ // more data can be fetched, but size not known, collect data
+ // and fetch next block
+ if (lengthIndicator == SQL_NO_TOTAL) {
+ fieldVal += fromSQLTCHAR<QVarLengthArray<CT>, sizeof(CT)>(buf, buf.size());
+ continue;
+ }
+ // if SQL_SUCCESS_WITH_INFO is returned, indicating that
+ // more data can be fetched, the length indicator does NOT
+ // contain the number of bytes returned - it contains the
+ // total number of bytes that CAN be fetched
+ const qsizetype rSize = (r == SQL_SUCCESS_WITH_INFO)
+ ? buf.size()
+ : qsizetype(lengthIndicator / sizeof(CT));
+ fieldVal += fromSQLTCHAR<QVarLengthArray<CT>, sizeof(CT)>(buf, rSize);
+ // lengthIndicator does not contain the termination character
+ if (lengthIndicator < SQLLEN((buf.size() - 1) * sizeof(CT))) {
+ // workaround for Drivermanagers that don't return SQL_NO_DATA
break;
- } else {
- qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
- return {};
}
+ } else if (r == SQL_NO_DATA) {
+ break;
+ } else {
+ qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
+ return {};
}
}
return fieldVal;
}
+static QVariant qGetStringData(SQLHANDLE hStmt, SQLUSMALLINT column, int colSize, bool unicode)
+{
+ if (colSize <= 0) {
+ colSize = 256; // default Prealloc size of QVarLengthArray
+ } else if (colSize > 65536) { // limit buffer size to 64 KB
+ colSize = 65536;
+ } else {
+ colSize++; // make sure there is room for more than the 0 termination
+ }
+ return unicode ? getStringDataImpl<SQLTCHAR>(hStmt, column, colSize, SQL_C_TCHAR)
+ : getStringDataImpl<SQLCHAR>(hStmt, column, colSize, SQL_C_CHAR);
+}
+
static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
{
QByteArray fieldVal;