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 :
16 3 : PG_MODULE_MAGIC_EXT(
17 : .name = "ltree",
18 : .version = PG_VERSION
19 : );
20 :
21 : /* compare functions */
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);
29 3 : PG_FUNCTION_INFO_V1(hash_ltree);
30 3 : PG_FUNCTION_INFO_V1(hash_ltree_extended);
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);
36 6 : PG_FUNCTION_INFO_V1(ltree_index);
37 3 : PG_FUNCTION_INFO_V1(ltree_addltree);
38 3 : PG_FUNCTION_INFO_V1(ltree_addtext);
39 2 : PG_FUNCTION_INFO_V1(ltree_textadd);
40 16 : PG_FUNCTION_INFO_V1(lca);
41 3 : PG_FUNCTION_INFO_V1(ltree2text);
42 3 : PG_FUNCTION_INFO_V1(text2ltree);
43 2 : PG_FUNCTION_INFO_V1(ltreeparentsel);
44 :
45 : /*
46 : * btree-comparison function.
47 : */
48 : int
49 98260 : ltree_compare(const ltree *a, const ltree *b)
50 : {
51 98260 : ltree_level *al = LTREE_FIRST(a);
52 98260 : ltree_level *bl = LTREE_FIRST(b);
53 98260 : int an = a->numlevel;
54 98260 : int bn = b->numlevel;
55 :
56 185826 : while (an > 0 && bn > 0)
57 : {
58 : int res;
59 :
60 176600 : res = memcmp(al->name, bl->name, Min(al->len, bl->len));
61 176600 : if (res == 0)
62 : {
63 93842 : if (al->len != bl->len)
64 6276 : return (int) al->len - (int) bl->len;
65 : }
66 : else
67 82758 : return res;
68 :
69 87566 : an--;
70 87566 : bn--;
71 87566 : al = LEVEL_NEXT(al);
72 87566 : bl = LEVEL_NEXT(bl);
73 : }
74 :
75 9226 : 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 19512 : ltree_compare_distance(const ltree *a, const ltree *b)
84 : {
85 19512 : ltree_level *al = LTREE_FIRST(a);
86 19512 : ltree_level *bl = LTREE_FIRST(b);
87 19512 : int an = a->numlevel;
88 19512 : int bn = b->numlevel;
89 :
90 20298 : while (an > 0 && bn > 0)
91 : {
92 : int res;
93 :
94 18133 : res = memcmp(al->name, bl->name, Min(al->len, bl->len));
95 18133 : if (res == 0)
96 : {
97 1817 : if (al->len != bl->len)
98 1031 : return (float) (al->len - bl->len) * 10.0 * (an + 1);
99 : }
100 : else
101 : {
102 16316 : if (res < 0)
103 10543 : return -1.0 * 10.0 * (an + 1);
104 : else
105 5773 : return 1.0 * 10.0 * (an + 1);
106 : }
107 :
108 786 : an--;
109 786 : bn--;
110 786 : al = LEVEL_NEXT(al);
111 786 : bl = LEVEL_NEXT(bl);
112 : }
113 :
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
125 51231 : ltree_cmp(PG_FUNCTION_ARGS)
126 : {
127 51231 : RUNCMP;
128 51231 : PG_RETURN_INT32(res);
129 : }
130 :
131 : Datum
132 2141 : ltree_lt(PG_FUNCTION_ARGS)
133 : {
134 2141 : RUNCMP;
135 2141 : PG_RETURN_BOOL(res < 0);
136 : }
137 :
138 : Datum
139 2141 : ltree_le(PG_FUNCTION_ARGS)
140 : {
141 2141 : RUNCMP;
142 2141 : PG_RETURN_BOOL(res <= 0);
143 : }
144 :
145 : Datum
146 2017 : ltree_eq(PG_FUNCTION_ARGS)
147 : {
148 2017 : RUNCMP;
149 2017 : PG_RETURN_BOOL(res == 0);
150 : }
151 :
152 : Datum
153 2118 : ltree_ge(PG_FUNCTION_ARGS)
154 : {
155 2118 : RUNCMP;
156 2118 : PG_RETURN_BOOL(res >= 0);
157 : }
158 :
159 : Datum
160 2118 : ltree_gt(PG_FUNCTION_ARGS)
161 : {
162 2118 : RUNCMP;
163 2118 : PG_RETURN_BOOL(res > 0);
164 : }
165 :
166 : Datum
167 0 : ltree_ne(PG_FUNCTION_ARGS)
168 : {
169 0 : RUNCMP;
170 0 : PG_RETURN_BOOL(res != 0);
171 : }
172 :
173 : /* Compute a hash for the ltree */
174 : Datum
175 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
240 2 : nlevel(PG_FUNCTION_ARGS)
241 : {
242 2 : ltree *a = PG_GETARG_LTREE_P(0);
243 2 : int res = a->numlevel;
244 :
245 2 : PG_FREE_IF_COPY(a, 0);
246 2 : PG_RETURN_INT32(res);
247 : }
248 :
249 : bool
250 21188 : inner_isparent(const ltree *c, const ltree *p)
251 : {
252 21188 : ltree_level *cl = LTREE_FIRST(c);
253 21188 : ltree_level *pl = LTREE_FIRST(p);
254 21188 : int pn = p->numlevel;
255 :
256 21188 : if (pn > c->numlevel)
257 10166 : return false;
258 :
259 12019 : while (pn > 0)
260 : {
261 11883 : if (cl->len != pl->len)
262 7960 : return false;
263 3923 : if (memcmp(cl->name, pl->name, cl->len) != 0)
264 2926 : return false;
265 :
266 997 : pn--;
267 997 : cl = LEVEL_NEXT(cl);
268 997 : pl = LEVEL_NEXT(pl);
269 : }
270 136 : return true;
271 : }
272 :
273 : Datum
274 11538 : ltree_isparent(PG_FUNCTION_ARGS)
275 : {
276 11538 : ltree *c = PG_GETARG_LTREE_P(1);
277 11538 : ltree *p = PG_GETARG_LTREE_P(0);
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 : {
288 9320 : ltree *c = PG_GETARG_LTREE_P(0);
289 9320 : ltree *p = PG_GETARG_LTREE_P(1);
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 *
299 10 : inner_subltree(ltree *t, int32 startpos, int32 endpos)
300 : {
301 10 : char *start = NULL,
302 10 : *end = NULL;
303 10 : ltree_level *ptr = LTREE_FIRST(t);
304 : ltree *res;
305 : int i;
306 :
307 10 : if (startpos < 0 || endpos < 0 || startpos >= t->numlevel || startpos > endpos)
308 1 : ereport(ERROR,
309 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
310 : errmsg("invalid positions")));
311 :
312 9 : if (endpos > t->numlevel)
313 2 : endpos = t->numlevel;
314 :
315 9 : start = end = (char *) ptr;
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);
323 8 : break;
324 : }
325 10 : ptr = LEVEL_NEXT(ptr);
326 : }
327 :
328 9 : res = (ltree *) palloc0(LTREE_HDRSIZE + (end - start));
329 9 : SET_VARSIZE(res, LTREE_HDRSIZE + (end - start));
330 9 : res->numlevel = endpos - startpos;
331 :
332 9 : memcpy(LTREE_FIRST(res), start, end - start);
333 :
334 9 : return res;
335 : }
336 :
337 : Datum
338 1 : subltree(PG_FUNCTION_ARGS)
339 : {
340 1 : ltree *t = PG_GETARG_LTREE_P(0);
341 1 : ltree *res = inner_subltree(t, PG_GETARG_INT32(1), PG_GETARG_INT32(2));
342 :
343 1 : PG_FREE_IF_COPY(t, 0);
344 1 : PG_RETURN_POINTER(res);
345 : }
346 :
347 : Datum
348 9 : subpath(PG_FUNCTION_ARGS)
349 : {
350 9 : ltree *t = PG_GETARG_LTREE_P(0);
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 :
356 9 : if (start < 0)
357 2 : start = t->numlevel + start;
358 :
359 9 : if (len < 0)
360 2 : end = t->numlevel + len;
361 7 : else if (len == 0)
362 5 : end = (fcinfo->nargs == 3) ? start : LTREE_MAX_LEVELS;
363 : else
364 2 : end = start + len;
365 :
366 9 : res = inner_subltree(t, start, end);
367 :
368 8 : PG_FREE_IF_COPY(t, 0);
369 8 : PG_RETURN_POINTER(res);
370 : }
371 :
372 : static ltree *
373 6 : ltree_concat(ltree *a, ltree *b)
374 : {
375 : ltree *r;
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 :
384 5 : r = (ltree *) palloc0(VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
385 5 : SET_VARSIZE(r, VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
386 5 : r->numlevel = (uint16) numlevel;
387 :
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);
392 5 : return r;
393 : }
394 :
395 : Datum
396 5 : ltree_addltree(PG_FUNCTION_ARGS)
397 : {
398 5 : ltree *a = PG_GETARG_LTREE_P(0);
399 5 : ltree *b = PG_GETARG_LTREE_P(1);
400 : ltree *r;
401 :
402 5 : r = ltree_concat(a, b);
403 4 : PG_FREE_IF_COPY(a, 0);
404 4 : PG_FREE_IF_COPY(b, 1);
405 4 : PG_RETURN_POINTER(r);
406 : }
407 :
408 : Datum
409 1 : ltree_addtext(PG_FUNCTION_ARGS)
410 : {
411 1 : ltree *a = PG_GETARG_LTREE_P(0);
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 :
422 1 : pfree(s);
423 :
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);
430 1 : PG_RETURN_POINTER(r);
431 : }
432 :
433 : Datum
434 18 : ltree_index(PG_FUNCTION_ARGS)
435 : {
436 18 : ltree *a = PG_GETARG_LTREE_P(0);
437 18 : ltree *b = PG_GETARG_LTREE_P(1);
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 : {
456 1 : PG_FREE_IF_COPY(a, 0);
457 1 : PG_FREE_IF_COPY(b, 1);
458 1 : PG_RETURN_INT32(-1);
459 : }
460 :
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 : {
470 79 : if (!(aptr->len == bptr->len && memcmp(aptr->name, bptr->name, aptr->len) == 0))
471 : break;
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;
479 14 : break;
480 : }
481 : }
482 92 : startptr = LEVEL_NEXT(startptr);
483 : }
484 :
485 17 : if (!found)
486 3 : i = -1;
487 :
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
494 0 : ltree_textadd(PG_FUNCTION_ARGS)
495 : {
496 0 : ltree *a = PG_GETARG_LTREE_P(1);
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 :
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 *
525 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 :
536 14 : if (len <= 0)
537 1 : return NULL; /* no inputs? */
538 13 : if ((*a)->numlevel == 0)
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;
546 23 : while (ptr - a < len)
547 : {
548 12 : if ((*ptr)->numlevel == 0)
549 1 : return NULL;
550 11 : else if ((*ptr)->numlevel == 1)
551 2 : num = 0;
552 : else
553 : {
554 9 : l1 = LTREE_FIRST(*a);
555 9 : l2 = LTREE_FIRST(*ptr);
556 9 : tmp = Min(num, (*ptr)->numlevel - 1);
557 9 : num = 0;
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)
562 14 : num = i + 1;
563 : else
564 : break;
565 14 : l1 = LEVEL_NEXT(l1);
566 14 : l2 = LEVEL_NEXT(l2);
567 : }
568 : }
569 11 : ptr++;
570 : }
571 :
572 : /* Now compute size of result ... */
573 11 : reslen = LTREE_HDRSIZE;
574 11 : l1 = LTREE_FIRST(*a);
575 21 : for (i = 0; i < num; i++)
576 : {
577 10 : reslen += MAXALIGN(l1->len + LEVEL_HDRSIZE);
578 10 : l1 = LEVEL_NEXT(l1);
579 : }
580 :
581 : /* ... and construct it by copying from *a */
582 11 : res = (ltree *) palloc0(reslen);
583 11 : SET_VARSIZE(res, reslen);
584 11 : res->numlevel = num;
585 :
586 11 : l1 = LTREE_FIRST(*a);
587 11 : l2 = LTREE_FIRST(res);
588 :
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 :
596 11 : return res;
597 : }
598 :
599 : Datum
600 6 : lca(PG_FUNCTION_ARGS)
601 : {
602 : int i;
603 : ltree **a,
604 : *res;
605 :
606 6 : a = palloc_array(ltree *, fcinfo->nargs);
607 21 : for (i = 0; i < fcinfo->nargs; i++)
608 15 : a[i] = PG_GETARG_LTREE_P(i);
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);
612 6 : pfree(a);
613 :
614 6 : if (res)
615 5 : PG_RETURN_POINTER(res);
616 : else
617 1 : PG_RETURN_NULL();
618 : }
619 :
620 : Datum
621 1 : text2ltree(PG_FUNCTION_ARGS)
622 : {
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)));
631 1 : pfree(s);
632 1 : PG_FREE_IF_COPY(in, 0);
633 1 : PG_RETURN_POINTER(out);
634 : }
635 :
636 :
637 : Datum
638 1 : ltree2text(PG_FUNCTION_ARGS)
639 : {
640 1 : ltree *in = PG_GETARG_LTREE_P(0);
641 : char *ptr;
642 : int i;
643 : ltree_level *curlevel;
644 : text *out;
645 :
646 1 : out = (text *) palloc(VARSIZE(in) + VARHDRSZ);
647 1 : ptr = VARDATA(out);
648 1 : curlevel = LTREE_FIRST(in);
649 6 : for (i = 0; i < in->numlevel; i++)
650 : {
651 5 : if (i != 0)
652 : {
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 :
661 1 : SET_VARSIZE(out, ptr - ((char *) out));
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
675 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. */
684 0 : selec = generic_restriction_selectivity(root, operator, InvalidOid,
685 : args, varRelid,
686 : 0.001);
687 :
688 0 : PG_RETURN_FLOAT8((float8) selec);
689 : }
|