OSDN Git Service

345ebc263eb4e06466cfbd4a40dc954eaa3e024e
[pg-rex/syncrep.git] / src / interfaces / libpq / fe-print.c
1 /*-------------------------------------------------------------------------
2  *
3  * fe-print.c
4  *        functions for pretty-printing query results
5  *
6  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * These functions were formerly part of fe-exec.c, but they
10  * didn't really belong there.
11  *
12  * IDENTIFICATION
13  *        $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.58 2004/12/31 22:03:50 pgsql Exp $
14  *
15  *-------------------------------------------------------------------------
16  */
17 #include "postgres_fe.h"
18
19 #include <signal.h>
20
21 #ifdef WIN32
22 #include "win32.h"
23 #else
24 #include <unistd.h>
25 #include <sys/ioctl.h>
26 #endif
27
28 #ifdef HAVE_TERMIOS_H
29 #include <termios.h>
30 #else
31 #ifndef WIN32
32 #include <sys/termios.h>
33 #endif
34 #endif
35
36 #include "libpq-fe.h"
37 #include "libpq-int.h"
38 #include "pqsignal.h"
39
40
41 static void do_field(const PQprintOpt *po, const PGresult *res,
42                  const int i, const int j, const int fs_len,
43                  char **fields,
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,
52                    const int row_index);
53 static void fill(int length, int max, char filler, FILE *fp);
54
55 /*
56  * PQprint()
57  *
58  * Format results of a query for printing.
59  *
60  * PQprintOpt is a typedef (structure) that containes
61  * various flags and options. consult libpq-fe.h for
62  * details
63  *
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.
67  */
68
69 void
70 PQprint(FILE *fout,
71                 const PGresult *res,
72                 const PQprintOpt *po)
73 {
74         int                     nFields;
75
76         nFields = PQnfields(res);
77
78         if (nFields > 0)
79         {                                                       /* only print rows with at least 1 field.  */
80                 int                     i,
81                                         j;
82                 int                     nTups;
83                 int                *fieldMax = NULL;    /* in case we don't use them */
84                 unsigned char *fieldNotNum = NULL;
85                 char       *border = NULL;
86                 char      **fields = NULL;
87                 const char **fieldNames;
88                 int                     fieldMaxLen = 0;
89                 int                     numFieldName;
90                 int                     fs_len = strlen(po->fieldSep);
91                 int                     total_line_length = 0;
92                 int                     usePipe = 0;
93                 char       *pagerenv;
94 #ifdef ENABLE_THREAD_SAFETY
95                 sigset_t        osigset;
96                 bool            sigpipe_masked = false;
97                 bool            sigpipe_pending;
98 #endif
99 #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
100                 pqsigfunc       oldsigpipehandler = NULL;
101 #endif
102
103 #ifdef TIOCGWINSZ
104                 struct winsize screen_size;
105
106 #else
107                 struct winsize
108                 {
109                         int                     ws_row;
110                         int                     ws_col;
111                 }                       screen_size;
112 #endif
113
114                 nTups = PQntuples(res);
115                 if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *))))
116                 {
117                         fprintf(stderr, libpq_gettext("out of memory\n"));
118                         exit(1);
119                 }
120                 if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1)))
121                 {
122                         fprintf(stderr, libpq_gettext("out of memory\n"));
123                         exit(1);
124                 }
125                 if (!(fieldMax = (int *) calloc(nFields, sizeof(int))))
126                 {
127                         fprintf(stderr, libpq_gettext("out of memory\n"));
128                         exit(1);
129                 }
130                 for (numFieldName = 0;
131                          po->fieldName && po->fieldName[numFieldName];
132                          numFieldName++)
133                         ;
134                 for (j = 0; j < nFields; j++)
135                 {
136                         int                     len;
137                         const char *s = (j < numFieldName && po->fieldName[j][0]) ?
138                         po->fieldName[j] : PQfname(res, j);
139
140                         fieldNames[j] = s;
141                         len = s ? strlen(s) : 0;
142                         fieldMax[j] = len;
143                         len += fs_len;
144                         if (len > fieldMaxLen)
145                                 fieldMaxLen = len;
146                         total_line_length += len;
147                 }
148
149                 total_line_length += nFields * strlen(po->fieldSep) + 1;
150
151                 if (fout == NULL)
152                         fout = stdout;
153                 if (po->pager && fout == stdout
154 #ifndef WIN32
155                         &&
156                         isatty(fileno(stdin)) &&
157                         isatty(fileno(stdout))
158 #endif
159                         )
160                 {
161                         /*
162                          * If we think there'll be more than one screen of output, try
163                          * to pipe to the pager program.
164                          */
165 #ifdef TIOCGWINSZ
166                         if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
167                                 screen_size.ws_col == 0 ||
168                                 screen_size.ws_row == 0)
169                         {
170                                 screen_size.ws_row = 24;
171                                 screen_size.ws_col = 80;
172                         }
173 #else
174                         screen_size.ws_row = 24;
175                         screen_size.ws_col = 80;
176 #endif
177                         pagerenv = getenv("PAGER");
178                         if (pagerenv != NULL &&
179                                 pagerenv[0] != '\0' &&
180                                 !po->html3 &&
181                                 ((po->expanded &&
182                                   nTups * (nFields + 1) >= screen_size.ws_row) ||
183                                  (!po->expanded &&
184                                   nTups * (total_line_length / screen_size.ws_col + 1) *
185                                   (1 + (po->standard != 0)) >= screen_size.ws_row -
186                                   (po->header != 0) *
187                                   (total_line_length / screen_size.ws_col + 1) * 2
188                                   - (po->header != 0) * 2               /* row count and newline */
189                                   )))
190                         {
191                                 fout = popen(pagerenv, "w");
192                                 if (fout)
193                                 {
194                                         usePipe = 1;
195 #ifdef ENABLE_THREAD_SAFETY
196                                         if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
197                                                 sigpipe_masked = true;
198 #else
199 #ifndef WIN32
200                                         oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
201 #endif
202 #endif
203                                 }
204                                 else
205                                         fout = stdout;
206                         }
207                 }
208
209                 if (!po->expanded && (po->align || po->html3))
210                 {
211                         if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
212                         {
213                                 fprintf(stderr, libpq_gettext("out of memory\n"));
214                                 exit(1);
215                         }
216                 }
217                 else if (po->header && !po->html3)
218                 {
219                         if (po->expanded)
220                         {
221                                 if (po->align)
222                                         fprintf(fout, "%-*s%s Value\n",
223                                                         fieldMaxLen - fs_len, "Field", po->fieldSep);
224                                 else
225                                         fprintf(fout, "%s%sValue\n", "Field", po->fieldSep);
226                         }
227                         else
228                         {
229                                 int                     len = 0;
230
231                                 for (j = 0; j < nFields; j++)
232                                 {
233                                         const char *s = fieldNames[j];
234
235                                         fputs(s, fout);
236                                         len += strlen(s) + fs_len;
237                                         if ((j + 1) < nFields)
238                                                 fputs(po->fieldSep, fout);
239                                 }
240                                 fputc('\n', fout);
241                                 for (len -= fs_len; len--; fputc('-', fout));
242                                 fputc('\n', fout);
243                         }
244                 }
245                 if (po->expanded && po->html3)
246                 {
247                         if (po->caption)
248                                 fprintf(fout, "<centre><h2>%s</h2></centre>\n", po->caption);
249                         else
250                                 fprintf(fout,
251                                                 "<centre><h2>"
252                                                 "Query retrieved %d rows * %d fields"
253                                                 "</h2></centre>\n",
254                                                 nTups, nFields);
255                 }
256                 for (i = 0; i < nTups; i++)
257                 {
258                         if (po->expanded)
259                         {
260                                 if (po->html3)
261                                         fprintf(fout,
262                                                   "<table %s><caption align=high>%d</caption>\n",
263                                                         po->tableOpt ? po->tableOpt : "", i);
264                                 else
265                                         fprintf(fout, "-- RECORD %d --\n", i);
266                         }
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);
273                 }
274                 if (!po->expanded && (po->align || po->html3))
275                 {
276                         if (po->html3)
277                         {
278                                 if (po->header)
279                                 {
280                                         if (po->caption)
281                                                 fprintf(fout,
282                                                   "<table %s><caption align=high>%s</caption>\n",
283                                                                 po->tableOpt ? po->tableOpt : "",
284                                                                 po->caption);
285                                         else
286                                                 fprintf(fout,
287                                                                 "<table %s><caption align=high>"
288                                                                 "Retrieved %d rows * %d fields"
289                                                                 "</caption>\n",
290                                                 po->tableOpt ? po->tableOpt : "", nTups, nFields);
291                                 }
292                                 else
293                                         fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
294                         }
295                         if (po->header)
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);
301                         free(fields);
302                         if (border)
303                                 free(border);
304                 }
305                 if (po->header && !po->html3)
306                         fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
307                                         (PQntuples(res) == 1) ? "" : "s");
308                 free(fieldMax);
309                 free(fieldNotNum);
310                 free((void *) fieldNames);
311                 if (usePipe)
312                 {
313 #ifdef WIN32
314                         _pclose(fout);
315 #else
316                         pclose(fout);
317 #endif
318 #ifdef ENABLE_THREAD_SAFETY
319                         /* we can't easily verify if EPIPE occurred, so say it did */
320                         if (sigpipe_masked)
321                                 pq_reset_sigpipe(&osigset, sigpipe_pending, true);
322 #else
323 #ifndef WIN32
324                         pqsignal(SIGPIPE, oldsigpipehandler);
325 #endif
326 #endif
327                 }
328                 if (po->html3 && !po->expanded)
329                         fputs("</table>\n", fout);
330         }
331 }
332
333
334 static void
335 do_field(const PQprintOpt *po, const PGresult *res,
336                  const int i, const int j, const int fs_len,
337                  char **fields,
338                  const int nFields, char const ** fieldNames,
339                  unsigned char *fieldNotNum, int *fieldMax,
340                  const int fieldMaxLen, FILE *fout)
341 {
342
343         const char *pval,
344                            *p;
345         int                     plen;
346         bool            skipit;
347
348         plen = PQgetlength(res, i, j);
349         pval = PQgetvalue(res, i, j);
350
351         if (plen < 1 || !pval || !*pval)
352         {
353                 if (po->align || po->expanded)
354                         skipit = true;
355                 else
356                 {
357                         skipit = false;
358                         goto efield;
359                 }
360         }
361         else
362                 skipit = false;
363
364         if (!skipit)
365         {
366                 if (po->align && !fieldNotNum[j])
367                 {
368                         /* Detect whether field contains non-numeric data */
369                         char            ch = '0';
370
371                         for (p = pval; *p; p += PQmblen(p, res->client_encoding))
372                         {
373                                 ch = *p;
374                                 if (!((ch >= '0' && ch <= '9') ||
375                                           ch == '.' ||
376                                           ch == 'E' ||
377                                           ch == 'e' ||
378                                           ch == ' ' ||
379                                           ch == '-'))
380                                 {
381                                         fieldNotNum[j] = 1;
382                                         break;
383                                 }
384                         }
385
386                         /*
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.
390                          */
391                         if (*pval == 'E' || *pval == 'e' ||
392                                 !(ch >= '0' && ch <= '9'))
393                                 fieldNotNum[j] = 1;
394                 }
395
396                 if (!po->expanded && (po->align || po->html3))
397                 {
398                         if (plen > fieldMax[j])
399                                 fieldMax[j] = plen;
400                         if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
401                         {
402                                 fprintf(stderr, libpq_gettext("out of memory\n"));
403                                 exit(1);
404                         }
405                         strcpy(fields[i * nFields + j], pval);
406                 }
407                 else
408                 {
409                         if (po->expanded)
410                         {
411                                 if (po->html3)
412                                         fprintf(fout,
413                                                         "<tr><td align=left><b>%s</b></td>"
414                                                         "<td align=%s>%s</td></tr>\n",
415                                                         fieldNames[j],
416                                                         fieldNotNum[j] ? "left" : "right",
417                                                         pval);
418                                 else
419                                 {
420                                         if (po->align)
421                                                 fprintf(fout,
422                                                                 "%-*s%s %s\n",
423                                                                 fieldMaxLen - fs_len, fieldNames[j],
424                                                                 po->fieldSep,
425                                                                 pval);
426                                         else
427                                                 fprintf(fout,
428                                                                 "%s%s%s\n",
429                                                                 fieldNames[j], po->fieldSep, pval);
430                                 }
431                         }
432                         else
433                         {
434                                 if (!po->html3)
435                                 {
436                                         fputs(pval, fout);
437                         efield:
438                                         if ((j + 1) < nFields)
439                                                 fputs(po->fieldSep, fout);
440                                         else
441                                                 fputc('\n', fout);
442                                 }
443                         }
444                 }
445         }
446 }
447
448
449 static char *
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)
453 {
454
455         int                     j;                              /* for loop index */
456         char       *border = NULL;
457
458         if (po->html3)
459                 fputs("<tr>", fout);
460         else
461         {
462                 int                     tot = 0;
463                 int                     n = 0;
464                 char       *p = NULL;
465
466                 for (; n < nFields; n++)
467                         tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
468                 if (po->standard)
469                         tot += fs_len * 2 + 2;
470                 border = malloc(tot + 1);
471                 if (!border)
472                 {
473                         fprintf(stderr, libpq_gettext("out of memory\n"));
474                         exit(1);
475                 }
476                 p = border;
477                 if (po->standard)
478                 {
479                         char       *fs = po->fieldSep;
480
481                         while (*fs++)
482                                 *p++ = '+';
483                 }
484                 for (j = 0; j < nFields; j++)
485                 {
486                         int                     len;
487
488                         for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
489                         if (po->standard || (j + 1) < nFields)
490                         {
491                                 char       *fs = po->fieldSep;
492
493                                 while (*fs++)
494                                         *p++ = '+';
495                         }
496                 }
497                 *p = '\0';
498                 if (po->standard)
499                         fprintf(fout, "%s\n", border);
500         }
501         if (po->standard)
502                 fputs(po->fieldSep, fout);
503         for (j = 0; j < nFields; j++)
504         {
505                 const char *s = PQfname(res, j);
506
507                 if (po->html3)
508                 {
509                         fprintf(fout, "<th align=%s>%s</th>",
510                                         fieldNotNum[j] ? "left" : "right", fieldNames[j]);
511                 }
512                 else
513                 {
514                         int                     n = strlen(s);
515
516                         if (n > fieldMax[j])
517                                 fieldMax[j] = n;
518                         if (po->standard)
519                                 fprintf(fout,
520                                                 fieldNotNum[j] ? " %-*s " : " %*s ",
521                                                 fieldMax[j], s);
522                         else
523                                 fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
524                         if (po->standard || (j + 1) < nFields)
525                                 fputs(po->fieldSep, fout);
526                 }
527         }
528         if (po->html3)
529                 fputs("</tr>\n", fout);
530         else
531                 fprintf(fout, "\n%s\n", border);
532         return border;
533 }
534
535
536 static void
537 output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
538                    unsigned char *fieldNotNum, int *fieldMax, char *border,
539                    const int row_index)
540 {
541
542         int                     field_index;    /* for loop index */
543
544         if (po->html3)
545                 fputs("<tr>", fout);
546         else if (po->standard)
547                 fputs(po->fieldSep, fout);
548         for (field_index = 0; field_index < nFields; field_index++)
549         {
550                 char       *p = fields[row_index * nFields + field_index];
551
552                 if (po->html3)
553                         fprintf(fout, "<td align=%s>%s</td>",
554                                 fieldNotNum[field_index] ? "left" : "right", p ? p : "");
555                 else
556                 {
557                         fprintf(fout,
558                                         fieldNotNum[field_index] ?
559                                         (po->standard ? " %-*s " : "%-*s") :
560                                         (po->standard ? " %*s " : "%*s"),
561                                         fieldMax[field_index],
562                                         p ? p : "");
563                         if (po->standard || field_index + 1 < nFields)
564                                 fputs(po->fieldSep, fout);
565                 }
566                 if (p)
567                         free(p);
568         }
569         if (po->html3)
570                 fputs("</tr>", fout);
571         else if (po->standard)
572                 fprintf(fout, "\n%s", border);
573         fputc('\n', fout);
574 }
575
576
577
578 /*
579  * really old printing routines
580  */
581
582 void
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? */
588                                 int quiet
589 )
590 {
591 #define DEFAULT_FIELD_SEP " "
592
593         int                     i,
594                                 j;
595         int                     nFields;
596         int                     nTuples;
597         int                *fLength = NULL;
598
599         if (fieldSep == NULL)
600                 fieldSep = DEFAULT_FIELD_SEP;
601
602         /* Get some useful info about the results */
603         nFields = PQnfields(res);
604         nTuples = PQntuples(res);
605
606         if (fp == NULL)
607                 fp = stdout;
608
609         /* Figure the field lengths to align to */
610         /* will be somewhat time consuming for very large results */
611         if (fillAlign)
612         {
613                 fLength = (int *) malloc(nFields * sizeof(int));
614                 for (j = 0; j < nFields; j++)
615                 {
616                         fLength[j] = strlen(PQfname(res, j));
617                         for (i = 0; i < nTuples; i++)
618                         {
619                                 int                     flen = PQgetlength(res, i, j);
620
621                                 if (flen > fLength[j])
622                                         fLength[j] = flen;
623                         }
624                 }
625         }
626
627         if (printHeader)
628         {
629                 /* first, print out the attribute names */
630                 for (i = 0; i < nFields; i++)
631                 {
632                         fputs(PQfname(res, i), fp);
633                         if (fillAlign)
634                                 fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
635                         fputs(fieldSep, fp);
636                 }
637                 fprintf(fp, "\n");
638
639                 /* Underline the attribute names */
640                 for (i = 0; i < nFields; i++)
641                 {
642                         if (fillAlign)
643                                 fill(0, fLength[i], '-', fp);
644                         fputs(fieldSep, fp);
645                 }
646                 fprintf(fp, "\n");
647         }
648
649         /* next, print out the instances */
650         for (i = 0; i < nTuples; i++)
651         {
652                 for (j = 0; j < nFields; j++)
653                 {
654                         fprintf(fp, "%s", PQgetvalue(res, i, j));
655                         if (fillAlign)
656                                 fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
657                         fputs(fieldSep, fp);
658                 }
659                 fprintf(fp, "\n");
660         }
661
662         if (!quiet)
663                 fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
664                                 (PQntuples(res) == 1) ? "" : "s");
665
666         fflush(fp);
667
668         if (fLength)
669                 free(fLength);
670 }
671
672
673
674 void
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
680                                                                  * width */
681 )
682 {
683         int                     nFields;
684         int                     nTups;
685         int                     i,
686                                 j;
687         char            formatString[80];
688
689         char       *tborder = NULL;
690
691         nFields = PQnfields(res);
692         nTups = PQntuples(res);
693
694         if (colWidth > 0)
695                 sprintf(formatString, "%%s %%-%ds", colWidth);
696         else
697                 sprintf(formatString, "%%s %%s");
698
699         if (nFields > 0)
700         {                                                       /* only print rows with at least 1 field.  */
701
702                 if (!TerseOutput)
703                 {
704                         int                     width;
705
706                         width = nFields * 14;
707                         tborder = malloc(width + 1);
708                         for (i = 0; i <= width; i++)
709                                 tborder[i] = '-';
710                         tborder[i] = '\0';
711                         fprintf(fout, "%s\n", tborder);
712                 }
713
714                 for (i = 0; i < nFields; i++)
715                 {
716                         if (PrintAttNames)
717                         {
718                                 fprintf(fout, formatString,
719                                                 TerseOutput ? "" : "|",
720                                                 PQfname(res, i));
721                         }
722                 }
723
724                 if (PrintAttNames)
725                 {
726                         if (TerseOutput)
727                                 fprintf(fout, "\n");
728                         else
729                                 fprintf(fout, "|\n%s\n", tborder);
730                 }
731
732                 for (i = 0; i < nTups; i++)
733                 {
734                         for (j = 0; j < nFields; j++)
735                         {
736                                 const char *pval = PQgetvalue(res, i, j);
737
738                                 fprintf(fout, formatString,
739                                                 TerseOutput ? "" : "|",
740                                                 pval ? pval : "");
741                         }
742                         if (TerseOutput)
743                                 fprintf(fout, "\n");
744                         else
745                                 fprintf(fout, "|\n%s\n", tborder);
746                 }
747         }
748 }
749
750
751
752 /* simply send out max-length number of filler characters to fp */
753
754 static void
755 fill(int length, int max, char filler, FILE *fp)
756 {
757         int                     count;
758
759         count = max - length;
760         while (count-- >= 0)
761                 putc(filler, fp);
762 }