--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp +++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp @@ -92,23 +92,39 @@ inline static QString fromSQLTCHAR(const QVarLengthArray& input, qsize return result; } +template +void toSQLTCHARImpl(QVarLengthArray &result, const QString &input); // primary template undefined + +template +void do_append(QVarLengthArray &result, const Container &c) +{ + result.append(reinterpret_cast(c.data()), c.size()); +} + +template <> +void toSQLTCHARImpl<1>(QVarLengthArray &result, const QString &input) +{ + const auto u8 = input.toUtf8(); + do_append(result, u8); +} + +template <> +void toSQLTCHARImpl<2>(QVarLengthArray &result, const QString &input) +{ + do_append(result, input); +} + +template <> +void toSQLTCHARImpl<4>(QVarLengthArray &result, const QString &input) +{ + const auto u32 = input.toUcs4(); + do_append(result, u32); +} + inline static QVarLengthArray toSQLTCHAR(const QString &input) { QVarLengthArray result; - result.resize(input.size()); - switch(sizeof(SQLTCHAR)) { - case 1: - memcpy(result.data(), input.toUtf8().data(), input.size()); - break; - case 2: - memcpy(result.data(), input.unicode(), input.size() * 2); - break; - case 4: - memcpy(result.data(), input.toUcs4().data(), input.size() * 4); - break; - default: - qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", int(sizeof(SQLTCHAR))); - } + toSQLTCHARImpl(result, input); result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't. return result; } --- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp +++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp @@ -1774,10 +1774,11 @@ bool QODBCResult::exec() case QMetaType::QString: if (d->unicode) { if (bindValueType(i) & QSql::Out) { - const QByteArray &first = tmpStorage.at(i); - QVarLengthArray array; - array.append((const SQLTCHAR *)first.constData(), first.size()); - values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR)); + const QByteArray &bytes = tmpStorage.at(i); + const auto strSize = bytes.size() / sizeof(SQLTCHAR); + QVarLengthArray string(strSize); + memcpy(string.data(), bytes.data(), strSize * sizeof(SQLTCHAR)); + values[i] = fromSQLTCHAR(string); } break; } --- 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(), - SQLINTEGER(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(), - SQLINTEGER(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)); @@ -1389,9 +1394,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", @@ -1419,7 +1427,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 indicators(values.count()); memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN)); @@ -1634,36 +1642,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(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 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(ba.constData()), // don't detach ba.size(), ind); break; } - ba = QByteArray(reinterpret_cast(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(ba.constData()), + const_cast(ba.constData()), // don't detach ba.size(), ind); break; @@ -2025,14 +2033,16 @@ bool QODBCDriver::open(const QString & db, SQLSMALLINT cb; QVarLengthArray 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)); @@ -2411,17 +2421,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); @@ -2494,28 +2502,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); @@ -2596,15 +2606,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);