diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 97809bb7b19..d6528d0bc10 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1,5 +1,5 @@
@@ -2007,6 +2007,33 @@ bar
+
+ FETCH_COUNT
+
+
+ If this variable is set to an integer value > 0,
+ the results of SELECT 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.
+
+
+
+ Although you can use any output format with this feature,
+ the default aligned> format tends to look bad
+ because each group of FETCH_COUNT rows
+ will be formatted separately, leading to varying column
+ widths across the row groups. The other output formats work better.
+
+
+
+
+
HISTCONTROL
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 3e9d23d1956..87e5ebc1fdd 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -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.
*
diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c
index 9674acf8b00..01f47e43b4f 100644
--- a/src/bin/psql/print.c
+++ b/src/bin/psql/print.c
@@ -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);
- if (i % col_count == 0)
- fputs(opt_recordsep, fout); /* another one */
+ fputs(opt_recordsep, fout);
+ 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);
-
- 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 title */
+ if (title && !opt_tuples_only)
{
- if (opt_border == 2)
- fprintf(fout, "|%c", line_count ? '+' : ' ');
- else if (opt_border == 1)
- fputc(line_count ? '+' : ' ', fout);
-
- for (i = 0; i < col_count; i++)
- {
- unsigned int nbspace;
-
- struct lineptr *this_line = col_lineptrs[i] + line_count;
- if (!complete[i])
- {
- 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 ? '+' : ' ');
- }
- }
- line_count++;
-
- if (opt_border == 2)
- fputs(" |", fout);
- else if (opt_border == 1)
- fputc(' ', fout);;
- fputc('\n', fout);
+ /* 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_horizontal_line(col_count, widths, opt_border, fout);
+ /* print headers */
+ if (!opt_tuples_only)
+ {
+ int cols_todo;
+ int line_count;
+
+ if (opt_border == 2)
+ _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)
+ {
+ if (opt_border == 2)
+ fprintf(fout, "|%c", line_count ? '+' : ' ');
+ else if (opt_border == 1)
+ fputc(line_count ? '+' : ' ', fout);
+
+ for (i = 0; i < col_count; i++)
+ {
+ unsigned int nbspace;
+
+ struct lineptr *this_line = col_lineptrs[i] + line_count;
+ if (!complete[i])
+ {
+ 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 ? '+' : ' ');
+ }
+ }
+ 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 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);
-
- /* print footers */
-
- if (!opt_tuples_only && footers && *footers && !cancel_pressed)
+ if (opt->stop_table)
{
- if (opt_border < 2)
- fputc('\n', fout);
- for (ptr = footers; *ptr; ptr++)
- fprintf(fout, "%s\n", *ptr);
+ if (opt_border == 2 && !cancel_pressed)
+ fprintf(fout, "%s\n", divider);
+
+ /* 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);
+ }
+
+ 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, "\n", fout);
-
- /* print title */
- if (!opt_tuples_only && title)
- {
- fputs(" ", fout);
- html_escaped_print(title, fout);
- fputs("\n", fout);
- }
-
- /* print headers and count columns */
- if (!opt_tuples_only)
- fputs(" \n", fout);
- for (i = 0, ptr = headers; *ptr; i++, ptr++)
- {
+ /* count columns */
+ for (ptr = headers; *ptr; ptr++)
col_count++;
+
+ if (opt->start_table)
+ {
+ fprintf(fout, "\n", fout);
+
+ /* print title */
+ if (!opt_tuples_only && title)
+ {
+ fputs(" ", fout);
+ html_escaped_print(title, fout);
+ fputs("\n", fout);
+ }
+
+ /* print headers */
if (!opt_tuples_only)
{
- fputs(" ", fout);
- html_escaped_print(*ptr, fout);
- fputs(" | \n", fout);
+ fputs(" \n", fout);
+ for (ptr = headers; *ptr; ptr++)
+ {
+ fputs(" ", fout);
+ html_escaped_print(*ptr, fout);
+ fputs(" | \n", fout);
+ }
+ fputs("
\n", fout);
}
}
- if (!opt_tuples_only)
- fputs(" \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(" \n", fout);
}
- fputs("
\n", fout);
-
- /* print footers */
-
- if (!opt_tuples_only && footers && *footers && !cancel_pressed)
+ if (opt->stop_table)
{
- fputs("", fout);
- for (ptr = footers; *ptr; ptr++)
- {
- html_escaped_print(*ptr, fout);
- fputs("
\n", fout);
- }
- fputs("
", fout);
- }
- fputc('\n', fout);
-}
+ fputs("
\n", fout);
+ /* print footers */
+ if (!opt_tuples_only && footers && *footers && !cancel_pressed)
+ {
+ fputs("", fout);
+ for (ptr = footers; *ptr; ptr++)
+ {
+ html_escaped_print(*ptr, fout);
+ fputs("
\n", fout);
+ }
+ fputs("
", 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, "\n", fout);
-
- /* print title */
- if (!opt_tuples_only && title)
- {
- fputs(" ", fout);
- html_escaped_print(title, fout);
- fputs("\n", fout);
- }
-
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
+ if (opt->start_table)
+ {
+ fprintf(fout, "\n", fout);
+
+ /* print title */
+ if (!opt_tuples_only && title)
+ {
+ fputs(" ", fout);
+ html_escaped_print(title, fout);
+ fputs("\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 Record %d |
\n", record++);
+ fprintf(fout,
+ "\n Record %lu |
\n",
+ record++);
else
fputs("\n |
\n", fout);
}
@@ -1123,26 +1185,29 @@ print_html_vertical(const char *title, const char *const * headers,
fputs("\n \n", fout);
}
- fputs("
\n", fout);
-
- /* print footers */
- if (!opt_tuples_only && footers && *footers && !cancel_pressed)
+ if (opt->stop_table)
{
- fputs("", fout);
- for (ptr = footers; *ptr; ptr++)
+ fputs("
\n", fout);
+
+ /* print footers */
+ if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
- html_escaped_print(*ptr, fout);
- fputs("
\n", fout);
+ fputs("", fout);
+ for (ptr = footers; *ptr; ptr++)
+ {
+ html_escaped_print(*ptr, fout);
+ fputs("
\n", fout);
+ }
+ fputs("
", fout);
}
- fputs("
", 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);
+ if (opt->stop_table)
+ {
+ if (opt_border == 2)
+ fputs("\\hline\n", fout);
- fputs("\\end{tabular}\n\n\\noindent ", fout);
+ fputs("\\end{tabular}\n\n\\noindent ", fout);
-
- /* print footers */
-
- 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);
+ if (opt->stop_table)
+ {
+ if (opt_border == 2)
+ fputs("\\hline\n", fout);
- fputs("\\end{tabular}\n\n\\noindent ", fout);
+ fputs("\\end{tabular}\n\n\\noindent ", fout);
-
- /* print footers */
-
- 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);
+ if (opt->stop_table)
+ {
+ fputs(".TE\n.DS L\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);
+ }
- /* 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);
+ if (opt->stop_table)
+ {
+ fputs(".TE\n.DS L\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);
+ }
- /* 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;
diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h
index a112998b484..0fc25dc846c 100644
--- a/src/bin/psql/print.h
+++ b/src/bin/psql/print.h
@@ -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 */
+ bool stop_table; /* print stop decoration, eg
*/
+ 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
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 9dc41e3bdfb..3e34f93b955 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -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;
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 8d3409bd194..6cbd95da9ed 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -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);