diff options
Diffstat (limited to 'src/backend/access/gin/ginlogic.c')
-rw-r--r-- | src/backend/access/gin/ginlogic.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c new file mode 100644 index 0000000..c38c27f --- /dev/null +++ b/src/backend/access/gin/ginlogic.c @@ -0,0 +1,246 @@ +/*------------------------------------------------------------------------- + * + * ginlogic.c + * routines for performing binary- and ternary-logic consistent checks. + * + * A GIN operator class can provide a boolean or ternary consistent + * function, or both. This file provides both boolean and ternary + * interfaces to the rest of the GIN code, even if only one of them is + * implemented by the opclass. + * + * Providing a boolean interface when the opclass implements only the + * ternary function is straightforward - just call the ternary function + * with the check-array as is, and map the GIN_TRUE, GIN_FALSE, GIN_MAYBE + * return codes to TRUE, FALSE and TRUE+recheck, respectively. Providing + * a ternary interface when the opclass only implements a boolean function + * is implemented by calling the boolean function many times, with all the + * MAYBE arguments set to all combinations of TRUE and FALSE (up to a + * certain number of MAYBE arguments). + * + * (A boolean function is enough to determine if an item matches, but a + * GIN scan can apply various optimizations if it can determine that an + * item matches or doesn't match, even if it doesn't know if some of the + * keys are present or not. That's what the ternary consistent function + * is used for.) + * + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/access/gin/ginlogic.c + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/gin_private.h" +#include "access/reloptions.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_type.h" +#include "miscadmin.h" +#include "storage/indexfsm.h" +#include "storage/lmgr.h" + + +/* + * Maximum number of MAYBE inputs that shimTriConsistentFn will try to + * resolve by calling all combinations. + */ +#define MAX_MAYBE_ENTRIES 4 + +/* + * Dummy consistent functions for an EVERYTHING key. Just claim it matches. + */ +static bool +trueConsistentFn(GinScanKey key) +{ + key->recheckCurItem = false; + return true; +} +static GinTernaryValue +trueTriConsistentFn(GinScanKey key) +{ + return GIN_TRUE; +} + +/* + * A helper function for calling a regular, binary logic, consistent function. + */ +static bool +directBoolConsistentFn(GinScanKey key) +{ + /* + * Initialize recheckCurItem in case the consistentFn doesn't know it + * should set it. The safe assumption in that case is to force recheck. + */ + key->recheckCurItem = true; + + return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo, + key->collation, + PointerGetDatum(key->entryRes), + UInt16GetDatum(key->strategy), + key->query, + UInt32GetDatum(key->nuserentries), + PointerGetDatum(key->extra_data), + PointerGetDatum(&key->recheckCurItem), + PointerGetDatum(key->queryValues), + PointerGetDatum(key->queryCategories))); +} + +/* + * A helper function for calling a native ternary logic consistent function. + */ +static GinTernaryValue +directTriConsistentFn(GinScanKey key) +{ + return DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo, + key->collation, + PointerGetDatum(key->entryRes), + UInt16GetDatum(key->strategy), + key->query, + UInt32GetDatum(key->nuserentries), + PointerGetDatum(key->extra_data), + PointerGetDatum(key->queryValues), + PointerGetDatum(key->queryCategories))); +} + +/* + * This function implements a binary logic consistency check, using a ternary + * logic consistent function provided by the opclass. GIN_MAYBE return value + * is interpreted as true with recheck flag. + */ +static bool +shimBoolConsistentFn(GinScanKey key) +{ + GinTernaryValue result; + + result = DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo, + key->collation, + PointerGetDatum(key->entryRes), + UInt16GetDatum(key->strategy), + key->query, + UInt32GetDatum(key->nuserentries), + PointerGetDatum(key->extra_data), + PointerGetDatum(key->queryValues), + PointerGetDatum(key->queryCategories))); + if (result == GIN_MAYBE) + { + key->recheckCurItem = true; + return true; + } + else + { + key->recheckCurItem = false; + return result; + } +} + +/* + * This function implements a tri-state consistency check, using a boolean + * consistent function provided by the opclass. + * + * Our strategy is to call consistentFn with MAYBE inputs replaced with every + * combination of TRUE/FALSE. If consistentFn returns the same value for every + * combination, that's the overall result. Otherwise, return MAYBE. Testing + * every combination is O(n^2), so this is only feasible for a small number of + * MAYBE inputs. + * + * NB: This function modifies the key->entryRes array! + */ +static GinTernaryValue +shimTriConsistentFn(GinScanKey key) +{ + int nmaybe; + int maybeEntries[MAX_MAYBE_ENTRIES]; + int i; + bool boolResult; + bool recheck = false; + GinTernaryValue curResult; + + /* + * Count how many MAYBE inputs there are, and store their indexes in + * maybeEntries. If there are too many MAYBE inputs, it's not feasible to + * test all combinations, so give up and return MAYBE. + */ + nmaybe = 0; + for (i = 0; i < key->nentries; i++) + { + if (key->entryRes[i] == GIN_MAYBE) + { + if (nmaybe >= MAX_MAYBE_ENTRIES) + return GIN_MAYBE; + maybeEntries[nmaybe++] = i; + } + } + + /* + * If none of the inputs were MAYBE, so we can just call consistent + * function as is. + */ + if (nmaybe == 0) + return directBoolConsistentFn(key); + + /* First call consistent function with all the maybe-inputs set FALSE */ + for (i = 0; i < nmaybe; i++) + key->entryRes[maybeEntries[i]] = GIN_FALSE; + curResult = directBoolConsistentFn(key); + + for (;;) + { + /* Twiddle the entries for next combination. */ + for (i = 0; i < nmaybe; i++) + { + if (key->entryRes[maybeEntries[i]] == GIN_FALSE) + { + key->entryRes[maybeEntries[i]] = GIN_TRUE; + break; + } + else + key->entryRes[maybeEntries[i]] = GIN_FALSE; + } + if (i == nmaybe) + break; + + boolResult = directBoolConsistentFn(key); + recheck |= key->recheckCurItem; + + if (curResult != boolResult) + return GIN_MAYBE; + } + + /* TRUE with recheck is taken to mean MAYBE */ + if (curResult == GIN_TRUE && recheck) + curResult = GIN_MAYBE; + + return curResult; +} + +/* + * Set up the implementation of the consistent functions for a scan key. + */ +void +ginInitConsistentFunction(GinState *ginstate, GinScanKey key) +{ + if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING) + { + key->boolConsistentFn = trueConsistentFn; + key->triConsistentFn = trueTriConsistentFn; + } + else + { + key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1]; + key->triConsistentFmgrInfo = &ginstate->triConsistentFn[key->attnum - 1]; + key->collation = ginstate->supportCollation[key->attnum - 1]; + + if (OidIsValid(ginstate->consistentFn[key->attnum - 1].fn_oid)) + key->boolConsistentFn = directBoolConsistentFn; + else + key->boolConsistentFn = shimBoolConsistentFn; + + if (OidIsValid(ginstate->triConsistentFn[key->attnum - 1].fn_oid)) + key->triConsistentFn = directTriConsistentFn; + else + key->triConsistentFn = shimTriConsistentFn; + } +} |