summaryrefslogtreecommitdiffstats
path: root/experimental/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'experimental/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp')
-rw-r--r--experimental/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp2035
1 files changed, 2035 insertions, 0 deletions
diff --git a/experimental/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp b/experimental/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp
new file mode 100644
index 000000000..fda38a8e2
--- /dev/null
+++ b/experimental/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp
@@ -0,0 +1,2035 @@
+/****************************************************************************
+**
+** Implementation of ODBC driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA.
+**
+** This file is part of the sql module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "tqsql_odbc.h"
+#include <tqsqlrecord.h>
+
+#if defined (TQ_OS_WIN32)
+#include <tqt_windows.h>
+#include <tqapplication.h>
+#endif
+#include <tqdatetime.h>
+#include <private/tqsqlextension_p.h>
+#include <private/tqinternal_p.h>
+#include <stdlib.h>
+
+// undefine this to prevent initial check of the ODBC driver
+#define ODBC_CHECK_DRIVER
+
+#if defined(TQ_ODBC_VERSION_2)
+//crude hack to get non-tqunicode capable driver managers to work
+# undef UNICODE
+# define STQLTCHAR STQLCHAR
+# define STQL_C_WCHAR STQL_C_CHAR
+#endif
+
+// newer platform SDKs use STQLLEN instead of STQLINTEGER
+#ifdef STQLLEN
+# define TQSTQLLEN STQLLEN
+#else
+# define TQSTQLLEN STQLINTEGER
+#endif
+
+#ifdef STQLULEN
+# define TQSQLULEN STQLULEN
+#else
+# define TQSQLULEN STQLUINTEGER
+#endif
+
+
+static const TQSTQLLEN COLNAMESIZE = 256;
+//Map TQt parameter types to ODBC types
+static const STQLSMALLINT qParamType[ 4 ] = { STQL_PARAM_INPUT, STQL_PARAM_INPUT, STQL_PARAM_OUTPUT, STQL_PARAM_INPUT_OUTPUT };
+
+class TQODBCPrivate
+{
+public:
+ TQODBCPrivate()
+ : hEnv(0), hDbc(0), hStmt(0), useSchema(FALSE)
+ {
+ sql_char_type = sql_varchar_type = sql_longvarchar_type = TQVariant::CString;
+ tqunicode = FALSE;
+ }
+
+ STQLHANDLE hEnv;
+ STQLHANDLE hDbc;
+ STQLHANDLE hStmt;
+
+ bool tqunicode;
+ bool useSchema;
+ TQVariant::Type sql_char_type;
+ TQVariant::Type sql_varchar_type;
+ TQVariant::Type sql_longvarchar_type;
+
+ TQSqlRecordInfo rInf;
+
+ bool checkDriver() const;
+ void checkUnicode();
+ void checkSchemaUsage();
+ bool setConnectionOptions( const TQString& connOpts );
+ void splitTableQualifier(const TQString &qualifier, TQString &catalog,
+ TQString &schema, TQString &table);
+};
+
+class TQODBCPreparedExtension : public TQSqlExtension
+{
+public:
+ TQODBCPreparedExtension( TQODBCResult * r )
+ : result( r ) {}
+
+ bool prepare( const TQString& query )
+ {
+ return result->prepare( query );
+ }
+
+ bool exec()
+ {
+ return result->exec();
+ }
+
+ TQODBCResult * result;
+};
+
+TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict();
+
+class TQODBCOpenExtension : public TQSqlOpenExtension
+{
+public:
+ TQODBCOpenExtension( TQODBCDriver *dri )
+ : TQSqlOpenExtension(), driver(dri) {}
+ ~TQODBCOpenExtension() {}
+
+ bool open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts );
+private:
+ TQODBCDriver *driver;
+};
+
+bool TQODBCOpenExtension::open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+static TQString qWarnODBCHandle(int handleType, STQLHANDLE handle)
+{
+ STQLINTEGER nativeCode_;
+ STQLSMALLINT msgLen;
+ STQLRETURN r = STQL_ERROR;
+ STQLTCHAR state_[STQL_STQLSTATE_SIZE+1];
+ STQLTCHAR description_[STQL_MAX_MESSAGE_LENGTH];
+ r = STQLGetDiagRec( handleType,
+ handle,
+ 1,
+ (STQLTCHAR*)state_,
+ &nativeCode_,
+ (STQLTCHAR*)description_,
+ STQL_MAX_MESSAGE_LENGTH-1, /* in bytes, not in characters */
+ &msgLen);
+ if ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO )
+#ifdef UNICODE
+ return TQString( (const TQChar*)description_, (uint)msgLen );
+#else
+ return TQString::fromLocal8Bit( (const char*)description_ );
+#endif
+ return TQString::null;
+}
+
+static TQString qODBCWarn( const TQODBCPrivate* odbc)
+{
+ return ( qWarnODBCHandle( STQL_HANDLE_ENV, odbc->hEnv ) + " "
+ + qWarnODBCHandle( STQL_HANDLE_DBC, odbc->hDbc ) + " "
+ + qWarnODBCHandle( STQL_HANDLE_STMT, odbc->hStmt ) );
+}
+
+static void qSqlWarning( const TQString& message, const TQODBCPrivate* odbc )
+{
+#ifdef TQT_CHECK_RANGE
+ qWarning( "%s\tError: %s", message.local8Bit().data(), qODBCWarn( odbc ).local8Bit().data() );
+#endif
+}
+
+static TQSqlError qMakeError( const TQString& err, int type, const TQODBCPrivate* p )
+{
+ return TQSqlError( "TQODBC3: " + err, qODBCWarn(p), type );
+}
+
+static TQVariant::Type qDecodeODBCType( STQLSMALLINT sqltype, const TQODBCPrivate* p )
+{
+ TQVariant::Type type = TQVariant::Invalid;
+ switch ( sqltype ) {
+ case STQL_DECIMAL:
+ case STQL_NUMERIC:
+ case STQL_REAL:
+ case STQL_FLOAT:
+ case STQL_DOUBLE:
+ type = TQVariant::Double;
+ break;
+ case STQL_SMALLINT:
+ case STQL_INTEGER:
+ case STQL_BIT:
+ case STQL_TINYINT:
+ type = TQVariant::Int;
+ break;
+ case STQL_BIGINT:
+ type = TQVariant::LongLong;
+ break;
+ case STQL_BINARY:
+ case STQL_VARBINARY:
+ case STQL_LONGVARBINARY:
+ type = TQVariant::ByteArray;
+ break;
+ case STQL_DATE:
+ case STQL_TYPE_DATE:
+ type = TQVariant::Date;
+ break;
+ case STQL_TIME:
+ case STQL_TYPE_TIME:
+ type = TQVariant::Time;
+ break;
+ case STQL_TIMESTAMP:
+ case STQL_TYPE_TIMESTAMP:
+ type = TQVariant::DateTime;
+ break;
+#ifndef TQ_ODBC_VERSION_2
+ case STQL_WCHAR:
+ case STQL_WVARCHAR:
+ case STQL_WLONGVARCHAR:
+ type = TQVariant::String;
+ break;
+#endif
+ case STQL_CHAR:
+ type = p->sql_char_type;
+ break;
+ case STQL_VARCHAR:
+ type = p->sql_varchar_type;
+ break;
+ case STQL_LONGVARCHAR:
+ type = p->sql_longvarchar_type;
+ break;
+ default:
+ type = TQVariant::CString;
+ break;
+ }
+ return type;
+}
+
+static TQString qGetStringData( STQLHANDLE hStmt, int column, int colSize, bool& isNull, bool tqunicode = FALSE )
+{
+ TQString fieldVal;
+ STQLRETURN r = STQL_ERROR;
+ TQSTQLLEN lengthIndicator = 0;
+
+ if ( colSize <= 0 ) {
+ colSize = 256;
+ } else if ( colSize > 65536 ) { // limit buffer size to 64 KB
+ colSize = 65536;
+ } else {
+ colSize++; // make sure there is room for more than the 0 termination
+ if ( tqunicode ) {
+ colSize *= 2; // a tiny bit faster, since it saves a STQLGetData() call
+ }
+ }
+ char* buf = new char[ colSize ];
+ while ( TRUE ) {
+ r = STQLGetData( hStmt,
+ column+1,
+ tqunicode ? STQL_C_WCHAR : STQL_C_CHAR,
+ (STQLPOINTER)buf,
+ (TQSTQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == STQL_NULL_DATA || lengthIndicator == STQL_NO_TOTAL ) {
+ fieldVal = TQString::null;
+ isNull = TRUE;
+ break;
+ }
+ // if STQL_SUCCESS_WITH_INFO is returned, indicating that
+ // more data can be fetched, the length indicator does NOT
+ // contain the number of bytes returned - it tqcontains the
+ // total number of bytes that CAN be fetched
+ // colSize-1: remove 0 termination when there is more data to fetch
+ int rSize = (r == STQL_SUCCESS_WITH_INFO) ? (tqunicode ? colSize-2 : colSize-1) : lengthIndicator;
+ if ( tqunicode ) {
+ fieldVal += TQString( (TQChar*) buf, rSize / 2 );
+ } else {
+ buf[ rSize ] = 0;
+ fieldVal += buf;
+ }
+ if ( lengthIndicator < colSize ) {
+ // workaround for Drivermanagers that don't return STQL_NO_DATA
+ break;
+ }
+ } else if ( r == STQL_NO_DATA ) {
+ break;
+ } else {
+#ifdef TQT_CHECK_RANGE
+ qWarning( "qGetStringData: Error while fetching data (%d)", r );
+#endif
+ fieldVal = TQString::null;
+ break;
+ }
+ }
+ delete[] buf;
+ return fieldVal;
+}
+
+static TQByteArray qGetBinaryData( STQLHANDLE hStmt, int column, TQSTQLLEN& lengthIndicator, bool& isNull )
+{
+ TQByteArray fieldVal;
+ STQLSMALLINT colNameLen;
+ STQLSMALLINT colType;
+ TQSQLULEN colSize;
+ STQLSMALLINT colScale;
+ STQLSMALLINT nullable;
+ STQLRETURN r = STQL_ERROR;
+
+ STQLTCHAR colName[COLNAMESIZE];
+ r = STQLDescribeCol( hStmt,
+ column+1,
+ colName,
+ COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable );
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS )
+ qWarning( "qGetBinaryData: Unable to describe column %d", column );
+#endif
+ // STQLDescribeCol may return 0 if size cannot be determined
+ if (!colSize) {
+ colSize = 256;
+ }
+ if ( colSize > 65536 ) { // read the field in 64 KB chunks
+ colSize = 65536;
+ }
+ char * buf = new char[ colSize ];
+ while ( TRUE ) {
+ r = STQLGetData( hStmt,
+ column+1,
+ STQL_C_BINARY,
+ (STQLPOINTER) buf,
+ (TQSTQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == STQL_NULL_DATA ) {
+ isNull = TRUE;
+ break;
+ } else {
+ int rSize;
+ r == STQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
+ if ( lengthIndicator == STQL_NO_TOTAL ) { // size cannot be determined
+ rSize = colSize;
+ }
+ // NB! This is not a memleak - the mem will be deleted by TQByteArray when
+ // no longer ref'd
+ char * tmp = (char *) malloc( rSize + fieldVal.size() );
+ if ( fieldVal.size() ) {
+ memcpy( tmp, fieldVal.data(), fieldVal.size() );
+ }
+ memcpy( tmp + fieldVal.size(), buf, rSize );
+ fieldVal = fieldVal.assign( tmp, fieldVal.size() + rSize );
+
+ if ( r == STQL_SUCCESS ) { // the whole field was read in one chunk
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ delete [] buf;
+ return fieldVal;
+}
+
+static int qGetIntData( STQLHANDLE hStmt, int column, bool& isNull )
+{
+ TQSTQLLEN intbuf = 0;
+ isNull = FALSE;
+ TQSTQLLEN lengthIndicator = 0;
+ STQLRETURN r = STQLGetData( hStmt,
+ column+1,
+ STQL_C_SLONG,
+ (STQLPOINTER)&intbuf,
+ (TQSTQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) || lengthIndicator == STQL_NULL_DATA ) {
+ isNull = TRUE;
+ return 0;
+ }
+ return (int)intbuf;
+}
+
+static double qGetDoubleData( STQLHANDLE hStmt, int column, bool& isNull )
+{
+ STQLDOUBLE dblbuf;
+ TQSTQLLEN lengthIndicator = 0;
+ isNull = FALSE;
+ STQLRETURN r = STQLGetData( hStmt,
+ column+1,
+ STQL_C_DOUBLE,
+ (STQLPOINTER)&dblbuf,
+ (TQSTQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) || lengthIndicator == STQL_NULL_DATA ) {
+ isNull = TRUE;
+ return 0.0;
+ }
+
+ return (double) dblbuf;
+}
+
+static STQLBIGINT qGetBigIntData( STQLHANDLE hStmt, int column, bool& isNull )
+{
+ STQLBIGINT lngbuf = TQ_INT64_C( 0 );
+ isNull = FALSE;
+ TQSTQLLEN lengthIndicator = 0;
+ STQLRETURN r = STQLGetData( hStmt,
+ column+1,
+ STQL_C_SBIGINT,
+ (STQLPOINTER) &lngbuf,
+ (TQSTQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) || lengthIndicator == STQL_NULL_DATA )
+ isNull = TRUE;
+
+ return lngbuf;
+}
+
+// creates a TQSqlFieldInfo from a valid hStmt generated
+// by STQLColumns. The hStmt has to point to a valid position.
+static TQSqlFieldInfo qMakeFieldInfo( const STQLHANDLE hStmt, const TQODBCPrivate* p )
+{
+ bool isNull;
+ TQString fname = qGetStringData( hStmt, 3, -1, isNull, p->tqunicode );
+ int type = qGetIntData( hStmt, 4, isNull ); // column type
+ int required = qGetIntData( hStmt, 10, isNull ); // nullable-flag
+ // required can be STQL_NO_NULLS, STQL_NULLABLE or STQL_NULLABLE_UNKNOWN
+ if ( required == STQL_NO_NULLS ) {
+ required = 1;
+ } else if ( required == STQL_NULLABLE ) {
+ required = 0;
+ } else {
+ required = -1;
+ }
+ int size = qGetIntData( hStmt, 6, isNull ); // column size
+ int prec = qGetIntData( hStmt, 8, isNull ); // precision
+ return TQSqlFieldInfo( fname, qDecodeODBCType( type, p ), required, size, prec, TQVariant(), type );
+}
+
+static TQSqlFieldInfo qMakeFieldInfo( const TQODBCPrivate* p, int i )
+{
+ STQLSMALLINT colNameLen;
+ STQLSMALLINT colType;
+ TQSQLULEN colSize;
+ STQLSMALLINT colScale;
+ STQLSMALLINT nullable;
+ STQLRETURN r = STQL_ERROR;
+ STQLTCHAR colName[ COLNAMESIZE ];
+ r = STQLDescribeCol( p->hStmt,
+ i+1,
+ colName,
+ (TQSQLULEN)COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable);
+
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( TQString("qMakeField: Unable to describe column %1").arg(i), p );
+#endif
+ return TQSqlFieldInfo();
+ }
+#ifdef UNICODE
+ TQString qColName( (const TQChar*)colName, (uint)colNameLen );
+#else
+ TQString qColName = TQString::fromLocal8Bit( (const char*)colName );
+#endif
+ // nullable can be STQL_NO_NULLS, STQL_NULLABLE or STQL_NULLABLE_UNKNOWN
+ int required = -1;
+ if ( nullable == STQL_NO_NULLS ) {
+ required = 1;
+ } else if ( nullable == STQL_NULLABLE ) {
+ required = 0;
+ }
+ TQVariant::Type type = qDecodeODBCType( colType, p );
+ return TQSqlFieldInfo( qColName,
+ type,
+ required,
+ (int)colSize == 0 ? -1 : (int)colSize,
+ (int)colScale == 0 ? -1 : (int)colScale,
+ TQVariant(),
+ (int)colType );
+}
+
+bool TQODBCPrivate::setConnectionOptions( const TQString& connOpts )
+{
+ // Set any connection attributes
+ TQStringList raw = TQStringList::split( ';', connOpts );
+ TQStringList opts;
+ STQLRETURN r = STQL_SUCCESS;
+ TQMap<TQString, TQString> connMap;
+ for ( TQStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) {
+ TQString tmp( *it );
+ int idx;
+ if ( (idx = tmp.tqfind( '=' )) != -1 )
+ connMap[ tmp.left( idx ) ] = tmp.mid( idx + 1 ).simplifyWhiteSpace();
+ else
+ qWarning( "TQODBCDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ }
+ if ( connMap.count() ) {
+ TQMap<TQString, TQString>::ConstIterator it;
+ TQString opt, val;
+ STQLUINTEGER v = 0;
+ for ( it = connMap.begin(); it != connMap.end(); ++it ) {
+ opt = it.key().upper();
+ val = it.data().upper();
+ r = STQL_SUCCESS;
+ if ( opt == "STQL_ATTR_ACCESS_MODE" ) {
+ if ( val == "STQL_MODE_READ_ONLY" ) {
+ v = STQL_MODE_READ_ONLY;
+ } else if ( val == "STQL_MODE_READ_WRITE" ) {
+ v = STQL_MODE_READ_WRITE;
+ } else {
+ qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = STQLSetConnectAttr( hDbc, STQL_ATTR_ACCESS_MODE, (STQLPOINTER) v, 0 );
+ } else if ( opt == "STQL_ATTR_CONNECTION_TIMEOUT" ) {
+ v = val.toUInt();
+ r = STQLSetConnectAttr( hDbc, STQL_ATTR_CONNECTION_TIMEOUT, (STQLPOINTER) v, 0 );
+ } else if ( opt == "STQL_ATTR_LOGIN_TIMEOUT" ) {
+ v = val.toUInt();
+ r = STQLSetConnectAttr( hDbc, STQL_ATTR_LOGIN_TIMEOUT, (STQLPOINTER) v, 0 );
+ } else if ( opt == "STQL_ATTR_CURRENT_CATALOG" ) {
+ val.ucs2(); // 0 terminate
+ r = STQLSetConnectAttr( hDbc, STQL_ATTR_CURRENT_CATALOG,
+#ifdef UNICODE
+ (STQLWCHAR*) val.tqunicode(),
+#else
+ (STQLCHAR*) val.latin1(),
+#endif
+ STQL_NTS );
+ } else if ( opt == "STQL_ATTR_METADATA_ID" ) {
+ if ( val == "STQL_TRUE" ) {
+ v = STQL_TRUE;
+ } else if ( val == "STQL_FALSE" ) {
+ v = STQL_FALSE;
+ } else {
+ qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = STQLSetConnectAttr( hDbc, STQL_ATTR_METADATA_ID, (STQLPOINTER) v, 0 );
+ } else if ( opt == "STQL_ATTR_PACKET_SIZE" ) {
+ v = val.toUInt();
+ r = STQLSetConnectAttr( hDbc, STQL_ATTR_PACKET_SIZE, (STQLPOINTER) v, 0 );
+ } else if ( opt == "STQL_ATTR_TRACEFILE" ) {
+ val.ucs2(); // 0 terminate
+ r = STQLSetConnectAttr( hDbc, STQL_ATTR_TRACEFILE,
+#ifdef UNICODE
+ (STQLWCHAR*) val.tqunicode(),
+#else
+ (STQLCHAR*) val.latin1(),
+#endif
+ STQL_NTS );
+ } else if ( opt == "STQL_ATTR_TRACE" ) {
+ if ( val == "STQL_OPT_TRACE_OFF" ) {
+ v = STQL_OPT_TRACE_OFF;
+ } else if ( val == "STQL_OPT_TRACE_ON" ) {
+ v = STQL_OPT_TRACE_ON;
+ } else {
+ qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = STQLSetConnectAttr( hDbc, STQL_ATTR_TRACE, (STQLPOINTER) v, 0 );
+ }
+#ifdef TQT_CHECK_RANGE
+ else {
+ qWarning( TQString("TQODBCDriver::open: Unknown connection attribute '%1'").arg( opt ) );
+ }
+#endif
+ if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( TQString("TQODBCDriver::open: Unable to set connection attribute '%1'").arg( opt ), this );
+#endif
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void TQODBCPrivate::splitTableQualifier(const TQString & qualifier, TQString &catalog,
+ TQString &schema, TQString &table)
+{
+ if (!useSchema) {
+ table = qualifier;
+ return;
+ }
+ TQStringList l = TQStringList::split( ".", qualifier, TRUE );
+ if ( l.count() > 3 )
+ return; // can't possibly be a valid table qualifier
+ int i = 0, n = l.count();
+ if ( n == 1 ) {
+ table = qualifier;
+ } else {
+ for ( TQStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
+ if ( n == 3 ) {
+ if ( i == 0 ) {
+ catalog = *it;
+ } else if ( i == 1 ) {
+ schema = *it;
+ } else if ( i == 2 ) {
+ table = *it;
+ }
+ } else if ( n == 2 ) {
+ if ( i == 0 ) {
+ schema = *it;
+ } else if ( i == 1 ) {
+ table = *it;
+ }
+ }
+ i++;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+TQODBCResult::TQODBCResult( const TQODBCDriver * db, TQODBCPrivate* p )
+: TQSqlResult(db)
+{
+ d = new TQODBCPrivate();
+ (*d) = (*p);
+ setExtension( new TQODBCPreparedExtension( this ) );
+}
+
+TQODBCResult::~TQODBCResult()
+{
+ if ( d->hStmt && driver()->isOpen() ) {
+ STQLRETURN r = STQLFreeHandle( STQL_HANDLE_STMT, d->hStmt );
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver: Unable to free statement handle " + TQString::number(r), d );
+#endif
+ }
+
+ delete d;
+}
+
+bool TQODBCResult::reset ( const TQString& query )
+{
+ setActive( FALSE );
+ setAt( TQSql::BeforeFirst );
+ STQLRETURN r;
+
+ d->rInf.clear();
+ // Always reallocate the statement handle - the statement attributes
+ // are not reset if STQLFreeStmt() is called which causes some problems.
+ if ( d->hStmt ) {
+ r = STQLFreeHandle( STQL_HANDLE_STMT, d->hStmt );
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::reset: Unable to free statement handle", d );
+#endif
+ return FALSE;
+ }
+ }
+ r = STQLAllocHandle( STQL_HANDLE_STMT,
+ d->hDbc,
+ &d->hStmt );
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::reset: Unable to allocate statement handle", d );
+#endif
+ return FALSE;
+ }
+
+ if ( isForwardOnly() ) {
+ r = STQLSetStmtAttr( d->hStmt,
+ STQL_ATTR_CURSOR_TYPE,
+ (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY,
+ STQL_IS_UINTEGER );
+ } else {
+ r = STQLSetStmtAttr( d->hStmt,
+ STQL_ATTR_CURSOR_TYPE,
+ (STQLPOINTER)STQL_CURSOR_STATIC,
+ STQL_IS_UINTEGER );
+ }
+ if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::reset: Unable to set 'STQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
+#endif
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ r = STQLExecDirect( d->hStmt,
+ (STQLWCHAR*) query.tqunicode(),
+ (STQLINTEGER) query.length() );
+#else
+ TQCString query8 = query.local8Bit();
+ r = STQLExecDirect( d->hStmt,
+ (STQLCHAR*) query8.data(),
+ (STQLINTEGER) query8.length() );
+#endif
+ if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) {
+ setLastError( qMakeError( "Unable to execute statement", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ STQLSMALLINT count;
+ r = STQLNumResultCols( d->hStmt, &count );
+ if ( count ) {
+ setSelect( TRUE );
+ for ( int i = 0; i < count; ++i ) {
+ d->rInf.append( qMakeFieldInfo( d, i ) );
+ }
+ } else {
+ setSelect( FALSE );
+ }
+ setActive( TRUE );
+ return TRUE;
+}
+
+bool TQODBCResult::fetch(int i)
+{
+ if ( isForwardOnly() && i < at() )
+ return FALSE;
+ if ( i == at() )
+ return TRUE;
+ fieldCache.clear();
+ nullCache.clear();
+ int actualIdx = i + 1;
+ if ( actualIdx <= 0 ) {
+ setAt( TQSql::BeforeFirst );
+ return FALSE;
+ }
+ STQLRETURN r;
+ if ( isForwardOnly() ) {
+ bool ok = TRUE;
+ while ( ok && i > at() )
+ ok = fetchNext();
+ return ok;
+ } else {
+ r = STQLFetchScroll( d->hStmt,
+ STQL_FETCH_ABSOLUTE,
+ actualIdx );
+ }
+ if ( r != STQL_SUCCESS ){
+ return FALSE;
+ }
+ setAt( i );
+ return TRUE;
+}
+
+bool TQODBCResult::fetchNext()
+{
+ STQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ r = STQLFetchScroll( d->hStmt,
+ STQL_FETCH_NEXT,
+ 0 );
+ if ( r != STQL_SUCCESS )
+ return FALSE;
+ setAt( at() + 1 );
+ return TRUE;
+}
+
+bool TQODBCResult::fetchFirst()
+{
+ if ( isForwardOnly() && at() != TQSql::BeforeFirst )
+ return FALSE;
+ STQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ if ( isForwardOnly() ) {
+ return fetchNext();
+ }
+ r = STQLFetchScroll( d->hStmt,
+ STQL_FETCH_FIRST,
+ 0 );
+ if ( r != STQL_SUCCESS )
+ return FALSE;
+ setAt( 0 );
+ return TRUE;
+}
+
+bool TQODBCResult::fetchPrior()
+{
+ if ( isForwardOnly() )
+ return FALSE;
+ STQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ r = STQLFetchScroll( d->hStmt,
+ STQL_FETCH_PRIOR,
+ 0 );
+ if ( r != STQL_SUCCESS )
+ return FALSE;
+ setAt( at() - 1 );
+ return TRUE;
+}
+
+bool TQODBCResult::fetchLast()
+{
+ STQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+
+ if ( isForwardOnly() ) {
+ // cannot seek to last row in forwardOnly mode, so we have to use brute force
+ int i = at();
+ if ( i == TQSql::AfterLast )
+ return FALSE;
+ if ( i == TQSql::BeforeFirst )
+ i = 0;
+ while ( fetchNext() )
+ ++i;
+ setAt( i );
+ return TRUE;
+ }
+
+ r = STQLFetchScroll( d->hStmt,
+ STQL_FETCH_LAST,
+ 0 );
+ if ( r != STQL_SUCCESS ) {
+ return FALSE;
+ }
+ STQLINTEGER currRow;
+ r = STQLGetStmtAttr( d->hStmt,
+ STQL_ROW_NUMBER,
+ &currRow,
+ STQL_IS_INTEGER,
+ 0 );
+ if ( r != STQL_SUCCESS )
+ return FALSE;
+ setAt( currRow-1 );
+ return TRUE;
+}
+
+TQVariant TQODBCResult::data( int field )
+{
+ if ( field >= (int) d->rInf.count() ) {
+ qWarning( "TQODBCResult::data: column %d out of range", field );
+ return TQVariant();
+ }
+ if ( fieldCache.tqcontains( field ) )
+ return fieldCache[ field ];
+ STQLRETURN r(0);
+ TQSTQLLEN lengthIndicator = 0;
+ bool isNull = FALSE;
+ int current = fieldCache.count();
+ for ( ; current < (field + 1); ++current ) {
+ const TQSqlFieldInfo info = d->rInf[ current ];
+ switch ( info.type() ) {
+ case TQVariant::LongLong:
+ fieldCache[ current ] = TQVariant( (TQ_LLONG) qGetBigIntData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case TQVariant::Int:
+ fieldCache[ current ] = TQVariant( qGetIntData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case TQVariant::Date:
+ DATE_STRUCT dbuf;
+ r = STQLGetData( d->hStmt,
+ current+1,
+ STQL_C_DATE,
+ (STQLPOINTER)&dbuf,
+ (TQSTQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != STQL_NULL_DATA ) ) {
+ fieldCache[ current ] = TQVariant( TQDate( dbuf.year, dbuf.month, dbuf.day ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = TQVariant( TQDate() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case TQVariant::Time:
+ TIME_STRUCT tbuf;
+ r = STQLGetData( d->hStmt,
+ current+1,
+ STQL_C_TIME,
+ (STQLPOINTER)&tbuf,
+ (TQSTQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != STQL_NULL_DATA ) ) {
+ fieldCache[ current ] = TQVariant( TQTime( tbuf.hour, tbuf.minute, tbuf.second ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = TQVariant( TQTime() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case TQVariant::DateTime:
+ TIMESTAMP_STRUCT dtbuf;
+ r = STQLGetData( d->hStmt,
+ current+1,
+ STQL_C_TIMESTAMP,
+ (STQLPOINTER)&dtbuf,
+ (TQSTQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != STQL_NULL_DATA ) ) {
+ fieldCache[ current ] = TQVariant( TQDateTime( TQDate( dtbuf.year, dtbuf.month, dtbuf.day ), TQTime( dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000 ) ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = TQVariant( TQDateTime() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case TQVariant::ByteArray: {
+ isNull = FALSE;
+ TQByteArray val = qGetBinaryData( d->hStmt, current, lengthIndicator, isNull );
+ fieldCache[ current ] = TQVariant( val );
+ nullCache[ current ] = isNull;
+ break; }
+ case TQVariant::String:
+ isNull = FALSE;
+ fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current,
+ info.length(), isNull, TRUE ) );
+ nullCache[ current ] = isNull;
+ break;
+ case TQVariant::Double:
+ if ( info.typeID() == STQL_DECIMAL || info.typeID() == STQL_NUMERIC )
+ // bind Double values as string to prevent loss of precision
+ fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current,
+ info.length() + 1, isNull, FALSE ) ); // length + 1 for the comma
+ else
+ fieldCache[ current ] = TQVariant( qGetDoubleData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case TQVariant::CString:
+ default:
+ isNull = FALSE;
+ fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current,
+ info.length(), isNull, FALSE ) );
+ nullCache[ current ] = isNull;
+ break;
+ }
+ }
+ return fieldCache[ --current ];
+}
+
+bool TQODBCResult::isNull( int field )
+{
+ if ( !fieldCache.tqcontains( field ) ) {
+ // since there is no good way to tqfind out whether the value is NULL
+ // without fetching the field we'll fetch it here.
+ // (data() also sets the NULL flag)
+ data( field );
+ }
+ return nullCache[ field ];
+}
+
+int TQODBCResult::size()
+{
+ return -1;
+}
+
+int TQODBCResult::numRowsAffected()
+{
+ TQSTQLLEN affectedRowCount(0);
+ STQLRETURN r = STQLRowCount( d->hStmt, &affectedRowCount );
+ if ( r == STQL_SUCCESS )
+ return affectedRowCount;
+#ifdef TQT_CHECK_RANGE
+ else
+ qSqlWarning( "TQODBCResult::numRowsAffected: Unable to count affected rows", d );
+#endif
+ return -1;
+}
+
+bool TQODBCResult::prepare( const TQString& query )
+{
+ setActive( FALSE );
+ setAt( TQSql::BeforeFirst );
+ STQLRETURN r;
+
+ d->rInf.clear();
+ if ( d->hStmt ) {
+ r = STQLFreeHandle( STQL_HANDLE_STMT, d->hStmt );
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to close statement", d );
+#endif
+ return FALSE;
+ }
+ }
+ r = STQLAllocHandle( STQL_HANDLE_STMT,
+ d->hDbc,
+ &d->hStmt );
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to allocate statement handle", d );
+#endif
+ return FALSE;
+ }
+
+ if ( isForwardOnly() ) {
+ r = STQLSetStmtAttr( d->hStmt,
+ STQL_ATTR_CURSOR_TYPE,
+ (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY,
+ STQL_IS_UINTEGER );
+ } else {
+ r = STQLSetStmtAttr( d->hStmt,
+ STQL_ATTR_CURSOR_TYPE,
+ (STQLPOINTER)STQL_CURSOR_STATIC,
+ STQL_IS_UINTEGER );
+ }
+ if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to set 'STQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
+#endif
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ r = STQLPrepare( d->hStmt,
+ (STQLWCHAR*) query.tqunicode(),
+ (STQLINTEGER) query.length() );
+#else
+ TQCString query8 = query.local8Bit();
+ r = STQLPrepare( d->hStmt,
+ (STQLCHAR*) query8.data(),
+ (STQLINTEGER) query8.length() );
+#endif
+
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to prepare statement", d );
+#endif
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQODBCResult::exec()
+{
+ STQLRETURN r;
+ TQPtrList<TQVirtualDestructor> tmpStorage; // holds temporary ptrs. which will be deleted on fu exit
+ tmpStorage.setAutoDelete( TRUE );
+
+ setActive( FALSE );
+ setAt( TQSql::BeforeFirst );
+ d->rInf.clear();
+
+ if ( !d->hStmt ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::exec: No statement handle available", d );
+#endif
+ return FALSE;
+ } else {
+ r = STQLFreeStmt( d->hStmt, STQL_CLOSE );
+ if ( r != STQL_SUCCESS ) {
+ qSqlWarning( "TQODBCResult::exec: Unable to close statement handle", d );
+ return FALSE;
+ }
+ }
+
+ // bind parameters - only positional binding allowed
+ if ( extension()->index.count() > 0 ) {
+ TQMap<int, TQString>::Iterator it;
+ int para = 1;
+ TQVariant val;
+ for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
+ val = extension()->values[ it.data() ].value;
+ TQSTQLLEN *ind = new TQSTQLLEN( STQL_NTS );
+ tmpStorage.append( qAutoDeleter(ind) );
+ if ( val.isNull() ) {
+ *ind = STQL_NULL_DATA;
+ }
+ switch ( val.type() ) {
+ case TQVariant::Date: {
+ DATE_STRUCT * dt = new DATE_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ TQDate qdt = val.toDate();
+ dt->year = qdt.year();
+ dt->month = qdt.month();
+ dt->day = qdt.day();
+ r = STQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ STQL_C_DATE,
+ STQL_DATE,
+ 0,
+ 0,
+ (void *) dt,
+ (TQSTQLLEN)0,
+ *ind == STQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::Time: {
+ TIME_STRUCT * dt = new TIME_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ TQTime qdt = val.toTime();
+ dt->hour = qdt.hour();
+ dt->minute = qdt.minute();
+ dt->second = qdt.second();
+ r = STQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ STQL_C_TIME,
+ STQL_TIME,
+ 0,
+ 0,
+ (void *) dt,
+ (TQSTQLLEN)0,
+ *ind == STQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::DateTime: {
+ TIMESTAMP_STRUCT * dt = new TIMESTAMP_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ TQDateTime qdt = val.toDateTime();
+ dt->year = qdt.date().year();
+ dt->month = qdt.date().month();
+ dt->day = qdt.date().day();
+ dt->hour = qdt.time().hour();
+ dt->minute = qdt.time().minute();
+ dt->second = qdt.time().second();
+ dt->fraction = 0;
+ r = STQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ STQL_C_TIMESTAMP,
+ STQL_TIMESTAMP,
+ 0,
+ 0,
+ (void *) dt,
+ (TQSTQLLEN)0,
+ *ind == STQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::Int: {
+ int * v = new int( val.toInt() );
+ tmpStorage.append( qAutoDeleter(v) );
+ r = STQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ STQL_C_SLONG,
+ STQL_INTEGER,
+ 0,
+ 0,
+ (void *) v,
+ (TQSTQLLEN)0,
+ *ind == STQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::Double: {
+ double * v = new double( val.toDouble() );
+ tmpStorage.append( qAutoDeleter(v) );
+ r = STQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ STQL_C_DOUBLE,
+ STQL_DOUBLE,
+ 0,
+ 0,
+ (void *) v,
+ (TQSTQLLEN)0,
+ *ind == STQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::ByteArray: {
+ if ( *ind != STQL_NULL_DATA ) {
+ *ind = val.asByteArray().size();
+ }
+ r = STQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ STQL_C_BINARY,
+ STQL_LONGVARBINARY,
+ val.asByteArray().size(),
+ 0,
+ (void *) val.asByteArray().data(),
+ (TQSTQLLEN)val.asByteArray().size(),
+ ind );
+ break; }
+#ifndef TQ_ODBC_VERSION_2
+ case TQVariant::String:
+ if ( d->tqunicode ) {
+ TQString * str = new TQString( val.asString() );
+ str->ucs2();
+ int len = str->length()*2;
+ tmpStorage.append( qAutoDeleter(str) );
+ r = STQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ STQL_C_WCHAR,
+ len > 8000 ? STQL_WLONGVARCHAR : STQL_WVARCHAR,
+ len > 8000 ? len : 0,
+ 0,
+ (void *) str->tqunicode(),
+ (TQSTQLLEN) len,
+ ind );
+ break;
+ }
+#endif
+ // fall through
+ default: {
+ TQCString * str = new TQCString( val.asString().local8Bit() );
+ tmpStorage.append( qAutoDeleter(str) );
+ r = STQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ STQL_C_CHAR,
+ str->length() > 4000 ? STQL_LONGVARCHAR : STQL_VARCHAR,
+ str->length() + 1,
+ 0,
+ (void *) str->data(),
+ (TQSTQLLEN)(str->length() + 1),
+ ind );
+ break; }
+ }
+ para++;
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qWarning( "TQODBCResult::exec: unable to bind variable: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to bind variable", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ }
+ }
+ r = STQLExecute( d->hStmt );
+ if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) {
+#ifdef TQT_CHECK_RANGE
+ qWarning( "TQODBCResult::exec: Unable to execute statement: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to execute statement", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ STQLSMALLINT count;
+ r = STQLNumResultCols( d->hStmt, &count );
+ if ( count ) {
+ setSelect( TRUE );
+ for ( int i = 0; i < count; ++i ) {
+ d->rInf.append( qMakeFieldInfo( d, i ) );
+ }
+ } else {
+ setSelect( FALSE );
+ }
+ setActive( TRUE );
+
+ //get out parameters
+ if ( extension()->index.count() > 0 ) {
+ TQMap<int, TQString>::Iterator it;
+ for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
+
+ STQLINTEGER* indPtr = qAutoDeleterData( (TQAutoDeleter<STQLINTEGER>*)tmpStorage.getFirst() );
+ if ( !indPtr )
+ return FALSE;
+ bool isNull = (*indPtr == STQL_NULL_DATA);
+ tmpStorage.removeFirst();
+
+ TQVariant::Type type = extension()->values[ it.data() ].value.type();
+ if ( isNull ) {
+ TQVariant v;
+ v.cast(type);
+ extension()->values[ it.data() ].value = v;
+ if (type != TQVariant::ByteArray)
+ tmpStorage.removeFirst();
+ continue;
+ }
+
+ switch (type) {
+ case TQVariant::Date: {
+ DATE_STRUCT * ds = qAutoDeleterData( (TQAutoDeleter<DATE_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( TQDate( ds->year, ds->month, ds->day ) );
+ break; }
+ case TQVariant::Time: {
+ TIME_STRUCT * dt = qAutoDeleterData( (TQAutoDeleter<TIME_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( TQTime( dt->hour, dt->minute, dt->second ) );
+ break; }
+ case TQVariant::DateTime: {
+ TIMESTAMP_STRUCT * dt = qAutoDeleterData( (TQAutoDeleter<TIMESTAMP_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( TQDateTime( TQDate( dt->year, dt->month, dt->day ),
+ TQTime( dt->hour, dt->minute, dt->second ) ) );
+ break; }
+ case TQVariant::Int: {
+ int * v = qAutoDeleterData( (TQAutoDeleter<int>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( *v );
+ break; }
+ case TQVariant::Double: {
+ double * v = qAutoDeleterData( (TQAutoDeleter<double>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( *v );
+ break; }
+ case TQVariant::ByteArray:
+ break;
+ case TQVariant::String:
+ if ( d->tqunicode ) {
+ TQString * str = qAutoDeleterData( (TQAutoDeleter<TQString>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( *str );
+ break;
+ }
+ // fall through
+ default: {
+ TQCString * str = qAutoDeleterData( (TQAutoDeleter<TQCString>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( *str );
+ break; }
+ }
+ if (type != TQVariant::ByteArray)
+ tmpStorage.removeFirst();
+ }
+ }
+
+ return TRUE;
+}
+
+////////////////////////////////////////
+
+
+TQODBCDriver::TQODBCDriver( TQObject * tqparent, const char * name )
+ : TQSqlDriver(tqparent,name ? name : "TQODBC")
+{
+ init();
+}
+
+TQODBCDriver::TQODBCDriver( STQLHANDLE env, STQLHANDLE con, TQObject * tqparent, const char * name )
+ : TQSqlDriver(tqparent,name ? name : "TQODBC")
+{
+ init();
+ d->hEnv = env;
+ d->hDbc = con;
+ if ( env && con ) {
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ }
+}
+
+void TQODBCDriver::init()
+{
+ qSqlOpenExtDict()->insert( this, new TQODBCOpenExtension(this) );
+ d = new TQODBCPrivate();
+}
+
+TQODBCDriver::~TQODBCDriver()
+{
+ cleanup();
+ delete d;
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool TQODBCDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions: {
+ if ( !d->hDbc )
+ return FALSE;
+ STQLUSMALLINT txn;
+ STQLSMALLINT t;
+ int r = STQLGetInfo( d->hDbc,
+ (STQLUSMALLINT)STQL_TXN_CAPABLE,
+ &txn,
+ sizeof(txn),
+ &t);
+ if ( r != STQL_SUCCESS || txn == STQL_TC_NONE )
+ return FALSE;
+ else
+ return TRUE;
+ }
+ case QuerySize:
+ return FALSE;
+ case BLOB:
+ return TRUE;
+ case Unicode:
+ return d->tqunicode;
+ case PreparedQueries:
+ return TRUE;
+ case PositionalPlaceholders:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+bool TQODBCDriver::open( const TQString&,
+ const TQString&,
+ const TQString&,
+ const TQString&,
+ int )
+{
+ qWarning("TQODBCDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool TQODBCDriver::open( const TQString & db,
+ const TQString & user,
+ const TQString & password,
+ const TQString &,
+ int,
+ const TQString& connOpts )
+{
+ if ( isOpen() )
+ close();
+ STQLRETURN r;
+ r = STQLAllocHandle( STQL_HANDLE_ENV,
+ STQL_NULL_HANDLE,
+ &d->hEnv);
+ if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::open: Unable to allocate environment", d );
+#endif
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ r = STQLSetEnvAttr( d->hEnv,
+ STQL_ATTR_ODBC_VERSION,
+ (STQLPOINTER)STQL_OV_ODBC2,
+ STQL_IS_UINTEGER );
+ r = STQLAllocHandle( STQL_HANDLE_DBC,
+ d->hEnv,
+ &d->hDbc);
+ if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::open: Unable to allocate connection", d );
+#endif
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ if ( !d->setConnectionOptions( connOpts ) )
+ return FALSE;
+
+ // Create the connection string
+ TQString connTQStr;
+ // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
+ if ( db.tqcontains(".dsn") )
+ connTQStr = "FILEDSN=" + db;
+ else if ( db.tqcontains( "DRIVER" ) || db.tqcontains( "SERVER" ) )
+ connTQStr = db;
+ else
+ connTQStr = "DSN=" + db;
+ connTQStr += ";UID=" + user + ";PWD=" + password;
+ STQLSMALLINT cb;
+ STQLTCHAR connOut[1024];
+ r = STQLDriverConnect( d->hDbc,
+ NULL,
+#ifdef UNICODE
+ (STQLWCHAR*)connTQStr.tqunicode(),
+#else
+ (STQLCHAR*)connTQStr.latin1(),
+#endif
+ (STQLSMALLINT)connTQStr.length(),
+ connOut,
+ 1024,
+ &cb,
+ STQL_DRIVER_NOPROMPT );
+ if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) {
+ setLastError( qMakeError( "Unable to connect", TQSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ if ( !d->checkDriver() ) {
+ setLastError( qMakeError( "Unable to connect - Driver doesn't support all needed functionality", TQSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ d->checkUnicode();
+ d->checkSchemaUsage();
+
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void TQODBCDriver::close()
+{
+ cleanup();
+ setOpen( FALSE );
+ setOpenError( FALSE );
+}
+
+void TQODBCDriver::cleanup()
+{
+ STQLRETURN r;
+ if ( !d )
+ return;
+
+ if( d->hDbc ) {
+ // Open statements/descriptors handles are automatically cleaned up by STQLDisconnect
+ if ( isOpen() ) {
+ r = STQLDisconnect( d->hDbc );
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::disconnect: Unable to disconnect datasource", d );
+#endif
+ }
+
+ r = STQLFreeHandle( STQL_HANDLE_DBC, d->hDbc );
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::cleanup: Unable to free connection handle", d );
+#endif
+ d->hDbc = 0;
+ }
+
+ if ( d->hEnv ) {
+ r = STQLFreeHandle( STQL_HANDLE_ENV, d->hEnv );
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::cleanup: Unable to free environment handle", d );
+#endif
+ d->hEnv = 0;
+ }
+}
+
+// checks whether the server can return char, varchar and longvarchar
+// as two byte tqunicode characters
+void TQODBCPrivate::checkUnicode()
+{
+#if defined(TQ_WS_WIN)
+ if ( !qt_wintqunicode ) {
+ tqunicode = FALSE;
+ return;
+ }
+#endif
+ STQLRETURN r;
+ STQLUINTEGER fFunc;
+
+ tqunicode = FALSE;
+ r = STQLGetInfo( hDbc,
+ STQL_CONVERT_CHAR,
+ (STQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( fFunc & STQL_CVT_WCHAR ) ) {
+ sql_char_type = TQVariant::String;
+ tqunicode = TRUE;
+ }
+
+ r = STQLGetInfo( hDbc,
+ STQL_CONVERT_VARCHAR,
+ (STQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( fFunc & STQL_CVT_WVARCHAR ) ) {
+ sql_varchar_type = TQVariant::String;
+ tqunicode = TRUE;
+ }
+
+ r = STQLGetInfo( hDbc,
+ STQL_CONVERT_LONGVARCHAR,
+ (STQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( fFunc & STQL_CVT_WLONGVARCHAR ) ) {
+ sql_longvarchar_type = TQVariant::String;
+ tqunicode = TRUE;
+ }
+}
+
+bool TQODBCPrivate::checkDriver() const
+{
+#ifdef ODBC_CHECK_DRIVER
+ // do not query for STQL_API_STQLFETCHSCROLL because it can't be used at this time
+ static const STQLUSMALLINT reqFunc[] = {
+ STQL_API_STQLDESCRIBECOL, STQL_API_STQLGETDATA, STQL_API_STQLCOLUMNS,
+ STQL_API_STQLGETSTMTATTR, STQL_API_STQLGETDIAGREC, STQL_API_STQLEXECDIRECT,
+ STQL_API_STQLGETINFO, STQL_API_STQLTABLES, 0
+ };
+
+ // these functions are optional
+ static const STQLUSMALLINT optFunc[] = {
+ STQL_API_STQLNUMRESULTCOLS, STQL_API_STQLROWCOUNT, 0
+ };
+
+ STQLRETURN r;
+ STQLUSMALLINT sup;
+
+
+ int i;
+ // check the required functions
+ for ( i = 0; reqFunc[ i ] != 0; ++i ) {
+
+ r = STQLGetFunctions( hDbc, reqFunc[ i ], &sup );
+
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS ) {
+ qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == STQL_FALSE ) {
+#ifdef TQT_CHECK_RANGE
+ qWarning ( "TQODBCDriver::open: Warning - Driver doesn't support all needed functionality (%d). "
+ "Please look at the TQt SQL Module Driver documentation for more information.", reqFunc[ i ] );
+#endif
+ return FALSE;
+ }
+ }
+
+ // these functions are optional and just generate a warning
+ for ( i = 0; optFunc[ i ] != 0; ++i ) {
+
+ r = STQLGetFunctions( hDbc, optFunc[ i ], &sup );
+
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS ) {
+ qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == STQL_FALSE ) {
+#ifdef TQT_CHECK_RANGE
+ qWarning( "TQODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (%d)", optFunc[ i ] );
+#endif
+ return TRUE;
+ }
+ }
+#endif //ODBC_CHECK_DRIVER
+
+ return TRUE;
+}
+
+void TQODBCPrivate::checkSchemaUsage()
+{
+ STQLRETURN r;
+ STQLUINTEGER val;
+
+ r = STQLGetInfo(hDbc,
+ STQL_SCHEMA_USAGE,
+ (STQLPOINTER) &val,
+ sizeof(val),
+ NULL);
+ if (r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO)
+ useSchema = (val != 0);
+}
+
+TQSqlQuery TQODBCDriver::createQuery() const
+{
+ return TQSqlQuery( new TQODBCResult( this, d ) );
+}
+
+bool TQODBCDriver::beginTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef TQT_CHECK_RANGE
+ qWarning(" TQODBCDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ STQLUINTEGER ac(STQL_AUTOCOMMIT_OFF);
+ STQLRETURN r = STQLSetConnectAttr( d->hDbc,
+ STQL_ATTR_AUTOCOMMIT,
+ (STQLPOINTER)ac,
+ sizeof(ac) );
+ if ( r != STQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to disable autocommit", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQODBCDriver::commitTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef TQT_CHECK_RANGE
+ qWarning(" TQODBCDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ STQLRETURN r = STQLEndTran( STQL_HANDLE_DBC,
+ d->hDbc,
+ STQL_COMMIT );
+ if ( r != STQL_SUCCESS ) {
+ setLastError( qMakeError("Unable to commit transaction", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool TQODBCDriver::rollbackTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef TQT_CHECK_RANGE
+ qWarning(" TQODBCDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ STQLRETURN r = STQLEndTran( STQL_HANDLE_DBC,
+ d->hDbc,
+ STQL_ROLLBACK );
+ if ( r != STQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to rollback transaction", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool TQODBCDriver::endTrans()
+{
+ STQLUINTEGER ac(STQL_AUTOCOMMIT_ON);
+ STQLRETURN r = STQLSetConnectAttr( d->hDbc,
+ STQL_ATTR_AUTOCOMMIT,
+ (STQLPOINTER)ac,
+ sizeof(ac));
+ if ( r != STQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to enable autocommit", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+TQStringList TQODBCDriver::tables( const TQString& typeName ) const
+{
+ TQStringList tl;
+ if ( !isOpen() )
+ return tl;
+ int type = typeName.toInt();
+ STQLHANDLE hStmt;
+
+ STQLRETURN r = STQLAllocHandle( STQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::tables: Unable to allocate handle", d );
+#endif
+ return tl;
+ }
+ r = STQLSetStmtAttr( hStmt,
+ STQL_ATTR_CURSOR_TYPE,
+ (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY,
+ STQL_IS_UINTEGER );
+ TQString tableType;
+ if ( typeName.isEmpty() || ((type & (int)TQSql::Tables) == (int)TQSql::Tables) )
+ tableType += "TABLE,";
+ if ( (type & (int)TQSql::Views) == (int)TQSql::Views )
+ tableType += "VIEW,";
+ if ( (type & (int)TQSql::SystemTables) == (int)TQSql::SystemTables )
+ tableType += "SYSTEM TABLE,";
+ if ( tableType.isEmpty() )
+ return tl;
+ tableType.truncate( tableType.length() - 1 );
+
+ r = STQLTables( hStmt,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ 0,
+#ifdef UNICODE
+ (STQLWCHAR*)tableType.tqunicode(),
+#else
+ (STQLCHAR*)tableType.latin1(),
+#endif
+ tableType.length() /* characters, not bytes */ );
+
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::tables Unable to execute table list", d );
+#endif
+ r = STQLFetchScroll( hStmt,
+ STQL_FETCH_NEXT,
+ 0);
+ while ( r == STQL_SUCCESS ) {
+ bool isNull;
+ TQString fieldVal = qGetStringData( hStmt, 2, -1, isNull, d->tqunicode );
+ tl.append( fieldVal );
+ r = STQLFetchScroll( hStmt,
+ STQL_FETCH_NEXT,
+ 0);
+ }
+
+ r = STQLFreeHandle( STQL_HANDLE_STMT, hStmt );
+ if ( r!= STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver: Unable to free statement handle" + TQString::number(r), d );
+ return tl;
+}
+
+TQSqlIndex TQODBCDriver::primaryIndex( const TQString& tablename ) const
+{
+ TQSqlIndex index( tablename );
+ if ( !isOpen() )
+ return index;
+ bool usingSpecialColumns = FALSE;
+ TQSqlRecord rec = record( tablename );
+
+ STQLHANDLE hStmt;
+ STQLRETURN r = STQLAllocHandle( STQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::primaryIndex: Unable to list primary key", d );
+#endif
+ return index;
+ }
+ TQString catalog, schema, table;
+ d->splitTableQualifier( tablename, catalog, schema, table );
+ r = STQLSetStmtAttr( hStmt,
+ STQL_ATTR_CURSOR_TYPE,
+ (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY,
+ STQL_IS_UINTEGER );
+ r = STQLPrimaryKeys( hStmt,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (STQLWCHAR*)catalog.tqunicode(),
+#else
+ catalog.length() == 0 ? NULL : (STQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (STQLWCHAR*)schema.tqunicode(),
+#else
+ schema.length() == 0 ? NULL : (STQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (STQLWCHAR*)table.tqunicode(),
+#else
+ (STQLCHAR*)table.latin1(),
+#endif
+ table.length() /* in characters, not in bytes */);
+
+ // if the STQLPrimaryKeys() call does not succeed (e.g the driver
+ // does not support it) - try an alternative method to get hold of
+ // the primary index (e.g MS Access and FoxPro)
+ if ( r != STQL_SUCCESS ) {
+ r = STQLSpecialColumns( hStmt,
+ STQL_BEST_ROWID,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (STQLWCHAR*)catalog.tqunicode(),
+#else
+ catalog.length() == 0 ? NULL : (STQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (STQLWCHAR*)schema.tqunicode(),
+#else
+ schema.length() == 0 ? NULL : (STQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (STQLWCHAR*)table.tqunicode(),
+#else
+ (STQLCHAR*)table.latin1(),
+#endif
+
+ table.length(),
+ STQL_SCOPE_CURROW,
+ STQL_NULLABLE );
+
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::primaryIndex: Unable to execute primary key list", d );
+#endif
+ } else {
+ usingSpecialColumns = TRUE;
+ }
+ }
+ r = STQLFetchScroll( hStmt,
+ STQL_FETCH_NEXT,
+ 0 );
+ bool isNull;
+ int fakeId = 0;
+ TQString cName, idxName;
+ // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
+ while ( r == STQL_SUCCESS ) {
+ if ( usingSpecialColumns ) {
+ cName = qGetStringData( hStmt, 1, -1, isNull, d->tqunicode ); // column name
+ idxName = TQString::number( fakeId++ ); // invent a fake index name
+ } else {
+ cName = qGetStringData( hStmt, 3, -1, isNull, d->tqunicode ); // column name
+ idxName = qGetStringData( hStmt, 5, -1, isNull, d->tqunicode ); // pk index name
+ }
+ TQSqlField *fld = rec.field(cName);
+ if (fld)
+ index.append(*fld);
+ index.setName( idxName );
+ r = STQLFetchScroll( hStmt,
+ STQL_FETCH_NEXT,
+ 0 );
+ }
+ r = STQLFreeHandle( STQL_HANDLE_STMT, hStmt );
+ if ( r!= STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver: Unable to free statement handle" + TQString::number(r), d );
+ return index;
+}
+
+TQSqlRecord TQODBCDriver::record( const TQString& tablename ) const
+{
+ return recordInfo( tablename ).toRecord();
+}
+
+TQSqlRecord TQODBCDriver::record( const TQSqlQuery& query ) const
+{
+ return recordInfo( query ).toRecord();
+}
+
+TQSqlRecordInfo TQODBCDriver::recordInfo( const TQString& tablename ) const
+{
+ TQSqlRecordInfo fil;
+ if ( !isOpen() )
+ return fil;
+
+ STQLHANDLE hStmt;
+ TQString catalog, schema, table;
+ d->splitTableQualifier( tablename, catalog, schema, table );
+ STQLRETURN r = STQLAllocHandle( STQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != STQL_SUCCESS ) {
+#ifdef TQT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::record: Unable to allocate handle", d );
+#endif
+ return fil;
+ }
+ r = STQLSetStmtAttr( hStmt,
+ STQL_ATTR_CURSOR_TYPE,
+ (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY,
+ STQL_IS_UINTEGER );
+ r = STQLColumns( hStmt,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (STQLWCHAR*)catalog.tqunicode(),
+#else
+ catalog.length() == 0 ? NULL : (STQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (STQLWCHAR*)schema.tqunicode(),
+#else
+ schema.length() == 0 ? NULL : (STQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (STQLWCHAR*)table.tqunicode(),
+#else
+ (STQLCHAR*)table.latin1(),
+#endif
+ table.length(),
+ NULL,
+ 0 );
+#ifdef TQT_CHECK_RANGE
+ if ( r != STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::record: Unable to execute column list", d );
+#endif
+ r = STQLFetchScroll( hStmt,
+ STQL_FETCH_NEXT,
+ 0);
+ // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
+ while ( r == STQL_SUCCESS ) {
+
+ fil.append( qMakeFieldInfo( hStmt, d ) );
+
+ r = STQLFetchScroll( hStmt,
+ STQL_FETCH_NEXT,
+ 0);
+ }
+
+ r = STQLFreeHandle( STQL_HANDLE_STMT, hStmt );
+ if ( r!= STQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver: Unable to free statement handle " + TQString::number(r), d );
+
+ return fil;
+}
+
+TQSqlRecordInfo TQODBCDriver::recordInfo( const TQSqlQuery& query ) const
+{
+ TQSqlRecordInfo fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.driver() == this ) {
+ TQODBCResult* result = (TQODBCResult*)query.result();
+ fil = result->d->rInf;
+ }
+ return fil;
+}
+
+STQLHANDLE TQODBCDriver::environment()
+{
+ return d->hEnv;
+}
+
+STQLHANDLE TQODBCDriver::connection()
+{
+ return d->hDbc;
+}
+
+TQString TQODBCDriver::formatValue( const TQSqlField* field,
+ bool trimStrings ) const
+{
+ TQString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else if ( field->type() == TQVariant::DateTime ) {
+ // Use an escape sequence for the datetime fields
+ if ( field->value().toDateTime().isValid() ){
+ TQDate dt = field->value().toDateTime().date();
+ TQTime tm = field->value().toDateTime().time();
+ // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
+ r = "{ ts '" +
+ TQString::number(dt.year()) + "-" +
+ TQString::number(dt.month()).rightJustify( 2, '0', TRUE ) + "-" +
+ TQString::number(dt.day()).rightJustify( 2, '0', TRUE ) + " " +
+ tm.toString() +
+ "' }";
+ } else
+ r = nullText();
+ } else if ( field->type() == TQVariant::ByteArray ) {
+ TQByteArray ba = field->value().toByteArray();
+ TQString res;
+ static const char hexchars[] = "0123456789abcdef";
+ for ( uint i = 0; i < ba.size(); ++i ) {
+ uchar s = (uchar) ba[(int)i];
+ res += hexchars[s >> 4];
+ res += hexchars[s & 0x0f];
+ }
+ r = "0x" + res;
+ } else {
+ r = TQSqlDriver::formatValue( field, trimStrings );
+ }
+ return r;
+}