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