aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/setrefs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-05-18 14:31:35 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2024-05-18 14:31:35 -0400
commit4ac385adc5b7ae6474fc8e8fb418131cf2e63b4b (patch)
tree42ae2197f24a989cd8efdd27b03bda95f8c8f0d5 /src/backend/optimizer/plan/setrefs.c
parent484b9587370ecb0325bfc30ca435697f9f52acc6 (diff)
downloadpostgresql-4ac385adc5b7ae6474fc8e8fb418131cf2e63b4b.tar.gz
postgresql-4ac385adc5b7ae6474fc8e8fb418131cf2e63b4b.zip
Account for optimized MinMax aggregates during SS_finalize_plan.
We are capable of optimizing MIN() and MAX() aggregates on indexed columns into subqueries that exploit the index, rather than the normal thing of scanning the whole table. When we do this, we replace the Aggref node(s) with Params referencing subquery outputs. Such Params really ought to be included in the per-plan-node extParam/allParam sets computed by SS_finalize_plan. However, we've never done so up to now because of an ancient implementation choice to perform that substitution during set_plan_references, which runs after SS_finalize_plan, so that SS_finalize_plan never sees these Params. The cleanest fix would be to perform a separate tree walk to do these substitutions before SS_finalize_plan runs. That seems unattractive, first because a whole-tree mutation pass is expensive, and second because we lack infrastructure for visiting expression subtrees in a Plan tree, so that we'd need a new function knowing as much as SS_finalize_plan knows about that. I also considered swapping the order of SS_finalize_plan and set_plan_references, but that fell foul of various assumptions that seem tricky to fix. So the approach adopted here is to teach SS_finalize_plan itself to check for such Aggrefs. I refactored things a bit in setrefs.c to avoid having three copies of the code that does that. Back-patch of v17 commits d0d44049d and 779ac2c74. When d0d44049d went in, there was no evidence that it was fixing a reachable bug, so I refrained from back-patching. Now we have such evidence. Per bug #18465 from Hal Takahara. Back-patch to all supported branches. Discussion: https://postgr.es/m/18465-2fae927718976b22@postgresql.org Discussion: https://postgr.es/m/2391880.1689025003@sss.pgh.pa.us
Diffstat (limited to 'src/backend/optimizer/plan/setrefs.c')
-rw-r--r--src/backend/optimizer/plan/setrefs.c68
1 files changed, 42 insertions, 26 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 9d912a84457..e165810e807 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -2088,22 +2088,14 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
if (IsA(node, Aggref))
{
Aggref *aggref = (Aggref *) node;
+ Param *aggparam;
/* See if the Aggref should be replaced by a Param */
- if (context->root->minmax_aggs != NIL &&
- list_length(aggref->args) == 1)
+ aggparam = find_minmax_agg_replacement_param(context->root, aggref);
+ if (aggparam != NULL)
{
- TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
- ListCell *lc;
-
- foreach(lc, context->root->minmax_aggs)
- {
- MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
-
- if (mminfo->aggfnoid == aggref->aggfnoid &&
- equal(mminfo->target, curTarget->expr))
- return (Node *) copyObject(mminfo->param);
- }
+ /* Make a copy of the Param for paranoia's sake */
+ return (Node *) copyObject(aggparam);
}
/* If no match, just fall through to process it normally */
}
@@ -3030,22 +3022,14 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
if (IsA(node, Aggref))
{
Aggref *aggref = (Aggref *) node;
+ Param *aggparam;
/* See if the Aggref should be replaced by a Param */
- if (context->root->minmax_aggs != NIL &&
- list_length(aggref->args) == 1)
+ aggparam = find_minmax_agg_replacement_param(context->root, aggref);
+ if (aggparam != NULL)
{
- TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
- ListCell *lc;
-
- foreach(lc, context->root->minmax_aggs)
- {
- MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
-
- if (mminfo->aggfnoid == aggref->aggfnoid &&
- equal(mminfo->target, curTarget->expr))
- return (Node *) copyObject(mminfo->param);
- }
+ /* Make a copy of the Param for paranoia's sake */
+ return (Node *) copyObject(aggparam);
}
/* If no match, just fall through to process it normally */
}
@@ -3199,6 +3183,38 @@ set_windowagg_runcondition_references(PlannerInfo *root,
return newlist;
}
+/*
+ * find_minmax_agg_replacement_param
+ * If the given Aggref is one that we are optimizing into a subquery
+ * (cf. planagg.c), then return the Param that should replace it.
+ * Else return NULL.
+ *
+ * This is exported so that SS_finalize_plan can use it before setrefs.c runs.
+ * Note that it will not find anything until we have built a Plan from a
+ * MinMaxAggPath, as root->minmax_aggs will never be filled otherwise.
+ */
+Param *
+find_minmax_agg_replacement_param(PlannerInfo *root, Aggref *aggref)
+{
+ if (root->minmax_aggs != NIL &&
+ list_length(aggref->args) == 1)
+ {
+ TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
+ ListCell *lc;
+
+ foreach(lc, root->minmax_aggs)
+ {
+ MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
+
+ if (mminfo->aggfnoid == aggref->aggfnoid &&
+ equal(mminfo->target, curTarget->expr))
+ return mminfo->param;
+ }
+ }
+ return NULL;
+}
+
+
/*****************************************************************************
* QUERY DEPENDENCY MANAGEMENT
*****************************************************************************/