Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into master-integration
[qt-netbsd.git] / src / sql / drivers / db2 / qsql_db2.cpp
blobd5b398429d7c39c43f221972dd82ba31791f28eb
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
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 #include <QDebug>
54 #if defined(Q_CC_BOR)
55 // DB2's sqlsystm.h (included through sqlcli1.h) defines the SQL_BIGINT_TYPE
56 // and SQL_BIGUINT_TYPE to wrong the types for Borland; so do the defines to
57 // the right type before including the header
58 #define SQL_BIGINT_TYPE qint64
59 #define SQL_BIGUINT_TYPE quint64
60 #endif
62 #define UNICODE
64 #include <sqlcli1.h>
66 #include <string.h>
68 QT_BEGIN_NAMESPACE
70 static const int COLNAMESIZE = 255;
71 static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
73 class QDB2DriverPrivate
75 public:
76 QDB2DriverPrivate(): hEnv(0), hDbc(0) {}
77 SQLHANDLE hEnv;
78 SQLHANDLE hDbc;
79 QString user;
82 class QDB2ResultPrivate
84 public:
85 QDB2ResultPrivate(const QDB2DriverPrivate* d): dp(d), hStmt(0)
87 ~QDB2ResultPrivate()
89 emptyValueCache();
91 void clearValueCache()
93 for (int i = 0; i < valueCache.count(); ++i) {
94 delete valueCache[i];
95 valueCache[i] = NULL;
98 void emptyValueCache()
100 clearValueCache();
101 valueCache.clear();
104 const QDB2DriverPrivate* dp;
105 SQLHANDLE hStmt;
106 QSqlRecord recInf;
107 QVector<QVariant*> valueCache;
110 static QString qFromTChar(SQLTCHAR* str)
112 return QString::fromUtf16(str);
115 // dangerous!! (but fast). Don't use in functions that
116 // require out parameters!
117 static SQLTCHAR* qToTChar(const QString& str)
119 return (SQLTCHAR*)str.utf16();
122 static QString qWarnDB2Handle(int handleType, SQLHANDLE handle)
124 SQLINTEGER nativeCode;
125 SQLSMALLINT msgLen;
126 SQLRETURN r = SQL_ERROR;
127 SQLTCHAR state[SQL_SQLSTATE_SIZE + 1];
128 SQLTCHAR description[SQL_MAX_MESSAGE_LENGTH];
129 r = SQLGetDiagRec(handleType,
130 handle,
132 (SQLTCHAR*) state,
133 &nativeCode,
134 (SQLTCHAR*) description,
135 SQL_MAX_MESSAGE_LENGTH - 1, /* in bytes, not in characters */
136 &msgLen);
137 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
138 return QString(qFromTChar(description));
139 return QString();
142 static QString qDB2Warn(const QDB2DriverPrivate* d)
144 return (qWarnDB2Handle(SQL_HANDLE_ENV, d->hEnv) + QLatin1Char(' ')
145 + qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc));
148 static QString qDB2Warn(const QDB2ResultPrivate* d)
150 return (qWarnDB2Handle(SQL_HANDLE_ENV, d->dp->hEnv) + QLatin1Char(' ')
151 + qWarnDB2Handle(SQL_HANDLE_DBC, d->dp->hDbc)
152 + qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt));
155 static void qSqlWarning(const QString& message, const QDB2DriverPrivate* d)
157 qWarning("%s\tError: %s", message.toLocal8Bit().constData(),
158 qDB2Warn(d).toLocal8Bit().constData());
161 static void qSqlWarning(const QString& message, const QDB2ResultPrivate* d)
163 qWarning("%s\tError: %s", message.toLocal8Bit().constData(),
164 qDB2Warn(d).toLocal8Bit().constData());
167 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
168 const QDB2DriverPrivate* p)
170 return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type);
173 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
174 const QDB2ResultPrivate* p)
176 return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type);
179 static QVariant::Type qDecodeDB2Type(SQLSMALLINT sqltype)
181 QVariant::Type type = QVariant::Invalid;
182 switch (sqltype) {
183 case SQL_REAL:
184 case SQL_FLOAT:
185 case SQL_DOUBLE:
186 case SQL_DECIMAL:
187 case SQL_NUMERIC:
188 type = QVariant::Double;
189 break;
190 case SQL_SMALLINT:
191 case SQL_INTEGER:
192 case SQL_BIT:
193 case SQL_TINYINT:
194 type = QVariant::Int;
195 break;
196 case SQL_BIGINT:
197 type = QVariant::LongLong;
198 break;
199 case SQL_BLOB:
200 case SQL_BINARY:
201 case SQL_VARBINARY:
202 case SQL_LONGVARBINARY:
203 case SQL_CLOB:
204 case SQL_DBCLOB:
205 type = QVariant::ByteArray;
206 break;
207 case SQL_DATE:
208 case SQL_TYPE_DATE:
209 type = QVariant::Date;
210 break;
211 case SQL_TIME:
212 case SQL_TYPE_TIME:
213 type = QVariant::Time;
214 break;
215 case SQL_TIMESTAMP:
216 case SQL_TYPE_TIMESTAMP:
217 type = QVariant::DateTime;
218 break;
219 case SQL_WCHAR:
220 case SQL_WVARCHAR:
221 case SQL_WLONGVARCHAR:
222 case SQL_CHAR:
223 case SQL_VARCHAR:
224 case SQL_LONGVARCHAR:
225 type = QVariant::String;
226 break;
227 default:
228 type = QVariant::ByteArray;
229 break;
231 return type;
234 static QSqlField qMakeFieldInfo(const QDB2ResultPrivate* d, int i)
236 SQLSMALLINT colNameLen;
237 SQLSMALLINT colType;
238 SQLUINTEGER colSize;
239 SQLSMALLINT colScale;
240 SQLSMALLINT nullable;
241 SQLRETURN r = SQL_ERROR;
242 SQLTCHAR colName[COLNAMESIZE];
243 r = SQLDescribeCol(d->hStmt,
244 i+1,
245 colName,
246 (SQLSMALLINT) COLNAMESIZE,
247 &colNameLen,
248 &colType,
249 &colSize,
250 &colScale,
251 &nullable);
253 if (r != SQL_SUCCESS) {
254 qSqlWarning(QString::fromLatin1("qMakeFieldInfo: Unable to describe column %1").arg(i), d);
255 return QSqlField();
257 QSqlField f(qFromTChar(colName), qDecodeDB2Type(colType));
258 // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
259 if (nullable == SQL_NO_NULLS)
260 f.setRequired(true);
261 else if (nullable == SQL_NULLABLE)
262 f.setRequired(false);
263 // else required is unknown
264 f.setLength(colSize == 0 ? -1 : int(colSize));
265 f.setPrecision(colScale == 0 ? -1 : int(colScale));
266 f.setSqlType(int(colType));
267 return f;
270 static int qGetIntData(SQLHANDLE hStmt, int column, bool& isNull)
272 SQLINTEGER intbuf;
273 isNull = false;
274 SQLINTEGER lengthIndicator = 0;
275 SQLRETURN r = SQLGetData(hStmt,
276 column + 1,
277 SQL_C_SLONG,
278 (SQLPOINTER) &intbuf,
280 &lengthIndicator);
281 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
282 isNull = true;
283 return 0;
285 return int(intbuf);
288 static double qGetDoubleData(SQLHANDLE hStmt, int column, bool& isNull)
290 SQLDOUBLE dblbuf;
291 isNull = false;
292 SQLINTEGER lengthIndicator = 0;
293 SQLRETURN r = SQLGetData(hStmt,
294 column+1,
295 SQL_C_DOUBLE,
296 (SQLPOINTER) &dblbuf,
298 &lengthIndicator);
299 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
300 isNull = true;
301 return 0.0;
304 return (double) dblbuf;
307 static SQLBIGINT qGetBigIntData(SQLHANDLE hStmt, int column, bool& isNull)
309 SQLBIGINT lngbuf = Q_INT64_C(0);
310 isNull = false;
311 SQLINTEGER lengthIndicator = 0;
312 SQLRETURN r = SQLGetData(hStmt,
313 column+1,
314 SQL_C_SBIGINT,
315 (SQLPOINTER) &lngbuf,
317 &lengthIndicator);
318 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA)
319 isNull = true;
321 return lngbuf;
324 static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool& isNull)
326 QString fieldVal;
327 SQLRETURN r = SQL_ERROR;
328 SQLINTEGER lengthIndicator = 0;
330 if (colSize <= 0)
331 colSize = 255;
332 else if (colSize > 65536) // limit buffer size to 64 KB
333 colSize = 65536;
334 else
335 colSize++; // make sure there is room for more than the 0 termination
336 SQLTCHAR* buf = new SQLTCHAR[colSize];
338 while (true) {
339 r = SQLGetData(hStmt,
340 column + 1,
341 SQL_C_WCHAR,
342 (SQLPOINTER)buf,
343 colSize * sizeof(SQLTCHAR),
344 &lengthIndicator);
345 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
346 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
347 fieldVal.clear();
348 isNull = true;
349 break;
351 fieldVal += qFromTChar(buf);
352 } else if (r == SQL_NO_DATA) {
353 break;
354 } else {
355 qWarning("qGetStringData: Error while fetching data (%d)", r);
356 fieldVal.clear();
357 break;
360 delete[] buf;
361 return fieldVal;
364 static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLINTEGER& lengthIndicator, bool& isNull)
366 QByteArray fieldVal;
367 SQLSMALLINT colNameLen;
368 SQLSMALLINT colType;
369 SQLUINTEGER colSize;
370 SQLSMALLINT colScale;
371 SQLSMALLINT nullable;
372 SQLRETURN r = SQL_ERROR;
374 SQLTCHAR colName[COLNAMESIZE];
375 r = SQLDescribeCol(hStmt,
376 column+1,
377 colName,
378 COLNAMESIZE,
379 &colNameLen,
380 &colType,
381 &colSize,
382 &colScale,
383 &nullable);
384 if (r != SQL_SUCCESS)
385 qWarning("qGetBinaryData: Unable to describe column %d", column);
386 // SQLDescribeCol may return 0 if size cannot be determined
387 if (!colSize)
388 colSize = 255;
389 else if (colSize > 65536) // read the field in 64 KB chunks
390 colSize = 65536;
391 char * buf = new char[colSize];
392 while (true) {
393 r = SQLGetData(hStmt,
394 column+1,
395 colType == SQL_DBCLOB ? SQL_C_CHAR : SQL_C_BINARY,
396 (SQLPOINTER) buf,
397 colSize,
398 &lengthIndicator);
399 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
400 if (lengthIndicator == SQL_NULL_DATA) {
401 isNull = true;
402 break;
403 } else {
404 int rSize;
405 r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
406 if (lengthIndicator == SQL_NO_TOTAL) // size cannot be determined
407 rSize = colSize;
408 fieldVal.append(QByteArray(buf, rSize));
409 if (r == SQL_SUCCESS) // the whole field was read in one chunk
410 break;
412 } else {
413 break;
416 delete [] buf;
417 return fieldVal;
420 static void qSplitTableQualifier(const QString & qualifier, QString * catalog,
421 QString * schema, QString * table)
423 if (!catalog || !schema || !table)
424 return;
425 QStringList l = qualifier.split(QLatin1Char('.'));
426 if (l.count() > 3)
427 return; // can't possibly be a valid table qualifier
428 int i = 0, n = l.count();
429 if (n == 1) {
430 *table = qualifier;
431 } else {
432 for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
433 if (n == 3) {
434 if (i == 0)
435 *catalog = *it;
436 else if (i == 1)
437 *schema = *it;
438 else if (i == 2)
439 *table = *it;
440 } else if (n == 2) {
441 if (i == 0)
442 *schema = *it;
443 else if (i == 1)
444 *table = *it;
446 i++;
451 // creates a QSqlField from a valid hStmt generated
452 // by SQLColumns. The hStmt has to point to a valid position.
453 static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt)
455 bool isNull;
456 int type = qGetIntData(hStmt, 4, isNull);
457 QSqlField f(qGetStringData(hStmt, 3, -1, isNull), qDecodeDB2Type(type));
458 int required = qGetIntData(hStmt, 10, isNull); // nullable-flag
459 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
460 if (required == SQL_NO_NULLS)
461 f.setRequired(true);
462 else if (required == SQL_NULLABLE)
463 f.setRequired(false);
464 // else we don't know.
465 f.setLength(qGetIntData(hStmt, 6, isNull)); // column size
466 f.setPrecision(qGetIntData(hStmt, 8, isNull)); // precision
467 f.setSqlType(type);
468 return f;
471 static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwardOnly = true)
473 SQLRETURN r;
474 if (!d->hStmt) {
475 r = SQLAllocHandle(SQL_HANDLE_STMT,
476 d->dp->hDbc,
477 &d->hStmt);
478 if (r != SQL_SUCCESS) {
479 qSqlWarning(QLatin1String("QDB2Result::reset: Unable to allocate statement handle"), d);
480 return false;
482 } else {
483 r = SQLFreeStmt(d->hStmt, SQL_CLOSE);
484 if (r != SQL_SUCCESS) {
485 qSqlWarning(QLatin1String("QDB2Result::reset: Unable to close statement handle"), d);
486 return false;
490 if (!setForwardOnly)
491 return true;
493 if (forwardOnly) {
494 r = SQLSetStmtAttr(d->hStmt,
495 SQL_ATTR_CURSOR_TYPE,
496 (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
497 SQL_IS_UINTEGER);
498 } else {
499 r = SQLSetStmtAttr(d->hStmt,
500 SQL_ATTR_CURSOR_TYPE,
501 (SQLPOINTER) SQL_CURSOR_STATIC,
502 SQL_IS_UINTEGER);
504 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
505 qSqlWarning(QString::fromLatin1("QDB2Result::reset: Unable to set %1 attribute.").arg(
506 forwardOnly ? QLatin1String("SQL_CURSOR_FORWARD_ONLY")
507 : QLatin1String("SQL_CURSOR_STATIC")), d);
508 return false;
510 return true;
513 QVariant QDB2Result::handle() const
515 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
518 /************************************/
520 QDB2Result::QDB2Result(const QDB2Driver* dr, const QDB2DriverPrivate* dp)
521 : QSqlResult(dr)
523 d = new QDB2ResultPrivate(dp);
526 QDB2Result::~QDB2Result()
528 if (d->hStmt) {
529 SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
530 if (r != SQL_SUCCESS)
531 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
532 + QString::number(r), d);
534 delete d;
537 bool QDB2Result::reset (const QString& query)
539 setActive(false);
540 setAt(QSql::BeforeFirstRow);
541 SQLRETURN r;
543 d->recInf.clear();
544 d->emptyValueCache();
546 if (!qMakeStatement(d, isForwardOnly()))
547 return false;
549 r = SQLExecDirect(d->hStmt,
550 qToTChar(query),
551 (SQLINTEGER) query.length());
552 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
553 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
554 "Unable to execute statement"), QSqlError::StatementError, d));
555 return false;
557 SQLSMALLINT count;
558 r = SQLNumResultCols(d->hStmt, &count);
559 if (count) {
560 setSelect(true);
561 for (int i = 0; i < count; ++i) {
562 d->recInf.append(qMakeFieldInfo(d, i));
564 } else {
565 setSelect(false);
567 d->valueCache.resize(count);
568 d->valueCache.fill(NULL);
569 setActive(true);
570 return true;
573 bool QDB2Result::prepare(const QString& query)
575 setActive(false);
576 setAt(QSql::BeforeFirstRow);
577 SQLRETURN r;
579 d->recInf.clear();
580 d->emptyValueCache();
582 if (!qMakeStatement(d, isForwardOnly()))
583 return false;
585 r = SQLPrepare(d->hStmt,
586 qToTChar(query),
587 (SQLINTEGER) query.length());
589 if (r != SQL_SUCCESS) {
590 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
591 "Unable to prepare statement"), QSqlError::StatementError, d));
592 return false;
594 return true;
597 bool QDB2Result::exec()
599 QList<QByteArray> tmpStorage; // holds temporary ptrs
600 QVarLengthArray<SQLINTEGER, 32> indicators(boundValues().count());
602 memset(indicators.data(), 0, indicators.size() * sizeof(SQLINTEGER));
603 setActive(false);
604 setAt(QSql::BeforeFirstRow);
605 SQLRETURN r;
607 d->recInf.clear();
608 d->emptyValueCache();
610 if (!qMakeStatement(d, isForwardOnly(), false))
611 return false;
614 QVector<QVariant> &values = boundValues();
615 int i;
616 for (i = 0; i < values.count(); ++i) {
617 // bind parameters - only positional binding allowed
618 SQLINTEGER *ind = &indicators[i];
619 if (values.at(i).isNull())
620 *ind = SQL_NULL_DATA;
621 if (bindValueType(i) & QSql::Out)
622 values[i].detach();
624 switch (values.at(i).type()) {
625 case QVariant::Date: {
626 QByteArray ba;
627 ba.resize(sizeof(DATE_STRUCT));
628 DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData();
629 QDate qdt = values.at(i).toDate();
630 dt->year = qdt.year();
631 dt->month = qdt.month();
632 dt->day = qdt.day();
633 r = SQLBindParameter(d->hStmt,
634 i + 1,
635 qParamType[(QFlag)(bindValueType(i)) & 3],
636 SQL_C_DATE,
637 SQL_DATE,
640 (void *) dt,
642 *ind == SQL_NULL_DATA ? ind : NULL);
643 tmpStorage.append(ba);
644 break; }
645 case QVariant::Time: {
646 QByteArray ba;
647 ba.resize(sizeof(TIME_STRUCT));
648 TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData();
649 QTime qdt = values.at(i).toTime();
650 dt->hour = qdt.hour();
651 dt->minute = qdt.minute();
652 dt->second = qdt.second();
653 r = SQLBindParameter(d->hStmt,
654 i + 1,
655 qParamType[(QFlag)(bindValueType(i)) & 3],
656 SQL_C_TIME,
657 SQL_TIME,
660 (void *) dt,
662 *ind == SQL_NULL_DATA ? ind : NULL);
663 tmpStorage.append(ba);
664 break; }
665 case QVariant::DateTime: {
666 QByteArray ba;
667 ba.resize(sizeof(TIMESTAMP_STRUCT));
668 TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData();
669 QDateTime qdt = values.at(i).toDateTime();
670 dt->year = qdt.date().year();
671 dt->month = qdt.date().month();
672 dt->day = qdt.date().day();
673 dt->hour = qdt.time().hour();
674 dt->minute = qdt.time().minute();
675 dt->second = qdt.time().second();
676 dt->fraction = qdt.time().msec() * 1000000;
677 r = SQLBindParameter(d->hStmt,
678 i + 1,
679 qParamType[(QFlag)(bindValueType(i)) & 3],
680 SQL_C_TIMESTAMP,
681 SQL_TIMESTAMP,
684 (void *) dt,
686 *ind == SQL_NULL_DATA ? ind : NULL);
687 tmpStorage.append(ba);
688 break; }
689 case QVariant::Int:
690 r = SQLBindParameter(d->hStmt,
691 i + 1,
692 qParamType[(QFlag)(bindValueType(i)) & 3],
693 SQL_C_SLONG,
694 SQL_INTEGER,
697 (void *)values.at(i).constData(),
699 *ind == SQL_NULL_DATA ? ind : NULL);
700 break;
701 case QVariant::Double:
702 r = SQLBindParameter(d->hStmt,
703 i + 1,
704 qParamType[(QFlag)(bindValueType(i)) & 3],
705 SQL_C_DOUBLE,
706 SQL_DOUBLE,
709 (void *)values.at(i).constData(),
711 *ind == SQL_NULL_DATA ? ind : NULL);
712 break;
713 case QVariant::ByteArray: {
714 int len = values.at(i).toByteArray().size();
715 if (*ind != SQL_NULL_DATA)
716 *ind = len;
717 r = SQLBindParameter(d->hStmt,
718 i + 1,
719 qParamType[(QFlag)(bindValueType(i)) & 3],
720 SQL_C_BINARY,
721 SQL_LONGVARBINARY,
722 len,
724 (void *)values.at(i).toByteArray().constData(),
725 len,
726 ind);
727 break; }
728 case QVariant::String:
730 QString str(values.at(i).toString());
731 if (*ind != SQL_NULL_DATA)
732 *ind = str.length() * sizeof(QChar);
733 if (bindValueType(i) & QSql::Out) {
734 QByteArray ba((char*)str.utf16(), str.capacity() * sizeof(QChar));
735 r = SQLBindParameter(d->hStmt,
736 i + 1,
737 qParamType[(QFlag)(bindValueType(i)) & 3],
738 SQL_C_WCHAR,
739 SQL_WVARCHAR,
740 str.length(),
742 (void *)ba.constData(),
743 ba.size(),
744 ind);
745 tmpStorage.append(ba);
746 } else {
747 void *data = (void*)str.utf16();
748 int len = str.length();
749 r = SQLBindParameter(d->hStmt,
750 i + 1,
751 qParamType[(QFlag)(bindValueType(i)) & 3],
752 SQL_C_WCHAR,
753 SQL_WVARCHAR,
754 len,
756 data,
757 len * sizeof(QChar),
758 ind);
760 break;
762 default: {
763 QByteArray ba = values.at(i).toString().toAscii();
764 int len = ba.length() + 1;
765 if (*ind != SQL_NULL_DATA)
766 *ind = ba.length();
767 r = SQLBindParameter(d->hStmt,
768 i + 1,
769 qParamType[(QFlag)(bindValueType(i)) & 3],
770 SQL_C_CHAR,
771 SQL_VARCHAR,
772 len,
774 (void *) ba.constData(),
775 len,
776 ind);
777 tmpStorage.append(ba);
778 break; }
780 if (r != SQL_SUCCESS) {
781 qWarning("QDB2Result::exec: unable to bind variable: %s",
782 qDB2Warn(d).toLocal8Bit().constData());
783 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
784 "Unable to bind variable"), QSqlError::StatementError, d));
785 return false;
789 r = SQLExecute(d->hStmt);
790 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
791 qWarning("QDB2Result::exec: Unable to execute statement: %s",
792 qDB2Warn(d).toLocal8Bit().constData());
793 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
794 "Unable to execute statement"), QSqlError::StatementError, d));
795 return false;
797 SQLSMALLINT count;
798 r = SQLNumResultCols(d->hStmt, &count);
799 if (count) {
800 setSelect(true);
801 for (int i = 0; i < count; ++i) {
802 d->recInf.append(qMakeFieldInfo(d, i));
804 } else {
805 setSelect(false);
807 setActive(true);
808 d->valueCache.resize(count);
809 d->valueCache.fill(NULL);
811 //get out parameters
812 if (!hasOutValues())
813 return true;
815 for (i = 0; i < values.count(); ++i) {
816 switch (values[i].type()) {
817 case QVariant::Date: {
818 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
819 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
820 break; }
821 case QVariant::Time: {
822 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
823 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
824 break; }
825 case QVariant::DateTime: {
826 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT *)tmpStorage.takeFirst().constData());
827 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
828 QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
829 break; }
830 case QVariant::Int:
831 case QVariant::Double:
832 case QVariant::ByteArray:
833 break;
834 case QVariant::String:
835 if (bindValueType(i) & QSql::Out)
836 values[i] = QString::fromUtf16((ushort*)tmpStorage.takeFirst().constData());
837 break;
838 default: {
839 values[i] = QString::fromAscii(tmpStorage.takeFirst().constData());
840 break; }
842 if (indicators[i] == SQL_NULL_DATA)
843 values[i] = QVariant(values[i].type());
845 return true;
848 bool QDB2Result::fetch(int i)
850 if (isForwardOnly() && i < at())
851 return false;
852 if (i == at())
853 return true;
854 d->clearValueCache();
855 int actualIdx = i + 1;
856 if (actualIdx <= 0) {
857 setAt(QSql::BeforeFirstRow);
858 return false;
860 SQLRETURN r;
861 if (isForwardOnly()) {
862 bool ok = true;
863 while (ok && i > at())
864 ok = fetchNext();
865 return ok;
866 } else {
867 r = SQLFetchScroll(d->hStmt,
868 SQL_FETCH_ABSOLUTE,
869 actualIdx);
871 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
872 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
873 "Unable to fetch record %1").arg(i), QSqlError::StatementError, d));
874 return false;
876 else if (r == SQL_NO_DATA)
877 return false;
878 setAt(i);
879 return true;
882 bool QDB2Result::fetchNext()
884 SQLRETURN r;
885 d->clearValueCache();
886 r = SQLFetchScroll(d->hStmt,
887 SQL_FETCH_NEXT,
889 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
890 if (r != SQL_NO_DATA)
891 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
892 "Unable to fetch next"), QSqlError::StatementError, d));
893 return false;
895 setAt(at() + 1);
896 return true;
899 bool QDB2Result::fetchFirst()
901 if (isForwardOnly() && at() != QSql::BeforeFirstRow)
902 return false;
903 if (isForwardOnly())
904 return fetchNext();
905 d->clearValueCache();
906 SQLRETURN r;
907 r = SQLFetchScroll(d->hStmt,
908 SQL_FETCH_FIRST,
910 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
911 if(r!= SQL_NO_DATA)
912 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", "Unable to fetch first"),
913 QSqlError::StatementError, d));
914 return false;
916 setAt(0);
917 return true;
920 bool QDB2Result::fetchLast()
922 d->clearValueCache();
924 int i = at();
925 if (i == QSql::AfterLastRow) {
926 if (isForwardOnly()) {
927 return false;
928 } else {
929 if (!fetch(0))
930 return false;
931 i = at();
935 while (fetchNext())
936 ++i;
938 if (i == QSql::BeforeFirstRow) {
939 setAt(QSql::AfterLastRow);
940 return false;
943 if (!isForwardOnly())
944 return fetch(i);
946 setAt(i);
947 return true;
951 QVariant QDB2Result::data(int field)
953 if (field >= d->recInf.count()) {
954 qWarning("QDB2Result::data: column %d out of range", field);
955 return QVariant();
957 SQLRETURN r = 0;
958 SQLINTEGER lengthIndicator = 0;
959 bool isNull = false;
960 const QSqlField info = d->recInf.field(field);
962 if (!info.isValid() || field >= d->valueCache.size())
963 return QVariant();
965 if (d->valueCache[field])
966 return *d->valueCache[field];
969 QVariant* v = 0;
970 switch (info.type()) {
971 case QVariant::LongLong:
972 v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull));
973 break;
974 case QVariant::Int:
975 v = new QVariant(qGetIntData(d->hStmt, field, isNull));
976 break;
977 case QVariant::Date: {
978 DATE_STRUCT dbuf;
979 r = SQLGetData(d->hStmt,
980 field + 1,
981 SQL_C_DATE,
982 (SQLPOINTER) &dbuf,
984 &lengthIndicator);
985 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
986 v = new QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
987 } else {
988 v = new QVariant(QDate());
989 isNull = true;
991 break; }
992 case QVariant::Time: {
993 TIME_STRUCT tbuf;
994 r = SQLGetData(d->hStmt,
995 field + 1,
996 SQL_C_TIME,
997 (SQLPOINTER) &tbuf,
999 &lengthIndicator);
1000 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1001 v = new QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
1002 } else {
1003 v = new QVariant(QTime());
1004 isNull = true;
1006 break; }
1007 case QVariant::DateTime: {
1008 TIMESTAMP_STRUCT dtbuf;
1009 r = SQLGetData(d->hStmt,
1010 field + 1,
1011 SQL_C_TIMESTAMP,
1012 (SQLPOINTER) &dtbuf,
1014 &lengthIndicator);
1015 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1016 v = new QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
1017 QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
1018 } else {
1019 v = new QVariant(QDateTime());
1020 isNull = true;
1022 break; }
1023 case QVariant::ByteArray:
1024 v = new QVariant(qGetBinaryData(d->hStmt, field, lengthIndicator, isNull));
1025 break;
1026 case QVariant::Double:
1028 switch(numericalPrecisionPolicy()) {
1029 case QSql::LowPrecisionInt32:
1030 v = new QVariant(qGetIntData(d->hStmt, field, isNull));
1031 break;
1032 case QSql::LowPrecisionInt64:
1033 v = new QVariant(qGetBigIntData(d->hStmt, field, isNull));
1034 break;
1035 case QSql::LowPrecisionDouble:
1036 v = new QVariant(qGetDoubleData(d->hStmt, field, isNull));
1037 break;
1038 case QSql::HighPrecision:
1039 default:
1040 // length + 1 for the comma
1041 v = new QVariant(qGetStringData(d->hStmt, field, info.length() + 1, isNull));
1042 break;
1044 break;
1046 case QVariant::String:
1047 default:
1048 v = new QVariant(qGetStringData(d->hStmt, field, info.length(), isNull));
1049 break;
1051 if (isNull)
1052 *v = QVariant(info.type());
1053 d->valueCache[field] = v;
1054 return *v;
1057 bool QDB2Result::isNull(int i)
1059 if (i >= d->valueCache.size())
1060 return true;
1062 if (d->valueCache[i])
1063 return d->valueCache[i]->isNull();
1064 return data(i).isNull();
1067 int QDB2Result::numRowsAffected()
1069 SQLINTEGER affectedRowCount = 0;
1070 SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
1071 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1072 return affectedRowCount;
1073 else
1074 qSqlWarning(QLatin1String("QDB2Result::numRowsAffected: Unable to count affected rows"), d);
1075 return -1;
1078 int QDB2Result::size()
1080 return -1;
1083 QSqlRecord QDB2Result::record() const
1085 if (isActive())
1086 return d->recInf;
1087 return QSqlRecord();
1090 bool QDB2Result::nextResult()
1092 setActive(false);
1093 setAt(QSql::BeforeFirstRow);
1094 d->recInf.clear();
1095 d->emptyValueCache();
1096 setSelect(false);
1098 SQLRETURN r = SQLMoreResults(d->hStmt);
1099 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1100 if (r != SQL_NO_DATA) {
1101 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1102 "Unable to fetch last"), QSqlError::ConnectionError, d));
1104 return false;
1107 SQLSMALLINT fieldCount;
1108 r = SQLNumResultCols(d->hStmt, &fieldCount);
1109 setSelect(fieldCount > 0);
1110 for (int i = 0; i < fieldCount; ++i)
1111 d->recInf.append(qMakeFieldInfo(d, i));
1113 d->valueCache.resize(fieldCount);
1114 d->valueCache.fill(NULL);
1115 setActive(true);
1117 return true;
1120 void QDB2Result::virtual_hook(int id, void *data)
1122 switch (id) {
1123 case QSqlResult::NextResult:
1124 Q_ASSERT(data);
1125 *static_cast<bool*>(data) = nextResult();
1126 break;
1127 case QSqlResult::DetachFromResultSet:
1128 if (d->hStmt)
1129 SQLCloseCursor(d->hStmt);
1130 break;
1131 default:
1132 QSqlResult::virtual_hook(id, data);
1136 /************************************/
1138 QDB2Driver::QDB2Driver(QObject* parent)
1139 : QSqlDriver(parent)
1141 d = new QDB2DriverPrivate;
1144 QDB2Driver::QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent)
1145 : QSqlDriver(parent)
1147 d = new QDB2DriverPrivate;
1148 d->hEnv = (SQLHANDLE)env;
1149 d->hDbc = (SQLHANDLE)con;
1150 if (env && con) {
1151 setOpen(true);
1152 setOpenError(false);
1156 QDB2Driver::~QDB2Driver()
1158 close();
1159 delete d;
1162 bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString& host, int port,
1163 const QString& connOpts)
1165 if (isOpen())
1166 close();
1167 SQLRETURN r;
1168 r = SQLAllocHandle(SQL_HANDLE_ENV,
1169 SQL_NULL_HANDLE,
1170 &d->hEnv);
1171 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1172 qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate environment"), d);
1173 setOpenError(true);
1174 return false;
1177 r = SQLAllocHandle(SQL_HANDLE_DBC,
1178 d->hEnv,
1179 &d->hDbc);
1180 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1181 qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate connection"), d);
1182 setOpenError(true);
1183 return false;
1186 QString protocol;
1187 // Set connection attributes
1188 const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
1189 for (int i = 0; i < opts.count(); ++i) {
1190 const QString tmp(opts.at(i));
1191 int idx;
1192 if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
1193 qWarning("QDB2Driver::open: Illegal connect option value '%s'",
1194 tmp.toLocal8Bit().constData());
1195 continue;
1198 const QString opt(tmp.left(idx));
1199 const QString val(tmp.mid(idx + 1).simplified());
1201 SQLUINTEGER v = 0;
1202 r = SQL_SUCCESS;
1203 if (opt == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
1204 if (val == QLatin1String("SQL_MODE_READ_ONLY")) {
1205 v = SQL_MODE_READ_ONLY;
1206 } else if (val == QLatin1String("SQL_MODE_READ_WRITE")) {
1207 v = SQL_MODE_READ_WRITE;
1208 } else {
1209 qWarning("QDB2Driver::open: Unknown option value '%s'",
1210 tmp.toLocal8Bit().constData());
1211 continue;
1213 r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0);
1214 } else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
1215 v = val.toUInt();
1216 r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0);
1217 } else if (opt.compare(QLatin1String("PROTOCOL"), Qt::CaseInsensitive) == 0) {
1218 protocol = tmp;
1220 else {
1221 qWarning("QDB2Driver::open: Unknown connection attribute '%s'",
1222 tmp.toLocal8Bit().constData());
1224 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
1225 qSqlWarning(QString::fromLatin1("QDB2Driver::open: "
1226 "Unable to set connection attribute '%1'").arg(opt), d);
1229 if (protocol.isEmpty())
1230 protocol = QLatin1String("PROTOCOL=TCPIP");
1232 if (port < 0 )
1233 port = 50000;
1235 QString connQStr;
1236 connQStr = protocol + QLatin1String(";DATABASE=") + db + QLatin1String(";HOSTNAME=") + host
1237 + QLatin1String(";PORT=") + QString::number(port) + QLatin1String(";UID=") + user
1238 + QLatin1String(";PWD=") + password;
1241 SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH];
1242 SQLSMALLINT cb;
1244 r = SQLDriverConnect(d->hDbc,
1245 NULL,
1246 qToTChar(connQStr),
1247 (SQLSMALLINT) connQStr.length(),
1248 connOut,
1249 SQL_MAX_OPTION_STRING_LENGTH,
1250 &cb,
1251 SQL_DRIVER_NOPROMPT);
1252 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1253 setLastError(qMakeError(tr("Unable to connect"),
1254 QSqlError::ConnectionError, d));
1255 setOpenError(true);
1256 return false;
1259 d->user = user;
1260 setOpen(true);
1261 setOpenError(false);
1262 return true;
1265 void QDB2Driver::close()
1267 SQLRETURN r;
1268 if (d->hDbc) {
1269 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1270 if (isOpen()) {
1271 r = SQLDisconnect(d->hDbc);
1272 if (r != SQL_SUCCESS)
1273 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to disconnect datasource"), d);
1275 r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
1276 if (r != SQL_SUCCESS)
1277 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free connection handle"), d);
1278 d->hDbc = 0;
1281 if (d->hEnv) {
1282 r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
1283 if (r != SQL_SUCCESS)
1284 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free environment handle"), d);
1285 d->hEnv = 0;
1287 setOpen(false);
1288 setOpenError(false);
1291 QSqlResult *QDB2Driver::createResult() const
1293 return new QDB2Result(this, d);
1296 QSqlRecord QDB2Driver::record(const QString& tableName) const
1298 QSqlRecord fil;
1299 if (!isOpen())
1300 return fil;
1302 SQLHANDLE hStmt;
1303 QString catalog, schema, table;
1304 qSplitTableQualifier(tableName, &catalog, &schema, &table);
1305 if (schema.isEmpty())
1306 schema = d->user;
1308 if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
1309 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
1310 else
1311 catalog = catalog.toUpper();
1313 if (isIdentifierEscaped(schema, QSqlDriver::TableName))
1314 schema = stripDelimiters(schema, QSqlDriver::TableName);
1315 else
1316 schema = schema.toUpper();
1318 if (isIdentifierEscaped(table, QSqlDriver::TableName))
1319 table = stripDelimiters(table, QSqlDriver::TableName);
1320 else
1321 table = table.toUpper();
1323 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1324 d->hDbc,
1325 &hStmt);
1326 if (r != SQL_SUCCESS) {
1327 qSqlWarning(QLatin1String("QDB2Driver::record: Unable to allocate handle"), d);
1328 return fil;
1331 r = SQLSetStmtAttr(hStmt,
1332 SQL_ATTR_CURSOR_TYPE,
1333 (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
1334 SQL_IS_UINTEGER);
1337 //Aside: szSchemaName and szTableName parameters of SQLColumns
1338 //are case sensitive search patterns, so no escaping is used.
1339 r = SQLColumns(hStmt,
1340 NULL,
1342 qToTChar(schema),
1343 schema.length(),
1344 qToTChar(table),
1345 table.length(),
1346 NULL,
1349 if (r != SQL_SUCCESS)
1350 qSqlWarning(QLatin1String("QDB2Driver::record: Unable to execute column list"), d);
1351 r = SQLFetchScroll(hStmt,
1352 SQL_FETCH_NEXT,
1354 while (r == SQL_SUCCESS) {
1355 fil.append(qMakeFieldInfo(hStmt));
1356 r = SQLFetchScroll(hStmt,
1357 SQL_FETCH_NEXT,
1361 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1362 if (r != SQL_SUCCESS)
1363 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
1364 + QString::number(r), d);
1366 return fil;
1369 QStringList QDB2Driver::tables(QSql::TableType type) const
1371 QStringList tl;
1372 if (!isOpen())
1373 return tl;
1375 SQLHANDLE hStmt;
1377 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1378 d->hDbc,
1379 &hStmt);
1380 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1381 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to allocate handle"), d);
1382 return tl;
1384 r = SQLSetStmtAttr(hStmt,
1385 SQL_ATTR_CURSOR_TYPE,
1386 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1387 SQL_IS_UINTEGER);
1389 QString tableType;
1390 if (type & QSql::Tables)
1391 tableType += QLatin1String("TABLE,");
1392 if (type & QSql::Views)
1393 tableType += QLatin1String("VIEW,");
1394 if (type & QSql::SystemTables)
1395 tableType += QLatin1String("SYSTEM TABLE,");
1396 if (tableType.isEmpty())
1397 return tl;
1398 tableType.chop(1);
1400 r = SQLTables(hStmt,
1401 NULL,
1403 NULL,
1405 NULL,
1407 qToTChar(tableType),
1408 tableType.length());
1410 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
1411 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to execute table list"), d);
1412 r = SQLFetchScroll(hStmt,
1413 SQL_FETCH_NEXT,
1415 while (r == SQL_SUCCESS) {
1416 bool isNull;
1417 QString fieldVal = qGetStringData(hStmt, 2, -1, isNull);
1418 QString userVal = qGetStringData(hStmt, 1, -1, isNull);
1419 QString user = d->user;
1420 if ( isIdentifierEscaped(user, QSqlDriver::TableName))
1421 user = stripDelimiters(user, QSqlDriver::TableName);
1422 else
1423 user = user.toUpper();
1425 if (userVal != user)
1426 fieldVal = userVal + QLatin1Char('.') + fieldVal;
1427 tl.append(fieldVal);
1428 r = SQLFetchScroll(hStmt,
1429 SQL_FETCH_NEXT,
1433 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1434 if (r != SQL_SUCCESS)
1435 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to free statement handle ")
1436 + QString::number(r), d);
1437 return tl;
1440 QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const
1442 QSqlIndex index(tablename);
1443 if (!isOpen())
1444 return index;
1445 QSqlRecord rec = record(tablename);
1447 SQLHANDLE hStmt;
1448 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1449 d->hDbc,
1450 &hStmt);
1451 if (r != SQL_SUCCESS) {
1452 qSqlWarning(QLatin1String("QDB2Driver::primaryIndex: Unable to list primary key"), d);
1453 return index;
1455 QString catalog, schema, table;
1456 qSplitTableQualifier(tablename, &catalog, &schema, &table);
1458 if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
1459 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
1460 else
1461 catalog = catalog.toUpper();
1463 if (isIdentifierEscaped(schema, QSqlDriver::TableName))
1464 schema = stripDelimiters(schema, QSqlDriver::TableName);
1465 else
1466 schema = schema.toUpper();
1468 if (isIdentifierEscaped(table, QSqlDriver::TableName))
1469 table = stripDelimiters(table, QSqlDriver::TableName);
1470 else
1471 table = table.toUpper();
1473 r = SQLSetStmtAttr(hStmt,
1474 SQL_ATTR_CURSOR_TYPE,
1475 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1476 SQL_IS_UINTEGER);
1478 r = SQLPrimaryKeys(hStmt,
1479 NULL,
1481 qToTChar(schema),
1482 schema.length(),
1483 qToTChar(table),
1484 table.length());
1485 r = SQLFetchScroll(hStmt,
1486 SQL_FETCH_NEXT,
1489 bool isNull;
1490 QString cName, idxName;
1491 // Store all fields in a StringList because the driver can't detail fields in this FETCH loop
1492 while (r == SQL_SUCCESS) {
1493 cName = qGetStringData(hStmt, 3, -1, isNull); // column name
1494 idxName = qGetStringData(hStmt, 5, -1, isNull); // pk index name
1495 index.append(rec.field(cName));
1496 index.setName(idxName);
1497 r = SQLFetchScroll(hStmt,
1498 SQL_FETCH_NEXT,
1501 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1502 if (r!= SQL_SUCCESS)
1503 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
1504 + QString::number(r), d);
1505 return index;
1508 bool QDB2Driver::hasFeature(DriverFeature f) const
1510 switch (f) {
1511 case QuerySize:
1512 case NamedPlaceholders:
1513 case BatchOperations:
1514 case LastInsertId:
1515 case SimpleLocking:
1516 case EventNotifications:
1517 return false;
1518 case BLOB:
1519 case Transactions:
1520 case MultipleResultSets:
1521 case PreparedQueries:
1522 case PositionalPlaceholders:
1523 case LowPrecisionNumbers:
1524 case FinishQuery:
1525 return true;
1526 case Unicode:
1527 return true;
1529 return false;
1532 bool QDB2Driver::beginTransaction()
1534 if (!isOpen()) {
1535 qWarning("QDB2Driver::beginTransaction: Database not open");
1536 return false;
1538 return setAutoCommit(false);
1541 bool QDB2Driver::commitTransaction()
1543 if (!isOpen()) {
1544 qWarning("QDB2Driver::commitTransaction: Database not open");
1545 return false;
1547 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
1548 d->hDbc,
1549 SQL_COMMIT);
1550 if (r != SQL_SUCCESS) {
1551 setLastError(qMakeError(tr("Unable to commit transaction"),
1552 QSqlError::TransactionError, d));
1553 return false;
1555 return setAutoCommit(true);
1558 bool QDB2Driver::rollbackTransaction()
1560 if (!isOpen()) {
1561 qWarning("QDB2Driver::rollbackTransaction: Database not open");
1562 return false;
1564 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
1565 d->hDbc,
1566 SQL_ROLLBACK);
1567 if (r != SQL_SUCCESS) {
1568 setLastError(qMakeError(tr("Unable to rollback transaction"),
1569 QSqlError::TransactionError, d));
1570 return false;
1572 return setAutoCommit(true);
1575 bool QDB2Driver::setAutoCommit(bool autoCommit)
1577 SQLUINTEGER ac = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
1578 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
1579 SQL_ATTR_AUTOCOMMIT,
1580 (SQLPOINTER)ac,
1581 sizeof(ac));
1582 if (r != SQL_SUCCESS) {
1583 setLastError(qMakeError(tr("Unable to set autocommit"),
1584 QSqlError::TransactionError, d));
1585 return false;
1587 return true;
1590 QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const
1592 if (field.isNull())
1593 return QLatin1String("NULL");
1595 switch (field.type()) {
1596 case QVariant::DateTime: {
1597 // Use an escape sequence for the datetime fields
1598 if (field.value().toDateTime().isValid()) {
1599 QDate dt = field.value().toDateTime().date();
1600 QTime tm = field.value().toDateTime().time();
1601 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
1602 return QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-')
1603 + QString::number(dt.month()) + QLatin1Char('-')
1604 + QString::number(dt.day()) + QLatin1Char('-')
1605 + QString::number(tm.hour()) + QLatin1Char('.')
1606 + QString::number(tm.minute()).rightJustified(2, QLatin1Char('0'), true)
1607 + QLatin1Char('.')
1608 + QString::number(tm.second()).rightJustified(2, QLatin1Char('0'), true)
1609 + QLatin1Char('.')
1610 + QString::number(tm.msec() * 1000).rightJustified(6, QLatin1Char('0'), true)
1611 + QLatin1Char('\'');
1612 } else {
1613 return QLatin1String("NULL");
1616 case QVariant::ByteArray: {
1617 QByteArray ba = field.value().toByteArray();
1618 QString res = QString::fromLatin1("BLOB(X'");
1619 static const char hexchars[] = "0123456789abcdef";
1620 for (int i = 0; i < ba.size(); ++i) {
1621 uchar s = (uchar) ba[i];
1622 res += QLatin1Char(hexchars[s >> 4]);
1623 res += QLatin1Char(hexchars[s & 0x0f]);
1625 res += QLatin1String("')");
1626 return res;
1628 default:
1629 return QSqlDriver::formatValue(field, trimStrings);
1633 QVariant QDB2Driver::handle() const
1635 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
1638 QString QDB2Driver::escapeIdentifier(const QString &identifier, IdentifierType) const
1640 QString res = identifier;
1641 if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
1642 res.replace(QLatin1Char('"'), QLatin1String("\"\""));
1643 res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
1644 res.replace(QLatin1Char('.'), QLatin1String("\".\""));
1646 return res;
1649 QT_END_NAMESPACE