]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/tsquery_cleanup.c
Prevent stack overflow in query-type functions.
[postgresql] / src / backend / utils / adt / tsquery_cleanup.c
1 /*-------------------------------------------------------------------------
2  *
3  * tsquery_cleanup.c
4  *       Cleanup query from NOT values and/or stopword
5  *       Utility functions to correct work.
6  *
7  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
8  *
9  *
10  * IDENTIFICATION
11  *        src/backend/utils/adt/tsquery_cleanup.c
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include "tsearch/ts_utils.h"
19 #include "miscadmin.h"
20
21 typedef struct NODE
22 {
23         struct NODE *left;
24         struct NODE *right;
25         QueryItem  *valnode;
26 } NODE;
27
28 /*
29  * make query tree from plain view of query
30  */
31 static NODE *
32 maketree(QueryItem *in)
33 {
34         NODE       *node = (NODE *) palloc(sizeof(NODE));
35
36         /* since this function recurses, it could be driven to stack overflow. */
37         check_stack_depth();
38
39         node->valnode = in;
40         node->right = node->left = NULL;
41         if (in->type == QI_OPR)
42         {
43                 node->right = maketree(in + 1);
44                 if (in->qoperator.oper != OP_NOT)
45                         node->left = maketree(in + in->qoperator.left);
46         }
47         return node;
48 }
49
50 /*
51  * Internal state for plaintree and plainnode
52  */
53 typedef struct
54 {
55         QueryItem  *ptr;
56         int                     len;                    /* allocated size of ptr */
57         int                     cur;                    /* number of elements in ptr */
58 } PLAINTREE;
59
60 static void
61 plainnode(PLAINTREE *state, NODE *node)
62 {
63         /* since this function recurses, it could be driven to stack overflow. */
64         check_stack_depth();
65
66         if (state->cur == state->len)
67         {
68                 state->len *= 2;
69                 state->ptr = (QueryItem *) repalloc((void *) state->ptr, state->len * sizeof(QueryItem));
70         }
71         memcpy((void *) &(state->ptr[state->cur]), (void *) node->valnode, sizeof(QueryItem));
72         if (node->valnode->type == QI_VAL)
73                 state->cur++;
74         else if (node->valnode->qoperator.oper == OP_NOT)
75         {
76                 state->ptr[state->cur].qoperator.left = 1;
77                 state->cur++;
78                 plainnode(state, node->right);
79         }
80         else
81         {
82                 int                     cur = state->cur;
83
84                 state->cur++;
85                 plainnode(state, node->right);
86                 state->ptr[cur].qoperator.left = state->cur - cur;
87                 plainnode(state, node->left);
88         }
89         pfree(node);
90 }
91
92 /*
93  * make plain view of tree from a NODE-tree representation
94  */
95 static QueryItem *
96 plaintree(NODE *root, int *len)
97 {
98         PLAINTREE       pl;
99
100         pl.cur = 0;
101         pl.len = 16;
102         if (root && (root->valnode->type == QI_VAL || root->valnode->type == QI_OPR))
103         {
104                 pl.ptr = (QueryItem *) palloc(pl.len * sizeof(QueryItem));
105                 plainnode(&pl, root);
106         }
107         else
108                 pl.ptr = NULL;
109         *len = pl.cur;
110         return pl.ptr;
111 }
112
113 static void
114 freetree(NODE *node)
115 {
116         /* since this function recurses, it could be driven to stack overflow. */
117         check_stack_depth();
118
119         if (!node)
120                 return;
121         if (node->left)
122                 freetree(node->left);
123         if (node->right)
124                 freetree(node->right);
125         pfree(node);
126 }
127
128 /*
129  * clean tree for ! operator.
130  * It's useful for debug, but in
131  * other case, such view is used with search in index.
132  * Operator ! always return TRUE
133  */
134 static NODE *
135 clean_NOT_intree(NODE *node)
136 {
137         /* since this function recurses, it could be driven to stack overflow. */
138         check_stack_depth();
139
140         if (node->valnode->type == QI_VAL)
141                 return node;
142
143         if (node->valnode->qoperator.oper == OP_NOT)
144         {
145                 freetree(node);
146                 return NULL;
147         }
148
149         /* operator & or | */
150         if (node->valnode->qoperator.oper == OP_OR)
151         {
152                 if ((node->left = clean_NOT_intree(node->left)) == NULL ||
153                         (node->right = clean_NOT_intree(node->right)) == NULL)
154                 {
155                         freetree(node);
156                         return NULL;
157                 }
158         }
159         else
160         {
161                 NODE       *res = node;
162
163                 Assert(node->valnode->qoperator.oper == OP_AND);
164
165                 node->left = clean_NOT_intree(node->left);
166                 node->right = clean_NOT_intree(node->right);
167                 if (node->left == NULL && node->right == NULL)
168                 {
169                         pfree(node);
170                         res = NULL;
171                 }
172                 else if (node->left == NULL)
173                 {
174                         res = node->right;
175                         pfree(node);
176                 }
177                 else if (node->right == NULL)
178                 {
179                         res = node->left;
180                         pfree(node);
181                 }
182                 return res;
183         }
184         return node;
185 }
186
187 QueryItem *
188 clean_NOT(QueryItem *ptr, int *len)
189 {
190         NODE       *root = maketree(ptr);
191
192         return plaintree(clean_NOT_intree(root), len);
193 }
194
195
196 #ifdef V_UNKNOWN                                /* exists in Windows headers */
197 #undef V_UNKNOWN
198 #endif
199 #ifdef V_FALSE                                  /* exists in Solaris headers */
200 #undef V_FALSE
201 #endif
202
203 /*
204  * output values for result output parameter of clean_fakeval_intree
205  */
206 #define V_UNKNOWN       0                       /* the expression can't be evaluated
207                                                                  * statically */
208 #define V_TRUE          1                       /* the expression is always true (not
209                                                                  * implemented) */
210 #define V_FALSE         2                       /* the expression is always false (not
211                                                                  * implemented) */
212 #define V_STOP          3                       /* the expression is a stop word */
213
214 /*
215  * Clean query tree from values which is always in
216  * text (stopword)
217  */
218 static NODE *
219 clean_fakeval_intree(NODE *node, char *result)
220 {
221         char            lresult = V_UNKNOWN,
222                                 rresult = V_UNKNOWN;
223
224         /* since this function recurses, it could be driven to stack overflow. */
225         check_stack_depth();
226
227         if (node->valnode->type == QI_VAL)
228                 return node;
229         else if (node->valnode->type == QI_VALSTOP)
230         {
231                 pfree(node);
232                 *result = V_STOP;
233                 return NULL;
234         }
235
236         Assert(node->valnode->type == QI_OPR);
237
238         if (node->valnode->qoperator.oper == OP_NOT)
239         {
240                 node->right = clean_fakeval_intree(node->right, &rresult);
241                 if (!node->right)
242                 {
243                         *result = V_STOP;
244                         freetree(node);
245                         return NULL;
246                 }
247         }
248         else
249         {
250                 NODE       *res = node;
251
252                 node->left = clean_fakeval_intree(node->left, &lresult);
253                 node->right = clean_fakeval_intree(node->right, &rresult);
254
255                 if (lresult == V_STOP && rresult == V_STOP)
256                 {
257                         freetree(node);
258                         *result = V_STOP;
259                         return NULL;
260                 }
261                 else if (lresult == V_STOP)
262                 {
263                         res = node->right;
264                         pfree(node);
265                 }
266                 else if (rresult == V_STOP)
267                 {
268                         res = node->left;
269                         pfree(node);
270                 }
271                 return res;
272         }
273         return node;
274 }
275
276 QueryItem *
277 clean_fakeval(QueryItem *ptr, int *len)
278 {
279         NODE       *root = maketree(ptr);
280         char            result = V_UNKNOWN;
281         NODE       *resroot;
282
283         resroot = clean_fakeval_intree(root, &result);
284         if (result != V_UNKNOWN)
285         {
286                 ereport(NOTICE,
287                                 (errmsg("text-search query contains only stop words or doesn't contain lexemes, ignored")));
288                 *len = 0;
289                 return NULL;
290         }
291
292         return plaintree(resroot, len);
293 }