| 1 | #!/usr/bin/env python |
|---|
| 2 | # -*- coding: utf-8 -*- |
|---|
| 3 | # |
|---|
| 4 | # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> |
|---|
| 5 | # |
|---|
| 6 | # This software is licensed as described in the file COPYING, which |
|---|
| 7 | # you should have received as part of this distribution. |
|---|
| 8 | |
|---|
| 9 | from __future__ import with_statement |
|---|
| 10 | |
|---|
| 11 | from trac.core import Component, TracError, implements |
|---|
| 12 | from trac.db.api import DatabaseManager |
|---|
| 13 | from trac.env import IEnvironmentSetupParticipant |
|---|
| 14 | |
|---|
| 15 | from tractags import db_default |
|---|
| 16 | from tractags.api import _ |
|---|
| 17 | from tractags.ticket import TicketTagProvider |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | class TagSetup(Component): |
|---|
| 21 | """[main] Plugin setup and upgrade handler.""" |
|---|
| 22 | |
|---|
| 23 | implements(IEnvironmentSetupParticipant) |
|---|
| 24 | |
|---|
| 25 | # IEnvironmentSetupParticipant methods |
|---|
| 26 | |
|---|
| 27 | def environment_created(self): |
|---|
| 28 | pass |
|---|
| 29 | |
|---|
| 30 | def environment_needs_upgrade(self, db=None): |
|---|
| 31 | schema_ver = self.get_schema_version() |
|---|
| 32 | if schema_ver == db_default.schema_version: |
|---|
| 33 | return False |
|---|
| 34 | elif schema_ver > db_default.schema_version: |
|---|
| 35 | raise TracError(_("A newer plugin version has been installed " |
|---|
| 36 | "before, but downgrading is unsupported.")) |
|---|
| 37 | self.log.info("TracTags database schema version is %d, should be %d", |
|---|
| 38 | schema_ver, db_default.schema_version) |
|---|
| 39 | return True |
|---|
| 40 | |
|---|
| 41 | def upgrade_environment(self, db=None): |
|---|
| 42 | """Each schema version should have its own upgrade module, named |
|---|
| 43 | upgrades/dbN.py, where 'N' is the version number (int). |
|---|
| 44 | """ |
|---|
| 45 | db_mgr = DatabaseManager(self.env) |
|---|
| 46 | schema_ver = self.get_schema_version() |
|---|
| 47 | |
|---|
| 48 | with self.env.db_transaction as db: |
|---|
| 49 | # Is this a new installation? |
|---|
| 50 | if not schema_ver: |
|---|
| 51 | # Perform a single-step install: Create plugin schema and |
|---|
| 52 | # insert default data into the database. |
|---|
| 53 | connector = db_mgr._get_connector()[0] |
|---|
| 54 | for table in db_default.schema: |
|---|
| 55 | for stmt in connector.to_sql(table): |
|---|
| 56 | db(stmt) |
|---|
| 57 | for table, cols, vals in db_default.get_data(db): |
|---|
| 58 | db.executemany("INSERT INTO %s (%s) VALUES (%s)" |
|---|
| 59 | % (table, ','.join(cols), |
|---|
| 60 | ','.join(['%s'] * len(cols))), vals) |
|---|
| 61 | else: |
|---|
| 62 | # Perform incremental upgrades. |
|---|
| 63 | for i in range(schema_ver + 1, db_default.schema_version + 1): |
|---|
| 64 | name = 'db%i' % i |
|---|
| 65 | try: |
|---|
| 66 | upgrades = __import__('tractags.upgrades', globals(), |
|---|
| 67 | locals(), [name]) |
|---|
| 68 | script = getattr(upgrades, name) |
|---|
| 69 | except AttributeError: |
|---|
| 70 | raise TracError(_("No upgrade module for version " |
|---|
| 71 | "%(num)i (%(version)s.py)", num=i, |
|---|
| 72 | version=name)) |
|---|
| 73 | cursor = db.cursor() |
|---|
| 74 | script.do_upgrade(self.env, i, cursor) |
|---|
| 75 | db("""UPDATE system SET value=%s |
|---|
| 76 | WHERE name='tags_version' |
|---|
| 77 | """, (db_default.schema_version,)) |
|---|
| 78 | self.log.info("Upgraded TracTags db schema from version %d to %d", |
|---|
| 79 | schema_ver, db_default.schema_version) |
|---|
| 80 | |
|---|
| 81 | TicketTagProvider(self.env)._fetch_tkt_tags() |
|---|
| 82 | self.log.info("Synchronized ticket attributes to tags table") |
|---|
| 83 | |
|---|
| 84 | def get_db_version(self): |
|---|
| 85 | for version, in self.env.db_query(""" |
|---|
| 86 | SELECT value FROM system |
|---|
| 87 | WHERE name='tags_version' |
|---|
| 88 | """): |
|---|
| 89 | return int(version) |
|---|
| 90 | |
|---|
| 91 | def get_schema_version(self): |
|---|
| 92 | """Return the current schema version for this plugin.""" |
|---|
| 93 | version = self.get_db_version() |
|---|
| 94 | if not version or version < 2: |
|---|
| 95 | # Care for pre-tags-0.7 installations. |
|---|
| 96 | tables = self._get_tables() |
|---|
| 97 | if 'tags' in tables: |
|---|
| 98 | self.env.log.debug("TracTags needs to register schema version") |
|---|
| 99 | return 2 |
|---|
| 100 | if 'wiki_namespace' in tables: |
|---|
| 101 | self.env.log.debug("TracTags needs to migrate old data") |
|---|
| 102 | return 1 |
|---|
| 103 | # This is a new installation. |
|---|
| 104 | return 0 |
|---|
| 105 | # The expected outcome for any up-to-date installation. |
|---|
| 106 | return version or 0 |
|---|
| 107 | |
|---|
| 108 | # Internal methods |
|---|
| 109 | |
|---|
| 110 | def _get_tables(self): |
|---|
| 111 | """Code from TracMigratePlugin by Jun Omae (see tracmigrate.admin).""" |
|---|
| 112 | dburi = self.config.get('trac', 'database') |
|---|
| 113 | if dburi.startswith('sqlite:'): |
|---|
| 114 | sql = """ |
|---|
| 115 | SELECT name FROM sqlite_master |
|---|
| 116 | WHERE type='table' AND NOT name='sqlite_sequence' |
|---|
| 117 | """ |
|---|
| 118 | elif dburi.startswith('postgres:'): |
|---|
| 119 | sql = """ |
|---|
| 120 | SELECT tablename FROM pg_tables |
|---|
| 121 | WHERE schemaname = ANY (current_schemas(false)) |
|---|
| 122 | """ |
|---|
| 123 | elif dburi.startswith('mysql:'): |
|---|
| 124 | sql = "SHOW TABLES" |
|---|
| 125 | else: |
|---|
| 126 | raise TracError('Unsupported database type "%s"' |
|---|
| 127 | % dburi.split(':')[0]) |
|---|
| 128 | with self.env.db_transaction as db: |
|---|
| 129 | cursor = db.cursor() |
|---|
| 130 | cursor.execute(sql) |
|---|
| 131 | return sorted(name for name, in cursor.fetchall()) |
|---|