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 <br...@momjian.us> 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 */