Header And Logo

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

tsquery_cleanup.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * tsquery_cleanup.c
00004  *   Cleanup query from NOT values and/or stopword
00005  *   Utility functions to correct work.
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/utils/adt/tsquery_cleanup.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 
00016 #include "postgres.h"
00017 
00018 #include "tsearch/ts_utils.h"
00019 #include "miscadmin.h"
00020 
00021 typedef struct NODE
00022 {
00023     struct NODE *left;
00024     struct NODE *right;
00025     QueryItem  *valnode;
00026 } NODE;
00027 
00028 /*
00029  * make query tree from plain view of query
00030  */
00031 static NODE *
00032 maketree(QueryItem *in)
00033 {
00034     NODE       *node = (NODE *) palloc(sizeof(NODE));
00035 
00036     node->valnode = in;
00037     node->right = node->left = NULL;
00038     if (in->type == QI_OPR)
00039     {
00040         node->right = maketree(in + 1);
00041         if (in->qoperator.oper != OP_NOT)
00042             node->left = maketree(in + in->qoperator.left);
00043     }
00044     return node;
00045 }
00046 
00047 /*
00048  * Internal state for plaintree and plainnode
00049  */
00050 typedef struct
00051 {
00052     QueryItem  *ptr;
00053     int         len;            /* allocated size of ptr */
00054     int         cur;            /* number of elements in ptr */
00055 } PLAINTREE;
00056 
00057 static void
00058 plainnode(PLAINTREE *state, NODE *node)
00059 {
00060     /* since this function recurses, it could be driven to stack overflow. */
00061     check_stack_depth();
00062 
00063     if (state->cur == state->len)
00064     {
00065         state->len *= 2;
00066         state->ptr = (QueryItem *) repalloc((void *) state->ptr, state->len * sizeof(QueryItem));
00067     }
00068     memcpy((void *) &(state->ptr[state->cur]), (void *) node->valnode, sizeof(QueryItem));
00069     if (node->valnode->type == QI_VAL)
00070         state->cur++;
00071     else if (node->valnode->qoperator.oper == OP_NOT)
00072     {
00073         state->ptr[state->cur].qoperator.left = 1;
00074         state->cur++;
00075         plainnode(state, node->right);
00076     }
00077     else
00078     {
00079         int         cur = state->cur;
00080 
00081         state->cur++;
00082         plainnode(state, node->right);
00083         state->ptr[cur].qoperator.left = state->cur - cur;
00084         plainnode(state, node->left);
00085     }
00086     pfree(node);
00087 }
00088 
00089 /*
00090  * make plain view of tree from a NODE-tree representation
00091  */
00092 static QueryItem *
00093 plaintree(NODE *root, int *len)
00094 {
00095     PLAINTREE   pl;
00096 
00097     pl.cur = 0;
00098     pl.len = 16;
00099     if (root && (root->valnode->type == QI_VAL || root->valnode->type == QI_OPR))
00100     {
00101         pl.ptr = (QueryItem *) palloc(pl.len * sizeof(QueryItem));
00102         plainnode(&pl, root);
00103     }
00104     else
00105         pl.ptr = NULL;
00106     *len = pl.cur;
00107     return pl.ptr;
00108 }
00109 
00110 static void
00111 freetree(NODE *node)
00112 {
00113     /* since this function recurses, it could be driven to stack overflow. */
00114     check_stack_depth();
00115 
00116     if (!node)
00117         return;
00118     if (node->left)
00119         freetree(node->left);
00120     if (node->right)
00121         freetree(node->right);
00122     pfree(node);
00123 }
00124 
00125 /*
00126  * clean tree for ! operator.
00127  * It's useful for debug, but in
00128  * other case, such view is used with search in index.
00129  * Operator ! always return TRUE
00130  */
00131 static NODE *
00132 clean_NOT_intree(NODE *node)
00133 {
00134     /* since this function recurses, it could be driven to stack overflow. */
00135     check_stack_depth();
00136 
00137     if (node->valnode->type == QI_VAL)
00138         return node;
00139 
00140     if (node->valnode->qoperator.oper == OP_NOT)
00141     {
00142         freetree(node);
00143         return NULL;
00144     }
00145 
00146     /* operator & or | */
00147     if (node->valnode->qoperator.oper == OP_OR)
00148     {
00149         if ((node->left = clean_NOT_intree(node->left)) == NULL ||
00150             (node->right = clean_NOT_intree(node->right)) == NULL)
00151         {
00152             freetree(node);
00153             return NULL;
00154         }
00155     }
00156     else
00157     {
00158         NODE       *res = node;
00159 
00160         Assert(node->valnode->qoperator.oper == OP_AND);
00161 
00162         node->left = clean_NOT_intree(node->left);
00163         node->right = clean_NOT_intree(node->right);
00164         if (node->left == NULL && node->right == NULL)
00165         {
00166             pfree(node);
00167             res = NULL;
00168         }
00169         else if (node->left == NULL)
00170         {
00171             res = node->right;
00172             pfree(node);
00173         }
00174         else if (node->right == NULL)
00175         {
00176             res = node->left;
00177             pfree(node);
00178         }
00179         return res;
00180     }
00181     return node;
00182 }
00183 
00184 QueryItem *
00185 clean_NOT(QueryItem *ptr, int *len)
00186 {
00187     NODE       *root = maketree(ptr);
00188 
00189     return plaintree(clean_NOT_intree(root), len);
00190 }
00191 
00192 
00193 #ifdef V_UNKNOWN                /* exists in Windows headers */
00194 #undef V_UNKNOWN
00195 #endif
00196 #ifdef V_FALSE                  /* exists in Solaris headers */
00197 #undef V_FALSE
00198 #endif
00199 
00200 /*
00201  * output values for result output parameter of clean_fakeval_intree
00202  */
00203 #define V_UNKNOWN   0           /* the expression can't be evaluated
00204                                  * statically */
00205 #define V_TRUE      1           /* the expression is always true (not
00206                                  * implemented) */
00207 #define V_FALSE     2           /* the expression is always false (not
00208                                  * implemented) */
00209 #define V_STOP      3           /* the expression is a stop word */
00210 
00211 /*
00212  * Clean query tree from values which is always in
00213  * text (stopword)
00214  */
00215 static NODE *
00216 clean_fakeval_intree(NODE *node, char *result)
00217 {
00218     char        lresult = V_UNKNOWN,
00219                 rresult = V_UNKNOWN;
00220 
00221     /* since this function recurses, it could be driven to stack overflow. */
00222     check_stack_depth();
00223 
00224     if (node->valnode->type == QI_VAL)
00225         return node;
00226     else if (node->valnode->type == QI_VALSTOP)
00227     {
00228         pfree(node);
00229         *result = V_STOP;
00230         return NULL;
00231     }
00232 
00233     Assert(node->valnode->type == QI_OPR);
00234 
00235     if (node->valnode->qoperator.oper == OP_NOT)
00236     {
00237         node->right = clean_fakeval_intree(node->right, &rresult);
00238         if (!node->right)
00239         {
00240             *result = V_STOP;
00241             freetree(node);
00242             return NULL;
00243         }
00244     }
00245     else
00246     {
00247         NODE       *res = node;
00248 
00249         node->left = clean_fakeval_intree(node->left, &lresult);
00250         node->right = clean_fakeval_intree(node->right, &rresult);
00251 
00252         if (lresult == V_STOP && rresult == V_STOP)
00253         {
00254             freetree(node);
00255             *result = V_STOP;
00256             return NULL;
00257         }
00258         else if (lresult == V_STOP)
00259         {
00260             res = node->right;
00261             pfree(node);
00262         }
00263         else if (rresult == V_STOP)
00264         {
00265             res = node->left;
00266             pfree(node);
00267         }
00268         return res;
00269     }
00270     return node;
00271 }
00272 
00273 QueryItem *
00274 clean_fakeval(QueryItem *ptr, int *len)
00275 {
00276     NODE       *root = maketree(ptr);
00277     char        result = V_UNKNOWN;
00278     NODE       *resroot;
00279 
00280     resroot = clean_fakeval_intree(root, &result);
00281     if (result != V_UNKNOWN)
00282     {
00283         ereport(NOTICE,
00284                 (errmsg("text-search query contains only stop words or doesn't contain lexemes, ignored")));
00285         *len = 0;
00286         return NULL;
00287     }
00288 
00289     return plaintree(resroot, len);
00290 }