diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 1055af5..933e181 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -86,6 +86,10 @@ static backslashResult exec_command_errverbose(PsqlScanState scan_state, bool ac static backslashResult exec_command_f(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_g(PsqlScanState scan_state, bool active_branch, const char *cmd); +static backslashResult process_command_g_options(char *first_option, + PsqlScanState scan_state, + bool active_branch, + const char *cmd); static backslashResult exec_command_gdesc(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_gexec(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_gset(PsqlScanState scan_state, bool active_branch); @@ -1280,19 +1284,40 @@ exec_command_f(PsqlScanState scan_state, bool active_branch) } /* - * \g [filename] -- send query, optionally with output to file/pipe - * \gx [filename] -- same as \g, with expanded mode forced + * \g [(pset-option[=pset-value] ...)] [filename/shell-command] + * \gx [(pset-option[=pset-value] ...)] [filename/shell-command] + * + * Send the current query. If pset options are specified, they are made + * active just for this query. If a filename or pipe command is given, + * the query output goes there. \gx implicitly forces expanded = 1 along + * with any other pset options that are specified. */ static backslashResult exec_command_g(PsqlScanState scan_state, bool active_branch, const char *cmd) { backslashResult status = PSQL_CMD_SKIP_LINE; + char *fname; - if (active_branch) + /* + * Because the option processing for this is fairly complicated, we do it + * and then decide whether the branch is active. + */ + fname = psql_scan_slash_option(scan_state, + OT_FILEPIPE, NULL, false); + + if (fname && fname[0] == '(') { - char *fname = psql_scan_slash_option(scan_state, - OT_FILEPIPE, NULL, false); + /* Consume pset options through trailing ')' ... */ + status = process_command_g_options(fname + 1, scan_state, + active_branch, cmd); + free(fname); + /* ... and again attempt to scan the filename. */ + fname = psql_scan_slash_option(scan_state, + OT_FILEPIPE, NULL, false); + } + if (status == PSQL_CMD_SKIP_LINE && active_branch) + { if (!fname) pset.gfname = NULL; else @@ -1300,22 +1325,89 @@ exec_command_g(PsqlScanState scan_state, bool active_branch, const char *cmd) expand_tilde(&fname); pset.gfname = pg_strdup(fname); } - free(fname); if (strcmp(cmd, "gx") == 0) { - /* save settings, then force expanded = 1 */ - pset.gsavepopt = savePsetInfo(&pset.popt); + /* save settings if not done already, then force expanded = 1 */ + if (pset.gsavepopt == NULL) + pset.gsavepopt = savePsetInfo(&pset.popt); pset.popt.topt.expanded = 1; } status = PSQL_CMD_SEND; } - else - ignore_slash_filepipe(scan_state); + + free(fname); return status; } /* + * Process parenthesized pset options for \g + */ +static backslashResult +process_command_g_options(char *first_option, PsqlScanState scan_state, + bool active_branch, const char *cmd) +{ + bool success = true; + bool found_r_paren = false; + + do + { + char *option; + size_t optlen; + + /* If not first time through, collect a new option */ + if (first_option) + option = first_option; + else + { + option = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false); + if (!option) + { + if (active_branch) + { + pg_log_error("\\%s: missing right parenthesis", cmd); + success = false; + } + break; + } + } + + /* Check for terminating right paren, and remove it from string */ + optlen = strlen(option); + if (optlen > 0 && option[optlen - 1] == ')') + { + option[--optlen] = '\0'; + found_r_paren = true; + } + + /* If there was anything besides right paren, parse/execute it */ + if (optlen > 0) + { + char *valptr = strchr(option, '='); + + if (valptr) + *valptr++ = '\0'; + if (active_branch) + { + /* save settings if not done already, then apply option */ + if (pset.gsavepopt == NULL) + pset.gsavepopt = savePsetInfo(&pset.popt); + success &= do_pset(option, valptr, &pset.popt, true); + } + } + + /* Clean up after this option. We should not free first_option. */ + if (first_option) + first_option = NULL; + else + free(option); + } while (!found_r_paren); + + return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; +} + +/* * \gdesc -- describe query result */ static backslashResult