Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbe0ebb6

Browse files
committed
Allow the built-in ordered-set aggregates to share transition state.
The built-in OSAs all share the same transition function, so they canshare transition state as long as the final functions cooperate to notdo the sort step more than once. To avoid running the tuplesort objectin randomAccess mode unnecessarily, add a bit of infrastructure tonodeAgg.c to let the aggregate functions find out whether the transitionstate is actually being shared or not.This doesn't work for the hypothetical aggregates, since those injecta hypothetical row that isn't traceable to the shared input state.So they remain marked aggfinalmodify = 'w'.Discussion:https://postgr.es/m/CAB4ELO5RZhOamuT9Xsf72ozbenDLLXZKSk07FiSVsuJNZB861A@mail.gmail.com
1 parentc3dfe0f commitbe0ebb6

File tree

7 files changed

+149
-73
lines changed

7 files changed

+149
-73
lines changed

‎src/backend/executor/nodeAgg.c

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,11 @@ typedef struct AggStatePerTransData
254254
*/
255255
Aggref*aggref;
256256

257+
/*
258+
* Is this state value actually being shared by more than one Aggref?
259+
*/
260+
boolaggshared;
261+
257262
/*
258263
* Nominal number of arguments for aggregate function. For plain aggs,
259264
* this excludes any ORDER BY expressions. For ordered-set aggs, this
@@ -3360,9 +3365,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
33603365
{
33613366
/*
33623367
* Existing compatible trans found, so just point the 'peragg' to
3363-
* the same per-trans struct.
3368+
* the same per-trans struct, and mark the trans state as shared.
33643369
*/
33653370
pertrans=&pertransstates[existing_transno];
3371+
pertrans->aggshared= true;
33663372
peragg->transno=existing_transno;
33673373
}
33683374
else
@@ -3512,6 +3518,7 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
35123518

35133519
/* Begin filling in the pertrans data */
35143520
pertrans->aggref=aggref;
3521+
pertrans->aggshared= false;
35153522
pertrans->aggCollation=aggref->inputcollid;
35163523
pertrans->transfn_oid=aggtransfn;
35173524
pertrans->serialfn_oid=aggserialfn;
@@ -4161,17 +4168,18 @@ AggGetAggref(FunctionCallInfo fcinfo)
41614168
{
41624169
if (fcinfo->context&&IsA(fcinfo->context,AggState))
41634170
{
4171+
AggState*aggstate= (AggState*)fcinfo->context;
41644172
AggStatePerAggcurperagg;
41654173
AggStatePerTranscurpertrans;
41664174

41674175
/* check curperagg (valid when in a final function) */
4168-
curperagg=((AggState*)fcinfo->context)->curperagg;
4176+
curperagg=aggstate->curperagg;
41694177

41704178
if (curperagg)
41714179
returncurperagg->aggref;
41724180

41734181
/* check curpertrans (valid when in a transition function) */
4174-
curpertrans=((AggState*)fcinfo->context)->curpertrans;
4182+
curpertrans=aggstate->curpertrans;
41754183

41764184
if (curpertrans)
41774185
returncurpertrans->aggref;
@@ -4201,6 +4209,44 @@ AggGetTempMemoryContext(FunctionCallInfo fcinfo)
42014209
returnNULL;
42024210
}
42034211

4212+
/*
4213+
* AggStateIsShared - find out whether transition state is shared
4214+
*
4215+
* If the function is being called as an aggregate support function,
4216+
* return TRUE if the aggregate's transition state is shared across
4217+
* multiple aggregates, FALSE if it is not.
4218+
*
4219+
* Returns TRUE if not called as an aggregate support function.
4220+
* This is intended as a conservative answer, ie "no you'd better not
4221+
* scribble on your input". In particular, will return TRUE if the
4222+
* aggregate is being used as a window function, which is a scenario
4223+
* in which changing the transition state is a bad idea. We might
4224+
* want to refine the behavior for the window case in future.
4225+
*/
4226+
bool
4227+
AggStateIsShared(FunctionCallInfofcinfo)
4228+
{
4229+
if (fcinfo->context&&IsA(fcinfo->context,AggState))
4230+
{
4231+
AggState*aggstate= (AggState*)fcinfo->context;
4232+
AggStatePerAggcurperagg;
4233+
AggStatePerTranscurpertrans;
4234+
4235+
/* check curperagg (valid when in a final function) */
4236+
curperagg=aggstate->curperagg;
4237+
4238+
if (curperagg)
4239+
returnaggstate->pertrans[curperagg->transno].aggshared;
4240+
4241+
/* check curpertrans (valid when in a transition function) */
4242+
curpertrans=aggstate->curpertrans;
4243+
4244+
if (curpertrans)
4245+
returncurpertrans->aggshared;
4246+
}
4247+
return true;
4248+
}
4249+
42044250
/*
42054251
* AggRegisterCallback - register a cleanup callback for an aggregate
42064252
*

‎src/backend/utils/adt/orderedsetaggs.c

Lines changed: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,22 @@
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

4551
typedefstructOSAPerQueryState
4652
{
47-
/* Aggref for this aggregate: */
53+
/*RepresentativeAggref for this aggregate: */
4854
Aggref*aggref;
4955
/* Memory context containing this struct and other per-query data: */
5056
MemoryContextqcontext;
57+
/* Do we expect multiple final-function calls within one group? */
58+
boolrescan_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
int64number_of_rows;
102+
/* Have we already done tuplesort_performsort? */
103+
boolsort_done;
94104
}OSAPerGroupState;
95105

96106
staticvoidordered_set_shutdown(Datumarg);
@@ -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
*/
318332
staticvoid
319333
ordered_set_shutdown(Datumarg)
@@ -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
returnrank;
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);

‎src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/*yyyymmddN */
56-
#defineCATALOG_VERSION_NO201710141
56+
#defineCATALOG_VERSION_NO201710161
5757

5858
#endif

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp