4040 * create just once per query because they will not change across groups.
4141 * The per-query struct and subsidiary data live in the executor's per-query
4242 * memory context, and go away implicitly at ExecutorEnd().
43+ *
44+ * These structs are set up during the first call of the transition function.
45+ * Because we allow nodeAgg.c to merge ordered-set aggregates (but not
46+ * hypothetical aggregates) with identical inputs and transition functions,
47+ * this info must not depend on the particular aggregate (ie, particular
48+ * final-function), nor on the direct argument(s) of the aggregate.
4349 */
4450
4551typedef struct OSAPerQueryState
4652{
47- /* Aggref for this aggregate: */
53+ /*Representative Aggref for this aggregate: */
4854Aggref * aggref ;
4955/* Memory context containing this struct and other per-query data: */
5056MemoryContext qcontext ;
57+ /* Do we expect multiple final-function calls within one group? */
58+ bool rescan_needed ;
5159
5260/* These fields are used only when accumulating tuples: */
5361
@@ -91,6 +99,8 @@ typedef struct OSAPerGroupState
9199Tuplesortstate * sortstate ;
92100/* Number of normal rows inserted into sortstate: */
93101int64 number_of_rows ;
102+ /* Have we already done tuplesort_performsort? */
103+ bool sort_done ;
94104}OSAPerGroupState ;
95105
96106static void ordered_set_shutdown (Datum arg );
@@ -146,6 +156,9 @@ ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples)
146156qstate -> aggref = aggref ;
147157qstate -> qcontext = qcontext ;
148158
159+ /* We need to support rescans if the trans state is shared */
160+ qstate -> rescan_needed = AggStateIsShared (fcinfo );
161+
149162/* Extract the sort information */
150163sortlist = aggref -> aggorder ;
151164numSortCols = list_length (sortlist );
@@ -277,15 +290,18 @@ ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples)
277290qstate -> sortOperators ,
278291qstate -> sortCollations ,
279292qstate -> sortNullsFirsts ,
280- work_mem , false);
293+ work_mem ,
294+ qstate -> rescan_needed );
281295else
282296osastate -> sortstate = tuplesort_begin_datum (qstate -> sortColType ,
283297qstate -> sortOperator ,
284298qstate -> sortCollation ,
285299qstate -> sortNullsFirst ,
286- work_mem , false);
300+ work_mem ,
301+ qstate -> rescan_needed );
287302
288303osastate -> number_of_rows = 0 ;
304+ osastate -> sort_done = false;
289305
290306/* Now register a shutdown callback to clean things up at end of group */
291307AggRegisterCallback (fcinfo ,
@@ -306,14 +322,12 @@ ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples)
306322 * group) by ExecutorEnd. But we must take care to release any potential
307323 * non-memory resources.
308324 *
309- * This callback is arguably unnecessary, since we don't support use of
310- * ordered-set aggs in AGG_HASHED mode and there is currently no non-error
311- * code path in non-hashed modes wherein nodeAgg.c won't call the finalfn
312- * after calling the transfn one or more times. So in principle we could rely
313- * on the finalfn to delete the tuplestore etc. However, it's possible that
314- * such a code path might exist in future, and in any case it'd be
315- * notationally tedious and sometimes require extra data copying to ensure
316- * we always delete the tuplestore in the finalfn.
325+ * In the case where we're not expecting multiple finalfn calls, we could
326+ * arguably rely on the finalfn to clean up; but it's easier and more testable
327+ * if we just do it the same way in either case. Note that many of the
328+ * finalfns could *not* free the tuplesort object, at least not without extra
329+ * data copying, because what they return is a pointer to a datum inside the
330+ * tuplesort object.
317331 */
318332static void
319333ordered_set_shutdown (Datum arg )
@@ -436,8 +450,14 @@ percentile_disc_final(PG_FUNCTION_ARGS)
436450if (osastate -> number_of_rows == 0 )
437451PG_RETURN_NULL ();
438452
439- /* Finish the sort */
440- tuplesort_performsort (osastate -> sortstate );
453+ /* Finish the sort, or rescan if we already did */
454+ if (!osastate -> sort_done )
455+ {
456+ tuplesort_performsort (osastate -> sortstate );
457+ osastate -> sort_done = true;
458+ }
459+ else
460+ tuplesort_rescan (osastate -> sortstate );
441461
442462/*----------
443463 * We need the smallest K such that (K/N) >= percentile.
@@ -457,13 +477,6 @@ percentile_disc_final(PG_FUNCTION_ARGS)
457477if (!tuplesort_getdatum (osastate -> sortstate , true,& val ,& isnull ,NULL ))
458478elog (ERROR ,"missing row in percentile_disc" );
459479
460- /*
461- * Note: we *cannot* clean up the tuplesort object here, because the value
462- * to be returned is allocated inside its sortcontext. We could use
463- * datumCopy to copy it out of there, but it doesn't seem worth the
464- * trouble, since the cleanup callback will clear the tuplesort later.
465- */
466-
467480/* We shouldn't have stored any nulls, but do the right thing anyway */
468481if (isnull )
469482PG_RETURN_NULL ();
@@ -543,8 +556,14 @@ percentile_cont_final_common(FunctionCallInfo fcinfo,
543556
544557Assert (expect_type == osastate -> qstate -> sortColType );
545558
546- /* Finish the sort */
547- tuplesort_performsort (osastate -> sortstate );
559+ /* Finish the sort, or rescan if we already did */
560+ if (!osastate -> sort_done )
561+ {
562+ tuplesort_performsort (osastate -> sortstate );
563+ osastate -> sort_done = true;
564+ }
565+ else
566+ tuplesort_rescan (osastate -> sortstate );
548567
549568first_row = floor (percentile * (osastate -> number_of_rows - 1 ));
550569second_row = ceil (percentile * (osastate -> number_of_rows - 1 ));
@@ -575,13 +594,6 @@ percentile_cont_final_common(FunctionCallInfo fcinfo,
575594val = lerpfunc (first_val ,second_val ,proportion );
576595}
577596
578- /*
579- * Note: we *cannot* clean up the tuplesort object here, because the value
580- * to be returned may be allocated inside its sortcontext. We could use
581- * datumCopy to copy it out of there, but it doesn't seem worth the
582- * trouble, since the cleanup callback will clear the tuplesort later.
583- */
584-
585597PG_RETURN_DATUM (val );
586598}
587599
@@ -779,8 +791,14 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS)
779791 */
780792if (i < num_percentiles )
781793{
782- /* Finish the sort */
783- tuplesort_performsort (osastate -> sortstate );
794+ /* Finish the sort, or rescan if we already did */
795+ if (!osastate -> sort_done )
796+ {
797+ tuplesort_performsort (osastate -> sortstate );
798+ osastate -> sort_done = true;
799+ }
800+ else
801+ tuplesort_rescan (osastate -> sortstate );
784802
785803for (;i < num_percentiles ;i ++ )
786804{
@@ -804,11 +822,6 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS)
804822}
805823}
806824
807- /*
808- * We could clean up the tuplesort object after forming the array, but
809- * probably not worth the trouble.
810- */
811-
812825/* We make the output array the same shape as the input */
813826PG_RETURN_POINTER (construct_md_array (result_datum ,result_isnull ,
814827ARR_NDIM (param ),
@@ -902,8 +915,14 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
902915 */
903916if (i < num_percentiles )
904917{
905- /* Finish the sort */
906- tuplesort_performsort (osastate -> sortstate );
918+ /* Finish the sort, or rescan if we already did */
919+ if (!osastate -> sort_done )
920+ {
921+ tuplesort_performsort (osastate -> sortstate );
922+ osastate -> sort_done = true;
923+ }
924+ else
925+ tuplesort_rescan (osastate -> sortstate );
907926
908927for (;i < num_percentiles ;i ++ )
909928{
@@ -962,11 +981,6 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
962981}
963982}
964983
965- /*
966- * We could clean up the tuplesort object after forming the array, but
967- * probably not worth the trouble.
968- */
969-
970984/* We make the output array the same shape as the input */
971985PG_RETURN_POINTER (construct_md_array (result_datum ,result_isnull ,
972986ARR_NDIM (param ),
@@ -1043,8 +1057,14 @@ mode_final(PG_FUNCTION_ARGS)
10431057
10441058shouldfree = !(osastate -> qstate -> typByVal );
10451059
1046- /* Finish the sort */
1047- tuplesort_performsort (osastate -> sortstate );
1060+ /* Finish the sort, or rescan if we already did */
1061+ if (!osastate -> sort_done )
1062+ {
1063+ tuplesort_performsort (osastate -> sortstate );
1064+ osastate -> sort_done = true;
1065+ }
1066+ else
1067+ tuplesort_rescan (osastate -> sortstate );
10481068
10491069/* Scan tuples and count frequencies */
10501070while (tuplesort_getdatum (osastate -> sortstate , true,& val ,& isnull ,& abbrev_val ))
@@ -1097,13 +1117,6 @@ mode_final(PG_FUNCTION_ARGS)
10971117if (shouldfree && !last_val_is_mode )
10981118pfree (DatumGetPointer (last_val ));
10991119
1100- /*
1101- * Note: we *cannot* clean up the tuplesort object here, because the value
1102- * to be returned is allocated inside its sortcontext. We could use
1103- * datumCopy to copy it out of there, but it doesn't seem worth the
1104- * trouble, since the cleanup callback will clear the tuplesort later.
1105- */
1106-
11071120if (mode_freq )
11081121PG_RETURN_DATUM (mode_val );
11091122else
@@ -1174,6 +1187,9 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
11741187
11751188hypothetical_check_argtypes (fcinfo ,nargs ,osastate -> qstate -> tupdesc );
11761189
1190+ /* because we need a hypothetical row, we can't share transition state */
1191+ Assert (!osastate -> sort_done );
1192+
11771193/* insert the hypothetical row into the sort */
11781194slot = osastate -> qstate -> tupslot ;
11791195ExecClearTuple (slot );
@@ -1190,6 +1206,7 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
11901206
11911207/* finish the sort */
11921208tuplesort_performsort (osastate -> sortstate );
1209+ osastate -> sort_done = true;
11931210
11941211/* iterate till we find the hypothetical row */
11951212while (tuplesort_gettupleslot (osastate -> sortstate , true, true,slot ,NULL ))
@@ -1207,10 +1224,6 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
12071224
12081225ExecClearTuple (slot );
12091226
1210- /* Might as well clean up the tuplesort object immediately */
1211- tuplesort_end (osastate -> sortstate );
1212- osastate -> sortstate = NULL ;
1213-
12141227return rank ;
12151228}
12161229
@@ -1329,6 +1342,9 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
13291342/* Get short-term context we can use for execTuplesMatch */
13301343tmpcontext = AggGetTempMemoryContext (fcinfo );
13311344
1345+ /* because we need a hypothetical row, we can't share transition state */
1346+ Assert (!osastate -> sort_done );
1347+
13321348/* insert the hypothetical row into the sort */
13331349slot = osastate -> qstate -> tupslot ;
13341350ExecClearTuple (slot );
@@ -1345,6 +1361,7 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
13451361
13461362/* finish the sort */
13471363tuplesort_performsort (osastate -> sortstate );
1364+ osastate -> sort_done = true;
13481365
13491366/*
13501367 * We alternate fetching into tupslot and extraslot so that we have the
@@ -1391,10 +1408,6 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
13911408
13921409ExecDropSingleTupleTableSlot (extraslot );
13931410
1394- /* Might as well clean up the tuplesort object immediately */
1395- tuplesort_end (osastate -> sortstate );
1396- osastate -> sortstate = NULL ;
1397-
13981411rank = rank - duplicate_count ;
13991412
14001413PG_RETURN_INT64 (rank );