1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtSql module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsql_odbc.h"
43 #include <qsqlrecord.h>
45 #if defined (Q_OS_WIN32)
46 #include <qt_windows.h>
48 #include <qcoreapplication.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>
62 // undefine this to prevent initial check of the ODBC driver
63 #define ODBC_CHECK_DRIVER
65 static const int COLNAMESIZE = 256;
66 //Map Qt parameter types to ODBC types
67 static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
69 inline static QString fromSQLTCHAR(const QVarLengthArray<SQLTCHAR>& input, int size=-1)
73 int realsize = qMin(size, input.size());
74 if(realsize > 0 && input[realsize-1] == 0)
76 switch(sizeof(SQLTCHAR)) {
78 result=QString::fromUtf8((const char *)input.constData(), realsize);
81 result=QString::fromUtf16((const ushort *)input.constData(), realsize);
84 result=QString::fromUcs4((const uint *)input.constData(), realsize);
87 qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", sizeof(SQLTCHAR));
92 inline static QVarLengthArray<SQLTCHAR> toSQLTCHAR(const QString &input)
94 QVarLengthArray<SQLTCHAR> result;
95 result.resize(input.size());
96 switch(sizeof(SQLTCHAR)) {
98 memcpy(result.data(), input.toUtf8().data(), input.size());
101 memcpy(result.data(), input.unicode(), input.size() * 2);
104 memcpy(result.data(), input.toUcs4().data(), input.size() * 4);
107 qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", sizeof(SQLTCHAR));
109 result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't.
113 class QODBCDriverPrivate
116 enum DefaultCase{Lower, Mixed, Upper, Sensitive};
118 : hEnv(0), hDbc(0), unicode(false), useSchema(false), disconnectCount(0), isMySqlServer(false),
119 isMSSqlServer(false), isFreeTDSDriver(false), hasSQLFetchScroll(true),
120 hasMultiResultSets(false), isQuoteInitialized(false), quote(QLatin1Char('"'))
132 bool isFreeTDSDriver;
133 bool hasSQLFetchScroll;
134 bool hasMultiResultSets;
136 bool checkDriver() const;
138 void checkSqlServer();
139 void checkHasSQLFetchScroll();
140 void checkHasMultiResults();
141 void checkSchemaUsage();
142 bool setConnectionOptions(const QString& connOpts);
143 void splitTableQualifier(const QString &qualifier, QString &catalog,
144 QString &schema, QString &table);
145 DefaultCase defaultCase() const;
146 QString adjustCase(const QString&) const;
149 bool isQuoteInitialized;
156 QODBCPrivate(QODBCDriverPrivate *dpp)
157 : hStmt(0), useSchema(false), hasSQLFetchScroll(true), driverPrivate(dpp), userForwardOnly(false)
159 unicode = dpp->unicode;
160 useSchema = dpp->useSchema;
161 disconnectCount = dpp->disconnectCount;
162 hasSQLFetchScroll = dpp->hasSQLFetchScroll;
165 inline void clearValues()
166 { fieldCache.fill(QVariant()); fieldCacheIdx = 0; }
168 SQLHANDLE dpEnv() const { return driverPrivate ? driverPrivate->hEnv : 0;}
169 SQLHANDLE dpDbc() const { return driverPrivate ? driverPrivate->hDbc : 0;}
176 QVector<QVariant> fieldCache;
179 bool hasSQLFetchScroll;
180 QODBCDriverPrivate *driverPrivate;
181 bool userForwardOnly;
183 bool isStmtHandleValid(const QSqlDriver *driver);
184 void updateStmtHandleState(const QSqlDriver *driver);
187 bool QODBCPrivate::isStmtHandleValid(const QSqlDriver *driver)
189 const QODBCDriver *odbcdriver = static_cast<const QODBCDriver*> (driver);
190 return disconnectCount == odbcdriver->d->disconnectCount;
193 void QODBCPrivate::updateStmtHandleState(const QSqlDriver *driver)
195 const QODBCDriver *odbcdriver = static_cast<const QODBCDriver*> (driver);
196 disconnectCount = odbcdriver->d->disconnectCount;
199 static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0)
201 SQLINTEGER nativeCode_ = 0;
202 SQLSMALLINT msgLen = 0;
203 SQLRETURN r = SQL_NO_DATA;
204 SQLTCHAR state_[SQL_SQLSTATE_SIZE+1];
205 QVarLengthArray<SQLTCHAR> description_(SQL_MAX_MESSAGE_LENGTH);
211 r = SQLGetDiagRec(handleType,
219 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && msgLen > 0)
220 description_.resize(msgLen+1);
221 r = SQLGetDiagRec(handleType,
229 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
231 *nativeCode = nativeCode_;
234 tmpstore = fromSQLTCHAR(description_, msgLen);
236 tmpstore = QString::fromUtf8((const char*)description_.constData(), msgLen);
238 if(result != tmpstore) {
239 if(!result.isEmpty())
240 result += QLatin1Char(' ');
243 } else if (r == SQL_ERROR || r == SQL_INVALID_HANDLE) {
247 } while (r != SQL_NO_DATA);
251 static QString qODBCWarn(const QODBCPrivate* odbc, int *nativeCode = 0)
253 return QString(qWarnODBCHandle(SQL_HANDLE_ENV, odbc->dpEnv()) + QLatin1Char(' ')
254 + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->dpDbc()) + QLatin1Char(' ')
255 + qWarnODBCHandle(SQL_HANDLE_STMT, odbc->hStmt, nativeCode)).simplified();
258 static QString qODBCWarn(const QODBCDriverPrivate* odbc, int *nativeCode = 0)
260 return QString(qWarnODBCHandle(SQL_HANDLE_ENV, odbc->hEnv) + QLatin1Char(' ')
261 + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->hDbc, nativeCode)).simplified();
264 static void qSqlWarning(const QString& message, const QODBCPrivate* odbc)
266 qWarning() << message << "\tError:" << qODBCWarn(odbc);
269 static void qSqlWarning(const QString &message, const QODBCDriverPrivate *odbc)
271 qWarning() << message << "\tError:" << qODBCWarn(odbc);
274 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, const QODBCPrivate* p)
277 QString message = qODBCWarn(p, &nativeCode);
278 return QSqlError(QLatin1String("QODBC3: ") + err, message, type, nativeCode);
281 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
282 const QODBCDriverPrivate* p)
285 QString message = qODBCWarn(p, &nativeCode);
286 return QSqlError(QLatin1String("QODBC3: ") + err, qODBCWarn(p), type, nativeCode);
290 static QVariant::Type qDecodeODBCType(SQLSMALLINT sqltype, const T* p, bool isSigned = true)
293 QVariant::Type type = QVariant::Invalid;
300 type = QVariant::Double;
305 type = isSigned ? QVariant::Int : QVariant::UInt;
308 type = QVariant::UInt;
311 type = isSigned ? QVariant::LongLong : QVariant::ULongLong;
315 case SQL_LONGVARBINARY:
316 type = QVariant::ByteArray;
320 type = QVariant::Date;
324 type = QVariant::Time;
327 case SQL_TYPE_TIMESTAMP:
328 type = QVariant::DateTime;
332 case SQL_WLONGVARCHAR:
333 type = QVariant::String;
337 #if (ODBCVER >= 0x0350)
340 case SQL_LONGVARCHAR:
341 type = QVariant::String;
344 type = QVariant::ByteArray;
350 static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool unicode = false)
353 SQLRETURN r = SQL_ERROR;
354 SQLLEN lengthIndicator = 0;
356 // NB! colSize must be a multiple of 2 for unicode enabled DBs
359 } else if (colSize > 65536) { // limit buffer size to 64 KB
362 colSize++; // make sure there is room for more than the 0 termination
365 r = SQLGetData(hStmt,
371 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
372 colSize = lengthIndicator/sizeof(SQLTCHAR) + 1;
373 QVarLengthArray<SQLTCHAR> buf(colSize);
374 memset(buf.data(), 0, colSize*sizeof(SQLTCHAR));
376 r = SQLGetData(hStmt,
379 (SQLPOINTER)buf.data(),
380 colSize*sizeof(SQLTCHAR),
382 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
383 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
387 // if SQL_SUCCESS_WITH_INFO is returned, indicating that
388 // more data can be fetched, the length indicator does NOT
389 // contain the number of bytes returned - it contains the
390 // total number of bytes that CAN be fetched
391 // colSize-1: remove 0 termination when there is more data to fetch
392 int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator/sizeof(SQLTCHAR);
393 fieldVal += fromSQLTCHAR(buf, rSize);
394 if (lengthIndicator < SQLLEN(colSize*sizeof(SQLTCHAR))) {
395 // workaround for Drivermanagers that don't return SQL_NO_DATA
398 } else if (r == SQL_NO_DATA) {
401 qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
407 r = SQLGetData(hStmt,
413 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
414 colSize = lengthIndicator + 1;
415 QVarLengthArray<SQLCHAR> buf(colSize);
417 r = SQLGetData(hStmt,
420 (SQLPOINTER)buf.data(),
423 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
424 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
428 // if SQL_SUCCESS_WITH_INFO is returned, indicating that
429 // more data can be fetched, the length indicator does NOT
430 // contain the number of bytes returned - it contains the
431 // total number of bytes that CAN be fetched
432 // colSize-1: remove 0 termination when there is more data to fetch
433 int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator;
434 fieldVal += QString::fromUtf8((const char *)buf.constData(), rSize);
435 if (lengthIndicator < SQLLEN(colSize)) {
436 // workaround for Drivermanagers that don't return SQL_NO_DATA
439 } else if (r == SQL_NO_DATA) {
442 qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
451 static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
454 SQLSMALLINT colNameLen;
457 SQLSMALLINT colScale;
458 SQLSMALLINT nullable;
459 SQLLEN lengthIndicator = 0;
460 SQLRETURN r = SQL_ERROR;
462 QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
464 r = SQLDescribeCol(hStmt,
473 if (r != SQL_SUCCESS)
474 qWarning() << "qGetBinaryData: Unable to describe column" << column;
475 // SQLDescribeCol may return 0 if size cannot be determined
478 else if (colSize > 65536) // read the field in 64 KB chunks
480 fieldVal.resize(colSize);
483 r = SQLGetData(hStmt,
486 (SQLPOINTER)(fieldVal.constData() + read),
489 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
491 if (lengthIndicator == SQL_NULL_DATA)
492 return QVariant(QVariant::ByteArray);
493 if (lengthIndicator > SQLLEN(colSize) || lengthIndicator == SQL_NO_TOTAL) {
497 read += lengthIndicator;
499 if (r == SQL_SUCCESS) { // the whole field was read in one chunk
500 fieldVal.resize(read);
503 fieldVal.resize(fieldVal.size() + colSize);
508 static QVariant qGetIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
510 SQLINTEGER intbuf = 0;
511 SQLLEN lengthIndicator = 0;
512 SQLRETURN r = SQLGetData(hStmt,
514 isSigned ? SQL_C_SLONG : SQL_C_ULONG,
518 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
519 return QVariant(QVariant::Invalid);
520 if (lengthIndicator == SQL_NULL_DATA)
521 return QVariant(QVariant::Int);
528 static QVariant qGetDoubleData(SQLHANDLE hStmt, int column)
531 SQLLEN lengthIndicator = 0;
532 SQLRETURN r = SQLGetData(hStmt,
535 (SQLPOINTER) &dblbuf,
538 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
539 return QVariant(QVariant::Invalid);
541 if(lengthIndicator == SQL_NULL_DATA)
542 return QVariant(QVariant::Double);
544 return (double) dblbuf;
548 static QVariant qGetBigIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
550 SQLBIGINT lngbuf = 0;
551 SQLLEN lengthIndicator = 0;
552 SQLRETURN r = SQLGetData(hStmt,
554 isSigned ? SQL_C_SBIGINT : SQL_C_UBIGINT,
555 (SQLPOINTER) &lngbuf,
558 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
559 return QVariant(QVariant::Invalid);
560 if (lengthIndicator == SQL_NULL_DATA)
561 return QVariant(QVariant::LongLong);
564 return qint64(lngbuf);
566 return quint64(lngbuf);
569 // creates a QSqlField from a valid hStmt generated
570 // by SQLColumns. The hStmt has to point to a valid position.
571 static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, const QODBCDriverPrivate* p)
573 QString fname = qGetStringData(hStmt, 3, -1, p->unicode);
574 int type = qGetIntData(hStmt, 4).toInt(); // column type
575 QSqlField f(fname, qDecodeODBCType(type, p));
576 QVariant var = qGetIntData(hStmt, 6);
577 f.setLength(var.isNull() ? -1 : var.toInt()); // column size
578 var = qGetIntData(hStmt, 8).toInt();
579 f.setPrecision(var.isNull() ? -1 : var.toInt()); // precision
581 int required = qGetIntData(hStmt, 10).toInt(); // nullable-flag
582 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
583 if (required == SQL_NO_NULLS)
585 else if (required == SQL_NULLABLE)
586 f.setRequired(false);
587 // else we don't know
591 static QSqlField qMakeFieldInfo(const QODBCPrivate* p, int i )
593 SQLSMALLINT colNameLen;
596 SQLSMALLINT colScale;
597 SQLSMALLINT nullable;
598 SQLRETURN r = SQL_ERROR;
599 QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
600 r = SQLDescribeCol(p->hStmt,
603 (SQLSMALLINT)COLNAMESIZE,
610 if (r != SQL_SUCCESS) {
611 qSqlWarning(QString::fromLatin1("qMakeField: Unable to describe column %1").arg(i), p);
615 SQLLEN unsignedFlag = SQL_FALSE;
616 r = SQLColAttribute (p->hStmt,
623 if (r != SQL_SUCCESS) {
624 qSqlWarning(QString::fromLatin1("qMakeField: Unable to get column attributes for column %1").arg(i), p);
628 QString qColName(fromSQLTCHAR(colName, colNameLen));
630 QString qColName = QString::fromUtf8((const char *)colName.constData());
632 // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
633 QVariant::Type type = qDecodeODBCType(colType, p, unsignedFlag == SQL_FALSE);
634 QSqlField f(qColName, type);
635 f.setSqlType(colType);
636 f.setLength(colSize == 0 ? -1 : int(colSize));
637 f.setPrecision(colScale == 0 ? -1 : int(colScale));
638 if (nullable == SQL_NO_NULLS)
640 else if (nullable == SQL_NULLABLE)
641 f.setRequired(false);
642 // else we don't know
646 static int qGetODBCVersion(const QString &connOpts)
648 if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"), Qt::CaseInsensitive))
653 QChar QODBCDriverPrivate::quoteChar()
655 if (!isQuoteInitialized) {
656 SQLTCHAR driverResponse[4];
658 int r = SQLGetInfo(hDbc,
659 SQL_IDENTIFIER_QUOTE_CHAR,
661 sizeof(driverResponse),
663 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
665 quote = QChar(driverResponse[0]);
667 quote = QLatin1Char(driverResponse[0]);
670 quote = QLatin1Char('"');
671 isQuoteInitialized = true;
677 bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
679 // Set any connection attributes
680 const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
681 SQLRETURN r = SQL_SUCCESS;
682 for (int i = 0; i < opts.count(); ++i) {
683 const QString tmp(opts.at(i));
685 if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
686 qWarning() << "QODBCDriver::open: Illegal connect option value '" << tmp << '\'';
689 const QString opt(tmp.left(idx));
690 const QString val(tmp.mid(idx + 1).simplified());
694 if (opt.toUpper() == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
695 if (val.toUpper() == QLatin1String("SQL_MODE_READ_ONLY")) {
696 v = SQL_MODE_READ_ONLY;
697 } else if (val.toUpper() == QLatin1String("SQL_MODE_READ_WRITE")) {
698 v = SQL_MODE_READ_WRITE;
700 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
703 r = SQLSetConnectAttr(hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0);
704 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_TIMEOUT")) {
706 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) v, 0);
707 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
709 r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0);
710 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) {
711 val.utf16(); // 0 terminate
712 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
714 toSQLTCHAR(val).data(),
716 (SQLCHAR*) val.toUtf8().data(),
718 val.length()*sizeof(SQLTCHAR));
719 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) {
720 if (val.toUpper() == QLatin1String("SQL_TRUE")) {
722 } else if (val.toUpper() == QLatin1String("SQL_FALSE")) {
725 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
728 r = SQLSetConnectAttr(hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) v, 0);
729 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_PACKET_SIZE")) {
731 r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) v, 0);
732 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) {
733 val.utf16(); // 0 terminate
734 r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
736 toSQLTCHAR(val).data(),
738 (SQLCHAR*) val.toUtf8().data(),
740 val.length()*sizeof(SQLTCHAR));
741 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) {
742 if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) {
743 v = SQL_OPT_TRACE_OFF;
744 } else if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_ON")) {
745 v = SQL_OPT_TRACE_ON;
747 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
750 r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0);
751 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_POOLING")) {
752 if (val == QLatin1String("SQL_CP_OFF"))
754 else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_DRIVER"))
755 v = SQL_CP_ONE_PER_DRIVER;
756 else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_HENV"))
757 v = SQL_CP_ONE_PER_HENV;
758 else if (val.toUpper() == QLatin1String("SQL_CP_DEFAULT"))
761 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
764 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)v, 0);
765 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CP_MATCH")) {
766 if (val.toUpper() == QLatin1String("SQL_CP_STRICT_MATCH"))
767 v = SQL_CP_STRICT_MATCH;
768 else if (val.toUpper() == QLatin1String("SQL_CP_RELAXED_MATCH"))
769 v = SQL_CP_RELAXED_MATCH;
770 else if (val.toUpper() == QLatin1String("SQL_CP_MATCH_DEFAULT"))
771 v = SQL_CP_MATCH_DEFAULT;
773 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
776 r = SQLSetConnectAttr(hDbc, SQL_ATTR_CP_MATCH, (SQLPOINTER)v, 0);
777 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_ODBC_VERSION")) {
778 // Already handled in QODBCDriver::open()
781 qWarning() << "QODBCDriver::open: Unknown connection attribute '" << opt << '\'';
783 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
784 qSqlWarning(QString::fromLatin1("QODBCDriver::open: Unable to set connection attribute'%1'").arg(
790 void QODBCDriverPrivate::splitTableQualifier(const QString & qualifier, QString &catalog,
791 QString &schema, QString &table)
797 QStringList l = qualifier.split(QLatin1Char('.'));
799 return; // can't possibly be a valid table qualifier
800 int i = 0, n = l.count();
804 for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
825 QODBCDriverPrivate::DefaultCase QODBCDriverPrivate::defaultCase() const
829 int r = SQLGetInfo(hDbc,
834 if ( r != SQL_SUCCESS)
835 ret = Mixed;//arbitrary case if driver cannot be queried
844 case (SQL_IC_SENSITIVE):
857 Adjust the casing of an identifier to match what the
858 database engine would have done to it.
860 QString QODBCDriverPrivate::adjustCase(const QString &identifier) const
862 QString ret = identifier;
863 switch(defaultCase()) {
865 ret = identifier.toLower();
868 ret = identifier.toUpper();
878 ////////////////////////////////////////////////////////////////////////////
880 QODBCResult::QODBCResult(const QODBCDriver * db, QODBCDriverPrivate* p)
883 d = new QODBCPrivate(p);
886 QODBCResult::~QODBCResult()
888 if (d->hStmt && d->isStmtHandleValid(driver()) && driver()->isOpen()) {
889 SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
890 if (r != SQL_SUCCESS)
891 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ")
892 + QString::number(r), d);
898 bool QODBCResult::reset (const QString& query)
901 setAt(QSql::BeforeFirstRow);
903 d->fieldCache.clear();
904 d->fieldCacheIdx = 0;
906 // Always reallocate the statement handle - the statement attributes
907 // are not reset if SQLFreeStmt() is called which causes some problems.
909 if (d->hStmt && d->isStmtHandleValid(driver())) {
910 r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
911 if (r != SQL_SUCCESS) {
912 qSqlWarning(QLatin1String("QODBCResult::reset: Unable to free statement handle"), d);
916 r = SQLAllocHandle(SQL_HANDLE_STMT,
919 if (r != SQL_SUCCESS) {
920 qSqlWarning(QLatin1String("QODBCResult::reset: Unable to allocate statement handle"), d);
924 d->updateStmtHandleState(driver());
926 if (d->userForwardOnly) {
927 r = SQLSetStmtAttr(d->hStmt,
928 SQL_ATTR_CURSOR_TYPE,
929 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
932 r = SQLSetStmtAttr(d->hStmt,
933 SQL_ATTR_CURSOR_TYPE,
934 (SQLPOINTER)SQL_CURSOR_STATIC,
937 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
938 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
939 "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
940 "Please check your ODBC driver configuration"), QSqlError::StatementError, d));
945 r = SQLExecDirect(d->hStmt,
946 toSQLTCHAR(query).data(),
947 (SQLINTEGER) query.length());
949 QByteArray query8 = query.toUtf8();
950 r = SQLExecDirect(d->hStmt,
951 (SQLCHAR*) query8.data(),
952 (SQLINTEGER) query8.length());
954 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) {
955 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
956 "Unable to execute statement"), QSqlError::StatementError, d));
960 if(r == SQL_NO_DATA) {
965 SQLINTEGER isScrollable, bufferLength;
966 r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, &bufferLength);
967 if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
968 QSqlResult::setForwardOnly(isScrollable==SQL_NONSCROLLABLE);
971 SQLNumResultCols(d->hStmt, &count);
974 for (int i = 0; i < count; ++i) {
975 d->rInf.append(qMakeFieldInfo(d, i));
977 d->fieldCache.resize(count);
986 bool QODBCResult::fetch(int i)
988 if (!driver()->isOpen())
991 if (isForwardOnly() && i < at())
996 int actualIdx = i + 1;
997 if (actualIdx <= 0) {
998 setAt(QSql::BeforeFirstRow);
1002 if (isForwardOnly()) {
1004 while (ok && i > at())
1008 r = SQLFetchScroll(d->hStmt,
1012 if (r != SQL_SUCCESS) {
1013 if (r != SQL_NO_DATA)
1014 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1015 "Unable to fetch"), QSqlError::ConnectionError, d));
1022 bool QODBCResult::fetchNext()
1027 if (d->hasSQLFetchScroll)
1028 r = SQLFetchScroll(d->hStmt,
1032 r = SQLFetch(d->hStmt);
1034 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1035 if (r != SQL_NO_DATA)
1036 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1037 "Unable to fetch next"), QSqlError::ConnectionError, d));
1044 bool QODBCResult::fetchFirst()
1046 if (isForwardOnly() && at() != QSql::BeforeFirstRow)
1050 if (isForwardOnly()) {
1053 r = SQLFetchScroll(d->hStmt,
1056 if (r != SQL_SUCCESS) {
1057 if (r != SQL_NO_DATA)
1058 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1059 "Unable to fetch first"), QSqlError::ConnectionError, d));
1066 bool QODBCResult::fetchPrevious()
1068 if (isForwardOnly())
1072 r = SQLFetchScroll(d->hStmt,
1075 if (r != SQL_SUCCESS) {
1076 if (r != SQL_NO_DATA)
1077 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1078 "Unable to fetch previous"), QSqlError::ConnectionError, d));
1085 bool QODBCResult::fetchLast()
1090 if (isForwardOnly()) {
1091 // cannot seek to last row in forwardOnly mode, so we have to use brute force
1093 if (i == QSql::AfterLastRow)
1095 if (i == QSql::BeforeFirstRow)
1103 r = SQLFetchScroll(d->hStmt,
1106 if (r != SQL_SUCCESS) {
1107 if (r != SQL_NO_DATA)
1108 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1109 "Unable to fetch last"), QSqlError::ConnectionError, d));
1113 r = SQLGetStmtAttr(d->hStmt,
1118 if (r != SQL_SUCCESS)
1124 QVariant QODBCResult::data(int field)
1126 if (field >= d->rInf.count() || field < 0) {
1127 qWarning() << "QODBCResult::data: column" << field << "out of range";
1130 if (field < d->fieldCacheIdx)
1131 return d->fieldCache.at(field);
1134 SQLLEN lengthIndicator = 0;
1136 for (int i = d->fieldCacheIdx; i <= field; ++i) {
1137 // some servers do not support fetching column n after we already
1138 // fetched column n+1, so cache all previous columns here
1139 const QSqlField info = d->rInf.field(i);
1140 switch (info.type()) {
1141 case QVariant::LongLong:
1142 d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
1144 case QVariant::ULongLong:
1145 d->fieldCache[i] = qGetBigIntData(d->hStmt, i, false);
1148 d->fieldCache[i] = qGetIntData(d->hStmt, i);
1150 case QVariant::UInt:
1151 d->fieldCache[i] = qGetIntData(d->hStmt, i, false);
1153 case QVariant::Date:
1155 r = SQLGetData(d->hStmt,
1161 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
1162 d->fieldCache[i] = QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
1164 d->fieldCache[i] = QVariant(QVariant::Date);
1166 case QVariant::Time:
1168 r = SQLGetData(d->hStmt,
1174 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
1175 d->fieldCache[i] = QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
1177 d->fieldCache[i] = QVariant(QVariant::Time);
1179 case QVariant::DateTime:
1180 TIMESTAMP_STRUCT dtbuf;
1181 r = SQLGetData(d->hStmt,
1187 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
1188 d->fieldCache[i] = QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
1189 QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
1191 d->fieldCache[i] = QVariant(QVariant::DateTime);
1193 case QVariant::ByteArray:
1194 d->fieldCache[i] = qGetBinaryData(d->hStmt, i);
1196 case QVariant::String:
1197 d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), d->unicode);
1199 case QVariant::Double:
1200 switch(numericalPrecisionPolicy()) {
1201 case QSql::LowPrecisionInt32:
1202 d->fieldCache[i] = qGetIntData(d->hStmt, i);
1204 case QSql::LowPrecisionInt64:
1205 d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
1207 case QSql::LowPrecisionDouble:
1208 d->fieldCache[i] = qGetDoubleData(d->hStmt, i);
1210 case QSql::HighPrecision:
1211 d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), false);
1216 d->fieldCache[i] = QVariant(qGetStringData(d->hStmt, i, info.length(), false));
1219 d->fieldCacheIdx = field + 1;
1221 return d->fieldCache[field];
1224 bool QODBCResult::isNull(int field)
1226 if (field < 0 || field > d->fieldCache.size())
1228 if (field <= d->fieldCacheIdx) {
1229 // since there is no good way to find out whether the value is NULL
1230 // without fetching the field we'll fetch it here.
1231 // (data() also sets the NULL flag)
1234 return d->fieldCache.at(field).isNull();
1237 int QODBCResult::size()
1242 int QODBCResult::numRowsAffected()
1244 SQLLEN affectedRowCount = 0;
1245 SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
1246 if (r == SQL_SUCCESS)
1247 return affectedRowCount;
1249 qSqlWarning(QLatin1String("QODBCResult::numRowsAffected: Unable to count affected rows"), d);
1253 bool QODBCResult::prepare(const QString& query)
1256 setAt(QSql::BeforeFirstRow);
1260 if (d->hStmt && d->isStmtHandleValid(driver())) {
1261 r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
1262 if (r != SQL_SUCCESS) {
1263 qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to close statement"), d);
1267 r = SQLAllocHandle(SQL_HANDLE_STMT,
1270 if (r != SQL_SUCCESS) {
1271 qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to allocate statement handle"), d);
1275 d->updateStmtHandleState(driver());
1277 if (d->userForwardOnly) {
1278 r = SQLSetStmtAttr(d->hStmt,
1279 SQL_ATTR_CURSOR_TYPE,
1280 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1283 r = SQLSetStmtAttr(d->hStmt,
1284 SQL_ATTR_CURSOR_TYPE,
1285 (SQLPOINTER)SQL_CURSOR_STATIC,
1288 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1289 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1290 "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
1291 "Please check your ODBC driver configuration"), QSqlError::StatementError, d));
1296 r = SQLPrepare(d->hStmt,
1297 toSQLTCHAR(query).data(),
1298 (SQLINTEGER) query.length());
1300 QByteArray query8 = query.toUtf8();
1301 r = SQLPrepare(d->hStmt,
1302 (SQLCHAR*) query8.data(),
1303 (SQLINTEGER) query8.length());
1306 if (r != SQL_SUCCESS) {
1307 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1308 "Unable to prepare statement"), QSqlError::StatementError, d));
1314 bool QODBCResult::exec()
1317 setAt(QSql::BeforeFirstRow);
1319 d->fieldCache.clear();
1320 d->fieldCacheIdx = 0;
1323 qSqlWarning(QLatin1String("QODBCResult::exec: No statement handle available"), d);
1328 SQLCloseCursor(d->hStmt);
1330 QList<QByteArray> tmpStorage; // holds temporary buffers
1331 QVarLengthArray<SQLLEN, 32> indicators(boundValues().count());
1332 memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN));
1334 // bind parameters - only positional binding allowed
1335 QVector<QVariant>& values = boundValues();
1338 for (i = 0; i < values.count(); ++i) {
1339 if (bindValueType(i) & QSql::Out)
1341 const QVariant &val = values.at(i);
1342 SQLLEN *ind = &indicators[i];
1344 *ind = SQL_NULL_DATA;
1345 switch (val.type()) {
1346 case QVariant::Date: {
1348 ba.resize(sizeof(DATE_STRUCT));
1349 DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData();
1350 QDate qdt = val.toDate();
1351 dt->year = qdt.year();
1352 dt->month = qdt.month();
1353 dt->day = qdt.day();
1354 r = SQLBindParameter(d->hStmt,
1356 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1363 *ind == SQL_NULL_DATA ? ind : NULL);
1364 tmpStorage.append(ba);
1366 case QVariant::Time: {
1368 ba.resize(sizeof(TIME_STRUCT));
1369 TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData();
1370 QTime qdt = val.toTime();
1371 dt->hour = qdt.hour();
1372 dt->minute = qdt.minute();
1373 dt->second = qdt.second();
1374 r = SQLBindParameter(d->hStmt,
1376 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1383 *ind == SQL_NULL_DATA ? ind : NULL);
1384 tmpStorage.append(ba);
1386 case QVariant::DateTime: {
1388 ba.resize(sizeof(TIMESTAMP_STRUCT));
1389 TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData();
1390 QDateTime qdt = val.toDateTime();
1391 dt->year = qdt.date().year();
1392 dt->month = qdt.date().month();
1393 dt->day = qdt.date().day();
1394 dt->hour = qdt.time().hour();
1395 dt->minute = qdt.time().minute();
1396 dt->second = qdt.time().second();
1397 dt->fraction = qdt.time().msec() * 1000000;
1398 r = SQLBindParameter(d->hStmt,
1400 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1407 *ind == SQL_NULL_DATA ? ind : NULL);
1408 tmpStorage.append(ba);
1411 r = SQLBindParameter(d->hStmt,
1413 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1418 (void *) val.constData(),
1420 *ind == SQL_NULL_DATA ? ind : NULL);
1422 case QVariant::UInt:
1423 r = SQLBindParameter(d->hStmt,
1425 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1430 (void *) val.constData(),
1432 *ind == SQL_NULL_DATA ? ind : NULL);
1434 case QVariant::Double:
1435 r = SQLBindParameter(d->hStmt,
1437 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1442 (void *) val.constData(),
1444 *ind == SQL_NULL_DATA ? ind : NULL);
1446 case QVariant::LongLong:
1447 r = SQLBindParameter(d->hStmt,
1449 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1454 (void *) val.constData(),
1456 *ind == SQL_NULL_DATA ? ind : NULL);
1458 case QVariant::ULongLong:
1459 r = SQLBindParameter(d->hStmt,
1461 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1466 (void *) val.constData(),
1468 *ind == SQL_NULL_DATA ? ind : NULL);
1470 case QVariant::ByteArray:
1471 if (*ind != SQL_NULL_DATA) {
1472 *ind = val.toByteArray().size();
1474 r = SQLBindParameter(d->hStmt,
1476 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1479 val.toByteArray().size(),
1481 (void *) val.toByteArray().constData(),
1482 val.toByteArray().size(),
1485 case QVariant::Bool:
1486 r = SQLBindParameter(d->hStmt,
1488 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1493 (void *) val.constData(),
1495 *ind == SQL_NULL_DATA ? ind : NULL);
1497 case QVariant::String:
1499 QString str = val.toString();
1500 if (*ind != SQL_NULL_DATA)
1501 *ind = str.length() * sizeof(SQLTCHAR);
1502 int strSize = str.length() * sizeof(SQLTCHAR);
1504 if (bindValueType(i) & QSql::Out) {
1505 QVarLengthArray<SQLTCHAR> a(toSQLTCHAR(str));
1506 a.reserve(str.capacity());
1507 QByteArray ba((const char *)a.constData(), a.size() * sizeof(SQLTCHAR));
1508 r = SQLBindParameter(d->hStmt,
1510 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1512 strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1513 0, // god knows... don't change this!
1518 tmpStorage.append(ba);
1521 QByteArray strba((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR));
1522 r = SQLBindParameter(d->hStmt,
1524 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1526 strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1529 (SQLPOINTER)strba.constData(),
1532 tmpStorage.append(strba);
1537 QByteArray str = val.toString().toUtf8();
1538 if (*ind != SQL_NULL_DATA)
1539 *ind = str.length();
1540 int strSize = str.length();
1542 r = SQLBindParameter(d->hStmt,
1544 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1546 strSize > 254 ? SQL_LONGVARCHAR : SQL_VARCHAR,
1549 (void *)str.constData(),
1552 tmpStorage.append(str);
1557 QByteArray ba = val.toByteArray();
1558 if (*ind != SQL_NULL_DATA)
1560 r = SQLBindParameter(d->hStmt,
1562 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1567 (void *) ba.constData(),
1570 tmpStorage.append(ba);
1573 if (r != SQL_SUCCESS) {
1574 qWarning() << "QODBCResult::exec: unable to bind variable:" << qODBCWarn(d);
1575 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1576 "Unable to bind variable"), QSqlError::StatementError, d));
1580 r = SQLExecute(d->hStmt);
1581 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1582 qWarning() << "QODBCResult::exec: Unable to execute statement:" << qODBCWarn(d);
1583 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1584 "Unable to execute statement"), QSqlError::StatementError, d));
1588 SQLINTEGER isScrollable, bufferLength;
1589 r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, &bufferLength);
1590 if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1591 QSqlResult::setForwardOnly(isScrollable==SQL_NONSCROLLABLE);
1594 SQLNumResultCols(d->hStmt, &count);
1597 for (int i = 0; i < count; ++i) {
1598 d->rInf.append(qMakeFieldInfo(d, i));
1600 d->fieldCache.resize(count);
1607 //get out parameters
1608 if (!hasOutValues())
1611 for (i = 0; i < values.count(); ++i) {
1612 switch (values.at(i).type()) {
1613 case QVariant::Date: {
1614 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
1615 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
1617 case QVariant::Time: {
1618 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
1619 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
1621 case QVariant::DateTime: {
1622 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT*)
1623 tmpStorage.takeFirst().constData());
1624 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
1625 QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
1627 case QVariant::Bool:
1629 case QVariant::UInt:
1630 case QVariant::Double:
1631 case QVariant::ByteArray:
1632 case QVariant::LongLong:
1633 case QVariant::ULongLong:
1636 case QVariant::String:
1638 if (bindValueType(i) & QSql::Out) {
1639 QByteArray first = tmpStorage.takeFirst();
1640 QVarLengthArray<SQLTCHAR> array;
1641 array.append((SQLTCHAR *)first.constData(), first.size());
1642 values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR));
1648 if (bindValueType(i) & QSql::Out)
1649 values[i] = tmpStorage.takeFirst();
1652 if (indicators[i] == SQL_NULL_DATA)
1653 values[i] = QVariant(values[i].type());
1658 QSqlRecord QODBCResult::record() const
1660 if (!isActive() || !isSelect())
1661 return QSqlRecord();
1665 QVariant QODBCResult::handle() const
1667 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
1670 bool QODBCResult::nextResult()
1673 setAt(QSql::BeforeFirstRow);
1675 d->fieldCache.clear();
1676 d->fieldCacheIdx = 0;
1679 SQLRETURN r = SQLMoreResults(d->hStmt);
1680 if (r != SQL_SUCCESS) {
1681 if (r == SQL_SUCCESS_WITH_INFO) {
1682 int nativeCode = -1;
1683 QString message = qODBCWarn(d, &nativeCode);
1684 qWarning() << "QODBCResult::nextResult():" << message;
1686 if (r != SQL_NO_DATA)
1687 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1688 "Unable to fetch last"), QSqlError::ConnectionError, d));
1694 SQLNumResultCols(d->hStmt, &count);
1697 for (int i = 0; i < count; ++i) {
1698 d->rInf.append(qMakeFieldInfo(d, i));
1700 d->fieldCache.resize(count);
1709 void QODBCResult::virtual_hook(int id, void *data)
1712 case QSqlResult::DetachFromResultSet:
1714 SQLCloseCursor(d->hStmt);
1716 case QSqlResult::NextResult:
1718 *static_cast<bool*>(data) = nextResult();
1721 QSqlResult::virtual_hook(id, data);
1725 void QODBCResult::setForwardOnly(bool forward)
1727 d->userForwardOnly = forward;
1728 QSqlResult::setForwardOnly(forward);
1731 ////////////////////////////////////////
1734 QODBCDriver::QODBCDriver(QObject *parent)
1735 : QSqlDriver(parent)
1740 QODBCDriver::QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject * parent)
1741 : QSqlDriver(parent)
1748 setOpenError(false);
1752 void QODBCDriver::init()
1754 d = new QODBCDriverPrivate();
1757 QODBCDriver::~QODBCDriver()
1763 bool QODBCDriver::hasFeature(DriverFeature f) const
1766 case Transactions: {
1771 int r = SQLGetInfo(d->hDbc,
1772 (SQLUSMALLINT)SQL_TXN_CAPABLE,
1776 if (r != SQL_SUCCESS || txn == SQL_TC_NONE)
1783 case PreparedQueries:
1784 case PositionalPlaceholders:
1786 case LowPrecisionNumbers:
1789 case NamedPlaceholders:
1791 case BatchOperations:
1793 case EventNotifications:
1795 case MultipleResultSets:
1796 return d->hasMultiResultSets;
1798 if(d->isMySqlServer)
1807 bool QODBCDriver::open(const QString & db,
1808 const QString & user,
1809 const QString & password,
1812 const QString& connOpts)
1817 r = SQLAllocHandle(SQL_HANDLE_ENV,
1820 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1821 qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate environment"), d);
1825 r = SQLSetEnvAttr(d->hEnv,
1826 SQL_ATTR_ODBC_VERSION,
1827 (SQLPOINTER)qGetODBCVersion(connOpts),
1829 r = SQLAllocHandle(SQL_HANDLE_DBC,
1832 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1833 qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate connection"), d);
1838 if (!d->setConnectionOptions(connOpts))
1841 // Create the connection string
1843 // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
1844 if (db.contains(QLatin1String(".dsn"), Qt::CaseInsensitive))
1845 connQStr = QLatin1String("FILEDSN=") + db;
1846 else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive)
1847 || db.contains(QLatin1String("SERVER="), Qt::CaseInsensitive))
1850 connQStr = QLatin1String("DSN=") + db;
1852 if (!user.isEmpty())
1853 connQStr += QLatin1String(";UID=") + user;
1854 if (!password.isEmpty())
1855 connQStr += QLatin1String(";PWD=") + password;
1858 QVarLengthArray<SQLTCHAR> connOut(1024);
1859 memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
1860 r = SQLDriverConnect(d->hDbc,
1863 toSQLTCHAR(connQStr).data(),
1865 (SQLCHAR*)connQStr.toUtf8().data(),
1867 (SQLSMALLINT)connQStr.length(),
1871 /*SQL_DRIVER_NOPROMPT*/0);
1873 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1874 setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
1879 if (!d->checkDriver()) {
1880 setLastError(qMakeError(tr("Unable to connect - Driver doesn't support all "
1881 "functionality required"), QSqlError::ConnectionError, d));
1887 d->checkSchemaUsage();
1888 d->checkSqlServer();
1889 d->checkHasSQLFetchScroll();
1890 d->checkHasMultiResults();
1892 setOpenError(false);
1893 if(d->isMSSqlServer) {
1894 QSqlQuery i(createResult());
1895 i.exec(QLatin1String("SET QUOTED_IDENTIFIER ON"));
1900 void QODBCDriver::close()
1904 setOpenError(false);
1907 void QODBCDriver::cleanup()
1914 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1916 r = SQLDisconnect(d->hDbc);
1917 if (r != SQL_SUCCESS)
1918 qSqlWarning(QLatin1String("QODBCDriver::disconnect: Unable to disconnect datasource"), d);
1920 d->disconnectCount++;
1923 r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
1924 if (r != SQL_SUCCESS)
1925 qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free connection handle"), d);
1930 r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
1931 if (r != SQL_SUCCESS)
1932 qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free environment handle"), d);
1937 // checks whether the server can return char, varchar and longvarchar
1938 // as two byte unicode characters
1939 void QODBCDriverPrivate::checkUnicode()
1945 r = SQLGetInfo(hDbc,
1950 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WCHAR)) {
1955 r = SQLGetInfo(hDbc,
1956 SQL_CONVERT_VARCHAR,
1960 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WVARCHAR)) {
1965 r = SQLGetInfo(hDbc,
1966 SQL_CONVERT_LONGVARCHAR,
1970 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
1975 r = SQLAllocHandle(SQL_HANDLE_STMT,
1979 r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
1980 if(r == SQL_SUCCESS) {
1981 r = SQLFetch(hStmt);
1982 if(r == SQL_SUCCESS) {
1983 QVarLengthArray<SQLWCHAR> buffer(10);
1984 r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL);
1985 if(r == SQL_SUCCESS && fromSQLTCHAR(buffer) == QLatin1String("test")) {
1990 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1993 bool QODBCDriverPrivate::checkDriver() const
1995 #ifdef ODBC_CHECK_DRIVER
1996 static const SQLUSMALLINT reqFunc[] = {
1997 SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
1998 SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
1999 SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
2002 // these functions are optional
2003 static const SQLUSMALLINT optFunc[] = {
2004 SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
2011 // check the required functions
2012 for (i = 0; reqFunc[i] != 0; ++i) {
2014 r = SQLGetFunctions(hDbc, reqFunc[i], &sup);
2016 if (r != SQL_SUCCESS) {
2017 qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
2020 if (sup == SQL_FALSE) {
2021 qWarning () << "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (" << reqFunc[i] <<
2022 ").\nPlease look at the Qt SQL Module Driver documentation for more information.";
2027 // these functions are optional and just generate a warning
2028 for (i = 0; optFunc[i] != 0; ++i) {
2030 r = SQLGetFunctions(hDbc, optFunc[i], &sup);
2032 if (r != SQL_SUCCESS) {
2033 qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
2036 if (sup == SQL_FALSE) {
2037 qWarning() << "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (" << optFunc[i] << ')';
2041 #endif //ODBC_CHECK_DRIVER
2046 void QODBCDriverPrivate::checkSchemaUsage()
2051 r = SQLGetInfo(hDbc,
2056 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
2057 useSchema = (val != 0);
2060 void QODBCDriverPrivate::checkSqlServer()
2063 QVarLengthArray<SQLTCHAR> serverString(200);
2065 memset(serverString.data(), 0, serverString.size() * sizeof(SQLTCHAR));
2067 r = SQLGetInfo(hDbc,
2069 serverString.data(),
2070 serverString.size() * sizeof(SQLTCHAR),
2072 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
2075 serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
2077 serverType = QString::fromUtf8((const char *)serverString.constData(), t);
2079 isMySqlServer = serverType.contains(QLatin1String("mysql"), Qt::CaseInsensitive);
2080 isMSSqlServer = serverType.contains(QLatin1String("Microsoft SQL Server"), Qt::CaseInsensitive);
2082 r = SQLGetInfo(hDbc,
2084 serverString.data(),
2085 serverString.size() * sizeof(SQLTCHAR),
2087 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
2090 serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
2092 serverType = QString::fromUtf8((const char *)serverString.constData(), t);
2094 isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive);
2095 unicode = unicode && !isFreeTDSDriver;
2099 void QODBCDriverPrivate::checkHasSQLFetchScroll()
2102 SQLRETURN r = SQLGetFunctions(hDbc, SQL_API_SQLFETCHSCROLL, &sup);
2103 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || sup != SQL_TRUE) {
2104 hasSQLFetchScroll = false;
2105 qWarning() << "QODBCDriver::checkHasSQLFetchScroll: Warning - Driver doesn't support scrollable result sets, use forward only mode for queries";
2109 void QODBCDriverPrivate::checkHasMultiResults()
2111 QVarLengthArray<SQLTCHAR> driverResponse(2);
2113 SQLRETURN r = SQLGetInfo(hDbc,
2114 SQL_MULT_RESULT_SETS,
2115 driverResponse.data(),
2116 driverResponse.size() * sizeof(SQLTCHAR),
2118 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
2120 hasMultiResultSets = fromSQLTCHAR(driverResponse, length/sizeof(SQLTCHAR)).startsWith(QLatin1Char('Y'));
2122 hasMultiResultSets = QString::fromUtf8((const char *)driverResponse.constData(), length).startsWith(QLatin1Char('Y'));
2126 QSqlResult *QODBCDriver::createResult() const
2128 return new QODBCResult(this, d);
2131 bool QODBCDriver::beginTransaction()
2134 qWarning() << "QODBCDriver::beginTransaction: Database not open";
2137 SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
2138 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
2139 SQL_ATTR_AUTOCOMMIT,
2142 if (r != SQL_SUCCESS) {
2143 setLastError(qMakeError(tr("Unable to disable autocommit"),
2144 QSqlError::TransactionError, d));
2150 bool QODBCDriver::commitTransaction()
2153 qWarning() << "QODBCDriver::commitTransaction: Database not open";
2156 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2159 if (r != SQL_SUCCESS) {
2160 setLastError(qMakeError(tr("Unable to commit transaction"),
2161 QSqlError::TransactionError, d));
2167 bool QODBCDriver::rollbackTransaction()
2170 qWarning() << "QODBCDriver::rollbackTransaction: Database not open";
2173 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2176 if (r != SQL_SUCCESS) {
2177 setLastError(qMakeError(tr("Unable to rollback transaction"),
2178 QSqlError::TransactionError, d));
2184 bool QODBCDriver::endTrans()
2186 SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
2187 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
2188 SQL_ATTR_AUTOCOMMIT,
2191 if (r != SQL_SUCCESS) {
2192 setLastError(qMakeError(tr("Unable to enable autocommit"), QSqlError::TransactionError, d));
2198 QStringList QODBCDriver::tables(QSql::TableType type) const
2205 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2208 if (r != SQL_SUCCESS) {
2209 qSqlWarning(QLatin1String("QODBCDriver::tables: Unable to allocate handle"), d);
2212 r = SQLSetStmtAttr(hStmt,
2213 SQL_ATTR_CURSOR_TYPE,
2214 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2216 QStringList tableType;
2217 if (type & QSql::Tables)
2218 tableType += QLatin1String("TABLE");
2219 if (type & QSql::Views)
2220 tableType += QLatin1String("VIEW");
2221 if (type & QSql::SystemTables)
2222 tableType += QLatin1String("SYSTEM TABLE");
2223 if (tableType.isEmpty())
2226 QString joinedTableTypeString = tableType.join(QLatin1String(","));
2228 r = SQLTables(hStmt,
2236 toSQLTCHAR(joinedTableTypeString).data(),
2238 (SQLCHAR*)joinedTableTypeString.toUtf8().data(),
2240 joinedTableTypeString.length() /* characters, not bytes */);
2242 if (r != SQL_SUCCESS)
2243 qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d);
2245 if (d->hasSQLFetchScroll)
2246 r = SQLFetchScroll(hStmt,
2250 r = SQLFetch(hStmt);
2252 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
2253 qWarning() << "QODBCDriver::tables failed to retrieve table/view list: (" << r << "," << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ")";
2254 return QStringList();
2257 while (r == SQL_SUCCESS) {
2258 QString fieldVal = qGetStringData(hStmt, 2, -1, false);
2259 tl.append(fieldVal);
2261 if (d->hasSQLFetchScroll)
2262 r = SQLFetchScroll(hStmt,
2266 r = SQLFetch(hStmt);
2269 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2270 if (r!= SQL_SUCCESS)
2271 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
2275 QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
2277 QSqlIndex index(tablename);
2280 bool usingSpecialColumns = false;
2281 QSqlRecord rec = record(tablename);
2284 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2287 if (r != SQL_SUCCESS) {
2288 qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to list primary key"), d);
2291 QString catalog, schema, table;
2292 d->splitTableQualifier(tablename, catalog, schema, table);
2294 if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
2295 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
2297 catalog = d->adjustCase(catalog);
2299 if (isIdentifierEscaped(schema, QSqlDriver::TableName))
2300 schema = stripDelimiters(schema, QSqlDriver::TableName);
2302 schema = d->adjustCase(schema);
2304 if (isIdentifierEscaped(table, QSqlDriver::TableName))
2305 table = stripDelimiters(table, QSqlDriver::TableName);
2307 table = d->adjustCase(table);
2309 r = SQLSetStmtAttr(hStmt,
2310 SQL_ATTR_CURSOR_TYPE,
2311 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2313 r = SQLPrimaryKeys(hStmt,
2315 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2317 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2321 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2323 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2327 toSQLTCHAR(table).data(),
2329 (SQLCHAR*)table.toUtf8().data(),
2331 table.length() /* in characters, not in bytes */);
2333 // if the SQLPrimaryKeys() call does not succeed (e.g the driver
2334 // does not support it) - try an alternative method to get hold of
2335 // the primary index (e.g MS Access and FoxPro)
2336 if (r != SQL_SUCCESS) {
2337 r = SQLSpecialColumns(hStmt,
2340 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2342 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2346 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2348 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2352 toSQLTCHAR(table).data(),
2354 (SQLCHAR*)table.toUtf8().data(),
2360 if (r != SQL_SUCCESS) {
2361 qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d);
2363 usingSpecialColumns = true;
2367 if (d->hasSQLFetchScroll)
2368 r = SQLFetchScroll(hStmt,
2372 r = SQLFetch(hStmt);
2375 QString cName, idxName;
2376 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2377 while (r == SQL_SUCCESS) {
2378 if (usingSpecialColumns) {
2379 cName = qGetStringData(hStmt, 1, -1, d->unicode); // column name
2380 idxName = QString::number(fakeId++); // invent a fake index name
2382 cName = qGetStringData(hStmt, 3, -1, d->unicode); // column name
2383 idxName = qGetStringData(hStmt, 5, -1, d->unicode); // pk index name
2385 index.append(rec.field(cName));
2386 index.setName(idxName);
2388 if (d->hasSQLFetchScroll)
2389 r = SQLFetchScroll(hStmt,
2393 r = SQLFetch(hStmt);
2396 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2397 if (r!= SQL_SUCCESS)
2398 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
2402 QSqlRecord QODBCDriver::record(const QString& tablename) const
2409 QString catalog, schema, table;
2410 d->splitTableQualifier(tablename, catalog, schema, table);
2412 if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
2413 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
2415 catalog = d->adjustCase(catalog);
2417 if (isIdentifierEscaped(schema, QSqlDriver::TableName))
2418 schema = stripDelimiters(schema, QSqlDriver::TableName);
2420 schema = d->adjustCase(schema);
2422 if (isIdentifierEscaped(table, QSqlDriver::TableName))
2423 table = stripDelimiters(table, QSqlDriver::TableName);
2425 table = d->adjustCase(table);
2427 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2430 if (r != SQL_SUCCESS) {
2431 qSqlWarning(QLatin1String("QODBCDriver::record: Unable to allocate handle"), d);
2434 r = SQLSetStmtAttr(hStmt,
2435 SQL_ATTR_CURSOR_TYPE,
2436 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2438 r = SQLColumns(hStmt,
2440 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2442 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2446 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2448 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2452 toSQLTCHAR(table).data(),
2454 (SQLCHAR*)table.toUtf8().data(),
2459 if (r != SQL_SUCCESS)
2460 qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d);
2462 if (d->hasSQLFetchScroll)
2463 r = SQLFetchScroll(hStmt,
2467 r = SQLFetch(hStmt);
2469 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2470 while (r == SQL_SUCCESS) {
2472 fil.append(qMakeFieldInfo(hStmt, d));
2474 if (d->hasSQLFetchScroll)
2475 r = SQLFetchScroll(hStmt,
2479 r = SQLFetch(hStmt);
2482 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2483 if (r!= SQL_SUCCESS)
2484 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") + QString::number(r), d);
2489 QString QODBCDriver::formatValue(const QSqlField &field,
2490 bool trimStrings) const
2493 if (field.isNull()) {
2494 r = QLatin1String("NULL");
2495 } else if (field.type() == QVariant::DateTime) {
2496 // Use an escape sequence for the datetime fields
2497 if (field.value().toDateTime().isValid()){
2498 QDate dt = field.value().toDateTime().date();
2499 QTime tm = field.value().toDateTime().time();
2500 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
2501 r = QLatin1String("{ ts '") +
2502 QString::number(dt.year()) + QLatin1Char('-') +
2503 QString::number(dt.month()).rightJustified(2, QLatin1Char('0'), true) +
2505 QString::number(dt.day()).rightJustified(2, QLatin1Char('0'), true) +
2508 QLatin1String("' }");
2510 r = QLatin1String("NULL");
2511 } else if (field.type() == QVariant::ByteArray) {
2512 QByteArray ba = field.value().toByteArray();
2514 static const char hexchars[] = "0123456789abcdef";
2515 for (int i = 0; i < ba.size(); ++i) {
2516 uchar s = (uchar) ba[i];
2517 res += QLatin1Char(hexchars[s >> 4]);
2518 res += QLatin1Char(hexchars[s & 0x0f]);
2520 r = QLatin1String("0x") + res;
2522 r = QSqlDriver::formatValue(field, trimStrings);
2527 QVariant QODBCDriver::handle() const
2529 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
2532 QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
2534 QChar quote = d->quoteChar();
2535 QString res = identifier;
2536 if(!identifier.isEmpty() && !identifier.startsWith(quote) && !identifier.endsWith(quote) ) {
2537 res.replace(quote, QString(quote)+QString(quote));
2538 res.prepend(quote).append(quote);
2539 res.replace(QLatin1Char('.'), QString(quote)+QLatin1Char('.')+QString(quote));
2544 bool QODBCDriver::isIdentifierEscaped(const QString &identifier, IdentifierType) const
2546 QChar quote = d->quoteChar();
2547 return identifier.size() > 2
2548 && identifier.startsWith(quote) //left delimited
2549 && identifier.endsWith(quote); //right delimited