Can I roll back an executed procedure in a transaction?
Begin Transaction
Exec dbo.InsertIntoCustomerTable
Rollback Transaction
Is this valid?
Of course you can.
It is very useful to test procedure on production for any errors or model problems. The best way is to put TRY .... CATCH block and raport errors. That's great way to test procedure.
Related
I am currently writing dwh loaders which get data from restapi then mainly inserts from database to database. There is log table with after insert trigger which call a "starter" function which starts the loading functon(s). So basically:
API -> check that last loading has FINNISHED succesfully -> insert to table -> insert to log table -> trigger (wait for FINNISH status) -> starter function -> loading function.
But i afriad that if something goes wrong with the procedure and it rolls back the full transaction (or at least there wont be finnished status)
Currently i use:
exception when others then
However im curios that is there any other error handling for trigger called procedure? Do you have any best practises?
Thank you in advance!
Never use WHEN OTHERS THEN. That will "swallow" errors you don't want to miss, like data corruption or other unexpected runtime events.
Normally, you shouldn't handle any exceptions in your trigger. Instead, write good code that does not cause an error under normal circumstances. You want that trigger to succeed, right? Under normal circumstances, you want the operation to fail if the trigger has a problem.
If you have to, catch and handle specific exceptions that are to be expected under ordinary circumstances.
We have translated our version of the program to work with PostgreSQL. She previously worked with MSSQL. But we ran into strange behavior in case of an error in PostgreSQL. If an error occurs during the execution of the script, the transaction will go into the interrupted status and wait for a commit or rollback call. In this case, this connection will continue to accept requests, but each call will lead to the error "the current transaction is interrupted, commands are ignored until the end of the transaction block.". All we could find is advice on how to call rollback or commit, but it is not possible to follow this on multiple servers. We will naturally fix the error that led to the crash, but later. This issue leads to severe server side crashes. Isn't it logical to rollback automatically? Couldn't execute the script, rollback and continue working. Here's what I think is logical. Please convince me if I am wrong.
We are using NpgSql to work with PostgreSQL, maybe he can help us with our problem? Perhaps there is some way to close the problematic connection or stop using it before committing the transaction?
We are using PostgreSQL 9.5 version. Our application is written in ASP.NET.
UPD, after an error in the script, the process [62493] generates many errors of this kind:
14:05:58.827 [62493] ERROR: DISCARD ALL cannot be executed inside a transaction block
14:05:58.827 [62493] OPERATOR: DISCARD ALL
14:05:58.827 [62493] ERROR: current transaction is aborted, commands ignored until end of transaction block
14:05:58.827 [62493] OPERATOR: BEGIN
14:05:58.827 [62493] ERROR: current transaction is aborted, commands ignored until end of transaction block
14:05:58.827 [62493] OPERATOR: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
14:05:58.827 [62493] ERROR: current transaction is aborted, commands ignored until end of transaction block
*Among these calls are calls to real stored procedures from the database.
Let me convince you. Observe this transaction:
/* poor man's table reorganization */
BEGIN;
CREATTE TABLE reorganized (LIKE mydata); -- syntax error
INSERT INTO reorganized SELECT * FROM mydata;
DROP TABLE mydata;
ALTER TABLE reorganized RENAME TO mydata;
COMMIT;
If an error would automatically cause a rollback, or if the statement with the error were rolled back, but the transaction could continue, the above transaction would lose the data in mydata.
Moreover, such a behavior would violate the atomicity guarantee of a transaction: either all statements succeed or no statement succeeds.
To work around this, you can use standard SQL savepoints:
BEGIN;
/* do some work */
SAVEPOINT a;
/* perform a statement that might fail */
ROLLBACK TO SAVEPOINT a; -- only if the statement above fails
/* continue doing work */
COMMIT;
But don't use more than 64 savepoints per transaction if you want good performance.
As far as I know, we can't use start transaction within functions, thus we can't use COMMIT and ROLLBACK in functions.
But how then we ROLLBACK by some if-condition?
How then we can perform a sequence of statements in a specific level of isolation? I mean a situation when an application wants to call a SQL (plpgsql) function and that function really needs to be run in a transaction with a certain isolation level. What to do in such a case?
In which cases then it is really practical to run ROLLBACK? Only when we manually write a script, check something and then ROLLBACK manually if we don't like the result. And in the same case, I see the practicality of savepoints. However, I feel like it is a serious constraint.
If you want to rollback the complete transaction, RAISE an exception.
If you only want to roll back part of your work, start a new block with a BEGIN at the point to which you want to roll back and add an EXCEPTION clause to the block.
Since the transaction is started outside the function, the isolation level already has to be set properly when you are in the function.
You can query
SELECT current_setting('transaction_isolation', TRUE);
and throw an error if the setting is not correct.
is too general or too simple to answer.
You roll back a transaction if you have reached a point in your processing where you want to undo everything you have done so far in the transaction.
Often, that happens implicitly rather than explicitly by throwing an error.
Let me open by saying: yes, I am aware of Determine if a transaction is active (Postgres)
Unfortunately the sole answer to that question is far too specific to the use case provided, and doesn't actually indicate whether or not a transaction is active.
The select txid_current(); trick suggested by How to check for pending operations in a PostgreSQL transaction doesn't appear to work - I always get the same transaction ID from adjacent calls to that function. Possibly this is because I'm trying to test it from pgAdmin, which is transparently starting transactions...? (Note: I don't actually care whether there are any pending changes or active locks, so looking at pg_locks isn't helpful - what if nothing's been touched since the transaction was started?)
So: How can I determine in PostgreSQL PL/pgSQL code if a transaction is currently active?
One possible use case is: the SP/FN in question will be doing its own explicit transaction management, and calling it with a transaction already active will greatly interfere with that. I want to raise an error so that the coding mistake of calling this SP/FN in a transaction can be corrected.
There are other use cases, though.
Ideally what I'm looking for is an equivalent to MSSQL's ##TRANCOUNT (though I don't really care how deeply the transactions may be nested...)
Postgres runs PL/pgSQL inside the transaction. Thus you can't control transaction from inside PL/pgSQL. Code will look like:
begin;
select plpgsql_fn();
do '/*same any plpgsql*/';
end;
So answering your question:
If you have PL/pgSQL running ATM, you have your transaction active ATM...
Of course you can do some trick, like starting/ending work over dblink or such. but then you can check select txid_current(); over the dblink successfully...
If you want to determine if there have been any data modifications in your transaction, call txid_current_if_assigned(). It returns NULL if nothing has been modified yet.
If you only want to know if you are inside some transaction, you can save yourself the trouble, because you always are.
Before PostgreSQL v11, you cannot use transaction control statements in a function.
I haven't found a clean way to do that, but you can always call BEGIN and if it succeeds it means there is no transaction in progress (don't forget to rollback). If it fails with "there is already a transaction in progress" this means you are within transaction (better not to rollback then).
I have a problem with existing database code (a trigger) that call a function trigger that use the NOTIFY command, which is not supported in the context of a prepared transaction.
My question is simple : from the function trigger, is there a way to detect that we are in the context of a prepared transaction ?
Thanks in advance.
There is no way to detect that the current transaction will be committed using prepared transactions and two-phase commit, because you haven't PREPAREd the transaction yet; the transaction has no idea it's going to be subjected to two-phase commit until after your trigger runs. PostgreSQL doesn't require that you BEGIN TRANSACTION FOR TWO PHASE COMMIT (imaginary syntax) or anything like that.
You can test for max_prepared_transactions > 0 in pg_settings to see if prepared transactions are enabled, but there's no way to know if 2PC will be used until it happens.