1 /*-------------------------------------------------------------------------
4 * routines to manage scans of inverted index relations
7 * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/gin/ginscan.c
12 *-------------------------------------------------------------------------
17 #include "access/gin_private.h"
18 #include "access/relscan.h"
20 #include "utils/memutils.h"
21 #include "utils/rel.h"
25 ginbeginscan(PG_FUNCTION_ARGS)
27 Relation rel = (Relation) PG_GETARG_POINTER(0);
28 int nkeys = PG_GETARG_INT32(1);
29 int norderbys = PG_GETARG_INT32(2);
33 /* no order by operators allowed */
34 Assert(norderbys == 0);
36 scan = RelationGetIndexScan(rel, nkeys, norderbys);
38 /* allocate private workspace */
39 so = (GinScanOpaque) palloc(sizeof(GinScanOpaqueData));
42 so->tempCtx = AllocSetContextCreate(CurrentMemoryContext,
43 "Gin scan temporary context",
44 ALLOCSET_DEFAULT_MINSIZE,
45 ALLOCSET_DEFAULT_INITSIZE,
46 ALLOCSET_DEFAULT_MAXSIZE);
47 so->keyCtx = AllocSetContextCreate(CurrentMemoryContext,
48 "Gin scan key context",
49 ALLOCSET_DEFAULT_MINSIZE,
50 ALLOCSET_DEFAULT_INITSIZE,
51 ALLOCSET_DEFAULT_MAXSIZE);
52 initGinState(&so->ginstate, scan->indexRelation);
56 PG_RETURN_POINTER(scan);
60 * Create a new GinScanEntry, unless an equivalent one already exists,
61 * in which case just return it
64 ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum,
65 StrategyNumber strategy, int32 searchMode,
66 Datum queryKey, GinNullCategory queryCategory,
67 bool isPartialMatch, Pointer extra_data)
69 GinState *ginstate = &so->ginstate;
70 GinScanEntry scanEntry;
74 * Look for an existing equivalent entry.
76 * Entries with non-null extra_data are never considered identical, since
77 * we can't know exactly what the opclass might be doing with that.
79 if (extra_data == NULL)
81 for (i = 0; i < so->totalentries; i++)
83 GinScanEntry prevEntry = so->entries[i];
85 if (prevEntry->extra_data == NULL &&
86 prevEntry->isPartialMatch == isPartialMatch &&
87 prevEntry->strategy == strategy &&
88 prevEntry->searchMode == searchMode &&
89 prevEntry->attnum == attnum &&
90 ginCompareEntries(ginstate, attnum,
92 prevEntry->queryCategory,
96 /* Successful match */
102 /* Nope, create a new entry */
103 scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData));
104 scanEntry->queryKey = queryKey;
105 scanEntry->queryCategory = queryCategory;
106 scanEntry->isPartialMatch = isPartialMatch;
107 scanEntry->extra_data = extra_data;
108 scanEntry->strategy = strategy;
109 scanEntry->searchMode = searchMode;
110 scanEntry->attnum = attnum;
112 scanEntry->buffer = InvalidBuffer;
113 ItemPointerSetMin(&scanEntry->curItem);
114 scanEntry->matchBitmap = NULL;
115 scanEntry->matchIterator = NULL;
116 scanEntry->matchResult = NULL;
117 scanEntry->list = NULL;
118 scanEntry->nlist = 0;
119 scanEntry->offset = InvalidOffsetNumber;
120 scanEntry->isFinished = false;
121 scanEntry->reduceResult = false;
123 /* Add it to so's array */
124 if (so->totalentries >= so->allocentries)
126 so->allocentries *= 2;
127 so->entries = (GinScanEntry *)
128 repalloc(so->entries, so->allocentries * sizeof(GinScanEntry));
130 so->entries[so->totalentries++] = scanEntry;
136 * Initialize the next GinScanKey using the output from the extractQueryFn
139 ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
140 StrategyNumber strategy, int32 searchMode,
141 Datum query, uint32 nQueryValues,
142 Datum *queryValues, GinNullCategory *queryCategories,
143 bool *partial_matches, Pointer *extra_data)
145 GinScanKey key = &(so->keys[so->nkeys++]);
146 GinState *ginstate = &so->ginstate;
147 uint32 nUserQueryValues = nQueryValues;
150 /* Non-default search modes add one "hidden" entry to each key */
151 if (searchMode != GIN_SEARCH_MODE_DEFAULT)
153 key->nentries = nQueryValues;
154 key->nuserentries = nUserQueryValues;
156 key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * nQueryValues);
157 key->entryRes = (bool *) palloc0(sizeof(bool) * nQueryValues);
160 key->queryValues = queryValues;
161 key->queryCategories = queryCategories;
162 key->extra_data = extra_data;
163 key->strategy = strategy;
164 key->searchMode = searchMode;
165 key->attnum = attnum;
167 ItemPointerSetMin(&key->curItem);
168 key->curItemMatches = false;
169 key->recheckCurItem = false;
170 key->isFinished = false;
172 key->nadditional = 0;
173 key->requiredEntries = NULL;
174 key->additionalEntries = NULL;
176 ginInitConsistentFunction(ginstate, key);
178 for (i = 0; i < nQueryValues; i++)
181 GinNullCategory queryCategory;
185 if (i < nUserQueryValues)
187 /* set up normal entry using extractQueryFn's outputs */
188 queryKey = queryValues[i];
189 queryCategory = queryCategories[i];
191 (ginstate->canPartialMatch[attnum - 1] && partial_matches)
192 ? partial_matches[i] : false;
193 this_extra = (extra_data) ? extra_data[i] : NULL;
197 /* set up hidden entry */
198 queryKey = (Datum) 0;
201 case GIN_SEARCH_MODE_INCLUDE_EMPTY:
202 queryCategory = GIN_CAT_EMPTY_ITEM;
204 case GIN_SEARCH_MODE_ALL:
205 queryCategory = GIN_CAT_EMPTY_QUERY;
207 case GIN_SEARCH_MODE_EVERYTHING:
208 queryCategory = GIN_CAT_EMPTY_QUERY;
211 elog(ERROR, "unexpected searchMode: %d", searchMode);
212 queryCategory = 0; /* keep compiler quiet */
215 isPartialMatch = false;
219 * We set the strategy to a fixed value so that ginFillScanEntry
220 * can combine these entries for different scan keys. This is
221 * safe because the strategy value in the entry struct is only
222 * used for partial-match cases. It's OK to overwrite our local
223 * variable here because this is the last loop iteration.
225 strategy = InvalidStrategy;
228 key->scanEntry[i] = ginFillScanEntry(so, attnum,
229 strategy, searchMode,
230 queryKey, queryCategory,
231 isPartialMatch, this_extra);
236 * Release current scan keys, if any.
239 ginFreeScanKeys(GinScanOpaque so)
243 if (so->keys == NULL)
246 for (i = 0; i < so->totalentries; i++)
248 GinScanEntry entry = so->entries[i];
250 if (entry->buffer != InvalidBuffer)
251 ReleaseBuffer(entry->buffer);
252 if (entry->matchIterator)
253 tbm_end_iterate(entry->matchIterator);
254 if (entry->matchBitmap)
255 tbm_free(entry->matchBitmap);
258 MemoryContextResetAndDeleteChildren(so->keyCtx);
263 so->totalentries = 0;
267 ginNewScanKey(IndexScanDesc scan)
269 ScanKey scankey = scan->keyData;
270 GinScanOpaque so = (GinScanOpaque) scan->opaque;
272 bool hasNullQuery = false;
273 MemoryContext oldCtx;
276 * Allocate all the scan key information in the key context. (If
277 * extractQuery leaks anything there, it won't be reset until the end of
278 * scan or rescan, but that's OK.)
280 oldCtx = MemoryContextSwitchTo(so->keyCtx);
282 /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
283 so->keys = (GinScanKey)
284 palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
287 /* initialize expansible array of GinScanEntry pointers */
288 so->totalentries = 0;
289 so->allocentries = 32;
290 so->entries = (GinScanEntry *)
291 palloc0(so->allocentries * sizeof(GinScanEntry));
293 so->isVoidRes = false;
295 for (i = 0; i < scan->numberOfKeys; i++)
297 ScanKey skey = &scankey[i];
299 int32 nQueryValues = 0;
300 bool *partial_matches = NULL;
301 Pointer *extra_data = NULL;
302 bool *nullFlags = NULL;
303 int32 searchMode = GIN_SEARCH_MODE_DEFAULT;
306 * We assume that GIN-indexable operators are strict, so a null query
307 * argument means an unsatisfiable query.
309 if (skey->sk_flags & SK_ISNULL)
311 so->isVoidRes = true;
315 /* OK to call the extractQueryFn */
316 queryValues = (Datum *)
317 DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
318 so->ginstate.supportCollation[skey->sk_attno - 1],
320 PointerGetDatum(&nQueryValues),
321 UInt16GetDatum(skey->sk_strategy),
322 PointerGetDatum(&partial_matches),
323 PointerGetDatum(&extra_data),
324 PointerGetDatum(&nullFlags),
325 PointerGetDatum(&searchMode)));
328 * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
329 * in particular we don't allow extractQueryFn to select
330 * GIN_SEARCH_MODE_EVERYTHING.
332 if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
333 searchMode > GIN_SEARCH_MODE_ALL)
334 searchMode = GIN_SEARCH_MODE_ALL;
336 /* Non-default modes require the index to have placeholders */
337 if (searchMode != GIN_SEARCH_MODE_DEFAULT)
341 * In default mode, no keys means an unsatisfiable query.
343 if (queryValues == NULL || nQueryValues <= 0)
345 if (searchMode == GIN_SEARCH_MODE_DEFAULT)
347 so->isVoidRes = true;
350 nQueryValues = 0; /* ensure sane value */
354 * If the extractQueryFn didn't create a nullFlags array, create one,
355 * assuming that everything's non-null. Otherwise, run through the
356 * array and make sure each value is exactly 0 or 1; this ensures
357 * binary compatibility with the GinNullCategory representation. While
358 * at it, detect whether any null keys are present.
360 if (nullFlags == NULL)
361 nullFlags = (bool *) palloc0(nQueryValues * sizeof(bool));
366 for (j = 0; j < nQueryValues; j++)
370 nullFlags[j] = true; /* not any other nonzero value */
375 /* now we can use the nullFlags as category codes */
377 ginFillScanKey(so, skey->sk_attno,
378 skey->sk_strategy, searchMode,
379 skey->sk_argument, nQueryValues,
380 queryValues, (GinNullCategory *) nullFlags,
381 partial_matches, extra_data);
385 * If there are no regular scan keys, generate an EVERYTHING scankey to
386 * drive a full-index scan.
388 if (so->nkeys == 0 && !so->isVoidRes)
391 ginFillScanKey(so, FirstOffsetNumber,
392 InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
394 NULL, NULL, NULL, NULL);
398 * If the index is version 0, it may be missing null and placeholder
399 * entries, which would render searches for nulls and full-index scans
400 * unreliable. Throw an error if so.
402 if (hasNullQuery && !so->isVoidRes)
404 GinStatsData ginStats;
406 ginGetStats(scan->indexRelation, &ginStats);
407 if (ginStats.ginVersion < 1)
409 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
410 errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
411 errhint("To fix this, do REINDEX INDEX \"%s\".",
412 RelationGetRelationName(scan->indexRelation))));
415 MemoryContextSwitchTo(oldCtx);
417 pgstat_count_index_scan(scan->indexRelation);
421 ginrescan(PG_FUNCTION_ARGS)
423 IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
424 ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
426 /* remaining arguments are ignored */
427 GinScanOpaque so = (GinScanOpaque) scan->opaque;
431 if (scankey && scan->numberOfKeys > 0)
433 memmove(scan->keyData, scankey,
434 scan->numberOfKeys * sizeof(ScanKeyData));
442 ginendscan(PG_FUNCTION_ARGS)
444 IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
445 GinScanOpaque so = (GinScanOpaque) scan->opaque;
449 MemoryContextDelete(so->tempCtx);
450 MemoryContextDelete(so->keyCtx);
458 ginmarkpos(PG_FUNCTION_ARGS)
460 elog(ERROR, "GIN does not support mark/restore");
465 ginrestrpos(PG_FUNCTION_ARGS)
467 elog(ERROR, "GIN does not support mark/restore");