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".
This commit is contained in:
Bruce Momjian 2005-04-28 13:09:59 +00:00
parent 989b55c550
commit a65b1b738c
2 changed files with 96 additions and 8 deletions

View File

@ -1,5 +1,5 @@
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.134 2005/03/14 06:19:01 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.135 2005/04/28 13:09:59 momjian Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
@ -2049,6 +2049,28 @@ bar
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<indexterm>
<primary>rollback</primary>
<secondary>psql</secondary>
</indexterm>
<term><varname>ON_ERROR_ROLLBACK</varname></term>
<listitem>
<para>
When <literal>on</>, if a statement in a transaction block
generates an error, the error is ignored and the transaction
continues. When <literal>interactive</>, such errors are only
ignored in interactive sessions, and not when reading script
files. When <literal>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 <command>SAVEPONT</> for you, just before each command
that is in a transaction block, and rolls back to the savepoint
on error.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>ON_ERROR_STOP</varname></term> <term><varname>ON_ERROR_STOP</varname></term>
<listitem> <listitem>

View File

@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2005, PostgreSQL Global Development Group * 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 "postgres_fe.h"
#include "common.h" #include "common.h"
@ -941,11 +941,13 @@ PrintQueryResults(PGresult *results)
bool bool
SendQuery(const char *query) SendQuery(const char *query)
{ {
PGresult *results; PGresult *results;
TimevalStruct before, TimevalStruct before, after;
after; bool OK, on_error_rollback_savepoint = false;
bool OK; PGTransactionStatusType transaction_status;
static bool on_error_rollback_warning = false;
const char *rollback_str;
if (!pset.db) if (!pset.db)
{ {
psql_error("You are currently not connected to a database.\n"); psql_error("You are currently not connected to a database.\n");
@ -973,7 +975,9 @@ SendQuery(const char *query)
SetCancelConn(); SetCancelConn();
if (PQtransactionStatus(pset.db) == PQTRANS_IDLE && transaction_status = PQtransactionStatus(pset.db);
if (transaction_status == PQTRANS_IDLE &&
!GetVariableBool(pset.vars, "AUTOCOMMIT") && !GetVariableBool(pset.vars, "AUTOCOMMIT") &&
!command_no_begin(query)) !command_no_begin(query))
{ {
@ -987,6 +991,33 @@ SendQuery(const char *query)
} }
PQclear(results); 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) if (pset.timing)
GETTIMEOFDAY(&before); GETTIMEOFDAY(&before);
@ -1005,6 +1036,41 @@ SendQuery(const char *query)
PQclear(results); 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 */ /* Possible microtiming output */
if (OK && pset.timing) if (OK && pset.timing)
printf(_("Time: %.3f ms\n"), DIFF_MSEC(&after, &before)); printf(_("Time: %.3f ms\n"), DIFF_MSEC(&after, &before));