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 **>(¶m),
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 **>(¶m),
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