// (c) 2004 Mark Kretschmann // (c) 2004 Christian Muehlhaeuser // (c) 2004 Sami Nieminen // (c) 2005 Ian Monroe // See COPYING file for licensing information. #define DEBUG_PREFIX "SQLite-DBEngine" #include "app.h" #include "amarok.h" #include "amarokconfig.h" #include "debug.h" #include "sqlite_dbengine.h" #include #include #include #include #include //DbConnection::sqlite_power() #include //query() #include //usleep() #include "sqlite/sqlite3.h" AMAROK_EXPORT_PLUGIN( SqliteDbEngine ) ////////////////////////////////////////////////////////////////////////////////////////// // CLASS SqliteConnection ////////////////////////////////////////////////////////////////////////////////////////// SqliteDbEngine::SqliteDbEngine() : DbConnection( new SqliteConfig( "collection.db" ) ) { const TQCString path = TQString(/*amaroK::saveLocation()+*/"collection.db").local8Bit(); // Open database file and check for correctness m_initialized = false; TQFile file( path ); if ( file.open( IO_ReadOnly ) ) { TQString format; file.readLine( format, 50 ); if ( !format.startsWith( "SQLite format 3" ) ) { warning() << "Database versions incompatible. Removing and rebuilding database.\n"; } else if ( sqlite3_open( path, &m_db ) != SQLITE_OK ) { warning() << "Database file corrupt. Removing and rebuilding database.\n"; sqlite3_close( m_db ); } else m_initialized = true; } if ( !m_initialized ) { // Remove old db file; create new TQFile::remove( path ); if ( sqlite3_open( path, &m_db ) == SQLITE_OK ) { m_initialized = true; } } if ( m_initialized ) { if( sqlite3_create_function(m_db, "rand", 0, SQLITE_UTF8, NULL, sqlite_rand, NULL, NULL) != SQLITE_OK ) m_initialized = false; if( sqlite3_create_function(m_db, "power", 2, SQLITE_UTF8, NULL, sqlite_power, NULL, NULL) != SQLITE_OK ) m_initialized = false; } //optimization for speeding up SQLite query( "PRAGMA default_synchronous = OFF;" ); } SqliteDbEngine::~SqliteDbEngine() { if ( m_db ) sqlite3_close( m_db ); } TQStringList SqliteDbEngine::query( const TQString& statement ) { TQStringList values; int error; const char* tail; sqlite3_stmt* stmt; //compile SQL program to virtual machine error = sqlite3_prepare( m_db, statement.utf8(), statement.length(), &stmt, &tail ); if ( error != SQLITE_OK ) { Debug::error() << k_funcinfo << " sqlite3_compile error:" << endl; Debug::error() << sqlite3_errmsg( m_db ) << endl; Debug::error() << "on query: " << statement << endl; values = TQStringList(); } else { int busyCnt = 0; int number = sqlite3_column_count( stmt ); //execute virtual machine by iterating over rows while ( true ) { error = sqlite3_step( stmt ); if ( error == SQLITE_BUSY ) { if ( busyCnt++ > 20 ) { Debug::error() << "Busy-counter has reached maximum. Aborting this sql statement!\n"; break; } ::usleep( 100000 ); // Sleep 100 msec debug() << "sqlite3_step: BUSY counter: " << busyCnt << endl; } if ( error == SQLITE_MISUSE ) debug() << "sqlite3_step: MISUSE" << endl; if ( error == SQLITE_DONE || error == SQLITE_ERROR ) break; //iterate over columns for ( int i = 0; i < number; i++ ) { values << TQString::fromUtf8( (const char*) sqlite3_column_text( stmt, i ) ); } } //deallocate vm resources sqlite3_finalize( stmt ); if ( error != SQLITE_DONE ) { Debug::error() << k_funcinfo << "sqlite_step error.\n"; Debug::error() << sqlite3_errmsg( m_db ) << endl; Debug::error() << "on query: " << statement << endl; values = TQStringList(); } } return values; } int SqliteDbEngine::insert( const TQString& statement, const TQString& /* table */ ) { int error; const char* tail; sqlite3_stmt* stmt; //compile SQL program to virtual machine error = sqlite3_prepare( m_db, statement.utf8(), statement.length(), &stmt, &tail ); if ( error != SQLITE_OK ) { Debug::error() << k_funcinfo << " sqlite3_compile error:" << endl; Debug::error() << sqlite3_errmsg( m_db ) << endl; Debug::error() << "on insert: " << statement << endl; } else { int busyCnt = 0; //execute virtual machine by iterating over rows while ( true ) { error = sqlite3_step( stmt ); if ( error == SQLITE_BUSY ) { if ( busyCnt++ > 20 ) { Debug::error() << "Busy-counter has reached maximum. Aborting this sql statement!\n"; break; } ::usleep( 100000 ); // Sleep 100 msec debug() << "sqlite3_step: BUSY counter: " << busyCnt << endl; } if ( error == SQLITE_MISUSE ) debug() << "sqlite3_step: MISUSE" << endl; if ( error == SQLITE_DONE || error == SQLITE_ERROR ) break; } //deallocate vm resources sqlite3_finalize( stmt ); if ( error != SQLITE_DONE ) { Debug::error() << k_funcinfo << "sqlite_step error.\n"; Debug::error() << sqlite3_errmsg( m_db ) << endl; Debug::error() << "on insert: " << statement << endl; } } return sqlite3_last_insert_rowid( m_db ); } // this implements a RAND() function compatible with the MySQL RAND() (0-param-form without seed) void SqliteDbEngine::sqlite_rand(sqlite3_context *context, int /*argc*/, sqlite3_value ** /*argv*/) { //sqlite3_result_double( context, static_cast(KApplication::random()) / (RAND_MAX+1.0) ); } // this implements a POWER() function compatible with the MySQL POWER() void SqliteDbEngine::sqlite_power(sqlite3_context *context, int argc, sqlite3_value **argv) { Q_ASSERT( argc==2 ); if( sqlite3_value_type(argv[0])==SQLITE_NULL || sqlite3_value_type(argv[1])==SQLITE_NULL ) { sqlite3_result_null(context); return; } double a = sqlite3_value_double(argv[0]); double b = sqlite3_value_double(argv[1]); sqlite3_result_double( context, pow(a,b) ); } ////////////////////////////////////////////////////////////////////////////////////////// // CLASS SqliteConfig ////////////////////////////////////////////////////////////////////////////////////////// SqliteConfig::SqliteConfig( const TQString& dbfile ) : m_dbfile( dbfile ) {}