1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtSql module of the Qt Toolkit.
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
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.
40 ****************************************************************************/
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>
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
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
76 QDB2DriverPrivate(): hEnv(0), hDbc(0) {}
82 class QDB2ResultPrivate
85 QDB2ResultPrivate(const QDB2DriverPrivate
* d
): dp(d
), hStmt(0)
91 void clearValueCache()
93 for (int i
= 0; i
< valueCache
.count(); ++i
) {
98 void emptyValueCache()
104 const QDB2DriverPrivate
* dp
;
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
;
126 SQLRETURN r
= SQL_ERROR
;
127 SQLTCHAR state
[SQL_SQLSTATE_SIZE
+ 1];
128 SQLTCHAR description
[SQL_MAX_MESSAGE_LENGTH
];
129 r
= SQLGetDiagRec(handleType
,
134 (SQLTCHAR
*) description
,
135 SQL_MAX_MESSAGE_LENGTH
- 1, /* in bytes, not in characters */
137 if (r
== SQL_SUCCESS
|| r
== SQL_SUCCESS_WITH_INFO
)
138 return QString(qFromTChar(description
));
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
;
188 type
= QVariant::Double
;
194 type
= QVariant::Int
;
197 type
= QVariant::LongLong
;
202 case SQL_LONGVARBINARY
:
205 type
= QVariant::ByteArray
;
209 type
= QVariant::Date
;
213 type
= QVariant::Time
;
216 case SQL_TYPE_TIMESTAMP
:
217 type
= QVariant::DateTime
;
221 case SQL_WLONGVARCHAR
:
224 case SQL_LONGVARCHAR
:
225 type
= QVariant::String
;
228 type
= QVariant::ByteArray
;
234 static QSqlField
qMakeFieldInfo(const QDB2ResultPrivate
* d
, int i
)
236 SQLSMALLINT colNameLen
;
239 SQLSMALLINT colScale
;
240 SQLSMALLINT nullable
;
241 SQLRETURN r
= SQL_ERROR
;
242 SQLTCHAR colName
[COLNAMESIZE
];
243 r
= SQLDescribeCol(d
->hStmt
,
246 (SQLSMALLINT
) COLNAMESIZE
,
253 if (r
!= SQL_SUCCESS
) {
254 qSqlWarning(QString::fromLatin1("qMakeFieldInfo: Unable to describe column %1").arg(i
), d
);
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
)
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
));
270 static int qGetIntData(SQLHANDLE hStmt
, int column
, bool& isNull
)
274 SQLINTEGER lengthIndicator
= 0;
275 SQLRETURN r
= SQLGetData(hStmt
,
278 (SQLPOINTER
) &intbuf
,
281 if ((r
!= SQL_SUCCESS
&& r
!= SQL_SUCCESS_WITH_INFO
) || lengthIndicator
== SQL_NULL_DATA
) {
288 static double qGetDoubleData(SQLHANDLE hStmt
, int column
, bool& isNull
)
292 SQLINTEGER lengthIndicator
= 0;
293 SQLRETURN r
= SQLGetData(hStmt
,
296 (SQLPOINTER
) &dblbuf
,
299 if ((r
!= SQL_SUCCESS
&& r
!= SQL_SUCCESS_WITH_INFO
) || lengthIndicator
== SQL_NULL_DATA
) {
304 return (double) dblbuf
;
307 static SQLBIGINT
qGetBigIntData(SQLHANDLE hStmt
, int column
, bool& isNull
)
309 SQLBIGINT lngbuf
= Q_INT64_C(0);
311 SQLINTEGER lengthIndicator
= 0;
312 SQLRETURN r
= SQLGetData(hStmt
,
315 (SQLPOINTER
) &lngbuf
,
318 if ((r
!= SQL_SUCCESS
&& r
!= SQL_SUCCESS_WITH_INFO
) || lengthIndicator
== SQL_NULL_DATA
)
324 static QString
qGetStringData(SQLHANDLE hStmt
, int column
, int colSize
, bool& isNull
)
327 SQLRETURN r
= SQL_ERROR
;
328 SQLINTEGER lengthIndicator
= 0;
332 else if (colSize
> 65536) // limit buffer size to 64 KB
335 colSize
++; // make sure there is room for more than the 0 termination
336 SQLTCHAR
* buf
= new SQLTCHAR
[colSize
];
339 r
= SQLGetData(hStmt
,
343 colSize
* sizeof(SQLTCHAR
),
345 if (r
== SQL_SUCCESS
|| r
== SQL_SUCCESS_WITH_INFO
) {
346 if (lengthIndicator
== SQL_NULL_DATA
|| lengthIndicator
== SQL_NO_TOTAL
) {
351 fieldVal
+= qFromTChar(buf
);
352 } else if (r
== SQL_NO_DATA
) {
355 qWarning("qGetStringData: Error while fetching data (%d)", r
);
364 static QByteArray
qGetBinaryData(SQLHANDLE hStmt
, int column
, SQLINTEGER
& lengthIndicator
, bool& isNull
)
367 SQLSMALLINT colNameLen
;
370 SQLSMALLINT colScale
;
371 SQLSMALLINT nullable
;
372 SQLRETURN r
= SQL_ERROR
;
374 SQLTCHAR colName
[COLNAMESIZE
];
375 r
= SQLDescribeCol(hStmt
,
384 if (r
!= SQL_SUCCESS
)
385 qWarning("qGetBinaryData: Unable to describe column %d", column
);
386 // SQLDescribeCol may return 0 if size cannot be determined
389 else if (colSize
> 65536) // read the field in 64 KB chunks
391 char * buf
= new char[colSize
];
393 r
= SQLGetData(hStmt
,
395 colType
== SQL_DBCLOB
? SQL_C_CHAR
: SQL_C_BINARY
,
399 if (r
== SQL_SUCCESS
|| r
== SQL_SUCCESS_WITH_INFO
) {
400 if (lengthIndicator
== SQL_NULL_DATA
) {
405 r
== SQL_SUCCESS
? rSize
= lengthIndicator
: rSize
= colSize
;
406 if (lengthIndicator
== SQL_NO_TOTAL
) // size cannot be determined
408 fieldVal
.append(QByteArray(buf
, rSize
));
409 if (r
== SQL_SUCCESS
) // the whole field was read in one chunk
420 static void qSplitTableQualifier(const QString
& qualifier
, QString
* catalog
,
421 QString
* schema
, QString
* table
)
423 if (!catalog
|| !schema
|| !table
)
425 QStringList l
= qualifier
.split(QLatin1Char('.'));
427 return; // can't possibly be a valid table qualifier
428 int i
= 0, n
= l
.count();
432 for (QStringList::Iterator it
= l
.begin(); it
!= l
.end(); ++it
) {
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
)
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
)
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
471 static bool qMakeStatement(QDB2ResultPrivate
* d
, bool forwardOnly
, bool setForwardOnly
= true)
475 r
= SQLAllocHandle(SQL_HANDLE_STMT
,
478 if (r
!= SQL_SUCCESS
) {
479 qSqlWarning(QLatin1String("QDB2Result::reset: Unable to allocate statement handle"), d
);
483 r
= SQLFreeStmt(d
->hStmt
, SQL_CLOSE
);
484 if (r
!= SQL_SUCCESS
) {
485 qSqlWarning(QLatin1String("QDB2Result::reset: Unable to close statement handle"), d
);
494 r
= SQLSetStmtAttr(d
->hStmt
,
495 SQL_ATTR_CURSOR_TYPE
,
496 (SQLPOINTER
) SQL_CURSOR_FORWARD_ONLY
,
499 r
= SQLSetStmtAttr(d
->hStmt
,
500 SQL_ATTR_CURSOR_TYPE
,
501 (SQLPOINTER
) SQL_CURSOR_STATIC
,
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
);
513 QVariant
QDB2Result::handle() const
515 return QVariant(qRegisterMetaType
<SQLHANDLE
>("SQLHANDLE"), &d
->hStmt
);
518 /************************************/
520 QDB2Result::QDB2Result(const QDB2Driver
* dr
, const QDB2DriverPrivate
* dp
)
523 d
= new QDB2ResultPrivate(dp
);
526 QDB2Result::~QDB2Result()
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
);
537 bool QDB2Result::reset (const QString
& query
)
540 setAt(QSql::BeforeFirstRow
);
544 d
->emptyValueCache();
546 if (!qMakeStatement(d
, isForwardOnly()))
549 r
= SQLExecDirect(d
->hStmt
,
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
));
558 r
= SQLNumResultCols(d
->hStmt
, &count
);
561 for (int i
= 0; i
< count
; ++i
) {
562 d
->recInf
.append(qMakeFieldInfo(d
, i
));
567 d
->valueCache
.resize(count
);
568 d
->valueCache
.fill(NULL
);
573 bool QDB2Result::prepare(const QString
& query
)
576 setAt(QSql::BeforeFirstRow
);
580 d
->emptyValueCache();
582 if (!qMakeStatement(d
, isForwardOnly()))
585 r
= SQLPrepare(d
->hStmt
,
587 (SQLINTEGER
) query
.length());
589 if (r
!= SQL_SUCCESS
) {
590 setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
591 "Unable to prepare statement"), QSqlError::StatementError
, d
));
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
));
604 setAt(QSql::BeforeFirstRow
);
608 d
->emptyValueCache();
610 if (!qMakeStatement(d
, isForwardOnly(), false))
614 QVector
<QVariant
> &values
= boundValues();
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
)
624 switch (values
.at(i
).type()) {
625 case QVariant::Date
: {
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();
633 r
= SQLBindParameter(d
->hStmt
,
635 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
642 *ind
== SQL_NULL_DATA
? ind
: NULL
);
643 tmpStorage
.append(ba
);
645 case QVariant::Time
: {
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
,
655 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
662 *ind
== SQL_NULL_DATA
? ind
: NULL
);
663 tmpStorage
.append(ba
);
665 case QVariant::DateTime
: {
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
,
679 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
686 *ind
== SQL_NULL_DATA
? ind
: NULL
);
687 tmpStorage
.append(ba
);
690 r
= SQLBindParameter(d
->hStmt
,
692 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
697 (void *)values
.at(i
).constData(),
699 *ind
== SQL_NULL_DATA
? ind
: NULL
);
701 case QVariant::Double
:
702 r
= SQLBindParameter(d
->hStmt
,
704 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
709 (void *)values
.at(i
).constData(),
711 *ind
== SQL_NULL_DATA
? ind
: NULL
);
713 case QVariant::ByteArray
: {
714 int len
= values
.at(i
).toByteArray().size();
715 if (*ind
!= SQL_NULL_DATA
)
717 r
= SQLBindParameter(d
->hStmt
,
719 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
724 (void *)values
.at(i
).toByteArray().constData(),
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
,
737 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
742 (void *)ba
.constData(),
745 tmpStorage
.append(ba
);
747 void *data
= (void*)str
.utf16();
748 int len
= str
.length();
749 r
= SQLBindParameter(d
->hStmt
,
751 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
763 QByteArray ba
= values
.at(i
).toString().toAscii();
764 int len
= ba
.length() + 1;
765 if (*ind
!= SQL_NULL_DATA
)
767 r
= SQLBindParameter(d
->hStmt
,
769 qParamType
[(QFlag
)(bindValueType(i
)) & 3],
774 (void *) ba
.constData(),
777 tmpStorage
.append(ba
);
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
));
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
));
798 r
= SQLNumResultCols(d
->hStmt
, &count
);
801 for (int i
= 0; i
< count
; ++i
) {
802 d
->recInf
.append(qMakeFieldInfo(d
, i
));
808 d
->valueCache
.resize(count
);
809 d
->valueCache
.fill(NULL
);
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
));
821 case QVariant::Time
: {
822 TIME_STRUCT dt
= *((TIME_STRUCT
*)tmpStorage
.takeFirst().constData());
823 values
[i
] = QVariant(QTime(dt
.hour
, dt
.minute
, dt
.second
));
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)));
831 case QVariant::Double
:
832 case QVariant::ByteArray
:
834 case QVariant::String
:
835 if (bindValueType(i
) & QSql::Out
)
836 values
[i
] = QString::fromUtf16((ushort
*)tmpStorage
.takeFirst().constData());
839 values
[i
] = QString::fromAscii(tmpStorage
.takeFirst().constData());
842 if (indicators
[i
] == SQL_NULL_DATA
)
843 values
[i
] = QVariant(values
[i
].type());
848 bool QDB2Result::fetch(int i
)
850 if (isForwardOnly() && i
< at())
854 d
->clearValueCache();
855 int actualIdx
= i
+ 1;
856 if (actualIdx
<= 0) {
857 setAt(QSql::BeforeFirstRow
);
861 if (isForwardOnly()) {
863 while (ok
&& i
> at())
867 r
= SQLFetchScroll(d
->hStmt
,
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
));
876 else if (r
== SQL_NO_DATA
)
882 bool QDB2Result::fetchNext()
885 d
->clearValueCache();
886 r
= SQLFetchScroll(d
->hStmt
,
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
));
899 bool QDB2Result::fetchFirst()
901 if (isForwardOnly() && at() != QSql::BeforeFirstRow
)
905 d
->clearValueCache();
907 r
= SQLFetchScroll(d
->hStmt
,
910 if (r
!= SQL_SUCCESS
&& r
!= SQL_SUCCESS_WITH_INFO
) {
912 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", "Unable to fetch first"),
913 QSqlError::StatementError
, d
));
920 bool QDB2Result::fetchLast()
922 d
->clearValueCache();
925 if (i
== QSql::AfterLastRow
) {
926 if (isForwardOnly()) {
938 if (i
== QSql::BeforeFirstRow
) {
939 setAt(QSql::AfterLastRow
);
943 if (!isForwardOnly())
951 QVariant
QDB2Result::data(int field
)
953 if (field
>= d
->recInf
.count()) {
954 qWarning("QDB2Result::data: column %d out of range", field
);
958 SQLINTEGER lengthIndicator
= 0;
960 const QSqlField info
= d
->recInf
.field(field
);
962 if (!info
.isValid() || field
>= d
->valueCache
.size())
965 if (d
->valueCache
[field
])
966 return *d
->valueCache
[field
];
970 switch (info
.type()) {
971 case QVariant::LongLong
:
972 v
= new QVariant((qint64
) qGetBigIntData(d
->hStmt
, field
, isNull
));
975 v
= new QVariant(qGetIntData(d
->hStmt
, field
, isNull
));
977 case QVariant::Date
: {
979 r
= SQLGetData(d
->hStmt
,
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
));
988 v
= new QVariant(QDate());
992 case QVariant::Time
: {
994 r
= SQLGetData(d
->hStmt
,
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
));
1003 v
= new QVariant(QTime());
1007 case QVariant::DateTime
: {
1008 TIMESTAMP_STRUCT dtbuf
;
1009 r
= SQLGetData(d
->hStmt
,
1012 (SQLPOINTER
) &dtbuf
,
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)));
1019 v
= new QVariant(QDateTime());
1023 case QVariant::ByteArray
:
1024 v
= new QVariant(qGetBinaryData(d
->hStmt
, field
, lengthIndicator
, isNull
));
1026 case QVariant::Double
:
1028 switch(numericalPrecisionPolicy()) {
1029 case QSql::LowPrecisionInt32
:
1030 v
= new QVariant(qGetIntData(d
->hStmt
, field
, isNull
));
1032 case QSql::LowPrecisionInt64
:
1033 v
= new QVariant(qGetBigIntData(d
->hStmt
, field
, isNull
));
1035 case QSql::LowPrecisionDouble
:
1036 v
= new QVariant(qGetDoubleData(d
->hStmt
, field
, isNull
));
1038 case QSql::HighPrecision
:
1040 // length + 1 for the comma
1041 v
= new QVariant(qGetStringData(d
->hStmt
, field
, info
.length() + 1, isNull
));
1046 case QVariant::String
:
1048 v
= new QVariant(qGetStringData(d
->hStmt
, field
, info
.length(), isNull
));
1052 *v
= QVariant(info
.type());
1053 d
->valueCache
[field
] = v
;
1057 bool QDB2Result::isNull(int i
)
1059 if (i
>= d
->valueCache
.size())
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
;
1074 qSqlWarning(QLatin1String("QDB2Result::numRowsAffected: Unable to count affected rows"), d
);
1078 int QDB2Result::size()
1083 QSqlRecord
QDB2Result::record() const
1087 return QSqlRecord();
1090 bool QDB2Result::nextResult()
1093 setAt(QSql::BeforeFirstRow
);
1095 d
->emptyValueCache();
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
));
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
);
1120 void QDB2Result::virtual_hook(int id
, void *data
)
1123 case QSqlResult::NextResult
:
1125 *static_cast<bool*>(data
) = nextResult();
1127 case QSqlResult::DetachFromResultSet
:
1129 SQLCloseCursor(d
->hStmt
);
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
;
1152 setOpenError(false);
1156 QDB2Driver::~QDB2Driver()
1162 bool QDB2Driver::open(const QString
& db
, const QString
& user
, const QString
& password
, const QString
& host
, int port
,
1163 const QString
& connOpts
)
1168 r
= SQLAllocHandle(SQL_HANDLE_ENV
,
1171 if (r
!= SQL_SUCCESS
&& r
!= SQL_SUCCESS_WITH_INFO
) {
1172 qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate environment"), d
);
1177 r
= SQLAllocHandle(SQL_HANDLE_DBC
,
1180 if (r
!= SQL_SUCCESS
&& r
!= SQL_SUCCESS_WITH_INFO
) {
1181 qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate connection"), d
);
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
));
1192 if ((idx
= tmp
.indexOf(QLatin1Char('='))) == -1) {
1193 qWarning("QDB2Driver::open: Illegal connect option value '%s'",
1194 tmp
.toLocal8Bit().constData());
1198 const QString
opt(tmp
.left(idx
));
1199 const QString
val(tmp
.mid(idx
+ 1).simplified());
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
;
1209 qWarning("QDB2Driver::open: Unknown option value '%s'",
1210 tmp
.toLocal8Bit().constData());
1213 r
= SQLSetConnectAttr(d
->hDbc
, SQL_ATTR_ACCESS_MODE
, (SQLPOINTER
) v
, 0);
1214 } else if (opt
== QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
1216 r
= SQLSetConnectAttr(d
->hDbc
, SQL_ATTR_LOGIN_TIMEOUT
, (SQLPOINTER
) v
, 0);
1217 } else if (opt
.compare(QLatin1String("PROTOCOL"), Qt::CaseInsensitive
) == 0) {
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");
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
];
1244 r
= SQLDriverConnect(d
->hDbc
,
1247 (SQLSMALLINT
) connQStr
.length(),
1249 SQL_MAX_OPTION_STRING_LENGTH
,
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
));
1261 setOpenError(false);
1265 void QDB2Driver::close()
1269 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
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
);
1282 r
= SQLFreeHandle(SQL_HANDLE_ENV
, d
->hEnv
);
1283 if (r
!= SQL_SUCCESS
)
1284 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free environment handle"), d
);
1288 setOpenError(false);
1291 QSqlResult
*QDB2Driver::createResult() const
1293 return new QDB2Result(this, d
);
1296 QSqlRecord
QDB2Driver::record(const QString
& tableName
) const
1303 QString catalog
, schema
, table
;
1304 qSplitTableQualifier(tableName
, &catalog
, &schema
, &table
);
1305 if (schema
.isEmpty())
1308 if (isIdentifierEscaped(catalog
, QSqlDriver::TableName
))
1309 catalog
= stripDelimiters(catalog
, QSqlDriver::TableName
);
1311 catalog
= catalog
.toUpper();
1313 if (isIdentifierEscaped(schema
, QSqlDriver::TableName
))
1314 schema
= stripDelimiters(schema
, QSqlDriver::TableName
);
1316 schema
= schema
.toUpper();
1318 if (isIdentifierEscaped(table
, QSqlDriver::TableName
))
1319 table
= stripDelimiters(table
, QSqlDriver::TableName
);
1321 table
= table
.toUpper();
1323 SQLRETURN r
= SQLAllocHandle(SQL_HANDLE_STMT
,
1326 if (r
!= SQL_SUCCESS
) {
1327 qSqlWarning(QLatin1String("QDB2Driver::record: Unable to allocate handle"), d
);
1331 r
= SQLSetStmtAttr(hStmt
,
1332 SQL_ATTR_CURSOR_TYPE
,
1333 (SQLPOINTER
) SQL_CURSOR_FORWARD_ONLY
,
1337 //Aside: szSchemaName and szTableName parameters of SQLColumns
1338 //are case sensitive search patterns, so no escaping is used.
1339 r
= SQLColumns(hStmt
,
1349 if (r
!= SQL_SUCCESS
)
1350 qSqlWarning(QLatin1String("QDB2Driver::record: Unable to execute column list"), d
);
1351 r
= SQLFetchScroll(hStmt
,
1354 while (r
== SQL_SUCCESS
) {
1355 fil
.append(qMakeFieldInfo(hStmt
));
1356 r
= SQLFetchScroll(hStmt
,
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
);
1369 QStringList
QDB2Driver::tables(QSql::TableType type
) const
1377 SQLRETURN r
= SQLAllocHandle(SQL_HANDLE_STMT
,
1380 if (r
!= SQL_SUCCESS
&& r
!= SQL_SUCCESS_WITH_INFO
) {
1381 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to allocate handle"), d
);
1384 r
= SQLSetStmtAttr(hStmt
,
1385 SQL_ATTR_CURSOR_TYPE
,
1386 (SQLPOINTER
)SQL_CURSOR_FORWARD_ONLY
,
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())
1400 r
= SQLTables(hStmt
,
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
,
1415 while (r
== SQL_SUCCESS
) {
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
);
1423 user
= user
.toUpper();
1425 if (userVal
!= user
)
1426 fieldVal
= userVal
+ QLatin1Char('.') + fieldVal
;
1427 tl
.append(fieldVal
);
1428 r
= SQLFetchScroll(hStmt
,
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
);
1440 QSqlIndex
QDB2Driver::primaryIndex(const QString
& tablename
) const
1442 QSqlIndex
index(tablename
);
1445 QSqlRecord rec
= record(tablename
);
1448 SQLRETURN r
= SQLAllocHandle(SQL_HANDLE_STMT
,
1451 if (r
!= SQL_SUCCESS
) {
1452 qSqlWarning(QLatin1String("QDB2Driver::primaryIndex: Unable to list primary key"), d
);
1455 QString catalog
, schema
, table
;
1456 qSplitTableQualifier(tablename
, &catalog
, &schema
, &table
);
1458 if (isIdentifierEscaped(catalog
, QSqlDriver::TableName
))
1459 catalog
= stripDelimiters(catalog
, QSqlDriver::TableName
);
1461 catalog
= catalog
.toUpper();
1463 if (isIdentifierEscaped(schema
, QSqlDriver::TableName
))
1464 schema
= stripDelimiters(schema
, QSqlDriver::TableName
);
1466 schema
= schema
.toUpper();
1468 if (isIdentifierEscaped(table
, QSqlDriver::TableName
))
1469 table
= stripDelimiters(table
, QSqlDriver::TableName
);
1471 table
= table
.toUpper();
1473 r
= SQLSetStmtAttr(hStmt
,
1474 SQL_ATTR_CURSOR_TYPE
,
1475 (SQLPOINTER
)SQL_CURSOR_FORWARD_ONLY
,
1478 r
= SQLPrimaryKeys(hStmt
,
1485 r
= SQLFetchScroll(hStmt
,
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
,
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
);
1508 bool QDB2Driver::hasFeature(DriverFeature f
) const
1512 case NamedPlaceholders
:
1513 case BatchOperations
:
1516 case EventNotifications
:
1520 case MultipleResultSets
:
1521 case PreparedQueries
:
1522 case PositionalPlaceholders
:
1523 case LowPrecisionNumbers
:
1532 bool QDB2Driver::beginTransaction()
1535 qWarning("QDB2Driver::beginTransaction: Database not open");
1538 return setAutoCommit(false);
1541 bool QDB2Driver::commitTransaction()
1544 qWarning("QDB2Driver::commitTransaction: Database not open");
1547 SQLRETURN r
= SQLEndTran(SQL_HANDLE_DBC
,
1550 if (r
!= SQL_SUCCESS
) {
1551 setLastError(qMakeError(tr("Unable to commit transaction"),
1552 QSqlError::TransactionError
, d
));
1555 return setAutoCommit(true);
1558 bool QDB2Driver::rollbackTransaction()
1561 qWarning("QDB2Driver::rollbackTransaction: Database not open");
1564 SQLRETURN r
= SQLEndTran(SQL_HANDLE_DBC
,
1567 if (r
!= SQL_SUCCESS
) {
1568 setLastError(qMakeError(tr("Unable to rollback transaction"),
1569 QSqlError::TransactionError
, d
));
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
,
1582 if (r
!= SQL_SUCCESS
) {
1583 setLastError(qMakeError(tr("Unable to set autocommit"),
1584 QSqlError::TransactionError
, d
));
1590 QString
QDB2Driver::formatValue(const QSqlField
&field
, bool trimStrings
) const
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)
1608 + QString::number(tm
.second()).rightJustified(2, QLatin1Char('0'), true)
1610 + QString::number(tm
.msec() * 1000).rightJustified(6, QLatin1Char('0'), true)
1611 + QLatin1Char('\'');
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("')");
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("\".\""));