diff options
Diffstat (limited to 'src/bin/psql/mainloop.c')
-rw-r--r-- | src/bin/psql/mainloop.c | 119 |
1 files changed, 89 insertions, 30 deletions
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index 6e358e2e1b8..2bc2f43b4e7 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -35,6 +35,7 @@ int MainLoop(FILE *source) { PsqlScanState scan_state; /* lexer working state */ + ConditionalStack cond_stack; /* \if status stack */ volatile PQExpBuffer query_buf; /* buffer for query being accumulated */ volatile PQExpBuffer previous_buf; /* if there isn't anything in the new * buffer yet, use this one for \e, @@ -50,16 +51,15 @@ MainLoop(FILE *source) volatile promptStatus_t prompt_status = PROMPT_READY; volatile int count_eof = 0; volatile bool die_on_error = false; - - /* Save the prior command source */ FILE *prev_cmd_source; bool prev_cmd_interactive; uint64 prev_lineno; - /* Save old settings */ + /* Save the prior command source */ prev_cmd_source = pset.cur_cmd_source; prev_cmd_interactive = pset.cur_cmd_interactive; prev_lineno = pset.lineno; + /* pset.stmt_lineno does not need to be saved and restored */ /* Establish new source */ pset.cur_cmd_source = source; @@ -69,6 +69,8 @@ MainLoop(FILE *source) /* Create working state */ scan_state = psql_scan_create(&psqlscan_callbacks); + cond_stack = conditional_stack_create(); + psql_scan_set_passthrough(scan_state, (void *) cond_stack); query_buf = createPQExpBuffer(); previous_buf = createPQExpBuffer(); @@ -122,7 +124,19 @@ MainLoop(FILE *source) cancel_pressed = false; if (pset.cur_cmd_interactive) + { putc('\n', stdout); + + /* + * if interactive user is in an \if block, then Ctrl-C will + * exit from the innermost \if. + */ + if (!conditional_stack_empty(cond_stack)) + { + psql_error("\\if: escaped\n"); + conditional_stack_pop(cond_stack); + } + } else { successResult = EXIT_USER; @@ -140,7 +154,8 @@ MainLoop(FILE *source) /* May need to reset prompt, eg after \r command */ if (query_buf->len == 0) prompt_status = PROMPT_READY; - line = gets_interactive(get_prompt(prompt_status), query_buf); + line = gets_interactive(get_prompt(prompt_status, cond_stack), + query_buf); } else { @@ -286,8 +301,10 @@ MainLoop(FILE *source) (scan_result == PSCAN_EOL && pset.singleline)) { /* - * Save query in history. We use history_buf to accumulate - * multi-line queries into a single history entry. + * Save line in history. We use history_buf to accumulate + * multi-line queries into a single history entry. Note that + * history accumulation works on input lines, so it doesn't + * matter whether the query will be ignored due to \if. */ if (pset.cur_cmd_interactive && !line_saved_in_history) { @@ -296,22 +313,36 @@ MainLoop(FILE *source) line_saved_in_history = true; } - /* execute query */ - success = SendQuery(query_buf->data); - slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; - pset.stmt_lineno = 1; - - /* transfer query to previous_buf by pointer-swapping */ + /* execute query unless we're in an inactive \if branch */ + if (conditional_active(cond_stack)) { - PQExpBuffer swap_buf = previous_buf; + success = SendQuery(query_buf->data); + slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; + pset.stmt_lineno = 1; - previous_buf = query_buf; - query_buf = swap_buf; - } - resetPQExpBuffer(query_buf); + /* transfer query to previous_buf by pointer-swapping */ + { + PQExpBuffer swap_buf = previous_buf; - added_nl_pos = -1; - /* we need not do psql_scan_reset() here */ + previous_buf = query_buf; + query_buf = swap_buf; + } + resetPQExpBuffer(query_buf); + + added_nl_pos = -1; + /* we need not do psql_scan_reset() here */ + } + else + { + /* if interactive, warn about non-executed query */ + if (pset.cur_cmd_interactive) + psql_error("query ignored; use \\endif or Ctrl-C to exit current \\if block\n"); + /* fake an OK result for purposes of loop checks */ + success = true; + slashCmdStatus = PSQL_CMD_SEND; + pset.stmt_lineno = 1; + /* note that query_buf doesn't change state */ + } } else if (scan_result == PSCAN_BACKSLASH) { @@ -343,21 +374,24 @@ MainLoop(FILE *source) /* execute backslash command */ slashCmdStatus = HandleSlashCmds(scan_state, - query_buf->len > 0 ? - query_buf : previous_buf); + cond_stack, + query_buf, + previous_buf); success = slashCmdStatus != PSQL_CMD_ERROR; - pset.stmt_lineno = 1; - if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) && - query_buf->len == 0) - { - /* copy previous buffer to current for handling */ - appendPQExpBufferStr(query_buf, previous_buf->data); - } + /* + * Resetting stmt_lineno after a backslash command isn't + * always appropriate, but it's what we've done historically + * and there have been few complaints. + */ + pset.stmt_lineno = 1; if (slashCmdStatus == PSQL_CMD_SEND) { + /* should not see this in inactive branch */ + Assert(conditional_active(cond_stack)); + success = SendQuery(query_buf->data); /* transfer query to previous_buf by pointer-swapping */ @@ -374,6 +408,8 @@ MainLoop(FILE *source) } else if (slashCmdStatus == PSQL_CMD_NEWEDIT) { + /* should not see this in inactive branch */ + Assert(conditional_active(cond_stack)); /* rescan query_buf as new input */ psql_scan_finish(scan_state); free(line); @@ -429,8 +465,17 @@ MainLoop(FILE *source) if (pset.cur_cmd_interactive) pg_send_history(history_buf); - /* execute query */ - success = SendQuery(query_buf->data); + /* execute query unless we're in an inactive \if branch */ + if (conditional_active(cond_stack)) + { + success = SendQuery(query_buf->data); + } + else + { + if (pset.cur_cmd_interactive) + psql_error("query ignored; use \\endif or Ctrl-C to exit current \\if block\n"); + success = true; + } if (!success && die_on_error) successResult = EXIT_USER; @@ -439,6 +484,19 @@ MainLoop(FILE *source) } /* + * Check for unbalanced \if-\endifs unless user explicitly quit, or the + * script is erroring out + */ + if (slashCmdStatus != PSQL_CMD_TERMINATE && + successResult != EXIT_USER && + !conditional_stack_empty(cond_stack)) + { + psql_error("reached EOF without finding closing \\endif(s)\n"); + if (die_on_error && !pset.cur_cmd_interactive) + successResult = EXIT_USER; + } + + /* * Let's just make real sure the SIGINT handler won't try to use * sigint_interrupt_jmp after we exit this routine. If there is an outer * MainLoop instance, it will reset sigint_interrupt_jmp to point to @@ -452,6 +510,7 @@ MainLoop(FILE *source) destroyPQExpBuffer(history_buf); psql_scan_destroy(scan_state); + conditional_stack_destroy(cond_stack); pset.cur_cmd_source = prev_cmd_source; pset.cur_cmd_interactive = prev_cmd_interactive; |