PostgreSQL Source Code git master
Loading...
Searching...
No Matches
ginscan.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * ginscan.c
4 * routines to manage scans of inverted index relations
5 *
6 *
7 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * IDENTIFICATION
11 * src/backend/access/gin/ginscan.c
12 *-------------------------------------------------------------------------
13 */
14
15#include "postgres.h"
16
17#include "access/gin_private.h"
18#include "access/relscan.h"
20#include "pgstat.h"
21#include "utils/memutils.h"
22#include "utils/rel.h"
23
24
26ginbeginscan(Relation rel, int nkeys, int norderbys)
27{
28 IndexScanDesc scan;
30
31 /* no order by operators allowed */
32 Assert(norderbys == 0);
33
34 scan = RelationGetIndexScan(rel, nkeys, norderbys);
35
36 /* allocate private workspace */
38 so->keys = NULL;
39 so->nkeys = 0;
41 "Gin scan temporary context",
44 "Gin scan key context",
46 initGinState(&so->ginstate, scan->indexRelation);
47
48 scan->opaque = so;
49
50 return scan;
51}
52
53/*
54 * Create a new GinScanEntry, unless an equivalent one already exists,
55 * in which case just return it
56 */
57static GinScanEntry
59 StrategyNumber strategy, int32 searchMode,
60 Datum queryKey, GinNullCategory queryCategory,
61 bool isPartialMatch, Pointer extra_data)
62{
63 GinState *ginstate = &so->ginstate;
64 GinScanEntry scanEntry;
65 uint32 i;
66
67 /*
68 * Look for an existing equivalent entry.
69 *
70 * Entries with non-null extra_data are never considered identical, since
71 * we can't know exactly what the opclass might be doing with that.
72 *
73 * Also, give up de-duplication once we have 100 entries. That avoids
74 * spending O(N^2) time on probably-fruitless de-duplication of large
75 * search-key sets. The threshold of 100 is arbitrary but matches
76 * predtest.c's threshold for what's a large array.
77 */
78 if (extra_data == NULL && so->totalentries < 100)
79 {
80 for (i = 0; i < so->totalentries; i++)
81 {
82 GinScanEntry prevEntry = so->entries[i];
83
84 if (prevEntry->extra_data == NULL &&
85 prevEntry->isPartialMatch == isPartialMatch &&
86 prevEntry->strategy == strategy &&
87 prevEntry->searchMode == searchMode &&
88 prevEntry->attnum == attnum &&
89 ginCompareEntries(ginstate, attnum,
90 prevEntry->queryKey,
91 prevEntry->queryCategory,
92 queryKey,
93 queryCategory) == 0)
94 {
95 /* Successful match */
96 return prevEntry;
97 }
98 }
99 }
100
101 /* Nope, create a new entry */
102 scanEntry = palloc_object(GinScanEntryData);
103 scanEntry->queryKey = queryKey;
104 scanEntry->queryCategory = queryCategory;
105 scanEntry->isPartialMatch = isPartialMatch;
106 scanEntry->extra_data = extra_data;
107 scanEntry->strategy = strategy;
108 scanEntry->searchMode = searchMode;
109 scanEntry->attnum = attnum;
110
111 scanEntry->buffer = InvalidBuffer;
112 ItemPointerSetMin(&scanEntry->curItem);
113 scanEntry->matchBitmap = NULL;
114 scanEntry->matchIterator = NULL;
116 scanEntry->matchNtuples = -1;
117 scanEntry->list = NULL;
118 scanEntry->nlist = 0;
119 scanEntry->offset = InvalidOffsetNumber;
120 scanEntry->isFinished = false;
121 scanEntry->reduceResult = false;
122
123 /* Add it to so's array */
124 if (so->totalentries >= so->allocentries)
125 {
126 so->allocentries *= 2;
127 so->entries = repalloc_array(so->entries, GinScanEntry, so->allocentries);
128 }
129 so->entries[so->totalentries++] = scanEntry;
130
131 return scanEntry;
132}
133
134/*
135 * Append hidden scan entry of given category to the scan key.
136 *
137 * NB: this had better be called at most once per scan key, since
138 * ginFillScanKey leaves room for only one hidden entry. Currently,
139 * it seems sufficiently clear that this is true that we don't bother
140 * with any cross-check logic.
141 */
142static void
144 GinNullCategory queryCategory)
145{
146 int i = key->nentries++;
147
148 /* strategy is of no interest because this is not a partial-match item */
149 key->scanEntry[i] = ginFillScanEntry(so, key->attnum,
150 InvalidStrategy, key->searchMode,
151 (Datum) 0, queryCategory,
152 false, NULL);
153}
154
155/*
156 * Initialize the next GinScanKey using the output from the extractQueryFn
157 */
158static void
160 StrategyNumber strategy, int32 searchMode,
162 Datum *queryValues, GinNullCategory *queryCategories,
163 bool *partial_matches, Pointer *extra_data)
164{
165 GinScanKey key = &(so->keys[so->nkeys++]);
166 GinState *ginstate = &so->ginstate;
167 uint32 i;
168
169 key->nentries = nQueryValues;
170 key->nuserentries = nQueryValues;
171
172 /* Allocate one extra array slot for possible "hidden" entry */
173 key->scanEntry = palloc_array(GinScanEntry, nQueryValues + 1);
174 key->entryRes = palloc0_array(GinTernaryValue, nQueryValues + 1);
175
176 key->query = query;
177 key->queryValues = queryValues;
178 key->queryCategories = queryCategories;
179 key->extra_data = extra_data;
180 key->strategy = strategy;
181 key->searchMode = searchMode;
182 key->attnum = attnum;
183
184 /*
185 * Initially, scan keys of GIN_SEARCH_MODE_ALL mode are marked
186 * excludeOnly. This might get changed later.
187 */
188 key->excludeOnly = (searchMode == GIN_SEARCH_MODE_ALL);
189
190 ItemPointerSetMin(&key->curItem);
191 key->curItemMatches = false;
192 key->recheckCurItem = false;
193 key->isFinished = false;
194 key->nrequired = 0;
195 key->nadditional = 0;
196 key->requiredEntries = NULL;
197 key->additionalEntries = NULL;
198
199 ginInitConsistentFunction(ginstate, key);
200
201 /* Set up normal scan entries using extractQueryFn's outputs */
202 for (i = 0; i < nQueryValues; i++)
203 {
204 Datum queryKey;
205 GinNullCategory queryCategory;
206 bool isPartialMatch;
208
209 queryKey = queryValues[i];
210 queryCategory = queryCategories[i];
211 isPartialMatch =
212 (ginstate->canPartialMatch[attnum - 1] && partial_matches)
213 ? partial_matches[i] : false;
214 this_extra = (extra_data) ? extra_data[i] : NULL;
215
216 key->scanEntry[i] = ginFillScanEntry(so, attnum,
217 strategy, searchMode,
218 queryKey, queryCategory,
219 isPartialMatch, this_extra);
220 }
221
222 /*
223 * For GIN_SEARCH_MODE_INCLUDE_EMPTY and GIN_SEARCH_MODE_EVERYTHING search
224 * modes, we add the "hidden" entry immediately. GIN_SEARCH_MODE_ALL is
225 * handled later, since we might be able to omit the hidden entry for it.
226 */
227 if (searchMode == GIN_SEARCH_MODE_INCLUDE_EMPTY)
229 else if (searchMode == GIN_SEARCH_MODE_EVERYTHING)
231}
232
233/*
234 * Release current scan keys, if any.
235 */
236void
238{
239 uint32 i;
240
241 if (so->keys == NULL)
242 return;
243
244 for (i = 0; i < so->totalentries; i++)
245 {
246 GinScanEntry entry = so->entries[i];
247
248 if (entry->buffer != InvalidBuffer)
249 ReleaseBuffer(entry->buffer);
250 if (entry->list)
251 pfree(entry->list);
252 if (entry->matchIterator)
254 if (entry->matchBitmap)
255 tbm_free(entry->matchBitmap);
256 }
257
258 MemoryContextReset(so->keyCtx);
259
260 so->keys = NULL;
261 so->nkeys = 0;
262 so->entries = NULL;
263 so->totalentries = 0;
264}
265
266void
268{
269 ScanKey scankey = scan->keyData;
271 int i;
272 int numExcludeOnly;
273 bool hasNullQuery = false;
274 bool attrHasNormalScan[INDEX_MAX_KEYS] = {false};
276
277 /*
278 * Allocate all the scan key information in the key context. (If
279 * extractQuery leaks anything there, it won't be reset until the end of
280 * scan or rescan, but that's OK.)
281 */
283
284 /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
285 so->keys = (GinScanKey)
286 palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
287 so->nkeys = 0;
288
289 /* initialize expansible array of GinScanEntry pointers */
290 so->totalentries = 0;
291 so->allocentries = 32;
292 so->entries = (GinScanEntry *)
293 palloc(so->allocentries * sizeof(GinScanEntry));
294
295 so->isVoidRes = false;
296
297 for (i = 0; i < scan->numberOfKeys; i++)
298 {
299 ScanKey skey = &scankey[i];
300 Datum *queryValues;
302 bool *partial_matches = NULL;
303 Pointer *extra_data = NULL;
304 bool *nullFlags = NULL;
305 GinNullCategory *categories;
306 int32 searchMode = GIN_SEARCH_MODE_DEFAULT;
307
308 /*
309 * We assume that GIN-indexable operators are strict, so a null query
310 * argument means an unsatisfiable query.
311 */
312 if (skey->sk_flags & SK_ISNULL)
313 {
314 so->isVoidRes = true;
315 break;
316 }
317
318 /* OK to call the extractQueryFn */
319 queryValues = (Datum *)
320 DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
321 so->ginstate.supportCollation[skey->sk_attno - 1],
322 skey->sk_argument,
324 UInt16GetDatum(skey->sk_strategy),
326 PointerGetDatum(&extra_data),
328 PointerGetDatum(&searchMode)));
329
330 /*
331 * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
332 * in particular we don't allow extractQueryFn to select
333 * GIN_SEARCH_MODE_EVERYTHING.
334 */
335 if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
336 searchMode > GIN_SEARCH_MODE_ALL)
337 searchMode = GIN_SEARCH_MODE_ALL;
338
339 /* Non-default modes require the index to have placeholders */
340 if (searchMode != GIN_SEARCH_MODE_DEFAULT)
341 hasNullQuery = true;
342
343 /*
344 * In default mode, no keys means an unsatisfiable query.
345 */
346 if (queryValues == NULL || nQueryValues <= 0)
347 {
348 if (searchMode == GIN_SEARCH_MODE_DEFAULT)
349 {
350 so->isVoidRes = true;
351 break;
352 }
353 nQueryValues = 0; /* ensure sane value */
354 }
355
356 /*
357 * Create GinNullCategory representation. If the extractQueryFn
358 * didn't create a nullFlags array, we assume everything is non-null.
359 * While at it, detect whether any null keys are present.
360 */
361 categories = (GinNullCategory *) palloc0(nQueryValues * sizeof(GinNullCategory));
362 if (nullFlags)
363 {
364 int32 j;
365
366 for (j = 0; j < nQueryValues; j++)
367 {
368 if (nullFlags[j])
369 {
370 categories[j] = GIN_CAT_NULL_KEY;
371 hasNullQuery = true;
372 }
373 }
374 }
375
376 ginFillScanKey(so, skey->sk_attno,
377 skey->sk_strategy, searchMode,
378 skey->sk_argument, nQueryValues,
379 queryValues, categories,
380 partial_matches, extra_data);
381
382 /* Remember if we had any non-excludeOnly keys */
383 if (searchMode != GIN_SEARCH_MODE_ALL)
384 attrHasNormalScan[skey->sk_attno - 1] = true;
385 }
386
387 /*
388 * Processing GIN_SEARCH_MODE_ALL scan keys requires us to make a second
389 * pass over the scan keys. Above we marked each such scan key as
390 * excludeOnly. If the involved column has any normal (not excludeOnly)
391 * scan key as well, then we can leave it like that. Otherwise, one
392 * excludeOnly scan key must receive a GIN_CAT_EMPTY_QUERY hidden entry
393 * and be set to normal (excludeOnly = false).
394 */
395 numExcludeOnly = 0;
396 for (i = 0; i < so->nkeys; i++)
397 {
398 GinScanKey key = &so->keys[i];
399
400 if (key->searchMode != GIN_SEARCH_MODE_ALL)
401 continue;
402
403 if (!attrHasNormalScan[key->attnum - 1])
404 {
405 key->excludeOnly = false;
407 attrHasNormalScan[key->attnum - 1] = true;
408 }
409 else
411 }
412
413 /*
414 * If we left any excludeOnly scan keys as-is, move them to the end of the
415 * scan key array: they must appear after normal key(s).
416 */
417 if (numExcludeOnly > 0)
418 {
420 int iNormalKey;
421 int iExcludeOnly;
422
423 /* We'd better have made at least one normal key */
425 /* Make a temporary array to hold the re-ordered scan keys */
426 tmpkeys = (GinScanKey) palloc(so->nkeys * sizeof(GinScanKeyData));
427 /* Re-order the keys ... */
428 iNormalKey = 0;
429 iExcludeOnly = so->nkeys - numExcludeOnly;
430 for (i = 0; i < so->nkeys; i++)
431 {
432 GinScanKey key = &so->keys[i];
433
434 if (key->excludeOnly)
435 {
436 memcpy(tmpkeys + iExcludeOnly, key, sizeof(GinScanKeyData));
437 iExcludeOnly++;
438 }
439 else
440 {
441 memcpy(tmpkeys + iNormalKey, key, sizeof(GinScanKeyData));
442 iNormalKey++;
443 }
444 }
445 Assert(iNormalKey == so->nkeys - numExcludeOnly);
446 Assert(iExcludeOnly == so->nkeys);
447 /* ... and copy them back to so->keys[] */
448 memcpy(so->keys, tmpkeys, so->nkeys * sizeof(GinScanKeyData));
449 pfree(tmpkeys);
450 }
451
452 /*
453 * If there are no regular scan keys, generate an EVERYTHING scankey to
454 * drive a full-index scan.
455 */
456 if (so->nkeys == 0 && !so->isVoidRes)
457 {
458 hasNullQuery = true;
461 (Datum) 0, 0,
462 NULL, NULL, NULL, NULL);
463 }
464
465 /*
466 * If the index is version 0, it may be missing null and placeholder
467 * entries, which would render searches for nulls and full-index scans
468 * unreliable. Throw an error if so.
469 */
470 if (hasNullQuery && !so->isVoidRes)
471 {
473
475 if (ginStats.ginVersion < 1)
478 errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
479 errhint("To fix this, do REINDEX INDEX \"%s\".",
481 }
482
484
486 if (scan->instrument)
487 scan->instrument->nsearches++;
488}
489
490void
492 ScanKey orderbys, int norderbys)
493{
495
497
498 if (scankey && scan->numberOfKeys > 0)
499 memcpy(scan->keyData, scankey, scan->numberOfKeys * sizeof(ScanKeyData));
500}
501
502
503void
505{
507
509
510 MemoryContextDelete(so->tempCtx);
511 MemoryContextDelete(so->keyCtx);
512
513 pfree(so);
514}
#define InvalidBlockNumber
Definition block.h:33
#define InvalidBuffer
Definition buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5501
#define Max(x, y)
Definition c.h:991
#define Assert(condition)
Definition c.h:873
int32_t int32
Definition c.h:542
uint32_t uint32
Definition c.h:546
void * Pointer
Definition c.h:537
int errhint(const char *fmt,...)
Definition elog.c:1330
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:150
#define palloc_object(type)
Definition fe_memutils.h:74
#define repalloc_array(pointer, type, count)
Definition fe_memutils.h:78
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define palloc0_array(type, count)
Definition fe_memutils.h:77
Datum FunctionCall7Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5, Datum arg6, Datum arg7)
Definition fmgr.c:1285
IndexScanDesc RelationGetIndexScan(Relation indexRelation, int nkeys, int norderbys)
Definition genam.c:80
#define GIN_SEARCH_MODE_ALL
Definition gin.h:38
#define GIN_SEARCH_MODE_EVERYTHING
Definition gin.h:39
#define GIN_SEARCH_MODE_DEFAULT
Definition gin.h:36
char GinTernaryValue
Definition gin.h:71
#define GIN_SEARCH_MODE_INCLUDE_EMPTY
Definition gin.h:37
GinScanOpaqueData * GinScanOpaque
static int ginCompareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, GinNullCategory categorya, Datum b, GinNullCategory categoryb)
struct GinScanKeyData * GinScanKey
#define GIN_CAT_EMPTY_ITEM
Definition ginblock.h:210
signed char GinNullCategory
Definition ginblock.h:206
#define GIN_CAT_NULL_KEY
Definition ginblock.h:209
#define ItemPointerSetMin(p)
Definition ginblock.h:166
#define GIN_CAT_EMPTY_QUERY
Definition ginblock.h:212
void ginInitConsistentFunction(GinState *ginstate, GinScanKey key)
Definition ginlogic.c:227
IndexScanDesc ginbeginscan(Relation rel, int nkeys, int norderbys)
Definition ginscan.c:26
void ginFreeScanKeys(GinScanOpaque so)
Definition ginscan.c:237
static void ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, StrategyNumber strategy, int32 searchMode, Datum query, uint32 nQueryValues, Datum *queryValues, GinNullCategory *queryCategories, bool *partial_matches, Pointer *extra_data)
Definition ginscan.c:159
void ginendscan(IndexScanDesc scan)
Definition ginscan.c:504
void ginNewScanKey(IndexScanDesc scan)
Definition ginscan.c:267
void ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, ScanKey orderbys, int norderbys)
Definition ginscan.c:491
static void ginScanKeyAddHiddenEntry(GinScanOpaque so, GinScanKey key, GinNullCategory queryCategory)
Definition ginscan.c:143
static GinScanEntry ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum, StrategyNumber strategy, int32 searchMode, Datum queryKey, GinNullCategory queryCategory, bool isPartialMatch, Pointer extra_data)
Definition ginscan.c:58
void ginGetStats(Relation index, GinStatsData *stats)
Definition ginutil.c:591
void initGinState(GinState *state, Relation index)
Definition ginutil.c:103
int j
Definition isn.c:78
int i
Definition isn.c:77
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:403
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc0(Size size)
Definition mcxt.c:1417
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define InvalidOffsetNumber
Definition off.h:26
uint16 OffsetNumber
Definition off.h:24
#define FirstOffsetNumber
Definition off.h:27
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
int16 attnum
#define INDEX_MAX_KEYS
#define pgstat_count_index_scan(rel)
Definition pgstat.h:705
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
static Datum UInt16GetDatum(uint16 X)
Definition postgres.h:202
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:342
static int fb(int x)
#define RelationGetRelationName(relation)
Definition rel.h:548
#define SK_ISNULL
Definition skey.h:115
uint16 StrategyNumber
Definition stratnum.h:22
#define InvalidStrategy
Definition stratnum.h:24
ItemPointerData curItem
TBMIterateResult matchResult
TIDBitmap * matchBitmap
ItemPointerData * list
TBMPrivateIterator * matchIterator
GinNullCategory queryCategory
StrategyNumber strategy
OffsetNumber offset
OffsetNumber attnum
bool canPartialMatch[INDEX_MAX_KEYS]
Definition gin_private.h:87
struct ScanKeyData * keyData
Definition relscan.h:142
struct IndexScanInstrumentation * instrument
Definition relscan.h:160
Relation indexRelation
Definition relscan.h:138
BlockNumber blockno
Definition tidbitmap.h:63
void tbm_free(TIDBitmap *tbm)
Definition tidbitmap.c:312
void tbm_end_private_iterate(TBMPrivateIterator *iterator)
Definition tidbitmap.c:1147