Perform provider-specific initialization in new functions.
authorJeff Davis <jdavis@postgresql.org>
Tue, 3 Dec 2024 07:20:32 +0000 (23:20 -0800)
committerJeff Davis <jdavis@postgresql.org>
Tue, 3 Dec 2024 07:24:35 +0000 (23:24 -0800)
Reviewed-by: Andreas Karlsson
Discussion: https://postgr.es/m/4548a168-62cd-457b-8d06-9ba7b985c477@proxel.se

src/backend/utils/adt/Makefile
src/backend/utils/adt/meson.build
src/backend/utils/adt/pg_locale.c
src/backend/utils/adt/pg_locale_builtin.c [new file with mode: 0644]
src/backend/utils/adt/pg_locale_icu.c
src/backend/utils/adt/pg_locale_libc.c

index 85e5eaf32ebc7b39848ea1bb95ef194bcb8032ba..35e8c01aab94fe26af0d40abdbc0aa0798b95aca 100644 (file)
@@ -79,6 +79,7 @@ OBJS = \
        orderedsetaggs.o \
        partitionfuncs.o \
        pg_locale.o \
+       pg_locale_builtin.o \
        pg_locale_icu.o \
        pg_locale_libc.o \
        pg_lsn.o \
index f73f294b8f5f4161e2df4ff4a2b9781fb2cd39c4..e86d6dc8e0aaab6b044841b36490e1eadabbdb7f 100644 (file)
@@ -66,6 +66,7 @@ backend_sources += files(
   'orderedsetaggs.c',
   'partitionfuncs.c',
   'pg_locale.c',
+  'pg_locale_builtin.c',
   'pg_locale_icu.c',
   'pg_locale_libc.c',
   'pg_lsn.c',
index 91cee7714b1e133bb56930d2526843483af36ed6..4cb56126e97b4e793632b04877ad91be5f8ecd4a 100644 (file)
 
 #define                MAX_L10N_DATA           80
 
+/* pg_locale_builtin.c */
+extern pg_locale_t create_pg_locale_builtin(Oid collid, MemoryContext context);
+
 /* pg_locale_icu.c */
 #ifdef USE_ICU
 extern UCollator *pg_ucol_open(const char *loc_str);
-extern UCollator *make_icu_collator(const char *iculocstr,
-                                                                       const char *icurules);
 extern int     strncoll_icu(const char *arg1, ssize_t len1,
                                                 const char *arg2, ssize_t len2,
                                                 pg_locale_t locale);
@@ -104,10 +105,10 @@ extern size_t strnxfrm_prefix_icu(char *dest, size_t destsize,
                                                                  const char *src, ssize_t srclen,
                                                                  pg_locale_t locale);
 #endif
+extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
 
 /* pg_locale_libc.c */
-extern locale_t make_libc_collator(const char *collate,
-                                                                  const char *ctype);
+extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
 extern int     strncoll_libc(const char *arg1, ssize_t len1,
                                                  const char *arg2, ssize_t len2,
                                                  pg_locale_t locale);
@@ -138,7 +139,7 @@ char           *localized_full_months[12 + 1];
 /* is the databases's LC_CTYPE the C locale? */
 bool           database_ctype_is_c = false;
 
-static struct pg_locale_struct default_locale;
+static pg_locale_t default_locale = NULL;
 
 /* indicates whether locale information cache is valid */
 static bool CurrentLocaleConvValid = false;
@@ -1194,7 +1195,6 @@ IsoLocaleName(const char *winlocname)
 
 #endif                                                 /* WIN32 && LC_MESSAGES */
 
-
 /*
  * Create a new pg_locale_t struct for the given collation oid.
  */
@@ -1207,80 +1207,23 @@ create_pg_locale(Oid collid, MemoryContext context)
        Datum           datum;
        bool            isnull;
 
-       result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
-
        tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for collation %u", collid);
        collform = (Form_pg_collation) GETSTRUCT(tp);
 
-       result->provider = collform->collprovider;
-       result->deterministic = collform->collisdeterministic;
-       result->is_default = false;
-
        if (collform->collprovider == COLLPROVIDER_BUILTIN)
-       {
-               const char *locstr;
-
-               datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_colllocale);
-               locstr = TextDatumGetCString(datum);
-
-               result->collate_is_c = true;
-               result->ctype_is_c = (strcmp(locstr, "C") == 0);
-
-               builtin_validate_locale(GetDatabaseEncoding(), locstr);
-
-               result->info.builtin.locale = MemoryContextStrdup(context,
-                                                                                                                 locstr);
-       }
+               result = create_pg_locale_builtin(collid, context);
        else if (collform->collprovider == COLLPROVIDER_ICU)
-       {
-#ifdef USE_ICU
-               const char *iculocstr;
-               const char *icurules;
-
-               datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_colllocale);
-               iculocstr = TextDatumGetCString(datum);
-
-               result->collate_is_c = false;
-               result->ctype_is_c = false;
-
-               datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collicurules, &isnull);
-               if (!isnull)
-                       icurules = TextDatumGetCString(datum);
-               else
-                       icurules = NULL;
-
-               result->info.icu.locale = MemoryContextStrdup(context, iculocstr);
-               result->info.icu.ucol = make_icu_collator(iculocstr, icurules);
-#else
-               /* could get here if a collation was created by a build with ICU */
-               ereport(ERROR,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("ICU is not supported in this build")));
-#endif
-       }
+               result = create_pg_locale_icu(collid, context);
        else if (collform->collprovider == COLLPROVIDER_LIBC)
-       {
-               const char *collcollate;
-               const char *collctype;
-
-               datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collcollate);
-               collcollate = TextDatumGetCString(datum);
-               datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collctype);
-               collctype = TextDatumGetCString(datum);
-
-               result->collate_is_c = (strcmp(collcollate, "C") == 0) ||
-                       (strcmp(collcollate, "POSIX") == 0);
-               result->ctype_is_c = (strcmp(collctype, "C") == 0) ||
-                       (strcmp(collctype, "POSIX") == 0);
-
-               result->info.lt = make_libc_collator(collcollate, collctype);
-       }
+               result = create_pg_locale_libc(collid, context);
        else
                /* shouldn't happen */
                PGLOCALE_SUPPORT_ERROR(collform->collprovider);
 
+       result->is_default = false;
+
        datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collversion,
                                                        &isnull);
        if (!isnull)
@@ -1336,7 +1279,9 @@ init_database_collation(void)
 {
        HeapTuple       tup;
        Form_pg_database dbform;
-       Datum           datum;
+       pg_locale_t result;
+
+       Assert(default_locale == NULL);
 
        /* Fetch our pg_database row normally, via syscache */
        tup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
@@ -1345,81 +1290,22 @@ init_database_collation(void)
        dbform = (Form_pg_database) GETSTRUCT(tup);
 
        if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
-       {
-               char       *datlocale;
-
-               datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datlocale);
-               datlocale = TextDatumGetCString(datum);
-
-               builtin_validate_locale(dbform->encoding, datlocale);
-
-               default_locale.collate_is_c = true;
-               default_locale.ctype_is_c = (strcmp(datlocale, "C") == 0);
-
-               default_locale.info.builtin.locale = MemoryContextStrdup(TopMemoryContext,
-                                                                                                                                datlocale);
-       }
+               result = create_pg_locale_builtin(DEFAULT_COLLATION_OID,
+                                                                                 TopMemoryContext);
        else if (dbform->datlocprovider == COLLPROVIDER_ICU)
-       {
-#ifdef USE_ICU
-               char       *datlocale;
-               char       *icurules;
-               bool            isnull;
-
-               datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datlocale);
-               datlocale = TextDatumGetCString(datum);
-
-               default_locale.collate_is_c = false;
-               default_locale.ctype_is_c = false;
-
-               datum = SysCacheGetAttr(DATABASEOID, tup, Anum_pg_database_daticurules, &isnull);
-               if (!isnull)
-                       icurules = TextDatumGetCString(datum);
-               else
-                       icurules = NULL;
-
-               default_locale.info.icu.locale = MemoryContextStrdup(TopMemoryContext, datlocale);
-               default_locale.info.icu.ucol = make_icu_collator(datlocale, icurules);
-#else
-               /* could get here if a collation was created by a build with ICU */
-               ereport(ERROR,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("ICU is not supported in this build")));
-#endif
-       }
+               result = create_pg_locale_icu(DEFAULT_COLLATION_OID,
+                                                                         TopMemoryContext);
        else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
-       {
-               const char *datcollate;
-               const char *datctype;
-
-               datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datcollate);
-               datcollate = TextDatumGetCString(datum);
-               datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datctype);
-               datctype = TextDatumGetCString(datum);
-
-               default_locale.collate_is_c = (strcmp(datcollate, "C") == 0) ||
-                       (strcmp(datcollate, "POSIX") == 0);
-               default_locale.ctype_is_c = (strcmp(datctype, "C") == 0) ||
-                       (strcmp(datctype, "POSIX") == 0);
-
-               default_locale.info.lt = make_libc_collator(datcollate, datctype);
-       }
+               result = create_pg_locale_libc(DEFAULT_COLLATION_OID,
+                                                                          TopMemoryContext);
        else
                /* shouldn't happen */
                PGLOCALE_SUPPORT_ERROR(dbform->datlocprovider);
 
-
-       default_locale.provider = dbform->datlocprovider;
-       default_locale.is_default = true;
-
-       /*
-        * Default locale is currently always deterministic.  Nondeterministic
-        * locales currently don't support pattern matching, which would break a
-        * lot of things if applied globally.
-        */
-       default_locale.deterministic = true;
-
+       result->is_default = true;
        ReleaseSysCache(tup);
+
+       default_locale = result;
 }
 
 /*
@@ -1437,7 +1323,7 @@ pg_newlocale_from_collation(Oid collid)
        bool            found;
 
        if (collid == DEFAULT_COLLATION_OID)
-               return &default_locale;
+               return default_locale;
 
        if (!OidIsValid(collid))
                elog(ERROR, "cache lookup failed for collation %u", collid);
diff --git a/src/backend/utils/adt/pg_locale_builtin.c b/src/backend/utils/adt/pg_locale_builtin.c
new file mode 100644 (file)
index 0000000..4246971
--- /dev/null
@@ -0,0 +1,70 @@
+/*-----------------------------------------------------------------------
+ *
+ * PostgreSQL locale utilities for builtin provider
+ *
+ * Portions Copyright (c) 2002-2024, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/adt/pg_locale_builtin.c
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "catalog/pg_database.h"
+#include "catalog/pg_collation.h"
+#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/memutils.h"
+#include "utils/pg_locale.h"
+#include "utils/syscache.h"
+
+extern pg_locale_t create_pg_locale_builtin(Oid collid,
+                                                                                       MemoryContext context);
+
+pg_locale_t
+create_pg_locale_builtin(Oid collid, MemoryContext context)
+{
+       const char *locstr;
+       pg_locale_t result;
+
+       if (collid == DEFAULT_COLLATION_OID)
+       {
+               HeapTuple       tp;
+               Datum           datum;
+
+               tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+               datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+                                                                          Anum_pg_database_datlocale);
+               locstr = TextDatumGetCString(datum);
+               ReleaseSysCache(tp);
+       }
+       else
+       {
+               HeapTuple       tp;
+               Datum           datum;
+
+               tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for collation %u", collid);
+               datum = SysCacheGetAttrNotNull(COLLOID, tp,
+                                                                          Anum_pg_collation_colllocale);
+               locstr = TextDatumGetCString(datum);
+               ReleaseSysCache(tp);
+       }
+
+       builtin_validate_locale(GetDatabaseEncoding(), locstr);
+
+       result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
+
+       result->info.builtin.locale = MemoryContextStrdup(context, locstr);
+       result->provider = COLLPROVIDER_BUILTIN;
+       result->deterministic = true;
+       result->collate_is_c = true;
+       result->ctype_is_c = (strcmp(locstr, "C") == 0);
+
+       return result;
+}
index 2a87e25dfb129871fa7c9d82db32acf711cf9473..73eb430d750141e5cab28cfb6dbfd1fd104cc073 100644 (file)
 #include "postgres.h"
 
 #ifdef USE_ICU
-
 #include <unicode/ucnv.h>
 #include <unicode/ustring.h>
+#endif
 
+#include "access/htup_details.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_collation.h"
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
 #include "utils/formatting.h"
+#include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/syscache.h"
 
 /*
  * Size of stack buffer to use for string transformations, used to avoid heap
  */
 #define                TEXTBUFLEN                      1024
 
+extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
+
+#ifdef USE_ICU
+
 extern UCollator *pg_ucol_open(const char *loc_str);
-extern UCollator *make_icu_collator(const char *iculocstr,
-                                                                       const char *icurules);
 extern int     strncoll_icu(const char *arg1, ssize_t len1,
                                                 const char *arg2, ssize_t len2,
                                                 pg_locale_t locale);
@@ -49,6 +57,8 @@ extern size_t strnxfrm_prefix_icu(char *dest, size_t destsize,
  */
 static UConverter *icu_converter = NULL;
 
+static UCollator *make_icu_collator(const char *iculocstr,
+                                                                       const char *icurules);
 static int     strncoll_icu_no_utf8(const char *arg1, ssize_t len1,
                                                                 const char *arg2, ssize_t len2,
                                                                 pg_locale_t locale);
@@ -63,6 +73,85 @@ static int32_t uchar_convert(UConverter *converter,
                                                         const char *src, int32_t srclen);
 static void icu_set_collation_attributes(UCollator *collator, const char *loc,
                                                                                 UErrorCode *status);
+#endif
+
+pg_locale_t
+create_pg_locale_icu(Oid collid, MemoryContext context)
+{
+#ifdef USE_ICU
+       bool            deterministic;
+       const char *iculocstr;
+       const char *icurules = NULL;
+       UCollator  *collator;
+       pg_locale_t result;
+
+       if (collid == DEFAULT_COLLATION_OID)
+       {
+               HeapTuple       tp;
+               Datum           datum;
+               bool            isnull;
+
+               tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+
+               /* default database collation is always deterministic */
+               deterministic = true;
+               datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+                                                                          Anum_pg_database_datlocale);
+               iculocstr = TextDatumGetCString(datum);
+               datum = SysCacheGetAttr(DATABASEOID, tp,
+                                                               Anum_pg_database_daticurules, &isnull);
+               if (!isnull)
+                       icurules = TextDatumGetCString(datum);
+
+               ReleaseSysCache(tp);
+       }
+       else
+       {
+               Form_pg_collation collform;
+               HeapTuple       tp;
+               Datum           datum;
+               bool            isnull;
+
+               tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for collation %u", collid);
+               collform = (Form_pg_collation) GETSTRUCT(tp);
+               deterministic = collform->collisdeterministic;
+               datum = SysCacheGetAttrNotNull(COLLOID, tp,
+                                                                          Anum_pg_collation_colllocale);
+               iculocstr = TextDatumGetCString(datum);
+               datum = SysCacheGetAttr(COLLOID, tp,
+                                                               Anum_pg_collation_collicurules, &isnull);
+               if (!isnull)
+                       icurules = TextDatumGetCString(datum);
+
+               ReleaseSysCache(tp);
+       }
+
+       collator = make_icu_collator(iculocstr, icurules);
+
+       result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
+       result->info.icu.locale = MemoryContextStrdup(context, iculocstr);
+       result->info.icu.ucol = collator;
+       result->provider = COLLPROVIDER_ICU;
+       result->deterministic = deterministic;
+       result->collate_is_c = false;
+       result->ctype_is_c = false;
+
+       return result;
+#else
+       /* could get here if a collation was created by a build with ICU */
+       ereport(ERROR,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("ICU is not supported in this build")));
+
+       return NULL;
+#endif
+}
+
+#ifdef USE_ICU
 
 /*
  * Wrapper around ucol_open() to handle API differences for older ICU
@@ -160,7 +249,7 @@ pg_ucol_open(const char *loc_str)
  *
  * Ensure that no path leaks a UCollator.
  */
-UCollator *
+static UCollator *
 make_icu_collator(const char *iculocstr, const char *icurules)
 {
        if (!icurules)
index 83f310fc71c15bfb1c86f14091af77175d427f13..374ac37ba0aea997c1b11515398a3b7c175856a5 100644 (file)
 
 #include "postgres.h"
 
+#include "access/htup_details.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_collation.h"
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
 #include "utils/formatting.h"
+#include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/syscache.h"
 
 /*
  * Size of stack buffer to use for string transformations, used to avoid heap
  */
 #define                TEXTBUFLEN                      1024
 
-extern locale_t make_libc_collator(const char *collate,
-                                                                  const char *ctype);
+extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
+
 extern int     strncoll_libc(const char *arg1, ssize_t len1,
                                                  const char *arg2, ssize_t len2,
                                                  pg_locale_t locale);
 extern size_t strnxfrm_libc(char *dest, size_t destsize,
                                                        const char *src, ssize_t srclen,
                                                        pg_locale_t locale);
-
+static locale_t make_libc_collator(const char *collate,
+                                                                  const char *ctype);
 static void report_newlocale_failure(const char *localename);
 
 #ifdef WIN32
@@ -41,6 +48,65 @@ static int   strncoll_libc_win32_utf8(const char *arg1, ssize_t len1,
                                                                         pg_locale_t locale);
 #endif
 
+pg_locale_t
+create_pg_locale_libc(Oid collid, MemoryContext context)
+{
+       const char *collate;
+       const char *ctype;
+       locale_t        loc;
+       pg_locale_t result;
+
+       if (collid == DEFAULT_COLLATION_OID)
+       {
+               HeapTuple       tp;
+               Datum           datum;
+
+               tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+               datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+                                                                          Anum_pg_database_datcollate);
+               collate = TextDatumGetCString(datum);
+               datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
+                                                                          Anum_pg_database_datctype);
+               ctype = TextDatumGetCString(datum);
+
+               ReleaseSysCache(tp);
+       }
+       else
+       {
+               HeapTuple       tp;
+               Datum           datum;
+
+               tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for collation %u", collid);
+
+               datum = SysCacheGetAttrNotNull(COLLOID, tp,
+                                                                          Anum_pg_collation_collcollate);
+               collate = TextDatumGetCString(datum);
+               datum = SysCacheGetAttrNotNull(COLLOID, tp,
+                                                                          Anum_pg_collation_collctype);
+               ctype = TextDatumGetCString(datum);
+
+               ReleaseSysCache(tp);
+       }
+
+
+       loc = make_libc_collator(collate, ctype);
+
+       result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
+       result->provider = COLLPROVIDER_LIBC;
+       result->deterministic = true;
+       result->collate_is_c = (strcmp(collate, "C") == 0) ||
+               (strcmp(collate, "POSIX") == 0);
+       result->ctype_is_c = (strcmp(ctype, "C") == 0) ||
+               (strcmp(ctype, "POSIX") == 0);
+       result->info.lt = loc;
+
+       return result;
+}
+
 /*
  * Create a locale_t with the given collation and ctype.
  *
@@ -49,7 +115,7 @@ static int   strncoll_libc_win32_utf8(const char *arg1, ssize_t len1,
  *
  * Ensure that no path leaks a locale_t.
  */
-locale_t
+static locale_t
 make_libc_collator(const char *collate, const char *ctype)
 {
        locale_t        loc = 0;