| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 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$ |
| ** GNU Lesser General Public License Usage |
| ** 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. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $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 (Msg %2, Level %3, State %4, Server %5, Line %6)") |
| .arg(QString::fromAscii(msgtext)) |
| .arg(msgno) |
| .arg(severity) |
| .arg(msgstate) |
| .arg(QString::fromAscii(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(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 |