/* * 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; } }
/* * 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))); }
void ginNewScanKey(IndexScanDesc scan) { ScanKey scankey = scan->keyData; GinScanOpaque so = (GinScanOpaque) scan->opaque; int i; bool hasNullQuery = false; MemoryContext oldCtx; /* * Allocate all the scan key information in the key context. (If * extractQuery leaks anything there, it won't be reset until the end of * scan or rescan, but that's OK.) */ oldCtx = MemoryContextSwitchTo(so->keyCtx); /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */ so->keys = (GinScanKey) palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData)); so->nkeys = 0; /* initialize expansible array of GinScanEntry pointers */ so->totalentries = 0; so->allocentries = 32; so->entries = (GinScanEntry *) palloc(so->allocentries * sizeof(GinScanEntry)); so->isVoidRes = false; for (i = 0; i < scan->numberOfKeys; i++) { ScanKey skey = &scankey[i]; Datum *queryValues; int32 nQueryValues = 0; bool *partial_matches = NULL; Pointer *extra_data = NULL; bool *nullFlags = NULL; int32 searchMode = GIN_SEARCH_MODE_DEFAULT; /* * We assume that GIN-indexable operators are strict, so a null query * argument means an unsatisfiable query. */ if (skey->sk_flags & SK_ISNULL) { so->isVoidRes = true; break; } /* OK to call the extractQueryFn */ queryValues = (Datum *) DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1], so->ginstate.supportCollation[skey->sk_attno - 1], skey->sk_argument, PointerGetDatum(&nQueryValues), UInt16GetDatum(skey->sk_strategy), PointerGetDatum(&partial_matches), PointerGetDatum(&extra_data), PointerGetDatum(&nullFlags), PointerGetDatum(&searchMode))); /* * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note * in particular we don't allow extractQueryFn to select * GIN_SEARCH_MODE_EVERYTHING. */ if (searchMode < GIN_SEARCH_MODE_DEFAULT || searchMode > GIN_SEARCH_MODE_ALL) searchMode = GIN_SEARCH_MODE_ALL; /* Non-default modes require the index to have placeholders */ if (searchMode != GIN_SEARCH_MODE_DEFAULT) hasNullQuery = true; /* * In default mode, no keys means an unsatisfiable query. */ if (queryValues == NULL || nQueryValues <= 0) { if (searchMode == GIN_SEARCH_MODE_DEFAULT) { so->isVoidRes = true; break; } nQueryValues = 0; /* ensure sane value */ } /* * If the extractQueryFn didn't create a nullFlags array, create one, * assuming that everything's non-null. Otherwise, run through the * array and make sure each value is exactly 0 or 1; this ensures * binary compatibility with the GinNullCategory representation. While * at it, detect whether any null keys are present. */ if (nullFlags == NULL) nullFlags = (bool *) palloc0(nQueryValues * sizeof(bool)); else { int32 j; for (j = 0; j < nQueryValues; j++) { if (nullFlags[j]) { nullFlags[j] = true; /* not any other nonzero value */ hasNullQuery = true; } } } /* now we can use the nullFlags as category codes */ ginFillScanKey(so, skey->sk_attno, skey->sk_strategy, searchMode, skey->sk_argument, nQueryValues, queryValues, (GinNullCategory *) nullFlags, partial_matches, extra_data); } /* * If there are no regular scan keys, generate an EVERYTHING scankey to * drive a full-index scan. */ if (so->nkeys == 0 && !so->isVoidRes) { hasNullQuery = true; ginFillScanKey(so, FirstOffsetNumber, InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING, (Datum) 0, 0, NULL, NULL, NULL, NULL); } /* * If the index is version 0, it may be missing null and placeholder * entries, which would render searches for nulls and full-index scans * unreliable. Throw an error if so. */ if (hasNullQuery && !so->isVoidRes) { GinStatsData ginStats; ginGetStats(scan->indexRelation, &ginStats); if (ginStats.ginVersion < 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"), errhint("To fix this, do REINDEX INDEX \"%s\".", RelationGetRelationName(scan->indexRelation)))); } MemoryContextSwitchTo(oldCtx); pgstat_count_index_scan(scan->indexRelation); }