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