summaryrefslogtreecommitdiff
path: root/contrib/ltree/lquery_op.c
diff options
context:
space:
mode:
authorJeff Davis2025-12-16 19:13:17 +0000
committerJeff Davis2025-12-16 20:57:12 +0000
commit806555e3000d0b0e0c536c1dc65548128d457d86 (patch)
tree8b63be0550fb7d31cc82630f1e5545f9bb0a7d0b /contrib/ltree/lquery_op.c
parentf79e239e0bc6e4d5fe91e1a0e573ecf0715d6c8c (diff)
ltree: fix case-insensitive matching.REL_18_STABLE
Previously, ltree_prefix_eq_ci() used lowercasing with the default collation; while ltree_crc32_sz() used tolower() directly. These were equivalent only if the default collation provider was libc and the encoding was single-byte. Change both to use casefolding with the default collation. Backpatch through 18, where the casefolding APIs were introduced. The bug exists in earlier versions, but would require some adaptation. A REINDEX is required for ltree indexes where the database default collation is not libc. Reviewed-by: Chao Li <li.evan.chao@gmail.com> Reviewed-by: Peter Eisentraut <peter@eisentraut.org> Backpatch-through: 18 Discussion: https://postgr.es/m/450ceb6260cad30d7afdf155d991a9caafee7c0d.camel@j-davis.com Discussion: https://postgr.es/m/01fc00fd66f641b9693d4f9f1af0ccf44cbdfbdf.camel@j-davis.com
Diffstat (limited to 'contrib/ltree/lquery_op.c')
-rw-r--r--contrib/ltree/lquery_op.c39
1 files changed, 36 insertions, 3 deletions
diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c
index 0b39d64a839..9b1de101213 100644
--- a/contrib/ltree/lquery_op.c
+++ b/contrib/ltree/lquery_op.c
@@ -93,11 +93,44 @@ ltree_prefix_eq(const char *a, size_t a_sz, const char *b, size_t b_sz)
bool
ltree_prefix_eq_ci(const char *a, size_t a_sz, const char *b, size_t b_sz)
{
- char *al = str_tolower(a, a_sz, DEFAULT_COLLATION_OID);
- char *bl = str_tolower(b, b_sz, DEFAULT_COLLATION_OID);
+ static pg_locale_t locale = NULL;
+ size_t al_sz = a_sz + 1;
+ size_t al_len;
+ char *al = palloc(al_sz);
+ size_t bl_sz = b_sz + 1;
+ size_t bl_len;
+ char *bl = palloc(bl_sz);
bool res;
- res = (strncmp(al, bl, a_sz) == 0);
+ if (!locale)
+ locale = pg_newlocale_from_collation(DEFAULT_COLLATION_OID);
+
+ /* casefold both a and b */
+
+ al_len = pg_strfold(al, al_sz, a, a_sz, locale);
+ if (al_len + 1 > al_sz)
+ {
+ /* grow buffer if needed and retry */
+ al_sz = al_len + 1;
+ al = repalloc(al, al_sz);
+ al_len = pg_strfold(al, al_sz, a, a_sz, locale);
+ Assert(al_len + 1 <= al_sz);
+ }
+
+ bl_len = pg_strfold(bl, bl_sz, b, b_sz, locale);
+ if (bl_len + 1 > bl_sz)
+ {
+ /* grow buffer if needed and retry */
+ bl_sz = bl_len + 1;
+ bl = repalloc(bl, bl_sz);
+ bl_len = pg_strfold(bl, bl_sz, b, b_sz, locale);
+ Assert(bl_len + 1 <= bl_sz);
+ }
+
+ if (al_len > bl_len)
+ res = false;
+ else
+ res = (strncmp(al, bl, al_len) == 0);
pfree(al);
pfree(bl);