summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r--src/backend/optimizer/plan/createplan.c68
-rw-r--r--src/backend/optimizer/plan/initsplan.c20
-rw-r--r--src/backend/optimizer/plan/planner.c150
-rw-r--r--src/backend/optimizer/plan/setrefs.c23
4 files changed, 145 insertions, 116 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 79d90bf5a3..c20abacca3 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.112 2002/03/12 00:51:45 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.113 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -80,18 +80,18 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
static NestLoop *make_nestloop(List *tlist,
List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype, Index joinrti);
+ JoinType jointype);
static HashJoin *make_hashjoin(List *tlist,
List *joinclauses, List *otherclauses,
List *hashclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype, Index joinrti);
+ JoinType jointype);
static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses,
List *mergeclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype, Index joinrti);
+ JoinType jointype);
/*
* create_plan
@@ -591,7 +591,6 @@ create_nestloop_plan(Query *root,
List *inner_tlist)
{
NestLoop *join_plan;
- Index joinrti = best_path->path.parent->joinrti;
if (IsA(inner_plan, IndexScan))
{
@@ -639,22 +638,19 @@ create_nestloop_plan(Query *root,
root,
outer_tlist,
NIL,
- innerrel,
- joinrti);
+ innerrel);
innerscan->indxqual = join_references(innerscan->indxqual,
root,
outer_tlist,
NIL,
- innerrel,
- joinrti);
+ innerrel);
/* fix the inner qpqual too, if it has join clauses */
if (NumRelids((Node *) inner_plan->qual) > 1)
inner_plan->qual = join_references(inner_plan->qual,
root,
outer_tlist,
NIL,
- innerrel,
- joinrti);
+ innerrel);
}
}
else if (IsA(inner_plan, TidScan))
@@ -665,8 +661,7 @@ create_nestloop_plan(Query *root,
root,
outer_tlist,
inner_tlist,
- innerscan->scan.scanrelid,
- joinrti);
+ innerscan->scan.scanrelid);
}
else if (IsA_Join(inner_plan))
{
@@ -688,22 +683,19 @@ create_nestloop_plan(Query *root,
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- joinrti);
+ (Index) 0);
otherclauses = join_references(otherclauses,
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- joinrti);
+ (Index) 0);
join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
outer_plan,
inner_plan,
- best_path->jointype,
- joinrti);
+ best_path->jointype);
copy_path_costsize(&join_plan->join.plan, &best_path->path);
@@ -723,7 +715,6 @@ create_mergejoin_plan(Query *root,
{
List *mergeclauses;
MergeJoin *join_plan;
- Index joinrti = best_path->jpath.path.parent->joinrti;
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
@@ -736,8 +727,7 @@ create_mergejoin_plan(Query *root,
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- joinrti);
+ (Index) 0);
/*
* Fix the additional qpquals too.
@@ -746,8 +736,7 @@ create_mergejoin_plan(Query *root,
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- joinrti);
+ (Index) 0);
/*
* Now set the references in the mergeclauses and rearrange them so
@@ -757,8 +746,7 @@ create_mergejoin_plan(Query *root,
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- joinrti));
+ (Index) 0));
/*
* Create explicit sort nodes for the outer and inner join paths if
@@ -824,8 +812,7 @@ create_mergejoin_plan(Query *root,
mergeclauses,
outer_plan,
inner_plan,
- best_path->jpath.jointype,
- joinrti);
+ best_path->jpath.jointype);
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
@@ -847,7 +834,6 @@ create_hashjoin_plan(Query *root,
HashJoin *join_plan;
Hash *hash_plan;
Node *innerhashkey;
- Index joinrti = best_path->jpath.path.parent->joinrti;
/*
* NOTE: there will always be exactly one hashclause in the list
@@ -866,8 +852,7 @@ create_hashjoin_plan(Query *root,
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- joinrti);
+ (Index) 0);
/*
* Fix the additional qpquals too.
@@ -876,8 +861,7 @@ create_hashjoin_plan(Query *root,
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- joinrti);
+ (Index) 0);
/*
* Now set the references in the hashclauses and rearrange them so
@@ -887,8 +871,7 @@ create_hashjoin_plan(Query *root,
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- joinrti));
+ (Index) 0));
/* Now the righthand op of the sole hashclause is the inner hash key. */
innerhashkey = (Node *) get_rightop(lfirst(hashclauses));
@@ -903,8 +886,7 @@ create_hashjoin_plan(Query *root,
hashclauses,
outer_plan,
(Plan *) hash_plan,
- best_path->jpath.jointype,
- joinrti);
+ best_path->jpath.jointype);
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
@@ -1363,8 +1345,7 @@ make_nestloop(List *tlist,
List *otherclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype,
- Index joinrti)
+ JoinType jointype)
{
NestLoop *node = makeNode(NestLoop);
Plan *plan = &node->join.plan;
@@ -1377,7 +1358,6 @@ make_nestloop(List *tlist,
plan->righttree = righttree;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
- node->join.joinrti = joinrti;
return node;
}
@@ -1389,8 +1369,7 @@ make_hashjoin(List *tlist,
List *hashclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype,
- Index joinrti)
+ JoinType jointype)
{
HashJoin *node = makeNode(HashJoin);
Plan *plan = &node->join.plan;
@@ -1404,7 +1383,6 @@ make_hashjoin(List *tlist,
node->hashclauses = hashclauses;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
- node->join.joinrti = joinrti;
return node;
}
@@ -1439,8 +1417,7 @@ make_mergejoin(List *tlist,
List *mergeclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype,
- Index joinrti)
+ JoinType jointype)
{
MergeJoin *node = makeNode(MergeJoin);
Plan *plan = &node->join.plan;
@@ -1454,7 +1431,6 @@ make_mergejoin(List *tlist,
node->mergeclauses = mergeclauses;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
- node->join.joinrti = joinrti;
return node;
}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 39ac5ba886..2410651d68 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.68 2002/04/16 23:08:10 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.69 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -173,16 +173,14 @@ add_vars_to_targetlist(Query *root, List *vars)
if (rel->reloptkind == RELOPT_OTHER_JOIN_REL)
{
/* Var is an alias */
- Var *leftsubvar,
- *rightsubvar;
-
- build_join_alias_subvars(root, var,
- &leftsubvar, &rightsubvar);
-
- rel = find_base_rel(root, leftsubvar->varno);
- add_var_to_tlist(rel, leftsubvar);
- rel = find_base_rel(root, rightsubvar->varno);
- add_var_to_tlist(rel, rightsubvar);
+ Node *expansion;
+ List *varsused;
+
+ expansion = flatten_join_alias_vars((Node *) var,
+ root, true);
+ varsused = pull_var_clause(expansion, false);
+ add_vars_to_targetlist(root, varsused);
+ freeList(varsused);
}
}
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 1df2cd2940..5eb17f3b05 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.115 2002/03/12 00:51:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.116 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -41,8 +41,10 @@
#define EXPRKIND_HAVING 2
-static Node *pull_up_subqueries(Query *parse, Node *jtnode);
+static Node *pull_up_subqueries(Query *parse, Node *jtnode,
+ bool below_outer_join);
static bool is_simple_subquery(Query *subquery);
+static bool has_nullable_targetlist(Query *subquery);
static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
static Node *preprocess_jointree(Query *parse, Node *jtnode);
static Node *preprocess_expression(Query *parse, Node *expr, int kind);
@@ -153,7 +155,7 @@ subquery_planner(Query *parse, double tuple_fraction)
* this query.
*/
parse->jointree = (FromExpr *)
- pull_up_subqueries(parse, (Node *) parse->jointree);
+ pull_up_subqueries(parse, (Node *) parse->jointree, false);
/*
* If so, we may have created opportunities to simplify the jointree.
@@ -264,6 +266,9 @@ subquery_planner(Query *parse, double tuple_fraction)
* the parent query. If the subquery has no special features like
* grouping/aggregation then we can merge it into the parent's jointree.
*
+ * below_outer_join is true if this jointree node is within the nullable
+ * side of an outer join. This restricts what we can do.
+ *
* A tricky aspect of this code is that if we pull up a subquery we have
* to replace Vars that reference the subquery's outputs throughout the
* parent query, including quals attached to jointree nodes above the one
@@ -274,7 +279,7 @@ subquery_planner(Query *parse, double tuple_fraction)
* copy of the tree; we have to invoke it just on the quals, instead.
*/
static Node *
-pull_up_subqueries(Query *parse, Node *jtnode)
+pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
{
if (jtnode == NULL)
return NULL;
@@ -288,16 +293,26 @@ pull_up_subqueries(Query *parse, Node *jtnode)
* Is this a subquery RTE, and if so, is the subquery simple
* enough to pull up? (If not, do nothing at this node.)
*
+ * If we are inside an outer join, only pull up subqueries whose
+ * targetlists are nullable --- otherwise substituting their tlist
+ * entries for upper Var references would do the wrong thing
+ * (the results wouldn't become NULL when they're supposed to).
+ * XXX This could be improved by generating pseudo-variables for
+ * such expressions; we'd have to figure out how to get the pseudo-
+ * variables evaluated at the right place in the modified plan tree.
+ * Fix it someday.
+ *
* Note: even if the subquery itself is simple enough, we can't pull
- * it up if there is a reference to its whole tuple result.
+ * it up if there is a reference to its whole tuple result. Perhaps
+ * a pseudo-variable is the answer here too.
*/
- if (subquery && is_simple_subquery(subquery) &&
+ if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(subquery) &&
+ (!below_outer_join || has_nullable_targetlist(subquery)) &&
!contain_whole_tuple_var((Node *) parse, varno, 0))
{
int rtoffset;
- Node *subjointree;
List *subtlist;
- List *l;
+ List *rt;
/*
* First, recursively pull up the subquery's subqueries, so
@@ -311,50 +326,62 @@ pull_up_subqueries(Query *parse, Node *jtnode)
* having chunks of structure multiply linked.
*/
subquery->jointree = (FromExpr *)
- pull_up_subqueries(subquery, (Node *) subquery->jointree);
+ pull_up_subqueries(subquery, (Node *) subquery->jointree,
+ below_outer_join);
/*
- * Append the subquery's rangetable to mine (currently, no
- * adjustments will be needed in the subquery's rtable).
+ * Now make a modifiable copy of the subquery that we can
+ * run OffsetVarNodes on.
*/
- rtoffset = length(parse->rtable);
- parse->rtable = nconc(parse->rtable,
- copyObject(subquery->rtable));
+ subquery = copyObject(subquery);
/*
- * Make copies of the subquery's jointree and targetlist with
- * varnos adjusted to match the merged rangetable.
+ * Adjust varnos in subquery so that we can append its
+ * rangetable to upper query's.
*/
- subjointree = copyObject(subquery->jointree);
- OffsetVarNodes(subjointree, rtoffset, 0);
- subtlist = copyObject(subquery->targetList);
- OffsetVarNodes((Node *) subtlist, rtoffset, 0);
+ rtoffset = length(parse->rtable);
+ OffsetVarNodes((Node *) subquery, rtoffset, 0);
/*
* Replace all of the top query's references to the subquery's
* outputs with copies of the adjusted subtlist items, being
* careful not to replace any of the jointree structure.
+ * (This'd be a lot cleaner if we could use query_tree_mutator.)
*/
+ subtlist = subquery->targetList;
parse->targetList = (List *)
ResolveNew((Node *) parse->targetList,
varno, 0, subtlist, CMD_SELECT, 0);
resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist);
+ Assert(parse->setOperations == NULL);
parse->havingQual =
ResolveNew(parse->havingQual,
varno, 0, subtlist, CMD_SELECT, 0);
- /*
- * Pull up any FOR UPDATE markers, too.
- */
- foreach(l, subquery->rowMarks)
+ foreach(rt, parse->rtable)
{
- int submark = lfirsti(l);
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
- parse->rowMarks = lappendi(parse->rowMarks,
- submark + rtoffset);
+ if (rte->rtekind == RTE_JOIN)
+ rte->joinaliasvars = (List *)
+ ResolveNew((Node *) rte->joinaliasvars,
+ varno, 0, subtlist, CMD_SELECT, 0);
}
/*
+ * Now append the adjusted rtable entries to upper query.
+ * (We hold off until after fixing the upper rtable entries;
+ * no point in running that code on the subquery ones too.)
+ */
+ parse->rtable = nconc(parse->rtable, subquery->rtable);
+
+ /*
+ * Pull up any FOR UPDATE markers, too. (OffsetVarNodes
+ * already adjusted the marker values, so just nconc the list.)
+ */
+ parse->rowMarks = nconc(parse->rowMarks, subquery->rowMarks);
+
+ /*
* Miscellaneous housekeeping.
*/
parse->hasSubLinks |= subquery->hasSubLinks;
@@ -364,7 +391,7 @@ pull_up_subqueries(Query *parse, Node *jtnode)
* Return the adjusted subquery jointree to replace the
* RangeTblRef entry in my jointree.
*/
- return subjointree;
+ return (Node *) subquery->jointree;
}
}
else if (IsA(jtnode, FromExpr))
@@ -373,35 +400,39 @@ pull_up_subqueries(Query *parse, Node *jtnode)
List *l;
foreach(l, f->fromlist)
- lfirst(l) = pull_up_subqueries(parse, lfirst(l));
+ lfirst(l) = pull_up_subqueries(parse, lfirst(l),
+ below_outer_join);
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
- /*
- * At the moment, we can't pull up subqueries that are inside the
- * nullable side of an outer join, because substituting their
- * target list entries for upper Var references wouldn't do the
- * right thing (the entries wouldn't go to NULL when they're
- * supposed to). Suppressing the pullup is an ugly,
- * performance-losing hack, but I see no alternative for now. Find
- * a better way to handle this when we redesign query trees ---
- * tgl 4/30/01.
- */
+ /* Recurse, being careful to tell myself when inside outer join */
switch (j->jointype)
{
case JOIN_INNER:
- j->larg = pull_up_subqueries(parse, j->larg);
- j->rarg = pull_up_subqueries(parse, j->rarg);
+ j->larg = pull_up_subqueries(parse, j->larg,
+ below_outer_join);
+ j->rarg = pull_up_subqueries(parse, j->rarg,
+ below_outer_join);
break;
case JOIN_LEFT:
- j->larg = pull_up_subqueries(parse, j->larg);
+ j->larg = pull_up_subqueries(parse, j->larg,
+ below_outer_join);
+ j->rarg = pull_up_subqueries(parse, j->rarg,
+ true);
break;
case JOIN_FULL:
+ j->larg = pull_up_subqueries(parse, j->larg,
+ true);
+ j->rarg = pull_up_subqueries(parse, j->rarg,
+ true);
break;
case JOIN_RIGHT:
- j->rarg = pull_up_subqueries(parse, j->rarg);
+ j->larg = pull_up_subqueries(parse, j->larg,
+ true);
+ j->rarg = pull_up_subqueries(parse, j->rarg,
+ below_outer_join);
break;
case JOIN_UNION:
@@ -485,6 +516,37 @@ is_simple_subquery(Query *subquery)
}
/*
+ * has_nullable_targetlist
+ * Check a subquery in the range table to see if all the non-junk
+ * targetlist items are simple variables (and, hence, will correctly
+ * go to NULL when examined above the point of an outer join).
+ *
+ * A possible future extension is to accept strict functions of simple
+ * variables, eg, "x + 1".
+ */
+static bool
+has_nullable_targetlist(Query *subquery)
+{
+ List *l;
+
+ foreach(l, subquery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+ /* ignore resjunk columns */
+ if (tle->resdom->resjunk)
+ continue;
+
+ /* Okay if tlist item is a simple Var */
+ if (tle->expr && IsA(tle->expr, Var))
+ continue;
+
+ return false;
+ }
+ return true;
+}
+
+/*
* Helper routine for pull_up_subqueries: do ResolveNew on every expression
* in the jointree, without changing the jointree structure itself. Ugly,
* but there's no other way...
@@ -675,7 +737,7 @@ preprocess_expression(Query *parse, Node *expr, int kind)
}
}
if (has_join_rtes)
- expr = flatten_join_alias_vars(expr, parse, 0);
+ expr = flatten_join_alias_vars(expr, parse, false);
return expr;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 2f48821ece..807876e4a7 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.74 2002/03/12 00:51:48 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.75 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -31,7 +31,6 @@ typedef struct
List *outer_tlist;
List *inner_tlist;
Index acceptable_rel;
- Index join_rti;
} join_references_context;
typedef struct
@@ -271,8 +270,7 @@ set_join_references(Query *root, Join *join)
root,
outer_tlist,
inner_tlist,
- (Index) 0,
- join->joinrti);
+ (Index) 0);
}
/*
@@ -367,8 +365,6 @@ set_uppernode_references(Plan *plan, Index subvarno)
* 'inner_tlist' is the target list of the inner join relation, or NIL
* 'acceptable_rel' is either zero or the rangetable index of a relation
* whose Vars may appear in the clause without provoking an error.
- * 'join_rti' is either zero or the join RTE index of join alias variables
- * that should be expanded.
*
* Returns the new expression tree. The original clause structure is
* not modified.
@@ -378,8 +374,7 @@ join_references(List *clauses,
Query *root,
List *outer_tlist,
List *inner_tlist,
- Index acceptable_rel,
- Index join_rti)
+ Index acceptable_rel)
{
join_references_context context;
@@ -387,7 +382,6 @@ join_references(List *clauses,
context.outer_tlist = outer_tlist;
context.inner_tlist = inner_tlist;
context.acceptable_rel = acceptable_rel;
- context.join_rti = join_rti;
return (List *) join_references_mutator((Node *) clauses, &context);
}
@@ -401,6 +395,7 @@ join_references_mutator(Node *node,
{
Var *var = (Var *) node;
Resdom *resdom;
+ Node *newnode;
/* First look for the var in the input tlists */
resdom = tlist_member((Node *) var, context->outer_tlist);
@@ -423,13 +418,11 @@ join_references_mutator(Node *node,
}
/* Perhaps it's a join alias that can be resolved to input vars? */
- if (var->varno == context->join_rti)
+ newnode = flatten_join_alias_vars((Node *) var,
+ context->root,
+ true);
+ if (!equal(newnode, (Node *) var))
{
- Node *newnode;
-
- newnode = flatten_join_alias_vars((Node *) var,
- context->root,
- context->join_rti);
/* Must now resolve the input vars... */
newnode = join_references_mutator(newnode, context);
return newnode;