Row-level triggers help track changes in your database automatically. They keep a record of who changed what and when.
Row-level triggers for audit logs in Supabase
CREATE FUNCTION audit_log_function() RETURNS trigger AS $$ BEGIN -- your audit logic here RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER audit_log_trigger AFTER INSERT OR UPDATE OR DELETE ON your_table FOR EACH ROW EXECUTE FUNCTION audit_log_function();
The function runs for each row changed in the table.
Use NEW to access new row data, OLD for old row data.
CREATE FUNCTION audit_log() RETURNS trigger AS $$
BEGIN
INSERT INTO audit_table(table_name, operation, changed_at)
VALUES ('users', TG_OP, now());
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER users_audit
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION audit_log();CREATE FUNCTION audit_log() RETURNS trigger AS $$
BEGIN
INSERT INTO audit_table(table_name, operation, old_data, new_data, changed_at)
VALUES ('orders', TG_OP, row_to_json(OLD), row_to_json(NEW), now());
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER orders_audit
AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW EXECUTE FUNCTION audit_log();This setup creates an audit_logs table and a trigger on the employees table. It records inserts, updates, and deletes with old and new data as JSON.
CREATE TABLE audit_logs (
id SERIAL PRIMARY KEY,
table_name TEXT NOT NULL,
operation TEXT NOT NULL,
old_data JSONB,
new_data JSONB,
changed_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
CREATE FUNCTION audit_log_function() RETURNS trigger AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
INSERT INTO audit_logs(table_name, operation, old_data, changed_at)
VALUES (TG_TABLE_NAME, TG_OP, row_to_json(OLD), now());
RETURN OLD;
ELSE
INSERT INTO audit_logs(table_name, operation, new_data, changed_at)
VALUES (TG_TABLE_NAME, TG_OP, row_to_json(NEW), now());
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON employees
FOR EACH ROW EXECUTE FUNCTION audit_log_function();Make sure the audit table has enough columns to store the data you want to track.
Triggers can slow down writes slightly because they run on every change.
Test your trigger functions carefully to avoid errors that block data changes.
Row-level triggers automatically log changes to your database rows.
They help keep an audit trail for security and debugging.
Use OLD and NEW to access data before and after changes.
Practice
Solution
Step 1: Understand what row-level triggers do
Row-level triggers activate for each row affected by an insert, update, or delete operation.Step 2: Connect triggers to audit logs
They are used to record changes to rows, capturing who changed what and when.Final Answer:
To automatically record changes made to individual rows in a table -> Option AQuick Check:
Row-level trigger purpose = record row changes [OK]
- Thinking triggers speed up queries
- Confusing triggers with table creation
- Assuming triggers delete old data
users?Solution
Step 1: Identify trigger timing and event
The question asks for AFTER UPDATE trigger on tableusers.Step 2: Confirm row-level execution
Row-level triggers useFOR EACH ROW, notFOR EACH STATEMENT.Final Answer:
CREATE TRIGGER user_update AFTER UPDATE ON users FOR EACH ROW EXECUTE FUNCTION audit_log(); -> Option CQuick Check:
AFTER UPDATE + FOR EACH ROW = CREATE TRIGGER user_update AFTER UPDATE ON users FOR EACH ROW EXECUTE FUNCTION audit_log(); [OK]
- Using BEFORE instead of AFTER
- Using FOR EACH STATEMENT instead of FOR EACH ROW
- Using INSERT event instead of UPDATE
CREATE OR REPLACE FUNCTION audit_log() RETURNS trigger AS $$ BEGIN INSERT INTO audit_table(user_id, action, changed_at) VALUES (NEW.id, TG_OP, NOW()); RETURN NEW; END; $$ LANGUAGE plpgsql;
What will happen when a row in the
users table is updated?Solution
Step 1: Analyze the trigger function logic
The function inserts a row intoaudit_tablewithNEW.id, operation typeTG_OP, and current time.Step 2: Understand trigger behavior on update
On update,NEWholds the new row data, so the insert logs the change.Final Answer:
A new row is inserted intoaudit_tablewith the user's id, action type, and timestamp -> Option AQuick Check:
Trigger inserts audit record on update = A new row is inserted intoaudit_tablewith the user's id, action type, and timestamp [OK]
- Thinking OLD is required for insert
- Assuming trigger deletes rows
- Confusing syntax causing errors
CREATE OR REPLACE FUNCTION audit_log_delete() RETURNS trigger AS $$ BEGIN INSERT INTO audit_table(user_id, action, changed_at) VALUES (NEW.id, TG_OP, NOW()); RETURN OLD; END; $$ LANGUAGE plpgsql;
But it fails when deleting rows. What is the error?
Solution
Step 1: Understand DELETE trigger context
On DELETE,NEWis NULL because the row is removed;OLDholds the deleted row.Step 2: Identify the error in the function
The function usesNEW.idwhich is NULL on DELETE, causing failure.Final Answer:
Using NEW.id on DELETE triggers causes an error because NEW is NULL -> Option DQuick Check:
DELETE triggers use OLD, not NEW [OK]
- Using NEW in DELETE triggers
- Thinking RETURN OLD is invalid
- Believing TG_OP or NOW() cause errors
orders table, including old and new values. Which approach correctly combines row-level triggers and audit logging in Supabase?Solution
Step 1: Identify audit log needs
You want to log who updated rows and capture old and new values for changes.Step 2: Choose correct trigger timing and data
Use AFTER UPDATE trigger FOR EACH ROW to access OLD and NEW data and insert audit record.Step 3: Confirm audit table insert logic
The trigger function should insert both OLD and NEW values intoaudit_ordersfor full change history.Final Answer:
Create a trigger function that inserts intoaudit_ordersusing both OLD and NEW row data, then create AFTER UPDATE trigger FOR EACH ROW onorders-> Option BQuick Check:
Use AFTER UPDATE + OLD/NEW + FOR EACH ROW for full audit [OK]
- Logging only NEW data misses old values
- Using BEFORE INSERT triggers for updates
- Deleting rows instead of logging changes
Ready to prove it?
Pass the test and this pattern moves unknown
