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.
228 lines
7.0 KiB
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 )
|
|
{}
|
|
|