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 3 : PG_FUNCTION_INFO_V1(_ltree_compress);
19 3 : PG_FUNCTION_INFO_V1(_ltree_same);
20 3 : PG_FUNCTION_INFO_V1(_ltree_union);
21 3 : PG_FUNCTION_INFO_V1(_ltree_penalty);
22 3 : PG_FUNCTION_INFO_V1(_ltree_picksplit);
23 3 : PG_FUNCTION_INFO_V1(_ltree_consistent);
24 3 : 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 7058 : hashing(BITVECP sign, ltree *t, int siglen)
34 : {
35 7058 : int tlen = t->numlevel;
36 7058 : ltree_level *cur = LTREE_FIRST(t);
37 : int hash;
38 :
39 53466 : while (tlen > 0)
40 : {
41 46408 : hash = ltree_crc32_sz(cur->name, cur->len);
42 46408 : AHASH(sign, hash, siglen);
43 46408 : cur = LEVEL_NEXT(cur);
44 46408 : tlen--;
45 : }
46 7058 : }
47 :
48 : Datum
49 6027 : _ltree_compress(PG_FUNCTION_ARGS)
50 : {
51 6027 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
52 6027 : GISTENTRY *retval = entry;
53 6027 : int siglen = LTREE_GET_ASIGLEN();
54 :
55 6027 : if (entry->leafkey)
56 : { /* ltree */
57 : ltree_gist *key;
58 2000 : ArrayType *val = DatumGetArrayTypeP(entry->key);
59 2000 : int num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
60 2000 : ltree *item = (ltree *) ARR_DATA_PTR(val);
61 :
62 2000 : if (ARR_NDIM(val) > 1)
63 0 : ereport(ERROR,
64 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
65 : errmsg("array must be one-dimensional")));
66 2000 : 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 2000 : key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
72 :
73 9058 : while (num > 0)
74 : {
75 7058 : hashing(LTG_SIGN(key), item, siglen);
76 7058 : num--;
77 7058 : item = NEXTVAL(item);
78 : }
79 :
80 2000 : retval = palloc_object(GISTENTRY);
81 2000 : gistentryinit(*retval, PointerGetDatum(key),
82 : entry->rel, entry->page,
83 : entry->offset, false);
84 : }
85 4027 : else if (!LTG_ISALLTRUE(DatumGetPointer(entry->key)))
86 : {
87 : int32 i;
88 : ltree_gist *key;
89 4027 : BITVECP sign = LTG_SIGN(DatumGetPointer(entry->key));
90 :
91 4027 : ALOOPBYTE(siglen)
92 : {
93 4027 : if ((sign[i] & 0xff) != 0xff)
94 4027 : 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 2000 : PG_RETURN_POINTER(retval);
104 : }
105 :
106 : Datum
107 8549 : _ltree_same(PG_FUNCTION_ARGS)
108 : {
109 8549 : ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
110 8549 : ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
111 8549 : bool *result = (bool *) PG_GETARG_POINTER(2);
112 8549 : int siglen = LTREE_GET_ASIGLEN();
113 :
114 8549 : if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
115 0 : *result = true;
116 8549 : else if (LTG_ISALLTRUE(a))
117 0 : *result = false;
118 8549 : else if (LTG_ISALLTRUE(b))
119 0 : *result = false;
120 : else
121 : {
122 : int32 i;
123 8549 : BITVECP sa = LTG_SIGN(a),
124 8549 : sb = LTG_SIGN(b);
125 :
126 8549 : *result = true;
127 12526734 : ALOOPBYTE(siglen)
128 : {
129 12520314 : if (sa[i] != sb[i])
130 : {
131 2129 : *result = false;
132 2129 : break;
133 : }
134 : }
135 : }
136 8549 : PG_RETURN_POINTER(result);
137 : }
138 :
139 : static int32
140 17098 : unionkey(BITVECP sbase, ltree_gist *add, int siglen)
141 : {
142 : int32 i;
143 17098 : BITVECP sadd = LTG_SIGN(add);
144 :
145 17098 : if (LTG_ISALLTRUE(add))
146 0 : return 1;
147 :
148 31194322 : ALOOPBYTE(siglen)
149 31177224 : sbase[i] |= sadd[i];
150 17098 : return 0;
151 : }
152 :
153 : Datum
154 8549 : _ltree_union(PG_FUNCTION_ARGS)
155 : {
156 8549 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
157 8549 : int *size = (int *) PG_GETARG_POINTER(1);
158 8549 : int siglen = LTREE_GET_ASIGLEN();
159 : int32 i;
160 8549 : ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
161 8549 : BITVECP base = LTG_SIGN(result);
162 :
163 25647 : for (i = 0; i < entryvec->n; i++)
164 : {
165 17098 : 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 8549 : *size = VARSIZE(result);
174 :
175 8549 : 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 137187 : hemdistsign(BITVECP a, BITVECP b, int siglen)
186 : {
187 : int i,
188 : diff,
189 137187 : dist = 0;
190 :
191 62425295 : ALOOPBYTE(siglen)
192 : {
193 62288108 : diff = (unsigned char) (a[i] ^ b[i]);
194 : /* Using the popcount functions here isn't likely to win */
195 62288108 : dist += pg_number_of_ones[diff];
196 : }
197 137187 : return dist;
198 : }
199 :
200 : static int
201 137187 : hemdist(ltree_gist *a, ltree_gist *b, int siglen)
202 : {
203 137187 : 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 137187 : else if (LTG_ISALLTRUE(b))
211 0 : return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
212 :
213 137187 : return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
214 : }
215 :
216 :
217 : Datum
218 20202 : _ltree_penalty(PG_FUNCTION_ARGS)
219 : {
220 20202 : ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
221 20202 : ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
222 20202 : float *penalty = (float *) PG_GETARG_POINTER(2);
223 20202 : int siglen = LTREE_GET_ASIGLEN();
224 :
225 20202 : *penalty = hemdist(origval, newval, siglen);
226 20202 : PG_RETURN_POINTER(penalty);
227 : }
228 :
229 : typedef struct
230 : {
231 : OffsetNumber pos;
232 : int32 cost;
233 : } SPLITCOST;
234 :
235 : static int
236 8644 : comparecost(const void *a, const void *b)
237 : {
238 8644 : return ((const SPLITCOST *) a)->cost - ((const SPLITCOST *) b)->cost;
239 : }
240 :
241 : Datum
242 949 : _ltree_picksplit(PG_FUNCTION_ARGS)
243 : {
244 949 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
245 949 : GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
246 949 : 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 949 : waste = -1;
257 : int32 nbytes;
258 949 : OffsetNumber seed_1 = 0,
259 949 : 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 949 : maxoff = entryvec->n - 2;
270 949 : nbytes = (maxoff + 2) * sizeof(OffsetNumber);
271 949 : v->spl_left = (OffsetNumber *) palloc(nbytes);
272 949 : v->spl_right = (OffsetNumber *) palloc(nbytes);
273 :
274 4217 : for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
275 : {
276 3268 : _k = GETENTRY(entryvec, k);
277 103385 : for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
278 : {
279 100117 : size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
280 100117 : if (size_waste > waste)
281 : {
282 1754 : waste = size_waste;
283 1754 : seed_1 = k;
284 1754 : seed_2 = j;
285 : }
286 : }
287 : }
288 :
289 949 : left = v->spl_left;
290 949 : v->spl_nleft = 0;
291 949 : right = v->spl_right;
292 949 : v->spl_nright = 0;
293 :
294 949 : if (seed_1 == 0 || seed_2 == 0)
295 : {
296 0 : seed_1 = 1;
297 0 : seed_2 = 2;
298 : }
299 :
300 : /* form initial .. */
301 949 : datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
302 949 : LTG_SIGN(GETENTRY(entryvec, seed_1)),
303 : siglen, NULL, NULL);
304 :
305 949 : datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
306 949 : LTG_SIGN(GETENTRY(entryvec, seed_2)),
307 : siglen, NULL, NULL);
308 :
309 949 : maxoff = OffsetNumberNext(maxoff);
310 : /* sort before ... */
311 949 : costvector = palloc_array(SPLITCOST, maxoff);
312 6115 : for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
313 : {
314 5166 : costvector[j - 1].pos = j;
315 5166 : _j = GETENTRY(entryvec, j);
316 5166 : size_alpha = hemdist(datum_l, _j, siglen);
317 5166 : size_beta = hemdist(datum_r, _j, siglen);
318 5166 : costvector[j - 1].cost = abs(size_alpha - size_beta);
319 : }
320 949 : qsort(costvector, maxoff, sizeof(SPLITCOST), comparecost);
321 :
322 949 : union_l = LTG_SIGN(datum_l);
323 949 : union_r = LTG_SIGN(datum_r);
324 :
325 6115 : for (k = 0; k < maxoff; k++)
326 : {
327 5166 : j = costvector[k].pos;
328 5166 : if (j == seed_1)
329 : {
330 949 : *left++ = j;
331 949 : v->spl_nleft++;
332 949 : continue;
333 : }
334 4217 : else if (j == seed_2)
335 : {
336 949 : *right++ = j;
337 949 : v->spl_nright++;
338 949 : continue;
339 : }
340 3268 : _j = GETENTRY(entryvec, j);
341 3268 : size_alpha = hemdist(datum_l, _j, siglen);
342 3268 : size_beta = hemdist(datum_r, _j, siglen);
343 :
344 3268 : if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
345 : {
346 1538 : 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 1538 : ptr = LTG_SIGN(_j);
354 1737210 : ALOOPBYTE(siglen)
355 1735672 : union_l[i] |= ptr[i];
356 : }
357 1538 : *left++ = j;
358 1538 : v->spl_nleft++;
359 : }
360 : else
361 : {
362 1730 : 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 1730 : ptr = LTG_SIGN(_j);
370 2106050 : ALOOPBYTE(siglen)
371 2104320 : union_r[i] |= ptr[i];
372 : }
373 1730 : *right++ = j;
374 1730 : v->spl_nright++;
375 : }
376 : }
377 :
378 949 : *right = *left = FirstOffsetNumber;
379 :
380 949 : v->spl_ldatum = PointerGetDatum(datum_l);
381 949 : v->spl_rdatum = PointerGetDatum(datum_r);
382 :
383 949 : PG_RETURN_POINTER(v);
384 : }
385 :
386 : static bool
387 2633 : gist_te(ltree_gist *key, ltree *query, int siglen)
388 : {
389 2633 : ltree_level *curq = LTREE_FIRST(query);
390 2633 : BITVECP sign = LTG_SIGN(key);
391 2633 : int qlen = query->numlevel;
392 : unsigned int hv;
393 :
394 2633 : if (LTG_ISALLTRUE(key))
395 0 : return true;
396 :
397 8867 : while (qlen > 0)
398 : {
399 6789 : hv = ltree_crc32_sz(curq->name, curq->len);
400 6789 : if (!GETBIT(sign, AHASHVAL(hv, siglen)))
401 555 : return false;
402 6234 : curq = LEVEL_NEXT(curq);
403 6234 : qlen--;
404 : }
405 :
406 2078 : return true;
407 : }
408 :
409 : typedef struct LtreeSignature
410 : {
411 : BITVECP sign;
412 : int siglen;
413 : } LtreeSignature;
414 :
415 : static bool
416 3981 : checkcondition_bit(void *cxt, ITEM *val)
417 : {
418 3981 : LtreeSignature *sig = cxt;
419 :
420 3981 : return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
421 : }
422 :
423 : static bool
424 2373 : gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
425 : {
426 : LtreeSignature sig;
427 :
428 2373 : if (LTG_ISALLTRUE(key))
429 0 : return true;
430 :
431 2373 : sig.sign = LTG_SIGN(key);
432 2373 : sig.siglen = siglen;
433 :
434 2373 : return ltree_execute(GETQUERY(query),
435 : &sig, false,
436 : checkcondition_bit);
437 : }
438 :
439 : static bool
440 16042 : gist_qe(ltree_gist *key, lquery *query, int siglen)
441 : {
442 16042 : lquery_level *curq = LQUERY_FIRST(query);
443 16042 : BITVECP sign = LTG_SIGN(key);
444 16042 : int qlen = query->numlevel;
445 :
446 16042 : if (LTG_ISALLTRUE(key))
447 0 : return true;
448 :
449 48253 : while (qlen > 0)
450 : {
451 38697 : if (curq->numvar && LQL_CANLOOKSIGN(curq))
452 : {
453 27092 : bool isexist = false;
454 27092 : int vlen = curq->numvar;
455 27092 : lquery_variant *curv = LQL_FIRST(curq);
456 :
457 33578 : while (vlen > 0)
458 : {
459 27092 : if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
460 : {
461 20606 : isexist = true;
462 20606 : break;
463 : }
464 6486 : curv = LVAR_NEXT(curv);
465 6486 : vlen--;
466 : }
467 27092 : if (!isexist)
468 6486 : return false;
469 : }
470 :
471 32211 : curq = LQL_NEXT(curq);
472 32211 : qlen--;
473 : }
474 :
475 9556 : return true;
476 : }
477 :
478 : static bool
479 2526 : _arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
480 : {
481 2526 : lquery *query = (lquery *) ARR_DATA_PTR(_query);
482 2526 : int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
483 :
484 2526 : if (ARR_NDIM(_query) > 1)
485 0 : ereport(ERROR,
486 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
487 : errmsg("array must be one-dimensional")));
488 2526 : 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 4653 : while (num > 0)
494 : {
495 3704 : if (gist_qe(key, query, siglen))
496 1577 : return true;
497 2127 : num--;
498 2127 : query = (lquery *) NEXTVAL(query);
499 : }
500 949 : return false;
501 : }
502 :
503 : Datum
504 19870 : _ltree_consistent(PG_FUNCTION_ARGS)
505 : {
506 19870 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
507 19870 : void *query = PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
508 19870 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
509 : #ifdef NOT_USED
510 : Oid subtype = PG_GETARG_OID(3);
511 : #endif
512 19870 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
513 19870 : int siglen = LTREE_GET_ASIGLEN();
514 19870 : ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
515 19870 : bool res = false;
516 :
517 : /* All cases served by this function are inexact */
518 19870 : *recheck = true;
519 :
520 19870 : switch (strategy)
521 : {
522 2633 : case 10:
523 : case 11:
524 2633 : res = gist_te(key, (ltree *) query, siglen);
525 2633 : break;
526 12338 : case 12:
527 : case 13:
528 12338 : res = gist_qe(key, (lquery *) query, siglen);
529 12338 : break;
530 2373 : case 14:
531 : case 15:
532 2373 : res = gist_qtxt(key, (ltxtquery *) query, siglen);
533 2373 : break;
534 2526 : case 16:
535 : case 17:
536 2526 : res = _arrq_cons(key, (ArrayType *) query, siglen);
537 2526 : break;
538 0 : default:
539 : /* internal error */
540 0 : elog(ERROR, "unrecognized StrategyNumber: %d", strategy);
541 : }
542 19870 : PG_FREE_IF_COPY(query, 1);
543 19870 : PG_RETURN_BOOL(res);
544 : }
545 :
546 : Datum
547 10 : _ltree_gist_options(PG_FUNCTION_ARGS)
548 : {
549 10 : local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
550 :
551 10 : init_local_reloptions(relopts, sizeof(LtreeGistOptions));
552 10 : add_local_int_reloption(relopts, "siglen", "signature length",
553 : LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX,
554 : offsetof(LtreeGistOptions, siglen));
555 :
556 10 : PG_RETURN_VOID();
557 : }
|