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 5997 : _ltree_compress(PG_FUNCTION_ARGS)
50 : {
51 5997 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
52 5997 : GISTENTRY *retval = entry;
53 5997 : int siglen = LTREE_GET_ASIGLEN();
54 :
55 5997 : 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 3997 : else if (!LTG_ISALLTRUE(DatumGetPointer(entry->key)))
86 : {
87 : int32 i;
88 : ltree_gist *key;
89 3997 : BITVECP sign = LTG_SIGN(DatumGetPointer(entry->key));
90 :
91 3997 : ALOOPBYTE(siglen)
92 : {
93 3997 : if ((sign[i] & 0xff) != 0xff)
94 3997 : 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 8803 : _ltree_same(PG_FUNCTION_ARGS)
108 : {
109 8803 : ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
110 8803 : ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
111 8803 : bool *result = (bool *) PG_GETARG_POINTER(2);
112 8803 : int siglen = LTREE_GET_ASIGLEN();
113 :
114 8803 : if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
115 0 : *result = true;
116 8803 : else if (LTG_ISALLTRUE(a))
117 0 : *result = false;
118 8803 : else if (LTG_ISALLTRUE(b))
119 0 : *result = false;
120 : else
121 : {
122 : int32 i;
123 8803 : BITVECP sa = LTG_SIGN(a),
124 8803 : sb = LTG_SIGN(b);
125 :
126 8803 : *result = true;
127 13067233 : ALOOPBYTE(siglen)
128 : {
129 13060595 : if (sa[i] != sb[i])
130 : {
131 2165 : *result = false;
132 2165 : break;
133 : }
134 : }
135 : }
136 8803 : PG_RETURN_POINTER(result);
137 : }
138 :
139 : static int32
140 17606 : unionkey(BITVECP sbase, ltree_gist *add, int siglen)
141 : {
142 : int32 i;
143 17606 : BITVECP sadd = LTG_SIGN(add);
144 :
145 17606 : if (LTG_ISALLTRUE(add))
146 0 : return 1;
147 :
148 32223022 : ALOOPBYTE(siglen)
149 32205416 : sbase[i] |= sadd[i];
150 17606 : return 0;
151 : }
152 :
153 : Datum
154 8803 : _ltree_union(PG_FUNCTION_ARGS)
155 : {
156 8803 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
157 8803 : int *size = (int *) PG_GETARG_POINTER(1);
158 8803 : int siglen = LTREE_GET_ASIGLEN();
159 : int32 i;
160 8803 : ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
161 8803 : BITVECP base = LTG_SIGN(result);
162 :
163 26409 : for (i = 0; i < entryvec->n; i++)
164 : {
165 17606 : 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 8803 : *size = VARSIZE(result);
174 :
175 8803 : 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 126392 : hemdistsign(BITVECP a, BITVECP b, int siglen)
186 : {
187 : int i,
188 : diff,
189 126392 : dist = 0;
190 :
191 60978512 : ALOOPBYTE(siglen)
192 : {
193 60852120 : diff = (unsigned char) (a[i] ^ b[i]);
194 : /* Using the popcount functions here isn't likely to win */
195 60852120 : dist += pg_number_of_ones[diff];
196 : }
197 126392 : return dist;
198 : }
199 :
200 : static int
201 126392 : hemdist(ltree_gist *a, ltree_gist *b, int siglen)
202 : {
203 126392 : 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 126392 : else if (LTG_ISALLTRUE(b))
211 0 : return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
212 :
213 126392 : return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
214 : }
215 :
216 :
217 : Datum
218 20177 : _ltree_penalty(PG_FUNCTION_ARGS)
219 : {
220 20177 : ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
221 20177 : ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
222 20177 : float *penalty = (float *) PG_GETARG_POINTER(2);
223 20177 : int siglen = LTREE_GET_ASIGLEN();
224 :
225 20177 : *penalty = hemdist(origval, newval, siglen);
226 20177 : PG_RETURN_POINTER(penalty);
227 : }
228 :
229 : typedef struct
230 : {
231 : OffsetNumber pos;
232 : int32 cost;
233 : } SPLITCOST;
234 :
235 : static int
236 8148 : comparecost(const void *a, const void *b)
237 : {
238 8148 : return ((const SPLITCOST *) a)->cost - ((const SPLITCOST *) b)->cost;
239 : }
240 :
241 : Datum
242 916 : _ltree_picksplit(PG_FUNCTION_ARGS)
243 : {
244 916 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
245 916 : GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
246 916 : 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 916 : waste = -1;
257 : int32 nbytes;
258 916 : OffsetNumber seed_1 = 0,
259 916 : 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 916 : maxoff = entryvec->n - 2;
270 916 : nbytes = (maxoff + 2) * sizeof(OffsetNumber);
271 916 : v->spl_left = (OffsetNumber *) palloc(nbytes);
272 916 : v->spl_right = (OffsetNumber *) palloc(nbytes);
273 :
274 3981 : for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
275 : {
276 3065 : _k = GETENTRY(entryvec, k);
277 93356 : for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
278 : {
279 90291 : size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
280 90291 : if (size_waste > waste)
281 : {
282 1685 : waste = size_waste;
283 1685 : seed_1 = k;
284 1685 : seed_2 = j;
285 : }
286 : }
287 : }
288 :
289 916 : left = v->spl_left;
290 916 : v->spl_nleft = 0;
291 916 : right = v->spl_right;
292 916 : v->spl_nright = 0;
293 :
294 916 : if (seed_1 == 0 || seed_2 == 0)
295 : {
296 0 : seed_1 = 1;
297 0 : seed_2 = 2;
298 : }
299 :
300 : /* form initial .. */
301 916 : datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
302 916 : LTG_SIGN(GETENTRY(entryvec, seed_1)),
303 : siglen, NULL, NULL);
304 :
305 916 : datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
306 916 : LTG_SIGN(GETENTRY(entryvec, seed_2)),
307 : siglen, NULL, NULL);
308 :
309 916 : maxoff = OffsetNumberNext(maxoff);
310 : /* sort before ... */
311 916 : costvector = palloc_array(SPLITCOST, maxoff);
312 5813 : for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
313 : {
314 4897 : costvector[j - 1].pos = j;
315 4897 : _j = GETENTRY(entryvec, j);
316 4897 : size_alpha = hemdist(datum_l, _j, siglen);
317 4897 : size_beta = hemdist(datum_r, _j, siglen);
318 4897 : costvector[j - 1].cost = abs(size_alpha - size_beta);
319 : }
320 916 : qsort(costvector, maxoff, sizeof(SPLITCOST), comparecost);
321 :
322 916 : union_l = LTG_SIGN(datum_l);
323 916 : union_r = LTG_SIGN(datum_r);
324 :
325 5813 : for (k = 0; k < maxoff; k++)
326 : {
327 4897 : j = costvector[k].pos;
328 4897 : if (j == seed_1)
329 : {
330 916 : *left++ = j;
331 916 : v->spl_nleft++;
332 916 : continue;
333 : }
334 3981 : else if (j == seed_2)
335 : {
336 916 : *right++ = j;
337 916 : v->spl_nright++;
338 916 : continue;
339 : }
340 3065 : _j = GETENTRY(entryvec, j);
341 3065 : size_alpha = hemdist(datum_l, _j, siglen);
342 3065 : size_beta = hemdist(datum_r, _j, siglen);
343 :
344 3065 : if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
345 : {
346 1428 : 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 1428 : ptr = LTG_SIGN(_j);
354 1680128 : ALOOPBYTE(siglen)
355 1678700 : union_l[i] |= ptr[i];
356 : }
357 1428 : *left++ = j;
358 1428 : v->spl_nleft++;
359 : }
360 : else
361 : {
362 1637 : 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 1637 : ptr = LTG_SIGN(_j);
370 2029501 : ALOOPBYTE(siglen)
371 2027864 : union_r[i] |= ptr[i];
372 : }
373 1637 : *right++ = j;
374 1637 : v->spl_nright++;
375 : }
376 : }
377 :
378 916 : *right = *left = FirstOffsetNumber;
379 :
380 916 : v->spl_ldatum = PointerGetDatum(datum_l);
381 916 : v->spl_rdatum = PointerGetDatum(datum_r);
382 :
383 916 : PG_RETURN_POINTER(v);
384 : }
385 :
386 : static bool
387 2628 : gist_te(ltree_gist *key, ltree *query, int siglen)
388 : {
389 2628 : ltree_level *curq = LTREE_FIRST(query);
390 2628 : BITVECP sign = LTG_SIGN(key);
391 2628 : int qlen = query->numlevel;
392 : unsigned int hv;
393 :
394 2628 : if (LTG_ISALLTRUE(key))
395 0 : return true;
396 :
397 8820 : while (qlen > 0)
398 : {
399 6756 : hv = ltree_crc32_sz(curq->name, curq->len);
400 6756 : if (!GETBIT(sign, AHASHVAL(hv, siglen)))
401 564 : return false;
402 6192 : curq = LEVEL_NEXT(curq);
403 6192 : qlen--;
404 : }
405 :
406 2064 : return true;
407 : }
408 :
409 : typedef struct LtreeSignature
410 : {
411 : BITVECP sign;
412 : int siglen;
413 : } LtreeSignature;
414 :
415 : static bool
416 3730 : checkcondition_bit(void *cxt, ITEM *val)
417 : {
418 3730 : LtreeSignature *sig = cxt;
419 :
420 3730 : return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
421 : }
422 :
423 : static bool
424 2179 : gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
425 : {
426 : LtreeSignature sig;
427 :
428 2179 : if (LTG_ISALLTRUE(key))
429 0 : return true;
430 :
431 2179 : sig.sign = LTG_SIGN(key);
432 2179 : sig.siglen = siglen;
433 :
434 2179 : return ltree_execute(GETQUERY(query),
435 : &sig, false,
436 : checkcondition_bit);
437 : }
438 :
439 : static bool
440 14897 : gist_qe(ltree_gist *key, lquery *query, int siglen)
441 : {
442 14897 : lquery_level *curq = LQUERY_FIRST(query);
443 14897 : BITVECP sign = LTG_SIGN(key);
444 14897 : int qlen = query->numlevel;
445 :
446 14897 : if (LTG_ISALLTRUE(key))
447 0 : return true;
448 :
449 45958 : while (qlen > 0)
450 : {
451 36727 : if (curq->numvar && LQL_CANLOOKSIGN(curq))
452 : {
453 25530 : bool isexist = false;
454 25530 : int vlen = curq->numvar;
455 25530 : lquery_variant *curv = LQL_FIRST(curq);
456 :
457 31196 : while (vlen > 0)
458 : {
459 25530 : if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
460 : {
461 19864 : isexist = true;
462 19864 : break;
463 : }
464 5666 : curv = LVAR_NEXT(curv);
465 5666 : vlen--;
466 : }
467 25530 : if (!isexist)
468 5666 : return false;
469 : }
470 :
471 31061 : curq = LQL_NEXT(curq);
472 31061 : qlen--;
473 : }
474 :
475 9231 : return true;
476 : }
477 :
478 : static bool
479 2275 : _arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
480 : {
481 2275 : lquery *query = (lquery *) ARR_DATA_PTR(_query);
482 2275 : int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
483 :
484 2275 : if (ARR_NDIM(_query) > 1)
485 0 : ereport(ERROR,
486 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
487 : errmsg("array must be one-dimensional")));
488 2275 : 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 4042 : while (num > 0)
494 : {
495 3258 : if (gist_qe(key, query, siglen))
496 1491 : return true;
497 1767 : num--;
498 1767 : query = (lquery *) NEXTVAL(query);
499 : }
500 784 : return false;
501 : }
502 :
503 : Datum
504 18721 : _ltree_consistent(PG_FUNCTION_ARGS)
505 : {
506 18721 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
507 18721 : void *query = PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
508 18721 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
509 : #ifdef NOT_USED
510 : Oid subtype = PG_GETARG_OID(3);
511 : #endif
512 18721 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
513 18721 : int siglen = LTREE_GET_ASIGLEN();
514 18721 : ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
515 18721 : bool res = false;
516 :
517 : /* All cases served by this function are inexact */
518 18721 : *recheck = true;
519 :
520 18721 : switch (strategy)
521 : {
522 2628 : case 10:
523 : case 11:
524 2628 : res = gist_te(key, (ltree *) query, siglen);
525 2628 : break;
526 11639 : case 12:
527 : case 13:
528 11639 : res = gist_qe(key, (lquery *) query, siglen);
529 11639 : break;
530 2179 : case 14:
531 : case 15:
532 2179 : res = gist_qtxt(key, (ltxtquery *) query, siglen);
533 2179 : break;
534 2275 : case 16:
535 : case 17:
536 2275 : res = _arrq_cons(key, (ArrayType *) query, siglen);
537 2275 : break;
538 0 : default:
539 : /* internal error */
540 0 : elog(ERROR, "unrecognized StrategyNumber: %d", strategy);
541 : }
542 18721 : PG_FREE_IF_COPY(query, 1);
543 18721 : 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 : }
|