[IMP] uninstall: use dedicated table instead of overloading ir.model.data.

bzr revid: vmt@openerp.com-20120601131414-880vmpctmjfxl84f
This commit is contained in:
Vo Minh Thu 2012-06-01 15:14:14 +02:00
parent 5df4aa8cca
commit d790650bfb
6 changed files with 99 additions and 42 deletions

View File

@ -29,8 +29,7 @@ from openerp import netsvc, pooler, tools
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools import config
from openerp.tools.translate import _
from openerp.osv.orm import except_orm, browse_record, EXT_ID_PREFIX_FK, \
EXT_ID_PREFIX_M2M_TABLE, EXT_ID_PREFIX_CONSTRAINT
from openerp.osv.orm import except_orm, browse_record, EXT_ID_PREFIX_M2M_TABLE
_logger = logging.getLogger(__name__)
@ -857,8 +856,7 @@ class ir_model_data(osv.osv):
model_obj = self.pool.get(model)
name = tools.ustr(data.name)
if name.startswith(EXT_ID_PREFIX_FK) or name.startswith(EXT_ID_PREFIX_M2M_TABLE)\
or name.startswith(EXT_ID_PREFIX_CONSTRAINT):
if name.startswith(EXT_ID_PREFIX_M2M_TABLE):
# double-check we are really going to delete all the owners of this schema element
cr.execute("""SELECT id from ir_model_data where name = %s and res_id IS NULL""", (data.name,))
external_ids = [x[0] for x in cr.fetchall()]
@ -866,16 +864,6 @@ class ir_model_data(osv.osv):
# as installed modules have defined this element we must not delete it!
continue
if name.startswith(EXT_ID_PREFIX_FK):
name = name[len(EXT_ID_PREFIX_FK):]
# test if FK exists on this table (it could be on a related m2m table, in which case we ignore it)
cr.execute("""SELECT 1 from pg_constraint cs JOIN pg_class cl ON (cs.conrelid = cl.oid)
WHERE cs.contype=%s and cs.conname=%s and cl.relname=%s""", ('f', name, model_obj._table))
if cr.fetchone():
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name),)
_logger.info('Dropped FK CONSTRAINT %s@%s', name, model)
continue
if name.startswith(EXT_ID_PREFIX_M2M_TABLE):
name = name[len(EXT_ID_PREFIX_M2M_TABLE):]
cr.execute("SELECT 1 FROM information_schema.tables WHERE table_name=%s", (name,))
@ -883,16 +871,6 @@ class ir_model_data(osv.osv):
to_drop_table.append(name)
continue
if name.startswith(EXT_ID_PREFIX_CONSTRAINT):
name = name[len(EXT_ID_PREFIX_CONSTRAINT):]
# test if constraint exists
cr.execute("""SELECT 1 from pg_constraint cs JOIN pg_class cl ON (cs.conrelid = cl.oid)
WHERE cs.contype=%s and cs.conname=%s and cl.relname=%s""", ('u', name, model_obj._table))
if cr.fetchone():
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name),)
_logger.info('Dropped CONSTRAINT %s@%s', name, model)
continue
pair_to_unlink = (model, res_id)
if pair_to_unlink not in to_unlink:
to_unlink.append(pair_to_unlink)

View File

@ -1,8 +1,12 @@
import logging
import openerp
from openerp import SUPERUSER_ID
from openerp.osv import fields
from openerp.osv.orm import Model
_logger = logging.getLogger(__name__)
class ir_model_constraint(Model):
"""
This model tracks PostgreSQL foreign keys and constraints used by OpenERP
@ -33,7 +37,7 @@ class ir_model_constraint(Model):
Delete PostgreSQL foreign keys and constraints tracked by this model.
"""
if uid != 1 and not self.pool.get('ir.model.access').check_groups(cr, uid, "base.group_system"):
if uid != SUPERUSER_ID and not self.pool.get('ir.model.access').check_groups(cr, uid, "base.group_system"):
raise except_orm(_('Permission Denied'), (_('Administrator access is required to uninstall a module')))
context = dict(context or {})
@ -41,10 +45,11 @@ class ir_model_constraint(Model):
ids_set = set(ids)
ids.sort()
ids.reverse()
to_unlink = []
for data in self.browse(cr, uid, ids, context):
model = data.model
model = data.model.name
model_obj = self.pool.get(model)
name = tools.ustr(data.name)
name = openerp.tools.ustr(data.name)
typ = data.type
# double-check we are really going to delete all the owners of this schema element
@ -52,20 +57,25 @@ class ir_model_constraint(Model):
external_ids = [x[0] for x in cr.fetchall()]
if (set(external_ids)-ids_set):
# as installed modules have defined this element we must not delete it!
pass
continue
elif typ == 'f':
if typ == 'f':
# test if FK exists on this table (it could be on a related m2m table, in which case we ignore it)
cr.execute("""SELECT 1 from pg_constraint cs JOIN pg_class cl ON (cs.conrelid = cl.oid)
WHERE cs.contype=%s and cs.conname=%s and cl.relname=%s""", ('f', name, model_obj._table))
if cr.fetchone():
print '>>> ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name)
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name),)
_logger.info('Dropped FK CONSTRAINT %s@%s', name, model)
elif typ == 'u':
if typ == 'u':
# test if constraint exists
cr.execute("""SELECT 1 from pg_constraint cs JOIN pg_class cl ON (cs.conrelid = cl.oid)
WHERE cs.contype=%s and cs.conname=%s and cl.relname=%s""", ('u', name, model_obj._table))
if cr.fetchone():
print '>>> ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name)
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name),)
_logger.info('Dropped CONSTRAINT %s@%s', name, model)
to_unlink.append(data.id)
self.unlink(cr, uid, to_unlink, context)

View File

@ -364,7 +364,10 @@ class module(osv.osv):
including the deletion of all database structures created by the module:
tables, columns, constraints, etc."""
ir_model_data = self.pool.get('ir.model.data')
ir_model_constraint = self.pool.get('ir.model.constraint')
modules_to_remove = [m.name for m in self.browse(cr, uid, ids, context)]
constraint_ids = ir_model_constraint.search(cr, uid, [('module', 'in', modules_to_remove)])
ir_model_constraint._module_data_uninstall(cr, uid, constraint_ids, context)
data_ids = ir_model_data.search(cr, uid, [('module', 'in', modules_to_remove)])
ir_model_data._module_data_uninstall(cr, uid, data_ids, context)
ir_model_data.unlink(cr, uid, data_ids, context)

View File

@ -71,9 +71,7 @@ _schema = logging.getLogger(__name__ + '.schema')
from openerp.tools import SKIPPED_ELEMENT_TYPES
# Prefixes for external IDs of schema elements
EXT_ID_PREFIX_FK = "_foreign_key_"
EXT_ID_PREFIX_M2M_TABLE = "_m2m_rel_table_"
EXT_ID_PREFIX_CONSTRAINT = "_constraint_"
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
regex_object_name = re.compile(r'^[a-z0-9_.]+$')
@ -3108,7 +3106,6 @@ class BaseModel(object):
""" Create the foreign keys recorded by _auto_init. """
for t, k, r, d in self._foreign_keys:
cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY ("%s") REFERENCES "%s" ON DELETE %s' % (t, k, r, d))
self._make_ext_id(cr, "%s%s_%s_fkey" % (EXT_ID_PREFIX_FK, t, k))
self._save_constraint(cr, "%s_%s_fkey" % (t, k), 'f')
cr.commit()
del self._foreign_keys
@ -3234,7 +3231,7 @@ class BaseModel(object):
for (key, con, _) in self._sql_constraints:
conname = '%s_%s' % (self._table, key)
self._make_ext_id(cr, EXT_ID_PREFIX_CONSTRAINT + conname)
self._save_constraint(cr, conname, 'u')
cr.execute("SELECT conname, pg_catalog.pg_get_constraintdef(oid, true) as condef FROM pg_constraint where conname=%s", (conname,))
existing_constraints = cr.dictfetchall()
sql_actions = {

View File

@ -1,11 +1,18 @@
# -*- coding: utf-8 -*-
import openerp
from openerp.osv import fields
from openerp.osv.orm import Model
class test_uninstall_model(Model):
_name = 'test_uninstall.model'
_columns = {
'name': fields.char('Name', size=64),
'ref': fields.many2one('res.users', string='User'),
}
_sql_constraints = [
('name_uniq', 'unique (name)', 'Each name must be unique.')
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

78
t.py
View File

@ -1,18 +1,80 @@
import openerp
from openerp import SUPERUSER_ID
from openerp.osv import fields
from openerp.osv.orm import Model
conf = openerp.tools.config
# TODO Exception handling (especially on cursors).
def get_registry(database_name):
registry = openerp.modules.registry.RegistryManager.get(database_name)
return registry
def reload_registry(database_name):
openerp.modules.registry.RegistryManager.new(
database_name, update_module=True)
def search_registry(database_name, model_name, domain):
registry = get_registry(database_name)
cr = registry.db.cursor()
model = registry.get(model_name)
record_ids = model.search(cr, SUPERUSER_ID,
domain, {})
cr.close()
return record_ids
def install_module(database_name, module_name):
registry = get_registry(database_name)
ir_module_module = registry.get('ir.module.module')
cr = registry.db.cursor()
module_ids = ir_module_module.search(cr, SUPERUSER_ID,
[('name', '=', module_name)], {})
assert len(module_ids) == 1
ir_module_module.button_install(cr, SUPERUSER_ID, module_ids, {})
cr.commit()
cr.close()
reload_registry(database_name)
def uninstall_module(database_name, module_name):
registry = get_registry(database_name)
ir_module_module = registry.get('ir.module.module')
cr = registry.db.cursor()
module_ids = ir_module_module.search(cr, SUPERUSER_ID,
[('name', '=', module_name)], {})
assert len(module_ids) == 1
ir_module_module.button_uninstall(cr, SUPERUSER_ID, module_ids, {})
cr.commit()
cr.close()
reload_registry(database_name)
if __name__ == '__main__':
openerp.netsvc.init_logger()
conf['addons_path'] = './openerp/tests/addons'
conf['init'] = {'test_uninstall': 1}
registry = openerp.modules.registry.RegistryManager.new('xx', update_module=True)
conf['addons_path'] = './openerp/tests/addons,../../addons/trunk,../../web/trunk/addons'
install_module('xx', 'test_uninstall')
registry = get_registry('xx')
assert registry.get('test_uninstall.model')
assert search_registry('xx', 'ir.model.data',
[('module', '=', 'test_uninstall')])
assert search_registry('xx', 'ir.model.fields',
[('model', '=', 'test_uninstall.model')])
uninstall_module('xx', 'test_uninstall')
registry = get_registry('xx')
assert not registry.get('test_uninstall.model')
assert not search_registry('xx', 'ir.model.data',
[('module', '=', 'test_uninstall')])
assert not search_registry('xx', 'ir.model.fields',
[('model', '=', 'test_uninstall.model')])
ir_model_constraint = registry.get('ir.model.constraint')
print ir_model_constraint
cr = registry.db.cursor()
ids = ir_model_constraint.search(cr, openerp.SUPERUSER_ID, [], {})
print ir_model_constraint.browse(cr, openerp.SUPERUSER_ID, ids, {})
ids = ir_model_constraint.search(cr, SUPERUSER_ID, [], {})
#print ir_model_constraint.browse(cr, SUPERUSER_ID, ids, {})
cr.close()
#####################################################################
@ -33,8 +95,8 @@ MY_MODULE = {
'depends': ['base'],
}
def create_virtual_module(db_name, module_name, info):
registry = openerp.modules.registry.RegistryManager.get(db_name)
def create_virtual_module(database_name, module_name, info):
registry = get_registry(database_name)
cr = registry.db.cursor()
cr.execute("""SELECT 1 FROM ir_module_module WHERE name=%s""", (module_name,))