10000 Prevent crash when ts_rewrite() replaces a non-top-level subtree with… · phan-pivotal/postgres@f4ccee4 · GitHub
[go: up one dir, main page]

Skip to content

Commit f4ccee4

Browse files
committed
Prevent crash when ts_rewrite() replaces a non-top-level subtree with null.
When ts_rewrite()'s replacement argument is an empty tsquery, it's supposed to simplify any operator nodes whose operand(s) become NULL; but it failed to do that reliably, because dropvoidsubtree() only examined the top level of the result tree. Rather than make a second recursive pass, let's just give the responsibility to dofindsubquery() to simplify while it's doing the main replacement pass. Per report from Andreas Seltenreich. Artur Zakirov, with some cosmetic changes by me. Back-patch to all supported branches. Discussion: https://postgr.es/m/8737i01dew.fsf@credativ.de
1 parent 981885d commit f4ccee4

File tree

3 files changed

+47
-29
lines changed

3 files changed

+47
-29
lines changed

src/backend/utils/adt/tsquery_rewrite.c

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
140140

141141
node->nchild = j;
142142

143+
/*
144+
* At this point we might have a node with zero or one child,
145+
* which should be simplified. But we leave it to our caller
146+
* (dofindsubquery) to take care of that.
147+
*/
148+
143149
/*
144150
* Re-sort the node to put new child in the right place. This
145151
* is a bit bogus, because it won't matter for findsubquery's
@@ -186,6 +192,15 @@ findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
186192
* Recursive guts of findsubquery(): attempt to replace "ex" with "subs"
187193
* at the root node, and if we failed to do so, recursively match against
188194
* child nodes.
195+
*
196+
* Delete any void subtrees resulting from the replacement.
197+
* In the following example '5' is replaced by empty operand:
198+
*
199+
* AND -> 6
200+
* / \
201+
* 5 OR
202+
* / \
203+
* 6 5
189204
*/
190205
static QTNode *
191206
dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
@@ -196,45 +211,33 @@ dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
196211
/* also, since it's a bit expensive, let's check for query cancel. */
197212
CHECK_FOR_INTERRUPTS();
198213

214+
/* match at the node itself */
199215
root = findeq(root, ex, subs, isfind);
200216

201-
if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
202-
{
203-
int i;
204-
205-
for (i = 0; i < root->nchild; i++)
206-
root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
207-
}
208-
209-
return root;
210-
}
211-
212-
/*
213-
* Delete any void subtrees that may have been inserted when the replacement
214-
* subtree is void.
215-
*/
216-
static QTNode *
217-
dropvoidsubtree(QTNode *root)
218-
{
219-
if (!root)
220-
return NULL;
221-
222-
if (root->valnode->type == QI_OPR)
217+
/* unless we matched here, consider matches at child nodes */
218+
if (root && (root->flags & QTN_NOCHANGE) == 0 &&
219+
root->valnode->type == QI_OPR)
223220
{
224221
int i,
225222
j = 0;
226223

224+
/*
225+
* Any subtrees that are replaced by NULL must be dropped from the
226+
* tree.
227+
*/
227228
for (i = 0; i < root->nchild; i++)
228229
{
229-
if (root->child[i])
230-
{
231-
root->child[j] = root->child[i];
230+
root->child[j] = dofindsubquery(root->child[i], ex, subs, isfind);
231+
if (root->child[j])
232232
j++;
233-
}
234233
}
235234

236235
root->nchild = j;
237236

237+
/*
238+
* If we have just zero or one remaining child node, simplify out this
239+
* operator node.
240+
*/
238241
if (root->nchild == 0)
239242
{
240243
QTNFree(root);
@@ -267,9 +270,6 @@ findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
267270

268271
root = dofindsubquery(root, ex, subs, &DidFind);
269272

270-
if (!subs && DidFind)
271-
root = dropvoidsubtree(root);
272-
273273
if (isfind)
274274
*isfind = DidFind;
275275

src/test/regress/expected/tsearch.out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,21 @@ SELECT ts_rewrite( 'bar & new & qq & foo & york', 'SELECT keyword, sample FROM t
894894
'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | ( 'big' & 'appl' | 'new' & 'york' ) )
895895
(1 row)
896896

897+
-- Check empty substitution
898+
SELECT ts_rewrite(to_tsquery('5 & (6 | 5)'), to_tsquery('5'), to_tsquery(''));
899+
NOTICE: text-search query doesn't contain lexemes: ""
900+
ts_rewrite
901+
------------
902+
'6'
903+
(1 row)
904+
905+
SELECT ts_rewrite(to_tsquery('!5'), to_tsquery('5'), to_tsquery(''));
906+
NOTICE: text-search query doesn't contain lexemes: ""
907+
ts_rewrite
908+
------------
909+
910+
(1 row)
911+
897912
SELECT keyword FROM test_tsquery WHERE keyword @> 'new';
898913
keyword
899914
----------------

src/test/regress/sql/tsearch.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ SELECT ts_rewrite( 'moscow', 'SELECT keyword, sample FROM test_tsquery');
316316
SELECT ts_rewrite( 'moscow & hotel', 'SELECT keyword, sample FROM test_tsquery');
317317
SELECT ts_rewrite( 'bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery');
318318

319+
-- Check empty substitution
320+
SELECT ts_rewrite(to_tsquery('5 & (6 | 5)'), to_tsquery('5'), to_tsquery(''));
321+
SELECT ts_rewrite(to_tsquery('!5'), to_tsquery('5'), to_tsquery(''));
319322

320323
SELECT keyword FROM test_tsquery WHERE keyword @> 'new';
321324
SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow';

0 commit comments

Comments
 (0)
0