644 lines
21 KiB
C++
644 lines
21 KiB
C++
/* -------------------------------------------------------------------------
|
|
*
|
|
* fe-print.cpp
|
|
* functions for pretty-printing query results
|
|
*
|
|
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* These functions were formerly part of fe-exec.c, but they
|
|
* didn't really belong there.
|
|
*
|
|
* IDENTIFICATION
|
|
* src/common/interfaces/libpq/fe-print.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres_fe.h"
|
|
|
|
#include <signal.h>
|
|
|
|
#ifdef WIN32
|
|
#include "win32.h"
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_TERMIOS_H
|
|
#include <termios.h>
|
|
#else
|
|
#ifndef WIN32
|
|
#include <sys/termios.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "libpq/libpq-fe.h"
|
|
#include "libpq/libpq-int.h"
|
|
#include "fe-auth.h"
|
|
#include "pqsignal.h"
|
|
|
|
static void do_field(const PQprintOpt* po, const PGresult* res, const int i, const int j, const int fs_len,
|
|
char** fields, const int nFields, const char** fieldNames, unsigned char* fieldNotNum, int* fieldMax,
|
|
const int fieldMaxLen, FILE* fout);
|
|
static char* do_header(FILE* fout, const PQprintOpt* po, const int nFields, int* fieldMax, const char** fieldNames,
|
|
unsigned char* fieldNotNum, const int fs_len, const PGresult* res);
|
|
static void output_row(FILE* fout, const PQprintOpt* po, const int nFields, char** fields, unsigned char* fieldNotNum,
|
|
const int* fieldMax, char* border, const int row_index);
|
|
static void fill(int length, int max, char filler, FILE* fp);
|
|
|
|
/*
|
|
* PQprint()
|
|
*
|
|
* Format results of a query for printing.
|
|
*
|
|
* PQprintOpt is a typedef (structure) that contains
|
|
* various flags and options. consult libpq-fe.h for
|
|
* details
|
|
*
|
|
* This function should probably be removed sometime since psql
|
|
* doesn't use it anymore. It is unclear to what extent this is used
|
|
* by external clients, however.
|
|
*/
|
|
void PQprint(FILE* fout, const PGresult* res, const PQprintOpt* po)
|
|
{
|
|
int nFields;
|
|
|
|
nFields = PQnfields(res);
|
|
|
|
if (nFields > 0) { /* only print rows with at least 1 field. */
|
|
int i, j;
|
|
int nTups;
|
|
int* fieldMax = NULL; /* in case we don't use them */
|
|
unsigned char* fieldNotNum = NULL;
|
|
char* border = NULL;
|
|
char** fields = NULL;
|
|
const char** fieldNames = NULL;
|
|
int fieldMaxLen = 0;
|
|
int numFieldName;
|
|
int fs_len = strlen(po->fieldSep);
|
|
int total_line_length = 0;
|
|
int usePipe = 0;
|
|
char* pagerenv = NULL;
|
|
|
|
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
|
|
sigset_t osigset;
|
|
bool sigpipe_masked = false;
|
|
bool sigpipe_pending = false;
|
|
#endif
|
|
#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
|
|
pqsigfunc oldsigpipehandler = NULL;
|
|
#endif
|
|
|
|
#ifdef TIOCGWINSZ
|
|
struct winsize screen_size;
|
|
#else
|
|
struct winsize {
|
|
int ws_row;
|
|
int ws_col;
|
|
} screen_size;
|
|
#endif
|
|
|
|
nTups = PQntuples(res);
|
|
if ((fieldNames = (const char**)calloc(nFields, sizeof(char*))) == NULL) {
|
|
fprintf(stderr, libpq_gettext("out of memory\n"));
|
|
abort();
|
|
}
|
|
if ((fieldNotNum = (unsigned char*)calloc(nFields, 1)) == NULL) {
|
|
fprintf(stderr, libpq_gettext("out of memory\n"));
|
|
abort();
|
|
}
|
|
if ((fieldMax = (int*)calloc(nFields, sizeof(int))) == NULL) {
|
|
fprintf(stderr, libpq_gettext("out of memory\n"));
|
|
abort();
|
|
}
|
|
for (numFieldName = 0; (po->fieldName != NULL) && (po->fieldName[numFieldName] != NULL); numFieldName++)
|
|
;
|
|
for (j = 0; j < nFields; j++) {
|
|
int len;
|
|
const char* s = (j < numFieldName && po->fieldName[j][0]) ? po->fieldName[j] : PQfname(res, j);
|
|
|
|
fieldNames[j] = s;
|
|
len = s != NULL ? strlen(s) : 0;
|
|
fieldMax[j] = len;
|
|
len += fs_len;
|
|
if (len > fieldMaxLen)
|
|
fieldMaxLen = len;
|
|
total_line_length += len;
|
|
}
|
|
|
|
total_line_length += nFields * strlen(po->fieldSep) + 1;
|
|
|
|
if (fout == NULL)
|
|
fout = stdout;
|
|
if (po->pager && fout == stdout && isatty(fileno(stdin)) && isatty(fileno(stdout))) {
|
|
/*
|
|
* If we think there'll be more than one screen of output, try to
|
|
* pipe to the pager program.
|
|
*/
|
|
#ifdef TIOCGWINSZ
|
|
if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 || screen_size.ws_col == 0 ||
|
|
screen_size.ws_row == 0) {
|
|
screen_size.ws_row = 24;
|
|
screen_size.ws_col = 80;
|
|
}
|
|
#else
|
|
screen_size.ws_row = 24;
|
|
screen_size.ws_col = 80;
|
|
#endif
|
|
char* tmp = gs_getenv_r("PAGER");
|
|
if (check_client_env(tmp) == NULL)
|
|
pagerenv = NULL;
|
|
else
|
|
pagerenv = strdup(tmp);
|
|
|
|
if (pagerenv != NULL && pagerenv[0] != '\0' && !po->html3 &&
|
|
((po->expanded && nTups * (nFields + 1) >= screen_size.ws_row) ||
|
|
(!po->expanded &&
|
|
nTups * (total_line_length / screen_size.ws_col + 1) * (1 + (po->standard != 0)) >=
|
|
screen_size.ws_row - (po->header != 0) * (total_line_length / screen_size.ws_col + 1) * 2 -
|
|
(po->header != 0) * 2 /* row count and newline */
|
|
))) {
|
|
if (check_client_env(pagerenv) == NULL) {
|
|
fprintf(stderr, libpq_gettext("check parameter failed.\n"));
|
|
abort();
|
|
}
|
|
|
|
fout = popen(pagerenv, "w");
|
|
if (fout != NULL) {
|
|
usePipe = 1;
|
|
#ifndef WIN32
|
|
#ifdef ENABLE_THREAD_SAFETY
|
|
if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
|
|
sigpipe_masked = true;
|
|
#else
|
|
oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
|
|
#endif /* ENABLE_THREAD_SAFETY */
|
|
#endif /* WIN32 */
|
|
} else
|
|
fout = stdout;
|
|
}
|
|
}
|
|
|
|
if (!po->expanded && (po->align || po->html3)) {
|
|
if ((fields = (char**)calloc(nFields * (nTups + 1), sizeof(char*))) == NULL) {
|
|
fprintf(stderr, libpq_gettext("out of memory\n"));
|
|
abort();
|
|
}
|
|
} else if (po->header && !po->html3) {
|
|
if (po->expanded) {
|
|
if (po->align)
|
|
fprintf(fout,
|
|
libpq_gettext("%-*s%s Value\n"),
|
|
fieldMaxLen - fs_len,
|
|
libpq_gettext("Field"),
|
|
po->fieldSep);
|
|
else
|
|
fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep);
|
|
} else {
|
|
int len = 0;
|
|
|
|
for (j = 0; j < nFields; j++) {
|
|
const char* s = fieldNames[j];
|
|
|
|
fputs(s, fout);
|
|
len += strlen(s) + fs_len;
|
|
if ((j + 1) < nFields)
|
|
fputs(po->fieldSep, fout);
|
|
}
|
|
fputc('\n', fout);
|
|
for (len -= fs_len; len--; fputc('-', fout))
|
|
;
|
|
fputc('\n', fout);
|
|
}
|
|
}
|
|
if (po->expanded && po->html3) {
|
|
if (po->caption != NULL)
|
|
fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
|
|
else
|
|
fprintf(fout,
|
|
"<center><h2>"
|
|
"Query retrieved %d rows * %d fields"
|
|
"</h2></center>\n",
|
|
nTups,
|
|
nFields);
|
|
}
|
|
for (i = 0; i < nTups; i++) {
|
|
if (po->expanded) {
|
|
if (po->html3)
|
|
fprintf(fout,
|
|
"<table %s><caption align=\"top\">%d</caption>\n",
|
|
po->tableOpt != NULL ? po->tableOpt : "",
|
|
i);
|
|
else
|
|
fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
|
|
}
|
|
for (j = 0; j < nFields; j++)
|
|
do_field(po, res, i, j, fs_len, fields, nFields, fieldNames, fieldNotNum, fieldMax, fieldMaxLen, fout);
|
|
if (po->html3 && po->expanded)
|
|
fputs("</table>\n", fout);
|
|
}
|
|
if (!po->expanded && (po->align || po->html3)) {
|
|
if (po->html3) {
|
|
if (po->header) {
|
|
if (po->caption != NULL)
|
|
fprintf(fout,
|
|
"<table %s><caption align=\"top\">%s</caption>\n",
|
|
po->tableOpt != NULL ? po->tableOpt : "",
|
|
po->caption);
|
|
else
|
|
fprintf(fout,
|
|
"<table %s><caption align=\"top\">"
|
|
"Retrieved %d rows * %d fields"
|
|
"</caption>\n",
|
|
po->tableOpt != NULL ? po->tableOpt : "",
|
|
nTups,
|
|
nFields);
|
|
} else
|
|
fprintf(fout, "<table %s>", po->tableOpt != NULL ? po->tableOpt : "");
|
|
}
|
|
if (po->header)
|
|
border = do_header(fout, po, nFields, fieldMax, fieldNames, fieldNotNum, fs_len, res);
|
|
for (i = 0; i < nTups; i++)
|
|
output_row(fout, po, nFields, fields, fieldNotNum, fieldMax, border, i);
|
|
libpq_free(fields);
|
|
libpq_free(border);
|
|
}
|
|
if (po->header && !po->html3)
|
|
fprintf(fout, "(%d row%s)\n\n", PQntuples(res), (PQntuples(res) == 1) ? "" : "s");
|
|
libpq_free(fieldMax);
|
|
libpq_free(fieldNotNum);
|
|
free((void*)fieldNames);
|
|
fieldNames = NULL;
|
|
if (usePipe) {
|
|
#ifdef WIN32
|
|
_pclose(fout);
|
|
#else
|
|
pclose(fout);
|
|
|
|
#ifdef ENABLE_THREAD_SAFETY
|
|
/* we can't easily verify if EPIPE occurred, so say it did */
|
|
if (sigpipe_masked)
|
|
pq_reset_sigpipe(&osigset, sigpipe_pending, true);
|
|
#else
|
|
pqsignal(SIGPIPE, oldsigpipehandler);
|
|
#endif /* ENABLE_THREAD_SAFETY */
|
|
#endif /* WIN32 */
|
|
}
|
|
if (po->html3 && !po->expanded)
|
|
fputs("</table>\n", fout);
|
|
libpq_free(pagerenv);
|
|
}
|
|
}
|
|
|
|
static void do_field(const PQprintOpt* po, const PGresult* res, const int i, const int j, const int fs_len,
|
|
char** fields, const int nFields, char const** fieldNames, unsigned char* fieldNotNum, int* fieldMax,
|
|
const int fieldMaxLen, FILE* fout)
|
|
{
|
|
const char* pval = NULL;
|
|
const char* p = NULL;
|
|
|
|
int plen;
|
|
bool skipit = false;
|
|
|
|
plen = PQgetlength(res, i, j);
|
|
pval = PQgetvalue(res, i, j);
|
|
|
|
if (plen < 1 || (pval == NULL) || !*pval) {
|
|
if (po->align || po->expanded)
|
|
skipit = true;
|
|
else {
|
|
goto efield;
|
|
}
|
|
} else
|
|
skipit = false;
|
|
|
|
if (!skipit) {
|
|
if (po->align && !fieldNotNum[j]) {
|
|
/* Detect whether field contains non-numeric data */
|
|
char ch = '0';
|
|
|
|
for (p = pval; *p; p += PQmblen(p, res->client_encoding)) {
|
|
ch = *p;
|
|
if (!((ch >= '0' && ch <= '9') || ch == '.' || ch == 'E' || ch == 'e' || ch == ' ' || ch == '-')) {
|
|
fieldNotNum[j] = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Above loop will believe E in first column is numeric; also, we
|
|
* insist on a digit in the last column for a numeric. This test
|
|
* is still not bulletproof but it handles most cases.
|
|
*/
|
|
if (*pval == 'E' || *pval == 'e' || !(ch >= '0' && ch <= '9'))
|
|
fieldNotNum[j] = 1;
|
|
}
|
|
|
|
if (!po->expanded && (po->align || po->html3)) {
|
|
if (plen > fieldMax[j])
|
|
fieldMax[j] = plen;
|
|
if ((fields[i * nFields + j] = (char*)malloc(plen + 1)) == NULL) {
|
|
fprintf(stderr, libpq_gettext("out of memory\n"));
|
|
abort();
|
|
}
|
|
check_strcpy_s(strcpy_s(fields[i * nFields + j], plen + 1, pval));
|
|
} else {
|
|
if (po->expanded) {
|
|
if (po->html3)
|
|
fprintf(fout,
|
|
"<tr><td align=\"left\"><b>%s</b></td>"
|
|
"<td align=\"%s\">%s</td></tr>\n",
|
|
fieldNames[j],
|
|
fieldNotNum[j] ? "left" : "right",
|
|
pval);
|
|
else {
|
|
if (po->align)
|
|
fprintf(fout, "%-*s%s %s\n", fieldMaxLen - fs_len, fieldNames[j], po->fieldSep, pval);
|
|
else
|
|
fprintf(fout, "%s%s%s\n", fieldNames[j], po->fieldSep, pval);
|
|
}
|
|
} else {
|
|
if (!po->html3) {
|
|
fputs(pval, fout);
|
|
efield:
|
|
if ((j + 1) < nFields)
|
|
fputs(po->fieldSep, fout);
|
|
else
|
|
fputc('\n', fout);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static char* do_header(FILE* fout, const PQprintOpt* po, const int nFields, int* fieldMax, const char** fieldNames,
|
|
unsigned char* fieldNotNum, const int fs_len, const PGresult* res)
|
|
{
|
|
|
|
int j; /* for loop index */
|
|
char* border = NULL;
|
|
|
|
if (po->html3)
|
|
fputs("<tr>", fout);
|
|
else {
|
|
int tot = 0;
|
|
int n = 0;
|
|
char* p = NULL;
|
|
|
|
for (; n < nFields; n++)
|
|
tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
|
|
if (po->standard)
|
|
tot += fs_len * 2 + 2;
|
|
border = (char*)malloc(tot + 1);
|
|
if (border == NULL) {
|
|
fprintf(stderr, libpq_gettext("out of memory\n"));
|
|
abort();
|
|
}
|
|
p = border;
|
|
if (po->standard) {
|
|
char* fs = po->fieldSep;
|
|
|
|
while (*fs++)
|
|
*p++ = '+';
|
|
}
|
|
for (j = 0; j < nFields; j++) {
|
|
int len;
|
|
|
|
for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-')
|
|
;
|
|
if (po->standard || (j + 1) < nFields) {
|
|
char* fs = po->fieldSep;
|
|
|
|
while (*fs++)
|
|
*p++ = '+';
|
|
}
|
|
}
|
|
*p = '\0';
|
|
if (po->standard)
|
|
fprintf(fout, "%s\n", border);
|
|
}
|
|
if (po->standard)
|
|
fputs(po->fieldSep, fout);
|
|
for (j = 0; j < nFields; j++) {
|
|
const char* s = PQfname(res, j);
|
|
|
|
if (po->html3) {
|
|
fprintf(fout, "<th align=\"%s\">%s</th>", fieldNotNum[j] ? "left" : "right", fieldNames[j]);
|
|
} else {
|
|
int n = strlen(s);
|
|
|
|
if (n > fieldMax[j])
|
|
fieldMax[j] = n;
|
|
if (po->standard)
|
|
fprintf(fout, fieldNotNum[j] ? " %-*s " : " %*s ", fieldMax[j], s);
|
|
else
|
|
fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
|
|
if (po->standard || (j + 1) < nFields)
|
|
fputs(po->fieldSep, fout);
|
|
}
|
|
}
|
|
if (po->html3)
|
|
fputs("</tr>\n", fout);
|
|
else
|
|
fprintf(fout, "\n%s\n", border);
|
|
return border;
|
|
}
|
|
|
|
static void output_row(FILE* fout, const PQprintOpt* po, const int nFields, char** fields, unsigned char* fieldNotNum,
|
|
const int* fieldMax, char* border, const int row_index)
|
|
{
|
|
|
|
int field_index; /* for loop index */
|
|
|
|
if (po->html3)
|
|
fputs("<tr>", fout);
|
|
else if (po->standard)
|
|
fputs(po->fieldSep, fout);
|
|
for (field_index = 0; field_index < nFields; field_index++) {
|
|
char* p = fields[row_index * nFields + field_index];
|
|
|
|
if (po->html3)
|
|
fprintf(fout, "<td align=\"%s\">%s</td>", fieldNotNum[field_index] ? "left" : "right", p != NULL ? p : "");
|
|
else {
|
|
fprintf(fout,
|
|
fieldNotNum[field_index] ? (po->standard ? " %-*s " : "%-*s") : (po->standard ? " %*s " : "%*s"),
|
|
fieldMax[field_index],
|
|
p != NULL ? p : "");
|
|
if (po->standard || field_index + 1 < nFields)
|
|
fputs(po->fieldSep, fout);
|
|
}
|
|
libpq_free(p);
|
|
}
|
|
if (po->html3)
|
|
fputs("</tr>", fout);
|
|
else if (po->standard)
|
|
fprintf(fout, "\n%s", border);
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
/*
|
|
* really old printing routines
|
|
*/
|
|
|
|
void PQdisplayTuples(const PGresult* res, FILE* fp, /* where to send the output */
|
|
int fillAlign, /* pad the fields with spaces */
|
|
const char* fieldSep, /* field separator */
|
|
int printHeader, /* display headers? */
|
|
int quiet)
|
|
{
|
|
#define DEFAULT_FIELD_SEP " "
|
|
|
|
int i, j;
|
|
int nFields;
|
|
int nTuples;
|
|
int* fLength = NULL;
|
|
|
|
if (fieldSep == NULL)
|
|
fieldSep = DEFAULT_FIELD_SEP;
|
|
|
|
/* Get some useful info about the results */
|
|
nFields = PQnfields(res);
|
|
nTuples = PQntuples(res);
|
|
|
|
if (fp == NULL)
|
|
fp = stdout;
|
|
|
|
/* Figure the field lengths to align to */
|
|
/* will be somewhat time consuming for very large results */
|
|
if (fillAlign) {
|
|
fLength = (int*)malloc(nFields * sizeof(int));
|
|
if (fLength == NULL) {
|
|
fprintf(stderr, libpq_gettext("out of memory\n"));
|
|
abort();
|
|
}
|
|
|
|
for (j = 0; j < nFields; j++) {
|
|
fLength[j] = strlen(PQfname(res, j));
|
|
for (i = 0; i < nTuples; i++) {
|
|
int flen = PQgetlength(res, i, j);
|
|
|
|
if (flen > fLength[j])
|
|
fLength[j] = flen;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (printHeader) {
|
|
/* first, print out the attribute names */
|
|
for (i = 0; i < nFields; i++) {
|
|
fputs(PQfname(res, i), fp);
|
|
if (fillAlign)
|
|
fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
|
|
fputs(fieldSep, fp);
|
|
}
|
|
fprintf(fp, "\n");
|
|
|
|
/* Underline the attribute names */
|
|
for (i = 0; i < nFields; i++) {
|
|
if (fillAlign)
|
|
fill(0, fLength[i], '-', fp);
|
|
fputs(fieldSep, fp);
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
/* next, print out the instances */
|
|
for (i = 0; i < nTuples; i++) {
|
|
for (j = 0; j < nFields; j++) {
|
|
fprintf(fp, "%s", PQgetvalue(res, i, j));
|
|
if (fillAlign)
|
|
fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
|
|
fputs(fieldSep, fp);
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
if (!quiet)
|
|
fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res), (PQntuples(res) == 1) ? "" : "s");
|
|
|
|
fflush(fp);
|
|
|
|
libpq_free(fLength);
|
|
}
|
|
|
|
void PQprintTuples(const PGresult* res, FILE* fout, /* output stream */
|
|
int PrintAttNames, /* print attribute names or not */
|
|
int TerseOutput, /* delimiter bars or not? */
|
|
int colWidth /* width of column, if 0, use variable width */
|
|
)
|
|
{
|
|
int nFields;
|
|
int nTups;
|
|
int i, j;
|
|
char formatString[80];
|
|
char* tborder = NULL;
|
|
int rcs = 0;
|
|
|
|
nFields = PQnfields(res);
|
|
nTups = PQntuples(res);
|
|
|
|
if (colWidth > 0)
|
|
rcs = sprintf_s(formatString, sizeof(formatString), "%%s %%-%ds", colWidth);
|
|
else
|
|
rcs = sprintf_s(formatString, sizeof(formatString), "%%s %%s");
|
|
|
|
securec_check_ss_c(rcs, "\0", "\0");
|
|
if (nFields > 0) { /* only print rows with at least 1 field. */
|
|
|
|
if (!TerseOutput) {
|
|
int width;
|
|
|
|
width = nFields * 14;
|
|
tborder = (char*)malloc(width + 1);
|
|
if (tborder == NULL) {
|
|
fprintf(stderr, libpq_gettext("out of memory\n"));
|
|
abort();
|
|
}
|
|
for (i = 0; i < width; i++)
|
|
tborder[i] = '-';
|
|
tborder[width] = '\0';
|
|
fprintf(fout, "%s\n", tborder);
|
|
}
|
|
|
|
for (i = 0; i < nFields; i++) {
|
|
if (PrintAttNames) {
|
|
fprintf(fout, formatString, TerseOutput ? "" : "|", PQfname(res, i));
|
|
}
|
|
}
|
|
|
|
if (PrintAttNames) {
|
|
if (TerseOutput)
|
|
fprintf(fout, "\n");
|
|
else
|
|
fprintf(fout, "|\n%s\n", tborder);
|
|
}
|
|
|
|
for (i = 0; i < nTups; i++) {
|
|
for (j = 0; j < nFields; j++) {
|
|
const char* pval = PQgetvalue(res, i, j);
|
|
|
|
fprintf(fout, formatString, TerseOutput ? "" : "|", pval != NULL ? pval : "");
|
|
}
|
|
if (TerseOutput)
|
|
fprintf(fout, "\n");
|
|
else
|
|
fprintf(fout, "|\n%s\n", tborder);
|
|
}
|
|
}
|
|
|
|
libpq_free(tborder);
|
|
}
|
|
|
|
/* simply send out max-length number of filler characters to fp */
|
|
|
|
static void fill(int length, int max, char filler, FILE* fp)
|
|
{
|
|
int count;
|
|
|
|
count = max - length;
|
|
while (count-- >= 0)
|
|
putc(filler, fp);
|
|
}
|