Merge lp:~mhr3/zeitgeist/refactoring into lp:~zeitgeist/zeitgeist/bluebird

Proposed by Michal Hruby
Status: Merged
Merged at revision: 382
Proposed branch: lp:~mhr3/zeitgeist/refactoring
Merge into: lp:~zeitgeist/zeitgeist/bluebird
Diff against target: 2390 lines (+1055/-968)
16 files modified
extensions/fts.vala (+14/-30)
extensions/storage-monitor.vala (+1/-1)
src/Makefile.am (+1/-0)
src/datamodel.vala (+2/-2)
src/db-reader.vala (+894/-0)
src/engine.vala (+16/-884)
src/extension-store.vala (+1/-1)
src/remote.vala (+4/-3)
src/sql-schema.vala (+1/-1)
src/sql.vala (+52/-15)
src/table-lookup.vala (+1/-1)
src/utils.vala (+53/-0)
src/zeitgeist-daemon.vala (+1/-1)
test/direct/Makefile.am (+1/-0)
test/direct/query-operators-test.vala (+11/-27)
test/direct/table-lookup-test.vala (+2/-2)
To merge this branch: bzr merge lp:~mhr3/zeitgeist/refactoring
Reviewer Review Type Date Requested Status
Siegfried Gevatter Approve
Review via email: mp+91334@code.launchpad.net

Description of the change

Refactor Engine class, so it's easier to do read-only DB access.

To post a comment you must log in.
lp:~mhr3/zeitgeist/refactoring updated
382. By Michal Hruby

Keep the deletion notification in DbReader only

383. By Michal Hruby

Handle null strings properly

384. By Michal Hruby

A small fix to interface definition

385. By Michal Hruby

Fix tests

386. By Michal Hruby

Get rid of the Indexer proxy workaround

Revision history for this message
Siegfried Gevatter (rainct) wrote :

Good work.

You aren't setting `is_read_only' to false anywhere. Other than that, looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'extensions/fts.vala'
2--- extensions/fts.vala 2012-01-02 19:30:04 +0000
3+++ extensions/fts.vala 2012-02-05 18:08:19 +0000
4@@ -24,13 +24,13 @@
5 [DBus (name = "org.gnome.zeitgeist.Index")]
6 public interface RemoteSearchEngine: Object
7 {
8- [DBus (signature = "a(asaasay)u")]
9- public abstract async Variant search (
10+ public abstract async void search (
11 string query_string,
12 [DBus (signature = "(xx)")] Variant time_range,
13 [DBus (signature = "a(asaasay)")] Variant filter_templates,
14 uint offset, uint count, uint result_type,
15- [DBus (signature = "a(asaasay)")] out Variant events) throws Error;
16+ [DBus (signature = "a(asaasay)")] out Variant events,
17+ out uint matches) throws Error;
18 }
19
20 /* Because of a Vala bug we have to define the proxy interface outside of
21@@ -39,12 +39,13 @@
22 [DBus (name = "org.gnome.zeitgeist.SimpleIndexer")]
23 public interface RemoteSimpleIndexer : Object
24 {
25- [DBus (signature = "a(asaasay)u")]
26- public abstract async Variant search (
27+ public abstract async void search (
28 string query_string,
29 [DBus (signature = "(xx)")] Variant time_range,
30 [DBus (signature = "a(asaasay)")] Variant filter_templates,
31- uint offset, uint count, uint result_type) throws Error;
32+ uint offset, uint count, uint result_type,
33+ [DBus (signature = "a(asaasay)")] out Variant events,
34+ out uint matches) throws Error;
35 }
36 */
37
38@@ -103,11 +104,9 @@
39 }
40 }
41
42- /* This whole method is one huge workaround for an issue with Vala
43- * enclosing all out/return parameters in a TUPLE variant */
44- public async Variant search (string query_string, Variant time_range,
45+ public async void search (string query_string, Variant time_range,
46 Variant filter_templates, uint offset, uint count, uint result_type,
47- out Variant events) throws Error
48+ out Variant events, out uint matches) throws Error
49 {
50 if (siin == null || !(siin is DBusProxy))
51 {
52@@ -116,26 +115,11 @@
53 "Not connected to SimpleIndexer");
54 }
55 var timer = new Timer ();
56- DBusProxy proxy = (DBusProxy) siin;
57- var b = new VariantBuilder (new VariantType ("(s(xx)a(asaasay)uuu)"));
58- b.add ("s", query_string);
59- b.add_value (time_range);
60- b.add_value (filter_templates);
61- b.add ("u", offset);
62- b.add ("u", count);
63- b.add ("u", result_type);
64- var result = yield proxy.call ("Search", b.end (), 0, -1, null);
65- events = result.get_child_value (0);
66- /* FIXME: this somehow doesn't work :(
67- * but it's fixable in a similar way as this method's signature
68- * is done */
69- /*
70- var result = yield siin.search (query_string, time_range,
71- filter_templates, offset, count, result_type);
72- */
73- debug ("Got %u results from indexer (in %f seconds)",
74- (uint) events.n_children (), timer.elapsed ());
75- return result.get_child_value (1);
76+ yield siin.search (query_string, time_range, filter_templates,
77+ offset, count, result_type,
78+ out events, out matches);
79+ debug ("Got %u[/%u] results from indexer (in %f seconds)",
80+ (uint) events.n_children (), matches, timer.elapsed ());
81 }
82
83 }
84
85=== modified file 'extensions/storage-monitor.vala'
86--- extensions/storage-monitor.vala 2012-01-27 13:34:18 +0000
87+++ extensions/storage-monitor.vala 2012-02-05 18:08:19 +0000
88@@ -106,7 +106,7 @@
89 "dav", "davs", "ftp", "http", "https", "mailto",
90 "sftp", "smb", "ssh" };
91
92- private Zeitgeist.SQLite.ZeitgeistDatabase database;
93+ private Zeitgeist.SQLite.Database database;
94 private unowned Sqlite.Database db;
95 private uint registration_id;
96
97
98=== modified file 'src/Makefile.am'
99--- src/Makefile.am 2011-12-31 18:15:06 +0000
100+++ src/Makefile.am 2012-02-05 18:08:19 +0000
101@@ -31,6 +31,7 @@
102 zeitgeist_daemon_VALASOURCES = \
103 zeitgeist-daemon.vala \
104 datamodel.vala \
105+ db-reader.vala \
106 engine.vala \
107 remote.vala \
108 extension.vala \
109
110=== modified file 'src/datamodel.vala'
111--- src/datamodel.vala 2012-01-25 17:37:55 +0000
112+++ src/datamodel.vala 2012-02-05 18:08:19 +0000
113@@ -268,7 +268,7 @@
114 {
115 var matches = false;
116 var parsed = template_property;
117- var is_negated = Engine.parse_negation (ref parsed);
118+ var is_negated = Utils.parse_negation (ref parsed);
119
120 if (parsed == "")
121 {
122@@ -283,7 +283,7 @@
123 {
124 matches = true;
125 }
126- else if (can_wildcard && Engine.parse_wildcard (ref parsed))
127+ else if (can_wildcard && Utils.parse_wildcard (ref parsed))
128 {
129 if (property.has_prefix (parsed)) matches = true;
130 }
131
132=== added file 'src/db-reader.vala'
133--- src/db-reader.vala 1970-01-01 00:00:00 +0000
134+++ src/db-reader.vala 2012-02-05 18:08:19 +0000
135@@ -0,0 +1,894 @@
136+/* db-reader.vala
137+ *
138+ * Copyright © 2011 Collabora Ltd.
139+ * By Siegfried-Angel Gevatter Pujals <siegfried@gevatter.com>
140+ * By Seif Lotfy <seif@lotfy.com>
141+ * Copyright © 2011 Canonical Ltd.
142+ * By Michal Hruby <michal.hruby@canonical.com>
143+ *
144+ * Based upon a Python implementation (2009-2011) by:
145+ * Markus Korn <thekorn@gmx.net>
146+ * Mikkel Kamstrup Erlandsen <mikkel.kamstrup@gmail.com>
147+ * Seif Lotfy <seif@lotfy.com>
148+ * Siegfried-Angel Gevatter Pujals <siegfried@gevatter.com>
149+ *
150+ * This program is free software: you can redistribute it and/or modify
151+ * it under the terms of the GNU Lesser General Public License as published by
152+ * the Free Software Foundation, either version 2.1 of the License, or
153+ * (at your option) any later version.
154+ *
155+ * This program is distributed in the hope that it will be useful,
156+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
157+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
158+ * GNU General Public License for more details.
159+ *
160+ * You should have received a copy of the GNU Lesser General Public License
161+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
162+ *
163+ */
164+
165+using Zeitgeist;
166+using Zeitgeist.SQLite;
167+using Zeitgeist.Utils;
168+
169+namespace Zeitgeist
170+{
171+
172+public class DbReader : Object
173+{
174+
175+ public Zeitgeist.SQLite.Database database { get; construct; }
176+ protected unowned Sqlite.Database db;
177+
178+ protected TableLookup interpretations_table;
179+ protected TableLookup manifestations_table;
180+ protected TableLookup mimetypes_table;
181+ protected TableLookup actors_table;
182+
183+ public DbReader () throws EngineError
184+ {
185+ Object (database: new Zeitgeist.SQLite.Database.read_only ());
186+ }
187+
188+ construct
189+ {
190+ database.set_deletion_callback (delete_from_cache);
191+ db = database.database;
192+
193+ interpretations_table = new TableLookup (database, "interpretation");
194+ manifestations_table = new TableLookup (database, "manifestation");
195+ mimetypes_table = new TableLookup (database, "mimetype");
196+ actors_table = new TableLookup (database, "actor");
197+ }
198+
199+ protected Event get_event_from_row (Sqlite.Statement stmt, uint32 event_id)
200+ {
201+ Event event = new Event ();
202+ event.id = event_id;
203+ event.timestamp = stmt.column_int64 (EventViewRows.TIMESTAMP);
204+ event.interpretation = interpretations_table.get_value (
205+ stmt.column_int (EventViewRows.INTERPRETATION));
206+ event.manifestation = manifestations_table.get_value (
207+ stmt.column_int (EventViewRows.MANIFESTATION));
208+ event.actor = actors_table.get_value (
209+ stmt.column_int (EventViewRows.ACTOR));
210+ event.origin = stmt.column_text (
211+ EventViewRows.EVENT_ORIGIN_URI);
212+
213+ // Load payload
214+ unowned uint8[] data = (uint8[]) stmt.column_blob(
215+ EventViewRows.PAYLOAD);
216+ data.length = stmt.column_bytes(EventViewRows.PAYLOAD);
217+ if (data != null)
218+ {
219+ event.payload = new ByteArray();
220+ event.payload.append(data);
221+ }
222+ return event;
223+ }
224+
225+ protected Subject get_subject_from_row (Sqlite.Statement stmt)
226+ {
227+ Subject subject = new Subject ();
228+ subject.uri = stmt.column_text (EventViewRows.SUBJECT_URI);
229+ subject.text = stmt.column_text (EventViewRows.SUBJECT_TEXT);
230+ subject.storage = stmt.column_text (EventViewRows.SUBJECT_STORAGE);
231+ subject.origin = stmt.column_text (EventViewRows.SUBJECT_ORIGIN_URI);
232+ subject.current_uri = stmt.column_text (
233+ EventViewRows.SUBJECT_CURRENT_URI);
234+ subject.interpretation = interpretations_table.get_value (
235+ stmt.column_int (EventViewRows.SUBJECT_INTERPRETATION));
236+ subject.manifestation = manifestations_table.get_value (
237+ stmt.column_int (EventViewRows.SUBJECT_MANIFESTATION));
238+ subject.mimetype = mimetypes_table.get_value (
239+ stmt.column_int (EventViewRows.SUBJECT_MIMETYPE));
240+ return subject;
241+ }
242+
243+ public GenericArray<Event?> get_events(uint32[] event_ids,
244+ BusName? sender=null) throws EngineError
245+ {
246+ // TODO: Consider if we still want the cache. This should be done
247+ // once everything is working, since it adds unneeded complexity.
248+ // It'd also benchmark it again first, we may have better options
249+ // to enhance the performance of SQLite now, and event processing
250+ // will be faster now being C.
251+
252+ if (event_ids.length == 0)
253+ return new GenericArray<Event?> ();
254+
255+ var sql_event_ids = database.get_sql_string_from_event_ids (event_ids);
256+ string sql = """
257+ SELECT * FROM event_view
258+ WHERE id IN (%s)
259+ """.printf (sql_event_ids);
260+
261+ Sqlite.Statement stmt;
262+ int rc = db.prepare_v2 (sql, -1, out stmt);
263+ database.assert_query_success (rc, "SQL error");
264+
265+ var events = new HashTable<uint32, Event?> (direct_hash, direct_equal);
266+
267+ // Create Events and Subjects from rows
268+ while ((rc = stmt.step ()) == Sqlite.ROW)
269+ {
270+ uint32 event_id = (uint32) stmt.column_int64 (EventViewRows.ID);
271+ Event? event = events.lookup (event_id);
272+ if (event == null)
273+ {
274+ event = get_event_from_row(stmt, event_id);
275+ events.insert (event_id, event);
276+ }
277+ Subject subject = get_subject_from_row(stmt);
278+ event.add_subject(subject);
279+ }
280+ if (rc != Sqlite.DONE)
281+ {
282+ throw new EngineError.DATABASE_ERROR ("Error: %d, %s\n",
283+ rc, db.errmsg ());
284+ }
285+
286+ // Sort events according to the sequence of event_ids
287+ var results = new GenericArray<Event?> ();
288+ results.length = event_ids.length;
289+ int i = 0;
290+ foreach (var id in event_ids)
291+ {
292+ results.set(i++, events.lookup (id));
293+ }
294+
295+ return results;
296+ }
297+
298+ public uint32[] find_event_ids (TimeRange time_range,
299+ GenericArray<Event> event_templates,
300+ uint storage_state, uint max_events, uint result_type,
301+ BusName? sender=null) throws EngineError
302+ {
303+
304+ WhereClause where = new WhereClause (WhereClause.Type.AND);
305+
306+ /**
307+ * We are using the unary operator here to tell SQLite to not use
308+ * the index on the timestamp column at the first place. This is a
309+ * "fix" for (LP: #672965) based on some benchmarks, which suggest
310+ * a performance win, but we might not oversee all implications.
311+ * (See http://www.sqlite.org/optoverview.html, section 6.0).
312+ * -- Markus Korn, 29/11/2010
313+ */
314+ if (time_range.start != 0)
315+ where.add (("+timestamp >= %" + int64.FORMAT).printf(
316+ time_range.start));
317+ if (time_range.end != 0)
318+ where.add (("+timestamp <= %" + int64.FORMAT).printf(
319+ time_range.end));
320+
321+ if (storage_state == StorageState.AVAILABLE ||
322+ storage_state == StorageState.NOT_AVAILABLE)
323+ {
324+ where.add ("(subj_storage_state=? OR subj_storage_state IS NULL)",
325+ storage_state.to_string ());
326+ }
327+ else if (storage_state != StorageState.ANY)
328+ {
329+ throw new EngineError.INVALID_ARGUMENT(
330+ "Unknown storage state '%u'".printf(storage_state));
331+ }
332+
333+ WhereClause tpl_conditions = get_where_clause_from_event_templates (
334+ event_templates);
335+ where.extend (tpl_conditions);
336+ //if (!where.may_have_results ())
337+ // return new uint32[0];
338+
339+ string sql = "SELECT id FROM event_view ";
340+ string where_sql = "";
341+ if (!where.is_empty ())
342+ {
343+ where_sql = "WHERE " + where.get_sql_conditions ();
344+ }
345+
346+ switch (result_type)
347+ {
348+ case ResultType.MOST_RECENT_EVENTS:
349+ sql += where_sql + " ORDER BY timestamp DESC";
350+ break;
351+ case ResultType.LEAST_RECENT_EVENTS:
352+ sql += where_sql + " ORDER BY timestamp ASC";
353+ break;
354+ case ResultType.MOST_RECENT_EVENT_ORIGIN:
355+ sql += group_and_sort ("origin", where_sql, false);
356+ break;
357+ case ResultType.LEAST_RECENT_EVENT_ORIGIN:
358+ sql += group_and_sort ("origin", where_sql, true);
359+ break;
360+ case ResultType.MOST_POPULAR_EVENT_ORIGIN:
361+ sql += group_and_sort ("origin", where_sql, false, false);
362+ break;
363+ case ResultType.LEAST_POPULAR_EVENT_ORIGIN:
364+ sql += group_and_sort ("origin", where_sql, true, true);
365+ break;
366+ case ResultType.MOST_RECENT_SUBJECTS:
367+ sql += group_and_sort ("subj_id", where_sql, false);
368+ break;
369+ case ResultType.LEAST_RECENT_SUBJECTS:
370+ sql += group_and_sort ("subj_id", where_sql, true);
371+ break;
372+ case ResultType.MOST_POPULAR_SUBJECTS:
373+ sql += group_and_sort ("subj_id", where_sql, false, false);
374+ break;
375+ case ResultType.LEAST_POPULAR_SUBJECTS:
376+ sql += group_and_sort ("subj_id", where_sql, true, true);
377+ break;
378+ case ResultType.MOST_RECENT_CURRENT_URI:
379+ sql += group_and_sort ("subj_id_current", where_sql, false);
380+ break;
381+ case ResultType.LEAST_RECENT_CURRENT_URI:
382+ sql += group_and_sort ("subj_id_current", where_sql, true);
383+ break;
384+ case ResultType.MOST_POPULAR_CURRENT_URI:
385+ sql += group_and_sort ("subj_id_current", where_sql,
386+ false, false);
387+ break;
388+ case ResultType.LEAST_POPULAR_CURRENT_URI:
389+ sql += group_and_sort ("subj_id_current", where_sql,
390+ true, true);
391+ break;
392+ case ResultType.MOST_RECENT_ACTOR:
393+ sql += group_and_sort ("actor", where_sql, false);
394+ break;
395+ case ResultType.LEAST_RECENT_ACTOR:
396+ sql += group_and_sort ("actor", where_sql, true);
397+ break;
398+ case ResultType.MOST_POPULAR_ACTOR:
399+ sql += group_and_sort ("actor", where_sql, false, false);
400+ break;
401+ case ResultType.LEAST_POPULAR_ACTOR:
402+ sql += group_and_sort ("actor", where_sql, true, true);
403+ break;
404+ case ResultType.OLDEST_ACTOR:
405+ sql += group_and_sort ("actor", where_sql, true, null, "min");
406+ break;
407+ case ResultType.MOST_RECENT_ORIGIN:
408+ sql += group_and_sort ("subj_origin", where_sql, false);
409+ break;
410+ case ResultType.LEAST_RECENT_ORIGIN:
411+ sql += group_and_sort ("subj_origin", where_sql, true);
412+ break;
413+ case ResultType.MOST_POPULAR_ORIGIN:
414+ sql += group_and_sort ("subj_origin", where_sql, false, false);
415+ break;
416+ case ResultType.LEAST_POPULAR_ORIGIN:
417+ sql += group_and_sort ("subj_origin", where_sql, true, true);
418+ break;
419+ case ResultType.MOST_RECENT_SUBJECT_INTERPRETATION:
420+ sql += group_and_sort ("subj_interpretation", where_sql, false);
421+ break;
422+ case ResultType.LEAST_RECENT_SUBJECT_INTERPRETATION:
423+ sql += group_and_sort ("subj_interpretation", where_sql, true);
424+ break;
425+ case ResultType.MOST_POPULAR_SUBJECT_INTERPRETATION:
426+ sql += group_and_sort ("subj_interpretation", where_sql,
427+ false, false);
428+ break;
429+ case ResultType.LEAST_POPULAR_SUBJECT_INTERPRETATION:
430+ sql += group_and_sort ("subj_interpretation", where_sql,
431+ true, true);
432+ break;
433+ case ResultType.MOST_RECENT_MIMETYPE:
434+ sql += group_and_sort ("subj_mimetype", where_sql, false);
435+ break;
436+ case ResultType.LEAST_RECENT_MIMETYPE:
437+ sql += group_and_sort ("subj_mimetype", where_sql, true);
438+ break;
439+ case ResultType.MOST_POPULAR_MIMETYPE:
440+ sql += group_and_sort ("subj_mimetype", where_sql,
441+ false, false);
442+ break;
443+ case ResultType.LEAST_POPULAR_MIMETYPE:
444+ sql += group_and_sort ("subj_mimetype", where_sql,
445+ true, true);
446+ break;
447+ default:
448+ string error_message = "Invalid ResultType.";
449+ warning (error_message);
450+ throw new EngineError.INVALID_ARGUMENT (error_message);
451+ }
452+
453+ int rc;
454+ Sqlite.Statement stmt;
455+
456+ rc = db.prepare_v2 (sql, -1, out stmt);
457+ database.assert_query_success(rc, "SQL error");
458+
459+ var arguments = where.get_bind_arguments ();
460+ for (int i = 0; i < arguments.length; ++i)
461+ stmt.bind_text (i + 1, arguments[i]);
462+
463+#if EXPLAIN_QUERIES
464+ database.explain_query (stmt);
465+#endif
466+
467+ uint32[] event_ids = {};
468+
469+ while ((rc = stmt.step()) == Sqlite.ROW)
470+ {
471+ var event_id = (uint32) uint64.parse(
472+ stmt.column_text (EventViewRows.ID));
473+ // Events are supposed to be contiguous in the database
474+ if (event_ids.length == 0 || event_ids[event_ids.length-1] != event_id) {
475+ event_ids += event_id;
476+ if (event_ids.length == max_events) break;
477+ }
478+ }
479+ if (rc != Sqlite.DONE && rc != Sqlite.ROW)
480+ {
481+ string error_message = "Error in find_event_ids: %d, %s".printf (
482+ rc, db.errmsg ());
483+ warning (error_message);
484+ throw new EngineError.DATABASE_ERROR (error_message);
485+ }
486+
487+ return event_ids;
488+ }
489+
490+ public GenericArray<Event?> find_events (TimeRange time_range,
491+ GenericArray<Event> event_templates,
492+ uint storage_state, uint max_events, uint result_type,
493+ BusName? sender=null) throws EngineError
494+ {
495+ return get_events (find_event_ids (time_range, event_templates,
496+ storage_state, max_events, result_type));
497+ }
498+
499+ private struct RelatedUri {
500+ public uint32 id;
501+ public int64 timestamp;
502+ public string uri;
503+ public int32 counter;
504+ }
505+
506+ public string[] find_related_uris (TimeRange time_range,
507+ GenericArray<Event> event_templates,
508+ GenericArray<Event> result_event_templates,
509+ uint storage_state, uint max_results, uint result_type,
510+ BusName? sender=null) throws EngineError
511+ {
512+ /**
513+ * Return a list of subject URIs commonly used together with events
514+ * matching the given template, considering data from within the
515+ * indicated timerange.
516+ * Only URIs for subjects matching the indicated `result_event_templates`
517+ * and `result_storage_state` are returned.
518+ */
519+ if (result_type == ResultType.MOST_RECENT_EVENTS ||
520+ result_type == ResultType.LEAST_RECENT_EVENTS)
521+ {
522+
523+ // We pick out the ids for relational event so we can set them as
524+ // roots the ids are taken from the events that match the
525+ // events_templates
526+ uint32[] ids = find_event_ids (time_range, event_templates,
527+ storage_state, 0, ResultType.LEAST_RECENT_EVENTS);
528+
529+ if (event_templates.length > 0 && ids.length == 0)
530+ {
531+ throw new EngineError.INVALID_ARGUMENT (
532+ "No results found for the event_templates");
533+ }
534+
535+ // Pick out the result_ids for the filtered results we would like to
536+ // take into account the ids are taken from the events that match
537+ // the result_event_templates if no result_event_templates are set we
538+ // consider all results as allowed
539+ uint32[] result_ids;
540+ result_ids = find_event_ids (time_range, result_event_templates,
541+ storage_state, 0, ResultType.LEAST_RECENT_EVENTS);
542+
543+ // From here we create several graphs with the maximum depth of 2
544+ // and push all the nodes and vertices (events) in one pot together
545+
546+ uint32[] pot = new uint32[ids.length + result_ids.length];
547+
548+ for (uint32 i=0; i < ids.length; i++)
549+ pot[i] = ids[i];
550+ for (uint32 i=0; i < result_ids.length; i++)
551+ pot[ids.length + i] = result_ids[ids.length + i];
552+
553+ Sqlite.Statement stmt;
554+
555+ var sql_event_ids = database.get_sql_string_from_event_ids (pot);
556+ string sql = """
557+ SELECT id, timestamp, subj_uri FROM event_view
558+ WHERE id IN (%s) ORDER BY timestamp ASC
559+ """.printf (sql_event_ids);
560+
561+ int rc = db.prepare_v2 (sql, -1, out stmt);
562+
563+ database.assert_query_success(rc, "SQL error");
564+
565+ // FIXME: fix this ugly code
566+ var temp_related_uris = new GenericArray<RelatedUri?>();
567+
568+ while ((rc = stmt.step()) == Sqlite.ROW)
569+ {
570+ RelatedUri ruri = RelatedUri(){
571+ id = (uint32) uint64.parse(stmt.column_text (0)),
572+ timestamp = stmt.column_int64 (1),
573+ uri = stmt.column_text (2),
574+ counter = 0
575+ };
576+ temp_related_uris.add (ruri);
577+ }
578+
579+ // RelatedUri[] related_uris = new RelatedUri[temp_related_uris.length];
580+ // for (int i=0; i<related_uris.length; i++)
581+ // related_uris[i] = temp_related_uris[i];
582+
583+ if (rc != Sqlite.DONE)
584+ {
585+ string error_message =
586+ "Error in find_related_uris: %d, %s".printf (
587+ rc, db.errmsg ());
588+ warning (error_message);
589+ throw new EngineError.DATABASE_ERROR (error_message);
590+ }
591+
592+ var uri_counter = new HashTable<string, RelatedUri?>(
593+ str_hash, str_equal);
594+
595+ for (int i = 0; i < temp_related_uris.length; i++)
596+ {
597+ var window = new GenericArray<unowned RelatedUri?>();
598+
599+ bool count_in_window = false;
600+ for (int j = int.max (0, i - 5);
601+ j < int.min (i, temp_related_uris.length);
602+ j++)
603+ {
604+ window.add(temp_related_uris[j]);
605+ if (temp_related_uris[j].id in ids)
606+ count_in_window = true;
607+ }
608+
609+ if (count_in_window)
610+ {
611+ for (int j = 0; j < window.length; j++)
612+ {
613+ if (uri_counter.lookup (window[j].uri) == null)
614+ {
615+ RelatedUri ruri = RelatedUri ()
616+ {
617+ id = window[j].id,
618+ timestamp = window[j].timestamp,
619+ uri = window[j].uri,
620+ counter = 0
621+ };
622+ uri_counter.insert (window[j].uri, ruri);
623+ }
624+ uri_counter.lookup (window[j].uri).counter++;
625+ if (uri_counter.lookup (window[j].uri).timestamp
626+ < window[j].timestamp)
627+ {
628+ uri_counter.lookup (window[j].uri).timestamp =
629+ window[j].timestamp;
630+ }
631+ }
632+ }
633+ }
634+
635+
636+ // We have the big hashtable with the structs, now we sort them by
637+ // most used and limit the result then sort again
638+ List<RelatedUri?> temp_ruris = new List<RelatedUri?>();
639+ List<RelatedUri?> values = new List<RelatedUri?>();
640+
641+ foreach (var uri in uri_counter.get_values())
642+ values.append(uri);
643+
644+ values.sort ((a, b) => a.counter - b.counter);
645+ values.sort ((a, b) => {
646+ int64 delta = a.timestamp - b.timestamp;
647+ if (delta < 0) return 1;
648+ else if (delta > 0) return -1;
649+ else return 0;
650+ });
651+
652+ foreach (RelatedUri ruri in values)
653+ {
654+ if (temp_ruris.length() < max_results)
655+ temp_ruris.append(ruri);
656+ else
657+ break;
658+ }
659+
660+ // Sort by recency
661+ if (result_type == 1)
662+ temp_ruris.sort ((a, b) => {
663+ int64 delta = a.timestamp - b.timestamp;
664+ if (delta < 0) return 1;
665+ else if (delta > 0) return -1;
666+ else return 0;});
667+
668+ string[] results = new string[temp_ruris.length()];
669+
670+ int i = 0;
671+ foreach (var uri in temp_ruris)
672+ {
673+ results[i] = uri.uri;
674+ stdout.printf("%i %lld %s\n", uri.counter,
675+ uri.timestamp,
676+ uri.uri);
677+ i++;
678+ }
679+
680+ return results;
681+ }
682+ else
683+ {
684+ throw new EngineError.DATABASE_ERROR ("Unsupported ResultType.");
685+ }
686+ }
687+
688+ /**
689+ * Clear all resources Engine is using (close database connection, etc.).
690+ *
691+ * After executing this method on an instance, no other function
692+ * may be called.
693+ */
694+ public virtual void close ()
695+ {
696+ database.close ();
697+ }
698+
699+ // Used by find_event_ids
700+ private string group_and_sort (string field, string where_sql,
701+ bool time_asc=false, bool? count_asc=null,
702+ string aggregation_type="max")
703+ {
704+ string time_sorting = (time_asc) ? "ASC" : "DESC";
705+ string aggregation_sql = "";
706+ string order_sql = "";
707+
708+ if (count_asc != null)
709+ {
710+ aggregation_sql = ", COUNT(%s) AS num_events".printf (field);
711+ order_sql = "num_events %s,".printf ((count_asc) ? "ASC" : "DESC");
712+ }
713+
714+ return """
715+ NATURAL JOIN (
716+ SELECT %s,
717+ %s(timestamp) AS timestamp
718+ %s
719+ FROM event_view %s
720+ GROUP BY %s)
721+ GROUP BY %s
722+ ORDER BY %s timestamp %s
723+ """.printf (
724+ field,
725+ aggregation_type,
726+ aggregation_sql,
727+ where_sql,
728+ field,
729+ field,
730+ order_sql, time_sorting);
731+ }
732+
733+ // Used by find_event_ids
734+ protected WhereClause get_where_clause_from_event_templates (
735+ GenericArray<Event> templates) throws EngineError
736+ {
737+ WhereClause where = new WhereClause (WhereClause.Type.OR);
738+ for (int i = 0; i < templates.length; ++i)
739+ {
740+ Event event_template = templates[i];
741+ where.extend (
742+ get_where_clause_from_event_template (event_template));
743+ }
744+ return where;
745+ }
746+
747+ // Used by get_where_clause_from_event_templates
748+ private WhereClause get_where_clause_from_event_template (Event template)
749+ throws EngineError
750+ {
751+ WhereClause where = new WhereClause (WhereClause.Type.AND);
752+
753+ // Event ID
754+ if (template.id != 0)
755+ where.add ("id=?", template.id.to_string());
756+
757+ // Interpretation
758+ if (!is_empty_string (template.interpretation))
759+ {
760+ assert_no_wildcard ("interpretation", template.interpretation);
761+ WhereClause subwhere = get_where_clause_for_symbol (
762+ "interpretation", template.interpretation,
763+ interpretations_table);
764+ if (!subwhere.is_empty ())
765+ where.extend (subwhere);
766+ }
767+
768+ // Manifestation
769+ if (!is_empty_string (template.manifestation))
770+ {
771+ assert_no_wildcard ("manifestation", template.interpretation);
772+ WhereClause subwhere = get_where_clause_for_symbol (
773+ "manifestation", template.manifestation,
774+ manifestations_table);
775+ if (!subwhere.is_empty ())
776+ where.extend (subwhere);
777+ }
778+
779+ // Actor
780+ if (!is_empty_string (template.actor))
781+ {
782+ string val = template.actor;
783+ bool like = parse_wildcard (ref val);
784+ bool negated = parse_negation (ref val);
785+
786+ if (like)
787+ where.add_wildcard_condition ("actor", val, negated);
788+ else
789+ where.add_match_condition ("actor",
790+ actors_table.get_id (val), negated);
791+ }
792+
793+ // Origin
794+ if (!is_empty_string (template.origin))
795+ {
796+ string val = template.origin;
797+ bool like = parse_wildcard (ref val);
798+ bool negated = parse_negation (ref val);
799+ assert_no_noexpand (val, "origin");
800+
801+ if (like)
802+ where.add_wildcard_condition ("origin", val, negated);
803+ else
804+ where.add_text_condition_subquery ("origin", val, negated);
805+ }
806+
807+ // Subject templates within the same event template are AND'd
808+ // See LP bug #592599.
809+ for (int i = 0; i < template.num_subjects(); ++i)
810+ {
811+ Subject subject_template = template.subjects[i];
812+
813+ // Subject interpretation
814+ if (!is_empty_string (subject_template.interpretation))
815+ {
816+ assert_no_wildcard ("subject interpretation",
817+ template.interpretation);
818+ WhereClause subwhere = get_where_clause_for_symbol (
819+ "subj_interpretation", subject_template.interpretation,
820+ interpretations_table);
821+ if (!subwhere.is_empty ())
822+ where.extend (subwhere);
823+ }
824+
825+ // Subject manifestation
826+ if (!is_empty_string (subject_template.manifestation))
827+ {
828+ assert_no_wildcard ("subject manifestation",
829+ subject_template.manifestation);
830+ WhereClause subwhere = get_where_clause_for_symbol (
831+ "subj_manifestation", subject_template.manifestation,
832+ manifestations_table);
833+ if (!subwhere.is_empty ())
834+ where.extend (subwhere);
835+ }
836+
837+ // Mime-Type
838+ if (!is_empty_string (subject_template.mimetype))
839+ {
840+ string val = subject_template.mimetype;
841+ bool like = parse_wildcard (ref val);
842+ bool negated = parse_negation (ref val);
843+ assert_no_noexpand (val, "mime-type");
844+
845+ if (like)
846+ where.add_wildcard_condition (
847+ "subj_mimetype", val, negated);
848+ else
849+ where.add_match_condition ("subj_mimetype",
850+ mimetypes_table.get_id (val), negated);
851+ }
852+
853+ // URI
854+ if (!is_empty_string (subject_template.uri))
855+ {
856+ string val = subject_template.uri;
857+ bool like = parse_wildcard (ref val);
858+ bool negated = parse_negation (ref val);
859+ assert_no_noexpand (val, "uri");
860+
861+ if (like)
862+ where.add_wildcard_condition ("subj_id", val, negated);
863+ else
864+ where.add_text_condition_subquery ("subj_id", val, negated);
865+ }
866+
867+ // Origin
868+ if (!is_empty_string (subject_template.origin))
869+ {
870+ string val = subject_template.origin;
871+ bool like = parse_wildcard (ref val);
872+ bool negated = parse_negation (ref val);
873+ assert_no_noexpand (val, "subject origin");
874+
875+ if (like)
876+ where.add_wildcard_condition (
877+ "subj_origin", val, negated);
878+ else
879+ where.add_text_condition_subquery (
880+ "subj_origin", val, negated);
881+ }
882+
883+ // Text
884+ if (!is_empty_string (subject_template.text))
885+ {
886+ // Negation, noexpand and prefix search aren't supported
887+ // for subject texts, but "!", "+" and "*" are valid as
888+ // plain text characters.
889+ where.add_text_condition_subquery ("subj_text_id",
890+ subject_template.text, false);
891+ }
892+
893+ // Current URI
894+ if (!is_empty_string (subject_template.current_uri))
895+ {
896+ string val = subject_template.current_uri;
897+ bool like = parse_wildcard (ref val);
898+ bool negated = parse_negation (ref val);
899+ assert_no_noexpand (val, "current_uri");
900+
901+ if (like)
902+ where.add_wildcard_condition (
903+ "subj_id_current", val, negated);
904+ else
905+ where.add_text_condition_subquery (
906+ "subj_id_current", val, negated);
907+ }
908+
909+ // Subject storage
910+ if (!is_empty_string (subject_template.storage))
911+ {
912+ string val = subject_template.storage;
913+ assert_no_negation ("subject storage", val);
914+ assert_no_wildcard ("subject storage", val);
915+ assert_no_noexpand (val, "subject storage");
916+ where.add_text_condition_subquery ("subj_storage_id", val);
917+ }
918+ }
919+
920+ return where;
921+ }
922+
923+ // Used by get_where_clause_from_event_templates
924+ /**
925+ * If the value starts with the negation operator, throw an
926+ * error.
927+ */
928+ protected void assert_no_negation (string field, string val)
929+ throws EngineError
930+ {
931+ if (!val.has_prefix ("!"))
932+ return;
933+ string error_message =
934+ "Field '%s' doesn't support negation".printf (field);
935+ warning (error_message);
936+ throw new EngineError.INVALID_ARGUMENT (error_message);
937+ }
938+
939+ // Used by get_where_clause_from_event_templates
940+ /**
941+ * If the value starts with the negation operator, throw an
942+ * error.
943+ */
944+ protected void assert_no_noexpand (string field, string val)
945+ throws EngineError
946+ {
947+ if (!val.has_prefix ("+"))
948+ return;
949+ string error_message =
950+ "Field '%s' doesn't support the no-expand operator".printf (field);
951+ warning (error_message);
952+ throw new EngineError.INVALID_ARGUMENT (error_message);
953+ }
954+
955+ // Used by get_where_clause_from_event_templates
956+ /**
957+ * If the value ends with the wildcard character, throw an error.
958+ */
959+ protected void assert_no_wildcard (string field, string val)
960+ throws EngineError
961+ {
962+ if (!val.has_suffix ("*"))
963+ return;
964+ string error_message =
965+ "Field '%s' doesn't support prefix search".printf (field);
966+ warning (error_message);
967+ throw new EngineError.INVALID_ARGUMENT (error_message);
968+ }
969+
970+ protected WhereClause get_where_clause_for_symbol (string table_name,
971+ string symbol, TableLookup lookup_table) throws EngineError
972+ {
973+ string _symbol = symbol;
974+ bool negated = parse_negation (ref _symbol);
975+ bool noexpand = parse_noexpand (ref _symbol);
976+ List<unowned string> symbols;
977+ if (noexpand)
978+ symbols = new List<unowned string> ();
979+ else
980+ symbols = Symbol.get_all_children (_symbol);
981+ symbols.prepend (_symbol);
982+
983+ WhereClause subwhere = new WhereClause(
984+ WhereClause.Type.OR, negated);
985+
986+ if (symbols.length () == 1)
987+ {
988+ subwhere.add_match_condition (table_name,
989+ lookup_table.get_id (_symbol));
990+ }
991+ else
992+ {
993+ var sb = new StringBuilder ();
994+ foreach (unowned string uri in symbols)
995+ {
996+ sb.append_printf ("%d,", lookup_table.get_id (uri));
997+ }
998+ sb.truncate (sb.len - 1);
999+
1000+ string sql = "%s IN (%s)".printf(table_name, sb.str);
1001+ subwhere.add(sql);
1002+ }
1003+
1004+ return subwhere;
1005+ }
1006+
1007+ private void delete_from_cache (string table, int64 rowid)
1008+ {
1009+ TableLookup table_lookup;
1010+
1011+ if (table == "interpretation")
1012+ table_lookup = interpretations_table;
1013+ else if (table == "manifestation")
1014+ table_lookup = manifestations_table;
1015+ else if (table == "mimetype")
1016+ table_lookup = mimetypes_table;
1017+ else if (table == "actor")
1018+ table_lookup = actors_table;
1019+ else
1020+ return;
1021+
1022+ table_lookup.remove((int) rowid);
1023+ }
1024+
1025+}
1026+
1027+}
1028+
1029+// vim:expandtab:ts=4:sw=4
1030
1031=== modified file 'src/engine.vala'
1032--- src/engine.vala 2012-01-25 17:37:55 +0000
1033+++ src/engine.vala 2012-02-05 18:08:19 +0000
1034@@ -29,37 +29,28 @@
1035 using Zeitgeist.SQLite;
1036
1037 namespace Zeitgeist
1038-{ // FIXME: increase indentation once we're ok with breaking 'bzr diff'
1039-
1040-public class Engine : Object
1041-{
1042-
1043- public Zeitgeist.SQLite.ZeitgeistDatabase database { get; private set; }
1044+{
1045+
1046+public class Engine : DbReader
1047+{
1048+
1049 public ExtensionStore extension_store;
1050 private ExtensionCollection extension_collection;
1051- private unowned Sqlite.Database db;
1052-
1053- protected TableLookup interpretations_table;
1054- protected TableLookup manifestations_table;
1055- protected TableLookup mimetypes_table;
1056- protected TableLookup actors_table;
1057
1058 private uint32 last_id;
1059
1060 public Engine () throws EngineError
1061 {
1062- database = new Zeitgeist.SQLite.ZeitgeistDatabase ();
1063- database.set_deletion_callback (delete_from_cache);
1064- db = database.database;
1065+ Object (database: new Zeitgeist.SQLite.Database ());
1066+
1067+ // TODO: take care of this if we decide to subclass Engine
1068 last_id = database.get_last_id ();
1069-
1070- interpretations_table = new TableLookup (database, "interpretation");
1071- manifestations_table = new TableLookup (database, "manifestation");
1072- mimetypes_table = new TableLookup (database, "mimetype");
1073- actors_table = new TableLookup (database, "actor");
1074-
1075+ extension_collection = new ExtensionCollection (this);
1076+ }
1077+
1078+ construct
1079+ {
1080 extension_store = new ExtensionStore (this);
1081- extension_collection = new ExtensionCollection (this);
1082 }
1083
1084 public string[] get_extension_names ()
1085@@ -67,495 +58,6 @@
1086 return extension_collection.get_extension_names ();
1087 }
1088
1089- private Event get_event_from_row (Sqlite.Statement stmt, uint32 event_id)
1090- {
1091- Event event = new Event ();
1092- event.id = event_id;
1093- event.timestamp = stmt.column_int64 (EventViewRows.TIMESTAMP);
1094- event.interpretation = interpretations_table.get_value (
1095- stmt.column_int (EventViewRows.INTERPRETATION));
1096- event.manifestation = manifestations_table.get_value (
1097- stmt.column_int (EventViewRows.MANIFESTATION));
1098- event.actor = actors_table.get_value (
1099- stmt.column_int (EventViewRows.ACTOR));
1100- event.origin = stmt.column_text (
1101- EventViewRows.EVENT_ORIGIN_URI);
1102-
1103- // Load payload
1104- unowned uint8[] data = (uint8[]) stmt.column_blob(
1105- EventViewRows.PAYLOAD);
1106- data.length = stmt.column_bytes(EventViewRows.PAYLOAD);
1107- if (data != null)
1108- {
1109- event.payload = new ByteArray();
1110- event.payload.append(data);
1111- }
1112- return event;
1113- }
1114-
1115- private Subject get_subject_from_row (Sqlite.Statement stmt)
1116- {
1117- Subject subject = new Subject ();
1118- subject.uri = stmt.column_text (EventViewRows.SUBJECT_URI);
1119- subject.text = stmt.column_text (EventViewRows.SUBJECT_TEXT);
1120- subject.storage = stmt.column_text (EventViewRows.SUBJECT_STORAGE);
1121- subject.origin = stmt.column_text (EventViewRows.SUBJECT_ORIGIN_URI);
1122- subject.current_uri = stmt.column_text (
1123- EventViewRows.SUBJECT_CURRENT_URI);
1124- subject.interpretation = interpretations_table.get_value (
1125- stmt.column_int (EventViewRows.SUBJECT_INTERPRETATION));
1126- subject.manifestation = manifestations_table.get_value (
1127- stmt.column_int (EventViewRows.SUBJECT_MANIFESTATION));
1128- subject.mimetype = mimetypes_table.get_value (
1129- stmt.column_int (EventViewRows.SUBJECT_MIMETYPE));
1130- return subject;
1131- }
1132-
1133- public GenericArray<Event?> get_events(uint32[] event_ids,
1134- BusName? sender=null) throws EngineError
1135- {
1136- // TODO: Consider if we still want the cache. This should be done
1137- // once everything is working, since it adds unneeded complexity.
1138- // It'd also benchmark it again first, we may have better options
1139- // to enhance the performance of SQLite now, and event processing
1140- // will be faster now being C.
1141-
1142- if (event_ids.length == 0)
1143- return new GenericArray<Event?> ();
1144-
1145- var sql_event_ids = database.get_sql_string_from_event_ids (event_ids);
1146- string sql = """
1147- SELECT * FROM event_view
1148- WHERE id IN (%s)
1149- """.printf (sql_event_ids);
1150-
1151- Sqlite.Statement stmt;
1152- int rc = db.prepare_v2 (sql, -1, out stmt);
1153- database.assert_query_success (rc, "SQL error");
1154-
1155- var events = new HashTable<uint32, Event?> (direct_hash, direct_equal);
1156-
1157- // Create Events and Subjects from rows
1158- while ((rc = stmt.step ()) == Sqlite.ROW)
1159- {
1160- uint32 event_id = (uint32) stmt.column_int64 (EventViewRows.ID);
1161- Event? event = events.lookup (event_id);
1162- if (event == null)
1163- {
1164- event = get_event_from_row(stmt, event_id);
1165- events.insert (event_id, event);
1166- }
1167- Subject subject = get_subject_from_row(stmt);
1168- event.add_subject(subject);
1169- }
1170- if (rc != Sqlite.DONE)
1171- {
1172- throw new EngineError.DATABASE_ERROR ("Error: %d, %s\n",
1173- rc, db.errmsg ());
1174- }
1175-
1176- // Sort events according to the sequence of event_ids
1177- var results = new GenericArray<Event?> ();
1178- results.length = event_ids.length;
1179- int i = 0;
1180- foreach (var id in event_ids)
1181- {
1182- results.set(i++, events.lookup (id));
1183- }
1184-
1185- return results;
1186- }
1187-
1188- public uint32[] find_event_ids (TimeRange time_range,
1189- GenericArray<Event> event_templates,
1190- uint storage_state, uint max_events, uint result_type,
1191- BusName? sender=null) throws EngineError
1192- {
1193-
1194- WhereClause where = new WhereClause (WhereClause.Type.AND);
1195-
1196- /**
1197- * We are using the unary operator here to tell SQLite to not use
1198- * the index on the timestamp column at the first place. This is a
1199- * "fix" for (LP: #672965) based on some benchmarks, which suggest
1200- * a performance win, but we might not oversee all implications.
1201- * (See http://www.sqlite.org/optoverview.html, section 6.0).
1202- * -- Markus Korn, 29/11/2010
1203- */
1204- if (time_range.start != 0)
1205- where.add (("+timestamp >= %" + int64.FORMAT).printf(
1206- time_range.start));
1207- if (time_range.end != 0)
1208- where.add (("+timestamp <= %" + int64.FORMAT).printf(
1209- time_range.end));
1210-
1211- if (storage_state == StorageState.AVAILABLE ||
1212- storage_state == StorageState.NOT_AVAILABLE)
1213- {
1214- where.add ("(subj_storage_state=? OR subj_storage_state IS NULL)",
1215- storage_state.to_string ());
1216- }
1217- else if (storage_state != StorageState.ANY)
1218- {
1219- throw new EngineError.INVALID_ARGUMENT(
1220- "Unknown storage state '%u'".printf(storage_state));
1221- }
1222-
1223- WhereClause tpl_conditions = get_where_clause_from_event_templates (
1224- event_templates);
1225- where.extend (tpl_conditions);
1226- //if (!where.may_have_results ())
1227- // return new uint32[0];
1228-
1229- string sql = "SELECT id FROM event_view ";
1230- string where_sql = "";
1231- if (!where.is_empty ())
1232- {
1233- where_sql = "WHERE " + where.get_sql_conditions ();
1234- }
1235-
1236- switch (result_type)
1237- {
1238- case ResultType.MOST_RECENT_EVENTS:
1239- sql += where_sql + " ORDER BY timestamp DESC";
1240- break;
1241- case ResultType.LEAST_RECENT_EVENTS:
1242- sql += where_sql + " ORDER BY timestamp ASC";
1243- break;
1244- case ResultType.MOST_RECENT_EVENT_ORIGIN:
1245- sql += group_and_sort ("origin", where_sql, false);
1246- break;
1247- case ResultType.LEAST_RECENT_EVENT_ORIGIN:
1248- sql += group_and_sort ("origin", where_sql, true);
1249- break;
1250- case ResultType.MOST_POPULAR_EVENT_ORIGIN:
1251- sql += group_and_sort ("origin", where_sql, false, false);
1252- break;
1253- case ResultType.LEAST_POPULAR_EVENT_ORIGIN:
1254- sql += group_and_sort ("origin", where_sql, true, true);
1255- break;
1256- case ResultType.MOST_RECENT_SUBJECTS:
1257- sql += group_and_sort ("subj_id", where_sql, false);
1258- break;
1259- case ResultType.LEAST_RECENT_SUBJECTS:
1260- sql += group_and_sort ("subj_id", where_sql, true);
1261- break;
1262- case ResultType.MOST_POPULAR_SUBJECTS:
1263- sql += group_and_sort ("subj_id", where_sql, false, false);
1264- break;
1265- case ResultType.LEAST_POPULAR_SUBJECTS:
1266- sql += group_and_sort ("subj_id", where_sql, true, true);
1267- break;
1268- case ResultType.MOST_RECENT_CURRENT_URI:
1269- sql += group_and_sort ("subj_id_current", where_sql, false);
1270- break;
1271- case ResultType.LEAST_RECENT_CURRENT_URI:
1272- sql += group_and_sort ("subj_id_current", where_sql, true);
1273- break;
1274- case ResultType.MOST_POPULAR_CURRENT_URI:
1275- sql += group_and_sort ("subj_id_current", where_sql,
1276- false, false);
1277- break;
1278- case ResultType.LEAST_POPULAR_CURRENT_URI:
1279- sql += group_and_sort ("subj_id_current", where_sql,
1280- true, true);
1281- break;
1282- case ResultType.MOST_RECENT_ACTOR:
1283- sql += group_and_sort ("actor", where_sql, false);
1284- break;
1285- case ResultType.LEAST_RECENT_ACTOR:
1286- sql += group_and_sort ("actor", where_sql, true);
1287- break;
1288- case ResultType.MOST_POPULAR_ACTOR:
1289- sql += group_and_sort ("actor", where_sql, false, false);
1290- break;
1291- case ResultType.LEAST_POPULAR_ACTOR:
1292- sql += group_and_sort ("actor", where_sql, true, true);
1293- break;
1294- case ResultType.OLDEST_ACTOR:
1295- sql += group_and_sort ("actor", where_sql, true, null, "min");
1296- break;
1297- case ResultType.MOST_RECENT_ORIGIN:
1298- sql += group_and_sort ("subj_origin", where_sql, false);
1299- break;
1300- case ResultType.LEAST_RECENT_ORIGIN:
1301- sql += group_and_sort ("subj_origin", where_sql, true);
1302- break;
1303- case ResultType.MOST_POPULAR_ORIGIN:
1304- sql += group_and_sort ("subj_origin", where_sql, false, false);
1305- break;
1306- case ResultType.LEAST_POPULAR_ORIGIN:
1307- sql += group_and_sort ("subj_origin", where_sql, true, true);
1308- break;
1309- case ResultType.MOST_RECENT_SUBJECT_INTERPRETATION:
1310- sql += group_and_sort ("subj_interpretation", where_sql, false);
1311- break;
1312- case ResultType.LEAST_RECENT_SUBJECT_INTERPRETATION:
1313- sql += group_and_sort ("subj_interpretation", where_sql, true);
1314- break;
1315- case ResultType.MOST_POPULAR_SUBJECT_INTERPRETATION:
1316- sql += group_and_sort ("subj_interpretation", where_sql,
1317- false, false);
1318- break;
1319- case ResultType.LEAST_POPULAR_SUBJECT_INTERPRETATION:
1320- sql += group_and_sort ("subj_interpretation", where_sql,
1321- true, true);
1322- break;
1323- case ResultType.MOST_RECENT_MIMETYPE:
1324- sql += group_and_sort ("subj_mimetype", where_sql, false);
1325- break;
1326- case ResultType.LEAST_RECENT_MIMETYPE:
1327- sql += group_and_sort ("subj_mimetype", where_sql, true);
1328- break;
1329- case ResultType.MOST_POPULAR_MIMETYPE:
1330- sql += group_and_sort ("subj_mimetype", where_sql,
1331- false, false);
1332- break;
1333- case ResultType.LEAST_POPULAR_MIMETYPE:
1334- sql += group_and_sort ("subj_mimetype", where_sql,
1335- true, true);
1336- break;
1337- default:
1338- string error_message = "Invalid ResultType.";
1339- warning (error_message);
1340- throw new EngineError.INVALID_ARGUMENT (error_message);
1341- }
1342-
1343- int rc;
1344- Sqlite.Statement stmt;
1345-
1346- rc = db.prepare_v2 (sql, -1, out stmt);
1347- database.assert_query_success(rc, "SQL error");
1348-
1349- var arguments = where.get_bind_arguments ();
1350- for (int i = 0; i < arguments.length; ++i)
1351- stmt.bind_text (i + 1, arguments[i]);
1352-
1353-#if EXPLAIN_QUERIES
1354- database.explain_query (stmt);
1355-#endif
1356-
1357- uint32[] event_ids = {};
1358-
1359- while ((rc = stmt.step()) == Sqlite.ROW)
1360- {
1361- var event_id = (uint32) uint64.parse(
1362- stmt.column_text (EventViewRows.ID));
1363- // Events are supposed to be contiguous in the database
1364- if (event_ids.length == 0 || event_ids[event_ids.length-1] != event_id) {
1365- event_ids += event_id;
1366- if (event_ids.length == max_events) break;
1367- }
1368- }
1369- if (rc != Sqlite.DONE && rc != Sqlite.ROW)
1370- {
1371- string error_message = "Error in find_event_ids: %d, %s".printf (
1372- rc, db.errmsg ());
1373- warning (error_message);
1374- throw new EngineError.DATABASE_ERROR (error_message);
1375- }
1376-
1377- return event_ids;
1378- }
1379-
1380- public GenericArray<Event?> find_events (TimeRange time_range,
1381- GenericArray<Event> event_templates,
1382- uint storage_state, uint max_events, uint result_type,
1383- BusName? sender=null) throws EngineError
1384- {
1385- return get_events (find_event_ids (time_range, event_templates,
1386- storage_state, max_events, result_type));
1387- }
1388-
1389- private struct RelatedUri {
1390- public uint32 id;
1391- public int64 timestamp;
1392- public string uri;
1393- public int32 counter;
1394- }
1395-
1396- public string[] find_related_uris (TimeRange time_range,
1397- GenericArray<Event> event_templates,
1398- GenericArray<Event> result_event_templates,
1399- uint storage_state, uint max_results, uint result_type,
1400- BusName? sender=null) throws EngineError
1401- {
1402- /**
1403- * Return a list of subject URIs commonly used together with events
1404- * matching the given template, considering data from within the
1405- * indicated timerange.
1406- * Only URIs for subjects matching the indicated `result_event_templates`
1407- * and `result_storage_state` are returned.
1408- */
1409- if (result_type == ResultType.MOST_RECENT_EVENTS ||
1410- result_type == ResultType.LEAST_RECENT_EVENTS)
1411- {
1412-
1413- // We pick out the ids for relational event so we can set them as
1414- // roots the ids are taken from the events that match the
1415- // events_templates
1416- uint32[] ids = find_event_ids (time_range, event_templates,
1417- storage_state, 0, ResultType.LEAST_RECENT_EVENTS);
1418-
1419- if (event_templates.length > 0 && ids.length == 0)
1420- {
1421- throw new EngineError.INVALID_ARGUMENT (
1422- "No results found for the event_templates");
1423- }
1424-
1425- // Pick out the result_ids for the filtered results we would like to
1426- // take into account the ids are taken from the events that match
1427- // the result_event_templates if no result_event_templates are set we
1428- // consider all results as allowed
1429- uint32[] result_ids;
1430- result_ids = find_event_ids (time_range, result_event_templates,
1431- storage_state, 0, ResultType.LEAST_RECENT_EVENTS);
1432-
1433- // From here we create several graphs with the maximum depth of 2
1434- // and push all the nodes and vertices (events) in one pot together
1435-
1436- uint32[] pot = new uint32[ids.length + result_ids.length];
1437-
1438- for (uint32 i=0; i < ids.length; i++)
1439- pot[i] = ids[i];
1440- for (uint32 i=0; i < result_ids.length; i++)
1441- pot[ids.length + i] = result_ids[ids.length + i];
1442-
1443- Sqlite.Statement stmt;
1444-
1445- var sql_event_ids = database.get_sql_string_from_event_ids (pot);
1446- string sql = """
1447- SELECT id, timestamp, subj_uri FROM event_view
1448- WHERE id IN (%s) ORDER BY timestamp ASC
1449- """.printf (sql_event_ids);
1450-
1451- int rc = db.prepare_v2 (sql, -1, out stmt);
1452-
1453- database.assert_query_success(rc, "SQL error");
1454-
1455- // FIXME: fix this ugly code
1456- var temp_related_uris = new GenericArray<RelatedUri?>();
1457-
1458- while ((rc = stmt.step()) == Sqlite.ROW)
1459- {
1460- RelatedUri ruri = RelatedUri(){
1461- id = (uint32) uint64.parse(stmt.column_text (0)),
1462- timestamp = stmt.column_int64 (1),
1463- uri = stmt.column_text (2),
1464- counter = 0
1465- };
1466- temp_related_uris.add (ruri);
1467- }
1468-
1469- // RelatedUri[] related_uris = new RelatedUri[temp_related_uris.length];
1470- // for (int i=0; i<related_uris.length; i++)
1471- // related_uris[i] = temp_related_uris[i];
1472-
1473- if (rc != Sqlite.DONE)
1474- {
1475- string error_message =
1476- "Error in find_related_uris: %d, %s".printf (
1477- rc, db.errmsg ());
1478- warning (error_message);
1479- throw new EngineError.DATABASE_ERROR (error_message);
1480- }
1481-
1482- var uri_counter = new HashTable<string, RelatedUri?>(
1483- str_hash, str_equal);
1484-
1485- for (int i = 0; i < temp_related_uris.length; i++)
1486- {
1487- var window = new GenericArray<unowned RelatedUri?>();
1488-
1489- bool count_in_window = false;
1490- for (int j = int.max (0, i - 5);
1491- j < int.min (i, temp_related_uris.length);
1492- j++)
1493- {
1494- window.add(temp_related_uris[j]);
1495- if (temp_related_uris[j].id in ids)
1496- count_in_window = true;
1497- }
1498-
1499- if (count_in_window)
1500- {
1501- for (int j = 0; j < window.length; j++)
1502- {
1503- if (uri_counter.lookup (window[j].uri) == null)
1504- {
1505- RelatedUri ruri = RelatedUri ()
1506- {
1507- id = window[j].id,
1508- timestamp = window[j].timestamp,
1509- uri = window[j].uri,
1510- counter = 0
1511- };
1512- uri_counter.insert (window[j].uri, ruri);
1513- }
1514- uri_counter.lookup (window[j].uri).counter++;
1515- if (uri_counter.lookup (window[j].uri).timestamp
1516- < window[j].timestamp)
1517- {
1518- uri_counter.lookup (window[j].uri).timestamp =
1519- window[j].timestamp;
1520- }
1521- }
1522- }
1523- }
1524-
1525-
1526- // We have the big hashtable with the structs, now we sort them by
1527- // most used and limit the result then sort again
1528- List<RelatedUri?> temp_ruris = new List<RelatedUri?>();
1529- List<RelatedUri?> values = new List<RelatedUri?>();
1530-
1531- foreach (var uri in uri_counter.get_values())
1532- values.append(uri);
1533-
1534- values.sort ((a, b) => a.counter - b.counter);
1535- values.sort ((a, b) => {
1536- int64 delta = a.timestamp - b.timestamp;
1537- if (delta < 0) return 1;
1538- else if (delta > 0) return -1;
1539- else return 0;
1540- });
1541-
1542- foreach (RelatedUri ruri in values)
1543- {
1544- if (temp_ruris.length() < max_results)
1545- temp_ruris.append(ruri);
1546- else
1547- break;
1548- }
1549-
1550- // Sort by recency
1551- if (result_type == 1)
1552- temp_ruris.sort ((a, b) => {
1553- int64 delta = a.timestamp - b.timestamp;
1554- if (delta < 0) return 1;
1555- else if (delta > 0) return -1;
1556- else return 0;});
1557-
1558- string[] results = new string[temp_ruris.length()];
1559-
1560- int i = 0;
1561- foreach (var uri in temp_ruris)
1562- {
1563- results[i] = uri.uri;
1564- stdout.printf("%i %lld %s\n", uri.counter,
1565- uri.timestamp,
1566- uri.uri);
1567- i++;
1568- }
1569-
1570- return results;
1571- }
1572- else
1573- {
1574- throw new EngineError.DATABASE_ERROR ("Unsupported ResultType.");
1575- }
1576- }
1577-
1578 public uint32[] insert_events (GenericArray<Event> events,
1579 BusName? sender=null) throws EngineError
1580 {
1581@@ -786,366 +288,14 @@
1582 * After executing this method on an Engine instance, no other function
1583 * of said instance may be called.
1584 */
1585- public void close ()
1586+ public override void close ()
1587 {
1588 // We delete the ExtensionCollection here so that it unloads
1589 // all extensions and they get a chance to access the database
1590 // (including through ExtensionStore) before it's closed.
1591 extension_collection = null;
1592- database.close ();
1593- }
1594-
1595- // Used by find_event_ids
1596- private string group_and_sort (string field, string where_sql,
1597- bool time_asc=false, bool? count_asc=null,
1598- string aggregation_type="max")
1599- {
1600- string time_sorting = (time_asc) ? "ASC" : "DESC";
1601- string aggregation_sql = "";
1602- string order_sql = "";
1603-
1604- if (count_asc != null)
1605- {
1606- aggregation_sql = ", COUNT(%s) AS num_events".printf (field);
1607- order_sql = "num_events %s,".printf ((count_asc) ? "ASC" : "DESC");
1608- }
1609-
1610- return """
1611- NATURAL JOIN (
1612- SELECT %s,
1613- %s(timestamp) AS timestamp
1614- %s
1615- FROM event_view %s
1616- GROUP BY %s)
1617- GROUP BY %s
1618- ORDER BY %s timestamp %s
1619- """.printf (
1620- field,
1621- aggregation_type,
1622- aggregation_sql,
1623- where_sql,
1624- field,
1625- field,
1626- order_sql, time_sorting);
1627- }
1628-
1629- // Used by find_event_ids
1630- private WhereClause get_where_clause_from_event_templates (
1631- GenericArray<Event> templates) throws EngineError
1632- {
1633- WhereClause where = new WhereClause (WhereClause.Type.OR);
1634- for (int i = 0; i < templates.length; ++i)
1635- {
1636- Event event_template = templates[i];
1637- where.extend (
1638- get_where_clause_from_event_template (event_template));
1639- }
1640- return where;
1641- }
1642-
1643- // Used by get_where_clause_from_event_templates
1644- private WhereClause get_where_clause_from_event_template (Event template)
1645- throws EngineError
1646- {
1647- WhereClause where = new WhereClause (WhereClause.Type.AND);
1648-
1649- // Event ID
1650- if (template.id != 0)
1651- where.add ("id=?", template.id.to_string());
1652-
1653- // Interpretation
1654- if (template.interpretation != "")
1655- {
1656- assert_no_wildcard ("interpretation", template.interpretation);
1657- WhereClause subwhere = get_where_clause_for_symbol (
1658- "interpretation", template.interpretation,
1659- interpretations_table);
1660- if (!subwhere.is_empty ())
1661- where.extend (subwhere);
1662- }
1663-
1664- // Manifestation
1665- if (template.manifestation != "")
1666- {
1667- assert_no_wildcard ("manifestation", template.interpretation);
1668- WhereClause subwhere = get_where_clause_for_symbol (
1669- "manifestation", template.manifestation,
1670- manifestations_table);
1671- if (!subwhere.is_empty ())
1672- where.extend (subwhere);
1673- }
1674-
1675- // Actor
1676- if (template.actor != "")
1677- {
1678- string val = template.actor;
1679- bool like = parse_wildcard (ref val);
1680- bool negated = parse_negation (ref val);
1681-
1682- if (like)
1683- where.add_wildcard_condition ("actor", val, negated);
1684- else
1685- where.add_match_condition ("actor",
1686- actors_table.get_id (val), negated);
1687- }
1688-
1689- // Origin
1690- if (template.origin != "")
1691- {
1692- string val = template.origin;
1693- bool like = parse_wildcard (ref val);
1694- bool negated = parse_negation (ref val);
1695- assert_no_noexpand (val, "origin");
1696-
1697- if (like)
1698- where.add_wildcard_condition ("origin", val, negated);
1699- else
1700- where.add_text_condition_subquery ("origin", val, negated);
1701- }
1702-
1703- // Subject templates within the same event template are AND'd
1704- // See LP bug #592599.
1705- for (int i = 0; i < template.num_subjects(); ++i)
1706- {
1707- Subject subject_template = template.subjects[i];
1708-
1709- // Subject interpretation
1710- if (subject_template.interpretation != "")
1711- {
1712- assert_no_wildcard ("subject interpretation",
1713- template.interpretation);
1714- WhereClause subwhere = get_where_clause_for_symbol (
1715- "subj_interpretation", subject_template.interpretation,
1716- interpretations_table);
1717- if (!subwhere.is_empty ())
1718- where.extend (subwhere);
1719- }
1720-
1721- // Subject manifestation
1722- if (subject_template.manifestation != "")
1723- {
1724- assert_no_wildcard ("subject manifestation",
1725- subject_template.manifestation);
1726- WhereClause subwhere = get_where_clause_for_symbol (
1727- "subj_manifestation", subject_template.manifestation,
1728- manifestations_table);
1729- if (!subwhere.is_empty ())
1730- where.extend (subwhere);
1731- }
1732-
1733- // Mime-Type
1734- if (subject_template.mimetype != "")
1735- {
1736- string val = subject_template.mimetype;
1737- bool like = parse_wildcard (ref val);
1738- bool negated = parse_negation (ref val);
1739- assert_no_noexpand (val, "mime-type");
1740-
1741- if (like)
1742- where.add_wildcard_condition (
1743- "subj_mimetype", val, negated);
1744- else
1745- where.add_match_condition ("subj_mimetype",
1746- mimetypes_table.get_id (val), negated);
1747- }
1748-
1749- // URI
1750- if (subject_template.uri != "")
1751- {
1752- string val = subject_template.uri;
1753- bool like = parse_wildcard (ref val);
1754- bool negated = parse_negation (ref val);
1755- assert_no_noexpand (val, "uri");
1756-
1757- if (like)
1758- where.add_wildcard_condition ("subj_id", val, negated);
1759- else
1760- where.add_text_condition_subquery ("subj_id", val, negated);
1761- }
1762-
1763- // Origin
1764- if (subject_template.origin != "")
1765- {
1766- string val = subject_template.origin;
1767- bool like = parse_wildcard (ref val);
1768- bool negated = parse_negation (ref val);
1769- assert_no_noexpand (val, "subject origin");
1770-
1771- if (like)
1772- where.add_wildcard_condition (
1773- "subj_origin", val, negated);
1774- else
1775- where.add_text_condition_subquery (
1776- "subj_origin", val, negated);
1777- }
1778-
1779- // Text
1780- if (subject_template.text != "")
1781- {
1782- // Negation, noexpand and prefix search aren't supported
1783- // for subject texts, but "!", "+" and "*" are valid as
1784- // plain text characters.
1785- where.add_text_condition_subquery ("subj_text_id",
1786- subject_template.text, false);
1787- }
1788-
1789- // Current URI
1790- if (subject_template.current_uri != "")
1791- {
1792- string val = subject_template.current_uri;
1793- bool like = parse_wildcard (ref val);
1794- bool negated = parse_negation (ref val);
1795- assert_no_noexpand (val, "current_uri");
1796-
1797- if (like)
1798- where.add_wildcard_condition (
1799- "subj_id_current", val, negated);
1800- else
1801- where.add_text_condition_subquery (
1802- "subj_id_current", val, negated);
1803- }
1804-
1805- // Subject storage
1806- if (subject_template.storage != "")
1807- {
1808- string val = subject_template.storage;
1809- assert_no_negation ("subject storage", val);
1810- assert_no_wildcard ("subject storage", val);
1811- assert_no_noexpand (val, "subject storage");
1812- where.add_text_condition_subquery ("subj_storage_id", val);
1813- }
1814- }
1815-
1816- return where;
1817- }
1818-
1819- // Used by get_where_clause_from_event_templates
1820- /**
1821- * Check if the value starts with the negation operator. If it does,
1822- * remove the operator from the value and return true. Otherwise,
1823- * return false.
1824- */
1825- public static bool parse_negation (ref string val)
1826- {
1827- if (!val.has_prefix ("!"))
1828- return false;
1829- val = val.substring (1);
1830- return true;
1831- }
1832-
1833- // Used by get_where_clause_from_event_templates
1834- /**
1835- * If the value starts with the negation operator, throw an
1836- * error.
1837- */
1838- protected void assert_no_negation (string field, string val)
1839- throws EngineError
1840- {
1841- if (!val.has_prefix ("!"))
1842- return;
1843- string error_message =
1844- "Field '%s' doesn't support negation".printf (field);
1845- warning (error_message);
1846- throw new EngineError.INVALID_ARGUMENT (error_message);
1847- }
1848-
1849- // Used by get_where_clause_from_event_templates
1850- /**
1851- * Check if the value starts with the noexpand operator. If it does,
1852- * remove the operator from the value and return true. Otherwise,
1853- * return false.
1854- *
1855- * Check for the negation operator before calling this function.
1856- */
1857- public static bool parse_noexpand (ref string val)
1858- {
1859- if (!val.has_prefix ("+"))
1860- return false;
1861- val = val.substring (1);
1862- return true;
1863- }
1864-
1865- // Used by get_where_clause_from_event_templates
1866- /**
1867- * If the value starts with the negation operator, throw an
1868- * error.
1869- */
1870- protected void assert_no_noexpand (string field, string val)
1871- throws EngineError
1872- {
1873- if (!val.has_prefix ("+"))
1874- return;
1875- string error_message =
1876- "Field '%s' doesn't support the no-expand operator".printf (field);
1877- warning (error_message);
1878- throw new EngineError.INVALID_ARGUMENT (error_message);
1879- }
1880-
1881- // Used by get_where_clause_from_event_templates
1882- /**
1883- * Check if the value ends with the wildcard character. If it does,
1884- * remove the wildcard character from the value and return true.
1885- * Otherwise, return false.
1886- */
1887- public static bool parse_wildcard (ref string val)
1888- {
1889- if (!val.has_suffix ("*"))
1890- return false;
1891- unowned uint8[] val_data = val.data;
1892- val_data[val_data.length-1] = '\0';
1893- return true;
1894- }
1895-
1896- // Used by get_where_clause_from_event_templates
1897- /**
1898- * If the value ends with the wildcard character, throw an error.
1899- */
1900- protected void assert_no_wildcard (string field, string val)
1901- throws EngineError
1902- {
1903- if (!val.has_suffix ("*"))
1904- return;
1905- string error_message =
1906- "Field '%s' doesn't support prefix search".printf (field);
1907- warning (error_message);
1908- throw new EngineError.INVALID_ARGUMENT (error_message);
1909- }
1910-
1911- protected WhereClause get_where_clause_for_symbol (string table_name,
1912- string symbol, TableLookup lookup_table) throws EngineError
1913- {
1914- string _symbol = symbol;
1915- bool negated = parse_negation (ref _symbol);
1916- bool noexpand = parse_noexpand (ref _symbol);
1917- List<unowned string> symbols;
1918- if (noexpand)
1919- symbols = new List<unowned string> ();
1920- else
1921- symbols = Symbol.get_all_children (_symbol);
1922- symbols.prepend (_symbol);
1923-
1924- WhereClause subwhere = new WhereClause(
1925- WhereClause.Type.OR, negated);
1926-
1927- if (symbols.length () == 1)
1928- {
1929- subwhere.add_match_condition (table_name,
1930- lookup_table.get_id (_symbol));
1931- }
1932- else
1933- {
1934- var sb = new StringBuilder ();
1935- foreach (unowned string uri in symbols)
1936- {
1937- sb.append_printf ("%d,", lookup_table.get_id (uri));
1938- }
1939- sb.truncate (sb.len - 1);
1940-
1941- string sql = "%s IN (%s)".printf(table_name, sb.str);
1942- subwhere.add(sql);
1943- }
1944-
1945- return subwhere;
1946+
1947+ base.close ();
1948 }
1949
1950 private void handle_move_event (Event event)
1951@@ -1194,24 +344,6 @@
1952 return 0;
1953 }
1954
1955- private void delete_from_cache (string table, int64 rowid)
1956- {
1957- TableLookup table_lookup;
1958-
1959- if (table == "interpretation")
1960- table_lookup = interpretations_table;
1961- else if (table == "manifestation")
1962- table_lookup = manifestations_table;
1963- else if (table == "mimetype")
1964- table_lookup = mimetypes_table;
1965- else if (table == "actor")
1966- table_lookup = actors_table;
1967- else
1968- return;
1969-
1970- table_lookup.remove((int) rowid);
1971- }
1972-
1973 }
1974
1975 }
1976
1977=== modified file 'src/extension-store.vala'
1978--- src/extension-store.vala 2012-01-26 10:08:09 +0000
1979+++ src/extension-store.vala 2012-02-05 18:08:19 +0000
1980@@ -25,7 +25,7 @@
1981 public class ExtensionStore : Object
1982 {
1983
1984- private Zeitgeist.SQLite.ZeitgeistDatabase database;
1985+ private Zeitgeist.SQLite.Database database;
1986 private unowned Sqlite.Database db;
1987 private Sqlite.Statement storage_stmt;
1988 private Sqlite.Statement retrieval_stmt;
1989
1990=== modified file 'src/remote.vala'
1991--- src/remote.vala 2011-11-29 16:04:59 +0000
1992+++ src/remote.vala 2012-02-05 18:08:19 +0000
1993@@ -114,12 +114,13 @@
1994 [DBus (name = "org.gnome.zeitgeist.Index")]
1995 public interface RemoteSimpleIndexer : Object
1996 {
1997- [DBus (signature = "a(asaasay)u")]
1998- public abstract async Variant search (
1999+ public abstract async void search (
2000 string query_string,
2001 [DBus (signature = "(xx)")] Variant time_range,
2002 [DBus (signature = "a(asaasay)")] Variant filter_templates,
2003- uint offset, uint count, uint result_type) throws Error;
2004+ uint offset, uint count, uint result_type,
2005+ [DBus (signature = "a(asaasay)")] out Variant events,
2006+ out uint matches) throws Error;
2007 }
2008
2009 /* FIXME: Remove this! Only here because of a bug in Vala (see ext-fts) */
2010
2011=== modified file 'src/sql-schema.vala'
2012--- src/sql-schema.vala 2012-01-30 18:14:03 +0000
2013+++ src/sql-schema.vala 2012-02-05 18:08:19 +0000
2014@@ -70,7 +70,7 @@
2015 }
2016 }
2017
2018- private static int get_schema_version (Sqlite.Database database)
2019+ public static int get_schema_version (Sqlite.Database database)
2020 {
2021 var sql = "SELECT version FROM schema_version WHERE schema='core'";
2022 int schema_version = -1;
2023
2024=== modified file 'src/sql.vala'
2025--- src/sql.vala 2012-01-25 17:37:55 +0000
2026+++ src/sql.vala 2012-02-05 18:08:19 +0000
2027@@ -51,8 +51,10 @@
2028
2029 public delegate void DeletionCallback (string table, int64 rowid);
2030
2031- public class ZeitgeistDatabase : Object
2032+ public class Database : Object
2033 {
2034+ private const int DEFAULT_OPEN_FLAGS =
2035+ Sqlite.OPEN_READWRITE | Sqlite.OPEN_CREATE;
2036
2037 public Sqlite.Statement event_insertion_stmt;
2038 public Sqlite.Statement id_retrieval_stmt;
2039@@ -64,12 +66,28 @@
2040 public Sqlite.Database database;
2041
2042 private DeletionCallback? deletion_callback = null;
2043+ private bool is_read_only;
2044
2045- public ZeitgeistDatabase () throws EngineError
2046+ public Database () throws EngineError
2047 {
2048 open_database (true);
2049
2050- prepare_queries ();
2051+ prepare_read_queries ();
2052+ prepare_modification_queries ();
2053+
2054+ // Register a data change notification callback to look for
2055+ // deletions, so we can keep the TableLookups up to date.
2056+ database.update_hook (update_callback);
2057+ }
2058+
2059+ public Database.read_only () throws EngineError
2060+ {
2061+ is_read_only = true;
2062+ open_database (false);
2063+
2064+ prepare_read_queries ();
2065+ // not initializing the modification queries will let us find
2066+ // issues more easily
2067
2068 // Register a data change notification callback to look for
2069 // deletions, so we can keep the TableLookups up to date.
2070@@ -79,9 +97,10 @@
2071 private void open_database (bool retry)
2072 throws EngineError
2073 {
2074+ int flags = is_read_only ? Sqlite.OPEN_READONLY : DEFAULT_OPEN_FLAGS;
2075 int rc = Sqlite.Database.open_v2 (
2076 Utils.get_database_file_path (),
2077- out database);
2078+ out database, flags);
2079
2080 if (rc == Sqlite.OK)
2081 {
2082@@ -89,7 +108,19 @@
2083 {
2084 // Error (like a malformed database) may not be exposed
2085 // until we try to operate on the database.
2086- DatabaseSchema.ensure_schema (database);
2087+ if (is_read_only)
2088+ {
2089+ int ver = DatabaseSchema.get_schema_version (database);
2090+ if (ver != DatabaseSchema.CORE_SCHEMA_VERSION)
2091+ {
2092+ throw new EngineError.DATABASE_CANTOPEN (
2093+ "Unable to open database");
2094+ }
2095+ }
2096+ else
2097+ {
2098+ DatabaseSchema.ensure_schema (database);
2099+ }
2100 }
2101 catch (EngineError err)
2102 {
2103@@ -296,7 +327,22 @@
2104 }
2105 }
2106
2107- private void prepare_queries () throws EngineError
2108+ private void prepare_read_queries () throws EngineError
2109+ {
2110+ int rc;
2111+ string sql;
2112+
2113+ // Event ID retrieval statement
2114+ sql = """
2115+ SELECT id FROM event
2116+ WHERE timestamp=? AND interpretation=? AND
2117+ manifestation=? AND actor=?
2118+ """;
2119+ rc = database.prepare_v2 (sql, -1, out id_retrieval_stmt);
2120+ assert_query_success (rc, "Event ID retrieval query error");
2121+ }
2122+
2123+ private void prepare_modification_queries () throws EngineError
2124 {
2125 int rc;
2126 string sql;
2127@@ -324,15 +370,6 @@
2128 rc = database.prepare_v2 (sql, -1, out event_insertion_stmt);
2129 assert_query_success (rc, "Insertion query error");
2130
2131- // Event ID retrieval statement
2132- sql = """
2133- SELECT id FROM event
2134- WHERE timestamp=? AND interpretation=? AND
2135- manifestation=? AND actor=?
2136- """;
2137- rc = database.prepare_v2 (sql, -1, out id_retrieval_stmt);
2138- assert_query_success (rc, "Event ID retrieval query error");
2139-
2140 // Move handling statment
2141 sql = """
2142 UPDATE event
2143
2144=== modified file 'src/table-lookup.vala'
2145--- src/table-lookup.vala 2012-01-26 10:08:09 +0000
2146+++ src/table-lookup.vala 2012-02-05 18:08:19 +0000
2147@@ -34,7 +34,7 @@
2148 private HashTable<string, int> value_to_id;
2149 private Sqlite.Statement insertion_stmt;
2150
2151- public TableLookup (ZeitgeistDatabase database, string table_name)
2152+ public TableLookup (Database database, string table_name)
2153 {
2154 db = database.database;
2155 table = table_name;
2156
2157=== modified file 'src/utils.vala'
2158--- src/utils.vala 2011-12-31 15:57:15 +0000
2159+++ src/utils.vala 2012-02-05 18:08:19 +0000
2160@@ -127,6 +127,59 @@
2161 File dbfile = File.new_for_path (get_database_file_path ());
2162 dbfile.set_display_name (get_database_file_retire_name ());
2163 }
2164+
2165+ /**
2166+ * Check if the value starts with the negation operator. If it does,
2167+ * remove the operator from the value and return true. Otherwise,
2168+ * return false.
2169+ */
2170+ public static bool parse_negation (ref string val)
2171+ {
2172+ if (!val.has_prefix ("!"))
2173+ return false;
2174+ val = val.substring (1);
2175+ return true;
2176+ }
2177+
2178+ /**
2179+ * Check if the value starts with the noexpand operator. If it does,
2180+ * remove the operator from the value and return true. Otherwise,
2181+ * return false.
2182+ *
2183+ * Check for the negation operator before calling this function.
2184+ */
2185+ public static bool parse_noexpand (ref string val)
2186+ {
2187+ if (!val.has_prefix ("+"))
2188+ return false;
2189+ val = val.substring (1);
2190+ return true;
2191+ }
2192+
2193+
2194+ /**
2195+ * Check if the value ends with the wildcard character. If it does,
2196+ * remove the wildcard character from the value and return true.
2197+ * Otherwise, return false.
2198+ */
2199+ public static bool parse_wildcard (ref string val)
2200+ {
2201+ if (!val.has_suffix ("*"))
2202+ return false;
2203+ unowned uint8[] val_data = val.data;
2204+ val_data[val_data.length-1] = '\0';
2205+ return true;
2206+ }
2207+
2208+ /**
2209+ * Return true if a string is empty (null or containing just a null
2210+ * byte).
2211+ */
2212+ public static bool is_empty_string (string? s)
2213+ {
2214+ return s == null || s == "";
2215+ }
2216+
2217 }
2218 }
2219
2220
2221=== modified file 'src/zeitgeist-daemon.vala'
2222--- src/zeitgeist-daemon.vala 2012-01-26 10:08:09 +0000
2223+++ src/zeitgeist-daemon.vala 2012-02-05 18:08:19 +0000
2224@@ -458,7 +458,7 @@
2225 var lm = LogLevelFlags.LEVEL_MESSAGE;
2226 var lw = LogLevelFlags.LEVEL_WARNING;
2227 var lc = LogLevelFlags.LEVEL_CRITICAL;
2228- switch (log_level)
2229+ switch (log_level.up ())
2230 {
2231 case "DEBUG":
2232 discarded = 0;
2233
2234=== modified file 'test/direct/Makefile.am'
2235--- test/direct/Makefile.am 2012-02-02 16:24:22 +0000
2236+++ test/direct/Makefile.am 2012-02-05 18:08:19 +0000
2237@@ -17,6 +17,7 @@
2238 $(NULL)
2239
2240 SRC_FILES = \
2241+ $(top_srcdir)/src/db-reader.vala \
2242 $(top_srcdir)/src/engine.vala \
2243 $(top_srcdir)/src/utils.vala \
2244 $(top_srcdir)/src/errors.vala \
2245
2246=== modified file 'test/direct/query-operators-test.vala'
2247--- test/direct/query-operators-test.vala 2012-01-26 10:08:09 +0000
2248+++ test/direct/query-operators-test.vala 2012-02-05 18:08:19 +0000
2249@@ -18,6 +18,8 @@
2250 *
2251 */
2252
2253+using Zeitgeist;
2254+
2255 int main (string[] args)
2256 {
2257
2258@@ -45,33 +47,18 @@
2259
2260 private class PublicEngine : Zeitgeist.Engine
2261 {
2262- public bool PUBLIC_parse_negation (ref string val)
2263- {
2264- return parse_negation (ref val);
2265- }
2266-
2267 public void PUBLIC_assert_no_negation (string field, string val)
2268 throws Zeitgeist.EngineError
2269 {
2270 assert_no_negation (field, val);
2271 }
2272
2273- public bool PUBLIC_parse_noexpand (ref string val)
2274- {
2275- return parse_noexpand (ref val);
2276- }
2277-
2278 public void PUBLIC_assert_no_noexpand (string field, string val)
2279 throws Zeitgeist.EngineError
2280 {
2281 assert_no_noexpand (field, val);
2282 }
2283
2284- public bool PUBLIC_parse_wildcard (ref string val)
2285- {
2286- return parse_wildcard (ref val);
2287- }
2288-
2289 public void PUBLIC_assert_no_wildcard (string field, string val)
2290 throws Zeitgeist.EngineError
2291 {
2292@@ -82,22 +69,21 @@
2293
2294 public void parse_negation_test ()
2295 {
2296- PublicEngine engine = new PublicEngine ();
2297 string val;
2298
2299 // Test string without a negation
2300 val = "no negation";
2301- assert (engine.PUBLIC_parse_negation (ref val) == false);
2302+ assert (Utils.parse_negation (ref val) == false);
2303 assert (val == "no negation");
2304
2305 // Test string with a valid negation
2306 val = "!negation";
2307- assert (engine.PUBLIC_parse_negation (ref val) == true);
2308+ assert (Utils.parse_negation (ref val) == true);
2309 assert (val == "negation");
2310
2311 // Test negation character in a meaningless position
2312 val = "some ! chars";
2313- assert (engine.PUBLIC_parse_negation (ref val) == false);
2314+ assert (Utils.parse_negation (ref val) == false);
2315 assert (val == "some ! chars");
2316 }
2317
2318@@ -121,22 +107,21 @@
2319
2320 public void parse_noexpand_test ()
2321 {
2322- PublicEngine engine = new PublicEngine ();
2323 string val;
2324
2325 // Test string without a negation
2326 val = "no expand";
2327- assert (engine.PUBLIC_parse_noexpand (ref val) == false);
2328+ assert (Utils.parse_noexpand (ref val) == false);
2329 assert (val == "no expand");
2330
2331 // Test string with a valid noexpand
2332 val = "+noexpand";
2333- assert (engine.PUBLIC_parse_noexpand (ref val) == true);
2334+ assert (Utils.parse_noexpand (ref val) == true);
2335 assert (val == "noexpand");
2336
2337 // Test negation character in a meaningless position
2338 val = "some + chars++";
2339- assert (engine.PUBLIC_parse_noexpand (ref val) == false);
2340+ assert (Utils.parse_noexpand (ref val) == false);
2341 assert (val == "some + chars++");
2342 }
2343
2344@@ -160,22 +145,21 @@
2345
2346 public void parse_wildcard_test ()
2347 {
2348- PublicEngine engine = new PublicEngine ();
2349 string val;
2350
2351 // Test string without a wildcard
2352 val = "no wildcard";
2353- assert (engine.PUBLIC_parse_wildcard (ref val) == false);
2354+ assert (Utils.parse_wildcard (ref val) == false);
2355 assert (val == "no wildcard");
2356
2357 // Test string with a valid wildcard
2358 val = "yes wildcar*";
2359- assert (engine.PUBLIC_parse_wildcard (ref val) == true);
2360+ assert (Utils.parse_wildcard (ref val) == true);
2361 assert (val == "yes wildcar");
2362
2363 // Test wildcard character in a meaningless position
2364 val = "some * chars";
2365- assert (engine.PUBLIC_parse_wildcard ( ref val) == false);
2366+ assert (Utils.parse_wildcard ( ref val) == false);
2367 assert (val == "some * chars");
2368 }
2369
2370
2371=== modified file 'test/direct/table-lookup-test.vala'
2372--- test/direct/table-lookup-test.vala 2011-12-31 00:31:17 +0000
2373+++ test/direct/table-lookup-test.vala 2012-02-05 18:08:19 +0000
2374@@ -52,7 +52,7 @@
2375
2376 public void basic_test ()
2377 {
2378- ZeitgeistDatabase database = new Zeitgeist.SQLite.ZeitgeistDatabase ();
2379+ Database database = new Zeitgeist.SQLite.Database ();
2380 unowned Sqlite.Database db = database.database;
2381 TableLookup table_lookup = new TableLookup (database, "actor");
2382
2383@@ -71,7 +71,7 @@
2384 public void engine_test ()
2385 {
2386 PublicEngine engine = new PublicEngine ();
2387- ZeitgeistDatabase database = engine.database;
2388+ Database database = engine.database;
2389 unowned Sqlite.Database db = database.database;
2390 TableLookup table_lookup = engine.get_actors_table_lookup();
2391

Subscribers

People subscribed via source and target branches