On 2025-08-19 03:52 +0200, Erik Wienhold wrote:
> On 2025-08-17 17:19 +0200, Tom Lane wrote:
> > This appears to fix the problem it sets out to fix, but it looks
> > to me like there are adjacent problems of the same ilk; do you
> > feel like looking at those?
> >
> > Specifically, I wondered whether we had the same bug with table
> > headers or cells that contain newlines. It looks like
> > print_aligned_text() is okay, because it counts up those newlines
> > and includes them in the extra_output_lines value that it passes
> > to IsPagerNeeded(). But print_aligned_vertical() and printTable()
> > have no such logic.
>
> I looked into these cases now and the unaligned and expanded modes
> indeed miss to use the pager.
>
> Fixing the generate_lines cases goes without saying since that one is
> about cells. But I'm not so sure if fixing the header cases (nl_column
> and view u&"1\000a2\000a3...") is worth the effort.
>
> > If not, can we fix that by moving all the "extra_lines" logic into
> > IsPagerNeeded(), and if not what shall we do about it?
>
> Looks doable. I'll prepare patch.
Here's v3 to address all of this. I split it into three separate
patches:
* v3-0001 is just a refactoring of the extra line counting, moving it
into IsPagerNeeded. This fixes the line counting for unaligned and
expanded output which, until now, called IsPagerNeeded with zero
extra_lines.
* v3-0002 fixes the line count in expanded mode when headers
contain more line breaks than the respective cells. This must be
checked for every cell because expanded mode repeats the headers for
every record. The header line counts are calculated once before
looping over all cells.
* v3-0003 is the original fix for the footer lines with two additions:
Here I also fix the counting of header lines in normal output mode
because I noticed that header lines are always counted right now even
when the header is not printed (in tuples_only mode or expanded mode.)
This now also considers the table title, if present.
> Manually testing this with the various printing options won't be fun
> though. Or are there tools to simulate the terminal size? I'm using
> tmux so it's fairly easy to create a horizonal split and resize the
> pane to be N lines high. But a script to run psql and check if the
> pager was invoked for a terminal with N lines would be cool.
I also came up with the attached test-psql-pager.py script to run the
test cases from my previous post against different output modes to count
the maximum number of lines for which psql sill triggers the pager.
This uses termios to control the terminal size. The script compares the
actual output (pager line count, output mode, test case) against a *.out
file with the expected output (similar to pg_regress) which I've also
attached. The *.out files correspond the respective patch numbers to
show how each patch improves the pager setup. The expected output in
0-master.out is the baseline on master.
--
Erik Wienhold
>From 94704e56585e9e60838f14821630c48b9ccbb164 Mon Sep 17 00:00:00 2001
From: Erik Wienhold <[email protected]>
Date: Tue, 9 Sep 2025 02:29:26 +0200
Subject: [PATCH v3 1/3] psql: Fix pager setup for unaligned and expanded
output
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
>From 0ecf9d642f92e41fa82829d49ac889d37fed1d05 Mon Sep 17 00:00:00 2001
From: Erik Wienhold <[email protected]>
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
>From e12ed8cad1c672488a64a44183c8a7b3154a57ca Mon Sep 17 00:00:00 2001
From: Erik Wienhold <[email protected]>
Date: Tue, 9 Sep 2025 02:36:49 +0200
Subject: [PATCH v3 3/3] psql: Fix counting of header and footer lines in pager
setup
* The table header only produces extra lines when printed in normal
mode. So don't count those lines in tuples_only mode or expanded
mode.
* Count the lines of the table title, if present.
* Count all footer lines instead of treating each footer as a single
line.
---
src/fe_utils/print.c | 39 +++++++++++++++++++++++++++------------
1 file changed, 27 insertions(+), 12 deletions(-)
diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index 82f4fc9af20..94043fae663 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -3394,15 +3394,8 @@ IsPagerNeeded(const printTableContent *cont, unsigned
int *width_wrap,
NULL, &nl_lines, NULL);
header_height[i] = nl_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 their lines */
for (i = 0, cell = cont->cells; *cell; cell++)
{
@@ -3449,12 +3442,34 @@ IsPagerNeeded(const printTableContent *cont, unsigned
int *width_wrap,
{
printTableFooter *f;
- /*
- * FIXME -- this is slightly bogus: it counts the number of
- * footers, not the number of lines in them.
- */
+ if (cont->title)
+ {
+ pg_wcssize((const unsigned char *) cont->title,
strlen(cont->title),
+ cont->opt->encoding, NULL,
&nl_lines, NULL);
+ lines += nl_lines;
+ }
+
+ if (!expanded)
+ {
+ /* Find the tallest header column */
+ for (i = 0; i < cont->ncolumns; i++)
+ {
+ if (header_height[i] > max_lines)
+ max_lines = header_height[i];
+ }
+
+ /* Add height of tallest header column */
+ lines += max_lines;
+ max_lines = 0;
+ }
+
+ /* Count all footer lines */
for (f = cont->footers; f; f = f->next)
- lines++;
+ {
+ pg_wcssize((const unsigned char *) f->data,
strlen(f->data),
+ cont->opt->encoding, NULL,
&nl_lines, NULL);
+ lines += nl_lines;
+ }
}
*fout = PageOutput(lines, cont->opt);
--
2.51.0
# This script runs different test cases through psql to find the maximum number
# of lines that still trigger psql to use the pager.
#
# Termios is used to set the terminal size.
#
# Use environment variable PATH to select the psql binary to test. Use libpq
# environment variables to select a database for testing in which this script
# can run the test setup.
import argparse
import os
import os.path
import subprocess
import sys
import tempfile
import time
import termios
import typing
def run_tests(outfile):
# Prepare database schema
proc = subprocess.run(['psql', '-X'], text=True, input=r'''
\set ON_ERROR_STOP on
BEGIN;
CREATE OR REPLACE FUNCTION generate_lines(n int)
RETURNS TABLE (lines text)
LANGUAGE sql
AS $$ SELECT string_agg(s::text, e'\n') FROM generate_series(1, n) s $$;
-- This creates a view name and column name with 24 line breaks when truncated
-- to the default NAMEDATALEN (63 = 9*2 + 15*3)
SELECT format('CREATE OR REPLACE VIEW %I (c) AS SELECT null;'
'CREATE OR REPLACE VIEW nl_column (%1$I) AS SELECT null;',
string_agg(s::text, e'\n'))
FROM generate_series(1, current_setting('max_identifier_length')::int) s
\gexec
COMMIT;
''').check_returncode()
testcases = []
for cmd in commands:
# Repeat each command with every combination of flags that affect the
# number of output lines.
for flags in range(1 << 3):
testcases.append(Testcase(
cmd=cmd,
unaligned=flags & 1,
tuples_only=flags & (1 << 1),
expanded=flags & (1 << 2),
))
# Remember current term size
save_term_size = termios.tcgetwinsize(sys.stdout.fileno())
max_paged_lines = []
for tc in testcases:
max_paged_lines.append(find_max_paged_lines(tc.psql_args()))
# Restore term size
termios.tcsetwinsize(sys.stdout.fileno(), save_term_size)
# Print the testcase results
for tc, lines in zip(testcases, max_paged_lines):
flags = ''
flags += 'A' if tc.unaligned else '-'
flags += 't' if tc.tuples_only else '-'
flags += 'x' if tc.expanded else '-'
# Make sure we get one output line per testcase
cmd = tc.cmd.replace('\n', r'\n')
print(f"{lines:2} {flags} {cmd}", file=outfile)
def run_psql_with_pager(args):
with tempfile.NamedTemporaryFile() as tmp:
mtime_before = os.stat(tmp.name).st_mtime_ns
env = {
# Inherit environment variables (especially PATH and libpq-specific
# ones).
**os.environ,
# Set PAGER so that we can tell from the temp file's mtime that the
# pager was triggered.
'PAGER': f'touch {tmp.name}',
}
proc = subprocess.run(['psql', '-X', *args], env=env)
if proc.returncode:
return None
mtime_after = os.stat(tmp.name).st_mtime_ns
pager_used = mtime_after > mtime_before
return pager_used
def find_max_paged_lines(psql_args):
# Binary search the maximum number of lines at which psql still triggers
# the pager.
min_lines = 1
max_lines = 100
cols = 100 # sufficient for our test cases
while min_lines <= max_lines:
lines = min_lines + (max_lines - min_lines) // 2
termios.tcsetwinsize(sys.stdout.fileno(), (lines, cols))
pager_used = run_psql_with_pager(psql_args)
if pager_used is None:
return -1
if pager_used:
min_lines = lines + 1
else:
max_lines = lines - 1
return max_lines
class Testcase(typing.NamedTuple):
cmd: str
unaligned: bool
tuples_only: bool
expanded: bool
def psql_args(self):
args = ['-c', self.cmd]
if self.unaligned:
args.append('-A')
if self.tuples_only:
args.append('-t')
if self.expanded:
args.append('-x')
return args
# The SQL and meta commands we will be testing
commands = [
'SELECT * FROM generate_lines(25)',
'SELECT * FROM nl_column',
'\\d nl_column',
'\\d+ nl_column',
'\\d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"',
'\\d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"',
]
parser = argparse.ArgumentParser()
parser.add_argument('expectfile', metavar='FILE', help="file with expected test
results")
args = parser.parse_args()
basename, _ = os.path.splitext(args.expectfile)
outfile = basename + '.tmp'
difffile = basename + '.diff'
with open(outfile, 'w') as fp:
run_tests(fp)
with open(difffile, 'w') as fp:
if os.path.isfile(args.expectfile):
srcfile = args.expectfile
else:
srcfile = os.devnull
proc = subprocess.run(['diff', '-u', srcfile, outfile], stdout=fp)
if proc.returncode:
print()
print("Test output does not match the expected output.")
print(f'The differences can be viewed in file "{difffile}".')
exit(1)
27 --- SELECT * FROM generate_lines(25)
2 A-- SELECT * FROM generate_lines(25)
27 -t- SELECT * FROM generate_lines(25)
2 At- SELECT * FROM generate_lines(25)
2 --x SELECT * FROM generate_lines(25)
2 A-x SELECT * FROM generate_lines(25)
2 -tx SELECT * FROM generate_lines(25)
2 Atx SELECT * FROM generate_lines(25)
27 --- SELECT * FROM nl_column
2 A-- SELECT * FROM nl_column
27 -t- SELECT * FROM nl_column
2 At- SELECT * FROM nl_column
2 --x SELECT * FROM nl_column
2 A-x SELECT * FROM nl_column
2 -tx SELECT * FROM nl_column
2 Atx SELECT * FROM nl_column
27 --- \d nl_column
2 A-- \d nl_column
27 -t- \d nl_column
2 At- \d nl_column
27 --x \d nl_column
2 A-x \d nl_column
27 -tx \d nl_column
2 Atx \d nl_column
29 --- \d+ nl_column
4 A-- \d+ nl_column
27 -t- \d+ nl_column
2 At- \d+ nl_column
29 --x \d+ nl_column
4 A-x \d+ nl_column
27 -tx \d+ nl_column
2 Atx \d+ nl_column
3 --- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 A-- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -t- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 At- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 --x \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 A-x \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -tx \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 Atx \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 --- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
4 A-- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -t- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 At- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 --x \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
4 A-x \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -tx \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 Atx \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
27 --- SELECT * FROM generate_lines(25)
27 A-- SELECT * FROM generate_lines(25)
27 -t- SELECT * FROM generate_lines(25)
27 At- SELECT * FROM generate_lines(25)
27 --x SELECT * FROM generate_lines(25)
27 A-x SELECT * FROM generate_lines(25)
27 -tx SELECT * FROM generate_lines(25)
27 Atx SELECT * FROM generate_lines(25)
27 --- SELECT * FROM nl_column
27 A-- SELECT * FROM nl_column
27 -t- SELECT * FROM nl_column
27 At- SELECT * FROM nl_column
27 --x SELECT * FROM nl_column
27 A-x SELECT * FROM nl_column
27 -tx SELECT * FROM nl_column
27 Atx SELECT * FROM nl_column
27 --- \d nl_column
27 A-- \d nl_column
27 -t- \d nl_column
27 At- \d nl_column
27 --x \d nl_column
27 A-x \d nl_column
27 -tx \d nl_column
27 Atx \d nl_column
29 --- \d+ nl_column
29 A-- \d+ nl_column
27 -t- \d+ nl_column
27 At- \d+ nl_column
29 --x \d+ nl_column
29 A-x \d+ nl_column
27 -tx \d+ nl_column
27 Atx \d+ nl_column
3 --- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 A-- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -t- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 At- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 --x \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 A-x \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -tx \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 Atx \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 --- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 A-- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -t- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 At- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 --x \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 A-x \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -tx \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 Atx \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
27 --- SELECT * FROM generate_lines(25)
27 A-- SELECT * FROM generate_lines(25)
27 -t- SELECT * FROM generate_lines(25)
27 At- SELECT * FROM generate_lines(25)
27 --x SELECT * FROM generate_lines(25)
27 A-x SELECT * FROM generate_lines(25)
27 -tx SELECT * FROM generate_lines(25)
27 Atx SELECT * FROM generate_lines(25)
27 --- SELECT * FROM nl_column
27 A-- SELECT * FROM nl_column
27 -t- SELECT * FROM nl_column
27 At- SELECT * FROM nl_column
51 --x SELECT * FROM nl_column
51 A-x SELECT * FROM nl_column
51 -tx SELECT * FROM nl_column
51 Atx SELECT * FROM nl_column
27 --- \d nl_column
27 A-- \d nl_column
27 -t- \d nl_column
27 At- \d nl_column
27 --x \d nl_column
27 A-x \d nl_column
27 -tx \d nl_column
27 Atx \d nl_column
29 --- \d+ nl_column
29 A-- \d+ nl_column
27 -t- \d+ nl_column
27 At- \d+ nl_column
29 --x \d+ nl_column
29 A-x \d+ nl_column
27 -tx \d+ nl_column
27 Atx \d+ nl_column
3 --- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 A-- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -t- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 At- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 --x \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 A-x \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -tx \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 Atx \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 --- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 A-- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -t- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 At- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 --x \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
5 A-x \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 -tx \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
3 Atx \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
27 --- SELECT * FROM generate_lines(25)
27 A-- SELECT * FROM generate_lines(25)
26 -t- SELECT * FROM generate_lines(25)
26 At- SELECT * FROM generate_lines(25)
26 --x SELECT * FROM generate_lines(25)
26 A-x SELECT * FROM generate_lines(25)
26 -tx SELECT * FROM generate_lines(25)
26 Atx SELECT * FROM generate_lines(25)
27 --- SELECT * FROM nl_column
27 A-- SELECT * FROM nl_column
2 -t- SELECT * FROM nl_column
2 At- SELECT * FROM nl_column
26 --x SELECT * FROM nl_column
26 A-x SELECT * FROM nl_column
26 -tx SELECT * FROM nl_column
26 Atx SELECT * FROM nl_column
28 --- \d nl_column
28 A-- \d nl_column
26 -t- \d nl_column
26 At- \d nl_column
28 --x \d nl_column
28 A-x \d nl_column
26 -tx \d nl_column
26 Atx \d nl_column
54 --- \d+ nl_column
54 A-- \d+ nl_column
26 -t- \d+ nl_column
26 At- \d+ nl_column
54 --x \d+ nl_column
54 A-x \d+ nl_column
26 -tx \d+ nl_column
26 Atx \d+ nl_column
28 --- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
28 A-- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 -t- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 At- \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
28 --x \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
28 A-x \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 -tx \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 Atx \d
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
30 --- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
30 A-- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 -t- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 At- \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
30 --x \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
30 A-x \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 -tx \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"
2 Atx \d+
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"