Calling a function in plpgsql exception block - postgresql

Is it possible to call a function from within the WHEN OTHERS THEN block?
Something like:
EXCEPTION
WHEN OTHERS THEN
my_fnc('a key ID', 'Custome message', SQLERRM||' - '||SQLSTATE);

you surely can:
t=# do $$ begin
perform 1/0;
exception when others then perform to_json(SQLERRM||' - '||SQLSTATE);
end;
$$;
DO
but just calling fn() is pointless, unless it performs some real action, so ritcher example:
t=# do $$ begin
perform 1/0;
exception when others then raise info '%', to_json(SQLERRM||' - '||SQLSTATE);
end;
$$;
INFO: "division by zero - 22012"
DO

Related

loggin postgresql when others exception

Database: RDS PostgreSQL 12
I have a simeple proc that errors out. When it errors out I would like to log a record of it and then raise the exception. However I can only get it to do one of those things. I can either get it to log the error or I can raise the exception. I've tried both inserting the record directly from the exception block or by calling a proc that will insert the record into the table. How can I get it to do both? I'll post the code for the two blocks below. Any help would be appreciated!
CREATE OR REPLACE PROCEDURE error_test_prc ()
LANGUAGE plpgsql
AS $body$
DECLARE
var1 int;
BEGIN
SELECT 10/0 INTO var1;
EXCEPTION
WHEN OTHERS THEN
--INSERT INTO mdm_raw.raw_err_msg(msg_date, msg) VALUES(current_date, 'test');
CALL mdm_raw.error_logging_prc('this is a different proc test');
RAISE EXCEPTION 'Caught in Block 1 %', SQLERRM;
END;
$body$
CREATE OR REPLACE PROCEDURE error_logging_prc (msg_text text)
LANGUAGE plpgsql
AS $body$
DECLARE
var1 int;
BEGIN
INSERT INTO mdm_raw.raw_err_msg(msg_date, msg) VALUES(current_date, msg_text);
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'Error Caught in error_logging_prc %', SQLERRM;
END;
$body$
The only known way to achieve that is to use the dblink extension to create a new connection from the database to itself. You can write your log message via dblink and commit it, then throw the error that will roll back the current transaction.
You can accomplish this by rollback on the current transaction, then insert and commit your log. See lines marked with --<<<<< in your procedure below. Another would be setting up a dblink.
CREATE OR REPLACE PROCEDURE error_test_prc ()
LANGUAGE plpgsql
AS $body$
DECLARE
var1 int;
BEGIN
SELECT 10/0 INTO var1;
EXCEPTION
WHEN OTHERS THEN
declare --<<<<<<
msg text --<<<<<<
begin --<<<<<<
msg = sqlerrm; --<<<<<<
rollback; --<<<<<<
CALL mdm_raw.error_logging_prc('this is a different proc test');
RAISE EXCEPTION 'Caught in Block 1 %',msg;
end ;
END;
$body$
CREATE OR REPLACE PROCEDURE error_logging_prc msg_text text)
LANGUAGE plpgsql
AS $body$
DECLARE
var1 int;
BEGIN
INSERT INTO mdm_raw.raw_err_msg(msg_date, msg) VALUES(current_date, msg_text);
commit; --<<<<<<
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'Error Caught in error_logging_prc %', SQLERRM;
END;
$body$

Postgres Exceptions

I need to port over some Oracle PL/SQL code to Postgres. This is my first time working with Postgres.
In Oracle, with regards to exceptions, I can have this:
IF v_customer_id IS NULL OR v_email IS NULL THEN
RAISE invalid_paramters;
END IF;
How is that done in Postgres? Basically validating input, and if anything fails validation, call a custom handler to perform whatever actions. From what I have read, Postgres does not support custom named exceptions.
Thanks for your time.
You could use RAISE with a custom message and a specific sqlstate constant:
--Anonymous block for testing purposes
DO $$
BEGIN
RAISE invalid_parameter_value USING MESSAGE = 'Invalid customer or email';
END $$;
Or you could simply raise a generic exception:
DO $$
BEGIN
RAISE EXCEPTION 'A generic exception (P0001)';
END $$;
You could also handle a exception:
DO $$
BEGIN
--This will raise a division by zero
PERFORM 0 / 0;
--You can catch a exception with a EXCEPTION block
EXCEPTION
WHEN division_by_zero THEN
RAISE INFO 'Division by zero catched';
WHEN raise_exception THEN
RAISE INFO 'Another error catched...';
END $$;
And get more detailed information about a exception:
DO $$
DECLARE
error_msg text;
BEGIN
--This will raise a division by zero
PERFORM 0 / 0;
--You can get more about error with GET STACKED DIAGNOSTICS
EXCEPTION
--Tip: OTHERS keyword will catch any exception
WHEN OTHERS THEN
GET STACKED DIAGNOSTICS error_msg = MESSAGE_TEXT;
RAISE EXCEPTION 'My custom exception: %', error_msg;
END $$;
I'd suggest you to take a look on sqlstates and control structures for more about error handling in PostgreSQL.
do $$
declare
v int := 1;
begin
begin
if v < 2 then -- Validation here
raise sqlstate 'A0001'; -- custom SQL state, any five upper case letters and/or digits
end if;
exception -- Catch exception in the nested BEGIN ... END block
when sqlstate 'A0001' then
raise notice 'Here is my exception handler';
v := 2;
end;
raise notice 'Continue here with value %', v; -- Reports that the v = 2
end $$;
Instead of capturing it in the main exception block, you can add a nested begin...end block in the place of RAISE EXCEPTION part and raise an exception inside and capture it in the others exception block inside the nested block with a RETURN keyword.
do
$$
begin
raise notice 'line 1';
begin
raise exception 'raising exception';
exception
when others then
raise notice 'captured in nested block';
end;
return;
raise notice 'check if it continues';
exception
when others then
raise notice 'main block others';
end;
$$
language plpgsql;

Raise and catch user defined exceptions

I use the RAISE EXCEPTION '...' USING ERRCODE='....' quite a lot in my code, as I can use the error code in my C# code. However, I would like to use it now in my plpgsql code, like this:
BEGIN
...
RAISE EXCEPTION 'Something is wrong' USING ERRCODE='S0001';
EXCEPTION WHEN 'S0001' THEN
-- Handle code S0001
END;
But that doesn't work. How can I catch and process my own thrown exceptions in plpgsql ?
Your exception handling clause should look like this:
EXCEPTION
WHEN SQLSTATE 'S0001'
THEN
...
END;
Use sqlstate, e.g.:
drop function if exists test();
create or replace function test()
returns int language plpgsql as $$
begin
raise exception using errcode = 50001;
return 0;
exception when sqlstate '50001' then
return sqlstate;
end $$;
select test();
test
-------
50001
(1 row)

PostgreSQL 9.5: Exception handling

I have table called employee with two columns, and have created two functions for
insertion and updation operations. These two function will be called through another
function which named as udf_3().
I want to do exception handling on the third function that is udf_3() which should
give me the details of which function has error.
--Table: employee
create table employee
(
id int,
name varchar(10)
);
--Function 1: udf_1() used for insertion.
create or replace function udf_1()
returns void as
$body$
begin
insert into employee values(1,'Mak');
end;
$body$
language plpgsql;
--Function 2: udf_2() used for updation.
create or replace function udf_2()
returns void as
$body$
begin
update employee
set a_id = 99
where name = 'Mak';
end;
$body$
language plpgsql;
--Function 3: udf_3() used to call all above function.
create or replace function udf_3()
returns int as
$body$
begin
perform udf_1();
perform udf_2();
return 0;
exception
when others then
RAISE INFO 'Error Name:%',SQLERRM;
RAISE INFO 'Error State:%', SQLSTATE;
return -1;
end;
$body$
language plpgsql;
--Function Calling:
select * from udf_3();
Exception:
INFO: Error Name:column "a_id" of relation "employee" does not exist
INFO: Error State:42703
Problem: I am able to get the exception BUT not able to get from which function i got exception.
According to the documentation
Within an exception handler, one may also retrieve information about the current exception by using the GET STACKED DIAGNOSTICS command
https://www.postgresql.org/docs/9.5/static/plpgsql-control-structures.html#PLPGSQL-EXCEPTION-DIAGNOSTICS
Example:
create or replace function udf_3()
returns int as
$body$
declare
err_context text;
begin
perform udf_1();
perform udf_2();
return 0;
exception
when others then
GET STACKED DIAGNOSTICS err_context = PG_EXCEPTION_CONTEXT;
RAISE INFO 'Error Name:%',SQLERRM;
RAISE INFO 'Error State:%', SQLSTATE;
RAISE INFO 'Error Context:%', err_context;
return -1;
end;
$body$
language plpgsql;
will display the following:
INFO: Error Context:SQL: "SELECT udf_1()"
But this is just a textual representation of the error. Your logic should not rely on it. It is better to use custom error codes to handle exception logic (and raise meaningful exceptions in your functions that you can catch and handle later on).
UPDATE:
Another solution is to separate your code in different blocks for which you can catch exceptions separately. In this case you know from which block the exception was raised:
DO $$
BEGIN
-- Block 1
BEGIN
-- any code that might raise an exception
RAISE EXCEPTION 'Exception 1'; -- for example
EXCEPTION
WHEN others THEN
RAISE INFO 'Caught in Block 1';
END;
-- Block 2
BEGIN
-- any code that might raise an exception
RAISE EXCEPTION 'Exception 2'; -- for example
EXCEPTION
WHEN others THEN
RAISE INFO 'Caught in Block 2';
END;
END $$

Matching regular expression in an if statement in PostgreSQL

I have written the following function:
CREATE FUNCTION Validate_Password_Hash() RETURNS trigger AS $$
BEGIN
IF (NEW.Password ~* '^[a-f0-9]{64}$')
THEN
RAISE EXCEPTION 'The password hash is invalid! Please use SHA-256. You tried to insert: %', NEW.Password;
END IF;
RETURN NEW;
END;
$$ LANGUAGE PLPGSQL;
And I attach it to a trigger to fire before an INSERT. However, even if a provide a valid SHA-256 hash, the exception still gets raised, which implies the hash doesn't match the pattern. What could possibly be the problem here?
Reverse the logic.
You are raising exception if the hash is valid.
Try this:
CREATE FUNCTION Validate_Password_Hash() RETURNS trigger AS $$
BEGIN
IF (NEW.Password !~* '^[a-f0-9]{64}$')
THEN
RAISE EXCEPTION 'The password hash is invalid! Please use SHA-256. You tried to insert: %', NEW.Password;
END IF;
RETURN NEW;
END;
$$ LANGUAGE PLPGSQL;