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

Restructure representation of join alias variables. An explicit JOIN

now has an RTE of its own, and references to its outputs now are Vars
referencing the JOIN RTE, rather than CASE-expressions.  This allows
reverse-listing in ruleutils.c to use the correct alias easily, rather
than painfully reverse-engineering the alias namespace as it used to do.
Also, nested FULL JOINs work correctly, because the result of the inner
joins are simple Vars that the planner can cope with.  This fixes a bug
reported a couple times now, notably by Tatsuo on 18-Nov-01.  The alias
Vars are expanded into COALESCE expressions where needed at the very end
of planning, rather than during parsing.
Also, beginnings of support for showing plan qualifier expressions in
EXPLAIN.  There are probably still cases that need work.
initdb forced due to change of stored-rule representation.
parent 66b6bf67
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.70 2002/03/06 06:09:33 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.71 2002/03/12 00:51:35 tgl Exp $
* *
*/ */
...@@ -15,12 +15,15 @@ ...@@ -15,12 +15,15 @@
#include "executor/instrument.h" #include "executor/instrument.h"
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
#include "nodes/print.h" #include "nodes/print.h"
#include "optimizer/clauses.h"
#include "optimizer/planner.h" #include "optimizer/planner.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h" #include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h" #include "tcop/pquery.h"
#include "utils/builtins.h"
#include "utils/relcache.h" #include "utils/relcache.h"
typedef struct ExplainState typedef struct ExplainState
{ {
/* options */ /* options */
...@@ -32,6 +35,14 @@ typedef struct ExplainState ...@@ -32,6 +35,14 @@ typedef struct ExplainState
static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es); static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest); static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest);
static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
int scanrelid,
StringInfo str, int indent, ExplainState *es);
static void show_upper_qual(List *qual, const char *qlabel,
const char *outer_name, int outer_varno, Plan *outer_plan,
const char *inner_name, int inner_varno, Plan *inner_plan,
StringInfo str, int indent, ExplainState *es);
static Node *make_ors_ands_explicit(List *orclauses);
/* Convert a null string pointer into "<>" */ /* Convert a null string pointer into "<>" */
#define stringStringInfo(s) (((s) == NULL) ? "<>" : (s)) #define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
...@@ -40,7 +51,6 @@ static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDes ...@@ -40,7 +51,6 @@ static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDes
/* /*
* ExplainQuery - * ExplainQuery -
* print out the execution plan for a given query * print out the execution plan for a given query
*
*/ */
void void
ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest) ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
...@@ -81,7 +91,6 @@ ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest) ...@@ -81,7 +91,6 @@ ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
/* /*
* ExplainOneQuery - * ExplainOneQuery -
* print out the execution plan for one query * print out the execution plan for one query
*
*/ */
static void static void
ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest) ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
...@@ -176,9 +185,6 @@ ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest) ...@@ -176,9 +185,6 @@ ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
pfree(es); pfree(es);
} }
/*****************************************************************************
*
*****************************************************************************/
/* /*
* explain_outNode - * explain_outNode -
...@@ -341,6 +347,90 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) ...@@ -341,6 +347,90 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
} }
appendStringInfo(str, "\n"); appendStringInfo(str, "\n");
/* quals */
switch (nodeTag(plan))
{
case T_IndexScan:
show_scan_qual(((IndexScan *) plan)->indxqualorig, true,
"indxqual",
((Scan *) plan)->scanrelid,
str, indent, es);
show_scan_qual(plan->qual, false, "qual",
((Scan *) plan)->scanrelid,
str, indent, es);
break;
case T_SeqScan:
case T_TidScan:
show_scan_qual(plan->qual, false, "qual",
((Scan *) plan)->scanrelid,
str, indent, es);
break;
case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual, "joinqual",
"outer", OUTER, outerPlan(plan),
"inner", INNER, innerPlan(plan),
str, indent, es);
show_upper_qual(plan->qual, "qual",
"outer", OUTER, outerPlan(plan),
"inner", INNER, innerPlan(plan),
str, indent, es);
break;
case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses, "merge",
"outer", OUTER, outerPlan(plan),
"inner", INNER, innerPlan(plan),
str, indent, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual, "joinqual",
"outer", OUTER, outerPlan(plan),
"inner", INNER, innerPlan(plan),
str, indent, es);
show_upper_qual(plan->qual, "qual",
"outer", OUTER, outerPlan(plan),
"inner", INNER, innerPlan(plan),
str, indent, es);
break;
case T_HashJoin:
show_upper_qual(((HashJoin *) plan)->hashclauses, "hash",
"outer", OUTER, outerPlan(plan),
"inner", INNER, innerPlan(plan),
str, indent, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual, "joinqual",
"outer", OUTER, outerPlan(plan),
"inner", INNER, innerPlan(plan),
str, indent, es);
show_upper_qual(plan->qual, "qual",
"outer", OUTER, outerPlan(plan),
"inner", INNER, innerPlan(plan),
str, indent, es);
break;
case T_SubqueryScan:
show_upper_qual(plan->qual, "qual",
"subplan", 1, ((SubqueryScan *) plan)->subplan,
"", 0, NULL,
str, indent, es);
break;
case T_Agg:
case T_Group:
show_upper_qual(plan->qual, "qual",
"subplan", 0, outerPlan(plan),
"", 0, NULL,
str, indent, es);
break;
case T_Result:
show_upper_qual((List *) ((Result *) plan)->resconstantqual,
"constqual",
"subplan", OUTER, outerPlan(plan),
"", 0, NULL,
str, indent, es);
show_upper_qual(plan->qual, "qual",
"subplan", OUTER, outerPlan(plan),
"", 0, NULL,
str, indent, es);
break;
default:
break;
}
/* initPlan-s */ /* initPlan-s */
if (plan->initPlan) if (plan->initPlan)
{ {
...@@ -448,3 +538,121 @@ Explain_PlanToString(Plan *plan, ExplainState *es) ...@@ -448,3 +538,121 @@ Explain_PlanToString(Plan *plan, ExplainState *es)
explain_outNode(str, plan, 0, es); explain_outNode(str, plan, 0, es);
return str; return str;
} }
/*
* Show a qualifier expression for a scan plan node
*/
static void
show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
int scanrelid,
StringInfo str, int indent, ExplainState *es)
{
RangeTblEntry *rte;
List *context;
Node *node;
char *exprstr;
int i;
/* No work if empty qual */
if (qual == NIL)
return;
if (is_or_qual)
{
if (lfirst(qual) == NIL && lnext(qual) == NIL)
return;
}
/* Generate deparse context */
Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
rte = rt_fetch(scanrelid, es->rtable);
/* Assume it's on a real relation */
Assert(rte->relname);
context = deparse_context_for(rte->relname, rte->relid);
/* Fix qual --- indexqual requires different processing */
if (is_or_qual)
node = make_ors_ands_explicit(qual);
else
node = (Node *) make_ands_explicit(qual);
/* Deparse the expression */
exprstr = deparse_expression(node, context, false);
/* And add to str */
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
}
/*
* Show a qualifier expression for an upper-level plan node
*/
static void
show_upper_qual(List *qual, const char *qlabel,
const char *outer_name, int outer_varno, Plan *outer_plan,
const char *inner_name, int inner_varno, Plan *inner_plan,
StringInfo str, int indent, ExplainState *es)
{
List *context;
Node *outercontext;
Node *innercontext;
Node *node;
char *exprstr;
int i;
/* No work if empty qual */
if (qual == NIL)
return;
/* Generate deparse context */
if (outer_plan)
outercontext = deparse_context_for_subplan(outer_name,
outer_plan->targetlist,
es->rtable);
else
outercontext = NULL;
if (inner_plan)
innercontext = deparse_context_for_subplan(inner_name,
inner_plan->targetlist,
es->rtable);
else
innercontext = NULL;
context = deparse_context_for_plan(outer_varno, outercontext,
inner_varno, innercontext);
/* Deparse the expression */
node = (Node *) make_ands_explicit(qual);
exprstr = deparse_expression(node, context, (inner_plan != NULL));
/* And add to str */
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
}
/*
* Indexscan qual lists have an implicit OR-of-ANDs structure. Make it
* explicit so deparsing works properly.
*/
static Node *
make_ors_ands_explicit(List *orclauses)
{
if (orclauses == NIL)
return NULL; /* probably can't happen */
else if (lnext(orclauses) == NIL)
return (Node *) make_ands_explicit(lfirst(orclauses));
else
{
List *args = NIL;
List *orptr;
foreach(orptr, orclauses)
{
args = lappend(args, make_ands_explicit(lfirst(orptr)));
}
return (Node *) make_orclause(args);
}
}
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.168 2002/03/08 04:37:16 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.169 2002/03/12 00:51:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -323,6 +323,7 @@ CopyJoinFields(Join *from, Join *newnode) ...@@ -323,6 +323,7 @@ CopyJoinFields(Join *from, Join *newnode)
{ {
newnode->jointype = from->jointype; newnode->jointype = from->jointype;
Node_Copy(from, newnode, joinqual); Node_Copy(from, newnode, joinqual);
newnode->joinrti = from->joinrti;
/* subPlan list must point to subplans in the new subtree, not the old */ /* subPlan list must point to subplans in the new subtree, not the old */
if (from->plan.subPlan != NIL) if (from->plan.subPlan != NIL)
newnode->plan.subPlan = nconc(newnode->plan.subPlan, newnode->plan.subPlan = nconc(newnode->plan.subPlan,
...@@ -970,8 +971,7 @@ _copyJoinExpr(JoinExpr *from) ...@@ -970,8 +971,7 @@ _copyJoinExpr(JoinExpr *from)
Node_Copy(from, newnode, using); Node_Copy(from, newnode, using);
Node_Copy(from, newnode, quals); Node_Copy(from, newnode, quals);
Node_Copy(from, newnode, alias); Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, colnames); newnode->rtindex = from->rtindex;
Node_Copy(from, newnode, colvars);
return newnode; return newnode;
} }
...@@ -1081,16 +1081,13 @@ _copyArrayRef(ArrayRef *from) ...@@ -1081,16 +1081,13 @@ _copyArrayRef(ArrayRef *from)
* _copyRelOptInfo * _copyRelOptInfo
* ---------------- * ----------------
*/ */
/*
* when you change this, also make sure to fix up xfunc_copyRelOptInfo in
* planner/path/xfunc.c accordingly!!!
* -- JMH, 8/2/93
*/
static RelOptInfo * static RelOptInfo *
_copyRelOptInfo(RelOptInfo *from) _copyRelOptInfo(RelOptInfo *from)
{ {
RelOptInfo *newnode = makeNode(RelOptInfo); RelOptInfo *newnode = makeNode(RelOptInfo);
newnode->reloptkind = from->reloptkind;
newnode->relids = listCopy(from->relids); newnode->relids = listCopy(from->relids);
newnode->rows = from->rows; newnode->rows = from->rows;
...@@ -1109,6 +1106,9 @@ _copyRelOptInfo(RelOptInfo *from) ...@@ -1109,6 +1106,9 @@ _copyRelOptInfo(RelOptInfo *from)
newnode->tuples = from->tuples; newnode->tuples = from->tuples;
Node_Copy(from, newnode, subplan); Node_Copy(from, newnode, subplan);
newnode->joinrti = from->joinrti;
newnode->joinrteids = listCopy(from->joinrteids);
Node_Copy(from, newnode, baserestrictinfo); Node_Copy(from, newnode, baserestrictinfo);
newnode->baserestrictcost = from->baserestrictcost; newnode->baserestrictcost = from->baserestrictcost;
newnode->outerjoinset = listCopy(from->outerjoinset); newnode->outerjoinset = listCopy(from->outerjoinset);
...@@ -1487,10 +1487,16 @@ _copyRangeTblEntry(RangeTblEntry *from) ...@@ -1487,10 +1487,16 @@ _copyRangeTblEntry(RangeTblEntry *from)
{ {
RangeTblEntry *newnode = makeNode(RangeTblEntry); RangeTblEntry *newnode = makeNode(RangeTblEntry);
newnode->rtekind = from->rtekind;
if (from->relname) if (from->relname)
newnode->relname = pstrdup(from->relname); newnode->relname = pstrdup(from->relname);
newnode->relid = from->relid; newnode->relid = from->relid;
Node_Copy(from, newnode, subquery); 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, alias); Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, eref); Node_Copy(from, newnode, eref);
newnode->inh = from->inh; newnode->inh = from->inh;
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.116 2002/03/08 04:37:16 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.117 2002/03/12 00:51:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -335,9 +335,7 @@ _equalJoinExpr(JoinExpr *a, JoinExpr *b) ...@@ -335,9 +335,7 @@ _equalJoinExpr(JoinExpr *a, JoinExpr *b)
return false; return false;
if (!equal(a->alias, b->alias)) if (!equal(a->alias, b->alias))
return false; return false;
if (!equal(a->colnames, b->colnames)) if (a->rtindex != b->rtindex)
return false;
if (!equal(a->colvars, b->colvars))
return false; return false;
return true; return true;
...@@ -1639,12 +1637,24 @@ _equalTargetEntry(TargetEntry *a, TargetEntry *b) ...@@ -1639,12 +1637,24 @@ _equalTargetEntry(TargetEntry *a, TargetEntry *b)
static bool static bool
_equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
{ {
if (a->rtekind != b->rtekind)
return false;
if (!equalstr(a->relname, b->relname)) if (!equalstr(a->relname, b->relname))
return false; return false;
if (a->relid != b->relid) if (a->relid != b->relid)
return false; return false;
if (!equal(a->subquery, b->subquery)) if (!equal(a->subquery, b->subquery))
return false; 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))
return false;
if (!equal(a->alias, b->alias)) if (!equal(a->alias, b->alias))
return false; return false;
if (!equal(a->eref, b->eref)) if (!equal(a->eref, b->eref))
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.148 2002/03/06 06:09:49 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.149 2002/03/12 00:51:39 tgl Exp $
* *
* NOTES * NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which * Every (plan) node in POSTGRES has an associated "out" routine which
...@@ -410,6 +410,8 @@ _outJoin(StringInfo str, Join *node) ...@@ -410,6 +410,8 @@ _outJoin(StringInfo str, Join *node)
appendStringInfo(str, " :jointype %d :joinqual ", appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->jointype); (int) node->jointype);
_outNode(str, node->joinqual); _outNode(str, node->joinqual);
appendStringInfo(str, " :joinrti %d ",
node->joinrti);
} }
/* /*
...@@ -423,6 +425,8 @@ _outNestLoop(StringInfo str, NestLoop *node) ...@@ -423,6 +425,8 @@ _outNestLoop(StringInfo str, NestLoop *node)
appendStringInfo(str, " :jointype %d :joinqual ", appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype); (int) node->join.jointype);
_outNode(str, node->join.joinqual); _outNode(str, node->join.joinqual);
appendStringInfo(str, " :joinrti %d ",
node->join.joinrti);
} }
/* /*
...@@ -436,6 +440,8 @@ _outMergeJoin(StringInfo str, MergeJoin *node) ...@@ -436,6 +440,8 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
appendStringInfo(str, " :jointype %d :joinqual ", appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype); (int) node->join.jointype);
_outNode(str, node->join.joinqual); _outNode(str, node->join.joinqual);
appendStringInfo(str, " :joinrti %d ",
node->join.joinrti);
appendStringInfo(str, " :mergeclauses "); appendStringInfo(str, " :mergeclauses ");
_outNode(str, node->mergeclauses); _outNode(str, node->mergeclauses);
...@@ -452,6 +458,8 @@ _outHashJoin(StringInfo str, HashJoin *node) ...@@ -452,6 +458,8 @@ _outHashJoin(StringInfo str, HashJoin *node)
appendStringInfo(str, " :jointype %d :joinqual ", appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype); (int) node->join.jointype);
_outNode(str, node->join.joinqual); _outNode(str, node->join.joinqual);
appendStringInfo(str, " :joinrti %d ",
node->join.joinrti);
appendStringInfo(str, " :hashclauses "); appendStringInfo(str, " :hashclauses ");
_outNode(str, node->hashclauses); _outNode(str, node->hashclauses);
...@@ -939,10 +947,7 @@ _outJoinExpr(StringInfo str, JoinExpr *node) ...@@ -939,10 +947,7 @@ _outJoinExpr(StringInfo str, JoinExpr *node)
_outNode(str, node->quals); _outNode(str, node->quals);
appendStringInfo(str, " :alias "); appendStringInfo(str, " :alias ");
_outNode(str, node->alias); _outNode(str, node->alias);
appendStringInfo(str, " :colnames "); appendStringInfo(str, " :rtindex %d ", node->rtindex);
_outNode(str, node->colnames);
appendStringInfo(str, " :colvars ");
_outNode(str, node->colvars);
} }
/* /*
...@@ -961,12 +966,21 @@ _outTargetEntry(StringInfo str, TargetEntry *node) ...@@ -961,12 +966,21 @@ _outTargetEntry(StringInfo str, TargetEntry *node)
static void static void
_outRangeTblEntry(StringInfo str, RangeTblEntry *node) _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
{ {
appendStringInfo(str, " RTE :relname "); appendStringInfo(str, " RTE :rtekind %d :relname ",
(int) node->rtekind);
_outToken(str, node->relname); _outToken(str, node->relname);
appendStringInfo(str, " :relid %u ", appendStringInfo(str, " :relid %u :subquery ",
node->relid); node->relid);
appendStringInfo(str, " :subquery ");
_outNode(str, node->subquery); _outNode(str, node->subquery);
appendStringInfo(str, " :jointype %d :joincoltypes ",
(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);
appendStringInfo(str, " :alias "); appendStringInfo(str, " :alias ");
_outNode(str, node->alias); _outNode(str, node->alias);
appendStringInfo(str, " :eref "); appendStringInfo(str, " :eref ");
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.115 2002/03/01 06:01:18 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.116 2002/03/12 00:51:39 tgl Exp $
* *
* NOTES * NOTES
* Most of the read functions for plan nodes are tested. (In fact, they * Most of the read functions for plan nodes are tested. (In fact, they
...@@ -421,6 +421,10 @@ _getJoin(Join *node) ...@@ -421,6 +421,10 @@ _getJoin(Join *node)
token = pg_strtok(&length); /* skip the :joinqual */ token = pg_strtok(&length); /* skip the :joinqual */
node->joinqual = nodeRead(true); /* get 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);
} }
...@@ -1343,7 +1347,7 @@ _readJoinExpr(void) ...@@ -1343,7 +1347,7 @@ _readJoinExpr(void)
local_node->jointype = (JoinType) atoi(token); local_node->jointype = (JoinType) atoi(token);
token = pg_strtok(&length); /* eat :isNatural */ token = pg_strtok(&length); /* eat :isNatural */
token = pg_strtok(&length); /* get :isNatural */ token = pg_strtok(&length); /* get isNatural */
local_node->isNatural = strtobool(token); local_node->isNatural = strtobool(token);
token = pg_strtok(&length); /* eat :larg */ token = pg_strtok(&length); /* eat :larg */
...@@ -1361,11 +1365,9 @@ _readJoinExpr(void) ...@@ -1361,11 +1365,9 @@ _readJoinExpr(void)
token = pg_strtok(&length); /* eat :alias */ token = pg_strtok(&length); /* eat :alias */
local_node->alias = nodeRead(true); /* now read it */ local_node->alias = nodeRead(true); /* now read it */
token = pg_strtok(&length); /* eat :colnames */ token = pg_strtok(&length); /* eat :rtindex */
local_node->colnames = nodeRead(true); /* now read it */ token = pg_strtok(&length); /* get rtindex */
local_node->rtindex = atoi(token);
token = pg_strtok(&length); /* eat :colvars */
local_node->colvars = nodeRead(true); /* now read it */
return local_node; return local_node;
} }
...@@ -1424,6 +1426,10 @@ _readRangeTblEntry(void) ...@@ -1424,6 +1426,10 @@ _readRangeTblEntry(void)
local_node = makeNode(RangeTblEntry); local_node = makeNode(RangeTblEntry);
token = pg_strtok(&length); /* eat :rtekind */
token = pg_strtok(&length); /* get :rtekind */
local_node->rtekind = (RTEKind) atoi(token);
token = pg_strtok(&length); /* eat :relname */ token = pg_strtok(&length); /* eat :relname */
token = pg_strtok(&length); /* get :relname */ token = pg_strtok(&length); /* get :relname */
local_node->relname = nullable_string(token, length); local_node->relname = nullable_string(token, length);
...@@ -1435,6 +1441,22 @@ _readRangeTblEntry(void) ...@@ -1435,6 +1441,22 @@ _readRangeTblEntry(void)
token = pg_strtok(&length); /* eat :subquery */ token = pg_strtok(&length); /* eat :subquery */
local_node->subquery = nodeRead(true); /* now read it */ local_node->subquery = nodeRead(true); /* now read it */
token = pg_strtok(&length); /* eat :jointype */
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 :alias */ token = pg_strtok(&length); /* eat :alias */
local_node->alias = nodeRead(true); /* now read it */ local_node->alias = nodeRead(true); /* now read it */
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.82 2002/03/01 20:50:20 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.83 2002/03/12 00:51:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -597,7 +597,7 @@ cost_mergejoin(Path *path, Query *root, ...@@ -597,7 +597,7 @@ cost_mergejoin(Path *path, Query *root,
leftvar = get_leftop(firstclause->clause); leftvar = get_leftop(firstclause->clause);
Assert(IsA(leftvar, Var)); Assert(IsA(leftvar, Var));
if (intMember(leftvar->varno, outer_path->parent->relids)) if (VARISRELMEMBER(leftvar->varno, outer_path->parent))
{ {
/* left side of clause is outer */ /* left side of clause is outer */
outerscansel = firstclause->left_mergescansel; outerscansel = firstclause->left_mergescansel;
...@@ -748,7 +748,7 @@ cost_hashjoin(Path *path, Query *root, ...@@ -748,7 +748,7 @@ cost_hashjoin(Path *path, Query *root,
* a large query, we cache the bucketsize estimate in the RestrictInfo * a large query, we cache the bucketsize estimate in the RestrictInfo
* node to avoid repeated lookups of statistics. * node to avoid repeated lookups of statistics.
*/ */
if (intMember(right->varno, inner_path->parent->relids)) if (VARISRELMEMBER(right->varno, inner_path->parent))
{ {
/* righthand side is inner */ /* righthand side is inner */
innerbucketsize = restrictinfo->right_bucketsize; innerbucketsize = restrictinfo->right_bucketsize;
...@@ -761,7 +761,7 @@ cost_hashjoin(Path *path, Query *root, ...@@ -761,7 +761,7 @@ cost_hashjoin(Path *path, Query *root,
} }
else else
{ {
Assert(intMember(left->varno, inner_path->parent->relids)); Assert(VARISRELMEMBER(left->varno, inner_path->parent));
/* lefthand side is inner */ /* lefthand side is inner */
innerbucketsize = restrictinfo->left_bucketsize; innerbucketsize = restrictinfo->left_bucketsize;
if (innerbucketsize < 0) if (innerbucketsize < 0)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.67 2001/11/11 19:18:54 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.68 2002/03/12 00:51:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel, static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel, RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list, List *restrictlist, List *mergeclause_list,
...@@ -720,8 +721,6 @@ hash_inner_and_outer(Query *root, ...@@ -720,8 +721,6 @@ hash_inner_and_outer(Query *root,
List *restrictlist, List *restrictlist,
JoinType jointype) JoinType jointype)
{ {
Relids outerrelids = outerrel->relids;
Relids innerrelids = innerrel->relids;
bool isouterjoin; bool isouterjoin;
List *i; List *i;
...@@ -773,13 +772,13 @@ hash_inner_and_outer(Query *root, ...@@ -773,13 +772,13 @@ hash_inner_and_outer(Query *root,
/* /*
* Check if clause is usable with these input rels. * Check if clause is usable with these input rels.
*/ */
if (intMember(left->varno, outerrelids) && if (VARISRELMEMBER(left->varno, outerrel) &&
intMember(right->varno, innerrelids)) VARISRELMEMBER(right->varno, innerrel))
{ {
/* righthand side is inner */ /* righthand side is inner */
} }
else if (intMember(left->varno, innerrelids) && else if (VARISRELMEMBER(left->varno, innerrel) &&
intMember(right->varno, outerrelids)) VARISRELMEMBER(right->varno, outerrel))
{ {
/* lefthand side is inner */ /* lefthand side is inner */
} }
...@@ -901,8 +900,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel, ...@@ -901,8 +900,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
JoinType jointype) JoinType jointype)
{ {
List *result_list = NIL; List *result_list = NIL;
Relids outerrelids = outerrel->relids;
Relids innerrelids = innerrel->relids;
bool isouterjoin = IS_OUTER_JOIN(jointype); bool isouterjoin = IS_OUTER_JOIN(jointype);
List *i; List *i;
...@@ -952,10 +949,10 @@ select_mergejoin_clauses(RelOptInfo *joinrel, ...@@ -952,10 +949,10 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
left = get_leftop(clause); left = get_leftop(clause);
right = get_rightop(clause); right = get_rightop(clause);
if ((intMember(left->varno, outerrelids) && if ((VARISRELMEMBER(left->varno, outerrel) &&
intMember(right->varno, innerrelids)) || VARISRELMEMBER(right->varno, innerrel)) ||
(intMember(left->varno, innerrelids) && (VARISRELMEMBER(left->varno, innerrel) &&
intMember(right->varno, outerrelids))) VARISRELMEMBER(right->varno, outerrel)))
result_list = lcons(restrictinfo, result_list); result_list = lcons(restrictinfo, result_list);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.55 2001/10/25 05:49:32 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.56 2002/03/12 00:51:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -340,7 +340,7 @@ make_jointree_rel(Query *root, Node *jtnode) ...@@ -340,7 +340,7 @@ make_jointree_rel(Query *root, Node *jtnode)
{ {
int varno = ((RangeTblRef *) jtnode)->rtindex; int varno = ((RangeTblRef *) jtnode)->rtindex;
return build_base_rel(root, varno); return find_base_rel(root, varno);
} }
else if (IsA(jtnode, FromExpr)) else if (IsA(jtnode, FromExpr))
{ {
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.36 2001/11/11 20:33:53 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.37 2002/03/12 00:51:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -854,7 +854,8 @@ make_pathkeys_for_mergeclauses(Query *root, ...@@ -854,7 +854,8 @@ make_pathkeys_for_mergeclauses(Query *root,
cache_mergeclause_pathkeys(root, restrictinfo); cache_mergeclause_pathkeys(root, restrictinfo);
key = (Node *) get_leftop(restrictinfo->clause); key = (Node *) get_leftop(restrictinfo->clause);
if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids)) if (IsA(key, Var) &&
VARISRELMEMBER(((Var *) key)->varno, rel))
{ {
/* Rel is left side of mergeclause */ /* Rel is left side of mergeclause */
pathkey = restrictinfo->left_pathkey; pathkey = restrictinfo->left_pathkey;
...@@ -862,7 +863,8 @@ make_pathkeys_for_mergeclauses(Query *root, ...@@ -862,7 +863,8 @@ make_pathkeys_for_mergeclauses(Query *root,
else else
{ {
key = (Node *) get_rightop(restrictinfo->clause); key = (Node *) get_rightop(restrictinfo->clause);
if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids)) if (IsA(key, Var) &&
VARISRELMEMBER(((Var *) key)->varno, rel))
{ {
/* Rel is right side of mergeclause */ /* Rel is right side of mergeclause */
pathkey = restrictinfo->right_pathkey; pathkey = restrictinfo->right_pathkey;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.111 2001/10/28 06:25:44 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.112 2002/03/12 00:51:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,7 +43,8 @@ static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist, ...@@ -43,7 +43,8 @@ static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
List *scan_clauses); List *scan_clauses);
static SubqueryScan *create_subqueryscan_plan(Path *best_path, static SubqueryScan *create_subqueryscan_plan(Path *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist, static NestLoop *create_nestloop_plan(Query *root,
NestPath *best_path, List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist, Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist); Plan *inner_plan, List *inner_tlist);
...@@ -52,7 +53,8 @@ static MergeJoin *create_mergejoin_plan(Query *root, ...@@ -52,7 +53,8 @@ static MergeJoin *create_mergejoin_plan(Query *root,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist, Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist); Plan *inner_plan, List *inner_tlist);
static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist, static HashJoin *create_hashjoin_plan(Query *root,
HashPath *best_path, List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist, Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist); Plan *inner_plan, List *inner_tlist);
...@@ -78,18 +80,18 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, ...@@ -78,18 +80,18 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
static NestLoop *make_nestloop(List *tlist, static NestLoop *make_nestloop(List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree, Plan *lefttree, Plan *righttree,
JoinType jointype); JoinType jointype, Index joinrti);
static HashJoin *make_hashjoin(List *tlist, static HashJoin *make_hashjoin(List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
List *hashclauses, List *hashclauses,
Plan *lefttree, Plan *righttree, Plan *lefttree, Plan *righttree,
JoinType jointype); JoinType jointype, Index joinrti);
static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree); static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist, static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
List *mergeclauses, List *mergeclauses,
Plan *lefttree, Plan *righttree, Plan *lefttree, Plan *righttree,
JoinType jointype); JoinType jointype, Index joinrti);
/* /*
* create_plan * create_plan
...@@ -259,7 +261,8 @@ create_join_plan(Query *root, JoinPath *best_path) ...@@ -259,7 +261,8 @@ create_join_plan(Query *root, JoinPath *best_path)
inner_tlist); inner_tlist);
break; break;
case T_HashJoin: case T_HashJoin:
plan = (Join *) create_hashjoin_plan((HashPath *) best_path, plan = (Join *) create_hashjoin_plan(root,
(HashPath *) best_path,
join_tlist, join_tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
...@@ -269,7 +272,8 @@ create_join_plan(Query *root, JoinPath *best_path) ...@@ -269,7 +272,8 @@ create_join_plan(Query *root, JoinPath *best_path)
inner_tlist); inner_tlist);
break; break;
case T_NestLoop: case T_NestLoop:
plan = (Join *) create_nestloop_plan((NestPath *) best_path, plan = (Join *) create_nestloop_plan(root,
(NestPath *) best_path,
join_tlist, join_tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
...@@ -576,7 +580,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses) ...@@ -576,7 +580,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
*****************************************************************************/ *****************************************************************************/
static NestLoop * static NestLoop *
create_nestloop_plan(NestPath *best_path, create_nestloop_plan(Query *root,
NestPath *best_path,
List *tlist, List *tlist,
List *joinclauses, List *joinclauses,
List *otherclauses, List *otherclauses,
...@@ -586,6 +591,7 @@ create_nestloop_plan(NestPath *best_path, ...@@ -586,6 +591,7 @@ create_nestloop_plan(NestPath *best_path,
List *inner_tlist) List *inner_tlist)
{ {
NestLoop *join_plan; NestLoop *join_plan;
Index joinrti = best_path->path.parent->joinrti;
if (IsA(inner_plan, IndexScan)) if (IsA(inner_plan, IndexScan))
{ {
...@@ -630,19 +636,25 @@ create_nestloop_plan(NestPath *best_path, ...@@ -630,19 +636,25 @@ create_nestloop_plan(NestPath *best_path,
/* only refs to outer vars get changed in the inner indexqual */ /* only refs to outer vars get changed in the inner indexqual */
innerscan->indxqualorig = join_references(indxqualorig, innerscan->indxqualorig = join_references(indxqualorig,
root,
outer_tlist, outer_tlist,
NIL, NIL,
innerrel); innerrel,
joinrti);
innerscan->indxqual = join_references(innerscan->indxqual, innerscan->indxqual = join_references(innerscan->indxqual,
root,
outer_tlist, outer_tlist,
NIL, NIL,
innerrel); innerrel,
joinrti);
/* fix the inner qpqual too, if it has join clauses */ /* fix the inner qpqual too, if it has join clauses */
if (NumRelids((Node *) inner_plan->qual) > 1) if (NumRelids((Node *) inner_plan->qual) > 1)
inner_plan->qual = join_references(inner_plan->qual, inner_plan->qual = join_references(inner_plan->qual,
root,
outer_tlist, outer_tlist,
NIL, NIL,
innerrel); innerrel,
joinrti);
} }
} }
else if (IsA(inner_plan, TidScan)) else if (IsA(inner_plan, TidScan))
...@@ -650,9 +662,11 @@ create_nestloop_plan(NestPath *best_path, ...@@ -650,9 +662,11 @@ create_nestloop_plan(NestPath *best_path,
TidScan *innerscan = (TidScan *) inner_plan; TidScan *innerscan = (TidScan *) inner_plan;
innerscan->tideval = join_references(innerscan->tideval, innerscan->tideval = join_references(innerscan->tideval,
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
innerscan->scan.scanrelid); innerscan->scan.scanrelid,
joinrti);
} }
else if (IsA_Join(inner_plan)) else if (IsA_Join(inner_plan))
{ {
...@@ -671,20 +685,25 @@ create_nestloop_plan(NestPath *best_path, ...@@ -671,20 +685,25 @@ create_nestloop_plan(NestPath *best_path,
* Set quals to contain INNER/OUTER var references. * Set quals to contain INNER/OUTER var references.
*/ */
joinclauses = join_references(joinclauses, joinclauses = join_references(joinclauses,
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
joinrti);
otherclauses = join_references(otherclauses, otherclauses = join_references(otherclauses,
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
joinrti);
join_plan = make_nestloop(tlist, join_plan = make_nestloop(tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
outer_plan, outer_plan,
inner_plan, inner_plan,
best_path->jointype); best_path->jointype,
joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->path); copy_path_costsize(&join_plan->join.plan, &best_path->path);
...@@ -704,6 +723,7 @@ create_mergejoin_plan(Query *root, ...@@ -704,6 +723,7 @@ create_mergejoin_plan(Query *root,
{ {
List *mergeclauses; List *mergeclauses;
MergeJoin *join_plan; MergeJoin *join_plan;
Index joinrti = best_path->jpath.path.parent->joinrti;
mergeclauses = get_actual_clauses(best_path->path_mergeclauses); mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
...@@ -713,26 +733,32 @@ create_mergejoin_plan(Query *root, ...@@ -713,26 +733,32 @@ create_mergejoin_plan(Query *root,
* clauses to contain INNER/OUTER var references. * clauses to contain INNER/OUTER var references.
*/ */
joinclauses = join_references(set_difference(joinclauses, mergeclauses), joinclauses = join_references(set_difference(joinclauses, mergeclauses),
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
joinrti);
/* /*
* Fix the additional qpquals too. * Fix the additional qpquals too.
*/ */
otherclauses = join_references(otherclauses, otherclauses = join_references(otherclauses,
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
joinrti);
/* /*
* Now set the references in the mergeclauses and rearrange them so * Now set the references in the mergeclauses and rearrange them so
* that the outer variable is always on the left. * that the outer variable is always on the left.
*/ */
mergeclauses = switch_outer(join_references(mergeclauses, mergeclauses = switch_outer(join_references(mergeclauses,
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0)); (Index) 0,
joinrti));
/* /*
* Create explicit sort nodes for the outer and inner join paths if * Create explicit sort nodes for the outer and inner join paths if
...@@ -798,7 +824,8 @@ create_mergejoin_plan(Query *root, ...@@ -798,7 +824,8 @@ create_mergejoin_plan(Query *root,
mergeclauses, mergeclauses,
outer_plan, outer_plan,
inner_plan, inner_plan,
best_path->jpath.jointype); best_path->jpath.jointype,
joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
...@@ -806,7 +833,8 @@ create_mergejoin_plan(Query *root, ...@@ -806,7 +833,8 @@ create_mergejoin_plan(Query *root,
} }
static HashJoin * static HashJoin *
create_hashjoin_plan(HashPath *best_path, create_hashjoin_plan(Query *root,
HashPath *best_path,
List *tlist, List *tlist,
List *joinclauses, List *joinclauses,
List *otherclauses, List *otherclauses,
...@@ -819,6 +847,7 @@ create_hashjoin_plan(HashPath *best_path, ...@@ -819,6 +847,7 @@ create_hashjoin_plan(HashPath *best_path,
HashJoin *join_plan; HashJoin *join_plan;
Hash *hash_plan; Hash *hash_plan;
Node *innerhashkey; Node *innerhashkey;
Index joinrti = best_path->jpath.path.parent->joinrti;
/* /*
* NOTE: there will always be exactly one hashclause in the list * NOTE: there will always be exactly one hashclause in the list
...@@ -834,26 +863,32 @@ create_hashjoin_plan(HashPath *best_path, ...@@ -834,26 +863,32 @@ create_hashjoin_plan(HashPath *best_path,
* clauses to contain INNER/OUTER var references. * clauses to contain INNER/OUTER var references.
*/ */
joinclauses = join_references(set_difference(joinclauses, hashclauses), joinclauses = join_references(set_difference(joinclauses, hashclauses),
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
joinrti);
/* /*
* Fix the additional qpquals too. * Fix the additional qpquals too.
*/ */
otherclauses = join_references(otherclauses, otherclauses = join_references(otherclauses,
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
joinrti);
/* /*
* Now set the references in the hashclauses and rearrange them so * Now set the references in the hashclauses and rearrange them so
* that the outer variable is always on the left. * that the outer variable is always on the left.
*/ */
hashclauses = switch_outer(join_references(hashclauses, hashclauses = switch_outer(join_references(hashclauses,
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0)); (Index) 0,
joinrti));
/* Now the righthand op of the sole hashclause is the inner hash key. */ /* Now the righthand op of the sole hashclause is the inner hash key. */
innerhashkey = (Node *) get_rightop(lfirst(hashclauses)); innerhashkey = (Node *) get_rightop(lfirst(hashclauses));
...@@ -868,7 +903,8 @@ create_hashjoin_plan(HashPath *best_path, ...@@ -868,7 +903,8 @@ create_hashjoin_plan(HashPath *best_path,
hashclauses, hashclauses,
outer_plan, outer_plan,
(Plan *) hash_plan, (Plan *) hash_plan,
best_path->jpath.jointype); best_path->jpath.jointype,
joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
...@@ -1327,7 +1363,8 @@ make_nestloop(List *tlist, ...@@ -1327,7 +1363,8 @@ make_nestloop(List *tlist,
List *otherclauses, List *otherclauses,
Plan *lefttree, Plan *lefttree,
Plan *righttree, Plan *righttree,
JoinType jointype) JoinType jointype,
Index joinrti)
{ {
NestLoop *node = makeNode(NestLoop); NestLoop *node = makeNode(NestLoop);
Plan *plan = &node->join.plan; Plan *plan = &node->join.plan;
...@@ -1340,6 +1377,7 @@ make_nestloop(List *tlist, ...@@ -1340,6 +1377,7 @@ make_nestloop(List *tlist,
plan->righttree = righttree; plan->righttree = righttree;
node->join.jointype = jointype; node->join.jointype = jointype;
node->join.joinqual = joinclauses; node->join.joinqual = joinclauses;
node->join.joinrti = joinrti;
return node; return node;
} }
...@@ -1351,7 +1389,8 @@ make_hashjoin(List *tlist, ...@@ -1351,7 +1389,8 @@ make_hashjoin(List *tlist,
List *hashclauses, List *hashclauses,
Plan *lefttree, Plan *lefttree,
Plan *righttree, Plan *righttree,
JoinType jointype) JoinType jointype,
Index joinrti)
{ {
HashJoin *node = makeNode(HashJoin); HashJoin *node = makeNode(HashJoin);
Plan *plan = &node->join.plan; Plan *plan = &node->join.plan;
...@@ -1365,6 +1404,7 @@ make_hashjoin(List *tlist, ...@@ -1365,6 +1404,7 @@ make_hashjoin(List *tlist,
node->hashclauses = hashclauses; node->hashclauses = hashclauses;
node->join.jointype = jointype; node->join.jointype = jointype;
node->join.joinqual = joinclauses; node->join.joinqual = joinclauses;
node->join.joinrti = joinrti;
return node; return node;
} }
...@@ -1399,7 +1439,8 @@ make_mergejoin(List *tlist, ...@@ -1399,7 +1439,8 @@ make_mergejoin(List *tlist,
List *mergeclauses, List *mergeclauses,
Plan *lefttree, Plan *lefttree,
Plan *righttree, Plan *righttree,
JoinType jointype) JoinType jointype,
Index joinrti)
{ {
MergeJoin *node = makeNode(MergeJoin); MergeJoin *node = makeNode(MergeJoin);
Plan *plan = &node->join.plan; Plan *plan = &node->join.plan;
...@@ -1413,6 +1454,7 @@ make_mergejoin(List *tlist, ...@@ -1413,6 +1454,7 @@ make_mergejoin(List *tlist,
node->mergeclauses = mergeclauses; node->mergeclauses = mergeclauses;
node->join.jointype = jointype; node->join.jointype = jointype;
node->join.joinqual = joinclauses; node->join.joinqual = joinclauses;
node->join.joinrti = joinrti;
return node; return node;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.66 2002/03/01 06:01:19 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.67 2002/03/12 00:51:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,67 +53,29 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); ...@@ -53,67 +53,29 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
/***************************************************************************** /*****************************************************************************
* *
* TARGET LISTS * JOIN TREES
* *
*****************************************************************************/ *****************************************************************************/
/* /*
* build_base_rel_tlists * add_base_rels_to_query
* Creates rel nodes for every relation mentioned in the target list *
* 'tlist' (if a node hasn't already been created) and adds them to * Scan the query's jointree and create baserel RelOptInfos for all
* root->base_rel_list. Creates targetlist entries for each var seen * the base relations (ie, table and subquery RTEs) appearing in the
* in 'tlist' and adds them to the tlist of the appropriate rel node. * jointree. Also, create otherrel RelOptInfos for join RTEs.
*/
void
build_base_rel_tlists(Query *root, List *tlist)
{
List *tlist_vars = pull_var_clause((Node *) tlist, false);
add_vars_to_targetlist(root, tlist_vars);
freeList(tlist_vars);
}
/*
* add_vars_to_targetlist
* For each variable appearing in the list, add it to the relation's
* targetlist if not already present. Corresponding base rel nodes
* will be created if not already present.
*/
static void
add_vars_to_targetlist(Query *root, List *vars)
{
List *temp;
foreach(temp, vars)
{
Var *var = (Var *) lfirst(temp);
RelOptInfo *rel = build_base_rel(root, var->varno);
add_var_to_tlist(rel, var);
}
}
/*----------
* add_missing_rels_to_query
* *
* If we have a relation listed in the join tree that does not appear * The return value is a list of all the baserel indexes (but not join RTE
* in the target list nor qualifications, we must add it to the base * indexes) included in the scanned jointree. This is actually just an
* relation list so that it can be processed. For instance, * internal convenience for marking join otherrels properly; no outside
* select count(*) from foo; * caller uses the result.
* would fail to scan foo if this routine were not called. More subtly,
* select f.x from foo f, foo f2
* is a join of f and f2. Note that if we have
* select foo.x from foo f
* this also gets turned into a join (between foo as foo and foo as f).
* *
* Returns a list of all the base relations (RelOptInfo nodes) that appear * At the end of this process, there should be one baserel RelOptInfo for
* in the join tree. This list can be used for cross-checking in the * every non-join RTE that is used in the query. Therefore, this routine
* reverse direction, ie, that we have a join tree entry for every * is the only place that should call build_base_rel. But build_other_rel
* relation used in the query. * will be used again later to build rels for inheritance children.
*----------
*/ */
List * List *
add_missing_rels_to_query(Query *root, Node *jtnode) add_base_rels_to_query(Query *root, Node *jtnode)
{ {
List *result = NIL; List *result = NIL;
...@@ -123,10 +85,8 @@ add_missing_rels_to_query(Query *root, Node *jtnode) ...@@ -123,10 +85,8 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
{ {
int varno = ((RangeTblRef *) jtnode)->rtindex; int varno = ((RangeTblRef *) jtnode)->rtindex;
/* This call to build_base_rel does the primary work... */ build_base_rel(root, varno);
RelOptInfo *rel = build_base_rel(root, varno); result = makeListi1(varno);
result = makeList1(rel);
} }
else if (IsA(jtnode, FromExpr)) else if (IsA(jtnode, FromExpr))
{ {
...@@ -136,24 +96,98 @@ add_missing_rels_to_query(Query *root, Node *jtnode) ...@@ -136,24 +96,98 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
foreach(l, f->fromlist) foreach(l, f->fromlist)
{ {
result = nconc(result, result = nconc(result,
add_missing_rels_to_query(root, lfirst(l))); add_base_rels_to_query(root, lfirst(l)));
} }
} }
else if (IsA(jtnode, JoinExpr)) else if (IsA(jtnode, JoinExpr))
{ {
JoinExpr *j = (JoinExpr *) jtnode; JoinExpr *j = (JoinExpr *) jtnode;
RelOptInfo *jrel;
result = add_missing_rels_to_query(root, j->larg); result = add_base_rels_to_query(root, j->larg);
result = nconc(result, result = nconc(result,
add_missing_rels_to_query(root, j->rarg)); add_base_rels_to_query(root, j->rarg));
/* the join's own rtindex is NOT added to result */
jrel = build_other_rel(root, j->rtindex);
/*
* Mark the join's otherrel with outerjoinset = list of baserel ids
* included in the join. Note we must copy here because result list
* is destructively modified by nconcs at higher levels.
*/
jrel->outerjoinset = listCopy(result);
/*
* Safety check: join RTEs should not be SELECT FOR UPDATE targets
*/
if (intMember(j->rtindex, root->rowMarks))
elog(ERROR, "SELECT FOR UPDATE cannot be applied to a join");
} }
else else
elog(ERROR, "add_missing_rels_to_query: unexpected node type %d", elog(ERROR, "add_base_rels_to_query: unexpected node type %d",
nodeTag(jtnode)); nodeTag(jtnode));
return result; return result;
} }
/*****************************************************************************
*
* TARGET LISTS
*
*****************************************************************************/
/*
* build_base_rel_tlists
* Creates targetlist entries for each var seen in 'tlist' and adds
* them to the tlist of the appropriate rel node.
*/
void
build_base_rel_tlists(Query *root, List *tlist)
{
List *tlist_vars = pull_var_clause((Node *) tlist, false);
add_vars_to_targetlist(root, tlist_vars);
freeList(tlist_vars);
}
/*
* add_vars_to_targetlist
* For each variable appearing in the list, add it to the owning
* relation's targetlist if not already present.
*
* Note that join alias variables will be attached to the otherrel for
* the join RTE. They will later be transferred to the tlist of
* the corresponding joinrel. We will also cause entries to be made
* for the Vars that the alias will eventually depend on.
*/
static void
add_vars_to_targetlist(Query *root, List *vars)
{
List *temp;
foreach(temp, vars)
{
Var *var = (Var *) lfirst(temp);
RelOptInfo *rel = find_base_rel(root, var->varno);
add_var_to_tlist(rel, var);
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);
}
}
}
/***************************************************************************** /*****************************************************************************
* *
* QUALIFICATIONS * QUALIFICATIONS
...@@ -165,10 +199,9 @@ add_missing_rels_to_query(Query *root, Node *jtnode) ...@@ -165,10 +199,9 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
* distribute_quals_to_rels * distribute_quals_to_rels
* Recursively scan the query's join tree for WHERE and JOIN/ON qual * Recursively scan the query's join tree for WHERE and JOIN/ON qual
* clauses, and add these to the appropriate RestrictInfo and JoinInfo * clauses, and add these to the appropriate RestrictInfo and JoinInfo
* lists belonging to base RelOptInfos. New base rel entries are created * lists belonging to base RelOptInfos. Also, base RelOptInfos are marked
* as needed. Also, base RelOptInfos are marked with outerjoinset * with outerjoinset information, to aid in proper positioning of qual
* information, to aid in proper positioning of qual clauses that appear * clauses that appear above outer joins.
* above outer joins.
* *
* NOTE: when dealing with inner joins, it is appropriate to let a qual clause * NOTE: when dealing with inner joins, it is appropriate to let a qual clause
* be evaluated at the lowest level where all the variables it mentions are * be evaluated at the lowest level where all the variables it mentions are
...@@ -181,7 +214,7 @@ add_missing_rels_to_query(Query *root, Node *jtnode) ...@@ -181,7 +214,7 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
* a rel, thereby forcing them up the join tree to the right level. * a rel, thereby forcing them up the join tree to the right level.
* *
* To ease the calculation of these values, distribute_quals_to_rels() returns * To ease the calculation of these values, distribute_quals_to_rels() returns
* the list of Relids involved in its own level of join. This is just an * the list of base Relids involved in its own level of join. This is just an
* internal convenience; no outside callers pay attention to the result. * internal convenience; no outside callers pay attention to the result.
*/ */
Relids Relids
...@@ -302,7 +335,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels) ...@@ -302,7 +335,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
foreach(relid, rels) foreach(relid, rels)
{ {
int relno = lfirsti(relid); int relno = lfirsti(relid);
RelOptInfo *rel = build_base_rel(root, relno); RelOptInfo *rel = find_base_rel(root, relno);
/* /*
* Since we do this bottom-up, any outer-rels previously marked * Since we do this bottom-up, any outer-rels previously marked
...@@ -382,11 +415,41 @@ distribute_qual_to_rels(Query *root, Node *clause, ...@@ -382,11 +415,41 @@ distribute_qual_to_rels(Query *root, Node *clause,
clause_get_relids_vars(clause, &relids, &vars); clause_get_relids_vars(clause, &relids, &vars);
/* /*
* Cross-check: clause should contain no relids not within its scope. * The clause might contain some join alias vars; if so, we want to
* Otherwise the parser messed up. * remove the join otherrelids from relids and add the referent joins'
* scope lists instead (thus ensuring that the clause can be evaluated
* no lower than that join node). We rely here on the marking done
* earlier by add_base_rels_to_query.
*
* We can combine this step with a cross-check that the clause contains
* no relids not within its scope. If the first crosscheck succeeds,
* the clause contains no aliases and we needn't look more closely.
*/ */
if (!is_subseti(relids, qualscope))
{
Relids newrelids = NIL;
List *relid;
foreach(relid, relids)
{
RelOptInfo *rel = find_other_rel(root, lfirsti(relid));
if (rel && rel->outerjoinset)
{
/* this relid is for a join RTE */
newrelids = set_unioni(newrelids, rel->outerjoinset);
}
else
{
/* this relid is for a true baserel */
newrelids = lappendi(newrelids, lfirsti(relid));
}
}
relids = newrelids;
/* Now repeat the crosscheck */
if (!is_subseti(relids, qualscope)) if (!is_subseti(relids, qualscope))
elog(ERROR, "JOIN qualification may not refer to other relations"); elog(ERROR, "JOIN qualification may not refer to other relations");
}
/* /*
* If the clause is variable-free, we force it to be evaluated at its * If the clause is variable-free, we force it to be evaluated at its
...@@ -439,7 +502,7 @@ distribute_qual_to_rels(Query *root, Node *clause, ...@@ -439,7 +502,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
can_be_equijoin = true; can_be_equijoin = true;
foreach(relid, relids) foreach(relid, relids)
{ {
RelOptInfo *rel = build_base_rel(root, lfirsti(relid)); RelOptInfo *rel = find_base_rel(root, lfirsti(relid));
if (rel->outerjoinset && if (rel->outerjoinset &&
!is_subseti(rel->outerjoinset, relids)) !is_subseti(rel->outerjoinset, relids))
...@@ -475,7 +538,7 @@ distribute_qual_to_rels(Query *root, Node *clause, ...@@ -475,7 +538,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
* There is only one relation participating in 'clause', so * There is only one relation participating in 'clause', so
* 'clause' is a restriction clause for that relation. * 'clause' is a restriction clause for that relation.
*/ */
RelOptInfo *rel = build_base_rel(root, lfirsti(relids)); RelOptInfo *rel = find_base_rel(root, lfirsti(relids));
/* /*
* Check for a "mergejoinable" clause even though it's not a join * Check for a "mergejoinable" clause even though it's not a join
...@@ -595,7 +658,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, ...@@ -595,7 +658,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
* Find or make the joininfo node for this combination of rels, * Find or make the joininfo node for this combination of rels,
* and add the restrictinfo node to it. * and add the restrictinfo node to it.
*/ */
joininfo = find_joininfo_node(build_base_rel(root, cur_relid), joininfo = find_joininfo_node(find_base_rel(root, cur_relid),
unjoined_relids); unjoined_relids);
joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo, joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
restrictinfo); restrictinfo);
...@@ -640,9 +703,6 @@ process_implied_equality(Query *root, Node *item1, Node *item2, ...@@ -640,9 +703,6 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
* If both vars belong to same rel, we need to look at that rel's * If both vars belong to same rel, we need to look at that rel's
* baserestrictinfo list. If different rels, each will have a * baserestrictinfo list. If different rels, each will have a
* joininfo node for the other, and we can scan either list. * joininfo node for the other, and we can scan either list.
*
* All baserel entries should already exist at this point, so use
* find_base_rel not build_base_rel.
*/ */
rel1 = find_base_rel(root, irel1); rel1 = find_base_rel(root, irel1);
if (irel1 == irel2) if (irel1 == irel2)
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.67 2001/10/25 05:49:33 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.68 2002/03/12 00:51:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -176,48 +176,32 @@ subplanner(Query *root, ...@@ -176,48 +176,32 @@ subplanner(Query *root,
List *flat_tlist, List *flat_tlist,
double tuple_fraction) double tuple_fraction)
{ {
List *joined_rels;
List *brel;
RelOptInfo *final_rel; RelOptInfo *final_rel;
Plan *resultplan; Plan *resultplan;
Path *cheapestpath; Path *cheapestpath;
Path *presortedpath; Path *presortedpath;
/* /* init lists to empty */
* Examine the targetlist and qualifications, adding entries to
* base_rel_list as relation references are found (e.g., in the
* qualification, the targetlist, etc.). Restrict and join clauses
* are added to appropriate lists belonging to the mentioned
* relations. We also build lists of equijoined keys for pathkey
* construction.
*/
root->base_rel_list = NIL; root->base_rel_list = NIL;
root->other_rel_list = NIL; root->other_rel_list = NIL;
root->join_rel_list = NIL; root->join_rel_list = NIL;
root->equi_key_list = NIL; root->equi_key_list = NIL;
build_base_rel_tlists(root, flat_tlist);
(void) distribute_quals_to_rels(root, (Node *) root->jointree);
/* /*
* Make sure we have RelOptInfo nodes for all relations to be joined. * Construct RelOptInfo nodes for all base relations in query.
*/ */
joined_rels = add_missing_rels_to_query(root, (Node *) root->jointree); (void) add_base_rels_to_query(root, (Node *) root->jointree);
/* /*
* Check that the join tree includes all the base relations used in * Examine the targetlist and qualifications, adding entries to
* the query --- otherwise, the parser or rewriter messed up. * baserel targetlists for all referenced Vars. Restrict and join
* clauses are added to appropriate lists belonging to the mentioned
* relations. We also build lists of equijoined keys for pathkey
* construction.
*/ */
foreach(brel, root->base_rel_list) build_base_rel_tlists(root, flat_tlist);
{
RelOptInfo *baserel = (RelOptInfo *) lfirst(brel);
int relid = lfirsti(baserel->relids);
if (!ptrMember(baserel, joined_rels)) (void) distribute_quals_to_rels(root, (Node *) root->jointree);
elog(ERROR, "Internal error: no jointree entry for rel %s (%d)",
rt_fetch(relid, root->rtable)->eref->relname, relid);
}
/* /*
* Use the completed lists of equijoined keys to deduce any implied * Use the completed lists of equijoined keys to deduce any implied
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.114 2001/12/10 22:54:12 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.115 2002/03/12 00:51:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -99,7 +99,7 @@ planner(Query *parse) ...@@ -99,7 +99,7 @@ planner(Query *parse)
result_plan->nParamExec = length(PlannerParamVar); result_plan->nParamExec = length(PlannerParamVar);
/* final cleanup of the plan */ /* final cleanup of the plan */
set_plan_references(result_plan); set_plan_references(parse, result_plan);
/* restore state for outer planner, if any */ /* restore state for outer planner, if any */
PlannerQueryLevel = save_PlannerQueryLevel; PlannerQueryLevel = save_PlannerQueryLevel;
...@@ -616,6 +616,9 @@ preprocess_jointree(Query *parse, Node *jtnode) ...@@ -616,6 +616,9 @@ preprocess_jointree(Query *parse, Node *jtnode)
static Node * static Node *
preprocess_expression(Query *parse, Node *expr, int kind) preprocess_expression(Query *parse, Node *expr, int kind)
{ {
bool has_join_rtes;
List *rt;
/* /*
* Simplify constant expressions. * Simplify constant expressions.
* *
...@@ -651,6 +654,29 @@ preprocess_expression(Query *parse, Node *expr, int kind) ...@@ -651,6 +654,29 @@ preprocess_expression(Query *parse, Node *expr, int kind)
if (PlannerQueryLevel > 1) if (PlannerQueryLevel > 1)
expr = SS_replace_correlation_vars(expr); expr = SS_replace_correlation_vars(expr);
/*
* If the query has any join RTEs, try to replace join alias variables
* with base-relation variables, to allow quals to be pushed down.
* We must do this after sublink processing, since it does not recurse
* into sublinks.
*
* The flattening pass is expensive enough that it seems worthwhile to
* scan the rangetable to see if we can avoid it.
*/
has_join_rtes = false;
foreach(rt, parse->rtable)
{
RangeTblEntry *rte = lfirst(rt);
if (rte->rtekind == RTE_JOIN)
{
has_join_rtes = true;
break;
}
}
if (has_join_rtes)
expr = flatten_join_alias_vars(expr, parse, 0);
return expr; return expr;
} }
......
...@@ -9,25 +9,29 @@ ...@@ -9,25 +9,29 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.73 2001/11/05 17:46:26 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.74 2002/03/12 00:51:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include <sys/types.h>
#include "postgres.h" #include "postgres.h"
#include <sys/types.h>
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "optimizer/var.h"
typedef struct typedef struct
{ {
Query *root;
List *outer_tlist; List *outer_tlist;
List *inner_tlist; List *inner_tlist;
Index acceptable_rel; Index acceptable_rel;
Index join_rti;
} join_references_context; } join_references_context;
typedef struct typedef struct
...@@ -38,7 +42,7 @@ typedef struct ...@@ -38,7 +42,7 @@ typedef struct
} replace_vars_with_subplan_refs_context; } replace_vars_with_subplan_refs_context;
static void fix_expr_references(Plan *plan, Node *node); static void fix_expr_references(Plan *plan, Node *node);
static void set_join_references(Join *join); static void set_join_references(Query *root, Join *join);
static void set_uppernode_references(Plan *plan, Index subvarno); static void set_uppernode_references(Plan *plan, Index subvarno);
static Node *join_references_mutator(Node *node, static Node *join_references_mutator(Node *node,
join_references_context *context); join_references_context *context);
...@@ -71,7 +75,7 @@ static bool fix_opids_walker(Node *node, void *context); ...@@ -71,7 +75,7 @@ static bool fix_opids_walker(Node *node, void *context);
* Returns nothing of interest, but modifies internal fields of nodes. * Returns nothing of interest, but modifies internal fields of nodes.
*/ */
void void
set_plan_references(Plan *plan) set_plan_references(Query *root, Plan *plan)
{ {
List *pl; List *pl;
...@@ -115,16 +119,16 @@ set_plan_references(Plan *plan) ...@@ -115,16 +119,16 @@ set_plan_references(Plan *plan)
fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) plan->qual);
/* Recurse into subplan too */ /* Recurse into subplan too */
set_plan_references(((SubqueryScan *) plan)->subplan); set_plan_references(root, ((SubqueryScan *) plan)->subplan);
break; break;
case T_NestLoop: case T_NestLoop:
set_join_references((Join *) plan); set_join_references(root, (Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
break; break;
case T_MergeJoin: case T_MergeJoin:
set_join_references((Join *) plan); set_join_references(root, (Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
...@@ -132,7 +136,7 @@ set_plan_references(Plan *plan) ...@@ -132,7 +136,7 @@ set_plan_references(Plan *plan)
(Node *) ((MergeJoin *) plan)->mergeclauses); (Node *) ((MergeJoin *) plan)->mergeclauses);
break; break;
case T_HashJoin: case T_HashJoin:
set_join_references((Join *) plan); set_join_references(root, (Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
...@@ -186,7 +190,7 @@ set_plan_references(Plan *plan) ...@@ -186,7 +190,7 @@ set_plan_references(Plan *plan)
* recurse into subplans. * recurse into subplans.
*/ */
foreach(pl, ((Append *) plan)->appendplans) foreach(pl, ((Append *) plan)->appendplans)
set_plan_references((Plan *) lfirst(pl)); set_plan_references(root, (Plan *) lfirst(pl));
break; break;
default: default:
elog(ERROR, "set_plan_references: unknown plan type %d", elog(ERROR, "set_plan_references: unknown plan type %d",
...@@ -203,21 +207,21 @@ set_plan_references(Plan *plan) ...@@ -203,21 +207,21 @@ set_plan_references(Plan *plan)
* plan's var nodes against the already-modified nodes of the * plan's var nodes against the already-modified nodes of the
* subplans. * subplans.
*/ */
set_plan_references(plan->lefttree); set_plan_references(root, plan->lefttree);
set_plan_references(plan->righttree); set_plan_references(root, plan->righttree);
foreach(pl, plan->initPlan) foreach(pl, plan->initPlan)
{ {
SubPlan *sp = (SubPlan *) lfirst(pl); SubPlan *sp = (SubPlan *) lfirst(pl);
Assert(IsA(sp, SubPlan)); Assert(IsA(sp, SubPlan));
set_plan_references(sp->plan); set_plan_references(root, sp->plan);
} }
foreach(pl, plan->subPlan) foreach(pl, plan->subPlan)
{ {
SubPlan *sp = (SubPlan *) lfirst(pl); SubPlan *sp = (SubPlan *) lfirst(pl);
Assert(IsA(sp, SubPlan)); Assert(IsA(sp, SubPlan));
set_plan_references(sp->plan); set_plan_references(root, sp->plan);
} }
} }
...@@ -256,7 +260,7 @@ fix_expr_references(Plan *plan, Node *node) ...@@ -256,7 +260,7 @@ fix_expr_references(Plan *plan, Node *node)
* 'join' is a join plan node * 'join' is a join plan node
*/ */
static void static void
set_join_references(Join *join) set_join_references(Query *root, Join *join)
{ {
Plan *outer = join->plan.lefttree; Plan *outer = join->plan.lefttree;
Plan *inner = join->plan.righttree; Plan *inner = join->plan.righttree;
...@@ -264,9 +268,11 @@ set_join_references(Join *join) ...@@ -264,9 +268,11 @@ set_join_references(Join *join)
List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist); List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
join->plan.targetlist = join_references(join->plan.targetlist, join->plan.targetlist = join_references(join->plan.targetlist,
root,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
join->joinrti);
} }
/* /*
...@@ -343,7 +349,8 @@ set_uppernode_references(Plan *plan, Index subvarno) ...@@ -343,7 +349,8 @@ set_uppernode_references(Plan *plan, Index subvarno)
* Creates a new set of targetlist entries or join qual clauses by * Creates a new set of targetlist entries or join qual clauses by
* changing the varno/varattno values of variables in the clauses * changing the varno/varattno values of variables in the clauses
* to reference target list values from the outer and inner join * to reference target list values from the outer and inner join
* relation target lists. * relation target lists. Also, any join alias variables in the
* clauses are expanded into references to their component variables.
* *
* This is used in two different scenarios: a normal join clause, where * This is used in two different scenarios: a normal join clause, where
* all the Vars in the clause *must* be replaced by OUTER or INNER references; * all the Vars in the clause *must* be replaced by OUTER or INNER references;
...@@ -360,21 +367,27 @@ set_uppernode_references(Plan *plan, Index subvarno) ...@@ -360,21 +367,27 @@ set_uppernode_references(Plan *plan, Index subvarno)
* 'inner_tlist' is the target list of the inner join relation, or NIL * '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 * 'acceptable_rel' is either zero or the rangetable index of a relation
* whose Vars may appear in the clause without provoking an error. * 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 * Returns the new expression tree. The original clause structure is
* not modified. * not modified.
*/ */
List * List *
join_references(List *clauses, join_references(List *clauses,
Query *root,
List *outer_tlist, List *outer_tlist,
List *inner_tlist, List *inner_tlist,
Index acceptable_rel) Index acceptable_rel,
Index join_rti)
{ {
join_references_context context; join_references_context context;
context.root = root;
context.outer_tlist = outer_tlist; context.outer_tlist = outer_tlist;
context.inner_tlist = inner_tlist; context.inner_tlist = inner_tlist;
context.acceptable_rel = acceptable_rel; context.acceptable_rel = acceptable_rel;
context.join_rti = join_rti;
return (List *) join_references_mutator((Node *) clauses, &context); return (List *) join_references_mutator((Node *) clauses, &context);
} }
...@@ -387,12 +400,14 @@ join_references_mutator(Node *node, ...@@ -387,12 +400,14 @@ join_references_mutator(Node *node,
if (IsA(node, Var)) if (IsA(node, Var))
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
Var *newvar = (Var *) copyObject(var);
Resdom *resdom; Resdom *resdom;
/* First look for the var in the input tlists */
resdom = tlist_member((Node *) var, context->outer_tlist); resdom = tlist_member((Node *) var, context->outer_tlist);
if (resdom) if (resdom)
{ {
Var *newvar = (Var *) copyObject(var);
newvar->varno = OUTER; newvar->varno = OUTER;
newvar->varattno = resdom->resno; newvar->varattno = resdom->resno;
return (Node *) newvar; return (Node *) newvar;
...@@ -400,18 +415,33 @@ join_references_mutator(Node *node, ...@@ -400,18 +415,33 @@ join_references_mutator(Node *node,
resdom = tlist_member((Node *) var, context->inner_tlist); resdom = tlist_member((Node *) var, context->inner_tlist);
if (resdom) if (resdom)
{ {
Var *newvar = (Var *) copyObject(var);
newvar->varno = INNER; newvar->varno = INNER;
newvar->varattno = resdom->resno; newvar->varattno = resdom->resno;
return (Node *) newvar; return (Node *) newvar;
} }
/* Perhaps it's a join alias that can be resolved to input vars? */
if (var->varno == context->join_rti)
{
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;
}
/* /*
* Var not in either tlist --- either raise an error, or return * No referent found for Var --- either raise an error, or return
* the Var unmodified. * the Var unmodified if it's for acceptable_rel.
*/ */
if (var->varno != context->acceptable_rel) if (var->varno != context->acceptable_rel)
elog(ERROR, "join_references: variable not in subplan target lists"); elog(ERROR, "join_references: variable not in subplan target lists");
return (Node *) newvar; return (Node *) copyObject(var);
} }
return expression_tree_mutator(node, return expression_tree_mutator(node,
join_references_mutator, join_references_mutator,
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.71 2002/03/05 05:10:24 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.72 2002/03/12 00:51:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -673,7 +673,7 @@ List * ...@@ -673,7 +673,7 @@ List *
expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent) expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
{ {
RangeTblEntry *rte = rt_fetch(rti, parse->rtable); RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
Oid parentOID = rte->relid; Oid parentOID;
List *inhOIDs; List *inhOIDs;
List *inhRTIs; List *inhRTIs;
List *l; List *l;
...@@ -681,10 +681,11 @@ expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent) ...@@ -681,10 +681,11 @@ expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
/* Does RT entry allow inheritance? */ /* Does RT entry allow inheritance? */
if (!rte->inh) if (!rte->inh)
return NIL; return NIL;
Assert(parentOID != InvalidOid && rte->subquery == NULL); Assert(rte->rtekind == RTE_RELATION);
/* Always clear the parent's inh flag, see above comments */ /* Always clear the parent's inh flag, see above comments */
rte->inh = false; rte->inh = false;
/* Fast path for common case of childless table */ /* Fast path for common case of childless table */
parentOID = rte->relid;
if (!has_subclass(parentOID)) if (!has_subclass(parentOID))
return NIL; return NIL;
/* Scan for all members of inheritance set */ /* Scan for all members of inheritance set */
...@@ -811,6 +812,19 @@ adjust_inherited_attrs_mutator(Node *node, ...@@ -811,6 +812,19 @@ adjust_inherited_attrs_mutator(Node *node,
rtr->rtindex = context->new_rt_index; rtr->rtindex = context->new_rt_index;
return (Node *) rtr; return (Node *) rtr;
} }
if (IsA(node, JoinExpr))
{
/* Copy the JoinExpr node with correct mutation of subnodes */
JoinExpr *j;
j = (JoinExpr *) expression_tree_mutator(node,
adjust_inherited_attrs_mutator,
(void *) context);
/* now fix JoinExpr's rtindex */
if (j->rtindex == context->old_rt_index)
j->rtindex = context->new_rt_index;
return (Node *) j;
}
/* /*
* We have to process RestrictInfo nodes specially: we do NOT want to * We have to process RestrictInfo nodes specially: we do NOT want to
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.93 2002/01/03 18:01:59 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.94 2002/03/12 00:51:50 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node, ...@@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node,
return true; return true;
if (walker(join->quals, context)) if (walker(join->quals, context))
return true; return true;
if (walker((Node *) join->colvars, context))
return true;
/* /*
* alias clause, using list, colnames list are deemed * alias clause, using list are deemed uninteresting.
* uninteresting.
*/ */
} }
break; break;
...@@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node, ...@@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node,
MUTATE(newnode->larg, join->larg, Node *); MUTATE(newnode->larg, join->larg, Node *);
MUTATE(newnode->rarg, join->rarg, Node *); MUTATE(newnode->rarg, join->rarg, Node *);
MUTATE(newnode->quals, join->quals, Node *); MUTATE(newnode->quals, join->quals, Node *);
MUTATE(newnode->colvars, join->colvars, List *); /* We do not mutate alias or using by default */
/* We do not mutate alias, using, or colnames by default */
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.35 2001/10/25 05:49:34 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -40,26 +40,26 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel, ...@@ -40,26 +40,26 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
/* /*
* build_base_rel * build_base_rel
* Returns relation entry corresponding to 'relid', creating a new one * Construct a new base relation RelOptInfo, and put it in the query's
* if necessary. This is for base relations. * base_rel_list.
*/ */
RelOptInfo * void
build_base_rel(Query *root, int relid) build_base_rel(Query *root, int relid)
{ {
List *rels; List *rels;
RelOptInfo *rel; RelOptInfo *rel;
/* Already made? */ /* Rel should not exist already */
foreach(rels, root->base_rel_list) foreach(rels, root->base_rel_list)
{ {
rel = (RelOptInfo *) lfirst(rels); rel = (RelOptInfo *) lfirst(rels);
/* length(rel->relids) == 1 for all members of base_rel_list */ /* length(rel->relids) == 1 for all members of base_rel_list */
if (lfirsti(rel->relids) == relid) if (lfirsti(rel->relids) == relid)
return rel; elog(ERROR, "build_base_rel: rel already exists");
} }
/* It should not exist as an "other" rel */ /* It should not exist as an "other" rel, either */
foreach(rels, root->other_rel_list) foreach(rels, root->other_rel_list)
{ {
rel = (RelOptInfo *) lfirst(rels); rel = (RelOptInfo *) lfirst(rels);
...@@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid) ...@@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid)
/* and add it to the list */ /* and add it to the list */
root->base_rel_list = lcons(rel, root->base_rel_list); root->base_rel_list = lcons(rel, root->base_rel_list);
return rel;
} }
/* /*
* build_other_rel * build_other_rel
* Returns relation entry corresponding to 'relid', creating a new one * Returns relation entry corresponding to 'relid', creating a new one
* if necessary. This is for 'other' relations, which are just like * if necessary. This is for 'other' relations, which are much like
* base relations except that they live in a different list. * base relations except that they live in a different list.
*/ */
RelOptInfo * RelOptInfo *
...@@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid) ...@@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid)
/* No existing RelOptInfo for this other rel, so make a new one */ /* No existing RelOptInfo for this other rel, so make a new one */
rel = make_base_rel(root, relid); rel = make_base_rel(root, relid);
/* if it's not a join rel, must be a child rel */
if (rel->reloptkind == RELOPT_BASEREL)
rel->reloptkind = RELOPT_OTHER_CHILD_REL;
/* and add it to the list */ /* and add it to the list */
root->other_rel_list = lcons(rel, root->other_rel_list); root->other_rel_list = lcons(rel, root->other_rel_list);
...@@ -127,8 +129,9 @@ static RelOptInfo * ...@@ -127,8 +129,9 @@ static RelOptInfo *
make_base_rel(Query *root, int relid) make_base_rel(Query *root, int relid)
{ {
RelOptInfo *rel = makeNode(RelOptInfo); RelOptInfo *rel = makeNode(RelOptInfo);
Oid relationObjectId; RangeTblEntry *rte = rt_fetch(relid, root->rtable);
rel->reloptkind = RELOPT_BASEREL;
rel->relids = makeListi1(relid); rel->relids = makeListi1(relid);
rel->rows = 0; rel->rows = 0;
rel->width = 0; rel->width = 0;
...@@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid) ...@@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid)
rel->pages = 0; rel->pages = 0;
rel->tuples = 0; rel->tuples = 0;
rel->subplan = NULL; rel->subplan = NULL;
rel->joinrti = 0;
rel->joinrteids = NIL;
rel->baserestrictinfo = NIL; rel->baserestrictinfo = NIL;
rel->baserestrictcost = 0; rel->baserestrictcost = 0;
rel->outerjoinset = NIL; rel->outerjoinset = NIL;
rel->joininfo = NIL; rel->joininfo = NIL;
rel->innerjoin = NIL; rel->innerjoin = NIL;
/* Check rtable to see if it's a plain relation or a subquery */ /* Check type of rtable entry */
relationObjectId = getrelid(relid, root->rtable); switch (rte->rtekind)
if (relationObjectId != InvalidOid)
{ {
/* Plain relation --- retrieve statistics from the system catalogs */ case RTE_RELATION:
{
/* Table --- retrieve statistics from the system catalogs */
bool indexed; bool indexed;
get_relation_info(relationObjectId, get_relation_info(rte->relid,
&indexed, &rel->pages, &rel->tuples); &indexed, &rel->pages, &rel->tuples);
if (indexed) if (indexed)
rel->indexlist = find_secondary_indexes(relationObjectId); rel->indexlist = find_secondary_indexes(rte->relid);
break;
} }
else case RTE_SUBQUERY:
{ /* Subquery --- mark it as such for later processing */
/* subquery --- mark it as such for later processing */
rel->issubquery = true; rel->issubquery = true;
break;
case RTE_JOIN:
/* Join --- must be an otherrel */
rel->reloptkind = RELOPT_OTHER_JOIN_REL;
break;
default:
elog(ERROR, "make_base_rel: unsupported RTE kind %d",
(int) rte->rtekind);
break;
} }
return rel; return rel;
...@@ -203,6 +217,47 @@ find_base_rel(Query *root, int relid) ...@@ -203,6 +217,47 @@ find_base_rel(Query *root, int relid)
return NULL; /* keep compiler quiet */ return NULL; /* keep compiler quiet */
} }
/*
* find_other_rel
* Find an otherrel entry, if one exists for the given relid.
* Return NULL if no entry.
*/
RelOptInfo *
find_other_rel(Query *root, int relid)
{
List *rels;
foreach(rels, root->other_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
if (lfirsti(rel->relids) == relid)
return rel;
}
return NULL;
}
/*
* find_other_rel_for_join
* Look for an otherrel for a join RTE matching the given baserel set.
* Return NULL if no entry.
*/
RelOptInfo *
find_other_rel_for_join(Query *root, List *relids)
{
List *rels;
foreach(rels, root->other_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
if (rel->reloptkind == RELOPT_OTHER_JOIN_REL
&& sameseti(relids, rel->outerjoinset))
return rel;
}
return NULL;
}
/* /*
* find_join_rel * find_join_rel
* Returns relation entry corresponding to 'relids' (a list of RT indexes), * Returns relation entry corresponding to 'relids' (a list of RT indexes),
...@@ -252,6 +307,7 @@ build_join_rel(Query *root, ...@@ -252,6 +307,7 @@ build_join_rel(Query *root,
{ {
List *joinrelids; List *joinrelids;
RelOptInfo *joinrel; RelOptInfo *joinrel;
RelOptInfo *joinrterel;
List *restrictlist; List *restrictlist;
List *new_outer_tlist; List *new_outer_tlist;
List *new_inner_tlist; List *new_inner_tlist;
...@@ -286,6 +342,7 @@ build_join_rel(Query *root, ...@@ -286,6 +342,7 @@ build_join_rel(Query *root,
* Nope, so make one. * Nope, so make one.
*/ */
joinrel = makeNode(RelOptInfo); joinrel = makeNode(RelOptInfo);
joinrel->reloptkind = RELOPT_JOINREL;
joinrel->relids = joinrelids; joinrel->relids = joinrelids;
joinrel->rows = 0; joinrel->rows = 0;
joinrel->width = 0; joinrel->width = 0;
...@@ -299,30 +356,61 @@ build_join_rel(Query *root, ...@@ -299,30 +356,61 @@ build_join_rel(Query *root,
joinrel->pages = 0; joinrel->pages = 0;
joinrel->tuples = 0; joinrel->tuples = 0;
joinrel->subplan = NULL; joinrel->subplan = NULL;
joinrel->joinrti = 0;
joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids),
inner_rel->joinrteids);
joinrel->baserestrictinfo = NIL; joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost = 0; joinrel->baserestrictcost = 0;
joinrel->outerjoinset = NIL; joinrel->outerjoinset = NIL;
joinrel->joininfo = NIL; joinrel->joininfo = NIL;
joinrel->innerjoin = NIL; joinrel->innerjoin = NIL;
/* Is there a join RTE matching this join? */
joinrterel = find_other_rel_for_join(root, joinrelids);
if (joinrterel)
{
/* Yes, remember its RT index */
joinrel->joinrti = lfirsti(joinrterel->relids);
joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids);
}
/* /*
* Create a new tlist by removing irrelevant elements from both tlists * Create a new tlist by removing irrelevant elements from both tlists
* of the outer and inner join relations and then merging the results * of the outer and inner join relations and then merging the results
* together. * together.
* *
* XXX right now we don't remove any irrelevant elements, we just
* append the two tlists together. Someday consider pruning vars from the
* join's targetlist if they are needed only to evaluate restriction
* clauses of this join, and will never be accessed at higher levels of
* the plantree.
*
* NOTE: the tlist order for a join rel will depend on which pair of * NOTE: the tlist order for a join rel will depend on which pair of
* outer and inner rels we first try to build it from. But the * outer and inner rels we first try to build it from. But the
* contents should be the same regardless. * contents should be the same regardless.
*
* XXX someday: consider pruning vars from the join's targetlist if they
* are needed only to evaluate restriction clauses of this join, and
* will never be accessed at higher levels of the plantree.
*/ */
new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1); new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1);
new_inner_tlist = new_join_tlist(inner_rel->targetlist, new_inner_tlist = new_join_tlist(inner_rel->targetlist,
length(new_outer_tlist) + 1); length(new_outer_tlist) + 1);
joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist); joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
/*
* If there are any alias variables attached to the matching join RTE,
* attach them to the tlist too, so that they will be evaluated for use
* at higher plan levels.
*/
if (joinrterel)
{
List *jrtetl;
foreach(jrtetl, joinrterel->targetlist)
{
TargetEntry *jrtete = lfirst(jrtetl);
add_var_to_tlist(joinrel, (Var *) jrtete->expr);
}
}
/* /*
* Construct restrict and join clause lists for the new joinrel. (The * Construct restrict and join clause lists for the new joinrel. (The
* caller might or might not need the restrictlist, but I need it * caller might or might not need the restrictlist, but I need it
......
...@@ -8,15 +8,18 @@ ...@@ -8,15 +8,18 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.33 2001/10/25 05:49:34 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.34 2002/03/12 00:51:51 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "nodes/makefuncs.h"
#include "nodes/plannodes.h" #include "nodes/plannodes.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/var.h" #include "optimizer/var.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
typedef struct typedef struct
...@@ -38,6 +41,12 @@ typedef struct ...@@ -38,6 +41,12 @@ typedef struct
bool includeUpperVars; bool includeUpperVars;
} pull_var_clause_context; } pull_var_clause_context;
typedef struct
{
Query *root;
int expandRTI;
} flatten_join_alias_vars_context;
static bool pull_varnos_walker(Node *node, static bool pull_varnos_walker(Node *node,
pull_varnos_context *context); pull_varnos_context *context);
static bool contain_var_reference_walker(Node *node, static bool contain_var_reference_walker(Node *node,
...@@ -45,6 +54,10 @@ static bool contain_var_reference_walker(Node *node, ...@@ -45,6 +54,10 @@ static bool contain_var_reference_walker(Node *node,
static bool contain_var_clause_walker(Node *node, void *context); static bool contain_var_clause_walker(Node *node, void *context);
static bool pull_var_clause_walker(Node *node, static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context); pull_var_clause_context *context);
static Node *flatten_join_alias_vars_mutator(Node *node,
flatten_join_alias_vars_context *context);
static Node *flatten_join_alias_var(Var *var, Query *root, int expandRTI);
static Node *find_jointree_item(Node *jtnode, int rtindex);
/* /*
...@@ -297,3 +310,363 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) ...@@ -297,3 +310,363 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
return expression_tree_walker(node, pull_var_clause_walker, return expression_tree_walker(node, pull_var_clause_walker,
(void *) context); (void *) context);
} }
/*
* flatten_join_alias_vars
* Whereever possible, replace Vars that reference JOIN outputs with
* references to the original relation variables instead. This allows
* quals involving such vars to be pushed down. Vars that cannot be
* simplified to non-join Vars are replaced by COALESCE expressions
* if they have varno = expandRTI, and are left as JOIN RTE references
* otherwise. (Pass expandRTI = 0 to prevent all COALESCE expansion.)
*
* Upper-level vars (with varlevelsup > 0) are ignored; normally there
* should not be any by the time this routine is called.
*
* Does not examine subqueries, therefore must only be used after reduction
* of sublinks to subplans!
*/
Node *
flatten_join_alias_vars(Node *node, Query *root, int expandRTI)
{
flatten_join_alias_vars_context context;
context.root = root;
context.expandRTI = expandRTI;
return flatten_join_alias_vars_mutator(node, &context);
}
static Node *
flatten_join_alias_vars_mutator(Node *node,
flatten_join_alias_vars_context *context)
{
if (node == NULL)
return NULL;
if (IsA(node, Var))
{
Var *var = (Var *) node;
if (var->varlevelsup != 0)
return node; /* no need to copy, really */
return flatten_join_alias_var(var, context->root, context->expandRTI);
}
return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
(void *) context);
}
static Node *
flatten_join_alias_var(Var *var, Query *root, int expandRTI)
{
Index varno = var->varno;
AttrNumber varattno = var->varattno;
Oid vartype = var->vartype;
int32 vartypmod = var->vartypmod;
JoinExpr *jexpr = NULL;
/*
* Loop to cope with joins of joins
*/
for (;;)
{
RangeTblEntry *rte = rt_fetch(varno, root->rtable);
Index leftrti,
rightrti;
AttrNumber leftattno,
rightattno;
RangeTblEntry *subrte;
Oid subtype;
int32 subtypmod;
if (rte->rtekind != RTE_JOIN)
break; /* reached a non-join RTE */
/*
* Find the RT indexes of the left and right children of the
* join node. We have to search the join tree to do this,
* which is a major pain in the neck --- but keeping RT indexes
* in other RT entries is worse, because it makes modifying
* querytrees difficult. (Perhaps we can improve on the
* rangetable/jointree datastructure someday.) One thing we
* can do is avoid repeated searches while tracing a single
* variable down to its baserel.
*/
if (jexpr == NULL)
jexpr = (JoinExpr *)
find_jointree_item((Node *) root->jointree, varno);
if (jexpr == NULL ||
!IsA(jexpr, JoinExpr) ||
jexpr->rtindex != varno)
elog(ERROR, "flatten_join_alias_var: failed to find JoinExpr");
if (IsA(jexpr->larg, RangeTblRef))
leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
else if (IsA(jexpr->larg, JoinExpr))
leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
else
{
elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
leftrti = 0; /* keep compiler quiet */
}
if (IsA(jexpr->rarg, RangeTblRef))
rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
else if (IsA(jexpr->rarg, JoinExpr))
rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
else
{
elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
rightrti = 0; /* keep compiler quiet */
}
/*
* See if the join var is from the left side, the right side,
* or both (ie, it is a USING/NATURAL JOIN merger column).
*/
Assert(varattno > 0);
leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
if (leftattno && rightattno)
{
/*
* Var is a merge var. If a left or right join, we can replace
* it by the left or right input var respectively; we only need
* a COALESCE for a full join. However, beware of the possibility
* that there's been a type promotion to make the input vars
* compatible; do not replace a var by one of a different type!
*/
if (rte->jointype == JOIN_INNER ||
rte->jointype == JOIN_LEFT)
{
subrte = rt_fetch(leftrti, root->rtable);
get_rte_attribute_type(subrte, leftattno,
&subtype, &subtypmod);
if (vartype == subtype && vartypmod == subtypmod)
{
varno = leftrti;
varattno = leftattno;
jexpr = (JoinExpr *) jexpr->larg;
continue;
}
}
if (rte->jointype == JOIN_INNER ||
rte->jointype == JOIN_RIGHT)
{
subrte = rt_fetch(rightrti, root->rtable);
get_rte_attribute_type(subrte, rightattno,
&subtype, &subtypmod);
if (vartype == subtype && vartypmod == subtypmod)
{
varno = rightrti;
varattno = rightattno;
jexpr = (JoinExpr *) jexpr->rarg;
continue;
}
}
/*
* This var cannot be substituted directly, only with a COALESCE.
* Do so only if it belongs to the particular join indicated by
* the caller.
*/
if (varno != expandRTI)
break;
{
Node *l_var,
*r_var;
CaseExpr *c = makeNode(CaseExpr);
CaseWhen *w = makeNode(CaseWhen);
NullTest *n = makeNode(NullTest);
subrte = rt_fetch(leftrti, root->rtable);
get_rte_attribute_type(subrte, leftattno,
&subtype, &subtypmod);
l_var = (Node *) makeVar(leftrti,
leftattno,
subtype,
subtypmod,
0);
if (subtype != vartype)
{
l_var = coerce_type(NULL, l_var, subtype,
vartype, vartypmod);
l_var = coerce_type_typmod(NULL, l_var,
vartype, vartypmod);
}
else if (subtypmod != vartypmod)
l_var = coerce_type_typmod(NULL, l_var,
vartype, vartypmod);
subrte = rt_fetch(rightrti, root->rtable);
get_rte_attribute_type(subrte, rightattno,
&subtype, &subtypmod);
r_var = (Node *) makeVar(rightrti,
rightattno,
subtype,
subtypmod,
0);
if (subtype != vartype)
{
r_var = coerce_type(NULL, r_var, subtype,
vartype, vartypmod);
r_var = coerce_type_typmod(NULL, r_var,
vartype, vartypmod);
}
else if (subtypmod != vartypmod)
r_var = coerce_type_typmod(NULL, r_var,
vartype, vartypmod);
n->arg = l_var;
n->nulltesttype = IS_NOT_NULL;
w->expr = (Node *) n;
w->result = l_var;
c->casetype = vartype;
c->args = makeList1(w);
c->defresult = r_var;
return (Node *) c;
}
}
else if (leftattno)
{
/* Here we do not need to check the type */
varno = leftrti;
varattno = leftattno;
jexpr = (JoinExpr *) jexpr->larg;
}
else
{
Assert(rightattno);
/* Here we do not need to check the type */
varno = rightrti;
varattno = rightattno;
jexpr = (JoinExpr *) jexpr->rarg;
}
}
/*
* When we fall out of the loop, we've reached the base Var.
*/
return (Node *) makeVar(varno,
varattno,
vartype,
vartypmod,
0);
}
/*
* Given a join alias Var, construct Vars for the two input vars it directly
* depends on. Note that this should *only* be called for merger alias Vars.
* In practice it is only used for Vars that got past flatten_join_alias_vars.
*/
void
build_join_alias_subvars(Query *root, Var *aliasvar,
Var **leftsubvar, Var **rightsubvar)
{
Index varno = aliasvar->varno;
AttrNumber varattno = aliasvar->varattno;
RangeTblEntry *rte;
JoinExpr *jexpr;
Index leftrti,
rightrti;
AttrNumber leftattno,
rightattno;
RangeTblEntry *subrte;
Oid subtype;
int32 subtypmod;
Assert(aliasvar->varlevelsup == 0);
rte = rt_fetch(varno, root->rtable);
Assert(rte->rtekind == RTE_JOIN);
/*
* Find the RT indexes of the left and right children of the
* join node.
*/
jexpr = (JoinExpr *) find_jointree_item((Node *) root->jointree, varno);
if (jexpr == NULL ||
!IsA(jexpr, JoinExpr) ||
jexpr->rtindex != varno)
elog(ERROR, "build_join_alias_subvars: failed to find JoinExpr");
if (IsA(jexpr->larg, RangeTblRef))
leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
else if (IsA(jexpr->larg, JoinExpr))
leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
else
{
elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
leftrti = 0; /* keep compiler quiet */
}
if (IsA(jexpr->rarg, RangeTblRef))
rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
else if (IsA(jexpr->rarg, JoinExpr))
rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
else
{
elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
rightrti = 0; /* keep compiler quiet */
}
Assert(varattno > 0);
leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
if (!(leftattno && rightattno))
elog(ERROR, "build_join_alias_subvars: non-merger variable");
subrte = rt_fetch(leftrti, root->rtable);
get_rte_attribute_type(subrte, leftattno,
&subtype, &subtypmod);
*leftsubvar = makeVar(leftrti,
leftattno,
subtype,
subtypmod,
0);
subrte = rt_fetch(rightrti, root->rtable);
get_rte_attribute_type(subrte, rightattno,
&subtype, &subtypmod);
*rightsubvar = makeVar(rightrti,
rightattno,
subtype,
subtypmod,
0);
}
/*
* Find jointree item matching the specified RT index
*/
static Node *
find_jointree_item(Node *jtnode, int rtindex)
{
if (jtnode == NULL)
return NULL;
if (IsA(jtnode, RangeTblRef))
{
if (((RangeTblRef *) jtnode)->rtindex == rtindex)
return jtnode;
}
else if (IsA(jtnode, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode;
List *l;
foreach(l, f->fromlist)
{
jtnode = find_jointree_item(lfirst(l), rtindex);
if (jtnode)
return jtnode;
}
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
if (j->rtindex == rtindex)
return jtnode;
jtnode = find_jointree_item(j->larg, rtindex);
if (jtnode)
return jtnode;
jtnode = find_jointree_item(j->rarg, rtindex);
if (jtnode)
return jtnode;
}
else
elog(ERROR, "find_jointree_item: unexpected node type %d",
nodeTag(jtnode));
return NULL;
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.219 2002/03/10 06:02:23 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.220 2002/03/12 00:51:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2045,10 +2045,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2045,10 +2045,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
Node *node; Node *node;
List *lefttl, List *lefttl,
*dtlist, *dtlist,
*targetvars, *colMods,
*targetnames, *targetnames,
*sv_namespace; *sv_namespace,
JoinExpr *jnode; *sv_rtable;
RangeTblEntry *jrte;
RangeTblRef *jrtr;
int tllen; int tllen;
qry->commandType = CMD_SELECT; qry->commandType = CMD_SELECT;
...@@ -2115,12 +2117,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2115,12 +2117,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/* /*
* Generate dummy targetlist for outer query using column names of * Generate dummy targetlist for outer query using column names of
* leftmost select and common datatypes of topmost set operation. Also * leftmost select and common datatypes of topmost set operation. Also
* make lists of the dummy vars and their names for use in parsing * make a list of the column names for use in parsing ORDER BY.
* ORDER BY. *
* XXX colMods is a hack to provide a dummy typmod list below. We
* should probably keep track of common typmod instead.
*/ */
qry->targetList = NIL; qry->targetList = NIL;
targetvars = NIL;
targetnames = NIL; targetnames = NIL;
colMods = NIL;
lefttl = leftmostQuery->targetList; lefttl = leftmostQuery->targetList;
foreach(dtlist, sostmt->colTypes) foreach(dtlist, sostmt->colTypes)
{ {
...@@ -2135,15 +2139,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2135,15 +2139,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
-1, -1,
colName, colName,
false); false);
expr = (Node *) makeVar(leftmostRTI, expr = (Node *) makeVar(1,
leftResdom->resno, leftResdom->resno,
colType, colType,
-1, -1,
0); 0);
qry->targetList = lappend(qry->targetList, qry->targetList = lappend(qry->targetList,
makeTargetEntry(resdom, expr)); makeTargetEntry(resdom, expr));
targetvars = lappend(targetvars, expr);
targetnames = lappend(targetnames, makeString(colName)); targetnames = lappend(targetnames, makeString(colName));
colMods = lappendi(colMods, -1);
lefttl = lnext(lefttl); lefttl = lnext(lefttl);
} }
...@@ -2190,7 +2194,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2190,7 +2194,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/* /*
* As a first step towards supporting sort clauses that are * As a first step towards supporting sort clauses that are
* expressions using the output columns, generate a namespace entry * expressions using the output columns, generate a namespace entry
* that makes the output columns visible. A JoinExpr node is handy * that makes the output columns visible. A Join RTE node is handy
* for this, since we can easily control the Vars generated upon * for this, since we can easily control the Vars generated upon
* matches. * matches.
* *
...@@ -2198,12 +2202,23 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2198,12 +2202,23 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
* "ORDER BY upper(foo)" will draw the right error message rather than * "ORDER BY upper(foo)" will draw the right error message rather than
* "foo not found". * "foo not found".
*/ */
jnode = makeNode(JoinExpr); jrte = addRangeTableEntryForJoin(NULL,
jnode->colnames = targetnames; targetnames,
jnode->colvars = targetvars; JOIN_INNER,
sostmt->colTypes,
colMods,
NIL,
NIL,
NULL,
true);
jrtr = makeNode(RangeTblRef);
jrtr->rtindex = 1;
sv_rtable = pstate->p_rtable;
pstate->p_rtable = makeList1(jrte);
sv_namespace = pstate->p_namespace; sv_namespace = pstate->p_namespace;
pstate->p_namespace = makeList1(jnode); pstate->p_namespace = makeList1(jrtr);
/* /*
* For now, we don't support resjunk sort clauses on the output of a * For now, we don't support resjunk sort clauses on the output of a
...@@ -2218,6 +2233,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2218,6 +2233,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList); qry->targetList);
pstate->p_namespace = sv_namespace; pstate->p_namespace = sv_namespace;
pstate->p_rtable = sv_rtable;
if (tllen != length(qry->targetList)) if (tllen != length(qry->targetList))
elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns"); elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns");
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.83 2001/10/25 05:49:37 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.84 2002/03/12 00:51:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -503,7 +503,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -503,7 +503,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
*res_colnames, *res_colnames,
*l_colvars, *l_colvars,
*r_colvars, *r_colvars,
*res_colvars; *coltypes,
*coltypmods,
*leftcolnos,
*rightcolnos;
Index leftrti,
rightrti;
RangeTblEntry *rte;
/* /*
* Recursively process the left and right subtrees * Recursively process the left and right subtrees
...@@ -525,39 +531,32 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -525,39 +531,32 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
/* /*
* Extract column name and var lists from both subtrees * Extract column name and var lists from both subtrees
*
* Note: expandRTE returns new lists, safe for me to modify
*/ */
if (IsA(j->larg, JoinExpr)) if (IsA(j->larg, RangeTblRef))
{ leftrti = ((RangeTblRef *) j->larg)->rtindex;
/* Make a copy of the subtree's lists so we can modify! */ else if (IsA(j->larg, JoinExpr))
l_colnames = copyObject(((JoinExpr *) j->larg)->colnames); leftrti = ((JoinExpr *) j->larg)->rtindex;
l_colvars = copyObject(((JoinExpr *) j->larg)->colvars);
}
else else
{ {
RangeTblEntry *rte; elog(ERROR, "transformFromClauseItem: unexpected subtree type");
leftrti = 0; /* keep compiler quiet */
Assert(IsA(j->larg, RangeTblRef));
rte = rt_fetch(((RangeTblRef *) j->larg)->rtindex,
pstate->p_rtable);
expandRTE(pstate, rte, &l_colnames, &l_colvars);
/* expandRTE returns new lists, so no need for copyObject */
}
if (IsA(j->rarg, JoinExpr))
{
/* Make a copy of the subtree's lists so we can modify! */
r_colnames = copyObject(((JoinExpr *) j->rarg)->colnames);
r_colvars = copyObject(((JoinExpr *) j->rarg)->colvars);
} }
rte = rt_fetch(leftrti, pstate->p_rtable);
expandRTE(pstate, rte, &l_colnames, &l_colvars);
if (IsA(j->rarg, RangeTblRef))
rightrti = ((RangeTblRef *) j->rarg)->rtindex;
else if (IsA(j->rarg, JoinExpr))
rightrti = ((JoinExpr *) j->rarg)->rtindex;
else else
{ {
RangeTblEntry *rte; elog(ERROR, "transformFromClauseItem: unexpected subtree type");
rightrti = 0; /* keep compiler quiet */
Assert(IsA(j->rarg, RangeTblRef));
rte = rt_fetch(((RangeTblRef *) j->rarg)->rtindex,
pstate->p_rtable);
expandRTE(pstate, rte, &r_colnames, &r_colvars);
/* expandRTE returns new lists, so no need for copyObject */
} }
rte = rt_fetch(rightrti, pstate->p_rtable);
expandRTE(pstate, rte, &r_colnames, &r_colvars);
/* /*
* Natural join does not explicitly specify columns; must generate * Natural join does not explicitly specify columns; must generate
...@@ -604,7 +603,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -604,7 +603,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
* Now transform the join qualifications, if any. * Now transform the join qualifications, if any.
*/ */
res_colnames = NIL; res_colnames = NIL;
res_colvars = NIL; coltypes = NIL;
coltypmods = NIL;
leftcolnos = NIL;
rightcolnos = NIL;
if (j->using) if (j->using)
{ {
...@@ -624,9 +626,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -624,9 +626,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
{ {
char *u_colname = strVal(lfirst(ucol)); char *u_colname = strVal(lfirst(ucol));
List *col; List *col;
Node *l_colvar, Var *l_colvar,
*r_colvar, *r_colvar;
*colvar; Oid outcoltype;
int32 outcoltypmod;
int ndx; int ndx;
int l_index = -1; int l_index = -1;
int r_index = -1; int r_index = -1;
...@@ -672,34 +675,28 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -672,34 +675,28 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
res_colnames = lappend(res_colnames, res_colnames = lappend(res_colnames,
nth(l_index, l_colnames)); nth(l_index, l_colnames));
switch (j->jointype) /*
{ * Choose output type if input types are dissimilar.
case JOIN_INNER: */
case JOIN_LEFT: outcoltype = l_colvar->vartype;
colvar = l_colvar; outcoltypmod = l_colvar->vartypmod;
break; if (outcoltype != r_colvar->vartype)
case JOIN_RIGHT:
colvar = r_colvar;
break;
default:
{ {
/* Need COALESCE(l_colvar, r_colvar) */ outcoltype =
CaseExpr *c = makeNode(CaseExpr); select_common_type(makeListi2(l_colvar->vartype,
CaseWhen *w = makeNode(CaseWhen); r_colvar->vartype),
NullTest *n = makeNode(NullTest); "JOIN/USING");
outcoltypmod = -1; /* ie, unknown */
n->arg = l_colvar;
n->nulltesttype = IS_NOT_NULL;
w->expr = (Node *) n;
w->result = l_colvar;
c->args = makeList1(w);
c->defresult = r_colvar;
colvar = transformExpr(pstate, (Node *) c,
EXPR_COLUMN_FIRST);
break;
} }
else if (outcoltypmod != r_colvar->vartypmod)
{
/* same type, but not same typmod */
outcoltypmod = -1; /* ie, unknown */
} }
res_colvars = lappend(res_colvars, colvar); coltypes = lappendi(coltypes, outcoltype);
coltypmods = lappendi(coltypmods, outcoltypmod);
leftcolnos = lappendi(leftcolnos, l_index+1);
rightcolnos = lappendi(rightcolnos, r_index+1);
} }
j->quals = transformJoinUsingClause(pstate, j->quals = transformJoinUsingClause(pstate,
...@@ -724,30 +721,53 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -724,30 +721,53 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
r_colnames, r_colvars, r_colnames, r_colvars,
&r_colnames, &r_colvars); &r_colnames, &r_colvars);
res_colnames = nconc(res_colnames, l_colnames); res_colnames = nconc(res_colnames, l_colnames);
res_colvars = nconc(res_colvars, l_colvars); 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_colnames = nconc(res_colnames, r_colnames); res_colnames = nconc(res_colnames, r_colnames);
res_colvars = nconc(res_colvars, r_colvars); 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);
}
/* /*
* Process alias (AS clause), if any. * Check alias (AS clause), if any.
*/ */
if (j->alias) if (j->alias)
{ {
/*
* If a column alias list is specified, substitute the alias
* names into my output-column list
*/
if (j->alias->attrs != NIL) if (j->alias->attrs != NIL)
{ {
if (length(j->alias->attrs) != length(res_colnames)) if (length(j->alias->attrs) > length(res_colnames))
elog(ERROR, "Column alias list for \"%s\" has wrong number of entries (need %d)", elog(ERROR, "Column alias list for \"%s\" has too many entries",
j->alias->relname, length(res_colnames)); j->alias->relname);
res_colnames = j->alias->attrs;
} }
} }
j->colnames = res_colnames; /*
j->colvars = res_colvars; * Now build an RTE for the result of the join
*/
rte = addRangeTableEntryForJoin(pstate, res_colnames,
j->jointype,
coltypes, coltypmods,
leftcolnos, rightcolnos,
j->alias, true);
/* assume new rte is at end */
j->rtindex = length(pstate->p_rtable);
Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable));
return (Node *) j; return (Node *) j;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.107 2002/03/07 16:35:36 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.108 2002/03/12 00:51:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -646,7 +646,7 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence) ...@@ -646,7 +646,7 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
* appear * appear
*/ */
if (ident->indirection == NIL && if (ident->indirection == NIL &&
refnameRangeOrJoinEntry(pstate, ident->name, &sublevels_up) != NULL) refnameRangeTblEntry(pstate, ident->name, &sublevels_up) != NULL)
{ {
ident->isRel = TRUE; ident->isRel = TRUE;
result = (Node *) ident; result = (Node *) ident;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.116 2002/02/19 20:11:15 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.117 2002/03/12 00:51:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -316,7 +316,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, ...@@ -316,7 +316,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
{ {
RangeTblEntry *rte; RangeTblEntry *rte;
int vnum; int vnum;
Node *rteorjoin;
int sublevels_up; int sublevels_up;
/* /*
...@@ -324,49 +323,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, ...@@ -324,49 +323,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
*/ */
refname = ((Ident *) arg)->name; refname = ((Ident *) arg)->name;
rteorjoin = refnameRangeOrJoinEntry(pstate, refname, rte = refnameRangeTblEntry(pstate, refname,
&sublevels_up); &sublevels_up);
if (rteorjoin == NULL) if (rte == NULL)
rte = addImplicitRTE(pstate, refname); rte = addImplicitRTE(pstate, refname);
else if (IsA(rteorjoin, RangeTblEntry))
rte = (RangeTblEntry *) rteorjoin;
else if (IsA(rteorjoin, JoinExpr))
{
/*
* The relation name refers to a join. We can't support
* functions on join tuples (since we don't have a named
* type for the join tuples), so error out.
*/
if (nargs == 1)
{
/*
* We have f(x) or more likely x.f where x is a join
* and f is not one of the attribute names of the join
* (else we'd have recognized it above). Give an
* appropriately vague error message. Would be nicer
* to know which syntax was used...
*/
elog(ERROR, "No such attribute or function %s.%s",
refname, funcname);
}
else
{
/*
* There are multiple arguments, so it must be a
* function call.
*/
elog(ERROR, "Cannot pass result of join %s to a function",
refname);
}
rte = NULL; /* keep compiler quiet */
}
else
{
elog(ERROR, "ParseFuncOrColumn: unexpected node type %d",
nodeTag(rteorjoin));
rte = NULL; /* keep compiler quiet */
}
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
...@@ -379,11 +340,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, ...@@ -379,11 +340,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
* sizeof(Pointer) to signal that the runtime representation * sizeof(Pointer) to signal that the runtime representation
* will be a pointer not an Oid. * will be a pointer not an Oid.
*/ */
if (rte->relname == NULL) if (rte->rtekind != RTE_RELATION)
{ {
/* /*
* RTE is a subselect; must fail for lack of a specific * RTE is a join or subselect; must fail for lack of a
* type * named tuple type
*/ */
if (nargs == 1) if (nargs == 1)
{ {
...@@ -397,7 +358,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, ...@@ -397,7 +358,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
} }
else else
{ {
elog(ERROR, "Cannot pass result of sub-select %s to a function", elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
refname); refname);
} }
} }
......
...@@ -8,21 +8,22 @@ ...@@ -8,21 +8,22 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.58 2002/03/06 06:09:54 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.59 2002/03/12 00:51:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h"
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <float.h> #include <float.h>
#include "postgres.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "fmgr.h" #include "fmgr.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_node.h" #include "parser/parse_node.h"
...@@ -165,51 +166,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno) ...@@ -165,51 +166,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
{ {
int vnum, int vnum,
sublevels_up; sublevels_up;
Oid vartypeid = 0; Oid vartypeid;
int32 type_mod = 0; int32 type_mod;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod);
if (rte->relid != InvalidOid)
{
/* Plain relation RTE --- get the attribute's type info */
HeapTuple tp;
Form_pg_attribute att_tup;
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attrno),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
rte->relname, attrno);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
vartypeid = att_tup->atttypid;
type_mod = att_tup->atttypmod;
ReleaseSysCache(tp);
}
else
{
/* Subselect RTE --- get type info from subselect's tlist */
List *tlistitem;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk || te->resdom->resno != attrno)
continue;
vartypeid = te->resdom->restype;
type_mod = te->resdom->restypmod;
break;
}
/* falling off end of list shouldn't happen... */
if (tlistitem == NIL)
elog(ERROR, "Subquery %s does not have attribute %d",
rte->eref->relname, attrno);
}
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up); return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.62 2002/03/06 06:09:55 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.63 2002/03/12 00:51:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -36,23 +36,20 @@ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, ...@@ -36,23 +36,20 @@ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
char *refname); char *refname);
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname); char *colname);
static Node *scanJoinForColumn(JoinExpr *join, char *colname,
int sublevels_up);
static bool isForUpdate(ParseState *pstate, char *relname); static bool isForUpdate(ParseState *pstate, char *relname);
static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
static int specialAttNum(char *a); static int specialAttNum(char *a);
static void warnAutoRange(ParseState *pstate, char *refname); static void warnAutoRange(ParseState *pstate, char *refname);
/* /*
* refnameRangeOrJoinEntry * refnameRangeTblEntry
* Given a refname, look to see if it matches any RTE or join table. * Given a refname, look to see if it matches any RTE.
* If so, return a pointer to the RangeTblEntry or JoinExpr. * If so, return a pointer to the RangeTblEntry.
* Optionally get its nesting depth (0 = current). If sublevels_up * Optionally get its nesting depth (0 = current). If sublevels_up
* is NULL, only consider items at the current nesting level. * is NULL, only consider items at the current nesting level.
*/ */
Node * RangeTblEntry *
refnameRangeOrJoinEntry(ParseState *pstate, refnameRangeTblEntry(ParseState *pstate,
char *refname, char *refname,
int *sublevels_up) int *sublevels_up)
{ {
...@@ -61,13 +58,19 @@ refnameRangeOrJoinEntry(ParseState *pstate, ...@@ -61,13 +58,19 @@ refnameRangeOrJoinEntry(ParseState *pstate,
while (pstate != NULL) while (pstate != NULL)
{ {
Node *rte; Node *nsnode;
rte = scanNameSpaceForRefname(pstate, nsnode = scanNameSpaceForRefname(pstate,
(Node *) pstate->p_namespace, (Node *) pstate->p_namespace,
refname); refname);
if (rte) if (nsnode)
return rte; {
/* should get an RTE or JoinExpr */
if (IsA(nsnode, RangeTblEntry))
return (RangeTblEntry *) nsnode;
Assert(IsA(nsnode, JoinExpr));
return rt_fetch(((JoinExpr *) nsnode)->rtindex, pstate->p_rtable);
}
pstate = pstate->parentParseState; pstate = pstate->parentParseState;
if (sublevels_up) if (sublevels_up)
...@@ -247,6 +250,12 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up) ...@@ -247,6 +250,12 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
* *
* Side effect: if we find a match, mark the RTE as requiring read access. * Side effect: if we find a match, mark the RTE as requiring read access.
* See comments in setTargetTable(). * See comments in setTargetTable().
*
* NOTE: if the RTE is for a join, marking it as requiring read access does
* nothing. It might seem that we need to propagate the mark to all the
* contained RTEs, but that is not necessary. This is so because a join
* expression can only appear in a FROM clause, and any table named in
* FROM will be marked checkForRead from the beginning.
*/ */
static Node * static Node *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
...@@ -279,10 +288,9 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) ...@@ -279,10 +288,9 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
return result; return result;
/* /*
* If the RTE represents a table (not a sub-select), consider system * If the RTE represents a real table, consider system column names.
* column names.
*/ */
if (rte->relid != InvalidOid) if (rte->rtekind == RTE_RELATION)
{ {
/* quick check to see if name could be a system column */ /* quick check to see if name could be a system column */
attnum = specialAttNum(colname); attnum = specialAttNum(colname);
...@@ -303,44 +311,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) ...@@ -303,44 +311,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
return result; return result;
} }
/*
* scanJoinForColumn
* Search the column names of a single join table for the given name.
* If found, return an appropriate Var node or expression, else return NULL.
* If the name proves ambiguous within this jointable, raise error.
*
* NOTE: unlike scanRTEForColumn, there's no need to worry about forcing
* checkForRead true for the referenced tables. This is so because a join
* expression can only appear in a FROM clause, and any table named in
* FROM will be marked checkForRead from the beginning.
*/
static Node *
scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
{
Node *result = NULL;
int attnum = 0;
List *c;
foreach(c, join->colnames)
{
attnum++;
if (strcmp(strVal(lfirst(c)), colname) == 0)
{
if (result)
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
result = copyObject(nth(attnum - 1, join->colvars));
/*
* If referencing an uplevel join item, we must adjust
* sublevels settings in the copied expression.
*/
if (sublevels_up > 0)
IncrementVarSublevelsUp(result, sublevels_up, 0);
}
}
return result;
}
/* /*
* colnameToVar * colnameToVar
* Search for an unqualified column name. * Search for an unqualified column name.
...@@ -382,9 +352,13 @@ colnameToVar(ParseState *pstate, char *colname) ...@@ -382,9 +352,13 @@ colnameToVar(ParseState *pstate, char *colname)
} }
else if (IsA(nsnode, JoinExpr)) else if (IsA(nsnode, JoinExpr))
{ {
JoinExpr *j = (JoinExpr *) nsnode; int varno = ((JoinExpr *) nsnode)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
newresult = scanJoinForColumn(j, colname, levels_up); /* joins are always inFromCl, so no need to check */
/* use orig_pstate here to get the right sublevels_up */
newresult = scanRTEForColumn(orig_pstate, rte, colname);
} }
else else
elog(ERROR, "colnameToVar: unexpected node type %d", elog(ERROR, "colnameToVar: unexpected node type %d",
...@@ -412,41 +386,26 @@ colnameToVar(ParseState *pstate, char *colname) ...@@ -412,41 +386,26 @@ colnameToVar(ParseState *pstate, char *colname)
/* /*
* qualifiedNameToVar * qualifiedNameToVar
* Search for a qualified column name (refname + column name). * Search for a qualified column name (refname + column name).
* If found, return the appropriate Var node (or expression). * If found, return the appropriate Var node.
* If not found, return NULL. If the name proves ambiguous, raise error. * If not found, return NULL. If the name proves ambiguous, raise error.
*/ */
Node * Node *
qualifiedNameToVar(ParseState *pstate, char *refname, char *colname, qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
bool implicitRTEOK) bool implicitRTEOK)
{ {
Node *result; RangeTblEntry *rte;
Node *rteorjoin;
int sublevels_up; int sublevels_up;
rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up); rte = refnameRangeTblEntry(pstate, refname, &sublevels_up);
if (rteorjoin == NULL) if (rte == NULL)
{ {
if (!implicitRTEOK) if (!implicitRTEOK)
return NULL; return NULL;
rteorjoin = (Node *) addImplicitRTE(pstate, refname); rte = addImplicitRTE(pstate, refname);
sublevels_up = 0;
} }
if (IsA(rteorjoin, RangeTblEntry)) return scanRTEForColumn(pstate, rte, colname);
result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
colname);
else if (IsA(rteorjoin, JoinExpr))
result = scanJoinForColumn((JoinExpr *) rteorjoin,
colname, sublevels_up);
else
{
elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
nodeTag(rteorjoin));
result = NULL; /* keep compiler quiet */
}
return result;
} }
/* /*
...@@ -474,9 +433,9 @@ addRangeTableEntry(ParseState *pstate, ...@@ -474,9 +433,9 @@ addRangeTableEntry(ParseState *pstate,
int numaliases; int numaliases;
int varattno; int varattno;
rte->rtekind = RTE_RELATION;
rte->relname = relname; rte->relname = relname;
rte->alias = alias; rte->alias = alias;
rte->subquery = NULL;
/* /*
* Get the rel's OID. This access also ensures that we have an * Get the rel's OID. This access also ensures that we have an
...@@ -563,6 +522,7 @@ addRangeTableEntryForSubquery(ParseState *pstate, ...@@ -563,6 +522,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
int varattno; int varattno;
List *tlistitem; List *tlistitem;
rte->rtekind = RTE_SUBQUERY;
rte->relname = NULL; rte->relname = NULL;
rte->relid = InvalidOid; rte->relid = InvalidOid;
rte->subquery = subquery; rte->subquery = subquery;
...@@ -621,6 +581,76 @@ addRangeTableEntryForSubquery(ParseState *pstate, ...@@ -621,6 +581,76 @@ addRangeTableEntryForSubquery(ParseState *pstate,
return rte; return rte;
} }
/*
* Add an entry for a join to the pstate's range table (p_rtable).
*
* This is much like addRangeTableEntry() except that it makes a join RTE.
*/
RangeTblEntry *
addRangeTableEntryForJoin(ParseState *pstate,
List *colnames,
JoinType jointype,
List *coltypes,
List *coltypmods,
List *leftcols,
List *rightcols,
Attr *alias,
bool inFromCl)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
Attr *eref;
int numaliases;
rte->rtekind = RTE_JOIN;
rte->relname = NULL;
rte->relid = InvalidOid;
rte->subquery = NULL;
rte->jointype = jointype;
rte->joincoltypes = coltypes;
rte->joincoltypmods = coltypmods;
rte->joinleftcols = leftcols;
rte->joinrightcols = rightcols;
rte->alias = alias;
eref = alias ? (Attr *) copyObject(alias) : makeAttr("unnamed_join", NULL);
numaliases = length(eref->attrs);
/* fill in any unspecified alias columns */
if (numaliases < length(colnames))
{
while (numaliases-- > 0)
colnames = lnext(colnames);
eref->attrs = nconc(eref->attrs, colnames);
}
rte->eref = eref;
/*----------
* Flags:
* - this RTE should be expanded to include descendant tables,
* - this RTE is in the FROM clause,
* - this RTE should be checked for read/write access rights.
*
* Joins are never checked for access rights.
*----------
*/
rte->inh = false; /* never true for joins */
rte->inFromCl = inFromCl;
rte->checkForRead = false;
rte->checkForWrite = false;
rte->checkAsUser = InvalidOid;
/*
* Add completed RTE to pstate's range table list, but not to join
* list nor namespace --- caller must do that if appropriate.
*/
if (pstate != NULL)
pstate->p_rtable = lappend(pstate->p_rtable, rte);
return rte;
}
/* /*
* Has the specified relname been selected FOR UPDATE? * Has the specified relname been selected FOR UPDATE?
*/ */
...@@ -720,15 +750,16 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, ...@@ -720,15 +750,16 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Need the RT index of the entry for creating Vars */ /* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
if (rte->relname) if (rte->rtekind == RTE_RELATION)
{ {
/* Ordinary relation RTE */ /* Ordinary relation RTE */
Relation rel; Relation rel;
int maxattrs; int maxattrs;
int numaliases;
rel = heap_openr(rte->relname, AccessShareLock); rel = heap_openr(rte->relname, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel); maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = length(rte->eref->attrs);
for (varattno = 0; varattno < maxattrs; varattno++) for (varattno = 0; varattno < maxattrs; varattno++)
{ {
...@@ -743,7 +774,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, ...@@ -743,7 +774,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{ {
char *label; char *label;
if (varattno < length(rte->eref->attrs)) if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->attrs)); label = strVal(nth(varattno, rte->eref->attrs));
else else
label = NameStr(attr->attname); label = NameStr(attr->attname);
...@@ -764,7 +795,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, ...@@ -764,7 +795,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
heap_close(rel, AccessShareLock); heap_close(rel, AccessShareLock);
} }
else else if (rte->rtekind == RTE_SUBQUERY)
{ {
/* Subquery RTE */ /* Subquery RTE */
List *aliasp = rte->eref->attrs; List *aliasp = rte->eref->attrs;
...@@ -802,56 +833,63 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, ...@@ -802,56 +833,63 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
} }
} }
} }
} else if (rte->rtekind == RTE_JOIN)
{
/* Join RTE */
List *aliasp = rte->eref->attrs;
List *coltypes = rte->joincoltypes;
List *coltypmods = rte->joincoltypmods;
/* varattno = 0;
* expandRelAttrs - while (aliasp)
* makes a list of TargetEntry nodes for the attributes of the rel {
*/ Assert(coltypes && coltypmods);
List * varattno++;
expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
{
List *name_list,
*var_list;
expandRTE(pstate, rte, &name_list, &var_list); if (colnames)
{
char *label = strVal(lfirst(aliasp));
return expandNamesVars(pstate, name_list, var_list); *colnames = lappend(*colnames, makeString(pstrdup(label)));
} }
/* if (colvars)
* expandJoinAttrs - {
* makes a list of TargetEntry nodes for the attributes of the join Var *varnode;
*/
List *
expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
{
List *vars;
vars = copyObject(join->colvars); varnode = makeVar(rtindex, varattno,
(Oid) lfirsti(coltypes),
(int32) lfirsti(coltypmods),
sublevels_up);
/* *colvars = lappend(*colvars, varnode);
* If referencing an uplevel join item, we must adjust sublevels }
* settings in the copied expression.
*/
if (sublevels_up > 0)
IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0);
return expandNamesVars(pstate, aliasp = lnext(aliasp);
copyObject(join->colnames), coltypes = lnext(coltypes);
vars); coltypmods = lnext(coltypmods);
}
Assert(coltypes == NIL && coltypmods == NIL);
}
else
elog(ERROR, "expandRTE: unsupported RTE kind %d",
(int) rte->rtekind);
} }
/* /*
* expandNamesVars - * expandRelAttrs -
* Workhorse for "*" expansion: produce a list of targetentries * Workhorse for "*" expansion: produce a list of targetentries
* given lists of column names (as String nodes) and var references. * for the attributes of the rte
*/ */
static List * List *
expandNamesVars(ParseState *pstate, List *names, List *vars) expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
{ {
List *names,
*vars;
List *te_list = NIL; List *te_list = NIL;
expandRTE(pstate, rte, &names, &vars);
while (names) while (names)
{ {
char *label = strVal(lfirst(names)); char *label = strVal(lfirst(names));
...@@ -875,22 +913,16 @@ expandNamesVars(ParseState *pstate, List *names, List *vars) ...@@ -875,22 +913,16 @@ expandNamesVars(ParseState *pstate, List *names, List *vars)
return te_list; return te_list;
} }
/* ---------- /*
* get_rte_attribute_name * get_rte_attribute_name
* Get an attribute name from a RangeTblEntry * Get an attribute name from a RangeTblEntry
* *
* This is unlike get_attname() because we use aliases if available. * This is unlike get_attname() because we use aliases if available.
* In particular, it will work on an RTE for a subselect, whereas * In particular, it will work on an RTE for a subselect or join, whereas
* get_attname() only works on real relations. * get_attname() only works on real relations.
* *
* "*" is returned if the given attnum is InvalidAttrNumber --- this case * "*" is returned if the given attnum is InvalidAttrNumber --- this case
* occurs when a Var represents a whole tuple of a relation. * occurs when a Var represents a whole tuple of a relation.
*
* XXX Actually, this is completely bogus, because refnames of RTEs are
* not guaranteed unique, and may not even have scope across the whole
* query. Cleanest fix would be to add refname/attname to Var nodes and
* just print those, rather than indulging in this hack.
* ----------
*/ */
char * char *
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
...@@ -901,7 +933,8 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) ...@@ -901,7 +933,8 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
return "*"; return "*";
/* /*
* If there is an alias, use it * If there is an alias, use it. (This path should always be taken
* for non-relation RTEs.)
*/ */
if (attnum > 0 && attnum <= length(rte->eref->attrs)) if (attnum > 0 && attnum <= length(rte->eref->attrs))
return strVal(nth(attnum - 1, rte->eref->attrs)); return strVal(nth(attnum - 1, rte->eref->attrs));
...@@ -909,9 +942,9 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) ...@@ -909,9 +942,9 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
/* /*
* Can get here for a system attribute (which never has an alias), or * Can get here for a system attribute (which never has an alias), or
* if alias name list is too short (which probably can't happen * if alias name list is too short (which probably can't happen
* anymore). Neither of these cases is valid for a subselect RTE. * anymore). Neither of these cases is valid for a non-relation RTE.
*/ */
if (rte->relid == InvalidOid) if (rte->rtekind != RTE_RELATION)
elog(ERROR, "Invalid attnum %d for rangetable entry %s", elog(ERROR, "Invalid attnum %d for rangetable entry %s",
attnum, rte->eref->relname); attnum, rte->eref->relname);
...@@ -925,6 +958,64 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) ...@@ -925,6 +958,64 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
return attname; return attname;
} }
/*
* get_rte_attribute_type
* Get attribute type information from a RangeTblEntry
*/
void
get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Oid *vartype, int32 *vartypmod)
{
if (rte->rtekind == RTE_RELATION)
{
/* Plain relation RTE --- get the attribute's type info */
HeapTuple tp;
Form_pg_attribute att_tup;
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
rte->relname, attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
}
else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subselect RTE --- get type info from subselect's tlist */
List *tlistitem;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk || te->resdom->resno != attnum)
continue;
*vartype = te->resdom->restype;
*vartypmod = te->resdom->restypmod;
return;
}
/* falling off end of list shouldn't happen... */
elog(ERROR, "Subquery %s does not have attribute %d",
rte->eref->relname, 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);
}
else
elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
(int) rte->rtekind);
}
/* /*
* given relation and att name, return id of variable * given relation and att name, return id of variable
* *
......
...@@ -8,12 +8,13 @@ ...@@ -8,12 +8,13 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.76 2001/11/05 17:46:26 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.77 2002/03/12 00:51:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
...@@ -118,30 +119,16 @@ transformTargetList(ParseState *pstate, List *targetlist) ...@@ -118,30 +119,16 @@ transformTargetList(ParseState *pstate, List *targetlist)
* Target item is relation.*, expand that table (eg. * Target item is relation.*, expand that table (eg.
* SELECT emp.*, dname FROM emp, dept) * SELECT emp.*, dname FROM emp, dept)
*/ */
Node *rteorjoin; RangeTblEntry *rte;
int sublevels_up; int sublevels_up;
rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname, rte = refnameRangeTblEntry(pstate, att->relname,
&sublevels_up); &sublevels_up);
if (rte == NULL)
rte = addImplicitRTE(pstate, att->relname);
if (rteorjoin == NULL)
{
rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
sublevels_up = 0;
}
if (IsA(rteorjoin, RangeTblEntry))
p_target = nconc(p_target,
expandRelAttrs(pstate,
(RangeTblEntry *) rteorjoin));
else if (IsA(rteorjoin, JoinExpr))
p_target = nconc(p_target, p_target = nconc(p_target,
expandJoinAttrs(pstate, expandRelAttrs(pstate, rte));
(JoinExpr *) rteorjoin,
sublevels_up));
else
elog(ERROR, "transformTargetList: unexpected node type %d",
nodeTag(rteorjoin));
} }
else else
{ {
...@@ -405,13 +392,20 @@ ExpandAllTables(ParseState *pstate) ...@@ -405,13 +392,20 @@ ExpandAllTables(ParseState *pstate)
foreach(ns, pstate->p_namespace) foreach(ns, pstate->p_namespace)
{ {
Node *n = (Node *) lfirst(ns); Node *n = (Node *) lfirst(ns);
if (IsA(n, RangeTblRef))
{
RangeTblEntry *rte; RangeTblEntry *rte;
if (IsA(n, RangeTblRef))
rte = rt_fetch(((RangeTblRef *) n)->rtindex, rte = rt_fetch(((RangeTblRef *) n)->rtindex,
pstate->p_rtable); pstate->p_rtable);
else if (IsA(n, JoinExpr))
rte = rt_fetch(((JoinExpr *) n)->rtindex,
pstate->p_rtable);
else
{
elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
"\n\t%s", nodeToString(n));
rte = NULL; /* keep compiler quiet */
}
/* /*
* Ignore added-on relations that were not listed in the FROM * Ignore added-on relations that were not listed in the FROM
...@@ -422,18 +416,6 @@ ExpandAllTables(ParseState *pstate) ...@@ -422,18 +416,6 @@ ExpandAllTables(ParseState *pstate)
target = nconc(target, expandRelAttrs(pstate, rte)); target = nconc(target, expandRelAttrs(pstate, rte));
} }
else if (IsA(n, JoinExpr))
{
/* A newfangled join expression */
JoinExpr *j = (JoinExpr *) n;
/* Currently, a join expr could only have come from FROM. */
target = nconc(target, expandJoinAttrs(pstate, j, 0));
}
else
elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
"\n\t%s", nodeToString(n));
}
/* Check for SELECT *; */ /* Check for SELECT *; */
if (target == NIL) if (target == NIL)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.98 2001/10/25 05:49:41 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.99 2002/03/12 00:51:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -289,6 +289,7 @@ ApplyRetrieveRule(Query *parsetree, ...@@ -289,6 +289,7 @@ ApplyRetrieveRule(Query *parsetree,
*/ */
rte = rt_fetch(rt_index, parsetree->rtable); rte = rt_fetch(rt_index, parsetree->rtable);
rte->rtekind = RTE_SUBQUERY;
rte->relname = NULL; rte->relname = NULL;
rte->relid = InvalidOid; rte->relid = InvalidOid;
rte->subquery = rule_action; rte->subquery = rule_action;
...@@ -354,17 +355,17 @@ markQueryForUpdate(Query *qry, bool skipOldNew) ...@@ -354,17 +355,17 @@ markQueryForUpdate(Query *qry, bool skipOldNew)
(rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO)) (rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
continue; continue;
if (rte->subquery) if (rte->rtekind == RTE_RELATION)
{
/* FOR UPDATE of subquery is propagated to subquery's rels */
markQueryForUpdate(rte->subquery, false);
}
else
{ {
if (!intMember(rti, qry->rowMarks)) if (!intMember(rti, qry->rowMarks))
qry->rowMarks = lappendi(qry->rowMarks, rti); qry->rowMarks = lappendi(qry->rowMarks, rti);
rte->checkForWrite = true; rte->checkForWrite = true;
} }
else if (rte->rtekind == RTE_SUBQUERY)
{
/* FOR UPDATE of subquery is propagated to subquery's rels */
markQueryForUpdate(rte->subquery, false);
}
} }
} }
...@@ -440,12 +441,18 @@ fireRIRrules(Query *parsetree) ...@@ -440,12 +441,18 @@ fireRIRrules(Query *parsetree)
* to do to this level of the query, but we must recurse into the * to do to this level of the query, but we must recurse into the
* subquery to expand any rule references in it. * subquery to expand any rule references in it.
*/ */
if (rte->subquery) if (rte->rtekind == RTE_SUBQUERY)
{ {
rte->subquery = fireRIRrules(rte->subquery); rte->subquery = fireRIRrules(rte->subquery);
continue; continue;
} }
/*
* Joins and other non-relation RTEs can be ignored completely.
*/
if (rte->rtekind != RTE_RELATION)
continue;
/* /*
* If the table is not referenced in the query, then we ignore it. * If the table is not referenced in the query, then we ignore it.
* This prevents infinite expansion loop due to new rtable entries * This prevents infinite expansion loop due to new rtable entries
...@@ -756,6 +763,7 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products) ...@@ -756,6 +763,7 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
result_relation = parsetree->resultRelation; result_relation = parsetree->resultRelation;
Assert(result_relation != 0); Assert(result_relation != 0);
rt_entry = rt_fetch(result_relation, parsetree->rtable); rt_entry = rt_fetch(result_relation, parsetree->rtable);
Assert(rt_entry->rtekind == RTE_RELATION);
/* /*
* This may well be the first access to the result relation during the * This may well be the first access to the result relation during the
...@@ -945,7 +953,7 @@ QueryRewrite(Query *parsetree) ...@@ -945,7 +953,7 @@ QueryRewrite(Query *parsetree)
RangeTblEntry *rte = rt_fetch(query->resultRelation, RangeTblEntry *rte = rt_fetch(query->resultRelation,
query->rtable); query->rtable);
if (rte->subquery) if (rte->rtekind == RTE_SUBQUERY)
{ {
switch (query->commandType) switch (query->commandType)
{ {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.61 2001/11/05 17:46:27 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.62 2002/03/12 00:51:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -101,8 +101,8 @@ checkExprHasSubLink_walker(Node *node, void *context) ...@@ -101,8 +101,8 @@ checkExprHasSubLink_walker(Node *node, void *context)
* *
* Find all Var nodes in the given tree with varlevelsup == sublevels_up, * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
* and increment their varno fields (rangetable indexes) by 'offset'. * and increment their varno fields (rangetable indexes) by 'offset'.
* The varnoold fields are adjusted similarly. Also, RangeTblRef nodes * The varnoold fields are adjusted similarly. Also, RangeTblRef and
* in join trees and setOp trees are adjusted. * JoinExpr nodes in join trees and setOp trees are adjusted.
* *
* NOTE: although this has the form of a walker, we cheat and modify the * NOTE: although this has the form of a walker, we cheat and modify the
* nodes in-place. The given expression tree should have been copied * nodes in-place. The given expression tree should have been copied
...@@ -140,6 +140,14 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) ...@@ -140,6 +140,14 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
/* the subquery itself is visited separately */ /* the subquery itself is visited separately */
return false; return false;
} }
if (IsA(node, JoinExpr))
{
JoinExpr *j = (JoinExpr *) node;
if (context->sublevels_up == 0)
j->rtindex += context->offset;
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
...@@ -200,7 +208,7 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) ...@@ -200,7 +208,7 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
* Find all Var nodes in the given tree belonging to a specific relation * Find all Var nodes in the given tree belonging to a specific relation
* (identified by sublevels_up and rt_index), and change their varno fields * (identified by sublevels_up and rt_index), and change their varno fields
* to 'new_index'. The varnoold fields are changed too. Also, RangeTblRef * to 'new_index'. The varnoold fields are changed too. Also, RangeTblRef
* nodes in join trees and setOp trees are adjusted. * and JoinExpr nodes in join trees and setOp trees are adjusted.
* *
* NOTE: although this has the form of a walker, we cheat and modify the * NOTE: although this has the form of a walker, we cheat and modify the
* nodes in-place. The given expression tree should have been copied * nodes in-place. The given expression tree should have been copied
...@@ -241,6 +249,15 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) ...@@ -241,6 +249,15 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
/* the subquery itself is visited separately */ /* the subquery itself is visited separately */
return false; return false;
} }
if (IsA(node, JoinExpr))
{
JoinExpr *j = (JoinExpr *) node;
if (context->sublevels_up == 0 &&
j->rtindex == context->rt_index)
j->rtindex = context->new_index;
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
...@@ -410,6 +427,15 @@ rangeTableEntry_used_walker(Node *node, ...@@ -410,6 +427,15 @@ rangeTableEntry_used_walker(Node *node,
/* the subquery itself is visited separately */ /* the subquery itself is visited separately */
return false; return false;
} }
if (IsA(node, JoinExpr))
{
JoinExpr *j = (JoinExpr *) node;
if (j->rtindex == context->rt_index &&
context->sublevels_up == 0)
return true;
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.92 2002/03/06 19:58:26 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.93 2002/03/12 00:51:59 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -73,17 +73,22 @@ typedef struct ...@@ -73,17 +73,22 @@ typedef struct
/* /*
* Each level of query context around a subtree needs a level of Var namespace. * Each level of query context around a subtree needs a level of Var namespace.
* The rangetable is the list of actual RTEs, and the namespace indicates * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
* which parts of the rangetable are accessible (and under what aliases) * the current context's namespaces list.
* in the expression currently being looked at. A Var having varlevelsup=N *
* refers to the N'th item (counting from 0) in the current context's * The rangetable is the list of actual RTEs from the query tree.
* namespaces list. *
* For deparsing plan trees, we allow two special RTE entries that are not
* part of the rtable list (mainly because they don't have consecutively
* allocated varnos).
*/ */
typedef struct typedef struct
{ {
List *rtable; /* List of RangeTblEntry nodes */ List *rtable; /* List of RangeTblEntry nodes */
List *namespace; /* List of joinlist items (RangeTblRef and int outer_varno; /* varno for outer_rte */
* JoinExpr nodes) */ RangeTblEntry *outer_rte; /* special RangeTblEntry, or NULL */
int inner_varno; /* varno for inner_rte */
RangeTblEntry *inner_rte; /* special RangeTblEntry, or NULL */
} deparse_namespace; } deparse_namespace;
...@@ -122,12 +127,6 @@ static void get_rule_sortgroupclause(SortClause *srt, List *tlist, ...@@ -122,12 +127,6 @@ static void get_rule_sortgroupclause(SortClause *srt, List *tlist,
deparse_context *context); deparse_context *context);
static void get_names_for_var(Var *var, deparse_context *context, static void get_names_for_var(Var *var, deparse_context *context,
char **refname, char **attname); char **refname, char **attname);
static bool get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
char **refname, char **attname);
static bool find_alias_in_namespace(Node *nsnode, Node *expr,
List *rangetable, int levelsup,
char **refname, char **attname);
static bool phony_equal(Node *expr1, Node *expr2, int levelsup);
static void get_rule_expr(Node *node, deparse_context *context); static void get_rule_expr(Node *node, deparse_context *context);
static void get_func_expr(Expr *expr, deparse_context *context); static void get_func_expr(Expr *expr, deparse_context *context);
static Node *strip_type_coercion(Node *expr, Oid resultType); static Node *strip_type_coercion(Node *expr, Oid resultType);
...@@ -644,7 +643,7 @@ deparse_expression(Node *expr, List *dpcontext, bool forceprefix) ...@@ -644,7 +643,7 @@ deparse_expression(Node *expr, List *dpcontext, bool forceprefix)
* *
* Given the name and OID of a relation, build deparsing context for an * Given the name and OID of a relation, build deparsing context for an
* expression referencing only that relation (as varno 1, varlevelsup 0). * expression referencing only that relation (as varno 1, varlevelsup 0).
* This is presently sufficient for the external uses of deparse_expression. * This is sufficient for many uses of deparse_expression.
* ---------- * ----------
*/ */
List * List *
...@@ -652,30 +651,119 @@ deparse_context_for(char *relname, Oid relid) ...@@ -652,30 +651,119 @@ deparse_context_for(char *relname, Oid relid)
{ {
deparse_namespace *dpns; deparse_namespace *dpns;
RangeTblEntry *rte; RangeTblEntry *rte;
RangeTblRef *rtr;
dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace)); dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
/* Build a minimal RTE for the rel */ /* Build a minimal RTE for the rel */
rte = makeNode(RangeTblEntry); rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RELATION;
rte->relname = relname; rte->relname = relname;
rte->relid = relid; rte->relid = relid;
rte->eref = makeNode(Attr); rte->eref = makeNode(Attr);
rte->eref->relname = relname; rte->eref->relname = relname;
rte->inh = false; rte->inh = false;
rte->inFromCl = true; rte->inFromCl = true;
/* Build one-element rtable */ /* Build one-element rtable */
dpns->rtable = makeList1(rte); dpns->rtable = makeList1(rte);
dpns->outer_varno = dpns->inner_varno = 0;
dpns->outer_rte = dpns->inner_rte = NULL;
/* Return a one-deep namespace stack */
return makeList1(dpns);
}
/*
* deparse_context_for_plan - Build deparse context for a plan node
*
* We assume we are dealing with an upper-level plan node having either
* one or two referenceable children (pass innercontext = NULL if only one).
* The passed-in Nodes should be made using deparse_context_for_subplan.
* The resulting context will work for deparsing quals, tlists, etc of the
* plan node.
*/
List *
deparse_context_for_plan(int outer_varno, Node *outercontext,
int inner_varno, Node *innercontext)
{
deparse_namespace *dpns;
/* Build a namespace list referencing this RTE only */ dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
rtr = makeNode(RangeTblRef);
rtr->rtindex = 1; dpns->rtable = NIL;
dpns->namespace = makeList1(rtr); dpns->outer_varno = outer_varno;
dpns->outer_rte = (RangeTblEntry *) outercontext;
dpns->inner_varno = inner_varno;
dpns->inner_rte = (RangeTblEntry *) innercontext;
/* Return a one-deep namespace stack */ /* Return a one-deep namespace stack */
return makeList1(dpns); return makeList1(dpns);
} }
/*
* deparse_context_for_subplan - Build deparse context for a plan node
*
* Helper routine to build one of the inputs for deparse_context_for_plan.
* Pass the tlist of the subplan node, plus the query rangetable.
*
* The returned node is actually a RangeTblEntry, but we declare it as just
* Node to discourage callers from assuming anything.
*/
Node *
deparse_context_for_subplan(const char *name, List *tlist,
List *rtable)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
List *attrs = NIL;
int nattrs = 0;
int rtablelength = length(rtable);
List *tl;
char buf[32];
foreach(tl, tlist)
{
TargetEntry *tle = lfirst(tl);
Resdom *resdom = tle->resdom;
nattrs++;
Assert(resdom->resno == nattrs);
if (resdom->resname)
{
attrs = lappend(attrs, makeString(resdom->resname));
continue;
}
if (tle->expr && IsA(tle->expr, Var))
{
Var *var = (Var *) tle->expr;
/* varno/varattno won't be any good, but varnoold might be */
if (var->varnoold > 0 && var->varnoold <= rtablelength)
{
RangeTblEntry *varrte = rt_fetch(var->varnoold, rtable);
char *varname;
varname = get_rte_attribute_name(varrte, var->varoattno);
attrs = lappend(attrs, makeString(varname));
continue;
}
}
/* Fallback if can't get name */
snprintf(buf, sizeof(buf), "?column%d?", resdom->resno);
attrs = lappend(attrs, makeString(pstrdup(buf)));
}
rte->rtekind = RTE_SPECIAL; /* XXX */
rte->relname = pstrdup(name);
rte->relid = InvalidOid;
rte->eref = makeNode(Attr);
rte->eref->relname = rte->relname;
rte->eref->attrs = attrs;
rte->inh = false;
rte->inFromCl = true;
return (Node *) rte;
}
/* ---------- /* ----------
* make_ruledef - reconstruct the CREATE RULE command * make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple * for a given pg_rewrite tuple
...@@ -789,7 +877,8 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) ...@@ -789,7 +877,8 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
context.namespaces = makeList1(&dpns); context.namespaces = makeList1(&dpns);
context.varprefix = (length(query->rtable) != 1); context.varprefix = (length(query->rtable) != 1);
dpns.rtable = query->rtable; dpns.rtable = query->rtable;
dpns.namespace = query->jointree ? query->jointree->fromlist : NIL; dpns.outer_varno = dpns.inner_varno = 0;
dpns.outer_rte = dpns.inner_rte = NULL;
get_rule_expr(qual, &context); get_rule_expr(qual, &context);
} }
...@@ -912,7 +1001,8 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace) ...@@ -912,7 +1001,8 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace)
context.varprefix = (parentnamespace != NIL || context.varprefix = (parentnamespace != NIL ||
length(query->rtable) != 1); length(query->rtable) != 1);
dpns.rtable = query->rtable; dpns.rtable = query->rtable;
dpns.namespace = query->jointree ? query->jointree->fromlist : NIL; dpns.outer_varno = dpns.inner_varno = 0;
dpns.outer_rte = dpns.inner_rte = NULL;
switch (query->commandType) switch (query->commandType)
{ {
...@@ -1382,12 +1472,11 @@ get_utility_query_def(Query *query, deparse_context *context) ...@@ -1382,12 +1472,11 @@ get_utility_query_def(Query *query, deparse_context *context)
/* /*
* Get the relation refname and attname for a (possibly nonlocal) Var. * Get the relation refname and attname for a (possibly nonlocal) Var.
* *
* refname will be returned as NULL if the Var references an unnamed join.
* In this case the Var *must* be displayed without any qualification.
*
* attname will be returned as NULL if the Var represents a whole tuple * attname will be returned as NULL if the Var represents a whole tuple
* of the relation. * of the relation.
*
* This is trickier than it ought to be because of the possibility of aliases
* and limited scope of refnames. We have to try to return the correct alias
* with respect to the current namespace given by the context.
*/ */
static void static void
get_names_for_var(Var *var, deparse_context *context, get_names_for_var(Var *var, deparse_context *context,
...@@ -1406,260 +1495,29 @@ get_names_for_var(Var *var, deparse_context *context, ...@@ -1406,260 +1495,29 @@ get_names_for_var(Var *var, deparse_context *context,
var->varlevelsup); var->varlevelsup);
dpns = (deparse_namespace *) lfirst(nslist); dpns = (deparse_namespace *) lfirst(nslist);
/* Scan namespace to see if we can find an alias for the var */ /* Find the relevant RTE */
if (find_alias_in_namespace((Node *) dpns->namespace, (Node *) var, if (var->varno >= 1 && var->varno <= length(dpns->rtable))
dpns->rtable, var->varlevelsup,
refname, attname))
return;
/*
* Otherwise, fall back on the rangetable entry. This should happen
* only for uses of special RTEs like *NEW* and *OLD*, which won't get
* placed in our namespace.
*/
rte = rt_fetch(var->varno, dpns->rtable); rte = rt_fetch(var->varno, dpns->rtable);
*refname = rte->eref->relname; else if (var->varno == dpns->outer_varno)
if (var->varattno == InvalidAttrNumber) rte = dpns->outer_rte;
*attname = NULL; else if (var->varno == dpns->inner_varno)
rte = dpns->inner_rte;
else
rte = NULL;
if (rte == NULL)
elog(ERROR, "get_names_for_var: bogus varno %d",
var->varno);
/* Emit results */
if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
*refname = NULL;
else else
*attname = get_rte_attribute_name(rte, var->varattno);
}
/*
* Check to see if a CASE expression matches a FULL JOIN's output expression.
* If so, return the refname and alias it should be expressed as.
*/
static bool
get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
char **refname, char **attname)
{
List *nslist;
int sup;
/*
* This could be done more efficiently if we first groveled through
* the CASE to find varlevelsup values, but it's probably not worth
* the trouble. All this code will go away someday anyway ...
*/
sup = 0;
foreach(nslist, context->namespaces)
{
deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
if (find_alias_in_namespace((Node *) dpns->namespace,
(Node *) caseexpr,
dpns->rtable, sup,
refname, attname))
return true;
sup++;
}
return false;
}
/*
* Recursively scan a namespace (same representation as a jointree) to see
* if we can find an alias for the given expression. If so, return the
* correct alias refname and attname. The expression may be either a plain
* Var or a CASE expression (which may be a FULL JOIN reference).
*/
static bool
find_alias_in_namespace(Node *nsnode, Node *expr,
List *rangetable, int levelsup,
char **refname, char **attname)
{
if (nsnode == NULL)
return false;
if (IsA(nsnode, RangeTblRef))
{
if (IsA(expr, Var))
{
Var *var = (Var *) expr;
int rtindex = ((RangeTblRef *) nsnode)->rtindex;
if (var->varno == rtindex && var->varlevelsup == levelsup)
{
RangeTblEntry *rte = rt_fetch(rtindex, rangetable);
*refname = rte->eref->relname; *refname = rte->eref->relname;
if (var->varattno == InvalidAttrNumber) if (var->varattno == InvalidAttrNumber)
*attname = NULL; *attname = NULL;
else else
*attname = get_rte_attribute_name(rte, var->varattno); *attname = get_rte_attribute_name(rte, var->varattno);
return true;
}
}
}
else if (IsA(nsnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) nsnode;
if (j->alias)
{
List *vlist;
List *nlist;
/*
* Does the expr match any of the output columns of the join?
*
* We can't just use equal() here, because the given expr may
* have nonzero levelsup, whereas the saved expression in the
* JoinExpr should have zero levelsup.
*/
nlist = j->colnames;
foreach(vlist, j->colvars)
{
if (phony_equal(lfirst(vlist), expr, levelsup))
{
*refname = j->alias->relname;
*attname = strVal(lfirst(nlist));
return true;
}
nlist = lnext(nlist);
}
/*
* Tables within an aliased join are invisible from outside
* the join, according to the scope rules of SQL92 (the join
* is considered a subquery). So, stop here.
*/
return false;
}
if (find_alias_in_namespace(j->larg, expr,
rangetable, levelsup,
refname, attname))
return true;
if (find_alias_in_namespace(j->rarg, expr,
rangetable, levelsup,
refname, attname))
return true;
}
else if (IsA(nsnode, List))
{
List *l;
foreach(l, (List *) nsnode)
{
if (find_alias_in_namespace(lfirst(l), expr,
rangetable, levelsup,
refname, attname))
return true;
}
}
else
elog(ERROR, "find_alias_in_namespace: unexpected node type %d",
nodeTag(nsnode));
return false;
}
/*
* Check for equality of two expressions, with the proviso that all Vars in
* expr1 should have varlevelsup = 0, while all Vars in expr2 should have
* varlevelsup = levelsup.
*
* In reality we only need to support equality checks on Vars and the type
* of CASE expression that is used for FULL JOIN outputs, so not all node
* types need be handled here.
*
* Otherwise, this code is a straight ripoff from equalfuncs.c.
*/
static bool
phony_equal(Node *expr1, Node *expr2, int levelsup)
{
if (expr1 == NULL || expr2 == NULL)
return (expr1 == expr2);
if (nodeTag(expr1) != nodeTag(expr2))
return false;
if (IsA(expr1, Var))
{
Var *a = (Var *) expr1;
Var *b = (Var *) expr2;
if (a->varno != b->varno)
return false;
if (a->varattno != b->varattno)
return false;
if (a->vartype != b->vartype)
return false;
if (a->vartypmod != b->vartypmod)
return false;
if (a->varlevelsup != 0 || b->varlevelsup != levelsup)
return false;
if (a->varnoold != b->varnoold)
return false;
if (a->varoattno != b->varoattno)
return false;
return true;
}
if (IsA(expr1, CaseExpr))
{
CaseExpr *a = (CaseExpr *) expr1;
CaseExpr *b = (CaseExpr *) expr2;
if (a->casetype != b->casetype)
return false;
if (!phony_equal(a->arg, b->arg, levelsup))
return false;
if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
return false;
if (!phony_equal(a->defresult, b->defresult, levelsup))
return false;
return true;
}
if (IsA(expr1, CaseWhen))
{
CaseWhen *a = (CaseWhen *) expr1;
CaseWhen *b = (CaseWhen *) expr2;
if (!phony_equal(a->expr, b->expr, levelsup))
return false;
if (!phony_equal(a->result, b->result, levelsup))
return false;
return true;
}
if (IsA(expr1, Expr))
{
Expr *a = (Expr *) expr1;
Expr *b = (Expr *) expr2;
if (a->opType != b->opType)
return false;
if (!phony_equal(a->oper, b->oper, levelsup))
return false;
if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
return false;
return true;
}
if (IsA(expr1, Func))
{
Func *a = (Func *) expr1;
Func *b = (Func *) expr2;
if (a->funcid != b->funcid)
return false;
if (a->functype != b->functype)
return false;
return true;
}
if (IsA(expr1, List))
{
List *la = (List *) expr1;
List *lb = (List *) expr2;
List *l;
if (length(la) != length(lb))
return false;
foreach(l, la)
{
if (!phony_equal(lfirst(l), lfirst(lb), levelsup))
return false;
lb = lnext(lb);
}
return true;
}
/* If we get here, there was something weird in a JOIN's colvars list */
elog(ERROR, "phony_equal: unexpected node type %d", nodeTag(expr1));
return false;
} }
/* ---------- /* ----------
...@@ -1695,7 +1553,7 @@ get_rule_expr(Node *node, deparse_context *context) ...@@ -1695,7 +1553,7 @@ get_rule_expr(Node *node, deparse_context *context)
char *attname; char *attname;
get_names_for_var(var, context, &refname, &attname); get_names_for_var(var, context, &refname, &attname);
if (context->varprefix || attname == NULL) if (refname && (context->varprefix || attname == NULL))
{ {
if (strcmp(refname, "*NEW*") == 0) if (strcmp(refname, "*NEW*") == 0)
appendStringInfo(buf, "new"); appendStringInfo(buf, "new");
...@@ -1767,6 +1625,10 @@ get_rule_expr(Node *node, deparse_context *context) ...@@ -1767,6 +1625,10 @@ get_rule_expr(Node *node, deparse_context *context)
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
break; break;
case FUNC_EXPR:
get_func_expr((Expr *) node, context);
break;
case OR_EXPR: case OR_EXPR:
appendStringInfoChar(buf, '('); appendStringInfoChar(buf, '(');
get_rule_expr((Node *) lfirst(args), context); get_rule_expr((Node *) lfirst(args), context);
...@@ -1795,8 +1657,13 @@ get_rule_expr(Node *node, deparse_context *context) ...@@ -1795,8 +1657,13 @@ get_rule_expr(Node *node, deparse_context *context)
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
break; break;
case FUNC_EXPR: case SUBPLAN_EXPR:
get_func_expr((Expr *) node, context); /*
* We cannot see an already-planned subplan in rule
* deparsing, only while EXPLAINing a query plan.
* For now, just punt.
*/
appendStringInfo(buf, "(subplan)");
break; break;
default: default:
...@@ -1927,19 +1794,6 @@ get_rule_expr(Node *node, deparse_context *context) ...@@ -1927,19 +1794,6 @@ get_rule_expr(Node *node, deparse_context *context)
{ {
CaseExpr *caseexpr = (CaseExpr *) node; CaseExpr *caseexpr = (CaseExpr *) node;
List *temp; List *temp;
char *refname;
char *attname;
/* Hack for providing aliases for FULL JOIN outputs */
if (get_alias_for_case(caseexpr, context,
&refname, &attname))
{
if (context->varprefix)
appendStringInfo(buf, "%s.",
quote_identifier(refname));
appendStringInfo(buf, "%s", quote_identifier(attname));
break;
}
appendStringInfo(buf, "CASE"); appendStringInfo(buf, "CASE");
foreach(temp, caseexpr->args) foreach(temp, caseexpr->args)
...@@ -2015,6 +1869,28 @@ get_rule_expr(Node *node, deparse_context *context) ...@@ -2015,6 +1869,28 @@ get_rule_expr(Node *node, deparse_context *context)
get_sublink_expr(node, context); get_sublink_expr(node, context);
break; break;
case T_Param:
{
Param *param = (Param *) node;
switch (param->paramkind)
{
case PARAM_NAMED:
case PARAM_NEW:
case PARAM_OLD:
appendStringInfo(buf, "$%s", param->paramname);
break;
case PARAM_NUM:
case PARAM_EXEC:
appendStringInfo(buf, "$%d", param->paramid);
break;
default:
appendStringInfo(buf, "(param)");
break;
}
}
break;
default: default:
printf("\n%s\n", nodeToString(node)); printf("\n%s\n", nodeToString(node));
elog(ERROR, "get_ruledef of %s: unknown node type %d in get_rule_expr()", elog(ERROR, "get_ruledef of %s: unknown node type %d in get_rule_expr()",
...@@ -2442,16 +2318,6 @@ static void ...@@ -2442,16 +2318,6 @@ static void
get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{ {
StringInfo buf = context->buf; StringInfo buf = context->buf;
deparse_namespace *dpns;
List *sv_namespace;
/*
* FROM-clause items have limited visibility of query's namespace.
* Save and restore the outer namespace setting while we munge it.
*/
dpns = (deparse_namespace *) lfirst(context->namespaces);
sv_namespace = dpns->namespace;
dpns->namespace = NIL;
if (IsA(jtnode, RangeTblRef)) if (IsA(jtnode, RangeTblRef))
{ {
...@@ -2544,7 +2410,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -2544,7 +2410,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
} }
else if (j->quals) else if (j->quals)
{ {
dpns->namespace = makeList2(j->larg, j->rarg);
appendStringInfo(buf, " ON ("); appendStringInfo(buf, " ON (");
get_rule_expr(j->quals, context); get_rule_expr(j->quals, context);
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
...@@ -2575,8 +2440,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -2575,8 +2440,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
else else
elog(ERROR, "get_from_clause_item: unexpected node type %d", elog(ERROR, "get_from_clause_item: unexpected node type %d",
nodeTag(jtnode)); nodeTag(jtnode));
dpns->namespace = sv_namespace;
} }
/* ---------- /* ----------
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: catversion.h,v 1.105 2002/03/01 22:45:16 petere Exp $ * $Id: catversion.h,v 1.106 2002/03/12 00:51:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200203011 #define CATALOG_VERSION_NO 200203111
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parsenodes.h,v 1.159 2002/03/08 04:37:18 tgl Exp $ * $Id: parsenodes.h,v 1.160 2002/03/12 00:52:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -430,9 +430,12 @@ typedef struct TargetEntry ...@@ -430,9 +430,12 @@ typedef struct TargetEntry
* RangeTblEntry - * RangeTblEntry -
* A range table is a List of RangeTblEntry nodes. * A range table is a List of RangeTblEntry nodes.
* *
* Currently we use the same node type for both plain relation references * A range table entry may represent a plain relation, a sub-select in
* and sub-selects in the FROM clause. It might be cleaner to abstract * FROM, or the result of a JOIN clause. (Only explicit JOIN syntax
* the common fields into a "superclass" nodetype. * produces an RTE, not the implicit join resulting from multiple FROM
* items. This is because we only need the RTE to deal with SQL features
* like outer joins and join-output-column aliasing.) Other special
* RTE types also exist, as indicated by RTEKind.
* *
* alias is an Attr node representing the AS alias-clause attached to the * alias is an Attr node representing the AS alias-clause attached to the
* FROM expression, or NULL if no clause. * FROM expression, or NULL if no clause.
...@@ -445,7 +448,7 @@ typedef struct TargetEntry ...@@ -445,7 +448,7 @@ typedef struct TargetEntry
* *
* inh is TRUE for relation references that should be expanded to include * inh is TRUE for relation references that should be expanded to include
* inheritance children, if the rel has any. This *must* be FALSE for * inheritance children, if the rel has any. This *must* be FALSE for
* subquery RTEs. * RTEs other than RTE_RELATION entries.
* *
* inFromCl marks those range variables that are listed in the FROM clause. * inFromCl marks those range variables that are listed in the FROM clause.
* In SQL, the query can only refer to range variables listed in the * In SQL, the query can only refer to range variables listed in the
...@@ -465,12 +468,28 @@ typedef struct TargetEntry ...@@ -465,12 +468,28 @@ typedef struct TargetEntry
* (This allows rules to act as setuid gateways.) * (This allows rules to act as setuid gateways.)
*-------------------- *--------------------
*/ */
typedef enum RTEKind
{
RTE_RELATION, /* ordinary relation reference */
RTE_SUBQUERY, /* subquery in FROM */
RTE_JOIN, /* join */
RTE_SPECIAL /* special rule relation (NEW or OLD) */
} RTEKind;
typedef struct RangeTblEntry typedef struct RangeTblEntry
{ {
NodeTag type; NodeTag type;
RTEKind rtekind; /* see above */
/* /*
* Fields valid for a plain relation RTE (else NULL/zero): * XXX the fields applicable to only some rte kinds should be merged
* into a union. I didn't do this yet because the diffs would impact
* a lot of code that is being actively worked on. FIXME later.
*/
/*
* Fields valid for a plain relation or inh_relation RTE (else NULL/zero):
*/ */
char *relname; /* real name of the relation */ char *relname; /* real name of the relation */
Oid relid; /* OID of the relation */ Oid relid; /* OID of the relation */
...@@ -480,6 +499,21 @@ typedef struct RangeTblEntry ...@@ -480,6 +499,21 @@ typedef struct RangeTblEntry
*/ */
Query *subquery; /* the sub-query */ Query *subquery; /* the sub-query */
/*
* Fields valid for a join RTE (else NULL):
*
* 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.
*/
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 */
/* /*
* Fields valid in all RTEs: * Fields valid in all RTEs:
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: plannodes.h,v 1.53 2001/11/05 17:46:34 momjian Exp $ * $Id: plannodes.h,v 1.54 2002/03/12 00:52:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -254,6 +254,7 @@ typedef struct SubqueryScan ...@@ -254,6 +254,7 @@ typedef struct SubqueryScan
* jointype: rule for joining tuples from left and right subtrees * jointype: rule for joining tuples from left and right subtrees
* joinqual: qual conditions that came from JOIN/ON or JOIN/USING * joinqual: qual conditions that came from JOIN/ON or JOIN/USING
* (plan.qual contains conditions that came from WHERE) * (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 * When jointype is INNER, joinqual and plan.qual are semantically
* interchangeable. For OUTER jointypes, the two are *not* interchangeable; * interchangeable. For OUTER jointypes, the two are *not* interchangeable;
...@@ -262,6 +263,8 @@ typedef struct SubqueryScan ...@@ -262,6 +263,8 @@ typedef struct SubqueryScan
* (But plan.qual is still applied before actually returning a tuple.) * (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 * For an outer join, only joinquals are allowed to be used as the merge
* or hash condition of a merge or hash join. * 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 typedef struct Join
...@@ -269,6 +272,7 @@ typedef struct Join ...@@ -269,6 +272,7 @@ typedef struct Join
Plan plan; Plan plan;
JoinType jointype; JoinType jointype;
List *joinqual; /* JOIN quals (in addition to plan.qual) */ List *joinqual; /* JOIN quals (in addition to plan.qual) */
Index joinrti; /* JOIN RTE, if any */
} Join; } Join;
/* ---------------- /* ----------------
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: primnodes.h,v 1.57 2001/11/05 17:46:34 momjian Exp $ * $Id: primnodes.h,v 1.58 2002/03/12 00:52:02 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -513,10 +513,9 @@ typedef struct RangeTblRef ...@@ -513,10 +513,9 @@ typedef struct RangeTblRef
* alias has a critical impact on semantics, because a join with an alias * alias has a critical impact on semantics, because a join with an alias
* restricts visibility of the tables/columns inside it. * restricts visibility of the tables/columns inside it.
* *
* During parse analysis, colnames is filled with a list of String nodes * During parse analysis, an RTE is created for the Join, and its index
* giving the column names (real or alias) of the output of the join, * is filled into rtindex. This RTE is present mainly so that Vars can
* and colvars is filled with a list of expressions that can be copied to * be created that refer to the outputs of the join.
* reference the output columns.
*---------- *----------
*/ */
typedef struct JoinExpr typedef struct JoinExpr
...@@ -529,9 +528,7 @@ typedef struct JoinExpr ...@@ -529,9 +528,7 @@ typedef struct JoinExpr
List *using; /* USING clause, if any (list of String) */ List *using; /* USING clause, if any (list of String) */
Node *quals; /* qualifiers on join, if any */ Node *quals; /* qualifiers on join, if any */
struct Attr *alias; /* user-written alias clause, if any */ struct Attr *alias; /* user-written alias clause, if any */
List *colnames; /* output column names (list of String) */ int rtindex; /* RT index assigned for join */
List *colvars; /* output column nodes (list of
* expressions) */
} JoinExpr; } JoinExpr;
/*---------- /*----------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: relation.h,v 1.62 2002/03/01 06:01:20 tgl Exp $ * $Id: relation.h,v 1.63 2002/03/12 00:52:02 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -39,16 +39,37 @@ typedef enum CostSelector ...@@ -39,16 +39,37 @@ typedef enum CostSelector
* RelOptInfo * RelOptInfo
* Per-relation information for planning/optimization * Per-relation information for planning/optimization
* *
* For planning purposes, a "base rel" is either a plain relation (a * For planning purposes, a "base rel" is either a plain relation (a table)
* table) or the output of a sub-SELECT that appears in the range table. * or the output of a sub-SELECT that appears in the range table.
* In either case it is uniquely identified by an RT index. A "joinrel" * In either case it is uniquely identified by an RT index. A "joinrel"
* is the joining of two or more base rels. A joinrel is identified by * is the joining of two or more base rels. A joinrel is identified by
* the set of RT indexes for its component baserels. * the set of RT indexes for its component baserels. We create RelOptInfo
* nodes for each baserel and joinrel, and store them in the Query's
* base_rel_list and join_rel_list respectively.
* *
* Note that there is only one joinrel for any given set of component * Note that there is only one joinrel for any given set of component
* baserels, no matter what order we assemble them in; so an unordered * baserels, no matter what order we assemble them in; so an unordered
* set is the right datatype to identify it with. * set is the right datatype to identify it with.
* *
* We also have "other rels", which are like base rels in that they refer to
* single RT indexes; but they are not part of the join tree, and are stored
* in other_rel_list not base_rel_list. An otherrel is created for each
* join RTE as an aid in processing Vars that refer to the join's outputs,
* but it serves no other purpose in planning. It is important not to
* confuse this otherrel with the joinrel that represents the matching set
* of base relations.
*
* A second category of otherrels are those made for child relations of an
* inheritance scan (SELECT FROM foo*). The parent table's RTE and
* corresponding baserel represent the whole result of the inheritance scan.
* The planner creates separate RTEs and associated RelOptInfos for each child
* table (including the parent table, in its capacity as a member of the
* inheritance set). These RelOptInfos are physically identical to baserels,
* but are otherrels because they are not in the main join tree. These added
* RTEs and otherrels are used to plan the scans of the individual tables in
* the inheritance set; then the parent baserel is given an Append plan
* comprising the best plans for the individual child tables.
*
* Parts of this data structure are specific to various scan and join * Parts of this data structure are specific to various scan and join
* mechanisms. It didn't seem worth creating new node types for them. * mechanisms. It didn't seem worth creating new node types for them.
* *
...@@ -69,7 +90,7 @@ typedef enum CostSelector ...@@ -69,7 +90,7 @@ typedef enum CostSelector
* pruneable - flag to let the planner know whether it can prune the * pruneable - flag to let the planner know whether it can prune the
* pathlist of this RelOptInfo or not. * pathlist of this RelOptInfo or not.
* *
* * If the relation is a base relation it will have these fields set: * If the relation is a base relation it will have these fields set:
* *
* issubquery - true if baserel is a subquery RTE rather than a table * issubquery - true if baserel is a subquery RTE rather than a table
* indexlist - list of IndexOptInfo nodes for relation's indexes * indexlist - list of IndexOptInfo nodes for relation's indexes
...@@ -82,15 +103,18 @@ typedef enum CostSelector ...@@ -82,15 +103,18 @@ typedef enum CostSelector
* upon creation of the RelOptInfo object; they are filled in when * upon creation of the RelOptInfo object; they are filled in when
* set_base_rel_pathlist processes the object. * set_base_rel_pathlist processes the object.
* *
* Note: if a base relation is the root of an inheritance tree * For otherrels that are inheritance children, these fields are filled
* (SELECT FROM foo*) it is still considered a base rel. We will * in just as for a baserel. In otherrels for join RTEs, these fields
* generate a list of candidate Paths for accessing that table itself, * are empty --- the only useful field of a join otherrel is its
* and also generate baserel RelOptInfo nodes for each child table, * outerjoinset.
* with their own candidate Path lists. Then, an AppendPath is built *
* from the cheapest Path for each of these tables, and set to be the * If the relation is a join relation it will have these fields set:
* only available Path for the inheritance baserel. *
* joinrti - RT index of corresponding JOIN RTE, if any; 0 if none
* joinrteids - List of RT indexes of JOIN RTEs included in this join
* (including joinrti)
* *
* * The presence of the remaining fields depends on the restrictions * The presence of the remaining fields depends on the restrictions
* and joins that the relation participates in: * and joins that the relation participates in:
* *
* baserestrictinfo - List of RestrictInfo nodes, containing info about * baserestrictinfo - List of RestrictInfo nodes, containing info about
...@@ -98,9 +122,11 @@ typedef enum CostSelector ...@@ -98,9 +122,11 @@ typedef enum CostSelector
* participates (only used for base rels) * participates (only used for base rels)
* baserestrictcost - Estimated cost of evaluating the baserestrictinfo * baserestrictcost - Estimated cost of evaluating the baserestrictinfo
* clauses at a single tuple (only used for base rels) * clauses at a single tuple (only used for base rels)
* outerjoinset - If the rel appears within the nullable side of an outer * outerjoinset - For a base rel: if the rel appears within the nullable
* join, the list of all relids participating in the highest * side of an outer join, the list of all relids
* such outer join; else NIL (only used for base rels) * participating in the highest such outer join; else NIL.
* For a join otherrel: the list of all baserel relids
* syntactically within the join. Otherwise, unused.
* joininfo - List of JoinInfo nodes, containing info about each join * joininfo - List of JoinInfo nodes, containing info about each join
* clause in which this relation participates * clause in which this relation participates
* innerjoin - List of Path nodes that represent indices that may be used * innerjoin - List of Path nodes that represent indices that may be used
...@@ -128,11 +154,20 @@ typedef enum CostSelector ...@@ -128,11 +154,20 @@ typedef enum CostSelector
* until after the outer join is performed. * until after the outer join is performed.
*---------- *----------
*/ */
typedef enum RelOptKind
{
RELOPT_BASEREL,
RELOPT_JOINREL,
RELOPT_OTHER_JOIN_REL,
RELOPT_OTHER_CHILD_REL
} RelOptKind;
typedef struct RelOptInfo typedef struct RelOptInfo
{ {
NodeTag type; NodeTag type;
RelOptKind reloptkind;
/* all relations included in this RelOptInfo */ /* all relations included in this RelOptInfo */
Relids relids; /* integer list of base relids (RT Relids relids; /* integer list of base relids (RT
* indexes) */ * indexes) */
...@@ -155,6 +190,10 @@ typedef struct RelOptInfo ...@@ -155,6 +190,10 @@ typedef struct RelOptInfo
double tuples; double tuples;
struct Plan *subplan; struct Plan *subplan;
/* information about a join rel (not set for base rels!) */
Index joinrti;
List *joinrteids;
/* used by various scans and joins: */ /* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if List *baserestrictinfo; /* RestrictInfo structures (if
* base rel) */ * base rel) */
...@@ -228,6 +267,16 @@ typedef struct IndexOptInfo ...@@ -228,6 +267,16 @@ typedef struct IndexOptInfo
bool unique; /* if a unique index */ bool unique; /* if a unique index */
} IndexOptInfo; } IndexOptInfo;
/*
* A Var is considered to belong to a relation if it's either from one
* of the actual base rels making up the relation, or it's a join alias
* var that is included in the relation.
*/
#define VARISRELMEMBER(varno,rel) (intMember((varno), (rel)->relids) || \
intMember((varno), (rel)->joinrteids))
/* /*
* PathKeys * PathKeys
* *
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pathnode.h,v 1.41 2001/11/05 17:46:34 momjian Exp $ * $Id: pathnode.h,v 1.42 2002/03/12 00:52:03 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -67,9 +67,11 @@ extern HashPath *create_hashjoin_path(Query *root, ...@@ -67,9 +67,11 @@ extern HashPath *create_hashjoin_path(Query *root,
/* /*
* prototypes for relnode.c * prototypes for relnode.c
*/ */
extern RelOptInfo *build_base_rel(Query *root, int relid); extern void build_base_rel(Query *root, int relid);
extern RelOptInfo *build_other_rel(Query *root, int relid); extern RelOptInfo *build_other_rel(Query *root, int relid);
extern RelOptInfo *find_base_rel(Query *root, int relid); extern RelOptInfo *find_base_rel(Query *root, int relid);
extern RelOptInfo *find_other_rel(Query *root, int relid);
extern RelOptInfo *find_other_rel_for_join(Query *root, List *relids);
extern RelOptInfo *build_join_rel(Query *root, extern RelOptInfo *build_join_rel(Query *root,
RelOptInfo *outer_rel, RelOptInfo *outer_rel,
RelOptInfo *inner_rel, RelOptInfo *inner_rel,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: planmain.h,v 1.54 2001/11/05 17:46:34 momjian Exp $ * $Id: planmain.h,v 1.55 2002/03/12 00:52:03 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,18 +47,19 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); ...@@ -47,18 +47,19 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
/* /*
* prototypes for plan/initsplan.c * prototypes for plan/initsplan.c
*/ */
extern List *add_base_rels_to_query(Query *root, Node *jtnode);
extern void build_base_rel_tlists(Query *root, List *tlist); extern void build_base_rel_tlists(Query *root, List *tlist);
extern Relids distribute_quals_to_rels(Query *root, Node *jtnode); extern Relids distribute_quals_to_rels(Query *root, Node *jtnode);
extern List *add_missing_rels_to_query(Query *root, Node *jtnode);
extern void process_implied_equality(Query *root, Node *item1, Node *item2, extern void process_implied_equality(Query *root, Node *item1, Node *item2,
Oid sortop1, Oid sortop2); Oid sortop1, Oid sortop2);
/* /*
* prototypes for plan/setrefs.c * prototypes for plan/setrefs.c
*/ */
extern void set_plan_references(Plan *plan); extern void set_plan_references(Query *root, Plan *plan);
extern List *join_references(List *clauses, List *outer_tlist, extern List *join_references(List *clauses, Query *root,
List *inner_tlist, Index acceptable_rel); List *outer_tlist, List *inner_tlist,
Index acceptable_rel, Index join_rti);
extern void fix_opids(Node *node); extern void fix_opids(Node *node);
/* /*
......
...@@ -7,14 +7,15 @@ ...@@ -7,14 +7,15 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: var.h,v 1.17 2001/11/05 17:46:34 momjian Exp $ * $Id: var.h,v 1.18 2002/03/12 00:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#ifndef VAR_H #ifndef VAR_H
#define VAR_H #define VAR_H
#include "nodes/primnodes.h" #include "nodes/parsenodes.h"
extern List *pull_varnos(Node *node); extern List *pull_varnos(Node *node);
extern bool contain_var_reference(Node *node, int varno, int varattno, extern bool contain_var_reference(Node *node, int varno, int varattno,
...@@ -22,5 +23,8 @@ extern bool contain_var_reference(Node *node, int varno, int varattno, ...@@ -22,5 +23,8 @@ 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_whole_tuple_var(Node *node, int varno, int levelsup);
extern bool contain_var_clause(Node *node); extern bool contain_var_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars); 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);
#endif /* VAR_H */ #endif /* VAR_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parse_relation.h,v 1.28 2001/11/05 17:46:35 momjian Exp $ * $Id: parse_relation.h,v 1.29 2002/03/12 00:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include "parser/parse_node.h" #include "parser/parse_node.h"
extern Node *refnameRangeOrJoinEntry(ParseState *pstate, extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
char *refname, char *refname,
int *sublevels_up); int *sublevels_up);
extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
...@@ -36,14 +36,21 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate, ...@@ -36,14 +36,21 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
Query *subquery, Query *subquery,
Attr *alias, Attr *alias,
bool inFromCl); bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
List *colnames,
JoinType jointype,
List *coltypes,
List *coltypmods,
List *leftcols,
List *rightcols,
Attr *alias,
bool inFromCl);
extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
bool addToJoinList, bool addToNameSpace); bool addToJoinList, bool addToNameSpace);
extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname); extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
extern void expandRTE(ParseState *pstate, RangeTblEntry *rte, extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
List **colnames, List **colvars); List **colnames, List **colvars);
extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte); extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte);
extern List *expandJoinAttrs(ParseState *pstate, JoinExpr *join,
int sublevels_up);
extern int attnameAttNum(Relation rd, char *a); extern int attnameAttNum(Relation rd, char *a);
extern Name attnumAttName(Relation rd, int attid); extern Name attnumAttName(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parsetree.h,v 1.16 2001/11/05 17:46:35 momjian Exp $ * $Id: parsetree.h,v 1.17 2002/03/12 00:52:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
* *
* Given the range index of a relation, return the corresponding * Given the range index of a relation, return the corresponding
* relation OID. Note that InvalidOid will be returned if the * relation OID. Note that InvalidOid will be returned if the
* RTE is for a sub-select rather than a relation. * RTE is for a non-relation-type RTE.
*/ */
#define getrelid(rangeindex,rangetable) \ #define getrelid(rangeindex,rangetable) \
(rt_fetch(rangeindex, rangetable)->relid) (rt_fetch(rangeindex, rangetable)->relid)
...@@ -52,4 +52,11 @@ ...@@ -52,4 +52,11 @@
*/ */
extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum); extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum);
/*
* Given an RTE and an attribute number, return the appropriate
* type and typemod info for that attribute of that RTE.
*/
extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Oid *vartype, int32 *vartypmod);
#endif /* PARSETREE_H */ #endif /* PARSETREE_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: builtins.h,v 1.171 2001/11/05 17:46:36 momjian Exp $ * $Id: builtins.h,v 1.172 2002/03/12 00:52:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#define BUILTINS_H #define BUILTINS_H
#include "fmgr.h" #include "fmgr.h"
#include "nodes/primnodes.h" #include "nodes/parsenodes.h"
#include "storage/itemptr.h" /* for setLastTid() */ #include "storage/itemptr.h" /* for setLastTid() */
/* /*
...@@ -343,6 +343,10 @@ extern Datum pg_get_expr(PG_FUNCTION_ARGS); ...@@ -343,6 +343,10 @@ extern Datum pg_get_expr(PG_FUNCTION_ARGS);
extern char *deparse_expression(Node *expr, List *dpcontext, extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix); bool forceprefix);
extern List *deparse_context_for(char *relname, Oid relid); extern List *deparse_context_for(char *relname, Oid relid);
extern List *deparse_context_for_plan(int outer_varno, Node *outercontext,
int inner_varno, Node *innercontext);
extern Node *deparse_context_for_subplan(const char *name, List *tlist,
List *rtable);
/* tid.c */ /* tid.c */
extern void setLastTid(const ItemPointer tid); extern void setLastTid(const ItemPointer tid);
......
...@@ -1846,8 +1846,33 @@ SELECT '' AS "xxx", * ...@@ -1846,8 +1846,33 @@ SELECT '' AS "xxx", *
SELECT '' AS "xxx", * SELECT '' AS "xxx", *
FROM J1_TBL UNION JOIN J2_TBL; FROM J1_TBL UNION JOIN J2_TBL;
ERROR: UNION JOIN is not implemented yet ERROR: UNION JOIN is not implemented yet
--
-- Multiway full join
--
CREATE TABLE t1 (name TEXT, n INTEGER);
CREATE TABLE t2 (name TEXT, n INTEGER);
CREATE TABLE t3 (name TEXT, n INTEGER);
INSERT INTO t1 VALUES ( 'aa', 11 );
INSERT INTO t2 VALUES ( 'aa', 12 );
INSERT INTO t2 VALUES ( 'bb', 22 );
INSERT INTO t2 VALUES ( 'dd', 42 );
INSERT INTO t3 VALUES ( 'aa', 13 );
INSERT INTO t3 VALUES ( 'bb', 23 );
INSERT INTO t3 VALUES ( 'cc', 33 );
SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name);
name | n | n | n
------+----+----+----
aa | 11 | 12 | 13
bb | | 22 | 23
cc | | | 33
dd | | 42 |
(4 rows)
-- --
-- Clean up -- Clean up
-- --
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
DROP TABLE J1_TBL; DROP TABLE J1_TBL;
DROP TABLE J2_TBL; DROP TABLE J2_TBL;
...@@ -198,10 +198,31 @@ SELECT '' AS "xxx", * ...@@ -198,10 +198,31 @@ SELECT '' AS "xxx", *
SELECT '' AS "xxx", * SELECT '' AS "xxx", *
FROM J1_TBL UNION JOIN J2_TBL; FROM J1_TBL UNION JOIN J2_TBL;
--
-- Multiway full join
--
CREATE TABLE t1 (name TEXT, n INTEGER);
CREATE TABLE t2 (name TEXT, n INTEGER);
CREATE TABLE t3 (name TEXT, n INTEGER);
INSERT INTO t1 VALUES ( 'aa', 11 );
INSERT INTO t2 VALUES ( 'aa', 12 );
INSERT INTO t2 VALUES ( 'bb', 22 );
INSERT INTO t2 VALUES ( 'dd', 42 );
INSERT INTO t3 VALUES ( 'aa', 13 );
INSERT INTO t3 VALUES ( 'bb', 23 );
INSERT INTO t3 VALUES ( 'cc', 33 );
SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name);
-- --
-- Clean up -- Clean up
-- --
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
DROP TABLE J1_TBL; DROP TABLE J1_TBL;
DROP TABLE J2_TBL; DROP TABLE J2_TBL;
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