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