Postgresql Trigger Not Firing - postgresql

I have a function and a trigger as below:
CREATE FUNCTION update() RETURNS TRIGGER AS $logupdate$
DECLARE
judge boolean;
BEGIN
judge := EXECUTE ('SELECT starttime,endtime,NEW.starttime,NEW.endtime FROM reserves WHERE bid = NEW.bid AND startdate = NEW.startdate AND (starttime,endtime) overlaps(NEW.starttime,NEW.endtime) IS NULL');
IF judge = f THEN RETURN false;
END IF;
RETURN NEW;
END;
$logupdate$ LANGUAGE plpgsql;
CREATE TRIGGER logupdate
before UPDATE ON reserves
FOR EACH ROW
EXECUTE PROCEDURE update();
The trigger should detect if the new input has overlapping with previous time, for example if there is an old time in my table [11:00 - 13:00], and a new input is [12:00 - 14:00] then the trigger should fire at it and stop insertion.
However, it doesn't work in my computer, what's wrong here?

Your trigger gets executed for every row. You don't need to query the tables. You already have access to the old and new rows.
CREATE FUNCTION update() RETURNS TRIGGER AS $logupdate$
BEGIN
IF (NEW.starttime, NEW.endtime) OVERLAPS (OLD.starttime, OLD.endtime) THEN
RETURN false;
END IF;
RETURN NEW;
END;
$logupdate$ LANGUAGE plpgsql;
However, this is super inefficient. You should consider using an exclusion constraint http://www.postgresql.org/docs/9.0/static/ddl-constraints.html
I'm pretty sure there's an exclusion constraint that will work with OVERLAPS

Related

Postgres Trigger is not firing. Syntax help is appreciated

I'm trying to mark a boolean column on a different table to true upon the insertion of a matching record.
Here's what I've got:
CREATE or replace FUNCTION mark_as_sold() RETURNS trigger AS
$BODY$
BEGIN
UPDATE listing
set listing.sold = true;
WHERE listing.id = NEW.listing_id;
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER update_child_after_update
AFTER INSERT
ON transaction
FOR EACH ROW
EXECUTE PROCEDURE mark_as_sold();
When I create a record on the transaction table, nothing happens. I'm stumped. Any help is greatly appreciated.
I figured it out. I was messing up my relations. Here's the proper code:
CREATE OR REPLACE FUNCTION mark_as_sold()
RETURNS TRIGGER
AS $$
BEGIN
UPDATE listing
set sold = true
WHERE listing.id = NEW.listing_id;
RETURN NEW;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER test_trigger BEFORE INSERT OR UPDATE
ON "transaction"
FOR EACH ROW EXECUTE PROCEDURE mark_as_sold();

postgresql trigger error control reached end of trigger without RETURN

Edit: I have added the updated code to my post, I was missing a return null outside my IF block
I have a table working with the most recent data and would like to add a trigger for updates made to it so that I can record the changes made to a particular column in another table history. I run this code to create a trigger function and test it but get an error
SQL Error [2F005]: ERROR: control reached end of trigger procedure without RETURN
Where: PL/pgSQL function update_history()
My code:
-- create function for updates to track history
CREATE function update_history ()
RETURNS TRIGGER
as $$
BEGIN
IF NEW.discipline <> OLD.discipline THEN
INSERT INTO history
(ShiftId, fieldName, OldValue, NewValue)
VALUES(New.shift_id, 'discipline', OLD.discipline, NEW.discipline);
END IF;
return null; -- notice return outside the IF block
END;
$$
language plpgsql;
-- create trigger for my table after updates are made to the working table
create trigger update_history_trigger
after update on working
for each row execute PROCEDURE update_history();
Your function cannot reach the RETURN statement when the condition is not met. Place the statement outside the IF block. The returned value is ignored and you can use NULL instead of NEW.
CREATE FUNCTION update_history ()
RETURNS TRIGGER
AS $$
BEGIN
IF NEW.discipline <> OLD.discipline THEN
INSERT INTO history (ShiftId, fieldName, OldValue, NewValue)
VALUES(NEW.shift_id, 'discipline', OLD.discipline, NEW.discipline);
END IF;
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
Per the documentation:
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.

What should return trigger BEFORE DELETE

I am trying to make simple trigger function (Postgresql) but I am getting same error 'function did not return any row' in all these cases (just simple examples):
New:
UPDATE somewhere SET something = something - 1;
RETURN NEW;
Old:
UPDATE somewhere SET something = something - 1;
RETURN OLD;
What should I return when I call this function "before delete"? ("after insert/update" works well)
Tyvm for tips!
Full code as requested:
Function:
CREATE OR REPLACE FUNCTION pictogram_frequency_on_delete()
RETURNS trigger AS
$BODY$
DECLARE
new_frequency RECORD;
target_unit RECORD;
current_row RECORD;
units_with_same_type RECORD;
what RECORD;
BEGIN
SET search_path TO 'myScheme';
CASE TG_OP
WHEN 'DELETED' THEN what := OLD;
ELSE what:= OLD;
END CASE;
SELECT unit_type_uid INTO STRICT target_unit
FROM unit
WHERE unit_uid = what.unit_uid;
SELECT count(*) AS exists INTO STRICT current_row
FROM unit_type_pictogram utp
WHERE utp.pictogram_uid = what.pictogram_uid
AND utp.unit_type_uid = target_unit.unit_type_uid;
IF (current_row.exists = 0) THEN
RETURN what; /* return new/old doesnt work too */
END IF;
UPDATE unit_type_pictogram utp
SET frequency = frequency - 1
WHERE utp.pictogram_uid = what.pictogram_uid
AND utp.unit_type_uid = target_unit.unit_type_uid;
RETURN what; /* return new/old doesnt work too */
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Trigger:
CREATE TRIGGER on_delete_frequency
BEFORE DELETE
ON unit_pictogram
FOR EACH ROW
EXECUTE PROCEDURE pictogram_frequency_on_delete();
From documentation:
Trigger functions invoked by per-statement triggers should always
return NULL. Trigger functions invoked by per-row triggers can return
a table row (a value of type HeapTuple) to the calling executor, if
they choose. A row-level trigger fired before an operation has the
following choices:
It can return NULL to skip the operation for the current row. This instructs the executor to not perform the row-level operation that
invoked the trigger (the insertion, modification, or deletion of a
particular table row).
For row-level INSERT and UPDATE triggers only, the returned row becomes the row that will be inserted or will replace the row being
updated. This allows the trigger function to modify the row being
inserted or updated.
A row-level BEFORE trigger that does not intend to cause either of
these behaviors must be careful to return as its result the same row
that was passed in (that is, the NEW row for INSERT and UPDATE
triggers, the OLD row for DELETE triggers).
EDIT
Try something like this:
CREATE OR REPLACE FUNCTION pictogram_frequency_on_delete()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE unit_type_pictogram AS utp
SET frequency = frequency - 1
FROM unit
WHERE utp.pictogram_uid = OLD.pictogram_uid
AND unit_uid = OLD.unit_uid
AND utp.unit_type_uid = unit.unit_type_uid;
RETURN OLD;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
You should RETURN OLD;.
Your function must be defined as RETURNS trigger.

PostgreSQL how to write trigger

Say I have a following table like this:
http://i.stack.imgur.com/qU3gh.png
where sid stands for sailor ID, and bid stands for boat ID.
I want to write a trigger and a function to detect whether the new input is invalid. Look at the table, the 3rd record shouldn't be there because the renting time has overlap with the former one, one boat can't be rented before it is returned. That is to say, I want a trigger and a function to stop input like the 3rd one, when someone tries to give a 3rd input, it will just stop you from doing so.
Currently I wrote the following code, but as I am new to this, I am really not sure whether it is correct:
CREATE FUNCTION update() RETURNS TRIGGER AS $logupdate$
DECLARE
judge boolean;
BEGIN
judge := EXECUTE ( 'SELECT starttime,endtime,NEW.starttime,NEW.endtime FROM reserves WHERE bid = NEW.bid AND startdate = NEW.startdate AND (starttime,endtime) overlaps(NEW.starttime,NEW.endtime) IS NULL');
IF judge = f THEN RAISE EXCEPTION 'failed due to some reasons';
END IF;
RETURN NEW;
END;
$logupdate$ LANGUAGE plpgsql;
CREATE TRIGGER logupdate
before UPDATE ON reserves
FOR EACH ROW
EXECUTE PROCEDURE update();
How can I correct this?
consider using a timestamp field instead of seperate date and time fields for simplicity. you can easily format a timestamp into either a date or time.
update and insert triggers must return NEW on success, or FALSE/NULL
in order to enforce a constraint with a trigger you must run a BEFORE UPDATE trigger, otherwise the row's already added.
CREATE TRIGGER logupdate
BEFORE UPDATE ON reserves
FOR EACH ROW EXECUTE PROCEDURE update();
CREATE FUNCTION update() RETURNS TRIGGER AS $$
BEGIN
minstart := EXECUTE ( 'SELECT MAX(endtime) FROM reserves WHERE bid = NEW.bid AND startdate = NEW.startdate' ) ;
IF minstart < NEW.starttime THEN RETURN FALSE;
RETURN NEW;
END
$$

Postgres trigger function

I need help in Postgres triggers.
I have table with 2 columns:
sold boolean;
id_shop int;
It stores if item is sold, or at which shop its located at.
I need a trigger, if I change the "sold" to true, then it also changes the id_shop to NULL (It can't be in shop if sold...)
I tried different ways, but it doesn't work or gives an error on update cmd...
create or replace function pardota_masina_veikals() RETURNS trigger AS $pardota_masina$
begin
IF NEW.sold=true THEN
update masinas
SET id_shop=null WHERE id=NEW.id;
END IF;
RETURN NEW;
END;
$pardota_masina$ LANGUAGE plpgsql;
CREATE TRIGGER pardota_masina_nevar_but_veikala
AFTER INSERT OR UPDATE ON masinas FOR EACH ROW EXECUTE PROCEDURE pardota_masina_veikals();
First of all you need a before trigger if you want to change a value of the row being updated (or inserted)
Secondly you don't need to "update" the table, just assign the new value to the NEW row:
create or replace function pardota_masina_veikals()
RETURNS trigger
AS
$pardota_masina$
begin
IF NEW.sold=true THEN
NEW.id_shop = NULL;
END IF;
RETURN NEW;
END;
$pardota_masina$
LANGUAGE plpgsql;
CREATE TRIGGER pardota_masina_nevar_but_veikala
BEFORE INSERT OR UPDATE ON masinas
FOR EACH ROW EXECUTE PROCEDURE pardota_masina_veikals();