pg.ddx.io  pgsql-bugs@postgresql.org mailing list archive  
help / color / mirror / Atom feed
unaccent fails when datlocprovider=i and datctype=C
2+ messages / 2 participants
[nested] [flat]

Commits from this thread:
  2023-03-17 8b87e92919 f413941f41  Fix t_isspace(), etc., when datlocprovider=i and datctype=C.
* unaccent fails when datlocprovider=i and datctype=C
@ 2023-03-08 05:49  Jeff Davis <pgsql@j-davis.com>
  0 siblings, 1 reply; 2+ messages in thread

From: Jeff Davis @ 2023-03-08 05:49 UTC (permalink / raw)
  To: pgsql-bugs

Repro:

$ initdb -D data -N --locale-provider=icu --icu-locale=en --locale=C

=# create extension unaccent;
ERROR:  invalid multibyte character for locale
HINT:  The server's LC_CTYPE locale is probably incompatible with the
database encoding.
CONTEXT:  line 1 of configuration file
".../share/tsearch_data/unaccent.rules": "¡  !
"

Cause: t_isspace() implementation is incomplete (notice "TODO"
comments):

    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, WC_BUF_LEN, ptr, clen, mylocale);

    return iswspace((wint_t) character[0]);

If using datlocprovider=c, then the earlier branch goes straight to
isspace(). But if datlocprovider=i, then
lc_ctype_is_c(DEFAULT_COLLATION_OID) returns false, and it goes into
char2wchar(). char2wchar() is essentially a wrapper around mbstowcs(),
which does not work on multibyte input when LC_CTYPE=C. 

Quick fix (attached): check whether datctype is C rather than the
default collation.

Eventually this should be fixed by doing character classification in
ICU when the provider is ICU.

-- 
Jeff Davis
PostgreSQL Contributor Team - AWS

Attachments:

v1-0001-Fix-t_isspace-etc.-when-datlocprovider-i-and-datc.patch (text/x-patch, 5.8K) download From d71a6a017e4eb794438d52a7ff30ee1e24af257d Mon Sep 17 00:00:00 2001 From: Jeff Davis <jeff@j-davis.com> Date: Tue, 7 Mar 2023 21:25:54 -0800 Subject: [PATCH v1] Fix t_isspace(), etc., when datlocprovider=i and datctype=C. Check whether the datctype is C to determine whether t_isspace() and related functions use isspace() or iswspace(). Previously, t_isspace() checked whether the database default collation was C; which is incorrect when the default collation uses the ICU provider. Backpatch-through: 15 --- src/backend/tsearch/ts_locale.c | 18 ++++++------------ src/backend/tsearch/wparser_def.c | 3 +-- src/backend/utils/adt/pg_locale.c | 3 +++ src/backend/utils/init/postinit.c | 4 ++++ src/include/utils/pg_locale.h | 2 ++ 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/backend/tsearch/ts_locale.c b/src/backend/tsearch/ts_locale.c index 0c03170990..f1150d30b7 100644 --- a/src/backend/tsearch/ts_locale.c +++ b/src/backend/tsearch/ts_locale.c @@ -38,10 +38,9 @@ t_isdigit(const char *ptr) { int clen = pg_mblen(ptr); 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)) + if (clen == 1 || database_ctype_is_c) return isdigit(TOUCHAR(ptr)); char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale); @@ -54,10 +53,9 @@ t_isspace(const char *ptr) { int clen = pg_mblen(ptr); 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)) + if (clen == 1 || database_ctype_is_c) return isspace(TOUCHAR(ptr)); char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale); @@ -70,10 +68,9 @@ t_isalpha(const char *ptr) { int clen = pg_mblen(ptr); 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)) + if (clen == 1 || database_ctype_is_c) return isalpha(TOUCHAR(ptr)); char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale); @@ -86,10 +83,9 @@ t_isalnum(const char *ptr) { int clen = pg_mblen(ptr); 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)) + if (clen == 1 || database_ctype_is_c) return isalnum(TOUCHAR(ptr)); char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale); @@ -102,10 +98,9 @@ t_isprint(const char *ptr) { int clen = pg_mblen(ptr); 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)) + if (clen == 1 || database_ctype_is_c) return isprint(TOUCHAR(ptr)); char2wchar(character, WC_BUF_LEN, ptr, clen, mylocale); @@ -273,7 +268,6 @@ char * lowerstr_with_len(const char *str, int len) { char *out; - Oid collation = DEFAULT_COLLATION_OID; /* TODO */ pg_locale_t mylocale = 0; /* TODO */ if (len == 0) @@ -285,7 +279,7 @@ lowerstr_with_len(const char *str, int len) * Also, for a C locale there is no need to process as multibyte. From * backend/utils/adt/oracle_compat.c Teodor */ - if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collation)) + if (pg_database_encoding_max_length() > 1 && !database_ctype_is_c) { wchar_t *wstr, *wptr; diff --git a/src/backend/tsearch/wparser_def.c b/src/backend/tsearch/wparser_def.c index cc3736454e..840a44ec00 100644 --- a/src/backend/tsearch/wparser_def.c +++ b/src/backend/tsearch/wparser_def.c @@ -297,11 +297,10 @@ TParserInit(char *str, int len) */ if (prs->charmaxlen > 1) { - Oid collation = DEFAULT_COLLATION_OID; /* TODO */ pg_locale_t mylocale = 0; /* TODO */ prs->usewide = true; - if (lc_ctype_is_c(collation)) + if (database_ctype_is_c) { /* * char2wchar doesn't work for C-locale and sizeof(pg_wchar) could diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 4aa5eaa984..646c4b0de1 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -106,6 +106,9 @@ char *localized_full_days[7 + 1]; char *localized_abbrev_months[12 + 1]; char *localized_full_months[12 + 1]; +/* is the databases's LC_CTYPE the C locale? */ +bool database_ctype_is_c = false; + /* indicates whether locale information cache is valid */ static bool CurrentLocaleConvValid = false; static bool CurrentLCTimeValid = false; diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 2f07ca7a0e..563c254f42 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -419,6 +419,10 @@ CheckMyDatabase(const char *name, bool am_superuser, bool override_allow_connect " which is not recognized by setlocale().", ctype), errhint("Recreate the database with another locale or install the missing locale."))); + if (strcmp(ctype, "C") == 0 || + strcmp(ctype, "POSIX") == 0) + database_ctype_is_c = true; + if (dbform->datlocprovider == COLLPROVIDER_ICU) { datum = SysCacheGetAttr(DATABASEOID, tup, Anum_pg_database_daticulocale, &isnull); diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index b8f22875a8..d61622eb31 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -47,6 +47,8 @@ extern PGDLLIMPORT char *localized_full_days[]; extern PGDLLIMPORT char *localized_abbrev_months[]; extern PGDLLIMPORT char *localized_full_months[]; +/* is the databases's LC_CTYPE the C locale? */ +extern PGDLLIMPORT bool database_ctype_is_c; extern bool check_locale(int category, const char *locale, char **canonname); extern char *pg_perm_setlocale(int category, const char *locale); -- 2.34.1
^ permalink raw reply [nested|flat] 2+ messages in thread

* Re: unaccent fails when datlocprovider=i and datctype=C
@ 2023-03-10 08:40  Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  parent: Jeff Davis <pgsql@j-davis.com>
  0 siblings, 0 replies; 2+ messages in thread

From: Peter Eisentraut @ 2023-03-10 08:40 UTC (permalink / raw)
  To: Jeff Davis <pgsql@j-davis.com>; pgsql-bugs

On 08.03.23 06:49, Jeff Davis wrote:
> $ initdb -D data -N --locale-provider=icu --icu-locale=en --locale=C

Is it even worth supporting that?  What is the point of this kind of setup?

> =# create extension unaccent;
> ERROR:  invalid multibyte character for locale
> HINT:  The server's LC_CTYPE locale is probably incompatible with the
> database encoding.
> CONTEXT:  line 1 of configuration file
> ".../share/tsearch_data/unaccent.rules": "¡  !
> "
> 
> Cause: t_isspace() implementation is incomplete (notice "TODO"
> comments):
> 
>      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, WC_BUF_LEN, ptr, clen, mylocale);
> 
>      return iswspace((wint_t) character[0]);
> 
> If using datlocprovider=c, then the earlier branch goes straight to
> isspace(). But if datlocprovider=i, then
> lc_ctype_is_c(DEFAULT_COLLATION_OID) returns false, and it goes into
> char2wchar(). char2wchar() is essentially a wrapper around mbstowcs(),
> which does not work on multibyte input when LC_CTYPE=C.
> 
> Quick fix (attached): check whether datctype is C rather than the
> default collation.

This seems right.  It's unfortunate that we would now have the 
possibilty that

lc_ctype_is_c(DEFAULT_COLLATION_OID) != database_ctype_is_c

but that seems to be the nature of things.  Maybe a comment somewhere?





^ permalink  raw  reply  [nested|flat] 2+ messages in thread


end of thread, other threads:[~2023-03-10 08:40 UTC | newest]

Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2023-03-08 05:49 unaccent fails when datlocprovider=i and datctype=C Jeff Davis <pgsql@j-davis.com>
2023-03-10 08:40 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox