PostgreSQL Error 27000: triggered data change violation
PostgreSQL error code 27000, triggered data change violation, occurs when a trigger function attempts to perform a data modification that is not permitted within its execution context. This typically happens when an AFTER trigger tries to modify the very table that fired it, or when an INSTEAD OF trigger on a view performs illegal data changes. PostgreSQL enforces these restrictions to protect transactional integrity and prevent uncontrolled recursive behavior.
Top 3 Causes
1. AFTER Trigger Modifying Its Own Triggering Table
The most common cause is an AFTER row-level trigger attempting to UPDATE or DELETE rows in the same table that fired the trigger. PostgreSQL blocks this to prevent recursive loops and integrity violations.
Problematic code:
-- This WILL raise error 27000
CREATE OR REPLACE FUNCTION bad_after_trigger()
RETURNS TRIGGER AS $$
BEGIN
-- Attempting to UPDATE the same table that fired this trigger
UPDATE orders SET status = 'processed' WHERE id = NEW.id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER after_insert_orders
AFTER INSERT ON orders
FOR EACH ROW EXECUTE FUNCTION bad_after_trigger();
Fix — switch to a BEFORE trigger:
CREATE OR REPLACE FUNCTION good_before_trigger()
RETURNS TRIGGER AS $$
BEGIN
-- Modify NEW directly before the row is written
NEW.status := 'processed';
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER before_insert_orders
BEFORE INSERT ON orders
FOR EACH ROW EXECUTE FUNCTION good_before_trigger();
2. INSTEAD OF Trigger on a View with Illegal Base Table Modifications
When defining INSTEAD OF triggers on complex views, modifying the wrong base table or creating a circular trigger chain will raise this error. The trigger must only update the correct underlying base tables.
-- Safe INSTEAD OF trigger: update only the correct base tables
CREATE OR REPLACE FUNCTION trg_update_employee_view()
RETURNS TRIGGER AS $$
BEGIN
UPDATE employees
SET name = NEW.name,
department_id = NEW.department_id
WHERE id = OLD.id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER instead_of_update_employees
INSTEAD OF UPDATE ON employee_details_view
FOR EACH ROW EXECUTE FUNCTION trg_update_employee_view();
3. Running TRUNCATE or DDL Inside a Trigger Function
Attempting to run TRUNCATE, ALTER TABLE, or other DDL statements inside a trigger function targeting the triggering table will cause this error. These operations must be moved outside the trigger context entirely.
-- WRONG: Never do this inside a trigger
-- TRUNCATE orders; -- Will raise error 27000
-- CORRECT: Use a separate stored procedure called outside trigger context
CREATE OR REPLACE PROCEDURE cleanup_old_orders(retention_days INT DEFAULT 90)
LANGUAGE plpgsql AS $$
BEGIN
DELETE FROM orders
WHERE created_at < NOW() - (retention_days || ' days')::INTERVAL;
END;
$$;
-- Call manually or via a scheduler like pg_cron
CALL cleanup_old_orders(90);
Quick Fix Checklist
-
Replace
AFTERwithBEFOREwhen you need to modify the triggering row — useNEW.column := valueinstead of issuing a newUPDATE. -
Redirect side effects to other tables — if you must use
AFTER, write to an audit or log table, not the source table. - Audit all triggers regularly using the system catalog:
SELECT trigger_name, event_object_table,
action_timing, event_manipulation
FROM information_schema.triggers
WHERE trigger_schema = 'public'
ORDER BY event_object_table;
Prevention Tips
-
Apply the Single Responsibility Principle:
BEFOREtriggers handle validation and value transformation;AFTERtriggers handle side effects on other tables only. Never mix these roles. - Test all trigger paths in staging before deploying to production. Draw a trigger execution flow diagram to identify any self-referencing or circular dependencies before writing a single line of code.
Related Errors
| Code | Name | Relationship |
|---|---|---|
| 09000 | triggered_action_exception | Parent error class for trigger failures |
| 23000 | integrity_constraint_violation | Often co-occurs during bad trigger data changes |
| 25006 | read_only_sql_transaction | Triggers modifying data in read-only replica contexts |
📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.
Top comments (0)