kexi

cursor.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
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 <kexidb/cursor.h>
21
22#include <kexidb/driver.h>
23#include <kexidb/driver_p.h>
24#include <kexidb/error.h>
25#include <kexidb/roweditbuffer.h>
26#include <kexiutils/utils.h>
27
28#include <kdebug.h>
29#include <tdelocale.h>
30
31#include <assert.h>
32#include <stdlib.h>
33
34using namespace KexiDB;
35
36#ifdef KEXI_DEBUG_GUI
37
38#endif
39
40Cursor::Cursor(Connection* conn, const TQString& statement, uint options)
41 : TQObject()
42 , m_conn(conn)
43 , m_query(0)
44 , m_rawStatement(statement)
45 , m_options(options)
46{
47#ifdef KEXI_DEBUG_GUI
48 KexiUtils::addKexiDBDebug(TQString("Create cursor: ")+statement);
49#endif
50 init();
51}
52
53Cursor::Cursor(Connection* conn, QuerySchema& query, uint options )
54 : TQObject()
55 , m_conn(conn)
56 , m_query(&query)
57 , m_options(options)
58{
59#ifdef KEXI_DEBUG_GUI
60 KexiUtils::addKexiDBDebug(TQString("Create cursor for query \"%1\": ").arg(query.name())+query.debugString());
61#endif
62 init();
63}
64
65void Cursor::init()
66{
67 assert(m_conn);
68 m_conn->m_cursors.insert(this,this);
69 m_opened = false;
70// , m_atFirst(false)
71// , m_atLast(false)
72// , m_beforeFirst(false)
73 m_atLast = false;
74 m_afterLast = false;
75 m_readAhead = false;
76 m_at = 0;
77//js:todo: if (m_query)
78// m_fieldCount = m_query->fieldsCount();
79// m_fieldCount = m_query ? m_query->fieldCount() : 0; //do not know
80 //<members related to buffering>
81// m_cols_pointers_mem_size = 0;
84 m_at_buffer = false;
85 m_result = -1;
86
87 m_containsROWIDInfo = (m_query && m_query->masterTable())
88 && m_conn->driver()->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE == false;
89
90 if (m_query) {
91 //get list of all fields
92 m_fieldsExpanded = new QueryColumnInfo::Vector();
94 m_containsROWIDInfo ? QuerySchema::WithInternalFieldsAndRowID : QuerySchema::WithInternalFields);
96 - m_query->internalFields().count() - (m_containsROWIDInfo?1:0);
98 } else {
101 m_fieldCount = 0;
102 }
104 m_queryParameters = 0;
105}
106
107Cursor::~Cursor()
108{
109#ifdef KEXI_DEBUG_GUI
110 if (m_query)
111 KexiUtils::addKexiDBDebug(TQString("~ Delete cursor for query"));
112 else
113 KexiUtils::addKexiDBDebug(TQString("~ Delete cursor: ")+m_rawStatement);
114#endif
115/* if (!m_query)
116 KexiDBDbg << "Cursor::~Cursor() '" << m_rawStatement.latin1() << "'" << endl;
117 else
118 KexiDBDbg << "Cursor::~Cursor() " << endl;*/
119
120 //take me if delete was
121 if (!m_conn->m_destructor_started)
122 m_conn->m_cursors.take(this);
123 else {
124 KexiDBDbg << "Cursor::~Cursor() can be destroyed with Conenction::deleteCursor(), not with delete operator !"<< endl;
125 exit(1);
126 }
127 delete m_fieldsExpanded;
128 delete m_queryParameters;
129}
130
132{
133 if (m_opened) {
134 if (!close())
135 return false;
136 }
137 if (!m_rawStatement.isEmpty())
138 m_conn->m_sql = m_rawStatement;
139 else {
140 if (!m_query) {
141 KexiDBDbg << "Cursor::open(): no query statement (or schema) defined!" << endl;
142 setError(ERR_SQL_EXECUTION_ERROR, i18n("No query statement or schema defined."));
143 return false;
144 }
146 options.alsoRetrieveROWID = m_containsROWIDInfo; /*get ROWID if needed*/
147 m_conn->m_sql = m_queryParameters
148 ? m_conn->selectStatement( *m_query, *m_queryParameters, options )
149 : m_conn->selectStatement( *m_query, options );
150 if (m_conn->m_sql.isEmpty()) {
151 KexiDBDbg << "Cursor::open(): empty statement!" << endl;
152 setError(ERR_SQL_EXECUTION_ERROR, i18n("Query statement is empty."));
153 return false;
154 }
155 }
156 m_sql = m_conn->m_sql;
157 m_opened = drv_open();
158// m_beforeFirst = true;
159 m_afterLast = false; //we are not @ the end
160 m_at = 0; //we are before 1st rec
161 if (!m_opened) {
162 setError(ERR_SQL_EXECUTION_ERROR, i18n("Error opening database cursor."));
163 return false;
164 }
165 m_validRecord = false;
166
167//luci: WHAT_EXACTLY_SHOULD_THAT_BE?
168// if (!m_readAhead) // jowenn: to ensure before first state, without cluttering implementation code
169 if (m_conn->driver()->beh->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY) {
170// KexiDBDbg << "READ AHEAD:" << endl;
171 m_readAhead = getNextRecord(); //true if any record in this query
172// KexiDBDbg << "READ AHEAD = " << m_readAhead << endl;
173 }
174 m_at = 0; //we are still before 1st rec
175 return !error();
176}
177
179{
180 if (!m_opened)
181 return true;
182 bool ret = drv_close();
183
184 clearBuffer();
185
186 m_opened = false;
187// m_beforeFirst = false;
188 m_afterLast = false;
189 m_readAhead = false;
190 m_fieldCount = 0;
192 m_at = -1;
193
194// KexiDBDbg<<"Cursor::close() == "<<ret<<endl;
195 return ret;
196}
197
199{
200 if (!m_opened)
201 return open();
202 return close() && open();
203}
204
206{
207 if (!m_opened)
208 return false;
209// if (!m_beforeFirst) { //cursor isn't @ first record now: reopen
210 if (!m_readAhead) {
211 if (m_options & Buffered) {
213 //eof and bof should now return true:
214 m_afterLast = true;
215 m_at = 0;
216 return false; //buffering completed and there is no records!
217 }
218 if (m_records_in_buf>0) {
219 //set state as we would be before first rec:
220 m_at_buffer = false;
221 m_at = 0;
222 //..and move to next, ie. 1st record
223// m_afterLast = m_afterLast = !getNextRecord();
224 m_afterLast = !getNextRecord();
225 return !m_afterLast;
226 }
227 }
228 if (m_afterLast && m_at==0) //failure if already no records
229 return false;
230 if (!reopen()) //try reopen
231 return false;
232 if (m_afterLast) //eof
233 return false;
234 }
235 else {
236 //we have a record already read-ahead: we now point @ that:
237 m_at = 1;
238 }
239// if (!m_atFirst) { //cursor isn't @ first record now: reopen
240// reopen();
241// }
242// if (m_validRecord) {
243// return true; //there is already valid record retrieved
244// }
245 //get first record
246// if (drv_moveFirst() && drv_getRecord()) {
247// m_beforeFirst = false;
248 m_afterLast = false;
249 m_readAhead = false; //1st record had been read
250// }
251 return m_validRecord;
252}
253
255{
256 if (!m_opened)
257 return false;
258 if (m_afterLast || m_atLast) {
259 return m_validRecord; //we already have valid last record retrieved
260 }
261 if (!getNextRecord()) { //at least next record must be retrieved
262// m_beforeFirst = false;
263 m_afterLast = true;
264 m_validRecord = false;
265 m_atLast = false;
266 return false; //no records
267 }
268 while (getNextRecord()) //move after last rec.
269 ;
270// m_beforeFirst = false;
271 m_afterLast = false;
272 //cursor shows last record data
273 m_atLast = true;
274// m_validRecord = true;
275
276/*
277 //we are before or @ last record:
278// if (m_atLast && m_validRecord) //we're already @ last rec.
279// return true;
280 if (m_validRecord) {
281 if (drv_getRecord())
282 }
283 if (!m_validRecord) {
284 if (drv_getRecord() && m_validRecord)
285 return true;
286 reopen();
287 }
288 */
289 return true;
290}
291
293{
294 if (!m_opened || m_afterLast)
295 return false;
296 if (getNextRecord()) {
297// m_validRecord = true;
298 return true;
299 }
300 return false;
301}
302
304{
305 if (!m_opened /*|| m_beforeFirst*/ || !(m_options & Buffered))
306 return false;
307
308 //we're after last record and there are records in the buffer
309 //--let's move to last record
310 if (m_afterLast && (m_records_in_buf>0)) {
312 m_at=m_records_in_buf;
313 m_at_buffer = true; //now current record is stored in the buffer
314 m_validRecord=true;
315 m_afterLast=false;
316 return true;
317 }
318 //we're at first record: go BOF
319 if ((m_at <= 1) || (m_records_in_buf <= 1/*sanity*/)) {
320 m_at=0;
321 m_at_buffer = false;
322 m_validRecord=false;
323 return false;
324 }
325
326 m_at--;
327 if (m_at_buffer) {//we already have got a pointer to buffer
328 drv_bufferMovePointerPrev(); //just move to prev record in the buffer
329 } else {//we have no pointer
330 //compute a place in the buffer that contain next record's data
332 m_at_buffer = true; //now current record is stored in the buffer
333 }
334 m_validRecord=true;
335 m_afterLast=false;
336 return true;
337}
338
339bool Cursor::eof() const
340{
341 return m_afterLast;
342}
343
344bool Cursor::bof() const
345{
346 return m_at==0;
347}
348
349TQ_LLONG Cursor::at() const
350{
351 if (m_readAhead)
352 return 0;
353 return m_at - 1;
354}
355
357{
358 return m_options & Buffered;
359}
360
361void Cursor::setBuffered(bool buffered)
362{
363 if (!m_opened)
364 return;
365 if (isBuffered()==buffered)
366 return;
367 m_options ^= Buffered;
368}
369
370void Cursor::clearBuffer()
371{
372 if ( !isBuffered() || m_fieldCount==0)
373 return;
374
376
378 m_at_buffer=false;
379}
380
382{
383 m_result = -1; //by default: invalid result of row fetching
384
385 if ((m_options & Buffered)) {//this cursor is buffered:
386// KexiDBDbg << "m_at < m_records_in_buf :: " << (long)m_at << " < " << m_records_in_buf << endl;
387//js if (m_at==-1) m_at=0;
388 if (m_at < m_records_in_buf) {//we have next record already buffered:
390//js if (m_at_buffer && (m_at!=0)) {//we already have got a pointer to buffer
391 if (m_at_buffer) {//we already have got a pointer to buffer
392 drv_bufferMovePointerNext(); //just move to next record in the buffer
393 } else {//we have no pointer
394 //compute a place in the buffer that contain next record's data
395 drv_bufferMovePointerTo(m_at-1+1);
396// drv_bufferMovePointerTo(m_at+1);
397 m_at_buffer = true; //now current record is stored in the buffer
398 }
399 }
400 else {//we are after last retrieved record: we need to physically fetch next record:
401 if (!m_readAhead) {//we have no record that was read ahead
403 //retrieve record only if we are not after
404 //the last buffer's item (i.e. when buffer is not fully filled):
405// KexiDBDbg<<"==== buffering: drv_getNextRecord() ===="<<endl;
406 drv_getNextRecord();
407 }
408 if ((FetchResult) m_result != FetchOK) {//there is no record
409 m_buffering_completed = true; //no more records for buffer
410// KexiDBDbg<<"m_result != FetchOK ********"<<endl;
411 m_validRecord = false;
412 m_afterLast = true;
413//js m_at = m_records_in_buf;
414 m_at = -1; //position is invalid now and will not be used
415 if ((FetchResult) m_result == FetchEnd) {
416 return false;
417 }
418 setError(ERR_CURSOR_RECORD_FETCHING, i18n("Cannot fetch next record."));
419 return false;
420 }
421 //we have a record: store this record's values in the buffer
424 }
425 else //we have a record that was read ahead: eat this
426 m_readAhead = false;
427 }
428 }
429 else {//we are after last retrieved record: we need to physically fetch next record:
430 if (!m_readAhead) {//we have no record that was read ahead
431// KexiDBDbg<<"==== no prefetched record ===="<<endl;
432 drv_getNextRecord();
433 if ((FetchResult)m_result != FetchOK) {//there is no record
434// KexiDBDbg<<"m_result != FetchOK ********"<<endl;
435 m_validRecord = false;
436 m_afterLast = true;
437 m_at = -1;
438 if ((FetchResult) m_result == FetchEnd) {
439 return false;
440 }
441 setError(ERR_CURSOR_RECORD_FETCHING, i18n("Cannot fetch next record."));
442 return false;
443 }
444 }
445 else //we have a record that was read ahead: eat this
446 m_readAhead = false;
447 }
448
449 m_at++;
450
451// if (m_data->curr_colname && m_data->curr_coldata)
452// for (int i=0;i<m_data->curr_cols;i++) {
453// KexiDBDbg<<i<<": "<< m_data->curr_colname[i]<<" == "<< m_data->curr_coldata[i]<<endl;
454// }
455// KexiDBDbg<<"m_at == "<<(long)m_at<<endl;
456
457 m_validRecord = true;
458 return true;
459}
460
461bool Cursor::updateRow(RowData& data, RowEditBuffer& buf, bool useROWID)
462{
464 clearError();
465 if (!m_query)
466 return false;
467 return m_conn->updateRow(*m_query, data, buf, useROWID);
468}
469
470bool Cursor::insertRow(RowData& data, RowEditBuffer& buf, bool getROWID)
471{
473 clearError();
474 if (!m_query)
475 return false;
476 return m_conn->insertRow(*m_query, data, buf, getROWID);
477}
478
479bool Cursor::deleteRow(RowData& data, bool useROWID)
480{
482 clearError();
483 if (!m_query)
484 return false;
485 return m_conn->deleteRow(*m_query, data, useROWID);
486}
487
489{
491 clearError();
492 if (!m_query)
493 return false;
494 return m_conn->deleteAllRows(*m_query);
495}
496
497TQString Cursor::debugString() const
498{
499 TQString dbg = "CURSOR( ";
500 if (!m_query) {
501 dbg += "RAW STATEMENT: '";
502 dbg += m_rawStatement;
503 dbg += "'\n";
504 }
505 else {
506 dbg += "QuerySchema: '";
507 dbg += m_conn->selectStatement( *m_query );
508 dbg += "'\n";
509 }
510 if (isOpened())
511 dbg += " OPENED";
512 else
513 dbg += " NOT_OPENED";
514 if (isBuffered())
515 dbg += " BUFFERED";
516 else
517 dbg += " NOT_BUFFERED";
518 dbg += " AT=";
519 dbg += TQString::number((unsigned long)at());
520 dbg += " )";
521 return dbg;
522}
523
524void Cursor::debug() const
525{
526 KexiDBDbg << debugString() << endl;
527}
528
529void Cursor::setOrderByColumnList(const TQStringList& columnNames)
530{
531 Q_UNUSED(columnNames);
533// all field names should be fooun, exit otherwise ..........
534
535 // OK
536//TODO if (!m_orderByColumnList)
537//TODO
538}
539
541void Cursor::setOrderByColumnList(const TQString& column1, const TQString& column2,
542 const TQString& column3, const TQString& column4, const TQString& column5)
543{
544 Q_UNUSED(column1);
545 Q_UNUSED(column2);
546 Q_UNUSED(column3);
547 Q_UNUSED(column4);
548 Q_UNUSED(column5);
551}
552
553QueryColumnInfo::Vector Cursor::orderByColumnList() const
554{
555 return m_orderByColumnList ? *m_orderByColumnList: QueryColumnInfo::Vector();
556}
557
558TQValueList<TQVariant> Cursor::queryParameters() const
559{
560 return m_queryParameters ? *m_queryParameters : TQValueList<TQVariant>();
561}
562
563void Cursor::setQueryParameters(const TQValueList<TQVariant>& params)
564{
565 if (!m_queryParameters)
566 m_queryParameters = new TQValueList<TQVariant>(params);
567 else
568 *m_queryParameters = params;
569}
570
571#include "cursor.moc"
options used in selectStatement()
Definition connection.h:672
bool alsoRetrieveROWID
True if ROWID should be also retrieved. False by default.
Definition connection.h:681
Provides database connection, allowing queries and data modification.
Definition connection.h:62
void setQueryParameters(const TQValueList< TQVariant > &params)
Sets query parameters params for this cursor.
Definition cursor.cpp:563
virtual bool moveLast()
Definition cursor.cpp:254
bool insertRow(RowData &data, RowEditBuffer &buf, bool getROWID=false)
Definition cursor.cpp:470
bool deleteRow(RowData &data, bool useROWID=false)
Definition cursor.cpp:479
int m_records_in_buf
number of records currently stored in the buffer
Definition cursor.h:340
virtual bool movePrev()
Definition cursor.cpp:303
virtual bool moveNext()
Definition cursor.cpp:292
virtual void drv_clearBuffer()
Definition cursor.h:313
bool isOpened() const
Definition cursor.h:121
TQValueList< TQVariant > queryParameters() const
Definition cursor.cpp:558
bool deleteAllRows()
Definition cursor.cpp:488
QueryColumnInfo::Vector orderByColumnList() const
Definition cursor.cpp:553
char m_result
result of a row fetching
Definition cursor.h:337
bool isBuffered() const
Definition cursor.cpp:356
TQ_LLONG at() const
Definition cursor.cpp:349
FetchResult
possible results of row fetching, used for m_result
Definition cursor.h:257
TQString debugString() const
Definition cursor.cpp:497
bool updateRow(RowData &data, RowEditBuffer &buf, bool useROWID=false)
Definition cursor.cpp:461
virtual bool close()
Definition cursor.cpp:178
Cursor(Connection *conn, const TQString &statement, uint options=NoOptions)
Definition cursor.cpp:40
QueryColumnInfo::Vector * m_fieldsExpanded
Useful e.g. for value(int) method when we need access to schema def.
Definition cursor.h:345
bool bof() const
Definition cursor.cpp:344
virtual void drv_appendCurrentRecordToBuffer()=0
virtual void drv_bufferMovePointerTo(TQ_LLONG at)=0
void setBuffered(bool buffered)
Definition cursor.cpp:361
void setOrderByColumnList(const TQStringList &columnNames)
Definition cursor.cpp:529
void debug() const
Outputs debug information.
Definition cursor.cpp:524
uint m_fieldCount
cached field count information
Definition cursor.h:334
QueryColumnInfo::Vector * m_orderByColumnList
Used by setOrderByColumnList()
Definition cursor.h:348
bool eof() const
Definition cursor.cpp:339
bool moveFirst()
Definition cursor.cpp:205
uint m_logicalFieldCount
logical field count, i.e. without intrernal values like ROWID or lookup
Definition cursor.h:335
virtual void drv_bufferMovePointerNext()=0
virtual void drv_bufferMovePointerPrev()=0
bool getNextRecord()
Definition cursor.cpp:381
uint options() const
Definition cursor.h:118
bool reopen()
Definition cursor.cpp:198
uint m_options
cursor options that describes its behaviour
Definition cursor.h:336
bool m_validRecord
true if valid record is currently retrieved @ current position
Definition cursor.h:331
QuerySchema * query() const
Definition cursor.h:104
bool m_buffering_completed
true if we already have all records stored in the buffer
Definition cursor.h:341
bool error() const
Definition object.h:38
TQString m_sql
used to store of actually executed SQL statement
Definition object.h:161
virtual void clearError()
Definition object.cpp:131
virtual void setError(int code=ERR_OTHER, const TQString &msg=TQString())
Definition object.cpp:53
KexiDB::QuerySchema provides information about database query.
TableSchema * masterTable() const
virtual TQString debugString()
@ WithInternalFieldsAndRowID
Like WithInternalFields but RowID (big int type) field is appended after internal fields.
QueryColumnInfo::Vector fieldsExpanded(FieldsExpandedOptions options=Default)
QueryColumnInfo::Vector internalFields()
provides data for single edited database row KexiDB::RowEditBuffer provides data for single edited ro...
High-level database connectivity library with database backend drivers.
TQValueVector< TQVariant > RowData
structure for storing single record with type information
Definition connection.h:47
type * findParent(TQObject *o, const char *className)