]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/tsquery_rewrite.c
Update copyright for 2009.
[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-2009, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  *        $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_rewrite.c,v 1.13 2009/01/01 17:23:50 momjian 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 ex is B
81                          * | C, we have a match after we convert tnode to A | (B | C).
82                          */
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
97                         {
98                                 tnode->sign = 0;
99                                 for (i = 0; i < ex->nchild; i++)
100                                 {
101                                         tnode->child[i] = node->child[counters[i]];
102                                         tnode->sign |= tnode->child[i]->sign;
103                                 }
104
105                                 if (QTNEq(tnode, ex))
106                                 {
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                                         }
117                                         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                                         }
132
133                                         node->nchild = j;
134
135                                         *isfind = true;
136
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);
145                         }
146                         else
147                                 QTNSort(node);
148                         pfree(counters);
149                 }
150         }
151         else
152         {
153                 Assert(node->valnode->type == QI_VAL);
154
155                 if (node->valnode->operand.valcrc != ex->valnode->operand.valcrc)
156                         return node;
157                 else if (QTNEq(node, ex))
158                 {
159                         QTNFree(node);
160                         if (subs)
161                         {
162                                 node = QTNCopy(subs);
163                                 node->flags |= QTN_NOCHANGE;
164                         }
165                         else
166                         {
167                                 node = NULL;
168                         }
169                         *isfind = true;
170                 }
171         }
172
173         return node;
174 }
175
176 static QTNode *
177 dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
178 {
179         /* since this function recurses, it could be driven to stack overflow. */
180         check_stack_depth();
181
182         root = findeq(root, ex, subs, isfind);
183
184         if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
185         {
186                 int                     i;
187
188                 for (i = 0; i < root->nchild; i++)
189                         root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
190         }
191
192         return root;
193 }
194
195 static QTNode *
196 dropvoidsubtree(QTNode *root)
197 {
198
199         if (!root)
200                 return NULL;
201
202         if (root->valnode->type == QI_OPR)
203         {
204                 int                     i,
205                                         j = 0;
206
207                 for (i = 0; i < root->nchild; i++)
208                 {
209                         if (root->child[i])
210                         {
211                                 root->child[j] = root->child[i];
212                                 j++;
213                         }
214                 }
215
216                 root->nchild = j;
217
218                 if (root->valnode->operator.oper == OP_NOT && root->nchild == 0)
219                 {
220                         QTNFree(root);
221                         root = NULL;
222                 }
223                 else if (root->nchild == 1)
224                 {
225                         QTNode     *nroot = root->child[0];
226
227                         pfree(root);
228                         root = nroot;
229                 }
230         }
231
232         return root;
233 }
234
235 QTNode *
236 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
237 {
238         bool            DidFind = false;
239
240         root = dofindsubquery(root, ex, subs, &DidFind);
241
242         if (!subs && DidFind)
243                 root = dropvoidsubtree(root);
244
245         if (isfind)
246                 *isfind = DidFind;
247
248         return root;
249 }
250
251 Datum
252 tsquery_rewrite_query(PG_FUNCTION_ARGS)
253 {
254         TSQuery         query = PG_GETARG_TSQUERY_COPY(0);
255         text       *in = PG_GETARG_TEXT_P(1);
256         TSQuery         rewrited = query;
257         MemoryContext outercontext = CurrentMemoryContext;
258         MemoryContext oldcontext;
259         QTNode     *tree;
260         char       *buf;
261         SPIPlanPtr      plan;
262         Portal          portal;
263         bool            isnull;
264         int                     i;
265
266         if (query->size == 0)
267         {
268                 PG_FREE_IF_COPY(in, 1);
269                 PG_RETURN_POINTER(rewrited);
270         }
271
272         tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
273         QTNTernary(tree);
274         QTNSort(tree);
275
276         buf = text_to_cstring(in);
277
278         SPI_connect();
279
280         if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
281                 elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
282
283         if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
284                 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
285
286         SPI_cursor_fetch(portal, true, 100);
287
288         if (SPI_tuptable == NULL ||
289                 SPI_tuptable->tupdesc->natts != 2 ||
290                 SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
291                 SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
292                 ereport(ERROR,
293                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
294                                  errmsg("ts_rewrite query must return two tsquery columns")));
295
296         while (SPI_processed > 0 && tree)
297         {
298                 for (i = 0; i < SPI_processed && tree; i++)
299                 {
300                         Datum           qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
301                         Datum           sdata;
302
303                         if (isnull)
304                                 continue;
305
306                         sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
307
308                         if (!isnull)
309                         {
310                                 TSQuery         qtex = DatumGetTSQuery(qdata);
311                                 TSQuery         qtsubs = DatumGetTSQuery(sdata);
312                                 QTNode     *qex,
313                                                    *qsubs = NULL;
314
315                                 if (qtex->size == 0)
316                                 {
317                                         if (qtex != (TSQuery) DatumGetPointer(qdata))
318                                                 pfree(qtex);
319                                         if (qtsubs != (TSQuery) DatumGetPointer(sdata))
320                                                 pfree(qtsubs);
321                                         continue;
322                                 }
323
324                                 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
325
326                                 QTNTernary(qex);
327                                 QTNSort(qex);
328
329                                 if (qtsubs->size)
330                                         qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
331
332                                 oldcontext = MemoryContextSwitchTo(outercontext);
333                                 tree = findsubquery(tree, qex, qsubs, NULL);
334                                 MemoryContextSwitchTo(oldcontext);
335
336                                 QTNFree(qex);
337                                 if (qtex != (TSQuery) DatumGetPointer(qdata))
338                                         pfree(qtex);
339                                 QTNFree(qsubs);
340                                 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
341                                         pfree(qtsubs);
342
343                                 if (tree)
344                                 {
345                                         /* ready the tree for another pass */
346                                         QTNClearFlags(tree, QTN_NOCHANGE);
347                                         QTNSort(tree);
348                                 }
349                         }
350                 }
351
352                 SPI_freetuptable(SPI_tuptable);
353                 SPI_cursor_fetch(portal, true, 100);
354         }
355
356         SPI_freetuptable(SPI_tuptable);
357         SPI_cursor_close(portal);
358         SPI_freeplan(plan);
359         SPI_finish();
360
361         if (tree)
362         {
363                 QTNBinary(tree);
364                 rewrited = QTN2QT(tree);
365                 QTNFree(tree);
366                 PG_FREE_IF_COPY(query, 0);
367         }
368         else
369         {
370                 SET_VARSIZE(rewrited, HDRSIZETQ);
371                 rewrited->size = 0;
372         }
373
374         pfree(buf);
375         PG_FREE_IF_COPY(in, 1);
376         PG_RETURN_POINTER(rewrited);
377 }
378
379 Datum
380 tsquery_rewrite(PG_FUNCTION_ARGS)
381 {
382         TSQuery         query = PG_GETARG_TSQUERY_COPY(0);
383         TSQuery         ex = PG_GETARG_TSQUERY(1);
384         TSQuery         subst = PG_GETARG_TSQUERY(2);
385         TSQuery         rewrited = query;
386         QTNode     *tree,
387                            *qex,
388                            *subs = NULL;
389
390         if (query->size == 0 || ex->size == 0)
391         {
392                 PG_FREE_IF_COPY(ex, 1);
393                 PG_FREE_IF_COPY(subst, 2);
394                 PG_RETURN_POINTER(rewrited);
395         }
396
397         tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
398         QTNTernary(tree);
399         QTNSort(tree);
400
401         qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
402         QTNTernary(qex);
403         QTNSort(qex);
404
405         if (subst->size)
406                 subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
407
408         tree = findsubquery(tree, qex, subs, NULL);
409
410         QTNFree(qex);
411         QTNFree(subs);
412
413         if (!tree)
414         {
415                 SET_VARSIZE(rewrited, HDRSIZETQ);
416                 rewrited->size = 0;
417                 PG_FREE_IF_COPY(ex, 1);
418                 PG_FREE_IF_COPY(subst, 2);
419                 PG_RETURN_POINTER(rewrited);
420         }
421         else
422         {
423                 QTNBinary(tree);
424                 rewrited = QTN2QT(tree);
425                 QTNFree(tree);
426         }
427
428         PG_FREE_IF_COPY(query, 0);
429         PG_FREE_IF_COPY(ex, 1);
430         PG_FREE_IF_COPY(subst, 2);
431         PG_RETURN_POINTER(rewrited);
432 }