Merge lp:~stefanor/ibid/upgradeable-db-schema into lp:~ibid-core/ibid/old-trunk-pack-0.92

Proposed by Stefano Rivera
Status: Merged
Approved by: Stefano Rivera
Approved revision: 601
Merged at revision: 594
Proposed branch: lp:~stefanor/ibid/upgradeable-db-schema
Merge into: lp:~ibid-core/ibid/old-trunk-pack-0.92
Diff against target: None lines
To merge this branch: bzr merge lp:~stefanor/ibid/upgradeable-db-schema
Reviewer Review Type Date Requested Status
Jonathan Hitchcock Approve
Michael Gorven Approve
Review via email: mp+5493@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Stefano Rivera (stefanor) wrote :

OK, I (finally) have something workable. To see it in action, take a look at lp:~stefanor/ibid/schema-strings

Known issues:
* Plugins with outdated schemas are simply not loaded. At startup time, this should probably abort startup
* Table upgrade is done with ibid-setup (hit ^C when it prompts you for the initial account). ibid-setup should probably output the upgrade progress (hint: logging) and not prompt for initial accounts, if it detects an upgrade.

Unknown issues:
* please provide :)

Revision history for this message
Stefano Rivera (stefanor) wrote :

To enable schema upgrades on an existing database, you'll have to do something like:

CREATE TABLE schema (
  id INTEGER NOT NULL,
  "table" VARCHAR(32) NOT NULL,
  version INTEGER NOT NULL,
  PRIMARY KEY (id),
  UNIQUE ("table")
);
INSERT INTO schema (id, "table", version) VALUES (1, "account_attributes", 1);
INSERT INTO schema (id, "table", version) VALUES (2, "accounts", 1);
INSERT INTO schema (id, "table", version) VALUES (3, "credentials", 1);
INSERT INTO schema (id, "table", version) VALUES (4, "factoid_names", 1);
INSERT INTO schema (id, "table", version) VALUES (5, "factoid_values", 1);
INSERT INTO schema (id, "table", version) VALUES (6, "factoids", 1);
INSERT INTO schema (id, "table", version) VALUES (7, "feeds", 1);
INSERT INTO schema (id, "table", version) VALUES (8, "identities", 1);
INSERT INTO schema (id, "table", version) VALUES (9, "karma", 1);
INSERT INTO schema (id, "table", version) VALUES (10, "memos", 1);
INSERT INTO schema (id, "table", version) VALUES (11, "permissions", 1);
INSERT INTO schema (id, "table", version) VALUES (12, "seen", 1);
INSERT INTO schema (id, "table", version) VALUES (13, "schema", 1);
INSERT INTO schema (id, "table", version) VALUES (14, "urls", 1);

Also, Known issue to examine tomorrow: Doesn't work in jaunty:
Traceback (most recent call last):
  File "scripts/ibid-setup", line 67, in <module>
    upgrade_schemas(Session)
  File "/home/stefanor/projects/ibid/tibid/ibid/models.py", line 374, in upgrade_schemas
    table.versioned_schema.upgrade_schema(sessionmaker)
  File "/home/stefanor/projects/ibid/tibid/ibid/models.py", line 87, in upgrade_schema
    eval('self.upgrade_%i_to_%i' % (version - 1, version))()
  File "/home/stefanor/projects/ibid/tibid/ibid/plugins/karma.py", line 27, in upgrade_1_to_2
    self.alter_column(Column('subject', Unicode(64), unique=True, nullable=False), length_only=True)
  File "/home/stefanor/projects/ibid/tibid/ibid/models.py", line 161, in alter_column
    if session.bind.dialect.name == 'sqlite':
AttributeError: 'SQLiteDialect' object has no attribute 'name'

599. By Stefano Rivera

Support dialect detection in SQLAlchemy 0.4

Revision history for this message
Stefano Rivera (stefanor) wrote :

> Also, Known issue to examine tomorrow: Doesn't work in jaunty:

Fixed in r599

600. By Stefano Rivera

Use getattr() over eval()

Revision history for this message
Stefano Rivera (stefanor) wrote :

Another known issue: There are no helpers for dealing with indexes yet.

Revision history for this message
Michael Gorven (mgorven) wrote :

I don't like that the MySQL engine is forced to InnoDB, but this otherwise
looks good.
 review approve

review: Approve
601. By Stefano Rivera

Move InnoDB preference into MySQLModeListener, use DatabaseManager in ibid-setup

Revision history for this message
Stefano Rivera (stefanor) wrote :

> I don't like that the MySQL engine is forced to InnoDB

That's configurable (and implemented better) in r601

Revision history for this message
Jonathan Hitchcock (vhata) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ibid/__init__.py'
2--- ibid/__init__.py 2009-03-16 20:55:20 +0000
3+++ ibid/__init__.py 2009-04-13 17:36:47 +0000
4@@ -93,7 +93,4 @@
5 class SourceException(IbidException):
6 pass
7
8-class ConfigException(Exception):
9- pass
10-
11 # vi: set et sta sw=4 ts=4:
12
13=== modified file 'ibid/core.py'
14--- ibid/core.py 2009-03-24 10:42:41 +0000
15+++ ibid/core.py 2009-04-13 19:09:37 +0000
16@@ -172,6 +172,8 @@
17 ibid.processors.append(klass(name))
18 else:
19 self.log.debug("Skipping Processor: %s.%s", name, klass.__name__)
20+
21+ ibid.models.check_schema_versions(ibid.databases['ibid'])
22
23 except Exception, e:
24 self.log.exception(u"Couldn't instantiate %s processor of %s plugin", classname, name)
25@@ -235,13 +237,28 @@
26 for database in ibid.config.databases.keys():
27 self.load(database)
28
29+ ibid.models.check_schema_versions(self['ibid'])
30+
31 def load(self, name):
32 uri = ibid.config.databases[name]
33 if uri.startswith('sqlite:///'):
34- engine = create_engine('sqlite:///', creator=sqlite_creator(join(ibid.options['base'], expanduser(uri.replace('sqlite:///', '', 1)))), encoding='utf-8', convert_unicode=True, assert_unicode=True, echo=False)
35+ engine = create_engine('sqlite:///',
36+ creator=sqlite_creator(join(ibid.options['base'], expanduser(uri.replace('sqlite:///', '', 1)))),
37+ encoding='utf-8', convert_unicode=True, assert_unicode=True, echo=False)
38+
39 else:
40- engine = create_engine(uri, encoding='utf-8', convert_unicode=True, assert_unicode=True)
41+ engine = create_engine(uri, encoding='utf-8', convert_unicode=True, assert_unicode=True, echo=False)
42+
43+ if uri.startswith('mysql://'):
44+ class MySQLModeListener(object):
45+ def connect(self, dbapi_con, con_record):
46+ dbapi_con.set_sql_mode("ANSI")
47+ engine.pool.add_listener(MySQLModeListener())
48+
49+ engine.dialect.use_ansiquotes = True
50+
51 self[name] = scoped_session(sessionmaker(bind=engine, transactional=False, autoflush=True))
52+
53 self.log.info(u"Loaded %s database", name)
54
55 def __getattr__(self, name):
56
57=== modified file 'ibid/models.py'
58--- ibid/models.py 2009-02-18 19:05:10 +0000
59+++ ibid/models.py 2009-04-13 19:07:30 +0000
60@@ -1,20 +1,254 @@
61-from sqlalchemy import Column, Integer, Unicode, DateTime, ForeignKey, UniqueConstraint, MetaData, Table
62+import logging
63+
64+from sqlalchemy import Column, Integer, Unicode, DateTime, ForeignKey, UniqueConstraint, MetaData, Table, PassiveDefault, __version__
65 from sqlalchemy.orm import relation
66 from sqlalchemy.ext.declarative import declarative_base
67 from sqlalchemy.sql import func
68+from sqlalchemy.sql.expression import text
69+from sqlalchemy.exceptions import OperationalError, InvalidRequestError
70+
71+if __version__ < '0.5':
72+ NoResultFound = InvalidRequestError
73+else:
74+ from sqlalchemy.orm.exc import NoResultFound
75
76 metadata = MetaData()
77 Base = declarative_base(metadata=metadata)
78+log = logging.getLogger('ibid.models')
79+
80+class VersionedSchema(object):
81+ """For an initial table schema, set
82+ table.versioned_schema = VersionedSchema(__table__, 1)
83+ Table creation (upgrading to version 1) is implicitly supported.
84+
85+ When you have upgrades to the schema, instead of using VersionedSchema
86+ directly, derive from it and include your own upgrade_x_to_y(self) methods,
87+ where y = x + 1
88+
89+ In the upgrade methods, you can call the helper functions:
90+ add_column, drop_column, rename_column, alter_column
91+ They try to do the correct thing in most situations, including rebuilding
92+ tables in SQLite, which doesn't actually support dropping/altering columns.
93+ For column parameters, while you can point to columns in the table
94+ definition, it is better style to repeat the Column() specification as the
95+ column might be altered in a future version.
96+ """
97+
98+ def __init__(self, table, version):
99+ self.table = table
100+ self.version = version
101+
102+ def is_up_to_date(self, session):
103+ "Is the table in the database up to date with the schema?"
104+
105+ if not session.bind.has_table(self.table.name):
106+ return False
107+
108+ try:
109+ schema = session.query(Schema).filter(Schema.table==unicode(self.table.name)).one()
110+ return schema.version == self.version
111+ except NoResultFound:
112+ return False
113+
114+ def upgrade_schema(self, sessionmaker):
115+ "Upgrade the table's schema to the latest version."
116+
117+ for fk in self.table.foreign_keys:
118+ dependancy = fk.target_fullname.split('.')[0]
119+ log.debug("Upgrading table %s before %s", dependancy, self.table.name)
120+ metadata.tables[dependancy].versioned_schema.upgrade_schema(sessionmaker)
121+
122+ self.upgrade_session = session = sessionmaker()
123+ trans = session.begin()
124+
125+ schema = session.query(Schema).filter(Schema.table==unicode(self.table.name)).first()
126+
127+ try:
128+ if not schema:
129+ log.info(u"Creating table %s", self.table.name)
130+
131+ # If MySQL, we prefer InnoDB:
132+ if 'mysql_engine' not in self.table.kwargs:
133+ self.table.kwargs['mysql_engine'] = 'InnoDB'
134+
135+ self.table.create(bind=session.bind)
136+
137+ schema = Schema(unicode(self.table.name), self.version)
138+ session.save_or_update(schema)
139+
140+ elif self.version > schema.version:
141+ self.upgrade_reflected_model = MetaData(session.bind, reflect=True)
142+ for version in range(schema.version + 1, self.version + 1):
143+ log.info(u"Upgrading table %s to version %i", self.table.name, version)
144+
145+ trans.commit()
146+ trans = session.begin()
147+
148+ eval('self.upgrade_%i_to_%i' % (version - 1, version))()
149+
150+ schema.version = version
151+ session.save_or_update(schema)
152+ del self.upgrade_reflected_model
153+
154+ trans.commit()
155+
156+ except:
157+ trans.rollback()
158+ raise
159+
160+ session.close()
161+ del self.upgrade_session
162+
163+ def get_reflected_model(self):
164+ "Get a reflected table from the current DB's schema"
165+
166+ return self.upgrade_reflected_model.tables.get(self.table.name, None)
167+
168+ def add_column(self, col):
169+ "Add column col to table"
170+
171+ session = self.upgrade_session
172+ table = self.get_reflected_model()
173+
174+ log.debug(u"Adding column %s to table %s", col.name, table.name)
175+
176+ table.append_column(col)
177+
178+ sg = session.bind.dialect.schemagenerator(session.bind.dialect, session.bind)
179+ description = sg.get_column_specification(col)
180+
181+ session.execute('ALTER TABLE "%s" ADD COLUMN %s;' % (table.name, description))
182+
183+ def drop_column(self, col_name):
184+ "Drop column col_name from table"
185+
186+ session = self.upgrade_session
187+
188+ log.debug(u"Dropping column %s from table %s", col_name, self.table.name)
189+
190+ if session.bind.dialect.name == 'sqlite':
191+ self.rebuild_sqlite({col_name: None})
192+ else:
193+ session.execute('ALTER TABLE "%s" DROP COLUMN "%s";' % (self.table.name, col_name))
194+
195+ def rename_column(self, col, old_name):
196+ "Rename column from old_name to Column col"
197+
198+ session = self.upgrade_session
199+ table = self.get_reflected_model()
200+
201+ log.debug(u"Rename column %s to %s in table %s", old_name, col.name, table.name)
202+
203+ if session.bind.dialect.name == 'sqlite':
204+ self.rebuild_sqlite({old_name: col})
205+ elif session.bind.dialect.name == 'mysql':
206+ self.alter_column(col, old_name)
207+ else:
208+ session.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s";' % (table.name, old_name, col.name))
209+
210+ def alter_column(self, col, old_name=None, length_only=False):
211+ """Change a column (possibly renaming from old_name) to Column col.
212+ Specify length_only if the change is simply a change of data-type length."""
213+
214+ session = self.upgrade_session
215+ table = self.get_reflected_model()
216+
217+ log.debug(u"Altering column %s in table %s", col.name, table.name)
218+
219+ sg = session.bind.dialect.schemagenerator(session.bind.dialect, session.bind)
220+ description = sg.get_column_specification(col)
221+
222+ if session.bind.dialect.name == 'sqlite':
223+ #TODO: Automatically detect length_only
224+ if length_only:
225+ # SQLite doesn't enforce value length restrictions, only type changes have a real effect
226+ return
227+
228+ self.rebuild_sqlite({old_name is None and col.name or old_name: col})
229+
230+ elif session.bind.dialect.name == 'mysql':
231+ session.execute('ALTER TABLE "%s" CHANGE "%s" %s;'
232+ % (table.name, old_name is not None and old_name or col.name, description))
233+
234+ else:
235+ if old_name is not None:
236+ self.rename_column(col, old_name)
237+ session.execute('ALTER TABLE "%s" ALTER COLUMN "%s" TYPE %s'
238+ % (table.name, col.name, description.split(" ", 1)[1]))
239+
240+ def rebuild_sqlite(self, colmap):
241+ """SQLite doesn't support modification of table schema - must rebuild the table.
242+ colmap maps old column names to new Columns (or None for column deletion).
243+ Only modified columns need to be listed, unchaged columns are carried over automatically.
244+ Specify table in case name has changed in a more recent version."""
245+
246+ session = self.upgrade_session
247+ table = self.get_reflected_model()
248+
249+ log.debug(u"Rebuilding SQLite table %s", table.name)
250+
251+ fullcolmap = {}
252+ for col in table.c:
253+ if col.name in colmap:
254+ if colmap[col.name] is not None:
255+ fullcolmap[col.name] = colmap[col.name].name
256+ else:
257+ fullcolmap[col.name] = col.name
258+
259+ for old, col in colmap.iteritems():
260+ del table.c[old]
261+ if col is not None:
262+ table.append_column(col)
263+
264+ session.execute('ALTER TABLE "%s" RENAME TO "%s_old";' % (table.name, table.name))
265+ table.create()
266+ session.execute('INSERT INTO "%s" ("%s") SELECT "%s" FROM "%s_old";'
267+ % (table.name, '", "'.join(fullcolmap.values()), '", "'.join(fullcolmap.keys()), table.name))
268+ session.execute('DROP TABLE "%s_old";' % table.name)
269+
270+class Schema(Base):
271+ __table__ = Table('schema', Base.metadata,
272+ Column('id', Integer, primary_key=True),
273+ Column('table', Unicode(32), unique=True, nullable=False),
274+ Column('version', Integer, nullable=False),
275+ useexisting=True)
276+
277+ # Upgrades to this table are probably going to be tricky
278+ class SchemaSchema(VersionedSchema):
279+ def upgrade_schema(self, sessionmaker):
280+ session = sessionmaker()
281+
282+ if not session.bind.has_table(self.table.name):
283+ metadata.bind = session.bind
284+ self.table.kwargs['mysql_engine'] = 'InnoDB'
285+ self.table.create()
286+
287+ schema = Schema(unicode(self.table.name), self.version)
288+ session.save_or_update(schema)
289+
290+ session.flush()
291+ session.close()
292+
293+ __table__.versioned_schema = SchemaSchema(__table__, 1)
294+
295+ def __init__(self, table, version=0):
296+ self.table = table
297+ self.version = version
298+
299+ def __repr__(self):
300+ return '<Schema %s>' % self.table
301
302 class Identity(Base):
303 __table__ = Table('identities', Base.metadata,
304- Column('id', Integer, primary_key=True),
305- Column('account_id', Integer, ForeignKey('accounts.id')),
306- Column('source', Unicode(16), nullable=False),
307- Column('identity', Unicode(64), nullable=False),
308- Column('created', DateTime, default=func.current_timestamp()),
309- UniqueConstraint('source', 'identity'),
310- useexisting=True)
311+ Column('id', Integer, primary_key=True),
312+ Column('account_id', Integer, ForeignKey('accounts.id')),
313+ Column('source', Unicode(16), nullable=False),
314+ Column('identity', Unicode(64), nullable=False),
315+ Column('created', DateTime, default=func.current_timestamp()),
316+ UniqueConstraint('source', 'identity'),
317+ useexisting=True)
318+
319+ __table__.versioned_schema = VersionedSchema(__table__, 1)
320
321 def __init__(self, source, identity, account_id=None):
322 self.source = source
323@@ -26,12 +260,14 @@
324
325 class Attribute(Base):
326 __table__ = Table('account_attributes', Base.metadata,
327- Column('id', Integer, primary_key=True),
328- Column('account_id', Integer, ForeignKey('accounts.id'), nullable=False),
329- Column('name', Unicode(32), nullable=False),
330- Column('value', Unicode(128), nullable=False),
331- UniqueConstraint('account_id', 'name'),
332- useexisting=True)
333+ Column('id', Integer, primary_key=True),
334+ Column('account_id', Integer, ForeignKey('accounts.id'), nullable=False),
335+ Column('name', Unicode(32), nullable=False),
336+ Column('value', Unicode(128), nullable=False),
337+ UniqueConstraint('account_id', 'name'),
338+ useexisting=True)
339+
340+ __table__.versioned_schema = VersionedSchema(__table__, 1)
341
342 def __init__(self, name, value):
343 self.name = name
344@@ -42,12 +278,14 @@
345
346 class Credential(Base):
347 __table__ = Table('credentials', Base.metadata,
348- Column('id', Integer, primary_key=True),
349- Column('account_id', Integer, ForeignKey('accounts.id'), nullable=False),
350- Column('source', Unicode(16)),
351- Column('method', Unicode(16), nullable=False),
352- Column('credential', Unicode(256), nullable=False),
353- useexisting=True)
354+ Column('id', Integer, primary_key=True),
355+ Column('account_id', Integer, ForeignKey('accounts.id'), nullable=False),
356+ Column('source', Unicode(16)),
357+ Column('method', Unicode(16), nullable=False),
358+ Column('credential', Unicode(256), nullable=False),
359+ useexisting=True)
360+
361+ __table__.versioned_schema = VersionedSchema(__table__, 1)
362
363 def __init__(self, method, credential, source=None, account_id=None):
364 self.account_id = account_id
365@@ -57,13 +295,14 @@
366
367 class Permission(Base):
368 __table__ = Table('permissions', Base.metadata,
369- Column('id', Integer, primary_key=True),
370- Column('account_id', Integer, ForeignKey('accounts.id'), nullable=False),
371- Column('name', Unicode(16), nullable=False),
372- Column('value', Unicode(4), nullable=False),
373- UniqueConstraint('account_id', 'name'),
374- useexisting=True)
375+ Column('id', Integer, primary_key=True),
376+ Column('account_id', Integer, ForeignKey('accounts.id'), nullable=False),
377+ Column('name', Unicode(16), nullable=False),
378+ Column('value', Unicode(4), nullable=False),
379+ UniqueConstraint('account_id', 'name'),
380+ useexisting=True)
381
382+ __table__.versioned_schema = VersionedSchema(__table__, 1)
383
384 def __init__(self, name=None, value=None, account_id=None):
385 self.account_id = account_id
386@@ -72,9 +311,11 @@
387
388 class Account(Base):
389 __table__ = Table('accounts', Base.metadata,
390- Column('id', Integer, primary_key=True),
391- Column('username', Unicode(32), unique=True, nullable=False),
392- useexisting=True)
393+ Column('id', Integer, primary_key=True),
394+ Column('username', Unicode(32), unique=True, nullable=False),
395+ useexisting=True)
396+
397+ __table__.versioned_schema = VersionedSchema(__table__, 1)
398
399 identities = relation(Identity, backref='account')
400 attributes = relation(Attribute)
401@@ -87,4 +328,36 @@
402 def __repr__(self):
403 return '<Account %s>' % self.username
404
405+def check_schema_versions(sessionmaker):
406+ """Pass through all tables, log out of date ones,
407+ and except if not all up to date"""
408+
409+ session = sessionmaker()
410+ upgrades = []
411+ for table in metadata.tables.itervalues():
412+ if not hasattr(table, 'versioned_schema'):
413+ log.error("Table %s is not versioned.", table.name)
414+ continue
415+
416+ if not table.versioned_schema.is_up_to_date(session):
417+ upgrades.append(table.name)
418+
419+ if not upgrades:
420+ return
421+
422+ raise Exception(u"Tables %s are out of date. Run ibid-setup" % u", ".join(upgrades))
423+
424+def upgrade_schemas(sessionmaker):
425+ "Pass through all tables and update schemas"
426+
427+ # Make sure schema table is created first
428+ metadata.tables['schema'].versioned_schema.upgrade_schema(sessionmaker)
429+
430+ for table in metadata.tables.itervalues():
431+ if not hasattr(table, 'versioned_schema'):
432+ log.error("Table %s is not versioned.", table.name)
433+ continue
434+
435+ table.versioned_schema.upgrade_schema(sessionmaker)
436+
437 # vi: set et sta sw=4 ts=4:
438
439=== modified file 'ibid/plugins/factoid.py'
440--- ibid/plugins/factoid.py 2009-03-17 14:40:03 +0000
441+++ ibid/plugins/factoid.py 2009-04-13 19:07:30 +0000
442@@ -10,7 +10,7 @@
443 from ibid.plugins import Processor, match, handler, authorise, auth_responses, RPC
444 from ibid.config import Option, IntOption
445 from ibid.plugins.identity import get_identities
446-from ibid.models import Base
447+from ibid.models import Base, VersionedSchema
448
449 help = {'factoids': u'Factoids are arbitrary pieces of information stored by a key. '
450 u'Factoids beginning with a command such as "<action>" or "<reply>" will supress the "name verb value" output. '
451@@ -27,6 +27,8 @@
452 Column('time', DateTime, nullable=False, default=func.current_timestamp()),
453 useexisting=True)
454
455+ __table__.versioned_schema = VersionedSchema(__table__, 1)
456+
457 def __init__(self, name, identity_id, factoid_id=None):
458 self.name = name
459 self.factoid_id = factoid_id
460@@ -44,6 +46,8 @@
461 Column('time', DateTime, nullable=False, default=func.current_timestamp()),
462 useexisting=True)
463
464+ __table__.versioned_schema = VersionedSchema(__table__, 1)
465+
466 def __init__(self, value, identity_id, factoid_id=None):
467 self.value = value
468 self.factoid_id = factoid_id
469@@ -58,6 +62,8 @@
470 Column('time', DateTime, nullable=False, default=func.current_timestamp()),
471 useexisting=True)
472
473+ __table__.versioned_schema = VersionedSchema(__table__, 1)
474+
475 names = relation(FactoidName, cascade='all,delete', backref='factoid')
476 values = relation(FactoidValue, cascade='all,delete', backref='factoid')
477
478
479=== modified file 'ibid/plugins/feeds.py'
480--- ibid/plugins/feeds.py 2009-03-11 11:11:04 +0000
481+++ ibid/plugins/feeds.py 2009-04-13 19:07:30 +0000
482@@ -12,7 +12,7 @@
483
484 import ibid
485 from ibid.plugins import Processor, match, authorise
486-from ibid.models import Base
487+from ibid.models import Base, VersionedSchema
488 from ibid.utils import cacheable_download, get_html_parse_tree
489
490 help = {'feeds': u'Displays articles from RSS and Atom feeds'}
491@@ -27,6 +27,8 @@
492 Column('identity_id', Integer, ForeignKey('identities.id'), nullable=False),
493 Column('time', DateTime, nullable=False),
494 useexisting=True)
495+
496+ __table__.versioned_schema = VersionedSchema(__table__, 1)
497
498 feed = None
499 entries = None
500
501=== modified file 'ibid/plugins/karma.py'
502--- ibid/plugins/karma.py 2009-03-16 10:43:01 +0000
503+++ ibid/plugins/karma.py 2009-04-13 19:07:30 +0000
504@@ -7,7 +7,7 @@
505 import ibid
506 from ibid.plugins import Processor, match, handler, authorise
507 from ibid.config import Option, BoolOption, IntOption
508-from ibid.models import Base
509+from ibid.models import Base, VersionedSchema
510
511 help = {'karma': u'Keeps track of karma for people and things.'}
512
513@@ -22,6 +22,8 @@
514 Column('time', DateTime, nullable=False, default=func.current_timestamp()),
515 useexisting=True)
516
517+ __table__.versioned_schema = VersionedSchema(__table__, 1)
518+
519 def __init__(self, subject):
520 self.subject = subject
521 self.changes = 0
522
523=== modified file 'ibid/plugins/memo.py'
524--- ibid/plugins/memo.py 2009-03-18 11:36:41 +0000
525+++ ibid/plugins/memo.py 2009-04-13 19:07:30 +0000
526@@ -10,7 +10,7 @@
527 from ibid.config import Option
528 from ibid.plugins.auth import permission
529 from ibid.plugins.identity import get_identities
530-from ibid.models import Base, Identity, Account
531+from ibid.models import Base, VersionedSchema, Identity, Account
532 from ibid.utils import ago
533
534 help = {'memo': u'Keeps messages for people.'}
535@@ -29,6 +29,8 @@
536 Column('time', DateTime, nullable=False, default=func.current_timestamp()),
537 useexisting=True)
538
539+ __table__.versioned_schema = VersionedSchema(__table__, 1)
540+
541 def __init__(self, from_id, to_id, memo, private=False):
542 self.from_id = from_id
543 self.to_id = to_id
544
545=== modified file 'ibid/plugins/seen.py'
546--- ibid/plugins/seen.py 2009-03-08 13:16:28 +0000
547+++ ibid/plugins/seen.py 2009-04-13 19:07:30 +0000
548@@ -7,7 +7,7 @@
549 import ibid
550 from ibid.plugins import Processor, match
551 from ibid.config import Option
552-from ibid.models import Base, Identity, Account
553+from ibid.models import Base, VersionedSchema, Identity, Account,
554 from ibid.utils import ago
555
556 help = {'seen': u'Records when people were last seen.'}
557@@ -24,6 +24,8 @@
558 UniqueConstraint('identity_id', 'type'),
559 useexisting=True)
560
561+ __table__.versioned_schema = VersionedSchema(__table__, 1)
562+
563 identity = relation('Identity')
564
565 def __init__(self, identity_id=None, type='message', channel=None, value=None):
566
567=== modified file 'ibid/plugins/url.py'
568--- ibid/plugins/url.py 2009-03-25 14:58:06 +0000
569+++ ibid/plugins/url.py 2009-04-13 19:07:30 +0000
570@@ -9,7 +9,7 @@
571 import ibid
572 from ibid.plugins import Processor, match, handler
573 from ibid.config import Option
574-from ibid.models import Base
575+from ibid.models import Base, VersionedSchema
576 from ibid.utils import get_html_parse_tree
577
578 help = {'url': u'Captures URLs seen in channel to database and/or to delicious, and shortens and lengthens URLs'}
579@@ -25,6 +25,8 @@
580 Column('time', DateTime, nullable=False),
581 useexisting=True)
582
583+ __table__.versioned_schema = VersionedSchema(__table__, 1)
584+
585 def __init__(self, url, channel, identity_id):
586 self.url = url
587 self.channel = channel
588
589=== modified file 'scripts/ibid-setup'
590--- scripts/ibid-setup 2009-03-16 16:52:51 +0000
591+++ scripts/ibid-setup 2009-04-13 17:36:47 +0000
592@@ -13,7 +13,7 @@
593
594 from ibid.plugins.auth import hash
595 from ibid.config import FileConfig
596-from ibid.models import Account, Identity, Permission, Credential, metadata
597+from ibid.models import Account, Identity, Permission, Credential, metadata, upgrade_schemas
598
599 for module in getModule('ibid.plugins').iterModules():
600 try:
601@@ -63,7 +63,8 @@
602 copyfileobj(resource_stream('ibid', 'logging.ini'), open('logging.ini', 'w'))
603
604 engine = create_engine(config.databases['ibid'], encoding='utf-8', convert_unicode=True, assert_unicode=True)
605-metadata.create_all(engine)
606+Session = sessionmaker(bind=engine, transactional=False)
607+upgrade_schemas(Session)
608
609 print u'Database tables created'
610
611@@ -77,8 +78,8 @@
612 print 'Password do not match'
613 exit(1)
614
615-Session = sessionmaker(bind=engine)
616 session = Session()
617+session.begin()
618 account = Account(identity)
619 identity = Identity(source, identity)
620 account.identities.append(identity)

Subscribers

People subscribed via source and target branches