Merge lp:~kamstrup/zeitgeist/schema_versions into lp:zeitgeist/0.1

Proposed by Mikkel Kamstrup Erlandsen
Status: Merged
Merge reported by: Mikkel Kamstrup Erlandsen
Merged at revision: not available
Proposed branch: lp:~kamstrup/zeitgeist/schema_versions
Merge into: lp:zeitgeist/0.1
Diff against target: 280 lines (+231/-1)
3 files modified
_zeitgeist/engine/__init__.py (+4/-0)
_zeitgeist/engine/sql.py (+84/-1)
_zeitgeist/engine/upgrades/core_0_1.py (+143/-0)
To merge this branch: bzr merge lp:~kamstrup/zeitgeist/schema_versions
Reviewer Review Type Date Requested Status
Siegfried Gevatter diff review Approve
Review via email: mp+26231@code.launchpad.net

Description of the change

It's not very thoroughly tested yet, but upgrades <= 0.3.3 to 0.3.4 seem to work.

But what is this?
Versioning of the core DB schema (and also adds the possibility to version other schema if we ever have that).

On startup we check if the schema version for the 'core' schema is what we expect and if that is case we assume the schema is good and no further setup is needed.

If the schema version is not what we want we look for a module called _zeitgeist.engine.upgrades.core_$oldversion_$newversion and execute its run() method if it's there.
In our case we are talking upgrading from core schema 0 to 1, so that would be _zeitgeist.engine.upgrades.core_0_1.py.

Note that I did it this way in order to minimize the number of .py files we need to stat and/or parse at startup. If no upgrades are necessary, none of the upgrade .py files are parsed let alone read from disk.

To post a comment you must log in.
1470. By Mikkel Kamstrup Erlandsen <kamstrup@hardback>

Describe the fixmes is core_0_1.py upgrade

1471. By Mikkel Kamstrup Erlandsen <kamstrup@hardback>

Merge with trunk

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

Go, go, go!

review: Approve (diff review)
1472. By Mikkel Kamstrup Erlandsen <kamstrup@hardback>

Fix hard coded 'core' schema in a SQL statement for the upgrade logic

1473. By Mikkel Kamstrup Erlandsen <kamstrup@hardback>

exit if the upgrade fails... running on a undefined schema version is very dangerous

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '_zeitgeist/engine/__init__.py'
2--- _zeitgeist/engine/__init__.py 2010-05-25 20:14:11 +0000
3+++ _zeitgeist/engine/__init__.py 2010-05-27 20:20:41 +0000
4@@ -81,5 +81,9 @@
5
6 # Extensions
7 DEFAULT_EXTENSIONS = _get_extensions()
8+
9+ # Required version of DB schema
10+ CORE_SCHEMA="core"
11+ CORE_SCHEMA_VERSION = 1
12
13 constants = _Constants()
14
15=== modified file '_zeitgeist/engine/sql.py'
16--- _zeitgeist/engine/sql.py 2010-05-14 16:59:04 +0000
17+++ _zeitgeist/engine/sql.py 2010-05-27 20:20:41 +0000
18@@ -22,6 +22,7 @@
19
20 import sqlite3
21 import logging
22+import time
23
24 from _zeitgeist.engine import constants
25
26@@ -49,14 +50,89 @@
27 else:
28 return super(UnicodeCursor, self).execute(statement)
29
30+def _get_schema_version (cursor, schema_name):
31+ """
32+ Returns the schema version for schema_name or returns 0 in case
33+ the schema doesn't exist.
34+ """
35+ try:
36+ schema_version_result = cursor.execute("""
37+ SELECT version FROM schema_version WHERE schema=?
38+ """, (schema_name,))
39+ result = schema_version_result.fetchone()
40+ return result[0] if result else 0
41+ except sqlite3.OperationalError, e:
42+ # The schema isn't there...
43+ log.debug ("Schema '%s' not found: %s" % (schema_name, e))
44+ return 0
45+
46+def _set_schema_version (cursor, schema_name, version):
47+ """
48+ Sets the version of `schema_name` to `version`
49+ """
50+ cursor.execute("""
51+ CREATE TABLE IF NOT EXISTS schema_version
52+ (schema VARCHAR PRIMARY KEY ON CONFLICT REPLACE, version INT)
53+ """)
54+
55+ # The 'ON CONFLICT REPLACE' on the PK converts INSERT to UPDATE
56+ # when appriopriate
57+ cursor.execute("""
58+ INSERT INTO schema_version VALUES (?, ?)
59+ """, (schema_name, version))
60+ cursor.connection.commit()
61+
62+def _do_schema_upgrade (cursor, schema_name, old_version, new_version):
63+ """
64+ Try and upgrade schema `schema_name` from version `old_version` to
65+ `new_version`. This is done by checking for an upgrade module named
66+ '_zeitgeist.engine.upgrades.$schema_name_$old_version_$new_version'
67+ and executing the run(cursor) method of that module
68+ """
69+ # Fire of the right upgrade module
70+ log.info("Upgrading database '%s' from version %s to %s. This may take a while" %
71+ (schema_name, old_version, new_version))
72+ upgrader_name = "%s_%s_%s" % (schema_name, old_version, new_version)
73+ module = __import__ ("_zeitgeist.engine.upgrades.%s" % upgrader_name)
74+ eval("module.engine.upgrades.%s.run(cursor)" % upgrader_name)
75+
76+ # Update the schema version
77+ _set_schema_version(cursor, schema_name, new_version)
78+
79+ log.info("Upgrade succesful")
80+
81 def create_db(file_path):
82 """Create the database and return a default cursor for it"""
83-
84+ start = time.time()
85 log.info("Using database: %s" % file_path)
86 conn = sqlite3.connect(file_path)
87 conn.row_factory = sqlite3.Row
88 cursor = conn.cursor(UnicodeCursor)
89
90+ # See if we have the right schema version, and try an upgrade if needed
91+ core_schema_version = _get_schema_version(cursor, constants.CORE_SCHEMA)
92+ if core_schema_version is not None:
93+ if core_schema_version == constants.CORE_SCHEMA_VERSION:
94+ _time = (time.time() - start)*1000
95+ log.debug("Core schema is good. DB loaded in %sms" % _time)
96+ return cursor
97+ else:
98+ try:
99+ _do_schema_upgrade (cursor,
100+ constants.CORE_SCHEMA,
101+ core_schema_version,
102+ constants.CORE_SCHEMA_VERSION)
103+ # Don't return here. The upgrade process might depend on the
104+ # tables, indexes, and views being set up (to avoid code dup)
105+ log.info("Running post upgrade setup")
106+ except Exception, e:
107+ log.fatal("Failed to upgrade database '%s' from version %s to %s: %s" %
108+ (constants.CORE_SCHEMA, core_schema_version, constants.CORE_SCHEMA_VERSION, e))
109+ raise SystemExit(27)
110+ else:
111+ log.info("Setting up initial database")
112+
113+
114 # uri
115 cursor.execute("""
116 CREATE TABLE IF NOT EXISTS uri
117@@ -277,6 +353,13 @@
118 FROM event
119 """)
120
121+ # All good. Set the schema version, so we don't have to do all this
122+ # sql the next time around
123+ _set_schema_version (cursor, constants.CORE_SCHEMA, constants.CORE_SCHEMA_VERSION)
124+ _time = (time.time() - start)*1000
125+ log.info("DB set up in %sms" % _time)
126+ cursor.connection.commit()
127+
128 return cursor
129
130 _cursor = None
131
132=== added directory '_zeitgeist/engine/upgrades'
133=== added file '_zeitgeist/engine/upgrades/__init__.py'
134=== added file '_zeitgeist/engine/upgrades/core_0_1.py'
135--- _zeitgeist/engine/upgrades/core_0_1.py 1970-01-01 00:00:00 +0000
136+++ _zeitgeist/engine/upgrades/core_0_1.py 2010-05-27 20:20:41 +0000
137@@ -0,0 +1,143 @@
138+import os
139+import sys
140+
141+INTERPRETATION_RENAMES = \
142+[
143+ ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/#ManifestationCode",
144+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SourceCode"),
145+
146+ ("http://www.semanticdesktop.org/ontologies/nfo/#Bookmark",
147+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark"),
148+
149+ ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/#Document",
150+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Document"),
151+
152+ ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/#Image",
153+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image"),
154+
155+ ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/#Video",
156+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Video"),
157+
158+ ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/#Audio",
159+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio"),
160+
161+ ("http://www.semanticdesktop.org/ontologies/2007/03/22/nmo/#Email",
162+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#Email"),
163+
164+ ("http://www.semanticdesktop.org/ontologies/2007/03/22/nmo/#IMMessage",
165+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#IMMessage"),
166+
167+ ("http://zeitgeist-project.com/schema/1.0/core#CreateEvent",
168+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#CreateEvent"),
169+
170+ ("http://zeitgeist-project.com/schema/1.0/core#ModifyEvent",
171+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#ModifyEvent"),
172+
173+ ("http://zeitgeist-project.com/schema/1.0/core#VisitEvent",
174+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#AccessEvent"),
175+
176+ ("http://zeitgeist-project.com/schema/1.0/core#OpenEvent",
177+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#AccessEvent"),
178+
179+ ("http://zeitgeist-project.com/schema/1.0/core#SaveEvent",
180+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#ModifyEvent"),
181+
182+ ("http://zeitgeist-project.com/schema/1.0/core#CloseEvent",
183+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#LeaveEvent"),
184+
185+ ("http://zeitgeist-project.com/schema/1.0/core#SendEvent",
186+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#SendEvent"),
187+
188+ ("http://zeitgeist-project.com/schema/1.0/core#ReceiveEvent",
189+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#ReceiveEvent"),
190+]
191+
192+# The following interpretations does not have a good candidate for replacement
193+# in the Nepomuk ontology. Now with schema versions in place we can consider
194+# adding our own hacky URIs for these:
195+# FIXME: FEED_MESSAGE
196+# FIXME: BROADCAST_MESSAGE
197+# FIXME: http://freedesktop.org/standards/xesam/1.0/core#SystemRessource
198+# FIXME: Note - like from Tomboy and what have we
199+
200+# We should reevaluate the usefulness of the following event interpretations
201+# FIXME: FOCUS_EVENT - We don't have a concrete use case except so hand wavy ideas
202+# FIXME: WARN_EVENT - wtf?
203+# FIXME: ERROR_EVENT - wtf?
204+
205+MANIFESTATION_RENAMES = \
206+[
207+ ("http://zeitgeist-project.com/schema/1.0/core#UserActivity",
208+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#UserActivity"),
209+
210+ ("http://zeitgeist-project.com/schema/1.0/core#HeuristicActivity",
211+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#HeuristicActivity"),
212+
213+ ("http://zeitgeist-project.com/schema/1.0/core#ScheduledActivity",
214+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#ScheduledActivity"),
215+
216+ ("http://zeitgeist-project.com/schema/1.0/core#UserNotification",
217+ "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#WorldActivity"),
218+
219+ ("http://www.semanticdesktop.org/ontologies/nfo/#FileDataObject",
220+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#FileDataObject"),
221+]
222+
223+# These are left alone, but are listed here for completeness
224+INTERPRETATION_DELETIONS = \
225+[
226+ "http://www.semanticdesktop.org/ontologies/2007/01/19/nie/#comment",
227+ "http://zeitgeist-project.com/schema/1.0/core#UnknownInterpretation",
228+]
229+
230+# These are left alone, but are listed here for completeness
231+MANIFESTATION_DELETIONS = \
232+[
233+ "http://zeitgeist-project.com/schema/1.0/core#UnknownManifestation",
234+]
235+
236+#
237+# This module upgrades the 'core' schema from version 0 (or unversioned
238+# pre 0.3.3 DBs) to DB core schema version 1
239+#
240+def run(cursor):
241+ for r in INTERPRETATION_RENAMES:
242+ cursor.execute("""
243+ UPDATE interpretation SET value=? WHERE value=?
244+ """, r)
245+
246+ for r in MANIFESTATION_RENAMES:
247+ cursor.execute("""
248+ UPDATE manifestation SET value=? WHERE value=?
249+ """, r)
250+
251+ # START WEB HISTORY UPGRADE
252+ # The case of Manifestation.WEB_HISTORY it's a little more tricky.
253+ # We must set the subject interpretation to Interpretation.WEBSITE
254+ # and set the subject manifestation to Manifestation.REMOTE_DATA_OBJECT.
255+ #
256+ # We accomplish this by renaming nfo#WebHistory to nfo#RemoteDataObject
257+ # and after that set the interpretation of all events with manifestation
258+ # nfo#RemoteDataObjects to nfo#Website.
259+
260+ cursor.execute("""
261+ UPDATE manifestation SET value=? WHERE value=?
262+ """, ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#WebHistory",
263+ "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#RemoteDataObject"))
264+
265+ try:
266+ cursor.execute("""
267+ INSERT INTO interpretation (value) VALUES (?)
268+ """, ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Website",))
269+ except:
270+ # Unique key constraint violation - it's already there...
271+ pass
272+
273+ website_id = cursor.execute("SELECT id FROM interpretation WHERE value='http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Website'").fetchone()[0]
274+ remotes = cursor.execute("SELECT id FROM event WHERE subj_manifestation='http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#RemoteDataObject'").fetchall()
275+ for event_id in remotes:
276+ cursor.execute("""
277+ UPDATE event SET subj_interpretation=%s WHERE id=?
278+ """ % website_id, (event_id,))
279+ # END WEB HISTORY UPGRADE
280+