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);
89extern size_t strfold_builtin(
char *dst,
size_t dstsize,
const char *src,
92extern size_t strlower_icu(
char *dst,
size_t dstsize,
const char *src,
94extern size_t strtitle_icu(
char *dst,
size_t dstsize,
const char *src,
96extern size_t strupper_icu(
char *dst,
size_t dstsize,
const char *src,
98extern size_t strfold_icu(
char *dst,
size_t dstsize,
const char *src,
101extern size_t strlower_libc(
char *dst,
size_t dstsize,
const char *src,
103extern size_t strtitle_libc(
char *dst,
size_t dstsize,
const char *src,
105extern size_t strupper_libc(
char *dst,
size_t dstsize,
const char *src,
149#define SH_PREFIX collation_cache
150#define SH_ELEMENT_TYPE collation_cache_entry
151#define SH_KEY_TYPE Oid
153#define SH_HASH_KEY(tb, key) murmurhash32((uint32) key)
154#define SH_EQUAL(tb, a, b) (a == b)
155#define SH_GET_HASH(tb, a) a->hash
156#define SH_SCOPE static inline
172#if defined(WIN32) && defined(LC_MESSAGES)
173static char *IsoLocaleName(
const char *);
207 if (category == LC_MESSAGES)
228 if (category == LC_CTYPE)
233 strlcpy(save_lc_ctype, result,
sizeof(save_lc_ctype));
234 result = save_lc_ctype;
246 envvar =
"LC_COLLATE";
253 envvar =
"LC_MESSAGES";
255 result = IsoLocaleName(
locale);
258 elog(
DEBUG3,
"IsoLocaleName() executed; locale: \"%s\"", result);
263 envvar =
"LC_MONETARY";
266 envvar =
"LC_NUMERIC";
272 elog(
FATAL,
"unrecognized LC category: %d", category);
276 if (
setenv(envvar, result, 1) != 0)
303 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
304 errmsg(
"locale name \"%s\" contains non-ASCII characters",
323 if (res && canonname)
328 elog(
WARNING,
"failed to restore old locale \"%s\"", save);
332 if (canonname && *canonname && !
pg_is_ascii(*canonname))
335 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
336 errmsg(
"locale name \"%s\" contains non-ASCII characters",
343 return (res != NULL);
420#if defined(LC_MESSAGES) && !defined(WIN32)
447 free(s->decimal_point);
448 free(s->thousands_sep);
450 free(s->int_curr_symbol);
451 free(s->currency_symbol);
452 free(s->mon_decimal_point);
453 free(s->mon_thousands_sep);
454 free(s->mon_grouping);
455 free(s->positive_sign);
456 free(s->negative_sign);
466 if (s->decimal_point == NULL)
468 if (s->thousands_sep == NULL)
470 if (s->grouping == NULL)
472 if (s->int_curr_symbol == NULL)
474 if (s->currency_symbol == NULL)
476 if (s->mon_decimal_point == NULL)
478 if (s->mon_thousands_sep == NULL)
480 if (s->mon_grouping == NULL)
482 if (s->positive_sign == NULL)
484 if (s->negative_sign == NULL)
509 (
errcode(ERRCODE_OUT_OF_MEMORY),
510 errmsg(
"out of memory")));
527 static struct lconv CurrentLocaleConv;
528 static bool CurrentLocaleConvAllocated =
false;
529 struct lconv *extlconv;
531 struct lconv worklconv = {0};
535 return &CurrentLocaleConv;
538 if (CurrentLocaleConvAllocated)
541 CurrentLocaleConvAllocated =
false;
552 "could not get lconv for LC_MONETARY = \"%s\", LC_NUMERIC = \"%s\": %m",
557 worklconv.decimal_point = strdup(extlconv->decimal_point);
558 worklconv.thousands_sep = strdup(extlconv->thousands_sep);
559 worklconv.grouping = strdup(extlconv->grouping);
560 worklconv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
561 worklconv.currency_symbol = strdup(extlconv->currency_symbol);
562 worklconv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
563 worklconv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
564 worklconv.mon_grouping = strdup(extlconv->mon_grouping);
565 worklconv.positive_sign = strdup(extlconv->positive_sign);
566 worklconv.negative_sign = strdup(extlconv->negative_sign);
568 worklconv.int_frac_digits = extlconv->int_frac_digits;
569 worklconv.frac_digits = extlconv->frac_digits;
570 worklconv.p_cs_precedes = extlconv->p_cs_precedes;
571 worklconv.p_sep_by_space = extlconv->p_sep_by_space;
572 worklconv.n_cs_precedes = extlconv->n_cs_precedes;
573 worklconv.n_sep_by_space = extlconv->n_sep_by_space;
574 worklconv.p_sign_posn = extlconv->p_sign_posn;
575 worklconv.n_sign_posn = extlconv->n_sign_posn;
583 (
errcode(ERRCODE_OUT_OF_MEMORY),
584 errmsg(
"out of memory")));
627 CurrentLocaleConv = worklconv;
628 CurrentLocaleConvAllocated =
true;
630 return &CurrentLocaleConv;
650strftime_l_win32(
char *dst,
size_t dstlen,
661 len = MultiByteToWideChar(CP_UTF8, 0,
format, -1,
664 elog(
ERROR,
"could not convert format string from UTF-8: error code %lu",
677 len = WideCharToMultiByte(CP_UTF8, 0, wbuf,
len, dst, dstlen - 1,
680 elog(
ERROR,
"could not convert string to UTF-8: error code %lu",
689#define strftime_l(a,b,c,d,e) strftime_l_win32(a,b,c,d,e)
727 struct tm timeinfobuf;
728 bool strftimefail =
false;
751 timenow = time(NULL);
752 timeinfo = gmtime_r(&timenow, &timeinfobuf);
768 for (
i = 0;
i < 7;
i++)
770 timeinfo->tm_wday =
i;
780 for (
i = 0;
i < 12;
i++)
782 timeinfo->tm_mon =
i;
783 timeinfo->tm_mday = 1;
829 for (
i = 0;
i < 7;
i++)
840 for (
i = 0;
i < 12;
i++)
854#if defined(WIN32) && defined(LC_MESSAGES)
907search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
909 wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
914 argv = (
wchar_t **) lparam;
915 *argv[2] = (wchar_t) 0;
917 memset(test_locale, 0,
sizeof(test_locale));
920 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
921 test_locale, LOCALE_NAME_MAX_LENGTH))
928 if (wcsrchr(pStr,
'-') == NULL || wcsrchr(argv[0],
'_') == NULL)
930 if (_wcsicmp(argv[0], test_locale) == 0)
932 wcscpy(argv[1], pStr);
933 *argv[2] = (wchar_t) 1;
947 wcscat(test_locale, L
"_");
948 len = wcslen(test_locale);
949 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
951 LOCALE_NAME_MAX_LENGTH -
len))
953 if (_wcsicmp(argv[0], test_locale) == 0)
955 wcscpy(argv[1], pStr);
956 *argv[2] = (wchar_t) 1;
973get_iso_localename(
const char *winlocname)
975 wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
976 wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
977 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
989 period = strchr(winlocname,
'.');
995 memset(wc_locale_name, 0,
sizeof(wc_locale_name));
996 memset(buffer, 0,
sizeof(buffer));
997 MultiByteToWideChar(CP_ACP, 0, winlocname,
len, wc_locale_name,
998 LOCALE_NAME_MAX_LENGTH);
1004 ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME, (LPWSTR) &buffer,
1005 LOCALE_NAME_MAX_LENGTH);
1014 argv[0] = wc_locale_name;
1016 argv[2] = (
wchar_t *) &ret_val;
1017 EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS, (LPARAM) argv,
1027 rc =
wchar2char(iso_lc_messages, buffer,
sizeof(iso_lc_messages), NULL);
1028 if (rc == -1 || rc ==
sizeof(iso_lc_messages))
1038 hyphen = strchr(iso_lc_messages,
'-');
1041 return iso_lc_messages;
1048IsoLocaleName(
const char *winlocname)
1050 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
1055 strcpy(iso_lc_messages,
"C");
1056 return iso_lc_messages;
1059 return get_iso_localename(winlocname);
1081 if (collform->collprovider == COLLPROVIDER_BUILTIN)
1083 else if (collform->collprovider == COLLPROVIDER_ICU)
1085 else if (collform->collprovider == COLLPROVIDER_LIBC)
1100 char *actual_versionstr;
1101 char *collversionstr;
1105 if (collform->collprovider == COLLPROVIDER_LIBC)
1112 if (!actual_versionstr)
1120 (
errmsg(
"collation \"%s\" has no actual version, but a version was recorded",
1121 NameStr(collform->collname))));
1124 if (strcmp(actual_versionstr, collversionstr) != 0)
1126 (
errmsg(
"collation \"%s\" has version mismatch",
1128 errdetail(
"The collation in the database was created using version %s, "
1129 "but the operating system provides version %s.",
1130 collversionstr, actual_versionstr),
1131 errhint(
"Rebuild all objects affected by this collation and run "
1132 "ALTER COLLATION %s REFRESH VERSION, "
1133 "or build PostgreSQL with the right library version.",
1135 NameStr(collform->collname)))));
1161 if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
1164 else if (dbform->datlocprovider == COLLPROVIDER_ICU)
1167 else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
1194 if (
collid == DEFAULT_COLLATION_OID)
1224 if (cache_entry->
locale == 0)
1232 return cache_entry->
locale;
1242 char *collversion = NULL;
1244 if (collprovider == COLLPROVIDER_BUILTIN)
1247 else if (collprovider == COLLPROVIDER_ICU)
1248 collversion = get_collation_actual_version_icu(collcollate);
1250 else if (collprovider == COLLPROVIDER_LIBC)
1257pg_strlower(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1260 if (
locale->provider == COLLPROVIDER_BUILTIN)
1263 else if (
locale->provider == COLLPROVIDER_ICU)
1266 else if (
locale->provider == COLLPROVIDER_LIBC)
1276pg_strtitle(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1279 if (
locale->provider == COLLPROVIDER_BUILTIN)
1282 else if (
locale->provider == COLLPROVIDER_ICU)
1285 else if (
locale->provider == COLLPROVIDER_LIBC)
1295pg_strupper(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1298 if (
locale->provider == COLLPROVIDER_BUILTIN)
1301 else if (
locale->provider == COLLPROVIDER_ICU)
1304 else if (
locale->provider == COLLPROVIDER_LIBC)
1314pg_strfold(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1317 if (
locale->provider == COLLPROVIDER_BUILTIN)
1320 else if (
locale->provider == COLLPROVIDER_ICU)
1324 else if (
locale->provider == COLLPROVIDER_LIBC)
1341 return locale->collate->strncoll(arg1, -1, arg2, -1,
locale);
1359pg_strncoll(
const char *arg1, ssize_t len1,
const char *arg2, ssize_t len2,
1362 return locale->collate->strncoll(arg1, len1, arg2, len2,
locale);
1380 return locale->collate->strxfrm_is_safe;
1427 return (
locale->collate->strnxfrm_prefix != NULL);
1464 return locale->collate->strnxfrm_prefix(
dest, destsize, src, srclen,
locale);
1474 if (strcmp(
locale,
"C") == 0)
1476 else if (strcmp(
locale,
"C.UTF-8") == 0)
1478 else if (strcmp(
locale,
"PG_UNICODE_FAST") == 0)
1483 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1484 errmsg(
"invalid locale name \"%s\" for builtin provider",
1498 const char *canonical_name = NULL;
1499 int required_encoding;
1501 if (strcmp(
locale,
"C") == 0)
1502 canonical_name =
"C";
1503 else if (strcmp(
locale,
"C.UTF-8") == 0 || strcmp(
locale,
"C.UTF8") == 0)
1504 canonical_name =
"C.UTF-8";
1505 else if (strcmp(
locale,
"PG_UNICODE_FAST") == 0)
1506 canonical_name =
"PG_UNICODE_FAST";
1508 if (!canonical_name)
1510 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1511 errmsg(
"invalid locale name \"%s\" for builtin provider",
1515 if (required_encoding >= 0 &&
encoding != required_encoding)
1517 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1518 errmsg(
"encoding \"%s\" does not match locale \"%s\"",
1521 return canonical_name;
1542 const bool strict =
true;
1550 langtag =
palloc(buflen);
1553 status = U_ZERO_ERROR;
1554 uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
1557 if ((status == U_BUFFER_OVERFLOW_ERROR ||
1558 status == U_STRING_NOT_TERMINATED_WARNING) &&
1562 langtag =
repalloc(langtag, buflen);
1569 if (U_FAILURE(status))
1575 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1576 errmsg(
"could not convert locale name \"%s\" to language tag: %s",
1577 loc_str, u_errorName(status))));
1584 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1585 errmsg(
"ICU is not supported in this build")));
1597 UCollator *collator;
1599 char lang[ULOC_LANG_CAPACITY];
1612 status = U_ZERO_ERROR;
1613 uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
1614 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1617 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1618 errmsg(
"could not get language from ICU locale \"%s\": %s",
1619 loc_str, u_errorName(status)),
1620 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1621 "icu_validation_level",
"disabled")));
1626 if (strcmp(lang,
"") == 0 ||
1627 strcmp(lang,
"root") == 0 || strcmp(lang,
"und") == 0)
1631 for (int32_t
i = 0; !found &&
i < uloc_countAvailable();
i++)
1633 const char *otherloc = uloc_getAvailable(
i);
1634 char otherlang[ULOC_LANG_CAPACITY];
1636 status = U_ZERO_ERROR;
1637 uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
1638 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1641 if (strcmp(lang, otherlang) == 0)
1647 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1648 errmsg(
"ICU locale \"%s\" has unknown language \"%s\"",
1650 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1651 "icu_validation_level",
"disabled")));
1654 collator = pg_ucol_open(loc_str);
1655 ucol_close(collator);
1659 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1660 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
size_t strfold_icu(char *dst, size_t dstsize, const char *src, ssize_t srclen, 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]
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)
size_t strupper_icu(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
bool check_locale_time(char **newval, void **extra, GucSource source)
size_t strlower_icu(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
size_t strtitle_libc(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
size_t strupper_libc(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
size_t strtitle_icu(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
pg_locale_t pg_newlocale_from_collation(Oid collid)
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)
size_t strupper_builtin(char *dst, size_t dstsize, const char *src, ssize_t srclen, 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)
size_t strlower_builtin(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
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)
bool check_locale_numeric(char **newval, void **extra, GucSource source)
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)
size_t strlower_libc(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
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_strxfrm_prefix_enabled(pg_locale_t locale)
char * icu_language_tag(const char *loc_str, int elevel)
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)
size_t strfold_builtin(char *dst, size_t dstsize, const char *src, ssize_t srclen, 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 strtitle_builtin(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
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)
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
size_t wchar2char(char *to, const wchar_t *from, size_t tolen, pg_locale_t locale)
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)
int pg_get_encoding_from_locale(const char *ctype, bool write_message)
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 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)