diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2023-04-06 15:52:37 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2023-04-06 15:52:37 -0400 |
commit | 34ad3aedb0ce4f36fd55aa916200a68a185bf1ed (patch) | |
tree | cfbf11297c747bf91e1797b86e9589fe781bde5c /src | |
parent | 0a6aaf01166c5f8f94abba949b92b2003c7fe9b5 (diff) | |
download | postgresql-34ad3aedb0ce4f36fd55aa916200a68a185bf1ed.tar.gz postgresql-34ad3aedb0ce4f36fd55aa916200a68a185bf1ed.zip |
Fix ts_headline() edge cases for empty query and empty search text.
tsquery's GETQUERY() macro is only safe to apply to a tsquery
that is known non-empty; otherwise it gives a pointer to garbage.
Before commit 5a617d75d, ts_headline() avoided this pitfall, but
only in a very indirect, nonobvious way. (hlCover could not reach
its TS_execute call, because if the query contains no lexemes
then hlFirstIndex would surely return -1.) After that commit,
it fell into the trap, resulting in weird errors such as
"unrecognized operator" and/or valgrind complaints. In HEAD,
fix this by not calling TS_execute_locations() at all for an
empty query. In the back branches, add a defensive check to
hlCover() --- that's not fixing any live bug, but I judge the
code a bit too fragile as-is.
Also, both mark_hl_fragments() and mark_hl_words() were careless
about the possibility of empty search text: in the cases where
no match has been found, they'd end up telling mark_fragment() to
mark from word indexes 0 to 0 inclusive, even when there is no
word 0. This is harmless since we over-allocated the prs->words
array, but it does annoy valgrind. Fix so that the end index is -1
and thus mark_fragment() will do nothing in such cases.
Bottom line is that this fixes a live bug in HEAD, but in the
back branches it's only getting rid of a valgrind nitpick.
Back-patch anyway.
Per report from Alexander Lakhin.
Discussion: https://postgr.es/m/c27f642d-020b-01ff-ae61-086af287c4fd@gmail.com
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/tsearch/wparser_def.c | 8 | ||||
-rw-r--r-- | src/test/regress/expected/tsearch.out | 21 | ||||
-rw-r--r-- | src/test/regress/sql/tsearch.sql | 6 |
3 files changed, 33 insertions, 2 deletions
diff --git a/src/backend/tsearch/wparser_def.c b/src/backend/tsearch/wparser_def.c index a3e5baf9782..98029e78fb4 100644 --- a/src/backend/tsearch/wparser_def.c +++ b/src/backend/tsearch/wparser_def.c @@ -2039,6 +2039,9 @@ hlCover(HeadlineParsedText *prs, TSQuery query, int max_cover, nextpmax; hlCheck ch; + if (query->size <= 0) + return false; /* empty query matches nothing */ + /* * We look for the earliest, shortest substring of prs->words that * satisfies the query. Both the pmin and pmax indices must be words @@ -2343,7 +2346,8 @@ mark_hl_fragments(HeadlineParsedText *prs, TSQuery query, bool highlightall, /* show the first min_words words if we have not marked anything */ if (num_f <= 0) { - startpos = endpos = curlen = 0; + startpos = curlen = 0; + endpos = -1; for (i = 0; i < prs->curwords && curlen < min_words; i++) { if (!NONWORDTOKEN(prs->words[i].type)) @@ -2498,7 +2502,7 @@ mark_hl_words(HeadlineParsedText *prs, TSQuery query, bool highlightall, if (bestlen < 0) { curlen = 0; - pose = 0; + pose = -1; for (i = 0; i < prs->curwords && curlen < min_words; i++) { if (!NONWORDTOKEN(prs->words[i].type)) diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out index 45b92a63388..4e52edf610f 100644 --- a/src/test/regress/expected/tsearch.out +++ b/src/test/regress/expected/tsearch.out @@ -1990,6 +1990,27 @@ to_tsquery('english','Lorem') && phraseto_tsquery('english','ullamcorper urna'), <b>Lorem</b> ipsum <b>urna</b>. Nullam nullam <b>ullamcorper</b> <b>urna</b> (1 row) +-- Edge cases with empty query +SELECT ts_headline('english', +'', ''::tsquery); +NOTICE: text-search query doesn't contain lexemes: "" +LINE 2: '', ''::tsquery); + ^ + ts_headline +------------- + +(1 row) + +SELECT ts_headline('english', +'foo bar', ''::tsquery); +NOTICE: text-search query doesn't contain lexemes: "" +LINE 2: 'foo bar', ''::tsquery); + ^ + ts_headline +------------- + foo bar +(1 row) + --Rewrite sub system CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT); \set ECHO none diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql index d929210998a..168eb0b9713 100644 --- a/src/test/regress/sql/tsearch.sql +++ b/src/test/regress/sql/tsearch.sql @@ -549,6 +549,12 @@ SELECT ts_headline('english', to_tsquery('english','Lorem') && phraseto_tsquery('english','ullamcorper urna'), 'MaxFragments=100, MaxWords=100, MinWords=1'); +-- Edge cases with empty query +SELECT ts_headline('english', +'', ''::tsquery); +SELECT ts_headline('english', +'foo bar', ''::tsquery); + --Rewrite sub system CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT); |