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
66767eea46
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
Pick-to: 6.5 6.4 6.2 5.15
Change-Id: I3696381d0a93af8861ce2b7915f212d9e5e9a243
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
e73389874d
commit
46af1fe49f
@ -745,6 +745,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)
|
||||
{
|
||||
@ -780,10 +788,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() == "SQL_ATTR_CURRENT_CATALOG"_L1) {
|
||||
val.utf16(); // 0 terminate
|
||||
r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
|
||||
toSQLTCHAR(val).data(),
|
||||
SQLINTEGER(val.length() * sizeof(SQLTCHAR)));
|
||||
r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, val);
|
||||
} else if (opt.toUpper() == "SQL_ATTR_METADATA_ID"_L1) {
|
||||
if (val.toUpper() == "SQL_TRUE"_L1) {
|
||||
v = SQL_TRUE;
|
||||
@ -798,10 +803,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() == "SQL_ATTR_TRACEFILE"_L1) {
|
||||
val.utf16(); // 0 terminate
|
||||
r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
|
||||
toSQLTCHAR(val).data(),
|
||||
SQLINTEGER(val.length() * sizeof(SQLTCHAR)));
|
||||
r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, val);
|
||||
} else if (opt.toUpper() == "SQL_ATTR_TRACE"_L1) {
|
||||
if (val.toUpper() == "SQL_OPT_TRACE_OFF"_L1) {
|
||||
v = SQL_OPT_TRACE_OFF;
|
||||
@ -1004,9 +1006,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));
|
||||
@ -1355,9 +1360,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",
|
||||
@ -1385,7 +1393,7 @@ bool QODBCResult::exec()
|
||||
SQLCloseCursor(d->hStmt);
|
||||
|
||||
QVariantList &values = boundValues();
|
||||
QByteArrayList tmpStorage(values.count(), QByteArray()); // holds temporary buffers
|
||||
QByteArrayList tmpStorage(values.count(), QByteArray()); // targets for SQLBindParameter()
|
||||
QVarLengthArray<SQLLEN, 32> indicators(values.count());
|
||||
memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN));
|
||||
|
||||
@ -1600,36 +1608,36 @@ bool QODBCResult::exec()
|
||||
case QMetaType::QString:
|
||||
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);
|
||||
const qsizetype 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(), int(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(reinterpret_cast<const char *>(toSQLTCHAR(str).constData()),
|
||||
int(strSize));
|
||||
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;
|
||||
@ -1991,14 +1999,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));
|
||||
@ -2377,17 +2387,15 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
|
||||
if (tableType.isEmpty())
|
||||
return tl;
|
||||
|
||||
QString joinedTableTypeString = tableType.join(u',');
|
||||
{
|
||||
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("QODBCDriver::tables Unable to execute table list"_L1, d);
|
||||
@ -2460,28 +2468,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("QODBCDriver::primaryIndex: Unable to execute primary key list"_L1, d);
|
||||
@ -2562,15 +2572,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("QODBCDriver::record: Unable to execute column list"_L1, d);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user