OCI: allow accessing results of PL/SQL procedure calls returning a REF CURSOR

Move the QOCIResult implementation of QSqlCachedResult into the private
qsql_oci_p.h header, and register it as a meta type that we can pass
through QVariant. Add a field that indicates whether that results
represents a cursor variable, and bind it to the statement handle if so.

Fixes: QTBUG-166
Fixes: QTBUG-44643
Change-Id: Iafbf5474ad7efc6d24eb52a5c5a1b3d2b6842387
Reviewed-by: Andy Shaw <andy.shaw@qt.io>
This commit is contained in:
Volker Hilsheimer 2024-04-09 14:17:52 +02:00
parent 334d4c7b69
commit 82681fd8a2
2 changed files with 83 additions and 48 deletions

View File

@ -45,9 +45,11 @@
//#define QOCI_DEBUG //#define QOCI_DEBUG
Q_DECLARE_OPAQUE_POINTER(OCIEnv*); Q_DECLARE_OPAQUE_POINTER(QOCIResult*)
Q_DECLARE_METATYPE(QOCIResult*)
Q_DECLARE_OPAQUE_POINTER(OCIEnv*)
Q_DECLARE_METATYPE(OCIEnv*) Q_DECLARE_METATYPE(OCIEnv*)
Q_DECLARE_OPAQUE_POINTER(OCIStmt*); Q_DECLARE_OPAQUE_POINTER(OCIStmt*)
Q_DECLARE_METATYPE(OCIStmt*) Q_DECLARE_METATYPE(OCIStmt*)
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -203,31 +205,6 @@ public:
}; };
class QOCICols; class QOCICols;
class QOCIResultPrivate;
class QOCIResult: public QSqlCachedResult
{
Q_DECLARE_PRIVATE(QOCIResult)
friend class QOCIDriver;
friend class QOCICols;
public:
QOCIResult(const QOCIDriver *db);
~QOCIResult();
bool prepare(const QString &query) override;
bool exec() override;
QVariant handle() const override;
protected:
bool gotoNext(ValueCache &values, int index) override;
bool reset(const QString &query) override;
int size() override;
int numRowsAffected() override;
QSqlRecord record() const override;
QVariant lastInsertId() const override;
bool execBatch(bool arrayBind = false) override;
void virtual_hook(int id, void *data) override;
bool fetchNext() override;
};
class QOCIResultPrivate: public QSqlCachedResultPrivate class QOCIResultPrivate: public QSqlCachedResultPrivate
{ {
@ -435,6 +412,18 @@ int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, in
const_cast<OCIRowid **>(&rptr->id), const_cast<OCIRowid **>(&rptr->id),
-1, -1,
SQLT_RDD, indPtr, 0, 0, 0, 0, OCI_DEFAULT); SQLT_RDD, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
} else if (val.canConvert<QOCIResult *>() && isOutValue(pos)) {
QOCIResult *res = qvariant_cast<QOCIResult *>(val);
if (res->internal_prepare()) {
r = OCIBindByPos(sql, hbnd, err,
pos + 1,
const_cast<OCIStmt **>(&res->d->sql),
(sb4)0,
SQLT_RSET, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
res->isCursor = true;
}
} else { } else {
qCWarning(lcOci, "Unknown bind variable"); qCWarning(lcOci, "Unknown bind variable");
r = OCI_ERROR; r = OCI_ERROR;
@ -1831,6 +1820,7 @@ QOCIResultPrivate::~QOCIResultPrivate()
QOCIResult::QOCIResult(const QOCIDriver *db) QOCIResult::QOCIResult(const QOCIDriver *db)
: QSqlCachedResult(*new QOCIResultPrivate(this, db)) : QSqlCachedResult(*new QOCIResultPrivate(this, db))
{ {
isCursor = false;
} }
QOCIResult::~QOCIResult() QOCIResult::~QOCIResult()
@ -1923,11 +1913,12 @@ int QOCIResult::numRowsAffected()
return rowCount; return rowCount;
} }
bool QOCIResult::prepare(const QString& query) +bool QOCIResult::internal_prepare()
{ {
Q_D(QOCIResult); Q_D(QOCIResult);
int r = 0; int r = 0;
QSqlResult::prepare(query); QString noStr;
QSqlResult::prepare(noStr);
delete d->cols; delete d->cols;
d->cols = nullptr; d->cols = nullptr;
@ -1940,8 +1931,7 @@ bool QOCIResult::prepare(const QString& query)
else else
qOraWarning("QOCIResult::prepare: unable to free statement handle:", d->err); qOraWarning("QOCIResult::prepare: unable to free statement handle:", d->err);
} }
if (query.isEmpty())
return false;
r = OCIHandleAlloc(d->env, r = OCIHandleAlloc(d->env,
reinterpret_cast<void **>(&d->sql), reinterpret_cast<void **>(&d->sql),
OCI_HTYPE_STMT, OCI_HTYPE_STMT,
@ -1953,6 +1943,19 @@ bool QOCIResult::prepare(const QString& query)
return false; return false;
} }
d->setStatementAttributes(); d->setStatementAttributes();
return true;
}
bool QOCIResult::prepare(const QString& query)
{
if (query.isEmpty())
return false;
if (!internal_prepare())
return false;
int r;
const OraText *txt = reinterpret_cast<const OraText *>(query.utf16()); const OraText *txt = reinterpret_cast<const OraText *>(query.utf16());
const int len = query.length() * sizeof(QChar); const int len = query.length() * sizeof(QChar);
r = OCIStmtPrepare(d->sql, r = OCIStmtPrepare(d->sql,
@ -2013,23 +2016,25 @@ bool QOCIResult::exec()
return false; return false;
} }
// execute if (!isCursor()) {
r = OCIStmtExecute(d->svc, // execute
d->sql, r = OCIStmtExecute(d->svc,
d->err, d->sql,
iters, d->err,
0, iters,
0, 0,
0, 0,
mode); 0,
if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { mode);
qOraWarning("QOCIResult::exec: unable to execute statement:", d->err); if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
setLastError(qMakeError(QCoreApplication::translate("QOCIResult", qOraWarning("QOCIResult::exec: unable to execute statement:", d->err);
"Unable to execute statement"), QSqlError::StatementError, d->err)); setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
#ifdef QOCI_DEBUG "Unable to execute statement"), QSqlError::StatementError, d->err));
qCDebug(lcOci) << "lastQuery()" << lastQuery(); #ifdef QOCI_DEBUG
#endif qCDebug(lcOci) << "lastQuery()" << lastQuery();
return false; #endif
return false;
}
} }
if (stmtType == OCI_STMT_SELECT) { if (stmtType == OCI_STMT_SELECT) {

View File

@ -16,6 +16,7 @@
// //
#include <QtSql/qsqldriver.h> #include <QtSql/qsqldriver.h>
#include <QtSql/private/qsqlcachedresult_p.h>
#ifdef QT_PLUGIN #ifdef QT_PLUGIN
#define Q_EXPORT_SQLDRIVER_OCI #define Q_EXPORT_SQLDRIVER_OCI
@ -25,6 +26,7 @@
typedef struct OCIEnv OCIEnv; typedef struct OCIEnv OCIEnv;
typedef struct OCISvcCtx OCISvcCtx; typedef struct OCISvcCtx OCISvcCtx;
struct QOCIResultPrivate;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -66,6 +68,34 @@ protected:
bool rollbackTransaction() override; bool rollbackTransaction() override;
}; };
class Q_EXPORT_SQLDRIVER_OCI QOCIResult : public QSqlCachedResult
{
friend class QOCIDriver;
friend struct QOCIResultPrivate;
friend class QOCICols;
public:
QOCIResult(const QOCIDriver * db, const QOCIDriverPrivate* p);
~QOCIResult();
bool prepare(const QString& query);
bool exec();
QVariant handle() const;
protected:
bool gotoNext(ValueCache &values, int index);
bool reset (const QString& query);
int size();
int numRowsAffected();
QSqlRecord record() const;
QVariant lastInsertId() const;
bool execBatch(bool arrayBind = false);
void virtual_hook(int id, void *data);
bool isCursor;
bool internal_prepare();
private:
QOCIResultPrivate *d;
};
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QSQL_OCI_H #endif // QSQL_OCI_H