LCOV - code coverage report
Current view: top level - contrib/pg_trgm - trgm_gist.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 79.8 % 431 344
Test Date: 2026-03-02 21:14:50 Functions: 87.5 % 32 28
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * contrib/pg_trgm/trgm_gist.c
       3              :  */
       4              : #include "postgres.h"
       5              : 
       6              : #include "access/reloptions.h"
       7              : #include "access/stratnum.h"
       8              : #include "fmgr.h"
       9              : #include "port/pg_bitutils.h"
      10              : #include "trgm.h"
      11              : #include "varatt.h"
      12              : 
      13              : /* gist_trgm_ops opclass options */
      14              : typedef struct
      15              : {
      16              :     int32       vl_len_;        /* varlena header (do not touch directly!) */
      17              :     int         siglen;         /* signature length in bytes */
      18              : } TrgmGistOptions;
      19              : 
      20              : #define GET_SIGLEN()            (PG_HAS_OPCLASS_OPTIONS() ? \
      21              :                                  ((TrgmGistOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
      22              :                                  SIGLEN_DEFAULT)
      23              : 
      24              : typedef struct
      25              : {
      26              :     /* most recent inputs to gtrgm_consistent */
      27              :     StrategyNumber strategy;
      28              :     text       *query;
      29              :     /* extracted trigrams for query */
      30              :     TRGM       *trigrams;
      31              :     /* if a regex operator, the extracted graph */
      32              :     TrgmPackedGraph *graph;
      33              : 
      34              :     /*
      35              :      * The "query" and "trigrams" are stored in the same palloc block as this
      36              :      * cache struct, at MAXALIGN'ed offsets.  The graph however isn't.
      37              :      */
      38              : } gtrgm_consistent_cache;
      39              : 
      40              : #define GETENTRY(vec,pos) ((TRGM *) DatumGetPointer((vec)->vector[(pos)].key))
      41              : 
      42              : 
      43            1 : PG_FUNCTION_INFO_V1(gtrgm_in);
      44            1 : PG_FUNCTION_INFO_V1(gtrgm_out);
      45            5 : PG_FUNCTION_INFO_V1(gtrgm_compress);
      46            5 : PG_FUNCTION_INFO_V1(gtrgm_decompress);
      47            5 : PG_FUNCTION_INFO_V1(gtrgm_consistent);
      48            5 : PG_FUNCTION_INFO_V1(gtrgm_distance);
      49            5 : PG_FUNCTION_INFO_V1(gtrgm_union);
      50            5 : PG_FUNCTION_INFO_V1(gtrgm_same);
      51            5 : PG_FUNCTION_INFO_V1(gtrgm_penalty);
      52            5 : PG_FUNCTION_INFO_V1(gtrgm_picksplit);
      53            5 : PG_FUNCTION_INFO_V1(gtrgm_options);
      54              : 
      55              : 
      56              : Datum
      57            0 : gtrgm_in(PG_FUNCTION_ARGS)
      58              : {
      59            0 :     ereport(ERROR,
      60              :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
      61              :              errmsg("cannot accept a value of type %s", "gtrgm")));
      62              : 
      63              :     PG_RETURN_VOID();           /* keep compiler quiet */
      64              : }
      65              : 
      66              : Datum
      67            0 : gtrgm_out(PG_FUNCTION_ARGS)
      68              : {
      69            0 :     ereport(ERROR,
      70              :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
      71              :              errmsg("cannot display a value of type %s", "gtrgm")));
      72              : 
      73              :     PG_RETURN_VOID();           /* keep compiler quiet */
      74              : }
      75              : 
      76              : static TRGM *
      77        28264 : gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign)
      78              : {
      79        28264 :     int         flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0);
      80        28264 :     int         size = CALCGTSIZE(flag, siglen);
      81        28264 :     TRGM       *res = palloc(size);
      82              : 
      83        28264 :     SET_VARSIZE(res, size);
      84        28264 :     res->flag = flag;
      85              : 
      86        28264 :     if (!isalltrue)
      87              :     {
      88        28264 :         if (sign)
      89          572 :             memcpy(GETSIGN(res), sign, siglen);
      90              :         else
      91        27692 :             memset(GETSIGN(res), 0, siglen);
      92              :     }
      93              : 
      94        28264 :     return res;
      95              : }
      96              : 
      97              : static void
      98        48133 : makesign(BITVECP sign, TRGM *a, int siglen)
      99              : {
     100              :     int32       k,
     101        48133 :                 len = ARRNELEM(a);
     102        48133 :     trgm       *ptr = GETARR(a);
     103        48133 :     int32       tmp = 0;
     104              : 
     105        48133 :     MemSet(sign, 0, siglen);
     106        48133 :     SETBIT(sign, SIGLENBIT(siglen));    /* set last unused bit */
     107       475073 :     for (k = 0; k < len; k++)
     108              :     {
     109       426940 :         CPTRGM(&tmp, ptr + k);
     110       426940 :         HASH(sign, tmp, siglen);
     111              :     }
     112        48133 : }
     113              : 
     114              : Datum
     115        27880 : gtrgm_compress(PG_FUNCTION_ARGS)
     116              : {
     117        27880 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     118        27880 :     int         siglen = GET_SIGLEN();
     119        27880 :     GISTENTRY  *retval = entry;
     120              : 
     121        27880 :     if (entry->leafkey)
     122              :     {                           /* trgm */
     123              :         TRGM       *res;
     124        24452 :         text       *val = DatumGetTextPP(entry->key);
     125              : 
     126        24452 :         res = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val));
     127        24452 :         retval = palloc_object(GISTENTRY);
     128        24452 :         gistentryinit(*retval, PointerGetDatum(res),
     129              :                       entry->rel, entry->page,
     130              :                       entry->offset, false);
     131              :     }
     132         3428 :     else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
     133         3428 :              !ISALLTRUE(DatumGetPointer(entry->key)))
     134              :     {
     135              :         int32       i;
     136              :         TRGM       *res;
     137         3428 :         BITVECP     sign = GETSIGN(DatumGetPointer(entry->key));
     138              : 
     139         3654 :         LOOPBYTE(siglen)
     140              :         {
     141         3654 :             if ((sign[i] & 0xff) != 0xff)
     142         3428 :                 PG_RETURN_POINTER(retval);
     143              :         }
     144              : 
     145            0 :         res = gtrgm_alloc(true, siglen, sign);
     146            0 :         retval = palloc_object(GISTENTRY);
     147            0 :         gistentryinit(*retval, PointerGetDatum(res),
     148              :                       entry->rel, entry->page,
     149              :                       entry->offset, false);
     150              :     }
     151        24452 :     PG_RETURN_POINTER(retval);
     152              : }
     153              : 
     154              : Datum
     155      1319755 : gtrgm_decompress(PG_FUNCTION_ARGS)
     156              : {
     157      1319755 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     158              :     GISTENTRY  *retval;
     159              :     text       *key;
     160              : 
     161      1319755 :     key = DatumGetTextPP(entry->key);
     162              : 
     163      1319755 :     if (key != (text *) DatumGetPointer(entry->key))
     164              :     {
     165              :         /* need to pass back the decompressed item */
     166            0 :         retval = palloc_object(GISTENTRY);
     167            0 :         gistentryinit(*retval, PointerGetDatum(key),
     168              :                       entry->rel, entry->page, entry->offset, entry->leafkey);
     169            0 :         PG_RETURN_POINTER(retval);
     170              :     }
     171              :     else
     172              :     {
     173              :         /* we can return the entry as-is */
     174      1319755 :         PG_RETURN_POINTER(entry);
     175              :     }
     176              : }
     177              : 
     178              : static int32
     179          668 : cnt_sml_sign_common(TRGM *qtrg, BITVECP sign, int siglen)
     180              : {
     181          668 :     int32       count = 0;
     182              :     int32       k,
     183          668 :                 len = ARRNELEM(qtrg);
     184          668 :     trgm       *ptr = GETARR(qtrg);
     185          668 :     int32       tmp = 0;
     186              : 
     187         6194 :     for (k = 0; k < len; k++)
     188              :     {
     189         5526 :         CPTRGM(&tmp, ptr + k);
     190         5526 :         count += GETBIT(sign, HASHVAL(tmp, siglen));
     191              :     }
     192              : 
     193          668 :     return count;
     194              : }
     195              : 
     196              : Datum
     197        36424 : gtrgm_consistent(PG_FUNCTION_ARGS)
     198              : {
     199        36424 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     200        36424 :     text       *query = PG_GETARG_TEXT_P(1);
     201        36424 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     202              : #ifdef NOT_USED
     203              :     Oid         subtype = PG_GETARG_OID(3);
     204              : #endif
     205        36424 :     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     206        36424 :     int         siglen = GET_SIGLEN();
     207        36424 :     TRGM       *key = (TRGM *) DatumGetPointer(entry->key);
     208              :     TRGM       *qtrg;
     209              :     bool        res;
     210        36424 :     Size        querysize = VARSIZE(query);
     211              :     gtrgm_consistent_cache *cache;
     212              :     double      nlimit;
     213              : 
     214              :     /*
     215              :      * We keep the extracted trigrams in cache, because trigram extraction is
     216              :      * relatively CPU-expensive.  When trying to reuse a cached value, check
     217              :      * strategy number not just query itself, because trigram extraction
     218              :      * depends on strategy.
     219              :      *
     220              :      * The cached structure is a single palloc chunk containing the
     221              :      * gtrgm_consistent_cache header, then the input query (4-byte length
     222              :      * word, uncompressed, starting at a MAXALIGN boundary), then the TRGM
     223              :      * value (also starting at a MAXALIGN boundary).  However we don't try to
     224              :      * include the regex graph (if any) in that struct.  (XXX currently, this
     225              :      * approach can leak regex graphs across index rescans.  Not clear if
     226              :      * that's worth fixing.)
     227              :      */
     228        36424 :     cache = (gtrgm_consistent_cache *) fcinfo->flinfo->fn_extra;
     229        36424 :     if (cache == NULL ||
     230        72736 :         cache->strategy != strategy ||
     231        36368 :         VARSIZE(cache->query) != querysize ||
     232        36368 :         memcmp(cache->query, query, querysize) != 0)
     233              :     {
     234              :         gtrgm_consistent_cache *newcache;
     235           56 :         TrgmPackedGraph *graph = NULL;
     236              :         Size        qtrgsize;
     237              : 
     238           56 :         switch (strategy)
     239              :         {
     240           28 :             case SimilarityStrategyNumber:
     241              :             case WordSimilarityStrategyNumber:
     242              :             case StrictWordSimilarityStrategyNumber:
     243              :             case EqualStrategyNumber:
     244           28 :                 qtrg = generate_trgm(VARDATA(query),
     245           28 :                                      querysize - VARHDRSZ);
     246           28 :                 break;
     247            7 :             case ILikeStrategyNumber:
     248              : #ifndef IGNORECASE
     249              :                 elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
     250              : #endif
     251              :                 pg_fallthrough;
     252              :             case LikeStrategyNumber:
     253            7 :                 qtrg = generate_wildcard_trgm(VARDATA(query),
     254            7 :                                               querysize - VARHDRSZ);
     255            7 :                 break;
     256           21 :             case RegExpICaseStrategyNumber:
     257              : #ifndef IGNORECASE
     258              :                 elog(ERROR, "cannot handle ~* with case-sensitive trigrams");
     259              : #endif
     260              :                 pg_fallthrough;
     261              :             case RegExpStrategyNumber:
     262           21 :                 qtrg = createTrgmNFA(query, PG_GET_COLLATION(),
     263           21 :                                      &graph, fcinfo->flinfo->fn_mcxt);
     264              :                 /* just in case an empty array is returned ... */
     265           21 :                 if (qtrg && ARRNELEM(qtrg) <= 0)
     266              :                 {
     267            0 :                     pfree(qtrg);
     268            0 :                     qtrg = NULL;
     269              :                 }
     270           21 :                 break;
     271            0 :             default:
     272            0 :                 elog(ERROR, "unrecognized strategy number: %d", strategy);
     273              :                 qtrg = NULL;    /* keep compiler quiet */
     274              :                 break;
     275              :         }
     276              : 
     277           56 :         qtrgsize = qtrg ? VARSIZE(qtrg) : 0;
     278              : 
     279              :         newcache = (gtrgm_consistent_cache *)
     280           56 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     281              :                                MAXALIGN(sizeof(gtrgm_consistent_cache)) +
     282           56 :                                MAXALIGN(querysize) +
     283              :                                qtrgsize);
     284              : 
     285           56 :         newcache->strategy = strategy;
     286           56 :         newcache->query = (text *)
     287              :             ((char *) newcache + MAXALIGN(sizeof(gtrgm_consistent_cache)));
     288           56 :         memcpy(newcache->query, query, querysize);
     289           56 :         if (qtrg)
     290              :         {
     291           52 :             newcache->trigrams = (TRGM *)
     292           52 :                 ((char *) newcache->query + MAXALIGN(querysize));
     293           52 :             memcpy((char *) newcache->trigrams, qtrg, qtrgsize);
     294              :             /* release qtrg in case it was made in fn_mcxt */
     295           52 :             pfree(qtrg);
     296              :         }
     297              :         else
     298            4 :             newcache->trigrams = NULL;
     299           56 :         newcache->graph = graph;
     300              : 
     301           56 :         if (cache)
     302            0 :             pfree(cache);
     303           56 :         fcinfo->flinfo->fn_extra = newcache;
     304           56 :         cache = newcache;
     305              :     }
     306              : 
     307        36424 :     qtrg = cache->trigrams;
     308              : 
     309        36424 :     switch (strategy)
     310              :     {
     311        34018 :         case SimilarityStrategyNumber:
     312              :         case WordSimilarityStrategyNumber:
     313              :         case StrictWordSimilarityStrategyNumber:
     314              : 
     315              :             /*
     316              :              * Similarity search is exact. (Strict) word similarity search is
     317              :              * inexact
     318              :              */
     319        34018 :             *recheck = (strategy != SimilarityStrategyNumber);
     320              : 
     321        34018 :             nlimit = index_strategy_get_limit(strategy);
     322              : 
     323        34018 :             if (GIST_LEAF(entry))
     324              :             {                   /* all leafs contains orig trgm */
     325        33394 :                 double      tmpsml = cnt_sml(qtrg, key, *recheck);
     326              : 
     327        33394 :                 res = (tmpsml >= nlimit);
     328              :             }
     329          624 :             else if (ISALLTRUE(key))
     330              :             {                   /* non-leaf contains signature */
     331            0 :                 res = true;
     332              :             }
     333              :             else
     334              :             {                   /* non-leaf contains signature */
     335          624 :                 int32       count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
     336          624 :                 int32       len = ARRNELEM(qtrg);
     337              : 
     338          624 :                 if (len == 0)
     339            0 :                     res = false;
     340              :                 else
     341          624 :                     res = (((((float8) count) / ((float8) len))) >= nlimit);
     342              :             }
     343        34018 :             break;
     344          190 :         case ILikeStrategyNumber:
     345              : #ifndef IGNORECASE
     346              :             elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
     347              : #endif
     348              :             pg_fallthrough;
     349              :         case LikeStrategyNumber:
     350              :         case EqualStrategyNumber:
     351              :             /* Wildcard and equal search are inexact */
     352          190 :             *recheck = true;
     353              : 
     354              :             /*
     355              :              * Check if all the extracted trigrams can be present in child
     356              :              * nodes.
     357              :              */
     358          190 :             if (GIST_LEAF(entry))
     359              :             {                   /* all leafs contains orig trgm */
     360          190 :                 res = trgm_contained_by(qtrg, key);
     361              :             }
     362            0 :             else if (ISALLTRUE(key))
     363              :             {                   /* non-leaf contains signature */
     364            0 :                 res = true;
     365              :             }
     366              :             else
     367              :             {                   /* non-leaf contains signature */
     368              :                 int32       k,
     369            0 :                             tmp = 0,
     370            0 :                             len = ARRNELEM(qtrg);
     371            0 :                 trgm       *ptr = GETARR(qtrg);
     372            0 :                 BITVECP     sign = GETSIGN(key);
     373              : 
     374            0 :                 res = true;
     375            0 :                 for (k = 0; k < len; k++)
     376              :                 {
     377            0 :                     CPTRGM(&tmp, ptr + k);
     378            0 :                     if (!GETBIT(sign, HASHVAL(tmp, siglen)))
     379              :                     {
     380            0 :                         res = false;
     381            0 :                         break;
     382              :                     }
     383              :                 }
     384              :             }
     385          190 :             break;
     386         2216 :         case RegExpICaseStrategyNumber:
     387              : #ifndef IGNORECASE
     388              :             elog(ERROR, "cannot handle ~* with case-sensitive trigrams");
     389              : #endif
     390              :             pg_fallthrough;
     391              :         case RegExpStrategyNumber:
     392              :             /* Regexp search is inexact */
     393         2216 :             *recheck = true;
     394              : 
     395              :             /* Check regex match as much as we can with available info */
     396         2216 :             if (qtrg)
     397              :             {
     398         2176 :                 if (GIST_LEAF(entry))
     399              :                 {               /* all leafs contains orig trgm */
     400              :                     bool       *check;
     401              : 
     402         2150 :                     check = trgm_presence_map(qtrg, key);
     403         2150 :                     res = trigramsMatchGraph(cache->graph, check);
     404         2150 :                     pfree(check);
     405              :                 }
     406           26 :                 else if (ISALLTRUE(key))
     407              :                 {               /* non-leaf contains signature */
     408            0 :                     res = true;
     409              :                 }
     410              :                 else
     411              :                 {               /* non-leaf contains signature */
     412              :                     int32       k,
     413           26 :                                 tmp = 0,
     414           26 :                                 len = ARRNELEM(qtrg);
     415           26 :                     trgm       *ptr = GETARR(qtrg);
     416           26 :                     BITVECP     sign = GETSIGN(key);
     417              :                     bool       *check;
     418              : 
     419              :                     /*
     420              :                      * GETBIT() tests may give false positives, due to limited
     421              :                      * size of the sign array.  But since trigramsMatchGraph()
     422              :                      * implements a monotone boolean function, false positives
     423              :                      * in the check array can't lead to false negative answer.
     424              :                      * So we can apply trigramsMatchGraph despite uncertainty,
     425              :                      * and that usefully improves the quality of the search.
     426              :                      */
     427           26 :                     check = (bool *) palloc(len * sizeof(bool));
     428         6578 :                     for (k = 0; k < len; k++)
     429              :                     {
     430         6552 :                         CPTRGM(&tmp, ptr + k);
     431         6552 :                         check[k] = GETBIT(sign, HASHVAL(tmp, siglen));
     432              :                     }
     433           26 :                     res = trigramsMatchGraph(cache->graph, check);
     434           26 :                     pfree(check);
     435              :                 }
     436              :             }
     437              :             else
     438              :             {
     439              :                 /* trigram-free query must be rechecked everywhere */
     440           40 :                 res = true;
     441              :             }
     442         2216 :             break;
     443            0 :         default:
     444            0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
     445              :             res = false;        /* keep compiler quiet */
     446              :             break;
     447              :     }
     448              : 
     449        36424 :     PG_RETURN_BOOL(res);
     450              : }
     451              : 
     452              : Datum
     453         2884 : gtrgm_distance(PG_FUNCTION_ARGS)
     454              : {
     455         2884 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     456         2884 :     text       *query = PG_GETARG_TEXT_P(1);
     457         2884 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     458              : #ifdef NOT_USED
     459              :     Oid         subtype = PG_GETARG_OID(3);
     460              : #endif
     461         2884 :     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     462         2884 :     int         siglen = GET_SIGLEN();
     463         2884 :     TRGM       *key = (TRGM *) DatumGetPointer(entry->key);
     464              :     TRGM       *qtrg;
     465              :     float8      res;
     466         2884 :     Size        querysize = VARSIZE(query);
     467         2884 :     char       *cache = (char *) fcinfo->flinfo->fn_extra;
     468              : 
     469              :     /*
     470              :      * Cache the generated trigrams across multiple calls with the same query.
     471              :      */
     472         5764 :     if (cache == NULL ||
     473         2880 :         VARSIZE(cache) != querysize ||
     474         2880 :         memcmp(cache, query, querysize) != 0)
     475              :     {
     476              :         char       *newcache;
     477              : 
     478            4 :         qtrg = generate_trgm(VARDATA(query), querysize - VARHDRSZ);
     479              : 
     480            4 :         newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     481            4 :                                       MAXALIGN(querysize) +
     482            4 :                                       VARSIZE(qtrg));
     483              : 
     484            4 :         memcpy(newcache, query, querysize);
     485            4 :         memcpy(newcache + MAXALIGN(querysize), qtrg, VARSIZE(qtrg));
     486              : 
     487            4 :         if (cache)
     488            0 :             pfree(cache);
     489            4 :         fcinfo->flinfo->fn_extra = newcache;
     490            4 :         cache = newcache;
     491              :     }
     492              : 
     493         2884 :     qtrg = (TRGM *) (cache + MAXALIGN(querysize));
     494              : 
     495         2884 :     switch (strategy)
     496              :     {
     497         2884 :         case DistanceStrategyNumber:
     498              :         case WordDistanceStrategyNumber:
     499              :         case StrictWordDistanceStrategyNumber:
     500              :             /* Only plain trigram distance is exact */
     501         2884 :             *recheck = (strategy != DistanceStrategyNumber);
     502         2884 :             if (GIST_LEAF(entry))
     503              :             {                   /* all leafs contains orig trgm */
     504              : 
     505              :                 /*
     506              :                  * Prevent gcc optimizing the sml variable using volatile
     507              :                  * keyword. Otherwise res can differ from the
     508              :                  * word_similarity_dist_op() function.
     509              :                  */
     510         2840 :                 float4 volatile sml = cnt_sml(qtrg, key, *recheck);
     511              : 
     512         2840 :                 res = 1.0 - sml;
     513              :             }
     514           44 :             else if (ISALLTRUE(key))
     515              :             {                   /* all leafs contains orig trgm */
     516            0 :                 res = 0.0;
     517              :             }
     518              :             else
     519              :             {                   /* non-leaf contains signature */
     520           44 :                 int32       count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
     521           44 :                 int32       len = ARRNELEM(qtrg);
     522              : 
     523           44 :                 res = (len == 0) ? -1.0 : 1.0 - ((float8) count) / ((float8) len);
     524              :             }
     525         2884 :             break;
     526            0 :         default:
     527            0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
     528              :             res = 0;            /* keep compiler quiet */
     529              :             break;
     530              :     }
     531              : 
     532         2884 :     PG_RETURN_FLOAT8(res);
     533              : }
     534              : 
     535              : static int32
     536        55384 : unionkey(BITVECP sbase, TRGM *add, int siglen)
     537              : {
     538              :     int32       i;
     539              : 
     540        55384 :     if (ISSIGNKEY(add))
     541              :     {
     542        27692 :         BITVECP     sadd = GETSIGN(add);
     543              : 
     544        27692 :         if (ISALLTRUE(add))
     545            0 :             return 1;
     546              : 
     547      6589148 :         LOOPBYTE(siglen)
     548      6561456 :             sbase[i] |= sadd[i];
     549              :     }
     550              :     else
     551              :     {
     552        27692 :         trgm       *ptr = GETARR(add);
     553        27692 :         int32       tmp = 0;
     554              : 
     555       277055 :         for (i = 0; i < ARRNELEM(add); i++)
     556              :         {
     557       249363 :             CPTRGM(&tmp, ptr + i);
     558       249363 :             HASH(sbase, tmp, siglen);
     559              :         }
     560              :     }
     561        55384 :     return 0;
     562              : }
     563              : 
     564              : 
     565              : Datum
     566        27692 : gtrgm_union(PG_FUNCTION_ARGS)
     567              : {
     568        27692 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     569        27692 :     int32       len = entryvec->n;
     570        27692 :     int        *size = (int *) PG_GETARG_POINTER(1);
     571        27692 :     int         siglen = GET_SIGLEN();
     572              :     int32       i;
     573        27692 :     TRGM       *result = gtrgm_alloc(false, siglen, NULL);
     574        27692 :     BITVECP     base = GETSIGN(result);
     575              : 
     576        83076 :     for (i = 0; i < len; i++)
     577              :     {
     578        55384 :         if (unionkey(base, GETENTRY(entryvec, i), siglen))
     579              :         {
     580            0 :             result->flag = ALLISTRUE;
     581            0 :             SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
     582            0 :             break;
     583              :         }
     584              :     }
     585              : 
     586        27692 :     *size = VARSIZE(result);
     587              : 
     588        27692 :     PG_RETURN_POINTER(result);
     589              : }
     590              : 
     591              : Datum
     592        27692 : gtrgm_same(PG_FUNCTION_ARGS)
     593              : {
     594        27692 :     TRGM       *a = (TRGM *) PG_GETARG_POINTER(0);
     595        27692 :     TRGM       *b = (TRGM *) PG_GETARG_POINTER(1);
     596        27692 :     bool       *result = (bool *) PG_GETARG_POINTER(2);
     597        27692 :     int         siglen = GET_SIGLEN();
     598              : 
     599        27692 :     if (ISSIGNKEY(a))
     600              :     {                           /* then b also ISSIGNKEY */
     601        27692 :         if (ISALLTRUE(a) && ISALLTRUE(b))
     602            0 :             *result = true;
     603        27692 :         else if (ISALLTRUE(a))
     604            0 :             *result = false;
     605        27692 :         else if (ISALLTRUE(b))
     606            0 :             *result = false;
     607              :         else
     608              :         {
     609              :             int32       i;
     610        27692 :             BITVECP     sa = GETSIGN(a),
     611        27692 :                         sb = GETSIGN(b);
     612              : 
     613        27692 :             *result = true;
     614      3179644 :             LOOPBYTE(siglen)
     615              :             {
     616      3154808 :                 if (sa[i] != sb[i])
     617              :                 {
     618         2856 :                     *result = false;
     619         2856 :                     break;
     620              :                 }
     621              :             }
     622              :         }
     623              :     }
     624              :     else
     625              :     {                           /* a and b ISARRKEY */
     626            0 :         int32       lena = ARRNELEM(a),
     627            0 :                     lenb = ARRNELEM(b);
     628              : 
     629            0 :         if (lena != lenb)
     630            0 :             *result = false;
     631              :         else
     632              :         {
     633            0 :             trgm       *ptra = GETARR(a),
     634            0 :                        *ptrb = GETARR(b);
     635              :             int32       i;
     636              : 
     637            0 :             *result = true;
     638            0 :             for (i = 0; i < lena; i++)
     639            0 :                 if (CMPTRGM(ptra + i, ptrb + i))
     640              :                 {
     641            0 :                     *result = false;
     642            0 :                     break;
     643              :                 }
     644              :         }
     645              :     }
     646              : 
     647        27692 :     PG_RETURN_POINTER(result);
     648              : }
     649              : 
     650              : static int32
     651            0 : sizebitvec(BITVECP sign, int siglen)
     652              : {
     653            0 :     return pg_popcount(sign, siglen);
     654              : }
     655              : 
     656              : static int
     657      4917965 : hemdistsign(BITVECP a, BITVECP b, int siglen)
     658              : {
     659              :     int         i,
     660              :                 diff,
     661      4917965 :                 dist = 0;
     662              : 
     663    369164005 :     LOOPBYTE(siglen)
     664              :     {
     665    364246040 :         diff = (unsigned char) (a[i] ^ b[i]);
     666              :         /* Using the popcount functions here isn't likely to win */
     667    364246040 :         dist += pg_number_of_ones[diff];
     668              :     }
     669      4917965 :     return dist;
     670              : }
     671              : 
     672              : static int
     673            0 : hemdist(TRGM *a, TRGM *b, int siglen)
     674              : {
     675            0 :     if (ISALLTRUE(a))
     676              :     {
     677            0 :         if (ISALLTRUE(b))
     678            0 :             return 0;
     679              :         else
     680            0 :             return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
     681              :     }
     682            0 :     else if (ISALLTRUE(b))
     683            0 :         return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
     684              : 
     685            0 :     return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
     686              : }
     687              : 
     688              : Datum
     689      1152765 : gtrgm_penalty(PG_FUNCTION_ARGS)
     690              : {
     691      1152765 :     GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
     692      1152765 :     GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
     693      1152765 :     float      *penalty = (float *) PG_GETARG_POINTER(2);
     694      1152765 :     int         siglen = GET_SIGLEN();
     695      1152765 :     TRGM       *origval = (TRGM *) DatumGetPointer(origentry->key);
     696      1152765 :     TRGM       *newval = (TRGM *) DatumGetPointer(newentry->key);
     697      1152765 :     BITVECP     orig = GETSIGN(origval);
     698              : 
     699      1152765 :     *penalty = 0.0;
     700              : 
     701      1152765 :     if (ISARRKEY(newval))
     702              :     {
     703      1152765 :         char       *cache = (char *) fcinfo->flinfo->fn_extra;
     704      1152765 :         TRGM       *cachedVal = NULL;
     705      1152765 :         Size        newvalsize = VARSIZE(newval);
     706              :         BITVECP     sign;
     707              : 
     708      1152765 :         if (cache != NULL)
     709      1152759 :             cachedVal = (TRGM *) (cache + MAXALIGN(siglen));
     710              : 
     711              :         /*
     712              :          * Cache the sign data across multiple calls with the same newval.
     713              :          */
     714      2305524 :         if (cache == NULL ||
     715      1152759 :             VARSIZE(cachedVal) != newvalsize ||
     716      1151732 :             memcmp(cachedVal, newval, newvalsize) != 0)
     717              :         {
     718              :             char       *newcache;
     719              : 
     720         3763 :             newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     721         3763 :                                           MAXALIGN(siglen) +
     722              :                                           newvalsize);
     723              : 
     724         3763 :             makesign((BITVECP) newcache, newval, siglen);
     725              : 
     726         3763 :             cachedVal = (TRGM *) (newcache + MAXALIGN(siglen));
     727         3763 :             memcpy(cachedVal, newval, newvalsize);
     728              : 
     729         3763 :             if (cache)
     730         3757 :                 pfree(cache);
     731         3763 :             fcinfo->flinfo->fn_extra = newcache;
     732         3763 :             cache = newcache;
     733              :         }
     734              : 
     735      1152765 :         sign = (BITVECP) cache;
     736              : 
     737      1152765 :         if (ISALLTRUE(origval))
     738            0 :             *penalty = ((float) (SIGLENBIT(siglen) - sizebitvec(sign, siglen))) / (float) (SIGLENBIT(siglen) + 1);
     739              :         else
     740      1152765 :             *penalty = hemdistsign(sign, orig, siglen);
     741              :     }
     742              :     else
     743            0 :         *penalty = hemdist(origval, newval, siglen);
     744      1152765 :     PG_RETURN_POINTER(penalty);
     745              : }
     746              : 
     747              : typedef struct
     748              : {
     749              :     bool        allistrue;
     750              :     BITVECP     sign;
     751              : } CACHESIGN;
     752              : 
     753              : static void
     754        44606 : fillcache(CACHESIGN *item, TRGM *key, BITVECP sign, int siglen)
     755              : {
     756        44606 :     item->allistrue = false;
     757        44606 :     item->sign = sign;
     758        44606 :     if (ISARRKEY(key))
     759        44370 :         makesign(item->sign, key, siglen);
     760          236 :     else if (ISALLTRUE(key))
     761            0 :         item->allistrue = true;
     762              :     else
     763          236 :         memcpy(item->sign, GETSIGN(key), siglen);
     764        44606 : }
     765              : 
     766              : #define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
     767              : typedef struct
     768              : {
     769              :     OffsetNumber pos;
     770              :     int32       cost;
     771              : } SPLITCOST;
     772              : 
     773              : static int
     774        50308 : comparecost(const void *a, const void *b)
     775              : {
     776        50308 :     if (((const SPLITCOST *) a)->cost == ((const SPLITCOST *) b)->cost)
     777        44690 :         return 0;
     778              :     else
     779         5618 :         return (((const SPLITCOST *) a)->cost > ((const SPLITCOST *) b)->cost) ? 1 : -1;
     780              : }
     781              : 
     782              : 
     783              : static int
     784      3677132 : hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
     785              : {
     786      3677132 :     if (a->allistrue)
     787              :     {
     788            0 :         if (b->allistrue)
     789            0 :             return 0;
     790              :         else
     791            0 :             return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen);
     792              :     }
     793      3677132 :     else if (b->allistrue)
     794            0 :         return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
     795              : 
     796      3677132 :     return hemdistsign(a->sign, b->sign, siglen);
     797              : }
     798              : 
     799              : Datum
     800          286 : gtrgm_picksplit(PG_FUNCTION_ARGS)
     801              : {
     802          286 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     803          286 :     OffsetNumber maxoff = entryvec->n - 1;
     804          286 :     GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
     805          286 :     int         siglen = GET_SIGLEN();
     806              :     OffsetNumber k,
     807              :                 j;
     808              :     TRGM       *datum_l,
     809              :                *datum_r;
     810              :     BITVECP     union_l,
     811              :                 union_r;
     812              :     int32       size_alpha,
     813              :                 size_beta;
     814              :     int32       size_waste,
     815          286 :                 waste = -1;
     816              :     int32       nbytes;
     817          286 :     OffsetNumber seed_1 = 0,
     818          286 :                 seed_2 = 0;
     819              :     OffsetNumber *left,
     820              :                *right;
     821              :     BITVECP     ptr;
     822              :     int         i;
     823              :     CACHESIGN  *cache;
     824              :     char       *cache_sign;
     825              :     SPLITCOST  *costvector;
     826              : 
     827              :     /* cache the sign data for each existing item */
     828          286 :     cache = palloc_array(CACHESIGN, maxoff + 1);
     829          286 :     cache_sign = palloc(siglen * (maxoff + 1));
     830              : 
     831        44892 :     for (k = FirstOffsetNumber; k <= maxoff; k = OffsetNumberNext(k))
     832        44606 :         fillcache(&cache[k], GETENTRY(entryvec, k), &cache_sign[siglen * k],
     833              :                   siglen);
     834              : 
     835              :     /* now find the two furthest-apart items */
     836        44606 :     for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
     837              :     {
     838      3632240 :         for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
     839              :         {
     840      3587920 :             size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
     841      3587920 :             if (size_waste > waste)
     842              :             {
     843          495 :                 waste = size_waste;
     844          495 :                 seed_1 = k;
     845          495 :                 seed_2 = j;
     846              :             }
     847              :         }
     848              :     }
     849              : 
     850              :     /* just in case we didn't make a selection ... */
     851          286 :     if (seed_1 == 0 || seed_2 == 0)
     852              :     {
     853            0 :         seed_1 = 1;
     854            0 :         seed_2 = 2;
     855              :     }
     856              : 
     857              :     /* initialize the result vectors */
     858          286 :     nbytes = maxoff * sizeof(OffsetNumber);
     859          286 :     v->spl_left = left = (OffsetNumber *) palloc(nbytes);
     860          286 :     v->spl_right = right = (OffsetNumber *) palloc(nbytes);
     861          286 :     v->spl_nleft = 0;
     862          286 :     v->spl_nright = 0;
     863              : 
     864              :     /* form initial .. */
     865          286 :     datum_l = gtrgm_alloc(cache[seed_1].allistrue, siglen, cache[seed_1].sign);
     866          286 :     datum_r = gtrgm_alloc(cache[seed_2].allistrue, siglen, cache[seed_2].sign);
     867              : 
     868          286 :     union_l = GETSIGN(datum_l);
     869          286 :     union_r = GETSIGN(datum_r);
     870              : 
     871              :     /* sort before ... */
     872          286 :     costvector = palloc_array(SPLITCOST, maxoff);
     873        44892 :     for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
     874              :     {
     875        44606 :         costvector[j - 1].pos = j;
     876        44606 :         size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
     877        44606 :         size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
     878        44606 :         costvector[j - 1].cost = abs(size_alpha - size_beta);
     879              :     }
     880          286 :     qsort(costvector, maxoff, sizeof(SPLITCOST), comparecost);
     881              : 
     882        44892 :     for (k = 0; k < maxoff; k++)
     883              :     {
     884        44606 :         j = costvector[k].pos;
     885        44606 :         if (j == seed_1)
     886              :         {
     887          286 :             *left++ = j;
     888          286 :             v->spl_nleft++;
     889          286 :             continue;
     890              :         }
     891        44320 :         else if (j == seed_2)
     892              :         {
     893          286 :             *right++ = j;
     894          286 :             v->spl_nright++;
     895          286 :             continue;
     896              :         }
     897              : 
     898        44034 :         if (ISALLTRUE(datum_l) || cache[j].allistrue)
     899              :         {
     900            0 :             if (ISALLTRUE(datum_l) && cache[j].allistrue)
     901            0 :                 size_alpha = 0;
     902              :             else
     903            0 :                 size_alpha = SIGLENBIT(siglen) -
     904            0 :                     sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) :
     905            0 :                                GETSIGN(cache[j].sign),
     906              :                                siglen);
     907              :         }
     908              :         else
     909        44034 :             size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
     910              : 
     911        44034 :         if (ISALLTRUE(datum_r) || cache[j].allistrue)
     912              :         {
     913            0 :             if (ISALLTRUE(datum_r) && cache[j].allistrue)
     914            0 :                 size_beta = 0;
     915              :             else
     916            0 :                 size_beta = SIGLENBIT(siglen) -
     917            0 :                     sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) :
     918            0 :                                GETSIGN(cache[j].sign),
     919              :                                siglen);
     920              :         }
     921              :         else
     922        44034 :             size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
     923              : 
     924        44034 :         if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
     925              :         {
     926        21914 :             if (ISALLTRUE(datum_l) || cache[j].allistrue)
     927              :             {
     928            0 :                 if (!ISALLTRUE(datum_l))
     929            0 :                     memset(GETSIGN(datum_l), 0xff, siglen);
     930              :             }
     931              :             else
     932              :             {
     933        21914 :                 ptr = cache[j].sign;
     934      2512166 :                 LOOPBYTE(siglen)
     935      2490252 :                     union_l[i] |= ptr[i];
     936              :             }
     937        21914 :             *left++ = j;
     938        21914 :             v->spl_nleft++;
     939              :         }
     940              :         else
     941              :         {
     942        22120 :             if (ISALLTRUE(datum_r) || cache[j].allistrue)
     943              :             {
     944            0 :                 if (!ISALLTRUE(datum_r))
     945            0 :                     memset(GETSIGN(datum_r), 0xff, siglen);
     946              :             }
     947              :             else
     948              :             {
     949        22120 :                 ptr = cache[j].sign;
     950      2474604 :                 LOOPBYTE(siglen)
     951      2452484 :                     union_r[i] |= ptr[i];
     952              :             }
     953        22120 :             *right++ = j;
     954        22120 :             v->spl_nright++;
     955              :         }
     956              :     }
     957              : 
     958          286 :     v->spl_ldatum = PointerGetDatum(datum_l);
     959          286 :     v->spl_rdatum = PointerGetDatum(datum_r);
     960              : 
     961          286 :     PG_RETURN_POINTER(v);
     962              : }
     963              : 
     964              : Datum
     965           31 : gtrgm_options(PG_FUNCTION_ARGS)
     966              : {
     967           31 :     local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
     968              : 
     969           31 :     init_local_reloptions(relopts, sizeof(TrgmGistOptions));
     970           31 :     add_local_int_reloption(relopts, "siglen",
     971              :                             "signature length in bytes",
     972              :                             SIGLEN_DEFAULT, 1, SIGLEN_MAX,
     973              :                             offsetof(TrgmGistOptions, siglen));
     974              : 
     975           31 :     PG_RETURN_VOID();
     976              : }
        

Generated by: LCOV version 2.0-1