@@ -4650,6 +4650,7 @@ array_insert_slice(ArrayType *destArray,
4650
4650
*
4651
4651
* element_type is the array element type (must be a valid array element type)
4652
4652
* rcontext is where to keep working state
4653
+ * subcontext is a flag determining whether to use a separate memory context
4653
4654
*
4654
4655
* Note: there are two common schemes for using accumArrayResult().
4655
4656
* In the older scheme, you start with a NULL ArrayBuildState pointer, and
@@ -4659,24 +4660,39 @@ array_insert_slice(ArrayType *destArray,
4659
4660
* once per element. In this scheme you always end with a non-NULL pointer
4660
4661
* that you can pass to makeArrayResult; you get an empty array if there
4661
4662
* 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.
4662
4676
*/
4663
4677
ArrayBuildState *
4664
- initArrayResult (Oid element_type , MemoryContext rcontext )
4678
+ initArrayResult (Oid element_type , MemoryContext rcontext , bool subcontext )
4665
4679
{
4666
4680
ArrayBuildState * astate ;
4667
- MemoryContext arr_context ;
4681
+ MemoryContext arr_context = rcontext ;
4668
4682
4669
4683
/* 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 );
4675
4690
4676
4691
astate = (ArrayBuildState * )
4677
4692
MemoryContextAlloc (arr_context , sizeof (ArrayBuildState ));
4678
4693
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 */
4680
4696
astate -> dvalues = (Datum * )
4681
4697
MemoryContextAlloc (arr_context , astate -> alen * sizeof (Datum ));
4682
4698
astate -> dnulls = (bool * )
@@ -4710,7 +4726,7 @@ accumArrayResult(ArrayBuildState *astate,
4710
4726
if (astate == NULL )
4711
4727
{
4712
4728
/* First time through --- initialize */
4713
- astate = initArrayResult (element_type , rcontext );
4729
+ astate = initArrayResult (element_type , rcontext , true );
4714
4730
}
4715
4731
else
4716
4732
{
@@ -4757,6 +4773,9 @@ accumArrayResult(ArrayBuildState *astate,
4757
4773
/*
4758
4774
* makeArrayResult - produce 1-D final result of accumArrayResult
4759
4775
*
4776
+ * Note: only releases astate if it was initialized within a separate memory
4777
+ * context (i.e. using subcontext=true when calling initArrayResult).
4778
+ *
4760
4779
* astate is working state (must not be NULL)
4761
4780
* rcontext is where to construct result
4762
4781
*/
@@ -4773,7 +4792,8 @@ makeArrayResult(ArrayBuildState *astate,
4773
4792
dims [0 ] = astate -> nelems ;
4774
4793
lbs [0 ] = 1 ;
4775
4794
4776
- return makeMdArrayResult (astate , ndims , dims , lbs , rcontext , true);
4795
+ return makeMdArrayResult (astate , ndims , dims , lbs , rcontext ,
4796
+ astate -> private_cxt );
4777
4797
}
4778
4798
4779
4799
/*
@@ -4782,6 +4802,11 @@ makeArrayResult(ArrayBuildState *astate,
4782
4802
* beware: no check that specified dimensions match the number of values
4783
4803
* accumulated.
4784
4804
*
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
+ *
4785
4810
* astate is working state (must not be NULL)
4786
4811
* rcontext is where to construct result
4787
4812
* release is true if okay to release working state
@@ -4814,7 +4839,10 @@ makeMdArrayResult(ArrayBuildState *astate,
4814
4839
4815
4840
/* Clean up all the junk */
4816
4841
if (release )
4842
+ {
4843
+ Assert (astate -> private_cxt );
4817
4844
MemoryContextDelete (astate -> mcontext );
4845
+ }
4818
4846
4819
4847
return PointerGetDatum (result );
4820
4848
}
@@ -4831,26 +4859,42 @@ makeMdArrayResult(ArrayBuildState *astate,
4831
4859
* initArrayResultArr - initialize an empty ArrayBuildStateArr
4832
4860
*
4833
4861
* 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)
4835
4863
* rcontext is where to keep working state
4864
+ * subcontext is a flag determining whether to use a separate memory context
4836
4865
*/
4837
4866
ArrayBuildStateArr *
4838
- initArrayResultArr (Oid array_type , Oid element_type , MemoryContext rcontext )
4867
+ initArrayResultArr (Oid array_type , Oid element_type , MemoryContext rcontext ,
4868
+ bool subcontext )
4839
4869
{
4840
4870
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
+ }
4842
4884
4843
4885
/* 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 );
4849
4892
4850
4893
/* Note we initialize all fields to zero */
4851
4894
astate = (ArrayBuildStateArr * )
4852
4895
MemoryContextAllocZero (arr_context , sizeof (ArrayBuildStateArr ));
4853
4896
astate -> mcontext = arr_context ;
4897
+ astate -> private_cxt = subcontext ;
4854
4898
4855
4899
/* Save relevant datatype information */
4856
4900
astate -> array_type = array_type ;
@@ -4897,21 +4941,9 @@ accumArrayResultArr(ArrayBuildStateArr *astate,
4897
4941
arg = DatumGetArrayTypeP (dvalue );
4898
4942
4899
4943
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);
4911
4945
else
4912
- {
4913
4946
Assert (astate -> array_type == array_type );
4914
- }
4915
4947
4916
4948
oldcontext = MemoryContextSwitchTo (astate -> mcontext );
4917
4949
@@ -5090,7 +5122,10 @@ makeArrayResultArr(ArrayBuildStateArr *astate,
5090
5122
5091
5123
/* Clean up all the junk */
5092
5124
if (release )
5125
+ {
5126
+ Assert (astate -> private_cxt );
5093
5127
MemoryContextDelete (astate -> mcontext );
5128
+ }
5094
5129
5095
5130
return PointerGetDatum (result );
5096
5131
}
@@ -5106,9 +5141,10 @@ makeArrayResultArr(ArrayBuildStateArr *astate,
5106
5141
*
5107
5142
* input_type is the input datatype (either element or array type)
5108
5143
* rcontext is where to keep working state
5144
+ * subcontext is a flag determining whether to use a separate memory context
5109
5145
*/
5110
5146
ArrayBuildStateAny *
5111
- initArrayResultAny (Oid input_type , MemoryContext rcontext )
5147
+ initArrayResultAny (Oid input_type , MemoryContext rcontext , bool subcontext )
5112
5148
{
5113
5149
ArrayBuildStateAny * astate ;
5114
5150
Oid element_type = get_element_type (input_type );
@@ -5118,7 +5154,7 @@ initArrayResultAny(Oid input_type, MemoryContext rcontext)
5118
5154
/* Array case */
5119
5155
ArrayBuildStateArr * arraystate ;
5120
5156
5121
- arraystate = initArrayResultArr (input_type , element_type , rcontext );
5157
+ arraystate = initArrayResultArr (input_type , InvalidOid , rcontext , subcontext );
5122
5158
astate = (ArrayBuildStateAny * )
5123
5159
MemoryContextAlloc (arraystate -> mcontext ,
5124
5160
sizeof (ArrayBuildStateAny ));
@@ -5133,7 +5169,7 @@ initArrayResultAny(Oid input_type, MemoryContext rcontext)
5133
5169
/* Let's just check that we have a type that can be put into arrays */
5134
5170
Assert (OidIsValid (get_array_type (input_type )));
5135
5171
5136
- scalarstate = initArrayResult (input_type , rcontext );
5172
+ scalarstate = initArrayResult (input_type , rcontext , subcontext );
5137
5173
astate = (ArrayBuildStateAny * )
5138
5174
MemoryContextAlloc (scalarstate -> mcontext ,
5139
5175
sizeof (ArrayBuildStateAny ));
@@ -5159,7 +5195,7 @@ accumArrayResultAny(ArrayBuildStateAny *astate,
5159
5195
MemoryContext rcontext )
5160
5196
{
5161
5197
if (astate == NULL )
5162
- astate = initArrayResultAny (input_type , rcontext );
5198
+ astate = initArrayResultAny (input_type , rcontext , true );
5163
5199
5164
5200
if (astate -> scalarstate )
5165
5201
(void ) accumArrayResult (astate -> scalarstate ,
0 commit comments