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: */
4854 Aggref * aggref ;
4955 /* Memory context containing this struct and other per-query data: */
5056 MemoryContext 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
9199 Tuplesortstate * sortstate ;
92100 /* Number of normal rows inserted into sortstate: */
93101 int64 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)
146156 qstate -> aggref = aggref ;
147157 qstate -> 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 */
150163 sortlist = aggref -> aggorder ;
151164 numSortCols = list_length (sortlist );
@@ -277,15 +290,18 @@ ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples)
277290 qstate -> sortOperators ,
278291 qstate -> sortCollations ,
279292 qstate -> sortNullsFirsts ,
280- work_mem , false);
293+ work_mem ,
294+ qstate -> rescan_needed );
281295 else
282296 osastate -> sortstate = tuplesort_begin_datum (qstate -> sortColType ,
283297 qstate -> sortOperator ,
284298 qstate -> sortCollation ,
285299 qstate -> sortNullsFirst ,
286- work_mem , false);
300+ work_mem ,
301+ qstate -> rescan_needed );
287302
288303 osastate -> number_of_rows = 0 ;
304+ osastate -> sort_done = false;
289305
290306 /* Now register a shutdown callback to clean things up at end of group */
291307 AggRegisterCallback (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)
436450 if (osastate -> number_of_rows == 0 )
437451 PG_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)
457477 if (!tuplesort_getdatum (osastate -> sortstate , true, & val , & isnull , NULL ))
458478 elog (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 */
468481 if (isnull )
469482 PG_RETURN_NULL ();
@@ -543,8 +556,14 @@ percentile_cont_final_common(FunctionCallInfo fcinfo,
543556
544557 Assert (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
549568 first_row = floor (percentile * (osastate -> number_of_rows - 1 ));
550569 second_row = ceil (percentile * (osastate -> number_of_rows - 1 ));
@@ -575,13 +594,6 @@ percentile_cont_final_common(FunctionCallInfo fcinfo,
575594 val = 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-
585597 PG_RETURN_DATUM (val );
586598}
587599
@@ -779,8 +791,14 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS)
779791 */
780792 if (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
785803 for (; 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 */
813826 PG_RETURN_POINTER (construct_md_array (result_datum , result_isnull ,
814827 ARR_NDIM (param ),
@@ -902,8 +915,14 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
902915 */
903916 if (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
908927 for (; 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 */
971985 PG_RETURN_POINTER (construct_md_array (result_datum , result_isnull ,
972986 ARR_NDIM (param ),
@@ -1043,8 +1057,14 @@ mode_final(PG_FUNCTION_ARGS)
10431057
10441058 shouldfree = !(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 */
10501070 while (tuplesort_getdatum (osastate -> sortstate , true, & val , & isnull , & abbrev_val ))
@@ -1097,13 +1117,6 @@ mode_final(PG_FUNCTION_ARGS)
10971117 if (shouldfree && !last_val_is_mode )
10981118 pfree (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-
11071120 if (mode_freq )
11081121 PG_RETURN_DATUM (mode_val );
11091122 else
@@ -1174,6 +1187,9 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
11741187
11751188 hypothetical_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 */
11781194 slot = osastate -> qstate -> tupslot ;
11791195 ExecClearTuple (slot );
@@ -1190,6 +1206,7 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
11901206
11911207 /* finish the sort */
11921208 tuplesort_performsort (osastate -> sortstate );
1209+ osastate -> sort_done = true;
11931210
11941211 /* iterate till we find the hypothetical row */
11951212 while (tuplesort_gettupleslot (osastate -> sortstate , true, true, slot , NULL ))
@@ -1207,10 +1224,6 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
12071224
12081225 ExecClearTuple (slot );
12091226
1210- /* Might as well clean up the tuplesort object immediately */
1211- tuplesort_end (osastate -> sortstate );
1212- osastate -> sortstate = NULL ;
1213-
12141227 return rank ;
12151228}
12161229
@@ -1329,6 +1342,9 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
13291342 /* Get short-term context we can use for execTuplesMatch */
13301343 tmpcontext = 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 */
13331349 slot = osastate -> qstate -> tupslot ;
13341350 ExecClearTuple (slot );
@@ -1345,6 +1361,7 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
13451361
13461362 /* finish the sort */
13471363 tuplesort_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
13921409 ExecDropSingleTupleTableSlot (extraslot );
13931410
1394- /* Might as well clean up the tuplesort object immediately */
1395- tuplesort_end (osastate -> sortstate );
1396- osastate -> sortstate = NULL ;
1397-
13981411 rank = rank - duplicate_count ;
13991412
14001413 PG_RETURN_INT64 (rank );
0 commit comments