On Thu, Jan 25, 2018 at 03:46:30PM -0500, Bruce Momjian wrote:
> On Mon, Jan 15, 2018 at 11:10:44AM -0500, Robert Haas wrote:
> > prompt_status does seem to be available in psql's MainLoop(), so I
> > think that could be done, but the problem is that I don't know exactly
> > what message would be useful to print when we're in a "in quotes"
> > state. If I had a good message text for that state, I might just
> > choose to use it always rather than multiplying the number of
> > messages.
> >
> > More broadly, I think what is needed here is less C-fu than
> > English-fu. If we come up with something good, we can make it print
> > that thing.
>
> I just read this thread and have some ideas. First, the reason 'help'
> was added so easily is because it pointed users at getting more
> information, and it required to be the first command in the query
> buffer.
>
> I realize we are now considering allowing 'help', 'quit', and 'exit' to
> appear alone on a line with whitespace before it, and remove the
> requirement that it be the first thing in the query buffer.
>
> I think there are a few things to consider.
>
> First, allowing whitespace to be before the keyword --- do we really
> think that typing <space>exit will more likely be typed by a user trying
> to exit than part of an SQL query? I think requiring no space around
> the keyword would reduce the number of false hints.
>
> Second, I am thinking we can check prompt_status and report "Use \q" if
> we are not in a quoted string, and suggest "Use Control-D then \q" (or
> "Use Control-C then \q" on Windows) and be done with it. By printing
> the Control-D only when we are in a quoted strong, we minimize the
> number of times that we are wrong due to stty changes.
I used Robert's patch and modified it to match the ideas I had above.
Specifically no white space can be before 'help', 'exit' or 'quit' and
prompt_status is used to adjust the suggestion. Here is the visible
behavior:
test=> exit
(exits)
test=> SELECT
test-> exit
--> Use \q to quit.
test-> \q
test=> SELECT '
test'> exit
--> Use control-D to quit.
test'> \q
test=> SELECT "
test"> exit
--> Use control-D to quit.
test"> \q
test=> SELECT /*
test*> exit
--> Use control-D to quit.
test*> \q
test=> SELECT 'asbs' AS
test-> exit
--> Use \q to quit.
test-> \q
test=> SELECT 'asbs' AS
test-> exit
test-> ;
exit
------
asbs
(1 row)
One open issue is the existing help display is inaccurate on Windows:
Use \\? for help or press control-C to clear the input buffer.
Oh, it clears the input buffer alright, _and_ exits psql. This patch
also removes the control-C mention on Windows.
--
Bruce Momjian <[email protected]> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ As you are, so once was I. As I am, so you will be. +
+ Ancient Roman grave inscription +
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
new file mode 100644
index a8778a5..a3ef150
*** a/src/bin/psql/mainloop.c
--- b/src/bin/psql/mainloop.c
*************** MainLoop(FILE *source)
*** 216,236 ****
continue;
}
! /* A request for help? Be friendly and give them some guidance */
! if (pset.cur_cmd_interactive && query_buf->len == 0 &&
! pg_strncasecmp(line, "help", 4) == 0 &&
! (line[4] == '\0' || line[4] == ';' || isspace((unsigned char) line[4])))
{
! free(line);
! puts(_("You are using psql, the command-line interface to PostgreSQL."));
! printf(_("Type: \\copyright for distribution terms\n"
! " \\h for help with SQL commands\n"
! " \\? for help with psql commands\n"
! " \\g or terminate with semicolon to execute query\n"
! " \\q to quit\n"));
! fflush(stdout);
! continue;
}
/* echo back if flag is set, unless interactive */
--- 216,323 ----
continue;
}
! /* Recognize "help", "quit", "exit" only in interactive mode */
! if (pset.cur_cmd_interactive)
{
! char *first_word = line;
! char *rest_of_line = NULL;
! bool found_help = false;
! bool found_exit_or_quit = false;
! /* Search for the words we recognize; must be first word */
! if (pg_strncasecmp(first_word, "help", 4) == 0)
! {
! rest_of_line = first_word + 4;
! found_help = true;
! }
! else if (pg_strncasecmp(first_word, "exit", 4) == 0 ||
! pg_strncasecmp(first_word, "quit", 4) == 0)
! {
! rest_of_line = first_word + 4;
! found_exit_or_quit = true;
! }
!
! /*
! * If we found a command word, check whether the rest of the line
! * contains only whitespace plus maybe one semicolon. If not,
! * ignore the command word after all.
! */
! if (rest_of_line != NULL)
! {
! /*
! * Ignore unless rest of line is whitespace, plus maybe one
! * semicolon
! */
! while (isspace((unsigned char) *rest_of_line))
! ++rest_of_line;
! if (*rest_of_line == ';')
! ++rest_of_line;
! while (isspace((unsigned char) *rest_of_line))
! ++rest_of_line;
! if (*rest_of_line != '\0')
! {
! found_help = false;
! found_exit_or_quit = false;
! }
! }
!
! /*
! * "help" is only a command when the query buffer is empty, but we
! * emit a one-line message even when it isn't to help confused
! * users. The text is still added to the query buffer in that
! * case.
! */
! if (found_help)
! {
! if (query_buf->len != 0)
! #ifndef WIN32
! puts(_("Use \\? for help or press control-C to clear the input buffer."));
! #else
! puts(_("Use \\? for help."));
! #endif
! else
! {
! puts(_("You are using psql, the command-line interface to PostgreSQL."));
! printf(_("Type: \\copyright for distribution terms\n"
! " \\h for help with SQL commands\n"
! " \\? for help with psql commands\n"
! " \\g or terminate with semicolon to execute query\n"
! " \\q to quit\n"));
! free(line);
! fflush(stdout);
! continue;
! }
! }
! /*
! * "quit" and "exit" are only commands when the query buffer is
! * empty, but we emit a one-line message even when it isn't to
! * help confused users. The text is still added to the query
! * buffer in that case.
! */
! if (found_exit_or_quit)
! {
! if (query_buf->len != 0)
! {
! if (prompt_status == PROMPT_READY ||
! prompt_status == PROMPT_CONTINUE ||
! prompt_status == PROMPT_PAREN)
! puts(_("Use \\q to quit."));
! else
! #ifndef WIN32
! puts(_("Use control-D to quit."));
! #else
! puts(_("Use control-C to quit."));
! #endif
! }
! else
! {
! /* exit app */
! free(line);
! fflush(stdout);
! successResult = EXIT_SUCCESS;
! break;
! }
! }
}
/* echo back if flag is set, unless interactive */