src/sql/drivers/db2/qsql_db2.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sql/drivers/db2/qsql_db2.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1650 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSql module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsql_db2.h"
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qsqlfield.h>
+#include <qsqlerror.h>
+#include <qsqlindex.h>
+#include <qsqlrecord.h>
+#include <qstringlist.h>
+#include <qvarlengtharray.h>
+#include <qvector.h>
+#include <QDebug>
+
+#if defined(Q_CC_BOR)
+// DB2's sqlsystm.h (included through sqlcli1.h) defines the SQL_BIGINT_TYPE
+// and SQL_BIGUINT_TYPE to wrong the types for Borland; so do the defines to
+// the right type before including the header
+#define SQL_BIGINT_TYPE qint64
+#define SQL_BIGUINT_TYPE quint64
+#endif
+
+#define UNICODE
+
+#include <sqlcli1.h>
+
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+static const int COLNAMESIZE = 255;
+static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
+
+class QDB2DriverPrivate
+{
+public:
+    QDB2DriverPrivate(): hEnv(0), hDbc(0) {}
+    SQLHANDLE hEnv;
+    SQLHANDLE hDbc;
+    QString user;
+};
+
+class QDB2ResultPrivate
+{
+public:
+    QDB2ResultPrivate(const QDB2DriverPrivate* d): dp(d), hStmt(0)
+    {}
+    ~QDB2ResultPrivate()
+    {
+        emptyValueCache();
+    }
+    void clearValueCache()
+    {
+        for (int i = 0; i < valueCache.count(); ++i) {
+            delete valueCache[i];
+            valueCache[i] = NULL;
+        }
+    }
+    void emptyValueCache()
+    {
+        clearValueCache();
+        valueCache.clear();
+    }
+
+    const QDB2DriverPrivate* dp;
+    SQLHANDLE hStmt;
+    QSqlRecord recInf;
+    QVector<QVariant*> valueCache;
+};
+
+static QString qFromTChar(SQLTCHAR* str)
+{
+    return QString::fromUtf16(str);
+}
+
+// dangerous!! (but fast). Don't use in functions that
+// require out parameters!
+static SQLTCHAR* qToTChar(const QString& str)
+{
+    return (SQLTCHAR*)str.utf16();
+}
+
+static QString qWarnDB2Handle(int handleType, SQLHANDLE handle)
+{
+    SQLINTEGER nativeCode;
+    SQLSMALLINT msgLen;
+    SQLRETURN r = SQL_ERROR;
+    SQLTCHAR state[SQL_SQLSTATE_SIZE + 1];
+    SQLTCHAR description[SQL_MAX_MESSAGE_LENGTH];
+    r = SQLGetDiagRec(handleType,
+                       handle,
+                       1,
+                       (SQLTCHAR*) state,
+                       &nativeCode,
+                       (SQLTCHAR*) description,
+                       SQL_MAX_MESSAGE_LENGTH - 1, /* in bytes, not in characters */
+                       &msgLen);
+    if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+        return QString(qFromTChar(description));
+    return QString();
+}
+
+static QString qDB2Warn(const QDB2DriverPrivate* d)
+{
+    return (qWarnDB2Handle(SQL_HANDLE_ENV, d->hEnv) + QLatin1Char(' ')
+             + qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc));
+}
+
+static QString qDB2Warn(const QDB2ResultPrivate* d)
+{
+    return (qWarnDB2Handle(SQL_HANDLE_ENV, d->dp->hEnv) + QLatin1Char(' ')
+             + qWarnDB2Handle(SQL_HANDLE_DBC, d->dp->hDbc)
+             + qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt));
+}
+
+static void qSqlWarning(const QString& message, const QDB2DriverPrivate* d)
+{
+    qWarning("%s\tError: %s", message.toLocal8Bit().constData(),
+                              qDB2Warn(d).toLocal8Bit().constData());
+}
+
+static void qSqlWarning(const QString& message, const QDB2ResultPrivate* d)
+{
+    qWarning("%s\tError: %s", message.toLocal8Bit().constData(),
+                              qDB2Warn(d).toLocal8Bit().constData());
+}
+
+static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
+                            const QDB2DriverPrivate* p)
+{
+    return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type);
+}
+
+static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
+                            const QDB2ResultPrivate* p)
+{
+    return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type);
+}
+
+static QVariant::Type qDecodeDB2Type(SQLSMALLINT sqltype)
+{
+    QVariant::Type type = QVariant::Invalid;
+    switch (sqltype) {
+    case SQL_REAL:
+    case SQL_FLOAT:
+    case SQL_DOUBLE:
+    case SQL_DECIMAL:
+    case SQL_NUMERIC:
+        type = QVariant::Double;
+        break;
+    case SQL_SMALLINT:
+    case SQL_INTEGER:
+    case SQL_BIT:
+    case SQL_TINYINT:
+        type = QVariant::Int;
+        break;
+    case SQL_BIGINT:
+        type = QVariant::LongLong;
+        break;
+    case SQL_BLOB:
+    case SQL_BINARY:
+    case SQL_VARBINARY:
+    case SQL_LONGVARBINARY:
+    case SQL_CLOB:
+    case SQL_DBCLOB:
+        type = QVariant::ByteArray;
+        break;
+    case SQL_DATE:
+    case SQL_TYPE_DATE:
+        type = QVariant::Date;
+        break;
+    case SQL_TIME:
+    case SQL_TYPE_TIME:
+        type = QVariant::Time;
+        break;
+    case SQL_TIMESTAMP:
+    case SQL_TYPE_TIMESTAMP:
+        type = QVariant::DateTime;
+        break;
+    case SQL_WCHAR:
+    case SQL_WVARCHAR:
+    case SQL_WLONGVARCHAR:
+    case SQL_CHAR:
+    case SQL_VARCHAR:
+    case SQL_LONGVARCHAR:
+        type = QVariant::String;
+        break;
+    default:
+        type = QVariant::ByteArray;
+        break;
+    }
+    return type;
+}
+
+static QSqlField qMakeFieldInfo(const QDB2ResultPrivate* d, int i)
+{
+    SQLSMALLINT colNameLen;
+    SQLSMALLINT colType;
+    SQLUINTEGER colSize;
+    SQLSMALLINT colScale;
+    SQLSMALLINT nullable;
+    SQLRETURN r = SQL_ERROR;
+    SQLTCHAR colName[COLNAMESIZE];
+    r = SQLDescribeCol(d->hStmt,
+                        i+1,
+                        colName,
+                        (SQLSMALLINT) COLNAMESIZE,
+                        &colNameLen,
+                        &colType,
+                        &colSize,
+                        &colScale,
+                        &nullable);
+
+    if (r != SQL_SUCCESS) {
+        qSqlWarning(QString::fromLatin1("qMakeFieldInfo: Unable to describe column %1").arg(i), d);
+        return QSqlField();
+    }
+    QSqlField f(qFromTChar(colName), qDecodeDB2Type(colType));
+    // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+    if (nullable == SQL_NO_NULLS)
+        f.setRequired(true);
+    else if (nullable == SQL_NULLABLE)
+        f.setRequired(false);
+    // else required is unknown
+    f.setLength(colSize == 0 ? -1 : int(colSize));
+    f.setPrecision(colScale == 0 ? -1 : int(colScale));
+    f.setSqlType(int(colType));
+    return f;
+}
+
+static int qGetIntData(SQLHANDLE hStmt, int column, bool& isNull)
+{
+    SQLINTEGER intbuf;
+    isNull = false;
+    SQLINTEGER lengthIndicator = 0;
+    SQLRETURN r = SQLGetData(hStmt,
+                              column + 1,
+                              SQL_C_SLONG,
+                              (SQLPOINTER) &intbuf,
+                              0,
+                              &lengthIndicator);
+    if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
+        isNull = true;
+        return 0;
+    }
+    return int(intbuf);
+}
+
+static double qGetDoubleData(SQLHANDLE hStmt, int column, bool& isNull)
+{
+    SQLDOUBLE dblbuf;
+    isNull = false;
+    SQLINTEGER lengthIndicator = 0;
+    SQLRETURN r = SQLGetData(hStmt,
+                              column+1,
+                              SQL_C_DOUBLE,
+                              (SQLPOINTER) &dblbuf,
+                              0,
+                              &lengthIndicator);
+    if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
+        isNull = true;
+        return 0.0;
+    }
+
+    return (double) dblbuf;
+}
+
+static SQLBIGINT qGetBigIntData(SQLHANDLE hStmt, int column, bool& isNull)
+{
+    SQLBIGINT lngbuf = Q_INT64_C(0);
+    isNull = false;
+    SQLINTEGER lengthIndicator = 0;
+    SQLRETURN r = SQLGetData(hStmt,
+                              column+1,
+                              SQL_C_SBIGINT,
+                              (SQLPOINTER) &lngbuf,
+                              0,
+                              &lengthIndicator);
+    if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA)
+        isNull = true;
+
+    return lngbuf;
+}
+
+static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool& isNull)
+{
+    QString     fieldVal;
+    SQLRETURN   r = SQL_ERROR;
+    SQLINTEGER  lengthIndicator = 0;
+
+    if (colSize <= 0)
+        colSize = 255;
+    else if (colSize > 65536) // limit buffer size to 64 KB
+        colSize = 65536;
+    else
+        colSize++; // make sure there is room for more than the 0 termination
+    SQLTCHAR* buf = new SQLTCHAR[colSize];
+
+    while (true) {
+        r = SQLGetData(hStmt,
+                        column + 1,
+                        SQL_C_WCHAR,
+                        (SQLPOINTER)buf,
+                        colSize * sizeof(SQLTCHAR),
+                        &lengthIndicator);
+        if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+            if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
+                fieldVal.clear();
+                isNull = true;
+                break;
+            }
+            fieldVal += qFromTChar(buf);
+        } else if (r == SQL_NO_DATA) {
+            break;
+        } else {
+            qWarning("qGetStringData: Error while fetching data (%d)", r);
+            fieldVal.clear();
+            break;
+        }
+    }
+    delete[] buf;
+    return fieldVal;
+}
+
+static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLINTEGER& lengthIndicator, bool& isNull)
+{
+    QByteArray fieldVal;
+    SQLSMALLINT colNameLen;
+    SQLSMALLINT colType;
+    SQLUINTEGER colSize;
+    SQLSMALLINT colScale;
+    SQLSMALLINT nullable;
+    SQLRETURN r = SQL_ERROR;
+
+    SQLTCHAR colName[COLNAMESIZE];
+    r = SQLDescribeCol(hStmt,
+                        column+1,
+                        colName,
+                        COLNAMESIZE,
+                        &colNameLen,
+                        &colType,
+                        &colSize,
+                        &colScale,
+                        &nullable);
+    if (r != SQL_SUCCESS)
+        qWarning("qGetBinaryData: Unable to describe column %d", column);
+    // SQLDescribeCol may return 0 if size cannot be determined
+    if (!colSize)
+        colSize = 255;
+    else if (colSize > 65536) // read the field in 64 KB chunks
+        colSize = 65536;
+    char * buf = new char[colSize];
+    while (true) {
+        r = SQLGetData(hStmt,
+                        column+1,
+                        colType == SQL_DBCLOB ? SQL_C_CHAR : SQL_C_BINARY,
+                        (SQLPOINTER) buf,
+                        colSize,
+                        &lengthIndicator);
+        if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+            if (lengthIndicator == SQL_NULL_DATA) {
+                isNull = true;
+                break;
+            } else {
+                int rSize;
+                r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
+                if (lengthIndicator == SQL_NO_TOTAL) // size cannot be determined
+                    rSize = colSize;
+                fieldVal.append(QByteArray(buf, rSize));
+                if (r == SQL_SUCCESS) // the whole field was read in one chunk
+                    break;
+            }
+        } else {
+            break;
+        }
+    }
+    delete [] buf;
+    return fieldVal;
+}
+
+static void qSplitTableQualifier(const QString & qualifier, QString * catalog,
+                                  QString * schema, QString * table)
+{
+    if (!catalog || !schema || !table)
+        return;
+    QStringList l = qualifier.split(QLatin1Char('.'));
+    if (l.count() > 3)
+        return; // can't possibly be a valid table qualifier
+    int i = 0, n = l.count();
+    if (n == 1) {
+        *table = qualifier;
+    } else {
+        for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
+            if (n == 3) {
+                if (i == 0)
+                    *catalog = *it;
+                else if (i == 1)
+                    *schema = *it;
+                else if (i == 2)
+                    *table = *it;
+            } else if (n == 2) {
+                if (i == 0)
+                    *schema = *it;
+                else if (i == 1)
+                    *table = *it;
+            }
+            i++;
+        }
+    }
+}
+
+// creates a QSqlField from a valid hStmt generated
+// by SQLColumns. The hStmt has to point to a valid position.
+static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt)
+{
+    bool isNull;
+    int type = qGetIntData(hStmt, 4, isNull);
+    QSqlField f(qGetStringData(hStmt, 3, -1, isNull), qDecodeDB2Type(type));
+    int required = qGetIntData(hStmt, 10, isNull); // nullable-flag
+    // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+    if (required == SQL_NO_NULLS)
+        f.setRequired(true);
+    else if (required == SQL_NULLABLE)
+        f.setRequired(false);
+    // else we don't know.
+    f.setLength(qGetIntData(hStmt, 6, isNull)); // column size
+    f.setPrecision(qGetIntData(hStmt, 8, isNull)); // precision
+    f.setSqlType(type);
+    return f;
+}
+
+static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwardOnly = true)
+{
+    SQLRETURN r;
+    if (!d->hStmt) {
+        r = SQLAllocHandle(SQL_HANDLE_STMT,
+                            d->dp->hDbc,
+                            &d->hStmt);
+        if (r != SQL_SUCCESS) {
+            qSqlWarning(QLatin1String("QDB2Result::reset: Unable to allocate statement handle"), d);
+            return false;
+        }
+    } else {
+        r = SQLFreeStmt(d->hStmt, SQL_CLOSE);
+        if (r != SQL_SUCCESS) {
+            qSqlWarning(QLatin1String("QDB2Result::reset: Unable to close statement handle"), d);
+            return false;
+        }
+    }
+
+    if (!setForwardOnly)
+        return true;
+
+    if (forwardOnly) {
+        r = SQLSetStmtAttr(d->hStmt,
+                            SQL_ATTR_CURSOR_TYPE,
+                            (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
+                            SQL_IS_UINTEGER);
+    } else {
+        r = SQLSetStmtAttr(d->hStmt,
+                            SQL_ATTR_CURSOR_TYPE,
+                            (SQLPOINTER) SQL_CURSOR_STATIC,
+                            SQL_IS_UINTEGER);
+    }
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        qSqlWarning(QString::fromLatin1("QDB2Result::reset: Unable to set %1 attribute.").arg(
+                     forwardOnly ? QLatin1String("SQL_CURSOR_FORWARD_ONLY")
+                                 : QLatin1String("SQL_CURSOR_STATIC")), d);
+        return false;
+    }
+    return true;
+}
+
+QVariant QDB2Result::handle() const
+{
+    return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
+}
+
+/************************************/
+
+QDB2Result::QDB2Result(const QDB2Driver* dr, const QDB2DriverPrivate* dp)
+    : QSqlResult(dr)
+{
+    d = new QDB2ResultPrivate(dp);
+}
+
+QDB2Result::~QDB2Result()
+{
+    if (d->hStmt) {
+        SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
+        if (r != SQL_SUCCESS)
+            qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
+                        + QString::number(r), d);
+    }
+    delete d;
+}
+
+bool QDB2Result::reset (const QString& query)
+{
+    setActive(false);
+    setAt(QSql::BeforeFirstRow);
+    SQLRETURN r;
+
+    d->recInf.clear();
+    d->emptyValueCache();
+
+    if (!qMakeStatement(d, isForwardOnly()))
+        return false;
+
+    r = SQLExecDirect(d->hStmt,
+                       qToTChar(query),
+                       (SQLINTEGER) query.length());
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
+                                "Unable to execute statement"), QSqlError::StatementError, d));
+        return false;
+    }
+    SQLSMALLINT count;
+    r = SQLNumResultCols(d->hStmt, &count);
+    if (count) {
+        setSelect(true);
+        for (int i = 0; i < count; ++i) {
+            d->recInf.append(qMakeFieldInfo(d, i));
+        }
+    } else {
+        setSelect(false);
+    }
+    d->valueCache.resize(count);
+    d->valueCache.fill(NULL);
+    setActive(true);
+    return true;
+}
+
+bool QDB2Result::prepare(const QString& query)
+{
+    setActive(false);
+    setAt(QSql::BeforeFirstRow);
+    SQLRETURN r;
+
+    d->recInf.clear();
+    d->emptyValueCache();
+
+    if (!qMakeStatement(d, isForwardOnly()))
+        return false;
+
+    r = SQLPrepare(d->hStmt,
+                    qToTChar(query),
+                    (SQLINTEGER) query.length());
+
+    if (r != SQL_SUCCESS) {
+        setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
+                     "Unable to prepare statement"), QSqlError::StatementError, d));
+        return false;
+    }
+    return true;
+}
+
+bool QDB2Result::exec()
+{
+    QList<QByteArray> tmpStorage; // holds temporary ptrs
+    QVarLengthArray<SQLINTEGER, 32> indicators(boundValues().count());
+
+    memset(indicators.data(), 0, indicators.size() * sizeof(SQLINTEGER));
+    setActive(false);
+    setAt(QSql::BeforeFirstRow);
+    SQLRETURN r;
+
+    d->recInf.clear();
+    d->emptyValueCache();
+
+    if (!qMakeStatement(d, isForwardOnly(), false))
+        return false;
+
+
+    QVector<QVariant> &values = boundValues();
+    int i;
+    for (i = 0; i < values.count(); ++i) {
+        // bind parameters - only positional binding allowed
+        SQLINTEGER *ind = &indicators[i];
+        if (values.at(i).isNull())
+            *ind = SQL_NULL_DATA;
+        if (bindValueType(i) & QSql::Out)
+            values[i].detach();
+
+        switch (values.at(i).type()) {
+            case QVariant::Date: {
+                QByteArray ba;
+                ba.resize(sizeof(DATE_STRUCT));
+                DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData();
+                QDate qdt = values.at(i).toDate();
+                dt->year = qdt.year();
+                dt->month = qdt.month();
+                dt->day = qdt.day();
+                r = SQLBindParameter(d->hStmt,
+                                     i + 1,
+                                     qParamType[(QFlag)(bindValueType(i)) & 3],
+                                     SQL_C_DATE,
+                                     SQL_DATE,
+                                     0,
+                                     0,
+                                     (void *) dt,
+                                     0,
+                                     *ind == SQL_NULL_DATA ? ind : NULL);
+                tmpStorage.append(ba);
+                break; }
+            case QVariant::Time: {
+                QByteArray ba;
+                ba.resize(sizeof(TIME_STRUCT));
+                TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData();
+                QTime qdt = values.at(i).toTime();
+                dt->hour = qdt.hour();
+                dt->minute = qdt.minute();
+                dt->second = qdt.second();
+                r = SQLBindParameter(d->hStmt,
+                                      i + 1,
+                                      qParamType[(QFlag)(bindValueType(i)) & 3],
+                                      SQL_C_TIME,
+                                      SQL_TIME,
+                                      0,
+                                      0,
+                                      (void *) dt,
+                                      0,
+                                      *ind == SQL_NULL_DATA ? ind : NULL);
+                tmpStorage.append(ba);
+                break; }
+            case QVariant::DateTime: {
+                QByteArray ba;
+                ba.resize(sizeof(TIMESTAMP_STRUCT));
+                TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData();
+                QDateTime qdt = values.at(i).toDateTime();
+                dt->year = qdt.date().year();
+                dt->month = qdt.date().month();
+                dt->day = qdt.date().day();
+                dt->hour = qdt.time().hour();
+                dt->minute = qdt.time().minute();
+                dt->second = qdt.time().second();
+                dt->fraction = qdt.time().msec() * 1000000;
+                r = SQLBindParameter(d->hStmt,
+                                      i + 1,
+                                      qParamType[(QFlag)(bindValueType(i)) & 3],
+                                      SQL_C_TIMESTAMP,
+                                      SQL_TIMESTAMP,
+                                      0,
+                                      0,
+                                      (void *) dt,
+                                      0,
+                                      *ind == SQL_NULL_DATA ? ind : NULL);
+                tmpStorage.append(ba);
+                break; }
+            case QVariant::Int:
+                r = SQLBindParameter(d->hStmt,
+                                      i + 1,
+                                      qParamType[(QFlag)(bindValueType(i)) & 3],
+                                      SQL_C_SLONG,
+                                      SQL_INTEGER,
+                                      0,
+                                      0,
+                                      (void *)values.at(i).constData(),
+                                      0,
+                                      *ind == SQL_NULL_DATA ? ind : NULL);
+                break;
+            case QVariant::Double:
+                r = SQLBindParameter(d->hStmt,
+                                      i + 1,
+                                      qParamType[(QFlag)(bindValueType(i)) & 3],
+                                      SQL_C_DOUBLE,
+                                      SQL_DOUBLE,
+                                      0,
+                                      0,
+                                      (void *)values.at(i).constData(),
+                                      0,
+                                      *ind == SQL_NULL_DATA ? ind : NULL);
+                break;
+            case QVariant::ByteArray: {
+                int len = values.at(i).toByteArray().size();
+                if (*ind != SQL_NULL_DATA)
+                    *ind = len;
+                r = SQLBindParameter(d->hStmt,
+                                      i + 1,
+                                      qParamType[(QFlag)(bindValueType(i)) & 3],
+                                      SQL_C_BINARY,
+                                      SQL_LONGVARBINARY,
+                                      len,
+                                      0,
+                                      (void *)values.at(i).toByteArray().constData(),
+                                      len,
+                                      ind);
+                break; }
+            case QVariant::String:
+            {
+                QString str(values.at(i).toString());
+                if (*ind != SQL_NULL_DATA)
+                    *ind = str.length() * sizeof(QChar);
+                if (bindValueType(i) & QSql::Out) {
+                    QByteArray ba((char*)str.utf16(), str.capacity() * sizeof(QChar));
+                    r = SQLBindParameter(d->hStmt,
+                                        i + 1,
+                                        qParamType[(QFlag)(bindValueType(i)) & 3],
+                                        SQL_C_WCHAR,
+                                        SQL_WVARCHAR,
+                                        str.length(),
+                                        0,
+                                        (void *)ba.constData(),
+                                        ba.size(),
+                                        ind);
+                    tmpStorage.append(ba);
+                } else {
+                    void *data = (void*)str.utf16();
+                    int len = str.length();
+                    r = SQLBindParameter(d->hStmt,
+                                        i + 1,
+                                        qParamType[(QFlag)(bindValueType(i)) & 3],
+                                        SQL_C_WCHAR,
+                                        SQL_WVARCHAR,
+                                        len,
+                                        0,
+                                        data,
+                                        len * sizeof(QChar),
+                                        ind);
+                }
+                break;
+            }
+            default: {
+                QByteArray ba = values.at(i).toString().toAscii();
+                int len = ba.length() + 1;
+                if (*ind != SQL_NULL_DATA)
+                    *ind = ba.length();
+                r = SQLBindParameter(d->hStmt,
+                                      i + 1,
+                                      qParamType[(QFlag)(bindValueType(i)) & 3],
+                                      SQL_C_CHAR,
+                                      SQL_VARCHAR,
+                                      len,
+                                      0,
+                                      (void *) ba.constData(),
+                                      len,
+                                      ind);
+                tmpStorage.append(ba);
+                break; }
+        }
+        if (r != SQL_SUCCESS) {
+            qWarning("QDB2Result::exec: unable to bind variable: %s",
+                     qDB2Warn(d).toLocal8Bit().constData());
+            setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
+                                    "Unable to bind variable"), QSqlError::StatementError, d));
+            return false;
+        }
+    }
+
+    r = SQLExecute(d->hStmt);
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        qWarning("QDB2Result::exec: Unable to execute statement: %s",
+                 qDB2Warn(d).toLocal8Bit().constData());
+        setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
+                                "Unable to execute statement"), QSqlError::StatementError, d));
+        return false;
+    }
+    SQLSMALLINT count;
+    r = SQLNumResultCols(d->hStmt, &count);
+    if (count) {
+        setSelect(true);
+        for (int i = 0; i < count; ++i) {
+            d->recInf.append(qMakeFieldInfo(d, i));
+        }
+    } else {
+        setSelect(false);
+    }
+    setActive(true);
+    d->valueCache.resize(count);
+    d->valueCache.fill(NULL);
+
+    //get out parameters
+    if (!hasOutValues())
+        return true;
+
+    for (i = 0; i < values.count(); ++i) {
+        switch (values[i].type()) {
+            case QVariant::Date: {
+                DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
+                values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
+                break; }
+            case QVariant::Time: {
+                TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
+                values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
+                break; }
+            case QVariant::DateTime: {
+                TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT *)tmpStorage.takeFirst().constData());
+                values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
+                              QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
+                break; }
+            case QVariant::Int:
+            case QVariant::Double:
+            case QVariant::ByteArray:
+                break;
+            case QVariant::String:
+                if (bindValueType(i) & QSql::Out)
+                    values[i] = QString::fromUtf16((ushort*)tmpStorage.takeFirst().constData());
+                break;
+            default: {
+                values[i] = QString::fromAscii(tmpStorage.takeFirst().constData());
+                break; }
+        }
+        if (indicators[i] == SQL_NULL_DATA)
+            values[i] = QVariant(values[i].type());
+    }
+    return true;
+}
+
+bool QDB2Result::fetch(int i)
+{
+    if (isForwardOnly() && i < at())
+        return false;
+    if (i == at())
+        return true;
+    d->clearValueCache();
+    int actualIdx = i + 1;
+    if (actualIdx <= 0) {
+        setAt(QSql::BeforeFirstRow);
+        return false;
+    }
+    SQLRETURN r;
+    if (isForwardOnly()) {
+        bool ok = true;
+        while (ok && i > at())
+            ok = fetchNext();
+        return ok;
+    } else {
+        r = SQLFetchScroll(d->hStmt,
+                            SQL_FETCH_ABSOLUTE,
+                            actualIdx);
+    }
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
+        setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
+                                "Unable to fetch record %1").arg(i), QSqlError::StatementError, d));
+        return false;
+    }
+    else if (r == SQL_NO_DATA)
+        return false;
+    setAt(i);
+    return true;
+}
+
+bool QDB2Result::fetchNext()
+{
+    SQLRETURN r;
+    d->clearValueCache();
+    r = SQLFetchScroll(d->hStmt,
+                       SQL_FETCH_NEXT,
+                       0);
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        if (r != SQL_NO_DATA)
+            setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
+                                    "Unable to fetch next"), QSqlError::StatementError, d));
+        return false;
+    }
+    setAt(at() + 1);
+    return true;
+}
+
+bool QDB2Result::fetchFirst()
+{
+    if (isForwardOnly() && at() != QSql::BeforeFirstRow)
+        return false;
+    if (isForwardOnly())
+        return fetchNext();
+    d->clearValueCache();
+    SQLRETURN r;
+    r = SQLFetchScroll(d->hStmt,
+                       SQL_FETCH_FIRST,
+                       0);
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        if(r!= SQL_NO_DATA)
+            setLastError(qMakeError(QCoreApplication::translate("QDB2Result", "Unable to fetch first"),
+                                    QSqlError::StatementError, d));
+        return false;
+    }
+    setAt(0);
+    return true;
+}
+
+bool QDB2Result::fetchLast()
+{
+    d->clearValueCache();
+
+    int i = at();
+    if (i == QSql::AfterLastRow) {
+        if (isForwardOnly()) {
+            return false;
+        } else {
+            if (!fetch(0))
+                return false;
+            i = at();
+        }
+    }
+
+    while (fetchNext())
+        ++i;
+
+    if (i == QSql::BeforeFirstRow) {
+        setAt(QSql::AfterLastRow);
+        return false;
+    }
+
+    if (!isForwardOnly())
+        return fetch(i);
+
+    setAt(i);
+    return true;
+}
+
+
+QVariant QDB2Result::data(int field)
+{
+    if (field >= d->recInf.count()) {
+        qWarning("QDB2Result::data: column %d out of range", field);
+        return QVariant();
+    }
+    SQLRETURN r = 0;
+    SQLINTEGER lengthIndicator = 0;
+    bool isNull = false;
+    const QSqlField info = d->recInf.field(field);
+
+    if (!info.isValid() || field >= d->valueCache.size())
+        return QVariant();
+
+    if (d->valueCache[field])
+        return *d->valueCache[field];
+
+
+    QVariant* v = 0;
+    switch (info.type()) {
+        case QVariant::LongLong:
+            v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull));
+            break;
+        case QVariant::Int:
+            v = new QVariant(qGetIntData(d->hStmt, field, isNull));
+            break;
+        case QVariant::Date: {
+            DATE_STRUCT dbuf;
+            r = SQLGetData(d->hStmt,
+                            field + 1,
+                            SQL_C_DATE,
+                            (SQLPOINTER) &dbuf,
+                            0,
+                            &lengthIndicator);
+            if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
+                v = new QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
+            } else {
+                v = new QVariant(QDate());
+                isNull = true;
+            }
+            break; }
+        case QVariant::Time: {
+            TIME_STRUCT tbuf;
+            r = SQLGetData(d->hStmt,
+                            field + 1,
+                            SQL_C_TIME,
+                            (SQLPOINTER) &tbuf,
+                            0,
+                            &lengthIndicator);
+            if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
+                v = new QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
+            } else {
+                v = new QVariant(QTime());
+                isNull = true;
+            }
+            break; }
+        case QVariant::DateTime: {
+            TIMESTAMP_STRUCT dtbuf;
+            r = SQLGetData(d->hStmt,
+                            field + 1,
+                            SQL_C_TIMESTAMP,
+                            (SQLPOINTER) &dtbuf,
+                            0,
+                            &lengthIndicator);
+            if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
+                v = new QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
+                                             QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
+            } else {
+                v = new QVariant(QDateTime());
+                isNull = true;
+            }
+            break; }
+        case QVariant::ByteArray:
+            v = new QVariant(qGetBinaryData(d->hStmt, field, lengthIndicator, isNull));
+            break;
+        case QVariant::Double:
+            {
+            QString value=qGetStringData(d->hStmt, field, info.length() + 1, isNull);
+            switch(numericalPrecisionPolicy()) {
+                case QSql::LowPrecisionInt32:
+                    v = new QVariant(qGetIntData(d->hStmt, field, isNull));
+                    break;
+                case QSql::LowPrecisionInt64:
+                    v = new QVariant(qGetBigIntData(d->hStmt, field, isNull));
+                    break;
+                case QSql::LowPrecisionDouble:
+                    v = new QVariant(qGetDoubleData(d->hStmt, field, isNull));
+                    break;
+                case QSql::HighPrecision:
+                default:
+                    // length + 1 for the comma
+                    v = new QVariant(qGetStringData(d->hStmt, field, info.length() + 1, isNull));
+                    break;
+            }
+            break;
+            }
+        case QVariant::String:
+        default:
+            v = new QVariant(qGetStringData(d->hStmt, field, info.length(), isNull));
+            break;
+    }
+    if (isNull)
+        *v = QVariant(info.type());
+    d->valueCache[field] = v;
+    return *v;
+}
+
+bool QDB2Result::isNull(int i)
+{
+    if (i >= d->valueCache.size())
+        return true;
+
+    if (d->valueCache[i])
+        return d->valueCache[i]->isNull();
+    return data(i).isNull();
+}
+
+int QDB2Result::numRowsAffected()
+{
+    SQLINTEGER affectedRowCount = 0;
+    SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
+    if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+        return affectedRowCount;
+    else
+        qSqlWarning(QLatin1String("QDB2Result::numRowsAffected: Unable to count affected rows"), d);
+    return -1;
+}
+
+int QDB2Result::size()
+{
+    return -1;
+}
+
+QSqlRecord QDB2Result::record() const
+{
+    if (isActive())
+        return d->recInf;
+    return QSqlRecord();
+}
+
+bool QDB2Result::nextResult()
+{
+    setActive(false);
+    setAt(QSql::BeforeFirstRow);
+    d->recInf.clear();
+    d->emptyValueCache();
+    setSelect(false);
+
+    SQLRETURN r = SQLMoreResults(d->hStmt);
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        if (r != SQL_NO_DATA) {
+            setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
+                "Unable to fetch last"), QSqlError::ConnectionError, d));
+        }
+        return false;
+    }
+
+    SQLSMALLINT fieldCount;
+    r = SQLNumResultCols(d->hStmt, &fieldCount);
+    setSelect(fieldCount > 0);
+    for (int i = 0; i < fieldCount; ++i)
+        d->recInf.append(qMakeFieldInfo(d, i));
+
+    d->valueCache.resize(fieldCount);
+    d->valueCache.fill(NULL);
+    setActive(true);
+
+    return true;
+}
+
+void QDB2Result::virtual_hook(int id, void *data)
+{
+    switch (id) {
+    case QSqlResult::NextResult:
+        Q_ASSERT(data);
+        *static_cast<bool*>(data) = nextResult();
+        break;
+    case QSqlResult::DetachFromResultSet:
+        if (d->hStmt)
+            SQLCloseCursor(d->hStmt);
+        break;
+    default:
+        QSqlResult::virtual_hook(id, data);
+    }
+}
+
+/************************************/
+
+QDB2Driver::QDB2Driver(QObject* parent)
+    : QSqlDriver(parent)
+{
+    d = new QDB2DriverPrivate;
+}
+
+QDB2Driver::QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent)
+    : QSqlDriver(parent)
+{
+    d = new QDB2DriverPrivate;
+    d->hEnv = (SQLHANDLE)env;
+    d->hDbc = (SQLHANDLE)con;
+    if (env && con) {
+        setOpen(true);
+        setOpenError(false);
+    }
+}
+
+QDB2Driver::~QDB2Driver()
+{
+    close();
+    delete d;
+}
+
+bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString& host, int port,
+                       const QString& connOpts)
+{
+    if (isOpen())
+      close();
+    SQLRETURN r;
+    r = SQLAllocHandle(SQL_HANDLE_ENV,
+                        SQL_NULL_HANDLE,
+                        &d->hEnv);
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate environment"), d);
+        setOpenError(true);
+        return false;
+    }
+
+    r = SQLAllocHandle(SQL_HANDLE_DBC,
+                        d->hEnv,
+                        &d->hDbc);
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate connection"), d);
+        setOpenError(true);
+        return false;
+    }
+
+    QString protocol;
+    // Set connection attributes
+    const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
+    for (int i = 0; i < opts.count(); ++i) {
+        const QString tmp(opts.at(i));
+        int idx;
+        if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
+            qWarning("QDB2Driver::open: Illegal connect option value '%s'",
+                     tmp.toLocal8Bit().constData());
+            continue;
+        }
+
+        const QString opt(tmp.left(idx));
+        const QString val(tmp.mid(idx + 1).simplified());
+
+        SQLUINTEGER v = 0;
+        r = SQL_SUCCESS;
+        if (opt == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
+            if (val == QLatin1String("SQL_MODE_READ_ONLY")) {
+                v = SQL_MODE_READ_ONLY;
+            } else if (val == QLatin1String("SQL_MODE_READ_WRITE")) {
+                v = SQL_MODE_READ_WRITE;
+            } else {
+                qWarning("QDB2Driver::open: Unknown option value '%s'",
+                         tmp.toLocal8Bit().constData());
+                continue;
+            }
+            r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0);
+        } else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
+            v = val.toUInt();
+            r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0);
+        } else if (opt.compare(QLatin1String("PROTOCOL"), Qt::CaseInsensitive) == 0) {
+                        protocol = tmp;
+        }
+        else {
+            qWarning("QDB2Driver::open: Unknown connection attribute '%s'",
+                      tmp.toLocal8Bit().constData());
+        }
+        if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
+            qSqlWarning(QString::fromLatin1("QDB2Driver::open: "
+                           "Unable to set connection attribute '%1'").arg(opt), d);
+    }
+
+    if (protocol.isEmpty())
+        protocol = QLatin1String("PROTOCOL=TCPIP");
+
+    if (port < 0 )
+        port = 50000;
+
+    QString connQStr;
+    connQStr =  protocol + QLatin1String(";DATABASE=") + db + QLatin1String(";HOSTNAME=") + host
+        + QLatin1String(";PORT=") + QString::number(port) + QLatin1String(";UID=") + user
+        + QLatin1String(";PWD=") + password;
+
+
+    SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH];
+    SQLSMALLINT cb;
+
+    r = SQLDriverConnect(d->hDbc,
+                          NULL,
+                          qToTChar(connQStr),
+                          (SQLSMALLINT) connQStr.length(),
+                          connOut,
+                          SQL_MAX_OPTION_STRING_LENGTH,
+                          &cb,
+                          SQL_DRIVER_NOPROMPT);
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        setLastError(qMakeError(tr("Unable to connect"),
+                                QSqlError::ConnectionError, d));
+        setOpenError(true);
+        return false;
+    }
+
+    d->user = user;
+    setOpen(true);
+    setOpenError(false);
+    return true;
+}
+
+void QDB2Driver::close()
+{
+    SQLRETURN r;
+    if (d->hDbc) {
+        // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
+        if (isOpen()) {
+            r = SQLDisconnect(d->hDbc);
+            if (r != SQL_SUCCESS)
+                qSqlWarning(QLatin1String("QDB2Driver::close: Unable to disconnect datasource"), d);
+        }
+        r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
+        if (r != SQL_SUCCESS)
+            qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free connection handle"), d);
+        d->hDbc = 0;
+    }
+
+    if (d->hEnv) {
+        r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
+        if (r != SQL_SUCCESS)
+            qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free environment handle"), d);
+        d->hEnv = 0;
+    }
+    setOpen(false);
+    setOpenError(false);
+}
+
+QSqlResult *QDB2Driver::createResult() const
+{
+    return new QDB2Result(this, d);
+}
+
+QSqlRecord QDB2Driver::record(const QString& tableName) const
+{
+    QSqlRecord fil;
+    if (!isOpen())
+        return fil;
+
+    SQLHANDLE hStmt;
+    QString catalog, schema, table;
+    qSplitTableQualifier(tableName, &catalog, &schema, &table);
+    if (schema.isEmpty())
+        schema = d->user;
+
+    if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
+        catalog = stripDelimiters(catalog, QSqlDriver::TableName);
+    else
+        catalog = catalog.toUpper();
+
+    if (isIdentifierEscaped(schema, QSqlDriver::TableName))
+        schema = stripDelimiters(schema, QSqlDriver::TableName);
+    else
+        schema = schema.toUpper();
+
+    if (isIdentifierEscaped(table, QSqlDriver::TableName))
+        table = stripDelimiters(table, QSqlDriver::TableName);
+    else
+        table = table.toUpper();
+
+    SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
+                                  d->hDbc,
+                                  &hStmt);
+    if (r != SQL_SUCCESS) {
+        qSqlWarning(QLatin1String("QDB2Driver::record: Unable to allocate handle"), d);
+        return fil;
+    }
+
+    r = SQLSetStmtAttr(hStmt,
+                        SQL_ATTR_CURSOR_TYPE,
+                        (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
+                        SQL_IS_UINTEGER);
+
+
+    //Aside: szSchemaName and szTableName parameters of SQLColumns
+    //are case sensitive search patterns, so no escaping is used.
+    r =  SQLColumns(hStmt,
+                     NULL,
+                     0,
+                     qToTChar(schema),
+                     schema.length(),
+                     qToTChar(table),
+                     table.length(),
+                     NULL,
+                     0);
+
+    if (r != SQL_SUCCESS)
+        qSqlWarning(QLatin1String("QDB2Driver::record: Unable to execute column list"), d);
+    r = SQLFetchScroll(hStmt,
+                        SQL_FETCH_NEXT,
+                        0);
+    while (r == SQL_SUCCESS) {
+        fil.append(qMakeFieldInfo(hStmt));
+        r = SQLFetchScroll(hStmt,
+                            SQL_FETCH_NEXT,
+                            0);
+    }
+
+    r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
+    if (r != SQL_SUCCESS)
+        qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
+                    + QString::number(r), d);
+
+    return fil;
+}
+
+QStringList QDB2Driver::tables(QSql::TableType type) const
+{
+    QStringList tl;
+    if (!isOpen())
+        return tl;
+
+    SQLHANDLE hStmt;
+
+    SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
+                                  d->hDbc,
+                                  &hStmt);
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+        qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to allocate handle"), d);
+        return tl;
+    }
+    r = SQLSetStmtAttr(hStmt,
+                        SQL_ATTR_CURSOR_TYPE,
+                        (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+                        SQL_IS_UINTEGER);
+
+    QString tableType;
+    if (type & QSql::Tables)
+        tableType += QLatin1String("TABLE,");
+    if (type & QSql::Views)
+        tableType += QLatin1String("VIEW,");
+    if (type & QSql::SystemTables)
+        tableType += QLatin1String("SYSTEM TABLE,");
+    if (tableType.isEmpty())
+        return tl;
+    tableType.chop(1);
+
+    r = SQLTables(hStmt,
+                   NULL,
+                   0,
+                   NULL,
+                   0,
+                   NULL,
+                   0,
+                   qToTChar(tableType),
+                   tableType.length());
+
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
+        qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to execute table list"), d);
+    r = SQLFetchScroll(hStmt,
+                        SQL_FETCH_NEXT,
+                        0);
+    while (r == SQL_SUCCESS) {
+        bool isNull;
+        QString fieldVal = qGetStringData(hStmt, 2, -1, isNull);
+        QString userVal = qGetStringData(hStmt, 1, -1, isNull);
+        QString user = d->user;
+        if ( isIdentifierEscaped(user, QSqlDriver::TableName))
+            user = stripDelimiters(user, QSqlDriver::TableName);
+        else
+            user = user.toUpper();
+
+        if (userVal != user)
+            fieldVal = userVal + QLatin1Char('.') + fieldVal;
+        tl.append(fieldVal);
+        r = SQLFetchScroll(hStmt,
+                            SQL_FETCH_NEXT,
+                            0);
+    }
+
+    r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
+    if (r != SQL_SUCCESS)
+        qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to free statement handle ")
+                    + QString::number(r), d);
+    return tl;
+}
+
+QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const
+{
+    QSqlIndex index(tablename);
+    if (!isOpen())
+        return index;
+    QSqlRecord rec = record(tablename);
+
+    SQLHANDLE hStmt;
+    SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
+                                  d->hDbc,
+                                  &hStmt);
+    if (r != SQL_SUCCESS) {
+        qSqlWarning(QLatin1String("QDB2Driver::primaryIndex: Unable to list primary key"), d);
+        return index;
+    }
+    QString catalog, schema, table;
+    qSplitTableQualifier(tablename, &catalog, &schema, &table);
+
+    if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
+        catalog = stripDelimiters(catalog, QSqlDriver::TableName);
+    else
+        catalog = catalog.toUpper();
+
+    if (isIdentifierEscaped(schema, QSqlDriver::TableName))
+        schema = stripDelimiters(schema, QSqlDriver::TableName);
+    else
+        schema = schema.toUpper();
+
+    if (isIdentifierEscaped(table, QSqlDriver::TableName))
+        table = stripDelimiters(table, QSqlDriver::TableName);
+    else
+        table = table.toUpper();
+
+    r = SQLSetStmtAttr(hStmt,
+                        SQL_ATTR_CURSOR_TYPE,
+                        (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+                        SQL_IS_UINTEGER);
+
+    r = SQLPrimaryKeys(hStmt,
+                        NULL,
+                        0,
+                        qToTChar(schema),
+                        schema.length(),
+                        qToTChar(table),
+                        table.length());
+    r = SQLFetchScroll(hStmt,
+                        SQL_FETCH_NEXT,
+                        0);
+
+    bool isNull;
+    QString cName, idxName;
+    // Store all fields in a StringList because the driver can't detail fields in this FETCH loop
+    while (r == SQL_SUCCESS) {
+        cName = qGetStringData(hStmt, 3, -1, isNull); // column name
+        idxName = qGetStringData(hStmt, 5, -1, isNull); // pk index name
+        index.append(rec.field(cName));
+        index.setName(idxName);
+        r = SQLFetchScroll(hStmt,
+                            SQL_FETCH_NEXT,
+                            0);
+    }
+    r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
+    if (r!= SQL_SUCCESS)
+        qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
+                    + QString::number(r), d);
+    return index;
+}
+
+bool QDB2Driver::hasFeature(DriverFeature f) const
+{
+    switch (f) {
+        case QuerySize:
+        case NamedPlaceholders:
+        case BatchOperations:
+        case LastInsertId:
+        case SimpleLocking:
+        case EventNotifications:
+            return false;
+        case BLOB:
+        case Transactions:
+        case MultipleResultSets:
+        case PreparedQueries:
+        case PositionalPlaceholders:
+        case LowPrecisionNumbers:
+        case FinishQuery:
+            return true;
+        case Unicode:
+            return true;
+    }
+    return false;
+}
+
+bool QDB2Driver::beginTransaction()
+{
+    if (!isOpen()) {
+        qWarning("QDB2Driver::beginTransaction: Database not open");
+        return false;
+    }
+    return setAutoCommit(false);
+}
+
+bool QDB2Driver::commitTransaction()
+{
+    if (!isOpen()) {
+        qWarning("QDB2Driver::commitTransaction: Database not open");
+        return false;
+    }
+    SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
+                              d->hDbc,
+                              SQL_COMMIT);
+    if (r != SQL_SUCCESS) {
+        setLastError(qMakeError(tr("Unable to commit transaction"),
+                     QSqlError::TransactionError, d));
+        return false;
+    }
+    return setAutoCommit(true);
+}
+
+bool QDB2Driver::rollbackTransaction()
+{
+    if (!isOpen()) {
+        qWarning("QDB2Driver::rollbackTransaction: Database not open");
+        return false;
+    }
+    SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
+                              d->hDbc,
+                              SQL_ROLLBACK);
+    if (r != SQL_SUCCESS) {
+        setLastError(qMakeError(tr("Unable to rollback transaction"),
+                                QSqlError::TransactionError, d));
+        return false;
+    }
+    return setAutoCommit(true);
+}
+
+bool QDB2Driver::setAutoCommit(bool autoCommit)
+{
+    SQLUINTEGER ac = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
+    SQLRETURN r  = SQLSetConnectAttr(d->hDbc,
+                                      SQL_ATTR_AUTOCOMMIT,
+                                      (SQLPOINTER)ac,
+                                      sizeof(ac));
+    if (r != SQL_SUCCESS) {
+        setLastError(qMakeError(tr("Unable to set autocommit"),
+                                QSqlError::TransactionError, d));
+        return false;
+    }
+    return true;
+}
+
+QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const
+{
+    if (field.isNull())
+        return QLatin1String("NULL");
+
+    switch (field.type()) {
+        case QVariant::DateTime: {
+            // Use an escape sequence for the datetime fields
+            if (field.value().toDateTime().isValid()) {
+                QDate dt = field.value().toDateTime().date();
+                QTime tm = field.value().toDateTime().time();
+                // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
+                return QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-')
+                       + QString::number(dt.month()) + QLatin1Char('-')
+                       + QString::number(dt.day()) + QLatin1Char('-')
+                       + QString::number(tm.hour()) + QLatin1Char('.')
+                       + QString::number(tm.minute()).rightJustified(2, QLatin1Char('0'), true)
+                       + QLatin1Char('.')
+                       + QString::number(tm.second()).rightJustified(2, QLatin1Char('0'), true)
+                       + QLatin1Char('.')
+                       + QString::number(tm.msec() * 1000).rightJustified(6, QLatin1Char('0'), true)
+                       + QLatin1Char('\'');
+                } else {
+                    return QLatin1String("NULL");
+                }
+        }
+        case QVariant::ByteArray: {
+            QByteArray ba = field.value().toByteArray();
+            QString res = QString::fromLatin1("BLOB(X'");
+            static const char hexchars[] = "0123456789abcdef";
+            for (int i = 0; i < ba.size(); ++i) {
+                uchar s = (uchar) ba[i];
+                res += QLatin1Char(hexchars[s >> 4]);
+                res += QLatin1Char(hexchars[s & 0x0f]);
+            }
+            res += QLatin1String("')");
+            return res;
+        }
+        default:
+            return QSqlDriver::formatValue(field, trimStrings);
+    }
+}
+
+QVariant QDB2Driver::handle() const
+{
+    return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
+}
+
+QString QDB2Driver::escapeIdentifier(const QString &identifier, IdentifierType) const
+{
+    QString res = identifier;
+    if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
+        res.replace(QLatin1Char('"'), QLatin1String("\"\""));
+        res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
+        res.replace(QLatin1Char('.'), QLatin1String("\".\""));
+    }
+    return res;
+}
+
+QT_END_NAMESPACE