Get rid of artificial restriction on hash table sizes on Windows.
authorTom Lane <[email protected]>
Sun, 25 Jul 2021 18:02:27 +0000 (14:02 -0400)
committerTom Lane <[email protected]>
Sun, 25 Jul 2021 18:02:27 +0000 (14:02 -0400)
The point of introducing the hash_mem_multiplier GUC was to let users
reproduce the old behavior of hash aggregation, i.e. that it could use
more than work_mem at need.  However, the implementation failed to get
the job done on Win64, where work_mem is clamped to 2GB to protect
various places that calculate memory sizes using "long int".  As
written, the same clamp was applied to hash_mem.  This resulted in
severe performance regressions for queries requiring a bit more than
2GB for hash aggregation, as they now spill to disk and there's no
way to stop that.

Getting rid of the work_mem restriction seems like a good idea, but
it's a big job and could not conceivably be back-patched.  However,
there's only a fairly small number of places that are concerned with
the hash_mem value, and it turns out to be possible to remove the
restriction there without too much code churn or any ABI breaks.
So, let's do that for now to fix the regression, and leave the
larger task for another day.

This patch does introduce a bit more infrastructure that should help
with the larger task, namely pg_bitutils.h support for working with
size_t values.

Per gripe from Laurent Hasson.  Back-patch to v13 where the
behavior change came in.

Discussion: https://postgr.es/m/997817.1627074924@sss.pgh.pa.us
Discussion: https://postgr.es/m/MN2PR15MB25601E80A9B6D1BA6F592B1985E39@MN2PR15MB2560.namprd15.prod.outlook.com

12 files changed:
src/backend/executor/execGrouping.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeMemoize.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/pathnode.c
src/backend/storage/ipc/shm_mq.c
src/include/miscadmin.h
src/include/port/pg_bitutils.h

index 5fd0b26cbc16b82b6f9e4bf629ce361cb9d961da..c11427a1f66c6734254623d763c3532396e149d2 100644 (file)
@@ -165,14 +165,16 @@ BuildTupleHashTableExt(PlanState *parent,
 {
    TupleHashTable hashtable;
    Size        entrysize = sizeof(TupleHashEntryData) + additionalsize;
-   int         hash_mem = get_hash_mem();
+   Size        hash_mem_limit;
    MemoryContext oldcontext;
    bool        allow_jit;
 
    Assert(nbuckets > 0);
 
    /* Limit initial table size request to not more than hash_mem */
-   nbuckets = Min(nbuckets, (long) ((hash_mem * 1024L) / entrysize));
+   hash_mem_limit = get_hash_memory_limit() / entrysize;
+   if (nbuckets > hash_mem_limit)
+       nbuckets = hash_mem_limit;
 
    oldcontext = MemoryContextSwitchTo(metacxt);
 
index 914b02ceee48e240f50ff854980fad07a3833ee2..39bea204d16ae2790588cb7fe7825aa84a366858 100644 (file)
@@ -1802,15 +1802,15 @@ hash_agg_set_limits(double hashentrysize, double input_groups, int used_bits,
 {
    int         npartitions;
    Size        partition_mem;
-   int         hash_mem = get_hash_mem();
+   Size        hash_mem_limit = get_hash_memory_limit();
 
    /* if not expected to spill, use all of hash_mem */
-   if (input_groups * hashentrysize < hash_mem * 1024L)
+   if (input_groups * hashentrysize <= hash_mem_limit)
    {
        if (num_partitions != NULL)
            *num_partitions = 0;
-       *mem_limit = hash_mem * 1024L;
-       *ngroups_limit = *mem_limit / hashentrysize;
+       *mem_limit = hash_mem_limit;
+       *ngroups_limit = hash_mem_limit / hashentrysize;
        return;
    }
 
@@ -1835,10 +1835,10 @@ hash_agg_set_limits(double hashentrysize, double input_groups, int used_bits,
     * minimum number of partitions, so we aren't going to dramatically exceed
     * work mem anyway.
     */
-   if (hash_mem * 1024L > 4 * partition_mem)
-       *mem_limit = hash_mem * 1024L - partition_mem;
+   if (hash_mem_limit > 4 * partition_mem)
+       *mem_limit = hash_mem_limit - partition_mem;
    else
-       *mem_limit = hash_mem * 1024L * 0.75;
+       *mem_limit = hash_mem_limit * 0.75;
 
    if (*mem_limit > hashentrysize)
        *ngroups_limit = *mem_limit / hashentrysize;
@@ -1992,32 +1992,36 @@ static int
 hash_choose_num_partitions(double input_groups, double hashentrysize,
                           int used_bits, int *log2_npartitions)
 {
-   Size        mem_wanted;
-   int         partition_limit;
+   Size        hash_mem_limit = get_hash_memory_limit();
+   double      partition_limit;
+   double      mem_wanted;
+   double      dpartitions;
    int         npartitions;
    int         partition_bits;
-   int         hash_mem = get_hash_mem();
 
    /*
     * Avoid creating so many partitions that the memory requirements of the
     * open partition files are greater than 1/4 of hash_mem.
     */
    partition_limit =
-       (hash_mem * 1024L * 0.25 - HASHAGG_READ_BUFFER_SIZE) /
+       (hash_mem_limit * 0.25 - HASHAGG_READ_BUFFER_SIZE) /
        HASHAGG_WRITE_BUFFER_SIZE;
 
    mem_wanted = HASHAGG_PARTITION_FACTOR * input_groups * hashentrysize;
 
    /* make enough partitions so that each one is likely to fit in memory */
-   npartitions = 1 + (mem_wanted / (hash_mem * 1024L));
+   dpartitions = 1 + (mem_wanted / hash_mem_limit);
+
+   if (dpartitions > partition_limit)
+       dpartitions = partition_limit;
 
-   if (npartitions > partition_limit)
-       npartitions = partition_limit;
+   if (dpartitions < HASHAGG_MIN_PARTITIONS)
+       dpartitions = HASHAGG_MIN_PARTITIONS;
+   if (dpartitions > HASHAGG_MAX_PARTITIONS)
+       dpartitions = HASHAGG_MAX_PARTITIONS;
 
-   if (npartitions < HASHAGG_MIN_PARTITIONS)
-       npartitions = HASHAGG_MIN_PARTITIONS;
-   if (npartitions > HASHAGG_MAX_PARTITIONS)
-       npartitions = HASHAGG_MAX_PARTITIONS;
+   /* HASHAGG_MAX_PARTITIONS limit makes this safe */
+   npartitions = (int) dpartitions;
 
    /* ceil(log2(npartitions)) */
    partition_bits = my_log2(npartitions);
@@ -2030,7 +2034,7 @@ hash_choose_num_partitions(double input_groups, double hashentrysize,
        *log2_npartitions = partition_bits;
 
    /* number of partitions will be a power of two */
-   npartitions = 1L << partition_bits;
+   npartitions = 1 << partition_bits;
 
    return npartitions;
 }
index c5f2d1d22b16a111a6d877221b80318b9e552a0b..73eb074cbf9151a5d143863ab5761a1c3bfca552 100644 (file)
@@ -675,15 +675,12 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
 {
    int         tupsize;
    double      inner_rel_bytes;
-   long        bucket_bytes;
-   long        hash_table_bytes;
-   long        skew_table_bytes;
-   long        max_pointers;
-   long        mppow2;
+   size_t      hash_table_bytes;
+   size_t      bucket_bytes;
+   size_t      max_pointers;
    int         nbatch = 1;
    int         nbuckets;
    double      dbuckets;
-   int         hash_mem = get_hash_mem();
 
    /* Force a plausible relation size if no info */
    if (ntuples <= 0.0)
@@ -700,9 +697,9 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
    inner_rel_bytes = ntuples * tupsize;
 
    /*
-    * Target in-memory hashtable size is hash_mem kilobytes.
+    * Compute in-memory hashtable size limit from GUCs.
     */
-   hash_table_bytes = hash_mem * 1024L;
+   hash_table_bytes = get_hash_memory_limit();
 
    /*
     * Parallel Hash tries to use the combined hash_mem of all workers to
@@ -710,7 +707,14 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
     * per worker and tries to process batches in parallel.
     */
    if (try_combined_hash_mem)
-       hash_table_bytes += hash_table_bytes * parallel_workers;
+   {
+       /* Careful, this could overflow size_t */
+       double      newlimit;
+
+       newlimit = (double) hash_table_bytes * (double) (parallel_workers + 1);
+       newlimit = Min(newlimit, (double) SIZE_MAX);
+       hash_table_bytes = (size_t) newlimit;
+   }
 
    *space_allowed = hash_table_bytes;
 
@@ -730,9 +734,12 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
     */
    if (useskew)
    {
-       skew_table_bytes = hash_table_bytes * SKEW_HASH_MEM_PERCENT / 100;
+       size_t      bytes_per_mcv;
+       size_t      skew_mcvs;
 
        /*----------
+        * Compute number of MCVs we could hold in hash_table_bytes
+        *
         * Divisor is:
         * size of a hash tuple +
         * worst-case size of skewBucket[] per MCV +
@@ -740,12 +747,26 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
         * size of skew bucket struct itself
         *----------
         */
-       *num_skew_mcvs = skew_table_bytes / (tupsize +
-                                            (8 * sizeof(HashSkewBucket *)) +
-                                            sizeof(int) +
-                                            SKEW_BUCKET_OVERHEAD);
-       if (*num_skew_mcvs > 0)
-           hash_table_bytes -= skew_table_bytes;
+       bytes_per_mcv = tupsize +
+           (8 * sizeof(HashSkewBucket *)) +
+           sizeof(int) +
+           SKEW_BUCKET_OVERHEAD;
+       skew_mcvs = hash_table_bytes / bytes_per_mcv;
+
+       /*
+        * Now scale by SKEW_HASH_MEM_PERCENT (we do it in this order so as
+        * not to worry about size_t overflow in the multiplication)
+        */
+       skew_mcvs = (skew_mcvs * SKEW_HASH_MEM_PERCENT) / 100;
+
+       /* Now clamp to integer range */
+       skew_mcvs = Min(skew_mcvs, INT_MAX);
+
+       *num_skew_mcvs = (int) skew_mcvs;
+
+       /* Reduce hash_table_bytes by the amount needed for the skew table */
+       if (skew_mcvs > 0)
+           hash_table_bytes -= skew_mcvs * bytes_per_mcv;
    }
    else
        *num_skew_mcvs = 0;
@@ -753,22 +774,20 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
    /*
     * Set nbuckets to achieve an average bucket load of NTUP_PER_BUCKET when
     * memory is filled, assuming a single batch; but limit the value so that
-    * the pointer arrays we'll try to allocate do not exceed hash_mem nor
-    * MaxAllocSize.
+    * the pointer arrays we'll try to allocate do not exceed hash_table_bytes
+    * nor MaxAllocSize.
     *
     * Note that both nbuckets and nbatch must be powers of 2 to make
     * ExecHashGetBucketAndBatch fast.
     */
-   max_pointers = *space_allowed / sizeof(HashJoinTuple);
+   max_pointers = hash_table_bytes / sizeof(HashJoinTuple);
    max_pointers = Min(max_pointers, MaxAllocSize / sizeof(HashJoinTuple));
    /* If max_pointers isn't a power of 2, must round it down to one */
-   mppow2 = 1L << my_log2(max_pointers);
-   if (max_pointers != mppow2)
-       max_pointers = mppow2 / 2;
+   max_pointers = pg_prevpower2_size_t(max_pointers);
 
    /* Also ensure we avoid integer overflow in nbatch and nbuckets */
    /* (this step is redundant given the current value of MaxAllocSize) */
-   max_pointers = Min(max_pointers, INT_MAX / 2);
+   max_pointers = Min(max_pointers, INT_MAX / 2 + 1);
 
    dbuckets = ceil(ntuples / NTUP_PER_BUCKET);
    dbuckets = Min(dbuckets, max_pointers);
@@ -776,7 +795,7 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
    /* don't let nbuckets be really small, though ... */
    nbuckets = Max(nbuckets, 1024);
    /* ... and force it to be a power of 2. */
-   nbuckets = 1 << my_log2(nbuckets);
+   nbuckets = pg_nextpower2_32(nbuckets);
 
    /*
     * If there's not enough space to store the projected number of tuples and
@@ -786,10 +805,10 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
    if (inner_rel_bytes + bucket_bytes > hash_table_bytes)
    {
        /* We'll need multiple batches */
-       long        lbuckets;
+       size_t      sbuckets;
        double      dbatch;
        int         minbatch;
-       long        bucket_size;
+       size_t      bucket_size;
 
        /*
         * If Parallel Hash with combined hash_mem would still need multiple
@@ -813,10 +832,10 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
         * overhead for the hash code, pointer to the next tuple, etc.
         */
        bucket_size = (tupsize * NTUP_PER_BUCKET + sizeof(HashJoinTuple));
-       lbuckets = 1L << my_log2(hash_table_bytes / bucket_size);
-       lbuckets = Min(lbuckets, max_pointers);
-       nbuckets = (int) lbuckets;
-       nbuckets = 1 << my_log2(nbuckets);
+       sbuckets = pg_nextpower2_size_t(hash_table_bytes / bucket_size);
+       sbuckets = Min(sbuckets, max_pointers);
+       nbuckets = (int) sbuckets;
+       nbuckets = pg_nextpower2_32(nbuckets);
        bucket_bytes = nbuckets * sizeof(HashJoinTuple);
 
        /*
@@ -1097,14 +1116,12 @@ ExecParallelHashIncreaseNumBatches(HashJoinTable hashtable)
                /* Figure out how many batches to use. */
                if (hashtable->nbatch == 1)
                {
-                   int         hash_mem = get_hash_mem();
-
                    /*
                     * We are going from single-batch to multi-batch.  We need
                     * to switch from one large combined memory budget to the
                     * regular hash_mem budget.
                     */
-                   pstate->space_allowed = hash_mem * 1024L;
+                   pstate->space_allowed = get_hash_memory_limit();
 
                    /*
                     * The combined hash_mem of all participants wasn't
@@ -1113,7 +1130,7 @@ ExecParallelHashIncreaseNumBatches(HashJoinTable hashtable)
                     * insufficient.  So try two batches per participant,
                     * rounded up to a power of two.
                     */
-                   new_nbatch = 1 << my_log2(pstate->nparticipants * 2);
+                   new_nbatch = pg_nextpower2_32(pstate->nparticipants * 2);
                }
                else
                {
@@ -1152,7 +1169,7 @@ ExecParallelHashIncreaseNumBatches(HashJoinTable hashtable)
                                   MaxAllocSize / sizeof(dsa_pointer_atomic));
                    new_nbuckets = (int) dbuckets;
                    new_nbuckets = Max(new_nbuckets, 1024);
-                   new_nbuckets = 1 << my_log2(new_nbuckets);
+                   new_nbuckets = pg_nextpower2_32(new_nbuckets);
                    dsa_free(hashtable->area, old_batch0->buckets);
                    hashtable->batches[0].shared->buckets =
                        dsa_allocate(hashtable->area,
@@ -3372,39 +3389,24 @@ ExecParallelHashTuplePrealloc(HashJoinTable hashtable, int batchno, size_t size)
 }
 
 /*
- * Get a hash_mem value by multiplying the work_mem GUC's value by the
- * hash_mem_multiplier GUC's value.
+ * Calculate the limit on how much memory can be used by Hash and similar
+ * plan types.  This is work_mem times hash_mem_multiplier, and is
+ * expressed in bytes.
  *
- * Returns a work_mem style KB value that hash-based nodes (including but not
- * limited to hash join) use in place of work_mem.  This is subject to the
- * same restrictions as work_mem itself.  (There is no such thing as the
- * hash_mem GUC, but it's convenient for our callers to pretend that there
- * is.)
- *
- * Exported for use by the planner, as well as other hash-based executor
+ * Exported for use by the planner, as well as other hash-like executor
  * nodes.  This is a rather random place for this, but there is no better
  * place.
  */
-int
-get_hash_mem(void)
+size_t
+get_hash_memory_limit(void)
 {
-   double      hash_mem;
+   double      mem_limit;
 
-   Assert(hash_mem_multiplier >= 1.0);
+   /* Do initial calculation in double arithmetic */
+   mem_limit = (double) work_mem * hash_mem_multiplier * 1024.0;
 
-   hash_mem = (double) work_mem * hash_mem_multiplier;
-
-   /*
-    * guc.c enforces a MAX_KILOBYTES limitation on work_mem in order to
-    * support the assumption that raw derived byte values can be stored in
-    * 'long' variables.  The returned hash_mem value must also meet this
-    * assumption.
-    *
-    * We clamp the final value rather than throw an error because it should
-    * be possible to set work_mem and hash_mem_multiplier independently.
-    */
-   if (hash_mem < MAX_KILOBYTES)
-       return (int) hash_mem;
+   /* Clamp in case it doesn't fit in size_t */
+   mem_limit = Min(mem_limit, (double) SIZE_MAX);
 
-   return MAX_KILOBYTES;
+   return (size_t) mem_limit;
 }
index 2fde4ebce69e82be91e3fc5fe927e2b17dbcde87..bec588b3a041e84ebe34764d05204293a5889f04 100644 (file)
@@ -905,7 +905,7 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
    mstate->mem_used = 0;
 
    /* Limit the total memory consumed by the cache to this */
-   mstate->mem_limit = get_hash_mem() * 1024L;
+   mstate->mem_limit = get_hash_memory_limit();
 
    /* A memory context dedicated for the cache */
    mstate->tableContext = AllocSetContextCreate(CurrentMemoryContext,
index b54cf34a8e1134aeaa5dd2030a86842a9f45d7fe..30c8595f7619da4449605fbd3fc7a9f8ca06de6f 100644 (file)
@@ -2438,7 +2438,7 @@ cost_memoize_rescan(PlannerInfo *root, MemoizePath *mpath,
    Cost        total_cost;
 
    /* available cache space */
-   hash_mem_bytes = get_hash_mem() * 1024L;
+   hash_mem_bytes = get_hash_memory_limit();
 
    /*
     * Set the number of bytes each cache entry should consume in the cache.
@@ -3860,7 +3860,6 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
    Cost        run_cost = workspace->run_cost;
    int         numbuckets = workspace->numbuckets;
    int         numbatches = workspace->numbatches;
-   int         hash_mem;
    Cost        cpu_per_tuple;
    QualCost    hash_qual_cost;
    QualCost    qp_qual_cost;
@@ -3986,10 +3985,8 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
     * that way, so it will be unable to drive the batch size below hash_mem
     * when this is true.)
     */
-   hash_mem = get_hash_mem();
    if (relation_byte_size(clamp_row_est(inner_path_rows * innermcvfreq),
-                          inner_path->pathtarget->width) >
-       (hash_mem * 1024L))
+                          inner_path->pathtarget->width) > get_hash_memory_limit())
        startup_cost += disable_cost;
 
    /*
index 1868c4eff4716c2f712c59a6d08ed445e397b143..86816ffe19dbe55992c83b65f21fcb1ce682b19d 100644 (file)
@@ -3668,7 +3668,7 @@ consider_groupingsets_paths(PlannerInfo *root,
                            double dNumGroups)
 {
    Query      *parse = root->parse;
-   int         hash_mem = get_hash_mem();
+   Size        hash_mem_limit = get_hash_memory_limit();
 
    /*
     * If we're not being offered sorted input, then only consider plans that
@@ -3734,7 +3734,7 @@ consider_groupingsets_paths(PlannerInfo *root,
         * with.  Override hash_mem in that case; otherwise, we'll rely on the
         * sorted-input case to generate usable mixed paths.
         */
-       if (hashsize > hash_mem * 1024L && gd->rollups)
+       if (hashsize > hash_mem_limit && gd->rollups)
            return;             /* nope, won't fit */
 
        /*
@@ -3853,7 +3853,7 @@ consider_groupingsets_paths(PlannerInfo *root,
    {
        List       *rollups = NIL;
        List       *hash_sets = list_copy(gd->unsortable_sets);
-       double      availspace = (hash_mem * 1024.0);
+       double      availspace = hash_mem_limit;
        ListCell   *lc;
 
        /*
index b5a61f393351e6252d35e3103c387312be38b835..c9f7a09d1021a697f2da910d1dbedfb5fb44ecbd 100644 (file)
@@ -724,7 +724,6 @@ static bool
 subplan_is_hashable(Plan *plan)
 {
    double      subquery_size;
-   int         hash_mem = get_hash_mem();
 
    /*
     * The estimated size of the subquery result must fit in hash_mem. (Note:
@@ -734,7 +733,7 @@ subplan_is_hashable(Plan *plan)
     */
    subquery_size = plan->plan_rows *
        (MAXALIGN(plan->plan_width) + MAXALIGN(SizeofHeapTupleHeader));
-   if (subquery_size > hash_mem * 1024L)
+   if (subquery_size > get_hash_memory_limit())
        return false;
 
    return true;
@@ -749,7 +748,6 @@ static bool
 subpath_is_hashable(Path *path)
 {
    double      subquery_size;
-   int         hash_mem = get_hash_mem();
 
    /*
     * The estimated size of the subquery result must fit in hash_mem. (Note:
@@ -759,7 +757,7 @@ subpath_is_hashable(Path *path)
     */
    subquery_size = path->rows *
        (MAXALIGN(path->pathtarget->width) + MAXALIGN(SizeofHeapTupleHeader));
-   if (subquery_size > hash_mem * 1024L)
+   if (subquery_size > get_hash_memory_limit())
        return false;
 
    return true;
index 037dfaacfd4241f5c205a291c91e97da24bac894..e9256a2d4d242368ce19290469b04e94a2a9495e 100644 (file)
@@ -1019,7 +1019,7 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
                    const char *construct)
 {
    int         numGroupCols = list_length(groupClauses);
-   int         hash_mem = get_hash_mem();
+   Size        hash_mem_limit = get_hash_memory_limit();
    bool        can_sort;
    bool        can_hash;
    Size        hashentrysize;
@@ -1055,13 +1055,11 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
     */
    hashentrysize = MAXALIGN(input_path->pathtarget->width) + MAXALIGN(SizeofMinimalTupleHeader);
 
-   if (hashentrysize * dNumGroups > hash_mem * 1024L)
+   if (hashentrysize * dNumGroups > hash_mem_limit)
        return false;
 
    /*
-    * See if the estimated cost is no more than doing it the other way.  We
-    * deliberately give the hash case more memory when hash_mem exceeds
-    * standard work mem (i.e. when hash_mem_multiplier exceeds 1.0).
+    * See if the estimated cost is no more than doing it the other way.
     *
     * We need to consider input_plan + hashagg versus input_plan + sort +
     * group.  Note that the actual result plan might involve a SetOp or
index 0c94cbe767aa8104cb9e230e1151ddf5ca50df50..41cbf328c4657caa88986e668ffae500681306c3 100644 (file)
@@ -1794,9 +1794,8 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
         * planner.c).
         */
        int         hashentrysize = subpath->pathtarget->width + 64;
-       int         hash_mem = get_hash_mem();
 
-       if (hashentrysize * pathnode->path.rows > hash_mem * 1024L)
+       if (hashentrysize * pathnode->path.rows > get_hash_memory_limit())
        {
            /*
             * We should not try to hash.  Hack the SpecialJoinInfo to
index 446f20df4617e36a72c243a8092c2736c8c87f35..91a7093e0335a5ae00ea2c8d0c1e4ae5f5859167 100644 (file)
@@ -727,11 +727,7 @@ shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait)
             * Increase size to the next power of 2 that's >= nbytes, but
             * limit to MaxAllocSize.
             */
-#if SIZEOF_SIZE_T == 4
-           newbuflen = pg_nextpower2_32(nbytes);
-#else
-           newbuflen = pg_nextpower2_64(nbytes);
-#endif
+           newbuflen = pg_nextpower2_size_t(nbytes);
            newbuflen = Min(newbuflen, MaxAllocSize);
 
            if (mqh->mqh_buffer != NULL)
index 4dc343cbc597df026c9df687fb39167be9c7f460..68d840d6996f94318b47aac6c0ee11313248dc6e 100644 (file)
@@ -486,6 +486,6 @@ extern bool BackupInProgress(void);
 extern void CancelBackup(void);
 
 /* in executor/nodeHash.c */
-extern int get_hash_mem(void);
+extern size_t get_hash_memory_limit(void);
 
 #endif                         /* MISCADMIN_H */
index f9b77ec2780ad73824f6468c022719889c613501..086bd08132ff961f09ec13072ce64f5b0b320de9 100644 (file)
@@ -137,7 +137,7 @@ pg_rightmost_one_pos64(uint64 word)
 
 /*
  * pg_nextpower2_32
- *     Returns the next highest power of 2 of 'num', or 'num', if it's
+ *     Returns the next higher power of 2 above 'num', or 'num' if it's
  *     already a power of 2.
  *
  * 'num' mustn't be 0 or be above PG_UINT32_MAX / 2 + 1.
@@ -160,7 +160,7 @@ pg_nextpower2_32(uint32 num)
 
 /*
  * pg_nextpower2_64
- *     Returns the next highest power of 2 of 'num', or 'num', if it's
+ *     Returns the next higher power of 2 above 'num', or 'num' if it's
  *     already a power of 2.
  *
  * 'num' mustn't be 0 or be above PG_UINT64_MAX / 2  + 1.
@@ -181,6 +181,52 @@ pg_nextpower2_64(uint64 num)
    return ((uint64) 1) << (pg_leftmost_one_pos64(num) + 1);
 }
 
+/*
+ * pg_nextpower2_size_t
+ *     Returns the next higher power of 2 above 'num', for a size_t input.
+ */
+#if SIZEOF_SIZE_T == 4
+#define pg_nextpower2_size_t(num) pg_nextpower2_32(num)
+#else
+#define pg_nextpower2_size_t(num) pg_nextpower2_64(num)
+#endif
+
+/*
+ * pg_prevpower2_32
+ *     Returns the next lower power of 2 below 'num', or 'num' if it's
+ *     already a power of 2.
+ *
+ * 'num' mustn't be 0.
+ */
+static inline uint32
+pg_prevpower2_32(uint32 num)
+{
+   return ((uint32) 1) << pg_leftmost_one_pos32(num);
+}
+
+/*
+ * pg_prevpower2_64
+ *     Returns the next lower power of 2 below 'num', or 'num' if it's
+ *     already a power of 2.
+ *
+ * 'num' mustn't be 0.
+ */
+static inline uint64
+pg_prevpower2_64(uint64 num)
+{
+   return ((uint64) 1) << pg_leftmost_one_pos64(num);
+}
+
+/*
+ * pg_prevpower2_size_t
+ *     Returns the next lower power of 2 below 'num', for a size_t input.
+ */
+#if SIZEOF_SIZE_T == 4
+#define pg_prevpower2_size_t(num) pg_prevpower2_32(num)
+#else
+#define pg_prevpower2_size_t(num) pg_prevpower2_64(num)
+#endif
+
 /*
  * pg_ceil_log2_32
  *     Returns equivalent of ceil(log2(num))