src/sql/drivers/odbc/qsql_odbc.cpp
changeset 19 fcece45ef507
parent 18 2f34d5167611
child 30 5dc02b23752f
--- a/src/sql/drivers/odbc/qsql_odbc.cpp	Fri Apr 16 15:50:13 2010 +0300
+++ b/src/sql/drivers/odbc/qsql_odbc.cpp	Mon May 03 13:17:34 2010 +0300
@@ -66,7 +66,7 @@
 //crude hack to get non-unicode capable driver managers to work
 # undef UNICODE
 # define SQLTCHAR SQLCHAR
-# define SQL_C_WCHAR SQL_C_CHAR
+# define SQL_C_TCHAR SQL_C_CHAR
 #endif
 
 // newer platform SDKs use SQLLEN instead of SQLINTEGER
@@ -78,31 +78,74 @@
 # define QSQLULEN SQLULEN
 #endif
 
-
 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 fromSQLTCHAR(const QVarLengthArray<SQLTCHAR>& input, int size=-1)
+{
+    QString result;
+
+    int realsize = qMin(size, input.size());
+    if(realsize > 0 && input[realsize-1] == 0)
+        realsize--;
+    switch(sizeof(SQLTCHAR)) {
+        case 1:
+            result=QString::fromUtf8((const char *)input.constData(), realsize);
+            break;
+        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(SQLTCHAR) is " << sizeof(SQLTCHAR) << "Don't know how to handle this";
+    }
+    return result;
+}
+
+inline static QVarLengthArray<SQLTCHAR> toSQLTCHAR(const QString &input)
+{
+    QVarLengthArray<SQLTCHAR> result;
+    result.resize(input.size());
+    switch(sizeof(SQLTCHAR)) {
+        case 1:
+            memcpy(result.data(), input.toUtf8().data(), input.size());
+            break;
+        case 2:
+            memcpy(result.data(), input.unicode(), input.size() * 2);
+            break;
+        case 4:
+            memcpy(result.data(), input.toUcs4().data(), input.size() * 4);
+            break;
+        default:
+            qCritical() << "sizeof(SQLTCHAR) is " << sizeof(SQLTCHAR) << "Don't know how to handle this";
+    }
+    result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't.
+    return result;
+}
+
 class QODBCDriverPrivate
 {
 public:
     enum DefaultCase{Lower, Mixed, Upper, Sensitive};
     QODBCDriverPrivate()
-    : hEnv(0), hDbc(0), useSchema(false), disconnectCount(0), isMySqlServer(false),
-           isMSSqlServer(false), hasSQLFetchScroll(true), hasMultiResultSets(false),
-           isQuoteInitialized(false), quote(QLatin1Char('"'))
+    : hEnv(0), hDbc(0), unicode(false), useSchema(false), disconnectCount(0), isMySqlServer(false),
+           isMSSqlServer(false), isFreeTDSDriver(false), hasSQLFetchScroll(true),
+           hasMultiResultSets(false), isQuoteInitialized(false), quote(QLatin1Char('"'))
     {
-        unicode = false;
     }
 
     SQLHANDLE hEnv;
     SQLHANDLE hDbc;
 
-    uint unicode :1;
-    uint useSchema :1;
+    bool unicode;
+    bool useSchema;
     int disconnectCount;
     bool isMySqlServer;
     bool isMSSqlServer;
+    bool isFreeTDSDriver;
     bool hasSQLFetchScroll;
     bool hasMultiResultSets;
 
@@ -129,7 +172,10 @@
     QODBCPrivate(QODBCDriverPrivate *dpp)
     : hStmt(0), useSchema(false), hasSQLFetchScroll(true), driverPrivate(dpp), userForwardOnly(false)
     {
-        unicode = false;
+        unicode = dpp->unicode;
+        useSchema = dpp->useSchema;
+        disconnectCount = dpp->disconnectCount;
+        hasSQLFetchScroll = dpp->hasSQLFetchScroll;
     }
 
     inline void clearValues()
@@ -139,8 +185,8 @@
     SQLHANDLE dpDbc() const { return driverPrivate ? driverPrivate->hDbc : 0;}
     SQLHANDLE hStmt;
 
-    uint unicode :1;
-    uint useSchema :1;
+    bool unicode;
+    bool useSchema;
 
     QSqlRecord rInf;
     QVector<QVariant> fieldCache;
@@ -177,19 +223,18 @@
     int i = 1;
 
     description_[0] = 0;
-    r = SQLGetDiagRec(handleType,
-                      handle,
-                      i,
-                      state_,
-                      &nativeCode_,
-                      0,
-                      NULL,
-                      &msgLen);
-    if(r == SQL_NO_DATA)
-        return QString();
-    description_.resize(msgLen+1);
     do {
         r = SQLGetDiagRec(handleType,
+                          handle,
+                          i,
+                          state_,
+                          &nativeCode_,
+                          0,
+                          NULL,
+                          &msgLen);
+        if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && msgLen > 0)
+            description_.resize(msgLen+1);
+        r = SQLGetDiagRec(handleType,
                             handle,
                             i,
                             state_,
@@ -202,9 +247,9 @@
                 *nativeCode = nativeCode_;
             QString tmpstore;
 #ifdef UNICODE
-            tmpstore = QString((const QChar*)description_.data(), msgLen);
+            tmpstore = fromSQLTCHAR(description_, msgLen);
 #else
-            tmpstore = QString::fromLocal8Bit((const char*)description_.data(), msgLen);
+            tmpstore = QString::fromUtf8((const char*)description_.constData(), msgLen);
 #endif
             if(result != tmpstore) {
                 if(!result.isEmpty())
@@ -223,13 +268,13 @@
 {
     return (qWarnODBCHandle(SQL_HANDLE_ENV, odbc->dpEnv()) + QLatin1Char(' ')
              + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->dpDbc()) + QLatin1Char(' ')
-             + qWarnODBCHandle(SQL_HANDLE_STMT, odbc->hStmt, nativeCode));
+             + qWarnODBCHandle(SQL_HANDLE_STMT, odbc->hStmt, nativeCode)).simplified();
 }
 
 static QString qODBCWarn(const QODBCDriverPrivate* odbc, int *nativeCode = 0)
 {
     return (qWarnODBCHandle(SQL_HANDLE_ENV, odbc->hEnv) + QLatin1Char(' ')
-             + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->hDbc, nativeCode));
+             + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->hDbc, nativeCode)).simplified();
 }
 
 static void qSqlWarning(const QString& message, const QODBCPrivate* odbc)
@@ -307,7 +352,9 @@
 #endif
     case SQL_CHAR:
     case SQL_VARCHAR:
+#if (ODBCVER >= 0x0350)
     case SQL_GUID:
+#endif
     case SQL_LONGVARCHAR:
         type = QVariant::String;
         break;
@@ -331,45 +378,89 @@
         colSize = 65536;
     } else {
         colSize++; // make sure there is room for more than the 0 termination
-        if (unicode) {
-            colSize *= 2; // a tiny bit faster, since it saves a SQLGetData() call
-        }
     }
-    QVarLengthArray<char> buf(colSize);
-    while (true) {
+    if(unicode) {
         r = SQLGetData(hStmt,
                         column+1,
-                        unicode ? SQL_C_WCHAR : SQL_C_CHAR,
-                        (SQLPOINTER)buf.data(),
-                        colSize,
+                        SQL_C_TCHAR,
+                        NULL,
+                        0,
                         &lengthIndicator);
-        if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
-            if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
+        if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
+            colSize = lengthIndicator/sizeof(SQLTCHAR) + 1;
+        QVarLengthArray<SQLTCHAR> buf(colSize);
+        memset(buf.data(), 0, colSize*sizeof(SQLTCHAR));
+        while (true) {
+            r = SQLGetData(hStmt,
+                            column+1,
+                            SQL_C_TCHAR,
+                            (SQLPOINTER)buf.data(),
+                            colSize*sizeof(SQLTCHAR),
+                            &lengthIndicator);
+            if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+                if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
+                    fieldVal.clear();
+                    break;
+                }
+                // if SQL_SUCCESS_WITH_INFO is returned, indicating that
+                // more data can be fetched, the length indicator does NOT
+                // contain the number of bytes returned - it contains the
+                // total number of bytes that CAN be fetched
+                // colSize-1: remove 0 termination when there is more data to fetch
+                int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator/sizeof(SQLTCHAR);
+                    fieldVal += fromSQLTCHAR(buf, rSize);
+                if (lengthIndicator < (unsigned int)colSize*sizeof(SQLTCHAR)) {
+                    // workaround for Drivermanagers that don't return SQL_NO_DATA
+                    break;
+                }
+            } else if (r == SQL_NO_DATA) {
+                break;
+            } else {
+                qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
                 fieldVal.clear();
                 break;
             }
-            // if SQL_SUCCESS_WITH_INFO is returned, indicating that
-            // more data can be fetched, the length indicator does NOT
-            // contain the number of bytes returned - it contains the
-            // total number of bytes that CAN be fetched
-            // colSize-1: remove 0 termination when there is more data to fetch
-            int rSize = (r == SQL_SUCCESS_WITH_INFO) ? (unicode ? colSize-2 : colSize-1) : lengthIndicator;
-            if (unicode) {
-                fieldVal += QString((const QChar*) buf.constData(), rSize / 2);
+        }
+    } else {
+        r = SQLGetData(hStmt,
+                        column+1,
+                        SQL_C_CHAR,
+                        NULL,
+                        0,
+                        &lengthIndicator);
+        if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
+            colSize = lengthIndicator + 1;
+        QVarLengthArray<SQLCHAR> buf(colSize);
+        while (true) {
+            r = SQLGetData(hStmt,
+                            column+1,
+                            SQL_C_CHAR,
+                            (SQLPOINTER)buf.data(),
+                            colSize,
+                            &lengthIndicator);
+            if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+                if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
+                    fieldVal.clear();
+                    break;
+                }
+                // if SQL_SUCCESS_WITH_INFO is returned, indicating that
+                // more data can be fetched, the length indicator does NOT
+                // contain the number of bytes returned - it contains the
+                // total number of bytes that CAN be fetched
+                // colSize-1: remove 0 termination when there is more data to fetch
+                int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator;
+                    fieldVal += QString::fromUtf8((const char *)buf.constData(), rSize);
+                if (lengthIndicator < (unsigned int)colSize) {
+                    // workaround for Drivermanagers that don't return SQL_NO_DATA
+                    break;
+                }
+            } else if (r == SQL_NO_DATA) {
+                break;
             } else {
-                fieldVal += QString::fromAscii(buf.constData(), rSize);
-            }
-            memset(buf.data(), 0, colSize);
-            if (lengthIndicator < colSize) {
-                // workaround for Drivermanagers that don't return SQL_NO_DATA
+                qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
+                fieldVal.clear();
                 break;
             }
-        } else if (r == SQL_NO_DATA) {
-            break;
-        } else {
-            qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
-            fieldVal.clear();
-            break;
         }
     }
     return fieldVal;
@@ -386,10 +477,11 @@
     QSQLLEN lengthIndicator = 0;
     SQLRETURN r = SQL_ERROR;
 
-    SQLTCHAR colName[COLNAMESIZE];
+    QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
+
     r = SQLDescribeCol(hStmt,
                        column + 1,
-                       colName,
+                       colName.data(),
                        COLNAMESIZE,
                        &colNameLen,
                        &colType,
@@ -522,10 +614,10 @@
     SQLSMALLINT colScale;
     SQLSMALLINT nullable;
     SQLRETURN r = SQL_ERROR;
-    SQLTCHAR colName[COLNAMESIZE];
+    QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
     r = SQLDescribeCol(p->hStmt,
                         i+1,
-                        colName,
+                        colName.data(),
                         (SQLSMALLINT)COLNAMESIZE,
                         &colNameLen,
                         &colType,
@@ -551,9 +643,9 @@
     }
 
 #ifdef UNICODE
-    QString qColName((const QChar*)colName, colNameLen);
+    QString qColName(fromSQLTCHAR(colName, colNameLen));
 #else
-    QString qColName = QString::fromLocal8Bit((const char*)colName);
+    QString qColName = QString::fromUtf8((const char *)colName.constData());
 #endif
     // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
     int required = -1;
@@ -581,24 +673,33 @@
     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()
 {
     if (!isQuoteInitialized) {
-        char driverResponse[4];
+        SQLTCHAR driverResponse[4];
         SQLSMALLINT length;
         int r = SQLGetInfo(hDbc,
                 SQL_IDENTIFIER_QUOTE_CHAR,
                 &driverResponse,
                 sizeof(driverResponse),
                 &length);
-        if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+        if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+#ifdef UNICODE
+            quote = QChar(driverResponse[0]);
+#else
             quote = QLatin1Char(driverResponse[0]);
-        } else {
+#endif
+        else
             quote = QLatin1Char('"');
-        }
         isQuoteInitialized = true;
     }
     return quote;
@@ -642,11 +743,11 @@
             val.utf16(); // 0 terminate
             r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
 #ifdef UNICODE
-                                    (SQLWCHAR*) val.unicode(),
+                                    toSQLTCHAR(val).data(),
 #else
-                                    (SQLCHAR*) val.toLatin1().constData(),
+                                    (SQLCHAR*) val.toUtf8().data(),
 #endif
-                                    SQL_NTS);
+                                    val.length()*sizeof(SQLTCHAR));
         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) {
             if (val.toUpper() == QLatin1String("SQL_TRUE")) {
                 v = SQL_TRUE;
@@ -664,11 +765,11 @@
             val.utf16(); // 0 terminate
             r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
 #ifdef UNICODE
-                                    (SQLWCHAR*) val.unicode(),
+                                    toSQLTCHAR(val).data(),
 #else
-                                    (SQLCHAR*) val.toLatin1().constData(),
+                                    (SQLCHAR*) val.toUtf8().data(),
 #endif
-                                    SQL_NTS);
+                                    val.length()*sizeof(SQLTCHAR));
         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) {
             if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) {
                 v = SQL_OPT_TRACE_OFF;
@@ -814,10 +915,6 @@
 : QSqlResult(db)
 {
     d = new QODBCPrivate(p);
-    d->unicode = p->unicode;
-    d->useSchema = p->useSchema;
-    d->disconnectCount = p->disconnectCount;
-    d->hasSQLFetchScroll = p->hasSQLFetchScroll;
 }
 
 QODBCResult::~QODBCResult()
@@ -880,12 +977,12 @@
 
 #ifdef UNICODE
     r = SQLExecDirect(d->hStmt,
-                       (SQLWCHAR*) query.unicode(),
+                       toSQLTCHAR(query).data(),
                        (SQLINTEGER) query.length());
 #else
-    QByteArray query8 = query.toLocal8Bit();
+    QByteArray query8 = query.toUtf8();
     r = SQLExecDirect(d->hStmt,
-                       (SQLCHAR*) query8.constData(),
+                       (SQLCHAR*) query8.data(),
                        (SQLINTEGER) query8.length());
 #endif
     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) {
@@ -1231,12 +1328,12 @@
 
 #ifdef UNICODE
     r = SQLPrepare(d->hStmt,
-                    (SQLWCHAR*) query.unicode(),
+                    toSQLTCHAR(query).data(),
                     (SQLINTEGER) query.length());
 #else
-    QByteArray query8 = query.toLocal8Bit();
+    QByteArray query8 = query.toUtf8();
     r = SQLPrepare(d->hStmt,
-                    (SQLCHAR*) query8.constData(),
+                    (SQLCHAR*) query8.data(),
                     (SQLINTEGER) query8.length());
 #endif
 
@@ -1435,43 +1532,44 @@
 #ifndef Q_ODBC_VERSION_2
                 if (d->unicode) {
                     QString str = val.toString();
-                    str.utf16();
                     if (*ind != SQL_NULL_DATA)
-                        *ind = str.length() * sizeof(QChar);
-                    int strSize = str.length() * sizeof(QChar);
+                        *ind = str.length() * sizeof(SQLTCHAR);
+                    int strSize = str.length() * sizeof(SQLTCHAR);
 
                     if (bindValueType(i) & QSql::Out) {
-                        QByteArray ba((char*)str.constData(), str.capacity() * sizeof(QChar));
+                        QVarLengthArray<SQLTCHAR> ba(toSQLTCHAR(str));
+                        ba.reserve(str.capacity());
                         r = SQLBindParameter(d->hStmt,
                                             i + 1,
                                             qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
-                                            SQL_C_WCHAR,
+                                            SQL_C_TCHAR,
                                             strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
                                             0, // god knows... don't change this!
                                             0,
                                             (void *)ba.constData(),
                                             ba.size(),
                                             ind);
-                        tmpStorage.append(ba);
+                        tmpStorage.append(QByteArray((const char *)ba.constData(), ba.size()*sizeof(SQLTCHAR)));
                         break;
                     }
-
+                    QByteArray strba((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR));
                     r = SQLBindParameter(d->hStmt,
                                           i + 1,
                                           qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
-                                          SQL_C_WCHAR,
+                                          SQL_C_TCHAR,
                                           strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
                                           strSize,
                                           0,
-                                          (void *)str.constData(),
-                                          strSize,
+                                          (SQLPOINTER)strba.constData(),
+                                          strba.size(),
                                           ind);
+                    tmpStorage.append(strba);
                     break;
                 }
                 else
 #endif
                 {
-                    QByteArray str = val.toString().toAscii();
+                    QByteArray str = val.toString().toUtf8();
                     if (*ind != SQL_NULL_DATA)
                         *ind = str.length();
                     int strSize = str.length();
@@ -1572,15 +1670,18 @@
                 break;
             case QVariant::String:
                 if (d->unicode) {
-                    if (bindValueType(i) & QSql::Out)
-                        values[i] = QString::fromUtf16((ushort*)tmpStorage.takeFirst().constData());
+                    if (bindValueType(i) & QSql::Out) {
+                        QByteArray first = tmpStorage.takeFirst();
+                        QVarLengthArray<SQLTCHAR> array;
+                        array.append((SQLTCHAR *)first.constData(), first.size());
+                        values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR*));
+                    }
                     break;
                 }
                 // fall through
             default: {
-                QByteArray ba = tmpStorage.takeFirst();
                 if (bindValueType(i) & QSql::Out)
-                    values[i] = QString::fromAscii(ba.constData());
+                    values[i] = tmpStorage.takeFirst();
                 break; }
         }
         if (indicators[i] == SQL_NULL_DATA)
@@ -1789,19 +1890,21 @@
         connQStr += QLatin1String(";PWD=") + password;
 
     SQLSMALLINT cb;
-    SQLTCHAR connOut[1024];
+    QVarLengthArray<SQLTCHAR> connOut(1024);
+    memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
     r = SQLDriverConnect(d->hDbc,
                           NULL,
 #ifdef UNICODE
-                          (SQLWCHAR*)connQStr.unicode(),
+                          toSQLTCHAR(connQStr).data(),
 #else
-                          (SQLCHAR*)connQStr.toLatin1().constData(),
+                          (SQLCHAR*)connQStr.toUtf8().data(),
 #endif
                           (SQLSMALLINT)connQStr.length(),
-                          connOut,
+                          connOut.data(),
                           1024,
                           &cb,
-                          SQL_DRIVER_NOPROMPT);
+                          /*SQL_DRIVER_NOPROMPT*/0);
+
     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
         setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
         setOpenError(true);
@@ -1886,6 +1989,7 @@
                     NULL);
     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WCHAR)) {
         unicode = true;
+        return;
     }
 
     r = SQLGetInfo(hDbc,
@@ -1895,6 +1999,7 @@
                     NULL);
     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WVARCHAR)) {
         unicode = true;
+        return;
     }
 
     r = SQLGetInfo(hDbc,
@@ -1904,7 +2009,25 @@
                     NULL);
     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
         unicode = true;
+        return;
     }
+    SQLHANDLE hStmt;
+    r = SQLAllocHandle(SQL_HANDLE_STMT,
+                                  hDbc,
+                                  &hStmt);
+
+    r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
+    if(r == SQL_SUCCESS) {
+        r = SQLFetch(hStmt);
+        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")) {
+                unicode = true;
+            }
+        }
+    }
+    r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
 }
 
 bool QODBCDriverPrivate::checkDriver() const
@@ -1977,24 +2100,40 @@
 void QODBCDriverPrivate::checkSqlServer()
 {
     SQLRETURN   r;
-    char serverString[200];
+    QVarLengthArray<SQLTCHAR> serverString(200);
     SQLSMALLINT t;
+    memset(serverString.data(), 0, serverString.size() * sizeof(SQLTCHAR));
 
     r = SQLGetInfo(hDbc,
                    SQL_DBMS_NAME,
-                   serverString,
-                   sizeof(serverString),
+                   serverString.data(),
+                   serverString.size() * sizeof(SQLTCHAR),
                    &t);
     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
         QString serverType;
 #ifdef UNICODE
-        serverType = QString(reinterpret_cast<const QChar*>(serverString), t/sizeof(QChar));
+        serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
 #else
-        serverType = QString::fromLocal8Bit(serverString, t);
+        serverType = QString::fromUtf8((const char *)serverString.constData(), t);
 #endif
         isMySqlServer = serverType.contains(QLatin1String("mysql"), Qt::CaseInsensitive);
         isMSSqlServer = serverType.contains(QLatin1String("Microsoft SQL Server"), Qt::CaseInsensitive);
     }
+    r = SQLGetInfo(hDbc,
+                   SQL_DRIVER_NAME,
+                   serverString.data(),
+                   serverString.size() * sizeof(SQLTCHAR),
+                   &t);
+    if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+        QString serverType;
+#ifdef UNICODE
+        serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
+#else
+        serverType = QString::fromUtf8((const char *)serverString.constData(), t);
+#endif
+        isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive);
+        unicode = isFreeTDSDriver == false;
+    }
 }
 
 void QODBCDriverPrivate::checkHasSQLFetchScroll()
@@ -2009,18 +2148,18 @@
 
 void QODBCDriverPrivate::checkHasMultiResults()
 {
-    char driverResponse[4];
+    QVarLengthArray<SQLTCHAR> driverResponse(2);
     SQLSMALLINT length;
     SQLRETURN r = SQLGetInfo(hDbc,
                              SQL_MULT_RESULT_SETS,
-                             driverResponse,
-                             sizeof(driverResponse),
+                             driverResponse.data(),
+                             driverResponse.size() * sizeof(SQLTCHAR),
                              &length);
     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
 #ifdef UNICODE
-        hasMultiResultSets = QString(reinterpret_cast<const QChar*>(driverResponse), length/sizeof(QChar)).startsWith(QLatin1Char('Y'));
+        hasMultiResultSets = fromSQLTCHAR(driverResponse, length/sizeof(SQLTCHAR)).startsWith(QLatin1Char('Y'));
 #else
-        hasMultiResultSets = QString::fromLocal8Bit(driverResponse, length).startsWith(QLatin1Char('Y'));
+        hasMultiResultSets = QString::fromUtf8((const char *)driverResponse.constData(), length).startsWith(QLatin1Char('Y'));
 #endif
 }
 
@@ -2134,9 +2273,9 @@
                    NULL,
                    0,
 #ifdef UNICODE
-                   (SQLWCHAR*)joinedTableTypeString.unicode(),
+                   toSQLTCHAR(joinedTableTypeString).data(),
 #else
-                   (SQLCHAR*)joinedTableTypeString.toLatin1().constData(),
+                   (SQLCHAR*)joinedTableTypeString.toUtf8().data(),
 #endif
                    joinedTableTypeString.length() /* characters, not bytes */);
 
@@ -2150,6 +2289,11 @@
     else
         r = SQLFetch(hStmt);
 
+    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
+        qWarning() << "QODBCDriver::tables failed to retrieve table/view list: (" << r << "," << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ")";
+        return QStringList();
+    }
+
     while (r == SQL_SUCCESS) {
         QString fieldVal = qGetStringData(hStmt, 2, -1, false);
         tl.append(fieldVal);
@@ -2208,21 +2352,21 @@
                         SQL_IS_UINTEGER);
     r = SQLPrimaryKeys(hStmt,
 #ifdef UNICODE
-                        catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+                        catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
 #else
-                        catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toLatin1().constData(),
+                        catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
 #endif
                         catalog.length(),
 #ifdef UNICODE
-                        schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+                        schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
 #else
-                        schema.length() == 0 ? NULL : (SQLCHAR*)schema.toLatin1().constData(),
+                        schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
 #endif
                         schema.length(),
 #ifdef UNICODE
-                        (SQLWCHAR*)table.unicode(),
+                        toSQLTCHAR(table).data(),
 #else
-                        (SQLCHAR*)table.toLatin1().constData(),
+                        (SQLCHAR*)table.toUtf8().data(),
 #endif
                         table.length() /* in characters, not in bytes */);
 
@@ -2233,21 +2377,21 @@
             r = SQLSpecialColumns(hStmt,
                         SQL_BEST_ROWID,
 #ifdef UNICODE
-                        catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+                        catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
 #else
-                        catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toLatin1().constData(),
+                        catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
 #endif
                         catalog.length(),
 #ifdef UNICODE
-                        schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+                        schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
 #else
-                        schema.length() == 0 ? NULL : (SQLCHAR*)schema.toLatin1().constData(),
+                        schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
 #endif
                         schema.length(),
 #ifdef UNICODE
-                        (SQLWCHAR*)table.unicode(),
+                        toSQLTCHAR(table).data(),
 #else
-                        (SQLCHAR*)table.toLatin1().constData(),
+                        (SQLCHAR*)table.toUtf8().data(),
 #endif
                         table.length(),
                         SQL_SCOPE_CURROW,
@@ -2333,21 +2477,21 @@
                         SQL_IS_UINTEGER);
     r =  SQLColumns(hStmt,
 #ifdef UNICODE
-                     catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+                     catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
 #else
-                     catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toLatin1().constData(),
+                     catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
 #endif
                      catalog.length(),
 #ifdef UNICODE
-                     schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+                     schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
 #else
-                     schema.length() == 0 ? NULL : (SQLCHAR*)schema.toLatin1().constData(),
+                     schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
 #endif
                      schema.length(),
 #ifdef UNICODE
-                     (SQLWCHAR*)table.unicode(),
+                     toSQLTCHAR(table).data(),
 #else
-                     (SQLCHAR*)table.toLatin1().constData(),
+                     (SQLCHAR*)table.toUtf8().data(),
 #endif
                      table.length(),
                      NULL,