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.
471 lines
10 KiB
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
|
|
|