diff options
Diffstat (limited to 'src/sql/qsqlcursor.cpp')
-rw-r--r-- | src/sql/qsqlcursor.cpp | 1549 |
1 files changed, 1549 insertions, 0 deletions
diff --git a/src/sql/qsqlcursor.cpp b/src/sql/qsqlcursor.cpp new file mode 100644 index 000000000..5ec8b754a --- /dev/null +++ b/src/sql/qsqlcursor.cpp @@ -0,0 +1,1549 @@ +/**************************************************************************** +** +** Implementation of TQSqlCursor class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-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 "qsqlcursor.h" + +#ifndef QT_NO_SQL + +#include "qsqldriver.h" +#include "qsqlresult.h" +#include "qdatetime.h" +#include "qsqldatabase.h" +#include "qsql.h" + +class TQSqlCursorPrivate +{ +public: + + TQSqlCursorPrivate( const TQString& name, TQSqlDatabase* sdb ) + : lastAt( TQSql::BeforeFirst ), nm( name ), srt( name ), md( 0 ), db( sdb ), q( 0 ) + {} + ~TQSqlCursorPrivate() + { + delete q; + } + + TQSqlQuery* query() + { + if ( !q ) + q = new TQSqlQuery( 0, db ); + return q; + } + + int lastAt; + TQString nm; //name + TQSqlIndex srt; //sort + TQString ftr; //filter + int md; //mode + TQSqlIndex priIndx; //primary index + TQSqlRecord editBuffer; + // the primary index as it was before the user changed the values in editBuffer + TQString editIndex; + TQSqlRecordInfo infoBuffer; + TQSqlDatabase* db; + TQSqlQuery* q; +}; + +TQString qOrderByClause( const TQSqlIndex & i, const TQString& prefix = TQString::null ) +{ + TQString str; + int k = i.count(); + if( k == 0 ) return TQString::null; + str = " order by " + i.toString( prefix ); + return str; +} + +TQString qWhereClause( const TQString& prefix, TQSqlField* field, const TQSqlDriver* driver ) +{ + TQString f; + if ( field && driver ) { + f = ( prefix.length() > 0 ? prefix + TQString(".") : TQString::null ) + field->name(); + if ( field->isNull() ) { + f += " IS NULL"; + } else { + f += " = " + driver->formatValue( field ); + } + } + return f; +} + +TQString qWhereClause( TQSqlRecord* rec, const TQString& prefix, const TQString& sep, + const TQSqlDriver* driver ) +{ + static TQString blank( " " ); + TQString filter; + bool separator = FALSE; + for ( uint j = 0; j < rec->count(); ++j ) { + TQSqlField* f = rec->field( j ); + if ( rec->isGenerated( j ) ) { + if ( separator ) + filter += sep + blank; + filter += qWhereClause( prefix, f, driver ); + filter += blank; + separator = TRUE; + } + } + return filter; +} + +/*! + \class TQSqlCursor qsqlcursor.h + \brief The TQSqlCursor class provides browsing and editing of SQL + tables and views. + + \ingroup database + \module sql + + A TQSqlCursor is a database record (see \l TQSqlRecord) that + corresponds to a table or view within an SQL database (see \l + TQSqlDatabase). There are two buffers in a cursor, one used for + browsing and one used for editing records. Each buffer contains a + list of fields which correspond to the fields in the table or + view. + + When positioned on a valid record, the browse buffer contains the + values of the current record's fields from the database. The edit + buffer is separate, and is used for editing existing records and + inserting new records. + + For browsing data, a cursor must first select() data from the + database. After a successful select() the cursor is active + (isActive() returns TRUE), but is initially not positioned on a + valid record (isValid() returns FALSE). To position the cursor on + a valid record, use one of the navigation functions, next(), + prev(), first(), last(), or seek(). Once positioned on a valid + record, data can be retrieved from the browse buffer using + value(). If a navigation function is not successful, it returns + FALSE, the cursor will no longer be positioned on a valid record + and the values returned by value() are undefined. + + For example: + + \quotefile sql/overview/retrieve2/main.cpp + \skipto TQSqlCursor + \printline TQSqlCursor + \printuntil } + + In the above example, a cursor is created specifying a table or + view name in the database. Then, select() is called, which can be + optionally parameterised to filter and order the records + retrieved. Each record in the cursor is retrieved using next(). + When next() returns FALSE, there are no more records to process, + and the loop terminates. + + For editing records (rows of data), a cursor contains a separate + edit buffer which is independent of the fields used when browsing. + The functions insert(), update() and del() operate on the edit + buffer. This allows the cursor to be repositioned to other + records while simultaneously maintaining a separate buffer for + edits. You can get a pointer to the edit buffer using + editBuffer(). The primeInsert(), primeUpdate() and primeDelete() + functions also return a pointer to the edit buffer and prepare it + for insert, update and delete respectively. Edit operations only + affect a single row at a time. Note that update() and del() + retquire that the table or view contain a primaryIndex() to ensure + that edit operations affect a unique record within the database. + + For example: + + \quotefile sql/overview/update/main.cpp + \skipto prices + \printline prices + \printuntil update + \printline + + To edit an existing database record, first move to the record you + wish to update. Call primeUpdate() to get the pointer to the + cursor's edit buffer. Then use this pointer to modify the values + in the edit buffer. Finally, call update() to save the changes to + the database. The values in the edit buffer will be used to + locate the appropriate record when updating the database (see + primaryIndex()). + + Similarly, when deleting an existing database record, first move + to the record you wish to delete. Then, call primeDelete() to get + the pointer to the edit buffer. Finally, call del() to delete the + record from the database. Again, the values in the edit buffer + will be used to locate and delete the appropriate record. + + To insert a new record, call primeInsert() to get the pointer to + the edit buffer. Use this pointer to populate the edit buffer + with new values and then insert() the record into the database. + + After calling insert(), update() or del(), the cursor is no longer + positioned on a valid record and can no longer be navigated + (isValid() return FALSE). The reason for this is that any changes + made to the database will not be visible until select() is called + to refresh the cursor. You can change this behavior by passing + FALSE to insert(), update() or del() which will prevent the cursor + from becoming invalid. The edits will still not be visible when + navigating the cursor until select() is called. + + TQSqlCursor contains virtual methods which allow editing behavior + to be customized by subclasses. This allows custom cursors to be + created that encapsulate the editing behavior of a database table + for an entire application. For example, a cursor can be customized + to always auto-number primary index fields, or provide fields with + suitable default values, when inserting new records. TQSqlCursor + generates SQL statements which are sent to the database engine; + you can control which fields are included in these statements + using setGenerated(). + + Note that TQSqlCursor does not inherit from TQObject. This means + that you are responsible for destroying instances of this class + yourself. However if you create a TQSqlCursor and use it in a + \l TQDataTable, \l TQDataBrowser or a \l TQDataView these classes will + usually take ownership of the cursor and destroy it when they + don't need it anymore. The documentation for TQDataTable, + TQDataBrowser and TQDataView explicitly states which calls take + ownership of the cursor. +*/ + +/*! + \enum TQSqlCursor::Mode + + This enum type describes how TQSqlCursor operates on records in the + database. + + \value ReadOnly the cursor can only SELECT records from the + database. + + \value Insert the cursor can INSERT records into the database. + + \value Update the cursor can UPDATE records in the database. + + \value Delete the cursor can DELETE records from the database. + + \value Writable the cursor can INSERT, UPDATE and DELETE records + in the database. +*/ + +/*! + Constructs a cursor on database \a db using table or view \a name. + + If \a autopopulate is TRUE (the default), the \a name of the + cursor must correspond to an existing table or view name in the + database so that field information can be automatically created. + If the table or view does not exist, the cursor will not be + functional. + + The cursor is created with an initial mode of TQSqlCursor::Writable + (meaning that records can be inserted, updated or deleted using + the cursor). If the cursor does not have a unique primary index, + update and deletes cannot be performed. + + Note that \a autopopulate refers to populating the cursor with + meta-data, e.g. the names of the table's fields, not with + retrieving data. The select() function is used to populate the + cursor with data. + + \sa setName() setMode() +*/ + +TQSqlCursor::TQSqlCursor( const TQString & name, bool autopopulate, TQSqlDatabase* db ) + : TQSqlRecord(), TQSqlQuery( TQString::null, db ) +{ + d = new TQSqlCursorPrivate( name, db ); + setMode( Writable ); + if ( !d->nm.isNull() ) + setName( d->nm, autopopulate ); +} + +/*! + Constructs a copy of \a other. +*/ + +TQSqlCursor::TQSqlCursor( const TQSqlCursor & other ) + : TQSqlRecord( other ), TQSqlQuery( other ) +{ + d = new TQSqlCursorPrivate( other.d->nm, other.d->db ); + d->lastAt = other.d->lastAt; + d->nm = other.d->nm; + d->srt = other.d->srt; + d->ftr = other.d->ftr; + d->priIndx = other.d->priIndx; + d->editBuffer = other.d->editBuffer; + d->infoBuffer = other.d->infoBuffer; + d->q = 0; // do not share queries + setMode( other.mode() ); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlCursor::~TQSqlCursor() +{ + delete d; +} + +/*! + Sets the cursor equal to \a other. +*/ + +TQSqlCursor& TQSqlCursor::operator=( const TQSqlCursor& other ) +{ + TQSqlRecord::operator=( other ); + TQSqlQuery::operator=( other ); + delete d; + d = new TQSqlCursorPrivate( other.d->nm, other.d->db ); + d->lastAt = other.d->lastAt; + d->nm = other.d->nm; + d->srt = other.d->srt; + d->ftr = other.d->ftr; + d->priIndx = other.d->priIndx; + d->editBuffer = other.d->editBuffer; + d->infoBuffer = other.d->infoBuffer; + d->q = 0; // do not share queries + setMode( other.mode() ); + return *this; +} + +/*! + Sets the current sort to \a sort. Note that no new records are + selected. To select new records, use select(). The \a sort will + apply to any subsequent select() calls that do not explicitly + specify a sort. +*/ + +void TQSqlCursor::setSort( const TQSqlIndex& sort ) +{ + d->srt = sort; +} + +/*! + Returns the current sort, or an empty index if there is no current + sort. +*/ +TQSqlIndex TQSqlCursor::sort() const +{ + return d->srt; +} + +/*! + Sets the current filter to \a filter. Note that no new records are + selected. To select new records, use select(). The \a filter will + apply to any subsequent select() calls that do not explicitly + specify a filter. + + The filter is a SQL \c WHERE clause without the keyword 'WHERE', + e.g. \c{name='Dave'} which will be processed by the DBMS. +*/ +void TQSqlCursor::setFilter( const TQString& filter ) +{ + d->ftr = filter; +} + +/*! + Returns the current filter, or an empty string if there is no + current filter. +*/ +TQString TQSqlCursor::filter() const +{ + return d->ftr; +} + +/*! + Sets the name of the cursor to \a name. If \a autopopulate is TRUE + (the default), the \a name must correspond to a valid table or + view name in the database. Also, note that all references to the + cursor edit buffer become invalidated when fields are + auto-populated. See the TQSqlCursor constructor documentation for + more information. +*/ +void TQSqlCursor::setName( const TQString& name, bool autopopulate ) +{ + d->nm = name; + if ( autopopulate ) { + if ( driver() ) { + d->infoBuffer = driver()->recordInfo( name ); + *this = d->infoBuffer.toRecord(); + d->editBuffer = *this; + d->priIndx = driver()->primaryIndex( name ); + } +#ifdef QT_CHECK_RANGE + if ( isEmpty() ) + qWarning("TQSqlCursor::setName: unable to build record, does '%s' exist?", name.latin1() ); +#endif + } +} + +/*! + Returns the name of the cursor. +*/ + +TQString TQSqlCursor::name() const +{ + return d->nm; +} + +/*! \reimp +*/ + +TQString TQSqlCursor::toString( const TQString& prefix, const TQString& sep ) const +{ + TQString pflist; + TQString pfix = prefix.isEmpty() ? TQString::null : prefix + "."; + bool comma = FALSE; + + for ( uint i = 0; i < count(); ++i ) { + const TQString fname = fieldName( i ); + if ( isGenerated( i ) ) { + if( comma ) + pflist += sep + " "; + pflist += pfix + fname; + comma = TRUE; + } + } + return pflist; +} + +/*! + \internal + + Assigns the record \a list. + +*/ +TQSqlRecord & TQSqlCursor::operator=( const TQSqlRecord & list ) +{ + return TQSqlRecord::operator=( list ); +} + +/*! + Append a copy of field \a fieldInfo to the end of the cursor. Note + that all references to the cursor edit buffer become invalidated. +*/ + +void TQSqlCursor::append( const TQSqlFieldInfo& fieldInfo ) +{ + d->editBuffer.append( fieldInfo.toField() ); + d->editBuffer.setGenerated( d->editBuffer.count() - 1, fieldInfo.isGenerated() ); + d->infoBuffer.append( fieldInfo ); + TQSqlRecord::append( fieldInfo.toField() ); + TQSqlRecord::setGenerated( TQSqlRecord::count() - 1, fieldInfo.isGenerated() ); +} + +/*! + Removes all fields from the cursor. Note that all references to + the cursor edit buffer become invalidated. +*/ +void TQSqlCursor::clear() +{ + d->editBuffer.clear(); + d->infoBuffer.clear(); + TQSqlRecord::clear(); +} + + +/*! + Insert a copy of \a fieldInfo at position \a pos. If a field + already exists at \a pos, it is removed. Note that all references + to the cursor edit buffer become invalidated. +*/ + +void TQSqlCursor::insert( int pos, const TQSqlFieldInfo& fieldInfo ) +{ + d->editBuffer.insert( pos, fieldInfo.toField() ); + d->editBuffer.setGenerated( pos, fieldInfo.isGenerated() ); + d->infoBuffer[ pos ] = fieldInfo; + TQSqlRecord::insert( pos, fieldInfo.toField() ); + TQSqlRecord::setGenerated( pos, fieldInfo.isGenerated() ); +} + +/*! + Removes the field at \a pos. If \a pos does not exist, nothing + happens. Note that all references to the cursor edit buffer become + invalidated. +*/ + +void TQSqlCursor::remove( int pos ) +{ + d->editBuffer.remove( pos ); + d->infoBuffer[ pos ] = TQSqlFieldInfo(); + TQSqlRecord::remove( pos ); +} + +/*! + Sets the generated flag for the field \a name to \a generated. If + the field does not exist, nothing happens. Only fields that have + \a generated set to TRUE are included in the SQL that is + generated by insert(), update() or del(). + + \sa isGenerated() +*/ + +void TQSqlCursor::setGenerated( const TQString& name, bool generated ) +{ + int pos = position( name ); + if ( pos == -1 ) + return; + TQSqlRecord::setGenerated( name, generated ); + d->editBuffer.setGenerated( name, generated ); + d->infoBuffer[ pos ].setGenerated( generated ); +} + +/*! + \overload + + Sets the generated flag for the field \a i to \a generated. + + \sa isGenerated() +*/ +void TQSqlCursor::setGenerated( int i, bool generated ) +{ + if ( i < 0 || i >= (int)d->infoBuffer.count() ) + return; + TQSqlRecord::setGenerated( i, generated ); + d->editBuffer.setGenerated( i, generated ); + d->infoBuffer[i].setGenerated( generated ); +} + +/*! + Returns the primary index associated with the cursor as defined in + the database, or an empty index if there is no primary index. If + \a setFromCursor is TRUE (the default), the index fields are + populated with the corresponding values in the cursor's current + record. +*/ + +TQSqlIndex TQSqlCursor::primaryIndex( bool setFromCursor ) const +{ + if ( setFromCursor ) { + for ( uint i = 0; i < d->priIndx.count(); ++i ) { + const TQString fn = d->priIndx.fieldName( i ); + if ( contains( fn ) ) + d->priIndx.setValue( i, value( fn ) ); + } + } + return d->priIndx; +} + +/*! + Sets the primary index associated with the cursor to the index \a + idx. Note that this index must contain a field or set of fields + which identify a unique record within the underlying database + table or view so that update() and del() will execute as expected. + + \sa update() del() +*/ + +void TQSqlCursor::setPrimaryIndex( const TQSqlIndex& idx ) +{ + d->priIndx = idx; +} + + +/*! + Returns an index composed of \a fieldNames, all in ASCending + order. Note that all field names must exist in the cursor, + otherwise an empty index is returned. + + \sa TQSqlIndex +*/ + +TQSqlIndex TQSqlCursor::index( const TQStringList& fieldNames ) const +{ + TQSqlIndex idx; + for ( TQStringList::ConstIterator it = fieldNames.begin(); it != fieldNames.end(); ++it ) { + const TQSqlField* f = field( (*it) ); + if ( !f ) { /* all fields must exist */ + idx.clear(); + break; + } + idx.append( *f ); + } + return idx; +} + +/*! + \overload + + Returns an index based on \a fieldName. +*/ + +TQSqlIndex TQSqlCursor::index( const TQString& fieldName ) const +{ + TQStringList fl( fieldName ); + return index( fl ); +} + +/*! + \overload + + Returns an index based on \a fieldName. +*/ + +TQSqlIndex TQSqlCursor::index( const char* fieldName ) const +{ + return index( TQStringList( TQString( fieldName ) ) ); +} + +/*! + Selects all fields in the cursor from the database matching the + filter criteria \a filter. The data is returned in the order + specified by the index \a sort. Returns TRUE if the data was + successfully selected; otherwise returns FALSE. + + The \a filter is a string containing a SQL \c WHERE clause but + without the 'WHERE' keyword. The cursor is initially positioned at + an invalid row after this function is called. To move to a valid + row, use seek(), first(), last(), prev() or next(). + + Example: + \code + TQSqlCursor cur( "Employee" ); // Use the Employee table or view + cur.select( "deptno=10" ); // select all records in department 10 + while( cur.next() ) { + ... // process data + } + ... + // select records in other departments, ordered by department number + cur.select( "deptno>10", cur.index( "deptno" ) ); + ... + \endcode + + The filter will apply to any subsequent select() calls that do not + explicitly specify another filter. Similarly the sort will apply + to any subsequent select() calls that do not explicitly specify + another sort. + + \code + TQSqlCursor cur( "Employee" ); + cur.select( "deptno=10" ); // select all records in department 10 + while( cur.next() ) { + ... // process data + } + ... + cur.select(); // re-selects all records in department 10 + ... + \endcode + +*/ + +bool TQSqlCursor::select( const TQString & filter, const TQSqlIndex & sort ) +{ + TQString fieldList = toString( d->nm ); + if ( fieldList.isEmpty() ) + return FALSE; + TQString str= "select " + fieldList; + str += " from " + d->nm; + if ( !filter.isEmpty() ) { + d->ftr = filter; + str += " where " + filter; + } else + d->ftr = TQString::null; + if ( sort.count() > 0 ) + str += " order by " + sort.toString( d->nm ); + d->srt = sort; + return exec( str ); +} + +/*! + \overload + + Selects all fields in the cursor from the database. The rows are + returned in the order specified by the last call to setSort() or + the last call to select() that specified a sort, whichever is the + most recent. If there is no current sort, the order in which the + rows are returned is undefined. The records are filtered according + to the filter specified by the last call to setFilter() or the + last call to select() that specified a filter, whichever is the + most recent. If there is no current filter, all records are + returned. The cursor is initially positioned at an invalid row. To + move to a valid row, use seek(), first(), last(), prev() or + next(). + + \sa setSort() setFilter() +*/ + +bool TQSqlCursor::select() +{ + return select( filter(), sort() ); +} + +/*! + \overload + + Selects all fields in the cursor from the database. The data is + returned in the order specified by the index \a sort. The records + are filtered according to the filter specified by the last call to + setFilter() or the last call to select() that specified a filter, + whichever is the most recent. The cursor is initially positioned + at an invalid row. To move to a valid row, use seek(), first(), + last(), prev() or next(). +*/ + +bool TQSqlCursor::select( const TQSqlIndex& sort ) +{ + return select( filter(), sort ); +} + +/*! + \overload + + Selects all fields in the cursor matching the filter index \a + filter. The data is returned in the order specified by the index + \a sort. The \a filter index works by constructing a WHERE clause + using the names of the fields from the \a filter and their values + from the current cursor record. The cursor is initially positioned + at an invalid row. To move to a valid row, use seek(), first(), + last(), prev() or next(). This function is useful, for example, + for retrieving data based upon a table's primary index: + + \code + TQSqlCursor cur( "Employee" ); + TQSqlIndex pk = cur.primaryIndex(); + cur.setValue( "id", 10 ); + cur.select( pk, pk ); // generates "SELECT ... FROM Employee WHERE id=10 ORDER BY id" + ... + \endcode + + In this example the TQSqlIndex, pk, is used for two different + purposes. When used as the filter (first) argument, the field + names it contains are used to construct the WHERE clause, each set + to the current cursor value, \c{WHERE id=10}, in this case. When + used as the sort (second) argument the field names it contains are + used for the ORDER BY clause, \c{ORDER BY id} in this example. +*/ + +bool TQSqlCursor::select( const TQSqlIndex & filter, const TQSqlIndex & sort ) +{ + return select( toString( filter, this, d->nm, "=", "and" ), sort ); +} + +/*! + Sets the cursor mode to \a mode. This value can be an OR'ed + combination of \l TQSqlCursor::Mode values. The default mode for a + cursor is \c TQSqlCursor::Writable. + + \code + TQSqlCursor cur( "Employee" ); + cur.setMode( TQSqlCursor::Writable ); // allow insert/update/delete + ... + cur.setMode( TQSqlCursor::Insert | TQSqlCursor::Update ); // allow inserts and updates only + ... + cur.setMode( TQSqlCursor::ReadOnly ); // no inserts/updates/deletes allowed + + \endcode +*/ + +void TQSqlCursor::setMode( int mode ) +{ + d->md = mode; +} + +/*! + Returns the current cursor mode. + + \sa setMode() +*/ + +int TQSqlCursor::mode() const +{ + return d->md; +} + +/*! + Sets field \a name to \a calculated. If the field \a name does not + exist, nothing happens. The value of a calculated field is set by + the calculateField() virtual function which you must reimplement + (or the field value will be an invalid TQVariant). Calculated + fields do not appear in generated SQL statements sent to the + database. + + \sa calculateField() TQSqlRecord::setGenerated() +*/ + +void TQSqlCursor::setCalculated( const TQString& name, bool calculated ) +{ + int pos = position( name ); + if ( pos < 0 ) + return; + d->infoBuffer[ pos ].setCalculated( calculated ); + if ( calculated ) + setGenerated( pos, FALSE ); +} + +/*! + Returns TRUE if the field \a name exists and is calculated; + otherwise returns FALSE. + + \sa setCalculated() +*/ + +bool TQSqlCursor::isCalculated( const TQString& name ) const +{ + int pos = position( name ); + if ( pos < 0 ) + return FALSE; + return d->infoBuffer[ pos ].isCalculated(); +} + +/*! + Sets field \a{name}'s trimmed status to \a trim. If the field \a + name does not exist, nothing happens. + + When a trimmed field of type string or cstring is read from the + database any trailing (right-most) spaces are removed. + + \sa isTrimmed() TQVariant +*/ + +void TQSqlCursor::setTrimmed( const TQString& name, bool trim ) +{ + int pos = position( name ); + if ( pos < 0 ) + return; + d->infoBuffer[ pos ].setTrim( trim ); +} + +/*! + Returns TRUE if the field \a name exists and is trimmed; otherwise + returns FALSE. + + When a trimmed field of type string or cstring is read from the + database any trailing (right-most) spaces are removed. + + \sa setTrimmed() +*/ + +bool TQSqlCursor::isTrimmed( const TQString& name ) const +{ + int pos = position( name ); + if ( pos < 0 ) + return FALSE; + return d->infoBuffer[ pos ].isTrim(); +} + +/*! + Returns TRUE if the cursor is read-only; otherwise returns FALSE. + The default is FALSE. Read-only cursors cannot be edited using + insert(), update() or del(). + + \sa setMode() +*/ + +bool TQSqlCursor::isReadOnly() const +{ + return d->md == 0; +} + +/*! + Returns TRUE if the cursor will perform inserts; otherwise returns + FALSE. + + \sa setMode() +*/ + +bool TQSqlCursor::canInsert() const +{ + return ( ( d->md & Insert ) == Insert ) ; +} + + +/*! + Returns TRUE if the cursor will perform updates; otherwise returns + FALSE. + + \sa setMode() +*/ + +bool TQSqlCursor::canUpdate() const +{ + return ( ( d->md & Update ) == Update ) ; +} + +/*! + Returns TRUE if the cursor will perform deletes; otherwise returns + FALSE. + + \sa setMode() +*/ + +bool TQSqlCursor::canDelete() const +{ + return ( ( d->md & Delete ) == Delete ) ; +} + +/*! + \overload + + Returns a formatted string composed of the \a prefix (e.g. table + or view name), ".", the \a field name, the \a fieldSep and the + field value. If the \a prefix is empty then the string will begin + with the \a field name. This function is useful for generating SQL + statements. +*/ + +TQString TQSqlCursor::toString( const TQString& prefix, TQSqlField* field, const TQString& fieldSep ) const +{ + TQString f; + if ( field && driver() ) { + f = ( prefix.length() > 0 ? prefix + TQString(".") : TQString::null ) + field->name(); + f += " " + fieldSep + " "; + if ( field->isNull() ) { + f += "NULL"; + } else { + f += driver()->formatValue( field ); + } + } + return f; +} + +/*! + Returns a formatted string composed of all the fields in \a rec. + Each field is composed of the \a prefix (e.g. table or view name), + ".", the field name, the \a fieldSep and the field value. If the + \a prefix is empty then each field will begin with the field name. + The fields are then joined together separated by \a sep. Fields + where isGenerated() returns FALSE are not included. This function + is useful for generating SQL statements. +*/ + +TQString TQSqlCursor::toString( TQSqlRecord* rec, const TQString& prefix, const TQString& fieldSep, + const TQString& sep ) const +{ + static TQString blank( " " ); + TQString filter; + bool separator = FALSE; + for ( uint j = 0; j < count(); ++j ) { + TQSqlField* f = rec->field( j ); + if ( rec->isGenerated( j ) ) { + if ( separator ) + filter += sep + blank; + filter += toString( prefix, f, fieldSep ); + filter += blank; + separator = TRUE; + } + } + return filter; +} + +/*! + \overload + + Returns a formatted string composed of all the fields in the index + \a i. Each field is composed of the \a prefix (e.g. table or view + name), ".", the field name, the \a fieldSep and the field value. + If the \a prefix is empty then each field will begin with the field + name. The field values are taken from \a rec. The fields are then + joined together separated by \a sep. Fields where isGenerated() + returns FALSE are ignored. This function is useful for generating + SQL statements. +*/ + +TQString TQSqlCursor::toString( const TQSqlIndex& i, TQSqlRecord* rec, const TQString& prefix, + const TQString& fieldSep, const TQString& sep ) const +{ + TQString filter; + bool separator = FALSE; + for( uint j = 0; j < i.count(); ++j ){ + if ( rec->isGenerated( j ) ) { + if( separator ) { + filter += " " + sep + " " ; + } + TQString fn = i.fieldName( j ); + TQSqlField* f = rec->field( fn ); + filter += toString( prefix, f, fieldSep ); + separator = TRUE; + } + } + return filter; +} + +/*! + \overload + + Inserts the current contents of the cursor's edit record buffer + into the database, if the cursor allows inserts. Returns the + number of rows affected by the insert. For error information, use + lastError(). + + If \a invalidate is TRUE (the default), the cursor will no longer + be positioned on a valid record and can no longer be navigated. A + new select() call must be made before navigating to a valid + record. + + \quotefile sql/overview/insert2/main.cpp + \skipto prices + \printline prices + \printuntil insert + + In the above example, a cursor is created on the 'prices' table + and a pointer to the insert buffer is atquired using primeInsert(). + Each field's value is set to the desired value and then insert() + is called to insert the data into the database. Remember: all edit + operations (insert(), update() and delete()) operate on the + contents of the cursor edit buffer and not on the contents of the + cursor itself. + + \sa setMode() lastError() +*/ + +int TQSqlCursor::insert( bool invalidate ) +{ + if ( ( d->md & Insert ) != Insert || !driver() ) + return FALSE; + int k = d->editBuffer.count(); + if ( k == 0 ) + return 0; + + TQString fList; + TQString vList; + bool comma = FALSE; + // use a prepared query if the driver supports it + if ( driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) { + int cnt = 0; + bool oraStyle = driver()->hasFeature( TQSqlDriver::NamedPlaceholders ); + for( int j = 0; j < k; ++j ) { + TQSqlField* f = d->editBuffer.field( j ); + if ( d->editBuffer.isGenerated( j ) ) { + if ( comma ) { + fList += ","; + vList += ","; + } + fList += f->name(); + vList += (oraStyle == TRUE) ? ":f" + TQString::number(cnt) : TQString("?"); + cnt++; + comma = TRUE; + } + } + if ( !comma ) { + return 0; + } + TQString str; + str.append( "insert into " ).append( name() ).append( "(" ).append( fList ).append( ") values (" ).append( vList ). append ( ")" ); + return applyPrepared( str, invalidate ); + } else { + for( int j = 0; j < k; ++j ) { + TQSqlField* f = d->editBuffer.field( j ); + if ( d->editBuffer.isGenerated( j ) ) { + if ( comma ) { + fList += ","; + vList += ","; + } + fList += f->name(); + vList += driver()->formatValue( f ); + comma = TRUE; + } + } + + if ( !comma ) { + // no valid fields found + return 0; + } + TQString str; + str.append( "insert into " ).append( name() ).append( "(" ).append( fList ).append( ") values (" ).append( vList ). append ( ")" ); + return apply( str, invalidate ); + } +} + +/*! + Returns the current internal edit buffer. If \a copy is TRUE (the + default is FALSE), the current cursor field values are first + copied into the edit buffer. The edit buffer is valid as long as + the cursor remains valid. The cursor retains ownership of the + returned pointer, so it must not be deleted or modified. + + \sa primeInsert(), primeUpdate() primeDelete() +*/ + +TQSqlRecord* TQSqlCursor::editBuffer( bool copy ) +{ + if ( copy ) { + for(uint i = 0; i < d->editBuffer.count(); i++) { + if ( TQSqlRecord::isNull( i ) ) { + d->editBuffer.setNull( i ); + } else { + d->editBuffer.setValue( i, value( i ) ); + } + } + } + return &d->editBuffer; +} + +/*! + This function primes the edit buffer's field values for update and + returns the edit buffer. The default implementation copies the + field values from the current cursor record into the edit buffer + (therefore, this function is equivalent to calling editBuffer( + TRUE ) ). The cursor retains ownership of the returned pointer, so + it must not be deleted or modified. + + \sa editBuffer() update() +*/ + +TQSqlRecord* TQSqlCursor::primeUpdate() +{ + // memorize the primary keys as they were before the user changed the values in editBuffer + TQSqlRecord* buf = editBuffer( TRUE ); + TQSqlIndex idx = primaryIndex( FALSE ); + if ( !idx.isEmpty() ) + d->editIndex = toString( idx, buf, d->nm, "=", "and" ); + else + d->editIndex = qWhereClause( buf, d->nm, "and", driver() ); + return buf; +} + +/*! + This function primes the edit buffer's field values for delete and + returns the edit buffer. The default implementation copies the + field values from the current cursor record into the edit buffer + (therefore, this function is equivalent to calling editBuffer( + TRUE ) ). The cursor retains ownership of the returned pointer, so + it must not be deleted or modified. + + \sa editBuffer() del() +*/ + +TQSqlRecord* TQSqlCursor::primeDelete() +{ + return editBuffer( TRUE ); +} + +/*! + This function primes the edit buffer's field values for insert and + returns the edit buffer. The default implementation clears all + field values in the edit buffer. The cursor retains ownership of + the returned pointer, so it must not be deleted or modified. + + \sa editBuffer() insert() +*/ + +TQSqlRecord* TQSqlCursor::primeInsert() +{ + d->editBuffer.clearValues(); + return &d->editBuffer; +} + + +/*! + Updates the database with the current contents of the edit buffer. + Returns the number of records which were updated. + For error information, use lastError(). + + Only records which meet the filter criteria specified by the + cursor's primary index are updated. If the cursor does not contain + a primary index, no update is performed and 0 is returned. + + If \a invalidate is TRUE (the default), the current cursor can no + longer be navigated. A new select() call must be made before you + can move to a valid record. For example: + + \quotefile sql/overview/update/main.cpp + \skipto prices + \printline prices + \printuntil update + \printline + + In the above example, a cursor is created on the 'prices' table + and is positioned on the record to be updated. Then a pointer to + the cursor's edit buffer is actquired using primeUpdate(). A new + value is calculated and placed into the edit buffer with the + setValue() call. Finally, an update() call is made on the cursor + which uses the tables's primary index to update the record in the + database with the contents of the cursor's edit buffer. Remember: + all edit operations (insert(), update() and delete()) operate on + the contents of the cursor edit buffer and not on the contents of + the cursor itself. + + Note that if the primary index does not uniquely distinguish + records the database may be changed into an inconsistent state. + + \sa setMode() lastError() +*/ + +int TQSqlCursor::update( bool invalidate ) +{ + if ( d->editIndex.isEmpty() ) + return 0; + return update( d->editIndex, invalidate ); +} + +/*! + \overload + + Updates the database with the current contents of the cursor edit + buffer using the specified \a filter. Returns the number of + records which were updated. + For error information, use lastError(). + + Only records which meet the filter criteria are updated, otherwise + all records in the table are updated. + + If \a invalidate is TRUE (the default), the cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. + + \sa primeUpdate() setMode() lastError() +*/ + +int TQSqlCursor::update( const TQString & filter, bool invalidate ) +{ + if ( ( d->md & Update ) != Update ) { + return FALSE; + } + int k = count(); + if ( k == 0 ) { + return 0; + } + + // use a prepared query if the driver supports it + if ( driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) { + TQString fList; + bool comma = FALSE; + int cnt = 0; + bool oraStyle = driver()->hasFeature( TQSqlDriver::NamedPlaceholders ); + for( int j = 0; j < k; ++j ) { + TQSqlField* f = d->editBuffer.field( j ); + if ( d->editBuffer.isGenerated( j ) ) { + if ( comma ) { + fList += ","; + } + fList += f->name() + " = " + (oraStyle == TRUE ? ":f" + TQString::number(cnt) : TQString("?")); + cnt++; + comma = TRUE; + } + } + if ( !comma ) { + return 0; + } + TQString str = "update " + name() + " set " + fList; + if ( filter.length() ) { + str+= " where " + filter; + } + return applyPrepared( str, invalidate ); + } else { + TQString str = "update " + name(); + str += " set " + toString( &d->editBuffer, TQString::null, "=", "," ); + if ( filter.length() ) { + str+= " where " + filter; + } + return apply( str, invalidate ); + } +} + +/*! + Deletes a record from the database using the cursor's primary + index and the contents of the cursor edit buffer. Returns the + number of records which were deleted. + For error information, use lastError(). + + Only records which meet the filter criteria specified by the + cursor's primary index are deleted. If the cursor does not contain + a primary index, no delete is performed and 0 is returned. If \a + invalidate is TRUE (the default), the current cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. For example: + + \quotefile sql/overview/delete/main.cpp + \skipto prices + \printline prices + \printuntil } + + In the above example, a cursor is created on the 'prices' table + and positioned to the record to be deleted. First primeDelete() is + called to populate the edit buffer with the current cursor values, + e.g. with an id of 999, and then del() is called to actually + delete the record from the database. Remember: all edit operations + (insert(), update() and delete()) operate on the contents of the + cursor edit buffer and not on the contents of the cursor itself. + + \sa primeDelete() setMode() lastError() +*/ + +int TQSqlCursor::del( bool invalidate ) +{ + TQSqlIndex idx = primaryIndex( FALSE ); + if ( idx.isEmpty() ) + return del( qWhereClause( &d->editBuffer, d->nm, "and", driver() ), invalidate ); + else + return del( toString( primaryIndex(), &d->editBuffer, d->nm, + "=", "and" ), invalidate ); +} + +/*! + \overload + + Deletes the current cursor record from the database using the + filter \a filter. Only records which meet the filter criteria are + deleted. Returns the number of records which were deleted. If \a + invalidate is TRUE (the default), the current cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. For error information, use lastError(). + + The \a filter is an SQL \c WHERE clause, e.g. \c{id=500}. + + \sa setMode() lastError() +*/ + +int TQSqlCursor::del( const TQString & filter, bool invalidate ) +{ + if ( ( d->md & Delete ) != Delete ) + return 0; + int k = count(); + if( k == 0 ) return 0; + TQString str = "delete from " + name(); + if ( filter.length() ) + str+= " where " + filter; + return apply( str, invalidate ); +} + +/* + \internal +*/ + +int TQSqlCursor::apply( const TQString& q, bool invalidate ) +{ + int ar = 0; + if ( invalidate ) { + if ( exec( q ) ) + ar = numRowsAffected(); + } else if ( driver() ) { + TQSqlQuery* sql = d->query(); + if ( sql && sql->exec( q ) ) + ar = sql->numRowsAffected(); + } + return ar; +} + +/* + \internal +*/ + +int TQSqlCursor::applyPrepared( const TQString& q, bool invalidate ) +{ + int ar = 0; + TQSqlQuery* sql = 0; + + if ( invalidate ) { + sql = (TQSqlQuery*)this; + d->lastAt = TQSql::BeforeFirst; + } else { + sql = d->query(); + } + if ( !sql ) + return 0; + + if ( invalidate || sql->lastQuery() != q ) { + if ( !sql->prepare( q ) ) + return 0; + } + + int cnt = 0; + int fieldCount = (int)count(); + for ( int j = 0; j < fieldCount; ++j ) { + const TQSqlField* f = d->editBuffer.field( j ); + if ( d->editBuffer.isGenerated( j ) ) { + sql->bindValue( cnt, f->value() ); + cnt++; + } + } + if ( sql->exec() ) { + ar = sql->numRowsAffected(); + } + return ar; +} + +/*! \reimp + + Executes the SQL query \a sql. Returns TRUE of the cursor is + active, otherwise returns FALSE. + +*/ +bool TQSqlCursor::exec( const TQString & sql ) +{ + d->lastAt = TQSql::BeforeFirst; + TQSqlQuery::exec( sql ); + return isActive(); +} + +/*! + Protected virtual function which is called whenever a field needs + to be calculated. If calculated fields are being used, derived + classes must reimplement this function and return the appropriate + value for field \a name. The default implementation returns an + invalid TQVariant. + + \sa setCalculated() +*/ + +TQVariant TQSqlCursor::calculateField( const TQString& ) +{ + return TQVariant(); +} + +/*! \internal + Ensure fieldlist is synced with query. + +*/ + +static TQString qTrim( const TQString& s ) +{ + TQString result = s; + int end = result.length() - 1; + while ( end >= 0 && result[end].isSpace() ) // skip white space from end + end--; + result.truncate( end + 1 ); + return result; +} + +/*! \internal + */ + +void TQSqlCursor::sync() +{ + if ( isActive() && isValid() && d->lastAt != at() ) { + d->lastAt = at(); + uint i = 0; + uint j = 0; + bool haveCalculatedFields = FALSE; + for ( ; i < count(); ++i ) { + if ( !haveCalculatedFields && d->infoBuffer[i].isCalculated() ) { + haveCalculatedFields = TRUE; + } + if ( TQSqlRecord::isGenerated( i ) ) { + TQVariant v = TQSqlQuery::value( j ); + if ( ( v.type() == TQVariant::String || v.type() == TQVariant::CString ) && + d->infoBuffer[ i ].isTrim() ) { + v = qTrim( v.toString() ); + } + TQSqlRecord::setValue( i, v ); + if ( TQSqlQuery::isNull( j ) ) + TQSqlRecord::field( i )->setNull(); + j++; + } + } + if ( haveCalculatedFields ) { + for ( i = 0; i < count(); ++i ) { + if ( d->infoBuffer[i].isCalculated() ) + TQSqlRecord::setValue( i, calculateField( fieldName( i ) ) ); + } + } + } +} + +/*! \reimp + +*/ + +void TQSqlCursor::afterSeek() +{ + sync(); +} + +/*! + \reimp + + Returns the value of field number \a i. +*/ + +TQVariant TQSqlCursor::value( int i ) const +{ + return TQSqlRecord::value( i ); +} + +/*! + \reimp + + Returns the value of the field called \a name. +*/ + +TQVariant TQSqlCursor::value( const TQString& name ) const +{ + return TQSqlRecord::value( name ); +} + +/*! \internal + cursors should be filled with TQSqlFieldInfos... +*/ +void TQSqlCursor::append( const TQSqlField& field ) +{ + append( TQSqlFieldInfo( field ) ); +} +/*! \internal + cursors should be filled with TQSqlFieldInfos... +*/ +void TQSqlCursor::insert( int pos, const TQSqlField& field ) +{ + insert( pos, TQSqlFieldInfo( field ) ); +} + +/*! + Returns TRUE if the field \a i is NULL or if there is no field at + position \a i; otherwise returns FALSE. + + This is the same as calling TQSqlRecord::isNull( \a i ) +*/ +bool TQSqlCursor::isNull( int i ) const +{ + return TQSqlRecord::isNull( i ); +} +/*! + \overload + + Returns TRUE if the field called \a name is NULL or if there is no + field called \a name; otherwise returns FALSE. + + This is the same as calling TQSqlRecord::isNull( \a name ) +*/ +bool TQSqlCursor::isNull( const TQString& name ) const +{ + return TQSqlRecord::isNull( name ); +} + +/*! \reimp */ +void TQSqlCursor::setValue( int i, const TQVariant& val ) +{ +#ifdef QT_DEBUG + qDebug("TQSqlCursor::setValue(): This will not affect actual database values. Use primeInsert(), primeUpdate() or primeDelete()."); +#endif + TQSqlRecord::setValue( i, val ); +} + +/*! \reimp */ +void TQSqlCursor::setValue( const TQString& name, const TQVariant& val ) +{ +#ifdef QT_DEBUG + qDebug("TQSqlCursor::setValue(): This will not affect actual database values. Use primeInsert(), primeUpdate() or primeDelete()."); +#endif + TQSqlRecord::setValue( name, val ); +} +#endif |