v2-0001-Fix-greedy-substring-search-for-non-deterministic.patch
text/x-patch
Filename: v2-0001-Fix-greedy-substring-search-for-non-deterministic.patch
Type: text/x-patch
Part: 0
From f547c38f7fffcaa4c744aea22e16ca9194505916 Mon Sep 17 00:00:00 2001
From: Laurenz Albe <laurenz.albe@cybertec.at>
Date: Tue, 2 Dec 2025 18:15:09 +0100
Subject: [PATCH v2] Fix greedy substring search for non-deterministic
collations
Due to an off-by-one error, the code failed to find matches at the
end of the haystack.
Author: Laurenz Albe <laurenz.albe@cybertec.at>
Reported-By: Adam Warland <adam.warland@infor.com>
Discussion: https://postgr.es/m/19341-1d9a22915edfec58%40postgresql.org
---
src/backend/utils/adt/varlena.c | 2 +-
src/test/regress/expected/collate.icu.utf8.out | 7 +++++++
src/test/regress/sql/collate.icu.utf8.sql | 3 +++
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 3894457ab40..7fe109ba73e 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -1149,7 +1149,7 @@ text_position_next_internal(char *start_ptr, TextPositionState *state)
* Else check if any of the possible substrings starting at hptr
* are equal to the needle.
*/
- for (const char *test_end = hptr; test_end < haystack_end; test_end += pg_mblen(test_end))
+ for (const char *test_end = hptr; test_end <= haystack_end; test_end += pg_mblen(test_end))
{
if (pg_strncoll(hptr, (test_end - hptr), needle, needle_len, state->locale) == 0)
{
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index b8579a1efc6..0a14c1d93ff 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -1484,6 +1484,13 @@ SELECT array_sort('{a,B}'::text[] COLLATE "C");
{B,a}
(1 row)
+-- test replace() at the end of the string (bug #19341)
+SELECT replace('testX' COLLATE case_insensitive, 'x' COLLATE case_insensitive, 'es');
+ replace
+---------
+ testes
+(1 row)
+
-- test language tags
CREATE COLLATION lt_insensitive (provider = icu, locale = 'en-u-ks-level1', deterministic = false);
SELECT 'aBcD' COLLATE lt_insensitive = 'AbCd' COLLATE lt_insensitive;
diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql
index 6f5abac0dc0..5e3fef9b381 100644
--- a/src/test/regress/sql/collate.icu.utf8.sql
+++ b/src/test/regress/sql/collate.icu.utf8.sql
@@ -568,6 +568,9 @@ SELECT 'abc' <= 'ABC' COLLATE case_insensitive, 'abc' >= 'ABC' COLLATE case_inse
SELECT array_sort('{a,B}'::text[] COLLATE case_insensitive);
SELECT array_sort('{a,B}'::text[] COLLATE "C");
+-- test replace() at the end of the string (bug #19341)
+SELECT replace('testX' COLLATE case_insensitive, 'x' COLLATE case_insensitive, 'es');
+
-- test language tags
CREATE COLLATION lt_insensitive (provider = icu, locale = 'en-u-ks-level1', deterministic = false);
SELECT 'aBcD' COLLATE lt_insensitive = 'AbCd' COLLATE lt_insensitive;
--
2.52.0