/src/bin/psql/command.c
C | 5166 lines | 4007 code | 457 blank | 702 comment | 766 complexity | 776c5e1253ef696bb31d23c888020b6a MD5 | raw file
Possible License(s): AGPL-3.0
Large files files are truncated, but you can click here to view the full file
- /*
- * psql - the PostgreSQL interactive terminal
- *
- * Copyright (c) 2000-2020, PostgreSQL Global Development Group
- *
- * src/bin/psql/command.c
- */
- #include "postgres_fe.h"
- #include <ctype.h>
- #include <time.h>
- #include <pwd.h>
- #ifndef WIN32
- #include <sys/stat.h> /* for stat() */
- #include <fcntl.h> /* open() flags */
- #include <unistd.h> /* for geteuid(), getpid(), stat() */
- #else
- #include <win32.h>
- #include <io.h>
- #include <fcntl.h>
- #include <direct.h>
- #include <sys/stat.h> /* for stat() */
- #endif
- #include "catalog/pg_class_d.h"
- #include "command.h"
- #include "common.h"
- #include "common/logging.h"
- #include "copy.h"
- #include "crosstabview.h"
- #include "describe.h"
- #include "fe_utils/cancel.h"
- #include "fe_utils/print.h"
- #include "fe_utils/string_utils.h"
- #include "help.h"
- #include "input.h"
- #include "large_obj.h"
- #include "libpq-fe.h"
- #include "mainloop.h"
- #include "portability/instr_time.h"
- #include "pqexpbuffer.h"
- #include "psqlscanslash.h"
- #include "settings.h"
- #include "variables.h"
- /*
- * Editable database object types.
- */
- typedef enum EditableObjectType
- {
- EditableFunction,
- EditableView
- } EditableObjectType;
- /* local function declarations */
- static backslashResult exec_command(const char *cmd,
- PsqlScanState scan_state,
- ConditionalStack cstack,
- PQExpBuffer query_buf,
- PQExpBuffer previous_buf);
- static backslashResult exec_command_a(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_C(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_connect(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_cd(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_conninfo(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_copy(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_copyright(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_crosstabview(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_d(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_edit(PsqlScanState scan_state, bool active_branch,
- PQExpBuffer query_buf, PQExpBuffer previous_buf);
- static backslashResult exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
- PQExpBuffer query_buf, bool is_func);
- static backslashResult exec_command_echo(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_elif(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf);
- static backslashResult exec_command_else(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf);
- static backslashResult exec_command_endif(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf);
- static backslashResult exec_command_encoding(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_errverbose(PsqlScanState scan_state, bool active_branch);
- 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);
- static backslashResult exec_command_help(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_html(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_include(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_if(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf);
- static backslashResult exec_command_list(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_lo(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_out(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_print(PsqlScanState scan_state, bool active_branch,
- PQExpBuffer query_buf, PQExpBuffer previous_buf);
- static backslashResult exec_command_password(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_prompt(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_pset(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_quit(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_reset(PsqlScanState scan_state, bool active_branch,
- PQExpBuffer query_buf);
- static backslashResult exec_command_s(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_set(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_setenv(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_sf_sv(PsqlScanState scan_state, bool active_branch,
- const char *cmd, bool is_func);
- static backslashResult exec_command_t(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_T(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_timing(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_unset(PsqlScanState scan_state, bool active_branch,
- const char *cmd);
- static backslashResult exec_command_write(PsqlScanState scan_state, bool active_branch,
- const char *cmd,
- PQExpBuffer query_buf, PQExpBuffer previous_buf);
- static backslashResult exec_command_watch(PsqlScanState scan_state, bool active_branch,
- PQExpBuffer query_buf, PQExpBuffer previous_buf);
- static backslashResult exec_command_x(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_z(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_shell_escape(PsqlScanState scan_state, bool active_branch);
- static backslashResult exec_command_slash_command_help(PsqlScanState scan_state, bool active_branch);
- static char *read_connect_arg(PsqlScanState scan_state);
- static PQExpBuffer gather_boolean_expression(PsqlScanState scan_state);
- static bool is_true_boolean_expression(PsqlScanState scan_state, const char *name);
- static void ignore_boolean_expression(PsqlScanState scan_state);
- static void ignore_slash_options(PsqlScanState scan_state);
- static void ignore_slash_filepipe(PsqlScanState scan_state);
- static void ignore_slash_whole_line(PsqlScanState scan_state);
- static bool is_branching_command(const char *cmd);
- static void save_query_text_state(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf);
- static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf);
- static void copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
- static bool do_connect(enum trivalue reuse_previous_specification,
- char *dbname, char *user, char *host, char *port);
- static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
- int lineno, bool *edited);
- static bool do_shell(const char *command);
- static bool do_watch(PQExpBuffer query_buf, double sleep);
- static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
- Oid *obj_oid);
- static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid,
- PQExpBuffer buf);
- static int strip_lineno_from_objdesc(char *obj);
- static int count_lines_in_buf(PQExpBuffer buf);
- static void print_with_linenumbers(FILE *output, char *lines,
- const char *header_keyword);
- static void minimal_error_message(PGresult *res);
- static void printSSLInfo(void);
- static void printGSSInfo(void);
- static bool printPsetInfo(const char *param, printQueryOpt *popt);
- static char *pset_value_string(const char *param, printQueryOpt *popt);
- #ifdef WIN32
- static void checkWin32Codepage(void);
- #endif
- /*----------
- * HandleSlashCmds:
- *
- * Handles all the different commands that start with '\'.
- * Ordinarily called by MainLoop().
- *
- * scan_state is a lexer working state that is set to continue scanning
- * just after the '\'. The lexer is advanced past the command and all
- * arguments on return.
- *
- * cstack is the current \if stack state. This will be examined, and
- * possibly modified by conditional commands.
- *
- * query_buf contains the query-so-far, which may be modified by
- * execution of the backslash command (for example, \r clears it).
- *
- * previous_buf contains the query most recently sent to the server
- * (empty if none yet). This should not be modified here, but some
- * commands copy its content into query_buf.
- *
- * query_buf and previous_buf will be NULL when executing a "-c"
- * command-line option.
- *
- * Returns a status code indicating what action is desired, see command.h.
- *----------
- */
- backslashResult
- HandleSlashCmds(PsqlScanState scan_state,
- ConditionalStack cstack,
- PQExpBuffer query_buf,
- PQExpBuffer previous_buf)
- {
- backslashResult status;
- char *cmd;
- char *arg;
- Assert(scan_state != NULL);
- Assert(cstack != NULL);
- /* Parse off the command name */
- cmd = psql_scan_slash_command(scan_state);
- /* And try to execute it */
- status = exec_command(cmd, scan_state, cstack, query_buf, previous_buf);
- if (status == PSQL_CMD_UNKNOWN)
- {
- pg_log_error("invalid command \\%s", cmd);
- if (pset.cur_cmd_interactive)
- pg_log_info("Try \\? for help.");
- status = PSQL_CMD_ERROR;
- }
- if (status != PSQL_CMD_ERROR)
- {
- /*
- * Eat any remaining arguments after a valid command. We want to
- * suppress evaluation of backticks in this situation, so transiently
- * push an inactive conditional-stack entry.
- */
- bool active_branch = conditional_active(cstack);
- conditional_stack_push(cstack, IFSTATE_IGNORED);
- while ((arg = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, false)))
- {
- if (active_branch)
- pg_log_warning("\\%s: extra argument \"%s\" ignored", cmd, arg);
- free(arg);
- }
- conditional_stack_pop(cstack);
- }
- else
- {
- /* silently throw away rest of line after an erroneous command */
- while ((arg = psql_scan_slash_option(scan_state,
- OT_WHOLE_LINE, NULL, false)))
- free(arg);
- }
- /* if there is a trailing \\, swallow it */
- psql_scan_slash_command_end(scan_state);
- free(cmd);
- /* some commands write to queryFout, so make sure output is sent */
- fflush(pset.queryFout);
- return status;
- }
- /*
- * Subroutine to actually try to execute a backslash command.
- *
- * The typical "success" result code is PSQL_CMD_SKIP_LINE, although some
- * commands return something else. Failure results are PSQL_CMD_ERROR,
- * unless PSQL_CMD_UNKNOWN is more appropriate.
- */
- static backslashResult
- exec_command(const char *cmd,
- PsqlScanState scan_state,
- ConditionalStack cstack,
- PQExpBuffer query_buf,
- PQExpBuffer previous_buf)
- {
- backslashResult status;
- bool active_branch = conditional_active(cstack);
- /*
- * In interactive mode, warn when we're ignoring a command within a false
- * \if-branch. But we continue on, so as to parse and discard the right
- * amount of parameter text. Each individual backslash command subroutine
- * is responsible for doing nothing after discarding appropriate
- * arguments, if !active_branch.
- */
- if (pset.cur_cmd_interactive && !active_branch &&
- !is_branching_command(cmd))
- {
- pg_log_warning("\\%s command ignored; use \\endif or Ctrl-C to exit current \\if block",
- cmd);
- }
- if (strcmp(cmd, "a") == 0)
- status = exec_command_a(scan_state, active_branch);
- else if (strcmp(cmd, "C") == 0)
- status = exec_command_C(scan_state, active_branch);
- else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
- status = exec_command_connect(scan_state, active_branch);
- else if (strcmp(cmd, "cd") == 0)
- status = exec_command_cd(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "conninfo") == 0)
- status = exec_command_conninfo(scan_state, active_branch);
- else if (pg_strcasecmp(cmd, "copy") == 0)
- status = exec_command_copy(scan_state, active_branch);
- else if (strcmp(cmd, "copyright") == 0)
- status = exec_command_copyright(scan_state, active_branch);
- else if (strcmp(cmd, "crosstabview") == 0)
- status = exec_command_crosstabview(scan_state, active_branch);
- else if (cmd[0] == 'd')
- status = exec_command_d(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0)
- status = exec_command_edit(scan_state, active_branch,
- query_buf, previous_buf);
- else if (strcmp(cmd, "ef") == 0)
- status = exec_command_ef_ev(scan_state, active_branch, query_buf, true);
- else if (strcmp(cmd, "ev") == 0)
- status = exec_command_ef_ev(scan_state, active_branch, query_buf, false);
- else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0 ||
- strcmp(cmd, "warn") == 0)
- status = exec_command_echo(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "elif") == 0)
- status = exec_command_elif(scan_state, cstack, query_buf);
- else if (strcmp(cmd, "else") == 0)
- status = exec_command_else(scan_state, cstack, query_buf);
- else if (strcmp(cmd, "endif") == 0)
- status = exec_command_endif(scan_state, cstack, query_buf);
- else if (strcmp(cmd, "encoding") == 0)
- status = exec_command_encoding(scan_state, active_branch);
- else if (strcmp(cmd, "errverbose") == 0)
- status = exec_command_errverbose(scan_state, active_branch);
- else if (strcmp(cmd, "f") == 0)
- status = exec_command_f(scan_state, active_branch);
- else if (strcmp(cmd, "g") == 0 || strcmp(cmd, "gx") == 0)
- status = exec_command_g(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "gdesc") == 0)
- status = exec_command_gdesc(scan_state, active_branch);
- else if (strcmp(cmd, "gexec") == 0)
- status = exec_command_gexec(scan_state, active_branch);
- else if (strcmp(cmd, "gset") == 0)
- status = exec_command_gset(scan_state, active_branch);
- else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
- status = exec_command_help(scan_state, active_branch);
- else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
- status = exec_command_html(scan_state, active_branch);
- else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0 ||
- strcmp(cmd, "ir") == 0 || strcmp(cmd, "include_relative") == 0)
- status = exec_command_include(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "if") == 0)
- status = exec_command_if(scan_state, cstack, query_buf);
- else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0 ||
- strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0)
- status = exec_command_list(scan_state, active_branch, cmd);
- else if (strncmp(cmd, "lo_", 3) == 0)
- status = exec_command_lo(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
- status = exec_command_out(scan_state, active_branch);
- else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0)
- status = exec_command_print(scan_state, active_branch,
- query_buf, previous_buf);
- else if (strcmp(cmd, "password") == 0)
- status = exec_command_password(scan_state, active_branch);
- else if (strcmp(cmd, "prompt") == 0)
- status = exec_command_prompt(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "pset") == 0)
- status = exec_command_pset(scan_state, active_branch);
- else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0)
- status = exec_command_quit(scan_state, active_branch);
- else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0)
- status = exec_command_reset(scan_state, active_branch, query_buf);
- else if (strcmp(cmd, "s") == 0)
- status = exec_command_s(scan_state, active_branch);
- else if (strcmp(cmd, "set") == 0)
- status = exec_command_set(scan_state, active_branch);
- else if (strcmp(cmd, "setenv") == 0)
- status = exec_command_setenv(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "sf") == 0 || strcmp(cmd, "sf+") == 0)
- status = exec_command_sf_sv(scan_state, active_branch, cmd, true);
- else if (strcmp(cmd, "sv") == 0 || strcmp(cmd, "sv+") == 0)
- status = exec_command_sf_sv(scan_state, active_branch, cmd, false);
- else if (strcmp(cmd, "t") == 0)
- status = exec_command_t(scan_state, active_branch);
- else if (strcmp(cmd, "T") == 0)
- status = exec_command_T(scan_state, active_branch);
- else if (strcmp(cmd, "timing") == 0)
- status = exec_command_timing(scan_state, active_branch);
- else if (strcmp(cmd, "unset") == 0)
- status = exec_command_unset(scan_state, active_branch, cmd);
- else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0)
- status = exec_command_write(scan_state, active_branch, cmd,
- query_buf, previous_buf);
- else if (strcmp(cmd, "watch") == 0)
- status = exec_command_watch(scan_state, active_branch,
- query_buf, previous_buf);
- else if (strcmp(cmd, "x") == 0)
- status = exec_command_x(scan_state, active_branch);
- else if (strcmp(cmd, "z") == 0)
- status = exec_command_z(scan_state, active_branch);
- else if (strcmp(cmd, "!") == 0)
- status = exec_command_shell_escape(scan_state, active_branch);
- else if (strcmp(cmd, "?") == 0)
- status = exec_command_slash_command_help(scan_state, active_branch);
- else
- status = PSQL_CMD_UNKNOWN;
- /*
- * All the commands that return PSQL_CMD_SEND want to execute previous_buf
- * if query_buf is empty. For convenience we implement that here, not in
- * the individual command subroutines.
- */
- if (status == PSQL_CMD_SEND)
- copy_previous_query(query_buf, previous_buf);
- return status;
- }
- /*
- * \a -- toggle field alignment
- *
- * This makes little sense but we keep it around.
- */
- static backslashResult
- exec_command_a(PsqlScanState scan_state, bool active_branch)
- {
- bool success = true;
- if (active_branch)
- {
- if (pset.popt.topt.format != PRINT_ALIGNED)
- success = do_pset("format", "aligned", &pset.popt, pset.quiet);
- else
- success = do_pset("format", "unaligned", &pset.popt, pset.quiet);
- }
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \C -- override table title (formerly change HTML caption)
- */
- static backslashResult
- exec_command_C(PsqlScanState scan_state, bool active_branch)
- {
- bool success = true;
- if (active_branch)
- {
- char *opt = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
- success = do_pset("title", opt, &pset.popt, pset.quiet);
- free(opt);
- }
- else
- ignore_slash_options(scan_state);
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \c or \connect -- connect to database using the specified parameters.
- *
- * \c [-reuse-previous=BOOL] dbname user host port
- *
- * Specifying a parameter as '-' is equivalent to omitting it. Examples:
- *
- * \c - - hst Connect to current database on current port of
- * host "hst" as current user.
- * \c - usr - prt Connect to current database on port "prt" of current host
- * as user "usr".
- * \c dbs Connect to database "dbs" on current port of current host
- * as current user.
- */
- static backslashResult
- exec_command_connect(PsqlScanState scan_state, bool active_branch)
- {
- bool success = true;
- if (active_branch)
- {
- static const char prefix[] = "-reuse-previous=";
- char *opt1,
- *opt2,
- *opt3,
- *opt4;
- enum trivalue reuse_previous = TRI_DEFAULT;
- opt1 = read_connect_arg(scan_state);
- if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0)
- {
- bool on_off;
- success = ParseVariableBool(opt1 + sizeof(prefix) - 1,
- "-reuse-previous",
- &on_off);
- if (success)
- {
- reuse_previous = on_off ? TRI_YES : TRI_NO;
- free(opt1);
- opt1 = read_connect_arg(scan_state);
- }
- }
- if (success) /* give up if reuse_previous was invalid */
- {
- opt2 = read_connect_arg(scan_state);
- opt3 = read_connect_arg(scan_state);
- opt4 = read_connect_arg(scan_state);
- success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
- free(opt2);
- free(opt3);
- free(opt4);
- }
- free(opt1);
- }
- else
- ignore_slash_options(scan_state);
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \cd -- change directory
- */
- static backslashResult
- exec_command_cd(PsqlScanState scan_state, bool active_branch, const char *cmd)
- {
- bool success = true;
- if (active_branch)
- {
- char *opt = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
- char *dir;
- if (opt)
- dir = opt;
- else
- {
- #ifndef WIN32
- struct passwd *pw;
- uid_t user_id = geteuid();
- errno = 0; /* clear errno before call */
- pw = getpwuid(user_id);
- if (!pw)
- {
- pg_log_error("could not get home directory for user ID %ld: %s",
- (long) user_id,
- errno ? strerror(errno) : _("user does not exist"));
- exit(EXIT_FAILURE);
- }
- dir = pw->pw_dir;
- #else /* WIN32 */
- /*
- * On Windows, 'cd' without arguments prints the current
- * directory, so if someone wants to code this here instead...
- */
- dir = "/";
- #endif /* WIN32 */
- }
- if (chdir(dir) == -1)
- {
- pg_log_error("\\%s: could not change directory to \"%s\": %m",
- cmd, dir);
- success = false;
- }
- if (opt)
- free(opt);
- }
- else
- ignore_slash_options(scan_state);
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \conninfo -- display information about the current connection
- */
- static backslashResult
- exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
- {
- if (active_branch)
- {
- char *db = PQdb(pset.db);
- if (db == NULL)
- printf(_("You are currently not connected to a database.\n"));
- else
- {
- char *host = PQhost(pset.db);
- char *hostaddr = PQhostaddr(pset.db);
- /*
- * If the host is an absolute path, the connection is via socket
- * unless overridden by hostaddr
- */
- if (is_absolute_path(host))
- {
- if (hostaddr && *hostaddr)
- printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
- db, PQuser(pset.db), hostaddr, PQport(pset.db));
- else
- printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
- db, PQuser(pset.db), host, PQport(pset.db));
- }
- else
- {
- if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
- printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
- db, PQuser(pset.db), host, hostaddr, PQport(pset.db));
- else
- printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
- db, PQuser(pset.db), host, PQport(pset.db));
- }
- printSSLInfo();
- printGSSInfo();
- }
- }
- return PSQL_CMD_SKIP_LINE;
- }
- /*
- * \copy -- run a COPY command
- */
- static backslashResult
- exec_command_copy(PsqlScanState scan_state, bool active_branch)
- {
- bool success = true;
- if (active_branch)
- {
- char *opt = psql_scan_slash_option(scan_state,
- OT_WHOLE_LINE, NULL, false);
- success = do_copy(opt);
- free(opt);
- }
- else
- ignore_slash_whole_line(scan_state);
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \copyright -- print copyright notice
- */
- static backslashResult
- exec_command_copyright(PsqlScanState scan_state, bool active_branch)
- {
- if (active_branch)
- print_copyright();
- return PSQL_CMD_SKIP_LINE;
- }
- /*
- * \crosstabview -- execute a query and display results in crosstab
- */
- static backslashResult
- exec_command_crosstabview(PsqlScanState scan_state, bool active_branch)
- {
- backslashResult status = PSQL_CMD_SKIP_LINE;
- if (active_branch)
- {
- int i;
- for (i = 0; i < lengthof(pset.ctv_args); i++)
- pset.ctv_args[i] = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
- pset.crosstab_flag = true;
- status = PSQL_CMD_SEND;
- }
- else
- ignore_slash_options(scan_state);
- return status;
- }
- /*
- * \d* commands
- */
- static backslashResult
- exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
- {
- backslashResult status = PSQL_CMD_SKIP_LINE;
- bool success = true;
- if (active_branch)
- {
- char *pattern;
- bool show_verbose,
- show_system;
- /* We don't do SQLID reduction on the pattern yet */
- pattern = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
- show_verbose = strchr(cmd, '+') ? true : false;
- show_system = strchr(cmd, 'S') ? true : false;
- switch (cmd[1])
- {
- case '\0':
- case '+':
- case 'S':
- if (pattern)
- success = describeTableDetails(pattern, show_verbose, show_system);
- else
- /* standard listing of interesting things */
- success = listTables("tvmsE", NULL, show_verbose, show_system);
- break;
- case 'A':
- {
- char *pattern2 = NULL;
- if (pattern && cmd[2] != '\0' && cmd[2] != '+')
- pattern2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
- switch (cmd[2])
- {
- case '\0':
- case '+':
- success = describeAccessMethods(pattern, show_verbose);
- break;
- case 'c':
- success = listOperatorClasses(pattern, pattern2, show_verbose);
- break;
- case 'f':
- success = listOperatorFamilies(pattern, pattern2, show_verbose);
- break;
- case 'o':
- success = listOpFamilyOperators(pattern, pattern2, show_verbose);
- break;
- case 'p':
- success = listOpFamilyProcedures(pattern, pattern2);
- break;
- default:
- status = PSQL_CMD_UNKNOWN;
- break;
- }
- if (pattern2)
- free(pattern2);
- }
- break;
- case 'a':
- success = describeAggregates(pattern, show_verbose, show_system);
- break;
- case 'b':
- success = describeTablespaces(pattern, show_verbose);
- break;
- case 'c':
- success = listConversions(pattern, show_verbose, show_system);
- break;
- case 'C':
- success = listCasts(pattern, show_verbose);
- break;
- case 'd':
- if (strncmp(cmd, "ddp", 3) == 0)
- success = listDefaultACLs(pattern);
- else
- success = objectDescription(pattern, show_system);
- break;
- case 'D':
- success = listDomains(pattern, show_verbose, show_system);
- break;
- case 'f': /* function subsystem */
- switch (cmd[2])
- {
- case '\0':
- case '+':
- case 'S':
- case 'a':
- case 'n':
- case 'p':
- case 't':
- case 'w':
- success = describeFunctions(&cmd[2], pattern, show_verbose, show_system);
- break;
- default:
- status = PSQL_CMD_UNKNOWN;
- break;
- }
- break;
- case 'g':
- /* no longer distinct from \du */
- success = describeRoles(pattern, show_verbose, show_system);
- break;
- case 'l':
- success = do_lo_list();
- break;
- case 'L':
- success = listLanguages(pattern, show_verbose, show_system);
- break;
- case 'n':
- success = listSchemas(pattern, show_verbose, show_system);
- break;
- case 'o':
- success = describeOperators(pattern, show_verbose, show_system);
- break;
- case 'O':
- success = listCollations(pattern, show_verbose, show_system);
- break;
- case 'p':
- success = permissionsList(pattern);
- break;
- case 'P':
- {
- switch (cmd[2])
- {
- case '\0':
- case '+':
- case 't':
- case 'i':
- case 'n':
- success = listPartitionedTables(&cmd[2], pattern, show_verbose);
- break;
- default:
- status = PSQL_CMD_UNKNOWN;
- break;
- }
- }
- break;
- case 'T':
- success = describeTypes(pattern, show_verbose, show_system);
- break;
- case 't':
- case 'v':
- case 'm':
- case 'i':
- case 's':
- case 'E':
- success = listTables(&cmd[1], pattern, show_verbose, show_system);
- break;
- case 'r':
- if (cmd[2] == 'd' && cmd[3] == 's')
- {
- char *pattern2 = NULL;
- if (pattern)
- pattern2 = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
- success = listDbRoleSettings(pattern, pattern2);
- if (pattern2)
- free(pattern2);
- }
- else
- status = PSQL_CMD_UNKNOWN;
- break;
- case 'R':
- switch (cmd[2])
- {
- case 'p':
- if (show_verbose)
- success = describePublications(pattern);
- else
- success = listPublications(pattern);
- break;
- case 's':
- success = describeSubscriptions(pattern, show_verbose);
- break;
- default:
- status = PSQL_CMD_UNKNOWN;
- }
- break;
- case 'u':
- success = describeRoles(pattern, show_verbose, show_system);
- break;
- case 'F': /* text search subsystem */
- switch (cmd[2])
- {
- case '\0':
- case '+':
- success = listTSConfigs(pattern, show_verbose);
- break;
- case 'p':
- success = listTSParsers(pattern, show_verbose);
- break;
- case 'd':
- success = listTSDictionaries(pattern, show_verbose);
- break;
- case 't':
- success = listTSTemplates(pattern, show_verbose);
- break;
- default:
- status = PSQL_CMD_UNKNOWN;
- break;
- }
- break;
- case 'e': /* SQL/MED subsystem */
- switch (cmd[2])
- {
- case 's':
- success = listForeignServers(pattern, show_verbose);
- break;
- case 'u':
- success = listUserMappings(pattern, show_verbose);
- break;
- case 'w':
- success = listForeignDataWrappers(pattern, show_verbose);
- break;
- case 't':
- success = listForeignTables(pattern, show_verbose);
- break;
- default:
- status = PSQL_CMD_UNKNOWN;
- break;
- }
- break;
- case 'x': /* Extensions */
- if (show_verbose)
- success = listExtensionContents(pattern);
- else
- success = listExtensions(pattern);
- break;
- case 'y': /* Event Triggers */
- success = listEventTriggers(pattern, show_verbose);
- break;
- default:
- status = PSQL_CMD_UNKNOWN;
- }
- if (pattern)
- free(pattern);
- }
- else
- ignore_slash_options(scan_state);
- if (!success)
- status = PSQL_CMD_ERROR;
- return status;
- }
- /*
- * \e or \edit -- edit the current query buffer, or edit a file and
- * make it the query buffer
- */
- static backslashResult
- exec_command_edit(PsqlScanState scan_state, bool active_branch,
- PQExpBuffer query_buf, PQExpBuffer previous_buf)
- {
- backslashResult status = PSQL_CMD_SKIP_LINE;
- if (active_branch)
- {
- if (!query_buf)
- {
- pg_log_error("no query buffer");
- status = PSQL_CMD_ERROR;
- }
- else
- {
- char *fname;
- char *ln = NULL;
- int lineno = -1;
- fname = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
- if (fname)
- {
- /* try to get separate lineno arg */
- ln = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
- if (ln == NULL)
- {
- /* only one arg; maybe it is lineno not fname */
- if (fname[0] &&
- strspn(fname, "0123456789") == strlen(fname))
- {
- /* all digits, so assume it is lineno */
- ln = fname;
- fname = NULL;
- }
- }
- }
- if (ln)
- {
- lineno = atoi(ln);
- if (lineno < 1)
- {
- pg_log_error("invalid line number: %s", ln);
- status = PSQL_CMD_ERROR;
- }
- }
- if (status != PSQL_CMD_ERROR)
- {
- expand_tilde(&fname);
- if (fname)
- canonicalize_path(fname);
- /* If query_buf is empty, recall previous query for editing */
- copy_previous_query(query_buf, previous_buf);
- if (do_edit(fname, query_buf, lineno, NULL))
- status = PSQL_CMD_NEWEDIT;
- else
- status = PSQL_CMD_ERROR;
- }
- if (fname)
- free(fname);
- if (ln)
- free(ln);
- }
- }
- else
- ignore_slash_options(scan_state);
- return status;
- }
- /*
- * \ef/\ev -- edit the named function/view, or
- * present a blank CREATE FUNCTION/VIEW template if no argument is given
- */
- static backslashResult
- exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
- PQExpBuffer query_buf, bool is_func)
- {
- backslashResult status = PSQL_CMD_SKIP_LINE;
- if (active_branch)
- {
- char *obj_desc = psql_scan_slash_option(scan_state,
- OT_WHOLE_LINE,
- NULL, true);
- int lineno = -1;
- if (pset.sversion < (is_func ? 80400 : 70400))
- {
- char sverbuf[32];
- formatPGVersionNumber(pset.sversion, false,
- sverbuf, sizeof(sverbuf));
- if (is_func)
- pg_log_error("The server (version %s) does not support editing function source.",
- sverbuf);
- else
- pg_log_error("The server (version %s) does not support editing view definitions.",
- sverbuf);
- status = PSQL_CMD_ERROR;
- }
- else if (!query_buf)
- {
- pg_log_error("no query buffer");
- status = PSQL_CMD_ERROR;
- }
- else
- {
- Oid obj_oid = InvalidOid;
- EditableObjectType eot = is_func ? EditableFunction : EditableView;
- lineno = strip_lineno_from_objdesc(obj_desc);
- if (lineno == 0)
- {
- /* error already reported */
- status = PSQL_CMD_ERROR;
- }
- else if (!obj_desc)
- {
- /* set up an empty command to fill in */
- resetPQExpBuffer(query_buf);
- if (is_func)
- appendPQExpBufferStr(query_buf,
- "CREATE FUNCTION ( )\n"
- " RETURNS \n"
- " LANGUAGE \n"
- " -- common options: IMMUTABLE STABLE STRICT SECURITY DEFINER\n"
- "AS $function$\n"
- "\n$function$\n");
- else
- appendPQExpBufferStr(query_buf,
- "CREATE VIEW AS\n"
- " SELECT \n"
- " -- something...\n");
- }
- else if (!lookup_object_oid(eot, obj_desc, &obj_oid))
- {
- /* error already reported */
- status = PSQL_CMD_ERROR;
- }
- else if (!get_create_object_cmd(eot, obj_oid, query_buf))
- {
- /* error already reported */
- status = PSQL_CMD_ERROR;
- }
- else if (is_func && lineno > 0)
- {
- /*
- * lineno "1" should correspond to the first line of the
- * function body. We expect that pg_get_functiondef() will
- * emit that on a line beginning with "AS ", and that there
- * can be no such line before the real start of the function
- * body. Increment lineno by the number of lines before that
- * line, so that it becomes relative to the first line of the
- * function definition.
- */
- const char *lines = query_buf->data;
- while (*lines != '\0')
- {
- if (strncmp(lines, "AS ", 3) == 0)
- break;
- lineno++;
- /* find start of next line */
- lines = strchr(lines, '\n');
- if (!lines)
- break;
- lines++;
- }
- }
- }
- if (status != PSQL_CMD_ERROR)
- {
- bool edited = false;
- if (!do_edit(NULL, query_buf, lineno, &edited))
- status = PSQL_CMD_ERROR;
- else if (!edited)
- puts(_("No changes"));
- else
- status = PSQL_CMD_NEWEDIT;
- }
- if (obj_desc)
- free(obj_desc);
- }
- else
- ignore_slash_whole_line(scan_state);
- return status;
- }
- /*
- * \echo, \qecho, and \warn -- echo arguments to stdout, query output, or stderr
- */
- static backslashResult
- exec_command_echo(PsqlScanState scan_state, bool active_branch, const char *cmd)
- {
- if (active_branch)
- {
- char *value;
- char quoted;
- bool no_newline = false;
- bool first = true;
- FILE *fout;
- if (strcmp(cmd, "qecho") == 0)
- fout = pset.queryFout;
- else if (strcmp(cmd, "warn") == 0)
- fout = stderr;
- else
- fout = stdout;
- while ((value = psql_scan_slash_option(scan_state,
- OT_NORMAL, "ed, false)))
- {
- if (first && !no_newline && !quoted && strcmp(value, "-n") == 0)
- no_newline = true;
- else
- {
- if (first)
- first = false;
- else
- fputc(' ', fout);
- fputs(value, fout);
- }
- free(value);
- }
- if (!no_newline)
- fputs("\n", fout);
- }
- else
- ignore_slash_options(scan_state);
- return PSQL_CMD_SKIP_LINE;
- }
- /*
- * \encoding -- set/show client side encoding
- */
- static backslashResult
- exec_command_encoding(PsqlScanState scan_state, bool active_branch)
- {
- if (active_branch)
- {
- char *encoding = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, false);
- if (!encoding)
- {
- /* show encoding */
- puts(pg_encoding_to_char(pset.encoding));
- }
- else
- {
- /* set encoding */
- if (PQsetClientEncoding(pset.db, encoding) == -1)
- pg_log_error("%s: invalid encoding name or conversion procedure not found", encoding);
- else
- {
- /* save encoding info into psql internal data */
- pset.encoding = PQclientEncoding(pset.db);
- pset.popt.topt.encoding = pset.encoding;
- SetVariable(pset.vars, "ENCODING",
- pg_encoding_to_char(pset.encoding));
- }
- free(encoding);
- }
- }
- else
- ignore_slash_options(scan_state);
- return PSQL_CMD_SKIP_LINE;
- }
- /*
- * \errverbose -- display verbose message from last failed query
- */
- static backslashResult
- exec_command_errverbose(PsqlScanState scan_state, bool active_branch)
- {
- if (active_branch)
- {
- if (pset.last_error_result)
- {
- char *msg;
- msg = PQresultVerboseErrorMessage(pset.last_error_result,
- PQERRORS_VERBOSE,
- PQSHOW_CONTEXT_ALWAYS);
- if (msg)
- {
- pg_log_error("%s", msg);
- PQfreemem(msg);
- }
- else
- puts(_("out of memory"));
- }
- else
- puts(_("There is no previous error."));
- }
- return PSQL_CMD_SKIP_LINE;
- }
- /*
- * \f -- change field separator
- */
- static backslashResult
- exec_command_f(PsqlScanState scan_state, bool active_branch)
- {
- bool success = true;
- if (active_branch)
- {
- char *fname = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, false);
- success = do_pset("fieldsep", fname, &pset.popt, pset.quiet);
- free(fname);
- }
- else
- ignore_slash_options(scan_state);
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \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 sets "expanded=on" 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;
- /*
- * 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] == '(')
- {
- /* 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
- {
- expand_tilde(&fname);
- pset.gfname = pg_strdup(fname);
- }
- if (strcmp(cmd, "gx") == 0)
- {
- /* save settings if not done already, then force expanded=on */
- if (pset.gsavepopt == NULL)
- pset.gsavepopt = savePsetInfo(&pset.popt);
- pset.popt.topt.expanded = 1;
- }
- status = PSQL_CMD_SEND;
- }
- free(fname);
- return status;
- }
- /*
- * Process parenthesized pset options for \g
- *
- * Note: okay to modify first_option, but not to free it; caller does that
- */
- 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 parentheses, parse/execute it */
- if (optlen > 0)
- {
- /* We can have either "name" or "name=value" */
- 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);
- /* If we failed after already changing some options, undo side-effects */
- if (!success && active_branch && pset.gsavepopt)
- {
- restorePsetInfo(&pset.popt, pset.gsavepopt);
- pset.gsavepopt = NULL;
- }
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \gdesc -- describe query result
- */
- static backslashResult
- exec_command_gdesc(PsqlScanState scan_state, bool active_branch)
- {
- backslashResult status = PSQL_CMD_SKIP_LINE;
- if (active_branch)
- {
- pset.gdesc_flag = true;
- status = PSQL_CMD_SEND;
- }
- return status;
- }
- /*
- * \gexec -- send query and execute each field of result
- */
- static backslashResult
- exec_command_gexec(PsqlScanState scan_state, bool active_branch)
- {
- backslashResult status = PSQL_CMD_SKIP_LINE;
- if (active_branch)
- {
- pset.gexec_flag = true;
- status = PSQL_CMD_SEND;
- }
- return status;
- }
- /*
- * \gset [prefix] -- send query and store result into variables
- */
- static backslashResult
- exec_command_gset(PsqlScanState scan_state, bool active_branch)
- {
- backslashResult status = PSQL_CMD_SKIP_LINE;
- if (active_branch)
- {
- char *prefix = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, false);
- if (prefix)
- pset.gset_prefix = prefix;
- else
- {
- /* we must set a non-NULL prefix to trigger storing */
- pset.gset_prefix = pg_strdup("");
- }
- /* gset_prefix is freed later */
- status = PSQL_CMD_SEND;
- }
- else
- ignore_slash_options(scan_state);
- return status;
- }
- /*
- * \help [topic] -- print help about SQL commands
- */
- static backslashResult
- exec_command_help(PsqlScanState scan_state, bool active_branch)
- {
- if (active_branch)
- {
- char *opt = psql_scan_slash_option(scan_state,
- OT_WHOLE_LINE, NULL, false);
- size_t len;
- /* strip any trailing spaces and semicolons */
- if (opt)
- {
- len = strlen(opt);
- while (len > 0 &&
- (isspace((unsigned char) opt[len - 1])
- || opt[len - 1] == ';'))
- opt[--len] = '\0';
- }
- helpSQL(opt, pset.popt.topt.pager);
- free(opt);
- }
- else
- ignore_slash_whole_line(scan_state);
- return PSQL_CMD_SKIP_LINE;
- }
- /*
- * \H and \html -- toggle HTML formatting
- */
- static backslashResult
- exec_command_html(PsqlScanState scan_state, bool active_branch)
- {
- bool success = true;
- if (active_branch)
- {
- if (pset.popt.topt.format != PRINT_HTML)
- success = do_pset("format", "html", &pset.popt, pset.quiet);
- else
- success = do_pset("format", "aligned", &pset.popt, pset.quiet);
- }
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \i and \ir -- include a file
- */
- static backslashResult
- exec_command_include(PsqlScanState scan_state, bool active_branch, const char *cmd)
- {
- bool success = true;
- if (active_branch)
- {
- char *fname = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
- if (!fname)
- {
- pg_log_error("\\%s: missing required argument", cmd);
- success = false;
- }
- else
- {
- bool include_relative;
- include_relative = (strcmp(cmd, "ir") == 0
- || strcmp(cmd, "include_relative") == 0);
- expand_tilde(&fname);
- success = (process_file(fname, include_relative) == EXIT_SUCCESS);
- free(fname);
- }
- }
- else
- ignore_slash_options(scan_state);
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \if <expr> -- beginning of an \if..\endif block
- *
- * <expr> is parsed as a boolean expression. Invalid expressions will emit a
- * warning and be treated as false. Statements that follow a false expression
- * will be parsed but ignored. Note that in the case where an \if statement
- * is itself within an inactive section of a block, then the entire inner
- * \if..\endif block will be parsed but ignored.
- */
- static backslashResult
- exec_command_if(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf)
- {
- if (conditional_active(cstack))
- {
- /*
- * First, push a new active stack entry; this ensures that the lexer
- * will perform variable substitution and backtick evaluation while
- * scanning the expression. (That should happen anyway, since we know
- * we're in an active outer branch, but let's be sure.)
- */
- conditional_stack_push(cstack, IFSTATE_TRUE);
- /* Remember current query state in case we need to restore later */
- save_query_text_state(scan_state, cstack, query_buf);
- /*
- * Evaluate the expression; if it's false, change to inactive state.
- */
- if (!is_true_boolean_expression(scan_state, "\\if expression"))
- conditional_stack_poke(cstack, IFSTATE_FALSE);
- }
- else
- {
- /*
- * We're within an inactive outer branch, so this entire \if block
- * will be ignored. We don't want to evaluate the expression, so push
- * the "ignored" stack state before scanning it.
- */
- conditional_stack_push(cstack, IFSTATE_IGNORED);
- /* Remember current query state in case we need to restore later */
- save_query_text_state(scan_state, cstack, query_buf);
- ignore_boolean_expression(scan_state);
- }
- return PSQL_CMD_SKIP_LINE;
- }
- /*
- * \elif <expr> -- alternative branch in an \if..\endif block
- *
- * <expr> is evaluated the same as in \if <expr>.
- */
- static backslashResult
- exec_command_elif(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf)
- {
- bool success = true;
- switch (conditional_stack_peek(cstack))
- {
- case IFSTATE_TRUE:
- /*
- * Just finished active branch of this \if block. Update saved
- * state so we will keep whatever data was put in query_buf by the
- * active branch.
- */
- save_query_text_state(scan_state, cstack, query_buf);
- /*
- * Discard \elif expression and ignore the rest until \endif.
- * Switch state before reading expression to ensure proper lexer
- * behavior.
- */
- conditional_stack_poke(cstack, IFSTATE_IGNORED);
- ignore_boolean_expression(scan_state);
- break;
- case IFSTATE_FALSE:
- /*
- * Discard any query text added by the just-skipped branch.
- */
- discard_query_text(scan_state, cstack, query_buf);
- /*
- * Have not yet found a true expression in this \if block, so this
- * might be the first. We have to change state before examining
- * the expression, or the lexer won't do the right thing.
- */
- conditional_stack_poke(cstack, IFSTATE_TRUE);
- if (!is_true_boolean_expression(scan_state, "\\elif expression"))
- conditional_stack_poke(cstack, IFSTATE_FALSE);
- break;
- case IFSTATE_IGNORED:
- /*
- * Discard any query text added by the just-skipped branch.
- */
- discard_query_text(scan_state, cstack, query_buf);
- /*
- * Skip expression and move on. Either the \if block already had
- * an active section, or whole block is being skipped.
- */
- ignore_boolean_expression(scan_state);
- break;
- case IFSTATE_ELSE_TRUE:
- case IFSTATE_ELSE_FALSE:
- pg_log_error("\\elif: cannot occur after \\else");
- success = false;
- break;
- case IFSTATE_NONE:
- /* no \if to elif from */
- pg_log_error("\\elif: no matching \\if");
- success = false;
- break;
- }
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \else -- final alternative in an \if..\endif block
- *
- * Statements within an \else branch will only be executed if
- * all previous \if and \elif expressions evaluated to false
- * and the block was not itself being ignored.
- */
- static backslashResult
- exec_command_else(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf)
- {
- bool success = true;
- switch (conditional_stack_peek(cstack))
- {
- case IFSTATE_TRUE:
- /*
- * Just finished active branch of this \if block. Update saved
- * state so we will keep whatever data was put in query_buf by the
- * active branch.
- */
- save_query_text_state(scan_state, cstack, query_buf);
- /* Now skip the \else branch */
- conditional_stack_poke(cstack, IFSTATE_ELSE_FALSE);
- break;
- case IFSTATE_FALSE:
- /*
- * Discard any query text added by the just-skipped branch.
- */
- discard_query_text(scan_state, cstack, query_buf);
- /*
- * We've not found any true \if or \elif expression, so execute
- * the \else branch.
- */
- conditional_stack_poke(cstack, IFSTATE_ELSE_TRUE);
- break;
- case IFSTATE_IGNORED:
- /*
- * Discard any query text added by the just-skipped branch.
- */
- discard_query_text(scan_state, cstack, query_buf);
- /*
- * Either we previously processed the active branch of this \if,
- * or the whole \if block is being skipped. Either way, skip the
- * \else branch.
- */
- conditional_stack_poke(cstack, IFSTATE_ELSE_FALSE);
- break;
- case IFSTATE_ELSE_TRUE:
- case IFSTATE_ELSE_FALSE:
- pg_log_error("\\else: cannot occur after \\else");
- success = false;
- break;
- case IFSTATE_NONE:
- /* no \if to else from */
- pg_log_error("\\else: no matching \\if");
- success = false;
- break;
- }
- return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
- }
- /*
- * \endif -- ends an \if...\endif block
- */
- static backslashResult
- exec_command_endif(PsqlScanState scan_state, ConditionalStack cstack,
- PQExpBuffer query_buf)
- {
- bool success = …
Large files files are truncated, but you can click here to view the full file