1/*-------------------------------------------------------------------------
4 * functions for pretty-printing query results
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * These functions were formerly part of fe-exec.c, but they
10 * didn't really belong there.
13 * src/interfaces/libpq/fe-print.c
15 *-------------------------------------------------------------------------
32#include <sys/termios.h>
41 const int i,
const int j,
const int fs_len,
43 const int nFields,
const char **fieldNames,
44 unsigned char *fieldNotNum,
int *fieldMax,
45 const int fieldMaxLen, FILE *fout);
47 int *fieldMax,
const char **fieldNames,
unsigned char *fieldNotNum,
48 const int fs_len,
const PGresult *res);
50 unsigned char *fieldNotNum,
int *fieldMax,
char *border,
52static void fill(
int length,
int max,
char filler, FILE *fp);
57 * Format results of a query for printing.
59 * PQprintOpt is a typedef (structure) that contains
60 * various flags and options. consult libpq-fe.h for
63 * This function should probably be removed sometime since psql
64 * doesn't use it anymore. It is unclear to what extent this is used
65 * by external clients, however.
75 {
/* only print rows with at least 1 field. */
79 int *fieldMax = NULL;
/* in case we don't use them */
80 unsigned char *fieldNotNum = NULL;
83 const char **fieldNames = NULL;
87 int total_line_length = 0;
93 bool sigpipe_masked =
false;
98 struct winsize screen_size;
108 fieldNames = (
const char **)
calloc(nFields,
sizeof(
char *));
109 fieldNotNum = (
unsigned char *)
calloc(nFields, 1);
110 fieldMax = (
int *)
calloc(nFields,
sizeof(
int));
111 if (!fieldNames || !fieldNotNum || !fieldMax)
116 for (numFieldName = 0;
120 for (
j = 0;
j < nFields;
j++)
123 const char *s = (
j < numFieldName && po->
fieldName[
j][0]) ?
127 len = s ? strlen(s) : 0;
130 if (
len > fieldMaxLen)
132 total_line_length +=
len;
135 total_line_length += nFields * strlen(po->
fieldSep) + 1;
139 if (po->
pager && fout ==
stdout && isatty(fileno(stdin)) &&
143 * If we think there'll be more than one screen of output, try to
144 * pipe to the pager program.
147 if (ioctl(fileno(
stdout), TIOCGWINSZ, &screen_size) == -1 ||
148 screen_size.ws_col == 0 ||
149 screen_size.ws_row == 0)
151 screen_size.ws_row = 24;
152 screen_size.ws_col = 80;
155 screen_size.ws_row = 24;
156 screen_size.ws_col = 80;
160 * Since this function is no longer used by psql, we don't examine
161 * PSQL_PAGER. It's possible that the hypothetical external users
162 * of the function would like that to happen, but in the name of
163 * backwards compatibility, we'll stick to just examining PAGER.
165 pagerenv = getenv(
"PAGER");
166 /* if PAGER is unset, empty or all-white-space, don't use pager */
167 if (pagerenv != NULL &&
168 strspn(pagerenv,
" \t\r\n") != strlen(pagerenv) &&
171 nTups * (nFields + 1) >= screen_size.ws_row) ||
173 nTups * (total_line_length / screen_size.ws_col + 1) *
174 (1 + (po->
standard != 0)) >= screen_size.ws_row -
176 (total_line_length / screen_size.ws_col + 1) * 2
177 - (po->
header != 0) * 2
/* row count and newline */
181 fout = popen(pagerenv,
"w");
187 sigpipe_masked =
true;
197 fields = (
char **)
calloc((
size_t) nTups + 1,
198 nFields *
sizeof(
char *));
219 for (
j = 0;
j < nFields;
j++)
221 const char *s = fieldNames[
j];
224 len += strlen(s) + fs_len;
225 if ((
j + 1) < nFields)
229 for (
len -= fs_len;
len--; fputc(
'-', fout));
240 "Query retrieved %d rows * %d fields"
244 for (
i = 0;
i < nTups;
i++)
250 "<table %s><caption align=\"top\">%d</caption>\n",
255 for (
j = 0;
j < nFields;
j++)
257 if (!
do_field(po, res,
i,
j, fs_len, fields, nFields,
258 fieldNames, fieldNotNum,
259 fieldMax, fieldMaxLen, fout))
263 fputs(
"</table>\n", fout);
273 "<table %s><caption align=\"top\">%s</caption>\n",
278 "<table %s><caption align=\"top\">"
279 "Retrieved %d rows * %d fields"
287 border =
do_header(fout, po, nFields, fieldMax, fieldNames,
288 fieldNotNum, fs_len, res);
289 for (
i = 0;
i < nTups;
i++)
291 fieldNotNum, fieldMax, border,
i);
297 fputs(
"</table>\n", fout);
305 /* if calloc succeeded, this shouldn't overflow size_t */
306 size_t numfields = ((size_t) nTups + 1) * (size_t) nFields;
308 while (numfields-- > 0)
309 free(fields[numfields]);
320 /* we can't easily verify if EPIPE occurred, so say it did */
331 const int i,
const int j,
const int fs_len,
333 const int nFields,
char const **fieldNames,
334 unsigned char *fieldNotNum,
int *fieldMax,
335 const int fieldMaxLen, FILE *fout)
345 if (plen < 1 || !pval || !*pval)
360 if (po->
align && !fieldNotNum[
j])
362 /* Detect whether field contains non-numeric data */
368 if (!((ch >=
'0' && ch <=
'9') ||
381 * Above loop will believe E in first column is numeric; also, we
382 * insist on a digit in the last column for a numeric. This test
383 * is still not bulletproof but it handles most cases.
385 if (*pval ==
'E' || *pval ==
'e' ||
386 !(ch >=
'0' && ch <=
'9'))
392 if (plen > fieldMax[
j])
394 if (!(fields[
i * nFields +
j] = (
char *)
malloc(plen + 1)))
399 strcpy(fields[
i * nFields +
j], pval);
407 "<tr><td align=\"left\"><b>%s</b></td>"
408 "<td align=\"%s\">%s</td></tr>\n",
410 fieldNotNum[
j] ?
"left" :
"right",
417 fieldMaxLen - fs_len, fieldNames[
j],
432 if ((
j + 1) < nFields)
446 const char **fieldNames,
unsigned char *fieldNotNum,
447 const int fs_len,
const PGresult *res)
449 int j;
/* for loop index */
460 for (; n < nFields; n++)
461 tot += fieldMax[n] + fs_len + (po->
standard ? 2 : 0);
463 tot += fs_len * 2 + 2;
478 for (
j = 0;
j < nFields;
j++)
497 for (
j = 0;
j < nFields;
j++)
503 fprintf(fout,
"<th align=\"%s\">%s</th>",
504 fieldNotNum[
j] ?
"left" :
"right", fieldNames[
j]);
514 fieldNotNum[
j] ?
" %-*s " :
" %*s ",
517 fprintf(fout, fieldNotNum[
j] ?
"%-*s" :
"%*s", fieldMax[
j], s);
523 fputs(
"</tr>\n", fout);
525 fprintf(fout,
"\n%s\n", border);
532 unsigned char *fieldNotNum,
int *fieldMax,
char *border,
535 int field_index;
/* for loop index */
541 for (field_index = 0; field_index < nFields; field_index++)
543 char *p = fields[row_index * nFields + field_index];
546 fprintf(fout,
"<td align=\"%s\">%s</td>",
547 fieldNotNum[field_index] ?
"left" :
"right", p ? p :
"");
551 fieldNotNum[field_index] ?
552 (po->
standard ?
" %-*s " :
"%-*s") :
554 fieldMax[field_index],
556 if (po->
standard || field_index + 1 < nFields)
561 fputs(
"</tr>", fout);
570 * really old printing routines
575 FILE *fp,
/* where to send the output */
576 int fillAlign,
/* pad the fields with spaces */
577 const char *fieldSep,
/* field separator */
578 int printHeader,
/* display headers? */
582#define DEFAULT_FIELD_SEP " "
590 if (fieldSep == NULL)
593 /* Get some useful info about the results */
600 /* Figure the field lengths to align to */
601 /* will be somewhat time consuming for very large results */
604 fLength = (
int *)
malloc(nFields *
sizeof(
int));
611 for (
j = 0;
j < nFields;
j++)
614 for (
i = 0;
i < nTuples;
i++)
618 if (flen > fLength[
j])
626 /* first, print out the attribute names */
627 for (
i = 0;
i < nFields;
i++)
636 /* Underline the attribute names */
637 for (
i = 0;
i < nFields;
i++)
640 fill(0, fLength[
i],
'-', fp);
646 /* next, print out the instances */
647 for (
i = 0;
i < nTuples;
i++)
649 for (
j = 0;
j < nFields;
j++)
672 FILE *fout,
/* output stream */
673 int PrintAttNames,
/* print attribute names or not */
674 int TerseOutput,
/* delimiter bars or not? */
675 int colWidth
/* width of column, if 0, use variable width */
682 char formatString[80];
683 char *tborder = NULL;
689 sprintf(formatString,
"%%s %%-%ds", colWidth);
691 sprintf(formatString,
"%%s %%s");
694 {
/* only print rows with at least 1 field. */
700 width = nFields * 14;
701 tborder = (
char *)
malloc(width + 1);
707 for (
i = 0;
i < width;
i++)
709 tborder[width] =
'0円';
710 fprintf(fout,
"%s\n", tborder);
713 for (
i = 0;
i < nFields;
i++)
718 TerseOutput ?
"" :
"|",
728 fprintf(fout,
"|\n%s\n", tborder);
731 for (
i = 0;
i < nTups;
i++)
733 for (
j = 0;
j < nFields;
j++)
738 TerseOutput ?
"" :
"|",
744 fprintf(fout,
"|\n%s\n", tborder);
752/* simply send out max-length number of filler characters to fp */
755 fill(
int length,
int max,
char filler, FILE *fp)
759 count = max - length;
#define fprintf(file, fmt, msg)
int PQmblenBounded(const char *s, int encoding)
void PQdisplayTuples(const PGresult *res, FILE *fp, int fillAlign, const char *fieldSep, int printHeader, int quiet)
static void fill(int length, int max, char filler, FILE *fp)
#define DEFAULT_FIELD_SEP
void PQprintTuples(const PGresult *res, FILE *fout, int PrintAttNames, int TerseOutput, int colWidth)
void PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
static char * do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum, const int fs_len, const PGresult *res)
static bool do_field(const PQprintOpt *po, const PGresult *res, const int i, const int j, const int fs_len, char **fields, const int nFields, const char **fieldNames, unsigned char *fieldNotNum, int *fieldMax, const int fieldMaxLen, FILE *fout)
static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields, unsigned char *fieldNotNum, int *fieldMax, char *border, const int row_index)
void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)