LCOV - code coverage report
Current view: top level - src/bin/psql - command.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 65.5 % 2595 1700
Test Date: 2026-03-01 05:14:52 Functions: 91.7 % 109 100
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * psql - the PostgreSQL interactive terminal
       3              :  *
       4              :  * Copyright (c) 2000-2026, PostgreSQL Global Development Group
       5              :  *
       6              :  * src/bin/psql/command.c
       7              :  */
       8              : #include "postgres_fe.h"
       9              : 
      10              : #include <ctype.h>
      11              : #include <time.h>
      12              : #include <pwd.h>
      13              : #include <utime.h>
      14              : #ifndef WIN32
      15              : #include <sys/stat.h>         /* for stat() */
      16              : #include <sys/time.h>         /* for setitimer() */
      17              : #include <fcntl.h>                /* open() flags */
      18              : #include <unistd.h>               /* for geteuid(), getpid(), stat() */
      19              : #else
      20              : #include <win32.h>
      21              : #include <io.h>
      22              : #include <fcntl.h>
      23              : #include <direct.h>
      24              : #include <sys/stat.h>         /* for stat() */
      25              : #endif
      26              : 
      27              : #include "catalog/pg_class_d.h"
      28              : #include "command.h"
      29              : #include "common.h"
      30              : #include "common/logging.h"
      31              : #include "common/string.h"
      32              : #include "copy.h"
      33              : #include "describe.h"
      34              : #include "fe_utils/cancel.h"
      35              : #include "fe_utils/print.h"
      36              : #include "fe_utils/string_utils.h"
      37              : #include "help.h"
      38              : #include "input.h"
      39              : #include "large_obj.h"
      40              : #include "libpq/pqcomm.h"
      41              : #include "mainloop.h"
      42              : #include "pqexpbuffer.h"
      43              : #include "psqlscanslash.h"
      44              : #include "settings.h"
      45              : #include "variables.h"
      46              : 
      47              : /*
      48              :  * Editable database object types.
      49              :  */
      50              : typedef enum EditableObjectType
      51              : {
      52              :     EditableFunction,
      53              :     EditableView,
      54              : } EditableObjectType;
      55              : 
      56              : /* local function declarations */
      57              : static backslashResult exec_command(const char *cmd,
      58              :                                     PsqlScanState scan_state,
      59              :                                     ConditionalStack cstack,
      60              :                                     PQExpBuffer query_buf,
      61              :                                     PQExpBuffer previous_buf);
      62              : static backslashResult exec_command_a(PsqlScanState scan_state, bool active_branch);
      63              : static backslashResult exec_command_bind(PsqlScanState scan_state, bool active_branch);
      64              : static backslashResult exec_command_bind_named(PsqlScanState scan_state, bool active_branch,
      65              :                                                const char *cmd);
      66              : static backslashResult exec_command_C(PsqlScanState scan_state, bool active_branch);
      67              : static backslashResult exec_command_connect(PsqlScanState scan_state, bool active_branch);
      68              : static backslashResult exec_command_cd(PsqlScanState scan_state, bool active_branch,
      69              :                                        const char *cmd);
      70              : static backslashResult exec_command_close_prepared(PsqlScanState scan_state,
      71              :                                                    bool active_branch, const char *cmd);
      72              : static backslashResult exec_command_conninfo(PsqlScanState scan_state, bool active_branch);
      73              : static backslashResult exec_command_copy(PsqlScanState scan_state, bool active_branch);
      74              : static backslashResult exec_command_copyright(PsqlScanState scan_state, bool active_branch);
      75              : static backslashResult exec_command_crosstabview(PsqlScanState scan_state, bool active_branch);
      76              : static backslashResult exec_command_d(PsqlScanState scan_state, bool active_branch,
      77              :                                       const char *cmd);
      78              : static bool exec_command_dfo(PsqlScanState scan_state, const char *cmd,
      79              :                              const char *pattern,
      80              :                              bool show_verbose, bool show_system);
      81              : static backslashResult exec_command_edit(PsqlScanState scan_state, bool active_branch,
      82              :                                          PQExpBuffer query_buf, PQExpBuffer previous_buf);
      83              : static backslashResult exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
      84              :                                           PQExpBuffer query_buf, bool is_func);
      85              : static backslashResult exec_command_echo(PsqlScanState scan_state, bool active_branch,
      86              :                                          const char *cmd);
      87              : static backslashResult exec_command_elif(PsqlScanState scan_state, ConditionalStack cstack,
      88              :                                          PQExpBuffer query_buf);
      89              : static backslashResult exec_command_else(PsqlScanState scan_state, ConditionalStack cstack,
      90              :                                          PQExpBuffer query_buf);
      91              : static backslashResult exec_command_endif(PsqlScanState scan_state, ConditionalStack cstack,
      92              :                                           PQExpBuffer query_buf);
      93              : static backslashResult exec_command_endpipeline(PsqlScanState scan_state, bool active_branch);
      94              : static backslashResult exec_command_encoding(PsqlScanState scan_state, bool active_branch);
      95              : static backslashResult exec_command_errverbose(PsqlScanState scan_state, bool active_branch);
      96              : static backslashResult exec_command_f(PsqlScanState scan_state, bool active_branch);
      97              : static backslashResult exec_command_flush(PsqlScanState scan_state, bool active_branch);
      98              : static backslashResult exec_command_flushrequest(PsqlScanState scan_state, bool active_branch);
      99              : static backslashResult exec_command_g(PsqlScanState scan_state, bool active_branch,
     100              :                                       const char *cmd);
     101              : static backslashResult process_command_g_options(char *first_option,
     102              :                                                  PsqlScanState scan_state,
     103              :                                                  bool active_branch,
     104              :                                                  const char *cmd);
     105              : static backslashResult exec_command_gdesc(PsqlScanState scan_state, bool active_branch);
     106              : static backslashResult exec_command_getenv(PsqlScanState scan_state, bool active_branch,
     107              :                                            const char *cmd);
     108              : static backslashResult exec_command_gexec(PsqlScanState scan_state, bool active_branch);
     109              : static backslashResult exec_command_getresults(PsqlScanState scan_state, bool active_branch);
     110              : static backslashResult exec_command_gset(PsqlScanState scan_state, bool active_branch);
     111              : static backslashResult exec_command_help(PsqlScanState scan_state, bool active_branch);
     112              : static backslashResult exec_command_html(PsqlScanState scan_state, bool active_branch);
     113              : static backslashResult exec_command_include(PsqlScanState scan_state, bool active_branch,
     114              :                                             const char *cmd);
     115              : static backslashResult exec_command_if(PsqlScanState scan_state, ConditionalStack cstack,
     116              :                                        PQExpBuffer query_buf);
     117              : static backslashResult exec_command_list(PsqlScanState scan_state, bool active_branch,
     118              :                                          const char *cmd);
     119              : static backslashResult exec_command_lo(PsqlScanState scan_state, bool active_branch,
     120              :                                        const char *cmd);
     121              : static backslashResult exec_command_out(PsqlScanState scan_state, bool active_branch);
     122              : static backslashResult exec_command_print(PsqlScanState scan_state, bool active_branch,
     123              :                                           PQExpBuffer query_buf, PQExpBuffer previous_buf);
     124              : static backslashResult exec_command_parse(PsqlScanState scan_state, bool active_branch,
     125              :                                           const char *cmd);
     126              : static backslashResult exec_command_password(PsqlScanState scan_state, bool active_branch);
     127              : static backslashResult exec_command_prompt(PsqlScanState scan_state, bool active_branch,
     128              :                                            const char *cmd);
     129              : static backslashResult exec_command_pset(PsqlScanState scan_state, bool active_branch);
     130              : static backslashResult exec_command_quit(PsqlScanState scan_state, bool active_branch);
     131              : static backslashResult exec_command_reset(PsqlScanState scan_state, bool active_branch,
     132              :                                           PQExpBuffer query_buf);
     133              : static backslashResult exec_command_restrict(PsqlScanState scan_state, bool active_branch,
     134              :                                              const char *cmd);
     135              : static backslashResult exec_command_s(PsqlScanState scan_state, bool active_branch);
     136              : static backslashResult exec_command_sendpipeline(PsqlScanState scan_state, bool active_branch);
     137              : static backslashResult exec_command_set(PsqlScanState scan_state, bool active_branch);
     138              : static backslashResult exec_command_setenv(PsqlScanState scan_state, bool active_branch,
     139              :                                            const char *cmd);
     140              : static backslashResult exec_command_sf_sv(PsqlScanState scan_state, bool active_branch,
     141              :                                           const char *cmd, bool is_func);
     142              : static backslashResult exec_command_startpipeline(PsqlScanState scan_state, bool active_branch);
     143              : static backslashResult exec_command_syncpipeline(PsqlScanState scan_state, bool active_branch);
     144              : static backslashResult exec_command_t(PsqlScanState scan_state, bool active_branch);
     145              : static backslashResult exec_command_T(PsqlScanState scan_state, bool active_branch);
     146              : static backslashResult exec_command_timing(PsqlScanState scan_state, bool active_branch);
     147              : static backslashResult exec_command_unrestrict(PsqlScanState scan_state, bool active_branch,
     148              :                                                const char *cmd);
     149              : static backslashResult exec_command_unset(PsqlScanState scan_state, bool active_branch,
     150              :                                           const char *cmd);
     151              : static backslashResult exec_command_write(PsqlScanState scan_state, bool active_branch,
     152              :                                           const char *cmd,
     153              :                                           PQExpBuffer query_buf, PQExpBuffer previous_buf);
     154              : static backslashResult exec_command_watch(PsqlScanState scan_state, bool active_branch,
     155              :                                           PQExpBuffer query_buf, PQExpBuffer previous_buf);
     156              : static backslashResult exec_command_x(PsqlScanState scan_state, bool active_branch);
     157              : static backslashResult exec_command_z(PsqlScanState scan_state, bool active_branch,
     158              :                                       const char *cmd);
     159              : static backslashResult exec_command_shell_escape(PsqlScanState scan_state, bool active_branch);
     160              : static backslashResult exec_command_slash_command_help(PsqlScanState scan_state, bool active_branch);
     161              : static char *read_connect_arg(PsqlScanState scan_state);
     162              : static PQExpBuffer gather_boolean_expression(PsqlScanState scan_state);
     163              : static bool is_true_boolean_expression(PsqlScanState scan_state, const char *name);
     164              : static void ignore_boolean_expression(PsqlScanState scan_state);
     165              : static void ignore_slash_options(PsqlScanState scan_state);
     166              : static void ignore_slash_filepipe(PsqlScanState scan_state);
     167              : static void ignore_slash_whole_line(PsqlScanState scan_state);
     168              : static bool is_branching_command(const char *cmd);
     169              : static void save_query_text_state(PsqlScanState scan_state, ConditionalStack cstack,
     170              :                                   PQExpBuffer query_buf);
     171              : static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack,
     172              :                                PQExpBuffer query_buf);
     173              : static bool copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
     174              : static bool do_connect(enum trivalue reuse_previous_specification,
     175              :                        char *dbname, char *user, char *host, char *port);
     176              : static void wait_until_connected(PGconn *conn);
     177              : static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
     178              :                     int lineno, bool discard_on_quit, bool *edited);
     179              : static bool do_shell(const char *command);
     180              : static bool do_watch(PQExpBuffer query_buf, double sleep, int iter, int min_rows);
     181              : static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
     182              :                               Oid *obj_oid);
     183              : static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid,
     184              :                                   PQExpBuffer buf);
     185              : static int  strip_lineno_from_objdesc(char *obj);
     186              : static int  count_lines_in_buf(PQExpBuffer buf);
     187              : static void print_with_linenumbers(FILE *output, char *lines, bool is_func);
     188              : static void minimal_error_message(PGresult *res);
     189              : 
     190              : static void printSSLInfo(void);
     191              : static void printGSSInfo(void);
     192              : static bool printPsetInfo(const char *param, printQueryOpt *popt);
     193              : static char *pset_value_string(const char *param, printQueryOpt *popt);
     194              : 
     195              : #ifdef WIN32
     196              : static void checkWin32Codepage(void);
     197              : #endif
     198              : 
     199              : static bool restricted;
     200              : static char *restrict_key;
     201              : 
     202              : 
     203              : /*----------
     204              :  * HandleSlashCmds:
     205              :  *
     206              :  * Handles all the different commands that start with '\'.
     207              :  * Ordinarily called by MainLoop().
     208              :  *
     209              :  * scan_state is a lexer working state that is set to continue scanning
     210              :  * just after the '\'.  The lexer is advanced past the command and all
     211              :  * arguments on return.
     212              :  *
     213              :  * cstack is the current \if stack state.  This will be examined, and
     214              :  * possibly modified by conditional commands.
     215              :  *
     216              :  * query_buf contains the query-so-far, which may be modified by
     217              :  * execution of the backslash command (for example, \r clears it).
     218              :  *
     219              :  * previous_buf contains the query most recently sent to the server
     220              :  * (empty if none yet).  This should not be modified here, but some
     221              :  * commands copy its content into query_buf.
     222              :  *
     223              :  * query_buf and previous_buf will be NULL when executing a "-c"
     224              :  * command-line option.
     225              :  *
     226              :  * Returns a status code indicating what action is desired, see command.h.
     227              :  *----------
     228              :  */
     229              : 
     230              : backslashResult
     231        28320 : HandleSlashCmds(PsqlScanState scan_state,
     232              :                 ConditionalStack cstack,
     233              :                 PQExpBuffer query_buf,
     234              :                 PQExpBuffer previous_buf)
     235              : {
     236              :     backslashResult status;
     237              :     char       *cmd;
     238              :     char       *arg;
     239              : 
     240              :     Assert(scan_state != NULL);
     241              :     Assert(cstack != NULL);
     242              : 
     243              :     /* Parse off the command name */
     244        28320 :     cmd = psql_scan_slash_command(scan_state);
     245              : 
     246              :     /*
     247              :      * And try to execute it.
     248              :      *
     249              :      * If we are in "restricted" mode, the only allowable backslash command is
     250              :      * \unrestrict (to exit restricted mode).
     251              :      */
     252        28320 :     if (restricted && strcmp(cmd, "unrestrict") != 0)
     253              :     {
     254            1 :         pg_log_error("backslash commands are restricted; only \\unrestrict is allowed");
     255            1 :         status = PSQL_CMD_ERROR;
     256              :     }
     257              :     else
     258        28319 :         status = exec_command(cmd, scan_state, cstack, query_buf, previous_buf);
     259              : 
     260        28318 :     if (status == PSQL_CMD_UNKNOWN)
     261              :     {
     262            9 :         pg_log_error("invalid command \\%s", cmd);
     263            9 :         if (pset.cur_cmd_interactive)
     264            0 :             pg_log_error_hint("Try \\? for help.");
     265            9 :         status = PSQL_CMD_ERROR;
     266              :     }
     267              : 
     268        28318 :     if (status != PSQL_CMD_ERROR)
     269              :     {
     270              :         /*
     271              :          * Eat any remaining arguments after a valid command.  We want to
     272              :          * suppress evaluation of backticks in this situation, so transiently
     273              :          * push an inactive conditional-stack entry.
     274              :          */
     275        27719 :         bool        active_branch = conditional_active(cstack);
     276              : 
     277        27719 :         conditional_stack_push(cstack, IFSTATE_IGNORED);
     278        27734 :         while ((arg = psql_scan_slash_option(scan_state,
     279              :                                              OT_NORMAL, NULL, false)))
     280              :         {
     281           15 :             if (active_branch)
     282           15 :                 pg_log_warning("\\%s: extra argument \"%s\" ignored", cmd, arg);
     283           15 :             free(arg);
     284              :         }
     285        27719 :         conditional_stack_pop(cstack);
     286              :     }
     287              :     else
     288              :     {
     289              :         /* silently throw away rest of line after an erroneous command */
     290          609 :         while ((arg = psql_scan_slash_option(scan_state,
     291              :                                              OT_WHOLE_LINE, NULL, false)))
     292           10 :             free(arg);
     293              :     }
     294              : 
     295              :     /* if there is a trailing \\, swallow it */
     296        28318 :     psql_scan_slash_command_end(scan_state);
     297              : 
     298        28318 :     free(cmd);
     299              : 
     300              :     /* some commands write to queryFout, so make sure output is sent */
     301        28318 :     fflush(pset.queryFout);
     302              : 
     303        28318 :     return status;
     304              : }
     305              : 
     306              : 
     307              : /*
     308              :  * Subroutine to actually try to execute a backslash command.
     309              :  *
     310              :  * The typical "success" result code is PSQL_CMD_SKIP_LINE, although some
     311              :  * commands return something else.  Failure result code is PSQL_CMD_ERROR,
     312              :  * unless PSQL_CMD_UNKNOWN is more appropriate.
     313              :  */
     314              : static backslashResult
     315        28319 : exec_command(const char *cmd,
     316              :              PsqlScanState scan_state,
     317              :              ConditionalStack cstack,
     318              :              PQExpBuffer query_buf,
     319              :              PQExpBuffer previous_buf)
     320              : {
     321              :     backslashResult status;
     322        28319 :     bool        active_branch = conditional_active(cstack);
     323              : 
     324              :     /*
     325              :      * In interactive mode, warn when we're ignoring a command within a false
     326              :      * \if-branch.  But we continue on, so as to parse and discard the right
     327              :      * amount of parameter text.  Each individual backslash command subroutine
     328              :      * is responsible for doing nothing after discarding appropriate
     329              :      * arguments, if !active_branch.
     330              :      */
     331        28319 :     if (pset.cur_cmd_interactive && !active_branch &&
     332            0 :         !is_branching_command(cmd))
     333              :     {
     334            0 :         pg_log_warning("\\%s command ignored; use \\endif or Ctrl-C to exit current \\if block",
     335              :                        cmd);
     336              :     }
     337              : 
     338        28319 :     if (strcmp(cmd, "a") == 0)
     339           27 :         status = exec_command_a(scan_state, active_branch);
     340        28292 :     else if (strcmp(cmd, "bind") == 0)
     341          336 :         status = exec_command_bind(scan_state, active_branch);
     342        27956 :     else if (strcmp(cmd, "bind_named") == 0)
     343           88 :         status = exec_command_bind_named(scan_state, active_branch, cmd);
     344        27868 :     else if (strcmp(cmd, "C") == 0)
     345            3 :         status = exec_command_C(scan_state, active_branch);
     346        27865 :     else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
     347          184 :         status = exec_command_connect(scan_state, active_branch);
     348        27681 :     else if (strcmp(cmd, "cd") == 0)
     349            3 :         status = exec_command_cd(scan_state, active_branch, cmd);
     350        27678 :     else if (strcmp(cmd, "close_prepared") == 0)
     351           25 :         status = exec_command_close_prepared(scan_state, active_branch, cmd);
     352        27653 :     else if (strcmp(cmd, "conninfo") == 0)
     353            3 :         status = exec_command_conninfo(scan_state, active_branch);
     354        27650 :     else if (pg_strcasecmp(cmd, "copy") == 0)
     355           87 :         status = exec_command_copy(scan_state, active_branch);
     356        27563 :     else if (strcmp(cmd, "copyright") == 0)
     357            4 :         status = exec_command_copyright(scan_state, active_branch);
     358        27559 :     else if (strcmp(cmd, "crosstabview") == 0)
     359           69 :         status = exec_command_crosstabview(scan_state, active_branch);
     360        27490 :     else if (cmd[0] == 'd')
     361         3471 :         status = exec_command_d(scan_state, active_branch, cmd);
     362        24019 :     else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0)
     363            3 :         status = exec_command_edit(scan_state, active_branch,
     364              :                                    query_buf, previous_buf);
     365        24016 :     else if (strcmp(cmd, "ef") == 0)
     366            3 :         status = exec_command_ef_ev(scan_state, active_branch, query_buf, true);
     367        24013 :     else if (strcmp(cmd, "ev") == 0)
     368            3 :         status = exec_command_ef_ev(scan_state, active_branch, query_buf, false);
     369        24010 :     else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0 ||
     370        13825 :              strcmp(cmd, "warn") == 0)
     371        19902 :         status = exec_command_echo(scan_state, active_branch, cmd);
     372         4108 :     else if (strcmp(cmd, "elif") == 0)
     373           24 :         status = exec_command_elif(scan_state, cstack, query_buf);
     374         4084 :     else if (strcmp(cmd, "else") == 0)
     375           66 :         status = exec_command_else(scan_state, cstack, query_buf);
     376         4018 :     else if (strcmp(cmd, "endif") == 0)
     377          103 :         status = exec_command_endif(scan_state, cstack, query_buf);
     378         3915 :     else if (strcmp(cmd, "endpipeline") == 0)
     379          149 :         status = exec_command_endpipeline(scan_state, active_branch);
     380         3766 :     else if (strcmp(cmd, "encoding") == 0)
     381           11 :         status = exec_command_encoding(scan_state, active_branch);
     382         3755 :     else if (strcmp(cmd, "errverbose") == 0)
     383            7 :         status = exec_command_errverbose(scan_state, active_branch);
     384         3748 :     else if (strcmp(cmd, "f") == 0)
     385            3 :         status = exec_command_f(scan_state, active_branch);
     386         3745 :     else if (strcmp(cmd, "flush") == 0)
     387           12 :         status = exec_command_flush(scan_state, active_branch);
     388         3733 :     else if (strcmp(cmd, "flushrequest") == 0)
     389           30 :         status = exec_command_flushrequest(scan_state, active_branch);
     390         3703 :     else if (strcmp(cmd, "g") == 0 || strcmp(cmd, "gx") == 0)
     391          189 :         status = exec_command_g(scan_state, active_branch, cmd);
     392         3514 :     else if (strcmp(cmd, "gdesc") == 0)
     393           43 :         status = exec_command_gdesc(scan_state, active_branch);
     394         3471 :     else if (strcmp(cmd, "getenv") == 0)
     395          161 :         status = exec_command_getenv(scan_state, active_branch, cmd);
     396         3310 :     else if (strcmp(cmd, "getresults") == 0)
     397           78 :         status = exec_command_getresults(scan_state, active_branch);
     398         3232 :     else if (strcmp(cmd, "gexec") == 0)
     399           29 :         status = exec_command_gexec(scan_state, active_branch);
     400         3203 :     else if (strcmp(cmd, "gset") == 0)
     401          416 :         status = exec_command_gset(scan_state, active_branch);
     402         2787 :     else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
     403            5 :         status = exec_command_help(scan_state, active_branch);
     404         2782 :     else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
     405            3 :         status = exec_command_html(scan_state, active_branch);
     406         2779 :     else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0 ||
     407         2776 :              strcmp(cmd, "ir") == 0 || strcmp(cmd, "include_relative") == 0)
     408            6 :         status = exec_command_include(scan_state, active_branch, cmd);
     409         2773 :     else if (strcmp(cmd, "if") == 0)
     410          110 :         status = exec_command_if(scan_state, cstack, query_buf);
     411         2663 :     else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0 ||
     412         2660 :              strcmp(cmd, "lx") == 0 || strcmp(cmd, "listx") == 0 ||
     413         2660 :              strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0 ||
     414         2660 :              strcmp(cmd, "lx+") == 0 || strcmp(cmd, "listx+") == 0 ||
     415         2660 :              strcmp(cmd, "l+x") == 0 || strcmp(cmd, "list+x") == 0)
     416            3 :         status = exec_command_list(scan_state, active_branch, cmd);
     417         2660 :     else if (strncmp(cmd, "lo_", 3) == 0)
     418           31 :         status = exec_command_lo(scan_state, active_branch, cmd);
     419         2629 :     else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
     420           21 :         status = exec_command_out(scan_state, active_branch);
     421         2608 :     else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0)
     422           21 :         status = exec_command_print(scan_state, active_branch,
     423              :                                     query_buf, previous_buf);
     424         2587 :     else if (strcmp(cmd, "parse") == 0)
     425           57 :         status = exec_command_parse(scan_state, active_branch, cmd);
     426         2530 :     else if (strcmp(cmd, "password") == 0)
     427            4 :         status = exec_command_password(scan_state, active_branch);
     428         2526 :     else if (strcmp(cmd, "prompt") == 0)
     429            3 :         status = exec_command_prompt(scan_state, active_branch, cmd);
     430         2523 :     else if (strcmp(cmd, "pset") == 0)
     431          899 :         status = exec_command_pset(scan_state, active_branch);
     432         1624 :     else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0)
     433          221 :         status = exec_command_quit(scan_state, active_branch);
     434         1403 :     else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0)
     435           48 :         status = exec_command_reset(scan_state, active_branch, query_buf);
     436         1355 :     else if (strcmp(cmd, "restrict") == 0)
     437           35 :         status = exec_command_restrict(scan_state, active_branch, cmd);
     438         1320 :     else if (strcmp(cmd, "s") == 0)
     439            3 :         status = exec_command_s(scan_state, active_branch);
     440         1317 :     else if (strcmp(cmd, "sendpipeline") == 0)
     441          295 :         status = exec_command_sendpipeline(scan_state, active_branch);
     442         1022 :     else if (strcmp(cmd, "set") == 0)
     443          511 :         status = exec_command_set(scan_state, active_branch);
     444          511 :     else if (strcmp(cmd, "setenv") == 0)
     445            9 :         status = exec_command_setenv(scan_state, active_branch, cmd);
     446          502 :     else if (strcmp(cmd, "sf") == 0 || strcmp(cmd, "sf+") == 0)
     447           33 :         status = exec_command_sf_sv(scan_state, active_branch, cmd, true);
     448          469 :     else if (strcmp(cmd, "sv") == 0 || strcmp(cmd, "sv+") == 0)
     449           66 :         status = exec_command_sf_sv(scan_state, active_branch, cmd, false);
     450          403 :     else if (strcmp(cmd, "startpipeline") == 0)
     451          149 :         status = exec_command_startpipeline(scan_state, active_branch);
     452          254 :     else if (strcmp(cmd, "syncpipeline") == 0)
     453           57 :         status = exec_command_syncpipeline(scan_state, active_branch);
     454          197 :     else if (strcmp(cmd, "t") == 0)
     455           35 :         status = exec_command_t(scan_state, active_branch);
     456          162 :     else if (strcmp(cmd, "T") == 0)
     457            3 :         status = exec_command_T(scan_state, active_branch);
     458          159 :     else if (strcmp(cmd, "timing") == 0)
     459            5 :         status = exec_command_timing(scan_state, active_branch);
     460          154 :     else if (strcmp(cmd, "unrestrict") == 0)
     461           34 :         status = exec_command_unrestrict(scan_state, active_branch, cmd);
     462          120 :     else if (strcmp(cmd, "unset") == 0)
     463           26 :         status = exec_command_unset(scan_state, active_branch, cmd);
     464           94 :     else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0)
     465            6 :         status = exec_command_write(scan_state, active_branch, cmd,
     466              :                                     query_buf, previous_buf);
     467           88 :     else if (strcmp(cmd, "watch") == 0)
     468           20 :         status = exec_command_watch(scan_state, active_branch,
     469              :                                     query_buf, previous_buf);
     470           68 :     else if (strcmp(cmd, "x") == 0)
     471           38 :         status = exec_command_x(scan_state, active_branch);
     472           30 :     else if (strcmp(cmd, "z") == 0 ||
     473           18 :              strcmp(cmd, "zS") == 0 || strcmp(cmd, "zx") == 0 ||
     474           15 :              strcmp(cmd, "zSx") == 0 || strcmp(cmd, "zxS") == 0)
     475           15 :         status = exec_command_z(scan_state, active_branch, cmd);
     476           15 :     else if (strcmp(cmd, "!") == 0)
     477            3 :         status = exec_command_shell_escape(scan_state, active_branch);
     478           12 :     else if (strcmp(cmd, "?") == 0)
     479            3 :         status = exec_command_slash_command_help(scan_state, active_branch);
     480              :     else
     481            9 :         status = PSQL_CMD_UNKNOWN;
     482              : 
     483              :     /*
     484              :      * All the commands that return PSQL_CMD_SEND want to execute previous_buf
     485              :      * if query_buf is empty.  For convenience we implement that here, not in
     486              :      * the individual command subroutines.
     487              :      */
     488        28317 :     if (status == PSQL_CMD_SEND)
     489         1502 :         (void) copy_previous_query(query_buf, previous_buf);
     490              : 
     491        28317 :     return status;
     492              : }
     493              : 
     494              : 
     495              : /*
     496              :  * \a -- toggle field alignment
     497              :  *
     498              :  * This makes little sense but we keep it around.
     499              :  */
     500              : static backslashResult
     501           27 : exec_command_a(PsqlScanState scan_state, bool active_branch)
     502              : {
     503           27 :     bool        success = true;
     504              : 
     505           27 :     if (active_branch)
     506              :     {
     507           24 :         if (pset.popt.topt.format != PRINT_ALIGNED)
     508           12 :             success = do_pset("format", "aligned", &pset.popt, pset.quiet);
     509              :         else
     510           12 :             success = do_pset("format", "unaligned", &pset.popt, pset.quiet);
     511              :     }
     512              : 
     513           27 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
     514              : }
     515              : 
     516              : /*
     517              :  * \bind -- set query parameters
     518              :  */
     519              : static backslashResult
     520          336 : exec_command_bind(PsqlScanState scan_state, bool active_branch)
     521              : {
     522          336 :     backslashResult status = PSQL_CMD_SKIP_LINE;
     523              : 
     524          336 :     if (active_branch)
     525              :     {
     526              :         char       *opt;
     527          333 :         int         nparams = 0;
     528          333 :         int         nalloc = 0;
     529              : 
     530          333 :         clean_extended_state();
     531              : 
     532          607 :         while ((opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false)))
     533              :         {
     534          274 :             nparams++;
     535          274 :             if (nparams > nalloc)
     536              :             {
     537          269 :                 nalloc = nalloc ? nalloc * 2 : 1;
     538          269 :                 pset.bind_params = pg_realloc_array(pset.bind_params, char *, nalloc);
     539              :             }
     540          274 :             pset.bind_params[nparams - 1] = opt;
     541              :         }
     542              : 
     543          333 :         pset.bind_nparams = nparams;
     544          333 :         pset.send_mode = PSQL_SEND_EXTENDED_QUERY_PARAMS;
     545              :     }
     546              :     else
     547            3 :         ignore_slash_options(scan_state);
     548              : 
     549          336 :     return status;
     550              : }
     551              : 
     552              : /*
     553              :  * \bind_named -- set query parameters for an existing prepared statement
     554              :  */
     555              : static backslashResult
     556           88 : exec_command_bind_named(PsqlScanState scan_state, bool active_branch,
     557              :                         const char *cmd)
     558              : {
     559           88 :     backslashResult status = PSQL_CMD_SKIP_LINE;
     560              : 
     561           88 :     if (active_branch)
     562              :     {
     563              :         char       *opt;
     564           85 :         int         nparams = 0;
     565           85 :         int         nalloc = 0;
     566              : 
     567           85 :         clean_extended_state();
     568              : 
     569              :         /* get the mandatory prepared statement name */
     570           85 :         opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
     571           85 :         if (!opt)
     572              :         {
     573            6 :             pg_log_error("\\%s: missing required argument", cmd);
     574            6 :             status = PSQL_CMD_ERROR;
     575              :         }
     576              :         else
     577              :         {
     578           79 :             pset.stmtName = opt;
     579           79 :             pset.send_mode = PSQL_SEND_EXTENDED_QUERY_PREPARED;
     580              : 
     581              :             /* set of parameters */
     582          167 :             while ((opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false)))
     583              :             {
     584           88 :                 nparams++;
     585           88 :                 if (nparams > nalloc)
     586              :                 {
     587           88 :                     nalloc = nalloc ? nalloc * 2 : 1;
     588           88 :                     pset.bind_params = pg_realloc_array(pset.bind_params, char *, nalloc);
     589              :                 }
     590           88 :                 pset.bind_params[nparams - 1] = opt;
     591              :             }
     592           79 :             pset.bind_nparams = nparams;
     593              :         }
     594              :     }
     595              :     else
     596            3 :         ignore_slash_options(scan_state);
     597              : 
     598           88 :     return status;
     599              : }
     600              : 
     601              : /*
     602              :  * \C -- override table title (formerly change HTML caption)
     603              :  */
     604              : static backslashResult
     605            3 : exec_command_C(PsqlScanState scan_state, bool active_branch)
     606              : {
     607            3 :     bool        success = true;
     608              : 
     609            3 :     if (active_branch)
     610              :     {
     611            0 :         char       *opt = psql_scan_slash_option(scan_state,
     612              :                                                  OT_NORMAL, NULL, true);
     613              : 
     614            0 :         success = do_pset("title", opt, &pset.popt, pset.quiet);
     615            0 :         free(opt);
     616              :     }
     617              :     else
     618            3 :         ignore_slash_options(scan_state);
     619              : 
     620            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
     621              : }
     622              : 
     623              : /*
     624              :  * \c or \connect -- connect to database using the specified parameters.
     625              :  *
     626              :  * \c [-reuse-previous=BOOL] dbname user host port
     627              :  *
     628              :  * Specifying a parameter as '-' is equivalent to omitting it.  Examples:
     629              :  *
     630              :  * \c - - hst       Connect to current database on current port of
     631              :  *                  host "hst" as current user.
     632              :  * \c - usr - prt   Connect to current database on port "prt" of current host
     633              :  *                  as user "usr".
     634              :  * \c dbs           Connect to database "dbs" on current port of current host
     635              :  *                  as current user.
     636              :  */
     637              : static backslashResult
     638          184 : exec_command_connect(PsqlScanState scan_state, bool active_branch)
     639              : {
     640          184 :     bool        success = true;
     641              : 
     642          184 :     if (active_branch)
     643              :     {
     644              :         static const char prefix[] = "-reuse-previous=";
     645              :         char       *opt1,
     646              :                    *opt2,
     647              :                    *opt3,
     648              :                    *opt4;
     649          181 :         enum trivalue reuse_previous = TRI_DEFAULT;
     650              : 
     651          181 :         opt1 = read_connect_arg(scan_state);
     652          181 :         if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0)
     653              :         {
     654              :             bool        on_off;
     655              : 
     656           11 :             success = ParseVariableBool(opt1 + sizeof(prefix) - 1,
     657              :                                         "-reuse-previous",
     658              :                                         &on_off);
     659           11 :             if (success)
     660              :             {
     661           11 :                 reuse_previous = on_off ? TRI_YES : TRI_NO;
     662           11 :                 free(opt1);
     663           11 :                 opt1 = read_connect_arg(scan_state);
     664              :             }
     665              :         }
     666              : 
     667          181 :         if (success)            /* give up if reuse_previous was invalid */
     668              :         {
     669          181 :             opt2 = read_connect_arg(scan_state);
     670          181 :             opt3 = read_connect_arg(scan_state);
     671          181 :             opt4 = read_connect_arg(scan_state);
     672              : 
     673          181 :             success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
     674              : 
     675          181 :             free(opt2);
     676          181 :             free(opt3);
     677          181 :             free(opt4);
     678              :         }
     679          181 :         free(opt1);
     680              :     }
     681              :     else
     682            3 :         ignore_slash_options(scan_state);
     683              : 
     684          184 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
     685              : }
     686              : 
     687              : /*
     688              :  * \cd -- change directory
     689              :  */
     690              : static backslashResult
     691            3 : exec_command_cd(PsqlScanState scan_state, bool active_branch, const char *cmd)
     692              : {
     693            3 :     bool        success = true;
     694              : 
     695            3 :     if (active_branch)
     696              :     {
     697            0 :         char       *opt = psql_scan_slash_option(scan_state,
     698              :                                                  OT_NORMAL, NULL, true);
     699              :         char       *dir;
     700              : 
     701            0 :         if (opt)
     702            0 :             dir = opt;
     703              :         else
     704              :         {
     705              : #ifndef WIN32
     706              :             /* This should match get_home_path() */
     707            0 :             dir = getenv("HOME");
     708            0 :             if (dir == NULL || dir[0] == '\0')
     709              :             {
     710            0 :                 uid_t       user_id = geteuid();
     711              :                 struct passwd *pw;
     712              : 
     713            0 :                 errno = 0;      /* clear errno before call */
     714            0 :                 pw = getpwuid(user_id);
     715            0 :                 if (pw)
     716            0 :                     dir = pw->pw_dir;
     717              :                 else
     718              :                 {
     719            0 :                     pg_log_error("could not get home directory for user ID %ld: %s",
     720              :                                  (long) user_id,
     721              :                                  errno ? strerror(errno) : _("user does not exist"));
     722            0 :                     success = false;
     723              :                 }
     724              :             }
     725              : #else                           /* WIN32 */
     726              : 
     727              :             /*
     728              :              * On Windows, 'cd' without arguments prints the current
     729              :              * directory, so if someone wants to code this here instead...
     730              :              */
     731              :             dir = "/";
     732              : #endif                          /* WIN32 */
     733              :         }
     734              : 
     735            0 :         if (success &&
     736            0 :             chdir(dir) < 0)
     737              :         {
     738            0 :             pg_log_error("\\%s: could not change directory to \"%s\": %m",
     739              :                          cmd, dir);
     740            0 :             success = false;
     741              :         }
     742              : 
     743            0 :         free(opt);
     744              :     }
     745              :     else
     746            3 :         ignore_slash_options(scan_state);
     747              : 
     748            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
     749              : }
     750              : 
     751              : /*
     752              :  * \close_prepared -- close a previously prepared statement
     753              :  */
     754              : static backslashResult
     755           25 : exec_command_close_prepared(PsqlScanState scan_state, bool active_branch, const char *cmd)
     756              : {
     757           25 :     backslashResult status = PSQL_CMD_SKIP_LINE;
     758              : 
     759           25 :     if (active_branch)
     760              :     {
     761           22 :         char       *opt = psql_scan_slash_option(scan_state,
     762              :                                                  OT_NORMAL, NULL, false);
     763              : 
     764           22 :         clean_extended_state();
     765              : 
     766           22 :         if (!opt)
     767              :         {
     768            3 :             pg_log_error("\\%s: missing required argument", cmd);
     769            3 :             status = PSQL_CMD_ERROR;
     770              :         }
     771              :         else
     772              :         {
     773           19 :             pset.stmtName = opt;
     774           19 :             pset.send_mode = PSQL_SEND_EXTENDED_CLOSE;
     775           19 :             status = PSQL_CMD_SEND;
     776              :         }
     777              :     }
     778              :     else
     779            3 :         ignore_slash_options(scan_state);
     780              : 
     781           25 :     return status;
     782              : }
     783              : 
     784              : /*
     785              :  * \conninfo -- display information about the current connection
     786              :  */
     787              : static backslashResult
     788            3 : exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
     789              : {
     790              :     printTableContent cont;
     791              :     int         rows,
     792              :                 cols;
     793              :     char       *db;
     794              :     char       *host;
     795              :     bool        print_hostaddr;
     796              :     char       *hostaddr;
     797              :     char       *protocol_version,
     798              :                *backend_pid;
     799              :     int         ssl_in_use,
     800              :                 password_used,
     801              :                 gssapi_used;
     802              :     int         version_num;
     803              :     char       *paramval;
     804              : 
     805            3 :     if (!active_branch)
     806            3 :         return PSQL_CMD_SKIP_LINE;
     807              : 
     808            0 :     db = PQdb(pset.db);
     809            0 :     if (db == NULL)
     810              :     {
     811            0 :         printf(_("You are currently not connected to a database.\n"));
     812            0 :         return PSQL_CMD_SKIP_LINE;
     813              :     }
     814              : 
     815              :     /* Get values for the parameters */
     816            0 :     host = PQhost(pset.db);
     817            0 :     hostaddr = PQhostaddr(pset.db);
     818            0 :     version_num = PQfullProtocolVersion(pset.db);
     819            0 :     protocol_version = psprintf("%d.%d", version_num / 10000,
     820              :                                 version_num % 10000);
     821            0 :     ssl_in_use = PQsslInUse(pset.db);
     822            0 :     password_used = PQconnectionUsedPassword(pset.db);
     823            0 :     gssapi_used = PQconnectionUsedGSSAPI(pset.db);
     824            0 :     backend_pid = psprintf("%d", PQbackendPID(pset.db));
     825              : 
     826              :     /* Only print hostaddr if it differs from host, and not if unixsock */
     827            0 :     print_hostaddr = (!is_unixsock_path(host) &&
     828            0 :                       hostaddr && *hostaddr && strcmp(host, hostaddr) != 0);
     829              : 
     830              :     /* Determine the exact number of rows to print */
     831            0 :     rows = 12;
     832            0 :     cols = 2;
     833            0 :     if (ssl_in_use)
     834            0 :         rows += 6;
     835            0 :     if (print_hostaddr)
     836            0 :         rows++;
     837              : 
     838              :     /* Set it all up */
     839            0 :     printTableInit(&cont, &pset.popt.topt, _("Connection Information"), cols, rows);
     840            0 :     printTableAddHeader(&cont, _("Parameter"), true, 'l');
     841            0 :     printTableAddHeader(&cont, _("Value"), true, 'l');
     842              : 
     843              :     /* Database */
     844            0 :     printTableAddCell(&cont, _("Database"), false, false);
     845            0 :     printTableAddCell(&cont, db, false, false);
     846              : 
     847              :     /* Client User */
     848            0 :     printTableAddCell(&cont, _("Client User"), false, false);
     849            0 :     printTableAddCell(&cont, PQuser(pset.db), false, false);
     850              : 
     851              :     /* Host/hostaddr/socket */
     852            0 :     if (is_unixsock_path(host))
     853              :     {
     854              :         /* hostaddr if specified overrides socket, so suppress the latter */
     855            0 :         if (hostaddr && *hostaddr)
     856              :         {
     857            0 :             printTableAddCell(&cont, _("Host Address"), false, false);
     858            0 :             printTableAddCell(&cont, hostaddr, false, false);
     859              :         }
     860              :         else
     861              :         {
     862            0 :             printTableAddCell(&cont, _("Socket Directory"), false, false);
     863            0 :             printTableAddCell(&cont, host, false, false);
     864              :         }
     865              :     }
     866              :     else
     867              :     {
     868            0 :         printTableAddCell(&cont, _("Host"), false, false);
     869            0 :         printTableAddCell(&cont, host, false, false);
     870            0 :         if (print_hostaddr)
     871              :         {
     872            0 :             printTableAddCell(&cont, _("Host Address"), false, false);
     873            0 :             printTableAddCell(&cont, hostaddr, false, false);
     874              :         }
     875              :     }
     876              : 
     877              :     /* Server Port */
     878            0 :     printTableAddCell(&cont, _("Server Port"), false, false);
     879            0 :     printTableAddCell(&cont, PQport(pset.db), false, false);
     880              : 
     881              :     /* Options */
     882            0 :     printTableAddCell(&cont, _("Options"), false, false);
     883            0 :     printTableAddCell(&cont, PQoptions(pset.db), false, false);
     884              : 
     885              :     /* Protocol Version */
     886            0 :     printTableAddCell(&cont, _("Protocol Version"), false, false);
     887            0 :     printTableAddCell(&cont, protocol_version, false, false);
     888              : 
     889              :     /* Password Used */
     890            0 :     printTableAddCell(&cont, _("Password Used"), false, false);
     891            0 :     printTableAddCell(&cont, password_used ? _("true") : _("false"), false, false);
     892              : 
     893              :     /* GSSAPI Authenticated */
     894            0 :     printTableAddCell(&cont, _("GSSAPI Authenticated"), false, false);
     895            0 :     printTableAddCell(&cont, gssapi_used ? _("true") : _("false"), false, false);
     896              : 
     897              :     /* Backend PID */
     898            0 :     printTableAddCell(&cont, _("Backend PID"), false, false);
     899            0 :     printTableAddCell(&cont, backend_pid, false, false);
     900              : 
     901              :     /* SSL Connection */
     902            0 :     printTableAddCell(&cont, _("SSL Connection"), false, false);
     903            0 :     printTableAddCell(&cont, ssl_in_use ? _("true") : _("false"), false, false);
     904              : 
     905              :     /* SSL Information */
     906            0 :     if (ssl_in_use)
     907              :     {
     908              :         char       *library,
     909              :                    *protocol,
     910              :                    *key_bits,
     911              :                    *cipher,
     912              :                    *compression,
     913              :                    *alpn;
     914              : 
     915            0 :         library = (char *) PQsslAttribute(pset.db, "library");
     916            0 :         protocol = (char *) PQsslAttribute(pset.db, "protocol");
     917            0 :         key_bits = (char *) PQsslAttribute(pset.db, "key_bits");
     918            0 :         cipher = (char *) PQsslAttribute(pset.db, "cipher");
     919            0 :         compression = (char *) PQsslAttribute(pset.db, "compression");
     920            0 :         alpn = (char *) PQsslAttribute(pset.db, "alpn");
     921              : 
     922            0 :         printTableAddCell(&cont, _("SSL Library"), false, false);
     923            0 :         printTableAddCell(&cont, library ? library : _("unknown"), false, false);
     924              : 
     925            0 :         printTableAddCell(&cont, _("SSL Protocol"), false, false);
     926            0 :         printTableAddCell(&cont, protocol ? protocol : _("unknown"), false, false);
     927              : 
     928            0 :         printTableAddCell(&cont, _("SSL Key Bits"), false, false);
     929            0 :         printTableAddCell(&cont, key_bits ? key_bits : _("unknown"), false, false);
     930              : 
     931            0 :         printTableAddCell(&cont, _("SSL Cipher"), false, false);
     932            0 :         printTableAddCell(&cont, cipher ? cipher : _("unknown"), false, false);
     933              : 
     934            0 :         printTableAddCell(&cont, _("SSL Compression"), false, false);
     935            0 :         printTableAddCell(&cont, (compression && strcmp(compression, "off") != 0) ?
     936            0 :                           _("true") : _("false"), false, false);
     937              : 
     938            0 :         printTableAddCell(&cont, _("ALPN"), false, false);
     939            0 :         printTableAddCell(&cont, (alpn && alpn[0] != '\0') ? alpn : _("none"), false, false);
     940              :     }
     941              : 
     942            0 :     paramval = (char *) PQparameterStatus(pset.db, "is_superuser");
     943            0 :     printTableAddCell(&cont, "Superuser", false, false);
     944            0 :     printTableAddCell(&cont, paramval ? paramval : _("unknown"), false, false);
     945              : 
     946            0 :     paramval = (char *) PQparameterStatus(pset.db, "in_hot_standby");
     947            0 :     printTableAddCell(&cont, "Hot Standby", false, false);
     948            0 :     printTableAddCell(&cont, paramval ? paramval : _("unknown"), false, false);
     949              : 
     950            0 :     printTable(&cont, pset.queryFout, false, pset.logfile);
     951            0 :     printTableCleanup(&cont);
     952              : 
     953            0 :     pfree(protocol_version);
     954            0 :     pfree(backend_pid);
     955              : 
     956            0 :     return PSQL_CMD_SKIP_LINE;
     957              : }
     958              : 
     959              : /*
     960              :  * \copy -- run a COPY command
     961              :  */
     962              : static backslashResult
     963           87 : exec_command_copy(PsqlScanState scan_state, bool active_branch)
     964              : {
     965           87 :     bool        success = true;
     966              : 
     967           87 :     if (active_branch)
     968              :     {
     969           84 :         char       *opt = psql_scan_slash_option(scan_state,
     970              :                                                  OT_WHOLE_LINE, NULL, false);
     971              : 
     972           84 :         success = do_copy(opt);
     973           84 :         free(opt);
     974              :     }
     975              :     else
     976            3 :         ignore_slash_whole_line(scan_state);
     977              : 
     978           87 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
     979              : }
     980              : 
     981              : /*
     982              :  * \copyright -- print copyright notice
     983              :  */
     984              : static backslashResult
     985            4 : exec_command_copyright(PsqlScanState scan_state, bool active_branch)
     986              : {
     987            4 :     if (active_branch)
     988            1 :         print_copyright();
     989              : 
     990            4 :     return PSQL_CMD_SKIP_LINE;
     991              : }
     992              : 
     993              : /*
     994              :  * \crosstabview -- execute a query and display result in crosstab
     995              :  */
     996              : static backslashResult
     997           69 : exec_command_crosstabview(PsqlScanState scan_state, bool active_branch)
     998              : {
     999           69 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1000              : 
    1001           69 :     if (active_branch)
    1002              :     {
    1003              :         int         i;
    1004              : 
    1005          330 :         for (i = 0; i < lengthof(pset.ctv_args); i++)
    1006          264 :             pset.ctv_args[i] = psql_scan_slash_option(scan_state,
    1007              :                                                       OT_NORMAL, NULL, true);
    1008           66 :         pset.crosstab_flag = true;
    1009           66 :         status = PSQL_CMD_SEND;
    1010              :     }
    1011              :     else
    1012            3 :         ignore_slash_options(scan_state);
    1013              : 
    1014           69 :     return status;
    1015              : }
    1016              : 
    1017              : /*
    1018              :  * \d* commands
    1019              :  */
    1020              : static backslashResult
    1021         3471 : exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
    1022              : {
    1023         3471 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1024         3471 :     bool        success = true;
    1025              : 
    1026         3471 :     if (active_branch)
    1027              :     {
    1028              :         char       *pattern;
    1029              :         bool        show_verbose,
    1030              :                     show_system;
    1031              :         unsigned short int save_expanded;
    1032              : 
    1033              :         /* We don't do SQLID reduction on the pattern yet */
    1034         3468 :         pattern = psql_scan_slash_option(scan_state,
    1035              :                                          OT_NORMAL, NULL, true);
    1036              : 
    1037         3468 :         show_verbose = strchr(cmd, '+') ? true : false;
    1038         3468 :         show_system = strchr(cmd, 'S') ? true : false;
    1039              : 
    1040              :         /*
    1041              :          * The 'x' option turns expanded mode on for this command only. This
    1042              :          * is allowed in all \d* commands, except \d by itself, since \dx is a
    1043              :          * separate command. So the 'x' option cannot appear immediately after
    1044              :          * \d, but it can appear after \d followed by other options.
    1045              :          */
    1046         3468 :         save_expanded = pset.popt.topt.expanded;
    1047         3468 :         if (cmd[1] != '\0' && strchr(&cmd[2], 'x'))
    1048           12 :             pset.popt.topt.expanded = 1;
    1049              : 
    1050         3468 :         switch (cmd[1])
    1051              :         {
    1052         2046 :             case '\0':
    1053              :             case '+':
    1054              :             case 'S':
    1055         2046 :                 if (pattern)
    1056         2037 :                     success = describeTableDetails(pattern, show_verbose, show_system);
    1057              :                 else
    1058              :                     /* standard listing of interesting things */
    1059            9 :                     success = listTables("tvmsE", NULL, show_verbose, show_system);
    1060         2046 :                 break;
    1061          111 :             case 'A':
    1062              :                 {
    1063          111 :                     char       *pattern2 = NULL;
    1064              : 
    1065          111 :                     if (pattern && cmd[2] != '\0' && cmd[2] != '+' && cmd[2] != 'x')
    1066           72 :                         pattern2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
    1067              : 
    1068          111 :                     switch (cmd[2])
    1069              :                     {
    1070           39 :                         case '\0':
    1071              :                         case '+':
    1072              :                         case 'x':
    1073           39 :                             success = describeAccessMethods(pattern, show_verbose);
    1074           39 :                             break;
    1075           15 :                         case 'c':
    1076           15 :                             success = listOperatorClasses(pattern, pattern2, show_verbose);
    1077           15 :                             break;
    1078           18 :                         case 'f':
    1079           18 :                             success = listOperatorFamilies(pattern, pattern2, show_verbose);
    1080           18 :                             break;
    1081           18 :                         case 'o':
    1082           18 :                             success = listOpFamilyOperators(pattern, pattern2, show_verbose);
    1083           18 :                             break;
    1084           21 :                         case 'p':
    1085           21 :                             success = listOpFamilyFunctions(pattern, pattern2, show_verbose);
    1086           21 :                             break;
    1087            0 :                         default:
    1088            0 :                             status = PSQL_CMD_UNKNOWN;
    1089            0 :                             break;
    1090              :                     }
    1091              : 
    1092          111 :                     free(pattern2);
    1093              :                 }
    1094          111 :                 break;
    1095           24 :             case 'a':
    1096           24 :                 success = describeAggregates(pattern, show_verbose, show_system);
    1097           24 :                 break;
    1098           12 :             case 'b':
    1099           12 :                 success = describeTablespaces(pattern, show_verbose);
    1100           12 :                 break;
    1101           27 :             case 'c':
    1102           27 :                 if (strncmp(cmd, "dconfig", 7) == 0)
    1103            6 :                     success = describeConfigurationParameters(pattern,
    1104              :                                                               show_verbose,
    1105              :                                                               show_system);
    1106              :                 else
    1107           21 :                     success = listConversions(pattern,
    1108              :                                               show_verbose,
    1109              :                                               show_system);
    1110           27 :                 break;
    1111           21 :             case 'C':
    1112           21 :                 success = listCasts(pattern, show_verbose);
    1113           21 :                 break;
    1114           39 :             case 'd':
    1115           39 :                 if (strncmp(cmd, "ddp", 3) == 0)
    1116           18 :                     success = listDefaultACLs(pattern);
    1117              :                 else
    1118           21 :                     success = objectDescription(pattern, show_system);
    1119           39 :                 break;
    1120           30 :             case 'D':
    1121           30 :                 success = listDomains(pattern, show_verbose, show_system);
    1122           30 :                 break;
    1123          149 :             case 'f':           /* function subsystem */
    1124          149 :                 switch (cmd[2])
    1125              :                 {
    1126          149 :                     case '\0':
    1127              :                     case '+':
    1128              :                     case 'S':
    1129              :                     case 'a':
    1130              :                     case 'n':
    1131              :                     case 'p':
    1132              :                     case 't':
    1133              :                     case 'w':
    1134              :                     case 'x':
    1135          149 :                         success = exec_command_dfo(scan_state, cmd, pattern,
    1136              :                                                    show_verbose, show_system);
    1137          149 :                         break;
    1138            0 :                     default:
    1139            0 :                         status = PSQL_CMD_UNKNOWN;
    1140            0 :                         break;
    1141              :                 }
    1142          149 :                 break;
    1143           12 :             case 'g':
    1144              :                 /* no longer distinct from \du */
    1145           12 :                 success = describeRoles(pattern, show_verbose, show_system);
    1146           12 :                 break;
    1147            3 :             case 'l':
    1148            3 :                 success = listLargeObjects(show_verbose);
    1149            3 :                 break;
    1150           15 :             case 'L':
    1151           15 :                 success = listLanguages(pattern, show_verbose, show_system);
    1152           15 :                 break;
    1153           12 :             case 'n':
    1154           12 :                 success = listSchemas(pattern, show_verbose, show_system);
    1155           12 :                 break;
    1156           31 :             case 'o':
    1157           31 :                 success = exec_command_dfo(scan_state, cmd, pattern,
    1158              :                                            show_verbose, show_system);
    1159           31 :                 break;
    1160           21 :             case 'O':
    1161           21 :                 success = listCollations(pattern, show_verbose, show_system);
    1162           21 :                 break;
    1163           36 :             case 'p':
    1164           36 :                 success = permissionsList(pattern, show_system);
    1165           36 :                 break;
    1166           54 :             case 'P':
    1167              :                 {
    1168           54 :                     switch (cmd[2])
    1169              :                     {
    1170           54 :                         case '\0':
    1171              :                         case '+':
    1172              :                         case 't':
    1173              :                         case 'i':
    1174              :                         case 'n':
    1175              :                         case 'x':
    1176           54 :                             success = listPartitionedTables(&cmd[2], pattern, show_verbose);
    1177           54 :                             break;
    1178            0 :                         default:
    1179            0 :                             status = PSQL_CMD_UNKNOWN;
    1180            0 :                             break;
    1181              :                     }
    1182              :                 }
    1183           54 :                 break;
    1184           34 :             case 'T':
    1185           34 :                 success = describeTypes(pattern, show_verbose, show_system);
    1186           34 :                 break;
    1187          162 :             case 't':
    1188              :             case 'v':
    1189              :             case 'm':
    1190              :             case 'i':
    1191              :             case 's':
    1192              :             case 'E':
    1193          162 :                 success = listTables(&cmd[1], pattern, show_verbose, show_system);
    1194          162 :                 break;
    1195           16 :             case 'r':
    1196           16 :                 if (cmd[2] == 'd' && cmd[3] == 's')
    1197           13 :                 {
    1198           13 :                     char       *pattern2 = NULL;
    1199              : 
    1200           13 :                     if (pattern)
    1201           12 :                         pattern2 = psql_scan_slash_option(scan_state,
    1202              :                                                           OT_NORMAL, NULL, true);
    1203           13 :                     success = listDbRoleSettings(pattern, pattern2);
    1204              : 
    1205           13 :                     free(pattern2);
    1206              :                 }
    1207            3 :                 else if (cmd[2] == 'g')
    1208            3 :                     success = describeRoleGrants(pattern, show_system);
    1209              :                 else
    1210            0 :                     status = PSQL_CMD_UNKNOWN;
    1211           16 :                 break;
    1212          279 :             case 'R':
    1213          279 :                 switch (cmd[2])
    1214              :                 {
    1215          195 :                     case 'p':
    1216          195 :                         if (show_verbose)
    1217          171 :                             success = describePublications(pattern);
    1218              :                         else
    1219           24 :                             success = listPublications(pattern);
    1220          195 :                         break;
    1221           84 :                     case 's':
    1222           84 :                         success = describeSubscriptions(pattern, show_verbose);
    1223           84 :                         break;
    1224            0 :                     default:
    1225            0 :                         status = PSQL_CMD_UNKNOWN;
    1226              :                 }
    1227          279 :                 break;
    1228            3 :             case 'u':
    1229            3 :                 success = describeRoles(pattern, show_verbose, show_system);
    1230            3 :                 break;
    1231           84 :             case 'F':           /* text search subsystem */
    1232           84 :                 switch (cmd[2])
    1233              :                 {
    1234           21 :                     case '\0':
    1235              :                     case '+':
    1236              :                     case 'x':
    1237           21 :                         success = listTSConfigs(pattern, show_verbose);
    1238           21 :                         break;
    1239           21 :                     case 'p':
    1240           21 :                         success = listTSParsers(pattern, show_verbose);
    1241           21 :                         break;
    1242           21 :                     case 'd':
    1243           21 :                         success = listTSDictionaries(pattern, show_verbose);
    1244           21 :                         break;
    1245           21 :                     case 't':
    1246           21 :                         success = listTSTemplates(pattern, show_verbose);
    1247           21 :                         break;
    1248            0 :                     default:
    1249            0 :                         status = PSQL_CMD_UNKNOWN;
    1250            0 :                         break;
    1251              :                 }
    1252           84 :                 break;
    1253          156 :             case 'e':           /* SQL/MED subsystem */
    1254          156 :                 switch (cmd[2])
    1255              :                 {
    1256           60 :                     case 's':
    1257           60 :                         success = listForeignServers(pattern, show_verbose);
    1258           60 :                         break;
    1259           30 :                     case 'u':
    1260           30 :                         success = listUserMappings(pattern, show_verbose);
    1261           30 :                         break;
    1262           57 :                     case 'w':
    1263           57 :                         success = listForeignDataWrappers(pattern, show_verbose);
    1264           57 :                         break;
    1265            9 :                     case 't':
    1266            9 :                         success = listForeignTables(pattern, show_verbose);
    1267            9 :                         break;
    1268            0 :                     default:
    1269            0 :                         status = PSQL_CMD_UNKNOWN;
    1270            0 :                         break;
    1271              :                 }
    1272          156 :                 break;
    1273           28 :             case 'x':           /* Extensions */
    1274           28 :                 if (show_verbose)
    1275           16 :                     success = listExtensionContents(pattern);
    1276              :                 else
    1277           12 :                     success = listExtensions(pattern);
    1278           28 :                 break;
    1279           51 :             case 'X':           /* Extended Statistics */
    1280           51 :                 success = listExtendedStats(pattern, show_verbose);
    1281           51 :                 break;
    1282           12 :             case 'y':           /* Event Triggers */
    1283           12 :                 success = listEventTriggers(pattern, show_verbose);
    1284           12 :                 break;
    1285            0 :             default:
    1286            0 :                 status = PSQL_CMD_UNKNOWN;
    1287              :         }
    1288              : 
    1289              :         /* Restore original expanded mode */
    1290         3468 :         pset.popt.topt.expanded = save_expanded;
    1291              : 
    1292         3468 :         free(pattern);
    1293              :     }
    1294              :     else
    1295            3 :         ignore_slash_options(scan_state);
    1296              : 
    1297         3471 :     if (!success)
    1298          466 :         status = PSQL_CMD_ERROR;
    1299              : 
    1300         3471 :     return status;
    1301              : }
    1302              : 
    1303              : /* \df and \do; messy enough to split out of exec_command_d */
    1304              : static bool
    1305          180 : exec_command_dfo(PsqlScanState scan_state, const char *cmd,
    1306              :                  const char *pattern,
    1307              :                  bool show_verbose, bool show_system)
    1308              : {
    1309              :     bool        success;
    1310              :     char       *arg_patterns[FUNC_MAX_ARGS];
    1311          180 :     int         num_arg_patterns = 0;
    1312              : 
    1313              :     /* Collect argument-type patterns too */
    1314          180 :     if (pattern)                /* otherwise it was just \df or \do */
    1315              :     {
    1316              :         char       *ap;
    1317              : 
    1318          220 :         while ((ap = psql_scan_slash_option(scan_state,
    1319          220 :                                             OT_NORMAL, NULL, true)) != NULL)
    1320              :         {
    1321           42 :             arg_patterns[num_arg_patterns++] = ap;
    1322           42 :             if (num_arg_patterns >= FUNC_MAX_ARGS)
    1323            0 :                 break;          /* protect limited-size array */
    1324              :         }
    1325              :     }
    1326              : 
    1327          180 :     if (cmd[1] == 'f')
    1328          149 :         success = describeFunctions(&cmd[2], pattern,
    1329              :                                     arg_patterns, num_arg_patterns,
    1330              :                                     show_verbose, show_system);
    1331              :     else
    1332           31 :         success = describeOperators(pattern,
    1333              :                                     arg_patterns, num_arg_patterns,
    1334              :                                     show_verbose, show_system);
    1335              : 
    1336          222 :     while (--num_arg_patterns >= 0)
    1337           42 :         free(arg_patterns[num_arg_patterns]);
    1338              : 
    1339          180 :     return success;
    1340              : }
    1341              : 
    1342              : /*
    1343              :  * \e or \edit -- edit the current query buffer, or edit a file and
    1344              :  * make it the query buffer
    1345              :  */
    1346              : static backslashResult
    1347            3 : exec_command_edit(PsqlScanState scan_state, bool active_branch,
    1348              :                   PQExpBuffer query_buf, PQExpBuffer previous_buf)
    1349              : {
    1350            3 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1351              : 
    1352            3 :     if (active_branch)
    1353              :     {
    1354            0 :         if (!query_buf)
    1355              :         {
    1356            0 :             pg_log_error("no query buffer");
    1357            0 :             status = PSQL_CMD_ERROR;
    1358              :         }
    1359              :         else
    1360              :         {
    1361              :             char       *fname;
    1362            0 :             char       *ln = NULL;
    1363            0 :             int         lineno = -1;
    1364              : 
    1365            0 :             fname = psql_scan_slash_option(scan_state,
    1366              :                                            OT_NORMAL, NULL, true);
    1367            0 :             if (fname)
    1368              :             {
    1369              :                 /* try to get separate lineno arg */
    1370            0 :                 ln = psql_scan_slash_option(scan_state,
    1371              :                                             OT_NORMAL, NULL, true);
    1372            0 :                 if (ln == NULL)
    1373              :                 {
    1374              :                     /* only one arg; maybe it is lineno not fname */
    1375            0 :                     if (fname[0] &&
    1376            0 :                         strspn(fname, "0123456789") == strlen(fname))
    1377              :                     {
    1378              :                         /* all digits, so assume it is lineno */
    1379            0 :                         ln = fname;
    1380            0 :                         fname = NULL;
    1381              :                     }
    1382              :                 }
    1383              :             }
    1384            0 :             if (ln)
    1385              :             {
    1386            0 :                 lineno = atoi(ln);
    1387            0 :                 if (lineno < 1)
    1388              :                 {
    1389            0 :                     pg_log_error("invalid line number: %s", ln);
    1390            0 :                     status = PSQL_CMD_ERROR;
    1391              :                 }
    1392              :             }
    1393            0 :             if (status != PSQL_CMD_ERROR)
    1394              :             {
    1395              :                 bool        discard_on_quit;
    1396              : 
    1397            0 :                 expand_tilde(&fname);
    1398            0 :                 if (fname)
    1399              :                 {
    1400            0 :                     canonicalize_path_enc(fname, pset.encoding);
    1401              :                     /* Always clear buffer if the file isn't modified */
    1402            0 :                     discard_on_quit = true;
    1403              :                 }
    1404              :                 else
    1405              :                 {
    1406              :                     /*
    1407              :                      * If query_buf is empty, recall previous query for
    1408              :                      * editing.  But in that case, the query buffer should be
    1409              :                      * emptied if editing doesn't modify the file.
    1410              :                      */
    1411            0 :                     discard_on_quit = copy_previous_query(query_buf,
    1412              :                                                           previous_buf);
    1413              :                 }
    1414              : 
    1415            0 :                 if (do_edit(fname, query_buf, lineno, discard_on_quit, NULL))
    1416            0 :                     status = PSQL_CMD_NEWEDIT;
    1417              :                 else
    1418            0 :                     status = PSQL_CMD_ERROR;
    1419              :             }
    1420              : 
    1421              :             /*
    1422              :              * On error while editing or if specifying an incorrect line
    1423              :              * number, reset the query buffer.
    1424              :              */
    1425            0 :             if (status == PSQL_CMD_ERROR)
    1426            0 :                 resetPQExpBuffer(query_buf);
    1427              : 
    1428            0 :             free(fname);
    1429            0 :             free(ln);
    1430              :         }
    1431              :     }
    1432              :     else
    1433            3 :         ignore_slash_options(scan_state);
    1434              : 
    1435            3 :     return status;
    1436              : }
    1437              : 
    1438              : /*
    1439              :  * \ef/\ev -- edit the named function/view, or
    1440              :  * present a blank CREATE FUNCTION/VIEW template if no argument is given
    1441              :  */
    1442              : static backslashResult
    1443            6 : exec_command_ef_ev(PsqlScanState scan_state, bool active_branch,
    1444              :                    PQExpBuffer query_buf, bool is_func)
    1445              : {
    1446            6 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1447              : 
    1448            6 :     if (active_branch)
    1449              :     {
    1450            0 :         char       *obj_desc = psql_scan_slash_option(scan_state,
    1451              :                                                       OT_WHOLE_LINE,
    1452              :                                                       NULL, true);
    1453            0 :         int         lineno = -1;
    1454              : 
    1455            0 :         if (!query_buf)
    1456              :         {
    1457            0 :             pg_log_error("no query buffer");
    1458            0 :             status = PSQL_CMD_ERROR;
    1459              :         }
    1460              :         else
    1461              :         {
    1462            0 :             Oid         obj_oid = InvalidOid;
    1463            0 :             EditableObjectType eot = is_func ? EditableFunction : EditableView;
    1464              : 
    1465            0 :             lineno = strip_lineno_from_objdesc(obj_desc);
    1466            0 :             if (lineno == 0)
    1467              :             {
    1468              :                 /* error already reported */
    1469            0 :                 status = PSQL_CMD_ERROR;
    1470              :             }
    1471            0 :             else if (!obj_desc)
    1472              :             {
    1473              :                 /* set up an empty command to fill in */
    1474            0 :                 resetPQExpBuffer(query_buf);
    1475            0 :                 if (is_func)
    1476            0 :                     appendPQExpBufferStr(query_buf,
    1477              :                                          "CREATE FUNCTION ( )\n"
    1478              :                                          " RETURNS \n"
    1479              :                                          " LANGUAGE \n"
    1480              :                                          " -- common options:  IMMUTABLE  STABLE  STRICT  SECURITY DEFINER\n"
    1481              :                                          "AS $function$\n"
    1482              :                                          "\n$function$\n");
    1483              :                 else
    1484            0 :                     appendPQExpBufferStr(query_buf,
    1485              :                                          "CREATE VIEW  AS\n"
    1486              :                                          " SELECT \n"
    1487              :                                          "  -- something...\n");
    1488              :             }
    1489            0 :             else if (!lookup_object_oid(eot, obj_desc, &obj_oid))
    1490              :             {
    1491              :                 /* error already reported */
    1492            0 :                 status = PSQL_CMD_ERROR;
    1493              :             }
    1494            0 :             else if (!get_create_object_cmd(eot, obj_oid, query_buf))
    1495              :             {
    1496              :                 /* error already reported */
    1497            0 :                 status = PSQL_CMD_ERROR;
    1498              :             }
    1499            0 :             else if (is_func && lineno > 0)
    1500              :             {
    1501              :                 /*
    1502              :                  * lineno "1" should correspond to the first line of the
    1503              :                  * function body.  We expect that pg_get_functiondef() will
    1504              :                  * emit that on a line beginning with "AS ", "BEGIN ", or
    1505              :                  * "RETURN ", and that there can be no such line before the
    1506              :                  * real start of the function body.  Increment lineno by the
    1507              :                  * number of lines before that line, so that it becomes
    1508              :                  * relative to the first line of the function definition.
    1509              :                  */
    1510            0 :                 const char *lines = query_buf->data;
    1511              : 
    1512            0 :                 while (*lines != '\0')
    1513              :                 {
    1514            0 :                     if (strncmp(lines, "AS ", 3) == 0 ||
    1515            0 :                         strncmp(lines, "BEGIN ", 6) == 0 ||
    1516            0 :                         strncmp(lines, "RETURN ", 7) == 0)
    1517              :                         break;
    1518            0 :                     lineno++;
    1519              :                     /* find start of next line */
    1520            0 :                     lines = strchr(lines, '\n');
    1521            0 :                     if (!lines)
    1522            0 :                         break;
    1523            0 :                     lines++;
    1524              :                 }
    1525              :             }
    1526              :         }
    1527              : 
    1528            0 :         if (status != PSQL_CMD_ERROR)
    1529              :         {
    1530            0 :             bool        edited = false;
    1531              : 
    1532            0 :             if (!do_edit(NULL, query_buf, lineno, true, &edited))
    1533            0 :                 status = PSQL_CMD_ERROR;
    1534            0 :             else if (!edited)
    1535            0 :                 puts(_("No changes"));
    1536              :             else
    1537            0 :                 status = PSQL_CMD_NEWEDIT;
    1538              :         }
    1539              : 
    1540              :         /*
    1541              :          * On error while doing object lookup or while editing, or if
    1542              :          * specifying an incorrect line number, reset the query buffer.
    1543              :          */
    1544            0 :         if (status == PSQL_CMD_ERROR)
    1545            0 :             resetPQExpBuffer(query_buf);
    1546              : 
    1547            0 :         free(obj_desc);
    1548              :     }
    1549              :     else
    1550            6 :         ignore_slash_whole_line(scan_state);
    1551              : 
    1552            6 :     return status;
    1553              : }
    1554              : 
    1555              : /*
    1556              :  * \echo, \qecho, and \warn -- echo arguments to stdout, query output, or stderr
    1557              :  */
    1558              : static backslashResult
    1559        19902 : exec_command_echo(PsqlScanState scan_state, bool active_branch, const char *cmd)
    1560              : {
    1561        19902 :     if (active_branch)
    1562              :     {
    1563              :         char       *value;
    1564              :         char        quoted;
    1565        19830 :         bool        no_newline = false;
    1566        19830 :         bool        first = true;
    1567              :         FILE       *fout;
    1568              : 
    1569        19830 :         if (strcmp(cmd, "qecho") == 0)
    1570           15 :             fout = pset.queryFout;
    1571        19815 :         else if (strcmp(cmd, "warn") == 0)
    1572         9717 :             fout = stderr;
    1573              :         else
    1574        10098 :             fout = stdout;
    1575              : 
    1576        39893 :         while ((value = psql_scan_slash_option(scan_state,
    1577              :                                                OT_NORMAL, &quoted, false)))
    1578              :         {
    1579        20063 :             if (first && !no_newline && !quoted && strcmp(value, "-n") == 0)
    1580            3 :                 no_newline = true;
    1581              :             else
    1582              :             {
    1583        20060 :                 if (first)
    1584        19830 :                     first = false;
    1585              :                 else
    1586          230 :                     fputc(' ', fout);
    1587        20060 :                 fputs(value, fout);
    1588              :             }
    1589        20063 :             free(value);
    1590              :         }
    1591        19830 :         if (!no_newline)
    1592        19827 :             fputs("\n", fout);
    1593              :     }
    1594              :     else
    1595           72 :         ignore_slash_options(scan_state);
    1596              : 
    1597        19902 :     return PSQL_CMD_SKIP_LINE;
    1598              : }
    1599              : 
    1600              : /*
    1601              :  * \encoding -- set/show client side encoding
    1602              :  */
    1603              : static backslashResult
    1604           11 : exec_command_encoding(PsqlScanState scan_state, bool active_branch)
    1605              : {
    1606           11 :     if (active_branch)
    1607              :     {
    1608            8 :         char       *encoding = psql_scan_slash_option(scan_state,
    1609              :                                                       OT_NORMAL, NULL, false);
    1610              : 
    1611            8 :         if (!encoding)
    1612              :         {
    1613              :             /* show encoding */
    1614            0 :             puts(pg_encoding_to_char(pset.encoding));
    1615              :         }
    1616              :         else
    1617              :         {
    1618              :             /* set encoding */
    1619            8 :             if (PQsetClientEncoding(pset.db, encoding) == -1)
    1620            0 :                 pg_log_error("%s: invalid encoding name or conversion procedure not found", encoding);
    1621              :             else
    1622              :             {
    1623              :                 /* save encoding info into psql internal data */
    1624            8 :                 pset.encoding = PQclientEncoding(pset.db);
    1625            8 :                 pset.popt.topt.encoding = pset.encoding;
    1626            8 :                 setFmtEncoding(pset.encoding);
    1627            8 :                 SetVariable(pset.vars, "ENCODING",
    1628              :                             pg_encoding_to_char(pset.encoding));
    1629              :             }
    1630            8 :             free(encoding);
    1631              :         }
    1632              :     }
    1633              :     else
    1634            3 :         ignore_slash_options(scan_state);
    1635              : 
    1636           11 :     return PSQL_CMD_SKIP_LINE;
    1637              : }
    1638              : 
    1639              : /*
    1640              :  * \errverbose -- display verbose message from last failed query
    1641              :  */
    1642              : static backslashResult
    1643            7 : exec_command_errverbose(PsqlScanState scan_state, bool active_branch)
    1644              : {
    1645            7 :     if (active_branch)
    1646              :     {
    1647            4 :         if (pset.last_error_result)
    1648              :         {
    1649              :             char       *msg;
    1650              : 
    1651            3 :             msg = PQresultVerboseErrorMessage(pset.last_error_result,
    1652              :                                               PQERRORS_VERBOSE,
    1653              :                                               PQSHOW_CONTEXT_ALWAYS);
    1654            3 :             if (msg)
    1655              :             {
    1656            3 :                 pg_log_error("%s", msg);
    1657            3 :                 PQfreemem(msg);
    1658              :             }
    1659              :             else
    1660            0 :                 puts(_("out of memory"));
    1661              :         }
    1662              :         else
    1663            1 :             puts(_("There is no previous error."));
    1664              :     }
    1665              : 
    1666            7 :     return PSQL_CMD_SKIP_LINE;
    1667              : }
    1668              : 
    1669              : /*
    1670              :  * \f -- change field separator
    1671              :  */
    1672              : static backslashResult
    1673            3 : exec_command_f(PsqlScanState scan_state, bool active_branch)
    1674              : {
    1675            3 :     bool        success = true;
    1676              : 
    1677            3 :     if (active_branch)
    1678              :     {
    1679            0 :         char       *fname = psql_scan_slash_option(scan_state,
    1680              :                                                    OT_NORMAL, NULL, false);
    1681              : 
    1682            0 :         success = do_pset("fieldsep", fname, &pset.popt, pset.quiet);
    1683            0 :         free(fname);
    1684              :     }
    1685              :     else
    1686            3 :         ignore_slash_options(scan_state);
    1687              : 
    1688            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    1689              : }
    1690              : 
    1691              : /*
    1692              :  * \flush -- call PQflush() on the connection
    1693              :  */
    1694              : static backslashResult
    1695           12 : exec_command_flush(PsqlScanState scan_state, bool active_branch)
    1696              : {
    1697           12 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1698              : 
    1699           12 :     if (active_branch)
    1700              :     {
    1701            9 :         pset.send_mode = PSQL_SEND_FLUSH;
    1702            9 :         status = PSQL_CMD_SEND;
    1703              :     }
    1704              :     else
    1705            3 :         ignore_slash_options(scan_state);
    1706              : 
    1707           12 :     return status;
    1708              : }
    1709              : 
    1710              : /*
    1711              :  * \flushrequest -- call PQsendFlushRequest() on the connection
    1712              :  */
    1713              : static backslashResult
    1714           30 : exec_command_flushrequest(PsqlScanState scan_state, bool active_branch)
    1715              : {
    1716           30 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1717              : 
    1718           30 :     if (active_branch)
    1719              :     {
    1720           27 :         pset.send_mode = PSQL_SEND_FLUSH_REQUEST;
    1721           27 :         status = PSQL_CMD_SEND;
    1722              :     }
    1723              :     else
    1724            3 :         ignore_slash_options(scan_state);
    1725              : 
    1726           30 :     return status;
    1727              : }
    1728              : 
    1729              : /*
    1730              :  * \g  [(pset-option[=pset-value] ...)] [filename/shell-command]
    1731              :  * \gx [(pset-option[=pset-value] ...)] [filename/shell-command]
    1732              :  *
    1733              :  * Send the current query.  If pset options are specified, they are made
    1734              :  * active just for this query.  If a filename or pipe command is given,
    1735              :  * the query output goes there.  \gx implicitly sets "expanded=on" along
    1736              :  * with any other pset options that are specified.
    1737              :  */
    1738              : static backslashResult
    1739          189 : exec_command_g(PsqlScanState scan_state, bool active_branch, const char *cmd)
    1740              : {
    1741          189 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1742              :     char       *fname;
    1743              : 
    1744              :     /*
    1745              :      * Because the option processing for this is fairly complicated, we do it
    1746              :      * and then decide whether the branch is active.
    1747              :      */
    1748          189 :     fname = psql_scan_slash_option(scan_state,
    1749              :                                    OT_FILEPIPE, NULL, false);
    1750              : 
    1751          189 :     if (fname && fname[0] == '(')
    1752              :     {
    1753              :         /* Consume pset options through trailing ')' ... */
    1754           12 :         status = process_command_g_options(fname + 1, scan_state,
    1755              :                                            active_branch, cmd);
    1756           12 :         free(fname);
    1757              :         /* ... and again attempt to scan the filename. */
    1758           12 :         fname = psql_scan_slash_option(scan_state,
    1759              :                                        OT_FILEPIPE, NULL, false);
    1760              :     }
    1761              : 
    1762          189 :     if (status == PSQL_CMD_SKIP_LINE && active_branch)
    1763              :     {
    1764          177 :         if (PQpipelineStatus(pset.db) != PQ_PIPELINE_OFF)
    1765              :         {
    1766           18 :             pg_log_error("\\%s not allowed in pipeline mode", cmd);
    1767           18 :             clean_extended_state();
    1768           18 :             free(fname);
    1769           18 :             return PSQL_CMD_ERROR;
    1770              :         }
    1771              : 
    1772          159 :         if (!fname)
    1773          143 :             pset.gfname = NULL;
    1774              :         else
    1775              :         {
    1776           16 :             expand_tilde(&fname);
    1777           16 :             pset.gfname = pg_strdup(fname);
    1778              :         }
    1779          159 :         if (strcmp(cmd, "gx") == 0)
    1780              :         {
    1781              :             /* save settings if not done already, then force expanded=on */
    1782           24 :             if (pset.gsavepopt == NULL)
    1783           21 :                 pset.gsavepopt = savePsetInfo(&pset.popt);
    1784           24 :             pset.popt.topt.expanded = 1;
    1785              :         }
    1786          159 :         status = PSQL_CMD_SEND;
    1787              :     }
    1788              : 
    1789          171 :     free(fname);
    1790              : 
    1791          171 :     return status;
    1792              : }
    1793              : 
    1794              : /*
    1795              :  * Process parenthesized pset options for \g
    1796              :  *
    1797              :  * Note: okay to modify first_option, but not to free it; caller does that
    1798              :  */
    1799              : static backslashResult
    1800           12 : process_command_g_options(char *first_option, PsqlScanState scan_state,
    1801              :                           bool active_branch, const char *cmd)
    1802              : {
    1803           12 :     bool        success = true;
    1804           12 :     bool        found_r_paren = false;
    1805              : 
    1806              :     do
    1807              :     {
    1808              :         char       *option;
    1809              :         size_t      optlen;
    1810              : 
    1811              :         /* If not first time through, collect a new option */
    1812           21 :         if (first_option)
    1813           12 :             option = first_option;
    1814              :         else
    1815              :         {
    1816            9 :             option = psql_scan_slash_option(scan_state,
    1817              :                                             OT_NORMAL, NULL, false);
    1818            9 :             if (!option)
    1819              :             {
    1820            0 :                 if (active_branch)
    1821              :                 {
    1822            0 :                     pg_log_error("\\%s: missing right parenthesis", cmd);
    1823            0 :                     success = false;
    1824              :                 }
    1825            0 :                 break;
    1826              :             }
    1827              :         }
    1828              : 
    1829              :         /* Check for terminating right paren, and remove it from string */
    1830           21 :         optlen = strlen(option);
    1831           21 :         if (optlen > 0 && option[optlen - 1] == ')')
    1832              :         {
    1833           12 :             option[--optlen] = '\0';
    1834           12 :             found_r_paren = true;
    1835              :         }
    1836              : 
    1837              :         /* If there was anything besides parentheses, parse/execute it */
    1838           21 :         if (optlen > 0)
    1839              :         {
    1840              :             /* We can have either "name" or "name=value" */
    1841           21 :             char       *valptr = strchr(option, '=');
    1842              : 
    1843           21 :             if (valptr)
    1844           21 :                 *valptr++ = '\0';
    1845           21 :             if (active_branch)
    1846              :             {
    1847              :                 /* save settings if not done already, then apply option */
    1848           21 :                 if (pset.gsavepopt == NULL)
    1849            9 :                     pset.gsavepopt = savePsetInfo(&pset.popt);
    1850           21 :                 success &= do_pset(option, valptr, &pset.popt, true);
    1851              :             }
    1852              :         }
    1853              : 
    1854              :         /* Clean up after this option.  We should not free first_option. */
    1855           21 :         if (first_option)
    1856           12 :             first_option = NULL;
    1857              :         else
    1858            9 :             free(option);
    1859           21 :     } while (!found_r_paren);
    1860              : 
    1861              :     /* If we failed after already changing some options, undo side-effects */
    1862           12 :     if (!success && active_branch && pset.gsavepopt)
    1863              :     {
    1864            0 :         restorePsetInfo(&pset.popt, pset.gsavepopt);
    1865            0 :         pset.gsavepopt = NULL;
    1866              :     }
    1867              : 
    1868           12 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    1869              : }
    1870              : 
    1871              : /*
    1872              :  * \gdesc -- describe query result
    1873              :  */
    1874              : static backslashResult
    1875           43 : exec_command_gdesc(PsqlScanState scan_state, bool active_branch)
    1876              : {
    1877           43 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1878              : 
    1879           43 :     if (active_branch)
    1880              :     {
    1881           43 :         pset.gdesc_flag = true;
    1882           43 :         status = PSQL_CMD_SEND;
    1883              :     }
    1884              : 
    1885           43 :     return status;
    1886              : }
    1887              : 
    1888              : /*
    1889              :  * \getenv -- set variable from environment variable
    1890              :  */
    1891              : static backslashResult
    1892          161 : exec_command_getenv(PsqlScanState scan_state, bool active_branch,
    1893              :                     const char *cmd)
    1894              : {
    1895          161 :     bool        success = true;
    1896              : 
    1897          161 :     if (active_branch)
    1898              :     {
    1899          161 :         char       *myvar = psql_scan_slash_option(scan_state,
    1900              :                                                    OT_NORMAL, NULL, false);
    1901          161 :         char       *envvar = psql_scan_slash_option(scan_state,
    1902              :                                                     OT_NORMAL, NULL, false);
    1903              : 
    1904          161 :         if (!myvar || !envvar)
    1905              :         {
    1906            0 :             pg_log_error("\\%s: missing required argument", cmd);
    1907            0 :             success = false;
    1908              :         }
    1909              :         else
    1910              :         {
    1911          161 :             char       *envval = getenv(envvar);
    1912              : 
    1913          161 :             if (envval && !SetVariable(pset.vars, myvar, envval))
    1914            0 :                 success = false;
    1915              :         }
    1916          161 :         free(myvar);
    1917          161 :         free(envvar);
    1918              :     }
    1919              :     else
    1920            0 :         ignore_slash_options(scan_state);
    1921              : 
    1922          161 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    1923              : }
    1924              : 
    1925              : /*
    1926              :  * \getresults -- read results
    1927              :  */
    1928              : static backslashResult
    1929           78 : exec_command_getresults(PsqlScanState scan_state, bool active_branch)
    1930              : {
    1931           78 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1932              : 
    1933           78 :     if (active_branch)
    1934              :     {
    1935              :         char       *opt;
    1936              :         int         num_results;
    1937              : 
    1938           75 :         pset.send_mode = PSQL_SEND_GET_RESULTS;
    1939           75 :         status = PSQL_CMD_SEND;
    1940           75 :         opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
    1941              : 
    1942           75 :         pset.requested_results = 0;
    1943           75 :         if (opt != NULL)
    1944              :         {
    1945           36 :             num_results = atoi(opt);
    1946           36 :             if (num_results < 0)
    1947              :             {
    1948            3 :                 pg_log_error("\\getresults: invalid number of requested results");
    1949            3 :                 return PSQL_CMD_ERROR;
    1950              :             }
    1951           33 :             pset.requested_results = num_results;
    1952              :         }
    1953              :     }
    1954              :     else
    1955            3 :         ignore_slash_options(scan_state);
    1956              : 
    1957           75 :     return status;
    1958              : }
    1959              : 
    1960              : 
    1961              : /*
    1962              :  * \gexec -- send query and execute each field of result
    1963              :  */
    1964              : static backslashResult
    1965           29 : exec_command_gexec(PsqlScanState scan_state, bool active_branch)
    1966              : {
    1967           29 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1968              : 
    1969           29 :     if (active_branch)
    1970              :     {
    1971           26 :         if (PQpipelineStatus(pset.db) != PQ_PIPELINE_OFF)
    1972              :         {
    1973            3 :             pg_log_error("\\%s not allowed in pipeline mode", "gexec");
    1974            3 :             clean_extended_state();
    1975            3 :             return PSQL_CMD_ERROR;
    1976              :         }
    1977           23 :         pset.gexec_flag = true;
    1978           23 :         status = PSQL_CMD_SEND;
    1979              :     }
    1980              : 
    1981           26 :     return status;
    1982              : }
    1983              : 
    1984              : /*
    1985              :  * \gset [prefix] -- send query and store result into variables
    1986              :  */
    1987              : static backslashResult
    1988          416 : exec_command_gset(PsqlScanState scan_state, bool active_branch)
    1989              : {
    1990          416 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    1991              : 
    1992          416 :     if (active_branch)
    1993              :     {
    1994          413 :         char       *prefix = psql_scan_slash_option(scan_state,
    1995              :                                                     OT_NORMAL, NULL, false);
    1996              : 
    1997          413 :         if (PQpipelineStatus(pset.db) != PQ_PIPELINE_OFF)
    1998              :         {
    1999            6 :             pg_log_error("\\%s not allowed in pipeline mode", "gset");
    2000            6 :             clean_extended_state();
    2001            6 :             return PSQL_CMD_ERROR;
    2002              :         }
    2003              : 
    2004          407 :         if (prefix)
    2005           54 :             pset.gset_prefix = prefix;
    2006              :         else
    2007              :         {
    2008              :             /* we must set a non-NULL prefix to trigger storing */
    2009          353 :             pset.gset_prefix = pg_strdup("");
    2010              :         }
    2011              :         /* gset_prefix is freed later */
    2012          407 :         status = PSQL_CMD_SEND;
    2013              :     }
    2014              :     else
    2015            3 :         ignore_slash_options(scan_state);
    2016              : 
    2017          410 :     return status;
    2018              : }
    2019              : 
    2020              : /*
    2021              :  * \help [topic] -- print help about SQL commands
    2022              :  */
    2023              : static backslashResult
    2024            5 : exec_command_help(PsqlScanState scan_state, bool active_branch)
    2025              : {
    2026            5 :     if (active_branch)
    2027              :     {
    2028            2 :         char       *opt = psql_scan_slash_option(scan_state,
    2029              :                                                  OT_WHOLE_LINE, NULL, true);
    2030              : 
    2031            2 :         helpSQL(opt, pset.popt.topt.pager);
    2032            2 :         free(opt);
    2033              :     }
    2034              :     else
    2035            3 :         ignore_slash_whole_line(scan_state);
    2036              : 
    2037            5 :     return PSQL_CMD_SKIP_LINE;
    2038              : }
    2039              : 
    2040              : /*
    2041              :  * \H and \html -- toggle HTML formatting
    2042              :  */
    2043              : static backslashResult
    2044            3 : exec_command_html(PsqlScanState scan_state, bool active_branch)
    2045              : {
    2046            3 :     bool        success = true;
    2047              : 
    2048            3 :     if (active_branch)
    2049              :     {
    2050            0 :         if (pset.popt.topt.format != PRINT_HTML)
    2051            0 :             success = do_pset("format", "html", &pset.popt, pset.quiet);
    2052              :         else
    2053            0 :             success = do_pset("format", "aligned", &pset.popt, pset.quiet);
    2054              :     }
    2055              : 
    2056            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2057              : }
    2058              : 
    2059              : /*
    2060              :  * \i and \ir -- include a file
    2061              :  */
    2062              : static backslashResult
    2063            6 : exec_command_include(PsqlScanState scan_state, bool active_branch, const char *cmd)
    2064              : {
    2065            6 :     bool        success = true;
    2066              : 
    2067            6 :     if (active_branch)
    2068              :     {
    2069            0 :         char       *fname = psql_scan_slash_option(scan_state,
    2070              :                                                    OT_NORMAL, NULL, true);
    2071              : 
    2072            0 :         if (!fname)
    2073              :         {
    2074            0 :             pg_log_error("\\%s: missing required argument", cmd);
    2075            0 :             success = false;
    2076              :         }
    2077              :         else
    2078              :         {
    2079              :             bool        include_relative;
    2080              : 
    2081            0 :             include_relative = (strcmp(cmd, "ir") == 0
    2082            0 :                                 || strcmp(cmd, "include_relative") == 0);
    2083            0 :             expand_tilde(&fname);
    2084            0 :             success = (process_file(fname, include_relative) == EXIT_SUCCESS);
    2085            0 :             free(fname);
    2086              :         }
    2087              :     }
    2088              :     else
    2089            6 :         ignore_slash_options(scan_state);
    2090              : 
    2091            6 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2092              : }
    2093              : 
    2094              : /*
    2095              :  * \if <expr> -- beginning of an \if..\endif block
    2096              :  *
    2097              :  * <expr> is parsed as a boolean expression.  Invalid expressions will emit a
    2098              :  * warning and be treated as false.  Statements that follow a false expression
    2099              :  * will be parsed but ignored.  Note that in the case where an \if statement
    2100              :  * is itself within an inactive section of a block, then the entire inner
    2101              :  * \if..\endif block will be parsed but ignored.
    2102              :  */
    2103              : static backslashResult
    2104          110 : exec_command_if(PsqlScanState scan_state, ConditionalStack cstack,
    2105              :                 PQExpBuffer query_buf)
    2106              : {
    2107          110 :     if (conditional_active(cstack))
    2108              :     {
    2109              :         /*
    2110              :          * First, push a new active stack entry; this ensures that the lexer
    2111              :          * will perform variable substitution and backtick evaluation while
    2112              :          * scanning the expression.  (That should happen anyway, since we know
    2113              :          * we're in an active outer branch, but let's be sure.)
    2114              :          */
    2115          107 :         conditional_stack_push(cstack, IFSTATE_TRUE);
    2116              : 
    2117              :         /* Remember current query state in case we need to restore later */
    2118          107 :         save_query_text_state(scan_state, cstack, query_buf);
    2119              : 
    2120              :         /*
    2121              :          * Evaluate the expression; if it's false, change to inactive state.
    2122              :          */
    2123          107 :         if (!is_true_boolean_expression(scan_state, "\\if expression"))
    2124           67 :             conditional_stack_poke(cstack, IFSTATE_FALSE);
    2125              :     }
    2126              :     else
    2127              :     {
    2128              :         /*
    2129              :          * We're within an inactive outer branch, so this entire \if block
    2130              :          * will be ignored.  We don't want to evaluate the expression, so push
    2131              :          * the "ignored" stack state before scanning it.
    2132              :          */
    2133            3 :         conditional_stack_push(cstack, IFSTATE_IGNORED);
    2134              : 
    2135              :         /* Remember current query state in case we need to restore later */
    2136            3 :         save_query_text_state(scan_state, cstack, query_buf);
    2137              : 
    2138            3 :         ignore_boolean_expression(scan_state);
    2139              :     }
    2140              : 
    2141          110 :     return PSQL_CMD_SKIP_LINE;
    2142              : }
    2143              : 
    2144              : /*
    2145              :  * \elif <expr> -- alternative branch in an \if..\endif block
    2146              :  *
    2147              :  * <expr> is evaluated the same as in \if <expr>.
    2148              :  */
    2149              : static backslashResult
    2150           24 : exec_command_elif(PsqlScanState scan_state, ConditionalStack cstack,
    2151              :                   PQExpBuffer query_buf)
    2152              : {
    2153           24 :     bool        success = true;
    2154              : 
    2155           24 :     switch (conditional_stack_peek(cstack))
    2156              :     {
    2157            3 :         case IFSTATE_TRUE:
    2158              : 
    2159              :             /*
    2160              :              * Just finished active branch of this \if block.  Update saved
    2161              :              * state so we will keep whatever data was put in query_buf by the
    2162              :              * active branch.
    2163              :              */
    2164            3 :             save_query_text_state(scan_state, cstack, query_buf);
    2165              : 
    2166              :             /*
    2167              :              * Discard \elif expression and ignore the rest until \endif.
    2168              :              * Switch state before reading expression to ensure proper lexer
    2169              :              * behavior.
    2170              :              */
    2171            3 :             conditional_stack_poke(cstack, IFSTATE_IGNORED);
    2172            3 :             ignore_boolean_expression(scan_state);
    2173            3 :             break;
    2174           12 :         case IFSTATE_FALSE:
    2175              : 
    2176              :             /*
    2177              :              * Discard any query text added by the just-skipped branch.
    2178              :              */
    2179           12 :             discard_query_text(scan_state, cstack, query_buf);
    2180              : 
    2181              :             /*
    2182              :              * Have not yet found a true expression in this \if block, so this
    2183              :              * might be the first.  We have to change state before examining
    2184              :              * the expression, or the lexer won't do the right thing.
    2185              :              */
    2186           12 :             conditional_stack_poke(cstack, IFSTATE_TRUE);
    2187           12 :             if (!is_true_boolean_expression(scan_state, "\\elif expression"))
    2188            9 :                 conditional_stack_poke(cstack, IFSTATE_FALSE);
    2189           12 :             break;
    2190            3 :         case IFSTATE_IGNORED:
    2191              : 
    2192              :             /*
    2193              :              * Discard any query text added by the just-skipped branch.
    2194              :              */
    2195            3 :             discard_query_text(scan_state, cstack, query_buf);
    2196              : 
    2197              :             /*
    2198              :              * Skip expression and move on.  Either the \if block already had
    2199              :              * an active section, or whole block is being skipped.
    2200              :              */
    2201            3 :             ignore_boolean_expression(scan_state);
    2202            3 :             break;
    2203            3 :         case IFSTATE_ELSE_TRUE:
    2204              :         case IFSTATE_ELSE_FALSE:
    2205            3 :             pg_log_error("\\elif: cannot occur after \\else");
    2206            3 :             success = false;
    2207            3 :             break;
    2208            3 :         case IFSTATE_NONE:
    2209              :             /* no \if to elif from */
    2210            3 :             pg_log_error("\\elif: no matching \\if");
    2211            3 :             success = false;
    2212            3 :             break;
    2213              :     }
    2214              : 
    2215           24 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2216              : }
    2217              : 
    2218              : /*
    2219              :  * \else -- final alternative in an \if..\endif block
    2220              :  *
    2221              :  * Statements within an \else branch will only be executed if
    2222              :  * all previous \if and \elif expressions evaluated to false
    2223              :  * and the block was not itself being ignored.
    2224              :  */
    2225              : static backslashResult
    2226           66 : exec_command_else(PsqlScanState scan_state, ConditionalStack cstack,
    2227              :                   PQExpBuffer query_buf)
    2228              : {
    2229           66 :     bool        success = true;
    2230              : 
    2231           66 :     switch (conditional_stack_peek(cstack))
    2232              :     {
    2233           30 :         case IFSTATE_TRUE:
    2234              : 
    2235              :             /*
    2236              :              * Just finished active branch of this \if block.  Update saved
    2237              :              * state so we will keep whatever data was put in query_buf by the
    2238              :              * active branch.
    2239              :              */
    2240           30 :             save_query_text_state(scan_state, cstack, query_buf);
    2241              : 
    2242              :             /* Now skip the \else branch */
    2243           30 :             conditional_stack_poke(cstack, IFSTATE_ELSE_FALSE);
    2244           30 :             break;
    2245           24 :         case IFSTATE_FALSE:
    2246              : 
    2247              :             /*
    2248              :              * Discard any query text added by the just-skipped branch.
    2249              :              */
    2250           24 :             discard_query_text(scan_state, cstack, query_buf);
    2251              : 
    2252              :             /*
    2253              :              * We've not found any true \if or \elif expression, so execute
    2254              :              * the \else branch.
    2255              :              */
    2256           24 :             conditional_stack_poke(cstack, IFSTATE_ELSE_TRUE);
    2257           24 :             break;
    2258            6 :         case IFSTATE_IGNORED:
    2259              : 
    2260              :             /*
    2261              :              * Discard any query text added by the just-skipped branch.
    2262              :              */
    2263            6 :             discard_query_text(scan_state, cstack, query_buf);
    2264              : 
    2265              :             /*
    2266              :              * Either we previously processed the active branch of this \if,
    2267              :              * or the whole \if block is being skipped.  Either way, skip the
    2268              :              * \else branch.
    2269              :              */
    2270            6 :             conditional_stack_poke(cstack, IFSTATE_ELSE_FALSE);
    2271            6 :             break;
    2272            3 :         case IFSTATE_ELSE_TRUE:
    2273              :         case IFSTATE_ELSE_FALSE:
    2274            3 :             pg_log_error("\\else: cannot occur after \\else");
    2275            3 :             success = false;
    2276            3 :             break;
    2277            3 :         case IFSTATE_NONE:
    2278              :             /* no \if to else from */
    2279            3 :             pg_log_error("\\else: no matching \\if");
    2280            3 :             success = false;
    2281            3 :             break;
    2282              :     }
    2283              : 
    2284           66 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2285              : }
    2286              : 
    2287              : /*
    2288              :  * \endif -- ends an \if...\endif block
    2289              :  */
    2290              : static backslashResult
    2291          103 : exec_command_endif(PsqlScanState scan_state, ConditionalStack cstack,
    2292              :                    PQExpBuffer query_buf)
    2293              : {
    2294          103 :     bool        success = true;
    2295              : 
    2296          103 :     switch (conditional_stack_peek(cstack))
    2297              :     {
    2298           24 :         case IFSTATE_TRUE:
    2299              :         case IFSTATE_ELSE_TRUE:
    2300              :             /* Close the \if block, keeping the query text */
    2301           24 :             success = conditional_stack_pop(cstack);
    2302              :             Assert(success);
    2303           24 :             break;
    2304           76 :         case IFSTATE_FALSE:
    2305              :         case IFSTATE_IGNORED:
    2306              :         case IFSTATE_ELSE_FALSE:
    2307              : 
    2308              :             /*
    2309              :              * Discard any query text added by the just-skipped branch.
    2310              :              */
    2311           76 :             discard_query_text(scan_state, cstack, query_buf);
    2312              : 
    2313              :             /* Close the \if block */
    2314           76 :             success = conditional_stack_pop(cstack);
    2315              :             Assert(success);
    2316           76 :             break;
    2317            3 :         case IFSTATE_NONE:
    2318              :             /* no \if to end */
    2319            3 :             pg_log_error("\\endif: no matching \\if");
    2320            3 :             success = false;
    2321            3 :             break;
    2322              :     }
    2323              : 
    2324          103 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2325              : }
    2326              : 
    2327              : /*
    2328              :  * \l -- list databases
    2329              :  */
    2330              : static backslashResult
    2331            3 : exec_command_list(PsqlScanState scan_state, bool active_branch, const char *cmd)
    2332              : {
    2333            3 :     bool        success = true;
    2334              : 
    2335            3 :     if (active_branch)
    2336              :     {
    2337              :         char       *pattern;
    2338              :         bool        show_verbose;
    2339              :         unsigned short int save_expanded;
    2340              : 
    2341            0 :         pattern = psql_scan_slash_option(scan_state,
    2342              :                                          OT_NORMAL, NULL, true);
    2343              : 
    2344            0 :         show_verbose = strchr(cmd, '+') ? true : false;
    2345              : 
    2346              :         /* if 'x' option specified, force expanded mode */
    2347            0 :         save_expanded = pset.popt.topt.expanded;
    2348            0 :         if (strchr(cmd, 'x'))
    2349            0 :             pset.popt.topt.expanded = 1;
    2350              : 
    2351            0 :         success = listAllDbs(pattern, show_verbose);
    2352              : 
    2353              :         /* restore original expanded mode */
    2354            0 :         pset.popt.topt.expanded = save_expanded;
    2355              : 
    2356            0 :         free(pattern);
    2357              :     }
    2358              :     else
    2359            3 :         ignore_slash_options(scan_state);
    2360              : 
    2361            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2362              : }
    2363              : 
    2364              : /*
    2365              :  * \lo_* -- large object operations
    2366              :  */
    2367              : static backslashResult
    2368           31 : exec_command_lo(PsqlScanState scan_state, bool active_branch, const char *cmd)
    2369              : {
    2370           31 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    2371           31 :     bool        success = true;
    2372              : 
    2373           31 :     if (active_branch)
    2374              :     {
    2375              :         char       *opt1,
    2376              :                    *opt2;
    2377              : 
    2378           28 :         opt1 = psql_scan_slash_option(scan_state,
    2379              :                                       OT_NORMAL, NULL, true);
    2380           28 :         opt2 = psql_scan_slash_option(scan_state,
    2381              :                                       OT_NORMAL, NULL, true);
    2382              : 
    2383           28 :         if (strcmp(cmd + 3, "export") == 0)
    2384              :         {
    2385            3 :             if (!opt2)
    2386              :             {
    2387            0 :                 pg_log_error("\\%s: missing required argument", cmd);
    2388            0 :                 success = false;
    2389              :             }
    2390              :             else
    2391              :             {
    2392            3 :                 expand_tilde(&opt2);
    2393            3 :                 success = do_lo_export(opt1, opt2);
    2394              :             }
    2395              :         }
    2396              : 
    2397           25 :         else if (strcmp(cmd + 3, "import") == 0)
    2398              :         {
    2399            7 :             if (!opt1)
    2400              :             {
    2401            0 :                 pg_log_error("\\%s: missing required argument", cmd);
    2402            0 :                 success = false;
    2403              :             }
    2404              :             else
    2405              :             {
    2406            7 :                 expand_tilde(&opt1);
    2407            7 :                 success = do_lo_import(opt1, opt2);
    2408              :             }
    2409              :         }
    2410              : 
    2411           18 :         else if (strncmp(cmd + 3, "list", 4) == 0)
    2412              :         {
    2413              :             bool        show_verbose;
    2414              :             unsigned short int save_expanded;
    2415              : 
    2416            6 :             show_verbose = strchr(cmd, '+') ? true : false;
    2417              : 
    2418              :             /* if 'x' option specified, force expanded mode */
    2419            6 :             save_expanded = pset.popt.topt.expanded;
    2420            6 :             if (strchr(cmd, 'x'))
    2421            0 :                 pset.popt.topt.expanded = 1;
    2422              : 
    2423            6 :             success = listLargeObjects(show_verbose);
    2424              : 
    2425              :             /* restore original expanded mode */
    2426            6 :             pset.popt.topt.expanded = save_expanded;
    2427              :         }
    2428              : 
    2429           12 :         else if (strcmp(cmd + 3, "unlink") == 0)
    2430              :         {
    2431           12 :             if (!opt1)
    2432              :             {
    2433            0 :                 pg_log_error("\\%s: missing required argument", cmd);
    2434            0 :                 success = false;
    2435              :             }
    2436              :             else
    2437           12 :                 success = do_lo_unlink(opt1);
    2438              :         }
    2439              : 
    2440              :         else
    2441            0 :             status = PSQL_CMD_UNKNOWN;
    2442              : 
    2443           28 :         free(opt1);
    2444           28 :         free(opt2);
    2445              :     }
    2446              :     else
    2447            3 :         ignore_slash_options(scan_state);
    2448              : 
    2449           31 :     if (!success)
    2450            0 :         status = PSQL_CMD_ERROR;
    2451              : 
    2452           31 :     return status;
    2453              : }
    2454              : 
    2455              : /*
    2456              :  * \o -- set query output
    2457              :  */
    2458              : static backslashResult
    2459           21 : exec_command_out(PsqlScanState scan_state, bool active_branch)
    2460              : {
    2461           21 :     bool        success = true;
    2462              : 
    2463           21 :     if (active_branch)
    2464              :     {
    2465           18 :         char       *fname = psql_scan_slash_option(scan_state,
    2466              :                                                    OT_FILEPIPE, NULL, true);
    2467              : 
    2468           18 :         expand_tilde(&fname);
    2469           18 :         success = setQFout(fname);
    2470           18 :         free(fname);
    2471              :     }
    2472              :     else
    2473            3 :         ignore_slash_filepipe(scan_state);
    2474              : 
    2475           21 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2476              : }
    2477              : 
    2478              : /*
    2479              :  * \p -- print the current query buffer
    2480              :  */
    2481              : static backslashResult
    2482           21 : exec_command_print(PsqlScanState scan_state, bool active_branch,
    2483              :                    PQExpBuffer query_buf, PQExpBuffer previous_buf)
    2484              : {
    2485           21 :     if (active_branch)
    2486              :     {
    2487              :         /*
    2488              :          * We want to print the same thing \g would execute, but not to change
    2489              :          * the query buffer state; so we can't use copy_previous_query().
    2490              :          * Also, beware of possibility that buffer pointers are NULL.
    2491              :          */
    2492           18 :         if (query_buf && query_buf->len > 0)
    2493            9 :             puts(query_buf->data);
    2494            9 :         else if (previous_buf && previous_buf->len > 0)
    2495            9 :             puts(previous_buf->data);
    2496            0 :         else if (!pset.quiet)
    2497            0 :             puts(_("Query buffer is empty."));
    2498           18 :         fflush(stdout);
    2499              :     }
    2500              : 
    2501           21 :     return PSQL_CMD_SKIP_LINE;
    2502              : }
    2503              : 
    2504              : /*
    2505              :  * \parse -- parse query
    2506              :  */
    2507              : static backslashResult
    2508           57 : exec_command_parse(PsqlScanState scan_state, bool active_branch,
    2509              :                    const char *cmd)
    2510              : {
    2511           57 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    2512              : 
    2513           57 :     if (active_branch)
    2514              :     {
    2515           54 :         char       *opt = psql_scan_slash_option(scan_state,
    2516              :                                                  OT_NORMAL, NULL, false);
    2517              : 
    2518           54 :         clean_extended_state();
    2519              : 
    2520           54 :         if (!opt)
    2521              :         {
    2522            3 :             pg_log_error("\\%s: missing required argument", cmd);
    2523            3 :             status = PSQL_CMD_ERROR;
    2524              :         }
    2525              :         else
    2526              :         {
    2527           51 :             pset.stmtName = opt;
    2528           51 :             pset.send_mode = PSQL_SEND_EXTENDED_PARSE;
    2529           51 :             status = PSQL_CMD_SEND;
    2530              :         }
    2531              :     }
    2532              :     else
    2533            3 :         ignore_slash_options(scan_state);
    2534              : 
    2535           57 :     return status;
    2536              : }
    2537              : 
    2538              : /*
    2539              :  * \password -- set user password
    2540              :  */
    2541              : static backslashResult
    2542            4 : exec_command_password(PsqlScanState scan_state, bool active_branch)
    2543              : {
    2544            4 :     bool        success = true;
    2545              : 
    2546            4 :     if (active_branch)
    2547              :     {
    2548            1 :         char       *user = psql_scan_slash_option(scan_state,
    2549              :                                                   OT_SQLID, NULL, true);
    2550            1 :         char       *pw1 = NULL;
    2551            1 :         char       *pw2 = NULL;
    2552              :         PQExpBufferData buf;
    2553              :         PromptInterruptContext prompt_ctx;
    2554              : 
    2555            1 :         if (user == NULL)
    2556              :         {
    2557              :             /* By default, the command applies to CURRENT_USER */
    2558              :             PGresult   *res;
    2559              : 
    2560            0 :             res = PSQLexec("SELECT CURRENT_USER");
    2561            0 :             if (!res)
    2562            0 :                 return PSQL_CMD_ERROR;
    2563              : 
    2564            0 :             user = pg_strdup(PQgetvalue(res, 0, 0));
    2565            0 :             PQclear(res);
    2566              :         }
    2567              : 
    2568              :         /* Set up to let SIGINT cancel simple_prompt_extended() */
    2569            1 :         prompt_ctx.jmpbuf = sigint_interrupt_jmp;
    2570            1 :         prompt_ctx.enabled = &sigint_interrupt_enabled;
    2571            1 :         prompt_ctx.canceled = false;
    2572              : 
    2573            1 :         initPQExpBuffer(&buf);
    2574            1 :         printfPQExpBuffer(&buf, _("Enter new password for user \"%s\": "), user);
    2575              : 
    2576            1 :         pw1 = simple_prompt_extended(buf.data, false, &prompt_ctx);
    2577            1 :         if (!prompt_ctx.canceled)
    2578            1 :             pw2 = simple_prompt_extended("Enter it again: ", false, &prompt_ctx);
    2579              : 
    2580            1 :         if (prompt_ctx.canceled)
    2581              :         {
    2582              :             /* fail silently */
    2583            0 :             success = false;
    2584              :         }
    2585            1 :         else if (strcmp(pw1, pw2) != 0)
    2586              :         {
    2587            0 :             pg_log_error("Passwords didn't match.");
    2588            0 :             success = false;
    2589              :         }
    2590              :         else
    2591              :         {
    2592            1 :             PGresult   *res = PQchangePassword(pset.db, user, pw1);
    2593              : 
    2594            1 :             if (PQresultStatus(res) != PGRES_COMMAND_OK)
    2595              :             {
    2596            0 :                 pg_log_info("%s", PQerrorMessage(pset.db));
    2597            0 :                 success = false;
    2598              :             }
    2599              : 
    2600            1 :             PQclear(res);
    2601              :         }
    2602              : 
    2603            1 :         free(user);
    2604            1 :         free(pw1);
    2605            1 :         free(pw2);
    2606            1 :         termPQExpBuffer(&buf);
    2607              :     }
    2608              :     else
    2609            3 :         ignore_slash_options(scan_state);
    2610              : 
    2611            4 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2612              : }
    2613              : 
    2614              : /*
    2615              :  * \prompt -- prompt and set variable
    2616              :  */
    2617              : static backslashResult
    2618            3 : exec_command_prompt(PsqlScanState scan_state, bool active_branch,
    2619              :                     const char *cmd)
    2620              : {
    2621            3 :     bool        success = true;
    2622              : 
    2623            3 :     if (active_branch)
    2624              :     {
    2625              :         char       *opt,
    2626            0 :                    *prompt_text = NULL;
    2627              :         char       *arg1,
    2628              :                    *arg2;
    2629              : 
    2630            0 :         arg1 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
    2631            0 :         arg2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
    2632              : 
    2633            0 :         if (!arg1)
    2634              :         {
    2635            0 :             pg_log_error("\\%s: missing required argument", cmd);
    2636            0 :             success = false;
    2637              :         }
    2638              :         else
    2639              :         {
    2640              :             char       *result;
    2641              :             PromptInterruptContext prompt_ctx;
    2642              : 
    2643              :             /* Set up to let SIGINT cancel simple_prompt_extended() */
    2644            0 :             prompt_ctx.jmpbuf = sigint_interrupt_jmp;
    2645            0 :             prompt_ctx.enabled = &sigint_interrupt_enabled;
    2646            0 :             prompt_ctx.canceled = false;
    2647              : 
    2648            0 :             if (arg2)
    2649              :             {
    2650            0 :                 prompt_text = arg1;
    2651            0 :                 opt = arg2;
    2652              :             }
    2653              :             else
    2654            0 :                 opt = arg1;
    2655              : 
    2656            0 :             if (!pset.inputfile)
    2657              :             {
    2658            0 :                 result = simple_prompt_extended(prompt_text, true, &prompt_ctx);
    2659              :             }
    2660              :             else
    2661              :             {
    2662            0 :                 if (prompt_text)
    2663              :                 {
    2664            0 :                     fputs(prompt_text, stdout);
    2665            0 :                     fflush(stdout);
    2666              :                 }
    2667            0 :                 result = gets_fromFile(stdin);
    2668            0 :                 if (!result)
    2669              :                 {
    2670            0 :                     pg_log_error("\\%s: could not read value for variable",
    2671              :                                  cmd);
    2672            0 :                     success = false;
    2673              :                 }
    2674              :             }
    2675              : 
    2676            0 :             if (prompt_ctx.canceled ||
    2677            0 :                 (result && !SetVariable(pset.vars, opt, result)))
    2678            0 :                 success = false;
    2679              : 
    2680            0 :             free(result);
    2681            0 :             free(prompt_text);
    2682            0 :             free(opt);
    2683              :         }
    2684              :     }
    2685              :     else
    2686            3 :         ignore_slash_options(scan_state);
    2687              : 
    2688            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2689              : }
    2690              : 
    2691              : /*
    2692              :  * \pset -- set printing parameters
    2693              :  */
    2694              : static backslashResult
    2695          899 : exec_command_pset(PsqlScanState scan_state, bool active_branch)
    2696              : {
    2697          899 :     bool        success = true;
    2698              : 
    2699          899 :     if (active_branch)
    2700              :     {
    2701          893 :         char       *opt0 = psql_scan_slash_option(scan_state,
    2702              :                                                   OT_NORMAL, NULL, false);
    2703          893 :         char       *opt1 = psql_scan_slash_option(scan_state,
    2704              :                                                   OT_NORMAL, NULL, false);
    2705              : 
    2706          893 :         if (!opt0)
    2707              :         {
    2708              :             /* list all variables */
    2709              : 
    2710              :             int         i;
    2711              :             static const char *const my_list[] = {
    2712              :                 "border", "columns", "csv_fieldsep",
    2713              :                 "display_false", "display_true", "expanded", "fieldsep",
    2714              :                 "fieldsep_zero", "footer", "format", "linestyle", "null",
    2715              :                 "numericlocale", "pager", "pager_min_lines",
    2716              :                 "recordsep", "recordsep_zero",
    2717              :                 "tableattr", "title", "tuples_only",
    2718              :                 "unicode_border_linestyle",
    2719              :                 "unicode_column_linestyle",
    2720              :                 "unicode_header_linestyle",
    2721              :                 "xheader_width",
    2722              :                 NULL
    2723              :             };
    2724              : 
    2725           75 :             for (i = 0; my_list[i] != NULL; i++)
    2726              :             {
    2727           72 :                 char       *val = pset_value_string(my_list[i], &pset.popt);
    2728              : 
    2729           72 :                 printf("%-24s %s\n", my_list[i], val);
    2730           72 :                 free(val);
    2731              :             }
    2732              : 
    2733            3 :             success = true;
    2734              :         }
    2735              :         else
    2736          890 :             success = do_pset(opt0, opt1, &pset.popt, pset.quiet);
    2737              : 
    2738          893 :         free(opt0);
    2739          893 :         free(opt1);
    2740              :     }
    2741              :     else
    2742            6 :         ignore_slash_options(scan_state);
    2743              : 
    2744          899 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2745              : }
    2746              : 
    2747              : /*
    2748              :  * \q or \quit -- exit psql
    2749              :  */
    2750              : static backslashResult
    2751          221 : exec_command_quit(PsqlScanState scan_state, bool active_branch)
    2752              : {
    2753          221 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    2754              : 
    2755          221 :     if (active_branch)
    2756          178 :         status = PSQL_CMD_TERMINATE;
    2757              : 
    2758          221 :     return status;
    2759              : }
    2760              : 
    2761              : /*
    2762              :  * \r -- reset (clear) the query buffer
    2763              :  */
    2764              : static backslashResult
    2765           48 : exec_command_reset(PsqlScanState scan_state, bool active_branch,
    2766              :                    PQExpBuffer query_buf)
    2767              : {
    2768           48 :     if (active_branch)
    2769              :     {
    2770           45 :         resetPQExpBuffer(query_buf);
    2771           45 :         psql_scan_reset(scan_state);
    2772           45 :         if (!pset.quiet)
    2773           30 :             puts(_("Query buffer reset (cleared)."));
    2774              :     }
    2775              : 
    2776           48 :     return PSQL_CMD_SKIP_LINE;
    2777              : }
    2778              : 
    2779              : /*
    2780              :  * \restrict -- enter "restricted mode" with the provided key
    2781              :  */
    2782              : static backslashResult
    2783           35 : exec_command_restrict(PsqlScanState scan_state, bool active_branch,
    2784              :                       const char *cmd)
    2785              : {
    2786           35 :     if (active_branch)
    2787              :     {
    2788              :         char       *opt;
    2789              : 
    2790              :         Assert(!restricted);
    2791              : 
    2792           32 :         opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
    2793           32 :         if (opt == NULL || opt[0] == '\0')
    2794              :         {
    2795            0 :             pg_log_error("\\%s: missing required argument", cmd);
    2796            0 :             return PSQL_CMD_ERROR;
    2797              :         }
    2798              : 
    2799           32 :         restrict_key = pstrdup(opt);
    2800           32 :         restricted = true;
    2801              :     }
    2802              :     else
    2803            3 :         ignore_slash_options(scan_state);
    2804              : 
    2805           35 :     return PSQL_CMD_SKIP_LINE;
    2806              : }
    2807              : 
    2808              : /*
    2809              :  * \s -- save history in a file or show it on the screen
    2810              :  */
    2811              : static backslashResult
    2812            3 : exec_command_s(PsqlScanState scan_state, bool active_branch)
    2813              : {
    2814            3 :     bool        success = true;
    2815              : 
    2816            3 :     if (active_branch)
    2817              :     {
    2818            0 :         char       *fname = psql_scan_slash_option(scan_state,
    2819              :                                                    OT_NORMAL, NULL, true);
    2820              : 
    2821            0 :         expand_tilde(&fname);
    2822            0 :         success = printHistory(fname, pset.popt.topt.pager);
    2823            0 :         if (success && !pset.quiet && fname)
    2824            0 :             printf(_("Wrote history to file \"%s\".\n"), fname);
    2825            0 :         if (!fname)
    2826            0 :             putchar('\n');
    2827            0 :         free(fname);
    2828              :     }
    2829              :     else
    2830            3 :         ignore_slash_options(scan_state);
    2831              : 
    2832            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2833              : }
    2834              : 
    2835              : /*
    2836              :  * \sendpipeline -- send an extended query to an ongoing pipeline
    2837              :  */
    2838              : static backslashResult
    2839          295 : exec_command_sendpipeline(PsqlScanState scan_state, bool active_branch)
    2840              : {
    2841          295 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    2842              : 
    2843          295 :     if (active_branch)
    2844              :     {
    2845          292 :         if (PQpipelineStatus(pset.db) != PQ_PIPELINE_OFF)
    2846              :         {
    2847          286 :             if (pset.send_mode == PSQL_SEND_EXTENDED_QUERY_PREPARED ||
    2848          262 :                 pset.send_mode == PSQL_SEND_EXTENDED_QUERY_PARAMS)
    2849              :             {
    2850          280 :                 status = PSQL_CMD_SEND;
    2851              :             }
    2852              :             else
    2853              :             {
    2854            6 :                 pg_log_error("\\sendpipeline must be used after \\bind or \\bind_named");
    2855            6 :                 clean_extended_state();
    2856            6 :                 return PSQL_CMD_ERROR;
    2857              :             }
    2858              :         }
    2859              :         else
    2860              :         {
    2861            6 :             pg_log_error("\\sendpipeline not allowed outside of pipeline mode");
    2862            6 :             clean_extended_state();
    2863            6 :             return PSQL_CMD_ERROR;
    2864              :         }
    2865              :     }
    2866              :     else
    2867            3 :         ignore_slash_options(scan_state);
    2868              : 
    2869          283 :     return status;
    2870              : }
    2871              : 
    2872              : /*
    2873              :  * \set -- set variable
    2874              :  */
    2875              : static backslashResult
    2876          511 : exec_command_set(PsqlScanState scan_state, bool active_branch)
    2877              : {
    2878          511 :     bool        success = true;
    2879              : 
    2880          511 :     if (active_branch)
    2881              :     {
    2882          508 :         char       *opt0 = psql_scan_slash_option(scan_state,
    2883              :                                                   OT_NORMAL, NULL, false);
    2884              : 
    2885          508 :         if (!opt0)
    2886              :         {
    2887              :             /* list all variables */
    2888            0 :             PrintVariables(pset.vars);
    2889            0 :             success = true;
    2890              :         }
    2891              :         else
    2892              :         {
    2893              :             /*
    2894              :              * Set variable to the concatenation of the arguments.
    2895              :              */
    2896              :             char       *newval;
    2897              :             char       *opt;
    2898              : 
    2899          508 :             opt = psql_scan_slash_option(scan_state,
    2900              :                                          OT_NORMAL, NULL, false);
    2901          508 :             newval = pg_strdup(opt ? opt : "");
    2902          508 :             free(opt);
    2903              : 
    2904          761 :             while ((opt = psql_scan_slash_option(scan_state,
    2905              :                                                  OT_NORMAL, NULL, false)))
    2906              :             {
    2907          253 :                 newval = pg_realloc(newval, strlen(newval) + strlen(opt) + 1);
    2908          253 :                 strcat(newval, opt);
    2909          253 :                 free(opt);
    2910              :             }
    2911              : 
    2912          508 :             if (!SetVariable(pset.vars, opt0, newval))
    2913           13 :                 success = false;
    2914              : 
    2915          508 :             free(newval);
    2916              :         }
    2917          508 :         free(opt0);
    2918              :     }
    2919              :     else
    2920            3 :         ignore_slash_options(scan_state);
    2921              : 
    2922          511 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2923              : }
    2924              : 
    2925              : /*
    2926              :  * \setenv -- set environment variable
    2927              :  */
    2928              : static backslashResult
    2929            9 : exec_command_setenv(PsqlScanState scan_state, bool active_branch,
    2930              :                     const char *cmd)
    2931              : {
    2932            9 :     bool        success = true;
    2933              : 
    2934            9 :     if (active_branch)
    2935              :     {
    2936            6 :         char       *envvar = psql_scan_slash_option(scan_state,
    2937              :                                                     OT_NORMAL, NULL, false);
    2938            6 :         char       *envval = psql_scan_slash_option(scan_state,
    2939              :                                                     OT_NORMAL, NULL, false);
    2940              : 
    2941            6 :         if (!envvar)
    2942              :         {
    2943            0 :             pg_log_error("\\%s: missing required argument", cmd);
    2944            0 :             success = false;
    2945              :         }
    2946            6 :         else if (strchr(envvar, '=') != NULL)
    2947              :         {
    2948            0 :             pg_log_error("\\%s: environment variable name must not contain \"=\"",
    2949              :                          cmd);
    2950            0 :             success = false;
    2951              :         }
    2952            6 :         else if (!envval)
    2953              :         {
    2954              :             /* No argument - unset the environment variable */
    2955            3 :             unsetenv(envvar);
    2956            3 :             success = true;
    2957              :         }
    2958              :         else
    2959              :         {
    2960              :             /* Set variable to the value of the next argument */
    2961            3 :             setenv(envvar, envval, 1);
    2962            3 :             success = true;
    2963              :         }
    2964            6 :         free(envvar);
    2965            6 :         free(envval);
    2966              :     }
    2967              :     else
    2968            3 :         ignore_slash_options(scan_state);
    2969              : 
    2970            9 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    2971              : }
    2972              : 
    2973              : /*
    2974              :  * \sf/\sv -- show a function/view's source code
    2975              :  */
    2976              : static backslashResult
    2977           99 : exec_command_sf_sv(PsqlScanState scan_state, bool active_branch,
    2978              :                    const char *cmd, bool is_func)
    2979              : {
    2980           99 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    2981              : 
    2982           99 :     if (active_branch)
    2983              :     {
    2984           93 :         bool        show_linenumbers = (strchr(cmd, '+') != NULL);
    2985              :         PQExpBuffer buf;
    2986              :         char       *obj_desc;
    2987           93 :         Oid         obj_oid = InvalidOid;
    2988           93 :         EditableObjectType eot = is_func ? EditableFunction : EditableView;
    2989              : 
    2990           93 :         buf = createPQExpBuffer();
    2991           93 :         obj_desc = psql_scan_slash_option(scan_state,
    2992              :                                           OT_WHOLE_LINE, NULL, true);
    2993           93 :         if (!obj_desc)
    2994              :         {
    2995            0 :             if (is_func)
    2996            0 :                 pg_log_error("function name is required");
    2997              :             else
    2998            0 :                 pg_log_error("view name is required");
    2999            0 :             status = PSQL_CMD_ERROR;
    3000              :         }
    3001           93 :         else if (!lookup_object_oid(eot, obj_desc, &obj_oid))
    3002              :         {
    3003              :             /* error already reported */
    3004            0 :             status = PSQL_CMD_ERROR;
    3005              :         }
    3006           93 :         else if (!get_create_object_cmd(eot, obj_oid, buf))
    3007              :         {
    3008              :             /* error already reported */
    3009            0 :             status = PSQL_CMD_ERROR;
    3010              :         }
    3011              :         else
    3012              :         {
    3013              :             FILE       *output;
    3014              :             bool        is_pager;
    3015              : 
    3016              :             /* Select output stream: stdout, pager, or file */
    3017           93 :             if (pset.queryFout == stdout)
    3018              :             {
    3019              :                 /* count lines in function to see if pager is needed */
    3020           93 :                 int         lineno = count_lines_in_buf(buf);
    3021              : 
    3022           93 :                 output = PageOutput(lineno, &(pset.popt.topt));
    3023           93 :                 is_pager = true;
    3024              :             }
    3025              :             else
    3026              :             {
    3027              :                 /* use previously set output file, without pager */
    3028            0 :                 output = pset.queryFout;
    3029            0 :                 is_pager = false;
    3030              :             }
    3031              : 
    3032           93 :             if (show_linenumbers)
    3033              :             {
    3034              :                 /* add line numbers */
    3035            9 :                 print_with_linenumbers(output, buf->data, is_func);
    3036              :             }
    3037              :             else
    3038              :             {
    3039              :                 /* just send the definition to output */
    3040           84 :                 fputs(buf->data, output);
    3041              :             }
    3042              : 
    3043           93 :             if (is_pager)
    3044           93 :                 ClosePager(output);
    3045              :         }
    3046              : 
    3047           93 :         free(obj_desc);
    3048           93 :         destroyPQExpBuffer(buf);
    3049              :     }
    3050              :     else
    3051            6 :         ignore_slash_whole_line(scan_state);
    3052              : 
    3053           99 :     return status;
    3054              : }
    3055              : 
    3056              : /*
    3057              :  * \startpipeline -- enter pipeline mode
    3058              :  */
    3059              : static backslashResult
    3060          149 : exec_command_startpipeline(PsqlScanState scan_state, bool active_branch)
    3061              : {
    3062          149 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    3063              : 
    3064          149 :     if (active_branch)
    3065              :     {
    3066          146 :         pset.send_mode = PSQL_SEND_START_PIPELINE_MODE;
    3067          146 :         status = PSQL_CMD_SEND;
    3068              :     }
    3069              :     else
    3070            3 :         ignore_slash_options(scan_state);
    3071              : 
    3072          149 :     return status;
    3073              : }
    3074              : 
    3075              : /*
    3076              :  * \syncpipeline -- send a sync message to an active pipeline
    3077              :  */
    3078              : static backslashResult
    3079           57 : exec_command_syncpipeline(PsqlScanState scan_state, bool active_branch)
    3080              : {
    3081           57 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    3082              : 
    3083           57 :     if (active_branch)
    3084              :     {
    3085           54 :         pset.send_mode = PSQL_SEND_PIPELINE_SYNC;
    3086           54 :         status = PSQL_CMD_SEND;
    3087              :     }
    3088              :     else
    3089            3 :         ignore_slash_options(scan_state);
    3090              : 
    3091           57 :     return status;
    3092              : }
    3093              : 
    3094              : /*
    3095              :  * \endpipeline -- end pipeline mode
    3096              :  */
    3097              : static backslashResult
    3098          149 : exec_command_endpipeline(PsqlScanState scan_state, bool active_branch)
    3099              : {
    3100          149 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    3101              : 
    3102          149 :     if (active_branch)
    3103              :     {
    3104          146 :         pset.send_mode = PSQL_SEND_END_PIPELINE_MODE;
    3105          146 :         status = PSQL_CMD_SEND;
    3106              :     }
    3107              :     else
    3108            3 :         ignore_slash_options(scan_state);
    3109              : 
    3110          149 :     return status;
    3111              : }
    3112              : 
    3113              : /*
    3114              :  * \t -- turn off table headers and row count
    3115              :  */
    3116              : static backslashResult
    3117           35 : exec_command_t(PsqlScanState scan_state, bool active_branch)
    3118              : {
    3119           35 :     bool        success = true;
    3120              : 
    3121           35 :     if (active_branch)
    3122              :     {
    3123           32 :         char       *opt = psql_scan_slash_option(scan_state,
    3124              :                                                  OT_NORMAL, NULL, true);
    3125              : 
    3126           32 :         success = do_pset("tuples_only", opt, &pset.popt, pset.quiet);
    3127           32 :         free(opt);
    3128              :     }
    3129              :     else
    3130            3 :         ignore_slash_options(scan_state);
    3131              : 
    3132           35 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    3133              : }
    3134              : 
    3135              : /*
    3136              :  * \T -- define html <table ...> attributes
    3137              :  */
    3138              : static backslashResult
    3139            3 : exec_command_T(PsqlScanState scan_state, bool active_branch)
    3140              : {
    3141            3 :     bool        success = true;
    3142              : 
    3143            3 :     if (active_branch)
    3144              :     {
    3145            0 :         char       *value = psql_scan_slash_option(scan_state,
    3146              :                                                    OT_NORMAL, NULL, false);
    3147              : 
    3148            0 :         success = do_pset("tableattr", value, &pset.popt, pset.quiet);
    3149            0 :         free(value);
    3150              :     }
    3151              :     else
    3152            3 :         ignore_slash_options(scan_state);
    3153              : 
    3154            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    3155              : }
    3156              : 
    3157              : /*
    3158              :  * \timing -- enable/disable timing of queries
    3159              :  */
    3160              : static backslashResult
    3161            5 : exec_command_timing(PsqlScanState scan_state, bool active_branch)
    3162              : {
    3163            5 :     bool        success = true;
    3164              : 
    3165            5 :     if (active_branch)
    3166              :     {
    3167            2 :         char       *opt = psql_scan_slash_option(scan_state,
    3168              :                                                  OT_NORMAL, NULL, false);
    3169              : 
    3170            2 :         if (opt)
    3171            2 :             success = ParseVariableBool(opt, "\\timing", &pset.timing);
    3172              :         else
    3173            0 :             pset.timing = !pset.timing;
    3174            2 :         if (!pset.quiet)
    3175              :         {
    3176            0 :             if (pset.timing)
    3177            0 :                 puts(_("Timing is on."));
    3178              :             else
    3179            0 :                 puts(_("Timing is off."));
    3180              :         }
    3181            2 :         free(opt);
    3182              :     }
    3183              :     else
    3184            3 :         ignore_slash_options(scan_state);
    3185              : 
    3186            5 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    3187              : }
    3188              : 
    3189              : /*
    3190              :  * \unrestrict -- exit "restricted mode" if provided key matches
    3191              :  */
    3192              : static backslashResult
    3193           34 : exec_command_unrestrict(PsqlScanState scan_state, bool active_branch,
    3194              :                         const char *cmd)
    3195              : {
    3196           34 :     if (active_branch)
    3197              :     {
    3198              :         char       *opt;
    3199              : 
    3200           31 :         opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
    3201           31 :         if (opt == NULL || opt[0] == '\0')
    3202              :         {
    3203            0 :             pg_log_error("\\%s: missing required argument", cmd);
    3204            0 :             return PSQL_CMD_ERROR;
    3205              :         }
    3206              : 
    3207           31 :         if (!restricted)
    3208              :         {
    3209            0 :             pg_log_error("\\%s: not currently in restricted mode", cmd);
    3210            0 :             return PSQL_CMD_ERROR;
    3211              :         }
    3212           31 :         else if (strcmp(opt, restrict_key) == 0)
    3213              :         {
    3214           31 :             pfree(restrict_key);
    3215           31 :             restricted = false;
    3216              :         }
    3217              :         else
    3218              :         {
    3219            0 :             pg_log_error("\\%s: wrong key", cmd);
    3220            0 :             return PSQL_CMD_ERROR;
    3221              :         }
    3222              :     }
    3223              :     else
    3224            3 :         ignore_slash_options(scan_state);
    3225              : 
    3226           34 :     return PSQL_CMD_SKIP_LINE;
    3227              : }
    3228              : 
    3229              : /*
    3230              :  * \unset -- unset variable
    3231              :  */
    3232              : static backslashResult
    3233           26 : exec_command_unset(PsqlScanState scan_state, bool active_branch,
    3234              :                    const char *cmd)
    3235              : {
    3236           26 :     bool        success = true;
    3237              : 
    3238           26 :     if (active_branch)
    3239              :     {
    3240           23 :         char       *opt = psql_scan_slash_option(scan_state,
    3241              :                                                  OT_NORMAL, NULL, false);
    3242              : 
    3243           23 :         if (!opt)
    3244              :         {
    3245            0 :             pg_log_error("\\%s: missing required argument", cmd);
    3246            0 :             success = false;
    3247              :         }
    3248           23 :         else if (!SetVariable(pset.vars, opt, NULL))
    3249            0 :             success = false;
    3250              : 
    3251           23 :         free(opt);
    3252              :     }
    3253              :     else
    3254            3 :         ignore_slash_options(scan_state);
    3255              : 
    3256           26 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    3257              : }
    3258              : 
    3259              : /*
    3260              :  * \w -- write query buffer to file
    3261              :  */
    3262              : static backslashResult
    3263            6 : exec_command_write(PsqlScanState scan_state, bool active_branch,
    3264              :                    const char *cmd,
    3265              :                    PQExpBuffer query_buf, PQExpBuffer previous_buf)
    3266              : {
    3267            6 :     backslashResult status = PSQL_CMD_SKIP_LINE;
    3268              : 
    3269            6 :     if (active_branch)
    3270              :     {
    3271            0 :         char       *fname = psql_scan_slash_option(scan_state,
    3272              :                                                    OT_FILEPIPE, NULL, true);
    3273            0 :         FILE       *fd = NULL;
    3274            0 :         bool        is_pipe = false;
    3275              : 
    3276            0 :         if (!query_buf)
    3277              :         {
    3278            0 :             pg_log_error("no query buffer");
    3279            0 :             status = PSQL_CMD_ERROR;
    3280              :         }
    3281              :         else
    3282              :         {
    3283            0 :             if (!fname)
    3284              :             {
    3285            0 :                 pg_log_error("\\%s: missing required argument", cmd);
    3286            0 :                 status = PSQL_CMD_ERROR;
    3287              :             }
    3288              :             else
    3289              :             {
    3290            0 :                 expand_tilde(&fname);
    3291            0 :                 if (fname[0] == '|')
    3292              :                 {
    3293            0 :                     is_pipe = true;
    3294            0 :                     fflush(NULL);
    3295            0 :                     disable_sigpipe_trap();
    3296            0 :                     fd = popen(&fname[1], "w");
    3297              :                 }
    3298              :                 else
    3299              :                 {
    3300            0 :                     canonicalize_path_enc(fname, pset.encoding);
    3301            0 :                     fd = fopen(fname, "w");
    3302              :                 }
    3303            0 :                 if (!fd)
    3304              :                 {
    3305            0 :                     pg_log_error("%s: %m", fname);
    3306            0 :                     status = PSQL_CMD_ERROR;
    3307              :                 }
    3308              :             }
    3309              :         }
    3310              : 
    3311            0 :         if (fd)
    3312              :         {
    3313              :             int         result;
    3314              : 
    3315              :             /*
    3316              :              * We want to print the same thing \g would execute, but not to
    3317              :              * change the query buffer state; so we can't use
    3318              :              * copy_previous_query().  Also, beware of possibility that buffer
    3319              :              * pointers are NULL.
    3320              :              */
    3321            0 :             if (query_buf && query_buf->len > 0)
    3322            0 :                 fprintf(fd, "%s\n", query_buf->data);
    3323            0 :             else if (previous_buf && previous_buf->len > 0)
    3324            0 :                 fprintf(fd, "%s\n", previous_buf->data);
    3325              : 
    3326            0 :             if (is_pipe)
    3327              :             {
    3328            0 :                 result = pclose(fd);
    3329              : 
    3330            0 :                 if (result != 0)
    3331              :                 {
    3332            0 :                     pg_log_error("%s: %s", fname, wait_result_to_str(result));
    3333            0 :                     status = PSQL_CMD_ERROR;
    3334              :                 }
    3335            0 :                 SetShellResultVariables(result);
    3336              :             }
    3337              :             else
    3338              :             {
    3339            0 :                 result = fclose(fd);
    3340              : 
    3341            0 :                 if (result == EOF)
    3342              :                 {
    3343            0 :                     pg_log_error("%s: %m", fname);
    3344            0 :                     status = PSQL_CMD_ERROR;
    3345              :                 }
    3346              :             }
    3347              :         }
    3348              : 
    3349            0 :         if (is_pipe)
    3350            0 :             restore_sigpipe_trap();
    3351              : 
    3352            0 :         free(fname);
    3353              :     }
    3354              :     else
    3355            6 :         ignore_slash_filepipe(scan_state);
    3356              : 
    3357            6 :     return status;
    3358              : }
    3359              : 
    3360              : /*
    3361              :  * \watch -- execute a query every N seconds.
    3362              :  * Optionally, stop after M iterations.
    3363              :  */
    3364              : static backslashResult
    3365           20 : exec_command_watch(PsqlScanState scan_state, bool active_branch,
    3366              :                    PQExpBuffer query_buf, PQExpBuffer previous_buf)
    3367              : {
    3368           20 :     bool        success = true;
    3369              : 
    3370           20 :     if (active_branch)
    3371              :     {
    3372           17 :         bool        have_sleep = false;
    3373           17 :         bool        have_iter = false;
    3374           17 :         bool        have_min_rows = false;
    3375           17 :         double      sleep = pset.watch_interval;
    3376           17 :         int         iter = 0;
    3377           17 :         int         min_rows = 0;
    3378              : 
    3379           17 :         if (PQpipelineStatus(pset.db) != PQ_PIPELINE_OFF)
    3380              :         {
    3381            3 :             pg_log_error("\\%s not allowed in pipeline mode", "watch");
    3382            3 :             clean_extended_state();
    3383            3 :             success = false;
    3384              :         }
    3385              : 
    3386              :         /*
    3387              :          * Parse arguments.  We allow either an unlabeled interval or
    3388              :          * "name=value", where name is from the set ('i', 'interval', 'c',
    3389              :          * 'count', 'm', 'min_rows').  The parsing of interval value should be
    3390              :          * kept in sync with ParseVariableDouble which is used for setting the
    3391              :          * default interval value.
    3392              :          */
    3393           37 :         while (success)
    3394              :         {
    3395           27 :             char       *opt = psql_scan_slash_option(scan_state,
    3396              :                                                      OT_NORMAL, NULL, true);
    3397              :             char       *valptr;
    3398              :             char       *opt_end;
    3399              : 
    3400           27 :             if (!opt)
    3401            7 :                 break;          /* no more arguments */
    3402              : 
    3403           20 :             valptr = strchr(opt, '=');
    3404           20 :             if (valptr)
    3405              :             {
    3406              :                 /* Labeled argument */
    3407           12 :                 valptr++;
    3408           12 :                 if (strncmp("i=", opt, strlen("i=")) == 0 ||
    3409            9 :                     strncmp("interval=", opt, strlen("interval=")) == 0)
    3410              :                 {
    3411            3 :                     if (have_sleep)
    3412              :                     {
    3413            0 :                         pg_log_error("\\watch: interval value is specified more than once");
    3414            0 :                         success = false;
    3415              :                     }
    3416              :                     else
    3417              :                     {
    3418            3 :                         have_sleep = true;
    3419            3 :                         errno = 0;
    3420            3 :                         sleep = strtod(valptr, &opt_end);
    3421            3 :                         if (sleep < 0 || *opt_end || errno == ERANGE)
    3422              :                         {
    3423            0 :                             pg_log_error("\\watch: incorrect interval value \"%s\"", valptr);
    3424            0 :                             success = false;
    3425              :                         }
    3426              :                     }
    3427              :                 }
    3428            9 :                 else if (strncmp("c=", opt, strlen("c=")) == 0 ||
    3429            4 :                          strncmp("count=", opt, strlen("count=")) == 0)
    3430              :                 {
    3431            5 :                     if (have_iter)
    3432              :                     {
    3433            1 :                         pg_log_error("\\watch: iteration count is specified more than once");
    3434            1 :                         success = false;
    3435              :                     }
    3436              :                     else
    3437              :                     {
    3438            4 :                         have_iter = true;
    3439            4 :                         errno = 0;
    3440            4 :                         iter = strtoint(valptr, &opt_end, 10);
    3441            4 :                         if (iter <= 0 || *opt_end || errno == ERANGE)
    3442              :                         {
    3443            0 :                             pg_log_error("\\watch: incorrect iteration count \"%s\"", valptr);
    3444            0 :                             success = false;
    3445              :                         }
    3446              :                     }
    3447              :                 }
    3448            4 :                 else if (strncmp("m=", opt, strlen("m=")) == 0 ||
    3449            1 :                          strncmp("min_rows=", opt, strlen("min_rows=")) == 0)
    3450              :                 {
    3451            4 :                     if (have_min_rows)
    3452              :                     {
    3453            1 :                         pg_log_error("\\watch: minimum row count specified more than once");
    3454            1 :                         success = false;
    3455              :                     }
    3456              :                     else
    3457              :                     {
    3458            3 :                         have_min_rows = true;
    3459            3 :                         errno = 0;
    3460            3 :                         min_rows = strtoint(valptr, &opt_end, 10);
    3461            3 :                         if (min_rows <= 0 || *opt_end || errno == ERANGE)
    3462              :                         {
    3463            1 :                             pg_log_error("\\watch: incorrect minimum row count \"%s\"", valptr);
    3464            1 :                             success = false;
    3465              :                         }
    3466              :                     }
    3467              :                 }
    3468              :                 else
    3469              :                 {
    3470            0 :                     pg_log_error("\\watch: unrecognized parameter \"%s\"", opt);
    3471            0 :                     success = false;
    3472              :                 }
    3473              :             }
    3474              :             else
    3475              :             {
    3476              :                 /* Unlabeled argument: take it as interval */
    3477            8 :                 if (have_sleep)
    3478              :                 {
    3479            1 :                     pg_log_error("\\watch: interval value is specified more than once");
    3480            1 :                     success = false;
    3481              :                 }
    3482              :                 else
    3483              :                 {
    3484            7 :                     have_sleep = true;
    3485            7 :                     errno = 0;
    3486            7 :                     sleep = strtod(opt, &opt_end);
    3487            7 :                     if (sleep < 0 || *opt_end || errno == ERANGE)
    3488              :                     {
    3489            3 :                         pg_log_error("\\watch: incorrect interval value \"%s\"", opt);
    3490            3 :                         success = false;
    3491              :                     }
    3492              :                 }
    3493              :             }
    3494              : 
    3495           20 :             free(opt);
    3496              :         }
    3497              : 
    3498              :         /* If we parsed arguments successfully, do the command */
    3499           17 :         if (success)
    3500              :         {
    3501              :             /* If query_buf is empty, recall and execute previous query */
    3502            7 :             (void) copy_previous_query(query_buf, previous_buf);
    3503              : 
    3504            7 :             success = do_watch(query_buf, sleep, iter, min_rows);
    3505              :         }
    3506              : 
    3507              :         /* Reset the query buffer as though for \r */
    3508           15 :         resetPQExpBuffer(query_buf);
    3509           15 :         psql_scan_reset(scan_state);
    3510              :     }
    3511              :     else
    3512            3 :         ignore_slash_options(scan_state);
    3513              : 
    3514           18 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    3515              : }
    3516              : 
    3517              : /*
    3518              :  * \x -- set or toggle expanded table representation
    3519              :  */
    3520              : static backslashResult
    3521           38 : exec_command_x(PsqlScanState scan_state, bool active_branch)
    3522              : {
    3523           38 :     bool        success = true;
    3524              : 
    3525           38 :     if (active_branch)
    3526              :     {
    3527           35 :         char       *opt = psql_scan_slash_option(scan_state,
    3528              :                                                  OT_NORMAL, NULL, true);
    3529              : 
    3530           35 :         success = do_pset("expanded", opt, &pset.popt, pset.quiet);
    3531           35 :         free(opt);
    3532              :     }
    3533              :     else
    3534            3 :         ignore_slash_options(scan_state);
    3535              : 
    3536           38 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    3537              : }
    3538              : 
    3539              : /*
    3540              :  * \z -- list table privileges (equivalent to \dp)
    3541              :  */
    3542              : static backslashResult
    3543           15 : exec_command_z(PsqlScanState scan_state, bool active_branch, const char *cmd)
    3544              : {
    3545           15 :     bool        success = true;
    3546              : 
    3547           15 :     if (active_branch)
    3548              :     {
    3549              :         char       *pattern;
    3550              :         bool        show_system;
    3551              :         unsigned short int save_expanded;
    3552              : 
    3553           12 :         pattern = psql_scan_slash_option(scan_state,
    3554              :                                          OT_NORMAL, NULL, true);
    3555              : 
    3556           12 :         show_system = strchr(cmd, 'S') ? true : false;
    3557              : 
    3558              :         /* if 'x' option specified, force expanded mode */
    3559           12 :         save_expanded = pset.popt.topt.expanded;
    3560           12 :         if (strchr(cmd, 'x'))
    3561            3 :             pset.popt.topt.expanded = 1;
    3562              : 
    3563           12 :         success = permissionsList(pattern, show_system);
    3564              : 
    3565              :         /* restore original expanded mode */
    3566           12 :         pset.popt.topt.expanded = save_expanded;
    3567              : 
    3568           12 :         free(pattern);
    3569              :     }
    3570              :     else
    3571            3 :         ignore_slash_options(scan_state);
    3572              : 
    3573           15 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    3574              : }
    3575              : 
    3576              : /*
    3577              :  * \! -- execute shell command
    3578              :  */
    3579              : static backslashResult
    3580            3 : exec_command_shell_escape(PsqlScanState scan_state, bool active_branch)
    3581              : {
    3582            3 :     bool        success = true;
    3583              : 
    3584            3 :     if (active_branch)
    3585              :     {
    3586            0 :         char       *opt = psql_scan_slash_option(scan_state,
    3587              :                                                  OT_WHOLE_LINE, NULL, false);
    3588              : 
    3589            0 :         success = do_shell(opt);
    3590            0 :         free(opt);
    3591              :     }
    3592              :     else
    3593            3 :         ignore_slash_whole_line(scan_state);
    3594              : 
    3595            3 :     return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
    3596              : }
    3597              : 
    3598              : /*
    3599              :  * \? -- print help about backslash commands
    3600              :  */
    3601              : static backslashResult
    3602            3 : exec_command_slash_command_help(PsqlScanState scan_state, bool active_branch)
    3603              : {
    3604            3 :     if (active_branch)
    3605              :     {
    3606            0 :         char       *opt0 = psql_scan_slash_option(scan_state,
    3607              :                                                   OT_NORMAL, NULL, false);
    3608              : 
    3609            0 :         if (!opt0 || strcmp(opt0, "commands") == 0)
    3610            0 :             slashUsage(pset.popt.topt.pager);
    3611            0 :         else if (strcmp(opt0, "options") == 0)
    3612            0 :             usage(pset.popt.topt.pager);
    3613            0 :         else if (strcmp(opt0, "variables") == 0)
    3614            0 :             helpVariables(pset.popt.topt.pager);
    3615              :         else
    3616            0 :             slashUsage(pset.popt.topt.pager);
    3617              : 
    3618            0 :         free(opt0);
    3619              :     }
    3620              :     else
    3621            3 :         ignore_slash_options(scan_state);
    3622              : 
    3623            3 :     return PSQL_CMD_SKIP_LINE;
    3624              : }
    3625              : 
    3626              : 
    3627              : /*
    3628              :  * Read and interpret an argument to the \connect slash command.
    3629              :  *
    3630              :  * Returns a malloc'd string, or NULL if no/empty argument.
    3631              :  */
    3632              : static char *
    3633          735 : read_connect_arg(PsqlScanState scan_state)
    3634              : {
    3635              :     char       *result;
    3636              :     char        quote;
    3637              : 
    3638              :     /*
    3639              :      * Ideally we should treat the arguments as SQL identifiers.  But for
    3640              :      * backwards compatibility with 7.2 and older pg_dump files, we have to
    3641              :      * take unquoted arguments verbatim (don't downcase them). For now,
    3642              :      * double-quoted arguments may be stripped of double quotes (as if SQL
    3643              :      * identifiers).  By 7.4 or so, pg_dump files can be expected to
    3644              :      * double-quote all mixed-case \connect arguments, and then we can get rid
    3645              :      * of OT_SQLIDHACK.
    3646              :      */
    3647          735 :     result = psql_scan_slash_option(scan_state, OT_SQLIDHACK, &quote, true);
    3648              : 
    3649          735 :     if (!result)
    3650          588 :         return NULL;
    3651              : 
    3652          147 :     if (quote)
    3653           12 :         return result;
    3654              : 
    3655          135 :     if (*result == '\0' || strcmp(result, "-") == 0)
    3656              :     {
    3657          109 :         free(result);
    3658          109 :         return NULL;
    3659              :     }
    3660              : 
    3661           26 :     return result;
    3662              : }
    3663              : 
    3664              : /*
    3665              :  * Read a boolean expression, return it as a PQExpBuffer string.
    3666              :  *
    3667              :  * Note: anything more or less than one token will certainly fail to be
    3668              :  * parsed by ParseVariableBool, so we don't worry about complaining here.
    3669              :  * This routine's return data structure will need to be rethought anyway
    3670              :  * to support likely future extensions such as "\if defined VARNAME".
    3671              :  */
    3672              : static PQExpBuffer
    3673          128 : gather_boolean_expression(PsqlScanState scan_state)
    3674              : {
    3675          128 :     PQExpBuffer exp_buf = createPQExpBuffer();
    3676          128 :     int         num_options = 0;
    3677              :     char       *value;
    3678              : 
    3679              :     /* collect all arguments for the conditional command into exp_buf */
    3680          262 :     while ((value = psql_scan_slash_option(scan_state,
    3681          262 :                                            OT_NORMAL, NULL, false)) != NULL)
    3682              :     {
    3683              :         /* add spaces between tokens */
    3684          134 :         if (num_options > 0)
    3685            6 :             appendPQExpBufferChar(exp_buf, ' ');
    3686          134 :         appendPQExpBufferStr(exp_buf, value);
    3687          134 :         num_options++;
    3688          134 :         free(value);
    3689              :     }
    3690              : 
    3691          128 :     return exp_buf;
    3692              : }
    3693              : 
    3694              : /*
    3695              :  * Read a boolean expression, return true if the expression
    3696              :  * was a valid boolean expression that evaluated to true.
    3697              :  * Otherwise return false.
    3698              :  *
    3699              :  * Note: conditional stack's top state must be active, else lexer will
    3700              :  * fail to expand variables and backticks.
    3701              :  */
    3702              : static bool
    3703          119 : is_true_boolean_expression(PsqlScanState scan_state, const char *name)
    3704              : {
    3705          119 :     PQExpBuffer buf = gather_boolean_expression(scan_state);
    3706          119 :     bool        value = false;
    3707          119 :     bool        success = ParseVariableBool(buf->data, name, &value);
    3708              : 
    3709          119 :     destroyPQExpBuffer(buf);
    3710          119 :     return success && value;
    3711              : }
    3712              : 
    3713              : /*
    3714              :  * Read a boolean expression, but do nothing with it.
    3715              :  *
    3716              :  * Note: conditional stack's top state must be INACTIVE, else lexer will
    3717              :  * expand variables and backticks, which we do not want here.
    3718              :  */
    3719              : static void
    3720            9 : ignore_boolean_expression(PsqlScanState scan_state)
    3721              : {
    3722            9 :     PQExpBuffer buf = gather_boolean_expression(scan_state);
    3723              : 
    3724            9 :     destroyPQExpBuffer(buf);
    3725            9 : }
    3726              : 
    3727              : /*
    3728              :  * Read and discard "normal" slash command options.
    3729              :  *
    3730              :  * This should be used for inactive-branch processing of any slash command
    3731              :  * that eats one or more OT_NORMAL, OT_SQLID, or OT_SQLIDHACK parameters.
    3732              :  * We don't need to worry about exactly how many it would eat, since the
    3733              :  * cleanup logic in HandleSlashCmds would silently discard any extras anyway.
    3734              :  */
    3735              : static void
    3736          195 : ignore_slash_options(PsqlScanState scan_state)
    3737              : {
    3738              :     char       *arg;
    3739              : 
    3740          435 :     while ((arg = psql_scan_slash_option(scan_state,
    3741          435 :                                          OT_NORMAL, NULL, false)) != NULL)
    3742          240 :         free(arg);
    3743          195 : }
    3744              : 
    3745              : /*
    3746              :  * Read and discard FILEPIPE slash command argument.
    3747              :  *
    3748              :  * This *MUST* be used for inactive-branch processing of any slash command
    3749              :  * that takes an OT_FILEPIPE option.  Otherwise we might consume a different
    3750              :  * amount of option text in active and inactive cases.
    3751              :  */
    3752              : static void
    3753            9 : ignore_slash_filepipe(PsqlScanState scan_state)
    3754              : {
    3755            9 :     char       *arg = psql_scan_slash_option(scan_state,
    3756              :                                              OT_FILEPIPE, NULL, false);
    3757              : 
    3758            9 :     free(arg);
    3759            9 : }
    3760              : 
    3761              : /*
    3762              :  * Read and discard whole-line slash command argument.
    3763              :  *
    3764              :  * This *MUST* be used for inactive-branch processing of any slash command
    3765              :  * that takes an OT_WHOLE_LINE option.  Otherwise we might consume a different
    3766              :  * amount of option text in active and inactive cases.
    3767              :  *
    3768              :  * Note: although callers might pass "semicolon" as either true or false,
    3769              :  * we need not duplicate that here, since it doesn't affect the amount of
    3770              :  * input text consumed.
    3771              :  */
    3772              : static void
    3773           21 : ignore_slash_whole_line(PsqlScanState scan_state)
    3774              : {
    3775           21 :     char       *arg = psql_scan_slash_option(scan_state,
    3776              :                                              OT_WHOLE_LINE, NULL, false);
    3777              : 
    3778           21 :     free(arg);
    3779           21 : }
    3780              : 
    3781              : /*
    3782              :  * Return true if the command given is a branching command.
    3783              :  */
    3784              : static bool
    3785            0 : is_branching_command(const char *cmd)
    3786              : {
    3787            0 :     return (strcmp(cmd, "if") == 0 ||
    3788            0 :             strcmp(cmd, "elif") == 0 ||
    3789            0 :             strcmp(cmd, "else") == 0 ||
    3790            0 :             strcmp(cmd, "endif") == 0);
    3791              : }
    3792              : 
    3793              : /*
    3794              :  * Prepare to possibly restore query buffer to its current state
    3795              :  * (cf. discard_query_text).
    3796              :  *
    3797              :  * We need to remember the length of the query buffer, and the lexer's
    3798              :  * notion of the parenthesis nesting depth.
    3799              :  */
    3800              : static void
    3801          143 : save_query_text_state(PsqlScanState scan_state, ConditionalStack cstack,
    3802              :                       PQExpBuffer query_buf)
    3803              : {
    3804          143 :     if (query_buf)
    3805          143 :         conditional_stack_set_query_len(cstack, query_buf->len);
    3806          143 :     conditional_stack_set_paren_depth(cstack,
    3807              :                                       psql_scan_get_paren_depth(scan_state));
    3808          143 : }
    3809              : 
    3810              : /*
    3811              :  * Discard any query text absorbed during an inactive conditional branch.
    3812              :  *
    3813              :  * We must discard data that was appended to query_buf during an inactive
    3814              :  * \if branch.  We don't have to do anything there if there's no query_buf.
    3815              :  *
    3816              :  * Also, reset the lexer state to the same paren depth there was before.
    3817              :  * (The rest of its state doesn't need attention, since we could not be
    3818              :  * inside a comment or literal or partial token.)
    3819              :  */
    3820              : static void
    3821          121 : discard_query_text(PsqlScanState scan_state, ConditionalStack cstack,
    3822              :                    PQExpBuffer query_buf)
    3823              : {
    3824          121 :     if (query_buf)
    3825              :     {
    3826          121 :         int         new_len = conditional_stack_get_query_len(cstack);
    3827              : 
    3828              :         Assert(new_len >= 0 && new_len <= query_buf->len);
    3829          121 :         query_buf->len = new_len;
    3830          121 :         query_buf->data[new_len] = '\0';
    3831              :     }
    3832          121 :     psql_scan_set_paren_depth(scan_state,
    3833              :                               conditional_stack_get_paren_depth(cstack));
    3834          121 : }
    3835              : 
    3836              : /*
    3837              :  * If query_buf is empty, copy previous_buf into it.
    3838              :  *
    3839              :  * This is used by various slash commands for which re-execution of a
    3840              :  * previous query is a common usage.  For convenience, we allow the
    3841              :  * case of query_buf == NULL (and do nothing).
    3842              :  *
    3843              :  * Returns "true" if the previous query was copied into the query
    3844              :  * buffer, else "false".
    3845              :  */
    3846              : static bool
    3847         1509 : copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf)
    3848              : {
    3849         1509 :     if (query_buf && query_buf->len == 0)
    3850              :     {
    3851          586 :         appendPQExpBufferStr(query_buf, previous_buf->data);
    3852          586 :         return true;
    3853              :     }
    3854          923 :     return false;
    3855              : }
    3856              : 
    3857              : /*
    3858              :  * Ask the user for a password; 'username' is the username the
    3859              :  * password is for, if one has been explicitly specified.
    3860              :  * Returns a malloc'd string.
    3861              :  * If 'canceled' is provided, *canceled will be set to true if the prompt
    3862              :  * is canceled via SIGINT, and to false otherwise.
    3863              :  */
    3864              : static char *
    3865            0 : prompt_for_password(const char *username, bool *canceled)
    3866              : {
    3867              :     char       *result;
    3868              :     PromptInterruptContext prompt_ctx;
    3869              : 
    3870              :     /* Set up to let SIGINT cancel simple_prompt_extended() */
    3871            0 :     prompt_ctx.jmpbuf = sigint_interrupt_jmp;
    3872            0 :     prompt_ctx.enabled = &sigint_interrupt_enabled;
    3873            0 :     prompt_ctx.canceled = false;
    3874              : 
    3875            0 :     if (username == NULL || username[0] == '\0')
    3876            0 :         result = simple_prompt_extended("Password: ", false, &prompt_ctx);
    3877              :     else
    3878              :     {
    3879              :         char       *prompt_text;
    3880              : 
    3881            0 :         prompt_text = psprintf(_("Password for user %s: "), username);
    3882            0 :         result = simple_prompt_extended(prompt_text, false, &prompt_ctx);
    3883            0 :         free(prompt_text);
    3884              :     }
    3885              : 
    3886            0 :     if (canceled)
    3887            0 :         *canceled = prompt_ctx.canceled;
    3888              : 
    3889            0 :     return result;
    3890              : }
    3891              : 
    3892              : static bool
    3893           24 : param_is_newly_set(const char *old_val, const char *new_val)
    3894              : {
    3895           24 :     if (new_val == NULL)
    3896            0 :         return false;
    3897              : 
    3898           24 :     if (old_val == NULL || strcmp(old_val, new_val) != 0)
    3899            0 :         return true;
    3900              : 
    3901           24 :     return false;
    3902              : }
    3903              : 
    3904              : /*
    3905              :  * do_connect -- handler for \connect
    3906              :  *
    3907              :  * Connects to a database with given parameters.  If we are told to re-use
    3908              :  * parameters, parameters from the previous connection are used where the
    3909              :  * command's own options do not supply a value.  Otherwise, libpq defaults
    3910              :  * are used.
    3911              :  *
    3912              :  * In interactive mode, if connection fails with the given parameters,
    3913              :  * the old connection will be kept.
    3914              :  */
    3915              : static bool
    3916          181 : do_connect(enum trivalue reuse_previous_specification,
    3917              :            char *dbname, char *user, char *host, char *port)
    3918              : {
    3919          181 :     PGconn     *o_conn = pset.db,
    3920          181 :                *n_conn = NULL;
    3921              :     PQconninfoOption *cinfo;
    3922          181 :     int         nconnopts = 0;
    3923          181 :     bool        same_host = false;
    3924          181 :     char       *password = NULL;
    3925              :     char       *client_encoding;
    3926          181 :     bool        success = true;
    3927          181 :     bool        keep_password = true;
    3928              :     bool        has_connection_string;
    3929              :     bool        reuse_previous;
    3930              : 
    3931          181 :     has_connection_string = dbname ?
    3932          181 :         recognized_connection_string(dbname) : false;
    3933              : 
    3934              :     /* Complain if we have additional arguments after a connection string. */
    3935          181 :     if (has_connection_string && (user || host || port))
    3936              :     {
    3937            0 :         pg_log_error("Do not give user, host, or port separately when using a connection string");
    3938            0 :         return false;
    3939              :     }
    3940              : 
    3941          181 :     switch (reuse_previous_specification)
    3942              :     {
    3943           11 :         case TRI_YES:
    3944           11 :             reuse_previous = true;
    3945           11 :             break;
    3946            0 :         case TRI_NO:
    3947            0 :             reuse_previous = false;
    3948            0 :             break;
    3949          170 :         default:
    3950          170 :             reuse_previous = !has_connection_string;
    3951          170 :             break;
    3952              :     }
    3953              : 
    3954              :     /*
    3955              :      * If we intend to re-use connection parameters, collect them out of the
    3956              :      * old connection, then replace individual values as necessary.  (We may
    3957              :      * need to resort to looking at pset.dead_conn, if the connection died
    3958              :      * previously.)  Otherwise, obtain a PQconninfoOption array containing
    3959              :      * libpq's defaults, and modify that.  Note this function assumes that
    3960              :      * PQconninfo, PQconndefaults, and PQconninfoParse will all produce arrays
    3961              :      * containing the same options in the same order.
    3962              :      */
    3963          181 :     if (reuse_previous)
    3964              :     {
    3965          181 :         if (o_conn)
    3966          181 :             cinfo = PQconninfo(o_conn);
    3967            0 :         else if (pset.dead_conn)
    3968            0 :             cinfo = PQconninfo(pset.dead_conn);
    3969              :         else
    3970              :         {
    3971              :             /* This is reachable after a non-interactive \connect failure */
    3972            0 :             pg_log_error("No database connection exists to re-use parameters from");
    3973            0 :             return false;
    3974              :         }
    3975              :     }
    3976              :     else
    3977            0 :         cinfo = PQconndefaults();
    3978              : 
    3979          181 :     if (cinfo)
    3980              :     {
    3981          181 :         if (has_connection_string)
    3982              :         {
    3983              :             /* Parse the connstring and insert values into cinfo */
    3984              :             PQconninfoOption *replcinfo;
    3985              :             char       *errmsg;
    3986              : 
    3987           11 :             replcinfo = PQconninfoParse(dbname, &errmsg);
    3988           11 :             if (replcinfo)
    3989              :             {
    3990              :                 PQconninfoOption *ci;
    3991              :                 PQconninfoOption *replci;
    3992           11 :                 bool        have_password = false;
    3993              : 
    3994           11 :                 for (ci = cinfo, replci = replcinfo;
    3995          572 :                      ci->keyword && replci->keyword;
    3996          561 :                      ci++, replci++)
    3997              :                 {
    3998              :                     Assert(strcmp(ci->keyword, replci->keyword) == 0);
    3999              :                     /* Insert value from connstring if one was provided */
    4000          561 :                     if (replci->val)
    4001              :                     {
    4002              :                         /*
    4003              :                          * We know that both val strings were allocated by
    4004              :                          * libpq, so the least messy way to avoid memory leaks
    4005              :                          * is to swap them.
    4006              :                          */
    4007           14 :                         char       *swap = replci->val;
    4008              : 
    4009           14 :                         replci->val = ci->val;
    4010           14 :                         ci->val = swap;
    4011              : 
    4012              :                         /*
    4013              :                          * Check whether connstring provides options affecting
    4014              :                          * password re-use.  While any change in user, host,
    4015              :                          * hostaddr, or port causes us to ignore the old
    4016              :                          * connection's password, we don't force that for
    4017              :                          * dbname, since passwords aren't database-specific.
    4018              :                          */
    4019           14 :                         if (replci->val == NULL ||
    4020           14 :                             strcmp(ci->val, replci->val) != 0)
    4021              :                         {
    4022           14 :                             if (strcmp(replci->keyword, "user") == 0 ||
    4023           11 :                                 strcmp(replci->keyword, "host") == 0 ||
    4024           11 :                                 strcmp(replci->keyword, "hostaddr") == 0 ||
    4025           11 :                                 strcmp(replci->keyword, "port") == 0)
    4026            3 :                                 keep_password = false;
    4027              :                         }
    4028              :                         /* Also note whether connstring contains a password. */
    4029           14 :                         if (strcmp(replci->keyword, "password") == 0)
    4030            0 :                             have_password = true;
    4031              :                     }
    4032          547 :                     else if (!reuse_previous)
    4033              :                     {
    4034              :                         /*
    4035              :                          * When we have a connstring and are not re-using
    4036              :                          * parameters, swap *all* entries, even those not set
    4037              :                          * by the connstring.  This avoids absorbing
    4038              :                          * environment-dependent defaults from the result of
    4039              :                          * PQconndefaults().  We don't want to do that because
    4040              :                          * they'd override service-file entries if the
    4041              :                          * connstring specifies a service parameter, whereas
    4042              :                          * the priority should be the other way around.  libpq
    4043              :                          * can certainly recompute any defaults we don't pass
    4044              :                          * here.  (In this situation, it's a bit wasteful to
    4045              :                          * have called PQconndefaults() at all, but not doing
    4046              :                          * so would require yet another major code path here.)
    4047              :                          */
    4048            0 :                         replci->val = ci->val;
    4049            0 :                         ci->val = NULL;
    4050              :                     }
    4051              :                 }
    4052              :                 Assert(ci->keyword == NULL && replci->keyword == NULL);
    4053              : 
    4054              :                 /* While here, determine how many option slots there are */
    4055           11 :                 nconnopts = ci - cinfo;
    4056              : 
    4057           11 :                 PQconninfoFree(replcinfo);
    4058              : 
    4059              :                 /*
    4060              :                  * If the connstring contains a password, tell the loop below
    4061              :                  * that we may use it, regardless of other settings (i.e.,
    4062              :                  * cinfo's password is no longer an "old" password).
    4063              :                  */
    4064           11 :                 if (have_password)
    4065            0 :                     keep_password = true;
    4066              : 
    4067              :                 /* Don't let code below try to inject dbname into params. */
    4068           11 :                 dbname = NULL;
    4069              :             }
    4070              :             else
    4071              :             {
    4072              :                 /* PQconninfoParse failed */
    4073            0 :                 if (errmsg)
    4074              :                 {
    4075            0 :                     pg_log_error("%s", errmsg);
    4076            0 :                     PQfreemem(errmsg);
    4077              :                 }
    4078              :                 else
    4079            0 :                     pg_log_error("out of memory");
    4080            0 :                 success = false;
    4081              :             }
    4082              :         }
    4083              :         else
    4084              :         {
    4085              :             /*
    4086              :              * If dbname isn't a connection string, then we'll inject it and
    4087              :              * the other parameters into the keyword array below.  (We can't
    4088              :              * easily insert them into the cinfo array because of memory
    4089              :              * management issues: PQconninfoFree would misbehave on Windows.)
    4090              :              * However, to avoid dependencies on the order in which parameters
    4091              :              * appear in the array, make a preliminary scan to set
    4092              :              * keep_password and same_host correctly.
    4093              :              *
    4094              :              * While any change in user, host, or port causes us to ignore the
    4095              :              * old connection's password, we don't force that for dbname,
    4096              :              * since passwords aren't database-specific.
    4097              :              */
    4098              :             PQconninfoOption *ci;
    4099              : 
    4100         8840 :             for (ci = cinfo; ci->keyword; ci++)
    4101              :             {
    4102         8670 :                 if (user && strcmp(ci->keyword, "user") == 0)
    4103              :                 {
    4104            4 :                     if (!(ci->val && strcmp(user, ci->val) == 0))
    4105            4 :                         keep_password = false;
    4106              :                 }
    4107         8666 :                 else if (host && strcmp(ci->keyword, "host") == 0)
    4108              :                 {
    4109            0 :                     if (ci->val && strcmp(host, ci->val) == 0)
    4110            0 :                         same_host = true;
    4111              :                     else
    4112            0 :                         keep_password = false;
    4113              :                 }
    4114         8666 :                 else if (port && strcmp(ci->keyword, "port") == 0)
    4115              :                 {
    4116            0 :                     if (!(ci->val && strcmp(port, ci->val) == 0))
    4117            0 :                         keep_password = false;
    4118              :                 }
    4119              :             }
    4120              : 
    4121              :             /* While here, determine how many option slots there are */
    4122          170 :             nconnopts = ci - cinfo;
    4123              :         }
    4124              :     }
    4125              :     else
    4126              :     {
    4127              :         /* We failed to create the cinfo structure */
    4128            0 :         pg_log_error("out of memory");
    4129            0 :         success = false;
    4130              :     }
    4131              : 
    4132              :     /*
    4133              :      * If the user asked to be prompted for a password, ask for one now. If
    4134              :      * not, use the password from the old connection, provided the username
    4135              :      * etc have not changed. Otherwise, try to connect without a password
    4136              :      * first, and then ask for a password if needed.
    4137              :      *
    4138              :      * XXX: this behavior leads to spurious connection attempts recorded in
    4139              :      * the postmaster's log.  But libpq offers no API that would let us obtain
    4140              :      * a password and then continue with the first connection attempt.
    4141              :      */
    4142          181 :     if (pset.getPassword == TRI_YES && success)
    4143              :     {
    4144            0 :         bool        canceled = false;
    4145              : 
    4146              :         /*
    4147              :          * If a connstring or URI is provided, we don't know which username
    4148              :          * will be used, since we haven't dug that out of the connstring.
    4149              :          * Don't risk issuing a misleading prompt.  As in startup.c, it does
    4150              :          * not seem worth working harder, since this getPassword setting is
    4151              :          * normally only used in noninteractive cases.
    4152              :          */
    4153            0 :         password = prompt_for_password(has_connection_string ? NULL : user,
    4154              :                                        &canceled);
    4155            0 :         success = !canceled;
    4156              :     }
    4157              : 
    4158              :     /*
    4159              :      * Consider whether to force client_encoding to "auto" (overriding
    4160              :      * anything in the connection string).  We do so if we have a terminal
    4161              :      * connection and there is no PGCLIENTENCODING environment setting.
    4162              :      */
    4163          181 :     if (pset.notty || getenv("PGCLIENTENCODING"))
    4164          181 :         client_encoding = NULL;
    4165              :     else
    4166            0 :         client_encoding = "auto";
    4167              : 
    4168              :     /* Loop till we have a connection or fail, which we might've already */
    4169          181 :     while (success)
    4170              :     {
    4171          181 :         const char **keywords = pg_malloc_array(const char *, nconnopts + 1);
    4172          181 :         const char **values = pg_malloc_array(const char *, nconnopts + 1);
    4173          181 :         int         paramnum = 0;
    4174              :         PQconninfoOption *ci;
    4175              : 
    4176              :         /*
    4177              :          * Copy non-default settings into the PQconnectdbParams parameter
    4178              :          * arrays; but inject any values specified old-style, as well as any
    4179              :          * interactively-obtained password, and a couple of fields we want to
    4180              :          * set forcibly.
    4181              :          *
    4182              :          * If you change this code, see also the initial-connection code in
    4183              :          * main().
    4184              :          */
    4185         9412 :         for (ci = cinfo; ci->keyword; ci++)
    4186              :         {
    4187         9231 :             keywords[paramnum] = ci->keyword;
    4188              : 
    4189         9231 :             if (dbname && strcmp(ci->keyword, "dbname") == 0)
    4190           12 :                 values[paramnum++] = dbname;
    4191         9219 :             else if (user && strcmp(ci->keyword, "user") == 0)
    4192            4 :                 values[paramnum++] = user;
    4193         9215 :             else if (host && strcmp(ci->keyword, "host") == 0)
    4194            0 :                 values[paramnum++] = host;
    4195         9215 :             else if (host && !same_host && strcmp(ci->keyword, "hostaddr") == 0)
    4196              :             {
    4197              :                 /* If we're changing the host value, drop any old hostaddr */
    4198            0 :                 values[paramnum++] = NULL;
    4199              :             }
    4200         9215 :             else if (port && strcmp(ci->keyword, "port") == 0)
    4201            0 :                 values[paramnum++] = port;
    4202              :             /* If !keep_password, we unconditionally drop old password */
    4203         9215 :             else if ((password || !keep_password) &&
    4204          353 :                      strcmp(ci->keyword, "password") == 0)
    4205            7 :                 values[paramnum++] = password;
    4206         9208 :             else if (strcmp(ci->keyword, "fallback_application_name") == 0)
    4207          181 :                 values[paramnum++] = pset.progname;
    4208         9027 :             else if (client_encoding &&
    4209            0 :                      strcmp(ci->keyword, "client_encoding") == 0)
    4210            0 :                 values[paramnum++] = client_encoding;
    4211         9027 :             else if (ci->val)
    4212         3435 :                 values[paramnum++] = ci->val;
    4213              :             /* else, don't bother making libpq parse this keyword */
    4214              :         }
    4215              :         /* add array terminator */
    4216          181 :         keywords[paramnum] = NULL;
    4217          181 :         values[paramnum] = NULL;
    4218              : 
    4219              :         /* Note we do not want libpq to re-expand the dbname parameter */
    4220          181 :         n_conn = PQconnectStartParams(keywords, values, false);
    4221              : 
    4222          181 :         pg_free(keywords);
    4223          181 :         pg_free(values);
    4224              : 
    4225          181 :         wait_until_connected(n_conn);
    4226          181 :         if (PQstatus(n_conn) == CONNECTION_OK)
    4227          181 :             break;
    4228              : 
    4229              :         /*
    4230              :          * Connection attempt failed; either retry the connection attempt with
    4231              :          * a new password, or give up.
    4232              :          */
    4233            0 :         if (!password && PQconnectionNeedsPassword(n_conn) && pset.getPassword != TRI_NO)
    4234            0 :         {
    4235            0 :             bool        canceled = false;
    4236              : 
    4237              :             /*
    4238              :              * Prompt for password using the username we actually connected
    4239              :              * with --- it might've come out of "dbname" rather than "user".
    4240              :              */
    4241            0 :             password = prompt_for_password(PQuser(n_conn), &canceled);
    4242            0 :             PQfinish(n_conn);
    4243            0 :             n_conn = NULL;
    4244            0 :             success = !canceled;
    4245            0 :             continue;
    4246              :         }
    4247              : 
    4248              :         /*
    4249              :          * We'll report the error below ... unless n_conn is NULL, indicating
    4250              :          * that libpq didn't have enough memory to make a PGconn.
    4251              :          */
    4252            0 :         if (n_conn == NULL)
    4253            0 :             pg_log_error("out of memory");
    4254              : 
    4255            0 :         success = false;
    4256              :     }                           /* end retry loop */
    4257              : 
    4258              :     /* Release locally allocated data, whether we succeeded or not */
    4259          181 :     pg_free(password);
    4260          181 :     PQconninfoFree(cinfo);
    4261              : 
    4262          181 :     if (!success)
    4263              :     {
    4264              :         /*
    4265              :          * Failed to connect to the database. In interactive mode, keep the
    4266              :          * previous connection to the DB; in scripting mode, close our
    4267              :          * previous connection as well.
    4268              :          */
    4269            0 :         if (pset.cur_cmd_interactive)
    4270              :         {
    4271            0 :             if (n_conn)
    4272              :             {
    4273            0 :                 pg_log_info("%s", PQerrorMessage(n_conn));
    4274            0 :                 PQfinish(n_conn);
    4275              :             }
    4276              : 
    4277              :             /* pset.db is left unmodified */
    4278            0 :             if (o_conn)
    4279            0 :                 pg_log_info("Previous connection kept");
    4280              :         }
    4281              :         else
    4282              :         {
    4283            0 :             if (n_conn)
    4284              :             {
    4285            0 :                 pg_log_error("\\connect: %s", PQerrorMessage(n_conn));
    4286            0 :                 PQfinish(n_conn);
    4287              :             }
    4288              : 
    4289            0 :             if (o_conn)
    4290              :             {
    4291              :                 /*
    4292              :                  * Transition to having no connection.
    4293              :                  *
    4294              :                  * Unlike CheckConnection(), we close the old connection
    4295              :                  * immediately to prevent its parameters from being re-used.
    4296              :                  * This is so that a script cannot accidentally reuse
    4297              :                  * parameters it did not expect to.  Otherwise, the state
    4298              :                  * cleanup should be the same as in CheckConnection().
    4299              :                  */
    4300            0 :                 PQfinish(o_conn);
    4301            0 :                 pset.db = NULL;
    4302            0 :                 ResetCancelConn();
    4303            0 :                 UnsyncVariables();
    4304              :             }
    4305              : 
    4306              :             /* On the same reasoning, release any dead_conn to prevent reuse */
    4307            0 :             if (pset.dead_conn)
    4308              :             {
    4309            0 :                 PQfinish(pset.dead_conn);
    4310            0 :                 pset.dead_conn = NULL;
    4311              :             }
    4312              :         }
    4313              : 
    4314            0 :         return false;
    4315              :     }
    4316              : 
    4317              :     /*
    4318              :      * Replace the old connection with the new one, and update
    4319              :      * connection-dependent variables.  Keep the resynchronization logic in
    4320              :      * sync with CheckConnection().
    4321              :      */
    4322          181 :     PQsetNoticeProcessor(n_conn, NoticeProcessor, NULL);
    4323          181 :     pset.db = n_conn;
    4324          181 :     SyncVariables();
    4325          181 :     connection_warnings(false); /* Must be after SyncVariables */
    4326              : 
    4327              :     /* Tell the user about the new connection */
    4328          181 :     if (!pset.quiet)
    4329              :     {
    4330           24 :         if (!o_conn ||
    4331           24 :             param_is_newly_set(PQhost(o_conn), PQhost(pset.db)) ||
    4332           12 :             param_is_newly_set(PQport(o_conn), PQport(pset.db)))
    4333            0 :         {
    4334            0 :             char       *connhost = PQhost(pset.db);
    4335            0 :             char       *hostaddr = PQhostaddr(pset.db);
    4336              : 
    4337            0 :             if (is_unixsock_path(connhost))
    4338              :             {
    4339              :                 /* hostaddr overrides connhost */
    4340            0 :                 if (hostaddr && *hostaddr)
    4341            0 :                     printf(_("You are now connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
    4342              :                            PQdb(pset.db), PQuser(pset.db), hostaddr, PQport(pset.db));
    4343              :                 else
    4344            0 :                     printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
    4345              :                            PQdb(pset.db), PQuser(pset.db), connhost, PQport(pset.db));
    4346              :             }
    4347              :             else
    4348              :             {
    4349            0 :                 if (hostaddr && *hostaddr && strcmp(connhost, hostaddr) != 0)
    4350            0 :                     printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
    4351              :                            PQdb(pset.db), PQuser(pset.db), connhost, hostaddr, PQport(pset.db));
    4352              :                 else
    4353            0 :                     printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
    4354              :                            PQdb(pset.db), PQuser(pset.db), connhost, PQport(pset.db));
    4355              :             }
    4356              :         }
    4357              :         else
    4358           12 :             printf(_("You are now connected to database \"%s\" as user \"%s\".\n"),
    4359              :                    PQdb(pset.db), PQuser(pset.db));
    4360              :     }
    4361              : 
    4362              :     /* Drop no-longer-needed connection(s) */
    4363          181 :     if (o_conn)
    4364          181 :         PQfinish(o_conn);
    4365          181 :     if (pset.dead_conn)
    4366              :     {
    4367            0 :         PQfinish(pset.dead_conn);
    4368            0 :         pset.dead_conn = NULL;
    4369              :     }
    4370              : 
    4371          181 :     return true;
    4372              : }
    4373              : 
    4374              : /*
    4375              :  * Processes the connection sequence described by PQconnectStartParams(). Don't
    4376              :  * worry about reporting errors in this function. Our caller will check the
    4377              :  * connection's status, and report appropriately.
    4378              :  */
    4379              : static void
    4380          181 : wait_until_connected(PGconn *conn)
    4381              : {
    4382          181 :     bool        forRead = false;
    4383              : 
    4384              :     while (true)
    4385          187 :     {
    4386              :         int         rc;
    4387              :         int         sock;
    4388              :         pg_usec_time_t end_time;
    4389              : 
    4390              :         /*
    4391              :          * On every iteration of the connection sequence, let's check if the
    4392              :          * user has requested a cancellation.
    4393              :          */
    4394          368 :         if (cancel_pressed)
    4395            0 :             break;
    4396              : 
    4397              :         /*
    4398              :          * Do not assume that the socket remains the same across
    4399              :          * PQconnectPoll() calls.
    4400              :          */
    4401          368 :         sock = PQsocket(conn);
    4402          368 :         if (sock == -1)
    4403            0 :             break;
    4404              : 
    4405              :         /*
    4406              :          * If the user sends SIGINT between the cancel_pressed check, and
    4407              :          * polling of the socket, it will not be recognized. Instead, we will
    4408              :          * just wait until the next step in the connection sequence or
    4409              :          * forever, which might require users to send SIGTERM or SIGQUIT.
    4410              :          *
    4411              :          * Some solutions would include the "self-pipe trick," using
    4412              :          * pselect(2) and ppoll(2), or using a timeout.
    4413              :          *
    4414              :          * The self-pipe trick requires a bit of code to setup. pselect(2) and
    4415              :          * ppoll(2) are not on all the platforms we support. The simplest
    4416              :          * solution happens to just be adding a timeout, so let's wait for 1
    4417              :          * second and check cancel_pressed again.
    4418              :          */
    4419          368 :         end_time = PQgetCurrentTimeUSec() + 1000000;
    4420          368 :         rc = PQsocketPoll(sock, forRead, !forRead, end_time);
    4421          368 :         if (rc == -1)
    4422            0 :             return;
    4423              : 
    4424          368 :         switch (PQconnectPoll(conn))
    4425              :         {
    4426          181 :             case PGRES_POLLING_OK:
    4427              :             case PGRES_POLLING_FAILED:
    4428          181 :                 return;
    4429          187 :             case PGRES_POLLING_READING:
    4430          187 :                 forRead = true;
    4431          187 :                 continue;
    4432            0 :             case PGRES_POLLING_WRITING:
    4433            0 :                 forRead = false;
    4434            0 :                 continue;
    4435              :             case PGRES_POLLING_ACTIVE:
    4436              :                 pg_unreachable();
    4437              :         }
    4438              :     }
    4439              : }
    4440              : 
    4441              : void
    4442          184 : connection_warnings(bool in_startup)
    4443              : {
    4444          184 :     if (!pset.quiet && !pset.notty)
    4445              :     {
    4446            3 :         int         client_ver = PG_VERSION_NUM;
    4447              :         char        cverbuf[32];
    4448              :         char        sverbuf[32];
    4449              : 
    4450            3 :         if (pset.sversion != client_ver)
    4451              :         {
    4452              :             const char *server_version;
    4453              : 
    4454              :             /* Try to get full text form, might include "devel" etc */
    4455            0 :             server_version = PQparameterStatus(pset.db, "server_version");
    4456              :             /* Otherwise fall back on pset.sversion */
    4457            0 :             if (!server_version)
    4458              :             {
    4459            0 :                 formatPGVersionNumber(pset.sversion, true,
    4460              :                                       sverbuf, sizeof(sverbuf));
    4461            0 :                 server_version = sverbuf;
    4462              :             }
    4463              : 
    4464            0 :             printf(_("%s (%s, server %s)\n"),
    4465              :                    pset.progname, PG_VERSION, server_version);
    4466              :         }
    4467              :         /* For version match, only print psql banner on startup. */
    4468            3 :         else if (in_startup)
    4469            3 :             printf("%s (%s)\n", pset.progname, PG_VERSION);
    4470              : 
    4471              :         /*
    4472              :          * Warn if server's major version is newer than ours, or if server
    4473              :          * predates our support cutoff (currently 9.2).
    4474              :          */
    4475            3 :         if (pset.sversion / 100 > client_ver / 100 ||
    4476            3 :             pset.sversion < 90200)
    4477            0 :             printf(_("WARNING: %s major version %s, server major version %s.\n"
    4478              :                      "         Some psql features might not work.\n"),
    4479              :                    pset.progname,
    4480              :                    formatPGVersionNumber(client_ver, false,
    4481              :                                          cverbuf, sizeof(cverbuf)),
    4482              :                    formatPGVersionNumber(pset.sversion, false,
    4483              :                                          sverbuf, sizeof(sverbuf)));
    4484              : 
    4485              : #ifdef WIN32
    4486              :         if (in_startup)
    4487              :             checkWin32Codepage();
    4488              : #endif
    4489            3 :         printSSLInfo();
    4490            3 :         printGSSInfo();
    4491              :     }
    4492          184 : }
    4493              : 
    4494              : 
    4495              : /*
    4496              :  * printSSLInfo
    4497              :  *
    4498              :  * Prints information about the current SSL connection, if SSL is in use
    4499              :  */
    4500              : static void
    4501            3 : printSSLInfo(void)
    4502              : {
    4503              :     const char *protocol;
    4504              :     const char *cipher;
    4505              :     const char *compression;
    4506              :     const char *alpn;
    4507              : 
    4508            3 :     if (!PQsslInUse(pset.db))
    4509            3 :         return;                 /* no SSL */
    4510              : 
    4511            0 :     protocol = PQsslAttribute(pset.db, "protocol");
    4512            0 :     cipher = PQsslAttribute(pset.db, "cipher");
    4513            0 :     compression = PQsslAttribute(pset.db, "compression");
    4514            0 :     alpn = PQsslAttribute(pset.db, "alpn");
    4515              : 
    4516            0 :     printf(_("SSL connection (protocol: %s, cipher: %s, compression: %s, ALPN: %s)\n"),
    4517              :            protocol ? protocol : _("unknown"),
    4518              :            cipher ? cipher : _("unknown"),
    4519              :            (compression && strcmp(compression, "off") != 0) ? _("on") : _("off"),
    4520              :            (alpn && alpn[0] != '\0') ? alpn : _("none"));
    4521              : }
    4522              : 
    4523              : /*
    4524              :  * printGSSInfo
    4525              :  *
    4526              :  * Prints information about the current GSSAPI connection, if GSSAPI encryption is in use
    4527              :  */
    4528              : static void
    4529            3 : printGSSInfo(void)
    4530              : {
    4531            3 :     if (!PQgssEncInUse(pset.db))
    4532            3 :         return;                 /* no GSSAPI encryption in use */
    4533              : 
    4534            0 :     printf(_("GSSAPI-encrypted connection\n"));
    4535              : }
    4536              : 
    4537              : 
    4538              : /*
    4539              :  * checkWin32Codepage
    4540              :  *
    4541              :  * Prints a warning when win32 console codepage differs from Windows codepage
    4542              :  */
    4543              : #ifdef WIN32
    4544              : static void
    4545              : checkWin32Codepage(void)
    4546              : {
    4547              :     unsigned int wincp,
    4548              :                 concp;
    4549              : 
    4550              :     wincp = GetACP();
    4551              :     concp = GetConsoleCP();
    4552              :     if (wincp != concp)
    4553              :     {
    4554              :         printf(_("WARNING: Console code page (%u) differs from Windows code page (%u)\n"
    4555              :                  "         8-bit characters might not work correctly. See psql reference\n"
    4556              :                  "         page \"Notes for Windows users\" for details.\n"),
    4557              :                concp, wincp);
    4558              :     }
    4559              : }
    4560              : #endif
    4561              : 
    4562              : 
    4563              : /*
    4564              :  * SyncVariables
    4565              :  *
    4566              :  * Make psql's internal variables agree with connection state upon
    4567              :  * establishing a new connection.
    4568              :  */
    4569              : void
    4570         9988 : SyncVariables(void)
    4571              : {
    4572              :     char        vbuf[32];
    4573              :     const char *server_version;
    4574              :     char       *service_name;
    4575              :     char       *service_file;
    4576              : 
    4577              :     /* get stuff from connection */
    4578         9988 :     pset.encoding = PQclientEncoding(pset.db);
    4579         9988 :     pset.popt.topt.encoding = pset.encoding;
    4580         9988 :     pset.sversion = PQserverVersion(pset.db);
    4581              : 
    4582         9988 :     setFmtEncoding(pset.encoding);
    4583              : 
    4584         9988 :     SetVariable(pset.vars, "DBNAME", PQdb(pset.db));
    4585         9988 :     SetVariable(pset.vars, "USER", PQuser(pset.db));
    4586         9988 :     SetVariable(pset.vars, "HOST", PQhost(pset.db));
    4587         9988 :     SetVariable(pset.vars, "PORT", PQport(pset.db));
    4588         9988 :     SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding));
    4589              : 
    4590         9988 :     service_name = get_conninfo_value("service");
    4591         9988 :     SetVariable(pset.vars, "SERVICE", service_name);
    4592         9988 :     if (service_name)
    4593           17 :         pg_free(service_name);
    4594              : 
    4595         9988 :     service_file = get_conninfo_value("servicefile");
    4596         9988 :     SetVariable(pset.vars, "SERVICEFILE", service_file);
    4597         9988 :     if (service_file)
    4598           17 :         pg_free(service_file);
    4599              : 
    4600              :     /* this bit should match connection_warnings(): */
    4601              :     /* Try to get full text form of version, might include "devel" etc */
    4602         9988 :     server_version = PQparameterStatus(pset.db, "server_version");
    4603              :     /* Otherwise fall back on pset.sversion */
    4604         9988 :     if (!server_version)
    4605              :     {
    4606            0 :         formatPGVersionNumber(pset.sversion, true, vbuf, sizeof(vbuf));
    4607            0 :         server_version = vbuf;
    4608              :     }
    4609         9988 :     SetVariable(pset.vars, "SERVER_VERSION_NAME", server_version);
    4610              : 
    4611         9988 :     snprintf(vbuf, sizeof(vbuf), "%d", pset.sversion);
    4612         9988 :     SetVariable(pset.vars, "SERVER_VERSION_NUM", vbuf);
    4613              : 
    4614              :     /* send stuff to it, too */
    4615         9988 :     PQsetErrorVerbosity(pset.db, pset.verbosity);
    4616         9988 :     PQsetErrorContextVisibility(pset.db, pset.show_context);
    4617         9988 : }
    4618              : 
    4619              : /*
    4620              :  * UnsyncVariables
    4621              :  *
    4622              :  * Clear variables that should be not be set when there is no connection.
    4623              :  */
    4624              : void
    4625            0 : UnsyncVariables(void)
    4626              : {
    4627            0 :     SetVariable(pset.vars, "DBNAME", NULL);
    4628            0 :     SetVariable(pset.vars, "SERVICE", NULL);
    4629            0 :     SetVariable(pset.vars, "SERVICEFILE", NULL);
    4630            0 :     SetVariable(pset.vars, "USER", NULL);
    4631            0 :     SetVariable(pset.vars, "HOST", NULL);
    4632            0 :     SetVariable(pset.vars, "PORT", NULL);
    4633            0 :     SetVariable(pset.vars, "ENCODING", NULL);
    4634            0 :     SetVariable(pset.vars, "SERVER_VERSION_NAME", NULL);
    4635            0 :     SetVariable(pset.vars, "SERVER_VERSION_NUM", NULL);
    4636            0 : }
    4637              : 
    4638              : 
    4639              : /*
    4640              :  * helper for do_edit(): actually invoke the editor
    4641              :  *
    4642              :  * Returns true on success, false if we failed to invoke the editor or
    4643              :  * it returned nonzero status.  (An error message is printed for failed-
    4644              :  * to-invoke cases, but not if the editor returns nonzero status.)
    4645              :  */
    4646              : static bool
    4647            0 : editFile(const char *fname, int lineno)
    4648              : {
    4649              :     const char *editorName;
    4650            0 :     const char *editor_lineno_arg = NULL;
    4651              :     char       *sys;
    4652              :     int         result;
    4653              : 
    4654              :     Assert(fname != NULL);
    4655              : 
    4656              :     /* Find an editor to use */
    4657            0 :     editorName = getenv("PSQL_EDITOR");
    4658            0 :     if (!editorName)
    4659            0 :         editorName = getenv("EDITOR");
    4660            0 :     if (!editorName)
    4661            0 :         editorName = getenv("VISUAL");
    4662            0 :     if (!editorName)
    4663            0 :         editorName = DEFAULT_EDITOR;
    4664              : 
    4665              :     /* Get line number argument, if we need it. */
    4666            0 :     if (lineno > 0)
    4667              :     {
    4668            0 :         editor_lineno_arg = getenv("PSQL_EDITOR_LINENUMBER_ARG");
    4669              : #ifdef DEFAULT_EDITOR_LINENUMBER_ARG
    4670            0 :         if (!editor_lineno_arg)
    4671            0 :             editor_lineno_arg = DEFAULT_EDITOR_LINENUMBER_ARG;
    4672              : #endif
    4673            0 :         if (!editor_lineno_arg)
    4674              :         {
    4675            0 :             pg_log_error("environment variable PSQL_EDITOR_LINENUMBER_ARG must be set to specify a line number");
    4676            0 :             return false;
    4677              :         }
    4678              :     }
    4679              : 
    4680              :     /*
    4681              :      * On Unix the EDITOR value should *not* be quoted, since it might include
    4682              :      * switches, eg, EDITOR="pico -t"; it's up to the user to put quotes in it
    4683              :      * if necessary.  But this policy is not very workable on Windows, due to
    4684              :      * severe brain damage in their command shell plus the fact that standard
    4685              :      * program paths include spaces.
    4686              :      */
    4687              : #ifndef WIN32
    4688            0 :     if (lineno > 0)
    4689            0 :         sys = psprintf("exec %s %s%d '%s'",
    4690              :                        editorName, editor_lineno_arg, lineno, fname);
    4691              :     else
    4692            0 :         sys = psprintf("exec %s '%s'",
    4693              :                        editorName, fname);
    4694              : #else
    4695              :     if (lineno > 0)
    4696              :         sys = psprintf("\"%s\" %s%d \"%s\"",
    4697              :                        editorName, editor_lineno_arg, lineno, fname);
    4698              :     else
    4699              :         sys = psprintf("\"%s\" \"%s\"",
    4700              :                        editorName, fname);
    4701              : #endif
    4702            0 :     fflush(NULL);
    4703            0 :     result = system(sys);
    4704            0 :     if (result == -1)
    4705            0 :         pg_log_error("could not start editor \"%s\"", editorName);
    4706            0 :     else if (result == 127)
    4707            0 :         pg_log_error("could not start /bin/sh");
    4708            0 :     free(sys);
    4709              : 
    4710            0 :     return result == 0;
    4711              : }
    4712              : 
    4713              : 
    4714              : /*
    4715              :  * do_edit -- handler for \e
    4716              :  *
    4717              :  * If you do not specify a filename, the current query buffer will be copied
    4718              :  * into a temporary file.
    4719              :  *
    4720              :  * After this function is done, the resulting file will be copied back into the
    4721              :  * query buffer.  As an exception to this, the query buffer will be emptied
    4722              :  * if the file was not modified (or the editor failed) and the caller passes
    4723              :  * "discard_on_quit" = true.
    4724              :  *
    4725              :  * If "edited" isn't NULL, *edited will be set to true if the query buffer
    4726              :  * is successfully replaced.
    4727              :  */
    4728              : static bool
    4729            0 : do_edit(const char *filename_arg, PQExpBuffer query_buf,
    4730              :         int lineno, bool discard_on_quit, bool *edited)
    4731              : {
    4732              :     char        fnametmp[MAXPGPATH];
    4733            0 :     FILE       *stream = NULL;
    4734              :     const char *fname;
    4735            0 :     bool        error = false;
    4736              :     int         fd;
    4737              :     struct stat before,
    4738              :                 after;
    4739              : 
    4740            0 :     if (filename_arg)
    4741            0 :         fname = filename_arg;
    4742              :     else
    4743              :     {
    4744              :         /* make a temp file to edit */
    4745              : #ifndef WIN32
    4746            0 :         const char *tmpdir = getenv("TMPDIR");
    4747              : 
    4748            0 :         if (!tmpdir)
    4749            0 :             tmpdir = "/tmp";
    4750              : #else
    4751              :         char        tmpdir[MAXPGPATH];
    4752              :         int         ret;
    4753              : 
    4754              :         ret = GetTempPath(MAXPGPATH, tmpdir);
    4755              :         if (ret == 0 || ret > MAXPGPATH)
    4756              :         {
    4757              :             pg_log_error("could not locate temporary directory: %s",
    4758              :                          !ret ? strerror(errno) : "");
    4759              :             return false;
    4760              :         }
    4761              : #endif
    4762              : 
    4763              :         /*
    4764              :          * No canonicalize_path() here. EDIT.EXE run from CMD.EXE prepends the
    4765              :          * current directory to the supplied path unless we use only
    4766              :          * backslashes, so we do that.
    4767              :          */
    4768              : #ifndef WIN32
    4769            0 :         snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
    4770            0 :                  "/", (int) getpid());
    4771              : #else
    4772              :         snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql", tmpdir,
    4773              :                  "" /* trailing separator already present */ , (int) getpid());
    4774              : #endif
    4775              : 
    4776            0 :         fname = (const char *) fnametmp;
    4777              : 
    4778            0 :         fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
    4779            0 :         if (fd != -1)
    4780            0 :             stream = fdopen(fd, "w");
    4781              : 
    4782            0 :         if (fd == -1 || !stream)
    4783              :         {
    4784            0 :             pg_log_error("could not open temporary file \"%s\": %m", fname);
    4785            0 :             error = true;
    4786              :         }
    4787              :         else
    4788              :         {
    4789            0 :             unsigned int ql = query_buf->len;
    4790              : 
    4791              :             /* force newline-termination of what we send to editor */
    4792            0 :             if (ql > 0 && query_buf->data[ql - 1] != '\n')
    4793              :             {
    4794            0 :                 appendPQExpBufferChar(query_buf, '\n');
    4795            0 :                 ql++;
    4796              :             }
    4797              : 
    4798            0 :             if (fwrite(query_buf->data, 1, ql, stream) != ql)
    4799              :             {
    4800            0 :                 pg_log_error("%s: %m", fname);
    4801              : 
    4802            0 :                 if (fclose(stream) != 0)
    4803            0 :                     pg_log_error("%s: %m", fname);
    4804              : 
    4805            0 :                 if (remove(fname) != 0)
    4806            0 :                     pg_log_error("%s: %m", fname);
    4807              : 
    4808            0 :                 error = true;
    4809              :             }
    4810            0 :             else if (fclose(stream) != 0)
    4811              :             {
    4812            0 :                 pg_log_error("%s: %m", fname);
    4813            0 :                 if (remove(fname) != 0)
    4814            0 :                     pg_log_error("%s: %m", fname);
    4815            0 :                 error = true;
    4816              :             }
    4817              :             else
    4818              :             {
    4819              :                 struct utimbuf ut;
    4820              : 
    4821              :                 /*
    4822              :                  * Try to set the file modification time of the temporary file
    4823              :                  * a few seconds in the past.  Otherwise, the low granularity
    4824              :                  * (one second, or even worse on some filesystems) that we can
    4825              :                  * portably measure with stat(2) could lead us to not
    4826              :                  * recognize a modification, if the user typed very quickly.
    4827              :                  *
    4828              :                  * This is a rather unlikely race condition, so don't error
    4829              :                  * out if the utime(2) call fails --- that would make the cure
    4830              :                  * worse than the disease.
    4831              :                  */
    4832            0 :                 ut.modtime = ut.actime = time(NULL) - 2;
    4833            0 :                 (void) utime(fname, &ut);
    4834              :             }
    4835              :         }
    4836              :     }
    4837              : 
    4838            0 :     if (!error && stat(fname, &before) != 0)
    4839              :     {
    4840            0 :         pg_log_error("%s: %m", fname);
    4841            0 :         error = true;
    4842              :     }
    4843              : 
    4844              :     /* call editor */
    4845            0 :     if (!error)
    4846            0 :         error = !editFile(fname, lineno);
    4847              : 
    4848            0 :     if (!error && stat(fname, &after) != 0)
    4849              :     {
    4850            0 :         pg_log_error("%s: %m", fname);
    4851            0 :         error = true;
    4852              :     }
    4853              : 
    4854              :     /* file was edited if the size or modification time has changed */
    4855            0 :     if (!error &&
    4856            0 :         (before.st_size != after.st_size ||
    4857            0 :          before.st_mtime != after.st_mtime))
    4858              :     {
    4859            0 :         stream = fopen(fname, PG_BINARY_R);
    4860            0 :         if (!stream)
    4861              :         {
    4862            0 :             pg_log_error("%s: %m", fname);
    4863            0 :             error = true;
    4864              :         }
    4865              :         else
    4866              :         {
    4867              :             /* read file back into query_buf */
    4868              :             char        line[1024];
    4869              : 
    4870            0 :             resetPQExpBuffer(query_buf);
    4871            0 :             while (fgets(line, sizeof(line), stream) != NULL)
    4872            0 :                 appendPQExpBufferStr(query_buf, line);
    4873              : 
    4874            0 :             if (ferror(stream))
    4875              :             {
    4876            0 :                 pg_log_error("%s: %m", fname);
    4877            0 :                 error = true;
    4878            0 :                 resetPQExpBuffer(query_buf);
    4879              :             }
    4880            0 :             else if (edited)
    4881              :             {
    4882            0 :                 *edited = true;
    4883              :             }
    4884              : 
    4885            0 :             fclose(stream);
    4886              :         }
    4887              :     }
    4888              :     else
    4889              :     {
    4890              :         /*
    4891              :          * If the file was not modified, and the caller requested it, discard
    4892              :          * the query buffer.
    4893              :          */
    4894            0 :         if (discard_on_quit)
    4895            0 :             resetPQExpBuffer(query_buf);
    4896              :     }
    4897              : 
    4898              :     /* remove temp file */
    4899            0 :     if (!filename_arg)
    4900              :     {
    4901            0 :         if (remove(fname) == -1)
    4902              :         {
    4903            0 :             pg_log_error("%s: %m", fname);
    4904            0 :             error = true;
    4905              :         }
    4906              :     }
    4907              : 
    4908            0 :     return !error;
    4909              : }
    4910              : 
    4911              : 
    4912              : 
    4913              : /*
    4914              :  * process_file
    4915              :  *
    4916              :  * Reads commands from filename and passes them to the main processing loop.
    4917              :  * Handler for \i and \ir, but can be used for other things as well.  Returns
    4918              :  * MainLoop() error code.
    4919              :  *
    4920              :  * If use_relative_path is true and filename is not an absolute path, then open
    4921              :  * the file from where the currently processed file (if any) is located.
    4922              :  */
    4923              : int
    4924         9537 : process_file(char *filename, bool use_relative_path)
    4925              : {
    4926              :     FILE       *fd;
    4927              :     int         result;
    4928              :     char       *oldfilename;
    4929              :     char        relpath[MAXPGPATH];
    4930              : 
    4931         9537 :     if (!filename)
    4932              :     {
    4933         2623 :         fd = stdin;
    4934         2623 :         filename = NULL;
    4935              :     }
    4936         6914 :     else if (strcmp(filename, "-") != 0)
    4937              :     {
    4938           21 :         canonicalize_path_enc(filename, pset.encoding);
    4939              : 
    4940              :         /*
    4941              :          * If we were asked to resolve the pathname relative to the location
    4942              :          * of the currently executing script, and there is one, and this is a
    4943              :          * relative pathname, then prepend all but the last pathname component
    4944              :          * of the current script to this pathname.
    4945              :          */
    4946           21 :         if (use_relative_path && pset.inputfile &&
    4947            0 :             !is_absolute_path(filename) && !has_drive_prefix(filename))
    4948              :         {
    4949            0 :             strlcpy(relpath, pset.inputfile, sizeof(relpath));
    4950            0 :             get_parent_directory(relpath);
    4951            0 :             join_path_components(relpath, relpath, filename);
    4952            0 :             canonicalize_path_enc(relpath, pset.encoding);
    4953              : 
    4954            0 :             filename = relpath;
    4955              :         }
    4956              : 
    4957           21 :         fd = fopen(filename, PG_BINARY_R);
    4958              : 
    4959           21 :         if (!fd)
    4960              :         {
    4961            0 :             pg_log_error("%s: %m", filename);
    4962            0 :             return EXIT_FAILURE;
    4963              :         }
    4964              :     }
    4965              :     else
    4966              :     {
    4967         6893 :         fd = stdin;
    4968         6893 :         filename = "<stdin>";   /* for future error messages */
    4969              :     }
    4970              : 
    4971         9537 :     oldfilename = pset.inputfile;
    4972         9537 :     pset.inputfile = filename;
    4973              : 
    4974         9537 :     pg_logging_config(pset.inputfile ? 0 : PG_LOG_FLAG_TERSE);
    4975              : 
    4976         9537 :     result = MainLoop(fd);
    4977              : 
    4978         9521 :     if (fd != stdin)
    4979           21 :         fclose(fd);
    4980              : 
    4981         9521 :     pset.inputfile = oldfilename;
    4982              : 
    4983         9521 :     pg_logging_config(pset.inputfile ? 0 : PG_LOG_FLAG_TERSE);
    4984              : 
    4985         9521 :     return result;
    4986              : }
    4987              : 
    4988              : 
    4989              : 
    4990              : static const char *
    4991            3 : _align2string(enum printFormat in)
    4992              : {
    4993            3 :     switch (in)
    4994              :     {
    4995            0 :         case PRINT_NOTHING:
    4996            0 :             return "nothing";
    4997              :             break;
    4998            3 :         case PRINT_ALIGNED:
    4999            3 :             return "aligned";
    5000              :             break;
    5001            0 :         case PRINT_ASCIIDOC:
    5002            0 :             return "asciidoc";
    5003              :             break;
    5004            0 :         case PRINT_CSV:
    5005            0 :             return "csv";
    5006              :             break;
    5007            0 :         case PRINT_HTML:
    5008            0 :             return "html";
    5009              :             break;
    5010            0 :         case PRINT_LATEX:
    5011            0 :             return "latex";
    5012              :             break;
    5013            0 :         case PRINT_LATEX_LONGTABLE:
    5014            0 :             return "latex-longtable";
    5015              :             break;
    5016            0 :         case PRINT_TROFF_MS:
    5017            0 :             return "troff-ms";
    5018              :             break;
    5019            0 :         case PRINT_UNALIGNED:
    5020            0 :             return "unaligned";
    5021              :             break;
    5022            0 :         case PRINT_WRAPPED:
    5023            0 :             return "wrapped";
    5024              :             break;
    5025              :     }
    5026            0 :     return "unknown";
    5027              : }
    5028              : 
    5029              : /*
    5030              :  * Parse entered Unicode linestyle.  If ok, update *linestyle and return
    5031              :  * true, else return false.
    5032              :  */
    5033              : static bool
    5034            0 : set_unicode_line_style(const char *value, size_t vallen,
    5035              :                        unicode_linestyle *linestyle)
    5036              : {
    5037            0 :     if (pg_strncasecmp("single", value, vallen) == 0)
    5038            0 :         *linestyle = UNICODE_LINESTYLE_SINGLE;
    5039            0 :     else if (pg_strncasecmp("double", value, vallen) == 0)
    5040            0 :         *linestyle = UNICODE_LINESTYLE_DOUBLE;
    5041              :     else
    5042            0 :         return false;
    5043            0 :     return true;
    5044              : }
    5045              : 
    5046              : static const char *
    5047            9 : _unicode_linestyle2string(int linestyle)
    5048              : {
    5049            9 :     switch (linestyle)
    5050              :     {
    5051            9 :         case UNICODE_LINESTYLE_SINGLE:
    5052            9 :             return "single";
    5053              :             break;
    5054            0 :         case UNICODE_LINESTYLE_DOUBLE:
    5055            0 :             return "double";
    5056              :             break;
    5057              :     }
    5058            0 :     return "unknown";
    5059              : }
    5060              : 
    5061              : /*
    5062              :  * do_pset
    5063              :  *
    5064              :  * Performs the assignment "param = value", where value could be NULL;
    5065              :  * for some params that has an effect such as inversion, for others
    5066              :  * it does nothing.
    5067              :  *
    5068              :  * Adjusts the state of the formatting options at *popt.  (In practice that
    5069              :  * is always pset.popt, but maybe someday it could be different.)
    5070              :  *
    5071              :  * If successful and quiet is false, then invokes printPsetInfo() to report
    5072              :  * the change.
    5073              :  *
    5074              :  * Returns true if successful, else false (eg for invalid param or value).
    5075              :  */
    5076              : bool
    5077         1004 : do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
    5078              : {
    5079         1004 :     size_t      vallen = 0;
    5080              : 
    5081              :     Assert(param != NULL);
    5082              : 
    5083         1004 :     if (value)
    5084          929 :         vallen = strlen(value);
    5085              : 
    5086              :     /* set format */
    5087         1004 :     if (strcmp(param, "format") == 0)
    5088              :     {
    5089              :         static const struct fmt
    5090              :         {
    5091              :             const char *name;
    5092              :             enum printFormat number;
    5093              :         }           formats[] =
    5094              :         {
    5095              :             /* remember to update error message below when adding more */
    5096              :             {"aligned", PRINT_ALIGNED},
    5097              :             {"asciidoc", PRINT_ASCIIDOC},
    5098              :             {"csv", PRINT_CSV},
    5099              :             {"html", PRINT_HTML},
    5100              :             {"latex", PRINT_LATEX},
    5101              :             {"troff-ms", PRINT_TROFF_MS},
    5102              :             {"unaligned", PRINT_UNALIGNED},
    5103              :             {"wrapped", PRINT_WRAPPED}
    5104              :         };
    5105              : 
    5106          319 :         if (!value)
    5107              :             ;
    5108              :         else
    5109              :         {
    5110          319 :             int         match_pos = -1;
    5111              : 
    5112         2850 :             for (int i = 0; i < lengthof(formats); i++)
    5113              :             {
    5114         2534 :                 if (pg_strncasecmp(formats[i].name, value, vallen) == 0)
    5115              :                 {
    5116          319 :                     if (match_pos < 0)
    5117          316 :                         match_pos = i;
    5118              :                     else
    5119              :                     {
    5120            3 :                         pg_log_error("\\pset: ambiguous abbreviation \"%s\" matches both \"%s\" and \"%s\"",
    5121              :                                      value,
    5122              :                                      formats[match_pos].name, formats[i].name);
    5123            3 :                         return false;
    5124              :                     }
    5125              :                 }
    5126              :             }
    5127          316 :             if (match_pos >= 0)
    5128          313 :                 popt->topt.format = formats[match_pos].number;
    5129            3 :             else if (pg_strncasecmp("latex-longtable", value, vallen) == 0)
    5130              :             {
    5131              :                 /*
    5132              :                  * We must treat latex-longtable specially because latex is a
    5133              :                  * prefix of it; if both were in the table above, we'd think
    5134              :                  * "latex" is ambiguous.
    5135              :                  */
    5136            3 :                 popt->topt.format = PRINT_LATEX_LONGTABLE;
    5137              :             }
    5138              :             else
    5139              :             {
    5140            0 :                 pg_log_error("\\pset: allowed formats are aligned, asciidoc, csv, html, latex, latex-longtable, troff-ms, unaligned, wrapped");
    5141            0 :                 return false;
    5142              :             }
    5143              :         }
    5144              :     }
    5145              : 
    5146              :     /* set table line style */
    5147          685 :     else if (strcmp(param, "linestyle") == 0)
    5148              :     {
    5149           15 :         if (!value)
    5150              :             ;
    5151           15 :         else if (pg_strncasecmp("ascii", value, vallen) == 0)
    5152            9 :             popt->topt.line_style = &pg_asciiformat;
    5153            6 :         else if (pg_strncasecmp("old-ascii", value, vallen) == 0)
    5154            6 :             popt->topt.line_style = &pg_asciiformat_old;
    5155            0 :         else if (pg_strncasecmp("unicode", value, vallen) == 0)
    5156            0 :             popt->topt.line_style = &pg_utf8format;
    5157              :         else
    5158              :         {
    5159            0 :             pg_log_error("\\pset: allowed line styles are ascii, old-ascii, unicode");
    5160            0 :             return false;
    5161              :         }
    5162              :     }
    5163              : 
    5164              :     /* set unicode border line style */
    5165          670 :     else if (strcmp(param, "unicode_border_linestyle") == 0)
    5166              :     {
    5167            0 :         if (!value)
    5168              :             ;
    5169            0 :         else if (set_unicode_line_style(value, vallen,
    5170              :                                         &popt->topt.unicode_border_linestyle))
    5171            0 :             refresh_utf8format(&(popt->topt));
    5172              :         else
    5173              :         {
    5174            0 :             pg_log_error("\\pset: allowed Unicode border line styles are single, double");
    5175            0 :             return false;
    5176              :         }
    5177              :     }
    5178              : 
    5179              :     /* set unicode column line style */
    5180          670 :     else if (strcmp(param, "unicode_column_linestyle") == 0)
    5181              :     {
    5182            0 :         if (!value)
    5183              :             ;
    5184            0 :         else if (set_unicode_line_style(value, vallen,
    5185              :                                         &popt->topt.unicode_column_linestyle))
    5186            0 :             refresh_utf8format(&(popt->topt));
    5187              :         else
    5188              :         {
    5189            0 :             pg_log_error("\\pset: allowed Unicode column line styles are single, double");
    5190            0 :             return false;
    5191              :         }
    5192              :     }
    5193              : 
    5194              :     /* set unicode header line style */
    5195          670 :     else if (strcmp(param, "unicode_header_linestyle") == 0)
    5196              :     {
    5197            0 :         if (!value)
    5198              :             ;
    5199            0 :         else if (set_unicode_line_style(value, vallen,
    5200              :                                         &popt->topt.unicode_header_linestyle))
    5201            0 :             refresh_utf8format(&(popt->topt));
    5202              :         else
    5203              :         {
    5204            0 :             pg_log_error("\\pset: allowed Unicode header line styles are single, double");
    5205            0 :             return false;
    5206              :         }
    5207              :     }
    5208              : 
    5209              :     /* set border style/width */
    5210          670 :     else if (strcmp(param, "border") == 0)
    5211              :     {
    5212          201 :         if (value)
    5213          201 :             popt->topt.border = atoi(value);
    5214              :     }
    5215              : 
    5216              :     /* set expanded/vertical mode */
    5217          469 :     else if (strcmp(param, "x") == 0 ||
    5218          469 :              strcmp(param, "expanded") == 0 ||
    5219          303 :              strcmp(param, "vertical") == 0)
    5220              :     {
    5221          166 :         if (value && pg_strcasecmp(value, "auto") == 0)
    5222            0 :             popt->topt.expanded = 2;
    5223          166 :         else if (value)
    5224              :         {
    5225              :             bool        on_off;
    5226              : 
    5227          130 :             if (ParseVariableBool(value, NULL, &on_off))
    5228          130 :                 popt->topt.expanded = on_off ? 1 : 0;
    5229              :             else
    5230              :             {
    5231            0 :                 PsqlVarEnumError(param, value, "on, off, auto");
    5232            0 :                 return false;
    5233              :             }
    5234              :         }
    5235              :         else
    5236           36 :             popt->topt.expanded = !popt->topt.expanded;
    5237              :     }
    5238              : 
    5239              :     /* header line width in expanded mode */
    5240          303 :     else if (strcmp(param, "xheader_width") == 0)
    5241              :     {
    5242            0 :         if (!value)
    5243              :             ;
    5244            0 :         else if (pg_strcasecmp(value, "full") == 0)
    5245            0 :             popt->topt.expanded_header_width_type = PRINT_XHEADER_FULL;
    5246            0 :         else if (pg_strcasecmp(value, "column") == 0)
    5247            0 :             popt->topt.expanded_header_width_type = PRINT_XHEADER_COLUMN;
    5248            0 :         else if (pg_strcasecmp(value, "page") == 0)
    5249            0 :             popt->topt.expanded_header_width_type = PRINT_XHEADER_PAGE;
    5250              :         else
    5251              :         {
    5252            0 :             int         intval = atoi(value);
    5253              : 
    5254            0 :             if (intval == 0)
    5255              :             {
    5256            0 :                 pg_log_error("\\pset: allowed xheader_width values are \"%s\" (default), \"%s\", \"%s\", or a number specifying the exact width", "full", "column", "page");
    5257            0 :                 return false;
    5258              :             }
    5259              : 
    5260            0 :             popt->topt.expanded_header_width_type = PRINT_XHEADER_EXACT_WIDTH;
    5261            0 :             popt->topt.expanded_header_exact_width = intval;
    5262              :         }
    5263              :     }
    5264              : 
    5265              :     /* field separator for CSV format */
    5266          303 :     else if (strcmp(param, "csv_fieldsep") == 0)
    5267              :     {
    5268           30 :         if (value)
    5269              :         {
    5270              :             /* CSV separator has to be a one-byte character */
    5271           30 :             if (strlen(value) != 1)
    5272              :             {
    5273            9 :                 pg_log_error("\\pset: csv_fieldsep must be a single one-byte character");
    5274            9 :                 return false;
    5275              :             }
    5276           21 :             if (value[0] == '"' || value[0] == '\n' || value[0] == '\r')
    5277              :             {
    5278            9 :                 pg_log_error("\\pset: csv_fieldsep cannot be a double quote, a newline, or a carriage return");
    5279            9 :                 return false;
    5280              :             }
    5281           12 :             popt->topt.csvFieldSep[0] = value[0];
    5282              :         }
    5283              :     }
    5284              : 
    5285              :     /* locale-aware numeric output */
    5286          273 :     else if (strcmp(param, "numericlocale") == 0)
    5287              :     {
    5288            6 :         if (value)
    5289            6 :             return ParseVariableBool(value, param, &popt->topt.numericLocale);
    5290              :         else
    5291            0 :             popt->topt.numericLocale = !popt->topt.numericLocale;
    5292              :     }
    5293              : 
    5294              :     /* null display */
    5295          267 :     else if (strcmp(param, "null") == 0)
    5296              :     {
    5297           48 :         if (value)
    5298              :         {
    5299           45 :             free(popt->nullPrint);
    5300           45 :             popt->nullPrint = pg_strdup(value);
    5301              :         }
    5302              :     }
    5303              : 
    5304              :     /* 'false' display */
    5305          219 :     else if (strcmp(param, "display_false") == 0)
    5306              :     {
    5307            9 :         if (value)
    5308              :         {
    5309            6 :             free(popt->falsePrint);
    5310            6 :             popt->falsePrint = pg_strdup(value);
    5311              :         }
    5312              :     }
    5313              : 
    5314              :     /* 'true' display */
    5315          210 :     else if (strcmp(param, "display_true") == 0)
    5316              :     {
    5317            9 :         if (value)
    5318              :         {
    5319            6 :             free(popt->truePrint);
    5320            6 :             popt->truePrint = pg_strdup(value);
    5321              :         }
    5322              :     }
    5323              : 
    5324              :     /* field separator for unaligned text */
    5325          201 :     else if (strcmp(param, "fieldsep") == 0)
    5326              :     {
    5327            3 :         if (value)
    5328              :         {
    5329            3 :             free(popt->topt.fieldSep.separator);
    5330            3 :             popt->topt.fieldSep.separator = pg_strdup(value);
    5331            3 :             popt->topt.fieldSep.separator_zero = false;
    5332              :         }
    5333              :     }
    5334              : 
    5335          198 :     else if (strcmp(param, "fieldsep_zero") == 0)
    5336              :     {
    5337            0 :         free(popt->topt.fieldSep.separator);
    5338            0 :         popt->topt.fieldSep.separator = NULL;
    5339            0 :         popt->topt.fieldSep.separator_zero = true;
    5340              :     }
    5341              : 
    5342              :     /* record separator for unaligned text */
    5343          198 :     else if (strcmp(param, "recordsep") == 0)
    5344              :     {
    5345            0 :         if (value)
    5346              :         {
    5347            0 :             free(popt->topt.recordSep.separator);
    5348            0 :             popt->topt.recordSep.separator = pg_strdup(value);
    5349            0 :             popt->topt.recordSep.separator_zero = false;
    5350              :         }
    5351              :     }
    5352              : 
    5353          198 :     else if (strcmp(param, "recordsep_zero") == 0)
    5354              :     {
    5355            0 :         free(popt->topt.recordSep.separator);
    5356            0 :         popt->topt.recordSep.separator = NULL;
    5357            0 :         popt->topt.recordSep.separator_zero = true;
    5358              :     }
    5359              : 
    5360              :     /* toggle between full and tuples-only format */
    5361          198 :     else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
    5362              :     {
    5363          147 :         if (value)
    5364          129 :             return ParseVariableBool(value, param, &popt->topt.tuples_only);
    5365              :         else
    5366           18 :             popt->topt.tuples_only = !popt->topt.tuples_only;
    5367              :     }
    5368              : 
    5369              :     /* set title override */
    5370           51 :     else if (strcmp(param, "C") == 0 || strcmp(param, "title") == 0)
    5371              :     {
    5372            3 :         free(popt->title);
    5373            3 :         if (!value)
    5374            0 :             popt->title = NULL;
    5375              :         else
    5376            3 :             popt->title = pg_strdup(value);
    5377              :     }
    5378              : 
    5379              :     /* set HTML table tag options */
    5380           48 :     else if (strcmp(param, "T") == 0 || strcmp(param, "tableattr") == 0)
    5381              :     {
    5382           24 :         free(popt->topt.tableAttr);
    5383           24 :         if (!value)
    5384           12 :             popt->topt.tableAttr = NULL;
    5385              :         else
    5386           12 :             popt->topt.tableAttr = pg_strdup(value);
    5387              :     }
    5388              : 
    5389              :     /* toggle use of pager */
    5390           24 :     else if (strcmp(param, "pager") == 0)
    5391              :     {
    5392            0 :         if (value && pg_strcasecmp(value, "always") == 0)
    5393            0 :             popt->topt.pager = 2;
    5394            0 :         else if (value)
    5395              :         {
    5396              :             bool        on_off;
    5397              : 
    5398            0 :             if (!ParseVariableBool(value, NULL, &on_off))
    5399              :             {
    5400            0 :                 PsqlVarEnumError(param, value, "on, off, always");
    5401            0 :                 return false;
    5402              :             }
    5403            0 :             popt->topt.pager = on_off ? 1 : 0;
    5404              :         }
    5405            0 :         else if (popt->topt.pager == 1)
    5406            0 :             popt->topt.pager = 0;
    5407              :         else
    5408            0 :             popt->topt.pager = 1;
    5409              :     }
    5410              : 
    5411              :     /* set minimum lines for pager use */
    5412           24 :     else if (strcmp(param, "pager_min_lines") == 0)
    5413              :     {
    5414            0 :         if (value &&
    5415            0 :             !ParseVariableNum(value, "pager_min_lines", &popt->topt.pager_min_lines))
    5416            0 :             return false;
    5417              :     }
    5418              : 
    5419              :     /* disable "(x rows)" footer */
    5420           24 :     else if (strcmp(param, "footer") == 0)
    5421              :     {
    5422            0 :         if (value)
    5423            0 :             return ParseVariableBool(value, param, &popt->topt.default_footer);
    5424              :         else
    5425            0 :             popt->topt.default_footer = !popt->topt.default_footer;
    5426              :     }
    5427              : 
    5428              :     /* set border style/width */
    5429           24 :     else if (strcmp(param, "columns") == 0)
    5430              :     {
    5431           24 :         if (value)
    5432           24 :             popt->topt.columns = atoi(value);
    5433              :     }
    5434              :     else
    5435              :     {
    5436            0 :         pg_log_error("\\pset: unknown option: %s", param);
    5437            0 :         return false;
    5438              :     }
    5439              : 
    5440          848 :     if (!quiet)
    5441            1 :         printPsetInfo(param, &pset.popt);
    5442              : 
    5443          848 :     return true;
    5444              : }
    5445              : 
    5446              : /*
    5447              :  * printPsetInfo: print the state of the "param" formatting parameter in popt.
    5448              :  */
    5449              : static bool
    5450            1 : printPsetInfo(const char *param, printQueryOpt *popt)
    5451              : {
    5452              :     Assert(param != NULL);
    5453              : 
    5454              :     /* show border style/width */
    5455            1 :     if (strcmp(param, "border") == 0)
    5456            0 :         printf(_("Border style is %d.\n"), popt->topt.border);
    5457              : 
    5458              :     /* show the target width for the wrapped format */
    5459            1 :     else if (strcmp(param, "columns") == 0)
    5460              :     {
    5461            0 :         if (!popt->topt.columns)
    5462            0 :             printf(_("Target width is unset.\n"));
    5463              :         else
    5464            0 :             printf(_("Target width is %d.\n"), popt->topt.columns);
    5465              :     }
    5466              : 
    5467              :     /* show expanded/vertical mode */
    5468            1 :     else if (strcmp(param, "x") == 0 || strcmp(param, "expanded") == 0 || strcmp(param, "vertical") == 0)
    5469              :     {
    5470            1 :         if (popt->topt.expanded == 1)
    5471            1 :             printf(_("Expanded display is on.\n"));
    5472            0 :         else if (popt->topt.expanded == 2)
    5473            0 :             printf(_("Expanded display is used automatically.\n"));
    5474              :         else
    5475            0 :             printf(_("Expanded display is off.\n"));
    5476              :     }
    5477              : 
    5478              :     /* show xheader width value */
    5479            0 :     else if (strcmp(param, "xheader_width") == 0)
    5480              :     {
    5481            0 :         if (popt->topt.expanded_header_width_type == PRINT_XHEADER_FULL)
    5482            0 :             printf(_("Expanded header width is \"%s\".\n"), "full");
    5483            0 :         else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_COLUMN)
    5484            0 :             printf(_("Expanded header width is \"%s\".\n"), "column");
    5485            0 :         else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_PAGE)
    5486            0 :             printf(_("Expanded header width is \"%s\".\n"), "page");
    5487            0 :         else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_EXACT_WIDTH)
    5488            0 :             printf(_("Expanded header width is %d.\n"), popt->topt.expanded_header_exact_width);
    5489              :     }
    5490              : 
    5491              :     /* show field separator for CSV format */
    5492            0 :     else if (strcmp(param, "csv_fieldsep") == 0)
    5493              :     {
    5494            0 :         printf(_("Field separator for CSV is \"%s\".\n"),
    5495              :                popt->topt.csvFieldSep);
    5496              :     }
    5497              : 
    5498              :     /* show boolean 'false' display */
    5499            0 :     else if (strcmp(param, "display_false") == 0)
    5500              :     {
    5501            0 :         printf(_("Boolean false display is \"%s\".\n"),
    5502              :                popt->falsePrint ? popt->falsePrint : "f");
    5503              :     }
    5504              : 
    5505              :     /* show boolean 'true' display */
    5506            0 :     else if (strcmp(param, "display_true") == 0)
    5507              :     {
    5508            0 :         printf(_("Boolean true display is \"%s\".\n"),
    5509              :                popt->truePrint ? popt->truePrint : "t");
    5510              :     }
    5511              : 
    5512              :     /* show field separator for unaligned text */
    5513            0 :     else if (strcmp(param, "fieldsep") == 0)
    5514              :     {
    5515            0 :         if (popt->topt.fieldSep.separator_zero)
    5516            0 :             printf(_("Field separator is zero byte.\n"));
    5517              :         else
    5518            0 :             printf(_("Field separator is \"%s\".\n"),
    5519              :                    popt->topt.fieldSep.separator);
    5520              :     }
    5521              : 
    5522            0 :     else if (strcmp(param, "fieldsep_zero") == 0)
    5523              :     {
    5524            0 :         printf(_("Field separator is zero byte.\n"));
    5525              :     }
    5526              : 
    5527              :     /* show disable "(x rows)" footer */
    5528            0 :     else if (strcmp(param, "footer") == 0)
    5529              :     {
    5530            0 :         if (popt->topt.default_footer)
    5531            0 :             printf(_("Default footer is on.\n"));
    5532              :         else
    5533            0 :             printf(_("Default footer is off.\n"));
    5534              :     }
    5535              : 
    5536              :     /* show format */
    5537            0 :     else if (strcmp(param, "format") == 0)
    5538              :     {
    5539            0 :         printf(_("Output format is %s.\n"), _align2string(popt->topt.format));
    5540              :     }
    5541              : 
    5542              :     /* show table line style */
    5543            0 :     else if (strcmp(param, "linestyle") == 0)
    5544              :     {
    5545            0 :         printf(_("Line style is %s.\n"),
    5546              :                get_line_style(&popt->topt)->name);
    5547              :     }
    5548              : 
    5549              :     /* show null display */
    5550            0 :     else if (strcmp(param, "null") == 0)
    5551              :     {
    5552            0 :         printf(_("Null display is \"%s\".\n"),
    5553              :                popt->nullPrint ? popt->nullPrint : "");
    5554              :     }
    5555              : 
    5556              :     /* show locale-aware numeric output */
    5557            0 :     else if (strcmp(param, "numericlocale") == 0)
    5558              :     {
    5559            0 :         if (popt->topt.numericLocale)
    5560            0 :             printf(_("Locale-adjusted numeric output is on.\n"));
    5561              :         else
    5562            0 :             printf(_("Locale-adjusted numeric output is off.\n"));
    5563              :     }
    5564              : 
    5565              :     /* show toggle use of pager */
    5566            0 :     else if (strcmp(param, "pager") == 0)
    5567              :     {
    5568            0 :         if (popt->topt.pager == 1)
    5569            0 :             printf(_("Pager is used for long output.\n"));
    5570            0 :         else if (popt->topt.pager == 2)
    5571            0 :             printf(_("Pager is always used.\n"));
    5572              :         else
    5573            0 :             printf(_("Pager usage is off.\n"));
    5574              :     }
    5575              : 
    5576              :     /* show minimum lines for pager use */
    5577            0 :     else if (strcmp(param, "pager_min_lines") == 0)
    5578              :     {
    5579            0 :         printf(ngettext("Pager won't be used for less than %d line.\n",
    5580              :                         "Pager won't be used for less than %d lines.\n",
    5581              :                         popt->topt.pager_min_lines),
    5582              :                popt->topt.pager_min_lines);
    5583              :     }
    5584              : 
    5585              :     /* show record separator for unaligned text */
    5586            0 :     else if (strcmp(param, "recordsep") == 0)
    5587              :     {
    5588            0 :         if (popt->topt.recordSep.separator_zero)
    5589            0 :             printf(_("Record separator is zero byte.\n"));
    5590            0 :         else if (strcmp(popt->topt.recordSep.separator, "\n") == 0)
    5591            0 :             printf(_("Record separator is <newline>.\n"));
    5592              :         else
    5593            0 :             printf(_("Record separator is \"%s\".\n"),
    5594              :                    popt->topt.recordSep.separator);
    5595              :     }
    5596              : 
    5597            0 :     else if (strcmp(param, "recordsep_zero") == 0)
    5598              :     {
    5599            0 :         printf(_("Record separator is zero byte.\n"));
    5600              :     }
    5601              : 
    5602              :     /* show HTML table tag options */
    5603            0 :     else if (strcmp(param, "T") == 0 || strcmp(param, "tableattr") == 0)
    5604              :     {
    5605            0 :         if (popt->topt.tableAttr)
    5606            0 :             printf(_("Table attributes are \"%s\".\n"),
    5607              :                    popt->topt.tableAttr);
    5608              :         else
    5609            0 :             printf(_("Table attributes unset.\n"));
    5610              :     }
    5611              : 
    5612              :     /* show title override */
    5613            0 :     else if (strcmp(param, "C") == 0 || strcmp(param, "title") == 0)
    5614              :     {
    5615            0 :         if (popt->title)
    5616            0 :             printf(_("Title is \"%s\".\n"), popt->title);
    5617              :         else
    5618            0 :             printf(_("Title is unset.\n"));
    5619              :     }
    5620              : 
    5621              :     /* show toggle between full and tuples-only format */
    5622            0 :     else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
    5623              :     {
    5624            0 :         if (popt->topt.tuples_only)
    5625            0 :             printf(_("Tuples only is on.\n"));
    5626              :         else
    5627            0 :             printf(_("Tuples only is off.\n"));
    5628              :     }
    5629              : 
    5630              :     /* Unicode style formatting */
    5631            0 :     else if (strcmp(param, "unicode_border_linestyle") == 0)
    5632              :     {
    5633            0 :         printf(_("Unicode border line style is \"%s\".\n"),
    5634              :                _unicode_linestyle2string(popt->topt.unicode_border_linestyle));
    5635              :     }
    5636              : 
    5637            0 :     else if (strcmp(param, "unicode_column_linestyle") == 0)
    5638              :     {
    5639            0 :         printf(_("Unicode column line style is \"%s\".\n"),
    5640              :                _unicode_linestyle2string(popt->topt.unicode_column_linestyle));
    5641              :     }
    5642              : 
    5643            0 :     else if (strcmp(param, "unicode_header_linestyle") == 0)
    5644              :     {
    5645            0 :         printf(_("Unicode header line style is \"%s\".\n"),
    5646              :                _unicode_linestyle2string(popt->topt.unicode_header_linestyle));
    5647              :     }
    5648              : 
    5649              :     else
    5650              :     {
    5651            0 :         pg_log_error("\\pset: unknown option: %s", param);
    5652            0 :         return false;
    5653              :     }
    5654              : 
    5655            1 :     return true;
    5656              : }
    5657              : 
    5658              : /*
    5659              :  * savePsetInfo: make a malloc'd copy of the data in *popt.
    5660              :  *
    5661              :  * Possibly this should be somewhere else, but it's a bit specific to psql.
    5662              :  */
    5663              : printQueryOpt *
    5664           30 : savePsetInfo(const printQueryOpt *popt)
    5665              : {
    5666              :     printQueryOpt *save;
    5667              : 
    5668           30 :     save = pg_malloc_object(printQueryOpt);
    5669              : 
    5670              :     /* Flat-copy all the scalar fields, then duplicate sub-structures. */
    5671           30 :     memcpy(save, popt, sizeof(printQueryOpt));
    5672              : 
    5673              :     /* topt.line_style points to const data that need not be duplicated */
    5674           30 :     if (popt->topt.fieldSep.separator)
    5675           30 :         save->topt.fieldSep.separator = pg_strdup(popt->topt.fieldSep.separator);
    5676           30 :     if (popt->topt.recordSep.separator)
    5677           30 :         save->topt.recordSep.separator = pg_strdup(popt->topt.recordSep.separator);
    5678           30 :     if (popt->topt.tableAttr)
    5679            0 :         save->topt.tableAttr = pg_strdup(popt->topt.tableAttr);
    5680           30 :     if (popt->nullPrint)
    5681            0 :         save->nullPrint = pg_strdup(popt->nullPrint);
    5682           30 :     if (popt->title)
    5683            0 :         save->title = pg_strdup(popt->title);
    5684              : 
    5685              :     /*
    5686              :      * footers and translate_columns are never set in psql's print settings,
    5687              :      * so we needn't write code to duplicate them.
    5688              :      */
    5689              :     Assert(popt->footers == NULL);
    5690              :     Assert(popt->translate_columns == NULL);
    5691              : 
    5692           30 :     return save;
    5693              : }
    5694              : 
    5695              : /*
    5696              :  * restorePsetInfo: restore *popt from the previously-saved copy *save,
    5697              :  * then free *save.
    5698              :  */
    5699              : void
    5700           30 : restorePsetInfo(printQueryOpt *popt, printQueryOpt *save)
    5701              : {
    5702              :     /* Free all the old data we're about to overwrite the pointers to. */
    5703              : 
    5704              :     /* topt.line_style points to const data that need not be duplicated */
    5705           30 :     free(popt->topt.fieldSep.separator);
    5706           30 :     free(popt->topt.recordSep.separator);
    5707           30 :     free(popt->topt.tableAttr);
    5708           30 :     free(popt->nullPrint);
    5709           30 :     free(popt->title);
    5710              : 
    5711              :     /*
    5712              :      * footers and translate_columns are never set in psql's print settings,
    5713              :      * so we needn't write code to duplicate them.
    5714              :      */
    5715              :     Assert(popt->footers == NULL);
    5716              :     Assert(popt->translate_columns == NULL);
    5717              : 
    5718              :     /* Now we may flat-copy all the fields, including pointers. */
    5719           30 :     memcpy(popt, save, sizeof(printQueryOpt));
    5720              : 
    5721              :     /* Lastly, free "save" ... but its sub-structures now belong to popt. */
    5722           30 :     free(save);
    5723           30 : }
    5724              : 
    5725              : static const char *
    5726           18 : pset_bool_string(bool val)
    5727              : {
    5728           18 :     return val ? "on" : "off";
    5729              : }
    5730              : 
    5731              : 
    5732              : static char *
    5733           18 : pset_quoted_string(const char *str)
    5734              : {
    5735           18 :     char       *ret = pg_malloc(strlen(str) * 2 + 3);
    5736           18 :     char       *r = ret;
    5737              : 
    5738           18 :     *r++ = '\'';
    5739              : 
    5740           33 :     for (; *str; str++)
    5741              :     {
    5742           15 :         if (*str == '\n')
    5743              :         {
    5744            3 :             *r++ = '\\';
    5745            3 :             *r++ = 'n';
    5746              :         }
    5747           12 :         else if (*str == '\'')
    5748              :         {
    5749            0 :             *r++ = '\\';
    5750            0 :             *r++ = '\'';
    5751              :         }
    5752              :         else
    5753           12 :             *r++ = *str;
    5754              :     }
    5755              : 
    5756           18 :     *r++ = '\'';
    5757           18 :     *r = '\0';
    5758              : 
    5759           18 :     return ret;
    5760              : }
    5761              : 
    5762              : 
    5763              : /*
    5764              :  * Return a malloc'ed string for the \pset value.
    5765              :  *
    5766              :  * Note that for some string parameters, print.c distinguishes between unset
    5767              :  * and empty string, but for others it doesn't.  This function should produce
    5768              :  * output that produces the correct setting when fed back into \pset.
    5769              :  */
    5770              : static char *
    5771           72 : pset_value_string(const char *param, printQueryOpt *popt)
    5772              : {
    5773              :     Assert(param != NULL);
    5774              : 
    5775           72 :     if (strcmp(param, "border") == 0)
    5776            3 :         return psprintf("%d", popt->topt.border);
    5777           69 :     else if (strcmp(param, "columns") == 0)
    5778            3 :         return psprintf("%d", popt->topt.columns);
    5779           66 :     else if (strcmp(param, "csv_fieldsep") == 0)
    5780            3 :         return pset_quoted_string(popt->topt.csvFieldSep);
    5781           63 :     else if (strcmp(param, "display_false") == 0)
    5782            3 :         return pset_quoted_string(popt->falsePrint ? popt->falsePrint : "f");
    5783           60 :     else if (strcmp(param, "display_true") == 0)
    5784            3 :         return pset_quoted_string(popt->truePrint ? popt->truePrint : "t");
    5785           57 :     else if (strcmp(param, "expanded") == 0)
    5786            6 :         return pstrdup(popt->topt.expanded == 2
    5787              :                        ? "auto"
    5788            3 :                        : pset_bool_string(popt->topt.expanded));
    5789           54 :     else if (strcmp(param, "fieldsep") == 0)
    5790            3 :         return pset_quoted_string(popt->topt.fieldSep.separator
    5791              :                                   ? popt->topt.fieldSep.separator
    5792              :                                   : "");
    5793           51 :     else if (strcmp(param, "fieldsep_zero") == 0)
    5794            3 :         return pstrdup(pset_bool_string(popt->topt.fieldSep.separator_zero));
    5795           48 :     else if (strcmp(param, "footer") == 0)
    5796            3 :         return pstrdup(pset_bool_string(popt->topt.default_footer));
    5797           45 :     else if (strcmp(param, "format") == 0)
    5798            3 :         return pstrdup(_align2string(popt->topt.format));
    5799           42 :     else if (strcmp(param, "linestyle") == 0)
    5800            3 :         return pstrdup(get_line_style(&popt->topt)->name);
    5801           39 :     else if (strcmp(param, "null") == 0)
    5802            3 :         return pset_quoted_string(popt->nullPrint
    5803              :                                   ? popt->nullPrint
    5804              :                                   : "");
    5805           36 :     else if (strcmp(param, "numericlocale") == 0)
    5806            3 :         return pstrdup(pset_bool_string(popt->topt.numericLocale));
    5807           33 :     else if (strcmp(param, "pager") == 0)
    5808            3 :         return psprintf("%d", popt->topt.pager);
    5809           30 :     else if (strcmp(param, "pager_min_lines") == 0)
    5810            3 :         return psprintf("%d", popt->topt.pager_min_lines);
    5811           27 :     else if (strcmp(param, "recordsep") == 0)
    5812            3 :         return pset_quoted_string(popt->topt.recordSep.separator
    5813              :                                   ? popt->topt.recordSep.separator
    5814              :                                   : "");
    5815           24 :     else if (strcmp(param, "recordsep_zero") == 0)
    5816            3 :         return pstrdup(pset_bool_string(popt->topt.recordSep.separator_zero));
    5817           21 :     else if (strcmp(param, "tableattr") == 0)
    5818            3 :         return popt->topt.tableAttr ? pset_quoted_string(popt->topt.tableAttr) : pstrdup("");
    5819           18 :     else if (strcmp(param, "title") == 0)
    5820            3 :         return popt->title ? pset_quoted_string(popt->title) : pstrdup("");
    5821           15 :     else if (strcmp(param, "tuples_only") == 0)
    5822            3 :         return pstrdup(pset_bool_string(popt->topt.tuples_only));
    5823           12 :     else if (strcmp(param, "unicode_border_linestyle") == 0)
    5824            3 :         return pstrdup(_unicode_linestyle2string(popt->topt.unicode_border_linestyle));
    5825            9 :     else if (strcmp(param, "unicode_column_linestyle") == 0)
    5826            3 :         return pstrdup(_unicode_linestyle2string(popt->topt.unicode_column_linestyle));
    5827            6 :     else if (strcmp(param, "unicode_header_linestyle") == 0)
    5828            3 :         return pstrdup(_unicode_linestyle2string(popt->topt.unicode_header_linestyle));
    5829            3 :     else if (strcmp(param, "xheader_width") == 0)
    5830              :     {
    5831            3 :         if (popt->topt.expanded_header_width_type == PRINT_XHEADER_FULL)
    5832            3 :             return pstrdup("full");
    5833            0 :         else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_COLUMN)
    5834            0 :             return pstrdup("column");
    5835            0 :         else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_PAGE)
    5836            0 :             return pstrdup("page");
    5837              :         else
    5838              :         {
    5839              :             /* must be PRINT_XHEADER_EXACT_WIDTH */
    5840              :             char        wbuff[32];
    5841              : 
    5842            0 :             snprintf(wbuff, sizeof(wbuff), "%d",
    5843              :                      popt->topt.expanded_header_exact_width);
    5844            0 :             return pstrdup(wbuff);
    5845              :         }
    5846              :     }
    5847              :     else
    5848            0 :         return pstrdup("ERROR");
    5849              : }
    5850              : 
    5851              : 
    5852              : 
    5853              : #ifndef WIN32
    5854              : #define DEFAULT_SHELL "/bin/sh"
    5855              : #else
    5856              : /*
    5857              :  *  CMD.EXE is in different places in different Win32 releases so we
    5858              :  *  have to rely on the path to find it.
    5859              :  */
    5860              : #define DEFAULT_SHELL "cmd.exe"
    5861              : #endif
    5862              : 
    5863              : static bool
    5864            0 : do_shell(const char *command)
    5865              : {
    5866              :     int         result;
    5867              : 
    5868            0 :     fflush(NULL);
    5869            0 :     if (!command)
    5870              :     {
    5871              :         char       *sys;
    5872              :         const char *shellName;
    5873              : 
    5874            0 :         shellName = getenv("SHELL");
    5875              : #ifdef WIN32
    5876              :         if (shellName == NULL)
    5877              :             shellName = getenv("COMSPEC");
    5878              : #endif
    5879            0 :         if (shellName == NULL)
    5880            0 :             shellName = DEFAULT_SHELL;
    5881              : 
    5882              :         /* See EDITOR handling comment for an explanation */
    5883              : #ifndef WIN32
    5884            0 :         sys = psprintf("exec %s", shellName);
    5885              : #else
    5886              :         sys = psprintf("\"%s\"", shellName);
    5887              : #endif
    5888            0 :         result = system(sys);
    5889            0 :         free(sys);
    5890              :     }
    5891              :     else
    5892            0 :         result = system(command);
    5893              : 
    5894            0 :     SetShellResultVariables(result);
    5895              : 
    5896            0 :     if (result == 127 || result == -1)
    5897              :     {
    5898            0 :         pg_log_error("\\!: failed");
    5899            0 :         return false;
    5900              :     }
    5901            0 :     return true;
    5902              : }
    5903              : 
    5904              : /*
    5905              :  * do_watch -- handler for \watch
    5906              :  *
    5907              :  * We break this out of exec_command to avoid having to plaster "volatile"
    5908              :  * onto a bunch of exec_command's variables to silence stupider compilers.
    5909              :  *
    5910              :  * "sleep" is the amount of time to sleep during each loop, measured in
    5911              :  * seconds.  The internals of this function should use "sleep_ms" for
    5912              :  * precise sleep time calculations.
    5913              :  */
    5914              : static bool
    5915            7 : do_watch(PQExpBuffer query_buf, double sleep, int iter, int min_rows)
    5916              : {
    5917            7 :     long        sleep_ms = (long) (sleep * 1000);
    5918            7 :     printQueryOpt myopt = pset.popt;
    5919              :     const char *strftime_fmt;
    5920              :     const char *user_title;
    5921              :     char       *title;
    5922            7 :     const char *pagerprog = NULL;
    5923            7 :     FILE       *pagerpipe = NULL;
    5924              :     int         title_len;
    5925            7 :     int         res = 0;
    5926            7 :     bool        done = false;
    5927              : #ifndef WIN32
    5928              :     sigset_t    sigalrm_sigchld_sigint;
    5929              :     sigset_t    sigalrm_sigchld;
    5930              :     sigset_t    sigint;
    5931              :     struct itimerval interval;
    5932              : #endif
    5933              : 
    5934            7 :     if (!query_buf || query_buf->len <= 0)
    5935              :     {
    5936            0 :         pg_log_error("\\watch cannot be used with an empty query");
    5937            0 :         return false;
    5938              :     }
    5939              : 
    5940              : #ifndef WIN32
    5941            7 :     sigemptyset(&sigalrm_sigchld_sigint);
    5942            7 :     sigaddset(&sigalrm_sigchld_sigint, SIGCHLD);
    5943            7 :     sigaddset(&sigalrm_sigchld_sigint, SIGALRM);
    5944            7 :     sigaddset(&sigalrm_sigchld_sigint, SIGINT);
    5945              : 
    5946            7 :     sigemptyset(&sigalrm_sigchld);
    5947            7 :     sigaddset(&sigalrm_sigchld, SIGCHLD);
    5948            7 :     sigaddset(&sigalrm_sigchld, SIGALRM);
    5949              : 
    5950            7 :     sigemptyset(&sigint);
    5951            7 :     sigaddset(&sigint, SIGINT);
    5952              : 
    5953              :     /*
    5954              :      * Block SIGALRM and SIGCHLD before we start the timer and the pager (if
    5955              :      * configured), to avoid races.  sigwait() will receive them.
    5956              :      */
    5957            7 :     sigprocmask(SIG_BLOCK, &sigalrm_sigchld, NULL);
    5958              : 
    5959              :     /*
    5960              :      * Set a timer to interrupt sigwait() so we can run the query at the
    5961              :      * requested intervals.
    5962              :      */
    5963            7 :     interval.it_value.tv_sec = sleep_ms / 1000;
    5964            7 :     interval.it_value.tv_usec = (sleep_ms % 1000) * 1000;
    5965            7 :     interval.it_interval = interval.it_value;
    5966            7 :     if (setitimer(ITIMER_REAL, &interval, NULL) < 0)
    5967              :     {
    5968            0 :         pg_log_error("could not set timer: %m");
    5969            0 :         done = true;
    5970              :     }
    5971              : #endif
    5972              : 
    5973              :     /*
    5974              :      * For \watch, we ignore the size of the result and always use the pager
    5975              :      * as long as we're talking to a terminal and "\pset pager" is enabled.
    5976              :      * However, we'll only use the pager identified by PSQL_WATCH_PAGER.  We
    5977              :      * ignore the regular PSQL_PAGER or PAGER environment variables, because
    5978              :      * traditional pagers probably won't be very useful for showing a stream
    5979              :      * of results.
    5980              :      */
    5981              : #ifndef WIN32
    5982            7 :     pagerprog = getenv("PSQL_WATCH_PAGER");
    5983              :     /* if variable is empty or all-white-space, don't use pager */
    5984            7 :     if (pagerprog && strspn(pagerprog, " \t\r\n") == strlen(pagerprog))
    5985            0 :         pagerprog = NULL;
    5986              : #endif
    5987            7 :     if (pagerprog && myopt.topt.pager &&
    5988            0 :         isatty(fileno(stdin)) && isatty(fileno(stdout)))
    5989              :     {
    5990            0 :         fflush(NULL);
    5991            0 :         disable_sigpipe_trap();
    5992            0 :         pagerpipe = popen(pagerprog, "w");
    5993              : 
    5994            0 :         if (!pagerpipe)
    5995              :             /* silently proceed without pager */
    5996            0 :             restore_sigpipe_trap();
    5997              :     }
    5998              : 
    5999              :     /*
    6000              :      * Choose format for timestamps.  We might eventually make this a \pset
    6001              :      * option.  In the meantime, using a variable for the format suppresses
    6002              :      * overly-anal-retentive gcc warnings about %c being Y2K sensitive.
    6003              :      */
    6004            7 :     strftime_fmt = "%c";
    6005              : 
    6006              :     /*
    6007              :      * Set up rendering options, in particular, disable the pager unless
    6008              :      * PSQL_WATCH_PAGER was successfully launched.
    6009              :      */
    6010            7 :     if (!pagerpipe)
    6011            7 :         myopt.topt.pager = 0;
    6012              : 
    6013              :     /*
    6014              :      * If there's a title in the user configuration, make sure we have room
    6015              :      * for it in the title buffer.  Allow 128 bytes for the timestamp plus 128
    6016              :      * bytes for the rest.
    6017              :      */
    6018            7 :     user_title = myopt.title;
    6019            7 :     title_len = (user_title ? strlen(user_title) : 0) + 256;
    6020            7 :     title = pg_malloc(title_len);
    6021              : 
    6022              :     /* Loop to run query and then sleep awhile */
    6023          408 :     while (!done)
    6024              :     {
    6025              :         time_t      timer;
    6026              :         char        timebuf[128];
    6027              : 
    6028              :         /*
    6029              :          * Prepare title for output.  Note that we intentionally include a
    6030              :          * newline at the end of the title; this is somewhat historical but it
    6031              :          * makes for reasonably nicely formatted output in simple cases.
    6032              :          */
    6033          408 :         timer = time(NULL);
    6034          408 :         strftime(timebuf, sizeof(timebuf), strftime_fmt, localtime(&timer));
    6035              : 
    6036          408 :         if (user_title)
    6037            0 :             snprintf(title, title_len, _("%s\t%s (every %gs)\n"),
    6038              :                      user_title, timebuf, sleep_ms / 1000.0);
    6039              :         else
    6040          408 :             snprintf(title, title_len, _("%s (every %gs)\n"),
    6041              :                      timebuf, sleep_ms / 1000.0);
    6042          408 :         myopt.title = title;
    6043              : 
    6044              :         /* Run the query and print out the result */
    6045          408 :         res = PSQLexecWatch(query_buf->data, &myopt, pagerpipe, min_rows);
    6046              : 
    6047              :         /*
    6048              :          * PSQLexecWatch handles the case where we can no longer repeat the
    6049              :          * query, and returns 0 or -1.
    6050              :          */
    6051          406 :         if (res <= 0)
    6052            5 :             break;
    6053              : 
    6054              :         /* If we have iteration count, check that it's not exceeded yet */
    6055          404 :         if (iter && (--iter <= 0))
    6056            3 :             break;
    6057              : 
    6058              :         /* Quit if error on pager pipe (probably pager has quit) */
    6059          401 :         if (pagerpipe && ferror(pagerpipe))
    6060            0 :             break;
    6061              : 
    6062              :         /* Tight loop, no wait needed */
    6063          401 :         if (sleep_ms == 0)
    6064            4 :             continue;
    6065              : 
    6066              : #ifdef WIN32
    6067              : 
    6068              :         /*
    6069              :          * Wait a while before running the query again.  Break the sleep into
    6070              :          * short intervals (at most 1s); that's probably unnecessary since
    6071              :          * pg_usleep is interruptible on Windows, but it's cheap insurance.
    6072              :          */
    6073              :         for (long i = sleep_ms; i > 0;)
    6074              :         {
    6075              :             long        s = Min(i, 1000L);
    6076              : 
    6077              :             pg_usleep(s * 1000L);
    6078              :             if (cancel_pressed)
    6079              :             {
    6080              :                 done = true;
    6081              :                 break;
    6082              :             }
    6083              :             i -= s;
    6084              :         }
    6085              : #else
    6086              :         /* sigwait() will handle SIGINT. */
    6087          397 :         sigprocmask(SIG_BLOCK, &sigint, NULL);
    6088          397 :         if (cancel_pressed)
    6089            0 :             done = true;
    6090              : 
    6091              :         /* Wait for SIGINT, SIGCHLD or SIGALRM. */
    6092          397 :         while (!done)
    6093              :         {
    6094              :             int         signal_received;
    6095              : 
    6096          397 :             errno = sigwait(&sigalrm_sigchld_sigint, &signal_received);
    6097          397 :             if (errno != 0)
    6098              :             {
    6099              :                 /* Some other signal arrived? */
    6100            0 :                 if (errno == EINTR)
    6101            0 :                     continue;
    6102              :                 else
    6103              :                 {
    6104            0 :                     pg_log_error("could not wait for signals: %m");
    6105            0 :                     done = true;
    6106          397 :                     break;
    6107              :                 }
    6108              :             }
    6109              :             /* On ^C or pager exit, it's time to stop running the query. */
    6110          397 :             if (signal_received == SIGINT || signal_received == SIGCHLD)
    6111            0 :                 done = true;
    6112              :             /* Otherwise, we must have SIGALRM.  Time to run the query again. */
    6113          397 :             break;
    6114              :         }
    6115              : 
    6116              :         /* Unblock SIGINT so that slow queries can be interrupted. */
    6117          397 :         sigprocmask(SIG_UNBLOCK, &sigint, NULL);
    6118              : #endif
    6119              :     }
    6120              : 
    6121            5 :     if (pagerpipe)
    6122              :     {
    6123            0 :         pclose(pagerpipe);
    6124            0 :         restore_sigpipe_trap();
    6125              :     }
    6126              :     else
    6127              :     {
    6128              :         /*
    6129              :          * If the terminal driver echoed "^C", libedit/libreadline might be
    6130              :          * confused about the cursor position.  Therefore, inject a newline
    6131              :          * before the next prompt is displayed.  We only do this when not
    6132              :          * using a pager, because pagers are expected to restore the screen to
    6133              :          * a sane state on exit.
    6134              :          */
    6135            5 :         fprintf(stdout, "\n");
    6136            5 :         fflush(stdout);
    6137              :     }
    6138              : 
    6139              : #ifndef WIN32
    6140              :     /* Disable the interval timer. */
    6141            5 :     memset(&interval, 0, sizeof(interval));
    6142            5 :     setitimer(ITIMER_REAL, &interval, NULL);
    6143              :     /* Unblock SIGINT, SIGCHLD and SIGALRM. */
    6144            5 :     sigprocmask(SIG_UNBLOCK, &sigalrm_sigchld_sigint, NULL);
    6145              : #endif
    6146              : 
    6147            5 :     pg_free(title);
    6148            5 :     return (res >= 0);
    6149              : }
    6150              : 
    6151              : /*
    6152              :  * a little code borrowed from PSQLexec() to manage ECHO_HIDDEN output.
    6153              :  * returns true unless we have ECHO_HIDDEN_NOEXEC.
    6154              :  */
    6155              : static bool
    6156          186 : echo_hidden_command(const char *query)
    6157              : {
    6158          186 :     if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF)
    6159              :     {
    6160            0 :         printf(_("/******** QUERY *********/\n"
    6161              :                  "%s\n"
    6162              :                  "/************************/\n\n"), query);
    6163            0 :         fflush(stdout);
    6164            0 :         if (pset.logfile)
    6165              :         {
    6166            0 :             fprintf(pset.logfile,
    6167            0 :                     _("/******** QUERY *********/\n"
    6168              :                       "%s\n"
    6169              :                       "/************************/\n\n"), query);
    6170            0 :             fflush(pset.logfile);
    6171              :         }
    6172              : 
    6173            0 :         if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC)
    6174            0 :             return false;
    6175              :     }
    6176          186 :     return true;
    6177              : }
    6178              : 
    6179              : /*
    6180              :  * Look up the object identified by obj_type and desc.  If successful,
    6181              :  * store its OID in *obj_oid and return true, else return false.
    6182              :  *
    6183              :  * Note that we'll fail if the object doesn't exist OR if there are multiple
    6184              :  * matching candidates OR if there's something syntactically wrong with the
    6185              :  * object description; unfortunately it can be hard to tell the difference.
    6186              :  */
    6187              : static bool
    6188           93 : lookup_object_oid(EditableObjectType obj_type, const char *desc,
    6189              :                   Oid *obj_oid)
    6190              : {
    6191           93 :     bool        result = true;
    6192           93 :     PQExpBuffer query = createPQExpBuffer();
    6193              :     PGresult   *res;
    6194              : 
    6195           93 :     switch (obj_type)
    6196              :     {
    6197           30 :         case EditableFunction:
    6198              : 
    6199              :             /*
    6200              :              * We have a function description, e.g. "x" or "x(int)".  Issue a
    6201              :              * query to retrieve the function's OID using a cast to regproc or
    6202              :              * regprocedure (as appropriate).
    6203              :              */
    6204           30 :             appendPQExpBufferStr(query, "SELECT ");
    6205           30 :             appendStringLiteralConn(query, desc, pset.db);
    6206           30 :             appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid",
    6207           30 :                               strchr(desc, '(') ? "regprocedure" : "regproc");
    6208           30 :             break;
    6209              : 
    6210           63 :         case EditableView:
    6211              : 
    6212              :             /*
    6213              :              * Convert view name (possibly schema-qualified) to OID.  Note:
    6214              :              * this code doesn't check if the relation is actually a view.
    6215              :              * We'll detect that in get_create_object_cmd().
    6216              :              */
    6217           63 :             appendPQExpBufferStr(query, "SELECT ");
    6218           63 :             appendStringLiteralConn(query, desc, pset.db);
    6219           63 :             appendPQExpBufferStr(query, "::pg_catalog.regclass::pg_catalog.oid");
    6220           63 :             break;
    6221              :     }
    6222              : 
    6223           93 :     if (!echo_hidden_command(query->data))
    6224              :     {
    6225            0 :         destroyPQExpBuffer(query);
    6226            0 :         return false;
    6227              :     }
    6228           93 :     res = PQexec(pset.db, query->data);
    6229           93 :     if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
    6230           93 :         *obj_oid = atooid(PQgetvalue(res, 0, 0));
    6231              :     else
    6232              :     {
    6233            0 :         minimal_error_message(res);
    6234            0 :         result = false;
    6235              :     }
    6236              : 
    6237           93 :     PQclear(res);
    6238           93 :     destroyPQExpBuffer(query);
    6239              : 
    6240           93 :     return result;
    6241              : }
    6242              : 
    6243              : /*
    6244              :  * Construct a "CREATE OR REPLACE ..." command that describes the specified
    6245              :  * database object.  If successful, the result is stored in buf.
    6246              :  */
    6247              : static bool
    6248           93 : get_create_object_cmd(EditableObjectType obj_type, Oid oid,
    6249              :                       PQExpBuffer buf)
    6250              : {
    6251           93 :     bool        result = true;
    6252           93 :     PQExpBuffer query = createPQExpBuffer();
    6253              :     PGresult   *res;
    6254              : 
    6255           93 :     switch (obj_type)
    6256              :     {
    6257           30 :         case EditableFunction:
    6258           30 :             printfPQExpBuffer(query,
    6259              :                               "SELECT pg_catalog.pg_get_functiondef(%u)",
    6260              :                               oid);
    6261           30 :             break;
    6262              : 
    6263           63 :         case EditableView:
    6264              : 
    6265              :             /*
    6266              :              * pg_get_viewdef() just prints the query, so we must prepend
    6267              :              * CREATE for ourselves.  We must fully qualify the view name to
    6268              :              * ensure the right view gets replaced.  Also, check relation kind
    6269              :              * to be sure it's a view.
    6270              :              *
    6271              :              * Starting with PG 9.4, views may have WITH [LOCAL|CASCADED]
    6272              :              * CHECK OPTION.  These are not part of the view definition
    6273              :              * returned by pg_get_viewdef() and so need to be retrieved
    6274              :              * separately.  Materialized views (introduced in 9.3) may have
    6275              :              * arbitrary storage parameter reloptions.
    6276              :              */
    6277           63 :             if (pset.sversion >= 90400)
    6278              :             {
    6279           63 :                 printfPQExpBuffer(query,
    6280              :                                   "SELECT nspname, relname, relkind, "
    6281              :                                   "pg_catalog.pg_get_viewdef(c.oid, true), "
    6282              :                                   "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
    6283              :                                   "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
    6284              :                                   "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption "
    6285              :                                   "FROM pg_catalog.pg_class c "
    6286              :                                   "LEFT JOIN pg_catalog.pg_namespace n "
    6287              :                                   "ON c.relnamespace = n.oid WHERE c.oid = %u",
    6288              :                                   oid);
    6289              :             }
    6290              :             else
    6291              :             {
    6292            0 :                 printfPQExpBuffer(query,
    6293              :                                   "SELECT nspname, relname, relkind, "
    6294              :                                   "pg_catalog.pg_get_viewdef(c.oid, true), "
    6295              :                                   "c.reloptions AS reloptions, "
    6296              :                                   "NULL AS checkoption "
    6297              :                                   "FROM pg_catalog.pg_class c "
    6298              :                                   "LEFT JOIN pg_catalog.pg_namespace n "
    6299              :                                   "ON c.relnamespace = n.oid WHERE c.oid = %u",
    6300              :                                   oid);
    6301              :             }
    6302           63 :             break;
    6303              :     }
    6304              : 
    6305           93 :     if (!echo_hidden_command(query->data))
    6306              :     {
    6307            0 :         destroyPQExpBuffer(query);
    6308            0 :         return false;
    6309              :     }
    6310           93 :     res = PQexec(pset.db, query->data);
    6311           93 :     if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
    6312              :     {
    6313           93 :         resetPQExpBuffer(buf);
    6314           93 :         switch (obj_type)
    6315              :         {
    6316           30 :             case EditableFunction:
    6317           30 :                 appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0));
    6318           30 :                 break;
    6319              : 
    6320           63 :             case EditableView:
    6321              :                 {
    6322           63 :                     char       *nspname = PQgetvalue(res, 0, 0);
    6323           63 :                     char       *relname = PQgetvalue(res, 0, 1);
    6324           63 :                     char       *relkind = PQgetvalue(res, 0, 2);
    6325           63 :                     char       *viewdef = PQgetvalue(res, 0, 3);
    6326           63 :                     char       *reloptions = PQgetvalue(res, 0, 4);
    6327           63 :                     char       *checkoption = PQgetvalue(res, 0, 5);
    6328              : 
    6329              :                     /*
    6330              :                      * If the backend ever supports CREATE OR REPLACE
    6331              :                      * MATERIALIZED VIEW, allow that here; but as of today it
    6332              :                      * does not, so editing a matview definition in this way
    6333              :                      * is impossible.
    6334              :                      */
    6335           63 :                     switch (relkind[0])
    6336              :                     {
    6337              : #ifdef NOT_USED
    6338              :                         case RELKIND_MATVIEW:
    6339              :                             appendPQExpBufferStr(buf, "CREATE OR REPLACE MATERIALIZED VIEW ");
    6340              :                             break;
    6341              : #endif
    6342           63 :                         case RELKIND_VIEW:
    6343           63 :                             appendPQExpBufferStr(buf, "CREATE OR REPLACE VIEW ");
    6344           63 :                             break;
    6345            0 :                         default:
    6346            0 :                             pg_log_error("\"%s.%s\" is not a view",
    6347              :                                          nspname, relname);
    6348            0 :                             result = false;
    6349            0 :                             break;
    6350              :                     }
    6351           63 :                     appendPQExpBuffer(buf, "%s.", fmtId(nspname));
    6352           63 :                     appendPQExpBufferStr(buf, fmtId(relname));
    6353              : 
    6354              :                     /* reloptions, if not an empty array "{}" */
    6355           63 :                     if (reloptions != NULL && strlen(reloptions) > 2)
    6356              :                     {
    6357            0 :                         appendPQExpBufferStr(buf, "\n WITH (");
    6358            0 :                         if (!appendReloptionsArray(buf, reloptions, "",
    6359              :                                                    pset.encoding,
    6360            0 :                                                    standard_strings()))
    6361              :                         {
    6362            0 :                             pg_log_error("could not parse reloptions array");
    6363            0 :                             result = false;
    6364              :                         }
    6365            0 :                         appendPQExpBufferChar(buf, ')');
    6366              :                     }
    6367              : 
    6368              :                     /* View definition from pg_get_viewdef (a SELECT query) */
    6369           63 :                     appendPQExpBuffer(buf, " AS\n%s", viewdef);
    6370              : 
    6371              :                     /* Get rid of the semicolon that pg_get_viewdef appends */
    6372           63 :                     if (buf->len > 0 && buf->data[buf->len - 1] == ';')
    6373           63 :                         buf->data[--(buf->len)] = '\0';
    6374              : 
    6375              :                     /* WITH [LOCAL|CASCADED] CHECK OPTION */
    6376           63 :                     if (checkoption && checkoption[0] != '\0')
    6377            0 :                         appendPQExpBuffer(buf, "\n WITH %s CHECK OPTION",
    6378              :                                           checkoption);
    6379              :                 }
    6380           63 :                 break;
    6381              :         }
    6382              :         /* Make sure result ends with a newline */
    6383           93 :         if (buf->len > 0 && buf->data[buf->len - 1] != '\n')
    6384           63 :             appendPQExpBufferChar(buf, '\n');
    6385              :     }
    6386              :     else
    6387              :     {
    6388            0 :         minimal_error_message(res);
    6389            0 :         result = false;
    6390              :     }
    6391              : 
    6392           93 :     PQclear(res);
    6393           93 :     destroyPQExpBuffer(query);
    6394              : 
    6395           93 :     return result;
    6396              : }
    6397              : 
    6398              : /*
    6399              :  * If the given argument of \ef or \ev ends with a line number, delete the line
    6400              :  * number from the argument string and return it as an integer.  (We need
    6401              :  * this kluge because we're too lazy to parse \ef's function or \ev's view
    6402              :  * argument carefully --- we just slop it up in OT_WHOLE_LINE mode.)
    6403              :  *
    6404              :  * Returns -1 if no line number is present, 0 on error, or a positive value
    6405              :  * on success.
    6406              :  */
    6407              : static int
    6408            0 : strip_lineno_from_objdesc(char *obj)
    6409              : {
    6410              :     char       *c;
    6411              :     int         lineno;
    6412              : 
    6413            0 :     if (!obj || obj[0] == '\0')
    6414            0 :         return -1;
    6415              : 
    6416            0 :     c = obj + strlen(obj) - 1;
    6417              : 
    6418              :     /*
    6419              :      * This business of parsing backwards is dangerous as can be in a
    6420              :      * multibyte environment: there is no reason to believe that we are
    6421              :      * looking at the first byte of a character, nor are we necessarily
    6422              :      * working in a "safe" encoding.  Fortunately the bitpatterns we are
    6423              :      * looking for are unlikely to occur as non-first bytes, but beware of
    6424              :      * trying to expand the set of cases that can be recognized.  We must
    6425              :      * guard the <ctype.h> macros by using isascii() first, too.
    6426              :      */
    6427              : 
    6428              :     /* skip trailing whitespace */
    6429            0 :     while (c > obj && isascii((unsigned char) *c) && isspace((unsigned char) *c))
    6430            0 :         c--;
    6431              : 
    6432              :     /* must have a digit as last non-space char */
    6433            0 :     if (c == obj || !isascii((unsigned char) *c) || !isdigit((unsigned char) *c))
    6434            0 :         return -1;
    6435              : 
    6436              :     /* find start of digit string */
    6437            0 :     while (c > obj && isascii((unsigned char) *c) && isdigit((unsigned char) *c))
    6438            0 :         c--;
    6439              : 
    6440              :     /* digits must be separated from object name by space or closing paren */
    6441              :     /* notice also that we are not allowing an empty object name ... */
    6442            0 :     if (c == obj || !isascii((unsigned char) *c) ||
    6443            0 :         !(isspace((unsigned char) *c) || *c == ')'))
    6444            0 :         return -1;
    6445              : 
    6446              :     /* parse digit string */
    6447            0 :     c++;
    6448            0 :     lineno = atoi(c);
    6449            0 :     if (lineno < 1)
    6450              :     {
    6451            0 :         pg_log_error("invalid line number: %s", c);
    6452            0 :         return 0;
    6453              :     }
    6454              : 
    6455              :     /* strip digit string from object name */
    6456            0 :     *c = '\0';
    6457              : 
    6458            0 :     return lineno;
    6459              : }
    6460              : 
    6461              : /*
    6462              :  * Count number of lines in the buffer.
    6463              :  * This is used to test if pager is needed or not.
    6464              :  */
    6465              : static int
    6466           93 : count_lines_in_buf(PQExpBuffer buf)
    6467              : {
    6468           93 :     int         lineno = 0;
    6469           93 :     const char *lines = buf->data;
    6470              : 
    6471         1448 :     while (*lines != '\0')
    6472              :     {
    6473         1355 :         lineno++;
    6474              :         /* find start of next line */
    6475         1355 :         lines = strchr(lines, '\n');
    6476         1355 :         if (!lines)
    6477            0 :             break;
    6478         1355 :         lines++;
    6479              :     }
    6480              : 
    6481           93 :     return lineno;
    6482              : }
    6483              : 
    6484              : /*
    6485              :  * Write text at *lines to output with line numbers.
    6486              :  *
    6487              :  * For functions, lineno "1" should correspond to the first line of the
    6488              :  * function body; lines before that are unnumbered.  We expect that
    6489              :  * pg_get_functiondef() will emit that on a line beginning with "AS ",
    6490              :  * "BEGIN ", or "RETURN ", and that there can be no such line before
    6491              :  * the real start of the function body.
    6492              :  *
    6493              :  * Caution: this scribbles on *lines.
    6494              :  */
    6495              : static void
    6496            9 : print_with_linenumbers(FILE *output, char *lines, bool is_func)
    6497              : {
    6498            9 :     bool        in_header = is_func;
    6499            9 :     int         lineno = 0;
    6500              : 
    6501           96 :     while (*lines != '\0')
    6502              :     {
    6503              :         char       *eol;
    6504              : 
    6505           87 :         if (in_header &&
    6506           45 :             (strncmp(lines, "AS ", 3) == 0 ||
    6507           45 :              strncmp(lines, "BEGIN ", 6) == 0 ||
    6508           39 :              strncmp(lines, "RETURN ", 7) == 0))
    6509            9 :             in_header = false;
    6510              : 
    6511              :         /* increment lineno only for body's lines */
    6512           87 :         if (!in_header)
    6513           51 :             lineno++;
    6514              : 
    6515              :         /* find and mark end of current line */
    6516           87 :         eol = strchr(lines, '\n');
    6517           87 :         if (eol != NULL)
    6518           87 :             *eol = '\0';
    6519              : 
    6520              :         /* show current line as appropriate */
    6521           87 :         if (in_header)
    6522           36 :             fprintf(output, "        %s\n", lines);
    6523              :         else
    6524           51 :             fprintf(output, "%-7d %s\n", lineno, lines);
    6525              : 
    6526              :         /* advance to next line, if any */
    6527           87 :         if (eol == NULL)
    6528            0 :             break;
    6529           87 :         lines = ++eol;
    6530              :     }
    6531            9 : }
    6532              : 
    6533              : /*
    6534              :  * Report just the primary error; this is to avoid cluttering the output
    6535              :  * with, for instance, a redisplay of the internally generated query
    6536              :  */
    6537              : static void
    6538            0 : minimal_error_message(PGresult *res)
    6539              : {
    6540              :     PQExpBuffer msg;
    6541              :     const char *fld;
    6542              : 
    6543            0 :     msg = createPQExpBuffer();
    6544              : 
    6545            0 :     fld = PQresultErrorField(res, PG_DIAG_SEVERITY);
    6546            0 :     if (fld)
    6547            0 :         printfPQExpBuffer(msg, "%s:  ", fld);
    6548              :     else
    6549            0 :         printfPQExpBuffer(msg, "ERROR:  ");
    6550            0 :     fld = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
    6551            0 :     if (fld)
    6552            0 :         appendPQExpBufferStr(msg, fld);
    6553              :     else
    6554            0 :         appendPQExpBufferStr(msg, "(not available)");
    6555            0 :     appendPQExpBufferChar(msg, '\n');
    6556              : 
    6557            0 :     pg_log_error("%s", msg->data);
    6558              : 
    6559            0 :     destroyPQExpBuffer(msg);
    6560            0 : }
        

Generated by: LCOV version 2.0-1