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