~ubuntu-branches/ubuntu/precise/zeitgeist/precise-proposed

« back to all changes in this revision

Viewing changes to _zeitgeist/engine/main.py

  • Committer: Bazaar Package Importer
  • Author(s): Siegfried-Angel Gevatter Pujals
  • Date: 2010-01-20 00:23:48 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20100120002348-hn9uooxjjmdgb4c0
Tags: 0.3.2-1
* New upstream release:
   - Add FindEvents, optimized shorthand for GetEvents(FindEventIds(...)).
   - Fix DeleteEvents and make it ignore bad requests.
   - Fix GetEvents not to raise an exception when called with an empty list.
   - ZeitgeistClient.get_version() now returns a Python list.
   - Some code refactoring, documentation changes and other little fixes.
* Delete debian/zeitgeist-daemon.bash_completion, as it's now in the tarball,
  and update debian/rules to reflect this change.
* debian/control.in:
   - Change Homepage field to zeitgeist-project.com.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
# Zeitgeist
4
4
#
5
5
# Copyright © 2009 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@gmail.com>
6
 
# Copyright © 2009 Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>
 
6
# Copyright © 2009-2010 Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>
7
7
# Copyright © 2009 Seif Lotfy <seif@lotfy.com>
8
8
# Copyright © 2009 Markus Korn <thekorn@gmx.net>
9
 
# Copyright © 2009 Alexander Gabriel <einalex@mayanna.org>
10
 
 
 
9
#
11
10
# This program is free software: you can redistribute it and/or modify
12
11
# it under the terms of the GNU Lesser General Public License as published by
13
12
# the Free Software Foundation, either version 3 of the License, or
32
31
        ResultType, get_timestamp_for_now
33
32
from _zeitgeist.engine.extension import ExtensionsCollection, load_class
34
33
from _zeitgeist.engine import constants
 
34
from _zeitgeist.engine.sql import get_default_cursor, unset_cursor, \
 
35
        TableLookup, WhereClause
35
36
 
36
37
logging.basicConfig(level=logging.DEBUG)
37
38
log = logging.getLogger("zeitgeist.engine")
38
39
 
39
 
class UnicodeCursor(sqlite3.Cursor):
40
 
        
41
 
        @staticmethod
42
 
        def fix_unicode(obj):
43
 
                if isinstance(obj, str):
44
 
                        obj = obj.decode("UTF-8")
45
 
                return unicode(obj)             
46
 
        
47
 
        def execute(self, statement, parameters=None):
48
 
                if parameters is not None:
49
 
                        parameters = [self.fix_unicode(p) for p in parameters]
50
 
                        return super(UnicodeCursor, self).execute(statement, parameters)
51
 
                else:
52
 
                        return super(UnicodeCursor, self).execute(statement)
53
 
 
54
 
def create_db(file_path):
55
 
        """Create the database and return a default cursor for it"""
56
 
        log.info("Using database: %s" % file_path)
57
 
        conn = sqlite3.connect(file_path)
58
 
        conn.row_factory = sqlite3.Row
59
 
        cursor = conn.cursor(UnicodeCursor)
60
 
        
61
 
        # uri
62
 
        cursor.execute("""
63
 
                CREATE TABLE IF NOT EXISTS uri
64
 
                        (id INTEGER PRIMARY KEY, value VARCHAR UNIQUE)
65
 
                """)
66
 
        cursor.execute("""
67
 
                CREATE UNIQUE INDEX IF NOT EXISTS uri_value ON uri(value)
68
 
                """)
69
 
        
70
 
        # interpretation
71
 
        cursor.execute("""
72
 
                CREATE TABLE IF NOT EXISTS interpretation
73
 
                        (id INTEGER PRIMARY KEY, value VARCHAR UNIQUE)
74
 
                """)
75
 
        cursor.execute("""
76
 
                CREATE UNIQUE INDEX IF NOT EXISTS interpretation_value
77
 
                        ON interpretation(value)
78
 
                """)
79
 
        
80
 
        # manifestation
81
 
        cursor.execute("""
82
 
                CREATE TABLE IF NOT EXISTS manifestation
83
 
                        (id INTEGER PRIMARY KEY, value VARCHAR UNIQUE)
84
 
                """)
85
 
        cursor.execute("""
86
 
                CREATE UNIQUE INDEX IF NOT EXISTS manifestation_value
87
 
                        ON manifestation(value)""")
88
 
        
89
 
        # mimetype
90
 
        cursor.execute("""
91
 
                CREATE TABLE IF NOT EXISTS mimetype
92
 
                        (id INTEGER PRIMARY KEY, value VARCHAR UNIQUE)
93
 
                """)
94
 
        cursor.execute("""
95
 
                CREATE UNIQUE INDEX IF NOT EXISTS mimetype_value
96
 
                        ON mimetype(value)""")
97
 
        
98
 
        # app
99
 
        cursor.execute("""
100
 
                CREATE TABLE IF NOT EXISTS actor
101
 
                        (id INTEGER PRIMARY KEY, value VARCHAR UNIQUE)
102
 
                """)
103
 
        cursor.execute("""
104
 
                CREATE UNIQUE INDEX IF NOT EXISTS actor_value
105
 
                        ON actor(value)""")
106
 
        
107
 
        # text
108
 
        cursor.execute("""
109
 
                CREATE TABLE IF NOT EXISTS text
110
 
                        (id INTEGER PRIMARY KEY, value VARCHAR UNIQUE)
111
 
                """)
112
 
        cursor.execute("""
113
 
                CREATE UNIQUE INDEX IF NOT EXISTS text_value
114
 
                        ON text(value)""")
115
 
        
116
 
        # payload, there's no value index for payload,
117
 
        # they can only be fetched by id
118
 
        cursor.execute("""
119
 
                CREATE TABLE IF NOT EXISTS payload
120
 
                        (id INTEGER PRIMARY KEY, value BLOB)
121
 
                """)    
122
 
        
123
 
        # storage, represented by a StatefulEntityTable
124
 
        cursor.execute("""
125
 
                CREATE TABLE IF NOT EXISTS storage
126
 
                        (id INTEGER PRIMARY KEY,
127
 
                         value VARCHAR UNIQUE,
128
 
                         state INTEGER)
129
 
                """)
130
 
        cursor.execute("""
131
 
                CREATE UNIQUE INDEX IF NOT EXISTS storage_value
132
 
                        ON storage(value)""")
133
 
        
134
 
        # event - the primary table for log statements
135
 
        # note that event.id is NOT unique, we can have multiple subjects per id
136
 
        # timestamps are integers (for now), if you would like to change it
137
 
        # please start a bugreport for it. In case we agree on this change
138
 
        # remember to also fix our unittests to reflect this change
139
 
        cursor.execute("""
140
 
                CREATE TABLE IF NOT EXISTS event
141
 
                        (id INTEGER,
142
 
                         timestamp INTEGER,
143
 
                         interpretation INTEGER,
144
 
                         manifestation INTEGER,                  
145
 
                         actor INTEGER,                  
146
 
                         payload INTEGER,
147
 
                         subj_id INTEGER,
148
 
                         subj_interpretation INTEGER,
149
 
                         subj_manifestation INTEGER,
150
 
                         subj_origin INTEGER,
151
 
                         subj_mimetype INTEGER,
152
 
                         subj_text INTEGER,
153
 
                         subj_storage INTEGER,
154
 
                         CONSTRAINT unique_event UNIQUE (timestamp, interpretation, manifestation, actor, subj_id)
155
 
                         )
156
 
                """)
157
 
        cursor.execute("""
158
 
                CREATE INDEX IF NOT EXISTS event_id
159
 
                        ON event(id)""")
160
 
        cursor.execute("""
161
 
                CREATE INDEX IF NOT EXISTS event_timestamp
162
 
                        ON event(timestamp)""")
163
 
        cursor.execute("""
164
 
                CREATE INDEX IF NOT EXISTS event_interpretation
165
 
                        ON event(interpretation)""")
166
 
        cursor.execute("""
167
 
                CREATE INDEX IF NOT EXISTS event_manifestation
168
 
                        ON event(manifestation)""")
169
 
        cursor.execute("""
170
 
                CREATE INDEX IF NOT EXISTS event_actor
171
 
                        ON event(actor)""")
172
 
        cursor.execute("""
173
 
                CREATE INDEX IF NOT EXISTS event_subj_id
174
 
                        ON event(subj_id)""")
175
 
        cursor.execute("""
176
 
                CREATE INDEX IF NOT EXISTS event_subj_interpretation
177
 
                        ON event(subj_interpretation)""")
178
 
        cursor.execute("""
179
 
                CREATE INDEX IF NOT EXISTS event_subj_manifestation
180
 
                        ON event(subj_manifestation)""")
181
 
        cursor.execute("""
182
 
                CREATE INDEX IF NOT EXISTS event_subj_origin
183
 
                        ON event(subj_origin)""")
184
 
        cursor.execute("""
185
 
                CREATE INDEX IF NOT EXISTS event_subj_mimetype
186
 
                        ON event(subj_mimetype)""")
187
 
        cursor.execute("""
188
 
                CREATE INDEX IF NOT EXISTS event_subj_text
189
 
                        ON event(subj_text)""")
190
 
        cursor.execute("""
191
 
                CREATE INDEX IF NOT EXISTS event_subj_storage
192
 
                        ON event(subj_storage)""")
193
 
        
194
 
        #cursor.execute("DROP VIEW event_view")
195
 
        cursor.execute("""
196
 
                CREATE VIEW IF NOT EXISTS event_view AS
197
 
                        SELECT event.id,
198
 
                                event.timestamp,
199
 
                                event.interpretation,
200
 
                                event.manifestation,
201
 
                                event.actor,
202
 
                                event.payload,
203
 
                                (SELECT value FROM uri WHERE uri.id=event.subj_id)
204
 
                                        AS subj_uri,
205
 
                                event.subj_interpretation,
206
 
                                event.subj_manifestation,
207
 
                                (SELECT value FROM uri WHERE uri.id=event.subj_origin)
208
 
                                        AS subj_origin,
209
 
                                event.subj_mimetype,
210
 
                                (SELECT value FROM text WHERE text.id = event.subj_text)
211
 
                                        AS subj_text,
212
 
                                (SELECT value FROM storage
213
 
                                        WHERE storage.id=event.subj_storage) AS subj_storage,
214
 
                                (SELECT state FROM storage
215
 
                                        WHERE storage.id=event.subj_storage) AS subj_storage_state
216
 
                        FROM event
217
 
                """)
218
 
        
219
 
        return cursor
220
 
 
221
 
_cursor = None
222
 
def get_default_cursor():
223
 
        global _cursor
224
 
        if not _cursor:
225
 
                dbfile = constants.DATABASE_FILE
226
 
                _cursor = create_db(dbfile)
227
 
        return _cursor
228
 
 
229
 
class TableLookup(dict):
230
 
        
231
 
        # We are not using an LRUCache as pressumably there won't be thousands
232
 
        # of manifestations/interpretations/mimetypes/actors on most
233
 
        # installations, so we can save us the overhead of tracking their usage.
234
 
        
235
 
        def __init__(self, cursor, table):
236
 
                
237
 
                self._cursor = cursor
238
 
                self._table = table
239
 
                
240
 
                for row in cursor.execute("SELECT id, value FROM %s" % table):
241
 
                        self[row["value"]] = row["id"]
242
 
                
243
 
                self._inv_dict = dict((value, key) for key, value in self.iteritems())
244
 
        
245
 
        def __getitem__(self, name):
246
 
                # Use this for inserting new properties into the database
247
 
                if name in self:
248
 
                        super(TableLookup, self).__getitem__(name)
249
 
                try:
250
 
                        self._cursor.execute(
251
 
                        "INSERT INTO %s (value) VALUES (?)" % self._table, (name,))
252
 
                        id = self._cursor.lastrowid
253
 
                except sqlite3.IntegrityError:
254
 
                        # This shouldn't happen, but just in case
255
 
                        # FIXME: Maybe we should remove it?
256
 
                        id = self._cursor.execute("SELECT id FROM %s WHERE value=?"
257
 
                                % self._table, (name,)).fetchone()[0]
258
 
                # If we are here it's a newly inserted value, insert it into cache
259
 
                self[name] = id
260
 
                self._inv_dict[id] = name
261
 
                return id
262
 
        
263
 
        def value(self, id):
264
 
                # When we fetch an event, it either was already in the database
265
 
                # at the time Zeitgeist started or it was inserted later -using
266
 
                # Zeitgeist-, so here we always have the data in memory already.
267
 
                return self._inv_dict[id]
268
 
        
269
 
        def id(self, name):
270
 
                # Use this when fetching values which are supposed to be in the
271
 
                # database already. Eg., in find_eventids.
272
 
                return super(TableLookup, self).__getitem__(name)
273
 
 
274
40
class ZeitgeistEngine:
275
41
        
276
42
        def __init__ (self):
303
69
                return self.__extensions
304
70
        
305
71
        def close(self):
306
 
                global _cursor
307
72
                self.extensions.unload()
308
73
                self._cursor.connection.close()
309
 
                self._cursor = _cursor = None
 
74
                self._cursor = None
 
75
                unset_cursor()
310
76
        
311
77
        def is_closed(self):
312
78
                return self._cursor is None
333
99
                                getattr(self, "_" + field).value(row["subj_" + field]))
334
100
                return subject
335
101
        
336
 
        def get_events(self, ids):
 
102
        def get_events(self, ids=None, rows=None):
337
103
                """
338
104
                Look up a list of events.
339
105
                """
340
106
                
341
107
                t = time.time()
342
108
                
343
 
                rows = self._cursor.execute("""
344
 
                        SELECT * FROM event_view
345
 
                        WHERE id IN (%s)
346
 
                        """ % ",".join("%d" % id for id in ids)).fetchall()
 
109
                if not ids and not rows:
 
110
                        return []
 
111
                
 
112
                if ids:
 
113
                        rows = self._cursor.execute("""
 
114
                                SELECT * FROM event_view
 
115
                                WHERE id IN (%s)
 
116
                                """ % ",".join("%d" % id for id in ids)).fetchall()
 
117
                else:
 
118
                        ids = (row[0] for row in rows)
 
119
                
347
120
                events = {}
348
121
                for row in rows:
349
122
                        # Assumption: all rows of a same event for its different
369
142
 
370
143
                return sorted_events
371
144
        
372
 
        def insert_events(self, events):
373
 
                t = time.time()
374
 
                m = map(self._insert_event_without_error, events)
375
 
                _cursor.connection.commit()
376
 
                log.debug("Inserted %d events in %fs" % (len(m), time.time()-t))
377
 
                return m
378
 
                
379
 
        def _insert_event_without_error(self, event):
380
 
                try:
381
 
                        return self._insert_event(event)
382
 
                except Exception, e:
383
 
                        log.exception("error while inserting '%r'" %event)
384
 
                        return 0
385
 
        
386
 
        def _insert_event(self, event):
387
 
                if not isinstance(event, Event):
388
 
                        raise ValueError("cannot insert object of type %r" %type(event))
389
 
                if event.id:
390
 
                        raise ValueError("Illegal event: Predefined event id")
391
 
                if not event.subjects:
392
 
                        raise ValueError("Illegal event format: No subject")
393
 
                if not event.timestamp:
394
 
                        event.timestamp = get_timestamp_for_now()
395
 
                
396
 
                event = self.extensions.apply_insert_hooks(event)
397
 
                if event is None:
398
 
                        raise AssertionError("Inserting of event was blocked by an extension")
399
 
                elif not isinstance(event, Event):
400
 
                        raise ValueError("cannot insert object of type %r" %type(event))
401
 
                
402
 
                id = self.next_event_id()
403
 
                
404
 
                if event.payload:
405
 
                        # TODO: Rigth now payloads are not unique and every event has its
406
 
                        # own one. We could optimize this to store those which are repeated
407
 
                        # for different events only once, especially considering that
408
 
                        # events cannot be modified once they've been inserted.
409
 
                        payload_id = self._cursor.execute(
410
 
                                "INSERT INTO payload (value) VALUES (?)", event.payload)
411
 
                        payload_id = self._cursor.lastrowid
412
 
                else:
413
 
                        # Don't use None here, as that'd be inserted literally into the DB
414
 
                        payload_id = ""
415
 
                
416
 
                # Make sure all URIs are inserted
417
 
                _origin = [subject.origin for subject in event.subjects if subject.origin]
418
 
                self._cursor.execute("INSERT OR IGNORE INTO uri (value) %s"
419
 
                        % " UNION ".join(["SELECT ?"] * (len(event.subjects) + len(_origin))),
420
 
                        [subject.uri for subject in event.subjects] + _origin)
421
 
                
422
 
                # Make sure all mimetypes are inserted
423
 
                _mimetype = [subject.mimetype for subject in event.subjects \
424
 
                        if subject.mimetype and not subject.mimetype in self._mimetype]
425
 
                if len(_mimetype) > 1:
426
 
                        self._cursor.execute("INSERT OR IGNORE INTO mimetype (value) %s"
427
 
                                % " UNION ".join(["SELECT ?"] * len(_mimetype)), _mimetype)
428
 
                
429
 
                # Make sure all texts are inserted
430
 
                _text = [subject.text for subject in event.subjects if subject.text]
431
 
                if _text:
432
 
                        self._cursor.execute("INSERT OR IGNORE INTO text (value) %s"
433
 
                                % " UNION ".join(["SELECT ?"] * len(_text)), _text)
434
 
                
435
 
                # Make sure all storages are inserted
436
 
                _storage = [subject.storage for subject in event.subjects if subject.storage]
437
 
                if _storage:
438
 
                        self._cursor.execute("INSERT OR IGNORE INTO storage (value) %s"
439
 
                                % " UNION ".join(["SELECT ?"] * len(_storage)), _storage)
440
 
                
441
 
                try:
442
 
                        for subject in event.subjects:  
443
 
                                self._cursor.execute("""
444
 
                                        INSERT INTO event VALUES (
445
 
                                                ?, ?, ?, ?, ?, ?,
446
 
                                                (SELECT id FROM uri WHERE value=?),
447
 
                                                ?, ?,
448
 
                                                (SELECT id FROM uri WHERE value=?),
449
 
                                                ?,
450
 
                                                (SELECT id FROM text WHERE value=?),
451
 
                                                (SELECT id from storage WHERE value=?)
452
 
                                        )""", (
453
 
                                                id,
454
 
                                                event.timestamp,
455
 
                                                self._interpretation[event.interpretation],
456
 
                                                self._manifestation[event.manifestation],
457
 
                                                self._actor[event.actor],
458
 
                                                payload_id,
459
 
                                                subject.uri,
460
 
                                                self._interpretation[subject.interpretation],
461
 
                                                self._manifestation[subject.manifestation],
462
 
                                                subject.origin,
463
 
                                                self._mimetype[subject.mimetype],
464
 
                                                subject.text,
465
 
                                                subject.storage))
466
 
                except sqlite3.IntegrityError:
467
 
                        # The event was already registered.
468
 
                        # Rollback _last_event_id and return the ID of the original event
469
 
                        self._last_event_id -= 1
470
 
                        self._cursor.execute("""
471
 
                                SELECT id FROM event
472
 
                                WHERE timestamp=? AND interpretation=? AND manifestation=?
473
 
                                        AND actor=?
474
 
                                """, (event.timestamp,
475
 
                                        self._interpretation[event.interpretation],
476
 
                                        self._manifestation[event.manifestation],
477
 
                                        self._actor[event.actor]))
478
 
                        return self._cursor.fetchone()[0]
479
 
                
480
 
                _cursor.connection.commit()
481
 
                
482
 
                return id
483
 
        
484
 
        def delete_events (self, ids):
485
 
                # Extract min and max timestamps for deleted events
486
 
                self._cursor.execute("""
487
 
                        SELECT MIN(timestamp), MAX(timestamp)
488
 
                        FROM event
489
 
                        WHERE id IN (%s)
490
 
                """ % ",".join(["?"] * len(ids)), ids)
491
 
                min_stamp, max_stamp = self._cursor.fetchone()
492
 
        
493
 
                # FIXME: Delete unused interpretation/manifestation/text/etc.
494
 
                self._cursor.execute("DELETE FROM event WHERE id IN (%s)"
495
 
                        % ",".join(["?"] * len(ids)), ids)
496
 
                
497
 
                return min_stamp, max_stamp
498
 
        
499
145
        @staticmethod
500
146
        def _build_templates(templates):
501
147
                for event_template in templates:
548
194
                
549
195
                return where
550
196
        
551
 
        def find_eventids(self, time_range, event_templates, storage_state,
552
 
                max_events, order):
 
197
        def _find_events(self, return_mode, time_range, event_templates,
 
198
                storage_state, max_events, order):
553
199
                """
554
200
                Accepts 'event_templates' as either a real list of Events or as
555
201
                a list of tuples (event_data,subject_data) as we do in the
556
 
                DBus API
 
202
                DBus API.
 
203
                
 
204
                Return modes:
 
205
                 - 0: IDs.
 
206
                 - 1: Events.
557
207
                """
558
208
                
559
209
                t = time.time()
563
213
                if not where.may_have_results():
564
214
                        return []
565
215
                
566
 
                sql = "SELECT DISTINCT id FROM event_view"
 
216
                if return_mode == 0:
 
217
                        sql = "SELECT DISTINCT id FROM event_view"
 
218
                else:
 
219
                        sql = "SELECT * FROM event_view"
 
220
                
567
221
                if where:
568
222
                        sql += " WHERE " + where.sql
569
223
                
577
231
                if max_events > 0:
578
232
                        sql += " LIMIT %d" % max_events
579
233
                
580
 
                result = [row[0] 
581
 
                                for row in self._cursor.execute(sql, where.arguments).fetchall()]
 
234
                result = self._cursor.execute(sql, where.arguments).fetchall()
 
235
                
 
236
                if return_mode == 1:
 
237
                        return self.get_events(rows=result)
 
238
                result = [row[0] for row in result]
582
239
                
583
240
                log.debug("Fetched %d event IDs in %fs" % (len(result), time.time()- t))
584
241
                return result
585
242
        
 
243
        def find_eventids(self, *args):
 
244
                return self._find_events(0, *args)
 
245
        
 
246
        def find_events(self, *args):
 
247
                return self._find_events(1, *args)
 
248
        
586
249
        def find_related_uris(self, timerange, event_templates, result_event_templates,
587
250
                result_storage_state):
588
251
                """
658
321
                        if support >= min_support]
659
322
                return [key for support, key in sorted(results, reverse=True)]
660
323
 
661
 
class WhereClause:
662
 
        
663
 
        AND = " AND "
664
 
        OR = " OR "
665
 
        
666
 
        def __init__(self, relation):
667
 
                self._conditions = []
668
 
                self.arguments = []
669
 
                self._relation = relation
670
 
                self._no_result_member = False
671
 
        
672
 
        def __len__(self):
673
 
                return len(self._conditions)
674
 
        
675
 
        def add(self, condition, arguments):
676
 
                if not condition:
677
 
                        return
678
 
                self._conditions.append(condition)
679
 
                if not hasattr(arguments, "__iter__"):
680
 
                        self.arguments.append(arguments)
 
324
        def insert_events(self, events):
 
325
                t = time.time()
 
326
                m = map(self._insert_event_without_error, events)
 
327
                self._cursor.connection.commit()
 
328
                log.debug("Inserted %d events in %fs" % (len(m), time.time()-t))
 
329
                return m
 
330
        
 
331
        def _insert_event_without_error(self, event):
 
332
                try:
 
333
                        return self._insert_event(event)
 
334
                except Exception, e:
 
335
                        log.exception("error while inserting '%r'" %event)
 
336
                        return 0
 
337
        
 
338
        def _insert_event(self, event):
 
339
                if not isinstance(event, Event):
 
340
                        raise ValueError("cannot insert object of type %r" %type(event))
 
341
                if event.id:
 
342
                        raise ValueError("Illegal event: Predefined event id")
 
343
                if not event.subjects:
 
344
                        raise ValueError("Illegal event format: No subject")
 
345
                if not event.timestamp:
 
346
                        event.timestamp = get_timestamp_for_now()
 
347
                
 
348
                event = self.extensions.apply_insert_hooks(event)
 
349
                if event is None:
 
350
                        raise AssertionError("Inserting of event was blocked by an extension")
 
351
                elif not isinstance(event, Event):
 
352
                        raise ValueError("cannot insert object of type %r" %type(event))
 
353
                
 
354
                id = self.next_event_id()
 
355
                
 
356
                if event.payload:
 
357
                        # TODO: Rigth now payloads are not unique and every event has its
 
358
                        # own one. We could optimize this to store those which are repeated
 
359
                        # for different events only once, especially considering that
 
360
                        # events cannot be modified once they've been inserted.
 
361
                        payload_id = self._cursor.execute(
 
362
                                "INSERT INTO payload (value) VALUES (?)", event.payload)
 
363
                        payload_id = self._cursor.lastrowid
681
364
                else:
682
 
                        self.arguments.extend(arguments)
683
 
        
684
 
        def extend(self, where):
685
 
                self.add(where.sql, where.arguments)
686
 
                if not where.may_have_results():
687
 
                        if self._relation == self.AND:
688
 
                                self.clear()
689
 
                        self.register_no_result()
690
 
        
691
 
        @property
692
 
        def sql(self):
693
 
                if self: # Do not return "()" if there are no conditions
694
 
                        return "(" + self._relation.join(self._conditions) + ")"
695
 
        
696
 
        def register_no_result(self):
697
 
                self._no_result_member = True
698
 
        
699
 
        def may_have_results(self):
700
 
                """
701
 
                Return False if we know from our cached data that the query
702
 
                will give no results.
703
 
                """
704
 
                return len(self._conditions) > 0 or not self._no_result_member
705
 
        
706
 
        def clear(self):
707
 
                """
708
 
                Reset this WhereClause to the state of a newly created one.
709
 
                """
710
 
                self._conditions = []
711
 
                self.arguments = []
712
 
                self._no_result_member = False
 
365
                        # Don't use None here, as that'd be inserted literally into the DB
 
366
                        payload_id = ""
 
367
                
 
368
                # Make sure all URIs are inserted
 
369
                _origin = [subject.origin for subject in event.subjects if subject.origin]
 
370
                self._cursor.execute("INSERT OR IGNORE INTO uri (value) %s"
 
371
                        % " UNION ".join(["SELECT ?"] * (len(event.subjects) + len(_origin))),
 
372
                        [subject.uri for subject in event.subjects] + _origin)
 
373
                
 
374
                # Make sure all mimetypes are inserted
 
375
                _mimetype = [subject.mimetype for subject in event.subjects \
 
376
                        if subject.mimetype and not subject.mimetype in self._mimetype]
 
377
                if len(_mimetype) > 1:
 
378
                        self._cursor.execute("INSERT OR IGNORE INTO mimetype (value) %s"
 
379
                                % " UNION ".join(["SELECT ?"] * len(_mimetype)), _mimetype)
 
380
                
 
381
                # Make sure all texts are inserted
 
382
                _text = [subject.text for subject in event.subjects if subject.text]
 
383
                if _text:
 
384
                        self._cursor.execute("INSERT OR IGNORE INTO text (value) %s"
 
385
                                % " UNION ".join(["SELECT ?"] * len(_text)), _text)
 
386
                
 
387
                # Make sure all storages are inserted
 
388
                _storage = [subject.storage for subject in event.subjects if subject.storage]
 
389
                if _storage:
 
390
                        self._cursor.execute("INSERT OR IGNORE INTO storage (value) %s"
 
391
                                % " UNION ".join(["SELECT ?"] * len(_storage)), _storage)
 
392
                
 
393
                try:
 
394
                        for subject in event.subjects:  
 
395
                                self._cursor.execute("""
 
396
                                        INSERT INTO event VALUES (
 
397
                                                ?, ?, ?, ?, ?, ?,
 
398
                                                (SELECT id FROM uri WHERE value=?),
 
399
                                                ?, ?,
 
400
                                                (SELECT id FROM uri WHERE value=?),
 
401
                                                ?,
 
402
                                                (SELECT id FROM text WHERE value=?),
 
403
                                                (SELECT id from storage WHERE value=?)
 
404
                                        )""", (
 
405
                                                id,
 
406
                                                event.timestamp,
 
407
                                                self._interpretation[event.interpretation],
 
408
                                                self._manifestation[event.manifestation],
 
409
                                                self._actor[event.actor],
 
410
                                                payload_id,
 
411
                                                subject.uri,
 
412
                                                self._interpretation[subject.interpretation],
 
413
                                                self._manifestation[subject.manifestation],
 
414
                                                subject.origin,
 
415
                                                self._mimetype[subject.mimetype],
 
416
                                                subject.text,
 
417
                                                subject.storage))
 
418
                except sqlite3.IntegrityError:
 
419
                        # The event was already registered.
 
420
                        # Rollback _last_event_id and return the ID of the original event
 
421
                        self._last_event_id -= 1
 
422
                        self._cursor.execute("""
 
423
                                SELECT id FROM event
 
424
                                WHERE timestamp=? AND interpretation=? AND manifestation=?
 
425
                                        AND actor=?
 
426
                                """, (event.timestamp,
 
427
                                        self._interpretation[event.interpretation],
 
428
                                        self._manifestation[event.manifestation],
 
429
                                        self._actor[event.actor]))
 
430
                        return self._cursor.fetchone()[0]
 
431
                
 
432
                self._cursor.connection.commit()
 
433
                
 
434
                return id
 
435
        
 
436
        def delete_events (self, ids):
 
437
                # Extract min and max timestamps for deleted events
 
438
                self._cursor.execute("""
 
439
                        SELECT MIN(timestamp), MAX(timestamp)
 
440
                        FROM event
 
441
                        WHERE id IN (%s)
 
442
                """ % ",".join(["?"] * len(ids)), ids)
 
443
                timestamps = self._cursor.fetchone()
 
444
                
 
445
                if timestamps:
 
446
                        # FIXME: Delete unused interpretation/manifestation/text/etc.
 
447
                        self._cursor.execute("DELETE FROM event WHERE id IN (%s)"
 
448
                                % ",".join(["?"] * len(ids)), ids)
 
449
                
 
450
                return timestamps