mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
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
This commit is contained in:
@ -1970,7 +1970,9 @@ testdb=>
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The new contents of the query buffer are then re-parsed according to
|
If you edit a file or the previous query, and you quit the editor without
|
||||||
|
modifying the file, the query buffer is cleared.
|
||||||
|
Otherwise, the new contents of the query buffer are re-parsed according to
|
||||||
the normal rules of <application>psql</application>, treating the
|
the normal rules of <application>psql</application>, treating the
|
||||||
whole buffer as a single line. Any complete queries are immediately
|
whole buffer as a single line. Any complete queries are immediately
|
||||||
executed; that is, if the query buffer contains or ends with a
|
executed; that is, if the query buffer contains or ends with a
|
||||||
@ -2039,7 +2041,8 @@ Tue Oct 26 21:40:57 CEST 1999
|
|||||||
in the form of a <command>CREATE OR REPLACE FUNCTION</command> or
|
in the form of a <command>CREATE OR REPLACE FUNCTION</command> or
|
||||||
<command>CREATE OR REPLACE PROCEDURE</command> command.
|
<command>CREATE OR REPLACE PROCEDURE</command> command.
|
||||||
Editing is done in the same way as for <literal>\edit</literal>.
|
Editing is done in the same way as for <literal>\edit</literal>.
|
||||||
After the editor exits, the updated command is executed immediately
|
If you quit the editor without saving, the statement is discarded.
|
||||||
|
If you save and exit the editor, the updated command is executed immediately
|
||||||
if you added a semicolon to it. Otherwise it is redisplayed;
|
if you added a semicolon to it. Otherwise it is redisplayed;
|
||||||
type semicolon or <literal>\g</literal> to send it, or <literal>\r</literal>
|
type semicolon or <literal>\g</literal> to send it, or <literal>\r</literal>
|
||||||
to cancel.
|
to cancel.
|
||||||
@ -2115,7 +2118,8 @@ Tue Oct 26 21:40:57 CEST 1999
|
|||||||
This command fetches and edits the definition of the named view,
|
This command fetches and edits the definition of the named view,
|
||||||
in the form of a <command>CREATE OR REPLACE VIEW</command> command.
|
in the form of a <command>CREATE OR REPLACE VIEW</command> command.
|
||||||
Editing is done in the same way as for <literal>\edit</literal>.
|
Editing is done in the same way as for <literal>\edit</literal>.
|
||||||
After the editor exits, the updated command is executed immediately
|
If you quit the editor without saving, the statement is discarded.
|
||||||
|
If you save and exit the editor, the updated command is executed immediately
|
||||||
if you added a semicolon to it. Otherwise it is redisplayed;
|
if you added a semicolon to it. Otherwise it is redisplayed;
|
||||||
type semicolon or <literal>\g</literal> to send it, or <literal>\r</literal>
|
type semicolon or <literal>\g</literal> to send it, or <literal>\r</literal>
|
||||||
to cancel.
|
to cancel.
|
||||||
|
@ -148,11 +148,11 @@ static void save_query_text_state(PsqlScanState scan_state, ConditionalStack cst
|
|||||||
PQExpBuffer query_buf);
|
PQExpBuffer query_buf);
|
||||||
static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack,
|
static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack,
|
||||||
PQExpBuffer query_buf);
|
PQExpBuffer query_buf);
|
||||||
static void copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
|
static bool copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
|
||||||
static bool do_connect(enum trivalue reuse_previous_specification,
|
static bool do_connect(enum trivalue reuse_previous_specification,
|
||||||
char *dbname, char *user, char *host, char *port);
|
char *dbname, char *user, char *host, char *port);
|
||||||
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
||||||
int lineno, bool *edited);
|
int lineno, bool discard_on_quit, bool *edited);
|
||||||
static bool do_shell(const char *command);
|
static bool do_shell(const char *command);
|
||||||
static bool do_watch(PQExpBuffer query_buf, double sleep);
|
static bool do_watch(PQExpBuffer query_buf, double sleep);
|
||||||
static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
|
static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
|
||||||
@ -418,7 +418,7 @@ exec_command(const char *cmd,
|
|||||||
* the individual command subroutines.
|
* the individual command subroutines.
|
||||||
*/
|
*/
|
||||||
if (status == PSQL_CMD_SEND)
|
if (status == PSQL_CMD_SEND)
|
||||||
copy_previous_query(query_buf, previous_buf);
|
(void) copy_previous_query(query_buf, previous_buf);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -1004,14 +1004,27 @@ exec_command_edit(PsqlScanState scan_state, bool active_branch,
|
|||||||
}
|
}
|
||||||
if (status != PSQL_CMD_ERROR)
|
if (status != PSQL_CMD_ERROR)
|
||||||
{
|
{
|
||||||
|
bool discard_on_quit;
|
||||||
|
|
||||||
expand_tilde(&fname);
|
expand_tilde(&fname);
|
||||||
if (fname)
|
if (fname)
|
||||||
|
{
|
||||||
canonicalize_path(fname);
|
canonicalize_path(fname);
|
||||||
|
/* Always clear buffer if the file isn't modified */
|
||||||
|
discard_on_quit = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If query_buf is empty, recall previous query for
|
||||||
|
* editing. But in that case, the query buffer should be
|
||||||
|
* emptied if editing doesn't modify the file.
|
||||||
|
*/
|
||||||
|
discard_on_quit = copy_previous_query(query_buf,
|
||||||
|
previous_buf);
|
||||||
|
}
|
||||||
|
|
||||||
/* If query_buf is empty, recall previous query for editing */
|
if (do_edit(fname, query_buf, lineno, discard_on_quit, NULL))
|
||||||
copy_previous_query(query_buf, previous_buf);
|
|
||||||
|
|
||||||
if (do_edit(fname, query_buf, lineno, NULL))
|
|
||||||
status = PSQL_CMD_NEWEDIT;
|
status = PSQL_CMD_NEWEDIT;
|
||||||
else
|
else
|
||||||
status = PSQL_CMD_ERROR;
|
status = PSQL_CMD_ERROR;
|
||||||
@ -1134,7 +1147,7 @@ exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
|
|||||||
{
|
{
|
||||||
bool edited = false;
|
bool edited = false;
|
||||||
|
|
||||||
if (!do_edit(NULL, query_buf, lineno, &edited))
|
if (!do_edit(NULL, query_buf, lineno, true, &edited))
|
||||||
status = PSQL_CMD_ERROR;
|
status = PSQL_CMD_ERROR;
|
||||||
else if (!edited)
|
else if (!edited)
|
||||||
puts(_("No changes"));
|
puts(_("No changes"));
|
||||||
@ -2637,7 +2650,7 @@ exec_command_watch(PsqlScanState scan_state, bool active_branch,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If query_buf is empty, recall and execute previous query */
|
/* If query_buf is empty, recall and execute previous query */
|
||||||
copy_previous_query(query_buf, previous_buf);
|
(void) copy_previous_query(query_buf, previous_buf);
|
||||||
|
|
||||||
success = do_watch(query_buf, sleep);
|
success = do_watch(query_buf, sleep);
|
||||||
|
|
||||||
@ -2961,12 +2974,19 @@ discard_query_text(PsqlScanState scan_state, ConditionalStack cstack,
|
|||||||
* This is used by various slash commands for which re-execution of a
|
* This is used by various slash commands for which re-execution of a
|
||||||
* previous query is a common usage. For convenience, we allow the
|
* previous query is a common usage. For convenience, we allow the
|
||||||
* case of query_buf == NULL (and do nothing).
|
* case of query_buf == NULL (and do nothing).
|
||||||
|
*
|
||||||
|
* Returns "true" if the previous query was copied into the query
|
||||||
|
* buffer, else "false".
|
||||||
*/
|
*/
|
||||||
static void
|
static bool
|
||||||
copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf)
|
copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf)
|
||||||
{
|
{
|
||||||
if (query_buf && query_buf->len == 0)
|
if (query_buf && query_buf->len == 0)
|
||||||
|
{
|
||||||
appendPQExpBufferStr(query_buf, previous_buf->data);
|
appendPQExpBufferStr(query_buf, previous_buf->data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3647,10 +3667,11 @@ UnsyncVariables(void)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* do_edit -- handler for \e
|
* helper for do_edit(): actually invoke the editor
|
||||||
*
|
*
|
||||||
* If you do not specify a filename, the current query buffer will be copied
|
* Returns true on success, false if we failed to invoke the editor or
|
||||||
* into a temporary one.
|
* it returned nonzero status. (An error message is printed for failed-
|
||||||
|
* to-invoke cases, but not if the editor returns nonzero status.)
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
editFile(const char *fname, int lineno)
|
editFile(const char *fname, int lineno)
|
||||||
@ -3719,10 +3740,23 @@ editFile(const char *fname, int lineno)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* call this one */
|
/*
|
||||||
|
* do_edit -- handler for \e
|
||||||
|
*
|
||||||
|
* If you do not specify a filename, the current query buffer will be copied
|
||||||
|
* into a temporary file.
|
||||||
|
*
|
||||||
|
* After this function is done, the resulting file will be copied back into the
|
||||||
|
* query buffer. As an exception to this, the query buffer will be emptied
|
||||||
|
* if the file was not modified (or the editor failed) and the caller passes
|
||||||
|
* "discard_on_quit" = true.
|
||||||
|
*
|
||||||
|
* If "edited" isn't NULL, *edited will be set to true if the query buffer
|
||||||
|
* is successfully replaced.
|
||||||
|
*/
|
||||||
static bool
|
static bool
|
||||||
do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
||||||
int lineno, bool *edited)
|
int lineno, bool discard_on_quit, bool *edited)
|
||||||
{
|
{
|
||||||
char fnametmp[MAXPGPATH];
|
char fnametmp[MAXPGPATH];
|
||||||
FILE *stream = NULL;
|
FILE *stream = NULL;
|
||||||
@ -3870,6 +3904,7 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
|||||||
{
|
{
|
||||||
pg_log_error("%s: %m", fname);
|
pg_log_error("%s: %m", fname);
|
||||||
error = true;
|
error = true;
|
||||||
|
resetPQExpBuffer(query_buf);
|
||||||
}
|
}
|
||||||
else if (edited)
|
else if (edited)
|
||||||
{
|
{
|
||||||
@ -3879,6 +3914,15 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
|||||||
fclose(stream);
|
fclose(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the file was not modified, and the caller requested it, discard
|
||||||
|
* the query buffer.
|
||||||
|
*/
|
||||||
|
if (discard_on_quit)
|
||||||
|
resetPQExpBuffer(query_buf);
|
||||||
|
}
|
||||||
|
|
||||||
/* remove temp file */
|
/* remove temp file */
|
||||||
if (!filename_arg)
|
if (!filename_arg)
|
||||||
|
Reference in New Issue
Block a user