PostgreSQL - Trigger Each Statement - How to get Query statement - postgresql

I'm doing a trigger Each Statement and I would like to access the Query Statement that triggered the trigger. Is this possible ?
Something like this:
update table_1 where id> 300;
This query triggers an update event in table table_1 and this event triggers a function associated with the update trigger. This trigger is configured as Each Statement and inside the trigger I would like to do something like this:
raise info 'Query Statement that fired this trigger:%', last_query();
Would this be possible?
Thank you!
Julio

Call the function current_query().

Related

How to invoke a trigger on all rows manually in postgres

My trigger is defined the following way:
CREATE TRIGGER update_contract_finished_at
AFTER INSERT OR DELETE OR UPDATE OF performed_on
ON task
FOR EACH ROW
EXECUTE PROCEDURE update_contract_finished_at_function();
I now want to evoke this trigger to set the variables which are updated by the trigger. How do I do that?
Something like
for each row in task
execute procedure update_contract_finished_at_function();
I know I can update with a standard update set statement. I also want to verifiy that my trigger works on all the data correctly.
I'd write a slightly modified copy of update_contract_finished_at_function that takes type task as input and returns void.
Then replace NEW in the trigger function with $1 and call the function like this:
SELECT copy_func(task) FROM task;
If the functions are almost identical, it should be good enough to test the trigget function.
The way to manually trigger your on update trigger once would be:
UPDATE task SET performed_on = performed_on
however depending on how complicated your logic is in there and how many rows you have in the table a separate query might be significantly faster for initializing a large number of rows.
Since you mentioned you want to test the behaviour of your trigger you can clone the table or do a table or database dump and restore the data afterwards. If this is a live system you should instead do a database dump, restore to another system, add your trigger, test it, repeat from restore until you nail it... and only after you're sure it does what you want update the live system with it.
I ended up writing a PL/pgSQL function that in a loop processes all events in chronological order and calling it:
create or replace function process_event_history()
returns void
language plpgsql
as
$$
declare
event record;
begin
for event in
select id, timestamp
from events
order by timestamp
loop
update events set timestamp = event.timestamp
where id = event.id;
end loop;
end;
$$;
--;;
-- Execute the above function causing the trigger to run for all events.
select process_event_history();
--;;
-- Remove the temporary processing function.
drop function process_event_history();

How to create a trigger in PostgreSQL without delegating control to a function?

I'm familiar with the method where the trigger is fired by a function:
CREATE TRIGGER mytrigger
BEFORE UPDATE ON mytable
FOR EACH ROW EXECUTE PROCEDURE some_function();
I would like to know if there is a way to now eliminate the need for the function. Something like this doesn't seem to work:
CREATE TRIGGER mytrigger
BEFORE UPDATE ON mytable
FOR EACH ROW
BEGIN
SET NEW.col1 = OLD.col1 + 1
END
The error I keep getting is: ERROR: syntax error at or near "BEGIN"
If I get rid of BEGIN and END, it says: ERROR: syntax error at or near "SET"
Other variations fail as well. For example:
CREATE TRIGGER mytrigger
BEFORE UPDATE ON mytable
FOR EACH ROW
UPDATE mytable SET col1 = OLD.col1 + 1;
Can this be done now? I'm currently using v9.4.
You can't do it, it simply isn't supported. That's why it isn't working.
You must use trigger functions in PostgreSQL. You can't just write some SQL in-line in the trigger.

postgresql trigger statement syntax (when data is in other table.)

I am trying to learn to create a trigger statement in PGSql, but I am having issues of passing my data column for this.
So, normally, I have something like this:
CREATE TRIGGER tsv_gin_update BEFORE INSERT OR UPDATE
ON MySuperTable FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger(content_tsv_gin,'public.wtf',content)
Here, content and content_tsv_gin are simply columns on MySuperTable - and this works great.
However, what I'd like to have is the content comming from another table andnot MySuperTable. So, I have tried something like this:
CREATE TRIGGER tsv_gin_update BEFORE INSERT OR UPDATE
ON MySuperTable FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger(content_tsv_gin,'public.wtf',nt.content) FROM NewTable nt;
.. This does not work and throws me an error... So, I'd like to know how I can pass a data column from another table into the TRIGGER statement - if this is possible at all...

Checking if the trigger exists before executing a query

I want before executing the following query
alter trigger my_trigger disable
check if the trigger exists or not
How can I check if the trigger exits ? (RDBMS: oracle)
You could do something like this:
select * from sys.triggers where name = 'My_Trigger'
Querying Information_Schema would be ideal, but I'm not sure trigger information can be found there.

Is it possible to dynamically loop through a table's columns?

I have a trigger function for a table test which has the following code snippet:
IF TG_OP='UPDATE' THEN
IF OLD.locked > 0 AND
( OLD.org_id <> NEW.org_id OR
OLD.document_code <> NEW.document_code OR
-- other columns ...
)
THEN
RAISE EXCEPTION 'Message';
-- more code
So I am statically checking all the column's new value with its previous value to ensure integrity. Now every time my business logic changes and I have to add new columns into that table, I will have to modify this trigger each time. I thought it would be better if somehow I could dynamically check all the columns of that table, without explicitly typing their name.
How can it be done?
From 9.0 beta2 documentation about WHEN clause in triggers, which might be able to be used in earlier versions within the trigger body:
OLD.* IS DISTINCT FROM NEW.*
or possibly (from 8.2 release notes)
IF row(new.*) IS DISTINCT FROM row(old.*)
Take a look at the information_schema, there is a view "columns". Execute a query to get all current columnnames from the table that fired the trigger:
SELECT
column_name
FROM
information_schema.columns
WHERE
table_schema = TG_TABLE_SCHEMA
AND
table_name = TG_TABLE_NAME;
Loop through the result and there you go!
More information can be found in the fine manual.
In Postgres 9.0 or later add a WHEN clause to your trigger definition (CREATE TRIGGER statement):
CREATE TRIGGER foo
BEFORE UPDATE
FOR EACH ROW
WHEN (OLD IS DISTINCT FROM NEW) -- parentheses required!
EXECUTE PROCEDURE ...;
Only possible for triggers BEFORE / AFTER UPDATE, where both OLD and NEW are defined. You'd get an exception trying to use this WHEN clause with INSERT or DELETE triggers.
And radically simplify the trigger function accordingly:
...
IF OLD.locked > 0 THEN
RAISE EXCEPTION 'Message';
END IF;
...
No need to test IF TG_OP='UPDATE' ... since this trigger only works for UPDATE anyway.
Or move that condition in the WHEN clause, too:
CREATE TRIGGER foo
BEFORE UPDATE
FOR EACH ROW
WHEN (OLD.locked > 0
AND OLD IS DISTINCT FROM NEW)
EXECUTE PROCEDURE ...;
Leaving only an unconditional RAISE EXCEPTION in your trigger function, which is only called when needed to begin with.
Read the fine print:
In a BEFORE trigger, the WHEN condition is evaluated just before the
function is or would be executed, so using WHEN is not materially
different from testing the same condition at the beginning of the
trigger function. Note in particular that the NEW row seen by the
condition is the current value, as possibly modified by earlier
triggers. Also, a BEFORE trigger's WHEN condition is not allowed to
examine the system columns of the NEW row (such as oid), because those
won't have been set yet.
In an AFTER trigger, the WHEN condition is evaluated just after the
row update occurs, and it determines whether an event is queued to
fire the trigger at the end of statement. So when an AFTER trigger's
WHEN condition does not return true, it is not necessary to queue an
event nor to re-fetch the row at end of statement. This can result in
significant speedups in statements that modify many rows, if the
trigger only needs to be fired for a few of the rows.
Related:
Fire trigger on update of columnA or ColumnB or ColumnC
To also address the question title
Is it possible to dynamically loop through a table's columns?
Yes. Examples:
Handle result when dynamic SQL is in a loop
Removing all columns with given name
Iteration over RECORD variable inside trigger
Use pl/perl or pl/python. They are much better suited for such tasks. much better.
You can also install hstore-new, and use it's row->hstore semantics, but that's definitely not a good idea when using normal datatypes.