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();