From 8362bf63dea22bbf6736609b0f49c152f975eb63 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 20 Jan 2010 01:29:50 +0000 Subject: Added old abandoned KDE3 version of koffice git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kexi/kexidb/drivers/sqlite/sqlitecursor.cpp | 567 ++++++++++++++++++++++++++++ 1 file changed, 567 insertions(+) create mode 100644 kexi/kexidb/drivers/sqlite/sqlitecursor.cpp (limited to 'kexi/kexidb/drivers/sqlite/sqlitecursor.cpp') diff --git a/kexi/kexidb/drivers/sqlite/sqlitecursor.cpp b/kexi/kexidb/drivers/sqlite/sqlitecursor.cpp new file mode 100644 index 00000000..4b18b437 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitecursor.cpp @@ -0,0 +1,567 @@ +/* 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 QString& 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 + QPtrVector records;//! buffer data +//#ifdef SQLITE3 +// bool rowDataReadyToFetch : 1; +//#endif + +#ifdef SQLITE3 + inline QVariant getValue(Field *f, int i) + { + int type = sqlite3_column_type(prepared_st_handle, i); + if (type==SQLITE_NULL) { + return QVariant(); + } + else if (!f || type==SQLITE_TEXT) { +//TODO: support for UTF-16 +#define GET_sqlite3_column_text QString::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 QDate::fromString( GET_sqlite3_column_text, Qt::ISODate ); + case Field::Time: + //QDateTime - a hack needed because QVariant(QTime) has broken isNull() + return KexiUtils::stringToHackedQTime(GET_sqlite3_column_text); + case Field::DateTime: { + QString tmp( GET_sqlite3_column_text ); + tmp[10] = 'T'; //for ISODate compatibility + return QDateTime::fromString( tmp, Qt::ISODate ); + } + case Field::Boolean: + return QVariant(sqliteStringToBool(GET_sqlite3_column_text), 1); + default: + return QVariant(); //TODO + } + } + } + else if (type==SQLITE_INTEGER) { + switch (f->type()) { + case Field::Byte: + case Field::ShortInteger: + case Field::Integer: + return QVariant( sqlite3_column_int(prepared_st_handle, i) ); + case Field::BigInteger: + return QVariant( (Q_LLONG)sqlite3_column_int64(prepared_st_handle, i) ); + case Field::Boolean: + return QVariant( sqlite3_column_int(prepared_st_handle, i)!=0, 1 ); + default:; + } + if (f->isFPNumericType()) //WEIRD, YEAH? + return QVariant( (double)sqlite3_column_int(prepared_st_handle, i) ); + else + return QVariant(); //TODO + } + else if (type==SQLITE_FLOAT) { + if (f && f->isFPNumericType()) + return QVariant( sqlite3_column_double(prepared_st_handle, i) ); + else if (!f || f->isIntegerType()) + return QVariant( (double)sqlite3_column_double(prepared_st_handle, i) ); + else + return QVariant(); //TODO + } + else if (type==SQLITE_BLOB) { + if (f && f->type()==Field::BLOB) { + QByteArray 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 QVariant(); //TODO + } + return QVariant(); + } +#endif //SQLITE3 +}; + +SQLiteCursor::SQLiteCursor(Connection* conn, const QString& 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] ? QString::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(Q_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 = QMIN(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] = QVariant(); + else if (f && f->isTextType()) +# ifdef SQLITE_UTF8 + data[i] = QString::fromUtf8( *col ); +# else + data[i] = QVariant( *col ); //only latin1 +# endif + else if (f && f->isFPNumericType()) + data[i] = QVariant( QCString(*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] = QVariant( QCString(*col).toInt() ); + case Field::BigInteger: + data[i] = QVariant( QString::fromLatin1(*col).toLongLong() ); + case Field::Boolean: + data[i] = QVariant( sqliteStringToBool(QString::fromLatin1(*col)), 1 ); + break; + case Field::Date: + data[i] = QDate::fromString( QString::fromLatin1(*col), Qt::ISODate ); + break; + case Field::Time: + //QDateTime - a hack needed because QVariant(QTime) has broken isNull() + data[i] = KexiUtils::stringToHackedQTime(QString::fromLatin1(*col)); + break; + case Field::DateTime: { + QString tmp( QString::fromLatin1(*col) ); + tmp[10] = 'T'; + data[i] = QDateTime::fromString( tmp, Qt::ISODate ); + break; + } + default: + data[i] = QVariant( *col ); + } + } + + col++; +#else //SQLITE3 + data[i] = d->getValue(f, i); //, !f /*!f means ROWID*/); +#endif + } +} + +QVariant SQLiteCursor::value(uint i) +{ +// if (i > (m_fieldCount-1+(m_containsROWIDInfo?1:0))) //range checking + if (i > (m_fieldCount-1)) //range checking + return QVariant(); +//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 QVariant( QCString(d->curr_coldata[i]).toInt() ); + else if (!f || f->isTextType()) + return QVariant( d->curr_coldata[i] ); + else if (f->isFPNumericType()) + return QVariant( QCString(d->curr_coldata[i]).toDouble() ); + + return QVariant( 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, QString &str) +{ + if (i > (m_fieldCount-1)) //range checking + return false; + str = d->curr_coldata[i]; + return true; +}*/ + +int SQLiteCursor::serverResult() +{ + return d->res; +} + +QString SQLiteCursor::serverResultName() +{ +#ifdef SQLITE2 + return QString::fromLatin1( sqlite_error_string(d->res) ); +#else //SQLITE3 + return QString::fromLatin1( d->result_name ); +#endif +} + +QString SQLiteCursor::serverErrorMsg() +{ + return d->errmsg; +} + +void SQLiteCursor::drv_clearServerResult() +{ + d->res = SQLITE_OK; + d->errmsg_p = 0; +} + -- cgit v1.2.1