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 int required = qGetIntData(hStmt, 10).toInt(); // nullable-flag
577 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
578 if (required == SQL_NO_NULLS)
580 else if (required == SQL_NULLABLE)
581 f.setRequired(false);
582 // else we don't know
583 QVariant var = qGetIntData(hStmt, 6);
584 f.setLength(var.isNull() ? -1 : var.toInt()); // column size
585 var = qGetIntData(hStmt, 8).toInt();
586 f.setPrecision(var.isNull() ? -1 : var.toInt()); // precision
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> ba(toSQLTCHAR(str));
1506 ba.reserve(str.capacity());
1507 r = SQLBindParameter(d->hStmt,
1509 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1511 strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1512 0, // god knows... don't change this!
1514 (void *)ba.constData(),
1517 tmpStorage.append(QByteArray((const char *)ba.constData(), ba.size()*sizeof(SQLTCHAR)));
1520 QByteArray strba((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR));
1521 r = SQLBindParameter(d->hStmt,
1523 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1525 strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1528 (SQLPOINTER)strba.constData(),
1531 tmpStorage.append(strba);
1536 QByteArray str = val.toString().toUtf8();
1537 if (*ind != SQL_NULL_DATA)
1538 *ind = str.length();
1539 int strSize = str.length();
1541 r = SQLBindParameter(d->hStmt,
1543 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1545 strSize > 254 ? SQL_LONGVARCHAR : SQL_VARCHAR,
1548 (void *)str.constData(),
1551 tmpStorage.append(str);
1556 QByteArray ba = val.toByteArray();
1557 if (*ind != SQL_NULL_DATA)
1559 r = SQLBindParameter(d->hStmt,
1561 qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1566 (void *) ba.constData(),
1569 tmpStorage.append(ba);
1572 if (r != SQL_SUCCESS) {
1573 qWarning() << "QODBCResult::exec: unable to bind variable:" << qODBCWarn(d);
1574 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1575 "Unable to bind variable"), QSqlError::StatementError, d));
1579 r = SQLExecute(d->hStmt);
1580 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1581 qWarning() << "QODBCResult::exec: Unable to execute statement:" << qODBCWarn(d);
1582 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1583 "Unable to execute statement"), QSqlError::StatementError, d));
1587 SQLINTEGER isScrollable, bufferLength;
1588 r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, &bufferLength);
1589 if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1590 QSqlResult::setForwardOnly(isScrollable==SQL_NONSCROLLABLE);
1593 SQLNumResultCols(d->hStmt, &count);
1596 for (int i = 0; i < count; ++i) {
1597 d->rInf.append(qMakeFieldInfo(d, i));
1599 d->fieldCache.resize(count);
1606 //get out parameters
1607 if (!hasOutValues())
1610 for (i = 0; i < values.count(); ++i) {
1611 switch (values.at(i).type()) {
1612 case QVariant::Date: {
1613 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
1614 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
1616 case QVariant::Time: {
1617 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
1618 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
1620 case QVariant::DateTime: {
1621 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT*)
1622 tmpStorage.takeFirst().constData());
1623 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
1624 QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
1626 case QVariant::Bool:
1628 case QVariant::UInt:
1629 case QVariant::Double:
1630 case QVariant::ByteArray:
1631 case QVariant::LongLong:
1632 case QVariant::ULongLong:
1635 case QVariant::String:
1637 if (bindValueType(i) & QSql::Out) {
1638 QByteArray first = tmpStorage.takeFirst();
1639 QVarLengthArray<SQLTCHAR> array;
1640 array.append((SQLTCHAR *)first.constData(), first.size());
1641 values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR*));
1647 if (bindValueType(i) & QSql::Out)
1648 values[i] = tmpStorage.takeFirst();
1651 if (indicators[i] == SQL_NULL_DATA)
1652 values[i] = QVariant(values[i].type());
1657 QSqlRecord QODBCResult::record() const
1659 if (!isActive() || !isSelect())
1660 return QSqlRecord();
1664 QVariant QODBCResult::handle() const
1666 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
1669 bool QODBCResult::nextResult()
1672 setAt(QSql::BeforeFirstRow);
1674 d->fieldCache.clear();
1675 d->fieldCacheIdx = 0;
1678 SQLRETURN r = SQLMoreResults(d->hStmt);
1679 if (r != SQL_SUCCESS) {
1680 if (r == SQL_SUCCESS_WITH_INFO) {
1681 int nativeCode = -1;
1682 QString message = qODBCWarn(d, &nativeCode);
1683 qWarning() << "QODBCResult::nextResult():" << message;
1685 if (r != SQL_NO_DATA)
1686 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1687 "Unable to fetch last"), QSqlError::ConnectionError, d));
1693 SQLNumResultCols(d->hStmt, &count);
1696 for (int i = 0; i < count; ++i) {
1697 d->rInf.append(qMakeFieldInfo(d, i));
1699 d->fieldCache.resize(count);
1708 void QODBCResult::virtual_hook(int id, void *data)
1711 case QSqlResult::DetachFromResultSet:
1713 SQLCloseCursor(d->hStmt);
1715 case QSqlResult::NextResult:
1717 *static_cast<bool*>(data) = nextResult();
1720 QSqlResult::virtual_hook(id, data);
1724 void QODBCResult::setForwardOnly(bool forward)
1726 d->userForwardOnly = forward;
1727 QSqlResult::setForwardOnly(forward);
1730 ////////////////////////////////////////
1733 QODBCDriver::QODBCDriver(QObject *parent)
1734 : QSqlDriver(parent)
1739 QODBCDriver::QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject * parent)
1740 : QSqlDriver(parent)
1747 setOpenError(false);
1751 void QODBCDriver::init()
1753 d = new QODBCDriverPrivate();
1756 QODBCDriver::~QODBCDriver()
1762 bool QODBCDriver::hasFeature(DriverFeature f) const
1765 case Transactions: {
1770 int r = SQLGetInfo(d->hDbc,
1771 (SQLUSMALLINT)SQL_TXN_CAPABLE,
1775 if (r != SQL_SUCCESS || txn == SQL_TC_NONE)
1782 case PreparedQueries:
1783 case PositionalPlaceholders:
1785 case LowPrecisionNumbers:
1788 case NamedPlaceholders:
1790 case BatchOperations:
1792 case EventNotifications:
1794 case MultipleResultSets:
1795 return d->hasMultiResultSets;
1797 if(d->isMySqlServer)
1806 bool QODBCDriver::open(const QString & db,
1807 const QString & user,
1808 const QString & password,
1811 const QString& connOpts)
1816 r = SQLAllocHandle(SQL_HANDLE_ENV,
1819 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1820 qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate environment"), d);
1824 r = SQLSetEnvAttr(d->hEnv,
1825 SQL_ATTR_ODBC_VERSION,
1826 (SQLPOINTER)qGetODBCVersion(connOpts),
1828 r = SQLAllocHandle(SQL_HANDLE_DBC,
1831 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1832 qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate connection"), d);
1837 if (!d->setConnectionOptions(connOpts))
1840 // Create the connection string
1842 // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
1843 if (db.contains(QLatin1String(".dsn"), Qt::CaseInsensitive))
1844 connQStr = QLatin1String("FILEDSN=") + db;
1845 else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive)
1846 || db.contains(QLatin1String("SERVER="), Qt::CaseInsensitive))
1849 connQStr = QLatin1String("DSN=") + db;
1851 if (!user.isEmpty())
1852 connQStr += QLatin1String(";UID=") + user;
1853 if (!password.isEmpty())
1854 connQStr += QLatin1String(";PWD=") + password;
1857 QVarLengthArray<SQLTCHAR> connOut(1024);
1858 memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
1859 r = SQLDriverConnect(d->hDbc,
1862 toSQLTCHAR(connQStr).data(),
1864 (SQLCHAR*)connQStr.toUtf8().data(),
1866 (SQLSMALLINT)connQStr.length(),
1870 /*SQL_DRIVER_NOPROMPT*/0);
1872 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1873 setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
1878 if (!d->checkDriver()) {
1879 setLastError(qMakeError(tr("Unable to connect - Driver doesn't support all "
1880 "functionality required"), QSqlError::ConnectionError, d));
1886 d->checkSchemaUsage();
1887 d->checkSqlServer();
1888 d->checkHasSQLFetchScroll();
1889 d->checkHasMultiResults();
1891 setOpenError(false);
1892 if(d->isMSSqlServer) {
1893 QSqlQuery i(createResult());
1894 i.exec(QLatin1String("SET QUOTED_IDENTIFIER ON"));
1899 void QODBCDriver::close()
1903 setOpenError(false);
1906 void QODBCDriver::cleanup()
1913 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1915 r = SQLDisconnect(d->hDbc);
1916 if (r != SQL_SUCCESS)
1917 qSqlWarning(QLatin1String("QODBCDriver::disconnect: Unable to disconnect datasource"), d);
1919 d->disconnectCount++;
1922 r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
1923 if (r != SQL_SUCCESS)
1924 qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free connection handle"), d);
1929 r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
1930 if (r != SQL_SUCCESS)
1931 qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free environment handle"), d);
1936 // checks whether the server can return char, varchar and longvarchar
1937 // as two byte unicode characters
1938 void QODBCDriverPrivate::checkUnicode()
1944 r = SQLGetInfo(hDbc,
1949 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WCHAR)) {
1954 r = SQLGetInfo(hDbc,
1955 SQL_CONVERT_VARCHAR,
1959 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WVARCHAR)) {
1964 r = SQLGetInfo(hDbc,
1965 SQL_CONVERT_LONGVARCHAR,
1969 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
1974 r = SQLAllocHandle(SQL_HANDLE_STMT,
1978 r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
1979 if(r == SQL_SUCCESS) {
1980 r = SQLFetch(hStmt);
1981 if(r == SQL_SUCCESS) {
1982 QVarLengthArray<SQLWCHAR> buffer(10);
1983 r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL);
1984 if(r == SQL_SUCCESS && fromSQLTCHAR(buffer) == QLatin1String("test")) {
1989 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1992 bool QODBCDriverPrivate::checkDriver() const
1994 #ifdef ODBC_CHECK_DRIVER
1995 static const SQLUSMALLINT reqFunc[] = {
1996 SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
1997 SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
1998 SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
2001 // these functions are optional
2002 static const SQLUSMALLINT optFunc[] = {
2003 SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
2010 // check the required functions
2011 for (i = 0; reqFunc[i] != 0; ++i) {
2013 r = SQLGetFunctions(hDbc, reqFunc[i], &sup);
2015 if (r != SQL_SUCCESS) {
2016 qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
2019 if (sup == SQL_FALSE) {
2020 qWarning () << "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (" << reqFunc[i] <<
2021 ").\nPlease look at the Qt SQL Module Driver documentation for more information.";
2026 // these functions are optional and just generate a warning
2027 for (i = 0; optFunc[i] != 0; ++i) {
2029 r = SQLGetFunctions(hDbc, optFunc[i], &sup);
2031 if (r != SQL_SUCCESS) {
2032 qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
2035 if (sup == SQL_FALSE) {
2036 qWarning() << "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (" << optFunc[i] << ')';
2040 #endif //ODBC_CHECK_DRIVER
2045 void QODBCDriverPrivate::checkSchemaUsage()
2050 r = SQLGetInfo(hDbc,
2055 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
2056 useSchema = (val != 0);
2059 void QODBCDriverPrivate::checkSqlServer()
2062 QVarLengthArray<SQLTCHAR> serverString(200);
2064 memset(serverString.data(), 0, serverString.size() * sizeof(SQLTCHAR));
2066 r = SQLGetInfo(hDbc,
2068 serverString.data(),
2069 serverString.size() * sizeof(SQLTCHAR),
2071 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
2074 serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
2076 serverType = QString::fromUtf8((const char *)serverString.constData(), t);
2078 isMySqlServer = serverType.contains(QLatin1String("mysql"), Qt::CaseInsensitive);
2079 isMSSqlServer = serverType.contains(QLatin1String("Microsoft SQL Server"), Qt::CaseInsensitive);
2081 r = SQLGetInfo(hDbc,
2083 serverString.data(),
2084 serverString.size() * sizeof(SQLTCHAR),
2086 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
2089 serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
2091 serverType = QString::fromUtf8((const char *)serverString.constData(), t);
2093 isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive);
2094 unicode = unicode && !isFreeTDSDriver;
2098 void QODBCDriverPrivate::checkHasSQLFetchScroll()
2101 SQLRETURN r = SQLGetFunctions(hDbc, SQL_API_SQLFETCHSCROLL, &sup);
2102 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || sup != SQL_TRUE) {
2103 hasSQLFetchScroll = false;
2104 qWarning() << "QODBCDriver::checkHasSQLFetchScroll: Warning - Driver doesn't support scrollable result sets, use forward only mode for queries";
2108 void QODBCDriverPrivate::checkHasMultiResults()
2110 QVarLengthArray<SQLTCHAR> driverResponse(2);
2112 SQLRETURN r = SQLGetInfo(hDbc,
2113 SQL_MULT_RESULT_SETS,
2114 driverResponse.data(),
2115 driverResponse.size() * sizeof(SQLTCHAR),
2117 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
2119 hasMultiResultSets = fromSQLTCHAR(driverResponse, length/sizeof(SQLTCHAR)).startsWith(QLatin1Char('Y'));
2121 hasMultiResultSets = QString::fromUtf8((const char *)driverResponse.constData(), length).startsWith(QLatin1Char('Y'));
2125 QSqlResult *QODBCDriver::createResult() const
2127 return new QODBCResult(this, d);
2130 bool QODBCDriver::beginTransaction()
2133 qWarning() << "QODBCDriver::beginTransaction: Database not open";
2136 SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
2137 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
2138 SQL_ATTR_AUTOCOMMIT,
2141 if (r != SQL_SUCCESS) {
2142 setLastError(qMakeError(tr("Unable to disable autocommit"),
2143 QSqlError::TransactionError, d));
2149 bool QODBCDriver::commitTransaction()
2152 qWarning() << "QODBCDriver::commitTransaction: Database not open";
2155 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2158 if (r != SQL_SUCCESS) {
2159 setLastError(qMakeError(tr("Unable to commit transaction"),
2160 QSqlError::TransactionError, d));
2166 bool QODBCDriver::rollbackTransaction()
2169 qWarning() << "QODBCDriver::rollbackTransaction: Database not open";
2172 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2175 if (r != SQL_SUCCESS) {
2176 setLastError(qMakeError(tr("Unable to rollback transaction"),
2177 QSqlError::TransactionError, d));
2183 bool QODBCDriver::endTrans()
2185 SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
2186 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
2187 SQL_ATTR_AUTOCOMMIT,
2190 if (r != SQL_SUCCESS) {
2191 setLastError(qMakeError(tr("Unable to enable autocommit"), QSqlError::TransactionError, d));
2197 QStringList QODBCDriver::tables(QSql::TableType type) const
2204 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2207 if (r != SQL_SUCCESS) {
2208 qSqlWarning(QLatin1String("QODBCDriver::tables: Unable to allocate handle"), d);
2211 r = SQLSetStmtAttr(hStmt,
2212 SQL_ATTR_CURSOR_TYPE,
2213 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2215 QStringList tableType;
2216 if (type & QSql::Tables)
2217 tableType += QLatin1String("TABLE");
2218 if (type & QSql::Views)
2219 tableType += QLatin1String("VIEW");
2220 if (type & QSql::SystemTables)
2221 tableType += QLatin1String("SYSTEM TABLE");
2222 if (tableType.isEmpty())
2225 QString joinedTableTypeString = tableType.join(QLatin1String(","));
2227 r = SQLTables(hStmt,
2235 toSQLTCHAR(joinedTableTypeString).data(),
2237 (SQLCHAR*)joinedTableTypeString.toUtf8().data(),
2239 joinedTableTypeString.length() /* characters, not bytes */);
2241 if (r != SQL_SUCCESS)
2242 qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d);
2244 if (d->hasSQLFetchScroll)
2245 r = SQLFetchScroll(hStmt,
2249 r = SQLFetch(hStmt);
2251 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
2252 qWarning() << "QODBCDriver::tables failed to retrieve table/view list: (" << r << "," << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ")";
2253 return QStringList();
2256 while (r == SQL_SUCCESS) {
2257 QString fieldVal = qGetStringData(hStmt, 2, -1, false);
2258 tl.append(fieldVal);
2260 if (d->hasSQLFetchScroll)
2261 r = SQLFetchScroll(hStmt,
2265 r = SQLFetch(hStmt);
2268 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2269 if (r!= SQL_SUCCESS)
2270 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
2274 QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
2276 QSqlIndex index(tablename);
2279 bool usingSpecialColumns = false;
2280 QSqlRecord rec = record(tablename);
2283 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2286 if (r != SQL_SUCCESS) {
2287 qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to list primary key"), d);
2290 QString catalog, schema, table;
2291 d->splitTableQualifier(tablename, catalog, schema, table);
2293 if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
2294 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
2296 catalog = d->adjustCase(catalog);
2298 if (isIdentifierEscaped(schema, QSqlDriver::TableName))
2299 schema = stripDelimiters(schema, QSqlDriver::TableName);
2301 schema = d->adjustCase(schema);
2303 if (isIdentifierEscaped(table, QSqlDriver::TableName))
2304 table = stripDelimiters(table, QSqlDriver::TableName);
2306 table = d->adjustCase(table);
2308 r = SQLSetStmtAttr(hStmt,
2309 SQL_ATTR_CURSOR_TYPE,
2310 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2312 r = SQLPrimaryKeys(hStmt,
2314 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2316 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2320 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2322 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2326 toSQLTCHAR(table).data(),
2328 (SQLCHAR*)table.toUtf8().data(),
2330 table.length() /* in characters, not in bytes */);
2332 // if the SQLPrimaryKeys() call does not succeed (e.g the driver
2333 // does not support it) - try an alternative method to get hold of
2334 // the primary index (e.g MS Access and FoxPro)
2335 if (r != SQL_SUCCESS) {
2336 r = SQLSpecialColumns(hStmt,
2339 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2341 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2345 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2347 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2351 toSQLTCHAR(table).data(),
2353 (SQLCHAR*)table.toUtf8().data(),
2359 if (r != SQL_SUCCESS) {
2360 qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d);
2362 usingSpecialColumns = true;
2366 if (d->hasSQLFetchScroll)
2367 r = SQLFetchScroll(hStmt,
2371 r = SQLFetch(hStmt);
2374 QString cName, idxName;
2375 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2376 while (r == SQL_SUCCESS) {
2377 if (usingSpecialColumns) {
2378 cName = qGetStringData(hStmt, 1, -1, d->unicode); // column name
2379 idxName = QString::number(fakeId++); // invent a fake index name
2381 cName = qGetStringData(hStmt, 3, -1, d->unicode); // column name
2382 idxName = qGetStringData(hStmt, 5, -1, d->unicode); // pk index name
2384 index.append(rec.field(cName));
2385 index.setName(idxName);
2387 if (d->hasSQLFetchScroll)
2388 r = SQLFetchScroll(hStmt,
2392 r = SQLFetch(hStmt);
2395 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2396 if (r!= SQL_SUCCESS)
2397 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
2401 QSqlRecord QODBCDriver::record(const QString& tablename) const
2408 QString catalog, schema, table;
2409 d->splitTableQualifier(tablename, catalog, schema, table);
2411 if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
2412 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
2414 catalog = d->adjustCase(catalog);
2416 if (isIdentifierEscaped(schema, QSqlDriver::TableName))
2417 schema = stripDelimiters(schema, QSqlDriver::TableName);
2419 schema = d->adjustCase(schema);
2421 if (isIdentifierEscaped(table, QSqlDriver::TableName))
2422 table = stripDelimiters(table, QSqlDriver::TableName);
2424 table = d->adjustCase(table);
2426 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2429 if (r != SQL_SUCCESS) {
2430 qSqlWarning(QLatin1String("QODBCDriver::record: Unable to allocate handle"), d);
2433 r = SQLSetStmtAttr(hStmt,
2434 SQL_ATTR_CURSOR_TYPE,
2435 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2437 r = SQLColumns(hStmt,
2439 catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2441 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2445 schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2447 schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2451 toSQLTCHAR(table).data(),
2453 (SQLCHAR*)table.toUtf8().data(),
2458 if (r != SQL_SUCCESS)
2459 qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d);
2461 if (d->hasSQLFetchScroll)
2462 r = SQLFetchScroll(hStmt,
2466 r = SQLFetch(hStmt);
2468 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2469 while (r == SQL_SUCCESS) {
2471 fil.append(qMakeFieldInfo(hStmt, d));
2473 if (d->hasSQLFetchScroll)
2474 r = SQLFetchScroll(hStmt,
2478 r = SQLFetch(hStmt);
2481 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2482 if (r!= SQL_SUCCESS)
2483 qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") + QString::number(r), d);
2488 QString QODBCDriver::formatValue(const QSqlField &field,
2489 bool trimStrings) const
2492 if (field.isNull()) {
2493 r = QLatin1String("NULL");
2494 } else if (field.type() == QVariant::DateTime) {
2495 // Use an escape sequence for the datetime fields
2496 if (field.value().toDateTime().isValid()){
2497 QDate dt = field.value().toDateTime().date();
2498 QTime tm = field.value().toDateTime().time();
2499 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
2500 r = QLatin1String("{ ts '") +
2501 QString::number(dt.year()) + QLatin1Char('-') +
2502 QString::number(dt.month()).rightJustified(2, QLatin1Char('0'), true) +
2504 QString::number(dt.day()).rightJustified(2, QLatin1Char('0'), true) +
2507 QLatin1String("' }");
2509 r = QLatin1String("NULL");
2510 } else if (field.type() == QVariant::ByteArray) {
2511 QByteArray ba = field.value().toByteArray();
2513 static const char hexchars[] = "0123456789abcdef";
2514 for (int i = 0; i < ba.size(); ++i) {
2515 uchar s = (uchar) ba[i];
2516 res += QLatin1Char(hexchars[s >> 4]);
2517 res += QLatin1Char(hexchars[s & 0x0f]);
2519 r = QLatin1String("0x") + res;
2521 r = QSqlDriver::formatValue(field, trimStrings);
2526 QVariant QODBCDriver::handle() const
2528 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
2531 QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
2533 QChar quote = d->quoteChar();
2534 QString res = identifier;
2535 if(!identifier.isEmpty() && !identifier.startsWith(quote) && !identifier.endsWith(quote) ) {
2536 res.replace(quote, QString(quote)+QString(quote));
2537 res.prepend(quote).append(quote);
2538 res.replace(QLatin1Char('.'), QString(quote)+QLatin1Char('.')+QString(quote));
2543 bool QODBCDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType) const
2545 QChar quote = d->quoteChar();
2546 return identifier.size() > 2
2547 && identifier.startsWith(quote) //left delimited
2548 && identifier.endsWith(quote); //right delimited