/**************************************************************************** | |
** | |
** 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 |