On Thu, Feb 18, 2021 at 8:15 PM Michael Paquier <mich...@paquier.xyz> wrote: > Could you just add a test with pg_collation_current_version(0)?
Done. > + pg_strncasecmp("POSIX.", collcollate, 6) != 0) > > I didn't know that "POSIX." was possible. Yeah, that isn't valid on my (quite current) GNU or FreeBSD systems, and doesn't show up in their "locale -a" output, but I wondered about that theoretical possibility and googled it, and that showed that it does exist out there, though I don't know where/which versions, possibly only a long time ago. You know what, let's just forget that bit, it's not necessary. Removed. > While on it, I guess that you could add tab completion support for > CREATE COLLATION foo FROM. Good point. Added. > And shouldn't CREATE COLLATION complete > with the list of existing collation? Rght, fixed.
From 1a3092e6ca386bac3292aa60c20e3b2a2ce08fff 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 v3 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 +- src/test/regress/expected/collate.icu.utf8.out | 14 ++++++++++++++ src/test/regress/sql/collate.icu.utf8.sql | 5 +++++ 7 files changed, 33 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); diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index bc3752e923..de70cb1212 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -2155,3 +2155,17 @@ DROP SCHEMA collate_tests CASCADE; RESET client_min_messages; -- leave a collation for pg_upgrade test CREATE COLLATION coll_icu_upgrade FROM "und-x-icu"; +-- Test user-visible function for inspecting versions +SELECT pg_collation_actual_version('"en-x-icu"'::regcollation) is not null; + ?column? +---------- + t +(1 row) + +-- Invalid OIDs are silently ignored +SELECT pg_collation_actual_version(0) is null; + ?column? +---------- + t +(1 row) + diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql index 0de2ed8d85..dd5d208854 100644 --- a/src/test/regress/sql/collate.icu.utf8.sql +++ b/src/test/regress/sql/collate.icu.utf8.sql @@ -883,3 +883,8 @@ RESET client_min_messages; -- leave a collation for pg_upgrade test CREATE COLLATION coll_icu_upgrade FROM "und-x-icu"; + +-- Test user-visible function for inspecting versions +SELECT pg_collation_actual_version('"en-x-icu"'::regcollation) is not null; +-- Invalid OIDs are silently ignored +SELECT pg_collation_actual_version(0) is null; -- 2.30.0
From ff2f63d3a98b059edc2c0e868f0734c017ec3825 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 v3 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 | 6 +++--- src/test/regress/sql/collate.icu.utf8.sql | 6 +++--- 6 files changed, 20 insertions(+), 20 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 de70cb1212..43fbff1de2 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 @@ -2156,14 +2156,14 @@ RESET client_min_messages; -- leave a collation for pg_upgrade test CREATE COLLATION coll_icu_upgrade FROM "und-x-icu"; -- Test user-visible function for inspecting versions -SELECT pg_collation_actual_version('"en-x-icu"'::regcollation) is not null; +SELECT pg_collation_current_version('"en-x-icu"'::regcollation) is not null; ?column? ---------- t (1 row) -- Invalid OIDs are silently ignored -SELECT pg_collation_actual_version(0) is null; +SELECT pg_collation_current_version(0) is null; ?column? ---------- t diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql index dd5d208854..8b341dbb24 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 @@ -885,6 +885,6 @@ RESET client_min_messages; CREATE COLLATION coll_icu_upgrade FROM "und-x-icu"; -- Test user-visible function for inspecting versions -SELECT pg_collation_actual_version('"en-x-icu"'::regcollation) is not null; +SELECT pg_collation_current_version('"en-x-icu"'::regcollation) is not null; -- Invalid OIDs are silently ignored -SELECT pg_collation_actual_version(0) is null; +SELECT pg_collation_current_version(0) is null; -- 2.30.0
From 5ebcead5e357a67d26a257c041e898002d0b16f7 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 v3 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. Reviewed-by: Michael Paquier <mich...@paquier.xyz> Discussion: https://postgr.es/m/20210117215940.GE8560%40telsasoft.com --- src/backend/utils/adt/pg_locale.c | 34 ++++--------------------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 2e4c6e9a26..df1f36132d 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -1636,37 +1636,17 @@ 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) { #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 +1667,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 bdaadc82251bb490d25cf55e4aaca4957d52e32d 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 v3 4/4] Tab-complete CREATE COLLATION. Reviewed-by: Michael Paquier <mich...@paquier.xyz> Discussion: https://postgr.es/m/20210117215940.GE8560%40telsasoft.com --- src/bin/psql/tab-complete.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index b64db82f02..62e1ccc397 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -981,6 +981,11 @@ static const SchemaQuery Query_for_list_of_statistics = { " FROM pg_catalog.pg_cursors "\ " WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'" +#define Query_for_list_of_collations \ +" SELECT pg_catalog.quote_ident(collname) "\ +" FROM pg_catalog.pg_collation "\ +" WHERE collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'" + /* * These object types were introduced later than our support cutoff of * server version 7.4. We use the VersionedQuery infrastructure so that @@ -1031,7 +1036,7 @@ static const pgsql_thing_t words_after_create[] = { {"AGGREGATE", NULL, NULL, Query_for_list_of_aggregates}, {"CAST", NULL, NULL, NULL}, /* Casts have complex structures for names, so * skip it */ - {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"}, + {"COLLATION", Query_for_list_of_collations}, /* * CREATE CONSTRAINT TRIGGER is not supported here because it is designed @@ -2433,6 +2438,22 @@ 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("(", "FROM"); + else if (Matches("CREATE", "COLLATION", MatchAny, "FROM")) + COMPLETE_WITH_QUERY(Query_for_list_of_collations); + else if (HeadMatches("CREATE", "COLLATION", MatchAny, "(*")) + { + 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