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
|
//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 };
|
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)>
|
template<typename C, int SIZE = sizeof(SQLTCHAR)>
|
||||||
inline static QString fromSQLTCHAR(const C &input, qsizetype size = -1)
|
inline static QString fromSQLTCHAR(const C &input, qsizetype size = -1)
|
||||||
{
|
{
|
||||||
@ -115,6 +138,7 @@ public:
|
|||||||
DefaultCase defaultCase() const;
|
DefaultCase defaultCase() const;
|
||||||
QString adjustCase(const QString&) const;
|
QString adjustCase(const QString&) const;
|
||||||
QChar quoteChar();
|
QChar quoteChar();
|
||||||
|
SQLRETURN sqlFetchNext(const SqlStmtHandle &hStmt) const;
|
||||||
SQLRETURN sqlFetchNext(SQLHANDLE hStmt) const;
|
SQLRETURN sqlFetchNext(SQLHANDLE hStmt) const;
|
||||||
private:
|
private:
|
||||||
bool isQuoteInitialized = false;
|
bool isQuoteInitialized = false;
|
||||||
@ -679,6 +703,11 @@ QChar QODBCDriverPrivate::quoteChar()
|
|||||||
return quote;
|
return quote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SQLRETURN QODBCDriverPrivate::sqlFetchNext(const SqlStmtHandle &hStmt) const
|
||||||
|
{
|
||||||
|
return sqlFetchNext(hStmt.handle());
|
||||||
|
}
|
||||||
|
|
||||||
SQLRETURN QODBCDriverPrivate::sqlFetchNext(SQLHANDLE hStmt) const
|
SQLRETURN QODBCDriverPrivate::sqlFetchNext(SQLHANDLE hStmt) const
|
||||||
{
|
{
|
||||||
if (hasSQLFetchScroll)
|
if (hasSQLFetchScroll)
|
||||||
@ -2051,11 +2080,8 @@ void QODBCDriverPrivate::checkUnicode()
|
|||||||
unicode = true;
|
unicode = true;
|
||||||
return;
|
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
|
// 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
|
// 'SELECT' statement without 'FROM' but support VALUE(foo) statement like e.g. DB2 or Oracle
|
||||||
const auto statements = {
|
const auto statements = {
|
||||||
@ -2065,21 +2091,21 @@ void QODBCDriverPrivate::checkUnicode()
|
|||||||
};
|
};
|
||||||
for (const auto &statement : statements) {
|
for (const auto &statement : statements) {
|
||||||
auto encoded = toSQLTCHAR(statement);
|
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)
|
if (r == SQL_SUCCESS)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (r == SQL_SUCCESS) {
|
if (r == SQL_SUCCESS) {
|
||||||
r = SQLFetch(hStmt);
|
r = SQLFetch(hStmt.handle());
|
||||||
if (r == SQL_SUCCESS) {
|
if (r == SQL_SUCCESS) {
|
||||||
QVarLengthArray<SQLWCHAR, 10> buffer(10);
|
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) {
|
if (r == SQL_SUCCESS && fromSQLTCHAR(buffer) == "test"_L1) {
|
||||||
unicode = true;
|
unicode = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QODBCDriverPrivate::checkDriver() const
|
bool QODBCDriverPrivate::checkDriver() const
|
||||||
@ -2212,25 +2238,20 @@ void QODBCDriverPrivate::checkHasMultiResults()
|
|||||||
void QODBCDriverPrivate::checkDateTimePrecision()
|
void QODBCDriverPrivate::checkDateTimePrecision()
|
||||||
{
|
{
|
||||||
SQLINTEGER columnSize;
|
SQLINTEGER columnSize;
|
||||||
SQLHANDLE hStmt;
|
SqlStmtHandle hStmt(hDbc);
|
||||||
|
|
||||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
|
if (!hStmt.isValid())
|
||||||
if (r != SQL_SUCCESS) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
r = SQLGetTypeInfo(hStmt, SQL_TIMESTAMP);
|
SQLRETURN r = SQLGetTypeInfo(hStmt.handle(), SQL_TIMESTAMP);
|
||||||
if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
|
if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
|
||||||
r = SQLFetch(hStmt);
|
r = SQLFetch(hStmt.handle());
|
||||||
if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO )
|
if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
|
||||||
{
|
if (SQLGetData(hStmt.handle(), 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS)
|
||||||
if (SQLGetData(hStmt, 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS) {
|
|
||||||
datetimePrecision = (int)columnSize;
|
datetimePrecision = (int)columnSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSqlResult *QODBCDriver::createResult() const
|
QSqlResult *QODBCDriver::createResult() const
|
||||||
{
|
{
|
||||||
@ -2314,16 +2335,13 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
|
|||||||
QStringList tl;
|
QStringList tl;
|
||||||
if (!isOpen())
|
if (!isOpen())
|
||||||
return tl;
|
return tl;
|
||||||
SQLHANDLE hStmt;
|
|
||||||
|
|
||||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
|
SqlStmtHandle hStmt(d->hDbc);
|
||||||
d->hDbc,
|
if (!hStmt.isValid()) {
|
||||||
&hStmt);
|
|
||||||
if (r != SQL_SUCCESS) {
|
|
||||||
qSqlWarning("QODBCDriver::tables: Unable to allocate handle"_L1, d);
|
qSqlWarning("QODBCDriver::tables: Unable to allocate handle"_L1, d);
|
||||||
return tl;
|
return tl;
|
||||||
}
|
}
|
||||||
r = SQLSetStmtAttr(hStmt,
|
SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
|
||||||
SQL_ATTR_CURSOR_TYPE,
|
SQL_ATTR_CURSOR_TYPE,
|
||||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
||||||
SQL_IS_UINTEGER);
|
SQL_IS_UINTEGER);
|
||||||
@ -2340,7 +2358,7 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
|
|||||||
{
|
{
|
||||||
auto joinedTableTypeString = toSQLTCHAR(tableType.join(u','));
|
auto joinedTableTypeString = toSQLTCHAR(tableType.join(u','));
|
||||||
|
|
||||||
r = SQLTables(hStmt,
|
r = SQLTables(hStmt.handle(),
|
||||||
nullptr, 0,
|
nullptr, 0,
|
||||||
nullptr, 0,
|
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) {
|
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
|
||||||
qSqlWarning("QODBCDriver::tables failed to retrieve table/view list: ("_L1
|
qSqlWarning("QODBCDriver::tables failed to retrieve table/view list: ("_L1
|
||||||
+ QString::number(r) + u':',
|
+ QString::number(r) + u':',
|
||||||
hStmt);
|
hStmt.handle());
|
||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (r == SQL_SUCCESS) {
|
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 = 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;
|
return tl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2378,18 +2393,15 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
|
|||||||
bool usingSpecialColumns = false;
|
bool usingSpecialColumns = false;
|
||||||
QSqlRecord rec = record(tablename);
|
QSqlRecord rec = record(tablename);
|
||||||
|
|
||||||
SQLHANDLE hStmt;
|
SqlStmtHandle hStmt(d->hDbc);
|
||||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
|
if (!hStmt.isValid()) {
|
||||||
d->hDbc,
|
|
||||||
&hStmt);
|
|
||||||
if (r != SQL_SUCCESS) {
|
|
||||||
qSqlWarning("QODBCDriver::primaryIndex: Unable to list primary key"_L1, d);
|
qSqlWarning("QODBCDriver::primaryIndex: Unable to list primary key"_L1, d);
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
QString catalog, schema, table;
|
QString catalog, schema, table;
|
||||||
d->splitTableQualifier(tablename, catalog, schema, table);
|
d->splitTableQualifier(tablename, catalog, schema, table);
|
||||||
|
|
||||||
r = SQLSetStmtAttr(hStmt,
|
SQLRETURN r = SQLSetStmtAttr(hStmt.handle(),
|
||||||
SQL_ATTR_CURSOR_TYPE,
|
SQL_ATTR_CURSOR_TYPE,
|
||||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
||||||
SQL_IS_UINTEGER);
|
SQL_IS_UINTEGER);
|
||||||
@ -2397,7 +2409,7 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
|
|||||||
auto c = toSQLTCHAR(catalog);
|
auto c = toSQLTCHAR(catalog);
|
||||||
auto s = toSQLTCHAR(schema);
|
auto s = toSQLTCHAR(schema);
|
||||||
auto t = toSQLTCHAR(table);
|
auto t = toSQLTCHAR(table);
|
||||||
r = SQLPrimaryKeys(hStmt,
|
r = SQLPrimaryKeys(hStmt.handle(),
|
||||||
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
||||||
schema.isEmpty() ? nullptr : s.data(), s.size(),
|
schema.isEmpty() ? nullptr : s.data(), s.size(),
|
||||||
t.data(), t.size());
|
t.data(), t.size());
|
||||||
@ -2410,7 +2422,7 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
|
|||||||
auto c = toSQLTCHAR(catalog);
|
auto c = toSQLTCHAR(catalog);
|
||||||
auto s = toSQLTCHAR(schema);
|
auto s = toSQLTCHAR(schema);
|
||||||
auto t = toSQLTCHAR(table);
|
auto t = toSQLTCHAR(table);
|
||||||
r = SQLSpecialColumns(hStmt,
|
r = SQLSpecialColumns(hStmt.handle(),
|
||||||
SQL_BEST_ROWID,
|
SQL_BEST_ROWID,
|
||||||
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
||||||
schema.isEmpty() ? nullptr : s.data(), s.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
|
// Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
|
||||||
while (r == SQL_SUCCESS) {
|
while (r == SQL_SUCCESS) {
|
||||||
if (usingSpecialColumns) {
|
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
|
idxName = QString::number(fakeId++); // invent a fake index name
|
||||||
} else {
|
} else {
|
||||||
cName = qGetStringData(hStmt, 3, -1, d->unicode).toString(); // column name
|
cName = qGetStringData(hStmt.handle(), 3, -1, d->unicode).toString(); // column name
|
||||||
idxName = qGetStringData(hStmt, 5, -1, d->unicode).toString(); // pk index name
|
idxName = qGetStringData(hStmt.handle(), 5, -1, d->unicode).toString(); // pk index name
|
||||||
}
|
}
|
||||||
index.append(rec.field(cName));
|
index.append(rec.field(cName));
|
||||||
index.setName(idxName);
|
index.setName(idxName);
|
||||||
|
|
||||||
r = d->sqlFetchNext(hStmt);
|
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;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2456,27 +2465,24 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const
|
|||||||
if (!isOpen())
|
if (!isOpen())
|
||||||
return fil;
|
return fil;
|
||||||
|
|
||||||
SQLHANDLE hStmt;
|
SqlStmtHandle hStmt;
|
||||||
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
|
if (!hStmt.isValid()) {
|
||||||
d->hDbc,
|
|
||||||
&hStmt);
|
|
||||||
if (r != SQL_SUCCESS) {
|
|
||||||
qSqlWarning("QODBCDriver::record: Unable to allocate handle"_L1, d);
|
qSqlWarning("QODBCDriver::record: Unable to allocate handle"_L1, d);
|
||||||
return fil;
|
return fil;
|
||||||
}
|
}
|
||||||
r = SQLSetStmtAttr(hStmt,
|
|
||||||
SQL_ATTR_CURSOR_TYPE,
|
|
||||||
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
|
|
||||||
SQL_IS_UINTEGER);
|
|
||||||
|
|
||||||
QString catalog, schema, table;
|
QString catalog, schema, table;
|
||||||
d->splitTableQualifier(tablename, 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 c = toSQLTCHAR(catalog);
|
||||||
auto s = toSQLTCHAR(schema);
|
auto s = toSQLTCHAR(schema);
|
||||||
auto t = toSQLTCHAR(table);
|
auto t = toSQLTCHAR(table);
|
||||||
r = SQLColumns(hStmt,
|
r = SQLColumns(hStmt.handle(),
|
||||||
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
catalog.isEmpty() ? nullptr : c.data(), c.size(),
|
||||||
schema.isEmpty() ? nullptr : s.data(), s.size(),
|
schema.isEmpty() ? nullptr : s.data(), s.size(),
|
||||||
t.data(), t.size(),
|
t.data(), t.size(),
|
||||||
@ -2489,15 +2495,9 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const
|
|||||||
r = d->sqlFetchNext(hStmt);
|
r = d->sqlFetchNext(hStmt);
|
||||||
// Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
|
// Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
|
||||||
while (r == SQL_SUCCESS) {
|
while (r == SQL_SUCCESS) {
|
||||||
|
fil.append(qMakeFieldInfo(hStmt.handle(), d));
|
||||||
fil.append(qMakeFieldInfo(hStmt, d));
|
|
||||||
r = d->sqlFetchNext(hStmt);
|
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;
|
return fil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user