Jump into concepts and practice - no test required
or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Trigger for audit logging
📖 Scenario: You are managing a PostgreSQL database for a small company. You want to keep track of changes made to the employees table. This helps you see who changed what and when.
🎯 Goal: Create a trigger that logs every insert, update, and delete on the employees table into an audit_log table.
📋 What You'll Learn
Create an employees table with columns id, name, and position.
Create an audit_log table with columns change_id, employee_id, operation, and change_time.
Write a trigger function called log_employee_changes that inserts a record into audit_log whenever employees is changed.
Create a trigger on employees that calls log_employee_changes after insert, update, or delete.
Test the trigger by inserting a new employee and printing the audit_log contents.
💡 Why This Matters
🌍 Real World
Audit logging is used in companies to track changes in databases for security and compliance.
💼 Career
Database administrators and DevOps engineers often create triggers to automate audit logging and monitor data changes.
Progress0 / 4 steps
1
Create the employees table
Create a table called employees with columns id as serial primary key, name as text, and position as text.
PostgreSQL
Hint
Use SERIAL PRIMARY KEY for id to auto-increment.
2
Create the audit_log table
Create a table called audit_log with columns change_id as serial primary key, employee_id as integer, operation as text, and change_time as timestamp with time zone defaulting to current time.
PostgreSQL
Hint
Use TIMESTAMPTZ DEFAULT now() to record the current time automatically.
3
Create the trigger function log_employee_changes
Write a trigger function called log_employee_changes in PL/pgSQL that inserts into audit_log the employee_id, operation ('INSERT', 'UPDATE', or 'DELETE'), and current time. Use TG_OP to get the operation type. For INSERT and UPDATE use NEW.id, for DELETE use OLD.id. Return NEW for INSERT and UPDATE, and OLD for DELETE.
PostgreSQL
Hint
Use TG_OP to detect the operation type and insert the correct id.
4
Create the trigger and test it
Create a trigger called employee_audit_trigger on employees that fires AFTER INSERT OR UPDATE OR DELETE and calls log_employee_changes. Then insert a new employee with name = 'Alice' and position = 'Engineer'. Finally, select all rows from audit_log to see the logged changes.
PostgreSQL
Hint
Use CREATE TRIGGER with AFTER INSERT OR UPDATE OR DELETE and FOR EACH ROW.
Insert a row into employees and then select from audit_log.
Practice
(1/5)
1. What is the main purpose of a trigger in PostgreSQL for audit logging?
easy
A. To backup the database periodically
B. To automatically record changes made to data in a table
C. To create new tables automatically
D. To speed up query execution
Solution
Step 1: Understand what triggers do
Triggers run code automatically when data changes occur in a table.
Step 2: Connect triggers to audit logging
Audit logging means recording who changed what and when, which triggers help automate.
Final Answer:
To automatically record changes made to data in a table -> Option B
Quick Check:
Trigger = automatic audit record [OK]
Hint: Triggers run code on data changes to log audits [OK]
Common Mistakes:
Thinking triggers speed up queries
Confusing triggers with backups
Assuming triggers create tables
2. Which of the following is the correct syntax to create a trigger function for audit logging in PostgreSQL?
easy
A. CREATE TRIGGER audit_log BEFORE INSERT ON audit_table EXECUTE FUNCTION log_changes();
B. CREATE FUNCTION audit_log() RETURNS void AS $$ BEGIN UPDATE audit_table SET changed = TRUE; END; $$ LANGUAGE sql;
C. CREATE FUNCTION audit_log() RETURNS trigger AS $$ BEGIN INSERT INTO audit_table VALUES (OLD.*); RETURN NEW; END; $$ LANGUAGE plpgsql;
D. CREATE FUNCTION audit_log() RETURNS trigger AS $$ BEGIN INSERT INTO audit_table VALUES (NEW.*); RETURN OLD; END; $$ LANGUAGE plpgsql;
Solution
Step 1: Check function return type and language
Trigger functions must return type 'trigger' and use 'plpgsql' language.
Step 2: Verify correct use of OLD and NEW
For audit logging on updates/deletes, OLD.* is used to capture previous data; function returns NEW to continue operation.
Final Answer:
CREATE FUNCTION audit_log() RETURNS trigger AS $$ BEGIN INSERT INTO audit_table VALUES (OLD.*); RETURN NEW; END; $$ LANGUAGE plpgsql; -> Option C
Quick Check:
Trigger function syntax = CREATE FUNCTION audit_log() RETURNS trigger AS $$ BEGIN INSERT INTO audit_table VALUES (OLD.*); RETURN NEW; END; $$ LANGUAGE plpgsql; [OK]
Hint: Trigger functions return 'trigger' and use plpgsql [OK]
Common Mistakes:
Using RETURNS void instead of RETURNS trigger
Returning OLD instead of NEW
Wrong language like SQL instead of plpgsql
3. Given this trigger function and trigger creation:
CREATE FUNCTION audit_func() RETURNS trigger AS $$ BEGIN INSERT INTO audit_log(user_name, action_time) VALUES (current_user, now()); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER audit_trigger AFTER INSERT ON employees FOR EACH ROW EXECUTE FUNCTION audit_func();
What happens when a new row is inserted into employees?
medium
A. A new row is added to audit_log with current user and timestamp
B. The insert into employees fails with an error
C. No action occurs because the trigger is AFTER INSERT
D. The employees row is deleted immediately
Solution
Step 1: Understand AFTER INSERT trigger behavior
AFTER INSERT triggers run after a new row is added, so the insert succeeds first.
Step 2: Analyze trigger function actions
The function inserts a row into audit_log with current user and timestamp, logging the event.
Final Answer:
A new row is added to audit_log with current user and timestamp -> Option A
Quick Check:
AFTER INSERT triggers log data after insert [OK]
Hint: AFTER INSERT triggers run after data is inserted [OK]
Common Mistakes:
Thinking AFTER INSERT prevents insert
Assuming trigger deletes data
Believing no action happens after insert
4. You wrote this trigger function:
CREATE FUNCTION audit_changes() RETURNS trigger AS $$ BEGIN INSERT INTO audit_log VALUES (NEW.*); RETURN NEW; END; $$ LANGUAGE plpgsql;
But when the trigger fires (e.g., on INSERT or UPDATE to the table), you get an error. What is the likely cause?
medium
A. Triggers cannot insert into tables
B. Trigger functions cannot use RETURN NEW
C. The function must be written in SQL, not plpgsql
D. The audit_log table does not match the NEW record structure
Solution
Step 1: Check compatibility of NEW.* with audit_log table
NEW.* expands to all columns of the triggering table, which must match audit_log columns exactly.
Step 2: Identify mismatch causes error
If audit_log has different columns or order, the insert fails when the trigger fires.
Final Answer:
The audit_log table does not match the NEW record structure -> Option D
Quick Check:
Column mismatch causes insert error [OK]
Hint: Ensure audit_log columns match NEW record exactly [OK]
Common Mistakes:
Thinking RETURN NEW is invalid
Assuming language must be SQL
Believing triggers cannot insert data
5. You want to create an audit log that records old and new values on UPDATE for a products table. Which trigger function code correctly captures both old and new data for audit logging?
hard
A. CREATE FUNCTION audit_update() RETURNS trigger AS $$ BEGIN INSERT INTO audit_log(old_name, new_name) VALUES (OLD.name, NEW.name); RETURN NEW; END; $$ LANGUAGE plpgsql;
B. CREATE FUNCTION audit_update() RETURNS trigger AS $$ BEGIN INSERT INTO audit_log(name) VALUES (NEW.name); RETURN OLD; END; $$ LANGUAGE plpgsql;
C. CREATE FUNCTION audit_update() RETURNS trigger AS $$ BEGIN INSERT INTO audit_log(old_name, new_name) VALUES (NEW.name, OLD.name); RETURN NEW; END; $$ LANGUAGE plpgsql;
D. CREATE FUNCTION audit_update() RETURNS trigger AS $$ BEGIN UPDATE audit_log SET name = NEW.name WHERE name = OLD.name; RETURN NEW; END; $$ LANGUAGE plpgsql;
Solution
Step 1: Identify correct use of OLD and NEW in UPDATE triggers
OLD contains previous row data, NEW contains updated data; audit log needs both.
Step 2: Check function logic and return value
Insert old and new names correctly, then return NEW to allow update to proceed.
Final Answer:
CREATE FUNCTION audit_update() RETURNS trigger AS $$ BEGIN INSERT INTO audit_log(old_name, new_name) VALUES (OLD.name, NEW.name); RETURN NEW; END; $$ LANGUAGE plpgsql; -> Option A
Quick Check:
OLD before, NEW after update [OK]
Hint: Use OLD for old data, NEW for new data in audit triggers [OK]