*** a/src/backend/optimizer/path/allpaths.c --- b/src/backend/optimizer/path/allpaths.c *************** *** 1018,1059 **** set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); /* ! * Copy/Modify targetlist. Even if this child is deemed empty, we need ! * its targetlist in case it falls on nullable side in a child-join ! * because of partitionwise join. ! * ! * NB: the resulting childrel->reltarget->exprs may contain arbitrary ! * expressions, which otherwise would not occur in a rel's targetlist. ! * Code that might be looking at an appendrel child must cope with ! * such. (Normally, a rel's targetlist would only include Vars and ! * PlaceHolderVars.) XXX we do not bother to update the cost or width ! * fields of childrel->reltarget; not clear if that would be useful. ! */ ! childrel->reltarget->exprs = (List *) ! adjust_appendrel_attrs(root, ! (Node *) rel->reltarget->exprs, ! 1, &appinfo); ! ! /* ! * We have to make child entries in the EquivalenceClass data ! * structures as well. This is needed either if the parent ! * participates in some eclass joins (because we will want to consider ! * inner-indexscan joins on the individual children) or if the parent ! * has useful pathkeys (because we should try to build MergeAppend ! * paths that produce those sort orderings). Even if this child is ! * deemed dummy, it may fall on nullable side in a child-join, which ! * in turn may participate in a MergeAppend, where we will need the ! * EquivalenceClass data structures. ! */ ! if (rel->has_eclass_joins || has_useful_pathkeys(root, rel)) ! add_child_rel_equivalences(root, appinfo, rel, childrel); ! childrel->has_eclass_joins = rel->has_eclass_joins; ! ! /* ! * We have to copy the parent's quals to the child, with appropriate ! * substitution of variables. However, only the baserestrictinfo ! * quals are needed before we can check for constraint exclusion; so ! * do that first and then check to see if we can disregard this child. * * The child rel's targetlist might contain non-Var expressions, which * means that substitution into the quals could produce opportunities --- 1018,1028 ---- Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); /* ! * We have to copy the parent's targetlist and quals to the child, ! * with appropriate substitution of variables. However, only the ! * baserestrictinfo quals are needed before we can check for ! * constraint exclusion; so do that first and then check to see if we ! * can disregard this child. * * The child rel's targetlist might contain non-Var expressions, which * means that substitution into the quals could produce opportunities *************** *** 1187,1197 **** set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, continue; } ! /* CE failed, so finish copying/modifying join quals. */ childrel->joininfo = (List *) adjust_appendrel_attrs(root, (Node *) rel->joininfo, 1, &appinfo); /* * Note: we could compute appropriate attr_needed data for the child's --- 1156,1191 ---- continue; } ! /* ! * CE failed, so finish copying/modifying targetlist and join quals. ! * ! * NB: the resulting childrel->reltarget->exprs may contain arbitrary ! * expressions, which otherwise would not occur in a rel's targetlist. ! * Code that might be looking at an appendrel child must cope with ! * such. (Normally, a rel's targetlist would only include Vars and ! * PlaceHolderVars.) XXX we do not bother to update the cost or width ! * fields of childrel->reltarget; not clear if that would be useful. ! */ childrel->joininfo = (List *) adjust_appendrel_attrs(root, (Node *) rel->joininfo, 1, &appinfo); + childrel->reltarget->exprs = (List *) + adjust_appendrel_attrs(root, + (Node *) rel->reltarget->exprs, + 1, &appinfo); + + /* + * We have to make child entries in the EquivalenceClass data + * structures as well. This is needed either if the parent + * participates in some eclass joins (because we will want to consider + * inner-indexscan joins on the individual children) or if the parent + * has useful pathkeys (because we should try to build MergeAppend + * paths that produce those sort orderings). + */ + if (rel->has_eclass_joins || has_useful_pathkeys(root, rel)) + add_child_rel_equivalences(root, appinfo, rel, childrel); + childrel->has_eclass_joins = rel->has_eclass_joins; /* * Note: we could compute appropriate attr_needed data for the child's *************** *** 1204,1212 **** set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, /* * If we consider partitionwise joins with the parent rel, do the same * for partitioned child rels. */ ! if (rel->consider_partitionwise_join && ! childRTE->relkind == RELKIND_PARTITIONED_TABLE) childrel->consider_partitionwise_join = true; /* --- 1198,1209 ---- /* * If we consider partitionwise joins with the parent rel, do the same * for partitioned child rels. + * + * Note: here we abuse the consider_partitionwise_join flag for child + * rels that are not partitioned, to tell try_partitionwise_join() + * that their targetlists and EC entries have been generated. */ ! if (rel->consider_partitionwise_join) childrel->consider_partitionwise_join = true; /* *** a/src/backend/optimizer/path/joinrels.c --- b/src/backend/optimizer/path/joinrels.c *************** *** 44,49 **** static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, --- 44,51 ---- RelOptInfo *rel2, RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo, List *parent_restrictlist); + static void update_child_rel_info(PlannerInfo *root, + RelOptInfo *rel, RelOptInfo *childrel); static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op); *************** *** 1312,1317 **** try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, --- 1314,1321 ---- RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo, List *parent_restrictlist) { + bool rel1_is_simple = IS_SIMPLE_REL(rel1); + bool rel2_is_simple = IS_SIMPLE_REL(rel2); int nparts; int cnt_parts; *************** *** 1376,1381 **** try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, --- 1380,1406 ---- AppendRelInfo **appinfos; int nappinfos; + /* + * If a child table has consider_partitionwise_join=false, it means + * that it's a dummy relation for which we skipped setting up tlist + * expressions and adding EC members in set_append_rel_size(), so do + * that now for use later. + */ + if (rel1_is_simple && !child_rel1->consider_partitionwise_join) + { + Assert(child_rel1->reloptkind == RELOPT_OTHER_MEMBER_REL); + Assert(IS_DUMMY_REL(child_rel1)); + update_child_rel_info(root, rel1, child_rel1); + child_rel1->consider_partitionwise_join = true; + } + if (rel2_is_simple && !child_rel2->consider_partitionwise_join) + { + Assert(child_rel2->reloptkind == RELOPT_OTHER_MEMBER_REL); + Assert(IS_DUMMY_REL(child_rel2)); + update_child_rel_info(root, rel2, child_rel2); + child_rel2->consider_partitionwise_join = true; + } + /* We should never try to join two overlapping sets of rels. */ Assert(!bms_overlap(child_rel1->relids, child_rel2->relids)); child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids); *************** *** 1417,1422 **** try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, --- 1442,1469 ---- } } + /* + * Set up tlist expressions for the childrel, and add EC members referencing + * the childrel. + */ + static void + update_child_rel_info(PlannerInfo *root, + RelOptInfo *rel, RelOptInfo *childrel) + { + AppendRelInfo *appinfo = root->append_rel_array[childrel->relid]; + + /* Make child tlist expressions */ + childrel->reltarget->exprs = (List *) + adjust_appendrel_attrs(root, + (Node *) rel->reltarget->exprs, + 1, &appinfo); + + /* Make child entries in the EquivalenceClass as well */ + if (rel->has_eclass_joins || has_useful_pathkeys(root, rel)) + add_child_rel_equivalences(root, appinfo, rel, childrel); + childrel->has_eclass_joins = rel->has_eclass_joins; + } + /* * Returns true if there exists an equi-join condition for each pair of * partition keys from given relations being joined.