From afcb4607789ba814a9541fb1b9278229a6e85c82 Mon Sep 17 00:00:00 2001 From: "Chao Li (Evan)" Date: Fri, 12 Jun 2026 16:48:15 +0800 Subject: [PATCH v1] Fix psql pager selection for wrapped expanded output psql decided whether to use the pager in expanded output before calculating the data column width used by wrapped format. As a result, wrapped expanded output could be counted without the extra lines added by wrapping, so psql could print more than a screenful of output directly to the terminal instead of invoking the pager. Move the pager decision in print_aligned_vertical() until after the wrapped data width is known. For wrapped vertical output, pass count_table_lines() a per-column wrap-width array using that common data width, since all expanded fields share the same data area width. Add pager test coverage for wrapped expanded output. Author: Chao Li --- src/bin/psql/t/030_pager.pl | 8 ++++++ src/fe_utils/print.c | 52 ++++++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/bin/psql/t/030_pager.pl b/src/bin/psql/t/030_pager.pl index d3f964639d3..a9a671d28ad 100644 --- a/src/bin/psql/t/030_pager.pl +++ b/src/bin/psql/t/030_pager.pl @@ -130,6 +130,14 @@ do_command( qr/55\r?$/m, "execute command with footer that needs pagination"); +# With the 80-column pty, the payload wraps to two lines per row. +# Expanded output also prints one record header per row, plus a trailing +# blank line, so this produces 11 * 3 + 1 = 34 lines. +do_command( + "\\pset expanded on\n\\pset format wrapped\n\\pset columns 80\nSELECT repeat('x',138) AS payload FROM generate_series(1,11);\n", + qr/34\r?$/m, + "execute SELECT query that needs pagination in expanded wrapped mode"); + # send psql an explicit \q to shut it down, else pty won't close properly $h->quit or die "psql returned $?"; diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c index bfbaf094d7e..5b03005c85e 100644 --- a/src/fe_utils/print.c +++ b/src/fe_utils/print.c @@ -1354,17 +1354,6 @@ print_aligned_vertical(const printTableContent *cont, return; } - /* - * Deal with the pager here instead of in printTable(), because we could - * get here via print_aligned_text() in expanded auto mode, and so we have - * to recalculate the pager requirement based on vertical output. - */ - if (!is_pager) - { - IsPagerNeeded(cont, NULL, true, &fout, &is_pager); - is_local_pager = is_pager; - } - /* Find the maximum dimensions for the headers */ for (i = 0; i < cont->ncolumns; i++) { @@ -1415,13 +1404,6 @@ print_aligned_vertical(const printTableContent *cont, dlineptr->ptr = pg_malloc(dformatsize); hlineptr->ptr = pg_malloc(hformatsize); - if (cont->opt->start_table) - { - /* print title */ - if (!opt_tuples_only && cont->title) - fprintf(fout, "%s\n", cont->title); - } - /* * Choose target output width: \pset columns, or $COLUMNS, or ioctl */ @@ -1569,6 +1551,40 @@ print_aligned_vertical(const printTableContent *cont, dwidth = newdwidth; } + /* + * Deal with the pager here instead of in printTable(), because we could + * get here via print_aligned_text() in expanded auto mode, and so we have + * to recalculate the pager requirement based on vertical output. + */ + if (!is_pager) + { + unsigned int *width_wrap = NULL; + + /* + * Wrapping can add extra output lines that count_table_lines() can + * only account for if it has wrap widths. Vertical output uses the + * same data width for every field, so use dwidth for every column. + */ + if (cont->opt->format == PRINT_WRAPPED && cont->ncolumns > 0) + { + width_wrap = pg_malloc_array(unsigned int, cont->ncolumns); + for (i = 0; i < cont->ncolumns; i++) + width_wrap[i] = dwidth; + } + + IsPagerNeeded(cont, width_wrap, true, &fout, &is_pager); + is_local_pager = is_pager; + + free(width_wrap); + } + + if (cont->opt->start_table) + { + /* print title */ + if (!opt_tuples_only && cont->title) + fprintf(fout, "%s\n", cont->title); + } + /* print records */ for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { -- 2.50.1 (Apple Git-155)