OSDN Git Service

Pgindent run for 8.0.
[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-2004, 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.54 2004/08/29 05:07:00 momjian 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
95 #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
96                 pqsigfunc       oldsigpipehandler = NULL;
97 #endif
98
99 #ifdef TIOCGWINSZ
100                 struct winsize screen_size;
101
102 #else
103                 struct winsize
104                 {
105                         int                     ws_row;
106                         int                     ws_col;
107                 }                       screen_size;
108 #endif
109
110                 nTups = PQntuples(res);
111                 if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *))))
112                 {
113                         perror("calloc");
114                         exit(1);
115                 }
116                 if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1)))
117                 {
118                         perror("calloc");
119                         exit(1);
120                 }
121                 if (!(fieldMax = (int *) calloc(nFields, sizeof(int))))
122                 {
123                         perror("calloc");
124                         exit(1);
125                 }
126                 for (numFieldName = 0;
127                          po->fieldName && po->fieldName[numFieldName];
128                          numFieldName++)
129                         ;
130                 for (j = 0; j < nFields; j++)
131                 {
132                         int                     len;
133                         const char *s = (j < numFieldName && po->fieldName[j][0]) ?
134                         po->fieldName[j] : PQfname(res, j);
135
136                         fieldNames[j] = s;
137                         len = s ? strlen(s) : 0;
138                         fieldMax[j] = len;
139                         len += fs_len;
140                         if (len > fieldMaxLen)
141                                 fieldMaxLen = len;
142                         total_line_length += len;
143                 }
144
145                 total_line_length += nFields * strlen(po->fieldSep) + 1;
146
147                 if (fout == NULL)
148                         fout = stdout;
149                 if (po->pager && fout == stdout
150 #ifndef WIN32
151                         &&
152                         isatty(fileno(stdin)) &&
153                         isatty(fileno(stdout))
154 #endif
155                         )
156                 {
157                         /*
158                          * If we think there'll be more than one screen of output, try
159                          * to pipe to the pager program.
160                          */
161 #ifdef TIOCGWINSZ
162                         if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
163                                 screen_size.ws_col == 0 ||
164                                 screen_size.ws_row == 0)
165                         {
166                                 screen_size.ws_row = 24;
167                                 screen_size.ws_col = 80;
168                         }
169 #else
170                         screen_size.ws_row = 24;
171                         screen_size.ws_col = 80;
172 #endif
173                         pagerenv = getenv("PAGER");
174                         if (pagerenv != NULL &&
175                                 pagerenv[0] != '\0' &&
176                                 !po->html3 &&
177                                 ((po->expanded &&
178                                   nTups * (nFields + 1) >= screen_size.ws_row) ||
179                                  (!po->expanded &&
180                                   nTups * (total_line_length / screen_size.ws_col + 1) *
181                                   (1 + (po->standard != 0)) >= screen_size.ws_row -
182                                   (po->header != 0) *
183                                   (total_line_length / screen_size.ws_col + 1) * 2
184                                   - (po->header != 0) * 2               /* row count and newline */
185                                   )))
186                         {
187                                 fout = popen(pagerenv, "w");
188                                 if (fout)
189                                 {
190                                         usePipe = 1;
191 #ifdef ENABLE_THREAD_SAFETY
192                                         pthread_setspecific(pq_thread_in_send, "t");
193 #else
194 #ifndef WIN32
195                                         oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
196 #endif
197 #endif
198                                 }
199                                 else
200                                         fout = stdout;
201                         }
202                 }
203
204                 if (!po->expanded && (po->align || po->html3))
205                 {
206                         if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
207                         {
208                                 perror("calloc");
209                                 exit(1);
210                         }
211                 }
212                 else if (po->header && !po->html3)
213                 {
214                         if (po->expanded)
215                         {
216                                 if (po->align)
217                                         fprintf(fout, "%-*s%s Value\n",
218                                                         fieldMaxLen - fs_len, "Field", po->fieldSep);
219                                 else
220                                         fprintf(fout, "%s%sValue\n", "Field", po->fieldSep);
221                         }
222                         else
223                         {
224                                 int                     len = 0;
225
226                                 for (j = 0; j < nFields; j++)
227                                 {
228                                         const char *s = fieldNames[j];
229
230                                         fputs(s, fout);
231                                         len += strlen(s) + fs_len;
232                                         if ((j + 1) < nFields)
233                                                 fputs(po->fieldSep, fout);
234                                 }
235                                 fputc('\n', fout);
236                                 for (len -= fs_len; len--; fputc('-', fout));
237                                 fputc('\n', fout);
238                         }
239                 }
240                 if (po->expanded && po->html3)
241                 {
242                         if (po->caption)
243                                 fprintf(fout, "<centre><h2>%s</h2></centre>\n", po->caption);
244                         else
245                                 fprintf(fout,
246                                                 "<centre><h2>"
247                                                 "Query retrieved %d rows * %d fields"
248                                                 "</h2></centre>\n",
249                                                 nTups, nFields);
250                 }
251                 for (i = 0; i < nTups; i++)
252                 {
253                         if (po->expanded)
254                         {
255                                 if (po->html3)
256                                         fprintf(fout,
257                                                   "<table %s><caption align=high>%d</caption>\n",
258                                                         po->tableOpt ? po->tableOpt : "", i);
259                                 else
260                                         fprintf(fout, "-- RECORD %d --\n", i);
261                         }
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);
268                 }
269                 if (!po->expanded && (po->align || po->html3))
270                 {
271                         if (po->html3)
272                         {
273                                 if (po->header)
274                                 {
275                                         if (po->caption)
276                                                 fprintf(fout,
277                                                   "<table %s><caption align=high>%s</caption>\n",
278                                                                 po->tableOpt ? po->tableOpt : "",
279                                                                 po->caption);
280                                         else
281                                                 fprintf(fout,
282                                                                 "<table %s><caption align=high>"
283                                                                 "Retrieved %d rows * %d fields"
284                                                                 "</caption>\n",
285                                                 po->tableOpt ? po->tableOpt : "", nTups, nFields);
286                                 }
287                                 else
288                                         fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
289                         }
290                         if (po->header)
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);
296                         free(fields);
297                         if (border)
298                                 free(border);
299                 }
300                 if (po->header && !po->html3)
301                         fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
302                                         (PQntuples(res) == 1) ? "" : "s");
303                 free(fieldMax);
304                 free(fieldNotNum);
305                 free((void *) fieldNames);
306                 if (usePipe)
307                 {
308 #ifdef WIN32
309                         _pclose(fout);
310 #else
311                         pclose(fout);
312 #endif
313 #ifdef ENABLE_THREAD_SAFETY
314                         pthread_setspecific(pq_thread_in_send, "f");
315 #else
316 #ifndef WIN32
317                         pqsignal(SIGPIPE, oldsigpipehandler);
318 #endif
319 #endif
320                 }
321                 if (po->html3 && !po->expanded)
322                         fputs("</table>\n", fout);
323         }
324 }
325
326
327 static void
328 do_field(const PQprintOpt *po, const PGresult *res,
329                  const int i, const int j, const int fs_len,
330                  char **fields,
331                  const int nFields, char const ** fieldNames,
332                  unsigned char *fieldNotNum, int *fieldMax,
333                  const int fieldMaxLen, FILE *fout)
334 {
335
336         const char *pval,
337                            *p;
338         int                     plen;
339         bool            skipit;
340
341         plen = PQgetlength(res, i, j);
342         pval = PQgetvalue(res, i, j);
343
344         if (plen < 1 || !pval || !*pval)
345         {
346                 if (po->align || po->expanded)
347                         skipit = true;
348                 else
349                 {
350                         skipit = false;
351                         goto efield;
352                 }
353         }
354         else
355                 skipit = false;
356
357         if (!skipit)
358         {
359                 if (po->align && !fieldNotNum[j])
360                 {
361                         /* Detect whether field contains non-numeric data */
362                         char            ch = '0';
363
364                         for (p = pval; *p; p += PQmblen(p, res->client_encoding))
365                         {
366                                 ch = *p;
367                                 if (!((ch >= '0' && ch <= '9') ||
368                                           ch == '.' ||
369                                           ch == 'E' ||
370                                           ch == 'e' ||
371                                           ch == ' ' ||
372                                           ch == '-'))
373                                 {
374                                         fieldNotNum[j] = 1;
375                                         break;
376                                 }
377                         }
378
379                         /*
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.
383                          */
384                         if (*pval == 'E' || *pval == 'e' ||
385                                 !(ch >= '0' && ch <= '9'))
386                                 fieldNotNum[j] = 1;
387                 }
388
389                 if (!po->expanded && (po->align || po->html3))
390                 {
391                         if (plen > fieldMax[j])
392                                 fieldMax[j] = plen;
393                         if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
394                         {
395                                 perror("malloc");
396                                 exit(1);
397                         }
398                         strcpy(fields[i * nFields + j], pval);
399                 }
400                 else
401                 {
402                         if (po->expanded)
403                         {
404                                 if (po->html3)
405                                         fprintf(fout,
406                                                         "<tr><td align=left><b>%s</b></td>"
407                                                         "<td align=%s>%s</td></tr>\n",
408                                                         fieldNames[j],
409                                                         fieldNotNum[j] ? "left" : "right",
410                                                         pval);
411                                 else
412                                 {
413                                         if (po->align)
414                                                 fprintf(fout,
415                                                                 "%-*s%s %s\n",
416                                                                 fieldMaxLen - fs_len, fieldNames[j],
417                                                                 po->fieldSep,
418                                                                 pval);
419                                         else
420                                                 fprintf(fout,
421                                                                 "%s%s%s\n",
422                                                                 fieldNames[j], po->fieldSep, pval);
423                                 }
424                         }
425                         else
426                         {
427                                 if (!po->html3)
428                                 {
429                                         fputs(pval, fout);
430                         efield:
431                                         if ((j + 1) < nFields)
432                                                 fputs(po->fieldSep, fout);
433                                         else
434                                                 fputc('\n', fout);
435                                 }
436                         }
437                 }
438         }
439 }
440
441
442 static char *
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)
446 {
447
448         int                     j;                              /* for loop index */
449         char       *border = NULL;
450
451         if (po->html3)
452                 fputs("<tr>", fout);
453         else
454         {
455                 int                     tot = 0;
456                 int                     n = 0;
457                 char       *p = NULL;
458
459                 for (; n < nFields; n++)
460                         tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
461                 if (po->standard)
462                         tot += fs_len * 2 + 2;
463                 border = malloc(tot + 1);
464                 if (!border)
465                 {
466                         perror("malloc");
467                         exit(1);
468                 }
469                 p = border;
470                 if (po->standard)
471                 {
472                         char       *fs = po->fieldSep;
473
474                         while (*fs++)
475                                 *p++ = '+';
476                 }
477                 for (j = 0; j < nFields; j++)
478                 {
479                         int                     len;
480
481                         for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
482                         if (po->standard || (j + 1) < nFields)
483                         {
484                                 char       *fs = po->fieldSep;
485
486                                 while (*fs++)
487                                         *p++ = '+';
488                         }
489                 }
490                 *p = '\0';
491                 if (po->standard)
492                         fprintf(fout, "%s\n", border);
493         }
494         if (po->standard)
495                 fputs(po->fieldSep, fout);
496         for (j = 0; j < nFields; j++)
497         {
498                 const char *s = PQfname(res, j);
499
500                 if (po->html3)
501                 {
502                         fprintf(fout, "<th align=%s>%s</th>",
503                                         fieldNotNum[j] ? "left" : "right", fieldNames[j]);
504                 }
505                 else
506                 {
507                         int                     n = strlen(s);
508
509                         if (n > fieldMax[j])
510                                 fieldMax[j] = n;
511                         if (po->standard)
512                                 fprintf(fout,
513                                                 fieldNotNum[j] ? " %-*s " : " %*s ",
514                                                 fieldMax[j], s);
515                         else
516                                 fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
517                         if (po->standard || (j + 1) < nFields)
518                                 fputs(po->fieldSep, fout);
519                 }
520         }
521         if (po->html3)
522                 fputs("</tr>\n", fout);
523         else
524                 fprintf(fout, "\n%s\n", border);
525         return border;
526 }
527
528
529 static void
530 output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
531                    unsigned char *fieldNotNum, int *fieldMax, char *border,
532                    const int row_index)
533 {
534
535         int                     field_index;    /* for loop index */
536
537         if (po->html3)
538                 fputs("<tr>", fout);
539         else if (po->standard)
540                 fputs(po->fieldSep, fout);
541         for (field_index = 0; field_index < nFields; field_index++)
542         {
543                 char       *p = fields[row_index * nFields + field_index];
544
545                 if (po->html3)
546                         fprintf(fout, "<td align=%s>%s</td>",
547                                 fieldNotNum[field_index] ? "left" : "right", p ? p : "");
548                 else
549                 {
550                         fprintf(fout,
551                                         fieldNotNum[field_index] ?
552                                         (po->standard ? " %-*s " : "%-*s") :
553                                         (po->standard ? " %*s " : "%*s"),
554                                         fieldMax[field_index],
555                                         p ? p : "");
556                         if (po->standard || field_index + 1 < nFields)
557                                 fputs(po->fieldSep, fout);
558                 }
559                 if (p)
560                         free(p);
561         }
562         if (po->html3)
563                 fputs("</tr>", fout);
564         else if (po->standard)
565                 fprintf(fout, "\n%s", border);
566         fputc('\n', fout);
567 }
568
569
570
571 /*
572  * really old printing routines
573  */
574
575 void
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? */
581                                 int quiet
582 )
583 {
584 #define DEFAULT_FIELD_SEP " "
585
586         int                     i,
587                                 j;
588         int                     nFields;
589         int                     nTuples;
590         int                *fLength = NULL;
591
592         if (fieldSep == NULL)
593                 fieldSep = DEFAULT_FIELD_SEP;
594
595         /* Get some useful info about the results */
596         nFields = PQnfields(res);
597         nTuples = PQntuples(res);
598
599         if (fp == NULL)
600                 fp = stdout;
601
602         /* Figure the field lengths to align to */
603         /* will be somewhat time consuming for very large results */
604         if (fillAlign)
605         {
606                 fLength = (int *) malloc(nFields * sizeof(int));
607                 for (j = 0; j < nFields; j++)
608                 {
609                         fLength[j] = strlen(PQfname(res, j));
610                         for (i = 0; i < nTuples; i++)
611                         {
612                                 int                     flen = PQgetlength(res, i, j);
613
614                                 if (flen > fLength[j])
615                                         fLength[j] = flen;
616                         }
617                 }
618         }
619
620         if (printHeader)
621         {
622                 /* first, print out the attribute names */
623                 for (i = 0; i < nFields; i++)
624                 {
625                         fputs(PQfname(res, i), fp);
626                         if (fillAlign)
627                                 fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
628                         fputs(fieldSep, fp);
629                 }
630                 fprintf(fp, "\n");
631
632                 /* Underline the attribute names */
633                 for (i = 0; i < nFields; i++)
634                 {
635                         if (fillAlign)
636                                 fill(0, fLength[i], '-', fp);
637                         fputs(fieldSep, fp);
638                 }
639                 fprintf(fp, "\n");
640         }
641
642         /* next, print out the instances */
643         for (i = 0; i < nTuples; i++)
644         {
645                 for (j = 0; j < nFields; j++)
646                 {
647                         fprintf(fp, "%s", PQgetvalue(res, i, j));
648                         if (fillAlign)
649                                 fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
650                         fputs(fieldSep, fp);
651                 }
652                 fprintf(fp, "\n");
653         }
654
655         if (!quiet)
656                 fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
657                                 (PQntuples(res) == 1) ? "" : "s");
658
659         fflush(fp);
660
661         if (fLength)
662                 free(fLength);
663 }
664
665
666
667 void
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
673                                                                  * width */
674 )
675 {
676         int                     nFields;
677         int                     nTups;
678         int                     i,
679                                 j;
680         char            formatString[80];
681
682         char       *tborder = NULL;
683
684         nFields = PQnfields(res);
685         nTups = PQntuples(res);
686
687         if (colWidth > 0)
688                 sprintf(formatString, "%%s %%-%ds", colWidth);
689         else
690                 sprintf(formatString, "%%s %%s");
691
692         if (nFields > 0)
693         {                                                       /* only print rows with at least 1 field.  */
694
695                 if (!TerseOutput)
696                 {
697                         int                     width;
698
699                         width = nFields * 14;
700                         tborder = malloc(width + 1);
701                         for (i = 0; i <= width; i++)
702                                 tborder[i] = '-';
703                         tborder[i] = '\0';
704                         fprintf(fout, "%s\n", tborder);
705                 }
706
707                 for (i = 0; i < nFields; i++)
708                 {
709                         if (PrintAttNames)
710                         {
711                                 fprintf(fout, formatString,
712                                                 TerseOutput ? "" : "|",
713                                                 PQfname(res, i));
714                         }
715                 }
716
717                 if (PrintAttNames)
718                 {
719                         if (TerseOutput)
720                                 fprintf(fout, "\n");
721                         else
722                                 fprintf(fout, "|\n%s\n", tborder);
723                 }
724
725                 for (i = 0; i < nTups; i++)
726                 {
727                         for (j = 0; j < nFields; j++)
728                         {
729                                 const char *pval = PQgetvalue(res, i, j);
730
731                                 fprintf(fout, formatString,
732                                                 TerseOutput ? "" : "|",
733                                                 pval ? pval : "");
734                         }
735                         if (TerseOutput)
736                                 fprintf(fout, "\n");
737                         else
738                                 fprintf(fout, "|\n%s\n", tborder);
739                 }
740         }
741 }
742
743
744
745 /* simply send out max-length number of filler characters to fp */
746
747 static void
748 fill(int length, int max, char filler, FILE *fp)
749 {
750         int                     count;
751
752         count = max - length;
753         while (count-- >= 0)
754                 putc(filler, fp);
755 }