aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/createplan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/createplan.c')
-rw-r--r--src/backend/optimizer/plan/createplan.c207
1 files changed, 168 insertions, 39 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 9a8204392a1..143b04ca9cb 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.221 2007/01/10 18:06:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.222 2007/01/20 20:45:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -121,8 +121,6 @@ static MergeJoin *make_mergejoin(List *tlist,
JoinType jointype);
static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
AttrNumber *sortColIdx, Oid *sortOperators, bool *nullsFirst);
-static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
- List *pathkeys);
/*
@@ -1425,23 +1423,21 @@ create_nestloop_plan(PlannerInfo *root,
* that have to be checked as qpquals at the join node.
*
* We can also remove any join clauses that are redundant with those
- * being used in the index scan; prior redundancy checks will not have
- * caught this case because the join clauses would never have been put
- * in the same joininfo list.
+ * being used in the index scan; this check is needed because
+ * find_eclass_clauses_for_index_join() may emit different clauses
+ * than generate_join_implied_equalities() did.
*
* We can skip this if the index path is an ordinary indexpath and not
- * a special innerjoin path.
+ * a special innerjoin path, since it then wouldn't be using any join
+ * clauses.
*/
IndexPath *innerpath = (IndexPath *) best_path->innerjoinpath;
if (innerpath->isjoininner)
- {
joinrestrictclauses =
select_nonredundant_join_clauses(root,
joinrestrictclauses,
- innerpath->indexclauses,
- IS_OUTER_JOIN(best_path->jointype));
- }
+ innerpath->indexclauses);
}
else if (IsA(best_path->innerjoinpath, BitmapHeapPath))
{
@@ -1471,8 +1467,7 @@ create_nestloop_plan(PlannerInfo *root,
joinrestrictclauses =
select_nonredundant_join_clauses(root,
joinrestrictclauses,
- bitmapclauses,
- IS_OUTER_JOIN(best_path->jointype));
+ bitmapclauses);
}
}
@@ -1516,7 +1511,21 @@ create_mergejoin_plan(PlannerInfo *root,
List *joinclauses;
List *otherclauses;
List *mergeclauses;
+ List *outerpathkeys;
+ List *innerpathkeys;
+ int nClauses;
+ Oid *mergefamilies;
+ int *mergestrategies;
+ bool *mergenullsfirst;
MergeJoin *join_plan;
+ int i;
+ EquivalenceClass *lastoeclass;
+ EquivalenceClass *lastieclass;
+ PathKey *opathkey;
+ PathKey *ipathkey;
+ ListCell *lc;
+ ListCell *lop;
+ ListCell *lip;
/* Get the join qual clauses (in plain expression form) */
/* Any pseudoconstant clauses are ignored here */
@@ -1542,7 +1551,8 @@ create_mergejoin_plan(PlannerInfo *root,
/*
* Rearrange mergeclauses, if needed, so that the outer variable is always
- * on the left.
+ * on the left; mark the mergeclause restrictinfos with correct
+ * outer_is_left status.
*/
mergeclauses = get_switched_clauses(best_path->path_mergeclauses,
best_path->jpath.outerjoinpath->parent->relids);
@@ -1564,7 +1574,10 @@ create_mergejoin_plan(PlannerInfo *root,
make_sort_from_pathkeys(root,
outer_plan,
best_path->outersortkeys);
+ outerpathkeys = best_path->outersortkeys;
}
+ else
+ outerpathkeys = best_path->jpath.outerjoinpath->pathkeys;
if (best_path->innersortkeys)
{
@@ -1573,8 +1586,87 @@ create_mergejoin_plan(PlannerInfo *root,
make_sort_from_pathkeys(root,
inner_plan,
best_path->innersortkeys);
+ innerpathkeys = best_path->innersortkeys;
+ }
+ else
+ innerpathkeys = best_path->jpath.innerjoinpath->pathkeys;
+
+ /*
+ * Compute the opfamily/strategy/nullsfirst arrays needed by the executor.
+ * The information is in the pathkeys for the two inputs, but we need to
+ * be careful about the possibility of mergeclauses sharing a pathkey
+ * (compare find_mergeclauses_for_pathkeys()).
+ */
+ nClauses = list_length(mergeclauses);
+ Assert(nClauses == list_length(best_path->path_mergeclauses));
+ mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid));
+ mergestrategies = (int *) palloc(nClauses * sizeof(int));
+ mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool));
+
+ lastoeclass = NULL;
+ lastieclass = NULL;
+ opathkey = NULL;
+ ipathkey = NULL;
+ lop = list_head(outerpathkeys);
+ lip = list_head(innerpathkeys);
+ i = 0;
+ foreach(lc, best_path->path_mergeclauses)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+ EquivalenceClass *oeclass;
+ EquivalenceClass *ieclass;
+
+ /* fetch outer/inner eclass from mergeclause */
+ Assert(IsA(rinfo, RestrictInfo));
+ if (rinfo->outer_is_left)
+ {
+ oeclass = rinfo->left_ec;
+ ieclass = rinfo->right_ec;
+ }
+ else
+ {
+ oeclass = rinfo->right_ec;
+ ieclass = rinfo->left_ec;
+ }
+ Assert(oeclass != NULL);
+ Assert(ieclass != NULL);
+
+ /* should match current or next pathkeys */
+ /* we check this carefully for debugging reasons */
+ if (oeclass != lastoeclass)
+ {
+ if (!lop)
+ elog(ERROR, "too few pathkeys for mergeclauses");
+ opathkey = (PathKey *) lfirst(lop);
+ lop = lnext(lop);
+ lastoeclass = opathkey->pk_eclass;
+ if (oeclass != lastoeclass)
+ elog(ERROR, "outer pathkeys do not match mergeclause");
+ }
+ if (ieclass != lastieclass)
+ {
+ if (!lip)
+ elog(ERROR, "too few pathkeys for mergeclauses");
+ ipathkey = (PathKey *) lfirst(lip);
+ lip = lnext(lip);
+ lastieclass = ipathkey->pk_eclass;
+ if (ieclass != lastieclass)
+ elog(ERROR, "inner pathkeys do not match mergeclause");
+ }
+ /* pathkeys should match each other too (more debugging) */
+ if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
+ opathkey->pk_strategy != ipathkey->pk_strategy ||
+ opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
+ elog(ERROR, "left and right pathkeys do not match in mergejoin");
+
+ /* OK, save info for executor */
+ mergefamilies[i] = opathkey->pk_opfamily;
+ mergestrategies[i] = opathkey->pk_strategy;
+ mergenullsfirst[i] = opathkey->pk_nulls_first;
+ i++;
}
+
/*
* Now we can build the mergejoin node.
*/
@@ -1582,9 +1674,9 @@ create_mergejoin_plan(PlannerInfo *root,
joinclauses,
otherclauses,
mergeclauses,
- best_path->path_mergeFamilies,
- best_path->path_mergeStrategies,
- best_path->path_mergeNullsFirst,
+ mergefamilies,
+ mergestrategies,
+ mergenullsfirst,
outer_plan,
inner_plan,
best_path->jpath.jointype);
@@ -1921,8 +2013,9 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, Oid *opfamily)
* Given a list of merge or hash joinclauses (as RestrictInfo nodes),
* extract the bare clauses, and rearrange the elements within the
* clauses, if needed, so the outer join variable is on the left and
- * the inner is on the right. The original data structure is not touched;
- * a modified list is returned.
+ * the inner is on the right. The original clause data structure is not
+ * touched; a modified list is returned. We do, however, set the transient
+ * outer_is_left field in each RestrictInfo to show which side was which.
*/
static List *
get_switched_clauses(List *clauses, Relids outerrelids)
@@ -1953,9 +2046,14 @@ get_switched_clauses(List *clauses, Relids outerrelids)
/* Commute it --- note this modifies the temp node in-place. */
CommuteOpExpr(temp);
t_list = lappend(t_list, temp);
+ restrictinfo->outer_is_left = false;
}
else
+ {
+ Assert(bms_is_subset(restrictinfo->left_relids, outerrelids));
t_list = lappend(t_list, clause);
+ restrictinfo->outer_is_left = true;
+ }
}
return t_list;
}
@@ -2490,7 +2588,7 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first,
* If the input plan type isn't one that can do projections, this means
* adding a Result node just to do the projection.
*/
-static Sort *
+Sort *
make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys)
{
List *tlist = lefttree->targetlist;
@@ -2512,41 +2610,55 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys)
foreach(i, pathkeys)
{
- List *keysublist = (List *) lfirst(i);
- PathKeyItem *pathkey = NULL;
+ PathKey *pathkey = (PathKey *) lfirst(i);
TargetEntry *tle = NULL;
+ Oid pk_datatype = InvalidOid;
+ Oid sortop;
ListCell *j;
/*
- * We can sort by any one of the sort key items listed in this
- * sublist. For now, we take the first one that corresponds to an
- * available Var in the tlist. If there isn't any, use the first one
- * that is an expression in the input's vars.
+ * We can sort by any non-constant expression listed in the pathkey's
+ * EquivalenceClass. For now, we take the first one that corresponds
+ * to an available Var in the tlist. If there isn't any, use the first
+ * one that is an expression in the input's vars. (The non-const
+ * restriction only matters if the EC is below_outer_join; but if it
+ * isn't, it won't contain consts anyway, else we'd have discarded
+ * the pathkey as redundant.)
*
* XXX if we have a choice, is there any way of figuring out which
* might be cheapest to execute? (For example, int4lt is likely much
* cheaper to execute than numericlt, but both might appear in the
- * same pathkey sublist...) Not clear that we ever will have a choice
- * in practice, so it may not matter.
+ * same equivalence class...) Not clear that we ever will have an
+ * interesting choice in practice, so it may not matter.
*/
- foreach(j, keysublist)
+ foreach(j, pathkey->pk_eclass->ec_members)
{
- pathkey = (PathKeyItem *) lfirst(j);
- Assert(IsA(pathkey, PathKeyItem));
- tle = tlist_member(pathkey->key, tlist);
+ EquivalenceMember *em = (EquivalenceMember *) lfirst(j);
+
+ if (em->em_is_const || em->em_is_child)
+ continue;
+ tle = tlist_member((Node *) em->em_expr, tlist);
if (tle)
- break;
+ {
+ pk_datatype = em->em_datatype;
+ break; /* found expr already in tlist */
+ }
}
if (!tle)
{
/* No matching Var; look for a computable expression */
- foreach(j, keysublist)
+ Expr *sortexpr = NULL;
+
+ foreach(j, pathkey->pk_eclass->ec_members)
{
+ EquivalenceMember *em = (EquivalenceMember *) lfirst(j);
List *exprvars;
ListCell *k;
- pathkey = (PathKeyItem *) lfirst(j);
- exprvars = pull_var_clause(pathkey->key, false);
+ if (em->em_is_const || em->em_is_child)
+ continue;
+ sortexpr = em->em_expr;
+ exprvars = pull_var_clause((Node *) sortexpr, false);
foreach(k, exprvars)
{
if (!tlist_member(lfirst(k), tlist))
@@ -2554,7 +2666,10 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys)
}
list_free(exprvars);
if (!k)
+ {
+ pk_datatype = em->em_datatype;
break; /* found usable expression */
+ }
}
if (!j)
elog(ERROR, "could not find pathkey item to sort");
@@ -2571,7 +2686,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys)
/*
* Add resjunk entry to input's tlist
*/
- tle = makeTargetEntry((Expr *) pathkey->key,
+ tle = makeTargetEntry(sortexpr,
list_length(tlist) + 1,
NULL,
true);
@@ -2580,13 +2695,27 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys)
}
/*
+ * Look up the correct sort operator from the PathKey's slightly
+ * abstracted representation.
+ */
+ sortop = get_opfamily_member(pathkey->pk_opfamily,
+ pk_datatype,
+ pk_datatype,
+ pathkey->pk_strategy);
+ if (!OidIsValid(sortop)) /* should not happen */
+ elog(ERROR, "could not find member %d(%u,%u) of opfamily %u",
+ pathkey->pk_strategy, pk_datatype, pk_datatype,
+ pathkey->pk_opfamily);
+
+ /*
* The column might already be selected as a sort key, if the pathkeys
* contain duplicate entries. (This can happen in scenarios where
* multiple mergejoinable clauses mention the same var, for example.)
* So enter it only once in the sort arrays.
*/
- numsortkeys = add_sort_column(tle->resno, pathkey->sortop,
- pathkey->nulls_first,
+ numsortkeys = add_sort_column(tle->resno,
+ sortop,
+ pathkey->pk_nulls_first,
numsortkeys,
sortColIdx, sortOperators, nullsFirst);
}