From c2f60711d25b256dbe2c1ddce833c5c69ca051cc Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 29 Aug 2006 22:25:08 +0000 Subject: [PATCH] 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. --- doc/src/sgml/ref/psql-ref.sgml | 29 +- src/bin/psql/common.c | 298 ++++++++- src/bin/psql/print.c | 1030 +++++++++++++++++--------------- src/bin/psql/print.h | 10 +- src/bin/psql/settings.h | 3 +- src/bin/psql/startup.c | 11 +- 6 files changed, 878 insertions(+), 503 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 97809bb7b1..d6528d0bc1 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 3e9d23d195..87e5ebc1fd 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 9674acf8b0..01f47e43b4 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(" \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, "
", fout); - html_escaped_print(title, fout); - fputs("
\n", fout); + + /* print title */ + if (!opt_tuples_only && title) + { + fputs(" \n", fout); + } + + /* print headers */ if (!opt_tuples_only) { - fputs(" \n", fout); + fputs(" \n", fout); + for (ptr = headers; *ptr; ptr++) + { + 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("
", fout); + html_escaped_print(title, fout); + fputs("
", fout); - html_escaped_print(*ptr, fout); - fputs("
", fout); + html_escaped_print(*ptr, 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(" \n", fout); - } - /* count columns */ for (ptr = headers; *ptr; ptr++) col_count++; + if (opt->start_table) + { + fprintf(fout, "
", fout); - html_escaped_print(title, fout); - fputs("
\n", fout); + + /* print title */ + if (!opt_tuples_only && title) + { + 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 \n", record++); + fprintf(fout, + "\n \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("
", fout); + html_escaped_print(title, fout); + fputs("
Record %d
Record %lu
 
\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 a112998b48..0fc25dc846 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 9dc41e3bdf..3e34f93b95 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 8d3409bd19..6cbd95da9e 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);