summaryrefslogtreecommitdiffstats
path: root/src/sql/drivers/odbc/tqsql_odbc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sql/drivers/odbc/tqsql_odbc.cpp')
-rw-r--r--src/sql/drivers/odbc/tqsql_odbc.cpp2041
1 files changed, 2041 insertions, 0 deletions
diff --git a/src/sql/drivers/odbc/tqsql_odbc.cpp b/src/sql/drivers/odbc/tqsql_odbc.cpp
new file mode 100644
index 00000000..605948ca
--- /dev/null
+++ b/src/sql/drivers/odbc/tqsql_odbc.cpp
@@ -0,0 +1,2041 @@
+/****************************************************************************
+**
+** Implementation of ODBC driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** 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 (Q_OS_WIN32)
+#include <qt_windows.h>
+#include <ntqapplication.h>
+#endif
+#include <ntqdatetime.h>
+#include <private/tqsqlextension_p.h>
+#include <private/qinternal_p.h>
+#include <stdlib.h>
+
+// undefine this to prevent initial check of the ODBC driver
+#define ODBC_CHECK_DRIVER
+
+#if defined(Q_ODBC_VERSION_2)
+//crude hack to get non-unicode capable driver managers to work
+# undef UNICODE
+# define SQLTCHAR SQLCHAR
+# define SQL_C_WCHAR SQL_C_CHAR
+#endif
+
+// newer platform SDKs use SQLLEN instead of SQLINTEGER
+#if defined(SQLLEN) || defined(Q_OS_WIN64) || defined(Q_OS_UNIX)
+# define TQSQLLEN SQLLEN
+#else
+# define TQSQLLEN SQLINTEGER
+#endif
+
+#if defined(SQLULEN) || defined(Q_OS_WIN64) || defined(Q_OS_UNIX)
+# define TQSQLULEN SQLULEN
+#else
+# define TQSQLULEN SQLUINTEGER
+#endif
+
+static const TQSQLLEN COLNAMESIZE = 256;
+//Map TQt parameter types to ODBC types
+static const SQLSMALLINT qParamType[ 4 ] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_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;
+ unicode = FALSE;
+ }
+
+ SQLHANDLE hEnv;
+ SQLHANDLE hDbc;
+ SQLHANDLE hStmt;
+
+ bool unicode;
+ 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> *tqSqlOpenExtDict();
+
+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, SQLHANDLE handle)
+{
+ SQLINTEGER nativeCode_;
+ SQLSMALLINT msgLen;
+ SQLRETURN r = SQL_ERROR;
+ SQLTCHAR state_[SQL_SQLSTATE_SIZE+1];
+ SQLTCHAR description_[SQL_MAX_MESSAGE_LENGTH];
+ r = SQLGetDiagRec( handleType,
+ handle,
+ 1,
+ (SQLTCHAR*)state_,
+ &nativeCode_,
+ (SQLTCHAR*)description_,
+ SQL_MAX_MESSAGE_LENGTH-1, /* in bytes, not in characters */
+ &msgLen);
+ if ( r == SQL_SUCCESS || r == SQL_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( SQL_HANDLE_ENV, odbc->hEnv ) + " "
+ + qWarnODBCHandle( SQL_HANDLE_DBC, odbc->hDbc ) + " "
+ + qWarnODBCHandle( SQL_HANDLE_STMT, odbc->hStmt ) );
+}
+
+static void qSqlWarning( const TQString& message, const TQODBCPrivate* odbc )
+{
+#ifdef QT_CHECK_RANGE
+ tqWarning( "%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( SQLSMALLINT sqltype, const TQODBCPrivate* p )
+{
+ TQVariant::Type type = TQVariant::Invalid;
+ switch ( sqltype ) {
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_REAL:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ type = TQVariant::Double;
+ break;
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIT:
+ case SQL_TINYINT:
+ type = TQVariant::Int;
+ break;
+ case SQL_BIGINT:
+ type = TQVariant::LongLong;
+ break;
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_LONGVARBINARY:
+ type = TQVariant::ByteArray;
+ break;
+ case SQL_DATE:
+ case SQL_TYPE_DATE:
+ type = TQVariant::Date;
+ break;
+ case SQL_TIME:
+ case SQL_TYPE_TIME:
+ type = TQVariant::Time;
+ break;
+ case SQL_TIMESTAMP:
+ case SQL_TYPE_TIMESTAMP:
+ type = TQVariant::DateTime;
+ break;
+#ifndef Q_ODBC_VERSION_2
+ case SQL_WCHAR:
+ case SQL_WVARCHAR:
+ case SQL_WLONGVARCHAR:
+ type = TQVariant::String;
+ break;
+#endif
+ case SQL_CHAR:
+ type = p->sql_char_type;
+ break;
+ case SQL_VARCHAR:
+ type = p->sql_varchar_type;
+ break;
+ case SQL_LONGVARCHAR:
+ type = p->sql_longvarchar_type;
+ break;
+ default:
+ type = TQVariant::CString;
+ break;
+ }
+ return type;
+}
+
+static TQString qGetStringData( SQLHANDLE hStmt, int column, int colSize, bool& isNull, bool unicode = FALSE )
+{
+ TQString fieldVal;
+ SQLRETURN r = SQL_ERROR;
+ TQSQLLEN 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 ( unicode ) {
+ colSize *= 2; // a tiny bit faster, since it saves a SQLGetData() call
+ }
+ }
+ char* buf = new char[ colSize ];
+ while ( TRUE ) {
+ r = SQLGetData( hStmt,
+ column+1,
+ unicode ? SQL_C_WCHAR : SQL_C_CHAR,
+ (SQLPOINTER)buf,
+ (TQSQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL ) {
+ fieldVal = TQString::null;
+ isNull = TRUE;
+ break;
+ }
+ // if SQL_SUCCESS_WITH_INFO is returned, indicating that
+ // more data can be fetched, the length indicator does NOT
+ // contain the number of bytes returned - it contains the
+ // total number of bytes that CAN be fetched
+ // colSize-1: remove 0 termination when there is more data to fetch
+ int rSize = (r == SQL_SUCCESS_WITH_INFO) ? (unicode ? colSize-2 : colSize-1) : lengthIndicator;
+ if ( unicode ) {
+ fieldVal += TQString( (TQChar*) buf, rSize / 2 );
+ } else {
+ buf[ rSize ] = 0;
+ fieldVal += buf;
+ }
+ if ( lengthIndicator < colSize ) {
+ // workaround for Drivermanagers that don't return SQL_NO_DATA
+ break;
+ }
+ } else if ( r == SQL_NO_DATA ) {
+ break;
+ } else {
+#ifdef QT_CHECK_RANGE
+ tqWarning( "qGetStringData: Error while fetching data (%d)", r );
+#endif
+ fieldVal = TQString::null;
+ break;
+ }
+ }
+ delete[] buf;
+ return fieldVal;
+}
+
+static TQByteArray qGetBinaryData( SQLHANDLE hStmt, int column, TQSQLLEN& lengthIndicator, bool& isNull )
+{
+ TQByteArray fieldVal;
+ SQLSMALLINT colNameLen;
+ SQLSMALLINT colType;
+ TQSQLULEN colSize;
+ SQLSMALLINT colScale;
+ SQLSMALLINT nullable;
+ SQLRETURN r = SQL_ERROR;
+
+ SQLTCHAR colName[COLNAMESIZE];
+ r = SQLDescribeCol( hStmt,
+ column+1,
+ colName,
+ COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ tqWarning( "qGetBinaryData: Unable to describe column %d", column );
+#endif
+ // SQLDescribeCol 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 = SQLGetData( hStmt,
+ column+1,
+ SQL_C_BINARY,
+ (SQLPOINTER) buf,
+ (TQSQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ break;
+ } else {
+ int rSize;
+ r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
+ if ( lengthIndicator == SQL_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 == SQL_SUCCESS ) { // the whole field was read in one chunk
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ delete [] buf;
+ return fieldVal;
+}
+
+static int qGetIntData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ TQSQLLEN intbuf = 0;
+ isNull = FALSE;
+ TQSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_SLONG,
+ (SQLPOINTER)&intbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ return 0;
+ }
+ return (int)intbuf;
+}
+
+static double qGetDoubleData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ SQLDOUBLE dblbuf;
+ TQSQLLEN lengthIndicator = 0;
+ isNull = FALSE;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_DOUBLE,
+ (SQLPOINTER)&dblbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ return 0.0;
+ }
+
+ return (double) dblbuf;
+}
+
+static SQLBIGINT qGetBigIntData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ SQLBIGINT lngbuf = TQ_INT64_C( 0 );
+ isNull = FALSE;
+ TQSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_SBIGINT,
+ (SQLPOINTER) &lngbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA )
+ isNull = TRUE;
+
+ return lngbuf;
+}
+
+// creates a TQSqlFieldInfo from a valid hStmt generated
+// by SQLColumns. The hStmt has to point to a valid position.
+static TQSqlFieldInfo qMakeFieldInfo( const SQLHANDLE hStmt, const TQODBCPrivate* p )
+{
+ bool isNull;
+ TQString fname = qGetStringData( hStmt, 3, -1, isNull, p->unicode );
+ int type = qGetIntData( hStmt, 4, isNull ); // column type
+ int required = qGetIntData( hStmt, 10, isNull ); // nullable-flag
+ // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+ if ( required == SQL_NO_NULLS ) {
+ required = 1;
+ } else if ( required == SQL_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 )
+{
+ SQLSMALLINT colNameLen;
+ SQLSMALLINT colType;
+ TQSQLULEN colSize;
+ SQLSMALLINT colScale;
+ SQLSMALLINT nullable;
+ SQLRETURN r = SQL_ERROR;
+ SQLTCHAR colName[ COLNAMESIZE ];
+ r = SQLDescribeCol( p->hStmt,
+ i+1,
+ colName,
+ (TQSQLULEN)COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable);
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_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 SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+ int required = -1;
+ if ( nullable == SQL_NO_NULLS ) {
+ required = 1;
+ } else if ( nullable == SQL_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;
+ SQLRETURN r = SQL_SUCCESS;
+ TQMap<TQString, TQString> connMap;
+ for ( TQStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) {
+ TQString tmp( *it );
+ int idx;
+ if ( (idx = tmp.find( '=' )) != -1 )
+ connMap[ tmp.left( idx ) ] = tmp.mid( idx + 1 ).simplifyWhiteSpace();
+ else
+ tqWarning( "TQODBCDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ }
+ if ( connMap.count() ) {
+ TQMap<TQString, TQString>::ConstIterator it;
+ TQString opt, val;
+ SQLUINTEGER v = 0;
+ for ( it = connMap.begin(); it != connMap.end(); ++it ) {
+ opt = it.key().upper();
+ val = it.data().upper();
+ r = SQL_SUCCESS;
+ if ( opt == "SQL_ATTR_ACCESS_MODE" ) {
+ if ( val == "SQL_MODE_READ_ONLY" ) {
+ v = SQL_MODE_READ_ONLY;
+ } else if ( val == "SQL_MODE_READ_WRITE" ) {
+ v = SQL_MODE_READ_WRITE;
+ } else {
+ tqWarning( "TQODBCDriver::open: Unknown option value '%s'", (*it).latin1() );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_CONNECTION_TIMEOUT" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_LOGIN_TIMEOUT" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_CURRENT_CATALOG" ) {
+ val.ucs2(); // 0 terminate
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_CURRENT_CATALOG,
+#ifdef UNICODE
+ (SQLWCHAR*) val.unicode(),
+#else
+ (SQLCHAR*) val.latin1(),
+#endif
+ SQL_NTS );
+ } else if ( opt == "SQL_ATTR_METADATA_ID" ) {
+ if ( val == "SQL_TRUE" ) {
+ v = SQL_TRUE;
+ } else if ( val == "SQL_FALSE" ) {
+ v = SQL_FALSE;
+ } else {
+ tqWarning( "TQODBCDriver::open: Unknown option value '%s'", (*it).latin1() );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_PACKET_SIZE" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_TRACEFILE" ) {
+ val.ucs2(); // 0 terminate
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACEFILE,
+#ifdef UNICODE
+ (SQLWCHAR*) val.unicode(),
+#else
+ (SQLCHAR*) val.latin1(),
+#endif
+ SQL_NTS );
+ } else if ( opt == "SQL_ATTR_TRACE" ) {
+ if ( val == "SQL_OPT_TRACE_OFF" ) {
+ v = SQL_OPT_TRACE_OFF;
+ } else if ( val == "SQL_OPT_TRACE_ON" ) {
+ v = SQL_OPT_TRACE_ON;
+ } else {
+ tqWarning( "TQODBCDriver::open: Unknown option value '%s'", (*it).latin1() );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0 );
+ }
+#ifdef QT_CHECK_RANGE
+ else {
+ tqWarning( "TQODBCDriver::open: Unknown connection attribute '%s'", opt.latin1() );
+ }
+#endif
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_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() ) {
+ SQLRETURN r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_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 );
+ SQLRETURN r;
+
+ d->rInf.clear();
+ // Always reallocate the statement handle - the statement attributes
+ // are not reset if SQLFreeStmt() is called which causes some problems.
+ if ( d->hStmt ) {
+ r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::reset: Unable to free statement handle", d );
+#endif
+ return FALSE;
+ }
+ }
+ r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::reset: Unable to allocate statement handle", d );
+#endif
+ return FALSE;
+ }
+
+ if ( isForwardOnly() ) {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ } else {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_STATIC,
+ SQL_IS_UINTEGER );
+ }
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
+#endif
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ r = SQLExecDirect( d->hStmt,
+ (SQLWCHAR*) query.unicode(),
+ (SQLINTEGER) query.length() );
+#else
+ TQCString query8 = query.local8Bit();
+ r = SQLExecDirect( d->hStmt,
+ (SQLCHAR*) query8.data(),
+ (SQLINTEGER) query8.length() );
+#endif
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+ setLastError( qMakeError( "Unable to execute statement", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ SQLSMALLINT count;
+ r = SQLNumResultCols( 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;
+ }
+ SQLRETURN r;
+ if ( isForwardOnly() ) {
+ bool ok = TRUE;
+ while ( ok && i > at() )
+ ok = fetchNext();
+ return ok;
+ } else {
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_ABSOLUTE,
+ actualIdx );
+ }
+ if ( r != SQL_SUCCESS ){
+ return FALSE;
+ }
+ setAt( i );
+ return TRUE;
+}
+
+bool TQODBCResult::fetchNext()
+{
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( at() + 1 );
+ return TRUE;
+}
+
+bool TQODBCResult::fetchFirst()
+{
+ if ( isForwardOnly() && at() != TQSql::BeforeFirst )
+ return FALSE;
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ if ( isForwardOnly() ) {
+ return fetchNext();
+ }
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_FIRST,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( 0 );
+ return TRUE;
+}
+
+bool TQODBCResult::fetchPrior()
+{
+ if ( isForwardOnly() )
+ return FALSE;
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_PRIOR,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( at() - 1 );
+ return TRUE;
+}
+
+bool TQODBCResult::fetchLast()
+{
+ SQLRETURN 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 = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_LAST,
+ 0 );
+ if ( r != SQL_SUCCESS ) {
+ return FALSE;
+ }
+ SQLINTEGER currRow;
+ r = SQLGetStmtAttr( d->hStmt,
+ SQL_ROW_NUMBER,
+ &currRow,
+ SQL_IS_INTEGER,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( currRow-1 );
+ return TRUE;
+}
+
+TQVariant TQODBCResult::data( int field )
+{
+ if ( field >= (int) d->rInf.count() ) {
+ tqWarning( "TQODBCResult::data: column %d out of range", field );
+ return TQVariant();
+ }
+ if ( fieldCache.contains( field ) )
+ return fieldCache[ field ];
+ SQLRETURN r(0);
+ TQSQLLEN 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 = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_DATE,
+ (SQLPOINTER)&dbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_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 = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_TIME,
+ (SQLPOINTER)&tbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_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 = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_TIMESTAMP,
+ (SQLPOINTER)&dtbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_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() == SQL_DECIMAL || info.typeID() == SQL_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.contains( field ) ) {
+ // since there is no good way to find 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()
+{
+ TQSQLLEN affectedRowCount(0);
+ SQLRETURN r = SQLRowCount( d->hStmt, &affectedRowCount );
+ if ( r == SQL_SUCCESS )
+ return affectedRowCount;
+#ifdef QT_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 );
+ SQLRETURN r;
+
+ d->rInf.clear();
+ if ( d->hStmt ) {
+ r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to close statement", d );
+#endif
+ return FALSE;
+ }
+ }
+ r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to allocate statement handle", d );
+#endif
+ return FALSE;
+ }
+
+ if ( isForwardOnly() ) {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ } else {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_STATIC,
+ SQL_IS_UINTEGER );
+ }
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
+#endif
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ r = SQLPrepare( d->hStmt,
+ (SQLWCHAR*) query.unicode(),
+ (SQLINTEGER) query.length() );
+#else
+ TQCString query8 = query.local8Bit();
+ r = SQLPrepare( d->hStmt,
+ (SQLCHAR*) query8.data(),
+ (SQLINTEGER) query8.length() );
+#endif
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to prepare statement", d );
+#endif
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQODBCResult::exec()
+{
+ SQLRETURN 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 QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::exec: No statement handle available", d );
+#endif
+ return FALSE;
+ } else {
+ r = SQLFreeStmt( d->hStmt, SQL_CLOSE );
+ if ( r != SQL_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;
+ TQSQLLEN *ind = new TQSQLLEN( SQL_NTS );
+ tmpStorage.append( qAutoDeleter(ind) );
+ if ( val.isNull() ) {
+ *ind = SQL_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 = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_DATE,
+ SQL_DATE,
+ 0,
+ 0,
+ (void *) dt,
+ (TQSQLLEN)0,
+ *ind == SQL_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 = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_TIME,
+ SQL_TIME,
+ 0,
+ 0,
+ (void *) dt,
+ (TQSQLLEN)0,
+ *ind == SQL_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 = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_TIMESTAMP,
+ SQL_TIMESTAMP,
+ 0,
+ 0,
+ (void *) dt,
+ (TQSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::Int: {
+ int * v = new int( val.toInt() );
+ tmpStorage.append( qAutoDeleter(v) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_SLONG,
+ SQL_INTEGER,
+ 0,
+ 0,
+ (void *) v,
+ (TQSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::Double: {
+ double * v = new double( val.toDouble() );
+ tmpStorage.append( qAutoDeleter(v) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_DOUBLE,
+ SQL_DOUBLE,
+ 0,
+ 0,
+ (void *) v,
+ (TQSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::ByteArray: {
+ if ( *ind != SQL_NULL_DATA ) {
+ *ind = val.asByteArray().size();
+ }
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_BINARY,
+ SQL_LONGVARBINARY,
+ val.asByteArray().size(),
+ 0,
+ (void *) val.asByteArray().data(),
+ (TQSQLLEN)val.asByteArray().size(),
+ ind );
+ break; }
+#ifndef Q_ODBC_VERSION_2
+ case TQVariant::String:
+ if ( d->unicode ) {
+ TQString * str = new TQString( val.asString() );
+ str->ucs2();
+ int len = str->length()*2;
+ tmpStorage.append( qAutoDeleter(str) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_WCHAR,
+ len > 8000 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ len > 8000 ? len : 0,
+ 0,
+ (void *) str->unicode(),
+ (TQSQLLEN) len,
+ ind );
+ break;
+ }
+#endif
+ // fall through
+ default: {
+ TQCString * str = new TQCString( val.asString().local8Bit() );
+ tmpStorage.append( qAutoDeleter(str) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_CHAR,
+ str->length() > 4000 ? SQL_LONGVARCHAR : SQL_VARCHAR,
+ str->length() + 1,
+ 0,
+ (void *) str->data(),
+ (TQSQLLEN)(str->length() + 1),
+ ind );
+ break; }
+ }
+ para++;
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ tqWarning( "TQODBCResult::exec: unable to bind variable: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to bind variable", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ }
+ }
+ r = SQLExecute( d->hStmt );
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ tqWarning( "TQODBCResult::exec: Unable to execute statement: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to execute statement", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ SQLSMALLINT count;
+ r = SQLNumResultCols( 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 ) {
+
+ SQLINTEGER* indPtr = qAutoDeleterData( (TQAutoDeleter<SQLINTEGER>*)tmpStorage.getFirst() );
+ if ( !indPtr )
+ return FALSE;
+ bool isNull = (*indPtr == SQL_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->unicode ) {
+ 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 * parent, const char * name )
+ : TQSqlDriver(parent,name ? name : "TQODBC")
+{
+ init();
+}
+
+TQODBCDriver::TQODBCDriver( SQLHANDLE env, SQLHANDLE con, TQObject * parent, const char * name )
+ : TQSqlDriver(parent,name ? name : "TQODBC")
+{
+ init();
+ d->hEnv = env;
+ d->hDbc = con;
+ if ( env && con ) {
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ }
+}
+
+void TQODBCDriver::init()
+{
+ tqSqlOpenExtDict()->insert( this, new TQODBCOpenExtension(this) );
+ d = new TQODBCPrivate();
+}
+
+TQODBCDriver::~TQODBCDriver()
+{
+ cleanup();
+ delete d;
+ if ( !tqSqlOpenExtDict()->isEmpty() ) {
+ TQSqlOpenExtension *ext = tqSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool TQODBCDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions: {
+ if ( !d->hDbc )
+ return FALSE;
+ SQLUSMALLINT txn;
+ SQLSMALLINT t;
+ int r = SQLGetInfo( d->hDbc,
+ (SQLUSMALLINT)SQL_TXN_CAPABLE,
+ &txn,
+ sizeof(txn),
+ &t);
+ if ( r != SQL_SUCCESS || txn == SQL_TC_NONE )
+ return FALSE;
+ else
+ return TRUE;
+ }
+ case QuerySize:
+ return FALSE;
+ case BLOB:
+ return TRUE;
+ case Unicode:
+ return d->unicode;
+ case PreparedQueries:
+ return TRUE;
+ case PositionalPlaceholders:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+bool TQODBCDriver::open( const TQString&,
+ const TQString&,
+ const TQString&,
+ const TQString&,
+ int )
+{
+ tqWarning("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();
+ SQLRETURN r;
+ r = SQLAllocHandle( SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &d->hEnv);
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::open: Unable to allocate environment", d );
+#endif
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ r = SQLSetEnvAttr( d->hEnv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER)SQL_OV_ODBC2,
+ SQL_IS_UINTEGER );
+ r = SQLAllocHandle( SQL_HANDLE_DBC,
+ d->hEnv,
+ &d->hDbc);
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_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.contains(".dsn") )
+ connTQStr = "FILEDSN=" + db;
+ else if ( db.contains( "DRIVER" ) || db.contains( "SERVER" ) )
+ connTQStr = db;
+ else
+ connTQStr = "DSN=" + db;
+ connTQStr += ";UID=" + user + ";PWD=" + password;
+ SQLSMALLINT cb;
+ SQLTCHAR connOut[1024];
+ r = SQLDriverConnect( d->hDbc,
+ NULL,
+#ifdef UNICODE
+ (SQLWCHAR*)connTQStr.unicode(),
+#else
+ (SQLCHAR*)connTQStr.latin1(),
+#endif
+ (SQLSMALLINT)connTQStr.length(),
+ connOut,
+ 1024,
+ &cb,
+ SQL_DRIVER_NOPROMPT );
+ if ( r != SQL_SUCCESS && r != SQL_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 );
+}
+
+bool TQODBCDriver::ping()
+{
+ // FIXME
+ // Implement ping if supported
+ return TRUE;
+}
+
+void TQODBCDriver::cleanup()
+{
+ SQLRETURN r;
+ if ( !d )
+ return;
+
+ if( d->hDbc ) {
+ // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
+ if ( isOpen() ) {
+ r = SQLDisconnect( d->hDbc );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::disconnect: Unable to disconnect datasource", d );
+#endif
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_DBC, d->hDbc );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::cleanup: Unable to free connection handle", d );
+#endif
+ d->hDbc = 0;
+ }
+
+ if ( d->hEnv ) {
+ r = SQLFreeHandle( SQL_HANDLE_ENV, d->hEnv );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_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 unicode characters
+void TQODBCPrivate::checkUnicode()
+{
+#if defined(TQ_WS_WIN)
+ if ( !qt_winunicode ) {
+ unicode = FALSE;
+ return;
+ }
+#endif
+ SQLRETURN r;
+ SQLUINTEGER fFunc;
+
+ unicode = FALSE;
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_CHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WCHAR ) ) {
+ sql_char_type = TQVariant::String;
+ unicode = TRUE;
+ }
+
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_VARCHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WVARCHAR ) ) {
+ sql_varchar_type = TQVariant::String;
+ unicode = TRUE;
+ }
+
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_LONGVARCHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WLONGVARCHAR ) ) {
+ sql_longvarchar_type = TQVariant::String;
+ unicode = TRUE;
+ }
+}
+
+bool TQODBCPrivate::checkDriver() const
+{
+#ifdef ODBC_CHECK_DRIVER
+ // do not query for SQL_API_SQLFETCHSCROLL because it can't be used at this time
+ static const SQLUSMALLINT reqFunc[] = {
+ SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
+ SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
+ SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
+ };
+
+ // these functions are optional
+ static const SQLUSMALLINT optFunc[] = {
+ SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
+ };
+
+ SQLRETURN r;
+ SQLUSMALLINT sup;
+
+
+ int i;
+ // check the required functions
+ for ( i = 0; reqFunc[ i ] != 0; ++i ) {
+
+ r = SQLGetFunctions( hDbc, reqFunc[ i ], &sup );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == SQL_FALSE ) {
+#ifdef QT_CHECK_RANGE
+ tqWarning ( "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 = SQLGetFunctions( hDbc, optFunc[ i ], &sup );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == SQL_FALSE ) {
+#ifdef QT_CHECK_RANGE
+ tqWarning( "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()
+{
+ SQLRETURN r;
+ SQLUINTEGER val;
+
+ r = SQLGetInfo(hDbc,
+ SQL_SCHEMA_USAGE,
+ (SQLPOINTER) &val,
+ sizeof(val),
+ NULL);
+ if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+ useSchema = (val != 0);
+}
+
+TQSqlQuery TQODBCDriver::createQuery() const
+{
+ return TQSqlQuery( new TQODBCResult( this, d ) );
+}
+
+bool TQODBCDriver::beginTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ tqWarning(" TQODBCDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
+ SQLRETURN r = SQLSetConnectAttr( d->hDbc,
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER)ac,
+ sizeof(ac) );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to disable autocommit", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQODBCDriver::commitTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ tqWarning(" TQODBCDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC,
+ d->hDbc,
+ SQL_COMMIT );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError("Unable to commit transaction", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool TQODBCDriver::rollbackTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ tqWarning(" TQODBCDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC,
+ d->hDbc,
+ SQL_ROLLBACK );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to rollback transaction", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool TQODBCDriver::endTrans()
+{
+ SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
+ SQLRETURN r = SQLSetConnectAttr( d->hDbc,
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER)ac,
+ sizeof(ac));
+ if ( r != SQL_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();
+ SQLHANDLE hStmt;
+
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::tables: Unable to allocate handle", d );
+#endif
+ return tl;
+ }
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_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 = SQLTables( hStmt,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ 0,
+#ifdef UNICODE
+ (SQLWCHAR*)tableType.unicode(),
+#else
+ (SQLCHAR*)tableType.latin1(),
+#endif
+ tableType.length() /* characters, not bytes */ );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::tables Unable to execute table list", d );
+#endif
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ while ( r == SQL_SUCCESS ) {
+ bool isNull;
+ TQString fieldVal = qGetStringData( hStmt, 2, -1, isNull, d->unicode );
+ tl.append( fieldVal );
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_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 );
+
+ SQLHANDLE hStmt;
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_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 = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ r = SQLPrimaryKeys( hStmt,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+ table.length() /* in characters, not in bytes */);
+
+ // if the SQLPrimaryKeys() 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 != SQL_SUCCESS ) {
+ r = SQLSpecialColumns( hStmt,
+ SQL_BEST_ROWID,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+
+ table.length(),
+ SQL_SCOPE_CURROW,
+ SQL_NULLABLE );
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::primaryIndex: Unable to execute primary key list", d );
+#endif
+ } else {
+ usingSpecialColumns = TRUE;
+ }
+ }
+ r = SQLFetchScroll( hStmt,
+ SQL_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 == SQL_SUCCESS ) {
+ if ( usingSpecialColumns ) {
+ cName = qGetStringData( hStmt, 1, -1, isNull, d->unicode ); // column name
+ idxName = TQString::number( fakeId++ ); // invent a fake index name
+ } else {
+ cName = qGetStringData( hStmt, 3, -1, isNull, d->unicode ); // column name
+ idxName = qGetStringData( hStmt, 5, -1, isNull, d->unicode ); // pk index name
+ }
+ TQSqlField *fld = rec.field(cName);
+ if (fld)
+ index.append(*fld);
+ index.setName( idxName );
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ }
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_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;
+
+ SQLHANDLE hStmt;
+ TQString catalog, schema, table;
+ d->splitTableQualifier( tablename, catalog, schema, table );
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::record: Unable to allocate handle", d );
+#endif
+ return fil;
+ }
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ r = SQLColumns( hStmt,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+ table.length(),
+ NULL,
+ 0 );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::record: Unable to execute column list", d );
+#endif
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
+ while ( r == SQL_SUCCESS ) {
+
+ fil.append( qMakeFieldInfo( hStmt, d ) );
+
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_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;
+}
+
+SQLHANDLE TQODBCDriver::environment()
+{
+ return d->hEnv;
+}
+
+SQLHANDLE 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;
+}