I wrote:
>> One question that I've got is why the ICU portion refuses to load
>> any entries unless is_encoding_supported_by_icu(GetDatabaseEncoding()).
>> Surely this is completely wrong?  I should think that what we load into
>> pg_collation ought to be independent of template1's encoding, the same
>> as it is for libc collations, and the right place to be making a test
>> like that is where somebody attempts to use an ICU collation.

Pursuant to the second part of that: I checked on what happens if you
try to use an ICU collation in a database with a not-supported-by-ICU
encoding.  We have to cope with that scenario even with the current
(broken IMO) initdb behavior, because even if template1 has a supported
encoding, it's possible to create another database that doesn't.
It does fail more or less cleanly; you get an "encoding "foo" not
supported by ICU" message at runtime (out of get_encoding_name_for_icu).
But that's quite a bit unlike the behavior for libc collations: with
those, you get an error in collation name lookup, along the lines of

collation "en_DK.utf8" for encoding "SQL_ASCII" does not exist

The attached proposed patch makes the behavior for ICU collations the
same, by dint of injecting the is_encoding_supported_by_icu() check
into collation name lookup.

                        regards, tom lane

diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 64f6fee..029a132 100644
*** a/src/backend/catalog/namespace.c
--- b/src/backend/catalog/namespace.c
*************** OpfamilyIsVisible(Oid opfid)
*** 1915,1923 ****
--- 1915,1974 ----
  }
  
  /*
+  * lookup_collation
+  *		If there's a collation of the given name/namespace, and it works
+  *		with the given encoding, return its OID.  Else return InvalidOid.
+  */
+ static Oid
+ lookup_collation(const char *collname, Oid collnamespace, int32 encoding)
+ {
+ 	Oid			collid;
+ 	HeapTuple	colltup;
+ 	Form_pg_collation collform;
+ 
+ 	/* Check for encoding-specific entry (exact match) */
+ 	collid = GetSysCacheOid3(COLLNAMEENCNSP,
+ 							 PointerGetDatum(collname),
+ 							 Int32GetDatum(encoding),
+ 							 ObjectIdGetDatum(collnamespace));
+ 	if (OidIsValid(collid))
+ 		return collid;
+ 
+ 	/*
+ 	 * Check for any-encoding entry.  This takes a bit more work: while libc
+ 	 * collations with collencoding = -1 do work with all encodings, ICU
+ 	 * collations only work with certain encodings, so we have to check that
+ 	 * aspect before deciding it's a match.
+ 	 */
+ 	colltup = SearchSysCache3(COLLNAMEENCNSP,
+ 							  PointerGetDatum(collname),
+ 							  Int32GetDatum(-1),
+ 							  ObjectIdGetDatum(collnamespace));
+ 	if (!HeapTupleIsValid(colltup))
+ 		return InvalidOid;
+ 	collform = (Form_pg_collation) GETSTRUCT(colltup);
+ 	if (collform->collprovider == COLLPROVIDER_ICU)
+ 	{
+ 		if (is_encoding_supported_by_icu(encoding))
+ 			collid = HeapTupleGetOid(colltup);
+ 		else
+ 			collid = InvalidOid;
+ 	}
+ 	else
+ 	{
+ 		collid = HeapTupleGetOid(colltup);
+ 	}
+ 	ReleaseSysCache(colltup);
+ 	return collid;
+ }
+ 
+ /*
   * CollationGetCollid
   *		Try to resolve an unqualified collation name.
   *		Returns OID if collation found in search path, else InvalidOid.
+  *
+  * Note that this will only find collations that work with the current
+  * database's encoding.
   */
  Oid
  CollationGetCollid(const char *collname)
*************** CollationGetCollid(const char *collname)
*** 1935,1953 ****
  		if (namespaceId == myTempNamespace)
  			continue;			/* do not look in temp namespace */
  
! 		/* Check for database-encoding-specific entry */
! 		collid = GetSysCacheOid3(COLLNAMEENCNSP,
! 								 PointerGetDatum(collname),
! 								 Int32GetDatum(dbencoding),
! 								 ObjectIdGetDatum(namespaceId));
! 		if (OidIsValid(collid))
! 			return collid;
! 
! 		/* Check for any-encoding entry */
! 		collid = GetSysCacheOid3(COLLNAMEENCNSP,
! 								 PointerGetDatum(collname),
! 								 Int32GetDatum(-1),
! 								 ObjectIdGetDatum(namespaceId));
  		if (OidIsValid(collid))
  			return collid;
  	}
--- 1986,1992 ----
  		if (namespaceId == myTempNamespace)
  			continue;			/* do not look in temp namespace */
  
! 		collid = lookup_collation(collname, namespaceId, dbencoding);
  		if (OidIsValid(collid))
  			return collid;
  	}
*************** CollationGetCollid(const char *collname)
*** 1961,1966 ****
--- 2000,2008 ----
   *		Determine whether a collation (identified by OID) is visible in the
   *		current search path.  Visible means "would be found by searching
   *		for the unqualified collation name".
+  *
+  * Note that only collations that work with the current database's encoding
+  * will be considered visible.
   */
  bool
  CollationIsVisible(Oid collid)
*************** CollationIsVisible(Oid collid)
*** 1990,1998 ****
  	{
  		/*
  		 * If it is in the path, it might still not be visible; it could be
! 		 * hidden by another conversion of the same name earlier in the path.
! 		 * So we must do a slow check to see if this conversion would be found
! 		 * by CollationGetCollid.
  		 */
  		char	   *collname = NameStr(collform->collname);
  
--- 2032,2041 ----
  	{
  		/*
  		 * If it is in the path, it might still not be visible; it could be
! 		 * hidden by another collation of the same name earlier in the path,
! 		 * or it might not work with the current DB encoding.  So we must do a
! 		 * slow check to see if this collation would be found by
! 		 * CollationGetCollid.
  		 */
  		char	   *collname = NameStr(collform->collname);
  
*************** PopOverrideSearchPath(void)
*** 3442,3447 ****
--- 3485,3493 ----
  
  /*
   * get_collation_oid - find a collation by possibly qualified name
+  *
+  * Note that this will only find collations that work with the current
+  * database's encoding.
   */
  Oid
  get_collation_oid(List *name, bool missing_ok)
*************** get_collation_oid(List *name, bool missi
*** 3463,3479 ****
  		if (missing_ok && !OidIsValid(namespaceId))
  			return InvalidOid;
  
! 		/* first try for encoding-specific entry, then any-encoding */
! 		colloid = GetSysCacheOid3(COLLNAMEENCNSP,
! 								  PointerGetDatum(collation_name),
! 								  Int32GetDatum(dbencoding),
! 								  ObjectIdGetDatum(namespaceId));
! 		if (OidIsValid(colloid))
! 			return colloid;
! 		colloid = GetSysCacheOid3(COLLNAMEENCNSP,
! 								  PointerGetDatum(collation_name),
! 								  Int32GetDatum(-1),
! 								  ObjectIdGetDatum(namespaceId));
  		if (OidIsValid(colloid))
  			return colloid;
  	}
--- 3509,3515 ----
  		if (missing_ok && !OidIsValid(namespaceId))
  			return InvalidOid;
  
! 		colloid = lookup_collation(collation_name, namespaceId, dbencoding);
  		if (OidIsValid(colloid))
  			return colloid;
  	}
*************** get_collation_oid(List *name, bool missi
*** 3489,3504 ****
  			if (namespaceId == myTempNamespace)
  				continue;		/* do not look in temp namespace */
  
! 			colloid = GetSysCacheOid3(COLLNAMEENCNSP,
! 									  PointerGetDatum(collation_name),
! 									  Int32GetDatum(dbencoding),
! 									  ObjectIdGetDatum(namespaceId));
! 			if (OidIsValid(colloid))
! 				return colloid;
! 			colloid = GetSysCacheOid3(COLLNAMEENCNSP,
! 									  PointerGetDatum(collation_name),
! 									  Int32GetDatum(-1),
! 									  ObjectIdGetDatum(namespaceId));
  			if (OidIsValid(colloid))
  				return colloid;
  		}
--- 3525,3531 ----
  			if (namespaceId == myTempNamespace)
  				continue;		/* do not look in temp namespace */
  
! 			colloid = lookup_collation(collation_name, namespaceId, dbencoding);
  			if (OidIsValid(colloid))
  				return colloid;
  		}
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to