Why is not possible to use Commit and rollback in a PostgreSQL procedure? - postgresql

I am creating procedures on a PostgreSQL database. I read about is not possible to use rollback inside these procedures.
Why?
Is it possible to use commit?
I guess that this is related with ACID properties, but what if we have two insert operations in a procedure. If the second fails the second one gets a rollback?
Thank you.

Postgres' overview gives a hint by explaining how their functions are different than traditional stored procedures:
Functions created with PL/pgSQL can be used anywhere that built-in functions could be used. For example, it is possible to create complex conditional computation functions and later use them to define operators or use them in index expressions.
This makes it awkward to support transactions within functions for every possible situation. From the docs:
Functions and trigger procedures are always executed within a transaction established by an outer query... However, a block containing an EXCEPTION clause effectively forms a subtransaction that can be rolled back without affecting the outer transaction.
If you were to have two INSERTs in a function, the first can be wrapped in an EXCEPTION block to catch any errors and decide if the second should be executed.

You are correct. You cannot rollback transactions that were started prior to the procedure from within the procedure. In addition, a transaction cannot be created within the procedure either. You would receive this error:
ERROR: cannot begin/end transactions in PL/pgSQL
SQL state: 0A000
Hint: Use a BEGIN block with an EXCEPTION clause instead.
As this error states, and as Matt mentioned, you could use an exception block to essentially perform a rollback. From the help:
When an error is caught by an EXCEPTION clause, the local variables of the PL/pgSQL function remain as they were when the error occurred, but all changes to persistent database state within the block are rolled back.
Alternatively, you could call the procedure from within a transaction, and roll that back if necessary.

Related

In a DB2 SQL function, is there an equivalent to the Finally block in JAVA and .Net..?

In DB2-400 v7.3, do functions or procedures have an equivalent to the Finally block as found in JAVA..? To quote the documentation:
The Finally block always executes when the try block exits.
It's used for cleanup purposes needed by that section of code. In a DB2 function, it could be used for dropping temporary objects, such as Declared global temporary tables.
If DB2 doesn't have such a feature, it's not the end of the world. It can easily be simulated in code.
Global Temporary Tables go away when the job ends, so no need to clean them up. SQL does not have a try() catch() block, so no there is no finally clause either.
You can write stored procedures in Java if you need that functionality, but I would expect those to be less performant as SQL stored procedures are compiled using C (which also doesn't have a finally block).
Alternatively you could write your stored procedures with RPGLE sub-procedures which do have an on-exit block which runs when the sub-procedure ends whether that is a normal or an abnormal end.

PostgreSQL function is always called inside a transaction

I've been trying to research if postgres functions are always run as a single transaction and have found hints in various answers but no links back to the official documentation. Can somebody cite a source that states postgres functions are always run inside a single transaction?
Have read through the documentation here but no luck: https://www.postgresql.org/docs/12/plpgsql-transactions.html
The functions are just being called via a select call and no transaction BEGIN/COMMIT calls are used:
select ccdb.fn_automation_for_updation()
In short I am going through a code base where a user is trying to solve concurrency issues inside a function but think a race condition is present.
In the documentation here it says:
PostgreSQL actually treats every SQL statement as being executed within a transaction. If you do not issue a BEGIN command, then each individual statement has an implicit BEGIN and (if successful) COMMIT wrapped around it.
In other words, even a single SELECT happens inside a transaction even if no specific BEGIN command was issued.
Functions that are called from a query will execute in the context of the transaction established by the query.
Procedures executed using CALL or DO do not automatically execute in a transaction; procedures should manage their own transactions. See: plpgsql transactions.

Subtransactions in functions that can commit

I read that I can use a BEGIN-EXCEPTION block to have a subtransaction in a FUNCTION that can be rolled back. But why is it not possible to commit this subtransaction?
How can I circumvent the "all-or-nothing" transaction behavior of functions written in PL/pgSQL? Is it possible to have the function make commits using subtransactions while the outer transaction could be rolled back?
You can't circumvent it at the time of writing (PG 9.3).
Or more precisely, not directly. You can mimic autonomous subtransactions by using dblink, but be wary that doing so is a can of worms: what's supposed to happen, for instance, if your outer transaction is rolled back?
For background and references to discussions related to the topic on the PG-Hackers list, see:
http://wiki.postgresql.org/wiki/Autonomous_subtransactions
http://www.postgresql.org/message-id/20111218082812.GA14355#leggeri.gi.lan

Modify Trigger in Postgresql

I need to modify a Trigger (which use a particular FUNCTION) already defined and it is being in use. If i modify it using CREATE OR REPLACE FUNCTION, what is the behaviour of Postgres? will it "pause" the old trigger while it is updating the function?. As far as i know, Postgres should execute all the REPLACE FUNCTION in one transaction (so the tables are locked and so the triggers being modify while it is updating, then next transactions locked will use the new FUNCTION not the old one. is it correct?
Yes. According to the documentation:
http://www.postgresql.org/docs/9.0/static/explicit-locking.html
Also, most PostgreSQL commands automatically acquire locks of appropriate modes to ensure that referenced tables are not dropped or modified in incompatible ways while the command executes. (For example, ALTER TABLE cannot safely be executed concurrently with other operations on the same table, so it obtains an exclusive lock on the table to enforce that.)
will it "pause" the old trigger while it is updating the function?
It should continue executing the old trigger functions when calls are in progress (depending on the isolation level, subsequent calls in the same transaction should use the old definition too; I'm not 100% sure the default level would do so, however), block new transactions that try to call the function while it's being updated, and execute the new function once it's replaced.
As far as i know, Postgres should execute all the REPLACE FUNCTION in one transaction (so the tables are locked and so the triggers being modify while it is updating, then next transactions locked will use the new FUNCTION not the old one. is it correct?
Best I'm aware the function associated to the trigger doesn't lock the table when it's updated.
Please take this with a grain of salt, though: the two above statements amount to what I'd intuitively expect mvcc to do, rather than knowing this area of Postgres' source code off the top of my head. (A few core contributors periodically come to SO, and might eventually chime in with a more precise answer.)
Note that this is relatively straightforward to test, that being said: open two psql sessions, open two transactions, and see what happens...

Advisory locks in postgres and evaluation order (how to acquire lock without using a separate query)

Is there any safe way of acquiring an advisory lock before executing a particular statement without using two separate queries? E.g., I assume that if I do something like the following, there is no guarantee that the lock will be acquired before the insert:
WITH x AS (SELECT pg_advisory_lock(1,2)) INSERT ...
But is there some similar way of getting the desired effect?
I'm pretty sure that SQL standards require implementations to behave as if the very first thing they do is to effectively materialize the common table expressions in the WITH clause. PostgreSQL complies with this requirement.
Common table expressions behave (mostly) as named objects. Multiple CTEs are materialized in the order they're declared. Backward references by name work as you'd expect, and forward references by name raise an error.
So I'm pretty sure that, in the general case, the CTE will have to materialize before the INSERT statement will run. But in your case, using PostgreSQL, I'm not dead certain, and here's why.
PostgreSQL's implementation [of common table expressions]
evaluates only as many rows of a WITH query as are actually
fetched by the parent query.
I'm not sure an INSERT statement fetches a row in this sense.
You haven't really told us enough about your use case to be sure, but in general for explicit locks to be useful in PostgreSQL they need to be acquired before the transaction acquires its snapshot. Advisory locks can be acquired before you start the transaction, and most locks with transactional scope should be acquired right after your begin your transaction; before anything which will need a transaction ID.
If you really don't need to acquire the lock before you have your transaction ID assigned and your snapshot set, and it is important to you that you issue one statement to acquire the lock and perform the insert, create a function which does both.