source: trunk/src/sql/drivers/odbc/qsql_odbc.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 87.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtSql module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsql_odbc.h"
43#include <qsqlrecord.h>
44
45#if defined (Q_OS_WIN32)
46#include <qt_windows.h>
47#endif
48#include <qcoreapplication.h>
49#include <qvariant.h>
50#include <qdatetime.h>
51#include <qsqlerror.h>
52#include <qsqlfield.h>
53#include <qsqlindex.h>
54#include <qstringlist.h>
55#include <qvarlengtharray.h>
56#include <qvector.h>
57#include <QDebug>
58#include <QSqlQuery>
59
60QT_BEGIN_NAMESPACE
61
62// undefine this to prevent initial check of the ODBC driver
63#define ODBC_CHECK_DRIVER
64
65// newer platform SDKs use SQLLEN instead of SQLINTEGER
66#if defined(WIN32) && (_MSC_VER < 1300) && !defined(__MINGW64_VERSION_MAJOR)
67# define QSQLLEN SQLINTEGER
68# define QSQLULEN SQLUINTEGER
69#else
70# define QSQLLEN SQLLEN
71# define QSQLULEN SQLULEN
72#endif
73
74static const int COLNAMESIZE = 256;
75//Map Qt parameter types to ODBC types
76static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
77
78inline static QString fromSQLTCHAR(const QVarLengthArray<SQLTCHAR>& input, int size=-1)
79{
80 QString result;
81
82 int realsize = qMin(size, input.size());
83 if(realsize > 0 && input[realsize-1] == 0)
84 realsize--;
85 switch(sizeof(SQLTCHAR)) {
86 case 1:
87 result=QString::fromUtf8((const char *)input.constData(), realsize);
88 break;
89 case 2:
90 result=QString::fromUtf16((const ushort *)input.constData(), realsize);
91 break;
92 case 4:
93 result=QString::fromUcs4((const uint *)input.constData(), realsize);
94 break;
95 default:
96 qCritical() << "sizeof(SQLTCHAR) is " << sizeof(SQLTCHAR) << "Don't know how to handle this";
97 }
98 return result;
99}
100
101inline static QVarLengthArray<SQLTCHAR> toSQLTCHAR(const QString &input)
102{
103 QVarLengthArray<SQLTCHAR> result;
104 result.resize(input.size());
105 switch(sizeof(SQLTCHAR)) {
106 case 1:
107 memcpy(result.data(), input.toUtf8().data(), input.size());
108 break;
109 case 2:
110 memcpy(result.data(), input.unicode(), input.size() * 2);
111 break;
112 case 4:
113 memcpy(result.data(), input.toUcs4().data(), input.size() * 4);
114 break;
115 default:
116 qCritical() << "sizeof(SQLTCHAR) is " << sizeof(SQLTCHAR) << "Don't know how to handle this";
117 }
118 result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't.
119 return result;
120}
121
122class QODBCDriverPrivate
123{
124public:
125 enum DefaultCase{Lower, Mixed, Upper, Sensitive};
126 QODBCDriverPrivate()
127 : hEnv(0), hDbc(0), unicode(false), useSchema(false), disconnectCount(0), isMySqlServer(false),
128 isMSSqlServer(false), isFreeTDSDriver(false), hasSQLFetchScroll(true),
129 hasMultiResultSets(false), isQuoteInitialized(false), quote(QLatin1Char('"'))
130 {
131 }
132
133 SQLHANDLE hEnv;
134 SQLHANDLE hDbc;
135
136 bool unicode;
137 bool useSchema;
138 int disconnectCount;
139 bool isMySqlServer;
140 bool isMSSqlServer;
141 bool isFreeTDSDriver;
142 bool hasSQLFetchScroll;
143 bool hasMultiResultSets;
144
145 bool checkDriver() const;
146 void checkUnicode();
147 void checkSqlServer();
148 void checkHasSQLFetchScroll();
149 void checkHasMultiResults();
150 void checkSchemaUsage();
151 bool setConnectionOptions(const QString& connOpts);
152 void splitTableQualifier(const QString &qualifier, QString &catalog,
153 QString &schema, QString &table);
154 DefaultCase defaultCase() const;
155 QString adjustCase(const QString&) const;
156 QChar quoteChar();
157private:
158 bool isQuoteInitialized;
159 QChar quote;
160};
161
162class QODBCPrivate
163{
164public:
165 QODBCPrivate(QODBCDriverPrivate *dpp)
166 : hStmt(0), useSchema(false), hasSQLFetchScroll(true), driverPrivate(dpp), userForwardOnly(false)
167 {
168 unicode = dpp->unicode;
169 useSchema = dpp->useSchema;
170 disconnectCount = dpp->disconnectCount;
171 hasSQLFetchScroll = dpp->hasSQLFetchScroll;
172 }
173
174 inline void clearValues()
175 { fieldCache.fill(QVariant()); fieldCacheIdx = 0; }
176
177 SQLHANDLE dpEnv() const { return driverPrivate ? driverPrivate->hEnv : 0;}
178 SQLHANDLE dpDbc() const { return driverPrivate ? driverPrivate->hDbc : 0;}
179 SQLHANDLE hStmt;
180
181 bool unicode;
182 bool useSchema;
183
184 QSqlRecord rInf;
185 QVector<QVariant> fieldCache;
186 int fieldCacheIdx;
187 int disconnectCount;
188 bool hasSQLFetchScroll;
189 QODBCDriverPrivate *driverPrivate;
190 bool userForwardOnly;
191
192 bool isStmtHandleValid(const QSqlDriver *driver);
193 void updateStmtHandleState(const QSqlDriver *driver);
194};
195
196bool QODBCPrivate::isStmtHandleValid(const QSqlDriver *driver)
197{
198 const QODBCDriver *odbcdriver = static_cast<const QODBCDriver*> (driver);
199 return disconnectCount == odbcdriver->d->disconnectCount;
200}
201
202void QODBCPrivate::updateStmtHandleState(const QSqlDriver *driver)
203{
204 const QODBCDriver *odbcdriver = static_cast<const QODBCDriver*> (driver);
205 disconnectCount = odbcdriver->d->disconnectCount;
206}
207
208static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0)
209{
210 SQLINTEGER nativeCode_ = 0;
211 SQLSMALLINT msgLen = 0;
212 SQLRETURN r = SQL_NO_DATA;
213 SQLTCHAR state_[SQL_SQLSTATE_SIZE+1];
214 QVarLengthArray<SQLTCHAR> description_(SQL_MAX_MESSAGE_LENGTH);
215 QString result;
216 int i = 1;
217
218 description_[0] = 0;
219 do {
220 r = SQLGetDiagRec(handleType,
221 handle,
222 i,
223 state_,
224 &nativeCode_,
225 0,
226 NULL,
227 &msgLen);
228 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && msgLen > 0)
229 description_.resize(msgLen+1);
230 r = SQLGetDiagRec(handleType,
231 handle,
232 i,
233 state_,
234 &nativeCode_,
235 description_.data(),
236 description_.size(),
237 &msgLen);
238 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
239 if (nativeCode)
240 *nativeCode = nativeCode_;
241 QString tmpstore;
242#ifdef UNICODE
243 tmpstore = fromSQLTCHAR(description_, msgLen);
244#else
245 tmpstore = QString::fromUtf8((const char*)description_.constData(), msgLen);
246#endif
247 if(result != tmpstore) {
248 if(!result.isEmpty())
249 result += QLatin1Char(' ');
250 result += tmpstore;
251 }
252 } else if (r == SQL_ERROR || r == SQL_INVALID_HANDLE) {
253 return result;
254 }
255 ++i;
256 } while (r != SQL_NO_DATA);
257 return result;
258}
259
260static QString qODBCWarn(const QODBCPrivate* odbc, int *nativeCode = 0)
261{
262 return QString(qWarnODBCHandle(SQL_HANDLE_ENV, odbc->dpEnv()) + QLatin1Char(' ')
263 + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->dpDbc()) + QLatin1Char(' ')
264 + qWarnODBCHandle(SQL_HANDLE_STMT, odbc->hStmt, nativeCode)).simplified();
265}
266
267static QString qODBCWarn(const QODBCDriverPrivate* odbc, int *nativeCode = 0)
268{
269 return QString(qWarnODBCHandle(SQL_HANDLE_ENV, odbc->hEnv) + QLatin1Char(' ')
270 + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->hDbc, nativeCode)).simplified();
271}
272
273static void qSqlWarning(const QString& message, const QODBCPrivate* odbc)
274{
275 qWarning() << message << "\tError:" << qODBCWarn(odbc);
276}
277
278static void qSqlWarning(const QString &message, const QODBCDriverPrivate *odbc)
279{
280 qWarning() << message << "\tError:" << qODBCWarn(odbc);
281}
282
283static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, const QODBCPrivate* p)
284{
285 int nativeCode = -1;
286 QString message = qODBCWarn(p, &nativeCode);
287 return QSqlError(QLatin1String("QODBC3: ") + err, message, type, nativeCode);
288}
289
290static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
291 const QODBCDriverPrivate* p)
292{
293 int nativeCode = -1;
294 QString message = qODBCWarn(p, &nativeCode);
295 return QSqlError(QLatin1String("QODBC3: ") + err, qODBCWarn(p), type, nativeCode);
296}
297
298template<class T>
299static QVariant::Type qDecodeODBCType(SQLSMALLINT sqltype, const T* p, bool isSigned = true)
300{
301 Q_UNUSED(p);
302 QVariant::Type type = QVariant::Invalid;
303 switch (sqltype) {
304 case SQL_DECIMAL:
305 case SQL_NUMERIC:
306 case SQL_REAL:
307 case SQL_FLOAT:
308 case SQL_DOUBLE:
309 type = QVariant::Double;
310 break;
311 case SQL_SMALLINT:
312 case SQL_INTEGER:
313 case SQL_BIT:
314 type = isSigned ? QVariant::Int : QVariant::UInt;
315 break;
316 case SQL_TINYINT:
317 type = QVariant::UInt;
318 break;
319 case SQL_BIGINT:
320 type = isSigned ? QVariant::LongLong : QVariant::ULongLong;
321 break;
322 case SQL_BINARY:
323 case SQL_VARBINARY:
324 case SQL_LONGVARBINARY:
325 type = QVariant::ByteArray;
326 break;
327 case SQL_DATE:
328 case SQL_TYPE_DATE:
329 type = QVariant::Date;
330 break;
331 case SQL_TIME:
332 case SQL_TYPE_TIME:
333 type = QVariant::Time;
334 break;
335 case SQL_TIMESTAMP:
336 case SQL_TYPE_TIMESTAMP:
337 type = QVariant::DateTime;
338 break;
339 case SQL_WCHAR:
340 case SQL_WVARCHAR:
341 case SQL_WLONGVARCHAR:
342 type = QVariant::String;
343 break;
344 case SQL_CHAR:
345 case SQL_VARCHAR:
346#if (ODBCVER >= 0x0350)
347 case SQL_GUID:
348#endif
349 case SQL_LONGVARCHAR:
350 type = QVariant::String;
351 break;
352 default:
353 type = QVariant::ByteArray;
354 break;
355 }
356 return type;
357}
358
359static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool unicode = false)
360{
361 QString fieldVal;
362 SQLRETURN r = SQL_ERROR;
363 QSQLLEN lengthIndicator = 0;
364
365 // NB! colSize must be a multiple of 2 for unicode enabled DBs
366 if (colSize <= 0) {
367 colSize = 256;
368 } else if (colSize > 65536) { // limit buffer size to 64 KB
369 colSize = 65536;
370 } else {
371 colSize++; // make sure there is room for more than the 0 termination
372 }
373 if(unicode) {
374 r = SQLGetData(hStmt,
375 column+1,
376 SQL_C_TCHAR,
377 NULL,
378 0,
379 &lengthIndicator);
380 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
381 colSize = lengthIndicator/sizeof(SQLTCHAR) + 1;
382 QVarLengthArray<SQLTCHAR> buf(colSize);
383 memset(buf.data(), 0, colSize*sizeof(SQLTCHAR));
384 while (true) {
385 r = SQLGetData(hStmt,
386 column+1,
387 SQL_C_TCHAR,
388 (SQLPOINTER)buf.data(),
389 colSize*sizeof(SQLTCHAR),
390 &lengthIndicator);
391 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
392 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
393 fieldVal.clear();
394 break;
395 }
396 // if SQL_SUCCESS_WITH_INFO is returned, indicating that
397 // more data can be fetched, the length indicator does NOT
398 // contain the number of bytes returned - it contains the
399 // total number of bytes that CAN be fetched
400 // colSize-1: remove 0 termination when there is more data to fetch
401 int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator/sizeof(SQLTCHAR);
402 fieldVal += fromSQLTCHAR(buf, rSize);
403 if (lengthIndicator < (unsigned int)colSize*sizeof(SQLTCHAR)) {
404 // workaround for Drivermanagers that don't return SQL_NO_DATA
405 break;
406 }
407 } else if (r == SQL_NO_DATA) {
408 break;
409 } else {
410 qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
411 fieldVal.clear();
412 break;
413 }
414 }
415 } else {
416 r = SQLGetData(hStmt,
417 column+1,
418 SQL_C_CHAR,
419 NULL,
420 0,
421 &lengthIndicator);
422 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
423 colSize = lengthIndicator + 1;
424 QVarLengthArray<SQLCHAR> buf(colSize);
425 while (true) {
426 r = SQLGetData(hStmt,
427 column+1,
428 SQL_C_CHAR,
429 (SQLPOINTER)buf.data(),
430 colSize,
431 &lengthIndicator);
432 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
433 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
434 fieldVal.clear();
435 break;
436 }
437 // if SQL_SUCCESS_WITH_INFO is returned, indicating that
438 // more data can be fetched, the length indicator does NOT
439 // contain the number of bytes returned - it contains the
440 // total number of bytes that CAN be fetched
441 // colSize-1: remove 0 termination when there is more data to fetch
442 int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator;
443 fieldVal += QString::fromUtf8((const char *)buf.constData(), rSize);
444 if (lengthIndicator < (unsigned int)colSize) {
445 // workaround for Drivermanagers that don't return SQL_NO_DATA
446 break;
447 }
448 } else if (r == SQL_NO_DATA) {
449 break;
450 } else {
451 qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
452 fieldVal.clear();
453 break;
454 }
455 }
456 }
457 return fieldVal;
458}
459
460static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
461{
462 QByteArray fieldVal;
463 SQLSMALLINT colNameLen;
464 SQLSMALLINT colType;
465 QSQLULEN colSize;
466 SQLSMALLINT colScale;
467 SQLSMALLINT nullable;
468 QSQLLEN lengthIndicator = 0;
469 SQLRETURN r = SQL_ERROR;
470
471 QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
472
473 r = SQLDescribeCol(hStmt,
474 column + 1,
475 colName.data(),
476 COLNAMESIZE,
477 &colNameLen,
478 &colType,
479 &colSize,
480 &colScale,
481 &nullable);
482 if (r != SQL_SUCCESS)
483 qWarning() << "qGetBinaryData: Unable to describe column" << column;
484 // SQLDescribeCol may return 0 if size cannot be determined
485 if (!colSize)
486 colSize = 255;
487 else if (colSize > 65536) // read the field in 64 KB chunks
488 colSize = 65536;
489 fieldVal.resize(colSize);
490 ulong read = 0;
491 while (true) {
492 r = SQLGetData(hStmt,
493 column+1,
494 SQL_C_BINARY,
495 (SQLPOINTER)(fieldVal.constData() + read),
496 colSize,
497 &lengthIndicator);
498 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
499 break;
500 if (lengthIndicator == SQL_NULL_DATA)
501 return QVariant(QVariant::ByteArray);
502 if (lengthIndicator > QSQLLEN(colSize) || lengthIndicator == SQL_NO_TOTAL) {
503 read += colSize;
504 colSize = 65536;
505 } else {
506 read += lengthIndicator;
507 }
508 if (r == SQL_SUCCESS) { // the whole field was read in one chunk
509 fieldVal.resize(read);
510 break;
511 }
512 fieldVal.resize(fieldVal.size() + colSize);
513 }
514 return fieldVal;
515}
516
517static QVariant qGetIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
518{
519 SQLINTEGER intbuf = 0;
520 QSQLLEN lengthIndicator = 0;
521 SQLRETURN r = SQLGetData(hStmt,
522 column+1,
523 isSigned ? SQL_C_SLONG : SQL_C_ULONG,
524 (SQLPOINTER)&intbuf,
525 sizeof(intbuf),
526 &lengthIndicator);
527 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
528 return QVariant(QVariant::Invalid);
529 if (lengthIndicator == SQL_NULL_DATA)
530 return QVariant(QVariant::Int);
531 if (isSigned)
532 return int(intbuf);
533 else
534 return uint(intbuf);
535}
536
537static QVariant qGetDoubleData(SQLHANDLE hStmt, int column)
538{
539 SQLDOUBLE dblbuf;
540 QSQLLEN lengthIndicator = 0;
541 SQLRETURN r = SQLGetData(hStmt,
542 column+1,
543 SQL_C_DOUBLE,
544 (SQLPOINTER) &dblbuf,
545 0,
546 &lengthIndicator);
547 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
548 return QVariant(QVariant::Invalid);
549 }
550 if(lengthIndicator == SQL_NULL_DATA)
551 return QVariant(QVariant::Double);
552
553 return (double) dblbuf;
554}
555
556
557static QVariant qGetBigIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
558{
559 SQLBIGINT lngbuf = 0;
560 QSQLLEN lengthIndicator = 0;
561 SQLRETURN r = SQLGetData(hStmt,
562 column+1,
563 isSigned ? SQL_C_SBIGINT : SQL_C_UBIGINT,
564 (SQLPOINTER) &lngbuf,
565 sizeof(lngbuf),
566 &lengthIndicator);
567 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
568 return QVariant(QVariant::Invalid);
569 if (lengthIndicator == SQL_NULL_DATA)
570 return QVariant(QVariant::LongLong);
571
572 if (isSigned)
573 return qint64(lngbuf);
574 else
575 return quint64(lngbuf);
576}
577
578// creates a QSqlField from a valid hStmt generated
579// by SQLColumns. The hStmt has to point to a valid position.
580static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, const QODBCDriverPrivate* p)
581{
582 QString fname = qGetStringData(hStmt, 3, -1, p->unicode);
583 int type = qGetIntData(hStmt, 4).toInt(); // column type
584 QSqlField f(fname, qDecodeODBCType(type, p));
585 int required = qGetIntData(hStmt, 10).toInt(); // nullable-flag
586 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
587 if (required == SQL_NO_NULLS)
588 f.setRequired(true);
589 else if (required == SQL_NULLABLE)
590 f.setRequired(false);
591 // else we don't know
592 QVariant var = qGetIntData(hStmt, 6);
593 f.setLength(var.isNull() ? -1 : var.toInt()); // column size
594 var = qGetIntData(hStmt, 8).toInt();
595 f.setPrecision(var.isNull() ? -1 : var.toInt()); // precision
596 f.setSqlType(type);
597 return f;
598}
599
600static QSqlField qMakeFieldInfo(const QODBCPrivate* p, int i )
601{
602 SQLSMALLINT colNameLen;
603 SQLSMALLINT colType;
604 QSQLULEN colSize;
605 SQLSMALLINT colScale;
606 SQLSMALLINT nullable;
607 SQLRETURN r = SQL_ERROR;
608 QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
609 r = SQLDescribeCol(p->hStmt,
610 i+1,
611 colName.data(),
612 (SQLSMALLINT)COLNAMESIZE,
613 &colNameLen,
614 &colType,
615 &colSize,
616 &colScale,
617 &nullable);
618
619 if (r != SQL_SUCCESS) {
620 qSqlWarning(QString::fromLatin1("qMakeField: Unable to describe column %1").arg(i), p);
621 return QSqlField();
622 }
623
624 QSQLLEN unsignedFlag = SQL_FALSE;
625 r = SQLColAttribute (p->hStmt,
626 i + 1,
627 SQL_DESC_UNSIGNED,
628 0,
629 0,
630 0,
631 &unsignedFlag);
632 if (r != SQL_SUCCESS) {
633 qSqlWarning(QString::fromLatin1("qMakeField: Unable to get column attributes for column %1").arg(i), p);
634 }
635
636#ifdef UNICODE
637 QString qColName(fromSQLTCHAR(colName, colNameLen));
638#else
639 QString qColName = QString::fromUtf8((const char *)colName.constData());
640#endif
641 // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
642 int required = -1;
643 if (nullable == SQL_NO_NULLS) {
644 required = 1;
645 } else if (nullable == SQL_NULLABLE) {
646 required = 0;
647 }
648 QVariant::Type type = qDecodeODBCType(colType, p, unsignedFlag == SQL_FALSE);
649 QSqlField f(qColName, type);
650 f.setSqlType(colType);
651 f.setLength(colSize == 0 ? -1 : int(colSize));
652 f.setPrecision(colScale == 0 ? -1 : int(colScale));
653 if (nullable == SQL_NO_NULLS)
654 f.setRequired(true);
655 else if (nullable == SQL_NULLABLE)
656 f.setRequired(false);
657 // else we don't know
658 return f;
659}
660
661static int qGetODBCVersion(const QString &connOpts)
662{
663 if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"), Qt::CaseInsensitive))
664 return SQL_OV_ODBC3;
665 return SQL_OV_ODBC2;
666}
667
668QChar QODBCDriverPrivate::quoteChar()
669{
670 if (!isQuoteInitialized) {
671 SQLTCHAR driverResponse[4];
672 SQLSMALLINT length;
673 int r = SQLGetInfo(hDbc,
674 SQL_IDENTIFIER_QUOTE_CHAR,
675 &driverResponse,
676 sizeof(driverResponse),
677 &length);
678 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
679#ifdef UNICODE
680 quote = QChar(driverResponse[0]);
681#else
682 quote = QLatin1Char(driverResponse[0]);
683#endif
684 else
685 quote = QLatin1Char('"');
686 isQuoteInitialized = true;
687 }
688 return quote;
689}
690
691
692bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
693{
694 // Set any connection attributes
695 const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
696 SQLRETURN r = SQL_SUCCESS;
697 for (int i = 0; i < opts.count(); ++i) {
698 const QString tmp(opts.at(i));
699 int idx;
700 if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
701 qWarning() << "QODBCDriver::open: Illegal connect option value '" << tmp << '\'';
702 continue;
703 }
704 const QString opt(tmp.left(idx));
705 const QString val(tmp.mid(idx + 1).simplified());
706 SQLUINTEGER v = 0;
707
708 r = SQL_SUCCESS;
709 if (opt.toUpper() == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
710 if (val.toUpper() == QLatin1String("SQL_MODE_READ_ONLY")) {
711 v = SQL_MODE_READ_ONLY;
712 } else if (val.toUpper() == QLatin1String("SQL_MODE_READ_WRITE")) {
713 v = SQL_MODE_READ_WRITE;
714 } else {
715 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
716 continue;
717 }
718 r = SQLSetConnectAttr(hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0);
719 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_TIMEOUT")) {
720 v = val.toUInt();
721 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) v, 0);
722 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
723 v = val.toUInt();
724 r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0);
725 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) {
726 val.utf16(); // 0 terminate
727 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
728#ifdef UNICODE
729 toSQLTCHAR(val).data(),
730#else
731 (SQLCHAR*) val.toUtf8().data(),
732#endif
733 val.length()*sizeof(SQLTCHAR));
734 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) {
735 if (val.toUpper() == QLatin1String("SQL_TRUE")) {
736 v = SQL_TRUE;
737 } else if (val.toUpper() == QLatin1String("SQL_FALSE")) {
738 v = SQL_FALSE;
739 } else {
740 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
741 continue;
742 }
743 r = SQLSetConnectAttr(hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) v, 0);
744 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_PACKET_SIZE")) {
745 v = val.toUInt();
746 r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) v, 0);
747 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) {
748 val.utf16(); // 0 terminate
749 r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
750#ifdef UNICODE
751 toSQLTCHAR(val).data(),
752#else
753 (SQLCHAR*) val.toUtf8().data(),
754#endif
755 val.length()*sizeof(SQLTCHAR));
756 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) {
757 if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) {
758 v = SQL_OPT_TRACE_OFF;
759 } else if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_ON")) {
760 v = SQL_OPT_TRACE_ON;
761 } else {
762 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
763 continue;
764 }
765 r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0);
766 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_POOLING")) {
767 if (val == QLatin1String("SQL_CP_OFF"))
768 v = SQL_CP_OFF;
769 else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_DRIVER"))
770 v = SQL_CP_ONE_PER_DRIVER;
771 else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_HENV"))
772 v = SQL_CP_ONE_PER_HENV;
773 else if (val.toUpper() == QLatin1String("SQL_CP_DEFAULT"))
774 v = SQL_CP_DEFAULT;
775 else {
776 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
777 continue;
778 }
779 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)v, 0);
780 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CP_MATCH")) {
781 if (val.toUpper() == QLatin1String("SQL_CP_STRICT_MATCH"))
782 v = SQL_CP_STRICT_MATCH;
783 else if (val.toUpper() == QLatin1String("SQL_CP_RELAXED_MATCH"))
784 v = SQL_CP_RELAXED_MATCH;
785 else if (val.toUpper() == QLatin1String("SQL_CP_MATCH_DEFAULT"))
786 v = SQL_CP_MATCH_DEFAULT;
787 else {
788 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
789 continue;
790 }
791 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CP_MATCH, (SQLPOINTER)v, 0);
792 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_ODBC_VERSION")) {
793 // Already handled in QODBCDriver::open()
794 continue;
795 } else {
796 qWarning() << "QODBCDriver::open: Unknown connection attribute '" << opt << '\'';
797 }
798 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
799 qSqlWarning(QString::fromLatin1("QODBCDriver::open: Unable to set connection attribute'%1'").arg(
800 opt), this);
801 }
802 return true;
803}
804
805void QODBCDriverPrivate::splitTableQualifier(const QString & qualifier, QString &catalog,
806 QString &schema, QString &table)
807{
808 if (!useSchema) {
809 table = qualifier;
810 return;
811 }
812 QStringList l = qualifier.split(QLatin1Char('.'));
813 if (l.count() > 3)
814 return; // can't possibly be a valid table qualifier
815 int i = 0, n = l.count();
816 if (n == 1) {
817 table = qualifier;
818 } else {
819 for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
820 if (n == 3) {
821 if (i == 0) {
822 catalog = *it;
823 } else if (i == 1) {
824 schema = *it;
825 } else if (i == 2) {
826 table = *it;
827 }
828 } else if (n == 2) {
829 if (i == 0) {
830 schema = *it;
831 } else if (i == 1) {
832 table = *it;
833 }
834 }
835 i++;
836 }
837 }
838}
839
840QODBCDriverPrivate::DefaultCase QODBCDriverPrivate::defaultCase() const
841{
842 DefaultCase ret;
843 SQLUSMALLINT casing;
844 int r = SQLGetInfo(hDbc,
845 SQL_IDENTIFIER_CASE,
846 &casing,
847 sizeof(casing),
848 NULL);
849 if ( r != SQL_SUCCESS)
850 ret = Mixed;//arbitrary case if driver cannot be queried
851 else {
852 switch (casing) {
853 case (SQL_IC_UPPER):
854 ret = Upper;
855 break;
856 case (SQL_IC_LOWER):
857 ret = Lower;
858 break;
859 case (SQL_IC_SENSITIVE):
860 ret = Sensitive;
861 break;
862 case (SQL_IC_MIXED):
863 default:
864 ret = Mixed;
865 break;
866 }
867 }
868 return ret;
869}
870
871/*
872 Adjust the casing of an identifier to match what the
873 database engine would have done to it.
874*/
875QString QODBCDriverPrivate::adjustCase(const QString &identifier) const
876{
877 QString ret = identifier;
878 switch(defaultCase()) {
879 case (Lower):
880 ret = identifier.toLower();
881 break;
882 case (Upper):
883 ret = identifier.toUpper();
884 break;
885 case(Mixed):
886 case(Sensitive):
887 default:
888 ret = identifier;
889 }
890 return ret;
891}
892
893////////////////////////////////////////////////////////////////////////////
894
895QODBCResult::QODBCResult(const QODBCDriver * db, QODBCDriverPrivate* p)
896: QSqlResult(db)
897{
898 d = new QODBCPrivate(p);
899}
900
901QODBCResult::~QODBCResult()
902{
903 if (d->hStmt && d->isStmtHandleValid(driver()) && driver()->isOpen()) {
904 SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
905 if (r != SQL_SUCCESS)
906 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ")
907 + QString::number(r), d);
908 }
909
910 delete d;
911}
912
913bool QODBCResult::reset (const QString& query)
914{
915 setActive(false);
916 setAt(QSql::BeforeFirstRow);
917 d->rInf.clear();
918 d->fieldCache.clear();
919 d->fieldCacheIdx = 0;
920
921 // Always reallocate the statement handle - the statement attributes
922 // are not reset if SQLFreeStmt() is called which causes some problems.
923 SQLRETURN r;
924 if (d->hStmt && d->isStmtHandleValid(driver())) {
925 r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
926 if (r != SQL_SUCCESS) {
927 qSqlWarning(QLatin1String("QODBCResult::reset: Unable to free statement handle"), d);
928 return false;
929 }
930 }
931 r = SQLAllocHandle(SQL_HANDLE_STMT,
932 d->dpDbc(),
933 &d->hStmt);
934 if (r != SQL_SUCCESS) {
935 qSqlWarning(QLatin1String("QODBCResult::reset: Unable to allocate statement handle"), d);
936 return false;
937 }
938
939 d->updateStmtHandleState(driver());
940
941 if (d->userForwardOnly) {
942 r = SQLSetStmtAttr(d->hStmt,
943 SQL_ATTR_CURSOR_TYPE,
944 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
945 SQL_IS_UINTEGER);
946 } else {
947 r = SQLSetStmtAttr(d->hStmt,
948 SQL_ATTR_CURSOR_TYPE,
949 (SQLPOINTER)SQL_CURSOR_STATIC,
950 SQL_IS_UINTEGER);
951 }
952 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
953 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
954 "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
955 "Please check your ODBC driver configuration"), QSqlError::StatementError, d));
956 return false;
957 }
958
959#ifdef UNICODE
960 r = SQLExecDirect(d->hStmt,
961 toSQLTCHAR(query).data(),
962 (SQLINTEGER) query.length());
963#else
964 QByteArray query8 = query.toUtf8();
965 r = SQLExecDirect(d->hStmt,
966 (SQLCHAR*) query8.data(),
967 (SQLINTEGER) query8.length());
968#endif
969 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) {
970 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
971 "Unable to execute statement"), QSqlError::StatementError, d));
972 return false;
973 }
974
975 if(r == SQL_NO_DATA) {
976 setSelect(false);
977 return true;
978 }
979
980 SQLINTEGER isScrollable, bufferLength;
981 r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, &bufferLength);
982 if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
983 QSqlResult::setForwardOnly(isScrollable==SQL_NONSCROLLABLE);
984
985 SQLSMALLINT count;
986 SQLNumResultCols(d->hStmt, &count);
987 if (count) {
988 setSelect(true);
989 for (int i = 0; i < count; ++i) {
990 d->rInf.append(qMakeFieldInfo(d, i));
991 }
992 d->fieldCache.resize(count);
993 } else {
994 setSelect(false);
995 }
996 setActive(true);
997
998 return true;
999}
1000
1001bool QODBCResult::fetch(int i)
1002{
1003 if (!driver()->isOpen())
1004 return false;
1005
1006 if (isForwardOnly() && i < at())
1007 return false;
1008 if (i == at())
1009 return true;
1010 d->clearValues();
1011 int actualIdx = i + 1;
1012 if (actualIdx <= 0) {
1013 setAt(QSql::BeforeFirstRow);
1014 return false;
1015 }
1016 SQLRETURN r;
1017 if (isForwardOnly()) {
1018 bool ok = true;
1019 while (ok && i > at())
1020 ok = fetchNext();
1021 return ok;
1022 } else {
1023 r = SQLFetchScroll(d->hStmt,
1024 SQL_FETCH_ABSOLUTE,
1025 actualIdx);
1026 }
1027 if (r != SQL_SUCCESS) {
1028 if (r != SQL_NO_DATA)
1029 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1030 "Unable to fetch"), QSqlError::ConnectionError, d));
1031 return false;
1032 }
1033 setAt(i);
1034 return true;
1035}
1036
1037bool QODBCResult::fetchNext()
1038{
1039 SQLRETURN r;
1040 d->clearValues();
1041
1042 if (d->hasSQLFetchScroll)
1043 r = SQLFetchScroll(d->hStmt,
1044 SQL_FETCH_NEXT,
1045 0);
1046 else
1047 r = SQLFetch(d->hStmt);
1048
1049 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1050 if (r != SQL_NO_DATA)
1051 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1052 "Unable to fetch next"), QSqlError::ConnectionError, d));
1053 return false;
1054 }
1055 setAt(at() + 1);
1056 return true;
1057}
1058
1059bool QODBCResult::fetchFirst()
1060{
1061 if (isForwardOnly() && at() != QSql::BeforeFirstRow)
1062 return false;
1063 SQLRETURN r;
1064 d->clearValues();
1065 if (isForwardOnly()) {
1066 return fetchNext();
1067 }
1068 r = SQLFetchScroll(d->hStmt,
1069 SQL_FETCH_FIRST,
1070 0);
1071 if (r != SQL_SUCCESS) {
1072 if (r != SQL_NO_DATA)
1073 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1074 "Unable to fetch first"), QSqlError::ConnectionError, d));
1075 return false;
1076 }
1077 setAt(0);
1078 return true;
1079}
1080
1081bool QODBCResult::fetchPrevious()
1082{
1083 if (isForwardOnly())
1084 return false;
1085 SQLRETURN r;
1086 d->clearValues();
1087 r = SQLFetchScroll(d->hStmt,
1088 SQL_FETCH_PRIOR,
1089 0);
1090 if (r != SQL_SUCCESS) {
1091 if (r != SQL_NO_DATA)
1092 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1093 "Unable to fetch previous"), QSqlError::ConnectionError, d));
1094 return false;
1095 }
1096 setAt(at() - 1);
1097 return true;
1098}
1099
1100bool QODBCResult::fetchLast()
1101{
1102 SQLRETURN r;
1103 d->clearValues();
1104
1105 if (isForwardOnly()) {
1106 // cannot seek to last row in forwardOnly mode, so we have to use brute force
1107 int i = at();
1108 if (i == QSql::AfterLastRow)
1109 return false;
1110 if (i == QSql::BeforeFirstRow)
1111 i = 0;
1112 while (fetchNext())
1113 ++i;
1114 setAt(i);
1115 return true;
1116 }
1117
1118 r = SQLFetchScroll(d->hStmt,
1119 SQL_FETCH_LAST,
1120 0);
1121 if (r != SQL_SUCCESS) {
1122 if (r != SQL_NO_DATA)
1123 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1124 "Unable to fetch last"), QSqlError::ConnectionError, d));
1125 return false;
1126 }
1127 SQLINTEGER currRow;
1128 r = SQLGetStmtAttr(d->hStmt,
1129 SQL_ROW_NUMBER,
1130 &currRow,
1131 SQL_IS_INTEGER,
1132 0);
1133 if (r != SQL_SUCCESS)
1134 return false;
1135 setAt(currRow-1);
1136 return true;
1137}
1138
1139QVariant QODBCResult::data(int field)
1140{
1141 if (field >= d->rInf.count() || field < 0) {
1142 qWarning() << "QODBCResult::data: column" << field << "out of range";
1143 return QVariant();
1144 }
1145 if (field < d->fieldCacheIdx)
1146 return d->fieldCache.at(field);
1147
1148 SQLRETURN r(0);
1149 QSQLLEN lengthIndicator = 0;
1150
1151 for (int i = d->fieldCacheIdx; i <= field; ++i) {
1152 // some servers do not support fetching column n after we already
1153 // fetched column n+1, so cache all previous columns here
1154 const QSqlField info = d->rInf.field(i);
1155 switch (info.type()) {
1156 case QVariant::LongLong:
1157 d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
1158 break;
1159 case QVariant::ULongLong:
1160 d->fieldCache[i] = qGetBigIntData(d->hStmt, i, false);
1161 break;
1162 case QVariant::Int:
1163 d->fieldCache[i] = qGetIntData(d->hStmt, i);
1164 break;
1165 case QVariant::UInt:
1166 d->fieldCache[i] = qGetIntData(d->hStmt, i, false);
1167 break;
1168 case QVariant::Date:
1169 DATE_STRUCT dbuf;
1170 r = SQLGetData(d->hStmt,
1171 i + 1,
1172 SQL_C_DATE,
1173 (SQLPOINTER)&dbuf,
1174 0,
1175 &lengthIndicator);
1176 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
1177 d->fieldCache[i] = QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
1178 else
1179 d->fieldCache[i] = QVariant(QVariant::Date);
1180 break;
1181 case QVariant::Time:
1182 TIME_STRUCT tbuf;
1183 r = SQLGetData(d->hStmt,
1184 i + 1,
1185 SQL_C_TIME,
1186 (SQLPOINTER)&tbuf,
1187 0,
1188 &lengthIndicator);
1189 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
1190 d->fieldCache[i] = QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
1191 else
1192 d->fieldCache[i] = QVariant(QVariant::Time);
1193 break;
1194 case QVariant::DateTime:
1195 TIMESTAMP_STRUCT dtbuf;
1196 r = SQLGetData(d->hStmt,
1197 i + 1,
1198 SQL_C_TIMESTAMP,
1199 (SQLPOINTER)&dtbuf,
1200 0,
1201 &lengthIndicator);
1202 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
1203 d->fieldCache[i] = QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
1204 QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
1205 else
1206 d->fieldCache[i] = QVariant(QVariant::DateTime);
1207 break;
1208 case QVariant::ByteArray:
1209 d->fieldCache[i] = qGetBinaryData(d->hStmt, i);
1210 break;
1211 case QVariant::String:
1212 d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), d->unicode);
1213 break;
1214 case QVariant::Double:
1215 switch(numericalPrecisionPolicy()) {
1216 case QSql::LowPrecisionInt32:
1217 d->fieldCache[i] = qGetIntData(d->hStmt, i);
1218 break;
1219 case QSql::LowPrecisionInt64:
1220 d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
1221 break;
1222 case QSql::LowPrecisionDouble:
1223 d->fieldCache[i] = qGetDoubleData(d->hStmt, i);
1224 break;
1225 case QSql::HighPrecision:
1226 d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), false);
1227 break;
1228 }
1229 break;
1230 default:
1231 d->fieldCache[i] = QVariant(qGetStringData(d->hStmt, i, info.length(), false));
1232 break;
1233 }
1234 d->fieldCacheIdx = field + 1;
1235 }
1236 return d->fieldCache[field];
1237}
1238
1239bool QODBCResult::isNull(int field)
1240{
1241 if (field < 0 || field > d->fieldCache.size())
1242 return true;
1243 if (field <= d->fieldCacheIdx) {
1244 // since there is no good way to find out whether the value is NULL
1245 // without fetching the field we'll fetch it here.
1246 // (data() also sets the NULL flag)
1247 data(field);
1248 }
1249 return d->fieldCache.at(field).isNull();
1250}
1251
1252int QODBCResult::size()
1253{
1254 return -1;
1255}
1256
1257int QODBCResult::numRowsAffected()
1258{
1259 QSQLLEN affectedRowCount = 0;
1260 SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
1261 if (r == SQL_SUCCESS)
1262 return affectedRowCount;
1263 else
1264 qSqlWarning(QLatin1String("QODBCResult::numRowsAffected: Unable to count affected rows"), d);
1265 return -1;
1266}
1267
1268bool QODBCResult::prepare(const QString& query)
1269{
1270 setActive(false);
1271 setAt(QSql::BeforeFirstRow);
1272 SQLRETURN r;
1273
1274 d->rInf.clear();
1275 if (d->hStmt && d->isStmtHandleValid(driver())) {
1276 r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
1277 if (r != SQL_SUCCESS) {
1278 qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to close statement"), d);
1279 return false;
1280 }
1281 }
1282 r = SQLAllocHandle(SQL_HANDLE_STMT,
1283 d->dpDbc(),
1284 &d->hStmt);
1285 if (r != SQL_SUCCESS) {
1286 qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to allocate statement handle"), d);
1287 return false;
1288 }
1289
1290 d->updateStmtHandleState(driver());
1291
1292 if (d->userForwardOnly) {
1293 r = SQLSetStmtAttr(d->hStmt,
1294 SQL_ATTR_CURSOR_TYPE,
1295 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1296 SQL_IS_UINTEGER);
1297 } else {
1298 r = SQLSetStmtAttr(d->hStmt,
1299 SQL_ATTR_CURSOR_TYPE,
1300 (SQLPOINTER)SQL_CURSOR_STATIC,
1301 SQL_IS_UINTEGER);
1302 }
1303 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1304 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1305 "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
1306 "Please check your ODBC driver configuration"), QSqlError::StatementError, d));
1307 return false;
1308 }
1309
1310#ifdef UNICODE
1311 r = SQLPrepare(d->hStmt,
1312 toSQLTCHAR(query).data(),
1313 (SQLINTEGER) query.length());
1314#else
1315 QByteArray query8 = query.toUtf8();
1316 r = SQLPrepare(d->hStmt,
1317 (SQLCHAR*) query8.data(),
1318 (SQLINTEGER) query8.length());
1319#endif
1320
1321 if (r != SQL_SUCCESS) {
1322 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1323 "Unable to prepare statement"), QSqlError::StatementError, d));
1324 return false;
1325 }
1326 return true;
1327}
1328
1329bool QODBCResult::exec()
1330{
1331 setActive(false);
1332 setAt(QSql::BeforeFirstRow);
1333 d->rInf.clear();
1334 d->fieldCache.clear();
1335 d->fieldCacheIdx = 0;
1336
1337 if (!d->hStmt) {
1338 qSqlWarning(QLatin1String("QODBCResult::exec: No statement handle available"), d);
1339 return false;
1340 }
1341
1342 if (isSelect())
1343 SQLCloseCursor(d->hStmt);
1344
1345 QList<QByteArray> tmpStorage; // holds temporary buffers
1346 QVarLengthArray<QSQLLEN, 32> indicators(boundValues().count());
1347 memset(indicators.data(), 0, indicators.size() * sizeof(QSQLLEN));
1348
1349 // bind parameters - only positional binding allowed
1350 QVector<QVariant>& values = boundValues();
1351 int i;
1352 SQLRETURN r;
1353 for (i = 0; i < values.count(); ++i) {
1354 if (bindValueType(i) & QSql::Out)
1355 values[i].detach();
1356 const QVariant &val = values.at(i);
1357 QSQLLEN *ind = &indicators[i];
1358 if (val.isNull())
1359 *ind = SQL_NULL_DATA;
1360 switch (val.type()) {
1361 case QVariant::Date: {
1362 QByteArray ba;
1363 ba.resize(sizeof(DATE_STRUCT));
1364 DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData();
1365 QDate qdt = val.toDate();
1366 dt->year = qdt.year();
1367 dt->month = qdt.month();
1368 dt->day = qdt.day();
1369 r = SQLBindParameter(d->hStmt,
1370 i + 1,
1371 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1372 SQL_C_DATE,
1373 SQL_DATE,
1374 0,
1375 0,
1376 (void *) dt,
1377 0,
1378 *ind == SQL_NULL_DATA ? ind : NULL);
1379 tmpStorage.append(ba);
1380 break; }
1381 case QVariant::Time: {
1382 QByteArray ba;
1383 ba.resize(sizeof(TIME_STRUCT));
1384 TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData();
1385 QTime qdt = val.toTime();
1386 dt->hour = qdt.hour();
1387 dt->minute = qdt.minute();
1388 dt->second = qdt.second();
1389 r = SQLBindParameter(d->hStmt,
1390 i + 1,
1391 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1392 SQL_C_TIME,
1393 SQL_TIME,
1394 0,
1395 0,
1396 (void *) dt,
1397 0,
1398 *ind == SQL_NULL_DATA ? ind : NULL);
1399 tmpStorage.append(ba);
1400 break; }
1401 case QVariant::DateTime: {
1402 QByteArray ba;
1403 ba.resize(sizeof(TIMESTAMP_STRUCT));
1404 TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData();
1405 QDateTime qdt = val.toDateTime();
1406 dt->year = qdt.date().year();
1407 dt->month = qdt.date().month();
1408 dt->day = qdt.date().day();
1409 dt->hour = qdt.time().hour();
1410 dt->minute = qdt.time().minute();
1411 dt->second = qdt.time().second();
1412 dt->fraction = qdt.time().msec() * 1000000;
1413 r = SQLBindParameter(d->hStmt,
1414 i + 1,
1415 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1416 SQL_C_TIMESTAMP,
1417 SQL_TIMESTAMP,
1418 19,
1419 0,
1420 (void *) dt,
1421 0,
1422 *ind == SQL_NULL_DATA ? ind : NULL);
1423 tmpStorage.append(ba);
1424 break; }
1425 case QVariant::Int:
1426 r = SQLBindParameter(d->hStmt,
1427 i + 1,
1428 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1429 SQL_C_SLONG,
1430 SQL_INTEGER,
1431 0,
1432 0,
1433 (void *) val.constData(),
1434 0,
1435 *ind == SQL_NULL_DATA ? ind : NULL);
1436 break;
1437 case QVariant::UInt:
1438 r = SQLBindParameter(d->hStmt,
1439 i + 1,
1440 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1441 SQL_C_ULONG,
1442 SQL_NUMERIC,
1443 15,
1444 0,
1445 (void *) val.constData(),
1446 0,
1447 *ind == SQL_NULL_DATA ? ind : NULL);
1448 break;
1449 case QVariant::Double:
1450 r = SQLBindParameter(d->hStmt,
1451 i + 1,
1452 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1453 SQL_C_DOUBLE,
1454 SQL_DOUBLE,
1455 0,
1456 0,
1457 (void *) val.constData(),
1458 0,
1459 *ind == SQL_NULL_DATA ? ind : NULL);
1460 break;
1461 case QVariant::LongLong:
1462 r = SQLBindParameter(d->hStmt,
1463 i + 1,
1464 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1465 SQL_C_SBIGINT,
1466 SQL_BIGINT,
1467 0,
1468 0,
1469 (void *) val.constData(),
1470 0,
1471 *ind == SQL_NULL_DATA ? ind : NULL);
1472 break;
1473 case QVariant::ULongLong:
1474 r = SQLBindParameter(d->hStmt,
1475 i + 1,
1476 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1477 SQL_C_UBIGINT,
1478 SQL_BIGINT,
1479 0,
1480 0,
1481 (void *) val.constData(),
1482 0,
1483 *ind == SQL_NULL_DATA ? ind : NULL);
1484 break;
1485 case QVariant::ByteArray:
1486 if (*ind != SQL_NULL_DATA) {
1487 *ind = val.toByteArray().size();
1488 }
1489 r = SQLBindParameter(d->hStmt,
1490 i + 1,
1491 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1492 SQL_C_BINARY,
1493 SQL_LONGVARBINARY,
1494 val.toByteArray().size(),
1495 0,
1496 (void *) val.toByteArray().constData(),
1497 val.toByteArray().size(),
1498 ind);
1499 break;
1500 case QVariant::Bool:
1501 r = SQLBindParameter(d->hStmt,
1502 i + 1,
1503 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1504 SQL_C_BIT,
1505 SQL_BIT,
1506 0,
1507 0,
1508 (void *) val.constData(),
1509 0,
1510 *ind == SQL_NULL_DATA ? ind : NULL);
1511 break;
1512 case QVariant::String:
1513 if (d->unicode) {
1514 QString str = val.toString();
1515 if (*ind != SQL_NULL_DATA)
1516 *ind = str.length() * sizeof(SQLTCHAR);
1517 int strSize = str.length() * sizeof(SQLTCHAR);
1518
1519 if (bindValueType(i) & QSql::Out) {
1520 QVarLengthArray<SQLTCHAR> ba(toSQLTCHAR(str));
1521 ba.reserve(str.capacity());
1522 r = SQLBindParameter(d->hStmt,
1523 i + 1,
1524 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1525 SQL_C_TCHAR,
1526 strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1527 0, // god knows... don't change this!
1528 0,
1529 (void *)ba.constData(),
1530 ba.size(),
1531 ind);
1532 tmpStorage.append(QByteArray((const char *)ba.constData(), ba.size()*sizeof(SQLTCHAR)));
1533 break;
1534 }
1535 QByteArray strba((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR));
1536 r = SQLBindParameter(d->hStmt,
1537 i + 1,
1538 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1539 SQL_C_TCHAR,
1540 strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1541 strSize,
1542 0,
1543 (SQLPOINTER)strba.constData(),
1544 strba.size(),
1545 ind);
1546 tmpStorage.append(strba);
1547 break;
1548 }
1549 else
1550 {
1551 QByteArray str = val.toString().toUtf8();
1552 if (*ind != SQL_NULL_DATA)
1553 *ind = str.length();
1554 int strSize = str.length();
1555
1556 r = SQLBindParameter(d->hStmt,
1557 i + 1,
1558 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1559 SQL_C_CHAR,
1560 strSize > 254 ? SQL_LONGVARCHAR : SQL_VARCHAR,
1561 strSize,
1562 0,
1563 (void *)str.constData(),
1564 strSize,
1565 ind);
1566 tmpStorage.append(str);
1567 break;
1568 }
1569 // fall through
1570 default: {
1571 QByteArray ba = val.toByteArray();
1572 if (*ind != SQL_NULL_DATA)
1573 *ind = ba.size();
1574 r = SQLBindParameter(d->hStmt,
1575 i + 1,
1576 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1577 SQL_C_BINARY,
1578 SQL_VARBINARY,
1579 ba.length() + 1,
1580 0,
1581 (void *) ba.constData(),
1582 ba.length() + 1,
1583 ind);
1584 tmpStorage.append(ba);
1585 break; }
1586 }
1587 if (r != SQL_SUCCESS) {
1588 qWarning() << "QODBCResult::exec: unable to bind variable:" << qODBCWarn(d);
1589 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1590 "Unable to bind variable"), QSqlError::StatementError, d));
1591 return false;
1592 }
1593 }
1594 r = SQLExecute(d->hStmt);
1595 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1596 qWarning() << "QODBCResult::exec: Unable to execute statement:" << qODBCWarn(d);
1597 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1598 "Unable to execute statement"), QSqlError::StatementError, d));
1599 return false;
1600 }
1601
1602 SQLINTEGER isScrollable, bufferLength;
1603 r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, &bufferLength);
1604 if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1605 QSqlResult::setForwardOnly(isScrollable==SQL_NONSCROLLABLE);
1606
1607 SQLSMALLINT count;
1608 SQLNumResultCols(d->hStmt, &count);
1609 if (count) {
1610 setSelect(true);
1611 for (int i = 0; i < count; ++i) {
1612 d->rInf.append(qMakeFieldInfo(d, i));
1613 }
1614 d->fieldCache.resize(count);
1615 } else {
1616 setSelect(false);
1617 }
1618 setActive(true);
1619
1620
1621 //get out parameters
1622 if (!hasOutValues())
1623 return true;
1624
1625 for (i = 0; i < values.count(); ++i) {
1626 switch (values.at(i).type()) {
1627 case QVariant::Date: {
1628 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
1629 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
1630 break; }
1631 case QVariant::Time: {
1632 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
1633 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
1634 break; }
1635 case QVariant::DateTime: {
1636 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT*)
1637 tmpStorage.takeFirst().constData());
1638 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
1639 QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
1640 break; }
1641 case QVariant::Bool:
1642 case QVariant::Int:
1643 case QVariant::UInt:
1644 case QVariant::Double:
1645 case QVariant::ByteArray:
1646 case QVariant::LongLong:
1647 case QVariant::ULongLong:
1648 //nothing to do
1649 break;
1650 case QVariant::String:
1651 if (d->unicode) {
1652 if (bindValueType(i) & QSql::Out) {
1653 QByteArray first = tmpStorage.takeFirst();
1654 QVarLengthArray<SQLTCHAR> array;
1655 array.append((SQLTCHAR *)first.constData(), first.size());
1656 values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR*));
1657 }
1658 break;
1659 }
1660 // fall through
1661 default: {
1662 if (bindValueType(i) & QSql::Out)
1663 values[i] = tmpStorage.takeFirst();
1664 break; }
1665 }
1666 if (indicators[i] == SQL_NULL_DATA)
1667 values[i] = QVariant(values[i].type());
1668 }
1669 return true;
1670}
1671
1672QSqlRecord QODBCResult::record() const
1673{
1674 if (!isActive() || !isSelect())
1675 return QSqlRecord();
1676 return d->rInf;
1677}
1678
1679QVariant QODBCResult::handle() const
1680{
1681 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
1682}
1683
1684bool QODBCResult::nextResult()
1685{
1686 setActive(false);
1687 setAt(QSql::BeforeFirstRow);
1688 d->rInf.clear();
1689 d->fieldCache.clear();
1690 d->fieldCacheIdx = 0;
1691 setSelect(false);
1692
1693 SQLRETURN r = SQLMoreResults(d->hStmt);
1694 if (r != SQL_SUCCESS) {
1695 if (r == SQL_SUCCESS_WITH_INFO) {
1696 int nativeCode = -1;
1697 QString message = qODBCWarn(d, &nativeCode);
1698 qWarning() << "QODBCResult::nextResult():" << message;
1699 } else {
1700 if (r != SQL_NO_DATA)
1701 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1702 "Unable to fetch last"), QSqlError::ConnectionError, d));
1703 return false;
1704 }
1705 }
1706
1707 SQLSMALLINT count;
1708 SQLNumResultCols(d->hStmt, &count);
1709 if (count) {
1710 setSelect(true);
1711 for (int i = 0; i < count; ++i) {
1712 d->rInf.append(qMakeFieldInfo(d, i));
1713 }
1714 d->fieldCache.resize(count);
1715 } else {
1716 setSelect(false);
1717 }
1718 setActive(true);
1719
1720 return true;
1721}
1722
1723void QODBCResult::virtual_hook(int id, void *data)
1724{
1725 switch (id) {
1726 case QSqlResult::DetachFromResultSet:
1727 if (d->hStmt)
1728 SQLCloseCursor(d->hStmt);
1729 break;
1730 case QSqlResult::NextResult:
1731 Q_ASSERT(data);
1732 *static_cast<bool*>(data) = nextResult();
1733 break;
1734 default:
1735 QSqlResult::virtual_hook(id, data);
1736 }
1737}
1738
1739void QODBCResult::setForwardOnly(bool forward)
1740{
1741 d->userForwardOnly = forward;
1742 QSqlResult::setForwardOnly(forward);
1743}
1744
1745////////////////////////////////////////
1746
1747
1748QODBCDriver::QODBCDriver(QObject *parent)
1749 : QSqlDriver(parent)
1750{
1751 init();
1752}
1753
1754QODBCDriver::QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject * parent)
1755 : QSqlDriver(parent)
1756{
1757 init();
1758 d->hEnv = env;
1759 d->hDbc = con;
1760 if (env && con) {
1761 setOpen(true);
1762 setOpenError(false);
1763 }
1764}
1765
1766void QODBCDriver::init()
1767{
1768 d = new QODBCDriverPrivate();
1769}
1770
1771QODBCDriver::~QODBCDriver()
1772{
1773 cleanup();
1774 delete d;
1775}
1776
1777bool QODBCDriver::hasFeature(DriverFeature f) const
1778{
1779 switch (f) {
1780 case Transactions: {
1781 if (!d->hDbc)
1782 return false;
1783 SQLUSMALLINT txn;
1784 SQLSMALLINT t;
1785 int r = SQLGetInfo(d->hDbc,
1786 (SQLUSMALLINT)SQL_TXN_CAPABLE,
1787 &txn,
1788 sizeof(txn),
1789 &t);
1790 if (r != SQL_SUCCESS || txn == SQL_TC_NONE)
1791 return false;
1792 else
1793 return true;
1794 }
1795 case Unicode:
1796 return d->unicode;
1797 case PreparedQueries:
1798 case PositionalPlaceholders:
1799 case FinishQuery:
1800 case LowPrecisionNumbers:
1801 return true;
1802 case QuerySize:
1803 case NamedPlaceholders:
1804 case LastInsertId:
1805 case BatchOperations:
1806 case SimpleLocking:
1807 case EventNotifications:
1808 return false;
1809 case MultipleResultSets:
1810 return d->hasMultiResultSets;
1811 case BLOB: {
1812 if(d->isMySqlServer)
1813 return true;
1814 else
1815 return false;
1816 }
1817 }
1818 return false;
1819}
1820
1821bool QODBCDriver::open(const QString & db,
1822 const QString & user,
1823 const QString & password,
1824 const QString &,
1825 int,
1826 const QString& connOpts)
1827{
1828 if (isOpen())
1829 close();
1830 SQLRETURN r;
1831 r = SQLAllocHandle(SQL_HANDLE_ENV,
1832 SQL_NULL_HANDLE,
1833 &d->hEnv);
1834 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1835 qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate environment"), d);
1836 setOpenError(true);
1837 return false;
1838 }
1839 r = SQLSetEnvAttr(d->hEnv,
1840 SQL_ATTR_ODBC_VERSION,
1841 (SQLPOINTER)qGetODBCVersion(connOpts),
1842 SQL_IS_UINTEGER);
1843 r = SQLAllocHandle(SQL_HANDLE_DBC,
1844 d->hEnv,
1845 &d->hDbc);
1846 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1847 qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate connection"), d);
1848 setOpenError(true);
1849 return false;
1850 }
1851
1852 if (!d->setConnectionOptions(connOpts))
1853 return false;
1854
1855 // Create the connection string
1856 QString connQStr;
1857 // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
1858 if (db.contains(QLatin1String(".dsn"), Qt::CaseInsensitive))
1859 connQStr = QLatin1String("FILEDSN=") + db;
1860 else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive)
1861 || db.contains(QLatin1String("SERVER="), Qt::CaseInsensitive))
1862 connQStr = db;
1863 else
1864 connQStr = QLatin1String("DSN=") + db;
1865
1866 if (!user.isEmpty())
1867 connQStr += QLatin1String(";UID=") + user;
1868 if (!password.isEmpty())
1869 connQStr += QLatin1String(";PWD=") + password;
1870
1871 SQLSMALLINT cb;
1872 QVarLengthArray<SQLTCHAR> connOut(1024);
1873 memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
1874 r = SQLDriverConnect(d->hDbc,
1875 NULL,
1876#ifdef UNICODE
1877 toSQLTCHAR(connQStr).data(),
1878#else
1879 (SQLCHAR*)connQStr.toUtf8().data(),
1880#endif
1881 (SQLSMALLINT)connQStr.length(),
1882 connOut.data(),
1883 1024,
1884 &cb,
1885 /*SQL_DRIVER_NOPROMPT*/0);
1886
1887 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1888 setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
1889 setOpenError(true);
1890 return false;
1891 }
1892
1893 if (!d->checkDriver()) {
1894 setLastError(qMakeError(tr("Unable to connect - Driver doesn't support all "
1895 "functionality required"), QSqlError::ConnectionError, d));
1896 setOpenError(true);
1897 return false;
1898 }
1899
1900 d->checkUnicode();
1901 d->checkSchemaUsage();
1902 d->checkSqlServer();
1903 d->checkHasSQLFetchScroll();
1904 d->checkHasMultiResults();
1905 setOpen(true);
1906 setOpenError(false);
1907 if(d->isMSSqlServer) {
1908 QSqlQuery i(createResult());
1909 i.exec(QLatin1String("SET QUOTED_IDENTIFIER ON"));
1910 }
1911 return true;
1912}
1913
1914void QODBCDriver::close()
1915{
1916 cleanup();
1917 setOpen(false);
1918 setOpenError(false);
1919}
1920
1921void QODBCDriver::cleanup()
1922{
1923 SQLRETURN r;
1924 if (!d)
1925 return;
1926
1927 if(d->hDbc) {
1928 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1929 if (isOpen()) {
1930 r = SQLDisconnect(d->hDbc);
1931 if (r != SQL_SUCCESS)
1932 qSqlWarning(QLatin1String("QODBCDriver::disconnect: Unable to disconnect datasource"), d);
1933 else
1934 d->disconnectCount++;
1935 }
1936
1937 r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
1938 if (r != SQL_SUCCESS)
1939 qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free connection handle"), d);
1940 d->hDbc = 0;
1941 }
1942
1943 if (d->hEnv) {
1944 r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
1945 if (r != SQL_SUCCESS)
1946 qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free environment handle"), d);
1947 d->hEnv = 0;
1948 }
1949}
1950
1951// checks whether the server can return char, varchar and longvarchar
1952// as two byte unicode characters
1953void QODBCDriverPrivate::checkUnicode()
1954{
1955 SQLRETURN r;
1956 SQLUINTEGER fFunc;
1957
1958 unicode = false;
1959 r = SQLGetInfo(hDbc,
1960 SQL_CONVERT_CHAR,
1961 (SQLPOINTER)&fFunc,
1962 sizeof(fFunc),
1963 NULL);
1964 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WCHAR)) {
1965 unicode = true;
1966 return;
1967 }
1968
1969 r = SQLGetInfo(hDbc,
1970 SQL_CONVERT_VARCHAR,
1971 (SQLPOINTER)&fFunc,
1972 sizeof(fFunc),
1973 NULL);
1974 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WVARCHAR)) {
1975 unicode = true;
1976 return;
1977 }
1978
1979 r = SQLGetInfo(hDbc,
1980 SQL_CONVERT_LONGVARCHAR,
1981 (SQLPOINTER)&fFunc,
1982 sizeof(fFunc),
1983 NULL);
1984 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
1985 unicode = true;
1986 return;
1987 }
1988 SQLHANDLE hStmt;
1989 r = SQLAllocHandle(SQL_HANDLE_STMT,
1990 hDbc,
1991 &hStmt);
1992
1993 r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
1994 if(r == SQL_SUCCESS) {
1995 r = SQLFetch(hStmt);
1996 if(r == SQL_SUCCESS) {
1997 QVarLengthArray<SQLWCHAR> buffer(10);
1998 r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL);
1999 if(r == SQL_SUCCESS && fromSQLTCHAR(buffer) == QLatin1String("test")) {
2000 unicode = true;
2001 }
2002 }
2003 }
2004 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2005}
2006
2007bool QODBCDriverPrivate::checkDriver() const
2008{
2009#ifdef ODBC_CHECK_DRIVER
2010 static const SQLUSMALLINT reqFunc[] = {
2011 SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
2012 SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
2013 SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
2014 };
2015
2016 // these functions are optional
2017 static const SQLUSMALLINT optFunc[] = {
2018 SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
2019 };
2020
2021 SQLRETURN r;
2022 SQLUSMALLINT sup;
2023
2024 int i;
2025 // check the required functions
2026 for (i = 0; reqFunc[i] != 0; ++i) {
2027
2028 r = SQLGetFunctions(hDbc, reqFunc[i], &sup);
2029
2030 if (r != SQL_SUCCESS) {
2031 qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
2032 return false;
2033 }
2034 if (sup == SQL_FALSE) {
2035 qWarning () << "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (" << reqFunc[i] <<
2036 ").\nPlease look at the Qt SQL Module Driver documentation for more information.";
2037 return false;
2038 }
2039 }
2040
2041 // these functions are optional and just generate a warning
2042 for (i = 0; optFunc[i] != 0; ++i) {
2043
2044 r = SQLGetFunctions(hDbc, optFunc[i], &sup);
2045
2046 if (r != SQL_SUCCESS) {
2047 qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
2048 return false;
2049 }
2050 if (sup == SQL_FALSE) {
2051 qWarning() << "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (" << optFunc[i] << ')';
2052 return true;
2053 }
2054 }
2055#endif //ODBC_CHECK_DRIVER
2056
2057 return true;
2058}
2059
2060void QODBCDriverPrivate::checkSchemaUsage()
2061{
2062 SQLRETURN r;
2063 SQLUINTEGER val;
2064
2065 r = SQLGetInfo(hDbc,
2066 SQL_SCHEMA_USAGE,
2067 (SQLPOINTER) &val,
2068 sizeof(val),
2069 NULL);
2070 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
2071 useSchema = (val != 0);
2072}
2073
2074void QODBCDriverPrivate::checkSqlServer()
2075{
2076 SQLRETURN r;
2077 QVarLengthArray<SQLTCHAR> serverString(200);
2078 SQLSMALLINT t;
2079 memset(serverString.data(), 0, serverString.size() * sizeof(SQLTCHAR));
2080
2081 r = SQLGetInfo(hDbc,
2082 SQL_DBMS_NAME,
2083 serverString.data(),
2084 serverString.size() * sizeof(SQLTCHAR),
2085 &t);
2086 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
2087 QString serverType;
2088#ifdef UNICODE
2089 serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
2090#else
2091 serverType = QString::fromUtf8((const char *)serverString.constData(), t);
2092#endif
2093 isMySqlServer = serverType.contains(QLatin1String("mysql"), Qt::CaseInsensitive);
2094 isMSSqlServer = serverType.contains(QLatin1String("Microsoft SQL Server"), Qt::CaseInsensitive);
2095 }
2096 r = SQLGetInfo(hDbc,
2097 SQL_DRIVER_NAME,
2098 serverString.data(),
2099 serverString.size() * sizeof(SQLTCHAR),
2100 &t);
2101 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
2102 QString serverType;
2103#ifdef UNICODE
2104 serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
2105#else
2106 serverType = QString::fromUtf8((const char *)serverString.constData(), t);
2107#endif
2108 isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive);
2109 unicode = unicode && !isFreeTDSDriver;
2110 }
2111}
2112
2113void QODBCDriverPrivate::checkHasSQLFetchScroll()
2114{
2115 SQLUSMALLINT sup;
2116 SQLRETURN r = SQLGetFunctions(hDbc, SQL_API_SQLFETCHSCROLL, &sup);
2117 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || sup != SQL_TRUE) {
2118 hasSQLFetchScroll = false;
2119 qWarning() << "QODBCDriver::checkHasSQLFetchScroll: Warning - Driver doesn't support scrollable result sets, use forward only mode for queries";
2120 }
2121}
2122
2123void QODBCDriverPrivate::checkHasMultiResults()
2124{
2125 QVarLengthArray<SQLTCHAR> driverResponse(2);
2126 SQLSMALLINT length;
2127 SQLRETURN r = SQLGetInfo(hDbc,
2128 SQL_MULT_RESULT_SETS,
2129 driverResponse.data(),
2130 driverResponse.size() * sizeof(SQLTCHAR),
2131 &length);
2132 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
2133#ifdef UNICODE
2134 hasMultiResultSets = fromSQLTCHAR(driverResponse, length/sizeof(SQLTCHAR)).startsWith(QLatin1Char('Y'));
2135#else
2136 hasMultiResultSets = QString::fromUtf8((const char *)driverResponse.constData(), length).startsWith(QLatin1Char('Y'));
2137#endif
2138}
2139
2140QSqlResult *QODBCDriver::createResult() const
2141{
2142 return new QODBCResult(this, d);
2143}
2144
2145bool QODBCDriver::beginTransaction()
2146{
2147 if (!isOpen()) {
2148 qWarning() << "QODBCDriver::beginTransaction: Database not open";
2149 return false;
2150 }
2151 SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
2152 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
2153 SQL_ATTR_AUTOCOMMIT,
2154 (SQLPOINTER)ac,
2155 sizeof(ac));
2156 if (r != SQL_SUCCESS) {
2157 setLastError(qMakeError(tr("Unable to disable autocommit"),
2158 QSqlError::TransactionError, d));
2159 return false;
2160 }
2161 return true;
2162}
2163
2164bool QODBCDriver::commitTransaction()
2165{
2166 if (!isOpen()) {
2167 qWarning() << "QODBCDriver::commitTransaction: Database not open";
2168 return false;
2169 }
2170 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2171 d->hDbc,
2172 SQL_COMMIT);
2173 if (r != SQL_SUCCESS) {
2174 setLastError(qMakeError(tr("Unable to commit transaction"),
2175 QSqlError::TransactionError, d));
2176 return false;
2177 }
2178 return endTrans();
2179}
2180
2181bool QODBCDriver::rollbackTransaction()
2182{
2183 if (!isOpen()) {
2184 qWarning() << "QODBCDriver::rollbackTransaction: Database not open";
2185 return false;
2186 }
2187 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2188 d->hDbc,
2189 SQL_ROLLBACK);
2190 if (r != SQL_SUCCESS) {
2191 setLastError(qMakeError(tr("Unable to rollback transaction"),
2192 QSqlError::TransactionError, d));
2193 return false;
2194 }
2195 return endTrans();
2196}
2197
2198bool QODBCDriver::endTrans()
2199{
2200 SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
2201 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
2202 SQL_ATTR_AUTOCOMMIT,
2203 (SQLPOINTER)ac,
2204 sizeof(ac));
2205 if (r != SQL_SUCCESS) {
2206 setLastError(qMakeError(tr("Unable to enable autocommit"), QSqlError::TransactionError, d));
2207 return false;
2208 }
2209 return true;
2210}
2211
2212QStringList QODBCDriver::tables(QSql::TableType type) const
2213{
2214 QStringList tl;
2215 if (!isOpen())
2216 return tl;
2217 SQLHANDLE hStmt;
2218
2219 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2220 d->hDbc,
2221 &hStmt);
2222 if (r != SQL_SUCCESS) {
2223 qSqlWarning(QLatin1String("QODBCDriver::tables: Unable to allocate handle"), d);
2224 return tl;
2225 }
2226 r = SQLSetStmtAttr(hStmt,
2227 SQL_ATTR_CURSOR_TYPE,
2228 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2229 SQL_IS_UINTEGER);
2230 QStringList tableType;
2231 if (type & QSql::Tables)
2232 tableType += QLatin1String("TABLE");
2233 if (type & QSql::Views)
2234 tableType += QLatin1String("VIEW");
2235 if (type & QSql::SystemTables)
2236 tableType += QLatin1String("SYSTEM TABLE");
2237 if (tableType.isEmpty())
2238 return tl;
2239
2240 QString joinedTableTypeString = tableType.join(QLatin1String(","));
2241
2242 r = SQLTables(hStmt,
2243 NULL,
2244 0,
2245 NULL,
2246 0,
2247 NULL,
2248 0,
2249#ifdef UNICODE
2250 toSQLTCHAR(joinedTableTypeString).data(),
2251#else
2252 (SQLCHAR*)joinedTableTypeString.toUtf8().data(),
2253#endif
2254 joinedTableTypeString.length() /* characters, not bytes */);
2255
2256 if (r != SQL_SUCCESS)
2257 qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d);
2258
2259 if (d->hasSQLFetchScroll)
2260 r = SQLFetchScroll(hStmt,
2261 SQL_FETCH_NEXT,
2262 0);
2263 else
2264 r = SQLFetch(hStmt);
2265
2266 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
2267 qWarning() << "QODBCDriver::tables failed to retrieve table/view list: (" << r << "," << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ")";
2268 return QStringList();
2269 }
2270
2271 while (r == SQL_SUCCESS) {
2272 QString fieldVal = qGetStringData(hStmt, 2, -1, false);
2273 tl.append(fieldVal);
2274
2275 if (d->hasSQLFetchScroll)
2276 r = SQLFetchScroll(hStmt,
2277 SQL_FETCH_NEXT,
2278 0);
2279 else
2280 r = SQLFetch(hStmt);
2281 }
2282
2283 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2284 if (r!= SQL_SUCCESS)
2285 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
2286 return tl;
2287}
2288
2289QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
2290{
2291 QSqlIndex index(tablename);
2292 if (!isOpen())
2293 return index;
2294 bool usingSpecialColumns = false;
2295 QSqlRecord rec = record(tablename);
2296
2297 SQLHANDLE hStmt;
2298 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2299 d->hDbc,
2300 &hStmt);
2301 if (r != SQL_SUCCESS) {
2302 qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to list primary key"), d);
2303 return index;
2304 }
2305 QString catalog, schema, table;
2306 d->splitTableQualifier(tablename, catalog, schema, table);
2307
2308 if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
2309 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
2310 else
2311 catalog = d->adjustCase(catalog);
2312
2313 if (isIdentifierEscaped(schema, QSqlDriver::TableName))
2314 schema = stripDelimiters(schema, QSqlDriver::TableName);
2315 else
2316 schema = d->adjustCase(schema);
2317
2318 if (isIdentifierEscaped(table, QSqlDriver::TableName))
2319 table = stripDelimiters(table, QSqlDriver::TableName);
2320 else
2321 table = d->adjustCase(table);
2322
2323 r = SQLSetStmtAttr(hStmt,
2324 SQL_ATTR_CURSOR_TYPE,
2325 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2326 SQL_IS_UINTEGER);
2327 r = SQLPrimaryKeys(hStmt,
2328#ifdef UNICODE
2329 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2330#else
2331 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2332#endif
2333 catalog.length(),
2334#ifdef UNICODE
2335 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2336#else
2337 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2338#endif
2339 schema.length(),
2340#ifdef UNICODE
2341 toSQLTCHAR(table).data(),
2342#else
2343 (SQLCHAR*)table.toUtf8().data(),
2344#endif
2345 table.length() /* in characters, not in bytes */);
2346
2347 // if the SQLPrimaryKeys() call does not succeed (e.g the driver
2348 // does not support it) - try an alternative method to get hold of
2349 // the primary index (e.g MS Access and FoxPro)
2350 if (r != SQL_SUCCESS) {
2351 r = SQLSpecialColumns(hStmt,
2352 SQL_BEST_ROWID,
2353#ifdef UNICODE
2354 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2355#else
2356 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2357#endif
2358 catalog.length(),
2359#ifdef UNICODE
2360 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2361#else
2362 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2363#endif
2364 schema.length(),
2365#ifdef UNICODE
2366 toSQLTCHAR(table).data(),
2367#else
2368 (SQLCHAR*)table.toUtf8().data(),
2369#endif
2370 table.length(),
2371 SQL_SCOPE_CURROW,
2372 SQL_NULLABLE);
2373
2374 if (r != SQL_SUCCESS) {
2375 qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d);
2376 } else {
2377 usingSpecialColumns = true;
2378 }
2379 }
2380
2381 if (d->hasSQLFetchScroll)
2382 r = SQLFetchScroll(hStmt,
2383 SQL_FETCH_NEXT,
2384 0);
2385 else
2386 r = SQLFetch(hStmt);
2387
2388 int fakeId = 0;
2389 QString cName, idxName;
2390 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2391 while (r == SQL_SUCCESS) {
2392 if (usingSpecialColumns) {
2393 cName = qGetStringData(hStmt, 1, -1, d->unicode); // column name
2394 idxName = QString::number(fakeId++); // invent a fake index name
2395 } else {
2396 cName = qGetStringData(hStmt, 3, -1, d->unicode); // column name
2397 idxName = qGetStringData(hStmt, 5, -1, d->unicode); // pk index name
2398 }
2399 index.append(rec.field(cName));
2400 index.setName(idxName);
2401
2402 if (d->hasSQLFetchScroll)
2403 r = SQLFetchScroll(hStmt,
2404 SQL_FETCH_NEXT,
2405 0);
2406 else
2407 r = SQLFetch(hStmt);
2408
2409 }
2410 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2411 if (r!= SQL_SUCCESS)
2412 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
2413 return index;
2414}
2415
2416QSqlRecord QODBCDriver::record(const QString& tablename) const
2417{
2418 QSqlRecord fil;
2419 if (!isOpen())
2420 return fil;
2421
2422 SQLHANDLE hStmt;
2423 QString catalog, schema, table;
2424 d->splitTableQualifier(tablename, catalog, schema, table);
2425
2426 if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
2427 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
2428 else
2429 catalog = d->adjustCase(catalog);
2430
2431 if (isIdentifierEscaped(schema, QSqlDriver::TableName))
2432 schema = stripDelimiters(schema, QSqlDriver::TableName);
2433 else
2434 schema = d->adjustCase(schema);
2435
2436 if (isIdentifierEscaped(table, QSqlDriver::TableName))
2437 table = stripDelimiters(table, QSqlDriver::TableName);
2438 else
2439 table = d->adjustCase(table);
2440
2441 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2442 d->hDbc,
2443 &hStmt);
2444 if (r != SQL_SUCCESS) {
2445 qSqlWarning(QLatin1String("QODBCDriver::record: Unable to allocate handle"), d);
2446 return fil;
2447 }
2448 r = SQLSetStmtAttr(hStmt,
2449 SQL_ATTR_CURSOR_TYPE,
2450 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2451 SQL_IS_UINTEGER);
2452 r = SQLColumns(hStmt,
2453#ifdef UNICODE
2454 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2455#else
2456 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2457#endif
2458 catalog.length(),
2459#ifdef UNICODE
2460 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2461#else
2462 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2463#endif
2464 schema.length(),
2465#ifdef UNICODE
2466 toSQLTCHAR(table).data(),
2467#else
2468 (SQLCHAR*)table.toUtf8().data(),
2469#endif
2470 table.length(),
2471 NULL,
2472 0);
2473 if (r != SQL_SUCCESS)
2474 qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d);
2475
2476 if (d->hasSQLFetchScroll)
2477 r = SQLFetchScroll(hStmt,
2478 SQL_FETCH_NEXT,
2479 0);
2480 else
2481 r = SQLFetch(hStmt);
2482
2483 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2484 while (r == SQL_SUCCESS) {
2485
2486 fil.append(qMakeFieldInfo(hStmt, d));
2487
2488 if (d->hasSQLFetchScroll)
2489 r = SQLFetchScroll(hStmt,
2490 SQL_FETCH_NEXT,
2491 0);
2492 else
2493 r = SQLFetch(hStmt);
2494 }
2495
2496 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2497 if (r!= SQL_SUCCESS)
2498 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") + QString::number(r), d);
2499
2500 return fil;
2501}
2502
2503QString QODBCDriver::formatValue(const QSqlField &field,
2504 bool trimStrings) const
2505{
2506 QString r;
2507 if (field.isNull()) {
2508 r = QLatin1String("NULL");
2509 } else if (field.type() == QVariant::DateTime) {
2510 // Use an escape sequence for the datetime fields
2511 if (field.value().toDateTime().isValid()){
2512 QDate dt = field.value().toDateTime().date();
2513 QTime tm = field.value().toDateTime().time();
2514 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
2515 r = QLatin1String("{ ts '") +
2516 QString::number(dt.year()) + QLatin1Char('-') +
2517 QString::number(dt.month()).rightJustified(2, QLatin1Char('0'), true) +
2518 QLatin1Char('-') +
2519 QString::number(dt.day()).rightJustified(2, QLatin1Char('0'), true) +
2520 QLatin1Char(' ') +
2521 tm.toString() +
2522 QLatin1String("' }");
2523 } else
2524 r = QLatin1String("NULL");
2525 } else if (field.type() == QVariant::ByteArray) {
2526 QByteArray ba = field.value().toByteArray();
2527 QString res;
2528 static const char hexchars[] = "0123456789abcdef";
2529 for (int i = 0; i < ba.size(); ++i) {
2530 uchar s = (uchar) ba[i];
2531 res += QLatin1Char(hexchars[s >> 4]);
2532 res += QLatin1Char(hexchars[s & 0x0f]);
2533 }
2534 r = QLatin1String("0x") + res;
2535 } else {
2536 r = QSqlDriver::formatValue(field, trimStrings);
2537 }
2538 return r;
2539}
2540
2541QVariant QODBCDriver::handle() const
2542{
2543 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
2544}
2545
2546QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
2547{
2548 QChar quote = d->quoteChar();
2549 QString res = identifier;
2550 if(!identifier.isEmpty() && !identifier.startsWith(quote) && !identifier.endsWith(quote) ) {
2551 res.replace(quote, QString(quote)+QString(quote));
2552 res.prepend(quote).append(quote);
2553 res.replace(QLatin1Char('.'), QString(quote)+QLatin1Char('.')+QString(quote));
2554 }
2555 return res;
2556}
2557
2558bool QODBCDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType) const
2559{
2560 QChar quote = d->quoteChar();
2561 return identifier.size() > 2
2562 && identifier.startsWith(quote) //left delimited
2563 && identifier.endsWith(quote); //right delimited
2564}
2565
2566QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.