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

Generated by: LCOV version 2.3.2-1