LCOV - code coverage report
Current view: top level - contrib/pg_trgm - trgm_gist.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 342 429 79.7 %
Date: 2026-02-01 09:17:49 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           8 : PG_FUNCTION_INFO_V1(gtrgm_compress);
      46           8 : PG_FUNCTION_INFO_V1(gtrgm_decompress);
      47           8 : PG_FUNCTION_INFO_V1(gtrgm_consistent);
      48           8 : PG_FUNCTION_INFO_V1(gtrgm_distance);
      49           8 : PG_FUNCTION_INFO_V1(gtrgm_union);
      50           8 : PG_FUNCTION_INFO_V1(gtrgm_same);
      51           8 : PG_FUNCTION_INFO_V1(gtrgm_penalty);
      52           8 : PG_FUNCTION_INFO_V1(gtrgm_picksplit);
      53           8 : 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       56274 : gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign)
      78             : {
      79       56274 :     int         flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0);
      80       56274 :     int         size = CALCGTSIZE(flag, siglen);
      81       56274 :     TRGM       *res = palloc(size);
      82             : 
      83       56274 :     SET_VARSIZE(res, size);
      84       56274 :     res->flag = flag;
      85             : 
      86       56274 :     if (!isalltrue)
      87             :     {
      88       56274 :         if (sign)
      89        1124 :             memcpy(GETSIGN(res), sign, siglen);
      90             :         else
      91       55150 :             memset(GETSIGN(res), 0, siglen);
      92             :     }
      93             : 
      94       56274 :     return res;
      95             : }
      96             : 
      97             : static void
      98       95454 : makesign(BITVECP sign, TRGM *a, int siglen)
      99             : {
     100             :     int32       k,
     101       95454 :                 len = ARRNELEM(a);
     102       95454 :     trgm       *ptr = GETARR(a);
     103       95454 :     int32       tmp = 0;
     104             : 
     105       95454 :     MemSet(sign, 0, siglen);
     106       95454 :     SETBIT(sign, SIGLENBIT(siglen));    /* set last unused bit */
     107      940480 :     for (k = 0; k < len; k++)
     108             :     {
     109      845026 :         CPTRGM(&tmp, ptr + k);
     110      845026 :         HASH(sign, tmp, siglen);
     111             :     }
     112       95454 : }
     113             : 
     114             : Datum
     115       55562 : gtrgm_compress(PG_FUNCTION_ARGS)
     116             : {
     117       55562 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     118       55562 :     int         siglen = GET_SIGLEN();
     119       55562 :     GISTENTRY  *retval = entry;
     120             : 
     121       55562 :     if (entry->leafkey)
     122             :     {                           /* trgm */
     123             :         TRGM       *res;
     124       48804 :         text       *val = DatumGetTextPP(entry->key);
     125             : 
     126       48804 :         res = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val));
     127       48804 :         retval = palloc_object(GISTENTRY);
     128       48804 :         gistentryinit(*retval, PointerGetDatum(res),
     129             :                       entry->rel, entry->page,
     130             :                       entry->offset, false);
     131             :     }
     132        6758 :     else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
     133        6758 :              !ISALLTRUE(DatumGetPointer(entry->key)))
     134             :     {
     135             :         int32       i;
     136             :         TRGM       *res;
     137        6758 :         BITVECP     sign = GETSIGN(DatumGetPointer(entry->key));
     138             : 
     139        7246 :         LOOPBYTE(siglen)
     140             :         {
     141        7246 :             if ((sign[i] & 0xff) != 0xff)
     142        6758 :                 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       48804 :     PG_RETURN_POINTER(retval);
     152             : }
     153             : 
     154             : Datum
     155     2706994 : gtrgm_decompress(PG_FUNCTION_ARGS)
     156             : {
     157     2706994 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     158             :     GISTENTRY  *retval;
     159             :     text       *key;
     160             : 
     161     2706994 :     key = DatumGetTextPP(entry->key);
     162             : 
     163     2706994 :     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     2706994 :         PG_RETURN_POINTER(entry);
     175             :     }
     176             : }
     177             : 
     178             : static int32
     179        1324 : cnt_sml_sign_common(TRGM *qtrg, BITVECP sign, int siglen)
     180             : {
     181        1324 :     int32       count = 0;
     182             :     int32       k,
     183        1324 :                 len = ARRNELEM(qtrg);
     184        1324 :     trgm       *ptr = GETARR(qtrg);
     185        1324 :     int32       tmp = 0;
     186             : 
     187       12246 :     for (k = 0; k < len; k++)
     188             :     {
     189       10922 :         CPTRGM(&tmp, ptr + k);
     190       10922 :         count += GETBIT(sign, HASHVAL(tmp, siglen));
     191             :     }
     192             : 
     193        1324 :     return count;
     194             : }
     195             : 
     196             : Datum
     197       74760 : gtrgm_consistent(PG_FUNCTION_ARGS)
     198             : {
     199       74760 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     200       74760 :     text       *query = PG_GETARG_TEXT_P(1);
     201       74760 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     202             : #ifdef NOT_USED
     203             :     Oid         subtype = PG_GETARG_OID(3);
     204             : #endif
     205       74760 :     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     206       74760 :     int         siglen = GET_SIGLEN();
     207       74760 :     TRGM       *key = (TRGM *) DatumGetPointer(entry->key);
     208             :     TRGM       *qtrg;
     209             :     bool        res;
     210       74760 :     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       74760 :     cache = (gtrgm_consistent_cache *) fcinfo->flinfo->fn_extra;
     229       74760 :     if (cache == NULL ||
     230      149296 :         cache->strategy != strategy ||
     231       74648 :         VARSIZE(cache->query) != querysize ||
     232       74648 :         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       74760 :     qtrg = cache->trigrams;
     308             : 
     309       74760 :     switch (strategy)
     310             :     {
     311       69950 :         case SimilarityStrategyNumber:
     312             :         case WordSimilarityStrategyNumber:
     313             :         case StrictWordSimilarityStrategyNumber:
     314             : 
     315             :             /*
     316             :              * Similarity search is exact. (Strict) word similarity search is
     317             :              * inexact
     318             :              */
     319       69950 :             *recheck = (strategy != SimilarityStrategyNumber);
     320             : 
     321       69950 :             nlimit = index_strategy_get_limit(strategy);
     322             : 
     323       69950 :             if (GIST_LEAF(entry))
     324             :             {                   /* all leafs contains orig trgm */
     325       68712 :                 double      tmpsml = cnt_sml(qtrg, key, *recheck);
     326             : 
     327       68712 :                 res = (tmpsml >= nlimit);
     328             :             }
     329        1238 :             else if (ISALLTRUE(key))
     330             :             {                   /* non-leaf contains signature */
     331           0 :                 res = true;
     332             :             }
     333             :             else
     334             :             {                   /* non-leaf contains signature */
     335        1238 :                 int32       count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
     336        1238 :                 int32       len = ARRNELEM(qtrg);
     337             : 
     338        1238 :                 if (len == 0)
     339           0 :                     res = false;
     340             :                 else
     341        1238 :                     res = (((((float8) count) / ((float8) len))) >= nlimit);
     342             :             }
     343       69950 :             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        4430 :         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        4430 :             *recheck = true;
     394             : 
     395             :             /* Check regex match as much as we can with available info */
     396        4430 :             if (qtrg)
     397             :             {
     398        4350 :                 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          50 :                 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          50 :                                 tmp = 0,
     414          50 :                                 len = ARRNELEM(qtrg);
     415          50 :                     trgm       *ptr = GETARR(qtrg);
     416          50 :                     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          50 :                     check = (bool *) palloc(len * sizeof(bool));
     428       12650 :                     for (k = 0; k < len; k++)
     429             :                     {
     430       12600 :                         CPTRGM(&tmp, ptr + k);
     431       12600 :                         check[k] = GETBIT(sign, HASHVAL(tmp, siglen));
     432             :                     }
     433          50 :                     res = trigramsMatchGraph(cache->graph, check);
     434          50 :                     pfree(check);
     435             :                 }
     436             :             }
     437             :             else
     438             :             {
     439             :                 /* trigram-free query must be rechecked everywhere */
     440          80 :                 res = true;
     441             :             }
     442        4430 :             break;
     443           0 :         default:
     444           0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
     445             :             res = false;        /* keep compiler quiet */
     446             :             break;
     447             :     }
     448             : 
     449       74760 :     PG_RETURN_BOOL(res);
     450             : }
     451             : 
     452             : Datum
     453        5962 : gtrgm_distance(PG_FUNCTION_ARGS)
     454             : {
     455        5962 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     456        5962 :     text       *query = PG_GETARG_TEXT_P(1);
     457        5962 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     458             : #ifdef NOT_USED
     459             :     Oid         subtype = PG_GETARG_OID(3);
     460             : #endif
     461        5962 :     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     462        5962 :     int         siglen = GET_SIGLEN();
     463        5962 :     TRGM       *key = (TRGM *) DatumGetPointer(entry->key);
     464             :     TRGM       *qtrg;
     465             :     float8      res;
     466        5962 :     Size        querysize = VARSIZE(query);
     467        5962 :     char       *cache = (char *) fcinfo->flinfo->fn_extra;
     468             : 
     469             :     /*
     470             :      * Cache the generated trigrams across multiple calls with the same query.
     471             :      */
     472       11916 :     if (cache == NULL ||
     473        5954 :         VARSIZE(cache) != querysize ||
     474        5954 :         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        5962 :     qtrg = (TRGM *) (cache + MAXALIGN(querysize));
     494             : 
     495        5962 :     switch (strategy)
     496             :     {
     497        5962 :         case DistanceStrategyNumber:
     498             :         case WordDistanceStrategyNumber:
     499             :         case StrictWordDistanceStrategyNumber:
     500             :             /* Only plain trigram distance is exact */
     501        5962 :             *recheck = (strategy != DistanceStrategyNumber);
     502        5962 :             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        5876 :                 float4 volatile sml = cnt_sml(qtrg, key, *recheck);
     511             : 
     512        5876 :                 res = 1.0 - sml;
     513             :             }
     514          86 :             else if (ISALLTRUE(key))
     515             :             {                   /* all leafs contains orig trgm */
     516           0 :                 res = 0.0;
     517             :             }
     518             :             else
     519             :             {                   /* non-leaf contains signature */
     520          86 :                 int32       count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
     521          86 :                 int32       len = ARRNELEM(qtrg);
     522             : 
     523          86 :                 res = (len == 0) ? -1.0 : 1.0 - ((float8) count) / ((float8) len);
     524             :             }
     525        5962 :             break;
     526           0 :         default:
     527           0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
     528             :             res = 0;            /* keep compiler quiet */
     529             :             break;
     530             :     }
     531             : 
     532        5962 :     PG_RETURN_FLOAT8(res);
     533             : }
     534             : 
     535             : static int32
     536      110300 : unionkey(BITVECP sbase, TRGM *add, int siglen)
     537             : {
     538             :     int32       i;
     539             : 
     540      110300 :     if (ISSIGNKEY(add))
     541             :     {
     542       55150 :         BITVECP     sadd = GETSIGN(add);
     543             : 
     544       55150 :         if (ISALLTRUE(add))
     545           0 :             return 1;
     546             : 
     547    13070630 :         LOOPBYTE(siglen)
     548    13015480 :             sbase[i] |= sadd[i];
     549             :     }
     550             :     else
     551             :     {
     552       55150 :         trgm       *ptr = GETARR(add);
     553       55150 :         int32       tmp = 0;
     554             : 
     555      551614 :         for (i = 0; i < ARRNELEM(add); i++)
     556             :         {
     557      496464 :             CPTRGM(&tmp, ptr + i);
     558      496464 :             HASH(sbase, tmp, siglen);
     559             :         }
     560             :     }
     561      110300 :     return 0;
     562             : }
     563             : 
     564             : 
     565             : Datum
     566       55150 : gtrgm_union(PG_FUNCTION_ARGS)
     567             : {
     568       55150 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     569       55150 :     int32       len = entryvec->n;
     570       55150 :     int        *size = (int *) PG_GETARG_POINTER(1);
     571       55150 :     int         siglen = GET_SIGLEN();
     572             :     int32       i;
     573       55150 :     TRGM       *result = gtrgm_alloc(false, siglen, NULL);
     574       55150 :     BITVECP     base = GETSIGN(result);
     575             : 
     576      165450 :     for (i = 0; i < len; i++)
     577             :     {
     578      110300 :         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       55150 :     *size = VARSIZE(result);
     587             : 
     588       55150 :     PG_RETURN_POINTER(result);
     589             : }
     590             : 
     591             : Datum
     592       55150 : gtrgm_same(PG_FUNCTION_ARGS)
     593             : {
     594       55150 :     TRGM       *a = (TRGM *) PG_GETARG_POINTER(0);
     595       55150 :     TRGM       *b = (TRGM *) PG_GETARG_POINTER(1);
     596       55150 :     bool       *result = (bool *) PG_GETARG_POINTER(2);
     597       55150 :     int         siglen = GET_SIGLEN();
     598             : 
     599       55150 :     if (ISSIGNKEY(a))
     600             :     {                           /* then b also ISSIGNKEY */
     601       55150 :         if (ISALLTRUE(a) && ISALLTRUE(b))
     602           0 :             *result = true;
     603       55150 :         else if (ISALLTRUE(a))
     604           0 :             *result = false;
     605       55150 :         else if (ISALLTRUE(b))
     606           0 :             *result = false;
     607             :         else
     608             :         {
     609             :             int32       i;
     610       55150 :             BITVECP     sa = GETSIGN(a),
     611       55150 :                         sb = GETSIGN(b);
     612             : 
     613       55150 :             *result = true;
     614     6340060 :             LOOPBYTE(siglen)
     615             :             {
     616     6290544 :                 if (sa[i] != sb[i])
     617             :                 {
     618        5634 :                     *result = false;
     619        5634 :                     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       55150 :     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     9842780 : hemdistsign(BITVECP a, BITVECP b, int siglen)
     658             : {
     659             :     int         i,
     660             :                 diff,
     661     9842780 :                 dist = 0;
     662             : 
     663   706333708 :     LOOPBYTE(siglen)
     664             :     {
     665   696490928 :         diff = (unsigned char) (a[i] ^ b[i]);
     666             :         /* Using the popcount functions here isn't likely to win */
     667   696490928 :         dist += pg_number_of_ones[diff];
     668             :     }
     669     9842780 :     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     2372438 : gtrgm_penalty(PG_FUNCTION_ARGS)
     690             : {
     691     2372438 :     GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
     692     2372438 :     GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
     693     2372438 :     float      *penalty = (float *) PG_GETARG_POINTER(2);
     694     2372438 :     int         siglen = GET_SIGLEN();
     695     2372438 :     TRGM       *origval = (TRGM *) DatumGetPointer(origentry->key);
     696     2372438 :     TRGM       *newval = (TRGM *) DatumGetPointer(newentry->key);
     697     2372438 :     BITVECP     orig = GETSIGN(origval);
     698             : 
     699     2372438 :     *penalty = 0.0;
     700             : 
     701     2372438 :     if (ISARRKEY(newval))
     702             :     {
     703     2372438 :         char       *cache = (char *) fcinfo->flinfo->fn_extra;
     704     2372438 :         TRGM       *cachedVal = (TRGM *) (cache + MAXALIGN(siglen));
     705     2372438 :         Size        newvalsize = VARSIZE(newval);
     706             :         BITVECP     sign;
     707             : 
     708             :         /*
     709             :          * Cache the sign data across multiple calls with the same newval.
     710             :          */
     711     4744864 :         if (cache == NULL ||
     712     2372426 :             VARSIZE(cachedVal) != newvalsize ||
     713     2370372 :             memcmp(cachedVal, newval, newvalsize) != 0)
     714             :         {
     715             :             char       *newcache;
     716             : 
     717        7526 :             newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     718        7526 :                                           MAXALIGN(siglen) +
     719             :                                           newvalsize);
     720             : 
     721        7526 :             makesign((BITVECP) newcache, newval, siglen);
     722             : 
     723        7526 :             cachedVal = (TRGM *) (newcache + MAXALIGN(siglen));
     724        7526 :             memcpy(cachedVal, newval, newvalsize);
     725             : 
     726        7526 :             if (cache)
     727        7514 :                 pfree(cache);
     728        7526 :             fcinfo->flinfo->fn_extra = newcache;
     729        7526 :             cache = newcache;
     730             :         }
     731             : 
     732     2372438 :         sign = (BITVECP) cache;
     733             : 
     734     2372438 :         if (ISALLTRUE(origval))
     735           0 :             *penalty = ((float) (SIGLENBIT(siglen) - sizebitvec(sign, siglen))) / (float) (SIGLENBIT(siglen) + 1);
     736             :         else
     737     2372438 :             *penalty = hemdistsign(sign, orig, siglen);
     738             :     }
     739             :     else
     740           0 :         *penalty = hemdist(origval, newval, siglen);
     741     2372438 :     PG_RETURN_POINTER(penalty);
     742             : }
     743             : 
     744             : typedef struct
     745             : {
     746             :     bool        allistrue;
     747             :     BITVECP     sign;
     748             : } CACHESIGN;
     749             : 
     750             : static void
     751       88384 : fillcache(CACHESIGN *item, TRGM *key, BITVECP sign, int siglen)
     752             : {
     753       88384 :     item->allistrue = false;
     754       88384 :     item->sign = sign;
     755       88384 :     if (ISARRKEY(key))
     756       87928 :         makesign(item->sign, key, siglen);
     757         456 :     else if (ISALLTRUE(key))
     758           0 :         item->allistrue = true;
     759             :     else
     760         456 :         memcpy(item->sign, GETSIGN(key), siglen);
     761       88384 : }
     762             : 
     763             : #define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
     764             : typedef struct
     765             : {
     766             :     OffsetNumber pos;
     767             :     int32       cost;
     768             : } SPLITCOST;
     769             : 
     770             : static int
     771       98962 : comparecost(const void *a, const void *b)
     772             : {
     773       98962 :     if (((const SPLITCOST *) a)->cost == ((const SPLITCOST *) b)->cost)
     774       88672 :         return 0;
     775             :     else
     776       10290 :         return (((const SPLITCOST *) a)->cost > ((const SPLITCOST *) b)->cost) ? 1 : -1;
     777             : }
     778             : 
     779             : 
     780             : static int
     781     7295822 : hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
     782             : {
     783     7295822 :     if (a->allistrue)
     784             :     {
     785           0 :         if (b->allistrue)
     786           0 :             return 0;
     787             :         else
     788           0 :             return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen);
     789             :     }
     790     7295822 :     else if (b->allistrue)
     791           0 :         return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
     792             : 
     793     7295822 :     return hemdistsign(a->sign, b->sign, siglen);
     794             : }
     795             : 
     796             : Datum
     797         562 : gtrgm_picksplit(PG_FUNCTION_ARGS)
     798             : {
     799         562 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     800         562 :     OffsetNumber maxoff = entryvec->n - 1;
     801         562 :     GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
     802         562 :     int         siglen = GET_SIGLEN();
     803             :     OffsetNumber k,
     804             :                 j;
     805             :     TRGM       *datum_l,
     806             :                *datum_r;
     807             :     BITVECP     union_l,
     808             :                 union_r;
     809             :     int32       size_alpha,
     810             :                 size_beta;
     811             :     int32       size_waste,
     812         562 :                 waste = -1;
     813             :     int32       nbytes;
     814         562 :     OffsetNumber seed_1 = 0,
     815         562 :                 seed_2 = 0;
     816             :     OffsetNumber *left,
     817             :                *right;
     818             :     BITVECP     ptr;
     819             :     int         i;
     820             :     CACHESIGN  *cache;
     821             :     char       *cache_sign;
     822             :     SPLITCOST  *costvector;
     823             : 
     824             :     /* cache the sign data for each existing item */
     825         562 :     cache = palloc_array(CACHESIGN, maxoff + 1);
     826         562 :     cache_sign = palloc(siglen * (maxoff + 1));
     827             : 
     828       88946 :     for (k = FirstOffsetNumber; k <= maxoff; k = OffsetNumberNext(k))
     829       88384 :         fillcache(&cache[k], GETENTRY(entryvec, k), &cache_sign[siglen * k],
     830             :                   siglen);
     831             : 
     832             :     /* now find the two furthest-apart items */
     833       88384 :     for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
     834             :     {
     835     7206876 :         for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
     836             :         {
     837     7119054 :             size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
     838     7119054 :             if (size_waste > waste)
     839             :             {
     840         982 :                 waste = size_waste;
     841         982 :                 seed_1 = k;
     842         982 :                 seed_2 = j;
     843             :             }
     844             :         }
     845             :     }
     846             : 
     847             :     /* just in case we didn't make a selection ... */
     848         562 :     if (seed_1 == 0 || seed_2 == 0)
     849             :     {
     850           0 :         seed_1 = 1;
     851           0 :         seed_2 = 2;
     852             :     }
     853             : 
     854             :     /* initialize the result vectors */
     855         562 :     nbytes = maxoff * sizeof(OffsetNumber);
     856         562 :     v->spl_left = left = (OffsetNumber *) palloc(nbytes);
     857         562 :     v->spl_right = right = (OffsetNumber *) palloc(nbytes);
     858         562 :     v->spl_nleft = 0;
     859         562 :     v->spl_nright = 0;
     860             : 
     861             :     /* form initial .. */
     862         562 :     datum_l = gtrgm_alloc(cache[seed_1].allistrue, siglen, cache[seed_1].sign);
     863         562 :     datum_r = gtrgm_alloc(cache[seed_2].allistrue, siglen, cache[seed_2].sign);
     864             : 
     865         562 :     union_l = GETSIGN(datum_l);
     866         562 :     union_r = GETSIGN(datum_r);
     867             : 
     868             :     /* sort before ... */
     869         562 :     costvector = palloc_array(SPLITCOST, maxoff);
     870       88946 :     for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
     871             :     {
     872       88384 :         costvector[j - 1].pos = j;
     873       88384 :         size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
     874       88384 :         size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
     875       88384 :         costvector[j - 1].cost = abs(size_alpha - size_beta);
     876             :     }
     877         562 :     qsort(costvector, maxoff, sizeof(SPLITCOST), comparecost);
     878             : 
     879       88946 :     for (k = 0; k < maxoff; k++)
     880             :     {
     881       88384 :         j = costvector[k].pos;
     882       88384 :         if (j == seed_1)
     883             :         {
     884         562 :             *left++ = j;
     885         562 :             v->spl_nleft++;
     886         562 :             continue;
     887             :         }
     888       87822 :         else if (j == seed_2)
     889             :         {
     890         562 :             *right++ = j;
     891         562 :             v->spl_nright++;
     892         562 :             continue;
     893             :         }
     894             : 
     895       87260 :         if (ISALLTRUE(datum_l) || cache[j].allistrue)
     896             :         {
     897           0 :             if (ISALLTRUE(datum_l) && cache[j].allistrue)
     898           0 :                 size_alpha = 0;
     899             :             else
     900           0 :                 size_alpha = SIGLENBIT(siglen) -
     901           0 :                     sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) :
     902           0 :                                GETSIGN(cache[j].sign),
     903             :                                siglen);
     904             :         }
     905             :         else
     906       87260 :             size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
     907             : 
     908       87260 :         if (ISALLTRUE(datum_r) || cache[j].allistrue)
     909             :         {
     910           0 :             if (ISALLTRUE(datum_r) && cache[j].allistrue)
     911           0 :                 size_beta = 0;
     912             :             else
     913           0 :                 size_beta = SIGLENBIT(siglen) -
     914           0 :                     sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) :
     915           0 :                                GETSIGN(cache[j].sign),
     916             :                                siglen);
     917             :         }
     918             :         else
     919       87260 :             size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
     920             : 
     921       87260 :         if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
     922             :         {
     923       43382 :             if (ISALLTRUE(datum_l) || cache[j].allistrue)
     924             :             {
     925           0 :                 if (!ISALLTRUE(datum_l))
     926           0 :                     memset(GETSIGN(datum_l), 0xff, siglen);
     927             :             }
     928             :             else
     929             :             {
     930       43382 :                 ptr = cache[j].sign;
     931     4744902 :                 LOOPBYTE(siglen)
     932     4701520 :                     union_l[i] |= ptr[i];
     933             :             }
     934       43382 :             *left++ = j;
     935       43382 :             v->spl_nleft++;
     936             :         }
     937             :         else
     938             :         {
     939       43878 :             if (ISALLTRUE(datum_r) || cache[j].allistrue)
     940             :             {
     941           0 :                 if (!ISALLTRUE(datum_r))
     942           0 :                     memset(GETSIGN(datum_r), 0xff, siglen);
     943             :             }
     944             :             else
     945             :             {
     946       43878 :                 ptr = cache[j].sign;
     947     4715134 :                 LOOPBYTE(siglen)
     948     4671256 :                     union_r[i] |= ptr[i];
     949             :             }
     950       43878 :             *right++ = j;
     951       43878 :             v->spl_nright++;
     952             :         }
     953             :     }
     954             : 
     955         562 :     v->spl_ldatum = PointerGetDatum(datum_l);
     956         562 :     v->spl_rdatum = PointerGetDatum(datum_r);
     957             : 
     958         562 :     PG_RETURN_POINTER(v);
     959             : }
     960             : 
     961             : Datum
     962          56 : gtrgm_options(PG_FUNCTION_ARGS)
     963             : {
     964          56 :     local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
     965             : 
     966          56 :     init_local_reloptions(relopts, sizeof(TrgmGistOptions));
     967          56 :     add_local_int_reloption(relopts, "siglen",
     968             :                             "signature length in bytes",
     969             :                             SIGLEN_DEFAULT, 1, SIGLEN_MAX,
     970             :                             offsetof(TrgmGistOptions, siglen));
     971             : 
     972          56 :     PG_RETURN_VOID();
     973             : }

Generated by: LCOV version 1.16