Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginscan.c
4 : * routines to manage scans of inverted index relations
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2017, 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"
19 : #include "pgstat.h"
20 : #include "utils/memutils.h"
21 : #include "utils/rel.h"
22 :
23 :
24 : IndexScanDesc
25 73 : ginbeginscan(Relation rel, int nkeys, int norderbys)
26 : {
27 : IndexScanDesc scan;
28 : GinScanOpaque so;
29 :
30 : /* no order by operators allowed */
31 73 : Assert(norderbys == 0);
32 :
33 73 : scan = RelationGetIndexScan(rel, nkeys, norderbys);
34 :
35 : /* allocate private workspace */
36 73 : so = (GinScanOpaque) palloc(sizeof(GinScanOpaqueData));
37 73 : so->keys = NULL;
38 73 : so->nkeys = 0;
39 73 : so->tempCtx = AllocSetContextCreate(CurrentMemoryContext,
40 : "Gin scan temporary context",
41 : ALLOCSET_DEFAULT_SIZES);
42 73 : so->keyCtx = AllocSetContextCreate(CurrentMemoryContext,
43 : "Gin scan key context",
44 : ALLOCSET_DEFAULT_SIZES);
45 73 : initGinState(&so->ginstate, scan->indexRelation);
46 :
47 73 : scan->opaque = so;
48 :
49 73 : return scan;
50 : }
51 :
52 : /*
53 : * Create a new GinScanEntry, unless an equivalent one already exists,
54 : * in which case just return it
55 : */
56 : static GinScanEntry
57 119 : ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum,
58 : StrategyNumber strategy, int32 searchMode,
59 : Datum queryKey, GinNullCategory queryCategory,
60 : bool isPartialMatch, Pointer extra_data)
61 : {
62 119 : GinState *ginstate = &so->ginstate;
63 : GinScanEntry scanEntry;
64 : uint32 i;
65 :
66 : /*
67 : * Look for an existing equivalent entry.
68 : *
69 : * Entries with non-null extra_data are never considered identical, since
70 : * we can't know exactly what the opclass might be doing with that.
71 : */
72 119 : if (extra_data == NULL)
73 : {
74 142 : for (i = 0; i < so->totalentries; i++)
75 : {
76 55 : GinScanEntry prevEntry = so->entries[i];
77 :
78 109 : if (prevEntry->extra_data == NULL &&
79 108 : prevEntry->isPartialMatch == isPartialMatch &&
80 96 : prevEntry->strategy == strategy &&
81 84 : prevEntry->searchMode == searchMode &&
82 84 : prevEntry->attnum == attnum &&
83 84 : ginCompareEntries(ginstate, attnum,
84 : prevEntry->queryKey,
85 42 : prevEntry->queryCategory,
86 : queryKey,
87 : queryCategory) == 0)
88 : {
89 : /* Successful match */
90 0 : return prevEntry;
91 : }
92 : }
93 : }
94 :
95 : /* Nope, create a new entry */
96 119 : scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData));
97 119 : scanEntry->queryKey = queryKey;
98 119 : scanEntry->queryCategory = queryCategory;
99 119 : scanEntry->isPartialMatch = isPartialMatch;
100 119 : scanEntry->extra_data = extra_data;
101 119 : scanEntry->strategy = strategy;
102 119 : scanEntry->searchMode = searchMode;
103 119 : scanEntry->attnum = attnum;
104 :
105 119 : scanEntry->buffer = InvalidBuffer;
106 119 : ItemPointerSetMin(&scanEntry->curItem);
107 119 : scanEntry->matchBitmap = NULL;
108 119 : scanEntry->matchIterator = NULL;
109 119 : scanEntry->matchResult = NULL;
110 119 : scanEntry->list = NULL;
111 119 : scanEntry->nlist = 0;
112 119 : scanEntry->offset = InvalidOffsetNumber;
113 119 : scanEntry->isFinished = false;
114 119 : scanEntry->reduceResult = false;
115 :
116 : /* Add it to so's array */
117 119 : if (so->totalentries >= so->allocentries)
118 : {
119 0 : so->allocentries *= 2;
120 0 : so->entries = (GinScanEntry *)
121 0 : repalloc(so->entries, so->allocentries * sizeof(GinScanEntry));
122 : }
123 119 : so->entries[so->totalentries++] = scanEntry;
124 :
125 119 : return scanEntry;
126 : }
127 :
128 : /*
129 : * Initialize the next GinScanKey using the output from the extractQueryFn
130 : */
131 : static void
132 74 : ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
133 : StrategyNumber strategy, int32 searchMode,
134 : Datum query, uint32 nQueryValues,
135 : Datum *queryValues, GinNullCategory *queryCategories,
136 : bool *partial_matches, Pointer *extra_data)
137 : {
138 74 : GinScanKey key = &(so->keys[so->nkeys++]);
139 74 : GinState *ginstate = &so->ginstate;
140 74 : uint32 nUserQueryValues = nQueryValues;
141 : uint32 i;
142 :
143 : /* Non-default search modes add one "hidden" entry to each key */
144 74 : if (searchMode != GIN_SEARCH_MODE_DEFAULT)
145 12 : nQueryValues++;
146 74 : key->nentries = nQueryValues;
147 74 : key->nuserentries = nUserQueryValues;
148 :
149 74 : key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * nQueryValues);
150 74 : key->entryRes = (GinTernaryValue *) palloc0(sizeof(GinTernaryValue) * nQueryValues);
151 :
152 74 : key->query = query;
153 74 : key->queryValues = queryValues;
154 74 : key->queryCategories = queryCategories;
155 74 : key->extra_data = extra_data;
156 74 : key->strategy = strategy;
157 74 : key->searchMode = searchMode;
158 74 : key->attnum = attnum;
159 :
160 74 : ItemPointerSetMin(&key->curItem);
161 74 : key->curItemMatches = false;
162 74 : key->recheckCurItem = false;
163 74 : key->isFinished = false;
164 74 : key->nrequired = 0;
165 74 : key->nadditional = 0;
166 74 : key->requiredEntries = NULL;
167 74 : key->additionalEntries = NULL;
168 :
169 74 : ginInitConsistentFunction(ginstate, key);
170 :
171 193 : for (i = 0; i < nQueryValues; i++)
172 : {
173 : Datum queryKey;
174 : GinNullCategory queryCategory;
175 : bool isPartialMatch;
176 : Pointer this_extra;
177 :
178 119 : if (i < nUserQueryValues)
179 : {
180 : /* set up normal entry using extractQueryFn's outputs */
181 107 : queryKey = queryValues[i];
182 107 : queryCategory = queryCategories[i];
183 139 : isPartialMatch =
184 139 : (ginstate->canPartialMatch[attnum - 1] && partial_matches)
185 32 : ? partial_matches[i] : false;
186 107 : this_extra = (extra_data) ? extra_data[i] : NULL;
187 : }
188 : else
189 : {
190 : /* set up hidden entry */
191 12 : queryKey = (Datum) 0;
192 12 : switch (searchMode)
193 : {
194 : case GIN_SEARCH_MODE_INCLUDE_EMPTY:
195 7 : queryCategory = GIN_CAT_EMPTY_ITEM;
196 7 : break;
197 : case GIN_SEARCH_MODE_ALL:
198 5 : queryCategory = GIN_CAT_EMPTY_QUERY;
199 5 : break;
200 : case GIN_SEARCH_MODE_EVERYTHING:
201 0 : queryCategory = GIN_CAT_EMPTY_QUERY;
202 0 : break;
203 : default:
204 0 : elog(ERROR, "unexpected searchMode: %d", searchMode);
205 : queryCategory = 0; /* keep compiler quiet */
206 : break;
207 : }
208 12 : isPartialMatch = false;
209 12 : this_extra = NULL;
210 :
211 : /*
212 : * We set the strategy to a fixed value so that ginFillScanEntry
213 : * can combine these entries for different scan keys. This is
214 : * safe because the strategy value in the entry struct is only
215 : * used for partial-match cases. It's OK to overwrite our local
216 : * variable here because this is the last loop iteration.
217 : */
218 12 : strategy = InvalidStrategy;
219 : }
220 :
221 119 : key->scanEntry[i] = ginFillScanEntry(so, attnum,
222 : strategy, searchMode,
223 : queryKey, queryCategory,
224 : isPartialMatch, this_extra);
225 : }
226 74 : }
227 :
228 : /*
229 : * Release current scan keys, if any.
230 : */
231 : void
232 221 : ginFreeScanKeys(GinScanOpaque so)
233 : {
234 : uint32 i;
235 :
236 221 : if (so->keys == NULL)
237 368 : return;
238 :
239 193 : for (i = 0; i < so->totalentries; i++)
240 : {
241 119 : GinScanEntry entry = so->entries[i];
242 :
243 119 : if (entry->buffer != InvalidBuffer)
244 0 : ReleaseBuffer(entry->buffer);
245 119 : if (entry->list)
246 101 : pfree(entry->list);
247 119 : if (entry->matchIterator)
248 0 : tbm_end_iterate(entry->matchIterator);
249 119 : if (entry->matchBitmap)
250 7 : tbm_free(entry->matchBitmap);
251 : }
252 :
253 74 : MemoryContextResetAndDeleteChildren(so->keyCtx);
254 :
255 74 : so->keys = NULL;
256 74 : so->nkeys = 0;
257 74 : so->entries = NULL;
258 74 : so->totalentries = 0;
259 : }
260 :
261 : void
262 74 : ginNewScanKey(IndexScanDesc scan)
263 : {
264 74 : ScanKey scankey = scan->keyData;
265 74 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
266 : int i;
267 74 : bool hasNullQuery = false;
268 : MemoryContext oldCtx;
269 :
270 : /*
271 : * Allocate all the scan key information in the key context. (If
272 : * extractQuery leaks anything there, it won't be reset until the end of
273 : * scan or rescan, but that's OK.)
274 : */
275 74 : oldCtx = MemoryContextSwitchTo(so->keyCtx);
276 :
277 : /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
278 74 : so->keys = (GinScanKey)
279 74 : palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
280 74 : so->nkeys = 0;
281 :
282 : /* initialize expansible array of GinScanEntry pointers */
283 74 : so->totalentries = 0;
284 74 : so->allocentries = 32;
285 74 : so->entries = (GinScanEntry *)
286 74 : palloc(so->allocentries * sizeof(GinScanEntry));
287 :
288 74 : so->isVoidRes = false;
289 :
290 296 : for (i = 0; i < scan->numberOfKeys; i++)
291 : {
292 76 : ScanKey skey = &scankey[i];
293 : Datum *queryValues;
294 76 : int32 nQueryValues = 0;
295 76 : bool *partial_matches = NULL;
296 76 : Pointer *extra_data = NULL;
297 76 : bool *nullFlags = NULL;
298 76 : int32 searchMode = GIN_SEARCH_MODE_DEFAULT;
299 :
300 : /*
301 : * We assume that GIN-indexable operators are strict, so a null query
302 : * argument means an unsatisfiable query.
303 : */
304 76 : if (skey->sk_flags & SK_ISNULL)
305 : {
306 0 : so->isVoidRes = true;
307 2 : break;
308 : }
309 :
310 : /* OK to call the extractQueryFn */
311 76 : queryValues = (Datum *)
312 76 : DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
313 : so->ginstate.supportCollation[skey->sk_attno - 1],
314 : skey->sk_argument,
315 : PointerGetDatum(&nQueryValues),
316 : UInt16GetDatum(skey->sk_strategy),
317 : PointerGetDatum(&partial_matches),
318 : PointerGetDatum(&extra_data),
319 : PointerGetDatum(&nullFlags),
320 : PointerGetDatum(&searchMode)));
321 :
322 : /*
323 : * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
324 : * in particular we don't allow extractQueryFn to select
325 : * GIN_SEARCH_MODE_EVERYTHING.
326 : */
327 152 : if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
328 76 : searchMode > GIN_SEARCH_MODE_ALL)
329 0 : searchMode = GIN_SEARCH_MODE_ALL;
330 :
331 : /* Non-default modes require the index to have placeholders */
332 76 : if (searchMode != GIN_SEARCH_MODE_DEFAULT)
333 12 : hasNullQuery = true;
334 :
335 : /*
336 : * In default mode, no keys means an unsatisfiable query.
337 : */
338 76 : if (queryValues == NULL || nQueryValues <= 0)
339 : {
340 11 : if (searchMode == GIN_SEARCH_MODE_DEFAULT)
341 : {
342 2 : so->isVoidRes = true;
343 2 : break;
344 : }
345 9 : nQueryValues = 0; /* ensure sane value */
346 : }
347 :
348 : /*
349 : * If the extractQueryFn didn't create a nullFlags array, create one,
350 : * assuming that everything's non-null. Otherwise, run through the
351 : * array and make sure each value is exactly 0 or 1; this ensures
352 : * binary compatibility with the GinNullCategory representation. While
353 : * at it, detect whether any null keys are present.
354 : */
355 74 : if (nullFlags == NULL)
356 40 : nullFlags = (bool *) palloc0(nQueryValues * sizeof(bool));
357 : else
358 : {
359 : int32 j;
360 :
361 75 : for (j = 0; j < nQueryValues; j++)
362 : {
363 41 : if (nullFlags[j])
364 : {
365 0 : nullFlags[j] = true; /* not any other nonzero value */
366 0 : hasNullQuery = true;
367 : }
368 : }
369 : }
370 : /* now we can use the nullFlags as category codes */
371 :
372 148 : ginFillScanKey(so, skey->sk_attno,
373 74 : skey->sk_strategy, searchMode,
374 : skey->sk_argument, nQueryValues,
375 : queryValues, (GinNullCategory *) nullFlags,
376 : partial_matches, extra_data);
377 : }
378 :
379 : /*
380 : * If there are no regular scan keys, generate an EVERYTHING scankey to
381 : * drive a full-index scan.
382 : */
383 74 : if (so->nkeys == 0 && !so->isVoidRes)
384 : {
385 0 : hasNullQuery = true;
386 0 : ginFillScanKey(so, FirstOffsetNumber,
387 : InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
388 : (Datum) 0, 0,
389 : NULL, NULL, NULL, NULL);
390 : }
391 :
392 : /*
393 : * If the index is version 0, it may be missing null and placeholder
394 : * entries, which would render searches for nulls and full-index scans
395 : * unreliable. Throw an error if so.
396 : */
397 74 : if (hasNullQuery && !so->isVoidRes)
398 : {
399 : GinStatsData ginStats;
400 :
401 12 : ginGetStats(scan->indexRelation, &ginStats);
402 12 : if (ginStats.ginVersion < 1)
403 0 : ereport(ERROR,
404 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
405 : errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
406 : errhint("To fix this, do REINDEX INDEX \"%s\".",
407 : RelationGetRelationName(scan->indexRelation))));
408 : }
409 :
410 74 : MemoryContextSwitchTo(oldCtx);
411 :
412 74 : pgstat_count_index_scan(scan->indexRelation);
413 74 : }
414 :
415 : void
416 74 : ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
417 : ScanKey orderbys, int norderbys)
418 : {
419 74 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
420 :
421 74 : ginFreeScanKeys(so);
422 :
423 74 : if (scankey && scan->numberOfKeys > 0)
424 : {
425 74 : memmove(scan->keyData, scankey,
426 74 : scan->numberOfKeys * sizeof(ScanKeyData));
427 : }
428 74 : }
429 :
430 :
431 : void
432 73 : ginendscan(IndexScanDesc scan)
433 : {
434 73 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
435 :
436 73 : ginFreeScanKeys(so);
437 :
438 73 : MemoryContextDelete(so->tempCtx);
439 73 : MemoryContextDelete(so->keyCtx);
440 :
441 73 : pfree(so);
442 73 : }
|