On Wed, Feb 17, 2021 at 8:04 PM Michael Paquier <mich...@paquier.xyz> wrote:
> On Wed, Feb 17, 2021 at 03:08:36PM +1300, Thomas Munro wrote:
> >               tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(oid));
> >               if (!HeapTupleIsValid(tp))
> > +             {
> > +                     if (found)
> > +                     {
> > +                             *found = false;
> > +                             return NULL;
> > +                     }
> >                       elog(ERROR, "cache lookup failed for collation %u", 
> > oid);
> > +             }
> >               collform = (Form_pg_collation) GETSTRUCT(tp);
> >               version = get_collation_actual_version(collform->collprovider,
> >                                                                             
> >              NameStr(collform->collcollate));
> > +             if (found)
> > +                     *found = true;
> >       }
>
> FWIW, we usually prefer using NULL instead of an error for the result
> of a system function if an object cannot be found because it allows
> users to not get failures in a middle of a full table scan if things
> like an InvalidOid is mixed in the data set.  For example, we do that
> in the partition functions, for objectaddress functions, etc.  That
> would make this patch set simpler, switching
> get_collation_version_for_oid() to just use a missing_ok argument.
> And that would be more consistent with the other syscache lookup
> functions we have here and there in the tree.

I guess I was trying to preserve a distinction between "unknown OID"
and "this is a collation OID, but I don't have version information for
it" (for example, "C.utf8").  But it hardly matters, and your
suggestion works for me.  Thanks for looking!
From 36acff2cdb3dbde81b82ca425b61b0b8a62e27c9 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Wed, 17 Feb 2021 14:10:40 +1300
Subject: [PATCH v2 1/4] Hide internal error for
 pg_collation_actual_version(<bad OID>).

Instead of an unsightly internal "cache lookup failed" message, just
return NULL for bad OIDs, as seems to be the convention for other
similar things.

Reported-by: Justin Pryzby <pry...@telsasoft.com>
Reviewed-by: Michael Paquier <mich...@paquier.xyz>
Discussion: https://postgr.es/m/20210117215940.GE8560%40telsasoft.com
---
 src/backend/catalog/index.c          | 5 +++--
 src/backend/catalog/pg_depend.c      | 3 ++-
 src/backend/commands/collationcmds.c | 2 +-
 src/backend/utils/adt/pg_locale.c    | 9 +++++++--
 src/include/utils/pg_locale.h        | 2 +-
 5 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index b4ab0b88ad..a9e3679631 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1290,7 +1290,8 @@ do_collation_version_check(const ObjectAddress *otherObject,
 		return false;
 
 	/* Ask the provider for the current version.  Give up if unsupported. */
-	current_version = get_collation_version_for_oid(otherObject->objectId);
+	current_version = get_collation_version_for_oid(otherObject->objectId,
+													false);
 	if (!current_version)
 		return false;
 
@@ -1369,7 +1370,7 @@ do_collation_version_update(const ObjectAddress *otherObject,
 	if (OidIsValid(*coll) && otherObject->objectId != *coll)
 		return false;
 
-	*new_version = get_collation_version_for_oid(otherObject->objectId);
+	*new_version = get_collation_version_for_oid(otherObject->objectId, false);
 
 	return true;
 }
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 63da24322d..362db7fe91 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -116,7 +116,8 @@ recordMultipleDependencies(const ObjectAddress *depender,
 					referenced->objectId == POSIX_COLLATION_OID)
 					continue;
 
-				version = get_collation_version_for_oid(referenced->objectId);
+				version = get_collation_version_for_oid(referenced->objectId,
+														false);
 
 				/*
 				 * Default collation is pinned, so we need to force recording
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 9634ae6809..a7ee452e19 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -273,7 +273,7 @@ pg_collation_actual_version(PG_FUNCTION_ARGS)
 	Oid			collid = PG_GETARG_OID(0);
 	char	   *version;
 
-	version = get_collation_version_for_oid(collid);
+	version = get_collation_version_for_oid(collid, true);
 
 	if (version)
 		PG_RETURN_TEXT_P(cstring_to_text(version));
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index e9c1231f9b..34b82b9335 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1726,10 +1726,11 @@ get_collation_actual_version(char collprovider, const char *collcollate)
 /*
  * Get provider-specific collation version string for a given collation OID.
  * Return NULL if the provider doesn't support versions, or the collation is
- * unversioned (for example "C").
+ * unversioned (for example "C").  Unknown OIDs result in NULL if missing_ok is
+ * true.
  */
 char *
-get_collation_version_for_oid(Oid oid)
+get_collation_version_for_oid(Oid oid, bool missing_ok)
 {
 	HeapTuple	tp;
 	char	   *version;
@@ -1751,7 +1752,11 @@ get_collation_version_for_oid(Oid oid)
 
 		tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(oid));
 		if (!HeapTupleIsValid(tp))
+		{
+			if (missing_ok)
+				return NULL;
 			elog(ERROR, "cache lookup failed for collation %u", oid);
+		}
 		collform = (Form_pg_collation) GETSTRUCT(tp);
 		version = get_collation_actual_version(collform->collprovider,
 											   NameStr(collform->collcollate));
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index 34dff74bd1..5a37caefbe 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -103,7 +103,7 @@ typedef struct pg_locale_struct *pg_locale_t;
 
 extern pg_locale_t pg_newlocale_from_collation(Oid collid);
 
-extern char *get_collation_version_for_oid(Oid collid);
+extern char *get_collation_version_for_oid(Oid collid, bool missing_ok);
 
 #ifdef USE_ICU
 extern int32_t icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes);
-- 
2.30.0

From b52fad59748cf669c509552676907fcb87de8aec Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Wed, 17 Feb 2021 14:19:40 +1300
Subject: [PATCH v2 2/4] pg_collation_actual_version() ->
 pg_collation_current_version().

The new name seems a bit more natural.

Discussion: https://postgr.es/m/20210117215940.GE8560%40telsasoft.com
---
 doc/src/sgml/func.sgml                         |  8 ++++----
 src/backend/commands/collationcmds.c           |  2 +-
 src/backend/utils/adt/pg_locale.c              | 14 +++++++-------
 src/include/catalog/pg_proc.dat                |  4 ++--
 src/test/regress/expected/collate.icu.utf8.out |  2 +-
 src/test/regress/sql/collate.icu.utf8.sql      |  2 +-
 6 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 1ab31a9056..d8224272a5 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -26227,14 +26227,14 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
-         <primary>pg_collation_actual_version</primary>
+         <primary>pg_collation_current_version</primary>
         </indexterm>
-        <function>pg_collation_actual_version</function> ( <type>oid</type> )
+        <function>pg_collation_current_version</function> ( <type>oid</type> )
         <returnvalue>text</returnvalue>
        </para>
        <para>
-        Returns the actual version of the collation object as it is currently
-        installed in the operating system.  <literal>null</literal> is returned
+        Returns the version of the collation object as reported by the ICU
+        library or operating system.  <literal>null</literal> is returned
         on operating systems where <productname>PostgreSQL</productname>
         doesn't have support for versions.
        </para></entry>
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index a7ee452e19..4b76a6051d 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -268,7 +268,7 @@ IsThereCollationInNamespace(const char *collname, Oid nspOid)
 }
 
 Datum
-pg_collation_actual_version(PG_FUNCTION_ARGS)
+pg_collation_current_version(PG_FUNCTION_ARGS)
 {
 	Oid			collid = PG_GETARG_OID(0);
 	char	   *version;
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 34b82b9335..2e4c6e9a26 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -127,8 +127,8 @@ static char *IsoLocaleName(const char *);	/* MSVC specific */
 static void icu_set_collation_attributes(UCollator *collator, const char *loc);
 #endif
 
-static char *get_collation_actual_version(char collprovider,
-										  const char *collcollate);
+static char *get_collation_current_version(char collprovider,
+										   const char *collcollate);
 
 /*
  * pg_perm_setlocale
@@ -1610,7 +1610,7 @@ pg_newlocale_from_collation(Oid collid)
  * the operating system/library.
  */
 static char *
-get_collation_actual_version(char collprovider, const char *collcollate)
+get_collation_current_version(char collprovider, const char *collcollate)
 {
 	char	   *collversion = NULL;
 
@@ -1743,8 +1743,8 @@ get_collation_version_for_oid(Oid oid, bool missing_ok)
 		if (!HeapTupleIsValid(tp))
 			elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
 		dbform = (Form_pg_database) GETSTRUCT(tp);
-		version = get_collation_actual_version(COLLPROVIDER_LIBC,
-											   NameStr(dbform->datcollate));
+		version = get_collation_current_version(COLLPROVIDER_LIBC,
+												NameStr(dbform->datcollate));
 	}
 	else
 	{
@@ -1758,8 +1758,8 @@ get_collation_version_for_oid(Oid oid, bool missing_ok)
 			elog(ERROR, "cache lookup failed for collation %u", oid);
 		}
 		collform = (Form_pg_collation) GETSTRUCT(tp);
-		version = get_collation_actual_version(collform->collprovider,
-											   NameStr(collform->collcollate));
+		version = get_collation_current_version(collform->collprovider,
+												NameStr(collform->collcollate));
 	}
 
 	ReleaseSysCache(tp);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 1487710d59..16044125ba 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11321,9 +11321,9 @@
 
 { oid => '3448',
   descr => 'get actual version of collation from operating system',
-  proname => 'pg_collation_actual_version', procost => '100',
+  proname => 'pg_collation_current_version', procost => '100',
   provolatile => 'v', prorettype => 'text', proargtypes => 'oid',
-  prosrc => 'pg_collation_actual_version' },
+  prosrc => 'pg_collation_current_version' },
 
 # system management/monitoring related functions
 { oid => '3353', descr => 'list files in the log directory',
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index bc3752e923..970638ed37 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -2018,7 +2018,7 @@ SELECT objid::regclass::text collate "C", refobjid::regcollation::text collate "
 CASE
 WHEN refobjid = 'default'::regcollation THEN 'XXX' -- depends on libc version support
 WHEN refobjversion IS NULL THEN 'version not tracked'
-WHEN refobjversion = pg_collation_actual_version(refobjid) THEN 'up to date'
+WHEN refobjversion = pg_collation_current_version(refobjid) THEN 'up to date'
 ELSE 'out of date'
 END AS version
 FROM pg_depend d
diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql
index 0de2ed8d85..be5b464ffa 100644
--- a/src/test/regress/sql/collate.icu.utf8.sql
+++ b/src/test/regress/sql/collate.icu.utf8.sql
@@ -820,7 +820,7 @@ SELECT objid::regclass::text collate "C", refobjid::regcollation::text collate "
 CASE
 WHEN refobjid = 'default'::regcollation THEN 'XXX' -- depends on libc version support
 WHEN refobjversion IS NULL THEN 'version not tracked'
-WHEN refobjversion = pg_collation_actual_version(refobjid) THEN 'up to date'
+WHEN refobjversion = pg_collation_current_version(refobjid) THEN 'up to date'
 ELSE 'out of date'
 END AS version
 FROM pg_depend d
-- 
2.30.0

From df2a4625f40c4469b2331f6485e607c9d25c23aa Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Wed, 17 Feb 2021 14:36:43 +1300
Subject: [PATCH v2 3/4] Refactor get_collation_current_version().

The code paths for three different OSes finished up with three different
ways of excluding C[.xxx] and POSIX from consideration.  Merge them.

Discussion: https://postgr.es/m/20210117215940.GE8560%40telsasoft.com
---
 src/backend/utils/adt/pg_locale.c | 35 +++++--------------------------
 1 file changed, 5 insertions(+), 30 deletions(-)

diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 2e4c6e9a26..697d518e63 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1636,37 +1636,18 @@ get_collation_current_version(char collprovider, const char *collcollate)
 	}
 	else
 #endif
-	if (collprovider == COLLPROVIDER_LIBC)
+	if (collprovider == COLLPROVIDER_LIBC &&
+		pg_strcasecmp("C", collcollate) != 0 &&
+		pg_strncasecmp("C.", collcollate, 2) != 0 &&
+		pg_strcasecmp("POSIX", collcollate) != 0 &&
+		pg_strncasecmp("POSIX.", collcollate, 6) != 0)
 	{
 #if defined(__GLIBC__)
-		char	   *copy = pstrdup(collcollate);
-		char	   *copy_suffix = strstr(copy, ".");
-		bool		need_version = true;
-
-		/*
-		 * Check for names like C.UTF-8 by chopping off the encoding suffix on
-		 * our temporary copy, so we can skip the version.
-		 */
-		if (copy_suffix)
-			*copy_suffix = '\0';
-		if (pg_strcasecmp("c", copy) == 0 ||
-			pg_strcasecmp("posix", copy) == 0)
-			need_version = false;
-		pfree(copy);
-		if (!need_version)
-			return NULL;
-
 		/* Use the glibc version because we don't have anything better. */
 		collversion = pstrdup(gnu_get_libc_version());
 #elif defined(LC_VERSION_MASK)
 		locale_t    loc;
 
-		/* C[.encoding] and POSIX never change. */
-		if (strcmp("C", collcollate) == 0 ||
-			strncmp("C.", collcollate, 2) == 0 ||
-			strcmp("POSIX", collcollate) == 0)
-			return NULL;
-
 		/* Look up FreeBSD collation version. */
 		loc = newlocale(LC_COLLATE, collcollate, NULL);
 		if (loc)
@@ -1687,12 +1668,6 @@ get_collation_current_version(char collprovider, const char *collcollate)
 		NLSVERSIONINFOEX version = {sizeof(NLSVERSIONINFOEX)};
 		WCHAR		wide_collcollate[LOCALE_NAME_MAX_LENGTH];
 
-		/* These would be invalid arguments, but have no version. */
-		if (pg_strcasecmp("c", collcollate) == 0 ||
-			pg_strcasecmp("posix", collcollate) == 0)
-			return NULL;
-
-		/* For all other names, ask the OS. */
 		MultiByteToWideChar(CP_ACP, 0, collcollate, -1, wide_collcollate,
 							LOCALE_NAME_MAX_LENGTH);
 		if (!GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version))
-- 
2.30.0

From 721a943dd86071a93089b8c6edd6a77b93ec8b60 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Wed, 17 Feb 2021 15:05:04 +1300
Subject: [PATCH v2 4/4] Tab-complete CREATE COLLATION.

Discussion: https://postgr.es/m/20210117215940.GE8560%40telsasoft.com
---
 src/bin/psql/tab-complete.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index b64db82f02..578e59ea2c 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2433,6 +2433,20 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
 		COMPLETE_WITH("HANDLER");
 
+	/* CREATE COLLATION */
+	else if (Matches("CREATE", "COLLATION", MatchAny))
+		COMPLETE_WITH("(");
+	else if (HeadMatches("CREATE", "COLLATION"))
+	{
+		if (TailMatches("(|*,"))
+			COMPLETE_WITH("LOCALE =", "LC_COLLATE =", "LC_CTYPE =",
+						  "PROVIDER =", "DETERMINISTIC =");
+		else if (TailMatches("PROVIDER", "="))
+			COMPLETE_WITH("libc", "icu");
+		else if (TailMatches("DETERMINISTIC", "="))
+			COMPLETE_WITH("true", "false");
+	}
+
 	/* CREATE DATABASE */
 	else if (Matches("CREATE", "DATABASE", MatchAny))
 		COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
-- 
2.30.0

Reply via email to