You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
krecipes/krecipes/src/backends/SQLite/qsql_sqlite.cpp

471 lines
10 KiB

/***************************************************************************
* Copyright (C) 2005 by *
* Jason Kivlighn (jkivlighn@gmail.com) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include "qsql_sqlite.h"
#include <ntqdatetime.h>
#include <ntqregexp.h>
#include <ntqfile.h>
#include <ntqptrvector.h>
#include <unistd.h>
#include <kdebug.h>
#include "libqsqlite/krecqsqlitedb.h"
#include "libqsqlite/krecqsqliteresult.h"
#define TQSQLITE_DRIVER_NAME "KRE_QSQLITE"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if HAVE_SQLITE3
#include <sqlite3.h>
#define sqlite_free sqlite3_free
#elif HAVE_SQLITE
#include <sqlite.h>
#endif
class KreSQLiteResult : public TQSqlResult
{
public:
KreSQLiteResult( const KreSQLiteDriver *d ): TQSqlResult( d )
{
db = d->db;
}
~KreSQLiteResult() {}
protected:
TQVariant data( int );
bool reset ( const TQString& );
bool fetch( int );
bool fetchFirst();
bool fetchNext();
bool fetchLast();
bool isNull( int );
TQSqlRecord record();
int size();
int numRowsAffected();
private:
TQSQLiteResult result;
TQSQLiteResultRow row;
TQSQLiteDB *db;
};
TQVariant KreSQLiteResult::data( int field )
{
if ( !isSelect() ) {
//tqWarning( "KreSQLiteResult::data: column %d out of range", field );
tqWarning( "KreSQLiteResult::data: not a select statement" );
return TQVariant();
}
return TQVariant(row.data(field));
}
bool KreSQLiteResult::fetch( int i )
{
kdDebug()<<"fetch_i"<<endl;
if ( isForwardOnly() ) { // fake a forward seek
if ( at() < i ) {
int x = i - at();
while ( --x && fetchNext() );
return fetchNext();
} else {
return false;
}
}
if ( at() == i )
return true;
row = result.first();
for ( int j = 0; j < i; ++j ) {
if ( result.atEnd() )
return false;
row = result.next();
}
setAt( i );
return true;
}
bool KreSQLiteResult::fetchNext()
{
row = result.next();
if ( result.atEnd() )
return false;
setAt( at() + 1 );
return true;
}
bool KreSQLiteResult::fetchFirst()
{
row = result.first();
if ( result.atEnd() )
return false;
setAt(0);
return true;
}
bool KreSQLiteResult::fetchLast()
{kdDebug()<<"fetchlast"<<endl;
if ( isForwardOnly() ) { // fake this since MySQL can't seek on forward only queries
bool success = fetchNext(); // did we move at all?
while ( fetchNext() );
return success;
}
int numRows = size();
if ( !numRows )
return false;
return fetch( numRows - 1 );
}
bool KreSQLiteResult::isNull( int i )
{
return false;
}
TQSqlRecord KreSQLiteResult::record()
{kdDebug()<<"record"<<endl;
return TQSqlRecord();
}
int KreSQLiteResult::size()
{
//kdDebug()<<"size: "<<result.size()<<endl;
return result.size();
}
int KreSQLiteResult::numRowsAffected()
{kdDebug()<<"numrowsaffected"<<endl;
return 1;/*sqlite3_changes(db)*/
}
/*
Execute \a query.
*/
bool KreSQLiteResult::reset(const TQString& query)
{
// this is where we build a query.
if (!driver())
return false;
if (!driver()-> isOpen() || driver()->isOpenError())
return false;
//cleanup
setAt( -1 );
setSelect(true);
result = db->executeQuery( query );
int res = result.getStatus();
if (res != SQLITE_OK ) {
setLastError(TQSqlError("Unable to execute statement", result.getError(), TQSqlError::Statement, res));
}
setActive(true);
return true;
}
/////////////////////////////////////////////////////////
KreSQLiteDriver::KreSQLiteDriver(TQObject * parent, const char * name)
: TQSqlDriver(parent, name ? name : TQSQLITE_DRIVER_NAME)
{
}
KreSQLiteDriver::KreSQLiteDriver(TQSQLiteDB *connection, TQObject *parent, const char *name)
: TQSqlDriver(parent, name ? name : TQSQLITE_DRIVER_NAME)
{
db = connection;
setOpen(true);
setOpenError(false);
}
KreSQLiteDriver::~KreSQLiteDriver()
{
}
bool KreSQLiteDriver::hasFeature(DriverFeature f) const
{
switch (f) {
case QuerySize:
case Transactions:
return true;
// case BLOB:
default:
return false;
}
}
/*
SQLite dbs have no user name, passwords, hosts or ports.
just file names.
*/
bool KreSQLiteDriver::open(const TQString & file, const TQString &, const TQString &, const TQString &, int)
{
if (isOpen())
close();
if (file.isEmpty())
return false;
db = new TQSQLiteDB;
if ( !db->open(TQFile::encodeName(file)) ) {
setLastError(TQSqlError("Error to open database", 0, TQSqlError::Connection));
setOpenError(true);
setOpen(false);
return false;
}
setOpen(true);
setOpenError(false);
return true;
}
void KreSQLiteDriver::close()
{
if (isOpen()) {
db->close();
delete db; db = 0;
setOpen(false);
setOpenError(false);
}
}
bool KreSQLiteDriver::ping()
{
if ( !isOpen() ) {
return FALSE;
}
// FIXME
// Implement ping if available
return TRUE;
}
TQSqlQuery KreSQLiteDriver::createQuery() const
{
return TQSqlQuery(new KreSQLiteResult(this));
}
bool KreSQLiteDriver::beginTransaction()
{
if (!isOpen() || isOpenError())
return false;
TQSQLiteResult result = db->executeQuery( "BEGIN" );
int status = result.getStatus();
if (status == TQSQLiteResult::Success)
return true;
setLastError(TQSqlError("Unable to begin transaction", result.getError(), TQSqlError::Transaction, status));
return false;
}
bool KreSQLiteDriver::commitTransaction()
{
if (!isOpen() || isOpenError())
return false;
TQSQLiteResult result = db->executeQuery( "COMMIT" );
int status = result.getStatus();
if (status == TQSQLiteResult::Success)
return true;
setLastError(TQSqlError("Unable to commit transaction", result.getError(), TQSqlError::Transaction, status));
return false;
}
bool KreSQLiteDriver::rollbackTransaction()
{
if (!isOpen() || isOpenError())
return false;
TQSQLiteResult result = db->executeQuery( "ROLLBACK" );
int status = result.getStatus();
if (status == SQLITE_OK)
return true;
setLastError(TQSqlError("Unable to rollback transaction", result.getError(), TQSqlError::Transaction, status));
return false;
}
TQStringList KreSQLiteDriver::tables(const TQString &typeName) const
{
TQStringList res;
if (!isOpen())
return res;
int type = typeName.toInt();
TQSqlQuery q = createQuery();
q.setForwardOnly(true);
#if (TQT_VERSION-0 >= 0x030000)
if ((type & (int)TQSql::Tables) && (type & (int)TQSql::Views))
q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
else if (typeName.isEmpty() || (type & (int)TQSql::Tables))
q.exec("SELECT name FROM sqlite_master WHERE type='table'");
else if (type & (int)TQSql::Views)
q.exec("SELECT name FROM sqlite_master WHERE type='view'");
#else
q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
#endif
if (q.isActive()) {
while(q.next()) {
res.append(q.value(0).toString());
}
}
#if (TQT_VERSION-0 >= 0x030000)
if (type & (int)TQSql::SystemTables) {
// there are no internal tables beside this one:
res.append("sqlite_master");
}
#endif
return res;
}
TQSqlIndex KreSQLiteDriver::primaryIndex(const TQString &tblname) const
{
TQSqlRecordInfo rec(recordInfo(tblname)); // expensive :(
if (!isOpen())
return TQSqlIndex();
TQSqlQuery q = createQuery();
q.setForwardOnly(true);
// finrst find a UNIQUE INDEX
q.exec("PRAGMA index_list('" + tblname + "');");
TQString indexname;
while(q.next()) {
if (q.value(2).toInt()==1) {
indexname = q.value(1).toString();
break;
}
}
if (indexname.isEmpty())
return TQSqlIndex();
q.exec("PRAGMA index_info('" + indexname + "');");
TQSqlIndex index(tblname, indexname);
while(q.next()) {
TQString name = q.value(2).toString();
TQSqlVariant::Type type = TQSqlVariant::Invalid;
if (rec.contains(name))
type = rec.find(name).type();
index.append(TQSqlField(name, type));
}
return index;
}
#if 0
TQSqlRecordInfo KreSQLiteDriver::recordInfo(const TQString &tbl) const
{
if (!isOpen())
return TQSqlRecordInfo();
TQSqlQuery q = createQuery();
q.setForwardOnly(true);
q.exec("SELECT * FROM " + tbl + " LIMIT 1");
return TQSqlRecordInfo();
// return recordInfo(q);
}
TQSqlRecord KreSQLiteDriver::record(const TQString &tblname) const
{
if (!isOpen())
return TQSqlRecord();
return recordInfo(tblname).toRecord();
}
TQSqlRecord KreSQLiteDriver::record(const TQSqlQuery& query) const
{
if (query.isActive() && query.driver() == this) {
KreSQLiteResult* result = (KreSQLiteResult*)query.result();
return result->rInf.toRecord();
}
return TQSqlRecord();
}
TQSqlRecordInfo KreSQLiteDriver::recordInfo(const TQSqlQuery& query) const
{
if (query.isActive() && query.driver() == this) {
KreSQLiteResult* result = (KreSQLiteResult*)query.result();
return result->rInf;
}
return TQSqlRecordInfo();
}
//this would be used below in formatValue()
static TQString escape( const TQString &s )
{
TQString s_escaped = s;
if ( !s_escaped.isEmpty() ) { //###: sqlite_mprintf() seems to fill an empty string with garbage
// Escape using SQLite's function
#if HAVE_SQLITE
char * escaped = sqlite_mprintf( "%q", s.latin1() ); // Escape the string(allocates memory)
#elif HAVE_SQLITE3
char * escaped = sqlite3_mprintf( "%q", s.latin1() ); // Escape the string(allocates memory)
#endif
s_escaped = escaped;
#if HAVE_SQLITE
sqlite_freemem( escaped ); // free allocated memory
#elif HAVE_SQLITE3
sqlite3_free( escaped ); // free allocated memory
#endif
}
return ( s_escaped );
}
// Everything is considered a string given the implementation of this driver (we don't have field info). This would ruin a TQByteArray (for the photo).
TQString KreSQLiteDriver::formatValue( const TQSqlField* field, bool trimStrings ) const
{
TQString r;
if ( field->isNull() ) {
r = nullText();
} else {
switch( field->type() ) {
case TQVariant::String:
case TQVariant::CString: {
// Escape '\' characters
r = TQSqlDriver::formatValue( field );
//r = escape(r);
//kdDebug()<<"escaping sqlite string: "<<r<<endl;
break;
}
default:
r = TQSqlDriver::formatValue( field, trimStrings );
}
}
return r;
}
#endif