Commit 6c598869 authored by Tom Lane's avatar Tom Lane

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.
parent c8996f9c
......@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.181 2002/04/24 02:48:54 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.182 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -323,7 +323,6 @@ CopyJoinFields(Join *from, Join *newnode)
{
newnode->jointype = from->jointype;
Node_Copy(from, newnode, joinqual);
newnode->joinrti = from->joinrti;
/* subPlan list must point to subplans in the new subtree, not the old */
if (from->plan.subPlan != NIL)
newnode->plan.subPlan = nconc(newnode->plan.subPlan,
......@@ -1475,10 +1474,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
newnode->relid = from->relid;
Node_Copy(from, newnode, subquery);
newnode->jointype = from->jointype;
newnode->joincoltypes = listCopy(from->joincoltypes);
newnode->joincoltypmods = listCopy(from->joincoltypmods);
newnode->joinleftcols = listCopy(from->joinleftcols);
newnode->joinrightcols = listCopy(from->joinrightcols);
Node_Copy(from, newnode, joinaliasvars);
Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, eref);
newnode->inh = from->inh;
......
......@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.129 2002/04/24 02:48:54 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.130 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1680,13 +1680,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
return false;
if (a->jointype != b->jointype)
return false;
if (!equali(a->joincoltypes, b->joincoltypes))
return false;
if (!equali(a->joincoltypmods, b->joincoltypmods))
return false;
if (!equali(a->joinleftcols, b->joinleftcols))
return false;
if (!equali(a->joinrightcols, b->joinrightcols))
if (!equal(a->joinaliasvars, b->joinaliasvars))
return false;
if (!equal(a->alias, b->alias))
return false;
......
......@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.156 2002/04/17 20:57:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.157 2002/04/28 19:54:28 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
......@@ -408,8 +408,6 @@ _outJoin(StringInfo str, Join *node)
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->jointype);
_outNode(str, node->joinqual);
appendStringInfo(str, " :joinrti %d ",
node->joinrti);
}
/*
......@@ -423,8 +421,6 @@ _outNestLoop(StringInfo str, NestLoop *node)
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
appendStringInfo(str, " :joinrti %d ",
node->join.joinrti);
}
/*
......@@ -438,8 +434,6 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
appendStringInfo(str, " :joinrti %d ",
node->join.joinrti);
appendStringInfo(str, " :mergeclauses ");
_outNode(str, node->mergeclauses);
......@@ -456,8 +450,6 @@ _outHashJoin(StringInfo str, HashJoin *node)
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
appendStringInfo(str, " :joinrti %d ",
node->join.joinrti);
appendStringInfo(str, " :hashclauses ");
_outNode(str, node->hashclauses);
......@@ -982,22 +974,16 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
{
case RTE_RELATION:
case RTE_SPECIAL:
appendStringInfo(str, ":relid %u ", node->relid);
appendStringInfo(str, ":relid %u", node->relid);
break;
case RTE_SUBQUERY:
appendStringInfo(str, ":subquery ");
_outNode(str, node->subquery);
break;
case RTE_JOIN:
appendStringInfo(str, ":jointype %d :joincoltypes ",
appendStringInfo(str, ":jointype %d :joinaliasvars ",
(int) node->jointype);
_outOidList(str, node->joincoltypes);
appendStringInfo(str, " :joincoltypmods ");
_outIntList(str, node->joincoltypmods);
appendStringInfo(str, " :joinleftcols ");
_outIntList(str, node->joinleftcols);
appendStringInfo(str, " :joinrightcols ");
_outIntList(str, node->joinrightcols);
_outNode(str, node->joinaliasvars);
break;
default:
elog(ERROR, "bogus rte kind %d", (int) node->rtekind);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.119 2002/04/11 20:00:00 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.120 2002/04/28 19:54:28 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
......@@ -421,10 +421,6 @@ _getJoin(Join *node)
token = pg_strtok(&length); /* skip the :joinqual */
node->joinqual = nodeRead(true); /* get the joinqual */
token = pg_strtok(&length); /* skip the :joinrti */
token = pg_strtok(&length); /* get the joinrti */
node->joinrti = atoi(token);
}
......@@ -1523,17 +1519,8 @@ _readRangeTblEntry(void)
token = pg_strtok(&length); /* get jointype */
local_node->jointype = (JoinType) atoi(token);
token = pg_strtok(&length); /* eat :joincoltypes */
local_node->joincoltypes = toOidList(nodeRead(true));
token = pg_strtok(&length); /* eat :joincoltypmods */
local_node->joincoltypmods = toIntList(nodeRead(true));
token = pg_strtok(&length); /* eat :joinleftcols */
local_node->joinleftcols = toIntList(nodeRead(true));
token = pg_strtok(&length); /* eat :joinrightcols */
local_node->joinrightcols = toIntList(nodeRead(true));
token = pg_strtok(&length); /* eat :joinaliasvars */
local_node->joinaliasvars = nodeRead(true); /* now read it */
break;
default:
......
......@@ -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;
}
......
......@@ -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);
}
}
}
......
......@@ -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,49 +326,61 @@ 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.
*/
......@@ -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:
......@@ -484,6 +515,37 @@ is_simple_subquery(Query *subquery)
return true;
}
/*
* 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,
......@@ -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;
}
......
......@@ -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;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.96 2002/04/05 00:31:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.97 2002/04/28 19:54:28 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -1902,6 +1902,8 @@ query_tree_walker(Query *query,
void *context,
bool visitQueryRTEs)
{
List *rt;
Assert(query != NULL && IsA(query, Query));
if (walker((Node *) query->targetList, context))
......@@ -1912,17 +1914,25 @@ query_tree_walker(Query *query,
return true;
if (walker(query->havingQual, context))
return true;
if (visitQueryRTEs)
foreach(rt, query->rtable)
{
List *rt;
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
foreach(rt, query->rtable)
switch (rte->rtekind)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
if (rte->subquery)
if (walker(rte->subquery, context))
case RTE_RELATION:
case RTE_SPECIAL:
/* nothing to do */
break;
case RTE_SUBQUERY:
if (visitQueryRTEs)
if (walker(rte->subquery, context))
return true;
break;
case RTE_JOIN:
if (walker(rte->joinaliasvars, context))
return true;
break;
}
}
return false;
......@@ -2281,32 +2291,42 @@ query_tree_mutator(Query *query,
void *context,
bool visitQueryRTEs)
{
List *newrt = NIL;
List *rt;
Assert(query != NULL && IsA(query, Query));
MUTATE(query->targetList, query->targetList, List *);
MUTATE(query->jointree, query->jointree, FromExpr *);
MUTATE(query->setOperations, query->setOperations, Node *);
MUTATE(query->havingQual, query->havingQual, Node *);
if (visitQueryRTEs)
foreach(rt, query->rtable)
{
List *newrt = NIL;
List *rt;
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
RangeTblEntry *newrte;
foreach(rt, query->rtable)
switch (rte->rtekind)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
if (rte->subquery)
{
RangeTblEntry *newrte;
case RTE_RELATION:
case RTE_SPECIAL:
/* nothing to do, don't bother to make a copy */
break;
case RTE_SUBQUERY:
if (visitQueryRTEs)
{
FLATCOPY(newrte, rte, RangeTblEntry);
CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
MUTATE(newrte->subquery, newrte->subquery, Query *);
rte = newrte;
}
break;
case RTE_JOIN:
FLATCOPY(newrte, rte, RangeTblEntry);
CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
MUTATE(newrte->subquery, newrte->subquery, Query *);
MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
rte = newrte;
}
newrt = lappend(newrt, rte);
break;
}
query->rtable = newrt;
newrt = lappend(newrt, rte);
}
query->rtable = newrt;
}
This diff is collapsed.
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.232 2002/04/24 02:22:54 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.233 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -2075,7 +2075,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
Node *node;
List *lefttl,
*dtlist,
*colMods,
*targetvars,
*targetnames,
*sv_namespace,
*sv_rtable;
......@@ -2145,14 +2145,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/*
* Generate dummy targetlist for outer query using column names of
* leftmost select and common datatypes of topmost set operation. Also
* make a list of the column names for use in parsing ORDER BY.
*
* XXX colMods is a hack to provide a dummy typmod list below. We
* should probably keep track of common typmod instead.
* make lists of the dummy vars and their names for use in parsing
* ORDER BY.
*/
qry->targetList = NIL;
targetvars = NIL;
targetnames = NIL;
colMods = NIL;
lefttl = leftmostQuery->targetList;
foreach(dtlist, sostmt->colTypes)
{
......@@ -2174,8 +2172,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
0);
qry->targetList = lappend(qry->targetList,
makeTargetEntry(resdom, expr));
targetvars = lappend(targetvars, expr);
targetnames = lappend(targetnames, makeString(colName));
colMods = lappendi(colMods, -1);
lefttl = lnext(lefttl);
}
......@@ -2232,10 +2230,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
jrte = addRangeTableEntryForJoin(NULL,
targetnames,
JOIN_INNER,
sostmt->colTypes,
colMods,
NIL,
NIL,
targetvars,
NULL,
true);
jrtr = makeNode(RangeTblRef);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.89 2002/04/16 23:08:11 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.90 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -39,7 +39,7 @@
static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};
static void extractUniqueColumns(List *common_colnames,
static void extractRemainingColumns(List *common_colnames,
List *src_colnames, List *src_colvars,
List **res_colnames, List **res_colvars);
static Node *transformJoinUsingClause(ParseState *pstate,
......@@ -51,6 +51,8 @@ static RangeTblRef *transformRangeSubselect(ParseState *pstate,
RangeSubselect *r);
static Node *transformFromClauseItem(ParseState *pstate, Node *n,
List **containedRels);
static Node *buildMergedJoinVar(JoinType jointype,
Var *l_colvar, Var *r_colvar);
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
List *tlist, int clause);
static List *addTargetToSortList(TargetEntry *tle, List *sortlist,
......@@ -194,9 +196,9 @@ interpretInhOption(InhOption inhOpt)
* Extract all not-in-common columns from column lists of a source table
*/
static void
extractUniqueColumns(List *common_colnames,
List *src_colnames, List *src_colvars,
List **res_colnames, List **res_colvars)
extractRemainingColumns(List *common_colnames,
List *src_colnames, List *src_colvars,
List **res_colnames, List **res_colvars)
{
List *new_colnames = NIL;
List *new_colvars = NIL;
......@@ -496,10 +498,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
*res_colnames,
*l_colvars,
*r_colvars,
*coltypes,
*coltypmods,
*leftcolnos,
*rightcolnos;
*res_colvars;
Index leftrti,
rightrti;
RangeTblEntry *rte;
......@@ -597,17 +596,14 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
* Now transform the join qualifications, if any.
*/
res_colnames = NIL;
coltypes = NIL;
coltypmods = NIL;
leftcolnos = NIL;
rightcolnos = NIL;
res_colvars = NIL;
if (j->using)
{
/*
* JOIN/USING (or NATURAL JOIN, as transformed above).
* Transform the list into an explicit ON-condition, and
* generate a list of result columns.
* generate a list of merged result columns.
*/
List *ucols = j->using;
List *l_usingvars = NIL;
......@@ -620,14 +616,22 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
{
char *u_colname = strVal(lfirst(ucol));
List *col;
Var *l_colvar,
*r_colvar;
Oid outcoltype;
int32 outcoltypmod;
int ndx;
int l_index = -1;
int r_index = -1;
Var *l_colvar,
*r_colvar;
/* Check for USING(foo,foo) */
foreach(col, res_colnames)
{
char *res_colname = strVal(lfirst(col));
if (strcmp(res_colname, u_colname) == 0)
elog(ERROR, "USING column name \"%s\" appears more than once", u_colname);
}
/* Find it in left input */
ndx = 0;
foreach(col, l_colnames)
{
......@@ -645,6 +649,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
elog(ERROR, "JOIN/USING column \"%s\" not found in left table",
u_colname);
/* Find it in right input */
ndx = 0;
foreach(col, r_colnames)
{
......@@ -667,30 +672,11 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
r_colvar = nth(r_index, r_colvars);
r_usingvars = lappend(r_usingvars, r_colvar);
res_colnames = lappend(res_colnames,
nth(l_index, l_colnames));
/*
* Choose output type if input types are dissimilar.
*/
outcoltype = l_colvar->vartype;
outcoltypmod = l_colvar->vartypmod;
if (outcoltype != r_colvar->vartype)
{
outcoltype =
select_common_type(makeListi2(l_colvar->vartype,
r_colvar->vartype),
"JOIN/USING");
outcoltypmod = -1; /* ie, unknown */
}
else if (outcoltypmod != r_colvar->vartypmod)
{
/* same type, but not same typmod */
outcoltypmod = -1; /* ie, unknown */
}
coltypes = lappendi(coltypes, outcoltype);
coltypmods = lappendi(coltypmods, outcoltypmod);
leftcolnos = lappendi(leftcolnos, l_index+1);
rightcolnos = lappendi(rightcolnos, r_index+1);
res_colnames = lappend(res_colnames, lfirst(ucol));
res_colvars = lappend(res_colvars,
buildMergedJoinVar(j->jointype,
l_colvar,
r_colvar));
}
j->quals = transformJoinUsingClause(pstate,
......@@ -708,34 +694,16 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
}
/* Add remaining columns from each side to the output columns */
extractUniqueColumns(res_colnames,
l_colnames, l_colvars,
&l_colnames, &l_colvars);
extractUniqueColumns(res_colnames,
r_colnames, r_colvars,
&r_colnames, &r_colvars);
extractRemainingColumns(res_colnames,
l_colnames, l_colvars,
&l_colnames, &l_colvars);
extractRemainingColumns(res_colnames,
r_colnames, r_colvars,
&r_colnames, &r_colvars);
res_colnames = nconc(res_colnames, l_colnames);
while (l_colvars)
{
Var *l_var = (Var *) lfirst(l_colvars);
coltypes = lappendi(coltypes, l_var->vartype);
coltypmods = lappendi(coltypmods, l_var->vartypmod);
leftcolnos = lappendi(leftcolnos, l_var->varattno);
rightcolnos = lappendi(rightcolnos, 0);
l_colvars = lnext(l_colvars);
}
res_colvars = nconc(res_colvars, l_colvars);
res_colnames = nconc(res_colnames, r_colnames);
while (r_colvars)
{
Var *r_var = (Var *) lfirst(r_colvars);
coltypes = lappendi(coltypes, r_var->vartype);
coltypmods = lappendi(coltypmods, r_var->vartypmod);
leftcolnos = lappendi(leftcolnos, 0);
rightcolnos = lappendi(rightcolnos, r_var->varattno);
r_colvars = lnext(r_colvars);
}
res_colvars = nconc(res_colvars, r_colvars);
/*
* Check alias (AS clause), if any.
......@@ -753,11 +721,12 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
/*
* Now build an RTE for the result of the join
*/
rte = addRangeTableEntryForJoin(pstate, res_colnames,
rte = addRangeTableEntryForJoin(pstate,
res_colnames,
j->jointype,
coltypes, coltypmods,
leftcolnos, rightcolnos,
j->alias, true);
res_colvars,
j->alias,
true);
/* assume new rte is at end */
j->rtindex = length(pstate->p_rtable);
......@@ -777,6 +746,115 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
* quiet */
}
/*
* buildMergedJoinVar -
* generate a suitable replacement expression for a merged join column
*/
static Node *
buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar)
{
Oid outcoltype;
int32 outcoltypmod;
Node *l_node,
*r_node,
*res_node;
/*
* Choose output type if input types are dissimilar.
*/
outcoltype = l_colvar->vartype;
outcoltypmod = l_colvar->vartypmod;
if (outcoltype != r_colvar->vartype)
{
outcoltype = select_common_type(makeListi2(l_colvar->vartype,
r_colvar->vartype),
"JOIN/USING");
outcoltypmod = -1; /* ie, unknown */
}
else if (outcoltypmod != r_colvar->vartypmod)
{
/* same type, but not same typmod */
outcoltypmod = -1; /* ie, unknown */
}
/*
* Insert coercion functions if needed. Note that a difference in
* typmod can only happen if input has typmod but outcoltypmod is -1.
* In that case we insert a RelabelType to clearly mark that result's
* typmod is not same as input.
*/
if (l_colvar->vartype != outcoltype)
l_node = coerce_type(NULL, (Node *) l_colvar, l_colvar->vartype,
outcoltype, outcoltypmod, false);
else if (l_colvar->vartypmod != outcoltypmod)
l_node = (Node *) makeRelabelType((Node *) l_colvar,
outcoltype, outcoltypmod);
else
l_node = (Node *) l_colvar;
if (r_colvar->vartype != outcoltype)
r_node = coerce_type(NULL, (Node *) r_colvar, r_colvar->vartype,
outcoltype, outcoltypmod, false);
else if (r_colvar->vartypmod != outcoltypmod)
r_node = (Node *) makeRelabelType((Node *) r_colvar,
outcoltype, outcoltypmod);
else
r_node = (Node *) r_colvar;
/*
* Choose what to emit
*/
switch (jointype)
{
case JOIN_INNER:
/*
* We can use either var; prefer non-coerced one if available.
*/
if (IsA(l_node, Var))
res_node = l_node;
else if (IsA(r_node, Var))
res_node = r_node;
else
res_node = l_node;
break;
case JOIN_LEFT:
/* Always use left var */
res_node = l_node;
break;
case JOIN_RIGHT:
/* Always use right var */
res_node = r_node;
break;
case JOIN_FULL:
{
/*
* Here we must build a COALESCE expression to ensure that
* the join output is non-null if either input is.
*/
CaseExpr *c = makeNode(CaseExpr);
CaseWhen *w = makeNode(CaseWhen);
NullTest *n = makeNode(NullTest);
n->arg = l_node;
n->nulltesttype = IS_NOT_NULL;
w->expr = (Node *) n;
w->result = l_node;
c->casetype = outcoltype;
c->args = makeList1(w);
c->defresult = r_node;
res_node = (Node *) c;
break;
}
default:
elog(ERROR, "buildMergedJoinVar: unexpected jointype %d",
(int) jointype);
res_node = NULL; /* keep compiler quiet */
break;
}
return res_node;
}
/*
* transformWhereClause -
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.67 2002/04/02 08:51:51 inoue Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.68 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -681,10 +681,7 @@ RangeTblEntry *
addRangeTableEntryForJoin(ParseState *pstate,
List *colnames,
JoinType jointype,
List *coltypes,
List *coltypmods,
List *leftcols,
List *rightcols,
List *aliasvars,
Alias *alias,
bool inFromCl)
{
......@@ -696,10 +693,7 @@ addRangeTableEntryForJoin(ParseState *pstate,
rte->relid = InvalidOid;
rte->subquery = NULL;
rte->jointype = jointype;
rte->joincoltypes = coltypes;
rte->joincoltypmods = coltypmods;
rte->joinleftcols = leftcols;
rte->joinrightcols = rightcols;
rte->joinaliasvars = aliasvars;
rte->alias = alias;
eref = alias ? (Alias *) copyObject(alias) : makeAlias("unnamed_join", NIL);
......@@ -922,13 +916,12 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{
/* Join RTE */
List *aliasp = rte->eref->colnames;
List *coltypes = rte->joincoltypes;
List *coltypmods = rte->joincoltypmods;
List *aliasvars = rte->joinaliasvars;
varattno = 0;
while (aliasp)
{
Assert(coltypes && coltypmods);
Assert(aliasvars);
varattno++;
if (colnames)
......@@ -940,21 +933,21 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
if (colvars)
{
Node *aliasvar = (Node *) lfirst(aliasvars);
Var *varnode;
varnode = makeVar(rtindex, varattno,
(Oid) lfirsti(coltypes),
(int32) lfirsti(coltypmods),
exprType(aliasvar),
exprTypmod(aliasvar),
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
aliasp = lnext(aliasp);
coltypes = lnext(coltypes);
coltypmods = lnext(coltypmods);
aliasvars = lnext(aliasvars);
}
Assert(coltypes == NIL && coltypmods == NIL);
Assert(aliasvars == NIL);
}
else
elog(ERROR, "expandRTE: unsupported RTE kind %d",
......@@ -1091,10 +1084,13 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
}
else if (rte->rtekind == RTE_JOIN)
{
/* Join RTE --- get type info directly from join RTE */
Assert(attnum > 0 && attnum <= length(rte->joincoltypes));
*vartype = (Oid) nthi(attnum-1, rte->joincoltypes);
*vartypmod = nthi(attnum-1, rte->joincoltypmods);
/* Join RTE --- get type info from join RTE's alias variable */
Node *aliasvar;
Assert(attnum > 0 && attnum <= length(rte->joinaliasvars));
aliasvar = (Node *) nth(attnum-1, rte->joinaliasvars);
*vartype = exprType(aliasvar);
*vartypmod = exprTypmod(aliasvar);
}
else
elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
......
......@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.128 2002/04/27 21:24:34 tgl Exp $
* $Id: catversion.h,v 1.129 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200204271
#define CATALOG_VERSION_NO 200204281
#endif
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.174 2002/04/24 02:48:55 momjian Exp $
* $Id: parsenodes.h,v 1.175 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -510,17 +510,14 @@ typedef struct RangeTblEntry
/*
* Fields valid for a join RTE (else NULL/zero):
*
* joincoltypes/joincoltypmods identify the column datatypes of the
* join result. joinleftcols and joinrightcols identify the source
* columns from the join's inputs: each entry is either a source column
* AttrNumber or zero. For normal columns exactly one is nonzero,
* but both are nonzero for a column "merged" by USING or NATURAL.
* joinaliasvars is a list of Vars or COALESCE expressions corresponding
* to the columns of the join result. An alias Var referencing column
* K of the join result can be replaced by the K'th element of
* joinaliasvars --- but to simplify the task of reverse-listing aliases
* correctly, we do not do that until planning time.
*/
JoinType jointype; /* type of join */
List *joincoltypes; /* integer list of column type OIDs */
List *joincoltypmods; /* integer list of column typmods */
List *joinleftcols; /* integer list of left-side column #s */
List *joinrightcols; /* integer list of right-side column #s */
List *joinaliasvars; /* list of alias-var expansions */
/*
* Fields valid in all RTEs:
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: plannodes.h,v 1.54 2002/03/12 00:52:01 tgl Exp $
* $Id: plannodes.h,v 1.55 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -254,7 +254,6 @@ typedef struct SubqueryScan
* jointype: rule for joining tuples from left and right subtrees
* joinqual: qual conditions that came from JOIN/ON or JOIN/USING
* (plan.qual contains conditions that came from WHERE)
* joinrti: rtable index of corresponding JOIN RTE, if any (0 if none)
*
* When jointype is INNER, joinqual and plan.qual are semantically
* interchangeable. For OUTER jointypes, the two are *not* interchangeable;
......@@ -263,8 +262,6 @@ typedef struct SubqueryScan
* (But plan.qual is still applied before actually returning a tuple.)
* For an outer join, only joinquals are allowed to be used as the merge
* or hash condition of a merge or hash join.
*
* joinrti is for the convenience of setrefs.c; it's not used in execution.
* ----------------
*/
typedef struct Join
......@@ -272,7 +269,6 @@ typedef struct Join
Plan plan;
JoinType jointype;
List *joinqual; /* JOIN quals (in addition to plan.qual) */
Index joinrti; /* JOIN RTE, if any */
} Join;
/* ----------------
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: planmain.h,v 1.55 2002/03/12 00:52:03 tgl Exp $
* $Id: planmain.h,v 1.56 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -59,7 +59,7 @@ extern void process_implied_equality(Query *root, Node *item1, Node *item2,
extern void set_plan_references(Query *root, Plan *plan);
extern List *join_references(List *clauses, Query *root,
List *outer_tlist, List *inner_tlist,
Index acceptable_rel, Index join_rti);
Index acceptable_rel);
extern void fix_opids(Node *node);
/*
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: var.h,v 1.18 2002/03/12 00:52:04 tgl Exp $
* $Id: var.h,v 1.19 2002/04/28 19:54:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -23,8 +23,6 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
extern bool contain_var_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars);
extern Node *flatten_join_alias_vars(Node *node, Query *root, int expandRTI);
extern void build_join_alias_subvars(Query *root, Var *aliasvar,
Var **leftsubvar, Var **rightsubvar);
extern Node *flatten_join_alias_vars(Node *node, Query *root, bool force);
#endif /* VAR_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_relation.h,v 1.31 2002/03/22 02:56:37 tgl Exp $
* $Id: parse_relation.h,v 1.32 2002/04/28 19:54:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -44,10 +44,7 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
List *colnames,
JoinType jointype,
List *coltypes,
List *coltypmods,
List *leftcols,
List *rightcols,
List *aliasvars,
Alias *alias,
bool inFromCl);
extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
......
......@@ -1868,6 +1868,138 @@ SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name);
dd | | 42 |
(4 rows)
--
-- Test interactions of join syntax and subqueries
--
-- Basic cases (we expect planner to pull up the subquery here)
SELECT * FROM
(SELECT * FROM t2) as s2
INNER JOIN
(SELECT * FROM t3) s3
USING (name);
name | n | n
------+----+----
aa | 12 | 13
bb | 22 | 23
(2 rows)
SELECT * FROM
(SELECT * FROM t2) as s2
LEFT JOIN
(SELECT * FROM t3) s3
USING (name);
name | n | n
------+----+----
aa | 12 | 13
bb | 22 | 23
dd | 42 |
(3 rows)
SELECT * FROM
(SELECT * FROM t2) as s2
FULL JOIN
(SELECT * FROM t3) s3
USING (name);
name | n | n
------+----+----
aa | 12 | 13
bb | 22 | 23
cc | | 33
dd | 42 |
(4 rows)
-- Cases with non-nullable expressions in subquery results;
-- make sure these go to null as expected
SELECT * FROM
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL INNER JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
name | s2_n | s2_2 | s3_n | s3_2
------+------+------+------+------
aa | 12 | 2 | 13 | 3
bb | 22 | 2 | 23 | 3
(2 rows)
SELECT * FROM
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL LEFT JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
name | s2_n | s2_2 | s3_n | s3_2
------+------+------+------+------
aa | 12 | 2 | 13 | 3
bb | 22 | 2 | 23 | 3
dd | 42 | 2 | |
(3 rows)
SELECT * FROM
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL FULL JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
name | s2_n | s2_2 | s3_n | s3_2
------+------+------+------+------
aa | 12 | 2 | 13 | 3
bb | 22 | 2 | 23 | 3
cc | | | 33 | 3
dd | 42 | 2 | |
(4 rows)
SELECT * FROM
(SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1
NATURAL INNER JOIN
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL INNER JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
name | s1_n | s1_1 | s2_n | s2_2 | s3_n | s3_2
------+------+------+------+------+------+------
aa | 11 | 1 | 12 | 2 | 13 | 3
(1 row)
SELECT * FROM
(SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1
NATURAL FULL JOIN
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL FULL JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
name | s1_n | s1_1 | s2_n | s2_2 | s3_n | s3_2
------+------+------+------+------+------+------
aa | 11 | 1 | 12 | 2 | 13 | 3
bb | | | 22 | 2 | 23 | 3
cc | | | | | 33 | 3
dd | | | 42 | 2 | |
(4 rows)
SELECT * FROM
(SELECT name, n as s1_n FROM t1) as s1
NATURAL FULL JOIN
(SELECT * FROM
(SELECT name, n as s2_n FROM t2) as s2
NATURAL FULL JOIN
(SELECT name, n as s3_n FROM t3) as s3
) ss2;
name | s1_n | s2_n | s3_n
------+------+------+------
aa | 11 | 12 | 13
bb | | 22 | 23
cc | | | 33
dd | | 42 |
(4 rows)
SELECT * FROM
(SELECT name, n as s1_n FROM t1) as s1
NATURAL FULL JOIN
(SELECT * FROM
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL FULL JOIN
(SELECT name, n as s3_n FROM t3) as s3
) ss2;
name | s1_n | s2_n | s2_2 | s3_n
------+------+------+------+------
aa | 11 | 12 | 2 | 13
bb | | 22 | 2 | 23
cc | | | | 33
dd | | 42 | 2 |
(4 rows)
--
-- Clean up
--
......
......@@ -216,6 +216,78 @@ INSERT INTO t3 VALUES ( 'cc', 33 );
SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name);
--
-- Test interactions of join syntax and subqueries
--
-- Basic cases (we expect planner to pull up the subquery here)
SELECT * FROM
(SELECT * FROM t2) as s2
INNER JOIN
(SELECT * FROM t3) s3
USING (name);
SELECT * FROM
(SELECT * FROM t2) as s2
LEFT JOIN
(SELECT * FROM t3) s3
USING (name);
SELECT * FROM
(SELECT * FROM t2) as s2
FULL JOIN
(SELECT * FROM t3) s3
USING (name);
-- Cases with non-nullable expressions in subquery results;
-- make sure these go to null as expected
SELECT * FROM
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL INNER JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
SELECT * FROM
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL LEFT JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
SELECT * FROM
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL FULL JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
SELECT * FROM
(SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1
NATURAL INNER JOIN
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL INNER JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
SELECT * FROM
(SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1
NATURAL FULL JOIN
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL FULL JOIN
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
SELECT * FROM
(SELECT name, n as s1_n FROM t1) as s1
NATURAL FULL JOIN
(SELECT * FROM
(SELECT name, n as s2_n FROM t2) as s2
NATURAL FULL JOIN
(SELECT name, n as s3_n FROM t3) as s3
) ss2;
SELECT * FROM
(SELECT name, n as s1_n FROM t1) as s1
NATURAL FULL JOIN
(SELECT * FROM
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
NATURAL FULL JOIN
(SELECT name, n as s3_n FROM t3) as s3
) ss2;
--
-- Clean up
--
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment