@@ -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+
14641561void
14651562make_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-
15471614bool
15481615pg_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