summaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execGrouping.c59
-rw-r--r--src/backend/executor/nodeSetOp.c9
-rw-r--r--src/backend/executor/nodeSubplan.c53
3 files changed, 119 insertions, 2 deletions
diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c
index b4bdaa3c305..e1a3a813dd9 100644
--- a/src/backend/executor/execGrouping.c
+++ b/src/backend/executor/execGrouping.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/htup_details.h"
#include "access/parallel.h"
#include "common/hashfn.h"
#include "executor/executor.h"
@@ -303,6 +304,64 @@ ResetTupleHashTable(TupleHashTable hashtable)
}
/*
+ * Estimate the amount of space needed for a TupleHashTable with nentries
+ * entries, if the tuples have average data width tupleWidth and the caller
+ * requires additionalsize extra space per entry.
+ *
+ * Return SIZE_MAX if it'd overflow size_t.
+ *
+ * nentries is "double" because this is meant for use by the planner,
+ * which typically works with double rowcount estimates. So we'd need to
+ * clamp to integer somewhere and that might as well be here. We do expect
+ * the value not to be NaN or negative, else the result will be garbage.
+ */
+Size
+EstimateTupleHashTableSpace(double nentries,
+ Size tupleWidth,
+ Size additionalsize)
+{
+ Size sh_space;
+ double tuples_space;
+
+ /* First estimate the space needed for the simplehash table */
+ sh_space = tuplehash_estimate_space(nentries);
+
+ /* Give up if that's already too big */
+ if (sh_space >= SIZE_MAX)
+ return sh_space;
+
+ /*
+ * Compute space needed for hashed tuples with additional data. nentries
+ * must be somewhat sane, so it should be safe to compute this product.
+ *
+ * We assume that the hashed tuples will be kept in a BumpContext so that
+ * there is not additional per-tuple overhead.
+ *
+ * (Note that this is only accurate if MEMORY_CONTEXT_CHECKING is off,
+ * else bump.c will add a MemoryChunk header to each tuple. However, it
+ * seems undesirable for debug builds to make different planning choices
+ * than production builds, so we assume the production behavior always.)
+ */
+ tuples_space = nentries * (MAXALIGN(SizeofMinimalTupleHeader) +
+ MAXALIGN(tupleWidth) +
+ MAXALIGN(additionalsize));
+
+ /*
+ * Check for size_t overflow. This coding is trickier than it may appear,
+ * because on 64-bit machines SIZE_MAX cannot be represented exactly as a
+ * double. We must cast it explicitly to suppress compiler warnings about
+ * an inexact conversion, and we must trust that any double value that
+ * compares strictly less than "(double) SIZE_MAX" will cast to a
+ * representable size_t value.
+ */
+ if (sh_space + tuples_space >= (double) SIZE_MAX)
+ return SIZE_MAX;
+
+ /* We don't bother estimating size of the miscellaneous overhead data */
+ return (Size) (sh_space + tuples_space);
+}
+
+/*
* Find or create a hashtable entry for the tuple group containing the
* given tuple. The tuple must be the same type as the hashtable entries.
*
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 7b223a7ca3a..5aabed18a09 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -111,6 +111,15 @@ build_hash_table(SetOpState *setopstate)
false);
}
+/* Planner support routine to estimate space needed for hash table */
+Size
+EstimateSetOpHashTableSpace(double nentries, Size tupleWidth)
+{
+ return EstimateTupleHashTableSpace(nentries,
+ tupleWidth,
+ sizeof(SetOpStatePerGroupData));
+}
+
/*
* We've completed processing a tuple group. Decide how many copies (if any)
* of its representative row to emit, and store the count into numOutput.
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 9f6e45bcb0b..1cd0988bb49 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -525,7 +525,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
node->tab_hash_funcs,
node->tab_collations,
nbuckets,
- 0,
+ 0, /* no additional data */
node->planstate->state->es_query_cxt,
node->tuplesContext,
innerecontext->ecxt_per_tuple_memory,
@@ -554,7 +554,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
node->tab_hash_funcs,
node->tab_collations,
nbuckets,
- 0,
+ 0, /* no additional data */
node->planstate->state->es_query_cxt,
node->tuplesContext,
innerecontext->ecxt_per_tuple_memory,
@@ -636,6 +636,55 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
MemoryContextSwitchTo(oldcontext);
}
+/* Planner support routine to estimate space needed for hash table(s) */
+Size
+EstimateSubplanHashTableSpace(double nentries,
+ Size tupleWidth,
+ bool unknownEqFalse)
+{
+ Size tab1space,
+ tab2space;
+
+ /* Estimate size of main hashtable */
+ tab1space = EstimateTupleHashTableSpace(nentries,
+ tupleWidth,
+ 0 /* no additional data */ );
+
+ /* Give up if that's already too big */
+ if (tab1space >= SIZE_MAX)
+ return tab1space;
+
+ /* Done if we don't need a hashnulls table */
+ if (unknownEqFalse)
+ return tab1space;
+
+ /*
+ * Adjust the rowcount estimate in the same way that buildSubPlanHash
+ * will, except that we don't bother with the special case for a single
+ * hash column. (We skip that detail because it'd be notationally painful
+ * for our caller to provide the column count, and this table has
+ * relatively little impact on the total estimate anyway.)
+ */
+ nentries /= 16;
+ if (nentries < 1)
+ nentries = 1;
+
+ /*
+ * It might be sane to also reduce the tupleWidth, but on the other hand
+ * we are not accounting for the space taken by the tuples' null bitmaps.
+ * Leave it alone for now.
+ */
+ tab2space = EstimateTupleHashTableSpace(nentries,
+ tupleWidth,
+ 0 /* no additional data */ );
+
+ /* Guard against overflow */
+ if (tab2space >= SIZE_MAX - tab1space)
+ return SIZE_MAX;
+
+ return tab1space + tab2space;
+}
+
/*
* execTuplesUnequal
* Return true if two tuples are definitely unequal in the indicated