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