Commit c2f60711 authored by Tom Lane's avatar Tom Lane

Create a FETCH_COUNT parameter that causes psql to execute SELECT-like

queries via a cursor, fetching a limited number of rows at a time and
therefore not risking exhausting memory.  A disadvantage of the scheme
is that 'aligned' output mode will align each group of rows independently
leading to odd-looking output, but all the other output formats work
reasonably well.  Chris Mair, with some additional hacking by moi.
parent 7c5ac5ce
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.166 2006/07/27 19:52:04 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.167 2006/08/29 22:25:04 tgl Exp $
PostgreSQL documentation
-->
......@@ -2007,6 +2007,33 @@ bar
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FETCH_COUNT</varname></term>
<listitem>
<para>
If this variable is set to an integer value &gt; 0,
the results of <command>SELECT</command> queries are fetched
and displayed in groups of that many rows, rather than the
default behavior of collecting the entire result set before
display. Therefore only a
limited amount of memory is used, regardless of the size of
the result set. Settings of 100 to 1000 are commonly used
when enabling this feature.
Keep in mind that when using this feature, a query may
fail after having already displayed some rows.
</para>
<tip>
<para>
Although you can use any output format with this feature,
the default <literal>aligned</> format tends to look bad
because each group of <varname>FETCH_COUNT</varname> rows
will be formatted separately, leading to varying column
widths across the row groups. The other output formats work better.
</para>
</tip>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>HISTCONTROL</varname></term>
<listitem>
......
......@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.126 2006/08/29 15:19:50 tgl Exp $
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.127 2006/08/29 22:25:07 tgl Exp $
*/
#include "postgres_fe.h"
#include "common.h"
......@@ -28,6 +28,7 @@
#include "command.h"
#include "copy.h"
#include "mb/pg_wchar.h"
#include "mbprint.h"
/* Workarounds for Windows */
......@@ -53,7 +54,9 @@ typedef struct _timeb TimevalStruct;
#endif
static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec);
static bool command_no_begin(const char *query);
static bool is_select_command(const char *query);
/*
* "Safe" wrapper around strdup()
......@@ -450,18 +453,15 @@ ResetCancelConn(void)
* AcceptResult
*
* Checks whether a result is valid, giving an error message if necessary;
* resets cancelConn as needed, and ensures that the connection to the backend
* is still up.
* and ensures that the connection to the backend is still up.
*
* Returns true for valid result, false for error state.
*/
static bool
AcceptResult(const PGresult *result, const char *query)
AcceptResult(const PGresult *result)
{
bool OK = true;
ResetCancelConn();
if (!result)
OK = false;
else
......@@ -560,7 +560,9 @@ PSQLexec(const char *query, bool start_xact)
res = PQexec(pset.db, query);
if (!AcceptResult(res, query) && res)
ResetCancelConn();
if (!AcceptResult(res))
{
PQclear(res);
res = NULL;
......@@ -602,6 +604,7 @@ PrintQueryTuples(const PGresult *results)
/* write output to \g argument, if any */
if (pset.gfname)
{
/* keep this code in sync with ExecQueryUsingCursor */
FILE *queryFout_copy = pset.queryFout;
bool queryFoutPipe_copy = pset.queryFoutPipe;
......@@ -782,11 +785,10 @@ bool
SendQuery(const char *query)
{
PGresult *results;
TimevalStruct before,
after;
PGTransactionStatusType transaction_status;
double elapsed_msec = 0;
bool OK,
on_error_rollback_savepoint = false;
PGTransactionStatusType transaction_status;
static bool on_error_rollback_warning = false;
if (!pset.db)
......@@ -869,20 +871,38 @@ SendQuery(const char *query)
}
}
if (pset.timing)
GETTIMEOFDAY(&before);
if (pset.fetch_count <= 0 || !is_select_command(query))
{
/* Default fetch-it-all-and-print mode */
TimevalStruct before,
after;
results = PQexec(pset.db, query);
if (pset.timing)
GETTIMEOFDAY(&before);
/* these operations are included in the timing result: */
OK = (AcceptResult(results, query) && ProcessCopyResult(results));
results = PQexec(pset.db, query);
if (pset.timing)
GETTIMEOFDAY(&after);
/* these operations are included in the timing result: */
ResetCancelConn();
OK = (AcceptResult(results) && ProcessCopyResult(results));
/* but printing results isn't: */
if (OK)
OK = PrintQueryResults(results);
if (pset.timing)
{
GETTIMEOFDAY(&after);
elapsed_msec = DIFF_MSEC(&after, &before);
}
/* but printing results isn't: */
if (OK)
OK = PrintQueryResults(results);
}
else
{
/* Fetch-in-segments mode */
OK = ExecQueryUsingCursor(query, &elapsed_msec);
ResetCancelConn();
results = NULL; /* PQclear(NULL) does nothing */
}
/* If we made a temporary savepoint, possibly release/rollback */
if (on_error_rollback_savepoint)
......@@ -904,9 +924,10 @@ SendQuery(const char *query)
* the user did RELEASE or ROLLBACK, our savepoint is gone. If
* they issued a SAVEPOINT, releasing ours would remove theirs.
*/
if (strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 ||
strcmp(PQcmdStatus(results), "RELEASE") == 0 ||
strcmp(PQcmdStatus(results), "ROLLBACK") == 0)
if (results &&
(strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 ||
strcmp(PQcmdStatus(results), "RELEASE") == 0 ||
strcmp(PQcmdStatus(results), "ROLLBACK") == 0))
svptres = NULL;
else
svptres = PQexec(pset.db, "RELEASE pg_psql_temporary_savepoint");
......@@ -927,7 +948,7 @@ SendQuery(const char *query)
/* Possible microtiming output */
if (OK && pset.timing)
printf(_("Time: %.3f ms\n"), DIFF_MSEC(&after, &before));
printf(_("Time: %.3f ms\n"), elapsed_msec);
/* check for events that may occur during query execution */
......@@ -947,6 +968,198 @@ SendQuery(const char *query)
}
/*
* ExecQueryUsingCursor: run a SELECT-like query using a cursor
*
* This feature allows result sets larger than RAM to be dealt with.
*
* Returns true if the query executed successfully, false otherwise.
*
* If pset.timing is on, total query time (exclusive of result-printing) is
* stored into *elapsed_msec.
*/
static bool
ExecQueryUsingCursor(const char *query, double *elapsed_msec)
{
bool OK = true;
PGresult *results;
PQExpBufferData buf;
printQueryOpt my_popt = pset.popt;
FILE *queryFout_copy = pset.queryFout;
bool queryFoutPipe_copy = pset.queryFoutPipe;
bool started_txn = false;
bool did_pager = false;
int ntuples;
char fetch_cmd[64];
TimevalStruct before,
after;
*elapsed_msec = 0;
/* initialize print options for partial table output */
my_popt.topt.start_table = true;
my_popt.topt.stop_table = false;
my_popt.topt.prior_records = 0;
if (pset.timing)
GETTIMEOFDAY(&before);
/* if we're not in a transaction, start one */
if (PQtransactionStatus(pset.db) == PQTRANS_IDLE)
{
results = PQexec(pset.db, "BEGIN");
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
PQclear(results);
if (!OK)
return false;
started_txn = true;
}
/* Send DECLARE CURSOR */
initPQExpBuffer(&buf);
appendPQExpBuffer(&buf, "DECLARE _psql_cursor NO SCROLL CURSOR FOR\n%s",
query);
results = PQexec(pset.db, buf.data);
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
PQclear(results);
termPQExpBuffer(&buf);
if (!OK)
goto cleanup;
if (pset.timing)
{
GETTIMEOFDAY(&after);
*elapsed_msec += DIFF_MSEC(&after, &before);
}
snprintf(fetch_cmd, sizeof(fetch_cmd),
"FETCH FORWARD %d FROM _psql_cursor",
pset.fetch_count);
/* prepare to write output to \g argument, if any */
if (pset.gfname)
{
/* keep this code in sync with PrintQueryTuples */
pset.queryFout = stdout; /* so it doesn't get closed */
/* open file/pipe */
if (!setQFout(pset.gfname))
{
pset.queryFout = queryFout_copy;
pset.queryFoutPipe = queryFoutPipe_copy;
OK = false;
goto cleanup;
}
}
for (;;)
{
if (pset.timing)
GETTIMEOFDAY(&before);
/* get FETCH_COUNT tuples at a time */
results = PQexec(pset.db, fetch_cmd);
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_TUPLES_OK);
if (pset.timing)
{
GETTIMEOFDAY(&after);
*elapsed_msec += DIFF_MSEC(&after, &before);
}
if (!OK)
{
PQclear(results);
break;
}
ntuples = PQntuples(results);
if (ntuples < pset.fetch_count)
{
/* this is the last result set, so allow footer decoration */
my_popt.topt.stop_table = true;
}
else if (pset.queryFout == stdout && !did_pager)
{
/*
* If query requires multiple result sets, hack to ensure that
* only one pager instance is used for the whole mess
*/
pset.queryFout = PageOutput(100000, my_popt.topt.pager);
did_pager = true;
}
printQuery(results, &my_popt, pset.queryFout, pset.logfile);
/* after the first result set, disallow header decoration */
my_popt.topt.start_table = false;
my_popt.topt.prior_records += ntuples;
PQclear(results);
if (ntuples < pset.fetch_count || cancel_pressed)
break;
}
/* close \g argument file/pipe, restore old setting */
if (pset.gfname)
{
/* keep this code in sync with PrintQueryTuples */
setQFout(NULL);
pset.queryFout = queryFout_copy;
pset.queryFoutPipe = queryFoutPipe_copy;
free(pset.gfname);
pset.gfname = NULL;
}
else if (did_pager)
{
ClosePager(pset.queryFout);
pset.queryFout = queryFout_copy;
pset.queryFoutPipe = queryFoutPipe_copy;
}
cleanup:
if (pset.timing)
GETTIMEOFDAY(&before);
/*
* We try to close the cursor on either success or failure, but on
* failure ignore the result (it's probably just a bleat about
* being in an aborted transaction)
*/
results = PQexec(pset.db, "CLOSE _psql_cursor");
if (OK)
{
OK = AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
}
PQclear(results);
if (started_txn)
{
results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK");
OK &= AcceptResult(results) &&
(PQresultStatus(results) == PGRES_COMMAND_OK);
PQclear(results);
}
if (pset.timing)
{
GETTIMEOFDAY(&after);
*elapsed_msec += DIFF_MSEC(&after, &before);
}
return OK;
}
/*
* Advance the given char pointer over white space and SQL comments.
*/
......@@ -1158,6 +1371,43 @@ command_no_begin(const char *query)
}
/*
* Check whether the specified command is a SELECT (or VALUES).
*/
static bool
is_select_command(const char *query)
{
int wordlen;
/*
* First advance over any whitespace, comments and left parentheses.
*/
for (;;)
{
query = skip_white_space(query);
if (query[0] == '(')
query++;
else
break;
}
/*
* Check word length (since "selectx" is not "select").
*/
wordlen = 0;
while (isalpha((unsigned char) query[wordlen]))
wordlen += PQmblen(&query[wordlen], pset.encoding);
if (wordlen == 6 && pg_strncasecmp(query, "select", 6) == 0)
return true;
if (wordlen == 6 && pg_strncasecmp(query, "values", 6) == 0)
return true;
return false;
}
/*
* Test if the current user is a database superuser.
*
......
......@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.88 2006/07/14 14:52:26 momjian Exp $
* $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.89 2006/08/29 22:25:07 tgl Exp $
*
* Note: we include postgres.h not postgres_fe.h so that we can include
* catalog/pg_type.h, and thereby have access to INT4OID and similar macros.
......@@ -175,10 +175,13 @@ format_numeric_locale(const char *my_str)
static void
print_unaligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const char *opt_fieldsep,
const char *opt_recordsep, bool opt_tuples_only,
bool opt_numeric_locale, FILE *fout)
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
const char *opt_fieldsep = opt->fieldSep;
const char *opt_recordsep = opt->recordSep;
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
......@@ -192,27 +195,33 @@ print_unaligned_text(const char *title, const char *const * headers,
if (!opt_recordsep)
opt_recordsep = "";
/* print title */
if (!opt_tuples_only && title)
fprintf(fout, "%s%s", title, opt_recordsep);
/* print headers and count columns */
/* count columns */
for (ptr = headers; *ptr; ptr++)
{
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
fprintf(fout, "%s%s", title, opt_recordsep);
/* print headers */
if (!opt_tuples_only)
{
if (col_count > 1)
fputs(opt_fieldsep, fout);
fputs(*ptr, fout);
for (ptr = headers; *ptr; ptr++)
{
if (ptr != headers)
fputs(opt_fieldsep, fout);
fputs(*ptr, fout);
}
need_recordsep = true;
}
}
if (!opt_tuples_only)
else /* assume continuing printout */
need_recordsep = true;
/* print cells */
i = 0;
for (ptr = cells; *ptr; ptr++)
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
if (need_recordsep)
{
......@@ -235,40 +244,44 @@ print_unaligned_text(const char *title, const char *const * headers,
fputs(opt_fieldsep, fout);
else
need_recordsep = true;
i++;
}
/* print footers */
if (!opt_tuples_only && footers && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
if (need_recordsep)
if (opt->stop_table)
{
if (!opt_tuples_only && footers && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
fputs(opt_recordsep, fout);
need_recordsep = false;
if (need_recordsep)
{
fputs(opt_recordsep, fout);
need_recordsep = false;
}
fputs(*ptr, fout);
need_recordsep = true;
}
fputs(*ptr, fout);
need_recordsep = true;
}
/* the last record needs to be concluded with a newline */
if (need_recordsep)
fputc('\n', fout);
/* the last record needs to be concluded with a newline */
if (need_recordsep)
fputc('\n', fout);
}
}
static void
print_unaligned_vertical(const char *title, const char *const * headers,
const char *const * cells,
const char *const * footers, const char *opt_align,
const char *opt_fieldsep, const char *opt_recordsep,
bool opt_tuples_only, bool opt_numeric_locale, FILE *fout)
const printTableOpt *opt, FILE *fout)
{
const char *opt_fieldsep = opt->fieldSep;
const char *opt_recordsep = opt->recordSep;
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
bool need_recordsep = false;
if (cancel_pressed)
return;
......@@ -278,22 +291,31 @@ print_unaligned_vertical(const char *title, const char *const * headers,
if (!opt_recordsep)
opt_recordsep = "";
/* print title */
if (!opt_tuples_only && title)
fputs(title, fout);
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
{
fputs(title, fout);
need_recordsep = true;
}
}
else /* assume continuing printout */
need_recordsep = true;
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
if (i != 0 || (!opt_tuples_only && title))
if (need_recordsep)
{
/* record separator is 2 occurrences of recordsep in this mode */
fputs(opt_recordsep, fout);
fputs(opt_recordsep, fout);
if (i % col_count == 0)
fputs(opt_recordsep, fout); /* another one */
need_recordsep = false;
if (cancel_pressed)
break;
}
......@@ -309,24 +331,31 @@ print_unaligned_vertical(const char *title, const char *const * headers,
}
else
fputs(*ptr, fout);
if ((i + 1) % col_count)
fputs(opt_recordsep, fout);
else
need_recordsep = true;
}
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
if (opt->stop_table)
{
fputs(opt_recordsep, fout);
for (ptr = footers; *ptr; ptr++)
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
fputs(opt_recordsep, fout);
fputs(*ptr, fout);
for (ptr = footers; *ptr; ptr++)
{
fputs(opt_recordsep, fout);
fputs(*ptr, fout);
}
}
}
fputc('\n', fout);
fputc('\n', fout);
}
}
/********************/
/* Aligned text */
/********************/
......@@ -367,14 +396,16 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int *widths,
}
static void
print_aligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, bool opt_tuples_only, bool opt_numeric_locale,
unsigned short int opt_border, int encoding,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
unsigned int col_count = 0;
unsigned int cell_count = 0;
unsigned int i;
......@@ -395,6 +426,9 @@ print_aligned_text(const char *title, const char *const * headers,
if (cancel_pressed)
return;
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
......@@ -407,7 +441,6 @@ print_aligned_text(const char *title, const char *const * headers,
format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
complete = pg_local_calloc(col_count, sizeof(*complete));
}
else
{
......@@ -496,80 +529,83 @@ print_aligned_text(const char *title, const char *const * headers,
}
else
lineptr_list = NULL;
/* print title */
if (title && !opt_tuples_only)
{
/* Get width & height */
int height;
pg_wcssize((unsigned char *)title, strlen(title), encoding, &tmp, &height, NULL);
if (tmp >= total_w)
fprintf(fout, "%s\n", title);
else
fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
}
/* print headers */
if (!opt_tuples_only)
if (opt->start_table)
{
int cols_todo;
int line_count;
if (opt_border == 2)
_print_horizontal_line(col_count, widths, opt_border, fout);
/* print title */
if (title && !opt_tuples_only)
{
/* Get width & height */
int height;
pg_wcssize((unsigned char *)title, strlen(title), encoding, &tmp, &height, NULL);
if (tmp >= total_w)
fprintf(fout, "%s\n", title);
else
fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
}
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *)headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
cols_todo = col_count;
line_count = 0;
memset(complete, 0, col_count*sizeof(int));
while (cols_todo)
/* print headers */
if (!opt_tuples_only)
{
int cols_todo;
int line_count;
if (opt_border == 2)
fprintf(fout, "|%c", line_count ? '+' : ' ');
else if (opt_border == 1)
fputc(line_count ? '+' : ' ', fout);
_print_horizontal_line(col_count, widths, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *)headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
cols_todo = col_count;
line_count = 0;
memset(complete, 0, col_count*sizeof(int));
while (cols_todo)
{
unsigned int nbspace;
if (opt_border == 2)
fprintf(fout, "|%c", line_count ? '+' : ' ');
else if (opt_border == 1)
fputc(line_count ? '+' : ' ', fout);
struct lineptr *this_line = col_lineptrs[i] + line_count;
if (!complete[i])
for (i = 0; i < col_count; i++)
{
nbspace = widths[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
unsigned int nbspace;
if (line_count == (heights[i]-1) || !(this_line+1)->ptr)
struct lineptr *this_line = col_lineptrs[i] + line_count;
if (!complete[i])
{
cols_todo--;
complete[i] = 1;
nbspace = widths[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
if (line_count == (heights[i]-1) || !(this_line+1)->ptr)
{
cols_todo--;
complete[i] = 1;
}
}
}
else
fprintf(fout, "%*s", widths[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
fputc(line_count ? '+' : ' ', fout);
else
fprintf(fout, " |%c", line_count ? '+' : ' ');
fprintf(fout, "%*s", widths[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
fputc(line_count ? '+' : ' ', fout);
else
fprintf(fout, " |%c", line_count ? '+' : ' ');
}
}
line_count++;
if (opt_border == 2)
fputs(" |", fout);
else if (opt_border == 1)
fputc(' ', fout);;
fputc('\n', fout);
}
line_count++;
if (opt_border == 2)
fputs(" |", fout);
else if (opt_border == 1)
fputc(' ', fout);;
fputc('\n', fout);
_print_horizontal_line(col_count, widths, opt_border, fout);
}
_print_horizontal_line(col_count, widths, opt_border, fout);
}
/* print cells */
......@@ -658,21 +694,24 @@ print_aligned_text(const char *title, const char *const * headers,
}
}
if (opt_border == 2 && !cancel_pressed)
_print_horizontal_line(col_count, widths, opt_border, fout);
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
_print_horizontal_line(col_count, widths, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
fprintf(fout, "%s\n", *ptr);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
fprintf(fout, "%s\n", *ptr);
/*
* for some reason MinGW (and MSVC) outputs an extra newline,
* so this suppresses it
*/
#ifndef WIN32
/*
* for some reason MinGW (and MSVC) outputs an extra newline, so this supresses it
*/
fputc('\n', fout);
fputc('\n', fout);
#endif
}
/* clean up */
free(widths);
......@@ -687,16 +726,18 @@ print_aligned_text(const char *title, const char *const * headers,
}
static void
print_aligned_vertical(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, bool opt_tuples_only,
bool opt_numeric_locale, unsigned short int opt_border,
int encoding, FILE *fout)
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
unsigned int col_count = 0;
unsigned int record = 1;
unsigned long record = opt->prior_records + 1;
const char *const * ptr;
unsigned int i,
hwidth = 0,
......@@ -712,14 +753,17 @@ print_aligned_vertical(const char *title, const char *const * headers,
if (cancel_pressed)
return;
if (opt_border > 2)
opt_border = 2;
if (cells[0] == NULL)
if (cells[0] == NULL && opt->start_table && opt->stop_table)
{
fprintf(fout, _("(No rows)\n"));
return;
}
/* count headers and find longest one */
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
......@@ -767,10 +811,6 @@ print_aligned_vertical(const char *title, const char *const * headers,
dlineptr->ptr = pg_local_malloc(dformatsize);
hlineptr->ptr = pg_local_malloc(hformatsize);
/* print title */
if (!opt_tuples_only && title)
fprintf(fout, "%s\n", title);
/* make horizontal border */
divider = pg_local_malloc(hwidth + dwidth + 10);
......@@ -788,6 +828,13 @@ print_aligned_vertical(const char *title, const char *const * headers,
if (opt_border == 2)
strcat(divider, "-+");
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
fprintf(fout, "%s\n", title);
}
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
......@@ -799,13 +846,13 @@ print_aligned_vertical(const char *title, const char *const * headers,
break;
if (!opt_tuples_only)
{
char *record_str = pg_local_malloc(32);
char record_str[64];
size_t record_str_len;
if (opt_border == 0)
snprintf(record_str, 32, "* Record %d", record++);
snprintf(record_str, 64, "* Record %lu", record++);
else
snprintf(record_str, 32, "[ RECORD %d ]", record++);
snprintf(record_str, 64, "[ RECORD %lu ]", record++);
record_str_len = strlen(record_str);
if (record_str_len + opt_border > strlen(divider))
......@@ -824,9 +871,8 @@ print_aligned_vertical(const char *title, const char *const * headers,
fprintf(fout, "%s\n", div_copy);
free(div_copy);
}
free(record_str);
}
else if (i != 0 || opt_border == 2)
else if (i != 0 || !opt->start_table || opt_border == 2)
fprintf(fout, "%s\n", divider);
}
......@@ -893,20 +939,23 @@ print_aligned_vertical(const char *title, const char *const * headers,
}
}
if (opt_border == 2 && !cancel_pressed)
fprintf(fout, "%s\n", divider);
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
fprintf(fout, "%s\n", divider);
/* print footers */
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
if (opt_border < 2)
fputc('\n', fout);
for (ptr = footers; *ptr; ptr++)
fprintf(fout, "%s\n", *ptr);
}
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
if (opt_border < 2)
fputc('\n', fout);
for (ptr = footers; *ptr; ptr++)
fprintf(fout, "%s\n", *ptr);
fputc('\n', fout);
}
fputc('\n', fout);
free(divider);
free(hlineptr->ptr);
free(dlineptr->ptr);
......@@ -915,9 +964,6 @@ print_aligned_vertical(const char *title, const char *const * headers,
}
/**********************/
/* HTML printing ******/
/**********************/
......@@ -964,14 +1010,16 @@ html_escaped_print(const char *in, FILE *fout)
}
static void
print_html_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, bool opt_tuples_only,
bool opt_numeric_locale, unsigned short int opt_border,
const char *opt_table_attr, FILE *fout)
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
const char *opt_table_attr = opt->tableAttr;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
......@@ -979,34 +1027,38 @@ print_html_text(const char *title, const char *const * headers,
if (cancel_pressed)
return;
fprintf(fout, "<table border=\"%d\"", opt_border);
if (opt_table_attr)
fprintf(fout, " %s", opt_table_attr);
fputs(">\n", fout);
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
/* print title */
if (!opt_tuples_only && title)
if (opt->start_table)
{
fputs(" <caption>", fout);
html_escaped_print(title, fout);
fputs("</caption>\n", fout);
}
fprintf(fout, "<table border=\"%d\"", opt_border);
if (opt_table_attr)
fprintf(fout, " %s", opt_table_attr);
fputs(">\n", fout);
/* print headers and count columns */
if (!opt_tuples_only)
fputs(" <tr>\n", fout);
for (i = 0, ptr = headers; *ptr; i++, ptr++)
{
col_count++;
/* print title */
if (!opt_tuples_only && title)
{
fputs(" <caption>", fout);
html_escaped_print(title, fout);
fputs("</caption>\n", fout);
}
/* print headers */
if (!opt_tuples_only)
{
fputs(" <th align=\"center\">", fout);
html_escaped_print(*ptr, fout);
fputs("</th>\n", fout);
fputs(" <tr>\n", fout);
for (ptr = headers; *ptr; ptr++)
{
fputs(" <th align=\"center\">", fout);
html_escaped_print(*ptr, fout);
fputs("</th>\n", fout);
}
fputs(" </tr>\n", fout);
}
}
if (!opt_tuples_only)
fputs(" </tr>\n", fout);
/* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
......@@ -1038,57 +1090,65 @@ print_html_text(const char *title, const char *const * headers,
fputs(" </tr>\n", fout);
}
fputs("</table>\n", fout);
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
if (opt->stop_table)
{
fputs("<p>", fout);
for (ptr = footers; *ptr; ptr++)
fputs("</table>\n", fout);
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
html_escaped_print(*ptr, fout);
fputs("<br />\n", fout);
fputs("<p>", fout);
for (ptr = footers; *ptr; ptr++)
{
html_escaped_print(*ptr, fout);
fputs("<br />\n", fout);
}
fputs("</p>", fout);
}
fputs("</p>", fout);
fputc('\n', fout);
}
fputc('\n', fout);
}
static void
print_html_vertical(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, bool opt_tuples_only,
bool opt_numeric_locale, unsigned short int opt_border,
const char *opt_table_attr, FILE *fout)
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
const char *opt_table_attr = opt->tableAttr;
unsigned int col_count = 0;
unsigned long record = opt->prior_records + 1;
unsigned int i;
unsigned int record = 1;
const char *const * ptr;
if (cancel_pressed)
return;
fprintf(fout, "<table border=\"%d\"", opt_border);
if (opt_table_attr)
fprintf(fout, " %s", opt_table_attr);
fputs(">\n", fout);
/* print title */
if (!opt_tuples_only && title)
{
fputs(" <caption>", fout);
html_escaped_print(title, fout);
fputs("</caption>\n", fout);
}
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
fprintf(fout, "<table border=\"%d\"", opt_border);
if (opt_table_attr)
fprintf(fout, " %s", opt_table_attr);
fputs(">\n", fout);
/* print title */
if (!opt_tuples_only && title)
{
fputs(" <caption>", fout);
html_escaped_print(title, fout);
fputs("</caption>\n", fout);
}
}
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
......@@ -1097,7 +1157,9 @@ print_html_vertical(const char *title, const char *const * headers,
if (cancel_pressed)
break;
if (!opt_tuples_only)
fprintf(fout, "\n <tr><td colspan=\"2\" align=\"center\">Record %d</td></tr>\n", record++);
fprintf(fout,
"\n <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
record++);
else
fputs("\n <tr><td colspan=\"2\">&nbsp;</td></tr>\n", fout);
}
......@@ -1123,26 +1185,29 @@ print_html_vertical(const char *title, const char *const * headers,
fputs("</td>\n </tr>\n", fout);
}
fputs("</table>\n", fout);
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
if (opt->stop_table)
{
fputs("<p>", fout);
for (ptr = footers; *ptr; ptr++)
fputs("</table>\n", fout);
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
html_escaped_print(*ptr, fout);
fputs("<br />\n", fout);
fputs("<p>", fout);
for (ptr = footers; *ptr; ptr++)
{
html_escaped_print(*ptr, fout);
fputs("<br />\n", fout);
}
fputs("</p>", fout);
}
fputs("</p>", fout);
fputc('\n', fout);
}
fputc('\n', fout);
}
/*************************/
/* LaTeX */
/* LaTeX */
/*************************/
......@@ -1184,14 +1249,15 @@ latex_escaped_print(const char *in, FILE *fout)
}
static void
print_latex_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, bool opt_tuples_only,
bool opt_numeric_locale, unsigned short int opt_border,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
......@@ -1199,56 +1265,58 @@ print_latex_text(const char *title, const char *const * headers,
if (cancel_pressed)
return;
/* print title */
if (!opt_tuples_only && title)
{
fputs("\\begin{center}\n", fout);
latex_escaped_print(title, fout);
fputs("\n\\end{center}\n\n", fout);
}
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
/* begin environment and set alignments and borders */
fputs("\\begin{tabular}{", fout);
if (opt_border == 2)
fputs("| ", fout);
for (i = 0; i < col_count; i++)
if (opt->start_table)
{
fputc(*(opt_align + i), fout);
if (opt_border != 0 && i < col_count - 1)
fputs(" | ", fout);
}
if (opt_border == 2)
fputs(" |", fout);
/* print title */
if (!opt_tuples_only && title)
{
fputs("\\begin{center}\n", fout);
latex_escaped_print(title, fout);
fputs("\n\\end{center}\n\n", fout);
}
fputs("}\n", fout);
/* begin environment and set alignments and borders */
fputs("\\begin{tabular}{", fout);
if (!opt_tuples_only && opt_border == 2)
fputs("\\hline\n", fout);
if (opt_border == 2)
fputs("| ", fout);
for (i = 0; i < col_count; i++)
{
fputc(*(opt_align + i), fout);
if (opt_border != 0 && i < col_count - 1)
fputs(" | ", fout);
}
if (opt_border == 2)
fputs(" |", fout);
/* print headers and count columns */
for (i = 0, ptr = headers; i < col_count; i++, ptr++)
{
fputs("}\n", fout);
if (!opt_tuples_only && opt_border == 2)
fputs("\\hline\n", fout);
/* print headers */
if (!opt_tuples_only)
{
if (i != 0)
fputs(" & ", fout);
fputs("\\textit{", fout);
latex_escaped_print(*ptr, fout);
fputc('}', fout);
for (i = 0, ptr = headers; i < col_count; i++, ptr++)
{
if (i != 0)
fputs(" & ", fout);
fputs("\\textit{", fout);
latex_escaped_print(*ptr, fout);
fputc('}', fout);
}
fputs(" \\\\\n", fout);
fputs("\\hline\n", fout);
}
}
if (!opt_tuples_only)
{
fputs(" \\\\\n", fout);
fputs("\\hline\n", fout);
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
......@@ -1272,66 +1340,74 @@ print_latex_text(const char *title, const char *const * headers,
fputs(" & ", fout);
}
if (opt_border == 2)
fputs("\\hline\n", fout);
fputs("\\end{tabular}\n\n\\noindent ", fout);
if (opt->stop_table)
{
if (opt_border == 2)
fputs("\\hline\n", fout);
/* print footers */
fputs("\\end{tabular}\n\n\\noindent ", fout);
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
{
latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout);
for (ptr = footers; *ptr; ptr++)
{
latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout);
}
}
fputc('\n', fout);
fputc('\n', fout);
}
}
static void
print_latex_vertical(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, bool opt_tuples_only,
bool opt_numeric_locale, unsigned short int opt_border,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0;
unsigned long record = opt->prior_records + 1;
unsigned int i;
const char *const * ptr;
unsigned int record = 1;
(void) opt_align; /* currently unused parameter */
if (cancel_pressed)
return;
/* print title */
if (!opt_tuples_only && title)
{
fputs("\\begin{center}\n", fout);
latex_escaped_print(title, fout);
fputs("\n\\end{center}\n\n", fout);
}
/* begin environment and set alignments and borders */
fputs("\\begin{tabular}{", fout);
if (opt_border == 0)
fputs("cl", fout);
else if (opt_border == 1)
fputs("c|l", fout);
else if (opt_border == 2)
fputs("|c|l|", fout);
fputs("}\n", fout);
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
{
fputs("\\begin{center}\n", fout);
latex_escaped_print(title, fout);
fputs("\n\\end{center}\n\n", fout);
}
/* begin environment and set alignments and borders */
fputs("\\begin{tabular}{", fout);
if (opt_border == 0)
fputs("cl", fout);
else if (opt_border == 1)
fputs("c|l", fout);
else if (opt_border == 2)
fputs("|c|l|", fout);
fputs("}\n", fout);
}
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
......@@ -1346,10 +1422,10 @@ print_latex_vertical(const char *title, const char *const * headers,
if (opt_border == 2)
{
fputs("\\hline\n", fout);
fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %d}} \\\\\n", record++);
fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
}
else
fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %d}} \\\\\n", record++);
fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++);
}
if (opt_border >= 1)
fputs("\\hline\n", fout);
......@@ -1361,34 +1437,36 @@ print_latex_vertical(const char *title, const char *const * headers,
fputs(" \\\\\n", fout);
}
if (opt_border == 2)
fputs("\\hline\n", fout);
fputs("\\end{tabular}\n\n\\noindent ", fout);
if (opt->stop_table)
{
if (opt_border == 2)
fputs("\\hline\n", fout);
/* print footers */
fputs("\\end{tabular}\n\n\\noindent ", fout);
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
{
if (opt_numeric_locale)
for (ptr = footers; *ptr; ptr++)
{
char *my_cell = format_numeric_locale(*ptr);
if (opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
latex_escaped_print(my_cell, fout);
free(my_cell);
latex_escaped_print(my_cell, fout);
free(my_cell);
}
else
latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout);
}
else
latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout);
}
fputc('\n', fout);
fputc('\n', fout);
}
}
/*************************/
/* Troff -ms */
/*************************/
......@@ -1411,14 +1489,15 @@ troff_ms_escaped_print(const char *in, FILE *fout)
}
static void
print_troff_ms_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, bool opt_tuples_only,
bool opt_numeric_locale, unsigned short int opt_border,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
......@@ -1426,49 +1505,53 @@ print_troff_ms_text(const char *title, const char *const * headers,
if (cancel_pressed)
return;
/* print title */
if (!opt_tuples_only && title)
{
fputs(".LP\n.DS C\n", fout);
troff_ms_escaped_print(title, fout);
fputs("\n.DE\n", fout);
}
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
/* begin environment and set alignments and borders */
fputs(".LP\n.TS\n", fout);
if (opt_border == 2)
fputs("center box;\n", fout);
else
fputs("center;\n", fout);
for (i = 0; i < col_count; i++)
if (opt->start_table)
{
fputc(*(opt_align + i), fout);
if (opt_border > 0 && i < col_count - 1)
fputs(" | ", fout);
}
fputs(".\n", fout);
/* print title */
if (!opt_tuples_only && title)
{
fputs(".LP\n.DS C\n", fout);
troff_ms_escaped_print(title, fout);
fputs("\n.DE\n", fout);
}
/* print headers and count columns */
for (i = 0, ptr = headers; i < col_count; i++, ptr++)
{
/* begin environment and set alignments and borders */
fputs(".LP\n.TS\n", fout);
if (opt_border == 2)
fputs("center box;\n", fout);
else
fputs("center;\n", fout);
for (i = 0; i < col_count; i++)
{
fputc(*(opt_align + i), fout);
if (opt_border > 0 && i < col_count - 1)
fputs(" | ", fout);
}
fputs(".\n", fout);
/* print headers */
if (!opt_tuples_only)
{
if (i != 0)
fputc('\t', fout);
fputs("\\fI", fout);
troff_ms_escaped_print(*ptr, fout);
fputs("\\fP", fout);
for (i = 0, ptr = headers; i < col_count; i++, ptr++)
{
if (i != 0)
fputc('\t', fout);
fputs("\\fI", fout);
troff_ms_escaped_print(*ptr, fout);
fputs("\\fP", fout);
}
fputs("\n_\n", fout);
}
}
if (!opt_tuples_only)
fputs("\n_\n", fout);
/* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
......@@ -1492,34 +1575,36 @@ print_troff_ms_text(const char *title, const char *const * headers,
fputc('\t', fout);
}
fputs(".TE\n.DS L\n", fout);
/* print footers */
if (opt->stop_table)
{
fputs(".TE\n.DS L\n", fout);
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
troff_ms_escaped_print(*ptr, fout);
fputc('\n', fout);
}
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
troff_ms_escaped_print(*ptr, fout);
fputc('\n', fout);
}
fputs(".DE\n", fout);
fputs(".DE\n", fout);
}
}
static void
print_troff_ms_vertical(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, bool opt_tuples_only,
bool opt_numeric_locale, unsigned short int opt_border,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0;
unsigned long record = opt->prior_records + 1;
unsigned int i;
const char *const * ptr;
unsigned int record = 1;
unsigned short current_format = 0; /* 0=none, 1=header, 2=body */
(void) opt_align; /* currently unused parameter */
......@@ -1527,29 +1612,37 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
if (cancel_pressed)
return;
/* print title */
if (!opt_tuples_only && title)
{
fputs(".LP\n.DS C\n", fout);
troff_ms_escaped_print(title, fout);
fputs("\n.DE\n", fout);
}
/* begin environment and set alignments and borders */
fputs(".LP\n.TS\n", fout);
if (opt_border == 2)
fputs("center box;\n", fout);
else
fputs("center;\n", fout);
/* basic format */
if (opt_tuples_only)
fputs("c l;\n", fout);
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
{
fputs(".LP\n.DS C\n", fout);
troff_ms_escaped_print(title, fout);
fputs("\n.DE\n", fout);
}
/* begin environment and set alignments and borders */
fputs(".LP\n.TS\n", fout);
if (opt_border == 2)
fputs("center box;\n", fout);
else
fputs("center;\n", fout);
/* basic format */
if (opt_tuples_only)
fputs("c l;\n", fout);
}
else
current_format = 2; /* assume tuples printed already */
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
......@@ -1562,14 +1655,14 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
{
if (current_format != 1)
{
if (opt_border == 2 && i > 0)
if (opt_border == 2 && record > 1)
fputs("_\n", fout);
if (current_format != 0)
fputs(".T&\n", fout);
fputs("c s.\n", fout);
current_format = 1;
}
fprintf(fout, "\\fIRecord %d\\fP\n", record++);
fprintf(fout, "\\fIRecord %lu\\fP\n", record++);
}
if (opt_border >= 1)
fputs("_\n", fout);
......@@ -1604,23 +1697,23 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
fputc('\n', fout);
}
fputs(".TE\n.DS L\n", fout);
/* print footers */
if (opt->stop_table)
{
fputs(".TE\n.DS L\n", fout);
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
troff_ms_escaped_print(*ptr, fout);
fputc('\n', fout);
}
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
troff_ms_escaped_print(*ptr, fout);
fputc('\n', fout);
}
fputs(".DE\n", fout);
fputs(".DE\n", fout);
}
}
/********************************/
/* Public functions */
/********************************/
......@@ -1644,6 +1737,7 @@ PageOutput(int lines, unsigned short int pager)
)
{
const char *pagerprog;
FILE *pagerpipe;
#ifdef TIOCGWINSZ
int result;
......@@ -1661,7 +1755,9 @@ PageOutput(int lines, unsigned short int pager)
#ifndef WIN32
pqsignal(SIGPIPE, SIG_IGN);
#endif
return popen(pagerprog, "w");
pagerpipe = popen(pagerprog, "w");
if (pagerpipe)
return pagerpipe;
#ifdef TIOCGWINSZ
}
#endif
......@@ -1670,6 +1766,33 @@ PageOutput(int lines, unsigned short int pager)
return stdout;
}
/*
* ClosePager
*
* Close previously opened pager pipe, if any
*/
void
ClosePager(FILE *pagerpipe)
{
if (pagerpipe && pagerpipe != stdout)
{
/*
* If printing was canceled midstream, warn about it.
*
* Some pagers like less use Ctrl-C as part of their command
* set. Even so, we abort our processing and warn the user
* what we did. If the pager quit as a result of the
* SIGINT, this message won't go anywhere ...
*/
if (cancel_pressed)
fprintf(pagerpipe, _("Interrupted\n"));
pclose(pagerpipe);
#ifndef WIN32
pqsignal(SIGPIPE, SIG_DFL);
#endif
}
}
void
......@@ -1680,10 +1803,8 @@ printTable(const char *title,
const char *align,
const printTableOpt *opt, FILE *fout, FILE *flog)
{
const char *default_footer[] = {NULL};
unsigned short int border = opt->border;
static const char *default_footer[] = {NULL};
FILE *output;
bool use_expanded;
if (cancel_pressed)
return;
......@@ -1694,19 +1815,6 @@ printTable(const char *title,
if (!footers)
footers = default_footer;
if (opt->format != PRINT_HTML && border > 2)
border = 2;
/*
* We only want to display the results in "expanded" format if this is a
* normal (user-submitted) query, not a table we're printing for a slash
* command.
*/
if (opt->expanded)
use_expanded = true;
else
use_expanded = false;
if (fout == stdout)
{
int col_count = 0,
......@@ -1739,59 +1847,50 @@ printTable(const char *title,
/* print the stuff */
if (flog)
print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, opt->numericLocale, border, opt->encoding, flog);
print_aligned_text(title, headers, cells, footers, align,
opt, flog);
switch (opt->format)
{
case PRINT_UNALIGNED:
if (use_expanded)
if (opt->expanded)
print_unaligned_vertical(title, headers, cells, footers, align,
opt->fieldSep, opt->recordSep,
opt->tuples_only, opt->numericLocale, output);
opt, output);
else
print_unaligned_text(title, headers, cells, footers, align,
opt->fieldSep, opt->recordSep,
opt->tuples_only, opt->numericLocale, output);
opt, output);
break;
case PRINT_ALIGNED:
if (use_expanded)
if (opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align,
opt->tuples_only, opt->numericLocale, border,
opt->encoding, output);
opt, output);
else
print_aligned_text(title, headers, cells, footers, align,
opt->tuples_only, opt->numericLocale,
border, opt->encoding, output);
opt, output);
break;
case PRINT_HTML:
if (use_expanded)
if (opt->expanded)
print_html_vertical(title, headers, cells, footers, align,
opt->tuples_only, opt->numericLocale,
border, opt->tableAttr, output);
opt, output);
else
print_html_text(title, headers, cells, footers,
align, opt->tuples_only, opt->numericLocale, border,
opt->tableAttr, output);
print_html_text(title, headers, cells, footers, align,
opt, output);
break;
case PRINT_LATEX:
if (use_expanded)
if (opt->expanded)
print_latex_vertical(title, headers, cells, footers, align,
opt->tuples_only, opt->numericLocale,
border, output);
opt, output);
else
print_latex_text(title, headers, cells, footers, align,
opt->tuples_only, opt->numericLocale,
border, output);
opt, output);
break;
case PRINT_TROFF_MS:
if (use_expanded)
if (opt->expanded)
print_troff_ms_vertical(title, headers, cells, footers, align,
opt->tuples_only, opt->numericLocale,
border, output);
opt, output);
else
print_troff_ms_text(title, headers, cells, footers, align,
opt->tuples_only, opt->numericLocale,
border, output);
opt, output);
break;
default:
fprintf(stderr, _("invalid output format (internal error): %d"), opt->format);
......@@ -1799,28 +1898,11 @@ printTable(const char *title,
}
/* Only close if we used the pager */
if (fout == stdout && output != stdout)
{
/*
* If printing was canceled midstream, warn about it.
*
* Some pagers like less use Ctrl-C as part of their command
* set. Even so, we abort our processing and warn the user
* what we did. If the pager quit as a result of the
* SIGINT, this message won't go anywhere ...
*/
if (cancel_pressed)
fprintf(output, _("Interrupted\n"));
pclose(output);
#ifndef WIN32
pqsignal(SIGPIPE, SIG_DFL);
#endif
}
if (output != fout)
ClosePager(output);
}
void
printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog)
{
......@@ -1864,13 +1946,15 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
footers = opt->footers;
else if (!opt->topt.expanded && opt->default_footer)
{
footers = pg_local_calloc(2, sizeof(*footers));
unsigned long total_records;
footers = pg_local_calloc(2, sizeof(*footers));
footers[0] = pg_local_malloc(100);
if (PQntuples(result) == 1)
total_records = opt->topt.prior_records + PQntuples(result);
if (total_records == 1)
snprintf(footers[0], 100, _("(1 row)"));
else
snprintf(footers[0], 100, _("(%d rows)"), PQntuples(result));
snprintf(footers[0], 100, _("(%lu rows)"), total_records);
}
else
footers = NULL;
......
......@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.31 2006/03/05 15:58:51 momjian Exp $
* $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.32 2006/08/29 22:25:07 tgl Exp $
*/
#ifndef PRINT_H
#define PRINT_H
......@@ -12,6 +12,7 @@
extern FILE *PageOutput(int lines, unsigned short int pager);
extern void ClosePager(FILE *pagerpipe);
extern void html_escaped_print(const char *in, FILE *fout);
......@@ -32,11 +33,14 @@ typedef struct _printTableOpt
enum printFormat format; /* one of the above */
bool expanded; /* expanded/vertical output (if supported by
* output format) */
unsigned short int border; /* Print a border around the table. 0=none,
* 1=dividing lines, 2=full */
unsigned short int pager; /* use pager for output (if to stdout and
* stdout is a tty) 0=off 1=on 2=always */
bool tuples_only; /* don't output headers, row counts, etc. */
unsigned short int border; /* Print a border around the table. 0=none,
* 1=dividing lines, 2=full */
bool start_table; /* print start decoration, eg <table> */
bool stop_table; /* print stop decoration, eg </table> */
unsigned long prior_records; /* start offset for record counters */
char *fieldSep; /* field separator for unaligned text mode */
char *recordSep; /* record separator for unaligned text mode */
bool numericLocale; /* locale-aware numeric units separator and
......
......@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/settings.h,v 1.29 2006/08/29 15:19:51 tgl Exp $
* $PostgreSQL: pgsql/src/bin/psql/settings.h,v 1.30 2006/08/29 22:25:07 tgl Exp $
*/
#ifndef SETTINGS_H
#define SETTINGS_H
......@@ -96,6 +96,7 @@ typedef struct _psqlSettings
bool quiet;
bool singleline;
bool singlestep;
int fetch_count;
PSQL_ECHO echo;
PSQL_ECHO_HIDDEN echo_hidden;
PSQL_ERROR_ROLLBACK on_error_rollback;
......
......@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.136 2006/08/29 15:19:51 tgl Exp $
* $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.137 2006/08/29 22:25:08 tgl Exp $
*/
#include "postgres_fe.h"
......@@ -145,6 +145,8 @@ main(int argc, char *argv[])
pset.popt.topt.format = PRINT_ALIGNED;
pset.popt.topt.border = 1;
pset.popt.topt.pager = 1;
pset.popt.topt.start_table = true;
pset.popt.topt.stop_table = true;
pset.popt.default_footer = true;
pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
......@@ -798,6 +800,12 @@ singlestep_hook(const char *newval)
pset.singlestep = ParseVariableBool(newval);
}
static void
fetch_count_hook(const char *newval)
{
pset.fetch_count = ParseVariableNum(newval, -1, -1, false);
}
static void
echo_hook(const char *newval)
{
......@@ -899,6 +907,7 @@ EstablishVariableSpace(void)
SetVariableAssignHook(pset.vars, "QUIET", quiet_hook);
SetVariableAssignHook(pset.vars, "SINGLELINE", singleline_hook);
SetVariableAssignHook(pset.vars, "SINGLESTEP", singlestep_hook);
SetVariableAssignHook(pset.vars, "FETCH_COUNT", fetch_count_hook);
SetVariableAssignHook(pset.vars, "ECHO", echo_hook);
SetVariableAssignHook(pset.vars, "ECHO_HIDDEN", echo_hidden_hook);
SetVariableAssignHook(pset.vars, "ON_ERROR_ROLLBACK", on_error_rollback_hook);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment