kexi

cursor.cpp

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