From 0452c25aedb7d2778e5b26fad924ed61b72bbe33 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 18 Mar 2016 13:35:40 +0100 Subject: Always compile sql drivers as plugins Compiling the drivers into Qt Sql does not make a lot of sense anymore, as we handle plugins well enough in the build system these days. [ChangeLog][Build system] SQL drivers are now always compiled as plugins. Change-Id: I364b82a480849399d1fafe4b20e9f08922569260 Reviewed-by: Oswald Buddenhagen Reviewed-by: Mark Brand --- src/plugins/sqldrivers/tds/qsql_tds.cpp | 881 ++++++++++++++++++++++++++++++++ 1 file changed, 881 insertions(+) create mode 100644 src/plugins/sqldrivers/tds/qsql_tds.cpp (limited to 'src/plugins/sqldrivers/tds/qsql_tds.cpp') diff --git a/src/plugins/sqldrivers/tds/qsql_tds.cpp b/src/plugins/sqldrivers/tds/qsql_tds.cpp new file mode 100644 index 0000000000..10d9fe7298 --- /dev/null +++ b/src/plugins/sqldrivers/tds/qsql_tds.cpp @@ -0,0 +1,881 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsql_tds_p.h" + +#include +#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 +#else +#define Q_USE_SYBASE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +Q_DECLARE_OPAQUE_POINTER(LOGINREC*) +Q_DECLARE_OPAQUE_POINTER(DBPROCESS*) + +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 QSqlDriverPrivate +{ + Q_DECLARE_PUBLIC(QTDSDriver) + +public: + QTDSDriverPrivate() : QSqlDriverPrivate(), login(0), initialized(false) { dbmsType = QSqlDriver::Sybase; } + LOGINREC* login; // login information + QString hostName; + QString db; + bool initialized; +}; + +struct QTDSColumnData +{ + void *data; + DBINT nullbind; +}; +Q_DECLARE_TYPEINFO(QTDSColumnData, Q_MOVABLE_TYPE); + +class QTDSResultPrivate; + +class QTDSResult : public QSqlCachedResult +{ + Q_DECLARE_PRIVATE(QTDSResult) + +public: + explicit QTDSResult(const QTDSDriver* db); + ~QTDSResult(); + QVariant handle() const; + +protected: + void cleanup(); + bool reset(const QString &query) Q_DECL_OVERRIDE; + int size() Q_DECL_OVERRIDE; + int numRowsAffected() Q_DECL_OVERRIDE; + bool gotoNext(QSqlCachedResult::ValueCache &values, int index) Q_DECL_OVERRIDE; + QSqlRecord record() const Q_DECL_OVERRIDE; +}; + +class QTDSResultPrivate: public QSqlCachedResultPrivate +{ + Q_DECLARE_PUBLIC(QTDSResult) + +public: + Q_DECLARE_SQLDRIVER_PRIVATE(QTDSDriver) + QTDSResultPrivate(QTDSResult *q, const QTDSDriver *drv) + : QSqlCachedResultPrivate(q, drv), + 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 buffer; + QSqlRecord rec; + +private: + QStringList errorMsgs; +}; + +typedef QHash 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 (Msg %2, Level %3, State %4, Server %5, Line %6)") + .arg(QString::fromLatin1(msgtext)) + .arg(msgno) + .arg(severity) + .arg(msgstate) + .arg(QString::fromLatin1(srvname)) + .arg(line); + p->addErrorMsg(errMsg); + if (severity > 10) { + // Severe messages are really errors in the sense of lastError + errMsg = p->getErrorMsgs(); + p->lastError = qMakeError(errMsg, QSqlError::UnknownError, msgno); + p->clearErrorMsgs(); + } + } + + 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(*new QTDSResultPrivate(this, db)) +{ + Q_D(QTDSResult); + d->login = d->drv_d_func()->login; + + d->dbproc = dbopen(d->login, const_cast(d->drv_d_func()->hostName.toLatin1().constData())); + if (!d->dbproc) + return; + if (dbuse(d->dbproc, const_cast(d->drv_d_func()->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() +{ + Q_D(QTDSResult); + cleanup(); + if (d->dbproc) + dbclose(d->dbproc); + errs()->remove(d->dbproc); +} + +void QTDSResult::cleanup() +{ + Q_D(QTDSResult); + d->clearErrorMsgs(); + d->rec.clear(); + for (int i = 0; i < d->buffer.size(); ++i) + free(d->buffer.at(i).data); + d->buffer.clear(); + // "can" stands for "cancel"... very clever. + dbcanquery(d->dbproc); + dbfreebuf(d->dbproc); + + QSqlCachedResult::cleanup(); +} + +QVariant QTDSResult::handle() const +{ + Q_D(const QTDSResult); + return QVariant(qRegisterMetaType("DBPROCESS*"), &d->dbproc); +} + +static inline bool qIsNull(const QTDSColumnData &p) +{ + return p.nullbind == -1; +} + +bool QTDSResult::gotoNext(QSqlCachedResult::ValueCache &values, int index) +{ + Q_D(QTDSResult); + 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))) { + values[idx] = QVariant(QVariant::DateTime); + } else { + DBDATETIME *bdt = (DBDATETIME*) d->buffer.at(i).data; + 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))) + values[idx] = QVariant(QVariant::Int); + else + values[idx] = *((int*)d->buffer.at(i).data); + break; + case QVariant::Double: + case QVariant::String: + if (qIsNull(d->buffer.at(i))) + values[idx] = QVariant(QVariant::String); + else + values[idx] = QString::fromLocal8Bit((const char*)d->buffer.at(i).data).trimmed(); + break; + case QVariant::ByteArray: { + if (qIsNull(d->buffer.at(i))) + values[idx] = QVariant(QVariant::ByteArray); + else + values[idx] = QByteArray((const char*)d->buffer.at(i).data); + 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) +{ + Q_D(QTDSResult); + cleanup(); + if (!driver() || !driver()-> isOpen() || driver()->isOpenError()) + return false; + setActive(false); + setAt(QSql::BeforeFirstRow); + if (dbcmd(d->dbproc, const_cast(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); + init(numCols); + } + for (int i = 0; i < numCols; ++i) { + int dbType = dbcoltype(d->dbproc, i+1); + QVariant::Type vType = qDecodeTDSType(dbType); + QSqlField f(QString::fromLatin1(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].data = p; + ret = dbnullbind(d->dbproc, i+1, &d->buffer[i].nullbind); + } else { + d->buffer[i].data = 0; + d->buffer[i].nullbind = 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() +{ + Q_D(const QTDSResult); +#ifdef DBNTWIN32 + if (dbiscount(d->dbproc)) { + return DBCOUNT(d->dbproc); + } + return -1; +#else + return DBCOUNT(d->dbproc); +#endif +} + +QSqlRecord QTDSResult::record() const +{ + Q_D(const QTDSResult); + return d->rec; +} + +/////////////////////////////////////////////////////////////////// + +QTDSDriver::QTDSDriver(QObject* parent) + : QSqlDriver(*new QTDSDriverPrivate, parent) +{ + init(); +} + +QTDSDriver::QTDSDriver(LOGINREC* rec, const QString& host, const QString &db, QObject* parent) + : QSqlDriver(*new QTDSDriverPrivate, parent) +{ + Q_D(QTDSDriver); + init(); + d->login = rec; + d->hostName = host; + d->db = db; + if (rec) { + setOpen(true); + setOpenError(false); + } +} + +QVariant QTDSDriver::handle() const +{ + Q_D(const QTDSDriver); + return QVariant(qRegisterMetaType("LOGINREC*"), &d->login); +} + +void QTDSDriver::init() +{ + Q_D(QTDSDriver); + d->initialized = (dbinit() == SUCCEED); + // 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(); +} + +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*/) +{ + Q_D(QTDSDriver); + if (isOpen()) + close(); + if (!d->initialized) { + setOpenError(true); + return false; + } + d->login = dblogin(); + if (!d->login) { + setOpenError(true); + return false; + } + DBSETLPWD(d->login, const_cast(password.toLocal8Bit().constData())); + DBSETLUSER(d->login, const_cast(user.toLocal8Bit().constData())); + + // Now, try to open and use the database. If this fails, return false. + DBPROCESS* dbproc; + + dbproc = dbopen(d->login, const_cast(host.toLatin1().constData())); + if (!dbproc) { + setLastError(qMakeError(tr("Unable to open connection"), QSqlError::ConnectionError, -1)); + setOpenError(true); + return false; + } + if (dbuse(dbproc, const_cast(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() +{ + Q_D(QTDSDriver); + 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 +{ + Q_UNUSED(type) + 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 -- cgit v1.2.3