21
21
#include "utils/builtins.h"
22
22
23
23
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
-
42
24
/*
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.
45
33
*/
46
34
static QTNode *
47
35
findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
48
36
{
37
+ /* Can't match unless signature matches and node type matches. */
49
38
if ((node->sign & ex->sign) != ex->sign ||
50
39
node->valnode->type != ex->valnode->type)
51
40
return node;
52
41
42
+ /* Ignore nodes marked NOCHANGE, too. */
53
43
if (node->flags & QTN_NOCHANGE)
54
44
return node;
55
45
56
46
if (node->valnode->type == QI_OPR)
57
47
{
48
+ /* Must be same operator. */
58
49
if (node->valnode->qoperator.oper != ex->valnode->qoperator.oper)
59
50
return node;
60
51
61
52
if (node->nchild == ex->nchild)
62
53
{
54
+ /*
55
+ * Simple case: when same number of children, match if equal.
56
+ * (This is reliable when the children were sorted earlier.)
57
+ */
63
58
if (QTNEq(node, ex))
64
59
{
60
+ /* Match; delete node and return a copy of subs instead. */
65
61
QTNFree(node);
66
62
if (subs)
67
63
{
@@ -73,79 +69,92 @@ findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
73
69
*isfind = true;
74
70
}
75
71
}
76
- else if (node->nchild > ex->nchild)
72
+ else if (node->nchild > ex->nchild && ex->nchild > 0 )
77
73
{
78
74
/*
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.
82
84
*/
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)
97
99
{
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)
100
103
{
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++;
103
108
}
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
+ }
104
120
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++)
106
126
{
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]);
117
129
else
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
+ }
132
132
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
+ }
134
140
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);
136
153
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;
145
155
}
146
- else
147
- QTNSort(node);
148
- pfree(counters);
156
+
157
+ pfree(matched);
149
158
}
150
159
}
151
160
else
@@ -173,12 +182,20 @@ findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
173
182
return node;
174
183
}
175
184
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
+ */
176
190
static QTNode *
177
191
dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
178
192
{
179
193
/* since this function recurses, it could be driven to stack overflow. */
180
194
check_stack_depth();
181
195
196
+ /* also, since it's a bit expensive, let's check for query cancel. */
197
+ CHECK_FOR_INTERRUPTS();
198
+
182
199
root = findeq(root, ex, subs, isfind);
183
200
184
201
if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
@@ -192,6 +209,10 @@ dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
192
209
return root;
193
210
}
194
211
212
+ /*
213
+ * Delete any void subtrees that may have been inserted when the replacement
214
+ * subtree is void.
215
+ */
195
216
static QTNode *
196
217
dropvoidsubtree(QTNode *root)
197
218
{
@@ -231,6 +252,14 @@ dropvoidsubtree(QTNode *root)
231
252
return root;
232
253
}
233
254
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
+ */
234
263
QTNode *
235
264
findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
236
265
{
@@ -344,6 +373,7 @@ tsquery_rewrite_query(PG_FUNCTION_ARGS)
344
373
{
345
374
/* ready the tree for another pass */
346
375
QTNClearFlags(tree, QTN_NOCHANGE);
376
+ QTNTernary(tree);
347
377
QTNSort(tree);
348
378
}
349
379
}
0 commit comments