On Wed, Dec 18, 2019 at 11:02 PM Thomas Munro <thomas.mu...@gmail.com> wrote:
> On Tue, Dec 17, 2019 at 1:40 AM Juan José Santamaría Flecha
> <juanjo.santama...@gmail.com> wrote:
> > This is a resume to illustrate an issue with locale = 'C':
> >
> > postgres=#   CREATE COLLATION c_test (locale = 'C');
> > CREATE COLLATION
> > postgres=# select collname, collprovider, collencoding, collcollate, 
> > collctype, collversion from pg_collation ;
> >         collname        | collprovider | collencoding |   collcollate    |  
> >   collctype     | collversion
> > ------------------------+--------------+--------------+------------------+------------------+-------------
> >  default                | d            |           -1 |                  |  
> >                 | <NULL>
> >  C                      | c            |           -1 | C                | 
> > C                | <NULL>
> >  POSIX                  | c            |           -1 | POSIX            | 
> > POSIX            | <NULL>
> >  ucs_basic              | c            |            6 | C                | 
> > C                | <NULL>
> >  und-x-icu              | i            |           -1 | und              | 
> > und              | 153.97
> > [... resumed ...]
> >  c_test                 | c            |            6 | C                | 
> > C                |
> > (757 rows)
> >
> >  Shouldn't it be NULL?

Done in this new 0002 patch (untested).  0001 removes the comment that
individual collations can't have a NULL version, reports NULL for
Linux/glibc collations like C.UTF-8 by stripping the suffix and
comparing with C and POSIX as suggested by Peter E.
From 95f35fcbc61354c984193293f70e8ee8a944df91 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Mon, 23 Mar 2020 12:48:59 +1300
Subject: [PATCH v3 1/2] Allow NULL version for individual collations.

Remove the documented restriction that collation providers must either
return NULL for all collations or non-NULL for all collations.

Use NULL for glibc collations like "C.UTF-8", which might otherwise lead
future proposed commits to force unnecessary index rebuilds.

Reviewed-by: Peter Eisentraut <peter.eisentr...@2ndquadrant.com>
Discussion: https://postgr.es/m/CA%2BhUKGJvqup3s%2BJowVTcacZADO6dOhfdBmvOPHLS3KXUJu41Jw%40mail.gmail.com
---
 src/backend/utils/adt/pg_locale.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 64fd3ae18a..b42122f9ce 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1505,10 +1505,6 @@ pg_newlocale_from_collation(Oid collid)
 /*
  * Get provider-specific collation version string for the given collation from
  * the operating system/library.
- *
- * A particular provider must always either return a non-NULL string or return
- * NULL (if it doesn't support versions).  It must not return NULL for some
- * collcollate and not NULL for others.
  */
 char *
 get_collation_actual_version(char collprovider, const char *collcollate)
@@ -1540,6 +1536,23 @@ get_collation_actual_version(char collprovider, const char *collcollate)
 	if (collprovider == COLLPROVIDER_LIBC)
 	{
 #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());
 #endif
-- 
2.20.1

From 953148ccfcb28856db70fe0cb4a99b9f2a08a0ce Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Fri, 8 Nov 2019 12:01:05 +1300
Subject: [PATCH v3 2/2] Add collation versions for Windows.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

On Vista and later, use GetNLSVersionEx() to request collation version
information.

Reviewed-by: Juan José Santamaría Flecha <juanjo.santama...@gmail.com>
Discussion: https://postgr.es/m/CA%2BhUKGJvqup3s%2BJowVTcacZADO6dOhfdBmvOPHLS3KXUJu41Jw%40mail.gmail.com
---
 src/backend/utils/adt/pg_locale.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index b42122f9ce..2562eb5416 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1555,6 +1555,33 @@ get_collation_actual_version(char collprovider, const char *collcollate)
 
 		/* Use the glibc version because we don't have anything better. */
 		collversion = pstrdup(gnu_get_libc_version());
+#elif defined(WIN32) && _WIN32_WINNT >= 0x0600
+		/*
+		 * If we are targeting Windows Vista and above, we can ask for a name
+		 * given a collation name (earlier versions required a location code
+		 * that we don't have).
+		 */
+		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))
+			ereport(ERROR,
+					(errmsg("could not get collation version for locale \"%s\": error code %lu",
+							collcollate,
+							GetLastError())));
+		collversion = psprintf("%d.%d,%d.%d",
+							   (version.dwNLSVersion >> 8) & 0xFFFF,
+							   version.dwNLSVersion & 0xFF,
+							   (version.dwDefinedVersion >> 8) & 0xFFFF,
+							   version.dwDefinedVersion & 0xFF);
 #endif
 	}
 
-- 
2.20.1

Reply via email to