src/sql/drivers/tds/qsql_tds.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sql/drivers/tds/qsql_tds.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,821 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSql module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+#ifdef Q_OS_WIN32    // We assume that MS SQL Server is used. Set Q_USE_SYBASE to force Sybase.
+// Conflicting declarations of LPCBYTE in sqlfront.h and winscard.h
+#define _WINSCARD_H_
+#include <windows.h>
+#else
+#define Q_USE_SYBASE
+#endif
+
+#include "qsql_tds.h"
+
+#include <qvariant.h>
+#include <qdatetime.h>
+#include <qhash.h>
+#include <qregexp.h>
+#include <qsqlerror.h>
+#include <qsqlfield.h>
+#include <qsqlindex.h>
+#include <qsqlquery.h>
+#include <qstringlist.h>
+#include <qvector.h>
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef DBNTWIN32
+#define QMSGHANDLE DBMSGHANDLE_PROC
+#define QERRHANDLE DBERRHANDLE_PROC
+#define QTDSCHAR SQLCHAR
+#define QTDSDATETIME4 SQLDATETIM4
+#define QTDSDATETIME SQLDATETIME
+#define QTDSDATETIME_N SQLDATETIMN
+#define QTDSDECIMAL SQLDECIMAL
+#define QTDSFLT4 SQLFLT4
+#define QTDSFLT8 SQLFLT8
+#define QTDSFLT8_N SQLFLTN
+#define QTDSINT1 SQLINT1
+#define QTDSINT2 SQLINT2
+#define QTDSINT4 SQLINT4
+#define QTDSINT4_N SQLINTN
+#define QTDSMONEY4 SQLMONEY4
+#define QTDSMONEY SQLMONEY
+#define QTDSMONEY_N SQLMONEYN
+#define QTDSNUMERIC SQLNUMERIC
+#define QTDSTEXT SQLTEXT
+#define QTDSVARCHAR SQLVARCHAR
+#define QTDSBIT SQLBIT
+#define QTDSBINARY SQLBINARY
+#define QTDSVARBINARY SQLVARBINARY
+#define QTDSIMAGE SQLIMAGE
+#else
+#define QMSGHANDLE MHANDLEFUNC
+#define QERRHANDLE EHANDLEFUNC
+#define QTDSCHAR SYBCHAR
+#define QTDSDATETIME4 SYBDATETIME4
+#define QTDSDATETIME SYBDATETIME
+#define QTDSDATETIME_N SYBDATETIMN
+#define QTDSDECIMAL SYBDECIMAL
+#define QTDSFLT8 SYBFLT8
+#define QTDSFLT8_N SYBFLTN
+#define QTDSFLT4 SYBREAL
+#define QTDSINT1 SYBINT1
+#define QTDSINT2 SYBINT2
+#define QTDSINT4 SYBINT4
+#define QTDSINT4_N SYBINTN
+#define QTDSMONEY4 SYBMONEY4
+#define QTDSMONEY SYBMONEY
+#define QTDSMONEY_N SYBMONEYN
+#define QTDSNUMERIC SYBNUMERIC
+#define QTDSTEXT SYBTEXT
+#define QTDSVARCHAR SYBVARCHAR
+#define QTDSBIT SYBBIT
+#define QTDSBINARY SYBBINARY
+#define QTDSVARBINARY SYBVARBINARY
+#define QTDSIMAGE SYBIMAGE
+// magic numbers not defined anywhere in Sybase headers
+#define QTDSDECIMAL_2 55
+#define QTDSNUMERIC_2 63
+#endif  //DBNTWIN32
+
+#define TDS_CURSOR_SIZE 50
+
+// workaround for FreeTDS
+#ifndef CS_PUBLIC
+#define CS_PUBLIC
+#endif
+
+QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, int errNo = -1)
+{
+    return QSqlError(QLatin1String("QTDS: ") + err, QString(), type, errNo);
+}
+
+class QTDSDriverPrivate
+{
+public:
+    QTDSDriverPrivate(): login(0) {}
+    LOGINREC* login;  // login information
+    QString hostName;
+    QString db;
+};
+
+
+class QTDSResultPrivate
+{
+public:
+    QTDSResultPrivate():login(0), dbproc(0) {}
+    LOGINREC* login;  // login information
+    DBPROCESS* dbproc; // connection from app to server
+    QSqlError lastError;
+    void addErrorMsg(QString& errMsg) { errorMsgs.append(errMsg); }
+    QString getErrorMsgs() { return errorMsgs.join(QLatin1String("\n")); }
+    void clearErrorMsgs() { errorMsgs.clear(); }
+    QVector<void *> buffer;
+    QSqlRecord rec;
+
+private:
+    QStringList errorMsgs;
+};
+
+typedef QHash<DBPROCESS *, QTDSResultPrivate *> QTDSErrorHash;
+Q_GLOBAL_STATIC(QTDSErrorHash, errs)
+
+extern "C" {
+static int CS_PUBLIC qTdsMsgHandler (DBPROCESS* dbproc,
+                            DBINT /*msgno*/,
+                            int msgstate,
+                            int severity,
+                            char* msgtext,
+                            char* /*srvname*/,
+                            char* /*procname*/,
+                            int /*line*/)
+{
+    QTDSResultPrivate* p = errs()->value(dbproc);
+
+    if (!p) {
+//        ### umm... temporary disabled since this throws a lot of warnings...
+//        qWarning("QTDSDriver warning (%d): [%s] from server [%s]", msgstate, msgtext, srvname);
+        return INT_CANCEL;
+    }
+
+    if (severity > 0) {
+        QString errMsg = QString::fromLatin1("%1 (%2)").arg(QString::fromAscii(msgtext)).arg(
+                                    msgstate);
+        p->addErrorMsg(errMsg);
+    }
+
+    return INT_CANCEL;
+}
+
+static int CS_PUBLIC qTdsErrHandler(DBPROCESS* dbproc,
+                                int /*severity*/,
+                                int dberr,
+                                int /*oserr*/,
+                                char* dberrstr,
+                                char* oserrstr)
+{
+    QTDSResultPrivate* p = errs()->value(dbproc);
+    if (!p) {
+        qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr);
+        return INT_CANCEL;
+    }
+    /*
+     * If the process is dead or NULL and
+     * we are not in the middle of logging in...
+     */
+    if((dbproc == NULL || DBDEAD(dbproc))) {
+        qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr);
+        return INT_CANCEL;
+    }
+
+
+    QString errMsg = QString::fromLatin1("%1 %2\n").arg(QLatin1String(dberrstr)).arg(
+                                QLatin1String(oserrstr));
+    errMsg += p->getErrorMsgs();
+    p->lastError = qMakeError(errMsg, QSqlError::UnknownError, dberr);
+    p->clearErrorMsgs();
+
+    return INT_CANCEL ;
+}
+
+} //extern "C"
+
+
+QVariant::Type qDecodeTDSType(int type)
+{
+    QVariant::Type t = QVariant::Invalid;
+    switch (type) {
+    case QTDSCHAR:
+    case QTDSTEXT:
+    case QTDSVARCHAR:
+        t = QVariant::String;
+        break;
+    case QTDSINT1:
+    case QTDSINT2:
+    case QTDSINT4:
+    case QTDSINT4_N:
+    case QTDSBIT:
+        t = QVariant::Int;
+        break;
+    case QTDSFLT4:
+    case QTDSFLT8:
+    case QTDSFLT8_N:
+    case QTDSMONEY4:
+    case QTDSMONEY:
+    case QTDSDECIMAL:
+    case QTDSNUMERIC:
+#ifdef QTDSNUMERIC_2
+    case QTDSNUMERIC_2:
+#endif
+#ifdef QTDSDECIMAL_2
+    case QTDSDECIMAL_2:
+#endif
+    case QTDSMONEY_N:
+        t = QVariant::Double;
+        break;
+    case QTDSDATETIME4:
+    case QTDSDATETIME:
+    case QTDSDATETIME_N:
+        t = QVariant::DateTime;
+        break;
+    case QTDSBINARY:
+    case QTDSVARBINARY:
+    case QTDSIMAGE:
+        t = QVariant::ByteArray;
+        break;
+    default:
+        t = QVariant::Invalid;
+        break;
+    }
+    return t;
+}
+
+QVariant::Type qFieldType(QTDSResultPrivate* d, int i)
+{
+    QVariant::Type type = qDecodeTDSType(dbcoltype(d->dbproc, i+1));
+    return type;
+}
+
+
+QTDSResult::QTDSResult(const QTDSDriver* db)
+    : QSqlCachedResult(db)
+{
+    d = new QTDSResultPrivate();
+    d->login = db->d->login;
+
+    d->dbproc = dbopen(d->login, const_cast<char*>(db->d->hostName.toLatin1().constData()));
+    if (!d->dbproc)
+        return;
+    if (dbuse(d->dbproc, const_cast<char*>(db->d->db.toLatin1().constData())) == FAIL)
+        return;
+
+    // insert d in error handler dict
+    errs()->insert(d->dbproc, d);
+    dbcmd(d->dbproc, "set quoted_identifier on");
+    dbsqlexec(d->dbproc);
+}
+
+QTDSResult::~QTDSResult()
+{
+    cleanup();
+    if (d->dbproc)
+        dbclose(d->dbproc);
+    errs()->remove(d->dbproc);
+    delete d;
+}
+
+void QTDSResult::cleanup()
+{
+    d->clearErrorMsgs();
+    d->rec.clear();
+    for (int i = 0; i < d->buffer.size() / 2; ++i)
+        free(d->buffer.at(i * 2));
+    d->buffer.clear();
+    // "can" stands for "cancel"... very clever.
+    dbcanquery(d->dbproc);
+    dbfreebuf(d->dbproc);
+
+    QSqlCachedResult::cleanup();
+}
+
+QVariant QTDSResult::handle() const
+{
+    return QVariant(qRegisterMetaType<DBPROCESS *>("DBPROCESS*"), &d->dbproc);
+}
+
+static inline bool qIsNull(const void *ind)
+{
+    return *reinterpret_cast<const DBINT *>(&ind) == -1;
+}
+
+bool QTDSResult::gotoNext(QSqlCachedResult::ValueCache &values, int index)
+{
+    STATUS stat = dbnextrow(d->dbproc);
+    if (stat == NO_MORE_ROWS) {
+        setAt(QSql::AfterLastRow);
+        return false;
+    }
+    if ((stat == FAIL) || (stat == BUF_FULL)) {
+        setLastError(d->lastError);
+        return false;
+    }
+
+    if (index < 0)
+        return true;
+
+    for (int i = 0; i < d->rec.count(); ++i) {
+        int idx = index + i;
+        switch (d->rec.field(i).type()) {
+            case QVariant::DateTime:
+                if (qIsNull(d->buffer.at(i * 2 + 1))) {
+                    values[idx] = QVariant(QVariant::DateTime);
+                } else {
+                    DBDATETIME *bdt = (DBDATETIME*) d->buffer.at(i * 2);
+                    QDate date = QDate::fromString(QLatin1String("1900-01-01"), Qt::ISODate);
+                    QTime time = QTime::fromString(QLatin1String("00:00:00"), Qt::ISODate);
+                    values[idx] = QDateTime(date.addDays(bdt->dtdays), time.addMSecs(int(bdt->dttime / 0.3)));
+                }
+                break;
+            case QVariant::Int:
+                if (qIsNull(d->buffer.at(i * 2 + 1)))
+                    values[idx] = QVariant(QVariant::Int);
+                else
+                    values[idx] = *((int*)d->buffer.at(i * 2));
+                break;
+            case QVariant::Double:
+            case QVariant::String:
+                if (qIsNull(d->buffer.at(i * 2 + 1)))
+                    values[idx] = QVariant(QVariant::String);
+                else
+                    values[idx] = QString::fromLocal8Bit((const char*)d->buffer.at(i * 2)).trimmed();
+                break;
+            case QVariant::ByteArray: {
+                if (qIsNull(d->buffer.at(i * 2 + 1)))
+                    values[idx] = QVariant(QVariant::ByteArray);
+                else
+                    values[idx] = QByteArray((const char*)d->buffer.at(i * 2));
+                break;
+            }
+            default:
+                // should never happen, and we already fired
+                // a warning while binding.
+                values[idx] = QVariant();
+                break;
+        }
+    }
+
+    return true;
+}
+
+bool QTDSResult::reset (const QString& query)
+{
+    cleanup();
+    if (!driver() || !driver()-> isOpen() || driver()->isOpenError())
+        return false;
+    setActive(false);
+    setAt(QSql::BeforeFirstRow);
+    if (dbcmd(d->dbproc, const_cast<char*>(query.toLocal8Bit().constData())) == FAIL) {
+        setLastError(d->lastError);
+        return false;
+    }
+
+    if (dbsqlexec(d->dbproc) == FAIL) {
+        setLastError(d->lastError);
+        dbfreebuf(d->dbproc);
+        return false;
+    }
+    if (dbresults(d->dbproc) != SUCCEED) {
+        setLastError(d->lastError);
+        dbfreebuf(d->dbproc);
+        return false;
+    }
+
+    setSelect((DBCMDROW(d->dbproc) == SUCCEED)); // decide whether or not we are dealing with a SELECT query
+    int numCols = dbnumcols(d->dbproc);
+    if (numCols > 0) {
+        d->buffer.resize(numCols * 2);
+        init(numCols);
+    }
+    for (int i = 0; i < numCols; ++i) {
+        int dbType = dbcoltype(d->dbproc, i+1);
+        QVariant::Type vType = qDecodeTDSType(dbType);
+        QSqlField f(QString::fromAscii(dbcolname(d->dbproc, i+1)), vType);
+        f.setSqlType(dbType);
+        f.setLength(dbcollen(d->dbproc, i+1));
+        d->rec.append(f);
+
+        RETCODE ret = -1;
+        void* p = 0;
+        switch (vType) {
+        case QVariant::Int:
+            p = malloc(4);
+            ret = dbbind(d->dbproc, i+1, INTBIND, (DBINT) 4, (unsigned char *)p);
+            break;
+        case QVariant::Double:
+            // use string binding to prevent loss of precision
+            p = malloc(50);
+            ret = dbbind(d->dbproc, i+1, STRINGBIND, 50, (unsigned char *)p);
+            break;
+        case QVariant::String:
+            p = malloc(dbcollen(d->dbproc, i+1) + 1);
+            ret = dbbind(d->dbproc, i+1, STRINGBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p);
+            break;
+        case QVariant::DateTime:
+            p = malloc(8);
+            ret = dbbind(d->dbproc, i+1, DATETIMEBIND, (DBINT) 8, (unsigned char *)p);
+            break;
+        case QVariant::ByteArray:
+            p = malloc(dbcollen(d->dbproc, i+1) + 1);
+            ret = dbbind(d->dbproc, i+1, BINARYBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p);
+            break;
+        default: //don't bind the field since we do not support it
+            qWarning("QTDSResult::reset: Unsupported type for field \"%s\"", dbcolname(d->dbproc, i+1));
+            break;
+        }
+        if (ret == SUCCEED) {
+            d->buffer[i * 2] = p;
+            ret = dbnullbind(d->dbproc, i+1, (DBINT*)(&d->buffer[i * 2 + 1]));
+        } else {
+            d->buffer[i * 2] = 0;
+            d->buffer[i * 2 + 1] = 0;
+            free(p);
+        }
+        if ((ret != SUCCEED) && (ret != -1)) {
+            setLastError(d->lastError);
+            return false;
+        }
+    }
+
+    setActive(true);
+    return true;
+}
+
+int QTDSResult::size()
+{
+    return -1;
+}
+
+int QTDSResult::numRowsAffected()
+{
+#ifdef DBNTWIN32
+    if (dbiscount(d->dbproc)) {
+        return DBCOUNT(d->dbproc);
+    }
+    return -1;
+#else
+    return DBCOUNT(d->dbproc);
+#endif
+}
+
+QSqlRecord QTDSResult::record() const
+{
+    return d->rec;
+}
+
+///////////////////////////////////////////////////////////////////
+
+QTDSDriver::QTDSDriver(QObject* parent)
+    : QSqlDriver(parent)
+{
+    init();
+}
+
+QTDSDriver::QTDSDriver(LOGINREC* rec, const QString& host, const QString &db, QObject* parent)
+    : QSqlDriver(parent)
+{
+    init();
+    d->login = rec;
+    d->hostName = host;
+    d->db = db;
+    if (rec) {
+        setOpen(true);
+        setOpenError(false);
+    }
+}
+
+QVariant QTDSDriver::handle() const
+{
+    return QVariant(qRegisterMetaType<LOGINREC *>("LOGINREC*"), &d->login);
+}
+
+void QTDSDriver::init()
+{
+    d = new QTDSDriverPrivate();
+    // the following two code-lines will fail compilation on some FreeTDS versions
+    // just comment them out if you have FreeTDS (you won't get any errors and warnings then)
+    dberrhandle((QERRHANDLE)qTdsErrHandler);
+    dbmsghandle((QMSGHANDLE)qTdsMsgHandler);
+}
+
+QTDSDriver::~QTDSDriver()
+{
+    dberrhandle(0);
+    dbmsghandle(0);
+    // dbexit also calls dbclose if necessary
+    dbexit();
+    delete d;
+}
+
+bool QTDSDriver::hasFeature(DriverFeature f) const
+{
+    switch (f) {
+    case Transactions:
+    case QuerySize:
+    case Unicode:
+    case SimpleLocking:
+    case EventNotifications:
+    case MultipleResultSets:
+        return false;
+    case BLOB:
+        return true;
+    default:
+        return false;
+    }
+}
+
+bool QTDSDriver::open(const QString & db,
+                       const QString & user,
+                       const QString & password,
+                       const QString & host,
+                       int /*port*/,
+                       const QString& /*connOpts*/)
+{
+    if (isOpen())
+        close();
+    if (!dbinit()) {
+        setOpenError(true);
+        return false;
+    }
+    d->login = dblogin();
+    if (!d->login) {
+        setOpenError(true);
+        return false;
+    }
+    DBSETLPWD(d->login, const_cast<char*>(password.toLocal8Bit().constData()));
+    DBSETLUSER(d->login, const_cast<char*>(user.toLocal8Bit().constData()));
+
+    // Now, try to open and use the database. If this fails, return false.
+    DBPROCESS* dbproc;
+
+    dbproc = dbopen(d->login, const_cast<char*>(host.toLatin1().constData()));
+    if (!dbproc) {
+        setLastError(qMakeError(tr("Unable to open connection"), QSqlError::ConnectionError, -1));
+        setOpenError(true);
+        return false;
+    }
+    if (dbuse(dbproc, const_cast<char*>(db.toLatin1().constData())) == FAIL) {
+        setLastError(qMakeError(tr("Unable to use database"), QSqlError::ConnectionError, -1));
+        setOpenError(true);
+        return false;
+    }
+    dbclose( dbproc );
+
+    setOpen(true);
+    setOpenError(false);
+    d->hostName = host;
+    d->db = db;
+    return true;
+}
+
+void QTDSDriver::close()
+{
+    if (isOpen()) {
+#ifdef Q_USE_SYBASE
+        dbloginfree(d->login);
+#else
+        dbfreelogin(d->login);
+#endif
+        d->login = 0;
+        setOpen(false);
+        setOpenError(false);
+    }
+}
+
+QSqlResult *QTDSDriver::createResult() const
+{
+    return new QTDSResult(this);
+}
+
+bool QTDSDriver::beginTransaction()
+{
+    return false;
+/*
+    if (!isOpen()) {
+        qWarning("QTDSDriver::beginTransaction: Database not open");
+        return false;
+    }
+    if (dbcmd(d->dbproc, "BEGIN TRANSACTION") == FAIL) {
+        setLastError(d->lastError);
+        dbfreebuf(d->dbproc);
+        return false;
+    }
+    if (dbsqlexec(d->dbproc) == FAIL) {
+        setLastError(d->lastError);
+        dbfreebuf(d->dbproc);
+        return false;
+    }
+    while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
+    dbfreebuf(d->dbproc);
+    inTransaction = true;
+    return true;
+*/
+}
+
+bool QTDSDriver::commitTransaction()
+{
+    return false;
+/*
+    if (!isOpen()) {
+        qWarning("QTDSDriver::commitTransaction: Database not open");
+        return false;
+    }
+    if (dbcmd(d->dbproc, "COMMIT TRANSACTION") == FAIL) {
+        setLastError(d->lastError);
+        dbfreebuf(d->dbproc);
+        return false;
+    }
+    if (dbsqlexec(d->dbproc) == FAIL) {
+        setLastError(d->lastError);
+        dbfreebuf(d->dbproc);
+        return false;
+    }
+    while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
+    dbfreebuf(d->dbproc);
+    inTransaction = false;
+    return true;
+*/
+}
+
+bool QTDSDriver::rollbackTransaction()
+{
+    return false;
+/*
+    if (!isOpen()) {
+        qWarning("QTDSDriver::rollbackTransaction: Database not open");
+        return false;
+    }
+    if (dbcmd(d->dbproc, "ROLLBACK TRANSACTION") == FAIL) {
+        setLastError(d->lastError);
+        dbfreebuf(d->dbproc);
+        return false;
+    }
+    if (dbsqlexec(d->dbproc) == FAIL) {
+        setLastError(d->lastError);
+        dbfreebuf(d->dbproc);
+        return false;
+    }
+    while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
+    dbfreebuf(d->dbproc);
+    inTransaction = false;
+    return true;
+*/
+}
+
+QSqlRecord QTDSDriver::record(const QString& tablename) const
+{
+    QSqlRecord info;
+    if (!isOpen())
+        return info;
+    QSqlQuery t(createResult());
+    t.setForwardOnly(true);
+
+    QString table = tablename;
+    if (isIdentifierEscaped(table, QSqlDriver::TableName))
+        table = stripDelimiters(table, QSqlDriver::TableName);
+
+    QString stmt (QLatin1String("select name, type, length, prec from syscolumns "
+                   "where id = (select id from sysobjects where name = '%1')"));
+    t.exec(stmt.arg(table));
+    while (t.next()) {
+        QSqlField f(t.value(0).toString().simplified(), qDecodeTDSType(t.value(1).toInt()));
+        f.setLength(t.value(2).toInt());
+        f.setPrecision(t.value(3).toInt());
+        f.setSqlType(t.value(1).toInt());
+        info.append(f);
+    }
+    return info;
+}
+
+QStringList QTDSDriver::tables(QSql::TableType type) const
+{
+    QStringList list;
+
+    if (!isOpen())
+        return list;
+
+    QStringList typeFilter;
+
+    if (type & QSql::Tables)
+        typeFilter += QLatin1String("type='U'");
+    if (type & QSql::SystemTables)
+        typeFilter += QLatin1String("type='S'");
+    if (type & QSql::Views)
+        typeFilter += QLatin1String("type='V'");
+
+    if (typeFilter.isEmpty())
+        return list;
+
+    QSqlQuery t(createResult());
+    t.setForwardOnly(true);
+    t.exec(QLatin1String("select name from sysobjects where ") + typeFilter.join(QLatin1String(" or ")));
+    while (t.next())
+        list.append(t.value(0).toString().simplified());
+
+    return list;
+}
+
+QString QTDSDriver::formatValue(const QSqlField &field,
+                                  bool trim) const
+{
+    QString r;
+    if (field.isNull())
+        r = QLatin1String("NULL");
+    else if (field.type() == QVariant::DateTime) {
+        if (field.value().toDateTime().isValid()){
+            r = field.value().toDateTime().toString(QLatin1String("yyyyMMdd hh:mm:ss"));
+            r.prepend(QLatin1String("'"));
+            r.append(QLatin1String("'"));
+        } else
+            r = QLatin1String("NULL");
+    } else if (field.type() == QVariant::ByteArray) {
+        QByteArray ba = field.value().toByteArray();
+        QString res;
+        static const char hexchars[] = "0123456789abcdef";
+        for (int i = 0; i < ba.size(); ++i) {
+            uchar s = (uchar) ba[i];
+            res += QLatin1Char(hexchars[s >> 4]);
+            res += QLatin1Char(hexchars[s & 0x0f]);
+        }
+        r = QLatin1String("0x") + res;
+    } else {
+        r = QSqlDriver::formatValue(field, trim);
+    }
+    return r;
+}
+
+QSqlIndex QTDSDriver::primaryIndex(const QString& tablename) const
+{
+    QSqlRecord rec = record(tablename);
+
+    QString table = tablename;
+    if (isIdentifierEscaped(table, QSqlDriver::TableName))
+        table = stripDelimiters(table, QSqlDriver::TableName);
+
+    QSqlIndex idx(table);
+    if ((!isOpen()) || (table.isEmpty()))
+        return QSqlIndex();
+
+    QSqlQuery t(createResult());
+    t.setForwardOnly(true);
+    t.exec(QString::fromLatin1("sp_helpindex '%1'").arg(table));
+    if (t.next()) {
+        QStringList fNames = t.value(2).toString().simplified().split(QLatin1Char(','));
+        QRegExp regx(QLatin1String("\\s*(\\S+)(?:\\s+(DESC|desc))?\\s*"));
+        for(QStringList::Iterator it = fNames.begin(); it != fNames.end(); ++it) {
+            regx.indexIn(*it);
+            QSqlField f(regx.cap(1), rec.field(regx.cap(1)).type());
+            if (regx.cap(2).toLower() == QLatin1String("desc")) {
+                idx.append(f, true);
+            } else {
+                idx.append(f, false);
+            }
+        }
+        idx.setName(t.value(0).toString().simplified());
+    }
+    return idx;
+}
+
+QString QTDSDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
+{
+    QString res = identifier;
+    if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
+        res.replace(QLatin1Char('"'), QLatin1String("\"\""));
+        res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
+        res.replace(QLatin1Char('.'), QLatin1String("\".\""));
+    }
+    return res;
+}
+
+QT_END_NAMESPACE