Skip to content

Commit 55873a0

Browse files
committed
Improve psql's behavior when the editor is exited without saving.
When editing the previous query buffer, if the editor is exited without modifying the temp file then clear the query buffer, rather than re-loading (and probably re-executing) the previous query buffer. This reduces the probability of accidentally re-executing something you didn't intend to. Similarly, in "\e file", if the file isn't actually modified then don't load it into the query buffer. And in "\ef" and "\ev", if no changes are made then clear the query buffer instead of loading the function or view definition into it. Cases where we fail to invoke the editor at all, or it returns a nonzero status, are treated like the no-file-modification case. Laurenz Albe, reviewed by Jacob Champion Discussion: https://postgr.es/m/0ba3f2a658bac6546d9934ab6ba63a805d46a49b.camel@cybertec.at
1 parent 225a22b commit 55873a0

File tree

2 files changed

+66
-18
lines changed

2 files changed

+66
-18
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,7 +1970,9 @@ testdb=>
19701970
</para>
19711971

19721972
<para>
1973-
The new contents of the query buffer are then re-parsed according to
1973+
If you edit a file or the previous query, and you quit the editor without
1974+
modifying the file, the query buffer is cleared.
1975+
Otherwise, the new contents of the query buffer are re-parsed according to
19741976
the normal rules of <application>psql</application>, treating the
19751977
whole buffer as a single line. Any complete queries are immediately
19761978
executed; that is, if the query buffer contains or ends with a
@@ -2039,7 +2041,8 @@ Tue Oct 26 21:40:57 CEST 1999
20392041
in the form of a <command>CREATE OR REPLACE FUNCTION</command> or
20402042
<command>CREATE OR REPLACE PROCEDURE</command> command.
20412043
Editing is done in the same way as for <literal>\edit</literal>.
2042-
After the editor exits, the updated command is executed immediately
2044+
If you quit the editor without saving, the statement is discarded.
2045+
If you save and exit the editor, the updated command is executed immediately
20432046
if you added a semicolon to it. Otherwise it is redisplayed;
20442047
type semicolon or <literal>\g</literal> to send it, or <literal>\r</literal>
20452048
to cancel.
@@ -2115,7 +2118,8 @@ Tue Oct 26 21:40:57 CEST 1999
21152118
This command fetches and edits the definition of the named view,
21162119
in the form of a <command>CREATE OR REPLACE VIEW</command> command.
21172120
Editing is done in the same way as for <literal>\edit</literal>.
2118-
After the editor exits, the updated command is executed immediately
2121+
If you quit the editor without saving, the statement is discarded.
2122+
If you save and exit the editor, the updated command is executed immediately
21192123
if you added a semicolon to it. Otherwise it is redisplayed;
21202124
type semicolon or <literal>\g</literal> to send it, or <literal>\r</literal>
21212125
to cancel.

src/bin/psql/command.c

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,11 @@ static void save_query_text_state(PsqlScanState scan_state, ConditionalStack cst
148148
PQExpBuffer query_buf);
149149
static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack,
150150
PQExpBuffer query_buf);
151-
static void copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
151+
static bool copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
152152
static bool do_connect(enum trivalue reuse_previous_specification,
153153
char *dbname, char *user, char *host, char *port);
154154
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
155-
int lineno, bool *edited);
155+
int lineno, bool discard_on_quit, bool *edited);
156156
static bool do_shell(const char *command);
157157
static bool do_watch(PQExpBuffer query_buf, double sleep);
158158
static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
@@ -418,7 +418,7 @@ exec_command(const char *cmd,
418418
* the individual command subroutines.
419419
*/
420420
if (status == PSQL_CMD_SEND)
421-
copy_previous_query(query_buf, previous_buf);
421+
(void) copy_previous_query(query_buf, previous_buf);
422422

423423
return status;
424424
}
@@ -1004,14 +1004,27 @@ exec_command_edit(PsqlScanState scan_state, bool active_branch,
10041004
}
10051005
if (status != PSQL_CMD_ERROR)
10061006
{
1007+
bool discard_on_quit;
1008+
10071009
expand_tilde(&fname);
10081010
if (fname)
1011+
{
10091012
canonicalize_path(fname);
1013+
/* Always clear buffer if the file isn't modified */
1014+
discard_on_quit = true;
1015+
}
1016+
else
1017+
{
1018+
/*
1019+
* If query_buf is empty, recall previous query for
1020+
* editing. But in that case, the query buffer should be
1021+
* emptied if editing doesn't modify the file.
1022+
*/
1023+
discard_on_quit = copy_previous_query(query_buf,
1024+
previous_buf);
1025+
}
10101026

1011-
/* If query_buf is empty, recall previous query for editing */
1012-
copy_previous_query(query_buf, previous_buf);
1013-
1014-
if (do_edit(fname, query_buf, lineno, NULL))
1027+
if (do_edit(fname, query_buf, lineno, discard_on_quit, NULL))
10151028
status = PSQL_CMD_NEWEDIT;
10161029
else
10171030
status = PSQL_CMD_ERROR;
@@ -1134,7 +1147,7 @@ exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
11341147
{
11351148
bool edited = false;
11361149

1137-
if (!do_edit(NULL, query_buf, lineno, &edited))
1150+
if (!do_edit(NULL, query_buf, lineno, true, &edited))
11381151
status = PSQL_CMD_ERROR;
11391152
else if (!edited)
11401153
puts(_("No changes"));
@@ -2637,7 +2650,7 @@ exec_command_watch(PsqlScanState scan_state, bool active_branch,
26372650
}
26382651

26392652
/* If query_buf is empty, recall and execute previous query */
2640-
copy_previous_query(query_buf, previous_buf);
2653+
(void) copy_previous_query(query_buf, previous_buf);
26412654

26422655
success = do_watch(query_buf, sleep);
26432656

@@ -2961,12 +2974,19 @@ discard_query_text(PsqlScanState scan_state, ConditionalStack cstack,
29612974
* This is used by various slash commands for which re-execution of a
29622975
* previous query is a common usage. For convenience, we allow the
29632976
* case of query_buf == NULL (and do nothing).
2977+
*
2978+
* Returns "true" if the previous query was copied into the query
2979+
* buffer, else "false".
29642980
*/
2965-
static void
2981+
static bool
29662982
copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf)
29672983
{
29682984
if (query_buf && query_buf->len == 0)
2985+
{
29692986
appendPQExpBufferStr(query_buf, previous_buf->data);
2987+
return true;
2988+
}
2989+
return false;
29702990
}
29712991

29722992
/*
@@ -3647,10 +3667,11 @@ UnsyncVariables(void)
36473667

36483668

36493669
/*
3650-
* do_edit -- handler for \e
3670+
* helper for do_edit(): actually invoke the editor
36513671
*
3652-
* If you do not specify a filename, the current query buffer will be copied
3653-
* into a temporary one.
3672+
* Returns true on success, false if we failed to invoke the editor or
3673+
* it returned nonzero status. (An error message is printed for failed-
3674+
* to-invoke cases, but not if the editor returns nonzero status.)
36543675
*/
36553676
static bool
36563677
editFile(const char *fname, int lineno)
@@ -3719,10 +3740,23 @@ editFile(const char *fname, int lineno)
37193740
}
37203741

37213742

3722-
/* call this one */
3743+
/*
3744+
* do_edit -- handler for \e
3745+
*
3746+
* If you do not specify a filename, the current query buffer will be copied
3747+
* into a temporary file.
3748+
*
3749+
* After this function is done, the resulting file will be copied back into the
3750+
* query buffer. As an exception to this, the query buffer will be emptied
3751+
* if the file was not modified (or the editor failed) and the caller passes
3752+
* "discard_on_quit" = true.
3753+
*
3754+
* If "edited" isn't NULL, *edited will be set to true if the query buffer
3755+
* is successfully replaced.
3756+
*/
37233757
static bool
37243758
do_edit(const char *filename_arg, PQExpBuffer query_buf,
3725-
int lineno, bool *edited)
3759+
int lineno, bool discard_on_quit, bool *edited)
37263760
{
37273761
char fnametmp[MAXPGPATH];
37283762
FILE *stream = NULL;
@@ -3870,6 +3904,7 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf,
38703904
{
38713905
pg_log_error("%s: %m", fname);
38723906
error = true;
3907+
resetPQExpBuffer(query_buf);
38733908
}
38743909
else if (edited)
38753910
{
@@ -3879,6 +3914,15 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf,
38793914
fclose(stream);
38803915
}
38813916
}
3917+
else
3918+
{
3919+
/*
3920+
* If the file was not modified, and the caller requested it, discard
3921+
* the query buffer.
3922+
*/
3923+
if (discard_on_quit)
3924+
resetPQExpBuffer(query_buf);
3925+
}
38823926

38833927
/* remove temp file */
38843928
if (!filename_arg)

0 commit comments

Comments
 (0)