diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 3a80b7f6940..41de5d5e7eb 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 6d03a4ae5a1..0feec434e20 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));