--- /dev/null
+/*
+ * Copyright (C) 2011, Jamie Thompson
+ *
+ * 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 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "TriggerDisabler.h"
+
+#include "Settings.h"
+
+#include <QtSql>
+
+#include <sqlite3.h>
+
+#include <stdexcept>
+
+static void _sqlite3ORStep(sqlite3_context *context, int numArgs, sqlite3_value **value);
+static void _sqlite3ORFinal(sqlite3_context *context);
+bool createSQLiteFunctions(const QSqlDatabase &db);
+
+using namespace DBBackends::RtcomEventLoggerComponents;
+
+TriggerDisabler::TriggerDisabler(const Settings &settings)
+ : m_Settings(settings), m_Reenabled(false)
+{
+ // Preserve default cache triggers
+ // Set up the database connection...
+ QSqlDatabase db(QSqlDatabase::addDatabase( "QSQLITE" ));
+
+ db.setDatabaseName(CurrentSettings().DBPath());
+ if ( ! db.open() )
+ throw std::runtime_error("Cannot open database: Unable to establish database connection");
+ else
+ {
+ qDebug() << "DB Opened";
+
+ QSqlQuery dbqGetTriggers(db);
+ dbqGetTriggers.setForwardOnly( true );
+
+ const char * sqlGetTriggers("SELECT name, sql FROM sqlite_master WHERE type = \"trigger\" AND name LIKE \"gc_update_ev_%\"");
+ if(dbqGetTriggers.exec(sqlGetTriggers))
+ {
+ qDebug() << "Query OK, " << dbqGetTriggers.numRowsAffected() << " rows affected.";
+
+ while( dbqGetTriggers.next())
+ {
+ QString name(dbqGetTriggers.value(0).value<QString>());
+ QString sqlTrigger(dbqGetTriggers.value(1).value<QString>());
+
+ qDebug() << "( " << name << ", " << sqlTrigger << " )";
+
+ Triggers().insert(name, sqlTrigger);
+ }
+
+ qDebug() << Triggers().count() << " triggers found.";
+
+ QSqlQuery dbqRemoveTriggers(db);
+ dbqRemoveTriggers.setForwardOnly( true );
+
+ const QString sqlRemoveTriggers("DROP TRIGGER ");
+ foreach(QString triggerName, Triggers().keys())
+ {
+ qDebug() << "Executing: " << sqlRemoveTriggers + triggerName;
+ if(dbqRemoveTriggers.exec(sqlRemoveTriggers + triggerName))
+ qDebug() << "Query OK, " << dbqGetTriggers.numRowsAffected() << " rows affected.";
+ else
+ qDebug() << "Query failed, " << dbqGetTriggers.numRowsAffected() << " rows affected. Error: " << dbqRemoveTriggers.lastError().text();
+ }
+ }
+ else
+ {
+ qDebug() << "SQL EXEC Error: " << "EXEC query failed";
+ qDebug() << "Query: " << sqlGetTriggers;
+ }
+
+ qDebug() << "Closing.";
+ db.close();
+ }
+
+ QSqlDatabase::removeDatabase("QSQLITE");
+
+ return;
+}
+
+TriggerDisabler::~TriggerDisabler()
+{
+ // Restore default cache triggers
+ if(!Reenabled())
+ Reenable();
+}
+
+void TriggerDisabler::Reenable()
+{
+ // Set up the database connection...
+ QSqlDatabase db(QSqlDatabase::addDatabase( "QSQLITE" ));
+
+ db.setDatabaseName(CurrentSettings().DBPath());
+ if (!db.open())
+ throw std::runtime_error("Cannot open database: Unable to establish database connection");
+ else
+ {
+ qDebug() << "DB Opened";
+
+ RestoreTriggers(db);
+
+ UpdateGroupCache(db);
+
+ qDebug() << "Closing DB.";
+ db.close();
+ }
+
+ QSqlDatabase::removeDatabase( "QSQLITE" );
+
+ Reenabled(true);
+}
+
+void TriggerDisabler::RestoreTriggers(QSqlDatabase &db)
+{
+ QSqlQuery dbqRestoreTriggers(db);
+ dbqRestoreTriggers.setForwardOnly( true );
+
+ foreach(QString triggerName, Triggers().keys())
+ {
+ qDebug() << "Restoring trigger " << triggerName;
+ qDebug() << "\tSQL: " << Triggers().value(triggerName);
+ if(dbqRestoreTriggers.exec(Triggers().value(triggerName)))
+ qDebug() << "Query OK, " << dbqRestoreTriggers.numRowsAffected() << " rows affected.";
+ else
+ qDebug() << "Query failed, " << dbqRestoreTriggers.numRowsAffected() << " rows affected. Error: " << dbqRestoreTriggers.lastError().text();
+ }
+}
+
+void TriggerDisabler::UpdateGroupCache(QSqlDatabase &db)
+{
+ const char * sqlUpdateGroupCache(
+ "INSERT OR REPLACE INTO groupcache "
+ "SELECT id, service_id, group_uid, total, readcount, mergedflags "
+ "FROM events e, "
+ "(SELECT max(start_time) as maxdate, group_uid as maxgid, count(group_uid) as total, total(is_read) as readcount, _OR(flags) as mergedflags "
+ "FROM events "
+ "GROUP BY maxgid) maxresults "
+ "WHERE group_uid = maxgid AND start_time=maxresults.maxdate "
+ "GROUP BY group_uid");
+
+ QSqlQuery dbqRecalculateGroupCache(db);
+ dbqRecalculateGroupCache.setForwardOnly(true);
+
+ createSQLiteFunctions(db);
+
+ qDebug() << "Updating group cache...";
+ if(dbqRecalculateGroupCache.exec(sqlUpdateGroupCache))
+ {
+ qDebug() << "Query OK, " << dbqRecalculateGroupCache.numRowsAffected() << " rows affected.";
+
+ while(dbqRecalculateGroupCache.next())
+ {
+ int id(dbqRecalculateGroupCache.value(0).value<int>());
+ QString group_uid(dbqRecalculateGroupCache.value(1).value<QString>());
+ int total(dbqRecalculateGroupCache.value(2).value<int>());
+ int readcount(dbqRecalculateGroupCache.value(3).value<int>());
+ int flags(dbqRecalculateGroupCache.value(4).value<int>());
+
+ qDebug() << "( " << id << ", " << group_uid << ", " << total << ", " << readcount << ", " << flags << " )";
+ }
+ }
+ else
+ {
+ qDebug() << "SQL EXEC Error: " << "EXEC query failed. Error: " << dbqRecalculateGroupCache.lastError().text();
+ qDebug() << "Query: " << sqlUpdateGroupCache;
+ }
+}
+
+bool createSQLiteFunctions(const QSqlDatabase &db)
+{
+ // Get handle to the driver and check it is both valid and refers to SQLite3.
+ QVariant v(db.driver()->handle());
+ if (!v.isValid() || qstrcmp(v.typeName(), "sqlite3*") != 0)
+ {
+ qDebug() << "Cannot get a sqlite3 handle to the driver.";
+ return false;
+ }
+
+ // Create a handler and attach functions.
+ sqlite3 *handler(*static_cast<sqlite3**>(v.data()));
+ if (!handler)
+ {
+ qDebug() << "Cannot get a sqlite3 handler.";
+ return false;
+ }
+
+ // Check validity of the state.
+ if (!db.isValid())
+ {
+ qDebug() << "Cannot create SQLite custom functions: db object is not valid.";
+ return false;
+ }
+
+ if (!db.isOpen())
+ {
+ qDebug() << "Cannot create SQLite custom functions: db object is not open.";
+ return false;
+ }
+
+ if (sqlite3_create_function(handler, "_OR", 1, SQLITE_ANY, NULL, NULL, _sqlite3ORStep, _sqlite3ORFinal))
+ qDebug() << "Cannot create SQLite functions: sqlite3_create_function failed.";
+
+ return true;
+}
+
+static void _sqlite3ORStep(sqlite3_context *context, int, sqlite3_value **value)
+{
+ int *result((int *)sqlite3_aggregate_context(context, sizeof(int)));
+ *result |= sqlite3_value_int(*value);
+}
+
+static void _sqlite3ORFinal(sqlite3_context *context)
+{
+ sqlite3_result_int(context, *(int *)sqlite3_aggregate_context(context, sizeof(int)));
+}
--- /dev/null
+/*
+ * Copyright (C) 2011, Jamie Thompson
+ *
+ * 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 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DBBACKENDS_RTCOMEVENTLOGGERCOMPONENTS_TRIGGERDISABLER_H
+#define DBBACKENDS_RTCOMEVENTLOGGERCOMPONENTS_TRIGGERDISABLER_H
+
+class Settings;
+
+#include <QHash>
+class QSqlDatabase;
+#include <QString>
+
+namespace DBBackends
+{
+ namespace RtcomEventLoggerComponents
+ {
+ class TriggerDisabler
+ {
+ public:
+ TriggerDisabler(const Settings &settings);
+ ~TriggerDisabler();
+
+ void Reenable();
+
+ protected:
+ const Settings &CurrentSettings() const { return m_Settings; }
+
+ const QHash<QString, QString> &Triggers() const { return m_Triggers; }
+
+ const bool Reenabled() const { return m_Reenabled; }
+
+ void RestoreTriggers(QSqlDatabase &db);
+ void UpdateGroupCache(QSqlDatabase &db);
+
+ private:
+ const Settings &m_Settings;
+
+ QHash<QString, QString> &Triggers() { return m_Triggers; }
+ QHash<QString, QString> m_Triggers;
+
+ void Reenabled(bool reenabled) { m_Reenabled = reenabled; }
+ bool m_Reenabled;
+ };
+ }
+}
+
+#endif // DBBACKENDS_RTCOMEVENTLOGGERCOMPONENTS_TRIGGERDISABLER_H