src/sql/drivers/ibase/qsql_ibase.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtSql module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qsql_ibase.h"
       
    43 #include <qcoreapplication.h>
       
    44 #include <qdatetime.h>
       
    45 #include <qvariant.h>
       
    46 #include <qsqlerror.h>
       
    47 #include <qsqlfield.h>
       
    48 #include <qsqlindex.h>
       
    49 #include <qsqlquery.h>
       
    50 #include <qlist.h>
       
    51 #include <qvector.h>
       
    52 #include <qtextcodec.h>
       
    53 #include <qmutex.h>
       
    54 #include <stdlib.h>
       
    55 #include <limits.h>
       
    56 #include <math.h>
       
    57 #include <qdebug.h>
       
    58 #include <QVarLengthArray>
       
    59 
       
    60 QT_BEGIN_NAMESPACE
       
    61 
       
    62 #define FBVERSION SQL_DIALECT_V6
       
    63 
       
    64 #ifndef SQLDA_CURRENT_VERSION
       
    65 #define SQLDA_CURRENT_VERSION SQLDA_VERSION1
       
    66 #endif
       
    67 
       
    68 enum { QIBaseChunkSize = SHRT_MAX / 2 };
       
    69 
       
    70 #if defined(FB_API_VER) && FB_API_VER >= 20
       
    71 static bool getIBaseError(QString& msg, const ISC_STATUS* status, ISC_LONG &sqlcode, QTextCodec *tc)
       
    72 #else
       
    73 static bool getIBaseError(QString& msg, ISC_STATUS* status, ISC_LONG &sqlcode, QTextCodec *tc)
       
    74 #endif
       
    75 {
       
    76     if (status[0] != 1 || status[1] <= 0)
       
    77         return false;
       
    78 
       
    79     msg.clear();
       
    80     sqlcode = isc_sqlcode(status);
       
    81     char buf[512];
       
    82 #if defined(FB_API_VER) && FB_API_VER >= 20
       
    83     while(fb_interpret(buf, 512, &status)) {
       
    84 #else
       
    85     while(isc_interprete(buf, &status)) {
       
    86 #endif
       
    87         if(!msg.isEmpty())
       
    88             msg += QLatin1String(" - ");
       
    89         if (tc)
       
    90             msg += tc->toUnicode(buf);
       
    91         else
       
    92             msg += QString::fromUtf8(buf);
       
    93     }
       
    94     return true;
       
    95 }
       
    96 
       
    97 static void createDA(XSQLDA *&sqlda)
       
    98 {
       
    99     sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1));
       
   100     if (sqlda == (XSQLDA*)0) return;
       
   101     sqlda->sqln = 1;
       
   102     sqlda->sqld = 0;
       
   103     sqlda->version = SQLDA_CURRENT_VERSION;
       
   104     sqlda->sqlvar[0].sqlind = 0;
       
   105     sqlda->sqlvar[0].sqldata = 0;
       
   106 }
       
   107 
       
   108 static void enlargeDA(XSQLDA *&sqlda, int n)
       
   109 {
       
   110     if (sqlda != (XSQLDA*)0)
       
   111         free(sqlda);
       
   112     sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
       
   113     if (sqlda == (XSQLDA*)0) return;
       
   114     sqlda->sqln = n;
       
   115     sqlda->version = SQLDA_CURRENT_VERSION;
       
   116 }
       
   117 
       
   118 static void initDA(XSQLDA *sqlda)
       
   119 {
       
   120     for (int i = 0; i < sqlda->sqld; ++i) {
       
   121         switch (sqlda->sqlvar[i].sqltype & ~1) {
       
   122         case SQL_INT64:
       
   123         case SQL_LONG:
       
   124         case SQL_SHORT:
       
   125         case SQL_FLOAT:
       
   126         case SQL_DOUBLE:
       
   127         case SQL_TIMESTAMP:
       
   128         case SQL_TYPE_TIME:
       
   129         case SQL_TYPE_DATE:
       
   130         case SQL_TEXT:
       
   131         case SQL_BLOB:
       
   132             sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen];
       
   133             break;
       
   134         case SQL_ARRAY:
       
   135             sqlda->sqlvar[i].sqldata = new char[sizeof(ISC_QUAD)];
       
   136             memset(sqlda->sqlvar[i].sqldata, 0, sizeof(ISC_QUAD));
       
   137             break;
       
   138         case SQL_VARYING:
       
   139             sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen + sizeof(short)];
       
   140             break;
       
   141         default:
       
   142             // not supported - do not bind.
       
   143             sqlda->sqlvar[i].sqldata = 0;
       
   144             break;
       
   145         }
       
   146         if (sqlda->sqlvar[i].sqltype & 1) {
       
   147             sqlda->sqlvar[i].sqlind = new short[1];
       
   148             *(sqlda->sqlvar[i].sqlind) = 0;
       
   149         } else {
       
   150             sqlda->sqlvar[i].sqlind = 0;
       
   151         }
       
   152     }
       
   153 }
       
   154 
       
   155 static void delDA(XSQLDA *&sqlda)
       
   156 {
       
   157     if (!sqlda)
       
   158         return;
       
   159     for (int i = 0; i < sqlda->sqld; ++i) {
       
   160         delete [] sqlda->sqlvar[i].sqlind;
       
   161         delete [] sqlda->sqlvar[i].sqldata;
       
   162     }
       
   163     free(sqlda);
       
   164     sqlda = 0;
       
   165 }
       
   166 
       
   167 static QVariant::Type qIBaseTypeName(int iType, bool hasScale)
       
   168 {
       
   169     switch (iType) {
       
   170     case blr_varying:
       
   171     case blr_varying2:
       
   172     case blr_text:
       
   173     case blr_cstring:
       
   174     case blr_cstring2:
       
   175         return QVariant::String;
       
   176     case blr_sql_time:
       
   177         return QVariant::Time;
       
   178     case blr_sql_date:
       
   179         return QVariant::Date;
       
   180     case blr_timestamp:
       
   181         return QVariant::DateTime;
       
   182     case blr_blob:
       
   183         return QVariant::ByteArray;
       
   184     case blr_quad:
       
   185     case blr_short:
       
   186     case blr_long:
       
   187         return (hasScale ? QVariant::Double : QVariant::Int);
       
   188     case blr_int64:
       
   189         return (hasScale ? QVariant::Double : QVariant::LongLong);
       
   190     case blr_float:
       
   191     case blr_d_float:
       
   192     case blr_double:
       
   193         return QVariant::Double;
       
   194     }
       
   195     qWarning("qIBaseTypeName: unknown datatype: %d", iType);
       
   196     return QVariant::Invalid;
       
   197 }
       
   198 
       
   199 static QVariant::Type qIBaseTypeName2(int iType, bool hasScale)
       
   200 {
       
   201     switch(iType & ~1) {
       
   202     case SQL_VARYING:
       
   203     case SQL_TEXT:
       
   204         return QVariant::String;
       
   205     case SQL_LONG:
       
   206     case SQL_SHORT:
       
   207         return (hasScale ? QVariant::Double : QVariant::Int);
       
   208     case SQL_INT64:
       
   209         return (hasScale ? QVariant::Double : QVariant::LongLong);
       
   210     case SQL_FLOAT:
       
   211     case SQL_DOUBLE:
       
   212         return QVariant::Double;
       
   213     case SQL_TIMESTAMP:
       
   214         return QVariant::DateTime;
       
   215     case SQL_TYPE_TIME:
       
   216         return QVariant::Time;
       
   217     case SQL_TYPE_DATE:
       
   218         return QVariant::Date;
       
   219     case SQL_ARRAY:
       
   220         return QVariant::List;
       
   221     case SQL_BLOB:
       
   222         return QVariant::ByteArray;
       
   223     default:
       
   224         return QVariant::Invalid;
       
   225     }
       
   226 }
       
   227 
       
   228 static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt)
       
   229 {
       
   230     static const QTime midnight(0, 0, 0, 0);
       
   231     static const QDate basedate(1858, 11, 17);
       
   232     ISC_TIMESTAMP ts;
       
   233     ts.timestamp_time = midnight.msecsTo(dt.time()) * 10;
       
   234     ts.timestamp_date = basedate.daysTo(dt.date());
       
   235     return ts;
       
   236 }
       
   237 
       
   238 static QDateTime fromTimeStamp(char *buffer)
       
   239 {
       
   240     static const QDate bd(1858, 11, 17);
       
   241     QTime t;
       
   242     QDate d;
       
   243 
       
   244     // have to demangle the structure ourselves because isc_decode_time
       
   245     // strips the msecs
       
   246     t = t.addMSecs(int(((ISC_TIMESTAMP*)buffer)->timestamp_time / 10));
       
   247     d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date));
       
   248 
       
   249     return QDateTime(d, t);
       
   250 }
       
   251 
       
   252 static ISC_TIME toTime(const QTime &t)
       
   253 {
       
   254     static const QTime midnight(0, 0, 0, 0);
       
   255     return (ISC_TIME)midnight.msecsTo(t) * 10;
       
   256 }
       
   257 
       
   258 static QTime fromTime(char *buffer)
       
   259 {
       
   260     QTime t;
       
   261     // have to demangle the structure ourselves because isc_decode_time
       
   262     // strips the msecs
       
   263     t = t.addMSecs(int((*(ISC_TIME*)buffer) / 10));
       
   264 
       
   265     return t;
       
   266 }
       
   267 
       
   268 static ISC_DATE toDate(const QDate &t)
       
   269 {
       
   270     static const QDate basedate(1858, 11, 17);
       
   271     ISC_DATE date;
       
   272 
       
   273     date = basedate.daysTo(t);
       
   274     return date;
       
   275 }
       
   276 
       
   277 static QDate fromDate(char *buffer)
       
   278 {
       
   279     static const QDate bd(1858, 11, 17);
       
   280     QDate d;
       
   281 
       
   282     // have to demangle the structure ourselves because isc_decode_time
       
   283     // strips the msecs
       
   284     d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date));
       
   285 
       
   286     return d;
       
   287 }
       
   288 
       
   289 static QByteArray encodeString(QTextCodec *tc, const QString &str)
       
   290 {
       
   291     if (tc)
       
   292         return tc->fromUnicode(str);
       
   293     return str.toUtf8();
       
   294 }
       
   295 
       
   296 struct QIBaseEventBuffer {
       
   297 #if defined(FB_API_VER) && FB_API_VER >= 20
       
   298     ISC_UCHAR *eventBuffer;
       
   299     ISC_UCHAR *resultBuffer;
       
   300 #else
       
   301     char *eventBuffer;
       
   302     char *resultBuffer;
       
   303 #endif
       
   304     ISC_LONG bufferLength;
       
   305     ISC_LONG eventId;
       
   306 
       
   307     enum QIBaseSubscriptionState { Starting, Subscribed, Finished };
       
   308     QIBaseSubscriptionState subscriptionState;
       
   309 };
       
   310 
       
   311 class QIBaseDriverPrivate
       
   312 {
       
   313 public:
       
   314     QIBaseDriverPrivate(QIBaseDriver *d) : q(d), ibase(0), trans(0), tc(0) {}
       
   315 
       
   316     bool isError(const char *msg, QSqlError::ErrorType typ = QSqlError::UnknownError)
       
   317     {
       
   318         QString imsg;
       
   319         ISC_LONG sqlcode;
       
   320         if (!getIBaseError(imsg, status, sqlcode, tc))
       
   321             return false;
       
   322 
       
   323         q->setLastError(QSqlError(QCoreApplication::translate("QIBaseDriver", msg),
       
   324                         imsg, typ, int(sqlcode)));
       
   325         return true;
       
   326     }
       
   327 
       
   328 public:
       
   329     QIBaseDriver* q;
       
   330     isc_db_handle ibase;
       
   331     isc_tr_handle trans;
       
   332     QTextCodec *tc;
       
   333     ISC_STATUS status[20];
       
   334     QMap<QString, QIBaseEventBuffer*> eventBuffers;
       
   335 };
       
   336 
       
   337 typedef QMap<void *, QIBaseDriver *> QIBaseBufferDriverMap;
       
   338 Q_GLOBAL_STATIC(QIBaseBufferDriverMap, qBufferDriverMap)
       
   339 Q_GLOBAL_STATIC(QMutex, qMutex);
       
   340 
       
   341 static void qFreeEventBuffer(QIBaseEventBuffer* eBuffer)
       
   342 {
       
   343     qMutex()->lock();
       
   344     qBufferDriverMap()->remove(reinterpret_cast<void *>(eBuffer->resultBuffer));
       
   345     qMutex()->unlock();
       
   346     delete eBuffer;
       
   347 }
       
   348 
       
   349 class QIBaseResultPrivate
       
   350 {
       
   351 public:
       
   352     QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb);
       
   353     ~QIBaseResultPrivate() { cleanup(); }
       
   354 
       
   355     void cleanup();
       
   356     bool isError(const char *msg, QSqlError::ErrorType typ = QSqlError::UnknownError)
       
   357     {
       
   358         QString imsg;
       
   359         ISC_LONG sqlcode;
       
   360         if (!getIBaseError(imsg, status, sqlcode, tc))
       
   361             return false;
       
   362 
       
   363         q->setLastError(QSqlError(QCoreApplication::translate("QIBaseResult", msg),
       
   364                         imsg, typ, int(sqlcode)));
       
   365         return true;
       
   366     }
       
   367 
       
   368     bool transaction();
       
   369     bool commit();
       
   370 
       
   371     bool isSelect();
       
   372     QVariant fetchBlob(ISC_QUAD *bId);
       
   373     bool writeBlob(int i, const QByteArray &ba);
       
   374     QVariant fetchArray(int pos, ISC_QUAD *arr);
       
   375     bool writeArray(int i, const QList<QVariant> &list);
       
   376 
       
   377 public:
       
   378     QIBaseResult *q;
       
   379     const QIBaseDriver *db;
       
   380     ISC_STATUS status[20];
       
   381     isc_tr_handle trans;
       
   382     //indicator whether we have a local transaction or a transaction on driver level
       
   383     bool localTransaction;
       
   384     isc_stmt_handle stmt;
       
   385     isc_db_handle ibase;
       
   386     XSQLDA *sqlda; // output sqlda
       
   387     XSQLDA *inda; // input parameters
       
   388     int queryType;
       
   389     QTextCodec *tc;
       
   390 };
       
   391 
       
   392 
       
   393 QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb):
       
   394     q(d), db(ddb), trans(0), stmt(0), ibase(ddb->d->ibase), sqlda(0), inda(0), queryType(-1), tc(ddb->d->tc)
       
   395 {
       
   396     localTransaction = (ddb->d->ibase == 0);
       
   397 }
       
   398 
       
   399 void QIBaseResultPrivate::cleanup()
       
   400 {
       
   401     commit();
       
   402     if (!localTransaction)
       
   403         trans = 0;
       
   404 
       
   405     if (stmt) {
       
   406         isc_dsql_free_statement(status, &stmt, DSQL_drop);
       
   407         stmt = 0;
       
   408     }
       
   409 
       
   410     delDA(sqlda);
       
   411     delDA(inda);
       
   412 
       
   413     queryType = -1;
       
   414     q->cleanup();
       
   415 }
       
   416 
       
   417 bool QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba)
       
   418 {
       
   419     isc_blob_handle handle = 0;
       
   420     ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata;
       
   421     isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
       
   422     if (!isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to create BLOB"),
       
   423                  QSqlError::StatementError)) {
       
   424         int i = 0;
       
   425         while (i < ba.size()) {
       
   426             isc_put_segment(status, &handle, qMin(ba.size() - i, int(QIBaseChunkSize)),
       
   427                             const_cast<char*>(ba.data()) + i);
       
   428             if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to write BLOB")))
       
   429                 return false;
       
   430             i += qMin(ba.size() - i, int(QIBaseChunkSize));
       
   431         }
       
   432     }
       
   433     isc_close_blob(status, &handle);
       
   434     return true;
       
   435 }
       
   436 
       
   437 QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
       
   438 {
       
   439     isc_blob_handle handle = 0;
       
   440 
       
   441     isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
       
   442     if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to open BLOB"),
       
   443                 QSqlError::StatementError))
       
   444         return QVariant();
       
   445 
       
   446     unsigned short len = 0;
       
   447     QByteArray ba;
       
   448     int chunkSize = QIBaseChunkSize;
       
   449     ba.resize(chunkSize);
       
   450     int read = 0;
       
   451     while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) {
       
   452         read += len;
       
   453         ba.resize(read + chunkSize);
       
   454     }
       
   455     ba.resize(read);
       
   456 
       
   457     bool isErr = (status[1] == isc_segstr_eof ? false :
       
   458                     isError(QT_TRANSLATE_NOOP("QIBaseResult",
       
   459                                                 "Unable to read BLOB"),
       
   460                                                 QSqlError::StatementError));
       
   461 
       
   462     isc_close_blob(status, &handle);
       
   463 
       
   464     if (isErr)
       
   465         return QVariant();
       
   466 
       
   467     ba.resize(read);
       
   468     return ba;
       
   469 }
       
   470 
       
   471 template<typename T>
       
   472 static QList<QVariant> toList(char** buf, int count, T* = 0)
       
   473 {
       
   474     QList<QVariant> res;
       
   475     for (int i = 0; i < count; ++i) {
       
   476         res.append(*(T*)(*buf));
       
   477         *buf += sizeof(T);
       
   478     }
       
   479     return res;
       
   480 }
       
   481 /* char** ? seems like bad influence from oracle ... */
       
   482 template<>
       
   483 QList<QVariant> toList<long>(char** buf, int count, long*)
       
   484 {
       
   485     QList<QVariant> res;
       
   486     for (int i = 0; i < count; ++i) {
       
   487         if (sizeof(int) == sizeof(long))
       
   488             res.append(int((*(long*)(*buf))));
       
   489         else
       
   490             res.append((qint64)(*(long*)(*buf)));
       
   491         *buf += sizeof(long);
       
   492     }
       
   493     return res;
       
   494 }
       
   495 
       
   496 static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim,
       
   497                              short* numElements, ISC_ARRAY_DESC *arrayDesc,
       
   498                              QTextCodec *tc)
       
   499 {
       
   500     const short dim = arrayDesc->array_desc_dimensions - 1;
       
   501     const unsigned char dataType = arrayDesc->array_desc_dtype;
       
   502     QList<QVariant> valList;
       
   503     unsigned short strLen = arrayDesc->array_desc_length;
       
   504 
       
   505     if (curDim != dim) {
       
   506         for(int i = 0; i < numElements[curDim]; ++i)
       
   507             buffer = readArrayBuffer(list, buffer, curDim + 1, numElements,
       
   508                                      arrayDesc, tc);
       
   509     } else {
       
   510         switch(dataType) {
       
   511             case blr_varying:
       
   512             case blr_varying2:
       
   513                  strLen += 2; // for the two terminating null values
       
   514             case blr_text:
       
   515             case blr_text2: {
       
   516                 int o;
       
   517                 for (int i = 0; i < numElements[dim]; ++i) {
       
   518                     for(o = 0; o < strLen && buffer[o]!=0; ++o )
       
   519                         ;
       
   520 
       
   521                     if (tc)
       
   522                         valList.append(tc->toUnicode(buffer, o));
       
   523                     else
       
   524                         valList.append(QString::fromUtf8(buffer, o));
       
   525 
       
   526                     buffer += strLen;
       
   527                 }
       
   528                 break; }
       
   529             case blr_long:
       
   530                 valList = toList<long>(&buffer, numElements[dim], static_cast<long *>(0));
       
   531                 break;
       
   532             case blr_short:
       
   533                 valList = toList<short>(&buffer, numElements[dim]);
       
   534                 break;
       
   535             case blr_int64:
       
   536                 valList = toList<qint64>(&buffer, numElements[dim]);
       
   537                 break;
       
   538             case blr_float:
       
   539                 valList = toList<float>(&buffer, numElements[dim]);
       
   540                 break;
       
   541             case blr_double:
       
   542                 valList = toList<double>(&buffer, numElements[dim]);
       
   543                 break;
       
   544             case blr_timestamp:
       
   545                 for(int i = 0; i < numElements[dim]; ++i) {
       
   546                     valList.append(fromTimeStamp(buffer));
       
   547                     buffer += sizeof(ISC_TIMESTAMP);
       
   548                 }
       
   549                 break;
       
   550             case blr_sql_time:
       
   551                 for(int i = 0; i < numElements[dim]; ++i) {
       
   552                     valList.append(fromTime(buffer));
       
   553                     buffer += sizeof(ISC_TIME);
       
   554                 }
       
   555                 break;
       
   556             case blr_sql_date:
       
   557                 for(int i = 0; i < numElements[dim]; ++i) {
       
   558                     valList.append(fromDate(buffer));
       
   559                     buffer += sizeof(ISC_DATE);
       
   560                 }
       
   561                 break;
       
   562         }
       
   563     }
       
   564     if (dim > 0)
       
   565         list.append(valList);
       
   566     else
       
   567         list += valList;
       
   568     return buffer;
       
   569 }
       
   570 
       
   571 QVariant QIBaseResultPrivate::fetchArray(int pos, ISC_QUAD *arr)
       
   572 {
       
   573     QList<QVariant> list;
       
   574     ISC_ARRAY_DESC desc;
       
   575 
       
   576     if (!arr)
       
   577         return list;
       
   578 
       
   579     QByteArray relname(sqlda->sqlvar[pos].relname, sqlda->sqlvar[pos].relname_length);
       
   580     QByteArray sqlname(sqlda->sqlvar[pos].aliasname, sqlda->sqlvar[pos].aliasname_length);
       
   581 
       
   582     isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
       
   583     if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"),
       
   584                 QSqlError::StatementError))
       
   585         return list;
       
   586 
       
   587 
       
   588     int arraySize = 1, subArraySize;
       
   589     short dimensions = desc.array_desc_dimensions;
       
   590     QVarLengthArray<short> numElements(dimensions);
       
   591 
       
   592     for(int i = 0; i < dimensions; ++i) {
       
   593         subArraySize = (desc.array_desc_bounds[i].array_bound_upper -
       
   594                       desc.array_desc_bounds[i].array_bound_lower + 1);
       
   595         numElements[i] = subArraySize;
       
   596         arraySize = subArraySize * arraySize;
       
   597     }
       
   598 
       
   599     ISC_LONG bufLen;
       
   600     QByteArray ba;
       
   601     /* varying arrayelements are stored with 2 trailing null bytes
       
   602        indicating the length of the string
       
   603      */
       
   604     if (desc.array_desc_dtype == blr_varying
       
   605         || desc.array_desc_dtype == blr_varying2) {
       
   606         desc.array_desc_length += 2;
       
   607         bufLen = desc.array_desc_length * arraySize * sizeof(short);
       
   608     } else {
       
   609         bufLen = desc.array_desc_length *  arraySize;
       
   610     }
       
   611 
       
   612 
       
   613     ba.resize(int(bufLen));
       
   614     isc_array_get_slice(status, &ibase, &trans, arr, &desc, ba.data(), &bufLen);
       
   615     if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get array data"),
       
   616                 QSqlError::StatementError))
       
   617         return list;
       
   618 
       
   619     readArrayBuffer(list, ba.data(), 0, numElements.data(), &desc, tc);
       
   620 
       
   621     return QVariant(list);
       
   622 }
       
   623 
       
   624 template<typename T>
       
   625 static char* fillList(char *buffer, const QList<QVariant> &list, T* = 0)
       
   626 {
       
   627     for (int i = 0; i < list.size(); ++i) {
       
   628         T val;
       
   629         val = qvariant_cast<T>(list.at(i));
       
   630         memcpy(buffer, &val, sizeof(T));
       
   631         buffer += sizeof(T);
       
   632     }
       
   633     return buffer;
       
   634 }
       
   635 
       
   636 template<>
       
   637 char* fillList<float>(char *buffer, const QList<QVariant> &list, float*)
       
   638 {
       
   639     for (int i = 0; i < list.size(); ++i) {
       
   640         double val;
       
   641         float val2 = 0;
       
   642         val = qvariant_cast<double>(list.at(i));
       
   643         val2 = (float)val;
       
   644         memcpy(buffer, &val2, sizeof(float));
       
   645         buffer += sizeof(float);
       
   646     }
       
   647     return buffer;
       
   648 }
       
   649 
       
   650 static char* qFillBufferWithString(char *buffer, const QString& string,
       
   651                                    short buflen, bool varying, bool array,
       
   652                                    QTextCodec *tc)
       
   653 {
       
   654     QByteArray str = encodeString(tc, string); // keep a copy of the string alive in this scope
       
   655     if (varying) {
       
   656         short tmpBuflen = buflen;
       
   657         if (str.length() < buflen)
       
   658             buflen = str.length();
       
   659         if (array) { // interbase stores varying arrayelements different than normal varying elements
       
   660             memcpy(buffer, str.data(), buflen);
       
   661             memset(buffer + buflen, 0, tmpBuflen - buflen);
       
   662         } else {
       
   663             *(short*)buffer = buflen; // first two bytes is the length
       
   664             memcpy(buffer + sizeof(short), str.data(), buflen);
       
   665         }
       
   666         buffer += tmpBuflen;
       
   667     } else {
       
   668         str = str.leftJustified(buflen, ' ', true);
       
   669         memcpy(buffer, str.data(), buflen);
       
   670         buffer += buflen;
       
   671     }
       
   672     return buffer;
       
   673 }
       
   674 
       
   675 static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
       
   676                                QVariant::Type type, short curDim, ISC_ARRAY_DESC *arrayDesc,
       
   677                                QString& error, QTextCodec *tc)
       
   678 {
       
   679     int i;
       
   680     ISC_ARRAY_BOUND *bounds = arrayDesc->array_desc_bounds;
       
   681     short dim = arrayDesc->array_desc_dimensions - 1;
       
   682 
       
   683     int elements = (bounds[curDim].array_bound_upper -
       
   684                     bounds[curDim].array_bound_lower + 1);
       
   685 
       
   686     if (list.size() != elements) { // size mismatch
       
   687         error = QLatin1String("Expected size: %1. Supplied size: %2");
       
   688         error = QLatin1String("Array size mismatch. Fieldname: %1 ")
       
   689                 + error.arg(elements).arg(list.size());
       
   690         return 0;
       
   691     }
       
   692 
       
   693     if (curDim != dim) {
       
   694         for(i = 0; i < list.size(); ++i) {
       
   695 
       
   696           if (list.at(i).type() != QVariant::List) { // dimensions mismatch
       
   697               error = QLatin1String("Array dimensons mismatch. Fieldname: %1");
       
   698               return 0;
       
   699           }
       
   700 
       
   701           buffer = createArrayBuffer(buffer, list.at(i).toList(), type, curDim + 1,
       
   702                                      arrayDesc, error, tc);
       
   703           if (!buffer)
       
   704               return 0;
       
   705         }
       
   706     } else {
       
   707         switch(type) {
       
   708         case QVariant::Int:
       
   709         case QVariant::UInt:
       
   710             if (arrayDesc->array_desc_dtype == blr_short)
       
   711                 buffer = fillList<short>(buffer, list);
       
   712             else
       
   713                 buffer = fillList<int>(buffer, list);
       
   714             break;
       
   715         case QVariant::Double:
       
   716             if (arrayDesc->array_desc_dtype == blr_float)
       
   717                 buffer = fillList<float>(buffer, list, static_cast<float *>(0));
       
   718             else
       
   719                 buffer = fillList<double>(buffer, list);
       
   720             break;
       
   721         case QVariant::LongLong:
       
   722             buffer = fillList<qint64>(buffer, list);
       
   723             break;
       
   724         case QVariant::ULongLong:
       
   725             buffer = fillList<quint64>(buffer, list);
       
   726             break;
       
   727         case QVariant::String:
       
   728             for (i = 0; i < list.size(); ++i)
       
   729                 buffer = qFillBufferWithString(buffer, list.at(i).toString(),
       
   730                                                arrayDesc->array_desc_length,
       
   731                                                arrayDesc->array_desc_dtype == blr_varying,
       
   732                                                true, tc);
       
   733             break;
       
   734         case QVariant::Date:
       
   735             for (i = 0; i < list.size(); ++i) {
       
   736                 *((ISC_DATE*)buffer) = toDate(list.at(i).toDate());
       
   737                 buffer += sizeof(ISC_DATE);
       
   738             }
       
   739             break;
       
   740         case QVariant::Time:
       
   741             for (i = 0; i < list.size(); ++i) {
       
   742                 *((ISC_TIME*)buffer) = toTime(list.at(i).toTime());
       
   743                 buffer += sizeof(ISC_TIME);
       
   744             }
       
   745             break;
       
   746 
       
   747         case QVariant::DateTime:
       
   748             for (i = 0; i < list.size(); ++i) {
       
   749                 *((ISC_TIMESTAMP*)buffer) = toTimeStamp(list.at(i).toDateTime());
       
   750                 buffer += sizeof(ISC_TIMESTAMP);
       
   751             }
       
   752             break;
       
   753         default:
       
   754             break;
       
   755         }
       
   756     }
       
   757     return buffer;
       
   758 }
       
   759 
       
   760 bool QIBaseResultPrivate::writeArray(int column, const QList<QVariant> &list)
       
   761 {
       
   762     QString error;
       
   763     ISC_QUAD *arrayId = (ISC_QUAD*) inda->sqlvar[column].sqldata;
       
   764     ISC_ARRAY_DESC desc;
       
   765 
       
   766     QByteArray relname(inda->sqlvar[column].relname, inda->sqlvar[column].relname_length);
       
   767     QByteArray sqlname(inda->sqlvar[column].aliasname, inda->sqlvar[column].aliasname_length);
       
   768 
       
   769     isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
       
   770     if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"),
       
   771                 QSqlError::StatementError))
       
   772         return false;
       
   773 
       
   774     short arraySize = 1;
       
   775     ISC_LONG bufLen;
       
   776     QList<QVariant> subList = list;
       
   777 
       
   778     short dimensions = desc.array_desc_dimensions;
       
   779     for(int i = 0; i < dimensions; ++i) {
       
   780         arraySize *= (desc.array_desc_bounds[i].array_bound_upper -
       
   781                       desc.array_desc_bounds[i].array_bound_lower + 1);
       
   782     }
       
   783 
       
   784     /* varying arrayelements are stored with 2 trailing null bytes
       
   785        indicating the length of the string
       
   786      */
       
   787     if (desc.array_desc_dtype == blr_varying ||
       
   788        desc.array_desc_dtype == blr_varying2)
       
   789         desc.array_desc_length += 2;
       
   790 
       
   791     bufLen = desc.array_desc_length * arraySize;
       
   792     QByteArray ba;
       
   793     ba.resize(int(bufLen));
       
   794 
       
   795     if (list.size() > arraySize) {
       
   796         error = QLatin1String("Array size missmatch: size of %1 is %2, size of provided list is %3");
       
   797         error = error.arg(QLatin1String(sqlname)).arg(arraySize).arg(list.size());
       
   798         q->setLastError(QSqlError(error, QLatin1String(""), QSqlError::StatementError));
       
   799         return false;
       
   800     }
       
   801 
       
   802     if (!createArrayBuffer(ba.data(), list,
       
   803                            qIBaseTypeName(desc.array_desc_dtype, inda->sqlvar[column].sqlscale < 0),
       
   804                            0, &desc, error, tc)) {
       
   805         q->setLastError(QSqlError(error.arg(QLatin1String(sqlname)), QLatin1String(""),
       
   806                         QSqlError::StatementError));
       
   807         return false;
       
   808     }
       
   809 
       
   810     /* readjust the buffer size*/
       
   811     if (desc.array_desc_dtype == blr_varying
       
   812         || desc.array_desc_dtype == blr_varying2)
       
   813         desc.array_desc_length -= 2;
       
   814 
       
   815     isc_array_put_slice(status, &ibase, &trans, arrayId, &desc, ba.data(), &bufLen);
       
   816     return true;
       
   817 }
       
   818 
       
   819 
       
   820 bool QIBaseResultPrivate::isSelect()
       
   821 {
       
   822     char acBuffer[9];
       
   823     char qType = isc_info_sql_stmt_type;
       
   824     isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer);
       
   825     if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get query info"),
       
   826                 QSqlError::StatementError))
       
   827         return false;
       
   828     int iLength = isc_vax_integer(&acBuffer[1], 2);
       
   829     queryType = isc_vax_integer(&acBuffer[3], iLength);
       
   830     return (queryType == isc_info_sql_stmt_select || queryType == isc_info_sql_stmt_exec_procedure);
       
   831 }
       
   832 
       
   833 bool QIBaseResultPrivate::transaction()
       
   834 {
       
   835     if (trans)
       
   836         return true;
       
   837     if (db->d->trans) {
       
   838         localTransaction = false;
       
   839         trans = db->d->trans;
       
   840         return true;
       
   841     }
       
   842     localTransaction = true;
       
   843 
       
   844     isc_start_transaction(status, &trans, 1, &ibase, 0, NULL);
       
   845     if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not start transaction"),
       
   846                 QSqlError::TransactionError))
       
   847         return false;
       
   848 
       
   849     return true;
       
   850 }
       
   851 
       
   852 // does nothing if the transaction is on the
       
   853 // driver level
       
   854 bool QIBaseResultPrivate::commit()
       
   855 {
       
   856     if (!trans)
       
   857         return false;
       
   858     // don't commit driver's transaction, the driver will do it for us
       
   859     if (!localTransaction)
       
   860         return true;
       
   861 
       
   862     isc_commit_transaction(status, &trans);
       
   863     trans = 0;
       
   864     return !isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to commit transaction"),
       
   865                     QSqlError::TransactionError);
       
   866 }
       
   867 
       
   868 //////////
       
   869 
       
   870 QIBaseResult::QIBaseResult(const QIBaseDriver* db):
       
   871     QSqlCachedResult(db)
       
   872 {
       
   873     d = new QIBaseResultPrivate(this, db);
       
   874 }
       
   875 
       
   876 QIBaseResult::~QIBaseResult()
       
   877 {
       
   878     delete d;
       
   879 }
       
   880 
       
   881 bool QIBaseResult::prepare(const QString& query)
       
   882 {
       
   883 //     qDebug("prepare: %s", qPrintable(query));
       
   884     if (!driver() || !driver()->isOpen() || driver()->isOpenError())
       
   885         return false;
       
   886     d->cleanup();
       
   887     setActive(false);
       
   888     setAt(QSql::BeforeFirstRow);
       
   889 
       
   890     createDA(d->sqlda);
       
   891     if (d->sqlda == (XSQLDA*)0) {
       
   892         qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory";
       
   893         return false;
       
   894     }
       
   895 
       
   896     createDA(d->inda);
       
   897     if (d->inda == (XSQLDA*)0){
       
   898         qWarning()<<"QIOBaseResult: createDA():  failed to allocate memory";
       
   899         return false;
       
   900     }
       
   901 
       
   902     if (!d->transaction())
       
   903         return false;
       
   904 
       
   905     isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
       
   906     if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not allocate statement"),
       
   907                    QSqlError::StatementError))
       
   908         return false;
       
   909     isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0,
       
   910         const_cast<char*>(encodeString(d->tc, query).constData()), FBVERSION, d->sqlda);
       
   911     if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not prepare statement"),
       
   912                    QSqlError::StatementError))
       
   913         return false;
       
   914 
       
   915     isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda);
       
   916     if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult",
       
   917                     "Could not describe input statement"), QSqlError::StatementError))
       
   918         return false;
       
   919     if (d->inda->sqld > d->inda->sqln) {
       
   920         enlargeDA(d->inda, d->inda->sqld);
       
   921         if (d->inda == (XSQLDA*)0) {
       
   922             qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory";
       
   923             return false;
       
   924         }
       
   925 
       
   926         isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda);
       
   927         if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult",
       
   928                         "Could not describe input statement"), QSqlError::StatementError))
       
   929             return false;
       
   930     }
       
   931     initDA(d->inda);
       
   932     if (d->sqlda->sqld > d->sqlda->sqln) {
       
   933         // need more field descriptors
       
   934         enlargeDA(d->sqlda, d->sqlda->sqld);
       
   935         if (d->sqlda == (XSQLDA*)0) {
       
   936             qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory";
       
   937             return false;
       
   938         }
       
   939 
       
   940         isc_dsql_describe(d->status, &d->stmt, FBVERSION, d->sqlda);
       
   941         if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not describe statement"),
       
   942                        QSqlError::StatementError))
       
   943             return false;
       
   944     }
       
   945     initDA(d->sqlda);
       
   946 
       
   947     setSelect(d->isSelect());
       
   948     if (!isSelect()) {
       
   949         free(d->sqlda);
       
   950         d->sqlda = 0;
       
   951     }
       
   952 
       
   953     return true;
       
   954 }
       
   955 
       
   956 
       
   957 bool QIBaseResult::exec()
       
   958 {
       
   959     bool ok = true;
       
   960 
       
   961     if (!d->trans)
       
   962         d->transaction();
       
   963 
       
   964     if (!driver() || !driver()->isOpen() || driver()->isOpenError())
       
   965         return false;
       
   966     setActive(false);
       
   967     setAt(QSql::BeforeFirstRow);
       
   968 
       
   969     if (d->inda) {
       
   970         QVector<QVariant>& values = boundValues();
       
   971         int i;
       
   972         if (values.count() > d->inda->sqld) {
       
   973             qWarning("QIBaseResult::exec: Parameter mismatch, expected %d, got %d parameters",
       
   974                      d->inda->sqld, values.count());
       
   975             return false;
       
   976         }
       
   977         int para = 0;
       
   978         for (i = 0; i < values.count(); ++i) {
       
   979             para = i;
       
   980             if (!d->inda->sqlvar[para].sqldata)
       
   981                 // skip unknown datatypes
       
   982                 continue;
       
   983             const QVariant val(values[i]);
       
   984             if (d->inda->sqlvar[para].sqltype & 1) {
       
   985                 if (val.isNull()) {
       
   986                     // set null indicator
       
   987                     *(d->inda->sqlvar[para].sqlind) = -1;
       
   988                     // and set the value to 0, otherwise it would count as empty string.
       
   989                     // it seems to be working with just setting sqlind to -1
       
   990                     //*((char*)d->inda->sqlvar[para].sqldata) = 0;
       
   991                     continue;
       
   992                 }
       
   993                 // a value of 0 means non-null.
       
   994                 *(d->inda->sqlvar[para].sqlind) = 0;
       
   995             }
       
   996             switch(d->inda->sqlvar[para].sqltype & ~1) {
       
   997             case SQL_INT64:
       
   998                 if (d->inda->sqlvar[para].sqlscale < 0)
       
   999                     *((qint64*)d->inda->sqlvar[para].sqldata) =
       
  1000                         (qint64)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
       
  1001                 else
       
  1002                     *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
       
  1003                 break;
       
  1004             case SQL_LONG:
       
  1005                 if (d->inda->sqlvar[para].sqlscale < 0)
       
  1006                     *((long*)d->inda->sqlvar[para].sqldata) =
       
  1007                         (long)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
       
  1008                 else
       
  1009                     *((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong();
       
  1010                 break;
       
  1011             case SQL_SHORT:
       
  1012                 if (d->inda->sqlvar[para].sqlscale < 0)
       
  1013                     *((short*)d->inda->sqlvar[para].sqldata) =
       
  1014                         (short)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
       
  1015                 else
       
  1016                     *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt();
       
  1017                 break;
       
  1018             case SQL_FLOAT:
       
  1019                 *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble();
       
  1020                 break;
       
  1021             case SQL_DOUBLE:
       
  1022                 *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble();
       
  1023                 break;
       
  1024             case SQL_TIMESTAMP:
       
  1025                 *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime());
       
  1026                 break;
       
  1027             case SQL_TYPE_TIME:
       
  1028                 *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime());
       
  1029                 break;
       
  1030             case SQL_TYPE_DATE:
       
  1031                 *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate());
       
  1032                 break;
       
  1033             case SQL_VARYING:
       
  1034             case SQL_TEXT:
       
  1035                 qFillBufferWithString(d->inda->sqlvar[para].sqldata, val.toString(),
       
  1036                                       d->inda->sqlvar[para].sqllen,
       
  1037                                       (d->inda->sqlvar[para].sqltype & ~1) == SQL_VARYING, false, d->tc);
       
  1038                 break;
       
  1039             case SQL_BLOB:
       
  1040                     ok &= d->writeBlob(para, val.toByteArray());
       
  1041                     break;
       
  1042             case SQL_ARRAY:
       
  1043                     ok &= d->writeArray(para, val.toList());
       
  1044                     break;
       
  1045             default:
       
  1046                     qWarning("QIBaseResult::exec: Unknown datatype %d",
       
  1047                              d->inda->sqlvar[para].sqltype & ~1);
       
  1048                     break;
       
  1049             }
       
  1050         }
       
  1051     }
       
  1052 
       
  1053     if (ok) {
       
  1054         if (colCount() && d->queryType != isc_info_sql_stmt_exec_procedure) {
       
  1055             isc_dsql_free_statement(d->status, &d->stmt, DSQL_close);
       
  1056             if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to close statement")))
       
  1057                 return false;
       
  1058             cleanup();
       
  1059         }
       
  1060         if (d->queryType == isc_info_sql_stmt_exec_procedure)
       
  1061             isc_dsql_execute2(d->status, &d->trans, &d->stmt, FBVERSION, d->inda, d->sqlda);
       
  1062         else
       
  1063             isc_dsql_execute(d->status, &d->trans, &d->stmt, FBVERSION, d->inda);
       
  1064         if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to execute query")))
       
  1065             return false;
       
  1066 
       
  1067         // Not all stored procedures necessarily return values.
       
  1068         if (d->queryType == isc_info_sql_stmt_exec_procedure && d->sqlda && d->sqlda->sqld == 0)
       
  1069             delDA(d->sqlda);
       
  1070 
       
  1071         if (d->sqlda)
       
  1072             init(d->sqlda->sqld);
       
  1073 
       
  1074         if (!isSelect())
       
  1075              d->commit();
       
  1076 
       
  1077         setActive(true);
       
  1078         return true;
       
  1079     }
       
  1080     return false;
       
  1081 }
       
  1082 
       
  1083 bool QIBaseResult::reset (const QString& query)
       
  1084 {
       
  1085     if (!prepare(query))
       
  1086         return false;
       
  1087     return exec();
       
  1088 }
       
  1089 
       
  1090 bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
       
  1091 {
       
  1092     ISC_STATUS stat = 0;
       
  1093 
       
  1094     // Stored Procedures are special - they populate our d->sqlda when executing,
       
  1095     // so we don't have to call isc_dsql_fetch
       
  1096     if (d->queryType == isc_info_sql_stmt_exec_procedure) {
       
  1097         // the first "fetch" shall succeed, all consecutive ones will fail since
       
  1098         // we only have one row to fetch for stored procedures
       
  1099         if (rowIdx != 0)
       
  1100             stat = 100;
       
  1101     } else {
       
  1102         stat = isc_dsql_fetch(d->status, &d->stmt, FBVERSION, d->sqlda);
       
  1103     }
       
  1104 
       
  1105     if (stat == 100) {
       
  1106         // no more rows
       
  1107         setAt(QSql::AfterLastRow);
       
  1108         return false;
       
  1109     }
       
  1110     if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not fetch next item"),
       
  1111                    QSqlError::StatementError))
       
  1112         return false;
       
  1113     if (rowIdx < 0) // not interested in actual values
       
  1114         return true;
       
  1115 
       
  1116     for (int i = 0; i < d->sqlda->sqld; ++i) {
       
  1117         int idx = rowIdx + i;
       
  1118         char *buf = d->sqlda->sqlvar[i].sqldata;
       
  1119         int size = d->sqlda->sqlvar[i].sqllen;
       
  1120         Q_ASSERT(buf);
       
  1121 
       
  1122         if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) {
       
  1123             // null value
       
  1124             QVariant v;
       
  1125             v.convert(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype, d->sqlda->sqlvar[i].sqlscale < 0));
       
  1126             if(v.type() == QVariant::Double) {
       
  1127                 switch(numericalPrecisionPolicy()) {
       
  1128                 case QSql::LowPrecisionInt32:
       
  1129                     v.convert(QVariant::Int);
       
  1130                     break;
       
  1131                 case QSql::LowPrecisionInt64:
       
  1132                     v.convert(QVariant::LongLong);
       
  1133                     break;
       
  1134                 case QSql::HighPrecision:
       
  1135                     v.convert(QVariant::String);
       
  1136                     break;
       
  1137                 }
       
  1138             }
       
  1139             row[idx] = v;
       
  1140             continue;
       
  1141         }
       
  1142 
       
  1143         switch(d->sqlda->sqlvar[i].sqltype & ~1) {
       
  1144         case SQL_VARYING:
       
  1145             // pascal strings - a short with a length information followed by the data
       
  1146             if (d->tc)
       
  1147                 row[idx] = d->tc->toUnicode(buf + sizeof(short), *(short*)buf);
       
  1148             else
       
  1149                 row[idx] = QString::fromUtf8(buf + sizeof(short), *(short*)buf);
       
  1150             break;
       
  1151         case SQL_INT64:
       
  1152             if (d->sqlda->sqlvar[i].sqlscale < 0)
       
  1153                 row[idx] = *(qint64*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale);
       
  1154             else
       
  1155                 row[idx] = QVariant(*(qint64*)buf);
       
  1156             break;
       
  1157         case SQL_LONG:
       
  1158             if (d->sqlda->sqlvar[i].sqllen == 4)
       
  1159                 if (d->sqlda->sqlvar[i].sqlscale < 0)
       
  1160                     row[idx] = QVariant(*(qint32*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale));
       
  1161                 else
       
  1162                     row[idx] = QVariant(*(qint32*)buf);
       
  1163             else
       
  1164                 row[idx] = QVariant(*(qint64*)buf);
       
  1165             break;
       
  1166         case SQL_SHORT:
       
  1167             if (d->sqlda->sqlvar[i].sqlscale < 0)
       
  1168                 row[idx] = QVariant(long((*(short*)buf)) * pow(10.0, d->sqlda->sqlvar[i].sqlscale));
       
  1169             else
       
  1170                 row[idx] = QVariant(int((*(short*)buf)));
       
  1171             break;
       
  1172         case SQL_FLOAT:
       
  1173             row[idx] = QVariant(double((*(float*)buf)));
       
  1174             break;
       
  1175         case SQL_DOUBLE:
       
  1176             row[idx] = QVariant(*(double*)buf);
       
  1177             break;
       
  1178         case SQL_TIMESTAMP:
       
  1179             row[idx] = fromTimeStamp(buf);
       
  1180             break;
       
  1181         case SQL_TYPE_TIME:
       
  1182             row[idx] = fromTime(buf);
       
  1183             break;
       
  1184         case SQL_TYPE_DATE:
       
  1185             row[idx] = fromDate(buf);
       
  1186             break;
       
  1187         case SQL_TEXT:
       
  1188             if (d->tc)
       
  1189                 row[idx] = d->tc->toUnicode(buf, size);
       
  1190             else
       
  1191                 row[idx] = QString::fromUtf8(buf, size);
       
  1192             break;
       
  1193         case SQL_BLOB:
       
  1194             row[idx] = d->fetchBlob((ISC_QUAD*)buf);
       
  1195             break;
       
  1196         case SQL_ARRAY:
       
  1197             row[idx] = d->fetchArray(i, (ISC_QUAD*)buf);
       
  1198             break;
       
  1199         default:
       
  1200             // unknown type - don't even try to fetch
       
  1201             row[idx] = QVariant();
       
  1202             break;
       
  1203         }
       
  1204         if (d->sqlda->sqlvar[i].sqlscale < 0) {
       
  1205             QVariant v = row[idx];
       
  1206             switch(numericalPrecisionPolicy()) {
       
  1207             case QSql::LowPrecisionInt32:
       
  1208                 if(v.convert(QVariant::Int))
       
  1209                     row[idx]=v;
       
  1210                 break;
       
  1211             case QSql::LowPrecisionInt64:
       
  1212                 if(v.convert(QVariant::LongLong))
       
  1213                     row[idx]=v;
       
  1214                 break;
       
  1215             case QSql::LowPrecisionDouble:
       
  1216                 if(v.convert(QVariant::Double))
       
  1217                     row[idx]=v;
       
  1218                 break;
       
  1219             case QSql::HighPrecision:
       
  1220                 if(v.convert(QVariant::String))
       
  1221                     row[idx]=v;
       
  1222                 break;
       
  1223             }
       
  1224         }
       
  1225     }
       
  1226 
       
  1227     return true;
       
  1228 }
       
  1229 
       
  1230 int QIBaseResult::size()
       
  1231 {
       
  1232     return -1;
       
  1233 
       
  1234 #if 0 /// ### FIXME
       
  1235     static char sizeInfo[] = {isc_info_sql_records};
       
  1236     char buf[64];
       
  1237 
       
  1238     //qDebug() << sizeInfo;
       
  1239     if (!isActive() || !isSelect())
       
  1240         return -1;
       
  1241 
       
  1242         char ct;
       
  1243         short len;
       
  1244         int val = 0;
       
  1245 //    while(val == 0) {
       
  1246         isc_dsql_sql_info(d->status, &d->stmt, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
       
  1247 //        isc_database_info(d->status, &d->ibase, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
       
  1248 
       
  1249         for(int i = 0; i < 66; ++i)
       
  1250             qDebug() << QString::number(buf[i]);
       
  1251 
       
  1252         for (char* c = buf + 3; *c != isc_info_end; /*nothing*/) {
       
  1253             ct = *(c++);
       
  1254             len = isc_vax_integer(c, 2);
       
  1255             c += 2;
       
  1256             val = isc_vax_integer(c, len);
       
  1257             c += len;
       
  1258             qDebug() << "size" << val;
       
  1259             if (ct == isc_info_req_select_count)
       
  1260                 return val;
       
  1261         }
       
  1262         //qDebug() << "size -1";
       
  1263         return -1;
       
  1264 
       
  1265         unsigned int i, result_size;
       
  1266         if (buf[0] == isc_info_sql_records) {
       
  1267             i = 3;
       
  1268             result_size = isc_vax_integer(&buf[1],2);
       
  1269             while (buf[i] != isc_info_end && i < result_size) {
       
  1270                 len = (short)isc_vax_integer(&buf[i+1],2);
       
  1271                 if (buf[i] == isc_info_req_select_count)
       
  1272                      return (isc_vax_integer(&buf[i+3],len));
       
  1273                 i += len+3;
       
  1274            }
       
  1275         }
       
  1276 //    }
       
  1277     return -1;
       
  1278 #endif
       
  1279 }
       
  1280 
       
  1281 int QIBaseResult::numRowsAffected()
       
  1282 {
       
  1283     static char acCountInfo[] = {isc_info_sql_records};
       
  1284     char cCountType;
       
  1285 
       
  1286     switch (d->queryType) {
       
  1287     case isc_info_sql_stmt_select:
       
  1288         cCountType = isc_info_req_select_count;
       
  1289         break;
       
  1290     case isc_info_sql_stmt_update:
       
  1291         cCountType = isc_info_req_update_count;
       
  1292         break;
       
  1293     case isc_info_sql_stmt_delete:
       
  1294         cCountType = isc_info_req_delete_count;
       
  1295         break;
       
  1296     case isc_info_sql_stmt_insert:
       
  1297         cCountType = isc_info_req_insert_count;
       
  1298         break;
       
  1299     default:
       
  1300         qWarning() << "numRowsAffected: Unknown statement type (" << d->queryType << ")";
       
  1301         return -1;
       
  1302     }
       
  1303 
       
  1304     char acBuffer[33];
       
  1305     int iResult = -1;
       
  1306     isc_dsql_sql_info(d->status, &d->stmt, sizeof(acCountInfo), acCountInfo, sizeof(acBuffer), acBuffer);
       
  1307     if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get statement info"),
       
  1308                    QSqlError::StatementError))
       
  1309         return -1;
       
  1310     for (char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; /*nothing*/) {
       
  1311         char cType = *pcBuf++;
       
  1312         short sLength = isc_vax_integer (pcBuf, 2);
       
  1313         pcBuf += 2;
       
  1314         int iValue = isc_vax_integer (pcBuf, sLength);
       
  1315         pcBuf += sLength;
       
  1316 
       
  1317         if (cType == cCountType) {
       
  1318             iResult = iValue;
       
  1319             break;
       
  1320         }
       
  1321     }
       
  1322     return iResult;
       
  1323 }
       
  1324 
       
  1325 QSqlRecord QIBaseResult::record() const
       
  1326 {
       
  1327     QSqlRecord rec;
       
  1328     if (!isActive() || !d->sqlda)
       
  1329         return rec;
       
  1330 
       
  1331     XSQLVAR v;
       
  1332     for (int i = 0; i < d->sqlda->sqld; ++i) {
       
  1333         v = d->sqlda->sqlvar[i];
       
  1334         QSqlField f(QString::fromLatin1(v.aliasname, v.aliasname_length).simplified(),
       
  1335                     qIBaseTypeName2(v.sqltype, v.sqlscale < 0));
       
  1336         f.setLength(v.sqllen);
       
  1337         f.setPrecision(qAbs(v.sqlscale));
       
  1338         f.setRequiredStatus((v.sqltype & 1) == 0 ? QSqlField::Required : QSqlField::Optional);
       
  1339         if(v.sqlscale < 0) {
       
  1340             QSqlQuery q(new QIBaseResult(d->db));
       
  1341             q.setForwardOnly(true);
       
  1342             q.exec(QLatin1String("select b.RDB$FIELD_PRECISION, b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, a.RDB$NULL_FLAG "
       
  1343                     "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
       
  1344                     "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
       
  1345                     "AND a.RDB$RELATION_NAME = '") + QString::fromAscii(v.relname, v.relname_length).toUpper() + QLatin1String("' "
       
  1346                     "AND a.RDB$FIELD_NAME = '") + QString::fromAscii(v.sqlname, v.sqlname_length).toUpper() + QLatin1String("' "));
       
  1347             if(q.first()) {
       
  1348                 if(v.sqlscale < 0) {
       
  1349                     f.setLength(q.value(0).toInt());
       
  1350                     f.setPrecision(qAbs(q.value(1).toInt()));
       
  1351                 } else {
       
  1352                     f.setLength(q.value(2).toInt());
       
  1353                     f.setPrecision(0);
       
  1354                 }
       
  1355                 f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional);
       
  1356             }
       
  1357         }
       
  1358         f.setSqlType(v.sqltype);
       
  1359         rec.append(f);
       
  1360     }
       
  1361     return rec;
       
  1362 }
       
  1363 
       
  1364 QVariant QIBaseResult::handle() const
       
  1365 {
       
  1366     return QVariant(qRegisterMetaType<isc_stmt_handle>("isc_stmt_handle"), &d->stmt);
       
  1367 }
       
  1368 
       
  1369 /*********************************/
       
  1370 
       
  1371 QIBaseDriver::QIBaseDriver(QObject * parent)
       
  1372     : QSqlDriver(parent)
       
  1373 {
       
  1374     d = new QIBaseDriverPrivate(this);
       
  1375 }
       
  1376 
       
  1377 QIBaseDriver::QIBaseDriver(isc_db_handle connection, QObject *parent)
       
  1378     : QSqlDriver(parent)
       
  1379 {
       
  1380     d = new QIBaseDriverPrivate(this);
       
  1381     d->ibase = connection;
       
  1382     setOpen(true);
       
  1383     setOpenError(false);
       
  1384 }
       
  1385 
       
  1386 QIBaseDriver::~QIBaseDriver()
       
  1387 {
       
  1388     delete d;
       
  1389 }
       
  1390 
       
  1391 bool QIBaseDriver::hasFeature(DriverFeature f) const
       
  1392 {
       
  1393     switch (f) {
       
  1394     case QuerySize:
       
  1395     case NamedPlaceholders:
       
  1396     case LastInsertId:
       
  1397     case BatchOperations:
       
  1398     case SimpleLocking:
       
  1399     case FinishQuery:
       
  1400     case MultipleResultSets:
       
  1401         return false;
       
  1402     case Transactions:
       
  1403     case PreparedQueries:
       
  1404     case PositionalPlaceholders:
       
  1405     case Unicode:
       
  1406     case BLOB:
       
  1407     case EventNotifications:
       
  1408     case LowPrecisionNumbers:
       
  1409         return true;
       
  1410     }
       
  1411     return false;
       
  1412 }
       
  1413 
       
  1414 bool QIBaseDriver::open(const QString & db,
       
  1415           const QString & user,
       
  1416           const QString & password,
       
  1417           const QString & host,
       
  1418           int /*port*/,
       
  1419           const QString & connOpts)
       
  1420 {
       
  1421     if (isOpen())
       
  1422         close();
       
  1423 
       
  1424     const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
       
  1425 
       
  1426     QString encString;
       
  1427     QByteArray role;
       
  1428     for (int i = 0; i < opts.count(); ++i) {
       
  1429         QString tmp(opts.at(i).simplified());
       
  1430         int idx;
       
  1431         if ((idx = tmp.indexOf(QLatin1Char('='))) != -1) {
       
  1432             QString val = tmp.mid(idx + 1).simplified();
       
  1433             QString opt = tmp.left(idx).simplified();
       
  1434             if (opt.toUpper() == QLatin1String("ISC_DPB_LC_CTYPE"))
       
  1435                 encString = val;
       
  1436             else if (opt.toUpper() == QLatin1String("ISC_DPB_SQL_ROLE_NAME")) {
       
  1437                 role = val.toLocal8Bit();
       
  1438                 role.truncate(255);
       
  1439             }
       
  1440         }
       
  1441     }
       
  1442 
       
  1443     // Use UNICODE_FSS when no ISC_DPB_LC_CTYPE is provided
       
  1444     if (encString.isEmpty())
       
  1445         encString = QLatin1String("UNICODE_FSS");
       
  1446     else {
       
  1447         d->tc = QTextCodec::codecForName(encString.toLocal8Bit());
       
  1448         if (!d->tc) {
       
  1449             qWarning("Unsupported encoding: %s. Using UNICODE_FFS for ISC_DPB_LC_CTYPE.", encString.toLocal8Bit().constData());
       
  1450             encString = QLatin1String("UNICODE_FSS"); // Fallback to UNICODE_FSS
       
  1451         }
       
  1452     }
       
  1453 
       
  1454     QByteArray enc = encString.toLocal8Bit();
       
  1455     QByteArray usr = user.toLocal8Bit();
       
  1456     QByteArray pass = password.toLocal8Bit();
       
  1457     enc.truncate(255);
       
  1458     usr.truncate(255);
       
  1459     pass.truncate(255);
       
  1460 
       
  1461     QByteArray ba;
       
  1462     ba.resize(usr.length() + pass.length() + enc.length() + role.length() + 6);
       
  1463     int i = -1;
       
  1464     ba[++i] = isc_dpb_version1;
       
  1465     ba[++i] = isc_dpb_user_name;
       
  1466     ba[++i] = usr.length();
       
  1467     memcpy(ba.data() + ++i, usr.data(), usr.length());
       
  1468     i += usr.length();
       
  1469     ba[i] = isc_dpb_password;
       
  1470     ba[++i] = pass.length();
       
  1471     memcpy(ba.data() + ++i, pass.data(), pass.length());
       
  1472     i += pass.length();
       
  1473     ba[i] = isc_dpb_lc_ctype;
       
  1474     ba[++i] = enc.length();
       
  1475     memcpy(ba.data() + ++i, enc.data(), enc.length());
       
  1476     i += enc.length();
       
  1477 
       
  1478     if (!role.isEmpty()) {
       
  1479         ba[i] = isc_dpb_sql_role_name;
       
  1480         ba[++i] = role.length();
       
  1481         memcpy(ba.data() + ++i, role.data(), role.length());
       
  1482         i += role.length();
       
  1483     }
       
  1484 
       
  1485     QString ldb;
       
  1486     if (!host.isEmpty())
       
  1487         ldb += host + QLatin1Char(':');
       
  1488     ldb += db;
       
  1489     isc_attach_database(d->status, 0, const_cast<char *>(ldb.toLocal8Bit().constData()),
       
  1490                         &d->ibase, i, ba.data());
       
  1491     if (d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Error opening database"),
       
  1492                    QSqlError::ConnectionError)) {
       
  1493         setOpenError(true);
       
  1494         return false;
       
  1495     }
       
  1496 
       
  1497     setOpen(true);
       
  1498     return true;
       
  1499 }
       
  1500 
       
  1501 void QIBaseDriver::close()
       
  1502 {
       
  1503     if (isOpen()) {
       
  1504 
       
  1505         if (d->eventBuffers.size()) {
       
  1506             ISC_STATUS status[20];
       
  1507             QMap<QString, QIBaseEventBuffer *>::const_iterator i;
       
  1508             for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
       
  1509                 QIBaseEventBuffer *eBuffer = i.value();
       
  1510                 eBuffer->subscriptionState = QIBaseEventBuffer::Finished;
       
  1511                 isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
       
  1512                 qFreeEventBuffer(eBuffer);
       
  1513             }
       
  1514             d->eventBuffers.clear();
       
  1515 
       
  1516 #if defined(FB_API_VER)
       
  1517             // Workaround for Firebird crash
       
  1518             QTime timer;
       
  1519             timer.start();
       
  1520             while (timer.elapsed() < 500)
       
  1521                 QCoreApplication::processEvents();
       
  1522 #endif
       
  1523         }
       
  1524 
       
  1525         isc_detach_database(d->status, &d->ibase);
       
  1526         d->ibase = 0;
       
  1527         setOpen(false);
       
  1528         setOpenError(false);
       
  1529     }
       
  1530 }
       
  1531 
       
  1532 QSqlResult *QIBaseDriver::createResult() const
       
  1533 {
       
  1534     return new QIBaseResult(this);
       
  1535 }
       
  1536 
       
  1537 bool QIBaseDriver::beginTransaction()
       
  1538 {
       
  1539     if (!isOpen() || isOpenError())
       
  1540         return false;
       
  1541     if (d->trans)
       
  1542         return false;
       
  1543 
       
  1544     isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL);
       
  1545     return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Could not start transaction"),
       
  1546                        QSqlError::TransactionError);
       
  1547 }
       
  1548 
       
  1549 bool QIBaseDriver::commitTransaction()
       
  1550 {
       
  1551     if (!isOpen() || isOpenError())
       
  1552         return false;
       
  1553     if (!d->trans)
       
  1554         return false;
       
  1555 
       
  1556     isc_commit_transaction(d->status, &d->trans);
       
  1557     d->trans = 0;
       
  1558     return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Unable to commit transaction"),
       
  1559                        QSqlError::TransactionError);
       
  1560 }
       
  1561 
       
  1562 bool QIBaseDriver::rollbackTransaction()
       
  1563 {
       
  1564     if (!isOpen() || isOpenError())
       
  1565         return false;
       
  1566     if (!d->trans)
       
  1567         return false;
       
  1568 
       
  1569     isc_rollback_transaction(d->status, &d->trans);
       
  1570     d->trans = 0;
       
  1571     return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Unable to rollback transaction"),
       
  1572                        QSqlError::TransactionError);
       
  1573 }
       
  1574 
       
  1575 QStringList QIBaseDriver::tables(QSql::TableType type) const
       
  1576 {
       
  1577     QStringList res;
       
  1578     if (!isOpen())
       
  1579         return res;
       
  1580 
       
  1581     QString typeFilter;
       
  1582 
       
  1583     if (type == QSql::SystemTables) {
       
  1584         typeFilter += QLatin1String("RDB$SYSTEM_FLAG != 0");
       
  1585     } else if (type == (QSql::SystemTables | QSql::Views)) {
       
  1586         typeFilter += QLatin1String("RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL");
       
  1587     } else {
       
  1588         if (!(type & QSql::SystemTables))
       
  1589             typeFilter += QLatin1String("RDB$SYSTEM_FLAG = 0 AND ");
       
  1590         if (!(type & QSql::Views))
       
  1591             typeFilter += QLatin1String("RDB$VIEW_BLR IS NULL AND ");
       
  1592         if (!(type & QSql::Tables))
       
  1593             typeFilter += QLatin1String("RDB$VIEW_BLR IS NOT NULL AND ");
       
  1594         if (!typeFilter.isEmpty())
       
  1595             typeFilter.chop(5);
       
  1596     }
       
  1597     if (!typeFilter.isEmpty())
       
  1598         typeFilter.prepend(QLatin1String("where "));
       
  1599 
       
  1600     QSqlQuery q(createResult());
       
  1601     q.setForwardOnly(true);
       
  1602     if (!q.exec(QLatin1String("select rdb$relation_name from rdb$relations ") + typeFilter))
       
  1603         return res;
       
  1604     while(q.next())
       
  1605             res << q.value(0).toString().simplified();
       
  1606 
       
  1607     return res;
       
  1608 }
       
  1609 
       
  1610 QSqlRecord QIBaseDriver::record(const QString& tablename) const
       
  1611 {
       
  1612     QSqlRecord rec;
       
  1613     if (!isOpen())
       
  1614         return rec;
       
  1615 
       
  1616     QSqlQuery q(createResult());
       
  1617     q.setForwardOnly(true);
       
  1618     QString table = tablename;
       
  1619     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
  1620         table = stripDelimiters(table, QSqlDriver::TableName);
       
  1621     else
       
  1622         table = table.toUpper();
       
  1623     q.exec(QLatin1String("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
       
  1624            "b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
       
  1625            "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
       
  1626            "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
       
  1627            "AND a.RDB$RELATION_NAME = '") + table + QLatin1String("' "
       
  1628            "ORDER BY a.RDB$FIELD_POSITION"));
       
  1629 
       
  1630     while (q.next()) {
       
  1631         int type = q.value(1).toInt();
       
  1632         bool hasScale = q.value(3).toInt() < 0;
       
  1633         QSqlField f(q.value(0).toString().simplified(), qIBaseTypeName(type, hasScale));
       
  1634         if(hasScale) {
       
  1635             f.setLength(q.value(4).toInt());
       
  1636             f.setPrecision(qAbs(q.value(3).toInt()));
       
  1637         } else {
       
  1638             f.setLength(q.value(2).toInt());
       
  1639             f.setPrecision(0);
       
  1640         }
       
  1641         f.setRequired(q.value(5).toInt() > 0 ? true : false);
       
  1642         f.setSqlType(type);
       
  1643 
       
  1644         rec.append(f);
       
  1645     }
       
  1646     return rec;
       
  1647 }
       
  1648 
       
  1649 QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
       
  1650 {
       
  1651     QSqlIndex index(table);
       
  1652     if (!isOpen())
       
  1653         return index;
       
  1654 
       
  1655     QString tablename = table;
       
  1656     if (isIdentifierEscaped(tablename, QSqlDriver::TableName))
       
  1657         tablename = stripDelimiters(tablename, QSqlDriver::TableName);
       
  1658     else
       
  1659         tablename = tablename.toUpper();
       
  1660 
       
  1661     QSqlQuery q(createResult());
       
  1662     q.setForwardOnly(true);
       
  1663     q.exec(QLatin1String("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
       
  1664            "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
       
  1665            "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
       
  1666            "AND a.RDB$RELATION_NAME = '") + tablename +
       
  1667            QLatin1String(" 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
       
  1668            "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
       
  1669            "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
       
  1670            "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
       
  1671            "ORDER BY b.RDB$FIELD_POSITION"));
       
  1672 
       
  1673     while (q.next()) {
       
  1674         QSqlField field(q.value(1).toString().simplified(), qIBaseTypeName(q.value(2).toInt(), q.value(3).toInt() < 0));
       
  1675         index.append(field); //TODO: asc? desc?
       
  1676         index.setName(q.value(0).toString());
       
  1677     }
       
  1678 
       
  1679     return index;
       
  1680 }
       
  1681 
       
  1682 QString QIBaseDriver::formatValue(const QSqlField &field, bool trimStrings) const
       
  1683 {
       
  1684     switch (field.type()) {
       
  1685     case QVariant::DateTime: {
       
  1686         QDateTime datetime = field.value().toDateTime();
       
  1687         if (datetime.isValid())
       
  1688             return QLatin1Char('\'') + QString::number(datetime.date().year()) + QLatin1Char('-') +
       
  1689                 QString::number(datetime.date().month()) + QLatin1Char('-') +
       
  1690                 QString::number(datetime.date().day()) + QLatin1Char(' ') +
       
  1691                 QString::number(datetime.time().hour()) + QLatin1Char(':') +
       
  1692                 QString::number(datetime.time().minute()) + QLatin1Char(':') +
       
  1693                 QString::number(datetime.time().second()) + QLatin1Char('.') +
       
  1694                 QString::number(datetime.time().msec()).rightJustified(3, QLatin1Char('0'), true) +
       
  1695 		QLatin1Char('\'');
       
  1696         else
       
  1697             return QLatin1String("NULL");
       
  1698     }
       
  1699     case QVariant::Time: {
       
  1700         QTime time = field.value().toTime();
       
  1701         if (time.isValid())
       
  1702             return QLatin1Char('\'') + QString::number(time.hour()) + QLatin1Char(':') +
       
  1703                 QString::number(time.minute()) + QLatin1Char(':') +
       
  1704                 QString::number(time.second()) + QLatin1Char('.') +
       
  1705                 QString::number(time.msec()).rightJustified(3, QLatin1Char('0'), true) +
       
  1706                 QLatin1Char('\'');
       
  1707         else
       
  1708             return QLatin1String("NULL");
       
  1709     }
       
  1710     case QVariant::Date: {
       
  1711         QDate date = field.value().toDate();
       
  1712         if (date.isValid())
       
  1713             return QLatin1Char('\'') + QString::number(date.year()) + QLatin1Char('-') +
       
  1714                 QString::number(date.month()) + QLatin1Char('-') +
       
  1715                 QString::number(date.day()) + QLatin1Char('\'');
       
  1716             else
       
  1717                 return QLatin1String("NULL");
       
  1718     }
       
  1719     default:
       
  1720         return QSqlDriver::formatValue(field, trimStrings);
       
  1721     }
       
  1722 }
       
  1723 
       
  1724 QVariant QIBaseDriver::handle() const
       
  1725 {
       
  1726     return QVariant(qRegisterMetaType<isc_db_handle>("isc_db_handle"), &d->ibase);
       
  1727 }
       
  1728 
       
  1729 #if defined(FB_API_VER) && FB_API_VER >= 20
       
  1730 static ISC_EVENT_CALLBACK qEventCallback(char *result, ISC_USHORT length, const ISC_UCHAR *updated)
       
  1731 #else
       
  1732 static isc_callback qEventCallback(char *result, short length, char *updated)
       
  1733 #endif
       
  1734 {
       
  1735     if (!updated)
       
  1736         return 0;
       
  1737 
       
  1738 
       
  1739     memcpy(result, updated, length);
       
  1740     qMutex()->lock();
       
  1741     QIBaseDriver *driver = qBufferDriverMap()->value(result);
       
  1742     qMutex()->unlock();
       
  1743 
       
  1744     // We use an asynchronous call (i.e., queued connection) because the event callback
       
  1745     // is executed in a different thread than the one in which the driver lives.
       
  1746     if (driver)
       
  1747         QMetaObject::invokeMethod(driver, "qHandleEventNotification", Qt::QueuedConnection, Q_ARG(void *, reinterpret_cast<void *>(result)));
       
  1748 
       
  1749     return 0;
       
  1750 }
       
  1751 
       
  1752 bool QIBaseDriver::subscribeToNotificationImplementation(const QString &name)
       
  1753 {
       
  1754     if (!isOpen()) {
       
  1755         qWarning("QIBaseDriver::subscribeFromNotificationImplementation: database not open.");
       
  1756         return false;
       
  1757     }
       
  1758 
       
  1759     if (d->eventBuffers.contains(name)) {
       
  1760         qWarning("QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%s'.",
       
  1761             qPrintable(name));
       
  1762         return false;
       
  1763     }
       
  1764 
       
  1765     QIBaseEventBuffer *eBuffer = new QIBaseEventBuffer;
       
  1766     eBuffer->subscriptionState = QIBaseEventBuffer::Starting;
       
  1767     eBuffer->bufferLength = isc_event_block(&eBuffer->eventBuffer,
       
  1768                                             &eBuffer->resultBuffer,
       
  1769                                             1,
       
  1770                                             name.toLocal8Bit().constData());
       
  1771 
       
  1772     qMutex()->lock();
       
  1773     qBufferDriverMap()->insert(eBuffer->resultBuffer, this);
       
  1774     qMutex()->unlock();
       
  1775 
       
  1776     d->eventBuffers.insert(name, eBuffer);
       
  1777 
       
  1778     ISC_STATUS status[20];
       
  1779     isc_que_events(status,
       
  1780                    &d->ibase,
       
  1781                    &eBuffer->eventId,
       
  1782                    eBuffer->bufferLength,
       
  1783                    eBuffer->eventBuffer,
       
  1784 #if defined (FB_API_VER) && FB_API_VER >= 20
       
  1785                    (ISC_EVENT_CALLBACK)qEventCallback,
       
  1786 #else
       
  1787                    (isc_callback)qEventCallback,
       
  1788 #endif
       
  1789                    eBuffer->resultBuffer);
       
  1790 
       
  1791     if (status[0] == 1 && status[1]) {
       
  1792         setLastError(QSqlError(QString::fromLatin1("Could not subscribe to event notifications for %1.").arg(name)));
       
  1793         d->eventBuffers.remove(name);
       
  1794         qFreeEventBuffer(eBuffer);
       
  1795         return false;
       
  1796     }
       
  1797 
       
  1798     return true;
       
  1799 }
       
  1800 
       
  1801 bool QIBaseDriver::unsubscribeFromNotificationImplementation(const QString &name)
       
  1802 {
       
  1803     if (!isOpen()) {
       
  1804         qWarning("QIBaseDriver::unsubscribeFromNotificationImplementation: database not open.");
       
  1805         return false;
       
  1806     }
       
  1807 
       
  1808     if (!d->eventBuffers.contains(name)) {
       
  1809         qWarning("QIBaseDriver::QIBaseSubscriptionState not subscribed to '%s'.",
       
  1810             qPrintable(name));
       
  1811         return false;
       
  1812     }
       
  1813 
       
  1814     QIBaseEventBuffer *eBuffer = d->eventBuffers.value(name);
       
  1815     ISC_STATUS status[20];
       
  1816     eBuffer->subscriptionState = QIBaseEventBuffer::Finished;
       
  1817     isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
       
  1818 
       
  1819     if (status[0] == 1 && status[1]) {
       
  1820         setLastError(QSqlError(QString::fromLatin1("Could not unsubscribe from event notifications for %1.").arg(name)));
       
  1821         return false;
       
  1822     }
       
  1823 
       
  1824     d->eventBuffers.remove(name);
       
  1825     qFreeEventBuffer(eBuffer);
       
  1826 
       
  1827     return true;
       
  1828 }
       
  1829 
       
  1830 QStringList QIBaseDriver::subscribedToNotificationsImplementation() const
       
  1831 {
       
  1832     return QStringList(d->eventBuffers.keys());
       
  1833 }
       
  1834 
       
  1835 void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer)
       
  1836 {
       
  1837     QMap<QString, QIBaseEventBuffer *>::const_iterator i;
       
  1838     for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
       
  1839         QIBaseEventBuffer* eBuffer = i.value();
       
  1840         if (reinterpret_cast<void *>(eBuffer->resultBuffer) != updatedResultBuffer)
       
  1841             continue;
       
  1842 
       
  1843         ISC_ULONG counts[20];
       
  1844         memset(counts, 0, sizeof(counts));
       
  1845         isc_event_counts(counts, eBuffer->bufferLength, eBuffer->eventBuffer, eBuffer->resultBuffer);
       
  1846         if (counts[0]) {
       
  1847 
       
  1848             if (eBuffer->subscriptionState == QIBaseEventBuffer::Subscribed)
       
  1849                 emit notification(i.key());
       
  1850             else if (eBuffer->subscriptionState == QIBaseEventBuffer::Starting)
       
  1851                 eBuffer->subscriptionState = QIBaseEventBuffer::Subscribed;
       
  1852 
       
  1853             ISC_STATUS status[20];
       
  1854             isc_que_events(status,
       
  1855                            &d->ibase,
       
  1856                            &eBuffer->eventId,
       
  1857                            eBuffer->bufferLength,
       
  1858                            eBuffer->eventBuffer,
       
  1859 #if defined (FB_API_VER) && FB_API_VER >= 20
       
  1860                                     (ISC_EVENT_CALLBACK)qEventCallback,
       
  1861 #else
       
  1862                                     (isc_callback)qEventCallback,
       
  1863 #endif
       
  1864                                    eBuffer->resultBuffer);
       
  1865             if (status[0] == 1 && status[1]) {
       
  1866                 qCritical("QIBaseDriver::qHandleEventNotification: could not resubscribe to '%s'",
       
  1867                     qPrintable(i.key()));
       
  1868             }
       
  1869 
       
  1870             return;
       
  1871         }
       
  1872     }
       
  1873 }
       
  1874 
       
  1875 QString QIBaseDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
       
  1876 {
       
  1877     QString res = identifier;
       
  1878     if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
       
  1879         res.replace(QLatin1Char('"'), QLatin1String("\"\""));
       
  1880         res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
       
  1881         res.replace(QLatin1Char('.'), QLatin1String("\".\""));
       
  1882     }
       
  1883     return res;
       
  1884 }
       
  1885 
       
  1886 QT_END_NAMESPACE