Fix UNLISTEN to fall out quickly if the current backend has never executed
[PostgreSQL.git] / contrib / ltree / lquery_op.c
blob0752e6380d5280b53dbae5f3e416056161b45fc6
1 /*
2 * op function for ltree and lquery
3 * Teodor Sigaev <teodor@stack.net>
4 * $PostgreSQL$
5 */
6 #include "postgres.h"
8 #include <ctype.h>
10 #include "utils/array.h"
11 #include "utils/formatting.h"
12 #include "ltree.h"
14 PG_FUNCTION_INFO_V1(ltq_regex);
15 PG_FUNCTION_INFO_V1(ltq_rregex);
17 PG_FUNCTION_INFO_V1(lt_q_regex);
18 PG_FUNCTION_INFO_V1(lt_q_rregex);
20 #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
22 typedef struct
24 lquery_level *q;
25 int nq;
26 ltree_level *t;
27 int nt;
28 int posq;
29 int post;
30 } FieldNot;
32 static char *
33 getlexeme(char *start, char *end, int *len)
35 char *ptr;
36 int charlen;
38 while (start < end && (charlen = pg_mblen(start)) == 1 && t_iseq(start,'_') )
39 start += charlen;
41 ptr = start;
42 if (ptr >= end)
43 return NULL;
45 while (ptr < end && !( (charlen = pg_mblen(ptr)) == 1 && t_iseq(ptr, '_') ) )
46 ptr += charlen;
48 *len = ptr - start;
49 return start;
52 bool
53 compare_subnode(ltree_level * t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
55 char *endt = t->name + t->len;
56 char *endq = qn + len;
57 char *tn;
58 int lent,
59 lenq;
60 bool isok;
62 while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
64 tn = t->name;
65 isok = false;
66 while ((tn = getlexeme(tn, endt, &lent)) != NULL)
68 if (
70 lent == lenq ||
71 (lent > lenq && anyend)
72 ) &&
73 (*cmpptr) (qn, tn, lenq) == 0)
76 isok = true;
77 break;
79 tn += lent;
82 if (!isok)
83 return false;
84 qn += lenq;
87 return true;
90 int
91 ltree_strncasecmp(const char *a, const char *b, size_t s)
93 char *al = str_tolower(a, s);
94 char *bl = str_tolower(b, s);
95 int res;
97 res = strncmp(al, bl,s);
99 pfree(al);
100 pfree(bl);
102 return res;
105 static bool
106 checkLevel(lquery_level * curq, ltree_level * curt)
108 int (*cmpptr) (const char *, const char *, size_t);
109 lquery_variant *curvar = LQL_FIRST(curq);
110 int i;
112 for (i = 0; i < curq->numvar; i++)
114 cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
116 if (curvar->flag & LVAR_SUBLEXEME)
118 if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND)))
119 return true;
121 else if (
123 curvar->len == curt->len ||
124 (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))
125 ) &&
126 (*cmpptr) (curvar->name, curt->name, curvar->len) == 0)
129 return true;
131 curvar = LVAR_NEXT(curvar);
133 return false;
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++;
146 static struct
148 bool muse;
149 uint32 high_pos;
150 } SomeStack =
153 false, 0,
156 static bool
157 checkCond(lquery_level * curq, int query_numlevel, ltree_level * curt, int tree_numlevel, FieldNot * ptr)
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;
168 if (SomeStack.muse)
170 high_pos = SomeStack.high_pos;
171 qlen--;
172 prevq = curq;
173 curq = LQL_NEXT(curq);
174 SomeStack.muse = false;
177 while (tlen > 0 && qlen > 0)
179 if (curq->numvar)
181 prevt = curt;
182 while (cur_tpos < low_pos)
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++;
193 if (ptr && curq->flag & LQL_NOT)
195 if (!(prevq && prevq->numvar == 0))
196 prevq = curq;
197 if (ptr->q == NULL)
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;
206 else
208 ptr->nt++;
209 ptr->nq++;
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++;
220 else
222 isok = false;
223 while (cur_tpos <= high_pos && tlen > 0 && !isok)
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)
231 FieldNot tmpptr;
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;
240 if (!isok && ptr)
241 ptr->nt++;
243 if (!isok)
244 return false;
246 if (ptr && ptr->q)
248 if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
249 return false;
250 ptr->q = NULL;
252 low_pos = cur_tpos;
253 high_pos = cur_tpos;
256 else
258 low_pos = cur_tpos + curq->low;
259 high_pos = cur_tpos + curq->high;
260 if (ptr && ptr->q)
262 ptr->nq++;
263 if (qlen == 1)
264 ptr->nt = tree_numlevel - ptr->post;
268 prevq = curq;
269 curq = LQL_NEXT(curq);
270 qlen--;
273 if (low_pos > tree_numlevel || tree_numlevel > high_pos)
274 return false;
276 while (qlen > 0)
278 if (curq->numvar)
280 if (!(curq->flag & LQL_NOT))
281 return false;
283 else
285 low_pos = cur_tpos + curq->low;
286 high_pos = cur_tpos + curq->high;
289 curq = LQL_NEXT(curq);
290 qlen--;
293 if (low_pos > tree_numlevel || tree_numlevel > high_pos)
294 return false;
296 if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
297 return false;
299 return true;
302 Datum
303 ltq_regex(PG_FUNCTION_ARGS)
305 ltree *tree = PG_GETARG_LTREE(0);
306 lquery *query = PG_GETARG_LQUERY(1);
307 bool res = false;
309 if (query->flag & LQUERY_HASNOT)
311 FieldNot fn;
313 fn.q = NULL;
315 res = checkCond(LQUERY_FIRST(query), query->numlevel,
316 LTREE_FIRST(tree), tree->numlevel, &fn);
318 else
320 res = checkCond(LQUERY_FIRST(query), query->numlevel,
321 LTREE_FIRST(tree), tree->numlevel, NULL);
324 PG_FREE_IF_COPY(tree, 0);
325 PG_FREE_IF_COPY(query, 1);
326 PG_RETURN_BOOL(res);
329 Datum
330 ltq_rregex(PG_FUNCTION_ARGS)
332 PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex,
333 PG_GETARG_DATUM(1),
334 PG_GETARG_DATUM(0)
338 Datum
339 lt_q_regex(PG_FUNCTION_ARGS)
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));
347 if (ARR_NDIM(_query) != 1)
348 ereport(ERROR,
349 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
350 errmsg("array must be one-dimensional")));
351 if (ARR_HASNULL(_query))
352 ereport(ERROR,
353 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
354 errmsg("array must not contain nulls")));
356 while (num > 0)
358 if (DatumGetBool(DirectFunctionCall2(ltq_regex,
359 PointerGetDatum(tree), PointerGetDatum(query))))
362 res = true;
363 break;
365 num--;
366 query = NEXTVAL(query);
369 PG_FREE_IF_COPY(tree, 0);
370 PG_FREE_IF_COPY(_query, 1);
371 PG_RETURN_BOOL(res);
374 Datum
375 lt_q_rregex(PG_FUNCTION_ARGS)
377 PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex,
378 PG_GETARG_DATUM(1),
379 PG_GETARG_DATUM(0)