Triggers
PL/Rust functions can be used to define trigger functions on data changes.
A trigger function is created using the CREATE FUNCTION
command, declaring it as a function with no arguments and a return type of
trigger
. Trigger variables are available from in trigger
to describe the condition that triggered the call and the new
and old
rows.
PL/Rust trigger support options are documented on docs.rs and defined in the .rs
files in the
trigger_support directory.
These examples are an expansion of the code from plrust/plrust/src/tests.rs
. The elaborations here
illustrate additional functionality.
Table for Triggers
Create the plrust.dog
table to allow us to keep track of our dogs, and how much attention
they have received via a count of scritches
.
CREATE TABLE plrust.dog (
id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT,
scritches INT NOT NULL DEFAULT 0,
last_scritch TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
The name
column in plrust.dog
is the only column without a default
value set. The scritches
and last_scritch
column both have defaults set.
The goal of this design is to only have to define the name
during INSERT
.
Subsequent UPDATE
queries should only have to update the
last_scritch
column.
Trigger example
The following example creates a trigger function named plrust.dog_trigger()
.
The trigger will be used on INSERT
and UPDATE
with slightly different
behavior depending on which operation is being used. This logic is based
on the value of trigger.op()?
, for INSERT
the trigger.new
object is used,
for UPDATE
the trigger.old
object is used.
This code is explained further after the code block.
CREATE OR REPLACE FUNCTION plrust.dog_trigger()
RETURNS trigger AS
$$
let tg_op = trigger.op()?;
let my_row = match tg_op {
INSERT => trigger.new().unwrap(),
_ => trigger.old().unwrap()
};
let mut my_row = my_row.into_owned();
let counter_field = "scritches";
match my_row.get_by_name::<i32>(counter_field)? {
Some(val) => my_row.set_by_name(counter_field, val + 1)?,
None => (),
}
Ok(Some(my_row))
$$
LANGUAGE plrust;
CREATE TRIGGER dog_trigger
BEFORE INSERT OR UPDATE ON plrust.dog
FOR EACH ROW
EXECUTE FUNCTION plrust.dog_trigger();
The tg_op
variable is available from the trigger.op()
method and has values
of INSERT
, UPDATE
, DELETE
and TRUNCATE
. See the definition
of PgTriggerOperation
for more.
The tg_op
value is used in a match
to define the my_row
variable.
#![allow(unused)] fn main() { let tg_op = trigger.op()?; let my_row = match tg_op { INSERT => trigger.new().unwrap(), _ => trigger.old().unwrap() }; let mut my_row = my_row.into_owned(); }
With the appropriate my_row
identified, the next step is to increment the
scritches
column by 1. This is defined in the counter_field
variable
for easy reuse. The get_by_name
and set_by_name
functions are used for
this operation.
#![allow(unused)] fn main() { let counter_field = "scritches"; match my_row.get_by_name::<i32>(counter_field)? { Some(val) => my_row.set_by_name(counter_field, val + 1)?, None => (), } }
Finally, the my_row
is returned for the operation to proceed.
#![allow(unused)] fn main() { Ok(Some(my_row)) }
Next we INSERT
a row and then query the table to observe the effects of the trigger.
INSERT INTO plrust.dog (name) VALUES ('Nami');
SELECT * FROM plrust.dog;
The results show that while the DEFAULT
value for the scritches
column is
defined as 0
in the table, the initial value is 1 because trigger updated
the value.
id | name | scritches | last_scritch
----+------+-----------+-------------------------------
1 | Nami | 1 | 2023-03-04 17:30:43.601525+00
If we update the record for Nami by setting the last_scritch
value to NOW()
the trigger will increment the scritches
column value for us.
UPDATE plrust.dog
SET last_scritch = NOW()
WHERE id = 1;
SELECT * FROM plrust.dog;
id | name | scritches | last_scritch
----+------+-----------+-------------------------------
1 | Nami | 2 | 2023-03-04 17:35:05.320482+00
Not yet supported
Event Triggers and DO
blocks are not (yet) supported by PL/Rust.