If a stored procedure fails in middle, are changes at that point from the beginning of SP rolled back implicitly or do we have to write any explicit code to make sure that SP runs in a database transaction only?
Strictly speaking, Postgres did not have stored procedures as defined in the ISO/IEC standard before version 11. The term is often used incorrectly to refer to functions, which provide much of the same functionality (and more) as other RDBMS provide with "stored procedures". The main difference being transaction handling.
What are the differences between “Stored Procedures” and “Stored Functions”?
True stored procedures were finally introduced with Postgres 11:
When to use stored procedure / user-defined function?
Functions are atomic in Postgres and automatically run inside their own transaction unless called within an outer transaction. They always run inside a single transaction and succeed or fail completely. Consequently, one cannot begin or commit transactions within the function. And commands like VACUUM, CREATE DATABASE, or CREATE INDEX CONCURRENTLY which do not run in a transaction context are not allowed.
The manual on PL/pgSQL:
Functions and trigger procedures are always executed within a
transaction established by an outer query — they cannot start or
commit that transaction, since there would be no context for them to
execute in. However, a block containing an EXCEPTION clause
effectively forms a subtransaction that can be rolled back without
affecting the outer transaction.
Error handling:
By default, any error occurring in a PL/pgSQL function aborts
execution of the function, and indeed of the surrounding transaction
as well. You can trap errors and recover from them by using a BEGIN
block with an EXCEPTION clause.
There are exceptions, including but not limited to:
data written to log files
changes made to a sequence
Important: Some PostgreSQL data types and functions have special rules
regarding transactional behavior. In particular, changes made to a
sequence (and therefore the counter of a column declared using serial)
are immediately visible to all other transactions and are not rolled
back if the transaction that made the changes aborts.
prepared statements
SQL Fiddle demo
dblink calls (or similar)
Does Postgres support nested or autonomous transactions?
If you are using Postgres 14 procedure like below:
CREATE OR REPLACE PROCEDURE test_error(schema_name text)
LANGUAGE plpgsql
AS
$$
declare
<declare any vars that you need>
BEGIN
<do your thing>
END
$$;
For all practical purposes, code written in between the BEGIN and END block is executed in a single transaction. Hence, if any of the statements in the block fail, all the previous statements will be rolled back automatically. You do not need to explicitly write any roll back code.
However, there are special cases where one can have fine grained control over when to start/commit/rollback transactions. Refer to : https://www.postgresql.org/docs/current/plpgsql-transactions.html for details.
From the official document of Postgresql:
In procedures invoked by the CALL command as well as in anonymous code
blocks (DO command), it is possible to end transactions using the
commands COMMIT and ROLLBACK. A new transaction is started
automatically after a transaction is ended using these commands, so
there is no separate START TRANSACTION command. (Note that BEGIN and
END have different meanings in PL/pgSQL.)
https://www.postgresql.org/docs/11/plpgsql-transactions.html
Related
I have a question but I can never get a clear answer. Any stored
procedure that used a transaction that I have looked at up until my recent job always had a commit transaction + a roll back in case of error. However I have seen a lot of code
at my new job that just has a begin transaction and then a commit at the end with no roll back. I understand why you would use a transaction with a rollback but why would you want to begin a transaction with no roll back? Is it so when you run that code you want to lock the table up so no values can be changed why your code is updating? If so why would you not want the added security of a roll back in case something goes wrong? Is this proper use of the transaction statement? Any thoughts or ideas would be great!
For Example:
BEGIN TRANSACTION [Tran1]
INSERT INTO [Test].[dbo].[T1]
([Title], [AVG])
VALUES ('Tidd130', 130), ('Tidd230', 230)
UPDATE [Test].[dbo].[T1]
SET [Title] = N'az2' ,[AVG] = 1
WHERE [dbo].[T1].[Title] = N'az'
COMMIT TRANSACTION [Tran1]
GO
shouldn't this code be using a roll back syntax for proper use of the begin transaction statement?
The idea is that if that set of transactions needs to be "all or nothing", wrapping the lot in a transaction is the way to ensure that is what will happen. You're not seeing an explicit rollback because that's not what they're guarding against. Imagine the ff scenario with your contrived example:
The insert happens
The server crashes (or the log fills up or some other external reason why things can't continue) before the update can happen
If they're both wrapped in the same transaction, the insert won't be reflected in the table data. Which is the desired behavior.
When transactions are not explicitly declared, SQL Server will automatically BEGIN and COMMIT a TRANSACTION for each command. This frees up each command's lock as soon as the command executes.
When executing multiple commands inside a single transaction (as in the example you posted), locks from all commands are held until the transaction is committed.
Depending on the desired behavior, the script you posted may be correct. However, I would be cautious to ensure that the developer did not mistakenly believe that the transaction would be automatically rolled back on error. If that behavior is desired, you do indeed need to explicitly ROLLBACK or SET XACT_ABORT ON
You use transaction when you need the outcome to be atomic, you would see this alot in financial related procedures where you are gravely worried about data acid consistency . Otherwise it is not necessary and introduces a great deal of locking overhead. There is a good question here and here that goes into great depth.
Edit
The takeaway point is if the procedure is a all or none and must either succeed or fail the correct decision is to use a transaction. If the procedure is not a all or none transaction such as simple insert update etc using a transaction is a) unnecessary and b) can introduce an undue performance overhead due to additional locking.
What is the best approach of breaking long running stored procedures (up to 20 minutes)?
Inside the Stored procedure is wrapped in a transaction. If I close connection will this transaction be rolled back?
Another approach is to start a transaction in C# before I start the stored procedure and when I want to cancel the stored procedure I just need to rollback the C# transaction.
If you close the connection, SQL Server will rollback the transaction if it notices the disconnect before the transaction commits. There'll be a (very) small time window where the transaction might complete just when you disconnect.
A custom transaction adds complexity and has few benefits for a single stored procedure call. So I'd go for the disconnect.
You can set a time out in three place-- the connection, the command, and if you are ultimately programming a web page-- in the page time out.
The relevant time out in this case is the command time out.
Update: Cancelling by a user's event:
To cancel your command by a user event, call Cancel() on the command. I haven't written code to test this, but I suspect that once you call ExecuteReader() it will block, so you'd need an async call-- BeginExecuteNonQuery(), which really is a pain to set up-- it requires extra things on the query string and I think it requires SQL2005+
UPDATE: Re: Transactions
C# (or ADO.NET) transaction code adds about two lines of code and guarantees invocations of stored procedures (which may have more than one statement in them, in not today, maybe a year from now) succeed or fail as a unit. They normally are not a source of poor performance and can be a source of poor performance when not used-- e.g. a long series of inserts runs faster in a transaction.
If you do not call CommitTrans() the transaction will rollback, you do not have to explicitly call Rollback()
Set Timeout at the time of beginning the transaction.
I have a stored procedure (SQL Server) which does nothing more than updates a single row in a table.
Does adding BEGIN TRANSACTION and COMMIT TRANSACTION before/after the update have any effect at all?
No. Transactions ensure that a set of modifying statements are atomic, namely that either all steps succeed or all steps fail. In case there is only one statement, there is no need to use transactions anymore ..
Anyway, T-SQL does that automaticaly. That's why they called it Transact SQL ...
I don't use Stored procedures very often and was wondering if it made sense to wrap my select queries in a transaction.
My procedure has three simple select queries, two of which use the returned value of the first.
In a highly concurrent application it could (theoretically) happen that data you've read in the first select is modified before the other selects are executed.
If that is a situation that could occur in your application you should use a transaction to wrap your selects. Make sure you pick the correct isolation level though, not all transaction types guarantee consistent reads.
Update :
You may also find this article on concurrent update/insert solutions (aka upsert) interesting. It puts several common methods of upsert to the test to see what method actually guarantees data is not modified between a select and the next statement. The results are, well, shocking I'd say.
Transactions are usually used when you have CREATE, UPDATE or DELETE statements and you want to have the atomic behavior, that is, Either commit everything or commit nothing.
However, you could use a transaction for READ select statements to:
Make sure nobody else could update the table of interest while the bunch of your select query is executing.
Have a look at this msdn post.
Most databases run every single query in a transaction even if not specified it is implicitly wrapped. This includes select statements.
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. A group of statements surrounded by BEGIN and COMMIT is sometimes called a transaction block.
https://www.postgresql.org/docs/current/tutorial-transactions.html
I've been sorting out the whole nested transaction thing in SQL server, and I've gleamed these nuggets of understanding of behavior of nested trans':
When nesting transactions, only the
outermost commit will actually
commit.
"Commit Trans txn_name", when nested
, will always apply to the innermost
transaction, even if txn_name refers
to an outer transaction.
"ROLLBACK TRAN" (no name) , even in
an inner transaction, will rollback
all transactions.
"ROLLBACK TRAN txn_name" - txn_name must
refer to the outermost txn name.
If not, it will fail.
Given these , is there any benefit of naming transactions? You cannot use it to target a specific tranasction, either for commit or rollback.
Is it only for code commenting purposes?
Thanks,
Yoni
Effectively it's just a programmers aide memoire. If you're dealing with a Tx that has a number of inner transactions, giving each meaningful names can help you make sure that the tranactions are appropriately nested and may catch logic errors.
You can have procedures rollback only their own work on error, allowing the caller to decide wether to abandon the entire transaction or recover and try an alternate path. See Exception handling and nested transactions for a procedure template that allows this atomic behavior.
The idea is to roll back part of your work, like a nested transaction. Does not always work as intended.
Stored procedures using old-style error handling and savepoints may not work as intended when they are used together with TRY … CATCH blocks: Avoid mixing old and new styles of error handling.
Already discussed here ##ERROR and/or TRY - CATCH