zeitgeist team mailing list archive
-
zeitgeist team
-
Mailing list archive
-
Message #04669
[Merge] lp:~mhr3/zeitgeist/refactoring into lp:zeitgeist
Michal Hruby has proposed merging lp:~mhr3/zeitgeist/refactoring into lp:zeitgeist.
Requested reviews:
Zeitgeist Framework Team (zeitgeist)
For more details, see:
https://code.launchpad.net/~mhr3/zeitgeist/refactoring/+merge/91334
Refactor Engine class, so it's easier to do read-only DB access.
--
https://code.launchpad.net/~mhr3/zeitgeist/refactoring/+merge/91334
Your team Zeitgeist Framework Team is requested to review the proposed merge of lp:~mhr3/zeitgeist/refactoring into lp:zeitgeist.
=== modified file 'extensions/storage-monitor.vala'
--- extensions/storage-monitor.vala 2012-01-27 13:34:18 +0000
+++ extensions/storage-monitor.vala 2012-02-02 19:02:53 +0000
@@ -106,7 +106,7 @@
"dav", "davs", "ftp", "http", "https", "mailto",
"sftp", "smb", "ssh" };
- private Zeitgeist.SQLite.ZeitgeistDatabase database;
+ private Zeitgeist.SQLite.Database database;
private unowned Sqlite.Database db;
private uint registration_id;
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2011-12-31 18:15:06 +0000
+++ src/Makefile.am 2012-02-02 19:02:53 +0000
@@ -31,6 +31,7 @@
zeitgeist_daemon_VALASOURCES = \
zeitgeist-daemon.vala \
datamodel.vala \
+ db-reader.vala \
engine.vala \
remote.vala \
extension.vala \
=== modified file 'src/datamodel.vala'
--- src/datamodel.vala 2012-01-25 17:37:55 +0000
+++ src/datamodel.vala 2012-02-02 19:02:53 +0000
@@ -268,7 +268,7 @@
{
var matches = false;
var parsed = template_property;
- var is_negated = Engine.parse_negation (ref parsed);
+ var is_negated = Utils.parse_negation (ref parsed);
if (parsed == "")
{
@@ -283,7 +283,7 @@
{
matches = true;
}
- else if (can_wildcard && Engine.parse_wildcard (ref parsed))
+ else if (can_wildcard && Utils.parse_wildcard (ref parsed))
{
if (property.has_prefix (parsed)) matches = true;
}
=== added file 'src/db-reader.vala'
--- src/db-reader.vala 1970-01-01 00:00:00 +0000
+++ src/db-reader.vala 2012-02-02 19:02:53 +0000
@@ -0,0 +1,894 @@
+/* db-reader.vala
+ *
+ * Copyright © 2011 Collabora Ltd.
+ * By Siegfried-Angel Gevatter Pujals <siegfried@xxxxxxxxxxxx>
+ * By Seif Lotfy <seif@xxxxxxxxx>
+ * Copyright © 2011 Canonical Ltd.
+ * By Michal Hruby <michal.hruby@xxxxxxxxxxxxx>
+ *
+ * Based upon a Python implementation (2009-2011) by:
+ * Markus Korn <thekorn@xxxxxxx>
+ * Mikkel Kamstrup Erlandsen <mikkel.kamstrup@xxxxxxxxx>
+ * Seif Lotfy <seif@xxxxxxxxx>
+ * Siegfried-Angel Gevatter Pujals <siegfried@xxxxxxxxxxxx>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+using Zeitgeist;
+using Zeitgeist.SQLite;
+using Zeitgeist.Utils;
+
+namespace Zeitgeist
+{
+
+public class DbReader : Object
+{
+
+ public Zeitgeist.SQLite.Database database { get; construct; }
+ protected unowned Sqlite.Database db;
+
+ protected TableLookup interpretations_table;
+ protected TableLookup manifestations_table;
+ protected TableLookup mimetypes_table;
+ protected TableLookup actors_table;
+
+ public DbReader () throws EngineError
+ {
+ Object (database: new Zeitgeist.SQLite.Database ());
+ }
+
+ construct
+ {
+ database.set_deletion_callback (delete_from_cache);
+ db = database.database;
+
+ interpretations_table = new TableLookup (database, "interpretation");
+ manifestations_table = new TableLookup (database, "manifestation");
+ mimetypes_table = new TableLookup (database, "mimetype");
+ actors_table = new TableLookup (database, "actor");
+ }
+
+ protected Event get_event_from_row (Sqlite.Statement stmt, uint32 event_id)
+ {
+ Event event = new Event ();
+ event.id = event_id;
+ event.timestamp = stmt.column_int64 (EventViewRows.TIMESTAMP);
+ event.interpretation = interpretations_table.get_value (
+ stmt.column_int (EventViewRows.INTERPRETATION));
+ event.manifestation = manifestations_table.get_value (
+ stmt.column_int (EventViewRows.MANIFESTATION));
+ event.actor = actors_table.get_value (
+ stmt.column_int (EventViewRows.ACTOR));
+ event.origin = stmt.column_text (
+ EventViewRows.EVENT_ORIGIN_URI);
+
+ // Load payload
+ unowned uint8[] data = (uint8[]) stmt.column_blob(
+ EventViewRows.PAYLOAD);
+ data.length = stmt.column_bytes(EventViewRows.PAYLOAD);
+ if (data != null)
+ {
+ event.payload = new ByteArray();
+ event.payload.append(data);
+ }
+ return event;
+ }
+
+ protected Subject get_subject_from_row (Sqlite.Statement stmt)
+ {
+ Subject subject = new Subject ();
+ subject.uri = stmt.column_text (EventViewRows.SUBJECT_URI);
+ subject.text = stmt.column_text (EventViewRows.SUBJECT_TEXT);
+ subject.storage = stmt.column_text (EventViewRows.SUBJECT_STORAGE);
+ subject.origin = stmt.column_text (EventViewRows.SUBJECT_ORIGIN_URI);
+ subject.current_uri = stmt.column_text (
+ EventViewRows.SUBJECT_CURRENT_URI);
+ subject.interpretation = interpretations_table.get_value (
+ stmt.column_int (EventViewRows.SUBJECT_INTERPRETATION));
+ subject.manifestation = manifestations_table.get_value (
+ stmt.column_int (EventViewRows.SUBJECT_MANIFESTATION));
+ subject.mimetype = mimetypes_table.get_value (
+ stmt.column_int (EventViewRows.SUBJECT_MIMETYPE));
+ return subject;
+ }
+
+ public GenericArray<Event?> get_events(uint32[] event_ids,
+ BusName? sender=null) throws EngineError
+ {
+ // TODO: Consider if we still want the cache. This should be done
+ // once everything is working, since it adds unneeded complexity.
+ // It'd also benchmark it again first, we may have better options
+ // to enhance the performance of SQLite now, and event processing
+ // will be faster now being C.
+
+ if (event_ids.length == 0)
+ return new GenericArray<Event?> ();
+
+ var sql_event_ids = database.get_sql_string_from_event_ids (event_ids);
+ string sql = """
+ SELECT * FROM event_view
+ WHERE id IN (%s)
+ """.printf (sql_event_ids);
+
+ Sqlite.Statement stmt;
+ int rc = db.prepare_v2 (sql, -1, out stmt);
+ database.assert_query_success (rc, "SQL error");
+
+ var events = new HashTable<uint32, Event?> (direct_hash, direct_equal);
+
+ // Create Events and Subjects from rows
+ while ((rc = stmt.step ()) == Sqlite.ROW)
+ {
+ uint32 event_id = (uint32) stmt.column_int64 (EventViewRows.ID);
+ Event? event = events.lookup (event_id);
+ if (event == null)
+ {
+ event = get_event_from_row(stmt, event_id);
+ events.insert (event_id, event);
+ }
+ Subject subject = get_subject_from_row(stmt);
+ event.add_subject(subject);
+ }
+ if (rc != Sqlite.DONE)
+ {
+ throw new EngineError.DATABASE_ERROR ("Error: %d, %s\n",
+ rc, db.errmsg ());
+ }
+
+ // Sort events according to the sequence of event_ids
+ var results = new GenericArray<Event?> ();
+ results.length = event_ids.length;
+ int i = 0;
+ foreach (var id in event_ids)
+ {
+ results.set(i++, events.lookup (id));
+ }
+
+ return results;
+ }
+
+ public uint32[] find_event_ids (TimeRange time_range,
+ GenericArray<Event> event_templates,
+ uint storage_state, uint max_events, uint result_type,
+ BusName? sender=null) throws EngineError
+ {
+
+ WhereClause where = new WhereClause (WhereClause.Type.AND);
+
+ /**
+ * We are using the unary operator here to tell SQLite to not use
+ * the index on the timestamp column at the first place. This is a
+ * "fix" for (LP: #672965) based on some benchmarks, which suggest
+ * a performance win, but we might not oversee all implications.
+ * (See http://www.sqlite.org/optoverview.html, section 6.0).
+ * -- Markus Korn, 29/11/2010
+ */
+ if (time_range.start != 0)
+ where.add (("+timestamp >= %" + int64.FORMAT).printf(
+ time_range.start));
+ if (time_range.end != 0)
+ where.add (("+timestamp <= %" + int64.FORMAT).printf(
+ time_range.end));
+
+ if (storage_state == StorageState.AVAILABLE ||
+ storage_state == StorageState.NOT_AVAILABLE)
+ {
+ where.add ("(subj_storage_state=? OR subj_storage_state IS NULL)",
+ storage_state.to_string ());
+ }
+ else if (storage_state != StorageState.ANY)
+ {
+ throw new EngineError.INVALID_ARGUMENT(
+ "Unknown storage state '%u'".printf(storage_state));
+ }
+
+ WhereClause tpl_conditions = get_where_clause_from_event_templates (
+ event_templates);
+ where.extend (tpl_conditions);
+ //if (!where.may_have_results ())
+ // return new uint32[0];
+
+ string sql = "SELECT id FROM event_view ";
+ string where_sql = "";
+ if (!where.is_empty ())
+ {
+ where_sql = "WHERE " + where.get_sql_conditions ();
+ }
+
+ switch (result_type)
+ {
+ case ResultType.MOST_RECENT_EVENTS:
+ sql += where_sql + " ORDER BY timestamp DESC";
+ break;
+ case ResultType.LEAST_RECENT_EVENTS:
+ sql += where_sql + " ORDER BY timestamp ASC";
+ break;
+ case ResultType.MOST_RECENT_EVENT_ORIGIN:
+ sql += group_and_sort ("origin", where_sql, false);
+ break;
+ case ResultType.LEAST_RECENT_EVENT_ORIGIN:
+ sql += group_and_sort ("origin", where_sql, true);
+ break;
+ case ResultType.MOST_POPULAR_EVENT_ORIGIN:
+ sql += group_and_sort ("origin", where_sql, false, false);
+ break;
+ case ResultType.LEAST_POPULAR_EVENT_ORIGIN:
+ sql += group_and_sort ("origin", where_sql, true, true);
+ break;
+ case ResultType.MOST_RECENT_SUBJECTS:
+ sql += group_and_sort ("subj_id", where_sql, false);
+ break;
+ case ResultType.LEAST_RECENT_SUBJECTS:
+ sql += group_and_sort ("subj_id", where_sql, true);
+ break;
+ case ResultType.MOST_POPULAR_SUBJECTS:
+ sql += group_and_sort ("subj_id", where_sql, false, false);
+ break;
+ case ResultType.LEAST_POPULAR_SUBJECTS:
+ sql += group_and_sort ("subj_id", where_sql, true, true);
+ break;
+ case ResultType.MOST_RECENT_CURRENT_URI:
+ sql += group_and_sort ("subj_id_current", where_sql, false);
+ break;
+ case ResultType.LEAST_RECENT_CURRENT_URI:
+ sql += group_and_sort ("subj_id_current", where_sql, true);
+ break;
+ case ResultType.MOST_POPULAR_CURRENT_URI:
+ sql += group_and_sort ("subj_id_current", where_sql,
+ false, false);
+ break;
+ case ResultType.LEAST_POPULAR_CURRENT_URI:
+ sql += group_and_sort ("subj_id_current", where_sql,
+ true, true);
+ break;
+ case ResultType.MOST_RECENT_ACTOR:
+ sql += group_and_sort ("actor", where_sql, false);
+ break;
+ case ResultType.LEAST_RECENT_ACTOR:
+ sql += group_and_sort ("actor", where_sql, true);
+ break;
+ case ResultType.MOST_POPULAR_ACTOR:
+ sql += group_and_sort ("actor", where_sql, false, false);
+ break;
+ case ResultType.LEAST_POPULAR_ACTOR:
+ sql += group_and_sort ("actor", where_sql, true, true);
+ break;
+ case ResultType.OLDEST_ACTOR:
+ sql += group_and_sort ("actor", where_sql, true, null, "min");
+ break;
+ case ResultType.MOST_RECENT_ORIGIN:
+ sql += group_and_sort ("subj_origin", where_sql, false);
+ break;
+ case ResultType.LEAST_RECENT_ORIGIN:
+ sql += group_and_sort ("subj_origin", where_sql, true);
+ break;
+ case ResultType.MOST_POPULAR_ORIGIN:
+ sql += group_and_sort ("subj_origin", where_sql, false, false);
+ break;
+ case ResultType.LEAST_POPULAR_ORIGIN:
+ sql += group_and_sort ("subj_origin", where_sql, true, true);
+ break;
+ case ResultType.MOST_RECENT_SUBJECT_INTERPRETATION:
+ sql += group_and_sort ("subj_interpretation", where_sql, false);
+ break;
+ case ResultType.LEAST_RECENT_SUBJECT_INTERPRETATION:
+ sql += group_and_sort ("subj_interpretation", where_sql, true);
+ break;
+ case ResultType.MOST_POPULAR_SUBJECT_INTERPRETATION:
+ sql += group_and_sort ("subj_interpretation", where_sql,
+ false, false);
+ break;
+ case ResultType.LEAST_POPULAR_SUBJECT_INTERPRETATION:
+ sql += group_and_sort ("subj_interpretation", where_sql,
+ true, true);
+ break;
+ case ResultType.MOST_RECENT_MIMETYPE:
+ sql += group_and_sort ("subj_mimetype", where_sql, false);
+ break;
+ case ResultType.LEAST_RECENT_MIMETYPE:
+ sql += group_and_sort ("subj_mimetype", where_sql, true);
+ break;
+ case ResultType.MOST_POPULAR_MIMETYPE:
+ sql += group_and_sort ("subj_mimetype", where_sql,
+ false, false);
+ break;
+ case ResultType.LEAST_POPULAR_MIMETYPE:
+ sql += group_and_sort ("subj_mimetype", where_sql,
+ true, true);
+ break;
+ default:
+ string error_message = "Invalid ResultType.";
+ warning (error_message);
+ throw new EngineError.INVALID_ARGUMENT (error_message);
+ }
+
+ int rc;
+ Sqlite.Statement stmt;
+
+ rc = db.prepare_v2 (sql, -1, out stmt);
+ database.assert_query_success(rc, "SQL error");
+
+ var arguments = where.get_bind_arguments ();
+ for (int i = 0; i < arguments.length; ++i)
+ stmt.bind_text (i + 1, arguments[i]);
+
+#if EXPLAIN_QUERIES
+ database.explain_query (stmt);
+#endif
+
+ uint32[] event_ids = {};
+
+ while ((rc = stmt.step()) == Sqlite.ROW)
+ {
+ var event_id = (uint32) uint64.parse(
+ stmt.column_text (EventViewRows.ID));
+ // Events are supposed to be contiguous in the database
+ if (event_ids.length == 0 || event_ids[event_ids.length-1] != event_id) {
+ event_ids += event_id;
+ if (event_ids.length == max_events) break;
+ }
+ }
+ if (rc != Sqlite.DONE && rc != Sqlite.ROW)
+ {
+ string error_message = "Error in find_event_ids: %d, %s".printf (
+ rc, db.errmsg ());
+ warning (error_message);
+ throw new EngineError.DATABASE_ERROR (error_message);
+ }
+
+ return event_ids;
+ }
+
+ public GenericArray<Event?> find_events (TimeRange time_range,
+ GenericArray<Event> event_templates,
+ uint storage_state, uint max_events, uint result_type,
+ BusName? sender=null) throws EngineError
+ {
+ return get_events (find_event_ids (time_range, event_templates,
+ storage_state, max_events, result_type));
+ }
+
+ private struct RelatedUri {
+ public uint32 id;
+ public int64 timestamp;
+ public string uri;
+ public int32 counter;
+ }
+
+ public string[] find_related_uris (TimeRange time_range,
+ GenericArray<Event> event_templates,
+ GenericArray<Event> result_event_templates,
+ uint storage_state, uint max_results, uint result_type,
+ BusName? sender=null) throws EngineError
+ {
+ /**
+ * Return a list of subject URIs commonly used together with events
+ * matching the given template, considering data from within the
+ * indicated timerange.
+ * Only URIs for subjects matching the indicated `result_event_templates`
+ * and `result_storage_state` are returned.
+ */
+ if (result_type == ResultType.MOST_RECENT_EVENTS ||
+ result_type == ResultType.LEAST_RECENT_EVENTS)
+ {
+
+ // We pick out the ids for relational event so we can set them as
+ // roots the ids are taken from the events that match the
+ // events_templates
+ uint32[] ids = find_event_ids (time_range, event_templates,
+ storage_state, 0, ResultType.LEAST_RECENT_EVENTS);
+
+ if (event_templates.length > 0 && ids.length == 0)
+ {
+ throw new EngineError.INVALID_ARGUMENT (
+ "No results found for the event_templates");
+ }
+
+ // Pick out the result_ids for the filtered results we would like to
+ // take into account the ids are taken from the events that match
+ // the result_event_templates if no result_event_templates are set we
+ // consider all results as allowed
+ uint32[] result_ids;
+ result_ids = find_event_ids (time_range, result_event_templates,
+ storage_state, 0, ResultType.LEAST_RECENT_EVENTS);
+
+ // From here we create several graphs with the maximum depth of 2
+ // and push all the nodes and vertices (events) in one pot together
+
+ uint32[] pot = new uint32[ids.length + result_ids.length];
+
+ for (uint32 i=0; i < ids.length; i++)
+ pot[i] = ids[i];
+ for (uint32 i=0; i < result_ids.length; i++)
+ pot[ids.length + i] = result_ids[ids.length + i];
+
+ Sqlite.Statement stmt;
+
+ var sql_event_ids = database.get_sql_string_from_event_ids (pot);
+ string sql = """
+ SELECT id, timestamp, subj_uri FROM event_view
+ WHERE id IN (%s) ORDER BY timestamp ASC
+ """.printf (sql_event_ids);
+
+ int rc = db.prepare_v2 (sql, -1, out stmt);
+
+ database.assert_query_success(rc, "SQL error");
+
+ // FIXME: fix this ugly code
+ var temp_related_uris = new GenericArray<RelatedUri?>();
+
+ while ((rc = stmt.step()) == Sqlite.ROW)
+ {
+ RelatedUri ruri = RelatedUri(){
+ id = (uint32) uint64.parse(stmt.column_text (0)),
+ timestamp = stmt.column_int64 (1),
+ uri = stmt.column_text (2),
+ counter = 0
+ };
+ temp_related_uris.add (ruri);
+ }
+
+ // RelatedUri[] related_uris = new RelatedUri[temp_related_uris.length];
+ // for (int i=0; i<related_uris.length; i++)
+ // related_uris[i] = temp_related_uris[i];
+
+ if (rc != Sqlite.DONE)
+ {
+ string error_message =
+ "Error in find_related_uris: %d, %s".printf (
+ rc, db.errmsg ());
+ warning (error_message);
+ throw new EngineError.DATABASE_ERROR (error_message);
+ }
+
+ var uri_counter = new HashTable<string, RelatedUri?>(
+ str_hash, str_equal);
+
+ for (int i = 0; i < temp_related_uris.length; i++)
+ {
+ var window = new GenericArray<unowned RelatedUri?>();
+
+ bool count_in_window = false;
+ for (int j = int.max (0, i - 5);
+ j < int.min (i, temp_related_uris.length);
+ j++)
+ {
+ window.add(temp_related_uris[j]);
+ if (temp_related_uris[j].id in ids)
+ count_in_window = true;
+ }
+
+ if (count_in_window)
+ {
+ for (int j = 0; j < window.length; j++)
+ {
+ if (uri_counter.lookup (window[j].uri) == null)
+ {
+ RelatedUri ruri = RelatedUri ()
+ {
+ id = window[j].id,
+ timestamp = window[j].timestamp,
+ uri = window[j].uri,
+ counter = 0
+ };
+ uri_counter.insert (window[j].uri, ruri);
+ }
+ uri_counter.lookup (window[j].uri).counter++;
+ if (uri_counter.lookup (window[j].uri).timestamp
+ < window[j].timestamp)
+ {
+ uri_counter.lookup (window[j].uri).timestamp =
+ window[j].timestamp;
+ }
+ }
+ }
+ }
+
+
+ // We have the big hashtable with the structs, now we sort them by
+ // most used and limit the result then sort again
+ List<RelatedUri?> temp_ruris = new List<RelatedUri?>();
+ List<RelatedUri?> values = new List<RelatedUri?>();
+
+ foreach (var uri in uri_counter.get_values())
+ values.append(uri);
+
+ values.sort ((a, b) => a.counter - b.counter);
+ values.sort ((a, b) => {
+ int64 delta = a.timestamp - b.timestamp;
+ if (delta < 0) return 1;
+ else if (delta > 0) return -1;
+ else return 0;
+ });
+
+ foreach (RelatedUri ruri in values)
+ {
+ if (temp_ruris.length() < max_results)
+ temp_ruris.append(ruri);
+ else
+ break;
+ }
+
+ // Sort by recency
+ if (result_type == 1)
+ temp_ruris.sort ((a, b) => {
+ int64 delta = a.timestamp - b.timestamp;
+ if (delta < 0) return 1;
+ else if (delta > 0) return -1;
+ else return 0;});
+
+ string[] results = new string[temp_ruris.length()];
+
+ int i = 0;
+ foreach (var uri in temp_ruris)
+ {
+ results[i] = uri.uri;
+ stdout.printf("%i %lld %s\n", uri.counter,
+ uri.timestamp,
+ uri.uri);
+ i++;
+ }
+
+ return results;
+ }
+ else
+ {
+ throw new EngineError.DATABASE_ERROR ("Unsupported ResultType.");
+ }
+ }
+
+ /**
+ * Clear all resources Engine is using (close database connection, etc.).
+ *
+ * After executing this method on an instance, no other function
+ * may be called.
+ */
+ public virtual void close ()
+ {
+ database.close ();
+ }
+
+ // Used by find_event_ids
+ private string group_and_sort (string field, string where_sql,
+ bool time_asc=false, bool? count_asc=null,
+ string aggregation_type="max")
+ {
+ string time_sorting = (time_asc) ? "ASC" : "DESC";
+ string aggregation_sql = "";
+ string order_sql = "";
+
+ if (count_asc != null)
+ {
+ aggregation_sql = ", COUNT(%s) AS num_events".printf (field);
+ order_sql = "num_events %s,".printf ((count_asc) ? "ASC" : "DESC");
+ }
+
+ return """
+ NATURAL JOIN (
+ SELECT %s,
+ %s(timestamp) AS timestamp
+ %s
+ FROM event_view %s
+ GROUP BY %s)
+ GROUP BY %s
+ ORDER BY %s timestamp %s
+ """.printf (
+ field,
+ aggregation_type,
+ aggregation_sql,
+ where_sql,
+ field,
+ field,
+ order_sql, time_sorting);
+ }
+
+ // Used by find_event_ids
+ protected WhereClause get_where_clause_from_event_templates (
+ GenericArray<Event> templates) throws EngineError
+ {
+ WhereClause where = new WhereClause (WhereClause.Type.OR);
+ for (int i = 0; i < templates.length; ++i)
+ {
+ Event event_template = templates[i];
+ where.extend (
+ get_where_clause_from_event_template (event_template));
+ }
+ return where;
+ }
+
+ // Used by get_where_clause_from_event_templates
+ private WhereClause get_where_clause_from_event_template (Event template)
+ throws EngineError
+ {
+ WhereClause where = new WhereClause (WhereClause.Type.AND);
+
+ // Event ID
+ if (template.id != 0)
+ where.add ("id=?", template.id.to_string());
+
+ // Interpretation
+ if (template.interpretation != "")
+ {
+ assert_no_wildcard ("interpretation", template.interpretation);
+ WhereClause subwhere = get_where_clause_for_symbol (
+ "interpretation", template.interpretation,
+ interpretations_table);
+ if (!subwhere.is_empty ())
+ where.extend (subwhere);
+ }
+
+ // Manifestation
+ if (template.manifestation != "")
+ {
+ assert_no_wildcard ("manifestation", template.interpretation);
+ WhereClause subwhere = get_where_clause_for_symbol (
+ "manifestation", template.manifestation,
+ manifestations_table);
+ if (!subwhere.is_empty ())
+ where.extend (subwhere);
+ }
+
+ // Actor
+ if (template.actor != "")
+ {
+ string val = template.actor;
+ bool like = parse_wildcard (ref val);
+ bool negated = parse_negation (ref val);
+
+ if (like)
+ where.add_wildcard_condition ("actor", val, negated);
+ else
+ where.add_match_condition ("actor",
+ actors_table.get_id (val), negated);
+ }
+
+ // Origin
+ if (template.origin != "")
+ {
+ string val = template.origin;
+ bool like = parse_wildcard (ref val);
+ bool negated = parse_negation (ref val);
+ assert_no_noexpand (val, "origin");
+
+ if (like)
+ where.add_wildcard_condition ("origin", val, negated);
+ else
+ where.add_text_condition_subquery ("origin", val, negated);
+ }
+
+ // Subject templates within the same event template are AND'd
+ // See LP bug #592599.
+ for (int i = 0; i < template.num_subjects(); ++i)
+ {
+ Subject subject_template = template.subjects[i];
+
+ // Subject interpretation
+ if (subject_template.interpretation != "")
+ {
+ assert_no_wildcard ("subject interpretation",
+ template.interpretation);
+ WhereClause subwhere = get_where_clause_for_symbol (
+ "subj_interpretation", subject_template.interpretation,
+ interpretations_table);
+ if (!subwhere.is_empty ())
+ where.extend (subwhere);
+ }
+
+ // Subject manifestation
+ if (subject_template.manifestation != "")
+ {
+ assert_no_wildcard ("subject manifestation",
+ subject_template.manifestation);
+ WhereClause subwhere = get_where_clause_for_symbol (
+ "subj_manifestation", subject_template.manifestation,
+ manifestations_table);
+ if (!subwhere.is_empty ())
+ where.extend (subwhere);
+ }
+
+ // Mime-Type
+ if (subject_template.mimetype != "")
+ {
+ string val = subject_template.mimetype;
+ bool like = parse_wildcard (ref val);
+ bool negated = parse_negation (ref val);
+ assert_no_noexpand (val, "mime-type");
+
+ if (like)
+ where.add_wildcard_condition (
+ "subj_mimetype", val, negated);
+ else
+ where.add_match_condition ("subj_mimetype",
+ mimetypes_table.get_id (val), negated);
+ }
+
+ // URI
+ if (subject_template.uri != "")
+ {
+ string val = subject_template.uri;
+ bool like = parse_wildcard (ref val);
+ bool negated = parse_negation (ref val);
+ assert_no_noexpand (val, "uri");
+
+ if (like)
+ where.add_wildcard_condition ("subj_id", val, negated);
+ else
+ where.add_text_condition_subquery ("subj_id", val, negated);
+ }
+
+ // Origin
+ if (subject_template.origin != "")
+ {
+ string val = subject_template.origin;
+ bool like = parse_wildcard (ref val);
+ bool negated = parse_negation (ref val);
+ assert_no_noexpand (val, "subject origin");
+
+ if (like)
+ where.add_wildcard_condition (
+ "subj_origin", val, negated);
+ else
+ where.add_text_condition_subquery (
+ "subj_origin", val, negated);
+ }
+
+ // Text
+ if (subject_template.text != "")
+ {
+ // Negation, noexpand and prefix search aren't supported
+ // for subject texts, but "!", "+" and "*" are valid as
+ // plain text characters.
+ where.add_text_condition_subquery ("subj_text_id",
+ subject_template.text, false);
+ }
+
+ // Current URI
+ if (subject_template.current_uri != "")
+ {
+ string val = subject_template.current_uri;
+ bool like = parse_wildcard (ref val);
+ bool negated = parse_negation (ref val);
+ assert_no_noexpand (val, "current_uri");
+
+ if (like)
+ where.add_wildcard_condition (
+ "subj_id_current", val, negated);
+ else
+ where.add_text_condition_subquery (
+ "subj_id_current", val, negated);
+ }
+
+ // Subject storage
+ if (subject_template.storage != "")
+ {
+ string val = subject_template.storage;
+ assert_no_negation ("subject storage", val);
+ assert_no_wildcard ("subject storage", val);
+ assert_no_noexpand (val, "subject storage");
+ where.add_text_condition_subquery ("subj_storage_id", val);
+ }
+ }
+
+ return where;
+ }
+
+ // Used by get_where_clause_from_event_templates
+ /**
+ * If the value starts with the negation operator, throw an
+ * error.
+ */
+ protected void assert_no_negation (string field, string val)
+ throws EngineError
+ {
+ if (!val.has_prefix ("!"))
+ return;
+ string error_message =
+ "Field '%s' doesn't support negation".printf (field);
+ warning (error_message);
+ throw new EngineError.INVALID_ARGUMENT (error_message);
+ }
+
+ // Used by get_where_clause_from_event_templates
+ /**
+ * If the value starts with the negation operator, throw an
+ * error.
+ */
+ protected void assert_no_noexpand (string field, string val)
+ throws EngineError
+ {
+ if (!val.has_prefix ("+"))
+ return;
+ string error_message =
+ "Field '%s' doesn't support the no-expand operator".printf (field);
+ warning (error_message);
+ throw new EngineError.INVALID_ARGUMENT (error_message);
+ }
+
+ // Used by get_where_clause_from_event_templates
+ /**
+ * If the value ends with the wildcard character, throw an error.
+ */
+ protected void assert_no_wildcard (string field, string val)
+ throws EngineError
+ {
+ if (!val.has_suffix ("*"))
+ return;
+ string error_message =
+ "Field '%s' doesn't support prefix search".printf (field);
+ warning (error_message);
+ throw new EngineError.INVALID_ARGUMENT (error_message);
+ }
+
+ protected WhereClause get_where_clause_for_symbol (string table_name,
+ string symbol, TableLookup lookup_table) throws EngineError
+ {
+ string _symbol = symbol;
+ bool negated = parse_negation (ref _symbol);
+ bool noexpand = parse_noexpand (ref _symbol);
+ List<unowned string> symbols;
+ if (noexpand)
+ symbols = new List<unowned string> ();
+ else
+ symbols = Symbol.get_all_children (_symbol);
+ symbols.prepend (_symbol);
+
+ WhereClause subwhere = new WhereClause(
+ WhereClause.Type.OR, negated);
+
+ if (symbols.length () == 1)
+ {
+ subwhere.add_match_condition (table_name,
+ lookup_table.get_id (_symbol));
+ }
+ else
+ {
+ var sb = new StringBuilder ();
+ foreach (unowned string uri in symbols)
+ {
+ sb.append_printf ("%d,", lookup_table.get_id (uri));
+ }
+ sb.truncate (sb.len - 1);
+
+ string sql = "%s IN (%s)".printf(table_name, sb.str);
+ subwhere.add(sql);
+ }
+
+ return subwhere;
+ }
+
+ private void delete_from_cache (string table, int64 rowid)
+ {
+ TableLookup table_lookup;
+
+ if (table == "interpretation")
+ table_lookup = interpretations_table;
+ else if (table == "manifestation")
+ table_lookup = manifestations_table;
+ else if (table == "mimetype")
+ table_lookup = mimetypes_table;
+ else if (table == "actor")
+ table_lookup = actors_table;
+ else
+ return;
+
+ table_lookup.remove((int) rowid);
+ }
+
+}
+
+}
+
+// vim:expandtab:ts=4:sw=4
=== modified file 'src/engine.vala'
--- src/engine.vala 2012-01-25 17:37:55 +0000
+++ src/engine.vala 2012-02-02 19:02:53 +0000
@@ -29,35 +29,26 @@
using Zeitgeist.SQLite;
namespace Zeitgeist
-{ // FIXME: increase indentation once we're ok with breaking 'bzr diff'
-
-public class Engine : Object
-{
-
- public Zeitgeist.SQLite.ZeitgeistDatabase database { get; private set; }
+{
+
+public class Engine : DbReader
+{
+
public ExtensionStore extension_store;
private ExtensionCollection extension_collection;
- private unowned Sqlite.Database db;
-
- protected TableLookup interpretations_table;
- protected TableLookup manifestations_table;
- protected TableLookup mimetypes_table;
- protected TableLookup actors_table;
private uint32 last_id;
public Engine () throws EngineError
{
- database = new Zeitgeist.SQLite.ZeitgeistDatabase ();
+ Object (database: new Zeitgeist.SQLite.Database ());
+ }
+
+ construct
+ {
database.set_deletion_callback (delete_from_cache);
- db = database.database;
last_id = database.get_last_id ();
- interpretations_table = new TableLookup (database, "interpretation");
- manifestations_table = new TableLookup (database, "manifestation");
- mimetypes_table = new TableLookup (database, "mimetype");
- actors_table = new TableLookup (database, "actor");
-
extension_store = new ExtensionStore (this);
extension_collection = new ExtensionCollection (this);
}
@@ -67,495 +58,6 @@
return extension_collection.get_extension_names ();
}
- private Event get_event_from_row (Sqlite.Statement stmt, uint32 event_id)
- {
- Event event = new Event ();
- event.id = event_id;
- event.timestamp = stmt.column_int64 (EventViewRows.TIMESTAMP);
- event.interpretation = interpretations_table.get_value (
- stmt.column_int (EventViewRows.INTERPRETATION));
- event.manifestation = manifestations_table.get_value (
- stmt.column_int (EventViewRows.MANIFESTATION));
- event.actor = actors_table.get_value (
- stmt.column_int (EventViewRows.ACTOR));
- event.origin = stmt.column_text (
- EventViewRows.EVENT_ORIGIN_URI);
-
- // Load payload
- unowned uint8[] data = (uint8[]) stmt.column_blob(
- EventViewRows.PAYLOAD);
- data.length = stmt.column_bytes(EventViewRows.PAYLOAD);
- if (data != null)
- {
- event.payload = new ByteArray();
- event.payload.append(data);
- }
- return event;
- }
-
- private Subject get_subject_from_row (Sqlite.Statement stmt)
- {
- Subject subject = new Subject ();
- subject.uri = stmt.column_text (EventViewRows.SUBJECT_URI);
- subject.text = stmt.column_text (EventViewRows.SUBJECT_TEXT);
- subject.storage = stmt.column_text (EventViewRows.SUBJECT_STORAGE);
- subject.origin = stmt.column_text (EventViewRows.SUBJECT_ORIGIN_URI);
- subject.current_uri = stmt.column_text (
- EventViewRows.SUBJECT_CURRENT_URI);
- subject.interpretation = interpretations_table.get_value (
- stmt.column_int (EventViewRows.SUBJECT_INTERPRETATION));
- subject.manifestation = manifestations_table.get_value (
- stmt.column_int (EventViewRows.SUBJECT_MANIFESTATION));
- subject.mimetype = mimetypes_table.get_value (
- stmt.column_int (EventViewRows.SUBJECT_MIMETYPE));
- return subject;
- }
-
- public GenericArray<Event?> get_events(uint32[] event_ids,
- BusName? sender=null) throws EngineError
- {
- // TODO: Consider if we still want the cache. This should be done
- // once everything is working, since it adds unneeded complexity.
- // It'd also benchmark it again first, we may have better options
- // to enhance the performance of SQLite now, and event processing
- // will be faster now being C.
-
- if (event_ids.length == 0)
- return new GenericArray<Event?> ();
-
- var sql_event_ids = database.get_sql_string_from_event_ids (event_ids);
- string sql = """
- SELECT * FROM event_view
- WHERE id IN (%s)
- """.printf (sql_event_ids);
-
- Sqlite.Statement stmt;
- int rc = db.prepare_v2 (sql, -1, out stmt);
- database.assert_query_success (rc, "SQL error");
-
- var events = new HashTable<uint32, Event?> (direct_hash, direct_equal);
-
- // Create Events and Subjects from rows
- while ((rc = stmt.step ()) == Sqlite.ROW)
- {
- uint32 event_id = (uint32) stmt.column_int64 (EventViewRows.ID);
- Event? event = events.lookup (event_id);
- if (event == null)
- {
- event = get_event_from_row(stmt, event_id);
- events.insert (event_id, event);
- }
- Subject subject = get_subject_from_row(stmt);
- event.add_subject(subject);
- }
- if (rc != Sqlite.DONE)
- {
- throw new EngineError.DATABASE_ERROR ("Error: %d, %s\n",
- rc, db.errmsg ());
- }
-
- // Sort events according to the sequence of event_ids
- var results = new GenericArray<Event?> ();
- results.length = event_ids.length;
- int i = 0;
- foreach (var id in event_ids)
- {
- results.set(i++, events.lookup (id));
- }
-
- return results;
- }
-
- public uint32[] find_event_ids (TimeRange time_range,
- GenericArray<Event> event_templates,
- uint storage_state, uint max_events, uint result_type,
- BusName? sender=null) throws EngineError
- {
-
- WhereClause where = new WhereClause (WhereClause.Type.AND);
-
- /**
- * We are using the unary operator here to tell SQLite to not use
- * the index on the timestamp column at the first place. This is a
- * "fix" for (LP: #672965) based on some benchmarks, which suggest
- * a performance win, but we might not oversee all implications.
- * (See http://www.sqlite.org/optoverview.html, section 6.0).
- * -- Markus Korn, 29/11/2010
- */
- if (time_range.start != 0)
- where.add (("+timestamp >= %" + int64.FORMAT).printf(
- time_range.start));
- if (time_range.end != 0)
- where.add (("+timestamp <= %" + int64.FORMAT).printf(
- time_range.end));
-
- if (storage_state == StorageState.AVAILABLE ||
- storage_state == StorageState.NOT_AVAILABLE)
- {
- where.add ("(subj_storage_state=? OR subj_storage_state IS NULL)",
- storage_state.to_string ());
- }
- else if (storage_state != StorageState.ANY)
- {
- throw new EngineError.INVALID_ARGUMENT(
- "Unknown storage state '%u'".printf(storage_state));
- }
-
- WhereClause tpl_conditions = get_where_clause_from_event_templates (
- event_templates);
- where.extend (tpl_conditions);
- //if (!where.may_have_results ())
- // return new uint32[0];
-
- string sql = "SELECT id FROM event_view ";
- string where_sql = "";
- if (!where.is_empty ())
- {
- where_sql = "WHERE " + where.get_sql_conditions ();
- }
-
- switch (result_type)
- {
- case ResultType.MOST_RECENT_EVENTS:
- sql += where_sql + " ORDER BY timestamp DESC";
- break;
- case ResultType.LEAST_RECENT_EVENTS:
- sql += where_sql + " ORDER BY timestamp ASC";
- break;
- case ResultType.MOST_RECENT_EVENT_ORIGIN:
- sql += group_and_sort ("origin", where_sql, false);
- break;
- case ResultType.LEAST_RECENT_EVENT_ORIGIN:
- sql += group_and_sort ("origin", where_sql, true);
- break;
- case ResultType.MOST_POPULAR_EVENT_ORIGIN:
- sql += group_and_sort ("origin", where_sql, false, false);
- break;
- case ResultType.LEAST_POPULAR_EVENT_ORIGIN:
- sql += group_and_sort ("origin", where_sql, true, true);
- break;
- case ResultType.MOST_RECENT_SUBJECTS:
- sql += group_and_sort ("subj_id", where_sql, false);
- break;
- case ResultType.LEAST_RECENT_SUBJECTS:
- sql += group_and_sort ("subj_id", where_sql, true);
- break;
- case ResultType.MOST_POPULAR_SUBJECTS:
- sql += group_and_sort ("subj_id", where_sql, false, false);
- break;
- case ResultType.LEAST_POPULAR_SUBJECTS:
- sql += group_and_sort ("subj_id", where_sql, true, true);
- break;
- case ResultType.MOST_RECENT_CURRENT_URI:
- sql += group_and_sort ("subj_id_current", where_sql, false);
- break;
- case ResultType.LEAST_RECENT_CURRENT_URI:
- sql += group_and_sort ("subj_id_current", where_sql, true);
- break;
- case ResultType.MOST_POPULAR_CURRENT_URI:
- sql += group_and_sort ("subj_id_current", where_sql,
- false, false);
- break;
- case ResultType.LEAST_POPULAR_CURRENT_URI:
- sql += group_and_sort ("subj_id_current", where_sql,
- true, true);
- break;
- case ResultType.MOST_RECENT_ACTOR:
- sql += group_and_sort ("actor", where_sql, false);
- break;
- case ResultType.LEAST_RECENT_ACTOR:
- sql += group_and_sort ("actor", where_sql, true);
- break;
- case ResultType.MOST_POPULAR_ACTOR:
- sql += group_and_sort ("actor", where_sql, false, false);
- break;
- case ResultType.LEAST_POPULAR_ACTOR:
- sql += group_and_sort ("actor", where_sql, true, true);
- break;
- case ResultType.OLDEST_ACTOR:
- sql += group_and_sort ("actor", where_sql, true, null, "min");
- break;
- case ResultType.MOST_RECENT_ORIGIN:
- sql += group_and_sort ("subj_origin", where_sql, false);
- break;
- case ResultType.LEAST_RECENT_ORIGIN:
- sql += group_and_sort ("subj_origin", where_sql, true);
- break;
- case ResultType.MOST_POPULAR_ORIGIN:
- sql += group_and_sort ("subj_origin", where_sql, false, false);
- break;
- case ResultType.LEAST_POPULAR_ORIGIN:
- sql += group_and_sort ("subj_origin", where_sql, true, true);
- break;
- case ResultType.MOST_RECENT_SUBJECT_INTERPRETATION:
- sql += group_and_sort ("subj_interpretation", where_sql, false);
- break;
- case ResultType.LEAST_RECENT_SUBJECT_INTERPRETATION:
- sql += group_and_sort ("subj_interpretation", where_sql, true);
- break;
- case ResultType.MOST_POPULAR_SUBJECT_INTERPRETATION:
- sql += group_and_sort ("subj_interpretation", where_sql,
- false, false);
- break;
- case ResultType.LEAST_POPULAR_SUBJECT_INTERPRETATION:
- sql += group_and_sort ("subj_interpretation", where_sql,
- true, true);
- break;
- case ResultType.MOST_RECENT_MIMETYPE:
- sql += group_and_sort ("subj_mimetype", where_sql, false);
- break;
- case ResultType.LEAST_RECENT_MIMETYPE:
- sql += group_and_sort ("subj_mimetype", where_sql, true);
- break;
- case ResultType.MOST_POPULAR_MIMETYPE:
- sql += group_and_sort ("subj_mimetype", where_sql,
- false, false);
- break;
- case ResultType.LEAST_POPULAR_MIMETYPE:
- sql += group_and_sort ("subj_mimetype", where_sql,
- true, true);
- break;
- default:
- string error_message = "Invalid ResultType.";
- warning (error_message);
- throw new EngineError.INVALID_ARGUMENT (error_message);
- }
-
- int rc;
- Sqlite.Statement stmt;
-
- rc = db.prepare_v2 (sql, -1, out stmt);
- database.assert_query_success(rc, "SQL error");
-
- var arguments = where.get_bind_arguments ();
- for (int i = 0; i < arguments.length; ++i)
- stmt.bind_text (i + 1, arguments[i]);
-
-#if EXPLAIN_QUERIES
- database.explain_query (stmt);
-#endif
-
- uint32[] event_ids = {};
-
- while ((rc = stmt.step()) == Sqlite.ROW)
- {
- var event_id = (uint32) uint64.parse(
- stmt.column_text (EventViewRows.ID));
- // Events are supposed to be contiguous in the database
- if (event_ids.length == 0 || event_ids[event_ids.length-1] != event_id) {
- event_ids += event_id;
- if (event_ids.length == max_events) break;
- }
- }
- if (rc != Sqlite.DONE && rc != Sqlite.ROW)
- {
- string error_message = "Error in find_event_ids: %d, %s".printf (
- rc, db.errmsg ());
- warning (error_message);
- throw new EngineError.DATABASE_ERROR (error_message);
- }
-
- return event_ids;
- }
-
- public GenericArray<Event?> find_events (TimeRange time_range,
- GenericArray<Event> event_templates,
- uint storage_state, uint max_events, uint result_type,
- BusName? sender=null) throws EngineError
- {
- return get_events (find_event_ids (time_range, event_templates,
- storage_state, max_events, result_type));
- }
-
- private struct RelatedUri {
- public uint32 id;
- public int64 timestamp;
- public string uri;
- public int32 counter;
- }
-
- public string[] find_related_uris (TimeRange time_range,
- GenericArray<Event> event_templates,
- GenericArray<Event> result_event_templates,
- uint storage_state, uint max_results, uint result_type,
- BusName? sender=null) throws EngineError
- {
- /**
- * Return a list of subject URIs commonly used together with events
- * matching the given template, considering data from within the
- * indicated timerange.
- * Only URIs for subjects matching the indicated `result_event_templates`
- * and `result_storage_state` are returned.
- */
- if (result_type == ResultType.MOST_RECENT_EVENTS ||
- result_type == ResultType.LEAST_RECENT_EVENTS)
- {
-
- // We pick out the ids for relational event so we can set them as
- // roots the ids are taken from the events that match the
- // events_templates
- uint32[] ids = find_event_ids (time_range, event_templates,
- storage_state, 0, ResultType.LEAST_RECENT_EVENTS);
-
- if (event_templates.length > 0 && ids.length == 0)
- {
- throw new EngineError.INVALID_ARGUMENT (
- "No results found for the event_templates");
- }
-
- // Pick out the result_ids for the filtered results we would like to
- // take into account the ids are taken from the events that match
- // the result_event_templates if no result_event_templates are set we
- // consider all results as allowed
- uint32[] result_ids;
- result_ids = find_event_ids (time_range, result_event_templates,
- storage_state, 0, ResultType.LEAST_RECENT_EVENTS);
-
- // From here we create several graphs with the maximum depth of 2
- // and push all the nodes and vertices (events) in one pot together
-
- uint32[] pot = new uint32[ids.length + result_ids.length];
-
- for (uint32 i=0; i < ids.length; i++)
- pot[i] = ids[i];
- for (uint32 i=0; i < result_ids.length; i++)
- pot[ids.length + i] = result_ids[ids.length + i];
-
- Sqlite.Statement stmt;
-
- var sql_event_ids = database.get_sql_string_from_event_ids (pot);
- string sql = """
- SELECT id, timestamp, subj_uri FROM event_view
- WHERE id IN (%s) ORDER BY timestamp ASC
- """.printf (sql_event_ids);
-
- int rc = db.prepare_v2 (sql, -1, out stmt);
-
- database.assert_query_success(rc, "SQL error");
-
- // FIXME: fix this ugly code
- var temp_related_uris = new GenericArray<RelatedUri?>();
-
- while ((rc = stmt.step()) == Sqlite.ROW)
- {
- RelatedUri ruri = RelatedUri(){
- id = (uint32) uint64.parse(stmt.column_text (0)),
- timestamp = stmt.column_int64 (1),
- uri = stmt.column_text (2),
- counter = 0
- };
- temp_related_uris.add (ruri);
- }
-
- // RelatedUri[] related_uris = new RelatedUri[temp_related_uris.length];
- // for (int i=0; i<related_uris.length; i++)
- // related_uris[i] = temp_related_uris[i];
-
- if (rc != Sqlite.DONE)
- {
- string error_message =
- "Error in find_related_uris: %d, %s".printf (
- rc, db.errmsg ());
- warning (error_message);
- throw new EngineError.DATABASE_ERROR (error_message);
- }
-
- var uri_counter = new HashTable<string, RelatedUri?>(
- str_hash, str_equal);
-
- for (int i = 0; i < temp_related_uris.length; i++)
- {
- var window = new GenericArray<unowned RelatedUri?>();
-
- bool count_in_window = false;
- for (int j = int.max (0, i - 5);
- j < int.min (i, temp_related_uris.length);
- j++)
- {
- window.add(temp_related_uris[j]);
- if (temp_related_uris[j].id in ids)
- count_in_window = true;
- }
-
- if (count_in_window)
- {
- for (int j = 0; j < window.length; j++)
- {
- if (uri_counter.lookup (window[j].uri) == null)
- {
- RelatedUri ruri = RelatedUri ()
- {
- id = window[j].id,
- timestamp = window[j].timestamp,
- uri = window[j].uri,
- counter = 0
- };
- uri_counter.insert (window[j].uri, ruri);
- }
- uri_counter.lookup (window[j].uri).counter++;
- if (uri_counter.lookup (window[j].uri).timestamp
- < window[j].timestamp)
- {
- uri_counter.lookup (window[j].uri).timestamp =
- window[j].timestamp;
- }
- }
- }
- }
-
-
- // We have the big hashtable with the structs, now we sort them by
- // most used and limit the result then sort again
- List<RelatedUri?> temp_ruris = new List<RelatedUri?>();
- List<RelatedUri?> values = new List<RelatedUri?>();
-
- foreach (var uri in uri_counter.get_values())
- values.append(uri);
-
- values.sort ((a, b) => a.counter - b.counter);
- values.sort ((a, b) => {
- int64 delta = a.timestamp - b.timestamp;
- if (delta < 0) return 1;
- else if (delta > 0) return -1;
- else return 0;
- });
-
- foreach (RelatedUri ruri in values)
- {
- if (temp_ruris.length() < max_results)
- temp_ruris.append(ruri);
- else
- break;
- }
-
- // Sort by recency
- if (result_type == 1)
- temp_ruris.sort ((a, b) => {
- int64 delta = a.timestamp - b.timestamp;
- if (delta < 0) return 1;
- else if (delta > 0) return -1;
- else return 0;});
-
- string[] results = new string[temp_ruris.length()];
-
- int i = 0;
- foreach (var uri in temp_ruris)
- {
- results[i] = uri.uri;
- stdout.printf("%i %lld %s\n", uri.counter,
- uri.timestamp,
- uri.uri);
- i++;
- }
-
- return results;
- }
- else
- {
- throw new EngineError.DATABASE_ERROR ("Unsupported ResultType.");
- }
- }
-
public uint32[] insert_events (GenericArray<Event> events,
BusName? sender=null) throws EngineError
{
@@ -786,366 +288,14 @@
* After executing this method on an Engine instance, no other function
* of said instance may be called.
*/
- public void close ()
+ public override void close ()
{
// We delete the ExtensionCollection here so that it unloads
// all extensions and they get a chance to access the database
// (including through ExtensionStore) before it's closed.
extension_collection = null;
- database.close ();
- }
-
- // Used by find_event_ids
- private string group_and_sort (string field, string where_sql,
- bool time_asc=false, bool? count_asc=null,
- string aggregation_type="max")
- {
- string time_sorting = (time_asc) ? "ASC" : "DESC";
- string aggregation_sql = "";
- string order_sql = "";
-
- if (count_asc != null)
- {
- aggregation_sql = ", COUNT(%s) AS num_events".printf (field);
- order_sql = "num_events %s,".printf ((count_asc) ? "ASC" : "DESC");
- }
-
- return """
- NATURAL JOIN (
- SELECT %s,
- %s(timestamp) AS timestamp
- %s
- FROM event_view %s
- GROUP BY %s)
- GROUP BY %s
- ORDER BY %s timestamp %s
- """.printf (
- field,
- aggregation_type,
- aggregation_sql,
- where_sql,
- field,
- field,
- order_sql, time_sorting);
- }
-
- // Used by find_event_ids
- private WhereClause get_where_clause_from_event_templates (
- GenericArray<Event> templates) throws EngineError
- {
- WhereClause where = new WhereClause (WhereClause.Type.OR);
- for (int i = 0; i < templates.length; ++i)
- {
- Event event_template = templates[i];
- where.extend (
- get_where_clause_from_event_template (event_template));
- }
- return where;
- }
-
- // Used by get_where_clause_from_event_templates
- private WhereClause get_where_clause_from_event_template (Event template)
- throws EngineError
- {
- WhereClause where = new WhereClause (WhereClause.Type.AND);
-
- // Event ID
- if (template.id != 0)
- where.add ("id=?", template.id.to_string());
-
- // Interpretation
- if (template.interpretation != "")
- {
- assert_no_wildcard ("interpretation", template.interpretation);
- WhereClause subwhere = get_where_clause_for_symbol (
- "interpretation", template.interpretation,
- interpretations_table);
- if (!subwhere.is_empty ())
- where.extend (subwhere);
- }
-
- // Manifestation
- if (template.manifestation != "")
- {
- assert_no_wildcard ("manifestation", template.interpretation);
- WhereClause subwhere = get_where_clause_for_symbol (
- "manifestation", template.manifestation,
- manifestations_table);
- if (!subwhere.is_empty ())
- where.extend (subwhere);
- }
-
- // Actor
- if (template.actor != "")
- {
- string val = template.actor;
- bool like = parse_wildcard (ref val);
- bool negated = parse_negation (ref val);
-
- if (like)
- where.add_wildcard_condition ("actor", val, negated);
- else
- where.add_match_condition ("actor",
- actors_table.get_id (val), negated);
- }
-
- // Origin
- if (template.origin != "")
- {
- string val = template.origin;
- bool like = parse_wildcard (ref val);
- bool negated = parse_negation (ref val);
- assert_no_noexpand (val, "origin");
-
- if (like)
- where.add_wildcard_condition ("origin", val, negated);
- else
- where.add_text_condition_subquery ("origin", val, negated);
- }
-
- // Subject templates within the same event template are AND'd
- // See LP bug #592599.
- for (int i = 0; i < template.num_subjects(); ++i)
- {
- Subject subject_template = template.subjects[i];
-
- // Subject interpretation
- if (subject_template.interpretation != "")
- {
- assert_no_wildcard ("subject interpretation",
- template.interpretation);
- WhereClause subwhere = get_where_clause_for_symbol (
- "subj_interpretation", subject_template.interpretation,
- interpretations_table);
- if (!subwhere.is_empty ())
- where.extend (subwhere);
- }
-
- // Subject manifestation
- if (subject_template.manifestation != "")
- {
- assert_no_wildcard ("subject manifestation",
- subject_template.manifestation);
- WhereClause subwhere = get_where_clause_for_symbol (
- "subj_manifestation", subject_template.manifestation,
- manifestations_table);
- if (!subwhere.is_empty ())
- where.extend (subwhere);
- }
-
- // Mime-Type
- if (subject_template.mimetype != "")
- {
- string val = subject_template.mimetype;
- bool like = parse_wildcard (ref val);
- bool negated = parse_negation (ref val);
- assert_no_noexpand (val, "mime-type");
-
- if (like)
- where.add_wildcard_condition (
- "subj_mimetype", val, negated);
- else
- where.add_match_condition ("subj_mimetype",
- mimetypes_table.get_id (val), negated);
- }
-
- // URI
- if (subject_template.uri != "")
- {
- string val = subject_template.uri;
- bool like = parse_wildcard (ref val);
- bool negated = parse_negation (ref val);
- assert_no_noexpand (val, "uri");
-
- if (like)
- where.add_wildcard_condition ("subj_id", val, negated);
- else
- where.add_text_condition_subquery ("subj_id", val, negated);
- }
-
- // Origin
- if (subject_template.origin != "")
- {
- string val = subject_template.origin;
- bool like = parse_wildcard (ref val);
- bool negated = parse_negation (ref val);
- assert_no_noexpand (val, "subject origin");
-
- if (like)
- where.add_wildcard_condition (
- "subj_origin", val, negated);
- else
- where.add_text_condition_subquery (
- "subj_origin", val, negated);
- }
-
- // Text
- if (subject_template.text != "")
- {
- // Negation, noexpand and prefix search aren't supported
- // for subject texts, but "!", "+" and "*" are valid as
- // plain text characters.
- where.add_text_condition_subquery ("subj_text_id",
- subject_template.text, false);
- }
-
- // Current URI
- if (subject_template.current_uri != "")
- {
- string val = subject_template.current_uri;
- bool like = parse_wildcard (ref val);
- bool negated = parse_negation (ref val);
- assert_no_noexpand (val, "current_uri");
-
- if (like)
- where.add_wildcard_condition (
- "subj_id_current", val, negated);
- else
- where.add_text_condition_subquery (
- "subj_id_current", val, negated);
- }
-
- // Subject storage
- if (subject_template.storage != "")
- {
- string val = subject_template.storage;
- assert_no_negation ("subject storage", val);
- assert_no_wildcard ("subject storage", val);
- assert_no_noexpand (val, "subject storage");
- where.add_text_condition_subquery ("subj_storage_id", val);
- }
- }
-
- return where;
- }
-
- // Used by get_where_clause_from_event_templates
- /**
- * Check if the value starts with the negation operator. If it does,
- * remove the operator from the value and return true. Otherwise,
- * return false.
- */
- public static bool parse_negation (ref string val)
- {
- if (!val.has_prefix ("!"))
- return false;
- val = val.substring (1);
- return true;
- }
-
- // Used by get_where_clause_from_event_templates
- /**
- * If the value starts with the negation operator, throw an
- * error.
- */
- protected void assert_no_negation (string field, string val)
- throws EngineError
- {
- if (!val.has_prefix ("!"))
- return;
- string error_message =
- "Field '%s' doesn't support negation".printf (field);
- warning (error_message);
- throw new EngineError.INVALID_ARGUMENT (error_message);
- }
-
- // Used by get_where_clause_from_event_templates
- /**
- * Check if the value starts with the noexpand operator. If it does,
- * remove the operator from the value and return true. Otherwise,
- * return false.
- *
- * Check for the negation operator before calling this function.
- */
- public static bool parse_noexpand (ref string val)
- {
- if (!val.has_prefix ("+"))
- return false;
- val = val.substring (1);
- return true;
- }
-
- // Used by get_where_clause_from_event_templates
- /**
- * If the value starts with the negation operator, throw an
- * error.
- */
- protected void assert_no_noexpand (string field, string val)
- throws EngineError
- {
- if (!val.has_prefix ("+"))
- return;
- string error_message =
- "Field '%s' doesn't support the no-expand operator".printf (field);
- warning (error_message);
- throw new EngineError.INVALID_ARGUMENT (error_message);
- }
-
- // Used by get_where_clause_from_event_templates
- /**
- * Check if the value ends with the wildcard character. If it does,
- * remove the wildcard character from the value and return true.
- * Otherwise, return false.
- */
- public static bool parse_wildcard (ref string val)
- {
- if (!val.has_suffix ("*"))
- return false;
- unowned uint8[] val_data = val.data;
- val_data[val_data.length-1] = '\0';
- return true;
- }
-
- // Used by get_where_clause_from_event_templates
- /**
- * If the value ends with the wildcard character, throw an error.
- */
- protected void assert_no_wildcard (string field, string val)
- throws EngineError
- {
- if (!val.has_suffix ("*"))
- return;
- string error_message =
- "Field '%s' doesn't support prefix search".printf (field);
- warning (error_message);
- throw new EngineError.INVALID_ARGUMENT (error_message);
- }
-
- protected WhereClause get_where_clause_for_symbol (string table_name,
- string symbol, TableLookup lookup_table) throws EngineError
- {
- string _symbol = symbol;
- bool negated = parse_negation (ref _symbol);
- bool noexpand = parse_noexpand (ref _symbol);
- List<unowned string> symbols;
- if (noexpand)
- symbols = new List<unowned string> ();
- else
- symbols = Symbol.get_all_children (_symbol);
- symbols.prepend (_symbol);
-
- WhereClause subwhere = new WhereClause(
- WhereClause.Type.OR, negated);
-
- if (symbols.length () == 1)
- {
- subwhere.add_match_condition (table_name,
- lookup_table.get_id (_symbol));
- }
- else
- {
- var sb = new StringBuilder ();
- foreach (unowned string uri in symbols)
- {
- sb.append_printf ("%d,", lookup_table.get_id (uri));
- }
- sb.truncate (sb.len - 1);
-
- string sql = "%s IN (%s)".printf(table_name, sb.str);
- subwhere.add(sql);
- }
-
- return subwhere;
+
+ base.close ();
}
private void handle_move_event (Event event)
=== modified file 'src/extension-store.vala'
--- src/extension-store.vala 2012-01-26 10:08:09 +0000
+++ src/extension-store.vala 2012-02-02 19:02:53 +0000
@@ -25,7 +25,7 @@
public class ExtensionStore : Object
{
- private Zeitgeist.SQLite.ZeitgeistDatabase database;
+ private Zeitgeist.SQLite.Database database;
private unowned Sqlite.Database db;
private Sqlite.Statement storage_stmt;
private Sqlite.Statement retrieval_stmt;
=== modified file 'src/sql-schema.vala'
--- src/sql-schema.vala 2012-01-30 18:14:03 +0000
+++ src/sql-schema.vala 2012-02-02 19:02:53 +0000
@@ -70,7 +70,7 @@
}
}
- private static int get_schema_version (Sqlite.Database database)
+ public static int get_schema_version (Sqlite.Database database)
{
var sql = "SELECT version FROM schema_version WHERE schema='core'";
int schema_version = -1;
=== modified file 'src/sql.vala'
--- src/sql.vala 2012-01-25 17:37:55 +0000
+++ src/sql.vala 2012-02-02 19:02:53 +0000
@@ -51,8 +51,10 @@
public delegate void DeletionCallback (string table, int64 rowid);
- public class ZeitgeistDatabase : Object
+ public class Database : Object
{
+ private const int DEFAULT_OPEN_FLAGS =
+ Sqlite.OPEN_READWRITE | Sqlite.OPEN_CREATE;
public Sqlite.Statement event_insertion_stmt;
public Sqlite.Statement id_retrieval_stmt;
@@ -64,12 +66,28 @@
public Sqlite.Database database;
private DeletionCallback? deletion_callback = null;
+ private bool is_read_only;
- public ZeitgeistDatabase () throws EngineError
+ public Database () throws EngineError
{
open_database (true);
- prepare_queries ();
+ prepare_read_queries ();
+ prepare_modification_queries ();
+
+ // Register a data change notification callback to look for
+ // deletions, so we can keep the TableLookups up to date.
+ database.update_hook (update_callback);
+ }
+
+ public Database.read_only () throws EngineError
+ {
+ is_read_only = true;
+ open_database (false);
+
+ prepare_read_queries ();
+ // not initializing the modification queries will let us find
+ // issues more easily
// Register a data change notification callback to look for
// deletions, so we can keep the TableLookups up to date.
@@ -79,9 +97,10 @@
private void open_database (bool retry)
throws EngineError
{
+ int flags = is_read_only ? Sqlite.OPEN_READONLY : DEFAULT_OPEN_FLAGS;
int rc = Sqlite.Database.open_v2 (
Utils.get_database_file_path (),
- out database);
+ out database, flags);
if (rc == Sqlite.OK)
{
@@ -89,7 +108,19 @@
{
// Error (like a malformed database) may not be exposed
// until we try to operate on the database.
- DatabaseSchema.ensure_schema (database);
+ if (is_read_only)
+ {
+ int ver = DatabaseSchema.get_schema_version (database);
+ if (ver != DatabaseSchema.CORE_SCHEMA_VERSION)
+ {
+ throw new EngineError.DATABASE_CANTOPEN (
+ "Unable to open database");
+ }
+ }
+ else
+ {
+ DatabaseSchema.ensure_schema (database);
+ }
}
catch (EngineError err)
{
@@ -296,7 +327,22 @@
}
}
- private void prepare_queries () throws EngineError
+ private void prepare_read_queries () throws EngineError
+ {
+ int rc;
+ string sql;
+
+ // Event ID retrieval statement
+ sql = """
+ SELECT id FROM event
+ WHERE timestamp=? AND interpretation=? AND
+ manifestation=? AND actor=?
+ """;
+ rc = database.prepare_v2 (sql, -1, out id_retrieval_stmt);
+ assert_query_success (rc, "Event ID retrieval query error");
+ }
+
+ private void prepare_modification_queries () throws EngineError
{
int rc;
string sql;
@@ -324,15 +370,6 @@
rc = database.prepare_v2 (sql, -1, out event_insertion_stmt);
assert_query_success (rc, "Insertion query error");
- // Event ID retrieval statement
- sql = """
- SELECT id FROM event
- WHERE timestamp=? AND interpretation=? AND
- manifestation=? AND actor=?
- """;
- rc = database.prepare_v2 (sql, -1, out id_retrieval_stmt);
- assert_query_success (rc, "Event ID retrieval query error");
-
// Move handling statment
sql = """
UPDATE event
=== modified file 'src/table-lookup.vala'
--- src/table-lookup.vala 2012-01-26 10:08:09 +0000
+++ src/table-lookup.vala 2012-02-02 19:02:53 +0000
@@ -34,7 +34,7 @@
private HashTable<string, int> value_to_id;
private Sqlite.Statement insertion_stmt;
- public TableLookup (ZeitgeistDatabase database, string table_name)
+ public TableLookup (Database database, string table_name)
{
db = database.database;
table = table_name;
=== modified file 'src/utils.vala'
--- src/utils.vala 2011-12-31 15:57:15 +0000
+++ src/utils.vala 2012-02-02 19:02:53 +0000
@@ -127,6 +127,50 @@
File dbfile = File.new_for_path (get_database_file_path ());
dbfile.set_display_name (get_database_file_retire_name ());
}
+
+ /**
+ * Check if the value starts with the negation operator. If it does,
+ * remove the operator from the value and return true. Otherwise,
+ * return false.
+ */
+ public static bool parse_negation (ref string val)
+ {
+ if (!val.has_prefix ("!"))
+ return false;
+ val = val.substring (1);
+ return true;
+ }
+
+ /**
+ * Check if the value starts with the noexpand operator. If it does,
+ * remove the operator from the value and return true. Otherwise,
+ * return false.
+ *
+ * Check for the negation operator before calling this function.
+ */
+ public static bool parse_noexpand (ref string val)
+ {
+ if (!val.has_prefix ("+"))
+ return false;
+ val = val.substring (1);
+ return true;
+ }
+
+
+ /**
+ * Check if the value ends with the wildcard character. If it does,
+ * remove the wildcard character from the value and return true.
+ * Otherwise, return false.
+ */
+ public static bool parse_wildcard (ref string val)
+ {
+ if (!val.has_suffix ("*"))
+ return false;
+ unowned uint8[] val_data = val.data;
+ val_data[val_data.length-1] = '\0';
+ return true;
+ }
+
}
}
=== modified file 'src/zeitgeist-daemon.vala'
--- src/zeitgeist-daemon.vala 2012-01-26 10:08:09 +0000
+++ src/zeitgeist-daemon.vala 2012-02-02 19:02:53 +0000
@@ -458,7 +458,7 @@
var lm = LogLevelFlags.LEVEL_MESSAGE;
var lw = LogLevelFlags.LEVEL_WARNING;
var lc = LogLevelFlags.LEVEL_CRITICAL;
- switch (log_level)
+ switch (log_level.up ())
{
case "DEBUG":
discarded = 0;
Follow ups