Age Owner Branch data TLA 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 "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
7492 neilc@samurai.com 69 :CBC 3780 : PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
70 : : {
71 : : int nFields;
72 : :
10085 bruce@momjian.us 73 : 3780 : nFields = PQnfields(res);
74 : :
75 [ + + ]: 3780 : if (nFields > 0)
76 : : { /* only print rows with at least 1 field. */
77 : : int i,
78 : : j;
79 : : int nTups;
80 : 3752 : int *fieldMax = NULL; /* in case we don't use them */
81 : 3752 : unsigned char *fieldNotNum = NULL;
82 : 3752 : char *border = NULL;
83 : 3752 : char **fields = NULL;
1632 tgl@sss.pgh.pa.us 84 : 3752 : const char **fieldNames = NULL;
10085 bruce@momjian.us 85 : 3752 : int fieldMaxLen = 0;
86 : : int numFieldName;
87 : 3752 : int fs_len = strlen(po->fieldSep);
88 : 3752 : int total_line_length = 0;
1632 tgl@sss.pgh.pa.us 89 : 3752 : bool usePipe = false;
90 : : char *pagerenv;
91 : :
92 : : #if !defined(WIN32)
93 : : sigset_t osigset;
7684 bruce@momjian.us 94 : 3752 : bool sigpipe_masked = false;
95 : : bool sigpipe_pending;
96 : : #endif
97 : :
98 : : #ifdef TIOCGWINSZ
99 : : 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 : : */
36 jchampion@postgresql 112 [ - + ]: 3752 : if (fs_len < strlen(po->fieldSep))
113 : : {
36 jchampion@postgresql 114 :UBC 0 : fprintf(stderr, libpq_gettext("overlong field separator\n"));
115 : 0 : goto exit;
116 : : }
117 : :
10085 bruce@momjian.us 118 :CBC 3752 : nTups = PQntuples(res);
1632 tgl@sss.pgh.pa.us 119 : 3752 : fieldNames = (const char **) calloc(nFields, sizeof(char *));
120 : 3752 : fieldNotNum = (unsigned char *) calloc(nFields, 1);
121 : 3752 : fieldMax = (int *) calloc(nFields, sizeof(int));
122 [ + - + - : 3752 : if (!fieldNames || !fieldNotNum || !fieldMax)
- + ]
123 : : {
7707 peter_e@gmx.net 124 :UBC 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
1632 tgl@sss.pgh.pa.us 125 : 0 : goto exit;
126 : : }
10085 bruce@momjian.us 127 :CBC 3752 : for (numFieldName = 0;
128 [ - + - - ]: 3752 : po->fieldName && po->fieldName[numFieldName];
10085 bruce@momjian.us 129 :UBC 0 : numFieldName++)
130 : : ;
10085 bruce@momjian.us 131 [ + + ]:CBC 9729 : for (j = 0; j < nFields; j++)
132 : : {
133 : : int len;
9379 134 [ - + - - ]: 5977 : const char *s = (j < numFieldName && po->fieldName[j][0]) ?
942 tgl@sss.pgh.pa.us 135 : 5977 : po->fieldName[j] : PQfname(res, j);
136 : :
10085 bruce@momjian.us 137 : 5977 : fieldNames[j] = s;
138 [ + - ]: 5977 : len = s ? strlen(s) : 0;
139 : 5977 : fieldMax[j] = len;
140 : 5977 : len += fs_len;
141 [ + + ]: 5977 : if (len > fieldMaxLen)
142 : 4984 : fieldMaxLen = len;
143 : 5977 : total_line_length += len;
144 : : }
145 : :
146 : 3752 : total_line_length += nFields * strlen(po->fieldSep) + 1;
147 : :
148 [ - + ]: 3752 : if (fout == NULL)
10085 bruce@momjian.us 149 :UBC 0 : fout = stdout;
6422 bruce@momjian.us 150 [ - + - - :CBC 3752 : if (po->pager && fout == stdout && isatty(fileno(stdin)) &&
- - - - ]
6422 bruce@momjian.us 151 :UBC 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
10085 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 : : }
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 &&
3296 tgl@sss.pgh.pa.us 179 [ # # ]: 0 : strspn(pagerenv, " \t\r\n") != strlen(pagerenv) &&
10085 bruce@momjian.us 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) *
9813 185 [ # # ]: 0 : (1 + (po->standard != 0)) >= screen_size.ws_row -
10085 186 : 0 : (po->header != 0) *
187 : 0 : (total_line_length / screen_size.ws_col + 1) * 2
3100 tgl@sss.pgh.pa.us 188 [ # # # # ]: 0 : - (po->header != 0) * 2 /* row count and newline */
189 : : )))
190 : : {
1205 191 : 0 : fflush(NULL);
10085 bruce@momjian.us 192 : 0 : fout = popen(pagerenv, "w");
193 [ # # ]: 0 : if (fout)
194 : : {
1632 tgl@sss.pgh.pa.us 195 : 0 : usePipe = true;
196 : : #ifndef WIN32
7684 197 [ # # ]: 0 : if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
198 : 0 : sigpipe_masked = true;
199 : : #endif /* WIN32 */
200 : : }
201 : : else
10085 bruce@momjian.us 202 : 0 : fout = stdout;
203 : : }
204 : : }
205 : :
10085 bruce@momjian.us 206 [ + - - + :CBC 3752 : if (!po->expanded && (po->align || po->html3))
- - ]
207 : : {
1632 tgl@sss.pgh.pa.us 208 : 3752 : fields = (char **) calloc((size_t) nTups + 1,
209 : : nFields * sizeof(char *));
210 [ - + ]: 3752 : if (!fields)
211 : : {
7707 peter_e@gmx.net 212 :UBC 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
1632 tgl@sss.pgh.pa.us 213 : 0 : goto exit;
214 : : }
215 : : }
10085 bruce@momjian.us 216 [ # # # # ]: 0 : else if (po->header && !po->html3)
217 : : {
218 [ # # ]: 0 : if (po->expanded)
219 : : {
220 [ # # ]: 0 : if (po->align)
7602 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 : : }
226 : : else
227 : : {
10085 228 : 0 : int len = 0;
229 : :
230 [ # # ]: 0 : for (j = 0; j < nFields; j++)
231 : : {
9379 232 : 0 : const char *s = fieldNames[j];
233 : :
10085 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 : : }
239 : 0 : fputc('\n', fout);
240 [ # # ]: 0 : for (len -= fs_len; len--; fputc('-', fout));
241 : 0 : fputc('\n', fout);
242 : : }
243 : : }
10085 bruce@momjian.us 244 [ - + - - ]:CBC 3752 : if (po->expanded && po->html3)
245 : : {
10085 bruce@momjian.us 246 [ # # ]:UBC 0 : if (po->caption)
7253 247 : 0 : fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
248 : : else
10085 249 : 0 : fprintf(fout,
250 : : "<center><h2>"
251 : : "Query retrieved %d rows * %d fields"
252 : : "</h2></center>\n",
253 : : nTups, nFields);
254 : : }
10085 bruce@momjian.us 255 [ + + ]:CBC 9214 : for (i = 0; i < nTups; i++)
256 : : {
257 [ - + ]: 5462 : if (po->expanded)
258 : : {
10085 bruce@momjian.us 259 [ # # ]:UBC 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
7602 264 : 0 : fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
265 : : }
10085 bruce@momjian.us 266 [ + + ]:CBC 15208 : for (j = 0; j < nFields; j++)
267 : : {
1632 tgl@sss.pgh.pa.us 268 [ - + ]: 9746 : if (!do_field(po, res, i, j, fs_len, fields, nFields,
269 : : fieldNames, fieldNotNum,
270 : : fieldMax, fieldMaxLen, fout))
1632 tgl@sss.pgh.pa.us 271 :UBC 0 : goto exit;
272 : : }
10085 bruce@momjian.us 273 [ - + - - ]:CBC 5462 : if (po->html3 && po->expanded)
10085 bruce@momjian.us 274 :UBC 0 : fputs("</table>\n", fout);
275 : : }
10085 bruce@momjian.us 276 [ + - - + :CBC 3752 : if (!po->expanded && (po->align || po->html3))
- - ]
277 : : {
278 [ - + ]: 3752 : if (po->html3)
279 : : {
10085 bruce@momjian.us 280 [ # # ]:UBC 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",
3100 tgl@sss.pgh.pa.us 292 [ # # ]: 0 : po->tableOpt ? po->tableOpt : "", nTups, nFields);
293 : : }
294 : : else
10085 bruce@momjian.us 295 [ # # ]: 0 : fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
296 : : }
10085 bruce@momjian.us 297 [ + - ]:CBC 3752 : if (po->header)
298 : 3752 : border = do_header(fout, po, nFields, fieldMax, fieldNames,
299 : : fieldNotNum, fs_len, res);
300 [ + + ]: 9214 : for (i = 0; i < nTups; i++)
301 : 5462 : output_row(fout, po, nFields, fields,
302 : : fieldNotNum, fieldMax, border, i);
303 : : }
304 [ + - + - ]: 3752 : if (po->header && !po->html3)
305 [ + + ]: 3752 : fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
1690 306 : 3752 : (PQntuples(res) == 1) ? "" : "s");
2566 tgl@sss.pgh.pa.us 307 [ + - - - ]: 3752 : if (po->html3 && !po->expanded)
2566 tgl@sss.pgh.pa.us 308 :UBC 0 : fputs("</table>\n", fout);
309 : :
1632 tgl@sss.pgh.pa.us 310 :CBC 3752 : exit:
1279 peter@eisentraut.org 311 : 3752 : free(fieldMax);
312 : 3752 : free(fieldNotNum);
313 : 3752 : free(border);
1632 tgl@sss.pgh.pa.us 314 [ + - ]: 3752 : if (fields)
315 : : {
316 : : /* if calloc succeeded, this shouldn't overflow size_t */
317 : 3752 : size_t numfields = ((size_t) nTups + 1) * (size_t) nFields;
318 : :
319 [ + + ]: 19475 : while (numfields-- > 0)
1279 peter@eisentraut.org 320 : 15723 : free(fields[numfields]);
1632 tgl@sss.pgh.pa.us 321 : 3752 : free(fields);
322 : : }
1279 peter@eisentraut.org 323 : 3752 : free(fieldNames);
10085 bruce@momjian.us 324 [ - + ]: 3752 : if (usePipe)
325 : : {
326 : : #ifdef WIN32
327 : : _pclose(fout);
328 : : #else
10085 bruce@momjian.us 329 :UBC 0 : pclose(fout);
330 : :
331 : : /* we can't easily verify if EPIPE occurred, so say it did */
7684 332 [ # # ]: 0 : if (sigpipe_masked)
tgl@sss.pgh.pa.us 333 : 0 : pq_reset_sigpipe(&osigset, sigpipe_pending, true);
334 : : #endif /* WIN32 */
335 : : }
336 : : }
10085 bruce@momjian.us 337 :CBC 3780 : }
338 : :
339 : :
340 : : static bool
9532 341 : 9746 : 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 : : const char *pval,
349 : : *p;
350 : : int plen;
351 : : bool skipit;
352 : :
10085 353 : 9746 : plen = PQgetlength(res, i, j);
354 : 9746 : pval = PQgetvalue(res, i, j);
355 : :
356 [ + + + - : 9746 : if (plen < 1 || !pval || !*pval)
- + ]
357 : : {
358 [ - + - - ]: 600 : if (po->align || po->expanded)
359 : 600 : skipit = true;
360 : : else
361 : : {
10085 bruce@momjian.us 362 :UBC 0 : skipit = false;
363 : 0 : goto efield;
364 : : }
365 : : }
366 : : else
10085 bruce@momjian.us 367 :CBC 9146 : skipit = false;
368 : :
369 [ + + ]: 9746 : if (!skipit)
370 : : {
9379 371 [ + - + + ]: 9146 : if (po->align && !fieldNotNum[j])
372 : : {
373 : : /* Detect whether field contains non-numeric data */
9604 tgl@sss.pgh.pa.us 374 : 7833 : char ch = '0';
375 : :
1653 376 [ + + ]: 23437 : for (p = pval; *p; p += PQmblenBounded(p, res->client_encoding))
377 : : {
9604 378 : 17431 : ch = *p;
9379 bruce@momjian.us 379 [ + + + + : 20574 : if (!((ch >= '0' && ch <= '9') ||
+ + + + ]
380 [ + + ]: 3146 : ch == '.' ||
381 [ + - ]: 3143 : ch == 'E' ||
382 [ + - ]: 3143 : ch == 'e' ||
383 : : ch == ' ' ||
384 : : ch == '-'))
385 : : {
9604 tgl@sss.pgh.pa.us 386 : 1827 : fieldNotNum[j] = 1;
387 : 1827 : break;
388 : : }
389 : : }
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 [ + + + - : 7833 : if (*pval == 'E' || *pval == 'e' ||
+ + ]
397 [ + + ]: 7803 : !(ch >= '0' && ch <= '9'))
10085 bruce@momjian.us 398 : 1827 : fieldNotNum[j] = 1;
399 : : }
400 : :
401 [ + - - + : 9146 : if (!po->expanded && (po->align || po->html3))
- - ]
402 : : {
9604 tgl@sss.pgh.pa.us 403 [ + + ]: 9146 : if (plen > fieldMax[j])
404 : 1337 : fieldMax[j] = plen;
36 jchampion@postgresql 405 [ - + ]: 9146 : if (!(fields[i * nFields + j] = (char *) malloc((size_t) plen + 1)))
406 : : {
7707 peter_e@gmx.net 407 :UBC 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
1632 tgl@sss.pgh.pa.us 408 : 0 : return false;
409 : : }
9604 tgl@sss.pgh.pa.us 410 :CBC 9146 : strcpy(fields[i * nFields + j], pval);
411 : : }
412 : : else
413 : : {
10085 bruce@momjian.us 414 [ # # ]:UBC 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 : : pval);
423 : : else
424 : : {
425 [ # # ]: 0 : if (po->align)
426 : 0 : fprintf(fout,
427 : : "%-*s%s %s\n",
9604 tgl@sss.pgh.pa.us 428 : 0 : fieldMaxLen - fs_len, fieldNames[j],
429 : 0 : po->fieldSep,
430 : : pval);
431 : : else
432 : 0 : fprintf(fout,
433 : : "%s%s%s\n",
434 : 0 : fieldNames[j], po->fieldSep, pval);
435 : : }
436 : : }
437 : : else
438 : : {
10085 bruce@momjian.us 439 [ # # ]: 0 : if (!po->html3)
440 : : {
9604 tgl@sss.pgh.pa.us 441 : 0 : fputs(pval, fout);
10085 bruce@momjian.us 442 : 0 : efield:
443 [ # # ]: 0 : if ((j + 1) < nFields)
444 : 0 : fputs(po->fieldSep, fout);
445 : : else
446 : 0 : fputc('\n', fout);
447 : : }
448 : : }
449 : : }
450 : : }
1632 tgl@sss.pgh.pa.us 451 :CBC 9746 : return true;
452 : : }
453 : :
454 : :
455 : : static char *
9532 bruce@momjian.us 456 : 3752 : 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 : : int j; /* for loop index */
10085 461 : 3752 : char *border = NULL;
462 : :
463 [ - + ]: 3752 : if (po->html3)
10085 bruce@momjian.us 464 :UBC 0 : fputs("<tr>", fout);
465 : : else
466 : : {
36 jchampion@postgresql 467 :CBC 3752 : size_t tot = 0;
10085 bruce@momjian.us 468 : 3752 : int n = 0;
469 : 3752 : char *p = NULL;
470 : :
471 : : /* Calculate the border size, checking for overflow. */
472 [ + + ]: 9729 : for (; n < nFields; n++)
473 : : {
474 : : /* Field plus separator, plus 2 extra '-' in standard format. */
22 jchampion@postgresql 475 [ + - + - ]:GNC 11954 : if (pg_add_size_overflow(tot, fieldMax[n], &tot) ||
476 : 5977 : pg_add_size_overflow(tot, fs_len, &tot) ||
477 [ - + - - ]: 5977 : (po->standard && pg_add_size_overflow(tot, 2, &tot)))
36 jchampion@postgresql 478 :UBC 0 : goto overflow;
479 : : }
10085 bruce@momjian.us 480 [ - + ]:CBC 3752 : if (po->standard)
481 : : {
482 : : /* An extra separator at the front and back. */
22 jchampion@postgresql 483 [ # # # # ]:UNC 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))
36 jchampion@postgresql 486 :UBC 0 : goto overflow;
487 : : }
22 jchampion@postgresql 488 [ - + ]:GNC 3752 : if (pg_add_size_overflow(tot, 1, &tot)) /* terminator */
36 jchampion@postgresql 489 :UBC 0 : goto overflow;
490 : :
36 jchampion@postgresql 491 :CBC 3752 : border = malloc(tot);
10085 bruce@momjian.us 492 [ - + ]: 3752 : if (!border)
493 : : {
7707 peter_e@gmx.net 494 :UBC 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
1632 tgl@sss.pgh.pa.us 495 : 0 : return NULL;
496 : : }
10085 bruce@momjian.us 497 :CBC 3752 : p = border;
498 [ - + ]: 3752 : if (po->standard)
499 : : {
9811 bruce@momjian.us 500 :UBC 0 : char *fs = po->fieldSep;
501 : :
10085 502 [ # # ]: 0 : while (*fs++)
503 : 0 : *p++ = '+';
504 : : }
10085 bruce@momjian.us 505 [ + + ]:CBC 9729 : for (j = 0; j < nFields; j++)
506 : : {
507 : : int len;
508 : :
509 [ - + + + ]: 61307 : for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
510 [ + - + + ]: 5977 : if (po->standard || (j + 1) < nFields)
511 : : {
9811 512 : 2225 : char *fs = po->fieldSep;
513 : :
10085 514 [ + + ]: 4450 : while (*fs++)
515 : 2225 : *p++ = '+';
516 : : }
517 : : }
518 : 3752 : *p = '\0';
519 [ - + ]: 3752 : if (po->standard)
10085 bruce@momjian.us 520 :UBC 0 : fprintf(fout, "%s\n", border);
521 : : }
10085 bruce@momjian.us 522 [ - + ]:CBC 3752 : if (po->standard)
10085 bruce@momjian.us 523 :UBC 0 : fputs(po->fieldSep, fout);
10085 bruce@momjian.us 524 [ + + ]:CBC 9729 : for (j = 0; j < nFields; j++)
525 : : {
9379 526 : 5977 : const char *s = PQfname(res, j);
527 : :
10085 528 [ - + ]: 5977 : if (po->html3)
529 : : {
7252 bruce@momjian.us 530 :UBC 0 : fprintf(fout, "<th align=\"%s\">%s</th>",
10085 531 [ # # ]: 0 : fieldNotNum[j] ? "left" : "right", fieldNames[j]);
532 : : }
533 : : else
534 : : {
10085 bruce@momjian.us 535 :CBC 5977 : int n = strlen(s);
536 : :
537 [ - + ]: 5977 : if (n > fieldMax[j])
10085 bruce@momjian.us 538 :UBC 0 : fieldMax[j] = n;
10085 bruce@momjian.us 539 [ - + ]:CBC 5977 : if (po->standard)
10085 bruce@momjian.us 540 :UBC 0 : fprintf(fout,
541 : 0 : fieldNotNum[j] ? " %-*s " : " %*s ",
542 [ # # ]: 0 : fieldMax[j], s);
543 : : else
10085 bruce@momjian.us 544 [ + + ]:CBC 5977 : fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
545 [ + - + + ]: 5977 : if (po->standard || (j + 1) < nFields)
546 : 2225 : fputs(po->fieldSep, fout);
547 : : }
548 : : }
549 [ - + ]: 3752 : if (po->html3)
10085 bruce@momjian.us 550 :UBC 0 : fputs("</tr>\n", fout);
551 : : else
10085 bruce@momjian.us 552 :CBC 3752 : fprintf(fout, "\n%s\n", border);
553 : 3752 : return border;
554 : :
36 jchampion@postgresql 555 :UBC 0 : overflow:
556 : 0 : fprintf(stderr, libpq_gettext("header size exceeds the maximum allowed\n"));
557 : 0 : return NULL;
558 : : }
559 : :
560 : :
561 : : static void
9532 bruce@momjian.us 562 :CBC 5462 : 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 : : int field_index; /* for loop index */
567 : :
10085 568 [ - + ]: 5462 : if (po->html3)
10085 bruce@momjian.us 569 :UBC 0 : fputs("<tr>", fout);
10085 bruce@momjian.us 570 [ - + ]:CBC 5462 : else if (po->standard)
10085 bruce@momjian.us 571 :UBC 0 : fputs(po->fieldSep, fout);
10085 bruce@momjian.us 572 [ + + ]:CBC 15208 : for (field_index = 0; field_index < nFields; field_index++)
573 : : {
574 : 9746 : char *p = fields[row_index * nFields + field_index];
575 : :
576 [ - + ]: 9746 : if (po->html3)
7252 bruce@momjian.us 577 [ # # ]:UBC 0 : fprintf(fout, "<td align=\"%s\">%s</td>",
7367 578 [ # # ]: 0 : fieldNotNum[field_index] ? "left" : "right", p ? p : "");
579 : : else
580 : : {
10085 bruce@momjian.us 581 [ + + ]:CBC 19492 : fprintf(fout,
582 : 9746 : fieldNotNum[field_index] ?
583 [ - + ]: 9746 : (po->standard ? " %-*s " : "%-*s") :
584 [ - + ]: 6598 : (po->standard ? " %*s " : "%*s"),
585 [ + + ]: 9746 : fieldMax[field_index],
586 : : p ? p : "");
587 [ + - + + ]: 9746 : if (po->standard || field_index + 1 < nFields)
588 : 4284 : fputs(po->fieldSep, fout);
589 : : }
590 : : }
591 [ - + ]: 5462 : if (po->html3)
10085 bruce@momjian.us 592 :UBC 0 : fputs("</tr>", fout);
10085 bruce@momjian.us 593 [ - + ]:CBC 5462 : else if (po->standard)
10085 bruce@momjian.us 594 :UBC 0 : fprintf(fout, "\n%s", border);
10085 bruce@momjian.us 595 :CBC 5462 : fputc('\n', fout);
596 : 5462 : }
597 : :
598 : :
599 : :
600 : : /*
601 : : * really old printing routines
602 : : */
603 : :
604 : : void
9453 peter_e@gmx.net 605 :UBC 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 : : int i,
616 : : j;
617 : : int nFields;
618 : : 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));
7492 neilc@samurai.com 636 [ # # ]: 0 : if (!fLength)
637 : : {
638 : 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
1632 tgl@sss.pgh.pa.us 639 : 0 : return;
640 : : }
641 : :
9453 peter_e@gmx.net 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 : : {
9379 bruce@momjian.us 647 : 0 : int flen = PQgetlength(res, i, j);
648 : :
9453 peter_e@gmx.net 649 [ # # ]: 0 : if (flen > fLength[j])
650 : 0 : fLength[j] = flen;
651 : : }
652 : : }
653 : : }
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 : : }
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 : : }
674 : 0 : fprintf(fp, "\n");
675 : : }
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 : : }
687 : 0 : fprintf(fp, "\n");
688 : : }
689 : :
690 [ # # ]: 0 : if (!quiet)
691 [ # # ]: 0 : fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
1690 bruce@momjian.us 692 : 0 : (PQntuples(res) == 1) ? "" : "s");
693 : :
9453 peter_e@gmx.net 694 : 0 : fflush(fp);
695 : :
1279 peter@eisentraut.org 696 : 0 : free(fLength);
697 : : }
698 : :
699 : :
700 : :
701 : : void
9453 peter_e@gmx.net 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 : : int nFields;
710 : : int nTups;
711 : : int i,
712 : : j;
713 : : 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 : : int width;
730 : :
731 : 0 : width = nFields * 14;
4713 tgl@sss.pgh.pa.us 732 : 0 : tborder = (char *) malloc(width + 1);
7492 neilc@samurai.com 733 [ # # ]: 0 : if (!tborder)
734 : : {
735 : 0 : fprintf(stderr, libpq_gettext("out of memory\n"));
1632 tgl@sss.pgh.pa.us 736 : 0 : return;
737 : : }
4713 738 [ # # ]: 0 : for (i = 0; i < width; i++)
9453 peter_e@gmx.net 739 : 0 : tborder[i] = '-';
4713 tgl@sss.pgh.pa.us 740 : 0 : tborder[width] = '\0';
9453 peter_e@gmx.net 741 : 0 : fprintf(fout, "%s\n", tborder);
742 : : }
743 : :
744 [ # # ]: 0 : for (i = 0; i < nFields; i++)
745 : : {
746 [ # # ]: 0 : if (PrintAttNames)
747 : : {
748 [ # # ]: 0 : fprintf(fout, formatString,
749 : : TerseOutput ? "" : "|",
750 : : PQfname(res, i));
751 : : }
752 : : }
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 : : }
761 : :
762 [ # # ]: 0 : for (i = 0; i < nTups; i++)
763 : : {
764 [ # # ]: 0 : for (j = 0; j < nFields; j++)
765 : : {
9379 bruce@momjian.us 766 : 0 : const char *pval = PQgetvalue(res, i, j);
767 : :
9453 peter_e@gmx.net 768 [ # # # # ]: 0 : fprintf(fout, formatString,
769 : : TerseOutput ? "" : "|",
770 : : pval ? pval : "");
771 : : }
772 [ # # ]: 0 : if (TerseOutput)
773 : 0 : fprintf(fout, "\n");
774 : : else
775 : 0 : fprintf(fout, "|\n%s\n", tborder);
776 : : }
777 : : }
778 : :
1279 peter@eisentraut.org 779 : 0 : free(tborder);
780 : : }
781 : :
782 : :
783 : : /* simply send out max-length number of filler characters to fp */
784 : :
785 : : static void
9444 peter_e@gmx.net 786 : 0 : fill(int length, int max, char filler, FILE *fp)
787 : : {
788 : : int count;
789 : :
9379 bruce@momjian.us 790 : 0 : count = max - length;
791 [ # # ]: 0 : while (count-- >= 0)
792 : 0 : putc(filler, fp);
9444 peter_e@gmx.net 793 : 0 : }
|