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