aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execExprInterp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execExprInterp.c')
-rw-r--r--src/backend/executor/execExprInterp.c49
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;