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
ginbeginscan(Relation rel,int nkeys,int norderbys)25 ginbeginscan(Relation rel, int nkeys, int norderbys)
26 {
27 	IndexScanDesc scan;
28 	GinScanOpaque so;
29 
30 	/* no order by operators allowed */
31 	Assert(norderbys == 0);
32 
33 	scan = RelationGetIndexScan(rel, nkeys, norderbys);
34 
35 	/* allocate private workspace */
36 	so = (GinScanOpaque) palloc(sizeof(GinScanOpaqueData));
37 	so->keys = NULL;
38 	so->nkeys = 0;
39 	so->tempCtx = AllocSetContextCreate(CurrentMemoryContext,
40 										"Gin scan temporary context",
41 										ALLOCSET_DEFAULT_SIZES);
42 	so->keyCtx = AllocSetContextCreate(CurrentMemoryContext,
43 									   "Gin scan key context",
44 									   ALLOCSET_DEFAULT_SIZES);
45 	initGinState(&so->ginstate, scan->indexRelation);
46 
47 	scan->opaque = so;
48 
49 	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
ginFillScanEntry(GinScanOpaque so,OffsetNumber attnum,StrategyNumber strategy,int32 searchMode,Datum queryKey,GinNullCategory queryCategory,bool isPartialMatch,Pointer extra_data)57 ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum,
58 				 StrategyNumber strategy, int32 searchMode,
59 				 Datum queryKey, GinNullCategory queryCategory,
60 				 bool isPartialMatch, Pointer extra_data)
61 {
62 	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 	if (extra_data == NULL)
73 	{
74 		for (i = 0; i < so->totalentries; i++)
75 		{
76 			GinScanEntry prevEntry = so->entries[i];
77 
78 			if (prevEntry->extra_data == NULL &&
79 				prevEntry->isPartialMatch == isPartialMatch &&
80 				prevEntry->strategy == strategy &&
81 				prevEntry->searchMode == searchMode &&
82 				prevEntry->attnum == attnum &&
83 				ginCompareEntries(ginstate, attnum,
84 								  prevEntry->queryKey,
85 								  prevEntry->queryCategory,
86 								  queryKey,
87 								  queryCategory) == 0)
88 			{
89 				/* Successful match */
90 				return prevEntry;
91 			}
92 		}
93 	}
94 
95 	/* Nope, create a new entry */
96 	scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData));
97 	scanEntry->queryKey = queryKey;
98 	scanEntry->queryCategory = queryCategory;
99 	scanEntry->isPartialMatch = isPartialMatch;
100 	scanEntry->extra_data = extra_data;
101 	scanEntry->strategy = strategy;
102 	scanEntry->searchMode = searchMode;
103 	scanEntry->attnum = attnum;
104 
105 	scanEntry->buffer = InvalidBuffer;
106 	ItemPointerSetMin(&scanEntry->curItem);
107 	scanEntry->matchBitmap = NULL;
108 	scanEntry->matchIterator = NULL;
109 	scanEntry->matchResult = NULL;
110 	scanEntry->list = NULL;
111 	scanEntry->nlist = 0;
112 	scanEntry->offset = InvalidOffsetNumber;
113 	scanEntry->isFinished = false;
114 	scanEntry->reduceResult = false;
115 
116 	/* Add it to so's array */
117 	if (so->totalentries >= so->allocentries)
118 	{
119 		so->allocentries *= 2;
120 		so->entries = (GinScanEntry *)
121 			repalloc(so->entries, so->allocentries * sizeof(GinScanEntry));
122 	}
123 	so->entries[so->totalentries++] = scanEntry;
124 
125 	return scanEntry;
126 }
127 
128 /*
129  * Initialize the next GinScanKey using the output from the extractQueryFn
130  */
131 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)132 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 	GinScanKey	key = &(so->keys[so->nkeys++]);
139 	GinState   *ginstate = &so->ginstate;
140 	uint32		nUserQueryValues = nQueryValues;
141 	uint32		i;
142 
143 	/* Non-default search modes add one "hidden" entry to each key */
144 	if (searchMode != GIN_SEARCH_MODE_DEFAULT)
145 		nQueryValues++;
146 	key->nentries = nQueryValues;
147 	key->nuserentries = nUserQueryValues;
148 
149 	key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * nQueryValues);
150 	key->entryRes = (GinTernaryValue *) palloc0(sizeof(GinTernaryValue) * nQueryValues);
151 
152 	key->query = query;
153 	key->queryValues = queryValues;
154 	key->queryCategories = queryCategories;
155 	key->extra_data = extra_data;
156 	key->strategy = strategy;
157 	key->searchMode = searchMode;
158 	key->attnum = attnum;
159 
160 	ItemPointerSetMin(&key->curItem);
161 	key->curItemMatches = false;
162 	key->recheckCurItem = false;
163 	key->isFinished = false;
164 	key->nrequired = 0;
165 	key->nadditional = 0;
166 	key->requiredEntries = NULL;
167 	key->additionalEntries = NULL;
168 
169 	ginInitConsistentFunction(ginstate, key);
170 
171 	for (i = 0; i < nQueryValues; i++)
172 	{
173 		Datum		queryKey;
174 		GinNullCategory queryCategory;
175 		bool		isPartialMatch;
176 		Pointer		this_extra;
177 
178 		if (i < nUserQueryValues)
179 		{
180 			/* set up normal entry using extractQueryFn's outputs */
181 			queryKey = queryValues[i];
182 			queryCategory = queryCategories[i];
183 			isPartialMatch =
184 				(ginstate->canPartialMatch[attnum - 1] && partial_matches)
185 				? partial_matches[i] : false;
186 			this_extra = (extra_data) ? extra_data[i] : NULL;
187 		}
188 		else
189 		{
190 			/* set up hidden entry */
191 			queryKey = (Datum) 0;
192 			switch (searchMode)
193 			{
194 				case GIN_SEARCH_MODE_INCLUDE_EMPTY:
195 					queryCategory = GIN_CAT_EMPTY_ITEM;
196 					break;
197 				case GIN_SEARCH_MODE_ALL:
198 					queryCategory = GIN_CAT_EMPTY_QUERY;
199 					break;
200 				case GIN_SEARCH_MODE_EVERYTHING:
201 					queryCategory = GIN_CAT_EMPTY_QUERY;
202 					break;
203 				default:
204 					elog(ERROR, "unexpected searchMode: %d", searchMode);
205 					queryCategory = 0;	/* keep compiler quiet */
206 					break;
207 			}
208 			isPartialMatch = false;
209 			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 			strategy = InvalidStrategy;
219 		}
220 
221 		key->scanEntry[i] = ginFillScanEntry(so, attnum,
222 											 strategy, searchMode,
223 											 queryKey, queryCategory,
224 											 isPartialMatch, this_extra);
225 	}
226 }
227 
228 /*
229  * Release current scan keys, if any.
230  */
231 void
ginFreeScanKeys(GinScanOpaque so)232 ginFreeScanKeys(GinScanOpaque so)
233 {
234 	uint32		i;
235 
236 	if (so->keys == NULL)
237 		return;
238 
239 	for (i = 0; i < so->totalentries; i++)
240 	{
241 		GinScanEntry entry = so->entries[i];
242 
243 		if (entry->buffer != InvalidBuffer)
244 			ReleaseBuffer(entry->buffer);
245 		if (entry->list)
246 			pfree(entry->list);
247 		if (entry->matchIterator)
248 			tbm_end_iterate(entry->matchIterator);
249 		if (entry->matchBitmap)
250 			tbm_free(entry->matchBitmap);
251 	}
252 
253 	MemoryContextResetAndDeleteChildren(so->keyCtx);
254 
255 	so->keys = NULL;
256 	so->nkeys = 0;
257 	so->entries = NULL;
258 	so->totalentries = 0;
259 }
260 
261 void
ginNewScanKey(IndexScanDesc scan)262 ginNewScanKey(IndexScanDesc scan)
263 {
264 	ScanKey		scankey = scan->keyData;
265 	GinScanOpaque so = (GinScanOpaque) scan->opaque;
266 	int			i;
267 	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 	oldCtx = MemoryContextSwitchTo(so->keyCtx);
276 
277 	/* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
278 	so->keys = (GinScanKey)
279 		palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
280 	so->nkeys = 0;
281 
282 	/* initialize expansible array of GinScanEntry pointers */
283 	so->totalentries = 0;
284 	so->allocentries = 32;
285 	so->entries = (GinScanEntry *)
286 		palloc(so->allocentries * sizeof(GinScanEntry));
287 
288 	so->isVoidRes = false;
289 
290 	for (i = 0; i < scan->numberOfKeys; i++)
291 	{
292 		ScanKey		skey = &scankey[i];
293 		Datum	   *queryValues;
294 		int32		nQueryValues = 0;
295 		bool	   *partial_matches = NULL;
296 		Pointer    *extra_data = NULL;
297 		bool	   *nullFlags = NULL;
298 		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 		if (skey->sk_flags & SK_ISNULL)
305 		{
306 			so->isVoidRes = true;
307 			break;
308 		}
309 
310 		/* OK to call the extractQueryFn */
311 		queryValues = (Datum *)
312 			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 		if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
328 			searchMode > GIN_SEARCH_MODE_ALL)
329 			searchMode = GIN_SEARCH_MODE_ALL;
330 
331 		/* Non-default modes require the index to have placeholders */
332 		if (searchMode != GIN_SEARCH_MODE_DEFAULT)
333 			hasNullQuery = true;
334 
335 		/*
336 		 * In default mode, no keys means an unsatisfiable query.
337 		 */
338 		if (queryValues == NULL || nQueryValues <= 0)
339 		{
340 			if (searchMode == GIN_SEARCH_MODE_DEFAULT)
341 			{
342 				so->isVoidRes = true;
343 				break;
344 			}
345 			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 		if (nullFlags == NULL)
356 			nullFlags = (bool *) palloc0(nQueryValues * sizeof(bool));
357 		else
358 		{
359 			int32		j;
360 
361 			for (j = 0; j < nQueryValues; j++)
362 			{
363 				if (nullFlags[j])
364 				{
365 					nullFlags[j] = true;	/* not any other nonzero value */
366 					hasNullQuery = true;
367 				}
368 			}
369 		}
370 		/* now we can use the nullFlags as category codes */
371 
372 		ginFillScanKey(so, skey->sk_attno,
373 					   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 	if (so->nkeys == 0 && !so->isVoidRes)
384 	{
385 		hasNullQuery = true;
386 		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 	if (hasNullQuery && !so->isVoidRes)
398 	{
399 		GinStatsData ginStats;
400 
401 		ginGetStats(scan->indexRelation, &ginStats);
402 		if (ginStats.ginVersion < 1)
403 			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 	MemoryContextSwitchTo(oldCtx);
411 
412 	pgstat_count_index_scan(scan->indexRelation);
413 }
414 
415 void
ginrescan(IndexScanDesc scan,ScanKey scankey,int nscankeys,ScanKey orderbys,int norderbys)416 ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
417 		  ScanKey orderbys, int norderbys)
418 {
419 	GinScanOpaque so = (GinScanOpaque) scan->opaque;
420 
421 	ginFreeScanKeys(so);
422 
423 	if (scankey && scan->numberOfKeys > 0)
424 	{
425 		memmove(scan->keyData, scankey,
426 				scan->numberOfKeys * sizeof(ScanKeyData));
427 	}
428 }
429 
430 
431 void
ginendscan(IndexScanDesc scan)432 ginendscan(IndexScanDesc scan)
433 {
434 	GinScanOpaque so = (GinScanOpaque) scan->opaque;
435 
436 	ginFreeScanKeys(so);
437 
438 	MemoryContextDelete(so->tempCtx);
439 	MemoryContextDelete(so->keyCtx);
440 
441 	pfree(so);
442 }
443