Fix for psql pager computations - Mailing list pgsql-patches

From Bruce Momjian
Subject Fix for psql pager computations
Date
Msg-id 200805142057.m4EKvCV24327@momjian.us
Whole thread Raw
Responses Re: Fix for psql pager computations  (Bruce Momjian <bruce@momjian.us>)
List pgsql-patches
The attached patch causes psql to use the pager if newlines or
'format=wrapped' has caused a single row to span more than one line and
the output is then too long for the screen.  It also uses the pager if
psql thinks the data will wrap off the edge of the screen.

The display width as defined by \pset columns, $COLUMNS, or the ioctl
(as previously discussed) We don't have similar settings for the number
of display rows.  I assume that is OK.

I believe this makes the pager more reliable, and in fact I have removed
the psql manual mention that pager computations are somewhat imperfect.

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.204
diff -c -c -r1.204 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml    14 May 2008 04:07:01 -0000    1.204
--- doc/src/sgml/ref/psql-ref.sgml    14 May 2008 19:56:10 -0000
***************
*** 1555,1561 ****
            <term><literal>columns</literal></term>
            <listitem>
            <para>
!           Controls the target width for the <literal>wrapped</> format.
            Zero (the default) causes the <literal>wrapped</> format to
            affect only screen output.
            </para>
--- 1555,1562 ----
            <term><literal>columns</literal></term>
            <listitem>
            <para>
!           Controls the target width for the <literal>wrapped</> format,
!           and width for determining if wide output requires the pager.
            Zero (the default) causes the <literal>wrapped</> format to
            affect only screen output.
            </para>
***************
*** 1717,1726 ****
            When the pager is <literal>off</>, the pager is not used. When the pager
            is <literal>on</>, the pager is used only when appropriate, i.e. the
            output is to a terminal and will not fit on the screen.
!           (<application>psql</> does not do a perfect job of estimating
!           when to use the pager.) <literal>\pset pager</> turns the
!           pager on and off. Pager can also be set to <literal>always</>,
!           which causes the pager to be always used.
            </para>
            </listitem>
            </varlistentry>
--- 1718,1726 ----
            When the pager is <literal>off</>, the pager is not used. When the pager
            is <literal>on</>, the pager is used only when appropriate, i.e. the
            output is to a terminal and will not fit on the screen.
!           <literal>\pset pager</> turns the pager on and off. Pager can
!           also be set to <literal>always</>, which causes the pager to be
!           always used.
            </para>
            </listitem>
            </varlistentry>
***************
*** 2734,2741 ****

      <listitem>
       <para>
!       Used for the <literal>wrapped</> output format if
!       <literal>\pset columns</> is zero.
       </para>
      </listitem>
     </varlistentry>
--- 2734,2742 ----

      <listitem>
       <para>
!       If <literal>\pset columns</> is zero, controls the
!       width for the <literal>wrapped</> format and width for determining
!       if wide output requires the pager.
       </para>
      </listitem>
     </varlistentry>
Index: src/bin/psql/print.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.101
diff -c -c -r1.101 print.c
*** src/bin/psql/print.c    13 May 2008 00:14:11 -0000    1.101
--- src/bin/psql/print.c    14 May 2008 19:56:12 -0000
***************
*** 45,50 ****
--- 45,52 ----

  /* Local functions */
  static int strlen_max_width(unsigned char *str, int *target_width, int encoding);
+ static void IsPagerNeeded(const printTableContent *cont, const int extra_lines,
+                           FILE **fout, bool *is_pager);


  static void *
***************
*** 394,400 ****
   *    Print pretty boxes around cells.
   */
  static void
! print_aligned_text(const printTableContent *cont, bool is_pager, FILE *fout)
  {
      bool        opt_tuples_only = cont->opt->tuples_only;
      bool        opt_numeric_locale = cont->opt->numericLocale;
--- 396,402 ----
   *    Print pretty boxes around cells.
   */
  static void
! print_aligned_text(const printTableContent *cont, FILE *fout)
  {
      bool        opt_tuples_only = cont->opt->tuples_only;
      bool        opt_numeric_locale = cont->opt->numericLocale;
***************
*** 416,421 ****
--- 418,425 ----
      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;

***************
*** 424,429 ****
--- 428,434 ----
      bool       *header_done;    /* Have all header lines been output? */
      int           *bytes_output;    /* Bytes output for column value */
      int            output_columns = 0;    /* Width of interactive console */
+     bool        is_pager = false;

      if (cancel_pressed)
          return;
***************
*** 476,485 ****
              max_nl_lines[i] = nl_lines;
          if (bytes_required > max_bytes[i])
              max_bytes[i] = bytes_required;

          width_header[i] = width;
      }
!
      /* scan all cells, find maximum width, compute cell_count */
      for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
      {
--- 481,495 ----
              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++)
      {
***************
*** 487,493 ****
                      nl_lines,
                      bytes_required;

-         /* Get width, ignore nl_lines */
          pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
                     &width, &nl_lines, &bytes_required);
          if (opt_numeric_locale && cont->aligns[i % col_count] == 'r')
--- 497,502 ----
***************
*** 552,579 ****
      for (i = 0; i < col_count; i++)
          width_wrap[i] = max_width[i];

!     if (cont->opt->format == PRINT_WRAPPED)
      {
!         /*
!          * Choose target output width: \pset columns, or $COLUMNS, or ioctl
!          */
!         if (cont->opt->columns > 0)
!             output_columns = cont->opt->columns;
!         else if ((fout == stdout && isatty(fileno(stdout))) || is_pager)
!         {
!             if (cont->opt->env_columns > 0)
!                 output_columns = cont->opt->env_columns;
  #ifdef TIOCGWINSZ
!             else
!             {
!                 struct winsize screen_size;
!
!                 if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
!                     output_columns = screen_size.ws_col;
!             }
! #endif
          }

          /*
           * Optional optimized word wrap. Shrink columns with a high max/avg
           * ratio.  Slighly bias against wider columns. (Increases chance a
--- 561,588 ----
      for (i = 0; i < col_count; i++)
          width_wrap[i] = max_width[i];

!     /*
!      * Choose target output width: \pset columns, or $COLUMNS, or ioctl
!      */
!     if (cont->opt->columns > 0)
!         output_columns = cont->opt->columns;
!     else if ((fout == stdout && isatty(fileno(stdout))) || is_pager)
      {
!         if (cont->opt->env_columns > 0)
!             output_columns = cont->opt->env_columns;
  #ifdef TIOCGWINSZ
!         else
!         {
!             struct winsize screen_size;
!
!             if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
!                 output_columns = screen_size.ws_col;
          }
+ #endif
+     }

+     if (cont->opt->format == PRINT_WRAPPED)
+     {
          /*
           * Optional optimized word wrap. Shrink columns with a high max/avg
           * ratio.  Slighly bias against wider columns. (Increases chance a
***************
*** 623,628 ****
--- 632,679 ----
          }
      }

+     /* If we wrapped beyond the display width, use the pager */
+     if (!is_pager && output_columns > 0 &&
+         (output_columns < total_header_width || output_columns < width_total))
+     {
+         fout = PageOutput(INT_MAX, cont->opt->pager);    /* force pager */
+         is_pager = true;
+     }
+
+     /* Check if newlines or our wrapping now need the pager */
+     if (!is_pager)
+     {
+         /* scan all cells, find maximum width, compute cell_count */
+         for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
+         {
+             int            width,
+                         nl_lines,
+                         bytes_required;
+
+             pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
+                        &width, &nl_lines, &bytes_required);
+             if (opt_numeric_locale && cont->align[i % col_count] == 'r')
+                 width += additional_numeric_locale_len(*ptr);
+
+             /*
+              *    A row can have both wrapping and newlines that cause
+              *    it to display across multiple lines.  We check
+              *    for both cases below.
+              */
+             if ((width-1) / width_wrap[i] + nl_lines > extra_row_output_lines)
+                 extra_row_output_lines = (width-1) / width_wrap[i] + nl_lines;
+
+             /* If last column, add tallest column height */
+             if (i % col_count == col_count - 1)
+             {
+                 /* Add height of tallest row */
+                 extra_output_lines += extra_row_output_lines;
+                 extra_row_output_lines = 0;
+             }
+         }
+         IsPagerNeeded(cont, extra_output_lines, &fout, &is_pager);
+     }
+
      /* time to output */
      if (cont->opt->start_table)
      {
***************
*** 882,887 ****
--- 933,941 ----
      for (i = 0; i < col_count; i++)
          free(format_buf[i]);
      free(format_buf);
+
+     if (is_pager)
+         ClosePager(fout);
  }


***************
*** 2115,2135 ****
  }

  /*
!  * Use this to print just any table in the supported formats.
   */
  void
! printTable(const printTableContent *cont, FILE *fout, FILE *flog)
  {
!     FILE       *output;
!     bool        is_pager = false;
!
!     if (cancel_pressed)
!         return;
!
!     if (cont->opt->format == PRINT_NOTHING)
!         return;
!
!     if (fout == stdout)
      {
          int            lines;

--- 2169,2183 ----
  }

  /*
!  * IsPagerNeeded
!  *
!  * Setup pager if required
   */
  void
! IsPagerNeeded(const printTableContent *cont, const int extra_lines, FILE **fout,
!               bool *is_pager)
  {
!     if (*fout == stdout)
      {
          int            lines;

***************
*** 2150,2207 ****
                  lines++;
          }

!         output = PageOutput(lines, cont->opt->pager);
!         is_pager = (output != fout);
      }
      else
!         output = fout;

      /* print the stuff */

      if (flog)
!         print_aligned_text(cont, is_pager, flog);

      switch (cont->opt->format)
      {
          case PRINT_UNALIGNED:
              if (cont->opt->expanded)
!                 print_unaligned_vertical(cont, output);
              else
!                 print_unaligned_text(cont, output);
              break;
          case PRINT_ALIGNED:
          case PRINT_WRAPPED:
              if (cont->opt->expanded)
!                 print_aligned_vertical(cont, output);
              else
!                 print_aligned_text(cont, is_pager, output);
              break;
          case PRINT_HTML:
              if (cont->opt->expanded)
!                 print_html_vertical(cont, output);
              else
!                 print_html_text(cont, output);
              break;
          case PRINT_LATEX:
              if (cont->opt->expanded)
!                 print_latex_vertical(cont, output);
              else
!                 print_latex_text(cont, output);
              break;
          case PRINT_TROFF_MS:
              if (cont->opt->expanded)
!                 print_troff_ms_vertical(cont, output);
              else
!                 print_troff_ms_text(cont, output);
              break;
          default:
!             fprintf(stderr, _("invalid output format (internal error): %d"),
                      cont->opt->format);
              exit(EXIT_FAILURE);
      }

      if (is_pager)
!         ClosePager(output);
  }

  /*
--- 2198,2276 ----
                  lines++;
          }

!         *fout = PageOutput(lines + extra_lines, cont->opt->pager);
!         *is_pager = (*fout != stdout);
      }
      else
!         *is_pager = false;
! }
!
! /*
!  * Use this to print just any table in the supported formats.
!  */
! void
! printTable(const printTableContent *cont, FILE *fout, FILE *flog)
! {
!     bool        is_pager = false;
!
!     if (cancel_pressed)
!         return;
!
!     if (cont->opt->format == PRINT_NOTHING)
!         return;
!
!     /* print_aligned_text() handles the pager itself */
!     if ((cont->opt->format != PRINT_ALIGNED &&
!          cont->opt->format != PRINT_WRAPPED) ||
!          cont->opt->expanded)
!         IsPagerNeeded(cont, 0, &fout, &is_pager);

      /* print the stuff */

      if (flog)
!         print_aligned_text(cont, flog);

      switch (cont->opt->format)
      {
          case PRINT_UNALIGNED:
              if (cont->opt->expanded)
!                 print_unaligned_vertical(cont, fout);
              else
!                 print_unaligned_text(cont, fout);
              break;
          case PRINT_ALIGNED:
          case PRINT_WRAPPED:
              if (cont->opt->expanded)
!                 print_aligned_vertical(cont, fout);
              else
!                 print_aligned_text(cont, fout);
              break;
          case PRINT_HTML:
              if (cont->opt->expanded)
!                 print_html_vertical(cont, fout);
              else
!                 print_html_text(cont, fout);
              break;
          case PRINT_LATEX:
              if (cont->opt->expanded)
!                 print_latex_vertical(cont, fout);
              else
!                 print_latex_text(cont, fout);
              break;
          case PRINT_TROFF_MS:
              if (cont->opt->expanded)
!                 print_troff_ms_vertical(cont, fout);
              else
!                 print_troff_ms_text(cont, fout);
              break;
          default:
!             fprintf(stderr, _("invalid fout format (internal error): %d"),
                      cont->opt->format);
              exit(EXIT_FAILURE);
      }

      if (is_pager)
!         ClosePager(fout);
  }

  /*

pgsql-patches by date:

Previous
From: "Merlin Moncure"
Date:
Subject: Re: libpq object hooks
Next
From: Bruce Momjian
Date:
Subject: Re: Patch to change psql default banner v6