KDb

KDbCursor.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2003-2016 JarosÅ‚aw Staniek <[email protected]>
3 
4  This program is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this program; see the file COPYING. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 
20 #include "KDbCursor.h"
21 #include "KDbConnection.h"
22 #include "KDbDriver.h"
23 #include "KDbDriverBehavior.h"
24 #include "KDbError.h"
25 #include "KDb.h"
26 #include "KDbNativeStatementBuilder.h"
27 #include "KDbQuerySchema.h"
28 #include "KDbRecordData.h"
29 #include "KDbRecordEditBuffer.h"
30 #include "kdb_debug.h"
31 
32 class Q_DECL_HIDDEN KDbCursor::Private
33 {
34 public:
35  Private()
36  : opened(false)
37  , atLast(false)
38  , readAhead(false)
39  , validRecord(false)
40  , atBuffer(false)
41  {
42  }
43 
44  ~Private() {
45  }
46 
47  bool containsRecordIdInfo; //!< true if result contains extra column for record id;
48  //!< used only for PostgreSQL now
49  //! @todo IMPORTANT: use something like QPointer<KDbConnection> conn;
50  KDbConnection *conn;
51  KDbEscapedString rawSql;
52  bool opened;
53  bool atLast;
54  bool readAhead;
55  bool validRecord; //!< true if valid record is currently retrieved @ current position
56 
57  //! Used by setOrderByColumnList()
58  KDbQueryColumnInfo::Vector orderByColumnList;
59  QList<QVariant> queryParameters;
60 
61  //<members related to buffering>
62  bool atBuffer; //!< true if we already point to the buffer with curr_coldata
63  //</members related to buffering>
64 };
65 
67  : m_query(nullptr)
68  , m_options(options)
69  , d(new Private)
70 {
71 #ifdef KDB_DEBUG_GUI
72  KDb::debugGUI(QLatin1String("Create cursor for raw SQL: ") + sql.toString());
73 #endif
74  init(conn);
75  d->rawSql = sql;
76 }
77 
79  : m_query(query)
80  , m_options(options)
81  , d(new Private)
82 {
83 #ifdef KDB_DEBUG_GUI
84  KDb::debugGUI(QString::fromLatin1("Create cursor for query \"%1\":\n")
85  .arg(KDb::iifNotEmpty(query->name(), QString::fromLatin1("<unnamed>")))
86  + KDbUtils::debugString(query));
87 #endif
88  init(conn);
89 }
90 
91 void KDbCursor::init(KDbConnection* conn)
92 {
93  Q_ASSERT(conn);
94  d->conn = conn;
95  d->conn->addCursor(this);
96  m_afterLast = false;
97  m_at = 0;
98  m_records_in_buf = 0;
99  m_buffering_completed = false;
101 
102  d->containsRecordIdInfo = (m_query && m_query->masterTable())
103  && d->conn->driver()->behavior()->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE == false;
104 
105  if (m_query) {
106  //get list of all fields
112  - m_query->internalFields(conn).count() - (d->containsRecordIdInfo ? 1 : 0);
115  } else {
116  m_visibleFieldsExpanded = nullptr;
118  m_fieldCount = 0;
120  }
121 }
122 
123 KDbCursor::~KDbCursor()
124 {
125 #ifdef KDB_DEBUG_GUI
126 #if 0 // too many details
127  if (m_query)
128  KDb::debugGUI(QLatin1String("~ Delete cursor for query"));
129  else
130  KDb::debugGUI(QLatin1String("~ Delete cursor: ") + m_rawSql.toString());
131 #endif
132 #endif
133  /* if (!m_query)
134  kdbDebug() << "KDbCursor::~KDbCursor() '" << m_rawSql.toLatin1() << "'";
135  else
136  kdbDebug() << "KDbCursor::~KDbCursor() ";*/
137 
138  d->conn->takeCursor(this);
140  delete d;
141 }
142 
143 bool KDbCursor::readAhead() const
144 {
145  return d->readAhead;
146 }
147 
149 {
150  return d->conn;
151 }
152 
154 {
155  return d->conn;
156 }
157 
159 {
160  return m_query;
161 }
162 
164 {
165  return d->rawSql;
166 }
167 
169 {
170  return m_options;
171 }
172 
174 {
175  return d->opened;
176 }
177 
179 {
180  return d->containsRecordIdInfo;
181 }
182 
184 {
186  if (!drv_storeCurrentRecord(data)) {
187  delete data;
188  return nullptr;
189  }
190  return data;
191 }
192 
194 {
195  if (!data) {
196  return false;
197  }
199  return drv_storeCurrentRecord(data);
200 }
201 
203 {
204  if (d->opened) {
205  if (!close())
206  return false;
207  }
208  if (!d->rawSql.isEmpty()) {
209  m_result.setSql(d->rawSql);
210  }
211  else {
212  if (!m_query) {
213  kdbDebug() << "no query statement (or schema) defined!";
214  m_result = KDbResult(ERR_SQL_EXECUTION_ERROR,
215  tr("No query statement or schema defined."));
216  return false;
217  }
219  options.setAlsoRetrieveRecordId(d->containsRecordIdInfo); /*get record Id if needed*/
221  KDbEscapedString sql;
222  if (!builder.generateSelectStatement(&sql, m_query, options, d->queryParameters)
223  || sql.isEmpty())
224  {
225  kdbDebug() << "no statement generated!";
226  m_result = KDbResult(ERR_SQL_EXECUTION_ERROR,
227  tr("Could not generate query statement."));
228  return false;
229  }
230  m_result.setSql(sql);
231 #ifdef KDB_DEBUG_GUI
232  KDb::debugGUI(QString::fromLatin1("SQL for query \"%1\": ")
233  .arg(KDb::iifNotEmpty(m_query->name(), QString::fromLatin1("<unnamed>")))
234  + m_result.sql().toString());
235 #endif
236  }
237  d->opened = drv_open(m_result.sql());
238  m_afterLast = false; //we are not @ the end
239  m_at = 0; //we are before 1st rec
240  if (!d->opened) {
241  m_result.setCode(ERR_SQL_EXECUTION_ERROR);
242  m_result.setMessage(tr("Error opening database cursor."));
243  return false;
244  }
245  d->validRecord = false;
246 
247  if (d->conn->driver()->behavior()->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY) {
248 // kdbDebug() << "READ AHEAD:";
249  d->readAhead = getNextRecord(); //true if any record in this query
250 // kdbDebug() << "READ AHEAD = " << d->readAhead;
251  }
252  m_at = 0; //we are still before 1st rec
253  return !m_result.isError();
254 }
255 
257 {
258  if (!d->opened) {
259  return true;
260  }
261  bool ret = drv_close();
262 
263  clearBuffer();
264 
265  d->opened = false;
266  m_afterLast = false;
267  d->readAhead = false;
268  m_fieldCount = 0;
271  m_at = -1;
272 
273 // kdbDebug() << ret;
274  return ret;
275 }
276 
278 {
279  if (!d->opened) {
280  return open();
281  }
282  return close() && open();
283 }
284 
286 {
287  if (!d->opened) {
288  return false;
289  }
290  if (!d->readAhead) {
291  if (m_options & KDbCursor::Option::Buffered) {
293  //eof and bof should now return true:
294  m_afterLast = true;
295  m_at = 0;
296  return false; //buffering completed and there is no records!
297  }
298  if (m_records_in_buf > 0) {
299  //set state as we would be before first rec:
300  d->atBuffer = false;
301  m_at = 0;
302  //..and move to next, i.e. 1st record
303  m_afterLast = !getNextRecord();
304  return !m_afterLast;
305  }
306  } else if (!(d->conn->driver()->behavior()->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY)) {
307  // not buffered
308  m_at = 0;
309  m_afterLast = !getNextRecord();
310  return !m_afterLast;
311  }
312 
313  if (m_afterLast && m_at == 0) //failure if already no records
314  return false;
315  if (!reopen()) //try reopen
316  return false;
317  if (m_afterLast) //eof
318  return false;
319  } else {
320  //we have a record already read-ahead: we now point @ that:
321  m_at = 1;
322  }
323  //get first record
324  m_afterLast = false;
325  d->readAhead = false; //1st record had been read
326  return d->validRecord;
327 }
328 
330 {
331  if (!d->opened) {
332  return false;
333  }
334  if (m_afterLast || d->atLast) {
335  return d->validRecord; //we already have valid last record retrieved
336  }
337  if (!getNextRecord()) { //at least next record must be retrieved
338  m_afterLast = true;
339  d->validRecord = false;
340  d->atLast = false;
341  return false; //no records
342  }
343  while (getNextRecord()) //move after last rec.
344  ;
345  m_afterLast = false;
346  //cursor shows last record data
347  d->atLast = true;
348  return true;
349 }
350 
352 {
353  if (!d->opened || m_afterLast) {
354  return false;
355  }
356  if (getNextRecord()) {
357  return true;
358  }
359  return false;
360 }
361 
363 {
364  if (!d->opened /*|| m_beforeFirst*/ || !(m_options & KDbCursor::Option::Buffered)) {
365  return false;
366  }
367  //we're after last record and there are records in the buffer
368  //--let's move to last record
369  if (m_afterLast && (m_records_in_buf > 0)) {
371  m_at = m_records_in_buf;
372  d->atBuffer = true; //now current record is stored in the buffer
373  d->validRecord = true;
374  m_afterLast = false;
375  return true;
376  }
377  //we're at first record: go BOF
378  if ((m_at <= 1) || (m_records_in_buf <= 1/*sanity*/)) {
379  m_at = 0;
380  d->atBuffer = false;
381  d->validRecord = false;
382  return false;
383  }
384 
385  m_at--;
386  if (d->atBuffer) {//we already have got a pointer to buffer
387  drv_bufferMovePointerPrev(); //just move to prev record in the buffer
388  } else {//we have no pointer
389  //compute a place in the buffer that contain next record's data
390  drv_bufferMovePointerTo(m_at - 1);
391  d->atBuffer = true; //now current record is stored in the buffer
392  }
393  d->validRecord = true;
394  m_afterLast = false;
395  return true;
396 }
397 
399 {
400  return m_options & KDbCursor::Option::Buffered;
401 }
402 
403 void KDbCursor::setBuffered(bool buffered)
404 {
405  if (!d->opened) {
406  return;
407  }
408  if (isBuffered() == buffered)
409  return;
410  m_options ^= KDbCursor::Option::Buffered;
411 }
412 
414 {
415  if (!isBuffered() || m_fieldCount == 0)
416  return;
417 
418  drv_clearBuffer();
419 
420  m_records_in_buf = 0;
421  d->atBuffer = false;
422 }
423 
425 {
426  m_fetchResult = FetchResult::Invalid; //by default: invalid result of record fetching
427 
428  if (m_options & KDbCursor::Option::Buffered) {//this cursor is buffered:
429 // kdbDebug() << "m_at < m_records_in_buf :: " << (long)m_at << " < " << m_records_in_buf;
430  if (m_at < m_records_in_buf) {//we have next record already buffered:
431  if (d->atBuffer) {//we already have got a pointer to buffer
432  drv_bufferMovePointerNext(); //just move to next record in the buffer
433  } else {//we have no pointer
434  //compute a place in the buffer that contain next record's data
435  drv_bufferMovePointerTo(m_at - 1 + 1);
436  d->atBuffer = true; //now current record is stored in the buffer
437  }
438  } else {//we are after last retrieved record: we need to physically fetch next record:
439  if (!d->readAhead) {//we have no record that was read ahead
440  if (!m_buffering_completed) {
441  //retrieve record only if we are not after
442  //the last buffer's item (i.e. when buffer is not fully filled):
443 // kdbDebug()<<"==== buffering: drv_getNextRecord() ====";
444  drv_getNextRecord();
445  }
446  if (m_fetchResult != FetchResult::Ok) {//there is no record
447  m_buffering_completed = true; //no more records for buffer
448 // kdbDebug()<<"m_fetchResult != FetchResult::Ok ********";
449  d->validRecord = false;
450  m_afterLast = true;
451  m_at = -1; //position is invalid now and will not be used
453  m_result = KDbResult(ERR_CURSOR_RECORD_FETCHING,
454  tr("Could not fetch next record."));
455  return false;
456  }
457  return false; // in case of m_fetchResult = FetchResult::End or m_fetchResult = FetchInvalid
458  }
459  //we have a record: store this record's values in the buffer
462  } else //we have a record that was read ahead: eat this
463  d->readAhead = false;
464  }
465  } else {//we are after last retrieved record: we need to physically fetch next record:
466  if (!d->readAhead) {//we have no record that was read ahead
467 // kdbDebug()<<"==== no prefetched record ====";
468  drv_getNextRecord();
469  if (m_fetchResult != FetchResult::Ok) {//there is no record
470 // kdbDebug()<<"m_fetchResult != FetchResult::Ok ********";
471  d->validRecord = false;
472  m_afterLast = true;
473  m_at = -1;
475  return false;
476  }
477  m_result = KDbResult(ERR_CURSOR_RECORD_FETCHING,
478  tr("Could not fetch next record."));
479  return false;
480  }
481  } else { //we have a record that was read ahead: eat this
482  d->readAhead = false;
483  }
484  }
485 
486  m_at++;
487 
488 // if (m_data->curr_colname && m_data->curr_coldata)
489 // for (int i=0;i<m_data->curr_cols;i++) {
490 // kdbDebug()<<i<<": "<< m_data->curr_colname[i]<<" == "<< m_data->curr_coldata[i];
491 // }
492 // kdbDebug()<<"m_at == "<<(long)m_at;
493 
494  d->validRecord = true;
495  return true;
496 }
497 
498 bool KDbCursor::updateRecord(KDbRecordData* data, KDbRecordEditBuffer* buf, bool useRecordId)
499 {
500 //! @todo doesn't update cursor's buffer YET!
501  clearResult();
502  if (!m_query)
503  return false;
504  return d->conn->updateRecord(m_query, data, buf, useRecordId);
505 }
506 
507 bool KDbCursor::insertRecord(KDbRecordData* data, KDbRecordEditBuffer* buf, bool useRecordId)
508 {
509 //! @todo doesn't update cursor's buffer YET!
510  if (!m_query) {
511  clearResult();
512  return false;
513  }
514  return d->conn->insertRecord(m_query, data, buf, useRecordId);
515 }
516 
517 bool KDbCursor::deleteRecord(KDbRecordData* data, bool useRecordId)
518 {
519 //! @todo doesn't update cursor's buffer YET!
520  clearResult();
521  if (!m_query)
522  return false;
523  return d->conn->deleteRecord(m_query, data, useRecordId);
524 }
525 
527 {
528 //! @todo doesn't update cursor's buffer YET!
529  clearResult();
530  if (!m_query)
531  return false;
532  return d->conn->deleteAllRecords(m_query);
533 }
534 
535 QDebug debug(QDebug dbg, KDbCursor& cursor, bool buildSql)
536 {
537  dbg.nospace() << "CURSOR(";
538  if (!cursor.query()) {
539  dbg.nospace() << "RAW SQL STATEMENT:" << cursor.rawSql().toString()
540  << "\n";
541  }
542  else if (buildSql) {
544  KDbEscapedString sql;
545  QString sqlString;
546  if (builder.generateSelectStatement(&sql, cursor.query())) {
547  sqlString = sql.toString();
548  }
549  else {
550  sqlString = QLatin1String("<CANNOT GENERATE!>");
551  }
552  dbg.nospace() << "KDbQuerySchema:" << sqlString << "\n";
553  }
554  if (cursor.isOpened()) {
555  dbg.space() << "OPENED";
556  }
557  else {
558  dbg.space() << "NOT_OPENED";
559  }
560  if (cursor.isBuffered()) {
561  dbg.space() << "BUFFERED";
562  }
563  else {
564  dbg.space() << "NOT_BUFFERED";
565  }
566  dbg.nospace() << "AT=" << cursor.at() << ")";
567  return dbg.space();
568 }
569 
570 QDebug operator<<(QDebug dbg, KDbCursor &cursor)
571 {
572  return debug(dbg, cursor, true /*buildSql*/);
573 }
574 
575 QDebug operator<<(QDebug dbg, const KDbCursor &cursor)
576 {
577  return debug(dbg, const_cast<KDbCursor&>(cursor), false /* !buildSql*/);
578 }
579 
581 {
582  Q_UNUSED(columnNames);
583 //! @todo implement this: all field names should be found, exit otherwise
584 
585  // OK
586 //! @todo if (!d->orderByColumnList)
587 }
588 
589 /*! Convenience method, similar to setOrderBy(const QStringList&). */
590 void KDbCursor::setOrderByColumnList(const QString& column1, const QString& column2,
591  const QString& column3, const QString& column4, const QString& column5)
592 {
593  Q_UNUSED(column1);
594  Q_UNUSED(column2);
595  Q_UNUSED(column3);
596  Q_UNUSED(column4);
597  Q_UNUSED(column5);
598 //! @todo implement this, like above
599 //! @todo add ORDER BY info to debugString()
600 }
601 
603 {
604  return d->orderByColumnList;
605 }
606 
608 {
609  return d->queryParameters;
610 }
611 
613 {
614  d->queryParameters = params;
615 }
616 
617 //! @todo extraMessages
618 #if 0
619 static const char *extraMessages[] = {
620  QT_TRANSLATE_NOOP("KDbCursor", "No connection for cursor open operation specified.")
621 };
622 #endif
@ Ok
the data is fetched
Provides database cursor functionality.
Definition: KDbCursor.h:68
virtual bool moveNext()
Definition: KDbCursor.cpp:351
KDbQueryColumnInfo::Vector internalFields(KDbConnection *conn) const
bool getNextRecord()
Definition: KDbCursor.cpp:424
Options used in KDbNativeStatementBuilder::generateSelectStatement()
void setQueryParameters(const QList< QVariant > &params)
Sets query parameters params for this cursor.
Definition: KDbCursor.cpp:612
KDbTableSchema * masterTable() const
FetchResult m_fetchResult
result of a record fetching
Definition: KDbCursor.h:316
KDbQuerySchema * query() const
Definition: KDbCursor.cpp:158
bool moveFirst()
Definition: KDbCursor.cpp:285
QDebug & nospace()
A builder for generating various types of native SQL statements.
KDbEscapedString rawSql() const
Definition: KDbCursor.cpp:163
Specialized string for escaping.
bool insertRecord(KDbRecordData *data, KDbRecordEditBuffer *buf, bool getRecrordId=false)
Definition: KDbCursor.cpp:507
bool generateSelectStatement(KDbEscapedString *target, KDbQuerySchema *querySchema, const KDbSelectStatementOptions &options, const QList< QVariant > &parameters=QList< QVariant >()) const
QString name
QDebug & space()
QDataStream & operator<<(QDataStream &out, const KDateTime &dateTime)
bool updateRecord(KDbRecordData *data, KDbRecordEditBuffer *buf, bool useRecordId=false)
Definition: KDbCursor.cpp:498
@ DriverEscaping
Identifiers are escaped by driver.
Definition: KDbGlobal.h:145
virtual void drv_bufferMovePointerTo(qint64 at)=0
bool deleteRecord(KDbRecordData *data, bool useRecordId=false)
Definition: KDbCursor.cpp:517
KDbRecordData * storeCurrentRecord() const
Definition: KDbCursor.cpp:183
virtual void drv_appendCurrentRecordToBuffer()=0
KDbQueryColumnInfo::Vector visibleFieldsExpanded(KDbConnection *conn, FieldsExpandedMode options=FieldsExpandedMode::Default) const
bool isBuffered() const
Definition: KDbCursor.cpp:398
void setOrderByColumnList(const QStringList &columnNames)
Definition: KDbCursor.cpp:580
@ End
at the end of data
KDbQueryColumnInfo::Vector * m_visibleFieldsExpanded
Useful e.g. for value(int) method to obtain access to schema definition.
Definition: KDbCursor.h:324
bool containsRecordIdInfo() const
Definition: KDbCursor.cpp:178
KDbQueryColumnInfo::Vector orderByColumnList() const
Definition: KDbCursor.cpp:602
int m_records_in_buf
number of records currently stored in the buffer
Definition: KDbCursor.h:319
T iifNotEmpty(const T &string, const T &stringIfEmpty)
Definition: KDb.h:820
bool deleteAllRecords()
Definition: KDbCursor.cpp:526
provides data for single edited database record
virtual void drv_clearBuffer()
Definition: KDbCursor.h:285
bool isError() const
Definition: KDbResult.cpp:64
@ WithInternalFieldsAndRecordId
Like WithInternalFields but record ID (big int type) field is appended after internal fields.
virtual bool drv_open(const KDbEscapedString &sql)=0
virtual bool drv_storeCurrentRecord(KDbRecordData *data) const =0
bool m_buffering_completed
true if we already have all records stored in the buffer
Definition: KDbCursor.h:320
virtual bool moveLast()
Definition: KDbCursor.cpp:329
Structure for storing single record with type information.
Definition: KDbRecordData.h:36
bool isOpened() const
Definition: KDbCursor.cpp:173
KDbQuerySchema provides information about database query.
virtual void drv_bufferMovePointerPrev()=0
bool reopen()
Definition: KDbCursor.cpp:277
QString fromLatin1(const char *str, int size)
KDbCursor(KDbConnection *conn, const KDbEscapedString &sql, Options options=KDbCursor::Option::None)
Definition: KDbCursor.cpp:66
void resize(int numCols)
@ Invalid
used before starting the fetching, result is not known yet
int count(const T &value) const const
int m_logicalFieldCount
logical field count, i.e. without internal values like Record Id or lookup
Definition: KDbCursor.h:305
virtual bool close()
Definition: KDbCursor.cpp:256
Provides database connection, allowing queries and data modification.
Definition: KDbConnection.h:51
void setBuffered(bool buffered)
Definition: KDbCursor.cpp:403
qint64 at() const
Definition: KDbCursor.h:161
@ Error
error of fetching
@ WithInternalFields
Like Default but internal fields (for lookup) are appended.
virtual void drv_bufferMovePointerNext()=0
int m_fieldCount
cached field count information
Definition: KDbCursor.h:301
QList< QVariant > queryParameters() const
Definition: KDbCursor.cpp:607
bool open()
Definition: KDbCursor.cpp:202
int m_fieldsToStoreInRecord
Used by storeCurrentRecord(), reimplement if needed.
Definition: KDbCursor.h:302
Options options() const
Definition: KDbCursor.cpp:168
void clearBuffer()
Definition: KDbCursor.cpp:413
KDbCursor::Options m_options
cursor options that describes its behavior
Definition: KDbCursor.h:306
KDbConnection * connection()
Definition: KDbCursor.cpp:148
virtual bool movePrev()
Definition: KDbCursor.cpp:362
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:21:33 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.