57#define PGLOCALE_SUPPORT_ERROR(provider) \
58 elog(ERROR, "unsupported collprovider for %s: %c", __func__, provider)
64#define TEXTBUFLEN 1024
66#define MAX_L10N_DATA 80
74extern UCollator *pg_ucol_open(
const char *loc_str);
75extern char *get_collation_actual_version_icu(
const char *collcollate);
111 .collate_is_c =
true,
127#define SH_PREFIX collation_cache
128#define SH_ELEMENT_TYPE collation_cache_entry
129#define SH_KEY_TYPE Oid
131#define SH_HASH_KEY(tb, key) murmurhash32((uint32) key)
132#define SH_EQUAL(tb, a, b) (a == b)
133#define SH_GET_HASH(tb, a) a->hash
134#define SH_SCOPE static inline
150#if defined(WIN32) && defined(LC_MESSAGES)
151static char *IsoLocaleName(
const char *);
185 if (category == LC_MESSAGES)
206 if (category == LC_CTYPE)
211 strlcpy(save_lc_ctype, result,
sizeof(save_lc_ctype));
212 result = save_lc_ctype;
224 envvar =
"LC_COLLATE";
231 envvar =
"LC_MESSAGES";
233 result = IsoLocaleName(
locale);
236 elog(
DEBUG3,
"IsoLocaleName() executed; locale: \"%s\"", result);
241 envvar =
"LC_MONETARY";
244 envvar =
"LC_NUMERIC";
250 elog(
FATAL,
"unrecognized LC category: %d", category);
254 if (
setenv(envvar, result, 1) != 0)
281 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
282 errmsg(
"locale name \"%s\" contains non-ASCII characters",
301 if (res && canonname)
306 elog(
WARNING,
"failed to restore old locale \"%s\"", save);
310 if (canonname && *canonname && !
pg_is_ascii(*canonname))
313 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
314 errmsg(
"locale name \"%s\" contains non-ASCII characters",
321 return (res != NULL);
398#if defined(LC_MESSAGES) && !defined(WIN32)
425 free(s->decimal_point);
426 free(s->thousands_sep);
428 free(s->int_curr_symbol);
429 free(s->currency_symbol);
430 free(s->mon_decimal_point);
431 free(s->mon_thousands_sep);
432 free(s->mon_grouping);
433 free(s->positive_sign);
434 free(s->negative_sign);
444 if (s->decimal_point == NULL)
446 if (s->thousands_sep == NULL)
448 if (s->grouping == NULL)
450 if (s->int_curr_symbol == NULL)
452 if (s->currency_symbol == NULL)
454 if (s->mon_decimal_point == NULL)
456 if (s->mon_thousands_sep == NULL)
458 if (s->mon_grouping == NULL)
460 if (s->positive_sign == NULL)
462 if (s->negative_sign == NULL)
487 (
errcode(ERRCODE_OUT_OF_MEMORY),
488 errmsg(
"out of memory")));
505 static struct lconv CurrentLocaleConv;
506 static bool CurrentLocaleConvAllocated =
false;
507 struct lconv *extlconv;
509 struct lconv worklconv = {0};
513 return &CurrentLocaleConv;
516 if (CurrentLocaleConvAllocated)
519 CurrentLocaleConvAllocated =
false;
530 "could not get lconv for LC_MONETARY = \"%s\", LC_NUMERIC = \"%s\": %m",
535 worklconv.decimal_point = strdup(extlconv->decimal_point);
536 worklconv.thousands_sep = strdup(extlconv->thousands_sep);
537 worklconv.grouping = strdup(extlconv->grouping);
538 worklconv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
539 worklconv.currency_symbol = strdup(extlconv->currency_symbol);
540 worklconv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
541 worklconv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
542 worklconv.mon_grouping = strdup(extlconv->mon_grouping);
543 worklconv.positive_sign = strdup(extlconv->positive_sign);
544 worklconv.negative_sign = strdup(extlconv->negative_sign);
546 worklconv.int_frac_digits = extlconv->int_frac_digits;
547 worklconv.frac_digits = extlconv->frac_digits;
548 worklconv.p_cs_precedes = extlconv->p_cs_precedes;
549 worklconv.p_sep_by_space = extlconv->p_sep_by_space;
550 worklconv.n_cs_precedes = extlconv->n_cs_precedes;
551 worklconv.n_sep_by_space = extlconv->n_sep_by_space;
552 worklconv.p_sign_posn = extlconv->p_sign_posn;
553 worklconv.n_sign_posn = extlconv->n_sign_posn;
561 (
errcode(ERRCODE_OUT_OF_MEMORY),
562 errmsg(
"out of memory")));
605 CurrentLocaleConv = worklconv;
606 CurrentLocaleConvAllocated =
true;
608 return &CurrentLocaleConv;
628strftime_l_win32(
char *dst,
size_t dstlen,
639 len = MultiByteToWideChar(CP_UTF8, 0,
format, -1,
642 elog(
ERROR,
"could not convert format string from UTF-8: error code %lu",
655 len = WideCharToMultiByte(CP_UTF8, 0, wbuf,
len, dst, dstlen - 1,
658 elog(
ERROR,
"could not convert string to UTF-8: error code %lu",
667#define strftime_l(a,b,c,d,e) strftime_l_win32(a,b,c,d,e)
705 struct tm timeinfobuf;
706 bool strftimefail =
false;
729 timenow = time(NULL);
730 timeinfo = gmtime_r(&timenow, &timeinfobuf);
746 for (
i = 0;
i < 7;
i++)
748 timeinfo->tm_wday =
i;
758 for (
i = 0;
i < 12;
i++)
760 timeinfo->tm_mon =
i;
761 timeinfo->tm_mday = 1;
807 for (
i = 0;
i < 7;
i++)
818 for (
i = 0;
i < 12;
i++)
832#if defined(WIN32) && defined(LC_MESSAGES)
885search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
887 wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
892 argv = (
wchar_t **) lparam;
893 *argv[2] = (wchar_t) 0;
895 memset(test_locale, 0,
sizeof(test_locale));
898 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
899 test_locale, LOCALE_NAME_MAX_LENGTH))
906 if (wcsrchr(pStr,
'-') == NULL || wcsrchr(argv[0],
'_') == NULL)
908 if (_wcsicmp(argv[0], test_locale) == 0)
910 wcscpy(argv[1], pStr);
911 *argv[2] = (wchar_t) 1;
925 wcscat(test_locale, L
"_");
926 len = wcslen(test_locale);
927 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
929 LOCALE_NAME_MAX_LENGTH -
len))
931 if (_wcsicmp(argv[0], test_locale) == 0)
933 wcscpy(argv[1], pStr);
934 *argv[2] = (wchar_t) 1;
951get_iso_localename(
const char *winlocname)
953 wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
954 wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
955 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
967 period = strchr(winlocname,
'.');
973 memset(wc_locale_name, 0,
sizeof(wc_locale_name));
974 memset(buffer, 0,
sizeof(buffer));
975 MultiByteToWideChar(CP_ACP, 0, winlocname,
len, wc_locale_name,
976 LOCALE_NAME_MAX_LENGTH);
982 ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME, (LPWSTR) &buffer,
983 LOCALE_NAME_MAX_LENGTH);
992 argv[0] = wc_locale_name;
994 argv[2] = (
wchar_t *) &ret_val;
995 EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS, (LPARAM) argv,
1005 rc =
wchar2char(iso_lc_messages, buffer,
sizeof(iso_lc_messages), NULL);
1006 if (rc == -1 || rc ==
sizeof(iso_lc_messages))
1016 hyphen = strchr(iso_lc_messages,
'-');
1019 return iso_lc_messages;
1026IsoLocaleName(
const char *winlocname)
1028 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
1033 strcpy(iso_lc_messages,
"C");
1034 return iso_lc_messages;
1037 return get_iso_localename(winlocname);
1059 if (collform->collprovider == COLLPROVIDER_BUILTIN)
1061 else if (collform->collprovider == COLLPROVIDER_ICU)
1063 else if (collform->collprovider == COLLPROVIDER_LIBC)
1081 char *actual_versionstr;
1082 char *collversionstr;
1086 if (collform->collprovider == COLLPROVIDER_LIBC)
1093 if (!actual_versionstr)
1101 (
errmsg(
"collation \"%s\" has no actual version, but a version was recorded",
1102 NameStr(collform->collname))));
1105 if (strcmp(actual_versionstr, collversionstr) != 0)
1107 (
errmsg(
"collation \"%s\" has version mismatch",
1109 errdetail(
"The collation in the database was created using version %s, "
1110 "but the operating system provides version %s.",
1111 collversionstr, actual_versionstr),
1112 errhint(
"Rebuild all objects affected by this collation and run "
1113 "ALTER COLLATION %s REFRESH VERSION, "
1114 "or build PostgreSQL with the right library version.",
1116 NameStr(collform->collname)))));
1142 if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
1145 else if (dbform->datlocprovider == COLLPROVIDER_ICU)
1148 else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
1191 if (
collid == DEFAULT_COLLATION_OID)
1198 if (
collid == C_COLLATION_OID)
1228 if (cache_entry->
locale == 0)
1236 return cache_entry->
locale;
1246 char *collversion = NULL;
1248 if (collprovider == COLLPROVIDER_BUILTIN)
1251 else if (collprovider == COLLPROVIDER_ICU)
1252 collversion = get_collation_actual_version_icu(collcollate);
1254 else if (collprovider == COLLPROVIDER_LIBC)
1261pg_strlower(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1264 return locale->ctype->strlower(dst, dstsize, src, srclen,
locale);
1268pg_strtitle(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1271 return locale->ctype->strtitle(dst, dstsize, src, srclen,
locale);
1275pg_strupper(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1278 return locale->ctype->strupper(dst, dstsize, src, srclen,
locale);
1282pg_strfold(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1285 if (
locale->ctype->strfold)
1286 return locale->ctype->strfold(dst, dstsize, src, srclen,
locale);
1288 return locale->ctype->strlower(dst, dstsize, src, srclen,
locale);
1299 return locale->collate->strncoll(arg1, -1, arg2, -1,
locale);
1317pg_strncoll(
const char *arg1, ssize_t len1,
const char *arg2, ssize_t len2,
1320 return locale->collate->strncoll(arg1, len1, arg2, len2,
locale);
1338 return locale->collate->strxfrm_is_safe;
1385 return (
locale->collate->strnxfrm_prefix != NULL);
1422 return locale->collate->strnxfrm_prefix(
dest, destsize, src, srclen,
locale);
1428 if (
locale->ctype == NULL)
1438 if (
locale->ctype == NULL)
1448 if (
locale->ctype == NULL)
1458 if (
locale->ctype == NULL)
1468 if (
locale->ctype == NULL)
1478 if (
locale->ctype == NULL)
1488 if (
locale->ctype == NULL)
1498 if (
locale->ctype == NULL)
1508 if (
locale->ctype == NULL)
1518 if (
locale->ctype == NULL)
1521 ((wc >=
'A' && wc <=
'F') ||
1522 (wc >=
'a' && wc <=
'f'))));
1530 if (
locale->ctype == NULL)
1543 if (
locale->ctype == NULL)
1574 return (
locale->ctype->char_tolower != NULL);
1595 if (strcmp(
locale,
"C") == 0)
1597 else if (strcmp(
locale,
"C.UTF-8") == 0)
1599 else if (strcmp(
locale,
"PG_UNICODE_FAST") == 0)
1604 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1605 errmsg(
"invalid locale name \"%s\" for builtin provider",
1619 const char *canonical_name = NULL;
1620 int required_encoding;
1622 if (strcmp(
locale,
"C") == 0)
1623 canonical_name =
"C";
1624 else if (strcmp(
locale,
"C.UTF-8") == 0 || strcmp(
locale,
"C.UTF8") == 0)
1625 canonical_name =
"C.UTF-8";
1626 else if (strcmp(
locale,
"PG_UNICODE_FAST") == 0)
1627 canonical_name =
"PG_UNICODE_FAST";
1629 if (!canonical_name)
1631 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1632 errmsg(
"invalid locale name \"%s\" for builtin provider",
1636 if (required_encoding >= 0 &&
encoding != required_encoding)
1638 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1639 errmsg(
"encoding \"%s\" does not match locale \"%s\"",
1642 return canonical_name;
1663 const bool strict =
true;
1671 langtag =
palloc(buflen);
1674 status = U_ZERO_ERROR;
1675 uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
1678 if ((status == U_BUFFER_OVERFLOW_ERROR ||
1679 status == U_STRING_NOT_TERMINATED_WARNING) &&
1683 langtag =
repalloc(langtag, buflen);
1690 if (U_FAILURE(status))
1696 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1697 errmsg(
"could not convert locale name \"%s\" to language tag: %s",
1698 loc_str, u_errorName(status))));
1705 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1706 errmsg(
"ICU is not supported in this build")));
1718 UCollator *collator;
1720 char lang[ULOC_LANG_CAPACITY];
1733 status = U_ZERO_ERROR;
1734 uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
1735 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1738 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1739 errmsg(
"could not get language from ICU locale \"%s\": %s",
1740 loc_str, u_errorName(status)),
1741 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1742 "icu_validation_level",
"disabled")));
1747 if (strcmp(lang,
"") == 0 ||
1748 strcmp(lang,
"root") == 0 || strcmp(lang,
"und") == 0)
1752 for (int32_t
i = 0; !found &&
i < uloc_countAvailable();
i++)
1754 const char *otherloc = uloc_getAvailable(
i);
1755 char otherlang[ULOC_LANG_CAPACITY];
1757 status = U_ZERO_ERROR;
1758 uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
1759 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1762 if (strcmp(lang, otherlang) == 0)
1768 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1769 errmsg(
"ICU locale \"%s\" has unknown language \"%s\"",
1771 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1772 "icu_validation_level",
"disabled")));
1775 collator = pg_ucol_open(loc_str);
1776 ucol_close(collator);
1780 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1781 errmsg(
"ICU is not supported in this build")));
#define TextDatumGetCString(d)
#define OidIsValid(objectId)
int errdetail(const char *fmt,...)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
Assert(PointerIsAligned(start, uint64))
#define HeapTupleIsValid(tuple)
static void * GETSTRUCT(const HeapTupleData *tuple)
char * get_namespace_name(Oid nspid)
int GetDatabaseEncoding(void)
char * pg_any_to_server(const char *s, int len, int encoding)
int pg_mbstrlen(const char *mbstr)
void SetMessageEncoding(int encoding)
char * MemoryContextStrdup(MemoryContext context, const char *string)
char * pstrdup(const char *in)
void * repalloc(void *pointer, Size size)
void pfree(void *pointer)
MemoryContext TopMemoryContext
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
FormData_pg_collation * Form_pg_collation
FormData_pg_database * Form_pg_database
char char_tolower(unsigned char ch, pg_locale_t locale)
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]
pg_wchar pg_towlower(pg_wchar wc, pg_locale_t locale)
bool pg_iswalnum(pg_wchar wc, pg_locale_t locale)
void icu_validate_locale(const char *loc_str)
pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context)
static bool CurrentLCTimeValid
void assign_locale_time(const char *newval, void *extra)
char * get_collation_actual_version(char collprovider, const char *collcollate)
pg_locale_t create_pg_locale_builtin(Oid collid, MemoryContext context)
bool check_locale_time(char **newval, void **extra, GucSource source)
bool pg_iswgraph(pg_wchar wc, pg_locale_t locale)
bool pg_iswprint(pg_wchar wc, pg_locale_t locale)
pg_locale_t pg_newlocale_from_collation(Oid collid)
bool pg_iswdigit(pg_wchar wc, pg_locale_t locale)
bool pg_iswupper(pg_wchar wc, pg_locale_t locale)
size_t pg_strfold(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
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)
static struct pg_locale_struct c_locale
bool pg_iswxdigit(pg_wchar wc, pg_locale_t locale)
char * pg_perm_setlocale(int category, const char *locale)
#define PGLOCALE_SUPPORT_ERROR(provider)
static pg_locale_t create_pg_locale(Oid collid, MemoryContext context)
static void cache_single_string(char **dst, const char *src, int encoding)
char * get_collation_actual_version_libc(const char *collcollate)
size_t pg_strlower(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
pg_wchar pg_towupper(pg_wchar wc, pg_locale_t locale)
bool check_locale_numeric(char **newval, void **extra, GucSource source)
bool char_tolower_enabled(pg_locale_t locale)
pg_locale_t pg_database_locale(void)
bool pg_iswspace(pg_wchar wc, pg_locale_t locale)
static void db_encoding_convert(int encoding, char **str)
void assign_locale_numeric(const char *newval, void *extra)
bool check_locale_messages(char **newval, void **extra, GucSource source)
char * get_collation_actual_version_builtin(const char *collcollate)
static void free_struct_lconv(struct lconv *s)
static MemoryContext CollationCacheContext
void assign_locale_messages(const char *newval, void *extra)
static bool CurrentLocaleConvValid
struct lconv * PGLC_localeconv(void)
pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context)
size_t pg_strtitle(char *dst, size_t dstsize, 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_iswpunct(pg_wchar wc, pg_locale_t locale)
bool pg_strxfrm_prefix_enabled(pg_locale_t locale)
char * icu_language_tag(const char *loc_str, int elevel)
bool char_is_cased(char ch, pg_locale_t locale)
char * localized_abbrev_months[12+1]
static pg_locale_t default_locale
static collation_cache_hash * CollationCache
int pg_strncoll(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2, pg_locale_t locale)
bool pg_iswlower(pg_wchar wc, pg_locale_t locale)
static bool struct_lconv_is_valid(struct lconv *s)
void init_database_collation(void)
char * localized_full_days[7+1]
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)
size_t pg_strupper(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
bool pg_iswalpha(pg_wchar wc, pg_locale_t 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)
bool check_locale_monetary(char **newval, void **extra, GucSource source)
static Oid last_collation_cache_oid
#define LOCALE_NAME_BUFLEN
static const unsigned char pg_char_properties[128]
size_t wchar2char(char *to, const wchar_t *from, size_t tolen, locale_t loc)
void report_newlocale_failure(const char *localename)
static rewind_source * source
#define pg_encoding_to_char
int pg_strcasecmp(const char *s1, const char *s2)
int pg_localeconv_r(const char *lc_monetary, const char *lc_numeric, struct lconv *output)
unsigned char pg_ascii_tolower(unsigned char ch)
int pg_get_encoding_from_locale(const char *ctype, bool write_message)
unsigned char pg_ascii_toupper(unsigned char ch)
size_t strlcpy(char *dst, const char *src, size_t siz)
void pg_localeconv_free(struct lconv *lconv)
static Datum ObjectIdGetDatum(Oid X)
static void AssertCouldGetRelation(void)
char * quote_qualified_identifier(const char *qualifier, const char *ident)
bool pg_is_ascii(const char *str)
const struct ctype_methods * ctype
const struct collate_methods * collate
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)
void _dosmaperr(unsigned long)