1 /*-------------------------------------------------------------------------
4 * functions for pretty-printing query results
6 * Portions Copyright (c) 1996-2005, 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 * $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.58 2004/12/31 22:03:50 pgsql Exp $
15 *-------------------------------------------------------------------------
17 #include "postgres_fe.h"
25 #include <sys/ioctl.h>
32 #include <sys/termios.h>
37 #include "libpq-int.h"
41 static void do_field(const PQprintOpt *po, const PGresult *res,
42 const int i, const int j, const int fs_len,
44 const int nFields, const char **fieldNames,
45 unsigned char *fieldNotNum, int *fieldMax,
46 const int fieldMaxLen, FILE *fout);
47 static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields,
48 int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum,
49 const int fs_len, const PGresult *res);
50 static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
51 unsigned char *fieldNotNum, int *fieldMax, char *border,
53 static void fill(int length, int max, char filler, FILE *fp);
58 * Format results of a query for printing.
60 * PQprintOpt is a typedef (structure) that containes
61 * various flags and options. consult libpq-fe.h for
64 * This function should probably be removed sometime since psql
65 * doesn't use it anymore. It is unclear to what extend this is used
66 * by external clients, however.
76 nFields = PQnfields(res);
79 { /* only print rows with at least 1 field. */
83 int *fieldMax = NULL; /* in case we don't use them */
84 unsigned char *fieldNotNum = NULL;
87 const char **fieldNames;
90 int fs_len = strlen(po->fieldSep);
91 int total_line_length = 0;
94 #ifdef ENABLE_THREAD_SAFETY
96 bool sigpipe_masked = false;
99 #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
100 pqsigfunc oldsigpipehandler = NULL;
104 struct winsize screen_size;
114 nTups = PQntuples(res);
115 if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *))))
117 fprintf(stderr, libpq_gettext("out of memory\n"));
120 if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1)))
122 fprintf(stderr, libpq_gettext("out of memory\n"));
125 if (!(fieldMax = (int *) calloc(nFields, sizeof(int))))
127 fprintf(stderr, libpq_gettext("out of memory\n"));
130 for (numFieldName = 0;
131 po->fieldName && po->fieldName[numFieldName];
134 for (j = 0; j < nFields; j++)
137 const char *s = (j < numFieldName && po->fieldName[j][0]) ?
138 po->fieldName[j] : PQfname(res, j);
141 len = s ? strlen(s) : 0;
144 if (len > fieldMaxLen)
146 total_line_length += len;
149 total_line_length += nFields * strlen(po->fieldSep) + 1;
153 if (po->pager && fout == stdout
156 isatty(fileno(stdin)) &&
157 isatty(fileno(stdout))
162 * If we think there'll be more than one screen of output, try
163 * to pipe to the pager program.
166 if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
167 screen_size.ws_col == 0 ||
168 screen_size.ws_row == 0)
170 screen_size.ws_row = 24;
171 screen_size.ws_col = 80;
174 screen_size.ws_row = 24;
175 screen_size.ws_col = 80;
177 pagerenv = getenv("PAGER");
178 if (pagerenv != NULL &&
179 pagerenv[0] != '\0' &&
182 nTups * (nFields + 1) >= screen_size.ws_row) ||
184 nTups * (total_line_length / screen_size.ws_col + 1) *
185 (1 + (po->standard != 0)) >= screen_size.ws_row -
187 (total_line_length / screen_size.ws_col + 1) * 2
188 - (po->header != 0) * 2 /* row count and newline */
191 fout = popen(pagerenv, "w");
195 #ifdef ENABLE_THREAD_SAFETY
196 if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
197 sigpipe_masked = true;
200 oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
209 if (!po->expanded && (po->align || po->html3))
211 if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
213 fprintf(stderr, libpq_gettext("out of memory\n"));
217 else if (po->header && !po->html3)
222 fprintf(fout, "%-*s%s Value\n",
223 fieldMaxLen - fs_len, "Field", po->fieldSep);
225 fprintf(fout, "%s%sValue\n", "Field", po->fieldSep);
231 for (j = 0; j < nFields; j++)
233 const char *s = fieldNames[j];
236 len += strlen(s) + fs_len;
237 if ((j + 1) < nFields)
238 fputs(po->fieldSep, fout);
241 for (len -= fs_len; len--; fputc('-', fout));
245 if (po->expanded && po->html3)
248 fprintf(fout, "<centre><h2>%s</h2></centre>\n", po->caption);
252 "Query retrieved %d rows * %d fields"
256 for (i = 0; i < nTups; i++)
262 "<table %s><caption align=high>%d</caption>\n",
263 po->tableOpt ? po->tableOpt : "", i);
265 fprintf(fout, "-- RECORD %d --\n", i);
267 for (j = 0; j < nFields; j++)
268 do_field(po, res, i, j, fs_len, fields, nFields,
269 fieldNames, fieldNotNum,
270 fieldMax, fieldMaxLen, fout);
271 if (po->html3 && po->expanded)
272 fputs("</table>\n", fout);
274 if (!po->expanded && (po->align || po->html3))
282 "<table %s><caption align=high>%s</caption>\n",
283 po->tableOpt ? po->tableOpt : "",
287 "<table %s><caption align=high>"
288 "Retrieved %d rows * %d fields"
290 po->tableOpt ? po->tableOpt : "", nTups, nFields);
293 fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
296 border = do_header(fout, po, nFields, fieldMax, fieldNames,
297 fieldNotNum, fs_len, res);
298 for (i = 0; i < nTups; i++)
299 output_row(fout, po, nFields, fields,
300 fieldNotNum, fieldMax, border, i);
305 if (po->header && !po->html3)
306 fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
307 (PQntuples(res) == 1) ? "" : "s");
310 free((void *) fieldNames);
318 #ifdef ENABLE_THREAD_SAFETY
319 /* we can't easily verify if EPIPE occurred, so say it did */
321 pq_reset_sigpipe(&osigset, sigpipe_pending, true);
324 pqsignal(SIGPIPE, oldsigpipehandler);
328 if (po->html3 && !po->expanded)
329 fputs("</table>\n", fout);
335 do_field(const PQprintOpt *po, const PGresult *res,
336 const int i, const int j, const int fs_len,
338 const int nFields, char const ** fieldNames,
339 unsigned char *fieldNotNum, int *fieldMax,
340 const int fieldMaxLen, FILE *fout)
348 plen = PQgetlength(res, i, j);
349 pval = PQgetvalue(res, i, j);
351 if (plen < 1 || !pval || !*pval)
353 if (po->align || po->expanded)
366 if (po->align && !fieldNotNum[j])
368 /* Detect whether field contains non-numeric data */
371 for (p = pval; *p; p += PQmblen(p, res->client_encoding))
374 if (!((ch >= '0' && ch <= '9') ||
387 * Above loop will believe E in first column is numeric; also,
388 * we insist on a digit in the last column for a numeric. This
389 * test is still not bulletproof but it handles most cases.
391 if (*pval == 'E' || *pval == 'e' ||
392 !(ch >= '0' && ch <= '9'))
396 if (!po->expanded && (po->align || po->html3))
398 if (plen > fieldMax[j])
400 if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
402 fprintf(stderr, libpq_gettext("out of memory\n"));
405 strcpy(fields[i * nFields + j], pval);
413 "<tr><td align=left><b>%s</b></td>"
414 "<td align=%s>%s</td></tr>\n",
416 fieldNotNum[j] ? "left" : "right",
423 fieldMaxLen - fs_len, fieldNames[j],
429 fieldNames[j], po->fieldSep, pval);
438 if ((j + 1) < nFields)
439 fputs(po->fieldSep, fout);
450 do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
451 const char **fieldNames, unsigned char *fieldNotNum,
452 const int fs_len, const PGresult *res)
455 int j; /* for loop index */
466 for (; n < nFields; n++)
467 tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
469 tot += fs_len * 2 + 2;
470 border = malloc(tot + 1);
473 fprintf(stderr, libpq_gettext("out of memory\n"));
479 char *fs = po->fieldSep;
484 for (j = 0; j < nFields; j++)
488 for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
489 if (po->standard || (j + 1) < nFields)
491 char *fs = po->fieldSep;
499 fprintf(fout, "%s\n", border);
502 fputs(po->fieldSep, fout);
503 for (j = 0; j < nFields; j++)
505 const char *s = PQfname(res, j);
509 fprintf(fout, "<th align=%s>%s</th>",
510 fieldNotNum[j] ? "left" : "right", fieldNames[j]);
520 fieldNotNum[j] ? " %-*s " : " %*s ",
523 fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
524 if (po->standard || (j + 1) < nFields)
525 fputs(po->fieldSep, fout);
529 fputs("</tr>\n", fout);
531 fprintf(fout, "\n%s\n", border);
537 output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
538 unsigned char *fieldNotNum, int *fieldMax, char *border,
542 int field_index; /* for loop index */
546 else if (po->standard)
547 fputs(po->fieldSep, fout);
548 for (field_index = 0; field_index < nFields; field_index++)
550 char *p = fields[row_index * nFields + field_index];
553 fprintf(fout, "<td align=%s>%s</td>",
554 fieldNotNum[field_index] ? "left" : "right", p ? p : "");
558 fieldNotNum[field_index] ?
559 (po->standard ? " %-*s " : "%-*s") :
560 (po->standard ? " %*s " : "%*s"),
561 fieldMax[field_index],
563 if (po->standard || field_index + 1 < nFields)
564 fputs(po->fieldSep, fout);
570 fputs("</tr>", fout);
571 else if (po->standard)
572 fprintf(fout, "\n%s", border);
579 * really old printing routines
583 PQdisplayTuples(const PGresult *res,
584 FILE *fp, /* where to send the output */
585 int fillAlign, /* pad the fields with spaces */
586 const char *fieldSep, /* field separator */
587 int printHeader, /* display headers? */
591 #define DEFAULT_FIELD_SEP " "
599 if (fieldSep == NULL)
600 fieldSep = DEFAULT_FIELD_SEP;
602 /* Get some useful info about the results */
603 nFields = PQnfields(res);
604 nTuples = PQntuples(res);
609 /* Figure the field lengths to align to */
610 /* will be somewhat time consuming for very large results */
613 fLength = (int *) malloc(nFields * sizeof(int));
614 for (j = 0; j < nFields; j++)
616 fLength[j] = strlen(PQfname(res, j));
617 for (i = 0; i < nTuples; i++)
619 int flen = PQgetlength(res, i, j);
621 if (flen > fLength[j])
629 /* first, print out the attribute names */
630 for (i = 0; i < nFields; i++)
632 fputs(PQfname(res, i), fp);
634 fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
639 /* Underline the attribute names */
640 for (i = 0; i < nFields; i++)
643 fill(0, fLength[i], '-', fp);
649 /* next, print out the instances */
650 for (i = 0; i < nTuples; i++)
652 for (j = 0; j < nFields; j++)
654 fprintf(fp, "%s", PQgetvalue(res, i, j));
656 fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
663 fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
664 (PQntuples(res) == 1) ? "" : "s");
675 PQprintTuples(const PGresult *res,
676 FILE *fout, /* output stream */
677 int PrintAttNames, /* print attribute names or not */
678 int TerseOutput, /* delimiter bars or not? */
679 int colWidth /* width of column, if 0, use variable
687 char formatString[80];
689 char *tborder = NULL;
691 nFields = PQnfields(res);
692 nTups = PQntuples(res);
695 sprintf(formatString, "%%s %%-%ds", colWidth);
697 sprintf(formatString, "%%s %%s");
700 { /* only print rows with at least 1 field. */
706 width = nFields * 14;
707 tborder = malloc(width + 1);
708 for (i = 0; i <= width; i++)
711 fprintf(fout, "%s\n", tborder);
714 for (i = 0; i < nFields; i++)
718 fprintf(fout, formatString,
719 TerseOutput ? "" : "|",
729 fprintf(fout, "|\n%s\n", tborder);
732 for (i = 0; i < nTups; i++)
734 for (j = 0; j < nFields; j++)
736 const char *pval = PQgetvalue(res, i, j);
738 fprintf(fout, formatString,
739 TerseOutput ? "" : "|",
745 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;