I have the "valid_id" check constraint on my requests table. But when it violates the constraint it shows following error
ERROR: new row for relation "requests" violates check constraint
"valid_name" DETAIL: Failing row contains ....
But instead of that I want to show message like "Failed to insert record. name is required".
Is there any way to show the custom error message in PostgreSQL?
This is kind of advanced territory here because you want to be pretty familiar with SQL states as well as existing error messages before you get started. Note that you want to re-used existing sql states as appropriate so that the application doesn't know you have overridden your check constraint.
But what you can do is create a function which runs the check and issues a raise exception if the check fails. Something like:
CREATE FUNCTION check_is_not_null(value text, column_name text) RETURNS BOOL
LANGUAGE plpgsql AS $$
begin
IF $1 IS NULL THEN
RAISE EXCEPTION 'Error: % is required', $2;
END IF;
RETURN TRUE;
END;
$$;
If you are using 8.4 or higher, you can specify an SQL state.
Related
In PostgreSQL I'm trying to make a database trigger on an INSERT that passes the primary key of the new row as the payload of a channel notification using NOTIFY
I'm having trouble wrapping my head around the correct usage age of the NEW variable in this context. No matter what I try seems to generate a syntax error.
Below is a simplified example of my use case.
CREATE OR REPLACE FUNCTION table_event_notify_function()
RETURNS trigger AS $$
BEGIN
NOTIFY table_event_notify, NEW.id;
return NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER table_events_insert_trigger AFTER INSERT ON table_events EXECUTE PROCEDURE table_event_notify_function();
psycopg2.errors.SyntaxError: syntax error at or near "NEW"
The payload parameter for NOTIFY must be a constant. To provide a dynamic parameter, you need to use pg_notify()
perform pg_notifiy('table_event_notify', new.id::txt);
I tried looking up for the solution but failed to find one. I understand that postgres rollbacks all the transactions once an exception is raised. But my requirement is to log a certain exception into a database table.
Following is a sample code:
CREATE OR REPLACE FUNCTION fn_test(
in_var numeric DEFAULT NULL::numeric)
RETURNS numeric AS
$BODY$
DECLARE
BEGIN
IF in_var=0
THEN
RAISE EXCEPTION using errcode='INELG';
ELSE IF in_var=1
THEN
RAISE EXCEPTION using errcode='INVAL';
ELSE
RETURN in_var;
END IF;
begin
EXCEPTION
WHEN sqlstate 'INELG'
THEN
INSERT INTO LOG_TBL(in_par,error_reason)
VALUES(in_var,'VALUE INELGIBLE');
RAISE EXCEPTION 'Unable to Process: Parameter Not Eligible';
WHEN sqlstate 'INVAL'
THEN
INSERT INTO LOG_TBL(in_par,error_reason)
VALUES(in_var,'VALUE INValid');
RAISE EXCEPTION 'Unable to Process: Parameter Invalid';
end;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;
The problem I face is as soon as an exception is raised the records in LOG_TBL in the exception section gets rolled back. Is there an alternative solution where I can insert the record into database table as well as raise an application error?
In a simple way - you can't. Once you are inside a function, that's it.
You either have to do this in the client layer or loop back in on another connection to log the error. Traditionally you would loop back in via dblink, but it might be possible to use Foreign Data Wrappers too - I have to admit I'm not sure.
Edit: Nope, it looks like postgres_fdw syncs the transactions between local and remote. Not helpful in this case. Looks like dblink is the obvious choice.
I´m creating a trigger on PGAdminIII where I want to delete the rows that have the foreign key on the other table. However I´m getting a Syntax error and I can´t find where the problem is:
CREATE TRIGGER clienteDelete
BEFORE DELETE ON cliente
FOR EACH ROW
BEGIN
DELETE FROM contacto WHERE contacto.id = OLD.contacto_idcontacto;
END;
ERROR: syntax error at or near "BEGIN"
LINE 4: BEGIN
^
********** Error **********
ERROR: syntax error at or near "BEGIN"
SQL state: 42601
Character: 68
I´m not used to the syntax of triggers on Postgres but that´s what I know according to the SQL standard. Any help will be highly apreciated
So Postgresql triggers has some limitations, for instance
PostgreSQL only allows the execution of a user-defined function for
the triggered action.
So to accomplish what you want you need to define a function and make the trigger fire that. Something like this should work:
CREATE FUNCTION clienteDelete() RETURNS TRIGGER AS $_$
BEGIN
DELETE FROM contacto WHERE contacto.id = OLD.contacto_idcontacto;
RETURN OLD;
END $_$ LANGUAGE 'plpgsql';
And the trigger:
CREATE TRIGGER delete_contacto
BEFORE DELETE ON cliente
FOR EACH ROW
EXECUTE PROCEDURE clienteDelete();
I'm no Postgresql expert though so expect the code above to not be perfect.
Is it possible for a trigger to be defined in such a way that the row that was to be inserted is not inserted, without raising an exception? My use case is that I want to simplify the exception handling for the client library: the client library will just execute a statement to insert a row in a table and I was hoping that the trigger could be defined, more or less using the below syntax:
CREATE TRIGGER control_tr AFTER INSERT ON tableFoo
FOR EACH ROW
EXECUTE PROCEDURE control_tr_fun();
CREATE OR REPLACE FUNCTION control_tr_fun() RETURNS TRIGGER AS $$
BEGIN
IF (NOT condition_is_met(NEW.a, NEW.b, NEW.c)) THEN
DO NOTHING INSTEAD OF INSERT // syntax I am hoping for instead of RAISE EXCEPTION
ELSE
RETURN NEW;
END IF;
END
$$ LANGUAGE plpgsql;
I appreciate that I can ask the client library to call a PL/pgSQL function or make a trigger that RAISEs an exception and ask the client library to catch the exception (if raised) and just ignore it, but I am looking for a way to implement this as transparently for the client as possible.
If you RETURN NULL then nothing will happen, the INSERT will fail silently. But you need to define the trigger as BEFORE INSERT, and not AFTER INSERT as it is in your example.
If you RAISE EXCEPTION, then the entire transaction will fail.
You can also RAISE NOTICE without failing the transaction and catch it in the client library, but only if it was the last notice produced. You cannot stack notices.
I am wondering if there is a way of getting an error back on update if the row does not exist in the database when I issue an update.
update users set email='aa#bb.com' where id=200
If users table does not have user with id=200, then postgres simply says "UPDATE 0". However, I am wondering if I can get an error back. That way, I don't have to issue 2 requests to the database, once to check existence, once to update. There could also be a race condition between checking the existence and issuing an update if we do it separately.
Why do I need an error? Simple - so the clients know they have used an invalid user id and they take corrective action.
You can use something like
update users set email='aa#bb.com' where id=200 returning id;
This query will return the id of the updated row. If it returns 0 rows - then throw an error in your application.
Also you may want to check if your db driver / framework returns the count of affected rows (like getUpdateCount() in JDBC).
You can use an anonymous code block.
do $$
begin
update users set email='aa#bb.com' where id=200;
if not found then raise exception 'User not found';
end if;
end $$;
Or regular function:
create or replace function update_user(new_email text, user_id integer)
returns void language plpgsql
as $$
begin
update users set email = new_email where id = user_id;
if not found then raise exception 'User id % not found', user_id;
end if;
end $$;
select update_user('aa#example.com', 200);