56#define PGLOCALE_SUPPORT_ERROR(provider) \
57 elog(ERROR, "unsupported collprovider for %s: %c", __func__, provider)
63#define TEXTBUFLEN 1024
65#define MAX_L10N_DATA 80
73extern UCollator *pg_ucol_open(
const char *loc_str);
74extern char *get_collation_actual_version_icu(
const char *collcollate);
88extern size_t strfold_builtin(
char *dst,
size_t dstsize,
const char *src,
91extern size_t strlower_icu(
char *dst,
size_t dstsize,
const char *src,
93extern size_t strtitle_icu(
char *dst,
size_t dstsize,
const char *src,
95extern size_t strupper_icu(
char *dst,
size_t dstsize,
const char *src,
97extern size_t strfold_icu(
char *dst,
size_t dstsize,
const char *src,
100extern size_t strlower_libc(
char *dst,
size_t dstsize,
const char *src,
102extern size_t strtitle_libc(
char *dst,
size_t dstsize,
const char *src,
104extern size_t strupper_libc(
char *dst,
size_t dstsize,
const char *src,
148#define SH_PREFIX collation_cache
149#define SH_ELEMENT_TYPE collation_cache_entry
150#define SH_KEY_TYPE Oid
152#define SH_HASH_KEY(tb, key) murmurhash32((uint32) key)
153#define SH_EQUAL(tb, a, b) (a == b)
154#define SH_GET_HASH(tb, a) a->hash
155#define SH_SCOPE static inline
171#if defined(WIN32) && defined(LC_MESSAGES)
172static char *IsoLocaleName(
const char *);
206 if (category == LC_MESSAGES)
227 if (category == LC_CTYPE)
232 strlcpy(save_lc_ctype, result,
sizeof(save_lc_ctype));
233 result = save_lc_ctype;
245 envvar =
"LC_COLLATE";
252 envvar =
"LC_MESSAGES";
254 result = IsoLocaleName(
locale);
257 elog(
DEBUG3,
"IsoLocaleName() executed; locale: \"%s\"", result);
262 envvar =
"LC_MONETARY";
265 envvar =
"LC_NUMERIC";
271 elog(
FATAL,
"unrecognized LC category: %d", category);
275 if (
setenv(envvar, result, 1) != 0)
302 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
303 errmsg(
"locale name \"%s\" contains non-ASCII characters",
322 if (res && canonname)
327 elog(
WARNING,
"failed to restore old locale \"%s\"", save);
331 if (canonname && *canonname && !
pg_is_ascii(*canonname))
334 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
335 errmsg(
"locale name \"%s\" contains non-ASCII characters",
342 return (res != NULL);
419#if defined(LC_MESSAGES) && !defined(WIN32)
446 free(s->decimal_point);
447 free(s->thousands_sep);
449 free(s->int_curr_symbol);
450 free(s->currency_symbol);
451 free(s->mon_decimal_point);
452 free(s->mon_thousands_sep);
453 free(s->mon_grouping);
454 free(s->positive_sign);
455 free(s->negative_sign);
465 if (s->decimal_point == NULL)
467 if (s->thousands_sep == NULL)
469 if (s->grouping == NULL)
471 if (s->int_curr_symbol == NULL)
473 if (s->currency_symbol == NULL)
475 if (s->mon_decimal_point == NULL)
477 if (s->mon_thousands_sep == NULL)
479 if (s->mon_grouping == NULL)
481 if (s->positive_sign == NULL)
483 if (s->negative_sign == NULL)
508 (
errcode(ERRCODE_OUT_OF_MEMORY),
509 errmsg(
"out of memory")));
526 static struct lconv CurrentLocaleConv;
527 static bool CurrentLocaleConvAllocated =
false;
528 struct lconv *extlconv;
530 struct lconv worklconv = {0};
534 return &CurrentLocaleConv;
537 if (CurrentLocaleConvAllocated)
540 CurrentLocaleConvAllocated =
false;
551 "could not get lconv for LC_MONETARY = \"%s\", LC_NUMERIC = \"%s\": %m",
556 worklconv.decimal_point = strdup(extlconv->decimal_point);
557 worklconv.thousands_sep = strdup(extlconv->thousands_sep);
558 worklconv.grouping = strdup(extlconv->grouping);
559 worklconv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
560 worklconv.currency_symbol = strdup(extlconv->currency_symbol);
561 worklconv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
562 worklconv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
563 worklconv.mon_grouping = strdup(extlconv->mon_grouping);
564 worklconv.positive_sign = strdup(extlconv->positive_sign);
565 worklconv.negative_sign = strdup(extlconv->negative_sign);
567 worklconv.int_frac_digits = extlconv->int_frac_digits;
568 worklconv.frac_digits = extlconv->frac_digits;
569 worklconv.p_cs_precedes = extlconv->p_cs_precedes;
570 worklconv.p_sep_by_space = extlconv->p_sep_by_space;
571 worklconv.n_cs_precedes = extlconv->n_cs_precedes;
572 worklconv.n_sep_by_space = extlconv->n_sep_by_space;
573 worklconv.p_sign_posn = extlconv->p_sign_posn;
574 worklconv.n_sign_posn = extlconv->n_sign_posn;
582 (
errcode(ERRCODE_OUT_OF_MEMORY),
583 errmsg(
"out of memory")));
626 CurrentLocaleConv = worklconv;
627 CurrentLocaleConvAllocated =
true;
629 return &CurrentLocaleConv;
649strftime_l_win32(
char *dst,
size_t dstlen,
660 len = MultiByteToWideChar(CP_UTF8, 0,
format, -1,
663 elog(
ERROR,
"could not convert format string from UTF-8: error code %lu",
676 len = WideCharToMultiByte(CP_UTF8, 0, wbuf,
len, dst, dstlen - 1,
679 elog(
ERROR,
"could not convert string to UTF-8: error code %lu",
688#define strftime_l(a,b,c,d,e) strftime_l_win32(a,b,c,d,e)
726 struct tm timeinfobuf;
727 bool strftimefail =
false;
750 timenow = time(NULL);
751 timeinfo = gmtime_r(&timenow, &timeinfobuf);
767 for (
i = 0;
i < 7;
i++)
769 timeinfo->tm_wday =
i;
779 for (
i = 0;
i < 12;
i++)
781 timeinfo->tm_mon =
i;
782 timeinfo->tm_mday = 1;
828 for (
i = 0;
i < 7;
i++)
839 for (
i = 0;
i < 12;
i++)
853#if defined(WIN32) && defined(LC_MESSAGES)
906search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
908 wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
913 argv = (
wchar_t **) lparam;
914 *argv[2] = (wchar_t) 0;
916 memset(test_locale, 0,
sizeof(test_locale));
919 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
920 test_locale, LOCALE_NAME_MAX_LENGTH))
927 if (wcsrchr(pStr,
'-') == NULL || wcsrchr(argv[0],
'_') == NULL)
929 if (_wcsicmp(argv[0], test_locale) == 0)
931 wcscpy(argv[1], pStr);
932 *argv[2] = (wchar_t) 1;
946 wcscat(test_locale, L
"_");
947 len = wcslen(test_locale);
948 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
950 LOCALE_NAME_MAX_LENGTH -
len))
952 if (_wcsicmp(argv[0], test_locale) == 0)
954 wcscpy(argv[1], pStr);
955 *argv[2] = (wchar_t) 1;
972get_iso_localename(
const char *winlocname)
974 wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
975 wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
976 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
988 period = strchr(winlocname,
'.');
994 memset(wc_locale_name, 0,
sizeof(wc_locale_name));
995 memset(buffer, 0,
sizeof(buffer));
996 MultiByteToWideChar(CP_ACP, 0, winlocname,
len, wc_locale_name,
997 LOCALE_NAME_MAX_LENGTH);
1003 ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME, (LPWSTR) &buffer,
1004 LOCALE_NAME_MAX_LENGTH);
1013 argv[0] = wc_locale_name;
1015 argv[2] = (
wchar_t *) &ret_val;
1016 EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS, (LPARAM) argv,
1026 rc =
wchar2char(iso_lc_messages, buffer,
sizeof(iso_lc_messages), NULL);
1027 if (rc == -1 || rc ==
sizeof(iso_lc_messages))
1037 hyphen = strchr(iso_lc_messages,
'-');
1040 return iso_lc_messages;
1047IsoLocaleName(
const char *winlocname)
1049 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
1054 strcpy(iso_lc_messages,
"C");
1055 return iso_lc_messages;
1058 return get_iso_localename(winlocname);
1080 if (collform->collprovider == COLLPROVIDER_BUILTIN)
1082 else if (collform->collprovider == COLLPROVIDER_ICU)
1084 else if (collform->collprovider == COLLPROVIDER_LIBC)
1099 char *actual_versionstr;
1100 char *collversionstr;
1104 if (collform->collprovider == COLLPROVIDER_LIBC)
1111 if (!actual_versionstr)
1119 (
errmsg(
"collation \"%s\" has no actual version, but a version was recorded",
1120 NameStr(collform->collname))));
1123 if (strcmp(actual_versionstr, collversionstr) != 0)
1125 (
errmsg(
"collation \"%s\" has version mismatch",
1127 errdetail(
"The collation in the database was created using version %s, "
1128 "but the operating system provides version %s.",
1129 collversionstr, actual_versionstr),
1130 errhint(
"Rebuild all objects affected by this collation and run "
1131 "ALTER COLLATION %s REFRESH VERSION, "
1132 "or build PostgreSQL with the right library version.",
1134 NameStr(collform->collname)))));
1160 if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
1163 else if (dbform->datlocprovider == COLLPROVIDER_ICU)
1166 else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
1193 if (
collid == DEFAULT_COLLATION_OID)
1221 if (cache_entry->
locale == 0)
1229 return cache_entry->
locale;
1239 char *collversion = NULL;
1241 if (collprovider == COLLPROVIDER_BUILTIN)
1244 else if (collprovider == COLLPROVIDER_ICU)
1245 collversion = get_collation_actual_version_icu(collcollate);
1247 else if (collprovider == COLLPROVIDER_LIBC)
1254pg_strlower(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1257 if (
locale->provider == COLLPROVIDER_BUILTIN)
1260 else if (
locale->provider == COLLPROVIDER_ICU)
1263 else if (
locale->provider == COLLPROVIDER_LIBC)
1273pg_strtitle(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1276 if (
locale->provider == COLLPROVIDER_BUILTIN)
1279 else if (
locale->provider == COLLPROVIDER_ICU)
1282 else if (
locale->provider == COLLPROVIDER_LIBC)
1292pg_strupper(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1295 if (
locale->provider == COLLPROVIDER_BUILTIN)
1298 else if (
locale->provider == COLLPROVIDER_ICU)
1301 else if (
locale->provider == COLLPROVIDER_LIBC)
1311pg_strfold(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1314 if (
locale->provider == COLLPROVIDER_BUILTIN)
1317 else if (
locale->provider == COLLPROVIDER_ICU)
1321 else if (
locale->provider == COLLPROVIDER_LIBC)
1338 return locale->collate->strncoll(arg1, -1, arg2, -1,
locale);
1356pg_strncoll(
const char *arg1, ssize_t len1,
const char *arg2, ssize_t len2,
1359 return locale->collate->strncoll(arg1, len1, arg2, len2,
locale);
1377 return locale->collate->strxfrm_is_safe;
1424 return (
locale->collate->strnxfrm_prefix != NULL);
1461 return locale->collate->strnxfrm_prefix(
dest, destsize, src, srclen,
locale);
1471 if (strcmp(
locale,
"C") == 0)
1473 else if (strcmp(
locale,
"C.UTF-8") == 0)
1475 else if (strcmp(
locale,
"PG_UNICODE_FAST") == 0)
1480 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1481 errmsg(
"invalid locale name \"%s\" for builtin provider",
1495 const char *canonical_name = NULL;
1496 int required_encoding;
1498 if (strcmp(
locale,
"C") == 0)
1499 canonical_name =
"C";
1500 else if (strcmp(
locale,
"C.UTF-8") == 0 || strcmp(
locale,
"C.UTF8") == 0)
1501 canonical_name =
"C.UTF-8";
1502 else if (strcmp(
locale,
"PG_UNICODE_FAST") == 0)
1503 canonical_name =
"PG_UNICODE_FAST";
1505 if (!canonical_name)
1507 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1508 errmsg(
"invalid locale name \"%s\" for builtin provider",
1512 if (required_encoding >= 0 &&
encoding != required_encoding)
1514 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1515 errmsg(
"encoding \"%s\" does not match locale \"%s\"",
1518 return canonical_name;
1539 const bool strict =
true;
1547 langtag =
palloc(buflen);
1550 status = U_ZERO_ERROR;
1551 uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
1554 if ((status == U_BUFFER_OVERFLOW_ERROR ||
1555 status == U_STRING_NOT_TERMINATED_WARNING) &&
1559 langtag =
repalloc(langtag, buflen);
1566 if (U_FAILURE(status))
1572 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1573 errmsg(
"could not convert locale name \"%s\" to language tag: %s",
1574 loc_str, u_errorName(status))));
1581 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1582 errmsg(
"ICU is not supported in this build")));
1594 UCollator *collator;
1596 char lang[ULOC_LANG_CAPACITY];
1609 status = U_ZERO_ERROR;
1610 uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
1611 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1614 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1615 errmsg(
"could not get language from ICU locale \"%s\": %s",
1616 loc_str, u_errorName(status)),
1617 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1618 "icu_validation_level",
"disabled")));
1623 if (strcmp(lang,
"") == 0 ||
1624 strcmp(lang,
"root") == 0 || strcmp(lang,
"und") == 0)
1628 for (int32_t
i = 0; !found &&
i < uloc_countAvailable();
i++)
1630 const char *otherloc = uloc_getAvailable(
i);
1631 char otherlang[ULOC_LANG_CAPACITY];
1633 status = U_ZERO_ERROR;
1634 uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
1635 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1638 if (strcmp(lang, otherlang) == 0)
1644 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1645 errmsg(
"ICU locale \"%s\" has unknown language \"%s\"",
1647 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1648 "icu_validation_level",
"disabled")));
1651 collator = pg_ucol_open(loc_str);
1652 ucol_close(collator);
1656 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1657 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)
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)