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