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.
amarok/amarok/src/database_refactor/sqlite/sqlite_dbengine.cpp

228 lines
7.0 KiB

// (c) 2004 Mark Kretschmann <markey@web.de>
// (c) 2004 Christian Muehlhaeuser <chris@chris.de>
// (c) 2004 Sami Nieminen <sami.nieminen@iki.fi>
// (c) 2005 Ian Monroe <ian@monroe.nu>
// 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 <kapplication.h>
#include <tqfile.h>
#include <tqimage.h>
#include <tqtimer.h>
#include <cmath> //DbConnection::sqlite_power()
#include <ctime> //query()
#include <unistd.h> //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<double>(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 )
{}