source: trunk/src/sql/drivers/odbc/qsql_odbc.cpp

Last change on this file was 196, checked in by rudi, 14 years ago

Add SQL module (currently it isn't build by default, however it's needed for QtDesigner)

File size: 55.8 KB
Line 
1/****************************************************************************
2**
3** Implementation of ODBC driver classes
4**
5** Created : 001103
6**
7** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
8**
9** This file is part of the sql module of the Qt GUI Toolkit.
10**
11** This file may be distributed under the terms of the Q Public License
12** as defined by Trolltech ASA of Norway and appearing in the file
13** LICENSE.QPL included in the packaging of this file.
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** Licensees holding valid Qt Enterprise Edition licenses may use this
21** file in accordance with the Qt Commercial License Agreement provided
22** with the Software.
23**
24** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26**
27** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
28** information about Qt Commercial License Agreements.
29** See http://www.trolltech.com/qpl/ for QPL licensing information.
30** See http://www.trolltech.com/gpl/ for GPL licensing information.
31**
32** Contact info@trolltech.com if any conditions of this licensing are
33** not clear to you.
34**
35**********************************************************************/
36
37#include "qsql_odbc.h"
38#include <qsqlrecord.h>
39
40#if defined (Q_OS_WIN32)
41#include <qt_windows.h>
42#include <qapplication.h>
43#endif
44#include <qdatetime.h>
45#include <private/qsqlextension_p.h>
46#include <private/qinternal_p.h>
47#include <stdlib.h>
48
49// undefine this to prevent initial check of the ODBC driver
50#define ODBC_CHECK_DRIVER
51
52#if defined(Q_ODBC_VERSION_2)
53//crude hack to get non-unicode capable driver managers to work
54# undef UNICODE
55# define SQLTCHAR SQLCHAR
56# define SQL_C_WCHAR SQL_C_CHAR
57#endif
58
59// newer platform SDKs use SQLLEN instead of SQLINTEGER
60#ifdef SQLLEN
61# define QSQLLEN SQLLEN
62#else
63# define QSQLLEN SQLINTEGER
64#endif
65
66#ifdef SQLULEN
67# define QSQLULEN SQLULEN
68#else
69# define QSQLULEN SQLUINTEGER
70#endif
71
72
73static const QSQLLEN COLNAMESIZE = 256;
74//Map Qt parameter types to ODBC types
75static const SQLSMALLINT qParamType[ 4 ] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
76
77class QODBCPrivate
78{
79public:
80 QODBCPrivate()
81 : hEnv(0), hDbc(0), hStmt(0), useSchema(FALSE)
82 {
83 sql_char_type = sql_varchar_type = sql_longvarchar_type = QVariant::CString;
84 unicode = FALSE;
85 }
86
87 SQLHANDLE hEnv;
88 SQLHANDLE hDbc;
89 SQLHANDLE hStmt;
90
91 bool unicode;
92 bool useSchema;
93 QVariant::Type sql_char_type;
94 QVariant::Type sql_varchar_type;
95 QVariant::Type sql_longvarchar_type;
96
97 QSqlRecordInfo rInf;
98
99 bool checkDriver() const;
100 void checkUnicode();
101 void checkSchemaUsage();
102 bool setConnectionOptions( const QString& connOpts );
103 void splitTableQualifier(const QString &qualifier, QString &catalog,
104 QString &schema, QString &table);
105};
106
107class QODBCPreparedExtension : public QSqlExtension
108{
109public:
110 QODBCPreparedExtension( QODBCResult * r )
111 : result( r ) {}
112
113 bool prepare( const QString& query )
114 {
115 return result->prepare( query );
116 }
117
118 bool exec()
119 {
120 return result->exec();
121 }
122
123 QODBCResult * result;
124};
125
126QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();
127
128class QODBCOpenExtension : public QSqlOpenExtension
129{
130public:
131 QODBCOpenExtension( QODBCDriver *dri )
132 : QSqlOpenExtension(), driver(dri) {}
133 ~QODBCOpenExtension() {}
134
135 bool open( const QString& db,
136 const QString& user,
137 const QString& password,
138 const QString& host,
139 int port,
140 const QString& connOpts );
141private:
142 QODBCDriver *driver;
143};
144
145bool QODBCOpenExtension::open( const QString& db,
146 const QString& user,
147 const QString& password,
148 const QString& host,
149 int port,
150 const QString& connOpts )
151{
152 return driver->open( db, user, password, host, port, connOpts );
153}
154
155static QString qWarnODBCHandle(int handleType, SQLHANDLE handle)
156{
157 SQLINTEGER nativeCode_;
158 SQLSMALLINT msgLen;
159 SQLRETURN r = SQL_ERROR;
160 SQLTCHAR state_[SQL_SQLSTATE_SIZE+1];
161 SQLTCHAR description_[SQL_MAX_MESSAGE_LENGTH];
162 r = SQLGetDiagRec( handleType,
163 handle,
164 1,
165 (SQLTCHAR*)state_,
166 &nativeCode_,
167 (SQLTCHAR*)description_,
168 SQL_MAX_MESSAGE_LENGTH-1, /* in bytes, not in characters */
169 &msgLen);
170 if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO )
171#ifdef UNICODE
172 return QString( (const QChar*)description_, (uint)msgLen );
173#else
174 return QString::fromLocal8Bit( (const char*)description_ );
175#endif
176 return QString::null;
177}
178
179static QString qODBCWarn( const QODBCPrivate* odbc)
180{
181 return ( qWarnODBCHandle( SQL_HANDLE_ENV, odbc->hEnv ) + " "
182 + qWarnODBCHandle( SQL_HANDLE_DBC, odbc->hDbc ) + " "
183 + qWarnODBCHandle( SQL_HANDLE_STMT, odbc->hStmt ) );
184}
185
186static void qSqlWarning( const QString& message, const QODBCPrivate* odbc )
187{
188#ifdef QT_CHECK_RANGE
189 qWarning( "%s\tError: %s", message.local8Bit().data(), qODBCWarn( odbc ).local8Bit().data() );
190#endif
191}
192
193static QSqlError qMakeError( const QString& err, int type, const QODBCPrivate* p )
194{
195 return QSqlError( "QODBC3: " + err, qODBCWarn(p), type );
196}
197
198static QVariant::Type qDecodeODBCType( SQLSMALLINT sqltype, const QODBCPrivate* p )
199{
200 QVariant::Type type = QVariant::Invalid;
201 switch ( sqltype ) {
202 case SQL_DECIMAL:
203 case SQL_NUMERIC:
204 case SQL_REAL:
205 case SQL_FLOAT:
206 case SQL_DOUBLE:
207 type = QVariant::Double;
208 break;
209 case SQL_SMALLINT:
210 case SQL_INTEGER:
211 case SQL_BIT:
212 case SQL_TINYINT:
213 type = QVariant::Int;
214 break;
215 case SQL_BIGINT:
216 type = QVariant::LongLong;
217 break;
218 case SQL_BINARY:
219 case SQL_VARBINARY:
220 case SQL_LONGVARBINARY:
221 type = QVariant::ByteArray;
222 break;
223 case SQL_DATE:
224 case SQL_TYPE_DATE:
225 type = QVariant::Date;
226 break;
227 case SQL_TIME:
228 case SQL_TYPE_TIME:
229 type = QVariant::Time;
230 break;
231 case SQL_TIMESTAMP:
232 case SQL_TYPE_TIMESTAMP:
233 type = QVariant::DateTime;
234 break;
235#ifndef Q_ODBC_VERSION_2
236 case SQL_WCHAR:
237 case SQL_WVARCHAR:
238 case SQL_WLONGVARCHAR:
239 type = QVariant::String;
240 break;
241#endif
242 case SQL_CHAR:
243 type = p->sql_char_type;
244 break;
245 case SQL_VARCHAR:
246 type = p->sql_varchar_type;
247 break;
248 case SQL_LONGVARCHAR:
249 type = p->sql_longvarchar_type;
250 break;
251 default:
252 type = QVariant::CString;
253 break;
254 }
255 return type;
256}
257
258static QString qGetStringData( SQLHANDLE hStmt, int column, int colSize, bool& isNull, bool unicode = FALSE )
259{
260 QString fieldVal;
261 SQLRETURN r = SQL_ERROR;
262 QSQLLEN lengthIndicator = 0;
263
264 if ( colSize <= 0 ) {
265 colSize = 256;
266 } else if ( colSize > 65536 ) { // limit buffer size to 64 KB
267 colSize = 65536;
268 } else {
269 colSize++; // make sure there is room for more than the 0 termination
270 if ( unicode ) {
271 colSize *= 2; // a tiny bit faster, since it saves a SQLGetData() call
272 }
273 }
274 char* buf = new char[ colSize ];
275 while ( TRUE ) {
276 r = SQLGetData( hStmt,
277 column+1,
278 unicode ? SQL_C_WCHAR : SQL_C_CHAR,
279 (SQLPOINTER)buf,
280 (QSQLLEN)colSize,
281 &lengthIndicator );
282 if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
283 if ( lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL ) {
284 fieldVal = QString::null;
285 isNull = TRUE;
286 break;
287 }
288 // if SQL_SUCCESS_WITH_INFO is returned, indicating that
289 // more data can be fetched, the length indicator does NOT
290 // contain the number of bytes returned - it contains the
291 // total number of bytes that CAN be fetched
292 // colSize-1: remove 0 termination when there is more data to fetch
293 int rSize = (r == SQL_SUCCESS_WITH_INFO) ? (unicode ? colSize-2 : colSize-1) : lengthIndicator;
294 if ( unicode ) {
295 fieldVal += QString( (QChar*) buf, rSize / 2 );
296 } else {
297 buf[ rSize ] = 0;
298 fieldVal += buf;
299 }
300 if ( lengthIndicator < colSize ) {
301 // workaround for Drivermanagers that don't return SQL_NO_DATA
302 break;
303 }
304 } else if ( r == SQL_NO_DATA ) {
305 break;
306 } else {
307#ifdef QT_CHECK_RANGE
308 qWarning( "qGetStringData: Error while fetching data (%d)", r );
309#endif
310 fieldVal = QString::null;
311 break;
312 }
313 }
314 delete[] buf;
315 return fieldVal;
316}
317
318static QByteArray qGetBinaryData( SQLHANDLE hStmt, int column, QSQLLEN& lengthIndicator, bool& isNull )
319{
320 QByteArray fieldVal;
321 SQLSMALLINT colNameLen;
322 SQLSMALLINT colType;
323 QSQLULEN colSize;
324 SQLSMALLINT colScale;
325 SQLSMALLINT nullable;
326 SQLRETURN r = SQL_ERROR;
327
328 SQLTCHAR colName[COLNAMESIZE];
329 r = SQLDescribeCol( hStmt,
330 column+1,
331 colName,
332 COLNAMESIZE,
333 &colNameLen,
334 &colType,
335 &colSize,
336 &colScale,
337 &nullable );
338#ifdef QT_CHECK_RANGE
339 if ( r != SQL_SUCCESS )
340 qWarning( "qGetBinaryData: Unable to describe column %d", column );
341#endif
342 // SQLDescribeCol may return 0 if size cannot be determined
343 if (!colSize) {
344 colSize = 256;
345 }
346 if ( colSize > 65536 ) { // read the field in 64 KB chunks
347 colSize = 65536;
348 }
349 char * buf = new char[ colSize ];
350 while ( TRUE ) {
351 r = SQLGetData( hStmt,
352 column+1,
353 SQL_C_BINARY,
354 (SQLPOINTER) buf,
355 (QSQLLEN)colSize,
356 &lengthIndicator );
357 if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
358 if ( lengthIndicator == SQL_NULL_DATA ) {
359 isNull = TRUE;
360 break;
361 } else {
362 int rSize;
363 r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
364 if ( lengthIndicator == SQL_NO_TOTAL ) { // size cannot be determined
365 rSize = colSize;
366 }
367 // NB! This is not a memleak - the mem will be deleted by QByteArray when
368 // no longer ref'd
369 char * tmp = (char *) malloc( rSize + fieldVal.size() );
370 if ( fieldVal.size() ) {
371 memcpy( tmp, fieldVal.data(), fieldVal.size() );
372 }
373 memcpy( tmp + fieldVal.size(), buf, rSize );
374 fieldVal = fieldVal.assign( tmp, fieldVal.size() + rSize );
375
376 if ( r == SQL_SUCCESS ) { // the whole field was read in one chunk
377 break;
378 }
379 }
380 } else {
381 break;
382 }
383 }
384 delete [] buf;
385 return fieldVal;
386}
387
388static int qGetIntData( SQLHANDLE hStmt, int column, bool& isNull )
389{
390 QSQLLEN intbuf = 0;
391 isNull = FALSE;
392 QSQLLEN lengthIndicator = 0;
393 SQLRETURN r = SQLGetData( hStmt,
394 column+1,
395 SQL_C_SLONG,
396 (SQLPOINTER)&intbuf,
397 (QSQLLEN)0,
398 &lengthIndicator );
399 if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) {
400 isNull = TRUE;
401 return 0;
402 }
403 return (int)intbuf;
404}
405
406static double qGetDoubleData( SQLHANDLE hStmt, int column, bool& isNull )
407{
408 SQLDOUBLE dblbuf;
409 QSQLLEN lengthIndicator = 0;
410 isNull = FALSE;
411 SQLRETURN r = SQLGetData( hStmt,
412 column+1,
413 SQL_C_DOUBLE,
414 (SQLPOINTER)&dblbuf,
415 (QSQLLEN)0,
416 &lengthIndicator );
417 if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) {
418 isNull = TRUE;
419 return 0.0;
420 }
421
422 return (double) dblbuf;
423}
424
425static SQLBIGINT qGetBigIntData( SQLHANDLE hStmt, int column, bool& isNull )
426{
427 SQLBIGINT lngbuf = Q_INT64_C( 0 );
428 isNull = FALSE;
429 QSQLLEN lengthIndicator = 0;
430 SQLRETURN r = SQLGetData( hStmt,
431 column+1,
432 SQL_C_SBIGINT,
433 (SQLPOINTER) &lngbuf,
434 (QSQLLEN)0,
435 &lengthIndicator );
436 if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA )
437 isNull = TRUE;
438
439 return lngbuf;
440}
441
442// creates a QSqlFieldInfo from a valid hStmt generated
443// by SQLColumns. The hStmt has to point to a valid position.
444static QSqlFieldInfo qMakeFieldInfo( const SQLHANDLE hStmt, const QODBCPrivate* p )
445{
446 bool isNull;
447 QString fname = qGetStringData( hStmt, 3, -1, isNull, p->unicode );
448 int type = qGetIntData( hStmt, 4, isNull ); // column type
449 int required = qGetIntData( hStmt, 10, isNull ); // nullable-flag
450 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
451 if ( required == SQL_NO_NULLS ) {
452 required = 1;
453 } else if ( required == SQL_NULLABLE ) {
454 required = 0;
455 } else {
456 required = -1;
457 }
458 int size = qGetIntData( hStmt, 6, isNull ); // column size
459 int prec = qGetIntData( hStmt, 8, isNull ); // precision
460 return QSqlFieldInfo( fname, qDecodeODBCType( type, p ), required, size, prec, QVariant(), type );
461}
462
463static QSqlFieldInfo qMakeFieldInfo( const QODBCPrivate* p, int i )
464{
465 SQLSMALLINT colNameLen;
466 SQLSMALLINT colType;
467 QSQLULEN colSize;
468 SQLSMALLINT colScale;
469 SQLSMALLINT nullable;
470 SQLRETURN r = SQL_ERROR;
471 SQLTCHAR colName[ COLNAMESIZE ];
472 r = SQLDescribeCol( p->hStmt,
473 i+1,
474 colName,
475 (QSQLULEN)COLNAMESIZE,
476 &colNameLen,
477 &colType,
478 &colSize,
479 &colScale,
480 &nullable);
481
482 if ( r != SQL_SUCCESS ) {
483#ifdef QT_CHECK_RANGE
484 qSqlWarning( QString("qMakeField: Unable to describe column %1").arg(i), p );
485#endif
486 return QSqlFieldInfo();
487 }
488#ifdef UNICODE
489 QString qColName( (const QChar*)colName, (uint)colNameLen );
490#else
491 QString qColName = QString::fromLocal8Bit( (const char*)colName );
492#endif
493 // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
494 int required = -1;
495 if ( nullable == SQL_NO_NULLS ) {
496 required = 1;
497 } else if ( nullable == SQL_NULLABLE ) {
498 required = 0;
499 }
500 QVariant::Type type = qDecodeODBCType( colType, p );
501 return QSqlFieldInfo( qColName,
502 type,
503 required,
504 (int)colSize == 0 ? -1 : (int)colSize,
505 (int)colScale == 0 ? -1 : (int)colScale,
506 QVariant(),
507 (int)colType );
508}
509
510bool QODBCPrivate::setConnectionOptions( const QString& connOpts )
511{
512 // Set any connection attributes
513 QStringList raw = QStringList::split( ';', connOpts );
514 QStringList opts;
515 SQLRETURN r = SQL_SUCCESS;
516 QMap<QString, QString> connMap;
517 for ( QStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) {
518 QString tmp( *it );
519 int idx;
520 if ( (idx = tmp.find( '=' )) != -1 )
521 connMap[ tmp.left( idx ) ] = tmp.mid( idx + 1 ).simplifyWhiteSpace();
522 else
523 qWarning( "QODBCDriver::open: Illegal connect option value '%s'", tmp.latin1() );
524 }
525 if ( connMap.count() ) {
526 QMap<QString, QString>::ConstIterator it;
527 QString opt, val;
528 SQLUINTEGER v = 0;
529 for ( it = connMap.begin(); it != connMap.end(); ++it ) {
530 opt = it.key().upper();
531 val = it.data().upper();
532 r = SQL_SUCCESS;
533 if ( opt == "SQL_ATTR_ACCESS_MODE" ) {
534 if ( val == "SQL_MODE_READ_ONLY" ) {
535 v = SQL_MODE_READ_ONLY;
536 } else if ( val == "SQL_MODE_READ_WRITE" ) {
537 v = SQL_MODE_READ_WRITE;
538 } else {
539 qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
540 break;
541 }
542 r = SQLSetConnectAttr( hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0 );
543 } else if ( opt == "SQL_ATTR_CONNECTION_TIMEOUT" ) {
544 v = val.toUInt();
545 r = SQLSetConnectAttr( hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) v, 0 );
546 } else if ( opt == "SQL_ATTR_LOGIN_TIMEOUT" ) {
547 v = val.toUInt();
548 r = SQLSetConnectAttr( hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0 );
549 } else if ( opt == "SQL_ATTR_CURRENT_CATALOG" ) {
550 val.ucs2(); // 0 terminate
551 r = SQLSetConnectAttr( hDbc, SQL_ATTR_CURRENT_CATALOG,
552#ifdef UNICODE
553 (SQLWCHAR*) val.unicode(),
554#else
555 (SQLCHAR*) val.latin1(),
556#endif
557 SQL_NTS );
558 } else if ( opt == "SQL_ATTR_METADATA_ID" ) {
559 if ( val == "SQL_TRUE" ) {
560 v = SQL_TRUE;
561 } else if ( val == "SQL_FALSE" ) {
562 v = SQL_FALSE;
563 } else {
564 qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
565 break;
566 }
567 r = SQLSetConnectAttr( hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) v, 0 );
568 } else if ( opt == "SQL_ATTR_PACKET_SIZE" ) {
569 v = val.toUInt();
570 r = SQLSetConnectAttr( hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) v, 0 );
571 } else if ( opt == "SQL_ATTR_TRACEFILE" ) {
572 val.ucs2(); // 0 terminate
573 r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACEFILE,
574#ifdef UNICODE
575 (SQLWCHAR*) val.unicode(),
576#else
577 (SQLCHAR*) val.latin1(),
578#endif
579 SQL_NTS );
580 } else if ( opt == "SQL_ATTR_TRACE" ) {
581 if ( val == "SQL_OPT_TRACE_OFF" ) {
582 v = SQL_OPT_TRACE_OFF;
583 } else if ( val == "SQL_OPT_TRACE_ON" ) {
584 v = SQL_OPT_TRACE_ON;
585 } else {
586 qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
587 break;
588 }
589 r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0 );
590 }
591#ifdef QT_CHECK_RANGE
592 else {
593 qWarning( QString("QODBCDriver::open: Unknown connection attribute '%1'").arg( opt ) );
594 }
595#endif
596 if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
597#ifdef QT_CHECK_RANGE
598 qSqlWarning( QString("QODBCDriver::open: Unable to set connection attribute '%1'").arg( opt ), this );
599#endif
600 return FALSE;
601 }
602 }
603 }
604 return TRUE;
605}
606
607void QODBCPrivate::splitTableQualifier(const QString & qualifier, QString &catalog,
608 QString &schema, QString &table)
609{
610 if (!useSchema) {
611 table = qualifier;
612 return;
613 }
614 QStringList l = QStringList::split( ".", qualifier, TRUE );
615 if ( l.count() > 3 )
616 return; // can't possibly be a valid table qualifier
617 int i = 0, n = l.count();
618 if ( n == 1 ) {
619 table = qualifier;
620 } else {
621 for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
622 if ( n == 3 ) {
623 if ( i == 0 ) {
624 catalog = *it;
625 } else if ( i == 1 ) {
626 schema = *it;
627 } else if ( i == 2 ) {
628 table = *it;
629 }
630 } else if ( n == 2 ) {
631 if ( i == 0 ) {
632 schema = *it;
633 } else if ( i == 1 ) {
634 table = *it;
635 }
636 }
637 i++;
638 }
639 }
640}
641
642////////////////////////////////////////////////////////////////////////////
643
644QODBCResult::QODBCResult( const QODBCDriver * db, QODBCPrivate* p )
645: QSqlResult(db)
646{
647 d = new QODBCPrivate();
648 (*d) = (*p);
649 setExtension( new QODBCPreparedExtension( this ) );
650}
651
652QODBCResult::~QODBCResult()
653{
654 if ( d->hStmt && driver()->isOpen() ) {
655 SQLRETURN r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
656#ifdef QT_CHECK_RANGE
657 if ( r != SQL_SUCCESS )
658 qSqlWarning( "QODBCDriver: Unable to free statement handle " + QString::number(r), d );
659#endif
660 }
661
662 delete d;
663}
664
665bool QODBCResult::reset ( const QString& query )
666{
667 setActive( FALSE );
668 setAt( QSql::BeforeFirst );
669 SQLRETURN r;
670
671 d->rInf.clear();
672 // Always reallocate the statement handle - the statement attributes
673 // are not reset if SQLFreeStmt() is called which causes some problems.
674 if ( d->hStmt ) {
675 r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
676 if ( r != SQL_SUCCESS ) {
677#ifdef QT_CHECK_RANGE
678 qSqlWarning( "QODBCResult::reset: Unable to free statement handle", d );
679#endif
680 return FALSE;
681 }
682 }
683 r = SQLAllocHandle( SQL_HANDLE_STMT,
684 d->hDbc,
685 &d->hStmt );
686 if ( r != SQL_SUCCESS ) {
687#ifdef QT_CHECK_RANGE
688 qSqlWarning( "QODBCResult::reset: Unable to allocate statement handle", d );
689#endif
690 return FALSE;
691 }
692
693 if ( isForwardOnly() ) {
694 r = SQLSetStmtAttr( d->hStmt,
695 SQL_ATTR_CURSOR_TYPE,
696 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
697 SQL_IS_UINTEGER );
698 } else {
699 r = SQLSetStmtAttr( d->hStmt,
700 SQL_ATTR_CURSOR_TYPE,
701 (SQLPOINTER)SQL_CURSOR_STATIC,
702 SQL_IS_UINTEGER );
703 }
704 if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
705#ifdef QT_CHECK_RANGE
706 qSqlWarning( "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
707#endif
708 return FALSE;
709 }
710
711#ifdef UNICODE
712 r = SQLExecDirect( d->hStmt,
713 (SQLWCHAR*) query.unicode(),
714 (SQLINTEGER) query.length() );
715#else
716 QCString query8 = query.local8Bit();
717 r = SQLExecDirect( d->hStmt,
718 (SQLCHAR*) query8.data(),
719 (SQLINTEGER) query8.length() );
720#endif
721 if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
722 setLastError( qMakeError( "Unable to execute statement", QSqlError::Statement, d ) );
723 return FALSE;
724 }
725 SQLSMALLINT count;
726 r = SQLNumResultCols( d->hStmt, &count );
727 if ( count ) {
728 setSelect( TRUE );
729 for ( int i = 0; i < count; ++i ) {
730 d->rInf.append( qMakeFieldInfo( d, i ) );
731 }
732 } else {
733 setSelect( FALSE );
734 }
735 setActive( TRUE );
736 return TRUE;
737}
738
739bool QODBCResult::fetch(int i)
740{
741 if ( isForwardOnly() && i < at() )
742 return FALSE;
743 if ( i == at() )
744 return TRUE;
745 fieldCache.clear();
746 nullCache.clear();
747 int actualIdx = i + 1;
748 if ( actualIdx <= 0 ) {
749 setAt( QSql::BeforeFirst );
750 return FALSE;
751 }
752 SQLRETURN r;
753 if ( isForwardOnly() ) {
754 bool ok = TRUE;
755 while ( ok && i > at() )
756 ok = fetchNext();
757 return ok;
758 } else {
759 r = SQLFetchScroll( d->hStmt,
760 SQL_FETCH_ABSOLUTE,
761 actualIdx );
762 }
763 if ( r != SQL_SUCCESS ){
764 return FALSE;
765 }
766 setAt( i );
767 return TRUE;
768}
769
770bool QODBCResult::fetchNext()
771{
772 SQLRETURN r;
773 fieldCache.clear();
774 nullCache.clear();
775 r = SQLFetchScroll( d->hStmt,
776 SQL_FETCH_NEXT,
777 0 );
778 if ( r != SQL_SUCCESS )
779 return FALSE;
780 setAt( at() + 1 );
781 return TRUE;
782}
783
784bool QODBCResult::fetchFirst()
785{
786 if ( isForwardOnly() && at() != QSql::BeforeFirst )
787 return FALSE;
788 SQLRETURN r;
789 fieldCache.clear();
790 nullCache.clear();
791 if ( isForwardOnly() ) {
792 return fetchNext();
793 }
794 r = SQLFetchScroll( d->hStmt,
795 SQL_FETCH_FIRST,
796 0 );
797 if ( r != SQL_SUCCESS )
798 return FALSE;
799 setAt( 0 );
800 return TRUE;
801}
802
803bool QODBCResult::fetchPrior()
804{
805 if ( isForwardOnly() )
806 return FALSE;
807 SQLRETURN r;
808 fieldCache.clear();
809 nullCache.clear();
810 r = SQLFetchScroll( d->hStmt,
811 SQL_FETCH_PRIOR,
812 0 );
813 if ( r != SQL_SUCCESS )
814 return FALSE;
815 setAt( at() - 1 );
816 return TRUE;
817}
818
819bool QODBCResult::fetchLast()
820{
821 SQLRETURN r;
822 fieldCache.clear();
823 nullCache.clear();
824
825 if ( isForwardOnly() ) {
826 // cannot seek to last row in forwardOnly mode, so we have to use brute force
827 int i = at();
828 if ( i == QSql::AfterLast )
829 return FALSE;
830 if ( i == QSql::BeforeFirst )
831 i = 0;
832 while ( fetchNext() )
833 ++i;
834 setAt( i );
835 return TRUE;
836 }
837
838 r = SQLFetchScroll( d->hStmt,
839 SQL_FETCH_LAST,
840 0 );
841 if ( r != SQL_SUCCESS ) {
842 return FALSE;
843 }
844 SQLINTEGER currRow;
845 r = SQLGetStmtAttr( d->hStmt,
846 SQL_ROW_NUMBER,
847 &currRow,
848 SQL_IS_INTEGER,
849 0 );
850 if ( r != SQL_SUCCESS )
851 return FALSE;
852 setAt( currRow-1 );
853 return TRUE;
854}
855
856QVariant QODBCResult::data( int field )
857{
858 if ( field >= (int) d->rInf.count() ) {
859 qWarning( "QODBCResult::data: column %d out of range", field );
860 return QVariant();
861 }
862 if ( fieldCache.contains( field ) )
863 return fieldCache[ field ];
864 SQLRETURN r(0);
865 QSQLLEN lengthIndicator = 0;
866 bool isNull = FALSE;
867 int current = fieldCache.count();
868 for ( ; current < (field + 1); ++current ) {
869 const QSqlFieldInfo info = d->rInf[ current ];
870 switch ( info.type() ) {
871 case QVariant::LongLong:
872 fieldCache[ current ] = QVariant( (Q_LLONG) qGetBigIntData( d->hStmt, current, isNull ) );
873 nullCache[ current ] = isNull;
874 break;
875 case QVariant::Int:
876 fieldCache[ current ] = QVariant( qGetIntData( d->hStmt, current, isNull ) );
877 nullCache[ current ] = isNull;
878 break;
879 case QVariant::Date:
880 DATE_STRUCT dbuf;
881 r = SQLGetData( d->hStmt,
882 current+1,
883 SQL_C_DATE,
884 (SQLPOINTER)&dbuf,
885 (QSQLLEN)0,
886 &lengthIndicator );
887 if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
888 fieldCache[ current ] = QVariant( QDate( dbuf.year, dbuf.month, dbuf.day ) );
889 nullCache[ current ] = FALSE;
890 } else {
891 fieldCache[ current ] = QVariant( QDate() );
892 nullCache[ current ] = TRUE;
893 }
894 break;
895 case QVariant::Time:
896 TIME_STRUCT tbuf;
897 r = SQLGetData( d->hStmt,
898 current+1,
899 SQL_C_TIME,
900 (SQLPOINTER)&tbuf,
901 (QSQLLEN)0,
902 &lengthIndicator );
903 if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
904 fieldCache[ current ] = QVariant( QTime( tbuf.hour, tbuf.minute, tbuf.second ) );
905 nullCache[ current ] = FALSE;
906 } else {
907 fieldCache[ current ] = QVariant( QTime() );
908 nullCache[ current ] = TRUE;
909 }
910 break;
911 case QVariant::DateTime:
912 TIMESTAMP_STRUCT dtbuf;
913 r = SQLGetData( d->hStmt,
914 current+1,
915 SQL_C_TIMESTAMP,
916 (SQLPOINTER)&dtbuf,
917 (QSQLLEN)0,
918 &lengthIndicator );
919 if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
920 fieldCache[ current ] = QVariant( QDateTime( QDate( dtbuf.year, dtbuf.month, dtbuf.day ), QTime( dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000 ) ) );
921 nullCache[ current ] = FALSE;
922 } else {
923 fieldCache[ current ] = QVariant( QDateTime() );
924 nullCache[ current ] = TRUE;
925 }
926 break;
927 case QVariant::ByteArray: {
928 isNull = FALSE;
929 QByteArray val = qGetBinaryData( d->hStmt, current, lengthIndicator, isNull );
930 fieldCache[ current ] = QVariant( val );
931 nullCache[ current ] = isNull;
932 break; }
933 case QVariant::String:
934 isNull = FALSE;
935 fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
936 info.length(), isNull, TRUE ) );
937 nullCache[ current ] = isNull;
938 break;
939 case QVariant::Double:
940 if ( info.typeID() == SQL_DECIMAL || info.typeID() == SQL_NUMERIC )
941 // bind Double values as string to prevent loss of precision
942 fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
943 info.length() + 1, isNull, FALSE ) ); // length + 1 for the comma
944 else
945 fieldCache[ current ] = QVariant( qGetDoubleData( d->hStmt, current, isNull ) );
946 nullCache[ current ] = isNull;
947 break;
948 case QVariant::CString:
949 default:
950 isNull = FALSE;
951 fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
952 info.length(), isNull, FALSE ) );
953 nullCache[ current ] = isNull;
954 break;
955 }
956 }
957 return fieldCache[ --current ];
958}
959
960bool QODBCResult::isNull( int field )
961{
962 if ( !fieldCache.contains( field ) ) {
963 // since there is no good way to find out whether the value is NULL
964 // without fetching the field we'll fetch it here.
965 // (data() also sets the NULL flag)
966 data( field );
967 }
968 return nullCache[ field ];
969}
970
971int QODBCResult::size()
972{
973 return -1;
974}
975
976int QODBCResult::numRowsAffected()
977{
978 QSQLLEN affectedRowCount(0);
979 SQLRETURN r = SQLRowCount( d->hStmt, &affectedRowCount );
980 if ( r == SQL_SUCCESS )
981 return affectedRowCount;
982#ifdef QT_CHECK_RANGE
983 else
984 qSqlWarning( "QODBCResult::numRowsAffected: Unable to count affected rows", d );
985#endif
986 return -1;
987}
988
989bool QODBCResult::prepare( const QString& query )
990{
991 setActive( FALSE );
992 setAt( QSql::BeforeFirst );
993 SQLRETURN r;
994
995 d->rInf.clear();
996 if ( d->hStmt ) {
997 r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
998 if ( r != SQL_SUCCESS ) {
999#ifdef QT_CHECK_RANGE
1000 qSqlWarning( "QODBCResult::prepare: Unable to close statement", d );
1001#endif
1002 return FALSE;
1003 }
1004 }
1005 r = SQLAllocHandle( SQL_HANDLE_STMT,
1006 d->hDbc,
1007 &d->hStmt );
1008 if ( r != SQL_SUCCESS ) {
1009#ifdef QT_CHECK_RANGE
1010 qSqlWarning( "QODBCResult::prepare: Unable to allocate statement handle", d );
1011#endif
1012 return FALSE;
1013 }
1014
1015 if ( isForwardOnly() ) {
1016 r = SQLSetStmtAttr( d->hStmt,
1017 SQL_ATTR_CURSOR_TYPE,
1018 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1019 SQL_IS_UINTEGER );
1020 } else {
1021 r = SQLSetStmtAttr( d->hStmt,
1022 SQL_ATTR_CURSOR_TYPE,
1023 (SQLPOINTER)SQL_CURSOR_STATIC,
1024 SQL_IS_UINTEGER );
1025 }
1026 if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
1027#ifdef QT_CHECK_RANGE
1028 qSqlWarning( "QODBCResult::prepare: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
1029#endif
1030 return FALSE;
1031 }
1032
1033#ifdef UNICODE
1034 r = SQLPrepare( d->hStmt,
1035 (SQLWCHAR*) query.unicode(),
1036 (SQLINTEGER) query.length() );
1037#else
1038 QCString query8 = query.local8Bit();
1039 r = SQLPrepare( d->hStmt,
1040 (SQLCHAR*) query8.data(),
1041 (SQLINTEGER) query8.length() );
1042#endif
1043
1044 if ( r != SQL_SUCCESS ) {
1045#ifdef QT_CHECK_RANGE
1046 qSqlWarning( "QODBCResult::prepare: Unable to prepare statement", d );
1047#endif
1048 return FALSE;
1049 }
1050 return TRUE;
1051}
1052
1053bool QODBCResult::exec()
1054{
1055 SQLRETURN r;
1056 QPtrList<QVirtualDestructor> tmpStorage; // holds temporary ptrs. which will be deleted on fu exit
1057 tmpStorage.setAutoDelete( TRUE );
1058
1059 setActive( FALSE );
1060 setAt( QSql::BeforeFirst );
1061 d->rInf.clear();
1062
1063 if ( !d->hStmt ) {
1064#ifdef QT_CHECK_RANGE
1065 qSqlWarning( "QODBCResult::exec: No statement handle available", d );
1066#endif
1067 return FALSE;
1068 } else {
1069 r = SQLFreeStmt( d->hStmt, SQL_CLOSE );
1070 if ( r != SQL_SUCCESS ) {
1071 qSqlWarning( "QODBCResult::exec: Unable to close statement handle", d );
1072 return FALSE;
1073 }
1074 }
1075
1076 // bind parameters - only positional binding allowed
1077 if ( extension()->index.count() > 0 ) {
1078 QMap<int, QString>::Iterator it;
1079 int para = 1;
1080 QVariant val;
1081 for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
1082 val = extension()->values[ it.data() ].value;
1083 QSQLLEN *ind = new QSQLLEN( SQL_NTS );
1084 tmpStorage.append( qAutoDeleter(ind) );
1085 if ( val.isNull() ) {
1086 *ind = SQL_NULL_DATA;
1087 }
1088 switch ( val.type() ) {
1089 case QVariant::Date: {
1090 DATE_STRUCT * dt = new DATE_STRUCT;
1091 tmpStorage.append( qAutoDeleter(dt) );
1092 QDate qdt = val.toDate();
1093 dt->year = qdt.year();
1094 dt->month = qdt.month();
1095 dt->day = qdt.day();
1096 r = SQLBindParameter( d->hStmt,
1097 para,
1098 qParamType[ (int)extension()->values[ it.data() ].typ ],
1099 SQL_C_DATE,
1100 SQL_DATE,
1101 0,
1102 0,
1103 (void *) dt,
1104 (QSQLLEN)0,
1105 *ind == SQL_NULL_DATA ? ind : NULL );
1106 break; }
1107 case QVariant::Time: {
1108 TIME_STRUCT * dt = new TIME_STRUCT;
1109 tmpStorage.append( qAutoDeleter(dt) );
1110 QTime qdt = val.toTime();
1111 dt->hour = qdt.hour();
1112 dt->minute = qdt.minute();
1113 dt->second = qdt.second();
1114 r = SQLBindParameter( d->hStmt,
1115 para,
1116 qParamType[ (int)extension()->values[ it.data() ].typ ],
1117 SQL_C_TIME,
1118 SQL_TIME,
1119 0,
1120 0,
1121 (void *) dt,
1122 (QSQLLEN)0,
1123 *ind == SQL_NULL_DATA ? ind : NULL );
1124 break; }
1125 case QVariant::DateTime: {
1126 TIMESTAMP_STRUCT * dt = new TIMESTAMP_STRUCT;
1127 tmpStorage.append( qAutoDeleter(dt) );
1128 QDateTime qdt = val.toDateTime();
1129 dt->year = qdt.date().year();
1130 dt->month = qdt.date().month();
1131 dt->day = qdt.date().day();
1132 dt->hour = qdt.time().hour();
1133 dt->minute = qdt.time().minute();
1134 dt->second = qdt.time().second();
1135 dt->fraction = 0;
1136 r = SQLBindParameter( d->hStmt,
1137 para,
1138 qParamType[ (int)extension()->values[ it.data() ].typ ],
1139 SQL_C_TIMESTAMP,
1140 SQL_TIMESTAMP,
1141 0,
1142 0,
1143 (void *) dt,
1144 (QSQLLEN)0,
1145 *ind == SQL_NULL_DATA ? ind : NULL );
1146 break; }
1147 case QVariant::Int: {
1148 int * v = new int( val.toInt() );
1149 tmpStorage.append( qAutoDeleter(v) );
1150 r = SQLBindParameter( d->hStmt,
1151 para,
1152 qParamType[ (int)extension()->values[ it.data() ].typ ],
1153 SQL_C_SLONG,
1154 SQL_INTEGER,
1155 0,
1156 0,
1157 (void *) v,
1158 (QSQLLEN)0,
1159 *ind == SQL_NULL_DATA ? ind : NULL );
1160 break; }
1161 case QVariant::Double: {
1162 double * v = new double( val.toDouble() );
1163 tmpStorage.append( qAutoDeleter(v) );
1164 r = SQLBindParameter( d->hStmt,
1165 para,
1166 qParamType[ (int)extension()->values[ it.data() ].typ ],
1167 SQL_C_DOUBLE,
1168 SQL_DOUBLE,
1169 0,
1170 0,
1171 (void *) v,
1172 (QSQLLEN)0,
1173 *ind == SQL_NULL_DATA ? ind : NULL );
1174 break; }
1175 case QVariant::ByteArray: {
1176 if ( *ind != SQL_NULL_DATA ) {
1177 *ind = val.asByteArray().size();
1178 }
1179 r = SQLBindParameter( d->hStmt,
1180 para,
1181 qParamType[ (int)extension()->values[ it.data() ].typ ],
1182 SQL_C_BINARY,
1183 SQL_LONGVARBINARY,
1184 val.asByteArray().size(),
1185 0,
1186 (void *) val.asByteArray().data(),
1187 (QSQLLEN)val.asByteArray().size(),
1188 ind );
1189 break; }
1190#ifndef Q_ODBC_VERSION_2
1191 case QVariant::String:
1192 if ( d->unicode ) {
1193 QString * str = new QString( val.asString() );
1194 str->ucs2();
1195 int len = str->length()*2;
1196 tmpStorage.append( qAutoDeleter(str) );
1197 r = SQLBindParameter( d->hStmt,
1198 para,
1199 qParamType[ (int)extension()->values[ it.data() ].typ ],
1200 SQL_C_WCHAR,
1201 len > 8000 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1202 len > 8000 ? len : 0,
1203 0,
1204 (void *) str->unicode(),
1205 (QSQLLEN) len,
1206 ind );
1207 break;
1208 }
1209#endif
1210 // fall through
1211 default: {
1212 QCString * str = new QCString( val.asString().local8Bit() );
1213 tmpStorage.append( qAutoDeleter(str) );
1214 r = SQLBindParameter( d->hStmt,
1215 para,
1216 qParamType[ (int)extension()->values[ it.data() ].typ ],
1217 SQL_C_CHAR,
1218 str->length() > 4000 ? SQL_LONGVARCHAR : SQL_VARCHAR,
1219 str->length() + 1,
1220 0,
1221 (void *) str->data(),
1222 (QSQLLEN)(str->length() + 1),
1223 ind );
1224 break; }
1225 }
1226 para++;
1227 if ( r != SQL_SUCCESS ) {
1228#ifdef QT_CHECK_RANGE
1229 qWarning( "QODBCResult::exec: unable to bind variable: %s", qODBCWarn( d ).local8Bit().data() );
1230#endif
1231 setLastError( qMakeError( "Unable to bind variable", QSqlError::Statement, d ) );
1232 return FALSE;
1233 }
1234 }
1235 }
1236 r = SQLExecute( d->hStmt );
1237 if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
1238#ifdef QT_CHECK_RANGE
1239 qWarning( "QODBCResult::exec: Unable to execute statement: %s", qODBCWarn( d ).local8Bit().data() );
1240#endif
1241 setLastError( qMakeError( "Unable to execute statement", QSqlError::Statement, d ) );
1242 return FALSE;
1243 }
1244 SQLSMALLINT count;
1245 r = SQLNumResultCols( d->hStmt, &count );
1246 if ( count ) {
1247 setSelect( TRUE );
1248 for ( int i = 0; i < count; ++i ) {
1249 d->rInf.append( qMakeFieldInfo( d, i ) );
1250 }
1251 } else {
1252 setSelect( FALSE );
1253 }
1254 setActive( TRUE );
1255
1256 //get out parameters
1257 if ( extension()->index.count() > 0 ) {
1258 QMap<int, QString>::Iterator it;
1259 for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
1260
1261 SQLINTEGER* indPtr = qAutoDeleterData( (QAutoDeleter<SQLINTEGER>*)tmpStorage.getFirst() );
1262 if ( !indPtr )
1263 return FALSE;
1264 bool isNull = (*indPtr == SQL_NULL_DATA);
1265 tmpStorage.removeFirst();
1266
1267 QVariant::Type type = extension()->values[ it.data() ].value.type();
1268 if ( isNull ) {
1269 QVariant v;
1270 v.cast(type);
1271 extension()->values[ it.data() ].value = v;
1272 if (type != QVariant::ByteArray)
1273 tmpStorage.removeFirst();
1274 continue;
1275 }
1276
1277 switch (type) {
1278 case QVariant::Date: {
1279 DATE_STRUCT * ds = qAutoDeleterData( (QAutoDeleter<DATE_STRUCT>*)tmpStorage.getFirst() );
1280 extension()->values[ it.data() ].value = QVariant( QDate( ds->year, ds->month, ds->day ) );
1281 break; }
1282 case QVariant::Time: {
1283 TIME_STRUCT * dt = qAutoDeleterData( (QAutoDeleter<TIME_STRUCT>*)tmpStorage.getFirst() );
1284 extension()->values[ it.data() ].value = QVariant( QTime( dt->hour, dt->minute, dt->second ) );
1285 break; }
1286 case QVariant::DateTime: {
1287 TIMESTAMP_STRUCT * dt = qAutoDeleterData( (QAutoDeleter<TIMESTAMP_STRUCT>*)tmpStorage.getFirst() );
1288 extension()->values[ it.data() ].value = QVariant( QDateTime( QDate( dt->year, dt->month, dt->day ),
1289 QTime( dt->hour, dt->minute, dt->second ) ) );
1290 break; }
1291 case QVariant::Int: {
1292 int * v = qAutoDeleterData( (QAutoDeleter<int>*)tmpStorage.getFirst() );
1293 extension()->values[ it.data() ].value = QVariant( *v );
1294 break; }
1295 case QVariant::Double: {
1296 double * v = qAutoDeleterData( (QAutoDeleter<double>*)tmpStorage.getFirst() );
1297 extension()->values[ it.data() ].value = QVariant( *v );
1298 break; }
1299 case QVariant::ByteArray:
1300 break;
1301 case QVariant::String:
1302 if ( d->unicode ) {
1303 QString * str = qAutoDeleterData( (QAutoDeleter<QString>*)tmpStorage.getFirst() );
1304 extension()->values[ it.data() ].value = QVariant( *str );
1305 break;
1306 }
1307 // fall through
1308 default: {
1309 QCString * str = qAutoDeleterData( (QAutoDeleter<QCString>*)tmpStorage.getFirst() );
1310 extension()->values[ it.data() ].value = QVariant( *str );
1311 break; }
1312 }
1313 if (type != QVariant::ByteArray)
1314 tmpStorage.removeFirst();
1315 }
1316 }
1317
1318 return TRUE;
1319}
1320
1321////////////////////////////////////////
1322
1323
1324QODBCDriver::QODBCDriver( QObject * parent, const char * name )
1325 : QSqlDriver(parent,name ? name : "QODBC")
1326{
1327 init();
1328}
1329
1330QODBCDriver::QODBCDriver( SQLHANDLE env, SQLHANDLE con, QObject * parent, const char * name )
1331 : QSqlDriver(parent,name ? name : "QODBC")
1332{
1333 init();
1334 d->hEnv = env;
1335 d->hDbc = con;
1336 if ( env && con ) {
1337 setOpen( TRUE );
1338 setOpenError( FALSE );
1339 }
1340}
1341
1342void QODBCDriver::init()
1343{
1344 qSqlOpenExtDict()->insert( this, new QODBCOpenExtension(this) );
1345 d = new QODBCPrivate();
1346}
1347
1348QODBCDriver::~QODBCDriver()
1349{
1350 cleanup();
1351 delete d;
1352 if ( !qSqlOpenExtDict()->isEmpty() ) {
1353 QSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
1354 delete ext;
1355 }
1356}
1357
1358bool QODBCDriver::hasFeature( DriverFeature f ) const
1359{
1360 switch ( f ) {
1361 case Transactions: {
1362 if ( !d->hDbc )
1363 return FALSE;
1364 SQLUSMALLINT txn;
1365 SQLSMALLINT t;
1366 int r = SQLGetInfo( d->hDbc,
1367 (SQLUSMALLINT)SQL_TXN_CAPABLE,
1368 &txn,
1369 sizeof(txn),
1370 &t);
1371 if ( r != SQL_SUCCESS || txn == SQL_TC_NONE )
1372 return FALSE;
1373 else
1374 return TRUE;
1375 }
1376 case QuerySize:
1377 return FALSE;
1378 case BLOB:
1379 return TRUE;
1380 case Unicode:
1381 return d->unicode;
1382 case PreparedQueries:
1383 return TRUE;
1384 case PositionalPlaceholders:
1385 return TRUE;
1386 default:
1387 return FALSE;
1388 }
1389}
1390
1391bool QODBCDriver::open( const QString&,
1392 const QString&,
1393 const QString&,
1394 const QString&,
1395 int )
1396{
1397 qWarning("QODBCDriver::open(): This version of open() is no longer supported." );
1398 return FALSE;
1399}
1400
1401bool QODBCDriver::open( const QString & db,
1402 const QString & user,
1403 const QString & password,
1404 const QString &,
1405 int,
1406 const QString& connOpts )
1407{
1408 if ( isOpen() )
1409 close();
1410 SQLRETURN r;
1411 r = SQLAllocHandle( SQL_HANDLE_ENV,
1412 SQL_NULL_HANDLE,
1413 &d->hEnv);
1414 if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
1415#ifdef QT_CHECK_RANGE
1416 qSqlWarning( "QODBCDriver::open: Unable to allocate environment", d );
1417#endif
1418 setOpenError( TRUE );
1419 return FALSE;
1420 }
1421 r = SQLSetEnvAttr( d->hEnv,
1422 SQL_ATTR_ODBC_VERSION,
1423 (SQLPOINTER)SQL_OV_ODBC2,
1424 SQL_IS_UINTEGER );
1425 r = SQLAllocHandle( SQL_HANDLE_DBC,
1426 d->hEnv,
1427 &d->hDbc);
1428 if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
1429#ifdef QT_CHECK_RANGE
1430 qSqlWarning( "QODBCDriver::open: Unable to allocate connection", d );
1431#endif
1432 setOpenError( TRUE );
1433 return FALSE;
1434 }
1435
1436 if ( !d->setConnectionOptions( connOpts ) )
1437 return FALSE;
1438
1439 // Create the connection string
1440 QString connQStr;
1441 // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
1442 if ( db.contains(".dsn") )
1443 connQStr = "FILEDSN=" + db;
1444 else if ( db.contains( "DRIVER" ) || db.contains( "SERVER" ) )
1445 connQStr = db;
1446 else
1447 connQStr = "DSN=" + db;
1448 connQStr += ";UID=" + user + ";PWD=" + password;
1449 SQLSMALLINT cb;
1450 SQLTCHAR connOut[1024];
1451 r = SQLDriverConnect( d->hDbc,
1452 NULL,
1453#ifdef UNICODE
1454 (SQLWCHAR*)connQStr.unicode(),
1455#else
1456 (SQLCHAR*)connQStr.latin1(),
1457#endif
1458 (SQLSMALLINT)connQStr.length(),
1459 connOut,
1460 1024,
1461 &cb,
1462 SQL_DRIVER_NOPROMPT );
1463 if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
1464 setLastError( qMakeError( "Unable to connect", QSqlError::Connection, d ) );
1465 setOpenError( TRUE );
1466 return FALSE;
1467 }
1468
1469 if ( !d->checkDriver() ) {
1470 setLastError( qMakeError( "Unable to connect - Driver doesn't support all needed functionality", QSqlError::Connection, d ) );
1471 setOpenError( TRUE );
1472 return FALSE;
1473 }
1474
1475 d->checkUnicode();
1476 d->checkSchemaUsage();
1477
1478 setOpen( TRUE );
1479 setOpenError( FALSE );
1480 return TRUE;
1481}
1482
1483void QODBCDriver::close()
1484{
1485 cleanup();
1486 setOpen( FALSE );
1487 setOpenError( FALSE );
1488}
1489
1490void QODBCDriver::cleanup()
1491{
1492 SQLRETURN r;
1493 if ( !d )
1494 return;
1495
1496 if( d->hDbc ) {
1497 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1498 if ( isOpen() ) {
1499 r = SQLDisconnect( d->hDbc );
1500#ifdef QT_CHECK_RANGE
1501 if ( r != SQL_SUCCESS )
1502 qSqlWarning( "QODBCDriver::disconnect: Unable to disconnect datasource", d );
1503#endif
1504 }
1505
1506 r = SQLFreeHandle( SQL_HANDLE_DBC, d->hDbc );
1507#ifdef QT_CHECK_RANGE
1508 if ( r != SQL_SUCCESS )
1509 qSqlWarning( "QODBCDriver::cleanup: Unable to free connection handle", d );
1510#endif
1511 d->hDbc = 0;
1512 }
1513
1514 if ( d->hEnv ) {
1515 r = SQLFreeHandle( SQL_HANDLE_ENV, d->hEnv );
1516#ifdef QT_CHECK_RANGE
1517 if ( r != SQL_SUCCESS )
1518 qSqlWarning( "QODBCDriver::cleanup: Unable to free environment handle", d );
1519#endif
1520 d->hEnv = 0;
1521 }
1522}
1523
1524// checks whether the server can return char, varchar and longvarchar
1525// as two byte unicode characters
1526void QODBCPrivate::checkUnicode()
1527{
1528#if defined(Q_WS_WIN)
1529 if ( !qt_winunicode ) {
1530 unicode = FALSE;
1531 return;
1532 }
1533#endif
1534 SQLRETURN r;
1535 SQLUINTEGER fFunc;
1536
1537 unicode = FALSE;
1538 r = SQLGetInfo( hDbc,
1539 SQL_CONVERT_CHAR,
1540 (SQLPOINTER)&fFunc,
1541 sizeof(fFunc),
1542 NULL );
1543 if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WCHAR ) ) {
1544 sql_char_type = QVariant::String;
1545 unicode = TRUE;
1546 }
1547
1548 r = SQLGetInfo( hDbc,
1549 SQL_CONVERT_VARCHAR,
1550 (SQLPOINTER)&fFunc,
1551 sizeof(fFunc),
1552 NULL );
1553 if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WVARCHAR ) ) {
1554 sql_varchar_type = QVariant::String;
1555 unicode = TRUE;
1556 }
1557
1558 r = SQLGetInfo( hDbc,
1559 SQL_CONVERT_LONGVARCHAR,
1560 (SQLPOINTER)&fFunc,
1561 sizeof(fFunc),
1562 NULL );
1563 if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WLONGVARCHAR ) ) {
1564 sql_longvarchar_type = QVariant::String;
1565 unicode = TRUE;
1566 }
1567}
1568
1569bool QODBCPrivate::checkDriver() const
1570{
1571#ifdef ODBC_CHECK_DRIVER
1572 // do not query for SQL_API_SQLFETCHSCROLL because it can't be used at this time
1573 static const SQLUSMALLINT reqFunc[] = {
1574 SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
1575 SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
1576 SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
1577 };
1578
1579 // these functions are optional
1580 static const SQLUSMALLINT optFunc[] = {
1581 SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
1582 };
1583
1584 SQLRETURN r;
1585 SQLUSMALLINT sup;
1586
1587
1588 int i;
1589 // check the required functions
1590 for ( i = 0; reqFunc[ i ] != 0; ++i ) {
1591
1592 r = SQLGetFunctions( hDbc, reqFunc[ i ], &sup );
1593
1594#ifdef QT_CHECK_RANGE
1595 if ( r != SQL_SUCCESS ) {
1596 qSqlWarning( "QODBCDriver::checkDriver: Cannot get list of supported functions", this );
1597 return FALSE;
1598 }
1599#endif
1600 if ( sup == SQL_FALSE ) {
1601#ifdef QT_CHECK_RANGE
1602 qWarning ( "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (%d). "
1603 "Please look at the Qt SQL Module Driver documentation for more information.", reqFunc[ i ] );
1604#endif
1605 return FALSE;
1606 }
1607 }
1608
1609 // these functions are optional and just generate a warning
1610 for ( i = 0; optFunc[ i ] != 0; ++i ) {
1611
1612 r = SQLGetFunctions( hDbc, optFunc[ i ], &sup );
1613
1614#ifdef QT_CHECK_RANGE
1615 if ( r != SQL_SUCCESS ) {
1616 qSqlWarning( "QODBCDriver::checkDriver: Cannot get list of supported functions", this );
1617 return FALSE;
1618 }
1619#endif
1620 if ( sup == SQL_FALSE ) {
1621#ifdef QT_CHECK_RANGE
1622 qWarning( "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (%d)", optFunc[ i ] );
1623#endif
1624 return TRUE;
1625 }
1626 }
1627#endif //ODBC_CHECK_DRIVER
1628
1629 return TRUE;
1630}
1631
1632void QODBCPrivate::checkSchemaUsage()
1633{
1634 SQLRETURN r;
1635 SQLUINTEGER val;
1636
1637 r = SQLGetInfo(hDbc,
1638 SQL_SCHEMA_USAGE,
1639 (SQLPOINTER) &val,
1640 sizeof(val),
1641 NULL);
1642 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1643 useSchema = (val != 0);
1644}
1645
1646QSqlQuery QODBCDriver::createQuery() const
1647{
1648 return QSqlQuery( new QODBCResult( this, d ) );
1649}
1650
1651bool QODBCDriver::beginTransaction()
1652{
1653 if ( !isOpen() ) {
1654#ifdef QT_CHECK_RANGE
1655 qWarning(" QODBCDriver::beginTransaction: Database not open" );
1656#endif
1657 return FALSE;
1658 }
1659 SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
1660 SQLRETURN r = SQLSetConnectAttr( d->hDbc,
1661 SQL_ATTR_AUTOCOMMIT,
1662 (SQLPOINTER)ac,
1663 sizeof(ac) );
1664 if ( r != SQL_SUCCESS ) {
1665 setLastError( qMakeError( "Unable to disable autocommit", QSqlError::Transaction, d ) );
1666 return FALSE;
1667 }
1668 return TRUE;
1669}
1670
1671bool QODBCDriver::commitTransaction()
1672{
1673 if ( !isOpen() ) {
1674#ifdef QT_CHECK_RANGE
1675 qWarning(" QODBCDriver::commitTransaction: Database not open" );
1676#endif
1677 return FALSE;
1678 }
1679 SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC,
1680 d->hDbc,
1681 SQL_COMMIT );
1682 if ( r != SQL_SUCCESS ) {
1683 setLastError( qMakeError("Unable to commit transaction", QSqlError::Transaction, d ) );
1684 return FALSE;
1685 }
1686 return endTrans();
1687}
1688
1689bool QODBCDriver::rollbackTransaction()
1690{
1691 if ( !isOpen() ) {
1692#ifdef QT_CHECK_RANGE
1693 qWarning(" QODBCDriver::rollbackTransaction: Database not open" );
1694#endif
1695 return FALSE;
1696 }
1697 SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC,
1698 d->hDbc,
1699 SQL_ROLLBACK );
1700 if ( r != SQL_SUCCESS ) {
1701 setLastError( qMakeError( "Unable to rollback transaction", QSqlError::Transaction, d ) );
1702 return FALSE;
1703 }
1704 return endTrans();
1705}
1706
1707bool QODBCDriver::endTrans()
1708{
1709 SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
1710 SQLRETURN r = SQLSetConnectAttr( d->hDbc,
1711 SQL_ATTR_AUTOCOMMIT,
1712 (SQLPOINTER)ac,
1713 sizeof(ac));
1714 if ( r != SQL_SUCCESS ) {
1715 setLastError( qMakeError( "Unable to enable autocommit", QSqlError::Transaction, d ) );
1716 return FALSE;
1717 }
1718 return TRUE;
1719}
1720
1721QStringList QODBCDriver::tables( const QString& typeName ) const
1722{
1723 QStringList tl;
1724 if ( !isOpen() )
1725 return tl;
1726 int type = typeName.toInt();
1727 SQLHANDLE hStmt;
1728
1729 SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
1730 d->hDbc,
1731 &hStmt );
1732 if ( r != SQL_SUCCESS ) {
1733#ifdef QT_CHECK_RANGE
1734 qSqlWarning( "QODBCDriver::tables: Unable to allocate handle", d );
1735#endif
1736 return tl;
1737 }
1738 r = SQLSetStmtAttr( hStmt,
1739 SQL_ATTR_CURSOR_TYPE,
1740 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1741 SQL_IS_UINTEGER );
1742 QString tableType;
1743 if ( typeName.isEmpty() || ((type & (int)QSql::Tables) == (int)QSql::Tables) )
1744 tableType += "TABLE,";
1745 if ( (type & (int)QSql::Views) == (int)QSql::Views )
1746 tableType += "VIEW,";
1747 if ( (type & (int)QSql::SystemTables) == (int)QSql::SystemTables )
1748 tableType += "SYSTEM TABLE,";
1749 if ( tableType.isEmpty() )
1750 return tl;
1751 tableType.truncate( tableType.length() - 1 );
1752
1753 r = SQLTables( hStmt,
1754 NULL,
1755 0,
1756 NULL,
1757 0,
1758 NULL,
1759 0,
1760#ifdef UNICODE
1761 (SQLWCHAR*)tableType.unicode(),
1762#else
1763 (SQLCHAR*)tableType.latin1(),
1764#endif
1765 tableType.length() /* characters, not bytes */ );
1766
1767#ifdef QT_CHECK_RANGE
1768 if ( r != SQL_SUCCESS )
1769 qSqlWarning( "QODBCDriver::tables Unable to execute table list", d );
1770#endif
1771 r = SQLFetchScroll( hStmt,
1772 SQL_FETCH_NEXT,
1773 0);
1774 while ( r == SQL_SUCCESS ) {
1775 bool isNull;
1776 QString fieldVal = qGetStringData( hStmt, 2, -1, isNull, d->unicode );
1777 tl.append( fieldVal );
1778 r = SQLFetchScroll( hStmt,
1779 SQL_FETCH_NEXT,
1780 0);
1781 }
1782
1783 r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
1784 if ( r!= SQL_SUCCESS )
1785 qSqlWarning( "QODBCDriver: Unable to free statement handle" + QString::number(r), d );
1786 return tl;
1787}
1788
1789QSqlIndex QODBCDriver::primaryIndex( const QString& tablename ) const
1790{
1791 QSqlIndex index( tablename );
1792 if ( !isOpen() )
1793 return index;
1794 bool usingSpecialColumns = FALSE;
1795 QSqlRecord rec = record( tablename );
1796
1797 SQLHANDLE hStmt;
1798 SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
1799 d->hDbc,
1800 &hStmt );
1801 if ( r != SQL_SUCCESS ) {
1802#ifdef QT_CHECK_RANGE
1803 qSqlWarning( "QODBCDriver::primaryIndex: Unable to list primary key", d );
1804#endif
1805 return index;
1806 }
1807 QString catalog, schema, table;
1808 d->splitTableQualifier( tablename, catalog, schema, table );
1809 r = SQLSetStmtAttr( hStmt,
1810 SQL_ATTR_CURSOR_TYPE,
1811 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1812 SQL_IS_UINTEGER );
1813 r = SQLPrimaryKeys( hStmt,
1814#ifdef UNICODE
1815 catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
1816#else
1817 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
1818#endif
1819 catalog.length(),
1820#ifdef UNICODE
1821 schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
1822#else
1823 schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
1824#endif
1825 schema.length(),
1826#ifdef UNICODE
1827 (SQLWCHAR*)table.unicode(),
1828#else
1829 (SQLCHAR*)table.latin1(),
1830#endif
1831 table.length() /* in characters, not in bytes */);
1832
1833 // if the SQLPrimaryKeys() call does not succeed (e.g the driver
1834 // does not support it) - try an alternative method to get hold of
1835 // the primary index (e.g MS Access and FoxPro)
1836 if ( r != SQL_SUCCESS ) {
1837 r = SQLSpecialColumns( hStmt,
1838 SQL_BEST_ROWID,
1839#ifdef UNICODE
1840 catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
1841#else
1842 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
1843#endif
1844 catalog.length(),
1845#ifdef UNICODE
1846 schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
1847#else
1848 schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
1849#endif
1850 schema.length(),
1851#ifdef UNICODE
1852 (SQLWCHAR*)table.unicode(),
1853#else
1854 (SQLCHAR*)table.latin1(),
1855#endif
1856
1857 table.length(),
1858 SQL_SCOPE_CURROW,
1859 SQL_NULLABLE );
1860
1861 if ( r != SQL_SUCCESS ) {
1862#ifdef QT_CHECK_RANGE
1863 qSqlWarning( "QODBCDriver::primaryIndex: Unable to execute primary key list", d );
1864#endif
1865 } else {
1866 usingSpecialColumns = TRUE;
1867 }
1868 }
1869 r = SQLFetchScroll( hStmt,
1870 SQL_FETCH_NEXT,
1871 0 );
1872 bool isNull;
1873 int fakeId = 0;
1874 QString cName, idxName;
1875 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
1876 while ( r == SQL_SUCCESS ) {
1877 if ( usingSpecialColumns ) {
1878 cName = qGetStringData( hStmt, 1, -1, isNull, d->unicode ); // column name
1879 idxName = QString::number( fakeId++ ); // invent a fake index name
1880 } else {
1881 cName = qGetStringData( hStmt, 3, -1, isNull, d->unicode ); // column name
1882 idxName = qGetStringData( hStmt, 5, -1, isNull, d->unicode ); // pk index name
1883 }
1884 QSqlField *fld = rec.field(cName);
1885 if (fld)
1886 index.append(*fld);
1887 index.setName( idxName );
1888 r = SQLFetchScroll( hStmt,
1889 SQL_FETCH_NEXT,
1890 0 );
1891 }
1892 r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
1893 if ( r!= SQL_SUCCESS )
1894 qSqlWarning( "QODBCDriver: Unable to free statement handle" + QString::number(r), d );
1895 return index;
1896}
1897
1898QSqlRecord QODBCDriver::record( const QString& tablename ) const
1899{
1900 return recordInfo( tablename ).toRecord();
1901}
1902
1903QSqlRecord QODBCDriver::record( const QSqlQuery& query ) const
1904{
1905 return recordInfo( query ).toRecord();
1906}
1907
1908QSqlRecordInfo QODBCDriver::recordInfo( const QString& tablename ) const
1909{
1910 QSqlRecordInfo fil;
1911 if ( !isOpen() )
1912 return fil;
1913
1914 SQLHANDLE hStmt;
1915 QString catalog, schema, table;
1916 d->splitTableQualifier( tablename, catalog, schema, table );
1917 SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
1918 d->hDbc,
1919 &hStmt );
1920 if ( r != SQL_SUCCESS ) {
1921#ifdef QT_CHECK_RANGE
1922 qSqlWarning( "QODBCDriver::record: Unable to allocate handle", d );
1923#endif
1924 return fil;
1925 }
1926 r = SQLSetStmtAttr( hStmt,
1927 SQL_ATTR_CURSOR_TYPE,
1928 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1929 SQL_IS_UINTEGER );
1930 r = SQLColumns( hStmt,
1931#ifdef UNICODE
1932 catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
1933#else
1934 catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
1935#endif
1936 catalog.length(),
1937#ifdef UNICODE
1938 schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
1939#else
1940 schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
1941#endif
1942 schema.length(),
1943#ifdef UNICODE
1944 (SQLWCHAR*)table.unicode(),
1945#else
1946 (SQLCHAR*)table.latin1(),
1947#endif
1948 table.length(),
1949 NULL,
1950 0 );
1951#ifdef QT_CHECK_RANGE
1952 if ( r != SQL_SUCCESS )
1953 qSqlWarning( "QODBCDriver::record: Unable to execute column list", d );
1954#endif
1955 r = SQLFetchScroll( hStmt,
1956 SQL_FETCH_NEXT,
1957 0);
1958 // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
1959 while ( r == SQL_SUCCESS ) {
1960
1961 fil.append( qMakeFieldInfo( hStmt, d ) );
1962
1963 r = SQLFetchScroll( hStmt,
1964 SQL_FETCH_NEXT,
1965 0);
1966 }
1967
1968 r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
1969 if ( r!= SQL_SUCCESS )
1970 qSqlWarning( "QODBCDriver: Unable to free statement handle " + QString::number(r), d );
1971
1972 return fil;
1973}
1974
1975QSqlRecordInfo QODBCDriver::recordInfo( const QSqlQuery& query ) const
1976{
1977 QSqlRecordInfo fil;
1978 if ( !isOpen() )
1979 return fil;
1980 if ( query.isActive() && query.driver() == this ) {
1981 QODBCResult* result = (QODBCResult*)query.result();
1982 fil = result->d->rInf;
1983 }
1984 return fil;
1985}
1986
1987SQLHANDLE QODBCDriver::environment()
1988{
1989 return d->hEnv;
1990}
1991
1992SQLHANDLE QODBCDriver::connection()
1993{
1994 return d->hDbc;
1995}
1996
1997QString QODBCDriver::formatValue( const QSqlField* field,
1998 bool trimStrings ) const
1999{
2000 QString r;
2001 if ( field->isNull() ) {
2002 r = nullText();
2003 } else if ( field->type() == QVariant::DateTime ) {
2004 // Use an escape sequence for the datetime fields
2005 if ( field->value().toDateTime().isValid() ){
2006 QDate dt = field->value().toDateTime().date();
2007 QTime tm = field->value().toDateTime().time();
2008 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
2009 r = "{ ts '" +
2010 QString::number(dt.year()) + "-" +
2011 QString::number(dt.month()).rightJustify( 2, '0', TRUE ) + "-" +
2012 QString::number(dt.day()).rightJustify( 2, '0', TRUE ) + " " +
2013 tm.toString() +
2014 "' }";
2015 } else
2016 r = nullText();
2017 } else if ( field->type() == QVariant::ByteArray ) {
2018 QByteArray ba = field->value().toByteArray();
2019 QString res;
2020 static const char hexchars[] = "0123456789abcdef";
2021 for ( uint i = 0; i < ba.size(); ++i ) {
2022 uchar s = (uchar) ba[(int)i];
2023 res += hexchars[s >> 4];
2024 res += hexchars[s & 0x0f];
2025 }
2026 r = "0x" + res;
2027 } else {
2028 r = QSqlDriver::formatValue( field, trimStrings );
2029 }
2030 return r;
2031}
Note: See TracBrowser for help on using the repository browser.