mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-30 02:01:20 +02:00
c8e1ba736b
Backpatch-through: 11
247 lines
7.2 KiB
C
247 lines
7.2 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* 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-2023, 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;
|
|
}
|
|
}
|