Skip to content

Commit 8240401

Browse files
committed
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
1 parent 6a1d8ce commit 8240401

File tree

1 file changed

+110
-81
lines changed

1 file changed

+110
-81
lines changed

src/backend/utils/adt/pg_locale.c

Lines changed: 110 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,103 @@ lc_ctype_is_c(Oid collation)
14611461
return (lookup_collation_cache(collation, true))->ctype_is_c;
14621462
}
14631463

1464+
/* simple subroutine for reporting errors from newlocale() */
1465+
static void
1466+
report_newlocale_failure(const char *localename)
1467+
{
1468+
int save_errno;
1469+
1470+
/*
1471+
* Windows doesn't provide any useful error indication from
1472+
* _create_locale(), and BSD-derived platforms don't seem to feel they
1473+
* need to set errno either (even though POSIX is pretty clear that
1474+
* newlocale should do so). So, if errno hasn't been set, assume ENOENT
1475+
* is what to report.
1476+
*/
1477+
if (errno == 0)
1478+
errno = ENOENT;
1479+
1480+
/*
1481+
* ENOENT means "no such locale", not "no such file", so clarify that
1482+
* errno with an errdetail message.
1483+
*/
1484+
save_errno = errno; /* auxiliary funcs might change errno */
1485+
ereport(ERROR,
1486+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1487+
errmsg("could not create locale \"%s\": %m",
1488+
localename),
1489+
(save_errno == ENOENT ?
1490+
errdetail("The operating system could not find any locale data for the locale name \"%s\".",
1491+
localename) : 0)));
1492+
}
1493+
1494+
/*
1495+
* Initialize the locale_t field.
1496+
*
1497+
* The "C" and "POSIX" locales are not actually handled by libc, so set the
1498+
* locale_t to zero in that case.
1499+
*/
1500+
static void
1501+
make_libc_collator(const char *collate, const char *ctype,
1502+
pg_locale_t result)
1503+
{
1504+
locale_t loc = 0;
1505+
1506+
if (strcmp(collate, ctype) == 0)
1507+
{
1508+
if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
1509+
{
1510+
/* Normal case where they're the same */
1511+
errno = 0;
1512+
#ifndef WIN32
1513+
loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collate,
1514+
NULL);
1515+
#else
1516+
loc = _create_locale(LC_ALL, collate);
1517+
#endif
1518+
if (!loc)
1519+
report_newlocale_failure(collate);
1520+
}
1521+
}
1522+
else
1523+
{
1524+
#ifndef WIN32
1525+
/* We need two newlocale() steps */
1526+
locale_t loc1 = 0;
1527+
1528+
if (strcmp(collate, "C") != 0 && strcmp(collate, "POSIX") != 0)
1529+
{
1530+
errno = 0;
1531+
loc1 = newlocale(LC_COLLATE_MASK, collate, NULL);
1532+
if (!loc1)
1533+
report_newlocale_failure(collate);
1534+
}
1535+
1536+
if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
1537+
{
1538+
errno = 0;
1539+
loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
1540+
if (!loc)
1541+
report_newlocale_failure(ctype);
1542+
}
1543+
else
1544+
loc = loc1;
1545+
#else
1546+
1547+
/*
1548+
* XXX The _create_locale() API doesn't appear to support this. Could
1549+
* perhaps be worked around by changing pg_locale_t to contain two
1550+
* separate fields.
1551+
*/
1552+
ereport(ERROR,
1553+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1554+
errmsg("collations with different collate and ctype values are not supported on this platform")));
1555+
#endif
1556+
}
1557+
1558+
result->info.lt = loc;
1559+
}
1560+
14641561
void
14651562
make_icu_collator(const char *iculocstr,
14661563
const char *icurules,
@@ -1514,36 +1611,6 @@ make_icu_collator(const char *iculocstr,
15141611
}
15151612

15161613

1517-
/* simple subroutine for reporting errors from newlocale() */
1518-
static void
1519-
report_newlocale_failure(const char *localename)
1520-
{
1521-
int save_errno;
1522-
1523-
/*
1524-
* Windows doesn't provide any useful error indication from
1525-
* _create_locale(), and BSD-derived platforms don't seem to feel they
1526-
* need to set errno either (even though POSIX is pretty clear that
1527-
* newlocale should do so). So, if errno hasn't been set, assume ENOENT
1528-
* is what to report.
1529-
*/
1530-
if (errno == 0)
1531-
errno = ENOENT;
1532-
1533-
/*
1534-
* ENOENT means "no such locale", not "no such file", so clarify that
1535-
* errno with an errdetail message.
1536-
*/
1537-
save_errno = errno; /* auxiliary funcs might change errno */
1538-
ereport(ERROR,
1539-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1540-
errmsg("could not create locale \"%s\": %m",
1541-
localename),
1542-
(save_errno == ENOENT ?
1543-
errdetail("The operating system could not find any locale data for the locale name \"%s\".",
1544-
localename) : 0)));
1545-
}
1546-
15471614
bool
15481615
pg_locale_deterministic(pg_locale_t locale)
15491616
{
@@ -1601,7 +1668,17 @@ init_database_collation(void)
16011668
}
16021669
else
16031670
{
1671+
const char *datcollate;
1672+
const char *datctype;
1673+
16041674
Assert(dbform->datlocprovider == COLLPROVIDER_LIBC);
1675+
1676+
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datcollate);
1677+
datcollate = TextDatumGetCString(datum);
1678+
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datctype);
1679+
datctype = TextDatumGetCString(datum);
1680+
1681+
make_libc_collator(datcollate, datctype, &default_locale);
16051682
}
16061683

16071684
default_locale.provider = dbform->datlocprovider;
@@ -1620,8 +1697,6 @@ init_database_collation(void)
16201697
* Create a pg_locale_t from a collation OID. Results are cached for the
16211698
* lifetime of the backend. Thus, do not free the result with freelocale().
16221699
*
1623-
* As a special optimization, the default/database collation returns 0.
1624-
*
16251700
* For simplicity, we always generate COLLATE + CTYPE even though we
16261701
* might only need one of them. Since this is called only once per session,
16271702
* it shouldn't cost much.
@@ -1635,12 +1710,7 @@ pg_newlocale_from_collation(Oid collid)
16351710
Assert(OidIsValid(collid));
16361711

16371712
if (collid == DEFAULT_COLLATION_OID)
1638-
{
1639-
if (default_locale.provider == COLLPROVIDER_LIBC)
1640-
return (pg_locale_t) 0;
1641-
else
1642-
return &default_locale;
1643-
}
1713+
return &default_locale;
16441714

16451715
cache_entry = lookup_collation_cache(collid, false);
16461716

@@ -1679,55 +1749,14 @@ pg_newlocale_from_collation(Oid collid)
16791749
else if (collform->collprovider == COLLPROVIDER_LIBC)
16801750
{
16811751
const char *collcollate;
1682-
const char *collctype pg_attribute_unused();
1683-
locale_t loc;
1752+
const char *collctype;
16841753

16851754
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collcollate);
16861755
collcollate = TextDatumGetCString(datum);
16871756
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collctype);
16881757
collctype = TextDatumGetCString(datum);
16891758

1690-
if (strcmp(collcollate, collctype) == 0)
1691-
{
1692-
/* Normal case where they're the same */
1693-
errno = 0;
1694-
#ifndef WIN32
1695-
loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collcollate,
1696-
NULL);
1697-
#else
1698-
loc = _create_locale(LC_ALL, collcollate);
1699-
#endif
1700-
if (!loc)
1701-
report_newlocale_failure(collcollate);
1702-
}
1703-
else
1704-
{
1705-
#ifndef WIN32
1706-
/* We need two newlocale() steps */
1707-
locale_t loc1;
1708-
1709-
errno = 0;
1710-
loc1 = newlocale(LC_COLLATE_MASK, collcollate, NULL);
1711-
if (!loc1)
1712-
report_newlocale_failure(collcollate);
1713-
errno = 0;
1714-
loc = newlocale(LC_CTYPE_MASK, collctype, loc1);
1715-
if (!loc)
1716-
report_newlocale_failure(collctype);
1717-
#else
1718-
1719-
/*
1720-
* XXX The _create_locale() API doesn't appear to support
1721-
* this. Could perhaps be worked around by changing
1722-
* pg_locale_t to contain two separate fields.
1723-
*/
1724-
ereport(ERROR,
1725-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1726-
errmsg("collations with different collate and ctype values are not supported on this platform")));
1727-
#endif
1728-
}
1729-
1730-
result.info.lt = loc;
1759+
make_libc_collator(collcollate, collctype, &result);
17311760
}
17321761
else if (collform->collprovider == COLLPROVIDER_ICU)
17331762
{

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy