1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginget.c
4 : * fetch tuples from a GIN scan.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.9 2007/11/15 21:14:31 momjian Exp $
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 : #include "access/gin.h"
17 : #include "catalog/index.h"
18 : #include "utils/memutils.h"
19 :
20 : static bool
21 : findItemInPage(Page page, ItemPointer item, OffsetNumber *off)
22 237 : {
23 237 : OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
24 : int res;
25 :
26 237 : if (GinPageGetOpaque(page)->flags & GIN_DELETED)
27 : /* page was deleted by concurrent vacuum */
28 0 : return false;
29 :
30 237 : if (*off > maxoff || *off == InvalidOffsetNumber)
31 0 : res = -1;
32 : else
33 237 : res = compareItemPointers(item, (ItemPointer) GinDataPageGetItem(page, *off));
34 :
35 237 : if (res == 0)
36 : {
37 : /* page isn't changed */
38 237 : return true;
39 : }
40 0 : else if (res > 0)
41 : {
42 : /*
43 : * some items was added before our position, look further to find it
44 : * or first greater
45 : */
46 :
47 0 : (*off)++;
48 0 : for (; *off <= maxoff; (*off)++)
49 : {
50 0 : res = compareItemPointers(item, (ItemPointer) GinDataPageGetItem(page, *off));
51 :
52 0 : if (res == 0)
53 0 : return true;
54 :
55 0 : if (res < 0)
56 : {
57 0 : (*off)--;
58 0 : return true;
59 : }
60 : }
61 : }
62 : else
63 : {
64 : /*
65 : * some items was deleted before our position, look from begining to
66 : * find it or first greater
67 : */
68 :
69 0 : for (*off = FirstOffsetNumber; *off <= maxoff; (*off)++)
70 : {
71 0 : res = compareItemPointers(item, (ItemPointer) GinDataPageGetItem(page, *off));
72 :
73 0 : if (res == 0)
74 0 : return true;
75 :
76 0 : if (res < 0)
77 : {
78 0 : (*off)--;
79 0 : return true;
80 : }
81 : }
82 : }
83 :
84 0 : return false;
85 : }
86 :
87 : /*
88 : * Start* functions setup state of searches: find correct buffer and locks it,
89 : * Stop* functions unlock buffer (but don't release!)
90 : */
91 : static void
92 : startScanEntry(Relation index, GinState *ginstate, GinScanEntry entry, bool firstCall)
93 1162 : {
94 1162 : if (entry->master != NULL)
95 : {
96 0 : entry->isFinished = entry->master->isFinished;
97 0 : return;
98 : }
99 :
100 1162 : if (firstCall)
101 : {
102 : /*
103 : * at first call we should find entry, and begin scan of posting tree
104 : * or just store posting list in memory
105 : */
106 : GinBtreeData btreeEntry;
107 : GinBtreeStack *stackEntry;
108 : Page page;
109 54 : bool needUnlock = TRUE;
110 :
111 54 : prepareEntryScan(&btreeEntry, index, entry->entry, ginstate);
112 54 : btreeEntry.searchMode = TRUE;
113 54 : stackEntry = ginFindLeafPage(&btreeEntry, NULL);
114 54 : page = BufferGetPage(stackEntry->buffer);
115 :
116 54 : entry->isFinished = TRUE;
117 54 : entry->buffer = InvalidBuffer;
118 54 : entry->offset = InvalidOffsetNumber;
119 54 : entry->list = NULL;
120 54 : entry->nlist = 0;
121 54 : entry->reduceResult = FALSE;
122 54 : entry->predictNumberResult = 0;
123 :
124 54 : if (btreeEntry.findItem(&btreeEntry, stackEntry))
125 : {
126 52 : IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stackEntry->off));
127 :
128 52 : if (GinIsPostingTree(itup))
129 : {
130 4 : BlockNumber rootPostingTree = GinGetPostingTree(itup);
131 : GinPostingTreeScan *gdi;
132 : Page page;
133 :
134 4 : LockBuffer(stackEntry->buffer, GIN_UNLOCK);
135 4 : needUnlock = FALSE;
136 4 : gdi = prepareScanPostingTree(index, rootPostingTree, TRUE);
137 :
138 4 : entry->buffer = scanBeginPostingTree(gdi);
139 4 : IncrBufferRefCount(entry->buffer);
140 :
141 4 : page = BufferGetPage(entry->buffer);
142 4 : entry->predictNumberResult = gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff;
143 :
144 4 : freeGinBtreeStack(gdi->stack);
145 4 : pfree(gdi);
146 4 : entry->isFinished = FALSE;
147 : }
148 48 : else if (GinGetNPosting(itup) > 0)
149 : {
150 48 : entry->nlist = GinGetNPosting(itup);
151 48 : entry->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * entry->nlist);
152 48 : memcpy(entry->list, GinGetPosting(itup), sizeof(ItemPointerData) * entry->nlist);
153 48 : entry->isFinished = FALSE;
154 : }
155 : }
156 :
157 54 : if (needUnlock)
158 50 : LockBuffer(stackEntry->buffer, GIN_UNLOCK);
159 54 : freeGinBtreeStack(stackEntry);
160 : }
161 1108 : else if (entry->buffer != InvalidBuffer)
162 : {
163 : /* we should find place where we was stopped */
164 : BlockNumber blkno;
165 : Page page;
166 :
167 237 : LockBuffer(entry->buffer, GIN_SHARE);
168 :
169 237 : if (!ItemPointerIsValid(&entry->curItem))
170 : /* start position */
171 0 : return;
172 : Assert(entry->offset != InvalidOffsetNumber);
173 :
174 237 : page = BufferGetPage(entry->buffer);
175 :
176 : /* try to find curItem in current buffer */
177 237 : if (findItemInPage(page, &entry->curItem, &entry->offset))
178 237 : return;
179 :
180 : /* walk to right */
181 0 : while ((blkno = GinPageGetOpaque(page)->rightlink) != InvalidBlockNumber)
182 : {
183 0 : LockBuffer(entry->buffer, GIN_UNLOCK);
184 0 : entry->buffer = ReleaseAndReadBuffer(entry->buffer, index, blkno);
185 0 : LockBuffer(entry->buffer, GIN_SHARE);
186 0 : page = BufferGetPage(entry->buffer);
187 :
188 0 : entry->offset = InvalidOffsetNumber;
189 0 : if (findItemInPage(page, &entry->curItem, &entry->offset))
190 0 : return;
191 : }
192 :
193 : /*
194 : * curItem and any greated items was deleted by concurrent vacuum, so
195 : * we finished scan with currrent entry
196 : */
197 : }
198 : }
199 :
200 : static void
201 : stopScanEntry(GinScanEntry entry)
202 1162 : {
203 1162 : if (entry->buffer != InvalidBuffer)
204 237 : LockBuffer(entry->buffer, GIN_UNLOCK);
205 1162 : }
206 :
207 : static void
208 : startScanKey(Relation index, GinState *ginstate, GinScanKey key)
209 478 : {
210 : uint32 i;
211 :
212 1640 : for (i = 0; i < key->nentries; i++)
213 1162 : startScanEntry(index, ginstate, key->scanEntry + i, key->firstCall);
214 :
215 478 : if (key->firstCall)
216 : {
217 26 : memset(key->entryRes, TRUE, sizeof(bool) * key->nentries);
218 26 : key->isFinished = FALSE;
219 26 : key->firstCall = FALSE;
220 :
221 26 : if (GinFuzzySearchLimit > 0)
222 : {
223 : /*
224 : * If all of keys more than threshold we will try to reduce
225 : * result, we hope (and only hope, for intersection operation of
226 : * array our supposition isn't true), that total result will not
227 : * more than minimal predictNumberResult.
228 : */
229 :
230 0 : for (i = 0; i < key->nentries; i++)
231 0 : if (key->scanEntry[i].predictNumberResult <= key->nentries * GinFuzzySearchLimit)
232 0 : return;
233 :
234 0 : for (i = 0; i < key->nentries; i++)
235 0 : if (key->scanEntry[i].predictNumberResult > key->nentries * GinFuzzySearchLimit)
236 : {
237 0 : key->scanEntry[i].predictNumberResult /= key->nentries;
238 0 : key->scanEntry[i].reduceResult = TRUE;
239 : }
240 : }
241 : }
242 : }
243 :
244 : static void
245 : stopScanKey(GinScanKey key)
246 478 : {
247 : uint32 i;
248 :
249 1640 : for (i = 0; i < key->nentries; i++)
250 1162 : stopScanEntry(key->scanEntry + i);
251 478 : }
252 :
253 : static void
254 : startScan(IndexScanDesc scan)
255 478 : {
256 : uint32 i;
257 478 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
258 :
259 956 : for (i = 0; i < so->nkeys; i++)
260 478 : startScanKey(scan->indexRelation, &so->ginstate, so->keys + i);
261 478 : }
262 :
263 : static void
264 : stopScan(IndexScanDesc scan)
265 478 : {
266 : uint32 i;
267 478 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
268 :
269 956 : for (i = 0; i < so->nkeys; i++)
270 478 : stopScanKey(so->keys + i);
271 478 : }
272 :
273 :
274 : static void
275 : entryGetNextItem(Relation index, GinScanEntry entry)
276 396 : {
277 396 : Page page = BufferGetPage(entry->buffer);
278 :
279 396 : entry->offset++;
280 788 : if (entry->offset <= GinPageGetOpaque(page)->maxoff && GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber)
281 : {
282 392 : entry->curItem = *(ItemPointerData *) GinDataPageGetItem(page, entry->offset);
283 : }
284 : else
285 : {
286 4 : BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
287 :
288 4 : LockBuffer(entry->buffer, GIN_UNLOCK);
289 4 : if (blkno == InvalidBlockNumber)
290 : {
291 4 : ReleaseBuffer(entry->buffer);
292 4 : entry->buffer = InvalidBuffer;
293 4 : entry->isFinished = TRUE;
294 : }
295 : else
296 : {
297 0 : entry->buffer = ReleaseAndReadBuffer(entry->buffer, index, blkno);
298 0 : LockBuffer(entry->buffer, GIN_SHARE);
299 0 : entry->offset = InvalidOffsetNumber;
300 0 : entryGetNextItem(index, entry);
301 : }
302 : }
303 396 : }
304 :
305 : #define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
306 : #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
307 :
308 : /*
309 : * Sets entry->curItem to new found heap item pointer for one
310 : * entry of one scan key
311 : */
312 : static bool
313 : entryGetItem(Relation index, GinScanEntry entry)
314 1330 : {
315 1330 : if (entry->master)
316 : {
317 0 : entry->isFinished = entry->master->isFinished;
318 0 : entry->curItem = entry->master->curItem;
319 : }
320 1330 : else if (entry->list)
321 : {
322 934 : entry->offset++;
323 934 : if (entry->offset <= entry->nlist)
324 886 : entry->curItem = entry->list[entry->offset - 1];
325 : else
326 : {
327 48 : ItemPointerSet(&entry->curItem, InvalidBlockNumber, InvalidOffsetNumber);
328 48 : entry->isFinished = TRUE;
329 : }
330 : }
331 : else
332 : {
333 : do
334 : {
335 396 : entryGetNextItem(index, entry);
336 396 : } while (entry->isFinished == FALSE && entry->reduceResult == TRUE && dropItem(entry));
337 : }
338 :
339 1330 : return entry->isFinished;
340 : }
341 :
342 : /*
343 : * Sets key->curItem to new found heap item pointer for one scan key
344 : * returns isFinished!
345 : */
346 : static bool
347 : keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx, GinScanKey key)
348 478 : {
349 : uint32 i;
350 : GinScanEntry entry;
351 : bool res;
352 : MemoryContext oldCtx;
353 :
354 478 : if (key->isFinished)
355 0 : return TRUE;
356 :
357 : do
358 : {
359 : /*
360 : * move forward from previously value and set new curItem, which is
361 : * minimal from entries->curItems
362 : */
363 1108 : ItemPointerSetMax(&key->curItem);
364 4274 : for (i = 0; i < key->nentries; i++)
365 : {
366 3166 : entry = key->scanEntry + i;
367 :
368 3166 : if (key->entryRes[i])
369 : {
370 1332 : if (entry->isFinished == FALSE && entryGetItem(index, entry) == FALSE)
371 : {
372 1278 : if (compareItemPointers(&entry->curItem, &key->curItem) < 0)
373 820 : key->curItem = entry->curItem;
374 : }
375 : else
376 54 : key->entryRes[i] = FALSE;
377 : }
378 1834 : else if (entry->isFinished == FALSE)
379 : {
380 1785 : if (compareItemPointers(&entry->curItem, &key->curItem) < 0)
381 830 : key->curItem = entry->curItem;
382 : }
383 : }
384 :
385 1108 : if (ItemPointerIsMax(&key->curItem))
386 : {
387 : /* all entries are finished */
388 26 : key->isFinished = TRUE;
389 26 : return TRUE;
390 : }
391 :
392 1082 : if (key->nentries == 1)
393 : {
394 : /* we can do not call consistentFn !! */
395 42 : key->entryRes[0] = TRUE;
396 42 : return FALSE;
397 : }
398 :
399 : /* setting up array for consistentFn */
400 4110 : for (i = 0; i < key->nentries; i++)
401 : {
402 3070 : entry = key->scanEntry + i;
403 :
404 4306 : if (entry->isFinished == FALSE && compareItemPointers(&entry->curItem, &key->curItem) == 0)
405 1236 : key->entryRes[i] = TRUE;
406 : else
407 1834 : key->entryRes[i] = FALSE;
408 : }
409 :
410 1040 : oldCtx = MemoryContextSwitchTo(tempCtx);
411 1040 : res = DatumGetBool(FunctionCall3(
412 : &ginstate->consistentFn,
413 : PointerGetDatum(key->entryRes),
414 : UInt16GetDatum(key->strategy),
415 : key->query
416 : ));
417 : MemoryContextSwitchTo(oldCtx);
418 1040 : MemoryContextReset(tempCtx);
419 1040 : } while (!res);
420 :
421 410 : return FALSE;
422 : }
423 :
424 : /*
425 : * Get heap item pointer from scan
426 : * returns true if found
427 : */
428 : static bool
429 : scanGetItem(IndexScanDesc scan, ItemPointerData *item)
430 478 : {
431 : uint32 i;
432 478 : GinScanOpaque so = (GinScanOpaque) scan->opaque;
433 :
434 478 : ItemPointerSetMin(item);
435 930 : for (i = 0; i < so->nkeys; i++)
436 : {
437 478 : GinScanKey key = so->keys + i;
438 :
439 478 : if (keyGetItem(scan->indexRelation, &so->ginstate, so->tempCtx, key) == FALSE)
440 : {
441 452 : if (compareItemPointers(item, &key->curItem) < 0)
442 452 : *item = key->curItem;
443 : }
444 : else
445 26 : return FALSE; /* finished one of keys */
446 : }
447 :
448 904 : for (i = 1; i <= so->nkeys; i++)
449 : {
450 452 : GinScanKey key = so->keys + i - 1;
451 :
452 : for (;;)
453 : {
454 452 : int cmp = compareItemPointers(item, &key->curItem);
455 :
456 452 : if (cmp == 0)
457 452 : break;
458 0 : else if (cmp > 0)
459 : {
460 0 : if (keyGetItem(scan->indexRelation, &so->ginstate, so->tempCtx, key) == TRUE)
461 0 : return FALSE; /* finished one of keys */
462 : }
463 : else
464 : { /* returns to begin */
465 0 : *item = key->curItem;
466 0 : i = 0;
467 0 : break;
468 : }
469 : }
470 : }
471 :
472 452 : return TRUE;
473 : }
474 :
475 : #define GinIsNewKey(s) ( ((GinScanOpaque) scan->opaque)->keys == NULL )
476 : #define GinIsVoidRes(s) ( ((GinScanOpaque) scan->opaque)->isVoidRes == true )
477 :
478 : Datum
479 : gingetmulti(PG_FUNCTION_ARGS)
480 0 : {
481 0 : IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
482 0 : ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
483 0 : int32 max_tids = PG_GETARG_INT32(2);
484 0 : int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3);
485 :
486 0 : if (GinIsNewKey(scan))
487 0 : newScanKey(scan);
488 :
489 0 : *returned_tids = 0;
490 :
491 0 : if (GinIsVoidRes(scan))
492 0 : PG_RETURN_BOOL(false);
493 :
494 0 : startScan(scan);
495 :
496 : do
497 : {
498 0 : if (scanGetItem(scan, tids + *returned_tids))
499 0 : (*returned_tids)++;
500 : else
501 0 : break;
502 0 : } while (*returned_tids < max_tids);
503 :
504 0 : stopScan(scan);
505 :
506 0 : PG_RETURN_BOOL(*returned_tids == max_tids);
507 : }
508 :
509 : Datum
510 : gingettuple(PG_FUNCTION_ARGS)
511 478 : {
512 478 : IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
513 478 : ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
514 : bool res;
515 :
516 478 : if (dir != ForwardScanDirection)
517 0 : elog(ERROR, "Gin doesn't support other scan directions than forward");
518 :
519 478 : if (GinIsNewKey(scan))
520 26 : newScanKey(scan);
521 :
522 478 : if (GinIsVoidRes(scan))
523 0 : PG_RETURN_BOOL(false);
524 :
525 478 : startScan(scan);
526 478 : res = scanGetItem(scan, &scan->xs_ctup.t_self);
527 478 : stopScan(scan);
528 :
529 478 : PG_RETURN_BOOL(res);
530 : }
|