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