util/src/sql/drivers/oci/qsql_oci.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtSql module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qsql_oci.h"
       
    43 
       
    44 #include <qcoreapplication.h>
       
    45 #include <qvariant.h>
       
    46 #include <qdatetime.h>
       
    47 #include <qmetatype.h>
       
    48 #include <qregexp.h>
       
    49 #include <qshareddata.h>
       
    50 #include <qsqlerror.h>
       
    51 #include <qsqlfield.h>
       
    52 #include <qsqlindex.h>
       
    53 #include <qsqlquery.h>
       
    54 #include <qstringlist.h>
       
    55 #include <qvarlengtharray.h>
       
    56 #include <qvector.h>
       
    57 #include <qdebug.h>
       
    58 
       
    59 #include <oci.h>
       
    60 #ifdef max
       
    61 #undef max
       
    62 #endif
       
    63 #ifdef min
       
    64 #undef min
       
    65 #endif
       
    66 
       
    67 #include <stdlib.h>
       
    68 
       
    69 #define QOCI_DYNAMIC_CHUNK_SIZE 65535
       
    70 #define QOCI_PREFETCH_MEM  10240
       
    71 
       
    72 // setting this define will allow using a query from a different
       
    73 // thread than its database connection.
       
    74 // warning - this is not fully tested and can lead to race conditions
       
    75 #define QOCI_THREADED
       
    76 
       
    77 //#define QOCI_DEBUG
       
    78 
       
    79 Q_DECLARE_METATYPE(OCIEnv*)
       
    80 Q_DECLARE_METATYPE(OCIStmt*)
       
    81 
       
    82 QT_BEGIN_NAMESPACE
       
    83 
       
    84 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
       
    85 enum { QOCIEncoding = 2002 }; // AL16UTF16LE
       
    86 #else
       
    87 enum { QOCIEncoding = 2000 }; // AL16UTF16
       
    88 #endif
       
    89 
       
    90 static const ub1 CSID_NCHAR = SQLCS_NCHAR;
       
    91 static const ub2 qOraCharset = OCI_UCS2ID;
       
    92 
       
    93 typedef QVarLengthArray<sb2, 32> IndicatorArray;
       
    94 typedef QVarLengthArray<ub2, 32> SizeArray;
       
    95 
       
    96 static QByteArray qMakeOraDate(const QDateTime& dt);
       
    97 static QDateTime qMakeDate(const char* oraDate);
       
    98 static QString qOraWarn(OCIError *err, int *errorCode = 0);
       
    99 #ifndef Q_CC_SUN
       
   100 static // for some reason, Sun CC can't use qOraWarning when it's declared static
       
   101 #endif
       
   102 void qOraWarning(const char* msg, OCIError *err);
       
   103 static QSqlError qMakeError(const QString& errString, QSqlError::ErrorType type, OCIError *err);
       
   104 
       
   105 class QOCIRowId: public QSharedData
       
   106 {
       
   107 public:
       
   108     QOCIRowId(OCIEnv *env);
       
   109     ~QOCIRowId();
       
   110 
       
   111     OCIRowid *id;
       
   112 
       
   113 private:
       
   114     QOCIRowId(const QOCIRowId &other): QSharedData(other) { Q_ASSERT(false); }
       
   115 };
       
   116 
       
   117 QOCIRowId::QOCIRowId(OCIEnv *env)
       
   118     : id(0)
       
   119 {
       
   120     OCIDescriptorAlloc (env, reinterpret_cast<dvoid **>(&id),
       
   121                         OCI_DTYPE_ROWID, 0, 0);
       
   122 }
       
   123 
       
   124 QOCIRowId::~QOCIRowId()
       
   125 {
       
   126     if (id)
       
   127         OCIDescriptorFree(id, OCI_DTYPE_ROWID);
       
   128 }
       
   129 
       
   130 typedef QSharedDataPointer<QOCIRowId> QOCIRowIdPointer;
       
   131 QT_BEGIN_INCLUDE_NAMESPACE
       
   132 Q_DECLARE_METATYPE(QOCIRowIdPointer)
       
   133 QT_END_INCLUDE_NAMESPACE
       
   134 
       
   135 class QOCICols;
       
   136 
       
   137 struct QOCIResultPrivate
       
   138 {
       
   139     QOCIResultPrivate(QOCIResult *result, const QOCIDriverPrivate *driver);
       
   140     ~QOCIResultPrivate();
       
   141 
       
   142     QOCICols *cols;
       
   143     QOCIResult *q;
       
   144     OCIEnv *env;
       
   145     OCIError *err;
       
   146     OCISvcCtx *&svc;
       
   147     OCIStmt *sql;
       
   148     bool transaction;
       
   149     int serverVersion;
       
   150     int prefetchRows, prefetchMem;
       
   151 
       
   152     void setCharset(OCIBind* hbnd);
       
   153     void setStatementAttributes();
       
   154     int bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos,
       
   155                   const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList<QByteArray> &tmpStorage);
       
   156     int bindValues(QVector<QVariant> &values, IndicatorArray &indicators, SizeArray &tmpSizes,
       
   157                    QList<QByteArray> &tmpStorage);
       
   158     void outValues(QVector<QVariant> &values, IndicatorArray &indicators,
       
   159                    QList<QByteArray> &tmpStorage);
       
   160     inline bool isOutValue(int i) const
       
   161     { return q->bindValueType(i) & QSql::Out; }
       
   162     inline bool isBinaryValue(int i) const
       
   163     { return q->bindValueType(i) & QSql::Binary; }
       
   164 };
       
   165 
       
   166 void QOCIResultPrivate::setStatementAttributes()
       
   167 {
       
   168     Q_ASSERT(sql);
       
   169 
       
   170     int r = 0;
       
   171 
       
   172     if (prefetchRows >= 0) {
       
   173         r = OCIAttrSet(sql,
       
   174                        OCI_HTYPE_STMT,
       
   175                        &prefetchRows,
       
   176                        0,
       
   177                        OCI_ATTR_PREFETCH_ROWS,
       
   178                        err);
       
   179         if (r != 0)
       
   180             qOraWarning("QOCIResultPrivate::setStatementAttributes:"
       
   181                         " Couldn't set OCI_ATTR_PREFETCH_ROWS: ", err);
       
   182     }
       
   183     if (prefetchMem >= 0) {
       
   184         r = OCIAttrSet(sql,
       
   185                        OCI_HTYPE_STMT,
       
   186                        &prefetchMem,
       
   187                        0,
       
   188                        OCI_ATTR_PREFETCH_MEMORY,
       
   189                        err);
       
   190         if (r != 0)
       
   191             qOraWarning("QOCIResultPrivate::setStatementAttributes:"
       
   192                         " Couldn't set OCI_ATTR_PREFETCH_MEMORY: ", err);
       
   193     }
       
   194 }
       
   195 
       
   196 void QOCIResultPrivate::setCharset(OCIBind* hbnd)
       
   197 {
       
   198     int r = 0;
       
   199 
       
   200     Q_ASSERT(hbnd);
       
   201 
       
   202     r = OCIAttrSet(hbnd,
       
   203                    OCI_HTYPE_BIND,
       
   204                    // this const cast is safe since OCI doesn't touch
       
   205                    // the charset.
       
   206                    const_cast<void *>(static_cast<const void *>(&qOraCharset)),
       
   207                    0,
       
   208                    OCI_ATTR_CHARSET_ID,
       
   209                    err);
       
   210     if (r != 0)
       
   211         qOraWarning("QOCIResultPrivate::setCharset: Couldn't set OCI_ATTR_CHARSET_ID: ", err);
       
   212 }
       
   213 
       
   214 int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos,
       
   215                    const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList<QByteArray> &tmpStorage)
       
   216 {
       
   217     int r = OCI_SUCCESS;
       
   218     void *data = const_cast<void *>(val.constData());
       
   219 
       
   220     switch (val.type()) {
       
   221     case QVariant::ByteArray:
       
   222         r = OCIBindByPos(sql, hbnd, err,
       
   223                          pos + 1,
       
   224                          isOutValue(pos)
       
   225                             ?  const_cast<char *>(reinterpret_cast<QByteArray *>(data)->constData())
       
   226                             : reinterpret_cast<QByteArray *>(data)->data(),
       
   227                          reinterpret_cast<QByteArray *>(data)->size(),
       
   228                          SQLT_BIN, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   229         break;
       
   230     case QVariant::Time:
       
   231     case QVariant::Date:
       
   232     case QVariant::DateTime: {
       
   233         QByteArray ba = qMakeOraDate(val.toDateTime());
       
   234         r = OCIBindByPos(sql, hbnd, err,
       
   235                          pos + 1,
       
   236                          ba.data(),
       
   237                          ba.size(),
       
   238                          SQLT_DAT, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   239         tmpStorage.append(ba);
       
   240         break; }
       
   241     case QVariant::Int:
       
   242         r = OCIBindByPos(sql, hbnd, err,
       
   243                          pos + 1,
       
   244                          // if it's an out value, the data is already detached
       
   245                          // so the const cast is safe.
       
   246                          const_cast<void *>(data),
       
   247                          sizeof(int),
       
   248                          SQLT_INT, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   249         break;
       
   250     case QVariant::UInt:
       
   251         r = OCIBindByPos(sql, hbnd, err,
       
   252                          pos + 1,
       
   253                          // if it's an out value, the data is already detached
       
   254                          // so the const cast is safe.
       
   255                          const_cast<void *>(data),
       
   256                          sizeof(uint),
       
   257                          SQLT_UIN, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   258         break;
       
   259     case QVariant::Double:
       
   260         r = OCIBindByPos(sql, hbnd, err,
       
   261                          pos + 1,
       
   262                          // if it's an out value, the data is already detached
       
   263                          // so the const cast is safe.
       
   264                          const_cast<void *>(data),
       
   265                          sizeof(double),
       
   266                          SQLT_FLT, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   267         break;
       
   268     case QVariant::UserType:
       
   269         if (qVariantCanConvert<QOCIRowIdPointer>(val) && !isOutValue(pos)) {
       
   270             // use a const pointer to prevent a detach
       
   271             const QOCIRowIdPointer rptr = qVariantValue<QOCIRowIdPointer>(val);
       
   272             r = OCIBindByPos(sql, hbnd, err,
       
   273                              pos + 1,
       
   274                              // it's an IN value, so const_cast is ok
       
   275                              const_cast<OCIRowid **>(&rptr->id),
       
   276                              -1,
       
   277                              SQLT_RDD, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   278         } else {
       
   279             qWarning("Unknown bind variable");
       
   280             r = OCI_ERROR;
       
   281         }
       
   282         break;
       
   283     case QVariant::String: {
       
   284         const QString s = val.toString();
       
   285         if (isBinaryValue(pos)) {
       
   286             r = OCIBindByPos(sql, hbnd, err,
       
   287                              pos + 1,
       
   288                              const_cast<ushort *>(s.utf16()),
       
   289                              s.length() * sizeof(QChar),
       
   290                              SQLT_LNG, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   291             break;
       
   292         } else if (!isOutValue(pos)) {
       
   293             // don't detach the string
       
   294             r = OCIBindByPos(sql, hbnd, err,
       
   295                              pos + 1,
       
   296                              // safe since oracle doesn't touch OUT values
       
   297                              const_cast<ushort *>(s.utf16()),
       
   298                              (s.length() + 1) * sizeof(QChar),
       
   299                              SQLT_STR, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   300             if (r == OCI_SUCCESS)
       
   301                 setCharset(*hbnd);
       
   302             break;
       
   303         }
       
   304     } // fall through for OUT values
       
   305     default: {
       
   306         const QString s = val.toString();
       
   307         // create a deep-copy
       
   308         QByteArray ba(reinterpret_cast<const char *>(s.utf16()), (s.length() + 1) * sizeof(QChar));
       
   309         if (isOutValue(pos)) {
       
   310             ba.reserve((s.capacity() + 1) * sizeof(QChar));
       
   311             *tmpSize = ba.size();
       
   312             r = OCIBindByPos(sql, hbnd, err,
       
   313                              pos + 1,
       
   314                              ba.data(),
       
   315                              ba.capacity(),
       
   316                              SQLT_STR, indPtr, tmpSize, 0, 0, 0, OCI_DEFAULT);
       
   317         } else {
       
   318             r = OCIBindByPos(sql, hbnd, err,
       
   319                              pos + 1,
       
   320                              ba.data(),
       
   321                              ba.size(),
       
   322                              SQLT_STR, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
       
   323         }
       
   324         if (r == OCI_SUCCESS)
       
   325             setCharset(*hbnd);
       
   326         tmpStorage.append(ba);
       
   327         break;
       
   328     } // default case
       
   329     } // switch
       
   330     if (r != OCI_SUCCESS)
       
   331         qOraWarning("QOCIResultPrivate::bindValue:", err);
       
   332     return r;
       
   333 }
       
   334 
       
   335 int QOCIResultPrivate::bindValues(QVector<QVariant> &values, IndicatorArray &indicators,
       
   336                             SizeArray &tmpSizes, QList<QByteArray> &tmpStorage)
       
   337 {
       
   338     int r = OCI_SUCCESS;
       
   339     for (int i = 0; i < values.count(); ++i) {
       
   340         if (isOutValue(i))
       
   341             values[i].detach();
       
   342         const QVariant &val = values.at(i);
       
   343 
       
   344         OCIBind * hbnd = 0; // Oracle handles these automatically
       
   345         sb2 *indPtr = &indicators[i];
       
   346         *indPtr = val.isNull() ? -1 : 0;
       
   347 
       
   348         bindValue(sql, &hbnd, err, i, val, indPtr, &tmpSizes[i], tmpStorage);
       
   349     }
       
   350     return r;
       
   351 }
       
   352 
       
   353 // will assign out value and remove its temp storage.
       
   354 static void qOraOutValue(QVariant &value, QList<QByteArray> &storage)
       
   355 {
       
   356     switch (value.type()) {
       
   357     case QVariant::Time:
       
   358         value = qMakeDate(storage.takeFirst()).time();
       
   359         break;
       
   360     case QVariant::Date:
       
   361         value = qMakeDate(storage.takeFirst()).date();
       
   362         break;
       
   363     case QVariant::DateTime:
       
   364         value = qMakeDate(storage.takeFirst());
       
   365         break;
       
   366     case QVariant::String:
       
   367         value = QString::fromUtf16(
       
   368                 reinterpret_cast<const ushort *>(storage.takeFirst().constData()));
       
   369         break;
       
   370     default:
       
   371         break; //nothing
       
   372     }
       
   373 }
       
   374 
       
   375 void QOCIResultPrivate::outValues(QVector<QVariant> &values, IndicatorArray &indicators,
       
   376                             QList<QByteArray> &tmpStorage)
       
   377 {
       
   378     for (int i = 0; i < values.count(); ++i) {
       
   379 
       
   380         if (!isOutValue(i))
       
   381             continue;
       
   382 
       
   383         qOraOutValue(values[i], tmpStorage);
       
   384 
       
   385         QVariant::Type typ = values.at(i).type();
       
   386         if (indicators[i] == -1) // NULL
       
   387             values[i] = QVariant(typ);
       
   388         else
       
   389             values[i] = QVariant(typ, values.at(i).constData());
       
   390     }
       
   391 }
       
   392 
       
   393 
       
   394 struct QOCIDriverPrivate
       
   395 {
       
   396     QOCIDriverPrivate();
       
   397 
       
   398     OCIEnv *env;
       
   399     OCISvcCtx *svc;
       
   400     OCIServer *srvhp;
       
   401     OCISession *authp;
       
   402     OCIError *err;
       
   403     bool transaction;
       
   404     int serverVersion;
       
   405     ub4 prefetchRows;
       
   406     ub2 prefetchMem;
       
   407     QString user;
       
   408 
       
   409     void allocErrorHandle();
       
   410 };
       
   411 
       
   412 QOCIDriverPrivate::QOCIDriverPrivate()
       
   413     : env(0), svc(0), srvhp(0), authp(0), err(0), transaction(false), serverVersion(-1),
       
   414       prefetchRows(-1), prefetchMem(QOCI_PREFETCH_MEM)
       
   415 {
       
   416 }
       
   417 
       
   418 void QOCIDriverPrivate::allocErrorHandle()
       
   419 {
       
   420     int r = OCIHandleAlloc(env,
       
   421                            reinterpret_cast<void **>(&err),
       
   422                            OCI_HTYPE_ERROR,
       
   423                            0,
       
   424                            0);
       
   425     if (r != 0)
       
   426         qWarning("QOCIDriver: unable to allocate error handle");
       
   427 }
       
   428 
       
   429 struct OraFieldInfo
       
   430 {
       
   431     QString name;
       
   432     QVariant::Type type;
       
   433     ub1 oraIsNull;
       
   434     ub4 oraType;
       
   435     sb1 oraScale;
       
   436     ub4 oraLength; // size in bytes
       
   437     ub4 oraFieldLength; // amount of characters
       
   438     sb2 oraPrecision;
       
   439 };
       
   440 
       
   441 QString qOraWarn(OCIError *err, int *errorCode)
       
   442 {
       
   443     sb4 errcode;
       
   444     text errbuf[1024];
       
   445     errbuf[0] = 0;
       
   446     errbuf[1] = 0;
       
   447 
       
   448     OCIErrorGet(err,
       
   449                 1,
       
   450                 0,
       
   451                 &errcode,
       
   452                 errbuf,
       
   453                 sizeof(errbuf),
       
   454                 OCI_HTYPE_ERROR);
       
   455     if (errorCode)
       
   456         *errorCode = errcode;
       
   457     return QString::fromUtf16(reinterpret_cast<const ushort *>(errbuf));
       
   458 }
       
   459 
       
   460 void qOraWarning(const char* msg, OCIError *err)
       
   461 {
       
   462 #ifdef QOCI_DEBUG
       
   463     qWarning("%s %s", msg, qPrintable(qOraWarn(err)));
       
   464 #else
       
   465     Q_UNUSED(msg);
       
   466     Q_UNUSED(err);
       
   467 #endif
       
   468 }
       
   469 
       
   470 static int qOraErrorNumber(OCIError *err)
       
   471 {
       
   472     sb4 errcode;
       
   473     OCIErrorGet(err,
       
   474                 1,
       
   475                 0,
       
   476                 &errcode,
       
   477                 0,
       
   478                 0,
       
   479                 OCI_HTYPE_ERROR);
       
   480     return errcode;
       
   481 }
       
   482 
       
   483 QSqlError qMakeError(const QString& errString, QSqlError::ErrorType type, OCIError *err)
       
   484 {
       
   485     int errorCode = 0;
       
   486     const QString oraErrorString = qOraWarn(err, &errorCode);
       
   487     return QSqlError(errString, oraErrorString, type, errorCode);
       
   488 }
       
   489 
       
   490 QVariant::Type qDecodeOCIType(const QString& ocitype, QSql::NumericalPrecisionPolicy precisionPolicy)
       
   491 {
       
   492     QVariant::Type type = QVariant::Invalid;
       
   493     if (ocitype == QLatin1String("VARCHAR2") || ocitype == QLatin1String("VARCHAR")
       
   494          || ocitype.startsWith(QLatin1String("INTERVAL"))
       
   495          || ocitype == QLatin1String("CHAR") || ocitype == QLatin1String("NVARCHAR2")
       
   496          || ocitype == QLatin1String("NCHAR"))
       
   497         type = QVariant::String;
       
   498     else if (ocitype == QLatin1String("NUMBER")
       
   499              || ocitype == QLatin1String("FLOAT")
       
   500              || ocitype == QLatin1String("BINARY_FLOAT")
       
   501              || ocitype == QLatin1String("BINARY_DOUBLE")) {
       
   502         switch(precisionPolicy) {
       
   503             case QSql::LowPrecisionInt32:
       
   504                 type = QVariant::Int;
       
   505                 break;
       
   506             case QSql::LowPrecisionInt64:
       
   507                 type = QVariant::LongLong;
       
   508                 break;
       
   509             case QSql::LowPrecisionDouble:
       
   510                 type = QVariant::Double;
       
   511                 break;
       
   512             case QSql::HighPrecision:
       
   513             default:
       
   514                 type = QVariant::String;
       
   515                 break;
       
   516         }
       
   517     }
       
   518     else if (ocitype == QLatin1String("LONG") || ocitype == QLatin1String("NCLOB")
       
   519              || ocitype == QLatin1String("CLOB"))
       
   520         type = QVariant::ByteArray;
       
   521     else if (ocitype == QLatin1String("RAW") || ocitype == QLatin1String("LONG RAW")
       
   522              || ocitype == QLatin1String("ROWID") || ocitype == QLatin1String("BLOB")
       
   523              || ocitype == QLatin1String("CFILE") || ocitype == QLatin1String("BFILE"))
       
   524         type = QVariant::ByteArray;
       
   525     else if (ocitype == QLatin1String("DATE") ||  ocitype.startsWith(QLatin1String("TIME")))
       
   526         type = QVariant::DateTime;
       
   527     else if (ocitype == QLatin1String("UNDEFINED"))
       
   528         type = QVariant::Invalid;
       
   529     if (type == QVariant::Invalid)
       
   530         qWarning("qDecodeOCIType: unknown type: %s", ocitype.toLocal8Bit().constData());
       
   531     return type;
       
   532 }
       
   533 
       
   534 QVariant::Type qDecodeOCIType(int ocitype, QSql::NumericalPrecisionPolicy precisionPolicy)
       
   535 {
       
   536     QVariant::Type type = QVariant::Invalid;
       
   537     switch (ocitype) {
       
   538     case SQLT_STR:
       
   539     case SQLT_VST:
       
   540     case SQLT_CHR:
       
   541     case SQLT_AFC:
       
   542     case SQLT_VCS:
       
   543     case SQLT_AVC:
       
   544     case SQLT_RDD:
       
   545     case SQLT_LNG:
       
   546 #ifdef SQLT_INTERVAL_YM
       
   547     case SQLT_INTERVAL_YM:
       
   548 #endif
       
   549 #ifdef SQLT_INTERVAL_DS
       
   550     case SQLT_INTERVAL_DS:
       
   551 #endif
       
   552         type = QVariant::String;
       
   553         break;
       
   554     case SQLT_INT:
       
   555         type = QVariant::Int;
       
   556         break;
       
   557     case SQLT_FLT:
       
   558     case SQLT_NUM:
       
   559     case SQLT_VNU:
       
   560     case SQLT_UIN:
       
   561         switch(precisionPolicy) {
       
   562             case QSql::LowPrecisionInt32:
       
   563                 type = QVariant::Int;
       
   564                 break;
       
   565             case QSql::LowPrecisionInt64:
       
   566                 type = QVariant::LongLong;
       
   567                 break;
       
   568             case QSql::LowPrecisionDouble:
       
   569                 type = QVariant::Double;
       
   570                 break;
       
   571             case QSql::HighPrecision:
       
   572             default:
       
   573                 type = QVariant::String;
       
   574                 break;
       
   575         }
       
   576         break;
       
   577     case SQLT_VBI:
       
   578     case SQLT_BIN:
       
   579     case SQLT_LBI:
       
   580     case SQLT_LVC:
       
   581     case SQLT_LVB:
       
   582     case SQLT_BLOB:
       
   583     case SQLT_CLOB:
       
   584     case SQLT_FILE:
       
   585     case SQLT_NTY:
       
   586     case SQLT_REF:
       
   587     case SQLT_RID:
       
   588         type = QVariant::ByteArray;
       
   589         break;
       
   590     case SQLT_DAT:
       
   591     case SQLT_ODT:
       
   592 #ifdef SQLT_TIMESTAMP
       
   593     case SQLT_TIMESTAMP:
       
   594     case SQLT_TIMESTAMP_TZ:
       
   595     case SQLT_TIMESTAMP_LTZ:
       
   596 #endif
       
   597         type = QVariant::DateTime;
       
   598         break;
       
   599     default:
       
   600         type = QVariant::Invalid;
       
   601         qWarning("qDecodeOCIType: unknown OCI datatype: %d", ocitype);
       
   602         break;
       
   603     }
       
   604         return type;
       
   605 }
       
   606 
       
   607 static QSqlField qFromOraInf(const OraFieldInfo &ofi)
       
   608 {
       
   609     QSqlField f(ofi.name, ofi.type);
       
   610     f.setRequired(ofi.oraIsNull == 0);
       
   611 
       
   612     if (ofi.type == QVariant::String && ofi.oraType != SQLT_NUM && ofi.oraType != SQLT_VNU)
       
   613         f.setLength(ofi.oraFieldLength);
       
   614     else
       
   615         f.setLength(ofi.oraPrecision == 0 ? 38 : int(ofi.oraPrecision));
       
   616 
       
   617     f.setPrecision(ofi.oraScale);
       
   618     f.setSqlType(int(ofi.oraType));
       
   619     return f;
       
   620 }
       
   621 
       
   622 /*!
       
   623     \internal
       
   624 
       
   625     Convert QDateTime to the internal Oracle DATE format NB!
       
   626     It does not handle BCE dates.
       
   627 */
       
   628 QByteArray qMakeOraDate(const QDateTime& dt)
       
   629 {
       
   630     QByteArray ba;
       
   631     ba.resize(7);
       
   632     int year = dt.date().year();
       
   633     ba[0]= (year / 100) + 100; // century
       
   634     ba[1]= (year % 100) + 100; // year
       
   635     ba[2]= dt.date().month();
       
   636     ba[3]= dt.date().day();
       
   637     ba[4]= dt.time().hour() + 1;
       
   638     ba[5]= dt.time().minute() + 1;
       
   639     ba[6]= dt.time().second() + 1;
       
   640     return ba;
       
   641 }
       
   642 
       
   643 QDateTime qMakeDate(const char* oraDate)
       
   644 {
       
   645     int century = oraDate[0];
       
   646     if(century >= 100){
       
   647         int year    = uchar(oraDate[1]);
       
   648         year = ((century-100)*100) + (year-100);
       
   649         int month = oraDate[2];
       
   650         int day   = oraDate[3];
       
   651         int hour  = oraDate[4] - 1;
       
   652         int min   = oraDate[5] - 1;
       
   653         int sec   = oraDate[6] - 1;
       
   654         return QDateTime(QDate(year,month,day), QTime(hour,min,sec));
       
   655     }
       
   656     return QDateTime();
       
   657 }
       
   658 
       
   659 class QOCICols
       
   660 {
       
   661 public:
       
   662     QOCICols(int size, QOCIResultPrivate* dp);
       
   663     ~QOCICols();
       
   664     void setCharset(OCIDefine* dfn);
       
   665     int readPiecewise(QVector<QVariant> &values, int index = 0);
       
   666     int readLOBs(QVector<QVariant> &values, int index = 0);
       
   667     int fieldFromDefine(OCIDefine* d);
       
   668     void getValues(QVector<QVariant> &v, int index);
       
   669     inline int size() { return fieldInf.size(); }
       
   670     static bool execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, bool arrayBind);
       
   671 
       
   672     QSqlRecord rec;
       
   673 
       
   674 private:
       
   675     char* create(int position, int size);
       
   676     OCILobLocator ** createLobLocator(int position, OCIEnv* env);
       
   677     OraFieldInfo qMakeOraField(const QOCIResultPrivate* p, OCIParam* param) const;
       
   678 
       
   679     class OraFieldInf
       
   680     {
       
   681     public:
       
   682         OraFieldInf(): data(0), len(0), ind(0), typ(QVariant::Invalid), oraType(0), def(0), lob(0)
       
   683         {}
       
   684         ~OraFieldInf();
       
   685         char *data;
       
   686         int len;
       
   687         sb2 ind;
       
   688         QVariant::Type typ;
       
   689         ub4 oraType;
       
   690         OCIDefine *def;
       
   691         OCILobLocator *lob;
       
   692     };
       
   693 
       
   694     QVector<OraFieldInf> fieldInf;
       
   695     const QOCIResultPrivate *const d;
       
   696 };
       
   697 
       
   698 QOCICols::OraFieldInf::~OraFieldInf()
       
   699 {
       
   700     delete [] data;
       
   701     if (lob) {
       
   702         int r = OCIDescriptorFree(lob, OCI_DTYPE_LOB);
       
   703         if (r != 0)
       
   704             qWarning("QOCICols: Cannot free LOB descriptor");
       
   705     }
       
   706 }
       
   707 
       
   708 QOCICols::QOCICols(int size, QOCIResultPrivate* dp)
       
   709     : fieldInf(size), d(dp)
       
   710 {
       
   711     ub4 dataSize = 0;
       
   712     OCIDefine* dfn = 0;
       
   713     int r;
       
   714 
       
   715     OCIParam* param = 0;
       
   716     sb4 parmStatus = 0;
       
   717     ub4 count = 1;
       
   718     int idx = 0;
       
   719     parmStatus = OCIParamGet(d->sql,
       
   720                              OCI_HTYPE_STMT,
       
   721                              d->err,
       
   722                              reinterpret_cast<void **>(&param),
       
   723                              count);
       
   724 
       
   725     while (parmStatus == OCI_SUCCESS) {
       
   726         OraFieldInfo ofi = qMakeOraField(d, param);
       
   727         if (ofi.oraType == SQLT_RDD)
       
   728             dataSize = 50;
       
   729 #ifdef SQLT_INTERVAL_YM
       
   730 #ifdef SQLT_INTERVAL_DS
       
   731         else if (ofi.oraType == SQLT_INTERVAL_YM || ofi.oraType == SQLT_INTERVAL_DS)
       
   732             // since we are binding interval datatype as string,
       
   733             // we are not interested in the number of bytes but characters.
       
   734             dataSize = 50;  // magic number
       
   735 #endif //SQLT_INTERVAL_DS
       
   736 #endif //SQLT_INTERVAL_YM
       
   737         else if (ofi.oraType == SQLT_NUM || ofi.oraType == SQLT_VNU){
       
   738             if (ofi.oraPrecision > 0)
       
   739                 dataSize = (ofi.oraPrecision + 1) * sizeof(utext);
       
   740             else
       
   741                 dataSize = (38 + 1) * sizeof(utext);
       
   742         }
       
   743         else
       
   744             dataSize = ofi.oraLength;
       
   745 
       
   746         fieldInf[idx].typ = ofi.type;
       
   747         fieldInf[idx].oraType = ofi.oraType;
       
   748         rec.append(qFromOraInf(ofi));
       
   749 
       
   750         switch (ofi.type) {
       
   751         case QVariant::DateTime:
       
   752             r = OCIDefineByPos(d->sql,
       
   753                                &dfn,
       
   754                                d->err,
       
   755                                count,
       
   756                                create(idx, dataSize+1),
       
   757                                dataSize+1,
       
   758                                SQLT_DAT,
       
   759                                &(fieldInf[idx].ind),
       
   760                                0, 0, OCI_DEFAULT);
       
   761             break;
       
   762         case QVariant::Double:
       
   763             r = OCIDefineByPos(d->sql,
       
   764                                &dfn,
       
   765                                d->err,
       
   766                                count,
       
   767                                create(idx, sizeof(double) - 1),
       
   768                                sizeof(double),
       
   769                                SQLT_FLT,
       
   770                                &(fieldInf[idx].ind),
       
   771                                0, 0, OCI_DEFAULT);
       
   772             break;
       
   773         case QVariant::Int:
       
   774             r = OCIDefineByPos(d->sql,
       
   775                                &dfn,
       
   776                                d->err,
       
   777                                count,
       
   778                                create(idx, sizeof(qint32) - 1),
       
   779                                sizeof(qint32),
       
   780                                SQLT_INT,
       
   781                                &(fieldInf[idx].ind),
       
   782                                0, 0, OCI_DEFAULT);
       
   783             break;
       
   784         case QVariant::LongLong:
       
   785             r = OCIDefineByPos(d->sql,
       
   786                                &dfn,
       
   787                                d->err,
       
   788                                count,
       
   789                                create(idx, sizeof(OCINumber)),
       
   790                                sizeof(OCINumber),
       
   791                                SQLT_VNU,
       
   792                                &(fieldInf[idx].ind),
       
   793                                0, 0, OCI_DEFAULT);
       
   794             break;
       
   795         case QVariant::ByteArray:
       
   796             // RAW and LONG RAW fields can't be bound to LOB locators
       
   797             if (ofi.oraType == SQLT_BIN) {
       
   798 //                                qDebug("binding SQLT_BIN");
       
   799                 r = OCIDefineByPos(d->sql,
       
   800                                    &dfn,
       
   801                                    d->err,
       
   802                                    count,
       
   803                                    create(idx, dataSize),
       
   804                                    dataSize,
       
   805                                    SQLT_BIN,
       
   806                                    &(fieldInf[idx].ind),
       
   807                                    0, 0, OCI_DYNAMIC_FETCH);
       
   808             } else if (ofi.oraType == SQLT_LBI) {
       
   809 //                                    qDebug("binding SQLT_LBI");
       
   810                 r = OCIDefineByPos(d->sql,
       
   811                                    &dfn,
       
   812                                    d->err,
       
   813                                    count,
       
   814                                    0,
       
   815                                    SB4MAXVAL,
       
   816                                    SQLT_LBI,
       
   817                                    &(fieldInf[idx].ind),
       
   818                                    0, 0, OCI_DYNAMIC_FETCH);
       
   819             } else if (ofi.oraType == SQLT_CLOB) {
       
   820                 r = OCIDefineByPos(d->sql,
       
   821                                    &dfn,
       
   822                                    d->err,
       
   823                                    count,
       
   824                                    createLobLocator(idx, d->env),
       
   825                                    -1,
       
   826                                    SQLT_CLOB,
       
   827                                    &(fieldInf[idx].ind),
       
   828                                    0, 0, OCI_DEFAULT);
       
   829             } else {
       
   830 //                 qDebug("binding SQLT_BLOB");
       
   831                 r = OCIDefineByPos(d->sql,
       
   832                                    &dfn,
       
   833                                    d->err,
       
   834                                    count,
       
   835                                    createLobLocator(idx, d->env),
       
   836                                    -1,
       
   837                                    SQLT_BLOB,
       
   838                                    &(fieldInf[idx].ind),
       
   839                                    0, 0, OCI_DEFAULT);
       
   840             }
       
   841             break;
       
   842         case QVariant::String:
       
   843             if (ofi.oraType == SQLT_LNG) {
       
   844                 r = OCIDefineByPos(d->sql,
       
   845                         &dfn,
       
   846                         d->err,
       
   847                         count,
       
   848                         0,
       
   849                         SB4MAXVAL,
       
   850                         SQLT_LNG,
       
   851                         &(fieldInf[idx].ind),
       
   852                         0, 0, OCI_DYNAMIC_FETCH);
       
   853             } else {
       
   854                 dataSize += dataSize + sizeof(QChar);
       
   855                 //qDebug("OCIDefineByPosStr(%d): %d", count, dataSize);
       
   856                 r = OCIDefineByPos(d->sql,
       
   857                         &dfn,
       
   858                         d->err,
       
   859                         count,
       
   860                         create(idx, dataSize),
       
   861                         dataSize,
       
   862                         SQLT_STR,
       
   863                         &(fieldInf[idx].ind),
       
   864                         0, 0, OCI_DEFAULT);
       
   865                 if (r == 0)
       
   866                     setCharset(dfn);
       
   867             }
       
   868            break;
       
   869         default:
       
   870             // this should make enough space even with character encoding
       
   871             dataSize = (dataSize + 1) * sizeof(utext) ;
       
   872             //qDebug("OCIDefineByPosDef(%d): %d", count, dataSize);
       
   873             r = OCIDefineByPos(d->sql,
       
   874                                 &dfn,
       
   875                                 d->err,
       
   876                                 count,
       
   877                                 create(idx, dataSize),
       
   878                                 dataSize+1,
       
   879                                 SQLT_STR,
       
   880                                 &(fieldInf[idx].ind),
       
   881                                 0, 0, OCI_DEFAULT);
       
   882             break;
       
   883         }
       
   884         if (r != 0)
       
   885             qOraWarning("QOCICols::bind:", d->err);
       
   886         fieldInf[idx].def = dfn;
       
   887         ++count;
       
   888         ++idx;
       
   889         parmStatus = OCIParamGet(d->sql,
       
   890                                   OCI_HTYPE_STMT,
       
   891                                   d->err,
       
   892                                   reinterpret_cast<void **>(&param),
       
   893                                   count);
       
   894     }
       
   895 }
       
   896 
       
   897 QOCICols::~QOCICols()
       
   898 {
       
   899 }
       
   900 
       
   901 char* QOCICols::create(int position, int size)
       
   902 {
       
   903     char* c = new char[size+1];
       
   904     // Oracle may not fill fixed width fields
       
   905     memset(c, 0, size+1);
       
   906     fieldInf[position].data = c;
       
   907     fieldInf[position].len = size;
       
   908     return c;
       
   909 }
       
   910 
       
   911 OCILobLocator **QOCICols::createLobLocator(int position, OCIEnv* env)
       
   912 {
       
   913     OCILobLocator *& lob = fieldInf[position].lob;
       
   914     int r = OCIDescriptorAlloc(env,
       
   915                                reinterpret_cast<void **>(&lob),
       
   916                                OCI_DTYPE_LOB,
       
   917                                0,
       
   918                                0);
       
   919     if (r != 0) {
       
   920         qWarning("QOCICols: Cannot create LOB locator");
       
   921         lob = 0;
       
   922     }
       
   923     return &lob;
       
   924 }
       
   925 
       
   926 void QOCICols::setCharset(OCIDefine* dfn)
       
   927 {
       
   928     int r = 0;
       
   929 
       
   930     Q_ASSERT(dfn);
       
   931 
       
   932     r = OCIAttrSet(dfn,
       
   933                    OCI_HTYPE_DEFINE,
       
   934                    // this const cast is safe since OCI doesn't touch
       
   935                    // the charset.
       
   936                    const_cast<void *>(static_cast<const void *>(&qOraCharset)),
       
   937                    0,
       
   938                    OCI_ATTR_CHARSET_ID,
       
   939                    d->err);
       
   940         if (r != 0)
       
   941             qOraWarning("QOCICols::setCharset: Couldn't set OCI_ATTR_CHARSET_ID: ", d->err);
       
   942 }
       
   943 
       
   944 int QOCICols::readPiecewise(QVector<QVariant> &values, int index)
       
   945 {
       
   946     OCIDefine*     dfn;
       
   947     ub4            typep;
       
   948     ub1            in_outp;
       
   949     ub4            iterp;
       
   950     ub4            idxp;
       
   951     ub1            piecep;
       
   952     sword          status;
       
   953     text           col [QOCI_DYNAMIC_CHUNK_SIZE+1];
       
   954     int            fieldNum = -1;
       
   955     int            r = 0;
       
   956     bool           nullField;
       
   957 
       
   958     do {
       
   959         r = OCIStmtGetPieceInfo(d->sql, d->err, reinterpret_cast<void **>(&dfn), &typep,
       
   960                                  &in_outp, &iterp, &idxp, &piecep);
       
   961         if (r != OCI_SUCCESS)
       
   962             qOraWarning("OCIResultPrivate::readPiecewise: unable to get piece info:", d->err);
       
   963         fieldNum = fieldFromDefine(dfn);
       
   964         bool isStringField = fieldInf.at(fieldNum).oraType == SQLT_LNG;
       
   965         ub4 chunkSize = QOCI_DYNAMIC_CHUNK_SIZE;
       
   966         nullField = false;
       
   967         r  = OCIStmtSetPieceInfo(dfn, OCI_HTYPE_DEFINE,
       
   968                                  d->err, col,
       
   969                                  &chunkSize, piecep, NULL, NULL);
       
   970         if (r != OCI_SUCCESS)
       
   971             qOraWarning("OCIResultPrivate::readPiecewise: unable to set piece info:", d->err);
       
   972         status = OCIStmtFetch (d->sql, d->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
       
   973         if (status == -1) {
       
   974             sb4 errcode;
       
   975             OCIErrorGet(d->err, 1, 0, &errcode, 0, 0,OCI_HTYPE_ERROR);
       
   976             switch (errcode) {
       
   977             case 1405: /* NULL */
       
   978                 nullField = true;
       
   979                 break;
       
   980             default:
       
   981                 qOraWarning("OCIResultPrivate::readPiecewise: unable to fetch next:", d->err);
       
   982                 break;
       
   983             }
       
   984         }
       
   985         if (status == OCI_NO_DATA)
       
   986             break;
       
   987         if (nullField || !chunkSize) {
       
   988             fieldInf[fieldNum].ind = -1;
       
   989         } else {
       
   990             if (isStringField) {
       
   991                 QString str = values.at(fieldNum + index).toString();
       
   992                 str += QString::fromUtf16(reinterpret_cast<const ushort *>(col),
       
   993                                           chunkSize / 2);
       
   994                 values[fieldNum + index] = str;
       
   995                 fieldInf[fieldNum].ind = 0;
       
   996             } else {
       
   997                 QByteArray ba = values.at(fieldNum + index).toByteArray();
       
   998                 int sz = ba.size();
       
   999                 ba.resize(sz + chunkSize);
       
  1000                 memcpy(ba.data() + sz, reinterpret_cast<char *>(col), chunkSize);
       
  1001                 values[fieldNum + index] = ba;
       
  1002                 fieldInf[fieldNum].ind = 0;
       
  1003             }
       
  1004         }
       
  1005     } while (status == OCI_SUCCESS_WITH_INFO || status == OCI_NEED_DATA);
       
  1006     return r;
       
  1007 }
       
  1008 
       
  1009 OraFieldInfo QOCICols::qMakeOraField(const QOCIResultPrivate* p, OCIParam* param) const
       
  1010 {
       
  1011     OraFieldInfo ofi;
       
  1012     ub2 colType(0);
       
  1013     text *colName = 0;
       
  1014     ub4 colNameLen(0);
       
  1015     sb1 colScale(0);
       
  1016     ub2 colLength(0);
       
  1017     ub2 colFieldLength(0);
       
  1018     sb2 colPrecision(0);
       
  1019     ub1 colIsNull(0);
       
  1020     int r(0);
       
  1021     QVariant::Type type(QVariant::Invalid);
       
  1022 
       
  1023     r = OCIAttrGet(param,
       
  1024                    OCI_DTYPE_PARAM,
       
  1025                    &colType,
       
  1026                    0,
       
  1027                    OCI_ATTR_DATA_TYPE,
       
  1028                    p->err);
       
  1029     if (r != 0)
       
  1030         qOraWarning("qMakeOraField:", p->err);
       
  1031 
       
  1032     r = OCIAttrGet(param,
       
  1033                    OCI_DTYPE_PARAM,
       
  1034                    &colName,
       
  1035                    &colNameLen,
       
  1036                    OCI_ATTR_NAME,
       
  1037                    p->err);
       
  1038     if (r != 0)
       
  1039         qOraWarning("qMakeOraField:", p->err);
       
  1040 
       
  1041     r = OCIAttrGet(param,
       
  1042                    OCI_DTYPE_PARAM,
       
  1043                    &colLength,
       
  1044                    0,
       
  1045                    OCI_ATTR_DATA_SIZE, /* in bytes */
       
  1046                    p->err);
       
  1047     if (r != 0)
       
  1048         qOraWarning("qMakeOraField:", p->err);
       
  1049 
       
  1050 #ifdef OCI_ATTR_CHAR_SIZE
       
  1051     r = OCIAttrGet(param,
       
  1052                    OCI_DTYPE_PARAM,
       
  1053                    &colFieldLength,
       
  1054                    0,
       
  1055                    OCI_ATTR_CHAR_SIZE,
       
  1056                    p->err);
       
  1057     if (r != 0)
       
  1058         qOraWarning("qMakeOraField:", p->err);
       
  1059 #else
       
  1060     // for Oracle8.
       
  1061     colFieldLength = colLength;
       
  1062 #endif
       
  1063 
       
  1064     r = OCIAttrGet(param,
       
  1065                    OCI_DTYPE_PARAM,
       
  1066                    &colPrecision,
       
  1067                    0,
       
  1068                    OCI_ATTR_PRECISION,
       
  1069                    p->err);
       
  1070     if (r != 0)
       
  1071         qOraWarning("qMakeOraField:", p->err);
       
  1072 
       
  1073     r = OCIAttrGet(param,
       
  1074                    OCI_DTYPE_PARAM,
       
  1075                    &colScale,
       
  1076                    0,
       
  1077                    OCI_ATTR_SCALE,
       
  1078                    p->err);
       
  1079     if (r != 0)
       
  1080         qOraWarning("qMakeOraField:", p->err);
       
  1081     r = OCIAttrGet(param,
       
  1082                    OCI_DTYPE_PARAM,
       
  1083                    &colType,
       
  1084                    0,
       
  1085                    OCI_ATTR_DATA_TYPE,
       
  1086                    p->err);
       
  1087     if (r != 0)
       
  1088         qOraWarning("qMakeOraField:", p->err);
       
  1089     r = OCIAttrGet(param,
       
  1090                    OCI_DTYPE_PARAM,
       
  1091                    &colIsNull,
       
  1092                    0,
       
  1093                    OCI_ATTR_IS_NULL,
       
  1094                    p->err);
       
  1095     if (r != 0)
       
  1096         qOraWarning("qMakeOraField:", p->err);
       
  1097 
       
  1098     type = qDecodeOCIType(colType, p->q->numericalPrecisionPolicy());
       
  1099 
       
  1100     if (type == QVariant::Int) {
       
  1101         if (colLength == 22 && colPrecision == 0 && colScale == 0)
       
  1102             type = QVariant::String;
       
  1103         if (colScale > 0)
       
  1104             type = QVariant::String;
       
  1105     }
       
  1106 
       
  1107     // bind as double if the precision policy asks for it
       
  1108     if (((colType == SQLT_FLT) || (colType == SQLT_NUM))
       
  1109             && (p->q->numericalPrecisionPolicy() == QSql::LowPrecisionDouble)) {
       
  1110         type = QVariant::Double;
       
  1111     }
       
  1112 
       
  1113     // bind as int32 or int64 if the precision policy asks for it
       
  1114     if ((colType == SQLT_NUM) || (colType == SQLT_VNU) || (colType == SQLT_UIN)
       
  1115             || (colType == SQLT_INT)) {
       
  1116         if (p->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
       
  1117             type = QVariant::LongLong;
       
  1118         else if (p->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
       
  1119             type = QVariant::Int;
       
  1120     }
       
  1121 
       
  1122     if (colType == SQLT_BLOB)
       
  1123         colLength = 0;
       
  1124 
       
  1125     // colNameLen is length in bytes
       
  1126     ofi.name = QString(reinterpret_cast<const QChar*>(colName), colNameLen / 2);
       
  1127     ofi.type = type;
       
  1128     ofi.oraType = colType;
       
  1129     ofi.oraFieldLength = colFieldLength;
       
  1130     ofi.oraLength = colLength;
       
  1131     ofi.oraScale = colScale;
       
  1132     ofi.oraPrecision = colPrecision;
       
  1133     ofi.oraIsNull = colIsNull;
       
  1134 
       
  1135     return ofi;
       
  1136 }
       
  1137 
       
  1138 struct QOCIBatchColumn
       
  1139 {
       
  1140     inline QOCIBatchColumn()
       
  1141         : bindh(0), bindAs(0), maxLen(0), recordCount(0),
       
  1142           data(0), lengths(0), indicators(0), maxarr_len(0), curelep(0) {}
       
  1143 
       
  1144     OCIBind* bindh;
       
  1145     ub2 bindAs;
       
  1146     ub4 maxLen;
       
  1147     ub4 recordCount;
       
  1148     char* data;
       
  1149     ub2* lengths;
       
  1150     sb2* indicators;
       
  1151     ub4 maxarr_len;
       
  1152     ub4 curelep;
       
  1153 };
       
  1154 
       
  1155 struct QOCIBatchCleanupHandler
       
  1156 {
       
  1157     inline QOCIBatchCleanupHandler(QVector<QOCIBatchColumn> &columns)
       
  1158         : col(columns) {}
       
  1159 
       
  1160     ~QOCIBatchCleanupHandler()
       
  1161     {
       
  1162         // deleting storage, length and indicator arrays
       
  1163         for ( int j = 0; j < col.count(); ++j){
       
  1164             delete[] col[j].lengths;
       
  1165             delete[] col[j].indicators;
       
  1166             delete[] col[j].data;
       
  1167         }
       
  1168     }
       
  1169 
       
  1170     QVector<QOCIBatchColumn> &col;
       
  1171 };
       
  1172 
       
  1173 bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, bool arrayBind)
       
  1174 {
       
  1175     int columnCount = boundValues.count();
       
  1176     if (boundValues.isEmpty() || columnCount == 0)
       
  1177         return false;
       
  1178 
       
  1179 #ifdef QOCI_DEBUG
       
  1180     qDebug() << "columnCount:" << columnCount << boundValues;
       
  1181 #endif
       
  1182 
       
  1183     int i;
       
  1184     sword r;
       
  1185 
       
  1186     QVarLengthArray<QVariant::Type> fieldTypes;
       
  1187     for (i = 0; i < columnCount; ++i) {
       
  1188         QVariant::Type tp = boundValues.at(i).type();
       
  1189         fieldTypes.append(tp == QVariant::List ? boundValues.at(i).toList().value(0).type()
       
  1190                                                : tp);
       
  1191     }
       
  1192 
       
  1193     QList<QByteArray> tmpStorage;
       
  1194     SizeArray tmpSizes(columnCount);
       
  1195     QVector<QOCIBatchColumn> columns(columnCount);
       
  1196     QOCIBatchCleanupHandler cleaner(columns);
       
  1197 
       
  1198     // figuring out buffer sizes
       
  1199     for (i = 0; i < columnCount; ++i) {
       
  1200 
       
  1201         if (boundValues.at(i).type() != QVariant::List) {
       
  1202 
       
  1203             // not a list - create a deep-copy of the single value
       
  1204             QOCIBatchColumn &singleCol = columns[i];
       
  1205             singleCol.indicators = new sb2[1];
       
  1206             *singleCol.indicators = boundValues.at(i).isNull() ? -1 : 0;
       
  1207 
       
  1208             r = d->bindValue(d->sql, &singleCol.bindh, d->err, i,
       
  1209                              boundValues.at(i), singleCol.indicators, &tmpSizes[i], tmpStorage);
       
  1210 
       
  1211             if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1212                 qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err);
       
  1213                 d->q->setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1214                          "Unable to bind column for batch execute"),
       
  1215                          QSqlError::StatementError, d->err));
       
  1216                 return false;
       
  1217             }
       
  1218             continue;
       
  1219         }
       
  1220 
       
  1221         QOCIBatchColumn &col = columns[i];
       
  1222         col.recordCount = boundValues.at(i).toList().count();
       
  1223 
       
  1224         col.lengths = new ub2[col.recordCount];
       
  1225         col.indicators = new sb2[col.recordCount];
       
  1226         col.maxarr_len = col.recordCount;
       
  1227         col.curelep = col.recordCount;
       
  1228 
       
  1229         switch (fieldTypes[i]) {
       
  1230             case QVariant::Time:
       
  1231             case QVariant::Date:
       
  1232             case QVariant::DateTime:
       
  1233                 col.bindAs = SQLT_DAT;
       
  1234                 col.maxLen = 7;
       
  1235                 break;
       
  1236 
       
  1237             case QVariant::Int:
       
  1238                 col.bindAs = SQLT_INT;
       
  1239                 col.maxLen = sizeof(int);
       
  1240                 break;
       
  1241 
       
  1242             case QVariant::UInt:
       
  1243                 col.bindAs = SQLT_UIN;
       
  1244                 col.maxLen = sizeof(uint);
       
  1245                 break;
       
  1246 
       
  1247             case QVariant::Double:
       
  1248                 col.bindAs = SQLT_FLT;
       
  1249                 col.maxLen = sizeof(double);
       
  1250                 break;
       
  1251 
       
  1252             case QVariant::UserType:
       
  1253                 col.bindAs = SQLT_RDD;
       
  1254                 col.maxLen = sizeof(OCIRowid*);
       
  1255                 break;
       
  1256 
       
  1257             case QVariant::String: {
       
  1258                 col.bindAs = SQLT_STR;
       
  1259                 for (uint j = 0; j < col.recordCount; ++j) {
       
  1260                     uint len;
       
  1261                     if(d->isOutValue(i))
       
  1262                         len = boundValues.at(i).toList().at(j).toString().capacity() + 1;
       
  1263                     else
       
  1264                         len = boundValues.at(i).toList().at(j).toString().length() + 1;
       
  1265                     if (len > col.maxLen)
       
  1266                         col.maxLen = len;
       
  1267                 }
       
  1268                 col.maxLen *= sizeof(QChar);
       
  1269                 break; }
       
  1270 
       
  1271             case QVariant::ByteArray:
       
  1272             default: {
       
  1273                 col.bindAs = SQLT_LBI;
       
  1274                 for (uint j = 0; j < col.recordCount; ++j) {
       
  1275                     if(d->isOutValue(i))
       
  1276                         col.lengths[j] = boundValues.at(i).toList().at(j).toByteArray().capacity();
       
  1277                     else
       
  1278                         col.lengths[j] = boundValues.at(i).toList().at(j).toByteArray().size();
       
  1279                     if (col.lengths[j] > col.maxLen)
       
  1280                         col.maxLen = col.lengths[j];
       
  1281                 }
       
  1282                 break; }
       
  1283         }
       
  1284 
       
  1285         col.data = new char[col.maxLen * col.recordCount];
       
  1286         memset(col.data, 0, col.maxLen * col.recordCount);
       
  1287 
       
  1288         // we may now populate column with data
       
  1289         for (uint row = 0; row < col.recordCount; ++row) {
       
  1290             const QVariant &val = boundValues.at(i).toList().at(row);
       
  1291 
       
  1292             if (val.isNull()){
       
  1293                 columns[i].indicators[row] = -1;
       
  1294                 columns[i].lengths[row] = 0;
       
  1295             } else {
       
  1296                 columns[i].indicators[row] = 0;
       
  1297                 char *dataPtr = columns[i].data + (columns[i].maxLen * row);
       
  1298                 switch (fieldTypes[i]) {
       
  1299                     case QVariant::Time:
       
  1300                     case QVariant::Date:
       
  1301                     case QVariant::DateTime:{
       
  1302                         columns[i].lengths[row] = columns[i].maxLen;
       
  1303                         const QByteArray ba = qMakeOraDate(val.toDateTime());
       
  1304                         Q_ASSERT(ba.size() == int(columns[i].maxLen));
       
  1305                         memcpy(dataPtr, ba.constData(), columns[i].maxLen);
       
  1306                         break;
       
  1307                     }
       
  1308                     case QVariant::Int:
       
  1309                         columns[i].lengths[row] = columns[i].maxLen;
       
  1310                         *reinterpret_cast<int*>(dataPtr) = val.toInt();
       
  1311                         break;
       
  1312 
       
  1313                     case QVariant::UInt:
       
  1314                         columns[i].lengths[row] = columns[i].maxLen;
       
  1315                         *reinterpret_cast<uint*>(dataPtr) = val.toUInt();
       
  1316                         break;
       
  1317 
       
  1318                     case QVariant::Double:
       
  1319                          columns[i].lengths[row] = columns[i].maxLen;
       
  1320                          *reinterpret_cast<double*>(dataPtr) = val.toDouble();
       
  1321                          break;
       
  1322 
       
  1323                     case QVariant::String: {
       
  1324                         const QString s = val.toString();
       
  1325                         columns[i].lengths[row] = (s.length() + 1) * sizeof(QChar);
       
  1326                         memcpy(dataPtr, s.utf16(), columns[i].lengths[row]);
       
  1327                         break;
       
  1328                     }
       
  1329                     case QVariant::UserType:
       
  1330                         if (qVariantCanConvert<QOCIRowIdPointer>(val)) {
       
  1331                             const QOCIRowIdPointer rptr = qVariantValue<QOCIRowIdPointer>(val);
       
  1332                             *reinterpret_cast<OCIRowid**>(dataPtr) = rptr->id;
       
  1333                             columns[i].lengths[row] = 0;
       
  1334                             break;
       
  1335                         }
       
  1336                     case QVariant::ByteArray:
       
  1337                     default: {
       
  1338                         const QByteArray ba = val.toByteArray();
       
  1339                         columns[i].lengths[row] = ba.size();
       
  1340                         memcpy(dataPtr, ba.constData(), ba.size());
       
  1341                         break;
       
  1342                     }
       
  1343                 }
       
  1344             }
       
  1345         }
       
  1346 
       
  1347         QOCIBatchColumn &bindColumn = columns[i];
       
  1348 
       
  1349 #ifdef QOCI_DEBUG
       
  1350             qDebug("OCIBindByPos(%p, %p, %p, %d, %p, %d, %d, %p, %p, 0, %d, %p, OCI_DEFAULT)",
       
  1351             d->sql, &bindColumn.bindh, d->err, i + 1, bindColumn.data,
       
  1352             bindColumn.maxLen, bindColumn.bindAs, bindColumn.indicators, bindColumn.lengths,
       
  1353             arrayBind ? bindColumn.maxarr_len : 0, arrayBind ? &bindColumn.curelep : 0);
       
  1354 
       
  1355         for (int ii = 0; ii < (int)bindColumn.recordCount; ++ii) {
       
  1356             qDebug(" record %d: indicator %d, length %d", ii, bindColumn.indicators[ii],
       
  1357                     bindColumn.lengths[ii]);
       
  1358         }
       
  1359 #endif
       
  1360 
       
  1361 
       
  1362         // binding the column
       
  1363         r = OCIBindByPos(
       
  1364                 d->sql, &bindColumn.bindh, d->err, i + 1,
       
  1365                 bindColumn.data,
       
  1366                 bindColumn.maxLen,
       
  1367                 bindColumn.bindAs,
       
  1368                 bindColumn.indicators,
       
  1369                 bindColumn.lengths,
       
  1370                 0,
       
  1371                 arrayBind ? bindColumn.maxarr_len : 0,
       
  1372                 arrayBind ? &bindColumn.curelep : 0,
       
  1373                 OCI_DEFAULT);
       
  1374 
       
  1375 #ifdef QOCI_DEBUG
       
  1376         qDebug("After OCIBindByPos: r = %d, bindh = %p", r, bindColumn.bindh);
       
  1377 #endif
       
  1378 
       
  1379         if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1380             qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err);
       
  1381             d->q->setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1382                      "Unable to bind column for batch execute"),
       
  1383                      QSqlError::StatementError, d->err));
       
  1384             return false;
       
  1385         }
       
  1386 
       
  1387         r = OCIBindArrayOfStruct (
       
  1388                 columns[i].bindh, d->err,
       
  1389                 columns[i].maxLen,
       
  1390                 sizeof(columns[i].indicators[0]),
       
  1391                 sizeof(columns[i].lengths[0]),
       
  1392                 0);
       
  1393 
       
  1394         if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1395             qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err);
       
  1396             d->q->setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1397                      "Unable to bind column for batch execute"),
       
  1398                      QSqlError::StatementError, d->err));
       
  1399             return false;
       
  1400         }
       
  1401     }
       
  1402 
       
  1403     //finaly we can execute
       
  1404     r = OCIStmtExecute(d->svc, d->sql, d->err,
       
  1405                        arrayBind ? 1 : columns[0].recordCount,
       
  1406                        0, NULL, NULL,
       
  1407                        d->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS);
       
  1408 
       
  1409     if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1410         qOraWarning("QOCIPrivate::execBatch: unable to execute batch statement:", d->err);
       
  1411         d->q->setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1412                         "Unable to execute batch statement"),
       
  1413                         QSqlError::StatementError, d->err));
       
  1414         return false;
       
  1415     }
       
  1416 
       
  1417     // for out parameters we copy data back to value vector
       
  1418     for (i = 0; i < columnCount; ++i) {
       
  1419 
       
  1420         if (!d->isOutValue(i))
       
  1421             continue;
       
  1422 
       
  1423         QVariant::Type tp = boundValues.at(i).type();
       
  1424         if (tp != QVariant::List) {
       
  1425             qOraOutValue(boundValues[i], tmpStorage);
       
  1426             if (*columns[i].indicators == -1)
       
  1427                 boundValues[i] = QVariant(tp);
       
  1428             continue;
       
  1429         }
       
  1430 
       
  1431         QVariantList *list = static_cast<QVariantList *>(const_cast<void*>(boundValues.at(i).data()));
       
  1432 
       
  1433         char* data = columns[i].data;
       
  1434         for (uint r = 0; r < columns[i].recordCount; ++r){
       
  1435 
       
  1436             if (columns[i].indicators[r] == -1) {
       
  1437                 (*list)[r] = QVariant();
       
  1438                 continue;
       
  1439             }
       
  1440 
       
  1441             switch(columns[i].bindAs) {
       
  1442 
       
  1443                 case SQLT_DAT:
       
  1444                     (*list)[r] =  qMakeDate(data + r * columns[i].maxLen);
       
  1445                     break;
       
  1446 
       
  1447                 case SQLT_INT:
       
  1448                     (*list)[r] =  *reinterpret_cast<int*>(data + r * columns[i].maxLen);
       
  1449                     break;
       
  1450 
       
  1451                 case SQLT_UIN:
       
  1452                     (*list)[r] =  *reinterpret_cast<uint*>(data + r * columns[i].maxLen);
       
  1453                     break;
       
  1454 
       
  1455                 case SQLT_FLT:
       
  1456                     (*list)[r] =  *reinterpret_cast<double*>(data + r * columns[i].maxLen);
       
  1457                     break;
       
  1458 
       
  1459                 case SQLT_STR:
       
  1460                     (*list)[r] =  QString::fromUtf16(reinterpret_cast<ushort *>(data
       
  1461                                                                 + r * columns[i].maxLen));
       
  1462                     break;
       
  1463 
       
  1464                 default:
       
  1465                     (*list)[r] =  QByteArray(data + r * columns[i].maxLen, columns[i].maxLen);
       
  1466                 break;
       
  1467             }
       
  1468         }
       
  1469     }
       
  1470 
       
  1471     d->q->setSelect(false);
       
  1472     d->q->setAt(QSql::BeforeFirstRow);
       
  1473     d->q->setActive(true);
       
  1474 
       
  1475     return true;
       
  1476 }
       
  1477 
       
  1478 template<class T, int sz>
       
  1479 int qReadLob(T &buf, const QOCIResultPrivate *d, OCILobLocator *lob)
       
  1480 {
       
  1481     ub1 csfrm;
       
  1482     ub4 amount;
       
  1483     int r;
       
  1484 
       
  1485     // Read this from the database, don't assume we know what it is set to
       
  1486     r = OCILobCharSetForm(d->env, d->err, lob, &csfrm);
       
  1487     if (r != OCI_SUCCESS) {
       
  1488         qOraWarning("OCIResultPrivate::readLobs: Couldn't get LOB char set form: ", d->err);
       
  1489         csfrm = 0;
       
  1490     }
       
  1491 
       
  1492     // Get the length of the LOB (this is in characters)
       
  1493     r = OCILobGetLength(d->svc, d->err, lob, &amount);
       
  1494     if (r == OCI_SUCCESS) {
       
  1495         if (amount == 0) {
       
  1496             // Short cut for null LOBs
       
  1497             buf.resize(0);
       
  1498             return OCI_SUCCESS;
       
  1499         }
       
  1500     } else {
       
  1501         qOraWarning("OCIResultPrivate::readLobs: Couldn't get LOB length: ", d->err);
       
  1502         return r;
       
  1503     }
       
  1504 
       
  1505     // Resize the buffer to hold the LOB contents
       
  1506     buf.resize(amount);
       
  1507 
       
  1508     // Read the LOB into the buffer
       
  1509     r = OCILobRead(d->svc,
       
  1510                    d->err,
       
  1511                    lob,
       
  1512                    &amount,
       
  1513                    1,
       
  1514                    buf.data(),
       
  1515                    buf.size() * sz, // this argument is in bytes, not characters
       
  1516                    0,
       
  1517                    0,
       
  1518                    // Extract the data from a CLOB in UTF-16 (ie. what QString uses internally)
       
  1519                    sz == 1 ? ub2(0) : ub2(QOCIEncoding),
       
  1520                    csfrm);
       
  1521 
       
  1522     if (r != OCI_SUCCESS)
       
  1523         qOraWarning("OCIResultPrivate::readLOBs: Cannot read LOB: ", d->err);
       
  1524 
       
  1525     return r;
       
  1526 }
       
  1527 
       
  1528 int QOCICols::readLOBs(QVector<QVariant> &values, int index)
       
  1529 {
       
  1530     OCILobLocator *lob;
       
  1531     int r = OCI_SUCCESS;
       
  1532 
       
  1533     for (int i = 0; i < size(); ++i) {
       
  1534         const OraFieldInf &fi = fieldInf.at(i);
       
  1535         if (fi.ind == -1 || !(lob = fi.lob))
       
  1536             continue;
       
  1537 
       
  1538         bool isClob = fi.oraType == SQLT_CLOB;
       
  1539         QVariant var;
       
  1540 
       
  1541         if (isClob) {
       
  1542             QString str;
       
  1543             r = qReadLob<QString, sizeof(QChar)>(str, d, lob);
       
  1544             var = str;
       
  1545         } else {
       
  1546             QByteArray buf;
       
  1547             r = qReadLob<QByteArray, sizeof(char)>(buf, d, lob);
       
  1548             var = buf;
       
  1549         }
       
  1550         if (r == OCI_SUCCESS)
       
  1551             values[index + i] = var;
       
  1552         else
       
  1553             break;
       
  1554     }
       
  1555     return r;
       
  1556 }
       
  1557 
       
  1558 int QOCICols::fieldFromDefine(OCIDefine* d)
       
  1559 {
       
  1560     for (int i = 0; i < fieldInf.count(); ++i) {
       
  1561         if (fieldInf.at(i).def == d)
       
  1562             return i;
       
  1563     }
       
  1564     return -1;
       
  1565 }
       
  1566 
       
  1567 void QOCICols::getValues(QVector<QVariant> &v, int index)
       
  1568 {
       
  1569     for (int i = 0; i < fieldInf.size(); ++i) {
       
  1570         const OraFieldInf &fld = fieldInf.at(i);
       
  1571 
       
  1572         if (fld.ind == -1) {
       
  1573             // got a NULL value
       
  1574             v[index + i] = QVariant(fld.typ);
       
  1575             continue;
       
  1576         }
       
  1577 
       
  1578         if (fld.oraType == SQLT_BIN || fld.oraType == SQLT_LBI || fld.oraType == SQLT_LNG)
       
  1579             continue; // already fetched piecewise
       
  1580 
       
  1581         switch (fld.typ) {
       
  1582         case QVariant::DateTime:
       
  1583             v[index + i] = QVariant(qMakeDate(fld.data));
       
  1584             break;
       
  1585         case QVariant::Double:
       
  1586         case QVariant::Int:
       
  1587         case QVariant::LongLong:
       
  1588             if (d->q->numericalPrecisionPolicy() != QSql::HighPrecision) {
       
  1589                 if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionDouble)
       
  1590                         && (fld.typ == QVariant::Double)) {
       
  1591                     v[index + i] = *reinterpret_cast<double *>(fld.data);
       
  1592                     break;
       
  1593                 } else if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
       
  1594                         && (fld.typ == QVariant::LongLong)) {
       
  1595                     qint64 qll = 0;
       
  1596                     int r = OCINumberToInt(d->err, reinterpret_cast<OCINumber *>(fld.data), sizeof(qint64),
       
  1597                                    OCI_NUMBER_SIGNED, &qll);
       
  1598                     if(r == OCI_SUCCESS)
       
  1599                         v[index + i] = qll;
       
  1600                     else
       
  1601                         v[index + i] = QVariant();
       
  1602                     break;
       
  1603                 } else if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
       
  1604                         && (fld.typ == QVariant::Int)) {
       
  1605                     v[index + i] = *reinterpret_cast<int *>(fld.data);
       
  1606                     break;
       
  1607                 }
       
  1608             }
       
  1609             // else fall through
       
  1610         case QVariant::String:
       
  1611             v[index + i] = QString::fromUtf16(reinterpret_cast<const ushort *>(fld.data));
       
  1612             break;
       
  1613         case QVariant::ByteArray:
       
  1614             if (fld.len > 0)
       
  1615                 v[index + i] = QByteArray(fld.data, fld.len);
       
  1616             else
       
  1617                 v[index + i] = QVariant(QVariant::ByteArray);
       
  1618             break;
       
  1619         default:
       
  1620             qWarning("QOCICols::value: unknown data type");
       
  1621             break;
       
  1622         }
       
  1623     }
       
  1624 }
       
  1625 
       
  1626 QOCIResultPrivate::QOCIResultPrivate(QOCIResult *result, const QOCIDriverPrivate *driver)
       
  1627     : cols(0), q(result), env(driver->env), err(0), svc(const_cast<OCISvcCtx*&>(driver->svc)),
       
  1628       sql(0), transaction(driver->transaction), serverVersion(driver->serverVersion),
       
  1629       prefetchRows(driver->prefetchRows), prefetchMem(driver->prefetchMem)
       
  1630 {
       
  1631     int r = OCIHandleAlloc(env,
       
  1632                            reinterpret_cast<void **>(&err),
       
  1633                            OCI_HTYPE_ERROR,
       
  1634                            0,
       
  1635                            0);
       
  1636     if (r != 0)
       
  1637         qWarning("QOCIResult: unable to alloc error handle");
       
  1638 }
       
  1639 
       
  1640 QOCIResultPrivate::~QOCIResultPrivate()
       
  1641 {
       
  1642     delete cols;
       
  1643 
       
  1644     int r = OCIHandleFree(err, OCI_HTYPE_ERROR);
       
  1645     if (r != 0)
       
  1646         qWarning("~QOCIResult: unable to free statement handle");
       
  1647 }
       
  1648 
       
  1649 
       
  1650 ////////////////////////////////////////////////////////////////////////////
       
  1651 
       
  1652 QOCIResult::QOCIResult(const QOCIDriver * db, const QOCIDriverPrivate* p)
       
  1653     : QSqlCachedResult(db)
       
  1654 {
       
  1655     d = new QOCIResultPrivate(this, p);
       
  1656 }
       
  1657 
       
  1658 QOCIResult::~QOCIResult()
       
  1659 {
       
  1660     if (d->sql) {
       
  1661         int r = OCIHandleFree(d->sql, OCI_HTYPE_STMT);
       
  1662         if (r != 0)
       
  1663             qWarning("~QOCIResult: unable to free statement handle");
       
  1664     }
       
  1665     delete d;
       
  1666 }
       
  1667 
       
  1668 QVariant QOCIResult::handle() const
       
  1669 {
       
  1670     return qVariantFromValue(d->sql);
       
  1671 }
       
  1672 
       
  1673 bool QOCIResult::reset (const QString& query)
       
  1674 {
       
  1675     if (!prepare(query))
       
  1676         return false;
       
  1677     return exec();
       
  1678 }
       
  1679 
       
  1680 bool QOCIResult::gotoNext(QSqlCachedResult::ValueCache &values, int index)
       
  1681 {
       
  1682     if (at() == QSql::AfterLastRow)
       
  1683         return false;
       
  1684 
       
  1685     bool piecewise = false;
       
  1686     int r = OCI_SUCCESS;
       
  1687     r = OCIStmtFetch(d->sql, d->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
       
  1688 
       
  1689     if (index < 0) //not interested in values
       
  1690         return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO;
       
  1691 
       
  1692     switch (r) {
       
  1693     case OCI_SUCCESS:
       
  1694         break;
       
  1695     case OCI_SUCCESS_WITH_INFO:
       
  1696         qOraWarning("QOCIResult::gotoNext: SuccessWithInfo: ", d->err);
       
  1697         r = OCI_SUCCESS; //ignore it
       
  1698         break;
       
  1699     case OCI_NO_DATA:
       
  1700         // end of rowset
       
  1701         return false;
       
  1702     case OCI_NEED_DATA:
       
  1703         piecewise = true;
       
  1704         r = OCI_SUCCESS;
       
  1705         break;
       
  1706     case OCI_ERROR:
       
  1707         if (qOraErrorNumber(d->err) == 1406) {
       
  1708             qWarning("QOCI Warning: data truncated for %s", lastQuery().toLocal8Bit().constData());
       
  1709             r = OCI_SUCCESS; /* ignore it */
       
  1710             break;
       
  1711         }
       
  1712         // fall through
       
  1713     default:
       
  1714         qOraWarning("QOCIResult::gotoNext: ", d->err);
       
  1715         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1716                                 "Unable to goto next"),
       
  1717                                 QSqlError::StatementError, d->err));
       
  1718         break;
       
  1719     }
       
  1720 
       
  1721     // need to read piecewise before assigning values
       
  1722     if (r == OCI_SUCCESS && piecewise)
       
  1723         r = d->cols->readPiecewise(values, index);
       
  1724 
       
  1725     if (r == OCI_SUCCESS)
       
  1726         d->cols->getValues(values, index);
       
  1727     if (r == OCI_SUCCESS)
       
  1728         r = d->cols->readLOBs(values, index);
       
  1729     if (r != OCI_SUCCESS)
       
  1730         setAt(QSql::AfterLastRow);
       
  1731     return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO;
       
  1732 }
       
  1733 
       
  1734 int QOCIResult::size()
       
  1735 {
       
  1736     return -1;
       
  1737 }
       
  1738 
       
  1739 int QOCIResult::numRowsAffected()
       
  1740 {
       
  1741     int rowCount;
       
  1742     OCIAttrGet(d->sql,
       
  1743                 OCI_HTYPE_STMT,
       
  1744                 &rowCount,
       
  1745                 NULL,
       
  1746                 OCI_ATTR_ROW_COUNT,
       
  1747                 d->err);
       
  1748     return rowCount;
       
  1749 }
       
  1750 
       
  1751 bool QOCIResult::prepare(const QString& query)
       
  1752 {
       
  1753     int r = 0;
       
  1754     QSqlResult::prepare(query);
       
  1755 
       
  1756     delete d->cols;
       
  1757     d->cols = 0;
       
  1758     QSqlCachedResult::cleanup();
       
  1759 
       
  1760     if (d->sql) {
       
  1761         r = OCIHandleFree(d->sql, OCI_HTYPE_STMT);
       
  1762         if (r != OCI_SUCCESS)
       
  1763             qOraWarning("QOCIResult::prepare: unable to free statement handle:", d->err);
       
  1764     }
       
  1765     if (query.isEmpty())
       
  1766         return false;
       
  1767     r = OCIHandleAlloc(d->env,
       
  1768                        reinterpret_cast<void **>(&d->sql),
       
  1769                        OCI_HTYPE_STMT,
       
  1770                        0,
       
  1771                        0);
       
  1772     if (r != OCI_SUCCESS) {
       
  1773         qOraWarning("QOCIResult::prepare: unable to alloc statement:", d->err);
       
  1774         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1775                      "Unable to alloc statement"), QSqlError::StatementError, d->err));
       
  1776         return false;
       
  1777     }
       
  1778     d->setStatementAttributes();
       
  1779     const OraText *txt = reinterpret_cast<const OraText *>(query.utf16());
       
  1780     const int len = query.length() * sizeof(QChar);
       
  1781     r = OCIStmtPrepare(d->sql,
       
  1782                        d->err,
       
  1783                        txt,
       
  1784                        len,
       
  1785                        OCI_NTV_SYNTAX,
       
  1786                        OCI_DEFAULT);
       
  1787     if (r != OCI_SUCCESS) {
       
  1788         qOraWarning("QOCIResult::prepare: unable to prepare statement:", d->err);
       
  1789         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1790                                 "Unable to prepare statement"), QSqlError::StatementError, d->err));
       
  1791         return false;
       
  1792     }
       
  1793     return true;
       
  1794 }
       
  1795 
       
  1796 bool QOCIResult::exec()
       
  1797 {
       
  1798     int r = 0;
       
  1799     ub2 stmtType=0;
       
  1800     ub4 iters;
       
  1801     ub4 mode;
       
  1802     QList<QByteArray> tmpStorage;
       
  1803     IndicatorArray indicators(boundValueCount());
       
  1804     SizeArray tmpSizes(boundValueCount());
       
  1805 
       
  1806     r = OCIAttrGet(d->sql,
       
  1807                     OCI_HTYPE_STMT,
       
  1808                     &stmtType,
       
  1809                     NULL,
       
  1810                     OCI_ATTR_STMT_TYPE,
       
  1811                     d->err);
       
  1812 
       
  1813     if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1814         qOraWarning("QOCIResult::exec: Unable to get statement type:", d->err);
       
  1815         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1816                      "Unable to get statement type"), QSqlError::StatementError, d->err));
       
  1817 #ifdef QOCI_DEBUG
       
  1818         qDebug() << "lastQuery()" << lastQuery();
       
  1819 #endif
       
  1820         return false;
       
  1821     }
       
  1822 
       
  1823     if (stmtType == OCI_STMT_SELECT) {
       
  1824         iters = 0;
       
  1825         mode = OCI_DEFAULT;
       
  1826     } else {
       
  1827         iters = 1;
       
  1828         mode = d->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS;
       
  1829     }
       
  1830 
       
  1831     // bind placeholders
       
  1832     if (boundValueCount() > 0
       
  1833         && d->bindValues(boundValues(), indicators, tmpSizes, tmpStorage) != OCI_SUCCESS) {
       
  1834         qOraWarning("QOCIResult::exec: unable to bind value: ", d->err);
       
  1835         setLastError(qMakeError(QCoreApplication::translate("QOCIResult", "Unable to bind value"),
       
  1836                     QSqlError::StatementError, d->err));
       
  1837 #ifdef QOCI_DEBUG
       
  1838         qDebug() << "lastQuery()" << lastQuery();
       
  1839 #endif
       
  1840         return false;
       
  1841     }
       
  1842 
       
  1843     // execute
       
  1844     r = OCIStmtExecute(d->svc,
       
  1845                        d->sql,
       
  1846                        d->err,
       
  1847                        iters,
       
  1848                        0,
       
  1849                        0,
       
  1850                        0,
       
  1851                        mode);
       
  1852     if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1853         qOraWarning("QOCIResult::exec: unable to execute statement:", d->err);
       
  1854         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1855                      "Unable to execute statement"), QSqlError::StatementError, d->err));
       
  1856 #ifdef QOCI_DEBUG
       
  1857         qDebug() << "lastQuery()" << lastQuery();
       
  1858 #endif
       
  1859         return false;
       
  1860     }
       
  1861 
       
  1862     if (stmtType == OCI_STMT_SELECT) {
       
  1863         ub4 parmCount = 0;
       
  1864         int r = OCIAttrGet(d->sql, OCI_HTYPE_STMT, reinterpret_cast<void **>(&parmCount),
       
  1865                            0, OCI_ATTR_PARAM_COUNT, d->err);
       
  1866         if (r == 0 && !d->cols)
       
  1867             d->cols = new QOCICols(parmCount, d);
       
  1868         setSelect(true);
       
  1869         QSqlCachedResult::init(parmCount);
       
  1870     } else { /* non-SELECT */
       
  1871         setSelect(false);
       
  1872     }
       
  1873     setAt(QSql::BeforeFirstRow);
       
  1874     setActive(true);
       
  1875 
       
  1876     if (hasOutValues())
       
  1877         d->outValues(boundValues(), indicators, tmpStorage);
       
  1878 
       
  1879     return true;
       
  1880 }
       
  1881 
       
  1882 QSqlRecord QOCIResult::record() const
       
  1883 {
       
  1884     QSqlRecord inf;
       
  1885     if (!isActive() || !isSelect() || !d->cols)
       
  1886         return inf;
       
  1887     return d->cols->rec;
       
  1888 }
       
  1889 
       
  1890 QVariant QOCIResult::lastInsertId() const
       
  1891 {
       
  1892     if (isActive()) {
       
  1893         QOCIRowIdPointer ptr(new QOCIRowId(d->env));
       
  1894 
       
  1895         int r = OCIAttrGet(d->sql, OCI_HTYPE_STMT, ptr.constData()->id,
       
  1896                            0, OCI_ATTR_ROWID, d->err);
       
  1897         if (r == OCI_SUCCESS)
       
  1898             return qVariantFromValue(ptr);
       
  1899     }
       
  1900     return QVariant();
       
  1901 }
       
  1902 
       
  1903 void QOCIResult::virtual_hook(int id, void *data)
       
  1904 {
       
  1905     Q_ASSERT(data);
       
  1906 
       
  1907     switch (id) {
       
  1908     case QSqlResult::BatchOperation:
       
  1909         QOCICols::execBatch(d, boundValues(), *reinterpret_cast<bool *>(data));
       
  1910         break;
       
  1911     default:
       
  1912         QSqlCachedResult::virtual_hook(id, data);
       
  1913     }
       
  1914 }
       
  1915 
       
  1916 ////////////////////////////////////////////////////////////////////////////
       
  1917 
       
  1918 
       
  1919 QOCIDriver::QOCIDriver(QObject* parent)
       
  1920     : QSqlDriver(parent)
       
  1921 {
       
  1922     d = new QOCIDriverPrivate();
       
  1923 
       
  1924 #ifdef QOCI_THREADED
       
  1925     const ub4 mode = OCI_UTF16 | OCI_OBJECT | OCI_THREADED;
       
  1926 #else
       
  1927     const ub4 mode = OCI_UTF16 | OCI_OBJECT;
       
  1928 #endif
       
  1929     int r = OCIEnvCreate(&d->env,
       
  1930                          mode,
       
  1931                          NULL,
       
  1932                          NULL,
       
  1933                          NULL,
       
  1934                          NULL,
       
  1935                          0,
       
  1936                          NULL);
       
  1937     if (r != 0) {
       
  1938         qWarning("QOCIDriver: unable to create environment");
       
  1939         setLastError(qMakeError(tr("Unable to initialize", "QOCIDriver"),
       
  1940                      QSqlError::ConnectionError, d->err));
       
  1941         return;
       
  1942     }
       
  1943 
       
  1944     d->allocErrorHandle();
       
  1945 }
       
  1946 
       
  1947 QOCIDriver::QOCIDriver(OCIEnv* env, OCISvcCtx* ctx, QObject* parent)
       
  1948     : QSqlDriver(parent)
       
  1949 {
       
  1950     d = new QOCIDriverPrivate();
       
  1951     d->env = env;
       
  1952     d->svc = ctx;
       
  1953 
       
  1954     d->allocErrorHandle();
       
  1955 
       
  1956     if (env && ctx) {
       
  1957         setOpen(true);
       
  1958         setOpenError(false);
       
  1959     }
       
  1960 }
       
  1961 
       
  1962 QOCIDriver::~QOCIDriver()
       
  1963 {
       
  1964     if (isOpen())
       
  1965         close();
       
  1966     int r = OCIHandleFree(d->err, OCI_HTYPE_ERROR);
       
  1967     if (r != OCI_SUCCESS)
       
  1968         qWarning("Unable to free Error handle: %d", r);
       
  1969     r = OCIHandleFree(d->env, OCI_HTYPE_ENV);
       
  1970     if (r != OCI_SUCCESS)
       
  1971         qWarning("Unable to free Environment handle: %d", r);
       
  1972 
       
  1973     delete d;
       
  1974 }
       
  1975 
       
  1976 bool QOCIDriver::hasFeature(DriverFeature f) const
       
  1977 {
       
  1978     switch (f) {
       
  1979     case Transactions:
       
  1980     case LastInsertId:
       
  1981     case BLOB:
       
  1982     case PreparedQueries:
       
  1983     case NamedPlaceholders:
       
  1984     case BatchOperations:
       
  1985     case LowPrecisionNumbers:
       
  1986         return true;
       
  1987     case QuerySize:
       
  1988     case PositionalPlaceholders:
       
  1989     case SimpleLocking:
       
  1990     case EventNotifications:
       
  1991     case FinishQuery:
       
  1992     case MultipleResultSets:
       
  1993         return false;
       
  1994     case Unicode:
       
  1995         return d->serverVersion >= 9;
       
  1996     }
       
  1997     return false;
       
  1998 }
       
  1999 
       
  2000 static void qParseOpts(const QString &options, QOCIDriverPrivate *d)
       
  2001 {
       
  2002     const QStringList opts(options.split(QLatin1Char(';'), QString::SkipEmptyParts));
       
  2003     for (int i = 0; i < opts.count(); ++i) {
       
  2004         const QString tmp(opts.at(i));
       
  2005         int idx;
       
  2006         if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
       
  2007             qWarning("QOCIDriver::parseArgs: Invalid parameter: '%s'",
       
  2008                      tmp.toLocal8Bit().constData());
       
  2009             continue;
       
  2010         }
       
  2011         const QString opt = tmp.left(idx);
       
  2012         const QString val = tmp.mid(idx + 1).simplified();
       
  2013         bool ok;
       
  2014         if (opt == QLatin1String("OCI_ATTR_PREFETCH_ROWS")) {
       
  2015             d->prefetchRows = val.toInt(&ok);
       
  2016             if (!ok)
       
  2017                 d->prefetchRows = -1;
       
  2018         } else if (opt == QLatin1String("OCI_ATTR_PREFETCH_MEMORY")) {
       
  2019             d->prefetchMem = val.toInt(&ok);
       
  2020             if (!ok)
       
  2021                 d->prefetchMem = -1;
       
  2022         } else {
       
  2023             qWarning ("QOCIDriver::parseArgs: Invalid parameter: '%s'",
       
  2024                       opt.toLocal8Bit().constData());
       
  2025         }
       
  2026     }
       
  2027 }
       
  2028 
       
  2029 bool QOCIDriver::open(const QString & db,
       
  2030                        const QString & user,
       
  2031                        const QString & password,
       
  2032                        const QString & hostname,
       
  2033                        int port,
       
  2034                        const QString &opts)
       
  2035 {
       
  2036     int r;
       
  2037 
       
  2038     if (isOpen())
       
  2039         close();
       
  2040 
       
  2041     qParseOpts(opts, d);
       
  2042 
       
  2043     // Connect without tnsnames.ora if a hostname is given
       
  2044     QString connectionString = db;
       
  2045     if (!hostname.isEmpty())
       
  2046         connectionString = 
       
  2047         QString::fromLatin1("(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=%1)(Port=%2))"
       
  2048                 "(CONNECT_DATA=(SID=%3)))").arg(hostname).arg((port > -1 ? port : 1521)).arg(db);
       
  2049 
       
  2050     r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->srvhp), OCI_HTYPE_SERVER, 0, 0);
       
  2051     if (r == OCI_SUCCESS)
       
  2052         r = OCIServerAttach(d->srvhp, d->err, reinterpret_cast<const OraText *>(connectionString.utf16()),
       
  2053                             connectionString.length() * sizeof(QChar), OCI_DEFAULT);
       
  2054     if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO)
       
  2055         r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->svc), OCI_HTYPE_SVCCTX, 0, 0);
       
  2056     if (r == OCI_SUCCESS)
       
  2057         r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->srvhp, 0, OCI_ATTR_SERVER, d->err);
       
  2058     if (r == OCI_SUCCESS)
       
  2059         r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->authp), OCI_HTYPE_SESSION, 0, 0);
       
  2060     if (r == OCI_SUCCESS)
       
  2061         r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>(user.utf16()),
       
  2062                        user.length() * sizeof(QChar), OCI_ATTR_USERNAME, d->err);
       
  2063     if (r == OCI_SUCCESS)
       
  2064         r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>(password.utf16()),
       
  2065                        password.length() * sizeof(QChar), OCI_ATTR_PASSWORD, d->err);
       
  2066 
       
  2067     OCITrans* trans;
       
  2068     if (r == OCI_SUCCESS)
       
  2069         r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&trans), OCI_HTYPE_TRANS, 0, 0);
       
  2070     if (r == OCI_SUCCESS)
       
  2071         r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, trans, 0, OCI_ATTR_TRANS, d->err);
       
  2072 
       
  2073     if (r == OCI_SUCCESS) {
       
  2074         if (user.isEmpty() && password.isEmpty())
       
  2075             r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_EXT, OCI_DEFAULT);
       
  2076         else
       
  2077             r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_RDBMS, OCI_DEFAULT);
       
  2078     }
       
  2079     if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO)
       
  2080         r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->authp, 0, OCI_ATTR_SESSION, d->err);
       
  2081 
       
  2082     if (r != OCI_SUCCESS) {
       
  2083         setLastError(qMakeError(tr("Unable to logon"), QSqlError::ConnectionError, d->err));
       
  2084         setOpenError(true);
       
  2085         if (d->authp)
       
  2086             OCIHandleFree(d->authp, OCI_HTYPE_SESSION);
       
  2087         d->authp = 0;
       
  2088         if (d->srvhp)
       
  2089             OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER);
       
  2090         d->srvhp = 0;
       
  2091         return false;
       
  2092     }
       
  2093 
       
  2094     // get server version
       
  2095     char vertxt[512];
       
  2096     r = OCIServerVersion(d->svc,
       
  2097                           d->err,
       
  2098                           reinterpret_cast<OraText *>(vertxt),
       
  2099                           sizeof(vertxt),
       
  2100                           OCI_HTYPE_SVCCTX);
       
  2101     if (r != 0) {
       
  2102         qWarning("QOCIDriver::open: could not get Oracle server version.");
       
  2103     } else {
       
  2104         QString versionStr;
       
  2105         versionStr = QString::fromUtf16(reinterpret_cast<ushort *>(vertxt));
       
  2106         QRegExp vers(QLatin1String("([0-9]+)\\.[0-9\\.]+[0-9]"));
       
  2107         if (vers.indexIn(versionStr) >= 0)
       
  2108             d->serverVersion = vers.cap(1).toInt();
       
  2109         if (d->serverVersion == 0)
       
  2110             d->serverVersion = -1;
       
  2111     }
       
  2112 
       
  2113     setOpen(true);
       
  2114     setOpenError(false);
       
  2115     d->user = user;
       
  2116 
       
  2117     return true;
       
  2118 }
       
  2119 
       
  2120 void QOCIDriver::close()
       
  2121 {
       
  2122     if (!isOpen())
       
  2123         return;
       
  2124 
       
  2125     OCISessionEnd(d->svc, d->err, d->authp, OCI_DEFAULT);
       
  2126     OCIServerDetach(d->srvhp, d->err, OCI_DEFAULT);
       
  2127     OCIHandleFree(d->authp, OCI_HTYPE_SESSION);
       
  2128     d->authp = 0;
       
  2129     OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER);
       
  2130     d->srvhp = 0;
       
  2131     OCIHandleFree(d->svc, OCI_HTYPE_SVCCTX);
       
  2132     d->svc = 0;
       
  2133     setOpen(false);
       
  2134     setOpenError(false);
       
  2135 }
       
  2136 
       
  2137 QSqlResult *QOCIDriver::createResult() const
       
  2138 {
       
  2139     return new QOCIResult(this, d);
       
  2140 }
       
  2141 
       
  2142 bool QOCIDriver::beginTransaction()
       
  2143 {
       
  2144     if (!isOpen()) {
       
  2145         qWarning("QOCIDriver::beginTransaction: Database not open");
       
  2146         return false;
       
  2147     }
       
  2148     int r = OCITransStart(d->svc,
       
  2149                           d->err,
       
  2150                           2,
       
  2151                           OCI_TRANS_READWRITE);
       
  2152     if (r == OCI_ERROR) {
       
  2153         qOraWarning("QOCIDriver::beginTransaction: ", d->err);
       
  2154         setLastError(qMakeError(QCoreApplication::translate("QOCIDriver",
       
  2155                                 "Unable to begin transaction"), QSqlError::TransactionError, d->err));
       
  2156         return false;
       
  2157     }
       
  2158     d->transaction = true;
       
  2159     return true;
       
  2160 }
       
  2161 
       
  2162 bool QOCIDriver::commitTransaction()
       
  2163 {
       
  2164     if (!isOpen()) {
       
  2165         qWarning("QOCIDriver::commitTransaction: Database not open");
       
  2166         return false;
       
  2167     }
       
  2168     int r = OCITransCommit(d->svc,
       
  2169                            d->err,
       
  2170                            0);
       
  2171     if (r == OCI_ERROR) {
       
  2172         qOraWarning("QOCIDriver::commitTransaction:", d->err);
       
  2173         setLastError(qMakeError(QCoreApplication::translate("QOCIDriver",
       
  2174                                 "Unable to commit transaction"), QSqlError::TransactionError, d->err));
       
  2175         return false;
       
  2176     }
       
  2177     d->transaction = false;
       
  2178     return true;
       
  2179 }
       
  2180 
       
  2181 bool QOCIDriver::rollbackTransaction()
       
  2182 {
       
  2183     if (!isOpen()) {
       
  2184         qWarning("QOCIDriver::rollbackTransaction: Database not open");
       
  2185         return false;
       
  2186     }
       
  2187     int r = OCITransRollback(d->svc,
       
  2188                              d->err,
       
  2189                              0);
       
  2190     if (r == OCI_ERROR) {
       
  2191         qOraWarning("QOCIDriver::rollbackTransaction:", d->err);
       
  2192         setLastError(qMakeError(QCoreApplication::translate("QOCIDriver",
       
  2193                                 "Unable to rollback transaction"), QSqlError::TransactionError, d->err));
       
  2194         return false;
       
  2195     }
       
  2196     d->transaction = false;
       
  2197     return true;
       
  2198 }
       
  2199 
       
  2200 QStringList QOCIDriver::tables(QSql::TableType type) const
       
  2201 {
       
  2202     QStringList tl;
       
  2203     QStringList sysUsers = QStringList() << QLatin1String("MDSYS")
       
  2204                                     << QLatin1String("LBACSYS")
       
  2205                                     << QLatin1String("SYS")
       
  2206                                     << QLatin1String("SYSTEM")
       
  2207                                     << QLatin1String("WKSYS")
       
  2208                                     << QLatin1String("CTXSYS")
       
  2209                                     << QLatin1String("WMSYS");
       
  2210 
       
  2211     QString user = d->user;
       
  2212     if ( isIdentifierEscaped(user, QSqlDriver::TableName))
       
  2213         user = stripDelimiters(user, QSqlDriver::TableName);
       
  2214     else
       
  2215         user = user.toUpper();
       
  2216 
       
  2217     if(sysUsers.contains(user))
       
  2218         sysUsers.removeAll(user);;
       
  2219 
       
  2220     if (!isOpen())
       
  2221         return tl;
       
  2222 
       
  2223     QSqlQuery t(createResult());
       
  2224     t.setForwardOnly(true);
       
  2225     if (type & QSql::Tables) {
       
  2226         QString query = QLatin1String("select owner, table_name from all_tables where ");
       
  2227         QStringList whereList;
       
  2228         foreach(const QString &sysUserName, sysUsers)
       
  2229             whereList << QLatin1String("owner != '") + sysUserName + QLatin1String("' ");
       
  2230         t.exec(query + whereList.join(QLatin1String(" and ")));
       
  2231 
       
  2232         while (t.next()) {
       
  2233             if (t.value(0).toString().toUpper() != user.toUpper())
       
  2234                 tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
       
  2235             else
       
  2236                 tl.append(t.value(1).toString());
       
  2237         }
       
  2238 
       
  2239         // list all table synonyms as well
       
  2240         query = QLatin1String("select owner, synonym_name from all_synonyms where ");
       
  2241         t.exec(query + whereList.join(QLatin1String(" and ")));
       
  2242         while (t.next()) {
       
  2243             if (t.value(0).toString() != d->user)
       
  2244                 tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
       
  2245             else
       
  2246                 tl.append(t.value(1).toString());
       
  2247         }
       
  2248     }
       
  2249     if (type & QSql::Views) {
       
  2250         QString query = QLatin1String("select owner, view_name from all_views where ");
       
  2251         QStringList whereList;
       
  2252         foreach(const QString &sysUserName, sysUsers)
       
  2253             whereList << QLatin1String("owner != '") + sysUserName + QLatin1String("' ");
       
  2254         t.exec(query + whereList.join(QLatin1String(" and ")));
       
  2255         while (t.next()) {
       
  2256             if (t.value(0).toString().toUpper() != d->user.toUpper())
       
  2257                 tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
       
  2258             else
       
  2259                 tl.append(t.value(1).toString());
       
  2260         }
       
  2261     }
       
  2262     if (type & QSql::SystemTables) {
       
  2263         t.exec(QLatin1String("select table_name from dictionary"));
       
  2264         while (t.next()) {
       
  2265             tl.append(t.value(0).toString());
       
  2266         }
       
  2267         QString query = QLatin1String("select owner, table_name from all_tables where ");
       
  2268         QStringList whereList;
       
  2269         foreach(const QString &sysUserName, sysUsers)
       
  2270             whereList << QLatin1String("owner = '") + sysUserName + QLatin1String("' ");
       
  2271         t.exec(query + whereList.join(QLatin1String(" or ")));
       
  2272 
       
  2273         while (t.next()) {
       
  2274             if (t.value(0).toString().toUpper() != user.toUpper())
       
  2275                 tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
       
  2276             else
       
  2277                 tl.append(t.value(1).toString());
       
  2278         }
       
  2279 
       
  2280         // list all table synonyms as well
       
  2281         query = QLatin1String("select owner, synonym_name from all_synonyms where ");
       
  2282         t.exec(query + whereList.join(QLatin1String(" or ")));
       
  2283         while (t.next()) {
       
  2284             if (t.value(0).toString() != d->user)
       
  2285                 tl.append(t.value(0).toString() + QLatin1String(".") + t.value(1).toString());
       
  2286             else
       
  2287                 tl.append(t.value(1).toString());
       
  2288         }
       
  2289     }
       
  2290     return tl;
       
  2291 }
       
  2292 
       
  2293 void qSplitTableAndOwner(const QString & tname, QString * tbl,
       
  2294                           QString * owner)
       
  2295 {
       
  2296     int i = tname.indexOf(QLatin1Char('.')); // prefixed with owner?
       
  2297     if (i != -1) {
       
  2298         *tbl = tname.right(tname.length() - i - 1);
       
  2299         *owner = tname.left(i);
       
  2300     } else {
       
  2301         *tbl = tname;
       
  2302     }
       
  2303 }
       
  2304 
       
  2305 QSqlRecord QOCIDriver::record(const QString& tablename) const
       
  2306 {
       
  2307     QSqlRecord fil;
       
  2308     if (!isOpen())
       
  2309         return fil;
       
  2310 
       
  2311     QSqlQuery t(createResult());
       
  2312     // using two separate queries for this is A LOT faster than using
       
  2313     // eg. a sub-query on the sys.synonyms table
       
  2314     QString stmt(QLatin1String("select column_name, data_type, data_length, "
       
  2315                   "data_precision, data_scale, nullable, data_default%1"
       
  2316                   "from all_tab_columns a "
       
  2317                   "where a.table_name=%2"));
       
  2318     if (d->serverVersion >= 9)
       
  2319         stmt = stmt.arg(QLatin1String(", char_length "));
       
  2320     else
       
  2321         stmt = stmt.arg(QLatin1String(" "));
       
  2322     bool buildRecordInfo = false;
       
  2323     QString table, owner, tmpStmt;
       
  2324     qSplitTableAndOwner(tablename, &table, &owner);
       
  2325 
       
  2326     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
  2327         table = stripDelimiters(table, QSqlDriver::TableName);
       
  2328     else
       
  2329         table = table.toUpper();
       
  2330 
       
  2331     tmpStmt = stmt.arg(QLatin1Char('\'') + table + QLatin1Char('\''));
       
  2332     if (owner.isEmpty()) {
       
  2333         owner = d->user;
       
  2334     }
       
  2335 
       
  2336     if (isIdentifierEscaped(owner, QSqlDriver::TableName))
       
  2337         owner = stripDelimiters(owner, QSqlDriver::TableName);
       
  2338     else
       
  2339         owner = owner.toUpper();
       
  2340 
       
  2341     tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\'');
       
  2342     t.setForwardOnly(true);
       
  2343     t.exec(tmpStmt);
       
  2344     if (!t.next()) { // try and see if the tablename is a synonym
       
  2345         stmt = stmt + QLatin1String(" join all_synonyms b "
       
  2346                               "on a.owner=b.table_owner and a.table_name=b.table_name "
       
  2347                               "where b.owner='") + owner +
       
  2348                       QLatin1String("' and b.synonym_name='") + table +
       
  2349                       QLatin1Char('\'');
       
  2350         t.setForwardOnly(true);
       
  2351         t.exec(stmt);
       
  2352         if (t.next())
       
  2353             buildRecordInfo = true;
       
  2354     } else {
       
  2355         buildRecordInfo = true;
       
  2356     }
       
  2357     QStringList keywords = QStringList() << QLatin1String("NUMBER") << QLatin1String("FLOAT") << QLatin1String("BINARY_FLOAT")
       
  2358               << QLatin1String("BINARY_DOUBLE");
       
  2359     if (buildRecordInfo) {
       
  2360         do {
       
  2361             QVariant::Type ty = qDecodeOCIType(t.value(1).toString(), t.numericalPrecisionPolicy());
       
  2362             QSqlField f(t.value(0).toString(), ty);
       
  2363             f.setRequired(t.value(5).toString() == QLatin1String("N"));
       
  2364             f.setPrecision(t.value(4).toInt());
       
  2365             if (d->serverVersion >= 9 && (ty == QVariant::String) && !t.isNull(3) && !keywords.contains(t.value(1).toString())) {
       
  2366                 // Oracle9: data_length == size in bytes, char_length == amount of characters
       
  2367                 f.setLength(t.value(7).toInt());
       
  2368             } else {
       
  2369                 f.setLength(t.value(t.isNull(3) ? 2 : 3).toInt());
       
  2370             }
       
  2371             f.setDefaultValue(t.value(6));
       
  2372             fil.append(f);
       
  2373         } while (t.next());
       
  2374     }
       
  2375     return fil;
       
  2376 }
       
  2377 
       
  2378 QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
       
  2379 {
       
  2380     QSqlIndex idx(tablename);
       
  2381     if (!isOpen())
       
  2382         return idx;
       
  2383     QSqlQuery t(createResult());
       
  2384     QString stmt(QLatin1String("select b.column_name, b.index_name, a.table_name, a.owner "
       
  2385                   "from all_constraints a, all_ind_columns b "
       
  2386                   "where a.constraint_type='P' "
       
  2387                   "and b.index_name = a.constraint_name "
       
  2388                   "and b.index_owner = a.owner"));
       
  2389 
       
  2390     bool buildIndex = false;
       
  2391     QString table, owner, tmpStmt;
       
  2392     qSplitTableAndOwner(tablename, &table, &owner);
       
  2393 
       
  2394     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
  2395         table = stripDelimiters(table, QSqlDriver::TableName);
       
  2396     else
       
  2397         table = table.toUpper();
       
  2398 
       
  2399     tmpStmt = stmt + QLatin1String(" and a.table_name='") + table + QLatin1Char('\'');
       
  2400     if (owner.isEmpty()) {
       
  2401         owner = d->user;
       
  2402     }
       
  2403 
       
  2404     if (isIdentifierEscaped(owner, QSqlDriver::TableName))
       
  2405         owner = stripDelimiters(owner, QSqlDriver::TableName);
       
  2406     else
       
  2407         owner = owner.toUpper();
       
  2408 
       
  2409     tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\'');
       
  2410     t.setForwardOnly(true);
       
  2411     t.exec(tmpStmt);
       
  2412 
       
  2413     if (!t.next()) {
       
  2414         stmt += QLatin1String(" and a.table_name=(select tname from sys.synonyms "
       
  2415                 "where sname='") + table + QLatin1String("' and creator=a.owner)");
       
  2416         t.setForwardOnly(true);
       
  2417         t.exec(stmt);
       
  2418         if (t.next()) {
       
  2419             owner = t.value(3).toString();
       
  2420             buildIndex = true;
       
  2421         }
       
  2422     } else {
       
  2423         buildIndex = true;
       
  2424     }
       
  2425     if (buildIndex) {
       
  2426         QSqlQuery tt(createResult());
       
  2427         tt.setForwardOnly(true);
       
  2428         idx.setName(t.value(1).toString());
       
  2429         do {
       
  2430             tt.exec(QLatin1String("select data_type from all_tab_columns where table_name='") +
       
  2431                      t.value(2).toString() + QLatin1String("' and column_name='") +
       
  2432                      t.value(0).toString() + QLatin1String("' and owner='") +
       
  2433                      owner + QLatin1Char('\''));
       
  2434             if (!tt.next()) {
       
  2435                 return QSqlIndex();
       
  2436             }
       
  2437             QSqlField f(t.value(0).toString(), qDecodeOCIType(tt.value(0).toString(), t.numericalPrecisionPolicy()));
       
  2438             idx.append(f);
       
  2439         } while (t.next());
       
  2440         return idx;
       
  2441     }
       
  2442     return QSqlIndex();
       
  2443 }
       
  2444 
       
  2445 QString QOCIDriver::formatValue(const QSqlField &field, bool trimStrings) const
       
  2446 {
       
  2447     switch (field.type()) {
       
  2448     case QVariant::DateTime: {
       
  2449         QDateTime datetime = field.value().toDateTime();
       
  2450         QString datestring;
       
  2451         if (datetime.isValid()) {
       
  2452             datestring = QLatin1String("TO_DATE('") + QString::number(datetime.date().year())
       
  2453                          + QLatin1Char('-')
       
  2454                          + QString::number(datetime.date().month()) + QLatin1Char('-')
       
  2455                          + QString::number(datetime.date().day()) + QLatin1Char(' ')
       
  2456                          + QString::number(datetime.time().hour()) + QLatin1Char(':')
       
  2457                          + QString::number(datetime.time().minute()) + QLatin1Char(':')
       
  2458                          + QString::number(datetime.time().second())
       
  2459                          + QLatin1String("','YYYY-MM-DD HH24:MI:SS')");
       
  2460         } else {
       
  2461             datestring = QLatin1String("NULL");
       
  2462         }
       
  2463         return datestring;
       
  2464     }
       
  2465     case QVariant::Time: {
       
  2466         QDateTime datetime = field.value().toDateTime();
       
  2467         QString datestring;
       
  2468         if (datetime.isValid()) {
       
  2469             datestring = QLatin1String("TO_DATE('")
       
  2470                          + QString::number(datetime.time().hour()) + QLatin1Char(':')
       
  2471                          + QString::number(datetime.time().minute()) + QLatin1Char(':')
       
  2472                          + QString::number(datetime.time().second())
       
  2473                          + QLatin1String("','HH24:MI:SS')");
       
  2474         } else {
       
  2475             datestring = QLatin1String("NULL");
       
  2476         }
       
  2477         return datestring;
       
  2478     }
       
  2479     case QVariant::Date: {
       
  2480         QDate date = field.value().toDate();
       
  2481         QString datestring;
       
  2482         if (date.isValid()) {
       
  2483             datestring = QLatin1String("TO_DATE('") + QString::number(date.year()) +
       
  2484                          QLatin1Char('-') +
       
  2485                          QString::number(date.month()) + QLatin1Char('-') +
       
  2486                          QString::number(date.day()) + QLatin1String("','YYYY-MM-DD')");
       
  2487         } else {
       
  2488             datestring = QLatin1String("NULL");
       
  2489         }
       
  2490         return datestring;
       
  2491     }
       
  2492     default:
       
  2493         break;
       
  2494     }
       
  2495     return QSqlDriver::formatValue(field, trimStrings);
       
  2496 }
       
  2497 
       
  2498 QVariant QOCIDriver::handle() const
       
  2499 {
       
  2500     return qVariantFromValue(d->env);
       
  2501 }
       
  2502 
       
  2503 QString QOCIDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
       
  2504 {
       
  2505     QString res = identifier;
       
  2506     if(!identifier.isEmpty() && !isIdentifierEscaped(identifier, type)) {
       
  2507         res.replace(QLatin1Char('"'), QLatin1String("\"\""));
       
  2508         res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
       
  2509         res.replace(QLatin1Char('.'), QLatin1String("\".\""));
       
  2510     }
       
  2511     return res;
       
  2512 }
       
  2513 
       
  2514 QT_END_NAMESPACE