Check function with IF statement in POSTGRESQL - postgresql

I have 10 functions, all 10 return '1' if the function has no errors and '0' if function has errors. I want to create another function witch calls all this functions and which checks it out if the functions return 0 or 1. After that, I want to run this function in linux crontab and the function's output (some text from if conditions) to go in a log file.
I'm not sure if I can check this functions like this. Thanks for your time!
CREATE OR REPLACE FUNCTION public.test_al1()
RETURNS text
LANGUAGE 'plpgsql'
COST 100
AS $BODY$
DECLARE
BEGIN
select public.test();
if (select public.test()) = 1 then
RAISE NOTICE 'No errors'
else
RAISE NOTICE 'Errors'
end if;
END
$BODY$;

You were missing a return point for your query and there were also a few ; missing. I'm not really sure what you want to achieve with this function, since you declared the function would return TEXT and there is no RETURN statement.
One option would be to not return anything and use RAISE as you've been doing - keep in mind that the intention of RAISE (without INFO, EXCEPTION, etc.) alone is rather to report error messages:
CREATE OR REPLACE FUNCTION public.test_al1() RETURNS VOID LANGUAGE plpgsql
AS $BODY$
BEGIN
IF public.test() = 1 THEN
RAISE 'Errors';
ELSE
RAISE 'No errors';
END IF;
END
$BODY$;
.. or alternatively you can simplify it a bit by returning the message as TEXT in the RETURN clause.
CREATE OR REPLACE FUNCTION public.test_al1() RETURNS TEXT LANGUAGE plpgsql
AS $BODY$
DECLARE res TEXT DEFAULT 'No errors';
BEGIN
IF public.test() = 1 THEN
res := 'Errors';
END IF;
RETURN res;
END
$BODY$;
Further reading: CREATE FUNCTION

Related

PostGreSQL - Controlling transactions

I have a Stored Procedure that in turns calls several other Stored Procedures; each of them returns true or false and internally deal with errors by store them into a table.
Something like this:
-- (MAIN STORED PROCEDURE)
BEGIN
CALL STORED_PROC_1('WW','TT','FF',result);
IF result = TRUE then
CALL STORED_PROC_2('a','b','c',result);
...
END IF;
END;
IF result != TRUE THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
-- (END MAIN STORED PROCEDURE)
-------
--Example of Stored Procedure 1
CREATE OR REPLACE PROCEDURE STORED_PROC_1 (IN a TEXT, IN b TEXT, IN c TEXT, INOUT result boolean)
AS $BODY$
BEGIN
-- DO SOME STUFF HERE
IF ERROR_FOUND THEN
INSERT INTO ERROR_LOG VALUES ('Error','Type of Error',CURRENT_DATE);
--COMMIT; (I cannot do this commit here but I would like to save the information going into the ERROR_LOG table)
result := FALSE;
ELSE
result := TRUE;
END IF;
END;
$BODY$;
This is actually what I want; only commit if all return TRUE;
The problem is that inside the STORED_PROC_1 or _2 there are error handlings that write into a Error Log table and ... if there are errors, they will return FALSE in the result and, that in turn will call a rollback and I will loose my Error Log.
Is there a way to create a sort of a memory table that I can load with the error info and write it after the ROLLBACK? Or is there a better way to achieve this?
Thanks a lot.
I would suggest using FUNCTIONs rather than PROCEDUREs if you can for all of this because they have transaction control built-in in a sense. If the FUNCTION fails you automatically rollback its trasaction.
That being said, instead of doing the insert directly I would make a function like the following:
CREATE OR REPLACE FUNCTION func_logerror(error TEXT, errortype TEXT, date DATE DEFAULT CURRENT_DATE)
RETURNS SETOF ERROR_LOG
AS $$
DECLARE
BEGIN
RETURN QUERY
INSERT INTO ERROR_LOG VALUES (error, errortype, date) RETURNING *;
END;
$$;
Call it using either SELECT or PERFORM depending on whether or not you want the results in the calling function.
PERFORM * FROM func_logerror('Error', 'Type of Error', CURRENT_DATE);
After reading your answer response I've come up with this to help clarify. I was suggesting not using stored procedures at all. Instead handle your error cases in a different manner:
-- (MAIN STORED PROCEDURE)
CREATE OR REPLACE FUNCTION FUNC_MAIN()
RETURNS boolean
AS $$
DECLARE
res boolean;
BEGIN
SELECT * INTO res FROM FUNC_1('WW','TT','FF');
IF res = TRUE
THEN
SELECT * INTO res FROM FUNC_2('a','b','c');
...
ELSE
RETURN FALSE;
END IF;
RETURN res;
END;
$$;
-- (END MAIN STORED PROCEDURE)
-------
--Example of Stored Procedure 1
CREATE OR REPLACE FUNCTION FUNC_1 (a TEXT, b TEXT, c TEXT)
RETURNS boolean
AS $$
DECLARE
BEGIN
-- ****complete data validation before writing to tables****
-- If you hit any invalid data return out of the function before writing any persistent data
-- Ex:
IF COALESCE(a, '') = '' -- could be anything that may produce ERROR_FOUND from your example
THEN
RAISE WARNING 'FUNC_1 | param: a: must have a value';
PERFORM * FROM "Schema".func_errlog('Error','Type of Error',CURRENT_DATE);
RETURN FALSE;
END IF;
RETURN TRUE; -- If you've made it to the end of the function there should be no errors
END;
$$;

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.

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.

How to execute PostgreSQL RAISE command dynamically

How to raise error from PostgreSQL SQL statement if some condition is met?
I tried code below but got error.
CREATE OR REPLACE FUNCTION "exec"(text)
RETURNS text AS
$BODY$
BEGIN
EXECUTE $1;
RETURN $1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
-- ERROR: syntax error at or near "raise"
-- LINE 1: raise 'test'
SELECT exec('raise ''test'' ') WHERE TRUE
In real application TRUE is replaced by some condition.
Update
I tried to extend answer to pass exception message parameters.
Tried code below but got syntax error.
How to pass message parameters ?
CREATE OR REPLACE FUNCTION exec(text, variadic )
RETURNS void LANGUAGE plpgsql AS
$BODY$
BEGIN
RAISE EXCEPTION $1, $2;
END;
$BODY$;
SELECT exec('Exception Param1=% Param2=%', 'param1', 2 );
You cannot call RAISE dynamically (with EXECUTE) in PL/pgSQL - that only works for SQL statements, and RAISE is a PL/pgSQL command.
Use this simple function instead:
CREATE OR REPLACE FUNCTION f_raise(text)
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
RAISE EXCEPTION '%', $1;
END
$func$;
Call:
SELECT f_raise('My message is empty!');
Related:
Generate an exception with a Context
Additional answer to comment
CREATE OR REPLACE FUNCTION f_raise1(VARIADIC text[])
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
RAISE EXCEPTION 'Reading % % %!', $1[1], $1[2], $1[3];
END
$func$;
Call:
SELECT f_raise1('the','manual','educates');
VARIADIC is not a data type, but an argument mode.
Elements have to be handled like any other array element.
To use multiple variables in a RAISE statement, put multiple % into the message text.
The above example will fail if no $3 is passed. You'd have to assemble a string from the variable number of input elements. Example:
CREATE OR REPLACE FUNCTION f_raise2(VARIADIC _arr text[])
RETURNS void
LANGUAGE plpgsql AS
$func$
DECLARE
_msg text := array_to_string(_arr, ' and '); -- simple string construction
BEGIN
RAISE EXCEPTION 'Reading %!', _msg;
END
$func$;
Call:
SELECT f_raise2('the','manual','educates');
I doubt you need a VARIADIC parameter for this at all. Read the manual here.
Instead, define all parameters, maybe add defaults:
CREATE OR REPLACE FUNCTION f_raise3(_param1 text = ''
, _param2 text = ''
, _param3 text = 'educates')
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
RAISE EXCEPTION 'Reading % % %!', $1, $2, $3;
END
$func$;
Call:
SELECT f_raise3('the','manual','educates');
Or:
SELECT f_raise3(); -- defaults kick in