How to call a procedure with Rollback in the end? - postgresql

I am working with PostgreSQL with DBeaver. I would like for test purposes to make a procedure call in a transaction; then to Rollback it. I have written the following script:
DO $$
BEGIN
call MyProcedure();
ROLLBACK $$;
I have tried to run it in DBeaver console. I have got the following error:
SQL Error [42601]: ERROR: syntax error at end of input
It pointed out to the final $$.
What is wrong; and how could I reach my goal: to make a procedure call in a transaction; then to Rollback it?

For the DO block, you are missing the end
DO $$
BEGIN
RAISE NOTICE 'test';
ROLLBACK;
END $$;
But since you want to display a table content, you can't use an anonymous block but instead just start/end the transaction.
BEGIN;
select * from test;
call my_procedure();
select * from test;
ROLLBACK;

Related

PL/pgSQL procedures and transaction control

I'm new to Postgres, but with experience from Oracle. Trying to create a stored procedure which is going to:
Insert a row
Handle exceptions and in case of an exception insert a row into a log table by calling dedicated procedure
Emit an audit log record into a log table in case the whole procedure ran successfully
By pseudo code:
CREATE OR REPLACE PROCEDURE test.p_insert(IN p_test_param character varying)
LANGUAGE 'plpgsql'
SECURITY DEFINER
AS $BODY$
DECLARE
-- some declarations
BEGIN
BEGIN
INSERT INTO test.a(a) VALUES (p_test_param);
EXCEPTION
WHEN OTHERS THEN
-- GET STACKED DIAGNOSTICS
CALL test.p_insert_log(...); -- Inserts a row into a log table, another COMMIT may be required?
RAISE;
END;
COMMIT; -- CAN'T DO
BEGIN
IF (SELECT test.f_debug()) THEN
CALL test.p_insert_log(...); -- Audit the execution
END IF;
END;
COMMIT; -- CAN'T DO EITHER
END;
$$BODY$$;
However when I try to test the procedure out from an anonymous block in PgAdmin such as:
BEGIN;
DO
LANGUAGE plpgsql
$$
BEGIN
CALL test.p_insert(
p_test_param => 'test'
);
END;
$$
I'm getting an error ERROR: invalid transaction termination. How can I get rid of it? My objective is to let the procedure carry out the transaction control, I don't want the caller to COMMIT or ROLLBACK anything. If I remove both COMMIT commands from the code of the procedure, it executes well, however the invoker must explicitly COMMIT or REVOKE the transaction afterwards, which is not desired. In Oracle the pseudo code with COMMIT statements would work, in Postgres it doesn't seem to work as I would like to. Could you please help me out? Thanks
Your code will work as intended. Perhaps you made some mistake in calling the code:
you cannot call the procedure from a function
you cannot call the procedure in an explicitly started transaction:
BEGIN;
CALL p_insert('something); -- will fail
COMMIT;

transaction with postgresql

I am new to Postgresql and I try to do something so simple with Oracle.
I created a procedure with a simple code inside :
CREATE OR REPLACE PROCEDURE user.test_proc(IN param1 character varying)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
i text;
BEGIN
if param1 = '1' then
insert into amian.test values ('1', '1');
rollback;
else
insert into amian.test values('2','2');
commit;
end if;
end;
$BODY$;
From unix environement I use psql :
psql –p port–d base–U user
\set AUTOCOMMIT off
call user.test_proc('2');
I have this error when the programm meets the COMMIT :
invalid transaction termination
What is wrong ? what should I do (I want to be with autocommit off) ?
Thank you
You can only use COMMIT and ROLLBACK in a procedure if the procedure is running in its own transaction (autocommit mode). See this quote from the documentation:
If CALL is executed in a transaction block, then the called procedure cannot execute transaction control statements. Transaction control statements are only allowed if CALL is executed in its own transaction.
Turning off autocommit in psql causes the client to send a BEGIN before the CALL statement, so you are in an explicitly started transaction. That causes the problem.
This restriction may be lifted at some point in the future, but currently that's the way it is.
Don't disable autocommit. You are risking long transactions, which can impair the health of your database.

call procedure from a function in postgresql

I have a procedure which has commit in it (postgresql v11), would like to call his procedure from a function. Getting below error as the procedure has commit, when commit is removed it works fine. Is there any work around to be able to call procedure with transaction control statements from a function (I know function does not support transactions and thats why its failing, but want to know if there is any alternative?)
CREATE OR REPLACE FUNCTION public.f()
RETURNS text
LANGUAGE plpgsql
AS $function$
BEGIN
raise notice 'Starting Function!!!';
call public.p();
return 'success' ;
END;
$function$;
CREATE OR REPLACE PROCEDURE public.p() LANGUAGE plpgsql AS $$
DECLARE src_schema TEXT;
BEGIN
raise notice 'Starting Procedure!!!';
commit;
RETURN;
END;
$$;
-- if the procedure has commit then its failing
imaods=> select public.f();
NOTICE: Starting Function!!!
NOTICE: Starting Procedure!!!
ERROR: invalid transaction termination
CONTEXT: PL/pgSQL function p() line 5 at COMMIT
SQL statement "CALL public.p()"
PL/pgSQL function f() line 4 at CALL
-- if the commit is removed from the procedure then it works fine
imaods=> CREATE OR REPLACE PROCEDURE public.p() LANGUAGE plpgsql AS $$
imaods$> DECLARE src_schema TEXT;
imaods$> BEGIN
imaods$> raise notice 'Starting Procedure!!!';
imaods$> RETURN;
imaods$> END;
imaods$> $$;
CREATE PROCEDURE
imaods=>
imaods=> select public.f();
NOTICE: Starting Function!!!
NOTICE: Starting Procedure!!!
f
---------
success
(1 row)
The documentation explains that:
Transaction control is only possible in CALL or DO invocations from the top level or nested CALL or DO invocations without any other intervening command. For example, if the call stack is CALL proc1() → CALL proc2() → CALL proc3(), then the second and third procedures can perform transaction control actions. But if the call stack is CALL proc1() → SELECT func2() → CALL proc3(), then the last procedure cannot do transaction control, because of the SELECT in between.
The reason is that you cannot have transaction management in PostgreSQL functions.

Postgresql how to multiple stored procedure in transaction

I have many stored procedure in my postgresql db,
and for some reason i need to run many procedure in transaction so if there is a error it will rollback.
is there any way to do this?
edit 1
i run this through java and for some reason i cant make transaction from java and i cant run query string, just store procedure only.
I actually thinking making procedure like this
CREATE OR REPLACE FUNCTION ldt_pricing_rule_v1_api.start()
RETURNS VOID
LANGUAGE PLPGSQL
SECURITY DEFINER
AS $$
BEGIN
EXECUTE 'begin transaction'
RETURN;
END
$$;
select ldt_pricing_rule_v1_api.start();
but it's will display this
ERROR: cannot begin/end transactions in PL/pgSQL
HINT: Use a BEGIN block with an EXCEPTION clause instead.
BEGIN ... COMMIT should to work.
BEGIN
SELECT func1();
SELECT func2();
COMMIT;
PostgreSQL 11 (it is not released yet) has procedures where you can control transactions explicitly. Procedures are started by CALL statement like any other databases. Now, PostgreSQL functions doesn't allow control transactions (explicitly).
Any PostgreSQL function is executed under transaction - explicitly started by user (like my example), or implicitly started by system (by autocommit mode).
So outer BEGIN starts explicit transaction:
BEGIN
SELECT func1();
SELECT func2();
COMMIT;
and if there is any unhandled fail, then only ROLLBACK command is available.
or implicit transaction:
CREATE OR REPLACE FUNCTION outerfx()
RETURNS void AS $$
BEGIN
PERFORM func1();
PERFORM func2();
END;
$$ LANGUAGE plpgsql;
SELECT outerfx(); -- starts outer transaction implicitly.
Now, functions func1, func2 are executed under transaction too.

How to save query errors in plpgsql to a table?

I need to save in a table the error code (SQLSTATE) and the error message (SQLERRM) returned by an INSERT or an UPDATE. My procedure must execute an INSERT, and if an error occurs, it must be saved into an apposite table.
But the problem is that if I use an EXCEPTION block, when an error occurs the transaction is aborted and any command after cannot execute.
How can I save the error returned by a query in a table using PLPGSQL?
There are two possible solutions:
use a CSV format of PostgreSQL log. Later you can import pg log to table by \copy statement. This way is preferred if it is possible, because it has minimal negative impact on performance.
You can emulate autonomous transactions via more techniques
PostgreSQL dblink
PostgreSQL FDW driver
Example of dblink based emulation (by Jon Roberts and me):
CREATE OR REPLACE FUNCTION fn_log_error(_function varchar,
_location int, _error varchar)
RETURNS void AS $$
DECLARE
_sql varchar;
_exec_error varchar;
BEGIN
PERFORM dblink_connect('autonom_connection', 'dbname=...');
_sql := format('INSERT INTO error_log (function_name, location,
error_message, error_time) VALUES (%L, %s, %L, %L)',
_function, _location, _error, clock_timestamp());
PERFORM dblink_exec('autonom_connection', _sql, false);
_exec_error := dblink_error_message('autonom_connection');
IF position('ERROR' in _exec_error) > 0
OR position('WARNING' in _exec_error) > 0 THEN
RAISE EXCEPTION '%', _exec_error;
END IF;
PERFORM dblink_disconnect('autonom_connection');
EXCEPTION
WHEN others THEN
PERFORM dblink_disconnect('autonom_connection');
RAISE EXCEPTION '(%)', SQLERRM;
END;
$$ LANGUAGE plpgsql;
Some other examples:
http://raghavt.blogspot.cz/2012/05/autonomous-transaction-in-postgresql-91.html
http://tapoueh.org/blog/2013/10/14-autonomous-transactions