LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginlogic.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 72.7 % 77 56
Test Date: 2026-02-17 17:20:33 Functions: 57.1 % 7 4
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * ginlogic.c
       4              :  *    routines for performing binary- and ternary-logic consistent checks.
       5              :  *
       6              :  * A GIN operator class can provide a boolean or ternary consistent
       7              :  * function, or both.  This file provides both boolean and ternary
       8              :  * interfaces to the rest of the GIN code, even if only one of them is
       9              :  * implemented by the opclass.
      10              :  *
      11              :  * Providing a boolean interface when the opclass implements only the
      12              :  * ternary function is straightforward - just call the ternary function
      13              :  * with the check-array as is, and map the GIN_TRUE, GIN_FALSE, GIN_MAYBE
      14              :  * return codes to TRUE, FALSE and TRUE+recheck, respectively.  Providing
      15              :  * a ternary interface when the opclass only implements a boolean function
      16              :  * is implemented by calling the boolean function many times, with all the
      17              :  * MAYBE arguments set to all combinations of TRUE and FALSE (up to a
      18              :  * certain number of MAYBE arguments).
      19              :  *
      20              :  * (A boolean function is enough to determine if an item matches, but a
      21              :  * GIN scan can apply various optimizations if it can determine that an
      22              :  * item matches or doesn't match, even if it doesn't know if some of the
      23              :  * keys are present or not.  That's what the ternary consistent function
      24              :  * is used for.)
      25              :  *
      26              :  *
      27              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      28              :  * Portions Copyright (c) 1994, Regents of the University of California
      29              :  *
      30              :  * IDENTIFICATION
      31              :  *          src/backend/access/gin/ginlogic.c
      32              :  *-------------------------------------------------------------------------
      33              :  */
      34              : 
      35              : #include "postgres.h"
      36              : 
      37              : #include "access/gin_private.h"
      38              : 
      39              : 
      40              : /*
      41              :  * Maximum number of MAYBE inputs that shimTriConsistentFn will try to
      42              :  * resolve by calling all combinations.
      43              :  */
      44              : #define MAX_MAYBE_ENTRIES   4
      45              : 
      46              : /*
      47              :  * Dummy consistent functions for an EVERYTHING key.  Just claim it matches.
      48              :  */
      49              : static bool
      50            0 : trueConsistentFn(GinScanKey key)
      51              : {
      52            0 :     key->recheckCurItem = false;
      53            0 :     return true;
      54              : }
      55              : static GinTernaryValue
      56            0 : trueTriConsistentFn(GinScanKey key)
      57              : {
      58            0 :     return GIN_TRUE;
      59              : }
      60              : 
      61              : /*
      62              :  * A helper function for calling a regular, binary logic, consistent function.
      63              :  */
      64              : static bool
      65        19278 : directBoolConsistentFn(GinScanKey key)
      66              : {
      67              :     /*
      68              :      * Initialize recheckCurItem in case the consistentFn doesn't know it
      69              :      * should set it.  The safe assumption in that case is to force recheck.
      70              :      */
      71        19278 :     key->recheckCurItem = true;
      72              : 
      73        38556 :     return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
      74              :                                           key->collation,
      75        19278 :                                           PointerGetDatum(key->entryRes),
      76        19278 :                                           UInt16GetDatum(key->strategy),
      77              :                                           key->query,
      78              :                                           UInt32GetDatum(key->nuserentries),
      79        19278 :                                           PointerGetDatum(key->extra_data),
      80        19278 :                                           PointerGetDatum(&key->recheckCurItem),
      81        19278 :                                           PointerGetDatum(key->queryValues),
      82        19278 :                                           PointerGetDatum(key->queryCategories)));
      83              : }
      84              : 
      85              : /*
      86              :  * A helper function for calling a native ternary logic consistent function.
      87              :  */
      88              : static GinTernaryValue
      89       486499 : directTriConsistentFn(GinScanKey key)
      90              : {
      91       972998 :     return DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
      92              :                                                      key->collation,
      93       486499 :                                                      PointerGetDatum(key->entryRes),
      94       486499 :                                                      UInt16GetDatum(key->strategy),
      95              :                                                      key->query,
      96              :                                                      UInt32GetDatum(key->nuserentries),
      97       486499 :                                                      PointerGetDatum(key->extra_data),
      98       486499 :                                                      PointerGetDatum(key->queryValues),
      99       486499 :                                                      PointerGetDatum(key->queryCategories)));
     100              : }
     101              : 
     102              : /*
     103              :  * This function implements a binary logic consistency check, using a ternary
     104              :  * logic consistent function provided by the opclass. GIN_MAYBE return value
     105              :  * is interpreted as true with recheck flag.
     106              :  */
     107              : static bool
     108            0 : shimBoolConsistentFn(GinScanKey key)
     109              : {
     110              :     GinTernaryValue result;
     111              : 
     112            0 :     result = DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
     113              :                                                        key->collation,
     114            0 :                                                        PointerGetDatum(key->entryRes),
     115            0 :                                                        UInt16GetDatum(key->strategy),
     116              :                                                        key->query,
     117              :                                                        UInt32GetDatum(key->nuserentries),
     118            0 :                                                        PointerGetDatum(key->extra_data),
     119            0 :                                                        PointerGetDatum(key->queryValues),
     120            0 :                                                        PointerGetDatum(key->queryCategories)));
     121            0 :     if (result == GIN_MAYBE)
     122              :     {
     123            0 :         key->recheckCurItem = true;
     124            0 :         return true;
     125              :     }
     126              :     else
     127              :     {
     128            0 :         key->recheckCurItem = false;
     129            0 :         return result;
     130              :     }
     131              : }
     132              : 
     133              : /*
     134              :  * This function implements a tri-state consistency check, using a boolean
     135              :  * consistent function provided by the opclass.
     136              :  *
     137              :  * Our strategy is to call consistentFn with MAYBE inputs replaced with every
     138              :  * combination of TRUE/FALSE. If consistentFn returns the same value for every
     139              :  * combination, that's the overall result. Otherwise, return MAYBE. Testing
     140              :  * every combination is O(n^2), so this is only feasible for a small number of
     141              :  * MAYBE inputs.
     142              :  *
     143              :  * NB: This function modifies the key->entryRes array.  For now that's okay
     144              :  * so long as we restore the entry-time contents before returning.  This may
     145              :  * need revisiting if we ever invent multithreaded GIN scans, though.
     146              :  */
     147              : static GinTernaryValue
     148        18801 : shimTriConsistentFn(GinScanKey key)
     149              : {
     150              :     int         nmaybe;
     151              :     int         maybeEntries[MAX_MAYBE_ENTRIES];
     152              :     int         i;
     153              :     bool        boolResult;
     154              :     bool        recheck;
     155              :     GinTernaryValue curResult;
     156              : 
     157              :     /*
     158              :      * Count how many MAYBE inputs there are, and store their indexes in
     159              :      * maybeEntries. If there are too many MAYBE inputs, it's not feasible to
     160              :      * test all combinations, so give up and return MAYBE.
     161              :      */
     162        18801 :     nmaybe = 0;
     163        71639 :     for (i = 0; i < key->nentries; i++)
     164              :     {
     165        52838 :         if (key->entryRes[i] == GIN_MAYBE)
     166              :         {
     167           38 :             if (nmaybe >= MAX_MAYBE_ENTRIES)
     168            0 :                 return GIN_MAYBE;
     169           38 :             maybeEntries[nmaybe++] = i;
     170              :         }
     171              :     }
     172              : 
     173              :     /*
     174              :      * If none of the inputs were MAYBE, we can just call the consistent
     175              :      * function as-is.
     176              :      */
     177        18801 :     if (nmaybe == 0)
     178        18775 :         return directBoolConsistentFn(key);
     179              : 
     180              :     /* First call consistent function with all the maybe-inputs set FALSE */
     181           64 :     for (i = 0; i < nmaybe; i++)
     182           38 :         key->entryRes[maybeEntries[i]] = GIN_FALSE;
     183           26 :     curResult = directBoolConsistentFn(key);
     184           26 :     recheck = key->recheckCurItem;
     185              : 
     186              :     for (;;)
     187              :     {
     188              :         /* Twiddle the entries for next combination. */
     189          108 :         for (i = 0; i < nmaybe; i++)
     190              :         {
     191           88 :             if (key->entryRes[maybeEntries[i]] == GIN_FALSE)
     192              :             {
     193           48 :                 key->entryRes[maybeEntries[i]] = GIN_TRUE;
     194           48 :                 break;
     195              :             }
     196              :             else
     197           40 :                 key->entryRes[maybeEntries[i]] = GIN_FALSE;
     198              :         }
     199           68 :         if (i == nmaybe)
     200           20 :             break;
     201              : 
     202           48 :         boolResult = directBoolConsistentFn(key);
     203           48 :         recheck |= key->recheckCurItem;
     204              : 
     205           48 :         if (curResult != boolResult)
     206              :         {
     207            6 :             curResult = GIN_MAYBE;
     208            6 :             break;
     209              :         }
     210              :     }
     211              : 
     212              :     /* TRUE with recheck is taken to mean MAYBE */
     213           26 :     if (curResult == GIN_TRUE && recheck)
     214            3 :         curResult = GIN_MAYBE;
     215              : 
     216              :     /* We must restore the original state of the entryRes array */
     217           64 :     for (i = 0; i < nmaybe; i++)
     218           38 :         key->entryRes[maybeEntries[i]] = GIN_MAYBE;
     219              : 
     220           26 :     return curResult;
     221              : }
     222              : 
     223              : /*
     224              :  * Set up the implementation of the consistent functions for a scan key.
     225              :  */
     226              : void
     227         1037 : ginInitConsistentFunction(GinState *ginstate, GinScanKey key)
     228              : {
     229         1037 :     if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING)
     230              :     {
     231            0 :         key->boolConsistentFn = trueConsistentFn;
     232            0 :         key->triConsistentFn = trueTriConsistentFn;
     233              :     }
     234              :     else
     235              :     {
     236         1037 :         key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1];
     237         1037 :         key->triConsistentFmgrInfo = &ginstate->triConsistentFn[key->attnum - 1];
     238         1037 :         key->collation = ginstate->supportCollation[key->attnum - 1];
     239              : 
     240         1037 :         if (OidIsValid(ginstate->consistentFn[key->attnum - 1].fn_oid))
     241         1037 :             key->boolConsistentFn = directBoolConsistentFn;
     242              :         else
     243            0 :             key->boolConsistentFn = shimBoolConsistentFn;
     244              : 
     245         1037 :         if (OidIsValid(ginstate->triConsistentFn[key->attnum - 1].fn_oid))
     246          691 :             key->triConsistentFn = directTriConsistentFn;
     247              :         else
     248          346 :             key->triConsistentFn = shimTriConsistentFn;
     249              :     }
     250         1037 : }
        

Generated by: LCOV version 2.0-1