summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers/db2/qsql_db2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sqldrivers/db2/qsql_db2.cpp')
-rw-r--r--src/plugins/sqldrivers/db2/qsql_db2.cpp1700
1 files changed, 1700 insertions, 0 deletions
diff --git a/src/plugins/sqldrivers/db2/qsql_db2.cpp b/src/plugins/sqldrivers/db2/qsql_db2.cpp
new file mode 100644
index 0000000000..4ccc3aca9e
--- /dev/null
+++ b/src/plugins/sqldrivers/db2/qsql_db2.cpp
@@ -0,0 +1,1700 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtSql module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsql_db2_p.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>
+#include <QtSql/private/qsqldriver_p.h>
+#include <QtSql/private/qsqlresult_p.h>
+
+#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 QSqlDriverPrivate
+{
+ Q_DECLARE_PUBLIC(QDB2Driver)
+
+public:
+ QDB2DriverPrivate() : QSqlDriverPrivate(), hEnv(0), hDbc(0) { dbmsType = QSqlDriver::DB2; }
+ SQLHANDLE hEnv;
+ SQLHANDLE hDbc;
+ QString user;
+};
+
+class QDB2ResultPrivate;
+
+class QDB2Result: public QSqlResult
+{
+ Q_DECLARE_PRIVATE(QDB2Result)
+
+public:
+ QDB2Result(const QDB2Driver *drv);
+ ~QDB2Result();
+ bool prepare(const QString &query) Q_DECL_OVERRIDE;
+ bool exec() Q_DECL_OVERRIDE;
+ QVariant handle() const Q_DECL_OVERRIDE;
+
+protected:
+ QVariant data(int field) Q_DECL_OVERRIDE;
+ bool reset(const QString &query) Q_DECL_OVERRIDE;
+ bool fetch(int i) Q_DECL_OVERRIDE;
+ bool fetchNext() Q_DECL_OVERRIDE;
+ bool fetchFirst() Q_DECL_OVERRIDE;
+ bool fetchLast() Q_DECL_OVERRIDE;
+ bool isNull(int i) Q_DECL_OVERRIDE;
+ int size() Q_DECL_OVERRIDE;
+ int numRowsAffected() Q_DECL_OVERRIDE;
+ QSqlRecord record() const Q_DECL_OVERRIDE;
+ void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
+ void detachFromResultSet() Q_DECL_OVERRIDE;
+ bool nextResult() Q_DECL_OVERRIDE;
+};
+
+class QDB2ResultPrivate: public QSqlResultPrivate
+{
+ Q_DECLARE_PUBLIC(QDB2Result)
+
+public:
+ Q_DECLARE_SQLDRIVER_PRIVATE(QDB2Driver)
+ QDB2ResultPrivate(QDB2Result *q, const QDB2Driver *drv)
+ : QSqlResultPrivate(q, drv),
+ 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();
+ }
+
+ SQLHANDLE hStmt;
+ QSqlRecord recInf;
+ QVector<QVariant*> valueCache;
+};
+
+static QString qFromTChar(SQLTCHAR* str)
+{
+ return QString((const QChar *)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->drv_d_func()->hEnv) + QLatin1Char(' ')
+ + qWarnDB2Handle(SQL_HANDLE_DBC, d->drv_d_func()->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->drv_d_func()->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
+{
+ Q_D(const QDB2Result);
+ return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
+}
+
+/************************************/
+
+QDB2Result::QDB2Result(const QDB2Driver *drv)
+ : QSqlResult(*new QDB2ResultPrivate(this, drv))
+{
+}
+
+QDB2Result::~QDB2Result()
+{
+ Q_D(const 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);
+ }
+}
+
+bool QDB2Result::reset (const QString& query)
+{
+ Q_D(QDB2Result);
+ 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 = 0;
+ 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)
+{
+ Q_D(QDB2Result);
+ 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()
+{
+ Q_D(QDB2Result);
+ 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[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[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[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[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[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[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[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[bindValueType(i) & 3],
+ SQL_C_WCHAR,
+ SQL_WVARCHAR,
+ len,
+ 0,
+ data,
+ len * sizeof(QChar),
+ ind);
+ }
+ break;
+ }
+ default: {
+ QByteArray ba = values.at(i).toString().toLatin1();
+ int len = ba.length() + 1;
+ if (*ind != SQL_NULL_DATA)
+ *ind = ba.length();
+ r = SQLBindParameter(d->hStmt,
+ i + 1,
+ qParamType[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 = 0;
+ 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((const QChar *)tmpStorage.takeFirst().constData());
+ break;
+ default: {
+ values[i] = QString::fromLatin1(tmpStorage.takeFirst().constData());
+ break; }
+ }
+ if (indicators[i] == SQL_NULL_DATA)
+ values[i] = QVariant(values[i].type());
+ }
+ return true;
+}
+
+bool QDB2Result::fetch(int i)
+{
+ Q_D(QDB2Result);
+ 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()
+{
+ Q_D(QDB2Result);
+ 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()
+{
+ Q_D(QDB2Result);
+ 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()
+{
+ Q_D(QDB2Result);
+ 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)
+{
+ Q_D(QDB2Result);
+ 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:
+ {
+ switch(numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ v = new QVariant(qGetIntData(d->hStmt, field, isNull));
+ break;
+ case QSql::LowPrecisionInt64:
+ v = new QVariant((qint64) 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)
+{
+ Q_D(const QDB2Result);
+ if (i >= d->valueCache.size())
+ return true;
+
+ if (d->valueCache[i])
+ return d->valueCache[i]->isNull();
+ return data(i).isNull();
+}
+
+int QDB2Result::numRowsAffected()
+{
+ Q_D(const QDB2Result);
+ 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
+{
+ Q_D(const QDB2Result);
+ if (isActive())
+ return d->recInf;
+ return QSqlRecord();
+}
+
+bool QDB2Result::nextResult()
+{
+ Q_D(QDB2Result);
+ 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 = 0;
+ 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)
+{
+ QSqlResult::virtual_hook(id, data);
+}
+
+void QDB2Result::detachFromResultSet()
+{
+ Q_D(QDB2Result);
+ if (d->hStmt)
+ SQLCloseCursor(d->hStmt);
+}
+
+/************************************/
+
+QDB2Driver::QDB2Driver(QObject* parent)
+ : QSqlDriver(*new QDB2DriverPrivate, parent)
+{
+}
+
+QDB2Driver::QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent)
+ : QSqlDriver(*new QDB2DriverPrivate, parent)
+{
+ Q_D(QDB2Driver);
+ d->hEnv = reinterpret_cast<intptr_t>(env);
+ d->hDbc = reinterpret_cast<intptr_t>(con);
+ if (env && con) {
+ setOpen(true);
+ setOpenError(false);
+ }
+}
+
+QDB2Driver::~QDB2Driver()
+{
+ close();
+}
+
+bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString& host, int port,
+ const QString& connOpts)
+{
+ Q_D(QDB2Driver);
+ 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, reinterpret_cast<SQLPOINTER>(v), 0);
+ } else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast<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()
+{
+ Q_D(QDB2Driver);
+ 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);
+}
+
+QSqlRecord QDB2Driver::record(const QString& tableName) const
+{
+ Q_D(const QDB2Driver);
+ 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
+{
+ Q_D(const QDB2Driver);
+ 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
+{
+ Q_D(const QDB2Driver);
+ 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:
+ case CancelQuery:
+ 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()
+{
+ Q_D(QDB2Driver);
+ 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()
+{
+ Q_D(QDB2Driver);
+ 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)
+{
+ Q_D(QDB2Driver);
+ SQLUINTEGER ac = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
+ SQLRETURN r = SQLSetConnectAttr(d->hDbc,
+ SQL_ATTR_AUTOCOMMIT,
+ reinterpret_cast<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
+{
+ Q_D(const QDB2Driver);
+ 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