Line data Source code
1 : /*
2 : * contrib/ltree/_ltree_gist.c
3 : *
4 : *
5 : * GiST support for ltree[]
6 : * Teodor Sigaev <teodor@stack.net>
7 : */
8 : #include "postgres.h"
9 :
10 : #include "access/gist.h"
11 : #include "access/reloptions.h"
12 : #include "access/stratnum.h"
13 : #include "crc32.h"
14 : #include "ltree.h"
15 : #include "port/pg_bitutils.h"
16 : #include "utils/array.h"
17 :
18 6 : PG_FUNCTION_INFO_V1(_ltree_compress);
19 6 : PG_FUNCTION_INFO_V1(_ltree_same);
20 6 : PG_FUNCTION_INFO_V1(_ltree_union);
21 6 : PG_FUNCTION_INFO_V1(_ltree_penalty);
22 6 : PG_FUNCTION_INFO_V1(_ltree_picksplit);
23 6 : PG_FUNCTION_INFO_V1(_ltree_consistent);
24 6 : PG_FUNCTION_INFO_V1(_ltree_gist_options);
25 :
26 : #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
27 : #define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
28 :
29 : #define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
30 :
31 :
32 : static void
33 14116 : hashing(BITVECP sign, ltree *t, int siglen)
34 : {
35 14116 : int tlen = t->numlevel;
36 14116 : ltree_level *cur = LTREE_FIRST(t);
37 : int hash;
38 :
39 106932 : while (tlen > 0)
40 : {
41 92816 : hash = ltree_crc32_sz(cur->name, cur->len);
42 92816 : AHASH(sign, hash, siglen);
43 92816 : cur = LEVEL_NEXT(cur);
44 92816 : tlen--;
45 : }
46 14116 : }
47 :
48 : Datum
49 11838 : _ltree_compress(PG_FUNCTION_ARGS)
50 : {
51 11838 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
52 11838 : GISTENTRY *retval = entry;
53 11838 : int siglen = LTREE_GET_ASIGLEN();
54 :
55 11838 : if (entry->leafkey)
56 : { /* ltree */
57 : ltree_gist *key;
58 4000 : ArrayType *val = DatumGetArrayTypeP(entry->key);
59 4000 : int num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
60 4000 : ltree *item = (ltree *) ARR_DATA_PTR(val);
61 :
62 4000 : if (ARR_NDIM(val) > 1)
63 0 : ereport(ERROR,
64 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
65 : errmsg("array must be one-dimensional")));
66 4000 : if (array_contains_nulls(val))
67 0 : ereport(ERROR,
68 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
69 : errmsg("array must not contain nulls")));
70 :
71 4000 : key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
72 :
73 18116 : while (num > 0)
74 : {
75 14116 : hashing(LTG_SIGN(key), item, siglen);
76 14116 : num--;
77 14116 : item = NEXTVAL(item);
78 : }
79 :
80 4000 : retval = palloc_object(GISTENTRY);
81 4000 : gistentryinit(*retval, PointerGetDatum(key),
82 : entry->rel, entry->page,
83 : entry->offset, false);
84 : }
85 7838 : else if (!LTG_ISALLTRUE(DatumGetPointer(entry->key)))
86 : {
87 : int32 i;
88 : ltree_gist *key;
89 7838 : BITVECP sign = LTG_SIGN(DatumGetPointer(entry->key));
90 :
91 7838 : ALOOPBYTE(siglen)
92 : {
93 7838 : if ((sign[i] & 0xff) != 0xff)
94 7838 : PG_RETURN_POINTER(retval);
95 : }
96 :
97 0 : key = ltree_gist_alloc(true, sign, siglen, NULL, NULL);
98 0 : retval = palloc_object(GISTENTRY);
99 0 : gistentryinit(*retval, PointerGetDatum(key),
100 : entry->rel, entry->page,
101 : entry->offset, false);
102 : }
103 4000 : PG_RETURN_POINTER(retval);
104 : }
105 :
106 : Datum
107 15258 : _ltree_same(PG_FUNCTION_ARGS)
108 : {
109 15258 : ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
110 15258 : ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
111 15258 : bool *result = (bool *) PG_GETARG_POINTER(2);
112 15258 : int siglen = LTREE_GET_ASIGLEN();
113 :
114 15258 : if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
115 0 : *result = true;
116 15258 : else if (LTG_ISALLTRUE(a))
117 0 : *result = false;
118 15258 : else if (LTG_ISALLTRUE(b))
119 0 : *result = false;
120 : else
121 : {
122 : int32 i;
123 15258 : BITVECP sa = LTG_SIGN(a),
124 15258 : sb = LTG_SIGN(b);
125 :
126 15258 : *result = true;
127 21402518 : ALOOPBYTE(siglen)
128 : {
129 21391474 : if (sa[i] != sb[i])
130 : {
131 4214 : *result = false;
132 4214 : break;
133 : }
134 : }
135 : }
136 15258 : PG_RETURN_POINTER(result);
137 : }
138 :
139 : static int32
140 30516 : unionkey(BITVECP sbase, ltree_gist *add, int siglen)
141 : {
142 : int32 i;
143 30516 : BITVECP sadd = LTG_SIGN(add);
144 :
145 30516 : if (LTG_ISALLTRUE(add))
146 0 : return 1;
147 :
148 54936644 : ALOOPBYTE(siglen)
149 54906128 : sbase[i] |= sadd[i];
150 30516 : return 0;
151 : }
152 :
153 : Datum
154 15258 : _ltree_union(PG_FUNCTION_ARGS)
155 : {
156 15258 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
157 15258 : int *size = (int *) PG_GETARG_POINTER(1);
158 15258 : int siglen = LTREE_GET_ASIGLEN();
159 : int32 i;
160 15258 : ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
161 15258 : BITVECP base = LTG_SIGN(result);
162 :
163 45774 : for (i = 0; i < entryvec->n; i++)
164 : {
165 30516 : if (unionkey(base, GETENTRY(entryvec, i), siglen))
166 : {
167 0 : result->flag |= LTG_ALLTRUE;
168 0 : SET_VARSIZE(result, LTG_HDRSIZE);
169 0 : break;
170 : }
171 : }
172 :
173 15258 : *size = VARSIZE(result);
174 :
175 15258 : PG_RETURN_POINTER(result);
176 : }
177 :
178 : static int32
179 0 : sizebitvec(BITVECP sign, int siglen)
180 : {
181 0 : return pg_popcount((const char *) sign, siglen);
182 : }
183 :
184 : static int
185 271300 : hemdistsign(BITVECP a, BITVECP b, int siglen)
186 : {
187 : int i,
188 : diff,
189 271300 : dist = 0;
190 :
191 118773444 : ALOOPBYTE(siglen)
192 : {
193 118502144 : diff = (unsigned char) (a[i] ^ b[i]);
194 : /* Using the popcount functions here isn't likely to win */
195 118502144 : dist += pg_number_of_ones[diff];
196 : }
197 271300 : return dist;
198 : }
199 :
200 : static int
201 271300 : hemdist(ltree_gist *a, ltree_gist *b, int siglen)
202 : {
203 271300 : if (LTG_ISALLTRUE(a))
204 : {
205 0 : if (LTG_ISALLTRUE(b))
206 0 : return 0;
207 : else
208 0 : return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(b), siglen);
209 : }
210 271300 : else if (LTG_ISALLTRUE(b))
211 0 : return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
212 :
213 271300 : return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
214 : }
215 :
216 :
217 : Datum
218 38620 : _ltree_penalty(PG_FUNCTION_ARGS)
219 : {
220 38620 : ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
221 38620 : ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
222 38620 : float *penalty = (float *) PG_GETARG_POINTER(2);
223 38620 : int siglen = LTREE_GET_ASIGLEN();
224 :
225 38620 : *penalty = hemdist(origval, newval, siglen);
226 38620 : PG_RETURN_POINTER(penalty);
227 : }
228 :
229 : typedef struct
230 : {
231 : OffsetNumber pos;
232 : int32 cost;
233 : } SPLITCOST;
234 :
235 : static int
236 16926 : comparecost(const void *a, const void *b)
237 : {
238 16926 : return ((const SPLITCOST *) a)->cost - ((const SPLITCOST *) b)->cost;
239 : }
240 :
241 : Datum
242 1812 : _ltree_picksplit(PG_FUNCTION_ARGS)
243 : {
244 1812 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
245 1812 : GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
246 1812 : int siglen = LTREE_GET_ASIGLEN();
247 : OffsetNumber k,
248 : j;
249 : ltree_gist *datum_l,
250 : *datum_r;
251 : BITVECP union_l,
252 : union_r;
253 : int32 size_alpha,
254 : size_beta;
255 : int32 size_waste,
256 1812 : waste = -1;
257 : int32 nbytes;
258 1812 : OffsetNumber seed_1 = 0,
259 1812 : seed_2 = 0;
260 : OffsetNumber *left,
261 : *right;
262 : OffsetNumber maxoff;
263 : BITVECP ptr;
264 : int i;
265 : SPLITCOST *costvector;
266 : ltree_gist *_k,
267 : *_j;
268 :
269 1812 : maxoff = entryvec->n - 2;
270 1812 : nbytes = (maxoff + 2) * sizeof(OffsetNumber);
271 1812 : v->spl_left = (OffsetNumber *) palloc(nbytes);
272 1812 : v->spl_right = (OffsetNumber *) palloc(nbytes);
273 :
274 8176 : for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
275 : {
276 6364 : _k = GETENTRY(entryvec, k);
277 206340 : for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
278 : {
279 199976 : size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
280 199976 : if (size_waste > waste)
281 : {
282 3256 : waste = size_waste;
283 3256 : seed_1 = k;
284 3256 : seed_2 = j;
285 : }
286 : }
287 : }
288 :
289 1812 : left = v->spl_left;
290 1812 : v->spl_nleft = 0;
291 1812 : right = v->spl_right;
292 1812 : v->spl_nright = 0;
293 :
294 1812 : if (seed_1 == 0 || seed_2 == 0)
295 : {
296 0 : seed_1 = 1;
297 0 : seed_2 = 2;
298 : }
299 :
300 : /* form initial .. */
301 1812 : datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
302 1812 : LTG_SIGN(GETENTRY(entryvec, seed_1)),
303 : siglen, NULL, NULL);
304 :
305 1812 : datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
306 1812 : LTG_SIGN(GETENTRY(entryvec, seed_2)),
307 : siglen, NULL, NULL);
308 :
309 1812 : maxoff = OffsetNumberNext(maxoff);
310 : /* sort before ... */
311 1812 : costvector = palloc_array(SPLITCOST, maxoff);
312 11800 : for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
313 : {
314 9988 : costvector[j - 1].pos = j;
315 9988 : _j = GETENTRY(entryvec, j);
316 9988 : size_alpha = hemdist(datum_l, _j, siglen);
317 9988 : size_beta = hemdist(datum_r, _j, siglen);
318 9988 : costvector[j - 1].cost = abs(size_alpha - size_beta);
319 : }
320 1812 : qsort(costvector, maxoff, sizeof(SPLITCOST), comparecost);
321 :
322 1812 : union_l = LTG_SIGN(datum_l);
323 1812 : union_r = LTG_SIGN(datum_r);
324 :
325 11800 : for (k = 0; k < maxoff; k++)
326 : {
327 9988 : j = costvector[k].pos;
328 9988 : if (j == seed_1)
329 : {
330 1812 : *left++ = j;
331 1812 : v->spl_nleft++;
332 1812 : continue;
333 : }
334 8176 : else if (j == seed_2)
335 : {
336 1812 : *right++ = j;
337 1812 : v->spl_nright++;
338 1812 : continue;
339 : }
340 6364 : _j = GETENTRY(entryvec, j);
341 6364 : size_alpha = hemdist(datum_l, _j, siglen);
342 6364 : size_beta = hemdist(datum_r, _j, siglen);
343 :
344 6364 : if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
345 : {
346 3104 : if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
347 : {
348 0 : if (!LTG_ISALLTRUE(datum_l))
349 0 : memset(union_l, 0xff, siglen);
350 : }
351 : else
352 : {
353 3104 : ptr = LTG_SIGN(_j);
354 3395392 : ALOOPBYTE(siglen)
355 3392288 : union_l[i] |= ptr[i];
356 : }
357 3104 : *left++ = j;
358 3104 : v->spl_nleft++;
359 : }
360 : else
361 : {
362 3260 : if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
363 : {
364 0 : if (!LTG_ISALLTRUE(datum_r))
365 0 : memset(union_r, 0xff, siglen);
366 : }
367 : else
368 : {
369 3260 : ptr = LTG_SIGN(_j);
370 3942828 : ALOOPBYTE(siglen)
371 3939568 : union_r[i] |= ptr[i];
372 : }
373 3260 : *right++ = j;
374 3260 : v->spl_nright++;
375 : }
376 : }
377 :
378 1812 : *right = *left = FirstOffsetNumber;
379 :
380 1812 : v->spl_ldatum = PointerGetDatum(datum_l);
381 1812 : v->spl_rdatum = PointerGetDatum(datum_r);
382 :
383 1812 : PG_RETURN_POINTER(v);
384 : }
385 :
386 : static bool
387 4822 : gist_te(ltree_gist *key, ltree *query, int siglen)
388 : {
389 4822 : ltree_level *curq = LTREE_FIRST(query);
390 4822 : BITVECP sign = LTG_SIGN(key);
391 4822 : int qlen = query->numlevel;
392 : unsigned int hv;
393 :
394 4822 : if (LTG_ISALLTRUE(key))
395 0 : return true;
396 :
397 16594 : while (qlen > 0)
398 : {
399 12670 : hv = ltree_crc32_sz(curq->name, curq->len);
400 12670 : if (!GETBIT(sign, AHASHVAL(hv, siglen)))
401 898 : return false;
402 11772 : curq = LEVEL_NEXT(curq);
403 11772 : qlen--;
404 : }
405 :
406 3924 : return true;
407 : }
408 :
409 : typedef struct LtreeSignature
410 : {
411 : BITVECP sign;
412 : int siglen;
413 : } LtreeSignature;
414 :
415 : static bool
416 6938 : checkcondition_bit(void *cxt, ITEM *val)
417 : {
418 6938 : LtreeSignature *sig = cxt;
419 :
420 6938 : return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
421 : }
422 :
423 : static bool
424 4152 : gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
425 : {
426 : LtreeSignature sig;
427 :
428 4152 : if (LTG_ISALLTRUE(key))
429 0 : return true;
430 :
431 4152 : sig.sign = LTG_SIGN(key);
432 4152 : sig.siglen = siglen;
433 :
434 4152 : return ltree_execute(GETQUERY(query),
435 : &sig, false,
436 : checkcondition_bit);
437 : }
438 :
439 : static bool
440 28682 : gist_qe(ltree_gist *key, lquery *query, int siglen)
441 : {
442 28682 : lquery_level *curq = LQUERY_FIRST(query);
443 28682 : BITVECP sign = LTG_SIGN(key);
444 28682 : int qlen = query->numlevel;
445 :
446 28682 : if (LTG_ISALLTRUE(key))
447 0 : return true;
448 :
449 86700 : while (qlen > 0)
450 : {
451 69272 : if (curq->numvar && LQL_CANLOOKSIGN(curq))
452 : {
453 48528 : bool isexist = false;
454 48528 : int vlen = curq->numvar;
455 48528 : lquery_variant *curv = LQL_FIRST(curq);
456 :
457 59782 : while (vlen > 0)
458 : {
459 48528 : if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
460 : {
461 37274 : isexist = true;
462 37274 : break;
463 : }
464 11254 : curv = LVAR_NEXT(curv);
465 11254 : vlen--;
466 : }
467 48528 : if (!isexist)
468 11254 : return false;
469 : }
470 :
471 58018 : curq = LQL_NEXT(curq);
472 58018 : qlen--;
473 : }
474 :
475 17428 : return true;
476 : }
477 :
478 : static bool
479 4720 : _arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
480 : {
481 4720 : lquery *query = (lquery *) ARR_DATA_PTR(_query);
482 4720 : int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
483 :
484 4720 : if (ARR_NDIM(_query) > 1)
485 0 : ereport(ERROR,
486 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
487 : errmsg("array must be one-dimensional")));
488 4720 : if (array_contains_nulls(_query))
489 0 : ereport(ERROR,
490 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
491 : errmsg("array must not contain nulls")));
492 :
493 8794 : while (num > 0)
494 : {
495 7040 : if (gist_qe(key, query, siglen))
496 2966 : return true;
497 4074 : num--;
498 4074 : query = (lquery *) NEXTVAL(query);
499 : }
500 1754 : return false;
501 : }
502 :
503 : Datum
504 35336 : _ltree_consistent(PG_FUNCTION_ARGS)
505 : {
506 35336 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
507 35336 : void *query = PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
508 35336 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
509 : #ifdef NOT_USED
510 : Oid subtype = PG_GETARG_OID(3);
511 : #endif
512 35336 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
513 35336 : int siglen = LTREE_GET_ASIGLEN();
514 35336 : ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
515 35336 : bool res = false;
516 :
517 : /* All cases served by this function are inexact */
518 35336 : *recheck = true;
519 :
520 35336 : switch (strategy)
521 : {
522 4822 : case 10:
523 : case 11:
524 4822 : res = gist_te(key, (ltree *) query, siglen);
525 4822 : break;
526 21642 : case 12:
527 : case 13:
528 21642 : res = gist_qe(key, (lquery *) query, siglen);
529 21642 : break;
530 4152 : case 14:
531 : case 15:
532 4152 : res = gist_qtxt(key, (ltxtquery *) query, siglen);
533 4152 : break;
534 4720 : case 16:
535 : case 17:
536 4720 : res = _arrq_cons(key, (ArrayType *) query, siglen);
537 4720 : break;
538 0 : default:
539 : /* internal error */
540 0 : elog(ERROR, "unrecognized StrategyNumber: %d", strategy);
541 : }
542 35336 : PG_FREE_IF_COPY(query, 1);
543 35336 : PG_RETURN_BOOL(res);
544 : }
545 :
546 : Datum
547 20 : _ltree_gist_options(PG_FUNCTION_ARGS)
548 : {
549 20 : local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
550 :
551 20 : init_local_reloptions(relopts, sizeof(LtreeGistOptions));
552 20 : add_local_int_reloption(relopts, "siglen", "signature length",
553 : LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX,
554 : offsetof(LtreeGistOptions, siglen));
555 :
556 20 : PG_RETURN_VOID();
557 : }
|