LCOV - code coverage report
Current view: top level - contrib/pg_trgm - trgm_gist.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 344 431 79.8 %
Date: 2026-02-09 18:18:03 Functions: 28 32 87.5 %
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           2 : PG_FUNCTION_INFO_V1(gtrgm_in);
      44           2 : PG_FUNCTION_INFO_V1(gtrgm_out);
      45          10 : PG_FUNCTION_INFO_V1(gtrgm_compress);
      46          10 : PG_FUNCTION_INFO_V1(gtrgm_decompress);
      47          10 : PG_FUNCTION_INFO_V1(gtrgm_consistent);
      48          10 : PG_FUNCTION_INFO_V1(gtrgm_distance);
      49          10 : PG_FUNCTION_INFO_V1(gtrgm_union);
      50          10 : PG_FUNCTION_INFO_V1(gtrgm_same);
      51          10 : PG_FUNCTION_INFO_V1(gtrgm_penalty);
      52          10 : PG_FUNCTION_INFO_V1(gtrgm_picksplit);
      53          10 : 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       56288 : gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign)
      78             : {
      79       56288 :     int         flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0);
      80       56288 :     int         size = CALCGTSIZE(flag, siglen);
      81       56288 :     TRGM       *res = palloc(size);
      82             : 
      83       56288 :     SET_VARSIZE(res, size);
      84       56288 :     res->flag = flag;
      85             : 
      86       56288 :     if (!isalltrue)
      87             :     {
      88       56288 :         if (sign)
      89        1112 :             memcpy(GETSIGN(res), sign, siglen);
      90             :         else
      91       55176 :             memset(GETSIGN(res), 0, siglen);
      92             :     }
      93             : 
      94       56288 :     return res;
      95             : }
      96             : 
      97             : static void
      98       95182 : makesign(BITVECP sign, TRGM *a, int siglen)
      99             : {
     100             :     int32       k,
     101       95182 :                 len = ARRNELEM(a);
     102       95182 :     trgm       *ptr = GETARR(a);
     103       95182 :     int32       tmp = 0;
     104             : 
     105       95182 :     MemSet(sign, 0, siglen);
     106       95182 :     SETBIT(sign, SIGLENBIT(siglen));    /* set last unused bit */
     107      936372 :     for (k = 0; k < len; k++)
     108             :     {
     109      841190 :         CPTRGM(&tmp, ptr + k);
     110      841190 :         HASH(sign, tmp, siglen);
     111             :     }
     112       95182 : }
     113             : 
     114             : Datum
     115       55736 : gtrgm_compress(PG_FUNCTION_ARGS)
     116             : {
     117       55736 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     118       55736 :     int         siglen = GET_SIGLEN();
     119       55736 :     GISTENTRY  *retval = entry;
     120             : 
     121       55736 :     if (entry->leafkey)
     122             :     {                           /* trgm */
     123             :         TRGM       *res;
     124       48904 :         text       *val = DatumGetTextPP(entry->key);
     125             : 
     126       48904 :         res = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val));
     127       48904 :         retval = palloc_object(GISTENTRY);
     128       48904 :         gistentryinit(*retval, PointerGetDatum(res),
     129             :                       entry->rel, entry->page,
     130             :                       entry->offset, false);
     131             :     }
     132        6832 :     else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
     133        6832 :              !ISALLTRUE(DatumGetPointer(entry->key)))
     134             :     {
     135             :         int32       i;
     136             :         TRGM       *res;
     137        6832 :         BITVECP     sign = GETSIGN(DatumGetPointer(entry->key));
     138             : 
     139        7142 :         LOOPBYTE(siglen)
     140             :         {
     141        7142 :             if ((sign[i] & 0xff) != 0xff)
     142        6832 :                 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       48904 :     PG_RETURN_POINTER(retval);
     152             : }
     153             : 
     154             : Datum
     155     2727968 : gtrgm_decompress(PG_FUNCTION_ARGS)
     156             : {
     157     2727968 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     158             :     GISTENTRY  *retval;
     159             :     text       *key;
     160             : 
     161     2727968 :     key = DatumGetTextPP(entry->key);
     162             : 
     163     2727968 :     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     2727968 :         PG_RETURN_POINTER(entry);
     175             :     }
     176             : }
     177             : 
     178             : static int32
     179        1290 : cnt_sml_sign_common(TRGM *qtrg, BITVECP sign, int siglen)
     180             : {
     181        1290 :     int32       count = 0;
     182             :     int32       k,
     183        1290 :                 len = ARRNELEM(qtrg);
     184        1290 :     trgm       *ptr = GETARR(qtrg);
     185        1290 :     int32       tmp = 0;
     186             : 
     187       11818 :     for (k = 0; k < len; k++)
     188             :     {
     189       10528 :         CPTRGM(&tmp, ptr + k);
     190       10528 :         count += GETBIT(sign, HASHVAL(tmp, siglen));
     191             :     }
     192             : 
     193        1290 :     return count;
     194             : }
     195             : 
     196             : Datum
     197       75332 : gtrgm_consistent(PG_FUNCTION_ARGS)
     198             : {
     199       75332 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     200       75332 :     text       *query = PG_GETARG_TEXT_P(1);
     201       75332 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     202             : #ifdef NOT_USED
     203             :     Oid         subtype = PG_GETARG_OID(3);
     204             : #endif
     205       75332 :     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     206       75332 :     int         siglen = GET_SIGLEN();
     207       75332 :     TRGM       *key = (TRGM *) DatumGetPointer(entry->key);
     208             :     TRGM       *qtrg;
     209             :     bool        res;
     210       75332 :     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       75332 :     cache = (gtrgm_consistent_cache *) fcinfo->flinfo->fn_extra;
     229       75332 :     if (cache == NULL ||
     230      150440 :         cache->strategy != strategy ||
     231       75220 :         VARSIZE(cache->query) != querysize ||
     232       75220 :         memcmp(cache->query, query, querysize) != 0)
     233             :     {
     234             :         gtrgm_consistent_cache *newcache;
     235         112 :         TrgmPackedGraph *graph = NULL;
     236             :         Size        qtrgsize;
     237             : 
     238         112 :         switch (strategy)
     239             :         {
     240          56 :             case SimilarityStrategyNumber:
     241             :             case WordSimilarityStrategyNumber:
     242             :             case StrictWordSimilarityStrategyNumber:
     243             :             case EqualStrategyNumber:
     244          56 :                 qtrg = generate_trgm(VARDATA(query),
     245          56 :                                      querysize - VARHDRSZ);
     246          56 :                 break;
     247          14 :             case ILikeStrategyNumber:
     248             : #ifndef IGNORECASE
     249             :                 elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
     250             : #endif
     251             :                 /* FALL THRU */
     252             :             case LikeStrategyNumber:
     253          14 :                 qtrg = generate_wildcard_trgm(VARDATA(query),
     254          14 :                                               querysize - VARHDRSZ);
     255          14 :                 break;
     256          42 :             case RegExpICaseStrategyNumber:
     257             : #ifndef IGNORECASE
     258             :                 elog(ERROR, "cannot handle ~* with case-sensitive trigrams");
     259             : #endif
     260             :                 /* FALL THRU */
     261             :             case RegExpStrategyNumber:
     262          42 :                 qtrg = createTrgmNFA(query, PG_GET_COLLATION(),
     263          42 :                                      &graph, fcinfo->flinfo->fn_mcxt);
     264             :                 /* just in case an empty array is returned ... */
     265          42 :                 if (qtrg && ARRNELEM(qtrg) <= 0)
     266             :                 {
     267           0 :                     pfree(qtrg);
     268           0 :                     qtrg = NULL;
     269             :                 }
     270          42 :                 break;
     271           0 :             default:
     272           0 :                 elog(ERROR, "unrecognized strategy number: %d", strategy);
     273             :                 qtrg = NULL;    /* keep compiler quiet */
     274             :                 break;
     275             :         }
     276             : 
     277         112 :         qtrgsize = qtrg ? VARSIZE(qtrg) : 0;
     278             : 
     279             :         newcache = (gtrgm_consistent_cache *)
     280         112 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     281             :                                MAXALIGN(sizeof(gtrgm_consistent_cache)) +
     282         112 :                                MAXALIGN(querysize) +
     283             :                                qtrgsize);
     284             : 
     285         112 :         newcache->strategy = strategy;
     286         112 :         newcache->query = (text *)
     287             :             ((char *) newcache + MAXALIGN(sizeof(gtrgm_consistent_cache)));
     288         112 :         memcpy(newcache->query, query, querysize);
     289         112 :         if (qtrg)
     290             :         {
     291         104 :             newcache->trigrams = (TRGM *)
     292         104 :                 ((char *) newcache->query + MAXALIGN(querysize));
     293         104 :             memcpy((char *) newcache->trigrams, qtrg, qtrgsize);
     294             :             /* release qtrg in case it was made in fn_mcxt */
     295         104 :             pfree(qtrg);
     296             :         }
     297             :         else
     298           8 :             newcache->trigrams = NULL;
     299         112 :         newcache->graph = graph;
     300             : 
     301         112 :         if (cache)
     302           0 :             pfree(cache);
     303         112 :         fcinfo->flinfo->fn_extra = newcache;
     304         112 :         cache = newcache;
     305             :     }
     306             : 
     307       75332 :     qtrg = cache->trigrams;
     308             : 
     309       75332 :     switch (strategy)
     310             :     {
     311       70528 :         case SimilarityStrategyNumber:
     312             :         case WordSimilarityStrategyNumber:
     313             :         case StrictWordSimilarityStrategyNumber:
     314             : 
     315             :             /*
     316             :              * Similarity search is exact. (Strict) word similarity search is
     317             :              * inexact
     318             :              */
     319       70528 :             *recheck = (strategy != SimilarityStrategyNumber);
     320             : 
     321       70528 :             nlimit = index_strategy_get_limit(strategy);
     322             : 
     323       70528 :             if (GIST_LEAF(entry))
     324             :             {                   /* all leafs contains orig trgm */
     325       69316 :                 double      tmpsml = cnt_sml(qtrg, key, *recheck);
     326             : 
     327       69316 :                 res = (tmpsml >= nlimit);
     328             :             }
     329        1212 :             else if (ISALLTRUE(key))
     330             :             {                   /* non-leaf contains signature */
     331           0 :                 res = true;
     332             :             }
     333             :             else
     334             :             {                   /* non-leaf contains signature */
     335        1212 :                 int32       count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
     336        1212 :                 int32       len = ARRNELEM(qtrg);
     337             : 
     338        1212 :                 if (len == 0)
     339           0 :                     res = false;
     340             :                 else
     341        1212 :                     res = (((((float8) count) / ((float8) len))) >= nlimit);
     342             :             }
     343       70528 :             break;
     344         380 :         case ILikeStrategyNumber:
     345             : #ifndef IGNORECASE
     346             :             elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
     347             : #endif
     348             :             /* FALL THRU */
     349             :         case LikeStrategyNumber:
     350             :         case EqualStrategyNumber:
     351             :             /* Wildcard and equal search are inexact */
     352         380 :             *recheck = true;
     353             : 
     354             :             /*
     355             :              * Check if all the extracted trigrams can be present in child
     356             :              * nodes.
     357             :              */
     358         380 :             if (GIST_LEAF(entry))
     359             :             {                   /* all leafs contains orig trgm */
     360         380 :                 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         380 :             break;
     386        4424 :         case RegExpICaseStrategyNumber:
     387             : #ifndef IGNORECASE
     388             :             elog(ERROR, "cannot handle ~* with case-sensitive trigrams");
     389             : #endif
     390             :             /* FALL THRU */
     391             :         case RegExpStrategyNumber:
     392             :             /* Regexp search is inexact */
     393        4424 :             *recheck = true;
     394             : 
     395             :             /* Check regex match as much as we can with available info */
     396        4424 :             if (qtrg)
     397             :             {
     398        4344 :                 if (GIST_LEAF(entry))
     399             :                 {               /* all leafs contains orig trgm */
     400             :                     bool       *check;
     401             : 
     402        4300 :                     check = trgm_presence_map(qtrg, key);
     403        4300 :                     res = trigramsMatchGraph(cache->graph, check);
     404        4300 :                     pfree(check);
     405             :                 }
     406          44 :                 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          44 :                                 tmp = 0,
     414          44 :                                 len = ARRNELEM(qtrg);
     415          44 :                     trgm       *ptr = GETARR(qtrg);
     416          44 :                     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          44 :                     check = (bool *) palloc(len * sizeof(bool));
     428       11132 :                     for (k = 0; k < len; k++)
     429             :                     {
     430       11088 :                         CPTRGM(&tmp, ptr + k);
     431       11088 :                         check[k] = GETBIT(sign, HASHVAL(tmp, siglen));
     432             :                     }
     433          44 :                     res = trigramsMatchGraph(cache->graph, check);
     434          44 :                     pfree(check);
     435             :                 }
     436             :             }
     437             :             else
     438             :             {
     439             :                 /* trigram-free query must be rechecked everywhere */
     440          80 :                 res = true;
     441             :             }
     442        4424 :             break;
     443           0 :         default:
     444           0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
     445             :             res = false;        /* keep compiler quiet */
     446             :             break;
     447             :     }
     448             : 
     449       75332 :     PG_RETURN_BOOL(res);
     450             : }
     451             : 
     452             : Datum
     453        6618 : gtrgm_distance(PG_FUNCTION_ARGS)
     454             : {
     455        6618 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     456        6618 :     text       *query = PG_GETARG_TEXT_P(1);
     457        6618 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     458             : #ifdef NOT_USED
     459             :     Oid         subtype = PG_GETARG_OID(3);
     460             : #endif
     461        6618 :     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     462        6618 :     int         siglen = GET_SIGLEN();
     463        6618 :     TRGM       *key = (TRGM *) DatumGetPointer(entry->key);
     464             :     TRGM       *qtrg;
     465             :     float8      res;
     466        6618 :     Size        querysize = VARSIZE(query);
     467        6618 :     char       *cache = (char *) fcinfo->flinfo->fn_extra;
     468             : 
     469             :     /*
     470             :      * Cache the generated trigrams across multiple calls with the same query.
     471             :      */
     472       13228 :     if (cache == NULL ||
     473        6610 :         VARSIZE(cache) != querysize ||
     474        6610 :         memcmp(cache, query, querysize) != 0)
     475             :     {
     476             :         char       *newcache;
     477             : 
     478           8 :         qtrg = generate_trgm(VARDATA(query), querysize - VARHDRSZ);
     479             : 
     480           8 :         newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     481           8 :                                       MAXALIGN(querysize) +
     482           8 :                                       VARSIZE(qtrg));
     483             : 
     484           8 :         memcpy(newcache, query, querysize);
     485           8 :         memcpy(newcache + MAXALIGN(querysize), qtrg, VARSIZE(qtrg));
     486             : 
     487           8 :         if (cache)
     488           0 :             pfree(cache);
     489           8 :         fcinfo->flinfo->fn_extra = newcache;
     490           8 :         cache = newcache;
     491             :     }
     492             : 
     493        6618 :     qtrg = (TRGM *) (cache + MAXALIGN(querysize));
     494             : 
     495        6618 :     switch (strategy)
     496             :     {
     497        6618 :         case DistanceStrategyNumber:
     498             :         case WordDistanceStrategyNumber:
     499             :         case StrictWordDistanceStrategyNumber:
     500             :             /* Only plain trigram distance is exact */
     501        6618 :             *recheck = (strategy != DistanceStrategyNumber);
     502        6618 :             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        6540 :                 float4 volatile sml = cnt_sml(qtrg, key, *recheck);
     511             : 
     512        6540 :                 res = 1.0 - sml;
     513             :             }
     514          78 :             else if (ISALLTRUE(key))
     515             :             {                   /* all leafs contains orig trgm */
     516           0 :                 res = 0.0;
     517             :             }
     518             :             else
     519             :             {                   /* non-leaf contains signature */
     520          78 :                 int32       count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
     521          78 :                 int32       len = ARRNELEM(qtrg);
     522             : 
     523          78 :                 res = (len == 0) ? -1.0 : 1.0 - ((float8) count) / ((float8) len);
     524             :             }
     525        6618 :             break;
     526           0 :         default:
     527           0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
     528             :             res = 0;            /* keep compiler quiet */
     529             :             break;
     530             :     }
     531             : 
     532        6618 :     PG_RETURN_FLOAT8(res);
     533             : }
     534             : 
     535             : static int32
     536      110352 : unionkey(BITVECP sbase, TRGM *add, int siglen)
     537             : {
     538             :     int32       i;
     539             : 
     540      110352 :     if (ISSIGNKEY(add))
     541             :     {
     542       55176 :         BITVECP     sadd = GETSIGN(add);
     543             : 
     544       55176 :         if (ISALLTRUE(add))
     545           0 :             return 1;
     546             : 
     547    13006584 :         LOOPBYTE(siglen)
     548    12951408 :             sbase[i] |= sadd[i];
     549             :     }
     550             :     else
     551             :     {
     552       55176 :         trgm       *ptr = GETARR(add);
     553       55176 :         int32       tmp = 0;
     554             : 
     555      551778 :         for (i = 0; i < ARRNELEM(add); i++)
     556             :         {
     557      496602 :             CPTRGM(&tmp, ptr + i);
     558      496602 :             HASH(sbase, tmp, siglen);
     559             :         }
     560             :     }
     561      110352 :     return 0;
     562             : }
     563             : 
     564             : 
     565             : Datum
     566       55176 : gtrgm_union(PG_FUNCTION_ARGS)
     567             : {
     568       55176 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     569       55176 :     int32       len = entryvec->n;
     570       55176 :     int        *size = (int *) PG_GETARG_POINTER(1);
     571       55176 :     int         siglen = GET_SIGLEN();
     572             :     int32       i;
     573       55176 :     TRGM       *result = gtrgm_alloc(false, siglen, NULL);
     574       55176 :     BITVECP     base = GETSIGN(result);
     575             : 
     576      165528 :     for (i = 0; i < len; i++)
     577             :     {
     578      110352 :         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       55176 :     *size = VARSIZE(result);
     587             : 
     588       55176 :     PG_RETURN_POINTER(result);
     589             : }
     590             : 
     591             : Datum
     592       55176 : gtrgm_same(PG_FUNCTION_ARGS)
     593             : {
     594       55176 :     TRGM       *a = (TRGM *) PG_GETARG_POINTER(0);
     595       55176 :     TRGM       *b = (TRGM *) PG_GETARG_POINTER(1);
     596       55176 :     bool       *result = (bool *) PG_GETARG_POINTER(2);
     597       55176 :     int         siglen = GET_SIGLEN();
     598             : 
     599       55176 :     if (ISSIGNKEY(a))
     600             :     {                           /* then b also ISSIGNKEY */
     601       55176 :         if (ISALLTRUE(a) && ISALLTRUE(b))
     602           0 :             *result = true;
     603       55176 :         else if (ISALLTRUE(a))
     604           0 :             *result = false;
     605       55176 :         else if (ISALLTRUE(b))
     606           0 :             *result = false;
     607             :         else
     608             :         {
     609             :             int32       i;
     610       55176 :             BITVECP     sa = GETSIGN(a),
     611       55176 :                         sb = GETSIGN(b);
     612             : 
     613       55176 :             *result = true;
     614     6159112 :             LOOPBYTE(siglen)
     615             :             {
     616     6109656 :                 if (sa[i] != sb[i])
     617             :                 {
     618        5720 :                     *result = false;
     619        5720 :                     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       55176 :     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     9843824 : hemdistsign(BITVECP a, BITVECP b, int siglen)
     658             : {
     659             :     int         i,
     660             :                 diff,
     661     9843824 :                 dist = 0;
     662             : 
     663   673917864 :     LOOPBYTE(siglen)
     664             :     {
     665   664074040 :         diff = (unsigned char) (a[i] ^ b[i]);
     666             :         /* Using the popcount functions here isn't likely to win */
     667   664074040 :         dist += pg_number_of_ones[diff];
     668             :     }
     669     9843824 :     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     2392394 : gtrgm_penalty(PG_FUNCTION_ARGS)
     690             : {
     691     2392394 :     GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
     692     2392394 :     GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
     693     2392394 :     float      *penalty = (float *) PG_GETARG_POINTER(2);
     694     2392394 :     int         siglen = GET_SIGLEN();
     695     2392394 :     TRGM       *origval = (TRGM *) DatumGetPointer(origentry->key);
     696     2392394 :     TRGM       *newval = (TRGM *) DatumGetPointer(newentry->key);
     697     2392394 :     BITVECP     orig = GETSIGN(origval);
     698             : 
     699     2392394 :     *penalty = 0.0;
     700             : 
     701     2392394 :     if (ISARRKEY(newval))
     702             :     {
     703     2392394 :         char       *cache = (char *) fcinfo->flinfo->fn_extra;
     704     2392394 :         TRGM       *cachedVal = NULL;
     705     2392394 :         Size        newvalsize = VARSIZE(newval);
     706             :         BITVECP     sign;
     707             : 
     708     2392394 :         if (cache != NULL)
     709     2392382 :             cachedVal = (TRGM *) (cache + MAXALIGN(siglen));
     710             : 
     711             :         /*
     712             :          * Cache the sign data across multiple calls with the same newval.
     713             :          */
     714     4784776 :         if (cache == NULL ||
     715     2392382 :             VARSIZE(cachedVal) != newvalsize ||
     716     2390328 :             memcmp(cachedVal, newval, newvalsize) != 0)
     717             :         {
     718             :             char       *newcache;
     719             : 
     720        7526 :             newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     721        7526 :                                           MAXALIGN(siglen) +
     722             :                                           newvalsize);
     723             : 
     724        7526 :             makesign((BITVECP) newcache, newval, siglen);
     725             : 
     726        7526 :             cachedVal = (TRGM *) (newcache + MAXALIGN(siglen));
     727        7526 :             memcpy(cachedVal, newval, newvalsize);
     728             : 
     729        7526 :             if (cache)
     730        7514 :                 pfree(cache);
     731        7526 :             fcinfo->flinfo->fn_extra = newcache;
     732        7526 :             cache = newcache;
     733             :         }
     734             : 
     735     2392394 :         sign = (BITVECP) cache;
     736             : 
     737     2392394 :         if (ISALLTRUE(origval))
     738           0 :             *penalty = ((float) (SIGLENBIT(siglen) - sizebitvec(sign, siglen))) / (float) (SIGLENBIT(siglen) + 1);
     739             :         else
     740     2392394 :             *penalty = hemdistsign(sign, orig, siglen);
     741             :     }
     742             :     else
     743           0 :         *penalty = hemdist(origval, newval, siglen);
     744     2392394 :     PG_RETURN_POINTER(penalty);
     745             : }
     746             : 
     747             : typedef struct
     748             : {
     749             :     bool        allistrue;
     750             :     BITVECP     sign;
     751             : } CACHESIGN;
     752             : 
     753             : static void
     754       88096 : fillcache(CACHESIGN *item, TRGM *key, BITVECP sign, int siglen)
     755             : {
     756       88096 :     item->allistrue = false;
     757       88096 :     item->sign = sign;
     758       88096 :     if (ISARRKEY(key))
     759       87656 :         makesign(item->sign, key, siglen);
     760         440 :     else if (ISALLTRUE(key))
     761           0 :         item->allistrue = true;
     762             :     else
     763         440 :         memcpy(item->sign, GETSIGN(key), siglen);
     764       88096 : }
     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       98806 : comparecost(const void *a, const void *b)
     775             : {
     776       98806 :     if (((const SPLITCOST *) a)->cost == ((const SPLITCOST *) b)->cost)
     777       88410 :         return 0;
     778             :     else
     779       10396 :         return (((const SPLITCOST *) a)->cost > ((const SPLITCOST *) b)->cost) ? 1 : -1;
     780             : }
     781             : 
     782             : 
     783             : static int
     784     7277462 : hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
     785             : {
     786     7277462 :     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     7277462 :     else if (b->allistrue)
     794           0 :         return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
     795             : 
     796     7277462 :     return hemdistsign(a->sign, b->sign, siglen);
     797             : }
     798             : 
     799             : Datum
     800         556 : gtrgm_picksplit(PG_FUNCTION_ARGS)
     801             : {
     802         556 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     803         556 :     OffsetNumber maxoff = entryvec->n - 1;
     804         556 :     GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
     805         556 :     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         556 :                 waste = -1;
     816             :     int32       nbytes;
     817         556 :     OffsetNumber seed_1 = 0,
     818         556 :                 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         556 :     cache = palloc_array(CACHESIGN, maxoff + 1);
     829         556 :     cache_sign = palloc(siglen * (maxoff + 1));
     830             : 
     831       88652 :     for (k = FirstOffsetNumber; k <= maxoff; k = OffsetNumberNext(k))
     832       88096 :         fillcache(&cache[k], GETENTRY(entryvec, k), &cache_sign[siglen * k],
     833             :                   siglen);
     834             : 
     835             :     /* now find the two furthest-apart items */
     836       88096 :     for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
     837             :     {
     838     7188810 :         for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
     839             :         {
     840     7101270 :             size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
     841     7101270 :             if (size_waste > waste)
     842             :             {
     843         972 :                 waste = size_waste;
     844         972 :                 seed_1 = k;
     845         972 :                 seed_2 = j;
     846             :             }
     847             :         }
     848             :     }
     849             : 
     850             :     /* just in case we didn't make a selection ... */
     851         556 :     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         556 :     nbytes = maxoff * sizeof(OffsetNumber);
     859         556 :     v->spl_left = left = (OffsetNumber *) palloc(nbytes);
     860         556 :     v->spl_right = right = (OffsetNumber *) palloc(nbytes);
     861         556 :     v->spl_nleft = 0;
     862         556 :     v->spl_nright = 0;
     863             : 
     864             :     /* form initial .. */
     865         556 :     datum_l = gtrgm_alloc(cache[seed_1].allistrue, siglen, cache[seed_1].sign);
     866         556 :     datum_r = gtrgm_alloc(cache[seed_2].allistrue, siglen, cache[seed_2].sign);
     867             : 
     868         556 :     union_l = GETSIGN(datum_l);
     869         556 :     union_r = GETSIGN(datum_r);
     870             : 
     871             :     /* sort before ... */
     872         556 :     costvector = palloc_array(SPLITCOST, maxoff);
     873       88652 :     for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
     874             :     {
     875       88096 :         costvector[j - 1].pos = j;
     876       88096 :         size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
     877       88096 :         size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
     878       88096 :         costvector[j - 1].cost = abs(size_alpha - size_beta);
     879             :     }
     880         556 :     qsort(costvector, maxoff, sizeof(SPLITCOST), comparecost);
     881             : 
     882       88652 :     for (k = 0; k < maxoff; k++)
     883             :     {
     884       88096 :         j = costvector[k].pos;
     885       88096 :         if (j == seed_1)
     886             :         {
     887         556 :             *left++ = j;
     888         556 :             v->spl_nleft++;
     889         556 :             continue;
     890             :         }
     891       87540 :         else if (j == seed_2)
     892             :         {
     893         556 :             *right++ = j;
     894         556 :             v->spl_nright++;
     895         556 :             continue;
     896             :         }
     897             : 
     898       86984 :         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       86984 :             size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
     910             : 
     911       86984 :         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       86984 :             size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
     923             : 
     924       86984 :         if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
     925             :         {
     926       43266 :             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       43266 :                 ptr = cache[j].sign;
     934     4514026 :                 LOOPBYTE(siglen)
     935     4470760 :                     union_l[i] |= ptr[i];
     936             :             }
     937       43266 :             *left++ = j;
     938       43266 :             v->spl_nleft++;
     939             :         }
     940             :         else
     941             :         {
     942       43718 :             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       43718 :                 ptr = cache[j].sign;
     950     4439422 :                 LOOPBYTE(siglen)
     951     4395704 :                     union_r[i] |= ptr[i];
     952             :             }
     953       43718 :             *right++ = j;
     954       43718 :             v->spl_nright++;
     955             :         }
     956             :     }
     957             : 
     958         556 :     v->spl_ldatum = PointerGetDatum(datum_l);
     959         556 :     v->spl_rdatum = PointerGetDatum(datum_r);
     960             : 
     961         556 :     PG_RETURN_POINTER(v);
     962             : }
     963             : 
     964             : Datum
     965          62 : gtrgm_options(PG_FUNCTION_ARGS)
     966             : {
     967          62 :     local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
     968             : 
     969          62 :     init_local_reloptions(relopts, sizeof(TrgmGistOptions));
     970          62 :     add_local_int_reloption(relopts, "siglen",
     971             :                             "signature length in bytes",
     972             :                             SIGLEN_DEFAULT, 1, SIGLEN_MAX,
     973             :                             offsetof(TrgmGistOptions, siglen));
     974             : 
     975          62 :     PG_RETURN_VOID();
     976             : }

Generated by: LCOV version 1.16