/***************************************************************************
* 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