From b577aa9ebc780c5095f213d36253f63580b2a067 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
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 cbc10964995..91ef1ea9dca 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 c6c4175dd6b..6da6b30cca3 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 */
 
 /********************************************************************
  *
-- 
GitLab