@@ -4650,6 +4650,7 @@ array_insert_slice(ArrayType *destArray,
46504650 *
46514651 * element_type is the array element type (must be a valid array element type)
46524652 * rcontext is where to keep working state
4653+ * subcontext is a flag determining whether to use a separate memory context
46534654 *
46544655 * Note: there are two common schemes for using accumArrayResult().
46554656 * In the older scheme, you start with a NULL ArrayBuildState pointer, and
@@ -4659,24 +4660,39 @@ array_insert_slice(ArrayType *destArray,
46594660 * once per element. In this scheme you always end with a non-NULL pointer
46604661 * that you can pass to makeArrayResult; you get an empty array if there
46614662 * were no elements. This is preferred if an empty array is what you want.
4663+ *
4664+ * It's possible to choose whether to create a separate memory context for the
4665+ * array build state, or whether to allocate it directly within rcontext.
4666+ *
4667+ * When there are many concurrent small states (e.g. array_agg() using hash
4668+ * aggregation of many small groups), using a separate memory context for each
4669+ * one may result in severe memory bloat. In such cases, use the same memory
4670+ * context to initialize all such array build states, and pass
4671+ * subcontext=false.
4672+ *
4673+ * In cases when the array build states have different lifetimes, using a
4674+ * single memory context is impractical. Instead, pass subcontext=true so that
4675+ * the array build states can be freed individually.
46624676 */
46634677ArrayBuildState *
4664- initArrayResult (Oid element_type , MemoryContext rcontext )
4678+ initArrayResult (Oid element_type , MemoryContext rcontext , bool subcontext )
46654679{
46664680 ArrayBuildState * astate ;
4667- MemoryContext arr_context ;
4681+ MemoryContext arr_context = rcontext ;
46684682
46694683 /* Make a temporary context to hold all the junk */
4670- arr_context = AllocSetContextCreate (rcontext ,
4671- "accumArrayResult" ,
4672- ALLOCSET_DEFAULT_MINSIZE ,
4673- ALLOCSET_DEFAULT_INITSIZE ,
4674- ALLOCSET_DEFAULT_MAXSIZE );
4684+ if (subcontext )
4685+ arr_context = AllocSetContextCreate (rcontext ,
4686+ "accumArrayResult" ,
4687+ ALLOCSET_DEFAULT_MINSIZE ,
4688+ ALLOCSET_DEFAULT_INITSIZE ,
4689+ ALLOCSET_DEFAULT_MAXSIZE );
46754690
46764691 astate = (ArrayBuildState * )
46774692 MemoryContextAlloc (arr_context , sizeof (ArrayBuildState ));
46784693 astate -> mcontext = arr_context ;
4679- astate -> alen = 64 ; /* arbitrary starting array size */
4694+ astate -> private_cxt = subcontext ;
4695+ astate -> alen = (subcontext ? 64 : 8 ); /* arbitrary starting array size */
46804696 astate -> dvalues = (Datum * )
46814697 MemoryContextAlloc (arr_context , astate -> alen * sizeof (Datum ));
46824698 astate -> dnulls = (bool * )
@@ -4710,7 +4726,7 @@ accumArrayResult(ArrayBuildState *astate,
47104726 if (astate == NULL )
47114727 {
47124728 /* First time through --- initialize */
4713- astate = initArrayResult (element_type , rcontext );
4729+ astate = initArrayResult (element_type , rcontext , true );
47144730 }
47154731 else
47164732 {
@@ -4757,6 +4773,9 @@ accumArrayResult(ArrayBuildState *astate,
47574773/*
47584774 * makeArrayResult - produce 1-D final result of accumArrayResult
47594775 *
4776+ * Note: only releases astate if it was initialized within a separate memory
4777+ * context (i.e. using subcontext=true when calling initArrayResult).
4778+ *
47604779 * astate is working state (must not be NULL)
47614780 * rcontext is where to construct result
47624781 */
@@ -4773,7 +4792,8 @@ makeArrayResult(ArrayBuildState *astate,
47734792 dims [0 ] = astate -> nelems ;
47744793 lbs [0 ] = 1 ;
47754794
4776- return makeMdArrayResult (astate , ndims , dims , lbs , rcontext , true);
4795+ return makeMdArrayResult (astate , ndims , dims , lbs , rcontext ,
4796+ astate -> private_cxt );
47774797}
47784798
47794799/*
@@ -4782,6 +4802,11 @@ makeArrayResult(ArrayBuildState *astate,
47824802 * beware: no check that specified dimensions match the number of values
47834803 * accumulated.
47844804 *
4805+ * Note: if the astate was not initialized within a separate memory context
4806+ * (that is, initArrayResult was called with subcontext=false), then using
4807+ * release=true is illegal. Instead, release astate along with the rest of its
4808+ * context when appropriate.
4809+ *
47854810 * astate is working state (must not be NULL)
47864811 * rcontext is where to construct result
47874812 * release is true if okay to release working state
@@ -4814,7 +4839,10 @@ makeMdArrayResult(ArrayBuildState *astate,
48144839
48154840 /* Clean up all the junk */
48164841 if (release )
4842+ {
4843+ Assert (astate -> private_cxt );
48174844 MemoryContextDelete (astate -> mcontext );
4845+ }
48184846
48194847 return PointerGetDatum (result );
48204848}
@@ -4831,26 +4859,42 @@ makeMdArrayResult(ArrayBuildState *astate,
48314859 * initArrayResultArr - initialize an empty ArrayBuildStateArr
48324860 *
48334861 * array_type is the array type (must be a valid varlena array type)
4834- * element_type is the type of the array's elements
4862+ * element_type is the type of the array's elements (lookup if InvalidOid)
48354863 * rcontext is where to keep working state
4864+ * subcontext is a flag determining whether to use a separate memory context
48364865 */
48374866ArrayBuildStateArr *
4838- initArrayResultArr (Oid array_type , Oid element_type , MemoryContext rcontext )
4867+ initArrayResultArr (Oid array_type , Oid element_type , MemoryContext rcontext ,
4868+ bool subcontext )
48394869{
48404870 ArrayBuildStateArr * astate ;
4841- MemoryContext arr_context ;
4871+ MemoryContext arr_context = rcontext ; /* by default use the parent ctx */
4872+
4873+ /* Lookup element type, unless element_type already provided */
4874+ if (! OidIsValid (element_type ))
4875+ {
4876+ element_type = get_element_type (array_type );
4877+
4878+ if (!OidIsValid (element_type ))
4879+ ereport (ERROR ,
4880+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
4881+ errmsg ("data type %s is not an array type" ,
4882+ format_type_be (array_type ))));
4883+ }
48424884
48434885 /* Make a temporary context to hold all the junk */
4844- arr_context = AllocSetContextCreate (rcontext ,
4845- "accumArrayResultArr" ,
4846- ALLOCSET_DEFAULT_MINSIZE ,
4847- ALLOCSET_DEFAULT_INITSIZE ,
4848- ALLOCSET_DEFAULT_MAXSIZE );
4886+ if (subcontext )
4887+ arr_context = AllocSetContextCreate (rcontext ,
4888+ "accumArrayResultArr" ,
4889+ ALLOCSET_DEFAULT_MINSIZE ,
4890+ ALLOCSET_DEFAULT_INITSIZE ,
4891+ ALLOCSET_DEFAULT_MAXSIZE );
48494892
48504893 /* Note we initialize all fields to zero */
48514894 astate = (ArrayBuildStateArr * )
48524895 MemoryContextAllocZero (arr_context , sizeof (ArrayBuildStateArr ));
48534896 astate -> mcontext = arr_context ;
4897+ astate -> private_cxt = subcontext ;
48544898
48554899 /* Save relevant datatype information */
48564900 astate -> array_type = array_type ;
@@ -4897,21 +4941,9 @@ accumArrayResultArr(ArrayBuildStateArr *astate,
48974941 arg = DatumGetArrayTypeP (dvalue );
48984942
48994943 if (astate == NULL )
4900- {
4901- /* First time through --- initialize */
4902- Oid element_type = get_element_type (array_type );
4903-
4904- if (!OidIsValid (element_type ))
4905- ereport (ERROR ,
4906- (errcode (ERRCODE_DATATYPE_MISMATCH ),
4907- errmsg ("data type %s is not an array type" ,
4908- format_type_be (array_type ))));
4909- astate = initArrayResultArr (array_type , element_type , rcontext );
4910- }
4944+ astate = initArrayResultArr (array_type , InvalidOid , rcontext , true);
49114945 else
4912- {
49134946 Assert (astate -> array_type == array_type );
4914- }
49154947
49164948 oldcontext = MemoryContextSwitchTo (astate -> mcontext );
49174949
@@ -5090,7 +5122,10 @@ makeArrayResultArr(ArrayBuildStateArr *astate,
50905122
50915123 /* Clean up all the junk */
50925124 if (release )
5125+ {
5126+ Assert (astate -> private_cxt );
50935127 MemoryContextDelete (astate -> mcontext );
5128+ }
50945129
50955130 return PointerGetDatum (result );
50965131}
@@ -5106,9 +5141,10 @@ makeArrayResultArr(ArrayBuildStateArr *astate,
51065141 *
51075142 * input_type is the input datatype (either element or array type)
51085143 * rcontext is where to keep working state
5144+ * subcontext is a flag determining whether to use a separate memory context
51095145 */
51105146ArrayBuildStateAny *
5111- initArrayResultAny (Oid input_type , MemoryContext rcontext )
5147+ initArrayResultAny (Oid input_type , MemoryContext rcontext , bool subcontext )
51125148{
51135149 ArrayBuildStateAny * astate ;
51145150 Oid element_type = get_element_type (input_type );
@@ -5118,7 +5154,7 @@ initArrayResultAny(Oid input_type, MemoryContext rcontext)
51185154 /* Array case */
51195155 ArrayBuildStateArr * arraystate ;
51205156
5121- arraystate = initArrayResultArr (input_type , element_type , rcontext );
5157+ arraystate = initArrayResultArr (input_type , InvalidOid , rcontext , subcontext );
51225158 astate = (ArrayBuildStateAny * )
51235159 MemoryContextAlloc (arraystate -> mcontext ,
51245160 sizeof (ArrayBuildStateAny ));
@@ -5133,7 +5169,7 @@ initArrayResultAny(Oid input_type, MemoryContext rcontext)
51335169 /* Let's just check that we have a type that can be put into arrays */
51345170 Assert (OidIsValid (get_array_type (input_type )));
51355171
5136- scalarstate = initArrayResult (input_type , rcontext );
5172+ scalarstate = initArrayResult (input_type , rcontext , subcontext );
51375173 astate = (ArrayBuildStateAny * )
51385174 MemoryContextAlloc (scalarstate -> mcontext ,
51395175 sizeof (ArrayBuildStateAny ));
@@ -5159,7 +5195,7 @@ accumArrayResultAny(ArrayBuildStateAny *astate,
51595195 MemoryContext rcontext )
51605196{
51615197 if (astate == NULL )
5162- astate = initArrayResultAny (input_type , rcontext );
5198+ astate = initArrayResultAny (input_type , rcontext , true );
51635199
51645200 if (astate -> scalarstate )
51655201 (void ) accumArrayResult (astate -> scalarstate ,
0 commit comments