LCOV - code coverage report
Current view: top level - src/bin/psql - input.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 75.0 % 132 99
Test Date: 2026-03-01 18:15:11 Functions: 90.0 % 10 9
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/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           67 : gets_interactive(const char *prompt, PQExpBuffer query_buf)
      68              : {
      69              : #ifdef USE_READLINE
      70           67 :     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           67 :         rl_reset_screen_size();
      83              : #endif
      84              : 
      85              :         /* Make current query_buf available to tab completion callback */
      86           67 :         tab_completion_query_buf = query_buf;
      87              : 
      88              :         /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
      89           67 :         sigint_interrupt_enabled = true;
      90              : 
      91           67 :         result = readline(prompt);
      92              : 
      93              :         /* Disable SIGINT again */
      94           67 :         sigint_interrupt_enabled = false;
      95              : 
      96              :         /* Pure neatnik-ism */
      97           67 :         tab_completion_query_buf = NULL;
      98              : 
      99           67 :         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           61 : pg_append_history(const char *s, PQExpBuffer history_buf)
     114              : {
     115              : #ifdef USE_READLINE
     116           61 :     if (useHistory && s)
     117              :     {
     118           61 :         appendPQExpBufferStr(history_buf, s);
     119           61 :         if (!s[0] || s[strlen(s) - 1] != '\n')
     120           61 :             appendPQExpBufferChar(history_buf, '\n');
     121              :     }
     122              : #endif
     123           61 : }
     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          202 : pg_send_history(PQExpBuffer history_buf)
     136              : {
     137              : #ifdef USE_READLINE
     138              :     static char *prev_hist = NULL;
     139              : 
     140          202 :     char       *s = history_buf->data;
     141              :     int         i;
     142              : 
     143              :     /* Trim any trailing \n's (OK to scribble on history_buf) */
     144          262 :     for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--)
     145              :         ;
     146          202 :     s[i + 1] = '\0';
     147              : 
     148          202 :     if (useHistory && s[0])
     149              :     {
     150           60 :         if (((pset.histcontrol & hctl_ignorespace) &&
     151            0 :              s[0] == ' ') ||
     152           60 :             ((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           60 :             free(prev_hist);
     161           60 :             prev_hist = pg_strdup(s);
     162              :             /* And send it to readline */
     163           60 :             add_history(s);
     164              :             /* Count lines added to history for use later */
     165           60 :             history_lines_added++;
     166              :         }
     167              :     }
     168              : 
     169          202 :     resetPQExpBuffer(history_buf);
     170              : #endif
     171          202 : }
     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       478166 : gets_fromFile(FILE *source)
     187              : {
     188              :     static PQExpBuffer buffer = NULL;
     189              : 
     190              :     char        line[1024];
     191              : 
     192       478166 :     if (buffer == NULL)         /* first time through? */
     193         9600 :         buffer = createPQExpBuffer();
     194              :     else
     195       468566 :         resetPQExpBuffer(buffer);
     196              : 
     197              :     for (;;)
     198         7873 :     {
     199              :         char       *result;
     200              : 
     201              :         /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
     202       486039 :         sigint_interrupt_enabled = true;
     203              : 
     204              :         /* Get some data */
     205       486039 :         result = fgets(line, sizeof(line), source);
     206              : 
     207              :         /* Disable SIGINT again */
     208       486039 :         sigint_interrupt_enabled = false;
     209              : 
     210              :         /* EOF or error? */
     211       486039 :         if (result == NULL)
     212              :         {
     213        17149 :             if (ferror(source))
     214              :             {
     215            0 :                 pg_log_error("could not read from input file: %m");
     216            0 :                 return NULL;
     217              :             }
     218        17149 :             break;
     219              :         }
     220              : 
     221       468890 :         appendPQExpBufferStr(buffer, line);
     222              : 
     223       468890 :         if (PQExpBufferBroken(buffer))
     224              :         {
     225            0 :             pg_log_error("out of memory");
     226            0 :             return NULL;
     227              :         }
     228              : 
     229              :         /* EOL? */
     230       468890 :         if (buffer->len > 0 && buffer->data[buffer->len - 1] == '\n')
     231              :         {
     232       461017 :             buffer->data[buffer->len - 1] = '\0';
     233       461017 :             return pg_strdup(buffer->data);
     234              :         }
     235              :     }
     236              : 
     237        17149 :     if (buffer->len > 0)      /* EOF after reading some bufferload(s) */
     238         7800 :         return pg_strdup(buffer->data);
     239              : 
     240              :     /* EOF, so return null */
     241         9349 :     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            1 : encode_history(void)
     300              : {
     301           40 :     BEGIN_ITERATE_HISTORY(cur_hist);
     302              :     {
     303              :         char       *cur_ptr;
     304              : 
     305              :         /* some platforms declare HIST_ENTRY.line as const char * */
     306         1097 :         for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
     307              :         {
     308         1058 :             if (*cur_ptr == '\n')
     309            1 :                 *cur_ptr = NL_IN_HISTORY;
     310              :         }
     311              :     }
     312              :     END_ITERATE_HISTORY();
     313            1 : }
     314              : 
     315              : /*
     316              :  * Reverse the above encoding
     317              :  */
     318              : static void
     319            3 : decode_history(void)
     320              : {
     321            3 :     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            3 : }
     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            3 : initializeInput(int flags)
     345              : {
     346              : #ifdef USE_READLINE
     347            3 :     if (flags & 1)
     348              :     {
     349              :         const char *histfile;
     350              :         char        home[MAXPGPATH];
     351              : 
     352            3 :         useReadline = true;
     353              : 
     354              :         /* set appropriate values for Readline's global variables */
     355            3 :         initialize_readline();
     356              : 
     357              : #ifdef HAVE_RL_VARIABLE_BIND
     358              :         /* set comment-begin to a useful value for SQL */
     359            3 :         (void) rl_variable_bind("comment-begin", "-- ");
     360              : #endif
     361              : 
     362              :         /* this reads ~/.inputrc, so do it after rl_variable_bind */
     363            3 :         rl_initialize();
     364              : 
     365            3 :         useHistory = true;
     366            3 :         using_history();
     367            3 :         history_lines_added = 0;
     368              : 
     369            3 :         histfile = GetVariable(pset.vars, "HISTFILE");
     370              : 
     371            3 :         if (histfile == NULL)
     372              :         {
     373              :             char       *envhist;
     374              : 
     375            3 :             envhist = getenv("PSQL_HISTORY");
     376            3 :             if (envhist != NULL && strlen(envhist) > 0)
     377            3 :                 histfile = envhist;
     378              :         }
     379              : 
     380            3 :         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            3 :             psql_history = pg_strdup(histfile);
     388            3 :             expand_tilde(&psql_history);
     389              :         }
     390              : 
     391            3 :         if (psql_history)
     392              :         {
     393            3 :             read_history(psql_history);
     394            3 :             decode_history();
     395              :         }
     396              :     }
     397              : #endif
     398              : 
     399            3 :     atexit(finishInput);
     400            3 : }
     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            3 : 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            3 :     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            1 :         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            1 :             if (max_lines >= 0)
     447              :             {
     448            1 :                 nlines = Max(max_lines - history_lines_added, 0);
     449            1 :                 (void) history_truncate_file(fname, nlines);
     450              :             }
     451              :             /* append_history fails if file doesn't already exist :-( */
     452            1 :             fd = open(fname, O_CREAT | O_WRONLY | PG_BINARY, 0600);
     453            1 :             if (fd >= 0)
     454            1 :                 close(fd);
     455              :             /* append the appropriate number of lines */
     456            1 :             if (max_lines >= 0)
     457            1 :                 nlines = Min(max_lines, history_lines_added);
     458              :             else
     459            0 :                 nlines = history_lines_added;
     460            1 :             errnum = append_history(nlines, fname);
     461            1 :             if (errnum == 0)
     462            1 :                 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            3 : finishInput(void)
     541              : {
     542              : #ifdef USE_READLINE
     543            3 :     if (useHistory && psql_history)
     544              :     {
     545            3 :         (void) saveHistory(psql_history, pset.histsize);
     546            3 :         free(psql_history);
     547            3 :         psql_history = NULL;
     548              :     }
     549              : #endif
     550            3 : }
        

Generated by: LCOV version 2.0-1