235 | | self.upgrade_environment(self.env.get_db_cnx()) |
236 | | |
237 | | def environment_needs_upgrade(self, db): |
238 | | return self._need_initialization(db) |
239 | | |
240 | | def upgrade_environment(self, db): |
241 | | # Create db |
242 | | if self._need_initialization(db): |
243 | | upgrade_db(self.env, self.SCHEMA, db) |
244 | | |
245 | | def _need_initialization(self, db): |
246 | | return need_db_upgrade(self.env, self.SCHEMA, db) |
247 | | }}} |
248 | | |
| 237 | self.upgrade_environment() |
| 238 | |
| 239 | def environment_needs_upgrade(self, db=None): |
| 240 | for realm in self.SCHEMA: |
| 241 | realm_metadata = self.SCHEMA[realm] |
| 242 | |
| 243 | if need_db_create_for_realm(self.env, realm, realm_metadata, db) or \ |
| 244 | need_db_upgrade_for_realm(self.env, realm, realm_metadata, db): |
| 245 | |
| 246 | return True |
| 247 | |
| 248 | return False |
| 249 | |
| 250 | def upgrade_environment(self, db=None): |
| 251 | # Create or update db |
| 252 | @self.env.with_transaction(db) |
| 253 | def do_upgrade_environment(db): |
| 254 | for realm in self.SCHEMA: |
| 255 | realm_metadata = self.SCHEMA[realm] |
| 256 | |
| 257 | if need_db_create_for_realm(self.env, realm, realm_metadata, db): |
| 258 | create_db_for_realm(self.env, realm, realm_metadata, db) |
| 259 | |
| 260 | elif need_db_upgrade_for_realm(self.env, realm, realm_metadata, db): |
| 261 | upgrade_db_for_realm(self.env, 'tracgenericworkflow.upgrades', realm, realm_metadata, db) |
| 262 | }}} |
| 263 | |
| 264 | [[BR]] |
254 | | Then here comes the declaration of the database schema, class fields and class metadata, to be later returned in the corresponding methods of the interface. |
255 | | |
| 270 | Then comes the declaration of the database schema, class fields and class metadata, to be later returned in the corresponding methods of the interface. |
| 271 | |
| 272 | [[BR]] |
| 273 | One note about upgrade. The call to "upgrade_db_for_realm" specifies, as the second parameter, the subdirectory where all of the DB upgrade scripts will be provided. |
| 274 | |
| 275 | This directory will contain simple python files, one for each table and for each DB version upgrade, with a structure similar to the following. |
| 276 | See a detailed explanation after the code box. |
| 277 | Refer to [http://testman4trac.svn.sourceforge.net/viewvc/testman4trac/testman4trac/trunk/testmanager/upgrades/ the TestManager plugin upgrade directory] for a more complete example. |
| 278 | {{{ |
| 279 | from trac.db import Table, Column, Index, DatabaseManager |
| 280 | from tracgenericclass.util import * |
| 281 | from testmanager.model import TestManagerModelProvider |
| 282 | |
| 283 | def do_upgrade(env, ver, db_backend, db): |
| 284 | """ |
| 285 | Add 'page_version' column to testcaseinplan table |
| 286 | """ |
| 287 | cursor = db.cursor() |
| 288 | |
| 289 | realm = 'testcaseinplan' |
| 290 | cursor.execute("CREATE TEMPORARY TABLE %(realm)s_old AS SELECT * FROM %(realm)s" % {'realm': realm}) |
| 291 | cursor.execute("DROP TABLE %(realm)s" % {'realm': realm}) |
| 292 | |
| 293 | table_metadata = Table('testcaseinplan', key = ('id', 'planid'))[ |
| 294 | Column('id'), |
| 295 | Column('planid'), |
| 296 | Column('page_name'), |
| 297 | Column('page_version', type='int'), |
| 298 | Column('status')] |
| 299 | |
| 300 | env.log.info("Updating table for class %s" % realm) |
| 301 | for stmt in db_backend.to_sql(table_metadata): |
| 302 | env.log.debug(stmt) |
| 303 | cursor.execute(stmt) |
| 304 | |
| 305 | cursor = db.cursor() |
| 306 | |
| 307 | cursor.execute("INSERT INTO %(realm)s (id,planid,page_name,page_version,status) " |
| 308 | "SELECT id,planid,page_name,-1,status FROM %(realm)s_old" % {'realm': realm}) |
| 309 | |
| 310 | cursor.execute("DROP TABLE %(realm)s_old" % {'realm': realm}) |
| 311 | |
| 312 | }}} |
| 313 | |
| 314 | [[BR]] |
| 315 | In case you need to update a table's structure (e.g. add columns), you will: |
| 316 | |
| 317 | 1) Create a temporary table with the same structure as the old table |
| 318 | 2) Copy all the contents of the current table into the temporary table |
| 319 | 3) Drop the original table |
| 320 | 4) Recreate the original table with the new structure |
| 321 | 5) Copy back all the contents from the temporary table into the updated original table |
| 322 | 6) Drop the temporary table |
| 323 | |
| 324 | [[BR]] |