Exception PostgreSQL - postgresql

I want to update a column topic in the table abonnement when id_abonnement=3. For this, I want to make an exception when id_abonnement=3 is not exist in database.
I'm trying something like that :
DO $$
BEGIN
BEGIN
update abonnement set topic = 'valeurTopic' where id_abonnement = 3;
EXCEPTION
WHEN "verification of id" THEN RAISE NOTICE 'id=3 not exist';
END;
END;
$$
Any solution please ?!

i find a solution. It's simply like that :
DO $$
BEGIN
BEGIN
IF EXISTS (SELECT * FROM abonnement WHERE id_abonnement=3)
THEN
UPDATE abonnement set topic= 'valeurTopic' WHERE id_abonnement = 3;
RAISE NOTICE 'good operation';
ELSE
RAISE NOTICE 'id = 3 not exist';
END IF;
END;
END;
$$

Related

Why does function i PostgreSQL not raise exception?

I have a before insert or update trigger which is supposed to validate values of two columns in the inserted/updated data, raise an exception if the data are not valid or - if valid - add values to two other columns in the insert/update.
However, when I test with invalid data I get "ERROR: query has no destination for result data. HINT: If you want to discard the results of a SELECT, user PERFORM instead. CONTEXT: PL/pgSQL function before_insert_update() line 3 at SQL statement."
I have tried to read the PostgreSQL documentation and I have googled for explanations, but I just have to admin that my understanding of SQL is not sufficient to make it work.
My function is
CREATE or replace FUNCTION before_insert_update()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
begin
select * from addresses
where addresses.roadname = NEW.roadname and
addresses.housenumber = NEW.housenumber;
if ##ROWCOUNT > 0 then
begin
select addresses.roadcode,
addresses.geom
into NEW.roadcode, NEW.geom
from addresses
where addresses.roadname = NEW.roadname and
addresses.housenumber = NEW.housenumber;
return NEW;
end;
else
declare _adresse text;
begin
_address := concat(NEW.roadname, ' ', NEW.housenumber);
raise exception 'The address does not exist: %', _address
using hint = 'Check the address here: https://danmarksadresser.dk/adresser-i-danmark/';
end;
end if;
END
$BODY$;
The trigger is pretty straight forward
BEFORE INSERT OR UPDATE
ON table
FOR EACH ROW
EXECUTE PROCEDURE before_insert_update();
What is it that I am doing wrong?
There is no need to run a test if the row is present. Just select it, and check the status afterwards:
CREATE or replace FUNCTION before_insert_update()
RETURNS trigger
LANGUAGE plpgsql
COST 100
VOLATILE NOT LEAKPROOF
AS
$BODY$
begin
select addresses.roadcode, addresses.geom
into NEW.roadcode, NEW.geom
from addresses
where addresses.roadname = NEW.roadname and
addresses.housenumber = NEW.housenumber;
if found then
return new;
end if;
raise exception 'The address does not exist: %', concat(NEW.roadname, ' ', NEW.housenumber)
using hint = 'Check the address here: https://danmarksadresser.dk/adresser-i-danmark/';
END
$BODY$;
Looks like you're trying to use SQL Server specific code in Postgres, which isn't going to work of course.
PL/pgSQL doesn't allow SELECTs just anywhere, when the result isn't passed to somehwere, like variables, etc.. And there is no ##ROWCOUNT. From what I can guess what you want to do, you can use EXISTS and your query for the condition of the IF.
CREATE
OR REPLACE FUNCTION before_insert_update()
RETURNS trigger
AS
$$
BEGIN
IF EXISTS(SELECT *
FROM addresses
WHERE addresses.roadname = new.roadname
AND addresses.housenumber = new.housenumber) THEN
SELECT addresses.roadcode,
addresses.geom
INTO new.roadcode,
new.geom
FROM addresses
WHERE addresses.roadname = new.roadname
AND addresses.housenumber = new.housenumber;
RETURN new;
ELSE
RAISE EXCEPTION 'The address does not exist: %', concat(new.roadname, ' ', new.housenumber)
USING HINT = 'Check the address here: https://danmarksadresser.dk/adresser-i-danmark/';
END IF;
END
$$
LANGUAGE plpgsql;

Postgres - Create trigger that can exist in multiple schemas

I have a postgres trigger that I have written. It works 100%, I've tested it thoroughly. However, when my application writes to that database table, it fails. I'll put the code below, then I'll explain the failure.
CREATE OR REPLACE FUNCTION validate_client_user_role()
RETURNS trigger AS
$BODY$
DECLARE role_has_client INT;
DECLARE user_has_client INT;
BEGIN
IF NEW.client_id IS NULL THEN
RAISE EXCEPTION 'client_id cannot be null';
END IF;
IF NEW.user_id IS NULL THEN
RAISE EXCEPTION 'user_id cannot be null';
END IF;
IF NEW.role_id IS NULL THEN
RAISE EXCEPTION 'role_id cannot be null';
END IF;
SELECT COUNT(*)
INTO role_has_client
FROM roles
WHERE id = NEW.role_id
AND client_id = NEW.client_id;
SELECT COUNT(*)
INTO user_has_client
FROM client_users
WHERE user_id = NEW.user_id
AND client_id = NEW.client_id;
IF role_has_client = 0 THEN
RAISE EXCEPTION 'Role is not allowed by client';
END IF;
IF user_has_client = 0 THEN
RAISE EXCEPTION 'User is not allowed by client';
END IF;
RETURN NEW;
END
$BODY$ LANGUAGE plpgsql;
CREATE TRIGGER client_user_role_validation
BEFORE INSERT OR UPDATE
ON client_user_roles
FOR EACH ROW
EXECUTE PROCEDURE validate_client_user_role();
So, my application database has multiple schemas, one for dev, qa, prod, etc. When I first write this DDL statement, I run this first:
set search_path to dev;
This then adds it properly to the "dev" schema. As long as I'm in my DB query console, I can validate this trigger is working perfectly.
However, when my application tries to write to the tables, the trigger fails, saying the relations (roles, client_users) that it tries to do selects from don't exist. I can fix this by modifying the function code to explicitly reference the "dev" schema for each table. I don't want to do this, though. I would prefer code that I can execute as a DDL statement for dev/qa/prod without needing to replace all the schema references or keep multiple copies of it.
I'm hoping someone can explain if there is some way to do this. Obviously the trigger is not inheriting from the schema it is assigned to when I execute it. However, if there is some postgres trick I'm not aware of to make this work, I would appreciate the guidance. Thank you.
You can use the TG_TABLE_SCHEMA variable and set_config() with IS_LOCAL = true to accomplish this:
CREATE OR REPLACE FUNCTION validate_client_user_role()
RETURNS trigger AS
$BODY$
DECLARE role_has_client INT;
DECLARE user_has_client INT;
BEGIN
IF NEW.client_id IS NULL THEN
RAISE EXCEPTION 'client_id cannot be null';
END IF;
IF NEW.user_id IS NULL THEN
RAISE EXCEPTION 'user_id cannot be null';
END IF;
IF NEW.role_id IS NULL THEN
RAISE EXCEPTION 'role_id cannot be null';
END IF;
PERFORM set_config('search_path', TG_TABLE_SCHEMA, true); -- <-- This line
SELECT COUNT(*)
INTO role_has_client
FROM roles
WHERE id = NEW.role_id
AND client_id = NEW.client_id;
SELECT COUNT(*)
INTO user_has_client
FROM client_users
WHERE user_id = NEW.user_id
AND client_id = NEW.client_id;
IF role_has_client = 0 THEN
RAISE EXCEPTION 'Role is not allowed by client';
END IF;
IF user_has_client = 0 THEN
RAISE EXCEPTION 'User is not allowed by client';
END IF;
RETURN NEW;
END
$BODY$ LANGUAGE plpgsql;

how to create triggers function before insert show test message

i have a table
CREATE TABLE test.emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
and function
CREATE FUNCTION test.emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
how to show message no need to update the values how can i modify my function
trigger is
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON test.emp
FOR EACH ROW EXECUTE PROCEDURE test.emp_stamp();
I think all you need to do is 1) make sure it's an update and not an insert (otherwise the reference to old won't make any sense and 2) compare to the existing record. Since Employee name seems to be the PK and the last two fields are only to capture changes, my guess is you only want to test the salary:
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
elsif TG_OP = 'UPDATE' and new.salary = old.salary then
RAISE EXCEPTION 'salary is already %', NEW.salary;
END IF;
But of course you could also use this for any field. If you wanted to make sure at least one changed, it would be something like this:
elsif TG_OP = 'UPDATE' and
new.salary = old.salary and
new.last_date = old.last_date and
new.last_user = old.last_user then
RAISE EXCEPTION 'Update does not alter any data';
END IF;

Postgres trigger function: substitute value before insert or update

I've looked up pretty much everything I could find regarding this issue, but I still don't understand what is wrong with this trigger:
CREATE OR REPLACE FUNCTION func_SubstitutePostLatLng_Upt()
RETURNS trigger AS
$BODY$
BEGIN
IF OLD.post_latlng IS NULL AND NEW.post_latlng IS NULL AND NEW.place_guid IS NOT NULL THEN
raise notice 'SELECT';
SELECT place.geom_center, place.city_guid
INTO NEW.post_latlng, NEW.city_guid
FROM public.place
WHERE (place.origin_id, place.place_guid) IN (VALUES (NEW.origin_id,NEW.place_guid));
raise notice 'Value db_geom: %', NEW.post_latlng;
raise notice 'Value db_city_guid: %', NEW.city_guid;
IF NEW.post_latlng IS NOT NULL THEN
NEW.post_geoaccuracy = 'place';
IF NEW.city_guid IS NOT NULL THEN
SELECT country_guid INTO NEW.country_guid
FROM public.city WHERE (origin_id, city_guid) IN (VALUES (NEW.origin_id,NEW.city_guid));
END IF;
END IF;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
DROP TRIGGER IF EXISTS trig_SubstitutePostLatLng_Upd on public.post;
CREATE TRIGGER trig_SubstitutePostLatLng_Upd
BEFORE UPDATE
ON public.post
FOR EACH ROW
WHEN (pg_trigger_depth() < 1)
EXECUTE PROCEDURE func_SubstitutePostLatLng_Upt()
(I have a second similar trigger for insert)
The code is supposed to do the following:
On Update on table "post", check if no post_latlng is submitted (=NULL), and if yes, substitute post_latlng from table place (geom_center), if available.
However, no matter what I do, I get the following when updating an entry in table "post" (=triggering the above trigger):
NOTICE: SELECT
NOTICE: Value db_geom: <NULL>
NOTICE: Value db_city_guid: <NULL>
INSERT 0 1
Query returned successfully in 47 msec.
The test-data for place_guid, geom_center etc. is definitely available and both
raise notice 'Value db_geom: %', NEW.post_latlng;
raise notice 'Value db_city_guid: %', NEW.city_guid;
should not output NULL.
There were several smaller issues, it now works. Here is a more cleaner code that uses variables in between:
CREATE OR REPLACE FUNCTION func_SubstitutePostLatLng_Upt()
RETURNS trigger AS
$BODY$
DECLARE
db_geom_center text;
db_city_guid text;
db_country_guid text;
BEGIN
IF OLD.post_latlng IS NULL AND NEW.post_latlng IS NULL AND NEW.place_guid IS NOT NULL THEN
SELECT place.geom_center, place.city_guid
INTO db_geom_center, db_city_guid
FROM public.place
WHERE (place.origin_id, place.place_guid) IN (VALUES (NEW.origin_id,NEW.place_guid));
IF db_geom_center IS NOT NULL THEN
NEW.post_latlng = db_geom_center;
NEW.post_geoaccuracy = 'place';
END IF;
IF db_city_guid IS NOT NULL THEN
NEW.city_guid = db_city_guid;
SELECT city.country_guid
INTO db_country_guid
FROM public.city
WHERE (city.origin_id, city.city_guid) IN (VALUES (NEW.origin_id,db_city_guid));
NEW.country_guid = db_country_guid;
END IF;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

How to store values in trigger PostgreSQL

I have a trigger that looks something like this:
CREATE OR REPLACE FUNCTION CHECK_SCHEDULE()
RETURNS TRIGGER AS
$BODY$
BEGIN
IF EXISTS(
SELECT DAY, TIME FROM MEETING
WHERE NEW.DAY = MEETING.DAY AND NEW.TIME > MEETING.TIME
) THEN
RAISE EXCEPTION 'THERE IS A MEETING HAPPENING ON % % ', NEW.DAY, NEW.TIME;
ELSE
RETURN NEW;
END IF;
END;
$BODY$ LANGUAGE PLPGSQL;
This works fine except I want the message to be the time it's conflicting with: There is a meeting happening on MEETING.DAY and MEETING.TIME.
However I cannot do this because it doesn't know what these variables are. Is it possible to store the values in my select clause so I can use them later?
You can move the day and time into a declared variable (e.g. a RECORD) for reference later.
CREATE OR REPLACE FUNCTION CHECK_SCHEDULE()
RETURNS TRIGGER AS
$BODY$
DECLARE
meetinginfo RECORD;
BEGIN
SELECT meeting.day, meeting.time
INTO meetinginfo
FROM meeting
WHERE new.day = meeting.day
AND new.time > meeting.time
ORDER BY new.time
LIMIT 1;
IF FOUND THEN
RAISE EXCEPTION 'THERE IS A MEETING HAPPENING ON % %', meetinginfo.day, meetinginfo.time;
END IF;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;