blob: 1cd782dbcbb54e8b25319c674156fc6dfa7d40ee [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $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((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->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((const QChar *)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:
{
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