Postgres trigger check amount before delete - sql

I am trying to create a postgres before trigger to check the amount of records that are going to be deleted before it actually does. For example to not delete more than 5 records

You could achieve that with an AFTER DELETE statement-level trigger. Inside the trigger function you can count the number of affected rows and throw an exception if the count is too high. The exception will force a rollback of the transaction that initiated the delete.
create function prevent_delete()
returns trigger
as
$BODY$
declare
l_count integer;
begin
select count(*)
into l_count
from old_table;
if l_count > 5 then
raise exception 'Too many rows would be deleted';
end if;
return null;
end;
$BODY$
LANGUAGE plpgsql;
And then create the trigger:
create trigger prevent_mass_delete
after delete on the_table
referencing old table as old_table
for each statement
execute procedure prevent_delete();

Related

Modify OLD to be returned by DELETE in postgresql trigger function

I have a trigger function in postgresql which will insert rows in the audit table on INSERT, UPDATE and DELETE operations. In my tables, there is a column called audit_id and I need to write the ID of inserted audit row in this field. This is my function
CREATE OR REPLACE FUNCTION my_audit_trigger()
RETURNS trigger LANGUAGE plpgsql
AS $function$
declare
audit_pk bigint;
begin
IF TG_OP = 'INSERT'
THEN
INSERT INTO audit.table_audit (rel_id, table_name, operation, after)
VALUES (TG_RELID, TG_TABLE_NAME, TG_OP, to_jsonb(NEW)) returning id into audit_pk;
NEW.audit_id := audit_pk;
RETURN NEW;
ELSIF TG_OP = 'UPDATE'
THEN
IF NEW != OLD THEN
INSERT INTO audit.table_audit (rel_id, table_name, operation, before, after)
VALUES (TG_RELID, TG_TABLE_NAME, TG_OP, to_jsonb(OLD), to_jsonb(NEW)) returning id into audit_pk;
END IF;
NEW.audit_id := audit_pk;
RETURN NEW;
ELSIF TG_OP = 'DELETE'
THEN
INSERT INTO audit.table_audit (rel_id, table_name, operation, before)
VALUES (TG_RELID, TG_TABLE_NAME, TG_OP, to_jsonb(OLD)) returning id into audit_pk;
OLD.audit_id := audit_pk;
RETURN OLD;
END IF;
end;
$function$;
As a result, when inserting or updating my table rows, I get back the audit id of the corresponding operation, but when I run DELETE command, I get back the audit ID of the previous operation, not of the DELETE itself. So I guess the problem is in OLD.audit_id := audit_pk;
More specifically, I run for example INSERT INTO table VALUES (this, that) RETURNING audit_id and I get back audit_id of the INSERT operation.
After, when running DELETE FROM table WHERE id = sth RETURNING audit_id I get audit_id of the INSERT operation, not of the DELETE.
Any help is appreciated, thank you.
P.S. This is how I create trigger
CREATE TRIGGER table_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON table
FOR EACH ROW
EXECUTE PROCEDURE my_audit_trigger();
I have similar problem. That seems that PG just does not support modification of OLD now, but, probably, this feature will be included into TODO list.
Currently you can modify only NEW for INSERT and UPDATE statements
For details look into this mail thread: Does 'instead of delete' trigger support modification of OLD

PostgreSQL trigger after update only on a updated row

I have a small table for news. I want to make a trigger which sets the update date and update time in the row (only for the rows that were updated)
I tried making the following:
CREATE FUNCTION add_update_dates()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF (OLD.news_name IS DISTINCT FROM NEW.news_name OR
OLD.news_description IS DISTINCT FROM NEW.news_description OR
OLD.news_text IS DISTINCT FROM NEW.news_text) THEN
UPDATE news SET news_update_date = current_date, news_update_time = current_time;
END IF;
RETURN new;
END
$$;
CREATE TRIGGER update_news_dates
AFTER UPDATE ON news
FOR EACH ROW
EXECUTE PROCEDURE add_update_dates();
But the trigger updates each row in my table (even those that are not updated), when I want only the updated ones. What am I doing wrong?
Your update statement is updating all the rows in the table! It has no where clause.
Just use assignment:
CREATE FUNCTION add_update_dates()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF (OLD.news_name IS DISTINCT FROM NEW.news_name OR
OLD.news_description IS DISTINCT FROM NEW.news_description OR
OLD.news_text IS DISTINCT FROM NEW.news_text
) THEN
NEW.news_update_date := current_date;
NEW.news_update_time := current_time;
END IF;
RETURN new;
END;
$$;
As an aside, storing date/time in separate columns makes no sense to me.

Postgresql delete record involving two tables and store this record in the third record

I'm trying to delete a record from the table 'student', where on a Cascade delete it will remove it from the 'entry' table. But before delete i need to store this record in the third table 'cancel'.
Here is what i worked out so far:
DELETE FROM "CMPS".student
WHERE sno = '1';
CREATE TRIGGER canceled BEFORE DELETE
ON entry
FOR EACH ROW
EXECUTE PROCEDURE trigger_backup_row
CREATE OR REPLACE FUNCTION trigger_backup_row(integer)
RETURNS trigger AS
$$
BEGIN
INSERT INTO cancel (eno, excode, sno) values (NEW.eno, NEW.excode, NEW.sno);
RETURN NEW;
END;
$$
language PLPGSQL
But comes back with an errors. Any help will be much appreciated.
I suppose you need:
CREATE OR REPLACE FUNCTION trigger_backup_row()
RETURNS trigger AS
$$
BEGIN
INSERT INTO cancel (eno, excode, sno) values (OLD.eno, OLD.excode, OLD.sno);
RETURN OLD;
END;
$$
language PLPGSQL
;
CREATE TRIGGER canceled BEFORE DELETE
ON entry
FOR EACH ROW
EXECUTE PROCEDURE trigger_backup_row()
;
trigger function do not use arguments
on delete yo udon't have any NEW row - just an OLD one

Create a trigger that prevents a deletion of tables with less than 20 rows

I need to create a trigger that will raise an error If someone tries to delete a Row in a table containing less than 20 rows. I am farely new to triggers, the following code is where I have come to a halt.
Create or Replace Trigger Lab16Trigger2
BEFORE Delete On employee_copy
WHEN (count(*) < 20)
Begin
RAISERROR('ORA-20101: At least 20 rows in employee_copy table');
End;
Assuming this is a homework assignment (the requirement doesn't generally make sense and a trigger won't work in a multi-user environment)
create or replace trigger trigger_name
after delete on table_name
declare
l_cnt pls_integer;
begin
select count(*)
into l_cnt
from table_name;
if( l_cnt < 20 )
then
raise_application_error( -20001, 'You must leave at least 20 rows in the table.' );
end if;
end;

PL/PgSQL creating a conditional trigger on insert

I'm working on my first ever Trigger. When I'm doing an INSERT on table I want to conditionaly remove rows from other table.
Here is a trigger:
CREATE OR REPLACE FUNCTION clear_seen_by()
RETURNS trigger AS
$BODY$
BEGIN
IF (OLD.popup = '1') THEN
DELETE FROM news_seen_by;
END IF;
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Invoked by:
CREATE TRIGGER clear_seen_by
AFTER INSERT
ON news
FOR EACH STATEMENT
EXECUTE PROCEDURE clear_seen_by();
As an error I see that NEW or OLD (if I motify the trigger) is not declared/unknown. Where is the problem?
In an INSERT statement you do not have an OLD record defined.
You should use NEW.popup instead, and also declare the trigger to be FOR EACH ROW.
CREATE OR REPLACE FUNCTION clear_seen_by() RETURNS trigger AS
$BODY$
BEGIN
IF (NEW.popup = '1') THEN
DELETE FROM news_seen_by;
END IF;
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
CREATE TRIGGER
clear_seen_by
AFTER INSERT ON
news
FOR EACH ROW EXECUTE PROCEDURE
clear_seen_by();
You declare a trigger FOR EACH STATEMENT. Maybe you need FOR EACH ROW?
FOR EACH STATEMENT triggers do not have NEW and OLD.