summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-12-11 13:09:57 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2016-12-11 13:09:57 -0500
commit0eaaaf00e296c2048b868b7c1d3c12c0eae6dd12 (patch)
tree42663ed9b46178ceea18aab5d95ed9e3d5a7187d /src/backend
parent9cda81f0056ca488dbd6cded64db1238aed816b2 (diff)
downloadpostgresql-0eaaaf00e296c2048b868b7c1d3c12c0eae6dd12.tar.gz
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
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/utils/adt/tsquery_rewrite.c58
1 files changed, 29 insertions, 29 deletions
diff --git a/src/backend/utils/adt/tsquery_rewrite.c b/src/backend/utils/adt/tsquery_rewrite.c
index e5bed6efb3..1e09f59005 100644
--- a/src/backend/utils/adt/tsquery_rewrite.c
+++ b/src/backend/utils/adt/tsquery_rewrite.c
@@ -141,6 +141,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
* remaining processing, and it's insufficient to prepare the
@@ -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;