v2-0001-hash-join-dsa-overflow.patch
text/plain
Filename: v2-0001-hash-join-dsa-overflow.patch
Type: text/plain
Part: 0
Message:
Re: DSA overflow in hash join
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 8d2201ab67f..5d312c07159 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -35,6 +35,7 @@
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
#include "miscadmin.h"
+#include "optimizer/cost.h"
#include "port/pg_bitutils.h"
#include "utils/dynahash.h"
#include "utils/lsyscache.h"
@@ -667,7 +668,9 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
double inner_rel_bytes;
size_t hash_table_bytes;
size_t bucket_bytes;
+ size_t max_alloc_pointers;
size_t max_pointers;
+ size_t max_batches;
int nbatch = 1;
int nbuckets;
double dbuckets;
@@ -771,10 +774,22 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
* ExecHashGetBucketAndBatch fast.
*/
max_pointers = hash_table_bytes / sizeof(HashJoinTuple);
- max_pointers = Min(max_pointers, MaxAllocSize / sizeof(HashJoinTuple));
+ max_alloc_pointers = MaxAllocSize / sizeof(HashJoinTuple);
+ max_pointers = Min(max_pointers, macx_alloca_pointers);
/* If max_pointers isn't a power of 2, must round it down to one */
max_pointers = pg_prevpower2_size_t(max_pointers);
+ /*
+ * Prevent DSA overflow. This is expanded definition of EstimateParallelHashJoinBatch function
+ * used in ExecParallelHashJoinSetUpBatches:
+ * dsa_allocate0(hashtable->area,
+ * EstimateParallelHashJoinBatch(hashtable) * nbatch)
+ */
+ max_batches = MaxAllocSize / (MAXALIGN(sizeof(ParallelHashJoinBatch)) +
+ MAXALIGN(sts_estimate(max_parallel_workers_per_gather + 1) * 2));
+ max_batches = Min(max_batches, MaxAllocSize / sizeof(ParallelHashJoinBatchAccessor));
+ max_batches = pg_prevpower2_size_t(max_batches);
+
/* 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 + 1);
@@ -844,6 +859,7 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
/* Calculate required number of batches. */
dbatch = ceil(inner_rel_bytes / (hash_table_bytes - bucket_bytes));
dbatch = Min(dbatch, max_pointers);
+ dbatch = Min(dbatch, max_batches);
minbatch = (int) dbatch;
nbatch = pg_nextpower2_32(Max(2, minbatch));
}
@@ -910,7 +926,8 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
* this during the initial sizing - once we start building the hash,
* nbucket is fixed.
*/
- while (nbatch > 0)
+ while (nbatch > 0 &&
+ nbuckets * 2 <= max_alloc_pointers) /* prevent allocation limit overflow */
{
/* how much memory are we using with current nbatch value */
size_t current_space = hash_table_bytes + (2 * nbatch * BLCKSZ);