Commit 50a38f65 authored by Jeff Davis's avatar Jeff Davis

Create memory context for HashAgg with a reasonable maxBlockSize.

If the memory context's maxBlockSize is too big, a single block
allocation can suddenly exceed work_mem. For Hash Aggregation, this
can mean spilling to disk too early or reporting a confusing memory
usage number for EXPLAN ANALYZE.

Introduce CreateWorkExprContext(), which is like CreateExprContext(),
except that it creates the AllocSet with a maxBlockSize that is
reasonable in proportion to work_mem.

Right now, CreateWorkExprContext() is only used by Hash Aggregation,
but it may be generally useful in the future.

Discussion: https://postgr.es/m/412a3fbf306f84d8d78c4009e11791867e62b87c.camel@j-davis.com
parent f0705bb6
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include "executor/executor.h" #include "executor/executor.h"
#include "jit/jit.h" #include "jit/jit.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "partitioning/partdesc.h" #include "partitioning/partdesc.h"
...@@ -227,21 +228,13 @@ FreeExecutorState(EState *estate) ...@@ -227,21 +228,13 @@ FreeExecutorState(EState *estate)
MemoryContextDelete(estate->es_query_cxt); MemoryContextDelete(estate->es_query_cxt);
} }
/* ---------------- /*
* CreateExprContext * Internal implementation for CreateExprContext() and CreateWorkExprContext()
* * that allows control over the AllocSet parameters.
* Create a context for expression evaluation within an EState.
*
* An executor run may require multiple ExprContexts (we usually make one
* for each Plan node, and a separate one for per-output-tuple processing
* such as constraint checking). Each ExprContext has its own "per-tuple"
* memory context.
*
* Note we make no assumption about the caller's memory context.
* ----------------
*/ */
ExprContext * static ExprContext *
CreateExprContext(EState *estate) CreateExprContextInternal(EState *estate, Size minContextSize,
Size initBlockSize, Size maxBlockSize)
{ {
ExprContext *econtext; ExprContext *econtext;
MemoryContext oldcontext; MemoryContext oldcontext;
...@@ -264,7 +257,9 @@ CreateExprContext(EState *estate) ...@@ -264,7 +257,9 @@ CreateExprContext(EState *estate)
econtext->ecxt_per_tuple_memory = econtext->ecxt_per_tuple_memory =
AllocSetContextCreate(estate->es_query_cxt, AllocSetContextCreate(estate->es_query_cxt,
"ExprContext", "ExprContext",
ALLOCSET_DEFAULT_SIZES); minContextSize,
initBlockSize,
maxBlockSize);
econtext->ecxt_param_exec_vals = estate->es_param_exec_vals; econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
econtext->ecxt_param_list_info = estate->es_param_list_info; econtext->ecxt_param_list_info = estate->es_param_list_info;
...@@ -294,6 +289,52 @@ CreateExprContext(EState *estate) ...@@ -294,6 +289,52 @@ CreateExprContext(EState *estate)
return econtext; return econtext;
} }
/* ----------------
* CreateExprContext
*
* Create a context for expression evaluation within an EState.
*
* An executor run may require multiple ExprContexts (we usually make one
* for each Plan node, and a separate one for per-output-tuple processing
* such as constraint checking). Each ExprContext has its own "per-tuple"
* memory context.
*
* Note we make no assumption about the caller's memory context.
* ----------------
*/
ExprContext *
CreateExprContext(EState *estate)
{
return CreateExprContextInternal(estate, ALLOCSET_DEFAULT_SIZES);
}
/* ----------------
* CreateWorkExprContext
*
* Like CreateExprContext, but specifies the AllocSet sizes to be reasonable
* in proportion to work_mem. If the maximum block allocation size is too
* large, it's easy to skip right past work_mem with a single allocation.
* ----------------
*/
ExprContext *
CreateWorkExprContext(EState *estate)
{
Size minContextSize = ALLOCSET_DEFAULT_MINSIZE;
Size initBlockSize = ALLOCSET_DEFAULT_INITSIZE;
Size maxBlockSize = ALLOCSET_DEFAULT_MAXSIZE;
/* choose the maxBlockSize to be no larger than 1/16 of work_mem */
while (16 * maxBlockSize > work_mem * 1024L)
maxBlockSize >>= 1;
if (maxBlockSize < ALLOCSET_DEFAULT_INITSIZE)
maxBlockSize = ALLOCSET_DEFAULT_INITSIZE;
return CreateExprContextInternal(estate, minContextSize,
initBlockSize, maxBlockSize);
}
/* ---------------- /* ----------------
* CreateStandaloneExprContext * CreateStandaloneExprContext
* *
......
...@@ -3277,10 +3277,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) ...@@ -3277,10 +3277,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
} }
if (use_hashing) if (use_hashing)
{ aggstate->hashcontext = CreateWorkExprContext(estate);
ExecAssignExprContext(estate, &aggstate->ss.ps);
aggstate->hashcontext = aggstate->ss.ps.ps_ExprContext;
}
ExecAssignExprContext(estate, &aggstate->ss.ps); ExecAssignExprContext(estate, &aggstate->ss.ps);
......
...@@ -493,6 +493,7 @@ extern void end_tup_output(TupOutputState *tstate); ...@@ -493,6 +493,7 @@ extern void end_tup_output(TupOutputState *tstate);
extern EState *CreateExecutorState(void); extern EState *CreateExecutorState(void);
extern void FreeExecutorState(EState *estate); extern void FreeExecutorState(EState *estate);
extern ExprContext *CreateExprContext(EState *estate); extern ExprContext *CreateExprContext(EState *estate);
extern ExprContext *CreateWorkExprContext(EState *estate);
extern ExprContext *CreateStandaloneExprContext(void); extern ExprContext *CreateStandaloneExprContext(void);
extern void FreeExprContext(ExprContext *econtext, bool isCommit); extern void FreeExprContext(ExprContext *econtext, bool isCommit);
extern void ReScanExprContext(ExprContext *econtext); extern void ReScanExprContext(ExprContext *econtext);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment