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