From d9e2c535607b361cc2bae8f5e0ad9a69b3b42c08 Mon Sep 17 00:00:00 2001
From: Charles Yin <charles.yin@nokia.com>
Date: Wed, 10 Nov 2010 13:29:02 +1000
Subject: [PATCH] Fix QODBC fails to exec ANY SQL sentence with ODBC + FreeTDS bug

1. Add checking code for Q_ODBC_VERSION_2 back, which is dropped in commit:
 de306893b464ed0fa00f98488912fd6269713519 by mistake.
2. Put the Q_ODBC_VERSION_2 macro checking at the top of qsql_odbc.cpp file,
   so that the UNICODE macro can be undefined in ODBC's sql.h header.
3. Add warning message for FreeTDS driver if loads FreeTDS ODBC driver in
   unicode mode.

Change-Id: Ic91983e7915c061c14a916d61b8b0c595c64371f
Reviewed-by: pending
---
 src/sql/drivers/odbc/qsql_odbc.cpp |   52 +++++++++++++++++++++++++++++++++++-
 1 files changed, 51 insertions(+), 1 deletions(-)

diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp
index 66dabfa..4cc8e6c 100644
--- a/src/sql/drivers/odbc/qsql_odbc.cpp
+++ b/src/sql/drivers/odbc/qsql_odbc.cpp
@@ -39,6 +39,13 @@
 **
 ****************************************************************************/
 
+#if defined(Q_ODBC_VERSION_2)
+//crude hack to get non-unicode capable driver managers to work
+//this macro must be before the #include "qsql_odbc.h"
+# undef UNICODE
+#endif
+
+
 #include "qsql_odbc.h"
 #include <qsqlrecord.h>
 
@@ -75,6 +82,26 @@ static const int COLNAMESIZE = 256;
 //Map Qt parameter types to ODBC types
 static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
 
+inline static QString fromSQLWCHAR(const QVarLengthArray<SQLWCHAR>& input, int size=-1)
+{
+    QString result;
+
+    int realsize = qMin(size, input.size());
+    if(realsize > 0 && input[realsize-1] == 0)
+        realsize--;
+    switch(sizeof(SQLWCHAR)) {
+        case 2:
+            result=QString::fromUtf16((const ushort *)input.constData(), realsize);
+            break;
+        case 4:
+            result=QString::fromUcs4((const uint *)input.constData(), realsize);
+            break;
+        default:
+            qCritical() << "sizeof(SQLWCHAR) is " << sizeof(SQLWCHAR) << "Don't know how to handle this";
+    }
+    return result;
+}
+
 inline static QString fromSQLTCHAR(const QVarLengthArray<SQLTCHAR>& input, int size=-1)
 {
     QString result;
@@ -336,11 +363,13 @@ static QVariant::Type qDecodeODBCType(SQLSMALLINT sqltype, const T* p, bool isSi
     case SQL_TYPE_TIMESTAMP:
         type = QVariant::DateTime;
         break;
+#ifndef Q_ODBC_VERSION_2
     case SQL_WCHAR:
     case SQL_WVARCHAR:
     case SQL_WLONGVARCHAR:
         type = QVariant::String;
         break;
+#endif
     case SQL_CHAR:
     case SQL_VARCHAR:
 #if (ODBCVER >= 0x0350)
@@ -660,9 +689,17 @@ static QSqlField qMakeFieldInfo(const QODBCPrivate* p, int i )
 
 static int qGetODBCVersion(const QString &connOpts)
 {
+#ifndef Q_ODBC_VERSION_2
     if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"), Qt::CaseInsensitive))
         return SQL_OV_ODBC3;
+#endif
+    if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC2"), Qt::CaseInsensitive))
+        return SQL_OV_ODBC2;
+#ifdef _IODBCUNIX_H
+    return SQL_OV_ODBC3;
+#else
     return SQL_OV_ODBC2;
+#endif
 }
 
 QChar QODBCDriverPrivate::quoteChar()
@@ -763,6 +800,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
                 continue;
             }
             r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0);
+#ifndef Q_ODBC_VERSION_2
         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_POOLING")) {
             if (val == QLatin1String("SQL_CP_OFF"))
                 v = SQL_CP_OFF;
@@ -789,6 +827,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
                 continue;
             }
             r = SQLSetConnectAttr(hDbc, SQL_ATTR_CP_MATCH, (SQLPOINTER)v, 0);
+#endif
         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_ODBC_VERSION")) {
             // Already handled in QODBCDriver::open()
             continue;
@@ -1510,6 +1549,7 @@ bool QODBCResult::exec()
                                       *ind == SQL_NULL_DATA ? ind : NULL);
                 break;
             case QVariant::String:
+#ifndef Q_ODBC_VERSION_2
                 if (d->unicode) {
                     QString str = val.toString();
                     if (*ind != SQL_NULL_DATA)
@@ -1547,6 +1587,7 @@ bool QODBCResult::exec()
                     break;
                 }
                 else
+#endif
                 {
                     QByteArray str = val.toString().toUtf8();
                     if (*ind != SQL_NULL_DATA)
@@ -1952,6 +1993,11 @@ void QODBCDriver::cleanup()
 // as two byte unicode characters
 void QODBCDriverPrivate::checkUnicode()
 {
+#if defined(Q_ODBC_VERSION_2)
+    unicode = false;
+    return;
+#endif
+
     SQLRETURN   r;
     SQLUINTEGER fFunc;
 
@@ -1996,7 +2042,7 @@ void QODBCDriverPrivate::checkUnicode()
         if(r == SQL_SUCCESS) {
             QVarLengthArray<SQLWCHAR> buffer(10);
             r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL);
-            if(r == SQL_SUCCESS && fromSQLTCHAR(buffer) == QLatin1String("test")) {
+            if(r == SQL_SUCCESS && fromSQLWCHAR(buffer) == QLatin1String("test")) {
                 unicode = true;
             }
         }
@@ -2106,6 +2152,10 @@ void QODBCDriverPrivate::checkSqlServer()
         serverType = QString::fromUtf8((const char *)serverString.constData(), t);
 #endif
         isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive);
+        if (isFreeTDSDriver && unicode) {
+            qWarning() << "QODBCDriver::checkSqlServer: Warning - The FreeTDS ODBC driver doesn't support unicode."
+                          "To make the FreeTDS work properly, the Qt ODBC driver needs to be built with Q_ODBC_VERSION_2 or undefine the UNICODE macro before building.";
+        }
         unicode = unicode && !isFreeTDSDriver;
     }
 }
-- 
1.7.0.4

