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)