Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a65b1b7

Browse files
committed
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".
1 parent 989b55c commit a65b1b7

File tree

2 files changed

+96
-8
lines changed

2 files changed

+96
-8
lines changed

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.134 2005/03/14 06:19:01 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.135 2005/04/28 13:09:59 momjian Exp $
33
PostgreSQL documentation
44
-->
55

@@ -2049,6 +2049,28 @@ bar
20492049
</listitem>
20502050
</varlistentry>
20512051

2052+
<varlistentry>
2053+
<indexterm>
2054+
<primary>rollback</primary>
2055+
<secondary>psql</secondary>
2056+
</indexterm>
2057+
<term><varname>ON_ERROR_ROLLBACK</varname></term>
2058+
<listitem>
2059+
<para>
2060+
When <literal>on</>, if a statement in a transaction block
2061+
generates an error, the error is ignored and the transaction
2062+
continues. When <literal>interactive</>, such errors are only
2063+
ignored in interactive sessions, and not when reading script
2064+
files. When <literal>off</> (the default), a statement in a
2065+
transaction block that generates an error aborts the entire
2066+
transaction. The on_error_rollback-on mode works by issuing an
2067+
implicit <command>SAVEPONT</> for you, just before each command
2068+
that is in a transaction block, and rolls back to the savepoint
2069+
on error.
2070+
</para>
2071+
</listitem>
2072+
</varlistentry>
2073+
20522074
<varlistentry>
20532075
<term><varname>ON_ERROR_STOP</varname></term>
20542076
<listitem>

src/bin/psql/common.c

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.96 2005/02/22 04:40:52 momjian Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.97 2005/04/28 13:09:59 momjian Exp $
77
*/
88
#include "postgres_fe.h"
99
#include "common.h"
@@ -941,11 +941,13 @@ PrintQueryResults(PGresult *results)
941941
bool
942942
SendQuery(const char *query)
943943
{
944-
PGresult *results;
945-
TimevalStruct before,
946-
after;
947-
bool OK;
948-
944+
PGresult *results;
945+
TimevalStruct before, after;
946+
bool OK, on_error_rollback_savepoint = false;
947+
PGTransactionStatusType transaction_status;
948+
static bool on_error_rollback_warning = false;
949+
const char *rollback_str;
950+
949951
if (!pset.db)
950952
{
951953
psql_error("You are currently not connected to a database.\n");
@@ -973,7 +975,9 @@ SendQuery(const char *query)
973975

974976
SetCancelConn();
975977

976-
if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
978+
transaction_status = PQtransactionStatus(pset.db);
979+
980+
if (transaction_status == PQTRANS_IDLE &&
977981
!GetVariableBool(pset.vars, "AUTOCOMMIT") &&
978982
!command_no_begin(query))
979983
{
@@ -987,6 +991,33 @@ SendQuery(const char *query)
987991
}
988992
PQclear(results);
989993
}
994+
else if (transaction_status == PQTRANS_INTRANS &&
995+
(rollback_str = GetVariable(pset.vars, "ON_ERROR_ROLLBACK")) != NULL &&
996+
/* !off and !interactive is 'on' */
997+
pg_strcasecmp(rollback_str, "off") != 0 &&
998+
(pset.cur_cmd_interactive ||
999+
pg_strcasecmp(rollback_str, "interactive") != 0))
1000+
{
1001+
if (on_error_rollback_warning == false && pset.sversion < 80000)
1002+
{
1003+
fprintf(stderr, _("The server version (%d) does not support savepoints for ON_ERROR_ROLLBACK.\n"),
1004+
pset.sversion);
1005+
on_error_rollback_warning = true;
1006+
}
1007+
else
1008+
{
1009+
results = PQexec(pset.db, "SAVEPOINT pg_psql_temporary_savepoint");
1010+
if (PQresultStatus(results) != PGRES_COMMAND_OK)
1011+
{
1012+
psql_error("%s", PQerrorMessage(pset.db));
1013+
PQclear(results);
1014+
ResetCancelConn();
1015+
return false;
1016+
}
1017+
PQclear(results);
1018+
on_error_rollback_savepoint = true;
1019+
}
1020+
}
9901021

9911022
if (pset.timing)
9921023
GETTIMEOFDAY(&before);
@@ -1005,6 +1036,41 @@ SendQuery(const char *query)
10051036

10061037
PQclear(results);
10071038

1039+
/* If we made a temporary savepoint, possibly release/rollback */
1040+
if (on_error_rollback_savepoint)
1041+
{
1042+
transaction_status = PQtransactionStatus(pset.db);
1043+
1044+
/* We always rollback on an error */
1045+
if (transaction_status == PQTRANS_INERROR)
1046+
results = PQexec(pset.db, "ROLLBACK TO pg_psql_temporary_savepoint");
1047+
/* If they are no longer in a transaction, then do nothing */
1048+
else if (transaction_status != PQTRANS_INTRANS)
1049+
results = NULL;
1050+
else
1051+
{
1052+
/*
1053+
* Do nothing if they are messing with savepoints themselves:
1054+
* If the user did RELEASE or ROLLBACK, our savepoint is gone.
1055+
* If they issued a SAVEPOINT, releasing ours would remove theirs.
1056+
*/
1057+
if (strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 ||
1058+
strcmp(PQcmdStatus(results), "RELEASE") == 0 ||
1059+
strcmp(PQcmdStatus(results), "ROLLBACK") ==0)
1060+
results = NULL;
1061+
else
1062+
results = PQexec(pset.db, "RELEASE pg_psql_temporary_savepoint");
1063+
}
1064+
if (PQresultStatus(results) != PGRES_COMMAND_OK)
1065+
{
1066+
psql_error("%s", PQerrorMessage(pset.db));
1067+
PQclear(results);
1068+
ResetCancelConn();
1069+
return false;
1070+
}
1071+
PQclear(results);
1072+
}
1073+
10081074
/* Possible microtiming output */
10091075
if (OK && pset.timing)
10101076
printf(_("Time: %.3f ms\n"), DIFF_MSEC(&after, &before));

0 commit comments

Comments
 (0)