*** a/src/bin/psql/command.c --- b/src/bin/psql/command.c *************** *** 2407,2418 **** do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) else if (strcmp(param, "footer") == 0) { if (value) ! popt->default_footer = ParseVariableBool(value); else ! popt->default_footer = !popt->default_footer; if (!quiet) { ! if (popt->default_footer) puts(_("Default footer is on.")); else puts(_("Default footer is off.")); --- 2407,2418 ---- else if (strcmp(param, "footer") == 0) { if (value) ! popt->topt.default_footer = ParseVariableBool(value); else ! popt->topt.default_footer = !popt->topt.default_footer; if (!quiet) { ! if (popt->topt.default_footer) puts(_("Default footer is on.")); else puts(_("Default footer is off.")); *** a/src/bin/psql/describe.c --- b/src/bin/psql/describe.c *************** *** 1130,1135 **** describeOneTableDetails(const char *schemaname, --- 1130,1136 ---- retval = false; + myopt.default_footer = false; /* This output looks confusing in expanded mode. */ myopt.expanded = false; *************** *** 2363,2368 **** describeRoles(const char *pattern, bool verbose) --- 2364,2371 ---- const char align = 'l'; char **attr; + myopt.default_footer = false; + initPQExpBuffer(&buf); if (pset.sversion >= 80100) *************** *** 3362,3368 **** describeOneTSParser(const char *oid, const char *nspname, const char *prsname) sprintf(title, _("Text search parser \"%s\""), prsname); myopt.title = title; myopt.footers = NULL; ! myopt.default_footer = false; myopt.translate_header = true; myopt.translate_columns = translate_columns; --- 3365,3371 ---- sprintf(title, _("Text search parser \"%s\""), prsname); myopt.title = title; myopt.footers = NULL; ! myopt.topt.default_footer = false; myopt.translate_header = true; myopt.translate_columns = translate_columns; *************** *** 3393,3399 **** describeOneTSParser(const char *oid, const char *nspname, const char *prsname) sprintf(title, _("Token types for parser \"%s\""), prsname); myopt.title = title; myopt.footers = NULL; ! myopt.default_footer = true; myopt.translate_header = true; myopt.translate_columns = NULL; --- 3396,3402 ---- sprintf(title, _("Token types for parser \"%s\""), prsname); myopt.title = title; myopt.footers = NULL; ! myopt.topt.default_footer = true; myopt.translate_header = true; myopt.translate_columns = NULL; *************** *** 3725,3731 **** describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, myopt.nullPrint = NULL; myopt.title = title.data; myopt.footers = NULL; ! myopt.default_footer = false; myopt.translate_header = true; printQuery(res, &myopt, pset.queryFout, pset.logfile); --- 3728,3734 ---- myopt.nullPrint = NULL; myopt.title = title.data; myopt.footers = NULL; ! myopt.topt.default_footer = false; myopt.translate_header = true; printQuery(res, &myopt, pset.queryFout, pset.logfile); *** a/src/bin/psql/print.c --- b/src/bin/psql/print.c *************** *** 44,49 **** static char *decimal_point; --- 44,52 ---- static char *grouping; static char *thousands_sep; + static char default_footer[100]; + static printTableFooter default_footer_cell = { default_footer, NULL }; + /* Line style control structures */ const printTextFormat pg_asciiformat = { *************** *** 278,283 **** print_separator(struct separator sep, FILE *fout) --- 281,314 ---- } + /* + * Return the list of explicitly-requested footers or, when applicable, the + * default "(xx rows)" footer. Always omit the default footer when given + * non-default footers, "\pset footer off", or a specific instruction to that + * effect from a calling backslash command. Vertical formats number each row, + * making the default footer redundant; they do not call this function. + * + * The return value may point to static storage; do not keep it across calls. + */ + static printTableFooter * + footers_with_default(const printTableContent *cont) + { + if (cont->footers == NULL && cont->opt->default_footer) + { + unsigned long total_records; + + total_records = cont->opt->prior_records + cont->nrows; + snprintf(default_footer, sizeof(default_footer), + ngettext("(%lu row)", "(%lu rows)", total_records), + total_records); + + return &default_footer_cell; + } + else + return cont->footers; + } + + /*************************/ /* Unaligned text */ /*************************/ *************** *** 340,350 **** print_unaligned_text(const printTableContent *cont, FILE *fout) /* print footers */ if (cont->opt->stop_table) { ! if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) { printTableFooter *f; ! for (f = cont->footers; f; f = f->next) { if (need_recordsep) { --- 371,383 ---- /* print footers */ if (cont->opt->stop_table) { ! printTableFooter *footers = footers_with_default(cont); ! ! if (!opt_tuples_only && footers != NULL && !cancel_pressed) { printTableFooter *f; ! for (f = footers; f; f = f->next) { if (need_recordsep) { *************** *** 1034,1049 **** print_aligned_text(const printTableContent *cont, FILE *fout) if (cont->opt->stop_table) { if (opt_border == 2 && !cancel_pressed) _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_BOTTOM, format, fout); /* print footers */ ! if (cont->footers && !opt_tuples_only && !cancel_pressed) { printTableFooter *f; ! for (f = cont->footers; f; f = f->next) fprintf(fout, "%s\n", f->data); } --- 1067,1084 ---- if (cont->opt->stop_table) { + printTableFooter *footers = footers_with_default(cont); + if (opt_border == 2 && !cancel_pressed) _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_BOTTOM, format, fout); /* print footers */ ! if (footers && !opt_tuples_only && !cancel_pressed) { printTableFooter *f; ! for (f = footers; f; f = f->next) fprintf(fout, "%s\n", f->data); } *************** *** 1447,1461 **** print_html_text(const printTableContent *cont, FILE *fout) if (cont->opt->stop_table) { fputs("\n", fout); /* print footers */ ! if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) { printTableFooter *f; fputs("

", fout); ! for (f = cont->footers; f; f = f->next) { html_escaped_print(f->data, fout); fputs("
\n", fout); --- 1482,1498 ---- if (cont->opt->stop_table) { + printTableFooter *footers = footers_with_default(cont); + fputs("\n", fout); /* print footers */ ! if (!opt_tuples_only && footers != NULL && !cancel_pressed) { printTableFooter *f; fputs("

", fout); ! for (f = footers; f; f = f->next) { html_escaped_print(f->data, fout); fputs("
\n", fout); *************** *** 1668,1684 **** print_latex_text(const printTableContent *cont, FILE *fout) if (cont->opt->stop_table) { if (opt_border == 2) fputs("\\hline\n", fout); fputs("\\end{tabular}\n\n\\noindent ", fout); /* print footers */ ! if (cont->footers && !opt_tuples_only && !cancel_pressed) { printTableFooter *f; ! for (f = cont->footers; f; f = f->next) { latex_escaped_print(f->data, fout); fputs(" \\\\\n", fout); --- 1705,1723 ---- if (cont->opt->stop_table) { + printTableFooter *footers = footers_with_default(cont); + if (opt_border == 2) fputs("\\hline\n", fout); fputs("\\end{tabular}\n\n\\noindent ", fout); /* print footers */ ! if (footers && !opt_tuples_only && !cancel_pressed) { printTableFooter *f; ! for (f = footers; f; f = f->next) { latex_escaped_print(f->data, fout); fputs(" \\\\\n", fout); *************** *** 1871,1884 **** print_troff_ms_text(const printTableContent *cont, FILE *fout) if (cont->opt->stop_table) { fputs(".TE\n.DS L\n", fout); /* print footers */ ! if (cont->footers && !opt_tuples_only && !cancel_pressed) { printTableFooter *f; ! for (f = cont->footers; f; f = f->next) { troff_ms_escaped_print(f->data, fout); fputc('\n', fout); --- 1910,1925 ---- if (cont->opt->stop_table) { + printTableFooter *footers = footers_with_default(cont); + fputs(".TE\n.DS L\n", fout); /* print footers */ ! if (footers && !opt_tuples_only && !cancel_pressed) { printTableFooter *f; ! for (f = footers; f; f = f->next) { troff_ms_escaped_print(f->data, fout); fputc('\n', fout); *************** *** 2481,2498 **** printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f for (footer = opt->footers; *footer; footer++) printTableAddFooter(&cont, *footer); } - else if (!opt->topt.expanded && opt->default_footer) - { - unsigned long total_records; - char default_footer[100]; - - total_records = opt->topt.prior_records + cont.nrows; - snprintf(default_footer, sizeof(default_footer), - ngettext("(%lu row)", "(%lu rows)", total_records), - total_records); - - printTableAddFooter(&cont, default_footer); - } printTable(&cont, fout, flog); printTableCleanup(&cont); --- 2522,2527 ---- *** a/src/bin/psql/print.h --- b/src/bin/psql/print.h *************** *** 85,90 **** typedef struct printTableOpt --- 85,91 ---- bool tuples_only; /* don't output headers, row counts, etc. */ bool start_table; /* print start decoration, eg */ bool stop_table; /* print stop decoration, eg
*/ + bool default_footer; /* allow "(xx rows)" default footer */ unsigned long prior_records; /* start offset for record counters */ const printTextFormat *line_style; /* line style (NULL for default) */ struct separator fieldSep; /* field separator for unaligned text mode */ *************** *** 141,147 **** typedef struct printQueryOpt bool quote; /* quote all values as much as possible */ char *title; /* override title */ char **footers; /* override footer (default is "(xx rows)") */ - bool default_footer; /* print default footer if footers==NULL */ bool translate_header; /* do gettext on column headers */ const bool *translate_columns; /* translate_columns[i-1] => do * gettext on col i */ --- 142,147 ---- *** a/src/bin/psql/startup.c --- b/src/bin/psql/startup.c *************** *** 129,135 **** main(int argc, char *argv[]) pset.popt.topt.pager = 1; pset.popt.topt.start_table = true; pset.popt.topt.stop_table = true; ! pset.popt.default_footer = true; /* We must get COLUMNS here before readline() sets it */ pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0; --- 129,135 ---- pset.popt.topt.pager = 1; pset.popt.topt.start_table = true; pset.popt.topt.stop_table = true; ! pset.popt.topt.default_footer = true; /* We must get COLUMNS here before readline() sets it */ pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0;