LCOV - code coverage report
Current view: top level - src/backend/access/hash - hashutil.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 54.8 % 219 120
Test Date: 2026-01-26 10:56:24 Functions: 75.0 % 16 12
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 26.2 % 103 27

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

Generated by: LCOV version 2.3.2-1