diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 85e5eaf32eb..35e8c01aab9 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -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 \ diff --git a/src/backend/utils/adt/meson.build b/src/backend/utils/adt/meson.build index f73f294b8f5..e86d6dc8e0a 100644 --- a/src/backend/utils/adt/meson.build +++ b/src/backend/utils/adt/meson.build @@ -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', diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 91cee7714b1..4cb56126e97 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -89,11 +89,12 @@ #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 index 00000000000..4246971a4d8 --- /dev/null +++ b/src/backend/utils/adt/pg_locale_builtin.c @@ -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; +} diff --git a/src/backend/utils/adt/pg_locale_icu.c b/src/backend/utils/adt/pg_locale_icu.c index 2a87e25dfb1..73eb430d750 100644 --- a/src/backend/utils/adt/pg_locale_icu.c +++ b/src/backend/utils/adt/pg_locale_icu.c @@ -12,14 +12,20 @@ #include "postgres.h" #ifdef USE_ICU - #include #include +#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 @@ -29,9 +35,11 @@ */ #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) diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c index 83f310fc71c..374ac37ba0a 100644 --- a/src/backend/utils/adt/pg_locale_libc.c +++ b/src/backend/utils/adt/pg_locale_libc.c @@ -11,10 +11,16 @@ #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 @@ -24,15 +30,16 @@ */ #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;