summaryrefslogtreecommitdiffstats
path: root/src/bin/psql/input.c
blob: 873d85079c95bcb830e9c810dc9734bfdd2e1159 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
/*
 * psql - the PostgreSQL interactive terminal
 *
 * Copyright (c) 2000-2023, PostgreSQL Global Development Group
 *
 * src/bin/psql/input.c
 */
#include "postgres_fe.h"

#ifndef WIN32
#include <unistd.h>
#endif
#include <fcntl.h>
#include <limits.h>

#include "common.h"
#include "common/logging.h"
#include "input.h"
#include "settings.h"
#include "tab-complete.h"

#ifndef WIN32
#define PSQLHISTORY ".psql_history"
#else
#define PSQLHISTORY "psql_history"
#endif

/* Runtime options for turning off readline and history */
/* (of course there is no runtime command for doing that :) */
#ifdef USE_READLINE
static bool useReadline;
static bool useHistory;

static char *psql_history;

static int	history_lines_added;


/*
 *	Preserve newlines in saved queries by mapping '\n' to NL_IN_HISTORY
 *
 *	It is assumed NL_IN_HISTORY will never be entered by the user
 *	nor appear inside a multi-byte string.  0x00 is not properly
 *	handled by the readline routines so it can not be used
 *	for this purpose.
 */
#define NL_IN_HISTORY	0x01
#endif

static void finishInput(void);


/*
 * gets_interactive()
 *
 * Gets a line of interactive input, using readline if desired.
 *
 * prompt: the prompt string to be used
 * query_buf: buffer containing lines already read in the current command
 * (query_buf is not modified here, but may be consulted for tab completion)
 *
 * The result is a malloc'd string.
 *
 * Caller *must* have set up sigint_interrupt_jmp before calling.
 */
char *
gets_interactive(const char *prompt, PQExpBuffer query_buf)
{
#ifdef USE_READLINE
	if (useReadline)
	{
		char	   *result;

		/*
		 * Some versions of readline don't notice SIGWINCH signals that arrive
		 * when not actively reading input.  The simplest fix is to always
		 * re-read the terminal size.  This leaves a window for SIGWINCH to be
		 * missed between here and where readline() enables libreadline's
		 * signal handler, but that's probably short enough to be ignored.
		 */
#ifdef HAVE_RL_RESET_SCREEN_SIZE
		rl_reset_screen_size();
#endif

		/* Make current query_buf available to tab completion callback */
		tab_completion_query_buf = query_buf;

		/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
		sigint_interrupt_enabled = true;

		/* On some platforms, readline is declared as readline(char *) */
		result = readline((char *) prompt);

		/* Disable SIGINT again */
		sigint_interrupt_enabled = false;

		/* Pure neatnik-ism */
		tab_completion_query_buf = NULL;

		return result;
	}
#endif

	fputs(prompt, stdout);
	fflush(stdout);
	return gets_fromFile(stdin);
}


/*
 * Append the line to the history buffer, making sure there is a trailing '\n'
 */
void
pg_append_history(const char *s, PQExpBuffer history_buf)
{
#ifdef USE_READLINE
	if (useHistory && s)
	{
		appendPQExpBufferStr(history_buf, s);
		if (!s[0] || s[strlen(s) - 1] != '\n')
			appendPQExpBufferChar(history_buf, '\n');
	}
#endif
}


/*
 * Emit accumulated history entry to readline's history mechanism,
 * then reset the buffer to empty.
 *
 * Note: we write nothing if history_buf is empty, so extra calls to this
 * function don't hurt.  There must have been at least one line added by
 * pg_append_history before we'll do anything.
 */
void
pg_send_history(PQExpBuffer history_buf)
{
#ifdef USE_READLINE
	static char *prev_hist = NULL;

	char	   *s = history_buf->data;
	int			i;

	/* Trim any trailing \n's (OK to scribble on history_buf) */
	for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--)
		;
	s[i + 1] = '\0';

	if (useHistory && s[0])
	{
		if (((pset.histcontrol & hctl_ignorespace) &&
			 s[0] == ' ') ||
			((pset.histcontrol & hctl_ignoredups) &&
			 prev_hist && strcmp(s, prev_hist) == 0))
		{
			/* Ignore this line as far as history is concerned */
		}
		else
		{
			/* Save each previous line for ignoredups processing */
			free(prev_hist);
			prev_hist = pg_strdup(s);
			/* And send it to readline */
			add_history(s);
			/* Count lines added to history for use later */
			history_lines_added++;
		}
	}

	resetPQExpBuffer(history_buf);
#endif
}


/*
 * gets_fromFile
 *
 * Gets a line of noninteractive input from a file (which could be stdin).
 * The result is a malloc'd string, or NULL on EOF or input error.
 *
 * Caller *must* have set up sigint_interrupt_jmp before calling.
 *
 * Note: we re-use a static PQExpBuffer for each call.  This is to avoid
 * leaking memory if interrupted by SIGINT.
 */
char *
gets_fromFile(FILE *source)
{
	static PQExpBuffer buffer = NULL;

	char		line[1024];

	if (buffer == NULL)			/* first time through? */
		buffer = createPQExpBuffer();
	else
		resetPQExpBuffer(buffer);

	for (;;)
	{
		char	   *result;

		/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
		sigint_interrupt_enabled = true;

		/* Get some data */
		result = fgets(line, sizeof(line), source);

		/* Disable SIGINT again */
		sigint_interrupt_enabled = false;

		/* EOF or error? */
		if (result == NULL)
		{
			if (ferror(source))
			{
				pg_log_error("could not read from input file: %m");
				return NULL;
			}
			break;
		}

		appendPQExpBufferStr(buffer, line);

		if (PQExpBufferBroken(buffer))
		{
			pg_log_error("out of memory");
			return NULL;
		}

		/* EOL? */
		if (buffer->len > 0 && buffer->data[buffer->len - 1] == '\n')
		{
			buffer->data[buffer->len - 1] = '\0';
			return pg_strdup(buffer->data);
		}
	}

	if (buffer->len > 0)		/* EOF after reading some bufferload(s) */
		return pg_strdup(buffer->data);

	/* EOF, so return null */
	return NULL;
}


#ifdef USE_READLINE

/*
 * Macros to iterate over each element of the history list in order
 *
 * You would think this would be simple enough, but in its inimitable fashion
 * libedit has managed to break it: in libreadline we must use next_history()
 * to go from oldest to newest, but in libedit we must use previous_history().
 * To detect what to do, we make a trial call of previous_history(): if it
 * fails, then either next_history() is what to use, or there's zero or one
 * history entry so that it doesn't matter which direction we go.
 *
 * In case that wasn't disgusting enough: the code below is not as obvious as
 * it might appear.  In some libedit releases history_set_pos(0) fails until
 * at least one add_history() call has been done.  This is not an issue for
 * printHistory() or encode_history(), which cannot be invoked before that has
 * happened.  In decode_history(), that's not so, and what actually happens is
 * that we are sitting on the newest entry to start with, previous_history()
 * fails, and we iterate over all the entries using next_history().  So the
 * decode_history() loop iterates over the entries in the wrong order when
 * using such a libedit release, and if there were another attempt to use
 * BEGIN_ITERATE_HISTORY() before some add_history() call had happened, it
 * wouldn't work.  Fortunately we don't care about either of those things.
 *
 * Usage pattern is:
 *
 *		BEGIN_ITERATE_HISTORY(varname);
 *		{
 *			loop body referencing varname->line;
 *		}
 *		END_ITERATE_HISTORY();
 */
#define BEGIN_ITERATE_HISTORY(VARNAME) \
	do { \
		HIST_ENTRY *VARNAME; \
		bool		use_prev_; \
		\
		history_set_pos(0); \
		use_prev_ = (previous_history() != NULL); \
		history_set_pos(0); \
		for (VARNAME = current_history(); VARNAME != NULL; \
			 VARNAME = use_prev_ ? previous_history() : next_history()) \
		{ \
			(void) 0

#define END_ITERATE_HISTORY() \
		} \
	} while(0)


/*
 * Convert newlines to NL_IN_HISTORY for safe saving in readline history file
 */
static void
encode_history(void)
{
	BEGIN_ITERATE_HISTORY(cur_hist);
	{
		char	   *cur_ptr;

		/* some platforms declare HIST_ENTRY.line as const char * */
		for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
		{
			if (*cur_ptr == '\n')
				*cur_ptr = NL_IN_HISTORY;
		}
	}
	END_ITERATE_HISTORY();
}

/*
 * Reverse the above encoding
 */
static void
decode_history(void)
{
	BEGIN_ITERATE_HISTORY(cur_hist);
	{
		char	   *cur_ptr;

		/* some platforms declare HIST_ENTRY.line as const char * */
		for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
		{
			if (*cur_ptr == NL_IN_HISTORY)
				*cur_ptr = '\n';
		}
	}
	END_ITERATE_HISTORY();
}
#endif							/* USE_READLINE */


/*
 * Put any startup stuff related to input in here. It's good to maintain
 * abstraction this way.
 *
 * The only "flag" right now is 1 for use readline & history.
 */
void
initializeInput(int flags)
{
#ifdef USE_READLINE
	if (flags & 1)
	{
		const char *histfile;
		char		home[MAXPGPATH];

		useReadline = true;

		/* set appropriate values for Readline's global variables */
		initialize_readline();

#ifdef HAVE_RL_VARIABLE_BIND
		/* set comment-begin to a useful value for SQL */
		(void) rl_variable_bind("comment-begin", "-- ");
#endif

		/* this reads ~/.inputrc, so do it after rl_variable_bind */
		rl_initialize();

		useHistory = true;
		using_history();
		history_lines_added = 0;

		histfile = GetVariable(pset.vars, "HISTFILE");

		if (histfile == NULL)
		{
			char	   *envhist;

			envhist = getenv("PSQL_HISTORY");
			if (envhist != NULL && strlen(envhist) > 0)
				histfile = envhist;
		}

		if (histfile == NULL)
		{
			if (get_home_path(home))
				psql_history = psprintf("%s/%s", home, PSQLHISTORY);
		}
		else
		{
			psql_history = pg_strdup(histfile);
			expand_tilde(&psql_history);
		}

		if (psql_history)
		{
			read_history(psql_history);
			decode_history();
		}
	}
#endif

	atexit(finishInput);
}


/*
 * This function saves the readline history when psql exits.
 *
 * fname: pathname of history file.  (Should really be "const char *",
 * but some ancient versions of readline omit the const-decoration.)
 *
 * max_lines: if >= 0, limit history file to that many entries.
 */
#ifdef USE_READLINE
static bool
saveHistory(char *fname, int max_lines)
{
	int			errnum;

	/*
	 * Suppressing the write attempt when HISTFILE is set to /dev/null may
	 * look like a negligible optimization, but it's necessary on e.g. macOS,
	 * where write_history will fail because it tries to chmod the target
	 * file.
	 */
	if (strcmp(fname, DEVNULL) != 0)
	{
		/*
		 * Encode \n, since otherwise readline will reload multiline history
		 * entries as separate lines.  (libedit doesn't really need this, but
		 * we do it anyway since it's too hard to tell which implementation we
		 * are using.)
		 */
		encode_history();

		/*
		 * On newer versions of libreadline, truncate the history file as
		 * needed and then append what we've added.  This avoids overwriting
		 * history from other concurrent sessions (although there are still
		 * race conditions when two sessions exit at about the same time). If
		 * we don't have those functions, fall back to write_history().
		 */
#if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY)
		{
			int			nlines;
			int			fd;

			/* truncate previous entries if needed */
			if (max_lines >= 0)
			{
				nlines = Max(max_lines - history_lines_added, 0);
				(void) history_truncate_file(fname, nlines);
			}
			/* append_history fails if file doesn't already exist :-( */
			fd = open(fname, O_CREAT | O_WRONLY | PG_BINARY, 0600);
			if (fd >= 0)
				close(fd);
			/* append the appropriate number of lines */
			if (max_lines >= 0)
				nlines = Min(max_lines, history_lines_added);
			else
				nlines = history_lines_added;
			errnum = append_history(nlines, fname);
			if (errnum == 0)
				return true;
		}
#else							/* don't have append support */
		{
			/* truncate what we have ... */
			if (max_lines >= 0)
				stifle_history(max_lines);
			/* ... and overwrite file.  Tough luck for concurrent sessions. */
			errnum = write_history(fname);
			if (errnum == 0)
				return true;
		}
#endif

		pg_log_error("could not save history to file \"%s\": %m", fname);
	}
	return false;
}
#endif



/*
 * Print history to the specified file, or to the console if fname is NULL
 * (psql \s command)
 *
 * We used to use saveHistory() for this purpose, but that doesn't permit
 * use of a pager; moreover libedit's implementation behaves incompatibly
 * (preferring to encode its output) and may fail outright when the target
 * file is specified as /dev/tty.
 */
bool
printHistory(const char *fname, unsigned short int pager)
{
#ifdef USE_READLINE
	FILE	   *output;
	bool		is_pager;

	if (!useHistory)
		return false;

	if (fname == NULL)
	{
		/* use pager, if enabled, when printing to console */
		output = PageOutput(INT_MAX, pager ? &(pset.popt.topt) : NULL);
		is_pager = true;
	}
	else
	{
		output = fopen(fname, "w");
		if (output == NULL)
		{
			pg_log_error("could not save history to file \"%s\": %m", fname);
			return false;
		}
		is_pager = false;
	}

	BEGIN_ITERATE_HISTORY(cur_hist);
	{
		fprintf(output, "%s\n", cur_hist->line);
	}
	END_ITERATE_HISTORY();

	if (is_pager)
		ClosePager(output);
	else
		fclose(output);

	return true;
#else
	pg_log_error("history is not supported by this installation");
	return false;
#endif
}


static void
finishInput(void)
{
#ifdef USE_READLINE
	if (useHistory && psql_history)
	{
		(void) saveHistory(psql_history, pset.histsize);
		free(psql_history);
		psql_history = NULL;
	}
#endif
}