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

Generated by: LCOV version 2.0-1