]> granicus.if.org Git - postgresql/blob - contrib/tsearch2/query_cleanup.c
Reduce WAL activity for page splits:
[postgresql] / contrib / tsearch2 / query_cleanup.c
1 /*
2  * Rewrite routines of query tree
3  * Teodor Sigaev <teodor@sigaev.ru>
4  */
5
6 #include "postgres.h"
7
8 #include <float.h>
9
10 #include "utils/builtins.h"
11
12 #include "query.h"
13 #include "query_cleanup.h"
14
15 typedef struct NODE
16 {
17         struct NODE *left;
18         struct NODE *right;
19         ITEM       *valnode;
20 }       NODE;
21
22 /*
23  * make query tree from plain view of query
24  */
25 static NODE *
26 maketree(ITEM * in)
27 {
28         NODE       *node = (NODE *) palloc(sizeof(NODE));
29
30         node->valnode = in;
31         node->right = node->left = NULL;
32         if (in->type == OPR)
33         {
34                 node->right = maketree(in + 1);
35                 if (in->val != (int4) '!')
36                         node->left = maketree(in + in->left);
37         }
38         return node;
39 }
40
41 typedef struct
42 {
43         ITEM       *ptr;
44         int4            len;
45         int4            cur;
46 }       PLAINTREE;
47
48 static void
49 plainnode(PLAINTREE * state, NODE * node)
50 {
51         if (state->cur == state->len)
52         {
53                 state->len *= 2;
54                 state->ptr = (ITEM *) repalloc((void *) state->ptr, state->len * sizeof(ITEM));
55         }
56         memcpy((void *) &(state->ptr[state->cur]), (void *) node->valnode, sizeof(ITEM));
57         if (node->valnode->type == VAL)
58                 state->cur++;
59         else if (node->valnode->val == (int4) '!')
60         {
61                 state->ptr[state->cur].left = 1;
62                 state->cur++;
63                 plainnode(state, node->right);
64         }
65         else
66         {
67                 int4            cur = state->cur;
68
69                 state->cur++;
70                 plainnode(state, node->right);
71                 state->ptr[cur].left = state->cur - cur;
72                 plainnode(state, node->left);
73         }
74         pfree(node);
75 }
76
77 /*
78  * make plain view of tree from 'normal' view of tree
79  */
80 static ITEM *
81 plaintree(NODE * root, int4 *len)
82 {
83         PLAINTREE       pl;
84
85         pl.cur = 0;
86         pl.len = 16;
87         if (root && (root->valnode->type == VAL || root->valnode->type == OPR))
88         {
89                 pl.ptr = (ITEM *) palloc(pl.len * sizeof(ITEM));
90                 plainnode(&pl, root);
91         }
92         else
93                 pl.ptr = NULL;
94         *len = pl.cur;
95         return pl.ptr;
96 }
97
98 static void
99 freetree(NODE * node)
100 {
101         if (!node)
102                 return;
103         if (node->left)
104                 freetree(node->left);
105         if (node->right)
106                 freetree(node->right);
107         pfree(node);
108 }
109
110 /*
111  * clean tree for ! operator.
112  * It's usefull for debug, but in
113  * other case, such view is used with search in index.
114  * Operator ! always return TRUE
115  */
116 static NODE *
117 clean_NOT_intree(NODE * node)
118 {
119         if (node->valnode->type == VAL)
120                 return node;
121
122         if (node->valnode->val == (int4) '!')
123         {
124                 freetree(node);
125                 return NULL;
126         }
127
128         /* operator & or | */
129         if (node->valnode->val == (int4) '|')
130         {
131                 if ((node->left = clean_NOT_intree(node->left)) == NULL ||
132                         (node->right = clean_NOT_intree(node->right)) == NULL)
133                 {
134                         freetree(node);
135                         return NULL;
136                 }
137         }
138         else
139         {
140                 NODE       *res = node;
141
142                 node->left = clean_NOT_intree(node->left);
143                 node->right = clean_NOT_intree(node->right);
144                 if (node->left == NULL && node->right == NULL)
145                 {
146                         pfree(node);
147                         res = NULL;
148                 }
149                 else if (node->left == NULL)
150                 {
151                         res = node->right;
152                         pfree(node);
153                 }
154                 else if (node->right == NULL)
155                 {
156                         res = node->left;
157                         pfree(node);
158                 }
159                 return res;
160         }
161         return node;
162 }
163
164 ITEM *
165 clean_NOT_v2(ITEM * ptr, int4 *len)
166 {
167         NODE       *root = maketree(ptr);
168
169         return plaintree(clean_NOT_intree(root), len);
170 }
171
172
173 #ifdef V_UNKNOWN                                /* exists in Windows headers */
174 #undef V_UNKNOWN
175 #endif
176
177 #define V_UNKNOWN       0
178 #define V_TRUE          1
179 #define V_FALSE         2
180 #define V_STOP          3
181
182 /*
183  * Clean query tree from values which is always in
184  * text (stopword)
185  */
186 static NODE *
187 clean_fakeval_intree(NODE * node, char *result)
188 {
189         char            lresult = V_UNKNOWN,
190                                 rresult = V_UNKNOWN;
191
192         if (node->valnode->type == VAL)
193                 return node;
194         else if (node->valnode->type == VALSTOP)
195         {
196                 pfree(node);
197                 *result = V_STOP;
198                 return NULL;
199         }
200
201
202         if (node->valnode->val == (int4) '!')
203         {
204                 node->right = clean_fakeval_intree(node->right, &rresult);
205                 if (!node->right)
206                 {
207                         *result = V_STOP;
208                         freetree(node);
209                         return NULL;
210                 }
211         }
212         else
213         {
214                 NODE       *res = node;
215
216                 node->left = clean_fakeval_intree(node->left, &lresult);
217                 node->right = clean_fakeval_intree(node->right, &rresult);
218                 if (lresult == V_STOP && rresult == V_STOP)
219                 {
220                         freetree(node);
221                         *result = V_STOP;
222                         return NULL;
223                 }
224                 else if (lresult == V_STOP)
225                 {
226                         res = node->right;
227                         pfree(node);
228                 }
229                 else if (rresult == V_STOP)
230                 {
231                         res = node->left;
232                         pfree(node);
233                 }
234                 return res;
235         }
236         return node;
237 }
238
239 ITEM *
240 clean_fakeval_v2(ITEM * ptr, int4 *len)
241 {
242         NODE       *root = maketree(ptr);
243         char            result = V_UNKNOWN;
244         NODE       *resroot;
245
246         resroot = clean_fakeval_intree(root, &result);
247         if (result != V_UNKNOWN)
248         {
249                 elog(NOTICE, "query contains only stopword(s) or doesn't contain lexeme(s), ignored");
250                 *len = 0;
251                 return NULL;
252         }
253
254         return plaintree(resroot, len);
255 }