]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/tsquery_rewrite.c
Set read_only = TRUE while evaluating input queries for ts_rewrite()
[postgresql] / src / backend / utils / adt / tsquery_rewrite.c
1 /*-------------------------------------------------------------------------
2  *
3  * tsquery_rewrite.c
4  *        Utilities for reconstructing tsquery
5  *
6  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  *        $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_rewrite.c,v 1.7 2007/10/24 03:30:03 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14
15 #include "postgres.h"
16
17 #include "executor/spi.h"
18 #include "tsearch/ts_type.h"
19 #include "tsearch/ts_utils.h"
20 #include "miscadmin.h"
21
22
23 static int
24 addone(int *counters, int last, int total)
25 {
26         /* since this function recurses, it could be driven to stack overflow. */
27         check_stack_depth();
28
29         counters[last]++;
30         if (counters[last] >= total)
31         {
32                 if (last == 0)
33                         return 0;
34                 if (addone(counters, last - 1, total - 1) == 0)
35                         return 0;
36                 counters[last] = counters[last - 1] + 1;
37         }
38         return 1;
39 }
40
41 /*
42  * If node is equal to ex, replace it with subs. Replacement is actually done
43  * by returning either node or a copy of subs.
44  */
45 static QTNode *
46 findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
47 {
48
49         if ((node->sign & ex->sign) != ex->sign || 
50                 node->valnode->type != ex->valnode->type)
51                 return node;
52
53         if (node->flags & QTN_NOCHANGE)
54                 return node;
55         
56         if (node->valnode->type == QI_OPR)
57         {
58                 if (node->valnode->operator.oper != ex->valnode->operator.oper)
59                         return node;
60
61                 if (node->nchild == ex->nchild)
62                 {
63                         if (QTNEq(node, ex))
64                         {
65                                 QTNFree(node);
66                                 if (subs)
67                                 {
68                                         node = QTNCopy(subs);
69                                         node->flags |= QTN_NOCHANGE;
70                                 }
71                                 else
72                                         node = NULL;
73                                 *isfind = true;
74                         }
75                 }
76                 else if (node->nchild > ex->nchild)
77                 {
78                         /*
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 
81                          * ex is B | C, we have a match after we convert tnode to
82                          * A | (B | C).
83                          */
84                         int                *counters = (int *) palloc(sizeof(int) * node->nchild);
85                         int                     i;
86                         QTNode     *tnode = (QTNode *) palloc(sizeof(QTNode));
87
88                         memset(tnode, 0, sizeof(QTNode));
89                         tnode->child = (QTNode **) palloc(sizeof(QTNode *) * ex->nchild);
90                         tnode->nchild = ex->nchild;
91                         tnode->valnode = (QueryItem *) palloc(sizeof(QueryItem));
92                         *(tnode->valnode) = *(ex->valnode);
93
94                         for (i = 0; i < ex->nchild; i++)
95                                 counters[i] = i;
96
97                         do
98                         {
99                                 tnode->sign = 0;
100                                 for (i = 0; i < ex->nchild; i++)
101                                 {
102                                         tnode->child[i] = node->child[counters[i]];
103                                         tnode->sign |= tnode->child[i]->sign;
104                                 }
105
106                                 if (QTNEq(tnode, ex))
107                                 {
108                                         int                     j = 0;
109
110                                         pfree(tnode->valnode);
111                                         pfree(tnode->child);
112                                         pfree(tnode);
113                                         if (subs)
114                                         {
115                                                 tnode = QTNCopy(subs);
116                                                 tnode->flags = QTN_NOCHANGE | QTN_NEEDFREE;
117                                         }
118                                         else
119                                                 tnode = NULL;
120
121                                         node->child[counters[0]] = tnode;
122
123                                         for (i = 1; i < ex->nchild; i++)
124                                                 node->child[counters[i]] = NULL;
125                                         for (i = 0; i < node->nchild; i++)
126                                         {
127                                                 if (node->child[i])
128                                                 {
129                                                         node->child[j] = node->child[i];
130                                                         j++;
131                                                 }
132                                         }
133
134                                         node->nchild = j;
135
136                                         *isfind = true;
137
138                                         break;
139                                 }
140                         } while (addone(counters, ex->nchild - 1, node->nchild));
141                         if (tnode && (tnode->flags & QTN_NOCHANGE) == 0)
142                         {
143                                 pfree(tnode->valnode);
144                                 pfree(tnode->child);
145                                 pfree(tnode);
146                         }
147                         else
148                                 QTNSort(node);
149                         pfree(counters);
150                 }
151         }
152         else 
153         {
154                 Assert(node->valnode->type == QI_VAL);
155
156                 if (node->valnode->operand.valcrc != ex->valnode->operand.valcrc)
157                         return node;
158                 else if (QTNEq(node, ex))
159                 {
160                         QTNFree(node);
161                         if (subs)
162                         {
163                                 node = QTNCopy(subs);
164                                 node->flags |= QTN_NOCHANGE;
165                         }
166                         else
167                         {
168                                 node = NULL;
169                         }
170                         *isfind = true;
171                 }
172         }
173
174         return node;
175 }
176
177 static QTNode *
178 dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
179 {
180         /* since this function recurses, it could be driven to stack overflow. */
181         check_stack_depth();
182
183         root = findeq(root, ex, subs, isfind);
184
185         if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
186         {
187                 int                     i;
188
189                 for (i = 0; i < root->nchild; i++)
190                         root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
191         }
192
193         return root;
194 }
195
196 static QTNode *
197 dropvoidsubtree(QTNode * root)
198 {
199
200         if (!root)
201                 return NULL;
202
203         if (root->valnode->type == QI_OPR)
204         {
205                 int                     i,
206                                         j = 0;
207
208                 for (i = 0; i < root->nchild; i++)
209                 {
210                         if (root->child[i])
211                         {
212                                 root->child[j] = root->child[i];
213                                 j++;
214                         }
215                 }
216
217                 root->nchild = j;
218
219                 if (root->valnode->operator.oper == OP_NOT && root->nchild == 0)
220                 {
221                         QTNFree(root);
222                         root = NULL;
223                 }
224                 else if (root->nchild == 1)
225                 {
226                         QTNode     *nroot = root->child[0];
227
228                         pfree(root);
229                         root = nroot;
230                 }
231         }
232
233         return root;
234 }
235
236 static QTNode *
237 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
238 {
239         bool            DidFind = false;
240
241         root = dofindsubquery(root, ex, subs, &DidFind);
242
243         if (!subs && DidFind)
244                 root = dropvoidsubtree(root);
245
246         if (isfind)
247                 *isfind = DidFind;
248
249         return root;
250 }
251
252 Datum
253 tsquery_rewrite_query(PG_FUNCTION_ARGS)
254 {
255         TSQuery         query = PG_GETARG_TSQUERY_COPY(0);
256         text       *in = PG_GETARG_TEXT_P(1);
257         TSQuery         rewrited = query;
258         MemoryContext outercontext = CurrentMemoryContext;
259         MemoryContext oldcontext;
260         QTNode     *tree;
261         char       *buf;
262         SPIPlanPtr      plan;
263         Portal          portal;
264         bool            isnull;
265         int                     i;
266
267         if (query->size == 0)
268         {
269                 PG_FREE_IF_COPY(in, 1);
270                 PG_RETURN_POINTER(rewrited);
271         }
272
273         tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
274         QTNTernary(tree);
275         QTNSort(tree);
276
277         buf = TextPGetCString(in);
278
279         SPI_connect();
280
281         if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
282                 elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
283
284         if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
285                 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
286
287         SPI_cursor_fetch(portal, true, 100);
288
289         if (SPI_tuptable == NULL ||
290                 SPI_tuptable->tupdesc->natts != 2 ||
291                 SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
292                 SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
293                 ereport(ERROR,
294                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
295                                  errmsg("ts_rewrite query must return two tsquery columns")));
296
297         while (SPI_processed > 0 && tree)
298         {
299                 for (i = 0; i < SPI_processed && tree; i++)
300                 {
301                         Datum           qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
302                         Datum           sdata;
303
304                         if (isnull)
305                                 continue;
306
307                         sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
308
309                         if (!isnull)
310                         {
311                                 TSQuery         qtex = DatumGetTSQuery(qdata);
312                                 TSQuery         qtsubs = DatumGetTSQuery(sdata);
313                                 QTNode     *qex,
314                                                    *qsubs = NULL;
315
316                                 if (qtex->size == 0)
317                                 {
318                                         if (qtex != (TSQuery) DatumGetPointer(qdata))
319                                                 pfree(qtex);
320                                         if (qtsubs != (TSQuery) DatumGetPointer(sdata))
321                                                 pfree(qtsubs);
322                                         continue;
323                                 }
324
325                                 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
326
327                                 QTNTernary(qex);
328                                 QTNSort(qex);
329
330                                 if (qtsubs->size)
331                                         qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
332
333                                 oldcontext = MemoryContextSwitchTo(outercontext);
334                                 tree = findsubquery(tree, qex, qsubs, NULL);
335                                 MemoryContextSwitchTo(oldcontext);
336
337                                 QTNFree(qex);
338                                 if (qtex != (TSQuery) DatumGetPointer(qdata))
339                                         pfree(qtex);
340                                 QTNFree(qsubs);
341                                 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
342                                         pfree(qtsubs);
343
344                                 if (tree)
345                                 {
346                                         /* ready the tree for another pass */
347                                         QTNClearFlags(tree, QTN_NOCHANGE);
348                                         QTNSort(tree);
349                                 }
350                         }
351                 }
352
353                 SPI_freetuptable(SPI_tuptable);
354                 SPI_cursor_fetch(portal, true, 100);
355         }
356
357         SPI_freetuptable(SPI_tuptable);
358         SPI_cursor_close(portal);
359         SPI_freeplan(plan);
360         SPI_finish();
361
362         if (tree)
363         {
364                 QTNBinary(tree);
365                 rewrited = QTN2QT(tree);
366                 QTNFree(tree);
367                 PG_FREE_IF_COPY(query, 0);
368         }
369         else
370         {
371                 SET_VARSIZE(rewrited, HDRSIZETQ);
372                 rewrited->size = 0;
373         }
374
375         pfree(buf);
376         PG_FREE_IF_COPY(in, 1);
377         PG_RETURN_POINTER(rewrited);
378 }
379
380 Datum
381 tsquery_rewrite(PG_FUNCTION_ARGS)
382 {
383         TSQuery         query = PG_GETARG_TSQUERY_COPY(0);
384         TSQuery         ex = PG_GETARG_TSQUERY(1);
385         TSQuery         subst = PG_GETARG_TSQUERY(2);
386         TSQuery         rewrited = query;
387         QTNode     *tree,
388                            *qex,
389                            *subs = NULL;
390
391         if (query->size == 0 || ex->size == 0)
392         {
393                 PG_FREE_IF_COPY(ex, 1);
394                 PG_FREE_IF_COPY(subst, 2);
395                 PG_RETURN_POINTER(rewrited);
396         }
397
398         tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
399         QTNTernary(tree);
400         QTNSort(tree);
401
402         qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
403         QTNTernary(qex);
404         QTNSort(qex);
405
406         if (subst->size)
407                 subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
408
409         tree = findsubquery(tree, qex, subs, NULL);
410
411         QTNFree(qex);
412         QTNFree(subs);
413
414         if (!tree)
415         {
416                 SET_VARSIZE(rewrited, HDRSIZETQ);
417                 rewrited->size = 0;
418                 PG_FREE_IF_COPY(ex, 1);
419                 PG_FREE_IF_COPY(subst, 2);
420                 PG_RETURN_POINTER(rewrited);
421         }
422         else
423         {
424                 QTNBinary(tree);
425                 rewrited = QTN2QT(tree);
426                 QTNFree(tree);
427         }
428
429         PG_FREE_IF_COPY(query, 0);
430         PG_FREE_IF_COPY(ex, 1);
431         PG_FREE_IF_COPY(subst, 2);
432         PG_RETURN_POINTER(rewrited);
433 }