LCOV - code coverage report
Current view: top level - src/bin/psql - input.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 99 132 75.0 %
Date: 2024-12-12 20:14:52 Functions: 9 10 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * psql - the PostgreSQL interactive terminal
       3             :  *
       4             :  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
       5             :  *
       6             :  * src/bin/psql/input.c
       7             :  */
       8             : #include "postgres_fe.h"
       9             : 
      10             : #ifndef WIN32
      11             : #include <unistd.h>
      12             : #endif
      13             : #include <fcntl.h>
      14             : #include <limits.h>
      15             : 
      16             : #include "common.h"
      17             : #include "common/logging.h"
      18             : #include "input.h"
      19             : #include "settings.h"
      20             : #include "tab-complete.h"
      21             : 
      22             : #ifndef WIN32
      23             : #define PSQLHISTORY ".psql_history"
      24             : #else
      25             : #define PSQLHISTORY "psql_history"
      26             : #endif
      27             : 
      28             : /* Runtime options for turning off readline and history */
      29             : /* (of course there is no runtime command for doing that :) */
      30             : #ifdef USE_READLINE
      31             : static bool useReadline;
      32             : static bool useHistory;
      33             : 
      34             : static char *psql_history;
      35             : 
      36             : static int  history_lines_added;
      37             : 
      38             : 
      39             : /*
      40             :  *  Preserve newlines in saved queries by mapping '\n' to NL_IN_HISTORY
      41             :  *
      42             :  *  It is assumed NL_IN_HISTORY will never be entered by the user
      43             :  *  nor appear inside a multi-byte string.  0x00 is not properly
      44             :  *  handled by the readline routines so it can not be used
      45             :  *  for this purpose.
      46             :  */
      47             : #define NL_IN_HISTORY   0x01
      48             : #endif
      49             : 
      50             : static void finishInput(void);
      51             : 
      52             : 
      53             : /*
      54             :  * gets_interactive()
      55             :  *
      56             :  * Gets a line of interactive input, using readline if desired.
      57             :  *
      58             :  * prompt: the prompt string to be used
      59             :  * query_buf: buffer containing lines already read in the current command
      60             :  * (query_buf is not modified here, but may be consulted for tab completion)
      61             :  *
      62             :  * The result is a malloc'd string.
      63             :  *
      64             :  * Caller *must* have set up sigint_interrupt_jmp before calling.
      65             :  */
      66             : char *
      67         108 : gets_interactive(const char *prompt, PQExpBuffer query_buf)
      68             : {
      69             : #ifdef USE_READLINE
      70         108 :     if (useReadline)
      71             :     {
      72             :         char       *result;
      73             : 
      74             :         /*
      75             :          * Some versions of readline don't notice SIGWINCH signals that arrive
      76             :          * when not actively reading input.  The simplest fix is to always
      77             :          * re-read the terminal size.  This leaves a window for SIGWINCH to be
      78             :          * missed between here and where readline() enables libreadline's
      79             :          * signal handler, but that's probably short enough to be ignored.
      80             :          */
      81             : #ifdef HAVE_RL_RESET_SCREEN_SIZE
      82         108 :         rl_reset_screen_size();
      83             : #endif
      84             : 
      85             :         /* Make current query_buf available to tab completion callback */
      86         108 :         tab_completion_query_buf = query_buf;
      87             : 
      88             :         /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
      89         108 :         sigint_interrupt_enabled = true;
      90             : 
      91         108 :         result = readline(prompt);
      92             : 
      93             :         /* Disable SIGINT again */
      94         108 :         sigint_interrupt_enabled = false;
      95             : 
      96             :         /* Pure neatnik-ism */
      97         108 :         tab_completion_query_buf = NULL;
      98             : 
      99         108 :         return result;
     100             :     }
     101             : #endif
     102             : 
     103           0 :     fputs(prompt, stdout);
     104           0 :     fflush(stdout);
     105           0 :     return gets_fromFile(stdin);
     106             : }
     107             : 
     108             : 
     109             : /*
     110             :  * Append the line to the history buffer, making sure there is a trailing '\n'
     111             :  */
     112             : void
     113          96 : pg_append_history(const char *s, PQExpBuffer history_buf)
     114             : {
     115             : #ifdef USE_READLINE
     116          96 :     if (useHistory && s)
     117             :     {
     118          96 :         appendPQExpBufferStr(history_buf, s);
     119          96 :         if (!s[0] || s[strlen(s) - 1] != '\n')
     120          96 :             appendPQExpBufferChar(history_buf, '\n');
     121             :     }
     122             : #endif
     123          96 : }
     124             : 
     125             : 
     126             : /*
     127             :  * Emit accumulated history entry to readline's history mechanism,
     128             :  * then reset the buffer to empty.
     129             :  *
     130             :  * Note: we write nothing if history_buf is empty, so extra calls to this
     131             :  * function don't hurt.  There must have been at least one line added by
     132             :  * pg_append_history before we'll do anything.
     133             :  */
     134             : void
     135         314 : pg_send_history(PQExpBuffer history_buf)
     136             : {
     137             : #ifdef USE_READLINE
     138             :     static char *prev_hist = NULL;
     139             : 
     140         314 :     char       *s = history_buf->data;
     141             :     int         i;
     142             : 
     143             :     /* Trim any trailing \n's (OK to scribble on history_buf) */
     144         408 :     for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--)
     145             :         ;
     146         314 :     s[i + 1] = '\0';
     147             : 
     148         314 :     if (useHistory && s[0])
     149             :     {
     150          94 :         if (((pset.histcontrol & hctl_ignorespace) &&
     151           0 :              s[0] == ' ') ||
     152          94 :             ((pset.histcontrol & hctl_ignoredups) &&
     153           0 :              prev_hist && strcmp(s, prev_hist) == 0))
     154             :         {
     155             :             /* Ignore this line as far as history is concerned */
     156             :         }
     157             :         else
     158             :         {
     159             :             /* Save each previous line for ignoredups processing */
     160          94 :             free(prev_hist);
     161          94 :             prev_hist = pg_strdup(s);
     162             :             /* And send it to readline */
     163          94 :             add_history(s);
     164             :             /* Count lines added to history for use later */
     165          94 :             history_lines_added++;
     166             :         }
     167             :     }
     168             : 
     169         314 :     resetPQExpBuffer(history_buf);
     170             : #endif
     171         314 : }
     172             : 
     173             : 
     174             : /*
     175             :  * gets_fromFile
     176             :  *
     177             :  * Gets a line of noninteractive input from a file (which could be stdin).
     178             :  * The result is a malloc'd string, or NULL on EOF or input error.
     179             :  *
     180             :  * Caller *must* have set up sigint_interrupt_jmp before calling.
     181             :  *
     182             :  * Note: we re-use a static PQExpBuffer for each call.  This is to avoid
     183             :  * leaking memory if interrupted by SIGINT.
     184             :  */
     185             : char *
     186      772814 : gets_fromFile(FILE *source)
     187             : {
     188             :     static PQExpBuffer buffer = NULL;
     189             : 
     190             :     char        line[1024];
     191             : 
     192      772814 :     if (buffer == NULL)         /* first time through? */
     193       15486 :         buffer = createPQExpBuffer();
     194             :     else
     195      757328 :         resetPQExpBuffer(buffer);
     196             : 
     197             :     for (;;)
     198       12658 :     {
     199             :         char       *result;
     200             : 
     201             :         /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
     202      785472 :         sigint_interrupt_enabled = true;
     203             : 
     204             :         /* Get some data */
     205      785472 :         result = fgets(line, sizeof(line), source);
     206             : 
     207             :         /* Disable SIGINT again */
     208      785472 :         sigint_interrupt_enabled = false;
     209             : 
     210             :         /* EOF or error? */
     211      785472 :         if (result == NULL)
     212             :         {
     213       27846 :             if (ferror(source))
     214             :             {
     215           0 :                 pg_log_error("could not read from input file: %m");
     216           0 :                 return NULL;
     217             :             }
     218       27846 :             break;
     219             :         }
     220             : 
     221      757626 :         appendPQExpBufferStr(buffer, line);
     222             : 
     223      757626 :         if (PQExpBufferBroken(buffer))
     224             :         {
     225           0 :             pg_log_error("out of memory");
     226           0 :             return NULL;
     227             :         }
     228             : 
     229             :         /* EOL? */
     230      757626 :         if (buffer->len > 0 && buffer->data[buffer->len - 1] == '\n')
     231             :         {
     232      744968 :             buffer->data[buffer->len - 1] = '\0';
     233      744968 :             return pg_strdup(buffer->data);
     234             :         }
     235             :     }
     236             : 
     237       27846 :     if (buffer->len > 0)      /* EOF after reading some bufferload(s) */
     238       12530 :         return pg_strdup(buffer->data);
     239             : 
     240             :     /* EOF, so return null */
     241       15316 :     return NULL;
     242             : }
     243             : 
     244             : 
     245             : #ifdef USE_READLINE
     246             : 
     247             : /*
     248             :  * Macros to iterate over each element of the history list in order
     249             :  *
     250             :  * You would think this would be simple enough, but in its inimitable fashion
     251             :  * libedit has managed to break it: in libreadline we must use next_history()
     252             :  * to go from oldest to newest, but in libedit we must use previous_history().
     253             :  * To detect what to do, we make a trial call of previous_history(): if it
     254             :  * fails, then either next_history() is what to use, or there's zero or one
     255             :  * history entry so that it doesn't matter which direction we go.
     256             :  *
     257             :  * In case that wasn't disgusting enough: the code below is not as obvious as
     258             :  * it might appear.  In some libedit releases history_set_pos(0) fails until
     259             :  * at least one add_history() call has been done.  This is not an issue for
     260             :  * printHistory() or encode_history(), which cannot be invoked before that has
     261             :  * happened.  In decode_history(), that's not so, and what actually happens is
     262             :  * that we are sitting on the newest entry to start with, previous_history()
     263             :  * fails, and we iterate over all the entries using next_history().  So the
     264             :  * decode_history() loop iterates over the entries in the wrong order when
     265             :  * using such a libedit release, and if there were another attempt to use
     266             :  * BEGIN_ITERATE_HISTORY() before some add_history() call had happened, it
     267             :  * wouldn't work.  Fortunately we don't care about either of those things.
     268             :  *
     269             :  * Usage pattern is:
     270             :  *
     271             :  *      BEGIN_ITERATE_HISTORY(varname);
     272             :  *      {
     273             :  *          loop body referencing varname->line;
     274             :  *      }
     275             :  *      END_ITERATE_HISTORY();
     276             :  */
     277             : #define BEGIN_ITERATE_HISTORY(VARNAME) \
     278             :     do { \
     279             :         HIST_ENTRY *VARNAME; \
     280             :         bool        use_prev_; \
     281             :         \
     282             :         history_set_pos(0); \
     283             :         use_prev_ = (previous_history() != NULL); \
     284             :         history_set_pos(0); \
     285             :         for (VARNAME = current_history(); VARNAME != NULL; \
     286             :              VARNAME = use_prev_ ? previous_history() : next_history()) \
     287             :         { \
     288             :             (void) 0
     289             : 
     290             : #define END_ITERATE_HISTORY() \
     291             :         } \
     292             :     } while(0)
     293             : 
     294             : 
     295             : /*
     296             :  * Convert newlines to NL_IN_HISTORY for safe saving in readline history file
     297             :  */
     298             : static void
     299           2 : encode_history(void)
     300             : {
     301          78 :     BEGIN_ITERATE_HISTORY(cur_hist);
     302             :     {
     303             :         char       *cur_ptr;
     304             : 
     305             :         /* some platforms declare HIST_ENTRY.line as const char * */
     306        2128 :         for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
     307             :         {
     308        2052 :             if (*cur_ptr == '\n')
     309           2 :                 *cur_ptr = NL_IN_HISTORY;
     310             :         }
     311             :     }
     312             :     END_ITERATE_HISTORY();
     313           2 : }
     314             : 
     315             : /*
     316             :  * Reverse the above encoding
     317             :  */
     318             : static void
     319           4 : decode_history(void)
     320             : {
     321           4 :     BEGIN_ITERATE_HISTORY(cur_hist);
     322             :     {
     323             :         char       *cur_ptr;
     324             : 
     325             :         /* some platforms declare HIST_ENTRY.line as const char * */
     326           0 :         for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
     327             :         {
     328           0 :             if (*cur_ptr == NL_IN_HISTORY)
     329           0 :                 *cur_ptr = '\n';
     330             :         }
     331             :     }
     332             :     END_ITERATE_HISTORY();
     333           4 : }
     334             : #endif                          /* USE_READLINE */
     335             : 
     336             : 
     337             : /*
     338             :  * Put any startup stuff related to input in here. It's good to maintain
     339             :  * abstraction this way.
     340             :  *
     341             :  * The only "flag" right now is 1 for use readline & history.
     342             :  */
     343             : void
     344           4 : initializeInput(int flags)
     345             : {
     346             : #ifdef USE_READLINE
     347           4 :     if (flags & 1)
     348             :     {
     349             :         const char *histfile;
     350             :         char        home[MAXPGPATH];
     351             : 
     352           4 :         useReadline = true;
     353             : 
     354             :         /* set appropriate values for Readline's global variables */
     355           4 :         initialize_readline();
     356             : 
     357             : #ifdef HAVE_RL_VARIABLE_BIND
     358             :         /* set comment-begin to a useful value for SQL */
     359           4 :         (void) rl_variable_bind("comment-begin", "-- ");
     360             : #endif
     361             : 
     362             :         /* this reads ~/.inputrc, so do it after rl_variable_bind */
     363           4 :         rl_initialize();
     364             : 
     365           4 :         useHistory = true;
     366           4 :         using_history();
     367           4 :         history_lines_added = 0;
     368             : 
     369           4 :         histfile = GetVariable(pset.vars, "HISTFILE");
     370             : 
     371           4 :         if (histfile == NULL)
     372             :         {
     373             :             char       *envhist;
     374             : 
     375           4 :             envhist = getenv("PSQL_HISTORY");
     376           4 :             if (envhist != NULL && strlen(envhist) > 0)
     377           4 :                 histfile = envhist;
     378             :         }
     379             : 
     380           4 :         if (histfile == NULL)
     381             :         {
     382           0 :             if (get_home_path(home))
     383           0 :                 psql_history = psprintf("%s/%s", home, PSQLHISTORY);
     384             :         }
     385             :         else
     386             :         {
     387           4 :             psql_history = pg_strdup(histfile);
     388           4 :             expand_tilde(&psql_history);
     389             :         }
     390             : 
     391           4 :         if (psql_history)
     392             :         {
     393           4 :             read_history(psql_history);
     394           4 :             decode_history();
     395             :         }
     396             :     }
     397             : #endif
     398             : 
     399           4 :     atexit(finishInput);
     400           4 : }
     401             : 
     402             : 
     403             : /*
     404             :  * This function saves the readline history when psql exits.
     405             :  *
     406             :  * fname: pathname of history file.  (Should really be "const char *",
     407             :  * but some ancient versions of readline omit the const-decoration.)
     408             :  *
     409             :  * max_lines: if >= 0, limit history file to that many entries.
     410             :  */
     411             : #ifdef USE_READLINE
     412             : static bool
     413           4 : saveHistory(char *fname, int max_lines)
     414             : {
     415             :     int         errnum;
     416             : 
     417             :     /*
     418             :      * Suppressing the write attempt when HISTFILE is set to /dev/null may
     419             :      * look like a negligible optimization, but it's necessary on e.g. macOS,
     420             :      * where write_history will fail because it tries to chmod the target
     421             :      * file.
     422             :      */
     423           4 :     if (strcmp(fname, DEVNULL) != 0)
     424             :     {
     425             :         /*
     426             :          * Encode \n, since otherwise readline will reload multiline history
     427             :          * entries as separate lines.  (libedit doesn't really need this, but
     428             :          * we do it anyway since it's too hard to tell which implementation we
     429             :          * are using.)
     430             :          */
     431           2 :         encode_history();
     432             : 
     433             :         /*
     434             :          * On newer versions of libreadline, truncate the history file as
     435             :          * needed and then append what we've added.  This avoids overwriting
     436             :          * history from other concurrent sessions (although there are still
     437             :          * race conditions when two sessions exit at about the same time). If
     438             :          * we don't have those functions, fall back to write_history().
     439             :          */
     440             : #if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY)
     441             :         {
     442             :             int         nlines;
     443             :             int         fd;
     444             : 
     445             :             /* truncate previous entries if needed */
     446           2 :             if (max_lines >= 0)
     447             :             {
     448           2 :                 nlines = Max(max_lines - history_lines_added, 0);
     449           2 :                 (void) history_truncate_file(fname, nlines);
     450             :             }
     451             :             /* append_history fails if file doesn't already exist :-( */
     452           2 :             fd = open(fname, O_CREAT | O_WRONLY | PG_BINARY, 0600);
     453           2 :             if (fd >= 0)
     454           2 :                 close(fd);
     455             :             /* append the appropriate number of lines */
     456           2 :             if (max_lines >= 0)
     457           2 :                 nlines = Min(max_lines, history_lines_added);
     458             :             else
     459           0 :                 nlines = history_lines_added;
     460           2 :             errnum = append_history(nlines, fname);
     461           2 :             if (errnum == 0)
     462           2 :                 return true;
     463             :         }
     464             : #else                           /* don't have append support */
     465             :         {
     466             :             /* truncate what we have ... */
     467             :             if (max_lines >= 0)
     468             :                 stifle_history(max_lines);
     469             :             /* ... and overwrite file.  Tough luck for concurrent sessions. */
     470             :             errnum = write_history(fname);
     471             :             if (errnum == 0)
     472             :                 return true;
     473             :         }
     474             : #endif
     475             : 
     476           0 :         pg_log_error("could not save history to file \"%s\": %m", fname);
     477             :     }
     478           2 :     return false;
     479             : }
     480             : #endif
     481             : 
     482             : 
     483             : 
     484             : /*
     485             :  * Print history to the specified file, or to the console if fname is NULL
     486             :  * (psql \s command)
     487             :  *
     488             :  * We used to use saveHistory() for this purpose, but that doesn't permit
     489             :  * use of a pager; moreover libedit's implementation behaves incompatibly
     490             :  * (preferring to encode its output) and may fail outright when the target
     491             :  * file is specified as /dev/tty.
     492             :  */
     493             : bool
     494           0 : printHistory(const char *fname, unsigned short int pager)
     495             : {
     496             : #ifdef USE_READLINE
     497             :     FILE       *output;
     498             :     bool        is_pager;
     499             : 
     500           0 :     if (!useHistory)
     501           0 :         return false;
     502             : 
     503           0 :     if (fname == NULL)
     504             :     {
     505             :         /* use pager, if enabled, when printing to console */
     506           0 :         output = PageOutput(INT_MAX, pager ? &(pset.popt.topt) : NULL);
     507           0 :         is_pager = true;
     508             :     }
     509             :     else
     510             :     {
     511           0 :         output = fopen(fname, "w");
     512           0 :         if (output == NULL)
     513             :         {
     514           0 :             pg_log_error("could not save history to file \"%s\": %m", fname);
     515           0 :             return false;
     516             :         }
     517           0 :         is_pager = false;
     518             :     }
     519             : 
     520           0 :     BEGIN_ITERATE_HISTORY(cur_hist);
     521             :     {
     522           0 :         fprintf(output, "%s\n", cur_hist->line);
     523             :     }
     524             :     END_ITERATE_HISTORY();
     525             : 
     526           0 :     if (is_pager)
     527           0 :         ClosePager(output);
     528             :     else
     529           0 :         fclose(output);
     530             : 
     531           0 :     return true;
     532             : #else
     533             :     pg_log_error("history is not supported by this installation");
     534             :     return false;
     535             : #endif
     536             : }
     537             : 
     538             : 
     539             : static void
     540           4 : finishInput(void)
     541             : {
     542             : #ifdef USE_READLINE
     543           4 :     if (useHistory && psql_history)
     544             :     {
     545           4 :         (void) saveHistory(psql_history, pset.histsize);
     546           4 :         free(psql_history);
     547           4 :         psql_history = NULL;
     548             :     }
     549             : #endif
     550           4 : }

Generated by: LCOV version 1.14