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);
123#define SH_PREFIX collation_cache
124#define SH_ELEMENT_TYPE collation_cache_entry
125#define SH_KEY_TYPE Oid
127#define SH_HASH_KEY(tb, key) murmurhash32((uint32) key)
128#define SH_EQUAL(tb, a, b) (a == b)
129#define SH_GET_HASH(tb, a) a->hash
130#define SH_SCOPE static inline
146#if defined(WIN32) && defined(LC_MESSAGES)
147static char *IsoLocaleName(
const char *);
181 if (category == LC_MESSAGES)
202 if (category == LC_CTYPE)
207 strlcpy(save_lc_ctype, result,
sizeof(save_lc_ctype));
208 result = save_lc_ctype;
220 envvar =
"LC_COLLATE";
227 envvar =
"LC_MESSAGES";
229 result = IsoLocaleName(
locale);
232 elog(
DEBUG3,
"IsoLocaleName() executed; locale: \"%s\"", result);
237 envvar =
"LC_MONETARY";
240 envvar =
"LC_NUMERIC";
246 elog(
FATAL,
"unrecognized LC category: %d", category);
250 if (
setenv(envvar, result, 1) != 0)
277 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
278 errmsg(
"locale name \"%s\" contains non-ASCII characters",
297 if (res && canonname)
302 elog(
WARNING,
"failed to restore old locale \"%s\"", save);
306 if (canonname && *canonname && !
pg_is_ascii(*canonname))
309 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
310 errmsg(
"locale name \"%s\" contains non-ASCII characters",
317 return (res != NULL);
394#if defined(LC_MESSAGES) && !defined(WIN32)
421 free(s->decimal_point);
422 free(s->thousands_sep);
424 free(s->int_curr_symbol);
425 free(s->currency_symbol);
426 free(s->mon_decimal_point);
427 free(s->mon_thousands_sep);
428 free(s->mon_grouping);
429 free(s->positive_sign);
430 free(s->negative_sign);
440 if (s->decimal_point == NULL)
442 if (s->thousands_sep == NULL)
444 if (s->grouping == NULL)
446 if (s->int_curr_symbol == NULL)
448 if (s->currency_symbol == NULL)
450 if (s->mon_decimal_point == NULL)
452 if (s->mon_thousands_sep == NULL)
454 if (s->mon_grouping == NULL)
456 if (s->positive_sign == NULL)
458 if (s->negative_sign == NULL)
483 (
errcode(ERRCODE_OUT_OF_MEMORY),
484 errmsg(
"out of memory")));
501 static struct lconv CurrentLocaleConv;
502 static bool CurrentLocaleConvAllocated =
false;
503 struct lconv *extlconv;
505 struct lconv worklconv = {0};
509 return &CurrentLocaleConv;
512 if (CurrentLocaleConvAllocated)
515 CurrentLocaleConvAllocated =
false;
526 "could not get lconv for LC_MONETARY = \"%s\", LC_NUMERIC = \"%s\": %m",
531 worklconv.decimal_point = strdup(extlconv->decimal_point);
532 worklconv.thousands_sep = strdup(extlconv->thousands_sep);
533 worklconv.grouping = strdup(extlconv->grouping);
534 worklconv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
535 worklconv.currency_symbol = strdup(extlconv->currency_symbol);
536 worklconv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
537 worklconv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
538 worklconv.mon_grouping = strdup(extlconv->mon_grouping);
539 worklconv.positive_sign = strdup(extlconv->positive_sign);
540 worklconv.negative_sign = strdup(extlconv->negative_sign);
542 worklconv.int_frac_digits = extlconv->int_frac_digits;
543 worklconv.frac_digits = extlconv->frac_digits;
544 worklconv.p_cs_precedes = extlconv->p_cs_precedes;
545 worklconv.p_sep_by_space = extlconv->p_sep_by_space;
546 worklconv.n_cs_precedes = extlconv->n_cs_precedes;
547 worklconv.n_sep_by_space = extlconv->n_sep_by_space;
548 worklconv.p_sign_posn = extlconv->p_sign_posn;
549 worklconv.n_sign_posn = extlconv->n_sign_posn;
557 (
errcode(ERRCODE_OUT_OF_MEMORY),
558 errmsg(
"out of memory")));
601 CurrentLocaleConv = worklconv;
602 CurrentLocaleConvAllocated =
true;
604 return &CurrentLocaleConv;
624strftime_l_win32(
char *dst,
size_t dstlen,
635 len = MultiByteToWideChar(CP_UTF8, 0,
format, -1,
638 elog(
ERROR,
"could not convert format string from UTF-8: error code %lu",
651 len = WideCharToMultiByte(CP_UTF8, 0, wbuf,
len, dst, dstlen - 1,
654 elog(
ERROR,
"could not convert string to UTF-8: error code %lu",
663#define strftime_l(a,b,c,d,e) strftime_l_win32(a,b,c,d,e)
701 struct tm timeinfobuf;
702 bool strftimefail =
false;
725 timenow = time(NULL);
726 timeinfo = gmtime_r(&timenow, &timeinfobuf);
742 for (
i = 0;
i < 7;
i++)
744 timeinfo->tm_wday =
i;
754 for (
i = 0;
i < 12;
i++)
756 timeinfo->tm_mon =
i;
757 timeinfo->tm_mday = 1;
803 for (
i = 0;
i < 7;
i++)
814 for (
i = 0;
i < 12;
i++)
828#if defined(WIN32) && defined(LC_MESSAGES)
881search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
883 wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
888 argv = (
wchar_t **) lparam;
889 *argv[2] = (wchar_t) 0;
891 memset(test_locale, 0,
sizeof(test_locale));
894 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
895 test_locale, LOCALE_NAME_MAX_LENGTH))
902 if (wcsrchr(pStr,
'-') == NULL || wcsrchr(argv[0],
'_') == NULL)
904 if (_wcsicmp(argv[0], test_locale) == 0)
906 wcscpy(argv[1], pStr);
907 *argv[2] = (wchar_t) 1;
921 wcscat(test_locale, L
"_");
922 len = wcslen(test_locale);
923 if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
925 LOCALE_NAME_MAX_LENGTH -
len))
927 if (_wcsicmp(argv[0], test_locale) == 0)
929 wcscpy(argv[1], pStr);
930 *argv[2] = (wchar_t) 1;
947get_iso_localename(
const char *winlocname)
949 wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
950 wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
951 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
963 period = strchr(winlocname,
'.');
969 memset(wc_locale_name, 0,
sizeof(wc_locale_name));
970 memset(buffer, 0,
sizeof(buffer));
971 MultiByteToWideChar(CP_ACP, 0, winlocname,
len, wc_locale_name,
972 LOCALE_NAME_MAX_LENGTH);
978 ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME, (LPWSTR) &buffer,
979 LOCALE_NAME_MAX_LENGTH);
988 argv[0] = wc_locale_name;
990 argv[2] = (
wchar_t *) &ret_val;
991 EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS, (LPARAM) argv,
1001 rc =
wchar2char(iso_lc_messages, buffer,
sizeof(iso_lc_messages), NULL);
1002 if (rc == -1 || rc ==
sizeof(iso_lc_messages))
1012 hyphen = strchr(iso_lc_messages,
'-');
1015 return iso_lc_messages;
1022IsoLocaleName(
const char *winlocname)
1024 static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
1029 strcpy(iso_lc_messages,
"C");
1030 return iso_lc_messages;
1033 return get_iso_localename(winlocname);
1055 if (collform->collprovider == COLLPROVIDER_BUILTIN)
1057 else if (collform->collprovider == COLLPROVIDER_ICU)
1059 else if (collform->collprovider == COLLPROVIDER_LIBC)
1077 char *actual_versionstr;
1078 char *collversionstr;
1082 if (collform->collprovider == COLLPROVIDER_LIBC)
1089 if (!actual_versionstr)
1097 (
errmsg(
"collation \"%s\" has no actual version, but a version was recorded",
1098 NameStr(collform->collname))));
1101 if (strcmp(actual_versionstr, collversionstr) != 0)
1103 (
errmsg(
"collation \"%s\" has version mismatch",
1105 errdetail(
"The collation in the database was created using version %s, "
1106 "but the operating system provides version %s.",
1107 collversionstr, actual_versionstr),
1108 errhint(
"Rebuild all objects affected by this collation and run "
1109 "ALTER COLLATION %s REFRESH VERSION, "
1110 "or build PostgreSQL with the right library version.",
1112 NameStr(collform->collname)))));
1138 if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
1141 else if (dbform->datlocprovider == COLLPROVIDER_ICU)
1144 else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
1171 if (
collid == DEFAULT_COLLATION_OID)
1201 if (cache_entry->
locale == 0)
1209 return cache_entry->
locale;
1219 char *collversion = NULL;
1221 if (collprovider == COLLPROVIDER_BUILTIN)
1224 else if (collprovider == COLLPROVIDER_ICU)
1225 collversion = get_collation_actual_version_icu(collcollate);
1227 else if (collprovider == COLLPROVIDER_LIBC)
1234pg_strlower(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1237 return locale->ctype->strlower(dst, dstsize, src, srclen,
locale);
1241pg_strtitle(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1244 return locale->ctype->strtitle(dst, dstsize, src, srclen,
locale);
1248pg_strupper(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1251 return locale->ctype->strupper(dst, dstsize, src, srclen,
locale);
1255pg_strfold(
char *dst,
size_t dstsize,
const char *src, ssize_t srclen,
1258 if (
locale->ctype->strfold)
1259 return locale->ctype->strfold(dst, dstsize, src, srclen,
locale);
1261 return locale->ctype->strlower(dst, dstsize, src, srclen,
locale);
1272 return locale->collate->strncoll(arg1, -1, arg2, -1,
locale);
1290pg_strncoll(
const char *arg1, ssize_t len1,
const char *arg2, ssize_t len2,
1293 return locale->collate->strncoll(arg1, len1, arg2, len2,
locale);
1311 return locale->collate->strxfrm_is_safe;
1358 return (
locale->collate->strnxfrm_prefix != NULL);
1395 return locale->collate->strnxfrm_prefix(
dest, destsize, src, srclen,
locale);
1419 return (
locale->ctype->char_tolower != NULL);
1440 if (strcmp(
locale,
"C") == 0)
1442 else if (strcmp(
locale,
"C.UTF-8") == 0)
1444 else if (strcmp(
locale,
"PG_UNICODE_FAST") == 0)
1449 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1450 errmsg(
"invalid locale name \"%s\" for builtin provider",
1464 const char *canonical_name = NULL;
1465 int required_encoding;
1467 if (strcmp(
locale,
"C") == 0)
1468 canonical_name =
"C";
1469 else if (strcmp(
locale,
"C.UTF-8") == 0 || strcmp(
locale,
"C.UTF8") == 0)
1470 canonical_name =
"C.UTF-8";
1471 else if (strcmp(
locale,
"PG_UNICODE_FAST") == 0)
1472 canonical_name =
"PG_UNICODE_FAST";
1474 if (!canonical_name)
1476 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1477 errmsg(
"invalid locale name \"%s\" for builtin provider",
1481 if (required_encoding >= 0 &&
encoding != required_encoding)
1483 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
1484 errmsg(
"encoding \"%s\" does not match locale \"%s\"",
1487 return canonical_name;
1508 const bool strict =
true;
1516 langtag =
palloc(buflen);
1519 status = U_ZERO_ERROR;
1520 uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
1523 if ((status == U_BUFFER_OVERFLOW_ERROR ||
1524 status == U_STRING_NOT_TERMINATED_WARNING) &&
1528 langtag =
repalloc(langtag, buflen);
1535 if (U_FAILURE(status))
1541 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1542 errmsg(
"could not convert locale name \"%s\" to language tag: %s",
1543 loc_str, u_errorName(status))));
1550 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1551 errmsg(
"ICU is not supported in this build")));
1563 UCollator *collator;
1565 char lang[ULOC_LANG_CAPACITY];
1578 status = U_ZERO_ERROR;
1579 uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
1580 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1583 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1584 errmsg(
"could not get language from ICU locale \"%s\": %s",
1585 loc_str, u_errorName(status)),
1586 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1587 "icu_validation_level",
"disabled")));
1592 if (strcmp(lang,
"") == 0 ||
1593 strcmp(lang,
"root") == 0 || strcmp(lang,
"und") == 0)
1597 for (int32_t
i = 0; !found &&
i < uloc_countAvailable();
i++)
1599 const char *otherloc = uloc_getAvailable(
i);
1600 char otherlang[ULOC_LANG_CAPACITY];
1602 status = U_ZERO_ERROR;
1603 uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
1604 if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1607 if (strcmp(lang, otherlang) == 0)
1613 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1614 errmsg(
"ICU locale \"%s\" has unknown language \"%s\"",
1616 errhint(
"To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1617 "icu_validation_level",
"disabled")));
1620 collator = pg_ucol_open(loc_str);
1621 ucol_close(collator);
1625 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1626 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]
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)
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)
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)
bool check_locale_numeric(char **newval, void **extra, GucSource source)
bool char_tolower_enabled(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_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)
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)
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, 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)
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 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)