1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtSql module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qsql_tds_p.h"
41 
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
50 
51 #include <qvariant.h>
52 #include <qdatetime.h>
53 #include <qhash.h>
54 #include <qregexp.h>
55 #include <qsqlerror.h>
56 #include <qsqlfield.h>
57 #include <qsqlindex.h>
58 #include <qsqlquery.h>
59 #include <QtSql/private/qsqlcachedresult_p.h>
60 #include <QtSql/private/qsqldriver_p.h>
61 #include <qstringlist.h>
62 #include <qvector.h>
63 
64 #include <stdlib.h>
65 
66 Q_DECLARE_OPAQUE_POINTER(LOGINREC*)
Q_DECLARE_OPAQUE_POINTER(DBPROCESS *)67 Q_DECLARE_OPAQUE_POINTER(DBPROCESS*)
68 
69 QT_BEGIN_NAMESPACE
70 
71 #ifdef DBNTWIN32
72 #define QMSGHANDLE DBMSGHANDLE_PROC
73 #define QERRHANDLE DBERRHANDLE_PROC
74 #define QTDSCHAR SQLCHAR
75 #define QTDSDATETIME4 SQLDATETIM4
76 #define QTDSDATETIME SQLDATETIME
77 #define QTDSDATETIME_N SQLDATETIMN
78 #define QTDSDECIMAL SQLDECIMAL
79 #define QTDSFLT4 SQLFLT4
80 #define QTDSFLT8 SQLFLT8
81 #define QTDSFLT8_N SQLFLTN
82 #define QTDSINT1 SQLINT1
83 #define QTDSINT2 SQLINT2
84 #define QTDSINT4 SQLINT4
85 #define QTDSINT4_N SQLINTN
86 #define QTDSMONEY4 SQLMONEY4
87 #define QTDSMONEY SQLMONEY
88 #define QTDSMONEY_N SQLMONEYN
89 #define QTDSNUMERIC SQLNUMERIC
90 #define QTDSTEXT SQLTEXT
91 #define QTDSVARCHAR SQLVARCHAR
92 #define QTDSBIT SQLBIT
93 #define QTDSBINARY SQLBINARY
94 #define QTDSVARBINARY SQLVARBINARY
95 #define QTDSIMAGE SQLIMAGE
96 #else
97 #define QMSGHANDLE MHANDLEFUNC
98 #define QERRHANDLE EHANDLEFUNC
99 #define QTDSCHAR SYBCHAR
100 #define QTDSDATETIME4 SYBDATETIME4
101 #define QTDSDATETIME SYBDATETIME
102 #define QTDSDATETIME_N SYBDATETIMN
103 #define QTDSDECIMAL SYBDECIMAL
104 #define QTDSFLT8 SYBFLT8
105 #define QTDSFLT8_N SYBFLTN
106 #define QTDSFLT4 SYBREAL
107 #define QTDSINT1 SYBINT1
108 #define QTDSINT2 SYBINT2
109 #define QTDSINT4 SYBINT4
110 #define QTDSINT4_N SYBINTN
111 #define QTDSMONEY4 SYBMONEY4
112 #define QTDSMONEY SYBMONEY
113 #define QTDSMONEY_N SYBMONEYN
114 #define QTDSNUMERIC SYBNUMERIC
115 #define QTDSTEXT SYBTEXT
116 #define QTDSVARCHAR SYBVARCHAR
117 #define QTDSBIT SYBBIT
118 #define QTDSBINARY SYBBINARY
119 #define QTDSVARBINARY SYBVARBINARY
120 #define QTDSIMAGE SYBIMAGE
121 // magic numbers not defined anywhere in Sybase headers
122 #define QTDSDECIMAL_2 55
123 #define QTDSNUMERIC_2 63
124 #endif  //DBNTWIN32
125 
126 #define TDS_CURSOR_SIZE 50
127 
128 // workaround for FreeTDS
129 #ifndef CS_PUBLIC
130 #define CS_PUBLIC
131 #endif
132 
133 QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, int errNo = -1)
134 {
135     return QSqlError(QLatin1String("QTDS: ") + err, QString(), type,
136                      errNo != -1 ? QString::number(errNo) : QString());
137 }
138 
139 class QTDSDriverPrivate : public QSqlDriverPrivate
140 {
141     Q_DECLARE_PUBLIC(QTDSDriver)
142 
143 public:
QTDSDriverPrivate()144     QTDSDriverPrivate() : QSqlDriverPrivate(), login(0), initialized(false) { dbmsType = QSqlDriver::Sybase; }
145     LOGINREC* login;  // login information
146     QString hostName;
147     QString db;
148     bool initialized;
149 };
150 
151 struct QTDSColumnData
152 {
153     void *data;
154     DBINT nullbind;
155 };
156 Q_DECLARE_TYPEINFO(QTDSColumnData, Q_MOVABLE_TYPE);
157 
158 class QTDSResultPrivate;
159 
160 class QTDSResult : public QSqlCachedResult
161 {
162     Q_DECLARE_PRIVATE(QTDSResult)
163 
164 public:
165     explicit QTDSResult(const QTDSDriver* db);
166     ~QTDSResult();
167     QVariant handle() const override;
168 
169 protected:
170     void cleanup();
171     bool reset(const QString &query) override;
172     int size() override;
173     int numRowsAffected() override;
174     bool gotoNext(QSqlCachedResult::ValueCache &values, int index) override;
175     QSqlRecord record() const override;
176 };
177 
178 class QTDSResultPrivate: public QSqlCachedResultPrivate
179 {
180     Q_DECLARE_PUBLIC(QTDSResult)
181 
182 public:
183     Q_DECLARE_SQLDRIVER_PRIVATE(QTDSDriver)
QTDSResultPrivate(QTDSResult * q,const QTDSDriver * drv)184     QTDSResultPrivate(QTDSResult *q, const QTDSDriver *drv)
185         : QSqlCachedResultPrivate(q, drv),
186           login(0),
187           dbproc(0) {}
188     LOGINREC* login;  // login information
189     DBPROCESS* dbproc; // connection from app to server
190     QSqlError lastError;
addErrorMsg(QString & errMsg)191     void addErrorMsg(QString& errMsg) { errorMsgs.append(errMsg); }
getErrorMsgs()192     QString getErrorMsgs() { return errorMsgs.join(QLatin1String("\n")); }
clearErrorMsgs()193     void clearErrorMsgs() { errorMsgs.clear(); }
194     QVector<QTDSColumnData> buffer;
195     QSqlRecord rec;
196 
197 private:
198     QStringList errorMsgs;
199 };
200 
201 typedef QHash<DBPROCESS *, QTDSResultPrivate *> QTDSErrorHash;
Q_GLOBAL_STATIC(QTDSErrorHash,errs)202 Q_GLOBAL_STATIC(QTDSErrorHash, errs)
203 
204 extern "C" {
205 static int CS_PUBLIC qTdsMsgHandler (DBPROCESS* dbproc,
206                             DBINT msgno,
207                             int msgstate,
208                             int severity,
209                             char* msgtext,
210                             char* srvname,
211                             char* /*procname*/,
212                             int line)
213 {
214     QTDSResultPrivate* p = errs()->value(dbproc);
215 
216     if (!p) {
217 //        ### umm... temporary disabled since this throws a lot of warnings...
218 //        qWarning("QTDSDriver warning (%d): [%s] from server [%s]", msgstate, msgtext, srvname);
219         return INT_CANCEL;
220     }
221 
222     if (severity > 0) {
223         QString errMsg = QString::fromLatin1("%1 (Msg %2, Level %3, State %4, Server %5, Line %6)")
224                          .arg(QString::fromLatin1(msgtext))
225                          .arg(msgno)
226                          .arg(severity)
227                          .arg(msgstate)
228                          .arg(QString::fromLatin1(srvname))
229                          .arg(line);
230         p->addErrorMsg(errMsg);
231         if (severity > 10) {
232             // Severe messages are really errors in the sense of lastError
233             errMsg = p->getErrorMsgs();
234             p->lastError = qMakeError(errMsg, QSqlError::UnknownError, msgno);
235             p->clearErrorMsgs();
236         }
237     }
238 
239     return INT_CANCEL;
240 }
241 
242 static int CS_PUBLIC qTdsErrHandler(DBPROCESS* dbproc,
243                                 int /*severity*/,
244                                 int dberr,
245                                 int /*oserr*/,
246                                 char* dberrstr,
247                                 char* oserrstr)
248 {
249     QTDSResultPrivate* p = errs()->value(dbproc);
250     if (!p) {
251         qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr);
252         return INT_CANCEL;
253     }
254     /*
255      * If the process is dead or NULL and
256      * we are not in the middle of logging in...
257      */
258     if((dbproc == NULL || DBDEAD(dbproc))) {
259         qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr);
260         return INT_CANCEL;
261     }
262 
263     const QString errMsg = QLatin1String(dberrstr) + QLatin1Char(' ')
264                          + QLatin1String(oserrstr) + QLatin1Char('\n')
265                          + p->getErrorMsgs();
266     p->lastError = qMakeError(errMsg, QSqlError::UnknownError, dberr);
267     p->clearErrorMsgs();
268 
269     return INT_CANCEL ;
270 }
271 
272 } //extern "C"
273 
274 
qDecodeTDSType(int type)275 QVariant::Type qDecodeTDSType(int type)
276 {
277     QVariant::Type t = QVariant::Invalid;
278     switch (type) {
279     case QTDSCHAR:
280     case QTDSTEXT:
281     case QTDSVARCHAR:
282         t = QVariant::String;
283         break;
284     case QTDSINT1:
285     case QTDSINT2:
286     case QTDSINT4:
287     case QTDSINT4_N:
288     case QTDSBIT:
289         t = QVariant::Int;
290         break;
291     case QTDSFLT4:
292     case QTDSFLT8:
293     case QTDSFLT8_N:
294     case QTDSMONEY4:
295     case QTDSMONEY:
296     case QTDSDECIMAL:
297     case QTDSNUMERIC:
298 #ifdef QTDSNUMERIC_2
299     case QTDSNUMERIC_2:
300 #endif
301 #ifdef QTDSDECIMAL_2
302     case QTDSDECIMAL_2:
303 #endif
304     case QTDSMONEY_N:
305         t = QVariant::Double;
306         break;
307     case QTDSDATETIME4:
308     case QTDSDATETIME:
309     case QTDSDATETIME_N:
310         t = QVariant::DateTime;
311         break;
312     case QTDSBINARY:
313     case QTDSVARBINARY:
314     case QTDSIMAGE:
315         t = QVariant::ByteArray;
316         break;
317     default:
318         t = QVariant::Invalid;
319         break;
320     }
321     return t;
322 }
323 
qFieldType(QTDSResultPrivate * d,int i)324 QVariant::Type qFieldType(QTDSResultPrivate* d, int i)
325 {
326     QVariant::Type type = qDecodeTDSType(dbcoltype(d->dbproc, i+1));
327     return type;
328 }
329 
330 
QTDSResult(const QTDSDriver * db)331 QTDSResult::QTDSResult(const QTDSDriver* db)
332     : QSqlCachedResult(*new QTDSResultPrivate(this, db))
333 {
334     Q_D(QTDSResult);
335     d->login = d->drv_d_func()->login;
336 
337     d->dbproc = dbopen(d->login, const_cast<char*>(d->drv_d_func()->hostName.toLatin1().constData()));
338     if (!d->dbproc)
339         return;
340     if (dbuse(d->dbproc, const_cast<char*>(d->drv_d_func()->db.toLatin1().constData())) == FAIL)
341         return;
342 
343     // insert d in error handler dict
344     errs()->insert(d->dbproc, d);
345     dbcmd(d->dbproc, "set quoted_identifier on");
346     dbsqlexec(d->dbproc);
347 }
348 
~QTDSResult()349 QTDSResult::~QTDSResult()
350 {
351     Q_D(QTDSResult);
352     cleanup();
353     if (d->dbproc)
354         dbclose(d->dbproc);
355     errs()->remove(d->dbproc);
356 }
357 
cleanup()358 void QTDSResult::cleanup()
359 {
360     Q_D(QTDSResult);
361     d->clearErrorMsgs();
362     d->rec.clear();
363     for (int i = 0; i < d->buffer.size(); ++i)
364         free(d->buffer.at(i).data);
365     d->buffer.clear();
366     // "can" stands for "cancel"... very clever.
367     dbcanquery(d->dbproc);
368     dbfreebuf(d->dbproc);
369 
370     QSqlCachedResult::cleanup();
371 }
372 
handle() const373 QVariant QTDSResult::handle() const
374 {
375     Q_D(const QTDSResult);
376     return QVariant(qRegisterMetaType<DBPROCESS *>("DBPROCESS*"), &d->dbproc);
377 }
378 
qIsNull(const QTDSColumnData & p)379 static inline bool qIsNull(const QTDSColumnData &p)
380 {
381     return p.nullbind == -1;
382 }
383 
gotoNext(QSqlCachedResult::ValueCache & values,int index)384 bool QTDSResult::gotoNext(QSqlCachedResult::ValueCache &values, int index)
385 {
386     Q_D(QTDSResult);
387     STATUS stat = dbnextrow(d->dbproc);
388     if (stat == NO_MORE_ROWS) {
389         setAt(QSql::AfterLastRow);
390         return false;
391     }
392     if ((stat == FAIL) || (stat == BUF_FULL)) {
393         setLastError(d->lastError);
394         return false;
395     }
396 
397     if (index < 0)
398         return true;
399 
400     for (int i = 0; i < d->rec.count(); ++i) {
401         int idx = index + i;
402         switch (d->rec.field(i).type()) {
403             case QVariant::DateTime:
404                 if (qIsNull(d->buffer.at(i))) {
405                     values[idx] = QVariant(QVariant::DateTime);
406                 } else {
407                     DBDATETIME *bdt = (DBDATETIME*) d->buffer.at(i).data;
408                     QDate date = QDate::fromString(QLatin1String("1900-01-01"), Qt::ISODate);
409                     QTime time = QTime::fromString(QLatin1String("00:00:00"), Qt::ISODate);
410                     values[idx] = QDateTime(date.addDays(bdt->dtdays), time.addMSecs(int(bdt->dttime / 0.3)));
411                 }
412                 break;
413             case QVariant::Int:
414                 if (qIsNull(d->buffer.at(i)))
415                     values[idx] = QVariant(QVariant::Int);
416                 else
417                     values[idx] = *((int*)d->buffer.at(i).data);
418                 break;
419             case QVariant::Double:
420             case QVariant::String:
421                 if (qIsNull(d->buffer.at(i)))
422                     values[idx] = QVariant(QVariant::String);
423                 else
424                     values[idx] = QString::fromLocal8Bit((const char*)d->buffer.at(i).data).trimmed();
425                 break;
426             case QVariant::ByteArray: {
427                 if (qIsNull(d->buffer.at(i)))
428                     values[idx] = QVariant(QVariant::ByteArray);
429                 else
430                     values[idx] = QByteArray((const char*)d->buffer.at(i).data);
431                 break;
432             }
433             default:
434                 // should never happen, and we already fired
435                 // a warning while binding.
436                 values[idx] = QVariant();
437                 break;
438         }
439     }
440 
441     return true;
442 }
443 
reset(const QString & query)444 bool QTDSResult::reset (const QString& query)
445 {
446     Q_D(QTDSResult);
447     cleanup();
448     if (!driver() || !driver()-> isOpen() || driver()->isOpenError())
449         return false;
450     setActive(false);
451     setAt(QSql::BeforeFirstRow);
452     if (dbcmd(d->dbproc, const_cast<char*>(query.toLocal8Bit().constData())) == FAIL) {
453         setLastError(d->lastError);
454         return false;
455     }
456 
457     if (dbsqlexec(d->dbproc) == FAIL) {
458         setLastError(d->lastError);
459         dbfreebuf(d->dbproc);
460         return false;
461     }
462     if (dbresults(d->dbproc) != SUCCEED) {
463         setLastError(d->lastError);
464         dbfreebuf(d->dbproc);
465         return false;
466     }
467 
468     setSelect((DBCMDROW(d->dbproc) == SUCCEED)); // decide whether or not we are dealing with a SELECT query
469     int numCols = dbnumcols(d->dbproc);
470     if (numCols > 0) {
471         d->buffer.resize(numCols);
472         init(numCols);
473     }
474     for (int i = 0; i < numCols; ++i) {
475         int dbType = dbcoltype(d->dbproc, i+1);
476         QVariant::Type vType = qDecodeTDSType(dbType);
477         QSqlField f(QString::fromLatin1(dbcolname(d->dbproc, i+1)), vType);
478         f.setSqlType(dbType);
479         f.setLength(dbcollen(d->dbproc, i+1));
480         d->rec.append(f);
481 
482         RETCODE ret = -1;
483         void* p = 0;
484         switch (vType) {
485         case QVariant::Int:
486             p = malloc(4);
487             ret = dbbind(d->dbproc, i+1, INTBIND, (DBINT) 4, (unsigned char *)p);
488             break;
489         case QVariant::Double:
490             // use string binding to prevent loss of precision
491             p = malloc(50);
492             ret = dbbind(d->dbproc, i+1, STRINGBIND, 50, (unsigned char *)p);
493             break;
494         case QVariant::String:
495             p = malloc(dbcollen(d->dbproc, i+1) + 1);
496             ret = dbbind(d->dbproc, i+1, STRINGBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p);
497             break;
498         case QVariant::DateTime:
499             p = malloc(8);
500             ret = dbbind(d->dbproc, i+1, DATETIMEBIND, (DBINT) 8, (unsigned char *)p);
501             break;
502         case QVariant::ByteArray:
503             p = malloc(dbcollen(d->dbproc, i+1) + 1);
504             ret = dbbind(d->dbproc, i+1, BINARYBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p);
505             break;
506         default: //don't bind the field since we do not support it
507             qWarning("QTDSResult::reset: Unsupported type for field \"%s\"", dbcolname(d->dbproc, i+1));
508             break;
509         }
510         if (ret == SUCCEED) {
511             d->buffer[i].data = p;
512             ret = dbnullbind(d->dbproc, i+1, &d->buffer[i].nullbind);
513         } else {
514             d->buffer[i].data = 0;
515             d->buffer[i].nullbind = 0;
516             free(p);
517         }
518         if ((ret != SUCCEED) && (ret != -1)) {
519             setLastError(d->lastError);
520             return false;
521         }
522     }
523 
524     setActive(true);
525     return true;
526 }
527 
size()528 int QTDSResult::size()
529 {
530     return -1;
531 }
532 
numRowsAffected()533 int QTDSResult::numRowsAffected()
534 {
535     Q_D(const QTDSResult);
536 #ifdef DBNTWIN32
537     if (dbiscount(d->dbproc)) {
538         return DBCOUNT(d->dbproc);
539     }
540     return -1;
541 #else
542     return DBCOUNT(d->dbproc);
543 #endif
544 }
545 
record() const546 QSqlRecord QTDSResult::record() const
547 {
548     Q_D(const QTDSResult);
549     return d->rec;
550 }
551 
552 ///////////////////////////////////////////////////////////////////
553 
QTDSDriver(QObject * parent)554 QTDSDriver::QTDSDriver(QObject* parent)
555     : QSqlDriver(*new QTDSDriverPrivate, parent)
556 {
557     init();
558 }
559 
QTDSDriver(LOGINREC * rec,const QString & host,const QString & db,QObject * parent)560 QTDSDriver::QTDSDriver(LOGINREC* rec, const QString& host, const QString &db, QObject* parent)
561     : QSqlDriver(*new QTDSDriverPrivate, parent)
562 {
563     Q_D(QTDSDriver);
564     init();
565     d->login = rec;
566     d->hostName = host;
567     d->db = db;
568     if (rec) {
569         setOpen(true);
570         setOpenError(false);
571     }
572 }
573 
handle() const574 QVariant QTDSDriver::handle() const
575 {
576     Q_D(const QTDSDriver);
577     return QVariant(qRegisterMetaType<LOGINREC *>("LOGINREC*"), &d->login);
578 }
579 
init()580 void QTDSDriver::init()
581 {
582     Q_D(QTDSDriver);
583     d->initialized = (dbinit() == SUCCEED);
584     // the following two code-lines will fail compilation on some FreeTDS versions
585     // just comment them out if you have FreeTDS (you won't get any errors and warnings then)
586     dberrhandle((QERRHANDLE)qTdsErrHandler);
587     dbmsghandle((QMSGHANDLE)qTdsMsgHandler);
588 }
589 
~QTDSDriver()590 QTDSDriver::~QTDSDriver()
591 {
592     dberrhandle(0);
593     dbmsghandle(0);
594     // dbexit also calls dbclose if necessary
595     dbexit();
596 }
597 
hasFeature(DriverFeature f) const598 bool QTDSDriver::hasFeature(DriverFeature f) const
599 {
600     switch (f) {
601     case Transactions:
602     case QuerySize:
603     case Unicode:
604     case SimpleLocking:
605     case EventNotifications:
606     case MultipleResultSets:
607         return false;
608     case BLOB:
609         return true;
610     default:
611         return false;
612     }
613 }
614 
open(const QString & db,const QString & user,const QString & password,const QString & host,int,const QString &)615 bool QTDSDriver::open(const QString & db,
616                        const QString & user,
617                        const QString & password,
618                        const QString & host,
619                        int /*port*/,
620                        const QString& /*connOpts*/)
621 {
622     Q_D(QTDSDriver);
623     if (isOpen())
624         close();
625     if (!d->initialized) {
626         setOpenError(true);
627         return false;
628     }
629     d->login = dblogin();
630     if (!d->login) {
631         setOpenError(true);
632         return false;
633     }
634     DBSETLPWD(d->login, const_cast<char*>(password.toLocal8Bit().constData()));
635     DBSETLUSER(d->login, const_cast<char*>(user.toLocal8Bit().constData()));
636 
637     // Now, try to open and use the database. If this fails, return false.
638     DBPROCESS* dbproc;
639 
640     dbproc = dbopen(d->login, const_cast<char*>(host.toLatin1().constData()));
641     if (!dbproc) {
642         setLastError(qMakeError(tr("Unable to open connection"), QSqlError::ConnectionError, -1));
643         setOpenError(true);
644         return false;
645     }
646     if (dbuse(dbproc, const_cast<char*>(db.toLatin1().constData())) == FAIL) {
647         setLastError(qMakeError(tr("Unable to use database"), QSqlError::ConnectionError, -1));
648         setOpenError(true);
649         return false;
650     }
651     dbclose( dbproc );
652 
653     setOpen(true);
654     setOpenError(false);
655     d->hostName = host;
656     d->db = db;
657     return true;
658 }
659 
close()660 void QTDSDriver::close()
661 {
662     Q_D(QTDSDriver);
663     if (isOpen()) {
664 #ifdef Q_USE_SYBASE
665         dbloginfree(d->login);
666 #else
667         dbfreelogin(d->login);
668 #endif
669         d->login = 0;
670         setOpen(false);
671         setOpenError(false);
672     }
673 }
674 
createResult() const675 QSqlResult *QTDSDriver::createResult() const
676 {
677     return new QTDSResult(this);
678 }
679 
beginTransaction()680 bool QTDSDriver::beginTransaction()
681 {
682     return false;
683 /*
684     if (!isOpen()) {
685         qWarning("QTDSDriver::beginTransaction: Database not open");
686         return false;
687     }
688     if (dbcmd(d->dbproc, "BEGIN TRANSACTION") == FAIL) {
689         setLastError(d->lastError);
690         dbfreebuf(d->dbproc);
691         return false;
692     }
693     if (dbsqlexec(d->dbproc) == FAIL) {
694         setLastError(d->lastError);
695         dbfreebuf(d->dbproc);
696         return false;
697     }
698     while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
699     dbfreebuf(d->dbproc);
700     inTransaction = true;
701     return true;
702 */
703 }
704 
commitTransaction()705 bool QTDSDriver::commitTransaction()
706 {
707     return false;
708 /*
709     if (!isOpen()) {
710         qWarning("QTDSDriver::commitTransaction: Database not open");
711         return false;
712     }
713     if (dbcmd(d->dbproc, "COMMIT TRANSACTION") == FAIL) {
714         setLastError(d->lastError);
715         dbfreebuf(d->dbproc);
716         return false;
717     }
718     if (dbsqlexec(d->dbproc) == FAIL) {
719         setLastError(d->lastError);
720         dbfreebuf(d->dbproc);
721         return false;
722     }
723     while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
724     dbfreebuf(d->dbproc);
725     inTransaction = false;
726     return true;
727 */
728 }
729 
rollbackTransaction()730 bool QTDSDriver::rollbackTransaction()
731 {
732     return false;
733 /*
734     if (!isOpen()) {
735         qWarning("QTDSDriver::rollbackTransaction: Database not open");
736         return false;
737     }
738     if (dbcmd(d->dbproc, "ROLLBACK TRANSACTION") == FAIL) {
739         setLastError(d->lastError);
740         dbfreebuf(d->dbproc);
741         return false;
742     }
743     if (dbsqlexec(d->dbproc) == FAIL) {
744         setLastError(d->lastError);
745         dbfreebuf(d->dbproc);
746         return false;
747     }
748     while(dbresults(d->dbproc) == NO_MORE_RESULTS) {}
749     dbfreebuf(d->dbproc);
750     inTransaction = false;
751     return true;
752 */
753 }
754 
record(const QString & tablename) const755 QSqlRecord QTDSDriver::record(const QString& tablename) const
756 {
757     QSqlRecord info;
758     if (!isOpen())
759         return info;
760     QSqlQuery t(createResult());
761     t.setForwardOnly(true);
762 
763     QString table = tablename;
764     if (isIdentifierEscaped(table, QSqlDriver::TableName))
765         table = stripDelimiters(table, QSqlDriver::TableName);
766 
767     QString stmt (QLatin1String("select name, type, length, prec from syscolumns "
768                    "where id = (select id from sysobjects where name = '%1')"));
769     t.exec(stmt.arg(table));
770     while (t.next()) {
771         QSqlField f(t.value(0).toString().simplified(), qDecodeTDSType(t.value(1).toInt()), tablename);
772         f.setLength(t.value(2).toInt());
773         f.setPrecision(t.value(3).toInt());
774         f.setSqlType(t.value(1).toInt());
775         info.append(f);
776     }
777     return info;
778 }
779 
tables(QSql::TableType type) const780 QStringList QTDSDriver::tables(QSql::TableType type) const
781 {
782     QStringList list;
783 
784     if (!isOpen())
785         return list;
786 
787     QStringList typeFilter;
788 
789     if (type & QSql::Tables)
790         typeFilter += QLatin1String("type='U'");
791     if (type & QSql::SystemTables)
792         typeFilter += QLatin1String("type='S'");
793     if (type & QSql::Views)
794         typeFilter += QLatin1String("type='V'");
795 
796     if (typeFilter.isEmpty())
797         return list;
798 
799     QSqlQuery t(createResult());
800     t.setForwardOnly(true);
801     t.exec(QLatin1String("select name from sysobjects where ") + typeFilter.join(QLatin1String(" or ")));
802     while (t.next())
803         list.append(t.value(0).toString().simplified());
804 
805     return list;
806 }
807 
formatValue(const QSqlField & field,bool trim) const808 QString QTDSDriver::formatValue(const QSqlField &field,
809                                   bool trim) const
810 {
811     QString r;
812     if (field.isNull())
813         r = QLatin1String("NULL");
814     else if (field.type() == QVariant::DateTime) {
815         if (field.value().toDateTime().isValid()){
816             r = field.value().toDateTime().toString(u"yyyyMMdd hh:mm:ss");
817             r.prepend(QLatin1String("'"));
818             r.append(QLatin1String("'"));
819         } else
820             r = QLatin1String("NULL");
821     } else if (field.type() == QVariant::ByteArray) {
822         QByteArray ba = field.value().toByteArray();
823         QString res;
824         static const char hexchars[] = "0123456789abcdef";
825         for (int i = 0; i < ba.size(); ++i) {
826             uchar s = (uchar) ba[i];
827             res += QLatin1Char(hexchars[s >> 4]);
828             res += QLatin1Char(hexchars[s & 0x0f]);
829         }
830         r = QLatin1String("0x") + res;
831     } else {
832         r = QSqlDriver::formatValue(field, trim);
833     }
834     return r;
835 }
836 
primaryIndex(const QString & tablename) const837 QSqlIndex QTDSDriver::primaryIndex(const QString& tablename) const
838 {
839     QSqlRecord rec = record(tablename);
840 
841     QString table = tablename;
842     if (isIdentifierEscaped(table, QSqlDriver::TableName))
843         table = stripDelimiters(table, QSqlDriver::TableName);
844 
845     QSqlIndex idx(table);
846     if ((!isOpen()) || (table.isEmpty()))
847         return QSqlIndex();
848 
849     QSqlQuery t(createResult());
850     t.setForwardOnly(true);
851     t.exec(QString::fromLatin1("sp_helpindex '%1'").arg(table));
852     if (t.next()) {
853         QStringList fNames = t.value(2).toString().simplified().split(QLatin1Char(','));
854         QRegExp regx(QLatin1String("\\s*(\\S+)(?:\\s+(DESC|desc))?\\s*"));
855         for(QStringList::Iterator it = fNames.begin(); it != fNames.end(); ++it) {
856             regx.indexIn(*it);
857             QSqlField f(regx.cap(1), rec.field(regx.cap(1)).type(), tablename);
858             if (regx.cap(2).toLower() == QLatin1String("desc")) {
859                 idx.append(f, true);
860             } else {
861                 idx.append(f, false);
862             }
863         }
864         idx.setName(t.value(0).toString().simplified());
865     }
866     return idx;
867 }
868 
escapeIdentifier(const QString & identifier,IdentifierType type) const869 QString QTDSDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
870 {
871     Q_UNUSED(type)
872     QString res = identifier;
873     if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
874         res.replace(QLatin1Char('"'), QLatin1String("\"\""));
875         res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
876         res.replace(QLatin1Char('.'), QLatin1String("\".\""));
877     }
878     return res;
879 }
880 
881 QT_END_NAMESPACE
882