]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/tsquery_rewrite.c
Update copyright for 2014
[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-2014, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  *        src/backend/utils/adt/tsquery_rewrite.c
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_utils.h"
21 #include "utils/builtins.h"
22
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 /*
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.
45  */
46 static QTNode *
47 findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
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->qoperator.oper != ex->valnode->qoperator.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->qoperand.valcrc != ex->valnode->qoperand.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         if (!root)
199                 return NULL;
200
201         if (root->valnode->type == QI_OPR)
202         {
203                 int                     i,
204                                         j = 0;
205
206                 for (i = 0; i < root->nchild; i++)
207                 {
208                         if (root->child[i])
209                         {
210                                 root->child[j] = root->child[i];
211                                 j++;
212                         }
213                 }
214
215                 root->nchild = j;
216
217                 if (root->nchild == 0)
218                 {
219                         QTNFree(root);
220                         root = NULL;
221                 }
222                 else if (root->nchild == 1 && root->valnode->qoperator.oper != OP_NOT)
223                 {
224                         QTNode     *nroot = root->child[0];
225
226                         pfree(root);
227                         root = nroot;
228                 }
229         }
230
231         return root;
232 }
233
234 QTNode *
235 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
236 {
237         bool            DidFind = false;
238
239         root = dofindsubquery(root, ex, subs, &DidFind);
240
241         if (!subs && DidFind)
242                 root = dropvoidsubtree(root);
243
244         if (isfind)
245                 *isfind = DidFind;
246
247         return root;
248 }
249
250 Datum
251 tsquery_rewrite_query(PG_FUNCTION_ARGS)
252 {
253         TSQuery         query = PG_GETARG_TSQUERY_COPY(0);
254         text       *in = PG_GETARG_TEXT_P(1);
255         TSQuery         rewrited = query;
256         MemoryContext outercontext = CurrentMemoryContext;
257         MemoryContext oldcontext;
258         QTNode     *tree;
259         char       *buf;
260         SPIPlanPtr      plan;
261         Portal          portal;
262         bool            isnull;
263         int                     i;
264
265         if (query->size == 0)
266         {
267                 PG_FREE_IF_COPY(in, 1);
268                 PG_RETURN_POINTER(rewrited);
269         }
270
271         tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
272         QTNTernary(tree);
273         QTNSort(tree);
274
275         buf = text_to_cstring(in);
276
277         SPI_connect();
278
279         if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
280                 elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
281
282         if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
283                 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
284
285         SPI_cursor_fetch(portal, true, 100);
286
287         if (SPI_tuptable == NULL ||
288                 SPI_tuptable->tupdesc->natts != 2 ||
289                 SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
290                 SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
291                 ereport(ERROR,
292                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
293                                  errmsg("ts_rewrite query must return two tsquery columns")));
294
295         while (SPI_processed > 0 && tree)
296         {
297                 for (i = 0; i < SPI_processed && tree; i++)
298                 {
299                         Datum           qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
300                         Datum           sdata;
301
302                         if (isnull)
303                                 continue;
304
305                         sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
306
307                         if (!isnull)
308                         {
309                                 TSQuery         qtex = DatumGetTSQuery(qdata);
310                                 TSQuery         qtsubs = DatumGetTSQuery(sdata);
311                                 QTNode     *qex,
312                                                    *qsubs = NULL;
313
314                                 if (qtex->size == 0)
315                                 {
316                                         if (qtex != (TSQuery) DatumGetPointer(qdata))
317                                                 pfree(qtex);
318                                         if (qtsubs != (TSQuery) DatumGetPointer(sdata))
319                                                 pfree(qtsubs);
320                                         continue;
321                                 }
322
323                                 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
324
325                                 QTNTernary(qex);
326                                 QTNSort(qex);
327
328                                 if (qtsubs->size)
329                                         qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
330
331                                 oldcontext = MemoryContextSwitchTo(outercontext);
332                                 tree = findsubquery(tree, qex, qsubs, NULL);
333                                 MemoryContextSwitchTo(oldcontext);
334
335                                 QTNFree(qex);
336                                 if (qtex != (TSQuery) DatumGetPointer(qdata))
337                                         pfree(qtex);
338                                 QTNFree(qsubs);
339                                 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
340                                         pfree(qtsubs);
341
342                                 if (tree)
343                                 {
344                                         /* ready the tree for another pass */
345                                         QTNClearFlags(tree, QTN_NOCHANGE);
346                                         QTNSort(tree);
347                                 }
348                         }
349                 }
350
351                 SPI_freetuptable(SPI_tuptable);
352                 SPI_cursor_fetch(portal, true, 100);
353         }
354
355         SPI_freetuptable(SPI_tuptable);
356         SPI_cursor_close(portal);
357         SPI_freeplan(plan);
358         SPI_finish();
359
360         if (tree)
361         {
362                 QTNBinary(tree);
363                 rewrited = QTN2QT(tree);
364                 QTNFree(tree);
365                 PG_FREE_IF_COPY(query, 0);
366         }
367         else
368         {
369                 SET_VARSIZE(rewrited, HDRSIZETQ);
370                 rewrited->size = 0;
371         }
372
373         pfree(buf);
374         PG_FREE_IF_COPY(in, 1);
375         PG_RETURN_POINTER(rewrited);
376 }
377
378 Datum
379 tsquery_rewrite(PG_FUNCTION_ARGS)
380 {
381         TSQuery         query = PG_GETARG_TSQUERY_COPY(0);
382         TSQuery         ex = PG_GETARG_TSQUERY(1);
383         TSQuery         subst = PG_GETARG_TSQUERY(2);
384         TSQuery         rewrited = query;
385         QTNode     *tree,
386                            *qex,
387                            *subs = NULL;
388
389         if (query->size == 0 || ex->size == 0)
390         {
391                 PG_FREE_IF_COPY(ex, 1);
392                 PG_FREE_IF_COPY(subst, 2);
393                 PG_RETURN_POINTER(rewrited);
394         }
395
396         tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
397         QTNTernary(tree);
398         QTNSort(tree);
399
400         qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
401         QTNTernary(qex);
402         QTNSort(qex);
403
404         if (subst->size)
405                 subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
406
407         tree = findsubquery(tree, qex, subs, NULL);
408
409         QTNFree(qex);
410         QTNFree(subs);
411
412         if (!tree)
413         {
414                 SET_VARSIZE(rewrited, HDRSIZETQ);
415                 rewrited->size = 0;
416                 PG_FREE_IF_COPY(ex, 1);
417                 PG_FREE_IF_COPY(subst, 2);
418                 PG_RETURN_POINTER(rewrited);
419         }
420         else
421         {
422                 QTNBinary(tree);
423                 rewrited = QTN2QT(tree);
424                 QTNFree(tree);
425         }
426
427         PG_FREE_IF_COPY(query, 0);
428         PG_FREE_IF_COPY(ex, 1);
429         PG_FREE_IF_COPY(subst, 2);
430         PG_RETURN_POINTER(rewrited);
431 }