Skip to content

Commit a3ff08e

Browse files
committed
Tweak behavior of psql --single-transaction depending on ON_ERROR_STOP
This commit, in completion of 157f873, forces a ROLLBACK for --single-transaction only when ON_ERROR_STOP is used when one of the steps defined by -f/-c fails. Hence, COMMIT is always used when ON_ERROR_STOP is not set, ignoring the status code of the last action taken in the set of switches specified by -c/-f (previously ROLLBACK would have been issued even without ON_ERROR_STOP if the last step failed, while COMMIT was issued if a step in-between failed as long as the last step succeeded, leading to more inconsistency). While on it, this adds much more test coverage in this area when not using ON_ERROR_STOP with multiple switch patterns involving -c and -f for query files, single queries and slash commands. The behavior of ON_ERROR_STOP is arguably a bug, but there was no much support for a backpatch to force a ROLLBACK on a step failure, so this change is done only on HEAD for now. Per discussion with Tom Lane and Kyotaro Horiguchi. Discussion: https://postgr.es/m/Yqbc8bAdwnP02na4@paquier.xyz
1 parent ba412c9 commit a3ff08e

File tree

3 files changed

+73
-11
lines changed

3 files changed

+73
-11
lines changed

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,8 @@ EOF
584584
<application>psql</application> to issue a <command>BEGIN</command> command
585585
before the first such option and a <command>COMMIT</command> command after
586586
the last one, thereby wrapping all the commands into a single
587-
transaction. If any of the commands fails, a
587+
transaction. If any of the commands fails and the variable
588+
<varname>ON_ERROR_STOP</varname> was set, a
588589
<command>ROLLBACK</command> command is sent instead. This ensures that
589590
either all the commands complete successfully, or no changes are
590591
applied.

src/bin/psql/startup.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,13 @@ main(int argc, char *argv[])
426426

427427
if (options.single_txn)
428428
{
429-
res = PSQLexec((successResult == EXIT_SUCCESS) ?
430-
"COMMIT" : "ROLLBACK");
429+
/*
430+
* Rollback the contents of the single transaction if the caller
431+
* has set ON_ERROR_STOP and one of the steps has failed. This
432+
* check needs to match the one done a couple of lines above.
433+
*/
434+
res = PSQLexec((successResult != EXIT_SUCCESS && pset.on_error_stop) ?
435+
"ROLLBACK" : "COMMIT");
431436
if (res == NULL)
432437
{
433438
if (pset.on_error_stop)

src/bin/psql/t/001_basic.pl

+64-8
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ sub psql_fails_like
204204
# query result.
205205
my $tempdir = PostgreSQL::Test::Utils::tempdir;
206206
$node->safe_psql('postgres', "CREATE TABLE tab_psql_single (a int);");
207+
208+
# Tests with ON_ERROR_STOP.
207209
$node->command_ok(
208210
[
209211
'psql', '-X',
@@ -212,11 +214,12 @@ sub psql_fails_like
212214
'INSERT INTO tab_psql_single VALUES (1)', '-c',
213215
'INSERT INTO tab_psql_single VALUES (2)'
214216
],
215-
'--single-transaction and multiple -c switches');
217+
'ON_ERROR_STOP, --single-transaction and multiple -c switches');
216218
my $row_count =
217219
$node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
218220
is($row_count, '2',
219-
'--single-transaction commits transaction, multiple -c switches');
221+
'--single-transaction commits transaction, ON_ERROR_STOP and multiple -c switches'
222+
);
220223

221224
$node->command_fails(
222225
[
@@ -226,11 +229,12 @@ sub psql_fails_like
226229
'INSERT INTO tab_psql_single VALUES (3)', '-c',
227230
"\\copy tab_psql_single FROM '$tempdir/nonexistent'"
228231
],
229-
'--single-transaction and multiple -c switches, error');
232+
'ON_ERROR_STOP, --single-transaction and multiple -c switches, error');
230233
$row_count =
231234
$node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
232235
is($row_count, '2',
233-
'client-side error rolls back transaction, multiple -c switches');
236+
'client-side error rolls back transaction, ON_ERROR_STOP and multiple -c switches'
237+
);
234238

235239
# Tests mixing files and commands.
236240
my $copy_sql_file = "$tempdir/tab_copy.sql";
@@ -244,22 +248,74 @@ sub psql_fails_like
244248
'ON_ERROR_STOP=1', '-f', $insert_sql_file, '-f',
245249
$insert_sql_file
246250
],
247-
'--single-transaction and multiple -f switches');
251+
'ON_ERROR_STOP, --single-transaction and multiple -f switches');
248252
$row_count =
249253
$node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
250254
is($row_count, '4',
251-
'--single-transaction commits transaction, multiple -f switches');
255+
'--single-transaction commits transaction, ON_ERROR_STOP and multiple -f switches'
256+
);
252257

253258
$node->command_fails(
254259
[
255260
'psql', '-X', '--single-transaction', '-v',
256261
'ON_ERROR_STOP=1', '-f', $insert_sql_file, '-f',
257262
$copy_sql_file
258263
],
259-
'--single-transaction and multiple -f switches, error');
264+
'ON_ERROR_STOP, --single-transaction and multiple -f switches, error');
260265
$row_count =
261266
$node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
262267
is($row_count, '4',
263-
'client-side error rolls back transaction, multiple -f switches');
268+
'client-side error rolls back transaction, ON_ERROR_STOP and multiple -f switches'
269+
);
270+
271+
# Tests without ON_ERROR_STOP.
272+
# The last switch fails on \copy. The command returns a failure and the
273+
# transaction commits.
274+
$node->command_fails(
275+
[
276+
'psql', '-X',
277+
'--single-transaction', '-f',
278+
$insert_sql_file, '-f',
279+
$insert_sql_file, '-c',
280+
"\\copy tab_psql_single FROM '$tempdir/nonexistent'"
281+
],
282+
'no ON_ERROR_STOP, --single-transaction and multiple -f/-c switches');
283+
$row_count =
284+
$node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
285+
is($row_count, '6',
286+
'client-side error commits transaction, no ON_ERROR_STOP and multiple -f/-c switches'
287+
);
288+
289+
# The last switch fails on \copy coming from an input file. The command
290+
# returns a success and the transaction commits.
291+
$node->command_ok(
292+
[
293+
'psql', '-X', '--single-transaction', '-f',
294+
$insert_sql_file, '-f', $insert_sql_file, '-f',
295+
$copy_sql_file
296+
],
297+
'no ON_ERROR_STOP, --single-transaction and multiple -f switches');
298+
$row_count =
299+
$node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
300+
is($row_count, '8',
301+
'client-side error commits transaction, no ON_ERROR_STOP and multiple -f switches'
302+
);
303+
304+
# The last switch makes the command return a success, and the contents of
305+
# the transaction commit even if there is a failure in-between.
306+
$node->command_ok(
307+
[
308+
'psql', '-X',
309+
'--single-transaction', '-c',
310+
'INSERT INTO tab_psql_single VALUES (5)', '-f',
311+
$copy_sql_file, '-c',
312+
'INSERT INTO tab_psql_single VALUES (6)'
313+
],
314+
'no ON_ERROR_STOP, --single-transaction and multiple -c switches');
315+
$row_count =
316+
$node->safe_psql('postgres', 'SELECT count(*) FROM tab_psql_single');
317+
is($row_count, '10',
318+
'client-side error commits transaction, no ON_ERROR_STOP and multiple -c switches'
319+
);
264320

265321
done_testing();

0 commit comments

Comments
 (0)