00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include "access/gin_private.h"
00018 #include "access/relscan.h"
00019 #include "miscadmin.h"
00020 #include "utils/datum.h"
00021 #include "utils/memutils.h"
00022
00023
00024 typedef struct pendingPosition
00025 {
00026 Buffer pendingBuffer;
00027 OffsetNumber firstOffset;
00028 OffsetNumber lastOffset;
00029 ItemPointerData item;
00030 bool *hasMatchKey;
00031 } pendingPosition;
00032
00033
00034
00035
00036
00037 static bool
00038 callConsistentFn(GinState *ginstate, GinScanKey key)
00039 {
00040
00041
00042
00043
00044 if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING)
00045 {
00046 key->recheckCurItem = false;
00047 return true;
00048 }
00049
00050
00051
00052
00053
00054 key->recheckCurItem = true;
00055
00056 return DatumGetBool(FunctionCall8Coll(&ginstate->consistentFn[key->attnum - 1],
00057 ginstate->supportCollation[key->attnum - 1],
00058 PointerGetDatum(key->entryRes),
00059 UInt16GetDatum(key->strategy),
00060 key->query,
00061 UInt32GetDatum(key->nuserentries),
00062 PointerGetDatum(key->extra_data),
00063 PointerGetDatum(&key->recheckCurItem),
00064 PointerGetDatum(key->queryValues),
00065 PointerGetDatum(key->queryCategories)));
00066 }
00067
00068
00069
00070
00071 static bool
00072 findItemInPostingPage(Page page, ItemPointer item, OffsetNumber *off)
00073 {
00074 OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
00075 int res;
00076
00077 if (GinPageGetOpaque(page)->flags & GIN_DELETED)
00078
00079 return false;
00080
00081
00082
00083
00084 for (*off = FirstOffsetNumber; *off <= maxoff; (*off)++)
00085 {
00086 res = ginCompareItemPointers(item, (ItemPointer) GinDataPageGetItem(page, *off));
00087
00088 if (res <= 0)
00089 return true;
00090 }
00091
00092 return false;
00093 }
00094
00095
00096
00097
00098 static bool
00099 moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack)
00100 {
00101 Page page = BufferGetPage(stack->buffer);
00102
00103 if (stack->off > PageGetMaxOffsetNumber(page))
00104 {
00105
00106
00107
00108 stack->blkno = GinPageGetOpaque(page)->rightlink;
00109
00110 if (GinPageRightMost(page))
00111 return false;
00112
00113 LockBuffer(stack->buffer, GIN_UNLOCK);
00114 stack->buffer = ReleaseAndReadBuffer(stack->buffer,
00115 btree->index,
00116 stack->blkno);
00117 LockBuffer(stack->buffer, GIN_SHARE);
00118 stack->off = FirstOffsetNumber;
00119 }
00120
00121 return true;
00122 }
00123
00124
00125
00126
00127
00128 static void
00129 scanPostingTree(Relation index, GinScanEntry scanEntry,
00130 BlockNumber rootPostingTree)
00131 {
00132 GinPostingTreeScan *gdi;
00133 Buffer buffer;
00134 Page page;
00135 BlockNumber blkno;
00136
00137
00138 gdi = ginPrepareScanPostingTree(index, rootPostingTree, TRUE);
00139
00140 buffer = ginScanBeginPostingTree(gdi);
00141 IncrBufferRefCount(buffer);
00142
00143 freeGinBtreeStack(gdi->stack);
00144 pfree(gdi);
00145
00146
00147
00148
00149 for (;;)
00150 {
00151 page = BufferGetPage(buffer);
00152
00153 if ((GinPageGetOpaque(page)->flags & GIN_DELETED) == 0 &&
00154 GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber)
00155 {
00156 tbm_add_tuples(scanEntry->matchBitmap,
00157 (ItemPointer) GinDataPageGetItem(page, FirstOffsetNumber),
00158 GinPageGetOpaque(page)->maxoff, false);
00159 scanEntry->predictNumberResult += GinPageGetOpaque(page)->maxoff;
00160 }
00161
00162 if (GinPageRightMost(page))
00163 break;
00164
00165 blkno = GinPageGetOpaque(page)->rightlink;
00166 LockBuffer(buffer, GIN_UNLOCK);
00167 buffer = ReleaseAndReadBuffer(buffer, index, blkno);
00168 LockBuffer(buffer, GIN_SHARE);
00169 }
00170
00171 UnlockReleaseBuffer(buffer);
00172 }
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187 static bool
00188 collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
00189 GinScanEntry scanEntry)
00190 {
00191 OffsetNumber attnum;
00192 Form_pg_attribute attr;
00193
00194
00195 scanEntry->matchBitmap = tbm_create(work_mem * 1024L);
00196
00197
00198 if (scanEntry->isPartialMatch &&
00199 scanEntry->queryCategory != GIN_CAT_NORM_KEY)
00200 return true;
00201
00202
00203 attnum = scanEntry->attnum;
00204 attr = btree->ginstate->origTupdesc->attrs[attnum - 1];
00205
00206 for (;;)
00207 {
00208 Page page;
00209 IndexTuple itup;
00210 Datum idatum;
00211 GinNullCategory icategory;
00212
00213
00214
00215
00216 if (moveRightIfItNeeded(btree, stack) == false)
00217 return true;
00218
00219 page = BufferGetPage(stack->buffer);
00220 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
00221
00222
00223
00224
00225 if (gintuple_get_attrnum(btree->ginstate, itup) != attnum)
00226 return true;
00227
00228
00229 idatum = gintuple_get_key(btree->ginstate, itup, &icategory);
00230
00231
00232
00233
00234 if (scanEntry->isPartialMatch)
00235 {
00236 int32 cmp;
00237
00238
00239
00240
00241
00242 if (icategory != GIN_CAT_NORM_KEY)
00243 return true;
00244
00245
00246
00247
00248
00249
00250
00251
00252 cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
00253 btree->ginstate->supportCollation[attnum - 1],
00254 scanEntry->queryKey,
00255 idatum,
00256 UInt16GetDatum(scanEntry->strategy),
00257 PointerGetDatum(scanEntry->extra_data)));
00258
00259 if (cmp > 0)
00260 return true;
00261 else if (cmp < 0)
00262 {
00263 stack->off++;
00264 continue;
00265 }
00266 }
00267 else if (scanEntry->searchMode == GIN_SEARCH_MODE_ALL)
00268 {
00269
00270
00271
00272
00273
00274
00275 if (icategory == GIN_CAT_NULL_ITEM)
00276 return true;
00277 }
00278
00279
00280
00281
00282 if (GinIsPostingTree(itup))
00283 {
00284 BlockNumber rootPostingTree = GinGetPostingTree(itup);
00285
00286
00287
00288
00289
00290
00291
00292
00293 if (icategory == GIN_CAT_NORM_KEY)
00294 idatum = datumCopy(idatum, attr->attbyval, attr->attlen);
00295
00296 LockBuffer(stack->buffer, GIN_UNLOCK);
00297
00298
00299 scanPostingTree(btree->index, scanEntry, rootPostingTree);
00300
00301
00302
00303
00304
00305 LockBuffer(stack->buffer, GIN_SHARE);
00306 page = BufferGetPage(stack->buffer);
00307 if (!GinPageIsLeaf(page))
00308 {
00309
00310
00311
00312
00313
00314 return false;
00315 }
00316
00317
00318 for (;;)
00319 {
00320 Datum newDatum;
00321 GinNullCategory newCategory;
00322
00323 if (moveRightIfItNeeded(btree, stack) == false)
00324 elog(ERROR, "lost saved point in index");
00325
00326 page = BufferGetPage(stack->buffer);
00327 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
00328
00329 if (gintuple_get_attrnum(btree->ginstate, itup) != attnum)
00330 elog(ERROR, "lost saved point in index");
00331 newDatum = gintuple_get_key(btree->ginstate, itup,
00332 &newCategory);
00333
00334 if (ginCompareEntries(btree->ginstate, attnum,
00335 newDatum, newCategory,
00336 idatum, icategory) == 0)
00337 break;
00338
00339 stack->off++;
00340 }
00341
00342 if (icategory == GIN_CAT_NORM_KEY && !attr->attbyval)
00343 pfree(DatumGetPointer(idatum));
00344 }
00345 else
00346 {
00347 tbm_add_tuples(scanEntry->matchBitmap,
00348 GinGetPosting(itup), GinGetNPosting(itup), false);
00349 scanEntry->predictNumberResult += GinGetNPosting(itup);
00350 }
00351
00352
00353
00354
00355 stack->off++;
00356 }
00357 }
00358
00359
00360
00361
00362 static void
00363 startScanEntry(GinState *ginstate, GinScanEntry entry)
00364 {
00365 GinBtreeData btreeEntry;
00366 GinBtreeStack *stackEntry;
00367 Page page;
00368 bool needUnlock;
00369
00370 restartScanEntry:
00371 entry->buffer = InvalidBuffer;
00372 ItemPointerSetMin(&entry->curItem);
00373 entry->offset = InvalidOffsetNumber;
00374 entry->list = NULL;
00375 entry->nlist = 0;
00376 entry->matchBitmap = NULL;
00377 entry->matchResult = NULL;
00378 entry->reduceResult = FALSE;
00379 entry->predictNumberResult = 0;
00380
00381
00382
00383
00384
00385 ginPrepareEntryScan(&btreeEntry, entry->attnum,
00386 entry->queryKey, entry->queryCategory,
00387 ginstate);
00388 btreeEntry.searchMode = TRUE;
00389 stackEntry = ginFindLeafPage(&btreeEntry, NULL);
00390 page = BufferGetPage(stackEntry->buffer);
00391 needUnlock = TRUE;
00392
00393 entry->isFinished = TRUE;
00394
00395 if (entry->isPartialMatch ||
00396 entry->queryCategory == GIN_CAT_EMPTY_QUERY)
00397 {
00398
00399
00400
00401
00402
00403
00404
00405 btreeEntry.findItem(&btreeEntry, stackEntry);
00406 if (collectMatchBitmap(&btreeEntry, stackEntry, entry) == false)
00407 {
00408
00409
00410
00411
00412
00413 if (entry->matchBitmap)
00414 {
00415 if (entry->matchIterator)
00416 tbm_end_iterate(entry->matchIterator);
00417 entry->matchIterator = NULL;
00418 tbm_free(entry->matchBitmap);
00419 entry->matchBitmap = NULL;
00420 }
00421 LockBuffer(stackEntry->buffer, GIN_UNLOCK);
00422 freeGinBtreeStack(stackEntry);
00423 goto restartScanEntry;
00424 }
00425
00426 if (entry->matchBitmap && !tbm_is_empty(entry->matchBitmap))
00427 {
00428 entry->matchIterator = tbm_begin_iterate(entry->matchBitmap);
00429 entry->isFinished = FALSE;
00430 }
00431 }
00432 else if (btreeEntry.findItem(&btreeEntry, stackEntry))
00433 {
00434 IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stackEntry->off));
00435
00436 if (GinIsPostingTree(itup))
00437 {
00438 BlockNumber rootPostingTree = GinGetPostingTree(itup);
00439 GinPostingTreeScan *gdi;
00440 Page page;
00441
00442
00443
00444
00445
00446
00447
00448
00449 LockBuffer(stackEntry->buffer, GIN_UNLOCK);
00450 needUnlock = FALSE;
00451 gdi = ginPrepareScanPostingTree(ginstate->index, rootPostingTree, TRUE);
00452
00453 entry->buffer = ginScanBeginPostingTree(gdi);
00454
00455
00456
00457
00458
00459
00460 IncrBufferRefCount(entry->buffer);
00461
00462 page = BufferGetPage(entry->buffer);
00463 entry->predictNumberResult = gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff;
00464
00465
00466
00467
00468 entry->list = (ItemPointerData *) palloc(BLCKSZ);
00469 entry->nlist = GinPageGetOpaque(page)->maxoff;
00470 memcpy(entry->list, GinDataPageGetItem(page, FirstOffsetNumber),
00471 GinPageGetOpaque(page)->maxoff * sizeof(ItemPointerData));
00472
00473 LockBuffer(entry->buffer, GIN_UNLOCK);
00474 freeGinBtreeStack(gdi->stack);
00475 pfree(gdi);
00476 entry->isFinished = FALSE;
00477 }
00478 else if (GinGetNPosting(itup) > 0)
00479 {
00480 entry->nlist = GinGetNPosting(itup);
00481 entry->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * entry->nlist);
00482 memcpy(entry->list, GinGetPosting(itup), sizeof(ItemPointerData) * entry->nlist);
00483 entry->isFinished = FALSE;
00484 }
00485 }
00486
00487 if (needUnlock)
00488 LockBuffer(stackEntry->buffer, GIN_UNLOCK);
00489 freeGinBtreeStack(stackEntry);
00490 }
00491
00492 static void
00493 startScanKey(GinState *ginstate, GinScanKey key)
00494 {
00495 ItemPointerSetMin(&key->curItem);
00496 key->curItemMatches = false;
00497 key->recheckCurItem = false;
00498 key->isFinished = false;
00499 }
00500
00501 static void
00502 startScan(IndexScanDesc scan)
00503 {
00504 GinScanOpaque so = (GinScanOpaque) scan->opaque;
00505 GinState *ginstate = &so->ginstate;
00506 uint32 i;
00507
00508 for (i = 0; i < so->totalentries; i++)
00509 startScanEntry(ginstate, so->entries[i]);
00510
00511 if (GinFuzzySearchLimit > 0)
00512 {
00513
00514
00515
00516
00517
00518
00519
00520 for (i = 0; i < so->totalentries; i++)
00521 if (so->entries[i]->predictNumberResult <= so->totalentries * GinFuzzySearchLimit)
00522 return;
00523
00524 for (i = 0; i < so->totalentries; i++)
00525 if (so->entries[i]->predictNumberResult > so->totalentries * GinFuzzySearchLimit)
00526 {
00527 so->entries[i]->predictNumberResult /= so->totalentries;
00528 so->entries[i]->reduceResult = TRUE;
00529 }
00530 }
00531
00532 for (i = 0; i < so->nkeys; i++)
00533 startScanKey(ginstate, so->keys + i);
00534 }
00535
00536
00537
00538
00539
00540
00541 static void
00542 entryGetNextItem(GinState *ginstate, GinScanEntry entry)
00543 {
00544 Page page;
00545 BlockNumber blkno;
00546
00547 for (;;)
00548 {
00549 if (entry->offset < entry->nlist)
00550 {
00551 entry->curItem = entry->list[entry->offset++];
00552 return;
00553 }
00554
00555 LockBuffer(entry->buffer, GIN_SHARE);
00556 page = BufferGetPage(entry->buffer);
00557 for (;;)
00558 {
00559
00560
00561
00562
00563
00564 blkno = GinPageGetOpaque(page)->rightlink;
00565
00566 LockBuffer(entry->buffer, GIN_UNLOCK);
00567 if (blkno == InvalidBlockNumber)
00568 {
00569 ReleaseBuffer(entry->buffer);
00570 ItemPointerSetInvalid(&entry->curItem);
00571 entry->buffer = InvalidBuffer;
00572 entry->isFinished = TRUE;
00573 return;
00574 }
00575
00576 entry->buffer = ReleaseAndReadBuffer(entry->buffer,
00577 ginstate->index,
00578 blkno);
00579 LockBuffer(entry->buffer, GIN_SHARE);
00580 page = BufferGetPage(entry->buffer);
00581
00582 entry->offset = InvalidOffsetNumber;
00583 if (!ItemPointerIsValid(&entry->curItem) ||
00584 findItemInPostingPage(page, &entry->curItem, &entry->offset))
00585 {
00586
00587
00588
00589 entry->nlist = GinPageGetOpaque(page)->maxoff;
00590 memcpy(entry->list, GinDataPageGetItem(page, FirstOffsetNumber),
00591 GinPageGetOpaque(page)->maxoff * sizeof(ItemPointerData));
00592
00593 LockBuffer(entry->buffer, GIN_UNLOCK);
00594
00595 if (!ItemPointerIsValid(&entry->curItem) ||
00596 ginCompareItemPointers(&entry->curItem,
00597 entry->list + entry->offset - 1) == 0)
00598 {
00599
00600
00601
00602
00603 break;
00604 }
00605
00606
00607
00608
00609 entry->curItem = entry->list[entry->offset - 1];
00610
00611 return;
00612 }
00613 }
00614 }
00615 }
00616
00617 #define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
00618 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633 static void
00634 entryGetItem(GinState *ginstate, GinScanEntry entry)
00635 {
00636 Assert(!entry->isFinished);
00637
00638 if (entry->matchBitmap)
00639 {
00640 do
00641 {
00642 if (entry->matchResult == NULL ||
00643 entry->offset >= entry->matchResult->ntuples)
00644 {
00645 entry->matchResult = tbm_iterate(entry->matchIterator);
00646
00647 if (entry->matchResult == NULL)
00648 {
00649 ItemPointerSetInvalid(&entry->curItem);
00650 tbm_end_iterate(entry->matchIterator);
00651 entry->matchIterator = NULL;
00652 entry->isFinished = TRUE;
00653 break;
00654 }
00655
00656
00657
00658
00659
00660
00661
00662 entry->offset = 0;
00663 }
00664
00665 if (entry->matchResult->ntuples < 0)
00666 {
00667
00668
00669
00670 ItemPointerSetLossyPage(&entry->curItem,
00671 entry->matchResult->blockno);
00672
00673
00674
00675
00676
00677
00678 break;
00679 }
00680
00681 ItemPointerSet(&entry->curItem,
00682 entry->matchResult->blockno,
00683 entry->matchResult->offsets[entry->offset]);
00684 entry->offset++;
00685 } while (entry->reduceResult == TRUE && dropItem(entry));
00686 }
00687 else if (!BufferIsValid(entry->buffer))
00688 {
00689 entry->offset++;
00690 if (entry->offset <= entry->nlist)
00691 entry->curItem = entry->list[entry->offset - 1];
00692 else
00693 {
00694 ItemPointerSetInvalid(&entry->curItem);
00695 entry->isFinished = TRUE;
00696 }
00697 }
00698 else
00699 {
00700 do
00701 {
00702 entryGetNextItem(ginstate, entry);
00703 } while (entry->isFinished == FALSE &&
00704 entry->reduceResult == TRUE &&
00705 dropItem(entry));
00706 }
00707 }
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729 static void
00730 keyGetItem(GinState *ginstate, MemoryContext tempCtx, GinScanKey key)
00731 {
00732 ItemPointerData minItem;
00733 ItemPointerData curPageLossy;
00734 uint32 i;
00735 uint32 lossyEntry;
00736 bool haveLossyEntry;
00737 GinScanEntry entry;
00738 bool res;
00739 MemoryContext oldCtx;
00740
00741 Assert(!key->isFinished);
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751 ItemPointerSetMax(&minItem);
00752
00753 for (i = 0; i < key->nentries; i++)
00754 {
00755 entry = key->scanEntry[i];
00756 if (entry->isFinished == FALSE &&
00757 ginCompareItemPointers(&entry->curItem, &minItem) < 0)
00758 minItem = entry->curItem;
00759 }
00760
00761 if (ItemPointerIsMax(&minItem))
00762 {
00763
00764 key->isFinished = TRUE;
00765 return;
00766 }
00767
00768
00769
00770
00771
00772
00773 if (ginCompareItemPointers(&key->curItem, &minItem) >= 0)
00774 return;
00775
00776
00777
00778
00779 key->curItem = minItem;
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805 ItemPointerSetLossyPage(&curPageLossy,
00806 GinItemPointerGetBlockNumber(&key->curItem));
00807
00808 lossyEntry = 0;
00809 haveLossyEntry = false;
00810 for (i = 0; i < key->nentries; i++)
00811 {
00812 entry = key->scanEntry[i];
00813 if (entry->isFinished == FALSE &&
00814 ginCompareItemPointers(&entry->curItem, &curPageLossy) == 0)
00815 {
00816 if (haveLossyEntry)
00817 {
00818
00819 key->curItem = curPageLossy;
00820 key->curItemMatches = true;
00821 key->recheckCurItem = true;
00822 return;
00823 }
00824 lossyEntry = i;
00825 haveLossyEntry = true;
00826 }
00827 }
00828
00829
00830 oldCtx = MemoryContextSwitchTo(tempCtx);
00831
00832 if (haveLossyEntry)
00833 {
00834
00835 memset(key->entryRes, FALSE, key->nentries);
00836 key->entryRes[lossyEntry] = TRUE;
00837
00838 if (callConsistentFn(ginstate, key))
00839 {
00840
00841 MemoryContextSwitchTo(oldCtx);
00842 MemoryContextReset(tempCtx);
00843
00844
00845 key->curItem = curPageLossy;
00846 key->curItemMatches = true;
00847 key->recheckCurItem = true;
00848 return;
00849 }
00850 }
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864 for (i = 0; i < key->nentries; i++)
00865 {
00866 entry = key->scanEntry[i];
00867 if (entry->isFinished == FALSE &&
00868 ginCompareItemPointers(&entry->curItem, &key->curItem) == 0)
00869 key->entryRes[i] = TRUE;
00870 else
00871 key->entryRes[i] = FALSE;
00872 }
00873 if (haveLossyEntry)
00874 key->entryRes[lossyEntry] = TRUE;
00875
00876 res = callConsistentFn(ginstate, key);
00877
00878 if (!res && haveLossyEntry && lossyEntry < key->nuserentries)
00879 {
00880
00881 key->entryRes[lossyEntry] = FALSE;
00882
00883 res = callConsistentFn(ginstate, key);
00884 }
00885
00886 key->curItemMatches = res;
00887
00888 if (haveLossyEntry)
00889 key->recheckCurItem = true;
00890
00891
00892 MemoryContextSwitchTo(oldCtx);
00893 MemoryContextReset(tempCtx);
00894 }
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905 static bool
00906 scanGetItem(IndexScanDesc scan, ItemPointer advancePast,
00907 ItemPointerData *item, bool *recheck)
00908 {
00909 GinScanOpaque so = (GinScanOpaque) scan->opaque;
00910 GinState *ginstate = &so->ginstate;
00911 ItemPointerData myAdvancePast = *advancePast;
00912 uint32 i;
00913 bool allFinished;
00914 bool match;
00915
00916 for (;;)
00917 {
00918
00919
00920
00921
00922
00923 allFinished = TRUE;
00924
00925 for (i = 0; i < so->totalentries; i++)
00926 {
00927 GinScanEntry entry = so->entries[i];
00928
00929 while (entry->isFinished == FALSE &&
00930 ginCompareItemPointers(&entry->curItem,
00931 &myAdvancePast) <= 0)
00932 entryGetItem(ginstate, entry);
00933
00934 if (entry->isFinished == FALSE)
00935 allFinished = FALSE;
00936 }
00937
00938 if (allFinished)
00939 {
00940
00941 return false;
00942 }
00943
00944
00945
00946
00947
00948
00949
00950 ItemPointerSetMax(item);
00951
00952 for (i = 0; i < so->nkeys; i++)
00953 {
00954 GinScanKey key = so->keys + i;
00955
00956 keyGetItem(&so->ginstate, so->tempCtx, key);
00957
00958 if (key->isFinished)
00959 return false;
00960
00961 if (ginCompareItemPointers(&key->curItem, item) < 0)
00962 *item = key->curItem;
00963 }
00964
00965 Assert(!ItemPointerIsMax(item));
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988 match = true;
00989 for (i = 0; i < so->nkeys; i++)
00990 {
00991 GinScanKey key = so->keys + i;
00992
00993 if (key->curItemMatches)
00994 {
00995 if (ginCompareItemPointers(item, &key->curItem) == 0)
00996 continue;
00997 if (ItemPointerIsLossyPage(&key->curItem) &&
00998 GinItemPointerGetBlockNumber(&key->curItem) ==
00999 GinItemPointerGetBlockNumber(item))
01000 continue;
01001 }
01002 match = false;
01003 break;
01004 }
01005
01006 if (match)
01007 break;
01008
01009
01010
01011
01012
01013 myAdvancePast = *item;
01014 }
01015
01016
01017
01018
01019 *recheck = false;
01020 for (i = 0; i < so->nkeys; i++)
01021 {
01022 GinScanKey key = so->keys + i;
01023
01024 if (key->recheckCurItem)
01025 {
01026 *recheck = true;
01027 break;
01028 }
01029 }
01030
01031 return TRUE;
01032 }
01033
01034
01035
01036
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047
01048
01049
01050 static bool
01051 scanGetCandidate(IndexScanDesc scan, pendingPosition *pos)
01052 {
01053 OffsetNumber maxoff;
01054 Page page;
01055 IndexTuple itup;
01056
01057 ItemPointerSetInvalid(&pos->item);
01058 for (;;)
01059 {
01060 page = BufferGetPage(pos->pendingBuffer);
01061
01062 maxoff = PageGetMaxOffsetNumber(page);
01063 if (pos->firstOffset > maxoff)
01064 {
01065 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
01066
01067 if (blkno == InvalidBlockNumber)
01068 {
01069 UnlockReleaseBuffer(pos->pendingBuffer);
01070 pos->pendingBuffer = InvalidBuffer;
01071
01072 return false;
01073 }
01074 else
01075 {
01076
01077
01078
01079
01080
01081
01082 Buffer tmpbuf = ReadBuffer(scan->indexRelation, blkno);
01083
01084 LockBuffer(tmpbuf, GIN_SHARE);
01085 UnlockReleaseBuffer(pos->pendingBuffer);
01086
01087 pos->pendingBuffer = tmpbuf;
01088 pos->firstOffset = FirstOffsetNumber;
01089 }
01090 }
01091 else
01092 {
01093 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, pos->firstOffset));
01094 pos->item = itup->t_tid;
01095 if (GinPageHasFullRow(page))
01096 {
01097
01098
01099
01100 for (pos->lastOffset = pos->firstOffset + 1; pos->lastOffset <= maxoff; pos->lastOffset++)
01101 {
01102 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, pos->lastOffset));
01103 if (!ItemPointerEquals(&pos->item, &itup->t_tid))
01104 break;
01105 }
01106 }
01107 else
01108 {
01109
01110
01111
01112 pos->lastOffset = maxoff + 1;
01113 }
01114
01115
01116
01117
01118
01119
01120 break;
01121 }
01122 }
01123
01124 return true;
01125 }
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137 static bool
01138 matchPartialInPendingList(GinState *ginstate, Page page,
01139 OffsetNumber off, OffsetNumber maxoff,
01140 GinScanEntry entry,
01141 Datum *datum, GinNullCategory *category,
01142 bool *datumExtracted)
01143 {
01144 IndexTuple itup;
01145 int32 cmp;
01146
01147
01148 if (entry->queryCategory != GIN_CAT_NORM_KEY)
01149 return false;
01150
01151 while (off < maxoff)
01152 {
01153 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
01154
01155 if (gintuple_get_attrnum(ginstate, itup) != entry->attnum)
01156 return false;
01157
01158 if (datumExtracted[off - 1] == false)
01159 {
01160 datum[off - 1] = gintuple_get_key(ginstate, itup,
01161 &category[off - 1]);
01162 datumExtracted[off - 1] = true;
01163 }
01164
01165
01166 if (category[off - 1] != GIN_CAT_NORM_KEY)
01167 return false;
01168
01169
01170
01171
01172
01173
01174
01175
01176 cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
01177 ginstate->supportCollation[entry->attnum - 1],
01178 entry->queryKey,
01179 datum[off - 1],
01180 UInt16GetDatum(entry->strategy),
01181 PointerGetDatum(entry->extra_data)));
01182 if (cmp == 0)
01183 return true;
01184 else if (cmp > 0)
01185 return false;
01186
01187 off++;
01188 }
01189
01190 return false;
01191 }
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205 static bool
01206 collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
01207 {
01208 GinScanOpaque so = (GinScanOpaque) scan->opaque;
01209 OffsetNumber attrnum;
01210 Page page;
01211 IndexTuple itup;
01212 int i,
01213 j;
01214
01215
01216
01217
01218 for (i = 0; i < so->nkeys; i++)
01219 {
01220 GinScanKey key = so->keys + i;
01221
01222 memset(key->entryRes, FALSE, key->nentries);
01223 }
01224 memset(pos->hasMatchKey, FALSE, so->nkeys);
01225
01226
01227
01228
01229
01230 for (;;)
01231 {
01232 Datum datum[BLCKSZ / sizeof(IndexTupleData)];
01233 GinNullCategory category[BLCKSZ / sizeof(IndexTupleData)];
01234 bool datumExtracted[BLCKSZ / sizeof(IndexTupleData)];
01235
01236 Assert(pos->lastOffset > pos->firstOffset);
01237 memset(datumExtracted + pos->firstOffset - 1, 0,
01238 sizeof(bool) * (pos->lastOffset - pos->firstOffset));
01239
01240 page = BufferGetPage(pos->pendingBuffer);
01241
01242 for (i = 0; i < so->nkeys; i++)
01243 {
01244 GinScanKey key = so->keys + i;
01245
01246 for (j = 0; j < key->nentries; j++)
01247 {
01248 GinScanEntry entry = key->scanEntry[j];
01249 OffsetNumber StopLow = pos->firstOffset,
01250 StopHigh = pos->lastOffset,
01251 StopMiddle;
01252
01253
01254 if (key->entryRes[j])
01255 continue;
01256
01257
01258
01259
01260
01261
01262
01263 while (StopLow < StopHigh)
01264 {
01265 int res;
01266
01267 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
01268
01269 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, StopMiddle));
01270
01271 attrnum = gintuple_get_attrnum(&so->ginstate, itup);
01272
01273 if (key->attnum < attrnum)
01274 {
01275 StopHigh = StopMiddle;
01276 continue;
01277 }
01278 if (key->attnum > attrnum)
01279 {
01280 StopLow = StopMiddle + 1;
01281 continue;
01282 }
01283
01284 if (datumExtracted[StopMiddle - 1] == false)
01285 {
01286 datum[StopMiddle - 1] =
01287 gintuple_get_key(&so->ginstate, itup,
01288 &category[StopMiddle - 1]);
01289 datumExtracted[StopMiddle - 1] = true;
01290 }
01291
01292 if (entry->queryCategory == GIN_CAT_EMPTY_QUERY)
01293 {
01294
01295 if (entry->searchMode == GIN_SEARCH_MODE_ALL)
01296 {
01297
01298 if (category[StopMiddle - 1] == GIN_CAT_NULL_ITEM)
01299 res = -1;
01300 else
01301 res = 0;
01302 }
01303 else
01304 {
01305
01306 res = 0;
01307 }
01308 }
01309 else
01310 {
01311 res = ginCompareEntries(&so->ginstate,
01312 entry->attnum,
01313 entry->queryKey,
01314 entry->queryCategory,
01315 datum[StopMiddle - 1],
01316 category[StopMiddle - 1]);
01317 }
01318
01319 if (res == 0)
01320 {
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330 if (entry->isPartialMatch)
01331 key->entryRes[j] =
01332 matchPartialInPendingList(&so->ginstate,
01333 page,
01334 StopMiddle,
01335 pos->lastOffset,
01336 entry,
01337 datum,
01338 category,
01339 datumExtracted);
01340 else
01341 key->entryRes[j] = true;
01342
01343
01344 break;
01345 }
01346 else if (res < 0)
01347 StopHigh = StopMiddle;
01348 else
01349 StopLow = StopMiddle + 1;
01350 }
01351
01352 if (StopLow >= StopHigh && entry->isPartialMatch)
01353 {
01354
01355
01356
01357
01358
01359
01360
01361
01362 key->entryRes[j] =
01363 matchPartialInPendingList(&so->ginstate,
01364 page,
01365 StopHigh,
01366 pos->lastOffset,
01367 entry,
01368 datum,
01369 category,
01370 datumExtracted);
01371 }
01372
01373 pos->hasMatchKey[i] |= key->entryRes[j];
01374 }
01375 }
01376
01377
01378 pos->firstOffset = pos->lastOffset;
01379
01380 if (GinPageHasFullRow(page))
01381 {
01382
01383
01384
01385
01386 break;
01387 }
01388 else
01389 {
01390
01391
01392
01393
01394 ItemPointerData item = pos->item;
01395
01396 if (scanGetCandidate(scan, pos) == false ||
01397 !ItemPointerEquals(&pos->item, &item))
01398 elog(ERROR, "could not find additional pending pages for same heap tuple");
01399 }
01400 }
01401
01402
01403
01404
01405 for (i = 0; i < so->nkeys; i++)
01406 {
01407 if (pos->hasMatchKey[i] == false)
01408 return false;
01409 }
01410
01411 return true;
01412 }
01413
01414
01415
01416
01417 static void
01418 scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
01419 {
01420 GinScanOpaque so = (GinScanOpaque) scan->opaque;
01421 MemoryContext oldCtx;
01422 bool recheck,
01423 match;
01424 int i;
01425 pendingPosition pos;
01426 Buffer metabuffer = ReadBuffer(scan->indexRelation, GIN_METAPAGE_BLKNO);
01427 BlockNumber blkno;
01428
01429 *ntids = 0;
01430
01431 LockBuffer(metabuffer, GIN_SHARE);
01432 blkno = GinPageGetMeta(BufferGetPage(metabuffer))->head;
01433
01434
01435
01436
01437
01438 if (blkno == InvalidBlockNumber)
01439 {
01440
01441 UnlockReleaseBuffer(metabuffer);
01442 return;
01443 }
01444
01445 pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno);
01446 LockBuffer(pos.pendingBuffer, GIN_SHARE);
01447 pos.firstOffset = FirstOffsetNumber;
01448 UnlockReleaseBuffer(metabuffer);
01449 pos.hasMatchKey = palloc(sizeof(bool) * so->nkeys);
01450
01451
01452
01453
01454
01455 while (scanGetCandidate(scan, &pos))
01456 {
01457
01458
01459
01460
01461
01462
01463
01464 if (!collectMatchesForHeapRow(scan, &pos))
01465 continue;
01466
01467
01468
01469
01470
01471 oldCtx = MemoryContextSwitchTo(so->tempCtx);
01472 recheck = false;
01473 match = true;
01474
01475 for (i = 0; i < so->nkeys; i++)
01476 {
01477 GinScanKey key = so->keys + i;
01478
01479 if (!callConsistentFn(&so->ginstate, key))
01480 {
01481 match = false;
01482 break;
01483 }
01484 recheck |= key->recheckCurItem;
01485 }
01486
01487 MemoryContextSwitchTo(oldCtx);
01488 MemoryContextReset(so->tempCtx);
01489
01490 if (match)
01491 {
01492 tbm_add_tuples(tbm, &pos.item, 1, recheck);
01493 (*ntids)++;
01494 }
01495 }
01496
01497 pfree(pos.hasMatchKey);
01498 }
01499
01500
01501 #define GinIsNewKey(s) ( ((GinScanOpaque) scan->opaque)->keys == NULL )
01502 #define GinIsVoidRes(s) ( ((GinScanOpaque) scan->opaque)->isVoidRes )
01503
01504 Datum
01505 gingetbitmap(PG_FUNCTION_ARGS)
01506 {
01507 IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
01508 TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
01509 int64 ntids;
01510 ItemPointerData iptr;
01511 bool recheck;
01512
01513
01514
01515
01516 if (GinIsNewKey(scan))
01517 ginNewScanKey(scan);
01518
01519 if (GinIsVoidRes(scan))
01520 PG_RETURN_INT64(0);
01521
01522 ntids = 0;
01523
01524
01525
01526
01527
01528
01529
01530
01531
01532
01533
01534 scanPendingInsert(scan, tbm, &ntids);
01535
01536
01537
01538
01539 startScan(scan);
01540
01541 ItemPointerSetMin(&iptr);
01542
01543 for (;;)
01544 {
01545 CHECK_FOR_INTERRUPTS();
01546
01547 if (!scanGetItem(scan, &iptr, &iptr, &recheck))
01548 break;
01549
01550 if (ItemPointerIsLossyPage(&iptr))
01551 tbm_add_page(tbm, ItemPointerGetBlockNumber(&iptr));
01552 else
01553 tbm_add_tuples(tbm, &iptr, 1, recheck);
01554 ntids++;
01555 }
01556
01557 PG_RETURN_INT64(ntids);
01558 }