Thread: thousands comma numeric formatting in psql
Hello, This is my first post to this list. Sorry if my english it's not so good. It's not my native language. I'm interrested to now if someone think that having a feature like 'thousands comma delimited numeric formatting' in psql it's a usefull thing. I've made a patch for psql that adds this feature, so issuing a select like this: my_database=> select 1234567.89; results in: ?column? -------------- 1,234,567.89 This feature is toggle on/off with a backslash command ('\n'): my_database=> \n Numeric formatting is off. my_database=> select 1234567.89; ?column? -------------- 1234567.89 For me, psql it's still the best client for postgres, faster and flexible than graphic ones. And having tables with many numeric columns witch contain huge numbers make difficult to read this numbers. One solution to deal with this is to use to_char function, but for complex selects against multiple tables it's not a good option. Another one is to make a custom function that works like this: select my_function(... complex subselect ...); but this seems ugly to me. By adding the '\n' switch to psql I can make any complex select and have all numeric fields in result formatted in easy readable form. I'm not an expert in postgresql, so if someone thinks there is an easier way to deal with numeric fields, please share. If my idea is considered usefull I can post the patch to this list. Best regards, Eugen.
On Tue, Jun 21, 2005 at 08:42:16 +0300, Eugen Nedelcu <eugen@sifolt.ro> wrote: > > One solution to deal with this is to use to_char function, but for > complex selects against multiple tables it's not a good option. Why not? You only have to apply it to the output expressions that need it. Note that if you output numbers like this, you also need to be able to read them back in. I don't think adding complexity for doing that is worth not having to add a few to_char calls in your select queries.
On Tue, Jun 21, 2005 at 06:59:38AM -0500, Bruno Wolff III wrote: > On Tue, Jun 21, 2005 at 08:42:16 +0300, > Eugen Nedelcu <eugen@sifolt.ro> wrote: > > > > One solution to deal with this is to use to_char function, but for > > complex selects against multiple tables it's not a good option. > > Why not? You only have to apply it to the output expressions that > need it. I think this: select * from table_with_text_and_numeric_fields; is much,much easier than: select text_field1,text_field2,to_char(numeric_field1, '99G999G999'), to_char(numeric_field2, '9G999G999G999'), text_field3, text_field4, to_char(numeric_field3, 'MI90G999D99') from table_with_text_and_numeric_fields; > > Note that if you output numbers like this, you also need to be able to > read them back in. I don't think adding complexity for doing that is > worth not having to add a few to_char calls in your select queries. > I don't know what 'read them back in' means to you. This formatting is only done when the number is output to the screen. Something like: fputs(thousands_comma_number, fout) instead of: fputs(original_number, fout) If you want to output to some file for reading back later, you could turn the feature off with the backslash switch '\n'. This is a patch for psql client and not for the backend. It's role is to output numbers to screen in easy readable form (2,345,675,454,543 is much easier to read then 2345675454543.456). I think graphical clients like pgAdmin or phppgadmin have a way to do this. I don't know this for sure but I will investigate it. Best Regards, Eugen
On Tue, Jun 21, 2005 at 04:03:43PM +0300, Eugen Nedelcu wrote: > This is a patch for psql client and not for the backend. It's role > is to output numbers to screen in easy readable form (2,345,675,454,543 > is much easier to read then 2345675454543.456). I think graphical > clients like pgAdmin or phppgadmin have a way to do this. I don't know > this for sure but I will investigate it. I think it would be much nicer if it was a backend setting. If it depended on the locale setting (LC_NUMERIC, I think) and worked for both input and output, it would be very good. -- Alvaro Herrera (<alvherre[a]surnet.cl>) "Uno puede defenderse de los ataques; contra los elogios se esta indefenso"
On Tue, Jun 21, 2005 at 16:03:43 +0300, Eugen Nedelcu <eugen@sifolt.ro> wrote: > > This is a patch for psql client and not for the backend. It's role > is to output numbers to screen in easy readable form (2,345,675,454,543 > is much easier to read then 2345675454543.456). I think graphical > clients like pgAdmin or phppgadmin have a way to do this. I don't know > this for sure but I will investigate it. I did miss that this was a psql only feature. I still don't see a great need, but it isn't going to cause a problem for copy or pg_dump that way.
Alvaro Herrera <alvherre@surnet.cl> writes: > On Tue, Jun 21, 2005 at 04:03:43PM +0300, Eugen Nedelcu wrote: >> This is a patch for psql client and not for the backend. > I think it would be much nicer if it was a backend setting. Doing this as a backend setting has been proposed and rejected before. The risk of breaking client code seems to outweigh any possible value. As a psql setting, though, it seems relatively harmless --- certainly no more dangerous than \x or the other pset formatting parameters. > If it depended on the locale setting (LC_NUMERIC, I think) Yes, I wanted to ask that too --- if the patch can cope with locales that exchange the roles of comma and period, that would make a lot of people very happy. regards, tom lane
Hello, I have included my patch attached to this mail. I have made the changes to deal with locale settings from client environment. So now you can start psql like this: (export LC_ALL=ro_RO; psql -U user db) and have numeric formatting with '.' as thousands separator and ',' as decimal point, or (export LC_ALL=en_US; psql -U user db) and have numeric formatting with ',' as thousands separator and '.' as decimal point. This formatting is default when locale is 'C' You can set any locale and numeric formatting code will take it in consideration. This patch is for version 7.3.2. The steps for install is: 1) cp thousands_comma.diff $POSTGRES_DIR/src/bin/psql 2) cd $POSTGRES_DIR/src/bin/psql 3) patch -p0 < thousands_comma.diff 4) ../../../configure && make Best Regards, Eugen
Attachment
Eugen Nedelcu wrote: > Hello, > > I have included my patch attached to this mail. > > I have made the changes to deal with locale settings from client > environment. So now you can start psql like this: > > (export LC_ALL=ro_RO; psql -U user db) > > and have numeric formatting with '.' as thousands separator and > ',' as decimal point, or > > (export LC_ALL=en_US; psql -U user db) > > and have numeric formatting with ',' as thousands separator and > '.' as decimal point. This formatting is default when locale is 'C' > > You can set any locale and numeric formatting code will take it in > consideration. > > This patch is for version 7.3.2. The steps for install is: > > 1) cp thousands_comma.diff $POSTGRES_DIR/src/bin/psql > 2) cd $POSTGRES_DIR/src/bin/psql > 3) patch -p0 < thousands_comma.diff > 4) ../../../configure && make I have heavily modified your patch and have applied it. Instead of using langinfo, I used a \pset variable numericsep. (We can talk about adding langinfo detection later.) By default, it is off, ''. If you set it to '.', the decimal marker will be ','. This also allows separators like ' ' too so numebers can appear as 100 000. I have also added documentation. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 Index: doc/src/sgml/ref/psql-ref.sgml =================================================================== RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v retrieving revision 1.145 diff -c -c -r1.145 psql-ref.sgml *** doc/src/sgml/ref/psql-ref.sgml 14 Jun 2005 02:57:38 -0000 1.145 --- doc/src/sgml/ref/psql-ref.sgml 10 Jul 2005 03:24:16 -0000 *************** *** 1493,1498 **** --- 1493,1510 ---- </varlistentry> <varlistentry> + <term><literal>numericsep</literal></term> + <listitem> + <para> + Specifies the character separator between groups of three digits + to the left of the decimal marker. The default is <literal>''</> + (none). Setting this to a period also changes the decimal marker + to a comma. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>recordsep</literal></term> <listitem> <para> Index: src/bin/psql/command.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v retrieving revision 1.146 diff -c -c -r1.146 command.c *** src/bin/psql/command.c 13 Jun 2005 06:36:22 -0000 1.146 --- src/bin/psql/command.c 10 Jul 2005 03:24:17 -0000 *************** *** 838,844 **** else if (strcmp(cmd, "x") == 0) success = do_pset("expanded", NULL, &pset.popt, quiet); - /* \z -- list table rights (equivalent to \dp) */ else if (strcmp(cmd, "z") == 0) { --- 838,843 ---- *************** *** 1421,1426 **** --- 1420,1436 ---- : _("Expanded display is off.\n")); } + else if (strcmp(param, "numericsep") == 0) + { + if (value) + { + free(popt->topt.numericSep); + popt->topt.numericSep = pg_strdup(value); + } + if (!quiet) + printf(_("Numeric separator is \"%s\".\n"), popt->topt.numericSep); + } + /* null display */ else if (strcmp(param, "null") == 0) { Index: src/bin/psql/help.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/help.c,v retrieving revision 1.103 diff -c -c -r1.103 help.c *** src/bin/psql/help.c 6 Jul 2005 03:14:48 -0000 1.103 --- src/bin/psql/help.c 10 Jul 2005 03:24:18 -0000 *************** *** 239,245 **** fprintf(output, _(" \\pset NAME [VALUE]\n" " set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" ! " recordsep|tuples_only|title|tableattr|pager})\n")); fprintf(output, _(" \\t show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n")); --- 239,245 ---- fprintf(output, _(" \\pset NAME [VALUE]\n" " set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" ! " numericsep|recordsep|tuples_only|title|tableattr|pager})\n")); fprintf(output, _(" \\t show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n")); Index: src/bin/psql/print.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.60 diff -c -c -r1.60 print.c *** src/bin/psql/print.c 14 Jun 2005 22:15:57 -0000 1.60 --- src/bin/psql/print.c 10 Jul 2005 03:24:24 -0000 *************** *** 29,48 **** #include "mbprint.h" /*************************/ /* Unaligned text */ /*************************/ static void ! print_unaligned_text(const char *title, const char *const * headers, ! const char *const * cells, const char *const * footers, ! const char *opt_fieldsep, const char *opt_recordsep, bool opt_barebones, ! FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const * ptr; bool need_recordsep = false; if (!opt_fieldsep) --- 29,135 ---- #include "mbprint.h" + static int + num_numericseps(const char *my_str) + { + int old_len, dec_len, int_len; + + if (my_str[0] == '-') + my_str++; + + old_len = strlen(my_str); + dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; + + int_len = old_len - dec_len; + if (int_len % 3 != 0) + return int_len / 3; + else + return int_len / 3 - 1; /* no leading separator */ + } + static int + len_with_numericsep(const char *my_str) + { + return strlen(my_str) + num_numericseps(my_str); + } + + static void + format_numericsep(char *my_str, char *numericsep) + { + int i, j, digits_before_sep, old_len, new_len, dec_len, int_len; + char *dec_point; + char *new_str; + char *dec_value; + + if (strcmp(numericsep, ".") != 0) + dec_point = "."; + else + dec_point = ","; + + if (my_str[0] == '-') + my_str++; + + old_len = strlen(my_str); + dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; + int_len = old_len - dec_len; + digits_before_sep = int_len % 3; + + new_len = int_len + int_len / 3 + dec_len; + if (digits_before_sep == 0) + new_len--; /* no leading separator */ + + new_str = malloc(new_len); + if (!new_str) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + + for (i=0, j=0; ; i++, j++) + { + /* hit decimal point */ + if (my_str[i] == '.') + { + new_str[j] = *dec_point; + new_str[j+1] = '\0'; + dec_value = strchr(my_str, '.'); + strcat(new_str, ++dec_value); + break; + } + + /* end of string */ + if (my_str[i] == '\0') + { + new_str[j] = '\0'; + break; + } + + /* add separator? */ + if (i != 0 && + (i - (digits_before_sep ? digits_before_sep : 3)) % 3 == 0) + new_str[j++] = *numericsep; + + new_str[j] = my_str[i]; + } + + strcpy(my_str, new_str); + free(new_str); + } + /*************************/ /* Unaligned text */ /*************************/ 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_barebones, ! char *opt_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const *ptr; bool need_recordsep = false; if (!opt_fieldsep) *************** *** 77,83 **** fputs(opt_recordsep, fout); need_recordsep = false; } ! fputs(*ptr, fout); if ((i + 1) % col_count) fputs(opt_fieldsep, fout); else --- 164,187 ---- fputs(opt_recordsep, fout); need_recordsep = false; } ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) > 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) ! { ! char *my_cell = malloc(len_with_numericsep(*ptr)); ! ! if (!my_cell) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } ! strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); ! fputs(my_cell, fout); ! free(my_cell); ! } ! else ! fputs(*ptr, fout); ! if ((i + 1) % col_count) fputs(opt_fieldsep, fout); else *************** *** 107,120 **** static void ! print_unaligned_vertical(const char *title, const char *const * headers, ! const char *const * cells, const char *const * footers, ! const char *opt_fieldsep, const char *opt_recordsep, bool opt_barebones, ! FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const * ptr; if (!opt_fieldsep) opt_fieldsep = ""; --- 211,225 ---- 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_barebones, char *opt_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const *ptr; if (!opt_fieldsep) opt_fieldsep = ""; *************** *** 141,147 **** fputs(headers[i % col_count], fout); fputs(opt_fieldsep, fout); ! fputs(*ptr, fout); } /* print footers */ --- 246,268 ---- fputs(headers[i % col_count], fout); fputs(opt_fieldsep, fout); ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) ! { ! char *my_cell = malloc(len_with_numericsep(*ptr)); ! ! if (!my_cell) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } ! strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); ! fputs(my_cell, fout); ! free(my_cell); ! } ! else ! fputs(*ptr, fout); } /* print footers */ *************** *** 202,210 **** 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_barebones, unsigned short int opt_border, int encoding, FILE *fout) { --- 323,331 ---- 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_barebones, char *opt_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { *************** *** 216,222 **** tmp; unsigned int *widths, total_w; ! const char *const * ptr; /* count columns */ for (ptr = headers; *ptr; ptr++) --- 337,343 ---- tmp; unsigned int *widths, total_w; ! const char *const *ptr; /* count columns */ for (ptr = headers; *ptr; ptr++) *************** *** 271,277 **** for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding); if (tmp > widths[i % col_count]) widths[i % col_count] = tmp; cell_w[i] = tmp; --- 392,406 ---- for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numericseps; ! ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) ! numericseps = num_numericseps(*ptr); ! else ! numericseps = 0; ! ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numericseps; if (tmp > widths[i % col_count]) widths[i % col_count] = tmp; cell_w[i] = tmp; *************** *** 351,358 **** /* content */ if (opt_align[i % col_count] == 'r') { ! fprintf(fout, "%*s%s", ! widths[i % col_count] - cell_w[i], "", cells[i]); } else { --- 480,501 ---- /* content */ if (opt_align[i % col_count] == 'r') { ! if (strlen(*ptr) > 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) ! { ! char *my_cell = malloc(cell_w[i]); ! ! if (!my_cell) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } ! strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); ! fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); ! free(my_cell); ! } ! else ! fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", *ptr); } else { *************** *** 406,419 **** static void ! print_aligned_vertical(const char *title, const char *const * headers, ! const char *const * cells, const char *const * footers, ! bool opt_barebones, unsigned short int opt_border, int encoding, FILE *fout) { unsigned int col_count = 0; unsigned int record = 1; ! const char *const * ptr; unsigned int i, tmp = 0, hwidth = 0, --- 549,563 ---- 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_barebones, ! char *opt_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { unsigned int col_count = 0; unsigned int record = 1; ! const char *const *ptr; unsigned int i, tmp = 0, hwidth = 0, *************** *** 471,477 **** /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding); if (tmp > dwidth) dwidth = tmp; cell_w[i] = tmp; --- 615,629 ---- /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numericseps; ! ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) ! numericseps = num_numericseps(*ptr); ! else ! numericseps = 0; ! ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numericseps; if (tmp > dwidth) dwidth = tmp; cell_w[i] = tmp; *************** *** 556,565 **** else fputs(" ", fout); ! if (opt_border < 2) ! fprintf(fout, "%s\n", *ptr); ! else ! fprintf(fout, "%-s%*s |\n", *ptr, dwidth - cell_w[i], ""); } if (opt_border == 2) --- 708,731 ---- else fputs(" ", fout); ! { ! char *my_cell = malloc(cell_w[i]); ! ! if (!my_cell) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } ! strcpy(my_cell, *ptr); ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) ! format_numericsep(my_cell, opt_numericsep); ! if (opt_border < 2) ! puts(my_cell); ! else ! fprintf(fout, "%-s%*s |\n", my_cell, dwidth - cell_w[i], ""); ! free(my_cell); ! } } if (opt_border == 2) *************** *** 637,651 **** 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_barebones, unsigned short int opt_border, ! const char *opt_table_attr, ! FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const * ptr; fprintf(fout, "<table border=\"%d\"", opt_border); if (opt_table_attr) --- 803,817 ---- 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_barebones, ! char *opt_numericsep, unsigned short int opt_border, ! const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const *ptr; fprintf(fout, "<table border=\"%d\"", opt_border); if (opt_table_attr) *************** *** 683,693 **** fputs(" <tr valign=\"top\">\n", fout); fprintf(fout, " <td align=\"%s\">", opt_align[(i) % col_count] == 'r' ? "right" : "left"); ! if ((*ptr)[strspn(*ptr, " \t")] == '\0') /* is string only ! * whitespace? */ fputs(" ", fout); else html_escaped_print(*ptr, fout); fputs("</td>\n", fout); if ((i + 1) % col_count == 0) --- 849,875 ---- fputs(" <tr valign=\"top\">\n", fout); fprintf(fout, " <td align=\"%s\">", opt_align[(i) % col_count] == 'r' ? "right" : "left"); ! /* is string only whitespace? */ ! if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); + else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && + opt_numericsep != NULL && strlen(opt_numericsep) > 0) + { + char *my_cell = malloc(len_with_numericsep(*ptr)); + + if (!my_cell) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + strcpy(my_cell, *ptr); + format_numericsep(my_cell, opt_numericsep); + html_escaped_print(my_cell, fout); + free(my_cell); + } else html_escaped_print(*ptr, fout); + fputs("</td>\n", fout); if ((i + 1) % col_count == 0) *************** *** 714,729 **** 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_barebones, unsigned short int opt_border, ! const char *opt_table_attr, ! FILE *fout) { unsigned int col_count = 0; unsigned int i; unsigned int record = 1; ! const char *const * ptr; fprintf(fout, "<table border=\"%d\"", opt_border); if (opt_table_attr) --- 896,911 ---- 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_barebones, ! char *opt_numericsep, unsigned short int opt_border, ! const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; unsigned int i; unsigned int record = 1; ! const char *const *ptr; fprintf(fout, "<table border=\"%d\"", opt_border); if (opt_table_attr) *************** *** 758,768 **** fputs("</th>\n", fout); fprintf(fout, " <td align=\"%s\">", opt_align[i % col_count] == 'r' ? "right" : "left"); ! if ((*ptr)[strspn(*ptr, " \t")] == '\0') /* is string only ! * whitespace? */ fputs(" ", fout); else html_escaped_print(*ptr, fout); fputs("</td>\n </tr>\n", fout); } --- 940,966 ---- fputs("</th>\n", fout); fprintf(fout, " <td align=\"%s\">", opt_align[i % col_count] == 'r' ? "right" : "left"); ! /* is string only whitespace? */ ! if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); + else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && + opt_numericsep != NULL && strlen(opt_numericsep) > 0) + { + char *my_cell = malloc(len_with_numericsep(*ptr)); + + if (!my_cell) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + strcpy(my_cell, *ptr); + format_numericsep(my_cell, opt_numericsep); + html_escaped_print(my_cell, fout); + free(my_cell); + } else html_escaped_print(*ptr, fout); + fputs("</td>\n </tr>\n", fout); } *************** *** 829,842 **** 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_barebones, unsigned short int opt_border, ! FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const * ptr; /* print title */ --- 1027,1040 ---- 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_barebones, ! unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const *ptr; /* print title */ *************** *** 921,934 **** 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_barebones, unsigned short int opt_border, ! FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const * ptr; unsigned int record = 1; (void) opt_align; /* currently unused parameter */ --- 1119,1132 ---- 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_barebones, ! unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const *ptr; unsigned int record = 1; (void) opt_align; /* currently unused parameter */ *************** *** 1027,1040 **** 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_barebones, unsigned short int opt_border, ! FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const * ptr; /* print title */ --- 1225,1238 ---- 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_barebones, ! unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const *ptr; /* print title */ *************** *** 1111,1124 **** 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_barebones, unsigned short int opt_border, ! FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const * ptr; unsigned int record = 1; unsigned short current_format = 0; /* 0=none, 1=header, 2=body */ --- 1309,1322 ---- 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_barebones, ! unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; unsigned int i; ! const char *const *ptr; unsigned int record = 1; unsigned short current_format = 0; /* 0=none, 1=header, 2=body */ *************** *** 1263,1271 **** void printTable(const char *title, ! const char *const * headers, ! const char *const * cells, ! const char *const * footers, const char *align, const printTableOpt *opt, FILE *fout, FILE *flog) { --- 1461,1469 ---- void printTable(const char *title, ! const char *const *headers, ! const char *const *cells, ! const char *const *footers, const char *align, const printTableOpt *opt, FILE *fout, FILE *flog) { *************** *** 1298,1304 **** int col_count = 0, row_count = 0, lines; ! const char *const * ptr; /* rough estimate of columns and rows */ if (headers) --- 1496,1502 ---- int col_count = 0, row_count = 0, lines; ! const char *const *ptr; /* rough estimate of columns and rows */ if (headers) *************** *** 1325,1362 **** /* print the stuff */ if (flog) ! print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, border, opt->encoding, flog); switch (opt->format) { case PRINT_UNALIGNED: if (use_expanded) ! print_unaligned_vertical(title, headers, cells, footers, opt->fieldSep, opt->recordSep, ! opt->tuples_only, output); else ! print_unaligned_text(title, headers, cells, footers, opt->fieldSep, opt->recordSep, ! opt->tuples_only, output); break; case PRINT_ALIGNED: if (use_expanded) ! print_aligned_vertical(title, headers, cells, footers, ! opt->tuples_only, border, opt->encoding, output); else ! print_aligned_text(title, headers, cells, footers, ! align, opt->tuples_only, border, opt->encoding, output); break; case PRINT_HTML: if (use_expanded) ! print_html_vertical(title, headers, cells, footers, ! align, opt->tuples_only, border, opt->tableAttr, output); else print_html_text(title, headers, cells, footers, ! align, opt->tuples_only, border, opt->tableAttr, output); break; case PRINT_LATEX: --- 1523,1560 ---- /* print the stuff */ if (flog) ! print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, opt->numericSep, border, opt->encoding,flog); switch (opt->format) { case PRINT_UNALIGNED: if (use_expanded) ! print_unaligned_vertical(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericSep, output); else ! print_unaligned_text(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericSep, output); break; case PRINT_ALIGNED: if (use_expanded) ! print_aligned_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->encoding, output); else ! print_aligned_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->encoding, output); break; case PRINT_HTML: if (use_expanded) ! print_html_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->tableAttr, output); else print_html_text(title, headers, cells, footers, ! align, opt->tuples_only, opt->numericSep, border, opt->tableAttr, output); break; case PRINT_LATEX: Index: src/bin/psql/print.h =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v retrieving revision 1.25 diff -c -c -r1.25 print.h *** src/bin/psql/print.h 14 Jun 2005 02:57:41 -0000 1.25 --- src/bin/psql/print.h 10 Jul 2005 03:24:24 -0000 *************** *** 40,45 **** --- 40,46 ---- char *fieldSep; /* field separator for unaligned text mode */ char *recordSep; /* record separator for unaligned text * mode */ + char *numericSep; /* numeric units separator */ char *tableAttr; /* attributes for HTML <table ...> */ int encoding; /* character encoding */ bool normal_query; /* are we presenting the results of a
Bruce Momjian wrote: > I have heavily modified your patch and have applied it. Instead of > using langinfo, I used a \pset variable numericsep. Why? -- Peter Eisentraut http://developer.postgresql.org/~petere/
Peter Eisentraut wrote: > Bruce Momjian wrote: > > I have heavily modified your patch and have applied it. Instead of > > using langinfo, I used a \pset variable numericsep. > > Why? Because I don't have langinfo on my system, so I can't test it, nor add configure code for it. It also prevents us from using space as the separator, which is the international standard. If you think we should use langinfo, we can discuss it, but at this point, it is not used. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Thanks, fixed, and applied. I also centralized the malloc into a function. We could use pg_malloc, but that doesn't export out to /scripts, where this is shared. We will fix that some day, but not right now. --------------------------------------------------------------------------- Eugen Nedelcu wrote: > On Mon, Jul 11, 2005 at 11:37:17PM -0400, Bruce Momjian wrote: > > > > Did you do a 'make clean' before testing? I can't reproduce any failure > > here. Can you supply a number that is failing, like this: > > > > SELECT 1000000; > > > > --------------------------------------------------------------------------- > > > > The bug is in the following code: > > new_str = malloc(new_len) and every one of > char *my_cell = malloc(....) > > Ring a bell? > > The corect form is: > > new_str = malloc(new_len + 1), > char *my_cell = malloc(.... + 1) > > because of the null character that must terminate the string (\0) > > The error apears of course randomly (more probably for a query which > returns many rows) > > Regarding locale aproach, it is trivial to replace langinfo with > localeconv: > > struct lconv *l = localeconv(); > char *dec_point = l->decimal_point; > > instead of: > > #include langinfo.h > char *dec_point = nl_langinfo(__DECIMAL_POINT); > > I used langinfo because in linux libc it is considered > "The Elegant and Fast Way" of using locale and conforms with > X/Open portability guide that every modern Unix follows > (Solaris, Linux, latest FreeBSD). > > Regarding locale vs. \pset numericsep, for me it doesn't matter > which one is used. I consider the patch very usefull, and I think > that other guys that use psql daily for working with financial data > will appreciate it too. > > With a quick setting like \pset numericsep ',' all my numeric fields > will appear in easy readable form. I must underline that to_char is > a backend function and we talk here about a little psql feature which > makes life a little easier (for me at least). > -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 Index: src/bin/psql/print.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.63 diff -c -c -r1.63 print.c *** src/bin/psql/print.c 10 Jul 2005 15:53:42 -0000 1.63 --- src/bin/psql/print.c 14 Jul 2005 06:44:10 -0000 *************** *** 29,34 **** --- 29,48 ---- #include "mbprint.h" + static void * + pg_local_malloc(size_t size) + { + void *tmp; + + tmp = malloc(size); + if (!tmp) + { + psql_error("out of memory\n"); + exit(EXIT_FAILURE); + } + return tmp; + } + static int num_numericseps(const char *my_str) { *************** *** 46,51 **** --- 60,66 ---- else return int_len / 3 - 1; /* no leading separator */ } + static int len_with_numericsep(const char *my_str) { *************** *** 77,88 **** if (digits_before_sep == 0) new_len--; /* no leading separator */ ! new_str = malloc(new_len); ! if (!new_str) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } for (i=0, j=0; ; i++, j++) { --- 92,98 ---- if (digits_before_sep == 0) new_len--; /* no leading separator */ ! new_str = pg_local_malloc(new_len + 1); for (i=0, j=0; ; i++, j++) { *************** *** 167,179 **** if ((opt_align[i % col_count] == 'r') && strlen(*ptr) > 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = malloc(len_with_numericsep(*ptr)); - if (!my_cell) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); fputs(my_cell, fout); --- 177,184 ---- if ((opt_align[i % col_count] == 'r') && strlen(*ptr) > 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); fputs(my_cell, fout); *************** *** 249,261 **** if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = malloc(len_with_numericsep(*ptr)); - if (!my_cell) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); fputs(my_cell, fout); --- 254,261 ---- if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); fputs(my_cell, fout); *************** *** 482,494 **** { if (strlen(*ptr) > 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = malloc(cell_w[i]); - if (!my_cell) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); --- 482,489 ---- { if (strlen(*ptr) > 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); *************** *** 634,645 **** fprintf(fout, "%s\n", title); /* make horizontal border */ ! divider = malloc(hwidth + dwidth + 10); ! if (!divider) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } divider[0] = '\0'; if (opt_border == 2) strcat(divider, "+-"); --- 629,635 ---- fprintf(fout, "%s\n", title); /* make horizontal border */ ! divider = pg_local_malloc(hwidth + dwidth + 10); divider[0] = '\0'; if (opt_border == 2) strcat(divider, "+-"); *************** *** 661,675 **** { if (!opt_barebones) { ! char *record_str = malloc(32); size_t record_str_len; - if (!record_str) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } - if (opt_border == 0) snprintf(record_str, 32, "* Record %d", record++); else --- 651,659 ---- { if (!opt_barebones) { ! char *record_str = pg_local_malloc(32); size_t record_str_len; if (opt_border == 0) snprintf(record_str, 32, "* Record %d", record++); else *************** *** 709,721 **** fputs(" ", fout); { ! char *my_cell = malloc(cell_w[i]); - if (!my_cell) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } strcpy(my_cell, *ptr); if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) --- 693,700 ---- fputs(" ", fout); { ! char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) *************** *** 855,867 **** else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = malloc(len_with_numericsep(*ptr)); - if (!my_cell) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); html_escaped_print(my_cell, fout); --- 834,841 ---- else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); html_escaped_print(my_cell, fout); *************** *** 946,958 **** else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = malloc(len_with_numericsep(*ptr)); - if (!my_cell) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); html_escaped_print(my_cell, fout); --- 920,927 ---- else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); format_numericsep(my_cell, opt_numericsep); html_escaped_print(my_cell, fout); *************** *** 1646,1657 **** exit(EXIT_FAILURE); } ! footers[0] = malloc(100); ! if (!footers[0]) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } if (PQntuples(result) == 1) snprintf(footers[0], 100, _("(1 row)")); else --- 1615,1621 ---- exit(EXIT_FAILURE); } ! footers[0] = pg_local_malloc(100); if (PQntuples(result) == 1) snprintf(footers[0], 100, _("(1 row)")); else
Eugen Nedelcu wrote: > Regarding locale aproach, it is trivial to replace langinfo with > localeconv: > > struct lconv *l = localeconv(); > char *dec_point = l->decimal_point; > > instead of: > > #include langinfo.h > char *dec_point = nl_langinfo(__DECIMAL_POINT); > > I used langinfo because in linux libc it is considered > "The Elegant and Fast Way" of using locale and conforms with > X/Open portability guide that every modern Unix follows > (Solaris, Linux, latest FreeBSD). > > Regarding locale vs. \pset numericsep, for me it doesn't matter > which one is used. I consider the patch very usefull, and I think > that other guys that use psql daily for working with financial data > will appreciate it too. > > With a quick setting like \pset numericsep ',' all my numeric fields > will appear in easy readable form. I must underline that to_char is > a backend function and we talk here about a little psql feature which > makes life a little easier (for me at least). OK, I have applied the following patch to make numerisep a boolean, made it locale-aware, set defaults if the locale doesn't return meaningful values, added code to handle locale-reported groupings, and updated the documentation. The only question I have is whether those locale values are single-byte strings in all locals, or could they be multi-byte or multi-character, in which case I have to treat them as strings. For now, I added a code comment that we treat them as single-byte strings. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 Index: doc/src/sgml/ref/psql-ref.sgml =================================================================== RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v retrieving revision 1.146 retrieving revision 1.147 diff -c -r1.146 -r1.147 *** doc/src/sgml/ref/psql-ref.sgml 10 Jul 2005 03:46:12 -0000 1.146 --- doc/src/sgml/ref/psql-ref.sgml 14 Jul 2005 08:42:36 -0000 1.147 *************** *** 1,5 **** <!-- ! $PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.146 2005/07/10 03:46:12 momjian Exp $ PostgreSQL documentation --> --- 1,5 ---- <!-- ! $PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.147 2005/07/14 08:42:36 momjian Exp $ PostgreSQL documentation --> *************** *** 1496,1505 **** <term><literal>numericsep</literal></term> <listitem> <para> ! Specifies the character separator between groups of three digits ! to the left of the decimal marker. The default is <literal>''</> ! (none). Setting this to a period also changes the decimal marker ! to a comma. </para> </listitem> </varlistentry> --- 1496,1504 ---- <term><literal>numericsep</literal></term> <listitem> <para> ! Toggles the display of a locale-aware character to separate groups ! of digits to the left of the decimal marker. It also enables ! a locale-aware decimal marker. </para> </listitem> </varlistentry> Index: src/bin/psql/command.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v retrieving revision 1.148 retrieving revision 1.149 diff -c -r1.148 -r1.149 *** src/bin/psql/command.c 14 Jul 2005 06:49:58 -0000 1.148 --- src/bin/psql/command.c 14 Jul 2005 08:42:37 -0000 1.149 *************** *** 3,9 **** * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.148 2005/07/14 06:49:58 momjian Exp $ */ #include "postgres_fe.h" #include "command.h" --- 3,9 ---- * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.149 2005/07/14 08:42:37 momjian Exp $ */ #include "postgres_fe.h" #include "command.h" *************** *** 1420,1434 **** : _("Expanded display is off.\n")); } else if (strcmp(param, "numericsep") == 0) { ! if (value) { ! free(popt->topt.numericSep); ! popt->topt.numericSep = pg_strdup(value); } - if (!quiet) - printf(_("Numeric separator is \"%s\".\n"), popt->topt.numericSep); } /* null display */ --- 1420,1436 ---- : _("Expanded display is off.\n")); } + /* numeric separators */ else if (strcmp(param, "numericsep") == 0) { ! popt->topt.numericSep = !popt->topt.numericSep; ! if (!quiet) { ! if (popt->topt.numericSep) ! puts(_("Showing numeric separators.")); ! else ! puts(_("Numeric separators are off.")); } } /* null display */ Index: src/bin/psql/print.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.66 retrieving revision 1.67 diff -c -r1.66 -r1.67 *** src/bin/psql/print.c 14 Jul 2005 07:32:01 -0000 1.66 --- src/bin/psql/print.c 14 Jul 2005 08:42:37 -0000 1.67 *************** *** 3,9 **** * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.66 2005/07/14 07:32:01 momjian Exp $ */ #include "postgres_fe.h" #include "common.h" --- 3,9 ---- * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.67 2005/07/14 08:42:37 momjian Exp $ */ #include "postgres_fe.h" #include "common.h" *************** *** 24,34 **** --- 24,40 ---- #include <termios.h> #endif + #include <locale.h> + #include "pqsignal.h" #include "libpq-fe.h" #include "mbprint.h" + static char *decimal_point; + static char *grouping; + static char *thousands_sep; + static void * pg_local_malloc(size_t size) { *************** *** 47,52 **** --- 53,59 ---- num_numericseps(const char *my_str) { int old_len, dec_len, int_len; + int groupdigits = atoi(grouping); if (my_str[0] == '-') my_str++; *************** *** 55,64 **** dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; int_len = old_len - dec_len; ! if (int_len % 3 != 0) ! return int_len / 3; else ! return int_len / 3 - 1; /* no leading separator */ } static int --- 62,71 ---- dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; int_len = old_len - dec_len; ! if (int_len % groupdigits != 0) ! return int_len / groupdigits; else ! return int_len / groupdigits - 1; /* no leading separator */ } static int *************** *** 68,84 **** } static void ! format_numericsep(char *my_str, char *numericsep) { int i, j, digits_before_sep, old_len, new_len, dec_len, int_len; - char *dec_point; char *new_str; char *dec_value; ! ! if (strcmp(numericsep, ".") != 0) ! dec_point = "."; ! else ! dec_point = ","; if (my_str[0] == '-') my_str++; --- 75,86 ---- } static void ! format_numericsep(char *my_str) { int i, j, digits_before_sep, old_len, new_len, dec_len, int_len; char *new_str; char *dec_value; ! int groupdigits = atoi(grouping); if (my_str[0] == '-') my_str++; *************** *** 86,94 **** old_len = strlen(my_str); dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; int_len = old_len - dec_len; ! digits_before_sep = int_len % 3; ! new_len = int_len + int_len / 3 + dec_len; if (digits_before_sep == 0) new_len--; /* no leading separator */ --- 88,96 ---- old_len = strlen(my_str); dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; int_len = old_len - dec_len; ! digits_before_sep = int_len % groupdigits; ! new_len = int_len + int_len / groupdigits + dec_len; if (digits_before_sep == 0) new_len--; /* no leading separator */ *************** *** 99,105 **** /* hit decimal point */ if (my_str[i] == '.') { ! new_str[j] = *dec_point; new_str[j+1] = '\0'; dec_value = strchr(my_str, '.'); strcat(new_str, ++dec_value); --- 101,107 ---- /* hit decimal point */ if (my_str[i] == '.') { ! new_str[j] = *decimal_point; new_str[j+1] = '\0'; dec_value = strchr(my_str, '.'); strcat(new_str, ++dec_value); *************** *** 115,122 **** /* add separator? */ if (i != 0 && ! (i - (digits_before_sep ? digits_before_sep : 3)) % 3 == 0) ! new_str[j++] = *numericsep; new_str[j] = my_str[i]; } --- 117,125 ---- /* add separator? */ if (i != 0 && ! (i - (digits_before_sep ? digits_before_sep : groupdigits)) ! % groupdigits == 0) ! new_str[j++] = *thousands_sep; new_str[j] = my_str[i]; } *************** *** 135,141 **** const char *const *cells, const char *const *footers, const char *opt_align, const char *opt_fieldsep, const char *opt_recordsep, bool opt_tuples_only, ! char *opt_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; --- 138,144 ---- 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_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; *************** *** 174,186 **** fputs(opt_recordsep, fout); need_recordsep = false; } ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) > 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); fputs(my_cell, fout); free(my_cell); } --- 177,188 ---- fputs(opt_recordsep, fout); need_recordsep = false; } ! if (opt_align[i % col_count] == 'r' && opt_numericsep) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); fputs(my_cell, fout); free(my_cell); } *************** *** 220,226 **** const char *const *cells, const char *const *footers, const char *opt_align, const char *opt_fieldsep, const char *opt_recordsep, ! bool opt_tuples_only, char *opt_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; --- 222,228 ---- 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_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; *************** *** 251,263 **** fputs(headers[i % col_count], fout); fputs(opt_fieldsep, fout); ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); fputs(my_cell, fout); free(my_cell); } --- 253,264 ---- fputs(headers[i % col_count], fout); fputs(opt_fieldsep, fout); ! if (opt_align[i % col_count] == 'r' && opt_numericsep) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); fputs(my_cell, fout); free(my_cell); } *************** *** 325,331 **** 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, char *opt_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { --- 326,332 ---- 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_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { *************** *** 394,401 **** { int numericseps; ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) numericseps = num_numericseps(*ptr); else numericseps = 0; --- 395,401 ---- { int numericseps; ! if (opt_align[i % col_count] == 'r' && opt_numericsep) numericseps = num_numericseps(*ptr); else numericseps = 0; *************** *** 480,491 **** /* content */ if (opt_align[i % col_count] == 'r') { ! if (strlen(*ptr) > 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); free(my_cell); } --- 480,491 ---- /* content */ if (opt_align[i % col_count] == 'r') { ! if (opt_numericsep) { char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); free(my_cell); } *************** *** 547,553 **** 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, ! char *opt_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { unsigned int col_count = 0; --- 547,553 ---- 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_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { unsigned int col_count = 0; *************** *** 612,619 **** { int numericseps; ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) numericseps = num_numericseps(*ptr); else numericseps = 0; --- 612,618 ---- { int numericseps; ! if (opt_align[i % col_count] == 'r' && opt_numericsep) numericseps = num_numericseps(*ptr); else numericseps = 0; *************** *** 696,704 **** char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) ! format_numericsep(my_cell, opt_numericsep); if (opt_border < 2) puts(my_cell); else --- 695,702 ---- char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! if (opt_align[i % col_count] == 'r' && opt_numericsep) ! format_numericsep(my_cell); if (opt_border < 2) puts(my_cell); else *************** *** 785,791 **** 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, ! char *opt_numericsep, unsigned short int opt_border, const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; --- 783,789 ---- 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_numericsep, unsigned short int opt_border, const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; *************** *** 831,843 **** /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); html_escaped_print(my_cell, fout); free(my_cell); } --- 829,840 ---- /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if (opt_align[i % col_count] == 'r' && opt_numericsep) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); html_escaped_print(my_cell, fout); free(my_cell); } *************** *** 873,879 **** 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, ! char *opt_numericsep, unsigned short int opt_border, const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; --- 870,876 ---- 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_numericsep, unsigned short int opt_border, const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; *************** *** 917,929 **** /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); html_escaped_print(my_cell, fout); free(my_cell); } --- 914,925 ---- /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if (opt_align[i % col_count] == 'r' && opt_numericsep) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); html_escaped_print(my_cell, fout); free(my_cell); } *************** *** 999,1005 **** 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, ! char *opt_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 995,1001 ---- 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_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; *************** *** 1060,1072 **** /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); latex_escaped_print(my_cell, fout); free(my_cell); } --- 1056,1067 ---- /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (opt_numericsep) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); latex_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1103,1109 **** 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, ! char *opt_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 1098,1104 ---- 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_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; *************** *** 1174,1186 **** if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { ! if (strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); latex_escaped_print(my_cell, fout); free(my_cell); } --- 1169,1180 ---- if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { ! if (opt_numericsep) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); latex_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1221,1227 **** 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, ! char *opt_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 1215,1221 ---- 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_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; *************** *** 1275,1287 **** /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); troff_ms_escaped_print(my_cell, fout); free(my_cell); } --- 1269,1280 ---- /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (opt_numericsep) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); troff_ms_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1315,1321 **** 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, ! char *opt_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 1308,1314 ---- 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_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; *************** *** 1345,1356 **** if (opt_tuples_only) fputs("c l;\n", fout); - /* count columns */ for (ptr = headers; *ptr; ptr++) col_count++; - /* print records */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { --- 1338,1347 ---- *************** *** 1390,1402 **** troff_ms_escaped_print(headers[i % col_count], fout); fputc('\t', fout); ! if (strlen(*ptr) != 0 && ! opt_numericsep != NULL && strlen(opt_numericsep) > 0) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell, opt_numericsep); troff_ms_escaped_print(my_cell, fout); free(my_cell); } --- 1381,1392 ---- troff_ms_escaped_print(headers[i % col_count], fout); fputc('\t', fout); ! if (opt_numericsep) { char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); troff_ms_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1714,1717 **** } ! /* the end */ --- 1704,1729 ---- } ! void ! setDecimalLocale(void) ! { ! struct lconv *extlconv; ! ! extlconv = localeconv(); ! ! /* These are treated as single-byte strings in the code */ ! if (*extlconv->decimal_point) ! decimal_point = strdup(extlconv->decimal_point); ! else ! decimal_point = "."; /* SQL output standard */ ! if (*extlconv->grouping && atoi(extlconv->grouping) > 0) ! grouping = strdup(extlconv->grouping); ! else ! grouping = "3"; /* most common */ ! if (*extlconv->thousands_sep) ! thousands_sep = strdup(extlconv->thousands_sep); ! else ! thousands_sep = ","; /* matches SQL standard decimal marker */ ! } ! ! Index: src/bin/psql/print.h =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v retrieving revision 1.26 retrieving revision 1.27 diff -c -r1.26 -r1.27 *** src/bin/psql/print.h 10 Jul 2005 03:46:13 -0000 1.26 --- src/bin/psql/print.h 14 Jul 2005 08:42:37 -0000 1.27 *************** *** 3,9 **** * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.26 2005/07/10 03:46:13 momjian Exp $ */ #ifndef PRINT_H #define PRINT_H --- 3,9 ---- * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.27 2005/07/14 08:42:37 momjian Exp $ */ #ifndef PRINT_H #define PRINT_H *************** *** 40,46 **** char *fieldSep; /* field separator for unaligned text mode */ char *recordSep; /* record separator for unaligned text * mode */ ! char *numericSep; /* numeric units separator */ char *tableAttr; /* attributes for HTML <table ...> */ int encoding; /* character encoding */ bool normal_query; /* are we presenting the results of a --- 40,47 ---- char *fieldSep; /* field separator for unaligned text mode */ char *recordSep; /* record separator for unaligned text * mode */ ! bool numericSep; /* locale-aware numeric units separator and ! * decimal marker */ char *tableAttr; /* attributes for HTML <table ...> */ int encoding; /* character encoding */ bool normal_query; /* are we presenting the results of a *************** *** 86,91 **** --- 87,94 ---- void printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog); + void setDecimalLocale(void); + #ifndef __CYGWIN__ #define DEFAULT_PAGER "more" #else Index: src/bin/psql/startup.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/startup.c,v retrieving revision 1.118 retrieving revision 1.119 diff -c -r1.118 -r1.119 *** src/bin/psql/startup.c 21 Jun 2005 04:02:33 -0000 1.118 --- src/bin/psql/startup.c 14 Jul 2005 08:42:37 -0000 1.119 *************** *** 3,9 **** * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.118 2005/06/21 04:02:33 tgl Exp $ */ #include "postgres_fe.h" --- 3,9 ---- * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.119 2005/07/14 08:42:37 momjian Exp $ */ #include "postgres_fe.h" *************** *** 130,135 **** --- 130,136 ---- setvbuf(stderr, NULL, _IONBF, 0); setup_win32_locks(); #endif + setDecimalLocale(); pset.cur_cmd_source = stdin; pset.cur_cmd_interactive = false; pset.encoding = PQenv2encoding();
Am Donnerstag, 14. Juli 2005 10:48 schrieb Bruce Momjian: > OK, I have applied the following patch to make numerisep a boolean, made > it locale-aware, set defaults if the locale doesn't return meaningful > values, added code to handle locale-reported groupings, and updated the > documentation. Then the name "numericsep" doesn't seem to make much sense anymore. -- Peter Eisentraut http://developer.postgresql.org/~petere/
Bruce Momjian <pgman@candle.pha.pa.us> writes: > OK, I have applied the following patch to make numerisep a boolean, "numericsep" is no longer even remotely reasonable as a name for the parameter. Something like "numeric_use_locale" would be appropriate (but probably too wordy). > The only question I have is whether those locale values are single-byte > strings in all locals, or could they be multi-byte or multi-character, > in which case I have to treat them as strings. I think you have to assume they could be strings. regards, tom lane
Tom Lane wrote: > > The only question I have is whether those locale values are single-byte > > strings in all locals, or could they be multi-byte or multi-character, > > in which case I have to treat them as strings. > > I think you have to assume they could be strings. OK, the following applied patch enables multi-byte locale strings for numericsep. I also reorganized the code a little. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 Index: src/bin/psql/print.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.68 diff -c -c -r1.68 print.c *** src/bin/psql/print.c 14 Jul 2005 15:54:21 -0000 1.68 --- src/bin/psql/print.c 14 Jul 2005 20:54:05 -0000 *************** *** 50,125 **** } static int ! num_numericseps(const char *my_str) { ! int old_len, dec_len, int_len; ! int groupdigits = atoi(grouping); if (my_str[0] == '-') my_str++; ! old_len = strlen(my_str); ! dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; - int_len = old_len - dec_len; if (int_len % groupdigits != 0) ! return int_len / groupdigits; else ! return int_len / groupdigits - 1; /* no leading separator */ } static int len_with_numericsep(const char *my_str) { ! return strlen(my_str) + num_numericseps(my_str); } static void format_numericsep(char *my_str) { ! int i, j, digits_before_sep, old_len, new_len, dec_len, int_len; ! char *new_str; ! char *dec_value; int groupdigits = atoi(grouping); if (my_str[0] == '-') my_str++; ! ! old_len = strlen(my_str); ! dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; ! int_len = old_len - dec_len; ! digits_before_sep = int_len % groupdigits; ! ! new_len = int_len + int_len / groupdigits + dec_len; ! if (digits_before_sep == 0) ! new_len--; /* no leading separator */ ! new_str = pg_local_malloc(new_len + 1); for (i=0, j=0; ; i++, j++) { ! /* hit decimal point */ if (my_str[i] == '.') { ! new_str[j] = *decimal_point; ! new_str[j+1] = '\0'; ! dec_value = strchr(my_str, '.'); ! strcat(new_str, ++dec_value); break; } ! /* end of string */ if (my_str[i] == '\0') { new_str[j] = '\0'; break; } ! /* add separator? */ ! if (i != 0 && ! (i - (digits_before_sep ? digits_before_sep : groupdigits)) ! % groupdigits == 0) ! new_str[j++] = *thousands_sep; new_str[j] = my_str[i]; } --- 50,128 ---- } static int ! integer_digits(const char *my_str) { ! int frac_len; if (my_str[0] == '-') my_str++; ! frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; ! ! return strlen(my_str) - frac_len; ! } ! ! static int ! len_numericseps(const char *my_str) ! { ! int int_len = integer_digits(my_str), sep_len; ! int groupdigits = atoi(grouping); if (int_len % groupdigits != 0) ! sep_len = int_len / groupdigits; else ! sep_len = int_len / groupdigits - 1; /* no leading separator */ ! ! return sep_len * strlen(thousands_sep) - ! strlen(".") + strlen(decimal_point); } static int len_with_numericsep(const char *my_str) { ! return strlen(my_str) + len_numericseps(my_str); } static void format_numericsep(char *my_str) { ! int i, j, int_len = integer_digits(my_str), leading_digits; int groupdigits = atoi(grouping); + char *new_str; if (my_str[0] == '-') my_str++; ! ! new_str = pg_local_malloc(len_numericseps(my_str) + 1); ! leading_digits = (int_len % groupdigits != 0) ? ! int_len % groupdigits : groupdigits; for (i=0, j=0; ; i++, j++) { ! /* Hit decimal point? */ if (my_str[i] == '.') { ! strcpy(&new_str[j], decimal_point); ! j += strlen(decimal_point); ! /* add fractional part */ ! strcpy(&new_str[j], &my_str[i] + 1); break; } ! /* End of string? */ if (my_str[i] == '\0') { new_str[j] = '\0'; break; } ! /* Add separator? */ ! if (i != 0 && (i - leading_digits) % groupdigits == 0) ! { ! strcpy(&new_str[j], thousands_sep); ! j += strlen(thousands_sep); ! } new_str[j] = my_str[i]; } *************** *** 396,402 **** int numericseps; if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = num_numericseps(*ptr); else numericseps = 0; --- 399,405 ---- int numericseps; if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = len_numericseps(*ptr); else numericseps = 0; *************** *** 613,619 **** int numericseps; if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = num_numericseps(*ptr); else numericseps = 0; --- 616,622 ---- int numericseps; if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = len_numericseps(*ptr); else numericseps = 0; *************** *** 1711,1717 **** extlconv = localeconv(); - /* These are treated as single-byte strings in the code */ if (*extlconv->decimal_point) decimal_point = strdup(extlconv->decimal_point); else --- 1714,1719 ----
Tom Lane wrote: > Bruce Momjian <pgman@candle.pha.pa.us> writes: > > OK, I have applied the following patch to make numerisep a boolean, > > "numericsep" is no longer even remotely reasonable as a name for the > parameter. Something like "numeric_use_locale" would be appropriate > (but probably too wordy). Basically, with the C locale, it changes 1000.00 to 1,000.00, so indeed is does add a numeric separator. For a European locale, 1000.00 becomes 1.000,00. so in that case is changes both the separator and decimal marker. We don't currently have a way to display 1000.00 as 1000,00. I am thinking of calling it just numericlocale. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
The new code is broken. Please test it with resonably large tables. Do not test it with querys like: select -132323435.34343; If I use a query like: select * from my_table limit 100; I can't see anything on my screen until I hit CTRL^C If I use a pager (\pset pager less) strange things happened: I can't see anything on my screen, hit CTRL^C, then quit psql, reset xterm, then less is still running and eat lots of memory and cpu. Best Regards, Eugen
Eugen Nedelcu wrote: > The new code is broken. Please test it with resonably large tables. > Do not test it with querys like: select -132323435.34343; > > If I use a query like: > > select * from my_table limit 100; > > I can't see anything on my screen until I hit CTRL^C > > If I use a pager (\pset pager less) strange things happened: > I can't see anything on my screen, hit CTRL^C, then quit psql, > reset xterm, then less is still running and eat lots of memory and > cpu. I did: SELECT random() * 1000000000 INTO test FROM generate_series(1,100000); \pset numericsep SELECT * FROM test; and it seemed to work fine. Did you do a complete rebuild/install? -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
In function format_numericsep() you have: new_str = pg_local_malloc(len_numericseps(my_str) + 1), instead of: new_str = pg_local_malloc(len_with_numericsep(my_str) + 1) Another bug is in function len_numericseps(). This apear for querys like: select NULL::numeric; or select * from table_with_numeric_fields; where one of numeric fields have null values; For such values, len_numericseps returns -1, instead of 0. Instead of: if (int_len % groupdigits != 0) sep_len = int_len / groupdigits; else sep_len = int_len / groupdigits - 1; you must have: if (int_len % groupdigits != 0 || int_len == 0) sep_len = int_len / groupdigits; else sep_len = int_len / groupdigits - 1; Best Regards, Eugen
Thanks, fix attached and applied --------------------------------------------------------------------------- Eugen Nedelcu wrote: > In function format_numericsep() you have: > > new_str = pg_local_malloc(len_numericseps(my_str) + 1), > > instead of: > > new_str = pg_local_malloc(len_with_numericsep(my_str) + 1) > > Another bug is in function len_numericseps(). This apear for querys > like: > > select NULL::numeric; or > select * from table_with_numeric_fields; where one of numeric fields > have null values; > > For such values, len_numericseps returns -1, instead of 0. > > Instead of: > > if (int_len % groupdigits != 0) > sep_len = int_len / groupdigits; > else > sep_len = int_len / groupdigits - 1; > > you must have: > > if (int_len % groupdigits != 0 || int_len == 0) > sep_len = int_len / groupdigits; > else > sep_len = int_len / groupdigits - 1; > > Best Regards, > Eugen > -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 Index: src/bin/psql/print.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.69 diff -c -c -r1.69 print.c *** src/bin/psql/print.c 14 Jul 2005 21:12:41 -0000 1.69 --- src/bin/psql/print.c 18 Jul 2005 17:16:37 -0000 *************** *** 68,77 **** int int_len = integer_digits(my_str), sep_len; int groupdigits = atoi(grouping); ! if (int_len % groupdigits != 0) ! sep_len = int_len / groupdigits; else ! sep_len = int_len / groupdigits - 1; /* no leading separator */ return sep_len * strlen(thousands_sep) - strlen(".") + strlen(decimal_point); --- 68,78 ---- int int_len = integer_digits(my_str), sep_len; int groupdigits = atoi(grouping); ! if (int_len == 0) ! sep_len = 0; else ! /* Don't count a leading separator */ ! sep_len = int_len / groupdigits - (int_len % groupdigits == 0); return sep_len * strlen(thousands_sep) - strlen(".") + strlen(decimal_point); *************** *** 93,99 **** if (my_str[0] == '-') my_str++; ! new_str = pg_local_malloc(len_numericseps(my_str) + 1); leading_digits = (int_len % groupdigits != 0) ? int_len % groupdigits : groupdigits; --- 94,100 ---- if (my_str[0] == '-') my_str++; ! new_str = pg_local_malloc(len_with_numericsep(my_str) + 1); leading_digits = (int_len % groupdigits != 0) ? int_len % groupdigits : groupdigits;
Bruce Momjian wrote: > Tom Lane wrote: > > Bruce Momjian <pgman@candle.pha.pa.us> writes: > > > OK, I have applied the following patch to make numerisep a boolean, > > > > "numericsep" is no longer even remotely reasonable as a name for the > > parameter. Something like "numeric_use_locale" would be appropriate > > (but probably too wordy). > > Basically, with the C locale, it changes 1000.00 to 1,000.00, so indeed > is does add a numeric separator. For a European locale, 1000.00 becomes > 1.000,00. so in that case is changes both the separator and decimal > marker. We don't currently have a way to display 1000.00 as 1000,00. > > I am thinking of calling it just numericlocale. Done, and attached. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 Index: doc/src/sgml/ref/psql-ref.sgml =================================================================== RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v retrieving revision 1.147 diff -c -c -r1.147 psql-ref.sgml *** doc/src/sgml/ref/psql-ref.sgml 14 Jul 2005 08:42:36 -0000 1.147 --- doc/src/sgml/ref/psql-ref.sgml 18 Jul 2005 19:38:56 -0000 *************** *** 1493,1499 **** </varlistentry> <varlistentry> ! <term><literal>numericsep</literal></term> <listitem> <para> Toggles the display of a locale-aware character to separate groups --- 1493,1499 ---- </varlistentry> <varlistentry> ! <term><literal>numericlocale</literal></term> <listitem> <para> Toggles the display of a locale-aware character to separate groups Index: src/bin/psql/command.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v retrieving revision 1.149 diff -c -c -r1.149 command.c *** src/bin/psql/command.c 14 Jul 2005 08:42:37 -0000 1.149 --- src/bin/psql/command.c 18 Jul 2005 19:39:02 -0000 *************** *** 1420,1435 **** : _("Expanded display is off.\n")); } ! /* numeric separators */ ! else if (strcmp(param, "numericsep") == 0) { ! popt->topt.numericSep = !popt->topt.numericSep; if (!quiet) { ! if (popt->topt.numericSep) ! puts(_("Showing numeric separators.")); else ! puts(_("Numeric separators are off.")); } } --- 1420,1435 ---- : _("Expanded display is off.\n")); } ! /* locale-aware numeric output */ ! else if (strcmp(param, "numericlocale") == 0) { ! popt->topt.numericLocale = !popt->topt.numericLocale; if (!quiet) { ! if (popt->topt.numericLocale) ! puts(_("Showing locale-adjusted numeric output.")); else ! puts(_("Locale-adjusted numeric output is off.")); } } Index: src/bin/psql/help.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/help.c,v retrieving revision 1.104 diff -c -c -r1.104 help.c *** src/bin/psql/help.c 10 Jul 2005 03:46:13 -0000 1.104 --- src/bin/psql/help.c 18 Jul 2005 19:39:02 -0000 *************** *** 239,245 **** fprintf(output, _(" \\pset NAME [VALUE]\n" " set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" ! " numericsep|recordsep|tuples_only|title|tableattr|pager})\n")); fprintf(output, _(" \\t show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n")); --- 239,245 ---- fprintf(output, _(" \\pset NAME [VALUE]\n" " set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" ! " numericlocale|recordsep|tuples_only|title|tableattr|pager})\n")); fprintf(output, _(" \\t show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n")); Index: src/bin/psql/print.c =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.71 diff -c -c -r1.71 print.c *** src/bin/psql/print.c 18 Jul 2005 19:27:37 -0000 1.71 --- src/bin/psql/print.c 18 Jul 2005 19:39:03 -0000 *************** *** 62,69 **** return strlen(my_str) - frac_len; } static int ! len_numericseps(const char *my_str) { int int_len = integer_digits(my_str), len = 0; int groupdigits = atoi(grouping); --- 62,70 ---- return strlen(my_str) - frac_len; } + /* Return additional length required for locale-aware numeric output */ static int ! additional_numeric_locale_len(const char *my_str) { int int_len = integer_digits(my_str), len = 0; int groupdigits = atoi(grouping); *************** *** 80,92 **** } static int ! len_with_numericsep(const char *my_str) { ! return strlen(my_str) + len_numericseps(my_str); } static void ! format_numericsep(char *my_str) { int i, j, int_len = integer_digits(my_str), leading_digits; int groupdigits = atoi(grouping); --- 81,93 ---- } static int ! strlen_with_numeric_locale(const char *my_str) { ! return strlen(my_str) + additional_numeric_locale_len(my_str); } static void ! format_numeric_locale(char *my_str) { int i, j, int_len = integer_digits(my_str), leading_digits; int groupdigits = atoi(grouping); *************** *** 95,101 **** if (my_str[0] == '-') my_str++; ! new_str = pg_local_malloc(len_with_numericsep(my_str) + 1); leading_digits = (int_len % groupdigits != 0) ? int_len % groupdigits : groupdigits; --- 96,102 ---- if (my_str[0] == '-') my_str++; ! new_str = pg_local_malloc(strlen_with_numeric_locale(my_str) + 1); leading_digits = (int_len % groupdigits != 0) ? int_len % groupdigits : groupdigits; *************** *** 143,149 **** 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_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; --- 144,150 ---- 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) { unsigned int col_count = 0; unsigned int i; *************** *** 182,193 **** fputs(opt_recordsep, fout); need_recordsep = false; } ! if (opt_align[i % col_count] == 'r' && opt_numericsep) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); fputs(my_cell, fout); free(my_cell); } --- 183,194 ---- fputs(opt_recordsep, fout); need_recordsep = false; } ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); fputs(my_cell, fout); free(my_cell); } *************** *** 227,233 **** 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_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; --- 228,234 ---- 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) { unsigned int col_count = 0; unsigned int i; *************** *** 258,269 **** fputs(headers[i % col_count], fout); fputs(opt_fieldsep, fout); ! if (opt_align[i % col_count] == 'r' && opt_numericsep) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); fputs(my_cell, fout); free(my_cell); } --- 259,270 ---- fputs(headers[i % col_count], fout); fputs(opt_fieldsep, fout); ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); fputs(my_cell, fout); free(my_cell); } *************** *** 331,337 **** 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_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { --- 332,338 ---- 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, FILE *fout) { *************** *** 398,411 **** for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numericseps; ! if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = len_numericseps(*ptr); else ! numericseps = 0; ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numericseps; if (tmp > widths[i % col_count]) widths[i % col_count] = tmp; cell_w[i] = tmp; --- 399,412 ---- for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numeric_locale_len; ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! numeric_locale_len = additional_numeric_locale_len(*ptr); else ! numeric_locale_len = 0; ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numeric_locale_len; if (tmp > widths[i % col_count]) widths[i % col_count] = tmp; cell_w[i] = tmp; *************** *** 485,496 **** /* content */ if (opt_align[i % col_count] == 'r') { ! if (opt_numericsep) { char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); free(my_cell); } --- 486,497 ---- /* content */ if (opt_align[i % col_count] == 'r') { ! if (opt_numeric_locale) { char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); free(my_cell); } *************** *** 552,558 **** 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_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { unsigned int col_count = 0; --- 553,559 ---- 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) { unsigned int col_count = 0; *************** *** 615,628 **** /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numericseps; ! if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = len_numericseps(*ptr); else ! numericseps = 0; ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numericseps; if (tmp > dwidth) dwidth = tmp; cell_w[i] = tmp; --- 616,629 ---- /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numeric_locale_len; ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! numeric_locale_len = additional_numeric_locale_len(*ptr); else ! numeric_locale_len = 0; ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numeric_locale_len; if (tmp > dwidth) dwidth = tmp; cell_w[i] = tmp; *************** *** 700,707 **** char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! if (opt_align[i % col_count] == 'r' && opt_numericsep) ! format_numericsep(my_cell); if (opt_border < 2) puts(my_cell); else --- 701,708 ---- char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! format_numeric_locale(my_cell); if (opt_border < 2) puts(my_cell); else *************** *** 788,794 **** 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_numericsep, unsigned short int opt_border, const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; --- 789,795 ---- 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) { unsigned int col_count = 0; *************** *** 834,845 **** /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if (opt_align[i % col_count] == 'r' && opt_numericsep) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); html_escaped_print(my_cell, fout); free(my_cell); } --- 835,846 ---- /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); html_escaped_print(my_cell, fout); free(my_cell); } *************** *** 875,881 **** 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_numericsep, unsigned short int opt_border, const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; --- 876,882 ---- 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) { unsigned int col_count = 0; *************** *** 919,930 **** /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if (opt_align[i % col_count] == 'r' && opt_numericsep) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); html_escaped_print(my_cell, fout); free(my_cell); } --- 920,931 ---- /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); html_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1000,1006 **** 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_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 1001,1007 ---- 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, FILE *fout) { unsigned int col_count = 0; *************** *** 1061,1072 **** /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (opt_numericsep) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); latex_escaped_print(my_cell, fout); free(my_cell); } --- 1062,1073 ---- /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); latex_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1103,1109 **** 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_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 1104,1110 ---- 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, FILE *fout) { unsigned int col_count = 0; *************** *** 1174,1185 **** if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { ! if (opt_numericsep) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); latex_escaped_print(my_cell, fout); free(my_cell); } --- 1175,1186 ---- if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { ! if (opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); latex_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1220,1226 **** 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_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 1221,1227 ---- 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, FILE *fout) { unsigned int col_count = 0; *************** *** 1274,1285 **** /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (opt_numericsep) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); troff_ms_escaped_print(my_cell, fout); free(my_cell); } --- 1275,1286 ---- /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); troff_ms_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1313,1319 **** 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_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 1314,1320 ---- 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, FILE *fout) { unsigned int col_count = 0; *************** *** 1386,1397 **** troff_ms_escaped_print(headers[i % col_count], fout); fputc('\t', fout); ! if (opt_numericsep) { ! char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numericsep(my_cell); troff_ms_escaped_print(my_cell, fout); free(my_cell); } --- 1387,1398 ---- troff_ms_escaped_print(headers[i % col_count], fout); fputc('\t', fout); ! if (opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); troff_ms_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1533,1539 **** /* print the stuff */ if (flog) ! print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, opt->numericSep, border, opt->encoding,flog); switch (opt->format) { --- 1534,1540 ---- /* print the stuff */ if (flog) ! print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, opt->numericLocale, border, opt->encoding,flog); switch (opt->format) { *************** *** 1541,1590 **** if (use_expanded) print_unaligned_vertical(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericSep, output); else print_unaligned_text(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericSep, output); break; case PRINT_ALIGNED: if (use_expanded) print_aligned_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->encoding, output); else print_aligned_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->encoding, output); break; case PRINT_HTML: if (use_expanded) print_html_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->tableAttr, output); else print_html_text(title, headers, cells, footers, ! align, opt->tuples_only, opt->numericSep, border, opt->tableAttr, output); break; case PRINT_LATEX: if (use_expanded) print_latex_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, output); else print_latex_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, output); break; case PRINT_TROFF_MS: if (use_expanded) print_troff_ms_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, output); else print_troff_ms_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, output); break; default: --- 1542,1591 ---- if (use_expanded) print_unaligned_vertical(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericLocale, output); else print_unaligned_text(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericLocale, output); break; case PRINT_ALIGNED: if (use_expanded) print_aligned_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericLocale, border, opt->encoding, output); else print_aligned_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericLocale, border, opt->encoding, output); break; case PRINT_HTML: if (use_expanded) print_html_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericLocale, border, opt->tableAttr, output); else print_html_text(title, headers, cells, footers, ! align, opt->tuples_only, opt->numericLocale, border, opt->tableAttr, output); break; case PRINT_LATEX: if (use_expanded) print_latex_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericLocale, border, output); else print_latex_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericLocale, border, output); break; case PRINT_TROFF_MS: if (use_expanded) print_troff_ms_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericLocale, border, output); else print_troff_ms_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericLocale, border, output); break; default: Index: src/bin/psql/print.h =================================================================== RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v retrieving revision 1.27 diff -c -c -r1.27 print.h *** src/bin/psql/print.h 14 Jul 2005 08:42:37 -0000 1.27 --- src/bin/psql/print.h 18 Jul 2005 19:39:03 -0000 *************** *** 40,46 **** char *fieldSep; /* field separator for unaligned text mode */ char *recordSep; /* record separator for unaligned text * mode */ ! bool numericSep; /* locale-aware numeric units separator and * decimal marker */ char *tableAttr; /* attributes for HTML <table ...> */ int encoding; /* character encoding */ --- 40,46 ---- char *fieldSep; /* field separator for unaligned text mode */ char *recordSep; /* record separator for unaligned text * mode */ ! bool numericLocale; /* locale-aware numeric units separator and * decimal marker */ char *tableAttr; /* attributes for HTML <table ...> */ int encoding; /* character encoding */