summaryrefslogtreecommitdiffstats
path: root/src/sql/drivers/mysql/qsql_mysql.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sql/drivers/mysql/qsql_mysql.cpp')
-rw-r--r--src/sql/drivers/mysql/qsql_mysql.cpp775
1 files changed, 775 insertions, 0 deletions
diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp
new file mode 100644
index 000000000..b0afb436d
--- /dev/null
+++ b/src/sql/drivers/mysql/qsql_mysql.cpp
@@ -0,0 +1,775 @@
+/****************************************************************************
+**
+** Implementation of MYSQL 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 retquirements 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 "qsql_mysql.h"
+#include <private/qsqlextension_p.h>
+
+#include <qdatetime.h>
+#include <qvaluevector.h>
+#include <qsqlrecord.h>
+
+#define TQMYSQL_DRIVER_NAME "TQMYSQL3"
+
+#ifdef Q_OS_WIN32
+// comment the next line out if you want to use MySQL/embedded on Win32 systems.
+// note that it will crash if you don't statically link to the mysql/e library!
+# define Q_NO_MYSQL_EMBEDDED
+#endif
+
+TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict();
+
+static int qMySqlConnectionCount = 0;
+static bool qMySqlInitHandledByUser = FALSE;
+
+class TQMYSQLOpenExtension : public TQSqlOpenExtension
+{
+public:
+ TQMYSQLOpenExtension( TQMYSQLDriver *dri )
+ : TQSqlOpenExtension(), driver(dri) {}
+ ~TQMYSQLOpenExtension() {}
+
+ bool open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts );
+
+private:
+ TQMYSQLDriver *driver;
+};
+
+bool TQMYSQLOpenExtension::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 );
+}
+
+class TQMYSQLDriverPrivate
+{
+public:
+ TQMYSQLDriverPrivate() : mysql(0) {}
+ MYSQL* mysql;
+};
+
+class TQMYSQLResultPrivate : public TQMYSQLDriverPrivate
+{
+public:
+ TQMYSQLResultPrivate() : TQMYSQLDriverPrivate(), result(0) {}
+ MYSQL_RES* result;
+ MYSQL_ROW row;
+ TQValueVector<TQVariant::Type> fieldTypes;
+};
+
+TQSqlError qMakeError( const TQString& err, int type, const TQMYSQLDriverPrivate* p )
+{
+ return TQSqlError(TQMYSQL_DRIVER_NAME ": " + err, TQString(mysql_error( p->mysql )), type, mysql_errno( p->mysql ));
+}
+
+TQVariant::Type qDecodeMYSQLType( int mysqltype, uint flags )
+{
+ TQVariant::Type type;
+ switch ( mysqltype ) {
+ case FIELD_TYPE_TINY :
+ case FIELD_TYPE_SHORT :
+ case FIELD_TYPE_LONG :
+ case FIELD_TYPE_INT24 :
+ type = (flags & UNSIGNED_FLAG) ? TQVariant::UInt : TQVariant::Int;
+ break;
+ case FIELD_TYPE_YEAR :
+ type = TQVariant::Int;
+ break;
+ case FIELD_TYPE_LONGLONG :
+ type = (flags & UNSIGNED_FLAG) ? TQVariant::ULongLong : TQVariant::LongLong;
+ break;
+ case FIELD_TYPE_DECIMAL :
+ case FIELD_TYPE_FLOAT :
+ case FIELD_TYPE_DOUBLE :
+ type = TQVariant::Double;
+ break;
+ case FIELD_TYPE_DATE :
+ type = TQVariant::Date;
+ break;
+ case FIELD_TYPE_TIME :
+ type = TQVariant::Time;
+ break;
+ case FIELD_TYPE_DATETIME :
+ case FIELD_TYPE_TIMESTAMP :
+ type = TQVariant::DateTime;
+ break;
+ case FIELD_TYPE_BLOB :
+ case FIELD_TYPE_TINY_BLOB :
+ case FIELD_TYPE_MEDIUM_BLOB :
+ case FIELD_TYPE_LONG_BLOB :
+ type = (flags & BINARY_FLAG) ? TQVariant::ByteArray : TQVariant::CString;
+ break;
+ default:
+ case FIELD_TYPE_ENUM :
+ case FIELD_TYPE_SET :
+ case FIELD_TYPE_STRING :
+ case FIELD_TYPE_VAR_STRING :
+ type = TQVariant::String;
+ break;
+ }
+ return type;
+}
+
+TQMYSQLResult::TQMYSQLResult( const TQMYSQLDriver* db )
+: TQSqlResult( db )
+{
+ d = new TQMYSQLResultPrivate();
+ d->mysql = db->d->mysql;
+}
+
+TQMYSQLResult::~TQMYSQLResult()
+{
+ cleanup();
+ delete d;
+}
+
+MYSQL_RES* TQMYSQLResult::result()
+{
+ return d->result;
+}
+
+void TQMYSQLResult::cleanup()
+{
+ if ( d->result ) {
+ mysql_free_result( d->result );
+ }
+ d->result = NULL;
+ d->row = NULL;
+ setAt( -1 );
+ setActive( FALSE );
+}
+
+bool TQMYSQLResult::fetch( int i )
+{
+ if ( isForwardOnly() ) { // fake a forward seek
+ if ( at() < i ) {
+ int x = i - at();
+ while ( --x && fetchNext() );
+ return fetchNext();
+ } else {
+ return FALSE;
+ }
+ }
+ if ( at() == i )
+ return TRUE;
+ mysql_data_seek( d->result, i );
+ d->row = mysql_fetch_row( d->result );
+ if ( !d->row )
+ return FALSE;
+ setAt( i );
+ return TRUE;
+}
+
+bool TQMYSQLResult::fetchNext()
+{
+ d->row = mysql_fetch_row( d->result );
+ if ( !d->row )
+ return FALSE;
+ setAt( at() + 1 );
+ return TRUE;
+}
+
+bool TQMYSQLResult::fetchLast()
+{
+ if ( isForwardOnly() ) { // fake this since MySQL can't seek on forward only queries
+ bool success = fetchNext(); // did we move at all?
+ while ( fetchNext() );
+ return success;
+ }
+ my_ulonglong numRows = mysql_num_rows( d->result );
+ if ( !numRows )
+ return FALSE;
+ return fetch( numRows - 1 );
+}
+
+bool TQMYSQLResult::fetchFirst()
+{
+ if ( isForwardOnly() ) // again, fake it
+ return fetchNext();
+ return fetch( 0 );
+}
+
+TQVariant TQMYSQLResult::data( int field )
+{
+ if ( !isSelect() || field >= (int) d->fieldTypes.count() ) {
+ qWarning( "TQMYSQLResult::data: column %d out of range", field );
+ return TQVariant();
+ }
+
+ TQString val( d->row[field] );
+ switch ( d->fieldTypes.at( field ) ) {
+ case TQVariant::LongLong:
+ return TQVariant( val.toLongLong() );
+ case TQVariant::ULongLong:
+ return TQVariant( val.toULongLong() );
+ case TQVariant::Int:
+ return TQVariant( val.toInt() );
+ case TQVariant::UInt:
+ return TQVariant( val.toUInt() );
+ case TQVariant::Double:
+ return TQVariant( val.toDouble() );
+ case TQVariant::Date:
+ if ( val.isEmpty() ) {
+ return TQVariant( TQDate() );
+ } else {
+ return TQVariant( TQDate::fromString( val, TQt::ISODate ) );
+ }
+ case TQVariant::Time:
+ if ( val.isEmpty() ) {
+ return TQVariant( TQTime() );
+ } else {
+ return TQVariant( TQTime::fromString( val, TQt::ISODate ) );
+ }
+ case TQVariant::DateTime:
+ if ( val.isEmpty() )
+ return TQVariant( TQDateTime() );
+ if ( val.length() == 14u )
+ // TIMESTAMPS have the format yyyyMMddhhmmss
+ val.insert(4, "-").insert(7, "-").insert(10, 'T').insert(13, ':').insert(16, ':');
+ return TQVariant( TQDateTime::fromString( val, TQt::ISODate ) );
+ case TQVariant::ByteArray: {
+ unsigned long* fl = mysql_fetch_lengths( d->result );
+ TQByteArray ba;
+ ba.duplicate( d->row[field], fl[field] );
+ return TQVariant( ba );
+ }
+ default:
+ case TQVariant::String:
+ case TQVariant::CString:
+ return TQVariant( val );
+ }
+#ifdef QT_CHECK_RANGE
+ qWarning("TQMYSQLResult::data: unknown data type");
+#endif
+ return TQVariant();
+}
+
+bool TQMYSQLResult::isNull( int field )
+{
+ if ( d->row[field] == NULL )
+ return TRUE;
+ return FALSE;
+}
+
+bool TQMYSQLResult::reset ( const TQString& query )
+{
+ if ( !driver() )
+ return FALSE;
+ if ( !driver()-> isOpen() || driver()->isOpenError() )
+ return FALSE;
+ cleanup();
+
+ const char *encQuery = query.ascii();
+ if ( mysql_real_query( d->mysql, encQuery, qstrlen(encQuery) ) ) {
+ setLastError( qMakeError("Unable to execute query", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ if ( isForwardOnly() ) {
+ if ( isActive() || isValid() ) // have to empty the results from previous query
+ fetchLast();
+ d->result = mysql_use_result( d->mysql );
+ } else {
+ d->result = mysql_store_result( d->mysql );
+ }
+ if ( !d->result && mysql_field_count( d->mysql ) > 0 ) {
+ setLastError( qMakeError( "Unable to store result", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ int numFields = mysql_field_count( d->mysql );
+ setSelect( !( numFields == 0) );
+ d->fieldTypes.resize( numFields );
+ if ( isSelect() ) {
+ for( int i = 0; i < numFields; i++) {
+ MYSQL_FIELD* field = mysql_fetch_field_direct( d->result, i );
+ if ( field->type == FIELD_TYPE_DECIMAL )
+ d->fieldTypes[i] = TQVariant::String;
+ else
+ d->fieldTypes[i] = qDecodeMYSQLType( field->type, field->flags );
+ }
+ }
+ setActive( TRUE );
+ return TRUE;
+}
+
+int TQMYSQLResult::size()
+{
+ return isSelect() ? (int)mysql_num_rows( d->result ) : -1;
+}
+
+int TQMYSQLResult::numRowsAffected()
+{
+ return (int)mysql_affected_rows( d->mysql );
+}
+
+/////////////////////////////////////////////////////////
+static void qServerEnd()
+{
+#ifndef Q_NO_MYSQL_EMBEDDED
+# if MYSQL_VERSION_ID >= 40000
+ mysql_server_end();
+# endif // MYSQL_VERSION_ID
+#endif // Q_NO_MYSQL_EMBEDDED
+}
+
+static void qServerInit()
+{
+#ifndef Q_NO_MYSQL_EMBEDDED
+# if MYSQL_VERSION_ID >= 40000
+ if ( qMySqlInitHandledByUser || qMySqlConnectionCount > 1 )
+ return;
+
+ // this should only be called once
+ // has no effect on client/server library
+ // but is vital for the embedded lib
+ if ( mysql_server_init( 0, 0, 0 ) ) {
+# ifdef QT_CHECK_RANGE
+ qWarning( "TQMYSQLDriver::qServerInit: unable to start server." );
+# endif
+ }
+
+# endif // MYSQL_VERSION_ID
+#endif // Q_NO_MYSQL_EMBEDDED
+}
+
+TQMYSQLDriver::TQMYSQLDriver( TQObject * parent, const char * name )
+ : TQSqlDriver( parent, name ? name : TQMYSQL_DRIVER_NAME )
+{
+ init();
+ qServerInit();
+}
+
+/*!
+ Create a driver instance with an already open connection handle.
+*/
+
+TQMYSQLDriver::TQMYSQLDriver( MYSQL * con, TQObject * parent, const char * name )
+ : TQSqlDriver( parent, name ? name : TQMYSQL_DRIVER_NAME )
+{
+ init();
+ if ( con ) {
+ d->mysql = (MYSQL *) con;
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ if (qMySqlConnectionCount == 1)
+ qMySqlInitHandledByUser = TRUE;
+ } else {
+ qServerInit();
+ }
+}
+
+void TQMYSQLDriver::init()
+{
+ qSqlOpenExtDict()->insert( this, new TQMYSQLOpenExtension(this) );
+ d = new TQMYSQLDriverPrivate();
+ d->mysql = 0;
+ qMySqlConnectionCount++;
+}
+
+TQMYSQLDriver::~TQMYSQLDriver()
+{
+ qMySqlConnectionCount--;
+ if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser)
+ qServerEnd();
+
+ delete d;
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool TQMYSQLDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions:
+// CLIENT_TRANSACTION should be defined in all recent mysql client libs > 3.23.34
+#ifdef CLIENT_TRANSACTIONS
+ if ( d->mysql ) {
+ if ( ( d->mysql->server_capabilities & CLIENT_TRANSACTIONS ) == CLIENT_TRANSACTIONS )
+ return TRUE;
+ }
+#endif
+ return FALSE;
+ case QuerySize:
+ return TRUE;
+ case BLOB:
+ return TRUE;
+ case Unicode:
+ return FALSE;
+ default:
+ return FALSE;
+ }
+}
+
+bool TQMYSQLDriver::open( const TQString&,
+ const TQString&,
+ const TQString&,
+ const TQString&,
+ int )
+{
+ qWarning("TQMYSQLDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool TQMYSQLDriver::open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts )
+{
+ if ( isOpen() )
+ close();
+
+ unsigned int optionFlags = 0;
+
+ TQStringList raw = TQStringList::split( ';', connOpts );
+ TQStringList opts;
+ TQStringList::ConstIterator it;
+
+ // extract the real options from the string
+ for ( it = raw.begin(); it != raw.end(); ++it ) {
+ TQString tmp( *it );
+ int idx;
+ if ( (idx = tmp.find( '=' )) != -1 ) {
+ TQString val( tmp.mid( idx + 1 ) );
+ val.simplifyWhiteSpace();
+ if ( val == "TRUE" || val == "1" )
+ opts << tmp.left( idx );
+ else
+ qWarning( "TQMYSQLDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ } else {
+ opts << tmp;
+ }
+ }
+
+ for ( it = opts.begin(); it != opts.end(); ++it ) {
+ TQString opt( (*it).upper() );
+ if ( opt == "CLIENT_COMPRESS" )
+ optionFlags |= CLIENT_COMPRESS;
+ else if ( opt == "CLIENT_FOUND_ROWS" )
+ optionFlags |= CLIENT_FOUND_ROWS;
+ else if ( opt == "CLIENT_IGNORE_SPACE" )
+ optionFlags |= CLIENT_IGNORE_SPACE;
+ else if ( opt == "CLIENT_INTERACTIVE" )
+ optionFlags |= CLIENT_INTERACTIVE;
+ else if ( opt == "CLIENT_NO_SCHEMA" )
+ optionFlags |= CLIENT_NO_SCHEMA;
+ else if ( opt == "CLIENT_ODBC" )
+ optionFlags |= CLIENT_ODBC;
+ else if ( opt == "CLIENT_SSL" )
+ optionFlags |= CLIENT_SSL;
+ else
+ qWarning( "TQMYSQLDriver::open: Unknown connect option '%s'", (*it).latin1() );
+ }
+
+ if ( (d->mysql = mysql_init((MYSQL*) 0)) &&
+ mysql_real_connect( d->mysql,
+ host,
+ user,
+ password,
+ db.isNull() ? TQString("") : db,
+ (port > -1) ? port : 0,
+ NULL,
+ optionFlags ) )
+ {
+ if ( !db.isEmpty() && mysql_select_db( d->mysql, db )) {
+ setLastError( qMakeError("Unable open database '" + db + "'", TQSqlError::Connection, d ) );
+ mysql_close( d->mysql );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ } else {
+ setLastError( qMakeError( "Unable to connect", TQSqlError::Connection, d ) );
+ mysql_close( d->mysql );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void TQMYSQLDriver::close()
+{
+ if ( isOpen() ) {
+ mysql_close( d->mysql );
+ setOpen( FALSE );
+ setOpenError( FALSE );
+ }
+}
+
+TQSqlQuery TQMYSQLDriver::createQuery() const
+{
+ return TQSqlQuery( new TQMYSQLResult( this ) );
+}
+
+TQStringList TQMYSQLDriver::tables( const TQString& typeName ) const
+{
+ TQStringList tl;
+ if ( !isOpen() )
+ return tl;
+ if ( !typeName.isEmpty() && !(typeName.toInt() & (int)TQSql::Tables) )
+ return tl;
+
+ MYSQL_RES* tableRes = mysql_list_tables( d->mysql, NULL );
+ MYSQL_ROW row;
+ int i = 0;
+ while ( tableRes && TRUE ) {
+ mysql_data_seek( tableRes, i );
+ row = mysql_fetch_row( tableRes );
+ if ( !row )
+ break;
+ tl.append( TQString(row[0]) );
+ i++;
+ }
+ mysql_free_result( tableRes );
+ return tl;
+}
+
+TQSqlIndex TQMYSQLDriver::primaryIndex( const TQString& tablename ) const
+{
+ TQSqlIndex idx;
+ if ( !isOpen() )
+ return idx;
+ TQSqlQuery i = createQuery();
+ TQString stmt( "show index from %1;" );
+ TQSqlRecord fil = record( tablename );
+ i.exec( stmt.arg( tablename ) );
+ while ( i.isActive() && i.next() ) {
+ if ( i.value(2).toString() == "PRIMARY" ) {
+ idx.append( *fil.field( i.value(4).toString() ) );
+ idx.setCursorName( i.value(0).toString() );
+ idx.setName( i.value(2).toString() );
+ }
+ }
+ return idx;
+}
+
+TQSqlRecord TQMYSQLDriver::record( const TQString& tablename ) const
+{
+ TQSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ MYSQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0);
+ if ( !r ) {
+ return fil;
+ }
+ MYSQL_FIELD* field;
+ while ( (field = mysql_fetch_field( r ))) {
+ TQSqlField f ( TQString( field->name ) , qDecodeMYSQLType( (int)field->type, field->flags ) );
+ fil.append ( f );
+ }
+ mysql_free_result( r );
+ return fil;
+}
+
+TQSqlRecord TQMYSQLDriver::record( const TQSqlQuery& query ) const
+{
+ TQSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.isSelect() && query.driver() == this ) {
+ TQMYSQLResult* result = (TQMYSQLResult*)query.result();
+ TQMYSQLResultPrivate* p = result->d;
+ if ( !mysql_errno( p->mysql ) ) {
+ for ( ;; ) {
+ MYSQL_FIELD* f = mysql_fetch_field( p->result );
+ if ( f ) {
+ TQSqlField fi( TQString((const char*)f->name), qDecodeMYSQLType( f->type, f->flags ) );
+ fil.append( fi );
+ } else
+ break;
+ }
+ }
+ mysql_field_seek( p->result, 0 );
+ }
+ return fil;
+}
+
+TQSqlRecordInfo TQMYSQLDriver::recordInfo( const TQString& tablename ) const
+{
+ TQSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ MYSQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0);
+ if ( !r ) {
+ return info;
+ }
+ MYSQL_FIELD* field;
+ while ( (field = mysql_fetch_field( r ))) {
+ info.append ( TQSqlFieldInfo( TQString( field->name ),
+ qDecodeMYSQLType( (int)field->type, field->flags ),
+ IS_NOT_NULL( field->flags ),
+ (int)field->length,
+ (int)field->decimals,
+ TQString( field->def ),
+ (int)field->type ) );
+ }
+ mysql_free_result( r );
+ return info;
+}
+
+TQSqlRecordInfo TQMYSQLDriver::recordInfo( const TQSqlQuery& query ) const
+{
+ TQSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ if ( query.isActive() && query.isSelect() && query.driver() == this ) {
+ TQMYSQLResult* result = (TQMYSQLResult*)query.result();
+ TQMYSQLResultPrivate* p = result->d;
+ if ( !mysql_errno( p->mysql ) ) {
+ for ( ;; ) {
+ MYSQL_FIELD* field = mysql_fetch_field( p->result );
+ if ( field ) {
+ info.append ( TQSqlFieldInfo( TQString( field->name ),
+ qDecodeMYSQLType( (int)field->type, field->flags ),
+ IS_NOT_NULL( field->flags ),
+ (int)field->length,
+ (int)field->decimals,
+ TQVariant(),
+ (int)field->type ) );
+
+ } else
+ break;
+ }
+ }
+ mysql_field_seek( p->result, 0 );
+ }
+ return info;
+}
+
+MYSQL* TQMYSQLDriver::mysql()
+{
+ return d->mysql;
+}
+
+bool TQMYSQLDriver::beginTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQMYSQLDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "BEGIN WORK" ) ) {
+ setLastError( qMakeError("Unable to begin transaction", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQMYSQLDriver::commitTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQMYSQLDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "COMMIT" ) ) {
+ setLastError( qMakeError("Unable to commit transaction", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQMYSQLDriver::rollbackTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQMYSQLDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "ROLLBACK" ) ) {
+ setLastError( qMakeError("Unable to rollback transaction", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+TQString TQMYSQLDriver::formatValue( const TQSqlField* field, bool trimStrings ) const
+{
+ TQString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else {
+ switch( field->type() ) {
+ case TQVariant::ByteArray: {
+
+ const TQByteArray ba = field->value().toByteArray();
+ // buffer has to be at least length*2+1 bytes
+ char* buffer = new char[ ba.size() * 2 + 1 ];
+ /*uint escapedSize =*/ mysql_escape_string( buffer, ba.data(), ba.size() );
+ r.append("'").append(buffer).append("'");
+ delete[] buffer;
+ }
+ break;
+ case TQVariant::String:
+ case TQVariant::CString: {
+ // Escape '\' characters
+ r = TQSqlDriver::formatValue( field );
+ r.replace( "\\", "\\\\" );
+ break;
+ }
+ default:
+ r = TQSqlDriver::formatValue( field, trimStrings );
+ }
+ }
+ return r;
+}