diff options
Diffstat (limited to 'src/sql/qsqlquery.cpp')
-rw-r--r-- | src/sql/qsqlquery.cpp | 1215 |
1 files changed, 1215 insertions, 0 deletions
diff --git a/src/sql/qsqlquery.cpp b/src/sql/qsqlquery.cpp new file mode 100644 index 000000000..c33eb8bf2 --- /dev/null +++ b/src/sql/qsqlquery.cpp @@ -0,0 +1,1215 @@ +/**************************************************************************** +** +** Implementation of TQSqlQuery class +** +** Created : 2000-11-03 +** +** Copyright (C) 2000-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 [email protected]. +** +** 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 "qsqlquery.h" + +#ifndef QT_NO_SQL + +//#define QT_DEBUG_SQL + +#include "qsqlresult.h" +#include "qsqldriver.h" +#include "qsqldatabase.h" +#include "qsql.h" +#include "qregexp.h" +#include "private/qsqlextension_p.h" + + +/*! +\internal +*/ +TQSqlResultShared::TQSqlResultShared( TQSqlResult* result ): sqlResult(result) +{ + if ( result ) + connect( result->driver(), SIGNAL(destroyed()), this, SLOT(slotResultDestroyed()) ); +} + +/*! +\internal +*/ +TQSqlResultShared::~TQSqlResultShared() +{ + delete sqlResult; +} + +/*! +\internal + +In case a plugin gets unloaded the pointer to the sqlResult gets invalid +*/ +void TQSqlResultShared::slotResultDestroyed() +{ + delete sqlResult; + sqlResult = 0; +} + +/*! + \class TQSqlQuery qsqlquery.h + \brief The TQSqlQuery class provides a means of executing and + manipulating SQL statements. + + \ingroup database + \mainclass + \module sql + + TQSqlQuery encapsulates the functionality involved in creating, + navigating and retrieving data from SQL queries which are executed + on a \l TQSqlDatabase. It can be used to execute DML (data + manipulation language) statements, e.g. \c SELECT, \c INSERT, \c + UPDATE and \c DELETE, and also DDL (data definition language) + statements, e.g. \c{CREATE TABLE}. It can also be used to + execute database-specific commands which are not standard SQL + (e.g. \c{SET DATESTYLE=ISO} for PostgreSQL). + + Successfully executed SQL statements set the query's state to + active (isActive() returns TRUE); otherwise the query's state is + set to inactive. In either case, when executing a new SQL + statement, the query is positioned on an invalid record; an active + query must be navigated to a valid record (so that isValid() + returns TRUE) before values can be retrieved. + + Navigating records is performed with the following functions: + + \list + \i \c next() + \i \c prev() + \i \c first() + \i \c last() + \i \c \link TQSqlQuery::seek() seek\endlink(int) + \endlist + + These functions allow the programmer to move forward, backward or + arbitrarily through the records returned by the query. If you only + need to move forward through the results, e.g. using next() or + using seek() with a positive offset, you can use setForwardOnly() + and save a significant amount of memory overhead. Once an active + query is positioned on a valid record, data can be retrieved using + value(). All data is transferred from the SQL backend using + TQVariants. + + For example: + + \code + TQSqlQuery query( "SELECT name FROM customer" ); + while ( query.next() ) { + TQString name = query.value(0).toString(); + doSomething( name ); + } + \endcode + + To access the data returned by a query, use the value() method. + Each field in the data returned by a SELECT statement is accessed + by passing the field's position in the statement, starting from 0. + Information about the fields can be obtained via TQSqlDatabase::record(). + For the sake of efficiency there are no functions to access a field + by name. (The \l TQSqlCursor class provides a higher-level interface + with field access by name and automatic SQL generation.) + + TQSqlQuery supports prepared query execution and the binding of + parameter values to placeholders. Some databases don't support + these features, so for them TQt emulates the retquired + functionality. For example, the Oracle and ODBC drivers have + proper prepared query support, and TQt makes use of it; but for + databases that don't have this support, TQt implements the feature + itself, e.g. by replacing placeholders with actual values when a + query is executed. The exception is positional binding using named + placeholders, which retquires that the database supports prepared + queries. + + Oracle databases identify placeholders by using a colon-name + syntax, e.g \c{:name}. ODBC simply uses \c ? characters. TQt + supports both syntaxes (although you can't mix them in the same + query). + + Below we present the same example using each of the four different + binding approaches. + + <b>Named binding using named placeholders</b> + \code + TQSqlQuery query; + query.prepare( "INSERT INTO atable (id, forename, surname) " + "VALUES (:id, :forename, :surname)" ); + query.bindValue( ":id", 1001 ); + query.bindValue( ":forename", "Bart" ); + query.bindValue( ":surname", "Simpson" ); + query.exec(); + \endcode + + <b>Positional binding using named placeholders</b> + \code + TQSqlQuery query; + query.prepare( "INSERT INTO atable (id, forename, surname) " + "VALUES (:id, :forename, :surname)" ); + query.bindValue( 0, 1001 ); + query.bindValue( 1, "Bart" ); + query.bindValue( 2, "Simpson" ); + query.exec(); + \endcode + <b>Note:</b> Using positional binding with named placeholders will + only work if the database supports prepared queries. This can be + checked with TQSqlDriver::hasFeature() using TQSqlDriver::PreparedQueries + as argument for driver feature. + + <b>Binding values using positional placeholders #1</b> + \code + TQSqlQuery query; + query.prepare( "INSERT INTO atable (id, forename, surname) " + "VALUES (?, ?, ?)" ); + query.bindValue( 0, 1001 ); + query.bindValue( 1, "Bart" ); + query.bindValue( 2, "Simpson" ); + query.exec(); + \endcode + + <b>Binding values using positional placeholders #2</b> + \code + query.prepare( "INSERT INTO atable (id, forename, surname) " + "VALUES (?, ?, ?)" ); + query.addBindValue( 1001 ); + query.addBindValue( "Bart" ); + query.addBindValue( "Simpson" ); + query.exec(); + \endcode + + <b>Binding values to a stored procedure</b> + This code calls a stored procedure called \c AsciiToInt(), passing + it a character through its in parameter, and taking its result in + the out parameter. + \code + TQSqlQuery query; + query.prepare( "call AsciiToInt(?, ?)" ); + query.bindValue( 0, "A" ); + query.bindValue( 1, 0, TQSql::Out ); + query.exec(); + int i = query.boundValue( 1 ).toInt(); // i is 65. + \endcode + + \sa TQSqlDatabase TQSqlCursor TQVariant +*/ + +/*! + Creates a TQSqlQuery object which uses the TQSqlResult \a r to + communicate with a database. +*/ + +TQSqlQuery::TQSqlQuery( TQSqlResult * r ) +{ + d = new TQSqlResultShared( r ); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlQuery::~TQSqlQuery() +{ + if (d->deref()) { + delete d; + } +} + +/*! + Constructs a copy of \a other. +*/ + +TQSqlQuery::TQSqlQuery( const TQSqlQuery& other ) + : d(other.d) +{ + d->ref(); +} + +/*! + Creates a TQSqlQuery object using the SQL \a query and the database + \a db. If \a db is 0, (the default), the application's default + database is used. If \a query is not a null string, it will be + executed. + + \sa TQSqlDatabase +*/ +TQSqlQuery::TQSqlQuery( const TQString& query, TQSqlDatabase* db ) +{ + init( query, db ); +} + +/*! + Creates a TQSqlQuery object using the database \a db. If \a db is + 0, the application's default database is used. + + \sa TQSqlDatabase +*/ + +TQSqlQuery::TQSqlQuery( TQSqlDatabase* db ) +{ + init( TQString::null, db ); +} + +/*! \internal +*/ + +void TQSqlQuery::init( const TQString& query, TQSqlDatabase* db ) +{ + d = new TQSqlResultShared( 0 ); + TQSqlDatabase* database = db; + if ( !database ) + database = TQSqlDatabase::database( TQSqlDatabase::defaultConnection, FALSE ); + if ( database ) + *this = database->driver()->createQuery(); + if ( !query.isNull() ) + exec( query ); +} + +/*! + Assigns \a other to the query. +*/ + +TQSqlQuery& TQSqlQuery::operator=( const TQSqlQuery& other ) +{ + other.d->ref(); + deref(); + d = other.d; + return *this; +} + +/*! + Returns TRUE if the query is active and positioned on a valid + record and the \a field is NULL; otherwise returns FALSE. Note + that for some drivers isNull() will not return accurate + information until after an attempt is made to retrieve data. + + \sa isActive() isValid() value() +*/ + +bool TQSqlQuery::isNull( int field ) const +{ + if ( !d->sqlResult ) + return FALSE; + if ( d->sqlResult->isActive() && d->sqlResult->isValid() ) + return d->sqlResult->isNull( field ); + return FALSE; +} + +/*! + Executes the SQL in \a query. Returns TRUE and sets the query + state to active if the query was successful; otherwise returns + FALSE and sets the query state to inactive. The \a query string + must use syntax appropriate for the SQL database being queried, + for example, standard SQL. + + After the query is executed, the query is positioned on an \e + invalid record, and must be navigated to a valid record before + data values can be retrieved, e.g. using next(). + + Note that the last error for this query is reset when exec() is + called. + + \sa isActive() isValid() next() prev() first() last() seek() +*/ + +bool TQSqlQuery::exec ( const TQString& query ) +{ + if ( !d->sqlResult ) + return FALSE; + if ( d->sqlResult->extension() && driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) + d->sqlResult->extension()->clear(); + d->sqlResult->setActive( FALSE ); + d->sqlResult->setLastError( TQSqlError() ); + d->sqlResult->setAt( TQSql::BeforeFirst ); + if ( !driver() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::exec: no driver" ); +#endif + return FALSE; + } + if ( d->count > 1 ) + *this = driver()->createQuery(); + d->sqlResult->setQuery( query.stripWhiteSpace() ); + d->executedQuery = d->sqlResult->lastQuery(); + if ( !driver()->isOpen() || driver()->isOpenError() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::exec: database not open" ); +#endif + return FALSE; + } + if ( query.isNull() || query.length() == 0 ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::exec: empty query" ); +#endif + return FALSE; + } +#ifdef QT_DEBUG_SQL + qDebug( "\n TQSqlQuery: " + query ); +#endif + return d->sqlResult->reset( query ); +} + +/*! + Returns the value of the \a{i}-th field in the query (zero based). + + The fields are numbered from left to right using the text of the + \c SELECT statement, e.g. in \c{SELECT forename, surname FROM people}, + field 0 is \c forename and field 1 is \c surname. Using \c{SELECT *} + is not recommended because the order of the fields in the query is + undefined. + + An invalid TQVariant is returned if field \a i does not exist, if + the query is inactive, or if the query is positioned on an invalid + record. + + \sa prev() next() first() last() seek() isActive() isValid() +*/ + +TQVariant TQSqlQuery::value( int i ) const +{ + if ( !d->sqlResult ) + return TQVariant(); + if ( isActive() && isValid() && ( i > TQSql::BeforeFirst ) ) { + return d->sqlResult->data( i ); + } else { +#ifdef QT_CHECK_RANGE + qWarning( "TQSqlQuery::value: not positioned on a valid record" ); +#endif + } + return TQVariant(); +} + +/*! + Returns the current internal position of the query. The first + record is at position zero. If the position is invalid, a + TQSql::Location will be returned indicating the invalid position. + + \sa prev() next() first() last() seek() isActive() isValid() +*/ + +int TQSqlQuery::at() const +{ + if ( !d->sqlResult ) + return TQSql::BeforeFirst; + return d->sqlResult->at(); +} + +/*! + Returns the text of the current query being used, or TQString::null + if there is no current query text. + + \sa executedQuery() +*/ + +TQString TQSqlQuery::lastQuery() const +{ + if ( !d->sqlResult ) + return TQString::null; + return d->sqlResult->lastQuery(); +} + +/*! + Returns the database driver associated with the query. +*/ + +const TQSqlDriver* TQSqlQuery::driver() const +{ + if ( !d->sqlResult ) + return 0; + return d->sqlResult->driver(); +} + +/*! + Returns the result associated with the query. +*/ + +const TQSqlResult* TQSqlQuery::result() const +{ + return d->sqlResult; +} + +/*! + Retrieves the record at position (offset) \a i, if available, and + positions the query on the retrieved record. The first record is + at position 0. Note that the query must be in an active state and + isSelect() must return TRUE before calling this function. + + If \a relative is FALSE (the default), the following rules apply: + + \list + \i If \a i is negative, the result is positioned before the + first record and FALSE is returned. + \i Otherwise, an attempt is made to move to the record at position + \a i. If the record at position \a i could not be retrieved, the + result is positioned after the last record and FALSE is returned. If + the record is successfully retrieved, TRUE is returned. + \endlist + + If \a relative is TRUE, the following rules apply: + + \list + \i If the result is currently positioned before the first + record or on the first record, and \a i is negative, there is no + change, and FALSE is returned. + \i If the result is currently located after the last record, and + \a i is positive, there is no change, and FALSE is returned. + \i If the result is currently located somewhere in the middle, + and the relative offset \a i moves the result below zero, the + result is positioned before the first record and FALSE is + returned. + \i Otherwise, an attempt is made to move to the record \a i + records ahead of the current record (or \a i records behind the + current record if \a i is negative). If the record at offset \a i + could not be retrieved, the result is positioned after the last + record if \a i >= 0, (or before the first record if \a i is + negative), and FALSE is returned. If the record is successfully + retrieved, TRUE is returned. + \endlist + + \sa next() prev() first() last() at() isActive() isValid() +*/ +bool TQSqlQuery::seek( int i, bool relative ) +{ + if ( !isSelect() || !isActive() ) + return FALSE; + beforeSeek(); + checkDetach(); + int actualIdx; + if ( !relative ) { // arbitrary seek + if ( i < 0 ) { + d->sqlResult->setAt( TQSql::BeforeFirst ); + afterSeek(); + return FALSE; + } + actualIdx = i; + } else { + switch ( at() ) { // relative seek + case TQSql::BeforeFirst: + if ( i > 0 ) + actualIdx = i; + else { + afterSeek(); + return FALSE; + } + break; + case TQSql::AfterLast: + if ( i < 0 ) { + d->sqlResult->fetchLast(); + actualIdx = at() + i; + } else { + afterSeek(); + return FALSE; + } + break; + default: + if ( ( at() + i ) < 0 ) { + d->sqlResult->setAt( TQSql::BeforeFirst ); + afterSeek(); + return FALSE; + } + actualIdx = at() + i; + break; + } + } + // let drivers optimize + if ( isForwardOnly() && actualIdx < at() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::seek: cannot seek backwards in a forward only query" ); +#endif + afterSeek(); + return FALSE; + } + if ( actualIdx == ( at() + 1 ) && at() != TQSql::BeforeFirst ) { + if ( !d->sqlResult->fetchNext() ) { + d->sqlResult->setAt( TQSql::AfterLast ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; + } + if ( actualIdx == ( at() - 1 ) ) { + if ( !d->sqlResult->fetchPrev() ) { + d->sqlResult->setAt( TQSql::BeforeFirst ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; + } + if ( !d->sqlResult->fetch( actualIdx ) ) { + d->sqlResult->setAt( TQSql::AfterLast ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; +} + +/*! + Retrieves the next record in the result, if available, and + positions the query on the retrieved record. Note that the result + must be in an active state and isSelect() must return TRUE before + calling this function or it will do nothing and return FALSE. + + The following rules apply: + + \list + \i If the result is currently located before the first + record, e.g. immediately after a query is executed, an attempt is + made to retrieve the first record. + + \i If the result is currently located after the last record, + there is no change and FALSE is returned. + + \i If the result is located somewhere in the middle, an attempt + is made to retrieve the next record. + \endlist + + If the record could not be retrieved, the result is positioned after + the last record and FALSE is returned. If the record is successfully + retrieved, TRUE is returned. + + \sa prev() first() last() seek() at() isActive() isValid() +*/ + +bool TQSqlQuery::next() +{ + if ( !isSelect() || !isActive() ) + return FALSE; + beforeSeek(); + checkDetach(); + bool b = FALSE; + switch ( at() ) { + case TQSql::BeforeFirst: + b = d->sqlResult->fetchFirst(); + afterSeek(); + return b; + case TQSql::AfterLast: + afterSeek(); + return FALSE; + default: + if ( !d->sqlResult->fetchNext() ) { + d->sqlResult->setAt( TQSql::AfterLast ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; + } +} + +/*! + Retrieves the previous record in the result, if available, and + positions the query on the retrieved record. Note that the result + must be in an active state and isSelect() must return TRUE before + calling this function or it will do nothing and return FALSE. + + The following rules apply: + + \list + \i If the result is currently located before the first record, + there is no change and FALSE is returned. + + \i If the result is currently located after the last record, an + attempt is made to retrieve the last record. + + \i If the result is somewhere in the middle, an attempt is made + to retrieve the previous record. + \endlist + + If the record could not be retrieved, the result is positioned + before the first record and FALSE is returned. If the record is + successfully retrieved, TRUE is returned. + + \sa next() first() last() seek() at() isActive() isValid() +*/ + +bool TQSqlQuery::prev() +{ + if ( !isSelect() || !isActive() ) + return FALSE; + if ( isForwardOnly() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::seek: cannot seek backwards in a forward only query" ); +#endif + return FALSE; + } + + beforeSeek(); + checkDetach(); + bool b = FALSE; + switch ( at() ) { + case TQSql::BeforeFirst: + afterSeek(); + return FALSE; + case TQSql::AfterLast: + b = d->sqlResult->fetchLast(); + afterSeek(); + return b; + default: + if ( !d->sqlResult->fetchPrev() ) { + d->sqlResult->setAt( TQSql::BeforeFirst ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; + } +} + +/*! + Retrieves the first record in the result, if available, and + positions the query on the retrieved record. Note that the result + must be in an active state and isSelect() must return TRUE before + calling this function or it will do nothing and return FALSE. + Returns TRUE if successful. If unsuccessful the query position is + set to an invalid position and FALSE is returned. + + \sa next() prev() last() seek() at() isActive() isValid() +*/ + +bool TQSqlQuery::first() +{ + if ( !isSelect() || !isActive() ) + return FALSE; + if ( isForwardOnly() && at() > TQSql::BeforeFirst ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::seek: cannot seek backwards in a forward only query" ); +#endif + return FALSE; + } + beforeSeek(); + checkDetach(); + bool b = FALSE; + b = d->sqlResult->fetchFirst(); + afterSeek(); + return b; +} + +/*! + Retrieves the last record in the result, if available, and + positions the query on the retrieved record. Note that the result + must be in an active state and isSelect() must return TRUE before + calling this function or it will do nothing and return FALSE. + Returns TRUE if successful. If unsuccessful the query position is + set to an invalid position and FALSE is returned. + + \sa next() prev() first() seek() at() isActive() isValid() +*/ + +bool TQSqlQuery::last() +{ + if ( !isSelect() || !isActive() ) + return FALSE; + beforeSeek(); + checkDetach(); + bool b = FALSE; + b = d->sqlResult->fetchLast(); + afterSeek(); + return b; +} + +/*! + Returns the size of the result, (number of rows returned), or -1 + if the size cannot be determined or if the database does not + support reporting information about query sizes. Note that for + non-\c SELECT statements (isSelect() returns FALSE), size() will + return -1. If the query is not active (isActive() returns FALSE), + -1 is returned. + + To determine the number of rows affected by a non-SELECT + statement, use numRowsAffected(). + + \sa isActive() numRowsAffected() TQSqlDriver::hasFeature() +*/ +int TQSqlQuery::size() const +{ + if ( !d->sqlResult ) + return -1; + if ( isActive() && d->sqlResult->driver()->hasFeature( TQSqlDriver::QuerySize ) ) + return d->sqlResult->size(); + return -1; +} + +/*! + Returns the number of rows affected by the result's SQL statement, + or -1 if it cannot be determined. Note that for \c SELECT + statements, the value is undefined; see size() instead. If the + query is not active (isActive() returns FALSE), -1 is returned. + + \sa size() TQSqlDriver::hasFeature() +*/ + +int TQSqlQuery::numRowsAffected() const +{ + if ( !d->sqlResult ) + return -1; + if ( isActive() ) + return d->sqlResult->numRowsAffected(); + return -1; +} + +/*! + Returns error information about the last error (if any) that + occurred. + + \sa TQSqlError +*/ + +TQSqlError TQSqlQuery::lastError() const +{ + if ( !d->sqlResult ) + return TQSqlError(); + return d->sqlResult->lastError(); +} + +/*! + Returns TRUE if the query is currently positioned on a valid + record; otherwise returns FALSE. +*/ + +bool TQSqlQuery::isValid() const +{ + if ( !d->sqlResult ) + return FALSE; + return d->sqlResult->isValid(); +} + +/*! + Returns TRUE if the query is currently active; otherwise returns + FALSE. +*/ + +bool TQSqlQuery::isActive() const +{ + if ( !d->sqlResult ) + return FALSE; + return d->sqlResult->isActive(); +} + +/*! + Returns TRUE if the current query is a \c SELECT statement; + otherwise returns FALSE. +*/ + +bool TQSqlQuery::isSelect() const +{ + if ( !d->sqlResult ) + return FALSE; + return d->sqlResult->isSelect(); +} + +/*! + Returns TRUE if you can only scroll \e forward through a result + set; otherwise returns FALSE. + + \sa setForwardOnly() +*/ +bool TQSqlQuery::isForwardOnly() const +{ + if ( !d->sqlResult ) + return FALSE; + return d->sqlResult->isForwardOnly(); +} + +/*! + Sets forward only mode to \a forward. If forward is TRUE only + next(), and seek() with positive values, are allowed for + navigating the results. Forward only mode needs far less memory + since results do not need to be cached. + + Forward only mode is off by default. + + Forward only mode cannot be used with data aware widgets like + TQDataTable, since they must to be able to scroll backward as well + as forward. + + \sa isForwardOnly(), next(), seek() +*/ +void TQSqlQuery::setForwardOnly( bool forward ) +{ + if ( d->sqlResult ) + d->sqlResult->setForwardOnly( forward ); +} + +/*! + \internal +*/ + +void TQSqlQuery::deref() +{ + if ( d->deref() ) { + delete d; + d = 0; + } +} + +/*! + \internal +*/ + +bool TQSqlQuery::checkDetach() +{ + if ( d->count > 1 && d->sqlResult ) { + TQString sql = d->sqlResult->lastQuery(); + *this = driver()->createQuery(); + exec( sql ); + return TRUE; + } + return FALSE; +} + + +/*! + Protected virtual function called before the internal record + pointer is moved to a new record. The default implementation does + nothing. +*/ + +void TQSqlQuery::beforeSeek() +{ + +} + + +/*! + Protected virtual function called after the internal record + pointer is moved to a new record. The default implementation does + nothing. +*/ + +void TQSqlQuery::afterSeek() +{ + +} + +// XXX: Hack to keep BCI - remove in 4.0. TQSqlExtension should be +// removed, and the prepare(), exec() etc. fu's should be +// made virtual members of TQSqlQuery/TQSqlResult + +/*! + Prepares the SQL query \a query for execution. The query may + contain placeholders for binding values. Both Oracle style + colon-name (e.g. \c{:surname}), and ODBC style (e.g. \c{?}) + placeholders are supported; but they cannot be mixed in the same + query. See the \link #details Description\endlink for examples. + + \sa exec(), bindValue(), addBindValue() +*/ +bool TQSqlQuery::prepare( const TQString& query ) +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return FALSE; + d->sqlResult->setActive( FALSE ); + d->sqlResult->setLastError( TQSqlError() ); + d->sqlResult->setAt( TQSql::BeforeFirst ); + d->sqlResult->extension()->clear(); + if ( !driver() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::prepare: no driver" ); +#endif + return FALSE; + } + if ( d->count > 1 ) + *this = driver()->createQuery(); + d->sqlResult->setQuery( query.stripWhiteSpace() ); + if ( !driver()->isOpen() || driver()->isOpenError() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::prepare: database not open" ); +#endif + return FALSE; + } + if ( query.isNull() || query.length() == 0 ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::prepare: empty query" ); +#endif + return FALSE; + } +#ifdef QT_DEBUG_SQL + qDebug( "\n TQSqlQuery: " + query ); +#endif + TQString q = query; + TQRegExp rx(TQString::fromLatin1("'[^']*'|:([a-zA-Z0-9_]+)")); + if ( driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) { + // below we substitute Oracle placeholders with ODBC ones and + // vice versa to make this db independent + int i = 0, cnt = 0; + if ( driver()->hasFeature( TQSqlDriver::NamedPlaceholders ) ) { + TQRegExp rx(TQString::fromLatin1("'[^']*'|\\?")); + while ( (i = rx.search( q, i )) != -1 ) { + if ( rx.cap(0) == "?" ) { + q = q.replace( i, 1, ":f" + TQString::number(cnt) ); + cnt++; + } + i += rx.matchedLength(); + } + } else if ( driver()->hasFeature( TQSqlDriver::PositionalPlaceholders ) ) { + while ( (i = rx.search( q, i )) != -1 ) { + if ( rx.cap(1).isEmpty() ) { + i += rx.matchedLength(); + } else { + // record the index of the placeholder - needed + // for emulating named bindings with ODBC + d->sqlResult->extension()->index[ cnt ]= rx.cap(0); + q = q.replace( i, rx.matchedLength(), "?" ); + i++; + cnt++; + } + } + } + d->executedQuery = q; + return d->sqlResult->extension()->prepare( q ); + } else { + int i = 0; + while ( (i = rx.search( q, i )) != -1 ) { + if ( !rx.cap(1).isEmpty() ) + d->sqlResult->extension()->holders.append( Holder( rx.cap(0), i ) ); + i += rx.matchedLength(); + } + return TRUE; // fake prepares should always succeed + } +} + +/*! + \overload + + Executes a previously prepared SQL query. Returns TRUE if the + query executed successfully; otherwise returns FALSE. + + \sa prepare(), bindValue(), addBindValue() +*/ +bool TQSqlQuery::exec() +{ + bool ret; + if ( !d->sqlResult || !d->sqlResult->extension() ) + return FALSE; + if ( driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) { + ret = d->sqlResult->extension()->exec(); + } else { + // fake preparation - just replace the placeholders.. + TQString query = d->sqlResult->lastQuery(); + if ( d->sqlResult->extension()->bindMethod() == TQSqlExtension::BindByName ) { + int i; + TQVariant val; + TQString holder; + for ( i = (int)d->sqlResult->extension()->holders.count() - 1; i >= 0; --i ) { + holder = d->sqlResult->extension()->holders[ (uint)i ].holderName; + val = d->sqlResult->extension()->values[ holder ].value; + TQSqlField f( "", val.type() ); + if ( val.isNull() ) + f.setNull(); + else + f.setValue( val ); + query = query.replace( (uint)d->sqlResult->extension()->holders[ (uint)i ].holderPos, + holder.length(), driver()->formatValue( &f ) ); + } + } else { + TQMap<int, TQString>::ConstIterator it; + TQString val; + int i = 0; + for ( it = d->sqlResult->extension()->index.begin(); + it != d->sqlResult->extension()->index.end(); ++it ) { + i = query.find( '?', i ); + if ( i > -1 ) { + TQSqlField f( "", d->sqlResult->extension()->values[ it.data() ].value.type() ); + if ( d->sqlResult->extension()->values[ it.data() ].value.isNull() ) + f.setNull(); + else + f.setValue( d->sqlResult->extension()->values[ it.data() ].value ); + val = driver()->formatValue( &f ); + query = query.replace( i, 1, driver()->formatValue( &f ) ); + i += val.length(); + } + } + } + // have to retain the original query w/placeholders.. + TQString orig = d->sqlResult->lastQuery(); + ret = exec( query ); + d->executedQuery = query; + d->sqlResult->setQuery( orig ); + } + d->sqlResult->extension()->resetBindCount(); + return ret; +} + +/*! + Set the placeholder \a placeholder to be bound to value \a val in + the prepared statement. Note that the placeholder mark (e.g \c{:}) + must be included when specifying the placeholder name. If \a type + is \c TQSql::Out or \c TQSql::InOut, the placeholder will be + overwritten with data from the database after the exec() call. + + \sa addBindValue(), prepare(), exec() +*/ +void TQSqlQuery::bindValue( const TQString& placeholder, const TQVariant& val, TQSql::ParameterType type ) +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return; + d->sqlResult->extension()->bindValue( placeholder, val, type ); +} + +/*! + \overload + + Set the placeholder in position \a pos to be bound to value \a val + in the prepared statement. Field numbering starts at 0. If \a type + is \c TQSql::Out or \c TQSql::InOut, the placeholder will be + overwritten with data from the database after the exec() call. + + \sa addBindValue(), prepare(), exec() +*/ +void TQSqlQuery::bindValue( int pos, const TQVariant& val, TQSql::ParameterType type ) +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return; + d->sqlResult->extension()->bindValue( pos, val, type ); +} + +/*! + Adds the value \a val to the list of values when using positional + value binding. The order of the addBindValue() calls determines + which placeholder a value will be bound to in the prepared query. + If \a type is \c TQSql::Out or \c TQSql::InOut, the placeholder will + be overwritten with data from the database after the exec() call. + + \sa bindValue(), prepare(), exec() +*/ +void TQSqlQuery::addBindValue( const TQVariant& val, TQSql::ParameterType type ) +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return; + d->sqlResult->extension()->addBindValue( val, type ); +} + + +/*! + \overload + + Binds the placeholder with type \c TQSql::In. +*/ +void TQSqlQuery::bindValue( const TQString& placeholder, const TQVariant& val ) +{ + bindValue( placeholder, val, TQSql::In ); +} + +/*! + \overload + + Binds the placeholder at position \a pos with type \c TQSql::In. +*/ +void TQSqlQuery::bindValue( int pos, const TQVariant& val ) +{ + bindValue( pos, val, TQSql::In ); +} + +/*! + \overload + + Binds the placeholder with type \c TQSql::In. +*/ +void TQSqlQuery::addBindValue( const TQVariant& val ) +{ + addBindValue( val, TQSql::In ); +} + +/*! + Returns the value for the \a placeholder. +*/ +TQVariant TQSqlQuery::boundValue( const TQString& placeholder ) const +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return TQVariant(); + return d->sqlResult->extension()->boundValue( placeholder ); +} + +/*! + \overload + + Returns the value for the placeholder at position \a pos. +*/ +TQVariant TQSqlQuery::boundValue( int pos ) const +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return TQVariant(); + return d->sqlResult->extension()->boundValue( pos ); +} + +/*! + Returns a map of the bound values. + + The bound values can be examined in the following way: + \code + TQSqlQuery query; + ... + // Examine the bound values - bound using named binding + TQMap<TQString, TQVariant>::ConstIterator it; + TQMap<TQString, TQVariant> vals = query.boundValues(); + for ( it = vals.begin(); it != vals.end(); ++it ) + qWarning( "Placeholder: " + it.key() + ", Value: " + (*it).toString() ); + ... + + // Examine the bound values - bound using positional binding + TQValueList<TQVariant>::ConstIterator it; + TQValueList<TQVariant> list = query.boundValues().values(); + int i = 0; + for ( it = list.begin(); it != list.end(); ++it ) + qWarning( "Placeholder pos: %d, Value: " + (*it).toString(), i++ ); + ... + + \endcode +*/ +TQMap<TQString,TQVariant> TQSqlQuery::boundValues() const +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return TQMap<TQString,TQVariant>(); + return d->sqlResult->extension()->boundValues(); +} + +/*! + Returns the last query that was executed. + + In most cases this function returns the same as lastQuery(). If a + prepared query with placeholders is executed on a DBMS that does + not support it, the preparation of this query is emulated. The + placeholders in the original query are replaced with their bound + values to form a new query. This function returns the modified + query. Useful for debugging purposes. + + \sa lastQuery() +*/ +TQString TQSqlQuery::executedQuery() const +{ + return d->executedQuery; +} +#endif // QT_NO_SQL |