LCOV - code coverage report
Current view: top level - src/backend/access/hash - hashutil.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 86 151 57.0 %
Date: 2019-11-13 23:06:49 Functions: 13 17 76.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * hashutil.c
       4             :  *    Utility code for Postgres hash implementation.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/hash/hashutil.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/hash.h"
      18             : #include "access/reloptions.h"
      19             : #include "access/relscan.h"
      20             : #include "storage/buf_internals.h"
      21             : #include "utils/lsyscache.h"
      22             : #include "utils/rel.h"
      23             : 
      24             : #define CALC_NEW_BUCKET(old_bucket, lowmask) \
      25             :             old_bucket | (lowmask + 1)
      26             : 
      27             : /*
      28             :  * _hash_checkqual -- does the index tuple satisfy the scan conditions?
      29             :  */
      30             : bool
      31       67928 : _hash_checkqual(IndexScanDesc scan, IndexTuple itup)
      32             : {
      33             :     /*
      34             :      * Currently, we can't check any of the scan conditions since we do not
      35             :      * have the original index entry value to supply to the sk_func. Always
      36             :      * return true; we expect that hashgettuple already set the recheck flag
      37             :      * to make the main indexscan code do it.
      38             :      */
      39             : #ifdef NOT_USED
      40             :     TupleDesc   tupdesc = RelationGetDescr(scan->indexRelation);
      41             :     ScanKey     key = scan->keyData;
      42             :     int         scanKeySize = scan->numberOfKeys;
      43             : 
      44             :     while (scanKeySize > 0)
      45             :     {
      46             :         Datum       datum;
      47             :         bool        isNull;
      48             :         Datum       test;
      49             : 
      50             :         datum = index_getattr(itup,
      51             :                               key->sk_attno,
      52             :                               tupdesc,
      53             :                               &isNull);
      54             : 
      55             :         /* assume sk_func is strict */
      56             :         if (isNull)
      57             :             return false;
      58             :         if (key->sk_flags & SK_ISNULL)
      59             :             return false;
      60             : 
      61             :         test = FunctionCall2Coll(&key->sk_func, key->sk_collation,
      62             :                                  datum, key->sk_argument);
      63             : 
      64             :         if (!DatumGetBool(test))
      65             :             return false;
      66             : 
      67             :         key++;
      68             :         scanKeySize--;
      69             :     }
      70             : #endif
      71             : 
      72       67928 :     return true;
      73             : }
      74             : 
      75             : /*
      76             :  * _hash_datum2hashkey -- given a Datum, call the index's hash function
      77             :  *
      78             :  * The Datum is assumed to be of the index's column type, so we can use the
      79             :  * "primary" hash function that's tracked for us by the generic index code.
      80             :  */
      81             : uint32
      82      451234 : _hash_datum2hashkey(Relation rel, Datum key)
      83             : {
      84             :     FmgrInfo   *procinfo;
      85             :     Oid         collation;
      86             : 
      87             :     /* XXX assumes index has only one attribute */
      88      451234 :     procinfo = index_getprocinfo(rel, 1, HASHSTANDARD_PROC);
      89      451234 :     collation = rel->rd_indcollation[0];
      90             : 
      91      451234 :     return DatumGetUInt32(FunctionCall1Coll(procinfo, collation, key));
      92             : }
      93             : 
      94             : /*
      95             :  * _hash_datum2hashkey_type -- given a Datum of a specified type,
      96             :  *          hash it in a fashion compatible with this index
      97             :  *
      98             :  * This is much more expensive than _hash_datum2hashkey, so use it only in
      99             :  * cross-type situations.
     100             :  */
     101             : uint32
     102           0 : _hash_datum2hashkey_type(Relation rel, Datum key, Oid keytype)
     103             : {
     104             :     RegProcedure hash_proc;
     105             :     Oid         collation;
     106             : 
     107             :     /* XXX assumes index has only one attribute */
     108           0 :     hash_proc = get_opfamily_proc(rel->rd_opfamily[0],
     109             :                                   keytype,
     110             :                                   keytype,
     111             :                                   HASHSTANDARD_PROC);
     112           0 :     if (!RegProcedureIsValid(hash_proc))
     113           0 :         elog(ERROR, "missing support function %d(%u,%u) for index \"%s\"",
     114             :              HASHSTANDARD_PROC, keytype, keytype,
     115             :              RelationGetRelationName(rel));
     116           0 :     collation = rel->rd_indcollation[0];
     117             : 
     118           0 :     return DatumGetUInt32(OidFunctionCall1Coll(hash_proc, collation, key));
     119             : }
     120             : 
     121             : /*
     122             :  * _hash_hashkey2bucket -- determine which bucket the hashkey maps to.
     123             :  */
     124             : Bucket
     125     1965234 : _hash_hashkey2bucket(uint32 hashkey, uint32 maxbucket,
     126             :                      uint32 highmask, uint32 lowmask)
     127             : {
     128             :     Bucket      bucket;
     129             : 
     130     1965234 :     bucket = hashkey & highmask;
     131     1965234 :     if (bucket > maxbucket)
     132      744020 :         bucket = bucket & lowmask;
     133             : 
     134     1965234 :     return bucket;
     135             : }
     136             : 
     137             : /*
     138             :  * _hash_log2 -- returns ceil(lg2(num))
     139             :  */
     140             : uint32
     141      452800 : _hash_log2(uint32 num)
     142             : {
     143             :     uint32      i,
     144             :                 limit;
     145             : 
     146      452800 :     limit = 1;
     147      452800 :     for (i = 0; limit < num; limit <<= 1, i++)
     148             :         ;
     149      452800 :     return i;
     150             : }
     151             : 
     152             : /*
     153             :  * _hash_spareindex -- returns spare index / global splitpoint phase of the
     154             :  *                     bucket
     155             :  */
     156             : uint32
     157      452444 : _hash_spareindex(uint32 num_bucket)
     158             : {
     159             :     uint32      splitpoint_group;
     160             :     uint32      splitpoint_phases;
     161             : 
     162      452444 :     splitpoint_group = _hash_log2(num_bucket);
     163             : 
     164      452444 :     if (splitpoint_group < HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE)
     165      436968 :         return splitpoint_group;
     166             : 
     167             :     /* account for single-phase groups */
     168       15476 :     splitpoint_phases = HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE;
     169             : 
     170             :     /* account for multi-phase groups before splitpoint_group */
     171       15476 :     splitpoint_phases +=
     172       15476 :         ((splitpoint_group - HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE) <<
     173             :          HASH_SPLITPOINT_PHASE_BITS);
     174             : 
     175             :     /* account for phases within current group */
     176       15476 :     splitpoint_phases +=
     177       30952 :         (((num_bucket - 1) >>
     178       30952 :           (splitpoint_group - (HASH_SPLITPOINT_PHASE_BITS + 1))) &
     179             :          HASH_SPLITPOINT_PHASE_MASK);   /* to 0-based value. */
     180             : 
     181       15476 :     return splitpoint_phases;
     182             : }
     183             : 
     184             : /*
     185             :  *  _hash_get_totalbuckets -- returns total number of buckets allocated till
     186             :  *                          the given splitpoint phase.
     187             :  */
     188             : uint32
     189        1048 : _hash_get_totalbuckets(uint32 splitpoint_phase)
     190             : {
     191             :     uint32      splitpoint_group;
     192             :     uint32      total_buckets;
     193             :     uint32      phases_within_splitpoint_group;
     194             : 
     195        1048 :     if (splitpoint_phase < HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE)
     196         988 :         return (1 << splitpoint_phase);
     197             : 
     198             :     /* get splitpoint's group */
     199          60 :     splitpoint_group = HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE;
     200          60 :     splitpoint_group +=
     201          60 :         ((splitpoint_phase - HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE) >>
     202             :          HASH_SPLITPOINT_PHASE_BITS);
     203             : 
     204             :     /* account for buckets before splitpoint_group */
     205          60 :     total_buckets = (1 << (splitpoint_group - 1));
     206             : 
     207             :     /* account for buckets within splitpoint_group */
     208          60 :     phases_within_splitpoint_group =
     209          60 :         (((splitpoint_phase - HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE) &
     210             :           HASH_SPLITPOINT_PHASE_MASK) + 1); /* from 0-based to 1-based */
     211          60 :     total_buckets +=
     212          60 :         (((1 << (splitpoint_group - 1)) >> HASH_SPLITPOINT_PHASE_BITS) *
     213             :          phases_within_splitpoint_group);
     214             : 
     215          60 :     return total_buckets;
     216             : }
     217             : 
     218             : /*
     219             :  * _hash_checkpage -- sanity checks on the format of all hash pages
     220             :  *
     221             :  * If flags is not zero, it is a bitwise OR of the acceptable page types
     222             :  * (values of hasho_flag & LH_PAGE_TYPE).
     223             :  */
     224             : void
     225     1663166 : _hash_checkpage(Relation rel, Buffer buf, int flags)
     226             : {
     227     1663166 :     Page        page = BufferGetPage(buf);
     228             : 
     229             :     /*
     230             :      * ReadBuffer verifies that every newly-read page passes
     231             :      * PageHeaderIsValid, which means it either contains a reasonably sane
     232             :      * page header or is all-zero.  We have to defend against the all-zero
     233             :      * case, however.
     234             :      */
     235     1663166 :     if (PageIsNew(page))
     236           0 :         ereport(ERROR,
     237             :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     238             :                  errmsg("index \"%s\" contains unexpected zero page at block %u",
     239             :                         RelationGetRelationName(rel),
     240             :                         BufferGetBlockNumber(buf)),
     241             :                  errhint("Please REINDEX it.")));
     242             : 
     243             :     /*
     244             :      * Additionally check that the special area looks sane.
     245             :      */
     246     1663166 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(HashPageOpaqueData)))
     247           0 :         ereport(ERROR,
     248             :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     249             :                  errmsg("index \"%s\" contains corrupted page at block %u",
     250             :                         RelationGetRelationName(rel),
     251             :                         BufferGetBlockNumber(buf)),
     252             :                  errhint("Please REINDEX it.")));
     253             : 
     254     1663166 :     if (flags)
     255             :     {
     256     1663166 :         HashPageOpaque opaque = (HashPageOpaque) PageGetSpecialPointer(page);
     257             : 
     258     1663166 :         if ((opaque->hasho_flag & flags) == 0)
     259           0 :             ereport(ERROR,
     260             :                     (errcode(ERRCODE_INDEX_CORRUPTED),
     261             :                      errmsg("index \"%s\" contains corrupted page at block %u",
     262             :                             RelationGetRelationName(rel),
     263             :                             BufferGetBlockNumber(buf)),
     264             :                      errhint("Please REINDEX it.")));
     265             :     }
     266             : 
     267             :     /*
     268             :      * When checking the metapage, also verify magic number and version.
     269             :      */
     270     1663166 :     if (flags == LH_META_PAGE)
     271             :     {
     272      452856 :         HashMetaPage metap = HashPageGetMeta(page);
     273             : 
     274      452856 :         if (metap->hashm_magic != HASH_MAGIC)
     275           0 :             ereport(ERROR,
     276             :                     (errcode(ERRCODE_INDEX_CORRUPTED),
     277             :                      errmsg("index \"%s\" is not a hash index",
     278             :                             RelationGetRelationName(rel))));
     279             : 
     280      452856 :         if (metap->hashm_version != HASH_VERSION)
     281           0 :             ereport(ERROR,
     282             :                     (errcode(ERRCODE_INDEX_CORRUPTED),
     283             :                      errmsg("index \"%s\" has wrong hash version",
     284             :                             RelationGetRelationName(rel)),
     285             :                      errhint("Please REINDEX it.")));
     286             :     }
     287     1663166 : }
     288             : 
     289             : bytea *
     290          80 : hashoptions(Datum reloptions, bool validate)
     291             : {
     292          80 :     return default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
     293             : }
     294             : 
     295             : /*
     296             :  * _hash_get_indextuple_hashkey - get the hash index tuple's hash key value
     297             :  */
     298             : uint32
     299     4685208 : _hash_get_indextuple_hashkey(IndexTuple itup)
     300             : {
     301             :     char       *attp;
     302             : 
     303             :     /*
     304             :      * We assume the hash key is the first attribute and can't be null, so
     305             :      * this can be done crudely but very very cheaply ...
     306             :      */
     307     4685208 :     attp = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
     308     4685208 :     return *((uint32 *) attp);
     309             : }
     310             : 
     311             : /*
     312             :  * _hash_convert_tuple - convert raw index data to hash key
     313             :  *
     314             :  * Inputs: values and isnull arrays for the user data column(s)
     315             :  * Outputs: values and isnull arrays for the index tuple, suitable for
     316             :  *      passing to index_form_tuple().
     317             :  *
     318             :  * Returns true if successful, false if not (because there are null values).
     319             :  * On a false result, the given data need not be indexed.
     320             :  *
     321             :  * Note: callers know that the index-column arrays are always of length 1.
     322             :  * In principle, there could be more than one input column, though we do not
     323             :  * currently support that.
     324             :  */
     325             : bool
     326      450932 : _hash_convert_tuple(Relation index,
     327             :                     Datum *user_values, bool *user_isnull,
     328             :                     Datum *index_values, bool *index_isnull)
     329             : {
     330             :     uint32      hashkey;
     331             : 
     332             :     /*
     333             :      * We do not insert null values into hash indexes.  This is okay because
     334             :      * the only supported search operator is '=', and we assume it is strict.
     335             :      */
     336      450932 :     if (user_isnull[0])
     337           0 :         return false;
     338             : 
     339      450932 :     hashkey = _hash_datum2hashkey(index, user_values[0]);
     340      450932 :     index_values[0] = UInt32GetDatum(hashkey);
     341      450932 :     index_isnull[0] = false;
     342      450932 :     return true;
     343             : }
     344             : 
     345             : /*
     346             :  * _hash_binsearch - Return the offset number in the page where the
     347             :  *                   specified hash value should be sought or inserted.
     348             :  *
     349             :  * We use binary search, relying on the assumption that the existing entries
     350             :  * are ordered by hash key.
     351             :  *
     352             :  * Returns the offset of the first index entry having hashkey >= hash_value,
     353             :  * or the page's max offset plus one if hash_value is greater than all
     354             :  * existing hash keys in the page.  This is the appropriate place to start
     355             :  * a search, or to insert a new item.
     356             :  */
     357             : OffsetNumber
     358      531716 : _hash_binsearch(Page page, uint32 hash_value)
     359             : {
     360             :     OffsetNumber upper;
     361             :     OffsetNumber lower;
     362             : 
     363             :     /* Loop invariant: lower <= desired place <= upper */
     364      531716 :     upper = PageGetMaxOffsetNumber(page) + 1;
     365      531716 :     lower = FirstOffsetNumber;
     366             : 
     367     4304758 :     while (upper > lower)
     368             :     {
     369             :         OffsetNumber off;
     370             :         IndexTuple  itup;
     371             :         uint32      hashkey;
     372             : 
     373     3241326 :         off = (upper + lower) / 2;
     374             :         Assert(OffsetNumberIsValid(off));
     375             : 
     376     3241326 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     377     3241326 :         hashkey = _hash_get_indextuple_hashkey(itup);
     378     3241326 :         if (hashkey < hash_value)
     379     1373220 :             lower = off + 1;
     380             :         else
     381     1868106 :             upper = off;
     382             :     }
     383             : 
     384      531716 :     return lower;
     385             : }
     386             : 
     387             : /*
     388             :  * _hash_binsearch_last
     389             :  *
     390             :  * Same as above, except that if there are multiple matching items in the
     391             :  * page, we return the offset of the last one instead of the first one,
     392             :  * and the possible range of outputs is 0..maxoffset not 1..maxoffset+1.
     393             :  * This is handy for starting a new page in a backwards scan.
     394             :  */
     395             : OffsetNumber
     396          56 : _hash_binsearch_last(Page page, uint32 hash_value)
     397             : {
     398             :     OffsetNumber upper;
     399             :     OffsetNumber lower;
     400             : 
     401             :     /* Loop invariant: lower <= desired place <= upper */
     402          56 :     upper = PageGetMaxOffsetNumber(page);
     403          56 :     lower = FirstOffsetNumber - 1;
     404             : 
     405         612 :     while (upper > lower)
     406             :     {
     407             :         IndexTuple  itup;
     408             :         OffsetNumber off;
     409             :         uint32      hashkey;
     410             : 
     411         500 :         off = (upper + lower + 1) / 2;
     412             :         Assert(OffsetNumberIsValid(off));
     413             : 
     414         500 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     415         500 :         hashkey = _hash_get_indextuple_hashkey(itup);
     416         500 :         if (hashkey > hash_value)
     417           0 :             upper = off - 1;
     418             :         else
     419         500 :             lower = off;
     420             :     }
     421             : 
     422          56 :     return lower;
     423             : }
     424             : 
     425             : /*
     426             :  *  _hash_get_oldblock_from_newbucket() -- get the block number of a bucket
     427             :  *          from which current (new) bucket is being split.
     428             :  */
     429             : BlockNumber
     430           0 : _hash_get_oldblock_from_newbucket(Relation rel, Bucket new_bucket)
     431             : {
     432             :     Bucket      old_bucket;
     433             :     uint32      mask;
     434             :     Buffer      metabuf;
     435             :     HashMetaPage metap;
     436             :     BlockNumber blkno;
     437             : 
     438             :     /*
     439             :      * To get the old bucket from the current bucket, we need a mask to modulo
     440             :      * into lower half of table.  This mask is stored in meta page as
     441             :      * hashm_lowmask, but here we can't rely on the same, because we need a
     442             :      * value of lowmask that was prevalent at the time when bucket split was
     443             :      * started.  Masking the most significant bit of new bucket would give us
     444             :      * old bucket.
     445             :      */
     446           0 :     mask = (((uint32) 1) << (fls(new_bucket) - 1)) - 1;
     447           0 :     old_bucket = new_bucket & mask;
     448             : 
     449           0 :     metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
     450           0 :     metap = HashPageGetMeta(BufferGetPage(metabuf));
     451             : 
     452           0 :     blkno = BUCKET_TO_BLKNO(metap, old_bucket);
     453             : 
     454           0 :     _hash_relbuf(rel, metabuf);
     455             : 
     456           0 :     return blkno;
     457             : }
     458             : 
     459             : /*
     460             :  *  _hash_get_newblock_from_oldbucket() -- get the block number of a bucket
     461             :  *          that will be generated after split from old bucket.
     462             :  *
     463             :  * This is used to find the new bucket from old bucket based on current table
     464             :  * half.  It is mainly required to finish the incomplete splits where we are
     465             :  * sure that not more than one bucket could have split in progress from old
     466             :  * bucket.
     467             :  */
     468             : BlockNumber
     469           0 : _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bucket)
     470             : {
     471             :     Bucket      new_bucket;
     472             :     Buffer      metabuf;
     473             :     HashMetaPage metap;
     474             :     BlockNumber blkno;
     475             : 
     476           0 :     metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
     477           0 :     metap = HashPageGetMeta(BufferGetPage(metabuf));
     478             : 
     479           0 :     new_bucket = _hash_get_newbucket_from_oldbucket(rel, old_bucket,
     480             :                                                     metap->hashm_lowmask,
     481             :                                                     metap->hashm_maxbucket);
     482           0 :     blkno = BUCKET_TO_BLKNO(metap, new_bucket);
     483             : 
     484           0 :     _hash_relbuf(rel, metabuf);
     485             : 
     486           0 :     return blkno;
     487             : }
     488             : 
     489             : /*
     490             :  *  _hash_get_newbucket_from_oldbucket() -- get the new bucket that will be
     491             :  *          generated after split from current (old) bucket.
     492             :  *
     493             :  * This is used to find the new bucket from old bucket.  New bucket can be
     494             :  * obtained by OR'ing old bucket with most significant bit of current table
     495             :  * half (lowmask passed in this function can be used to identify msb of
     496             :  * current table half).  There could be multiple buckets that could have
     497             :  * been split from current bucket.  We need the first such bucket that exists.
     498             :  * Caller must ensure that no more than one split has happened from old
     499             :  * bucket.
     500             :  */
     501             : Bucket
     502         872 : _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
     503             :                                    uint32 lowmask, uint32 maxbucket)
     504             : {
     505             :     Bucket      new_bucket;
     506             : 
     507         872 :     new_bucket = CALC_NEW_BUCKET(old_bucket, lowmask);
     508         872 :     if (new_bucket > maxbucket)
     509             :     {
     510           0 :         lowmask = lowmask >> 1;
     511           0 :         new_bucket = CALC_NEW_BUCKET(old_bucket, lowmask);
     512             :     }
     513             : 
     514         872 :     return new_bucket;
     515             : }
     516             : 
     517             : /*
     518             :  * _hash_kill_items - set LP_DEAD state for items an indexscan caller has
     519             :  * told us were killed.
     520             :  *
     521             :  * scan->opaque, referenced locally through so, contains information about the
     522             :  * current page and killed tuples thereon (generally, this should only be
     523             :  * called if so->numKilled > 0).
     524             :  *
     525             :  * The caller does not have a lock on the page and may or may not have the
     526             :  * page pinned in a buffer.  Note that read-lock is sufficient for setting
     527             :  * LP_DEAD status (which is only a hint).
     528             :  *
     529             :  * The caller must have pin on bucket buffer, but may or may not have pin
     530             :  * on overflow buffer, as indicated by HashScanPosIsPinned(so->currPos).
     531             :  *
     532             :  * We match items by heap TID before assuming they are the right ones to
     533             :  * delete.
     534             :  *
     535             :  * There are never any scans active in a bucket at the time VACUUM begins,
     536             :  * because VACUUM takes a cleanup lock on the primary bucket page and scans
     537             :  * hold a pin.  A scan can begin after VACUUM leaves the primary bucket page
     538             :  * but before it finishes the entire bucket, but it can never pass VACUUM,
     539             :  * because VACUUM always locks the next page before releasing the lock on
     540             :  * the previous one.  Therefore, we don't have to worry about accidentally
     541             :  * killing a TID that has been reused for an unrelated tuple.
     542             :  */
     543             : void
     544           0 : _hash_kill_items(IndexScanDesc scan)
     545             : {
     546           0 :     HashScanOpaque so = (HashScanOpaque) scan->opaque;
     547           0 :     Relation    rel = scan->indexRelation;
     548             :     BlockNumber blkno;
     549             :     Buffer      buf;
     550             :     Page        page;
     551             :     HashPageOpaque opaque;
     552             :     OffsetNumber offnum,
     553             :                 maxoff;
     554           0 :     int         numKilled = so->numKilled;
     555             :     int         i;
     556           0 :     bool        killedsomething = false;
     557           0 :     bool        havePin = false;
     558             : 
     559             :     Assert(so->numKilled > 0);
     560             :     Assert(so->killedItems != NULL);
     561             :     Assert(HashScanPosIsValid(so->currPos));
     562             : 
     563             :     /*
     564             :      * Always reset the scan state, so we don't look for same items on other
     565             :      * pages.
     566             :      */
     567           0 :     so->numKilled = 0;
     568             : 
     569           0 :     blkno = so->currPos.currPage;
     570           0 :     if (HashScanPosIsPinned(so->currPos))
     571             :     {
     572             :         /*
     573             :          * We already have pin on this buffer, so, all we need to do is
     574             :          * acquire lock on it.
     575             :          */
     576           0 :         havePin = true;
     577           0 :         buf = so->currPos.buf;
     578           0 :         LockBuffer(buf, BUFFER_LOCK_SHARE);
     579             :     }
     580             :     else
     581           0 :         buf = _hash_getbuf(rel, blkno, HASH_READ, LH_OVERFLOW_PAGE);
     582             : 
     583           0 :     page = BufferGetPage(buf);
     584           0 :     opaque = (HashPageOpaque) PageGetSpecialPointer(page);
     585           0 :     maxoff = PageGetMaxOffsetNumber(page);
     586             : 
     587           0 :     for (i = 0; i < numKilled; i++)
     588             :     {
     589           0 :         int         itemIndex = so->killedItems[i];
     590           0 :         HashScanPosItem *currItem = &so->currPos.items[itemIndex];
     591             : 
     592           0 :         offnum = currItem->indexOffset;
     593             : 
     594             :         Assert(itemIndex >= so->currPos.firstItem &&
     595             :                itemIndex <= so->currPos.lastItem);
     596             : 
     597           0 :         while (offnum <= maxoff)
     598             :         {
     599           0 :             ItemId      iid = PageGetItemId(page, offnum);
     600           0 :             IndexTuple  ituple = (IndexTuple) PageGetItem(page, iid);
     601             : 
     602           0 :             if (ItemPointerEquals(&ituple->t_tid, &currItem->heapTid))
     603             :             {
     604             :                 /* found the item */
     605           0 :                 ItemIdMarkDead(iid);
     606           0 :                 killedsomething = true;
     607           0 :                 break;          /* out of inner search loop */
     608             :             }
     609           0 :             offnum = OffsetNumberNext(offnum);
     610             :         }
     611             :     }
     612             : 
     613             :     /*
     614             :      * Since this can be redone later if needed, mark as dirty hint. Whenever
     615             :      * we mark anything LP_DEAD, we also set the page's
     616             :      * LH_PAGE_HAS_DEAD_TUPLES flag, which is likewise just a hint.
     617             :      */
     618           0 :     if (killedsomething)
     619             :     {
     620           0 :         opaque->hasho_flag |= LH_PAGE_HAS_DEAD_TUPLES;
     621           0 :         MarkBufferDirtyHint(buf, true);
     622             :     }
     623             : 
     624           0 :     if (so->hashso_bucket_buf == so->currPos.buf ||
     625             :         havePin)
     626           0 :         LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
     627             :     else
     628           0 :         _hash_relbuf(rel, buf);
     629           0 : }

Generated by: LCOV version 1.13