LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginlogic.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 56 77 72.7 %
Date: 2025-04-30 13:15:34 Functions: 4 7 57.1 %
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-2025, 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       37486 : 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       37486 :     key->recheckCurItem = true;
      72             : 
      73       74972 :     return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
      74             :                                           key->collation,
      75       37486 :                                           PointerGetDatum(key->entryRes),
      76       37486 :                                           UInt16GetDatum(key->strategy),
      77             :                                           key->query,
      78             :                                           UInt32GetDatum(key->nuserentries),
      79       37486 :                                           PointerGetDatum(key->extra_data),
      80       37486 :                                           PointerGetDatum(&key->recheckCurItem),
      81       37486 :                                           PointerGetDatum(key->queryValues),
      82       37486 :                                           PointerGetDatum(key->queryCategories)));
      83             : }
      84             : 
      85             : /*
      86             :  * A helper function for calling a native ternary logic consistent function.
      87             :  */
      88             : static GinTernaryValue
      89      969098 : directTriConsistentFn(GinScanKey key)
      90             : {
      91     1938196 :     return DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
      92             :                                                      key->collation,
      93      969098 :                                                      PointerGetDatum(key->entryRes),
      94      969098 :                                                      UInt16GetDatum(key->strategy),
      95             :                                                      key->query,
      96             :                                                      UInt32GetDatum(key->nuserentries),
      97      969098 :                                                      PointerGetDatum(key->extra_data),
      98      969098 :                                                      PointerGetDatum(key->queryValues),
      99      969098 :                                                      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       36540 : 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       36540 :     nmaybe = 0;
     163      141154 :     for (i = 0; i < key->nentries; i++)
     164             :     {
     165      104614 :         if (key->entryRes[i] == GIN_MAYBE)
     166             :         {
     167          76 :             if (nmaybe >= MAX_MAYBE_ENTRIES)
     168           0 :                 return GIN_MAYBE;
     169          76 :             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       36540 :     if (nmaybe == 0)
     178       36488 :         return directBoolConsistentFn(key);
     179             : 
     180             :     /* First call consistent function with all the maybe-inputs set FALSE */
     181         128 :     for (i = 0; i < nmaybe; i++)
     182          76 :         key->entryRes[maybeEntries[i]] = GIN_FALSE;
     183          52 :     curResult = directBoolConsistentFn(key);
     184          52 :     recheck = key->recheckCurItem;
     185             : 
     186             :     for (;;)
     187             :     {
     188             :         /* Twiddle the entries for next combination. */
     189         216 :         for (i = 0; i < nmaybe; i++)
     190             :         {
     191         176 :             if (key->entryRes[maybeEntries[i]] == GIN_FALSE)
     192             :             {
     193          96 :                 key->entryRes[maybeEntries[i]] = GIN_TRUE;
     194          96 :                 break;
     195             :             }
     196             :             else
     197          80 :                 key->entryRes[maybeEntries[i]] = GIN_FALSE;
     198             :         }
     199         136 :         if (i == nmaybe)
     200          40 :             break;
     201             : 
     202          96 :         boolResult = directBoolConsistentFn(key);
     203          96 :         recheck |= key->recheckCurItem;
     204             : 
     205          96 :         if (curResult != boolResult)
     206             :         {
     207          12 :             curResult = GIN_MAYBE;
     208          12 :             break;
     209             :         }
     210             :     }
     211             : 
     212             :     /* TRUE with recheck is taken to mean MAYBE */
     213          52 :     if (curResult == GIN_TRUE && recheck)
     214           6 :         curResult = GIN_MAYBE;
     215             : 
     216             :     /* We must restore the original state of the entryRes array */
     217         128 :     for (i = 0; i < nmaybe; i++)
     218          76 :         key->entryRes[maybeEntries[i]] = GIN_MAYBE;
     219             : 
     220          52 :     return curResult;
     221             : }
     222             : 
     223             : /*
     224             :  * Set up the implementation of the consistent functions for a scan key.
     225             :  */
     226             : void
     227        1716 : ginInitConsistentFunction(GinState *ginstate, GinScanKey key)
     228             : {
     229        1716 :     if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING)
     230             :     {
     231           0 :         key->boolConsistentFn = trueConsistentFn;
     232           0 :         key->triConsistentFn = trueTriConsistentFn;
     233             :     }
     234             :     else
     235             :     {
     236        1716 :         key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1];
     237        1716 :         key->triConsistentFmgrInfo = &ginstate->triConsistentFn[key->attnum - 1];
     238        1716 :         key->collation = ginstate->supportCollation[key->attnum - 1];
     239             : 
     240        1716 :         if (OidIsValid(ginstate->consistentFn[key->attnum - 1].fn_oid))
     241        1716 :             key->boolConsistentFn = directBoolConsistentFn;
     242             :         else
     243           0 :             key->boolConsistentFn = shimBoolConsistentFn;
     244             : 
     245        1716 :         if (OidIsValid(ginstate->triConsistentFn[key->attnum - 1].fn_oid))
     246        1374 :             key->triConsistentFn = directTriConsistentFn;
     247             :         else
     248         342 :             key->triConsistentFn = shimTriConsistentFn;
     249             :     }
     250        1716 : }

Generated by: LCOV version 1.14