How to create a trigger in PostgreSQL

Updated: January 5, 2024 By: Guest Contributor Post a comment

Introduction

Triggers in PostgreSQL are powerful tools that allow developers to define functions to be automatically executed before or after changes to the database. This tutorial will guide you through the creation of basic to advanced triggers in PostgreSQL.

Understanding Triggers

Before diving into creating a trigger, let’s define what triggers are and why they’re useful. A trigger is a database object tied to a table—it’s a piece of code that automatically runs in response to specific events on that table, such as INSERT, UPDATE, or DELETE operations. They can be used for auditing, enforcing business rules, maintaining complex integrity constraints, and more.

Creating a Basic Trigger

Let’s start by creating a simple trigger that logs each new row added to a table. First, we need a table:

CREATE TABLE user_audit (
    audit_id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    action TEXT NOT NULL,
    action_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Next, we define a trigger function:

CREATE OR REPLACE FUNCTION log_user_update()
RETURNS TRIGGER AS $
BEGIN
    INSERT INTO user_audit(user_id, action)
    VALUES (NEW.user_id, 'INSERT');
    RETURN NEW;
END;
$ LANGUAGE plpgsql;

Now, we create a trigger that calls this function on each insert:

CREATE TRIGGER user_update_trigger
AFTER INSERT ON users
FOR EACH ROW EXECUTE FUNCTION log_user_update();

Conditional Triggers

You can make triggers conditional based on given criteria. For example, you can define a trigger to run only when a certain column is updated:

CREATE TRIGGER user_update_age_trigger
AFTER UPDATE OF age ON users
FOR EACH ROW
WHEN (OLD.age != NEW.age)
EXECUTE FUNCTION log_user_update();

Modifying Data with Triggers

Triggers in PostgreSQL can also modify the data being inserted, updated, or deleted. Below is an example of a BEFORE trigger that automatically capitalizes names before they are inserted into the database:

CREATE OR REPLACE FUNCTION capitalize_names()
RETURNS TRIGGER AS $
BEGIN
    NEW.name := UPPER(NEW.name);
    RETURN NEW;
END;
$ LANGUAGE plpgsql;

CREATE TRIGGER before_insert_user_trigger
BEFORE INSERT ON users
FOR EACH ROW EXECUTE FUNCTION capitalize_names();

Partitioning Data with Triggers

Triggers can help with table partitioning by redirecting an insert to the correct partition:

CREATE OR REPLACE FUNCTION insert_partition_trigger()
RETURNS TRIGGER AS $
BEGIN
    IF (NEW.log_date >= '2021-01-01' AND NEW.log_date < '2022-01-01') THEN
        INSERT INTO logs_2021 VALUES (NEW.*);
    ELSIF (NEW.log_date >= '2022-01-01' AND NEW.log_date < '2023-01-01') THEN
        INSERT INTO logs_2022 VALUES (NEW.*);
    -- Add more partitions as necessary
    END IF;
    RETURN NULL;
END;
$ LANGUAGE plpgsql;

-- Trigger that redirects to the proper table partition
CREATE TRIGGER partition_insert_trigger
BEFORE INSERT ON logs
FOR EACH ROW EXECUTE FUNCTION insert_partition_trigger();

Advanced Trigger Concepts

Beyond basic usage, PostgreSQL triggers can involve more complex logic. This might include error handling with EXCEPTION blocks, handling transaction statements within a trigger, or leveraging session information to enforce row-level security.

Trigger Limitations and Performance Concerns

Keep in mind that triggers add overhead to transactions, which can affect performance, particularly when bulk loading data or when used on tables with very high write volumes. Unintended recursion can also occur if triggers cause other triggers to fire. Careful planning and understanding of the event trigger chain are essential.

Testing Your Triggers

Safety checks are an important part of trigger creation. After creating a trigger, you should thoroughly test it to ensure that it performs as expected and does not introduce any regressions or unwanted behavior.

Managing Triggers

It’s important to be able to manage your triggers. PostgreSQL offers several commands for this:

-- Disable a trigger
ALTER TABLE users DISABLE TRIGGER user_update_trigger;

-- Enable a trigger
ALTER TABLE users ENABLE TRIGGER user_update_trigger;

-- Drop a trigger
DROP TRIGGER IF EXISTS user_update_trigger ON users;

Conclusion

In this tutorial, we explored how to create triggers in PostgreSQL from simple loggers to complex partitioning schemes. Remember, triggers are powerful but must be used wisely to ensure they do not become a maintenance headache or a performance bottleneck. With the right practices, they can be an invaluable asset to your database arsenal.