[POS-commit] r6302 - in stoqlib/trunk: data/sql
external/sqlobject/postgres stoqlib stoqlib/database stoqlib/domain
Johan Dahlin
jdahlin at async.com.br
Tue Apr 3 19:20:06 BRT 2007
Author: jdahlin
Date: Tue Apr 3 19:20:05 2007
New Revision: 6302
Added:
stoqlib/trunk/data/sql/patch-1.sql
Removed:
stoqlib/trunk/data/sql/postgres-schema-migration-6.sql
stoqlib/trunk/data/sql/postgres-schema-migration-7.sql
Modified:
stoqlib/trunk/external/sqlobject/postgres/pgconnection.py
stoqlib/trunk/stoqlib/__init__.py
stoqlib/trunk/stoqlib/database/admin.py
stoqlib/trunk/stoqlib/database/migration.py
stoqlib/trunk/stoqlib/domain/system.py
Log:
#2752: Rework database migration
Added: stoqlib/trunk/data/sql/patch-1.sql
==============================================================================
--- (empty file)
+++ stoqlib/trunk/data/sql/patch-1.sql Tue Apr 3 19:20:05 2007
@@ -0,0 +1,5 @@
+-- #2752: Rework database migration
+DELETE FROM system_table;
+ALTER TABLE system_table ADD COLUMN generation integer;
+ALTER TABLE system_table RENAME COLUMN version TO patchlevel;
+ALTER TABLE system_table RENAME COLUMN update_date TO updated;
Modified: stoqlib/trunk/external/sqlobject/postgres/pgconnection.py
==============================================================================
--- stoqlib/trunk/external/sqlobject/postgres/pgconnection.py (original)
+++ stoqlib/trunk/external/sqlobject/postgres/pgconnection.py Tue Apr 3 19:20:05 2007
@@ -216,6 +216,15 @@
name)
return res[0] == 1
+ def tableHasColumn(self, table_name, column):
+ res = self.queryOne(
+ """SELECT 1 FROM pg_class, pg_attribute
+ WHERE pg_attribute.attrelid = pg_class.oid AND
+ pg_class.relname=%s AND
+ attname=%s""" % (self.sqlrepr(table_name),
+ self.sqlrepr(column)))
+ return bool(res)
+
def dbVersion(self):
version_string = self.queryOne('SELECT VERSION();')[0]
version = version_string.split(' ', 2)[1]
Modified: stoqlib/trunk/stoqlib/__init__.py
==============================================================================
--- stoqlib/trunk/stoqlib/__init__.py (original)
+++ stoqlib/trunk/stoqlib/__init__.py Tue Apr 3 19:20:05 2007
@@ -29,8 +29,6 @@
from kiwi.environ import Library
-FIRST_DB_VERSION = 5
-
program_name = "Stoqlib"
website = 'http://www.stoq.com.br/'
version = "0.8.10"
Modified: stoqlib/trunk/stoqlib/database/admin.py
==============================================================================
--- stoqlib/trunk/stoqlib/database/admin.py (original)
+++ stoqlib/trunk/stoqlib/database/admin.py Tue Apr 3 19:20:05 2007
@@ -2,7 +2,7 @@
# vi:si:et:sw=4:sts=4:ts=4
##
-## Copyright (C) 2005 Async Open Source <http://www.async.com.br>
+## Copyright (C) 2005-2007 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
@@ -38,6 +38,7 @@
from stoqlib.database.database import execute_sql, clean_database
from stoqlib.database.interfaces import ICurrentUser, IDatabaseSettings
+from stoqlib.database.migration import schema_migration
from stoqlib.database.runtime import new_transaction
from stoqlib.domain.interfaces import (IIndividual, IEmployee, IUser,
ISalesPerson)
@@ -45,7 +46,6 @@
from stoqlib.domain.person import EmployeeRoleHistory
from stoqlib.domain.profile import UserProfile
from stoqlib.domain.sellable import SellableTaxConstant, SellableUnit
-from stoqlib.domain.system import SystemTable
from stoqlib.exceptions import StoqlibError
from stoqlib.lib.parameters import sysparam, ensure_system_parameters
from stoqlib.lib.translation import stoqlib_gettext
@@ -205,6 +205,8 @@
schema = environ.find_resource('sql', 'views.sql')
execute_sql(schema)
+ schema_migration.update_schema()
+
def create_default_profiles():
trans = new_transaction()
@@ -228,7 +230,3 @@
ensure_sellable_constants()
ensure_system_parameters()
create_default_profiles()
-
- trans = new_transaction()
- SystemTable.update(trans, check_new_db=True)
- trans.commit(close=True)
Modified: stoqlib/trunk/stoqlib/database/migration.py
==============================================================================
--- stoqlib/trunk/stoqlib/database/migration.py (original)
+++ stoqlib/trunk/stoqlib/database/migration.py Tue Apr 3 19:20:05 2007
@@ -2,7 +2,7 @@
# vi:si:et:sw=4:sts=4:ts=4
##
-## Copyright (C) 2006 Async Open Source <http://www.async.com.br>
+## Copyright (C) 2007 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
@@ -20,102 +20,126 @@
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Evandro Vale Miquelito <evandro at async.com.br>
+## Johan Dahlin <jdahlin at async.com.br>
##
##
""" Routines for database schema migration"""
-from kiwi.component import get_utility
+import glob
+import operator
+import os
+import shutil
+import tempfile
+
from kiwi.environ import environ
-import stoqlib
-from stoqlib.database.admin import create_base_schema
from stoqlib.database.database import execute_sql
-from stoqlib.database.interfaces import IDatabaseSettings
-from stoqlib.database.runtime import new_transaction
+from stoqlib.database.runtime import new_transaction, get_connection
from stoqlib.domain.profile import update_profile_applications
from stoqlib.domain.system import SystemTable
from stoqlib.exceptions import DatabaseInconsistency
+from stoqlib.lib.defaults import stoqlib_gettext
from stoqlib.lib.parameters import (check_parameter_presence,
ensure_system_parameters)
+_ = stoqlib_gettext
+
+def _extract_version(patch_filename):
+ return int(patch_filename[:-4].split('-', 1)[1])
+
class SchemaMigration:
"""Schema migration management"""
- def __init__(self):
- self.current_db_version = None
- self.db_version = stoqlib.db_version
-
- def _get_migration_files(self, current_db_version, db_version):
- """Returns a list of all the migration sql files for a certain
- db schema version
- """
- rdbms_name = get_utility(IDatabaseSettings).rdbms
- migration_files = []
- for version in range(current_db_version + 1, self.db_version +1):
- filename = '%s-schema-migration-%s.sql' % (rdbms_name, version)
- sql_file = environ.find_resource('sql', filename)
- migration_files.append(sql_file)
- return migration_files
+ def _get_patches(self):
+ patches = []
+ for directory in environ.get_resource_paths('sql'):
+ for patch in glob.glob(os.path.join(directory, 'patch-*.sql')):
+ patches.append((patch, _extract_version(os.path.basename(patch))))
+ return sorted(patches, key=operator.itemgetter(1))
+
+ def _get_generation(self, conn, current_version):
+ if current_version > 0:
+ generation = SystemTable.select(connection=conn).max('generation')
+ else:
+ generation = 0
+
+ return generation
def _check_up_to_date(self, conn):
- """Checks if the current database schema is up to date"""
- if not conn.tableExists(SystemTable.sqlmeta.table):
- SystemTable.createTable(connection=conn)
- self.current_db_version = stoqlib.FIRST_DB_VERSION
- SystemTable.update(conn, check_new_db=True,
- version=self.current_db_version)
+ # Fetch the latest, eg the last in the list
+ patches = self._get_patches()
+ unused, latest_available = patches[-1]
+
+ current_version = self.get_current_version(conn)
+ if current_version == latest_available:
return True
- results = SystemTable.select(connection=conn)
- self.current_db_version = results.max('version')
- if self.current_db_version == self.db_version:
- return False
- if self.current_db_version > self.db_version:
- raise DatabaseInconsistency('The current version of database '
- '(%s) is greater than the system '
- 'version (%s)'
- % (self.current_db_version,
- self.db_version))
- return True
+ elif current_version > latest_available:
+ raise DatabaseInconsistency(
+ 'The current version of database (%d) is greater than the '
+ 'latest available version (%d)'
+ % (current_version, latest_available))
+
+ return False
def check_updated(self, conn):
if not self._check_up_to_date(conn):
- return True
+ return False
- if check_parameter_presence(conn):
- return True
+ if not check_parameter_presence(conn):
+ return False
- return False
+ return True
def update_schema(self):
"""Check the current version of database and update the schema if
it's needed
"""
- trans = new_transaction()
+ conn = get_connection()
- if not self._check_up_to_date(trans):
- trans.commit(close=True)
+ if self._check_up_to_date(conn):
return
- # Updating the schema for all the versions from the current database
- # version to the last schema version.
- sql_files = self._get_migration_files(self.current_db_version,
- self.db_version)
- for sql_file in sql_files:
- execute_sql(sql_file, trans)
- parts = sql_file.replace('.sql', '').split('-')
- version = parts[-1]
- try:
- version = int(version)
- except ValueError:
- raise ValueError("Bad sql file name, got %s" % sql_file)
- SystemTable.update(trans, version=version)
- # checks if there is new applications and update all the user
- # profiles on the system
- update_profile_applications(trans)
- # Updating the parameter list
- ensure_system_parameters(update=True)
- # Update the base schema
- create_base_schema()
- trans.commit(close=True)
+ patches = self._get_patches()
+ current_version = self.get_current_version(conn)
+ generation = self._get_generation(conn, current_version)
+
+ initializing = current_version == 0
+
+ for patch, patchlevel in patches:
+ if patchlevel <= current_version:
+ continue
+ temporary = tempfile.mktemp(prefix="patch-%d" % patchlevel)
+ shutil.copy(patch, temporary)
+ open(temporary, 'a').write(
+ """INSERT INTO system_table (updated, patchlevel, generation) VALUES
+ (NOW(), %s, %s)""" % (conn.sqlrepr(patchlevel),
+ conn.sqlrepr(generation)))
+ execute_sql(temporary)
+ os.unlink(temporary)
+
+ if not initializing:
+ # checks if there is new applications and update all the user
+ # profiles on the system
+ trans = new_transaction()
+ update_profile_applications(trans)
+ trans.commit(close=True)
+
+ # Updating the parameter list
+ ensure_system_parameters(update=True)
+
+ return current_version, patchlevel
+
+ def get_current_version(self, conn):
+ assert conn.tableExists('system_table')
+
+ if conn.tableHasColumn('system_table', 'generation'):
+ results = SystemTable.select(connection=conn)
+ current_version = results.max('patchlevel')
+ elif conn.tableHasColumn('asellable', 'code'):
+ raise SystemExit(_("Unsupported database version, you need to reinstall"))
+ else:
+ current_version = 0
+
+ return current_version
schema_migration = SchemaMigration()
Modified: stoqlib/trunk/stoqlib/domain/system.py
==============================================================================
--- stoqlib/trunk/stoqlib/domain/system.py (original)
+++ stoqlib/trunk/stoqlib/domain/system.py Tue Apr 3 19:20:05 2007
@@ -2,7 +2,7 @@
# vi:si:et:sw=4:sts=4:ts=4
##
-## Copyright (C) 2006 Async Open Source <http://www.async.com.br>
+## Copyright (C) 2006-2007 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
@@ -25,40 +25,21 @@
##
""" Routines for system data management"""
-import datetime
-
from sqlobject import SQLObject
from sqlobject import DateTimeCol, IntCol
-from stoqlib import db_version
from stoqlib.domain.base import AbstractModel
class SystemTable(SQLObject, AbstractModel):
"""Stores information about database schema migration
- I{update_date}: the date when the database schema was updated
- I{version}: the version of the schema installed
+ I{update}: the date when the database schema was updated
+ I{patchlevel}: the version of the schema installed
"""
- update_date = DateTimeCol()
- version = IntCol()
-
- @classmethod
- def update(cls, trans, check_new_db=False, version=None):
- """Add a new entry on SystemTable with the current schema version"""
- result = cls.select(connection=trans)
- if result and check_new_db:
- raise ValueError(
- 'SystemTable should be empty at this point got %d results' %
- result.count())
- elif not result and not check_new_db:
- raise ValueError(
- 'SystemTable should have at least one item at this point, '
- 'got nothing')
-
- return cls(version=version or db_version,
- update_date=datetime.datetime.now(),
- connection=trans)
+ updated = DateTimeCol()
+ patchlevel = IntCol()
+ generation = IntCol()
@classmethod
def is_available(cls, conn):
More information about the POS-commit
mailing list