73 #include <unicode/ucnv.h>
74 #include <unicode/ustring.h>
78 #include <gnu/libc-version.h>
86 #define PGLOCALE_SUPPORT_ERROR(provider) \
87 elog(ERROR, "unsupported collprovider for %s: %c", __func__, provider)
93 #define TEXTBUFLEN 1024
95 #define MAX_L10N_DATA 80
139 #define SH_PREFIX collation_cache
140 #define SH_ELEMENT_TYPE collation_cache_entry
141 #define SH_KEY_TYPE Oid
142 #define SH_KEY collid
143 #define SH_HASH_KEY(tb, key) murmurhash32((uint32) key)
144 #define SH_EQUAL(tb, a, b) (a == b)
145 #define SH_GET_HASH(tb, a) a->hash
146 #define SH_SCOPE static inline
147 #define SH_STORE_HASH
162 #if defined(WIN32) && defined(LC_MESSAGES)
163 static char *IsoLocaleName(
const char *);
172 static UConverter *icu_converter = NULL;
174 static UCollator *pg_ucol_open(
const char *loc_str);
175 static void init_icu_converter(
void);
176 static size_t uchar_length(UConverter *converter,
177 const char *
str, int32_t
len);
178 static int32_t uchar_convert(UConverter *converter,
179 UChar *
dest, int32_t destlen,
180 const char *src, int32_t srclen);
181 static void icu_set_collation_attributes(UCollator *collator,
const char *loc,
189 #ifndef HAVE_MBSTOWCS_L
194 return _mbstowcs_l(
dest, src, n, loc);
197 locale_t save_locale = uselocale(loc);
199 result = mbstowcs(
dest, src, n);
200 uselocale(save_locale);
205 #ifndef HAVE_WCSTOMBS_L
210 return _wcstombs_l(
dest, src, n, loc);
213 locale_t save_locale = uselocale(loc);
215 result = wcstombs(
dest, src, n);
216 uselocale(save_locale);
253 if (category == LC_MESSAGES)
274 if (category == LC_CTYPE)
279 strlcpy(save_lc_ctype, result,
sizeof(save_lc_ctype));
280 result = save_lc_ctype;
292 envvar =
"LC_COLLATE";
299 envvar =
"LC_MESSAGES";
301 result = IsoLocaleName(
locale);
304 elog(
DEBUG3,
"IsoLocaleName() executed; locale: \"%s\"", result);
309 envvar =
"LC_MONETARY";
312 envvar =
"LC_NUMERIC";
318 elog(
FATAL,
"unrecognized LC category: %d", category);
322 if (
setenv(envvar, result, 1) != 0)
349 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
350 errmsg(
"locale name \"%s\" contains non-ASCII characters",
369 if (
res && canonname)
374 elog(
WARNING,
"failed to restore old locale \"%s\"", save);
378 if (canonname && *canonname && !
pg_is_ascii(*canonname))
381 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
382 errmsg(
"locale name \"%s\" contains non-ASCII characters",
389 return (
res != NULL);
466 #if defined(LC_MESSAGES) && !defined(WIN32)
493 free(s->decimal_point);
494 free(s->thousands_sep);
496 free(s->int_curr_symbol);
497 free(s->currency_symbol);
498 free(s->mon_decimal_point);
499 free(s->mon_thousands_sep);
500 free(s->mon_grouping);
501 free(s->positive_sign);
502 free(s->negative_sign);
512 if (s->decimal_point == NULL)
514 if (s->thousands_sep == NULL)
516 if (s->grouping == NULL)
518 if (s->int_curr_symbol == NULL)
520 if (s->currency_symbol == NULL)
522 if (s->mon_decimal_point == NULL)
524 if (s->mon_thousands_sep == NULL)
526 if (s->mon_grouping == NULL)
528 if (s->positive_sign == NULL)
530 if (s->negative_sign == NULL)
555 (
errcode(ERRCODE_OUT_OF_MEMORY),
556 errmsg(
"out of memory")));
573 static struct lconv CurrentLocaleConv;
574 static bool CurrentLocaleConvAllocated =
false;
575 struct lconv *extlconv;
576 struct lconv worklconv;
577 char *save_lc_monetary;
578 char *save_lc_numeric;
585 return &CurrentLocaleConv;
588 if (CurrentLocaleConvAllocated)
591 CurrentLocaleConvAllocated =
false;
606 memset(&worklconv, 0,
sizeof(worklconv));
609 save_lc_monetary =
setlocale(LC_MONETARY, NULL);
610 if (!save_lc_monetary)
612 save_lc_monetary =
pstrdup(save_lc_monetary);
614 save_lc_numeric =
setlocale(LC_NUMERIC, NULL);
615 if (!save_lc_numeric)
617 save_lc_numeric =
pstrdup(save_lc_numeric);
636 save_lc_ctype =
setlocale(LC_CTYPE, NULL);
639 save_lc_ctype =
pstrdup(save_lc_ctype);
649 extlconv = localeconv();
652 worklconv.decimal_point = strdup(extlconv->decimal_point);
653 worklconv.thousands_sep = strdup(extlconv->thousands_sep);
654 worklconv.grouping = strdup(extlconv->grouping);
663 extlconv = localeconv();
666 worklconv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
667 worklconv.currency_symbol = strdup(extlconv->currency_symbol);
668 worklconv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
669 worklconv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
670 worklconv.mon_grouping = strdup(extlconv->mon_grouping);
671 worklconv.positive_sign = strdup(extlconv->positive_sign);
672 worklconv.negative_sign = strdup(extlconv->negative_sign);
674 worklconv.int_frac_digits = extlconv->int_frac_digits;
675 worklconv.frac_digits = extlconv->frac_digits;
676 worklconv.p_cs_precedes = extlconv->p_cs_precedes;
677 worklconv.p_sep_by_space = extlconv->p_sep_by_space;
678 worklconv.n_cs_precedes = extlconv->n_cs_precedes;
679 worklconv.n_sep_by_space = extlconv->n_sep_by_space;
680 worklconv.p_sign_posn = extlconv->p_sign_posn;
681 worklconv.n_sign_posn = extlconv->n_sign_posn;
693 elog(
FATAL,
"failed to restore LC_CTYPE to \"%s\"", save_lc_ctype);
695 if (!
setlocale(LC_MONETARY, save_lc_monetary))
696 elog(
FATAL,
"failed to restore LC_MONETARY to \"%s\"", save_lc_monetary);
697 if (!
setlocale(LC_NUMERIC, save_lc_numeric))
698 elog(
FATAL,
"failed to restore LC_NUMERIC to \"%s\"", save_lc_numeric);
710 pfree(save_lc_monetary);
711 pfree(save_lc_numeric);
713 pfree(save_lc_ctype);
719 (
errcode(ERRCODE_OUT_OF_MEMORY),
720 errmsg(
"out of memory")));
759 CurrentLocaleConv = worklconv;
760 CurrentLocaleConvAllocated =
true;
762 return &CurrentLocaleConv;
782 strftime_win32(
char *dst,
size_t dstlen,
793 len = MultiByteToWideChar(CP_UTF8, 0,
format, -1,
796 elog(
ERROR,
"could not convert format string from UTF-8: error code %lu",
809 len = WideCharToMultiByte(CP_UTF8, 0, wbuf,
len, dst, dstlen - 1,
812 elog(
ERROR,
"could not convert string to UTF-8: error code %lu",
821 #define strftime(a,b,c,d) strftime_win32(a,b,c,d)
859 struct tm timeinfobuf;
860 bool strftimefail =
false;
885 save_lc_time =
pstrdup(save_lc_time);
897 save_lc_ctype =
setlocale(LC_CTYPE, NULL);
900 save_lc_ctype =
pstrdup(save_lc_ctype);
909 timenow = time(NULL);
910 timeinfo = gmtime_r(&timenow, &timeinfobuf);
926 for (
i = 0;
i < 7;
i++)
928 timeinfo->tm_wday =
i;
938 for (
i = 0;
i < 12;
i++)
940 timeinfo->tm_mon =
i;
941 timeinfo->tm_mday = 1;
956 elog(
FATAL,
"failed to restore LC_CTYPE to \"%s\"", save_lc_ctype);
959 elog(
FATAL,
"failed to restore LC_TIME to \"%s\"", save_lc_time);
971 pfree(save_lc_ctype);
998 for (
i = 0;
i < 7;
i++)
1009 for (
i = 0;
i < 12;
i++)
1023 #if defined(WIN32) && defined(LC_MESSAGES)
1070 #if defined(_MSC_VER)
1084 static BOOL CALLBACK
1085 search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
1087 wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
1092 argv = (
wchar_t **) lparam;
1093 *argv[2] = (wchar_t) 0;
1095 memset(test_locale, 0,
sizeof(test_locale));
1098 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
1099 test_locale, LOCALE_NAME_MAX_LENGTH))
1106 if (wcsrchr(pStr,
'-') == NULL || wcsrchr(argv[0],
'_') == NULL)
1108 if (_wcsicmp(argv[0], test_locale) == 0)
1110 wcscpy(argv[1], pStr);
1111 *argv[2] = (wchar_t) 1;
1125 wcscat(test_locale, L
"_");
1126 len = wcslen(test_locale);
1127 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
1129 LOCALE_NAME_MAX_LENGTH -
len))
1131 if (_wcsicmp(argv[0], test_locale) == 0)
1133 wcscpy(argv[1], pStr);
1134 *argv[2] = (wchar_t) 1;
1151 get_iso_localename(
const char *winlocname)
1153 wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
1154 wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
1155 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
1167 period = strchr(winlocname,
'.');
1173 memset(wc_locale_name, 0,
sizeof(wc_locale_name));
1174 memset(buffer, 0,
sizeof(buffer));
1175 MultiByteToWideChar(CP_ACP, 0, winlocname,
len, wc_locale_name,
1176 LOCALE_NAME_MAX_LENGTH);
1182 ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME, (LPWSTR) &buffer,
1183 LOCALE_NAME_MAX_LENGTH);
1192 argv[0] = wc_locale_name;
1194 argv[2] = (
wchar_t *) &ret_val;
1195 EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS, (LPARAM) argv,
1205 rc =
wchar2char(iso_lc_messages, buffer,
sizeof(iso_lc_messages), NULL);
1206 if (rc == -1 || rc ==
sizeof(iso_lc_messages))
1216 hyphen = strchr(iso_lc_messages,
'-');
1219 return iso_lc_messages;
1226 IsoLocaleName(
const char *winlocname)
1228 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
1233 strcpy(iso_lc_messages,
"C");
1234 return iso_lc_messages;
1237 return get_iso_localename(winlocname);
1243 IsoLocaleName(
const char *winlocname)
1268 Assert(collation != DEFAULT_COLLATION_OID);
1279 cache_entry = collation_cache_insert(
CollationCache, collation, &found);
1314 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1315 errmsg(
"could not create locale \"%s\": %m",
1317 (save_errno == ENOENT ?
1318 errdetail(
"The operating system could not find any locale data for the locale name \"%s\".",
1335 if (strcmp(collate, ctype) == 0)
1337 if (strcmp(ctype,
"C") != 0 && strcmp(ctype,
"POSIX") != 0)
1342 loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collate,
1345 loc = _create_locale(LC_ALL, collate);
1357 if (strcmp(collate,
"C") != 0 && strcmp(collate,
"POSIX") != 0)
1360 loc1 = newlocale(LC_COLLATE_MASK, collate, NULL);
1365 if (strcmp(ctype,
"C") != 0 && strcmp(ctype,
"POSIX") != 0)
1368 loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
1386 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1387 errmsg(
"collations with different collate and ctype values are not supported on this platform")));
1401 make_icu_collator(
const char *iculocstr,
const char *icurules)
1406 return pg_ucol_open(iculocstr);
1410 UCollator *collator_std_rules;
1411 UCollator *collator_all_rules;
1412 const UChar *std_rules;
1424 icu_to_uchar(&my_rules, icurules, strlen(icurules));
1426 collator_std_rules = pg_ucol_open(iculocstr);
1428 std_rules = ucol_getRules(collator_std_rules, &length);
1430 total = u_strlen(std_rules) + u_strlen(my_rules) + 1;
1436 ucol_close(collator_std_rules);
1438 (
errcode(ERRCODE_OUT_OF_MEMORY),
1439 errmsg(
"out of memory")));
1442 u_strcpy(all_rules, std_rules);
1443 u_strcat(all_rules, my_rules);
1445 ucol_close(collator_std_rules);
1447 status = U_ZERO_ERROR;
1448 collator_all_rules = ucol_openRules(all_rules, u_strlen(all_rules),
1449 UCOL_DEFAULT, UCOL_DEFAULT_STRENGTH,
1451 if (U_FAILURE(status))
1454 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1455 errmsg(
"could not open collator for locale \"%s\" with rules \"%s\": %s",
1456 iculocstr, icurules, u_errorName(status))));
1459 return collator_all_rules;
1480 if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
1495 else if (dbform->datlocprovider == COLLPROVIDER_ICU)
1508 datum =
SysCacheGetAttr(DATABASEOID, tup, Anum_pg_database_daticurules, &isnull);
1519 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1520 errmsg(
"ICU is not supported in this build")));
1523 else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
1525 const char *datcollate;
1526 const char *datctype;
1534 (strcmp(datcollate,
"POSIX") == 0);
1536 (strcmp(datctype,
"POSIX") == 0);
1570 if (
collid == DEFAULT_COLLATION_OID)
1581 if (cache_entry->
locale == 0)
1597 memset(&result, 0,
sizeof(result));
1598 result.
provider = collform->collprovider;
1601 if (collform->collprovider == COLLPROVIDER_BUILTIN)
1609 result.
ctype_is_c = (strcmp(locstr,
"C") == 0);
1616 else if (collform->collprovider == COLLPROVIDER_ICU)
1619 const char *iculocstr;
1620 const char *icurules;
1628 datum =
SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collicurules, &isnull);
1635 result.
info.icu.ucol = make_icu_collator(iculocstr, icurules);
1639 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1640 errmsg(
"ICU is not supported in this build")));
1643 else if (collform->collprovider == COLLPROVIDER_LIBC)
1645 const char *collcollate;
1646 const char *collctype;
1653 result.
collate_is_c = (strcmp(collcollate,
"C") == 0) ||
1654 (strcmp(collcollate,
"POSIX") == 0);
1655 result.
ctype_is_c = (strcmp(collctype,
"C") == 0) ||
1656 (strcmp(collctype,
"POSIX") == 0);
1668 char *actual_versionstr;
1669 char *collversionstr;
1673 if (collform->collprovider == COLLPROVIDER_LIBC)
1680 if (!actual_versionstr)
1688 (
errmsg(
"collation \"%s\" has no actual version, but a version was recorded",
1689 NameStr(collform->collname))));
1692 if (strcmp(actual_versionstr, collversionstr) != 0)
1694 (
errmsg(
"collation \"%s\" has version mismatch",
1696 errdetail(
"The collation in the database was created using version %s, "
1697 "but the operating system provides version %s.",
1698 collversionstr, actual_versionstr),
1699 errhint(
"Rebuild all objects affected by this collation and run "
1700 "ALTER COLLATION %s REFRESH VERSION, "
1701 "or build PostgreSQL with the right library version.",
1703 NameStr(collform->collname)))));
1712 cache_entry->
locale = resultp;
1718 return cache_entry->
locale;
1728 char *collversion = NULL;
1737 if (collprovider == COLLPROVIDER_BUILTIN)
1739 if (strcmp(collcollate,
"C") == 0)
1741 else if (strcmp(collcollate,
"C.UTF-8") == 0)
1745 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1746 errmsg(
"invalid locale name \"%s\" for builtin provider",
1751 if (collprovider == COLLPROVIDER_ICU)
1753 UCollator *collator;
1754 UVersionInfo versioninfo;
1755 char buf[U_MAX_VERSION_STRING_LENGTH];
1757 collator = pg_ucol_open(collcollate);
1759 ucol_getVersion(collator, versioninfo);
1760 ucol_close(collator);
1762 u_versionToString(versioninfo,
buf);
1767 if (collprovider == COLLPROVIDER_LIBC &&
1772 #if defined(__GLIBC__)
1774 collversion =
pstrdup(gnu_get_libc_version());
1775 #elif defined(LC_VERSION_MASK)
1779 loc = newlocale(LC_COLLATE_MASK, collcollate, NULL);
1783 pstrdup(querylocale(LC_COLLATE_MASK | LC_VERSION_MASK, loc));
1788 (
errmsg(
"could not load locale \"%s\"", collcollate)));
1789 #elif defined(WIN32)
1795 NLSVERSIONINFOEX version = {
sizeof(NLSVERSIONINFOEX)};
1796 WCHAR wide_collcollate[LOCALE_NAME_MAX_LENGTH];
1798 MultiByteToWideChar(CP_ACP, 0, collcollate, -1, wide_collcollate,
1799 LOCALE_NAME_MAX_LENGTH);
1800 if (!GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version))
1809 if (GetLastError() == ERROR_INVALID_PARAMETER)
1813 (
errmsg(
"could not get collation version for locale \"%s\": error code %lu",
1817 collversion =
psprintf(
"%lu.%lu,%lu.%lu",
1818 (version.dwNLSVersion >> 8) & 0xFFFF,
1819 version.dwNLSVersion & 0xFF,
1820 (version.dwDefinedVersion >> 8) & 0xFFFF,
1821 version.dwDefinedVersion & 0xFF);
1838 strncoll_libc_win32_utf8(
const char *arg1, ssize_t len1,
const char *arg2,
1854 len1 = strlen(arg1);
1856 len2 = strlen(arg2);
1858 a1len = len1 * 2 + 2;
1859 a2len = len2 * 2 + 2;
1872 r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1,
1873 (LPWSTR) a1p, a1len / 2);
1876 (
errmsg(
"could not convert string to UTF-16: error code %lu",
1879 ((LPWSTR) a1p)[r] = 0;
1885 r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2,
1886 (LPWSTR) a2p, a2len / 2);
1889 (
errmsg(
"could not convert string to UTF-16: error code %lu",
1892 ((LPWSTR) a2p)[r] = 0;
1896 if (result == 2147483647)
1898 (
errmsg(
"could not compare Unicode strings: %m")));
1920 size_t bufsize1 = (len1 == -1) ? 0 : len1 + 1;
1921 size_t bufsize2 = (len2 == -1) ? 0 : len2 + 1;
1931 return strncoll_libc_win32_utf8(arg1, len1, arg2, len2,
locale);
1946 memcpy(buf1, arg1, len1);
1957 char *buf2 =
buf + bufsize1;
1959 memcpy(buf2, arg2, len2);
1985 strncoll_icu_no_utf8(
const char *arg1, ssize_t len1,
1999 #ifdef HAVE_UCOL_STRCOLLUTF8
2003 init_icu_converter();
2005 ulen1 = uchar_length(icu_converter, arg1, len1);
2006 ulen2 = uchar_length(icu_converter, arg2, len2);
2008 bufsize1 = (ulen1 + 1) *
sizeof(UChar);
2009 bufsize2 = (ulen2 + 1) *
sizeof(UChar);
2014 uchar1 = (UChar *)
buf;
2015 uchar2 = (UChar *) (
buf + bufsize1);
2017 ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1);
2018 ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2);
2020 result = ucol_strcoll(
locale->info.icu.ucol,
2038 strncoll_icu(
const char *arg1, ssize_t len1,
const char *arg2, ssize_t len2,
2045 #ifdef HAVE_UCOL_STRCOLLUTF8
2050 status = U_ZERO_ERROR;
2051 result = ucol_strcollUTF8(
locale->info.icu.ucol,
2055 if (U_FAILURE(status))
2057 (
errmsg(
"collation failed: %s", u_errorName(status))));
2062 result = strncoll_icu_no_utf8(arg1, len1, arg2, len2,
locale);
2080 if (
locale->provider == COLLPROVIDER_LIBC)
2083 else if (
locale->provider == COLLPROVIDER_ICU)
2084 result = strncoll_icu(arg1, -1, arg2, -1,
locale);
2108 pg_strncoll(
const char *arg1, ssize_t len1,
const char *arg2, ssize_t len2,
2113 if (
locale->provider == COLLPROVIDER_LIBC)
2116 else if (
locale->provider == COLLPROVIDER_ICU)
2117 result = strncoll_icu(arg1, len1, arg2, len2,
locale);
2151 memcpy(
buf, src, srclen);
2160 Assert(result >= destsize ||
dest[result] ==
'\0');
2169 strnxfrm_icu(
char *
dest,
size_t destsize,
const char *src, ssize_t srclen,
2181 init_icu_converter();
2183 ulen = uchar_length(icu_converter, src, srclen);
2185 uchar_bsize = (ulen + 1) *
sizeof(UChar);
2190 uchar = (UChar *)
buf;
2192 ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
2194 result_bsize = ucol_getSortKey(
locale->info.icu.ucol,
2196 (uint8_t *)
dest, destsize);
2202 Assert(result_bsize > 0);
2209 Assert(result_bsize >= destsize ||
dest[result_bsize] ==
'\0');
2211 return result_bsize;
2216 strnxfrm_prefix_icu_no_utf8(
char *
dest,
size_t destsize,
2217 const char *src, ssize_t srclen,
2226 UChar *uchar = NULL;
2233 init_icu_converter();
2235 ulen = uchar_length(icu_converter, src, srclen);
2237 uchar_bsize = (ulen + 1) *
sizeof(UChar);
2242 uchar = (UChar *)
buf;
2244 ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
2246 uiter_setString(&iter, uchar, ulen);
2248 status = U_ZERO_ERROR;
2249 result_bsize = ucol_nextSortKeyPart(
locale->info.icu.ucol,
2255 if (U_FAILURE(status))
2257 (
errmsg(
"sort key generation failed: %s",
2258 u_errorName(status))));
2260 return result_bsize;
2265 strnxfrm_prefix_icu(
char *
dest,
size_t destsize,
2266 const char *src, ssize_t srclen,
2279 uiter_setUTF8(&iter, src, srclen);
2281 status = U_ZERO_ERROR;
2282 result = ucol_nextSortKeyPart(
locale->info.icu.ucol,
2288 if (U_FAILURE(status))
2290 (
errmsg(
"sort key generation failed: %s",
2291 u_errorName(status))));
2294 result = strnxfrm_prefix_icu_no_utf8(
dest, destsize, src, srclen,
2319 if (
locale->provider == COLLPROVIDER_LIBC)
2320 #ifdef TRUST_STRXFRM
2325 else if (
locale->provider == COLLPROVIDER_ICU)
2344 if (
locale->provider == COLLPROVIDER_LIBC)
2347 else if (
locale->provider == COLLPROVIDER_ICU)
2348 result = strnxfrm_icu(
dest, destsize, src, -1,
locale);
2382 if (
locale->provider == COLLPROVIDER_LIBC)
2385 else if (
locale->provider == COLLPROVIDER_ICU)
2386 result = strnxfrm_icu(
dest, destsize, src, srclen,
locale);
2402 if (
locale->provider == COLLPROVIDER_LIBC)
2404 else if (
locale->provider == COLLPROVIDER_ICU)
2450 if (
locale->provider == COLLPROVIDER_ICU)
2451 result = strnxfrm_prefix_icu(
dest, destsize, src, -1,
locale);
2466 if (strcmp(
locale,
"C") == 0)
2468 if (strcmp(
locale,
"C.UTF-8") == 0)
2472 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
2473 errmsg(
"invalid locale name \"%s\" for builtin provider",
2487 const char *canonical_name = NULL;
2488 int required_encoding;
2490 if (strcmp(
locale,
"C") == 0)
2491 canonical_name =
"C";
2492 else if (strcmp(
locale,
"C.UTF-8") == 0 || strcmp(
locale,
"C.UTF8") == 0)
2493 canonical_name =
"C.UTF-8";
2495 if (!canonical_name)
2497 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
2498 errmsg(
"invalid locale name \"%s\" for builtin provider",
2502 if (required_encoding >= 0 &&
encoding != required_encoding)
2504 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
2505 errmsg(
"encoding \"%s\" does not match locale \"%s\"",
2508 return canonical_name;
2521 pg_ucol_open(
const char *loc_str)
2523 UCollator *collator;
2525 const char *orig_str = loc_str;
2526 char *fixed_str = NULL;
2537 if (loc_str == NULL)
2538 elog(
ERROR,
"opening default collator is not supported");
2545 if (U_ICU_VERSION_MAJOR_NUM < 55)
2547 char lang[ULOC_LANG_CAPACITY];
2549 status = U_ZERO_ERROR;
2550 uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
2551 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
2554 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2555 errmsg(
"could not get language from locale \"%s\": %s",
2556 loc_str, u_errorName(status))));
2559 if (strcmp(lang,
"und") == 0)
2561 const char *remainder = loc_str + strlen(
"und");
2563 fixed_str =
palloc(strlen(
"root") + strlen(remainder) + 1);
2564 strcpy(fixed_str,
"root");
2565 strcat(fixed_str, remainder);
2567 loc_str = fixed_str;
2571 status = U_ZERO_ERROR;
2572 collator = ucol_open(loc_str, &status);
2573 if (U_FAILURE(status))
2576 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2577 errmsg(
"could not open collator for locale \"%s\": %s",
2578 orig_str, u_errorName(status))));
2580 if (U_ICU_VERSION_MAJOR_NUM < 54)
2582 status = U_ZERO_ERROR;
2583 icu_set_collation_attributes(collator, loc_str, &status);
2589 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
2591 ucol_close(collator);
2593 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2594 errmsg(
"could not open collator for locale \"%s\": %s",
2595 orig_str, u_errorName(status))));
2599 if (fixed_str != NULL)
2606 init_icu_converter(
void)
2608 const char *icu_encoding_name;
2616 if (!icu_encoding_name)
2618 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2619 errmsg(
"encoding \"%s\" not supported by ICU",
2622 status = U_ZERO_ERROR;
2623 conv = ucnv_open(icu_encoding_name, &status);
2624 if (U_FAILURE(status))
2626 (
errmsg(
"could not open ICU converter for encoding \"%s\": %s",
2627 icu_encoding_name, u_errorName(status))));
2629 icu_converter = conv;
2638 uchar_length(UConverter *converter,
const char *
str, int32_t
len)
2640 UErrorCode status = U_ZERO_ERROR;
2643 ulen = ucnv_toUChars(converter, NULL, 0,
str,
len, &status);
2644 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
2646 (
errmsg(
"%s failed: %s",
"ucnv_toUChars", u_errorName(status))));
2657 uchar_convert(UConverter *converter, UChar *
dest, int32_t destlen,
2658 const char *src, int32_t srclen)
2660 UErrorCode status = U_ZERO_ERROR;
2663 status = U_ZERO_ERROR;
2664 ulen = ucnv_toUChars(converter,
dest, destlen, src, srclen, &status);
2665 if (U_FAILURE(status))
2667 (
errmsg(
"%s failed: %s",
"ucnv_toUChars", u_errorName(status))));
2684 icu_to_uchar(UChar **buff_uchar,
const char *buff,
size_t nbytes)
2688 init_icu_converter();
2690 len_uchar = uchar_length(icu_converter, buff, nbytes);
2692 *buff_uchar =
palloc((len_uchar + 1) *
sizeof(**buff_uchar));
2693 len_uchar = uchar_convert(icu_converter,
2694 *buff_uchar, len_uchar + 1, buff, nbytes);
2711 icu_from_uchar(
char **result,
const UChar *buff_uchar, int32_t len_uchar)
2716 init_icu_converter();
2718 status = U_ZERO_ERROR;
2719 len_result = ucnv_fromUChars(icu_converter, NULL, 0,
2720 buff_uchar, len_uchar, &status);
2721 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
2723 (
errmsg(
"%s failed: %s",
"ucnv_fromUChars",
2724 u_errorName(status))));
2726 *result =
palloc(len_result + 1);
2728 status = U_ZERO_ERROR;
2729 len_result = ucnv_fromUChars(icu_converter, *result, len_result + 1,
2730 buff_uchar, len_uchar, &status);
2731 if (U_FAILURE(status) ||
2732 status == U_STRING_NOT_TERMINATED_WARNING)
2734 (
errmsg(
"%s failed: %s",
"ucnv_fromUChars",
2735 u_errorName(status))));
2754 icu_set_collation_attributes(UCollator *collator, const
char *loc,
2758 char *icu_locale_id;
2770 *status = U_ZERO_ERROR;
2771 len = uloc_canonicalize(loc, NULL, 0, status);
2773 *status = U_ZERO_ERROR;
2774 len = uloc_canonicalize(loc, icu_locale_id,
len + 1, status);
2775 if (U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING)
2778 lower_str =
asc_tolower(icu_locale_id, strlen(icu_locale_id));
2780 pfree(icu_locale_id);
2782 str = strchr(lower_str,
'@');
2789 char *
e = strchr(
token,
'=');
2795 UColAttribute uattr;
2796 UColAttributeValue uvalue;
2798 *status = U_ZERO_ERROR;
2807 if (strcmp(
name,
"colstrength") == 0)
2808 uattr = UCOL_STRENGTH;
2809 else if (strcmp(
name,
"colbackwards") == 0)
2810 uattr = UCOL_FRENCH_COLLATION;
2811 else if (strcmp(
name,
"colcaselevel") == 0)
2812 uattr = UCOL_CASE_LEVEL;
2813 else if (strcmp(
name,
"colcasefirst") == 0)
2814 uattr = UCOL_CASE_FIRST;
2815 else if (strcmp(
name,
"colalternate") == 0)
2816 uattr = UCOL_ALTERNATE_HANDLING;
2817 else if (strcmp(
name,
"colnormalization") == 0)
2818 uattr = UCOL_NORMALIZATION_MODE;
2819 else if (strcmp(
name,
"colnumeric") == 0)
2820 uattr = UCOL_NUMERIC_COLLATION;
2825 if (strcmp(
value,
"primary") == 0)
2826 uvalue = UCOL_PRIMARY;
2827 else if (strcmp(
value,
"secondary") == 0)
2828 uvalue = UCOL_SECONDARY;
2829 else if (strcmp(
value,
"tertiary") == 0)
2830 uvalue = UCOL_TERTIARY;
2831 else if (strcmp(
value,
"quaternary") == 0)
2832 uvalue = UCOL_QUATERNARY;
2833 else if (strcmp(
value,
"identical") == 0)
2834 uvalue = UCOL_IDENTICAL;
2835 else if (strcmp(
value,
"no") == 0)
2837 else if (strcmp(
value,
"yes") == 0)
2839 else if (strcmp(
value,
"shifted") == 0)
2840 uvalue = UCOL_SHIFTED;
2841 else if (strcmp(
value,
"non-ignorable") == 0)
2842 uvalue = UCOL_NON_IGNORABLE;
2843 else if (strcmp(
value,
"lower") == 0)
2844 uvalue = UCOL_LOWER_FIRST;
2845 else if (strcmp(
value,
"upper") == 0)
2846 uvalue = UCOL_UPPER_FIRST;
2849 *status = U_ILLEGAL_ARGUMENT_ERROR;
2853 ucol_setAttribute(collator, uattr, uvalue, status);
2877 const bool strict =
true;
2885 langtag =
palloc(buflen);
2888 status = U_ZERO_ERROR;
2889 uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
2892 if ((status == U_BUFFER_OVERFLOW_ERROR ||
2893 status == U_STRING_NOT_TERMINATED_WARNING) &&
2897 langtag =
repalloc(langtag, buflen);
2904 if (U_FAILURE(status))
2910 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2911 errmsg(
"could not convert locale name \"%s\" to language tag: %s",
2912 loc_str, u_errorName(status))));
2919 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2920 errmsg(
"ICU is not supported in this build")));
2932 UCollator *collator;
2934 char lang[ULOC_LANG_CAPACITY];
2947 status = U_ZERO_ERROR;
2948 uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
2949 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
2952 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2953 errmsg(
"could not get language from ICU locale \"%s\": %s",
2954 loc_str, u_errorName(status)),
2955 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
2956 "icu_validation_level",
"disabled")));
2961 if (strcmp(lang,
"") == 0 ||
2962 strcmp(lang,
"root") == 0 || strcmp(lang,
"und") == 0)
2966 for (int32_t
i = 0; !found &&
i < uloc_countAvailable();
i++)
2968 const char *otherloc = uloc_getAvailable(
i);
2969 char otherlang[ULOC_LANG_CAPACITY];
2971 status = U_ZERO_ERROR;
2972 uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
2973 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
2976 if (strcmp(lang, otherlang) == 0)
2982 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2983 errmsg(
"ICU locale \"%s\" has unknown language \"%s\"",
2985 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
2986 "icu_validation_level",
"disabled")));
2989 collator = pg_ucol_open(loc_str);
2990 ucol_close(collator);
2994 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2995 errmsg(
"ICU is not supported in this build")));
3030 result = WideCharToMultiByte(CP_UTF8, 0, from, -1, to, tolen,
3047 result = wcstombs(to, from, tolen);
3068 char2wchar(
wchar_t *to,
size_t tolen,
const char *from,
size_t fromlen,
3087 result = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen - 1);
3109 result = mbstowcs(to,
str, tolen);
3133 (
errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
3134 errmsg(
"invalid multibyte character for locale"),
3135 errhint(
"The server's LC_CTYPE locale is probably incompatible with the database encoding.")));
#define TextDatumGetCString(d)
#define pg_attribute_unused()
#define Assert(condition)
#define OidIsValid(objectId)
static void PGresult * res
int errdetail(const char *fmt,...)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
const char * get_encoding_name_for_icu(int encoding)
#define MCXT_ALLOC_NO_OOM
#define HeapTupleIsValid(tuple)
char * get_namespace_name(Oid nspid)
char * pg_any_to_server(const char *s, int len, int encoding)
int GetDatabaseEncoding(void)
int pg_mbstrlen(const char *mbstr)
bool pg_verifymbstr(const char *mbstr, int len, bool noError)
void SetMessageEncoding(int encoding)
char * pnstrdup(const char *in, Size len)
char * pstrdup(const char *in)
void pfree(void *pointer)
MemoryContext TopMemoryContext
void * repalloc(void *pointer, Size size)
void * palloc_extended(Size size, int flags)
void * MemoryContextAlloc(MemoryContext context, Size size)
char * MemoryContextStrdup(MemoryContext context, const char *string)
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
FormData_pg_collation * Form_pg_collation
FormData_pg_database * Form_pg_database
static pg_locale_t last_collation_cache_locale
void cache_locale_time(void)
size_t pg_strnxfrm(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale)
bool pg_strxfrm_enabled(pg_locale_t locale)
char * localized_full_months[12+1]
size_t wchar2char(char *to, const wchar_t *from, size_t tolen, pg_locale_t locale)
struct lconv * PGLC_localeconv(void)
static struct pg_locale_struct default_locale
void icu_validate_locale(const char *loc_str)
static bool CurrentLCTimeValid
void assign_locale_time(const char *newval, void *extra)
bool check_locale_time(char **newval, void **extra, GucSource source)
pg_locale_t pg_newlocale_from_collation(Oid collid)
int builtin_locale_encoding(const char *locale)
size_t pg_strnxfrm_prefix(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale)
#define PGLOCALE_SUPPORT_ERROR(provider)
static void cache_single_string(char **dst, const char *src, int encoding)
static locale_t make_libc_collator(const char *collate, const char *ctype)
bool check_locale_numeric(char **newval, void **extra, GucSource source)
static void db_encoding_convert(int encoding, char **str)
static size_t wcstombs_l(char *dest, const wchar_t *src, size_t n, locale_t loc)
void assign_locale_numeric(const char *newval, void *extra)
bool check_locale_messages(char **newval, void **extra, GucSource source)
char * get_collation_actual_version(char collprovider, const char *collcollate)
static void free_struct_lconv(struct lconv *s)
char * pg_perm_setlocale(int category, const char *locale)
static int strncoll_libc(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2, pg_locale_t locale)
static MemoryContext CollationCacheContext
void assign_locale_messages(const char *newval, void *extra)
static bool CurrentLocaleConvValid
char * icu_language_tag(const char *loc_str, int elevel)
static size_t strnxfrm_libc(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale)
int pg_strcoll(const char *arg1, const char *arg2, pg_locale_t locale)
bool pg_strxfrm_prefix_enabled(pg_locale_t locale)
static void report_newlocale_failure(const char *localename)
char * localized_abbrev_months[12+1]
static collation_cache_hash * CollationCache
int pg_strncoll(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2, pg_locale_t locale)
static bool struct_lconv_is_valid(struct lconv *s)
void init_database_collation(void)
char * localized_full_days[7+1]
static collation_cache_entry * lookup_collation_cache(Oid collation)
size_t pg_strxfrm(char *dest, const char *src, size_t destsize, pg_locale_t locale)
const char * builtin_validate_locale(int encoding, const char *locale)
void assign_locale_monetary(const char *newval, void *extra)
bool check_locale(int category, const char *locale, char **canonname)
char * localized_abbrev_days[7+1]
size_t pg_strxfrm_prefix(char *dest, const char *src, size_t destsize, pg_locale_t locale)
size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, pg_locale_t locale)
bool check_locale_monetary(char **newval, void **extra, GucSource source)
static size_t mbstowcs_l(wchar_t *dest, const char *src, size_t n, locale_t loc)
static Oid last_collation_cache_oid
#define LOCALE_NAME_BUFLEN
static rewind_source * source
#define pg_encoding_to_char
int pg_strcasecmp(const char *s1, const char *s2)
char * strsep(char **stringp, const char *delim)
int pg_get_encoding_from_locale(const char *ctype, bool write_message)
size_t strlcpy(char *dst, const char *src, size_t siz)
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
static Datum ObjectIdGetDatum(Oid X)
char * psprintf(const char *fmt,...)
char * quote_qualified_identifier(const char *qualifier, const char *ident)
bool pg_is_ascii(const char *str)
union pg_locale_struct::@153 info
struct pg_locale_struct::@153::@154 builtin
void ReleaseSysCache(HeapTuple tuple)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)