[PATCH v3 1/3] psql: Fix pager setup for unaligned and expanded output

Erik Wienhold <ewie@ewie.name>

From: Erik Wienhold <ewie@ewie.name>
To:
Date: 2025-09-09T00:29:26Z
Lists: pgsql-hackers
Move the line counting logic to the pager setup so that it applies to
unaligned and expanded output modes as well.  While at it, get rid of
the top if-else branch in IsPagerNeeded in favor of an early return when
not writing to stdout.  This avoids having extra indentation for the
most part of that function.
---
 src/fe_utils/print.c | 146 ++++++++++++++++++++++++-------------------
 1 file changed, 80 insertions(+), 66 deletions(-)

diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index 4af0f32f2fc..44d5af7577e 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -266,8 +266,8 @@ static const unicodeStyleFormat unicode_style = {
 
 /* Local functions */
 static int	strlen_max_width(unsigned char *str, int *target_width, int encoding);
-static void IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded,
-						  FILE **fout, bool *is_pager);
+static void IsPagerNeeded(const printTableContent *cont, unsigned int *width_wrap,
+						  bool expanded, FILE **fout, bool *is_pager);
 
 static void print_aligned_vertical(const printTableContent *cont,
 								   FILE *fout, bool is_pager);
@@ -656,8 +656,6 @@ print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager)
 	unsigned char **format_buf;
 	unsigned int width_total;
 	unsigned int total_header_width;
-	unsigned int extra_row_output_lines = 0;
-	unsigned int extra_output_lines = 0;
 
 	const char *const *ptr;
 
@@ -722,14 +720,9 @@ print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager)
 			max_nl_lines[i] = nl_lines;
 		if (bytes_required > max_bytes[i])
 			max_bytes[i] = bytes_required;
-		if (nl_lines > extra_row_output_lines)
-			extra_row_output_lines = nl_lines;
 
 		width_header[i] = width;
 	}
-	/* Add height of tallest header column */
-	extra_output_lines += extra_row_output_lines;
-	extra_row_output_lines = 0;
 
 	/* scan all cells, find maximum width, compute cell_count */
 	for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
@@ -892,40 +885,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager)
 	/* Check if newlines or our wrapping now need the pager */
 	if (!is_pager && fout == stdout)
 	{
-		/* scan all cells, find maximum width, compute cell_count */
-		for (i = 0, ptr = cont->cells; *ptr; ptr++, cell_count++)
-		{
-			int			width,
-						nl_lines,
-						bytes_required;
-
-			pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
-					   &width, &nl_lines, &bytes_required);
-
-			/*
-			 * A row can have both wrapping and newlines that cause it to
-			 * display across multiple lines.  We check for both cases below.
-			 */
-			if (width > 0 && width_wrap[i])
-			{
-				unsigned int extra_lines;
-
-				/* don't count the first line of nl_lines - it's not "extra" */
-				extra_lines = ((width - 1) / width_wrap[i]) + nl_lines - 1;
-				if (extra_lines > extra_row_output_lines)
-					extra_row_output_lines = extra_lines;
-			}
-
-			/* i is the current column number: increment with wrap */
-			if (++i >= col_count)
-			{
-				i = 0;
-				/* At last column of each row, add tallest column height */
-				extra_output_lines += extra_row_output_lines;
-				extra_row_output_lines = 0;
-			}
-		}
-		IsPagerNeeded(cont, extra_output_lines, false, &fout, &is_pager);
+		IsPagerNeeded(cont, width_wrap, false, &fout, &is_pager);
 		is_local_pager = is_pager;
 	}
 
@@ -1376,7 +1336,7 @@ print_aligned_vertical(const printTableContent *cont,
 	 */
 	if (!is_pager)
 	{
-		IsPagerNeeded(cont, 0, true, &fout, &is_pager);
+		IsPagerNeeded(cont, NULL, true, &fout, &is_pager);
 		is_local_pager = is_pager;
 	}
 
@@ -3400,35 +3360,89 @@ printTableCleanup(printTableContent *const content)
  * Setup pager if required
  */
 static void
-IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded,
-			  FILE **fout, bool *is_pager)
+IsPagerNeeded(const printTableContent *cont, unsigned int *width_wrap,
+			  bool expanded, FILE **fout, bool *is_pager)
 {
-	if (*fout == stdout)
+	int			lines,
+				max_lines = 0,
+				nl_lines,
+				i;
+	const char *const *cell = NULL;
+
+	if (*fout != stdout)
 	{
-		int			lines;
+		*is_pager = false;
+		return;
+	}
 
-		if (expanded)
-			lines = (cont->ncolumns + 1) * cont->nrows;
-		else
-			lines = cont->nrows + 1;
+	if (expanded)
+		lines = (cont->ncolumns + 1) * cont->nrows;
+	else
+		lines = cont->nrows + 1;
 
-		if (!cont->opt->tuples_only)
-		{
-			printTableFooter *f;
+	/* Scan all column headers to find maximum 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);
 
-			/*
-			 * FIXME -- this is slightly bogus: it counts the number of
-			 * footers, not the number of lines in them.
-			 */
-			for (f = cont->footers; f; f = f->next)
-				lines++;
+		if (nl_lines > max_lines)
+			max_lines = nl_lines;
+	}
+
+	/* Add height of tallest header column */
+	lines += max_lines;
+	max_lines = 0;
+
+	/* Scan all cells to count extra lines */
+	for (i = 0, cell = cont->cells; *cell; cell++)
+	{
+		int			width;
+		unsigned int extra_lines;
+
+		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];
+
+		if (extra_lines > max_lines)
+			max_lines = extra_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 (!cont->opt->tuples_only)
+	{
+		printTableFooter *f;
 
-		*fout = PageOutput(lines + extra_lines, cont->opt);
-		*is_pager = (*fout != stdout);
+		/*
+		 * FIXME -- this is slightly bogus: it counts the number of
+		 * footers, not the number of lines in them.
+		 */
+		for (f = cont->footers; f; f = f->next)
+			lines++;
 	}
-	else
-		*is_pager = false;
+
+	*fout = PageOutput(lines, cont->opt);
+	*is_pager = (*fout != stdout);
 }
 
 /*
@@ -3456,7 +3470,7 @@ printTable(const printTableContent *cont,
 		cont->opt->format != PRINT_ALIGNED &&
 		cont->opt->format != PRINT_WRAPPED)
 	{
-		IsPagerNeeded(cont, 0, (cont->opt->expanded == 1), &fout, &is_pager);
+		IsPagerNeeded(cont, NULL, (cont->opt->expanded == 1), &fout, &is_pager);
 		is_local_pager = is_pager;
 	}
 
-- 
2.51.0


--h4pjkjw5wkhovhea
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v3-0002-psql-Fix-counting-of-cell-lines-in-expanded-mode.patch"