Re: Fix for psql pager computations - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: Fix for psql pager computations
Date
Msg-id 200805161659.m4GGx7Y21004@momjian.us
Whole thread Raw
In response to Fix for psql pager computations  (Bruce Momjian <bruce@momjian.us>)
List pgsql-patches
Bruce Momjian wrote:
> 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.

Update patch applied.

--
  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    16 May 2008 16:49:19 -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    16 May 2008 16:49:20 -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,484 ****
--- 481,494 ----
              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,680 ----
          }
      }

+     /* 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 > 0 && width_wrap[i] &&
+                 (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 ****
--- 934,942 ----
      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;

--- 2170,2184 ----
  }

  /*
!  * 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);
  }

  /*
--- 2199,2277 ----
                  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: Andrew Dunstan
Date:
Subject: Re: libpq object hooks
Next
From: Bruce Momjian
Date:
Subject: Re: Patch to change psql default banner v6