src/sql/drivers/oci/qsql_oci.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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_FILE:
       
   584     case SQLT_NTY:
       
   585     case SQLT_REF:
       
   586     case SQLT_RID:
       
   587     case SQLT_CLOB:
       
   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 = boundValues.at(i).toList().at(j).toString().length() + 1;
       
  1261                     if (len > col.maxLen)
       
  1262                         col.maxLen = len;
       
  1263                 }
       
  1264                 col.maxLen *= sizeof(QChar);
       
  1265                 break; }
       
  1266 
       
  1267             case QVariant::ByteArray:
       
  1268             default: {
       
  1269                 col.bindAs = SQLT_LBI;
       
  1270                 for (uint j = 0; j < col.recordCount; ++j) {
       
  1271                     col.lengths[j] = boundValues.at(i).toList().at(j).toByteArray().size();
       
  1272                     if (col.lengths[j] > col.maxLen)
       
  1273                         col.maxLen = col.lengths[j];
       
  1274                 }
       
  1275                 break; }
       
  1276         }
       
  1277 
       
  1278         col.data = new char[col.maxLen * col.recordCount];
       
  1279         memset(col.data, 0, col.maxLen * col.recordCount);
       
  1280 
       
  1281         // we may now populate column with data
       
  1282         for (uint row = 0; row < col.recordCount; ++row) {
       
  1283             const QVariant &val = boundValues.at(i).toList().at(row);
       
  1284 
       
  1285             if (val.isNull()){
       
  1286                 columns[i].indicators[row] = -1;
       
  1287                 columns[i].lengths[row] = 0;
       
  1288             } else {
       
  1289                 columns[i].indicators[row] = 0;
       
  1290                 char *dataPtr = columns[i].data + (columns[i].maxLen * row);
       
  1291                 switch (fieldTypes[i]) {
       
  1292                     case QVariant::Time:
       
  1293                     case QVariant::Date:
       
  1294                     case QVariant::DateTime:{
       
  1295                         columns[i].lengths[row] = columns[i].maxLen;
       
  1296                         const QByteArray ba = qMakeOraDate(val.toDateTime());
       
  1297                         Q_ASSERT(ba.size() == int(columns[i].maxLen));
       
  1298                         memcpy(dataPtr, ba.constData(), columns[i].maxLen);
       
  1299                         break;
       
  1300                     }
       
  1301                     case QVariant::Int:
       
  1302                         columns[i].lengths[row] = columns[i].maxLen;
       
  1303                         *reinterpret_cast<int*>(dataPtr) = val.toInt();
       
  1304                         break;
       
  1305 
       
  1306                     case QVariant::UInt:
       
  1307                         columns[i].lengths[row] = columns[i].maxLen;
       
  1308                         *reinterpret_cast<uint*>(dataPtr) = val.toUInt();
       
  1309                         break;
       
  1310 
       
  1311                     case QVariant::Double:
       
  1312                          columns[i].lengths[row] = columns[i].maxLen;
       
  1313                          *reinterpret_cast<double*>(dataPtr) = val.toDouble();
       
  1314                          break;
       
  1315 
       
  1316                     case QVariant::String: {
       
  1317                         const QString s = val.toString();
       
  1318                         columns[i].lengths[row] = (s.length() + 1) * sizeof(QChar);
       
  1319                         memcpy(dataPtr, s.utf16(), columns[i].lengths[row]);
       
  1320                         break;
       
  1321                     }
       
  1322                     case QVariant::UserType:
       
  1323                         if (qVariantCanConvert<QOCIRowIdPointer>(val)) {
       
  1324                             const QOCIRowIdPointer rptr = qVariantValue<QOCIRowIdPointer>(val);
       
  1325                             *reinterpret_cast<OCIRowid**>(dataPtr) = rptr->id;
       
  1326                             columns[i].lengths[row] = 0;
       
  1327                             break;
       
  1328                         }
       
  1329                     case QVariant::ByteArray:
       
  1330                     default: {
       
  1331                         const QByteArray ba = val.toByteArray();
       
  1332                         columns[i].lengths[row] = ba.size();
       
  1333                         memcpy(dataPtr, ba.constData(), ba.size());
       
  1334                         break;
       
  1335                     }
       
  1336                 }
       
  1337             }
       
  1338         }
       
  1339 
       
  1340         QOCIBatchColumn &bindColumn = columns[i];
       
  1341 
       
  1342 #ifdef QOCI_DEBUG
       
  1343             qDebug("OCIBindByPos(%p, %p, %p, %d, %p, %d, %d, %p, %p, 0, %d, %p, OCI_DEFAULT)",
       
  1344             d->sql, &bindColumn.bindh, d->err, i + 1, bindColumn.data,
       
  1345             bindColumn.maxLen, bindColumn.bindAs, bindColumn.indicators, bindColumn.lengths,
       
  1346             arrayBind ? bindColumn.maxarr_len : 0, arrayBind ? &bindColumn.curelep : 0);
       
  1347 
       
  1348         for (int ii = 0; ii < (int)bindColumn.recordCount; ++ii) {
       
  1349             qDebug(" record %d: indicator %d, length %d", ii, bindColumn.indicators[ii],
       
  1350                     bindColumn.lengths[ii]);
       
  1351         }
       
  1352 #endif
       
  1353 
       
  1354 
       
  1355         // binding the column
       
  1356         r = OCIBindByPos(
       
  1357                 d->sql, &bindColumn.bindh, d->err, i + 1,
       
  1358                 bindColumn.data,
       
  1359                 bindColumn.maxLen,
       
  1360                 bindColumn.bindAs,
       
  1361                 bindColumn.indicators,
       
  1362                 bindColumn.lengths,
       
  1363                 0,
       
  1364                 arrayBind ? bindColumn.maxarr_len : 0,
       
  1365                 arrayBind ? &bindColumn.curelep : 0,
       
  1366                 OCI_DEFAULT);
       
  1367 
       
  1368 #ifdef QOCI_DEBUG
       
  1369         qDebug("After OCIBindByPos: r = %d, bindh = %p", r, bindColumn.bindh);
       
  1370 #endif
       
  1371 
       
  1372         if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1373             qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err);
       
  1374             d->q->setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1375                      "Unable to bind column for batch execute"),
       
  1376                      QSqlError::StatementError, d->err));
       
  1377             return false;
       
  1378         }
       
  1379 
       
  1380         r = OCIBindArrayOfStruct (
       
  1381                 columns[i].bindh, d->err,
       
  1382                 columns[i].maxLen,
       
  1383                 sizeof(columns[i].indicators[0]),
       
  1384                 sizeof(columns[i].lengths[0]),
       
  1385                 0);
       
  1386 
       
  1387         if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1388             qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err);
       
  1389             d->q->setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1390                      "Unable to bind column for batch execute"),
       
  1391                      QSqlError::StatementError, d->err));
       
  1392             return false;
       
  1393         }
       
  1394     }
       
  1395 
       
  1396     //finaly we can execute
       
  1397     r = OCIStmtExecute(d->svc, d->sql, d->err,
       
  1398                        arrayBind ? 1 : columns[0].recordCount,
       
  1399                        0, NULL, NULL,
       
  1400                        d->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS);
       
  1401 
       
  1402     if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1403         qOraWarning("QOCIPrivate::execBatch: unable to execute batch statement:", d->err);
       
  1404         d->q->setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1405                         "Unable to execute batch statement"),
       
  1406                         QSqlError::StatementError, d->err));
       
  1407         return false;
       
  1408     }
       
  1409 
       
  1410     // for out parameters we copy data back to value vector
       
  1411     for (i = 0; i < columnCount; ++i) {
       
  1412 
       
  1413         if (!d->isOutValue(i))
       
  1414             continue;
       
  1415 
       
  1416         QVariant::Type tp = boundValues.at(i).type();
       
  1417         if (tp != QVariant::List) {
       
  1418             qOraOutValue(boundValues[i], tmpStorage);
       
  1419             if (*columns[i].indicators == -1)
       
  1420                 boundValues[i] = QVariant(tp);
       
  1421             continue;
       
  1422         }
       
  1423 
       
  1424         QVariantList *list = static_cast<QVariantList *>(const_cast<void*>(boundValues.at(i).data()));
       
  1425 
       
  1426         char* data = columns[i].data;
       
  1427         for (uint r = 0; r < columns[i].recordCount; ++r){
       
  1428 
       
  1429             if (columns[i].indicators[r] == -1) {
       
  1430                 (*list)[r] = QVariant();
       
  1431                 continue;
       
  1432             }
       
  1433 
       
  1434             switch(columns[i].bindAs) {
       
  1435 
       
  1436                 case SQLT_DAT:
       
  1437                     (*list)[r] =  qMakeDate(data + r * columns[i].maxLen);
       
  1438                     break;
       
  1439 
       
  1440                 case SQLT_INT:
       
  1441                     (*list)[r] =  *reinterpret_cast<int*>(data + r * columns[i].maxLen);
       
  1442                     break;
       
  1443 
       
  1444                 case SQLT_UIN:
       
  1445                     (*list)[r] =  *reinterpret_cast<uint*>(data + r * columns[i].maxLen);
       
  1446                     break;
       
  1447 
       
  1448                 case SQLT_FLT:
       
  1449                     (*list)[r] =  *reinterpret_cast<double*>(data + r * columns[i].maxLen);
       
  1450                     break;
       
  1451 
       
  1452                 case SQLT_STR:
       
  1453                     (*list)[r] =  QString::fromUtf16(reinterpret_cast<ushort *>(data
       
  1454                                                                 + r * columns[i].maxLen));
       
  1455                     break;
       
  1456 
       
  1457                 default:
       
  1458                     (*list)[r] =  QByteArray(data + r * columns[i].maxLen, columns[i].maxLen);
       
  1459                 break;
       
  1460             }
       
  1461         }
       
  1462     }
       
  1463 
       
  1464     d->q->setSelect(false);
       
  1465     d->q->setAt(QSql::BeforeFirstRow);
       
  1466     d->q->setActive(true);
       
  1467 
       
  1468     return true;
       
  1469 }
       
  1470 
       
  1471 template<class T, int sz>
       
  1472 int qReadLob(T &buf, const QOCIResultPrivate *d, OCILobLocator *lob)
       
  1473 {
       
  1474     ub1 csfrm;
       
  1475     ub4 amount;
       
  1476     int r;
       
  1477 
       
  1478     // Read this from the database, don't assume we know what it is set to
       
  1479     r = OCILobCharSetForm(d->env, d->err, lob, &csfrm);
       
  1480     if (r != OCI_SUCCESS) {
       
  1481         qOraWarning("OCIResultPrivate::readLobs: Couldn't get LOB char set form: ", d->err);
       
  1482         csfrm = 0;
       
  1483     }
       
  1484 
       
  1485     // Get the length of the LOB (this is in characters)
       
  1486     r = OCILobGetLength(d->svc, d->err, lob, &amount);
       
  1487     if (r == OCI_SUCCESS) {
       
  1488         if (amount == 0) {
       
  1489             // Short cut for null LOBs
       
  1490             buf.resize(0);
       
  1491             return OCI_SUCCESS;
       
  1492         }
       
  1493     } else {
       
  1494         qOraWarning("OCIResultPrivate::readLobs: Couldn't get LOB length: ", d->err);
       
  1495         return r;
       
  1496     }
       
  1497 
       
  1498     // Resize the buffer to hold the LOB contents
       
  1499     buf.resize(amount);
       
  1500 
       
  1501     // Read the LOB into the buffer
       
  1502     r = OCILobRead(d->svc,
       
  1503                    d->err,
       
  1504                    lob,
       
  1505                    &amount,
       
  1506                    1,
       
  1507                    buf.data(),
       
  1508                    buf.size() * sz, // this argument is in bytes, not characters
       
  1509                    0,
       
  1510                    0,
       
  1511                    // Extract the data from a CLOB in UTF-16 (ie. what QString uses internally)
       
  1512                    sz == 1 ? ub2(0) : ub2(QOCIEncoding),
       
  1513                    csfrm);
       
  1514 
       
  1515     if (r != OCI_SUCCESS)
       
  1516         qOraWarning("OCIResultPrivate::readLOBs: Cannot read LOB: ", d->err);
       
  1517 
       
  1518     return r;
       
  1519 }
       
  1520 
       
  1521 int QOCICols::readLOBs(QVector<QVariant> &values, int index)
       
  1522 {
       
  1523     OCILobLocator *lob;
       
  1524     int r = OCI_SUCCESS;
       
  1525 
       
  1526     for (int i = 0; i < size(); ++i) {
       
  1527         const OraFieldInf &fi = fieldInf.at(i);
       
  1528         if (fi.ind == -1 || !(lob = fi.lob))
       
  1529             continue;
       
  1530 
       
  1531         bool isClob = fi.oraType == SQLT_CLOB;
       
  1532         QVariant var;
       
  1533 
       
  1534         if (isClob) {
       
  1535             QString str;
       
  1536             r = qReadLob<QString, sizeof(QChar)>(str, d, lob);
       
  1537             var = str;
       
  1538         } else {
       
  1539             QByteArray buf;
       
  1540             r = qReadLob<QByteArray, sizeof(char)>(buf, d, lob);
       
  1541             var = buf;
       
  1542         }
       
  1543         if (r == OCI_SUCCESS)
       
  1544             values[index + i] = var;
       
  1545         else
       
  1546             break;
       
  1547     }
       
  1548     return r;
       
  1549 }
       
  1550 
       
  1551 int QOCICols::fieldFromDefine(OCIDefine* d)
       
  1552 {
       
  1553     for (int i = 0; i < fieldInf.count(); ++i) {
       
  1554         if (fieldInf.at(i).def == d)
       
  1555             return i;
       
  1556     }
       
  1557     return -1;
       
  1558 }
       
  1559 
       
  1560 void QOCICols::getValues(QVector<QVariant> &v, int index)
       
  1561 {
       
  1562     for (int i = 0; i < fieldInf.size(); ++i) {
       
  1563         const OraFieldInf &fld = fieldInf.at(i);
       
  1564 
       
  1565         if (fld.ind == -1) {
       
  1566             // got a NULL value
       
  1567             v[index + i] = QVariant(fld.typ);
       
  1568             continue;
       
  1569         }
       
  1570 
       
  1571         if (fld.oraType == SQLT_BIN || fld.oraType == SQLT_LBI || fld.oraType == SQLT_LNG)
       
  1572             continue; // already fetched piecewise
       
  1573 
       
  1574         switch (fld.typ) {
       
  1575         case QVariant::DateTime:
       
  1576             v[index + i] = QVariant(qMakeDate(fld.data));
       
  1577             break;
       
  1578         case QVariant::Double:
       
  1579         case QVariant::Int:
       
  1580         case QVariant::LongLong:
       
  1581             if (d->q->numericalPrecisionPolicy() != QSql::HighPrecision) {
       
  1582                 if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionDouble)
       
  1583                         && (fld.typ == QVariant::Double)) {
       
  1584                     v[index + i] = *reinterpret_cast<double *>(fld.data);
       
  1585                     break;
       
  1586                 } else if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
       
  1587                         && (fld.typ == QVariant::LongLong)) {
       
  1588                     qint64 qll = 0;
       
  1589                     int r = OCINumberToInt(d->err, reinterpret_cast<OCINumber *>(fld.data), sizeof(qint64),
       
  1590                                    OCI_NUMBER_SIGNED, &qll);
       
  1591                     if(r == OCI_SUCCESS)
       
  1592                         v[index + i] = qll;
       
  1593                     else
       
  1594                         v[index + i] = QVariant();
       
  1595                     break;
       
  1596                 } else if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
       
  1597                         && (fld.typ == QVariant::Int)) {
       
  1598                     v[index + i] = *reinterpret_cast<int *>(fld.data);
       
  1599                     break;
       
  1600                 }
       
  1601             }
       
  1602             // else fall through
       
  1603         case QVariant::String:
       
  1604             v[index + i] = QString::fromUtf16(reinterpret_cast<const ushort *>(fld.data));
       
  1605             break;
       
  1606         case QVariant::ByteArray:
       
  1607             if (fld.len > 0)
       
  1608                 v[index + i] = QByteArray(fld.data, fld.len);
       
  1609             else
       
  1610                 v[index + i] = QVariant(QVariant::ByteArray);
       
  1611             break;
       
  1612         default:
       
  1613             qWarning("QOCICols::value: unknown data type");
       
  1614             break;
       
  1615         }
       
  1616     }
       
  1617 }
       
  1618 
       
  1619 QOCIResultPrivate::QOCIResultPrivate(QOCIResult *result, const QOCIDriverPrivate *driver)
       
  1620     : cols(0), q(result), env(driver->env), err(0), svc(const_cast<OCISvcCtx*&>(driver->svc)),
       
  1621       sql(0), transaction(driver->transaction), serverVersion(driver->serverVersion),
       
  1622       prefetchRows(driver->prefetchRows), prefetchMem(driver->prefetchMem)
       
  1623 {
       
  1624     int r = OCIHandleAlloc(env,
       
  1625                            reinterpret_cast<void **>(&err),
       
  1626                            OCI_HTYPE_ERROR,
       
  1627                            0,
       
  1628                            0);
       
  1629     if (r != 0)
       
  1630         qWarning("QOCIResult: unable to alloc error handle");
       
  1631 }
       
  1632 
       
  1633 QOCIResultPrivate::~QOCIResultPrivate()
       
  1634 {
       
  1635     delete cols;
       
  1636 
       
  1637     int r = OCIHandleFree(err, OCI_HTYPE_ERROR);
       
  1638     if (r != 0)
       
  1639         qWarning("~QOCIResult: unable to free statement handle");
       
  1640 }
       
  1641 
       
  1642 
       
  1643 ////////////////////////////////////////////////////////////////////////////
       
  1644 
       
  1645 QOCIResult::QOCIResult(const QOCIDriver * db, const QOCIDriverPrivate* p)
       
  1646     : QSqlCachedResult(db)
       
  1647 {
       
  1648     d = new QOCIResultPrivate(this, p);
       
  1649 }
       
  1650 
       
  1651 QOCIResult::~QOCIResult()
       
  1652 {
       
  1653     if (d->sql) {
       
  1654         int r = OCIHandleFree(d->sql, OCI_HTYPE_STMT);
       
  1655         if (r != 0)
       
  1656             qWarning("~QOCIResult: unable to free statement handle");
       
  1657     }
       
  1658     delete d;
       
  1659 }
       
  1660 
       
  1661 QVariant QOCIResult::handle() const
       
  1662 {
       
  1663     return qVariantFromValue(d->sql);
       
  1664 }
       
  1665 
       
  1666 bool QOCIResult::reset (const QString& query)
       
  1667 {
       
  1668     if (!prepare(query))
       
  1669         return false;
       
  1670     return exec();
       
  1671 }
       
  1672 
       
  1673 bool QOCIResult::gotoNext(QSqlCachedResult::ValueCache &values, int index)
       
  1674 {
       
  1675     if (at() == QSql::AfterLastRow)
       
  1676         return false;
       
  1677 
       
  1678     bool piecewise = false;
       
  1679     int r = OCI_SUCCESS;
       
  1680     r = OCIStmtFetch(d->sql, d->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
       
  1681 
       
  1682     if (index < 0) //not interested in values
       
  1683         return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO;
       
  1684 
       
  1685     switch (r) {
       
  1686     case OCI_SUCCESS:
       
  1687         break;
       
  1688     case OCI_SUCCESS_WITH_INFO:
       
  1689         qOraWarning("QOCIResult::gotoNext: SuccessWithInfo: ", d->err);
       
  1690         r = OCI_SUCCESS; //ignore it
       
  1691         break;
       
  1692     case OCI_NO_DATA:
       
  1693         // end of rowset
       
  1694         return false;
       
  1695     case OCI_NEED_DATA:
       
  1696         piecewise = true;
       
  1697         r = OCI_SUCCESS;
       
  1698         break;
       
  1699     case OCI_ERROR:
       
  1700         if (qOraErrorNumber(d->err) == 1406) {
       
  1701             qWarning("QOCI Warning: data truncated for %s", lastQuery().toLocal8Bit().constData());
       
  1702             r = OCI_SUCCESS; /* ignore it */
       
  1703             break;
       
  1704         }
       
  1705         // fall through
       
  1706     default:
       
  1707         qOraWarning("QOCIResult::gotoNext: ", d->err);
       
  1708         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1709                                 "Unable to goto next"),
       
  1710                                 QSqlError::StatementError, d->err));
       
  1711         break;
       
  1712     }
       
  1713 
       
  1714     // need to read piecewise before assigning values
       
  1715     if (r == OCI_SUCCESS && piecewise)
       
  1716         r = d->cols->readPiecewise(values, index);
       
  1717 
       
  1718     if (r == OCI_SUCCESS)
       
  1719         d->cols->getValues(values, index);
       
  1720     if (r == OCI_SUCCESS)
       
  1721         r = d->cols->readLOBs(values, index);
       
  1722     if (r != OCI_SUCCESS)
       
  1723         setAt(QSql::AfterLastRow);
       
  1724     return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO;
       
  1725 }
       
  1726 
       
  1727 int QOCIResult::size()
       
  1728 {
       
  1729     return -1;
       
  1730 }
       
  1731 
       
  1732 int QOCIResult::numRowsAffected()
       
  1733 {
       
  1734     int rowCount;
       
  1735     OCIAttrGet(d->sql,
       
  1736                 OCI_HTYPE_STMT,
       
  1737                 &rowCount,
       
  1738                 NULL,
       
  1739                 OCI_ATTR_ROW_COUNT,
       
  1740                 d->err);
       
  1741     return rowCount;
       
  1742 }
       
  1743 
       
  1744 bool QOCIResult::prepare(const QString& query)
       
  1745 {
       
  1746     int r = 0;
       
  1747     QSqlResult::prepare(query);
       
  1748 
       
  1749     delete d->cols;
       
  1750     d->cols = 0;
       
  1751     QSqlCachedResult::cleanup();
       
  1752 
       
  1753     if (d->sql) {
       
  1754         r = OCIHandleFree(d->sql, OCI_HTYPE_STMT);
       
  1755         if (r != OCI_SUCCESS)
       
  1756             qOraWarning("QOCIResult::prepare: unable to free statement handle:", d->err);
       
  1757     }
       
  1758     if (query.isEmpty())
       
  1759         return false;
       
  1760     r = OCIHandleAlloc(d->env,
       
  1761                        reinterpret_cast<void **>(&d->sql),
       
  1762                        OCI_HTYPE_STMT,
       
  1763                        0,
       
  1764                        0);
       
  1765     if (r != OCI_SUCCESS) {
       
  1766         qOraWarning("QOCIResult::prepare: unable to alloc statement:", d->err);
       
  1767         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1768                      "Unable to alloc statement"), QSqlError::StatementError, d->err));
       
  1769         return false;
       
  1770     }
       
  1771     d->setStatementAttributes();
       
  1772     const OraText *txt = reinterpret_cast<const OraText *>(query.utf16());
       
  1773     const int len = query.length() * sizeof(QChar);
       
  1774     r = OCIStmtPrepare(d->sql,
       
  1775                        d->err,
       
  1776                        txt,
       
  1777                        len,
       
  1778                        OCI_NTV_SYNTAX,
       
  1779                        OCI_DEFAULT);
       
  1780     if (r != OCI_SUCCESS) {
       
  1781         qOraWarning("QOCIResult::prepare: unable to prepare statement:", d->err);
       
  1782         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1783                                 "Unable to prepare statement"), QSqlError::StatementError, d->err));
       
  1784         return false;
       
  1785     }
       
  1786     return true;
       
  1787 }
       
  1788 
       
  1789 bool QOCIResult::exec()
       
  1790 {
       
  1791     int r = 0;
       
  1792     ub2 stmtType=0;
       
  1793     ub4 iters;
       
  1794     ub4 mode;
       
  1795     QList<QByteArray> tmpStorage;
       
  1796     IndicatorArray indicators(boundValueCount());
       
  1797     SizeArray tmpSizes(boundValueCount());
       
  1798 
       
  1799     r = OCIAttrGet(d->sql,
       
  1800                     OCI_HTYPE_STMT,
       
  1801                     &stmtType,
       
  1802                     NULL,
       
  1803                     OCI_ATTR_STMT_TYPE,
       
  1804                     d->err);
       
  1805 
       
  1806     if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1807         qOraWarning("QOCIResult::exec: Unable to get statement type:", d->err);
       
  1808         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1809                      "Unable to get statement type"), QSqlError::StatementError, d->err));
       
  1810 #ifdef QOCI_DEBUG
       
  1811         qDebug() << "lastQuery()" << lastQuery();
       
  1812 #endif
       
  1813         return false;
       
  1814     }
       
  1815 
       
  1816     if (stmtType == OCI_STMT_SELECT) {
       
  1817         iters = 0;
       
  1818         mode = OCI_DEFAULT;
       
  1819     } else {
       
  1820         iters = 1;
       
  1821         mode = d->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS;
       
  1822     }
       
  1823 
       
  1824     // bind placeholders
       
  1825     if (boundValueCount() > 0
       
  1826         && d->bindValues(boundValues(), indicators, tmpSizes, tmpStorage) != OCI_SUCCESS) {
       
  1827         qOraWarning("QOCIResult::exec: unable to bind value: ", d->err);
       
  1828         setLastError(qMakeError(QCoreApplication::translate("QOCIResult", "Unable to bind value"),
       
  1829                     QSqlError::StatementError, d->err));
       
  1830 #ifdef QOCI_DEBUG
       
  1831         qDebug() << "lastQuery()" << lastQuery();
       
  1832 #endif
       
  1833         return false;
       
  1834     }
       
  1835 
       
  1836     // execute
       
  1837     r = OCIStmtExecute(d->svc,
       
  1838                        d->sql,
       
  1839                        d->err,
       
  1840                        iters,
       
  1841                        0,
       
  1842                        0,
       
  1843                        0,
       
  1844                        mode);
       
  1845     if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) {
       
  1846         qOraWarning("QOCIResult::exec: unable to execute statement:", d->err);
       
  1847         setLastError(qMakeError(QCoreApplication::translate("QOCIResult",
       
  1848                      "Unable to execute statement"), QSqlError::StatementError, d->err));
       
  1849 #ifdef QOCI_DEBUG
       
  1850         qDebug() << "lastQuery()" << lastQuery();
       
  1851 #endif
       
  1852         return false;
       
  1853     }
       
  1854 
       
  1855     if (stmtType == OCI_STMT_SELECT) {
       
  1856         ub4 parmCount = 0;
       
  1857         int r = OCIAttrGet(d->sql, OCI_HTYPE_STMT, reinterpret_cast<void **>(&parmCount),
       
  1858                            0, OCI_ATTR_PARAM_COUNT, d->err);
       
  1859         if (r == 0 && !d->cols)
       
  1860             d->cols = new QOCICols(parmCount, d);
       
  1861         setSelect(true);
       
  1862         QSqlCachedResult::init(parmCount);
       
  1863     } else { /* non-SELECT */
       
  1864         setSelect(false);
       
  1865     }
       
  1866     setAt(QSql::BeforeFirstRow);
       
  1867     setActive(true);
       
  1868 
       
  1869     if (hasOutValues())
       
  1870         d->outValues(boundValues(), indicators, tmpStorage);
       
  1871 
       
  1872     return true;
       
  1873 }
       
  1874 
       
  1875 QSqlRecord QOCIResult::record() const
       
  1876 {
       
  1877     QSqlRecord inf;
       
  1878     if (!isActive() || !isSelect() || !d->cols)
       
  1879         return inf;
       
  1880     return d->cols->rec;
       
  1881 }
       
  1882 
       
  1883 QVariant QOCIResult::lastInsertId() const
       
  1884 {
       
  1885     if (isActive()) {
       
  1886         QOCIRowIdPointer ptr(new QOCIRowId(d->env));
       
  1887 
       
  1888         int r = OCIAttrGet(d->sql, OCI_HTYPE_STMT, ptr.constData()->id,
       
  1889                            0, OCI_ATTR_ROWID, d->err);
       
  1890         if (r == OCI_SUCCESS)
       
  1891             return qVariantFromValue(ptr);
       
  1892     }
       
  1893     return QVariant();
       
  1894 }
       
  1895 
       
  1896 void QOCIResult::virtual_hook(int id, void *data)
       
  1897 {
       
  1898     Q_ASSERT(data);
       
  1899 
       
  1900     switch (id) {
       
  1901     case QSqlResult::BatchOperation:
       
  1902         QOCICols::execBatch(d, boundValues(), *reinterpret_cast<bool *>(data));
       
  1903         break;
       
  1904     default:
       
  1905         QSqlCachedResult::virtual_hook(id, data);
       
  1906     }
       
  1907 }
       
  1908 
       
  1909 ////////////////////////////////////////////////////////////////////////////
       
  1910 
       
  1911 
       
  1912 QOCIDriver::QOCIDriver(QObject* parent)
       
  1913     : QSqlDriver(parent)
       
  1914 {
       
  1915     d = new QOCIDriverPrivate();
       
  1916 
       
  1917 #ifdef QOCI_THREADED
       
  1918     const ub4 mode = OCI_UTF16 | OCI_OBJECT | OCI_THREADED;
       
  1919 #else
       
  1920     const ub4 mode = OCI_UTF16 | OCI_OBJECT;
       
  1921 #endif
       
  1922     int r = OCIEnvCreate(&d->env,
       
  1923                          mode,
       
  1924                          NULL,
       
  1925                          NULL,
       
  1926                          NULL,
       
  1927                          NULL,
       
  1928                          0,
       
  1929                          NULL);
       
  1930     if (r != 0) {
       
  1931         qWarning("QOCIDriver: unable to create environment");
       
  1932         setLastError(qMakeError(tr("Unable to initialize", "QOCIDriver"),
       
  1933                      QSqlError::ConnectionError, d->err));
       
  1934         return;
       
  1935     }
       
  1936 
       
  1937     d->allocErrorHandle();
       
  1938 }
       
  1939 
       
  1940 QOCIDriver::QOCIDriver(OCIEnv* env, OCISvcCtx* ctx, QObject* parent)
       
  1941     : QSqlDriver(parent)
       
  1942 {
       
  1943     d = new QOCIDriverPrivate();
       
  1944     d->env = env;
       
  1945     d->svc = ctx;
       
  1946 
       
  1947     d->allocErrorHandle();
       
  1948 
       
  1949     if (env && ctx) {
       
  1950         setOpen(true);
       
  1951         setOpenError(false);
       
  1952     }
       
  1953 }
       
  1954 
       
  1955 QOCIDriver::~QOCIDriver()
       
  1956 {
       
  1957     if (isOpen())
       
  1958         close();
       
  1959     int r = OCIHandleFree(d->err, OCI_HTYPE_ERROR);
       
  1960     if (r != OCI_SUCCESS)
       
  1961         qWarning("Unable to free Error handle: %d", r);
       
  1962     r = OCIHandleFree(d->env, OCI_HTYPE_ENV);
       
  1963     if (r != OCI_SUCCESS)
       
  1964         qWarning("Unable to free Environment handle: %d", r);
       
  1965 
       
  1966     delete d;
       
  1967 }
       
  1968 
       
  1969 bool QOCIDriver::hasFeature(DriverFeature f) const
       
  1970 {
       
  1971     switch (f) {
       
  1972     case Transactions:
       
  1973     case LastInsertId:
       
  1974     case BLOB:
       
  1975     case PreparedQueries:
       
  1976     case NamedPlaceholders:
       
  1977     case BatchOperations:
       
  1978     case LowPrecisionNumbers:
       
  1979         return true;
       
  1980     case QuerySize:
       
  1981     case PositionalPlaceholders:
       
  1982     case SimpleLocking:
       
  1983     case EventNotifications:
       
  1984     case FinishQuery:
       
  1985     case MultipleResultSets:
       
  1986         return false;
       
  1987     case Unicode:
       
  1988         return d->serverVersion >= 9;
       
  1989     }
       
  1990     return false;
       
  1991 }
       
  1992 
       
  1993 static void qParseOpts(const QString &options, QOCIDriverPrivate *d)
       
  1994 {
       
  1995     const QStringList opts(options.split(QLatin1Char(';'), QString::SkipEmptyParts));
       
  1996     for (int i = 0; i < opts.count(); ++i) {
       
  1997         const QString tmp(opts.at(i));
       
  1998         int idx;
       
  1999         if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
       
  2000             qWarning("QOCIDriver::parseArgs: Invalid parameter: '%s'",
       
  2001                      tmp.toLocal8Bit().constData());
       
  2002             continue;
       
  2003         }
       
  2004         const QString opt = tmp.left(idx);
       
  2005         const QString val = tmp.mid(idx + 1).simplified();
       
  2006         bool ok;
       
  2007         if (opt == QLatin1String("OCI_ATTR_PREFETCH_ROWS")) {
       
  2008             d->prefetchRows = val.toInt(&ok);
       
  2009             if (!ok)
       
  2010                 d->prefetchRows = -1;
       
  2011         } else if (opt == QLatin1String("OCI_ATTR_PREFETCH_MEMORY")) {
       
  2012             d->prefetchMem = val.toInt(&ok);
       
  2013             if (!ok)
       
  2014                 d->prefetchMem = -1;
       
  2015         } else {
       
  2016             qWarning ("QOCIDriver::parseArgs: Invalid parameter: '%s'",
       
  2017                       opt.toLocal8Bit().constData());
       
  2018         }
       
  2019     }
       
  2020 }
       
  2021 
       
  2022 bool QOCIDriver::open(const QString & db,
       
  2023                        const QString & user,
       
  2024                        const QString & password,
       
  2025                        const QString & hostname,
       
  2026                        int port,
       
  2027                        const QString &opts)
       
  2028 {
       
  2029     int r;
       
  2030 
       
  2031     if (isOpen())
       
  2032         close();
       
  2033 
       
  2034     qParseOpts(opts, d);
       
  2035 
       
  2036     // Connect without tnsnames.ora if a hostname is given
       
  2037     QString connectionString = db;
       
  2038     if (!hostname.isEmpty())
       
  2039         connectionString = 
       
  2040         QString::fromLatin1("(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=%1)(Port=%2))"
       
  2041                 "(CONNECT_DATA=(SID=%3)))").arg(hostname).arg((port > -1 ? port : 1521)).arg(db);
       
  2042 
       
  2043     r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->srvhp), OCI_HTYPE_SERVER, 0, 0);
       
  2044     if (r == OCI_SUCCESS)
       
  2045         r = OCIServerAttach(d->srvhp, d->err, reinterpret_cast<const OraText *>(connectionString.utf16()),
       
  2046                             connectionString.length() * sizeof(QChar), OCI_DEFAULT);
       
  2047     if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO)
       
  2048         r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->svc), OCI_HTYPE_SVCCTX, 0, 0);
       
  2049     if (r == OCI_SUCCESS)
       
  2050         r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->srvhp, 0, OCI_ATTR_SERVER, d->err);
       
  2051     if (r == OCI_SUCCESS)
       
  2052         r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->authp), OCI_HTYPE_SESSION, 0, 0);
       
  2053     if (r == OCI_SUCCESS)
       
  2054         r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>(user.utf16()),
       
  2055                        user.length() * sizeof(QChar), OCI_ATTR_USERNAME, d->err);
       
  2056     if (r == OCI_SUCCESS)
       
  2057         r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>(password.utf16()),
       
  2058                        password.length() * sizeof(QChar), OCI_ATTR_PASSWORD, d->err);
       
  2059 
       
  2060     OCITrans* trans;
       
  2061     if (r == OCI_SUCCESS)
       
  2062         r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&trans), OCI_HTYPE_TRANS, 0, 0);
       
  2063     if (r == OCI_SUCCESS)
       
  2064         r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, trans, 0, OCI_ATTR_TRANS, d->err);
       
  2065 
       
  2066     if (r == OCI_SUCCESS) {
       
  2067         if (user.isEmpty() && password.isEmpty())
       
  2068             r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_EXT, OCI_DEFAULT);
       
  2069         else
       
  2070             r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_RDBMS, OCI_DEFAULT);
       
  2071     }
       
  2072     if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO)
       
  2073         r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->authp, 0, OCI_ATTR_SESSION, d->err);
       
  2074 
       
  2075     if (r != OCI_SUCCESS) {
       
  2076         setLastError(qMakeError(tr("Unable to logon"), QSqlError::ConnectionError, d->err));
       
  2077         setOpenError(true);
       
  2078         if (d->authp)
       
  2079             OCIHandleFree(d->authp, OCI_HTYPE_SESSION);
       
  2080         d->authp = 0;
       
  2081         if (d->srvhp)
       
  2082             OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER);
       
  2083         d->srvhp = 0;
       
  2084         return false;
       
  2085     }
       
  2086 
       
  2087     // get server version
       
  2088     char vertxt[512];
       
  2089     r = OCIServerVersion(d->svc,
       
  2090                           d->err,
       
  2091                           reinterpret_cast<OraText *>(vertxt),
       
  2092                           sizeof(vertxt),
       
  2093                           OCI_HTYPE_SVCCTX);
       
  2094     if (r != 0) {
       
  2095         qWarning("QOCIDriver::open: could not get Oracle server version.");
       
  2096     } else {
       
  2097         QString versionStr;
       
  2098         versionStr = QString::fromUtf16(reinterpret_cast<ushort *>(vertxt));
       
  2099         QRegExp vers(QLatin1String("([0-9]+)\\.[0-9\\.]+[0-9]"));
       
  2100         if (vers.indexIn(versionStr) >= 0)
       
  2101             d->serverVersion = vers.cap(1).toInt();
       
  2102         if (d->serverVersion == 0)
       
  2103             d->serverVersion = -1;
       
  2104     }
       
  2105 
       
  2106     setOpen(true);
       
  2107     setOpenError(false);
       
  2108     d->user = user;
       
  2109 
       
  2110     return true;
       
  2111 }
       
  2112 
       
  2113 void QOCIDriver::close()
       
  2114 {
       
  2115     if (!isOpen())
       
  2116         return;
       
  2117 
       
  2118     OCISessionEnd(d->svc, d->err, d->authp, OCI_DEFAULT);
       
  2119     OCIServerDetach(d->srvhp, d->err, OCI_DEFAULT);
       
  2120     OCIHandleFree(d->authp, OCI_HTYPE_SESSION);
       
  2121     d->authp = 0;
       
  2122     OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER);
       
  2123     d->srvhp = 0;
       
  2124     OCIHandleFree(d->svc, OCI_HTYPE_SVCCTX);
       
  2125     d->svc = 0;
       
  2126     setOpen(false);
       
  2127     setOpenError(false);
       
  2128 }
       
  2129 
       
  2130 QSqlResult *QOCIDriver::createResult() const
       
  2131 {
       
  2132     return new QOCIResult(this, d);
       
  2133 }
       
  2134 
       
  2135 bool QOCIDriver::beginTransaction()
       
  2136 {
       
  2137     if (!isOpen()) {
       
  2138         qWarning("QOCIDriver::beginTransaction: Database not open");
       
  2139         return false;
       
  2140     }
       
  2141     int r = OCITransStart(d->svc,
       
  2142                           d->err,
       
  2143                           2,
       
  2144                           OCI_TRANS_READWRITE);
       
  2145     if (r == OCI_ERROR) {
       
  2146         qOraWarning("QOCIDriver::beginTransaction: ", d->err);
       
  2147         setLastError(qMakeError(QCoreApplication::translate("QOCIDriver",
       
  2148                                 "Unable to begin transaction"), QSqlError::TransactionError, d->err));
       
  2149         return false;
       
  2150     }
       
  2151     d->transaction = true;
       
  2152     return true;
       
  2153 }
       
  2154 
       
  2155 bool QOCIDriver::commitTransaction()
       
  2156 {
       
  2157     if (!isOpen()) {
       
  2158         qWarning("QOCIDriver::commitTransaction: Database not open");
       
  2159         return false;
       
  2160     }
       
  2161     int r = OCITransCommit(d->svc,
       
  2162                            d->err,
       
  2163                            0);
       
  2164     if (r == OCI_ERROR) {
       
  2165         qOraWarning("QOCIDriver::commitTransaction:", d->err);
       
  2166         setLastError(qMakeError(QCoreApplication::translate("QOCIDriver",
       
  2167                                 "Unable to commit transaction"), QSqlError::TransactionError, d->err));
       
  2168         return false;
       
  2169     }
       
  2170     d->transaction = false;
       
  2171     return true;
       
  2172 }
       
  2173 
       
  2174 bool QOCIDriver::rollbackTransaction()
       
  2175 {
       
  2176     if (!isOpen()) {
       
  2177         qWarning("QOCIDriver::rollbackTransaction: Database not open");
       
  2178         return false;
       
  2179     }
       
  2180     int r = OCITransRollback(d->svc,
       
  2181                              d->err,
       
  2182                              0);
       
  2183     if (r == OCI_ERROR) {
       
  2184         qOraWarning("QOCIDriver::rollbackTransaction:", d->err);
       
  2185         setLastError(qMakeError(QCoreApplication::translate("QOCIDriver",
       
  2186                                 "Unable to rollback transaction"), QSqlError::TransactionError, d->err));
       
  2187         return false;
       
  2188     }
       
  2189     d->transaction = false;
       
  2190     return true;
       
  2191 }
       
  2192 
       
  2193 QStringList QOCIDriver::tables(QSql::TableType type) const
       
  2194 {
       
  2195     QStringList tl;
       
  2196     if (!isOpen())
       
  2197         return tl;
       
  2198 
       
  2199     QSqlQuery t(createResult());
       
  2200     t.setForwardOnly(true);
       
  2201     if (type & QSql::Tables) {
       
  2202         t.exec(QLatin1String("select owner, table_name from all_tables "
       
  2203                 "where owner != 'MDSYS' "
       
  2204                 "and owner != 'LBACSYS' "
       
  2205                 "and owner != 'SYS' "
       
  2206                 "and owner != 'SYSTEM' "
       
  2207                 "and owner != 'WKSYS'"
       
  2208                 "and owner != 'CTXSYS'"
       
  2209                 "and owner != '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         while (t.next()) {
       
  2218             if (t.value(0).toString().toUpper() != user.toUpper())
       
  2219                 tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
       
  2220             else
       
  2221                 tl.append(t.value(1).toString());
       
  2222         }
       
  2223 
       
  2224         // list all table synonyms as well
       
  2225         t.exec(QLatin1String("select owner, synonym_name from all_synonyms "
       
  2226                 "where owner != 'MDSYS' "
       
  2227                 "and owner != 'LBACSYS' "
       
  2228                 "and owner != 'SYS' "
       
  2229                 "and owner != 'SYSTEM' "
       
  2230                 "and owner != 'WKSYS'"
       
  2231                 "and owner != 'CTXSYS'"
       
  2232                 "and owner != 'WMSYS'"));
       
  2233         while (t.next()) {
       
  2234             if (t.value(0).toString() != d->user)
       
  2235                 tl.append(t.value(0).toString() + QLatin1String(".") + t.value(1).toString());
       
  2236             else
       
  2237                 tl.append(t.value(1).toString());
       
  2238         }
       
  2239     }
       
  2240     if (type & QSql::Views) {
       
  2241         t.exec(QLatin1String("select owner, view_name from all_views "
       
  2242                 "where owner != 'MDSYS' "
       
  2243                 "and owner != 'LBACSYS' "
       
  2244                 "and owner != 'SYS' "
       
  2245                 "and owner != 'SYSTEM' "
       
  2246                 "and owner != 'WKSYS'"
       
  2247                 "and owner != 'CTXSYS'"
       
  2248                 "and owner != 'WMSYS'"));
       
  2249         while (t.next()) {
       
  2250             if (t.value(0).toString().toUpper() != d->user.toUpper())
       
  2251                 tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
       
  2252             else
       
  2253                 tl.append(t.value(1).toString());
       
  2254         }
       
  2255     }
       
  2256     if (type & QSql::SystemTables) {
       
  2257         t.exec(QLatin1String("select table_name from dictionary"));
       
  2258         while (t.next()) {
       
  2259             tl.append(t.value(0).toString());
       
  2260         }
       
  2261     }
       
  2262     return tl;
       
  2263 }
       
  2264 
       
  2265 void qSplitTableAndOwner(const QString & tname, QString * tbl,
       
  2266                           QString * owner)
       
  2267 {
       
  2268     int i = tname.indexOf(QLatin1Char('.')); // prefixed with owner?
       
  2269     if (i != -1) {
       
  2270         *tbl = tname.right(tname.length() - i - 1);
       
  2271         *owner = tname.left(i);
       
  2272     } else {
       
  2273         *tbl = tname;
       
  2274     }
       
  2275 }
       
  2276 
       
  2277 QSqlRecord QOCIDriver::record(const QString& tablename) const
       
  2278 {
       
  2279     QSqlRecord fil;
       
  2280     if (!isOpen())
       
  2281         return fil;
       
  2282 
       
  2283     QSqlQuery t(createResult());
       
  2284     // using two separate queries for this is A LOT faster than using
       
  2285     // eg. a sub-query on the sys.synonyms table
       
  2286     QString stmt(QLatin1String("select column_name, data_type, data_length, "
       
  2287                   "data_precision, data_scale, nullable, data_default%1"
       
  2288                   "from all_tab_columns a "
       
  2289                   "where a.table_name=%2"));
       
  2290     if (d->serverVersion >= 9)
       
  2291         stmt = stmt.arg(QLatin1String(", char_length "));
       
  2292     else
       
  2293         stmt = stmt.arg(QLatin1String(" "));
       
  2294     bool buildRecordInfo = false;
       
  2295     QString table, owner, tmpStmt;
       
  2296     qSplitTableAndOwner(tablename, &table, &owner);
       
  2297 
       
  2298     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
  2299         table = stripDelimiters(table, QSqlDriver::TableName);
       
  2300     else
       
  2301         table = table.toUpper();
       
  2302 
       
  2303     tmpStmt = stmt.arg(QLatin1Char('\'') + table + QLatin1Char('\''));
       
  2304     if (owner.isEmpty()) {
       
  2305         owner = d->user;
       
  2306     }
       
  2307 
       
  2308     if (isIdentifierEscaped(owner, QSqlDriver::TableName))
       
  2309         owner = stripDelimiters(owner, QSqlDriver::TableName);
       
  2310     else
       
  2311         owner = owner.toUpper();
       
  2312 
       
  2313     tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\'');
       
  2314     t.setForwardOnly(true);
       
  2315     t.exec(tmpStmt);
       
  2316     if (!t.next()) { // try and see if the tablename is a synonym
       
  2317         stmt = stmt + QLatin1String(" join all_synonyms b "
       
  2318                               "on a.owner=b.table_owner and a.table_name=b.table_name "
       
  2319                               "where b.owner='") + owner +
       
  2320                       QLatin1String("' and b.synonym_name='") + table +
       
  2321                       QLatin1Char('\'');
       
  2322         t.setForwardOnly(true);
       
  2323         t.exec(stmt);
       
  2324         if (t.next())
       
  2325             buildRecordInfo = true;
       
  2326     } else {
       
  2327         buildRecordInfo = true;
       
  2328     }
       
  2329     QStringList keywords = QStringList() << QLatin1String("NUMBER") << QLatin1String("FLOAT") << QLatin1String("BINARY_FLOAT")
       
  2330               << QLatin1String("BINARY_DOUBLE");
       
  2331     if (buildRecordInfo) {
       
  2332         do {
       
  2333             QVariant::Type ty = qDecodeOCIType(t.value(1).toString(), t.numericalPrecisionPolicy());
       
  2334             QSqlField f(t.value(0).toString(), ty);
       
  2335             f.setRequired(t.value(5).toString() == QLatin1String("N"));
       
  2336             f.setPrecision(t.value(4).toInt());
       
  2337             if (d->serverVersion >= 9 && (ty == QVariant::String) && !t.isNull(3) && !keywords.contains(t.value(1).toString())) {
       
  2338                 // Oracle9: data_length == size in bytes, char_length == amount of characters
       
  2339                 f.setLength(t.value(7).toInt());
       
  2340             } else {
       
  2341                 f.setLength(t.value(t.isNull(3) ? 2 : 3).toInt());
       
  2342             }
       
  2343             f.setDefaultValue(t.value(6));
       
  2344             fil.append(f);
       
  2345         } while (t.next());
       
  2346     }
       
  2347     return fil;
       
  2348 }
       
  2349 
       
  2350 QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
       
  2351 {
       
  2352     QSqlIndex idx(tablename);
       
  2353     if (!isOpen())
       
  2354         return idx;
       
  2355     QSqlQuery t(createResult());
       
  2356     QString stmt(QLatin1String("select b.column_name, b.index_name, a.table_name, a.owner "
       
  2357                   "from all_constraints a, all_ind_columns b "
       
  2358                   "where a.constraint_type='P' "
       
  2359                   "and b.index_name = a.constraint_name "
       
  2360                   "and b.index_owner = a.owner"));
       
  2361 
       
  2362     bool buildIndex = false;
       
  2363     QString table, owner, tmpStmt;
       
  2364     qSplitTableAndOwner(tablename, &table, &owner);
       
  2365 
       
  2366     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
  2367         table = stripDelimiters(table, QSqlDriver::TableName);
       
  2368     else
       
  2369         table = table.toUpper();
       
  2370 
       
  2371     tmpStmt = stmt + QLatin1String(" and a.table_name='") + table + QLatin1Char('\'');
       
  2372     if (owner.isEmpty()) {
       
  2373         owner = d->user;
       
  2374     }
       
  2375 
       
  2376     if (isIdentifierEscaped(owner, QSqlDriver::TableName))
       
  2377         owner = stripDelimiters(owner, QSqlDriver::TableName);
       
  2378     else
       
  2379         owner = owner.toUpper();
       
  2380 
       
  2381     tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\'');
       
  2382     t.setForwardOnly(true);
       
  2383     t.exec(tmpStmt);
       
  2384 
       
  2385     if (!t.next()) {
       
  2386         stmt += QLatin1String(" and a.table_name=(select tname from sys.synonyms "
       
  2387                 "where sname='") + table + QLatin1String("' and creator=a.owner)");
       
  2388         t.setForwardOnly(true);
       
  2389         t.exec(stmt);
       
  2390         if (t.next()) {
       
  2391             owner = t.value(3).toString();
       
  2392             buildIndex = true;
       
  2393         }
       
  2394     } else {
       
  2395         buildIndex = true;
       
  2396     }
       
  2397     if (buildIndex) {
       
  2398         QSqlQuery tt(createResult());
       
  2399         tt.setForwardOnly(true);
       
  2400         idx.setName(t.value(1).toString());
       
  2401         do {
       
  2402             tt.exec(QLatin1String("select data_type from all_tab_columns where table_name='") +
       
  2403                      t.value(2).toString() + QLatin1String("' and column_name='") +
       
  2404                      t.value(0).toString() + QLatin1String("' and owner='") +
       
  2405                      owner + QLatin1Char('\''));
       
  2406             if (!tt.next()) {
       
  2407                 return QSqlIndex();
       
  2408             }
       
  2409             QSqlField f(t.value(0).toString(), qDecodeOCIType(tt.value(0).toString(), t.numericalPrecisionPolicy()));
       
  2410             idx.append(f);
       
  2411         } while (t.next());
       
  2412         return idx;
       
  2413     }
       
  2414     return QSqlIndex();
       
  2415 }
       
  2416 
       
  2417 QString QOCIDriver::formatValue(const QSqlField &field, bool trimStrings) const
       
  2418 {
       
  2419     switch (field.type()) {
       
  2420     case QVariant::DateTime: {
       
  2421         QDateTime datetime = field.value().toDateTime();
       
  2422         QString datestring;
       
  2423         if (datetime.isValid()) {
       
  2424             datestring = QLatin1String("TO_DATE('") + QString::number(datetime.date().year())
       
  2425                          + QLatin1Char('-')
       
  2426                          + QString::number(datetime.date().month()) + QLatin1Char('-')
       
  2427                          + QString::number(datetime.date().day()) + QLatin1Char(' ')
       
  2428                          + QString::number(datetime.time().hour()) + QLatin1Char(':')
       
  2429                          + QString::number(datetime.time().minute()) + QLatin1Char(':')
       
  2430                          + QString::number(datetime.time().second())
       
  2431                          + QLatin1String("','YYYY-MM-DD HH24:MI:SS')");
       
  2432         } else {
       
  2433             datestring = QLatin1String("NULL");
       
  2434         }
       
  2435         return datestring;
       
  2436     }
       
  2437     case QVariant::Time: {
       
  2438         QDateTime datetime = field.value().toDateTime();
       
  2439         QString datestring;
       
  2440         if (datetime.isValid()) {
       
  2441             datestring = QLatin1String("TO_DATE('")
       
  2442                          + QString::number(datetime.time().hour()) + QLatin1Char(':')
       
  2443                          + QString::number(datetime.time().minute()) + QLatin1Char(':')
       
  2444                          + QString::number(datetime.time().second())
       
  2445                          + QLatin1String("','HH24:MI:SS')");
       
  2446         } else {
       
  2447             datestring = QLatin1String("NULL");
       
  2448         }
       
  2449         return datestring;
       
  2450     }
       
  2451     case QVariant::Date: {
       
  2452         QDate date = field.value().toDate();
       
  2453         QString datestring;
       
  2454         if (date.isValid()) {
       
  2455             datestring = QLatin1String("TO_DATE('") + QString::number(date.year()) +
       
  2456                          QLatin1Char('-') +
       
  2457                          QString::number(date.month()) + QLatin1Char('-') +
       
  2458                          QString::number(date.day()) + QLatin1String("','YYYY-MM-DD')");
       
  2459         } else {
       
  2460             datestring = QLatin1String("NULL");
       
  2461         }
       
  2462         return datestring;
       
  2463     }
       
  2464     default:
       
  2465         break;
       
  2466     }
       
  2467     return QSqlDriver::formatValue(field, trimStrings);
       
  2468 }
       
  2469 
       
  2470 QVariant QOCIDriver::handle() const
       
  2471 {
       
  2472     return qVariantFromValue(d->env);
       
  2473 }
       
  2474 
       
  2475 QString QOCIDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
       
  2476 {
       
  2477     QString res = identifier;
       
  2478     if(!identifier.isEmpty() && !isIdentifierEscaped(identifier, type)) {
       
  2479         res.replace(QLatin1Char('"'), QLatin1String("\"\""));
       
  2480         res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
       
  2481         res.replace(QLatin1Char('.'), QLatin1String("\".\""));
       
  2482     }
       
  2483     return res;
       
  2484 }
       
  2485 
       
  2486 QT_END_NAMESPACE