Add xheader_width pset option to psql

The setting controls tha maximum length of the header line in expanded
format output. Possible settings are full, column, page, or an integer.
the default is full, the current behaviour, and in this case the header
line is the length of the widest line of output. column causes the
header to be truncated to the width of the first column, page causes it
to be truncated to the width of the terminal page, and an integer causes
it to be truncated to that value. If the full value is less than the
page or integer value no truncation occurs. If given without an argument
this option prints its current setting.

Platon Pronko, somewhat modified by me.

Discussion: https://postgr.es/m/f03d38a3-db96-a56e-d1bc-dbbc80bbde4d@gmail.com
This commit is contained in:
Andrew Dunstan 2022-07-25 14:24:50 -04:00
parent b35617de37
commit a45388d6e0
7 changed files with 164 additions and 18 deletions

View File

@ -2842,6 +2842,39 @@ lo_import 152801
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>xheader_width</literal></term>
<listitem>
<para>
Sets the maximum width of the header for expanded output to one of
<literal>full</literal> (the default value),
<literal>column</literal>, <literal>page</literal>, or an
<replaceable class="parameter">integer value</replaceable>.
</para>
<para>
<literal>full</literal>: the expanded header is not truncated,
and will be as wide as the widest output
line.
</para>
<para>
<literal>column</literal>: truncate the header line at the
width of the first column.
</para>
<para>
<literal>page</literal>: truncate the the header line at the terminal
width.
</para>
<para>
<replaceable class="parameter">integer value</replaceable>: specify
the exact maximum width of the header line.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><literal>fieldsep</literal></term> <term><literal>fieldsep</literal></term>
<listitem> <listitem>

View File

@ -2244,6 +2244,7 @@ exec_command_pset(PsqlScanState scan_state, bool active_branch)
"unicode_border_linestyle", "unicode_border_linestyle",
"unicode_column_linestyle", "unicode_column_linestyle",
"unicode_header_linestyle", "unicode_header_linestyle",
"xheader_width",
NULL NULL
}; };
@ -4369,6 +4370,29 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
popt->topt.expanded = !popt->topt.expanded; popt->topt.expanded = !popt->topt.expanded;
} }
/* header line width in expanded mode */
else if (strcmp(param, "xheader_width") == 0)
{
if (! value)
;
else if (pg_strcasecmp(value, "full") == 0)
popt->topt.expanded_header_width_type = PRINT_XHEADER_FULL;
else if (pg_strcasecmp(value, "column") == 0)
popt->topt.expanded_header_width_type = PRINT_XHEADER_COLUMN;
else if (pg_strcasecmp(value, "page") == 0)
popt->topt.expanded_header_width_type = PRINT_XHEADER_PAGE;
else
{
popt->topt.expanded_header_width_type = PRINT_XHEADER_EXACT_WIDTH;
popt->topt.expanded_header_exact_width = atoi(value);
if (popt->topt.expanded_header_exact_width == 0)
{
pg_log_error("\\pset: allowed xheader_width values are full (default), column, page, or a number specifying the exact width.");
return false;
}
}
}
/* field separator for CSV format */ /* field separator for CSV format */
else if (strcmp(param, "csv_fieldsep") == 0) else if (strcmp(param, "csv_fieldsep") == 0)
{ {
@ -4561,6 +4585,19 @@ printPsetInfo(const char *param, printQueryOpt *popt)
printf(_("Expanded display is off.\n")); printf(_("Expanded display is off.\n"));
} }
/* show xheader width value */
else if (strcmp(param, "xheader_width") == 0)
{
if (popt->topt.expanded_header_width_type == PRINT_XHEADER_FULL)
printf(_("Expanded header width is 'full'.\n"));
else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_COLUMN)
printf(_("Expanded header width is 'column'.\n"));
else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_PAGE)
printf(_("Expanded header width is 'page'.\n"));
else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_EXACT_WIDTH)
printf(_("Expanded header width is %d.\n"), popt->topt.expanded_header_exact_width);
}
/* show field separator for CSV format */ /* show field separator for CSV format */
else if (strcmp(param, "csv_fieldsep") == 0) else if (strcmp(param, "csv_fieldsep") == 0)
{ {
@ -4881,6 +4918,23 @@ pset_value_string(const char *param, printQueryOpt *popt)
return pstrdup(_unicode_linestyle2string(popt->topt.unicode_column_linestyle)); return pstrdup(_unicode_linestyle2string(popt->topt.unicode_column_linestyle));
else if (strcmp(param, "unicode_header_linestyle") == 0) else if (strcmp(param, "unicode_header_linestyle") == 0)
return pstrdup(_unicode_linestyle2string(popt->topt.unicode_header_linestyle)); return pstrdup(_unicode_linestyle2string(popt->topt.unicode_header_linestyle));
else if (strcmp(param, "xheader_width") == 0)
{
if (popt->topt.expanded_header_width_type == PRINT_XHEADER_FULL)
return(pstrdup("full"));
else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_COLUMN)
return(pstrdup("column"));
else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_PAGE)
return(pstrdup("page"));
else
{
/* must be PRINT_XHEADER_EXACT_WIDTH */
char wbuff[32];
snprintf(wbuff, sizeof(wbuff), "%d",
popt->topt.expanded_header_exact_width);
return pstrdup(wbuff);
}
}
else else
return pstrdup("ERROR"); return pstrdup("ERROR");
} }

View File

@ -4654,13 +4654,16 @@ psql_completion(const char *text, int start, int end)
"tableattr", "title", "tuples_only", "tableattr", "title", "tuples_only",
"unicode_border_linestyle", "unicode_border_linestyle",
"unicode_column_linestyle", "unicode_column_linestyle",
"unicode_header_linestyle"); "unicode_header_linestyle",
"xheader_width");
else if (TailMatchesCS("\\pset", MatchAny)) else if (TailMatchesCS("\\pset", MatchAny))
{ {
if (TailMatchesCS("format")) if (TailMatchesCS("format"))
COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex", COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex",
"latex-longtable", "troff-ms", "unaligned", "latex-longtable", "troff-ms", "unaligned",
"wrapped"); "wrapped");
else if (TailMatchesCS("xheader_width"))
COMPLETE_WITH_CS("full", "column", "page");
else if (TailMatchesCS("linestyle")) else if (TailMatchesCS("linestyle"))
COMPLETE_WITH_CS("ascii", "old-ascii", "unicode"); COMPLETE_WITH_CS("ascii", "old-ascii", "unicode");
else if (TailMatchesCS("pager")) else if (TailMatchesCS("pager"))

View File

@ -1222,15 +1222,16 @@ cleanup:
static void static void
print_aligned_vertical_line(const printTextFormat *format, print_aligned_vertical_line(const printTableOpt *topt,
const unsigned short opt_border,
unsigned long record, unsigned long record,
unsigned int hwidth, unsigned int hwidth,
unsigned int dwidth, unsigned int dwidth,
int output_columns,
printTextRule pos, printTextRule pos,
FILE *fout) FILE *fout)
{ {
const printTextLineFormat *lformat = &format->lrule[pos]; const printTextLineFormat *lformat = &get_line_style(topt)->lrule[pos];
const unsigned short opt_border = topt->border;
unsigned int i; unsigned int i;
int reclen = 0; int reclen = 0;
@ -1259,8 +1260,18 @@ print_aligned_vertical_line(const printTextFormat *format,
if (reclen-- <= 0) if (reclen-- <= 0)
fputs(lformat->hrule, fout); fputs(lformat->hrule, fout);
if (reclen-- <= 0) if (reclen-- <= 0)
fputs(lformat->midvrule, fout); {
if (reclen-- <= 0) if (topt->expanded_header_width_type == PRINT_XHEADER_COLUMN)
{
fputs(lformat->rightvrule, fout);
}
else
{
fputs(lformat->midvrule, fout);
}
}
if (reclen-- <= 0
&& topt->expanded_header_width_type != PRINT_XHEADER_COLUMN)
fputs(lformat->hrule, fout); fputs(lformat->hrule, fout);
} }
else else
@ -1268,12 +1279,43 @@ print_aligned_vertical_line(const printTextFormat *format,
if (reclen-- <= 0) if (reclen-- <= 0)
fputc(' ', fout); fputc(' ', fout);
} }
if (reclen < 0)
reclen = 0; if (topt->expanded_header_width_type != PRINT_XHEADER_COLUMN)
for (i = reclen; i < dwidth; i++) {
fputs(opt_border > 0 ? lformat->hrule : " ", fout); if (topt->expanded_header_width_type == PRINT_XHEADER_PAGE
if (opt_border == 2) || topt->expanded_header_width_type == PRINT_XHEADER_EXACT_WIDTH)
fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); {
if (topt->expanded_header_width_type == PRINT_XHEADER_EXACT_WIDTH)
{
output_columns = topt->expanded_header_exact_width;
}
if (output_columns > 0)
{
if (opt_border == 0)
dwidth = Min(dwidth, Max(0, (int) (output_columns - hwidth)));
if (opt_border == 1)
dwidth = Min(dwidth, Max(0, (int) (output_columns - hwidth - 3)));
/*
* Handling the xheader width for border=2 doesn't make
* much sense because this format has an additional
* right border, but keep this for consistency.
*/
if (opt_border == 2)
dwidth = Min(dwidth, Max(0, (int) (output_columns - hwidth - 7)));
}
}
if (reclen < 0)
reclen = 0;
if (dwidth < reclen)
dwidth = reclen;
for (i = reclen; i < dwidth; i++)
fputs(opt_border > 0 ? lformat->hrule : " ", fout);
if (opt_border == 2)
fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
}
fputc('\n', fout); fputc('\n', fout);
} }
@ -1570,11 +1612,12 @@ print_aligned_vertical(const printTableContent *cont,
lhwidth++; /* for newline indicators */ lhwidth++; /* for newline indicators */
if (!opt_tuples_only) if (!opt_tuples_only)
print_aligned_vertical_line(format, opt_border, record++, print_aligned_vertical_line(cont->opt, record++,
lhwidth, dwidth, pos, fout); lhwidth, dwidth, output_columns,
pos, fout);
else if (i != 0 || !cont->opt->start_table || opt_border == 2) else if (i != 0 || !cont->opt->start_table || opt_border == 2)
print_aligned_vertical_line(format, opt_border, 0, lhwidth, print_aligned_vertical_line(cont->opt, 0, lhwidth,
dwidth, pos, fout); dwidth, output_columns, pos, fout);
} }
/* Format the header */ /* Format the header */
@ -1760,8 +1803,8 @@ print_aligned_vertical(const printTableContent *cont,
if (cont->opt->stop_table) if (cont->opt->stop_table)
{ {
if (opt_border == 2 && !cancel_pressed) if (opt_border == 2 && !cancel_pressed)
print_aligned_vertical_line(format, opt_border, 0, hwidth, dwidth, print_aligned_vertical_line(cont->opt, 0, hwidth, dwidth,
PRINT_RULE_BOTTOM, fout); output_columns, PRINT_RULE_BOTTOM, fout);
/* print footers */ /* print footers */
if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)

View File

@ -66,6 +66,15 @@ typedef enum printTextLineWrap
PRINT_LINE_WRAP_NEWLINE /* Newline in data */ PRINT_LINE_WRAP_NEWLINE /* Newline in data */
} printTextLineWrap; } printTextLineWrap;
typedef enum printXheaderWidthType
{
/* Expanded header line width variants */
PRINT_XHEADER_FULL, /* do not truncate header line (this is the default) */
PRINT_XHEADER_COLUMN, /* only print header line above the first column */
PRINT_XHEADER_PAGE, /* header line must not be longer than terminal width */
PRINT_XHEADER_EXACT_WIDTH, /* explicitly specified width */
} printXheaderWidthType;
typedef struct printTextFormat typedef struct printTextFormat
{ {
/* A complete line style */ /* A complete line style */
@ -101,6 +110,8 @@ typedef struct printTableOpt
enum printFormat format; /* see enum above */ enum printFormat format; /* see enum above */
unsigned short int expanded; /* expanded/vertical output (if supported unsigned short int expanded; /* expanded/vertical output (if supported
* by output format); 0=no, 1=yes, 2=auto */ * by output format); 0=no, 1=yes, 2=auto */
printXheaderWidthType expanded_header_width_type; /* width type for header line in expanded mode */
int expanded_header_exact_width; /* explicit width for header line in expanded mode */
unsigned short int border; /* Print a border around the table. 0=none, unsigned short int border; /* Print a border around the table. 0=none,
* 1=dividing lines, 2=full */ * 1=dividing lines, 2=full */
unsigned short int pager; /* use pager for output (if to stdout and unsigned short int pager; /* use pager for output (if to stdout and

View File

@ -317,6 +317,7 @@ tuples_only off
unicode_border_linestyle single unicode_border_linestyle single
unicode_column_linestyle single unicode_column_linestyle single
unicode_header_linestyle single unicode_header_linestyle single
xheader_width full
-- test multi-line headers, wrapping, and newline indicators -- test multi-line headers, wrapping, and newline indicators
-- in aligned, unaligned, and wrapped formats -- in aligned, unaligned, and wrapped formats
prepare q as select array_to_string(array_agg(repeat('x',2*n)),E'\n') as "ab prepare q as select array_to_string(array_agg(repeat('x',2*n)),E'\n') as "ab

View File

@ -3569,6 +3569,7 @@ printTextFormat
printTextLineFormat printTextLineFormat
printTextLineWrap printTextLineWrap
printTextRule printTextRule
printXheaderWidthType
printfunc printfunc
priv_map priv_map
process_file_callback_t process_file_callback_t