From 0ecf9d642f92e41fa82829d49ac889d37fed1d05 Mon Sep 17 00:00:00 2001 From: Erik Wienhold Date: Wed, 10 Sep 2025 02:58:56 +0200 Subject: [PATCH v3 2/3] psql: Fix counting of cell lines in expanded mode In expanded mode we repeat the table header in every record by printing header and cell side by side. But the pager setup only counts the lines in the cells. The total line count is off if headers have more line breaks than some of the respective cells. Fix this by distinguishing between expanded and normal mode in the loop that counts the cell lines. While at it, change that loop to count all lines of each cell instead of just the extra lines to make the logic easier to follow. The first line of each cell was accounted for in the initial value of the total line count which now only accounts for the separator lines, depending on the output mode. --- src/fe_utils/print.c | 60 ++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c index 44d5af7577e..82f4fc9af20 100644 --- a/src/fe_utils/print.c +++ b/src/fe_utils/print.c @@ -3366,6 +3366,7 @@ IsPagerNeeded(const printTableContent *cont, unsigned int *width_wrap, int lines, max_lines = 0, nl_lines, + *header_height = NULL, i; const char *const *cell = NULL; @@ -3375,18 +3376,25 @@ IsPagerNeeded(const printTableContent *cont, unsigned int *width_wrap, return; } - if (expanded) - lines = (cont->ncolumns + 1) * cont->nrows; - else - lines = cont->nrows + 1; + /* + * Count one line per record separator in expanded mode. In normal mode, + * we write a single separator line between header and rows. + */ + lines = expanded ? cont->nrows : 1; - /* Scan all column headers to find maximum height */ + /* + * Scan all column headers and cache their line count since expanded + * output repeats the header for every record. + */ + header_height = pg_malloc0(cont->ncolumns * sizeof(header_height)); for (i = 0; i < cont->ncolumns; i++) { pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]), cont->opt->encoding, NULL, &nl_lines, NULL); + header_height[i] = nl_lines; + if (nl_lines > max_lines) max_lines = nl_lines; } @@ -3395,37 +3403,45 @@ IsPagerNeeded(const printTableContent *cont, unsigned int *width_wrap, lines += max_lines; max_lines = 0; - /* Scan all cells to count extra lines */ + /* Scan all cells to count their lines */ for (i = 0, cell = cont->cells; *cell; cell++) { int width; - unsigned int extra_lines; + /* Count the original line breaks */ pg_wcssize((const unsigned char *) *cell, strlen(*cell), cont->opt->encoding, &width, &nl_lines, NULL); - /* - * A cell can have both wrapping and newlines that cause it to display - * across multiple lines. We check for both cases below. - */ - - /* Don't count the first line of nl_lines -- it's not "extra" */ - extra_lines = nl_lines - 1; - /* Count extra lines due to wrapping */ if (width > 0 && width_wrap && width_wrap[i]) - extra_lines += (width - 1) / width_wrap[i]; + nl_lines += (width - 1) / width_wrap[i]; - if (extra_lines > max_lines) - max_lines = extra_lines; + if (expanded) + { + /* Pick the height of the header or cell, whichever is taller */ + if (nl_lines > header_height[i]) + lines += nl_lines; + else + lines += header_height[i]; + } + else + { + /* Remember max line count in the current row */ + if (nl_lines > max_lines) + max_lines = nl_lines; + } /* i is the current column number: increment with wrap */ if (++i >= cont->ncolumns) { i = 0; - /* At last column of each row, add tallest column height */ - lines += max_lines; - max_lines = 0; + + if (!expanded) + { + /* At last column of each row, add tallest column height */ + lines += max_lines; + max_lines = 0; + } } } @@ -3443,6 +3459,8 @@ IsPagerNeeded(const printTableContent *cont, unsigned int *width_wrap, *fout = PageOutput(lines, cont->opt); *is_pager = (*fout != stdout); + + free(header_height); } /* -- 2.51.0