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