Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * fe-print.c
4 : * functions for pretty-printing query results
5 : *
6 : * Portions Copyright (c) 1996-2025, 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 : * src/interfaces/libpq/fe-print.c
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 :
39 :
40 : static bool do_field(const PQprintOpt *po, const PGresult *res,
41 : const int i, const int j, const int fs_len,
42 : char **fields,
43 : const int nFields, const char **fieldNames,
44 : unsigned char *fieldNotNum, int *fieldMax,
45 : const int fieldMaxLen, FILE *fout);
46 : static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields,
47 : int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum,
48 : const int fs_len, const PGresult *res);
49 : static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
50 : unsigned char *fieldNotNum, int *fieldMax, char *border,
51 : const int row_index);
52 : static void fill(int length, int max, char filler, FILE *fp);
53 :
54 : /*
55 : * PQprint()
56 : *
57 : * Format results of a query for printing.
58 : *
59 : * PQprintOpt is a typedef (structure) that contains
60 : * various flags and options. consult libpq-fe.h for
61 : * details
62 : *
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.
66 : */
67 : void
68 7666 : PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
69 : {
70 : int nFields;
71 :
72 7666 : nFields = PQnfields(res);
73 :
74 7666 : if (nFields > 0)
75 : { /* only print rows with at least 1 field. */
76 : int i,
77 : j;
78 : int nTups;
79 7556 : int *fieldMax = NULL; /* in case we don't use them */
80 7556 : unsigned char *fieldNotNum = NULL;
81 7556 : char *border = NULL;
82 7556 : char **fields = NULL;
83 7556 : const char **fieldNames = NULL;
84 7556 : int fieldMaxLen = 0;
85 : int numFieldName;
86 7556 : int fs_len = strlen(po->fieldSep);
87 7556 : int total_line_length = 0;
88 7556 : bool usePipe = false;
89 : char *pagerenv;
90 :
91 : #if !defined(WIN32)
92 : sigset_t osigset;
93 7556 : bool sigpipe_masked = false;
94 : bool sigpipe_pending;
95 : #endif
96 :
97 : #ifdef TIOCGWINSZ
98 : struct winsize screen_size;
99 : #else
100 : struct winsize
101 : {
102 : int ws_row;
103 : int ws_col;
104 : } screen_size;
105 : #endif
106 :
107 : /*
108 : * Quick sanity check on po->fieldSep, since we make heavy use of int
109 : * math throughout.
110 : */
111 7556 : if (fs_len < strlen(po->fieldSep))
112 : {
113 0 : fprintf(stderr, libpq_gettext("overlong field separator\n"));
114 0 : goto exit;
115 : }
116 :
117 7556 : nTups = PQntuples(res);
118 7556 : fieldNames = (const char **) calloc(nFields, sizeof(char *));
119 7556 : fieldNotNum = (unsigned char *) calloc(nFields, 1);
120 7556 : fieldMax = (int *) calloc(nFields, sizeof(int));
121 7556 : if (!fieldNames || !fieldNotNum || !fieldMax)
122 : {
123 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
124 0 : goto exit;
125 : }
126 7556 : for (numFieldName = 0;
127 7556 : po->fieldName && po->fieldName[numFieldName];
128 0 : numFieldName++)
129 : ;
130 19470 : for (j = 0; j < nFields; j++)
131 : {
132 : int len;
133 11914 : const char *s = (j < numFieldName && po->fieldName[j][0]) ?
134 11914 : po->fieldName[j] : PQfname(res, j);
135 :
136 11914 : fieldNames[j] = s;
137 11914 : len = s ? strlen(s) : 0;
138 11914 : fieldMax[j] = len;
139 11914 : len += fs_len;
140 11914 : if (len > fieldMaxLen)
141 10020 : fieldMaxLen = len;
142 11914 : total_line_length += len;
143 : }
144 :
145 7556 : total_line_length += nFields * strlen(po->fieldSep) + 1;
146 :
147 7556 : if (fout == NULL)
148 0 : fout = stdout;
149 7556 : if (po->pager && fout == stdout && isatty(fileno(stdin)) &&
150 0 : isatty(fileno(stdout)))
151 : {
152 : /*
153 : * If we think there'll be more than one screen of output, try to
154 : * pipe to the pager program.
155 : */
156 : #ifdef TIOCGWINSZ
157 0 : if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
158 0 : screen_size.ws_col == 0 ||
159 0 : screen_size.ws_row == 0)
160 : {
161 0 : screen_size.ws_row = 24;
162 0 : screen_size.ws_col = 80;
163 : }
164 : #else
165 : screen_size.ws_row = 24;
166 : screen_size.ws_col = 80;
167 : #endif
168 :
169 : /*
170 : * Since this function is no longer used by psql, we don't examine
171 : * PSQL_PAGER. It's possible that the hypothetical external users
172 : * of the function would like that to happen, but in the name of
173 : * backwards compatibility, we'll stick to just examining PAGER.
174 : */
175 0 : pagerenv = getenv("PAGER");
176 : /* if PAGER is unset, empty or all-white-space, don't use pager */
177 0 : if (pagerenv != NULL &&
178 0 : strspn(pagerenv, " \t\r\n") != strlen(pagerenv) &&
179 0 : !po->html3 &&
180 0 : ((po->expanded &&
181 0 : nTups * (nFields + 1) >= screen_size.ws_row) ||
182 0 : (!po->expanded &&
183 0 : nTups * (total_line_length / screen_size.ws_col + 1) *
184 0 : (1 + (po->standard != 0)) >= screen_size.ws_row -
185 0 : (po->header != 0) *
186 0 : (total_line_length / screen_size.ws_col + 1) * 2
187 0 : - (po->header != 0) * 2 /* row count and newline */
188 : )))
189 : {
190 0 : fflush(NULL);
191 0 : fout = popen(pagerenv, "w");
192 0 : if (fout)
193 : {
194 0 : usePipe = true;
195 : #ifndef WIN32
196 0 : if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
197 0 : sigpipe_masked = true;
198 : #endif /* WIN32 */
199 : }
200 : else
201 0 : fout = stdout;
202 : }
203 : }
204 :
205 7556 : if (!po->expanded && (po->align || po->html3))
206 : {
207 7556 : fields = (char **) calloc((size_t) nTups + 1,
208 : nFields * sizeof(char *));
209 7556 : if (!fields)
210 : {
211 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
212 0 : goto exit;
213 : }
214 : }
215 0 : else if (po->header && !po->html3)
216 : {
217 0 : if (po->expanded)
218 : {
219 0 : if (po->align)
220 0 : fprintf(fout, libpq_gettext("%-*s%s Value\n"),
221 0 : fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep);
222 : else
223 0 : fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep);
224 : }
225 : else
226 : {
227 0 : int len = 0;
228 :
229 0 : for (j = 0; j < nFields; j++)
230 : {
231 0 : const char *s = fieldNames[j];
232 :
233 0 : fputs(s, fout);
234 0 : len += strlen(s) + fs_len;
235 0 : if ((j + 1) < nFields)
236 0 : fputs(po->fieldSep, fout);
237 : }
238 0 : fputc('\n', fout);
239 0 : for (len -= fs_len; len--; fputc('-', fout));
240 0 : fputc('\n', fout);
241 : }
242 : }
243 7556 : if (po->expanded && po->html3)
244 : {
245 0 : if (po->caption)
246 0 : fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
247 : else
248 0 : fprintf(fout,
249 : "<center><h2>"
250 : "Query retrieved %d rows * %d fields"
251 : "</h2></center>\n",
252 : nTups, nFields);
253 : }
254 18306 : for (i = 0; i < nTups; i++)
255 : {
256 10750 : if (po->expanded)
257 : {
258 0 : if (po->html3)
259 0 : fprintf(fout,
260 : "<table %s><caption align=\"top\">%d</caption>\n",
261 0 : po->tableOpt ? po->tableOpt : "", i);
262 : else
263 0 : fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
264 : }
265 29626 : for (j = 0; j < nFields; j++)
266 : {
267 18876 : if (!do_field(po, res, i, j, fs_len, fields, nFields,
268 : fieldNames, fieldNotNum,
269 : fieldMax, fieldMaxLen, fout))
270 0 : goto exit;
271 : }
272 10750 : if (po->html3 && po->expanded)
273 0 : fputs("</table>\n", fout);
274 : }
275 7556 : if (!po->expanded && (po->align || po->html3))
276 : {
277 7556 : if (po->html3)
278 : {
279 0 : if (po->header)
280 : {
281 0 : if (po->caption)
282 0 : fprintf(fout,
283 : "<table %s><caption align=\"top\">%s</caption>\n",
284 0 : po->tableOpt ? po->tableOpt : "",
285 0 : po->caption);
286 : else
287 0 : fprintf(fout,
288 : "<table %s><caption align=\"top\">"
289 : "Retrieved %d rows * %d fields"
290 : "</caption>\n",
291 0 : po->tableOpt ? po->tableOpt : "", nTups, nFields);
292 : }
293 : else
294 0 : fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
295 : }
296 7556 : if (po->header)
297 7556 : border = do_header(fout, po, nFields, fieldMax, fieldNames,
298 : fieldNotNum, fs_len, res);
299 18306 : for (i = 0; i < nTups; i++)
300 10750 : output_row(fout, po, nFields, fields,
301 : fieldNotNum, fieldMax, border, i);
302 : }
303 7556 : if (po->header && !po->html3)
304 7556 : fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
305 7556 : (PQntuples(res) == 1) ? "" : "s");
306 7556 : if (po->html3 && !po->expanded)
307 0 : fputs("</table>\n", fout);
308 :
309 7556 : exit:
310 7556 : free(fieldMax);
311 7556 : free(fieldNotNum);
312 7556 : free(border);
313 7556 : if (fields)
314 : {
315 : /* if calloc succeeded, this shouldn't overflow size_t */
316 7556 : size_t numfields = ((size_t) nTups + 1) * (size_t) nFields;
317 :
318 38346 : while (numfields-- > 0)
319 30790 : free(fields[numfields]);
320 7556 : free(fields);
321 : }
322 7556 : free(fieldNames);
323 7556 : if (usePipe)
324 : {
325 : #ifdef WIN32
326 : _pclose(fout);
327 : #else
328 0 : pclose(fout);
329 :
330 : /* we can't easily verify if EPIPE occurred, so say it did */
331 0 : if (sigpipe_masked)
332 0 : pq_reset_sigpipe(&osigset, sigpipe_pending, true);
333 : #endif /* WIN32 */
334 : }
335 : }
336 7666 : }
337 :
338 :
339 : static bool
340 18876 : do_field(const PQprintOpt *po, const PGresult *res,
341 : const int i, const int j, const int fs_len,
342 : char **fields,
343 : const int nFields, char const **fieldNames,
344 : unsigned char *fieldNotNum, int *fieldMax,
345 : const int fieldMaxLen, FILE *fout)
346 : {
347 : const char *pval,
348 : *p;
349 : int plen;
350 : bool skipit;
351 :
352 18876 : plen = PQgetlength(res, i, j);
353 18876 : pval = PQgetvalue(res, i, j);
354 :
355 18876 : if (plen < 1 || !pval || !*pval)
356 : {
357 1292 : if (po->align || po->expanded)
358 1292 : skipit = true;
359 : else
360 : {
361 0 : skipit = false;
362 0 : goto efield;
363 : }
364 : }
365 : else
366 17584 : skipit = false;
367 :
368 18876 : if (!skipit)
369 : {
370 17584 : if (po->align && !fieldNotNum[j])
371 : {
372 : /* Detect whether field contains non-numeric data */
373 15356 : char ch = '0';
374 :
375 46198 : for (p = pval; *p; p += PQmblenBounded(p, res->client_encoding))
376 : {
377 34414 : ch = *p;
378 40618 : if (!((ch >= '0' && ch <= '9') ||
379 6210 : ch == '.' ||
380 6204 : ch == 'E' ||
381 6204 : ch == 'e' ||
382 : ch == ' ' ||
383 : ch == '-'))
384 : {
385 3572 : fieldNotNum[j] = 1;
386 3572 : break;
387 : }
388 : }
389 :
390 : /*
391 : * Above loop will believe E in first column is numeric; also, we
392 : * insist on a digit in the last column for a numeric. This test
393 : * is still not bulletproof but it handles most cases.
394 : */
395 15356 : if (*pval == 'E' || *pval == 'e' ||
396 15296 : !(ch >= '0' && ch <= '9'))
397 3572 : fieldNotNum[j] = 1;
398 : }
399 :
400 17584 : if (!po->expanded && (po->align || po->html3))
401 : {
402 17584 : if (plen > fieldMax[j])
403 2476 : fieldMax[j] = plen;
404 17584 : if (!(fields[i * nFields + j] = (char *) malloc((size_t) plen + 1)))
405 : {
406 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
407 0 : return false;
408 : }
409 17584 : strcpy(fields[i * nFields + j], pval);
410 : }
411 : else
412 : {
413 0 : if (po->expanded)
414 : {
415 0 : if (po->html3)
416 0 : fprintf(fout,
417 : "<tr><td align=\"left\"><b>%s</b></td>"
418 : "<td align=\"%s\">%s</td></tr>\n",
419 0 : fieldNames[j],
420 0 : fieldNotNum[j] ? "left" : "right",
421 : pval);
422 : else
423 : {
424 0 : if (po->align)
425 0 : fprintf(fout,
426 : "%-*s%s %s\n",
427 0 : fieldMaxLen - fs_len, fieldNames[j],
428 0 : po->fieldSep,
429 : pval);
430 : else
431 0 : fprintf(fout,
432 : "%s%s%s\n",
433 0 : fieldNames[j], po->fieldSep, pval);
434 : }
435 : }
436 : else
437 : {
438 0 : if (!po->html3)
439 : {
440 0 : fputs(pval, fout);
441 0 : efield:
442 0 : if ((j + 1) < nFields)
443 0 : fputs(po->fieldSep, fout);
444 : else
445 0 : fputc('\n', fout);
446 : }
447 : }
448 : }
449 : }
450 18876 : return true;
451 : }
452 :
453 :
454 : /*
455 : * Frontend version of the backend's add_size(), intended to be API-compatible
456 : * with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
457 : * Returns true instead if the addition overflows.
458 : *
459 : * TODO: move to common/int.h
460 : */
461 : static bool
462 31384 : add_size_overflow(size_t s1, size_t s2, size_t *dst)
463 : {
464 : size_t result;
465 :
466 31384 : result = s1 + s2;
467 31384 : if (result < s1 || result < s2)
468 0 : return true;
469 :
470 31384 : *dst = result;
471 31384 : return false;
472 : }
473 :
474 :
475 : static char *
476 7556 : do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
477 : const char **fieldNames, unsigned char *fieldNotNum,
478 : const int fs_len, const PGresult *res)
479 : {
480 : int j; /* for loop index */
481 7556 : char *border = NULL;
482 :
483 7556 : if (po->html3)
484 0 : fputs("<tr>", fout);
485 : else
486 : {
487 7556 : size_t tot = 0;
488 7556 : int n = 0;
489 7556 : char *p = NULL;
490 :
491 : /* Calculate the border size, checking for overflow. */
492 19470 : for (; n < nFields; n++)
493 : {
494 : /* Field plus separator, plus 2 extra '-' in standard format. */
495 23828 : if (add_size_overflow(tot, fieldMax[n], &tot) ||
496 11914 : add_size_overflow(tot, fs_len, &tot) ||
497 11914 : (po->standard && add_size_overflow(tot, 2, &tot)))
498 0 : goto overflow;
499 : }
500 7556 : if (po->standard)
501 : {
502 : /* An extra separator at the front and back. */
503 0 : if (add_size_overflow(tot, fs_len, &tot) ||
504 0 : add_size_overflow(tot, fs_len, &tot) ||
505 0 : add_size_overflow(tot, 2, &tot))
506 0 : goto overflow;
507 : }
508 7556 : if (add_size_overflow(tot, 1, &tot)) /* terminator */
509 0 : goto overflow;
510 :
511 7556 : border = malloc(tot);
512 7556 : if (!border)
513 : {
514 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
515 0 : return NULL;
516 : }
517 7556 : p = border;
518 7556 : if (po->standard)
519 : {
520 0 : char *fs = po->fieldSep;
521 :
522 0 : while (*fs++)
523 0 : *p++ = '+';
524 : }
525 19470 : for (j = 0; j < nFields; j++)
526 : {
527 : int len;
528 :
529 122774 : for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
530 11914 : if (po->standard || (j + 1) < nFields)
531 : {
532 4358 : char *fs = po->fieldSep;
533 :
534 8716 : while (*fs++)
535 4358 : *p++ = '+';
536 : }
537 : }
538 7556 : *p = '\0';
539 7556 : if (po->standard)
540 0 : fprintf(fout, "%s\n", border);
541 : }
542 7556 : if (po->standard)
543 0 : fputs(po->fieldSep, fout);
544 19470 : for (j = 0; j < nFields; j++)
545 : {
546 11914 : const char *s = PQfname(res, j);
547 :
548 11914 : if (po->html3)
549 : {
550 0 : fprintf(fout, "<th align=\"%s\">%s</th>",
551 0 : fieldNotNum[j] ? "left" : "right", fieldNames[j]);
552 : }
553 : else
554 : {
555 11914 : int n = strlen(s);
556 :
557 11914 : if (n > fieldMax[j])
558 0 : fieldMax[j] = n;
559 11914 : if (po->standard)
560 0 : fprintf(fout,
561 0 : fieldNotNum[j] ? " %-*s " : " %*s ",
562 0 : fieldMax[j], s);
563 : else
564 11914 : fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
565 11914 : if (po->standard || (j + 1) < nFields)
566 4358 : fputs(po->fieldSep, fout);
567 : }
568 : }
569 7556 : if (po->html3)
570 0 : fputs("</tr>\n", fout);
571 : else
572 7556 : fprintf(fout, "\n%s\n", border);
573 7556 : return border;
574 :
575 0 : overflow:
576 0 : fprintf(stderr, libpq_gettext("header size exceeds the maximum allowed\n"));
577 0 : return NULL;
578 : }
579 :
580 :
581 : static void
582 10750 : output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
583 : unsigned char *fieldNotNum, int *fieldMax, char *border,
584 : const int row_index)
585 : {
586 : int field_index; /* for loop index */
587 :
588 10750 : if (po->html3)
589 0 : fputs("<tr>", fout);
590 10750 : else if (po->standard)
591 0 : fputs(po->fieldSep, fout);
592 29626 : for (field_index = 0; field_index < nFields; field_index++)
593 : {
594 18876 : char *p = fields[row_index * nFields + field_index];
595 :
596 18876 : if (po->html3)
597 0 : fprintf(fout, "<td align=\"%s\">%s</td>",
598 0 : fieldNotNum[field_index] ? "left" : "right", p ? p : "");
599 : else
600 : {
601 37752 : fprintf(fout,
602 18876 : fieldNotNum[field_index] ?
603 18876 : (po->standard ? " %-*s " : "%-*s") :
604 13060 : (po->standard ? " %*s " : "%*s"),
605 18876 : fieldMax[field_index],
606 : p ? p : "");
607 18876 : if (po->standard || field_index + 1 < nFields)
608 8126 : fputs(po->fieldSep, fout);
609 : }
610 : }
611 10750 : if (po->html3)
612 0 : fputs("</tr>", fout);
613 10750 : else if (po->standard)
614 0 : fprintf(fout, "\n%s", border);
615 10750 : fputc('\n', fout);
616 10750 : }
617 :
618 :
619 :
620 : /*
621 : * really old printing routines
622 : */
623 :
624 : void
625 0 : PQdisplayTuples(const PGresult *res,
626 : FILE *fp, /* where to send the output */
627 : int fillAlign, /* pad the fields with spaces */
628 : const char *fieldSep, /* field separator */
629 : int printHeader, /* display headers? */
630 : int quiet
631 : )
632 : {
633 : #define DEFAULT_FIELD_SEP " "
634 :
635 : int i,
636 : j;
637 : int nFields;
638 : int nTuples;
639 0 : int *fLength = NULL;
640 :
641 0 : if (fieldSep == NULL)
642 0 : fieldSep = DEFAULT_FIELD_SEP;
643 :
644 : /* Get some useful info about the results */
645 0 : nFields = PQnfields(res);
646 0 : nTuples = PQntuples(res);
647 :
648 0 : if (fp == NULL)
649 0 : fp = stdout;
650 :
651 : /* Figure the field lengths to align to */
652 : /* will be somewhat time consuming for very large results */
653 0 : if (fillAlign)
654 : {
655 0 : fLength = (int *) malloc(nFields * sizeof(int));
656 0 : if (!fLength)
657 : {
658 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
659 0 : return;
660 : }
661 :
662 0 : for (j = 0; j < nFields; j++)
663 : {
664 0 : fLength[j] = strlen(PQfname(res, j));
665 0 : for (i = 0; i < nTuples; i++)
666 : {
667 0 : int flen = PQgetlength(res, i, j);
668 :
669 0 : if (flen > fLength[j])
670 0 : fLength[j] = flen;
671 : }
672 : }
673 : }
674 :
675 0 : if (printHeader)
676 : {
677 : /* first, print out the attribute names */
678 0 : for (i = 0; i < nFields; i++)
679 : {
680 0 : fputs(PQfname(res, i), fp);
681 0 : if (fillAlign)
682 0 : fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
683 0 : fputs(fieldSep, fp);
684 : }
685 0 : fprintf(fp, "\n");
686 :
687 : /* Underline the attribute names */
688 0 : for (i = 0; i < nFields; i++)
689 : {
690 0 : if (fillAlign)
691 0 : fill(0, fLength[i], '-', fp);
692 0 : fputs(fieldSep, fp);
693 : }
694 0 : fprintf(fp, "\n");
695 : }
696 :
697 : /* next, print out the instances */
698 0 : for (i = 0; i < nTuples; i++)
699 : {
700 0 : for (j = 0; j < nFields; j++)
701 : {
702 0 : fprintf(fp, "%s", PQgetvalue(res, i, j));
703 0 : if (fillAlign)
704 0 : fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
705 0 : fputs(fieldSep, fp);
706 : }
707 0 : fprintf(fp, "\n");
708 : }
709 :
710 0 : if (!quiet)
711 0 : fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
712 0 : (PQntuples(res) == 1) ? "" : "s");
713 :
714 0 : fflush(fp);
715 :
716 0 : free(fLength);
717 : }
718 :
719 :
720 :
721 : void
722 0 : PQprintTuples(const PGresult *res,
723 : FILE *fout, /* output stream */
724 : int PrintAttNames, /* print attribute names or not */
725 : int TerseOutput, /* delimiter bars or not? */
726 : int colWidth /* width of column, if 0, use variable width */
727 : )
728 : {
729 : int nFields;
730 : int nTups;
731 : int i,
732 : j;
733 : char formatString[80];
734 0 : char *tborder = NULL;
735 :
736 0 : nFields = PQnfields(res);
737 0 : nTups = PQntuples(res);
738 :
739 0 : if (colWidth > 0)
740 0 : sprintf(formatString, "%%s %%-%ds", colWidth);
741 : else
742 0 : sprintf(formatString, "%%s %%s");
743 :
744 0 : if (nFields > 0)
745 : { /* only print rows with at least 1 field. */
746 :
747 0 : if (!TerseOutput)
748 : {
749 : int width;
750 :
751 0 : width = nFields * 14;
752 0 : tborder = (char *) malloc(width + 1);
753 0 : if (!tborder)
754 : {
755 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
756 0 : return;
757 : }
758 0 : for (i = 0; i < width; i++)
759 0 : tborder[i] = '-';
760 0 : tborder[width] = '\0';
761 0 : fprintf(fout, "%s\n", tborder);
762 : }
763 :
764 0 : for (i = 0; i < nFields; i++)
765 : {
766 0 : if (PrintAttNames)
767 : {
768 0 : fprintf(fout, formatString,
769 : TerseOutput ? "" : "|",
770 : PQfname(res, i));
771 : }
772 : }
773 :
774 0 : if (PrintAttNames)
775 : {
776 0 : if (TerseOutput)
777 0 : fprintf(fout, "\n");
778 : else
779 0 : fprintf(fout, "|\n%s\n", tborder);
780 : }
781 :
782 0 : for (i = 0; i < nTups; i++)
783 : {
784 0 : for (j = 0; j < nFields; j++)
785 : {
786 0 : const char *pval = PQgetvalue(res, i, j);
787 :
788 0 : fprintf(fout, formatString,
789 : TerseOutput ? "" : "|",
790 : pval ? pval : "");
791 : }
792 0 : if (TerseOutput)
793 0 : fprintf(fout, "\n");
794 : else
795 0 : fprintf(fout, "|\n%s\n", tborder);
796 : }
797 : }
798 :
799 0 : free(tborder);
800 : }
801 :
802 :
803 : /* simply send out max-length number of filler characters to fp */
804 :
805 : static void
806 0 : fill(int length, int max, char filler, FILE *fp)
807 : {
808 : int count;
809 :
810 0 : count = max - length;
811 0 : while (count-- >= 0)
812 0 : putc(filler, fp);
813 0 : }
|