diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/pg_locale.c | 144 | ||||
-rw-r--r-- | src/backend/utils/init/postinit.c | 21 |
2 files changed, 109 insertions, 56 deletions
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 871a710967c..4019255f8ea 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -1288,26 +1288,37 @@ lookup_collation_cache(Oid collation, bool set_flags) { /* Attempt to set the flags */ HeapTuple tp; - Datum datum; - bool isnull; - const char *collcollate; - const char *collctype; + Form_pg_collation collform; tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for collation %u", collation); + collform = (Form_pg_collation) GETSTRUCT(tp); - datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collcollate, &isnull); - Assert(!isnull); - collcollate = TextDatumGetCString(datum); - datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collctype, &isnull); - Assert(!isnull); - collctype = TextDatumGetCString(datum); - - cache_entry->collate_is_c = ((strcmp(collcollate, "C") == 0) || - (strcmp(collcollate, "POSIX") == 0)); - cache_entry->ctype_is_c = ((strcmp(collctype, "C") == 0) || - (strcmp(collctype, "POSIX") == 0)); + if (collform->collprovider == COLLPROVIDER_LIBC) + { + Datum datum; + bool isnull; + const char *collcollate; + const char *collctype; + + datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collcollate, &isnull); + Assert(!isnull); + collcollate = TextDatumGetCString(datum); + datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collctype, &isnull); + Assert(!isnull); + collctype = TextDatumGetCString(datum); + + cache_entry->collate_is_c = ((strcmp(collcollate, "C") == 0) || + (strcmp(collcollate, "POSIX") == 0)); + cache_entry->ctype_is_c = ((strcmp(collctype, "C") == 0) || + (strcmp(collctype, "POSIX") == 0)); + } + else + { + cache_entry->collate_is_c = false; + cache_entry->ctype_is_c = false; + } cache_entry->flags_valid = true; @@ -1340,6 +1351,9 @@ lc_collate_is_c(Oid collation) static int result = -1; char *localeptr; + if (default_locale.provider == COLLPROVIDER_ICU) + return false; + if (result >= 0) return (bool) result; localeptr = setlocale(LC_COLLATE, NULL); @@ -1390,6 +1404,9 @@ lc_ctype_is_c(Oid collation) static int result = -1; char *localeptr; + if (default_locale.provider == COLLPROVIDER_ICU) + return false; + if (result >= 0) return (bool) result; localeptr = setlocale(LC_CTYPE, NULL); @@ -1418,6 +1435,38 @@ lc_ctype_is_c(Oid collation) return (lookup_collation_cache(collation, true))->ctype_is_c; } +struct pg_locale_struct default_locale; + +void +make_icu_collator(const char *iculocstr, + struct pg_locale_struct *resultp) +{ +#ifdef USE_ICU + UCollator *collator; + UErrorCode status; + + status = U_ZERO_ERROR; + collator = ucol_open(iculocstr, &status); + if (U_FAILURE(status)) + ereport(ERROR, + (errmsg("could not open collator for locale \"%s\": %s", + iculocstr, u_errorName(status)))); + + if (U_ICU_VERSION_MAJOR_NUM < 54) + icu_set_collation_attributes(collator, iculocstr); + + /* We will leak this string if the caller errors later :-( */ + resultp->info.icu.locale = MemoryContextStrdup(TopMemoryContext, iculocstr); + resultp->info.icu.ucol = collator; +#else /* not USE_ICU */ + /* 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"), \ + errhint("You need to rebuild PostgreSQL using %s.", "--with-icu"))); +#endif /* not USE_ICU */ +} + /* simple subroutine for reporting errors from newlocale() */ #ifdef HAVE_LOCALE_T @@ -1475,7 +1524,12 @@ pg_newlocale_from_collation(Oid collid) Assert(OidIsValid(collid)); if (collid == DEFAULT_COLLATION_OID) - return (pg_locale_t) 0; + { + if (default_locale.provider == COLLPROVIDER_ICU) + return &default_locale; + else + return (pg_locale_t) 0; + } cache_entry = lookup_collation_cache(collid, false); @@ -1484,8 +1538,6 @@ pg_newlocale_from_collation(Oid collid) /* We haven't computed this yet in this session, so do it */ HeapTuple tp; Form_pg_collation collform; - const char *collcollate; - const char *collctype pg_attribute_unused(); struct pg_locale_struct result; pg_locale_t resultp; Datum datum; @@ -1496,13 +1548,6 @@ pg_newlocale_from_collation(Oid collid) elog(ERROR, "cache lookup failed for collation %u", collid); collform = (Form_pg_collation) GETSTRUCT(tp); - datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collcollate, &isnull); - Assert(!isnull); - collcollate = TextDatumGetCString(datum); - datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collctype, &isnull); - Assert(!isnull); - collctype = TextDatumGetCString(datum); - /* We'll fill in the result struct locally before allocating memory */ memset(&result, 0, sizeof(result)); result.provider = collform->collprovider; @@ -1511,8 +1556,17 @@ pg_newlocale_from_collation(Oid collid) if (collform->collprovider == COLLPROVIDER_LIBC) { #ifdef HAVE_LOCALE_T + const char *collcollate; + const char *collctype pg_attribute_unused(); locale_t loc; + datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collcollate, &isnull); + Assert(!isnull); + collcollate = TextDatumGetCString(datum); + datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collctype, &isnull); + Assert(!isnull); + collctype = TextDatumGetCString(datum); + if (strcmp(collcollate, collctype) == 0) { /* Normal case where they're the same */ @@ -1563,36 +1617,12 @@ pg_newlocale_from_collation(Oid collid) } else if (collform->collprovider == COLLPROVIDER_ICU) { -#ifdef USE_ICU - UCollator *collator; - UErrorCode status; - - if (strcmp(collcollate, collctype) != 0) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("collations with different collate and ctype values are not supported by ICU"))); - - status = U_ZERO_ERROR; - collator = ucol_open(collcollate, &status); - if (U_FAILURE(status)) - ereport(ERROR, - (errmsg("could not open collator for locale \"%s\": %s", - collcollate, u_errorName(status)))); + const char *iculocstr; - if (U_ICU_VERSION_MAJOR_NUM < 54) - icu_set_collation_attributes(collator, collcollate); - - /* We will leak this string if we get an error below :-( */ - result.info.icu.locale = MemoryContextStrdup(TopMemoryContext, - collcollate); - result.info.icu.ucol = collator; -#else /* not USE_ICU */ - /* 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"), \ - errhint("You need to rebuild PostgreSQL using %s.", "--with-icu"))); -#endif /* not USE_ICU */ + datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_colliculocale, &isnull); + Assert(!isnull); + iculocstr = TextDatumGetCString(datum); + make_icu_collator(iculocstr, &result); } datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collversion, @@ -1604,7 +1634,11 @@ pg_newlocale_from_collation(Oid collid) collversionstr = TextDatumGetCString(datum); - actual_versionstr = get_collation_actual_version(collform->collprovider, collcollate); + datum = SysCacheGetAttr(COLLOID, tp, collform->collprovider == COLLPROVIDER_ICU ? Anum_pg_collation_colliculocale : Anum_pg_collation_collcollate, &isnull); + Assert(!isnull); + + actual_versionstr = get_collation_actual_version(collform->collprovider, + TextDatumGetCString(datum)); if (!actual_versionstr) { /* diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 86d193c89fc..6452b42dbff 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -318,6 +318,7 @@ CheckMyDatabase(const char *name, bool am_superuser, bool override_allow_connect bool isnull; char *collate; char *ctype; + char *iculocale; /* Fetch our pg_database row normally, via syscache */ tup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId)); @@ -420,6 +421,24 @@ CheckMyDatabase(const char *name, bool am_superuser, bool override_allow_connect " which is not recognized by setlocale().", ctype), errhint("Recreate the database with another locale or install the missing locale."))); + if (dbform->datlocprovider == COLLPROVIDER_ICU) + { + datum = SysCacheGetAttr(DATABASEOID, tup, Anum_pg_database_daticulocale, &isnull); + Assert(!isnull); + iculocale = TextDatumGetCString(datum); + make_icu_collator(iculocale, &default_locale); + } + else + iculocale = NULL; + + default_locale.provider = dbform->datlocprovider; + /* + * 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; + /* * Check collation version. See similar code in * pg_newlocale_from_collation(). Note that here we warn instead of error @@ -434,7 +453,7 @@ CheckMyDatabase(const char *name, bool am_superuser, bool override_allow_connect collversionstr = TextDatumGetCString(datum); - actual_versionstr = get_collation_actual_version(COLLPROVIDER_LIBC, collate); + actual_versionstr = get_collation_actual_version(dbform->datlocprovider, dbform->datlocprovider == COLLPROVIDER_ICU ? iculocale : collate); if (!actual_versionstr) ereport(WARNING, (errmsg("database \"%s\" has no actual collation version, but a version was recorded", |