2121#include "utils/builtins.h"
2222
2323
24- static int
25- addone (int * counters ,int last ,int total )
26- {
27- /* since this function recurses, it could be driven to stack overflow. */
28- check_stack_depth ();
29-
30- counters [last ]++ ;
31- if (counters [last ] >=total )
32- {
33- if (last == 0 )
34- return 0 ;
35- if (addone (counters ,last - 1 ,total - 1 )== 0 )
36- return 0 ;
37- counters [last ]= counters [last - 1 ]+ 1 ;
38- }
39- return 1 ;
40- }
41-
4224/*
43- * If node is equal to ex, replace it with subs. Replacement is actually done
44- * by returning either node or a copy of subs.
25+ * If "node" is equal to "ex", return a copy of "subs" instead.
26+ * If "ex" matches a subset of node's children, return a modified version
27+ * of "node" in which those children are replaced with a copy of "subs".
28+ * Otherwise return "node" unmodified.
29+ *
30+ * The QTN_NOCHANGE bit is set in successfully modified nodes, so that
31+ * we won't uselessly recurse into them.
32+ * Also, set *isfind true if we make a replacement.
4533 */
4634static QTNode *
4735findeq (QTNode * node ,QTNode * ex ,QTNode * subs ,bool * isfind )
4836{
37+ /* Can't match unless signature matches and node type matches. */
4938if ((node -> sign & ex -> sign )!= ex -> sign ||
5039node -> valnode -> type != ex -> valnode -> type )
5140return node ;
5241
42+ /* Ignore nodes marked NOCHANGE, too. */
5343if (node -> flags & QTN_NOCHANGE )
5444return node ;
5545
5646if (node -> valnode -> type == QI_OPR )
5747{
48+ /* Must be same operator. */
5849if (node -> valnode -> qoperator .oper != ex -> valnode -> qoperator .oper )
5950return node ;
6051
6152if (node -> nchild == ex -> nchild )
6253{
54+ /*
55+ * Simple case: when same number of children, match if equal.
56+ * (This is reliable when the children were sorted earlier.)
57+ */
6358if (QTNEq (node ,ex ))
6459{
60+ /* Match; delete node and return a copy of subs instead. */
6561QTNFree (node );
6662if (subs )
6763{
@@ -73,79 +69,92 @@ findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
7369* isfind = true;
7470}
7571}
76- else if (node -> nchild > ex -> nchild )
72+ else if (node -> nchild > ex -> nchild && ex -> nchild > 0 )
7773{
7874/*
79- * AND and NOT are commutative, so we check if a subset of the
80- * children match. For example, if tnode is A | B | C, and ex is B
81- * | C, we have a match after we convert tnode to A | (B | C).
75+ * AND and OR are commutative/associative, so we should check if a
76+ * subset of the children match. For example, if node is A|B|C,
77+ * and ex is B|C, we have a match after we notionally convert node
78+ * to A|(B|C). This does not work for NOT or PHRASE nodes, but we
79+ * can't get here for those node types because they have a fixed
80+ * number of children.
81+ *
82+ * Because we expect that the children are sorted, it suffices to
83+ * make one pass through the two lists to find the matches.
8284 */
83- int * counters = ( int * ) palloc ( sizeof ( int ) * node -> nchild ) ;
84- int i ;
85- QTNode * tnode = ( QTNode * ) palloc ( sizeof ( QTNode ));
86-
87- memset ( tnode , 0 , sizeof ( QTNode ));
88- tnode -> child = ( QTNode * * ) palloc ( sizeof ( QTNode * ) * ex -> nchild );
89- tnode -> nchild = ex -> nchild ;
90- tnode -> valnode = ( QueryItem * ) palloc ( sizeof ( QueryItem ) );
91- * ( tnode -> valnode ) = * ( ex -> valnode );
92-
93- for ( i = 0 ; i < ex -> nchild ; i ++ )
94- counters [ i ] = i ;
95-
96- do
85+ bool * matched ;
86+ int nmatched ;
87+ int i ,
88+ j ;
89+
90+ /* Assert that the subset rule is OK */
91+ Assert ( node -> valnode -> qoperator . oper == OP_AND ||
92+ node -> valnode -> qoperator . oper == OP_OR );
93+
94+ /* matched[] will record which children of node matched */
95+ matched = ( bool * ) palloc0 ( node -> nchild * sizeof ( bool ));
96+ nmatched = 0 ;
97+ i = j = 0 ;
98+ while ( i < node -> nchild && j < ex -> nchild )
9799{
98- tnode -> sign = 0 ;
99- for (i = 0 ;i < ex -> nchild ;i ++ )
100+ int cmp = QTNodeCompare (node -> child [i ],ex -> child [j ]);
101+
102+ if (cmp == 0 )
100103{
101- tnode -> child [i ]= node -> child [counters [i ]];
102- tnode -> sign |=tnode -> child [i ]-> sign ;
104+ /* match! */
105+ matched [i ]= true;
106+ nmatched ++ ;
107+ i ++ ,j ++ ;
103108}
109+ else if (cmp < 0 )
110+ {
111+ /* node->child[i] has no match, ignore it */
112+ i ++ ;
113+ }
114+ else
115+ {
116+ /* ex->child[j] has no match; we can give up immediately */
117+ break ;
118+ }
119+ }
104120
105- if (QTNEq (tnode ,ex ))
121+ if (nmatched == ex -> nchild )
122+ {
123+ /* collapse out the matched children of node */
124+ j = 0 ;
125+ for (i = 0 ;i < node -> nchild ;i ++ )
106126{
107- int j = 0 ;
108-
109- pfree (tnode -> valnode );
110- pfree (tnode -> child );
111- pfree (tnode );
112- if (subs )
113- {
114- tnode = QTNCopy (subs );
115- tnode -> flags = QTN_NOCHANGE |QTN_NEEDFREE ;
116- }
127+ if (matched [i ])
128+ QTNFree (node -> child [i ]);
117129else
118- tnode = NULL ;
119-
120- node -> child [counters [0 ]]= tnode ;
121-
122- for (i = 1 ;i < ex -> nchild ;i ++ )
123- node -> child [counters [i ]]= NULL ;
124- for (i = 0 ;i < node -> nchild ;i ++ )
125- {
126- if (node -> child [i ])
127- {
128- node -> child [j ]= node -> child [i ];
129- j ++ ;
130- }
131- }
130+ node -> child [j ++ ]= node -> child [i ];
131+ }
132132
133- node -> nchild = j ;
133+ /* and instead insert a copy of subs */
134+ if (subs )
135+ {
136+ subs = QTNCopy (subs );
137+ subs -> flags |=QTN_NOCHANGE ;
138+ node -> child [j ++ ]= subs ;
139+ }
134140
135- * isfind = true;
141+ node -> nchild = j ;
142+
143+ /*
144+ * Re-sort the node to put new child in the right place. This
145+ * is a bit bogus, because it won't matter for findsubquery's
146+ * remaining processing, and it's insufficient to prepare the
147+ * tree for another search (we would need to re-flatten as
148+ * well, and we don't want to do that because we'd lose the
149+ * QTN_NOCHANGE marking on the new child). But it's needed to
150+ * keep the results the same as the regression tests expect.
151+ */
152+ QTNSort (node );
136153
137- break ;
138- }
139- }while (addone (counters ,ex -> nchild - 1 ,node -> nchild ));
140- if (tnode && (tnode -> flags & QTN_NOCHANGE )== 0 )
141- {
142- pfree (tnode -> valnode );
143- pfree (tnode -> child );
144- pfree (tnode );
154+ * isfind = true;
145155}
146- else
147- QTNSort (node );
148- pfree (counters );
156+
157+ pfree (matched );
149158}
150159}
151160else
@@ -173,12 +182,20 @@ findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
173182return node ;
174183}
175184
185+ /*
186+ * Recursive guts of findsubquery(): attempt to replace "ex" with "subs"
187+ * at the root node, and if we failed to do so, recursively match against
188+ * child nodes.
189+ */
176190static QTNode *
177191dofindsubquery (QTNode * root ,QTNode * ex ,QTNode * subs ,bool * isfind )
178192{
179193/* since this function recurses, it could be driven to stack overflow. */
180194check_stack_depth ();
181195
196+ /* also, since it's a bit expensive, let's check for query cancel. */
197+ CHECK_FOR_INTERRUPTS ();
198+
182199root = findeq (root ,ex ,subs ,isfind );
183200
184201if (root && (root -> flags & QTN_NOCHANGE )== 0 && root -> valnode -> type == QI_OPR )
@@ -192,6 +209,10 @@ dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
192209return root ;
193210}
194211
212+ /*
213+ * Delete any void subtrees that may have been inserted when the replacement
214+ * subtree is void.
215+ */
195216static QTNode *
196217dropvoidsubtree (QTNode * root )
197218{
@@ -231,6 +252,14 @@ dropvoidsubtree(QTNode *root)
231252return root ;
232253}
233254
255+ /*
256+ * Substitute "subs" for "ex" throughout the QTNode tree at root.
257+ *
258+ * If isfind isn't NULL, set *isfind to show whether we made any substitution.
259+ *
260+ * Both "root" and "ex" must have been through QTNTernary and QTNSort
261+ * to ensure reliable matching.
262+ */
234263QTNode *
235264findsubquery (QTNode * root ,QTNode * ex ,QTNode * subs ,bool * isfind )
236265{
@@ -344,6 +373,7 @@ tsquery_rewrite_query(PG_FUNCTION_ARGS)
344373{
345374/* ready the tree for another pass */
346375QTNClearFlags (tree ,QTN_NOCHANGE );
376+ QTNTernary (tree );
347377QTNSort (tree );
348378}
349379}