From b577aa9ebc780c5095f213d36253f63580b2a067 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Thu, 8 Feb 2007 18:19:33 +0000 Subject: [PATCH] Fix bug when localized to_char() day or month names were incorectly trnasformed to lower or upper string. Pavel Stehule --- src/backend/utils/adt/formatting.c | 145 +++++++++++++++++++++++--- src/backend/utils/adt/oracle_compat.c | 78 +++++++++++++- 2 files changed, 206 insertions(+), 17 deletions(-) diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index cbc1096499..91ef1ea9dc 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * formatting.c * - * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.119 2007/02/08 03:22:28 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.120 2007/02/08 18:19:33 momjian Exp $ * * * Portions Copyright (c) 1999-2007, PostgreSQL Global Development Group @@ -82,6 +82,7 @@ #include "utils/int8.h" #include "utils/numeric.h" #include "utils/pg_locale.h" +#include "mb/pg_wchar.h" #define _(x) gettext((x)) @@ -113,6 +114,7 @@ #define MAXFLOATWIDTH 64 #define MAXDOUBLEWIDTH 128 + /* ---------- * External (defined in PgSQL datetime.c (timestamp utils)) * ---------- @@ -946,6 +948,20 @@ static char *localize_month(int index); static char *localize_day_full(int index); static char *localize_day(int index); +/* + * External (defined in oracle_compat.c + */ +#if defined(HAVE_WCSTOMBS) && defined(HAVE_TOWLOWER) +#define USE_WIDE_UPPER_LOWER +extern char *wstring_upper(char *str); +extern char *wstring_lower(char *str); +static char *localized_str_toupper(char *buff); +static char *localized_str_tolower(char *buff); +#else +#define localized_str_toupper str_toupper +#define localized_str_tolower str_tolower +#endif + /* ---------- * Fast sequential search, use index for data selection which * go to seq. cycle (it is very fast for unwanted strings) @@ -1500,6 +1516,7 @@ str_toupper(char *buff) *p_buff = pg_toupper((unsigned char) *p_buff); ++p_buff; } + return buff; } @@ -1523,6 +1540,61 @@ str_tolower(char *buff) return buff; } + +#ifdef USE_WIDE_UPPER_LOWER +/* ---------- + * Convert localized string to upper string. Input string is modified in place. + * ---------- + */ +static char * +localized_str_toupper(char *buff) +{ + if (!buff) + return NULL; + + if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) + return wstring_upper(buff); + else + { + char *p_buff = buff; + + while (*p_buff) + { + *p_buff = pg_toupper((unsigned char) *p_buff); + ++p_buff; + } + } + + return buff; +} + +/* ---------- + * Convert localized string to upper string. Input string is modified in place. + * ---------- + */ +static char * +localized_str_tolower(char *buff) +{ + if (!buff) + return NULL; + + if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) + return wstring_lower(buff); + else + { + char *p_buff = buff; + + while (*p_buff) + { + *p_buff = pg_tolower((unsigned char) *p_buff); + ++p_buff; + } + } + + return buff; +} +#endif /* USE_WIDE_UPPER_LOWER */ + /* ---------- * Sequential search with to upper/lower conversion * ---------- @@ -2182,10 +2254,15 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) + { strcpy(workbuff, localize_month_full(tm->tm_mon - 1)); + sprintf(inout, "%*s", 0, localized_str_toupper(workbuff)); + } else + { strcpy(workbuff, months_full[tm->tm_mon - 1]); - sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, str_toupper(workbuff)); + sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(workbuff)); + } return strlen(p_inout); case DCH_Month: @@ -2203,10 +2280,15 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1)); + { + strcpy(workbuff, localize_month_full(tm->tm_mon - 1)); + sprintf(inout, "%*s", 0, localized_str_tolower(workbuff)); + } else + { sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]); - *inout = pg_tolower((unsigned char) *inout); + *inout = pg_tolower((unsigned char) *inout); + } return strlen(p_inout); case DCH_MON: @@ -2214,10 +2296,15 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - strcpy(inout, localize_month(tm->tm_mon - 1)); + { + strcpy(workbuff, localize_month(tm->tm_mon - 1)); + strcpy(inout, localized_str_toupper(workbuff)); + } else + { strcpy(inout, months[tm->tm_mon - 1]); - str_toupper(inout); + str_toupper(inout); + } return strlen(p_inout); case DCH_Mon: @@ -2235,10 +2322,15 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - strcpy(inout, localize_month(tm->tm_mon - 1)); + { + strcpy(workbuff, localize_month(tm->tm_mon - 1)); + strcpy(inout, localized_str_tolower(workbuff)); + } else + { strcpy(inout, months[tm->tm_mon - 1]); - *inout = pg_tolower((unsigned char) *inout); + *inout = pg_tolower((unsigned char) *inout); + } return strlen(p_inout); case DCH_MM: @@ -2266,16 +2358,21 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, case DCH_DAY: INVALID_FOR_INTERVAL; if (S_TM(suf)) + { strcpy(workbuff, localize_day_full(tm->tm_wday)); + sprintf(inout, "%*s", 0, localized_str_toupper(workbuff)); + } else + { strcpy(workbuff, days[tm->tm_wday]); - sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, str_toupper(workbuff)); + sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(workbuff)); + } return strlen(p_inout); case DCH_Day: INVALID_FOR_INTERVAL; if (S_TM(suf)) - sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday)); + sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday)); else sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]); return strlen(p_inout); @@ -2283,19 +2380,30 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, case DCH_day: INVALID_FOR_INTERVAL; if (S_TM(suf)) - sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday)); + { + strcpy(workbuff, localize_day_full(tm->tm_wday)); + sprintf(inout, "%*s", 0, localized_str_tolower(workbuff)); + } else + { sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]); - *inout = pg_tolower((unsigned char) *inout); + *inout = pg_tolower((unsigned char) *inout); + } return strlen(p_inout); case DCH_DY: INVALID_FOR_INTERVAL; if (S_TM(suf)) - strcpy(inout, localize_day(tm->tm_wday)); + { + strcpy(workbuff, localize_day(tm->tm_wday)); + strcpy(inout, localized_str_toupper(workbuff)); + } else + { strcpy(inout, days_short[tm->tm_wday]); - str_toupper(inout); + str_toupper(inout); + } + return strlen(p_inout); case DCH_Dy: @@ -2309,10 +2417,15 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, case DCH_dy: INVALID_FOR_INTERVAL; if (S_TM(suf)) - strcpy(inout, localize_day(tm->tm_wday)); + { + strcpy(workbuff, localize_day(tm->tm_wday)); + strcpy(inout, localized_str_tolower(workbuff)); + } else + { strcpy(inout, days_short[tm->tm_wday]); - *inout = pg_tolower((unsigned char) *inout); + *inout = pg_tolower((unsigned char) *inout); + } return strlen(p_inout); case DCH_DDD: diff --git a/src/backend/utils/adt/oracle_compat.c b/src/backend/utils/adt/oracle_compat.c index c6c4175dd6..6da6b30cca 100644 --- a/src/backend/utils/adt/oracle_compat.c +++ b/src/backend/utils/adt/oracle_compat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/oracle_compat.c,v 1.68 2007/01/05 22:19:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/oracle_compat.c,v 1.69 2007/02/08 18:19:33 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -46,6 +46,8 @@ */ #if defined(HAVE_WCSTOMBS) && defined(HAVE_TOWLOWER) #define USE_WIDE_UPPER_LOWER +char *wstring_lower (char *str); +char *wstring_upper(char *str); #endif static text *dotrim(const char *string, int stringlen, @@ -258,6 +260,80 @@ win32_wcstotext(const wchar_t *str, int ncodes) #define wcstotext win32_wcstotext #endif /* WIN32 */ +#ifdef USE_WIDE_UPPER_LOWER +/* + * string_upper and string_lower are used for correct multibyte upper/lower + * transformations localized strings. Returns pointers to transformated + * string. + */ +char * +wstring_upper(char *str) +{ + wchar_t *workspace; + text *in_text; + text *out_text; + char *result; + int nbytes = strlen(str); + int i; + + in_text = palloc(nbytes + VARHDRSZ); + memcpy(VARDATA(in_text), str, nbytes); + VARATT_SIZEP(in_text) = nbytes + VARHDRSZ; + + workspace = texttowcs(in_text); + + for (i = 0; workspace[i] != 0; i++) + workspace[i] = towupper(workspace[i]); + + out_text = wcstotext(workspace, i); + + nbytes = VARSIZE(out_text) - VARHDRSZ; + result = palloc(nbytes + 1); + memcpy(result, VARDATA(out_text), nbytes); + + result[nbytes] = '\0'; + + pfree(workspace); + pfree(in_text); + pfree(out_text); + + return result; +} + +char * +wstring_lower(char *str) +{ + wchar_t *workspace; + text *in_text; + text *out_text; + char *result; + int nbytes = strlen(str); + int i; + + in_text = palloc(nbytes + VARHDRSZ); + memcpy(VARDATA(in_text), str, nbytes); + VARATT_SIZEP(in_text) = nbytes + VARHDRSZ; + + workspace = texttowcs(in_text); + + for (i = 0; workspace[i] != 0; i++) + workspace[i] = towlower(workspace[i]); + + out_text = wcstotext(workspace, i); + + nbytes = VARSIZE(out_text) - VARHDRSZ; + result = palloc(nbytes + 1); + memcpy(result, VARDATA(out_text), nbytes); + + result[nbytes] = '\0'; + + pfree(workspace); + pfree(in_text); + pfree(out_text); + + return result; +} +#endif /* USE_WIDE_UPPER_LOWER */ /******************************************************************** *