diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 2a9c412020..ee0c8fbea6 100644 *** a/doc/src/sgml/ref/psql-ref.sgml --- b/doc/src/sgml/ref/psql-ref.sgml *************** *** 2366,2372 **** lo_import 152801 aligned, wrapped, html, asciidoc, latex (uses tabular), ! latex-longtable, or troff-ms. Unique abbreviations are allowed. (That would mean one letter is enough.) --- 2366,2373 ---- aligned, wrapped, html, asciidoc, latex (uses tabular), ! latex-longtable, ! rst, markdown, or troff-ms. Unique abbreviations are allowed. (That would mean one letter is enough.) *************** *** 2394,2400 **** lo_import 152801 The html, asciidoc, latex, ! latex-longtable, and troff-ms formats put out tables that are intended to be included in documents using the respective mark-up language. They are not complete documents! This might not be --- 2395,2402 ---- The html, asciidoc, latex, ! latex-longtable, troff-ms, ! markdown and rst formats put out tables that are intended to be included in documents using the respective mark-up language. They are not complete documents! This might not be diff --git a/src/bin/psql/command.c bindex 4f4a0aa9bd..97f4f37923 100644 *** a/src/bin/psql/command.c --- b/src/bin/psql/command.c *************** *** 2518,2523 **** _align2string(enum printFormat in) --- 2518,2529 ---- case PRINT_TROFF_MS: return "troff-ms"; break; + case PRINT_MARKDOWN: + return "markdown"; + break; + case PRINT_RST: + return "rst"; + break; } return "unknown"; } *************** *** 2589,2597 **** do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) popt->topt.format = PRINT_LATEX_LONGTABLE; else if (pg_strncasecmp("troff-ms", value, vallen) == 0) popt->topt.format = PRINT_TROFF_MS; else { ! psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, asciidoc, latex, latex-longtable, troff-ms\n"); return false; } } --- 2595,2607 ---- popt->topt.format = PRINT_LATEX_LONGTABLE; else if (pg_strncasecmp("troff-ms", value, vallen) == 0) popt->topt.format = PRINT_TROFF_MS; + else if (pg_strncasecmp("markdown", value, vallen) == 0) + popt->topt.format = PRINT_MARKDOWN; + else if (pg_strncasecmp("rst", value, vallen) == 0) + popt->topt.format = PRINT_RST; else { ! psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, asciidoc, latex, latex-longtable, troff-ms, markdown, rst\n"); return false; } } diff --git a/src/bin/psql/helindex ba14df0344..d9dc5e10d9 100644 *** a/src/bin/psql/help.c --- b/src/bin/psql/help.c *************** *** 375,381 **** helpVariables(unsigned short int pager) fprintf(output, _(" fieldsep field separator for unaligned output (default \"%s\")\n"), DEFAULT_FIELD_SEP); fprintf(output, _(" fieldsep_zero set field separator for unaligned output to zero byte\n")); fprintf(output, _(" footer enable or disable display of the table footer [on, off]\n")); ! fprintf(output, _(" format set output format [unaligned, aligned, wrapped, html, asciidoc, ...]\n")); fprintf(output, _(" linestyle set the border line drawing style [ascii, old-ascii, unicode]\n")); fprintf(output, _(" null set the string to be printed in place of a null value\n")); fprintf(output, _(" numericlocale enable or disable display of a locale-specific character to separate\n" --- 375,381 ---- fprintf(output, _(" fieldsep field separator for unaligned output (default \"%s\")\n"), DEFAULT_FIELD_SEP); fprintf(output, _(" fieldsep_zero set field separator for unaligned output to zero byte\n")); fprintf(output, _(" footer enable or disable display of the table footer [on, off]\n")); ! fprintf(output, _(" format set output format [unaligned, aligned, wrapped, html, asciidoc, rst, markdown ...]\n")); fprintf(output, _(" linestyle set the border line drawing style [ascii, old-ascii, unicode]\n")); fprintf(output, _(" null set the string to be printed in place of a null value\n")); fprintf(output, _(" numericlocale enable or disable display of a locale-specific character to separate\n" diff --git a/src/bin/psql/index f7494065de..ea14ec3d28 100644 *** a/src/bin/psql/tab-complete.c --- b/src/bin/psql/tab-complete.c *************** *** 3422,3428 **** psql_completion(const char *text, int start, int end) { static const char *const my_list[] = {"unaligned", "aligned", "wrapped", "html", "asciidoc", ! "latex", "latex-longtable", "troff-ms", NULL}; COMPLETE_WITH_LIST_CS(my_list); } --- 3422,3428 ---- { static const char *const my_list[] = {"unaligned", "aligned", "wrapped", "html", "asciidoc", ! "latex", "latex-longtable", "troff-ms", "markdown", "rst", NULL}; COMPLETE_WITH_LIST_CS(my_list); } diff --git a/src/fe_utils/print.c index 9180b90004..322fa12b24 100644 *** a/src/fe_utils/print.c --- b/src/fe_utils/print.c *************** *** 56,61 **** static char default_footer[100]; --- 56,104 ---- static printTableFooter default_footer_cell = {default_footer, NULL}; /* Line style control structures */ + + const printTextFormat pg_markdown = + { + "markdown", + { + {"", "", "", ""}, + {"-", "|", "|", "|"}, + {"", "", "", ""}, + {"", "|", "|", "|"} + }, + "|", + "|", + "|", + " ", + "+", + " ", + " ", + ".", + ".", + true + }; + + const printTextFormat pg_rst = + { + "rst", + { + {"-", "+", "+", "+"}, + {"=", "+", "+", "+"}, + {"-", "+", "+", "+"}, + {" ", "|", "|", "|"} + }, + "|", + "|", + "|", + " ", + "+", + " ", + " ", + ".", + ".", + true + }; + const printTextFormat pg_asciiformat = { "ascii", *************** *** 205,210 **** static void IsPagerNeeded(const printTableContent *cont, int extra_lines, bool e --- 248,255 ---- static void print_aligned_vertical(const printTableContent *cont, FILE *fout, bool is_pager); + static void skip_leading_spaces_print(const char *in, FILE *fout); + /* Count number of digits in integral part of number */ static int *************** *** 574,579 **** _print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths, --- 619,655 ---- fputc('\n', fout); } + /* + * skip leading spaces + */ + static void + skip_leading_spaces_print(const char *in, FILE *fout) + { + const char *p; + bool leading_space = true; + unsigned int spac; /**leading spaces*/ + spac = 0; + + for (p = in; *p; p++) + { + if (*p != ' ' && *p != '\n') + { + leading_space = false; + fputc(*p, fout); + } + else if (*p == '\n') + { + fputc(*p, fout); + leading_space = true; + } + else if (leading_space) + spac++; + else + fputc(*p, fout); + } + if (spac != 0) + fprintf(fout, "%*s", spac, " "); + } /* * Print pretty boxes around cells. *************** *** 622,627 **** print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager) --- 698,709 ---- if (opt_border > 2) opt_border = 2; + if (format == &pg_markdown) + opt_border = 2; + + if (format == &pg_rst) + opt_border = 2; + if (cont->ncolumns > 0) { col_count = cont->ncolumns; *************** *** 1052,1060 **** print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager) else /* Left aligned cell */ { /* spaces second */ ! fputnbytes(fout, ! (char *) (this_line->ptr + bytes_output[j]), ! bytes_to_output); } bytes_output[j] += bytes_to_output; --- 1134,1145 ---- else /* Left aligned cell */ { /* spaces second */ ! if (format != &pg_rst) ! fputnbytes(fout, ! (char *) (this_line->ptr + bytes_output[j]), ! bytes_to_output); ! else ! skip_leading_spaces_print((char *) (this_line->ptr + bytes_output[j]), fout); } bytes_output[j] += bytes_to_output; *************** *** 1123,1142 **** print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager) fputc('\n', fout); } while (more_lines); } 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); --- 1208,1241 ---- fputc('\n', fout); } while (more_lines); + + if (format == &pg_rst) + _print_horizontal_line(col_count, width_wrap, opt_border, + PRINT_RULE_BOTTOM, format, fout); } if (cont->opt->stop_table) { printTableFooter *footers = footers_with_default(cont); ! if ((opt_border == 2 && format != &pg_rst) && !cancel_pressed) ! /* ! * dont add line after last row, because line is added after every row ! */ _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; + /* + * add newline after table because rst needs empty line after table + */ + if (format == &pg_rst || format == &pg_markdown) + { + fprintf(fout, "\n"); + } for (f = footers; f; f = f->next) fprintf(fout, "%s\n", f->data); *************** *** 1192,1197 **** print_aligned_vertical_line(const printTextFormat *format, --- 1291,1298 ---- { if (opt_border == 0) reclen = fprintf(fout, "* Record %lu", record); + else if (format == &pg_rst) + reclen = fprintf(fout, "**RECORD %lu**", record); else reclen = fprintf(fout, "[ RECORD %lu ]", record); } *************** *** 1257,1262 **** print_aligned_vertical(const printTableContent *cont, --- 1358,1366 ---- if (opt_border > 2) opt_border = 2; + if (format == &pg_rst) /*rst works only with border 2*/ + opt_border = 2; + if (cont->cells[0] == NULL && cont->opt->start_table && cont->opt->stop_table) { *************** *** 1305,1310 **** print_aligned_vertical(const printTableContent *cont, --- 1409,1416 ---- if (fs > hformatsize) hformatsize = fs; } + if (format == &pg_rst) + hwidth = hwidth + 4; /* find longest data cell */ for (i = 0, ptr = cont->cells; *ptr; ptr++, i++) *************** *** 1503,1509 **** print_aligned_vertical(const printTableContent *cont, if (cancel_pressed) break; ! if (i == 0) pos = PRINT_RULE_TOP; else pos = PRINT_RULE_MIDDLE; --- 1609,1615 ---- if (cancel_pressed) break; ! if (i == 0 || format == &pg_rst) pos = PRINT_RULE_TOP; else pos = PRINT_RULE_MIDDLE; *************** *** 1518,1526 **** print_aligned_vertical(const printTableContent *cont, (format == &pg_asciiformat_old)) lhwidth++; /* for newline indicators */ ! if (!opt_tuples_only) print_aligned_vertical_line(format, opt_border, record++, lhwidth, dwidth, pos, fout); else if (i != 0 || !cont->opt->start_table || opt_border == 2) print_aligned_vertical_line(format, opt_border, 0, lhwidth, dwidth, pos, fout); --- 1624,1642 ---- (format == &pg_asciiformat_old)) lhwidth++; /* for newline indicators */ ! if (!opt_tuples_only && cont->opt->format != PRINT_RST) print_aligned_vertical_line(format, opt_border, record++, lhwidth, dwidth, pos, fout); + else if (!opt_tuples_only && format == &pg_rst) + { + if (i ==0) + print_aligned_vertical_line(format, opt_border, 0, lhwidth, + dwidth, pos, fout); + print_aligned_vertical_line(format, opt_border, record++, + 0, lhwidth + dwidth, PRINT_RULE_DATA, fout); /* because need of only one column*/ + print_aligned_vertical_line(format, opt_border, 0, lhwidth, + dwidth, pos, fout); + } else if (i != 0 || !cont->opt->start_table || opt_border == 2) print_aligned_vertical_line(format, opt_border, 0, lhwidth, dwidth, pos, fout); *************** *** 1564,1572 **** print_aligned_vertical(const printTableContent *cont, /* * Header text */ ! strlen_max_width(hlineptr[hline].ptr, &target_width, ! encoding); ! fprintf(fout, "%-s", hlineptr[hline].ptr); /* * Spacer --- 1680,1695 ---- /* * Header text */ ! if (format != &pg_rst) ! strlen_max_width(hlineptr[hline].ptr, &target_width, ! encoding); ! else ! strlen_max_width(hlineptr[hline].ptr, &target_width - 4, ! encoding); ! if (format != &pg_rst) ! fprintf(fout, "%-s", hlineptr[hline].ptr); ! else ! fprintf(fout, "**%-s**", hlineptr[hline].ptr); /*header bold*/ /* * Spacer *************** *** 1640,1647 **** print_aligned_vertical(const printTableContent *cont, */ bytes_to_output = strlen_max_width(dlineptr[dline].ptr + offset, &target_width, encoding); ! fputnbytes(fout, (char *) (dlineptr[dline].ptr + offset), ! bytes_to_output); chars_to_output -= target_width; offset += bytes_to_output; --- 1763,1774 ---- */ bytes_to_output = strlen_max_width(dlineptr[dline].ptr + offset, &target_width, encoding); ! ! if (format != &pg_rst) ! fputnbytes(fout, (char *) (dlineptr[dline].ptr + offset), ! bytes_to_output); ! else ! skip_leading_spaces_print((char *) (dlineptr[dline].ptr + offset), fout); chars_to_output -= target_width; offset += bytes_to_output; *************** *** 1704,1714 **** print_aligned_vertical(const printTableContent *cont, fprintf(fout, "%*s %s\n", dwidth, "", dformat->rightvrule); } } } if (cont->opt->stop_table) { ! if (opt_border == 2 && !cancel_pressed) print_aligned_vertical_line(format, opt_border, 0, hwidth, dwidth, PRINT_RULE_BOTTOM, fout); --- 1831,1844 ---- fprintf(fout, "%*s %s\n", dwidth, "", dformat->rightvrule); } } + if (opt_border == 2 && format == &pg_rst) + print_aligned_vertical_line(format, opt_border, 0, hwidth, dwidth, + PRINT_RULE_BOTTOM, fout); } if (cont->opt->stop_table) { ! if (opt_border == 2 && !cancel_pressed && format != &pg_rst) print_aligned_vertical_line(format, opt_border, 0, hwidth, dwidth, PRINT_RULE_BOTTOM, fout); *************** *** 3262,3267 **** printTable(const printTableContent *cont, --- 3392,3407 ---- else print_troff_ms_text(cont, fout); break; + case PRINT_RST: + if (cont->opt->expanded == 1) + print_aligned_vertical(cont, fout, false); + else + print_aligned_text(cont, fout, false); + break; + case PRINT_MARKDOWN: + print_aligned_text(cont, fout, false); + break; + default: fprintf(stderr, _("invalid output format (internal error): %d"), cont->opt->format); *************** *** 3416,3422 **** get_line_style(const printTableOpt *opt) * printTableOpt struct can be initialized to zeroes to get default * behavior. */ ! if (opt->line_style != NULL) return opt->line_style; else return &pg_asciiformat; --- 3556,3566 ---- * printTableOpt struct can be initialized to zeroes to get default * behavior. */ ! if (opt->format == PRINT_RST) ! return &pg_rst; ! else if (opt->format == PRINT_MARKDOWN) ! return &pg_markdown; ! else if (opt->line_style != NULL) return opt->line_style; else return &pg_asciiformat; diff --git a/src/include/feindex d89b6febcb..e8b7532bbf 100644 *** a/src/include/fe_utils/print.h --- b/src/include/fe_utils/print.h *************** *** 33,39 **** enum printFormat PRINT_ASCIIDOC, PRINT_LATEX, PRINT_LATEX_LONGTABLE, ! PRINT_TROFF_MS /* add your favourite output format here ... */ }; --- 33,41 ---- PRINT_ASCIIDOC, PRINT_LATEX, PRINT_LATEX_LONGTABLE, ! PRINT_TROFF_MS, ! PRINT_MARKDOWN, ! PRINT_RST /* add your favourite output format here ... */ }; *************** *** 176,181 **** typedef struct printQueryOpt --- 178,185 ---- extern volatile bool cancel_pressed; extern const printTextFormat pg_asciiformat; + extern const printTextFormat pg_markdown; /*linestyle markdown*/ + extern const printTextFormat pg_rst; /*linestyle rst*/ extern const printTextFormat pg_asciiformat_old; extern printTextFormat pg_utf8format; /* ideally would be const, but... */ diff --git a/src/test/regress/expecindex eb7f197b12..ab79a52d67 100644 *** a/src/test/regress/expected/psql.out --- b/src/test/regress/expected/psql.out *************** *** 2763,2765 **** NOTICE: foo --- 2763,2864 ---- CONTEXT: PL/pgSQL function inline_code_block line 3 at RAISE ERROR: bar CONTEXT: PL/pgSQL function inline_code_block line 4 at RAISE + prepare q AS VALUES(E'Elephant, kangaroo,\nsquirrel, gorilla', 121, + (279./278.)::text, 0.1111, repeat('Hello ', 10)) + , (E'goat, rhinoceros,\nmonkey, ape', 11121, (1279./1278.)::text, 5.1111, + repeat('xxxxxx ', 10)) + , (E'donkey, cow, horse, tit,\neagle, whale,\naligator, + pelican,\ngrasshoper\npig\n\tbat', 14351, (12345./245.)::text, 345.11, + repeat('yyyyyy ', 10)); + \pset format rst + execute q; + +--------------------------+---------+---------------------+---------+------------------------------------------------------------------------+ + | column1 | column2 | column3 | column4 | column5 | + +==========================+=========+=====================+=========+========================================================================+ + | Elephant, kangaroo, | 121 | 1.0035971223021583 | 0.1111 | Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello | + | squirrel, gorilla | | | | | + +--------------------------+---------+---------------------+---------+------------------------------------------------------------------------+ + | goat, rhinoceros, | 11121 | 1.0007824726134585 | 5.1111 | xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx | + | monkey, ape | | | | | + +--------------------------+---------+---------------------+---------+------------------------------------------------------------------------+ + | donkey, cow, horse, tit, | 14351 | 50.3877551020408163 | 345.11 | yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy | + | eagle, whale, | | | | | + | aligator, | | | | | + | pelican, | | | | | + | grasshoper | | | | | + | pig | | | | | + | bat | | | | | + +--------------------------+---------+---------------------+---------+------------------------------------------------------------------------+ + + (3 rows) + + \pset format markdown + execute q; + + | column1 | column2 | column3 | column4 | column5 | + |--------------------------|---------|---------------------|---------|------------------------------------------------------------------------| + | Elephant, kangaroo, | 121 | 1.0035971223021583 | 0.1111 | Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello | + | squirrel, gorilla | | | | | + | goat, rhinoceros, | 11121 | 1.0007824726134585 | 5.1111 | xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx | + | monkey, ape | | | | | + | donkey, cow, horse, tit, | 14351 | 50.3877551020408163 | 345.11 | yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy | + | eagle, whale, | | | | | + | aligator, | | | | | + | pelican, | | | | | + | grasshoper | | | | | + | pig | | | | | + | bat | | | | | + + + (3 rows) + + \pset format rst + \x + execute q; + +-------------+------------------------------------------------------------------------+ + | **RECORD 1** | + +-------------+------------------------------------------------------------------------+ + | **column1** | Elephant, kangaroo, | + | | squirrel, gorilla | + +-------------+------------------------------------------------------------------------+ + | **column2** | 121 | + +-------------+------------------------------------------------------------------------+ + | **column3** | 1.0035971223021583 | + +-------------+------------------------------------------------------------------------+ + | **column4** | 0.1111 | + +-------------+------------------------------------------------------------------------+ + | **column5** | Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello | + +-------------+------------------------------------------------------------------------+ + | **RECORD 2** | + +-------------+------------------------------------------------------------------------+ + | **column1** | goat, rhinoceros, | + | | monkey, ape | + +-------------+------------------------------------------------------------------------+ + | **column2** | 11121 | + +-------------+------------------------------------------------------------------------+ + | **column3** | 1.0007824726134585 | + +-------------+------------------------------------------------------------------------+ + | **column4** | 5.1111 | + +-------------+------------------------------------------------------------------------+ + | **column5** | xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx | + +-------------+------------------------------------------------------------------------+ + | **RECORD 3** | + +-------------+------------------------------------------------------------------------+ + | **column1** | donkey, cow, horse, tit, | + | | eagle, whale, | + | | aligator, | + | | pelican, | + | | grasshoper | + | | pig | + | | bat | + +-------------+------------------------------------------------------------------------+ + | **column2** | 14351 | + +-------------+------------------------------------------------------------------------+ + | **column3** | 50.3877551020408163 | + +-------------+------------------------------------------------------------------------+ + | **column4** | 345.11 | + +-------------+------------------------------------------------------------------------+ + | **column5** | yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy yyyyyy | + +-------------+------------------------------------------------------------------------+ + + deallocate q; diff --git a/src/test/regress/sql/psql.sqindex 8f8e17a87c..a87578b8b6 100644 *** a/src/test/regress/sql/psql.sql --- b/src/test/regress/sql/psql.sql *************** *** 404,406 **** begin --- 404,423 ---- raise notice 'foo'; raise exception 'bar'; end $$; + + prepare q AS VALUES(E'Elephant, kangaroo,\nsquirrel, gorilla', 121, + (279./278.)::text, 0.1111, repeat('Hello ', 10)) + , (E'goat, rhinoceros,\nmonkey, ape', 11121, (1279./1278.)::text, 5.1111, + repeat('xxxxxx ', 10)) + , (E'donkey, cow, horse, tit,\neagle, whale,\naligator, + pelican,\ngrasshoper\npig\n\tbat', 14351, (12345./245.)::text, 345.11, + repeat('yyyyyy ', 10)); + + \pset format rst + execute q; + \pset format markdown + execute q; + \pset format rst + \x + execute q; + deallocate q;