From a024b08053222061d1b8e2b3b43f31de2a85f20a Mon Sep 17 00:00:00 2001 From: Quoc - Pham Ngoc Date: Wed, 15 Jul 2026 22:13:37 +0700 Subject: [PATCH] [IMP] database_cleanup: Drop dependent views before dropping tables --- database_cleanup/wizards/purge_line_tables.py | 114 ++++++++++++------ 1 file changed, 74 insertions(+), 40 deletions(-) diff --git a/database_cleanup/wizards/purge_line_tables.py b/database_cleanup/wizards/purge_line_tables.py index 82b7be4a0b1..a97440bb06f 100644 --- a/database_cleanup/wizards/purge_line_tables.py +++ b/database_cleanup/wizards/purge_line_tables.py @@ -34,49 +34,11 @@ def purge(self): objs = self.env["cleanup.purge.line.table"].browse( self._context.get("active_ids") ) - tables = objs.mapped("name") for line in objs: if line.purged: continue - - # Retrieve constraints on the tables to be dropped - # This query is referenced in numerous places - # on the Internet but credits probably go to Tom Lane - # in this post http://www.postgresql.org/\ - # message-id/22895.1226088573@sss.pgh.pa.us - # Only using the constraint name and the source table, - # but I'm leaving the rest in for easier debugging - self.env.cr.execute( - """ - SELECT conname, confrelid::regclass, af.attname AS fcol, - conrelid::regclass, a.attname AS col - FROM pg_attribute af, pg_attribute a, - (SELECT conname, conrelid, confrelid,conkey[i] AS conkey, - confkey[i] AS confkey - FROM (select conname, conrelid, confrelid, conkey, - confkey, generate_series(1,array_upper(conkey,1)) AS i - FROM pg_constraint WHERE contype = 'f') ss) ss2 - WHERE af.attnum = confkey AND af.attrelid = confrelid AND - a.attnum = conkey AND a.attrelid = conrelid - AND confrelid::regclass = '%s'::regclass; - """, - (IdentifierAdapter(line.name, quote=False),), - ) - - for constraint in self.env.cr.fetchall(): - if constraint[3] in tables: - self.logger.info( - "Dropping constraint %s on table %s (to be dropped)", - constraint[0], - constraint[3], - ) - self.env.cr.execute( - "ALTER TABLE %s DROP CONSTRAINT %s", - ( - IdentifierAdapter(constraint[3]), - IdentifierAdapter(constraint[0]), - ), - ) + line._drop_constraints(objs) + line._drop_dependent_views(objs) if line.table_type == "base": _sql_type = "TABLE" @@ -88,3 +50,75 @@ def purge(self): ) line.write({"purged": True}) return True + + def _drop_constraints(self, lines): + self.ensure_one() + # Retrieve constraints on the tables to be dropped + # This query is referenced in numerous places + # on the Internet but credits probably go to Tom Lane + # in this post http://www.postgresql.org/\ + # message-id/22895.1226088573@sss.pgh.pa.us + # Only using the constraint name and the source table, + # but I'm leaving the rest in for easier debugging + tables = lines.mapped("name") + self.env.cr.execute( + """ + SELECT conname, confrelid::regclass, af.attname AS fcol, + conrelid::regclass, a.attname AS col + FROM pg_attribute af, pg_attribute a, + (SELECT conname, conrelid, confrelid,conkey[i] AS conkey, + confkey[i] AS confkey + FROM (select conname, conrelid, confrelid, conkey, + confkey, generate_series(1,array_upper(conkey,1)) AS i + FROM pg_constraint WHERE contype = 'f') ss) ss2 + WHERE af.attnum = confkey AND af.attrelid = confrelid AND + a.attnum = conkey AND a.attrelid = conrelid + AND confrelid::regclass = '%s'::regclass; + """, + (IdentifierAdapter(self.name, quote=False),), + ) + + for constraint in self.env.cr.fetchall(): + if constraint[3] in tables: + self.logger.info( + "Dropping constraint %s on table %s (to be dropped)", + constraint[0], + constraint[3], + ) + self.env.cr.execute( + "ALTER TABLE %s DROP CONSTRAINT %s", + ( + IdentifierAdapter(constraint[3]), + IdentifierAdapter(constraint[0]), + ), + ) + + def _drop_dependent_views(self, lines): + self.ensure_one() + self.env.cr.execute( + """ + SELECT + DISTINCT dependent_view.relname + FROM pg_depend dep + JOIN pg_rewrite rw + ON dep.objid = rw.oid + JOIN pg_class dependent_view + ON rw.ev_class = dependent_view.oid + JOIN pg_class source_table + ON dep.refobjid = source_table.oid + JOIN pg_namespace dependent_ns + ON dependent_view.relnamespace = dependent_ns.oid + WHERE source_table.relname = '%s'; + """, + (IdentifierAdapter(self.name, quote=False),), + ) + for dependent_view in self.env.cr.fetchall(): + self.logger.info( + "Dropping view %s depends on table %s (to be dropped)", + dependent_view[0], + self.name, + ) + self.env.cr.execute("DROP VIEW %s", (IdentifierAdapter(dependent_view[0]),)) + lines.filtered(lambda x, view=dependent_view[0]: x.name == view).write( + {"purged": True} + )