Merge lp:~mhr3/zeitgeist/refactoring into lp:~zeitgeist/zeitgeist/bluebird
- refactoring
- Merge into 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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Siegfried Gevatter | Approve | ||
Review via email: mp+91334@code.launchpad.net |
Commit message
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
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 |
Good work.
You aren't setting `is_read_only' to false anywhere. Other than that, looks good.