Age Owner Branch data TLA 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-2025, 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
3622 tgl@sss.pgh.pa.us 25 :CBC 969 : ginbeginscan(Relation rel, int nkeys, int norderbys)
26 : : {
27 : : IndexScanDesc scan;
28 : : GinScanOpaque so;
29 : :
30 : : /* no order by operators allowed */
5494 31 [ - + ]: 969 : Assert(norderbys == 0);
32 : :
33 : 969 : scan = RelationGetIndexScan(rel, nkeys, norderbys);
34 : :
35 : : /* allocate private workspace */
7 michael@paquier.xyz 36 :GNC 969 : so = (GinScanOpaque) palloc_object(GinScanOpaqueData);
5494 tgl@sss.pgh.pa.us 37 :CBC 969 : so->keys = NULL;
38 : 969 : so->nkeys = 0;
39 : 969 : so->tempCtx = AllocSetContextCreate(CurrentMemoryContext,
40 : : "Gin scan temporary context",
41 : : ALLOCSET_DEFAULT_SIZES);
3969 heikki.linnakangas@i 42 : 969 : so->keyCtx = AllocSetContextCreate(CurrentMemoryContext,
43 : : "Gin scan key context",
44 : : ALLOCSET_DEFAULT_SIZES);
5494 tgl@sss.pgh.pa.us 45 : 969 : initGinState(&so->ginstate, scan->indexRelation);
46 : :
47 : 969 : scan->opaque = so;
48 : :
3622 49 : 969 : 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
5457 57 : 2038 : ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum,
58 : : StrategyNumber strategy, int32 searchMode,
59 : : Datum queryKey, GinNullCategory queryCategory,
60 : : bool isPartialMatch, Pointer extra_data)
61 : : {
62 : 2038 : 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 : : * Also, give up de-duplication once we have 100 entries. That avoids
73 : : * spending O(N^2) time on probably-fruitless de-duplication of large
74 : : * search-key sets. The threshold of 100 is arbitrary but matches
75 : : * predtest.c's threshold for what's a large array.
76 : : */
286 77 [ + + + - ]: 2038 : if (extra_data == NULL && so->totalentries < 100)
78 : : {
5457 79 [ + + ]: 2817 : for (i = 0; i < so->totalentries; i++)
80 : : {
81 : 1795 : GinScanEntry prevEntry = so->entries[i];
82 : :
83 [ + + ]: 1795 : if (prevEntry->extra_data == NULL &&
84 [ + - ]: 1645 : prevEntry->isPartialMatch == isPartialMatch &&
85 [ + + ]: 1645 : prevEntry->strategy == strategy &&
86 [ + - ]: 1575 : prevEntry->searchMode == searchMode &&
87 [ + + - + ]: 3138 : prevEntry->attnum == attnum &&
88 : 1563 : ginCompareEntries(ginstate, attnum,
89 : : prevEntry->queryKey,
90 : 1563 : prevEntry->queryCategory,
91 : : queryKey,
92 : : queryCategory) == 0)
93 : : {
94 : : /* Successful match */
5457 tgl@sss.pgh.pa.us 95 :UBC 0 : return prevEntry;
96 : : }
97 : : }
98 : : }
99 : :
100 : : /* Nope, create a new entry */
7 michael@paquier.xyz 101 :GNC 2038 : scanEntry = palloc_object(GinScanEntryData);
5457 tgl@sss.pgh.pa.us 102 :CBC 2038 : scanEntry->queryKey = queryKey;
103 : 2038 : scanEntry->queryCategory = queryCategory;
104 : 2038 : scanEntry->isPartialMatch = isPartialMatch;
105 : 2038 : scanEntry->extra_data = extra_data;
106 : 2038 : scanEntry->strategy = strategy;
107 : 2038 : scanEntry->searchMode = searchMode;
108 : 2038 : scanEntry->attnum = attnum;
109 : :
110 : 2038 : scanEntry->buffer = InvalidBuffer;
111 : 2038 : ItemPointerSetMin(&scanEntry->curItem);
112 : 2038 : scanEntry->matchBitmap = NULL;
113 : 2038 : scanEntry->matchIterator = NULL;
277 melanieplageman@gmai 114 : 2038 : scanEntry->matchResult.blockno = InvalidBlockNumber;
296 115 : 2038 : scanEntry->matchNtuples = -1;
5457 tgl@sss.pgh.pa.us 116 : 2038 : scanEntry->list = NULL;
117 : 2038 : scanEntry->nlist = 0;
118 : 2038 : scanEntry->offset = InvalidOffsetNumber;
119 : 2038 : scanEntry->isFinished = false;
120 : 2038 : scanEntry->reduceResult = false;
121 : :
122 : : /* Add it to so's array */
123 [ + + ]: 2038 : if (so->totalentries >= so->allocentries)
124 : : {
125 : 3 : so->allocentries *= 2;
7 michael@paquier.xyz 126 :GNC 3 : so->entries = repalloc_array(so->entries, GinScanEntry, so->allocentries);
127 : : }
5457 tgl@sss.pgh.pa.us 128 :CBC 2038 : so->entries[so->totalentries++] = scanEntry;
129 : :
130 : 2038 : return scanEntry;
131 : : }
132 : :
133 : : /*
134 : : * Append hidden scan entry of given category to the scan key.
135 : : *
136 : : * NB: this had better be called at most once per scan key, since
137 : : * ginFillScanKey leaves room for only one hidden entry. Currently,
138 : : * it seems sufficiently clear that this is true that we don't bother
139 : : * with any cross-check logic.
140 : : */
141 : : static void
2160 akorotkov@postgresql 142 : 163 : ginScanKeyAddHiddenEntry(GinScanOpaque so, GinScanKey key,
143 : : GinNullCategory queryCategory)
144 : : {
145 : 163 : int i = key->nentries++;
146 : :
147 : : /* strategy is of no interest because this is not a partial-match item */
148 : 163 : key->scanEntry[i] = ginFillScanEntry(so, key->attnum,
149 : : InvalidStrategy, key->searchMode,
150 : : (Datum) 0, queryCategory,
151 : : false, NULL);
152 : 163 : }
153 : :
154 : : /*
155 : : * Initialize the next GinScanKey using the output from the extractQueryFn
156 : : */
157 : : static void
5457 tgl@sss.pgh.pa.us 158 : 1032 : ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
159 : : StrategyNumber strategy, int32 searchMode,
160 : : Datum query, uint32 nQueryValues,
161 : : Datum *queryValues, GinNullCategory *queryCategories,
162 : : bool *partial_matches, Pointer *extra_data)
163 : : {
164 : 1032 : GinScanKey key = &(so->keys[so->nkeys++]);
165 : 1032 : GinState *ginstate = &so->ginstate;
166 : : uint32 i;
167 : :
5458 168 : 1032 : key->nentries = nQueryValues;
2160 akorotkov@postgresql 169 : 1032 : key->nuserentries = nQueryValues;
170 : :
171 : : /* Allocate one extra array slot for possible "hidden" entry */
7 michael@paquier.xyz 172 :GNC 1032 : key->scanEntry = palloc_array(GinScanEntry, nQueryValues + 1);
173 : 1032 : key->entryRes = palloc0_array(GinTernaryValue, nQueryValues + 1);
174 : :
5458 tgl@sss.pgh.pa.us 175 :CBC 1032 : key->query = query;
176 : 1032 : key->queryValues = queryValues;
177 : 1032 : key->queryCategories = queryCategories;
178 : 1032 : key->extra_data = extra_data;
7169 teodor@sigaev.ru 179 : 1032 : key->strategy = strategy;
5458 tgl@sss.pgh.pa.us 180 : 1032 : key->searchMode = searchMode;
6368 181 : 1032 : key->attnum = attnum;
182 : :
183 : : /*
184 : : * Initially, scan keys of GIN_SEARCH_MODE_ALL mode are marked
185 : : * excludeOnly. This might get changed later.
186 : : */
2160 akorotkov@postgresql 187 : 1032 : key->excludeOnly = (searchMode == GIN_SEARCH_MODE_ALL);
188 : :
5618 tgl@sss.pgh.pa.us 189 : 1032 : ItemPointerSetMin(&key->curItem);
5457 190 : 1032 : key->curItemMatches = false;
191 : 1032 : key->recheckCurItem = false;
192 : 1032 : key->isFinished = false;
3974 heikki.linnakangas@i 193 : 1032 : key->nrequired = 0;
194 : 1032 : key->nadditional = 0;
195 : 1032 : key->requiredEntries = NULL;
196 : 1032 : key->additionalEntries = NULL;
197 : :
4331 198 : 1032 : ginInitConsistentFunction(ginstate, key);
199 : :
200 : : /* Set up normal scan entries using extractQueryFn's outputs */
5458 tgl@sss.pgh.pa.us 201 [ + + ]: 2907 : for (i = 0; i < nQueryValues; i++)
202 : : {
203 : : Datum queryKey;
204 : : GinNullCategory queryCategory;
205 : : bool isPartialMatch;
206 : : Pointer this_extra;
207 : :
2160 akorotkov@postgresql 208 : 1875 : queryKey = queryValues[i];
209 : 1875 : queryCategory = queryCategories[i];
210 : 1875 : isPartialMatch =
211 [ + + + - ]: 1875 : (ginstate->canPartialMatch[attnum - 1] && partial_matches)
212 [ + + + + ]: 1875 : ? partial_matches[i] : false;
213 [ + + ]: 1875 : this_extra = (extra_data) ? extra_data[i] : NULL;
214 : :
5457 tgl@sss.pgh.pa.us 215 : 1875 : key->scanEntry[i] = ginFillScanEntry(so, attnum,
216 : : strategy, searchMode,
217 : : queryKey, queryCategory,
218 : : isPartialMatch, this_extra);
219 : : }
220 : :
221 : : /*
222 : : * For GIN_SEARCH_MODE_INCLUDE_EMPTY and GIN_SEARCH_MODE_EVERYTHING search
223 : : * modes, we add the "hidden" entry immediately. GIN_SEARCH_MODE_ALL is
224 : : * handled later, since we might be able to omit the hidden entry for it.
225 : : */
2160 akorotkov@postgresql 226 [ + + ]: 1032 : if (searchMode == GIN_SEARCH_MODE_INCLUDE_EMPTY)
227 : 22 : ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_ITEM);
228 [ - + ]: 1010 : else if (searchMode == GIN_SEARCH_MODE_EVERYTHING)
2160 akorotkov@postgresql 229 :UBC 0 : ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_QUERY);
7169 teodor@sigaev.ru 230 :CBC 1032 : }
231 : :
232 : : /*
233 : : * Release current scan keys, if any.
234 : : */
235 : : void
3974 heikki.linnakangas@i 236 : 2913 : ginFreeScanKeys(GinScanOpaque so)
237 : : {
238 : : uint32 i;
239 : :
5457 tgl@sss.pgh.pa.us 240 [ + + ]: 2913 : if (so->keys == NULL)
7169 teodor@sigaev.ru 241 : 1941 : return;
242 : :
5457 tgl@sss.pgh.pa.us 243 [ + + ]: 3010 : for (i = 0; i < so->totalentries; i++)
244 : : {
245 : 2038 : GinScanEntry entry = so->entries[i];
246 : :
247 [ - + ]: 2038 : if (entry->buffer != InvalidBuffer)
5457 tgl@sss.pgh.pa.us 248 :UBC 0 : ReleaseBuffer(entry->buffer);
3566 tgl@sss.pgh.pa.us 249 [ + + ]:CBC 2038 : if (entry->list)
250 : 1092 : pfree(entry->list);
5457 251 [ - + ]: 2038 : if (entry->matchIterator)
364 melanieplageman@gmai 252 :UBC 0 : tbm_end_private_iterate(entry->matchIterator);
5457 tgl@sss.pgh.pa.us 253 [ + + ]:CBC 2038 : if (entry->matchBitmap)
254 : 440 : tbm_free(entry->matchBitmap);
255 : : }
256 : :
763 nathan@postgresql.or 257 : 972 : MemoryContextReset(so->keyCtx);
258 : :
3969 heikki.linnakangas@i 259 : 972 : so->keys = NULL;
260 : 972 : so->nkeys = 0;
5457 tgl@sss.pgh.pa.us 261 : 972 : so->entries = NULL;
262 : 972 : so->totalentries = 0;
263 : : }
264 : :
265 : : void
5540 266 : 972 : ginNewScanKey(IndexScanDesc scan)
267 : : {
7014 bruce@momjian.us 268 : 972 : ScanKey scankey = scan->keyData;
7169 teodor@sigaev.ru 269 : 972 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
270 : : int i;
271 : : int numExcludeOnly;
5458 tgl@sss.pgh.pa.us 272 : 972 : bool hasNullQuery = false;
2160 akorotkov@postgresql 273 : 972 : bool attrHasNormalScan[INDEX_MAX_KEYS] = {false};
274 : : MemoryContext oldCtx;
275 : :
276 : : /*
277 : : * Allocate all the scan key information in the key context. (If
278 : : * extractQuery leaks anything there, it won't be reset until the end of
279 : : * scan or rescan, but that's OK.)
280 : : */
3969 heikki.linnakangas@i 281 : 972 : oldCtx = MemoryContextSwitchTo(so->keyCtx);
282 : :
283 : : /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
5458 tgl@sss.pgh.pa.us 284 : 972 : so->keys = (GinScanKey)
285 : 972 : palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
5457 286 : 972 : so->nkeys = 0;
287 : :
288 : : /* initialize expansible array of GinScanEntry pointers */
289 : 972 : so->totalentries = 0;
290 : 972 : so->allocentries = 32;
291 : 972 : so->entries = (GinScanEntry *)
3566 292 : 972 : palloc(so->allocentries * sizeof(GinScanEntry));
293 : :
6895 teodor@sigaev.ru 294 : 972 : so->isVoidRes = false;
295 : :
7014 bruce@momjian.us 296 [ + + ]: 2004 : for (i = 0; i < scan->numberOfKeys; i++)
297 : : {
6111 tgl@sss.pgh.pa.us 298 : 1038 : ScanKey skey = &scankey[i];
299 : : Datum *queryValues;
5458 300 : 1038 : int32 nQueryValues = 0;
6033 bruce@momjian.us 301 : 1038 : bool *partial_matches = NULL;
302 : 1038 : Pointer *extra_data = NULL;
5458 tgl@sss.pgh.pa.us 303 : 1038 : bool *nullFlags = NULL;
304 : : GinNullCategory *categories;
305 : 1038 : int32 searchMode = GIN_SEARCH_MODE_DEFAULT;
306 : :
307 : : /*
308 : : * We assume that GIN-indexable operators are strict, so a null query
309 : : * argument means an unsatisfiable query.
310 : : */
6111 311 [ - + ]: 1038 : if (skey->sk_flags & SK_ISNULL)
312 : : {
6100 teodor@sigaev.ru 313 :UBC 0 : so->isVoidRes = true;
6100 teodor@sigaev.ru 314 :CBC 6 : break;
315 : : }
316 : :
317 : : /* OK to call the extractQueryFn */
318 : : queryValues = (Datum *)
5353 tgl@sss.pgh.pa.us 319 : 3114 : DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
3101 320 : 1038 : so->ginstate.supportCollation[skey->sk_attno - 1],
321 : : skey->sk_argument,
322 : : PointerGetDatum(&nQueryValues),
323 : 1038 : UInt16GetDatum(skey->sk_strategy),
324 : : PointerGetDatum(&partial_matches),
325 : : PointerGetDatum(&extra_data),
326 : : PointerGetDatum(&nullFlags),
327 : : PointerGetDatum(&searchMode)));
328 : :
329 : : /*
330 : : * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
331 : : * in particular we don't allow extractQueryFn to select
332 : : * GIN_SEARCH_MODE_EVERYTHING.
333 : : */
5458 334 [ + - ]: 1038 : if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
335 [ - + ]: 1038 : searchMode > GIN_SEARCH_MODE_ALL)
5458 tgl@sss.pgh.pa.us 336 :UBC 0 : searchMode = GIN_SEARCH_MODE_ALL;
337 : :
338 : : /* Non-default modes require the index to have placeholders */
5458 tgl@sss.pgh.pa.us 339 [ + + ]:CBC 1038 : if (searchMode != GIN_SEARCH_MODE_DEFAULT)
340 : 184 : hasNullQuery = true;
341 : :
342 : : /*
343 : : * In default mode, no keys means an unsatisfiable query.
344 : : */
345 [ + + + + ]: 1038 : if (queryValues == NULL || nQueryValues <= 0)
346 : : {
347 [ + + ]: 154 : if (searchMode == GIN_SEARCH_MODE_DEFAULT)
348 : : {
349 : 6 : so->isVoidRes = true;
350 : 6 : break;
351 : : }
352 : 148 : nQueryValues = 0; /* ensure sane value */
353 : : }
354 : :
355 : : /*
356 : : * Create GinNullCategory representation. If the extractQueryFn
357 : : * didn't create a nullFlags array, we assume everything is non-null.
358 : : * While at it, detect whether any null keys are present.
359 : : */
2913 peter_e@gmx.net 360 : 1032 : categories = (GinNullCategory *) palloc0(nQueryValues * sizeof(GinNullCategory));
361 [ + + ]: 1032 : if (nullFlags)
362 : : {
363 : : int32 j;
364 : :
5458 tgl@sss.pgh.pa.us 365 [ + + ]: 512 : for (j = 0; j < nQueryValues; j++)
366 : : {
367 [ - + ]: 232 : if (nullFlags[j])
368 : : {
2913 peter_e@gmx.net 369 :UBC 0 : categories[j] = GIN_CAT_NULL_KEY;
5458 tgl@sss.pgh.pa.us 370 : 0 : hasNullQuery = true;
371 : : }
372 : : }
373 : : }
374 : :
5457 tgl@sss.pgh.pa.us 375 :CBC 1032 : ginFillScanKey(so, skey->sk_attno,
376 : 1032 : skey->sk_strategy, searchMode,
377 : : skey->sk_argument, nQueryValues,
378 : : queryValues, categories,
379 : : partial_matches, extra_data);
380 : :
381 : : /* Remember if we had any non-excludeOnly keys */
2160 akorotkov@postgresql 382 [ + + ]: 1032 : if (searchMode != GIN_SEARCH_MODE_ALL)
383 : 870 : attrHasNormalScan[skey->sk_attno - 1] = true;
384 : : }
385 : :
386 : : /*
387 : : * Processing GIN_SEARCH_MODE_ALL scan keys requires us to make a second
388 : : * pass over the scan keys. Above we marked each such scan key as
389 : : * excludeOnly. If the involved column has any normal (not excludeOnly)
390 : : * scan key as well, then we can leave it like that. Otherwise, one
391 : : * excludeOnly scan key must receive a GIN_CAT_EMPTY_QUERY hidden entry
392 : : * and be set to normal (excludeOnly = false).
393 : : */
113 tgl@sss.pgh.pa.us 394 : 972 : numExcludeOnly = 0;
2160 akorotkov@postgresql 395 [ + + ]: 2004 : for (i = 0; i < so->nkeys; i++)
396 : : {
397 : 1032 : GinScanKey key = &so->keys[i];
398 : :
399 [ + + ]: 1032 : if (key->searchMode != GIN_SEARCH_MODE_ALL)
400 : 870 : continue;
401 : :
402 [ + + ]: 162 : if (!attrHasNormalScan[key->attnum - 1])
403 : : {
404 : 141 : key->excludeOnly = false;
405 : 141 : ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_QUERY);
406 : 141 : attrHasNormalScan[key->attnum - 1] = true;
407 : : }
408 : : else
113 tgl@sss.pgh.pa.us 409 : 21 : numExcludeOnly++;
410 : : }
411 : :
412 : : /*
413 : : * If we left any excludeOnly scan keys as-is, move them to the end of the
414 : : * scan key array: they must appear after normal key(s).
415 : : */
416 [ + + ]: 972 : if (numExcludeOnly > 0)
417 : : {
418 : : GinScanKey tmpkeys;
419 : : int iNormalKey;
420 : : int iExcludeOnly;
421 : :
422 : : /* We'd better have made at least one normal key */
423 [ - + ]: 21 : Assert(numExcludeOnly < so->nkeys);
424 : : /* Make a temporary array to hold the re-ordered scan keys */
425 : 21 : tmpkeys = (GinScanKey) palloc(so->nkeys * sizeof(GinScanKeyData));
426 : : /* Re-order the keys ... */
427 : 21 : iNormalKey = 0;
428 : 21 : iExcludeOnly = so->nkeys - numExcludeOnly;
429 [ + + ]: 75 : for (i = 0; i < so->nkeys; i++)
430 : : {
431 : 54 : GinScanKey key = &so->keys[i];
432 : :
433 [ + + ]: 54 : if (key->excludeOnly)
434 : : {
435 : 21 : memcpy(tmpkeys + iExcludeOnly, key, sizeof(GinScanKeyData));
436 : 21 : iExcludeOnly++;
437 : : }
438 : : else
439 : : {
440 : 33 : memcpy(tmpkeys + iNormalKey, key, sizeof(GinScanKeyData));
441 : 33 : iNormalKey++;
442 : : }
443 : : }
444 [ - + ]: 21 : Assert(iNormalKey == so->nkeys - numExcludeOnly);
445 [ - + ]: 21 : Assert(iExcludeOnly == so->nkeys);
446 : : /* ... and copy them back to so->keys[] */
447 : 21 : memcpy(so->keys, tmpkeys, so->nkeys * sizeof(GinScanKeyData));
448 : 21 : pfree(tmpkeys);
449 : : }
450 : :
451 : : /*
452 : : * If there are no regular scan keys, generate an EVERYTHING scankey to
453 : : * drive a full-index scan.
454 : : */
5457 455 [ + + - + ]: 972 : if (so->nkeys == 0 && !so->isVoidRes)
456 : : {
5458 tgl@sss.pgh.pa.us 457 :UBC 0 : hasNullQuery = true;
5457 458 : 0 : ginFillScanKey(so, FirstOffsetNumber,
459 : : InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
460 : : (Datum) 0, 0,
461 : : NULL, NULL, NULL, NULL);
462 : : }
463 : :
464 : : /*
465 : : * If the index is version 0, it may be missing null and placeholder
466 : : * entries, which would render searches for nulls and full-index scans
467 : : * unreliable. Throw an error if so.
468 : : */
5458 tgl@sss.pgh.pa.us 469 [ + + + - ]:CBC 972 : if (hasNullQuery && !so->isVoidRes)
470 : : {
471 : : GinStatsData ginStats;
472 : :
473 : 164 : ginGetStats(scan->indexRelation, &ginStats);
474 [ - + ]: 164 : if (ginStats.ginVersion < 1)
5458 tgl@sss.pgh.pa.us 475 [ # # ]:UBC 0 : ereport(ERROR,
476 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
477 : : errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
478 : : errhint("To fix this, do REINDEX INDEX \"%s\".",
479 : : RelationGetRelationName(scan->indexRelation))));
480 : : }
481 : :
3969 heikki.linnakangas@i 482 :CBC 972 : MemoryContextSwitchTo(oldCtx);
483 : :
6779 tgl@sss.pgh.pa.us 484 [ - + - - : 972 : pgstat_count_index_scan(scan->indexRelation);
+ - ]
281 pg@bowt.ie 485 [ + - ]: 972 : if (scan->instrument)
486 : 972 : scan->instrument->nsearches++;
7169 teodor@sigaev.ru 487 : 972 : }
488 : :
489 : : void
3622 tgl@sss.pgh.pa.us 490 : 972 : ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
491 : : ScanKey orderbys, int norderbys)
492 : : {
5494 493 : 972 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
494 : :
3974 heikki.linnakangas@i 495 : 972 : ginFreeScanKeys(so);
496 : :
7014 bruce@momjian.us 497 [ + - + - ]: 972 : if (scankey && scan->numberOfKeys > 0)
462 peter@eisentraut.org 498 : 972 : memcpy(scan->keyData, scankey, scan->numberOfKeys * sizeof(ScanKeyData));
7169 teodor@sigaev.ru 499 : 972 : }
500 : :
501 : :
502 : : void
3622 tgl@sss.pgh.pa.us 503 : 969 : ginendscan(IndexScanDesc scan)
504 : : {
7014 bruce@momjian.us 505 : 969 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
506 : :
3974 heikki.linnakangas@i 507 : 969 : ginFreeScanKeys(so);
508 : :
5494 tgl@sss.pgh.pa.us 509 : 969 : MemoryContextDelete(so->tempCtx);
3969 heikki.linnakangas@i 510 : 969 : MemoryContextDelete(so->keyCtx);
511 : :
5494 tgl@sss.pgh.pa.us 512 : 969 : pfree(so);
7169 teodor@sigaev.ru 513 : 969 : }
|