*** src/backend/commands/define.c~	Sun Aug  3 22:39:58 2003
--- src/backend/commands/define.c	Fri Feb 20 18:52:22 2004
***************
*** 38,61 ****
  #include "catalog/namespace.h"
  #include "commands/defrem.h"
  #include "parser/parse_type.h"
  #include "utils/int8.h"
  
  
  /*
!  * Translate the input language name to lower case.
   *
!  * Output buffer must be NAMEDATALEN long.
   */
! void
! case_translate_language_name(const char *input, char *output)
  {
! 	int			i;
! 
! 	MemSet(output, 0, NAMEDATALEN);		/* ensure result Name is
! 										 * zero-filled */
! 
! 	for (i = 0; i < NAMEDATALEN - 1 && input[i]; ++i)
! 		output[i] = tolower((unsigned char) input[i]);
  }
  
  
--- 38,56 ----
  #include "catalog/namespace.h"
  #include "commands/defrem.h"
  #include "parser/parse_type.h"
+ #include "parser/scansup.h"
  #include "utils/int8.h"
  
  
  /*
!  * Translate the input language name to lower case, and truncate if needed.
   *
!  * Returns a palloc'd string
   */
! char *
! case_translate_language_name(const char *input)
  {
! 	return downcase_truncate_identifier(input, strlen(input), false);
  }
  
  
*** src/backend/commands/functioncmds.c~	Thu Oct  2 02:34:03 2003
--- src/backend/commands/functioncmds.c	Fri Feb 20 18:52:23 2004
***************
*** 393,399 ****
  	Oid			prorettype;
  	bool		returnsSet;
  	char	   *language;
! 	char		languageName[NAMEDATALEN];
  	Oid			languageOid;
  	Oid			languageValidator;
  	char	   *funcname;
--- 393,399 ----
  	Oid			prorettype;
  	bool		returnsSet;
  	char	   *language;
! 	char	   *languageName;
  	Oid			languageOid;
  	Oid			languageValidator;
  	char	   *funcname;
***************
*** 428,434 ****
  			   &as_clause, &language, &volatility, &isStrict, &security);
  
  	/* Convert language name to canonical case */
! 	case_translate_language_name(language, languageName);
  
  	/* Look up the language and validate permissions */
  	languageTuple = SearchSysCache(LANGNAME,
--- 428,434 ----
  			   &as_clause, &language, &volatility, &isStrict, &security);
  
  	/* Convert language name to canonical case */
! 	languageName = case_translate_language_name(language);
  
  	/* Look up the language and validate permissions */
  	languageTuple = SearchSysCache(LANGNAME,
*** src/backend/commands/proclang.c~	Thu Oct  2 02:34:03 2003
--- src/backend/commands/proclang.c	Fri Feb 20 18:52:23 2004
***************
*** 40,50 ****
  void
  CreateProceduralLanguage(CreatePLangStmt *stmt)
  {
! 	char		languageName[NAMEDATALEN];
  	Oid			procOid,
  				valProcOid;
  	Oid			funcrettype;
  	Oid			typev[FUNC_MAX_ARGS];
  	char		nulls[Natts_pg_language];
  	Datum		values[Natts_pg_language];
  	Relation	rel;
--- 40,51 ----
  void
  CreateProceduralLanguage(CreatePLangStmt *stmt)
  {
! 	char	   *languageName;
  	Oid			procOid,
  				valProcOid;
  	Oid			funcrettype;
  	Oid			typev[FUNC_MAX_ARGS];
+ 	NameData	langname;
  	char		nulls[Natts_pg_language];
  	Datum		values[Natts_pg_language];
  	Relation	rel;
***************
*** 66,72 ****
  	 * Translate the language name and check that this language doesn't
  	 * already exist
  	 */
! 	case_translate_language_name(stmt->plname, languageName);
  
  	if (SearchSysCacheExists(LANGNAME,
  							 PointerGetDatum(languageName),
--- 67,73 ----
  	 * Translate the language name and check that this language doesn't
  	 * already exist
  	 */
! 	languageName = case_translate_language_name(stmt->plname);
  
  	if (SearchSysCacheExists(LANGNAME,
  							 PointerGetDatum(languageName),
***************
*** 124,135 ****
  	}
  
  	i = 0;
! 	values[i++] = PointerGetDatum(languageName);
! 	values[i++] = BoolGetDatum(true);	/* lanispl */
! 	values[i++] = BoolGetDatum(stmt->pltrusted);
! 	values[i++] = ObjectIdGetDatum(procOid);
! 	values[i++] = ObjectIdGetDatum(valProcOid);
! 	nulls[i] = 'n';				/* lanacl */
  
  	rel = heap_openr(LanguageRelationName, RowExclusiveLock);
  
--- 125,137 ----
  	}
  
  	i = 0;
! 	namestrcpy(&langname, languageName);
! 	values[i++] = NameGetDatum(&langname);			/* lanname */
! 	values[i++] = BoolGetDatum(true);				/* lanispl */
! 	values[i++] = BoolGetDatum(stmt->pltrusted);	/* lanpltrusted */
! 	values[i++] = ObjectIdGetDatum(procOid);		/* lanplcallfoid */
! 	values[i++] = ObjectIdGetDatum(valProcOid);		/* lanvalidator */
! 	nulls[i] = 'n';									/* lanacl */
  
  	rel = heap_openr(LanguageRelationName, RowExclusiveLock);
  
***************
*** 173,179 ****
  void
  DropProceduralLanguage(DropPLangStmt *stmt)
  {
! 	char		languageName[NAMEDATALEN];
  	HeapTuple	langTup;
  	ObjectAddress object;
  
--- 175,181 ----
  void
  DropProceduralLanguage(DropPLangStmt *stmt)
  {
! 	char	   *languageName;
  	HeapTuple	langTup;
  	ObjectAddress object;
  
***************
*** 189,195 ****
  	 * Translate the language name, check that this language exist and is
  	 * a PL
  	 */
! 	case_translate_language_name(stmt->plname, languageName);
  
  	langTup = SearchSysCache(LANGNAME,
  							 CStringGetDatum(languageName),
--- 191,197 ----
  	 * Translate the language name, check that this language exist and is
  	 * a PL
  	 */
! 	languageName = case_translate_language_name(stmt->plname);
  
  	langTup = SearchSysCache(LANGNAME,
  							 CStringGetDatum(languageName),
*** src/backend/parser/keywords.c~	Sun Aug  3 22:40:01 2003
--- src/backend/parser/keywords.c	Fri Feb 20 18:52:23 2004
***************
*** 365,381 ****
  
  	/*
  	 * Apply an ASCII-only downcasing.	We must not use tolower() since it
! 	 * may produce the wrong translation in some locales (eg, Turkish),
! 	 * and we don't trust isupper() very much either.  In an ASCII-based
! 	 * encoding the tests against A and Z are sufficient, but we also
! 	 * check isupper() so that we will work correctly under EBCDIC.  The
! 	 * actual case conversion step should work for either ASCII or EBCDIC.
  	 */
  	for (i = 0; i < len; i++)
  	{
  		char		ch = text[i];
  
! 		if (ch >= 'A' && ch <= 'Z' && isupper((unsigned char) ch))
  			ch += 'a' - 'A';
  		word[i] = ch;
  	}
--- 365,377 ----
  
  	/*
  	 * Apply an ASCII-only downcasing.	We must not use tolower() since it
! 	 * may produce the wrong translation in some locales (eg, Turkish).
  	 */
  	for (i = 0; i < len; i++)
  	{
  		char		ch = text[i];
  
! 		if (ch >= 'A' && ch <= 'Z')
  			ch += 'a' - 'A';
  		word[i] = ch;
  	}
*** src/backend/parser/scan.l~	Thu Oct  9 15:13:23 2003
--- src/backend/parser/scan.l	Fri Feb 20 18:52:23 2004
***************
*** 26,31 ****
--- 26,32 ----
  #include "parser/keywords.h"
  /* Not needed now that this file is compiled as part of gram.y */
  /* #include "parser/parse.h" */
+ #include "parser/scansup.h"
  #include "utils/builtins.h"
  #include "mb/pg_wchar.h"
  
***************
*** 394,416 ****
  					startlit();
  				}
  <xd>{xdstop}	{
  					BEGIN(INITIAL);
  					if (literallen == 0)
  						yyerror("zero-length delimited identifier");
  					if (literallen >= NAMEDATALEN)
! 					{
! 						int len;
! 
! 						len = pg_mbcliplen(literalbuf, literallen,
! 										   NAMEDATALEN-1);
! 						ereport(NOTICE,
! 								(errcode(ERRCODE_NAME_TOO_LONG),
! 								 errmsg("identifier \"%s\" will be truncated to \"%.*s\"",
! 										literalbuf, len, literalbuf)));
! 						literalbuf[len] = '\0';
! 						literallen = len;
! 					}
! 					yylval.str = litbufdup();
  					return IDENT;
  				}
  <xd>{xddouble} {
--- 395,409 ----
  					startlit();
  				}
  <xd>{xdstop}	{
+ 					char		   *ident;
+ 
  					BEGIN(INITIAL);
  					if (literallen == 0)
  						yyerror("zero-length delimited identifier");
+ 					ident = litbufdup();
  					if (literallen >= NAMEDATALEN)
! 						truncate_identifier(ident, literallen, true);
! 					yylval.str = ident;
  					return IDENT;
  				}
  <xd>{xddouble} {
***************
*** 532,538 ****
  {identifier}	{
  					const ScanKeyword *keyword;
  					char		   *ident;
- 					int				i;
  
  					/* Is it a keyword? */
  					keyword = ScanKeywordLookup(yytext);
--- 525,530 ----
***************
*** 545,572 ****
  					/*
  					 * No.  Convert the identifier to lower case, and truncate
  					 * if necessary.
- 					 *
- 					 * Note: here we use a locale-dependent case conversion,
- 					 * which seems appropriate under standard SQL rules, whereas
- 					 * the keyword comparison was NOT locale-dependent.
  					 */
! 					ident = pstrdup(yytext);
! 					for (i = 0; ident[i]; i++)
! 					{
! 						if (isupper((unsigned char) ident[i]))
! 							ident[i] = tolower((unsigned char) ident[i]);
! 					}
! 					if (i >= NAMEDATALEN)
!                     {
! 						int len;
! 
! 						len = pg_mbcliplen(ident, i, NAMEDATALEN-1);
! 						ereport(NOTICE,
! 								(errcode(ERRCODE_NAME_TOO_LONG),
! 								 errmsg("identifier \"%s\" will be truncated to \"%.*s\"",
! 										ident, len, ident)));
! 						ident[len] = '\0';
!                     }
  					yylval.str = ident;
  					return IDENT;
  				}
--- 537,544 ----
  					/*
  					 * No.  Convert the identifier to lower case, and truncate
  					 * if necessary.
  					 */
! 					ident = downcase_truncate_identifier(yytext, yyleng, true);
  					yylval.str = ident;
  					return IDENT;
  				}
*** src/backend/parser/scansup.c~	Sun Aug  3 22:40:02 2003
--- src/backend/parser/scansup.c	Fri Feb 20 18:52:23 2004
***************
*** 19,24 ****
--- 19,26 ----
  
  #include "miscadmin.h"
  #include "parser/scansup.h"
+ #include "mb/pg_wchar.h"
+ 
  
  /* ----------------
   *		scanstr
***************
*** 32,38 ****
   */
  
  char *
! scanstr(char *s)
  {
  	char	   *newStr;
  	int			len,
--- 34,40 ----
   */
  
  char *
! scanstr(const char *s)
  {
  	char	   *newStr;
  	int			len,
***************
*** 108,111 ****
--- 110,185 ----
  	}
  	newStr[j] = '\0';
  	return newStr;
+ }
+ 
+ 
+ /*
+  * downcase_truncate_identifier() --- do appropriate downcasing and
+  * truncation of an unquoted identifier.  Optionally warn of truncation.
+  *
+  * Returns a palloc'd string containing the adjusted identifier.
+  *
+  * Note: in some usages the passed string is not null-terminated.
+  *
+  * Note: the API of this function is designed to allow for downcasing
+  * transformations that increase the string length, but we don't yet
+  * support that.  If you want to implement it, you'll need to fix
+  * SplitIdentifierString() in utils/adt/varlena.c.
+  */
+ char *
+ downcase_truncate_identifier(const char *ident, int len, bool warn)
+ {
+ 	char	   *result;
+ 	int			i;
+ 
+ 	result = palloc(len + 1);
+ 	/*
+ 	 * SQL99 specifies Unicode-aware case normalization, which we don't yet
+ 	 * have the infrastructure for.  Instead we use tolower() to provide a
+ 	 * locale-aware translation.  However, there are some locales where this
+ 	 * is not right either (eg, Turkish may do strange things with 'i' and
+ 	 * 'I').  Our current compromise is to use tolower() for characters with
+ 	 * the high bit set, and use an ASCII-only downcasing for 7-bit
+ 	 * characters.
+ 	 */
+ 	for (i = 0; i < len; i++)
+ 	{
+ 		unsigned char	ch = (unsigned char) ident[i];
+ 
+ 		if (ch >= 'A' && ch <= 'Z')
+ 			ch += 'a' - 'A';
+ 		else if (ch >= 0x80 && isupper(ch))
+ 			ch = tolower(ch);
+ 		result[i] = (char) ch;
+ 	}
+ 	result[i] = '\0';
+ 
+ 	if (i >= NAMEDATALEN)
+ 		truncate_identifier(result, i, warn);
+ 
+ 	return result;
+ }
+ 
+ /*
+  * truncate_identifier() --- truncate an identifier to NAMEDATALEN-1 bytes.
+  *
+  * The given string is modified in-place, if necessary.  A warning is
+  * issued if requested.
+  *
+  * We require the caller to pass in the string length since this saves a
+  * strlen() call in some common usages.
+  */
+ void
+ truncate_identifier(char *ident, int len, bool warn)
+ {
+ 	if (len >= NAMEDATALEN)
+ 	{
+ 		len = pg_mbcliplen(ident, len, NAMEDATALEN-1);
+ 		if (warn)
+ 			ereport(NOTICE,
+ 					(errcode(ERRCODE_NAME_TOO_LONG),
+ 					 errmsg("identifier \"%s\" will be truncated to \"%.*s\"",
+ 							ident, len, ident)));
+ 		ident[len] = '\0';
+ 	}
  }
*** src/backend/utils/adt/varlena.c~	Fri Jan 30 19:45:34 2004
--- src/backend/utils/adt/varlena.c	Fri Feb 20 18:52:23 2004
***************
*** 16,32 ****
  
  #include <ctype.h>
  
- #include "mb/pg_wchar.h"
- #include "miscadmin.h"
  #include "access/tuptoaster.h"
  #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
- #include "utils/pg_locale.h"
  #include "utils/lsyscache.h"
  
  
  typedef struct varlena unknown;
--- 16,33 ----
  
  #include <ctype.h>
  
  #include "access/tuptoaster.h"
  #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "mb/pg_wchar.h"
+ #include "miscadmin.h"
+ #include "parser/scansup.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
+ #include "utils/pg_locale.h"
  
  
  typedef struct varlena unknown;
***************
*** 1681,1687 ****
  	{
  		char	   *curname;
  		char	   *endp;
- 		int			curlen;
  
  		if (*nextp == '\"')
  		{
--- 1682,1687 ----
***************
*** 1704,1724 ****
  		else
  		{
  			/* Unquoted name --- extends to separator or whitespace */
  			curname = nextp;
  			while (*nextp && *nextp != separator &&
  				   !isspace((unsigned char) *nextp))
- 			{
- 				/*
- 				 * It's important that this match the identifier
- 				 * downcasing code used by backend/parser/scan.l.
- 				 */
- 				if (isupper((unsigned char) *nextp))
- 					*nextp = tolower((unsigned char) *nextp);
  				nextp++;
- 			}
  			endp = nextp;
  			if (curname == nextp)
  				return false;	/* empty unquoted name not allowed */
  		}
  
  		while (isspace((unsigned char) *nextp))
--- 1704,1733 ----
  		else
  		{
  			/* Unquoted name --- extends to separator or whitespace */
+ 			char	   *downname;
+ 			int			len;
+ 
  			curname = nextp;
  			while (*nextp && *nextp != separator &&
  				   !isspace((unsigned char) *nextp))
  				nextp++;
  			endp = nextp;
  			if (curname == nextp)
  				return false;	/* empty unquoted name not allowed */
+ 			/*
+ 			 * Downcase the identifier, using same code as main lexer does.
+ 			 *
+ 			 * XXX because we want to overwrite the input in-place, we cannot
+ 			 * support a downcasing transformation that increases the
+ 			 * string length.  This is not a problem given the current
+ 			 * implementation of downcase_truncate_identifier, but we'll
+ 			 * probably have to do something about this someday.
+ 			 */
+ 			len = endp - curname;
+ 			downname = downcase_truncate_identifier(curname, len, false);
+ 			Assert(strlen(downname) <= len);
+ 			strncpy(curname, downname, len);
+ 			pfree(downname);
  		}
  
  		while (isspace((unsigned char) *nextp))
***************
*** 1739,1751 ****
  		/* Now safe to overwrite separator with a null */
  		*endp = '\0';
  
! 		/* Truncate name if it's overlength; again, should match scan.l */
! 		curlen = strlen(curname);
! 		if (curlen >= NAMEDATALEN)
! 		{
! 			curlen = pg_mbcliplen(curname, curlen, NAMEDATALEN - 1);
! 			curname[curlen] = '\0';
! 		}
  
  		/*
  		 * Finished isolating current name --- add it to list
--- 1748,1755 ----
  		/* Now safe to overwrite separator with a null */
  		*endp = '\0';
  
! 		/* Truncate name if it's overlength */
! 		truncate_identifier(curname, strlen(curname), false);
  
  		/*
  		 * Finished isolating current name --- add it to list
*** src/include/commands/defrem.h~	Sun Aug  3 22:40:13 2003
--- src/include/commands/defrem.h	Fri Feb 20 18:52:23 2004
***************
*** 61,67 ****
  
  /* support routines in commands/define.c */
  
! extern void case_translate_language_name(const char *input, char *output);
  
  extern char *defGetString(DefElem *def);
  extern double defGetNumeric(DefElem *def);
--- 61,67 ----
  
  /* support routines in commands/define.c */
  
! extern char *case_translate_language_name(const char *input);
  
  extern char *defGetString(DefElem *def);
  extern double defGetNumeric(DefElem *def);
*** src/include/parser/scansup.h~	Sun Aug  3 22:40:14 2003
--- src/include/parser/scansup.h	Fri Feb 20 18:52:23 2004
***************
*** 15,20 ****
  #ifndef SCANSUP_H
  #define SCANSUP_H
  
! extern char *scanstr(char *s);
  
  #endif   /* SCANSUP_H */
--- 15,25 ----
  #ifndef SCANSUP_H
  #define SCANSUP_H
  
! extern char *scanstr(const char *s);
! 
! extern char *downcase_truncate_identifier(const char *ident, int len,
! 										  bool warn);
! 
! extern void truncate_identifier(char *ident, int len, bool warn);
  
  #endif   /* SCANSUP_H */
*** src/pl/plpgsql/src/pl_funcs.c~	Thu Sep 25 19:02:12 2003
--- src/pl/plpgsql/src/pl_funcs.c	Fri Feb 20 18:52:23 2004
***************
*** 40,46 ****
  
  #include <ctype.h>
  
! #include "mb/pg_wchar.h"
  
  
  /* ----------
--- 40,46 ----
  
  #include <ctype.h>
  
! #include "parser/scansup.h"
  
  
  /* ----------
***************
*** 348,362 ****
  	{
  		char	   *curident;
  		char	   *cp;
- 		int			i;
  
  		/* Process current identifier */
- 		curident = palloc(strlen(s) + 1);		/* surely enough room */
- 		cp = curident;
  
  		if (*s == '"')
  		{
  			/* Quoted identifier: copy, collapsing out doubled quotes */
  			s++;
  			while (*s)
  			{
--- 348,362 ----
  	{
  		char	   *curident;
  		char	   *cp;
  
  		/* Process current identifier */
  
  		if (*s == '"')
  		{
  			/* Quoted identifier: copy, collapsing out doubled quotes */
+ 
+ 			curident = palloc(strlen(s) + 1); /* surely enough room */
+ 			cp = curident;
  			s++;
  			while (*s)
  			{
***************
*** 373,407 ****
  						(errcode(ERRCODE_SYNTAX_ERROR),
  						 errmsg("unterminated \" in name: %s", sstart)));
  			s++;
  		}
  		else
  		{
! 			/*
! 			 * Normal identifier: downcase, stop at dot or whitespace.
! 			 *
! 			 * Note that downcasing is locale-sensitive, following SQL99
! 			 * rules for identifiers.  We have already decided that the
! 			 * item is not a PLPGSQL keyword.
! 			 */
! 			while (*s && *s != '.' && !isspace((unsigned char) *s))
! 			{
! 				if (isupper((unsigned char) *s))
! 					*cp++ = tolower((unsigned char) *s++);
! 				else
! 					*cp++ = *s++;
! 			}
! 		}
  
! 		/* Truncate to NAMEDATALEN */
! 		*cp = '\0';
! 		i = cp - curident;
! 
! 		if (i >= NAMEDATALEN)
! 		{
! 			int			len;
! 
! 			len = pg_mbcliplen(curident, i, NAMEDATALEN - 1);
! 			curident[len] = '\0';
  		}
  
  		/* Pass ident to caller */
--- 373,392 ----
  						(errcode(ERRCODE_SYNTAX_ERROR),
  						 errmsg("unterminated \" in name: %s", sstart)));
  			s++;
+ 			*cp = '\0';
+ 			/* Truncate to NAMEDATALEN */
+ 			truncate_identifier(curident, cp-curident, false);
  		}
  		else
  		{
! 			/* Normal identifier: extends till dot or whitespace */
! 			const char *thisstart = s;
  
! 			while (*s && *s != '.' && !isspace((unsigned char) *s))
! 				s++;
! 			/* Downcase and truncate to NAMEDATALEN */
! 			curident = downcase_truncate_identifier(thisstart, s-thisstart,
! 													false);
  		}
  
  		/* Pass ident to caller */
