<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Charles Green</title>
    <description>The latest articles on Forem by Charles Green (@charles_green_1791).</description>
    <link>https://forem.com/charles_green_1791</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3630582%2Ff18db5f5-75f8-4653-984f-561348a0bd09.png</url>
      <title>Forem: Charles Green</title>
      <link>https://forem.com/charles_green_1791</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/charles_green_1791"/>
    <language>en</language>
    <item>
      <title>Postgres Source Code Walk-through : HTAB, a Generic Hash Table</title>
      <dc:creator>Charles Green</dc:creator>
      <pubDate>Wed, 26 Nov 2025 12:17:38 +0000</pubDate>
      <link>https://forem.com/charles_green_1791/postgres-source-code-walk-through-htab-a-generic-hash-table-3846</link>
      <guid>https://forem.com/charles_green_1791/postgres-source-code-walk-through-htab-a-generic-hash-table-3846</guid>
      <description>&lt;p&gt;The following is the source code of a hash table, written in &lt;code&gt;dynahash.c&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HTAB&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;HASHHDR&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="cm"&gt;/* =&amp;gt; shared control information */&lt;/span&gt;
    &lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="cm"&gt;/* directory of segment starts */&lt;/span&gt;
    &lt;span class="n"&gt;HashValueFunc&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="cm"&gt;/* hash function */&lt;/span&gt;
    &lt;span class="n"&gt;HashCompareFunc&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="cm"&gt;/* key comparison function */&lt;/span&gt;
    &lt;span class="n"&gt;HashCopyFunc&lt;/span&gt; &lt;span class="n"&gt;keycopy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="cm"&gt;/* key copying function */&lt;/span&gt;
    &lt;span class="n"&gt;HashAllocFunc&lt;/span&gt; &lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="cm"&gt;/* memory allocator */&lt;/span&gt;
    &lt;span class="n"&gt;MemoryContext&lt;/span&gt; &lt;span class="n"&gt;hcxt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="cm"&gt;/* memory context if default allocator used */&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt;       &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tabname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="cm"&gt;/* table name (for error messages) */&lt;/span&gt;
    &lt;span class="n"&gt;bool&lt;/span&gt;        &lt;span class="n"&gt;isshared&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="cm"&gt;/* true if table is in shared memory */&lt;/span&gt;

    &lt;span class="cm"&gt;/* freezing a shared table isn't allowed, so we can keep state here */&lt;/span&gt;
    &lt;span class="n"&gt;bool&lt;/span&gt;        &lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="cm"&gt;/* true = no more inserts allowed */&lt;/span&gt;

    &lt;span class="cm"&gt;/* We keep local copies of these fixed values to reduce contention */&lt;/span&gt;
    &lt;span class="n"&gt;Size&lt;/span&gt;        &lt;span class="n"&gt;keysize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="cm"&gt;/* hash key length in bytes */&lt;/span&gt;
    &lt;span class="n"&gt;int64&lt;/span&gt;       &lt;span class="n"&gt;ssize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="cm"&gt;/* segment size --- must be power of 2 */&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt;         &lt;span class="n"&gt;sshift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="cm"&gt;/* segment shift = log2(ssize) */&lt;/span&gt;

    &lt;span class="cm"&gt;/*
     * In a USE_VALGRIND build, non-shared hashtables keep an slist chain of
     * all the element blocks they have allocated.  This pacifies Valgrind,
     * which would otherwise often claim that the element blocks are "possibly
     * lost" for lack of any non-interior pointers to their starts.
     */&lt;/span&gt;
&lt;span class="cp"&gt;#ifdef USE_VALGRIND
&lt;/span&gt;    &lt;span class="n"&gt;slist_head&lt;/span&gt;  &lt;span class="n"&gt;element_blocks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initialization
&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;HTAB&lt;/code&gt; is created by calling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="n"&gt;hash_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tabname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;int64&lt;/span&gt; &lt;span class="n"&gt;nelem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;HASHCTL&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tabname&lt;/code&gt; is the name of the hash table, used only for debugging.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nelem&lt;/code&gt; specifies the estimated maximum number of elements.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;info&lt;/code&gt; provides configuration details such as the hash function and match function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flags&lt;/code&gt; is a bitmask specifying which fields in &lt;code&gt;info&lt;/code&gt; are valid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The function begins by allocating memory for the &lt;code&gt;HTAB&lt;/code&gt; structure, along with space to store the table name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Initialize the hash header, plus a copy of the table name */&lt;/span&gt;
    &lt;span class="n"&gt;hashp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;MemoryContextAlloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CurrentDynaHashCxt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tabname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;MemSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tabname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;strcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tabname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tabname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, memory is allocated from a specific memory context (which you can think of as a memory pool). Notice that the allocation size is &lt;code&gt;sizeof(HTAB) + strlen(tabname) + 1&lt;/code&gt;. The extra &lt;code&gt;strlen(tabname) + 1&lt;/code&gt; bytes are reserved to store the table’s name. This is confirmed by the assignment &lt;code&gt;hashp-&amp;gt;tabname = (char *)(hashp + 1)&lt;/code&gt;, which points &lt;code&gt;tabname&lt;/code&gt; to the memory immediately following the &lt;code&gt;HTAB&lt;/code&gt; structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
     * Select the appropriate hash function (see comments at head of file).
     */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;HASH_FUNCTION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HASH_BLOBS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;HASH_STRINGS&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;HASH_BLOBS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;HASH_STRINGS&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="cm"&gt;/* We can optimize hashing for common key sizes */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keysize&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uint32_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tag_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/*
         * string_hash used to be considered the default hash method, and in a
         * non-assert build it effectively still is.  But we now consider it
         * an assertion error to not say HASH_STRINGS explicitly.  To help
         * catch mistaken usage of HASH_STRINGS, we also insist on a
         * reasonably long string length: if the keysize is only 4 or 8 bytes,
         * it's almost certainly an integer or pointer not a string.
         */&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;HASH_STRINGS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keysize&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;string_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need to assign a hash function to the table. If there is a given function in &lt;code&gt;info&lt;/code&gt;, we directly use it; if &lt;code&gt;HASH_BLOBS&lt;/code&gt; is set, we assign corresponding hash function based on the size of the key; otherwise, &lt;code&gt;string_hash&lt;/code&gt; is used by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="cm"&gt;/*
     * If you don't specify a match function, it defaults to string_compare if
     * you used string_hash, and to memcmp otherwise.
     *
     * Note: explicitly specifying string_hash is deprecated, because this
     * might not work for callers in loadable modules on some platforms due to
     * referencing a trampoline instead of the string_hash function proper.
     * Specify HASH_STRINGS instead.
     */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;HASH_COMPARE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;string_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HashCompareFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;string_compare&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memcmp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, &lt;code&gt;match&lt;/code&gt; function must also be determined. It's a comparator for user defined key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="cm"&gt;/*
     * Similarly, the key-copying function defaults to strlcpy or memcpy.
     */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;HASH_KEYCOPY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keycopy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keycopy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;string_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/*
         * The signature of keycopy is meant for memcpy(), which returns
         * void*, but strlcpy() returns size_t.  Since we never use the return
         * value of keycopy, and size_t is pretty much always the same size as
         * void *, this should be safe.  The extra cast in the middle is to
         * avoid warnings from -Wcast-function-type.
         */&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keycopy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HashCopyFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_funcptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;strlcpy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keycopy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, the method to copy a key is defined.&lt;/p&gt;

&lt;p&gt;We now comes to the initialization of &lt;code&gt;HASHHDR&lt;/code&gt; - the runtime metadata of the hash table. In order to understand it better, we need to know how does a hash table store elements, namely, its memory layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Layout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  C-style structure embedding
&lt;/h3&gt;

&lt;p&gt;Suppose we want to implement a linked list that can store any user-defined type. In C++, we’d naturally write something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ListNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ListNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In C, however, we don’t have templates. Instead, we achieve a similar effect by defining a small “header” structure that user-defined nodes embed at the &lt;strong&gt;beginning&lt;/strong&gt; of their own struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ListHeader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ListHeader&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CustomizedNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ListHeader&lt;/span&gt; &lt;span class="n"&gt;header_part&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// extra part&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This clever design ensures that for any user-defined node, the first few bytes always form a valid header. In other word, since the header is at offset 0, the list implementation can safely &lt;strong&gt;reinterpret&lt;/strong&gt; any user node as a &lt;code&gt;ListHeader&lt;/code&gt; and manipulate it without knowing the rest of the struct.&lt;/p&gt;

&lt;p&gt;Conceptually, this trick mimics a simple form of inheritance in C: the header acts like a &lt;em&gt;base class&lt;/em&gt;, and the user-defined node behaves like a &lt;em&gt;derived class&lt;/em&gt; that inherits from it.&lt;/p&gt;

&lt;p&gt;Postgres' hash table is implemented in this way: each hash bucket is a linked list, and each entry begins with a header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HASHELEMENT&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HASHELEMENT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="cm"&gt;/* link to next entry in same bucket */&lt;/span&gt;
    &lt;span class="n"&gt;uint32&lt;/span&gt;      &lt;span class="n"&gt;hashvalue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="cm"&gt;/* hash function result for this entry */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;HASHELEMENT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postgres adds one more constraint: the bytes immediately following the header must store the key. As a result, a user-defined hash table entry has the following memory layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------------+
| HASHELEMENT header  |   &amp;lt;- PostgreSQL internal header
+---------------------+
| user key bytes      |   &amp;lt;- this has length = keysize
+---------------------+
| user data bytes     |   &amp;lt;- whatever your struct stores
+---------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the keys always follow &lt;code&gt;HASHELEMENT&lt;/code&gt;, Postgres can compute the key’s starting address with a simple macro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * Key (also entry) part of a HASHELEMENT
 */&lt;/span&gt;
&lt;span class="cp"&gt;#define ELEMENTKEY(helem)  (((char *)(helem)) + MAXALIGN(sizeof(HASHELEMENT)))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Segment-Bucket Layout
&lt;/h3&gt;

&lt;p&gt;Postgres’ hash table stores its buckets indirectly through an array of &lt;em&gt;segments&lt;/em&gt;, referenced by the &lt;code&gt;dir&lt;/code&gt; pointer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="cm"&gt;/* directory of segment starts */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each segment is simply an array of bucket pointers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// dynahash.c&lt;/span&gt;
&lt;span class="cm"&gt;/* A hash bucket is a linked list of HASHELEMENTs */&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="n"&gt;HASHELEMENT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;HASHBUCKET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cm"&gt;/* A hash segment is an array of bucket headers */&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="n"&gt;HASHBUCKET&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;HASHBUCKET&lt;/code&gt; is the head pointer of a linked list of &lt;code&gt;HASHELEMENT&lt;/code&gt;s:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// hsearch.h&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HASHELEMENT&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HASHELEMENT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="cm"&gt;/* link to next entry in same bucket */&lt;/span&gt;
    &lt;span class="n"&gt;uint32&lt;/span&gt;      &lt;span class="n"&gt;hashvalue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="cm"&gt;/* hash function result for this entry */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;HASHELEMENT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As discussed earlier in C-style structure embedding, &lt;code&gt;HASHELEMENT&lt;/code&gt; serves like the &lt;strong&gt;base class&lt;/strong&gt; for every stored entry, with user data following immediately behind it in memory.&lt;/p&gt;

&lt;p&gt;Visually, the structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;  &lt;span class="n"&gt;HTAB&lt;/span&gt;
&lt;span class="o"&gt;+-------+&lt;/span&gt;       &lt;span class="n"&gt;Segments&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;dir&lt;/span&gt;  &lt;span class="o"&gt;|--&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+---------------+&lt;/span&gt;                                      &lt;span class="n"&gt;Buckets&lt;/span&gt;  
&lt;span class="o"&gt;+-------+&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="n"&gt;seg&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;     &lt;span class="o"&gt;|--------------------------------&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;+----------+&lt;/span&gt;
             &lt;span class="o"&gt;+---------------+&lt;/span&gt;             &lt;span class="n"&gt;Buckets&lt;/span&gt;                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
             &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="n"&gt;seg&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;     &lt;span class="o"&gt;|--------&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;+----------+&lt;/span&gt;             &lt;span class="o"&gt;+----------+&lt;/span&gt;
             &lt;span class="o"&gt;+---------------+&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
                                         &lt;span class="o"&gt;+----------+&lt;/span&gt;             &lt;span class="o"&gt;+----------+&lt;/span&gt;
                                         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
                                         &lt;span class="o"&gt;+----------+&lt;/span&gt;             &lt;span class="o"&gt;+----------+&lt;/span&gt;
                                         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
                                         &lt;span class="o"&gt;+----------+&lt;/span&gt;             &lt;span class="o"&gt;+----------+&lt;/span&gt;
                                         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;reserved&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;
                                         &lt;span class="o"&gt;+----------+&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During the initialization, Postgres first allocates the segment directory, and then allocates each segment (each being an array of buckets):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt;
&lt;span class="nf"&gt;init_htab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;int64&lt;/span&gt; &lt;span class="n"&gt;nelem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="cm"&gt;/* Allocate a directory */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CurrentDynaHashCxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hcxt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dsize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="cm"&gt;/* Allocate initial segments */&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;segp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nsegs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;nsegs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nsegs&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;segp&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;segp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seg_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;segp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like the notion of &lt;code&gt;capacity&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; of &lt;code&gt;std::vector&lt;/code&gt; in &lt;code&gt;c++&lt;/code&gt;, hash table allocates &lt;code&gt;dsize&lt;/code&gt; segments in one sitting, with &lt;code&gt;nsegs&lt;/code&gt; recording how many of them are actually used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HASHHDR&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;int64&lt;/span&gt;     &lt;span class="n"&gt;dsize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="cm"&gt;/* directory size */&lt;/span&gt;
  &lt;span class="n"&gt;int64&lt;/span&gt;     &lt;span class="n"&gt;nsegs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="cm"&gt;/* number of allocated segments (&amp;lt;= dsize) */&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given the hash value of a key, a bucket id is computed. Postgres takes the id's higher bits as segment index, and the next &lt;code&gt;sshift&lt;/code&gt; lower bits as bucket offset within a segment. Each segment contains exactly &lt;code&gt;ssize = 2^sshift&lt;/code&gt; buckets. Importantly, both &lt;code&gt;ssize&lt;/code&gt; and &lt;code&gt;sshift&lt;/code&gt; are fixed at table creation and never change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HASHHDR&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;int64&lt;/span&gt;     &lt;span class="n"&gt;ssize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="cm"&gt;/* segment size --- must be power of 2 */&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt;           &lt;span class="n"&gt;sshift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="cm"&gt;/* segment shift = log2(ssize) */&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In comparison, &lt;code&gt;dsize&lt;/code&gt; is mutable and will be doubled when table expands, see table expansion process for details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entry Look Up
&lt;/h2&gt;

&lt;p&gt;The lookup procedure begins with &lt;code&gt;hash_search&lt;/code&gt;, which internally calls &lt;code&gt;hash_search_with_hash_value&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="nf"&gt;hash_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keyPtr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;HASHACTION&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;foundPtr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hash_search_with_hash_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                       &lt;span class="n"&gt;keyPtr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                       &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyPtr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keysize&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                       &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                       &lt;span class="n"&gt;foundPtr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;action&lt;/code&gt; parameter controls what to do after the lookup: search only, insert if missing, delete, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load Check and Expansion Trigger&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the action is insertion, hash table checks the current load by comparing &lt;code&gt;nentries&lt;/code&gt; and &lt;code&gt;max_bucket&lt;/code&gt;. If the former is larger, expansion is triggered. (see  table expansion process).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="cm"&gt;/*
     * If inserting, check if it is time to split a bucket.
     *
     * NOTE: failure to expand table is not a fatal error, it just means we
     * have to run at higher fill factor than we wanted.  However, if we're
     * using the palloc allocator then it will throw error anyway on
     * out-of-memory, so we must do this before modifying the table.
     */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;HASH_ENTER&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;HASH_ENTER_NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/*
         * Can't split if running in partitioned mode, nor if frozen, nor if
         * table is the subject of any active hash_seq_search scans.
         */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;freeList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;nentries&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;max_bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;IS_PARTITIONED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;has_seq_scans&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;expand_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Determining the Bucket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the expansion check, the hash value is passed to &lt;code&gt;hash_initial_lookup&lt;/code&gt;, which returns the bucket ID and the address of the target bucket (a pointer to a &lt;code&gt;HASHBUCKET&lt;/code&gt; pointer).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * Do initial lookup of a bucket for the given hash value, retrieving its
 * bucket number and its hash bucket.
 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt;
&lt;span class="nf"&gt;hash_initial_lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt; &lt;span class="n"&gt;hashvalue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HASHBUCKET&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;bucketptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;HASHHDR&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt; &lt;span class="n"&gt;segp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;int64&lt;/span&gt;       &lt;span class="n"&gt;segment_num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;int64&lt;/span&gt;       &lt;span class="n"&gt;segment_ndx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;uint32&lt;/span&gt;      &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calc_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashvalue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;segment_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sshift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;segment_ndx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MOD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ssize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;segp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;segment_num&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;segp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hash_corrupted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bucketptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;segp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;segment_ndx&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;calc_bucket&lt;/code&gt; determines the bucket ID, whose upper bits select the segment (&lt;code&gt;segment_num&lt;/code&gt;), while the lower &lt;code&gt;sshift&lt;/code&gt; bits select the bucket within that segment (&lt;code&gt;segment_ndx&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scanning the Bucket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once we obtain the bucket pointer, we traverse its linked list of &lt;code&gt;HASHELEMENT&lt;/code&gt;s to locate a matching entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currBucket&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currBucket&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hashvalue&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;hashvalue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ELEMENTKEY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currBucket&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;keyPtr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keysize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;prevBucketPtr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currBucket&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;currBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;prevBucketPtr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#ifdef HASH_STATISTICS
&lt;/span&gt;        &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;collisions&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foundPtr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;foundPtr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currBucket&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lookup first compares the stored &lt;code&gt;hashvalue&lt;/code&gt; (cheap check) and then calls &lt;code&gt;match&lt;/code&gt; to compare the user-provided key data. If no match is found, &lt;code&gt;currBucket&lt;/code&gt; becomes &lt;code&gt;NULL&lt;/code&gt;, and the caller may proceed with insertion or deletion depending on the original action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Expansion And Linear Hashing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mapping a hash value to a bucket id
&lt;/h3&gt;

&lt;p&gt;In the previous chapter, we explained how to locate an entry once its bucket ID is known: use the higher bits as the segment index and the lower bits as the offset within that segment. What we haven't addressed is &lt;strong&gt;how a hash value is mapped to a bucket ID&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A conventional hash table computes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bucket_id = hash_value % num_bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this approach is not rehashing-friendly - when the table grows, rehashing is needed and &lt;code&gt;num_bucket&lt;/code&gt; gets increment. Every  entry's &lt;code&gt;bucket_id&lt;/code&gt; needs to be recomputed, which introduce a great performance penalty. &lt;/p&gt;

&lt;p&gt;By comparison, &lt;code&gt;Postgres&lt;/code&gt; employs linear hashing, minimizing redistribution( see data redistribution for details). The table maintains three members:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HASHHDR&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;uint32&lt;/span&gt;      &lt;span class="n"&gt;max_bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="cm"&gt;/* ID of maximum bucket in use */&lt;/span&gt;
    &lt;span class="n"&gt;uint32&lt;/span&gt;      &lt;span class="n"&gt;high_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="cm"&gt;/* mask to modulo into entire table */&lt;/span&gt;
    &lt;span class="n"&gt;uint32&lt;/span&gt;      &lt;span class="n"&gt;low_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="cm"&gt;/* mask to modulo into lower half of table */&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;max_bucket&lt;/code&gt; is simply the index of the last active bucket, i.e. &lt;code&gt;number_of_buckets - 1&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;high_mask&lt;/code&gt; is an all-one bitmask with the same number of bits as &lt;code&gt;max_bucket&lt;/code&gt;. For instance, assume &lt;code&gt;max_bucket&lt;/code&gt; is 43,  &lt;code&gt;101011&lt;/code&gt; in binary form, then &lt;code&gt;high_mask&lt;/code&gt; is 63, &lt;code&gt;111111&lt;/code&gt; in binary. Mathematically, we have &lt;code&gt;high_mask = next_power_of_two(max_bucket) - 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;low_mask&lt;/code&gt;  is just &lt;code&gt;high_mask&lt;/code&gt; right shift 1: &lt;code&gt;lower_mask = high_mask &amp;gt;&amp;gt; 1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To determine the bucket for a given &lt;code&gt;hash_val&lt;/code&gt;, PostgreSQL first tries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hash_val&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;high_mask&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the result is larger than &lt;code&gt;max_bucket&lt;/code&gt;, it means this bucket hasn’t been allocated yet, so it recomputes using fewer bits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hash_val&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;low_mask&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 1001001   &amp;lt;- max bucket
                 1111111   &amp;lt;- high mask
010110101011101001010110   &amp;lt;- hash value
                  111111   &amp;lt;- low mask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the corresponding code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Convert a hash value to a bucket number */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt;
&lt;span class="nf"&gt;calc_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HASHHDR&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt; &lt;span class="n"&gt;hash_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;uint32&lt;/span&gt;      &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hash_val&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;max_bucket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;low_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  table expansion process
&lt;/h3&gt;

&lt;p&gt;Recall in Entry Look Up, the function &lt;code&gt;expand&lt;/code&gt; is invoked when &lt;code&gt;hctl-&amp;gt;freeList[0].nentries &amp;gt; (int64) hctl-&amp;gt;max_bucket&lt;/code&gt;, which can be roughly interpreted as the load factor reaches 1. This function adds a single hash bucket to the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * Expand the table by adding one more hash bucket.
 */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt;
&lt;span class="n"&gt;expand_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new bucket’s ID is simply the current &lt;code&gt;max_bucket + 1&lt;/code&gt;, so it is placed immediately after the last existing bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;  &lt;span class="n"&gt;HTAB&lt;/span&gt;
&lt;span class="o"&gt;+-------+&lt;/span&gt;        &lt;span class="n"&gt;Segments&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;dir&lt;/span&gt;  &lt;span class="o"&gt;|--&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+---------------+&lt;/span&gt;                                      &lt;span class="n"&gt;Buckets&lt;/span&gt;  
&lt;span class="o"&gt;+-------+&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="n"&gt;seg&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;     &lt;span class="o"&gt;|--------------------------------&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;+----------+&lt;/span&gt;
             &lt;span class="o"&gt;+---------------+&lt;/span&gt;             &lt;span class="n"&gt;Buckets&lt;/span&gt;                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
             &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="n"&gt;seg&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;     &lt;span class="o"&gt;|--------&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;+----------+&lt;/span&gt;             &lt;span class="o"&gt;+----------+&lt;/span&gt;
             &lt;span class="o"&gt;+---------------+&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
                                         &lt;span class="o"&gt;+----------+&lt;/span&gt;             &lt;span class="o"&gt;+----------+&lt;/span&gt;
                                         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
                                         &lt;span class="o"&gt;+----------+&lt;/span&gt;             &lt;span class="o"&gt;+----------+&lt;/span&gt;
                                         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
                                         &lt;span class="o"&gt;+----------+&lt;/span&gt;             &lt;span class="o"&gt;+----------+&lt;/span&gt;
                                         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;|---&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;
                                         &lt;span class="o"&gt;+----------+&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the current segment has space, the new bucket is appended at the end, just like placing bucket 1 after bucket 0. If the segment is full, the bucket moves to the next segment. For example, bucket 4 is the first bucket in segment 1 because segment 0 has no remaining slots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in expand_table:&lt;/span&gt;
    &lt;span class="n"&gt;new_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;max_bucket&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;new_segnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sshift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;new_segndx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MOD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ssize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_segnum&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nsegs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* Allocate new segment if necessary -- could fail if dir full */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_segnum&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dsize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;dir_realloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;new_segnum&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seg_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nsegs&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* OK, we created a new bucket */&lt;/span&gt;
    &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;max_bucket&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A natural question arises: &lt;strong&gt;what if all current segments are used?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For instance, adding bucket 8 would require segment 2, which might not exist yet. In such cases, additional segments are to be allocated to accommodate new bucket, and that's why &lt;code&gt;dir_realloc(hashp)&lt;/code&gt; is called. &lt;/p&gt;

&lt;p&gt;PostgreSQL always doubles the number of segments during reallocation. This is because the segment ID is determined by the high-order bits of the bucket ID. Expanding the table effectively uses &lt;strong&gt;one more high bit&lt;/strong&gt;, which doubles the segment address space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt;
&lt;span class="nf"&gt;dir_realloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="cm"&gt;/* Reallocate directory */&lt;/span&gt;
    &lt;span class="n"&gt;new_dsize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dsize&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;old_dirsize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dsize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;new_dirsize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_dsize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;old_p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;CurrentDynaHashCxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hcxt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HASHSEGMENT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;new_dirsize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_dirsize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;MemSet&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;old_dirsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_dirsize&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;old_dirsize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dsize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_dsize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="cm"&gt;/* XXX assume the allocator is palloc, so we know how to free */&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DynaHashAlloc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;pfree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;memcpy&lt;/code&gt; copies all existing segment pointers into the newly allocated directory. The remaining space is zeroed out, preparing room for future segments.&lt;/p&gt;

&lt;h3&gt;
  
  
  data redistribution
&lt;/h3&gt;

&lt;p&gt;After a new bucket is added, some entries may map to a different bucket and therefore need to be moved. A key property of  linear hashing is that only one existing bucket is ever subject to redistribution when a new bucket is created. More precisely, when we add a new bucket with id &lt;code&gt;new_bucket&lt;/code&gt;, we only need to inspect the bucket with &lt;code&gt;id = new_bucket &amp;amp; low_mask&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="cm"&gt;/*
     * *Before* changing masks, find old bucket corresponding to same hash
     * values; values in that bucket may need to be relocated to new bucket.
     * Note that new_bucket is certainly larger than low_mask at this point,
     * so we can skip the first step of the regular hash mask calc.
     */&lt;/span&gt;
    &lt;span class="n"&gt;old_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;low_mask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See Proof: Only One Bucket Requires Rehashing for details.&lt;/p&gt;

&lt;p&gt;One by one, we check entries in that bucket, and move those whose bucket id changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="n"&gt;old_segnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;old_bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sshift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;old_segndx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MOD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ssize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;old_seg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;old_segnum&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;new_seg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;new_segnum&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="n"&gt;oldlink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;old_seg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;old_segndx&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;newlink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;new_seg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;new_segndx&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;oldlink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="n"&gt;currElement&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="n"&gt;currElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nextElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;nextElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currElement&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;calc_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currElement&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hashvalue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;old_bucket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;oldlink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;oldlink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;currElement&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;newlink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;newlink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;currElement&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Proof: Only One Bucket Requires Rehashing
&lt;/h3&gt;

&lt;p&gt;Assume an entry with hash value &lt;code&gt;hash_val&lt;/code&gt; is stored in bucket &lt;code&gt;old_bucket&lt;/code&gt; before the new bucket is added. We want to determine under what conditions this entry needs to be moved.&lt;/p&gt;

&lt;p&gt;When a new bucket is added, &lt;code&gt;max_bucket++&lt;/code&gt;, and this leads to two possible scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Case 1: &lt;code&gt;max_bucket&lt;/code&gt; grows from &lt;code&gt;2^n - 1&lt;/code&gt; to &lt;code&gt;2^n&lt;/code&gt;. its bit length increases by 1, so  &lt;code&gt;high_mask&lt;/code&gt; and &lt;code&gt;low_mask&lt;/code&gt; are updated.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;expand_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTAB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hashp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;max_bucket&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * If we crossed a power of 2, readjust masks.
     */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;new_bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;low_mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;new_bucket&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;low_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Case 2: Otherwise, we haven’t crossed a power-of-two boundary, so neither mask changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We now analyze these two cases separately, focusing on the algorithm of getting bucket_id from hash value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Convert a hash value to a bucket number */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt;
&lt;span class="nf"&gt;calc_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HASHHDR&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt; &lt;span class="n"&gt;hash_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;uint32&lt;/span&gt;      &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hash_val&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;max_bucket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hctl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;low_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  case 1
&lt;/h4&gt;

&lt;p&gt;For case 1, let’s call the masks before the expansion (&lt;code&gt;max_bucket == 2^n − 1&lt;/code&gt;) &lt;code&gt;old_low_mask&lt;/code&gt; and &lt;code&gt;old_high_mask&lt;/code&gt;, and the masks after the expansion (&lt;code&gt;max_bucket == 2^n&lt;/code&gt;) &lt;code&gt;new_low_mask&lt;/code&gt; and &lt;code&gt;new_high_mask&lt;/code&gt;. By the update rule, we immediately have:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;old_high_mask == new_low_mask&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before the expansion, when &lt;code&gt;max_bucket&lt;/code&gt; was still &lt;code&gt;2^n − 1&lt;/code&gt;, we also had:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;old_high_mask == old_max_bucket&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_val &amp;amp; old_high_mask &amp;lt;= old_max_bucket&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means the &lt;code&gt;if&lt;/code&gt; condition in &lt;code&gt;calc_bucket()&lt;/code&gt; never fired, and therefore&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;old_bucket_id = hash_val &amp;amp; old_high_mask&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When &lt;code&gt;max_bucket&lt;/code&gt; becomes &lt;code&gt;2^n&lt;/code&gt;, we want to determine when an entry’s new bucket ID differs from its old one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sub-case 1: the if-condition holds &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;new_bucket_id = hash_val &amp;amp; new_low_mask&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But &lt;code&gt;new_low_mask == old_high_mask&lt;/code&gt;, so this is exactly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;new_bucket_id = old_bucket_id&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and the entry does not need to move.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sub-case 2: the if-condition fails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;new_bucket_id = hash_val &amp;amp; new_high_mask&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_val &amp;amp; new_high_mask &amp;lt;= new_max_bucket ... [1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note that &lt;code&gt;new_max_bucket&lt;/code&gt; is the smallest number with the same number of bits as &lt;code&gt;new_high_mask&lt;/code&gt;, so the only way [1] holds  is when: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_val &amp;amp; new_high_mask == new_max_bucket ... [2]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since &lt;code&gt;new_max_bucket&lt;/code&gt; has exactly one bit set — the new highest bit — masking the same hash value with the &lt;em&gt;shorter&lt;/em&gt; &lt;code&gt;old_high_mask&lt;/code&gt; necessarily yields:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;old_bucket_id = hash_val &amp;amp; old_high_mask == 0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In addition, since the old bucket id is 0, it can also be represented by: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;old_bucket_id = hash_val &amp;amp; old_low_mask&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                1111111   &amp;lt;- new high mask  
  100101001001001000000   &amp;lt;- hash value
                 111111   &amp;lt;- old high mask = new low mask
  So:
  new_bucket_id = 1000000
  old_bucket_id =  000000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combining these two sub-cases, we can conclude that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;any entries need to be moved must be in bucket 0, mathematically, its bucket id = new_max_bucket &amp;amp; old_low_mask&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  case 2
&lt;/h4&gt;

&lt;p&gt;In case 2, neither &lt;code&gt;low_mask&lt;/code&gt; nor &lt;code&gt;high_mask&lt;/code&gt; changes. Looking at the &lt;code&gt;calc_bucket&lt;/code&gt;, we can reason as follows: &lt;/p&gt;

&lt;p&gt;For an entry to move, its old bucket ID must differ from its new bucket ID. In other words, two calls to &lt;code&gt;calc_bucket()&lt;/code&gt; - before and after adding the new bucket - must return different results. Since  &lt;code&gt;hash_value&lt;/code&gt;, &lt;code&gt;high_mask&lt;/code&gt;, and &lt;code&gt;low_mask&lt;/code&gt; remain the same, there are only two ways this could happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;if&lt;/code&gt; condition succeeds before the expansion and fails afterward, or&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;if&lt;/code&gt; condition fails before the expansion and succeeds afterward.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Claim: the second situation is impossible&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Prove by contradiction: &lt;/p&gt;

&lt;p&gt;If the &lt;code&gt;if&lt;/code&gt; condition fails in the earlier call, then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_value &amp;amp; high_bitmask &amp;lt;= old_max_bucket ... [1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the &lt;code&gt;if&lt;/code&gt; condition succeeds in the later call, then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_value &amp;amp; high_bitmask &amp;gt; new_max_bucket ... [2]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And of course:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;new_max_bucket &amp;gt; old_max_bucket ... [3]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Combining [1] [2] and [3] yields:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_value &amp;amp; high_bitmask &amp;gt; new_max_bucket &amp;gt; old_max_bucket &amp;gt;= hash_value &amp;amp; high_bitmask&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;which is clearly a contradiction. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So this case cannot occur.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now consider the first possibility:&lt;/p&gt;

&lt;p&gt;If the &lt;code&gt;if&lt;/code&gt; condition succeeds in the earlier call, then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_value &amp;amp; high_bitmask &amp;gt; old_max_bucket ...  [1] &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the &lt;code&gt;if&lt;/code&gt; condition fails in the later call, then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_value &amp;amp; high_bitmask &amp;lt;= new_max_bucket ... [2] &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We further have:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;new_max_bucket == old_max_bucket + 1 ... [3] &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Combining [1] [2] and [3] yields:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hash_value &amp;amp; high_bitmask == new_max_bucket ... [4]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From [4] we can derive:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;old_bucket_id &lt;/p&gt;

&lt;p&gt;== hash_value &amp;amp; low_bitmask &lt;/p&gt;

&lt;p&gt;== (hash_value &amp;amp; high_bitmask) &amp;amp; low_bitmask &lt;/p&gt;

&lt;p&gt;== new_max_bucket &amp;amp; low_bitmask&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thus the only entries that need to be moved are those in bucket: &lt;code&gt;new_max_bucket &amp;amp; low_mask&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  combine these two cases
&lt;/h4&gt;

&lt;p&gt;Putting both cases together, we can draw a final conclusion:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Whenever a new bucket is added to the hash table, the only entries that may need to be relocated are those originally in bucket &lt;code&gt;new_max_bucket &amp;amp; low_mask&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This follows directly from the earlier analysis: regardless of whether &lt;code&gt;low_mask&lt;/code&gt; changes (Case 1) or stays the same (Case 2), the condition for an entry's bucket ID to change is always: its original bucket must match &lt;code&gt;new_max_bucket &amp;amp; low_mask&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>c</category>
    </item>
  </channel>
</rss>
