Tom Lane d8ff060ecd Fix behavior of printTable() and friends with externally-invoked pager.
The formatting modes that depend on knowledge of the terminal window width
did not work right when printing a query result that's been fetched in
sections (as a result of FETCH_SIZE).  ExecQueryUsingCursor() would force
use of the pager as soon as there's more than one result section, and then
print.c would see an output file pointer that's not stdout and incorrectly
conclude that the terminal window width isn't relevant.

This has been broken all along for non-expanded "wrapped" output format,
and as of 9.5 the issue affects expanded mode as well.  The problem also
caused "\pset expanded auto" mode to invariably *not* switch to expanded
output in a segmented result, which seems to me to be exactly backwards.

To fix, we need to pass down an "is_pager" flag to inform the print.c
subroutines that some calling level has already replaced stdout with a
pager pipe, so they should (a) not do that again and (b) nonetheless honor
the window size.  (Notably, this makes the first is_pager test in
print_aligned_text() not be dead code anymore.)

This patch is a bit invasive because there are so many existing calls of
printQuery()/printTable(), but fortunately all but a couple can just pass
"false" for the added parameter.

Back-patch to 9.5 but no further.  Given the lack of field complaints,
it's not clear that we should change the behavior in stable branches.
Also, the API change for printQuery()/printTable() might possibly break
third-party code, again something we don't like to do in stable branches.
However, it's not quite too late to do this in 9.5, and with the larger
scope of the problem there, it seems worth doing.
2015-12-02 18:20:41 -05:00

203 lines
7.2 KiB
C

/*
* psql - the PostgreSQL interactive terminal
*
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
*
* src/bin/psql/print.h
*/
#ifndef PRINT_H
#define PRINT_H
#include "libpq-fe.h"
enum printFormat
{
PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED,
PRINT_ALIGNED,
PRINT_WRAPPED,
PRINT_HTML,
PRINT_ASCIIDOC,
PRINT_LATEX,
PRINT_LATEX_LONGTABLE,
PRINT_TROFF_MS
/* add your favourite output format here ... */
};
typedef struct printTextLineFormat
{
/* Line drawing characters to be used in various contexts */
const char *hrule; /* horizontal line character */
const char *leftvrule; /* left vertical line (+horizontal) */
const char *midvrule; /* intra-column vertical line (+horizontal) */
const char *rightvrule; /* right vertical line (+horizontal) */
} printTextLineFormat;
typedef enum printTextRule
{
/* Additional context for selecting line drawing characters */
PRINT_RULE_TOP, /* top horizontal line */
PRINT_RULE_MIDDLE, /* intra-data horizontal line */
PRINT_RULE_BOTTOM, /* bottom horizontal line */
PRINT_RULE_DATA /* data line (hrule is unused here) */
} printTextRule;
typedef enum printTextLineWrap
{
/* Line wrapping conditions */
PRINT_LINE_WRAP_NONE, /* No wrapping */
PRINT_LINE_WRAP_WRAP, /* Wraparound due to overlength line */
PRINT_LINE_WRAP_NEWLINE /* Newline in data */
} printTextLineWrap;
typedef struct printTextFormat
{
/* A complete line style */
const char *name; /* for display purposes */
printTextLineFormat lrule[4]; /* indexed by enum printTextRule */
const char *midvrule_nl; /* vertical line for continue after newline */
const char *midvrule_wrap; /* vertical line for wrapped data */
const char *midvrule_blank; /* vertical line for blank data */
const char *header_nl_left; /* left mark after newline */
const char *header_nl_right; /* right mark for newline */
const char *nl_left; /* left mark after newline */
const char *nl_right; /* right mark for newline */
const char *wrap_left; /* left mark after wrapped data */
const char *wrap_right; /* right mark for wrapped data */
bool wrap_right_border; /* use right-hand border for wrap
* marks when border=0? */
} printTextFormat;
typedef enum unicode_linestyle
{
UNICODE_LINESTYLE_SINGLE = 0,
UNICODE_LINESTYLE_DOUBLE
} unicode_linestyle;
struct separator
{
char *separator;
bool separator_zero;
};
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 */
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
* stdout is a tty) 0=off 1=on 2=always */
int pager_min_lines;/* don't use pager unless there are at least
* this many lines */
bool tuples_only; /* don't output headers, row counts, etc. */
bool start_table; /* print start decoration, eg <table> */
bool stop_table; /* print stop decoration, eg </table> */
bool default_footer; /* allow "(xx rows)" default footer */
unsigned long prior_records; /* start offset for record counters */
const printTextFormat *line_style; /* line style (NULL for default) */
struct separator fieldSep; /* field separator for unaligned text mode */
struct separator recordSep; /* record separator for unaligned text mode */
bool numericLocale; /* locale-aware numeric units separator and
* decimal marker */
char *tableAttr; /* attributes for HTML <table ...> */
int encoding; /* character encoding */
int env_columns; /* $COLUMNS on psql start, 0 is unset */
int columns; /* target width for wrapped format */
unicode_linestyle unicode_border_linestyle;
unicode_linestyle unicode_column_linestyle;
unicode_linestyle unicode_header_linestyle;
} printTableOpt;
/*
* Table footers are implemented as a singly-linked list.
*
* This is so that you don't need to know the number of footers in order to
* initialise the printTableContent struct, which is very convenient when
* preparing complex footers (as in describeOneTableDetails).
*/
typedef struct printTableFooter
{
char *data;
struct printTableFooter *next;
} printTableFooter;
/*
* The table content struct holds all the information which will be displayed
* by printTable().
*/
typedef struct printTableContent
{
const printTableOpt *opt;
const char *title; /* May be NULL */
int ncolumns; /* Specified in Init() */
int nrows; /* Specified in Init() */
const char **headers; /* NULL-terminated array of header strings */
const char **header; /* Pointer to the last added header */
const char **cells; /* NULL-terminated array of cell content
* strings */
const char **cell; /* Pointer to the last added cell */
long cellsadded; /* Number of cells added this far */
bool *cellmustfree; /* true for cells that need to be free()d */
printTableFooter *footers; /* Pointer to the first footer */
printTableFooter *footer; /* Pointer to the last added footer */
char *aligns; /* Array of alignment specifiers; 'l' or 'r',
* one per column */
char *align; /* Pointer to the last added alignment */
} printTableContent;
typedef struct printQueryOpt
{
printTableOpt topt; /* the options above */
char *nullPrint; /* how to print null entities */
bool quote; /* quote all values as much as possible */
char *title; /* override title */
char **footers; /* override footer (default is "(xx rows)") */
bool translate_header; /* do gettext on column headers */
const bool *translate_columns; /* translate_columns[i-1] => do
* gettext on col i */
int n_translate_columns; /* length of translate_columns[] */
} printQueryOpt;
extern const printTextFormat pg_asciiformat;
extern const printTextFormat pg_asciiformat_old;
extern const printTextFormat pg_utf8format;
extern FILE *PageOutput(int lines, const printTableOpt *topt);
extern void ClosePager(FILE *pagerpipe);
extern void html_escaped_print(const char *in, FILE *fout);
extern void printTableInit(printTableContent *const content,
const printTableOpt *opt, const char *title,
const int ncolumns, const int nrows);
extern void printTableAddHeader(printTableContent *const content,
char *header, const bool translate, const char align);
extern void printTableAddCell(printTableContent *const content,
char *cell, const bool translate, const bool mustfree);
extern void printTableAddFooter(printTableContent *const content,
const char *footer);
extern void printTableSetFooter(printTableContent *const content,
const char *footer);
extern void printTableCleanup(printTableContent *const content);
extern void printTable(const printTableContent *cont,
FILE *fout, bool is_pager, FILE *flog);
extern void printQuery(const PGresult *result, const printQueryOpt *opt,
FILE *fout, bool is_pager, FILE *flog);
extern void setDecimalLocale(void);
extern const printTextFormat *get_line_style(const printTableOpt *opt);
extern void refresh_utf8format(const printTableOpt *opt);
#ifndef __CYGWIN__
#define DEFAULT_PAGER "more"
#else
#define DEFAULT_PAGER "less"
#endif
#endif /* PRINT_H */