Fix UNLISTEN to fall out quickly if the current backend has never executed
[PostgreSQL.git] / contrib / ltree / ltree_op.c
blobaf37070067d9b08ea2a5a8b1f836c6ac6e73d02d
1 /*
2 * op function for ltree
3 * Teodor Sigaev <teodor@stack.net>
4 * $PostgreSQL$
5 */
6 #include "postgres.h"
8 #include <ctype.h>
10 #include "catalog/pg_statistic.h"
11 #include "utils/builtins.h"
12 #include "utils/lsyscache.h"
13 #include "utils/selfuncs.h"
14 #include "utils/syscache.h"
15 #include "ltree.h"
17 PG_MODULE_MAGIC;
19 /* compare functions */
20 PG_FUNCTION_INFO_V1(ltree_cmp);
21 PG_FUNCTION_INFO_V1(ltree_lt);
22 PG_FUNCTION_INFO_V1(ltree_le);
23 PG_FUNCTION_INFO_V1(ltree_eq);
24 PG_FUNCTION_INFO_V1(ltree_ne);
25 PG_FUNCTION_INFO_V1(ltree_ge);
26 PG_FUNCTION_INFO_V1(ltree_gt);
27 PG_FUNCTION_INFO_V1(nlevel);
28 PG_FUNCTION_INFO_V1(ltree_isparent);
29 PG_FUNCTION_INFO_V1(ltree_risparent);
30 PG_FUNCTION_INFO_V1(subltree);
31 PG_FUNCTION_INFO_V1(subpath);
32 PG_FUNCTION_INFO_V1(ltree_index);
33 PG_FUNCTION_INFO_V1(ltree_addltree);
34 PG_FUNCTION_INFO_V1(ltree_addtext);
35 PG_FUNCTION_INFO_V1(ltree_textadd);
36 PG_FUNCTION_INFO_V1(lca);
37 PG_FUNCTION_INFO_V1(ltree2text);
38 PG_FUNCTION_INFO_V1(text2ltree);
39 PG_FUNCTION_INFO_V1(ltreeparentsel);
41 Datum ltree_cmp(PG_FUNCTION_ARGS);
42 Datum ltree_lt(PG_FUNCTION_ARGS);
43 Datum ltree_le(PG_FUNCTION_ARGS);
44 Datum ltree_eq(PG_FUNCTION_ARGS);
45 Datum ltree_ne(PG_FUNCTION_ARGS);
46 Datum ltree_ge(PG_FUNCTION_ARGS);
47 Datum ltree_gt(PG_FUNCTION_ARGS);
48 Datum nlevel(PG_FUNCTION_ARGS);
49 Datum subltree(PG_FUNCTION_ARGS);
50 Datum subpath(PG_FUNCTION_ARGS);
51 Datum ltree_index(PG_FUNCTION_ARGS);
52 Datum ltree_addltree(PG_FUNCTION_ARGS);
53 Datum ltree_addtext(PG_FUNCTION_ARGS);
54 Datum ltree_textadd(PG_FUNCTION_ARGS);
55 Datum lca(PG_FUNCTION_ARGS);
56 Datum ltree2text(PG_FUNCTION_ARGS);
57 Datum text2ltree(PG_FUNCTION_ARGS);
58 Datum ltreeparentsel(PG_FUNCTION_ARGS);
60 int
61 ltree_compare(const ltree * a, const ltree * b)
63 ltree_level *al = LTREE_FIRST(a);
64 ltree_level *bl = LTREE_FIRST(b);
65 int an = a->numlevel;
66 int bn = b->numlevel;
67 int res = 0;
69 while (an > 0 && bn > 0)
71 if ((res = strncmp(al->name, bl->name, Min(al->len, bl->len))) == 0)
73 if (al->len != bl->len)
74 return (al->len - bl->len) * 10 * (an + 1);
76 else
77 return res * 10 * (an + 1);
79 an--;
80 bn--;
81 al = LEVEL_NEXT(al);
82 bl = LEVEL_NEXT(bl);
85 return (a->numlevel - b->numlevel) * 10 * (an + 1);
88 #define RUNCMP \
89 ltree *a = PG_GETARG_LTREE(0); \
90 ltree *b = PG_GETARG_LTREE(1); \
91 int res = ltree_compare(a,b); \
92 PG_FREE_IF_COPY(a,0); \
93 PG_FREE_IF_COPY(b,1); \
95 Datum
96 ltree_cmp(PG_FUNCTION_ARGS)
98 RUNCMP
99 PG_RETURN_INT32(res);
102 Datum
103 ltree_lt(PG_FUNCTION_ARGS)
105 RUNCMP
106 PG_RETURN_BOOL((res < 0) ? true : false);
109 Datum
110 ltree_le(PG_FUNCTION_ARGS)
112 RUNCMP
113 PG_RETURN_BOOL((res <= 0) ? true : false);
116 Datum
117 ltree_eq(PG_FUNCTION_ARGS)
119 RUNCMP
120 PG_RETURN_BOOL((res == 0) ? true : false);
123 Datum
124 ltree_ge(PG_FUNCTION_ARGS)
126 RUNCMP
127 PG_RETURN_BOOL((res >= 0) ? true : false);
130 Datum
131 ltree_gt(PG_FUNCTION_ARGS)
133 RUNCMP
134 PG_RETURN_BOOL((res > 0) ? true : false);
137 Datum
138 ltree_ne(PG_FUNCTION_ARGS)
140 RUNCMP
141 PG_RETURN_BOOL((res != 0) ? true : false);
144 Datum
145 nlevel(PG_FUNCTION_ARGS)
147 ltree *a = PG_GETARG_LTREE(0);
148 int res = a->numlevel;
150 PG_FREE_IF_COPY(a, 0);
151 PG_RETURN_INT32(res);
154 bool
155 inner_isparent(const ltree * c, const ltree * p)
157 ltree_level *cl = LTREE_FIRST(c);
158 ltree_level *pl = LTREE_FIRST(p);
159 int pn = p->numlevel;
161 if (pn > c->numlevel)
162 return false;
164 while (pn > 0)
166 if (cl->len != pl->len)
167 return false;
168 if (strncmp(cl->name, pl->name, cl->len))
169 return false;
171 pn--;
172 cl = LEVEL_NEXT(cl);
173 pl = LEVEL_NEXT(pl);
175 return true;
178 Datum
179 ltree_isparent(PG_FUNCTION_ARGS)
181 ltree *c = PG_GETARG_LTREE(1);
182 ltree *p = PG_GETARG_LTREE(0);
183 bool res = inner_isparent(c, p);
185 PG_FREE_IF_COPY(c, 1);
186 PG_FREE_IF_COPY(p, 0);
187 PG_RETURN_BOOL(res);
190 Datum
191 ltree_risparent(PG_FUNCTION_ARGS)
193 ltree *c = PG_GETARG_LTREE(0);
194 ltree *p = PG_GETARG_LTREE(1);
195 bool res = inner_isparent(c, p);
197 PG_FREE_IF_COPY(c, 0);
198 PG_FREE_IF_COPY(p, 1);
199 PG_RETURN_BOOL(res);
203 static ltree *
204 inner_subltree(ltree * t, int4 startpos, int4 endpos)
206 char *start = NULL,
207 *end = NULL;
208 ltree_level *ptr = LTREE_FIRST(t);
209 ltree *res;
210 int i;
212 if (startpos < 0 || endpos < 0 || startpos >= t->numlevel || startpos > endpos)
213 ereport(ERROR,
214 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
215 errmsg("invalid positions")));
217 if (endpos > t->numlevel)
218 endpos = t->numlevel;
220 start = end = (char *) ptr;
221 for (i = 0; i < endpos; i++)
223 if (i == startpos)
224 start = (char *) ptr;
225 if (i == endpos - 1)
227 end = (char *) LEVEL_NEXT(ptr);
228 break;
230 ptr = LEVEL_NEXT(ptr);
233 res = (ltree *) palloc(LTREE_HDRSIZE + (end - start));
234 SET_VARSIZE(res, LTREE_HDRSIZE + (end - start));
235 res->numlevel = endpos - startpos;
237 memcpy(LTREE_FIRST(res), start, end - start);
239 return res;
242 Datum
243 subltree(PG_FUNCTION_ARGS)
245 ltree *t = PG_GETARG_LTREE(0);
246 ltree *res = inner_subltree(t, PG_GETARG_INT32(1), PG_GETARG_INT32(2));
248 PG_FREE_IF_COPY(t, 0);
249 PG_RETURN_POINTER(res);
252 Datum
253 subpath(PG_FUNCTION_ARGS)
255 ltree *t = PG_GETARG_LTREE(0);
256 int4 start = PG_GETARG_INT32(1);
257 int4 len = (fcinfo->nargs == 3) ? PG_GETARG_INT32(2) : 0;
258 int4 end;
259 ltree *res;
261 end = start + len;
263 if (start < 0)
265 start = t->numlevel + start;
266 end = start + len;
268 if (start < 0)
269 { /* start > t->numlevel */
270 start = t->numlevel + start;
271 end = start + len;
274 if (len < 0)
275 end = t->numlevel + len;
276 else if (len == 0)
277 end = (fcinfo->nargs == 3) ? start : 0xffff;
279 res = inner_subltree(t, start, end);
281 PG_FREE_IF_COPY(t, 0);
282 PG_RETURN_POINTER(res);
285 static ltree *
286 ltree_concat(ltree * a, ltree * b)
288 ltree *r;
290 r = (ltree *) palloc(VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
291 SET_VARSIZE(r, VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
292 r->numlevel = a->numlevel + b->numlevel;
294 memcpy(LTREE_FIRST(r), LTREE_FIRST(a), VARSIZE(a) - LTREE_HDRSIZE);
295 memcpy(((char *) LTREE_FIRST(r)) + VARSIZE(a) - LTREE_HDRSIZE,
296 LTREE_FIRST(b),
297 VARSIZE(b) - LTREE_HDRSIZE);
298 return r;
301 Datum
302 ltree_addltree(PG_FUNCTION_ARGS)
304 ltree *a = PG_GETARG_LTREE(0);
305 ltree *b = PG_GETARG_LTREE(1);
306 ltree *r;
308 r = ltree_concat(a, b);
309 PG_FREE_IF_COPY(a, 0);
310 PG_FREE_IF_COPY(b, 1);
311 PG_RETURN_POINTER(r);
314 Datum
315 ltree_addtext(PG_FUNCTION_ARGS)
317 ltree *a = PG_GETARG_LTREE(0);
318 text *b = PG_GETARG_TEXT_PP(1);
319 char *s;
320 ltree *r,
321 *tmp;
323 s = text_to_cstring(b);
325 tmp = (ltree *) DatumGetPointer(DirectFunctionCall1(ltree_in,
326 PointerGetDatum(s)));
328 pfree(s);
330 r = ltree_concat(a, tmp);
332 pfree(tmp);
334 PG_FREE_IF_COPY(a, 0);
335 PG_FREE_IF_COPY(b, 1);
336 PG_RETURN_POINTER(r);
339 Datum
340 ltree_index(PG_FUNCTION_ARGS)
342 ltree *a = PG_GETARG_LTREE(0);
343 ltree *b = PG_GETARG_LTREE(1);
344 int start = (fcinfo->nargs == 3) ? PG_GETARG_INT32(2) : 0;
345 int i,
347 ltree_level *startptr,
348 *aptr,
349 *bptr;
350 bool found = false;
352 if (start < 0)
354 if (-start >= a->numlevel)
355 start = 0;
356 else
357 start = (int) (a->numlevel) + start;
360 if (a->numlevel - start < b->numlevel || a->numlevel == 0 || b->numlevel == 0)
362 PG_FREE_IF_COPY(a, 0);
363 PG_FREE_IF_COPY(b, 1);
364 PG_RETURN_INT32(-1);
367 startptr = LTREE_FIRST(a);
368 for (i = 0; i <= a->numlevel - b->numlevel; i++)
370 if (i >= start)
372 aptr = startptr;
373 bptr = LTREE_FIRST(b);
374 for (j = 0; j < b->numlevel; j++)
376 if (!(aptr->len == bptr->len && strncmp(aptr->name, bptr->name, aptr->len) == 0))
377 break;
378 aptr = LEVEL_NEXT(aptr);
379 bptr = LEVEL_NEXT(bptr);
382 if (j == b->numlevel)
384 found = true;
385 break;
388 startptr = LEVEL_NEXT(startptr);
391 if (!found)
392 i = -1;
394 PG_FREE_IF_COPY(a, 0);
395 PG_FREE_IF_COPY(b, 1);
396 PG_RETURN_INT32(i);
399 Datum
400 ltree_textadd(PG_FUNCTION_ARGS)
402 ltree *a = PG_GETARG_LTREE(1);
403 text *b = PG_GETARG_TEXT_PP(0);
404 char *s;
405 ltree *r,
406 *tmp;
408 s = text_to_cstring(b);
410 tmp = (ltree *) DatumGetPointer(DirectFunctionCall1(ltree_in,
411 PointerGetDatum(s)));
413 pfree(s);
415 r = ltree_concat(tmp, a);
417 pfree(tmp);
419 PG_FREE_IF_COPY(a, 1);
420 PG_FREE_IF_COPY(b, 0);
421 PG_RETURN_POINTER(r);
424 ltree *
425 lca_inner(ltree ** a, int len)
427 int tmp,
428 num = ((*a)->numlevel) ? (*a)->numlevel - 1 : 0;
429 ltree **ptr = a + 1;
430 int i,
431 reslen = LTREE_HDRSIZE;
432 ltree_level *l1,
433 *l2;
434 ltree *res;
437 if ((*a)->numlevel == 0)
438 return NULL;
440 while (ptr - a < len)
442 if ((*ptr)->numlevel == 0)
443 return NULL;
444 else if ((*ptr)->numlevel == 1)
445 num = 0;
446 else
448 l1 = LTREE_FIRST(*a);
449 l2 = LTREE_FIRST(*ptr);
450 tmp = num;
451 num = 0;
452 for (i = 0; i < Min(tmp, (*ptr)->numlevel - 1); i++)
454 if (l1->len == l2->len && strncmp(l1->name, l2->name, l1->len) == 0)
455 num = i + 1;
456 else
457 break;
458 l1 = LEVEL_NEXT(l1);
459 l2 = LEVEL_NEXT(l2);
462 ptr++;
465 l1 = LTREE_FIRST(*a);
466 for (i = 0; i < num; i++)
468 reslen += MAXALIGN(l1->len + LEVEL_HDRSIZE);
469 l1 = LEVEL_NEXT(l1);
472 res = (ltree *) palloc(reslen);
473 SET_VARSIZE(res, reslen);
474 res->numlevel = num;
476 l1 = LTREE_FIRST(*a);
477 l2 = LTREE_FIRST(res);
479 for (i = 0; i < num; i++)
481 memcpy(l2, l1, MAXALIGN(l1->len + LEVEL_HDRSIZE));
482 l1 = LEVEL_NEXT(l1);
483 l2 = LEVEL_NEXT(l2);
486 return res;
489 Datum
490 lca(PG_FUNCTION_ARGS)
492 int i;
493 ltree **a,
494 *res;
496 a = (ltree **) palloc(sizeof(ltree *) * fcinfo->nargs);
497 for (i = 0; i < fcinfo->nargs; i++)
498 a[i] = PG_GETARG_LTREE(i);
499 res = lca_inner(a, (int) fcinfo->nargs);
500 for (i = 0; i < fcinfo->nargs; i++)
501 PG_FREE_IF_COPY(a[i], i);
502 pfree(a);
504 if (res)
505 PG_RETURN_POINTER(res);
506 else
507 PG_RETURN_NULL();
510 Datum
511 text2ltree(PG_FUNCTION_ARGS)
513 text *in = PG_GETARG_TEXT_PP(0);
514 char *s;
515 ltree *out;
517 s = text_to_cstring(in);
519 out = (ltree *) DatumGetPointer(DirectFunctionCall1(ltree_in,
520 PointerGetDatum(s)));
521 pfree(s);
522 PG_FREE_IF_COPY(in, 0);
523 PG_RETURN_POINTER(out);
527 Datum
528 ltree2text(PG_FUNCTION_ARGS)
530 ltree *in = PG_GETARG_LTREE(0);
531 char *ptr;
532 int i;
533 ltree_level *curlevel;
534 text *out;
536 out = (text *) palloc(VARSIZE(in) + VARHDRSZ);
537 ptr = VARDATA(out);
538 curlevel = LTREE_FIRST(in);
539 for (i = 0; i < in->numlevel; i++)
541 if (i != 0)
543 *ptr = '.';
544 ptr++;
546 memcpy(ptr, curlevel->name, curlevel->len);
547 ptr += curlevel->len;
548 curlevel = LEVEL_NEXT(curlevel);
551 SET_VARSIZE(out, ptr - ((char *) out));
552 PG_FREE_IF_COPY(in, 0);
554 PG_RETURN_POINTER(out);
558 #define DEFAULT_PARENT_SEL 0.001
561 * ltreeparentsel - Selectivity of parent relationship for ltree data types.
563 Datum
564 ltreeparentsel(PG_FUNCTION_ARGS)
566 PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
567 Oid operator = PG_GETARG_OID(1);
568 List *args = (List *) PG_GETARG_POINTER(2);
569 int varRelid = PG_GETARG_INT32(3);
570 VariableStatData vardata;
571 Node *other;
572 bool varonleft;
573 double selec;
576 * If expression is not variable <@ something or something <@ variable,
577 * then punt and return a default estimate.
579 if (!get_restriction_variable(root, args, varRelid,
580 &vardata, &other, &varonleft))
581 PG_RETURN_FLOAT8(DEFAULT_PARENT_SEL);
584 * If the something is a NULL constant, assume operator is strict and
585 * return zero, ie, operator will never return TRUE.
587 if (IsA(other, Const) &&
588 ((Const *) other)->constisnull)
590 ReleaseVariableStats(vardata);
591 PG_RETURN_FLOAT8(0.0);
594 if (IsA(other, Const))
596 /* Variable is being compared to a known non-null constant */
597 Datum constval = ((Const *) other)->constvalue;
598 FmgrInfo contproc;
599 double mcvsum;
600 double mcvsel;
601 double nullfrac;
602 int hist_size;
604 fmgr_info(get_opcode(operator), &contproc);
607 * Is the constant "<@" to any of the column's most common values?
609 mcvsel = mcv_selectivity(&vardata, &contproc, constval, varonleft,
610 &mcvsum);
613 * If the histogram is large enough, see what fraction of it the
614 * constant is "<@" to, and assume that's representative of the
615 * non-MCV population. Otherwise use the default selectivity for the
616 * non-MCV population.
618 selec = histogram_selectivity(&vardata, &contproc,
619 constval, varonleft,
620 10, 1, &hist_size);
621 if (selec < 0)
623 /* Nope, fall back on default */
624 selec = DEFAULT_PARENT_SEL;
626 else if (hist_size < 100)
629 * For histogram sizes from 10 to 100, we combine the
630 * histogram and default selectivities, putting increasingly
631 * more trust in the histogram for larger sizes.
633 double hist_weight = hist_size / 100.0;
635 selec = selec * hist_weight +
636 DEFAULT_PARENT_SEL * (1.0 - hist_weight);
639 /* In any case, don't believe extremely small or large estimates. */
640 if (selec < 0.0001)
641 selec = 0.0001;
642 else if (selec > 0.9999)
643 selec = 0.9999;
645 if (HeapTupleIsValid(vardata.statsTuple))
646 nullfrac = ((Form_pg_statistic) GETSTRUCT(vardata.statsTuple))->stanullfrac;
647 else
648 nullfrac = 0.0;
651 * Now merge the results from the MCV and histogram calculations,
652 * realizing that the histogram covers only the non-null values that
653 * are not listed in MCV.
655 selec *= 1.0 - nullfrac - mcvsum;
656 selec += mcvsel;
658 else
659 selec = DEFAULT_PARENT_SEL;
661 ReleaseVariableStats(vardata);
663 /* result should be in range, but make sure... */
664 CLAMP_PROBABILITY(selec);
666 PG_RETURN_FLOAT8((float8) selec);