Header And Logo

PostgreSQL
| The world's most advanced open source database.

tsquery_rewrite.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * tsquery_rewrite.c
00004  *    Utilities for reconstructing tsquery
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  *
00008  *
00009  * IDENTIFICATION
00010  *    src/backend/utils/adt/tsquery_rewrite.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 
00015 #include "postgres.h"
00016 
00017 #include "catalog/pg_type.h"
00018 #include "executor/spi.h"
00019 #include "miscadmin.h"
00020 #include "tsearch/ts_utils.h"
00021 #include "utils/builtins.h"
00022 
00023 
00024 static int
00025 addone(int *counters, int last, int total)
00026 {
00027     /* since this function recurses, it could be driven to stack overflow. */
00028     check_stack_depth();
00029 
00030     counters[last]++;
00031     if (counters[last] >= total)
00032     {
00033         if (last == 0)
00034             return 0;
00035         if (addone(counters, last - 1, total - 1) == 0)
00036             return 0;
00037         counters[last] = counters[last - 1] + 1;
00038     }
00039     return 1;
00040 }
00041 
00042 /*
00043  * If node is equal to ex, replace it with subs. Replacement is actually done
00044  * by returning either node or a copy of subs.
00045  */
00046 static QTNode *
00047 findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
00048 {
00049 
00050     if ((node->sign & ex->sign) != ex->sign ||
00051         node->valnode->type != ex->valnode->type)
00052         return node;
00053 
00054     if (node->flags & QTN_NOCHANGE)
00055         return node;
00056 
00057     if (node->valnode->type == QI_OPR)
00058     {
00059         if (node->valnode->qoperator.oper != ex->valnode->qoperator.oper)
00060             return node;
00061 
00062         if (node->nchild == ex->nchild)
00063         {
00064             if (QTNEq(node, ex))
00065             {
00066                 QTNFree(node);
00067                 if (subs)
00068                 {
00069                     node = QTNCopy(subs);
00070                     node->flags |= QTN_NOCHANGE;
00071                 }
00072                 else
00073                     node = NULL;
00074                 *isfind = true;
00075             }
00076         }
00077         else if (node->nchild > ex->nchild)
00078         {
00079             /*
00080              * AND and NOT are commutative, so we check if a subset of the
00081              * children match. For example, if tnode is A | B | C, and ex is B
00082              * | C, we have a match after we convert tnode to A | (B | C).
00083              */
00084             int        *counters = (int *) palloc(sizeof(int) * node->nchild);
00085             int         i;
00086             QTNode     *tnode = (QTNode *) palloc(sizeof(QTNode));
00087 
00088             memset(tnode, 0, sizeof(QTNode));
00089             tnode->child = (QTNode **) palloc(sizeof(QTNode *) * ex->nchild);
00090             tnode->nchild = ex->nchild;
00091             tnode->valnode = (QueryItem *) palloc(sizeof(QueryItem));
00092             *(tnode->valnode) = *(ex->valnode);
00093 
00094             for (i = 0; i < ex->nchild; i++)
00095                 counters[i] = i;
00096 
00097             do
00098             {
00099                 tnode->sign = 0;
00100                 for (i = 0; i < ex->nchild; i++)
00101                 {
00102                     tnode->child[i] = node->child[counters[i]];
00103                     tnode->sign |= tnode->child[i]->sign;
00104                 }
00105 
00106                 if (QTNEq(tnode, ex))
00107                 {
00108                     int         j = 0;
00109 
00110                     pfree(tnode->valnode);
00111                     pfree(tnode->child);
00112                     pfree(tnode);
00113                     if (subs)
00114                     {
00115                         tnode = QTNCopy(subs);
00116                         tnode->flags = QTN_NOCHANGE | QTN_NEEDFREE;
00117                     }
00118                     else
00119                         tnode = NULL;
00120 
00121                     node->child[counters[0]] = tnode;
00122 
00123                     for (i = 1; i < ex->nchild; i++)
00124                         node->child[counters[i]] = NULL;
00125                     for (i = 0; i < node->nchild; i++)
00126                     {
00127                         if (node->child[i])
00128                         {
00129                             node->child[j] = node->child[i];
00130                             j++;
00131                         }
00132                     }
00133 
00134                     node->nchild = j;
00135 
00136                     *isfind = true;
00137 
00138                     break;
00139                 }
00140             } while (addone(counters, ex->nchild - 1, node->nchild));
00141             if (tnode && (tnode->flags & QTN_NOCHANGE) == 0)
00142             {
00143                 pfree(tnode->valnode);
00144                 pfree(tnode->child);
00145                 pfree(tnode);
00146             }
00147             else
00148                 QTNSort(node);
00149             pfree(counters);
00150         }
00151     }
00152     else
00153     {
00154         Assert(node->valnode->type == QI_VAL);
00155 
00156         if (node->valnode->qoperand.valcrc != ex->valnode->qoperand.valcrc)
00157             return node;
00158         else if (QTNEq(node, ex))
00159         {
00160             QTNFree(node);
00161             if (subs)
00162             {
00163                 node = QTNCopy(subs);
00164                 node->flags |= QTN_NOCHANGE;
00165             }
00166             else
00167             {
00168                 node = NULL;
00169             }
00170             *isfind = true;
00171         }
00172     }
00173 
00174     return node;
00175 }
00176 
00177 static QTNode *
00178 dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
00179 {
00180     /* since this function recurses, it could be driven to stack overflow. */
00181     check_stack_depth();
00182 
00183     root = findeq(root, ex, subs, isfind);
00184 
00185     if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
00186     {
00187         int         i;
00188 
00189         for (i = 0; i < root->nchild; i++)
00190             root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
00191     }
00192 
00193     return root;
00194 }
00195 
00196 static QTNode *
00197 dropvoidsubtree(QTNode *root)
00198 {
00199 
00200     if (!root)
00201         return NULL;
00202 
00203     if (root->valnode->type == QI_OPR)
00204     {
00205         int         i,
00206                     j = 0;
00207 
00208         for (i = 0; i < root->nchild; i++)
00209         {
00210             if (root->child[i])
00211             {
00212                 root->child[j] = root->child[i];
00213                 j++;
00214             }
00215         }
00216 
00217         root->nchild = j;
00218 
00219         if (root->nchild == 0)
00220         {
00221             QTNFree(root);
00222             root = NULL;
00223         }
00224         else if (root->nchild == 1 && root->valnode->qoperator.oper != OP_NOT)
00225         {
00226             QTNode     *nroot = root->child[0];
00227 
00228             pfree(root);
00229             root = nroot;
00230         }
00231     }
00232 
00233     return root;
00234 }
00235 
00236 QTNode *
00237 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
00238 {
00239     bool        DidFind = false;
00240 
00241     root = dofindsubquery(root, ex, subs, &DidFind);
00242 
00243     if (!subs && DidFind)
00244         root = dropvoidsubtree(root);
00245 
00246     if (isfind)
00247         *isfind = DidFind;
00248 
00249     return root;
00250 }
00251 
00252 Datum
00253 tsquery_rewrite_query(PG_FUNCTION_ARGS)
00254 {
00255     TSQuery     query = PG_GETARG_TSQUERY_COPY(0);
00256     text       *in = PG_GETARG_TEXT_P(1);
00257     TSQuery     rewrited = query;
00258     MemoryContext outercontext = CurrentMemoryContext;
00259     MemoryContext oldcontext;
00260     QTNode     *tree;
00261     char       *buf;
00262     SPIPlanPtr  plan;
00263     Portal      portal;
00264     bool        isnull;
00265     int         i;
00266 
00267     if (query->size == 0)
00268     {
00269         PG_FREE_IF_COPY(in, 1);
00270         PG_RETURN_POINTER(rewrited);
00271     }
00272 
00273     tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
00274     QTNTernary(tree);
00275     QTNSort(tree);
00276 
00277     buf = text_to_cstring(in);
00278 
00279     SPI_connect();
00280 
00281     if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
00282         elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
00283 
00284     if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
00285         elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
00286 
00287     SPI_cursor_fetch(portal, true, 100);
00288 
00289     if (SPI_tuptable == NULL ||
00290         SPI_tuptable->tupdesc->natts != 2 ||
00291         SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
00292         SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
00293         ereport(ERROR,
00294                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00295                  errmsg("ts_rewrite query must return two tsquery columns")));
00296 
00297     while (SPI_processed > 0 && tree)
00298     {
00299         for (i = 0; i < SPI_processed && tree; i++)
00300         {
00301             Datum       qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
00302             Datum       sdata;
00303 
00304             if (isnull)
00305                 continue;
00306 
00307             sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
00308 
00309             if (!isnull)
00310             {
00311                 TSQuery     qtex = DatumGetTSQuery(qdata);
00312                 TSQuery     qtsubs = DatumGetTSQuery(sdata);
00313                 QTNode     *qex,
00314                            *qsubs = NULL;
00315 
00316                 if (qtex->size == 0)
00317                 {
00318                     if (qtex != (TSQuery) DatumGetPointer(qdata))
00319                         pfree(qtex);
00320                     if (qtsubs != (TSQuery) DatumGetPointer(sdata))
00321                         pfree(qtsubs);
00322                     continue;
00323                 }
00324 
00325                 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
00326 
00327                 QTNTernary(qex);
00328                 QTNSort(qex);
00329 
00330                 if (qtsubs->size)
00331                     qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
00332 
00333                 oldcontext = MemoryContextSwitchTo(outercontext);
00334                 tree = findsubquery(tree, qex, qsubs, NULL);
00335                 MemoryContextSwitchTo(oldcontext);
00336 
00337                 QTNFree(qex);
00338                 if (qtex != (TSQuery) DatumGetPointer(qdata))
00339                     pfree(qtex);
00340                 QTNFree(qsubs);
00341                 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
00342                     pfree(qtsubs);
00343 
00344                 if (tree)
00345                 {
00346                     /* ready the tree for another pass */
00347                     QTNClearFlags(tree, QTN_NOCHANGE);
00348                     QTNSort(tree);
00349                 }
00350             }
00351         }
00352 
00353         SPI_freetuptable(SPI_tuptable);
00354         SPI_cursor_fetch(portal, true, 100);
00355     }
00356 
00357     SPI_freetuptable(SPI_tuptable);
00358     SPI_cursor_close(portal);
00359     SPI_freeplan(plan);
00360     SPI_finish();
00361 
00362     if (tree)
00363     {
00364         QTNBinary(tree);
00365         rewrited = QTN2QT(tree);
00366         QTNFree(tree);
00367         PG_FREE_IF_COPY(query, 0);
00368     }
00369     else
00370     {
00371         SET_VARSIZE(rewrited, HDRSIZETQ);
00372         rewrited->size = 0;
00373     }
00374 
00375     pfree(buf);
00376     PG_FREE_IF_COPY(in, 1);
00377     PG_RETURN_POINTER(rewrited);
00378 }
00379 
00380 Datum
00381 tsquery_rewrite(PG_FUNCTION_ARGS)
00382 {
00383     TSQuery     query = PG_GETARG_TSQUERY_COPY(0);
00384     TSQuery     ex = PG_GETARG_TSQUERY(1);
00385     TSQuery     subst = PG_GETARG_TSQUERY(2);
00386     TSQuery     rewrited = query;
00387     QTNode     *tree,
00388                *qex,
00389                *subs = NULL;
00390 
00391     if (query->size == 0 || ex->size == 0)
00392     {
00393         PG_FREE_IF_COPY(ex, 1);
00394         PG_FREE_IF_COPY(subst, 2);
00395         PG_RETURN_POINTER(rewrited);
00396     }
00397 
00398     tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
00399     QTNTernary(tree);
00400     QTNSort(tree);
00401 
00402     qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
00403     QTNTernary(qex);
00404     QTNSort(qex);
00405 
00406     if (subst->size)
00407         subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
00408 
00409     tree = findsubquery(tree, qex, subs, NULL);
00410 
00411     QTNFree(qex);
00412     QTNFree(subs);
00413 
00414     if (!tree)
00415     {
00416         SET_VARSIZE(rewrited, HDRSIZETQ);
00417         rewrited->size = 0;
00418         PG_FREE_IF_COPY(ex, 1);
00419         PG_FREE_IF_COPY(subst, 2);
00420         PG_RETURN_POINTER(rewrited);
00421     }
00422     else
00423     {
00424         QTNBinary(tree);
00425         rewrited = QTN2QT(tree);
00426         QTNFree(tree);
00427     }
00428 
00429     PG_FREE_IF_COPY(query, 0);
00430     PG_FREE_IF_COPY(ex, 1);
00431     PG_FREE_IF_COPY(subst, 2);
00432     PG_RETURN_POINTER(rewrited);
00433 }