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