postgres.git / summary / log / commit / refs
commit b3510700e994a525958dc98a33d320786312d19f
Author: Jeff Davis <jdavis@postgresql.org>
Date: Thu Aug 20 20:54:36 2026 +0000
First released in: 16.16, 17.12, 18.7
hashtext: fix fragile code.
Previously, in the path for non-deterministic collations, the code
assumed that bsize==rsize. That assumption seems to be true for ICU,
and all non-deterministic collations are ICU, so it's not known to be
an actual bug.
The only known place where bsize may not equal rsize is in the libc
provider, where strxfrm() can return an upper bound of the size needed
to store the result. That means the initial call to determine the
buffer size (with dest==NULL, n==0) could return a larger number than
the actual call with an adequate dest buffer. That's OK, because libc
locales are always deterministic.
Commit 679c5084cf2 partially fixed the assumption, but missed this
part. Fix it, and add a more prominent documentation note.
Reviewed-by: Haibo Yan <tristan.yim@gmail.com>
Discussion: https://postgr.es/m/CABXr29Hb31nkj1g2Jmk+1BhAm=3ecGs_pWy4tU++j8CQBnbMxQ@mail.gmail.com
Backpatch-through: 16
src/backend/access/hash/hashfunc.c | 4 ++--
src/backend/utils/adt/pg_locale.c | 8 +++++---
src/backend/utils/adt/pg_locale_libc.c | 5 +++++
src/backend/utils/adt/varchar.c | 4 ++--
4 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index adab96b9f13..ed272e6f01e 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -310,7 +310,7 @@ hashtext(PG_FUNCTION_ARGS)
* character in the hash, but it was done before and the behavior must
* be preserved.
*/
- result = hash_any((uint8_t *) buf, bsize + 1);
+ result = hash_any((uint8_t *) buf, rsize + 1);
pfree(buf);
}
@@ -365,7 +365,7 @@ hashtextextended(PG_FUNCTION_ARGS)
* character in the hash, but it was done before and the behavior must
* be preserved.
*/
- result = hash_any_extended((uint8_t *) buf, bsize + 1,
+ result = hash_any_extended((uint8_t *) buf, rsize + 1,
PG_GETARG_INT64(1));
pfree(buf);
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index ebf38647f95..b6f3917c67d 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1566,9 +1566,11 @@ pg_strxfrm(char *dest, const char *src, size_t destsize, pg_locale_t locale)
* pg_strxfrm_enabled() first, otherwise this function may return wrong
* results or an error.
*
- * Returns the number of bytes needed (or more) to store the transformed
- * string, excluding the terminating nul byte. If the value returned is
- * 'destsize' or greater, the resulting contents of 'dest' are undefined.
+ * Returns the number of bytes needed (NB: or more; see comments above
+ * strnxfrm_libc()) to store the transformed string, excluding the terminating
+ * nul byte. If the value returned is 'destsize' or greater, the resulting
+ * contents of 'dest' are undefined, and the caller should use the return
+ * value to resize the buffer.
*/
size_t
pg_strnxfrm(char *dest, size_t destsize, const char *src, ssize_t srclen,
diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c
index 31274f069d2..13f22b5a5d1 100644
--- a/src/backend/utils/adt/pg_locale_libc.c
+++ b/src/backend/utils/adt/pg_locale_libc.c
@@ -622,6 +622,11 @@ strncoll_libc(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2,
* NUL-terminate src, if necessary, and pass to strxfrm_l().
*
* A source length of -1 means that it's already NUL-terminated.
+ *
+ * NB: it's possible for this function to return a different size needed for
+ * two calls with the same input string. If destsize is too small to hold the
+ * result, strxfrm() may return the upper bound of the size needed rather than
+ * the exact size needed.
*/
size_t
strnxfrm_libc(char *dest, size_t destsize, const char *src, ssize_t srclen,
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 3f40c9da1a0..38620260338 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -1029,7 +1029,7 @@ hashbpchar(PG_FUNCTION_ARGS)
* character in the hash, but it was done before and the behavior must
* be preserved.
*/
- result = hash_any((uint8_t *) buf, bsize + 1);
+ result = hash_any((uint8_t *) buf, rsize + 1);
pfree(buf);
}
@@ -1086,7 +1086,7 @@ hashbpcharextended(PG_FUNCTION_ARGS)
* character in the hash, but it was done before and the behavior must
* be preserved.
*/
- result = hash_any_extended((uint8_t *) buf, bsize + 1,
+ result = hash_any_extended((uint8_t *) buf, rsize + 1,
PG_GETARG_INT64(1));
pfree(buf);
[parent: 47c350c522e3]