source: trunk/src/sql/drivers/oci/qsql_oci.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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