Procedures Triggers Functions
π₯ PostgreSQL Summary: Triggers, Functions, and Proceduresβ
Coming from an MS SQL (BI/DE) background, I instinctively default to stored procs for any non-relational logic. However, in PostgreSQL, functions and triggers are incredibly powerful and often a better fit. So hereβs a concise breakdown of triggers, functions, and procedures in PostgreSQL, including when to use each and permission considerations.
π₯ Summary Table: Triggers vs. Functions vs. Proceduresβ
Feature | Triggers | Functions | Procedures |
---|---|---|---|
Purpose | Auto-execute logic on table changes | Return a value from a query | Execute multi-step operations |
Called By | INSERT , UPDATE , DELETE events | SELECT function_name() | CALL procedure_name() |
Returns | Nothing | A value (or table) | Nothing |
Can Modify Data? | β Yes | β οΈ Yes (if SECURITY DEFINER ) | β Yes |
Transaction Control? | β No | β No | β
Yes (COMMIT / ROLLBACK ) |
Main Use Case | Auditing, enforcing constraints | Encapsulating queries | Batch operations, data migration |
Grant Permission | GRANT TRIGGER | GRANT EXECUTE ON FUNCTION | GRANT EXECUTE ON PROCEDURE |
π When to Use Each?β
β Use a TRIGGER
when you need automatic actions on table updates (e.g., logging changes).
β Use a FUNCTION
when you want to return values or encapsulate queries.
β Use a PROCEDURE
when you need multi-step changes inside a single transaction.
π 1. PostgreSQL Triggersβ
π What Are Triggers?β
- Triggers are automatic actions executed before or after
INSERT
,UPDATE
, orDELETE
operations on a table. - They enforce business logic, audit changes, or cascade updates.
π When to Use?β
β Automatically log changes (audit logs).
β Enforce constraints beyond standard foreign keys.
β Modify data before inserting/updating.
π Key Permissionsβ
Privilege | Effect |
---|---|
TRIGGER | Allows creating, modifying, and dropping triggers. |
Without TRIGGER | User can only execute existing triggers. |
π₯ Example: Create an Audit Log Triggerβ
CREATE TABLE user_audit (
id SERIAL PRIMARY KEY,
user_id UUID,
action TEXT,
changed_at TIMESTAMP DEFAULT now()
);
CREATE OR REPLACE FUNCTION log_user_changes() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO user_audit (user_id, action)
VALUES (NEW.user_id, TG_OP);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER user_trigger
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION log_user_changes();
β
Automatically logs inserts, updates, and deletes for users
.
π 2. PostgreSQL Functionsβ
π What Are Functions?β
- Functions return a value (
RETURNS something
) and are used for calculations, data transformations, and queries. - Functions run inside a transaction (safe for SELECT queries).
π When to Use?β
β Compute derived values (e.g., full_name(first_name, last_name)
).
β Encapsulate complex queries for reuse.
β Perform operations inside queries (e.g., formatting).
π Key Permissionsβ
Privilege | Effect |
---|---|
EXECUTE | Allows running the function. |
Without EXECUTE | The user cannot call the function. |
π₯ Example: Create a Functionβ
CREATE OR REPLACE FUNCTION get_active_users() RETURNS TABLE(user_id UUID, email TEXT) AS $$
BEGIN
RETURN QUERY SELECT user_id, primary_email FROM users WHERE is_active = TRUE;
END;
$$ LANGUAGE plpgsql;
β Can be used like a table:
SELECT * FROM get_active_users();
Grant Execute Permissionβ
GRANT EXECUTE ON FUNCTION get_active_users() TO app_user;
β
Now app_user
can call the function.
π 3. PostgreSQL Proceduresβ
π What Are Procedures?β
- Procedures (
CALL procedure_name()
) allow executing multiple SQL statements inside a single transaction. - Unlike functions, procedures can commit or roll back transactions.
π When to Use?β
β Execute multi-step database changes.
β Run batch operations.
β Perform transaction-safe updates.
π Key Permissionsβ
Privilege | Effect |
---|---|
EXECUTE | Allows calling the procedure. |
Without EXECUTE | The user cannot run it. |
π₯ Example: Create a Procedureβ
CREATE OR REPLACE PROCEDURE deactivate_users(days_old INT) AS $$
BEGIN
UPDATE users
SET is_active = FALSE
WHERE last_login < NOW() - INTERVAL '1 day' * days_old;
END;
$$ LANGUAGE plpgsql;
β To call it:
CALL deactivate_users(30);
Grant Execute Permissionβ
GRANT EXECUTE ON PROCEDURE deactivate_users(INT) TO app_user;
β
Now app_user
can deactivate old users.