diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 59b596d6f2..9489a8e2af 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -624,7 +624,7 @@ exec_command(const char *cmd, if (!options[0]) { - fprintf(stderr, "Usage \\%s \n", cmd); + fprintf(stderr, "Usage: \\%s \n", cmd); success = false; } else diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 30167847a0..6bbf663733 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -433,7 +433,6 @@ SendQuery(PsqlSettings *pset, const char *query) fgets(buf, 3, stdin); if (buf[0] == 'x') return false; - fflush(stdin); } cancelConn = pset->db; @@ -479,7 +478,6 @@ SendQuery(PsqlSettings *pset, const char *query) { success = true; printQuery(results, &pset->popt, pset->queryFout); - fflush(pset->queryFout); } break; case PGRES_EMPTY_QUERY: @@ -488,10 +486,8 @@ SendQuery(PsqlSettings *pset, const char *query) case PGRES_COMMAND_OK: success = true; pset->lastOid = PQoidValue(results); - if (!GetVariableBool(pset->vars, "quiet")) { + if (!GetVariableBool(pset->vars, "quiet")) fprintf(pset->queryFout, "%s\n", PQcmdStatus(results)); - fflush(pset->queryFout); - } break; case PGRES_COPY_OUT: @@ -515,10 +511,11 @@ SendQuery(PsqlSettings *pset, const char *query) case PGRES_BAD_RESPONSE: success = false; fputs(PQerrorMessage(pset->db), pset->queryFout); - fflush(pset->queryFout); break; } + fflush(pset->queryFout); + if (PQstatus(pset->db) == CONNECTION_BAD) { fputs("The connection to the server was lost. Attempting reset: ", stderr); @@ -541,6 +538,7 @@ SendQuery(PsqlSettings *pset, const char *query) fprintf(pset->queryFout, "Asynchronous NOTIFY '%s' from backend with pid '%d' received.\n", notify->relname, notify->be_pid); free(notify); + fflush(pset->queryFout); } if (results) diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index 31fe4ce773..250f1233ea 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -32,11 +32,7 @@ gets_interactive(const char *prompt) #ifdef USE_READLINE if (useReadline) - { s = readline(prompt); - fputc('\r', stdout); - fflush(stdout); - } else { #endif diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index 067be54e2c..f85247689b 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -29,6 +29,8 @@ int MainLoop(PsqlSettings *pset, FILE *source) { PQExpBuffer query_buf; /* buffer for query being accumulated */ + PQExpBuffer previous_buf; /* if there isn't anything in the new buffer + yet, use this one for \e, etc. */ char *line; /* current line of input */ int len; /* length of the line */ int successResult = EXIT_SUCCESS; @@ -63,7 +65,8 @@ MainLoop(PsqlSettings *pset, FILE *source) query_buf = createPQExpBuffer(); - if (!query_buf) + previous_buf = createPQExpBuffer(); + if (!query_buf || !previous_buf) { perror("createPQExpBuffer"); exit(EXIT_FAILURE); @@ -80,21 +83,21 @@ MainLoop(PsqlSettings *pset, FILE *source) { if (slashCmdStatus == CMD_NEWEDIT) { - /* * just returned from editing the line? then just copy to the * input buffer */ - line = strdup(query_buf->data); + line = xstrdup(query_buf->data); resetPQExpBuffer(query_buf); - /* reset parsing state since we are rescanning whole query */ + /* reset parsing state since we are rescanning whole line */ xcomment = false; in_quote = 0; paren_level = 0; + slashCmdStatus = CMD_UNKNOWN; } else { - + fflush(stdout); /* * otherwise, set interactive prompt if necessary and get * another line @@ -170,8 +173,6 @@ MainLoop(PsqlSettings *pset, FILE *source) puts(line); - slashCmdStatus = CMD_UNKNOWN; - len = strlen(line); query_start = 0; @@ -275,11 +276,13 @@ MainLoop(PsqlSettings *pset, FILE *source) /* semicolon? then send query */ else if (line[i] == ';' && !was_bslash) { + /* delete the old query buffer from last time around */ + if (slashCmdStatus == CMD_SEND) + line[i] = '\0'; /* is there anything else on the line? */ if (line[query_start + strspn(line + query_start, " \t")] != '\0') { - /* * insert a cosmetic newline, if this is not the first * line in the buffer @@ -292,8 +295,11 @@ MainLoop(PsqlSettings *pset, FILE *source) /* execute query */ success = SendQuery(pset, query_buf->data); + slashCmdStatus = success ? CMD_SEND : CMD_ERROR; - resetPQExpBuffer(query_buf); + resetPQExpBuffer(previous_buf); + appendPQExpBufferStr(previous_buf, query_buf->data); + resetPQExpBuffer(query_buf); query_start = i + thislen; } @@ -316,7 +322,6 @@ MainLoop(PsqlSettings *pset, FILE *source) /* is there anything else on the line? */ if (line[query_start + strspn(line + query_start, " \t")] != '\0') { - /* * insert a cosmetic newline, if this is not the first * line in the buffer @@ -327,17 +332,27 @@ MainLoop(PsqlSettings *pset, FILE *source) appendPQExpBufferStr(query_buf, line + query_start); } - /* handle backslash command */ - - slashCmdStatus = HandleSlashCmds(pset, &line[i], query_buf, &end_of_cmd); + /* handle backslash command */ + slashCmdStatus = HandleSlashCmds(pset, &line[i], + query_buf->len>0 ? query_buf : previous_buf, + &end_of_cmd); success = slashCmdStatus != CMD_ERROR; + if ((slashCmdStatus == CMD_SEND || slashCmdStatus == CMD_NEWEDIT) && + query_buf->len == 0) { + /* copy previous buffer to current for for handling */ + appendPQExpBufferStr(query_buf, previous_buf->data); + } + if (slashCmdStatus == CMD_SEND) { success = SendQuery(pset, query_buf->data); - resetPQExpBuffer(query_buf); query_start = i + thislen; + + resetPQExpBuffer(previous_buf); + appendPQExpBufferStr(previous_buf, query_buf->data); + resetPQExpBuffer(query_buf); } /* is there anything left after the backslash command? */ @@ -358,13 +373,6 @@ MainLoop(PsqlSettings *pset, FILE *source) } /* for (line) */ - if (!success && die_on_error && !pset->cur_cmd_interactive) - { - successResult = EXIT_USER; - break; - } - - if (slashCmdStatus == CMD_TERMINATE) { successResult = EXIT_SUCCESS; @@ -387,7 +395,17 @@ MainLoop(PsqlSettings *pset, FILE *source) if (query_buf->data[0] != '\0' && GetVariableBool(pset->vars, "singleline")) { success = SendQuery(pset, query_buf->data); - resetPQExpBuffer(query_buf); + slashCmdStatus = success ? CMD_SEND : CMD_ERROR; + resetPQExpBuffer(previous_buf); + appendPQExpBufferStr(previous_buf, query_buf->data); + resetPQExpBuffer(query_buf); + } + + + if (!success && die_on_error && !pset->cur_cmd_interactive) + { + successResult = EXIT_USER; + break; } @@ -397,9 +415,10 @@ MainLoop(PsqlSettings *pset, FILE *source) successResult = EXIT_BADCONN; break; } - } /* while */ + } /* while !EOF */ destroyPQExpBuffer(query_buf); + destroyPQExpBuffer(previous_buf); pset->cur_cmd_source = prev_cmd_source; pset->cur_cmd_interactive = prev_cmd_interactive; diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 481f76f343..15d40a806a 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -18,7 +18,7 @@ #include #include /* for Oid type */ -#define DEFAULT_PAGER "/bin/more" +#define DEFAULT_PAGER "more" @@ -325,6 +325,11 @@ print_aligned_vertical(const char *title, const char * const * headers, dwidth = 0; char *divider; + if (cells[0] == NULL) { + puts("(No rows)\n"); + return; + } + /* count columns and find longest header */ for (ptr = headers; *ptr; ptr++) { diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h index 828dbd40d0..85cf9dcb7f 100644 --- a/src/bin/psql/settings.h +++ b/src/bin/psql/settings.h @@ -13,7 +13,7 @@ #include "print.h" #define DEFAULT_FIELD_SEP "|" -#define DEFAULT_EDITOR "/bin/vi" +#define DEFAULT_EDITOR "vi" #define DEFAULT_PROMPT1 "%/%R%# " #define DEFAULT_PROMPT2 "%/%R%# " diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index f2025b0d26..2b08ff3f86 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -203,7 +203,7 @@ char ** psql_completion(char *text, int start, int end) (void)end; /* not used */ - rl_filename_quoting_desired = 1; + rl_completion_append_character = ' '; /* Clear a few things. */ completion_charp = NULL; @@ -501,6 +501,11 @@ char ** psql_completion(char *text, int start, int end) COMPLETE_WITH_QUERY(Query_for_list_of_tables); +/* ... FROM ... */ + else if (strcasecmp(prev_wd, "FROM") == 0 ) + COMPLETE_WITH_QUERY(Query_for_list_of_tables); + + /* Backslash commands */ else if (strcmp(prev_wd, "\\connect")==0 || strcmp(prev_wd, "\\c")==0) COMPLETE_WITH_QUERY(Query_for_list_of_databases); @@ -510,7 +515,7 @@ char ** psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(sql_commands); else if (strcmp(prev_wd, "\\pset")==0) { char * my_list[] = { "format", "border", "expanded", "null", "fieldsep", - "tuples_only", "title", "tableattr", "pager" }; + "tuples_only", "title", "tableattr", "pager", NULL }; COMPLETE_WITH_LIST(my_list); } else if( strcmp(prev_wd, "\\e")==0 || strcmp(prev_wd, "\\edit")==0 || @@ -541,8 +546,8 @@ char ** psql_completion(char *text, int start, int end) of default list. If we were to just return NULL, readline automatically attempts filename completion, and that's usually no good. */ if (matches == NULL) { - char * my_list[] = { "", "", NULL }; - COMPLETE_WITH_LIST(my_list); + COMPLETE_WITH_CONST(""); + rl_completion_append_character = '\0'; }