src/sql/drivers/tds/qsql_tds.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
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 <qglobal.h>
       
    43 #ifdef Q_OS_WIN32    // We assume that MS SQL Server is used. Set Q_USE_SYBASE to force Sybase.
       
    44 // Conflicting declarations of LPCBYTE in sqlfront.h and winscard.h
       
    45 #define _WINSCARD_H_
       
    46 #include <windows.h>
       
    47 #else
       
    48 #define Q_USE_SYBASE
       
    49 #endif
       
    50 
       
    51 #include "qsql_tds.h"
       
    52 
       
    53 #include <qvariant.h>
       
    54 #include <qdatetime.h>
       
    55 #include <qhash.h>
       
    56 #include <qregexp.h>
       
    57 #include <qsqlerror.h>
       
    58 #include <qsqlfield.h>
       
    59 #include <qsqlindex.h>
       
    60 #include <qsqlquery.h>
       
    61 #include <qstringlist.h>
       
    62 #include <qvector.h>
       
    63 
       
    64 #include <stdlib.h>
       
    65 
       
    66 QT_BEGIN_NAMESPACE
       
    67 
       
    68 #ifdef DBNTWIN32
       
    69 #define QMSGHANDLE DBMSGHANDLE_PROC
       
    70 #define QERRHANDLE DBERRHANDLE_PROC
       
    71 #define QTDSCHAR SQLCHAR
       
    72 #define QTDSDATETIME4 SQLDATETIM4
       
    73 #define QTDSDATETIME SQLDATETIME
       
    74 #define QTDSDATETIME_N SQLDATETIMN
       
    75 #define QTDSDECIMAL SQLDECIMAL
       
    76 #define QTDSFLT4 SQLFLT4
       
    77 #define QTDSFLT8 SQLFLT8
       
    78 #define QTDSFLT8_N SQLFLTN
       
    79 #define QTDSINT1 SQLINT1
       
    80 #define QTDSINT2 SQLINT2
       
    81 #define QTDSINT4 SQLINT4
       
    82 #define QTDSINT4_N SQLINTN
       
    83 #define QTDSMONEY4 SQLMONEY4
       
    84 #define QTDSMONEY SQLMONEY
       
    85 #define QTDSMONEY_N SQLMONEYN
       
    86 #define QTDSNUMERIC SQLNUMERIC
       
    87 #define QTDSTEXT SQLTEXT
       
    88 #define QTDSVARCHAR SQLVARCHAR
       
    89 #define QTDSBIT SQLBIT
       
    90 #define QTDSBINARY SQLBINARY
       
    91 #define QTDSVARBINARY SQLVARBINARY
       
    92 #define QTDSIMAGE SQLIMAGE
       
    93 #else
       
    94 #define QMSGHANDLE MHANDLEFUNC
       
    95 #define QERRHANDLE EHANDLEFUNC
       
    96 #define QTDSCHAR SYBCHAR
       
    97 #define QTDSDATETIME4 SYBDATETIME4
       
    98 #define QTDSDATETIME SYBDATETIME
       
    99 #define QTDSDATETIME_N SYBDATETIMN
       
   100 #define QTDSDECIMAL SYBDECIMAL
       
   101 #define QTDSFLT8 SYBFLT8
       
   102 #define QTDSFLT8_N SYBFLTN
       
   103 #define QTDSFLT4 SYBREAL
       
   104 #define QTDSINT1 SYBINT1
       
   105 #define QTDSINT2 SYBINT2
       
   106 #define QTDSINT4 SYBINT4
       
   107 #define QTDSINT4_N SYBINTN
       
   108 #define QTDSMONEY4 SYBMONEY4
       
   109 #define QTDSMONEY SYBMONEY
       
   110 #define QTDSMONEY_N SYBMONEYN
       
   111 #define QTDSNUMERIC SYBNUMERIC
       
   112 #define QTDSTEXT SYBTEXT
       
   113 #define QTDSVARCHAR SYBVARCHAR
       
   114 #define QTDSBIT SYBBIT
       
   115 #define QTDSBINARY SYBBINARY
       
   116 #define QTDSVARBINARY SYBVARBINARY
       
   117 #define QTDSIMAGE SYBIMAGE
       
   118 // magic numbers not defined anywhere in Sybase headers
       
   119 #define QTDSDECIMAL_2 55
       
   120 #define QTDSNUMERIC_2 63
       
   121 #endif  //DBNTWIN32
       
   122 
       
   123 #define TDS_CURSOR_SIZE 50
       
   124 
       
   125 // workaround for FreeTDS
       
   126 #ifndef CS_PUBLIC
       
   127 #define CS_PUBLIC
       
   128 #endif
       
   129 
       
   130 QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, int errNo = -1)
       
   131 {
       
   132     return QSqlError(QLatin1String("QTDS: ") + err, QString(), type, errNo);
       
   133 }
       
   134 
       
   135 class QTDSDriverPrivate
       
   136 {
       
   137 public:
       
   138     QTDSDriverPrivate(): login(0) {}
       
   139     LOGINREC* login;  // login information
       
   140     QString hostName;
       
   141     QString db;
       
   142 };
       
   143 
       
   144 
       
   145 class QTDSResultPrivate
       
   146 {
       
   147 public:
       
   148     QTDSResultPrivate():login(0), dbproc(0) {}
       
   149     LOGINREC* login;  // login information
       
   150     DBPROCESS* dbproc; // connection from app to server
       
   151     QSqlError lastError;
       
   152     void addErrorMsg(QString& errMsg) { errorMsgs.append(errMsg); }
       
   153     QString getErrorMsgs() { return errorMsgs.join(QLatin1String("\n")); }
       
   154     void clearErrorMsgs() { errorMsgs.clear(); }
       
   155     QVector<void *> buffer;
       
   156     QSqlRecord rec;
       
   157 
       
   158 private:
       
   159     QStringList errorMsgs;
       
   160 };
       
   161 
       
   162 typedef QHash<DBPROCESS *, QTDSResultPrivate *> QTDSErrorHash;
       
   163 Q_GLOBAL_STATIC(QTDSErrorHash, errs)
       
   164 
       
   165 extern "C" {
       
   166 static int CS_PUBLIC qTdsMsgHandler (DBPROCESS* dbproc,
       
   167                             DBINT /*msgno*/,
       
   168                             int msgstate,
       
   169                             int severity,
       
   170                             char* msgtext,
       
   171                             char* /*srvname*/,
       
   172                             char* /*procname*/,
       
   173                             int /*line*/)
       
   174 {
       
   175     QTDSResultPrivate* p = errs()->value(dbproc);
       
   176 
       
   177     if (!p) {
       
   178 //        ### umm... temporary disabled since this throws a lot of warnings...
       
   179 //        qWarning("QTDSDriver warning (%d): [%s] from server [%s]", msgstate, msgtext, srvname);
       
   180         return INT_CANCEL;
       
   181     }
       
   182 
       
   183     if (severity > 0) {
       
   184         QString errMsg = QString::fromLatin1("%1 (%2)").arg(QString::fromAscii(msgtext)).arg(
       
   185                                     msgstate);
       
   186         p->addErrorMsg(errMsg);
       
   187     }
       
   188 
       
   189     return INT_CANCEL;
       
   190 }
       
   191 
       
   192 static int CS_PUBLIC qTdsErrHandler(DBPROCESS* dbproc,
       
   193                                 int /*severity*/,
       
   194                                 int dberr,
       
   195                                 int /*oserr*/,
       
   196                                 char* dberrstr,
       
   197                                 char* oserrstr)
       
   198 {
       
   199     QTDSResultPrivate* p = errs()->value(dbproc);
       
   200     if (!p) {
       
   201         qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr);
       
   202         return INT_CANCEL;
       
   203     }
       
   204     /*
       
   205      * If the process is dead or NULL and
       
   206      * we are not in the middle of logging in...
       
   207      */
       
   208     if((dbproc == NULL || DBDEAD(dbproc))) {
       
   209         qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr);
       
   210         return INT_CANCEL;
       
   211     }
       
   212 
       
   213 
       
   214     QString errMsg = QString::fromLatin1("%1 %2\n").arg(QLatin1String(dberrstr)).arg(
       
   215                                 QLatin1String(oserrstr));
       
   216     errMsg += p->getErrorMsgs();
       
   217     p->lastError = qMakeError(errMsg, QSqlError::UnknownError, dberr);
       
   218     p->clearErrorMsgs();
       
   219 
       
   220     return INT_CANCEL ;
       
   221 }
       
   222 
       
   223 } //extern "C"
       
   224 
       
   225 
       
   226 QVariant::Type qDecodeTDSType(int type)
       
   227 {
       
   228     QVariant::Type t = QVariant::Invalid;
       
   229     switch (type) {
       
   230     case QTDSCHAR:
       
   231     case QTDSTEXT:
       
   232     case QTDSVARCHAR:
       
   233         t = QVariant::String;
       
   234         break;
       
   235     case QTDSINT1:
       
   236     case QTDSINT2:
       
   237     case QTDSINT4:
       
   238     case QTDSINT4_N:
       
   239     case QTDSBIT:
       
   240         t = QVariant::Int;
       
   241         break;
       
   242     case QTDSFLT4:
       
   243     case QTDSFLT8:
       
   244     case QTDSFLT8_N:
       
   245     case QTDSMONEY4:
       
   246     case QTDSMONEY:
       
   247     case QTDSDECIMAL:
       
   248     case QTDSNUMERIC:
       
   249 #ifdef QTDSNUMERIC_2
       
   250     case QTDSNUMERIC_2:
       
   251 #endif
       
   252 #ifdef QTDSDECIMAL_2
       
   253     case QTDSDECIMAL_2:
       
   254 #endif
       
   255     case QTDSMONEY_N:
       
   256         t = QVariant::Double;
       
   257         break;
       
   258     case QTDSDATETIME4:
       
   259     case QTDSDATETIME:
       
   260     case QTDSDATETIME_N:
       
   261         t = QVariant::DateTime;
       
   262         break;
       
   263     case QTDSBINARY:
       
   264     case QTDSVARBINARY:
       
   265     case QTDSIMAGE:
       
   266         t = QVariant::ByteArray;
       
   267         break;
       
   268     default:
       
   269         t = QVariant::Invalid;
       
   270         break;
       
   271     }
       
   272     return t;
       
   273 }
       
   274 
       
   275 QVariant::Type qFieldType(QTDSResultPrivate* d, int i)
       
   276 {
       
   277     QVariant::Type type = qDecodeTDSType(dbcoltype(d->dbproc, i+1));
       
   278     return type;
       
   279 }
       
   280 
       
   281 
       
   282 QTDSResult::QTDSResult(const QTDSDriver* db)
       
   283     : QSqlCachedResult(db)
       
   284 {
       
   285     d = new QTDSResultPrivate();
       
   286     d->login = db->d->login;
       
   287 
       
   288     d->dbproc = dbopen(d->login, const_cast<char*>(db->d->hostName.toLatin1().constData()));
       
   289     if (!d->dbproc)
       
   290         return;
       
   291     if (dbuse(d->dbproc, const_cast<char*>(db->d->db.toLatin1().constData())) == FAIL)
       
   292         return;
       
   293 
       
   294     // insert d in error handler dict
       
   295     errs()->insert(d->dbproc, d);
       
   296     dbcmd(d->dbproc, "set quoted_identifier on");
       
   297     dbsqlexec(d->dbproc);
       
   298 }
       
   299 
       
   300 QTDSResult::~QTDSResult()
       
   301 {
       
   302     cleanup();
       
   303     if (d->dbproc)
       
   304         dbclose(d->dbproc);
       
   305     errs()->remove(d->dbproc);
       
   306     delete d;
       
   307 }
       
   308 
       
   309 void QTDSResult::cleanup()
       
   310 {
       
   311     d->clearErrorMsgs();
       
   312     d->rec.clear();
       
   313     for (int i = 0; i < d->buffer.size() / 2; ++i)
       
   314         free(d->buffer.at(i * 2));
       
   315     d->buffer.clear();
       
   316     // "can" stands for "cancel"... very clever.
       
   317     dbcanquery(d->dbproc);
       
   318     dbfreebuf(d->dbproc);
       
   319 
       
   320     QSqlCachedResult::cleanup();
       
   321 }
       
   322 
       
   323 QVariant QTDSResult::handle() const
       
   324 {
       
   325     return QVariant(qRegisterMetaType<DBPROCESS *>("DBPROCESS*"), &d->dbproc);
       
   326 }
       
   327 
       
   328 static inline bool qIsNull(const void *ind)
       
   329 {
       
   330     return *reinterpret_cast<const DBINT *>(&ind) == -1;
       
   331 }
       
   332 
       
   333 bool QTDSResult::gotoNext(QSqlCachedResult::ValueCache &values, int index)
       
   334 {
       
   335     STATUS stat = dbnextrow(d->dbproc);
       
   336     if (stat == NO_MORE_ROWS) {
       
   337         setAt(QSql::AfterLastRow);
       
   338         return false;
       
   339     }
       
   340     if ((stat == FAIL) || (stat == BUF_FULL)) {
       
   341         setLastError(d->lastError);
       
   342         return false;
       
   343     }
       
   344 
       
   345     if (index < 0)
       
   346         return true;
       
   347 
       
   348     for (int i = 0; i < d->rec.count(); ++i) {
       
   349         int idx = index + i;
       
   350         switch (d->rec.field(i).type()) {
       
   351             case QVariant::DateTime:
       
   352                 if (qIsNull(d->buffer.at(i * 2 + 1))) {
       
   353                     values[idx] = QVariant(QVariant::DateTime);
       
   354                 } else {
       
   355                     DBDATETIME *bdt = (DBDATETIME*) d->buffer.at(i * 2);
       
   356                     QDate date = QDate::fromString(QLatin1String("1900-01-01"), Qt::ISODate);
       
   357                     QTime time = QTime::fromString(QLatin1String("00:00:00"), Qt::ISODate);
       
   358                     values[idx] = QDateTime(date.addDays(bdt->dtdays), time.addMSecs(int(bdt->dttime / 0.3)));
       
   359                 }
       
   360                 break;
       
   361             case QVariant::Int:
       
   362                 if (qIsNull(d->buffer.at(i * 2 + 1)))
       
   363                     values[idx] = QVariant(QVariant::Int);
       
   364                 else
       
   365                     values[idx] = *((int*)d->buffer.at(i * 2));
       
   366                 break;
       
   367             case QVariant::Double:
       
   368             case QVariant::String:
       
   369                 if (qIsNull(d->buffer.at(i * 2 + 1)))
       
   370                     values[idx] = QVariant(QVariant::String);
       
   371                 else
       
   372                     values[idx] = QString::fromLocal8Bit((const char*)d->buffer.at(i * 2)).trimmed();
       
   373                 break;
       
   374             case QVariant::ByteArray: {
       
   375                 if (qIsNull(d->buffer.at(i * 2 + 1)))
       
   376                     values[idx] = QVariant(QVariant::ByteArray);
       
   377                 else
       
   378                     values[idx] = QByteArray((const char*)d->buffer.at(i * 2));
       
   379                 break;
       
   380             }
       
   381             default:
       
   382                 // should never happen, and we already fired
       
   383                 // a warning while binding.
       
   384                 values[idx] = QVariant();
       
   385                 break;
       
   386         }
       
   387     }
       
   388 
       
   389     return true;
       
   390 }
       
   391 
       
   392 bool QTDSResult::reset (const QString& query)
       
   393 {
       
   394     cleanup();
       
   395     if (!driver() || !driver()-> isOpen() || driver()->isOpenError())
       
   396         return false;
       
   397     setActive(false);
       
   398     setAt(QSql::BeforeFirstRow);
       
   399     if (dbcmd(d->dbproc, const_cast<char*>(query.toLocal8Bit().constData())) == FAIL) {
       
   400         setLastError(d->lastError);
       
   401         return false;
       
   402     }
       
   403 
       
   404     if (dbsqlexec(d->dbproc) == FAIL) {
       
   405         setLastError(d->lastError);
       
   406         dbfreebuf(d->dbproc);
       
   407         return false;
       
   408     }
       
   409     if (dbresults(d->dbproc) != SUCCEED) {
       
   410         setLastError(d->lastError);
       
   411         dbfreebuf(d->dbproc);
       
   412         return false;
       
   413     }
       
   414 
       
   415     setSelect((DBCMDROW(d->dbproc) == SUCCEED)); // decide whether or not we are dealing with a SELECT query
       
   416     int numCols = dbnumcols(d->dbproc);
       
   417     if (numCols > 0) {
       
   418         d->buffer.resize(numCols * 2);
       
   419         init(numCols);
       
   420     }
       
   421     for (int i = 0; i < numCols; ++i) {
       
   422         int dbType = dbcoltype(d->dbproc, i+1);
       
   423         QVariant::Type vType = qDecodeTDSType(dbType);
       
   424         QSqlField f(QString::fromAscii(dbcolname(d->dbproc, i+1)), vType);
       
   425         f.setSqlType(dbType);
       
   426         f.setLength(dbcollen(d->dbproc, i+1));
       
   427         d->rec.append(f);
       
   428 
       
   429         RETCODE ret = -1;
       
   430         void* p = 0;
       
   431         switch (vType) {
       
   432         case QVariant::Int:
       
   433             p = malloc(4);
       
   434             ret = dbbind(d->dbproc, i+1, INTBIND, (DBINT) 4, (unsigned char *)p);
       
   435             break;
       
   436         case QVariant::Double:
       
   437             // use string binding to prevent loss of precision
       
   438             p = malloc(50);
       
   439             ret = dbbind(d->dbproc, i+1, STRINGBIND, 50, (unsigned char *)p);
       
   440             break;
       
   441         case QVariant::String:
       
   442             p = malloc(dbcollen(d->dbproc, i+1) + 1);
       
   443             ret = dbbind(d->dbproc, i+1, STRINGBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p);
       
   444             break;
       
   445         case QVariant::DateTime:
       
   446             p = malloc(8);
       
   447             ret = dbbind(d->dbproc, i+1, DATETIMEBIND, (DBINT) 8, (unsigned char *)p);
       
   448             break;
       
   449         case QVariant::ByteArray:
       
   450             p = malloc(dbcollen(d->dbproc, i+1) + 1);
       
   451             ret = dbbind(d->dbproc, i+1, BINARYBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p);
       
   452             break;
       
   453         default: //don't bind the field since we do not support it
       
   454             qWarning("QTDSResult::reset: Unsupported type for field \"%s\"", dbcolname(d->dbproc, i+1));
       
   455             break;
       
   456         }
       
   457         if (ret == SUCCEED) {
       
   458             d->buffer[i * 2] = p;
       
   459             ret = dbnullbind(d->dbproc, i+1, (DBINT*)(&d->buffer[i * 2 + 1]));
       
   460         } else {
       
   461             d->buffer[i * 2] = 0;
       
   462             d->buffer[i * 2 + 1] = 0;
       
   463             free(p);
       
   464         }
       
   465         if ((ret != SUCCEED) && (ret != -1)) {
       
   466             setLastError(d->lastError);
       
   467             return false;
       
   468         }
       
   469     }
       
   470 
       
   471     setActive(true);
       
   472     return true;
       
   473 }
       
   474 
       
   475 int QTDSResult::size()
       
   476 {
       
   477     return -1;
       
   478 }
       
   479 
       
   480 int QTDSResult::numRowsAffected()
       
   481 {
       
   482 #ifdef DBNTWIN32
       
   483     if (dbiscount(d->dbproc)) {
       
   484         return DBCOUNT(d->dbproc);
       
   485     }
       
   486     return -1;
       
   487 #else
       
   488     return DBCOUNT(d->dbproc);
       
   489 #endif
       
   490 }
       
   491 
       
   492 QSqlRecord QTDSResult::record() const
       
   493 {
       
   494     return d->rec;
       
   495 }
       
   496 
       
   497 ///////////////////////////////////////////////////////////////////
       
   498 
       
   499 QTDSDriver::QTDSDriver(QObject* parent)
       
   500     : QSqlDriver(parent)
       
   501 {
       
   502     init();
       
   503 }
       
   504 
       
   505 QTDSDriver::QTDSDriver(LOGINREC* rec, const QString& host, const QString &db, QObject* parent)
       
   506     : QSqlDriver(parent)
       
   507 {
       
   508     init();
       
   509     d->login = rec;
       
   510     d->hostName = host;
       
   511     d->db = db;
       
   512     if (rec) {
       
   513         setOpen(true);
       
   514         setOpenError(false);
       
   515     }
       
   516 }
       
   517 
       
   518 QVariant QTDSDriver::handle() const
       
   519 {
       
   520     return QVariant(qRegisterMetaType<LOGINREC *>("LOGINREC*"), &d->login);
       
   521 }
       
   522 
       
   523 void QTDSDriver::init()
       
   524 {
       
   525     d = new QTDSDriverPrivate();
       
   526     // the following two code-lines will fail compilation on some FreeTDS versions
       
   527     // just comment them out if you have FreeTDS (you won't get any errors and warnings then)
       
   528     dberrhandle((QERRHANDLE)qTdsErrHandler);
       
   529     dbmsghandle((QMSGHANDLE)qTdsMsgHandler);
       
   530 }
       
   531 
       
   532 QTDSDriver::~QTDSDriver()
       
   533 {
       
   534     dberrhandle(0);
       
   535     dbmsghandle(0);
       
   536     // dbexit also calls dbclose if necessary
       
   537     dbexit();
       
   538     delete d;
       
   539 }
       
   540 
       
   541 bool QTDSDriver::hasFeature(DriverFeature f) const
       
   542 {
       
   543     switch (f) {
       
   544     case Transactions:
       
   545     case QuerySize:
       
   546     case Unicode:
       
   547     case SimpleLocking:
       
   548     case EventNotifications:
       
   549     case MultipleResultSets:
       
   550         return false;
       
   551     case BLOB:
       
   552         return true;
       
   553     default:
       
   554         return false;
       
   555     }
       
   556 }
       
   557 
       
   558 bool QTDSDriver::open(const QString & db,
       
   559                        const QString & user,
       
   560                        const QString & password,
       
   561                        const QString & host,
       
   562                        int /*port*/,
       
   563                        const QString& /*connOpts*/)
       
   564 {
       
   565     if (isOpen())
       
   566         close();
       
   567     if (!dbinit()) {
       
   568         setOpenError(true);
       
   569         return false;
       
   570     }
       
   571     d->login = dblogin();
       
   572     if (!d->login) {
       
   573         setOpenError(true);
       
   574         return false;
       
   575     }
       
   576     DBSETLPWD(d->login, const_cast<char*>(password.toLocal8Bit().constData()));
       
   577     DBSETLUSER(d->login, const_cast<char*>(user.toLocal8Bit().constData()));
       
   578 
       
   579     // Now, try to open and use the database. If this fails, return false.
       
   580     DBPROCESS* dbproc;
       
   581 
       
   582     dbproc = dbopen(d->login, const_cast<char*>(host.toLatin1().constData()));
       
   583     if (!dbproc) {
       
   584         setLastError(qMakeError(tr("Unable to open connection"), QSqlError::ConnectionError, -1));
       
   585         setOpenError(true);
       
   586         return false;
       
   587     }
       
   588     if (dbuse(dbproc, const_cast<char*>(db.toLatin1().constData())) == FAIL) {
       
   589         setLastError(qMakeError(tr("Unable to use database"), QSqlError::ConnectionError, -1));
       
   590         setOpenError(true);
       
   591         return false;
       
   592     }
       
   593     dbclose( dbproc );
       
   594 
       
   595     setOpen(true);
       
   596     setOpenError(false);
       
   597     d->hostName = host;
       
   598     d->db = db;
       
   599     return true;
       
   600 }
       
   601 
       
   602 void QTDSDriver::close()
       
   603 {
       
   604     if (isOpen()) {
       
   605 #ifdef Q_USE_SYBASE
       
   606         dbloginfree(d->login);
       
   607 #else
       
   608         dbfreelogin(d->login);
       
   609 #endif
       
   610         d->login = 0;
       
   611         setOpen(false);
       
   612         setOpenError(false);
       
   613     }
       
   614 }
       
   615 
       
   616 QSqlResult *QTDSDriver::createResult() const
       
   617 {
       
   618     return new QTDSResult(this);
       
   619 }
       
   620 
       
   621 bool QTDSDriver::beginTransaction()
       
   622 {
       
   623     return false;
       
   624 /*
       
   625     if (!isOpen()) {
       
   626         qWarning("QTDSDriver::beginTransaction: Database not open");
       
   627         return false;
       
   628     }
       
   629     if (dbcmd(d->dbproc, "BEGIN TRANSACTION") == FAIL) {
       
   630         setLastError(d->lastError);
       
   631         dbfreebuf(d->dbproc);
       
   632         return false;
       
   633     }
       
   634     if (dbsqlexec(d->dbproc) == FAIL) {
       
   635         setLastError(d->lastError);
       
   636         dbfreebuf(d->dbproc);
       
   637         return false;
       
   638     }
       
   639     while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
       
   640     dbfreebuf(d->dbproc);
       
   641     inTransaction = true;
       
   642     return true;
       
   643 */
       
   644 }
       
   645 
       
   646 bool QTDSDriver::commitTransaction()
       
   647 {
       
   648     return false;
       
   649 /*
       
   650     if (!isOpen()) {
       
   651         qWarning("QTDSDriver::commitTransaction: Database not open");
       
   652         return false;
       
   653     }
       
   654     if (dbcmd(d->dbproc, "COMMIT TRANSACTION") == FAIL) {
       
   655         setLastError(d->lastError);
       
   656         dbfreebuf(d->dbproc);
       
   657         return false;
       
   658     }
       
   659     if (dbsqlexec(d->dbproc) == FAIL) {
       
   660         setLastError(d->lastError);
       
   661         dbfreebuf(d->dbproc);
       
   662         return false;
       
   663     }
       
   664     while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
       
   665     dbfreebuf(d->dbproc);
       
   666     inTransaction = false;
       
   667     return true;
       
   668 */
       
   669 }
       
   670 
       
   671 bool QTDSDriver::rollbackTransaction()
       
   672 {
       
   673     return false;
       
   674 /*
       
   675     if (!isOpen()) {
       
   676         qWarning("QTDSDriver::rollbackTransaction: Database not open");
       
   677         return false;
       
   678     }
       
   679     if (dbcmd(d->dbproc, "ROLLBACK TRANSACTION") == FAIL) {
       
   680         setLastError(d->lastError);
       
   681         dbfreebuf(d->dbproc);
       
   682         return false;
       
   683     }
       
   684     if (dbsqlexec(d->dbproc) == FAIL) {
       
   685         setLastError(d->lastError);
       
   686         dbfreebuf(d->dbproc);
       
   687         return false;
       
   688     }
       
   689     while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
       
   690     dbfreebuf(d->dbproc);
       
   691     inTransaction = false;
       
   692     return true;
       
   693 */
       
   694 }
       
   695 
       
   696 QSqlRecord QTDSDriver::record(const QString& tablename) const
       
   697 {
       
   698     QSqlRecord info;
       
   699     if (!isOpen())
       
   700         return info;
       
   701     QSqlQuery t(createResult());
       
   702     t.setForwardOnly(true);
       
   703 
       
   704     QString table = tablename;
       
   705     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
   706         table = stripDelimiters(table, QSqlDriver::TableName);
       
   707 
       
   708     QString stmt (QLatin1String("select name, type, length, prec from syscolumns "
       
   709                    "where id = (select id from sysobjects where name = '%1')"));
       
   710     t.exec(stmt.arg(table));
       
   711     while (t.next()) {
       
   712         QSqlField f(t.value(0).toString().simplified(), qDecodeTDSType(t.value(1).toInt()));
       
   713         f.setLength(t.value(2).toInt());
       
   714         f.setPrecision(t.value(3).toInt());
       
   715         f.setSqlType(t.value(1).toInt());
       
   716         info.append(f);
       
   717     }
       
   718     return info;
       
   719 }
       
   720 
       
   721 QStringList QTDSDriver::tables(QSql::TableType type) const
       
   722 {
       
   723     QStringList list;
       
   724 
       
   725     if (!isOpen())
       
   726         return list;
       
   727 
       
   728     QStringList typeFilter;
       
   729 
       
   730     if (type & QSql::Tables)
       
   731         typeFilter += QLatin1String("type='U'");
       
   732     if (type & QSql::SystemTables)
       
   733         typeFilter += QLatin1String("type='S'");
       
   734     if (type & QSql::Views)
       
   735         typeFilter += QLatin1String("type='V'");
       
   736 
       
   737     if (typeFilter.isEmpty())
       
   738         return list;
       
   739 
       
   740     QSqlQuery t(createResult());
       
   741     t.setForwardOnly(true);
       
   742     t.exec(QLatin1String("select name from sysobjects where ") + typeFilter.join(QLatin1String(" or ")));
       
   743     while (t.next())
       
   744         list.append(t.value(0).toString().simplified());
       
   745 
       
   746     return list;
       
   747 }
       
   748 
       
   749 QString QTDSDriver::formatValue(const QSqlField &field,
       
   750                                   bool trim) const
       
   751 {
       
   752     QString r;
       
   753     if (field.isNull())
       
   754         r = QLatin1String("NULL");
       
   755     else if (field.type() == QVariant::DateTime) {
       
   756         if (field.value().toDateTime().isValid()){
       
   757             r = field.value().toDateTime().toString(QLatin1String("yyyyMMdd hh:mm:ss"));
       
   758             r.prepend(QLatin1String("'"));
       
   759             r.append(QLatin1String("'"));
       
   760         } else
       
   761             r = QLatin1String("NULL");
       
   762     } else if (field.type() == QVariant::ByteArray) {
       
   763         QByteArray ba = field.value().toByteArray();
       
   764         QString res;
       
   765         static const char hexchars[] = "0123456789abcdef";
       
   766         for (int i = 0; i < ba.size(); ++i) {
       
   767             uchar s = (uchar) ba[i];
       
   768             res += QLatin1Char(hexchars[s >> 4]);
       
   769             res += QLatin1Char(hexchars[s & 0x0f]);
       
   770         }
       
   771         r = QLatin1String("0x") + res;
       
   772     } else {
       
   773         r = QSqlDriver::formatValue(field, trim);
       
   774     }
       
   775     return r;
       
   776 }
       
   777 
       
   778 QSqlIndex QTDSDriver::primaryIndex(const QString& tablename) const
       
   779 {
       
   780     QSqlRecord rec = record(tablename);
       
   781 
       
   782     QString table = tablename;
       
   783     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
   784         table = stripDelimiters(table, QSqlDriver::TableName);
       
   785 
       
   786     QSqlIndex idx(table);
       
   787     if ((!isOpen()) || (table.isEmpty()))
       
   788         return QSqlIndex();
       
   789 
       
   790     QSqlQuery t(createResult());
       
   791     t.setForwardOnly(true);
       
   792     t.exec(QString::fromLatin1("sp_helpindex '%1'").arg(table));
       
   793     if (t.next()) {
       
   794         QStringList fNames = t.value(2).toString().simplified().split(QLatin1Char(','));
       
   795         QRegExp regx(QLatin1String("\\s*(\\S+)(?:\\s+(DESC|desc))?\\s*"));
       
   796         for(QStringList::Iterator it = fNames.begin(); it != fNames.end(); ++it) {
       
   797             regx.indexIn(*it);
       
   798             QSqlField f(regx.cap(1), rec.field(regx.cap(1)).type());
       
   799             if (regx.cap(2).toLower() == QLatin1String("desc")) {
       
   800                 idx.append(f, true);
       
   801             } else {
       
   802                 idx.append(f, false);
       
   803             }
       
   804         }
       
   805         idx.setName(t.value(0).toString().simplified());
       
   806     }
       
   807     return idx;
       
   808 }
       
   809 
       
   810 QString QTDSDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
       
   811 {
       
   812     QString res = identifier;
       
   813     if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
       
   814         res.replace(QLatin1Char('"'), QLatin1String("\"\""));
       
   815         res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
       
   816         res.replace(QLatin1Char('.'), QLatin1String("\".\""));
       
   817     }
       
   818     return res;
       
   819 }
       
   820 
       
   821 QT_END_NAMESPACE