Add xheader_width pset option to psql
authorAndrew Dunstan <andrew@dunslane.net>
Mon, 25 Jul 2022 18:24:50 +0000 (14:24 -0400)
committerAndrew Dunstan <andrew@dunslane.net>
Mon, 25 Jul 2022 18:25:02 +0000 (14:25 -0400)
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

doc/src/sgml/ref/psql-ref.sgml
src/bin/psql/command.c
src/bin/psql/tab-complete.c
src/fe_utils/print.c
src/include/fe_utils/print.h
src/test/regress/expected/psql.out
src/tools/pgindent/typedefs.list

index 65bb0a6a3f3328b8140bd3f0aa886ae4b650973b..a95ddd60ee2a1eda550cd290d4c8aa364eb22dc6 100644 (file)
@@ -2842,6 +2842,39 @@ lo_import 152801
           </listitem>
           </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>
           <term><literal>fieldsep</literal></term>
           <listitem>
index cac98804ab59268f595a004e95934b33ea41f618..a81bd3307b47fd80f7817b42cd5cd97fbc5e9750 100644 (file)
@@ -2244,6 +2244,7 @@ exec_command_pset(PsqlScanState scan_state, bool active_branch)
                "unicode_border_linestyle",
                "unicode_column_linestyle",
                "unicode_header_linestyle",
+               "xheader_width",
                NULL
            };
 
@@ -4369,6 +4370,29 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
            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 */
    else if (strcmp(param, "csv_fieldsep") == 0)
    {
@@ -4561,6 +4585,19 @@ printPsetInfo(const char *param, printQueryOpt *popt)
            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 */
    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));
    else if (strcmp(param, "unicode_header_linestyle") == 0)
        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
        return pstrdup("ERROR");
 }
index 5f06768085fe0e8bcec5d5720bef623bce49abaf..f265e043e958c1ceabb949c049176a59e702e050 100644 (file)
@@ -4654,13 +4654,16 @@ psql_completion(const char *text, int start, int end)
                         "tableattr", "title", "tuples_only",
                         "unicode_border_linestyle",
                         "unicode_column_linestyle",
-                        "unicode_header_linestyle");
+                        "unicode_header_linestyle",
+                        "xheader_width");
    else if (TailMatchesCS("\\pset", MatchAny))
    {
        if (TailMatchesCS("format"))
            COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex",
                             "latex-longtable", "troff-ms", "unaligned",
                             "wrapped");
+       else if (TailMatchesCS("xheader_width"))
+           COMPLETE_WITH_CS("full", "column", "page");
        else if (TailMatchesCS("linestyle"))
            COMPLETE_WITH_CS("ascii", "old-ascii", "unicode");
        else if (TailMatchesCS("pager"))
index fe676a971b93a1d9c22fbb2b813861197a87dfdc..a48c9196978ee21fe43cbf88d115e56cb185e7c7 100644 (file)
@@ -1222,15 +1222,16 @@ cleanup:
 
 
 static void
-print_aligned_vertical_line(const printTextFormat *format,
-                           const unsigned short opt_border,
+print_aligned_vertical_line(const printTableOpt *topt,
                            unsigned long record,
                            unsigned int hwidth,
                            unsigned int dwidth,
+                           int output_columns,
                            printTextRule pos,
                            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;
    int         reclen = 0;
 
@@ -1259,8 +1260,18 @@ print_aligned_vertical_line(const printTextFormat *format,
        if (reclen-- <= 0)
            fputs(lformat->hrule, fout);
        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);
    }
    else
@@ -1268,12 +1279,43 @@ print_aligned_vertical_line(const printTextFormat *format,
        if (reclen-- <= 0)
            fputc(' ', fout);
    }
-   if (reclen < 0)
-       reclen = 0;
-   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);
+
+   if (topt->expanded_header_width_type != PRINT_XHEADER_COLUMN)
+   {
+       if (topt->expanded_header_width_type == PRINT_XHEADER_PAGE
+           || topt->expanded_header_width_type == PRINT_XHEADER_EXACT_WIDTH)
+       {
+           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);
 }
 
@@ -1570,11 +1612,12 @@ print_aligned_vertical(const printTableContent *cont,
                lhwidth++;      /* for newline indicators */
 
            if (!opt_tuples_only)
-               print_aligned_vertical_line(format, opt_border, record++,
-                                           lhwidth, dwidth, pos, fout);
+               print_aligned_vertical_line(cont->opt, record++,
+                                           lhwidth, dwidth, output_columns,
+                                           pos, fout);
            else if (i != 0 || !cont->opt->start_table || opt_border == 2)
-               print_aligned_vertical_line(format, opt_border, 0, lhwidth,
-                                           dwidth, pos, fout);
+               print_aligned_vertical_line(cont->opt, 0, lhwidth,
+                                           dwidth, output_columns, pos, fout);
        }
 
        /* Format the header */
@@ -1760,8 +1803,8 @@ print_aligned_vertical(const printTableContent *cont,
    if (cont->opt->stop_table)
    {
        if (opt_border == 2 && !cancel_pressed)
-           print_aligned_vertical_line(format, opt_border, 0, hwidth, dwidth,
-                                       PRINT_RULE_BOTTOM, fout);
+           print_aligned_vertical_line(cont->opt, 0, hwidth, dwidth,
+                                       output_columns, PRINT_RULE_BOTTOM, fout);
 
        /* print footers */
        if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
index bb2f1bf4e64275e38e69a881660ef28657f22b97..b8dbfa5f543d26e2d1ccfe64c9ef31d4225799b7 100644 (file)
@@ -66,6 +66,15 @@ typedef enum printTextLineWrap
    PRINT_LINE_WRAP_NEWLINE     /* Newline in data */
 } 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
 {
    /* A complete line style */
@@ -101,6 +110,8 @@ typedef struct printTableOpt
    enum printFormat format;    /* see enum above */
    unsigned short int expanded;    /* expanded/vertical output (if supported
                                     * 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,
                                 * 1=dividing lines, 2=full */
    unsigned short int pager;   /* use pager for output (if to stdout and
index 60acbd1241e57507bc92e01bacfe6e607b97699f..a7f5700edc121d2346d8c7f165ed84294083b1e3 100644 (file)
@@ -317,6 +317,7 @@ tuples_only              off
 unicode_border_linestyle single
 unicode_column_linestyle single
 unicode_header_linestyle single
+xheader_width            full
 -- test multi-line headers, wrapping, and newline indicators
 -- in aligned, unaligned, and wrapped formats
 prepare q as select array_to_string(array_agg(repeat('x',2*n)),E'\n') as "ab
index 34a76ceb60fc882fa840e3df12d023cc35c67c0a..60709ddc9870ec6afe1cfb81c83adb912a4497c4 100644 (file)
@@ -3569,6 +3569,7 @@ printTextFormat
 printTextLineFormat
 printTextLineWrap
 printTextRule
+printXheaderWidthType
 printfunc
 priv_map
 process_file_callback_t