Age Owner Branch data TLA 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 *
3038 tgl@sss.pgh.pa.us 67 :CBC 54 : gets_interactive(const char *prompt, PQExpBuffer query_buf)
68 : : {
69 : : #ifdef USE_READLINE
8928 bruce@momjian.us 70 [ + - ]: 54 : 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
3042 tgl@sss.pgh.pa.us 82 : 54 : rl_reset_screen_size();
83 : : #endif
84 : :
85 : : /* Make current query_buf available to tab completion callback */
3038 86 : 54 : tab_completion_query_buf = query_buf;
87 : :
88 : : /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
6514 89 : 54 : sigint_interrupt_enabled = true;
90 : :
192 peter@eisentraut.org 91 :GNC 54 : result = readline(prompt);
92 : :
93 : : /* Disable SIGINT again */
6514 tgl@sss.pgh.pa.us 94 :CBC 54 : sigint_interrupt_enabled = false;
95 : :
96 : : /* Pure neatnik-ism */
3038 97 : 54 : tab_completion_query_buf = NULL;
98 : :
6514 99 : 54 : return result;
100 : : }
101 : : #endif
102 : :
6517 tgl@sss.pgh.pa.us 103 :UBC 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
6517 tgl@sss.pgh.pa.us 113 :CBC 44 : pg_append_history(const char *s, PQExpBuffer history_buf)
114 : : {
115 : : #ifdef USE_READLINE
4534 rhaas@postgresql.org 116 [ + - + - ]: 44 : if (useHistory && s)
117 : : {
6517 tgl@sss.pgh.pa.us 118 : 44 : appendPQExpBufferStr(history_buf, s);
4534 rhaas@postgresql.org 119 [ + - + - ]: 44 : if (!s[0] || s[strlen(s) - 1] != '\n')
6637 bruce@momjian.us 120 : 44 : appendPQExpBufferChar(history_buf, '\n');
121 : : }
122 : : #endif
123 : 44 : }
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
6517 tgl@sss.pgh.pa.us 135 : 148 : pg_send_history(PQExpBuffer history_buf)
136 : : {
137 : : #ifdef USE_READLINE
138 : : static char *prev_hist = NULL;
139 : :
6402 bruce@momjian.us 140 : 148 : char *s = history_buf->data;
141 : : int i;
142 : :
143 : : /* Trim any trailing \n's (OK to scribble on history_buf) */
6321 tgl@sss.pgh.pa.us 144 [ + + + + ]: 191 : for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--)
145 : : ;
146 : 148 : s[i + 1] = '\0';
147 : :
6517 148 [ + + + + ]: 148 : if (useHistory && s[0])
149 : : {
6438 150 [ - + ]: 43 : if (((pset.histcontrol & hctl_ignorespace) &&
6438 tgl@sss.pgh.pa.us 151 [ # # ]:UBC 0 : s[0] == ' ') ||
6438 tgl@sss.pgh.pa.us 152 [ - + - - ]:CBC 43 : ((pset.histcontrol & hctl_ignoredups) &&
6438 tgl@sss.pgh.pa.us 153 [ # # ]:UBC 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 */
668 peter@eisentraut.org 160 :CBC 43 : free(prev_hist);
7385 neilc@samurai.com 161 : 43 : prev_hist = pg_strdup(s);
162 : : /* And send it to readline */
8768 bruce@momjian.us 163 : 43 : add_history(s);
164 : : /* Count lines added to history for use later */
5327 tgl@sss.pgh.pa.us 165 : 43 : history_lines_added++;
166 : : }
167 : : }
168 : :
6517 169 : 148 : resetPQExpBuffer(history_buf);
170 : : #endif
6637 bruce@momjian.us 171 : 148 : }
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 *
8928 186 : 375523 : gets_fromFile(FILE *source)
187 : : {
188 : : static PQExpBuffer buffer = NULL;
189 : :
190 : : char line[1024];
191 : :
6514 tgl@sss.pgh.pa.us 192 [ + + ]: 375523 : if (buffer == NULL) /* first time through? */
193 : 7719 : buffer = createPQExpBuffer();
194 : : else
195 : 367804 : resetPQExpBuffer(buffer);
196 : :
197 : : for (;;)
8928 bruce@momjian.us 198 : 6130 : {
199 : : char *result;
200 : :
201 : : /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
6514 tgl@sss.pgh.pa.us 202 : 381653 : sigint_interrupt_enabled = true;
203 : :
204 : : /* Get some data */
205 : 381653 : result = fgets(line, sizeof(line), source);
206 : :
207 : : /* Disable SIGINT again */
208 : 381653 : sigint_interrupt_enabled = false;
209 : :
210 : : /* EOF or error? */
211 [ + + ]: 381653 : if (result == NULL)
212 : : {
5982 peter_e@gmx.net 213 [ - + ]: 13707 : if (ferror(source))
214 : : {
1840 peter@eisentraut.org 215 :UBC 0 : pg_log_error("could not read from input file: %m");
5982 peter_e@gmx.net 216 : 0 : return NULL;
217 : : }
6514 tgl@sss.pgh.pa.us 218 :CBC 13707 : break;
219 : : }
220 : :
221 : 367946 : appendPQExpBufferStr(buffer, line);
222 : :
5618 223 [ + - - + ]: 367946 : if (PQExpBufferBroken(buffer))
224 : : {
1840 peter@eisentraut.org 225 :UBC 0 : pg_log_error("out of memory");
5618 tgl@sss.pgh.pa.us 226 : 0 : return NULL;
227 : : }
228 : :
229 : : /* EOL? */
2817 tgl@sss.pgh.pa.us 230 [ + - + + ]:CBC 367946 : if (buffer->len > 0 && buffer->data[buffer->len - 1] == '\n')
231 : : {
6514 232 : 361816 : buffer->data[buffer->len - 1] = '\0';
233 : 361816 : return pg_strdup(buffer->data);
234 : : }
235 : : }
236 : :
237 [ + + ]: 13707 : if (buffer->len > 0) /* EOF after reading some bufferload(s) */
238 : 6066 : return pg_strdup(buffer->data);
239 : :
240 : : /* EOF, so return null */
8928 bruce@momjian.us 241 : 7641 : 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
6635 299 : 1 : encode_history(void)
300 : : {
3506 tgl@sss.pgh.pa.us 301 [ - + + + ]: 35 : BEGIN_ITERATE_HISTORY(cur_hist);
302 : : {
303 : : char *cur_ptr;
304 : :
305 : : /* some platforms declare HIST_ENTRY.line as const char * */
6510 306 [ + + ]: 957 : for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
307 : : {
6637 bruce@momjian.us 308 [ + + ]: 923 : if (*cur_ptr == '\n')
6636 309 : 1 : *cur_ptr = NL_IN_HISTORY;
310 : : }
311 : : }
312 : : END_ITERATE_HISTORY();
6637 313 : 1 : }
314 : :
315 : : /*
316 : : * Reverse the above encoding
317 : : */
318 : : static void
6635 319 : 2 : decode_history(void)
320 : : {
3506 tgl@sss.pgh.pa.us 321 [ - - - + ]: 2 : BEGIN_ITERATE_HISTORY(cur_hist);
322 : : {
323 : : char *cur_ptr;
324 : :
325 : : /* some platforms declare HIST_ENTRY.line as const char * */
6510 tgl@sss.pgh.pa.us 326 [ # # ]:UBC 0 : for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
327 : : {
6636 bruce@momjian.us 328 [ # # ]: 0 : if (*cur_ptr == NL_IN_HISTORY)
6637 329 : 0 : *cur_ptr = '\n';
330 : : }
331 : : }
332 : : END_ITERATE_HISTORY();
6637 bruce@momjian.us 333 :CBC 2 : }
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
8857 peter_e@gmx.net 344 : 2 : initializeInput(int flags)
345 : : {
346 : : #ifdef USE_READLINE
7696 bruce@momjian.us 347 [ + - ]: 2 : if (flags & 1)
348 : : {
349 : : const char *histfile;
350 : : char home[MAXPGPATH];
351 : :
8928 352 : 2 : useReadline = true;
353 : :
354 : : /* set appropriate values for Readline's global variables */
8768 355 : 2 : initialize_readline();
356 : :
357 : : #ifdef HAVE_RL_VARIABLE_BIND
358 : : /* set comment-begin to a useful value for SQL */
865 tgl@sss.pgh.pa.us 359 : 2 : (void) rl_variable_bind("comment-begin", "-- ");
360 : : #endif
361 : :
362 : : /* this reads ~/.inputrc, so do it after rl_variable_bind */
3041 363 : 2 : rl_initialize();
364 : :
8928 bruce@momjian.us 365 : 2 : useHistory = true;
366 : 2 : using_history();
5327 tgl@sss.pgh.pa.us 367 : 2 : history_lines_added = 0;
368 : :
6438 369 : 2 : histfile = GetVariable(pset.vars, "HISTFILE");
370 : :
4425 andrew@dunslane.net 371 [ + - ]: 2 : if (histfile == NULL)
372 : : {
373 : : char *envhist;
374 : :
375 : 2 : envhist = getenv("PSQL_HISTORY");
376 [ + - + - ]: 2 : if (envhist != NULL && strlen(envhist) > 0)
377 : 2 : histfile = envhist;
378 : : }
379 : :
6438 tgl@sss.pgh.pa.us 380 [ - + ]: 2 : if (histfile == NULL)
381 : : {
6883 bruce@momjian.us 382 [ # # ]:UBC 0 : if (get_home_path(home))
3827 tgl@sss.pgh.pa.us 383 : 0 : psql_history = psprintf("%s/%s", home, PSQLHISTORY);
384 : : }
385 : : else
386 : : {
6438 tgl@sss.pgh.pa.us 387 :CBC 2 : psql_history = pg_strdup(histfile);
6883 bruce@momjian.us 388 : 2 : expand_tilde(&psql_history);
389 : : }
390 : :
391 [ + - ]: 2 : if (psql_history)
392 : : {
7386 neilc@samurai.com 393 : 2 : read_history(psql_history);
6517 tgl@sss.pgh.pa.us 394 : 2 : decode_history();
395 : : }
396 : : }
397 : : #endif
398 : :
8768 bruce@momjian.us 399 : 2 : atexit(finishInput);
8928 400 : 2 : }
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
3506 tgl@sss.pgh.pa.us 413 : 2 : 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 [ + + ]: 2 : 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 */
5327 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
5327 tgl@sss.pgh.pa.us 459 :UBC 0 : nlines = history_lines_added;
3319 tgl@sss.pgh.pa.us 460 :CBC 1 : errnum = append_history(nlines, fname);
461 [ + - ]: 1 : if (errnum == 0)
5327 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 : :
1840 peter@eisentraut.org 476 :UBC 0 : pg_log_error("could not save history to file \"%s\": %m", fname);
477 : : }
7696 bruce@momjian.us 478 :CBC 1 : 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
3506 tgl@sss.pgh.pa.us 494 :UBC 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 */
3305 andrew@dunslane.net 506 [ # # ]: 0 : output = PageOutput(INT_MAX, pager ? &(pset.popt.topt) : NULL);
3506 tgl@sss.pgh.pa.us 507 : 0 : is_pager = true;
508 : : }
509 : : else
510 : : {
511 : 0 : output = fopen(fname, "w");
512 [ # # ]: 0 : if (output == NULL)
513 : : {
1840 peter@eisentraut.org 514 : 0 : pg_log_error("could not save history to file \"%s\": %m", fname);
3506 tgl@sss.pgh.pa.us 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
8928 bruce@momjian.us 540 :CBC 2 : finishInput(void)
541 : : {
542 : : #ifdef USE_READLINE
6883 543 [ + - + - ]: 2 : if (useHistory && psql_history)
544 : : {
2628 tgl@sss.pgh.pa.us 545 : 2 : (void) saveHistory(psql_history, pset.histsize);
6883 bruce@momjian.us 546 : 2 : free(psql_history);
547 : 2 : psql_history = NULL;
548 : : }
549 : : #endif
8928 550 : 2 : }
|