summaryrefslogtreecommitdiffstats
path: root/src/sql/drivers/ibase
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/sql/drivers/ibase
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/sql/drivers/ibase')
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.cpp1886
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.h131
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.pri11
3 files changed, 2028 insertions, 0 deletions
diff --git a/src/sql/drivers/ibase/qsql_ibase.cpp b/src/sql/drivers/ibase/qsql_ibase.cpp
new file mode 100644
index 0000000000..be8f49bf42
--- /dev/null
+++ b/src/sql/drivers/ibase/qsql_ibase.cpp
@@ -0,0 +1,1886 @@
+/****************************************************************************
+**
+** 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$
+** 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_ibase.h"
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qvariant.h>
+#include <qsqlerror.h>
+#include <qsqlfield.h>
+#include <qsqlindex.h>
+#include <qsqlquery.h>
+#include <qlist.h>
+#include <qvector.h>
+#include <qtextcodec.h>
+#include <qmutex.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+#include <qdebug.h>
+#include <QVarLengthArray>
+
+QT_BEGIN_NAMESPACE
+
+#define FBVERSION SQL_DIALECT_V6
+
+#ifndef SQLDA_CURRENT_VERSION
+#define SQLDA_CURRENT_VERSION SQLDA_VERSION1
+#endif
+
+enum { QIBaseChunkSize = SHRT_MAX / 2 };
+
+#if defined(FB_API_VER) && FB_API_VER >= 20
+static bool getIBaseError(QString& msg, const ISC_STATUS* status, ISC_LONG &sqlcode, QTextCodec *tc)
+#else
+static bool getIBaseError(QString& msg, ISC_STATUS* status, ISC_LONG &sqlcode, QTextCodec *tc)
+#endif
+{
+ if (status[0] != 1 || status[1] <= 0)
+ return false;
+
+ msg.clear();
+ sqlcode = isc_sqlcode(status);
+ char buf[512];
+#if defined(FB_API_VER) && FB_API_VER >= 20
+ while(fb_interpret(buf, 512, &status)) {
+#else
+ while(isc_interprete(buf, &status)) {
+#endif
+ if(!msg.isEmpty())
+ msg += QLatin1String(" - ");
+ if (tc)
+ msg += tc->toUnicode(buf);
+ else
+ msg += QString::fromUtf8(buf);
+ }
+ return true;
+}
+
+static void createDA(XSQLDA *&sqlda)
+{
+ sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1));
+ if (sqlda == (XSQLDA*)0) return;
+ sqlda->sqln = 1;
+ sqlda->sqld = 0;
+ sqlda->version = SQLDA_CURRENT_VERSION;
+ sqlda->sqlvar[0].sqlind = 0;
+ sqlda->sqlvar[0].sqldata = 0;
+}
+
+static void enlargeDA(XSQLDA *&sqlda, int n)
+{
+ if (sqlda != (XSQLDA*)0)
+ free(sqlda);
+ sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
+ if (sqlda == (XSQLDA*)0) return;
+ sqlda->sqln = n;
+ sqlda->version = SQLDA_CURRENT_VERSION;
+}
+
+static void initDA(XSQLDA *sqlda)
+{
+ for (int i = 0; i < sqlda->sqld; ++i) {
+ switch (sqlda->sqlvar[i].sqltype & ~1) {
+ case SQL_INT64:
+ case SQL_LONG:
+ case SQL_SHORT:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ case SQL_TIMESTAMP:
+ case SQL_TYPE_TIME:
+ case SQL_TYPE_DATE:
+ case SQL_TEXT:
+ case SQL_BLOB:
+ sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen];
+ break;
+ case SQL_ARRAY:
+ sqlda->sqlvar[i].sqldata = new char[sizeof(ISC_QUAD)];
+ memset(sqlda->sqlvar[i].sqldata, 0, sizeof(ISC_QUAD));
+ break;
+ case SQL_VARYING:
+ sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen + sizeof(short)];
+ break;
+ default:
+ // not supported - do not bind.
+ sqlda->sqlvar[i].sqldata = 0;
+ break;
+ }
+ if (sqlda->sqlvar[i].sqltype & 1) {
+ sqlda->sqlvar[i].sqlind = new short[1];
+ *(sqlda->sqlvar[i].sqlind) = 0;
+ } else {
+ sqlda->sqlvar[i].sqlind = 0;
+ }
+ }
+}
+
+static void delDA(XSQLDA *&sqlda)
+{
+ if (!sqlda)
+ return;
+ for (int i = 0; i < sqlda->sqld; ++i) {
+ delete [] sqlda->sqlvar[i].sqlind;
+ delete [] sqlda->sqlvar[i].sqldata;
+ }
+ free(sqlda);
+ sqlda = 0;
+}
+
+static QVariant::Type qIBaseTypeName(int iType, bool hasScale)
+{
+ switch (iType) {
+ case blr_varying:
+ case blr_varying2:
+ case blr_text:
+ case blr_cstring:
+ case blr_cstring2:
+ return QVariant::String;
+ case blr_sql_time:
+ return QVariant::Time;
+ case blr_sql_date:
+ return QVariant::Date;
+ case blr_timestamp:
+ return QVariant::DateTime;
+ case blr_blob:
+ return QVariant::ByteArray;
+ case blr_quad:
+ case blr_short:
+ case blr_long:
+ return (hasScale ? QVariant::Double : QVariant::Int);
+ case blr_int64:
+ return (hasScale ? QVariant::Double : QVariant::LongLong);
+ case blr_float:
+ case blr_d_float:
+ case blr_double:
+ return QVariant::Double;
+ }
+ qWarning("qIBaseTypeName: unknown datatype: %d", iType);
+ return QVariant::Invalid;
+}
+
+static QVariant::Type qIBaseTypeName2(int iType, bool hasScale)
+{
+ switch(iType & ~1) {
+ case SQL_VARYING:
+ case SQL_TEXT:
+ return QVariant::String;
+ case SQL_LONG:
+ case SQL_SHORT:
+ return (hasScale ? QVariant::Double : QVariant::Int);
+ case SQL_INT64:
+ return (hasScale ? QVariant::Double : QVariant::LongLong);
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ return QVariant::Double;
+ case SQL_TIMESTAMP:
+ return QVariant::DateTime;
+ case SQL_TYPE_TIME:
+ return QVariant::Time;
+ case SQL_TYPE_DATE:
+ return QVariant::Date;
+ case SQL_ARRAY:
+ return QVariant::List;
+ case SQL_BLOB:
+ return QVariant::ByteArray;
+ default:
+ return QVariant::Invalid;
+ }
+}
+
+static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt)
+{
+ static const QTime midnight(0, 0, 0, 0);
+ static const QDate basedate(1858, 11, 17);
+ ISC_TIMESTAMP ts;
+ ts.timestamp_time = midnight.msecsTo(dt.time()) * 10;
+ ts.timestamp_date = basedate.daysTo(dt.date());
+ return ts;
+}
+
+static QDateTime fromTimeStamp(char *buffer)
+{
+ static const QDate bd(1858, 11, 17);
+ QTime t;
+ QDate d;
+
+ // have to demangle the structure ourselves because isc_decode_time
+ // strips the msecs
+ t = t.addMSecs(int(((ISC_TIMESTAMP*)buffer)->timestamp_time / 10));
+ d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date));
+
+ return QDateTime(d, t);
+}
+
+static ISC_TIME toTime(const QTime &t)
+{
+ static const QTime midnight(0, 0, 0, 0);
+ return (ISC_TIME)midnight.msecsTo(t) * 10;
+}
+
+static QTime fromTime(char *buffer)
+{
+ QTime t;
+ // have to demangle the structure ourselves because isc_decode_time
+ // strips the msecs
+ t = t.addMSecs(int((*(ISC_TIME*)buffer) / 10));
+
+ return t;
+}
+
+static ISC_DATE toDate(const QDate &t)
+{
+ static const QDate basedate(1858, 11, 17);
+ ISC_DATE date;
+
+ date = basedate.daysTo(t);
+ return date;
+}
+
+static QDate fromDate(char *buffer)
+{
+ static const QDate bd(1858, 11, 17);
+ QDate d;
+
+ // have to demangle the structure ourselves because isc_decode_time
+ // strips the msecs
+ d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date));
+
+ return d;
+}
+
+static QByteArray encodeString(QTextCodec *tc, const QString &str)
+{
+ if (tc)
+ return tc->fromUnicode(str);
+ return str.toUtf8();
+}
+
+struct QIBaseEventBuffer {
+#if defined(FB_API_VER) && FB_API_VER >= 20
+ ISC_UCHAR *eventBuffer;
+ ISC_UCHAR *resultBuffer;
+#else
+ char *eventBuffer;
+ char *resultBuffer;
+#endif
+ ISC_LONG bufferLength;
+ ISC_LONG eventId;
+
+ enum QIBaseSubscriptionState { Starting, Subscribed, Finished };
+ QIBaseSubscriptionState subscriptionState;
+};
+
+class QIBaseDriverPrivate
+{
+public:
+ QIBaseDriverPrivate(QIBaseDriver *d) : q(d), ibase(0), trans(0), tc(0) {}
+
+ bool isError(const char *msg, QSqlError::ErrorType typ = QSqlError::UnknownError)
+ {
+ QString imsg;
+ ISC_LONG sqlcode;
+ if (!getIBaseError(imsg, status, sqlcode, tc))
+ return false;
+
+ q->setLastError(QSqlError(QCoreApplication::translate("QIBaseDriver", msg),
+ imsg, typ, int(sqlcode)));
+ return true;
+ }
+
+public:
+ QIBaseDriver* q;
+ isc_db_handle ibase;
+ isc_tr_handle trans;
+ QTextCodec *tc;
+ ISC_STATUS status[20];
+ QMap<QString, QIBaseEventBuffer*> eventBuffers;
+};
+
+typedef QMap<void *, QIBaseDriver *> QIBaseBufferDriverMap;
+Q_GLOBAL_STATIC(QIBaseBufferDriverMap, qBufferDriverMap)
+Q_GLOBAL_STATIC(QMutex, qMutex);
+
+static void qFreeEventBuffer(QIBaseEventBuffer* eBuffer)
+{
+ qMutex()->lock();
+ qBufferDriverMap()->remove(reinterpret_cast<void *>(eBuffer->resultBuffer));
+ qMutex()->unlock();
+ delete eBuffer;
+}
+
+class QIBaseResultPrivate
+{
+public:
+ QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb);
+ ~QIBaseResultPrivate() { cleanup(); }
+
+ void cleanup();
+ bool isError(const char *msg, QSqlError::ErrorType typ = QSqlError::UnknownError)
+ {
+ QString imsg;
+ ISC_LONG sqlcode;
+ if (!getIBaseError(imsg, status, sqlcode, tc))
+ return false;
+
+ q->setLastError(QSqlError(QCoreApplication::translate("QIBaseResult", msg),
+ imsg, typ, int(sqlcode)));
+ return true;
+ }
+
+ bool transaction();
+ bool commit();
+
+ bool isSelect();
+ QVariant fetchBlob(ISC_QUAD *bId);
+ bool writeBlob(int i, const QByteArray &ba);
+ QVariant fetchArray(int pos, ISC_QUAD *arr);
+ bool writeArray(int i, const QList<QVariant> &list);
+
+public:
+ QIBaseResult *q;
+ const QIBaseDriver *db;
+ ISC_STATUS status[20];
+ isc_tr_handle trans;
+ //indicator whether we have a local transaction or a transaction on driver level
+ bool localTransaction;
+ isc_stmt_handle stmt;
+ isc_db_handle ibase;
+ XSQLDA *sqlda; // output sqlda
+ XSQLDA *inda; // input parameters
+ int queryType;
+ QTextCodec *tc;
+};
+
+
+QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb):
+ q(d), db(ddb), trans(0), stmt(0), ibase(ddb->d->ibase), sqlda(0), inda(0), queryType(-1), tc(ddb->d->tc)
+{
+ localTransaction = (ddb->d->ibase == 0);
+}
+
+void QIBaseResultPrivate::cleanup()
+{
+ commit();
+ if (!localTransaction)
+ trans = 0;
+
+ if (stmt) {
+ isc_dsql_free_statement(status, &stmt, DSQL_drop);
+ stmt = 0;
+ }
+
+ delDA(sqlda);
+ delDA(inda);
+
+ queryType = -1;
+ q->cleanup();
+}
+
+bool QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba)
+{
+ isc_blob_handle handle = 0;
+ ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata;
+ isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
+ if (!isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to create BLOB"),
+ QSqlError::StatementError)) {
+ int i = 0;
+ while (i < ba.size()) {
+ isc_put_segment(status, &handle, qMin(ba.size() - i, int(QIBaseChunkSize)),
+ const_cast<char*>(ba.data()) + i);
+ if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to write BLOB")))
+ return false;
+ i += qMin(ba.size() - i, int(QIBaseChunkSize));
+ }
+ }
+ isc_close_blob(status, &handle);
+ return true;
+}
+
+QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
+{
+ isc_blob_handle handle = 0;
+
+ isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
+ if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to open BLOB"),
+ QSqlError::StatementError))
+ return QVariant();
+
+ unsigned short len = 0;
+ QByteArray ba;
+ int chunkSize = QIBaseChunkSize;
+ ba.resize(chunkSize);
+ int read = 0;
+ while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) {
+ read += len;
+ ba.resize(read + chunkSize);
+ }
+ ba.resize(read);
+
+ bool isErr = (status[1] == isc_segstr_eof ? false :
+ isError(QT_TRANSLATE_NOOP("QIBaseResult",
+ "Unable to read BLOB"),
+ QSqlError::StatementError));
+
+ isc_close_blob(status, &handle);
+
+ if (isErr)
+ return QVariant();
+
+ ba.resize(read);
+ return ba;
+}
+
+template<typename T>
+static QList<QVariant> toList(char** buf, int count, T* = 0)
+{
+ QList<QVariant> res;
+ for (int i = 0; i < count; ++i) {
+ res.append(*(T*)(*buf));
+ *buf += sizeof(T);
+ }
+ return res;
+}
+/* char** ? seems like bad influence from oracle ... */
+template<>
+QList<QVariant> toList<long>(char** buf, int count, long*)
+{
+ QList<QVariant> res;
+ for (int i = 0; i < count; ++i) {
+ if (sizeof(int) == sizeof(long))
+ res.append(int((*(long*)(*buf))));
+ else
+ res.append((qint64)(*(long*)(*buf)));
+ *buf += sizeof(long);
+ }
+ return res;
+}
+
+static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim,
+ short* numElements, ISC_ARRAY_DESC *arrayDesc,
+ QTextCodec *tc)
+{
+ const short dim = arrayDesc->array_desc_dimensions - 1;
+ const unsigned char dataType = arrayDesc->array_desc_dtype;
+ QList<QVariant> valList;
+ unsigned short strLen = arrayDesc->array_desc_length;
+
+ if (curDim != dim) {
+ for(int i = 0; i < numElements[curDim]; ++i)
+ buffer = readArrayBuffer(list, buffer, curDim + 1, numElements,
+ arrayDesc, tc);
+ } else {
+ switch(dataType) {
+ case blr_varying:
+ case blr_varying2:
+ strLen += 2; // for the two terminating null values
+ case blr_text:
+ case blr_text2: {
+ int o;
+ for (int i = 0; i < numElements[dim]; ++i) {
+ for(o = 0; o < strLen && buffer[o]!=0; ++o )
+ ;
+
+ if (tc)
+ valList.append(tc->toUnicode(buffer, o));
+ else
+ valList.append(QString::fromUtf8(buffer, o));
+
+ buffer += strLen;
+ }
+ break; }
+ case blr_long:
+ valList = toList<long>(&buffer, numElements[dim], static_cast<long *>(0));
+ break;
+ case blr_short:
+ valList = toList<short>(&buffer, numElements[dim]);
+ break;
+ case blr_int64:
+ valList = toList<qint64>(&buffer, numElements[dim]);
+ break;
+ case blr_float:
+ valList = toList<float>(&buffer, numElements[dim]);
+ break;
+ case blr_double:
+ valList = toList<double>(&buffer, numElements[dim]);
+ break;
+ case blr_timestamp:
+ for(int i = 0; i < numElements[dim]; ++i) {
+ valList.append(fromTimeStamp(buffer));
+ buffer += sizeof(ISC_TIMESTAMP);
+ }
+ break;
+ case blr_sql_time:
+ for(int i = 0; i < numElements[dim]; ++i) {
+ valList.append(fromTime(buffer));
+ buffer += sizeof(ISC_TIME);
+ }
+ break;
+ case blr_sql_date:
+ for(int i = 0; i < numElements[dim]; ++i) {
+ valList.append(fromDate(buffer));
+ buffer += sizeof(ISC_DATE);
+ }
+ break;
+ }
+ }
+ if (dim > 0)
+ list.append(valList);
+ else
+ list += valList;
+ return buffer;
+}
+
+QVariant QIBaseResultPrivate::fetchArray(int pos, ISC_QUAD *arr)
+{
+ QList<QVariant> list;
+ ISC_ARRAY_DESC desc;
+
+ if (!arr)
+ return list;
+
+ QByteArray relname(sqlda->sqlvar[pos].relname, sqlda->sqlvar[pos].relname_length);
+ QByteArray sqlname(sqlda->sqlvar[pos].aliasname, sqlda->sqlvar[pos].aliasname_length);
+
+ isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
+ if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"),
+ QSqlError::StatementError))
+ return list;
+
+
+ int arraySize = 1, subArraySize;
+ short dimensions = desc.array_desc_dimensions;
+ QVarLengthArray<short> numElements(dimensions);
+
+ for(int i = 0; i < dimensions; ++i) {
+ subArraySize = (desc.array_desc_bounds[i].array_bound_upper -
+ desc.array_desc_bounds[i].array_bound_lower + 1);
+ numElements[i] = subArraySize;
+ arraySize = subArraySize * arraySize;
+ }
+
+ ISC_LONG bufLen;
+ QByteArray ba;
+ /* varying arrayelements are stored with 2 trailing null bytes
+ indicating the length of the string
+ */
+ if (desc.array_desc_dtype == blr_varying
+ || desc.array_desc_dtype == blr_varying2) {
+ desc.array_desc_length += 2;
+ bufLen = desc.array_desc_length * arraySize * sizeof(short);
+ } else {
+ bufLen = desc.array_desc_length * arraySize;
+ }
+
+
+ ba.resize(int(bufLen));
+ isc_array_get_slice(status, &ibase, &trans, arr, &desc, ba.data(), &bufLen);
+ if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get array data"),
+ QSqlError::StatementError))
+ return list;
+
+ readArrayBuffer(list, ba.data(), 0, numElements.data(), &desc, tc);
+
+ return QVariant(list);
+}
+
+template<typename T>
+static char* fillList(char *buffer, const QList<QVariant> &list, T* = 0)
+{
+ for (int i = 0; i < list.size(); ++i) {
+ T val;
+ val = qvariant_cast<T>(list.at(i));
+ memcpy(buffer, &val, sizeof(T));
+ buffer += sizeof(T);
+ }
+ return buffer;
+}
+
+template<>
+char* fillList<float>(char *buffer, const QList<QVariant> &list, float*)
+{
+ for (int i = 0; i < list.size(); ++i) {
+ double val;
+ float val2 = 0;
+ val = qvariant_cast<double>(list.at(i));
+ val2 = (float)val;
+ memcpy(buffer, &val2, sizeof(float));
+ buffer += sizeof(float);
+ }
+ return buffer;
+}
+
+static char* qFillBufferWithString(char *buffer, const QString& string,
+ short buflen, bool varying, bool array,
+ QTextCodec *tc)
+{
+ QByteArray str = encodeString(tc, string); // keep a copy of the string alive in this scope
+ if (varying) {
+ short tmpBuflen = buflen;
+ if (str.length() < buflen)
+ buflen = str.length();
+ if (array) { // interbase stores varying arrayelements different than normal varying elements
+ memcpy(buffer, str.data(), buflen);
+ memset(buffer + buflen, 0, tmpBuflen - buflen);
+ } else {
+ *(short*)buffer = buflen; // first two bytes is the length
+ memcpy(buffer + sizeof(short), str.data(), buflen);
+ }
+ buffer += tmpBuflen;
+ } else {
+ str = str.leftJustified(buflen, ' ', true);
+ memcpy(buffer, str.data(), buflen);
+ buffer += buflen;
+ }
+ return buffer;
+}
+
+static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
+ QVariant::Type type, short curDim, ISC_ARRAY_DESC *arrayDesc,
+ QString& error, QTextCodec *tc)
+{
+ int i;
+ ISC_ARRAY_BOUND *bounds = arrayDesc->array_desc_bounds;
+ short dim = arrayDesc->array_desc_dimensions - 1;
+
+ int elements = (bounds[curDim].array_bound_upper -
+ bounds[curDim].array_bound_lower + 1);
+
+ if (list.size() != elements) { // size mismatch
+ error = QLatin1String("Expected size: %1. Supplied size: %2");
+ error = QLatin1String("Array size mismatch. Fieldname: %1 ")
+ + error.arg(elements).arg(list.size());
+ return 0;
+ }
+
+ if (curDim != dim) {
+ for(i = 0; i < list.size(); ++i) {
+
+ if (list.at(i).type() != QVariant::List) { // dimensions mismatch
+ error = QLatin1String("Array dimensons mismatch. Fieldname: %1");
+ return 0;
+ }
+
+ buffer = createArrayBuffer(buffer, list.at(i).toList(), type, curDim + 1,
+ arrayDesc, error, tc);
+ if (!buffer)
+ return 0;
+ }
+ } else {
+ switch(type) {
+ case QVariant::Int:
+ case QVariant::UInt:
+ if (arrayDesc->array_desc_dtype == blr_short)
+ buffer = fillList<short>(buffer, list);
+ else
+ buffer = fillList<int>(buffer, list);
+ break;
+ case QVariant::Double:
+ if (arrayDesc->array_desc_dtype == blr_float)
+ buffer = fillList<float>(buffer, list, static_cast<float *>(0));
+ else
+ buffer = fillList<double>(buffer, list);
+ break;
+ case QVariant::LongLong:
+ buffer = fillList<qint64>(buffer, list);
+ break;
+ case QVariant::ULongLong:
+ buffer = fillList<quint64>(buffer, list);
+ break;
+ case QVariant::String:
+ for (i = 0; i < list.size(); ++i)
+ buffer = qFillBufferWithString(buffer, list.at(i).toString(),
+ arrayDesc->array_desc_length,
+ arrayDesc->array_desc_dtype == blr_varying,
+ true, tc);
+ break;
+ case QVariant::Date:
+ for (i = 0; i < list.size(); ++i) {
+ *((ISC_DATE*)buffer) = toDate(list.at(i).toDate());
+ buffer += sizeof(ISC_DATE);
+ }
+ break;
+ case QVariant::Time:
+ for (i = 0; i < list.size(); ++i) {
+ *((ISC_TIME*)buffer) = toTime(list.at(i).toTime());
+ buffer += sizeof(ISC_TIME);
+ }
+ break;
+
+ case QVariant::DateTime:
+ for (i = 0; i < list.size(); ++i) {
+ *((ISC_TIMESTAMP*)buffer) = toTimeStamp(list.at(i).toDateTime());
+ buffer += sizeof(ISC_TIMESTAMP);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return buffer;
+}
+
+bool QIBaseResultPrivate::writeArray(int column, const QList<QVariant> &list)
+{
+ QString error;
+ ISC_QUAD *arrayId = (ISC_QUAD*) inda->sqlvar[column].sqldata;
+ ISC_ARRAY_DESC desc;
+
+ QByteArray relname(inda->sqlvar[column].relname, inda->sqlvar[column].relname_length);
+ QByteArray sqlname(inda->sqlvar[column].aliasname, inda->sqlvar[column].aliasname_length);
+
+ isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
+ if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"),
+ QSqlError::StatementError))
+ return false;
+
+ short arraySize = 1;
+ ISC_LONG bufLen;
+ QList<QVariant> subList = list;
+
+ short dimensions = desc.array_desc_dimensions;
+ for(int i = 0; i < dimensions; ++i) {
+ arraySize *= (desc.array_desc_bounds[i].array_bound_upper -
+ desc.array_desc_bounds[i].array_bound_lower + 1);
+ }
+
+ /* varying arrayelements are stored with 2 trailing null bytes
+ indicating the length of the string
+ */
+ if (desc.array_desc_dtype == blr_varying ||
+ desc.array_desc_dtype == blr_varying2)
+ desc.array_desc_length += 2;
+
+ bufLen = desc.array_desc_length * arraySize;
+ QByteArray ba;
+ ba.resize(int(bufLen));
+
+ if (list.size() > arraySize) {
+ error = QLatin1String("Array size missmatch: size of %1 is %2, size of provided list is %3");
+ error = error.arg(QLatin1String(sqlname)).arg(arraySize).arg(list.size());
+ q->setLastError(QSqlError(error, QLatin1String(""), QSqlError::StatementError));
+ return false;
+ }
+
+ if (!createArrayBuffer(ba.data(), list,
+ qIBaseTypeName(desc.array_desc_dtype, inda->sqlvar[column].sqlscale < 0),
+ 0, &desc, error, tc)) {
+ q->setLastError(QSqlError(error.arg(QLatin1String(sqlname)), QLatin1String(""),
+ QSqlError::StatementError));
+ return false;
+ }
+
+ /* readjust the buffer size*/
+ if (desc.array_desc_dtype == blr_varying
+ || desc.array_desc_dtype == blr_varying2)
+ desc.array_desc_length -= 2;
+
+ isc_array_put_slice(status, &ibase, &trans, arrayId, &desc, ba.data(), &bufLen);
+ return true;
+}
+
+
+bool QIBaseResultPrivate::isSelect()
+{
+ char acBuffer[9];
+ char qType = isc_info_sql_stmt_type;
+ isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer);
+ if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get query info"),
+ QSqlError::StatementError))
+ return false;
+ int iLength = isc_vax_integer(&acBuffer[1], 2);
+ queryType = isc_vax_integer(&acBuffer[3], iLength);
+ return (queryType == isc_info_sql_stmt_select || queryType == isc_info_sql_stmt_exec_procedure);
+}
+
+bool QIBaseResultPrivate::transaction()
+{
+ if (trans)
+ return true;
+ if (db->d->trans) {
+ localTransaction = false;
+ trans = db->d->trans;
+ return true;
+ }
+ localTransaction = true;
+
+ isc_start_transaction(status, &trans, 1, &ibase, 0, NULL);
+ if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not start transaction"),
+ QSqlError::TransactionError))
+ return false;
+
+ return true;
+}
+
+// does nothing if the transaction is on the
+// driver level
+bool QIBaseResultPrivate::commit()
+{
+ if (!trans)
+ return false;
+ // don't commit driver's transaction, the driver will do it for us
+ if (!localTransaction)
+ return true;
+
+ isc_commit_transaction(status, &trans);
+ trans = 0;
+ return !isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to commit transaction"),
+ QSqlError::TransactionError);
+}
+
+//////////
+
+QIBaseResult::QIBaseResult(const QIBaseDriver* db):
+ QSqlCachedResult(db)
+{
+ d = new QIBaseResultPrivate(this, db);
+}
+
+QIBaseResult::~QIBaseResult()
+{
+ delete d;
+}
+
+bool QIBaseResult::prepare(const QString& query)
+{
+// qDebug("prepare: %s", qPrintable(query));
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return false;
+ d->cleanup();
+ setActive(false);
+ setAt(QSql::BeforeFirstRow);
+
+ createDA(d->sqlda);
+ if (d->sqlda == (XSQLDA*)0) {
+ qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory";
+ return false;
+ }
+
+ createDA(d->inda);
+ if (d->inda == (XSQLDA*)0){
+ qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory";
+ return false;
+ }
+
+ if (!d->transaction())
+ return false;
+
+ isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not allocate statement"),
+ QSqlError::StatementError))
+ return false;
+ isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0,
+ const_cast<char*>(encodeString(d->tc, query).constData()), FBVERSION, d->sqlda);
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not prepare statement"),
+ QSqlError::StatementError))
+ return false;
+
+ isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda);
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult",
+ "Could not describe input statement"), QSqlError::StatementError))
+ return false;
+ if (d->inda->sqld > d->inda->sqln) {
+ enlargeDA(d->inda, d->inda->sqld);
+ if (d->inda == (XSQLDA*)0) {
+ qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory";
+ return false;
+ }
+
+ isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda);
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult",
+ "Could not describe input statement"), QSqlError::StatementError))
+ return false;
+ }
+ initDA(d->inda);
+ if (d->sqlda->sqld > d->sqlda->sqln) {
+ // need more field descriptors
+ enlargeDA(d->sqlda, d->sqlda->sqld);
+ if (d->sqlda == (XSQLDA*)0) {
+ qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory";
+ return false;
+ }
+
+ isc_dsql_describe(d->status, &d->stmt, FBVERSION, d->sqlda);
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not describe statement"),
+ QSqlError::StatementError))
+ return false;
+ }
+ initDA(d->sqlda);
+
+ setSelect(d->isSelect());
+ if (!isSelect()) {
+ free(d->sqlda);
+ d->sqlda = 0;
+ }
+
+ return true;
+}
+
+
+bool QIBaseResult::exec()
+{
+ bool ok = true;
+
+ if (!d->trans)
+ d->transaction();
+
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return false;
+ setActive(false);
+ setAt(QSql::BeforeFirstRow);
+
+ if (d->inda) {
+ QVector<QVariant>& values = boundValues();
+ int i;
+ if (values.count() > d->inda->sqld) {
+ qWarning("QIBaseResult::exec: Parameter mismatch, expected %d, got %d parameters",
+ d->inda->sqld, values.count());
+ return false;
+ }
+ int para = 0;
+ for (i = 0; i < values.count(); ++i) {
+ para = i;
+ if (!d->inda->sqlvar[para].sqldata)
+ // skip unknown datatypes
+ continue;
+ const QVariant val(values[i]);
+ if (d->inda->sqlvar[para].sqltype & 1) {
+ if (val.isNull()) {
+ // set null indicator
+ *(d->inda->sqlvar[para].sqlind) = -1;
+ // and set the value to 0, otherwise it would count as empty string.
+ // it seems to be working with just setting sqlind to -1
+ //*((char*)d->inda->sqlvar[para].sqldata) = 0;
+ continue;
+ }
+ // a value of 0 means non-null.
+ *(d->inda->sqlvar[para].sqlind) = 0;
+ }
+ switch(d->inda->sqlvar[para].sqltype & ~1) {
+ case SQL_INT64:
+ if (d->inda->sqlvar[para].sqlscale < 0)
+ *((qint64*)d->inda->sqlvar[para].sqldata) =
+ (qint64)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
+ else
+ *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
+ break;
+ case SQL_LONG:
+ if (d->inda->sqlvar[para].sqlscale < 0)
+ *((long*)d->inda->sqlvar[para].sqldata) =
+ (long)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
+ else
+ *((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong();
+ break;
+ case SQL_SHORT:
+ if (d->inda->sqlvar[para].sqlscale < 0)
+ *((short*)d->inda->sqlvar[para].sqldata) =
+ (short)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
+ else
+ *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt();
+ break;
+ case SQL_FLOAT:
+ *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble();
+ break;
+ case SQL_DOUBLE:
+ *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble();
+ break;
+ case SQL_TIMESTAMP:
+ *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime());
+ break;
+ case SQL_TYPE_TIME:
+ *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime());
+ break;
+ case SQL_TYPE_DATE:
+ *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate());
+ break;
+ case SQL_VARYING:
+ case SQL_TEXT:
+ qFillBufferWithString(d->inda->sqlvar[para].sqldata, val.toString(),
+ d->inda->sqlvar[para].sqllen,
+ (d->inda->sqlvar[para].sqltype & ~1) == SQL_VARYING, false, d->tc);
+ break;
+ case SQL_BLOB:
+ ok &= d->writeBlob(para, val.toByteArray());
+ break;
+ case SQL_ARRAY:
+ ok &= d->writeArray(para, val.toList());
+ break;
+ default:
+ qWarning("QIBaseResult::exec: Unknown datatype %d",
+ d->inda->sqlvar[para].sqltype & ~1);
+ break;
+ }
+ }
+ }
+
+ if (ok) {
+ if (colCount() && d->queryType != isc_info_sql_stmt_exec_procedure) {
+ isc_dsql_free_statement(d->status, &d->stmt, DSQL_close);
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to close statement")))
+ return false;
+ cleanup();
+ }
+ if (d->queryType == isc_info_sql_stmt_exec_procedure)
+ isc_dsql_execute2(d->status, &d->trans, &d->stmt, FBVERSION, d->inda, d->sqlda);
+ else
+ isc_dsql_execute(d->status, &d->trans, &d->stmt, FBVERSION, d->inda);
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to execute query")))
+ return false;
+
+ // Not all stored procedures necessarily return values.
+ if (d->queryType == isc_info_sql_stmt_exec_procedure && d->sqlda && d->sqlda->sqld == 0)
+ delDA(d->sqlda);
+
+ if (d->sqlda)
+ init(d->sqlda->sqld);
+
+ if (!isSelect())
+ d->commit();
+
+ setActive(true);
+ return true;
+ }
+ return false;
+}
+
+bool QIBaseResult::reset (const QString& query)
+{
+ if (!prepare(query))
+ return false;
+ return exec();
+}
+
+bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
+{
+ ISC_STATUS stat = 0;
+
+ // Stored Procedures are special - they populate our d->sqlda when executing,
+ // so we don't have to call isc_dsql_fetch
+ if (d->queryType == isc_info_sql_stmt_exec_procedure) {
+ // the first "fetch" shall succeed, all consecutive ones will fail since
+ // we only have one row to fetch for stored procedures
+ if (rowIdx != 0)
+ stat = 100;
+ } else {
+ stat = isc_dsql_fetch(d->status, &d->stmt, FBVERSION, d->sqlda);
+ }
+
+ if (stat == 100) {
+ // no more rows
+ setAt(QSql::AfterLastRow);
+ return false;
+ }
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not fetch next item"),
+ QSqlError::StatementError))
+ return false;
+ if (rowIdx < 0) // not interested in actual values
+ return true;
+
+ for (int i = 0; i < d->sqlda->sqld; ++i) {
+ int idx = rowIdx + i;
+ char *buf = d->sqlda->sqlvar[i].sqldata;
+ int size = d->sqlda->sqlvar[i].sqllen;
+ Q_ASSERT(buf);
+
+ if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) {
+ // null value
+ QVariant v;
+ v.convert(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype, d->sqlda->sqlvar[i].sqlscale < 0));
+ if(v.type() == QVariant::Double) {
+ switch(numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ v.convert(QVariant::Int);
+ break;
+ case QSql::LowPrecisionInt64:
+ v.convert(QVariant::LongLong);
+ break;
+ case QSql::HighPrecision:
+ v.convert(QVariant::String);
+ break;
+ }
+ }
+ row[idx] = v;
+ continue;
+ }
+
+ switch(d->sqlda->sqlvar[i].sqltype & ~1) {
+ case SQL_VARYING:
+ // pascal strings - a short with a length information followed by the data
+ if (d->tc)
+ row[idx] = d->tc->toUnicode(buf + sizeof(short), *(short*)buf);
+ else
+ row[idx] = QString::fromUtf8(buf + sizeof(short), *(short*)buf);
+ break;
+ case SQL_INT64:
+ if (d->sqlda->sqlvar[i].sqlscale < 0)
+ row[idx] = *(qint64*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale);
+ else
+ row[idx] = QVariant(*(qint64*)buf);
+ break;
+ case SQL_LONG:
+ if (d->sqlda->sqlvar[i].sqllen == 4)
+ if (d->sqlda->sqlvar[i].sqlscale < 0)
+ row[idx] = QVariant(*(qint32*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale));
+ else
+ row[idx] = QVariant(*(qint32*)buf);
+ else
+ row[idx] = QVariant(*(qint64*)buf);
+ break;
+ case SQL_SHORT:
+ if (d->sqlda->sqlvar[i].sqlscale < 0)
+ row[idx] = QVariant(long((*(short*)buf)) * pow(10.0, d->sqlda->sqlvar[i].sqlscale));
+ else
+ row[idx] = QVariant(int((*(short*)buf)));
+ break;
+ case SQL_FLOAT:
+ row[idx] = QVariant(double((*(float*)buf)));
+ break;
+ case SQL_DOUBLE:
+ row[idx] = QVariant(*(double*)buf);
+ break;
+ case SQL_TIMESTAMP:
+ row[idx] = fromTimeStamp(buf);
+ break;
+ case SQL_TYPE_TIME:
+ row[idx] = fromTime(buf);
+ break;
+ case SQL_TYPE_DATE:
+ row[idx] = fromDate(buf);
+ break;
+ case SQL_TEXT:
+ if (d->tc)
+ row[idx] = d->tc->toUnicode(buf, size);
+ else
+ row[idx] = QString::fromUtf8(buf, size);
+ break;
+ case SQL_BLOB:
+ row[idx] = d->fetchBlob((ISC_QUAD*)buf);
+ break;
+ case SQL_ARRAY:
+ row[idx] = d->fetchArray(i, (ISC_QUAD*)buf);
+ break;
+ default:
+ // unknown type - don't even try to fetch
+ row[idx] = QVariant();
+ break;
+ }
+ if (d->sqlda->sqlvar[i].sqlscale < 0) {
+ QVariant v = row[idx];
+ switch(numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ if(v.convert(QVariant::Int))
+ row[idx]=v;
+ break;
+ case QSql::LowPrecisionInt64:
+ if(v.convert(QVariant::LongLong))
+ row[idx]=v;
+ break;
+ case QSql::LowPrecisionDouble:
+ if(v.convert(QVariant::Double))
+ row[idx]=v;
+ break;
+ case QSql::HighPrecision:
+ if(v.convert(QVariant::String))
+ row[idx]=v;
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+int QIBaseResult::size()
+{
+ return -1;
+
+#if 0 /// ### FIXME
+ static char sizeInfo[] = {isc_info_sql_records};
+ char buf[64];
+
+ //qDebug() << sizeInfo;
+ if (!isActive() || !isSelect())
+ return -1;
+
+ char ct;
+ short len;
+ int val = 0;
+// while(val == 0) {
+ isc_dsql_sql_info(d->status, &d->stmt, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
+// isc_database_info(d->status, &d->ibase, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
+
+ for(int i = 0; i < 66; ++i)
+ qDebug() << QString::number(buf[i]);
+
+ for (char* c = buf + 3; *c != isc_info_end; /*nothing*/) {
+ ct = *(c++);
+ len = isc_vax_integer(c, 2);
+ c += 2;
+ val = isc_vax_integer(c, len);
+ c += len;
+ qDebug() << "size" << val;
+ if (ct == isc_info_req_select_count)
+ return val;
+ }
+ //qDebug() << "size -1";
+ return -1;
+
+ unsigned int i, result_size;
+ if (buf[0] == isc_info_sql_records) {
+ i = 3;
+ result_size = isc_vax_integer(&buf[1],2);
+ while (buf[i] != isc_info_end && i < result_size) {
+ len = (short)isc_vax_integer(&buf[i+1],2);
+ if (buf[i] == isc_info_req_select_count)
+ return (isc_vax_integer(&buf[i+3],len));
+ i += len+3;
+ }
+ }
+// }
+ return -1;
+#endif
+}
+
+int QIBaseResult::numRowsAffected()
+{
+ static char acCountInfo[] = {isc_info_sql_records};
+ char cCountType;
+
+ switch (d->queryType) {
+ case isc_info_sql_stmt_select:
+ cCountType = isc_info_req_select_count;
+ break;
+ case isc_info_sql_stmt_update:
+ cCountType = isc_info_req_update_count;
+ break;
+ case isc_info_sql_stmt_delete:
+ cCountType = isc_info_req_delete_count;
+ break;
+ case isc_info_sql_stmt_insert:
+ cCountType = isc_info_req_insert_count;
+ break;
+ default:
+ qWarning() << "numRowsAffected: Unknown statement type (" << d->queryType << ")";
+ return -1;
+ }
+
+ char acBuffer[33];
+ int iResult = -1;
+ isc_dsql_sql_info(d->status, &d->stmt, sizeof(acCountInfo), acCountInfo, sizeof(acBuffer), acBuffer);
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get statement info"),
+ QSqlError::StatementError))
+ return -1;
+ for (char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; /*nothing*/) {
+ char cType = *pcBuf++;
+ short sLength = isc_vax_integer (pcBuf, 2);
+ pcBuf += 2;
+ int iValue = isc_vax_integer (pcBuf, sLength);
+ pcBuf += sLength;
+
+ if (cType == cCountType) {
+ iResult = iValue;
+ break;
+ }
+ }
+ return iResult;
+}
+
+QSqlRecord QIBaseResult::record() const
+{
+ QSqlRecord rec;
+ if (!isActive() || !d->sqlda)
+ return rec;
+
+ XSQLVAR v;
+ for (int i = 0; i < d->sqlda->sqld; ++i) {
+ v = d->sqlda->sqlvar[i];
+ QSqlField f(QString::fromLatin1(v.aliasname, v.aliasname_length).simplified(),
+ qIBaseTypeName2(v.sqltype, v.sqlscale < 0));
+ f.setLength(v.sqllen);
+ f.setPrecision(qAbs(v.sqlscale));
+ f.setRequiredStatus((v.sqltype & 1) == 0 ? QSqlField::Required : QSqlField::Optional);
+ if(v.sqlscale < 0) {
+ QSqlQuery q(new QIBaseResult(d->db));
+ q.setForwardOnly(true);
+ q.exec(QLatin1String("select b.RDB$FIELD_PRECISION, b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, a.RDB$NULL_FLAG "
+ "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
+ "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
+ "AND a.RDB$RELATION_NAME = '") + QString::fromAscii(v.relname, v.relname_length).toUpper() + QLatin1String("' "
+ "AND a.RDB$FIELD_NAME = '") + QString::fromAscii(v.sqlname, v.sqlname_length).toUpper() + QLatin1String("' "));
+ if(q.first()) {
+ if(v.sqlscale < 0) {
+ f.setLength(q.value(0).toInt());
+ f.setPrecision(qAbs(q.value(1).toInt()));
+ } else {
+ f.setLength(q.value(2).toInt());
+ f.setPrecision(0);
+ }
+ f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional);
+ }
+ }
+ f.setSqlType(v.sqltype);
+ rec.append(f);
+ }
+ return rec;
+}
+
+QVariant QIBaseResult::handle() const
+{
+ return QVariant(qRegisterMetaType<isc_stmt_handle>("isc_stmt_handle"), &d->stmt);
+}
+
+/*********************************/
+
+QIBaseDriver::QIBaseDriver(QObject * parent)
+ : QSqlDriver(parent)
+{
+ d = new QIBaseDriverPrivate(this);
+}
+
+QIBaseDriver::QIBaseDriver(isc_db_handle connection, QObject *parent)
+ : QSqlDriver(parent)
+{
+ d = new QIBaseDriverPrivate(this);
+ d->ibase = connection;
+ setOpen(true);
+ setOpenError(false);
+}
+
+QIBaseDriver::~QIBaseDriver()
+{
+ delete d;
+}
+
+bool QIBaseDriver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case QuerySize:
+ case NamedPlaceholders:
+ case LastInsertId:
+ case BatchOperations:
+ case SimpleLocking:
+ case FinishQuery:
+ case MultipleResultSets:
+ return false;
+ case Transactions:
+ case PreparedQueries:
+ case PositionalPlaceholders:
+ case Unicode:
+ case BLOB:
+ case EventNotifications:
+ case LowPrecisionNumbers:
+ return true;
+ }
+ return false;
+}
+
+bool QIBaseDriver::open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int /*port*/,
+ const QString & connOpts)
+{
+ if (isOpen())
+ close();
+
+ const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
+
+ QString encString;
+ QByteArray role;
+ for (int i = 0; i < opts.count(); ++i) {
+ QString tmp(opts.at(i).simplified());
+ int idx;
+ if ((idx = tmp.indexOf(QLatin1Char('='))) != -1) {
+ QString val = tmp.mid(idx + 1).simplified();
+ QString opt = tmp.left(idx).simplified();
+ if (opt.toUpper() == QLatin1String("ISC_DPB_LC_CTYPE"))
+ encString = val;
+ else if (opt.toUpper() == QLatin1String("ISC_DPB_SQL_ROLE_NAME")) {
+ role = val.toLocal8Bit();
+ role.truncate(255);
+ }
+ }
+ }
+
+ // Use UNICODE_FSS when no ISC_DPB_LC_CTYPE is provided
+ if (encString.isEmpty())
+ encString = QLatin1String("UNICODE_FSS");
+ else {
+ d->tc = QTextCodec::codecForName(encString.toLocal8Bit());
+ if (!d->tc) {
+ qWarning("Unsupported encoding: %s. Using UNICODE_FFS for ISC_DPB_LC_CTYPE.", encString.toLocal8Bit().constData());
+ encString = QLatin1String("UNICODE_FSS"); // Fallback to UNICODE_FSS
+ }
+ }
+
+ QByteArray enc = encString.toLocal8Bit();
+ QByteArray usr = user.toLocal8Bit();
+ QByteArray pass = password.toLocal8Bit();
+ enc.truncate(255);
+ usr.truncate(255);
+ pass.truncate(255);
+
+ QByteArray ba;
+ ba.resize(usr.length() + pass.length() + enc.length() + role.length() + 6);
+ int i = -1;
+ ba[++i] = isc_dpb_version1;
+ ba[++i] = isc_dpb_user_name;
+ ba[++i] = usr.length();
+ memcpy(ba.data() + ++i, usr.data(), usr.length());
+ i += usr.length();
+ ba[i] = isc_dpb_password;
+ ba[++i] = pass.length();
+ memcpy(ba.data() + ++i, pass.data(), pass.length());
+ i += pass.length();
+ ba[i] = isc_dpb_lc_ctype;
+ ba[++i] = enc.length();
+ memcpy(ba.data() + ++i, enc.data(), enc.length());
+ i += enc.length();
+
+ if (!role.isEmpty()) {
+ ba[i] = isc_dpb_sql_role_name;
+ ba[++i] = role.length();
+ memcpy(ba.data() + ++i, role.data(), role.length());
+ i += role.length();
+ }
+
+ QString ldb;
+ if (!host.isEmpty())
+ ldb += host + QLatin1Char(':');
+ ldb += db;
+ isc_attach_database(d->status, 0, const_cast<char *>(ldb.toLocal8Bit().constData()),
+ &d->ibase, i, ba.data());
+ if (d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Error opening database"),
+ QSqlError::ConnectionError)) {
+ setOpenError(true);
+ return false;
+ }
+
+ setOpen(true);
+ return true;
+}
+
+void QIBaseDriver::close()
+{
+ if (isOpen()) {
+
+ if (d->eventBuffers.size()) {
+ ISC_STATUS status[20];
+ QMap<QString, QIBaseEventBuffer *>::const_iterator i;
+ for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
+ QIBaseEventBuffer *eBuffer = i.value();
+ eBuffer->subscriptionState = QIBaseEventBuffer::Finished;
+ isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
+ qFreeEventBuffer(eBuffer);
+ }
+ d->eventBuffers.clear();
+
+#if defined(FB_API_VER)
+ // Workaround for Firebird crash
+ QTime timer;
+ timer.start();
+ while (timer.elapsed() < 500)
+ QCoreApplication::processEvents();
+#endif
+ }
+
+ isc_detach_database(d->status, &d->ibase);
+ d->ibase = 0;
+ setOpen(false);
+ setOpenError(false);
+ }
+}
+
+QSqlResult *QIBaseDriver::createResult() const
+{
+ return new QIBaseResult(this);
+}
+
+bool QIBaseDriver::beginTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return false;
+ if (d->trans)
+ return false;
+
+ isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL);
+ return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Could not start transaction"),
+ QSqlError::TransactionError);
+}
+
+bool QIBaseDriver::commitTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return false;
+ if (!d->trans)
+ return false;
+
+ isc_commit_transaction(d->status, &d->trans);
+ d->trans = 0;
+ return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Unable to commit transaction"),
+ QSqlError::TransactionError);
+}
+
+bool QIBaseDriver::rollbackTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return false;
+ if (!d->trans)
+ return false;
+
+ isc_rollback_transaction(d->status, &d->trans);
+ d->trans = 0;
+ return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Unable to rollback transaction"),
+ QSqlError::TransactionError);
+}
+
+QStringList QIBaseDriver::tables(QSql::TableType type) const
+{
+ QStringList res;
+ if (!isOpen())
+ return res;
+
+ QString typeFilter;
+
+ if (type == QSql::SystemTables) {
+ typeFilter += QLatin1String("RDB$SYSTEM_FLAG != 0");
+ } else if (type == (QSql::SystemTables | QSql::Views)) {
+ typeFilter += QLatin1String("RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL");
+ } else {
+ if (!(type & QSql::SystemTables))
+ typeFilter += QLatin1String("RDB$SYSTEM_FLAG = 0 AND ");
+ if (!(type & QSql::Views))
+ typeFilter += QLatin1String("RDB$VIEW_BLR IS NULL AND ");
+ if (!(type & QSql::Tables))
+ typeFilter += QLatin1String("RDB$VIEW_BLR IS NOT NULL AND ");
+ if (!typeFilter.isEmpty())
+ typeFilter.chop(5);
+ }
+ if (!typeFilter.isEmpty())
+ typeFilter.prepend(QLatin1String("where "));
+
+ QSqlQuery q(createResult());
+ q.setForwardOnly(true);
+ if (!q.exec(QLatin1String("select rdb$relation_name from rdb$relations ") + typeFilter))
+ return res;
+ while(q.next())
+ res << q.value(0).toString().simplified();
+
+ return res;
+}
+
+QSqlRecord QIBaseDriver::record(const QString& tablename) const
+{
+ QSqlRecord rec;
+ if (!isOpen())
+ return rec;
+
+ QSqlQuery q(createResult());
+ q.setForwardOnly(true);
+ QString table = tablename;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ else
+ table = table.toUpper();
+ q.exec(QLatin1String("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
+ "b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
+ "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
+ "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
+ "AND a.RDB$RELATION_NAME = '") + table + QLatin1String("' "
+ "ORDER BY a.RDB$FIELD_POSITION"));
+
+ while (q.next()) {
+ int type = q.value(1).toInt();
+ bool hasScale = q.value(3).toInt() < 0;
+ QSqlField f(q.value(0).toString().simplified(), qIBaseTypeName(type, hasScale));
+ if(hasScale) {
+ f.setLength(q.value(4).toInt());
+ f.setPrecision(qAbs(q.value(3).toInt()));
+ } else {
+ f.setLength(q.value(2).toInt());
+ f.setPrecision(0);
+ }
+ f.setRequired(q.value(5).toInt() > 0 ? true : false);
+ f.setSqlType(type);
+
+ rec.append(f);
+ }
+ return rec;
+}
+
+QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
+{
+ QSqlIndex index(table);
+ if (!isOpen())
+ return index;
+
+ QString tablename = table;
+ if (isIdentifierEscaped(tablename, QSqlDriver::TableName))
+ tablename = stripDelimiters(tablename, QSqlDriver::TableName);
+ else
+ tablename = tablename.toUpper();
+
+ QSqlQuery q(createResult());
+ q.setForwardOnly(true);
+ q.exec(QLatin1String("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
+ "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
+ "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
+ "AND a.RDB$RELATION_NAME = '") + tablename +
+ QLatin1String(" 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
+ "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
+ "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
+ "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
+ "ORDER BY b.RDB$FIELD_POSITION"));
+
+ while (q.next()) {
+ QSqlField field(q.value(1).toString().simplified(), qIBaseTypeName(q.value(2).toInt(), q.value(3).toInt() < 0));
+ index.append(field); //TODO: asc? desc?
+ index.setName(q.value(0).toString());
+ }
+
+ return index;
+}
+
+QString QIBaseDriver::formatValue(const QSqlField &field, bool trimStrings) const
+{
+ switch (field.type()) {
+ case QVariant::DateTime: {
+ QDateTime datetime = field.value().toDateTime();
+ if (datetime.isValid())
+ return QLatin1Char('\'') + QString::number(datetime.date().year()) + QLatin1Char('-') +
+ QString::number(datetime.date().month()) + QLatin1Char('-') +
+ QString::number(datetime.date().day()) + QLatin1Char(' ') +
+ QString::number(datetime.time().hour()) + QLatin1Char(':') +
+ QString::number(datetime.time().minute()) + QLatin1Char(':') +
+ QString::number(datetime.time().second()) + QLatin1Char('.') +
+ QString::number(datetime.time().msec()).rightJustified(3, QLatin1Char('0'), true) +
+ QLatin1Char('\'');
+ else
+ return QLatin1String("NULL");
+ }
+ case QVariant::Time: {
+ QTime time = field.value().toTime();
+ if (time.isValid())
+ return QLatin1Char('\'') + QString::number(time.hour()) + QLatin1Char(':') +
+ QString::number(time.minute()) + QLatin1Char(':') +
+ QString::number(time.second()) + QLatin1Char('.') +
+ QString::number(time.msec()).rightJustified(3, QLatin1Char('0'), true) +
+ QLatin1Char('\'');
+ else
+ return QLatin1String("NULL");
+ }
+ case QVariant::Date: {
+ QDate date = field.value().toDate();
+ if (date.isValid())
+ return QLatin1Char('\'') + QString::number(date.year()) + QLatin1Char('-') +
+ QString::number(date.month()) + QLatin1Char('-') +
+ QString::number(date.day()) + QLatin1Char('\'');
+ else
+ return QLatin1String("NULL");
+ }
+ default:
+ return QSqlDriver::formatValue(field, trimStrings);
+ }
+}
+
+QVariant QIBaseDriver::handle() const
+{
+ return QVariant(qRegisterMetaType<isc_db_handle>("isc_db_handle"), &d->ibase);
+}
+
+#if defined(FB_API_VER) && FB_API_VER >= 20
+static ISC_EVENT_CALLBACK qEventCallback(char *result, ISC_USHORT length, const ISC_UCHAR *updated)
+#else
+static isc_callback qEventCallback(char *result, short length, char *updated)
+#endif
+{
+ if (!updated)
+ return 0;
+
+
+ memcpy(result, updated, length);
+ qMutex()->lock();
+ QIBaseDriver *driver = qBufferDriverMap()->value(result);
+ qMutex()->unlock();
+
+ // We use an asynchronous call (i.e., queued connection) because the event callback
+ // is executed in a different thread than the one in which the driver lives.
+ if (driver)
+ QMetaObject::invokeMethod(driver, "qHandleEventNotification", Qt::QueuedConnection, Q_ARG(void *, reinterpret_cast<void *>(result)));
+
+ return 0;
+}
+
+bool QIBaseDriver::subscribeToNotificationImplementation(const QString &name)
+{
+ if (!isOpen()) {
+ qWarning("QIBaseDriver::subscribeFromNotificationImplementation: database not open.");
+ return false;
+ }
+
+ if (d->eventBuffers.contains(name)) {
+ qWarning("QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%s'.",
+ qPrintable(name));
+ return false;
+ }
+
+ QIBaseEventBuffer *eBuffer = new QIBaseEventBuffer;
+ eBuffer->subscriptionState = QIBaseEventBuffer::Starting;
+ eBuffer->bufferLength = isc_event_block(&eBuffer->eventBuffer,
+ &eBuffer->resultBuffer,
+ 1,
+ name.toLocal8Bit().constData());
+
+ qMutex()->lock();
+ qBufferDriverMap()->insert(eBuffer->resultBuffer, this);
+ qMutex()->unlock();
+
+ d->eventBuffers.insert(name, eBuffer);
+
+ ISC_STATUS status[20];
+ isc_que_events(status,
+ &d->ibase,
+ &eBuffer->eventId,
+ eBuffer->bufferLength,
+ eBuffer->eventBuffer,
+#if defined (FB_API_VER) && FB_API_VER >= 20
+ (ISC_EVENT_CALLBACK)qEventCallback,
+#else
+ (isc_callback)qEventCallback,
+#endif
+ eBuffer->resultBuffer);
+
+ if (status[0] == 1 && status[1]) {
+ setLastError(QSqlError(QString::fromLatin1("Could not subscribe to event notifications for %1.").arg(name)));
+ d->eventBuffers.remove(name);
+ qFreeEventBuffer(eBuffer);
+ return false;
+ }
+
+ return true;
+}
+
+bool QIBaseDriver::unsubscribeFromNotificationImplementation(const QString &name)
+{
+ if (!isOpen()) {
+ qWarning("QIBaseDriver::unsubscribeFromNotificationImplementation: database not open.");
+ return false;
+ }
+
+ if (!d->eventBuffers.contains(name)) {
+ qWarning("QIBaseDriver::QIBaseSubscriptionState not subscribed to '%s'.",
+ qPrintable(name));
+ return false;
+ }
+
+ QIBaseEventBuffer *eBuffer = d->eventBuffers.value(name);
+ ISC_STATUS status[20];
+ eBuffer->subscriptionState = QIBaseEventBuffer::Finished;
+ isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
+
+ if (status[0] == 1 && status[1]) {
+ setLastError(QSqlError(QString::fromLatin1("Could not unsubscribe from event notifications for %1.").arg(name)));
+ return false;
+ }
+
+ d->eventBuffers.remove(name);
+ qFreeEventBuffer(eBuffer);
+
+ return true;
+}
+
+QStringList QIBaseDriver::subscribedToNotificationsImplementation() const
+{
+ return QStringList(d->eventBuffers.keys());
+}
+
+void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer)
+{
+ QMap<QString, QIBaseEventBuffer *>::const_iterator i;
+ for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
+ QIBaseEventBuffer* eBuffer = i.value();
+ if (reinterpret_cast<void *>(eBuffer->resultBuffer) != updatedResultBuffer)
+ continue;
+
+ ISC_ULONG counts[20];
+ memset(counts, 0, sizeof(counts));
+ isc_event_counts(counts, eBuffer->bufferLength, eBuffer->eventBuffer, eBuffer->resultBuffer);
+ if (counts[0]) {
+
+ if (eBuffer->subscriptionState == QIBaseEventBuffer::Subscribed)
+ emit notification(i.key());
+ else if (eBuffer->subscriptionState == QIBaseEventBuffer::Starting)
+ eBuffer->subscriptionState = QIBaseEventBuffer::Subscribed;
+
+ ISC_STATUS status[20];
+ isc_que_events(status,
+ &d->ibase,
+ &eBuffer->eventId,
+ eBuffer->bufferLength,
+ eBuffer->eventBuffer,
+#if defined (FB_API_VER) && FB_API_VER >= 20
+ (ISC_EVENT_CALLBACK)qEventCallback,
+#else
+ (isc_callback)qEventCallback,
+#endif
+ eBuffer->resultBuffer);
+ if (status[0] == 1 && status[1]) {
+ qCritical("QIBaseDriver::qHandleEventNotification: could not resubscribe to '%s'",
+ qPrintable(i.key()));
+ }
+
+ return;
+ }
+ }
+}
+
+QString QIBaseDriver::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
diff --git a/src/sql/drivers/ibase/qsql_ibase.h b/src/sql/drivers/ibase/qsql_ibase.h
new file mode 100644
index 0000000000..8949c07e89
--- /dev/null
+++ b/src/sql/drivers/ibase/qsql_ibase.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** 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$
+** 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$
+**
+****************************************************************************/
+
+#ifndef QSQL_IBASE_H
+#define QSQL_IBASE_H
+
+#include <QtSql/qsqlresult.h>
+#include <QtSql/qsqldriver.h>
+#include <QtSql/private/qsqlcachedresult_p.h>
+#include <ibase.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+class QIBaseDriverPrivate;
+class QIBaseResultPrivate;
+class QIBaseDriver;
+
+class QIBaseResult : public QSqlCachedResult
+{
+ friend class QIBaseResultPrivate;
+
+public:
+ explicit QIBaseResult(const QIBaseDriver* db);
+ virtual ~QIBaseResult();
+
+ bool prepare(const QString& query);
+ bool exec();
+ QVariant handle() const;
+
+protected:
+ bool gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx);
+ bool reset (const QString& query);
+ int size();
+ int numRowsAffected();
+ QSqlRecord record() const;
+
+private:
+ QIBaseResultPrivate* d;
+};
+
+class QIBaseDriver : public QSqlDriver
+{
+ Q_OBJECT
+ friend class QIBaseDriverPrivate;
+ friend class QIBaseResultPrivate;
+public:
+ explicit QIBaseDriver(QObject *parent = 0);
+ explicit QIBaseDriver(isc_db_handle connection, QObject *parent = 0);
+ virtual ~QIBaseDriver();
+ bool hasFeature(DriverFeature f) const;
+ bool open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port,
+ const QString & connOpts);
+ bool open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port) { return open (db, user, password, host, port, QString()); }
+ void close();
+ QSqlResult *createResult() const;
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+ QStringList tables(QSql::TableType) const;
+
+ QSqlRecord record(const QString& tablename) const;
+ QSqlIndex primaryIndex(const QString &table) const;
+
+ QString formatValue(const QSqlField &field, bool trimStrings) const;
+ QVariant handle() const;
+
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const;
+
+protected Q_SLOTS:
+ bool subscribeToNotificationImplementation(const QString &name);
+ bool unsubscribeFromNotificationImplementation(const QString &name);
+ QStringList subscribedToNotificationsImplementation() const;
+
+private Q_SLOTS:
+ void qHandleEventNotification(void* updatedResultBuffer);
+
+private:
+ QIBaseDriverPrivate* d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+#endif // QSQL_IBASE_H
diff --git a/src/sql/drivers/ibase/qsql_ibase.pri b/src/sql/drivers/ibase/qsql_ibase.pri
new file mode 100644
index 0000000000..33fbb0de30
--- /dev/null
+++ b/src/sql/drivers/ibase/qsql_ibase.pri
@@ -0,0 +1,11 @@
+HEADERS += $$PWD/qsql_ibase.h
+SOURCES += $$PWD/qsql_ibase.cpp
+
+unix {
+ !contains(LIBS, .*gds.*):!contains(LIBS, .*libfb.*):LIBS += -lgds
+} else {
+ !contains(LIBS, .*gds.*):!contains(LIBS, .*fbclient.*) {
+ win32-borland:LIBS += gds32.lib
+ else:LIBS += -lgds32_ms
+ }
+}