2 * op function for ltree
3 * Teodor Sigaev <teodor@stack.net>
4 * contrib/ltree/ltree_op.c
10 #include "common/hashfn.h"
12 #include "utils/builtins.h"
13 #include "utils/selfuncs.h"
18 /* compare functions */
19 PG_FUNCTION_INFO_V1(ltree_cmp
);
20 PG_FUNCTION_INFO_V1(ltree_lt
);
21 PG_FUNCTION_INFO_V1(ltree_le
);
22 PG_FUNCTION_INFO_V1(ltree_eq
);
23 PG_FUNCTION_INFO_V1(ltree_ne
);
24 PG_FUNCTION_INFO_V1(ltree_ge
);
25 PG_FUNCTION_INFO_V1(ltree_gt
);
26 PG_FUNCTION_INFO_V1(hash_ltree
);
27 PG_FUNCTION_INFO_V1(hash_ltree_extended
);
28 PG_FUNCTION_INFO_V1(nlevel
);
29 PG_FUNCTION_INFO_V1(ltree_isparent
);
30 PG_FUNCTION_INFO_V1(ltree_risparent
);
31 PG_FUNCTION_INFO_V1(subltree
);
32 PG_FUNCTION_INFO_V1(subpath
);
33 PG_FUNCTION_INFO_V1(ltree_index
);
34 PG_FUNCTION_INFO_V1(ltree_addltree
);
35 PG_FUNCTION_INFO_V1(ltree_addtext
);
36 PG_FUNCTION_INFO_V1(ltree_textadd
);
37 PG_FUNCTION_INFO_V1(lca
);
38 PG_FUNCTION_INFO_V1(ltree2text
);
39 PG_FUNCTION_INFO_V1(text2ltree
);
40 PG_FUNCTION_INFO_V1(ltreeparentsel
);
43 ltree_compare(const ltree
*a
, const ltree
*b
)
45 ltree_level
*al
= LTREE_FIRST(a
);
46 ltree_level
*bl
= LTREE_FIRST(b
);
50 while (an
> 0 && bn
> 0)
54 if ((res
= memcmp(al
->name
, bl
->name
, Min(al
->len
, bl
->len
))) == 0)
56 if (al
->len
!= bl
->len
)
57 return (al
->len
- bl
->len
) * 10 * (an
+ 1);
65 return res
* 10 * (an
+ 1);
74 return (a
->numlevel
- b
->numlevel
) * 10 * (an
+ 1);
78 ltree *a = PG_GETARG_LTREE_P(0); \
79 ltree *b = PG_GETARG_LTREE_P(1); \
80 int res = ltree_compare(a,b); \
81 PG_FREE_IF_COPY(a,0); \
85 ltree_cmp(PG_FUNCTION_ARGS
)
92 ltree_lt(PG_FUNCTION_ARGS
)
95 PG_RETURN_BOOL(res
< 0);
99 ltree_le(PG_FUNCTION_ARGS
)
102 PG_RETURN_BOOL(res
<= 0);
106 ltree_eq(PG_FUNCTION_ARGS
)
109 PG_RETURN_BOOL(res
== 0);
113 ltree_ge(PG_FUNCTION_ARGS
)
116 PG_RETURN_BOOL(res
>= 0);
120 ltree_gt(PG_FUNCTION_ARGS
)
123 PG_RETURN_BOOL(res
> 0);
127 ltree_ne(PG_FUNCTION_ARGS
)
130 PG_RETURN_BOOL(res
!= 0);
133 /* Compute a hash for the ltree */
135 hash_ltree(PG_FUNCTION_ARGS
)
137 ltree
*a
= PG_GETARG_LTREE_P(0);
139 int an
= a
->numlevel
;
140 ltree_level
*al
= LTREE_FIRST(a
);
144 uint32 levelHash
= DatumGetUInt32(hash_any((unsigned char *) al
->name
, al
->len
));
147 * Combine hash values of successive elements by multiplying the
148 * current value by 31 and adding on the new element's hash value.
150 * This method is borrowed from hash_array(), which see for further
153 result
= (result
<< 5) - result
+ levelHash
;
159 PG_FREE_IF_COPY(a
, 0);
160 PG_RETURN_UINT32(result
);
163 /* Compute an extended hash for the ltree */
165 hash_ltree_extended(PG_FUNCTION_ARGS
)
167 ltree
*a
= PG_GETARG_LTREE_P(0);
168 const uint64 seed
= PG_GETARG_INT64(1);
170 int an
= a
->numlevel
;
171 ltree_level
*al
= LTREE_FIRST(a
);
174 * If the path has length zero, return 1 + seed to ensure that the low 32
175 * bits of the result match hash_ltree when the seed is 0, as required by
176 * the hash index support functions, but to also return a different value
177 * when there is a seed.
181 PG_FREE_IF_COPY(a
, 0);
182 PG_RETURN_UINT64(result
+ seed
);
187 uint64 levelHash
= DatumGetUInt64(hash_any_extended((unsigned char *) al
->name
, al
->len
, seed
));
189 result
= (result
<< 5) - result
+ levelHash
;
195 PG_FREE_IF_COPY(a
, 0);
196 PG_RETURN_UINT64(result
);
200 nlevel(PG_FUNCTION_ARGS
)
202 ltree
*a
= PG_GETARG_LTREE_P(0);
203 int res
= a
->numlevel
;
205 PG_FREE_IF_COPY(a
, 0);
206 PG_RETURN_INT32(res
);
210 inner_isparent(const ltree
*c
, const ltree
*p
)
212 ltree_level
*cl
= LTREE_FIRST(c
);
213 ltree_level
*pl
= LTREE_FIRST(p
);
214 int pn
= p
->numlevel
;
216 if (pn
> c
->numlevel
)
221 if (cl
->len
!= pl
->len
)
223 if (memcmp(cl
->name
, pl
->name
, cl
->len
) != 0)
234 ltree_isparent(PG_FUNCTION_ARGS
)
236 ltree
*c
= PG_GETARG_LTREE_P(1);
237 ltree
*p
= PG_GETARG_LTREE_P(0);
238 bool res
= inner_isparent(c
, p
);
240 PG_FREE_IF_COPY(c
, 1);
241 PG_FREE_IF_COPY(p
, 0);
246 ltree_risparent(PG_FUNCTION_ARGS
)
248 ltree
*c
= PG_GETARG_LTREE_P(0);
249 ltree
*p
= PG_GETARG_LTREE_P(1);
250 bool res
= inner_isparent(c
, p
);
252 PG_FREE_IF_COPY(c
, 0);
253 PG_FREE_IF_COPY(p
, 1);
259 inner_subltree(ltree
*t
, int32 startpos
, int32 endpos
)
263 ltree_level
*ptr
= LTREE_FIRST(t
);
267 if (startpos
< 0 || endpos
< 0 || startpos
>= t
->numlevel
|| startpos
> endpos
)
269 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
270 errmsg("invalid positions")));
272 if (endpos
> t
->numlevel
)
273 endpos
= t
->numlevel
;
275 start
= end
= (char *) ptr
;
276 for (i
= 0; i
< endpos
; i
++)
279 start
= (char *) ptr
;
282 end
= (char *) LEVEL_NEXT(ptr
);
285 ptr
= LEVEL_NEXT(ptr
);
288 res
= (ltree
*) palloc0(LTREE_HDRSIZE
+ (end
- start
));
289 SET_VARSIZE(res
, LTREE_HDRSIZE
+ (end
- start
));
290 res
->numlevel
= endpos
- startpos
;
292 memcpy(LTREE_FIRST(res
), start
, end
- start
);
298 subltree(PG_FUNCTION_ARGS
)
300 ltree
*t
= PG_GETARG_LTREE_P(0);
301 ltree
*res
= inner_subltree(t
, PG_GETARG_INT32(1), PG_GETARG_INT32(2));
303 PG_FREE_IF_COPY(t
, 0);
304 PG_RETURN_POINTER(res
);
308 subpath(PG_FUNCTION_ARGS
)
310 ltree
*t
= PG_GETARG_LTREE_P(0);
311 int32 start
= PG_GETARG_INT32(1);
312 int32 len
= (fcinfo
->nargs
== 3) ? PG_GETARG_INT32(2) : 0;
320 start
= t
->numlevel
+ start
;
324 { /* start > t->numlevel */
325 start
= t
->numlevel
+ start
;
330 end
= t
->numlevel
+ len
;
332 end
= (fcinfo
->nargs
== 3) ? start
: 0xffff;
334 res
= inner_subltree(t
, start
, end
);
336 PG_FREE_IF_COPY(t
, 0);
337 PG_RETURN_POINTER(res
);
341 ltree_concat(ltree
*a
, ltree
*b
)
344 int numlevel
= (int) a
->numlevel
+ b
->numlevel
;
346 if (numlevel
> LTREE_MAX_LEVELS
)
348 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
349 errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)",
350 numlevel
, LTREE_MAX_LEVELS
)));
352 r
= (ltree
*) palloc0(VARSIZE(a
) + VARSIZE(b
) - LTREE_HDRSIZE
);
353 SET_VARSIZE(r
, VARSIZE(a
) + VARSIZE(b
) - LTREE_HDRSIZE
);
354 r
->numlevel
= (uint16
) numlevel
;
356 memcpy(LTREE_FIRST(r
), LTREE_FIRST(a
), VARSIZE(a
) - LTREE_HDRSIZE
);
357 memcpy(((char *) LTREE_FIRST(r
)) + VARSIZE(a
) - LTREE_HDRSIZE
,
359 VARSIZE(b
) - LTREE_HDRSIZE
);
364 ltree_addltree(PG_FUNCTION_ARGS
)
366 ltree
*a
= PG_GETARG_LTREE_P(0);
367 ltree
*b
= PG_GETARG_LTREE_P(1);
370 r
= ltree_concat(a
, b
);
371 PG_FREE_IF_COPY(a
, 0);
372 PG_FREE_IF_COPY(b
, 1);
373 PG_RETURN_POINTER(r
);
377 ltree_addtext(PG_FUNCTION_ARGS
)
379 ltree
*a
= PG_GETARG_LTREE_P(0);
380 text
*b
= PG_GETARG_TEXT_PP(1);
385 s
= text_to_cstring(b
);
387 tmp
= (ltree
*) DatumGetPointer(DirectFunctionCall1(ltree_in
,
388 PointerGetDatum(s
)));
392 r
= ltree_concat(a
, tmp
);
396 PG_FREE_IF_COPY(a
, 0);
397 PG_FREE_IF_COPY(b
, 1);
398 PG_RETURN_POINTER(r
);
402 ltree_index(PG_FUNCTION_ARGS
)
404 ltree
*a
= PG_GETARG_LTREE_P(0);
405 ltree
*b
= PG_GETARG_LTREE_P(1);
406 int start
= (fcinfo
->nargs
== 3) ? PG_GETARG_INT32(2) : 0;
409 ltree_level
*startptr
,
416 if (-start
>= a
->numlevel
)
419 start
= (int) (a
->numlevel
) + start
;
422 if (a
->numlevel
- start
< b
->numlevel
|| a
->numlevel
== 0 || b
->numlevel
== 0)
424 PG_FREE_IF_COPY(a
, 0);
425 PG_FREE_IF_COPY(b
, 1);
429 startptr
= LTREE_FIRST(a
);
430 for (i
= 0; i
<= a
->numlevel
- b
->numlevel
; i
++)
435 bptr
= LTREE_FIRST(b
);
436 for (j
= 0; j
< b
->numlevel
; j
++)
438 if (!(aptr
->len
== bptr
->len
&& memcmp(aptr
->name
, bptr
->name
, aptr
->len
) == 0))
440 aptr
= LEVEL_NEXT(aptr
);
441 bptr
= LEVEL_NEXT(bptr
);
444 if (j
== b
->numlevel
)
450 startptr
= LEVEL_NEXT(startptr
);
456 PG_FREE_IF_COPY(a
, 0);
457 PG_FREE_IF_COPY(b
, 1);
462 ltree_textadd(PG_FUNCTION_ARGS
)
464 ltree
*a
= PG_GETARG_LTREE_P(1);
465 text
*b
= PG_GETARG_TEXT_PP(0);
470 s
= text_to_cstring(b
);
472 tmp
= (ltree
*) DatumGetPointer(DirectFunctionCall1(ltree_in
,
473 PointerGetDatum(s
)));
477 r
= ltree_concat(tmp
, a
);
481 PG_FREE_IF_COPY(a
, 1);
482 PG_FREE_IF_COPY(b
, 0);
483 PG_RETURN_POINTER(r
);
487 * Common code for variants of lca(), find longest common ancestor of inputs
489 * Returns NULL if there is no common ancestor, ie, the longest common
493 lca_inner(ltree
**a
, int len
)
505 return NULL
; /* no inputs? */
506 if ((*a
)->numlevel
== 0)
507 return NULL
; /* any empty input means NULL result */
509 /* num is the length of the longest common ancestor so far */
510 num
= (*a
)->numlevel
- 1;
512 /* Compare each additional input to *a */
514 while (ptr
- a
< len
)
516 if ((*ptr
)->numlevel
== 0)
518 else if ((*ptr
)->numlevel
== 1)
522 l1
= LTREE_FIRST(*a
);
523 l2
= LTREE_FIRST(*ptr
);
524 tmp
= Min(num
, (*ptr
)->numlevel
- 1);
526 for (i
= 0; i
< tmp
; i
++)
528 if (l1
->len
== l2
->len
&&
529 memcmp(l1
->name
, l2
->name
, l1
->len
) == 0)
540 /* Now compute size of result ... */
541 reslen
= LTREE_HDRSIZE
;
542 l1
= LTREE_FIRST(*a
);
543 for (i
= 0; i
< num
; i
++)
545 reslen
+= MAXALIGN(l1
->len
+ LEVEL_HDRSIZE
);
549 /* ... and construct it by copying from *a */
550 res
= (ltree
*) palloc0(reslen
);
551 SET_VARSIZE(res
, reslen
);
554 l1
= LTREE_FIRST(*a
);
555 l2
= LTREE_FIRST(res
);
557 for (i
= 0; i
< num
; i
++)
559 memcpy(l2
, l1
, MAXALIGN(l1
->len
+ LEVEL_HDRSIZE
));
568 lca(PG_FUNCTION_ARGS
)
574 a
= (ltree
**) palloc(sizeof(ltree
*) * fcinfo
->nargs
);
575 for (i
= 0; i
< fcinfo
->nargs
; i
++)
576 a
[i
] = PG_GETARG_LTREE_P(i
);
577 res
= lca_inner(a
, (int) fcinfo
->nargs
);
578 for (i
= 0; i
< fcinfo
->nargs
; i
++)
579 PG_FREE_IF_COPY(a
[i
], i
);
583 PG_RETURN_POINTER(res
);
589 text2ltree(PG_FUNCTION_ARGS
)
591 text
*in
= PG_GETARG_TEXT_PP(0);
595 s
= text_to_cstring(in
);
597 out
= (ltree
*) DatumGetPointer(DirectFunctionCall1(ltree_in
,
598 PointerGetDatum(s
)));
600 PG_FREE_IF_COPY(in
, 0);
601 PG_RETURN_POINTER(out
);
606 ltree2text(PG_FUNCTION_ARGS
)
608 ltree
*in
= PG_GETARG_LTREE_P(0);
611 ltree_level
*curlevel
;
614 out
= (text
*) palloc(VARSIZE(in
) + VARHDRSZ
);
616 curlevel
= LTREE_FIRST(in
);
617 for (i
= 0; i
< in
->numlevel
; i
++)
624 memcpy(ptr
, curlevel
->name
, curlevel
->len
);
625 ptr
+= curlevel
->len
;
626 curlevel
= LEVEL_NEXT(curlevel
);
629 SET_VARSIZE(out
, ptr
- ((char *) out
));
630 PG_FREE_IF_COPY(in
, 0);
632 PG_RETURN_POINTER(out
);
637 * ltreeparentsel - Selectivity of parent relationship for ltree data types.
639 * This function is not used anymore, if the ltree extension has been
640 * updated to 1.2 or later.
643 ltreeparentsel(PG_FUNCTION_ARGS
)
645 PlannerInfo
*root
= (PlannerInfo
*) PG_GETARG_POINTER(0);
646 Oid
operator = PG_GETARG_OID(1);
647 List
*args
= (List
*) PG_GETARG_POINTER(2);
648 int varRelid
= PG_GETARG_INT32(3);
651 /* Use generic restriction selectivity logic, with default 0.001. */
652 selec
= generic_restriction_selectivity(root
, operator, InvalidOid
,
656 PG_RETURN_FLOAT8((float8
) selec
);