1 /*-------------------------------------------------------------------------
4 * functions for pretty-printing query results
6 * Portions Copyright (c) 1996-2004, 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.54 2004/08/29 05:07:00 momjian 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;
95 #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
96 pqsigfunc oldsigpipehandler = NULL;
100 struct winsize screen_size;
110 nTups = PQntuples(res);
111 if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *))))
116 if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1)))
121 if (!(fieldMax = (int *) calloc(nFields, sizeof(int))))
126 for (numFieldName = 0;
127 po->fieldName && po->fieldName[numFieldName];
130 for (j = 0; j < nFields; j++)
133 const char *s = (j < numFieldName && po->fieldName[j][0]) ?
134 po->fieldName[j] : PQfname(res, j);
137 len = s ? strlen(s) : 0;
140 if (len > fieldMaxLen)
142 total_line_length += len;
145 total_line_length += nFields * strlen(po->fieldSep) + 1;
149 if (po->pager && fout == stdout
152 isatty(fileno(stdin)) &&
153 isatty(fileno(stdout))
158 * If we think there'll be more than one screen of output, try
159 * to pipe to the pager program.
162 if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
163 screen_size.ws_col == 0 ||
164 screen_size.ws_row == 0)
166 screen_size.ws_row = 24;
167 screen_size.ws_col = 80;
170 screen_size.ws_row = 24;
171 screen_size.ws_col = 80;
173 pagerenv = getenv("PAGER");
174 if (pagerenv != NULL &&
175 pagerenv[0] != '\0' &&
178 nTups * (nFields + 1) >= screen_size.ws_row) ||
180 nTups * (total_line_length / screen_size.ws_col + 1) *
181 (1 + (po->standard != 0)) >= screen_size.ws_row -
183 (total_line_length / screen_size.ws_col + 1) * 2
184 - (po->header != 0) * 2 /* row count and newline */
187 fout = popen(pagerenv, "w");
191 #ifdef ENABLE_THREAD_SAFETY
192 pthread_setspecific(pq_thread_in_send, "t");
195 oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
204 if (!po->expanded && (po->align || po->html3))
206 if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
212 else if (po->header && !po->html3)
217 fprintf(fout, "%-*s%s Value\n",
218 fieldMaxLen - fs_len, "Field", po->fieldSep);
220 fprintf(fout, "%s%sValue\n", "Field", po->fieldSep);
226 for (j = 0; j < nFields; j++)
228 const char *s = fieldNames[j];
231 len += strlen(s) + fs_len;
232 if ((j + 1) < nFields)
233 fputs(po->fieldSep, fout);
236 for (len -= fs_len; len--; fputc('-', fout));
240 if (po->expanded && po->html3)
243 fprintf(fout, "<centre><h2>%s</h2></centre>\n", po->caption);
247 "Query retrieved %d rows * %d fields"
251 for (i = 0; i < nTups; i++)
257 "<table %s><caption align=high>%d</caption>\n",
258 po->tableOpt ? po->tableOpt : "", i);
260 fprintf(fout, "-- RECORD %d --\n", i);
262 for (j = 0; j < nFields; j++)
263 do_field(po, res, i, j, fs_len, fields, nFields,
264 fieldNames, fieldNotNum,
265 fieldMax, fieldMaxLen, fout);
266 if (po->html3 && po->expanded)
267 fputs("</table>\n", fout);
269 if (!po->expanded && (po->align || po->html3))
277 "<table %s><caption align=high>%s</caption>\n",
278 po->tableOpt ? po->tableOpt : "",
282 "<table %s><caption align=high>"
283 "Retrieved %d rows * %d fields"
285 po->tableOpt ? po->tableOpt : "", nTups, nFields);
288 fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
291 border = do_header(fout, po, nFields, fieldMax, fieldNames,
292 fieldNotNum, fs_len, res);
293 for (i = 0; i < nTups; i++)
294 output_row(fout, po, nFields, fields,
295 fieldNotNum, fieldMax, border, i);
300 if (po->header && !po->html3)
301 fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
302 (PQntuples(res) == 1) ? "" : "s");
305 free((void *) fieldNames);
313 #ifdef ENABLE_THREAD_SAFETY
314 pthread_setspecific(pq_thread_in_send, "f");
317 pqsignal(SIGPIPE, oldsigpipehandler);
321 if (po->html3 && !po->expanded)
322 fputs("</table>\n", fout);
328 do_field(const PQprintOpt *po, const PGresult *res,
329 const int i, const int j, const int fs_len,
331 const int nFields, char const ** fieldNames,
332 unsigned char *fieldNotNum, int *fieldMax,
333 const int fieldMaxLen, FILE *fout)
341 plen = PQgetlength(res, i, j);
342 pval = PQgetvalue(res, i, j);
344 if (plen < 1 || !pval || !*pval)
346 if (po->align || po->expanded)
359 if (po->align && !fieldNotNum[j])
361 /* Detect whether field contains non-numeric data */
364 for (p = pval; *p; p += PQmblen(p, res->client_encoding))
367 if (!((ch >= '0' && ch <= '9') ||
380 * Above loop will believe E in first column is numeric; also,
381 * we insist on a digit in the last column for a numeric. This
382 * test is still not bulletproof but it handles most cases.
384 if (*pval == 'E' || *pval == 'e' ||
385 !(ch >= '0' && ch <= '9'))
389 if (!po->expanded && (po->align || po->html3))
391 if (plen > fieldMax[j])
393 if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
398 strcpy(fields[i * nFields + j], pval);
406 "<tr><td align=left><b>%s</b></td>"
407 "<td align=%s>%s</td></tr>\n",
409 fieldNotNum[j] ? "left" : "right",
416 fieldMaxLen - fs_len, fieldNames[j],
422 fieldNames[j], po->fieldSep, pval);
431 if ((j + 1) < nFields)
432 fputs(po->fieldSep, fout);
443 do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
444 const char **fieldNames, unsigned char *fieldNotNum,
445 const int fs_len, const PGresult *res)
448 int j; /* for loop index */
459 for (; n < nFields; n++)
460 tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
462 tot += fs_len * 2 + 2;
463 border = malloc(tot + 1);
472 char *fs = po->fieldSep;
477 for (j = 0; j < nFields; j++)
481 for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
482 if (po->standard || (j + 1) < nFields)
484 char *fs = po->fieldSep;
492 fprintf(fout, "%s\n", border);
495 fputs(po->fieldSep, fout);
496 for (j = 0; j < nFields; j++)
498 const char *s = PQfname(res, j);
502 fprintf(fout, "<th align=%s>%s</th>",
503 fieldNotNum[j] ? "left" : "right", fieldNames[j]);
513 fieldNotNum[j] ? " %-*s " : " %*s ",
516 fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
517 if (po->standard || (j + 1) < nFields)
518 fputs(po->fieldSep, fout);
522 fputs("</tr>\n", fout);
524 fprintf(fout, "\n%s\n", border);
530 output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
531 unsigned char *fieldNotNum, int *fieldMax, char *border,
535 int field_index; /* for loop index */
539 else if (po->standard)
540 fputs(po->fieldSep, fout);
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") :
553 (po->standard ? " %*s " : "%*s"),
554 fieldMax[field_index],
556 if (po->standard || field_index + 1 < nFields)
557 fputs(po->fieldSep, fout);
563 fputs("</tr>", fout);
564 else if (po->standard)
565 fprintf(fout, "\n%s", border);
572 * really old printing routines
576 PQdisplayTuples(const PGresult *res,
577 FILE *fp, /* where to send the output */
578 int fillAlign, /* pad the fields with spaces */
579 const char *fieldSep, /* field separator */
580 int printHeader, /* display headers? */
584 #define DEFAULT_FIELD_SEP " "
592 if (fieldSep == NULL)
593 fieldSep = DEFAULT_FIELD_SEP;
595 /* Get some useful info about the results */
596 nFields = PQnfields(res);
597 nTuples = PQntuples(res);
602 /* Figure the field lengths to align to */
603 /* will be somewhat time consuming for very large results */
606 fLength = (int *) malloc(nFields * sizeof(int));
607 for (j = 0; j < nFields; j++)
609 fLength[j] = strlen(PQfname(res, j));
610 for (i = 0; i < nTuples; i++)
612 int flen = PQgetlength(res, i, j);
614 if (flen > fLength[j])
622 /* first, print out the attribute names */
623 for (i = 0; i < nFields; i++)
625 fputs(PQfname(res, i), fp);
627 fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
632 /* Underline the attribute names */
633 for (i = 0; i < nFields; i++)
636 fill(0, fLength[i], '-', fp);
642 /* next, print out the instances */
643 for (i = 0; i < nTuples; i++)
645 for (j = 0; j < nFields; j++)
647 fprintf(fp, "%s", PQgetvalue(res, i, j));
649 fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
656 fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
657 (PQntuples(res) == 1) ? "" : "s");
668 PQprintTuples(const PGresult *res,
669 FILE *fout, /* output stream */
670 int PrintAttNames, /* print attribute names or not */
671 int TerseOutput, /* delimiter bars or not? */
672 int colWidth /* width of column, if 0, use variable
680 char formatString[80];
682 char *tborder = NULL;
684 nFields = PQnfields(res);
685 nTups = PQntuples(res);
688 sprintf(formatString, "%%s %%-%ds", colWidth);
690 sprintf(formatString, "%%s %%s");
693 { /* only print rows with at least 1 field. */
699 width = nFields * 14;
700 tborder = malloc(width + 1);
701 for (i = 0; i <= width; i++)
704 fprintf(fout, "%s\n", tborder);
707 for (i = 0; i < nFields; i++)
711 fprintf(fout, formatString,
712 TerseOutput ? "" : "|",
722 fprintf(fout, "|\n%s\n", tborder);
725 for (i = 0; i < nTups; i++)
727 for (j = 0; j < nFields; j++)
729 const char *pval = PQgetvalue(res, i, j);
731 fprintf(fout, formatString,
732 TerseOutput ? "" : "|",
738 fprintf(fout, "|\n%s\n", tborder);
745 /* simply send out max-length number of filler characters to fp */
748 fill(int length, int max, char filler, FILE *fp)
752 count = max - length;