2 * op function for ltree
3 * Teodor Sigaev <teodor@stack.net>
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"
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
);
61 ltree_compare(const ltree
* a
, const ltree
* b
)
63 ltree_level
*al
= LTREE_FIRST(a
);
64 ltree_level
*bl
= LTREE_FIRST(b
);
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);
77 return res
* 10 * (an
+ 1);
85 return (a
->numlevel
- b
->numlevel
) * 10 * (an
+ 1);
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); \
96 ltree_cmp(PG_FUNCTION_ARGS
)
103 ltree_lt(PG_FUNCTION_ARGS
)
106 PG_RETURN_BOOL((res
< 0) ? true : false);
110 ltree_le(PG_FUNCTION_ARGS
)
113 PG_RETURN_BOOL((res
<= 0) ? true : false);
117 ltree_eq(PG_FUNCTION_ARGS
)
120 PG_RETURN_BOOL((res
== 0) ? true : false);
124 ltree_ge(PG_FUNCTION_ARGS
)
127 PG_RETURN_BOOL((res
>= 0) ? true : false);
131 ltree_gt(PG_FUNCTION_ARGS
)
134 PG_RETURN_BOOL((res
> 0) ? true : false);
138 ltree_ne(PG_FUNCTION_ARGS
)
141 PG_RETURN_BOOL((res
!= 0) ? true : false);
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
);
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
)
166 if (cl
->len
!= pl
->len
)
168 if (strncmp(cl
->name
, pl
->name
, cl
->len
))
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);
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);
204 inner_subltree(ltree
* t
, int4 startpos
, int4 endpos
)
208 ltree_level
*ptr
= LTREE_FIRST(t
);
212 if (startpos
< 0 || endpos
< 0 || startpos
>= t
->numlevel
|| startpos
> endpos
)
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
++)
224 start
= (char *) ptr
;
227 end
= (char *) LEVEL_NEXT(ptr
);
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
);
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
);
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;
265 start
= t
->numlevel
+ start
;
269 { /* start > t->numlevel */
270 start
= t
->numlevel
+ start
;
275 end
= t
->numlevel
+ len
;
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
);
286 ltree_concat(ltree
* a
, ltree
* b
)
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
,
297 VARSIZE(b
) - LTREE_HDRSIZE
);
302 ltree_addltree(PG_FUNCTION_ARGS
)
304 ltree
*a
= PG_GETARG_LTREE(0);
305 ltree
*b
= PG_GETARG_LTREE(1);
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
);
315 ltree_addtext(PG_FUNCTION_ARGS
)
317 ltree
*a
= PG_GETARG_LTREE(0);
318 text
*b
= PG_GETARG_TEXT_PP(1);
323 s
= text_to_cstring(b
);
325 tmp
= (ltree
*) DatumGetPointer(DirectFunctionCall1(ltree_in
,
326 PointerGetDatum(s
)));
330 r
= ltree_concat(a
, tmp
);
334 PG_FREE_IF_COPY(a
, 0);
335 PG_FREE_IF_COPY(b
, 1);
336 PG_RETURN_POINTER(r
);
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;
347 ltree_level
*startptr
,
354 if (-start
>= a
->numlevel
)
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);
367 startptr
= LTREE_FIRST(a
);
368 for (i
= 0; i
<= a
->numlevel
- b
->numlevel
; i
++)
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))
378 aptr
= LEVEL_NEXT(aptr
);
379 bptr
= LEVEL_NEXT(bptr
);
382 if (j
== b
->numlevel
)
388 startptr
= LEVEL_NEXT(startptr
);
394 PG_FREE_IF_COPY(a
, 0);
395 PG_FREE_IF_COPY(b
, 1);
400 ltree_textadd(PG_FUNCTION_ARGS
)
402 ltree
*a
= PG_GETARG_LTREE(1);
403 text
*b
= PG_GETARG_TEXT_PP(0);
408 s
= text_to_cstring(b
);
410 tmp
= (ltree
*) DatumGetPointer(DirectFunctionCall1(ltree_in
,
411 PointerGetDatum(s
)));
415 r
= ltree_concat(tmp
, a
);
419 PG_FREE_IF_COPY(a
, 1);
420 PG_FREE_IF_COPY(b
, 0);
421 PG_RETURN_POINTER(r
);
425 lca_inner(ltree
** a
, int len
)
428 num
= ((*a
)->numlevel
) ? (*a
)->numlevel
- 1 : 0;
431 reslen
= LTREE_HDRSIZE
;
437 if ((*a
)->numlevel
== 0)
440 while (ptr
- a
< len
)
442 if ((*ptr
)->numlevel
== 0)
444 else if ((*ptr
)->numlevel
== 1)
448 l1
= LTREE_FIRST(*a
);
449 l2
= LTREE_FIRST(*ptr
);
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)
465 l1
= LTREE_FIRST(*a
);
466 for (i
= 0; i
< num
; i
++)
468 reslen
+= MAXALIGN(l1
->len
+ LEVEL_HDRSIZE
);
472 res
= (ltree
*) palloc(reslen
);
473 SET_VARSIZE(res
, reslen
);
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
));
490 lca(PG_FUNCTION_ARGS
)
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
);
505 PG_RETURN_POINTER(res
);
511 text2ltree(PG_FUNCTION_ARGS
)
513 text
*in
= PG_GETARG_TEXT_PP(0);
517 s
= text_to_cstring(in
);
519 out
= (ltree
*) DatumGetPointer(DirectFunctionCall1(ltree_in
,
520 PointerGetDatum(s
)));
522 PG_FREE_IF_COPY(in
, 0);
523 PG_RETURN_POINTER(out
);
528 ltree2text(PG_FUNCTION_ARGS
)
530 ltree
*in
= PG_GETARG_LTREE(0);
533 ltree_level
*curlevel
;
536 out
= (text
*) palloc(VARSIZE(in
) + VARHDRSZ
);
538 curlevel
= LTREE_FIRST(in
);
539 for (i
= 0; i
< in
->numlevel
; i
++)
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.
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
;
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
;
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
,
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
,
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. */
642 else if (selec
> 0.9999)
645 if (HeapTupleIsValid(vardata
.statsTuple
))
646 nullfrac
= ((Form_pg_statistic
) GETSTRUCT(vardata
.statsTuple
))->stanullfrac
;
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
;
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
);