mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Fix encoding issue when lc_monetary or lc_numeric are different encoding
from lc_ctype, that could happen on Windows. We need to change lc_ctype together with lc_monetary or lc_numeric, and convert strings in lconv from lc_ctype encoding to the database encoding. The bug reported by Mikko, original patch by Hiroshi Inoue, with changes by Bruce and me.
This commit is contained in:
@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Portions Copyright (c) 2002-2010, PostgreSQL Global Development Group
|
* Portions Copyright (c) 2002-2010, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.53 2010/02/27 20:20:44 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.54 2010/04/22 01:55:52 itagaki Exp $
|
||||||
*
|
*
|
||||||
*-----------------------------------------------------------------------
|
*-----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -387,6 +387,28 @@ free_struct_lconv(struct lconv * s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a strdup'ed string converted from the specified encoding to the
|
||||||
|
* database encoding.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
db_encoding_strdup(int encoding, const char *str)
|
||||||
|
{
|
||||||
|
char *pstr;
|
||||||
|
char *mstr;
|
||||||
|
|
||||||
|
/* convert the string to the database encoding */
|
||||||
|
pstr = (char *) pg_do_encoding_conversion(
|
||||||
|
(unsigned char *) str, strlen(str),
|
||||||
|
encoding, GetDatabaseEncoding());
|
||||||
|
mstr = strdup(pstr);
|
||||||
|
if (pstr != str)
|
||||||
|
pfree(pstr);
|
||||||
|
|
||||||
|
return mstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the POSIX lconv struct (contains number/money formatting
|
* Return the POSIX lconv struct (contains number/money formatting
|
||||||
* information) with locale information for all categories.
|
* information) with locale information for all categories.
|
||||||
@ -398,6 +420,14 @@ PGLC_localeconv(void)
|
|||||||
struct lconv *extlconv;
|
struct lconv *extlconv;
|
||||||
char *save_lc_monetary;
|
char *save_lc_monetary;
|
||||||
char *save_lc_numeric;
|
char *save_lc_numeric;
|
||||||
|
char *decimal_point;
|
||||||
|
char *grouping;
|
||||||
|
char *thousands_sep;
|
||||||
|
int encoding;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
char *save_lc_ctype;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Did we do it already? */
|
/* Did we do it already? */
|
||||||
if (CurrentLocaleConvValid)
|
if (CurrentLocaleConvValid)
|
||||||
@ -413,28 +443,48 @@ PGLC_localeconv(void)
|
|||||||
if (save_lc_numeric)
|
if (save_lc_numeric)
|
||||||
save_lc_numeric = pstrdup(save_lc_numeric);
|
save_lc_numeric = pstrdup(save_lc_numeric);
|
||||||
|
|
||||||
setlocale(LC_MONETARY, locale_monetary);
|
#ifdef WIN32
|
||||||
setlocale(LC_NUMERIC, locale_numeric);
|
/* set user's value of ctype locale */
|
||||||
|
save_lc_ctype = setlocale(LC_CTYPE, NULL);
|
||||||
|
if (save_lc_ctype)
|
||||||
|
save_lc_ctype = pstrdup(save_lc_ctype);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Get formatting information */
|
/* Get formatting information for numeric */
|
||||||
|
#ifdef WIN32
|
||||||
|
setlocale(LC_CTYPE, locale_numeric);
|
||||||
|
#endif
|
||||||
|
setlocale(LC_NUMERIC, locale_numeric);
|
||||||
extlconv = localeconv();
|
extlconv = localeconv();
|
||||||
|
encoding = pg_get_encoding_from_locale(locale_numeric);
|
||||||
|
|
||||||
|
decimal_point = db_encoding_strdup(encoding, extlconv->decimal_point);
|
||||||
|
thousands_sep = db_encoding_strdup(encoding, extlconv->thousands_sep);
|
||||||
|
grouping = strdup(extlconv->grouping);
|
||||||
|
|
||||||
|
/* Get formatting information for monetary */
|
||||||
|
#ifdef WIN32
|
||||||
|
setlocale(LC_CTYPE, locale_monetary);
|
||||||
|
#endif
|
||||||
|
setlocale(LC_MONETARY, locale_monetary);
|
||||||
|
extlconv = localeconv();
|
||||||
|
encoding = pg_get_encoding_from_locale(locale_monetary);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Must copy all values since restoring internal settings may overwrite
|
* Must copy all values since restoring internal settings may overwrite
|
||||||
* localeconv()'s results.
|
* localeconv()'s results.
|
||||||
*/
|
*/
|
||||||
CurrentLocaleConv = *extlconv;
|
CurrentLocaleConv = *extlconv;
|
||||||
CurrentLocaleConv.currency_symbol = strdup(extlconv->currency_symbol);
|
CurrentLocaleConv.decimal_point = decimal_point;
|
||||||
CurrentLocaleConv.decimal_point = strdup(extlconv->decimal_point);
|
CurrentLocaleConv.grouping = grouping;
|
||||||
CurrentLocaleConv.grouping = strdup(extlconv->grouping);
|
CurrentLocaleConv.thousands_sep = thousands_sep;
|
||||||
CurrentLocaleConv.thousands_sep = strdup(extlconv->thousands_sep);
|
CurrentLocaleConv.int_curr_symbol = db_encoding_strdup(encoding, extlconv->int_curr_symbol);
|
||||||
CurrentLocaleConv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
|
CurrentLocaleConv.currency_symbol = db_encoding_strdup(encoding, extlconv->currency_symbol);
|
||||||
CurrentLocaleConv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
|
CurrentLocaleConv.mon_decimal_point = db_encoding_strdup(encoding, extlconv->mon_decimal_point);
|
||||||
CurrentLocaleConv.mon_grouping = strdup(extlconv->mon_grouping);
|
CurrentLocaleConv.mon_grouping = strdup(extlconv->mon_grouping);
|
||||||
CurrentLocaleConv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
|
CurrentLocaleConv.mon_thousands_sep = db_encoding_strdup(encoding, extlconv->mon_thousands_sep);
|
||||||
CurrentLocaleConv.negative_sign = strdup(extlconv->negative_sign);
|
CurrentLocaleConv.negative_sign = db_encoding_strdup(encoding, extlconv->negative_sign);
|
||||||
CurrentLocaleConv.positive_sign = strdup(extlconv->positive_sign);
|
CurrentLocaleConv.positive_sign = db_encoding_strdup(encoding, extlconv->positive_sign);
|
||||||
CurrentLocaleConv.n_sign_posn = extlconv->n_sign_posn;
|
|
||||||
|
|
||||||
/* Try to restore internal settings */
|
/* Try to restore internal settings */
|
||||||
if (save_lc_monetary)
|
if (save_lc_monetary)
|
||||||
@ -449,6 +499,15 @@ PGLC_localeconv(void)
|
|||||||
pfree(save_lc_numeric);
|
pfree(save_lc_numeric);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
/* try to restore internal ctype settings */
|
||||||
|
if (save_lc_ctype)
|
||||||
|
{
|
||||||
|
setlocale(LC_CTYPE, save_lc_ctype);
|
||||||
|
pfree(save_lc_ctype);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
CurrentLocaleConvValid = true;
|
CurrentLocaleConvValid = true;
|
||||||
return &CurrentLocaleConv;
|
return &CurrentLocaleConv;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user