source: trunk/src/sql/drivers/db2/qsql_db2.cpp@ 182

Last change on this file since 182 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 52.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtSql module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsql_db2.h"
43#include <qcoreapplication.h>
44#include <qdatetime.h>
45#include <qsqlfield.h>
46#include <qsqlerror.h>
47#include <qsqlindex.h>
48#include <qsqlrecord.h>
49#include <qstringlist.h>
50#include <qvarlengtharray.h>
51#include <qvector.h>
52
53#ifndef UNICODE
54#define UNICODE
55#endif
56
57#if defined(Q_CC_BOR)
58// DB2's sqlsystm.h (included through sqlcli1.h) defines the SQL_BIGINT_TYPE
59// and SQL_BIGUINT_TYPE to wrong the types for Borland; so do the defines to
60// the right type before including the header
61#define SQL_BIGINT_TYPE qint64
62#define SQL_BIGUINT_TYPE quint64
63#endif
64
65#include <sqlcli1.h>
66
67#include <string.h>
68
69QT_BEGIN_NAMESPACE
70
71static const int COLNAMESIZE = 255;
72static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
73
74class QDB2DriverPrivate
75{
76public:
77 QDB2DriverPrivate(): hEnv(0), hDbc(0) {}
78 SQLHANDLE hEnv;
79 SQLHANDLE hDbc;
80 QString user;
81};
82
83class QDB2ResultPrivate
84{
85public:
86 QDB2ResultPrivate(const QDB2DriverPrivate* d): dp(d), hStmt(0), precisionPolicy(QSql::HighPrecision)
87 {}
88 ~QDB2ResultPrivate()
89 {
90 for (int i = 0; i < valueCache.count(); ++i)
91 delete valueCache[i];
92 }
93
94 const QDB2DriverPrivate* dp;
95 SQLHANDLE hStmt;
96 QSqlRecord recInf;
97 QVector<QVariant*> valueCache;
98 QSql::NumericalPrecisionPolicy precisionPolicy;
99};
100
101static QString qFromTChar(SQLTCHAR* str)
102{
103#ifdef UNICODE
104 return QString::fromUtf16(str);
105#else
106 return QString::fromLocal8Bit((const char*) str);
107#endif
108}
109
110// dangerous!! (but fast). Don't use in functions that
111// require out parameters!
112static SQLTCHAR* qToTChar(const QString& str)
113{
114#ifdef UNICODE
115 return (SQLTCHAR*)str.utf16();
116#else
117 return (unsigned char*) str.ascii();
118#endif
119}
120
121static QString qWarnDB2Handle(int handleType, SQLHANDLE handle)
122{
123 SQLINTEGER nativeCode;
124 SQLSMALLINT msgLen;
125 SQLRETURN r = SQL_ERROR;
126 SQLTCHAR state[SQL_SQLSTATE_SIZE + 1];
127 SQLTCHAR description[SQL_MAX_MESSAGE_LENGTH];
128 r = SQLGetDiagRec(handleType,
129 handle,
130 1,
131 (SQLTCHAR*) state,
132 &nativeCode,
133 (SQLTCHAR*) description,
134 SQL_MAX_MESSAGE_LENGTH - 1, /* in bytes, not in characters */
135 &msgLen);
136 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
137 return QString(qFromTChar(description));
138 return QString();
139}
140
141static QString qDB2Warn(const QDB2DriverPrivate* d)
142{
143 return (qWarnDB2Handle(SQL_HANDLE_ENV, d->hEnv) + QLatin1Char(' ')
144 + qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc));
145}
146
147static QString qDB2Warn(const QDB2ResultPrivate* d)
148{
149 return (qWarnDB2Handle(SQL_HANDLE_ENV, d->dp->hEnv) + QLatin1Char(' ')
150 + qWarnDB2Handle(SQL_HANDLE_DBC, d->dp->hDbc)
151 + qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt));
152}
153
154static void qSqlWarning(const QString& message, const QDB2DriverPrivate* d)
155{
156 qWarning("%s\tError: %s", message.toLocal8Bit().constData(),
157 qDB2Warn(d).toLocal8Bit().constData());
158}
159
160static void qSqlWarning(const QString& message, const QDB2ResultPrivate* d)
161{
162 qWarning("%s\tError: %s", message.toLocal8Bit().constData(),
163 qDB2Warn(d).toLocal8Bit().constData());
164}
165
166static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
167 const QDB2DriverPrivate* p)
168{
169 return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type);
170}
171
172static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
173 const QDB2ResultPrivate* p)
174{
175 return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type);
176}
177
178static QVariant::Type qDecodeDB2Type(SQLSMALLINT sqltype)
179{
180 QVariant::Type type = QVariant::Invalid;
181 switch (sqltype) {
182 case SQL_REAL:
183 case SQL_FLOAT:
184 case SQL_DOUBLE:
185 case SQL_DECIMAL:
186 case SQL_NUMERIC:
187 type = QVariant::Double;
188 break;
189 case SQL_SMALLINT:
190 case SQL_INTEGER:
191 case SQL_BIT:
192 case SQL_TINYINT:
193 type = QVariant::Int;
194 break;
195 case SQL_BIGINT:
196 type = QVariant::LongLong;
197 break;
198 case SQL_BLOB:
199 case SQL_BINARY:
200 case SQL_VARBINARY:
201 case SQL_LONGVARBINARY:
202 case SQL_CLOB:
203 case SQL_DBCLOB:
204 type = QVariant::ByteArray;
205 break;
206 case SQL_DATE:
207 case SQL_TYPE_DATE:
208 type = QVariant::Date;
209 break;
210 case SQL_TIME:
211 case SQL_TYPE_TIME:
212 type = QVariant::Time;
213 break;
214 case SQL_TIMESTAMP:
215 case SQL_TYPE_TIMESTAMP:
216 type = QVariant::DateTime;
217 break;
218 case SQL_WCHAR:
219 case SQL_WVARCHAR:
220 case SQL_WLONGVARCHAR:
221 case SQL_CHAR:
222 case SQL_VARCHAR:
223 case SQL_LONGVARCHAR:
224 type = QVariant::String;
225 break;
226 default:
227 type = QVariant::ByteArray;
228 break;
229 }
230 return type;
231}
232
233static QSqlField qMakeFieldInfo(const QDB2ResultPrivate* d, int i)
234{
235 SQLSMALLINT colNameLen;
236 SQLSMALLINT colType;
237 SQLUINTEGER colSize;
238 SQLSMALLINT colScale;
239 SQLSMALLINT nullable;
240 SQLRETURN r = SQL_ERROR;
241 SQLTCHAR colName[COLNAMESIZE];
242 r = SQLDescribeCol(d->hStmt,
243 i+1,
244 colName,
245 (SQLSMALLINT) COLNAMESIZE,
246 &colNameLen,
247 &colType,
248 &colSize,
249 &colScale,
250 &nullable);
251
252 if (r != SQL_SUCCESS) {
253 qSqlWarning(QString::fromLatin1("qMakeFieldInfo: Unable to describe column %1").arg(i), d);
254 return QSqlField();
255 }
256 QSqlField f(qFromTChar(colName), qDecodeDB2Type(colType));
257 // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
258 if (nullable == SQL_NO_NULLS)
259 f.setRequired(true);
260 else if (nullable == SQL_NULLABLE)
261 f.setRequired(false);
262 // else required is unknown
263 f.setLength(colSize == 0 ? -1 : int(colSize));
264 f.setPrecision(colScale == 0 ? -1 : int(colScale));
265 f.setSqlType(int(colType));
266 return f;
267}
268
269static int qGetIntData(SQLHANDLE hStmt, int column, bool& isNull)
270{
271 SQLINTEGER intbuf;
272 isNull = false;
273 SQLINTEGER lengthIndicator = 0;
274 SQLRETURN r = SQLGetData(hStmt,
275 column + 1,
276 SQL_C_SLONG,
277 (SQLPOINTER) &intbuf,
278 0,
279 &lengthIndicator);
280 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
281 isNull = true;
282 return 0;
283 }
284 return int(intbuf);
285}
286
287static double qGetDoubleData(SQLHANDLE hStmt, int column, bool& isNull)
288{
289 SQLDOUBLE dblbuf;
290 isNull = false;
291 SQLINTEGER lengthIndicator = 0;
292 SQLRETURN r = SQLGetData(hStmt,
293 column+1,
294 SQL_C_DOUBLE,
295 (SQLPOINTER) &dblbuf,
296 0,
297 &lengthIndicator);
298 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
299 isNull = true;
300 return 0.0;
301 }
302
303 return (double) dblbuf;
304}
305
306static SQLBIGINT qGetBigIntData(SQLHANDLE hStmt, int column, bool& isNull)
307{
308 SQLBIGINT lngbuf = Q_INT64_C(0);
309 isNull = false;
310 SQLINTEGER lengthIndicator = 0;
311 SQLRETURN r = SQLGetData(hStmt,
312 column+1,
313 SQL_C_SBIGINT,
314 (SQLPOINTER) &lngbuf,
315 0,
316 &lengthIndicator);
317 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA)
318 isNull = true;
319
320 return lngbuf;
321}
322
323static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool& isNull)
324{
325 QString fieldVal;
326 SQLRETURN r = SQL_ERROR;
327 SQLINTEGER lengthIndicator = 0;
328
329 if (colSize <= 0)
330 colSize = 255;
331 else if (colSize > 65536) // limit buffer size to 64 KB
332 colSize = 65536;
333 else
334 colSize++; // make sure there is room for more than the 0 termination
335 SQLTCHAR* buf = new SQLTCHAR[colSize];
336
337 while (true) {
338 r = SQLGetData(hStmt,
339 column+1,
340#ifdef UNICODE
341 SQL_C_WCHAR,
342#else
343 SQL_C_CHAR,
344#endif
345 (SQLPOINTER)buf,
346 colSize * sizeof(SQLTCHAR),
347 &lengthIndicator);
348 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
349 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
350 fieldVal.clear();
351 isNull = true;
352 break;
353 }
354 fieldVal += qFromTChar(buf);
355 } else if (r == SQL_NO_DATA) {
356 break;
357 } else {
358 qWarning("qGetStringData: Error while fetching data (%d)", r);
359 fieldVal.clear();
360 break;
361 }
362 }
363 delete[] buf;
364 return fieldVal;
365}
366
367static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLINTEGER& lengthIndicator, bool& isNull)
368{
369 QByteArray fieldVal;
370 SQLSMALLINT colNameLen;
371 SQLSMALLINT colType;
372 SQLUINTEGER colSize;
373 SQLSMALLINT colScale;
374 SQLSMALLINT nullable;
375 SQLRETURN r = SQL_ERROR;
376
377 SQLTCHAR colName[COLNAMESIZE];
378 r = SQLDescribeCol(hStmt,
379 column+1,
380 colName,
381 COLNAMESIZE,
382 &colNameLen,
383 &colType,
384 &colSize,
385 &colScale,
386 &nullable);
387 if (r != SQL_SUCCESS)
388 qWarning("qGetBinaryData: Unable to describe column %d", column);
389 // SQLDescribeCol may return 0 if size cannot be determined
390 if (!colSize)
391 colSize = 255;
392 else if (colSize > 65536) // read the field in 64 KB chunks
393 colSize = 65536;
394 char * buf = new char[colSize];
395 while (true) {
396 r = SQLGetData(hStmt,
397 column+1,
398 colType == SQL_DBCLOB ? SQL_C_CHAR : SQL_C_BINARY,
399 (SQLPOINTER) buf,
400 colSize,
401 &lengthIndicator);
402 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
403 if (lengthIndicator == SQL_NULL_DATA) {
404 isNull = true;
405 break;
406 } else {
407 int rSize;
408 r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
409 if (lengthIndicator == SQL_NO_TOTAL) // size cannot be determined
410 rSize = colSize;
411 fieldVal.append(QByteArray(buf, rSize));
412 if (r == SQL_SUCCESS) // the whole field was read in one chunk
413 break;
414 }
415 } else {
416 break;
417 }
418 }
419 delete [] buf;
420 return fieldVal;
421}
422
423static void qSplitTableQualifier(const QString & qualifier, QString * catalog,
424 QString * schema, QString * table)
425{
426 if (!catalog || !schema || !table)
427 return;
428 QStringList l = qualifier.split(QLatin1Char('.'));
429 if (l.count() > 3)
430 return; // can't possibly be a valid table qualifier
431 int i = 0, n = l.count();
432 if (n == 1) {
433 *table = qualifier;
434 } else {
435 for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
436 if (n == 3) {
437 if (i == 0)
438 *catalog = *it;
439 else if (i == 1)
440 *schema = *it;
441 else if (i == 2)
442 *table = *it;
443 } else if (n == 2) {
444 if (i == 0)
445 *schema = *it;
446 else if (i == 1)
447 *table = *it;
448 }
449 i++;
450 }
451 }
452}
453
454// creates a QSqlField from a valid hStmt generated
455// by SQLColumns. The hStmt has to point to a valid position.
456static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt)
457{
458 bool isNull;
459 int type = qGetIntData(hStmt, 4, isNull);
460 QSqlField f(qGetStringData(hStmt, 3, -1, isNull), qDecodeDB2Type(type));
461 int required = qGetIntData(hStmt, 10, isNull); // nullable-flag
462 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
463 if (required == SQL_NO_NULLS)
464 f.setRequired(true);
465 else if (required == SQL_NULLABLE)
466 f.setRequired(false);
467 // else we don't know.
468 f.setLength(qGetIntData(hStmt, 6, isNull)); // column size
469 f.setPrecision(qGetIntData(hStmt, 8, isNull)); // precision
470 f.setSqlType(type);
471 return f;
472}
473
474static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwardOnly = true)
475{
476 SQLRETURN r;
477 if (!d->hStmt) {
478 r = SQLAllocHandle(SQL_HANDLE_STMT,
479 d->dp->hDbc,
480 &d->hStmt);
481 if (r != SQL_SUCCESS) {
482 qSqlWarning(QLatin1String("QDB2Result::reset: Unable to allocate statement handle"), d);
483 return false;
484 }
485 } else {
486 r = SQLFreeStmt(d->hStmt, SQL_CLOSE);
487 if (r != SQL_SUCCESS) {
488 qSqlWarning(QLatin1String("QDB2Result::reset: Unable to close statement handle"), d);
489 return false;
490 }
491 }
492
493 if (!setForwardOnly)
494 return true;
495
496 if (forwardOnly) {
497 r = SQLSetStmtAttr(d->hStmt,
498 SQL_ATTR_CURSOR_TYPE,
499 (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
500 SQL_IS_UINTEGER);
501 } else {
502 r = SQLSetStmtAttr(d->hStmt,
503 SQL_ATTR_CURSOR_TYPE,
504 (SQLPOINTER) SQL_CURSOR_STATIC,
505 SQL_IS_UINTEGER);
506 }
507 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
508 qSqlWarning(QString::fromLatin1("QDB2Result::reset: Unable to set %1 attribute.").arg(
509 forwardOnly ? QLatin1String("SQL_CURSOR_FORWARD_ONLY")
510 : QLatin1String("SQL_CURSOR_STATIC")), d);
511 return false;
512 }
513 return true;
514}
515
516QVariant QDB2Result::handle() const
517{
518 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
519}
520
521/************************************/
522
523QDB2Result::QDB2Result(const QDB2Driver* dr, const QDB2DriverPrivate* dp)
524 : QSqlResult(dr)
525{
526 d = new QDB2ResultPrivate(dp);
527}
528
529QDB2Result::~QDB2Result()
530{
531 if (d->hStmt) {
532 SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
533 if (r != SQL_SUCCESS)
534 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
535 + QString::number(r), d);
536 }
537 delete d;
538}
539
540bool QDB2Result::reset (const QString& query)
541{
542 setActive(false);
543 setAt(QSql::BeforeFirstRow);
544 SQLRETURN r;
545
546 d->recInf.clear();
547 d->valueCache.clear();
548
549 if (!qMakeStatement(d, isForwardOnly()))
550 return false;
551
552 r = SQLExecDirect(d->hStmt,
553 qToTChar(query),
554 (SQLINTEGER) query.length());
555 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
556 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
557 "Unable to execute statement"), QSqlError::StatementError, d));
558 return false;
559 }
560 SQLSMALLINT count;
561 r = SQLNumResultCols(d->hStmt, &count);
562 if (count) {
563 setSelect(true);
564 for (int i = 0; i < count; ++i) {
565 d->recInf.append(qMakeFieldInfo(d, i));
566 }
567 } else {
568 setSelect(false);
569 }
570 d->valueCache.resize(count);
571 setActive(true);
572 return true;
573}
574
575bool QDB2Result::prepare(const QString& query)
576{
577 setActive(false);
578 setAt(QSql::BeforeFirstRow);
579 SQLRETURN r;
580
581 d->recInf.clear();
582 d->valueCache.clear();
583
584 if (!qMakeStatement(d, isForwardOnly()))
585 return false;
586
587 r = SQLPrepare(d->hStmt,
588 qToTChar(query),
589 (SQLINTEGER) query.length());
590
591 if (r != SQL_SUCCESS) {
592 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
593 "Unable to prepare statement"), QSqlError::StatementError, d));
594 return false;
595 }
596 return true;
597}
598
599bool QDB2Result::exec()
600{
601 QList<QByteArray> tmpStorage; // holds temporary ptrs
602 QVarLengthArray<SQLINTEGER, 32> indicators(boundValues().count());
603
604 memset(indicators.data(), 0, indicators.size() * sizeof(SQLINTEGER));
605 setActive(false);
606 setAt(QSql::BeforeFirstRow);
607 SQLRETURN r;
608
609 d->recInf.clear();
610 d->valueCache.clear();
611
612 if (!qMakeStatement(d, isForwardOnly(), false))
613 return false;
614
615
616 QVector<QVariant> &values = boundValues();
617 int i;
618 for (i = 0; i < values.count(); ++i) {
619 // bind parameters - only positional binding allowed
620 SQLINTEGER *ind = &indicators[i];
621 if (values.at(i).isNull())
622 *ind = SQL_NULL_DATA;
623 if (bindValueType(i) & QSql::Out)
624 values[i].detach();
625
626 switch (values.at(i).type()) {
627 case QVariant::Date: {
628 QByteArray ba;
629 ba.resize(sizeof(DATE_STRUCT));
630 DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData();
631 QDate qdt = values.at(i).toDate();
632 dt->year = qdt.year();
633 dt->month = qdt.month();
634 dt->day = qdt.day();
635 r = SQLBindParameter(d->hStmt,
636 i + 1,
637 qParamType[(QFlag)(bindValueType(i)) & 3],
638 SQL_C_DATE,
639 SQL_DATE,
640 0,
641 0,
642 (void *) dt,
643 0,
644 *ind == SQL_NULL_DATA ? ind : NULL);
645 tmpStorage.append(ba);
646 break; }
647 case QVariant::Time: {
648 QByteArray ba;
649 ba.resize(sizeof(TIME_STRUCT));
650 TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData();
651 QTime qdt = values.at(i).toTime();
652 dt->hour = qdt.hour();
653 dt->minute = qdt.minute();
654 dt->second = qdt.second();
655 r = SQLBindParameter(d->hStmt,
656 i + 1,
657 qParamType[(QFlag)(bindValueType(i)) & 3],
658 SQL_C_TIME,
659 SQL_TIME,
660 0,
661 0,
662 (void *) dt,
663 0,
664 *ind == SQL_NULL_DATA ? ind : NULL);
665 tmpStorage.append(ba);
666 break; }
667 case QVariant::DateTime: {
668 QByteArray ba;
669 ba.resize(sizeof(TIMESTAMP_STRUCT));
670 TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData();
671 QDateTime qdt = values.at(i).toDateTime();
672 dt->year = qdt.date().year();
673 dt->month = qdt.date().month();
674 dt->day = qdt.date().day();
675 dt->hour = qdt.time().hour();
676 dt->minute = qdt.time().minute();
677 dt->second = qdt.time().second();
678 dt->fraction = qdt.time().msec() * 1000000;
679 r = SQLBindParameter(d->hStmt,
680 i + 1,
681 qParamType[(QFlag)(bindValueType(i)) & 3],
682 SQL_C_TIMESTAMP,
683 SQL_TIMESTAMP,
684 0,
685 0,
686 (void *) dt,
687 0,
688 *ind == SQL_NULL_DATA ? ind : NULL);
689 tmpStorage.append(ba);
690 break; }
691 case QVariant::Int:
692 r = SQLBindParameter(d->hStmt,
693 i + 1,
694 qParamType[(QFlag)(bindValueType(i)) & 3],
695 SQL_C_SLONG,
696 SQL_INTEGER,
697 0,
698 0,
699 (void *)values.at(i).constData(),
700 0,
701 *ind == SQL_NULL_DATA ? ind : NULL);
702 break;
703 case QVariant::Double:
704 r = SQLBindParameter(d->hStmt,
705 i + 1,
706 qParamType[(QFlag)(bindValueType(i)) & 3],
707 SQL_C_DOUBLE,
708 SQL_DOUBLE,
709 0,
710 0,
711 (void *)values.at(i).constData(),
712 0,
713 *ind == SQL_NULL_DATA ? ind : NULL);
714 break;
715 case QVariant::ByteArray: {
716 int len = values.at(i).toByteArray().size();
717 if (*ind != SQL_NULL_DATA)
718 *ind = len;
719 r = SQLBindParameter(d->hStmt,
720 i + 1,
721 qParamType[(QFlag)(bindValueType(i)) & 3],
722 SQL_C_BINARY,
723 SQL_LONGVARBINARY,
724 len,
725 0,
726 (void *)values.at(i).toByteArray().constData(),
727 len,
728 ind);
729 break; }
730 case QVariant::String:
731#ifdef UNICODE
732 {
733 QString str(values.at(i).toString());
734 if (*ind != SQL_NULL_DATA)
735 *ind = str.length() * sizeof(QChar);
736 if (bindValueType(i) & QSql::Out) {
737 QByteArray ba((char*)str.utf16(), str.capacity() * sizeof(QChar));
738 r = SQLBindParameter(d->hStmt,
739 i + 1,
740 qParamType[(QFlag)(bindValueType(i)) & 3],
741 SQL_C_WCHAR,
742 SQL_WVARCHAR,
743 str.length(),
744 0,
745 (void *)ba.constData(),
746 ba.size(),
747 ind);
748 tmpStorage.append(ba);
749 } else {
750 void *data = (void*)str.utf16();
751 int len = str.length();
752 r = SQLBindParameter(d->hStmt,
753 i + 1,
754 qParamType[(QFlag)(bindValueType(i)) & 3],
755 SQL_C_WCHAR,
756 SQL_WVARCHAR,
757 len,
758 0,
759 data,
760 len * sizeof(QChar),
761 ind);
762 }
763 break;
764 }
765#endif
766 // fall through
767 default: {
768 QByteArray ba = values.at(i).toString().toAscii();
769 int len = ba.length() + 1;
770 if (*ind != SQL_NULL_DATA)
771 *ind = ba.length();
772 r = SQLBindParameter(d->hStmt,
773 i + 1,
774 qParamType[(QFlag)(bindValueType(i)) & 3],
775 SQL_C_CHAR,
776 SQL_VARCHAR,
777 len,
778 0,
779 (void *) ba.constData(),
780 len,
781 ind);
782 tmpStorage.append(ba);
783 break; }
784 }
785 if (r != SQL_SUCCESS) {
786 qWarning("QDB2Result::exec: unable to bind variable: %s",
787 qDB2Warn(d).toLocal8Bit().constData());
788 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
789 "Unable to bind variable"), QSqlError::StatementError, d));
790 return false;
791 }
792 }
793
794 r = SQLExecute(d->hStmt);
795 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
796 qWarning("QDB2Result::exec: Unable to execute statement: %s",
797 qDB2Warn(d).toLocal8Bit().constData());
798 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
799 "Unable to execute statement"), QSqlError::StatementError, d));
800 return false;
801 }
802 SQLSMALLINT count;
803 r = SQLNumResultCols(d->hStmt, &count);
804 if (count) {
805 setSelect(true);
806 for (int i = 0; i < count; ++i) {
807 d->recInf.append(qMakeFieldInfo(d, i));
808 }
809 } else {
810 setSelect(false);
811 }
812 setActive(true);
813 d->valueCache.resize(count);
814
815 //get out parameters
816 if (!hasOutValues())
817 return true;
818
819 for (i = 0; i < values.count(); ++i) {
820 switch (values[i].type()) {
821 case QVariant::Date: {
822 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
823 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
824 break; }
825 case QVariant::Time: {
826 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
827 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
828 break; }
829 case QVariant::DateTime: {
830 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT *)tmpStorage.takeFirst().constData());
831 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
832 QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
833 break; }
834 case QVariant::Int:
835 case QVariant::Double:
836 case QVariant::ByteArray:
837 break;
838 case QVariant::String:
839#ifdef UNICODE
840 if (bindValueType(i) & QSql::Out)
841 values[i] = QString::fromUtf16((ushort*)tmpStorage.takeFirst().constData());
842 break;
843#endif
844 // fall through
845 default: {
846 values[i] = QString::fromAscii(tmpStorage.takeFirst().constData());
847 break; }
848 }
849 if (indicators[i] == SQL_NULL_DATA)
850 values[i] = QVariant(values[i].type());
851 }
852 return true;
853}
854
855bool QDB2Result::fetch(int i)
856{
857 if (isForwardOnly() && i < at())
858 return false;
859 if (i == at())
860 return true;
861 d->valueCache.fill(0);
862 int actualIdx = i + 1;
863 if (actualIdx <= 0) {
864 setAt(QSql::BeforeFirstRow);
865 return false;
866 }
867 SQLRETURN r;
868 if (isForwardOnly()) {
869 bool ok = true;
870 while (ok && i > at())
871 ok = fetchNext();
872 return ok;
873 } else {
874 r = SQLFetchScroll(d->hStmt,
875 SQL_FETCH_ABSOLUTE,
876 actualIdx);
877 }
878 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
879 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
880 "Unable to fetch record %1").arg(i), QSqlError::StatementError, d));
881 return false;
882 }
883 setAt(i);
884 return true;
885}
886
887bool QDB2Result::fetchNext()
888{
889 SQLRETURN r;
890 d->valueCache.fill(0);
891 r = SQLFetchScroll(d->hStmt,
892 SQL_FETCH_NEXT,
893 0);
894 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
895 if (r != SQL_NO_DATA)
896 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
897 "Unable to fetch next"), QSqlError::StatementError, d));
898 return false;
899 }
900 setAt(at() + 1);
901 return true;
902}
903
904bool QDB2Result::fetchFirst()
905{
906 if (isForwardOnly() && at() != QSql::BeforeFirstRow)
907 return false;
908 if (isForwardOnly())
909 return fetchNext();
910 d->valueCache.fill(0);
911 SQLRETURN r;
912 r = SQLFetchScroll(d->hStmt,
913 SQL_FETCH_FIRST,
914 0);
915 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
916 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", "Unable to fetch first"),
917 QSqlError::StatementError, d));
918 return false;
919 }
920 setAt(0);
921 return true;
922}
923
924bool QDB2Result::fetchLast()
925{
926 d->valueCache.fill(0);
927
928 int i = at();
929 if (i == QSql::AfterLastRow) {
930 if (isForwardOnly()) {
931 return false;
932 } else {
933 if (!fetch(0))
934 return false;
935 i = at();
936 }
937 }
938
939 while (fetchNext())
940 ++i;
941
942 if (i == QSql::BeforeFirstRow) {
943 setAt(QSql::AfterLastRow);
944 return false;
945 }
946
947 if (!isForwardOnly())
948 return fetch(i);
949
950 setAt(i);
951 return true;
952}
953
954
955QVariant QDB2Result::data(int field)
956{
957 if (field >= d->recInf.count()) {
958 qWarning("QDB2Result::data: column %d out of range", field);
959 return QVariant();
960 }
961 SQLRETURN r = 0;
962 SQLINTEGER lengthIndicator = 0;
963 bool isNull = false;
964 const QSqlField info = d->recInf.field(field);
965
966 if (!info.isValid() || field >= d->valueCache.size())
967 return QVariant();
968
969 if (d->valueCache[field])
970 return *d->valueCache[field];
971
972
973 QVariant* v = 0;
974 switch (info.type()) {
975 case QVariant::LongLong:
976 v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull));
977 break;
978 case QVariant::Int:
979 v = new QVariant(qGetIntData(d->hStmt, field, isNull));
980 break;
981 case QVariant::Date: {
982 DATE_STRUCT dbuf;
983 r = SQLGetData(d->hStmt,
984 field + 1,
985 SQL_C_DATE,
986 (SQLPOINTER) &dbuf,
987 0,
988 &lengthIndicator);
989 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
990 v = new QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
991 } else {
992 v = new QVariant(QDate());
993 isNull = true;
994 }
995 break; }
996 case QVariant::Time: {
997 TIME_STRUCT tbuf;
998 r = SQLGetData(d->hStmt,
999 field + 1,
1000 SQL_C_TIME,
1001 (SQLPOINTER) &tbuf,
1002 0,
1003 &lengthIndicator);
1004 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1005 v = new QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
1006 } else {
1007 v = new QVariant(QTime());
1008 isNull = true;
1009 }
1010 break; }
1011 case QVariant::DateTime: {
1012 TIMESTAMP_STRUCT dtbuf;
1013 r = SQLGetData(d->hStmt,
1014 field + 1,
1015 SQL_C_TIMESTAMP,
1016 (SQLPOINTER) &dtbuf,
1017 0,
1018 &lengthIndicator);
1019 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1020 v = new QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
1021 QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
1022 } else {
1023 v = new QVariant(QDateTime());
1024 isNull = true;
1025 }
1026 break; }
1027 case QVariant::ByteArray:
1028 v = new QVariant(qGetBinaryData(d->hStmt, field, lengthIndicator, isNull));
1029 break;
1030 case QVariant::Double:
1031 {
1032 QString value=qGetStringData(d->hStmt, field, info.length() + 1, isNull);
1033 bool ok=false;
1034 switch(d->precisionPolicy) {
1035 case QSql::LowPrecisionInt32:
1036 v = new QVariant(value.toInt(&ok));
1037 break;
1038 case QSql::LowPrecisionInt64:
1039 v = new QVariant(value.toLongLong(&ok));
1040 break;
1041 case QSql::LowPrecisionDouble:
1042 v = new QVariant(value.toDouble(&ok));
1043 break;
1044 case QSql::HighPrecision:
1045 default:
1046 // length + 1 for the comma
1047 v = new QVariant(qGetStringData(d->hStmt, field, info.length() + 1, isNull));
1048 ok = true;
1049 break;
1050 }
1051 if(!ok)
1052 v = new QVariant();
1053 break;
1054 }
1055 case QVariant::String:
1056 default:
1057 v = new QVariant(qGetStringData(d->hStmt, field, info.length(), isNull));
1058 break;
1059 }
1060 if (isNull)
1061 *v = QVariant(info.type());
1062 d->valueCache[field] = v;
1063 return *v;
1064}
1065
1066bool QDB2Result::isNull(int i)
1067{
1068 if (i >= d->valueCache.size())
1069 return true;
1070
1071 if (d->valueCache[i])
1072 return d->valueCache[i]->isNull();
1073 return data(i).isNull();
1074}
1075
1076int QDB2Result::numRowsAffected()
1077{
1078 SQLINTEGER affectedRowCount = 0;
1079 SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
1080 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1081 return affectedRowCount;
1082 else
1083 qSqlWarning(QLatin1String("QDB2Result::numRowsAffected: Unable to count affected rows"), d);
1084 return -1;
1085}
1086
1087int QDB2Result::size()
1088{
1089 return -1;
1090}
1091
1092QSqlRecord QDB2Result::record() const
1093{
1094 if (isActive())
1095 return d->recInf;
1096 return QSqlRecord();
1097}
1098
1099bool QDB2Result::nextResult()
1100{
1101 setActive(false);
1102 setAt(QSql::BeforeFirstRow);
1103 d->recInf.clear();
1104 d->valueCache.clear();
1105 setSelect(false);
1106
1107 SQLRETURN r = SQLMoreResults(d->hStmt);
1108 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1109 if (r != SQL_NO_DATA) {
1110 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1111 "Unable to fetch last"), QSqlError::ConnectionError, d));
1112 }
1113 return false;
1114 }
1115
1116 SQLSMALLINT fieldCount;
1117 r = SQLNumResultCols(d->hStmt, &fieldCount);
1118 setSelect(fieldCount > 0);
1119 for (int i = 0; i < fieldCount; ++i)
1120 d->recInf.append(qMakeFieldInfo(d, i));
1121
1122 d->valueCache.resize(fieldCount);
1123 setActive(true);
1124
1125 return true;
1126}
1127
1128void QDB2Result::virtual_hook(int id, void *data)
1129{
1130 switch (id) {
1131 case QSqlResult::NextResult:
1132 Q_ASSERT(data);
1133 *static_cast<bool*>(data) = nextResult();
1134 break;
1135 case QSqlResult::SetNumericalPrecision:
1136 Q_ASSERT(data);
1137 d->precisionPolicy = *reinterpret_cast<QSql::NumericalPrecisionPolicy *>(data);
1138 break;
1139 default:
1140 QSqlResult::virtual_hook(id, data);
1141 }
1142}
1143
1144/************************************/
1145
1146QDB2Driver::QDB2Driver(QObject* parent)
1147 : QSqlDriver(parent)
1148{
1149 d = new QDB2DriverPrivate;
1150}
1151
1152QDB2Driver::QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent)
1153 : QSqlDriver(parent)
1154{
1155 d = new QDB2DriverPrivate;
1156 d->hEnv = (SQLHANDLE)env;
1157 d->hDbc = (SQLHANDLE)con;
1158 if (env && con) {
1159 setOpen(true);
1160 setOpenError(false);
1161 }
1162}
1163
1164QDB2Driver::~QDB2Driver()
1165{
1166 close();
1167 delete d;
1168}
1169
1170bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString&, int,
1171 const QString& connOpts)
1172{
1173 if (isOpen())
1174 close();
1175 SQLRETURN r;
1176 r = SQLAllocHandle(SQL_HANDLE_ENV,
1177 SQL_NULL_HANDLE,
1178 &d->hEnv);
1179 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1180 qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate environment"), d);
1181 setOpenError(true);
1182 return false;
1183 }
1184
1185 r = SQLAllocHandle(SQL_HANDLE_DBC,
1186 d->hEnv,
1187 &d->hDbc);
1188 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1189 qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate connection"), d);
1190 setOpenError(true);
1191 return false;
1192 }
1193 // Set connection attributes
1194 const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
1195 for (int i = 0; i < opts.count(); ++i) {
1196 const QString tmp(opts.at(i));
1197 int idx;
1198 if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
1199 qWarning("QDB2Driver::open: Illegal connect option value '%s'",
1200 tmp.toLocal8Bit().constData());
1201 continue;
1202 }
1203
1204 const QString opt(tmp.left(idx));
1205 const QString val(tmp.mid(idx + 1).simplified());
1206
1207 SQLUINTEGER v = 0;
1208 r = SQL_SUCCESS;
1209 if (opt == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
1210 if (val == QLatin1String("SQL_MODE_READ_ONLY")) {
1211 v = SQL_MODE_READ_ONLY;
1212 } else if (val == QLatin1String("SQL_MODE_READ_WRITE")) {
1213 v = SQL_MODE_READ_WRITE;
1214 } else {
1215 qWarning("QDB2Driver::open: Unknown option value '%s'",
1216 tmp.toLocal8Bit().constData());
1217 continue;
1218 }
1219 r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0);
1220 } else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
1221 v = val.toUInt();
1222 r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0);
1223 } else {
1224 qWarning("QDB2Driver::open: Unknown connection attribute '%s'",
1225 tmp.toLocal8Bit().constData());
1226 }
1227 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
1228 qSqlWarning(QString::fromLatin1("QDB2Driver::open: "
1229 "Unable to set connection attribute '%1'").arg(opt), d);
1230 }
1231
1232 QString connQStr;
1233 connQStr = QLatin1String("DSN=") + db + QLatin1String(";UID=") + user + QLatin1String(";PWD=")
1234 + password;
1235 SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH];
1236 SQLSMALLINT cb;
1237
1238 r = SQLDriverConnect(d->hDbc,
1239 NULL,
1240 qToTChar(connQStr),
1241 (SQLSMALLINT) connQStr.length(),
1242 connOut,
1243 SQL_MAX_OPTION_STRING_LENGTH,
1244 &cb,
1245 SQL_DRIVER_NOPROMPT);
1246 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1247 setLastError(qMakeError(tr("Unable to connect"),
1248 QSqlError::ConnectionError, d));
1249 setOpenError(true);
1250 return false;
1251 }
1252
1253 d->user = user.toUpper();
1254 setOpen(true);
1255 setOpenError(false);
1256 return true;
1257}
1258
1259void QDB2Driver::close()
1260{
1261 SQLRETURN r;
1262 if (d->hDbc) {
1263 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1264 if (isOpen()) {
1265 r = SQLDisconnect(d->hDbc);
1266 if (r != SQL_SUCCESS)
1267 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to disconnect datasource"), d);
1268 }
1269 r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
1270 if (r != SQL_SUCCESS)
1271 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free connection handle"), d);
1272 d->hDbc = 0;
1273 }
1274
1275 if (d->hEnv) {
1276 r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
1277 if (r != SQL_SUCCESS)
1278 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free environment handle"), d);
1279 d->hEnv = 0;
1280 }
1281 setOpen(false);
1282 setOpenError(false);
1283}
1284
1285QSqlResult *QDB2Driver::createResult() const
1286{
1287 return new QDB2Result(this, d);
1288}
1289
1290QSqlRecord QDB2Driver::record(const QString& tableName) const
1291{
1292 QSqlRecord fil;
1293 if (!isOpen())
1294 return fil;
1295
1296 SQLHANDLE hStmt;
1297 QString catalog, schema, table;
1298 qSplitTableQualifier(tableName.toUpper(), &catalog, &schema, &table);
1299 if (schema.isEmpty())
1300 schema = d->user;
1301
1302 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1303 d->hDbc,
1304 &hStmt);
1305 if (r != SQL_SUCCESS) {
1306 qSqlWarning(QLatin1String("QDB2Driver::record: Unable to allocate handle"), d);
1307 return fil;
1308 }
1309
1310 r = SQLSetStmtAttr(hStmt,
1311 SQL_ATTR_CURSOR_TYPE,
1312 (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
1313 SQL_IS_UINTEGER);
1314
1315 r = SQLColumns(hStmt,
1316 NULL,
1317 0,
1318 qToTChar(schema),
1319 schema.length(),
1320 qToTChar(table),
1321 table.length(),
1322 NULL,
1323 0);
1324
1325 if (r != SQL_SUCCESS)
1326 qSqlWarning(QLatin1String("QDB2Driver::record: Unable to execute column list"), d);
1327 r = SQLFetchScroll(hStmt,
1328 SQL_FETCH_NEXT,
1329 0);
1330 while (r == SQL_SUCCESS) {
1331 fil.append(qMakeFieldInfo(hStmt));
1332 r = SQLFetchScroll(hStmt,
1333 SQL_FETCH_NEXT,
1334 0);
1335 }
1336
1337 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1338 if (r != SQL_SUCCESS)
1339 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
1340 + QString::number(r), d);
1341
1342 return fil;
1343}
1344
1345QStringList QDB2Driver::tables(QSql::TableType type) const
1346{
1347 QStringList tl;
1348 if (!isOpen())
1349 return tl;
1350
1351 SQLHANDLE hStmt;
1352
1353 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1354 d->hDbc,
1355 &hStmt);
1356 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1357 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to allocate handle"), d);
1358 return tl;
1359 }
1360 r = SQLSetStmtAttr(hStmt,
1361 SQL_ATTR_CURSOR_TYPE,
1362 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1363 SQL_IS_UINTEGER);
1364
1365 QString tableType;
1366 if (type & QSql::Tables)
1367 tableType += QLatin1String("TABLE,");
1368 if (type & QSql::Views)
1369 tableType += QLatin1String("VIEW,");
1370 if (type & QSql::SystemTables)
1371 tableType += QLatin1String("SYSTEM TABLE,");
1372 if (tableType.isEmpty())
1373 return tl;
1374 tableType.chop(1);
1375
1376 r = SQLTables(hStmt,
1377 NULL,
1378 0,
1379 NULL,
1380 0,
1381 NULL,
1382 0,
1383 qToTChar(tableType),
1384 tableType.length());
1385
1386 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
1387 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to execute table list"), d);
1388 r = SQLFetchScroll(hStmt,
1389 SQL_FETCH_NEXT,
1390 0);
1391 while (r == SQL_SUCCESS) {
1392 bool isNull;
1393 QString fieldVal = qGetStringData(hStmt, 2, -1, isNull);
1394 QString userVal = qGetStringData(hStmt, 1, -1, isNull);
1395 if (userVal != d->user)
1396 fieldVal = userVal + QLatin1Char('.') + fieldVal;
1397 tl.append(fieldVal);
1398 r = SQLFetchScroll(hStmt,
1399 SQL_FETCH_NEXT,
1400 0);
1401 }
1402
1403 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1404 if (r != SQL_SUCCESS)
1405 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to free statement handle ")
1406 + QString::number(r), d);
1407 return tl;
1408}
1409
1410QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const
1411{
1412 QSqlIndex index(tablename);
1413 if (!isOpen())
1414 return index;
1415 QSqlRecord rec = record(tablename);
1416
1417 SQLHANDLE hStmt;
1418 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1419 d->hDbc,
1420 &hStmt);
1421 if (r != SQL_SUCCESS) {
1422 qSqlWarning(QLatin1String("QDB2Driver::primaryIndex: Unable to list primary key"), d);
1423 return index;
1424 }
1425 QString catalog, schema, table;
1426 qSplitTableQualifier(tablename.toUpper(), &catalog, &schema, &table);
1427 r = SQLSetStmtAttr(hStmt,
1428 SQL_ATTR_CURSOR_TYPE,
1429 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1430 SQL_IS_UINTEGER);
1431
1432 r = SQLPrimaryKeys(hStmt,
1433 NULL,
1434 0,
1435 qToTChar(schema),
1436 schema.length(),
1437 qToTChar(table),
1438 table.length());
1439 r = SQLFetchScroll(hStmt,
1440 SQL_FETCH_NEXT,
1441 0);
1442
1443 bool isNull;
1444 QString cName, idxName;
1445 // Store all fields in a StringList because the driver can't detail fields in this FETCH loop
1446 while (r == SQL_SUCCESS) {
1447 cName = qGetStringData(hStmt, 3, -1, isNull); // column name
1448 idxName = qGetStringData(hStmt, 5, -1, isNull); // pk index name
1449 index.append(rec.field(cName));
1450 index.setName(idxName);
1451 r = SQLFetchScroll(hStmt,
1452 SQL_FETCH_NEXT,
1453 0);
1454 }
1455 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1456 if (r!= SQL_SUCCESS)
1457 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
1458 + QString::number(r), d);
1459 return index;
1460}
1461
1462bool QDB2Driver::hasFeature(DriverFeature f) const
1463{
1464 switch (f) {
1465 case QuerySize:
1466 case NamedPlaceholders:
1467 case BatchOperations:
1468 case LastInsertId:
1469 case SimpleLocking:
1470 case LowPrecisionNumbers:
1471 case EventNotifications:
1472 return false;
1473 case BLOB:
1474 case Transactions:
1475 case MultipleResultSets:
1476 case PreparedQueries:
1477 case PositionalPlaceholders:
1478 return true;
1479 case Unicode:
1480 // this is the query that shows the codepage for the types:
1481 // select typename, codepage from syscat.datatypes
1482#ifdef UNICODE
1483 return true;
1484#else
1485 return false;
1486#endif
1487 }
1488 return false;
1489}
1490
1491bool QDB2Driver::beginTransaction()
1492{
1493 if (!isOpen()) {
1494 qWarning("QDB2Driver::beginTransaction: Database not open");
1495 return false;
1496 }
1497 return setAutoCommit(false);
1498}
1499
1500bool QDB2Driver::commitTransaction()
1501{
1502 if (!isOpen()) {
1503 qWarning("QDB2Driver::commitTransaction: Database not open");
1504 return false;
1505 }
1506 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
1507 d->hDbc,
1508 SQL_COMMIT);
1509 if (r != SQL_SUCCESS) {
1510 setLastError(qMakeError(tr("Unable to commit transaction"),
1511 QSqlError::TransactionError, d));
1512 return false;
1513 }
1514 return setAutoCommit(true);
1515}
1516
1517bool QDB2Driver::rollbackTransaction()
1518{
1519 if (!isOpen()) {
1520 qWarning("QDB2Driver::rollbackTransaction: Database not open");
1521 return false;
1522 }
1523 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
1524 d->hDbc,
1525 SQL_ROLLBACK);
1526 if (r != SQL_SUCCESS) {
1527 setLastError(qMakeError(tr("Unable to rollback transaction"),
1528 QSqlError::TransactionError, d));
1529 return false;
1530 }
1531 return setAutoCommit(true);
1532}
1533
1534bool QDB2Driver::setAutoCommit(bool autoCommit)
1535{
1536 SQLUINTEGER ac = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
1537 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
1538 SQL_ATTR_AUTOCOMMIT,
1539 (SQLPOINTER)ac,
1540 sizeof(ac));
1541 if (r != SQL_SUCCESS) {
1542 setLastError(qMakeError(tr("Unable to set autocommit"),
1543 QSqlError::TransactionError, d));
1544 return false;
1545 }
1546 return true;
1547}
1548
1549QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const
1550{
1551 if (field.isNull())
1552 return QLatin1String("NULL");
1553
1554 switch (field.type()) {
1555 case QVariant::DateTime: {
1556 // Use an escape sequence for the datetime fields
1557 if (field.value().toDateTime().isValid()) {
1558 QDate dt = field.value().toDateTime().date();
1559 QTime tm = field.value().toDateTime().time();
1560 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
1561 return QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-')
1562 + QString::number(dt.month()) + QLatin1Char('-')
1563 + QString::number(dt.day()) + QLatin1Char('-')
1564 + QString::number(tm.hour()) + QLatin1Char('.')
1565 + QString::number(tm.minute()).rightJustified(2, QLatin1Char('0'), true)
1566 + QLatin1Char('.')
1567 + QString::number(tm.second()).rightJustified(2, QLatin1Char('0'), true)
1568 + QLatin1Char('.')
1569 + QString::number(tm.msec() * 1000).rightJustified(6, QLatin1Char('0'), true)
1570 + QLatin1Char('\'');
1571 } else {
1572 return QLatin1String("NULL");
1573 }
1574 }
1575 case QVariant::ByteArray: {
1576 QByteArray ba = field.value().toByteArray();
1577 QString res = QString::fromLatin1("BLOB(X'");
1578 static const char hexchars[] = "0123456789abcdef";
1579 for (int i = 0; i < ba.size(); ++i) {
1580 uchar s = (uchar) ba[i];
1581 res += QLatin1Char(hexchars[s >> 4]);
1582 res += QLatin1Char(hexchars[s & 0x0f]);
1583 }
1584 res += QLatin1String("')");
1585 return res;
1586 }
1587 default:
1588 return QSqlDriver::formatValue(field, trimStrings);
1589 }
1590}
1591
1592QVariant QDB2Driver::handle() const
1593{
1594 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
1595}
1596
1597QString QDB2Driver::escapeIdentifier(const QString &identifier, IdentifierType) const
1598{
1599 QString res = identifier;
1600 if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) {
1601 res.replace(QLatin1Char('"'), QLatin1String("\"\""));
1602 res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
1603 res.replace(QLatin1Char('.'), QLatin1String("\".\""));
1604 }
1605 return res;
1606}
1607
1608QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.