PostgreSQL function is always called inside a transaction - postgresql

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.

Related

PostgreSQL: Allow only one instance of stored procedure to run at a time

I have a stored procedure on Postgres, which processes large data and takes a good time to complete.
In my application, there is a chance that 2 processes or schedulers can run this procedure at same time. I want to know if there is a built in mechanism in db to allow only instance of this procedure to run at db level.
I searched the internet, but didn't find anything concrete.
There is nothing built in to define a procedure (or function) so that concurrent execution is prevented.
But you can use advisory locks to achieve something like that.
At the very beginning of the procedure, you can add something like:
perform pg_advisory_lock(987654321);
which will then wait to get the lock. If a second session invokes the procedure it will have to wait.
Make sure you release the lock at the end of the procedure using pg_advisory_unlock() as they are not released when the transaction is committed.
If you use advisory locks elsewhere, make sure you use a key that can't be used in other places.

Execute Postgres query only if a lock is granted

I have some SQL queries which will be executed in a clustered environment. I'm trying to use a Postgres advisory lock to prevent multiple executions of these queries.
Begin transaction.
Execute pg_try_advisory_xact_lock and check the boolean return value.
If true (the lock is acquired), then proceed with the actual business-logic queries (arbitrary).
If false (another instance is already holding the exclusive lock), then do nothing.
End transaction.
Note that I'm using the non-blocking _try_ variant which returns a boolean. If the lock is not immediately available, some other instance is already executing the command, so nothing needs to be done.
The question "PostgreSQL IF statement" suggests embedding a PL/pgSQL IF clause using DO. Is there a way to do this without this extra complexity?

SSMS - Do some statements under transaction then interactively inspect before rollback/commit?

In building a script that will run against a production SQL Server I'd like to build and test it interactively.
I.e., create a script with a BEGIN TRANSACTION followed with some statements to delete and/or insert and/or update, possibly in batches if required. Then I'd like to execute the script in a query window and with the transaction still active proceed to query the database (in that window? in a different window?) in various ways to see how it would look if the transaction was committed, and then finally rollback.
Is this possible? Or what should I be doing instead?
It would be best to do testing on a PreProd server, but it's technically possible to do what you're saying.
If you begin a transaction and then run some statements, with no COMMIT, you can then query the affected tables in another window, by first declaring SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED. When you're done, you can then go back to the first window and execute a ROLLBACK statement.

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

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.

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