summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@qt.io>2023-01-30 15:37:13 +0100
committerMarc Mutz <marc.mutz@qt.io>2023-02-03 08:20:38 +0000
commit458e2714e1a9c10647bc112a1d33afc7191be4c6 (patch)
tree7bfcef6aeb4769b979bdeeb783c09853a8f2c566
parent54fd964b179569dec2dc3e8e301e6ff928278323 (diff)
SQL/ODBC: fix some users of toSQLTCHAR() to not assume identical UTF-8/16/32 string lengths
We already fixed the implementation of toSQLTCHAR() in 66767eea46bea0f19f8ae5ad6ebc641d86867701 to not assume that a UTF-8 or UTF-32-encoded string has the same number of code points as the equivalent UTF-16 string, but it turns out that users of the function, as well as other code, also failed to account for this. This patch fixes callers of toSQLTCHAR() to use const auto encoded = toSQLTCHAR(s); ~~~ use encoded.data(), encoded.size() ~~~ (except we can't make `encoded` const, because the SQL API isn't const-correct and takes void* instead of const void*) instead of the anti-pattern ~~~ use toSQLTCHAR(s).data(), s.size() ~~~ As a drive-by: - Extract Method qt_string_SQLSetConnectAttr() - skipping an unneeded .utf16() call (a NUL-terminated string is not required for calling toSQLTCHAR()) - de-duplicate some code in exec() - and make a comment there slightly more informative - replace - NULL with nullptr - size() == 0 with isEmpty() - C-style with constructor-style casts Manual conflict resolutions: - _L1 vs. QLatin1String - u'x' vs. QLatin1Char('x') - qsizetype vs. int Change-Id: I3696381d0a93af8861ce2b7915f212d9e5e9a243 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> (cherry picked from commit 46af1fe49f7f419dc1b3231de9860e2da0ea48f8) Reviewed-by: Andy Shaw <andy.shaw@qt.io>
-rw-r--r--src/plugins/sqldrivers/odbc/qsql_odbc.cpp153
1 files changed, 83 insertions, 70 deletions
diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
index fdef313d46..58b220ad3e 100644
--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
@@ -779,6 +779,14 @@ QChar QODBCDriverPrivate::quoteChar()
return quote;
}
+static SQLRETURN qt_string_SQLSetConnectAttr(SQLHDBC handle, SQLINTEGER attr, const QString &val)
+{
+ auto encoded = toSQLTCHAR(val);
+ return SQLSetConnectAttr(handle, attr,
+ encoded.data(),
+ SQLINTEGER(encoded.size() * sizeof(SQLTCHAR))); // size in bytes
+}
+
bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
{
@@ -814,10 +822,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
v = val.toUInt();
r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) size_t(v), 0);
} else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) {
- val.utf16(); // 0 terminate
- r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
- toSQLTCHAR(val).data(),
- val.length()*sizeof(SQLTCHAR));
+ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, val);
} else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) {
if (val.toUpper() == QLatin1String("SQL_TRUE")) {
v = SQL_TRUE;
@@ -832,10 +837,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
v = val.toUInt();
r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) size_t(v), 0);
} else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) {
- val.utf16(); // 0 terminate
- r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
- toSQLTCHAR(val).data(),
- val.length()*sizeof(SQLTCHAR));
+ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, val);
} else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) {
if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) {
v = SQL_OPT_TRACE_OFF;
@@ -1038,9 +1040,12 @@ bool QODBCResult::reset (const QString& query)
return false;
}
- r = SQLExecDirect(d->hStmt,
- toSQLTCHAR(query).data(),
- (SQLINTEGER) query.length());
+ {
+ auto encoded = toSQLTCHAR(query);
+ r = SQLExecDirect(d->hStmt,
+ encoded.data(),
+ SQLINTEGER(encoded.size()));
+ }
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) {
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
"Unable to execute statement"), QSqlError::StatementError, d));
@@ -1387,9 +1392,12 @@ bool QODBCResult::prepare(const QString& query)
return false;
}
- r = SQLPrepare(d->hStmt,
- toSQLTCHAR(query).data(),
- (SQLINTEGER) query.length());
+ {
+ auto encoded = toSQLTCHAR(query);
+ r = SQLPrepare(d->hStmt,
+ encoded.data(),
+ SQLINTEGER(encoded.size()));
+ }
if (r != SQL_SUCCESS) {
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
@@ -1417,7 +1425,7 @@ bool QODBCResult::exec()
SQLCloseCursor(d->hStmt);
QVector<QVariant>& values = boundValues();
- QVector<QByteArray> tmpStorage(values.count(), QByteArray()); // holds temporary buffers
+ QVector<QByteArray> tmpStorage(values.count(), QByteArray()); // targets for SQLBindParameter()
QVarLengthArray<SQLLEN, 32> indicators(values.count());
memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN));
@@ -1596,35 +1604,36 @@ bool QODBCResult::exec()
case QVariant::String:
if (d->unicode) {
QByteArray &ba = tmpStorage[i];
- QString str = val.toString();
+ {
+ const auto encoded = toSQLTCHAR(val.toString());
+ ba = QByteArray(reinterpret_cast<const char *>(encoded.data()),
+ encoded.size() * sizeof(SQLTCHAR));
+ }
+
if (*ind != SQL_NULL_DATA)
- *ind = str.length() * sizeof(SQLTCHAR);
- int strSize = str.length() * sizeof(SQLTCHAR);
+ *ind = ba.size();
if (bindValueType(i) & QSql::Out) {
- const QVarLengthArray<SQLTCHAR> a(toSQLTCHAR(str));
- ba = QByteArray((const char *)a.constData(), a.size() * sizeof(SQLTCHAR));
r = SQLBindParameter(d->hStmt,
i + 1,
qParamType[bindValueType(i) & QSql::InOut],
SQL_C_TCHAR,
- strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
0, // god knows... don't change this!
0,
- ba.data(),
+ const_cast<char *>(ba.constData()), // don't detach
ba.size(),
ind);
break;
}
- ba = QByteArray ((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR));
r = SQLBindParameter(d->hStmt,
i + 1,
qParamType[bindValueType(i) & QSql::InOut],
SQL_C_TCHAR,
- strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
- strSize,
+ ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ ba.size(),
0,
- const_cast<char *>(ba.constData()),
+ const_cast<char *>(ba.constData()), // don't detach
ba.size(),
ind);
break;
@@ -1983,14 +1992,16 @@ bool QODBCDriver::open(const QString & db,
SQLSMALLINT cb;
QVarLengthArray<SQLTCHAR> connOut(1024);
memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
- r = SQLDriverConnect(d->hDbc,
- NULL,
- toSQLTCHAR(connQStr).data(),
- (SQLSMALLINT)connQStr.length(),
- connOut.data(),
- 1024,
- &cb,
- /*SQL_DRIVER_NOPROMPT*/0);
+ {
+ auto encoded = toSQLTCHAR(connQStr);
+ r = SQLDriverConnect(d->hDbc,
+ nullptr,
+ encoded.data(), SQLSMALLINT(encoded.size()),
+ connOut.data(),
+ 1024,
+ &cb,
+ /*SQL_DRIVER_NOPROMPT*/0);
+ }
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
@@ -2369,17 +2380,15 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
if (tableType.isEmpty())
return tl;
- QString joinedTableTypeString = tableType.join(QLatin1Char(','));
+ {
+ auto joinedTableTypeString = toSQLTCHAR(tableType.join(u','));
- r = SQLTables(hStmt,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- 0,
- toSQLTCHAR(joinedTableTypeString).data(),
- joinedTableTypeString.length() /* characters, not bytes */);
+ r = SQLTables(hStmt,
+ nullptr, 0,
+ nullptr, 0,
+ nullptr, 0,
+ joinedTableTypeString.data(), joinedTableTypeString.size());
+ }
if (r != SQL_SUCCESS)
qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d);
@@ -2453,28 +2462,30 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
SQL_ATTR_CURSOR_TYPE,
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
SQL_IS_UINTEGER);
- r = SQLPrimaryKeys(hStmt,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length() /* in characters, not in bytes */);
+ {
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLPrimaryKeys(hStmt,
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size());
+ }
// if the SQLPrimaryKeys() call does not succeed (e.g the driver
// does not support it) - try an alternative method to get hold of
// the primary index (e.g MS Access and FoxPro)
if (r != SQL_SUCCESS) {
- r = SQLSpecialColumns(hStmt,
- SQL_BEST_ROWID,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length(),
- SQL_SCOPE_CURROW,
- SQL_NULLABLE);
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLSpecialColumns(hStmt,
+ SQL_BEST_ROWID,
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size(),
+ SQL_SCOPE_CURROW,
+ SQL_NULLABLE);
if (r != SQL_SUCCESS) {
qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d);
@@ -2555,15 +2566,17 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const
SQL_ATTR_CURSOR_TYPE,
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
SQL_IS_UINTEGER);
- r = SQLColumns(hStmt,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length(),
- NULL,
- 0);
+ {
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLColumns(hStmt,
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size(),
+ nullptr,
+ 0);
+ }
if (r != SQL_SUCCESS)
qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d);