On Tue, Oct 06, 2009 at 10:44:27AM +0100, Roger Leigh wrote: > On Mon, Oct 05, 2009 at 04:32:08PM -0400, Tom Lane wrote: > > Roger Leigh <rle...@codelibre.net> writes: > > > On Sun, Oct 04, 2009 at 11:22:27PM +0300, Peter Eisentraut wrote: > > >> Elsewhere in the psql code, notably in mbprint.c, we make the decision > > >> on whether to apply certain Unicode-aware processing based on whether > > >> the client encoding is UTF8. The same should be done here. > > >> > > >> There is a patch somewhere in the pipeline that would automatically set > > >> the psql client encoding to whatever the locale says, but until that is > > >> done, the client encoding should be the sole setting that rules what > > >> kind of character set processing is done on the client side. > > > > > OK, that makes sense to a certain extent. However, the characters > > > used to draw the table lines are not really that related to the > > > client encoding for data sent from the database (IMHO). > > > > Huh? The data *in* the table is going to be in the client_encoding, and > > psql contains no mechanisms that would translate it to something else. > > Surrounding it with decoration in a different encoding is just a recipe > > for breakage. > > Ah, I was under the mistaken assumption that this was iconv()ed or > otherwise translated for correct display. In that case, I'll leave > the patch as is (using the client encoding for table lines). > > I've attached an updated copy of the patch (it just removes the > now unneeded langinfo.h header).
This patch included a bit of code not intended for inclusion (setting of client encoding based on locale), which the attached (and hopefully final!) revision of the patch excludes. Regards, Roger -- .''`. Roger Leigh : :' : Debian GNU/Linux http://people.debian.org/~rleigh/ `. `' Printing on GNU/Linux? http://gutenprint.sourceforge.net/ `- GPG Public Key: 0x25BFB848 Please GPG sign your mail.
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 85e9375..cd1c137 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1690,6 +1690,54 @@ lo_import 152801 </varlistentry> <varlistentry> + <term><literal>linestyle</literal></term> + <listitem> + <para> + Sets the line drawing style of text table output to one + of <literal>ascii</literal>, or <literal>utf8</literal>. + Unique abbreviations are allowed. (That would mean one + letter is enough.) <literal>utf8</literal> will be selected + by default if supported by your locale, + otherwise <literal>ascii</literal> will be used. + </para> + + <para> + Query result tables are displayed as text for some output + formats (<literal>unaligned</literal>, + <literal>aligned</literal>, and <literal>wrapped</literal> + formats). The tables are drawn using characters of the + user's locale character set. By + default, <acronym>ASCII</acronym> characters will be used, + which will display correctly in all locales. However, if + the user is using a locale with a <acronym>UTF-8</acronym> + character set, the default will be to + use <acronym>UTF-8</acronym> box drawing characters in place + of ASCII punctuation to display more readable tables. + </para> + + <para> + This option is useful for overriding the default line + style, for example to force the use of + only <acronym>ASCII</acronym> characters when extended + character sets such as <acronym>UTF-8</acronym> are + inappropriate. This might be the case if preserving output + compatibility with older psql versions is important (prior + to 8.5.0). + </para> + + <para> + <quote>UTF8</quote> use Unicode <acronym>UTF-8</acronym> box + drawing characters. + </para> + + <para> + <quote>ASCII</quote> use plain <acronym>ASCII</acronym> characters. + </para> + + </listitem> + </varlistentry> + + <varlistentry> <term><literal>columns</literal></term> <listitem> <para> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index da57eb4..223f11c 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -46,6 +46,7 @@ #include "input.h" #include "large_obj.h" #include "mainloop.h" +#include "mbprint.h" #include "print.h" #include "psqlscan.h" #include "settings.h" @@ -596,6 +597,14 @@ exec_command(const char *cmd, /* save encoding info into psql internal data */ pset.encoding = PQclientEncoding(pset.db); pset.popt.topt.encoding = pset.encoding; + if (!pset.popt.topt.line_style_set) + { + if (pset.encoding == get_utf8_id()) + pset.popt.topt.line_style = &utf8format; + else + pset.popt.topt.line_style = &asciiformat; + } + SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); } @@ -1432,6 +1441,13 @@ SyncVariables(void) /* get stuff from connection */ pset.encoding = PQclientEncoding(pset.db); pset.popt.topt.encoding = pset.encoding; + if (!pset.popt.topt.line_style_set) + { + if (pset.encoding == get_utf8_id()) + pset.popt.topt.line_style = &utf8format; + else + pset.popt.topt.line_style = &asciiformat; + } pset.sversion = PQserverVersion(pset.db); SetVariable(pset.vars, "DBNAME", PQdb(pset.db)); @@ -1772,6 +1788,27 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) printf(_("Output format is %s.\n"), _align2string(popt->topt.format)); } + /* set table line style */ + else if (strcmp(param, "linestyle") == 0) + { + if (!value) + ; + else if (pg_strncasecmp("ascii", value, vallen) == 0) + popt->topt.line_style = &asciiformat; + else if (pg_strncasecmp("utf8", value, vallen) == 0) + popt->topt.line_style = &utf8format; + else + { + psql_error("\\pset: allowed table styles are ascii, utf8\n"); + return false; + } + + popt->topt.line_style_set = true; + + if (!quiet) + printf(_("Table style is %s.\n"), popt->topt.line_style->name); + } + /* set border style/width */ else if (strcmp(param, "border") == 0) { diff --git a/src/bin/psql/mbprint.c b/src/bin/psql/mbprint.c index 339ee0c..8cb1905 100644 --- a/src/bin/psql/mbprint.c +++ b/src/bin/psql/mbprint.c @@ -30,7 +30,7 @@ typedef unsigned int pg_wchar; -static int +int get_utf8_id(void) { static int utf8_id = -1; diff --git a/src/bin/psql/mbprint.h b/src/bin/psql/mbprint.h index 83792e2..25e6018 100644 --- a/src/bin/psql/mbprint.h +++ b/src/bin/psql/mbprint.h @@ -9,6 +9,9 @@ struct lineptr int width; }; +extern int +get_utf8_id(void); + extern unsigned char *mbvalidate(unsigned char *pwcs, int encoding); extern int pg_wcswidth(const unsigned char *pwcs, size_t len, int encoding); diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 7505cd4..7b108c4 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -356,38 +356,77 @@ print_unaligned_vertical(const printTableContent *cont, FILE *fout) /* Aligned text */ /********************/ +const printTextFormat asciiformat = +{ + "ascii", + { + { "-", "+", "+", "+" }, + { "-", "+", "+", "+" }, + { "-", "+", "+", "+" }, + { "", "|", "|", "|" } + }, + ":", + ";", + " " +}; + +const struct printTextFormat utf8format = +{ + "utf8", + { + /* ─, ┌, ┬, ┐ */ + { "\342\224\200", "\342\224\214", "\342\224\254", "\342\224\220" }, + /* ─, ├, ┼, ┤ */ + { "\342\224\200", "\342\224\234", "\342\224\274", "\342\224\244" }, + /* ─, └, ┴, ┘ */ + { "\342\224\200", "\342\224\224", "\342\224\264", "\342\224\230" }, + /* N/A, │, │, │ */ + { "", "\342\224\202", "\342\224\202", "\342\224\202" } + }, + /* ╎ */ + "\342\225\216", + /* ┊ */ + "\342\224\212", + /* ╷ */ + "\342\225\267" +}; /* draw "line" */ static void _print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths, - unsigned short border, FILE *fout) + unsigned short border, printTextRule pos, + const printTextFormat *format, + FILE *fout) { unsigned int i, j; + const printTextLineFormat *lformat = &format->lrule[pos]; + if (border == 1) - fputc('-', fout); + fputs(lformat->hrule, fout); else if (border == 2) - fputs("+-", fout); + fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); for (i = 0; i < ncolumns; i++) { for (j = 0; j < widths[i]; j++) - fputc('-', fout); + fputs(lformat->hrule, fout); if (i < ncolumns - 1) { if (border == 0) fputc(' ', fout); else - fputs("-+-", fout); + fprintf(fout, "%s%s%s", lformat->hrule, + lformat->midvrule, lformat->hrule); } } if (border == 2) - fputs("-+", fout); + fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); else if (border == 1) - fputc('-', fout); + fputs(lformat->hrule, fout); fputc('\n', fout); } @@ -403,6 +442,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) bool opt_numeric_locale = cont->opt->numericLocale; int encoding = cont->opt->encoding; unsigned short opt_border = cont->opt->border; + const printTextFormat *format = cont->opt->line_style; unsigned int col_count = 0, cell_count = 0; @@ -431,6 +471,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) int *bytes_output; /* Bytes output for column value */ int output_columns = 0; /* Width of interactive console */ bool is_pager = false; + const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA]; if (cancel_pressed) return; @@ -709,7 +750,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) int curr_nl_line; if (opt_border == 2) - _print_horizontal_line(col_count, width_wrap, opt_border, fout); + _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_TOP, format, fout); for (i = 0; i < col_count; i++) pg_wcsformat((unsigned char *) cont->headers[i], @@ -722,7 +763,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) while (more_col_wrapping) { if (opt_border == 2) - fprintf(fout, "|%c", curr_nl_line ? '+' : ' '); + fprintf(fout, "%s%c", dformat->leftvrule, curr_nl_line ? '+' : ' '); else if (opt_border == 1) fputc(curr_nl_line ? '+' : ' ', fout); @@ -753,19 +794,20 @@ print_aligned_text(const printTableContent *cont, FILE *fout) if (opt_border == 0) fputc(curr_nl_line ? '+' : ' ', fout); else - fprintf(fout, " |%c", curr_nl_line ? '+' : ' '); + fprintf(fout, " %s%c", dformat->midvrule, curr_nl_line ? '+' : ' '); } } curr_nl_line++; if (opt_border == 2) - fputs(" |", fout); + fprintf(fout, " %s", + dformat->rightvrule); else if (opt_border == 1) fputc(' ', fout); fputc('\n', fout); } - _print_horizontal_line(col_count, width_wrap, opt_border, fout); + _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_MIDDLE, format, fout); } } @@ -811,7 +853,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) /* left border */ if (opt_border == 2) - fputs("| ", fout); + fprintf(fout, "%s ", dformat->leftvrule); else if (opt_border == 1) fputc(' ', fout); @@ -884,22 +926,26 @@ print_aligned_text(const printTableContent *cont, FILE *fout) fputc(' ', fout); /* Next value is beyond past newlines? */ else if (col_lineptrs[j + 1][curr_nl_line[j + 1]].ptr == NULL) - fputs(" ", fout); + fprintf(fout, " %s ", + format->midvrule_blank); /* In wrapping of value? */ else if (bytes_output[j + 1] != 0) - fputs(" ; ", fout); + fprintf(fout, " %s ", + format->midvrule_wrap); /* After first newline value */ else if (curr_nl_line[j + 1] != 0) - fputs(" : ", fout); + fprintf(fout, " %s ", + format->midvrule_cont); + /* Ordinary line */ else - /* Ordinary line */ - fputs(" | ", fout); + fprintf(fout, " %s ", + dformat->midvrule); } } /* end-of-row border */ if (opt_border == 2) - fputs(" |", fout); + fprintf(fout, " %s", dformat->rightvrule); fputc('\n', fout); } while (more_lines); @@ -908,7 +954,7 @@ 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, fout); + _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_BOTTOM, format, fout); /* print footers */ if (cont->footers && !opt_tuples_only && !cancel_pressed) @@ -941,6 +987,62 @@ print_aligned_text(const printTableContent *cont, FILE *fout) ClosePager(fout); } +static inline void +print_aligned_vertical_line(const printTableContent *cont, + unsigned long record, + unsigned int hwidth, + unsigned int dwidth, + printTextRule pos, + FILE *fout) +{ + unsigned short opt_border = cont->opt->border; + unsigned int i; + int reclen = 0; + const printTextFormat *format = cont->opt->line_style; + const printTextLineFormat *lformat = &format->lrule[pos]; + + if (opt_border == 2) + fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); + else if (opt_border == 1) + fputs(lformat->hrule, fout); + + if (record) + { + if (opt_border == 0) + reclen = fprintf(fout, "* Record %lu", record); + else + reclen = fprintf(fout, "[ RECORD %lu ]", record); + } + if (opt_border != 2) + reclen++; + if (reclen < 0) + reclen = 0; + for (i = reclen; i < hwidth; i++) + fputs(opt_border > 0 ? lformat->hrule : " ", fout); + reclen -= hwidth; + + if (opt_border > 0) + { + if (reclen-- <= 0) + fputs(lformat->hrule, fout); + if (reclen-- <= 0) + fputs(lformat->midvrule, fout); + if (reclen-- <= 0) + fputs(lformat->hrule, fout); + } + else + { + if (reclen-- <= 0) + fputc(' ', fout); + } + if (reclen < 0) + reclen = 0; + for (i = reclen; i < dwidth; i++) + fputs(opt_border > 0 ? lformat->hrule : " ", fout); + if (opt_border == 2) + fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); + fputc('\n', fout); +} static void print_aligned_vertical(const printTableContent *cont, FILE *fout) @@ -948,6 +1050,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) bool opt_tuples_only = cont->opt->tuples_only; bool opt_numeric_locale = cont->opt->numericLocale; unsigned short opt_border = cont->opt->border; + const printTextFormat *format = cont->opt->line_style; int encoding = cont->opt->encoding; unsigned long record = cont->opt->prior_records + 1; const char *const * ptr; @@ -958,9 +1061,9 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) dheight = 1, hformatsize = 0, dformatsize = 0; - char *divider; struct lineptr *hlineptr, *dlineptr; + const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA]; if (cancel_pressed) return; @@ -1026,21 +1129,6 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) dlineptr->ptr = pg_local_malloc(dformatsize); hlineptr->ptr = pg_local_malloc(hformatsize); - /* make horizontal border */ - divider = pg_local_malloc(hwidth + dwidth + 10); - divider[0] = '\0'; - if (opt_border == 2) - strcat(divider, "+-"); - for (i = 0; i < hwidth; i++) - strcat(divider, opt_border > 0 ? "-" : " "); - if (opt_border > 0) - strcat(divider, "-+-"); - else - strcat(divider, " "); - for (i = 0; i < dwidth; i++) - strcat(divider, opt_border > 0 ? "-" : " "); - if (opt_border == 2) - strcat(divider, "-+"); if (cont->opt->start_table) { @@ -1052,40 +1140,25 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) /* print records */ for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { - int line_count, - dcomplete, - hcomplete; + int line_count, + dcomplete, + hcomplete; + printTextRule pos = PRINT_RULE_MIDDLE; + if (i == 0) + pos = PRINT_RULE_TOP; + else if (!(*(ptr+1))) + pos = PRINT_RULE_BOTTOM; + + if (cancel_pressed) + break; if (i % cont->ncolumns == 0) { - if (cancel_pressed) - break; - if (!opt_tuples_only) - { - char record_str[64]; - size_t record_str_len; - - if (opt_border == 0) - snprintf(record_str, 64, "* Record %lu", record++); - else - snprintf(record_str, 64, "[ RECORD %lu ]", record++); - record_str_len = strlen(record_str); - - if (record_str_len + opt_border > strlen(divider)) - fprintf(fout, "%.*s%s\n", opt_border, divider, record_str); - else - { - char *div_copy = pg_strdup(divider); - - strncpy(div_copy + opt_border, record_str, record_str_len); - fprintf(fout, "%s\n", div_copy); - free(div_copy); - } - } + if (!opt_tuples_only) + print_aligned_vertical_line(cont, record++, hwidth, dwidth, pos, fout); else if (i != 0 || !cont->opt->start_table || opt_border == 2) - fprintf(fout, "%s\n", divider); + print_aligned_vertical_line(cont, 0, hwidth, dwidth, pos, fout); } - /* Format the header */ pg_wcsformat((unsigned char *) cont->headers[i % cont->ncolumns], strlen(cont->headers[i % cont->ncolumns]), @@ -1099,7 +1172,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) while (!dcomplete || !hcomplete) { if (opt_border == 2) - fputs("| ", fout); + fprintf(fout, "%s ", dformat->leftvrule); if (!hcomplete) { fprintf(fout, "%-s%*s", hlineptr[line_count].ptr, @@ -1112,9 +1185,13 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) fprintf(fout, "%*s", hwidth, ""); if (opt_border > 0) - fprintf(fout, " %c ", (line_count == 0) ? '|' : ':'); + { + fprintf(fout, " %s ", + (line_count == 0) ? + format->midvrule_cont : dformat->midvrule); + } else - fputs(" ", fout); + fputc(' ', fout); if (!dcomplete) { @@ -1125,8 +1202,8 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) if (opt_border < 2) fprintf(fout, "%s\n", my_cell); else - fprintf(fout, "%-s%*s |\n", my_cell, - (int) (dwidth - strlen(my_cell)), ""); + fprintf(fout, "%-s%*s %s\n", my_cell, + (int) (dwidth - strlen(my_cell)), "", dformat->rightvrule); free(my_cell); } else @@ -1134,8 +1211,8 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) if (opt_border < 2) fprintf(fout, "%s\n", dlineptr[line_count].ptr); else - fprintf(fout, "%-s%*s |\n", dlineptr[line_count].ptr, - dwidth - dlineptr[line_count].width, ""); + fprintf(fout, "%-s%*s %s\n", dlineptr[line_count].ptr, + dwidth - dlineptr[line_count].width, "", dformat->rightvrule); } if (!dlineptr[line_count + 1].ptr) @@ -1146,7 +1223,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) if (opt_border < 2) fputc('\n', fout); else - fprintf(fout, "%*s |\n", dwidth, ""); + fprintf(fout, "%*s %s\n", dwidth, "", dformat->rightvrule); } line_count++; } @@ -1155,7 +1232,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) if (cont->opt->stop_table) { if (opt_border == 2 && !cancel_pressed) - fprintf(fout, "%s\n", divider); + print_aligned_vertical_line(cont, 0, hwidth, dwidth, PRINT_RULE_BOTTOM, fout); /* print footers */ if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) @@ -1171,7 +1248,6 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) fputc('\n', fout); } - free(divider); free(hlineptr->ptr); free(dlineptr->ptr); free(hlineptr); diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h index 55122d7..dd38c5d 100644 --- a/src/bin/psql/print.h +++ b/src/bin/psql/print.h @@ -23,10 +23,36 @@ enum printFormat /* add your favourite output format here ... */ }; +typedef struct printTextLineFormat +{ + const char *hrule; + const char *leftvrule; + const char *midvrule; + const char *rightvrule; +} printTextLineFormat; + +typedef struct printTextFormat +{ + const char *name; + printTextLineFormat lrule[4]; + const char *midvrule_cont; + const char *midvrule_wrap; + const char *midvrule_blank; +} printTextFormat; + +typedef enum printTextRule +{ + PRINT_RULE_TOP, + PRINT_RULE_MIDDLE, + PRINT_RULE_BOTTOM, + PRINT_RULE_DATA +} printTextRule; typedef struct printTableOpt { enum printFormat format; /* one of the above */ + const printTextFormat *line_style; /* table line formatting */ + bool line_style_set; /* table line formatting set manually*/ bool expanded; /* expanded/vertical output (if supported by * output format) */ unsigned short int border; /* Print a border around the table. 0=none, @@ -95,6 +121,9 @@ typedef struct printQueryOpt * gettext on col i */ } printQueryOpt; +extern const printTextFormat asciiformat; +extern const printTextFormat utf8format; + extern FILE *PageOutput(int lines, unsigned short int pager); extern void ClosePager(FILE *pagerpipe); diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 429e5d9..eb855be 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -125,6 +125,10 @@ main(int argc, char *argv[]) /* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */ pset.popt.topt.format = PRINT_ALIGNED; + + /* Default table style to plain ASCII */ + pset.popt.topt.line_style = &asciiformat; + pset.popt.topt.line_style_set = false; pset.popt.topt.border = 1; pset.popt.topt.pager = 1; pset.popt.topt.start_table = true; diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 3e38b06..69a0fe5 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -2264,7 +2264,7 @@ psql_completion(char *text, int start, int end) { static const char *const my_list[] = {"format", "border", "expanded", - "null", "fieldsep", "tuples_only", "title", "tableattr", "pager", + "null", "fieldsep", "tuples_only", "title", "tableattr", "linestyle", "pager", "recordsep", NULL}; COMPLETE_WITH_LIST(my_list); diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c index cb477a6..681a82e 100644 --- a/src/test/regress/pg_regress_main.c +++ b/src/test/regress/pg_regress_main.c @@ -59,7 +59,7 @@ psql_start_test(const char *testname, add_stringlist_item(expectfiles, expectfile); snprintf(psql_cmd, sizeof(psql_cmd), - SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE, + SYSTEMQUOTE "\"%s%spsql\" -X -a -q -P linestyle=ascii -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE, psqldir ? psqldir : "", psqldir ? "/" : "", dblist->str,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers