diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2022-07-19 11:18:19 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2022-07-19 11:18:19 -0400 |
commit | e2f6c307c02924e6ee1667890b56280ab1960d2e (patch) | |
tree | 37fface37170a61d10cb7df3b6b23e691c298445 /src/backend/optimizer/prep/prepunion.c | |
parent | 1679d57a550530ebef624738cc1b12647714fca6 (diff) | |
download | postgresql-e2f6c307c02924e6ee1667890b56280ab1960d2e.tar.gz postgresql-e2f6c307c02924e6ee1667890b56280ab1960d2e.zip |
Estimate cost of elided SubqueryScan, Append, MergeAppend nodes better.
setrefs.c contains logic to discard no-op SubqueryScan nodes, that is,
ones that have no qual to check and copy the input targetlist unchanged.
(Formally it's not very nice to be applying such optimizations so late
in the planner, but there are practical reasons for it; mostly that we
can't unify relids between the subquery and the parent query until we
flatten the rangetable during setrefs.c.) This behavior falsifies our
previous cost estimates, since we would've charged cpu_tuple_cost per
row just to pass data through the node. Most of the time that's little
enough to not matter, but there are cases where this effect visibly
changes the plan compared to what you would've gotten with no
sub-select.
To improve the situation, make the callers of cost_subqueryscan tell
it whether they think the targetlist is trivial. cost_subqueryscan
already has the qual list, so it can check the other half of the
condition easily. It could make its own determination of tlist
triviality too, but doing so would be repetitive (for callers that
may call it several times) or unnecessarily expensive (for callers
that can determine this more cheaply than a general test would do).
This isn't a 100% solution, because createplan.c also does things
that can falsify any earlier estimate of whether the tlist is
trivial. However, it fixes nearly all cases in practice, if results
for the regression tests are anything to go by.
setrefs.c also contains logic to discard no-op Append and MergeAppend
nodes. We did have knowledge of that behavior at costing time, but
somebody failed to update it when a check on parallel-awareness was
added to the setrefs.c logic. Fix that while we're here.
These changes result in two minor changes in query plans shown in
our regression tests. Neither is relevant to the purposes of its
test case AFAICT.
Patch by me; thanks to Richard Guo for review.
Discussion: https://postgr.es/m/2581077.1651703520@sss.pgh.pa.us
Diffstat (limited to 'src/backend/optimizer/prep/prepunion.c')
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index f004fad1d92..2214920dea4 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -78,7 +78,8 @@ static List *generate_setop_tlist(List *colTypes, List *colCollations, Index varno, bool hack_constants, List *input_tlist, - List *refnames_tlist); + List *refnames_tlist, + bool *trivial_tlist); static List *generate_append_tlist(List *colTypes, List *colCollations, bool flag, List *input_tlists, @@ -226,6 +227,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, Path *subpath; Path *path; List *tlist; + bool trivial_tlist; Assert(subquery != NULL); @@ -254,7 +256,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, rtr->rtindex, true, subroot->processed_tlist, - refnames_tlist); + refnames_tlist, + &trivial_tlist); rel->reltarget = create_pathtarget(root, tlist); /* Return the fully-fledged tlist to caller, too */ @@ -291,6 +294,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, * soon too, likely.) */ path = (Path *) create_subqueryscan_path(root, rel, subpath, + trivial_tlist, NIL, NULL); add_path(rel, path); @@ -309,6 +313,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, partial_subpath = linitial(final_rel->partial_pathlist); partial_path = (Path *) create_subqueryscan_path(root, rel, partial_subpath, + trivial_tlist, NIL, NULL); add_partial_path(rel, partial_path); } @@ -376,6 +381,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, !tlist_same_collations(*pTargetList, colCollations, junkOK)) { PathTarget *target; + bool trivial_tlist; ListCell *lc; *pTargetList = generate_setop_tlist(colTypes, colCollations, @@ -383,7 +389,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, 0, false, *pTargetList, - refnames_tlist); + refnames_tlist, + &trivial_tlist); target = create_pathtarget(root, *pTargetList); /* Apply projection to each path */ @@ -1117,6 +1124,7 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses, * hack_constants: true to copy up constants (see comments in code) * input_tlist: targetlist of this node's input node * refnames_tlist: targetlist to take column names from + * trivial_tlist: output parameter, set to true if targetlist is trivial */ static List * generate_setop_tlist(List *colTypes, List *colCollations, @@ -1124,7 +1132,8 @@ generate_setop_tlist(List *colTypes, List *colCollations, Index varno, bool hack_constants, List *input_tlist, - List *refnames_tlist) + List *refnames_tlist, + bool *trivial_tlist) { List *tlist = NIL; int resno = 1; @@ -1135,6 +1144,8 @@ generate_setop_tlist(List *colTypes, List *colCollations, TargetEntry *tle; Node *expr; + *trivial_tlist = true; /* until proven differently */ + forfour(ctlc, colTypes, cclc, colCollations, itlc, input_tlist, rtlc, refnames_tlist) { @@ -1160,6 +1171,9 @@ generate_setop_tlist(List *colTypes, List *colCollations, * this only at the first level of subquery-scan plans; we don't want * phony constants appearing in the output tlists of upper-level * nodes! + * + * Note that copying a constant doesn't in itself require us to mark + * the tlist nontrivial; see trivial_subqueryscan() in setrefs.c. */ if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const)) expr = (Node *) inputtle->expr; @@ -1185,6 +1199,7 @@ generate_setop_tlist(List *colTypes, List *colCollations, expr, colType, "UNION/INTERSECT/EXCEPT"); + *trivial_tlist = false; /* the coercion makes it not trivial */ } /* @@ -1199,9 +1214,12 @@ generate_setop_tlist(List *colTypes, List *colCollations, * will reach the executor without any further processing. */ if (exprCollation(expr) != colColl) + { expr = applyRelabelType(expr, exprType(expr), exprTypmod(expr), colColl, COERCE_IMPLICIT_CAST, -1, false); + *trivial_tlist = false; /* the relabel makes it not trivial */ + } tle = makeTargetEntry((Expr *) expr, (AttrNumber) resno++, @@ -1234,6 +1252,7 @@ generate_setop_tlist(List *colTypes, List *colCollations, pstrdup("flag"), true); tlist = lappend(tlist, tle); + *trivial_tlist = false; /* the extra entry makes it not trivial */ } return tlist; |