8
8
* Portions Copyright (c) 1994, Regents of the University of California
9
9
*
10
10
* IDENTIFICATION
11
- * $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.31 2010/07/31 00:30:54 tgl Exp $
11
+ * $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.32 2010/08/01 19:16:39 tgl Exp $
12
12
*-------------------------------------------------------------------------
13
13
*/
14
14
@@ -43,13 +43,12 @@ findItemInPage(Page page, ItemPointer item, OffsetNumber *off)
43
43
int res ;
44
44
45
45
if (GinPageGetOpaque (page )-> flags & GIN_DELETED )
46
- /* page was deleted by concurrent vacuum */
46
+ /* page was deleted by concurrent vacuum */
47
47
return false;
48
48
49
49
/*
50
50
* scan page to find equal or first greater value
51
51
*/
52
-
53
52
for (* off = FirstOffsetNumber ; * off <= maxoff ; (* off )++ )
54
53
{
55
54
res = compareItemPointers (item , (ItemPointer ) GinDataPageGetItem (page , * off ));
@@ -527,12 +526,40 @@ entryGetNextItem(Relation index, GinScanEntry entry)
527
526
}
528
527
}
529
528
529
+ /* convenience function for invoking a key's consistentFn */
530
+ static inline bool
531
+ callConsistentFn (GinState * ginstate , GinScanKey key )
532
+ {
533
+ /*
534
+ * Initialize recheckCurItem in case the consistentFn doesn't know it
535
+ * should set it. The safe assumption in that case is to force recheck.
536
+ */
537
+ key -> recheckCurItem = true;
538
+
539
+ return DatumGetBool (FunctionCall6 (& ginstate -> consistentFn [key -> attnum - 1 ],
540
+ PointerGetDatum (key -> entryRes ),
541
+ UInt16GetDatum (key -> strategy ),
542
+ key -> query ,
543
+ UInt32GetDatum (key -> nentries ),
544
+ PointerGetDatum (key -> extra_data ),
545
+ PointerGetDatum (& key -> recheckCurItem )));
546
+ }
547
+
530
548
#define gin_rand () (((double) random()) / ((double) MAX_RANDOM_VALUE))
531
549
#define dropItem (e ) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
532
550
533
551
/*
534
552
* Sets entry->curItem to next heap item pointer for one entry of one scan key,
535
553
* or sets entry->isFinished to TRUE if there are no more.
554
+ *
555
+ * Item pointers must be returned in ascending order.
556
+ *
557
+ * Note: this can return a "lossy page" item pointer, indicating that the
558
+ * entry potentially matches all items on that heap page. However, it is
559
+ * not allowed to return both a lossy page pointer and exact (regular)
560
+ * item pointers for the same page. (Doing so would break the key-combination
561
+ * logic in keyGetItem and scanGetItem; see comment in scanGetItem.) In the
562
+ * current implementation this is guaranteed by the behavior of tidbitmaps.
536
563
*/
537
564
static void
538
565
entryGetItem (Relation index , GinScanEntry entry )
@@ -625,15 +652,20 @@ entryGetItem(Relation index, GinScanEntry entry)
625
652
* item pointer (including the case where the item pointer is a lossy page
626
653
* pointer).
627
654
*
628
- * Note: lossy page could be returned after single items from the same page.
629
- * This is OK since the results will just be used to build a bitmap; we'll
630
- * set a bitmap entry more than once, but never actually return a row twice.
655
+ * Item pointers must be returned in ascending order.
656
+ *
657
+ * Note: this can return a "lossy page" item pointer, indicating that the
658
+ * key potentially matches all items on that heap page. However, it is
659
+ * not allowed to return both a lossy page pointer and exact (regular)
660
+ * item pointers for the same page. (Doing so would break the key-combination
661
+ * logic in scanGetItem.)
631
662
*/
632
663
static void
633
664
keyGetItem (Relation index , GinState * ginstate , MemoryContext tempCtx ,
634
665
GinScanKey key , ItemPointer advancePast )
635
666
{
636
667
ItemPointerData myAdvancePast = * advancePast ;
668
+ ItemPointerData curPageLossy ;
637
669
uint32 i ;
638
670
uint32 lossyEntry ;
639
671
bool haveLossyEntry ;
@@ -691,87 +723,112 @@ keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx,
691
723
myAdvancePast = key -> curItem ;
692
724
693
725
/*
694
- * Prepare entryRes array to be passed to consistentFn.
695
- *
696
- * If key->nentries == 1 then the consistentFn should always succeed,
697
- * but we must call it anyway to find out the recheck status.
698
- *
699
726
* Lossy-page entries pose a problem, since we don't know the correct
700
727
* entryRes state to pass to the consistentFn, and we also don't know
701
728
* what its combining logic will be (could be AND, OR, or even NOT).
702
- * Our strategy for a single lossy-page entry is to try the
703
- * consistentFn both ways and return a hit if it accepts either one
704
- * (forcing the hit to be marked lossy so it will be rechecked).
729
+ * If the logic is OR then the consistentFn might succeed for all
730
+ * items in the lossy page even when none of the other entries match.
731
+ *
732
+ * If we have a single lossy-page entry then we check to see if the
733
+ * consistentFn will succeed with only that entry TRUE. If so,
734
+ * we return a lossy-page pointer to indicate that the whole heap
735
+ * page must be checked. (On the next call, we'll advance past all
736
+ * regular and lossy entries for this page before resuming search,
737
+ * thus ensuring that we never return both regular and lossy pointers
738
+ * for the same page.)
705
739
*
706
740
* This idea could be generalized to more than one lossy-page entry,
707
741
* but ideally lossy-page entries should be infrequent so it would
708
- * seldom be the case that we have more than one. If we do find more
709
- * than one, we just punt and return the item as lossy.
742
+ * seldom be the case that we have more than one at once. So it
743
+ * doesn't seem worth the extra complexity to optimize that case.
744
+ * If we do find more than one, we just punt and return a lossy-page
745
+ * pointer always.
710
746
*
711
747
* Note that only lossy-page entries pointing to the current item's
712
- * page should trigger this processing.
748
+ * page should trigger this processing; we might have future lossy
749
+ * pages in the entry array, but they aren't relevant yet.
713
750
*/
751
+ ItemPointerSetLossyPage (& curPageLossy ,
752
+ GinItemPointerGetBlockNumber (& key -> curItem ));
753
+
714
754
lossyEntry = 0 ;
715
755
haveLossyEntry = false;
716
756
for (i = 0 ; i < key -> nentries ; i ++ )
717
757
{
718
758
entry = key -> scanEntry + i ;
719
-
720
- if (entry -> isFinished )
721
- key -> entryRes [i ] = FALSE;
722
- else if (ItemPointerIsLossyPage (& entry -> curItem ) &&
723
- GinItemPointerGetBlockNumber (& entry -> curItem ) ==
724
- GinItemPointerGetBlockNumber (& key -> curItem ))
759
+ if (entry -> isFinished == FALSE &&
760
+ compareItemPointers (& entry -> curItem , & curPageLossy ) == 0 )
725
761
{
726
762
if (haveLossyEntry )
727
763
{
728
- /* Too many lossy entries, punt */
764
+ /* Multiple lossy entries, punt */
765
+ key -> curItem = curPageLossy ;
729
766
key -> recheckCurItem = true;
730
767
return ;
731
768
}
732
769
lossyEntry = i ;
733
770
haveLossyEntry = true;
734
- /* initially assume TRUE */
735
- key -> entryRes [i ] = TRUE;
736
771
}
737
- else if (compareItemPointers (& entry -> curItem , & key -> curItem ) == 0 )
738
- key -> entryRes [i ] = TRUE;
739
- else
740
- key -> entryRes [i ] = FALSE;
741
772
}
742
773
774
+ /* prepare for calling consistentFn in temp context */
743
775
oldCtx = MemoryContextSwitchTo (tempCtx );
744
776
777
+ if (haveLossyEntry )
778
+ {
779
+ /* Single lossy-page entry, so see if whole page matches */
780
+ memset (key -> entryRes , FALSE, key -> nentries );
781
+ key -> entryRes [lossyEntry ] = TRUE;
782
+
783
+ if (callConsistentFn (ginstate , key ))
784
+ {
785
+ /* Yes, so clean up ... */
786
+ MemoryContextSwitchTo (oldCtx );
787
+ MemoryContextReset (tempCtx );
788
+
789
+ /* and return lossy pointer for whole page */
790
+ key -> curItem = curPageLossy ;
791
+ key -> recheckCurItem = true;
792
+ return ;
793
+ }
794
+ }
795
+
745
796
/*
746
- * Initialize recheckCurItem in case the consistentFn doesn't know it
747
- * should set it. The safe assumption in that case is to force
748
- * recheck.
797
+ * At this point we know that we don't need to return a lossy
798
+ * whole-page pointer, but we might have matches for individual exact
799
+ * item pointers, possibly in combination with a lossy pointer. Our
800
+ * strategy if there's a lossy pointer is to try the consistentFn both
801
+ * ways and return a hit if it accepts either one (forcing the hit to
802
+ * be marked lossy so it will be rechecked).
803
+ *
804
+ * Prepare entryRes array to be passed to consistentFn.
805
+ *
806
+ * (If key->nentries == 1 then the consistentFn should always succeed,
807
+ * but we must call it anyway to find out the recheck status.)
749
808
*/
750
- key -> recheckCurItem = true;
809
+ for (i = 0 ; i < key -> nentries ; i ++ )
810
+ {
811
+ entry = key -> scanEntry + i ;
812
+ if (entry -> isFinished == FALSE &&
813
+ compareItemPointers (& entry -> curItem , & key -> curItem ) == 0 )
814
+ key -> entryRes [i ] = TRUE;
815
+ else
816
+ key -> entryRes [i ] = FALSE;
817
+ }
818
+ if (haveLossyEntry )
819
+ key -> entryRes [lossyEntry ] = TRUE;
751
820
752
- res = DatumGetBool (FunctionCall6 (& ginstate -> consistentFn [key -> attnum - 1 ],
753
- PointerGetDatum (key -> entryRes ),
754
- UInt16GetDatum (key -> strategy ),
755
- key -> query ,
756
- UInt32GetDatum (key -> nentries ),
757
- PointerGetDatum (key -> extra_data ),
758
- PointerGetDatum (& key -> recheckCurItem )));
821
+ res = callConsistentFn (ginstate , key );
759
822
760
823
if (!res && haveLossyEntry )
761
824
{
762
825
/* try the other way for the lossy item */
763
826
key -> entryRes [lossyEntry ] = FALSE;
764
- key -> recheckCurItem = true;
765
827
766
- res = DatumGetBool (FunctionCall6 (& ginstate -> consistentFn [key -> attnum - 1 ],
767
- PointerGetDatum (key -> entryRes ),
768
- UInt16GetDatum (key -> strategy ),
769
- key -> query ,
770
- UInt32GetDatum (key -> nentries ),
771
- PointerGetDatum (key -> extra_data ),
772
- PointerGetDatum (& key -> recheckCurItem )));
828
+ res = callConsistentFn (ginstate , key );
773
829
}
774
830
831
+ /* clean up after consistentFn calls */
775
832
MemoryContextSwitchTo (oldCtx );
776
833
MemoryContextReset (tempCtx );
777
834
@@ -1108,7 +1165,6 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
1108
1165
GinScanOpaque so = (GinScanOpaque ) scan -> opaque ;
1109
1166
MemoryContext oldCtx ;
1110
1167
bool recheck ,
1111
- keyrecheck ,
1112
1168
match ;
1113
1169
int i ;
1114
1170
pendingPosition pos ;
@@ -1152,8 +1208,8 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
1152
1208
continue ;
1153
1209
1154
1210
/*
1155
- * Matching of entries of one row is finished, so check row by
1156
- * consistent function .
1211
+ * Matching of entries of one row is finished, so check row using
1212
+ * consistent functions .
1157
1213
*/
1158
1214
oldCtx = MemoryContextSwitchTo (so -> tempCtx );
1159
1215
recheck = false;
@@ -1163,21 +1219,12 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
1163
1219
{
1164
1220
GinScanKey key = so -> keys + i ;
1165
1221
1166
- keyrecheck = true;
1167
-
1168
- if (!DatumGetBool (FunctionCall6 (& so -> ginstate .consistentFn [key -> attnum - 1 ],
1169
- PointerGetDatum (key -> entryRes ),
1170
- UInt16GetDatum (key -> strategy ),
1171
- key -> query ,
1172
- UInt32GetDatum (key -> nentries ),
1173
- PointerGetDatum (key -> extra_data ),
1174
- PointerGetDatum (& keyrecheck ))))
1222
+ if (!callConsistentFn (& so -> ginstate , key ))
1175
1223
{
1176
1224
match = false;
1177
1225
break ;
1178
1226
}
1179
-
1180
- recheck |= keyrecheck ;
1227
+ recheck |= key -> recheckCurItem ;
1181
1228
}
1182
1229
1183
1230
MemoryContextSwitchTo (oldCtx );
@@ -1248,11 +1295,26 @@ scanGetItem(IndexScanDesc scan, ItemPointer advancePast,
1248
1295
1249
1296
Assert (!ItemPointerIsMax (item ));
1250
1297
1251
- /*
1298
+ /*----------
1252
1299
* Now *item contains first ItemPointer after previous result.
1253
1300
*
1254
1301
* The item is a valid hit only if all the keys returned either
1255
1302
* that exact TID, or a lossy reference to the same page.
1303
+ *
1304
+ * This logic works only if a keyGetItem stream can never contain both
1305
+ * exact and lossy pointers for the same page. Else we could have a
1306
+ * case like
1307
+ *
1308
+ * stream 1 stream 2
1309
+ * ... ...
1310
+ * 42/6 42/7
1311
+ * 50/1 42/0xffff
1312
+ * ... ...
1313
+ *
1314
+ * We would conclude that 42/6 is not a match and advance stream 1,
1315
+ * thus never detecting the match to the lossy pointer in stream 2.
1316
+ * (keyGetItem has a similar problem versus entryGetItem.)
1317
+ *----------
1256
1318
*/
1257
1319
match = true;
1258
1320
for (i = 0 ; i < so -> nkeys ; i ++ )
0 commit comments