BUG #19748: tsvectorrecv accepts duplicate lexemes and position 0 that tsvectorin rejects

From: PG Bug reporting form <noreply(at)postgresql(dot)org>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Cc: kehan5800(at)gmail(dot)com
Subject: BUG #19748: tsvectorrecv accepts duplicate lexemes and position 0 that tsvectorin rejects
Date: 2026-10-04 05:19:27
Message-ID: 19748-6075f0508fa1ffc0@postgresql.org
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

The following bug has been logged on the website:

Bug reference: 19748
Logged by: Ke
Email address: kehan5800(at)gmail(dot)com
PostgreSQL version: 18.6
Operating system: Ubuntu 22.04.2 x86_64
Description:

tsvectorrecv() (src/backend/utils/adt/tsvector.c:446) has this comment
(tsvector.c:552-556):

* Enforce that datalen is still within MAXSTRPOS, ie the last lexeme
* didn't go past that. We could allow that, since no "pos" field
* overflowed, but tsvectorrecv shouldn't accept values that other
* tsvector-constructing routines wouldn't.

Two checks are still missing relative to tsvectorin():

1. Duplicate lexemes are not merged. tsvectorin() runs uniqueentry()
(tsvector.c:265). tsvectorrecv() detects "compareentry(...) <= 0"
(tsvector.c:514-517), but only sorts (tsvector.c:563); equal entries
stay. The result is a tsvector with repeated lexemes, which no other
constructor produces, and which silently changes on a text round trip.

2. The first position is never checked. tsvectorin() rejects position 0
("wrong position info in tsvector", tsvector_parser.c:334); the receive
loop only checks that positions ascend for j > 0 (tsvector.c:540-545),
so a leading position 0 is accepted.

Self-contained reproduction (bash + psql; the files are COPY BINARY files
with one tsvector column):

psql -c "CREATE TABLE tv (v tsvector)"

# one row: tsvector with 2 entries, 'a' (no positions), 'a' (no
positions)
printf
'PGCOPY\n\377\r\n\0\0\0\0\0\0\0\0\0\0\001\0\0\0\014\0\0\0\002a\0\0\0a\0\0\0\377\377'
> dup.bin
# one row: tsvector with 1 entry, 'a' with one position, 0
printf
'PGCOPY\n\377\r\n\0\0\0\0\0\0\0\0\0\0\001\0\0\0\012\0\0\0\001a\0\0\001\0\0\377\377'
> pos0.bin

psql -c "\copy tv from 'dup.bin' with (format binary)" -- COPY 1
psql -c "\copy tv from 'pos0.bin' with (format binary)" -- COPY 1

SELECT v, length(v), pg_input_is_valid(v::text, 'tsvector') FROM tv;
v | length | pg_input_is_valid
---------+--------+-------------------
'a':0 | 1 | f
'a' 'a' | 2 | t

SELECT v, length(v), v::text::tsvector AS reparsed,
length(v::text::tsvector), v = v::text::tsvector AS same
FROM tv WHERE length(v) = 2;
v | length | reparsed | length | same
---------+--------+----------+--------+------
'a' 'a' | 2 | 'a' | 1 | f

SELECT v::text::tsvector FROM tv WHERE length(v) = 1;
ERROR: wrong position info in tsvector: "'a':0"

Consequences through pg_dump (plain format, psql restore), from the
attached transcript:

duplicates: 'a' 'a' -> 'a', 'a':1 'a':2 -> 'a':1,2, 'a' 'a' 'b' -> 'a'
'b';
restore reports no error and length(v) changes
position 0: ERROR: wrong position info in tsvector: "'a':0";
0 of 2 rows of that table restored

Expected: tsvectorrecv() rejects (or normalises, as tsvectorin() does for
duplicates) values that tsvectorin() would not produce, as its own comment
says it should.

Actual: both are accepted; the duplicate case later changes silently on a
text dump/restore, and the position-0 case makes the dump unrestorable.

Suggested fix:
- after the qsort in tsvectorrecv(), merge adjacent equal entries the way
uniqueentry() does (merging and de-duplicating their position lists),
or reject them with ERRCODE_INVALID_BINARY_REPRESENTATION;
- in the position loop, also reject WEP_GETPOS(wepptr[0]) == 0.
The existing elog(ERROR, ...) calls in tsvectorrecv() could at the same
time become ereport(ERROR, errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
...), since they are reachable from client input (COPY ... FORMAT binary,
binary-format parameters).

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message PG Bug reporting form 2026-10-04 05:20:01 BUG #19749: bpchar_ops declares equalimage although bpchar equality ignores trailing spaces
Previous Message PG Bug reporting form 2026-10-04 05:19:03 BUG #19747: pg_dump does not pin array_nulls, so restore mangles NULL array elements