diff --git a/src/backend/utils/adt/tsquery_rewrite.c b/src/backend/utils/adt/tsquery_rewrite.c index e5bed6efb38..1e09f590056 100644 --- a/src/backend/utils/adt/tsquery_rewrite.c +++ b/src/backend/utils/adt/tsquery_rewrite.c @@ -140,6 +140,12 @@ findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind) node->nchild = j; + /* + * At this point we might have a node with zero or one child, + * which should be simplified. But we leave it to our caller + * (dofindsubquery) to take care of that. + */ + /* * Re-sort the node to put new child in the right place. This * 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) * Recursive guts of findsubquery(): attempt to replace "ex" with "subs" * at the root node, and if we failed to do so, recursively match against * child nodes. + * + * Delete any void subtrees resulting from the replacement. + * In the following example '5' is replaced by empty operand: + * + * AND -> 6 + * / \ + * 5 OR + * / \ + * 6 5 */ static QTNode * dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind) @@ -196,45 +211,33 @@ dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind) /* also, since it's a bit expensive, let's check for query cancel. */ CHECK_FOR_INTERRUPTS(); + /* match at the node itself */ root = findeq(root, ex, subs, isfind); - if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR) - { - int i; - - for (i = 0; i < root->nchild; i++) - root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind); - } - - return root; -} - -/* - * Delete any void subtrees that may have been inserted when the replacement - * subtree is void. - */ -static QTNode * -dropvoidsubtree(QTNode *root) -{ - if (!root) - return NULL; - - if (root->valnode->type == QI_OPR) + /* unless we matched here, consider matches at child nodes */ + if (root && (root->flags & QTN_NOCHANGE) == 0 && + root->valnode->type == QI_OPR) { int i, j = 0; + /* + * Any subtrees that are replaced by NULL must be dropped from the + * tree. + */ for (i = 0; i < root->nchild; i++) { - if (root->child[i]) - { - root->child[j] = root->child[i]; + root->child[j] = dofindsubquery(root->child[i], ex, subs, isfind); + if (root->child[j]) j++; - } } root->nchild = j; + /* + * If we have just zero or one remaining child node, simplify out this + * operator node. + */ if (root->nchild == 0) { QTNFree(root); @@ -267,9 +270,6 @@ findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind) root = dofindsubquery(root, ex, subs, &DidFind); - if (!subs && DidFind) - root = dropvoidsubtree(root); - if (isfind) *isfind = DidFind; diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out index 55d6a857387..be4bdf21f10 100644 --- a/src/test/regress/expected/tsearch.out +++ b/src/test/regress/expected/tsearch.out @@ -1251,6 +1251,21 @@ SELECT ts_rewrite('5 <-> (6 | 8)', 'SELECT keyword, sample FROM test_tsquery'::t '5' <-> '7' | '5' <-> '8' (1 row) +-- Check empty substitution +SELECT ts_rewrite(to_tsquery('5 & (6 | 5)'), to_tsquery('5'), to_tsquery('')); +NOTICE: text-search query doesn't contain lexemes: "" + ts_rewrite +------------ + '6' +(1 row) + +SELECT ts_rewrite(to_tsquery('!5'), to_tsquery('5'), to_tsquery('')); +NOTICE: text-search query doesn't contain lexemes: "" + ts_rewrite +------------ + +(1 row) + SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; keyword ---------------- diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql index afd990e6969..de43860c704 100644 --- a/src/test/regress/sql/tsearch.sql +++ b/src/test/regress/sql/tsearch.sql @@ -418,6 +418,9 @@ SELECT ts_rewrite('1 & (2 <2> 3)', 'SELECT keyword, sample FROM test_tsquery'::t SELECT ts_rewrite('5 <-> (1 & (2 <-> 3))', 'SELECT keyword, sample FROM test_tsquery'::text ); SELECT ts_rewrite('5 <-> (6 | 8)', 'SELECT keyword, sample FROM test_tsquery'::text ); +-- Check empty substitution +SELECT ts_rewrite(to_tsquery('5 & (6 | 5)'), to_tsquery('5'), to_tsquery('')); +SELECT ts_rewrite(to_tsquery('!5'), to_tsquery('5'), to_tsquery('')); SELECT keyword FROM test_tsquery WHERE keyword @> 'new'; SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow';