summaryrefslogtreecommitdiffstats
path: root/kopete/plugins/statistics/statisticsdb.cpp
blob: c0a412320618fe272418040838c756730cb1c379 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
    statisticsdb.cpp

    Copyright (c) 2003-2004 by Marc Cramdal        <marc.cramdal@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 <tqfile.h>

#include "sqlite/sqlite3.h"

#include <kgenericfactory.h>
#include <kaboutdata.h>
#include <kaction.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kdeversion.h>

#include "statisticsdb.h"

#include <unistd.h>
#include <time.h>

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;
}