Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into master-integration
[qt-netbsd.git] / src / sql / drivers / tds / qsql_tds.cpp
blobf7f096170f9ee059718f335e6de3db3a701b0bb1
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtSql module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include <qglobal.h>
43 #ifdef Q_OS_WIN32 // We assume that MS SQL Server is used. Set Q_USE_SYBASE to force Sybase.
44 // Conflicting declarations of LPCBYTE in sqlfront.h and winscard.h
45 #define _WINSCARD_H_
46 #include <windows.h>
47 #else
48 #define Q_USE_SYBASE
49 #endif
51 #include "qsql_tds.h"
53 #include <qvariant.h>
54 #include <qdatetime.h>
55 #include <qhash.h>
56 #include <qregexp.h>
57 #include <qsqlerror.h>
58 #include <qsqlfield.h>
59 #include <qsqlindex.h>
60 #include <qsqlquery.h>
61 #include <qstringlist.h>
62 #include <qvector.h>
64 #include <stdlib.h>
66 QT_BEGIN_NAMESPACE
68 #ifdef DBNTWIN32
69 #define QMSGHANDLE DBMSGHANDLE_PROC
70 #define QERRHANDLE DBERRHANDLE_PROC
71 #define QTDSCHAR SQLCHAR
72 #define QTDSDATETIME4 SQLDATETIM4
73 #define QTDSDATETIME SQLDATETIME
74 #define QTDSDATETIME_N SQLDATETIMN
75 #define QTDSDECIMAL SQLDECIMAL
76 #define QTDSFLT4 SQLFLT4
77 #define QTDSFLT8 SQLFLT8
78 #define QTDSFLT8_N SQLFLTN
79 #define QTDSINT1 SQLINT1
80 #define QTDSINT2 SQLINT2
81 #define QTDSINT4 SQLINT4
82 #define QTDSINT4_N SQLINTN
83 #define QTDSMONEY4 SQLMONEY4
84 #define QTDSMONEY SQLMONEY
85 #define QTDSMONEY_N SQLMONEYN
86 #define QTDSNUMERIC SQLNUMERIC
87 #define QTDSTEXT SQLTEXT
88 #define QTDSVARCHAR SQLVARCHAR
89 #define QTDSBIT SQLBIT
90 #define QTDSBINARY SQLBINARY
91 #define QTDSVARBINARY SQLVARBINARY
92 #define QTDSIMAGE SQLIMAGE
93 #else
94 #define QMSGHANDLE MHANDLEFUNC
95 #define QERRHANDLE EHANDLEFUNC
96 #define QTDSCHAR SYBCHAR
97 #define QTDSDATETIME4 SYBDATETIME4
98 #define QTDSDATETIME SYBDATETIME
99 #define QTDSDATETIME_N SYBDATETIMN
100 #define QTDSDECIMAL SYBDECIMAL
101 #define QTDSFLT8 SYBFLT8
102 #define QTDSFLT8_N SYBFLTN
103 #define QTDSFLT4 SYBREAL
104 #define QTDSINT1 SYBINT1
105 #define QTDSINT2 SYBINT2
106 #define QTDSINT4 SYBINT4
107 #define QTDSINT4_N SYBINTN
108 #define QTDSMONEY4 SYBMONEY4
109 #define QTDSMONEY SYBMONEY
110 #define QTDSMONEY_N SYBMONEYN
111 #define QTDSNUMERIC SYBNUMERIC
112 #define QTDSTEXT SYBTEXT
113 #define QTDSVARCHAR SYBVARCHAR
114 #define QTDSBIT SYBBIT
115 #define QTDSBINARY SYBBINARY
116 #define QTDSVARBINARY SYBVARBINARY
117 #define QTDSIMAGE SYBIMAGE
118 // magic numbers not defined anywhere in Sybase headers
119 #define QTDSDECIMAL_2 55
120 #define QTDSNUMERIC_2 63
121 #endif //DBNTWIN32
123 #define TDS_CURSOR_SIZE 50
125 // workaround for FreeTDS
126 #ifndef CS_PUBLIC
127 #define CS_PUBLIC
128 #endif
130 QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, int errNo = -1)
132 return QSqlError(QLatin1String("QTDS: ") + err, QString(), type, errNo);
135 class QTDSDriverPrivate
137 public:
138 QTDSDriverPrivate(): login(0) {}
139 LOGINREC* login; // login information
140 QString hostName;
141 QString db;
145 class QTDSResultPrivate
147 public:
148 QTDSResultPrivate():login(0), dbproc(0) {}
149 LOGINREC* login; // login information
150 DBPROCESS* dbproc; // connection from app to server
151 QSqlError lastError;
152 void addErrorMsg(QString& errMsg) { errorMsgs.append(errMsg); }
153 QString getErrorMsgs() { return errorMsgs.join(QLatin1String("\n")); }
154 void clearErrorMsgs() { errorMsgs.clear(); }
155 QVector<void *> buffer;
156 QSqlRecord rec;
158 private:
159 QStringList errorMsgs;
162 typedef QHash<DBPROCESS *, QTDSResultPrivate *> QTDSErrorHash;
163 Q_GLOBAL_STATIC(QTDSErrorHash, errs)
165 extern "C" {
166 static int CS_PUBLIC qTdsMsgHandler (DBPROCESS* dbproc,
167 DBINT msgno,
168 int msgstate,
169 int severity,
170 char* msgtext,
171 char* srvname,
172 char* /*procname*/,
173 int line)
175 QTDSResultPrivate* p = errs()->value(dbproc);
177 if (!p) {
178 // ### umm... temporary disabled since this throws a lot of warnings...
179 // qWarning("QTDSDriver warning (%d): [%s] from server [%s]", msgstate, msgtext, srvname);
180 return INT_CANCEL;
183 if (severity > 0) {
184 QString errMsg = QString::fromLatin1("%1 (Msg %2, Level %3, State %4, Server %5, Line %6)")
185 .arg(QString::fromAscii(msgtext))
186 .arg(msgno)
187 .arg(severity)
188 .arg(msgstate)
189 .arg(QString::fromAscii(srvname))
190 .arg(line);
191 p->addErrorMsg(errMsg);
192 if (severity > 10) {
193 // Severe messages are really errors in the sense of lastError
194 errMsg = p->getErrorMsgs();
195 p->lastError = qMakeError(errMsg, QSqlError::UnknownError, msgno);
196 p->clearErrorMsgs();
200 return INT_CANCEL;
203 static int CS_PUBLIC qTdsErrHandler(DBPROCESS* dbproc,
204 int /*severity*/,
205 int dberr,
206 int /*oserr*/,
207 char* dberrstr,
208 char* oserrstr)
210 QTDSResultPrivate* p = errs()->value(dbproc);
211 if (!p) {
212 qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr);
213 return INT_CANCEL;
216 * If the process is dead or NULL and
217 * we are not in the middle of logging in...
219 if((dbproc == NULL || DBDEAD(dbproc))) {
220 qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr);
221 return INT_CANCEL;
225 QString errMsg = QString::fromLatin1("%1 %2\n").arg(QLatin1String(dberrstr)).arg(
226 QLatin1String(oserrstr));
227 errMsg += p->getErrorMsgs();
228 p->lastError = qMakeError(errMsg, QSqlError::UnknownError, dberr);
229 p->clearErrorMsgs();
231 return INT_CANCEL ;
234 } //extern "C"
237 QVariant::Type qDecodeTDSType(int type)
239 QVariant::Type t = QVariant::Invalid;
240 switch (type) {
241 case QTDSCHAR:
242 case QTDSTEXT:
243 case QTDSVARCHAR:
244 t = QVariant::String;
245 break;
246 case QTDSINT1:
247 case QTDSINT2:
248 case QTDSINT4:
249 case QTDSINT4_N:
250 case QTDSBIT:
251 t = QVariant::Int;
252 break;
253 case QTDSFLT4:
254 case QTDSFLT8:
255 case QTDSFLT8_N:
256 case QTDSMONEY4:
257 case QTDSMONEY:
258 case QTDSDECIMAL:
259 case QTDSNUMERIC:
260 #ifdef QTDSNUMERIC_2
261 case QTDSNUMERIC_2:
262 #endif
263 #ifdef QTDSDECIMAL_2
264 case QTDSDECIMAL_2:
265 #endif
266 case QTDSMONEY_N:
267 t = QVariant::Double;
268 break;
269 case QTDSDATETIME4:
270 case QTDSDATETIME:
271 case QTDSDATETIME_N:
272 t = QVariant::DateTime;
273 break;
274 case QTDSBINARY:
275 case QTDSVARBINARY:
276 case QTDSIMAGE:
277 t = QVariant::ByteArray;
278 break;
279 default:
280 t = QVariant::Invalid;
281 break;
283 return t;
286 QVariant::Type qFieldType(QTDSResultPrivate* d, int i)
288 QVariant::Type type = qDecodeTDSType(dbcoltype(d->dbproc, i+1));
289 return type;
293 QTDSResult::QTDSResult(const QTDSDriver* db)
294 : QSqlCachedResult(db)
296 d = new QTDSResultPrivate();
297 d->login = db->d->login;
299 d->dbproc = dbopen(d->login, const_cast<char*>(db->d->hostName.toLatin1().constData()));
300 if (!d->dbproc)
301 return;
302 if (dbuse(d->dbproc, const_cast<char*>(db->d->db.toLatin1().constData())) == FAIL)
303 return;
305 // insert d in error handler dict
306 errs()->insert(d->dbproc, d);
307 dbcmd(d->dbproc, "set quoted_identifier on");
308 dbsqlexec(d->dbproc);
311 QTDSResult::~QTDSResult()
313 cleanup();
314 if (d->dbproc)
315 dbclose(d->dbproc);
316 errs()->remove(d->dbproc);
317 delete d;
320 void QTDSResult::cleanup()
322 d->clearErrorMsgs();
323 d->rec.clear();
324 for (int i = 0; i < d->buffer.size() / 2; ++i)
325 free(d->buffer.at(i * 2));
326 d->buffer.clear();
327 // "can" stands for "cancel"... very clever.
328 dbcanquery(d->dbproc);
329 dbfreebuf(d->dbproc);
331 QSqlCachedResult::cleanup();
334 QVariant QTDSResult::handle() const
336 return QVariant(qRegisterMetaType<DBPROCESS *>("DBPROCESS*"), &d->dbproc);
339 static inline bool qIsNull(const void *ind)
341 return *reinterpret_cast<const DBINT *>(&ind) == -1;
344 bool QTDSResult::gotoNext(QSqlCachedResult::ValueCache &values, int index)
346 STATUS stat = dbnextrow(d->dbproc);
347 if (stat == NO_MORE_ROWS) {
348 setAt(QSql::AfterLastRow);
349 return false;
351 if ((stat == FAIL) || (stat == BUF_FULL)) {
352 setLastError(d->lastError);
353 return false;
356 if (index < 0)
357 return true;
359 for (int i = 0; i < d->rec.count(); ++i) {
360 int idx = index + i;
361 switch (d->rec.field(i).type()) {
362 case QVariant::DateTime:
363 if (qIsNull(d->buffer.at(i * 2 + 1))) {
364 values[idx] = QVariant(QVariant::DateTime);
365 } else {
366 DBDATETIME *bdt = (DBDATETIME*) d->buffer.at(i * 2);
367 QDate date = QDate::fromString(QLatin1String("1900-01-01"), Qt::ISODate);
368 QTime time = QTime::fromString(QLatin1String("00:00:00"), Qt::ISODate);
369 values[idx] = QDateTime(date.addDays(bdt->dtdays), time.addMSecs(int(bdt->dttime / 0.3)));
371 break;
372 case QVariant::Int:
373 if (qIsNull(d->buffer.at(i * 2 + 1)))
374 values[idx] = QVariant(QVariant::Int);
375 else
376 values[idx] = *((int*)d->buffer.at(i * 2));
377 break;
378 case QVariant::Double:
379 case QVariant::String:
380 if (qIsNull(d->buffer.at(i * 2 + 1)))
381 values[idx] = QVariant(QVariant::String);
382 else
383 values[idx] = QString::fromLocal8Bit((const char*)d->buffer.at(i * 2)).trimmed();
384 break;
385 case QVariant::ByteArray: {
386 if (qIsNull(d->buffer.at(i * 2 + 1)))
387 values[idx] = QVariant(QVariant::ByteArray);
388 else
389 values[idx] = QByteArray((const char*)d->buffer.at(i * 2));
390 break;
392 default:
393 // should never happen, and we already fired
394 // a warning while binding.
395 values[idx] = QVariant();
396 break;
400 return true;
403 bool QTDSResult::reset (const QString& query)
405 cleanup();
406 if (!driver() || !driver()-> isOpen() || driver()->isOpenError())
407 return false;
408 setActive(false);
409 setAt(QSql::BeforeFirstRow);
410 if (dbcmd(d->dbproc, const_cast<char*>(query.toLocal8Bit().constData())) == FAIL) {
411 setLastError(d->lastError);
412 return false;
415 if (dbsqlexec(d->dbproc) == FAIL) {
416 setLastError(d->lastError);
417 dbfreebuf(d->dbproc);
418 return false;
420 if (dbresults(d->dbproc) != SUCCEED) {
421 setLastError(d->lastError);
422 dbfreebuf(d->dbproc);
423 return false;
426 setSelect((DBCMDROW(d->dbproc) == SUCCEED)); // decide whether or not we are dealing with a SELECT query
427 int numCols = dbnumcols(d->dbproc);
428 if (numCols > 0) {
429 d->buffer.resize(numCols * 2);
430 init(numCols);
432 for (int i = 0; i < numCols; ++i) {
433 int dbType = dbcoltype(d->dbproc, i+1);
434 QVariant::Type vType = qDecodeTDSType(dbType);
435 QSqlField f(QString::fromAscii(dbcolname(d->dbproc, i+1)), vType);
436 f.setSqlType(dbType);
437 f.setLength(dbcollen(d->dbproc, i+1));
438 d->rec.append(f);
440 RETCODE ret = -1;
441 void* p = 0;
442 switch (vType) {
443 case QVariant::Int:
444 p = malloc(4);
445 ret = dbbind(d->dbproc, i+1, INTBIND, (DBINT) 4, (unsigned char *)p);
446 break;
447 case QVariant::Double:
448 // use string binding to prevent loss of precision
449 p = malloc(50);
450 ret = dbbind(d->dbproc, i+1, STRINGBIND, 50, (unsigned char *)p);
451 break;
452 case QVariant::String:
453 p = malloc(dbcollen(d->dbproc, i+1) + 1);
454 ret = dbbind(d->dbproc, i+1, STRINGBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p);
455 break;
456 case QVariant::DateTime:
457 p = malloc(8);
458 ret = dbbind(d->dbproc, i+1, DATETIMEBIND, (DBINT) 8, (unsigned char *)p);
459 break;
460 case QVariant::ByteArray:
461 p = malloc(dbcollen(d->dbproc, i+1) + 1);
462 ret = dbbind(d->dbproc, i+1, BINARYBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p);
463 break;
464 default: //don't bind the field since we do not support it
465 qWarning("QTDSResult::reset: Unsupported type for field \"%s\"", dbcolname(d->dbproc, i+1));
466 break;
468 if (ret == SUCCEED) {
469 d->buffer[i * 2] = p;
470 ret = dbnullbind(d->dbproc, i+1, (DBINT*)(&d->buffer[i * 2 + 1]));
471 } else {
472 d->buffer[i * 2] = 0;
473 d->buffer[i * 2 + 1] = 0;
474 free(p);
476 if ((ret != SUCCEED) && (ret != -1)) {
477 setLastError(d->lastError);
478 return false;
482 setActive(true);
483 return true;
486 int QTDSResult::size()
488 return -1;
491 int QTDSResult::numRowsAffected()
493 #ifdef DBNTWIN32
494 if (dbiscount(d->dbproc)) {
495 return DBCOUNT(d->dbproc);
497 return -1;
498 #else
499 return DBCOUNT(d->dbproc);
500 #endif
503 QSqlRecord QTDSResult::record() const
505 return d->rec;
508 ///////////////////////////////////////////////////////////////////
510 QTDSDriver::QTDSDriver(QObject* parent)
511 : QSqlDriver(parent)
513 init();
516 QTDSDriver::QTDSDriver(LOGINREC* rec, const QString& host, const QString &db, QObject* parent)
517 : QSqlDriver(parent)
519 init();
520 d->login = rec;
521 d->hostName = host;
522 d->db = db;
523 if (rec) {
524 setOpen(true);
525 setOpenError(false);
529 QVariant QTDSDriver::handle() const
531 return QVariant(qRegisterMetaType<LOGINREC *>("LOGINREC*"), &d->login);
534 void QTDSDriver::init()
536 d = new QTDSDriverPrivate();
537 // the following two code-lines will fail compilation on some FreeTDS versions
538 // just comment them out if you have FreeTDS (you won't get any errors and warnings then)
539 dberrhandle((QERRHANDLE)qTdsErrHandler);
540 dbmsghandle((QMSGHANDLE)qTdsMsgHandler);
543 QTDSDriver::~QTDSDriver()
545 dberrhandle(0);
546 dbmsghandle(0);
547 // dbexit also calls dbclose if necessary
548 dbexit();
549 delete d;
552 bool QTDSDriver::hasFeature(DriverFeature f) const
554 switch (f) {
555 case Transactions:
556 case QuerySize:
557 case Unicode:
558 case SimpleLocking:
559 case EventNotifications:
560 case MultipleResultSets:
561 return false;
562 case BLOB:
563 return true;
564 default:
565 return false;
569 bool QTDSDriver::open(const QString & db,
570 const QString & user,
571 const QString & password,
572 const QString & host,
573 int /*port*/,
574 const QString& /*connOpts*/)
576 if (isOpen())
577 close();
578 if (!dbinit()) {
579 setOpenError(true);
580 return false;
582 d->login = dblogin();
583 if (!d->login) {
584 setOpenError(true);
585 return false;
587 DBSETLPWD(d->login, const_cast<char*>(password.toLocal8Bit().constData()));
588 DBSETLUSER(d->login, const_cast<char*>(user.toLocal8Bit().constData()));
590 // Now, try to open and use the database. If this fails, return false.
591 DBPROCESS* dbproc;
593 dbproc = dbopen(d->login, const_cast<char*>(host.toLatin1().constData()));
594 if (!dbproc) {
595 setLastError(qMakeError(tr("Unable to open connection"), QSqlError::ConnectionError, -1));
596 setOpenError(true);
597 return false;
599 if (dbuse(dbproc, const_cast<char*>(db.toLatin1().constData())) == FAIL) {
600 setLastError(qMakeError(tr("Unable to use database"), QSqlError::ConnectionError, -1));
601 setOpenError(true);
602 return false;
604 dbclose( dbproc );
606 setOpen(true);
607 setOpenError(false);
608 d->hostName = host;
609 d->db = db;
610 return true;
613 void QTDSDriver::close()
615 if (isOpen()) {
616 #ifdef Q_USE_SYBASE
617 dbloginfree(d->login);
618 #else
619 dbfreelogin(d->login);
620 #endif
621 d->login = 0;
622 setOpen(false);
623 setOpenError(false);
627 QSqlResult *QTDSDriver::createResult() const
629 return new QTDSResult(this);
632 bool QTDSDriver::beginTransaction()
634 return false;
636 if (!isOpen()) {
637 qWarning("QTDSDriver::beginTransaction: Database not open");
638 return false;
640 if (dbcmd(d->dbproc, "BEGIN TRANSACTION") == FAIL) {
641 setLastError(d->lastError);
642 dbfreebuf(d->dbproc);
643 return false;
645 if (dbsqlexec(d->dbproc) == FAIL) {
646 setLastError(d->lastError);
647 dbfreebuf(d->dbproc);
648 return false;
650 while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
651 dbfreebuf(d->dbproc);
652 inTransaction = true;
653 return true;
657 bool QTDSDriver::commitTransaction()
659 return false;
661 if (!isOpen()) {
662 qWarning("QTDSDriver::commitTransaction: Database not open");
663 return false;
665 if (dbcmd(d->dbproc, "COMMIT TRANSACTION") == FAIL) {
666 setLastError(d->lastError);
667 dbfreebuf(d->dbproc);
668 return false;
670 if (dbsqlexec(d->dbproc) == FAIL) {
671 setLastError(d->lastError);
672 dbfreebuf(d->dbproc);
673 return false;
675 while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
676 dbfreebuf(d->dbproc);
677 inTransaction = false;
678 return true;
682 bool QTDSDriver::rollbackTransaction()
684 return false;
686 if (!isOpen()) {
687 qWarning("QTDSDriver::rollbackTransaction: Database not open");
688 return false;
690 if (dbcmd(d->dbproc, "ROLLBACK TRANSACTION") == FAIL) {
691 setLastError(d->lastError);
692 dbfreebuf(d->dbproc);
693 return false;
695 if (dbsqlexec(d->dbproc) == FAIL) {
696 setLastError(d->lastError);
697 dbfreebuf(d->dbproc);
698 return false;
700 while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
701 dbfreebuf(d->dbproc);
702 inTransaction = false;
703 return true;
707 QSqlRecord QTDSDriver::record(const QString& tablename) const
709 QSqlRecord info;
710 if (!isOpen())
711 return info;
712 QSqlQuery t(createResult());
713 t.setForwardOnly(true);
715 QString table = tablename;
716 if (isIdentifierEscaped(table, QSqlDriver::TableName))
717 table = stripDelimiters(table, QSqlDriver::TableName);
719 QString stmt (QLatin1String("select name, type, length, prec from syscolumns "
720 "where id = (select id from sysobjects where name = '%1')"));
721 t.exec(stmt.arg(table));
722 while (t.next()) {
723 QSqlField f(t.value(0).toString().simplified(), qDecodeTDSType(t.value(1).toInt()));
724 f.setLength(t.value(2).toInt());
725 f.setPrecision(t.value(3).toInt());
726 f.setSqlType(t.value(1).toInt());
727 info.append(f);
729 return info;
732 QStringList QTDSDriver::tables(QSql::TableType type) const
734 QStringList list;
736 if (!isOpen())
737 return list;
739 QStringList typeFilter;
741 if (type & QSql::Tables)
742 typeFilter += QLatin1String("type='U'");
743 if (type & QSql::SystemTables)
744 typeFilter += QLatin1String("type='S'");
745 if (type & QSql::Views)
746 typeFilter += QLatin1String("type='V'");
748 if (typeFilter.isEmpty())
749 return list;
751 QSqlQuery t(createResult());
752 t.setForwardOnly(true);
753 t.exec(QLatin1String("select name from sysobjects where ") + typeFilter.join(QLatin1String(" or ")));
754 while (t.next())
755 list.append(t.value(0).toString().simplified());
757 return list;
760 QString QTDSDriver::formatValue(const QSqlField &field,
761 bool trim) const
763 QString r;
764 if (field.isNull())
765 r = QLatin1String("NULL");
766 else if (field.type() == QVariant::DateTime) {
767 if (field.value().toDateTime().isValid()){
768 r = field.value().toDateTime().toString(QLatin1String("yyyyMMdd hh:mm:ss"));
769 r.prepend(QLatin1String("'"));
770 r.append(QLatin1String("'"));
771 } else
772 r = QLatin1String("NULL");
773 } else if (field.type() == QVariant::ByteArray) {
774 QByteArray ba = field.value().toByteArray();
775 QString res;
776 static const char hexchars[] = "0123456789abcdef";
777 for (int i = 0; i < ba.size(); ++i) {
778 uchar s = (uchar) ba[i];
779 res += QLatin1Char(hexchars[s >> 4]);
780 res += QLatin1Char(hexchars[s & 0x0f]);
782 r = QLatin1String("0x") + res;
783 } else {
784 r = QSqlDriver::formatValue(field, trim);
786 return r;
789 QSqlIndex QTDSDriver::primaryIndex(const QString& tablename) const
791 QSqlRecord rec = record(tablename);
793 QString table = tablename;
794 if (isIdentifierEscaped(table, QSqlDriver::TableName))
795 table = stripDelimiters(table, QSqlDriver::TableName);
797 QSqlIndex idx(table);
798 if ((!isOpen()) || (table.isEmpty()))
799 return QSqlIndex();
801 QSqlQuery t(createResult());
802 t.setForwardOnly(true);
803 t.exec(QString::fromLatin1("sp_helpindex '%1'").arg(table));
804 if (t.next()) {
805 QStringList fNames = t.value(2).toString().simplified().split(QLatin1Char(','));
806 QRegExp regx(QLatin1String("\\s*(\\S+)(?:\\s+(DESC|desc))?\\s*"));
807 for(QStringList::Iterator it = fNames.begin(); it != fNames.end(); ++it) {
808 regx.indexIn(*it);
809 QSqlField f(regx.cap(1), rec.field(regx.cap(1)).type());
810 if (regx.cap(2).toLower() == QLatin1String("desc")) {
811 idx.append(f, true);
812 } else {
813 idx.append(f, false);
816 idx.setName(t.value(0).toString().simplified());
818 return idx;
821 QString QTDSDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
823 QString res = identifier;
824 if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
825 res.replace(QLatin1Char('"'), QLatin1String("\"\""));
826 res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
827 res.replace(QLatin1Char('.'), QLatin1String("\".\""));
829 return res;
832 QT_END_NAMESPACE