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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy