Re: BUG #19748: tsvectorrecv accepts duplicate lexemes and position 0 that tsvectorin rejects - Mailing list pgsql-bugs

From Tom Lane
Subject Re: BUG #19748: tsvectorrecv accepts duplicate lexemes and position 0 that tsvectorin rejects
Date
Msg-id 1568030.1791322067@sss.pgh.pa.us
Whole thread
In response to Re: BUG #19748: tsvectorrecv accepts duplicate lexemes and position 0 that tsvectorin rejects  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-bugs
I wrote:
> PG Bug reporting form <noreply@postgresql.org> writes:
>> Two checks are still missing relative to tsvectorin():
>> 1. Duplicate lexemes are not merged.

> So my proposal is to just throw an error if the lexeme sequence isn't
> strictly increasing, as it already does for positions.  That is
> probably not something to back-patch, but this issue is surely not
> important enough to worry about back-patching.

>> 2. ... a leading position 0 is accepted.

> Agreed, that's an oversight.

Here's what that would look like.

            regards, tom lane

From 3c32c2cefc572489300d6fab5690e76a0e76cfd4 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 6 Oct 2026 17:23:45 -0400
Subject: [PATCH v1] Tighten data validity checks in tsvectorrecv().

Insist that the input lexemes be already sorted and non-duplicate.
There's no good reason for them not to be, since the required ordering
is simple and platform-independent.  The previous coding was willing
to re-sort them, but that left a gap: it didn't de-duplicate them.
De-duplicating would potentially require merging position lists,
which would be painful; we can't easily re-use tsvectorin()'s logic
for that, since it uses a different temporary representation.
Let's just dodge the problem by insisting the data be fully correct
as-presented, as we were already doing for the position lists.

Also, there was an oversight in the position-list checking: it did
not verify that the first position isn't zero.

While these problems allow tsvectorrecv() to construct tsvectors
that would not be generated internally, the vectors aren't so broken
that we can't work with them.  Therefore, the best course of action
seems to be to apply these tighter rules in master but not back-patch.
Back-patching might break application workflows that aren't causing
any serious problems.  Any such users will need to clean their data
before loading it into PG >= v20, but they'd face that issue anyway,
and a major update is a better time for it than a minor update.

Bug: #19748
Reported-by: Ke <kehan5800@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/19748-6075f0508fa1ffc0@postgresql.org
---
 src/backend/utils/adt/tsvector.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c
index 21d29a7e4f6..2fe782374e7 100644
--- a/src/backend/utils/adt/tsvector.c
+++ b/src/backend/utils/adt/tsvector.c
@@ -454,7 +454,6 @@ tsvectorrecv(PG_FUNCTION_ARGS)
                                  * WordEntries */
     Size        hdrlen;
     Size        len;            /* allocated size of vec */
-    bool        needSort = false;

     nentries = pq_getmsgint(buf, sizeof(int32));

@@ -512,16 +511,18 @@ tsvectorrecv(PG_FUNCTION_ARGS)

         datalen += lex_len;

+        /* Verify correct sorting of lexemes, with no duplicates */
         if (i > 0 && compareentry(&vec->entries[i],
                                   &vec->entries[i - 1],
                                   STRPTR(vec)) <= 0)
-            needSort = true;
+            elog(ERROR, "tsvector lexemes are misordered");

         /* Receive positions */
         if (npos > 0)
         {
             uint16        j;
             WordEntryPos *wepptr;
+            WordEntryPos lastpos = 0;

             /*
              * Pad to 2-byte alignment if necessary. Though we used palloc0
@@ -539,9 +540,13 @@ tsvectorrecv(PG_FUNCTION_ARGS)
             wepptr = POSDATAPTR(vec, &vec->entries[i]);
             for (j = 0; j < npos; j++)
             {
-                wepptr[j] = (WordEntryPos) pq_getmsgint(buf, sizeof(WordEntryPos));
-                if (j > 0 && WEP_GETPOS(wepptr[j]) <= WEP_GETPOS(wepptr[j - 1]))
+                WordEntryPos thispos;
+
+                thispos = (WordEntryPos) pq_getmsgint(buf, sizeof(WordEntryPos));
+                /* Verify positions are sorted, nonduplicate, and not zero */
+                if (WEP_GETPOS(thispos) <= WEP_GETPOS(lastpos))
                     elog(ERROR, "position information is misordered");
+                wepptr[j] = lastpos = thispos;
             }

             datalen += sizeof(uint16) + npos * sizeof(WordEntryPos);
@@ -559,9 +564,5 @@ tsvectorrecv(PG_FUNCTION_ARGS)

     SET_VARSIZE(vec, hdrlen + datalen);

-    if (needSort)
-        qsort_arg(ARRPTR(vec), vec->size, sizeof(WordEntry),
-                  compareentry, STRPTR(vec));
-
     PG_RETURN_TSVECTOR(vec);
 }
--
2.52.0


pgsql-bugs by date:

Previous
From: Zsolt Parragi
Date:
Subject: Re: autovacuum: automatically propagate updated parameters
Next
From: shihao zhong
Date:
Subject: Re: BUG #19732: first_value/last_value/nth_value return NULL with EXCLUDE TIES when the current row is outside its f