Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * op function for ltree
3 : : * Teodor Sigaev <teodor@stack.net>
4 : : * contrib/ltree/ltree_op.c
5 : : */
6 : : #include "postgres.h"
7 : :
8 : : #include <ctype.h>
9 : :
10 : : #include "common/hashfn.h"
11 : : #include "ltree.h"
12 : : #include "utils/builtins.h"
13 : : #include "utils/selfuncs.h"
14 : : #include "varatt.h"
15 : :
461 tgl@sss.pgh.pa.us 16 :CBC 3 : PG_MODULE_MAGIC_EXT(
17 : : .name = "ltree",
18 : : .version = PG_VERSION
19 : : );
20 : :
21 : : /* compare functions */
8736 bruce@momjian.us 22 : 3 : PG_FUNCTION_INFO_V1(ltree_cmp);
23 : 3 : PG_FUNCTION_INFO_V1(ltree_lt);
24 : 3 : PG_FUNCTION_INFO_V1(ltree_le);
25 : 3 : PG_FUNCTION_INFO_V1(ltree_eq);
26 : 2 : PG_FUNCTION_INFO_V1(ltree_ne);
27 : 3 : PG_FUNCTION_INFO_V1(ltree_ge);
28 : 3 : PG_FUNCTION_INFO_V1(ltree_gt);
831 tgl@sss.pgh.pa.us 29 : 3 : PG_FUNCTION_INFO_V1(hash_ltree);
30 : 3 : PG_FUNCTION_INFO_V1(hash_ltree_extended);
8736 bruce@momjian.us 31 : 3 : PG_FUNCTION_INFO_V1(nlevel);
32 : 3 : PG_FUNCTION_INFO_V1(ltree_isparent);
33 : 3 : PG_FUNCTION_INFO_V1(ltree_risparent);
34 : 3 : PG_FUNCTION_INFO_V1(subltree);
35 : 6 : PG_FUNCTION_INFO_V1(subpath);
8492 36 : 6 : PG_FUNCTION_INFO_V1(ltree_index);
8736 37 : 3 : PG_FUNCTION_INFO_V1(ltree_addltree);
38 : 3 : PG_FUNCTION_INFO_V1(ltree_addtext);
39 : 2 : PG_FUNCTION_INFO_V1(ltree_textadd);
8731 40 : 16 : PG_FUNCTION_INFO_V1(lca);
8492 41 : 3 : PG_FUNCTION_INFO_V1(ltree2text);
42 : 3 : PG_FUNCTION_INFO_V1(text2ltree);
7369 tgl@sss.pgh.pa.us 43 : 2 : PG_FUNCTION_INFO_V1(ltreeparentsel);
44 : :
45 : : /*
46 : : * btree-comparison function.
47 : : */
48 : : int
6228 bruce@momjian.us 49 : 99092 : ltree_compare(const ltree *a, const ltree *b)
50 : : {
8736 51 : 99092 : ltree_level *al = LTREE_FIRST(a);
52 : 99092 : ltree_level *bl = LTREE_FIRST(b);
8700 53 : 99092 : int an = a->numlevel;
54 : 99092 : int bn = b->numlevel;
55 : :
56 [ + + + + ]: 183731 : while (an > 0 && bn > 0)
57 : : {
58 : : int res;
59 : :
14 heikki.linnakangas@i 60 : 174506 : res = memcmp(al->name, bl->name, Min(al->len, bl->len));
61 [ + + ]: 174506 : if (res == 0)
62 : : {
63 [ + + ]: 90840 : if (al->len != bl->len)
64 : 6201 : return (int) al->len - (int) bl->len;
65 : : }
66 : : else
67 : 83666 : return res;
68 : :
69 : 84639 : an--;
70 : 84639 : bn--;
71 : 84639 : al = LEVEL_NEXT(al);
72 : 84639 : bl = LEVEL_NEXT(bl);
73 : : }
74 : :
75 : 9225 : return a->numlevel - b->numlevel;
76 : : }
77 : :
78 : : /*
79 : : * Returns a "distance" between a and b. If a < b, the distance is negative,
80 : : * consistent with the ltree_compare() ordering.
81 : : */
82 : : float
83 : 19150 : ltree_compare_distance(const ltree *a, const ltree *b)
84 : : {
85 : 19150 : ltree_level *al = LTREE_FIRST(a);
86 : 19150 : ltree_level *bl = LTREE_FIRST(b);
87 : 19150 : int an = a->numlevel;
88 : 19150 : int bn = b->numlevel;
89 : :
90 [ + + + - ]: 19921 : while (an > 0 && bn > 0)
91 : : {
92 : : int res;
93 : :
94 : 17756 : res = memcmp(al->name, bl->name, Min(al->len, bl->len));
95 [ + + ]: 17756 : if (res == 0)
96 : : {
8700 bruce@momjian.us 97 [ + + ]: 1815 : if (al->len != bl->len)
14 heikki.linnakangas@i 98 : 1044 : return (float) (al->len - bl->len) * 10.0 * (an + 1);
99 : : }
100 : : else
101 : : {
2825 tgl@sss.pgh.pa.us 102 [ + + ]: 15941 : if (res < 0)
14 heikki.linnakangas@i 103 : 10346 : return -1.0 * 10.0 * (an + 1);
104 : : else
105 : 5595 : return 1.0 * 10.0 * (an + 1);
106 : : }
107 : :
8700 bruce@momjian.us 108 : 771 : an--;
109 : 771 : bn--;
110 : 771 : al = LEVEL_NEXT(al);
111 : 771 : bl = LEVEL_NEXT(bl);
112 : : }
113 : :
14 heikki.linnakangas@i 114 : 2165 : return ((float) (a->numlevel - b->numlevel)) * 10.0 * (an + 1);
115 : : }
116 : :
117 : : #define RUNCMP \
118 : : ltree *a = PG_GETARG_LTREE_P(0); \
119 : : ltree *b = PG_GETARG_LTREE_P(1); \
120 : : int res = ltree_compare(a,b); \
121 : : PG_FREE_IF_COPY(a,0); \
122 : : PG_FREE_IF_COPY(b,1)
123 : :
124 : : Datum
8700 bruce@momjian.us 125 : 51231 : ltree_cmp(PG_FUNCTION_ARGS)
126 : : {
3207 tgl@sss.pgh.pa.us 127 [ + - + + ]: 51231 : RUNCMP;
128 : 51231 : PG_RETURN_INT32(res);
129 : : }
130 : :
131 : : Datum
8700 bruce@momjian.us 132 : 2141 : ltree_lt(PG_FUNCTION_ARGS)
133 : : {
3207 tgl@sss.pgh.pa.us 134 [ + + - + ]: 2141 : RUNCMP;
1723 michael@paquier.xyz 135 : 2141 : PG_RETURN_BOOL(res < 0);
136 : : }
137 : :
138 : : Datum
8700 bruce@momjian.us 139 : 2141 : ltree_le(PG_FUNCTION_ARGS)
140 : : {
3207 tgl@sss.pgh.pa.us 141 [ + + - + ]: 2141 : RUNCMP;
1723 michael@paquier.xyz 142 : 2141 : PG_RETURN_BOOL(res <= 0);
143 : : }
144 : :
145 : : Datum
8700 bruce@momjian.us 146 : 2017 : ltree_eq(PG_FUNCTION_ARGS)
147 : : {
3207 tgl@sss.pgh.pa.us 148 [ + + + + ]: 2017 : RUNCMP;
1723 michael@paquier.xyz 149 : 2017 : PG_RETURN_BOOL(res == 0);
150 : : }
151 : :
152 : : Datum
8700 bruce@momjian.us 153 : 2923 : ltree_ge(PG_FUNCTION_ARGS)
154 : : {
3207 tgl@sss.pgh.pa.us 155 [ + + - + ]: 2923 : RUNCMP;
1723 michael@paquier.xyz 156 : 2923 : PG_RETURN_BOOL(res >= 0);
157 : : }
158 : :
159 : : Datum
8700 bruce@momjian.us 160 : 2923 : ltree_gt(PG_FUNCTION_ARGS)
161 : : {
3207 tgl@sss.pgh.pa.us 162 [ + + - + ]: 2923 : RUNCMP;
1723 michael@paquier.xyz 163 : 2923 : PG_RETURN_BOOL(res > 0);
164 : : }
165 : :
166 : : Datum
8700 bruce@momjian.us 167 :UBC 0 : ltree_ne(PG_FUNCTION_ARGS)
168 : : {
3207 tgl@sss.pgh.pa.us 169 [ # # # # ]: 0 : RUNCMP;
1723 michael@paquier.xyz 170 : 0 : PG_RETURN_BOOL(res != 0);
171 : : }
172 : :
173 : : /* Compute a hash for the ltree */
174 : : Datum
831 tgl@sss.pgh.pa.us 175 :CBC 3031 : hash_ltree(PG_FUNCTION_ARGS)
176 : : {
177 : 3031 : ltree *a = PG_GETARG_LTREE_P(0);
178 : 3031 : uint32 result = 1;
179 : 3031 : int an = a->numlevel;
180 : 3031 : ltree_level *al = LTREE_FIRST(a);
181 : :
182 [ + + ]: 22872 : while (an > 0)
183 : : {
184 : 19841 : uint32 levelHash = DatumGetUInt32(hash_any((unsigned char *) al->name, al->len));
185 : :
186 : : /*
187 : : * Combine hash values of successive elements by multiplying the
188 : : * current value by 31 and adding on the new element's hash value.
189 : : *
190 : : * This method is borrowed from hash_array(), which see for further
191 : : * commentary.
192 : : */
193 : 19841 : result = (result << 5) - result + levelHash;
194 : :
195 : 19841 : an--;
196 : 19841 : al = LEVEL_NEXT(al);
197 : : }
198 : :
199 [ + + ]: 3031 : PG_FREE_IF_COPY(a, 0);
200 : 3031 : PG_RETURN_UINT32(result);
201 : : }
202 : :
203 : : /* Compute an extended hash for the ltree */
204 : : Datum
205 : 12 : hash_ltree_extended(PG_FUNCTION_ARGS)
206 : : {
207 : 12 : ltree *a = PG_GETARG_LTREE_P(0);
208 : 12 : const uint64 seed = PG_GETARG_INT64(1);
209 : 12 : uint64 result = 1;
210 : 12 : int an = a->numlevel;
211 : 12 : ltree_level *al = LTREE_FIRST(a);
212 : :
213 : : /*
214 : : * If the path has length zero, return 1 + seed to ensure that the low 32
215 : : * bits of the result match hash_ltree when the seed is 0, as required by
216 : : * the hash index support functions, but to also return a different value
217 : : * when there is a seed.
218 : : */
219 [ + + ]: 12 : if (an == 0)
220 : : {
221 [ - + ]: 2 : PG_FREE_IF_COPY(a, 0);
222 : 2 : PG_RETURN_UINT64(result + seed);
223 : : }
224 : :
225 [ + + ]: 28 : while (an > 0)
226 : : {
227 : 18 : uint64 levelHash = DatumGetUInt64(hash_any_extended((unsigned char *) al->name, al->len, seed));
228 : :
229 : 18 : result = (result << 5) - result + levelHash;
230 : :
231 : 18 : an--;
232 : 18 : al = LEVEL_NEXT(al);
233 : : }
234 : :
235 [ - + ]: 10 : PG_FREE_IF_COPY(a, 0);
236 : 10 : PG_RETURN_UINT64(result);
237 : : }
238 : :
239 : : Datum
8700 bruce@momjian.us 240 : 2 : nlevel(PG_FUNCTION_ARGS)
241 : : {
3207 tgl@sss.pgh.pa.us 242 : 2 : ltree *a = PG_GETARG_LTREE_P(0);
8700 bruce@momjian.us 243 : 2 : int res = a->numlevel;
244 : :
245 [ - + ]: 2 : PG_FREE_IF_COPY(a, 0);
8736 246 : 2 : PG_RETURN_INT32(res);
247 : : }
248 : :
249 : : bool
6228 250 : 21186 : inner_isparent(const ltree *c, const ltree *p)
251 : : {
8736 252 : 21186 : ltree_level *cl = LTREE_FIRST(c);
253 : 21186 : ltree_level *pl = LTREE_FIRST(p);
8700 254 : 21186 : int pn = p->numlevel;
255 : :
256 [ + + ]: 21186 : if (pn > c->numlevel)
8736 257 : 10165 : return false;
258 : :
8700 259 [ + + ]: 12018 : while (pn > 0)
260 : : {
261 [ + + ]: 11882 : if (cl->len != pl->len)
8736 262 : 7959 : return false;
2825 tgl@sss.pgh.pa.us 263 [ + + ]: 3923 : if (memcmp(cl->name, pl->name, cl->len) != 0)
8736 bruce@momjian.us 264 : 2926 : return false;
265 : :
266 : 997 : pn--;
8700 267 : 997 : cl = LEVEL_NEXT(cl);
268 : 997 : pl = LEVEL_NEXT(pl);
269 : : }
8736 270 : 136 : return true;
271 : : }
272 : :
273 : : Datum
8700 274 : 11538 : ltree_isparent(PG_FUNCTION_ARGS)
275 : : {
3207 tgl@sss.pgh.pa.us 276 : 11538 : ltree *c = PG_GETARG_LTREE_P(1);
277 : 11538 : ltree *p = PG_GETARG_LTREE_P(0);
8700 bruce@momjian.us 278 : 11538 : bool res = inner_isparent(c, p);
279 : :
280 [ - + ]: 11538 : PG_FREE_IF_COPY(c, 1);
281 [ + + ]: 11538 : PG_FREE_IF_COPY(p, 0);
282 : 11538 : PG_RETURN_BOOL(res);
283 : : }
284 : :
285 : : Datum
286 : 9320 : ltree_risparent(PG_FUNCTION_ARGS)
287 : : {
3207 tgl@sss.pgh.pa.us 288 : 9320 : ltree *c = PG_GETARG_LTREE_P(0);
289 : 9320 : ltree *p = PG_GETARG_LTREE_P(1);
8700 bruce@momjian.us 290 : 9320 : bool res = inner_isparent(c, p);
291 : :
292 [ + + ]: 9320 : PG_FREE_IF_COPY(c, 0);
293 [ - + ]: 9320 : PG_FREE_IF_COPY(p, 1);
294 : 9320 : PG_RETURN_BOOL(res);
295 : : }
296 : :
297 : :
298 : : static ltree *
5118 peter_e@gmx.net 299 : 10 : inner_subltree(ltree *t, int32 startpos, int32 endpos)
300 : : {
8700 bruce@momjian.us 301 : 10 : char *start = NULL,
302 : 10 : *end = NULL;
8736 303 : 10 : ltree_level *ptr = LTREE_FIRST(t);
304 : : ltree *res;
305 : : int i;
306 : :
8383 teodor@sigaev.ru 307 [ + + + - : 10 : if (startpos < 0 || endpos < 0 || startpos >= t->numlevel || startpos > endpos)
+ - - + ]
8377 tgl@sss.pgh.pa.us 308 [ + - ]:GBC 1 : ereport(ERROR,
309 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
310 : : errmsg("invalid positions")));
311 : :
8700 bruce@momjian.us 312 [ + + ]:CBC 9 : if (endpos > t->numlevel)
8736 313 : 2 : endpos = t->numlevel;
314 : :
8383 teodor@sigaev.ru 315 : 9 : start = end = (char *) ptr;
8700 bruce@momjian.us 316 [ + + ]: 19 : for (i = 0; i < endpos; i++)
317 : : {
318 [ + + ]: 18 : if (i == startpos)
319 : 7 : start = (char *) ptr;
320 [ + + ]: 18 : if (i == endpos - 1)
321 : : {
322 : 8 : end = (char *) LEVEL_NEXT(ptr);
8736 323 : 8 : break;
324 : : }
8700 325 : 10 : ptr = LEVEL_NEXT(ptr);
326 : : }
327 : :
3766 andres@anarazel.de 328 : 9 : res = (ltree *) palloc0(LTREE_HDRSIZE + (end - start));
7062 tgl@sss.pgh.pa.us 329 : 9 : SET_VARSIZE(res, LTREE_HDRSIZE + (end - start));
8700 bruce@momjian.us 330 : 9 : res->numlevel = endpos - startpos;
331 : :
332 : 9 : memcpy(LTREE_FIRST(res), start, end - start);
333 : :
8736 334 : 9 : return res;
335 : : }
336 : :
337 : : Datum
8700 338 : 1 : subltree(PG_FUNCTION_ARGS)
339 : : {
3207 tgl@sss.pgh.pa.us 340 : 1 : ltree *t = PG_GETARG_LTREE_P(0);
8700 bruce@momjian.us 341 : 1 : ltree *res = inner_subltree(t, PG_GETARG_INT32(1), PG_GETARG_INT32(2));
342 : :
343 [ - + ]: 1 : PG_FREE_IF_COPY(t, 0);
8736 344 : 1 : PG_RETURN_POINTER(res);
345 : : }
346 : :
347 : : Datum
8700 348 : 9 : subpath(PG_FUNCTION_ARGS)
349 : : {
3207 tgl@sss.pgh.pa.us 350 : 9 : ltree *t = PG_GETARG_LTREE_P(0);
5118 peter_e@gmx.net 351 : 9 : int32 start = PG_GETARG_INT32(1);
352 [ + + ]: 9 : int32 len = (fcinfo->nargs == 3) ? PG_GETARG_INT32(2) : 0;
353 : : int32 end;
354 : : ltree *res;
355 : :
8700 bruce@momjian.us 356 [ + + ]: 9 : if (start < 0)
8736 bruce@momjian.us 357 :GBC 2 : start = t->numlevel + start;
358 : :
8700 bruce@momjian.us 359 [ + + ]:CBC 9 : if (len < 0)
8736 360 : 2 : end = t->numlevel + len;
8700 361 [ + + ]: 7 : else if (len == 0)
241 tgl@sss.pgh.pa.us 362 [ + + ]:GNC 5 : end = (fcinfo->nargs == 3) ? start : LTREE_MAX_LEVELS;
363 : : else
364 : 2 : end = start + len;
365 : :
8700 bruce@momjian.us 366 :CBC 9 : res = inner_subltree(t, start, end);
367 : :
368 [ - + ]: 8 : PG_FREE_IF_COPY(t, 0);
8736 369 : 8 : PG_RETURN_POINTER(res);
370 : : }
371 : :
372 : : static ltree *
6228 373 : 6 : ltree_concat(ltree *a, ltree *b)
374 : : {
375 : : ltree *r;
2285 tgl@sss.pgh.pa.us 376 : 6 : int numlevel = (int) a->numlevel + b->numlevel;
377 : :
378 [ + + ]: 6 : if (numlevel > LTREE_MAX_LEVELS)
379 [ + - ]: 1 : ereport(ERROR,
380 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
381 : : errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)",
382 : : numlevel, LTREE_MAX_LEVELS)));
383 : :
3766 andres@anarazel.de 384 : 5 : r = (ltree *) palloc0(VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
7062 tgl@sss.pgh.pa.us 385 : 5 : SET_VARSIZE(r, VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
2285 386 : 5 : r->numlevel = (uint16) numlevel;
387 : :
7062 388 : 5 : memcpy(LTREE_FIRST(r), LTREE_FIRST(a), VARSIZE(a) - LTREE_HDRSIZE);
389 : 5 : memcpy(((char *) LTREE_FIRST(r)) + VARSIZE(a) - LTREE_HDRSIZE,
390 : : LTREE_FIRST(b),
391 : 5 : VARSIZE(b) - LTREE_HDRSIZE);
8700 bruce@momjian.us 392 : 5 : return r;
393 : : }
394 : :
395 : : Datum
396 : 5 : ltree_addltree(PG_FUNCTION_ARGS)
397 : : {
3207 tgl@sss.pgh.pa.us 398 : 5 : ltree *a = PG_GETARG_LTREE_P(0);
399 : 5 : ltree *b = PG_GETARG_LTREE_P(1);
400 : : ltree *r;
401 : :
8736 bruce@momjian.us 402 : 5 : r = ltree_concat(a, b);
8700 403 [ - + ]: 4 : PG_FREE_IF_COPY(a, 0);
404 [ - + ]: 4 : PG_FREE_IF_COPY(b, 1);
8736 405 : 4 : PG_RETURN_POINTER(r);
406 : : }
407 : :
408 : : Datum
8700 409 : 1 : ltree_addtext(PG_FUNCTION_ARGS)
410 : : {
3207 tgl@sss.pgh.pa.us 411 : 1 : ltree *a = PG_GETARG_LTREE_P(0);
6671 412 : 1 : text *b = PG_GETARG_TEXT_PP(1);
413 : : char *s;
414 : : ltree *r,
415 : : *tmp;
416 : :
417 : 1 : s = text_to_cstring(b);
418 : :
419 : 1 : tmp = (ltree *) DatumGetPointer(DirectFunctionCall1(ltree_in,
420 : : PointerGetDatum(s)));
421 : :
8736 bruce@momjian.us 422 : 1 : pfree(s);
423 : :
8700 424 : 1 : r = ltree_concat(a, tmp);
425 : :
426 : 1 : pfree(tmp);
427 : :
428 [ - + ]: 1 : PG_FREE_IF_COPY(a, 0);
429 [ - + ]: 1 : PG_FREE_IF_COPY(b, 1);
8736 430 : 1 : PG_RETURN_POINTER(r);
431 : : }
432 : :
433 : : Datum
8492 434 : 18 : ltree_index(PG_FUNCTION_ARGS)
435 : : {
3207 tgl@sss.pgh.pa.us 436 : 18 : ltree *a = PG_GETARG_LTREE_P(0);
437 : 18 : ltree *b = PG_GETARG_LTREE_P(1);
8366 bruce@momjian.us 438 [ + + ]: 18 : int start = (fcinfo->nargs == 3) ? PG_GETARG_INT32(2) : 0;
439 : : int i,
440 : : j;
441 : : ltree_level *startptr,
442 : : *aptr,
443 : : *bptr;
444 : 18 : bool found = false;
445 : :
446 [ + + ]: 18 : if (start < 0)
447 : : {
448 [ + + ]: 5 : if (-start >= a->numlevel)
449 : 1 : start = 0;
450 : : else
451 : 4 : start = (int) (a->numlevel) + start;
452 : : }
453 : :
454 [ + + + - : 18 : if (a->numlevel - start < b->numlevel || a->numlevel == 0 || b->numlevel == 0)
- + ]
455 : : {
8492 456 [ - + ]: 1 : PG_FREE_IF_COPY(a, 0);
457 [ - + ]: 1 : PG_FREE_IF_COPY(b, 1);
458 : 1 : PG_RETURN_INT32(-1);
459 : : }
460 : :
8366 461 : 17 : startptr = LTREE_FIRST(a);
462 [ + + ]: 109 : for (i = 0; i <= a->numlevel - b->numlevel; i++)
463 : : {
464 [ + + ]: 106 : if (i >= start)
465 : : {
466 : 58 : aptr = startptr;
467 : 58 : bptr = LTREE_FIRST(b);
468 [ + + ]: 93 : for (j = 0; j < b->numlevel; j++)
469 : : {
5670 rhaas@postgresql.org 470 [ + - + + ]: 79 : if (!(aptr->len == bptr->len && memcmp(aptr->name, bptr->name, aptr->len) == 0))
471 : : break;
8366 bruce@momjian.us 472 : 35 : aptr = LEVEL_NEXT(aptr);
473 : 35 : bptr = LEVEL_NEXT(bptr);
474 : : }
475 : :
476 [ + + ]: 58 : if (j == b->numlevel)
477 : : {
478 : 14 : found = true;
8492 479 : 14 : break;
480 : : }
481 : : }
8366 482 : 92 : startptr = LEVEL_NEXT(startptr);
483 : : }
484 : :
485 [ + + ]: 17 : if (!found)
486 : 3 : i = -1;
487 : :
8492 488 [ - + ]: 17 : PG_FREE_IF_COPY(a, 0);
489 [ - + ]: 17 : PG_FREE_IF_COPY(b, 1);
490 : 17 : PG_RETURN_INT32(i);
491 : : }
492 : :
493 : : Datum
8700 bruce@momjian.us 494 :UBC 0 : ltree_textadd(PG_FUNCTION_ARGS)
495 : : {
3207 tgl@sss.pgh.pa.us 496 : 0 : ltree *a = PG_GETARG_LTREE_P(1);
6671 497 : 0 : text *b = PG_GETARG_TEXT_PP(0);
498 : : char *s;
499 : : ltree *r,
500 : : *tmp;
501 : :
502 : 0 : s = text_to_cstring(b);
503 : :
504 : 0 : tmp = (ltree *) DatumGetPointer(DirectFunctionCall1(ltree_in,
505 : : PointerGetDatum(s)));
506 : :
8700 bruce@momjian.us 507 : 0 : pfree(s);
508 : :
509 : 0 : r = ltree_concat(tmp, a);
510 : :
511 : 0 : pfree(tmp);
512 : :
513 [ # # ]: 0 : PG_FREE_IF_COPY(a, 1);
514 [ # # ]: 0 : PG_FREE_IF_COPY(b, 0);
515 : 0 : PG_RETURN_POINTER(r);
516 : : }
517 : :
518 : : /*
519 : : * Common code for variants of lca(), find longest common ancestor of inputs
520 : : *
521 : : * Returns NULL if there is no common ancestor, ie, the longest common
522 : : * prefix is empty.
523 : : */
524 : : ltree *
6228 bruce@momjian.us 525 :CBC 14 : lca_inner(ltree **a, int len)
526 : : {
527 : : int tmp,
528 : : num,
529 : : i,
530 : : reslen;
531 : : ltree **ptr;
532 : : ltree_level *l1,
533 : : *l2;
534 : : ltree *res;
535 : :
2909 tgl@sss.pgh.pa.us 536 [ + + ]: 14 : if (len <= 0)
537 : 1 : return NULL; /* no inputs? */
8700 bruce@momjian.us 538 [ + + ]: 13 : if ((*a)->numlevel == 0)
2909 tgl@sss.pgh.pa.us 539 : 1 : return NULL; /* any empty input means NULL result */
540 : :
541 : : /* num is the length of the longest common ancestor so far */
542 : 12 : num = (*a)->numlevel - 1;
543 : :
544 : : /* Compare each additional input to *a */
545 : 12 : ptr = a + 1;
8700 bruce@momjian.us 546 [ + + ]: 23 : while (ptr - a < len)
547 : : {
548 [ + + ]: 12 : if ((*ptr)->numlevel == 0)
8731 549 : 1 : return NULL;
8700 550 [ + + ]: 11 : else if ((*ptr)->numlevel == 1)
551 : 2 : num = 0;
552 : : else
553 : : {
8731 554 : 9 : l1 = LTREE_FIRST(*a);
555 : 9 : l2 = LTREE_FIRST(*ptr);
2909 tgl@sss.pgh.pa.us 556 : 9 : tmp = Min(num, (*ptr)->numlevel - 1);
8700 bruce@momjian.us 557 : 9 : num = 0;
2909 tgl@sss.pgh.pa.us 558 [ + + ]: 23 : for (i = 0; i < tmp; i++)
559 : : {
560 [ + + ]: 21 : if (l1->len == l2->len &&
561 [ + + ]: 18 : memcmp(l1->name, l2->name, l1->len) == 0)
8700 bruce@momjian.us 562 : 14 : num = i + 1;
563 : : else
564 : : break;
565 : 14 : l1 = LEVEL_NEXT(l1);
566 : 14 : l2 = LEVEL_NEXT(l2);
567 : : }
568 : : }
8731 569 : 11 : ptr++;
570 : : }
571 : :
572 : : /* Now compute size of result ... */
2909 tgl@sss.pgh.pa.us 573 : 11 : reslen = LTREE_HDRSIZE;
8731 bruce@momjian.us 574 : 11 : l1 = LTREE_FIRST(*a);
8700 575 [ + + ]: 21 : for (i = 0; i < num; i++)
576 : : {
8731 577 : 10 : reslen += MAXALIGN(l1->len + LEVEL_HDRSIZE);
8700 578 : 10 : l1 = LEVEL_NEXT(l1);
579 : : }
580 : :
581 : : /* ... and construct it by copying from *a */
3766 andres@anarazel.de 582 : 11 : res = (ltree *) palloc0(reslen);
7062 tgl@sss.pgh.pa.us 583 : 11 : SET_VARSIZE(res, reslen);
8731 bruce@momjian.us 584 : 11 : res->numlevel = num;
585 : :
586 : 11 : l1 = LTREE_FIRST(*a);
587 : 11 : l2 = LTREE_FIRST(res);
588 : :
8700 589 [ + + ]: 21 : for (i = 0; i < num; i++)
590 : : {
591 : 10 : memcpy(l2, l1, MAXALIGN(l1->len + LEVEL_HDRSIZE));
592 : 10 : l1 = LEVEL_NEXT(l1);
593 : 10 : l2 = LEVEL_NEXT(l2);
594 : : }
595 : :
8731 596 : 11 : return res;
597 : : }
598 : :
599 : : Datum
8700 600 : 6 : lca(PG_FUNCTION_ARGS)
601 : : {
602 : : int i;
603 : : ltree **a,
604 : : *res;
605 : :
207 michael@paquier.xyz 606 :GNC 6 : a = palloc_array(ltree *, fcinfo->nargs);
8700 bruce@momjian.us 607 [ + + ]:CBC 21 : for (i = 0; i < fcinfo->nargs; i++)
3207 tgl@sss.pgh.pa.us 608 : 15 : a[i] = PG_GETARG_LTREE_P(i);
8700 bruce@momjian.us 609 : 6 : res = lca_inner(a, (int) fcinfo->nargs);
610 [ + + ]: 21 : for (i = 0; i < fcinfo->nargs; i++)
611 [ - + ]: 15 : PG_FREE_IF_COPY(a[i], i);
8731 612 : 6 : pfree(a);
613 : :
8700 614 [ + + ]: 6 : if (res)
8731 615 : 5 : PG_RETURN_POINTER(res);
616 : : else
617 : 1 : PG_RETURN_NULL();
618 : : }
619 : :
620 : : Datum
8492 621 : 1 : text2ltree(PG_FUNCTION_ARGS)
622 : : {
6671 tgl@sss.pgh.pa.us 623 : 1 : text *in = PG_GETARG_TEXT_PP(0);
624 : : char *s;
625 : : ltree *out;
626 : :
627 : 1 : s = text_to_cstring(in);
628 : :
629 : 1 : out = (ltree *) DatumGetPointer(DirectFunctionCall1(ltree_in,
630 : : PointerGetDatum(s)));
8492 bruce@momjian.us 631 : 1 : pfree(s);
8366 632 [ - + ]: 1 : PG_FREE_IF_COPY(in, 0);
8492 633 : 1 : PG_RETURN_POINTER(out);
634 : : }
635 : :
636 : :
637 : : Datum
638 : 1 : ltree2text(PG_FUNCTION_ARGS)
639 : : {
3207 tgl@sss.pgh.pa.us 640 : 1 : ltree *in = PG_GETARG_LTREE_P(0);
641 : : char *ptr;
642 : : int i;
643 : : ltree_level *curlevel;
644 : : text *out;
645 : :
7062 646 : 1 : out = (text *) palloc(VARSIZE(in) + VARHDRSZ);
8366 bruce@momjian.us 647 : 1 : ptr = VARDATA(out);
8492 648 : 1 : curlevel = LTREE_FIRST(in);
8366 649 [ + + ]: 6 : for (i = 0; i < in->numlevel; i++)
650 : : {
651 [ + + ]: 5 : if (i != 0)
652 : : {
8492 653 : 4 : *ptr = '.';
654 : 4 : ptr++;
655 : : }
656 : 5 : memcpy(ptr, curlevel->name, curlevel->len);
657 : 5 : ptr += curlevel->len;
658 : 5 : curlevel = LEVEL_NEXT(curlevel);
659 : : }
660 : :
7063 tgl@sss.pgh.pa.us 661 : 1 : SET_VARSIZE(out, ptr - ((char *) out));
8492 bruce@momjian.us 662 [ - + ]: 1 : PG_FREE_IF_COPY(in, 0);
663 : :
664 : 1 : PG_RETURN_POINTER(out);
665 : : }
666 : :
667 : :
668 : : /*
669 : : * ltreeparentsel - Selectivity of parent relationship for ltree data types.
670 : : *
671 : : * This function is not used anymore, if the ltree extension has been
672 : : * updated to 1.2 or later.
673 : : */
674 : : Datum
7370 bruce@momjian.us 675 :UBC 0 : ltreeparentsel(PG_FUNCTION_ARGS)
676 : : {
677 : 0 : PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
678 : 0 : Oid operator = PG_GETARG_OID(1);
679 : 0 : List *args = (List *) PG_GETARG_POINTER(2);
680 : 0 : int varRelid = PG_GETARG_INT32(3);
681 : : double selec;
682 : :
683 : : /* Use generic restriction selectivity logic, with default 0.001. */
2216 tgl@sss.pgh.pa.us 684 : 0 : selec = generic_restriction_selectivity(root, operator, InvalidOid,
685 : : args, varRelid,
686 : : 0.001);
687 : :
7370 bruce@momjian.us 688 : 0 : PG_RETURN_FLOAT8((float8) selec);
689 : : }
|