is postgresql nested exception possible? - postgresql

i am using postgrest exception in function 'public.main_function'. in exception 'others', i also use sub functions to save my log data.
but my sub function 'public.something_went_wrong_log_creation' might be error sometimes.
how can i add exception (nested exception) in exception 'others' below?
CREATE OR REPLACE FUNCTION public.main_function(request json)
RETURNS integer AS
$BODY$
BEGIN
-- statement 1
-- statement 2
-- statement 3
RETURN 1;
EXCEPTION
-- SOMETHING WENT WRONG
WHEN others
THEN
-- LOG SOMETHING WENT WRONG
PERFORM public.something_went_wrong_log_creation();
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
CREATE OR REPLACE FUNCTION public.something_went_wrong_log_creation()
RETURNS integer AS
$BODY$
BEGIN
-- statement 1
-- statement 2
-- statement 3
RETURN 1;
EXCEPTION
-- SOMETHING WENT WRONG
WHEN others
THEN
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE

You can use subblock and wrap your exception handling code in it.
CREATE OR REPLACE FUNCTION public.main_function(request json)
RETURNS integer AS
$BODY$
BEGIN
-- statement 1
-- statement 2
-- statement 3
RETURN 1;
EXCEPTION
-- SOMETHING WENT WRONG
WHEN others
THEN
BEGIN
-- LOG SOMETHING WENT WRONG
PERFORM public.something_went_wrong_log_creation();
RETURN 0;
EXCEPTION
WHEN others
THEN
RETURN -1;
END;
END;
$BODY$
LANGUAGE plpgsql VOLATILE

Related

RETURN QUERY doesn't stop following code execution, while return does

I have the following 2 functions:
CREATE or replace FUNCTION func_return_query() RETURNS SETOF varchar AS
$BODY$
BEGIN
RETURN QUERY SELECT ename from emp;
IF NOT FOUND THEN
RAISE EXCEPTION 'No flight at %.', $1;
END IF;
raise notice 'hello world,return query';
END;
$BODY$
LANGUAGE plpgsql;
select func_return_query()
When I run this code, I see a message hello world, return query.
But for this function here, I don't see that message hello world, return:
CREATE or replace FUNCTION func_return() RETURNS integer AS
$BODY$
BEGIN
return 10;
raise notice 'hell world,return';
END;
$BODY$
LANGUAGE plpgsql;
select func_return()
Both of them are using return, why return query doesn't stop the following code execution, when return does?

How to return Select * INTO STRICT variable from function

I am trying to return a single row of a table in a function and it is important that if there isn't any row or more then one there is an exception raised. I am using SELECT * INTO STRICT for that reason. However, I can't seem to find the correct way to return. Was wondering if someone knew the correct way of returning the single row?
I know I can separate the check and get it work but was curious if there was a way to get this to work.
CREATE OR REPLACE FUNCTION GameInfo.getAllPlayerInfo(
playerID GameInfo.Player.PID%Type)
RETURNS TABLE (PID VARCHAR,Name VARCHAR, Email VARCHAR,Password VARCHAR) AS
$$
DECLARE
found_player GameInfo.Player%ROWTYPE;
BEGIN
SELECT * INTO STRICT found_player FROM GameInfo.Player WHERE Player.PID =
$1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'PID % not found';
WHEN TOO_MANY_ROWS THEN
RAISE EXCEPTION 'PID % not unique';
RETURN found_player;
END;
$$ LANGUAGE plpgsql
STABLE
SECURITY DEFINER;
Your function is declared as returns table, so in PL/pgSQL you need to use return next... to return a row.
That return statements needs to be moved before the exception handling. Currently your function does not return anything, if a row is found.
You can also simplify the function declaration by using returns setof so you don't need to specify all columns.
CREATE OR REPLACE FUNCTION GameInfo.getAllPlayerInfo(playerID GameInfo.Player.PID%Type)
RETURNS setof gameinfo.player
AS $$
DECLARE
found_player GameInfo.Player%ROWTYPE;
BEGIN
SELECT *
INTO STRICT found_player
FROM GameInfo.Player
WHERE Player.PID = playerid;
RETURN NEXT found_player; --<< here
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'PID % not found';
WHEN TOO_MANY_ROWS THEN
RAISE EXCEPTION 'PID % not unique';
END;
$$
LANGUAGE plpgsql
STABLE
SECURITY DEFINER;
Note that set returning functions need to be used in the FROM clause:
select *
from gameinfo.getallplayerinfo(1);

Trigger run after hibernate insert returning error [duplicate]

Is it possible to cancel previous operations in a user defined function?
For example:
CREATE OR REPLACE FUNCTION transact_test () RETURNS BOOLEAN
AS $$
BEGIN
UPDATE table1 SET ...
UPDATE table2 SET ...
IF some_condition THEN
--Here is possible to cancel all above operations?
RETURN FALSE;
END IF;
RETURN TRUE;
END;
$$
LANGUAGE plpgsql;
Both answers so far are incorrect.
If you try to start a transaction or use a SAVEPOINT inside a plpgsql function you get an error message like this:
ERROR: cannot begin/end transactions in PL/pgSQL
HINT: Use a BEGIN block with an EXCEPTION clause instead.
CONTEXT: PL/pgSQL function "f_savepoint" line 6 at SQL statement
If you try a SAVEPOINT inside a plain SQL function:
ERROR: SAVEPOINT is not allowed in a SQL function
CONTEXT: SQL function "f_savepoint2" during startup
As the error message instructs, use a BEGIN block inside a plpgsql function instead. Your demo could look like this:
CREATE OR REPLACE FUNCTION transact_test(boolean)
RETURNS boolean AS
$func$
BEGIN -- start a nested BEGIN block
UPDATE t SET i = i+1 WHERE i = 1;
UPDATE t SET i = i+1 WHERE i = 3;
IF $1 THEN
RAISE EXCEPTION 'foo'; -- cancels all of the above
END IF;
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RETURN FALSE;
-- do nothing
END
$func$ LANGUAGE plpgsql;
-> SQLfiddle demonstrating it all.

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 $$

Cancel previous operations in user defined function

Is it possible to cancel previous operations in a user defined function?
For example:
CREATE OR REPLACE FUNCTION transact_test () RETURNS BOOLEAN
AS $$
BEGIN
UPDATE table1 SET ...
UPDATE table2 SET ...
IF some_condition THEN
--Here is possible to cancel all above operations?
RETURN FALSE;
END IF;
RETURN TRUE;
END;
$$
LANGUAGE plpgsql;
Both answers so far are incorrect.
If you try to start a transaction or use a SAVEPOINT inside a plpgsql function you get an error message like this:
ERROR: cannot begin/end transactions in PL/pgSQL
HINT: Use a BEGIN block with an EXCEPTION clause instead.
CONTEXT: PL/pgSQL function "f_savepoint" line 6 at SQL statement
If you try a SAVEPOINT inside a plain SQL function:
ERROR: SAVEPOINT is not allowed in a SQL function
CONTEXT: SQL function "f_savepoint2" during startup
As the error message instructs, use a BEGIN block inside a plpgsql function instead. Your demo could look like this:
CREATE OR REPLACE FUNCTION transact_test(boolean)
RETURNS boolean AS
$func$
BEGIN -- start a nested BEGIN block
UPDATE t SET i = i+1 WHERE i = 1;
UPDATE t SET i = i+1 WHERE i = 3;
IF $1 THEN
RAISE EXCEPTION 'foo'; -- cancels all of the above
END IF;
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RETURN FALSE;
-- do nothing
END
$func$ LANGUAGE plpgsql;
-> SQLfiddle demonstrating it all.