Do not return NULL from pg_newlocale_from_collation().
Previously, pg_newlocale_from_collation() returned NULL as a special case for the DEFAULT_COLLATION_OID if the provider was libc. In that case the behavior would depend on the last call to setlocale(). Now, consistent with the other providers, it will return a pointer to default_locale, which is not dependent on setlocale(). Note: for the C and POSIX locales, the locale_t structure within the pg_locale_t will still be zero, because those locales are implemented with internal logic and do not use libc at all. lc_collate_is_c() and lc_ctype_is_c() still depend on setlocale() to determine the current locale, which will be removed in a subsequent commit. Discussion: https://postgr.es/m/cfd9eb85-c52a-4ec9-a90e-a5e4de56e57d@eisentraut.org Reviewed-by: Peter Eisentraut, Andreas Karlsson
This commit is contained in:
parent
6a1d8cef46
commit
8240401437
@ -1461,6 +1461,103 @@ lc_ctype_is_c(Oid collation)
|
|||||||
return (lookup_collation_cache(collation, true))->ctype_is_c;
|
return (lookup_collation_cache(collation, true))->ctype_is_c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* simple subroutine for reporting errors from newlocale() */
|
||||||
|
static void
|
||||||
|
report_newlocale_failure(const char *localename)
|
||||||
|
{
|
||||||
|
int save_errno;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows doesn't provide any useful error indication from
|
||||||
|
* _create_locale(), and BSD-derived platforms don't seem to feel they
|
||||||
|
* need to set errno either (even though POSIX is pretty clear that
|
||||||
|
* newlocale should do so). So, if errno hasn't been set, assume ENOENT
|
||||||
|
* is what to report.
|
||||||
|
*/
|
||||||
|
if (errno == 0)
|
||||||
|
errno = ENOENT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ENOENT means "no such locale", not "no such file", so clarify that
|
||||||
|
* errno with an errdetail message.
|
||||||
|
*/
|
||||||
|
save_errno = errno; /* auxiliary funcs might change errno */
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("could not create locale \"%s\": %m",
|
||||||
|
localename),
|
||||||
|
(save_errno == ENOENT ?
|
||||||
|
errdetail("The operating system could not find any locale data for the locale name \"%s\".",
|
||||||
|
localename) : 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the locale_t field.
|
||||||
|
*
|
||||||
|
* The "C" and "POSIX" locales are not actually handled by libc, so set the
|
||||||
|
* locale_t to zero in that case.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
make_libc_collator(const char *collate, const char *ctype,
|
||||||
|
pg_locale_t result)
|
||||||
|
{
|
||||||
|
locale_t loc = 0;
|
||||||
|
|
||||||
|
if (strcmp(collate, ctype) == 0)
|
||||||
|
{
|
||||||
|
if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
|
||||||
|
{
|
||||||
|
/* Normal case where they're the same */
|
||||||
|
errno = 0;
|
||||||
|
#ifndef WIN32
|
||||||
|
loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collate,
|
||||||
|
NULL);
|
||||||
|
#else
|
||||||
|
loc = _create_locale(LC_ALL, collate);
|
||||||
|
#endif
|
||||||
|
if (!loc)
|
||||||
|
report_newlocale_failure(collate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifndef WIN32
|
||||||
|
/* We need two newlocale() steps */
|
||||||
|
locale_t loc1 = 0;
|
||||||
|
|
||||||
|
if (strcmp(collate, "C") != 0 && strcmp(collate, "POSIX") != 0)
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
loc1 = newlocale(LC_COLLATE_MASK, collate, NULL);
|
||||||
|
if (!loc1)
|
||||||
|
report_newlocale_failure(collate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
|
||||||
|
if (!loc)
|
||||||
|
report_newlocale_failure(ctype);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
loc = loc1;
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX The _create_locale() API doesn't appear to support this. Could
|
||||||
|
* perhaps be worked around by changing pg_locale_t to contain two
|
||||||
|
* separate fields.
|
||||||
|
*/
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("collations with different collate and ctype values are not supported on this platform")));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
result->info.lt = loc;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
make_icu_collator(const char *iculocstr,
|
make_icu_collator(const char *iculocstr,
|
||||||
const char *icurules,
|
const char *icurules,
|
||||||
@ -1514,36 +1611,6 @@ make_icu_collator(const char *iculocstr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* simple subroutine for reporting errors from newlocale() */
|
|
||||||
static void
|
|
||||||
report_newlocale_failure(const char *localename)
|
|
||||||
{
|
|
||||||
int save_errno;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Windows doesn't provide any useful error indication from
|
|
||||||
* _create_locale(), and BSD-derived platforms don't seem to feel they
|
|
||||||
* need to set errno either (even though POSIX is pretty clear that
|
|
||||||
* newlocale should do so). So, if errno hasn't been set, assume ENOENT
|
|
||||||
* is what to report.
|
|
||||||
*/
|
|
||||||
if (errno == 0)
|
|
||||||
errno = ENOENT;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ENOENT means "no such locale", not "no such file", so clarify that
|
|
||||||
* errno with an errdetail message.
|
|
||||||
*/
|
|
||||||
save_errno = errno; /* auxiliary funcs might change errno */
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("could not create locale \"%s\": %m",
|
|
||||||
localename),
|
|
||||||
(save_errno == ENOENT ?
|
|
||||||
errdetail("The operating system could not find any locale data for the locale name \"%s\".",
|
|
||||||
localename) : 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
pg_locale_deterministic(pg_locale_t locale)
|
pg_locale_deterministic(pg_locale_t locale)
|
||||||
{
|
{
|
||||||
@ -1601,7 +1668,17 @@ init_database_collation(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
const char *datcollate;
|
||||||
|
const char *datctype;
|
||||||
|
|
||||||
Assert(dbform->datlocprovider == COLLPROVIDER_LIBC);
|
Assert(dbform->datlocprovider == COLLPROVIDER_LIBC);
|
||||||
|
|
||||||
|
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datcollate);
|
||||||
|
datcollate = TextDatumGetCString(datum);
|
||||||
|
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datctype);
|
||||||
|
datctype = TextDatumGetCString(datum);
|
||||||
|
|
||||||
|
make_libc_collator(datcollate, datctype, &default_locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
default_locale.provider = dbform->datlocprovider;
|
default_locale.provider = dbform->datlocprovider;
|
||||||
@ -1620,8 +1697,6 @@ init_database_collation(void)
|
|||||||
* Create a pg_locale_t from a collation OID. Results are cached for the
|
* Create a pg_locale_t from a collation OID. Results are cached for the
|
||||||
* lifetime of the backend. Thus, do not free the result with freelocale().
|
* lifetime of the backend. Thus, do not free the result with freelocale().
|
||||||
*
|
*
|
||||||
* As a special optimization, the default/database collation returns 0.
|
|
||||||
*
|
|
||||||
* For simplicity, we always generate COLLATE + CTYPE even though we
|
* For simplicity, we always generate COLLATE + CTYPE even though we
|
||||||
* might only need one of them. Since this is called only once per session,
|
* might only need one of them. Since this is called only once per session,
|
||||||
* it shouldn't cost much.
|
* it shouldn't cost much.
|
||||||
@ -1635,12 +1710,7 @@ pg_newlocale_from_collation(Oid collid)
|
|||||||
Assert(OidIsValid(collid));
|
Assert(OidIsValid(collid));
|
||||||
|
|
||||||
if (collid == DEFAULT_COLLATION_OID)
|
if (collid == DEFAULT_COLLATION_OID)
|
||||||
{
|
return &default_locale;
|
||||||
if (default_locale.provider == COLLPROVIDER_LIBC)
|
|
||||||
return (pg_locale_t) 0;
|
|
||||||
else
|
|
||||||
return &default_locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache_entry = lookup_collation_cache(collid, false);
|
cache_entry = lookup_collation_cache(collid, false);
|
||||||
|
|
||||||
@ -1679,55 +1749,14 @@ pg_newlocale_from_collation(Oid collid)
|
|||||||
else if (collform->collprovider == COLLPROVIDER_LIBC)
|
else if (collform->collprovider == COLLPROVIDER_LIBC)
|
||||||
{
|
{
|
||||||
const char *collcollate;
|
const char *collcollate;
|
||||||
const char *collctype pg_attribute_unused();
|
const char *collctype;
|
||||||
locale_t loc;
|
|
||||||
|
|
||||||
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collcollate);
|
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collcollate);
|
||||||
collcollate = TextDatumGetCString(datum);
|
collcollate = TextDatumGetCString(datum);
|
||||||
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collctype);
|
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collctype);
|
||||||
collctype = TextDatumGetCString(datum);
|
collctype = TextDatumGetCString(datum);
|
||||||
|
|
||||||
if (strcmp(collcollate, collctype) == 0)
|
make_libc_collator(collcollate, collctype, &result);
|
||||||
{
|
|
||||||
/* Normal case where they're the same */
|
|
||||||
errno = 0;
|
|
||||||
#ifndef WIN32
|
|
||||||
loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collcollate,
|
|
||||||
NULL);
|
|
||||||
#else
|
|
||||||
loc = _create_locale(LC_ALL, collcollate);
|
|
||||||
#endif
|
|
||||||
if (!loc)
|
|
||||||
report_newlocale_failure(collcollate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifndef WIN32
|
|
||||||
/* We need two newlocale() steps */
|
|
||||||
locale_t loc1;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
loc1 = newlocale(LC_COLLATE_MASK, collcollate, NULL);
|
|
||||||
if (!loc1)
|
|
||||||
report_newlocale_failure(collcollate);
|
|
||||||
errno = 0;
|
|
||||||
loc = newlocale(LC_CTYPE_MASK, collctype, loc1);
|
|
||||||
if (!loc)
|
|
||||||
report_newlocale_failure(collctype);
|
|
||||||
#else
|
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX The _create_locale() API doesn't appear to support
|
|
||||||
* this. Could perhaps be worked around by changing
|
|
||||||
* pg_locale_t to contain two separate fields.
|
|
||||||
*/
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("collations with different collate and ctype values are not supported on this platform")));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
result.info.lt = loc;
|
|
||||||
}
|
}
|
||||||
else if (collform->collprovider == COLLPROVIDER_ICU)
|
else if (collform->collprovider == COLLPROVIDER_ICU)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user