diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2017-10-14 15:21:39 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2017-10-14 15:21:39 -0400 |
commit | 4de2d4fba38f4f7aff7f95401eb43a6cd05a6db4 (patch) | |
tree | d899e4a764d3d6234918a1652882c71f1af564ed /src/backend/executor/nodeAgg.c | |
parent | 5f340cb30ce2f0d9f272840b0d977b0a4b854f0b (diff) | |
download | postgresql-4de2d4fba38f4f7aff7f95401eb43a6cd05a6db4.tar.gz postgresql-4de2d4fba38f4f7aff7f95401eb43a6cd05a6db4.zip |
Explicitly track whether aggregate final functions modify transition state.
Up to now, there's been hard-wired assumptions that normal aggregates'
final functions never modify their transition states, while ordered-set
aggregates' final functions always do. This has always been a bit
limiting, and in particular it's getting in the way of improving the
built-in ordered-set aggregates to allow merging of transition states.
Therefore, let's introduce catalog and CREATE AGGREGATE infrastructure
that lets the finalfn's behavior be declared explicitly.
There are now three possibilities for the finalfn behavior: it's purely
read-only, it trashes the transition state irrecoverably, or it changes
the state in such a way that no more transfn calls are possible but the
state can still be passed to other, compatible finalfns. There are no
examples of this third case today, but we'll shortly make the built-in
OSAs act like that.
This change allows user-defined aggregates to explicitly disclaim support
for use as window functions, and/or to prevent transition state merging,
if their implementations cannot handle that. While it was previously
possible to handle the window case with a run-time error check, there was
not any way to prevent transition state merging, which in retrospect is
something commit 804163bc2 should have provided for. But better late
than never.
In passing, split out pg_aggregate.c's extern function declarations into
a new header file pg_aggregate_fn.h, similarly to what we've done for
some other catalog headers, so that pg_aggregate.h itself can be safe
for frontend files to include. This lets pg_dump use the symbolic
names for relevant constants.
Discussion: https://postgr.es/m/4834.1507849699@sss.pgh.pa.us
Diffstat (limited to 'src/backend/executor/nodeAgg.c')
-rw-r--r-- | src/backend/executor/nodeAgg.c | 65 |
1 files changed, 40 insertions, 25 deletions
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 6543ecebd3e..40d8ec9db46 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -248,9 +248,9 @@ typedef struct AggStatePerTransData /* * Link to an Aggref expr this state value is for. * - * There can be multiple Aggref's sharing the same state value, as long as - * the inputs and transition function are identical. This points to the - * first one of them. + * There can be multiple Aggref's sharing the same state value, so long as + * the inputs and transition functions are identical and the final + * functions are not read-write. This points to the first one of them. */ Aggref *aggref; @@ -419,8 +419,8 @@ typedef struct AggStatePerAggData Oid finalfn_oid; /* - * fmgr lookup data for final function --- only valid when finalfn_oid oid - * is not InvalidOid. + * fmgr lookup data for final function --- only valid when finalfn_oid is + * not InvalidOid. */ FmgrInfo finalfn; @@ -439,6 +439,11 @@ typedef struct AggStatePerAggData int16 resulttypeLen; bool resulttypeByVal; + /* + * "sharable" is false if this agg cannot share state values with other + * aggregates because the final function is read-write. + */ + bool sharable; } AggStatePerAggData; /* @@ -572,6 +577,7 @@ static void build_pertrans_for_aggref(AggStatePerTrans pertrans, static int find_compatible_peragg(Aggref *newagg, AggState *aggstate, int lastaggno, List **same_input_transnos); static int find_compatible_pertrans(AggState *aggstate, Aggref *newagg, + bool sharable, Oid aggtransfn, Oid aggtranstype, Oid aggserialfn, Oid aggdeserialfn, Datum initValue, bool initValueIsNull, @@ -3105,6 +3111,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) AclResult aclresult; Oid transfn_oid, finalfn_oid; + bool sharable; Oid serialfn_oid, deserialfn_oid; Expr *finalfnexpr; @@ -3177,6 +3184,15 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) else peragg->finalfn_oid = finalfn_oid = aggform->aggfinalfn; + /* + * If finalfn is marked read-write, we can't share transition states; + * but it is okay to share states for AGGMODIFY_SHARABLE aggs. Also, + * if we're not executing the finalfn here, we can share regardless. + */ + sharable = (aggform->aggfinalmodify != AGGMODIFY_READ_WRITE) || + (finalfn_oid == InvalidOid); + peragg->sharable = sharable; + serialfn_oid = InvalidOid; deserialfn_oid = InvalidOid; @@ -3315,11 +3331,12 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) * 2. Build working state for invoking the transition function, or * look up previously initialized working state, if we can share it. * - * find_compatible_peragg() already collected a list of per-Trans's - * with the same inputs. Check if any of them have the same transition - * function and initial value. + * find_compatible_peragg() already collected a list of sharable + * per-Trans's with the same inputs. Check if any of them have the + * same transition function and initial value. */ existing_transno = find_compatible_pertrans(aggstate, aggref, + sharable, transfn_oid, aggtranstype, serialfn_oid, deserialfn_oid, initValue, initValueIsNull, @@ -3724,10 +3741,10 @@ GetAggInitVal(Datum textInitVal, Oid transtype) * with this one, with the same input parameters. If no compatible aggregate * can be found, returns -1. * - * As a side-effect, this also collects a list of existing per-Trans structs - * with matching inputs. If no identical Aggref is found, the list is passed - * later to find_compatible_pertrans, to see if we can at least reuse the - * state value of another aggregate. + * As a side-effect, this also collects a list of existing, sharable per-Trans + * structs with matching inputs. If no identical Aggref is found, the list is + * passed later to find_compatible_pertrans, to see if we can at least reuse + * the state value of another aggregate. */ static int find_compatible_peragg(Aggref *newagg, AggState *aggstate, @@ -3785,11 +3802,15 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate, } /* - * Not identical, but it had the same inputs. Return it to the caller, - * in case we can re-use its per-trans state. + * Not identical, but it had the same inputs. If the final function + * permits sharing, return its transno to the caller, in case we can + * re-use its per-trans state. (If there's already sharing going on, + * we might report a transno more than once. find_compatible_pertrans + * is cheap enough that it's not worth spending cycles to avoid that.) */ - *same_input_transnos = lappend_int(*same_input_transnos, - peragg->transno); + if (peragg->sharable) + *same_input_transnos = lappend_int(*same_input_transnos, + peragg->transno); } return -1; @@ -3804,7 +3825,7 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate, * verified to match.) */ static int -find_compatible_pertrans(AggState *aggstate, Aggref *newagg, +find_compatible_pertrans(AggState *aggstate, Aggref *newagg, bool sharable, Oid aggtransfn, Oid aggtranstype, Oid aggserialfn, Oid aggdeserialfn, Datum initValue, bool initValueIsNull, @@ -3812,14 +3833,8 @@ find_compatible_pertrans(AggState *aggstate, Aggref *newagg, { ListCell *lc; - /* - * For the moment, never try to share transition states between different - * ordered-set aggregates. This is necessary because the finalfns of the - * built-in OSAs (see orderedsetaggs.c) are destructive of their - * transition states. We should fix them so we can allow this, but not - * losing performance in the normal non-shared case will take some work. - */ - if (AGGKIND_IS_ORDERED_SET(newagg->aggkind)) + /* If this aggregate can't share transition states, give up */ + if (!sharable) return -1; foreach(lc, transnos) |