SQL/ODBC: Add helper class SqlStmtHandle
Add RAII SqlStmtHandle helper class to make sure the statement handle is properly cleaned up also on early exit. Change-Id: I7aba4472be1e2991f395eeb7e43f8dd272336694 Reviewed-by: Axel Spoerl <axel.spoerl@qt.io> Reviewed-by: Andy Shaw <andy.shaw@qt.io> (cherry picked from commit 4f4ac705f0f918a133a6ff676180e99307358823) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
3ff5f601b6
commit
874f5c1f46
@ -41,6 +41,29 @@ static constexpr SQLSMALLINT TABLENAMESIZE = 128;
|
||||
//Map Qt parameter types to ODBC types
|
||||
static constexpr SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
|
||||
|
||||
class SqlStmtHandle
|
||||
{
|
||||
public:
|
||||
SqlStmtHandle(SQLHANDLE hDbc = SQL_NULL_HSTMT)
|
||||
{
|
||||
SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &stmtHandle);
|
||||
}
|
||||
~SqlStmtHandle()
|
||||
{
|
||||
if (stmtHandle != SQL_NULL_HSTMT)
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmtHandle);
|
||||
}
|
||||
SQLHANDLE handle() const
|
||||
{
|
||||
return stmtHandle;
|
||||
}
|
||||
bool isValid() const
|
||||
{
|
||||
return stmtHandle != SQL_NULL_HSTMT;
|
||||
}
|
||||
SQLHANDLE stmtHandle = SQL_NULL_HSTMT;
|
||||
};
|
||||
|
||||
template<typename C, int SIZE = sizeof(SQLTCHAR)>
|
||||
inline static QString fromSQLTCHAR(const C &input, qsizetype size = -1)
|
||||
{
|
||||
@ -115,6 +138,7 @@ public:
|
||||
DefaultCase defaultCase() const;
|
||||
QString adjustCase(const QString&) const;
|
||||
QChar quoteChar();
|
||||
SQLRETURN sqlFetchNext(const SqlStmtHandle &hStmt) const;
|
||||
SQLRETURN sqlFetchNext(SQLHANDLE hStmt) const;
|
||||
private:
|
||||
bool isQuoteInitialized = false;
|
||||
@ -679,6 +703,11 @@ QChar QODBCDriverPrivate::quoteChar()
|
||||
return quote;
|
||||
}
|
||||
|
||||
SQLRETURN QODBCDriverPrivate::sqlFetchNext(const SqlStmtHandle &hStmt) const
|
||||
{
|
||||
return sqlFetchNext(hStmt.handle());
|
||||
}
|
||||
|
||||
SQLRETURN QODBCDriverPrivate::sqlFetchNext(SQLHANDLE hStmt) const
|
||||
{
|
||||
if (hasSQLFetchScroll)
|
||||
@ -2051,11 +2080,8 @@ void QODBCDriverPrivate::checkUnicode()
|
||||
unicode = true;
|
||||
return;
|
||||
}
|
||||
SQLHANDLE hStmt;
|
||||
r = SQLAllocHandle(SQL_HANDLE_STMT,
|
||||
hDbc,
|
||||
&hStmt);
|
||||
|
||||
SqlStmtHandle hStmt(hDbc);
|
||||
// for databases which do not return something useful in SQLGetInfo and are picky about a
|
||||
// 'SELECT' statement without 'FROM' but support VALUE(foo) statement like e.g. DB2 or Oracle
|
||||
const auto statements = {
|
||||
@ -2065,21 +2091,21 @@ void QODBCDriverPrivate::checkUnicode()
|
||||
};
|
||||
for (const auto &statement : statements) {
|
||||
auto encoded = toSQLTCHAR(statement);
|
||||
r = SQLExecDirect(hStmt, encoded.data(), SQLINTEGER(encoded.size()));
|
||||
r = SQLExecDirect(hStmt.handle(), encoded.data(), SQLINTEGER(encoded.size()));
|
||||
if (r == SQL_SUCCESS)
|
||||
break;
|
||||
}
|
||||
if (r == SQL_SUCCESS) {
|
||||
r = SQLFetch(hStmt);
|
||||
r = SQLFetch(hStmt.handle());
|
||||
if (r == SQL_SUCCESS) {
|
||||
QVarLengthArray<SQLWCHAR, 10> buffer(10);
|
||||
r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL);
|
||||
r = SQLGetData(hStmt.handle(), 1, SQL_C_WCHAR, buffer.data(),
|
||||
buffer.size() * sizeof(SQLWCHAR), NULL);
|
||||
if (r == SQL_SUCCESS && fromSQLTCHAR(buffer) == "test"_L1) {
|
||||
unicode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
||||
}
|
||||
|
||||
bool QODBCDriverPrivate::checkDriver() const
|
||||
@ -2212,24 +2238,19 @@ void QODBCDriverPrivate::checkHasMultiResults()
|
||||
void QODBCDriverPrivate::checkDateTimePrecision()
|
||||
{
|
||||
SQLINTEGER columnSize;
|
||||
SQLHANDLE hStmt;
|
||||
SqlStmtHandle hStmt(hDbc);
|
||||
|
||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
|
||||
if (r != SQL_SUCCESS) {
|
||||
if (!hStmt.isValid())
|
||||
return;
|
||||
}
|
||||
|
||||
r = SQLGetTypeInfo(hStmt, SQL_TIMESTAMP);
|
||||
SQLRETURN r = SQLGetTypeInfo(hStmt.handle(), SQL_TIMESTAMP);
|
||||
if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
|
||||
r = SQLFetch(hStmt);
|
||||
if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO )
|
||||
{
|
||||
if (SQLGetData(hStmt, 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS) {
|
||||
r = SQLFetch(hStmt.handle());
|
||||
if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
|
||||
if (SQLGetData(hStmt.handle(), 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS)
|
||||
datetimePrecision = (int)columnSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
||||
}
|
||||
|
||||
QSqlResult *QODBCDriver::createResult() const
|
||||
@ -2314,19 +2335,16 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
|
||||
QStringList tl;
|
||||
if (!isOpen())
|
||||
return tl;
|
||||
SQLHANDLE hStmt;
|
||||
|
||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
|
||||
d->hDbc,
|
||||
&hStmt);
|
||||
if (r != SQL_SUCCESS) {
|
||||
SqlStmtHandle hStmt(d->hDbc);
|
||||
if (!hStmt.isValid()) {
|
||||
qSqlWarning("QODBCDriver::tables: Unable to allocate handle"_L1, d);
|
||||
return tl;
|
||||
}
|
||||
r = SQLSetStmtAttr(hStmt,
|
||||
SQL_ATTR_CURSOR_TYPE,
|
||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
||||
SQL_IS_UINTEGER);
|
||||
SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
|
||||
SQL_ATTR_CURSOR_TYPE,
|
||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
||||
SQL_IS_UINTEGER);
|
||||
QStringList tableType;
|
||||
if (type & QSql::Tables)
|
||||
tableType += "TABLE"_L1;
|
||||
@ -2340,7 +2358,7 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
|
||||
{
|
||||
auto joinedTableTypeString = toSQLTCHAR(tableType.join(u','));
|
||||
|
||||
r = SQLTables(hStmt,
|
||||
r = SQLTables(hStmt.handle(),
|
||||
nullptr, 0,
|
||||
nullptr, 0,
|
||||
nullptr, 0,
|
||||
@ -2354,18 +2372,15 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
|
||||
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
|
||||
qSqlWarning("QODBCDriver::tables failed to retrieve table/view list: ("_L1
|
||||
+ QString::number(r) + u':',
|
||||
hStmt);
|
||||
hStmt.handle());
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
while (r == SQL_SUCCESS) {
|
||||
tl.append(qGetStringData(hStmt, 2, -1, d->unicode).toString());
|
||||
tl.append(qGetStringData(hStmt.handle(), 2, -1, d->unicode).toString());
|
||||
r = d->sqlFetchNext(hStmt);
|
||||
}
|
||||
|
||||
r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
||||
if (r!= SQL_SUCCESS)
|
||||
qSqlWarning("QODBCDriver: Unable to free statement handle"_L1 + QString::number(r), d);
|
||||
return tl;
|
||||
}
|
||||
|
||||
@ -2378,26 +2393,23 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
|
||||
bool usingSpecialColumns = false;
|
||||
QSqlRecord rec = record(tablename);
|
||||
|
||||
SQLHANDLE hStmt;
|
||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
|
||||
d->hDbc,
|
||||
&hStmt);
|
||||
if (r != SQL_SUCCESS) {
|
||||
SqlStmtHandle hStmt(d->hDbc);
|
||||
if (!hStmt.isValid()) {
|
||||
qSqlWarning("QODBCDriver::primaryIndex: Unable to list primary key"_L1, d);
|
||||
return index;
|
||||
}
|
||||
QString catalog, schema, table;
|
||||
d->splitTableQualifier(tablename, catalog, schema, table);
|
||||
|
||||
r = SQLSetStmtAttr(hStmt,
|
||||
SQL_ATTR_CURSOR_TYPE,
|
||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
||||
SQL_IS_UINTEGER);
|
||||
SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
|
||||
SQL_ATTR_CURSOR_TYPE,
|
||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
||||
SQL_IS_UINTEGER);
|
||||
{
|
||||
auto c = toSQLTCHAR(catalog);
|
||||
auto s = toSQLTCHAR(schema);
|
||||
auto t = toSQLTCHAR(table);
|
||||
r = SQLPrimaryKeys(hStmt,
|
||||
r = SQLPrimaryKeys(hStmt.handle(),
|
||||
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
||||
schema.isEmpty() ? nullptr : s.data(), s.size(),
|
||||
t.data(), t.size());
|
||||
@ -2410,7 +2422,7 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
|
||||
auto c = toSQLTCHAR(catalog);
|
||||
auto s = toSQLTCHAR(schema);
|
||||
auto t = toSQLTCHAR(table);
|
||||
r = SQLSpecialColumns(hStmt,
|
||||
r = SQLSpecialColumns(hStmt.handle(),
|
||||
SQL_BEST_ROWID,
|
||||
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
||||
schema.isEmpty() ? nullptr : s.data(), s.size(),
|
||||
@ -2432,20 +2444,17 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
|
||||
// Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
|
||||
while (r == SQL_SUCCESS) {
|
||||
if (usingSpecialColumns) {
|
||||
cName = qGetStringData(hStmt, 1, -1, d->unicode).toString(); // column name
|
||||
cName = qGetStringData(hStmt.handle(), 1, -1, d->unicode).toString(); // column name
|
||||
idxName = QString::number(fakeId++); // invent a fake index name
|
||||
} else {
|
||||
cName = qGetStringData(hStmt, 3, -1, d->unicode).toString(); // column name
|
||||
idxName = qGetStringData(hStmt, 5, -1, d->unicode).toString(); // pk index name
|
||||
cName = qGetStringData(hStmt.handle(), 3, -1, d->unicode).toString(); // column name
|
||||
idxName = qGetStringData(hStmt.handle(), 5, -1, d->unicode).toString(); // pk index name
|
||||
}
|
||||
index.append(rec.field(cName));
|
||||
index.setName(idxName);
|
||||
|
||||
r = d->sqlFetchNext(hStmt);
|
||||
}
|
||||
r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
||||
if (r!= SQL_SUCCESS)
|
||||
qSqlWarning("QODBCDriver: Unable to free statement handle"_L1 + QString::number(r), d);
|
||||
return index;
|
||||
}
|
||||
|
||||
@ -2456,27 +2465,24 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const
|
||||
if (!isOpen())
|
||||
return fil;
|
||||
|
||||
SQLHANDLE hStmt;
|
||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
|
||||
d->hDbc,
|
||||
&hStmt);
|
||||
if (r != SQL_SUCCESS) {
|
||||
SqlStmtHandle hStmt;
|
||||
if (!hStmt.isValid()) {
|
||||
qSqlWarning("QODBCDriver::record: Unable to allocate handle"_L1, d);
|
||||
return fil;
|
||||
}
|
||||
r = SQLSetStmtAttr(hStmt,
|
||||
SQL_ATTR_CURSOR_TYPE,
|
||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
||||
SQL_IS_UINTEGER);
|
||||
|
||||
QString catalog, schema, table;
|
||||
d->splitTableQualifier(tablename, catalog, schema, table);
|
||||
|
||||
SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
|
||||
SQL_ATTR_CURSOR_TYPE,
|
||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
||||
SQL_IS_UINTEGER);
|
||||
{
|
||||
auto c = toSQLTCHAR(catalog);
|
||||
auto s = toSQLTCHAR(schema);
|
||||
auto t = toSQLTCHAR(table);
|
||||
r = SQLColumns(hStmt,
|
||||
r = SQLColumns(hStmt.handle(),
|
||||
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
||||
schema.isEmpty() ? nullptr : s.data(), s.size(),
|
||||
t.data(), t.size(),
|
||||
@ -2489,15 +2495,9 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const
|
||||
r = d->sqlFetchNext(hStmt);
|
||||
// Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
|
||||
while (r == SQL_SUCCESS) {
|
||||
|
||||
fil.append(qMakeFieldInfo(hStmt, d));
|
||||
fil.append(qMakeFieldInfo(hStmt.handle(), d));
|
||||
r = d->sqlFetchNext(hStmt);
|
||||
}
|
||||
|
||||
r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
||||
if (r!= SQL_SUCCESS)
|
||||
qSqlWarning("QODBCDriver: Unable to free statement handle "_L1 + QString::number(r), d);
|
||||
|
||||
return fil;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user