Trigger preventing insert - tsql

Is there any reason that having a trigger on a table would prevent the original insert statement from inserting? The trigger is run AFTER the row is inserted in the table, and there is no transaction rollback in the trigger.

It will happen when there is an exception in the trigger (when an error happens in a trigger the batch is aborted).

Related

Postgres concurrent transactions unexpected issue

When the following transaction is run concurrently on different connections it sometimes errors with
trigger "my_trigger" for relation "my_table" already exists
What am I doing wrong?
BEGIN;
DROP TRIGGER IF EXISTS my_trigger ON my_table;
CREATE TRIGGER my_trigger
AFTER INSERT ON my_table
REFERENCING NEW TABLE AS new_table
FOR EACH STATEMENT EXECUTE PROCEDURE my_function();
COMMIT;
I am trying to set up a system where I can add triggers to notify about data changes in specific tables. If a table already has such a trigger then skip it. Otherwise CREATE all CRUD triggers. This logic needs to run sequentially in case of concurrent requests.
After trying ISOLATION LEVEL SERIALIZABLE I noticed that any conflicting transactions are failed and dropped (I would need to manually check sql status and retry). But what I want is to queue up these transactions and run afterwards one by one in the order they're sent.
At the moment I am trying to achieve this by having a my_triggers (table_name TEXT) table that has a BEFORE INSERT OR DELETE trigger. Within this trigger I do the actual table trigger upsert logic. Inserts or deletes on my_triggers are made with LOCK TABLE my_triggers IN ACCESS EXCLUSIVE MODE ... which should queue up conflicting CRUD transactions ?!
What happens is following:
BEGIN....DROP TRIGGER IF EXISTS....CREATE TRIGGER....COMMIT;
..BEGIN....DROP TRIGGER IF EXISTS....CREATE TRIGGER--------EXCEPTION.
Both transactions starts when trigger is not present.
Both succeed in drop trigger because of "IF EXISTS" statement.
First transaction starts creating a trigger. For that a SHARE ROW EXCLUSIVE lock is placed on table my_table. The lock SHARE ROW EXCLUSIVE conflicts with it self so no other transaction is allowed to create a trigger until the first one completes.
Second transaction blocks on CREATE TRIGGER.
First transaction completes.
Second transaction proceeds with CREATE TRIGGER but it already exists. Exception is raised.
What you need is adding a LOCK before DROP TRIGGER statement. This way you will ensure the trigger is dropped and not created in concurrent transaction.
BEGIN;
LOCK TABLE my_table IN SHARE ROW EXCLUSIVE MODE ;
DROP TRIGGER IF EXISTS my_trigger ON my_table;
CREATE TRIGGER my_trigger
AFTER INSERT ON my_table
REFERENCING NEW TABLE AS new_table
FOR EACH STATEMENT EXECUTE PROCEDURE my_function();
COMMIT;

Field is not populated by trigger AFTER INSERT

I have created trigger on AFTER INSERT:
trigger in PostgreSQL
The trigger function:
trigger function
When I try to insert record to db the field heroku_id__c is not populated:
record in db
What am I doing wrong?
Assigning to the NEW record only makes sense in a BEFORE trigger. At the time an AFTER trigger runs, the row has already been written, and nothing happens any more with the NEW record.
But do not use RETURN NULL in the BEFORE trigger unless you want to cancel the INSERT.

trying to automatically timestamp postgresql table, ERROR: record "new" is not assigned yet [duplicate]

I have a pretty simple trigger:
CREATE OR REPLACE FUNCTION f_log_datei()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO logs (aktion, tabelle, benutzer_id) VALUES(TG_OP, 'dateien', NEW.benutzer_id);
END; $$ LANGUAGE 'plpgsql';
CREATE TRIGGER log_datei AFTER INSERT OR UPDATE OR DELETE
ON dateien
FOR EACH STATEMENT
EXECUTE PROCEDURE f_log_datei();
My table logs is the following:
CREATE TABLE logs(
id int PRIMARY KEY DEFAULT NEXTVAL('logs_id_seq'),
zeit timestamp DEFAULT now(),
aktion char(6),
tabelle varchar(32),
alt varchar(256),
neu varchar(256),
benutzer_id int references benutzer(id)
);
After inserting something in dateien I get the following error:
ERROR: record "new" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT: SQL statement "INSERT INTO logs (aktion, tabelle, benutzer_id) VALUES(TG_OP, 'dateien', NEW.benutzer_id)"
PL/pgSQL function "f_log_datei" line 3 at SQL statement
Why did I get this error? I looked into the documentation and it seems they use new in the same way I do.
From the fine manual:
36.1. Overview of Trigger Behavior
[...]
For a row-level trigger, the input data also includes the NEW row for INSERT and UPDATE triggers, and/or the OLD row for UPDATE and DELETE triggers. Statement-level triggers do not currently have any way to examine the individual row(s) modified by the statement.
And from Trigger Procedures:
NEW
Data type RECORD; variable holding the new database row for INSERT/UPDATE operations in row-level triggers. This variable is NULL in statement-level triggers and for DELETE operations.
Note what it says about row-level triggers and statement-level triggers.
You have a statement-level trigger:
...
FOR EACH STATEMENT
EXECUTE PROCEDURE f_log_datei();
Statement-level triggers are triggered once per statement and a statement can apply to multiple rows so the notion of affected row (which is what NEW and OLD are about) simply doesn't apply.
If you want to use NEW (or OLD) in a trigger then you want the trigger to execute for each affected row and that means you want a row-level trigger:
CREATE TRIGGER log_datei AFTER INSERT OR UPDATE OR DELETE
ON dateien
FOR EACH ROW
EXECUTE PROCEDURE f_log_datei();
I just changed FOR EACH STATEMENT to FOR EACH ROW.
Your trigger should also be returning something:
A trigger function must return either NULL or a record/row value having exactly the structure of the table the trigger was fired for.
[...]
The return value of a row-level trigger fired AFTER or a statement-level trigger fired BEFORE or AFTER is always ignored; it might as well be null. However, any of these types of triggers might still abort the entire operation by raising an error.
So you should RETURN NEW; or RETURN NULL; in your trigger. You have an AFTER trigger so it doesn't matter which RETURN you use but I'd go with RETURN NEW;.

INSERT statement that does not fire an INSERT trigger

I am using PostgreSQL 9.2 and I need to write an INSERT statement which copies data from table A to table B without firing the INSERT trigger defined on table B (maybe some sort of bulk insertion operation??).
On this specific table (table B) many INSERT, UPDATE and DELETE operations are executed. During each and every one of this executions, a trigger must fire.
I cannot temporary disable the triggers because of standard, day-to-day DML operations.
Can anyone help me with the syntax for this non-trigger-firing INSERT statement?
Run your "privileged" inserts as a different user. That way your trigger can check the current user and exit if it shouldn't do anything.

how to stop/fire trigger in stored procedure dynamically?

Suppose I have a table MyTab in database. I have a trigger, for example, delete trigger on this table.
Then in a stored procedure, I try to delete data from this table but want to stop the delete trigger only for this deletion. After that, put the trigger back on normal. Is it possible to have codes in stored procedure like:
stop trigger on MyTab;
delete from MyTab where ...;
put the trigger back;
As mentioned here, you can disable and enable the trigger, though I'd probably put this in a try-catch and/or transaction so you don't get stuck with your trigger disabled because of an error.
For example:
set xact_abort on; -- Auto-rollback on any error
begin transaction;
alter table MyTab disable trigger TR_MyTab_Delete;
delete from MyTab where 1/0 = 1; -- Causes div by zero error
alter table MyTab enable trigger TR_MyTab_Delete; -- Won't run becuase of above error
commit;
The script above will throw an error but won't leave the trigger disabled, as set xact_abort on guarantees that my transaction is rolled back, including the disabling of the trigger.