Module pgx::trigger_support

source ·
Expand description

Support for writing Rust trigger functions

A “no-op” trigger that gets the current [PgHeapTuple][crate::PgHeapTuple], panicking (into a PostgreSQL error) if it doesn’t exist:

use pgx::prelude::*;

#[pg_trigger]
fn trigger_example<'a>(trigger: &'a PgTrigger<'a>) -> Result<
    Option<PgHeapTuple<'a, impl WhoAllocated>>,
    PgHeapTupleError,
> {
    Ok(Some(trigger.old().expect("No current HeapTuple")))
}

Trigger functions only accept one argument, a [PgTrigger], and they return a Result containing either a [PgHeapTuple][crate::PgHeapTuple] or any error that implements impl std::error::Error.

Use from SQL

The trigger_example example above would generate something like the following SQL:

-- pgx-examples/triggers/src/lib.rs:25
-- triggers::trigger_example
CREATE FUNCTION "trigger_example"()
    RETURNS TRIGGER
    LANGUAGE c
    AS 'MODULE_PATHNAME', 'trigger_example_wrapper';

Users could then use it like so:

CREATE TABLE test (
    id serial8 NOT NULL PRIMARY KEY,
    title varchar(50),
    description text,
    payload jsonb
);

CREATE TRIGGER test_trigger
    BEFORE INSERT ON test
    FOR EACH ROW
    EXECUTE PROCEDURE trigger_example();

INSERT INTO test (title, description, payload)
    VALUES ('Fox', 'a description', '{"key": "value"}');

This can also be done via the extension_sql attribute:

pgx::extension_sql!(
    r#"
CREATE TABLE test (
    id serial8 NOT NULL PRIMARY KEY,
    title varchar(50),
    description text,
    payload jsonb
);
*
CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE trigger_example();
INSERT INTO test (title, description, payload) VALUES ('Fox', 'a description', '{"key": "value"}');
"#,
    name = "create_trigger",
    requires = [ trigger_example ]
);

Working with WhoAllocated

Trigger functions can return [PgHeapTuple][crate::PgHeapTuple]s which are AllocatedByRust or AllocatedByPostgres. In most cases, it can be inferred by the compiler using impl WhoAllocated<pg_sys::HeapTupleData>>.

When it can’t, the function definition permits for it to be specified:

use pgx::prelude::*;

#[pg_trigger]
fn example_allocated_by_rust<'a>(trigger: &'a PgTrigger<'a>) -> Result<
    Option<PgHeapTuple<'a, AllocatedByRust>>,
    PgHeapTupleError,
> {
    let current = trigger.old().expect("No current HeapTuple");
    Ok(Some(current.into_owned()))
}

#[pg_trigger]
fn example_allocated_by_postgres<'a>(trigger: &'a PgTrigger<'a>) -> Result<
    Option<PgHeapTuple<'a, AllocatedByPostgres>>,
    PgHeapTupleError,
> {
    let current = trigger.old().expect("No current HeapTuple");
    Ok(Some(current))
}

Error Handling

Trigger functions can return any impl std::error::Error. Returned errors become PostgreSQL errors.

use pgx::prelude::*;

#[derive(thiserror::Error, Debug)]
enum CustomTriggerError {
    #[error("No current HeapTuple")]
    NoCurrentHeapTuple,
    #[error("pgx::PgHeapTupleError: {0}")]
    PgHeapTuple(PgHeapTupleError),
}

#[pg_trigger]
fn example_custom_error<'a>(trigger: &'a PgTrigger<'a>) -> Result<
    Option<PgHeapTuple<'a, impl WhoAllocated>>,
    CustomTriggerError,
> {
    trigger.old().map(|t| Some(t)).ok_or(CustomTriggerError::NoCurrentHeapTuple)
}

Lifetimes

Triggers are free to use lifetimes to hone their code, the generated wrapper is as generous as possible.

use pgx::prelude::*;

#[derive(thiserror::Error, Debug)]
enum CustomTriggerError<'a> {
    #[error("No current HeapTuple")]
    NoCurrentHeapTuple,
    #[error("pgx::PgHeapTupleError: {0}")]
    PgHeapTuple(PgHeapTupleError),
    #[error("A borrowed error variant: {0}")]
    SomeStr(&'a str),
}

#[pg_trigger]
fn example_lifetimes<'a, 'b>(trigger: &'a PgTrigger<'a>) -> Result<
    Option<PgHeapTuple<'a, AllocatedByRust>>,
    CustomTriggerError<'b>,
> {
    return Err(CustomTriggerError::SomeStr("Oopsie"))
}

Escape hatches

Unsafe pgx::pg_sys::FunctionCallInfo and pgx::pg_sys::TriggerData (include its contained pgx::pg_sys::Trigger) accessors are available..

Re-exports

  • pub use pg_trigger::PgTrigger;
  • pub use pg_trigger_error::PgTriggerError;
  • pub use pg_trigger_level::PgTriggerLevel;
  • pub use pg_trigger_option::PgTriggerOperation;
  • pub use pg_trigger_when::PgTriggerWhen;

Structs

Enums

  • Indicates which trigger tuple to convert into a [crate::PgHeapTuple].

Functions