diff options
Diffstat (limited to 'src/sql/drivers/oci')
-rw-r--r-- | src/sql/drivers/oci/qsql_oci.cpp | 2725 | ||||
-rw-r--r-- | src/sql/drivers/oci/qsql_oci.pri | 9 | ||||
-rw-r--r-- | src/sql/drivers/oci/qsql_oci_p.h | 106 |
3 files changed, 0 insertions, 2840 deletions
diff --git a/src/sql/drivers/oci/qsql_oci.cpp b/src/sql/drivers/oci/qsql_oci.cpp deleted file mode 100644 index 47d6db7ea4..0000000000 --- a/src/sql/drivers/oci/qsql_oci.cpp +++ /dev/null @@ -1,2725 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtSql module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsql_oci_p.h" - -#include <qcoreapplication.h> -#include <qvariant.h> -#include <qdatetime.h> -#include <qmetatype.h> -#include <qregexp.h> -#include <qshareddata.h> -#include <qsqlerror.h> -#include <qsqlfield.h> -#include <qsqlindex.h> -#include <qsqlquery.h> -#include <QtSql/private/qsqlcachedresult_p.h> -#include <QtSql/private/qsqldriver_p.h> -#include <qstringlist.h> -#include <qvarlengtharray.h> -#include <qvector.h> -#include <qdebug.h> - -// This is needed for oracle oci when compiling with mingw-w64 headers -#if defined(__MINGW64_VERSION_MAJOR) && defined(_WIN64) -#define _int64 __int64 -#endif - - -#include <oci.h> -#ifdef max -#undef max -#endif -#ifdef min -#undef min -#endif - -#include <stdlib.h> - -#define QOCI_DYNAMIC_CHUNK_SIZE 65535 -#define QOCI_PREFETCH_MEM 10240 - -// setting this define will allow using a query from a different -// thread than its database connection. -// warning - this is not fully tested and can lead to race conditions -#define QOCI_THREADED - -//#define QOCI_DEBUG - -Q_DECLARE_OPAQUE_POINTER(OCIEnv*); -Q_DECLARE_METATYPE(OCIEnv*) -Q_DECLARE_OPAQUE_POINTER(OCIStmt*); -Q_DECLARE_METATYPE(OCIStmt*) - -QT_BEGIN_NAMESPACE - -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN -enum { QOCIEncoding = 2002 }; // AL16UTF16LE -#else -enum { QOCIEncoding = 2000 }; // AL16UTF16 -#endif - -#ifdef OCI_ATTR_CHARSET_FORM -// Always set the OCI_ATTR_CHARSET_FORM to SQLCS_NCHAR is safe -// because Oracle server will deal with the implicit Conversion -// Between CHAR and NCHAR. -// see: http://download.oracle.com/docs/cd/A91202_01/901_doc/appdev.901/a89857/oci05bnd.htm#422705 -static const ub1 qOraCharsetForm = SQLCS_NCHAR; -#endif - -#if defined (OCI_UTF16ID) -static const ub2 qOraCharset = OCI_UTF16ID; -#else -static const ub2 qOraCharset = OCI_UCS2ID; -#endif - -typedef QVarLengthArray<sb2, 32> IndicatorArray; -typedef QVarLengthArray<ub2, 32> SizeArray; - -static QByteArray qMakeOraDate(const QDateTime& dt); -static QDateTime qMakeDate(const char* oraDate); - -static QByteArray qMakeOCINumber(const qlonglong &ll, OCIError *err); -static QByteArray qMakeOCINumber(const qulonglong& ull, OCIError* err); - -static qlonglong qMakeLongLong(const char* ociNumber, OCIError* err); -static qulonglong qMakeULongLong(const char* ociNumber, OCIError* err); - -static QString qOraWarn(OCIError *err, int *errorCode = 0); - -#ifndef Q_CC_SUN -static // for some reason, Sun CC can't use qOraWarning when it's declared static -#endif -void qOraWarning(const char* msg, OCIError *err); -static QSqlError qMakeError(const QString& errString, QSqlError::ErrorType type, OCIError *err); - - - -class QOCIRowId: public QSharedData -{ -public: - QOCIRowId(OCIEnv *env); - ~QOCIRowId(); - - OCIRowid *id; - -private: - QOCIRowId(const QOCIRowId &other): QSharedData(other) { Q_ASSERT(false); } -}; - -QOCIRowId::QOCIRowId(OCIEnv *env) - : id(0) -{ - OCIDescriptorAlloc (env, reinterpret_cast<dvoid **>(&id), - OCI_DTYPE_ROWID, 0, 0); -} - -QOCIRowId::~QOCIRowId() -{ - if (id) - OCIDescriptorFree(id, OCI_DTYPE_ROWID); -} - -typedef QSharedDataPointer<QOCIRowId> QOCIRowIdPointer; -QT_BEGIN_INCLUDE_NAMESPACE -Q_DECLARE_METATYPE(QOCIRowIdPointer) -QT_END_INCLUDE_NAMESPACE - -class QOCIDriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QOCIDriver) - -public: - QOCIDriverPrivate(); - - OCIEnv *env; - OCISvcCtx *svc; - OCIServer *srvhp; - OCISession *authp; - OCIError *err; - bool transaction; - int serverVersion; - int prefetchRows; - int prefetchMem; - QString user; - - void allocErrorHandle(); -}; - -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) Q_DECL_OVERRIDE; - bool exec() Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - -protected: - bool gotoNext(ValueCache &values, int index) Q_DECL_OVERRIDE; - bool reset(const QString &query) Q_DECL_OVERRIDE; - int size() Q_DECL_OVERRIDE; - int numRowsAffected() Q_DECL_OVERRIDE; - QSqlRecord record() const Q_DECL_OVERRIDE; - QVariant lastInsertId() const Q_DECL_OVERRIDE; - bool execBatch(bool arrayBind = false) Q_DECL_OVERRIDE; - void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; -}; - -class QOCIResultPrivate: public QSqlCachedResultPrivate -{ -public: - Q_DECLARE_PUBLIC(QOCIResult) - Q_DECLARE_SQLDRIVER_PRIVATE(QOCIDriver) - QOCIResultPrivate(QOCIResult *q, const QOCIDriver *drv); - ~QOCIResultPrivate(); - - QOCICols *cols; - OCIEnv *env; - OCIError *err; - OCISvcCtx *&svc; - OCIStmt *sql; - bool transaction; - int serverVersion; - int prefetchRows, prefetchMem; - - void setStatementAttributes(); - int bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos, - const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList<QByteArray> &tmpStorage); - int bindValues(QVector<QVariant> &values, IndicatorArray &indicators, SizeArray &tmpSizes, - QList<QByteArray> &tmpStorage); - void outValues(QVector<QVariant> &values, IndicatorArray &indicators, - QList<QByteArray> &tmpStorage); - inline bool isOutValue(int i) const - { Q_Q(const QOCIResult); return q->bindValueType(i) & QSql::Out; } - inline bool isBinaryValue(int i) const - { Q_Q(const QOCIResult); return q->bindValueType(i) & QSql::Binary; } - - void setCharset(dvoid* handle, ub4 type) const - { - int r = 0; - Q_ASSERT(handle); - -#ifdef OCI_ATTR_CHARSET_FORM - r = OCIAttrSet(handle, - type, - // this const cast is safe since OCI doesn't touch - // the charset. - const_cast<void *>(static_cast<const void *>(&qOraCharsetForm)), - 0, - OCI_ATTR_CHARSET_FORM, - //Strange Oracle bug: some Oracle servers crash the server process with non-zero error handle (mostly for 10g). - //So ignore the error message here. - 0); - #ifdef QOCI_DEBUG - if (r != 0) - qWarning("QOCIResultPrivate::setCharset: Couldn't set OCI_ATTR_CHARSET_FORM."); - #endif -#endif - - r = OCIAttrSet(handle, - type, - // this const cast is safe since OCI doesn't touch - // the charset. - const_cast<void *>(static_cast<const void *>(&qOraCharset)), - 0, - OCI_ATTR_CHARSET_ID, - err); - if (r != 0) - qOraWarning("QOCIResultPrivate::setCharsetI Couldn't set OCI_ATTR_CHARSET_ID: ", err); - - } -}; - -void QOCIResultPrivate::setStatementAttributes() -{ - Q_ASSERT(sql); - - int r = 0; - - if (prefetchRows >= 0) { - r = OCIAttrSet(sql, - OCI_HTYPE_STMT, - &prefetchRows, - 0, - OCI_ATTR_PREFETCH_ROWS, - err); - if (r != 0) - qOraWarning("QOCIResultPrivate::setStatementAttributes:" - " Couldn't set OCI_ATTR_PREFETCH_ROWS: ", err); - } - if (prefetchMem >= 0) { - r = OCIAttrSet(sql, - OCI_HTYPE_STMT, - &prefetchMem, - 0, - OCI_ATTR_PREFETCH_MEMORY, - err); - if (r != 0) - qOraWarning("QOCIResultPrivate::setStatementAttributes:" - " Couldn't set OCI_ATTR_PREFETCH_MEMORY: ", err); - } -} - -int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos, - const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList<QByteArray> &tmpStorage) -{ - int r = OCI_SUCCESS; - void *data = const_cast<void *>(val.constData()); - - switch (val.type()) { - case QVariant::ByteArray: - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - isOutValue(pos) - ? const_cast<char *>(reinterpret_cast<QByteArray *>(data)->constData()) - : reinterpret_cast<QByteArray *>(data)->data(), - reinterpret_cast<QByteArray *>(data)->size(), - SQLT_BIN, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - case QVariant::Time: - case QVariant::Date: - case QVariant::DateTime: { - QByteArray ba = qMakeOraDate(val.toDateTime()); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.size(), - SQLT_DAT, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - tmpStorage.append(ba); - break; } - case QVariant::Int: - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // if it's an out value, the data is already detached - // so the const cast is safe. - const_cast<void *>(data), - sizeof(int), - SQLT_INT, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - case QVariant::UInt: - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // if it's an out value, the data is already detached - // so the const cast is safe. - const_cast<void *>(data), - sizeof(uint), - SQLT_UIN, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - case QVariant::LongLong: - { - QByteArray ba = qMakeOCINumber(val.toLongLong(), err); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.size(), - SQLT_VNU, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - tmpStorage.append(ba); - break; - } - case QVariant::ULongLong: - { - QByteArray ba = qMakeOCINumber(val.toULongLong(), err); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.size(), - SQLT_VNU, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - tmpStorage.append(ba); - break; - } - case QVariant::Double: - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // if it's an out value, the data is already detached - // so the const cast is safe. - const_cast<void *>(data), - sizeof(double), - SQLT_FLT, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - case QVariant::UserType: - if (val.canConvert<QOCIRowIdPointer>() && !isOutValue(pos)) { - // use a const pointer to prevent a detach - const QOCIRowIdPointer rptr = qvariant_cast<QOCIRowIdPointer>(val); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // it's an IN value, so const_cast is ok - const_cast<OCIRowid **>(&rptr->id), - -1, - SQLT_RDD, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - } else { - qWarning("Unknown bind variable"); - r = OCI_ERROR; - } - break; - case QVariant::String: { - const QString s = val.toString(); - if (isBinaryValue(pos)) { - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - const_cast<ushort *>(s.utf16()), - s.length() * sizeof(QChar), - SQLT_LNG, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - } else if (!isOutValue(pos)) { - // don't detach the string - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // safe since oracle doesn't touch OUT values - const_cast<ushort *>(s.utf16()), - (s.length() + 1) * sizeof(QChar), - SQLT_STR, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - if (r == OCI_SUCCESS) - setCharset(*hbnd, OCI_HTYPE_BIND); - break; - } - } // fall through for OUT values - default: { - const QString s = val.toString(); - // create a deep-copy - QByteArray ba(reinterpret_cast<const char *>(s.utf16()), (s.length() + 1) * sizeof(QChar)); - if (isOutValue(pos)) { - ba.reserve((s.capacity() + 1) * sizeof(QChar)); - *tmpSize = ba.size(); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.capacity(), - SQLT_STR, indPtr, tmpSize, 0, 0, 0, OCI_DEFAULT); - } else { - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.size(), - SQLT_STR, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - } - if (r == OCI_SUCCESS) - setCharset(*hbnd, OCI_HTYPE_BIND); - tmpStorage.append(ba); - break; - } // default case - } // switch - if (r != OCI_SUCCESS) - qOraWarning("QOCIResultPrivate::bindValue:", err); - return r; -} - -int QOCIResultPrivate::bindValues(QVector<QVariant> &values, IndicatorArray &indicators, - SizeArray &tmpSizes, QList<QByteArray> &tmpStorage) -{ - int r = OCI_SUCCESS; - for (int i = 0; i < values.count(); ++i) { - if (isOutValue(i)) - values[i].detach(); - const QVariant &val = values.at(i); - - OCIBind * hbnd = 0; // Oracle handles these automatically - sb2 *indPtr = &indicators[i]; - *indPtr = val.isNull() ? -1 : 0; - - bindValue(sql, &hbnd, err, i, val, indPtr, &tmpSizes[i], tmpStorage); - } - return r; -} - -// will assign out value and remove its temp storage. -static void qOraOutValue(QVariant &value, QList<QByteArray> &storage, OCIError* err) -{ - switch (value.type()) { - case QVariant::Time: - value = qMakeDate(storage.takeFirst()).time(); - break; - case QVariant::Date: - value = qMakeDate(storage.takeFirst()).date(); - break; - case QVariant::DateTime: - value = qMakeDate(storage.takeFirst()); - break; - case QVariant::LongLong: - value = qMakeLongLong(storage.takeFirst(), err); - break; - case QVariant::ULongLong: - value = qMakeULongLong(storage.takeFirst(), err); - break; - case QVariant::String: - value = QString( - reinterpret_cast<const QChar *>(storage.takeFirst().constData())); - break; - default: - break; //nothing - } -} - -void QOCIResultPrivate::outValues(QVector<QVariant> &values, IndicatorArray &indicators, - QList<QByteArray> &tmpStorage) -{ - for (int i = 0; i < values.count(); ++i) { - - if (!isOutValue(i)) - continue; - - qOraOutValue(values[i], tmpStorage, err); - - QVariant::Type typ = values.at(i).type(); - if (indicators[i] == -1) // NULL - values[i] = QVariant(typ); - else - values[i] = QVariant(typ, values.at(i).constData()); - } -} - - -QOCIDriverPrivate::QOCIDriverPrivate() - : QSqlDriverPrivate(), env(0), svc(0), srvhp(0), authp(0), err(0), transaction(false), - serverVersion(-1), prefetchRows(-1), prefetchMem(QOCI_PREFETCH_MEM) -{ - dbmsType = QSqlDriver::Oracle; -} - -void QOCIDriverPrivate::allocErrorHandle() -{ - int r = OCIHandleAlloc(env, - reinterpret_cast<void **>(&err), - OCI_HTYPE_ERROR, - 0, - 0); - if (r != 0) - qWarning("QOCIDriver: unable to allocate error handle"); -} - -struct OraFieldInfo -{ - QString name; - QVariant::Type type; - ub1 oraIsNull; - ub4 oraType; - sb1 oraScale; - ub4 oraLength; // size in bytes - ub4 oraFieldLength; // amount of characters - sb2 oraPrecision; -}; - -QString qOraWarn(OCIError *err, int *errorCode) -{ - sb4 errcode; - text errbuf[1024]; - errbuf[0] = 0; - errbuf[1] = 0; - - OCIErrorGet(err, - 1, - 0, - &errcode, - errbuf, - sizeof(errbuf), - OCI_HTYPE_ERROR); - if (errorCode) - *errorCode = errcode; - return QString(reinterpret_cast<const QChar *>(errbuf)); -} - -void qOraWarning(const char* msg, OCIError *err) -{ -#ifdef QOCI_DEBUG - qWarning("%s %s", msg, qPrintable(qOraWarn(err))); -#else - Q_UNUSED(msg); - Q_UNUSED(err); -#endif -} - -static int qOraErrorNumber(OCIError *err) -{ - sb4 errcode; - OCIErrorGet(err, - 1, - 0, - &errcode, - 0, - 0, - OCI_HTYPE_ERROR); - return errcode; -} - -QSqlError qMakeError(const QString& errString, QSqlError::ErrorType type, OCIError *err) -{ - int errorCode = 0; - const QString oraErrorString = qOraWarn(err, &errorCode); - return QSqlError(errString, oraErrorString, type, errorCode); -} - -QVariant::Type qDecodeOCIType(const QString& ocitype, QSql::NumericalPrecisionPolicy precisionPolicy) -{ - QVariant::Type type = QVariant::Invalid; - if (ocitype == QLatin1String("VARCHAR2") || ocitype == QLatin1String("VARCHAR") - || ocitype.startsWith(QLatin1String("INTERVAL")) - || ocitype == QLatin1String("CHAR") || ocitype == QLatin1String("NVARCHAR2") - || ocitype == QLatin1String("NCHAR")) - type = QVariant::String; - else if (ocitype == QLatin1String("NUMBER") - || ocitype == QLatin1String("FLOAT") - || ocitype == QLatin1String("BINARY_FLOAT") - || ocitype == QLatin1String("BINARY_DOUBLE")) { - switch(precisionPolicy) { - case QSql::LowPrecisionInt32: - type = QVariant::Int; - break; - case QSql::LowPrecisionInt64: - type = QVariant::LongLong; - break; - case QSql::LowPrecisionDouble: - type = QVariant::Double; - break; - case QSql::HighPrecision: - default: - type = QVariant::String; - break; - } - } - else if (ocitype == QLatin1String("LONG") || ocitype == QLatin1String("NCLOB") - || ocitype == QLatin1String("CLOB")) - type = QVariant::ByteArray; - else if (ocitype == QLatin1String("RAW") || ocitype == QLatin1String("LONG RAW") - || ocitype == QLatin1String("ROWID") || ocitype == QLatin1String("BLOB") - || ocitype == QLatin1String("CFILE") || ocitype == QLatin1String("BFILE")) - type = QVariant::ByteArray; - else if (ocitype == QLatin1String("DATE") || ocitype.startsWith(QLatin1String("TIME"))) - type = QVariant::DateTime; - else if (ocitype == QLatin1String("UNDEFINED")) - type = QVariant::Invalid; - if (type == QVariant::Invalid) - qWarning("qDecodeOCIType: unknown type: %s", ocitype.toLocal8Bit().constData()); - return type; -} - -QVariant::Type qDecodeOCIType(int ocitype, QSql::NumericalPrecisionPolicy precisionPolicy) -{ - QVariant::Type type = QVariant::Invalid; - switch (ocitype) { - case SQLT_STR: - case SQLT_VST: - case SQLT_CHR: - case SQLT_AFC: - case SQLT_VCS: - case SQLT_AVC: - case SQLT_RDD: - case SQLT_LNG: -#ifdef SQLT_INTERVAL_YM - case SQLT_INTERVAL_YM: -#endif -#ifdef SQLT_INTERVAL_DS - case SQLT_INTERVAL_DS: -#endif - type = QVariant::String; - break; - case SQLT_INT: - type = QVariant::Int; - break; - case SQLT_FLT: - case SQLT_NUM: - case SQLT_VNU: - case SQLT_UIN: - switch(precisionPolicy) { - case QSql::LowPrecisionInt32: - type = QVariant::Int; - break; - case QSql::LowPrecisionInt64: - type = QVariant::LongLong; - break; - case QSql::LowPrecisionDouble: - type = QVariant::Double; - break; - case QSql::HighPrecision: - default: - type = QVariant::String; - break; - } - break; - case SQLT_VBI: - case SQLT_BIN: - case SQLT_LBI: - case SQLT_LVC: - case SQLT_LVB: - case SQLT_BLOB: - case SQLT_CLOB: - case SQLT_FILE: - case SQLT_NTY: - case SQLT_REF: - case SQLT_RID: - type = QVariant::ByteArray; - break; - case SQLT_DAT: - case SQLT_ODT: -#ifdef SQLT_TIMESTAMP - case SQLT_TIMESTAMP: - case SQLT_TIMESTAMP_TZ: - case SQLT_TIMESTAMP_LTZ: -#endif - type = QVariant::DateTime; - break; - default: - type = QVariant::Invalid; - qWarning("qDecodeOCIType: unknown OCI datatype: %d", ocitype); - break; - } - return type; -} - -static QSqlField qFromOraInf(const OraFieldInfo &ofi) -{ - QSqlField f(ofi.name, ofi.type); - f.setRequired(ofi.oraIsNull == 0); - - if (ofi.type == QVariant::String && ofi.oraType != SQLT_NUM && ofi.oraType != SQLT_VNU) - f.setLength(ofi.oraFieldLength); - else - f.setLength(ofi.oraPrecision == 0 ? 38 : int(ofi.oraPrecision)); - - f.setPrecision(ofi.oraScale); - f.setSqlType(int(ofi.oraType)); - return f; -} - -/*! - \internal - - Convert QDateTime to the internal Oracle DATE format NB! - It does not handle BCE dates. -*/ -QByteArray qMakeOraDate(const QDateTime& dt) -{ - QByteArray ba; - ba.resize(7); - int year = dt.date().year(); - ba[0]= (year / 100) + 100; // century - ba[1]= (year % 100) + 100; // year - ba[2]= dt.date().month(); - ba[3]= dt.date().day(); - ba[4]= dt.time().hour() + 1; - ba[5]= dt.time().minute() + 1; - ba[6]= dt.time().second() + 1; - return ba; -} - -/*! - \internal - - Convert qlonglong to the internal Oracle OCINumber format. - */ -QByteArray qMakeOCINumber(const qlonglong& ll, OCIError* err) -{ - QByteArray ba(sizeof(OCINumber), 0); - - OCINumberFromInt(err, - &ll, - sizeof(qlonglong), - OCI_NUMBER_SIGNED, - reinterpret_cast<OCINumber*>(ba.data())); - return ba; -} - -/*! - \internal - - Convert qulonglong to the internal Oracle OCINumber format. - */ -QByteArray qMakeOCINumber(const qulonglong& ull, OCIError* err) -{ - QByteArray ba(sizeof(OCINumber), 0); - - OCINumberFromInt(err, - &ull, - sizeof(qlonglong), - OCI_NUMBER_UNSIGNED, - reinterpret_cast<OCINumber*>(ba.data())); - return ba; -} - -qlonglong qMakeLongLong(const char* ociNumber, OCIError* err) -{ - qlonglong qll = 0; - OCINumberToInt(err, reinterpret_cast<const OCINumber *>(ociNumber), sizeof(qlonglong), - OCI_NUMBER_SIGNED, &qll); - return qll; -} - -qulonglong qMakeULongLong(const char* ociNumber, OCIError* err) -{ - qulonglong qull = 0; - OCINumberToInt(err, reinterpret_cast<const OCINumber *>(ociNumber), sizeof(qulonglong), - OCI_NUMBER_UNSIGNED, &qull); - return qull; -} - -QDateTime qMakeDate(const char* oraDate) -{ - int century = uchar(oraDate[0]); - if(century >= 100){ - int year = uchar(oraDate[1]); - year = ((century-100)*100) + (year-100); - int month = oraDate[2]; - int day = oraDate[3]; - int hour = oraDate[4] - 1; - int min = oraDate[5] - 1; - int sec = oraDate[6] - 1; - return QDateTime(QDate(year,month,day), QTime(hour,min,sec)); - } - return QDateTime(); -} - -class QOCICols -{ -public: - QOCICols(int size, QOCIResultPrivate* dp); - ~QOCICols(); - int readPiecewise(QVector<QVariant> &values, int index = 0); - int readLOBs(QVector<QVariant> &values, int index = 0); - int fieldFromDefine(OCIDefine* d); - void getValues(QVector<QVariant> &v, int index); - inline int size() { return fieldInf.size(); } - static bool execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, bool arrayBind); - - QSqlRecord rec; - -private: - char* create(int position, int size); - OCILobLocator ** createLobLocator(int position, OCIEnv* env); - OraFieldInfo qMakeOraField(const QOCIResultPrivate* p, OCIParam* param) const; - - class OraFieldInf - { - public: - OraFieldInf(): data(0), len(0), ind(0), typ(QVariant::Invalid), oraType(0), def(0), lob(0) - {} - ~OraFieldInf(); - char *data; - int len; - sb2 ind; - QVariant::Type typ; - ub4 oraType; - OCIDefine *def; - OCILobLocator *lob; - }; - - QVector<OraFieldInf> fieldInf; - const QOCIResultPrivate *const d; -}; - -QOCICols::OraFieldInf::~OraFieldInf() -{ - delete [] data; - if (lob) { - int r = OCIDescriptorFree(lob, OCI_DTYPE_LOB); - if (r != 0) - qWarning("QOCICols: Cannot free LOB descriptor"); - } -} - -QOCICols::QOCICols(int size, QOCIResultPrivate* dp) - : fieldInf(size), d(dp) -{ - ub4 dataSize = 0; - OCIDefine* dfn = 0; - int r; - - OCIParam* param = 0; - sb4 parmStatus = 0; - ub4 count = 1; - int idx = 0; - parmStatus = OCIParamGet(d->sql, - OCI_HTYPE_STMT, - d->err, - reinterpret_cast<void **>(¶m), - count); - - while (parmStatus == OCI_SUCCESS) { - OraFieldInfo ofi = qMakeOraField(d, param); - if (ofi.oraType == SQLT_RDD) - dataSize = 50; -#ifdef SQLT_INTERVAL_YM -#ifdef SQLT_INTERVAL_DS - else if (ofi.oraType == SQLT_INTERVAL_YM || ofi.oraType == SQLT_INTERVAL_DS) - // since we are binding interval datatype as string, - // we are not interested in the number of bytes but characters. - dataSize = 50; // magic number -#endif //SQLT_INTERVAL_DS -#endif //SQLT_INTERVAL_YM - else if (ofi.oraType == SQLT_NUM || ofi.oraType == SQLT_VNU){ - if (ofi.oraPrecision > 0) - dataSize = (ofi.oraPrecision + 1) * sizeof(utext); - else - dataSize = (38 + 1) * sizeof(utext); - } - else - dataSize = ofi.oraLength; - - fieldInf[idx].typ = ofi.type; - fieldInf[idx].oraType = ofi.oraType; - rec.append(qFromOraInf(ofi)); - - switch (ofi.type) { - case QVariant::DateTime: - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, dataSize+1), - dataSize+1, - SQLT_DAT, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - case QVariant::Double: - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, sizeof(double) - 1), - sizeof(double), - SQLT_FLT, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - case QVariant::Int: - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, sizeof(qint32) - 1), - sizeof(qint32), - SQLT_INT, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - case QVariant::LongLong: - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, sizeof(OCINumber)), - sizeof(OCINumber), - SQLT_VNU, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - case QVariant::ByteArray: - // RAW and LONG RAW fields can't be bound to LOB locators - if (ofi.oraType == SQLT_BIN) { -// qDebug("binding SQLT_BIN"); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, dataSize), - dataSize, - SQLT_BIN, - &(fieldInf[idx].ind), - 0, 0, OCI_DYNAMIC_FETCH); - } else if (ofi.oraType == SQLT_LBI) { -// qDebug("binding SQLT_LBI"); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - 0, - SB4MAXVAL, - SQLT_LBI, - &(fieldInf[idx].ind), - 0, 0, OCI_DYNAMIC_FETCH); - } else if (ofi.oraType == SQLT_CLOB) { - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - createLobLocator(idx, d->env), - -1, - SQLT_CLOB, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - } else { -// qDebug("binding SQLT_BLOB"); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - createLobLocator(idx, d->env), - -1, - SQLT_BLOB, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - } - break; - case QVariant::String: - if (ofi.oraType == SQLT_LNG) { - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - 0, - SB4MAXVAL, - SQLT_LNG, - &(fieldInf[idx].ind), - 0, 0, OCI_DYNAMIC_FETCH); - } else { - dataSize += dataSize + sizeof(QChar); - //qDebug("OCIDefineByPosStr(%d): %d", count, dataSize); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, dataSize), - dataSize, - SQLT_STR, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - if (r == 0) - d->setCharset(dfn, OCI_HTYPE_DEFINE); - } - break; - default: - // this should make enough space even with character encoding - dataSize = (dataSize + 1) * sizeof(utext) ; - //qDebug("OCIDefineByPosDef(%d): %d", count, dataSize); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, dataSize), - dataSize+1, - SQLT_STR, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - } - if (r != 0) - qOraWarning("QOCICols::bind:", d->err); - fieldInf[idx].def = dfn; - ++count; - ++idx; - parmStatus = OCIParamGet(d->sql, - OCI_HTYPE_STMT, - d->err, - reinterpret_cast<void **>(¶m), - count); - } -} - -QOCICols::~QOCICols() -{ -} - -char* QOCICols::create(int position, int size) -{ - char* c = new char[size+1]; - // Oracle may not fill fixed width fields - memset(c, 0, size+1); - fieldInf[position].data = c; - fieldInf[position].len = size; - return c; -} - -OCILobLocator **QOCICols::createLobLocator(int position, OCIEnv* env) -{ - OCILobLocator *& lob = fieldInf[position].lob; - int r = OCIDescriptorAlloc(env, - reinterpret_cast<void **>(&lob), - OCI_DTYPE_LOB, - 0, - 0); - if (r != 0) { - qWarning("QOCICols: Cannot create LOB locator"); - lob = 0; - } - return &lob; -} - -int QOCICols::readPiecewise(QVector<QVariant> &values, int index) -{ - OCIDefine* dfn; - ub4 typep; - ub1 in_outp; - ub4 iterp; - ub4 idxp; - ub1 piecep; - sword status; - text col [QOCI_DYNAMIC_CHUNK_SIZE+1]; - int fieldNum = -1; - int r = 0; - bool nullField; - - do { - r = OCIStmtGetPieceInfo(d->sql, d->err, reinterpret_cast<void **>(&dfn), &typep, - &in_outp, &iterp, &idxp, &piecep); - if (r != OCI_SUCCESS) - qOraWarning("OCIResultPrivate::readPiecewise: unable to get piece info:", d->err); - fieldNum = fieldFromDefine(dfn); - bool isStringField = fieldInf.at(fieldNum).oraType == SQLT_LNG; - ub4 chunkSize = QOCI_DYNAMIC_CHUNK_SIZE; - nullField = false; - r = OCIStmtSetPieceInfo(dfn, OCI_HTYPE_DEFINE, - d->err, col, - &chunkSize, piecep, NULL, NULL); - if (r != OCI_SUCCESS) - qOraWarning("OCIResultPrivate::readPiecewise: unable to set piece info:", d->err); - status = OCIStmtFetch (d->sql, d->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); - if (status == -1) { - sb4 errcode; - OCIErrorGet(d->err, 1, 0, &errcode, 0, 0,OCI_HTYPE_ERROR); - switch (errcode) { - case 1405: /* NULL */ - nullField = true; - break; - default: - qOraWarning("OCIResultPrivate::readPiecewise: unable to fetch next:", d->err); - break; - } - } - if (status == OCI_NO_DATA) - break; - if (nullField || !chunkSize) { - fieldInf[fieldNum].ind = -1; - } else { - if (isStringField) { - QString str = values.at(fieldNum + index).toString(); - str += QString(reinterpret_cast<const QChar *>(col), chunkSize / 2); - values[fieldNum + index] = str; - fieldInf[fieldNum].ind = 0; - } else { - QByteArray ba = values.at(fieldNum + index).toByteArray(); - int sz = ba.size(); - ba.resize(sz + chunkSize); - memcpy(ba.data() + sz, reinterpret_cast<char *>(col), chunkSize); - values[fieldNum + index] = ba; - fieldInf[fieldNum].ind = 0; - } - } - } while (status == OCI_SUCCESS_WITH_INFO || status == OCI_NEED_DATA); - return r; -} - -OraFieldInfo QOCICols::qMakeOraField(const QOCIResultPrivate* p, OCIParam* param) const -{ - OraFieldInfo ofi; - ub2 colType(0); - text *colName = 0; - ub4 colNameLen(0); - sb1 colScale(0); - ub2 colLength(0); - ub2 colFieldLength(0); - sb2 colPrecision(0); - ub1 colIsNull(0); - int r(0); - QVariant::Type type(QVariant::Invalid); - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colType, - 0, - OCI_ATTR_DATA_TYPE, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colName, - &colNameLen, - OCI_ATTR_NAME, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colLength, - 0, - OCI_ATTR_DATA_SIZE, /* in bytes */ - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - -#ifdef OCI_ATTR_CHAR_SIZE - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colFieldLength, - 0, - OCI_ATTR_CHAR_SIZE, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); -#else - // for Oracle8. - colFieldLength = colLength; -#endif - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colPrecision, - 0, - OCI_ATTR_PRECISION, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colScale, - 0, - OCI_ATTR_SCALE, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colType, - 0, - OCI_ATTR_DATA_TYPE, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colIsNull, - 0, - OCI_ATTR_IS_NULL, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - - type = qDecodeOCIType(colType, p->q_func()->numericalPrecisionPolicy()); - - if (type == QVariant::Int) { - if (colLength == 22 && colPrecision == 0 && colScale == 0) - type = QVariant::String; - if (colScale > 0) - type = QVariant::String; - } - - // bind as double if the precision policy asks for it - if (((colType == SQLT_FLT) || (colType == SQLT_NUM)) - && (p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionDouble)) { - type = QVariant::Double; - } - - // bind as int32 or int64 if the precision policy asks for it - if ((colType == SQLT_NUM) || (colType == SQLT_VNU) || (colType == SQLT_UIN) - || (colType == SQLT_INT)) { - if (p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt64) - type = QVariant::LongLong; - else if (p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt32) - type = QVariant::Int; - } - - if (colType == SQLT_BLOB) - colLength = 0; - - // colNameLen is length in bytes - ofi.name = QString(reinterpret_cast<const QChar*>(colName), colNameLen / 2); - ofi.type = type; - ofi.oraType = colType; - ofi.oraFieldLength = colFieldLength; - ofi.oraLength = colLength; - ofi.oraScale = colScale; - ofi.oraPrecision = colPrecision; - ofi.oraIsNull = colIsNull; - - return ofi; -} - -struct QOCIBatchColumn -{ - inline QOCIBatchColumn() - : bindh(0), bindAs(0), maxLen(0), recordCount(0), - data(0), lengths(0), indicators(0), maxarr_len(0), curelep(0) {} - - OCIBind* bindh; - ub2 bindAs; - ub4 maxLen; - ub4 recordCount; - char* data; - ub2* lengths; - sb2* indicators; - ub4 maxarr_len; - ub4 curelep; -}; - -struct QOCIBatchCleanupHandler -{ - inline QOCIBatchCleanupHandler(QVector<QOCIBatchColumn> &columns) - : col(columns) {} - - ~QOCIBatchCleanupHandler() - { - // deleting storage, length and indicator arrays - for ( int j = 0; j < col.count(); ++j){ - delete[] col[j].lengths; - delete[] col[j].indicators; - delete[] col[j].data; - } - } - - QVector<QOCIBatchColumn> &col; -}; - -bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, bool arrayBind) -{ - int columnCount = boundValues.count(); - if (boundValues.isEmpty() || columnCount == 0) - return false; - -#ifdef QOCI_DEBUG - qDebug() << "columnCount:" << columnCount << boundValues; -#endif - - int i; - sword r; - - QVarLengthArray<QVariant::Type> fieldTypes; - for (i = 0; i < columnCount; ++i) { - QVariant::Type tp = boundValues.at(i).type(); - fieldTypes.append(tp == QVariant::List ? boundValues.at(i).toList().value(0).type() - : tp); - } - - QList<QByteArray> tmpStorage; - SizeArray tmpSizes(columnCount); - QVector<QOCIBatchColumn> columns(columnCount); - QOCIBatchCleanupHandler cleaner(columns); - - // figuring out buffer sizes - for (i = 0; i < columnCount; ++i) { - - if (boundValues.at(i).type() != QVariant::List) { - - // not a list - create a deep-copy of the single value - QOCIBatchColumn &singleCol = columns[i]; - singleCol.indicators = new sb2[1]; - *singleCol.indicators = boundValues.at(i).isNull() ? -1 : 0; - - r = d->bindValue(d->sql, &singleCol.bindh, d->err, i, - boundValues.at(i), singleCol.indicators, &tmpSizes[i], tmpStorage); - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err); - d->q_func()->setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to bind column for batch execute"), - QSqlError::StatementError, d->err)); - return false; - } - continue; - } - - QOCIBatchColumn &col = columns[i]; - col.recordCount = boundValues.at(i).toList().count(); - - col.lengths = new ub2[col.recordCount]; - col.indicators = new sb2[col.recordCount]; - col.maxarr_len = col.recordCount; - col.curelep = col.recordCount; - - switch (fieldTypes[i]) { - case QVariant::Time: - case QVariant::Date: - case QVariant::DateTime: - col.bindAs = SQLT_DAT; - col.maxLen = 7; - break; - - case QVariant::Int: - col.bindAs = SQLT_INT; - col.maxLen = sizeof(int); - break; - - case QVariant::UInt: - col.bindAs = SQLT_UIN; - col.maxLen = sizeof(uint); - break; - - case QVariant::LongLong: - col.bindAs = SQLT_VNU; - col.maxLen = sizeof(OCINumber); - break; - - case QVariant::ULongLong: - col.bindAs = SQLT_VNU; - col.maxLen = sizeof(OCINumber); - break; - - case QVariant::Double: - col.bindAs = SQLT_FLT; - col.maxLen = sizeof(double); - break; - - case QVariant::UserType: - col.bindAs = SQLT_RDD; - col.maxLen = sizeof(OCIRowid*); - break; - - case QVariant::String: { - col.bindAs = SQLT_STR; - for (uint j = 0; j < col.recordCount; ++j) { - uint len; - if(d->isOutValue(i)) - len = boundValues.at(i).toList().at(j).toString().capacity() + 1; - else - len = boundValues.at(i).toList().at(j).toString().length() + 1; - if (len > col.maxLen) - col.maxLen = len; - } - col.maxLen *= sizeof(QChar); - break; } - - case QVariant::ByteArray: - default: { - col.bindAs = SQLT_LBI; - for (uint j = 0; j < col.recordCount; ++j) { - if(d->isOutValue(i)) - col.lengths[j] = boundValues.at(i).toList().at(j).toByteArray().capacity(); - else - col.lengths[j] = boundValues.at(i).toList().at(j).toByteArray().size(); - if (col.lengths[j] > col.maxLen) - col.maxLen = col.lengths[j]; - } - break; } - } - - col.data = new char[col.maxLen * col.recordCount]; - memset(col.data, 0, col.maxLen * col.recordCount); - - // we may now populate column with data - for (uint row = 0; row < col.recordCount; ++row) { - const QVariant &val = boundValues.at(i).toList().at(row); - - if (val.isNull()){ - columns[i].indicators[row] = -1; - columns[i].lengths[row] = 0; - } else { - columns[i].indicators[row] = 0; - char *dataPtr = columns[i].data + (columns[i].maxLen * row); - switch (fieldTypes[i]) { - case QVariant::Time: - case QVariant::Date: - case QVariant::DateTime:{ - columns[i].lengths[row] = columns[i].maxLen; - const QByteArray ba = qMakeOraDate(val.toDateTime()); - Q_ASSERT(ba.size() == int(columns[i].maxLen)); - memcpy(dataPtr, ba.constData(), columns[i].maxLen); - break; - } - case QVariant::Int: - columns[i].lengths[row] = columns[i].maxLen; - *reinterpret_cast<int*>(dataPtr) = val.toInt(); - break; - - case QVariant::UInt: - columns[i].lengths[row] = columns[i].maxLen; - *reinterpret_cast<uint*>(dataPtr) = val.toUInt(); - break; - - case QVariant::LongLong: - { - columns[i].lengths[row] = columns[i].maxLen; - const QByteArray ba = qMakeOCINumber(val.toLongLong(), d->err); - Q_ASSERT(ba.size() == int(columns[i].maxLen)); - memcpy(dataPtr, ba.constData(), columns[i].maxLen); - break; - } - case QVariant::ULongLong: - { - columns[i].lengths[row] = columns[i].maxLen; - const QByteArray ba = qMakeOCINumber(val.toULongLong(), d->err); - Q_ASSERT(ba.size() == int(columns[i].maxLen)); - memcpy(dataPtr, ba.constData(), columns[i].maxLen); - break; - } - case QVariant::Double: - columns[i].lengths[row] = columns[i].maxLen; - *reinterpret_cast<double*>(dataPtr) = val.toDouble(); - break; - - case QVariant::String: { - const QString s = val.toString(); - columns[i].lengths[row] = (s.length() + 1) * sizeof(QChar); - memcpy(dataPtr, s.utf16(), columns[i].lengths[row]); - break; - } - case QVariant::UserType: - if (val.canConvert<QOCIRowIdPointer>()) { - const QOCIRowIdPointer rptr = qvariant_cast<QOCIRowIdPointer>(val); - *reinterpret_cast<OCIRowid**>(dataPtr) = rptr->id; - columns[i].lengths[row] = 0; - break; - } - case QVariant::ByteArray: - default: { - const QByteArray ba = val.toByteArray(); - columns[i].lengths[row] = ba.size(); - memcpy(dataPtr, ba.constData(), ba.size()); - break; - } - } - } - } - - QOCIBatchColumn &bindColumn = columns[i]; - -#ifdef QOCI_DEBUG - qDebug("OCIBindByPos(%p, %p, %p, %d, %p, %d, %d, %p, %p, 0, %d, %p, OCI_DEFAULT)", - d->sql, &bindColumn.bindh, d->err, i + 1, bindColumn.data, - bindColumn.maxLen, bindColumn.bindAs, bindColumn.indicators, bindColumn.lengths, - arrayBind ? bindColumn.maxarr_len : 0, arrayBind ? &bindColumn.curelep : 0); - - for (int ii = 0; ii < (int)bindColumn.recordCount; ++ii) { - qDebug(" record %d: indicator %d, length %d", ii, bindColumn.indicators[ii], - bindColumn.lengths[ii]); - } -#endif - - - // binding the column - r = OCIBindByPos( - d->sql, &bindColumn.bindh, d->err, i + 1, - bindColumn.data, - bindColumn.maxLen, - bindColumn.bindAs, - bindColumn.indicators, - bindColumn.lengths, - 0, - arrayBind ? bindColumn.maxarr_len : 0, - arrayBind ? &bindColumn.curelep : 0, - OCI_DEFAULT); - -#ifdef QOCI_DEBUG - qDebug("After OCIBindByPos: r = %d, bindh = %p", r, bindColumn.bindh); -#endif - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err); - d->q_func()->setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to bind column for batch execute"), - QSqlError::StatementError, d->err)); - return false; - } - - r = OCIBindArrayOfStruct ( - columns[i].bindh, d->err, - columns[i].maxLen, - sizeof(columns[i].indicators[0]), - sizeof(columns[i].lengths[0]), - 0); - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err); - d->q_func()->setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to bind column for batch execute"), - QSqlError::StatementError, d->err)); - return false; - } - } - - //finaly we can execute - r = OCIStmtExecute(d->svc, d->sql, d->err, - arrayBind ? 1 : columns[0].recordCount, - 0, NULL, NULL, - d->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS); - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIPrivate::execBatch: unable to execute batch statement:", d->err); - d->q_func()->setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to execute batch statement"), - QSqlError::StatementError, d->err)); - return false; - } - - // for out parameters we copy data back to value vector - for (i = 0; i < columnCount; ++i) { - - if (!d->isOutValue(i)) - continue; - - QVariant::Type tp = boundValues.at(i).type(); - if (tp != QVariant::List) { - qOraOutValue(boundValues[i], tmpStorage, d->err); - if (*columns[i].indicators == -1) - boundValues[i] = QVariant(tp); - continue; - } - - QVariantList *list = static_cast<QVariantList *>(const_cast<void*>(boundValues.at(i).data())); - - char* data = columns[i].data; - for (uint r = 0; r < columns[i].recordCount; ++r){ - - if (columns[i].indicators[r] == -1) { - (*list)[r] = QVariant(); - continue; - } - - switch(columns[i].bindAs) { - - case SQLT_DAT: - (*list)[r] = qMakeDate(data + r * columns[i].maxLen); - break; - - case SQLT_INT: - (*list)[r] = *reinterpret_cast<int*>(data + r * columns[i].maxLen); - break; - - case SQLT_UIN: - (*list)[r] = *reinterpret_cast<uint*>(data + r * columns[i].maxLen); - break; - - case SQLT_VNU: - { - switch (boundValues.at(i).type()) { - case QVariant::LongLong: - (*list)[r] = qMakeLongLong(data + r * columns[i].maxLen, d->err); - break; - case QVariant::ULongLong: - (*list)[r] = qMakeULongLong(data + r * columns[i].maxLen, d->err); - break; - default: - break; - } - break; - } - - case SQLT_FLT: - (*list)[r] = *reinterpret_cast<double*>(data + r * columns[i].maxLen); - break; - - case SQLT_STR: - (*list)[r] = QString(reinterpret_cast<const QChar *>(data - + r * columns[i].maxLen)); - break; - - default: - (*list)[r] = QByteArray(data + r * columns[i].maxLen, columns[i].maxLen); - break; - } - } - } - - d->q_func()->setSelect(false); - d->q_func()->setAt(QSql::BeforeFirstRow); - d->q_func()->setActive(true); - - return true; -} - -template<class T, int sz> -int qReadLob(T &buf, const QOCIResultPrivate *d, OCILobLocator *lob) -{ - ub1 csfrm; - ub4 amount; - int r; - - // Read this from the database, don't assume we know what it is set to - r = OCILobCharSetForm(d->env, d->err, lob, &csfrm); - if (r != OCI_SUCCESS) { - qOraWarning("OCIResultPrivate::readLobs: Couldn't get LOB char set form: ", d->err); - csfrm = 0; - } - - // Get the length of the LOB (this is in characters) - r = OCILobGetLength(d->svc, d->err, lob, &amount); - if (r == OCI_SUCCESS) { - if (amount == 0) { - // Short cut for null LOBs - buf.resize(0); - return OCI_SUCCESS; - } - } else { - qOraWarning("OCIResultPrivate::readLobs: Couldn't get LOB length: ", d->err); - return r; - } - - // Resize the buffer to hold the LOB contents - buf.resize(amount); - - // Read the LOB into the buffer - r = OCILobRead(d->svc, - d->err, - lob, - &amount, - 1, - buf.data(), - buf.size() * sz, // this argument is in bytes, not characters - 0, - 0, - // Extract the data from a CLOB in UTF-16 (ie. what QString uses internally) - sz == 1 ? ub2(0) : ub2(QOCIEncoding), - csfrm); - - if (r != OCI_SUCCESS) - qOraWarning("OCIResultPrivate::readLOBs: Cannot read LOB: ", d->err); - - return r; -} - -int QOCICols::readLOBs(QVector<QVariant> &values, int index) -{ - OCILobLocator *lob; - int r = OCI_SUCCESS; - - for (int i = 0; i < size(); ++i) { - const OraFieldInf &fi = fieldInf.at(i); - if (fi.ind == -1 || !(lob = fi.lob)) - continue; - - bool isClob = fi.oraType == SQLT_CLOB; - QVariant var; - - if (isClob) { - QString str; - r = qReadLob<QString, sizeof(QChar)>(str, d, lob); - var = str; - } else { - QByteArray buf; - r = qReadLob<QByteArray, sizeof(char)>(buf, d, lob); - var = buf; - } - if (r == OCI_SUCCESS) - values[index + i] = var; - else - break; - } - return r; -} - -int QOCICols::fieldFromDefine(OCIDefine* d) -{ - for (int i = 0; i < fieldInf.count(); ++i) { - if (fieldInf.at(i).def == d) - return i; - } - return -1; -} - -void QOCICols::getValues(QVector<QVariant> &v, int index) -{ - for (int i = 0; i < fieldInf.size(); ++i) { - const OraFieldInf &fld = fieldInf.at(i); - - if (fld.ind == -1) { - // got a NULL value - v[index + i] = QVariant(fld.typ); - continue; - } - - if (fld.oraType == SQLT_BIN || fld.oraType == SQLT_LBI || fld.oraType == SQLT_LNG) - continue; // already fetched piecewise - - switch (fld.typ) { - case QVariant::DateTime: - v[index + i] = QVariant(qMakeDate(fld.data)); - break; - case QVariant::Double: - case QVariant::Int: - case QVariant::LongLong: - if (d->q_func()->numericalPrecisionPolicy() != QSql::HighPrecision) { - if ((d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionDouble) - && (fld.typ == QVariant::Double)) { - v[index + i] = *reinterpret_cast<double *>(fld.data); - break; - } else if ((d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt64) - && (fld.typ == QVariant::LongLong)) { - qint64 qll = 0; - int r = OCINumberToInt(d->err, reinterpret_cast<OCINumber *>(fld.data), sizeof(qint64), - OCI_NUMBER_SIGNED, &qll); - if(r == OCI_SUCCESS) - v[index + i] = qll; - else - v[index + i] = QVariant(); - break; - } else if ((d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt32) - && (fld.typ == QVariant::Int)) { - v[index + i] = *reinterpret_cast<int *>(fld.data); - break; - } - } - // else fall through - case QVariant::String: - v[index + i] = QString(reinterpret_cast<const QChar *>(fld.data)); - break; - case QVariant::ByteArray: - if (fld.len > 0) - v[index + i] = QByteArray(fld.data, fld.len); - else - v[index + i] = QVariant(QVariant::ByteArray); - break; - default: - qWarning("QOCICols::value: unknown data type"); - break; - } - } -} - -QOCIResultPrivate::QOCIResultPrivate(QOCIResult *q, const QOCIDriver *drv) - : QSqlCachedResultPrivate(q, drv), - cols(0), - env(drv_d_func()->env), - err(0), - svc(const_cast<OCISvcCtx*&>(drv_d_func()->svc)), - sql(0), - transaction(drv_d_func()->transaction), - serverVersion(drv_d_func()->serverVersion), - prefetchRows(drv_d_func()->prefetchRows), - prefetchMem(drv_d_func()->prefetchMem) -{ - int r = OCIHandleAlloc(env, - reinterpret_cast<void **>(&err), - OCI_HTYPE_ERROR, - 0, - 0); - if (r != 0) - qWarning("QOCIResult: unable to alloc error handle"); -} - -QOCIResultPrivate::~QOCIResultPrivate() -{ - delete cols; - - int r = OCIHandleFree(err, OCI_HTYPE_ERROR); - if (r != 0) - qWarning("~QOCIResult: unable to free statement handle"); -} - - -//////////////////////////////////////////////////////////////////////////// - -QOCIResult::QOCIResult(const QOCIDriver *db) - : QSqlCachedResult(*new QOCIResultPrivate(this, db)) -{ -} - -QOCIResult::~QOCIResult() -{ - Q_D(QOCIResult); - if (d->sql) { - int r = OCIHandleFree(d->sql, OCI_HTYPE_STMT); - if (r != 0) - qWarning("~QOCIResult: unable to free statement handle"); - } -} - -QVariant QOCIResult::handle() const -{ - Q_D(const QOCIResult); - return QVariant::fromValue(d->sql); -} - -bool QOCIResult::reset (const QString& query) -{ - if (!prepare(query)) - return false; - return exec(); -} - -bool QOCIResult::gotoNext(QSqlCachedResult::ValueCache &values, int index) -{ - Q_D(QOCIResult); - if (at() == QSql::AfterLastRow) - return false; - - bool piecewise = false; - int r = OCI_SUCCESS; - r = OCIStmtFetch(d->sql, d->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); - - if (index < 0) //not interested in values - return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO; - - switch (r) { - case OCI_SUCCESS: - break; - case OCI_SUCCESS_WITH_INFO: - qOraWarning("QOCIResult::gotoNext: SuccessWithInfo: ", d->err); - r = OCI_SUCCESS; //ignore it - break; - case OCI_NO_DATA: - // end of rowset - return false; - case OCI_NEED_DATA: - piecewise = true; - r = OCI_SUCCESS; - break; - case OCI_ERROR: - if (qOraErrorNumber(d->err) == 1406) { - qWarning("QOCI Warning: data truncated for %s", lastQuery().toLocal8Bit().constData()); - r = OCI_SUCCESS; /* ignore it */ - break; - } - // fall through - default: - qOraWarning("QOCIResult::gotoNext: ", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to goto next"), - QSqlError::StatementError, d->err)); - break; - } - - // need to read piecewise before assigning values - if (r == OCI_SUCCESS && piecewise) - r = d->cols->readPiecewise(values, index); - - if (r == OCI_SUCCESS) - d->cols->getValues(values, index); - if (r == OCI_SUCCESS) - r = d->cols->readLOBs(values, index); - if (r != OCI_SUCCESS) - setAt(QSql::AfterLastRow); - return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO; -} - -int QOCIResult::size() -{ - return -1; -} - -int QOCIResult::numRowsAffected() -{ - Q_D(QOCIResult); - int rowCount; - OCIAttrGet(d->sql, - OCI_HTYPE_STMT, - &rowCount, - NULL, - OCI_ATTR_ROW_COUNT, - d->err); - return rowCount; -} - -bool QOCIResult::prepare(const QString& query) -{ - Q_D(QOCIResult); - int r = 0; - QSqlResult::prepare(query); - - delete d->cols; - d->cols = 0; - QSqlCachedResult::cleanup(); - - if (d->sql) { - r = OCIHandleFree(d->sql, OCI_HTYPE_STMT); - if (r != OCI_SUCCESS) - qOraWarning("QOCIResult::prepare: unable to free statement handle:", d->err); - } - if (query.isEmpty()) - return false; - r = OCIHandleAlloc(d->env, - reinterpret_cast<void **>(&d->sql), - OCI_HTYPE_STMT, - 0, - 0); - if (r != OCI_SUCCESS) { - qOraWarning("QOCIResult::prepare: unable to alloc statement:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to alloc statement"), QSqlError::StatementError, d->err)); - return false; - } - d->setStatementAttributes(); - const OraText *txt = reinterpret_cast<const OraText *>(query.utf16()); - const int len = query.length() * sizeof(QChar); - r = OCIStmtPrepare(d->sql, - d->err, - txt, - len, - OCI_NTV_SYNTAX, - OCI_DEFAULT); - if (r != OCI_SUCCESS) { - qOraWarning("QOCIResult::prepare: unable to prepare statement:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to prepare statement"), QSqlError::StatementError, d->err)); - return false; - } - return true; -} - -bool QOCIResult::exec() -{ - Q_D(QOCIResult); - int r = 0; - ub2 stmtType=0; - ub4 iters; - ub4 mode; - QList<QByteArray> tmpStorage; - IndicatorArray indicators(boundValueCount()); - SizeArray tmpSizes(boundValueCount()); - - r = OCIAttrGet(d->sql, - OCI_HTYPE_STMT, - &stmtType, - NULL, - OCI_ATTR_STMT_TYPE, - d->err); - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIResult::exec: Unable to get statement type:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to get statement type"), QSqlError::StatementError, d->err)); -#ifdef QOCI_DEBUG - qDebug() << "lastQuery()" << lastQuery(); -#endif - return false; - } - - iters = stmtType == OCI_STMT_SELECT ? 0 : 1; - mode = d->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS; - - // bind placeholders - if (boundValueCount() > 0 - && d->bindValues(boundValues(), indicators, tmpSizes, tmpStorage) != OCI_SUCCESS) { - qOraWarning("QOCIResult::exec: unable to bind value: ", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", "Unable to bind value"), - QSqlError::StatementError, d->err)); -#ifdef QOCI_DEBUG - qDebug() << "lastQuery()" << lastQuery(); -#endif - return false; - } - - // execute - r = OCIStmtExecute(d->svc, - d->sql, - d->err, - iters, - 0, - 0, - 0, - mode); - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIResult::exec: unable to execute statement:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to execute statement"), QSqlError::StatementError, d->err)); -#ifdef QOCI_DEBUG - qDebug() << "lastQuery()" << lastQuery(); -#endif - return false; - } - - if (stmtType == OCI_STMT_SELECT) { - ub4 parmCount = 0; - int r = OCIAttrGet(d->sql, OCI_HTYPE_STMT, reinterpret_cast<void **>(&parmCount), - 0, OCI_ATTR_PARAM_COUNT, d->err); - if (r == 0 && !d->cols) - d->cols = new QOCICols(parmCount, d); - setSelect(true); - QSqlCachedResult::init(parmCount); - } else { /* non-SELECT */ - setSelect(false); - } - setAt(QSql::BeforeFirstRow); - setActive(true); - - if (hasOutValues()) - d->outValues(boundValues(), indicators, tmpStorage); - - return true; -} - -QSqlRecord QOCIResult::record() const -{ - Q_D(const QOCIResult); - QSqlRecord inf; - if (!isActive() || !isSelect() || !d->cols) - return inf; - return d->cols->rec; -} - -QVariant QOCIResult::lastInsertId() const -{ - Q_D(const QOCIResult); - if (isActive()) { - QOCIRowIdPointer ptr(new QOCIRowId(d->env)); - - int r = OCIAttrGet(d->sql, OCI_HTYPE_STMT, ptr.constData()->id, - 0, OCI_ATTR_ROWID, d->err); - if (r == OCI_SUCCESS) - return QVariant::fromValue(ptr); - } - return QVariant(); -} - -bool QOCIResult::execBatch(bool arrayBind) -{ - Q_D(QOCIResult); - QOCICols::execBatch(d, boundValues(), arrayBind); - resetBindCount(); - return lastError().type() == QSqlError::NoError; -} - -void QOCIResult::virtual_hook(int id, void *data) -{ - Q_ASSERT(data); - - QSqlCachedResult::virtual_hook(id, data); -} - -//////////////////////////////////////////////////////////////////////////// - - -QOCIDriver::QOCIDriver(QObject* parent) - : QSqlDriver(*new QOCIDriverPrivate, parent) -{ - Q_D(QOCIDriver); -#ifdef QOCI_THREADED - const ub4 mode = OCI_UTF16 | OCI_OBJECT | OCI_THREADED; -#else - const ub4 mode = OCI_UTF16 | OCI_OBJECT; -#endif - int r = OCIEnvCreate(&d->env, - mode, - NULL, - NULL, - NULL, - NULL, - 0, - NULL); - if (r != 0) { - qWarning("QOCIDriver: unable to create environment"); - setLastError(qMakeError(tr("Unable to initialize", "QOCIDriver"), - QSqlError::ConnectionError, d->err)); - return; - } - - d->allocErrorHandle(); -} - -QOCIDriver::QOCIDriver(OCIEnv* env, OCISvcCtx* ctx, QObject* parent) - : QSqlDriver(*new QOCIDriverPrivate, parent) -{ - Q_D(QOCIDriver); - d->env = env; - d->svc = ctx; - - d->allocErrorHandle(); - - if (env && ctx) { - setOpen(true); - setOpenError(false); - } -} - -QOCIDriver::~QOCIDriver() -{ - Q_D(QOCIDriver); - if (isOpen()) - close(); - int r = OCIHandleFree(d->err, OCI_HTYPE_ERROR); - if (r != OCI_SUCCESS) - qWarning("Unable to free Error handle: %d", r); - r = OCIHandleFree(d->env, OCI_HTYPE_ENV); - if (r != OCI_SUCCESS) - qWarning("Unable to free Environment handle: %d", r); -} - -bool QOCIDriver::hasFeature(DriverFeature f) const -{ - Q_D(const QOCIDriver); - switch (f) { - case Transactions: - case LastInsertId: - case BLOB: - case PreparedQueries: - case NamedPlaceholders: - case BatchOperations: - case LowPrecisionNumbers: - return true; - case QuerySize: - case PositionalPlaceholders: - case SimpleLocking: - case EventNotifications: - case FinishQuery: - case CancelQuery: - case MultipleResultSets: - return false; - case Unicode: - return d->serverVersion >= 9; - } - return false; -} - -static void qParseOpts(const QString &options, QOCIDriverPrivate *d) -{ - const QStringList opts(options.split(QLatin1Char(';'), QString::SkipEmptyParts)); - for (int i = 0; i < opts.count(); ++i) { - const QString tmp(opts.at(i)); - int idx; - if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) { - qWarning("QOCIDriver::parseArgs: Invalid parameter: '%s'", - tmp.toLocal8Bit().constData()); - continue; - } - const QString opt = tmp.left(idx); - const QString val = tmp.mid(idx + 1).simplified(); - bool ok; - if (opt == QLatin1String("OCI_ATTR_PREFETCH_ROWS")) { - d->prefetchRows = val.toInt(&ok); - if (!ok) - d->prefetchRows = -1; - } else if (opt == QLatin1String("OCI_ATTR_PREFETCH_MEMORY")) { - d->prefetchMem = val.toInt(&ok); - if (!ok) - d->prefetchMem = -1; - } else { - qWarning ("QOCIDriver::parseArgs: Invalid parameter: '%s'", - opt.toLocal8Bit().constData()); - } - } -} - -bool QOCIDriver::open(const QString & db, - const QString & user, - const QString & password, - const QString & hostname, - int port, - const QString &opts) -{ - Q_D(QOCIDriver); - int r; - - if (isOpen()) - close(); - - qParseOpts(opts, d); - - // Connect without tnsnames.ora if a hostname is given - QString connectionString = db; - if (!hostname.isEmpty()) - connectionString = - QString::fromLatin1("(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=%1)(Port=%2))" - "(CONNECT_DATA=(SID=%3)))").arg(hostname).arg((port > -1 ? port : 1521)).arg(db); - - r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->srvhp), OCI_HTYPE_SERVER, 0, 0); - if (r == OCI_SUCCESS) - r = OCIServerAttach(d->srvhp, d->err, reinterpret_cast<const OraText *>(connectionString.utf16()), - connectionString.length() * sizeof(QChar), OCI_DEFAULT); - if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO) - r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->svc), OCI_HTYPE_SVCCTX, 0, 0); - if (r == OCI_SUCCESS) - r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->srvhp, 0, OCI_ATTR_SERVER, d->err); - if (r == OCI_SUCCESS) - r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->authp), OCI_HTYPE_SESSION, 0, 0); - if (r == OCI_SUCCESS) - r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>(user.utf16()), - user.length() * sizeof(QChar), OCI_ATTR_USERNAME, d->err); - if (r == OCI_SUCCESS) - r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>(password.utf16()), - password.length() * sizeof(QChar), OCI_ATTR_PASSWORD, d->err); - - OCITrans* trans; - if (r == OCI_SUCCESS) - r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&trans), OCI_HTYPE_TRANS, 0, 0); - if (r == OCI_SUCCESS) - r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, trans, 0, OCI_ATTR_TRANS, d->err); - - if (r == OCI_SUCCESS) { - if (user.isEmpty() && password.isEmpty()) - r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_EXT, OCI_DEFAULT); - else - r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_RDBMS, OCI_DEFAULT); - } - if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO) - r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->authp, 0, OCI_ATTR_SESSION, d->err); - - if (r != OCI_SUCCESS) { - setLastError(qMakeError(tr("Unable to logon"), QSqlError::ConnectionError, d->err)); - setOpenError(true); - if (d->authp) - OCIHandleFree(d->authp, OCI_HTYPE_SESSION); - d->authp = 0; - if (d->srvhp) - OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER); - d->srvhp = 0; - return false; - } - - // get server version - char vertxt[512]; - r = OCIServerVersion(d->svc, - d->err, - reinterpret_cast<OraText *>(vertxt), - sizeof(vertxt), - OCI_HTYPE_SVCCTX); - if (r != 0) { - qWarning("QOCIDriver::open: could not get Oracle server version."); - } else { - QString versionStr; - versionStr = QString(reinterpret_cast<const QChar *>(vertxt)); - QRegExp vers(QLatin1String("([0-9]+)\\.[0-9\\.]+[0-9]")); - if (vers.indexIn(versionStr) >= 0) - d->serverVersion = vers.cap(1).toInt(); - if (d->serverVersion == 0) - d->serverVersion = -1; - } - - setOpen(true); - setOpenError(false); - d->user = user; - - return true; -} - -void QOCIDriver::close() -{ - Q_D(QOCIDriver); - if (!isOpen()) - return; - - OCISessionEnd(d->svc, d->err, d->authp, OCI_DEFAULT); - OCIServerDetach(d->srvhp, d->err, OCI_DEFAULT); - OCIHandleFree(d->authp, OCI_HTYPE_SESSION); - d->authp = 0; - OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER); - d->srvhp = 0; - OCIHandleFree(d->svc, OCI_HTYPE_SVCCTX); - d->svc = 0; - setOpen(false); - setOpenError(false); -} - -QSqlResult *QOCIDriver::createResult() const -{ - return new QOCIResult(this); -} - -bool QOCIDriver::beginTransaction() -{ - Q_D(QOCIDriver); - if (!isOpen()) { - qWarning("QOCIDriver::beginTransaction: Database not open"); - return false; - } - int r = OCITransStart(d->svc, - d->err, - 2, - OCI_TRANS_READWRITE); - if (r == OCI_ERROR) { - qOraWarning("QOCIDriver::beginTransaction: ", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIDriver", - "Unable to begin transaction"), QSqlError::TransactionError, d->err)); - return false; - } - d->transaction = true; - return true; -} - -bool QOCIDriver::commitTransaction() -{ - Q_D(QOCIDriver); - if (!isOpen()) { - qWarning("QOCIDriver::commitTransaction: Database not open"); - return false; - } - int r = OCITransCommit(d->svc, - d->err, - 0); - if (r == OCI_ERROR) { - qOraWarning("QOCIDriver::commitTransaction:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIDriver", - "Unable to commit transaction"), QSqlError::TransactionError, d->err)); - return false; - } - d->transaction = false; - return true; -} - -bool QOCIDriver::rollbackTransaction() -{ - Q_D(QOCIDriver); - if (!isOpen()) { - qWarning("QOCIDriver::rollbackTransaction: Database not open"); - return false; - } - int r = OCITransRollback(d->svc, - d->err, - 0); - if (r == OCI_ERROR) { - qOraWarning("QOCIDriver::rollbackTransaction:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIDriver", - "Unable to rollback transaction"), QSqlError::TransactionError, d->err)); - return false; - } - d->transaction = false; - return true; -} - -enum Expression { - OrExpression, - AndExpression -}; - -static QString make_where_clause(const QString &user, Expression e) -{ - static const char sysUsers[][8] = { - "MDSYS", - "LBACSYS", - "SYS", - "SYSTEM", - "WKSYS", - "CTXSYS", - "WMSYS", - }; - static const char joinC[][4] = { "or" , "and" }; - static Q_CONSTEXPR QLatin1Char bang[] = { QLatin1Char(' '), QLatin1Char('!') }; - - const QLatin1String join(joinC[e], -1); // -1: force strlen call - - QString result; - result.reserve(sizeof sysUsers / sizeof *sysUsers * - // max-sizeof(owner != <sysuser> and ) - (9 + sizeof *sysUsers + 5)); - for (const auto &sysUser : sysUsers) { - const QLatin1String l1(sysUser, -1); // -1: force strlen call - if (l1 != user) - result += QLatin1String("owner ") + bang[e] + QLatin1String("= '") + l1 + QLatin1Char(' ') + join + QLatin1Char(' '); - } - - result.chop(join.size() + 2); // remove final " <join> " - - return result; -} - -QStringList QOCIDriver::tables(QSql::TableType type) const -{ - Q_D(const QOCIDriver); - QStringList tl; - - QString user = d->user; - if ( isIdentifierEscaped(user, QSqlDriver::TableName)) - user = stripDelimiters(user, QSqlDriver::TableName); - else - user = user.toUpper(); - - if (!isOpen()) - return tl; - - QSqlQuery t(createResult()); - t.setForwardOnly(true); - if (type & QSql::Tables) { - const QLatin1String tableQuery("select owner, table_name from all_tables where "); - const QString where = make_where_clause(user, AndExpression); - t.exec(tableQuery + where); - while (t.next()) { - if (t.value(0).toString().toUpper() != user.toUpper()) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - - // list all table synonyms as well - const QLatin1String synonymQuery("select owner, synonym_name from all_synonyms where "); - t.exec(synonymQuery + where); - while (t.next()) { - if (t.value(0).toString() != d->user) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - } - if (type & QSql::Views) { - const QLatin1String query("select owner, view_name from all_views where "); - const QString where = make_where_clause(user, AndExpression); - t.exec(query + where); - while (t.next()) { - if (t.value(0).toString().toUpper() != d->user.toUpper()) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - } - if (type & QSql::SystemTables) { - t.exec(QLatin1String("select table_name from dictionary")); - while (t.next()) { - tl.append(t.value(0).toString()); - } - const QLatin1String tableQuery("select owner, table_name from all_tables where "); - const QString where = make_where_clause(user, OrExpression); - t.exec(tableQuery + where); - while (t.next()) { - if (t.value(0).toString().toUpper() != user.toUpper()) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - - // list all table synonyms as well - const QLatin1String synonymQuery("select owner, synonym_name from all_synonyms where "); - t.exec(synonymQuery + where); - while (t.next()) { - if (t.value(0).toString() != d->user) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - } - return tl; -} - -void qSplitTableAndOwner(const QString & tname, QString * tbl, - QString * owner) -{ - int i = tname.indexOf(QLatin1Char('.')); // prefixed with owner? - if (i != -1) { - *tbl = tname.right(tname.length() - i - 1); - *owner = tname.left(i); - } else { - *tbl = tname; - } -} - -QSqlRecord QOCIDriver::record(const QString& tablename) const -{ - Q_D(const QOCIDriver); - QSqlRecord fil; - if (!isOpen()) - return fil; - - QSqlQuery t(createResult()); - // using two separate queries for this is A LOT faster than using - // eg. a sub-query on the sys.synonyms table - QString stmt(QLatin1String("select column_name, data_type, data_length, " - "data_precision, data_scale, nullable, data_default%1" - "from all_tab_columns a " - "where a.table_name=%2")); - if (d->serverVersion >= 9) - stmt = stmt.arg(QLatin1String(", char_length ")); - else - stmt = stmt.arg(QLatin1String(" ")); - bool buildRecordInfo = false; - QString table, owner, tmpStmt; - qSplitTableAndOwner(tablename, &table, &owner); - - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = table.toUpper(); - - tmpStmt = stmt.arg(QLatin1Char('\'') + table + QLatin1Char('\'')); - if (owner.isEmpty()) { - owner = d->user; - } - - if (isIdentifierEscaped(owner, QSqlDriver::TableName)) - owner = stripDelimiters(owner, QSqlDriver::TableName); - else - owner = owner.toUpper(); - - tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\''); - t.setForwardOnly(true); - t.exec(tmpStmt); - if (!t.next()) { // try and see if the tablename is a synonym - stmt = stmt + QLatin1String(" join all_synonyms b " - "on a.owner=b.table_owner and a.table_name=b.table_name " - "where b.owner='") + owner + - QLatin1String("' and b.synonym_name='") + table + - QLatin1Char('\''); - t.setForwardOnly(true); - t.exec(stmt); - if (t.next()) - buildRecordInfo = true; - } else { - buildRecordInfo = true; - } - QStringList keywords = QStringList() << QLatin1String("NUMBER") << QLatin1String("FLOAT") << QLatin1String("BINARY_FLOAT") - << QLatin1String("BINARY_DOUBLE"); - if (buildRecordInfo) { - do { - QVariant::Type ty = qDecodeOCIType(t.value(1).toString(), t.numericalPrecisionPolicy()); - QSqlField f(t.value(0).toString(), ty); - f.setRequired(t.value(5).toString() == QLatin1String("N")); - f.setPrecision(t.value(4).toInt()); - if (d->serverVersion >= 9 && (ty == QVariant::String) && !t.isNull(3) && !keywords.contains(t.value(1).toString())) { - // Oracle9: data_length == size in bytes, char_length == amount of characters - f.setLength(t.value(7).toInt()); - } else { - f.setLength(t.value(t.isNull(3) ? 2 : 3).toInt()); - } - f.setDefaultValue(t.value(6)); - fil.append(f); - } while (t.next()); - } - return fil; -} - -QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const -{ - Q_D(const QOCIDriver); - QSqlIndex idx(tablename); - if (!isOpen()) - return idx; - QSqlQuery t(createResult()); - QString stmt(QLatin1String("select b.column_name, b.index_name, a.table_name, a.owner " - "from all_constraints a, all_ind_columns b " - "where a.constraint_type='P' " - "and b.index_name = a.constraint_name " - "and b.index_owner = a.owner")); - - bool buildIndex = false; - QString table, owner, tmpStmt; - qSplitTableAndOwner(tablename, &table, &owner); - - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = table.toUpper(); - - tmpStmt = stmt + QLatin1String(" and a.table_name='") + table + QLatin1Char('\''); - if (owner.isEmpty()) { - owner = d->user; - } - - if (isIdentifierEscaped(owner, QSqlDriver::TableName)) - owner = stripDelimiters(owner, QSqlDriver::TableName); - else - owner = owner.toUpper(); - - tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\''); - t.setForwardOnly(true); - t.exec(tmpStmt); - - if (!t.next()) { - stmt += QLatin1String(" and a.table_name=(select tname from sys.synonyms " - "where sname='") + table + QLatin1String("' and creator=a.owner)"); - t.setForwardOnly(true); - t.exec(stmt); - if (t.next()) { - owner = t.value(3).toString(); - buildIndex = true; - } - } else { - buildIndex = true; - } - if (buildIndex) { - QSqlQuery tt(createResult()); - tt.setForwardOnly(true); - idx.setName(t.value(1).toString()); - do { - tt.exec(QLatin1String("select data_type from all_tab_columns where table_name='") + - t.value(2).toString() + QLatin1String("' and column_name='") + - t.value(0).toString() + QLatin1String("' and owner='") + - owner + QLatin1Char('\'')); - if (!tt.next()) { - return QSqlIndex(); - } - QSqlField f(t.value(0).toString(), qDecodeOCIType(tt.value(0).toString(), t.numericalPrecisionPolicy())); - idx.append(f); - } while (t.next()); - return idx; - } - return QSqlIndex(); -} - -QString QOCIDriver::formatValue(const QSqlField &field, bool trimStrings) const -{ - switch (field.type()) { - case QVariant::DateTime: { - QDateTime datetime = field.value().toDateTime(); - QString datestring; - if (datetime.isValid()) { - datestring = QLatin1String("TO_DATE('") + QString::number(datetime.date().year()) - + QLatin1Char('-') - + QString::number(datetime.date().month()) + QLatin1Char('-') - + QString::number(datetime.date().day()) + QLatin1Char(' ') - + QString::number(datetime.time().hour()) + QLatin1Char(':') - + QString::number(datetime.time().minute()) + QLatin1Char(':') - + QString::number(datetime.time().second()) - + QLatin1String("','YYYY-MM-DD HH24:MI:SS')"); - } else { - datestring = QLatin1String("NULL"); - } - return datestring; - } - case QVariant::Time: { - QDateTime datetime = field.value().toDateTime(); - QString datestring; - if (datetime.isValid()) { - datestring = QLatin1String("TO_DATE('") - + QString::number(datetime.time().hour()) + QLatin1Char(':') - + QString::number(datetime.time().minute()) + QLatin1Char(':') - + QString::number(datetime.time().second()) - + QLatin1String("','HH24:MI:SS')"); - } else { - datestring = QLatin1String("NULL"); - } - return datestring; - } - case QVariant::Date: { - QDate date = field.value().toDate(); - QString datestring; - if (date.isValid()) { - datestring = QLatin1String("TO_DATE('") + QString::number(date.year()) + - QLatin1Char('-') + - QString::number(date.month()) + QLatin1Char('-') + - QString::number(date.day()) + QLatin1String("','YYYY-MM-DD')"); - } else { - datestring = QLatin1String("NULL"); - } - return datestring; - } - default: - break; - } - return QSqlDriver::formatValue(field, trimStrings); -} - -QVariant QOCIDriver::handle() const -{ - Q_D(const QOCIDriver); - return QVariant::fromValue(d->env); -} - -QString QOCIDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const -{ - QString res = identifier; - if(!identifier.isEmpty() && !isIdentifierEscaped(identifier, type)) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/oci/qsql_oci.pri b/src/sql/drivers/oci/qsql_oci.pri deleted file mode 100644 index 66ccdb1abb..0000000000 --- a/src/sql/drivers/oci/qsql_oci.pri +++ /dev/null @@ -1,9 +0,0 @@ -HEADERS += $$PWD/qsql_oci_p.h -SOURCES += $$PWD/qsql_oci.cpp - -unix { - !contains(LIBS, .*clnts.*):LIBS += -lclntsh -} else { - LIBS *= -loci -} -mac:QMAKE_LFLAGS += -Wl,-flat_namespace,-U,_environ diff --git a/src/sql/drivers/oci/qsql_oci_p.h b/src/sql/drivers/oci/qsql_oci_p.h deleted file mode 100644 index 69911f4bee..0000000000 --- a/src/sql/drivers/oci/qsql_oci_p.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtSql module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSQL_OCI_H -#define QSQL_OCI_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtSql/qsqldriver.h> - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_OCI -#else -#define Q_EXPORT_SQLDRIVER_OCI Q_SQL_EXPORT -#endif - -typedef struct OCIEnv OCIEnv; -typedef struct OCISvcCtx OCISvcCtx; - -QT_BEGIN_NAMESPACE - -class QSqlResult; -class QOCIDriverPrivate; - -class Q_EXPORT_SQLDRIVER_OCI QOCIDriver : public QSqlDriver -{ - Q_DECLARE_PRIVATE(QOCIDriver) - Q_OBJECT - friend class QOCICols; - friend class QOCIResultPrivate; - -public: - explicit QOCIDriver(QObject* parent = 0); - QOCIDriver(OCIEnv* env, OCISvcCtx* ctx, QObject* parent = 0); - ~QOCIDriver(); - bool hasFeature(DriverFeature f) const; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port, - const QString &connOpts) Q_DECL_OVERRIDE; - void close() Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString& tablename) const Q_DECL_OVERRIDE; - QString formatValue(const QSqlField &field, - bool trimStrings) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE; - -protected: - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; -}; - -QT_END_NAMESPACE - -#endif // QSQL_OCI_H |