From 03b138e90479895bf5467f38b40d817512822fcd Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 16 Dec 2015 16:58:56 -0500 Subject: [PATCH] Cope with Readline's failure to track SIGWINCH events outside of input. It emerges that libreadline doesn't notice terminal window size change events unless they occur while collecting input. This is easy to stumble over if you resize the window while using a pager to look at query output, but it can be demonstrated without any pager involvement. The symptom is that queries exceeding one line are misdisplayed during subsequent input cycles, because libreadline has the wrong idea of the screen dimensions. The safest, simplest way to fix this is to call rl_reset_screen_size() just before calling readline(). That causes an extra ioctl(TIOCGWINSZ) for every command; but since it only happens when reading from a tty, the performance impact should be negligible. A more valid objection is that this still leaves a tiny window during entry to readline() wherein delivery of SIGWINCH will be missed; but the practical consequences of that are probably negligible. In any case, there doesn't seem to be any good way to avoid the race, since readline exposes no functions that seem safe to call from a generic signal handler --- rl_reset_screen_size() certainly isn't. It turns out that we also need an explicit rl_initialize() call, else rl_reset_screen_size() dumps core when called before the first readline() call. rl_reset_screen_size() is not present in old versions of libreadline, so we need a configure test for that. (rl_initialize() is present at least back to readline 4.0, so we won't bother with a test for it.) We would need a configure test anyway since libedit's emulation of libreadline doesn't currently include such a function. Fortunately, libedit seems not to have any corresponding bug. Merlin Moncure, adjusted a bit by me --- configure | 3 ++- configure.in | 2 +- src/bin/psql/input.c | 12 ++++++++++++ src/include/pg_config.h.in | 3 +++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/configure b/configure index c8512e89fb7..11ee392ac6e 100755 --- a/configure +++ b/configure @@ -22698,7 +22698,8 @@ _ACEOF fi -for ac_func in rl_completion_matches rl_filename_completion_function + +for ac_func in rl_completion_matches rl_filename_completion_function rl_reset_screen_size do as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 diff --git a/configure.in b/configure.in index 40f496ad616..4f8af48267e 100644 --- a/configure.in +++ b/configure.in @@ -1493,7 +1493,7 @@ LIBS="$LIBS_including_readline" if test "$with_readline" = yes; then PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER - AC_CHECK_FUNCS([rl_completion_matches rl_filename_completion_function]) + AC_CHECK_FUNCS([rl_completion_matches rl_filename_completion_function rl_reset_screen_size]) AC_CHECK_FUNCS([append_history history_truncate_file]) fi diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index d56f37fb9ac..14ac034b576 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -70,6 +70,17 @@ gets_interactive(const char *prompt) { 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 + /* Enable SIGINT to longjmp to sigint_interrupt_jmp */ sigint_interrupt_enabled = true; @@ -335,6 +346,7 @@ initializeInput(int flags) char home[MAXPGPATH]; useReadline = true; + rl_initialize(); initialize_readline(); useHistory = true; diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index c09e0ed021e..1843be9fd26 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -427,6 +427,9 @@ /* Define to 1 if you have the `rl_filename_completion_function' function. */ #undef HAVE_RL_FILENAME_COMPLETION_FUNCTION +/* Define to 1 if you have the `rl_reset_screen_size' function. */ +#undef HAVE_RL_RESET_SCREEN_SIZE + /* Define to 1 if you have the `scandir' function. */ #undef HAVE_SCANDIR -- 2.39.5