Query goal: | Find cases where the same table has multiple triggers with the same type (row-level, statement-level) that react to the same event with the same WHEN condition and with the same way (by invoking the same function). |
Notes about the query: | The Tr subquery has been created based on the subquery of INFORMATION_SCHEMA.triggers view. |
Query type: | Problem detection (Each row in the result could represent a flaw in the design) |
Query reliability: | High (Few or no false-positive results) |
Query license: | MIT License |
Fixing suggestion: | All but one are redundant. Drop the redundant triggers. |
Data source: | INFORMATION_SCHEMA+system catalog |
SQL query: | Click on query to copy it
WITH Tr AS (SELECT n.nspname AS trigger_schema, t.tgname AS trigger_name, em.text AS event_manipulation, c.relname AS trigger_table, np.nspname AS routine_schema, p.proname AS routine_name, (SELECT DISTINCT action_condition FROM INFORMATION_SCHEMA.triggers AS it WHERE it.trigger_schema=n.nspname AND it.event_object_table=c.relname AND it.trigger_name=t.tgname) AS action_condition, CASE t.tgtype::integer & 1 WHEN 1 THEN 'ROW' ELSE 'STATEMENT' END AS action_orientation, CASE t.tgtype::integer & 66 WHEN 2 THEN 'BEFORE' WHEN 64 THEN 'INSTEAD OF' ELSE 'AFTER' END AS action_timing FROM pg_namespace n, pg_class c, pg_trigger t, pg_proc p, pg_namespace np, ( VALUES (4,'INSERT'), (8,'DELETE'), (16,'UPDATE'), (32,'TRUNCATE')) em(num, text) WHERE n.oid = c.relnamespace AND c.oid = t.tgrelid AND t.tgfoid=p.oid AND p.pronamespace=np.oid AND (t.tgtype::integer & em.num) <> 0 AND NOT t.tgisinternal AND NOT pg_is_other_temp_schema(n.oid)) SELECT trigger_schema, trigger_table, action_timing, event_manipulation, action_orientation, routine_schema, routine_name, action_condition, Count(*) AS number_of_triggers FROM tr GROUP BY trigger_schema, trigger_table, action_timing, event_manipulation, action_orientation, routine_schema, routine_name, action_condition HAVING Count(*)>1 ORDER BY Count(*) DESC, trigger_schema, trigger_table; |
SQL query | Description |
---|---|
WITH Tr AS (SELECT n.nspname AS trigger_schema, t.tgname AS trigger_name, em.text AS event_manipulation, c.relname AS trigger_table, np.nspname AS routine_schema, p.proname AS routine_name, CASE WHEN pg_has_role(c.relowner, 'USAGE') THEN ( SELECT rm.m[1] AS m FROM regexp_matches(pg_get_triggerdef(t.oid), '.{35,} WHEN \((.+)\) EXECUTE PROCEDURE') rm(m) LIMIT 1) ELSE NULL END AS action_condition, CASE t.tgtype::integer & 1 WHEN 1 THEN 'ROW' ELSE 'STATEMENT' END AS action_orientation, CASE t.tgtype::integer & 66 WHEN 2 THEN 'BEFORE' WHEN 64 THEN 'INSTEAD OF' ELSE 'AFTER' END AS action_timing FROM pg_namespace n, pg_class c, pg_trigger t, pg_proc p, pg_namespace np, ( VALUES (4,'INSERT'), (8,'DELETE'), (16,'UPDATE'), (32,'TRUNCATE')) em(num, text) WHERE n.oid = c.relnamespace AND c.oid = t.tgrelid AND t.tgfoid=p.oid AND p.pronamespace=np.oid AND (t.tgtype::integer & em.num) <> 0 AND NOT t.tgisinternal AND NOT pg_is_other_temp_schema(n.oid)), duplicate_triggers AS (SELECT trigger_schema, trigger_table, action_timing, event_manipulation, action_orientation, routine_schema, routine_name, action_condition, Count(*) AS cnt, array_agg(trigger_name) AS triggers FROM tr GROUP BY trigger_schema, trigger_table, action_timing, event_manipulation, action_orientation, routine_schema, routine_name, action_condition HAVING Count(*)>1) SELECT format('DROP TRIGGER %1$I ON %2$I.%3$I;', unnest(triggers), trigger_schema, trigger_table) AS statements FROM duplicate_triggers ORDER BY cnt DESC, trigger_schema, trigger_table; | Drop the trigger. One of the triggers must stay in place. |
Collection name | Collection description |
---|---|
Find problems automatically | Queries, that results point to problems in the database. Each query in the collection produces an initial assessment. However, a human reviewer has the final say as to whether there is a problem or not . |
Category name | Category description |
---|---|
Comfortability of database evolution | Queries of this category provide information about the means that influence database evolution. |
Comfortability of data management | Queries of this category provide information about the means that have been used to make the use or management of database more comfortable and thus, more efficient. |
Duplication of implementation elements | Queries of this catergory provide information about the duplication of the database objects. |
Triggers and rules | Queries of this category provide information about triggers and rules in a database. |
Reference |
---|
https://refactoring.guru/smells/alternative-classes-with-different-interfaces |
https://refactoring.guru/smells/duplicate-code |
The corresponding code smell in case of cleaning code is "G5: Duplication". (Robert C. Martin, Clean Code) |