summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-04-28 19:54:29 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-04-28 19:54:29 +0000
commit6c5988694218a62c6bc90fc625cbc64f732520cc (patch)
treecdc64472760a6ecbf73e2334bf23ae0767bf2f21 /src/backend/optimizer/plan
parentc8996f9c6bd82765849da85a9cde5de27f8cae79 (diff)
downloadpostgresql-6c5988694218a62c6bc90fc625cbc64f732520cc.tar.gz
Second try at fixing join alias variables. Instead of attaching miscellaneous
lists to join RTEs, attach a list of Vars and COALESCE expressions that will replace the join's alias variables during planning. This simplifies flatten_join_alias_vars while still making it easy to fix up varno references when transforming the query tree. Add regression test cases for interactions of subqueries with outer joins.
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;