LCOV - differential code coverage report
Current view: top level - src/backend/access/hash - hashutil.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 54.9 % 153 84 69 84
Current Date: 2024-04-14 14:21:10 Functions: 75.0 % 16 12 4 12
Baseline: 16@8cea358b128 Branches: 30.9 % 94 29 65 29
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 54.9 % 153 84 69 84
Function coverage date bins:
(240..) days: 75.0 % 16 12 4 12
Branch coverage date bins:
(240..) days: 30.9 % 94 29 65 29

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * hashutil.c
                                  4                 :                :  *    Utility code for Postgres hash implementation.
                                  5                 :                :  *
                                  6                 :                :  * Portions Copyright (c) 1996-2024, 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
10141 scrappy@hub.org            31                 :CBC       50585 : _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                 :                : 
 6665 tgl@sss.pgh.pa.us          72                 :          50585 :     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
 7528                            82                 :         362670 : _hash_datum2hashkey(Relation rel, Datum key)
                                 83                 :                : {
                                 84                 :                :     FmgrInfo   *procinfo;
                                 85                 :                :     Oid         collation;
                                 86                 :                : 
                                 87                 :                :     /* XXX assumes index has only one attribute */
 2418 rhaas@postgresql.org       88                 :         362670 :     procinfo = index_getprocinfo(rel, 1, HASHSTANDARD_PROC);
 4740 tgl@sss.pgh.pa.us          89                 :         362670 :     collation = rel->rd_indcollation[0];
                                 90                 :                : 
                                 91                 :         362670 :     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
 6284 tgl@sss.pgh.pa.us         102                 :UBC           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));
 4740                           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
 7528 tgl@sss.pgh.pa.us         125                 :CBC     2559006 : _hash_hashkey2bucket(uint32 hashkey, uint32 maxbucket,
                                126                 :                :                      uint32 highmask, uint32 lowmask)
                                127                 :                : {
                                128                 :                :     Bucket      bucket;
                                129                 :                : 
                                130                 :        2559006 :     bucket = hashkey & highmask;
                                131         [ +  + ]:        2559006 :     if (bucket > maxbucket)
                                132                 :        1096768 :         bucket = bucket & lowmask;
                                133                 :                : 
 9357 bruce@momjian.us          134                 :        2559006 :     return bucket;
                                135                 :                : }
                                136                 :                : 
                                137                 :                : /*
                                138                 :                :  * _hash_spareindex -- returns spare index / global splitpoint phase of the
                                139                 :                :  *                     bucket
                                140                 :                :  */
                                141                 :                : uint32
 2568 rhaas@postgresql.org      142                 :         362678 : _hash_spareindex(uint32 num_bucket)
                                143                 :                : {
                                144                 :                :     uint32      splitpoint_group;
                                145                 :                :     uint32      splitpoint_phases;
                                146                 :                : 
 1467 drowley@postgresql.o      147                 :         362678 :     splitpoint_group = pg_ceil_log2_32(num_bucket);
                                148                 :                : 
 2568 rhaas@postgresql.org      149         [ +  + ]:         362678 :     if (splitpoint_group < HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE)
                                150                 :         351420 :         return splitpoint_group;
                                151                 :                : 
                                152                 :                :     /* account for single-phase groups */
                                153                 :          11258 :     splitpoint_phases = HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE;
                                154                 :                : 
                                155                 :                :     /* account for multi-phase groups before splitpoint_group */
                                156                 :          11258 :     splitpoint_phases +=
                                157                 :          11258 :         ((splitpoint_group - HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE) <<
                                158                 :                :          HASH_SPLITPOINT_PHASE_BITS);
                                159                 :                : 
                                160                 :                :     /* account for phases within current group */
                                161                 :          11258 :     splitpoint_phases +=
 2567                           162                 :          11258 :         (((num_bucket - 1) >>
                                163                 :          11258 :           (splitpoint_group - (HASH_SPLITPOINT_PHASE_BITS + 1))) &
                                164                 :                :          HASH_SPLITPOINT_PHASE_MASK);   /* to 0-based value. */
                                165                 :                : 
 2568                           166                 :          11258 :     return splitpoint_phases;
                                167                 :                : }
                                168                 :                : 
                                169                 :                : /*
                                170                 :                :  *  _hash_get_totalbuckets -- returns total number of buckets allocated till
                                171                 :                :  *                          the given splitpoint phase.
                                172                 :                :  */
                                173                 :                : uint32
                                174                 :           1063 : _hash_get_totalbuckets(uint32 splitpoint_phase)
                                175                 :                : {
                                176                 :                :     uint32      splitpoint_group;
                                177                 :                :     uint32      total_buckets;
                                178                 :                :     uint32      phases_within_splitpoint_group;
                                179                 :                : 
                                180         [ +  + ]:           1063 :     if (splitpoint_phase < HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE)
                                181                 :           1017 :         return (1 << splitpoint_phase);
                                182                 :                : 
                                183                 :                :     /* get splitpoint's group */
                                184                 :             46 :     splitpoint_group = HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE;
                                185                 :             46 :     splitpoint_group +=
                                186                 :             46 :         ((splitpoint_phase - HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE) >>
                                187                 :                :          HASH_SPLITPOINT_PHASE_BITS);
                                188                 :                : 
                                189                 :                :     /* account for buckets before splitpoint_group */
                                190                 :             46 :     total_buckets = (1 << (splitpoint_group - 1));
                                191                 :                : 
                                192                 :                :     /* account for buckets within splitpoint_group */
                                193                 :             46 :     phases_within_splitpoint_group =
                                194                 :             46 :         (((splitpoint_phase - HASH_SPLITPOINT_GROUPS_WITH_ONE_PHASE) &
                                195                 :                :           HASH_SPLITPOINT_PHASE_MASK) + 1); /* from 0-based to 1-based */
                                196                 :             46 :     total_buckets +=
                                197                 :             46 :         (((1 << (splitpoint_group - 1)) >> HASH_SPLITPOINT_PHASE_BITS) *
                                198                 :                :          phases_within_splitpoint_group);
                                199                 :                : 
                                200                 :             46 :     return total_buckets;
                                201                 :                : }
                                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
 6734 tgl@sss.pgh.pa.us         210                 :        1352692 : _hash_checkpage(Relation rel, Buffer buf, int flags)
                                211                 :                : {
 2916 kgrittn@postgresql.o      212                 :        1352692 :     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                 :                :      */
 6734 tgl@sss.pgh.pa.us         220         [ -  + ]:        1352692 :     if (PageIsNew(page))
 6734 tgl@sss.pgh.pa.us         221         [ #  # ]:UBC           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                 :                :      */
 5754 tgl@sss.pgh.pa.us         231         [ -  + ]:CBC     1352692 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(HashPageOpaqueData)))
 6734 tgl@sss.pgh.pa.us         232         [ #  # ]:UBC           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                 :                : 
 6734 tgl@sss.pgh.pa.us         239         [ +  - ]:CBC     1352692 :     if (flags)
                                240                 :                :     {
  744 michael@paquier.xyz       241                 :        1352692 :         HashPageOpaque opaque = HashPageGetOpaque(page);
                                242                 :                : 
 6734 tgl@sss.pgh.pa.us         243         [ -  + ]:        1352692 :         if ((opaque->hasho_flag & flags) == 0)
 6734 tgl@sss.pgh.pa.us         244         [ #  # ]:UBC           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                 :                :     }
                                251                 :                : 
                                252                 :                :     /*
                                253                 :                :      * When checking the metapage, also verify magic number and version.
                                254                 :                :      */
 7530 tgl@sss.pgh.pa.us         255         [ +  + ]:CBC     1352692 :     if (flags == LH_META_PAGE)
                                256                 :                :     {
 5690                           257                 :         363919 :         HashMetaPage metap = HashPageGetMeta(page);
                                258                 :                : 
 7530                           259         [ -  + ]:         363919 :         if (metap->hashm_magic != HASH_MAGIC)
 7530 tgl@sss.pgh.pa.us         260         [ #  # ]:UBC           0 :             ereport(ERROR,
                                261                 :                :                     (errcode(ERRCODE_INDEX_CORRUPTED),
                                262                 :                :                      errmsg("index \"%s\" is not a hash index",
                                263                 :                :                             RelationGetRelationName(rel))));
                                264                 :                : 
 7530 tgl@sss.pgh.pa.us         265         [ -  + ]:CBC      363919 :         if (metap->hashm_version != HASH_VERSION)
 7530 tgl@sss.pgh.pa.us         266         [ #  # ]:UBC           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                 :                :     }
10141 scrappy@hub.org           272                 :CBC     1352692 : }
                                273                 :                : 
                                274                 :                : bytea *
 3010 tgl@sss.pgh.pa.us         275                 :             54 : hashoptions(Datum reloptions, bool validate)
                                276                 :                : {
                                277                 :                :     static const relopt_parse_elt tab[] = {
                                278                 :                :         {"fillfactor", RELOPT_TYPE_INT, offsetof(HashOptions, fillfactor)},
                                279                 :                :     };
                                280                 :                : 
 1602 michael@paquier.xyz       281                 :             54 :     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
 5690 tgl@sss.pgh.pa.us         291                 :        3644037 : _hash_get_indextuple_hashkey(IndexTuple itup)
                                292                 :                : {
                                293                 :                :     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                 :        3644037 :     attp = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
                                300                 :        3644037 :     return *((uint32 *) attp);
                                301                 :                : }
                                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
 2851                           318                 :         362371 : _hash_convert_tuple(Relation index,
                                319                 :                :                     Datum *user_values, bool *user_isnull,
                                320                 :                :                     Datum *index_values, bool *index_isnull)
                                321                 :                : {
                                322                 :                :     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         [ -  + ]:         362371 :     if (user_isnull[0])
 2851 tgl@sss.pgh.pa.us         329                 :UBC           0 :         return false;
                                330                 :                : 
 2851 tgl@sss.pgh.pa.us         331                 :CBC      362371 :     hashkey = _hash_datum2hashkey(index, user_values[0]);
                                332                 :         362371 :     index_values[0] = UInt32GetDatum(hashkey);
                                333                 :         362371 :     index_isnull[0] = false;
                                334                 :         362371 :     return true;
                                335                 :                : }
                                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
 5690                           350                 :         367804 : _hash_binsearch(Page page, uint32 hash_value)
                                351                 :                : {
                                352                 :                :     OffsetNumber upper;
                                353                 :                :     OffsetNumber lower;
                                354                 :                : 
                                355                 :                :     /* Loop invariant: lower <= desired place <= upper */
                                356                 :         367804 :     upper = PageGetMaxOffsetNumber(page) + 1;
                                357                 :         367804 :     lower = FirstOffsetNumber;
                                358                 :                : 
                                359         [ +  + ]:        2744524 :     while (upper > lower)
                                360                 :                :     {
                                361                 :                :         OffsetNumber off;
                                362                 :                :         IndexTuple  itup;
                                363                 :                :         uint32      hashkey;
                                364                 :                : 
                                365                 :        2376720 :         off = (upper + lower) / 2;
                                366   [ +  -  +  -  :        2376720 :         Assert(OffsetNumberIsValid(off));
                                              -  + ]
                                367                 :                : 
                                368                 :        2376720 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
                                369                 :        2376720 :         hashkey = _hash_get_indextuple_hashkey(itup);
                                370         [ +  + ]:        2376720 :         if (hashkey < hash_value)
                                371                 :         992749 :             lower = off + 1;
                                372                 :                :         else
                                373                 :        1383971 :             upper = off;
                                374                 :                :     }
                                375                 :                : 
                                376                 :         367804 :     return lower;
                                377                 :                : }
                                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                 :             42 : _hash_binsearch_last(Page page, uint32 hash_value)
                                389                 :                : {
                                390                 :                :     OffsetNumber upper;
                                391                 :                :     OffsetNumber lower;
                                392                 :                : 
                                393                 :                :     /* Loop invariant: lower <= desired place <= upper */
                                394                 :             42 :     upper = PageGetMaxOffsetNumber(page);
                                395                 :             42 :     lower = FirstOffsetNumber - 1;
                                396                 :                : 
                                397         [ +  + ]:            417 :     while (upper > lower)
                                398                 :                :     {
                                399                 :                :         IndexTuple  itup;
                                400                 :                :         OffsetNumber off;
                                401                 :                :         uint32      hashkey;
                                402                 :                : 
                                403                 :            375 :         off = (upper + lower + 1) / 2;
                                404   [ +  -  +  -  :            375 :         Assert(OffsetNumberIsValid(off));
                                              -  + ]
                                405                 :                : 
                                406                 :            375 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
                                407                 :            375 :         hashkey = _hash_get_indextuple_hashkey(itup);
                                408         [ -  + ]:            375 :         if (hashkey > hash_value)
 5690 tgl@sss.pgh.pa.us         409                 :UBC           0 :             upper = off - 1;
                                410                 :                :         else
 5690 tgl@sss.pgh.pa.us         411                 :CBC         375 :             lower = off;
                                412                 :                :     }
                                413                 :                : 
                                414                 :             42 :     return lower;
                                415                 :                : }
                                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
 2692 rhaas@postgresql.org      422                 :UBC           0 : _hash_get_oldblock_from_newbucket(Relation rel, Bucket new_bucket)
                                423                 :                : {
                                424                 :                :     Bucket      old_bucket;
                                425                 :                :     uint32      mask;
                                426                 :                :     Buffer      metabuf;
                                427                 :                :     HashMetaPage metap;
                                428                 :                :     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                 :                :      */
  632 tmunro@postgresql.or      438                 :              0 :     mask = (((uint32) 1) << pg_leftmost_one_pos32(new_bucket)) - 1;
 2692 rhaas@postgresql.org      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                 :                : }
                                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                 :                :     Bucket      new_bucket;
                                464                 :                :     Buffer      metabuf;
                                465                 :                :     HashMetaPage metap;
                                466                 :                :     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                 :                :                                                     metap->hashm_lowmask,
                                473                 :                :                                                     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                 :                : }
                                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
 2692 rhaas@postgresql.org      494                 :CBC         669 : _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
                                495                 :                :                                    uint32 lowmask, uint32 maxbucket)
                                496                 :                : {
                                497                 :                :     Bucket      new_bucket;
                                498                 :                : 
                                499                 :            669 :     new_bucket = CALC_NEW_BUCKET(old_bucket, lowmask);
                                500         [ -  + ]:            669 :     if (new_bucket > maxbucket)
                                501                 :                :     {
 2692 rhaas@postgresql.org      502                 :UBC           0 :         lowmask = lowmask >> 1;
                                503                 :              0 :         new_bucket = CALC_NEW_BUCKET(old_bucket, lowmask);
                                504                 :                :     }
                                505                 :                : 
 2692 rhaas@postgresql.org      506                 :CBC         669 :     return new_bucket;
                                507                 :                : }
                                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
 2587 rhaas@postgresql.org      536                 :UBC           0 : _hash_kill_items(IndexScanDesc scan)
                                537                 :                : {
 2524 bruce@momjian.us          538                 :              0 :     HashScanOpaque so = (HashScanOpaque) scan->opaque;
 2396 rhaas@postgresql.org      539                 :              0 :     Relation    rel = scan->indexRelation;
                                540                 :                :     BlockNumber blkno;
                                541                 :                :     Buffer      buf;
                                542                 :                :     Page        page;
                                543                 :                :     HashPageOpaque opaque;
                                544                 :                :     OffsetNumber offnum,
                                545                 :                :                 maxoff;
 2524 bruce@momjian.us          546                 :              0 :     int         numKilled = so->numKilled;
                                547                 :                :     int         i;
                                548                 :              0 :     bool        killedsomething = false;
 2396 rhaas@postgresql.org      549                 :              0 :     bool        havePin = false;
                                550                 :                : 
 2587                           551         [ #  # ]:              0 :     Assert(so->numKilled > 0);
                                552         [ #  # ]:              0 :     Assert(so->killedItems != NULL);
 2396                           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                 :                :      */
 2587                           559                 :              0 :     so->numKilled = 0;
                                560                 :                : 
 2396                           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                 :                :     }
                                572                 :                :     else
                                573                 :              0 :         buf = _hash_getbuf(rel, blkno, HASH_READ, LH_OVERFLOW_PAGE);
                                574                 :                : 
                                575                 :              0 :     page = BufferGetPage(buf);
  744 michael@paquier.xyz       576                 :              0 :     opaque = HashPageGetOpaque(page);
 2587 rhaas@postgresql.org      577                 :              0 :     maxoff = PageGetMaxOffsetNumber(page);
                                578                 :                : 
                                579         [ #  # ]:              0 :     for (i = 0; i < numKilled; i++)
                                580                 :                :     {
 2396                           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                 :                : 
 2587                           589         [ #  # ]:              0 :         while (offnum <= maxoff)
                                590                 :                :         {
 2524 bruce@momjian.us          591                 :              0 :             ItemId      iid = PageGetItemId(page, offnum);
 2587 rhaas@postgresql.org      592                 :              0 :             IndexTuple  ituple = (IndexTuple) PageGetItem(page, iid);
                                593                 :                : 
 2396                           594         [ #  # ]:              0 :             if (ItemPointerEquals(&ituple->t_tid, &currItem->heapTid))
                                595                 :                :             {
                                596                 :                :                 /* found the item */
 2587                           597                 :              0 :                 ItemIdMarkDead(iid);
                                598                 :              0 :                 killedsomething = true;
 2524 bruce@momjian.us          599                 :              0 :                 break;          /* out of inner search loop */
                                600                 :                :             }
 2587 rhaas@postgresql.org      601                 :              0 :             offnum = OffsetNumberNext(offnum);
                                602                 :                :         }
                                603                 :                :     }
                                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;
 2396                           613                 :              0 :         MarkBufferDirtyHint(buf, true);
                                614                 :                :     }
                                615                 :                : 
                                616   [ #  #  #  # ]:              0 :     if (so->hashso_bucket_buf == so->currPos.buf ||
                                617                 :                :         havePin)
                                618                 :              0 :         LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
                                619                 :                :     else
                                620                 :              0 :         _hash_relbuf(rel, buf);
 2587                           621                 :              0 : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622