diff options
Diffstat (limited to 'src/backend/executor/execExprInterp.c')
-rw-r--r-- | src/backend/executor/execExprInterp.c | 49 |
1 files changed, 36 insertions, 13 deletions
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 4cd46f17176..ca44d39100a 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -4341,15 +4341,40 @@ ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup } /* - * Ensure that the current transition value is a child of the aggcontext, - * rather than the per-tuple context. + * Ensure that the new transition value is stored in the aggcontext, + * rather than the per-tuple context. This should be invoked only when + * we know (a) the transition data type is pass-by-reference, and (b) + * the newValue is distinct from the oldValue. * * NB: This can change the current memory context. + * + * We copy the presented newValue into the aggcontext, except when the datum + * points to a R/W expanded object that is already a child of the aggcontext, + * in which case we need not copy. We then delete the oldValue, if not null. + * + * If the presented datum points to a R/W expanded object that is a child of + * some other context, ideally we would just reparent it under the aggcontext. + * Unfortunately, that doesn't work easily, and it wouldn't help anyway for + * aggregate-aware transfns. We expect that a transfn that deals in expanded + * objects and is aware of the memory management conventions for aggregate + * transition values will (1) on first call, return a R/W expanded object that + * is already in the right context, allowing us to do nothing here, and (2) on + * subsequent calls, modify and return that same object, so that control + * doesn't even reach here. However, if we have a generic transfn that + * returns a new R/W expanded object (probably in the per-tuple context), + * reparenting that result would cause problems. We'd pass that R/W object to + * the next invocation of the transfn, and then it would be at liberty to + * change or delete that object, and if it deletes it then our own attempt to + * delete the now-old transvalue afterwards would be a double free. We avoid + * this problem by forcing the stored transvalue to always be a flat + * non-expanded object unless the transfn is visibly doing aggregate-aware + * memory management. This is somewhat inefficient, but the best answer to + * that is to write a smarter transfn. */ Datum -ExecAggTransReparent(AggState *aggstate, AggStatePerTrans pertrans, - Datum newValue, bool newValueIsNull, - Datum oldValue, bool oldValueIsNull) +ExecAggCopyTransValue(AggState *aggstate, AggStatePerTrans pertrans, + Datum newValue, bool newValueIsNull, + Datum oldValue, bool oldValueIsNull) { Assert(newValue != oldValue); @@ -4559,12 +4584,10 @@ ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans, /* * For pass-by-ref datatype, must copy the new value into aggcontext and * free the prior transValue. But if transfn returned a pointer to its - * first input, we don't need to do anything. Also, if transfn returned a - * pointer to a R/W expanded object that is already a child of the - * aggcontext, assume we can adopt that value without copying it. + * first input, we don't need to do anything. * * It's safe to compare newVal with pergroup->transValue without regard - * for either being NULL, because ExecAggTransReparent() takes care to set + * for either being NULL, because ExecAggCopyTransValue takes care to set * transValue to 0 when NULL. Otherwise we could end up accidentally not * reparenting, when the transValue has the same numerical value as * newValue, despite being NULL. This is a somewhat hot path, making it @@ -4573,10 +4596,10 @@ ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans, * argument. */ if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue)) - newVal = ExecAggTransReparent(aggstate, pertrans, - newVal, fcinfo->isnull, - pergroup->transValue, - pergroup->transValueIsNull); + newVal = ExecAggCopyTransValue(aggstate, pertrans, + newVal, fcinfo->isnull, + pergroup->transValue, + pergroup->transValueIsNull); pergroup->transValue = newVal; pergroup->transValueIsNull = fcinfo->isnull; |