/* statisticsdb.cpp Copyright (c) 2003-2004 by Marc Cramdal ************************************************************************* * * * 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 #include "sqlite3.h" #include #include #include #include #include #include #include #include "statisticsdb.h" #include #include StatisticsDB::StatisticsDB() { TQCString path = (::locateLocal("appdata", "kopete_statistics-0.1.db")).latin1(); kdDebug() << "statistics: DB path:" << path << endl; // Open database file and check for correctness bool failOpen = true; TQFile file( path ); if ( file.open( IO_ReadOnly ) ) { TQString format; file.readLine( format, 50 ); if ( !format.startsWith( "SQLite format 3" ) ) { kdWarning() << "[statistics] Database versions incompatible. Removing and rebuilding database.\n"; } else if ( sqlite3_open( path, &m_db ) != SQLITE_OK ) { kdWarning() << "[statistics] Database file corrupt. Removing and rebuilding database.\n"; sqlite3_close( m_db ); } else failOpen = false; } if ( failOpen ) { // Remove old db file; create new TQFile::remove( path ); sqlite3_open( path, &m_db ); } kdDebug() << "[Statistics] Contructor"<< endl; // Creates the tables if they do not exist. TQStringList result = query("SELECT name FROM sqlite_master WHERE type='table'"); if (!result.contains("contacts")) { query(TQString("CREATE TABLE contacts " "(id INTEGER PRIMARY KEY," "statisticid TEXT," "contactid TEXT" ");")); } if (!result.contains("contactstatus")) { kdDebug() << "[Statistics] Database empty"<< endl; query(TQString("CREATE TABLE contactstatus " "(id INTEGER PRIMARY KEY," "metacontactid TEXT," "status TEXT," "datetimebegin INTEGER," "datetimeend INTEGER" ");")); } if (!result.contains("commonstats")) { // To store things like the contact answer time etc. query(TQString("CREATE TABLE commonstats" " (id INTEGER PRIMARY KEY," "metacontactid TEXT," "statname TEXT," // for instance, answertime, lastmessage, messagelength ... "statvalue1 TEXT," "statvalue2 TEXT" ");")); } /// @fixme This is not used anywhere if (!result.contains("statsgroup")) { query(TQString("CREATE TABLE statsgroup" "(id INTEGER PRIMARY KEY," "datetimebegin INTEGER," "datetimeend INTEGER," "caption TEXT);")); } } StatisticsDB::~StatisticsDB() { sqlite3_close(m_db); } /** * Executes a SQL query on the already opened database * @param statement SQL program to execute. Only one SQL statement is allowed. * @param debug Set to true for verbose debug output. * @retval names Will contain all column names, set to NULL if not used. * @return The queried data, or TQStringList() on error. */ TQStringList StatisticsDB::query( const TQString& statement, TQStringList* const names, bool debug ) { if ( debug ) kdDebug() << "query-start: " << statement << endl; clock_t start = clock(); if ( !m_db ) { kdError() << k_funcinfo << "[CollectionDB] SQLite pointer == NULL.\n"; return TQStringList(); } int error; TQStringList values; 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 ) { kdError() << k_funcinfo << "[CollectionDB] sqlite3_compile error:" << endl; kdError() << sqlite3_errmsg( m_db ) << endl; kdError() << "on query: " << statement << endl; return TQStringList(); } 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 ) { kdError() << "[CollectionDB] Busy-counter has reached maximum. Aborting this sql statement!\n"; break; } ::usleep( 100000 ); // Sleep 100 msec kdDebug() << "[CollectionDB] sqlite3_step: BUSY counter: " << busyCnt << endl; } if ( error == SQLITE_MISUSE ) kdDebug() << "[CollectionDB] 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 ) ); if ( names ) *names << TQString( sqlite3_column_name( stmt, i ) ); } } //deallocate vm ressources sqlite3_finalize( stmt ); if ( error != SQLITE_DONE ) { kdError() << k_funcinfo << "sqlite_step error.\n"; kdError() << sqlite3_errmsg( m_db ) << endl; kdError() << "on query: " << statement << endl; return TQStringList(); } if ( debug ) { clock_t finish = clock(); const double duration = (double) (finish - start) / CLOCKS_PER_SEC; kdDebug() << "[CollectionDB] SQL-query (" << duration << "s): " << statement << endl; } return values; }