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