/* This file is part of the KDE project Copyright (C) 2003-2006 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "sqlitecursor.h" #include "sqliteconnection.h" #include "sqliteconnection_p.h" #include #include #include #include #include #include #include #include #include #include using namespace KexiDB; //! safer interpretations of boolean values for SQLite static bool sqliteStringToBool(const TQString& s) { return s.lower()=="yes" || (s.lower()!="no" && s!="0"); } //---------------------------------------------------- class KexiDB::SQLiteCursorData : public SQLiteConnectionInternal { public: SQLiteCursorData(Connection* conn) : SQLiteConnectionInternal(conn) // : curr_cols(0) // errmsg_p(0) // , res(SQLITE_OK) , curr_coldata(0) , curr_colname(0) , cols_pointers_mem_size(0) // , rec_stored(false) /* MOVED TO Cursor: , cols_pointers_mem_size(0) , records_in_buf(0) , buffering_completed(false) , at_buffer(false)*/ //#ifdef SQLITE3 // , rowDataReadyToFetch(false) //#endif { data_owned = false; } /*#ifdef SQLITE3 void fetchRowDataIfNeeded() { if (!rowDataReadyToFetch) return true; rowDataReadyToFetch = false; m_fieldCount = sqlite3_data_count(data); for (int i=0; i records; //buffer data bool at_buffer; //! true if we already point to the buffer with curr_coldata */ /* int prev_cols; const char **prev_coldata; const char **prev_colname;*/ uint cols_pointers_mem_size; //! size of record's array of pointers to values TQPtrVector records;//! buffer data //#ifdef SQLITE3 // bool rowDataReadyToFetch : 1; //#endif #ifdef SQLITE3 inline TQVariant getValue(Field *f, int i) { int type = sqlite3_column_type(prepared_st_handle, i); if (type==SQLITE_NULL) { return TQVariant(); } else if (!f || type==SQLITE_TEXT) { //TODO: support for UTF-16 #define GET_sqlite3_column_text TQString::fromUtf8( (const char*)sqlite3_column_text(prepared_st_handle, i) ) if (!f || f->isTextType()) return GET_sqlite3_column_text; else { switch (f->type()) { case Field::Date: return TQDate::fromString( GET_sqlite3_column_text, Qt::ISODate ); case Field::Time: //TQDateTime - a hack needed because TQVariant(TQTime) has broken isNull() return KexiUtils::stringToHackedTQTime(GET_sqlite3_column_text); case Field::DateTime: { TQString tmp( GET_sqlite3_column_text ); tmp[10] = 'T'; //for ISODate compatibility return TQDateTime::fromString( tmp, Qt::ISODate ); } case Field::Boolean: return TQVariant(sqliteStringToBool(GET_sqlite3_column_text), 1); default: return TQVariant(); //TODO } } } else if (type==SQLITE_INTEGER) { switch (f->type()) { case Field::Byte: case Field::ShortInteger: case Field::Integer: return TQVariant( sqlite3_column_int(prepared_st_handle, i) ); case Field::BigInteger: return TQVariant( (TQ_LLONG)sqlite3_column_int64(prepared_st_handle, i) ); case Field::Boolean: return TQVariant( sqlite3_column_int(prepared_st_handle, i)!=0, 1 ); default:; } if (f->isFPNumericType()) //WEIRD, YEAH? return TQVariant( (double)sqlite3_column_int(prepared_st_handle, i) ); else return TQVariant(); //TODO } else if (type==SQLITE_FLOAT) { if (f && f->isFPNumericType()) return TQVariant( sqlite3_column_double(prepared_st_handle, i) ); else if (!f || f->isIntegerType()) return TQVariant( (double)sqlite3_column_double(prepared_st_handle, i) ); else return TQVariant(); //TODO } else if (type==SQLITE_BLOB) { if (f && f->type()==Field::BLOB) { TQByteArray ba; //! @todo efficient enough? ba.duplicate((const char*)sqlite3_column_blob(prepared_st_handle, i), sqlite3_column_bytes(prepared_st_handle, i)); return ba; } else return TQVariant(); //TODO } return TQVariant(); } #endif //SQLITE3 }; SQLiteCursor::SQLiteCursor(Connection* conn, const TQString& statement, uint options) : Cursor( conn, statement, options ) , d( new SQLiteCursorData(conn) ) { d->data = static_cast(conn)->d->data; } SQLiteCursor::SQLiteCursor(Connection* conn, QuerySchema& query, uint options ) : Cursor( conn, query, options ) , d( new SQLiteCursorData(conn) ) { d->data = static_cast(conn)->d->data; } SQLiteCursor::~SQLiteCursor() { close(); delete d; } bool SQLiteCursor::drv_open() { // d->st.resize(statement.length()*2); //TODO: decode // d->st = statement.local8Bit(); // d->st = m_conn->driver()->escapeString( statement.local8Bit() ); if(! d->data) { // this may as example be the case if SQLiteConnection::drv_useDatabase() // wasn't called before. Normaly sqlite_compile/sqlite3_prepare // should handle it, but it crashes in in sqlite3SafetyOn at util.c:786 kdWarning() << "SQLiteCursor::drv_open(): Database handle undefined." << endl; return false; } #ifdef SQLITE2 d->st = m_sql.local8Bit(); d->res = sqlite_compile( d->data, d->st.data(), (const char**)&d->utail, &d->prepared_st_handle, &d->errmsg_p ); #else //SQLITE3 d->st = m_sql.utf8(); d->res = sqlite3_prepare( d->data, /* Database handle */ d->st.data(), /* SQL statement, UTF-8 encoded */ d->st.length(), /* Length of zSql in bytes. */ &d->prepared_st_handle, /* OUT: Statement handle */ 0/*const char **pzTail*/ /* OUT: Pointer to unused portion of zSql */ ); #endif if (d->res!=SQLITE_OK) { d->storeResult(); return false; } //cursor is automatically @ first record // m_beforeFirst = true; if (isBuffered()) { d->records.resize(128); //TODO: manage size dynamically } return true; } /*bool SQLiteCursor::drv_getFirstRecord() { bool ok = drv_getNextRecord();*/ /* if ((m_options & Buffered) && ok) { //1st record is there: //compute parameters for cursor's buffer: //-size of record's array of pointer to values d->cols_pointers_mem_size = d->curr_cols * sizeof(char*); d->records_in_buf = 1; }*/ /*return ok; }*/ bool SQLiteCursor::drv_close() { #ifdef SQLITE2 d->res = sqlite_finalize( d->prepared_st_handle, &d->errmsg_p ); #else //SQLITE3 d->res = sqlite3_finalize( d->prepared_st_handle ); #endif if (d->res!=SQLITE_OK) { d->storeResult(); return false; } return true; } void SQLiteCursor::drv_getNextRecord() { #ifdef SQLITE2 static int _fieldCount; d->res = sqlite_step( d->prepared_st_handle, &_fieldCount, &d->curr_coldata, &d->curr_colname); #else //SQLITE3 d->res = sqlite3_step( d->prepared_st_handle ); #endif if (d->res == SQLITE_ROW) { m_result = FetchOK; #ifdef SQLITE2 m_fieldCount = (uint)_fieldCount; #else m_fieldCount = sqlite3_data_count(d->prepared_st_handle); //#else //for SQLITE3 data fetching is delayed. Now we even do not take field count information // // -- just set a flag that we've a data not fetched but available // d->rowDataReadyToFetch = true; #endif //(m_logicalFieldCount introduced) m_fieldCount -= (m_containsROWIDInfo ? 1 : 0); } else { //#ifdef SQLITE3 // d->rowDataReadyToFetch = false; //#endif if (d->res==SQLITE_DONE) m_result = FetchEnd; else m_result = FetchError; } //debug /* if (m_result == FetchOK && d->curr_coldata) { for (uint i=0;icurr_colname[i]<<" "<< d->curr_colname[m_fieldCount+i] << " = " << (d->curr_coldata[i] ? TQString::fromLocal8Bit(d->curr_coldata[i]) : "(NULL)") <cols_pointers_mem_size) d->cols_pointers_mem_size = m_fieldCount * sizeof(char*); const char **record = (const char**)malloc(d->cols_pointers_mem_size); const char **src_col = d->curr_coldata; const char **dest_col = record; for (uint i=0; irecords.insert(m_records_in_buf,record); // KexiDBDrvDbg << "SQLiteCursor::drv_appendCurrentRecordToBuffer() ok." <curr_coldata++; //move to next record in the buffer } void SQLiteCursor::drv_bufferMovePointerPrev() { d->curr_coldata--; //move to prev record in the buffer } //compute a place in the buffer that contain next record's data //and move internal buffer pointer to that place void SQLiteCursor::drv_bufferMovePointerTo(TQ_LLONG at) { d->curr_coldata = d->records.at(at); } void SQLiteCursor::drv_clearBuffer() { if (d->cols_pointers_mem_size>0) { const uint records_in_buf = m_records_in_buf; const char ***r_ptr = d->records.data(); for (uint i=0; icurr_cols; col++, field_data++) { for (uint col=0; colcurr_cols=0; // m_fieldCount=0; m_records_in_buf=0; d->cols_pointers_mem_size=0; // m_at_buffer=false; d->records.clear(); } /* void SQLiteCursor::drv_storeCurrentRecord() { #if 0 assert(!m_data->rec_stored); m_data->rec_stored = true; m_data->next_cols = m_data->curr_cols; for (int i=0;icurr_cols;i++) { KexiDBDrvDbg<<"[COPY] "<curr_coldata[i]<curr_coldata[i]) m_data->next_coldata[i] = strdup( m_data->curr_coldata[i] ); else m_data->next_coldata[i] = 0; } #endif } */ /*TODO const char *** SQLiteCursor::bufferData() { if (!isBuffered()) return 0; return m_records.data(); }*/ const char ** SQLiteCursor::rowData() const { return d->curr_coldata; } void SQLiteCursor::storeCurrentRow(RowData &data) const { #ifdef SQLITE2 const char **col = d->curr_coldata; #endif //const uint realCount = m_fieldCount + (m_containsROWIDInfo ? 1 : 0); data.resize(m_fieldCount); if (!m_fieldsExpanded) {//simple version: without types for( uint i=0; iprepared_st_handle, i) ); #endif } return; } //const uint fieldsExpandedCount = m_fieldsExpanded->count(); const uint maxCount = TQMIN(m_fieldCount, m_fieldsExpanded->count()); // i - visible field's index, j - physical index for( uint i=0, j=0; iisColumnVisible(j)) // j++; while (j < maxCount && !m_fieldsExpanded->at(j)->visible) j++; if (j >= (maxCount /*+(m_containsROWIDInfo ? 1 : 0)*/)) { //ERR! break; } //(m_logicalFieldCount introduced) Field *f = (m_containsROWIDInfo && i>=m_fieldCount) ? 0 : m_fieldsExpanded->at(j)->field; Field *f = (i>=m_fieldCount) ? 0 : m_fieldsExpanded->at(j)->field; // KexiDBDrvDbg << "SQLiteCursor::storeCurrentRow(): col=" << (col ? *col : 0) << endl; #ifdef SQLITE2 if (!*col) data[i] = TQVariant(); else if (f && f->isTextType()) # ifdef SQLITE_UTF8 data[i] = TQString::fromUtf8( *col ); # else data[i] = TQVariant( *col ); //only latin1 # endif else if (f && f->isFPNumericType()) data[i] = TQVariant( TQCString(*col).toDouble() ); else { switch (f ? f->type() : Field::Integer/*ROWINFO*/) { //todo: use short, etc. case Field::Byte: case Field::ShortInteger: case Field::Integer: data[i] = TQVariant( TQCString(*col).toInt() ); case Field::BigInteger: data[i] = TQVariant( TQString::tqfromLatin1(*col).toLongLong() ); case Field::Boolean: data[i] = TQVariant( sqliteStringToBool(TQString::tqfromLatin1(*col)), 1 ); break; case Field::Date: data[i] = TQDate::fromString( TQString::tqfromLatin1(*col), Qt::ISODate ); break; case Field::Time: //TQDateTime - a hack needed because TQVariant(TQTime) has broken isNull() data[i] = KexiUtils::stringToHackedTQTime(TQString::tqfromLatin1(*col)); break; case Field::DateTime: { TQString tmp( TQString::tqfromLatin1(*col) ); tmp[10] = 'T'; data[i] = TQDateTime::fromString( tmp, Qt::ISODate ); break; } default: data[i] = TQVariant( *col ); } } col++; #else //SQLITE3 data[i] = d->getValue(f, i); //, !f /*!f means ROWID*/); #endif } } TQVariant SQLiteCursor::value(uint i) { // if (i > (m_fieldCount-1+(m_containsROWIDInfo?1:0))) //range checking if (i > (m_fieldCount-1)) //range checking return TQVariant(); //TODO: allow disable range checking! - performance reasons // const KexiDB::Field *f = m_query ? m_query->field(i) : 0; KexiDB::Field *f = (m_fieldsExpanded && icount()) ? m_fieldsExpanded->at(i)->field : 0; #ifdef SQLITE2 //from most to least frequently used types: //(m_logicalFieldCount introduced) if (i==m_fieldCount || f && f->isIntegerType()) if (!f || f->isIntegerType()) return TQVariant( TQCString(d->curr_coldata[i]).toInt() ); else if (!f || f->isTextType()) return TQVariant( d->curr_coldata[i] ); else if (f->isFPNumericType()) return TQVariant( TQCString(d->curr_coldata[i]).toDouble() ); return TQVariant( d->curr_coldata[i] ); //default #else return d->getValue(f, i); //, i==m_logicalFieldCount/*ROWID*/); #endif } /*! Stores string value taken from field number \a i to \a str. \return false when range checking failed. bool SQLiteCursor::storeStringValue(uint i, TQString &str) { if (i > (m_fieldCount-1)) //range checking return false; str = d->curr_coldata[i]; return true; }*/ int SQLiteCursor::serverResult() { return d->res; } TQString SQLiteCursor::serverResultName() { #ifdef SQLITE2 return TQString::tqfromLatin1( sqlite_error_string(d->res) ); #else //SQLITE3 return TQString::tqfromLatin1( d->result_name ); #endif } TQString SQLiteCursor::serverErrorMsg() { return d->errmsg; } void SQLiteCursor::drv_clearServerResult() { d->res = SQLITE_OK; d->errmsg_p = 0; }