aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2018-11-03 13:56:10 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2018-11-03 13:56:10 -0400
commitbf4a9562e8b93ebb69715c7dbdfc90dd6945e369 (patch)
tree8603d594f64932e7838359c44f7064dd62849a78
parent33e6c34c32677a168bee4bc6c335aa8d73211a56 (diff)
downloadpostgresql-bf4a9562e8b93ebb69715c7dbdfc90dd6945e369.tar.gz
postgresql-bf4a9562e8b93ebb69715c7dbdfc90dd6945e369.zip
Make ts_locale.c's character-type functions cope with UTF-16.
On Windows, in UTF8 database encoding, what char2wchar() produces is UTF16 not UTF32, ie, characters above U+FFFF will be represented by surrogate pairs. t_isdigit() and siblings did not account for this and failed to provide a large enough result buffer. That in turn led to bogus "invalid multibyte character for locale" errors, because contrary to what you might think from char2wchar()'s documentation, its Windows code path doesn't cope sanely with buffer overflow. The solution for t_isdigit() and siblings is pretty clear: provide a 3-wchar_t result buffer not 2. char2wchar() also needs some work to provide more consistent, and more accurately documented, buffer overrun behavior. But that's a bigger job and it doesn't actually have any immediate payoff, so leave it for later. Per bug #15476 from Kenji Uno, who deserves credit for identifying the cause of the problem. Back-patch to all active branches. Discussion: https://postgr.es/m/15476-4314f480acf0f114@postgresql.org
-rw-r--r--src/backend/tsearch/ts_locale.c27
1 files changed, 19 insertions, 8 deletions
diff --git a/src/backend/tsearch/ts_locale.c b/src/backend/tsearch/ts_locale.c
index a114d6635be..62deece7a93 100644
--- a/src/backend/tsearch/ts_locale.c
+++ b/src/backend/tsearch/ts_locale.c
@@ -21,18 +21,29 @@
static void tsearch_readline_callback(void *arg);
+/*
+ * The reason these functions use a 3-wchar_t output buffer, not 2 as you
+ * might expect, is that on Windows "wchar_t" is 16 bits and what we'll be
+ * getting from char2wchar() is UTF16 not UTF32. A single input character
+ * may therefore produce a surrogate pair rather than just one wchar_t;
+ * we also need room for a trailing null. When we do get a surrogate pair,
+ * we pass just the first code to iswdigit() etc, so that these functions will
+ * always return false for characters outside the Basic Multilingual Plane.
+ */
+#define WC_BUF_LEN 3
+
int
t_isdigit(const char *ptr)
{
int clen = pg_mblen(ptr);
- wchar_t character[2];
+ wchar_t character[WC_BUF_LEN];
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
pg_locale_t mylocale = 0; /* TODO */
if (clen == 1 || lc_ctype_is_c(collation))
return isdigit(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen, mylocale);
+ char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale);
return iswdigit((wint_t) character[0]);
}
@@ -41,14 +52,14 @@ int
t_isspace(const char *ptr)
{
int clen = pg_mblen(ptr);
- wchar_t character[2];
+ wchar_t character[WC_BUF_LEN];
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
pg_locale_t mylocale = 0; /* TODO */
if (clen == 1 || lc_ctype_is_c(collation))
return isspace(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen, mylocale);
+ char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale);
return iswspace((wint_t) character[0]);
}
@@ -57,14 +68,14 @@ int
t_isalpha(const char *ptr)
{
int clen = pg_mblen(ptr);
- wchar_t character[2];
+ wchar_t character[WC_BUF_LEN];
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
pg_locale_t mylocale = 0; /* TODO */
if (clen == 1 || lc_ctype_is_c(collation))
return isalpha(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen, mylocale);
+ char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale);
return iswalpha((wint_t) character[0]);
}
@@ -73,14 +84,14 @@ int
t_isprint(const char *ptr)
{
int clen = pg_mblen(ptr);
- wchar_t character[2];
+ wchar_t character[WC_BUF_LEN];
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
pg_locale_t mylocale = 0; /* TODO */
if (clen == 1 || lc_ctype_is_c(collation))
return isprint(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen, mylocale);
+ char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale);
return iswprint((wint_t) character[0]);
}