Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to roll back a transaction on error in PostgreSQL?

I'm writing a script for PostgreSQL and since I want it to be executed atomically, I'm wrapping it inside a transaction.
I expected the script to look something like this:

BEGIN
-- 1) Execute some valid actions;
-- 2) Execute some action that causes an error.
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
END; -- A.k.a. COMMIT;

However, in this case pgAdmin warns me about a syntax error right after the initial BEGIN. If I terminate the command there by appending a semicolon like so: BEGIN; it instead informs me about error near EXCEPTION.
I realize that perhaps I'm mixing up syntax for control structures and transactions, however I couldn't find any mention of how to roll back a failed transaction in the docs (nor in SO for that matter).

I also considered that perhaps the transaction is rolled back automatically on error, but it doesn't seem to be the case since the following script:

BEGIN;
-- 1) Execute some valid actions;
-- 2) Execute some action that causes an error.
COMMIT;

warns me that: ERROR: current transaction is aborted, commands ignored until end of transaction block and I have to then manually ROLLBACK; the transaction.

It seems I'm missing something fundamental here, but what?

EDIT:
I tried using DO as well like so:

DO $$
BEGIN
-- 1) Execute some valid actions;
-- 2) Execute some action that causes an error.
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
END; $$

pgAdmin hits me back with a: ERROR: cannot begin/end transactions in PL/pgSQL. HINT: Use a BEGIN block with an EXCEPTION clause instead. which confuses me to no end, because that is exactly what I am (I think) doing.

POST-ACCEPT EDIT: Regarding Laurenz's comment: "Your SQL script would contain a COMMIT. That ends the transaction and rolls it back." - this is not the behavior that I observe. Please consider the following example (which is just a concrete version of an example I already provided in my original question):

BEGIN;

-- Just a simple, self-referencing table.
CREATE TABLE "Dummy" (
    "Id" INT GENERATED ALWAYS AS IDENTITY,
    "ParentId" INT NULL,
    CONSTRAINT "PK_Dummy" PRIMARY KEY ("Id"),
    CONSTRAINT "FK_Dummy_Dummy" FOREIGN KEY ("ParentId") REFERENCES "Dummy" ("Id")
);

-- Foreign key violation terminates the transaction.
INSERT INTO "Dummy" ("ParentId")
VALUES (99);

COMMIT;

When I execute the script above, I'm greeted with: ERROR: insert or update on table "Dummy" violates foreign key constraint "FK_Dummy_Dummy". DETAIL: Key (ParentId)=(99) is not present in table "Dummy". which is as expected. However, if I then try to check whether my Dummy table was created or rolled back like so:

SELECT EXISTS (
    SELECT FROM information_schema."tables"
    WHERE "table_name" = 'Dummy');

instead of a simple false, I get the same error that I already mentioned twice: ERROR: current transaction is aborted, commands ignored until end of transaction block. Then I have to manually terminate the transaction via issuing ROLLBACK;.

So to me it seems that either the comment mentioned above is false or at least I'm heavily misinterpreting something here.

like image 886
Marchyello Avatar asked Aug 19 '20 15:08

Marchyello


People also ask

How do I rollback a transaction in PostgreSQL?

To roll back a prepared transaction, you must be either the same user that executed the transaction originally, or a superuser. But you do not have to be in the same session that executed the transaction. This command cannot be executed inside a transaction block. The prepared transaction is rolled back immediately.

How do I rollback a transaction?

You can see that the syntax of the rollback SQL statement is simple. You just have to write the statement ROLLBACK TRANSACTION, followed by the name of the transaction that you want to rollback.

Can you rollback committed transaction?

After you commit the transaction, the changes are visible to other users' statements that execute after the commit. You can roll back (undo) any changes made during the transaction with the ROLLBACK statement (see ROLLBACK.

Does Postgres support rollback?

Please note: In PostgreSQL, we can rollback DDL objects as well.

Is it possible to rollback a PostgreSQL transaction?

Otherwise, if PostgreSQL implicitly ended the transaction, the parts of the transaction after the error would be executed, which would violate the all-or-nothing atomicity principle. So yes, you have to roll back, but you cannot do that in the PL/pgSQL code: the whole PL/pgSQL code is part of a single transaction.

What is rollback in SQL Server?

ROLLBACK 1 Description. ROLLBACK rolls back the current transaction and causes all the updates made by the transaction to be discarded. 2 Parameters. Optional key words. They have no effect. ... 3 Notes. Use COMMIT to successfully terminate a transaction. Issuing ROLLBACK outside of a transaction block emits a warning and otherwise has no effect.

What happens if rollback is issued outside a transaction block?

Issuing ROLLBACK outside of a transaction block emits a warning and otherwise has no effect. ROLLBACK AND CHAIN outside of a transaction block is an error. The command ROLLBACK conforms to the SQL standard. The form ROLLBACK TRANSACTION is a PostgreSQL extension.

How to undo changes done in transactions in PostgreSQL?

PostgreSQL ROLLBACK command is used to undo the changes done in transactions. As we know transactions in database languages are used for purpose of large computations, for example in banks.


Video Answer


1 Answers

You cannot use ROLLBACK in PL/pgSQL, except in certain limited cases inside procedures.

You don't need to explicitly roll back in your PL/pgSQL code. Just let the exception propagate out of the PL/pgSQL code, and it will cause an error, which will cause the whole transaction to be rolled back.

Your comments suggest that this code is called from an SQL script. Then the solution would be to have a COMMIT in that SQL script at some place after the PL/pgSQL code. That would end the transaction and roll it back.

like image 155
Laurenz Albe Avatar answered Oct 23 '22 18:10

Laurenz Albe