diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 47bf3342a59..ababd371dfa 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -584,8 +584,10 @@ EOF
psql to issue a BEGIN command
before the first such option and a COMMIT command after
the last one, thereby wrapping all the commands into a single
- transaction. This ensures that either all the commands complete
- successfully, or no changes are applied.
+ transaction. If any of the commands fails, a
+ ROLLBACK command is sent instead. This ensures that
+ either all the commands complete successfully, or no changes are
+ applied.
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index ddff9039158..574f49d4c9b 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -426,7 +426,9 @@ main(int argc, char *argv[])
if (options.single_txn)
{
- if ((res = PSQLexec("COMMIT")) == NULL)
+ res = PSQLexec((successResult == EXIT_SUCCESS) ?
+ "COMMIT" : "ROLLBACK");
+ if (res == NULL)
{
if (pset.on_error_stop)
{
diff --git a/src/bin/psql/t/001_basic.pl b/src/bin/psql/t/001_basic.pl
index c3ed18e84da..d7e20f0ac61 100644
--- a/src/bin/psql/t/001_basic.pl
+++ b/src/bin/psql/t/001_basic.pl
@@ -198,4 +198,68 @@ like(
^LOCATION: .*$/m,
'\errverbose after \gdesc with error');
+# Check behavior when using multiple -c and -f switches.
+# Note that we cannot test backend-side errors as tests are unstable in this
+# case: IPC::Run can complain about a SIGPIPE if psql quits before reading a
+# query result.
+my $tempdir = PostgreSQL::Test::Utils::tempdir;
+$node->safe_psql('postgres', "CREATE TABLE tab_psql_single (a int);");
+$node->command_ok(
+ [
+ 'psql', '-X',
+ '--single-transaction', '-v',
+ 'ON_ERROR_STOP=1', '-c',
+ 'INSERT INTO tab_psql_single VALUES (1)', '-c',
+ 'INSERT INTO tab_psql_single VALUES (2)'
+ ],
+ '--single-transaction and multiple -c switches');
+my $row_count =
+ $node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
+is($row_count, '2',
+ '--single-transaction commits transaction, multiple -c switches');
+
+$node->command_fails(
+ [
+ 'psql', '-X',
+ '--single-transaction', '-v',
+ 'ON_ERROR_STOP=1', '-c',
+ 'INSERT INTO tab_psql_single VALUES (3)', '-c',
+ "\\copy tab_psql_single FROM '$tempdir/nonexistent'"
+ ],
+ '--single-transaction and multiple -c switches, error');
+$row_count =
+ $node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
+is($row_count, '2',
+ 'client-side error rolls back transaction, multiple -c switches');
+
+# Tests mixing files and commands.
+my $copy_sql_file = "$tempdir/tab_copy.sql";
+my $insert_sql_file = "$tempdir/tab_insert.sql";
+append_to_file($copy_sql_file,
+ "\\copy tab_psql_single FROM '$tempdir/nonexistent';");
+append_to_file($insert_sql_file, 'INSERT INTO tab_psql_single VALUES (4);');
+$node->command_ok(
+ [
+ 'psql', '-X', '--single-transaction', '-v',
+ 'ON_ERROR_STOP=1', '-f', $insert_sql_file, '-f',
+ $insert_sql_file
+ ],
+ '--single-transaction and multiple -f switches');
+$row_count =
+ $node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
+is($row_count, '4',
+ '--single-transaction commits transaction, multiple -f switches');
+
+$node->command_fails(
+ [
+ 'psql', '-X', '--single-transaction', '-v',
+ 'ON_ERROR_STOP=1', '-f', $insert_sql_file, '-f',
+ $copy_sql_file
+ ],
+ '--single-transaction and multiple -f switches, error');
+$row_count =
+ $node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
+is($row_count, '4',
+ 'client-side error rolls back transaction, multiple -f switches');
+
done_testing();