kexi

sqlite/sqlitecursor.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 "sqlitecursor.h"
21
22#include "sqliteconnection.h"
23#include "sqliteconnection_p.h"
24
25#include <kexidb/error.h>
26#include <kexidb/driver.h>
27#include <kexiutils/utils.h>
28
29#include <assert.h>
30#include <string.h>
31#include <stdlib.h>
32
33#include <kdebug.h>
34#include <tdelocale.h>
35
36#include <tqptrvector.h>
37#include <tqdatetime.h>
38
39using namespace KexiDB;
40
42static bool sqliteStringToBool(const TQString& s)
43{
44 return s.lower()=="yes" || (s.lower()!="no" && s!="0");
45}
46
47//----------------------------------------------------
48
49class KexiDB::SQLiteCursorData : public SQLiteConnectionInternal
50{
51 public:
52 SQLiteCursorData(Connection* conn)
53 :
54 SQLiteConnectionInternal(conn)
55// : curr_cols(0)
56// errmsg_p(0)
57// , res(SQLITE_OK)
58 , curr_coldata(0)
59 , curr_colname(0)
60 , cols_pointers_mem_size(0)
61// , rec_stored(false)
62/* MOVED TO Cursor:
63 , cols_pointers_mem_size(0)
64 , records_in_buf(0)
65 , buffering_completed(false)
66 , at_buffer(false)*/
67//#ifdef SQLITE3
68// , rowDataReadyToFetch(false)
69//#endif
70 {
71 data_owned = false;
72 }
73
74/*#ifdef SQLITE3
75 void fetchRowDataIfNeeded()
76 {
77 if (!rowDataReadyToFetch)
78 return true;
79 rowDataReadyToFetch = false;
80 m_fieldCount = sqlite3_data_count(data);
81 for (int i=0; i<m_fieldCount; i++) {
82
83 }
84 }
85#endif*/
86
87 TQCString st;
88 //for sqlite:
89// sqlite_struct *data; //! taken from SQLiteConnection
90#ifdef SQLITE2
91 sqlite_vm *prepared_st_handle; //vm
92#else //SQLITE3
93 sqlite3_stmt *prepared_st_handle;
94#endif
95
96 char *utail;
97
98// TQString errmsg; //<! server-specific message of last operation
99// char *errmsg_p; //<! temporary: server-specific message of last operation
100// int res; //<! result code of last operation on server
101
102// int curr_cols;
103 const char **curr_coldata;
104 const char **curr_colname;
105
106 int next_cols;
107// const char **next_coldata;
108// const char **next_colname;
109// bool rec_stored : 1; //! true, current record is stored in next_coldata
110
111/* MOVED TO Cursor:
112 uint cols_pointers_mem_size; //! size of record's array of pointers to values
113 int records_in_buf; //! number of records currently stored in the buffer
114 bool buffering_completed; //! true if we have already all records stored in the buffer
115 TQPtrVector<const char*> records; //buffer data
116 bool at_buffer; //! true if we already point to the buffer with curr_coldata
117*/
118
119/* int prev_cols;
120 const char **prev_coldata;
121 const char **prev_colname;*/
122
123 uint cols_pointers_mem_size;
124 TQPtrVector<const char*> records;
125//#ifdef SQLITE3
126// bool rowDataReadyToFetch : 1;
127//#endif
128
129#ifdef SQLITE3
130 inline TQVariant getValue(Field *f, int i)
131 {
132 int type = sqlite3_column_type(prepared_st_handle, i);
133 if (type==SQLITE_NULL) {
134 return TQVariant();
135 }
136 else if (!f || type==SQLITE_TEXT) {
137//TODO: support for UTF-16
138#define GET_sqlite3_column_text TQString::fromUtf8( (const char*)sqlite3_column_text(prepared_st_handle, i) )
139 if (!f || f->isTextType())
140 return GET_sqlite3_column_text;
141 else {
142 switch (f->type()) {
143 case Field::Date:
144 return TQDate::fromString( GET_sqlite3_column_text, TQt::ISODate );
145 case Field::Time:
146 //TQDateTime - a hack needed because TQVariant(TQTime) has broken isNull()
147 return KexiUtils::stringToHackedTQTime(GET_sqlite3_column_text);
148 case Field::DateTime: {
149 TQString tmp( GET_sqlite3_column_text );
150 tmp[10] = 'T'; //for ISODate compatibility
151 return TQDateTime::fromString( tmp, TQt::ISODate );
152 }
153 case Field::Boolean:
154 return TQVariant(sqliteStringToBool(GET_sqlite3_column_text));
155 default:
156 return TQVariant(); //TODO
157 }
158 }
159 }
160 else if (type==SQLITE_INTEGER) {
161 switch (f->type()) {
162 case Field::Byte:
164 case Field::Integer:
165 return TQVariant( sqlite3_column_int(prepared_st_handle, i) );
167 return TQVariant( (TQ_LLONG)sqlite3_column_int64(prepared_st_handle, i) );
168 case Field::Boolean:
169 return TQVariant( sqlite3_column_int(prepared_st_handle, i)!=0 );
170 default:;
171 }
172 if (f->isFPNumericType()) //WEIRD, YEAH?
173 return TQVariant( (double)sqlite3_column_int(prepared_st_handle, i) );
174 else
175 return TQVariant(); //TODO
176 }
177 else if (type==SQLITE_FLOAT) {
178 if (f && f->isFPNumericType())
179 return TQVariant( sqlite3_column_double(prepared_st_handle, i) );
180 else if (!f || f->isIntegerType())
181 return TQVariant( (double)sqlite3_column_double(prepared_st_handle, i) );
182 else
183 return TQVariant(); //TODO
184 }
185 else if (type==SQLITE_BLOB) {
186 if (f && f->type()==Field::BLOB) {
187 TQByteArray ba;
189 ba.duplicate((const char*)sqlite3_column_blob(prepared_st_handle, i),
190 sqlite3_column_bytes(prepared_st_handle, i));
191 return ba;
192 } else
193 return TQVariant(); //TODO
194 }
195 return TQVariant();
196 }
197#endif //SQLITE3
198};
199
200SQLiteCursor::SQLiteCursor(Connection* conn, const TQString& statement, uint options)
201 : Cursor( conn, statement, options )
202 , d( new SQLiteCursorData(conn) )
203{
204 d->data = static_cast<SQLiteConnection*>(conn)->d->data;
205}
206
207SQLiteCursor::SQLiteCursor(Connection* conn, QuerySchema& query, uint options )
208 : Cursor( conn, query, options )
209 , d( new SQLiteCursorData(conn) )
210{
211 d->data = static_cast<SQLiteConnection*>(conn)->d->data;
212}
213
214SQLiteCursor::~SQLiteCursor()
215{
216 close();
217 delete d;
218}
219
220bool SQLiteCursor::drv_open()
221{
222// d->st.resize(statement.length()*2);
223 //TODO: decode
224// d->st = statement.local8Bit();
225// d->st = m_conn->driver()->escapeString( statement.local8Bit() );
226
227 if(! d->data) {
228 // this may as example be the case if SQLiteConnection::drv_useDatabase()
229 // wasn't called before. Normaly sqlite_compile/sqlite3_prepare
230 // should handle it, but it crashes in in sqlite3SafetyOn at util.c:786
231 kdWarning() << "SQLiteCursor::drv_open(): Database handle undefined." << endl;
232 return false;
233 }
234
235#ifdef SQLITE2
236 d->st = m_sql.local8Bit();
237 d->res = sqlite_compile(
238 d->data,
239 d->st.data(),
240 (const char**)&d->utail,
241 &d->prepared_st_handle,
242 &d->errmsg_p );
243#else //SQLITE3
244 d->st = m_sql.utf8();
245 d->res = sqlite3_prepare(
246 d->data, /* Database handle */
247 d->st.data(), /* SQL statement, UTF-8 encoded */
248 d->st.length(), /* Length of zSql in bytes. */
249 &d->prepared_st_handle, /* OUT: Statement handle */
250 0/*const char **pzTail*/ /* OUT: Pointer to unused portion of zSql */
251 );
252#endif
253 if (d->res!=SQLITE_OK) {
254 d->storeResult();
255 return false;
256 }
257//cursor is automatically @ first record
258// m_beforeFirst = true;
259
260 if (isBuffered()) {
261 d->records.resize(128); //TODO: manage size dynamically
262 }
263
264 return true;
265}
266
267/*bool SQLiteCursor::drv_getFirstRecord()
268{
269 bool ok = drv_getNextRecord();*/
270/* if ((m_options & Buffered) && ok) { //1st record is there:
271 //compute parameters for cursor's buffer:
272 //-size of record's array of pointer to values
273 d->cols_pointers_mem_size = d->curr_cols * sizeof(char*);
274 d->records_in_buf = 1;
275 }*/
276 /*return ok;
277}*/
278
279bool SQLiteCursor::drv_close()
280{
281#ifdef SQLITE2
282 d->res = sqlite_finalize( d->prepared_st_handle, &d->errmsg_p );
283#else //SQLITE3
284 d->res = sqlite3_finalize( d->prepared_st_handle );
285#endif
286 if (d->res!=SQLITE_OK) {
287 d->storeResult();
288 return false;
289 }
290 return true;
291}
292
293void SQLiteCursor::drv_getNextRecord()
294{
295#ifdef SQLITE2
296 static int _fieldCount;
297 d->res = sqlite_step(
298 d->prepared_st_handle,
299 &_fieldCount,
300 &d->curr_coldata,
301 &d->curr_colname);
302#else //SQLITE3
303 d->res = sqlite3_step( d->prepared_st_handle );
304#endif
305 if (d->res == SQLITE_ROW) {
306 m_result = FetchOK;
307#ifdef SQLITE2
308 m_fieldCount = (uint)_fieldCount;
309#else
310 m_fieldCount = sqlite3_data_count(d->prepared_st_handle);
311//#else //for SQLITE3 data fetching is delayed. Now we even do not take field count information
312// // -- just set a flag that we've a data not fetched but available
313// d->rowDataReadyToFetch = true;
314#endif
315 //(m_logicalFieldCount introduced) m_fieldCount -= (m_containsROWIDInfo ? 1 : 0);
316 } else {
317//#ifdef SQLITE3
318// d->rowDataReadyToFetch = false;
319//#endif
320 if (d->res==SQLITE_DONE)
321 m_result = FetchEnd;
322 else
323 m_result = FetchError;
324 }
325
326 //debug
327/* if (((int)m_result == (int)FetchOK) && d->curr_coldata) {
328 for (uint i=0;i<m_fieldCount;i++) {
329 KexiDBDrvDbg<<"col."<< i<<": "<< d->curr_colname[i]<<" "<< d->curr_colname[m_fieldCount+i]
330 << " = " << (d->curr_coldata[i] ? TQString::fromLocal8Bit(d->curr_coldata[i]) : "(NULL)") <<endl;
331 }
332 KexiDBDrvDbg << "SQLiteCursor::drv_getNextRecord(): "<<m_fieldCount<<" col(s) fetched"<<endl;
333 }*/
334}
335
337{
338// KexiDBDrvDbg << "SQLiteCursor::drv_appendCurrentRecordToBuffer():" <<endl;
339 if (!d->curr_coldata)
340 return;
341
342 if (!d->cols_pointers_mem_size)
343 d->cols_pointers_mem_size = m_fieldCount * sizeof(char*);
344 const char **record = (const char**)malloc(d->cols_pointers_mem_size);
345 const char **src_col = d->curr_coldata;
346 const char **dest_col = record;
347 for (uint i=0; i<m_fieldCount; i++,src_col++,dest_col++) {
348// KexiDBDrvDbg << i <<": '" << *src_col << "'" <<endl;
349// KexiDBDrvDbg << "src_col: " << src_col << endl;
350 *dest_col = *src_col ? strdup(*src_col) : 0;
351 }
352 d->records.insert(m_records_in_buf,record);
353// KexiDBDrvDbg << "SQLiteCursor::drv_appendCurrentRecordToBuffer() ok." <<endl;
354}
355
357{
358 d->curr_coldata++; //move to next record in the buffer
359}
360
362{
363 d->curr_coldata--; //move to prev record in the buffer
364}
365
366//compute a place in the buffer that contain next record's data
367//and move internal buffer pointer to that place
368void SQLiteCursor::drv_bufferMovePointerTo(TQ_LLONG at)
369{
370 d->curr_coldata = d->records.at(at);
371}
372
374{
375 if (d->cols_pointers_mem_size>0) {
376 const uint records_in_buf = m_records_in_buf;
377 const char ***r_ptr = d->records.data();
378 for (uint i=0; i<records_in_buf; i++, r_ptr++) {
379 // const char **record = m_records.at(i);
380 const char **field_data = *r_ptr;
381 // for (int col=0; col<d->curr_cols; col++, field_data++) {
382 for (uint col=0; col<m_fieldCount; col++, field_data++) {
383 free((void*)*field_data); //free field memory
384 }
385 free(*r_ptr); //free pointers to fields array
386 }
387 }
388// d->curr_cols=0;
389// m_fieldCount=0;
391 d->cols_pointers_mem_size=0;
392// m_at_buffer=false;
393 d->records.clear();
394}
395
396/*
397void SQLiteCursor::drv_storeCurrentRecord()
398{
399#if 0
400 assert(!m_data->rec_stored);
401 m_data->rec_stored = true;
402 m_data->next_cols = m_data->curr_cols;
403 for (int i=0;i<m_data->curr_cols;i++) {
404 KexiDBDrvDbg<<"[COPY] "<<i<<": "<< m_data->curr_coldata[i]<<endl;
405 if (m_data->curr_coldata[i])
406 m_data->next_coldata[i] = strdup( m_data->curr_coldata[i] );
407 else
408 m_data->next_coldata[i] = 0;
409 }
410#endif
411}
412*/
413
414/*TODO
415const char *** SQLiteCursor::bufferData()
416{
417 if (!isBuffered())
418 return 0;
419 return m_records.data();
420}*/
421
422const char ** SQLiteCursor::rowData() const
423{
424 return d->curr_coldata;
425}
426
427void SQLiteCursor::storeCurrentRow(RowData &data) const
428{
429#ifdef SQLITE2
430 const char **col = d->curr_coldata;
431#endif
432 //const uint realCount = m_fieldCount + (m_containsROWIDInfo ? 1 : 0);
433 data.resize(m_fieldCount);
434 if (!m_fieldsExpanded) {//simple version: without types
435 for( uint i=0; i<m_fieldCount; i++ ) {
436#ifdef SQLITE2
437 data[i] = TQVariant( *col );
438 col++;
439#else //SQLITE3
440 data[i] = TQString::fromUtf8( (const char*)sqlite3_column_text(d->prepared_st_handle, i) );
441#endif
442 }
443 return;
444 }
445
446 //const uint fieldsExpandedCount = m_fieldsExpanded->count();
447 const uint maxCount = TQMIN(m_fieldCount, m_fieldsExpanded->count());
448 // i - visible field's index, j - physical index
449 for( uint i=0, j=0; i<m_fieldCount; i++, j++ ) {
450// while (j < m_detailedVisibility.count() && !m_detailedVisibility[j]) //!m_query->isColumnVisible(j))
451// j++;
452 while (j < maxCount && !m_fieldsExpanded->at(j)->visible)
453 j++;
454 if (j >= (maxCount /*+(m_containsROWIDInfo ? 1 : 0)*/)) {
455 //ERR!
456 break;
457 }
458 //(m_logicalFieldCount introduced) Field *f = (m_containsROWIDInfo && i>=m_fieldCount) ? 0 : m_fieldsExpanded->at(j)->field;
459 Field *f = (i>=m_fieldCount) ? 0 : m_fieldsExpanded->at(j)->field;
460// KexiDBDrvDbg << "SQLiteCursor::storeCurrentRow(): col=" << (col ? *col : 0) << endl;
461
462#ifdef SQLITE2
463 if (!*col)
464 data[i] = TQVariant();
465 else if (f && f->isTextType())
466# ifdef SQLITE_UTF8
467 data[i] = TQString::fromUtf8( *col );
468# else
469 data[i] = TQVariant( *col ); //only latin1
470# endif
471 else if (f && f->isFPNumericType())
472 data[i] = TQVariant( TQCString(*col).toDouble() );
473 else {
474 switch (f ? f->type() : Field::Integer/*ROWINFO*/) {
475//todo: use short, etc.
476 case Field::Byte:
477 case Field::ShortInteger:
478 case Field::Integer:
479 data[i] = TQVariant( TQCString(*col).toInt() );
480 case Field::BigInteger:
481 data[i] = TQVariant( TQString::fromLatin1(*col).toLongLong() );
482 case Field::Boolean:
483 data[i] = TQVariant( sqliteStringToBool(TQString::fromLatin1(*col)) );
484 break;
485 case Field::Date:
486 data[i] = TQDate::fromString( TQString::fromLatin1(*col), TQt::ISODate );
487 break;
488 case Field::Time:
489 //TQDateTime - a hack needed because TQVariant(TQTime) has broken isNull()
490 data[i] = KexiUtils::stringToHackedTQTime(TQString::fromLatin1(*col));
491 break;
492 case Field::DateTime: {
493 TQString tmp( TQString::fromLatin1(*col) );
494 tmp[10] = 'T';
495 data[i] = TQDateTime::fromString( tmp, TQt::ISODate );
496 break;
497 }
498 default:
499 data[i] = TQVariant( *col );
500 }
501 }
502
503 col++;
504#else //SQLITE3
505 data[i] = d->getValue(f, i); //, !f /*!f means ROWID*/);
506#endif
507 }
508}
509
510TQVariant SQLiteCursor::value(uint i)
511{
512// if (i > (m_fieldCount-1+(m_containsROWIDInfo?1:0))) //range checking
513 if (i > (m_fieldCount-1)) //range checking
514 return TQVariant();
515//TODO: allow disable range checking! - performance reasons
516// const KexiDB::Field *f = m_query ? m_query->field(i) : 0;
517 KexiDB::Field *f = (m_fieldsExpanded && i<m_fieldsExpanded->count())
518 ? m_fieldsExpanded->at(i)->field : 0;
519#ifdef SQLITE2
520 //from most to least frequently used types:
521//(m_logicalFieldCount introduced) if (i==m_fieldCount || f && f->isIntegerType())
522 if (!f || f->isIntegerType())
523 return TQVariant( TQCString(d->curr_coldata[i]).toInt() );
524 else if (!f || f->isTextType())
525 return TQVariant( d->curr_coldata[i] );
526 else if (f->isFPNumericType())
527 return TQVariant( TQCString(d->curr_coldata[i]).toDouble() );
528
529 return TQVariant( d->curr_coldata[i] ); //default
530#else
531 return d->getValue(f, i); //, i==m_logicalFieldCount/*ROWID*/);
532#endif
533}
534
544
546{
547 return d->res;
548}
549
551{
552#ifdef SQLITE2
553 return TQString::fromLatin1( sqlite_error_string(d->res) );
554#else //SQLITE3
555 return TQString::fromLatin1( d->result_name );
556#endif
557}
558
560{
561 return d->errmsg;
562}
563
565{
566 d->res = SQLITE_OK;
567 d->errmsg_p = 0;
568}
569
Provides database connection, allowing queries and data modification.
Definition connection.h:62
Provides database cursor functionality.
Definition cursor.h:67
int m_records_in_buf
number of records currently stored in the buffer
Definition cursor.h:340
virtual void drv_clearBuffer()
Definition cursor.h:313
virtual TQString serverResultName()
Definition cursor.h:240
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
virtual bool close()
Definition cursor.cpp:178
QueryColumnInfo::Vector * m_fieldsExpanded
Useful e.g. for value(int) method when we need access to schema def.
Definition cursor.h:345
virtual void drv_appendCurrentRecordToBuffer()=0
virtual int serverResult()
Definition cursor.h:233
uint m_fieldCount
cached field count information
Definition cursor.h:334
virtual void drv_bufferMovePointerNext()=0
virtual void drv_bufferMovePointerPrev()=0
virtual const char ** rowData() const =0
virtual TQString serverErrorMsg()
Definition cursor.h:247
virtual void drv_clearServerResult()=0
Type type() const
Definition field.cpp:134
@ ShortInteger
Definition field.h:86
bool isFPNumericType() const
Definition field.h:266
bool isIntegerType() const
Definition field.h:259
bool isTextType() const
Definition field.h:280
TQString m_sql
used to store of actually executed SQL statement
Definition object.h:161
KexiDB::QuerySchema provides information about database query.
sqlite-specific connection
High-level database connectivity library with database backend drivers.
TQValueVector< TQVariant > RowData
structure for storing single record with type information
Definition connection.h:47
TQDateTime stringToHackedTQTime(const TQString &s)
TQDateTime - a hack needed because TQVariant(TQTime) has broken isNull().