summaryrefslogtreecommitdiffstats
path: root/src/sql/drivers/ibase/qsql_ibase.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@theqtcompany.com>2016-03-18 13:35:40 +0100
committerLars Knoll <lars.knoll@theqtcompany.com>2016-04-07 09:08:53 +0000
commit0452c25aedb7d2778e5b26fad924ed61b72bbe33 (patch)
tree7abfae2cc219e549089f0aead346c9b159decbaa /src/sql/drivers/ibase/qsql_ibase.cpp
parent00ca784be60cb38dcb342755f602edc929805ce4 (diff)
Always compile sql drivers as plugins
Compiling the drivers into Qt Sql does not make a lot of sense anymore, as we handle plugins well enough in the build system these days. [ChangeLog][Build system] SQL drivers are now always compiled as plugins. Change-Id: I364b82a480849399d1fafe4b20e9f08922569260 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> Reviewed-by: Mark Brand <mabrand@mabrand.nl>
Diffstat (limited to 'src/sql/drivers/ibase/qsql_ibase.cpp')
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.cpp1948
1 files changed, 0 insertions, 1948 deletions
diff --git a/src/sql/drivers/ibase/qsql_ibase.cpp b/src/sql/drivers/ibase/qsql_ibase.cpp
deleted file mode 100644
index 6fd91b6b76..0000000000
--- a/src/sql/drivers/ibase/qsql_ibase.cpp
+++ /dev/null
@@ -1,1948 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsql_ibase_p.h"
-#include <qcoreapplication.h>
-#include <qdatetime.h>
-#include <qvariant.h>
-#include <qsqlerror.h>
-#include <qsqlfield.h>
-#include <qsqlindex.h>
-#include <qsqlquery.h>
-#include <QtSql/private/qsqlcachedresult_p.h>
-#include <QtSql/private/qsqldriver_p.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(0, 0);
- 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(0, 0);
- // 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 QSqlDriverPrivate
-{
- Q_DECLARE_PUBLIC(QIBaseDriver)
-public:
- QIBaseDriverPrivate() : QSqlDriverPrivate(), ibase(0), trans(0), tc(0) { dbmsType = QSqlDriver::Interbase; }
-
- bool isError(const char *msg, QSqlError::ErrorType typ = QSqlError::UnknownError)
- {
- Q_Q(QIBaseDriver);
- 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:
- 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;
-
-class QIBaseResult : public QSqlCachedResult
-{
- Q_DECLARE_PRIVATE(QIBaseResult)
-
-public:
- explicit QIBaseResult(const QIBaseDriver* db);
-
- bool prepare(const QString &query) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
-
-protected:
- bool gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) Q_DECL_OVERRIDE;
- bool reset (const QString &query) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
-};
-
-class QIBaseResultPrivate: public QSqlCachedResultPrivate
-{
- Q_DECLARE_PUBLIC(QIBaseResult)
-
-public:
- Q_DECLARE_SQLDRIVER_PRIVATE(QIBaseDriver)
-
- QIBaseResultPrivate(QIBaseResult *q, const QIBaseDriver *drv);
- ~QIBaseResultPrivate() { cleanup(); }
-
- void cleanup();
- bool isError(const char *msg, QSqlError::ErrorType typ = QSqlError::UnknownError)
- {
- Q_Q(QIBaseResult);
- 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:
- 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 *q, const QIBaseDriver *drv)
- : QSqlCachedResultPrivate(q, drv),
- trans(0),
- localTransaction(!drv_d_func()->ibase),
- stmt(0),
- ibase(drv_d_func()->ibase),
- sqlda(0),
- inda(0),
- queryType(-1),
- tc(drv_d_func()->tc)
-{
-}
-
-void QIBaseResultPrivate::cleanup()
-{
- Q_Q(QIBaseResult);
- 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)
-{
- Q_Q(QIBaseResult);
- 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 (drv_d_func()->trans) {
- localTransaction = false;
- trans = drv_d_func()->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(*new QIBaseResultPrivate(this, db))
-{
-}
-
-bool QIBaseResult::prepare(const QString& query)
-{
- Q_D(QIBaseResult);
-// 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()
-{
- Q_D(QIBaseResult);
- 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].sqllen == 4) {
- if (d->inda->sqlvar[para].sqlscale < 0)
- *((qint32*)d->inda->sqlvar[para].sqldata) =
- (qint32)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
- else
- *((qint32*)d->inda->sqlvar[para].sqldata) = (qint32)val.toInt();
- } else {
- *((qint64*)d->inda->sqlvar[para].sqldata) = 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)
-{
- Q_D(QIBaseResult);
- 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;
- case QSql::LowPrecisionDouble:
- // no conversion
- 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()
-{
- Q_D(QIBaseResult);
- static char acCountInfo[] = {isc_info_sql_records};
- char cCountType;
- bool bIsProcedure = false;
-
- 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;
- case isc_info_sql_stmt_exec_procedure:
- bIsProcedure = true; // will sum all changes
- 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 (bIsProcedure) {
- if (cType == isc_info_req_insert_count || cType == isc_info_req_update_count
- || cType == isc_info_req_delete_count) {
- if (iResult == -1)
- iResult = 0;
- iResult += iValue;
- }
- } else if (cType == cCountType) {
- iResult = iValue;
- break;
- }
- }
- return iResult;
-}
-
-QSqlRecord QIBaseResult::record() const
-{
- Q_D(const QIBaseResult);
- 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(driver()->createResult());
- 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::fromLatin1(v.relname, v.relname_length).toUpper() + QLatin1String("' "
- "AND a.RDB$FIELD_NAME = '") + QString::fromLatin1(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
-{
- Q_D(const QIBaseResult);
- return QVariant(qRegisterMetaType<isc_stmt_handle>("isc_stmt_handle"), &d->stmt);
-}
-
-/*********************************/
-
-QIBaseDriver::QIBaseDriver(QObject * parent)
- : QSqlDriver(*new QIBaseDriverPrivate, parent)
-{
-}
-
-QIBaseDriver::QIBaseDriver(isc_db_handle connection, QObject *parent)
- : QSqlDriver(*new QIBaseDriverPrivate, parent)
-{
- Q_D(QIBaseDriver);
- d->ibase = connection;
- setOpen(true);
- setOpenError(false);
-}
-
-QIBaseDriver::~QIBaseDriver()
-{
-}
-
-bool QIBaseDriver::hasFeature(DriverFeature f) const
-{
- switch (f) {
- case QuerySize:
- case NamedPlaceholders:
- case LastInsertId:
- case BatchOperations:
- case SimpleLocking:
- case FinishQuery:
- case MultipleResultSets:
- case CancelQuery:
- 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)
-{
- Q_D(QIBaseDriver);
- 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.reserve(usr.length() + pass.length() + enc.length() + role.length() + 9);
- ba.append(char(isc_dpb_version1));
- ba.append(char(isc_dpb_user_name));
- ba.append(char(usr.length()));
- ba.append(usr.data(), usr.length());
- ba.append(char(isc_dpb_password));
- ba.append(char(pass.length()));
- ba.append(pass.data(), pass.length());
- ba.append(char(isc_dpb_lc_ctype));
- ba.append(char(enc.length()));
- ba.append(enc.data(), enc.length());
-
- if (!role.isEmpty()) {
- ba.append(char(isc_dpb_sql_role_name));
- ba.append(char(role.length()));
- ba.append(role.data(), role.length());
- }
-
- QString portString;
- if (port != -1)
- portString = QStringLiteral("/%1").arg(port);
-
- QString ldb;
- if (!host.isEmpty())
- ldb += host + portString + QLatin1Char(':');
- ldb += db;
- isc_attach_database(d->status, 0, const_cast<char *>(ldb.toLocal8Bit().constData()),
- &d->ibase, ba.size(), ba.data());
- if (d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Error opening database"),
- QSqlError::ConnectionError)) {
- setOpenError(true);
- return false;
- }
-
- setOpen(true);
- setOpenError(false);
- return true;
-}
-
-void QIBaseDriver::close()
-{
- Q_D(QIBaseDriver);
- 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()
-{
- Q_D(QIBaseDriver);
- 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()
-{
- Q_D(QIBaseDriver);
- 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()
-{
- Q_D(QIBaseDriver);
- 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);
- 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
-{
- Q_D(const QIBaseDriver);
- 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::subscribeToNotification(const QString &name)
-{
- Q_D(QIBaseDriver);
- 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::unsubscribeFromNotification(const QString &name)
-{
- Q_D(QIBaseDriver);
- 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::subscribedToNotifications() const
-{
- Q_D(const QIBaseDriver);
- return QStringList(d->eventBuffers.keys());
-}
-
-void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer)
-{
- Q_D(QIBaseDriver);
- 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());
- emit notification(i.key(), QSqlDriver::UnknownSource, QVariant());
- }
- 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 (Q_UNLIKELY(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