|
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_db2.h" |
|
43 #include <qcoreapplication.h> |
|
44 #include <qdatetime.h> |
|
45 #include <qsqlfield.h> |
|
46 #include <qsqlerror.h> |
|
47 #include <qsqlindex.h> |
|
48 #include <qsqlrecord.h> |
|
49 #include <qstringlist.h> |
|
50 #include <qvarlengtharray.h> |
|
51 #include <qvector.h> |
|
52 #include <QDebug> |
|
53 |
|
54 #if defined(Q_CC_BOR) |
|
55 // DB2's sqlsystm.h (included through sqlcli1.h) defines the SQL_BIGINT_TYPE |
|
56 // and SQL_BIGUINT_TYPE to wrong the types for Borland; so do the defines to |
|
57 // the right type before including the header |
|
58 #define SQL_BIGINT_TYPE qint64 |
|
59 #define SQL_BIGUINT_TYPE quint64 |
|
60 #endif |
|
61 |
|
62 #define UNICODE |
|
63 |
|
64 #include <sqlcli1.h> |
|
65 |
|
66 #include <string.h> |
|
67 |
|
68 QT_BEGIN_NAMESPACE |
|
69 |
|
70 static const int COLNAMESIZE = 255; |
|
71 static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT }; |
|
72 |
|
73 class QDB2DriverPrivate |
|
74 { |
|
75 public: |
|
76 QDB2DriverPrivate(): hEnv(0), hDbc(0) {} |
|
77 SQLHANDLE hEnv; |
|
78 SQLHANDLE hDbc; |
|
79 QString user; |
|
80 }; |
|
81 |
|
82 class QDB2ResultPrivate |
|
83 { |
|
84 public: |
|
85 QDB2ResultPrivate(const QDB2DriverPrivate* d): dp(d), hStmt(0) |
|
86 {} |
|
87 ~QDB2ResultPrivate() |
|
88 { |
|
89 emptyValueCache(); |
|
90 } |
|
91 void clearValueCache() |
|
92 { |
|
93 for (int i = 0; i < valueCache.count(); ++i) { |
|
94 delete valueCache[i]; |
|
95 valueCache[i] = NULL; |
|
96 } |
|
97 } |
|
98 void emptyValueCache() |
|
99 { |
|
100 clearValueCache(); |
|
101 valueCache.clear(); |
|
102 } |
|
103 |
|
104 const QDB2DriverPrivate* dp; |
|
105 SQLHANDLE hStmt; |
|
106 QSqlRecord recInf; |
|
107 QVector<QVariant*> valueCache; |
|
108 }; |
|
109 |
|
110 static QString qFromTChar(SQLTCHAR* str) |
|
111 { |
|
112 return QString::fromUtf16(str); |
|
113 } |
|
114 |
|
115 // dangerous!! (but fast). Don't use in functions that |
|
116 // require out parameters! |
|
117 static SQLTCHAR* qToTChar(const QString& str) |
|
118 { |
|
119 return (SQLTCHAR*)str.utf16(); |
|
120 } |
|
121 |
|
122 static QString qWarnDB2Handle(int handleType, SQLHANDLE handle) |
|
123 { |
|
124 SQLINTEGER nativeCode; |
|
125 SQLSMALLINT msgLen; |
|
126 SQLRETURN r = SQL_ERROR; |
|
127 SQLTCHAR state[SQL_SQLSTATE_SIZE + 1]; |
|
128 SQLTCHAR description[SQL_MAX_MESSAGE_LENGTH]; |
|
129 r = SQLGetDiagRec(handleType, |
|
130 handle, |
|
131 1, |
|
132 (SQLTCHAR*) state, |
|
133 &nativeCode, |
|
134 (SQLTCHAR*) description, |
|
135 SQL_MAX_MESSAGE_LENGTH - 1, /* in bytes, not in characters */ |
|
136 &msgLen); |
|
137 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) |
|
138 return QString(qFromTChar(description)); |
|
139 return QString(); |
|
140 } |
|
141 |
|
142 static QString qDB2Warn(const QDB2DriverPrivate* d) |
|
143 { |
|
144 return (qWarnDB2Handle(SQL_HANDLE_ENV, d->hEnv) + QLatin1Char(' ') |
|
145 + qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc)); |
|
146 } |
|
147 |
|
148 static QString qDB2Warn(const QDB2ResultPrivate* d) |
|
149 { |
|
150 return (qWarnDB2Handle(SQL_HANDLE_ENV, d->dp->hEnv) + QLatin1Char(' ') |
|
151 + qWarnDB2Handle(SQL_HANDLE_DBC, d->dp->hDbc) |
|
152 + qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt)); |
|
153 } |
|
154 |
|
155 static void qSqlWarning(const QString& message, const QDB2DriverPrivate* d) |
|
156 { |
|
157 qWarning("%s\tError: %s", message.toLocal8Bit().constData(), |
|
158 qDB2Warn(d).toLocal8Bit().constData()); |
|
159 } |
|
160 |
|
161 static void qSqlWarning(const QString& message, const QDB2ResultPrivate* d) |
|
162 { |
|
163 qWarning("%s\tError: %s", message.toLocal8Bit().constData(), |
|
164 qDB2Warn(d).toLocal8Bit().constData()); |
|
165 } |
|
166 |
|
167 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, |
|
168 const QDB2DriverPrivate* p) |
|
169 { |
|
170 return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type); |
|
171 } |
|
172 |
|
173 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, |
|
174 const QDB2ResultPrivate* p) |
|
175 { |
|
176 return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type); |
|
177 } |
|
178 |
|
179 static QVariant::Type qDecodeDB2Type(SQLSMALLINT sqltype) |
|
180 { |
|
181 QVariant::Type type = QVariant::Invalid; |
|
182 switch (sqltype) { |
|
183 case SQL_REAL: |
|
184 case SQL_FLOAT: |
|
185 case SQL_DOUBLE: |
|
186 case SQL_DECIMAL: |
|
187 case SQL_NUMERIC: |
|
188 type = QVariant::Double; |
|
189 break; |
|
190 case SQL_SMALLINT: |
|
191 case SQL_INTEGER: |
|
192 case SQL_BIT: |
|
193 case SQL_TINYINT: |
|
194 type = QVariant::Int; |
|
195 break; |
|
196 case SQL_BIGINT: |
|
197 type = QVariant::LongLong; |
|
198 break; |
|
199 case SQL_BLOB: |
|
200 case SQL_BINARY: |
|
201 case SQL_VARBINARY: |
|
202 case SQL_LONGVARBINARY: |
|
203 case SQL_CLOB: |
|
204 case SQL_DBCLOB: |
|
205 type = QVariant::ByteArray; |
|
206 break; |
|
207 case SQL_DATE: |
|
208 case SQL_TYPE_DATE: |
|
209 type = QVariant::Date; |
|
210 break; |
|
211 case SQL_TIME: |
|
212 case SQL_TYPE_TIME: |
|
213 type = QVariant::Time; |
|
214 break; |
|
215 case SQL_TIMESTAMP: |
|
216 case SQL_TYPE_TIMESTAMP: |
|
217 type = QVariant::DateTime; |
|
218 break; |
|
219 case SQL_WCHAR: |
|
220 case SQL_WVARCHAR: |
|
221 case SQL_WLONGVARCHAR: |
|
222 case SQL_CHAR: |
|
223 case SQL_VARCHAR: |
|
224 case SQL_LONGVARCHAR: |
|
225 type = QVariant::String; |
|
226 break; |
|
227 default: |
|
228 type = QVariant::ByteArray; |
|
229 break; |
|
230 } |
|
231 return type; |
|
232 } |
|
233 |
|
234 static QSqlField qMakeFieldInfo(const QDB2ResultPrivate* d, int i) |
|
235 { |
|
236 SQLSMALLINT colNameLen; |
|
237 SQLSMALLINT colType; |
|
238 SQLUINTEGER colSize; |
|
239 SQLSMALLINT colScale; |
|
240 SQLSMALLINT nullable; |
|
241 SQLRETURN r = SQL_ERROR; |
|
242 SQLTCHAR colName[COLNAMESIZE]; |
|
243 r = SQLDescribeCol(d->hStmt, |
|
244 i+1, |
|
245 colName, |
|
246 (SQLSMALLINT) COLNAMESIZE, |
|
247 &colNameLen, |
|
248 &colType, |
|
249 &colSize, |
|
250 &colScale, |
|
251 &nullable); |
|
252 |
|
253 if (r != SQL_SUCCESS) { |
|
254 qSqlWarning(QString::fromLatin1("qMakeFieldInfo: Unable to describe column %1").arg(i), d); |
|
255 return QSqlField(); |
|
256 } |
|
257 QSqlField f(qFromTChar(colName), qDecodeDB2Type(colType)); |
|
258 // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN |
|
259 if (nullable == SQL_NO_NULLS) |
|
260 f.setRequired(true); |
|
261 else if (nullable == SQL_NULLABLE) |
|
262 f.setRequired(false); |
|
263 // else required is unknown |
|
264 f.setLength(colSize == 0 ? -1 : int(colSize)); |
|
265 f.setPrecision(colScale == 0 ? -1 : int(colScale)); |
|
266 f.setSqlType(int(colType)); |
|
267 return f; |
|
268 } |
|
269 |
|
270 static int qGetIntData(SQLHANDLE hStmt, int column, bool& isNull) |
|
271 { |
|
272 SQLINTEGER intbuf; |
|
273 isNull = false; |
|
274 SQLINTEGER lengthIndicator = 0; |
|
275 SQLRETURN r = SQLGetData(hStmt, |
|
276 column + 1, |
|
277 SQL_C_SLONG, |
|
278 (SQLPOINTER) &intbuf, |
|
279 0, |
|
280 &lengthIndicator); |
|
281 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) { |
|
282 isNull = true; |
|
283 return 0; |
|
284 } |
|
285 return int(intbuf); |
|
286 } |
|
287 |
|
288 static double qGetDoubleData(SQLHANDLE hStmt, int column, bool& isNull) |
|
289 { |
|
290 SQLDOUBLE dblbuf; |
|
291 isNull = false; |
|
292 SQLINTEGER lengthIndicator = 0; |
|
293 SQLRETURN r = SQLGetData(hStmt, |
|
294 column+1, |
|
295 SQL_C_DOUBLE, |
|
296 (SQLPOINTER) &dblbuf, |
|
297 0, |
|
298 &lengthIndicator); |
|
299 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) { |
|
300 isNull = true; |
|
301 return 0.0; |
|
302 } |
|
303 |
|
304 return (double) dblbuf; |
|
305 } |
|
306 |
|
307 static SQLBIGINT qGetBigIntData(SQLHANDLE hStmt, int column, bool& isNull) |
|
308 { |
|
309 SQLBIGINT lngbuf = Q_INT64_C(0); |
|
310 isNull = false; |
|
311 SQLINTEGER lengthIndicator = 0; |
|
312 SQLRETURN r = SQLGetData(hStmt, |
|
313 column+1, |
|
314 SQL_C_SBIGINT, |
|
315 (SQLPOINTER) &lngbuf, |
|
316 0, |
|
317 &lengthIndicator); |
|
318 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) |
|
319 isNull = true; |
|
320 |
|
321 return lngbuf; |
|
322 } |
|
323 |
|
324 static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool& isNull) |
|
325 { |
|
326 QString fieldVal; |
|
327 SQLRETURN r = SQL_ERROR; |
|
328 SQLINTEGER lengthIndicator = 0; |
|
329 |
|
330 if (colSize <= 0) |
|
331 colSize = 255; |
|
332 else if (colSize > 65536) // limit buffer size to 64 KB |
|
333 colSize = 65536; |
|
334 else |
|
335 colSize++; // make sure there is room for more than the 0 termination |
|
336 SQLTCHAR* buf = new SQLTCHAR[colSize]; |
|
337 |
|
338 while (true) { |
|
339 r = SQLGetData(hStmt, |
|
340 column + 1, |
|
341 SQL_C_WCHAR, |
|
342 (SQLPOINTER)buf, |
|
343 colSize * sizeof(SQLTCHAR), |
|
344 &lengthIndicator); |
|
345 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { |
|
346 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) { |
|
347 fieldVal.clear(); |
|
348 isNull = true; |
|
349 break; |
|
350 } |
|
351 fieldVal += qFromTChar(buf); |
|
352 } else if (r == SQL_NO_DATA) { |
|
353 break; |
|
354 } else { |
|
355 qWarning("qGetStringData: Error while fetching data (%d)", r); |
|
356 fieldVal.clear(); |
|
357 break; |
|
358 } |
|
359 } |
|
360 delete[] buf; |
|
361 return fieldVal; |
|
362 } |
|
363 |
|
364 static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLINTEGER& lengthIndicator, bool& isNull) |
|
365 { |
|
366 QByteArray fieldVal; |
|
367 SQLSMALLINT colNameLen; |
|
368 SQLSMALLINT colType; |
|
369 SQLUINTEGER colSize; |
|
370 SQLSMALLINT colScale; |
|
371 SQLSMALLINT nullable; |
|
372 SQLRETURN r = SQL_ERROR; |
|
373 |
|
374 SQLTCHAR colName[COLNAMESIZE]; |
|
375 r = SQLDescribeCol(hStmt, |
|
376 column+1, |
|
377 colName, |
|
378 COLNAMESIZE, |
|
379 &colNameLen, |
|
380 &colType, |
|
381 &colSize, |
|
382 &colScale, |
|
383 &nullable); |
|
384 if (r != SQL_SUCCESS) |
|
385 qWarning("qGetBinaryData: Unable to describe column %d", column); |
|
386 // SQLDescribeCol may return 0 if size cannot be determined |
|
387 if (!colSize) |
|
388 colSize = 255; |
|
389 else if (colSize > 65536) // read the field in 64 KB chunks |
|
390 colSize = 65536; |
|
391 char * buf = new char[colSize]; |
|
392 while (true) { |
|
393 r = SQLGetData(hStmt, |
|
394 column+1, |
|
395 colType == SQL_DBCLOB ? SQL_C_CHAR : SQL_C_BINARY, |
|
396 (SQLPOINTER) buf, |
|
397 colSize, |
|
398 &lengthIndicator); |
|
399 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { |
|
400 if (lengthIndicator == SQL_NULL_DATA) { |
|
401 isNull = true; |
|
402 break; |
|
403 } else { |
|
404 int rSize; |
|
405 r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize; |
|
406 if (lengthIndicator == SQL_NO_TOTAL) // size cannot be determined |
|
407 rSize = colSize; |
|
408 fieldVal.append(QByteArray(buf, rSize)); |
|
409 if (r == SQL_SUCCESS) // the whole field was read in one chunk |
|
410 break; |
|
411 } |
|
412 } else { |
|
413 break; |
|
414 } |
|
415 } |
|
416 delete [] buf; |
|
417 return fieldVal; |
|
418 } |
|
419 |
|
420 static void qSplitTableQualifier(const QString & qualifier, QString * catalog, |
|
421 QString * schema, QString * table) |
|
422 { |
|
423 if (!catalog || !schema || !table) |
|
424 return; |
|
425 QStringList l = qualifier.split(QLatin1Char('.')); |
|
426 if (l.count() > 3) |
|
427 return; // can't possibly be a valid table qualifier |
|
428 int i = 0, n = l.count(); |
|
429 if (n == 1) { |
|
430 *table = qualifier; |
|
431 } else { |
|
432 for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { |
|
433 if (n == 3) { |
|
434 if (i == 0) |
|
435 *catalog = *it; |
|
436 else if (i == 1) |
|
437 *schema = *it; |
|
438 else if (i == 2) |
|
439 *table = *it; |
|
440 } else if (n == 2) { |
|
441 if (i == 0) |
|
442 *schema = *it; |
|
443 else if (i == 1) |
|
444 *table = *it; |
|
445 } |
|
446 i++; |
|
447 } |
|
448 } |
|
449 } |
|
450 |
|
451 // creates a QSqlField from a valid hStmt generated |
|
452 // by SQLColumns. The hStmt has to point to a valid position. |
|
453 static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt) |
|
454 { |
|
455 bool isNull; |
|
456 int type = qGetIntData(hStmt, 4, isNull); |
|
457 QSqlField f(qGetStringData(hStmt, 3, -1, isNull), qDecodeDB2Type(type)); |
|
458 int required = qGetIntData(hStmt, 10, isNull); // nullable-flag |
|
459 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN |
|
460 if (required == SQL_NO_NULLS) |
|
461 f.setRequired(true); |
|
462 else if (required == SQL_NULLABLE) |
|
463 f.setRequired(false); |
|
464 // else we don't know. |
|
465 f.setLength(qGetIntData(hStmt, 6, isNull)); // column size |
|
466 f.setPrecision(qGetIntData(hStmt, 8, isNull)); // precision |
|
467 f.setSqlType(type); |
|
468 return f; |
|
469 } |
|
470 |
|
471 static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwardOnly = true) |
|
472 { |
|
473 SQLRETURN r; |
|
474 if (!d->hStmt) { |
|
475 r = SQLAllocHandle(SQL_HANDLE_STMT, |
|
476 d->dp->hDbc, |
|
477 &d->hStmt); |
|
478 if (r != SQL_SUCCESS) { |
|
479 qSqlWarning(QLatin1String("QDB2Result::reset: Unable to allocate statement handle"), d); |
|
480 return false; |
|
481 } |
|
482 } else { |
|
483 r = SQLFreeStmt(d->hStmt, SQL_CLOSE); |
|
484 if (r != SQL_SUCCESS) { |
|
485 qSqlWarning(QLatin1String("QDB2Result::reset: Unable to close statement handle"), d); |
|
486 return false; |
|
487 } |
|
488 } |
|
489 |
|
490 if (!setForwardOnly) |
|
491 return true; |
|
492 |
|
493 if (forwardOnly) { |
|
494 r = SQLSetStmtAttr(d->hStmt, |
|
495 SQL_ATTR_CURSOR_TYPE, |
|
496 (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY, |
|
497 SQL_IS_UINTEGER); |
|
498 } else { |
|
499 r = SQLSetStmtAttr(d->hStmt, |
|
500 SQL_ATTR_CURSOR_TYPE, |
|
501 (SQLPOINTER) SQL_CURSOR_STATIC, |
|
502 SQL_IS_UINTEGER); |
|
503 } |
|
504 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
505 qSqlWarning(QString::fromLatin1("QDB2Result::reset: Unable to set %1 attribute.").arg( |
|
506 forwardOnly ? QLatin1String("SQL_CURSOR_FORWARD_ONLY") |
|
507 : QLatin1String("SQL_CURSOR_STATIC")), d); |
|
508 return false; |
|
509 } |
|
510 return true; |
|
511 } |
|
512 |
|
513 QVariant QDB2Result::handle() const |
|
514 { |
|
515 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt); |
|
516 } |
|
517 |
|
518 /************************************/ |
|
519 |
|
520 QDB2Result::QDB2Result(const QDB2Driver* dr, const QDB2DriverPrivate* dp) |
|
521 : QSqlResult(dr) |
|
522 { |
|
523 d = new QDB2ResultPrivate(dp); |
|
524 } |
|
525 |
|
526 QDB2Result::~QDB2Result() |
|
527 { |
|
528 if (d->hStmt) { |
|
529 SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt); |
|
530 if (r != SQL_SUCCESS) |
|
531 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ") |
|
532 + QString::number(r), d); |
|
533 } |
|
534 delete d; |
|
535 } |
|
536 |
|
537 bool QDB2Result::reset (const QString& query) |
|
538 { |
|
539 setActive(false); |
|
540 setAt(QSql::BeforeFirstRow); |
|
541 SQLRETURN r; |
|
542 |
|
543 d->recInf.clear(); |
|
544 d->emptyValueCache(); |
|
545 |
|
546 if (!qMakeStatement(d, isForwardOnly())) |
|
547 return false; |
|
548 |
|
549 r = SQLExecDirect(d->hStmt, |
|
550 qToTChar(query), |
|
551 (SQLINTEGER) query.length()); |
|
552 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
553 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", |
|
554 "Unable to execute statement"), QSqlError::StatementError, d)); |
|
555 return false; |
|
556 } |
|
557 SQLSMALLINT count; |
|
558 r = SQLNumResultCols(d->hStmt, &count); |
|
559 if (count) { |
|
560 setSelect(true); |
|
561 for (int i = 0; i < count; ++i) { |
|
562 d->recInf.append(qMakeFieldInfo(d, i)); |
|
563 } |
|
564 } else { |
|
565 setSelect(false); |
|
566 } |
|
567 d->valueCache.resize(count); |
|
568 d->valueCache.fill(NULL); |
|
569 setActive(true); |
|
570 return true; |
|
571 } |
|
572 |
|
573 bool QDB2Result::prepare(const QString& query) |
|
574 { |
|
575 setActive(false); |
|
576 setAt(QSql::BeforeFirstRow); |
|
577 SQLRETURN r; |
|
578 |
|
579 d->recInf.clear(); |
|
580 d->emptyValueCache(); |
|
581 |
|
582 if (!qMakeStatement(d, isForwardOnly())) |
|
583 return false; |
|
584 |
|
585 r = SQLPrepare(d->hStmt, |
|
586 qToTChar(query), |
|
587 (SQLINTEGER) query.length()); |
|
588 |
|
589 if (r != SQL_SUCCESS) { |
|
590 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", |
|
591 "Unable to prepare statement"), QSqlError::StatementError, d)); |
|
592 return false; |
|
593 } |
|
594 return true; |
|
595 } |
|
596 |
|
597 bool QDB2Result::exec() |
|
598 { |
|
599 QList<QByteArray> tmpStorage; // holds temporary ptrs |
|
600 QVarLengthArray<SQLINTEGER, 32> indicators(boundValues().count()); |
|
601 |
|
602 memset(indicators.data(), 0, indicators.size() * sizeof(SQLINTEGER)); |
|
603 setActive(false); |
|
604 setAt(QSql::BeforeFirstRow); |
|
605 SQLRETURN r; |
|
606 |
|
607 d->recInf.clear(); |
|
608 d->emptyValueCache(); |
|
609 |
|
610 if (!qMakeStatement(d, isForwardOnly(), false)) |
|
611 return false; |
|
612 |
|
613 |
|
614 QVector<QVariant> &values = boundValues(); |
|
615 int i; |
|
616 for (i = 0; i < values.count(); ++i) { |
|
617 // bind parameters - only positional binding allowed |
|
618 SQLINTEGER *ind = &indicators[i]; |
|
619 if (values.at(i).isNull()) |
|
620 *ind = SQL_NULL_DATA; |
|
621 if (bindValueType(i) & QSql::Out) |
|
622 values[i].detach(); |
|
623 |
|
624 switch (values.at(i).type()) { |
|
625 case QVariant::Date: { |
|
626 QByteArray ba; |
|
627 ba.resize(sizeof(DATE_STRUCT)); |
|
628 DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData(); |
|
629 QDate qdt = values.at(i).toDate(); |
|
630 dt->year = qdt.year(); |
|
631 dt->month = qdt.month(); |
|
632 dt->day = qdt.day(); |
|
633 r = SQLBindParameter(d->hStmt, |
|
634 i + 1, |
|
635 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
636 SQL_C_DATE, |
|
637 SQL_DATE, |
|
638 0, |
|
639 0, |
|
640 (void *) dt, |
|
641 0, |
|
642 *ind == SQL_NULL_DATA ? ind : NULL); |
|
643 tmpStorage.append(ba); |
|
644 break; } |
|
645 case QVariant::Time: { |
|
646 QByteArray ba; |
|
647 ba.resize(sizeof(TIME_STRUCT)); |
|
648 TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData(); |
|
649 QTime qdt = values.at(i).toTime(); |
|
650 dt->hour = qdt.hour(); |
|
651 dt->minute = qdt.minute(); |
|
652 dt->second = qdt.second(); |
|
653 r = SQLBindParameter(d->hStmt, |
|
654 i + 1, |
|
655 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
656 SQL_C_TIME, |
|
657 SQL_TIME, |
|
658 0, |
|
659 0, |
|
660 (void *) dt, |
|
661 0, |
|
662 *ind == SQL_NULL_DATA ? ind : NULL); |
|
663 tmpStorage.append(ba); |
|
664 break; } |
|
665 case QVariant::DateTime: { |
|
666 QByteArray ba; |
|
667 ba.resize(sizeof(TIMESTAMP_STRUCT)); |
|
668 TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData(); |
|
669 QDateTime qdt = values.at(i).toDateTime(); |
|
670 dt->year = qdt.date().year(); |
|
671 dt->month = qdt.date().month(); |
|
672 dt->day = qdt.date().day(); |
|
673 dt->hour = qdt.time().hour(); |
|
674 dt->minute = qdt.time().minute(); |
|
675 dt->second = qdt.time().second(); |
|
676 dt->fraction = qdt.time().msec() * 1000000; |
|
677 r = SQLBindParameter(d->hStmt, |
|
678 i + 1, |
|
679 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
680 SQL_C_TIMESTAMP, |
|
681 SQL_TIMESTAMP, |
|
682 0, |
|
683 0, |
|
684 (void *) dt, |
|
685 0, |
|
686 *ind == SQL_NULL_DATA ? ind : NULL); |
|
687 tmpStorage.append(ba); |
|
688 break; } |
|
689 case QVariant::Int: |
|
690 r = SQLBindParameter(d->hStmt, |
|
691 i + 1, |
|
692 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
693 SQL_C_SLONG, |
|
694 SQL_INTEGER, |
|
695 0, |
|
696 0, |
|
697 (void *)values.at(i).constData(), |
|
698 0, |
|
699 *ind == SQL_NULL_DATA ? ind : NULL); |
|
700 break; |
|
701 case QVariant::Double: |
|
702 r = SQLBindParameter(d->hStmt, |
|
703 i + 1, |
|
704 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
705 SQL_C_DOUBLE, |
|
706 SQL_DOUBLE, |
|
707 0, |
|
708 0, |
|
709 (void *)values.at(i).constData(), |
|
710 0, |
|
711 *ind == SQL_NULL_DATA ? ind : NULL); |
|
712 break; |
|
713 case QVariant::ByteArray: { |
|
714 int len = values.at(i).toByteArray().size(); |
|
715 if (*ind != SQL_NULL_DATA) |
|
716 *ind = len; |
|
717 r = SQLBindParameter(d->hStmt, |
|
718 i + 1, |
|
719 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
720 SQL_C_BINARY, |
|
721 SQL_LONGVARBINARY, |
|
722 len, |
|
723 0, |
|
724 (void *)values.at(i).toByteArray().constData(), |
|
725 len, |
|
726 ind); |
|
727 break; } |
|
728 case QVariant::String: |
|
729 { |
|
730 QString str(values.at(i).toString()); |
|
731 if (*ind != SQL_NULL_DATA) |
|
732 *ind = str.length() * sizeof(QChar); |
|
733 if (bindValueType(i) & QSql::Out) { |
|
734 QByteArray ba((char*)str.utf16(), str.capacity() * sizeof(QChar)); |
|
735 r = SQLBindParameter(d->hStmt, |
|
736 i + 1, |
|
737 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
738 SQL_C_WCHAR, |
|
739 SQL_WVARCHAR, |
|
740 str.length(), |
|
741 0, |
|
742 (void *)ba.constData(), |
|
743 ba.size(), |
|
744 ind); |
|
745 tmpStorage.append(ba); |
|
746 } else { |
|
747 void *data = (void*)str.utf16(); |
|
748 int len = str.length(); |
|
749 r = SQLBindParameter(d->hStmt, |
|
750 i + 1, |
|
751 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
752 SQL_C_WCHAR, |
|
753 SQL_WVARCHAR, |
|
754 len, |
|
755 0, |
|
756 data, |
|
757 len * sizeof(QChar), |
|
758 ind); |
|
759 } |
|
760 break; |
|
761 } |
|
762 default: { |
|
763 QByteArray ba = values.at(i).toString().toAscii(); |
|
764 int len = ba.length() + 1; |
|
765 if (*ind != SQL_NULL_DATA) |
|
766 *ind = ba.length(); |
|
767 r = SQLBindParameter(d->hStmt, |
|
768 i + 1, |
|
769 qParamType[(QFlag)(bindValueType(i)) & 3], |
|
770 SQL_C_CHAR, |
|
771 SQL_VARCHAR, |
|
772 len, |
|
773 0, |
|
774 (void *) ba.constData(), |
|
775 len, |
|
776 ind); |
|
777 tmpStorage.append(ba); |
|
778 break; } |
|
779 } |
|
780 if (r != SQL_SUCCESS) { |
|
781 qWarning("QDB2Result::exec: unable to bind variable: %s", |
|
782 qDB2Warn(d).toLocal8Bit().constData()); |
|
783 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", |
|
784 "Unable to bind variable"), QSqlError::StatementError, d)); |
|
785 return false; |
|
786 } |
|
787 } |
|
788 |
|
789 r = SQLExecute(d->hStmt); |
|
790 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
791 qWarning("QDB2Result::exec: Unable to execute statement: %s", |
|
792 qDB2Warn(d).toLocal8Bit().constData()); |
|
793 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", |
|
794 "Unable to execute statement"), QSqlError::StatementError, d)); |
|
795 return false; |
|
796 } |
|
797 SQLSMALLINT count; |
|
798 r = SQLNumResultCols(d->hStmt, &count); |
|
799 if (count) { |
|
800 setSelect(true); |
|
801 for (int i = 0; i < count; ++i) { |
|
802 d->recInf.append(qMakeFieldInfo(d, i)); |
|
803 } |
|
804 } else { |
|
805 setSelect(false); |
|
806 } |
|
807 setActive(true); |
|
808 d->valueCache.resize(count); |
|
809 d->valueCache.fill(NULL); |
|
810 |
|
811 //get out parameters |
|
812 if (!hasOutValues()) |
|
813 return true; |
|
814 |
|
815 for (i = 0; i < values.count(); ++i) { |
|
816 switch (values[i].type()) { |
|
817 case QVariant::Date: { |
|
818 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData()); |
|
819 values[i] = QVariant(QDate(ds.year, ds.month, ds.day)); |
|
820 break; } |
|
821 case QVariant::Time: { |
|
822 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData()); |
|
823 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second)); |
|
824 break; } |
|
825 case QVariant::DateTime: { |
|
826 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT *)tmpStorage.takeFirst().constData()); |
|
827 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day), |
|
828 QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000))); |
|
829 break; } |
|
830 case QVariant::Int: |
|
831 case QVariant::Double: |
|
832 case QVariant::ByteArray: |
|
833 break; |
|
834 case QVariant::String: |
|
835 if (bindValueType(i) & QSql::Out) |
|
836 values[i] = QString::fromUtf16((ushort*)tmpStorage.takeFirst().constData()); |
|
837 break; |
|
838 default: { |
|
839 values[i] = QString::fromAscii(tmpStorage.takeFirst().constData()); |
|
840 break; } |
|
841 } |
|
842 if (indicators[i] == SQL_NULL_DATA) |
|
843 values[i] = QVariant(values[i].type()); |
|
844 } |
|
845 return true; |
|
846 } |
|
847 |
|
848 bool QDB2Result::fetch(int i) |
|
849 { |
|
850 if (isForwardOnly() && i < at()) |
|
851 return false; |
|
852 if (i == at()) |
|
853 return true; |
|
854 d->clearValueCache(); |
|
855 int actualIdx = i + 1; |
|
856 if (actualIdx <= 0) { |
|
857 setAt(QSql::BeforeFirstRow); |
|
858 return false; |
|
859 } |
|
860 SQLRETURN r; |
|
861 if (isForwardOnly()) { |
|
862 bool ok = true; |
|
863 while (ok && i > at()) |
|
864 ok = fetchNext(); |
|
865 return ok; |
|
866 } else { |
|
867 r = SQLFetchScroll(d->hStmt, |
|
868 SQL_FETCH_ABSOLUTE, |
|
869 actualIdx); |
|
870 } |
|
871 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) { |
|
872 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", |
|
873 "Unable to fetch record %1").arg(i), QSqlError::StatementError, d)); |
|
874 return false; |
|
875 } |
|
876 else if (r == SQL_NO_DATA) |
|
877 return false; |
|
878 setAt(i); |
|
879 return true; |
|
880 } |
|
881 |
|
882 bool QDB2Result::fetchNext() |
|
883 { |
|
884 SQLRETURN r; |
|
885 d->clearValueCache(); |
|
886 r = SQLFetchScroll(d->hStmt, |
|
887 SQL_FETCH_NEXT, |
|
888 0); |
|
889 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
890 if (r != SQL_NO_DATA) |
|
891 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", |
|
892 "Unable to fetch next"), QSqlError::StatementError, d)); |
|
893 return false; |
|
894 } |
|
895 setAt(at() + 1); |
|
896 return true; |
|
897 } |
|
898 |
|
899 bool QDB2Result::fetchFirst() |
|
900 { |
|
901 if (isForwardOnly() && at() != QSql::BeforeFirstRow) |
|
902 return false; |
|
903 if (isForwardOnly()) |
|
904 return fetchNext(); |
|
905 d->clearValueCache(); |
|
906 SQLRETURN r; |
|
907 r = SQLFetchScroll(d->hStmt, |
|
908 SQL_FETCH_FIRST, |
|
909 0); |
|
910 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
911 if(r!= SQL_NO_DATA) |
|
912 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", "Unable to fetch first"), |
|
913 QSqlError::StatementError, d)); |
|
914 return false; |
|
915 } |
|
916 setAt(0); |
|
917 return true; |
|
918 } |
|
919 |
|
920 bool QDB2Result::fetchLast() |
|
921 { |
|
922 d->clearValueCache(); |
|
923 |
|
924 int i = at(); |
|
925 if (i == QSql::AfterLastRow) { |
|
926 if (isForwardOnly()) { |
|
927 return false; |
|
928 } else { |
|
929 if (!fetch(0)) |
|
930 return false; |
|
931 i = at(); |
|
932 } |
|
933 } |
|
934 |
|
935 while (fetchNext()) |
|
936 ++i; |
|
937 |
|
938 if (i == QSql::BeforeFirstRow) { |
|
939 setAt(QSql::AfterLastRow); |
|
940 return false; |
|
941 } |
|
942 |
|
943 if (!isForwardOnly()) |
|
944 return fetch(i); |
|
945 |
|
946 setAt(i); |
|
947 return true; |
|
948 } |
|
949 |
|
950 |
|
951 QVariant QDB2Result::data(int field) |
|
952 { |
|
953 if (field >= d->recInf.count()) { |
|
954 qWarning("QDB2Result::data: column %d out of range", field); |
|
955 return QVariant(); |
|
956 } |
|
957 SQLRETURN r = 0; |
|
958 SQLINTEGER lengthIndicator = 0; |
|
959 bool isNull = false; |
|
960 const QSqlField info = d->recInf.field(field); |
|
961 |
|
962 if (!info.isValid() || field >= d->valueCache.size()) |
|
963 return QVariant(); |
|
964 |
|
965 if (d->valueCache[field]) |
|
966 return *d->valueCache[field]; |
|
967 |
|
968 |
|
969 QVariant* v = 0; |
|
970 switch (info.type()) { |
|
971 case QVariant::LongLong: |
|
972 v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull)); |
|
973 break; |
|
974 case QVariant::Int: |
|
975 v = new QVariant(qGetIntData(d->hStmt, field, isNull)); |
|
976 break; |
|
977 case QVariant::Date: { |
|
978 DATE_STRUCT dbuf; |
|
979 r = SQLGetData(d->hStmt, |
|
980 field + 1, |
|
981 SQL_C_DATE, |
|
982 (SQLPOINTER) &dbuf, |
|
983 0, |
|
984 &lengthIndicator); |
|
985 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) { |
|
986 v = new QVariant(QDate(dbuf.year, dbuf.month, dbuf.day)); |
|
987 } else { |
|
988 v = new QVariant(QDate()); |
|
989 isNull = true; |
|
990 } |
|
991 break; } |
|
992 case QVariant::Time: { |
|
993 TIME_STRUCT tbuf; |
|
994 r = SQLGetData(d->hStmt, |
|
995 field + 1, |
|
996 SQL_C_TIME, |
|
997 (SQLPOINTER) &tbuf, |
|
998 0, |
|
999 &lengthIndicator); |
|
1000 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) { |
|
1001 v = new QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second)); |
|
1002 } else { |
|
1003 v = new QVariant(QTime()); |
|
1004 isNull = true; |
|
1005 } |
|
1006 break; } |
|
1007 case QVariant::DateTime: { |
|
1008 TIMESTAMP_STRUCT dtbuf; |
|
1009 r = SQLGetData(d->hStmt, |
|
1010 field + 1, |
|
1011 SQL_C_TIMESTAMP, |
|
1012 (SQLPOINTER) &dtbuf, |
|
1013 0, |
|
1014 &lengthIndicator); |
|
1015 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) { |
|
1016 v = new QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day), |
|
1017 QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000))); |
|
1018 } else { |
|
1019 v = new QVariant(QDateTime()); |
|
1020 isNull = true; |
|
1021 } |
|
1022 break; } |
|
1023 case QVariant::ByteArray: |
|
1024 v = new QVariant(qGetBinaryData(d->hStmt, field, lengthIndicator, isNull)); |
|
1025 break; |
|
1026 case QVariant::Double: |
|
1027 { |
|
1028 QString value=qGetStringData(d->hStmt, field, info.length() + 1, isNull); |
|
1029 switch(numericalPrecisionPolicy()) { |
|
1030 case QSql::LowPrecisionInt32: |
|
1031 v = new QVariant(qGetIntData(d->hStmt, field, isNull)); |
|
1032 break; |
|
1033 case QSql::LowPrecisionInt64: |
|
1034 v = new QVariant(qGetBigIntData(d->hStmt, field, isNull)); |
|
1035 break; |
|
1036 case QSql::LowPrecisionDouble: |
|
1037 v = new QVariant(qGetDoubleData(d->hStmt, field, isNull)); |
|
1038 break; |
|
1039 case QSql::HighPrecision: |
|
1040 default: |
|
1041 // length + 1 for the comma |
|
1042 v = new QVariant(qGetStringData(d->hStmt, field, info.length() + 1, isNull)); |
|
1043 break; |
|
1044 } |
|
1045 break; |
|
1046 } |
|
1047 case QVariant::String: |
|
1048 default: |
|
1049 v = new QVariant(qGetStringData(d->hStmt, field, info.length(), isNull)); |
|
1050 break; |
|
1051 } |
|
1052 if (isNull) |
|
1053 *v = QVariant(info.type()); |
|
1054 d->valueCache[field] = v; |
|
1055 return *v; |
|
1056 } |
|
1057 |
|
1058 bool QDB2Result::isNull(int i) |
|
1059 { |
|
1060 if (i >= d->valueCache.size()) |
|
1061 return true; |
|
1062 |
|
1063 if (d->valueCache[i]) |
|
1064 return d->valueCache[i]->isNull(); |
|
1065 return data(i).isNull(); |
|
1066 } |
|
1067 |
|
1068 int QDB2Result::numRowsAffected() |
|
1069 { |
|
1070 SQLINTEGER affectedRowCount = 0; |
|
1071 SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount); |
|
1072 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) |
|
1073 return affectedRowCount; |
|
1074 else |
|
1075 qSqlWarning(QLatin1String("QDB2Result::numRowsAffected: Unable to count affected rows"), d); |
|
1076 return -1; |
|
1077 } |
|
1078 |
|
1079 int QDB2Result::size() |
|
1080 { |
|
1081 return -1; |
|
1082 } |
|
1083 |
|
1084 QSqlRecord QDB2Result::record() const |
|
1085 { |
|
1086 if (isActive()) |
|
1087 return d->recInf; |
|
1088 return QSqlRecord(); |
|
1089 } |
|
1090 |
|
1091 bool QDB2Result::nextResult() |
|
1092 { |
|
1093 setActive(false); |
|
1094 setAt(QSql::BeforeFirstRow); |
|
1095 d->recInf.clear(); |
|
1096 d->emptyValueCache(); |
|
1097 setSelect(false); |
|
1098 |
|
1099 SQLRETURN r = SQLMoreResults(d->hStmt); |
|
1100 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
1101 if (r != SQL_NO_DATA) { |
|
1102 setLastError(qMakeError(QCoreApplication::translate("QODBCResult", |
|
1103 "Unable to fetch last"), QSqlError::ConnectionError, d)); |
|
1104 } |
|
1105 return false; |
|
1106 } |
|
1107 |
|
1108 SQLSMALLINT fieldCount; |
|
1109 r = SQLNumResultCols(d->hStmt, &fieldCount); |
|
1110 setSelect(fieldCount > 0); |
|
1111 for (int i = 0; i < fieldCount; ++i) |
|
1112 d->recInf.append(qMakeFieldInfo(d, i)); |
|
1113 |
|
1114 d->valueCache.resize(fieldCount); |
|
1115 d->valueCache.fill(NULL); |
|
1116 setActive(true); |
|
1117 |
|
1118 return true; |
|
1119 } |
|
1120 |
|
1121 void QDB2Result::virtual_hook(int id, void *data) |
|
1122 { |
|
1123 switch (id) { |
|
1124 case QSqlResult::NextResult: |
|
1125 Q_ASSERT(data); |
|
1126 *static_cast<bool*>(data) = nextResult(); |
|
1127 break; |
|
1128 case QSqlResult::DetachFromResultSet: |
|
1129 if (d->hStmt) |
|
1130 SQLCloseCursor(d->hStmt); |
|
1131 break; |
|
1132 default: |
|
1133 QSqlResult::virtual_hook(id, data); |
|
1134 } |
|
1135 } |
|
1136 |
|
1137 /************************************/ |
|
1138 |
|
1139 QDB2Driver::QDB2Driver(QObject* parent) |
|
1140 : QSqlDriver(parent) |
|
1141 { |
|
1142 d = new QDB2DriverPrivate; |
|
1143 } |
|
1144 |
|
1145 QDB2Driver::QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent) |
|
1146 : QSqlDriver(parent) |
|
1147 { |
|
1148 d = new QDB2DriverPrivate; |
|
1149 d->hEnv = (SQLHANDLE)env; |
|
1150 d->hDbc = (SQLHANDLE)con; |
|
1151 if (env && con) { |
|
1152 setOpen(true); |
|
1153 setOpenError(false); |
|
1154 } |
|
1155 } |
|
1156 |
|
1157 QDB2Driver::~QDB2Driver() |
|
1158 { |
|
1159 close(); |
|
1160 delete d; |
|
1161 } |
|
1162 |
|
1163 bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString& host, int port, |
|
1164 const QString& connOpts) |
|
1165 { |
|
1166 if (isOpen()) |
|
1167 close(); |
|
1168 SQLRETURN r; |
|
1169 r = SQLAllocHandle(SQL_HANDLE_ENV, |
|
1170 SQL_NULL_HANDLE, |
|
1171 &d->hEnv); |
|
1172 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
1173 qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate environment"), d); |
|
1174 setOpenError(true); |
|
1175 return false; |
|
1176 } |
|
1177 |
|
1178 r = SQLAllocHandle(SQL_HANDLE_DBC, |
|
1179 d->hEnv, |
|
1180 &d->hDbc); |
|
1181 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
1182 qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate connection"), d); |
|
1183 setOpenError(true); |
|
1184 return false; |
|
1185 } |
|
1186 |
|
1187 QString protocol; |
|
1188 // Set connection attributes |
|
1189 const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts)); |
|
1190 for (int i = 0; i < opts.count(); ++i) { |
|
1191 const QString tmp(opts.at(i)); |
|
1192 int idx; |
|
1193 if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) { |
|
1194 qWarning("QDB2Driver::open: Illegal connect option value '%s'", |
|
1195 tmp.toLocal8Bit().constData()); |
|
1196 continue; |
|
1197 } |
|
1198 |
|
1199 const QString opt(tmp.left(idx)); |
|
1200 const QString val(tmp.mid(idx + 1).simplified()); |
|
1201 |
|
1202 SQLUINTEGER v = 0; |
|
1203 r = SQL_SUCCESS; |
|
1204 if (opt == QLatin1String("SQL_ATTR_ACCESS_MODE")) { |
|
1205 if (val == QLatin1String("SQL_MODE_READ_ONLY")) { |
|
1206 v = SQL_MODE_READ_ONLY; |
|
1207 } else if (val == QLatin1String("SQL_MODE_READ_WRITE")) { |
|
1208 v = SQL_MODE_READ_WRITE; |
|
1209 } else { |
|
1210 qWarning("QDB2Driver::open: Unknown option value '%s'", |
|
1211 tmp.toLocal8Bit().constData()); |
|
1212 continue; |
|
1213 } |
|
1214 r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0); |
|
1215 } else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) { |
|
1216 v = val.toUInt(); |
|
1217 r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0); |
|
1218 } else if (opt.compare(QLatin1String("PROTOCOL"), Qt::CaseInsensitive) == 0) { |
|
1219 protocol = tmp; |
|
1220 } |
|
1221 else { |
|
1222 qWarning("QDB2Driver::open: Unknown connection attribute '%s'", |
|
1223 tmp.toLocal8Bit().constData()); |
|
1224 } |
|
1225 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) |
|
1226 qSqlWarning(QString::fromLatin1("QDB2Driver::open: " |
|
1227 "Unable to set connection attribute '%1'").arg(opt), d); |
|
1228 } |
|
1229 |
|
1230 if (protocol.isEmpty()) |
|
1231 protocol = QLatin1String("PROTOCOL=TCPIP"); |
|
1232 |
|
1233 if (port < 0 ) |
|
1234 port = 50000; |
|
1235 |
|
1236 QString connQStr; |
|
1237 connQStr = protocol + QLatin1String(";DATABASE=") + db + QLatin1String(";HOSTNAME=") + host |
|
1238 + QLatin1String(";PORT=") + QString::number(port) + QLatin1String(";UID=") + user |
|
1239 + QLatin1String(";PWD=") + password; |
|
1240 |
|
1241 |
|
1242 SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH]; |
|
1243 SQLSMALLINT cb; |
|
1244 |
|
1245 r = SQLDriverConnect(d->hDbc, |
|
1246 NULL, |
|
1247 qToTChar(connQStr), |
|
1248 (SQLSMALLINT) connQStr.length(), |
|
1249 connOut, |
|
1250 SQL_MAX_OPTION_STRING_LENGTH, |
|
1251 &cb, |
|
1252 SQL_DRIVER_NOPROMPT); |
|
1253 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
1254 setLastError(qMakeError(tr("Unable to connect"), |
|
1255 QSqlError::ConnectionError, d)); |
|
1256 setOpenError(true); |
|
1257 return false; |
|
1258 } |
|
1259 |
|
1260 d->user = user; |
|
1261 setOpen(true); |
|
1262 setOpenError(false); |
|
1263 return true; |
|
1264 } |
|
1265 |
|
1266 void QDB2Driver::close() |
|
1267 { |
|
1268 SQLRETURN r; |
|
1269 if (d->hDbc) { |
|
1270 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect |
|
1271 if (isOpen()) { |
|
1272 r = SQLDisconnect(d->hDbc); |
|
1273 if (r != SQL_SUCCESS) |
|
1274 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to disconnect datasource"), d); |
|
1275 } |
|
1276 r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc); |
|
1277 if (r != SQL_SUCCESS) |
|
1278 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free connection handle"), d); |
|
1279 d->hDbc = 0; |
|
1280 } |
|
1281 |
|
1282 if (d->hEnv) { |
|
1283 r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv); |
|
1284 if (r != SQL_SUCCESS) |
|
1285 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free environment handle"), d); |
|
1286 d->hEnv = 0; |
|
1287 } |
|
1288 setOpen(false); |
|
1289 setOpenError(false); |
|
1290 } |
|
1291 |
|
1292 QSqlResult *QDB2Driver::createResult() const |
|
1293 { |
|
1294 return new QDB2Result(this, d); |
|
1295 } |
|
1296 |
|
1297 QSqlRecord QDB2Driver::record(const QString& tableName) const |
|
1298 { |
|
1299 QSqlRecord fil; |
|
1300 if (!isOpen()) |
|
1301 return fil; |
|
1302 |
|
1303 SQLHANDLE hStmt; |
|
1304 QString catalog, schema, table; |
|
1305 qSplitTableQualifier(tableName, &catalog, &schema, &table); |
|
1306 if (schema.isEmpty()) |
|
1307 schema = d->user; |
|
1308 |
|
1309 if (isIdentifierEscaped(catalog, QSqlDriver::TableName)) |
|
1310 catalog = stripDelimiters(catalog, QSqlDriver::TableName); |
|
1311 else |
|
1312 catalog = catalog.toUpper(); |
|
1313 |
|
1314 if (isIdentifierEscaped(schema, QSqlDriver::TableName)) |
|
1315 schema = stripDelimiters(schema, QSqlDriver::TableName); |
|
1316 else |
|
1317 schema = schema.toUpper(); |
|
1318 |
|
1319 if (isIdentifierEscaped(table, QSqlDriver::TableName)) |
|
1320 table = stripDelimiters(table, QSqlDriver::TableName); |
|
1321 else |
|
1322 table = table.toUpper(); |
|
1323 |
|
1324 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, |
|
1325 d->hDbc, |
|
1326 &hStmt); |
|
1327 if (r != SQL_SUCCESS) { |
|
1328 qSqlWarning(QLatin1String("QDB2Driver::record: Unable to allocate handle"), d); |
|
1329 return fil; |
|
1330 } |
|
1331 |
|
1332 r = SQLSetStmtAttr(hStmt, |
|
1333 SQL_ATTR_CURSOR_TYPE, |
|
1334 (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY, |
|
1335 SQL_IS_UINTEGER); |
|
1336 |
|
1337 |
|
1338 //Aside: szSchemaName and szTableName parameters of SQLColumns |
|
1339 //are case sensitive search patterns, so no escaping is used. |
|
1340 r = SQLColumns(hStmt, |
|
1341 NULL, |
|
1342 0, |
|
1343 qToTChar(schema), |
|
1344 schema.length(), |
|
1345 qToTChar(table), |
|
1346 table.length(), |
|
1347 NULL, |
|
1348 0); |
|
1349 |
|
1350 if (r != SQL_SUCCESS) |
|
1351 qSqlWarning(QLatin1String("QDB2Driver::record: Unable to execute column list"), d); |
|
1352 r = SQLFetchScroll(hStmt, |
|
1353 SQL_FETCH_NEXT, |
|
1354 0); |
|
1355 while (r == SQL_SUCCESS) { |
|
1356 fil.append(qMakeFieldInfo(hStmt)); |
|
1357 r = SQLFetchScroll(hStmt, |
|
1358 SQL_FETCH_NEXT, |
|
1359 0); |
|
1360 } |
|
1361 |
|
1362 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); |
|
1363 if (r != SQL_SUCCESS) |
|
1364 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ") |
|
1365 + QString::number(r), d); |
|
1366 |
|
1367 return fil; |
|
1368 } |
|
1369 |
|
1370 QStringList QDB2Driver::tables(QSql::TableType type) const |
|
1371 { |
|
1372 QStringList tl; |
|
1373 if (!isOpen()) |
|
1374 return tl; |
|
1375 |
|
1376 SQLHANDLE hStmt; |
|
1377 |
|
1378 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, |
|
1379 d->hDbc, |
|
1380 &hStmt); |
|
1381 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { |
|
1382 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to allocate handle"), d); |
|
1383 return tl; |
|
1384 } |
|
1385 r = SQLSetStmtAttr(hStmt, |
|
1386 SQL_ATTR_CURSOR_TYPE, |
|
1387 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, |
|
1388 SQL_IS_UINTEGER); |
|
1389 |
|
1390 QString tableType; |
|
1391 if (type & QSql::Tables) |
|
1392 tableType += QLatin1String("TABLE,"); |
|
1393 if (type & QSql::Views) |
|
1394 tableType += QLatin1String("VIEW,"); |
|
1395 if (type & QSql::SystemTables) |
|
1396 tableType += QLatin1String("SYSTEM TABLE,"); |
|
1397 if (tableType.isEmpty()) |
|
1398 return tl; |
|
1399 tableType.chop(1); |
|
1400 |
|
1401 r = SQLTables(hStmt, |
|
1402 NULL, |
|
1403 0, |
|
1404 NULL, |
|
1405 0, |
|
1406 NULL, |
|
1407 0, |
|
1408 qToTChar(tableType), |
|
1409 tableType.length()); |
|
1410 |
|
1411 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) |
|
1412 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to execute table list"), d); |
|
1413 r = SQLFetchScroll(hStmt, |
|
1414 SQL_FETCH_NEXT, |
|
1415 0); |
|
1416 while (r == SQL_SUCCESS) { |
|
1417 bool isNull; |
|
1418 QString fieldVal = qGetStringData(hStmt, 2, -1, isNull); |
|
1419 QString userVal = qGetStringData(hStmt, 1, -1, isNull); |
|
1420 QString user = d->user; |
|
1421 if ( isIdentifierEscaped(user, QSqlDriver::TableName)) |
|
1422 user = stripDelimiters(user, QSqlDriver::TableName); |
|
1423 else |
|
1424 user = user.toUpper(); |
|
1425 |
|
1426 if (userVal != user) |
|
1427 fieldVal = userVal + QLatin1Char('.') + fieldVal; |
|
1428 tl.append(fieldVal); |
|
1429 r = SQLFetchScroll(hStmt, |
|
1430 SQL_FETCH_NEXT, |
|
1431 0); |
|
1432 } |
|
1433 |
|
1434 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); |
|
1435 if (r != SQL_SUCCESS) |
|
1436 qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to free statement handle ") |
|
1437 + QString::number(r), d); |
|
1438 return tl; |
|
1439 } |
|
1440 |
|
1441 QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const |
|
1442 { |
|
1443 QSqlIndex index(tablename); |
|
1444 if (!isOpen()) |
|
1445 return index; |
|
1446 QSqlRecord rec = record(tablename); |
|
1447 |
|
1448 SQLHANDLE hStmt; |
|
1449 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, |
|
1450 d->hDbc, |
|
1451 &hStmt); |
|
1452 if (r != SQL_SUCCESS) { |
|
1453 qSqlWarning(QLatin1String("QDB2Driver::primaryIndex: Unable to list primary key"), d); |
|
1454 return index; |
|
1455 } |
|
1456 QString catalog, schema, table; |
|
1457 qSplitTableQualifier(tablename, &catalog, &schema, &table); |
|
1458 |
|
1459 if (isIdentifierEscaped(catalog, QSqlDriver::TableName)) |
|
1460 catalog = stripDelimiters(catalog, QSqlDriver::TableName); |
|
1461 else |
|
1462 catalog = catalog.toUpper(); |
|
1463 |
|
1464 if (isIdentifierEscaped(schema, QSqlDriver::TableName)) |
|
1465 schema = stripDelimiters(schema, QSqlDriver::TableName); |
|
1466 else |
|
1467 schema = schema.toUpper(); |
|
1468 |
|
1469 if (isIdentifierEscaped(table, QSqlDriver::TableName)) |
|
1470 table = stripDelimiters(table, QSqlDriver::TableName); |
|
1471 else |
|
1472 table = table.toUpper(); |
|
1473 |
|
1474 r = SQLSetStmtAttr(hStmt, |
|
1475 SQL_ATTR_CURSOR_TYPE, |
|
1476 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, |
|
1477 SQL_IS_UINTEGER); |
|
1478 |
|
1479 r = SQLPrimaryKeys(hStmt, |
|
1480 NULL, |
|
1481 0, |
|
1482 qToTChar(schema), |
|
1483 schema.length(), |
|
1484 qToTChar(table), |
|
1485 table.length()); |
|
1486 r = SQLFetchScroll(hStmt, |
|
1487 SQL_FETCH_NEXT, |
|
1488 0); |
|
1489 |
|
1490 bool isNull; |
|
1491 QString cName, idxName; |
|
1492 // Store all fields in a StringList because the driver can't detail fields in this FETCH loop |
|
1493 while (r == SQL_SUCCESS) { |
|
1494 cName = qGetStringData(hStmt, 3, -1, isNull); // column name |
|
1495 idxName = qGetStringData(hStmt, 5, -1, isNull); // pk index name |
|
1496 index.append(rec.field(cName)); |
|
1497 index.setName(idxName); |
|
1498 r = SQLFetchScroll(hStmt, |
|
1499 SQL_FETCH_NEXT, |
|
1500 0); |
|
1501 } |
|
1502 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); |
|
1503 if (r!= SQL_SUCCESS) |
|
1504 qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ") |
|
1505 + QString::number(r), d); |
|
1506 return index; |
|
1507 } |
|
1508 |
|
1509 bool QDB2Driver::hasFeature(DriverFeature f) const |
|
1510 { |
|
1511 switch (f) { |
|
1512 case QuerySize: |
|
1513 case NamedPlaceholders: |
|
1514 case BatchOperations: |
|
1515 case LastInsertId: |
|
1516 case SimpleLocking: |
|
1517 case EventNotifications: |
|
1518 return false; |
|
1519 case BLOB: |
|
1520 case Transactions: |
|
1521 case MultipleResultSets: |
|
1522 case PreparedQueries: |
|
1523 case PositionalPlaceholders: |
|
1524 case LowPrecisionNumbers: |
|
1525 case FinishQuery: |
|
1526 return true; |
|
1527 case Unicode: |
|
1528 return true; |
|
1529 } |
|
1530 return false; |
|
1531 } |
|
1532 |
|
1533 bool QDB2Driver::beginTransaction() |
|
1534 { |
|
1535 if (!isOpen()) { |
|
1536 qWarning("QDB2Driver::beginTransaction: Database not open"); |
|
1537 return false; |
|
1538 } |
|
1539 return setAutoCommit(false); |
|
1540 } |
|
1541 |
|
1542 bool QDB2Driver::commitTransaction() |
|
1543 { |
|
1544 if (!isOpen()) { |
|
1545 qWarning("QDB2Driver::commitTransaction: Database not open"); |
|
1546 return false; |
|
1547 } |
|
1548 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC, |
|
1549 d->hDbc, |
|
1550 SQL_COMMIT); |
|
1551 if (r != SQL_SUCCESS) { |
|
1552 setLastError(qMakeError(tr("Unable to commit transaction"), |
|
1553 QSqlError::TransactionError, d)); |
|
1554 return false; |
|
1555 } |
|
1556 return setAutoCommit(true); |
|
1557 } |
|
1558 |
|
1559 bool QDB2Driver::rollbackTransaction() |
|
1560 { |
|
1561 if (!isOpen()) { |
|
1562 qWarning("QDB2Driver::rollbackTransaction: Database not open"); |
|
1563 return false; |
|
1564 } |
|
1565 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC, |
|
1566 d->hDbc, |
|
1567 SQL_ROLLBACK); |
|
1568 if (r != SQL_SUCCESS) { |
|
1569 setLastError(qMakeError(tr("Unable to rollback transaction"), |
|
1570 QSqlError::TransactionError, d)); |
|
1571 return false; |
|
1572 } |
|
1573 return setAutoCommit(true); |
|
1574 } |
|
1575 |
|
1576 bool QDB2Driver::setAutoCommit(bool autoCommit) |
|
1577 { |
|
1578 SQLUINTEGER ac = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF; |
|
1579 SQLRETURN r = SQLSetConnectAttr(d->hDbc, |
|
1580 SQL_ATTR_AUTOCOMMIT, |
|
1581 (SQLPOINTER)ac, |
|
1582 sizeof(ac)); |
|
1583 if (r != SQL_SUCCESS) { |
|
1584 setLastError(qMakeError(tr("Unable to set autocommit"), |
|
1585 QSqlError::TransactionError, d)); |
|
1586 return false; |
|
1587 } |
|
1588 return true; |
|
1589 } |
|
1590 |
|
1591 QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const |
|
1592 { |
|
1593 if (field.isNull()) |
|
1594 return QLatin1String("NULL"); |
|
1595 |
|
1596 switch (field.type()) { |
|
1597 case QVariant::DateTime: { |
|
1598 // Use an escape sequence for the datetime fields |
|
1599 if (field.value().toDateTime().isValid()) { |
|
1600 QDate dt = field.value().toDateTime().date(); |
|
1601 QTime tm = field.value().toDateTime().time(); |
|
1602 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10 |
|
1603 return QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-') |
|
1604 + QString::number(dt.month()) + QLatin1Char('-') |
|
1605 + QString::number(dt.day()) + QLatin1Char('-') |
|
1606 + QString::number(tm.hour()) + QLatin1Char('.') |
|
1607 + QString::number(tm.minute()).rightJustified(2, QLatin1Char('0'), true) |
|
1608 + QLatin1Char('.') |
|
1609 + QString::number(tm.second()).rightJustified(2, QLatin1Char('0'), true) |
|
1610 + QLatin1Char('.') |
|
1611 + QString::number(tm.msec() * 1000).rightJustified(6, QLatin1Char('0'), true) |
|
1612 + QLatin1Char('\''); |
|
1613 } else { |
|
1614 return QLatin1String("NULL"); |
|
1615 } |
|
1616 } |
|
1617 case QVariant::ByteArray: { |
|
1618 QByteArray ba = field.value().toByteArray(); |
|
1619 QString res = QString::fromLatin1("BLOB(X'"); |
|
1620 static const char hexchars[] = "0123456789abcdef"; |
|
1621 for (int i = 0; i < ba.size(); ++i) { |
|
1622 uchar s = (uchar) ba[i]; |
|
1623 res += QLatin1Char(hexchars[s >> 4]); |
|
1624 res += QLatin1Char(hexchars[s & 0x0f]); |
|
1625 } |
|
1626 res += QLatin1String("')"); |
|
1627 return res; |
|
1628 } |
|
1629 default: |
|
1630 return QSqlDriver::formatValue(field, trimStrings); |
|
1631 } |
|
1632 } |
|
1633 |
|
1634 QVariant QDB2Driver::handle() const |
|
1635 { |
|
1636 return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc); |
|
1637 } |
|
1638 |
|
1639 QString QDB2Driver::escapeIdentifier(const QString &identifier, IdentifierType) const |
|
1640 { |
|
1641 QString res = identifier; |
|
1642 if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) { |
|
1643 res.replace(QLatin1Char('"'), QLatin1String("\"\"")); |
|
1644 res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); |
|
1645 res.replace(QLatin1Char('.'), QLatin1String("\".\"")); |
|
1646 } |
|
1647 return res; |
|
1648 } |
|
1649 |
|
1650 QT_END_NAMESPACE |