Skip to content

Commit 43f0c20

Browse files
committed
Fix overoptimistic assumptions in column width estimation for subqueries.
set_append_rel_pathlist supposed that, while computing per-column width estimates for the appendrel, it could ignore child rels for which the translated reltargetlist entry wasn't a Var. This gave rise to completely silly estimates in some common cases, such as constant outputs from some or all of the arms of a UNION ALL. Instead, fall back on get_typavgwidth to estimate from the value's datatype; which might be a poor estimate but at least it's not completely wacko. That problem was exposed by an Assert in set_subquery_size_estimates, which unfortunately was still overoptimistic even with that fix, since we don't compute attr_widths estimates for appendrels that are entirely excluded by constraints. So remove the Assert; we'll just fall back on get_typavgwidth in such cases. Also, since set_subquery_size_estimates calls set_baserel_size_estimates which calls set_rel_width, there's no need for set_subquery_size_estimates to call get_typavgwidth; set_rel_width will handle it for us if we just leave the estimate set to zero. Remove the unnecessary code. Per report from Erik Rijkers and subsequent investigation.
1 parent 1af55e2 commit 43f0c20

File tree

2 files changed

+44
-22
lines changed

2 files changed

+44
-22
lines changed

src/backend/optimizer/path/allpaths.c

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,15 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
403403
continue;
404404
}
405405

406-
/* CE failed, so finish copying targetlist and join quals */
406+
/*
407+
* CE failed, so finish copying/modifying targetlist and join quals.
408+
*
409+
* Note: the resulting childrel->reltargetlist may contain arbitrary
410+
* expressions, which normally would not occur in a reltargetlist.
411+
* That is okay because nothing outside of this routine will look at
412+
* the child rel's reltargetlist. We do have to cope with the case
413+
* while constructing attr_widths estimates below, though.
414+
*/
407415
childrel->joininfo = (List *)
408416
adjust_appendrel_attrs((Node *) rel->joininfo,
409417
appinfo);
@@ -486,23 +494,36 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
486494
parent_rows += childrel->rows;
487495
parent_size += childrel->width * childrel->rows;
488496

497+
/*
498+
* Accumulate per-column estimates too. We need not do anything
499+
* for PlaceHolderVars in the parent list. If child expression
500+
* isn't a Var, or we didn't record a width estimate for it, we
501+
* have to fall back on a datatype-based estimate.
502+
*
503+
* By construction, child's reltargetlist is 1-to-1 with parent's.
504+
*/
489505
forboth(parentvars, rel->reltargetlist,
490506
childvars, childrel->reltargetlist)
491507
{
492508
Var *parentvar = (Var *) lfirst(parentvars);
493-
Var *childvar = (Var *) lfirst(childvars);
494-
495-
/*
496-
* Accumulate per-column estimates too. Whole-row Vars and
497-
* PlaceHolderVars can be ignored here.
498-
*/
499-
if (IsA(parentvar, Var) &&
500-
IsA(childvar, Var))
509+
Node *childvar = (Node *) lfirst(childvars);
510+
511+
if (IsA(parentvar, Var))
501512
{
502513
int pndx = parentvar->varattno - rel->min_attr;
503-
int cndx = childvar->varattno - childrel->min_attr;
504-
505-
parent_attrsizes[pndx] += childrel->attr_widths[cndx] * childrel->rows;
514+
int32 child_width = 0;
515+
516+
if (IsA(childvar, Var))
517+
{
518+
int cndx = ((Var *) childvar)->varattno - childrel->min_attr;
519+
520+
child_width = childrel->attr_widths[cndx];
521+
}
522+
if (child_width <= 0)
523+
child_width = get_typavgwidth(exprType(childvar),
524+
exprTypmod(childvar));
525+
Assert(child_width > 0);
526+
parent_attrsizes[pndx] += child_width * childrel->rows;
506527
}
507528
}
508529
}

src/backend/optimizer/path/costsize.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3238,14 +3238,14 @@ set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
32383238
/*
32393239
* Compute per-output-column width estimates by examining the subquery's
32403240
* targetlist. For any output that is a plain Var, get the width estimate
3241-
* that was made while planning the subquery. Otherwise, fall back on a
3242-
* datatype-based estimate.
3241+
* that was made while planning the subquery. Otherwise, we leave it to
3242+
* set_rel_width to fill in a datatype-based default estimate.
32433243
*/
32443244
foreach(lc, subroot->parse->targetList)
32453245
{
32463246
TargetEntry *te = (TargetEntry *) lfirst(lc);
32473247
Node *texpr = (Node *) te->expr;
3248-
int32 item_width;
3248+
int32 item_width = 0;
32493249

32503250
Assert(IsA(te, TargetEntry));
32513251
/* junk columns aren't visible to upper query */
@@ -3256,8 +3256,14 @@ set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
32563256
* XXX This currently doesn't work for subqueries containing set
32573257
* operations, because the Vars in their tlists are bogus references
32583258
* to the first leaf subquery, which wouldn't give the right answer
3259-
* even if we could still get to its PlannerInfo. So fall back on
3260-
* datatype in that case.
3259+
* even if we could still get to its PlannerInfo.
3260+
*
3261+
* Also, the subquery could be an appendrel for which all branches are
3262+
* known empty due to constraint exclusion, in which case
3263+
* set_append_rel_pathlist will have left the attr_widths set to zero.
3264+
*
3265+
* In either case, we just leave the width estimate zero until
3266+
* set_rel_width fixes it.
32613267
*/
32623268
if (IsA(texpr, Var) &&
32633269
subroot->parse->setOperations == NULL)
@@ -3267,11 +3273,6 @@ set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
32673273

32683274
item_width = subrel->attr_widths[var->varattno - subrel->min_attr];
32693275
}
3270-
else
3271-
{
3272-
item_width = get_typavgwidth(exprType(texpr), exprTypmod(texpr));
3273-
}
3274-
Assert(item_width > 0);
32753276
Assert(te->resno >= rel->min_attr && te->resno <= rel->max_attr);
32763277
rel->attr_widths[te->resno - rel->min_attr] = item_width;
32773278
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy