]> granicus.if.org Git - postgresql/blob - contrib/ltree/lquery_op.c
Replace int2/int4 in C code with int16/int32
[postgresql] / contrib / ltree / lquery_op.c
1 /*
2  * op function for ltree and lquery
3  * Teodor Sigaev <teodor@stack.net>
4  * contrib/ltree/lquery_op.c
5  */
6 #include "postgres.h"
7
8 #include <ctype.h>
9
10 #include "catalog/pg_collation.h"
11 #include "utils/formatting.h"
12 #include "ltree.h"
13
14 PG_FUNCTION_INFO_V1(ltq_regex);
15 PG_FUNCTION_INFO_V1(ltq_rregex);
16
17 PG_FUNCTION_INFO_V1(lt_q_regex);
18 PG_FUNCTION_INFO_V1(lt_q_rregex);
19
20 #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
21
22 typedef struct
23 {
24         lquery_level *q;
25         int                     nq;
26         ltree_level *t;
27         int                     nt;
28         int                     posq;
29         int                     post;
30 } FieldNot;
31
32 static char *
33 getlexeme(char *start, char *end, int *len)
34 {
35         char       *ptr;
36         int                     charlen;
37
38         while (start < end && (charlen = pg_mblen(start)) == 1 && t_iseq(start, '_'))
39                 start += charlen;
40
41         ptr = start;
42         if (ptr >= end)
43                 return NULL;
44
45         while (ptr < end && !((charlen = pg_mblen(ptr)) == 1 && t_iseq(ptr, '_')))
46                 ptr += charlen;
47
48         *len = ptr - start;
49         return start;
50 }
51
52 bool
53                         compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
54 {
55         char       *endt = t->name + t->len;
56         char       *endq = qn + len;
57         char       *tn;
58         int                     lent,
59                                 lenq;
60         bool            isok;
61
62         while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
63         {
64                 tn = t->name;
65                 isok = false;
66                 while ((tn = getlexeme(tn, endt, &lent)) != NULL)
67                 {
68                         if (
69                                 (
70                                  lent == lenq ||
71                                  (lent > lenq && anyend)
72                                  ) &&
73                                 (*cmpptr) (qn, tn, lenq) == 0)
74                         {
75
76                                 isok = true;
77                                 break;
78                         }
79                         tn += lent;
80                 }
81
82                 if (!isok)
83                         return false;
84                 qn += lenq;
85         }
86
87         return true;
88 }
89
90 int
91 ltree_strncasecmp(const char *a, const char *b, size_t s)
92 {
93         char       *al = str_tolower(a, s, DEFAULT_COLLATION_OID);
94         char       *bl = str_tolower(b, s, DEFAULT_COLLATION_OID);
95         int                     res;
96
97         res = strncmp(al, bl, s);
98
99         pfree(al);
100         pfree(bl);
101
102         return res;
103 }
104
105 static bool
106 checkLevel(lquery_level *curq, ltree_level *curt)
107 {
108         int                     (*cmpptr) (const char *, const char *, size_t);
109         lquery_variant *curvar = LQL_FIRST(curq);
110         int                     i;
111
112         for (i = 0; i < curq->numvar; i++)
113         {
114                 cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
115
116                 if (curvar->flag & LVAR_SUBLEXEME)
117                 {
118                         if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND)))
119                                 return true;
120                 }
121                 else if (
122                                  (
123                                   curvar->len == curt->len ||
124                                   (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))
125                                   ) &&
126                                  (*cmpptr) (curvar->name, curt->name, curvar->len) == 0)
127                 {
128
129                         return true;
130                 }
131                 curvar = LVAR_NEXT(curvar);
132         }
133         return false;
134 }
135
136 /*
137 void
138 printFieldNot(FieldNot *fn ) {
139         while(fn->q) {
140                 elog(NOTICE,"posQ:%d lenQ:%d posT:%d lenT:%d", fn->posq,fn->nq,fn->post,fn->nt);
141                 fn++;
142         }
143 }
144 */
145
146 static struct
147 {
148         bool            muse;
149         uint32          high_pos;
150 }       SomeStack =
151
152 {
153         false, 0,
154 };
155
156 static bool
157 checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr)
158 {
159         uint32          low_pos = 0,
160                                 high_pos = 0,
161                                 cur_tpos = 0;
162         int                     tlen = tree_numlevel,
163                                 qlen = query_numlevel;
164         int                     isok;
165         lquery_level *prevq = NULL;
166         ltree_level *prevt = NULL;
167
168         if (SomeStack.muse)
169         {
170                 high_pos = SomeStack.high_pos;
171                 qlen--;
172                 prevq = curq;
173                 curq = LQL_NEXT(curq);
174                 SomeStack.muse = false;
175         }
176
177         while (tlen > 0 && qlen > 0)
178         {
179                 if (curq->numvar)
180                 {
181                         prevt = curt;
182                         while (cur_tpos < low_pos)
183                         {
184                                 curt = LEVEL_NEXT(curt);
185                                 tlen--;
186                                 cur_tpos++;
187                                 if (tlen == 0)
188                                         return false;
189                                 if (ptr && ptr->q)
190                                         ptr->nt++;
191                         }
192
193                         if (ptr && curq->flag & LQL_NOT)
194                         {
195                                 if (!(prevq && prevq->numvar == 0))
196                                         prevq = curq;
197                                 if (ptr->q == NULL)
198                                 {
199                                         ptr->t = prevt;
200                                         ptr->q = prevq;
201                                         ptr->nt = 1;
202                                         ptr->nq = 1 + ((prevq == curq) ? 0 : 1);
203                                         ptr->posq = query_numlevel - qlen - ((prevq == curq) ? 0 : 1);
204                                         ptr->post = cur_tpos;
205                                 }
206                                 else
207                                 {
208                                         ptr->nt++;
209                                         ptr->nq++;
210                                 }
211
212                                 if (qlen == 1 && ptr->q->numvar == 0)
213                                         ptr->nt = tree_numlevel - ptr->post;
214                                 curt = LEVEL_NEXT(curt);
215                                 tlen--;
216                                 cur_tpos++;
217                                 if (high_pos < cur_tpos)
218                                         high_pos++;
219                         }
220                         else
221                         {
222                                 isok = false;
223                                 while (cur_tpos <= high_pos && tlen > 0 && !isok)
224                                 {
225                                         isok = checkLevel(curq, curt);
226                                         curt = LEVEL_NEXT(curt);
227                                         tlen--;
228                                         cur_tpos++;
229                                         if (isok && prevq && prevq->numvar == 0 && tlen > 0 && cur_tpos <= high_pos)
230                                         {
231                                                 FieldNot        tmpptr;
232
233                                                 if (ptr)
234                                                         memcpy(&tmpptr, ptr, sizeof(FieldNot));
235                                                 SomeStack.high_pos = high_pos - cur_tpos;
236                                                 SomeStack.muse = true;
237                                                 if (checkCond(prevq, qlen + 1, curt, tlen, (ptr) ? &tmpptr : NULL))
238                                                         return true;
239                                         }
240                                         if (!isok && ptr)
241                                                 ptr->nt++;
242                                 }
243                                 if (!isok)
244                                         return false;
245
246                                 if (ptr && ptr->q)
247                                 {
248                                         if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
249                                                 return false;
250                                         ptr->q = NULL;
251                                 }
252                                 low_pos = cur_tpos;
253                                 high_pos = cur_tpos;
254                         }
255                 }
256                 else
257                 {
258                         low_pos = cur_tpos + curq->low;
259                         high_pos = cur_tpos + curq->high;
260                         if (ptr && ptr->q)
261                         {
262                                 ptr->nq++;
263                                 if (qlen == 1)
264                                         ptr->nt = tree_numlevel - ptr->post;
265                         }
266                 }
267
268                 prevq = curq;
269                 curq = LQL_NEXT(curq);
270                 qlen--;
271         }
272
273         if (low_pos > tree_numlevel || tree_numlevel > high_pos)
274                 return false;
275
276         while (qlen > 0)
277         {
278                 if (curq->numvar)
279                 {
280                         if (!(curq->flag & LQL_NOT))
281                                 return false;
282                 }
283                 else
284                 {
285                         low_pos = cur_tpos + curq->low;
286                         high_pos = cur_tpos + curq->high;
287                 }
288
289                 curq = LQL_NEXT(curq);
290                 qlen--;
291         }
292
293         if (low_pos > tree_numlevel || tree_numlevel > high_pos)
294                 return false;
295
296         if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
297                 return false;
298
299         return true;
300 }
301
302 Datum
303 ltq_regex(PG_FUNCTION_ARGS)
304 {
305         ltree      *tree = PG_GETARG_LTREE(0);
306         lquery     *query = PG_GETARG_LQUERY(1);
307         bool            res = false;
308
309         if (query->flag & LQUERY_HASNOT)
310         {
311                 FieldNot        fn;
312
313                 fn.q = NULL;
314
315                 res = checkCond(LQUERY_FIRST(query), query->numlevel,
316                                                 LTREE_FIRST(tree), tree->numlevel, &fn);
317         }
318         else
319         {
320                 res = checkCond(LQUERY_FIRST(query), query->numlevel,
321                                                 LTREE_FIRST(tree), tree->numlevel, NULL);
322         }
323
324         PG_FREE_IF_COPY(tree, 0);
325         PG_FREE_IF_COPY(query, 1);
326         PG_RETURN_BOOL(res);
327 }
328
329 Datum
330 ltq_rregex(PG_FUNCTION_ARGS)
331 {
332         PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex,
333                                                                                 PG_GETARG_DATUM(1),
334                                                                                 PG_GETARG_DATUM(0)
335                                                                                 ));
336 }
337
338 Datum
339 lt_q_regex(PG_FUNCTION_ARGS)
340 {
341         ltree      *tree = PG_GETARG_LTREE(0);
342         ArrayType  *_query = PG_GETARG_ARRAYTYPE_P(1);
343         lquery     *query = (lquery *) ARR_DATA_PTR(_query);
344         bool            res = false;
345         int                     num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
346
347         if (ARR_NDIM(_query) > 1)
348                 ereport(ERROR,
349                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
350                                  errmsg("array must be one-dimensional")));
351         if (array_contains_nulls(_query))
352                 ereport(ERROR,
353                                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
354                                  errmsg("array must not contain nulls")));
355
356         while (num > 0)
357         {
358                 if (DatumGetBool(DirectFunctionCall2(ltq_regex,
359                                                          PointerGetDatum(tree), PointerGetDatum(query))))
360                 {
361
362                         res = true;
363                         break;
364                 }
365                 num--;
366                 query = NEXTVAL(query);
367         }
368
369         PG_FREE_IF_COPY(tree, 0);
370         PG_FREE_IF_COPY(_query, 1);
371         PG_RETURN_BOOL(res);
372 }
373
374 Datum
375 lt_q_rregex(PG_FUNCTION_ARGS)
376 {
377         PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex,
378                                                                                 PG_GETARG_DATUM(1),
379                                                                                 PG_GETARG_DATUM(0)
380                                                                                 ));
381 }