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