LCOV - differential code coverage report
Current view: top level - src/bin/psql - input.c (source / functions) Coverage Total Hit LBC UIC UBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 78.0 % 132 103 1 16 12 59 1 43 17 57 2
Current Date: 2023-04-08 15:15:32 Functions: 90.0 % 10 9 1 6 1 2 1 6
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a