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