From: Bruce Momjian Date: Thu, 28 Apr 2005 13:09:59 +0000 (+0000) Subject: Add psql \set ON_ERROR_ROLLBACK to allow statements in a transaction to X-Git-Tag: REL8_1_0BETA1~907 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a65b1b738c07c98baaf38289eccd1b5fa054453d;p=postgresql Add psql \set ON_ERROR_ROLLBACK to allow statements in a transaction to error without affecting the entire transaction. Valid values are "on|interactive|off". --- diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 3a80b7f694..41de5d5e7e 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1,5 +1,5 @@ @@ -2049,6 +2049,28 @@ bar + + + rollback + psql + + ON_ERROR_ROLLBACK + + + When on, if a statement in a transaction block + generates an error, the error is ignored and the transaction + continues. When interactive, such errors are only + ignored in interactive sessions, and not when reading script + files. When off (the default), a statement in a + transaction block that generates an error aborts the entire + transaction. The on_error_rollback-on mode works by issuing an + implicit SAVEPONT for you, just before each command + that is in a transaction block, and rolls back to the savepoint + on error. + + + + ON_ERROR_STOP diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 6d03a4ae5a..0feec434e2 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.96 2005/02/22 04:40:52 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.97 2005/04/28 13:09:59 momjian Exp $ */ #include "postgres_fe.h" #include "common.h" @@ -941,11 +941,13 @@ PrintQueryResults(PGresult *results) bool SendQuery(const char *query) { - PGresult *results; - TimevalStruct before, - after; - bool OK; - + PGresult *results; + TimevalStruct before, after; + bool OK, on_error_rollback_savepoint = false; + PGTransactionStatusType transaction_status; + static bool on_error_rollback_warning = false; + const char *rollback_str; + if (!pset.db) { psql_error("You are currently not connected to a database.\n"); @@ -973,7 +975,9 @@ SendQuery(const char *query) SetCancelConn(); - if (PQtransactionStatus(pset.db) == PQTRANS_IDLE && + transaction_status = PQtransactionStatus(pset.db); + + if (transaction_status == PQTRANS_IDLE && !GetVariableBool(pset.vars, "AUTOCOMMIT") && !command_no_begin(query)) { @@ -987,6 +991,33 @@ SendQuery(const char *query) } PQclear(results); } + else if (transaction_status == PQTRANS_INTRANS && + (rollback_str = GetVariable(pset.vars, "ON_ERROR_ROLLBACK")) != NULL && + /* !off and !interactive is 'on' */ + pg_strcasecmp(rollback_str, "off") != 0 && + (pset.cur_cmd_interactive || + pg_strcasecmp(rollback_str, "interactive") != 0)) + { + if (on_error_rollback_warning == false && pset.sversion < 80000) + { + fprintf(stderr, _("The server version (%d) does not support savepoints for ON_ERROR_ROLLBACK.\n"), + pset.sversion); + on_error_rollback_warning = true; + } + else + { + results = PQexec(pset.db, "SAVEPOINT pg_psql_temporary_savepoint"); + if (PQresultStatus(results) != PGRES_COMMAND_OK) + { + psql_error("%s", PQerrorMessage(pset.db)); + PQclear(results); + ResetCancelConn(); + return false; + } + PQclear(results); + on_error_rollback_savepoint = true; + } + } if (pset.timing) GETTIMEOFDAY(&before); @@ -1005,6 +1036,41 @@ SendQuery(const char *query) PQclear(results); + /* If we made a temporary savepoint, possibly release/rollback */ + if (on_error_rollback_savepoint) + { + transaction_status = PQtransactionStatus(pset.db); + + /* We always rollback on an error */ + if (transaction_status == PQTRANS_INERROR) + results = PQexec(pset.db, "ROLLBACK TO pg_psql_temporary_savepoint"); + /* If they are no longer in a transaction, then do nothing */ + else if (transaction_status != PQTRANS_INTRANS) + results = NULL; + else + { + /* + * Do nothing if they are messing with savepoints themselves: + * If the user did RELEASE or ROLLBACK, our savepoint is gone. + * If they issued a SAVEPOINT, releasing ours would remove theirs. + */ + if (strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 || + strcmp(PQcmdStatus(results), "RELEASE") == 0 || + strcmp(PQcmdStatus(results), "ROLLBACK") ==0) + results = NULL; + else + results = PQexec(pset.db, "RELEASE pg_psql_temporary_savepoint"); + } + if (PQresultStatus(results) != PGRES_COMMAND_OK) + { + psql_error("%s", PQerrorMessage(pset.db)); + PQclear(results); + ResetCancelConn(); + return false; + } + PQclear(results); + } + /* Possible microtiming output */ if (OK && pset.timing) printf(_("Time: %.3f ms\n"), DIFF_MSEC(&after, &before));