From eadd5e0a0a6aa8babfbedce91f28f9701c2812ea Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Fri, 1 Jun 2012 17:34:49 +0200 Subject: [PATCH] [IMP] uninstall: use a dedicated table for many2many relationships instead of overlaoding ir.model.data. bzr revid: vmt@openerp.com-20120601153449-uj2sl7qjzz3s7xnj --- openerp/addons/base/base.sql | 20 ++++++++++++--- openerp/addons/base/ir/__init__.py | 1 + openerp/addons/base/ir/ir_model.py | 23 +---------------- openerp/addons/base/ir/ir_model_constraint.py | 2 -- openerp/addons/base/module/module.py | 3 +++ openerp/osv/orm.py | 25 +++++++++++-------- openerp/tests/addons/test_uninstall/models.py | 1 + 7 files changed, 38 insertions(+), 37 deletions(-) diff --git a/openerp/addons/base/base.sql b/openerp/addons/base/base.sql index a227007353c..0a8f50c24bf 100644 --- a/openerp/addons/base/base.sql +++ b/openerp/addons/base/base.sql @@ -347,9 +347,8 @@ CREATE TABLE ir_model_data ( res_id integer, primary key(id) ); --- Records foreign keys and constraints --- installed by a module (so they can be removed them when the module is --- uninstalled).: +-- Records foreign keys and constraints installed by a module (so they can be +-- removed them when the module is uninstalled): -- - for a foreign key: type 'f', -- - for a constraint: type 'u'. CREATE TABLE ir_model_constraint ( @@ -366,6 +365,21 @@ CREATE TABLE ir_model_constraint ( name character varying(128) NOT NULL ); +-- Records relation tables (i.e. implementing many2many) installed by a module +-- (so they can be removed them when the module is uninstalled). +CREATE TABLE ir_model_relation ( + id serial NOT NULL, + create_uid integer, + create_date timestamp without time zone, + write_date timestamp without time zone, + write_uid integer, + date_init timestamp without time zone, + date_update timestamp without time zone, + module integer NOT NULL references ir_module_module on delete restrict, + model integer NOT NULL references ir_model on delete restrict, + name character varying(128) NOT NULL +); + --------------------------------- -- Users --------------------------------- diff --git a/openerp/addons/base/ir/__init__.py b/openerp/addons/base/ir/__init__.py index f4605d1615f..bc4f3eecfbf 100644 --- a/openerp/addons/base/ir/__init__.py +++ b/openerp/addons/base/ir/__init__.py @@ -21,6 +21,7 @@ import ir_model import ir_model_constraint +import ir_model_relation import ir_sequence import ir_needaction import ir_ui_menu diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index 174f52b1944..231b19bb3f8 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -29,7 +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_M2M_TABLE +from openerp.osv.orm import except_orm, browse_record _logger = logging.getLogger(__name__) @@ -847,7 +847,6 @@ class ir_model_data(osv.osv): ids_set = set(ids) wkf_todo = [] to_unlink = [] - to_drop_table = [] ids.sort() ids.reverse() for data in self.browse(cr, uid, ids, context): @@ -856,21 +855,6 @@ class ir_model_data(osv.osv): model_obj = self.pool.get(model) name = tools.ustr(data.name) - 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()] - if (set(external_ids)-ids_set): - # as installed modules have defined this element we must not delete it! - 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,)) - if cr.fetchone() and not name in to_drop_table: - to_drop_table.append(name) - continue - pair_to_unlink = (model, res_id) if pair_to_unlink not in to_unlink: to_unlink.append(pair_to_unlink) @@ -890,11 +874,6 @@ class ir_model_data(osv.osv): except: _logger.info('Unable to force processing of workflow for item %s@%s in order to leave activity to be deleted', res_id, model) - # drop m2m relation tables - for table in to_drop_table: - cr.execute('DROP TABLE %s CASCADE'% (table),) - _logger.info('Dropped table %s', table) - def unlink_if_refcount(to_unlink): for model, res_id in to_unlink: external_ids = self.search(cr, uid, [('model', '=', model),('res_id', '=', res_id)]) diff --git a/openerp/addons/base/ir/ir_model_constraint.py b/openerp/addons/base/ir/ir_model_constraint.py index 620edb32363..912284a8216 100644 --- a/openerp/addons/base/ir/ir_model_constraint.py +++ b/openerp/addons/base/ir/ir_model_constraint.py @@ -64,7 +64,6 @@ class ir_model_constraint(Model): 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) @@ -73,7 +72,6 @@ class ir_model_constraint(Model): 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) diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py index 371bfb1b93c..20151946ed0 100644 --- a/openerp/addons/base/module/module.py +++ b/openerp/addons/base/module/module.py @@ -365,9 +365,12 @@ class module(osv.osv): tables, columns, constraints, etc.""" ir_model_data = self.pool.get('ir.model.data') ir_model_constraint = self.pool.get('ir.model.constraint') + ir_model_relation = self.pool.get('ir.model.relation') 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) + relation_ids = ir_model_relation.search(cr, uid, [('module', 'in', modules_to_remove)]) + ir_model_relation._module_data_uninstall(cr, uid, relation_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) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 64c627849ac..51485b83266 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -70,9 +70,6 @@ _schema = logging.getLogger(__name__ + '.schema') # List of etree._Element subclasses that we choose to ignore when parsing XML. from openerp.tools import SKIPPED_ELEMENT_TYPES -# Prefixes for external IDs of schema elements -EXT_ID_PREFIX_M2M_TABLE = "_m2m_rel_table_" - regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I) regex_object_name = re.compile(r'^[a-z0-9_.]+$') @@ -2743,6 +2740,7 @@ class BaseModel(object): self._table, column['attname']) def _save_constraint(self, cr, constraint_name, type): + """ TODO """ assert type in ('f', 'u') cr.execute(""" SELECT 1 FROM ir_model_constraint, ir_module_module @@ -2759,13 +2757,20 @@ class BaseModel(object): (SELECT id FROM ir_model WHERE model=%s), %s)""", (constraint_name, self._module, self._name, type)) - # quick creation of ir.model.data entry to make uninstall of schema elements easier - def _make_ext_id(self, cr, ext_id): - cr.execute('SELECT 1 FROM ir_model_data WHERE name=%s AND module=%s', (ext_id, self._module)) + def _save_relation_table(self, cr, relation_table): + """ TODO """ + cr.execute(""" + SELECT 1 FROM ir_model_relation, ir_module_module + WHERE ir_model_relation.module=ir_module_module.id + AND ir_model_relation.name=%s + AND ir_module_module.name=%s + """, (relation_table, self._module)) if not cr.rowcount: - cr.execute("""INSERT INTO ir_model_data (name,date_init,date_update,module,model) - VALUES (%s, now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC', %s, %s)""", - (ext_id, self._module, self._name)) + cr.execute("""INSERT INTO ir_model_relation (name, date_init, date_update, module, model) + VALUES (%s, now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC', + (SELECT id FROM ir_module_module WHERE name=%s), + (SELECT id FROM ir_model WHERE model=%s))""", + (relation_table, self._module, self._name)) # checked version: for direct m2o starting from `self` def _m2o_add_foreign_key_checked(self, source_field, dest_model, ondelete): @@ -3195,7 +3200,7 @@ class BaseModel(object): def _m2m_raise_or_create_relation(self, cr, f): m2m_tbl, col1, col2 = f._sql_names(self) - self._make_ext_id(cr, EXT_ID_PREFIX_M2M_TABLE + m2m_tbl) + self._save_relation_table(cr, m2m_tbl) cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (m2m_tbl,)) if not cr.dictfetchall(): if not self.pool.get(f._obj): diff --git a/openerp/tests/addons/test_uninstall/models.py b/openerp/tests/addons/test_uninstall/models.py index 335aedc941a..d7211dc2130 100644 --- a/openerp/tests/addons/test_uninstall/models.py +++ b/openerp/tests/addons/test_uninstall/models.py @@ -9,6 +9,7 @@ class test_uninstall_model(Model): _columns = { 'name': fields.char('Name', size=64), 'ref': fields.many2one('res.users', string='User'), + 'rel': fields.many2many('res.users', string='Users'), } _sql_constraints = [