Trigger to reshape phonenumber - postgresql

I want a trigger is my database which changes the first zero of a phone number to the appropriate country-code (in my case it's +31). My code so far is:
create or replace function correct_number()
returns trigger as $$
declare
Pnumber integer;
begin
select substring(phone, 1, 1) from "user" where phone = new.phone into Pnumber;
if Pnumber = 0 then
update "user"
set phone = overlay("user".phone placing '+31' from 1 for 1)
where id = new.id;
return null;
end if;
end
$$ language plpgsql;
create trigger force_countrynr
before insert on "user"
for each statement execute procedure correct_number();
There problem is that every time I try to add data, my DBMS gives an error that the end of the procedure is reached without a return. This means obviously that it doesn't consider the if-statement to be true, but I can't figure out why.
This is the DDL of the attribute that my function concerns is a varchar(13) and the my testquery is as follows:
insert into gebruiker
values('U5', 'Hans', '12345', 'Hans', null, 'Kraay', 'Hansworst', '0000QW', 'beukenweg', '4', null, '0612345678', 'test#notnow.nl', '2015-12-12');
I've tried changing the datatype of the zero in my if-statement, changing the datatype of the number-variable and many things more, but I can't get it to work.

You want to correct the number in each inserted row, so the trigger should be for each row.
There is no reason to query the table in the trigger, as the currently inserted row is available in the new variable.
There is no reason to update the table, just modify the new record and return it.
create or replace function correct_number()
returns trigger as $$
begin
if left(new.phone, 1) = '0' then
new.phone:= '+31' || right(new.phone, -1);
end if;
return new;
end
$$ language plpgsql;
create trigger force_countrynr
before insert on "user"
for each row execute procedure correct_number();

Related

Fill field based on column other table

I have a really simple problem and I am probably overthinking this way too much. But here it goes:
I want the fields of a column in one of my tables to be filled automatically whenever I make a new record. The value should be the same (UUID) as the specified (UUID) value from a column in another table. These two columns are joined via a foreign key. So far I have tried making a trigger function but with no results so far:
Create or replace function project_id()
returns trigger
as $$ begin
if new.project_id is null then
insert into sporen (project_id)
select project_id
from project_info
where project_code = 'ant0001';
end if;
return new;
end;
$$ language plpgsql;
CREATE TRIGGER
project_id_default
BEFORE update ON
sporen
FOR EACH ROW EXECUTE PROCEDURE project_id();
Do I need to specify something as a default in my table? Or am I going about it completely wrong?
You only need to assign project_info.project_id to NEW.project_id in your trigger function. No INSERT is needed. Here is an illustration.
Create or replace function project_id() returns trigger as
$$
begin
if new.project_id is null then
new.project_id :=
(
select pi.project_id
from project_info pi
where pi.project_code = NEW.project_code
);
end if;
return new;
end;
$$ language plpgsql;
You do not need to specify a default value for project_id in your table.

How to use json string element in a trigger function without declaring it as a variable

I'm learning about Postgresql - I don't have a specific application in mind, but the general idea is to update one column with json and not worry about the rest.
I have a table with a few columns (id, name, amount, json). I would like to update only the json column and have a trigger update the other columns from the json.
For example, if json was set to {"name" = "fred", "amount" = 100}, the trigger would populate the name and amount columns
so far, this works:
create table one(id serial primary key, name text, amount int, data jsonb);
CREATE OR REPLACE FUNCTION test()
RETURNS trigger AS
$$
DECLARE
json_name text = (data -> 'name') from one;
json_amount int = (data -> 'amount') from one;
BEGIN
IF json_name IS DISTINCT FROM OLD.name THEN
UPDATE one SET name = json_name;
END IF;
IF json_amount IS DISTINCT FROM OLD.amount THEN
UPDATE one SET amount = json_amount;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
CREATE TRIGGER test_trigger
AFTER INSERT OR UPDATE
OF data ON one
FOR EACH ROW
EXECUTE PROCEDURE test();
I'm trying to do this without declaring any variables. for example, I've tried these:
IF one.data->'name' IS DISTINCT FROM OLD.name THEN
or
IF one.data->'name'::text IS DISTINCT FROM OLD.name THEN
or
IF ((data->'name') from one) IS DISTINCT FROM OLD.name THEN
but none work.
Making such a trigger may be a horrible idea, and that would be nice to know, but I'm mainly interested figuring out this json stuff :)
I've made an example here:
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=92f2e6dd3630c76178ca4cfe4dc30b10
Make it a before trigger and instead of an UPDATE manipulate new.
CREATE OR REPLACE FUNCTION test()
RETURNS trigger
AS
$$
BEGIN
new.name := new.data->'name';
new.amount := new.data->'amount';
RETURN new;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER test_trigger
BEFORE INSERT
OR UPDATE
OF data
ON one
FOR EACH ROW
EXECUTE PROCEDURE test();
Although the answer provided by #sticky bit goes into the right direction, it doesn't handle type conversion well.
Here's a version that is better at dealing with empty JSON strings:
CREATE OR REPLACE FUNCTION test()
RETURNS trigger AS
$$
BEGIN
NEW.name := (NEW.data->>'name');
NEW.amount := round((NEW.data->>'amount')::numeric)::int;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
CREATE TRIGGER test_trigger
BEFORE INSERT OR UPDATE
OF data ON one
FOR EACH ROW
EXECUTE PROCEDURE test();
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=15a232d4092e23c7bc0425b849b31976
Please remember that executing multiple UPDATEs inside a trigger function after an original INSERT or UPDATE is going to kill performance.

PSQL Add value from row to another value in the same row using triggers

I have a test table with three columns (file, qty, qty_total). I will input multiple rows like this for example, insert into test_table (file,qty) VALUS (A,5);. What i want is for on commit is for a trigger to take the value from qty and add it to qty_total. As what will happen is that this value will get updated as this example demonstrates. Update test_table set qty = 10 where file = A; So the qty_total is now 15. Thanks
Managed to solve this myself. I created a trigger function `CREATE FUNCTION public.qty_total()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
IF TG_OP = 'UPDATE' THEN
NEW."total" := (OLD.total + NEW.col2);
RETURN NEW;
ELSE
NEW."total" := NEW.col2;
RETURN NEW;
END IF;
END;
$BODY$;
ALTER FUNCTION public.qty_total()
OWNER TO postgres; This was called by a trigger CREATE TRIGGER qty_trigger
BEFORE INSERT OR UPDATE
ON public.test
FOR EACH ROW
EXECUTE PROCEDURE qty_total(); now when i insert a new code and value, the value is copied to the total, when it is updated, the value is added to the total and i have my new qty_total. This may not have the best error catching in it, but since i am passing the data from php, i am happy to make sure the errors are caught and removed.

ERROR: unterminated dollar-quoted string at or near "$BODY$

I'm using sql fiddle...PostgreSQL 9.3:
CREATE TABLE HotelStays
(roomNum INTEGER NOT NULL,
arrDate DATE NOT NULL,
depDate DATE NOT NULL,
guestName CHAR(30) NOT NULL,
PRIMARY KEY (roomNum, arrDate))
;
CREATE OR REPLACE FUNCTION new_customer() RETURNS void AS
$BODY$
DECLARE
depatureDate DATE;\
BEGIN
SELECT depDate INTO depatureDate FROM HotelStays WHERE OLD.roomNum = NEW.roomNum;
IF (depatureDate <= NEW,arrDate)
INSERT INTO HotelStays (roomNum, arrDate, depDate, guestName)
VALUES (:NEW.roomNum, :NEW.arrDate, :NEW.depDate, :NEW.guestName);
END IF;
RETURN;
END
$BODY$
LANGUAGE 'plpgsql' ;
CREATE TRIGGER;
INSERT INTO HotelStays(roomNum, arrDate, depDate, guestName)
VALUES
(123, to_date('20160202', 'YYYYMMDD'), to_date('20160206','YYYYMMDD'), 'A');
Problem I am trying to solve: a new entry (for a new guest) could be put in for a room number, even before the existing guest has checked out.
I'm trying to solve this question using triggers. Please help me out. Thanks in advance.
As author mentioned, he used SQL Fiddle. I had the same problem with db-fiddle.com and resolved it by replacing $$ or $BODY$ with single quotes ' (and doubling single quotes elsewhere in between.
For example, db-fiddle
CREATE OR REPLACE FUNCTION update_datem()
RETURNS trigger AS
'
BEGIN
NEW.dateM = DATE_TRUNC(''MONTH'', NEW.date);
RETURN NEW;
END;
'
LANGUAGE plpgsql;
There are several errors in your code. First the backslash in depatureDate DATE;\. You are also missing a THEN for the IF clause and new does not need a : in front of it. You also have a , instead of a . in NEW,arrDate. And the final END is missing a ;.
Not an error, but the language name is an identifier, do not put it in single quotes.
The line CREATE TRIGGER; is also wrong. If you want to create trigger your function also needs to be declared as returns trigger and has to return the new row if it is a "before" trigger. If you intend to use an after trigger you still need to return something from that.
I am not sure what the condition WHERE OLD.roomNum = NEW.roomNum; is supposed to select. If you want to get the room number of the changed row, just use new.depdate. The select .. into ... will fail if that query returns more then one row. You probably meant to use where roomnum = new.roomnum or something similar.
So the function should be something like this:
CREATE OR REPLACE FUNCTION new_customer()
RETURNS trigger
AS
$BODY$
DECLARE
depatureDate DATE;
BEGIN
SELECT depDate
INTO depatureDate
FROM HotelStays
WHERE roomNum = NEW.roomNum;
IF (depatureDate <= NEW.arrDate) THEN
INSERT INTO HotelStays (roomNum, arrDate, depDate, guestName)
VALUES (NEW.roomNum, nEW.arrDate, NEW.depDate, NEW.guestName);
END IF;
RETURN NEW; -- this is important for a trigger
END;
$BODY$
LANGUAGE plpgsql;
And the code to create the trigger would be something like this:
CREATE TRIGGER check_stays
before update or insert on hotelstays
execute procedure new_customer();

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();