mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Add option to use ICU as global locale provider
This adds the option to use ICU as the default locale provider for either the whole cluster or a database. New options for initdb, createdb, and CREATE DATABASE are used to select this. Since some (legacy) code still uses the libc locale facilities directly, we still need to set the libc global locale settings even if ICU is otherwise selected. So pg_database now has three locale-related fields: the existing datcollate and datctype, which are always set, and a new daticulocale, which is only set if ICU is selected. A similar change is made in pg_collation for consistency, but in that case, only the libc-related fields or the ICU-related field is set, never both. Reviewed-by: Julien Rouhaud <rjuju123@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/5e756dd6-0e91-d778-96fd-b1bcb06c161a%402ndquadrant.com
This commit is contained in:
@ -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);
|
||||
if (collform->collprovider == COLLPROVIDER_LIBC)
|
||||
{
|
||||
Datum datum;
|
||||
bool isnull;
|
||||
const char *collcollate;
|
||||
const char *collctype;
|
||||
|
||||
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));
|
||||
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;
|
||||
const char *iculocstr;
|
||||
|
||||
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))));
|
||||
|
||||
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)
|
||||
{
|
||||
/*
|
||||
|
Reference in New Issue
Block a user