File indexing completed on 2022-11-23 11:08:48

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KDbCursor.h"
0021 #include "KDbConnection.h"
0022 #include "KDbDriver.h"
0023 #include "KDbDriverBehavior.h"
0024 #include "KDbError.h"
0025 #include "KDb.h"
0026 #include "KDbNativeStatementBuilder.h"
0027 #include "KDbQuerySchema.h"
0028 #include "KDbRecordData.h"
0029 #include "KDbRecordEditBuffer.h"
0030 #include "kdb_debug.h"
0031 
0032 class Q_DECL_HIDDEN KDbCursor::Private
0033 {
0034 public:
0035     Private()
0036         : opened(false)
0037         , atLast(false)
0038         , readAhead(false)
0039         , validRecord(false)
0040         , atBuffer(false)
0041     {
0042     }
0043 
0044     ~Private() {
0045     }
0046 
0047     bool containsRecordIdInfo; //!< true if result contains extra column for record id;
0048                                //!< used only for PostgreSQL now
0049     //! @todo IMPORTANT: use something like QPointer<KDbConnection> conn;
0050     KDbConnection *conn;
0051     KDbEscapedString rawSql;
0052     bool opened;
0053     bool atLast;
0054     bool readAhead;
0055     bool validRecord; //!< true if valid record is currently retrieved @ current position
0056 
0057     //! Used by setOrderByColumnList()
0058     KDbQueryColumnInfo::Vector orderByColumnList;
0059     QList<QVariant> queryParameters;
0060 
0061     //<members related to buffering>
0062     bool atBuffer; //!< true if we already point to the buffer with curr_coldata
0063     //</members related to buffering>
0064 };
0065 
0066 KDbCursor::KDbCursor(KDbConnection* conn, const KDbEscapedString& sql, Options options)
0067         : m_query(nullptr)
0068         , m_options(options)
0069         , d(new Private)
0070 {
0071 #ifdef KDB_DEBUG_GUI
0072     KDb::debugGUI(QLatin1String("Create cursor for raw SQL: ") + sql.toString());
0073 #endif
0074     init(conn);
0075     d->rawSql = sql;
0076 }
0077 
0078 KDbCursor::KDbCursor(KDbConnection* conn, KDbQuerySchema* query, Options options)
0079         : m_query(query)
0080         , m_options(options)
0081         , d(new Private)
0082 {
0083 #ifdef KDB_DEBUG_GUI
0084     KDb::debugGUI(QString::fromLatin1("Create cursor for query \"%1\":\n")
0085                   .arg(KDb::iifNotEmpty(query->name(), QString::fromLatin1("<unnamed>")))
0086                   + KDbUtils::debugString(query));
0087 #endif
0088     init(conn);
0089 }
0090 
0091 void KDbCursor::init(KDbConnection* conn)
0092 {
0093     Q_ASSERT(conn);
0094     d->conn = conn;
0095     d->conn->addCursor(this);
0096     m_afterLast = false;
0097     m_at = 0;
0098     m_records_in_buf = 0;
0099     m_buffering_completed = false;
0100     m_fetchResult = FetchResult::Invalid;
0101 
0102     d->containsRecordIdInfo = (m_query && m_query->masterTable())
0103                               && d->conn->driver()->behavior()->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE == false;
0104 
0105     if (m_query) {
0106         //get list of all fields
0107         m_visibleFieldsExpanded = new KDbQueryColumnInfo::Vector();
0108         *m_visibleFieldsExpanded = m_query->visibleFieldsExpanded(conn,
0109                     d->containsRecordIdInfo ? KDbQuerySchema::FieldsExpandedMode::WithInternalFieldsAndRecordId
0110                                             : KDbQuerySchema::FieldsExpandedMode::WithInternalFields);
0111         m_logicalFieldCount = m_visibleFieldsExpanded->count()
0112                               - m_query->internalFields(conn).count() - (d->containsRecordIdInfo ? 1 : 0);
0113         m_fieldCount = m_visibleFieldsExpanded->count();
0114         m_fieldsToStoreInRecord = m_fieldCount;
0115     } else {
0116         m_visibleFieldsExpanded = nullptr;
0117         m_logicalFieldCount = 0;
0118         m_fieldCount = 0;
0119         m_fieldsToStoreInRecord = 0;
0120     }
0121 }
0122 
0123 KDbCursor::~KDbCursor()
0124 {
0125 #ifdef KDB_DEBUG_GUI
0126 #if 0 // too many details
0127     if (m_query)
0128         KDb::debugGUI(QLatin1String("~ Delete cursor for query"));
0129     else
0130         KDb::debugGUI(QLatin1String("~ Delete cursor: ") + m_rawSql.toString());
0131 #endif
0132 #endif
0133     /* if (!m_query)
0134         kdbDebug() << "KDbCursor::~KDbCursor() '" << m_rawSql.toLatin1() << "'";
0135       else
0136         kdbDebug() << "KDbCursor::~KDbCursor() ";*/
0137 
0138     d->conn->takeCursor(this);
0139     delete m_visibleFieldsExpanded;
0140     delete d;
0141 }
0142 
0143 bool KDbCursor::readAhead() const
0144 {
0145     return d->readAhead;
0146 }
0147 
0148 KDbConnection* KDbCursor::connection()
0149 {
0150     return d->conn;
0151 }
0152 
0153 const KDbConnection* KDbCursor::connection() const
0154 {
0155     return d->conn;
0156 }
0157 
0158 KDbQuerySchema *KDbCursor::query() const
0159 {
0160     return m_query;
0161 }
0162 
0163 KDbEscapedString KDbCursor::rawSql() const
0164 {
0165     return d->rawSql;
0166 }
0167 
0168 KDbCursor::Options KDbCursor::options() const
0169 {
0170     return m_options;
0171 }
0172 
0173 bool KDbCursor::isOpened() const
0174 {
0175     return d->opened;
0176 }
0177 
0178 bool KDbCursor::containsRecordIdInfo() const
0179 {
0180     return d->containsRecordIdInfo;
0181 }
0182 
0183 KDbRecordData* KDbCursor::storeCurrentRecord() const
0184 {
0185     KDbRecordData* data = new KDbRecordData(m_fieldsToStoreInRecord);
0186     if (!drv_storeCurrentRecord(data)) {
0187         delete data;
0188         return nullptr;
0189     }
0190     return data;
0191 }
0192 
0193 bool KDbCursor::storeCurrentRecord(KDbRecordData* data) const
0194 {
0195     if (!data) {
0196         return false;
0197     }
0198     data->resize(m_fieldsToStoreInRecord);
0199     return drv_storeCurrentRecord(data);
0200 }
0201 
0202 bool KDbCursor::open()
0203 {
0204     if (d->opened) {
0205         if (!close())
0206             return false;
0207     }
0208     if (!d->rawSql.isEmpty()) {
0209         m_result.setSql(d->rawSql);
0210     }
0211     else {
0212         if (!m_query) {
0213             kdbDebug() << "no query statement (or schema) defined!";
0214             m_result = KDbResult(ERR_SQL_EXECUTION_ERROR,
0215                                  tr("No query statement or schema defined."));
0216             return false;
0217         }
0218         KDbSelectStatementOptions options;
0219         options.setAlsoRetrieveRecordId(d->containsRecordIdInfo); /*get record Id if needed*/
0220         KDbNativeStatementBuilder builder(d->conn, KDb::DriverEscaping);
0221         KDbEscapedString sql;
0222         if (!builder.generateSelectStatement(&sql, m_query, options, d->queryParameters)
0223             || sql.isEmpty())
0224         {
0225             kdbDebug() << "no statement generated!";
0226             m_result = KDbResult(ERR_SQL_EXECUTION_ERROR,
0227                                  tr("Could not generate query statement."));
0228             return false;
0229         }
0230         m_result.setSql(sql);
0231 #ifdef KDB_DEBUG_GUI
0232         KDb::debugGUI(QString::fromLatin1("SQL for query \"%1\": ")
0233                          .arg(KDb::iifNotEmpty(m_query->name(), QString::fromLatin1("<unnamed>")))
0234                       + m_result.sql().toString());
0235 #endif
0236     }
0237     d->opened = drv_open(m_result.sql());
0238     m_afterLast = false; //we are not @ the end
0239     m_at = 0; //we are before 1st rec
0240     if (!d->opened) {
0241         m_result.setCode(ERR_SQL_EXECUTION_ERROR);
0242         m_result.setMessage(tr("Error opening database cursor."));
0243         return false;
0244     }
0245     d->validRecord = false;
0246 
0247     if (d->conn->driver()->behavior()->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY) {
0248 //  kdbDebug() << "READ AHEAD:";
0249         d->readAhead = getNextRecord(); //true if any record in this query
0250 //  kdbDebug() << "READ AHEAD = " << d->readAhead;
0251     }
0252     m_at = 0; //we are still before 1st rec
0253     return !m_result.isError();
0254 }
0255 
0256 bool KDbCursor::close()
0257 {
0258     if (!d->opened) {
0259         return true;
0260     }
0261     bool ret = drv_close();
0262 
0263     clearBuffer();
0264 
0265     d->opened = false;
0266     m_afterLast = false;
0267     d->readAhead = false;
0268     m_fieldCount = 0;
0269     m_fieldsToStoreInRecord = 0;
0270     m_logicalFieldCount = 0;
0271     m_at = -1;
0272 
0273 // kdbDebug() << ret;
0274     return ret;
0275 }
0276 
0277 bool KDbCursor::reopen()
0278 {
0279     if (!d->opened) {
0280         return open();
0281     }
0282     return close() && open();
0283 }
0284 
0285 bool KDbCursor::moveFirst()
0286 {
0287     if (!d->opened) {
0288         return false;
0289     }
0290     if (!d->readAhead) {
0291         if (m_options & KDbCursor::Option::Buffered) {
0292             if (m_records_in_buf == 0 && m_buffering_completed) {
0293                 //eof and bof should now return true:
0294                 m_afterLast = true;
0295                 m_at = 0;
0296                 return false; //buffering completed and there is no records!
0297             }
0298             if (m_records_in_buf > 0) {
0299                 //set state as we would be before first rec:
0300                 d->atBuffer = false;
0301                 m_at = 0;
0302                 //..and move to next, i.e. 1st record
0303                 m_afterLast = !getNextRecord();
0304                 return !m_afterLast;
0305             }
0306         } else if (!(d->conn->driver()->behavior()->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY))  {
0307             // not buffered
0308             m_at = 0;
0309             m_afterLast = !getNextRecord();
0310             return !m_afterLast;
0311         }
0312 
0313         if (m_afterLast && m_at == 0) //failure if already no records
0314             return false;
0315         if (!reopen()) //try reopen
0316             return false;
0317         if (m_afterLast) //eof
0318             return false;
0319     } else {
0320         //we have a record already read-ahead: we now point @ that:
0321         m_at = 1;
0322     }
0323     //get first record
0324     m_afterLast = false;
0325     d->readAhead = false; //1st record had been read
0326     return d->validRecord;
0327 }
0328 
0329 bool KDbCursor::moveLast()
0330 {
0331     if (!d->opened) {
0332         return false;
0333     }
0334     if (m_afterLast || d->atLast) {
0335         return d->validRecord; //we already have valid last record retrieved
0336     }
0337     if (!getNextRecord()) { //at least next record must be retrieved
0338         m_afterLast = true;
0339         d->validRecord = false;
0340         d->atLast = false;
0341         return false; //no records
0342     }
0343     while (getNextRecord()) //move after last rec.
0344         ;
0345     m_afterLast = false;
0346     //cursor shows last record data
0347     d->atLast = true;
0348     return true;
0349 }
0350 
0351 bool KDbCursor::moveNext()
0352 {
0353     if (!d->opened || m_afterLast) {
0354         return false;
0355     }
0356     if (getNextRecord()) {
0357         return true;
0358     }
0359     return false;
0360 }
0361 
0362 bool KDbCursor::movePrev()
0363 {
0364     if (!d->opened /*|| m_beforeFirst*/ || !(m_options & KDbCursor::Option::Buffered)) {
0365         return false;
0366     }
0367     //we're after last record and there are records in the buffer
0368     //--let's move to last record
0369     if (m_afterLast && (m_records_in_buf > 0)) {
0370         drv_bufferMovePointerTo(m_records_in_buf - 1);
0371         m_at = m_records_in_buf;
0372         d->atBuffer = true; //now current record is stored in the buffer
0373         d->validRecord = true;
0374         m_afterLast = false;
0375         return true;
0376     }
0377     //we're at first record: go BOF
0378     if ((m_at <= 1) || (m_records_in_buf <= 1/*sanity*/)) {
0379         m_at = 0;
0380         d->atBuffer = false;
0381         d->validRecord = false;
0382         return false;
0383     }
0384 
0385     m_at--;
0386     if (d->atBuffer) {//we already have got a pointer to buffer
0387         drv_bufferMovePointerPrev(); //just move to prev record in the buffer
0388     } else {//we have no pointer
0389         //compute a place in the buffer that contain next record's data
0390         drv_bufferMovePointerTo(m_at - 1);
0391         d->atBuffer = true; //now current record is stored in the buffer
0392     }
0393     d->validRecord = true;
0394     m_afterLast = false;
0395     return true;
0396 }
0397 
0398 bool KDbCursor::isBuffered() const
0399 {
0400     return m_options & KDbCursor::Option::Buffered;
0401 }
0402 
0403 void KDbCursor::setBuffered(bool buffered)
0404 {
0405     if (!d->opened) {
0406         return;
0407     }
0408     if (isBuffered() == buffered)
0409         return;
0410     m_options ^= KDbCursor::Option::Buffered;
0411 }
0412 
0413 void KDbCursor::clearBuffer()
0414 {
0415     if (!isBuffered() || m_fieldCount == 0)
0416         return;
0417 
0418     drv_clearBuffer();
0419 
0420     m_records_in_buf = 0;
0421     d->atBuffer = false;
0422 }
0423 
0424 bool KDbCursor::getNextRecord()
0425 {
0426     m_fetchResult = FetchResult::Invalid; //by default: invalid result of record fetching
0427 
0428     if (m_options & KDbCursor::Option::Buffered) {//this cursor is buffered:
0429 //  kdbDebug() << "m_at < m_records_in_buf :: " << (long)m_at << " < " << m_records_in_buf;
0430         if (m_at < m_records_in_buf) {//we have next record already buffered:
0431             if (d->atBuffer) {//we already have got a pointer to buffer
0432                 drv_bufferMovePointerNext(); //just move to next record in the buffer
0433             } else {//we have no pointer
0434                 //compute a place in the buffer that contain next record's data
0435                 drv_bufferMovePointerTo(m_at - 1 + 1);
0436                 d->atBuffer = true; //now current record is stored in the buffer
0437             }
0438         } else {//we are after last retrieved record: we need to physically fetch next record:
0439             if (!d->readAhead) {//we have no record that was read ahead
0440                 if (!m_buffering_completed) {
0441                     //retrieve record only if we are not after
0442                     //the last buffer's item (i.e. when buffer is not fully filled):
0443 //     kdbDebug()<<"==== buffering: drv_getNextRecord() ====";
0444                     drv_getNextRecord();
0445                 }
0446                 if (m_fetchResult != FetchResult::Ok) {//there is no record
0447                     m_buffering_completed = true; //no more records for buffer
0448 //     kdbDebug()<<"m_fetchResult != FetchResult::Ok ********";
0449                     d->validRecord = false;
0450                     m_afterLast = true;
0451                     m_at = -1; //position is invalid now and will not be used
0452                     if (m_fetchResult == FetchResult::Error) {
0453                         m_result = KDbResult(ERR_CURSOR_RECORD_FETCHING,
0454                                              tr("Could not fetch next record."));
0455                         return false;
0456                     }
0457                     return false; // in case of m_fetchResult = FetchResult::End or m_fetchResult = FetchInvalid
0458                 }
0459                 //we have a record: store this record's values in the buffer
0460                 drv_appendCurrentRecordToBuffer();
0461                 m_records_in_buf++;
0462             } else //we have a record that was read ahead: eat this
0463                 d->readAhead = false;
0464         }
0465     } else {//we are after last retrieved record: we need to physically fetch next record:
0466         if (!d->readAhead) {//we have no record that was read ahead
0467 //   kdbDebug()<<"==== no prefetched record ====";
0468             drv_getNextRecord();
0469             if (m_fetchResult != FetchResult::Ok) {//there is no record
0470 //    kdbDebug()<<"m_fetchResult != FetchResult::Ok ********";
0471                 d->validRecord = false;
0472                 m_afterLast = true;
0473                 m_at = -1;
0474                 if (m_fetchResult == FetchResult::End) {
0475                     return false;
0476                 }
0477                 m_result = KDbResult(ERR_CURSOR_RECORD_FETCHING,
0478                                      tr("Could not fetch next record."));
0479                 return false;
0480             }
0481         } else { //we have a record that was read ahead: eat this
0482             d->readAhead = false;
0483         }
0484     }
0485 
0486     m_at++;
0487 
0488 // if (m_data->curr_colname && m_data->curr_coldata)
0489 //  for (int i=0;i<m_data->curr_cols;i++) {
0490 //   kdbDebug()<<i<<": "<< m_data->curr_colname[i]<<" == "<< m_data->curr_coldata[i];
0491 //  }
0492 // kdbDebug()<<"m_at == "<<(long)m_at;
0493 
0494     d->validRecord = true;
0495     return true;
0496 }
0497 
0498 bool KDbCursor::updateRecord(KDbRecordData* data, KDbRecordEditBuffer* buf, bool useRecordId)
0499 {
0500 //! @todo doesn't update cursor's buffer YET!
0501     clearResult();
0502     if (!m_query)
0503         return false;
0504     return d->conn->updateRecord(m_query, data, buf, useRecordId);
0505 }
0506 
0507 bool KDbCursor::insertRecord(KDbRecordData* data, KDbRecordEditBuffer* buf, bool useRecordId)
0508 {
0509 //! @todo doesn't update cursor's buffer YET!
0510     if (!m_query) {
0511         clearResult();
0512         return false;
0513     }
0514     return d->conn->insertRecord(m_query, data, buf, useRecordId);
0515 }
0516 
0517 bool KDbCursor::deleteRecord(KDbRecordData* data, bool useRecordId)
0518 {
0519 //! @todo doesn't update cursor's buffer YET!
0520     clearResult();
0521     if (!m_query)
0522         return false;
0523     return d->conn->deleteRecord(m_query, data, useRecordId);
0524 }
0525 
0526 bool KDbCursor::deleteAllRecords()
0527 {
0528 //! @todo doesn't update cursor's buffer YET!
0529     clearResult();
0530     if (!m_query)
0531         return false;
0532     return d->conn->deleteAllRecords(m_query);
0533 }
0534 
0535 QDebug debug(QDebug dbg, KDbCursor& cursor, bool buildSql)
0536 {
0537     dbg.nospace() << "CURSOR(";
0538     if (!cursor.query()) {
0539         dbg.nospace() << "RAW SQL STATEMENT:" << cursor.rawSql().toString()
0540                       << "\n";
0541     }
0542     else if (buildSql) {
0543         KDbNativeStatementBuilder builder(cursor.connection(), KDb::DriverEscaping);
0544         KDbEscapedString sql;
0545         QString sqlString;
0546         if (builder.generateSelectStatement(&sql, cursor.query())) {
0547             sqlString = sql.toString();
0548         }
0549         else {
0550             sqlString = QLatin1String("<CANNOT GENERATE!>");
0551         }
0552         dbg.nospace() << "KDbQuerySchema:" << sqlString << "\n";
0553     }
0554     if (cursor.isOpened()) {
0555         dbg.space() << "OPENED";
0556     }
0557     else {
0558         dbg.space() << "NOT_OPENED";
0559     }
0560     if (cursor.isBuffered()) {
0561         dbg.space() << "BUFFERED";
0562     }
0563     else {
0564         dbg.space() << "NOT_BUFFERED";
0565     }
0566     dbg.nospace() << "AT=" << cursor.at() << ")";
0567     return dbg.space();
0568 }
0569 
0570 QDebug operator<<(QDebug dbg, KDbCursor &cursor)
0571 {
0572     return debug(dbg, cursor, true /*buildSql*/);
0573 }
0574 
0575 QDebug operator<<(QDebug dbg, const KDbCursor &cursor)
0576 {
0577     return debug(dbg, const_cast<KDbCursor&>(cursor), false /* !buildSql*/);
0578 }
0579 
0580 void KDbCursor::setOrderByColumnList(const QStringList& columnNames)
0581 {
0582     Q_UNUSED(columnNames);
0583 //! @todo implement this: all field names should be found, exit otherwise
0584 
0585     // OK
0586 //! @todo if (!d->orderByColumnList)
0587 }
0588 
0589 /*! Convenience method, similar to setOrderBy(const QStringList&). */
0590 void KDbCursor::setOrderByColumnList(const QString& column1, const QString& column2,
0591                                   const QString& column3, const QString& column4, const QString& column5)
0592 {
0593     Q_UNUSED(column1);
0594     Q_UNUSED(column2);
0595     Q_UNUSED(column3);
0596     Q_UNUSED(column4);
0597     Q_UNUSED(column5);
0598 //! @todo implement this, like above
0599 //! @todo add ORDER BY info to debugString()
0600 }
0601 
0602 KDbQueryColumnInfo::Vector KDbCursor::orderByColumnList() const
0603 {
0604     return d->orderByColumnList;
0605 }
0606 
0607 QList<QVariant> KDbCursor::queryParameters() const
0608 {
0609     return d->queryParameters;
0610 }
0611 
0612 void KDbCursor::setQueryParameters(const QList<QVariant>& params)
0613 {
0614     d->queryParameters = params;
0615 }
0616 
0617 //! @todo extraMessages
0618 #if 0
0619 static const char *extraMessages[] = {
0620     QT_TRANSLATE_NOOP("KDbCursor", "No connection for cursor open operation specified.")
0621 };
0622 #endif