Skip to content

Commit 8bb006a

Browse files
committed
Fix edge case leading to agg transitions skipping ExecAggTransReparent() calls.
The code checking whether an aggregate transition value needs to be reparented into the current context has always only compared the transition return value with the previous transition value by datum, i.e. without regard for NULLness. This normally works, because when the transition function returns NULL (via fcinfo->isnull), it'll return a value that won't be the same as its input value. But there's no hard requirement that that's the case. And it turns out, it's possible to hit this case (see discussion or reproducers), leading to a non-null transition value not being reparented, followed by a crash caused by that. Instead of adding another comparison of NULLness, instead have ExecAggTransReparent() ensure that pergroup->transValue ends up as 0 when the new transition value is NULL. That avoids having to add an additional branch to the much more common cases of the transition function returning the old transition value (which is a pointer in this case), and when the new value is different, but not NULL. In branches since 69c3936, also deduplicate the reparenting code between the expression evaluation based transitions, and the path for ordered aggregates. Reported-By: Teodor Sigaev, Nikita Glukhov Author: Andres Freund Discussion: https://postgr.es/m/bd34e930-cfec-ea9b-3827-a8bc50891393@sigaev.ru Backpatch: 9.4-, this issue has existed since at least 7.4
1 parent 1ef7332 commit 8bb006a

File tree

1 file changed

+19
-0
lines changed

1 file changed

+19
-0
lines changed

src/backend/executor/nodeAgg.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,15 @@ advance_transition_function(AggState *aggstate,
922922
* first input, we don't need to do anything. Also, if transfn returned a
923923
* pointer to a R/W expanded object that is already a child of the
924924
* aggcontext, assume we can adopt that value without copying it.
925+
*
926+
* It's safe to compare newVal with pergroupstate->transValue without
927+
* regard for either being NULL, because we below take care to set
928+
* transValue to 0 when NULL. Otherwise we could end up accidentally not
929+
* reparenting, when the transValue has the same numerical value as
930+
* newVal, despite being NULL. This is a somewhat hot path, making it
931+
* undesirable to instead solve this with another branch for the common
932+
* case of the transition function returning its (modified) input
933+
* argument.
925934
*/
926935
if (!pertrans->transtypeByVal &&
927936
DatumGetPointer(newVal) != DatumGetPointer(pergroupstate->transValue))
@@ -939,6 +948,16 @@ advance_transition_function(AggState *aggstate,
939948
pertrans->transtypeByVal,
940949
pertrans->transtypeLen);
941950
}
951+
else
952+
{
953+
/*
954+
* Ensure that pergroupstate->transValue ends up being 0, so we
955+
* can safely compare newVal/transValue without having to check
956+
* their respective nullness.
957+
*/
958+
newVal = (Datum) 0;
959+
}
960+
942961
if (!pergroupstate->transValueIsNull)
943962
{
944963
if (DatumIsReadWriteExpandedObject(pergroupstate->transValue,

0 commit comments

Comments
 (0)