kexi

cursor.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This program is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this program; see the file COPYING.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <kexidb/cursor.h>
00021 
00022 #include <kexidb/driver.h>
00023 #include <kexidb/driver_p.h>
00024 #include <kexidb/error.h>
00025 #include <kexidb/roweditbuffer.h>
00026 #include <kexiutils/utils.h>
00027 
00028 #include <kdebug.h>
00029 #include <klocale.h>
00030 
00031 #include <assert.h>
00032 #include <stdlib.h>
00033 
00034 using namespace KexiDB;
00035 
00036 #ifdef KEXI_DEBUG_GUI
00037 
00038 #endif
00039 
00040 Cursor::Cursor(Connection* conn, const QString& statement, uint options)
00041     : QObject()
00042     , m_conn(conn)
00043     , m_query(0)
00044     , m_rawStatement(statement)
00045     , m_options(options)
00046 {
00047 #ifdef KEXI_DEBUG_GUI
00048     KexiUtils::addKexiDBDebug(QString("Create cursor: ")+statement);
00049 #endif
00050     init();
00051 }
00052 
00053 Cursor::Cursor(Connection* conn, QuerySchema& query, uint options )
00054     : QObject()
00055     , m_conn(conn)
00056     , m_query(&query)
00057     , m_options(options)
00058 {
00059 #ifdef KEXI_DEBUG_GUI
00060     KexiUtils::addKexiDBDebug(QString("Create cursor for query \"%1\": ").arg(query.name())+query.debugString());
00061 #endif
00062     init();
00063 }
00064 
00065 void Cursor::init()
00066 {
00067     assert(m_conn);
00068     m_conn->m_cursors.insert(this,this);
00069     m_opened = false;
00070 //  , m_atFirst(false)
00071 //  , m_atLast(false)
00072 //  , m_beforeFirst(false)
00073     m_atLast = false;
00074     m_afterLast = false;
00075     m_readAhead = false;
00076     m_at = 0;
00077 //js:todo:  if (m_query)
00078 //      m_fieldCount = m_query->fieldsCount();
00079 //  m_fieldCount = m_query ? m_query->fieldCount() : 0; //do not know
00080     //<members related to buffering>
00081 //  m_cols_pointers_mem_size = 0;
00082     m_records_in_buf = 0;
00083     m_buffering_completed = false;
00084     m_at_buffer = false;
00085     m_result = -1;
00086 
00087     m_containsROWIDInfo = (m_query && m_query->masterTable()) 
00088         && m_conn->driver()->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE == false;
00089 
00090     if (m_query) {
00091         //get list of all fields
00092         m_fieldsExpanded = new QueryColumnInfo::Vector();
00093         *m_fieldsExpanded = m_query->fieldsExpanded(
00094             m_containsROWIDInfo ? QuerySchema::WithInternalFieldsAndRowID : QuerySchema::WithInternalFields);
00095         m_logicalFieldCount = m_fieldsExpanded->count() 
00096             - m_query->internalFields().count() - (m_containsROWIDInfo?1:0);
00097         m_fieldCount = m_fieldsExpanded->count();
00098     } else {
00099         m_fieldsExpanded = 0;
00100         m_logicalFieldCount = 0;
00101         m_fieldCount = 0;
00102     }
00103     m_orderByColumnList = 0;
00104     m_queryParameters = 0;
00105 }
00106 
00107 Cursor::~Cursor()
00108 {
00109 #ifdef KEXI_DEBUG_GUI
00110     if (m_query)
00111         KexiUtils::addKexiDBDebug(QString("~ Delete cursor for query"));
00112     else
00113         KexiUtils::addKexiDBDebug(QString("~ Delete cursor: ")+m_rawStatement);
00114 #endif
00115 /*  if (!m_query)
00116         KexiDBDbg << "Cursor::~Cursor() '" << m_rawStatement.latin1() << "'" << endl;
00117     else
00118         KexiDBDbg << "Cursor::~Cursor() " << endl;*/
00119 
00120     //take me if delete was 
00121     if (!m_conn->m_destructor_started)
00122         m_conn->m_cursors.take(this);
00123     else {
00124         KexiDBDbg << "Cursor::~Cursor() can be destroyed with Conenction::deleteCursor(), not with delete operator !"<< endl;
00125         exit(1);
00126     }
00127     delete m_fieldsExpanded;
00128     delete m_queryParameters;
00129 }
00130 
00131 bool Cursor::open()
00132 {
00133     if (m_opened) {
00134         if (!close())
00135             return false;
00136     }
00137     if (!m_rawStatement.isEmpty())
00138         m_conn->m_sql = m_rawStatement;
00139     else {
00140         if (!m_query) {
00141             KexiDBDbg << "Cursor::open(): no query statement (or schema) defined!" << endl;
00142             setError(ERR_SQL_EXECUTION_ERROR, i18n("No query statement or schema defined."));
00143             return false;
00144         }
00145         Connection::SelectStatementOptions options;
00146         options.alsoRetrieveROWID = m_containsROWIDInfo; /*get ROWID if needed*/
00147         m_conn->m_sql = m_queryParameters 
00148             ? m_conn->selectStatement( *m_query, *m_queryParameters, options )
00149             : m_conn->selectStatement( *m_query, options );
00150         if (m_conn->m_sql.isEmpty()) {
00151             KexiDBDbg << "Cursor::open(): empty statement!" << endl;
00152             setError(ERR_SQL_EXECUTION_ERROR, i18n("Query statement is empty."));
00153             return false;
00154         }
00155     }
00156     m_sql = m_conn->m_sql;
00157     m_opened = drv_open();
00158 //  m_beforeFirst = true;
00159     m_afterLast = false; //we are not @ the end
00160     m_at = 0; //we are before 1st rec
00161     if (!m_opened) {
00162         setError(ERR_SQL_EXECUTION_ERROR, i18n("Error opening database cursor."));
00163         return false;
00164     }
00165     m_validRecord = false;
00166 
00167 //luci: WHAT_EXACTLY_SHOULD_THAT_BE?
00168 //  if (!m_readAhead) // jowenn: to ensure before first state, without cluttering implementation code
00169     if (m_conn->driver()->beh->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY) {
00170 //      KexiDBDbg << "READ AHEAD:" << endl;
00171         m_readAhead = getNextRecord(); //true if any record in this query
00172 //      KexiDBDbg << "READ AHEAD = " << m_readAhead << endl;
00173     }
00174     m_at = 0; //we are still before 1st rec
00175     return !error();
00176 }
00177 
00178 bool Cursor::close()
00179 {
00180     if (!m_opened)
00181         return true;
00182     bool ret = drv_close();
00183 
00184     clearBuffer();
00185 
00186     m_opened = false;
00187 //  m_beforeFirst = false;
00188     m_afterLast = false;
00189     m_readAhead = false;
00190     m_fieldCount = 0;
00191     m_logicalFieldCount = 0;
00192     m_at = -1;
00193 
00194 //  KexiDBDbg<<"Cursor::close() == "<<ret<<endl;
00195     return ret;
00196 }
00197 
00198 bool Cursor::reopen()
00199 {
00200     if (!m_opened)
00201         return open();
00202     return close() && open();
00203 }
00204 
00205 bool Cursor::moveFirst()
00206 {
00207     if (!m_opened)
00208         return false;
00209 //  if (!m_beforeFirst) { //cursor isn't @ first record now: reopen
00210     if (!m_readAhead) {
00211         if (m_options & Buffered) {
00212             if (m_records_in_buf==0 && m_buffering_completed) {
00213                 //eof and bof should now return true:
00214                 m_afterLast = true;
00215                 m_at = 0;
00216                 return false; //buffering completed and there is no records!
00217             }
00218             if (m_records_in_buf>0) {
00219                 //set state as we would be before first rec:
00220                 m_at_buffer = false;
00221                 m_at = 0;
00222                 //..and move to next, ie. 1st record
00223 //              m_afterLast = m_afterLast = !getNextRecord();
00224                 m_afterLast = !getNextRecord();
00225                 return !m_afterLast;
00226             }
00227         }
00228         if (m_afterLast && m_at==0) //failure if already no records
00229             return false;
00230         if (!reopen()) //try reopen
00231             return false;
00232         if (m_afterLast) //eof
00233             return false;
00234     }
00235     else {
00236         //we have a record already read-ahead: we now point @ that:
00237         m_at = 1;
00238     }
00239 //  if (!m_atFirst) { //cursor isn't @ first record now: reopen
00240 //      reopen();
00241 //  }
00242 //  if (m_validRecord) {
00243 //      return true; //there is already valid record retrieved
00244 //  }
00245     //get first record
00246 //  if (drv_moveFirst() && drv_getRecord()) {
00247 //      m_beforeFirst = false;
00248         m_afterLast = false;
00249         m_readAhead = false; //1st record had been read
00250 //  }
00251     return m_validRecord;
00252 }
00253 
00254 bool Cursor::moveLast()
00255 {
00256     if (!m_opened)
00257         return false;
00258     if (m_afterLast || m_atLast) {
00259         return m_validRecord; //we already have valid last record retrieved
00260     }
00261     if (!getNextRecord()) { //at least next record must be retrieved
00262 //      m_beforeFirst = false;
00263         m_afterLast = true;
00264         m_validRecord = false;
00265         m_atLast = false;
00266         return false; //no records
00267     }
00268     while (getNextRecord()) //move after last rec.
00269         ;
00270 //  m_beforeFirst = false;
00271     m_afterLast = false;
00272     //cursor shows last record data
00273     m_atLast = true; 
00274 //  m_validRecord = true;
00275 
00276 /*
00277     //we are before or @ last record:
00278 //  if (m_atLast && m_validRecord) //we're already @ last rec.
00279 //      return true;
00280     if (m_validRecord) {
00281         if (drv_getRecord())
00282     }
00283     if (!m_validRecord) {
00284         if (drv_getRecord() && m_validRecord)
00285             return true;
00286         reopen();
00287     }
00288     */
00289     return true;
00290 }
00291 
00292 bool Cursor::moveNext()
00293 {
00294     if (!m_opened || m_afterLast)
00295         return false;
00296     if (getNextRecord()) {
00297 //      m_validRecord = true;
00298         return true;
00299     }
00300     return false;
00301 }
00302 
00303 bool Cursor::movePrev()
00304 {
00305     if (!m_opened /*|| m_beforeFirst*/ || !(m_options & Buffered))
00306         return false;
00307 
00308     //we're after last record and there are records in the buffer
00309     //--let's move to last record
00310     if (m_afterLast && (m_records_in_buf>0)) {
00311         drv_bufferMovePointerTo(m_records_in_buf-1);
00312         m_at=m_records_in_buf;
00313         m_at_buffer = true; //now current record is stored in the buffer
00314         m_validRecord=true;
00315         m_afterLast=false;
00316         return true;
00317     }
00318     //we're at first record: go BOF
00319     if ((m_at <= 1) || (m_records_in_buf <= 1/*sanity*/)) {
00320         m_at=0;
00321         m_at_buffer = false;
00322         m_validRecord=false;
00323         return false;
00324     }
00325 
00326     m_at--;
00327     if (m_at_buffer) {//we already have got a pointer to buffer
00328         drv_bufferMovePointerPrev(); //just move to prev record in the buffer
00329     } else {//we have no pointer
00330         //compute a place in the buffer that contain next record's data
00331         drv_bufferMovePointerTo(m_at-1);
00332         m_at_buffer = true; //now current record is stored in the buffer
00333     }
00334     m_validRecord=true;
00335     m_afterLast=false;
00336     return true;
00337 }
00338 
00339 bool Cursor::eof() const
00340 {
00341     return m_afterLast;
00342 }
00343 
00344 bool Cursor::bof() const
00345 {
00346     return m_at==0;
00347 }
00348 
00349 Q_LLONG Cursor::at() const
00350 {
00351     if (m_readAhead)
00352         return 0;
00353     return m_at - 1;
00354 }
00355 
00356 bool Cursor::isBuffered() const
00357 {
00358     return m_options & Buffered;
00359 }
00360 
00361 void Cursor::setBuffered(bool buffered)
00362 {
00363     if (!m_opened)
00364         return;
00365     if (isBuffered()==buffered)
00366         return;
00367     m_options ^= Buffered;
00368 }
00369 
00370 void Cursor::clearBuffer()
00371 {
00372     if ( !isBuffered() || m_fieldCount==0)
00373         return;
00374     
00375     drv_clearBuffer();
00376     
00377     m_records_in_buf=0;
00378     m_at_buffer=false;
00379 }
00380 
00381 bool Cursor::getNextRecord()
00382 {
00383     m_result = -1; //by default: invalid result of row fetching
00384 
00385     if ((m_options & Buffered)) {//this cursor is buffered:
00386 //      KexiDBDbg << "m_at < m_records_in_buf :: " << (long)m_at << " < " << m_records_in_buf << endl;
00387 //js        if (m_at==-1) m_at=0;
00388         if (m_at < m_records_in_buf) {//we have next record already buffered:
00390 //js            if (m_at_buffer && (m_at!=0)) {//we already have got a pointer to buffer
00391             if (m_at_buffer) {//we already have got a pointer to buffer
00392                 drv_bufferMovePointerNext(); //just move to next record in the buffer
00393             } else {//we have no pointer
00394                 //compute a place in the buffer that contain next record's data
00395                 drv_bufferMovePointerTo(m_at-1+1);
00396 //              drv_bufferMovePointerTo(m_at+1);
00397                 m_at_buffer = true; //now current record is stored in the buffer
00398             }
00399         }
00400         else {//we are after last retrieved record: we need to physically fetch next record:
00401             if (!m_readAhead) {//we have no record that was read ahead
00402                 if (!m_buffering_completed) {
00403                     //retrieve record only if we are not after 
00404                     //the last buffer's item (i.e. when buffer is not fully filled):
00405 //                  KexiDBDbg<<"==== buffering: drv_getNextRecord() ===="<<endl;
00406                     drv_getNextRecord();
00407                 }
00408                 if ((FetchResult) m_result != FetchOK) {//there is no record
00409                     m_buffering_completed = true; //no more records for buffer
00410 //                  KexiDBDbg<<"m_result != FetchOK ********"<<endl;
00411                     m_validRecord = false;
00412                     m_afterLast = true;
00413 //js                    m_at = m_records_in_buf;
00414                     m_at = -1; //position is invalid now and will not be used
00415                     if ((FetchResult) m_result == FetchEnd) {
00416                         return false;
00417                     }
00418                     setError(ERR_CURSOR_RECORD_FETCHING, i18n("Cannot fetch next record."));
00419                     return false;
00420                 }
00421                 //we have a record: store this record's values in the buffer
00422                 drv_appendCurrentRecordToBuffer();
00423                 m_records_in_buf++;
00424             }
00425             else //we have a record that was read ahead: eat this
00426                 m_readAhead = false;
00427         }
00428     }
00429     else {//we are after last retrieved record: we need to physically fetch next record:
00430         if (!m_readAhead) {//we have no record that was read ahead
00431 //          KexiDBDbg<<"==== no prefetched record ===="<<endl;
00432             drv_getNextRecord();
00433             if ((FetchResult)m_result != FetchOK) {//there is no record
00434 //              KexiDBDbg<<"m_result != FetchOK ********"<<endl;
00435                 m_validRecord = false;
00436                 m_afterLast = true;
00437                 m_at = -1;
00438                 if ((FetchResult) m_result == FetchEnd) {
00439                     return false;
00440                 }
00441                 setError(ERR_CURSOR_RECORD_FETCHING, i18n("Cannot fetch next record."));
00442                 return false;
00443             }
00444         }
00445         else //we have a record that was read ahead: eat this
00446             m_readAhead = false;
00447     }
00448 
00449     m_at++;
00450     
00451 //  if (m_data->curr_colname && m_data->curr_coldata)
00452 //      for (int i=0;i<m_data->curr_cols;i++) {
00453 //          KexiDBDbg<<i<<": "<< m_data->curr_colname[i]<<" == "<< m_data->curr_coldata[i]<<endl;
00454 //      }
00455 //  KexiDBDbg<<"m_at == "<<(long)m_at<<endl;
00456 
00457     m_validRecord = true;
00458     return true;
00459 }
00460 
00461 bool Cursor::updateRow(RowData& data, RowEditBuffer& buf, bool useROWID)
00462 {
00464     clearError();
00465     if (!m_query)
00466         return false;
00467     return m_conn->updateRow(*m_query, data, buf, useROWID);
00468 }
00469 
00470 bool Cursor::insertRow(RowData& data, RowEditBuffer& buf, bool getROWID)
00471 {
00473     clearError();
00474     if (!m_query)
00475         return false;
00476     return m_conn->insertRow(*m_query, data, buf, getROWID);
00477 }
00478 
00479 bool Cursor::deleteRow(RowData& data, bool useROWID)
00480 {
00482     clearError();
00483     if (!m_query)
00484         return false;
00485     return m_conn->deleteRow(*m_query, data, useROWID);
00486 }
00487 
00488 bool Cursor::deleteAllRows()
00489 {
00491     clearError();
00492     if (!m_query)
00493         return false;
00494     return m_conn->deleteAllRows(*m_query);
00495 }
00496 
00497 QString Cursor::debugString() const
00498 {
00499     QString dbg = "CURSOR( ";
00500     if (!m_query) {
00501         dbg += "RAW STATEMENT: '";
00502         dbg += m_rawStatement;
00503         dbg += "'\n";
00504     }
00505     else {
00506         dbg += "QuerySchema: '";
00507         dbg += m_conn->selectStatement( *m_query );
00508         dbg += "'\n";
00509     }
00510     if (isOpened())
00511         dbg += " OPENED";
00512     else
00513         dbg += " NOT_OPENED";
00514     if (isBuffered())
00515         dbg += " BUFFERED";
00516     else
00517         dbg += " NOT_BUFFERED";
00518     dbg += " AT=";
00519     dbg += QString::number((unsigned long)at());
00520     dbg += " )";
00521     return dbg;
00522 }
00523 
00524 void Cursor::debug() const
00525 {
00526     KexiDBDbg << debugString() << endl;
00527 }
00528 
00529 void Cursor::setOrderByColumnList(const QStringList& columnNames)
00530 {
00531     Q_UNUSED(columnNames);
00533 // all field names should be fooun, exit otherwise ..........
00534 
00535     // OK
00536 //TODO  if (!m_orderByColumnList)
00537 //TODO
00538 }
00539 
00541 void Cursor::setOrderByColumnList(const QString& column1, const QString& column2, 
00542     const QString& column3, const QString& column4, const QString& column5)
00543 {
00544     Q_UNUSED(column1);
00545     Q_UNUSED(column2);
00546     Q_UNUSED(column3);
00547     Q_UNUSED(column4);
00548     Q_UNUSED(column5);
00551 }
00552 
00553 QueryColumnInfo::Vector Cursor::orderByColumnList() const
00554 {
00555     return m_orderByColumnList ? *m_orderByColumnList: QueryColumnInfo::Vector();
00556 }
00557 
00558 QValueList<QVariant> Cursor::queryParameters() const
00559 {
00560     return m_queryParameters ? *m_queryParameters : QValueList<QVariant>();
00561 }
00562 
00563 void Cursor::setQueryParameters(const QValueList<QVariant>& params)
00564 {
00565     if (!m_queryParameters)
00566         m_queryParameters = new QValueList<QVariant>(params);
00567     else
00568         *m_queryParameters = params;
00569 }
00570 
00571 #include "cursor.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys