diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c index cd9594226d6..8e7078f42c8 100644 --- a/src/backend/parser/scansup.c +++ b/src/backend/parser/scansup.c @@ -18,7 +18,6 @@ #include "mb/pg_wchar.h" #include "parser/scansup.h" -#include "utils/pg_locale.h" /* @@ -47,22 +46,35 @@ char * downcase_identifier(const char *ident, int len, bool warn, bool truncate) { char *result; - size_t needed pg_attribute_unused(); + int i; + bool enc_is_single_byte; + + result = palloc(len + 1); + enc_is_single_byte = pg_database_encoding_max_length() == 1; /* - * Preserves string length. - * - * NB: if we decide to support Unicode-aware identifier case folding, then - * we need to account for a change in string length. + * SQL99 specifies Unicode-aware case normalization, which we don't yet + * have the infrastructure for. Instead we use tolower() to provide a + * locale-aware translation. However, there are some locales where this + * is not right either (eg, Turkish may do strange things with 'i' and + * 'I'). Our current compromise is to use tolower() for characters with + * the high bit set, as long as they aren't part of a multi-byte + * character, and use an ASCII-only downcasing for 7-bit characters. */ - result = palloc(len + 1); + for (i = 0; i < len; i++) + { + unsigned char ch = (unsigned char) ident[i]; - needed = pg_downcase_ident(result, len + 1, ident, len); - Assert(needed == len); - Assert(result[len] == '\0'); + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + else if (enc_is_single_byte && IS_HIGHBIT_SET(ch) && isupper(ch)) + ch = tolower(ch); + result[i] = (char) ch; + } + result[i] = '\0'; - if (len >= NAMEDATALEN && truncate) - truncate_identifier(result, len, warn); + if (i >= NAMEDATALEN && truncate) + truncate_identifier(result, i, warn); return result; } diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index cca08ceac0b..88d2d54d388 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -1434,30 +1434,6 @@ pg_strfold(char *dst, size_t dstsize, const char *src, size_t srclen, return locale->ctype->strfold(dst, dstsize, src, srclen, locale); } -/* - * pg_downcase_ident() - * - * Lowercase an identifier using historical identifier-folding semantics, and - * return the result length (not including terminating NUL). If the result - * length is less than dstsize, the NUL-terminated result is stored in dst; - * otherwise the contents of dst are undefined. - * - * XXX: callers currently depend on the result length being equal to srclen, - * but that may change in the future if we change to proper case folding. - */ -size_t -pg_downcase_ident(char *dst, size_t dstsize, const char *src, size_t srclen) -{ - pg_locale_t locale = default_locale; - - if (locale == NULL || locale->ctype == NULL || - locale->ctype->downcase_ident == NULL) - return strlower_c(dst, dstsize, src, srclen); - else - return locale->ctype->downcase_ident(dst, dstsize, src, srclen, - locale); -} - /* * pg_strcoll * diff --git a/src/backend/utils/adt/pg_locale_builtin.c b/src/backend/utils/adt/pg_locale_builtin.c index 8fa0b637b06..184d35f9360 100644 --- a/src/backend/utils/adt/pg_locale_builtin.c +++ b/src/backend/utils/adt/pg_locale_builtin.c @@ -219,8 +219,6 @@ static const struct ctype_methods ctype_methods_builtin = { .strtitle = strtitle_builtin, .strupper = strupper_builtin, .strfold = strfold_builtin, - /* uses plain ASCII semantics for historical reasons */ - .downcase_ident = NULL, .wc_isdigit = wc_isdigit_builtin, .wc_isalpha = wc_isalpha_builtin, .wc_isalnum = wc_isalnum_builtin, diff --git a/src/backend/utils/adt/pg_locale_icu.c b/src/backend/utils/adt/pg_locale_icu.c index c35b9edc652..d0081264ea5 100644 --- a/src/backend/utils/adt/pg_locale_icu.c +++ b/src/backend/utils/adt/pg_locale_icu.c @@ -72,8 +72,6 @@ static size_t strupper_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale); static size_t strfold_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale); -static size_t downcase_ident_icu(char *dst, size_t dstsize, const char *src, - size_t srclen, pg_locale_t locale); static int strncoll_icu(const char *arg1, size_t len1, const char *arg2, size_t len2, pg_locale_t locale); @@ -145,7 +143,7 @@ static int32_t foldcase_options(const char *locale); /* * XXX: many of the functions below rely on casts directly from pg_wchar to - * UChar32, which is correct for UTF-8 and LATIN1, but not in general. + * UChar32, which is correct for the UTF-8 encoding, but not in general. */ static pg_wchar @@ -270,7 +268,6 @@ static const struct ctype_methods ctype_methods_icu = { .strtitle = strtitle_icu, .strupper = strupper_icu, .strfold = strfold_icu, - .downcase_ident = downcase_ident_icu, .wc_isdigit = wc_isdigit_icu, .wc_isalpha = wc_isalpha_icu, .wc_isalnum = wc_isalnum_icu, @@ -291,8 +288,6 @@ static const struct ctype_methods ctype_methods_icu_utf8 = { .strtitle = strtitle_icu_utf8, .strupper = strupper_icu_utf8, .strfold = strfold_icu_utf8, - /* uses plain ASCII semantics for historical reasons */ - .downcase_ident = NULL, .wc_isdigit = wc_isdigit_icu, .wc_isalpha = wc_isalpha_icu, .wc_isalnum = wc_isalnum_icu, @@ -308,28 +303,6 @@ static const struct ctype_methods ctype_methods_icu_utf8 = { .wc_tolower = tolower_icu, }; -/* - * ICU still depends on libc for compatibility with certain historical - * behavior for single-byte encodings. See downcase_ident_icu(). - * - * XXX: consider fixing by decoding the single byte into a code point, and - * using u_tolower(). - */ -static locale_t -make_libc_ctype_locale(const char *ctype) -{ - locale_t loc; - -#ifndef WIN32 - loc = newlocale(LC_CTYPE_MASK, ctype, NULL); -#else - loc = _create_locale(LC_ALL, ctype); -#endif - if (!loc) - report_newlocale_failure(ctype); - - return loc; -} #endif pg_locale_t @@ -340,7 +313,6 @@ create_pg_locale_icu(Oid collid, MemoryContext context) const char *iculocstr; const char *icurules = NULL; UCollator *collator; - locale_t loc = (locale_t) 0; pg_locale_t result; if (collid == DEFAULT_COLLATION_OID) @@ -363,18 +335,6 @@ create_pg_locale_icu(Oid collid, MemoryContext context) if (!isnull) icurules = TextDatumGetCString(datum); - /* libc only needed for default locale and single-byte encoding */ - if (pg_database_encoding_max_length() == 1) - { - const char *ctype; - - datum = SysCacheGetAttrNotNull(DATABASEOID, tp, - Anum_pg_database_datctype); - ctype = TextDatumGetCString(datum); - - loc = make_libc_ctype_locale(ctype); - } - ReleaseSysCache(tp); } else @@ -405,7 +365,6 @@ create_pg_locale_icu(Oid collid, MemoryContext context) result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct)); result->icu.locale = MemoryContextStrdup(context, iculocstr); result->icu.ucol = collator; - result->icu.lt = loc; result->deterministic = deterministic; result->collate_is_c = false; result->ctype_is_c = false; @@ -716,39 +675,6 @@ strfold_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen, return needed; } -/* - * For historical compatibility, behavior is not multibyte-aware. - * - * NB: uses libc tolower_l() for single-byte encodings (also for historical - * compatibility), and therefore relies on the LC_CTYPE setting. - */ -static size_t -downcase_ident_icu(char *dst, size_t dstsize, const char *src, - size_t srclen, pg_locale_t locale) -{ - size_t i; - bool libc_lower; - locale_t lt = locale->icu.lt; - - libc_lower = lt && (pg_database_encoding_max_length() == 1); - - for (i = 0; i < srclen && i < dstsize; i++) - { - unsigned char ch = (unsigned char) src[i]; - - if (ch >= 'A' && ch <= 'Z') - ch = pg_ascii_tolower(ch); - else if (libc_lower && IS_HIGHBIT_SET(ch) && isupper_l(ch, lt)) - ch = tolower_l(ch, lt); - dst[i] = (char) ch; - } - - if (i < dstsize) - dst[i] = '\0'; - - return srclen; -} - /* * strncoll_icu_utf8() * diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c index 7348ed90fc3..02c419aa62a 100644 --- a/src/backend/utils/adt/pg_locale_libc.c +++ b/src/backend/utils/adt/pg_locale_libc.c @@ -390,41 +390,12 @@ tolower_libc_mb(pg_wchar wc, pg_locale_t locale) return wc; } -/* - * Characters A..Z always downcase to a..z, even in the Turkish - * locale. Characters beyond 127 use tolower(). - */ -static size_t -downcase_ident_libc_sb(char *dst, size_t dstsize, const char *src, - size_t srclen, pg_locale_t locale) -{ - locale_t loc = locale->lt; - size_t i; - - for (i = 0; i < srclen && i < dstsize; i++) - { - unsigned char ch = (unsigned char) src[i]; - - if (ch >= 'A' && ch <= 'Z') - ch = pg_ascii_tolower(ch); - else if (IS_HIGHBIT_SET(ch) && isupper_l(ch, loc)) - ch = tolower_l(ch, loc); - dst[i] = (char) ch; - } - - if (i < dstsize) - dst[i] = '\0'; - - return srclen; -} - static const struct ctype_methods ctype_methods_libc_sb = { .strlower = strlower_libc_sb, .strtitle = strtitle_libc_sb, .strupper = strupper_libc_sb, /* in libc, casefolding is the same as lowercasing */ .strfold = strlower_libc_sb, - .downcase_ident = downcase_ident_libc_sb, .wc_isdigit = wc_isdigit_libc_sb, .wc_isalpha = wc_isalpha_libc_sb, .wc_isalnum = wc_isalnum_libc_sb, @@ -451,8 +422,6 @@ static const struct ctype_methods ctype_methods_libc_other_mb = { .strupper = strupper_libc_mb, /* in libc, casefolding is the same as lowercasing */ .strfold = strlower_libc_mb, - /* uses plain ASCII semantics for historical reasons */ - .downcase_ident = NULL, .wc_isdigit = wc_isdigit_libc_sb, .wc_isalpha = wc_isalpha_libc_sb, .wc_isalnum = wc_isalnum_libc_sb, @@ -474,8 +443,6 @@ static const struct ctype_methods ctype_methods_libc_utf8 = { .strupper = strupper_libc_mb, /* in libc, casefolding is the same as lowercasing */ .strfold = strlower_libc_mb, - /* uses plain ASCII semantics for historical reasons */ - .downcase_ident = NULL, .wc_isdigit = wc_isdigit_libc_mb, .wc_isalpha = wc_isalpha_libc_mb, .wc_isalnum = wc_isalnum_libc_mb, diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index fcd508f5dd6..36cd22dd255 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -116,11 +116,6 @@ struct ctype_methods const char *src, size_t srclen, pg_locale_t locale); - /* optional */ - size_t (*downcase_ident) (char *dest, size_t destsize, - const char *src, size_t srclen, - pg_locale_t locale); - /* required */ bool (*wc_isdigit) (pg_wchar wc, pg_locale_t locale); bool (*wc_isalpha) (pg_wchar wc, pg_locale_t locale); @@ -175,7 +170,6 @@ struct pg_locale_struct const char *locale; struct UCollator *ucol; struct UCaseMap *ucasemap; - locale_t lt; } icu; #endif }; @@ -199,8 +193,6 @@ extern size_t pg_strupper(char *dst, size_t dstsize, extern size_t pg_strfold(char *dst, size_t dstsize, const char *src, size_t srclen, pg_locale_t locale); -extern size_t pg_downcase_ident(char *dst, size_t dstsize, - const char *src, size_t srclen); extern int pg_strcoll(const char *arg1, const char *arg2, pg_locale_t locale); extern int pg_strncoll(const char *arg1, size_t len1, const char *arg2, size_t len2, pg_locale_t locale);