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

Last change on this file since 643 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

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