aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/explain.c220
-rw-r--r--src/backend/nodes/copyfuncs.c22
-rw-r--r--src/backend/nodes/equalfuncs.c18
-rw-r--r--src/backend/nodes/outfuncs.c30
-rw-r--r--src/backend/nodes/readfuncs.c36
-rw-r--r--src/backend/optimizer/path/costsize.c8
-rw-r--r--src/backend/optimizer/path/joinpath.c23
-rw-r--r--src/backend/optimizer/path/joinrels.c4
-rw-r--r--src/backend/optimizer/path/pathkeys.c8
-rw-r--r--src/backend/optimizer/plan/createplan.c98
-rw-r--r--src/backend/optimizer/plan/initsplan.c214
-rw-r--r--src/backend/optimizer/plan/planmain.c38
-rw-r--r--src/backend/optimizer/plan/planner.c30
-rw-r--r--src/backend/optimizer/plan/setrefs.c74
-rw-r--r--src/backend/optimizer/prep/prepunion.c20
-rw-r--r--src/backend/optimizer/util/clauses.c11
-rw-r--r--src/backend/optimizer/util/relnode.c150
-rw-r--r--src/backend/optimizer/util/var.c375
-rw-r--r--src/backend/parser/analyze.c44
-rw-r--r--src/backend/parser/parse_clause.c162
-rw-r--r--src/backend/parser/parse_expr.c4
-rw-r--r--src/backend/parser/parse_func.c55
-rw-r--r--src/backend/parser/parse_node.c53
-rw-r--r--src/backend/parser/parse_relation.c357
-rw-r--r--src/backend/parser/parse_target.c66
-rw-r--r--src/backend/rewrite/rewriteHandler.c26
-rw-r--r--src/backend/rewrite/rewriteManip.c34
-rw-r--r--src/backend/utils/adt/ruleutils.c471
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/nodes/parsenodes.h46
-rw-r--r--src/include/nodes/plannodes.h6
-rw-r--r--src/include/nodes/primnodes.h13
-rw-r--r--src/include/nodes/relation.h101
-rw-r--r--src/include/optimizer/pathnode.h6
-rw-r--r--src/include/optimizer/planmain.h11
-rw-r--r--src/include/optimizer/var.h8
-rw-r--r--src/include/parser/parse_relation.h15
-rw-r--r--src/include/parser/parsetree.h11
-rw-r--r--src/include/utils/builtins.h8
-rw-r--r--src/test/regress/expected/join.out25
-rw-r--r--src/test/regress/sql/join.sql23
41 files changed, 1941 insertions, 987 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 319a9c83d82..e230ba598a0 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* 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 @@
#include "executor/instrument.h"
#include "lib/stringinfo.h"
#include "nodes/print.h"
+#include "optimizer/clauses.h"
#include "optimizer/planner.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
+#include "utils/builtins.h"
#include "utils/relcache.h"
+
typedef struct ExplainState
{
/* options */
@@ -32,6 +35,14 @@ typedef struct ExplainState
static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
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 "<>" */
#define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
@@ -40,7 +51,6 @@ static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDes
/*
* ExplainQuery -
* print out the execution plan for a given query
- *
*/
void
ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
@@ -81,7 +91,6 @@ ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
/*
* ExplainOneQuery -
* print out the execution plan for one query
- *
*/
static void
ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
@@ -176,9 +185,6 @@ ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
pfree(es);
}
-/*****************************************************************************
- *
- *****************************************************************************/
/*
* explain_outNode -
@@ -341,6 +347,90 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
}
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 */
if (plan->initPlan)
{
@@ -448,3 +538,121 @@ Explain_PlanToString(Plan *plan, ExplainState *es)
explain_outNode(str, plan, 0, es);
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);
+ }
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ae9ac430456..97eeb35ea38 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.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)
{
newnode->jointype = from->jointype;
Node_Copy(from, newnode, joinqual);
+ newnode->joinrti = from->joinrti;
/* subPlan list must point to subplans in the new subtree, not the old */
if (from->plan.subPlan != NIL)
newnode->plan.subPlan = nconc(newnode->plan.subPlan,
@@ -970,8 +971,7 @@ _copyJoinExpr(JoinExpr *from)
Node_Copy(from, newnode, using);
Node_Copy(from, newnode, quals);
Node_Copy(from, newnode, alias);
- Node_Copy(from, newnode, colnames);
- Node_Copy(from, newnode, colvars);
+ newnode->rtindex = from->rtindex;
return newnode;
}
@@ -1081,16 +1081,13 @@ _copyArrayRef(ArrayRef *from)
* _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 *
_copyRelOptInfo(RelOptInfo *from)
{
RelOptInfo *newnode = makeNode(RelOptInfo);
+ newnode->reloptkind = from->reloptkind;
+
newnode->relids = listCopy(from->relids);
newnode->rows = from->rows;
@@ -1109,6 +1106,9 @@ _copyRelOptInfo(RelOptInfo *from)
newnode->tuples = from->tuples;
Node_Copy(from, newnode, subplan);
+ newnode->joinrti = from->joinrti;
+ newnode->joinrteids = listCopy(from->joinrteids);
+
Node_Copy(from, newnode, baserestrictinfo);
newnode->baserestrictcost = from->baserestrictcost;
newnode->outerjoinset = listCopy(from->outerjoinset);
@@ -1487,10 +1487,16 @@ _copyRangeTblEntry(RangeTblEntry *from)
{
RangeTblEntry *newnode = makeNode(RangeTblEntry);
+ newnode->rtekind = from->rtekind;
if (from->relname)
newnode->relname = pstrdup(from->relname);
newnode->relid = from->relid;
Node_Copy(from, newnode, subquery);
+ newnode->jointype = from->jointype;
+ newnode->joincoltypes = listCopy(from->joincoltypes);
+ newnode->joincoltypmods = listCopy(from->joincoltypmods);
+ newnode->joinleftcols = listCopy(from->joinleftcols);
+ newnode->joinrightcols = listCopy(from->joinrightcols);
Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, eref);
newnode->inh = from->inh;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index b9269c5a834..760554dbb63 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.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)
return false;
if (!equal(a->alias, b->alias))
return false;
- if (!equal(a->colnames, b->colnames))
- return false;
- if (!equal(a->colvars, b->colvars))
+ if (a->rtindex != b->rtindex)
return false;
return true;
@@ -1639,12 +1637,24 @@ _equalTargetEntry(TargetEntry *a, TargetEntry *b)
static bool
_equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
{
+ if (a->rtekind != b->rtekind)
+ return false;
if (!equalstr(a->relname, b->relname))
return false;
if (a->relid != b->relid)
return false;
if (!equal(a->subquery, b->subquery))
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))
return false;
if (!equal(a->eref, b->eref))
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index d3b737ed213..3699fc38ffb 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.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
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -410,6 +410,8 @@ _outJoin(StringInfo str, Join *node)
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->jointype);
_outNode(str, node->joinqual);
+ appendStringInfo(str, " :joinrti %d ",
+ node->joinrti);
}
/*
@@ -423,6 +425,8 @@ _outNestLoop(StringInfo str, NestLoop *node)
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
+ appendStringInfo(str, " :joinrti %d ",
+ node->join.joinrti);
}
/*
@@ -436,6 +440,8 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
+ appendStringInfo(str, " :joinrti %d ",
+ node->join.joinrti);
appendStringInfo(str, " :mergeclauses ");
_outNode(str, node->mergeclauses);
@@ -452,6 +458,8 @@ _outHashJoin(StringInfo str, HashJoin *node)
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
+ appendStringInfo(str, " :joinrti %d ",
+ node->join.joinrti);
appendStringInfo(str, " :hashclauses ");
_outNode(str, node->hashclauses);
@@ -939,10 +947,7 @@ _outJoinExpr(StringInfo str, JoinExpr *node)
_outNode(str, node->quals);
appendStringInfo(str, " :alias ");
_outNode(str, node->alias);
- appendStringInfo(str, " :colnames ");
- _outNode(str, node->colnames);
- appendStringInfo(str, " :colvars ");
- _outNode(str, node->colvars);
+ appendStringInfo(str, " :rtindex %d ", node->rtindex);
}
/*
@@ -961,12 +966,21 @@ _outTargetEntry(StringInfo str, TargetEntry *node)
static void
_outRangeTblEntry(StringInfo str, RangeTblEntry *node)
{
- appendStringInfo(str, " RTE :relname ");
+ appendStringInfo(str, " RTE :rtekind %d :relname ",
+ (int) node->rtekind);
_outToken(str, node->relname);
- appendStringInfo(str, " :relid %u ",
+ appendStringInfo(str, " :relid %u :subquery ",
node->relid);
- appendStringInfo(str, " :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 ");
_outNode(str, node->alias);
appendStringInfo(str, " :eref ");
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 7cdd0c73c39..c0794123b3e 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
*
*
* 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
* Most of the read functions for plan nodes are tested. (In fact, they
@@ -421,6 +421,10 @@ _getJoin(Join *node)
token = pg_strtok(&length); /* skip the :joinqual */
node->joinqual = nodeRead(true); /* get the joinqual */
+
+ token = pg_strtok(&length); /* skip the :joinrti */
+ token = pg_strtok(&length); /* get the joinrti */
+ node->joinrti = atoi(token);
}
@@ -1343,7 +1347,7 @@ _readJoinExpr(void)
local_node->jointype = (JoinType) atoi(token);
token = pg_strtok(&length); /* eat :isNatural */
- token = pg_strtok(&length); /* get :isNatural */
+ token = pg_strtok(&length); /* get isNatural */
local_node->isNatural = strtobool(token);
token = pg_strtok(&length); /* eat :larg */
@@ -1361,11 +1365,9 @@ _readJoinExpr(void)
token = pg_strtok(&length); /* eat :alias */
local_node->alias = nodeRead(true); /* now read it */
- token = pg_strtok(&length); /* eat :colnames */
- local_node->colnames = nodeRead(true); /* now read it */
-
- token = pg_strtok(&length); /* eat :colvars */
- local_node->colvars = nodeRead(true); /* now read it */
+ token = pg_strtok(&length); /* eat :rtindex */
+ token = pg_strtok(&length); /* get rtindex */
+ local_node->rtindex = atoi(token);
return local_node;
}
@@ -1424,6 +1426,10 @@ _readRangeTblEntry(void)
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); /* get :relname */
local_node->relname = nullable_string(token, length);
@@ -1435,6 +1441,22 @@ _readRangeTblEntry(void)
token = pg_strtok(&length); /* eat :subquery */
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 */
local_node->alias = nodeRead(true); /* now read it */
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index e215a6cd366..2bc0bb8a137 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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,
leftvar = get_leftop(firstclause->clause);
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 */
outerscansel = firstclause->left_mergescansel;
@@ -748,7 +748,7 @@ cost_hashjoin(Path *path, Query *root,
* a large query, we cache the bucketsize estimate in the RestrictInfo
* 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 */
innerbucketsize = restrictinfo->right_bucketsize;
@@ -761,7 +761,7 @@ cost_hashjoin(Path *path, Query *root,
}
else
{
- Assert(intMember(left->varno, inner_path->parent->relids));
+ Assert(VARISRELMEMBER(left->varno, inner_path->parent));
/* lefthand side is inner */
innerbucketsize = restrictinfo->left_bucketsize;
if (innerbucketsize < 0)
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 4d60569e7ef..11bc8a9f7d3 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -8,7 +8,7 @@
*
*
* 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 @@
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
+
static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
@@ -720,8 +721,6 @@ hash_inner_and_outer(Query *root,
List *restrictlist,
JoinType jointype)
{
- Relids outerrelids = outerrel->relids;
- Relids innerrelids = innerrel->relids;
bool isouterjoin;
List *i;
@@ -773,13 +772,13 @@ hash_inner_and_outer(Query *root,
/*
* Check if clause is usable with these input rels.
*/
- if (intMember(left->varno, outerrelids) &&
- intMember(right->varno, innerrelids))
+ if (VARISRELMEMBER(left->varno, outerrel) &&
+ VARISRELMEMBER(right->varno, innerrel))
{
/* righthand side is inner */
}
- else if (intMember(left->varno, innerrelids) &&
- intMember(right->varno, outerrelids))
+ else if (VARISRELMEMBER(left->varno, innerrel) &&
+ VARISRELMEMBER(right->varno, outerrel))
{
/* lefthand side is inner */
}
@@ -901,8 +900,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
JoinType jointype)
{
List *result_list = NIL;
- Relids outerrelids = outerrel->relids;
- Relids innerrelids = innerrel->relids;
bool isouterjoin = IS_OUTER_JOIN(jointype);
List *i;
@@ -952,10 +949,10 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
left = get_leftop(clause);
right = get_rightop(clause);
- if ((intMember(left->varno, outerrelids) &&
- intMember(right->varno, innerrelids)) ||
- (intMember(left->varno, innerrelids) &&
- intMember(right->varno, outerrelids)))
+ if ((VARISRELMEMBER(left->varno, outerrel) &&
+ VARISRELMEMBER(right->varno, innerrel)) ||
+ (VARISRELMEMBER(left->varno, innerrel) &&
+ VARISRELMEMBER(right->varno, outerrel)))
result_list = lcons(restrictinfo, result_list);
}
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 745a1eb0b5b..d2d511e75e7 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -8,7 +8,7 @@
*
*
* 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)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- return build_base_rel(root, varno);
+ return find_base_rel(root, varno);
}
else if (IsA(jtnode, FromExpr))
{
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index f91e25cdb4f..60e05ca340f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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,
cache_mergeclause_pathkeys(root, restrictinfo);
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 */
pathkey = restrictinfo->left_pathkey;
@@ -862,7 +863,8 @@ make_pathkeys_for_mergeclauses(Query *root,
else
{
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 */
pathkey = restrictinfo->right_pathkey;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 88c5499b302..79d90bf5a3c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.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,
List *scan_clauses);
static SubqueryScan *create_subqueryscan_plan(Path *best_path,
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,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
@@ -52,7 +53,8 @@ static MergeJoin *create_mergejoin_plan(Query *root,
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_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,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
@@ -78,18 +80,18 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
static NestLoop *make_nestloop(List *tlist,
List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype);
+ JoinType jointype, Index joinrti);
static HashJoin *make_hashjoin(List *tlist,
List *joinclauses, List *otherclauses,
List *hashclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype);
+ JoinType jointype, Index joinrti);
static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses,
List *mergeclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype);
+ JoinType jointype, Index joinrti);
/*
* create_plan
@@ -259,7 +261,8 @@ create_join_plan(Query *root, JoinPath *best_path)
inner_tlist);
break;
case T_HashJoin:
- plan = (Join *) create_hashjoin_plan((HashPath *) best_path,
+ plan = (Join *) create_hashjoin_plan(root,
+ (HashPath *) best_path,
join_tlist,
joinclauses,
otherclauses,
@@ -269,7 +272,8 @@ create_join_plan(Query *root, JoinPath *best_path)
inner_tlist);
break;
case T_NestLoop:
- plan = (Join *) create_nestloop_plan((NestPath *) best_path,
+ plan = (Join *) create_nestloop_plan(root,
+ (NestPath *) best_path,
join_tlist,
joinclauses,
otherclauses,
@@ -576,7 +580,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
*****************************************************************************/
static NestLoop *
-create_nestloop_plan(NestPath *best_path,
+create_nestloop_plan(Query *root,
+ NestPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
@@ -586,6 +591,7 @@ create_nestloop_plan(NestPath *best_path,
List *inner_tlist)
{
NestLoop *join_plan;
+ Index joinrti = best_path->path.parent->joinrti;
if (IsA(inner_plan, IndexScan))
{
@@ -630,19 +636,25 @@ create_nestloop_plan(NestPath *best_path,
/* only refs to outer vars get changed in the inner indexqual */
innerscan->indxqualorig = join_references(indxqualorig,
+ root,
outer_tlist,
NIL,
- innerrel);
+ innerrel,
+ joinrti);
innerscan->indxqual = join_references(innerscan->indxqual,
+ root,
outer_tlist,
NIL,
- innerrel);
+ innerrel,
+ joinrti);
/* fix the inner qpqual too, if it has join clauses */
if (NumRelids((Node *) inner_plan->qual) > 1)
inner_plan->qual = join_references(inner_plan->qual,
+ root,
outer_tlist,
NIL,
- innerrel);
+ innerrel,
+ joinrti);
}
}
else if (IsA(inner_plan, TidScan))
@@ -650,9 +662,11 @@ create_nestloop_plan(NestPath *best_path,
TidScan *innerscan = (TidScan *) inner_plan;
innerscan->tideval = join_references(innerscan->tideval,
+ root,
outer_tlist,
inner_tlist,
- innerscan->scan.scanrelid);
+ innerscan->scan.scanrelid,
+ joinrti);
}
else if (IsA_Join(inner_plan))
{
@@ -671,20 +685,25 @@ create_nestloop_plan(NestPath *best_path,
* Set quals to contain INNER/OUTER var references.
*/
joinclauses = join_references(joinclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
otherclauses = join_references(otherclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
outer_plan,
inner_plan,
- best_path->jointype);
+ best_path->jointype,
+ joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->path);
@@ -704,6 +723,7 @@ create_mergejoin_plan(Query *root,
{
List *mergeclauses;
MergeJoin *join_plan;
+ Index joinrti = best_path->jpath.path.parent->joinrti;
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
@@ -713,26 +733,32 @@ create_mergejoin_plan(Query *root,
* clauses to contain INNER/OUTER var references.
*/
joinclauses = join_references(set_difference(joinclauses, mergeclauses),
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
/*
* Fix the additional qpquals too.
*/
otherclauses = join_references(otherclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
/*
* Now set the references in the mergeclauses and rearrange them so
* that the outer variable is always on the left.
*/
mergeclauses = switch_outer(join_references(mergeclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0));
+ (Index) 0,
+ joinrti));
/*
* Create explicit sort nodes for the outer and inner join paths if
@@ -798,7 +824,8 @@ create_mergejoin_plan(Query *root,
mergeclauses,
outer_plan,
inner_plan,
- best_path->jpath.jointype);
+ best_path->jpath.jointype,
+ joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
@@ -806,7 +833,8 @@ create_mergejoin_plan(Query *root,
}
static HashJoin *
-create_hashjoin_plan(HashPath *best_path,
+create_hashjoin_plan(Query *root,
+ HashPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
@@ -819,6 +847,7 @@ create_hashjoin_plan(HashPath *best_path,
HashJoin *join_plan;
Hash *hash_plan;
Node *innerhashkey;
+ Index joinrti = best_path->jpath.path.parent->joinrti;
/*
* NOTE: there will always be exactly one hashclause in the list
@@ -834,26 +863,32 @@ create_hashjoin_plan(HashPath *best_path,
* clauses to contain INNER/OUTER var references.
*/
joinclauses = join_references(set_difference(joinclauses, hashclauses),
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
/*
* Fix the additional qpquals too.
*/
otherclauses = join_references(otherclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
/*
* Now set the references in the hashclauses and rearrange them so
* that the outer variable is always on the left.
*/
hashclauses = switch_outer(join_references(hashclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0));
+ (Index) 0,
+ joinrti));
/* Now the righthand op of the sole hashclause is the inner hash key. */
innerhashkey = (Node *) get_rightop(lfirst(hashclauses));
@@ -868,7 +903,8 @@ create_hashjoin_plan(HashPath *best_path,
hashclauses,
outer_plan,
(Plan *) hash_plan,
- best_path->jpath.jointype);
+ best_path->jpath.jointype,
+ joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
@@ -1327,7 +1363,8 @@ make_nestloop(List *tlist,
List *otherclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype)
+ JoinType jointype,
+ Index joinrti)
{
NestLoop *node = makeNode(NestLoop);
Plan *plan = &node->join.plan;
@@ -1340,6 +1377,7 @@ make_nestloop(List *tlist,
plan->righttree = righttree;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->join.joinrti = joinrti;
return node;
}
@@ -1351,7 +1389,8 @@ make_hashjoin(List *tlist,
List *hashclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype)
+ JoinType jointype,
+ Index joinrti)
{
HashJoin *node = makeNode(HashJoin);
Plan *plan = &node->join.plan;
@@ -1365,6 +1404,7 @@ make_hashjoin(List *tlist,
node->hashclauses = hashclauses;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->join.joinrti = joinrti;
return node;
}
@@ -1399,7 +1439,8 @@ make_mergejoin(List *tlist,
List *mergeclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype)
+ JoinType jointype,
+ Index joinrti)
{
MergeJoin *node = makeNode(MergeJoin);
Plan *plan = &node->join.plan;
@@ -1413,6 +1454,7 @@ make_mergejoin(List *tlist,
node->mergeclauses = mergeclauses;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->join.joinrti = joinrti;
return node;
}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 76677b4059c..2c9acc73b7f 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.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);
/*****************************************************************************
*
- * TARGET LISTS
+ * JOIN TREES
*
*****************************************************************************/
/*
- * build_base_rel_tlists
- * Creates rel nodes for every relation mentioned in the target list
- * 'tlist' (if a node hasn't already been created) and adds them to
- * root->base_rel_list. 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 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
+ * add_base_rels_to_query
+ *
+ * Scan the query's jointree and create baserel RelOptInfos for all
+ * the base relations (ie, table and subquery RTEs) appearing in the
+ * jointree. Also, create otherrel RelOptInfos for join RTEs.
*
- * If we have a relation listed in the join tree that does not appear
- * in the target list nor qualifications, we must add it to the base
- * relation list so that it can be processed. For instance,
- * select count(*) from foo;
- * 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).
+ * The return value is a list of all the baserel indexes (but not join RTE
+ * indexes) included in the scanned jointree. This is actually just an
+ * internal convenience for marking join otherrels properly; no outside
+ * caller uses the result.
*
- * Returns a list of all the base relations (RelOptInfo nodes) that appear
- * in the join tree. This list can be used for cross-checking in the
- * reverse direction, ie, that we have a join tree entry for every
- * relation used in the query.
- *----------
+ * At the end of this process, there should be one baserel RelOptInfo for
+ * every non-join RTE that is used in the query. Therefore, this routine
+ * is the only place that should call build_base_rel. But build_other_rel
+ * will be used again later to build rels for inheritance children.
*/
List *
-add_missing_rels_to_query(Query *root, Node *jtnode)
+add_base_rels_to_query(Query *root, Node *jtnode)
{
List *result = NIL;
@@ -123,10 +85,8 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- /* This call to build_base_rel does the primary work... */
- RelOptInfo *rel = build_base_rel(root, varno);
-
- result = makeList1(rel);
+ build_base_rel(root, varno);
+ result = makeListi1(varno);
}
else if (IsA(jtnode, FromExpr))
{
@@ -136,19 +96,33 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
foreach(l, f->fromlist)
{
result = nconc(result,
- add_missing_rels_to_query(root, lfirst(l)));
+ add_base_rels_to_query(root, lfirst(l)));
}
}
else if (IsA(jtnode, JoinExpr))
{
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,
- 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
- elog(ERROR, "add_missing_rels_to_query: unexpected node type %d",
+ elog(ERROR, "add_base_rels_to_query: unexpected node type %d",
nodeTag(jtnode));
return result;
}
@@ -156,6 +130,66 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
/*****************************************************************************
*
+ * 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
*
*****************************************************************************/
@@ -165,10 +199,9 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
* distribute_quals_to_rels
* Recursively scan the query's join tree for WHERE and JOIN/ON qual
* clauses, and add these to the appropriate RestrictInfo and JoinInfo
- * lists belonging to base RelOptInfos. New base rel entries are created
- * as needed. Also, base RelOptInfos are marked with outerjoinset
- * information, to aid in proper positioning of qual clauses that appear
- * above outer joins.
+ * lists belonging to base RelOptInfos. Also, base RelOptInfos are marked
+ * with outerjoinset information, to aid in proper positioning of qual
+ * clauses that appear above outer joins.
*
* 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
@@ -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.
*
* 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.
*/
Relids
@@ -302,7 +335,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
foreach(relid, rels)
{
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
@@ -382,11 +415,41 @@ distribute_qual_to_rels(Query *root, Node *clause,
clause_get_relids_vars(clause, &relids, &vars);
/*
- * Cross-check: clause should contain no relids not within its scope.
- * Otherwise the parser messed up.
+ * The clause might contain some join alias vars; if so, we want to
+ * 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))
- elog(ERROR, "JOIN qualification may not refer to other relations");
+ {
+ 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))
+ elog(ERROR, "JOIN qualification may not refer to other relations");
+ }
/*
* 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,
can_be_equijoin = true;
foreach(relid, relids)
{
- RelOptInfo *rel = build_base_rel(root, lfirsti(relid));
+ RelOptInfo *rel = find_base_rel(root, lfirsti(relid));
if (rel->outerjoinset &&
!is_subseti(rel->outerjoinset, relids))
@@ -475,7 +538,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
* There is only one relation participating in 'clause', so
* '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
@@ -595,7 +658,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
* Find or make the joininfo node for this combination of rels,
* 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);
joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
restrictinfo);
@@ -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
* baserestrictinfo list. If different rels, each will have a
* 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);
if (irel1 == irel2)
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 57c9b963c73..ee1d1890712 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
*
*
* 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,
List *flat_tlist,
double tuple_fraction)
{
- List *joined_rels;
- List *brel;
RelOptInfo *final_rel;
Plan *resultplan;
Path *cheapestpath;
Path *presortedpath;
- /*
- * 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.
- */
+ /* init lists to empty */
root->base_rel_list = NIL;
root->other_rel_list = NIL;
root->join_rel_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
- * the query --- otherwise, the parser or rewriter messed up.
+ * Examine the targetlist and qualifications, adding entries to
+ * 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)
- {
- RelOptInfo *baserel = (RelOptInfo *) lfirst(brel);
- int relid = lfirsti(baserel->relids);
+ build_base_rel_tlists(root, flat_tlist);
- if (!ptrMember(baserel, joined_rels))
- elog(ERROR, "Internal error: no jointree entry for rel %s (%d)",
- rt_fetch(relid, root->rtable)->eref->relname, relid);
- }
+ (void) distribute_quals_to_rels(root, (Node *) root->jointree);
/*
* Use the completed lists of equijoined keys to deduce any implied
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index eb92c3d3af9..1df2cd29402 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.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)
result_plan->nParamExec = length(PlannerParamVar);
/* final cleanup of the plan */
- set_plan_references(result_plan);
+ set_plan_references(parse, result_plan);
/* restore state for outer planner, if any */
PlannerQueryLevel = save_PlannerQueryLevel;
@@ -616,6 +616,9 @@ preprocess_jointree(Query *parse, Node *jtnode)
static Node *
preprocess_expression(Query *parse, Node *expr, int kind)
{
+ bool has_join_rtes;
+ List *rt;
+
/*
* Simplify constant expressions.
*
@@ -651,6 +654,29 @@ preprocess_expression(Query *parse, Node *expr, int kind)
if (PlannerQueryLevel > 1)
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;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 167901acdf0..2f48821ece6 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,25 +9,29 @@
*
*
* 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 <sys/types.h>
+
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
+#include "optimizer/var.h"
+
typedef struct
{
+ Query *root;
List *outer_tlist;
List *inner_tlist;
Index acceptable_rel;
+ Index join_rti;
} join_references_context;
typedef struct
@@ -38,7 +42,7 @@ typedef struct
} replace_vars_with_subplan_refs_context;
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 Node *join_references_mutator(Node *node,
join_references_context *context);
@@ -71,7 +75,7 @@ static bool fix_opids_walker(Node *node, void *context);
* Returns nothing of interest, but modifies internal fields of nodes.
*/
void
-set_plan_references(Plan *plan)
+set_plan_references(Query *root, Plan *plan)
{
List *pl;
@@ -115,16 +119,16 @@ set_plan_references(Plan *plan)
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
/* Recurse into subplan too */
- set_plan_references(((SubqueryScan *) plan)->subplan);
+ set_plan_references(root, ((SubqueryScan *) plan)->subplan);
break;
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->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
break;
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->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
@@ -132,7 +136,7 @@ set_plan_references(Plan *plan)
(Node *) ((MergeJoin *) plan)->mergeclauses);
break;
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->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
@@ -186,7 +190,7 @@ set_plan_references(Plan *plan)
* recurse into subplans.
*/
foreach(pl, ((Append *) plan)->appendplans)
- set_plan_references((Plan *) lfirst(pl));
+ set_plan_references(root, (Plan *) lfirst(pl));
break;
default:
elog(ERROR, "set_plan_references: unknown plan type %d",
@@ -203,21 +207,21 @@ set_plan_references(Plan *plan)
* plan's var nodes against the already-modified nodes of the
* subplans.
*/
- set_plan_references(plan->lefttree);
- set_plan_references(plan->righttree);
+ set_plan_references(root, plan->lefttree);
+ set_plan_references(root, plan->righttree);
foreach(pl, plan->initPlan)
{
SubPlan *sp = (SubPlan *) lfirst(pl);
Assert(IsA(sp, SubPlan));
- set_plan_references(sp->plan);
+ set_plan_references(root, sp->plan);
}
foreach(pl, plan->subPlan)
{
SubPlan *sp = (SubPlan *) lfirst(pl);
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)
* 'join' is a join plan node
*/
static void
-set_join_references(Join *join)
+set_join_references(Query *root, Join *join)
{
Plan *outer = join->plan.lefttree;
Plan *inner = join->plan.righttree;
@@ -264,9 +268,11 @@ set_join_references(Join *join)
List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
join->plan.targetlist = join_references(join->plan.targetlist,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ join->joinrti);
}
/*
@@ -343,7 +349,8 @@ set_uppernode_references(Plan *plan, Index subvarno)
* Creates a new set of targetlist entries or join qual clauses by
* changing the varno/varattno values of variables in the clauses
* 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
* 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)
* 'inner_tlist' is the target list of the inner join relation, or NIL
* 'acceptable_rel' is either zero or the rangetable index of a relation
* whose Vars may appear in the clause without provoking an error.
+ * 'join_rti' is either zero or the join RTE index of join alias variables
+ * that should be expanded.
*
* Returns the new expression tree. The original clause structure is
* not modified.
*/
List *
join_references(List *clauses,
+ Query *root,
List *outer_tlist,
List *inner_tlist,
- Index acceptable_rel)
+ Index acceptable_rel,
+ Index join_rti)
{
join_references_context context;
+ context.root = root;
context.outer_tlist = outer_tlist;
context.inner_tlist = inner_tlist;
context.acceptable_rel = acceptable_rel;
+ context.join_rti = join_rti;
return (List *) join_references_mutator((Node *) clauses, &context);
}
@@ -387,12 +400,14 @@ join_references_mutator(Node *node,
if (IsA(node, Var))
{
Var *var = (Var *) node;
- Var *newvar = (Var *) copyObject(var);
Resdom *resdom;
+ /* First look for the var in the input tlists */
resdom = tlist_member((Node *) var, context->outer_tlist);
if (resdom)
{
+ Var *newvar = (Var *) copyObject(var);
+
newvar->varno = OUTER;
newvar->varattno = resdom->resno;
return (Node *) newvar;
@@ -400,18 +415,33 @@ join_references_mutator(Node *node,
resdom = tlist_member((Node *) var, context->inner_tlist);
if (resdom)
{
+ Var *newvar = (Var *) copyObject(var);
+
newvar->varno = INNER;
newvar->varattno = resdom->resno;
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
- * the Var unmodified.
+ * No referent found for Var --- either raise an error, or return
+ * the Var unmodified if it's for acceptable_rel.
*/
if (var->varno != context->acceptable_rel)
elog(ERROR, "join_references: variable not in subplan target lists");
- return (Node *) newvar;
+ return (Node *) copyObject(var);
}
return expression_tree_mutator(node,
join_references_mutator,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 64d8b78f066..b2f18780fcc 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -14,7 +14,7 @@
*
*
* 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 *
expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
{
RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
- Oid parentOID = rte->relid;
+ Oid parentOID;
List *inhOIDs;
List *inhRTIs;
List *l;
@@ -681,10 +681,11 @@ expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
/* Does RT entry allow inheritance? */
if (!rte->inh)
return NIL;
- Assert(parentOID != InvalidOid && rte->subquery == NULL);
+ Assert(rte->rtekind == RTE_RELATION);
/* Always clear the parent's inh flag, see above comments */
rte->inh = false;
/* Fast path for common case of childless table */
+ parentOID = rte->relid;
if (!has_subclass(parentOID))
return NIL;
/* Scan for all members of inheritance set */
@@ -811,6 +812,19 @@ adjust_inherited_attrs_mutator(Node *node,
rtr->rtindex = context->new_rt_index;
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
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8a4bcf4d9c4..6585fb1905d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* 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
* AUTHOR DATE MAJOR EVENT
@@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node,
return true;
if (walker(join->quals, context))
return true;
- if (walker((Node *) join->colvars, context))
- return true;
-
/*
- * alias clause, using list, colnames list are deemed
- * uninteresting.
+ * alias clause, using list are deemed uninteresting.
*/
}
break;
@@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node,
MUTATE(newnode->larg, join->larg, Node *);
MUTATE(newnode->rarg, join->rarg, Node *);
MUTATE(newnode->quals, join->quals, Node *);
- MUTATE(newnode->colvars, join->colvars, List *);
- /* We do not mutate alias, using, or colnames by default */
+ /* We do not mutate alias or using by default */
return (Node *) newnode;
}
break;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 457826f5d31..0fad16fbdd0 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
*
*
* 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,
/*
* build_base_rel
- * Returns relation entry corresponding to 'relid', creating a new one
- * if necessary. This is for base relations.
+ * Construct a new base relation RelOptInfo, and put it in the query's
+ * base_rel_list.
*/
-RelOptInfo *
+void
build_base_rel(Query *root, int relid)
{
List *rels;
RelOptInfo *rel;
- /* Already made? */
+ /* Rel should not exist already */
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
/* length(rel->relids) == 1 for all members of base_rel_list */
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)
{
rel = (RelOptInfo *) lfirst(rels);
@@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid)
/* and add it to the list */
root->base_rel_list = lcons(rel, root->base_rel_list);
-
- return rel;
}
/*
* build_other_rel
* 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.
*/
RelOptInfo *
@@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid)
/* No existing RelOptInfo for this other rel, so make a new one */
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 */
root->other_rel_list = lcons(rel, root->other_rel_list);
@@ -127,8 +129,9 @@ static RelOptInfo *
make_base_rel(Query *root, int relid)
{
RelOptInfo *rel = makeNode(RelOptInfo);
- Oid relationObjectId;
+ RangeTblEntry *rte = rt_fetch(relid, root->rtable);
+ rel->reloptkind = RELOPT_BASEREL;
rel->relids = makeListi1(relid);
rel->rows = 0;
rel->width = 0;
@@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid)
rel->pages = 0;
rel->tuples = 0;
rel->subplan = NULL;
+ rel->joinrti = 0;
+ rel->joinrteids = NIL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost = 0;
rel->outerjoinset = NIL;
rel->joininfo = NIL;
rel->innerjoin = NIL;
- /* Check rtable to see if it's a plain relation or a subquery */
- relationObjectId = getrelid(relid, root->rtable);
-
- if (relationObjectId != InvalidOid)
- {
- /* Plain relation --- retrieve statistics from the system catalogs */
- bool indexed;
-
- get_relation_info(relationObjectId,
- &indexed, &rel->pages, &rel->tuples);
- if (indexed)
- rel->indexlist = find_secondary_indexes(relationObjectId);
- }
- else
+ /* Check type of rtable entry */
+ switch (rte->rtekind)
{
- /* subquery --- mark it as such for later processing */
- rel->issubquery = true;
+ case RTE_RELATION:
+ {
+ /* Table --- retrieve statistics from the system catalogs */
+ bool indexed;
+
+ get_relation_info(rte->relid,
+ &indexed, &rel->pages, &rel->tuples);
+ if (indexed)
+ rel->indexlist = find_secondary_indexes(rte->relid);
+ break;
+ }
+ case RTE_SUBQUERY:
+ /* Subquery --- mark it as such for later processing */
+ 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;
@@ -204,6 +218,47 @@ find_base_rel(Query *root, int relid)
}
/*
+ * 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
* Returns relation entry corresponding to 'relids' (a list of RT indexes),
* or NULL if none exists. This is for join relations.
@@ -252,6 +307,7 @@ build_join_rel(Query *root,
{
List *joinrelids;
RelOptInfo *joinrel;
+ RelOptInfo *joinrterel;
List *restrictlist;
List *new_outer_tlist;
List *new_inner_tlist;
@@ -286,6 +342,7 @@ build_join_rel(Query *root,
* Nope, so make one.
*/
joinrel = makeNode(RelOptInfo);
+ joinrel->reloptkind = RELOPT_JOINREL;
joinrel->relids = joinrelids;
joinrel->rows = 0;
joinrel->width = 0;
@@ -299,24 +356,38 @@ build_join_rel(Query *root,
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->subplan = NULL;
+ joinrel->joinrti = 0;
+ joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids),
+ inner_rel->joinrteids);
joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost = 0;
joinrel->outerjoinset = NIL;
joinrel->joininfo = 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
* of the outer and inner join relations and then merging the results
* 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
* outer and inner rels we first try to build it from. But the
* 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_inner_tlist = new_join_tlist(inner_rel->targetlist,
@@ -324,6 +395,23 @@ build_join_rel(Query *root,
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
* caller might or might not need the restrictlist, but I need it
* anyway for set_joinrel_size_estimates().)
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 2521ffb2b6c..568e024d20b 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,15 +8,18 @@
*
*
* 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 "nodes/makefuncs.h"
#include "nodes/plannodes.h"
#include "optimizer/clauses.h"
#include "optimizer/var.h"
+#include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
typedef struct
@@ -38,6 +41,12 @@ typedef struct
bool includeUpperVars;
} pull_var_clause_context;
+typedef struct
+{
+ Query *root;
+ int expandRTI;
+} flatten_join_alias_vars_context;
+
static bool pull_varnos_walker(Node *node,
pull_varnos_context *context);
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 pull_var_clause_walker(Node *node,
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)
return expression_tree_walker(node, pull_var_clause_walker,
(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;
+}
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5fb298e1c27..98f5030f78b 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.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)
Node *node;
List *lefttl,
*dtlist,
- *targetvars,
+ *colMods,
*targetnames,
- *sv_namespace;
- JoinExpr *jnode;
+ *sv_namespace,
+ *sv_rtable;
+ RangeTblEntry *jrte;
+ RangeTblRef *jrtr;
int tllen;
qry->commandType = CMD_SELECT;
@@ -2115,12 +2117,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/*
* Generate dummy targetlist for outer query using column names of
* leftmost select and common datatypes of topmost set operation. Also
- * make lists of the dummy vars and their names for use in parsing
- * ORDER BY.
+ * make a list of the column names for use in parsing ORDER BY.
+ *
+ * XXX colMods is a hack to provide a dummy typmod list below. We
+ * should probably keep track of common typmod instead.
*/
qry->targetList = NIL;
- targetvars = NIL;
targetnames = NIL;
+ colMods = NIL;
lefttl = leftmostQuery->targetList;
foreach(dtlist, sostmt->colTypes)
{
@@ -2135,15 +2139,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
-1,
colName,
false);
- expr = (Node *) makeVar(leftmostRTI,
+ expr = (Node *) makeVar(1,
leftResdom->resno,
colType,
-1,
0);
qry->targetList = lappend(qry->targetList,
makeTargetEntry(resdom, expr));
- targetvars = lappend(targetvars, expr);
targetnames = lappend(targetnames, makeString(colName));
+ colMods = lappendi(colMods, -1);
lefttl = lnext(lefttl);
}
@@ -2190,7 +2194,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/*
* As a first step towards supporting sort clauses that are
* 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
* matches.
*
@@ -2198,12 +2202,23 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
* "ORDER BY upper(foo)" will draw the right error message rather than
* "foo not found".
*/
- jnode = makeNode(JoinExpr);
- jnode->colnames = targetnames;
- jnode->colvars = targetvars;
+ jrte = addRangeTableEntryForJoin(NULL,
+ targetnames,
+ 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;
- pstate->p_namespace = makeList1(jnode);
+ pstate->p_namespace = makeList1(jrtr);
/*
* For now, we don't support resjunk sort clauses on the output of a
@@ -2218,6 +2233,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList);
pstate->p_namespace = sv_namespace;
+ pstate->p_rtable = sv_rtable;
if (tllen != length(qry->targetList))
elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns");
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 61904439e7d..2f1eda4a9bf 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* 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)
*res_colnames,
*l_colvars,
*r_colvars,
- *res_colvars;
+ *coltypes,
+ *coltypmods,
+ *leftcolnos,
+ *rightcolnos;
+ Index leftrti,
+ rightrti;
+ RangeTblEntry *rte;
/*
* Recursively process the left and right subtrees
@@ -525,39 +531,32 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
/*
* Extract column name and var lists from both subtrees
+ *
+ * Note: expandRTE returns new lists, safe for me to modify
*/
- if (IsA(j->larg, JoinExpr))
- {
- /* Make a copy of the subtree's lists so we can modify! */
- l_colnames = copyObject(((JoinExpr *) j->larg)->colnames);
- l_colvars = copyObject(((JoinExpr *) j->larg)->colvars);
- }
+ if (IsA(j->larg, RangeTblRef))
+ leftrti = ((RangeTblRef *) j->larg)->rtindex;
+ else if (IsA(j->larg, JoinExpr))
+ leftrti = ((JoinExpr *) j->larg)->rtindex;
else
{
- RangeTblEntry *rte;
-
- 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);
+ elog(ERROR, "transformFromClauseItem: unexpected subtree type");
+ leftrti = 0; /* keep compiler quiet */
}
+ 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
{
- RangeTblEntry *rte;
-
- 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 */
+ elog(ERROR, "transformFromClauseItem: unexpected subtree type");
+ rightrti = 0; /* keep compiler quiet */
}
+ rte = rt_fetch(rightrti, pstate->p_rtable);
+ expandRTE(pstate, rte, &r_colnames, &r_colvars);
/*
* Natural join does not explicitly specify columns; must generate
@@ -604,7 +603,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
* Now transform the join qualifications, if any.
*/
res_colnames = NIL;
- res_colvars = NIL;
+ coltypes = NIL;
+ coltypmods = NIL;
+ leftcolnos = NIL;
+ rightcolnos = NIL;
if (j->using)
{
@@ -624,9 +626,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
{
char *u_colname = strVal(lfirst(ucol));
List *col;
- Node *l_colvar,
- *r_colvar,
- *colvar;
+ Var *l_colvar,
+ *r_colvar;
+ Oid outcoltype;
+ int32 outcoltypmod;
int ndx;
int l_index = -1;
int r_index = -1;
@@ -672,34 +675,28 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
res_colnames = lappend(res_colnames,
nth(l_index, l_colnames));
- switch (j->jointype)
+ /*
+ * Choose output type if input types are dissimilar.
+ */
+ outcoltype = l_colvar->vartype;
+ outcoltypmod = l_colvar->vartypmod;
+ if (outcoltype != r_colvar->vartype)
{
- case JOIN_INNER:
- case JOIN_LEFT:
- colvar = l_colvar;
- break;
- case JOIN_RIGHT:
- colvar = r_colvar;
- break;
- default:
- {
- /* Need COALESCE(l_colvar, r_colvar) */
- CaseExpr *c = makeNode(CaseExpr);
- CaseWhen *w = makeNode(CaseWhen);
- NullTest *n = makeNode(NullTest);
-
- 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;
- }
+ outcoltype =
+ select_common_type(makeListi2(l_colvar->vartype,
+ r_colvar->vartype),
+ "JOIN/USING");
+ outcoltypmod = -1; /* ie, unknown */
}
- res_colvars = lappend(res_colvars, colvar);
+ else if (outcoltypmod != r_colvar->vartypmod)
+ {
+ /* same type, but not same typmod */
+ outcoltypmod = -1; /* ie, unknown */
+ }
+ coltypes = lappendi(coltypes, outcoltype);
+ coltypmods = lappendi(coltypmods, outcoltypmod);
+ leftcolnos = lappendi(leftcolnos, l_index+1);
+ rightcolnos = lappendi(rightcolnos, r_index+1);
}
j->quals = transformJoinUsingClause(pstate,
@@ -724,30 +721,53 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
r_colnames, r_colvars,
&r_colnames, &r_colvars);
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_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 a column alias list is specified, substitute the alias
- * names into my output-column list
- */
if (j->alias->attrs != NIL)
{
- if (length(j->alias->attrs) != length(res_colnames))
- elog(ERROR, "Column alias list for \"%s\" has wrong number of entries (need %d)",
- j->alias->relname, length(res_colnames));
- res_colnames = j->alias->attrs;
+ if (length(j->alias->attrs) > length(res_colnames))
+ elog(ERROR, "Column alias list for \"%s\" has too many entries",
+ j->alias->relname);
}
}
- 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;
}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index f740d632cc1..9c32fac2314 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* 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)
* appear
*/
if (ident->indirection == NIL &&
- refnameRangeOrJoinEntry(pstate, ident->name, &sublevels_up) != NULL)
+ refnameRangeTblEntry(pstate, ident->name, &sublevels_up) != NULL)
{
ident->isRel = TRUE;
result = (Node *) ident;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 766a5daad55..ed39d6c1036 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
*
*
* 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,
{
RangeTblEntry *rte;
int vnum;
- Node *rteorjoin;
int sublevels_up;
/*
@@ -324,49 +323,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
*/
refname = ((Ident *) arg)->name;
- rteorjoin = refnameRangeOrJoinEntry(pstate, refname,
- &sublevels_up);
+ rte = refnameRangeTblEntry(pstate, refname,
+ &sublevels_up);
- if (rteorjoin == NULL)
+ if (rte == NULL)
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);
@@ -379,11 +340,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
* sizeof(Pointer) to signal that the runtime representation
* 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
- * type
+ * RTE is a join or subselect; must fail for lack of a
+ * named tuple type
*/
if (nargs == 1)
{
@@ -397,7 +358,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
}
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);
}
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index a43dcb13af2..be825c26f9e 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -8,21 +8,22 @@
*
*
* 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 <errno.h>
#include <float.h>
-#include "postgres.h"
-
#include "access/heapam.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "fmgr.h"
#include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_node.h"
@@ -165,51 +166,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
{
int vnum,
sublevels_up;
- Oid vartypeid = 0;
- int32 type_mod = 0;
+ Oid vartypeid;
+ int32 type_mod;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
-
- 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);
- }
-
+ get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod);
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
}
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 9440914a776..1609c89ce07 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
*
*
* 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,38 +36,41 @@ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
char *refname);
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname);
-static Node *scanJoinForColumn(JoinExpr *join, char *colname,
- int sublevels_up);
static bool isForUpdate(ParseState *pstate, char *relname);
-static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
static int specialAttNum(char *a);
static void warnAutoRange(ParseState *pstate, char *refname);
/*
- * refnameRangeOrJoinEntry
- * Given a refname, look to see if it matches any RTE or join table.
- * If so, return a pointer to the RangeTblEntry or JoinExpr.
+ * refnameRangeTblEntry
+ * Given a refname, look to see if it matches any RTE.
+ * If so, return a pointer to the RangeTblEntry.
* Optionally get its nesting depth (0 = current). If sublevels_up
* is NULL, only consider items at the current nesting level.
*/
-Node *
-refnameRangeOrJoinEntry(ParseState *pstate,
- char *refname,
- int *sublevels_up)
+RangeTblEntry *
+refnameRangeTblEntry(ParseState *pstate,
+ char *refname,
+ int *sublevels_up)
{
if (sublevels_up)
*sublevels_up = 0;
while (pstate != NULL)
{
- Node *rte;
+ Node *nsnode;
- rte = scanNameSpaceForRefname(pstate,
- (Node *) pstate->p_namespace,
- refname);
- if (rte)
- return rte;
+ nsnode = scanNameSpaceForRefname(pstate,
+ (Node *) pstate->p_namespace,
+ refname);
+ if (nsnode)
+ {
+ /* 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;
if (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.
* 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 *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
@@ -279,10 +288,9 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
return result;
/*
- * If the RTE represents a table (not a sub-select), consider system
- * column names.
+ * If the RTE represents a real table, consider system column names.
*/
- if (rte->relid != InvalidOid)
+ if (rte->rtekind == RTE_RELATION)
{
/* quick check to see if name could be a system column */
attnum = specialAttNum(colname);
@@ -304,44 +312,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
}
/*
- * 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
* Search for an unqualified column name.
* If found, return the appropriate Var node (or expression).
@@ -382,9 +352,13 @@ colnameToVar(ParseState *pstate, char *colname)
}
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
elog(ERROR, "colnameToVar: unexpected node type %d",
@@ -412,41 +386,26 @@ colnameToVar(ParseState *pstate, char *colname)
/*
* qualifiedNameToVar
* 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.
*/
Node *
qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
bool implicitRTEOK)
{
- Node *result;
- Node *rteorjoin;
+ RangeTblEntry *rte;
int sublevels_up;
- rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up);
+ rte = refnameRangeTblEntry(pstate, refname, &sublevels_up);
- if (rteorjoin == NULL)
+ if (rte == NULL)
{
if (!implicitRTEOK)
return NULL;
- rteorjoin = (Node *) addImplicitRTE(pstate, refname);
- sublevels_up = 0;
+ rte = addImplicitRTE(pstate, refname);
}
- if (IsA(rteorjoin, RangeTblEntry))
- 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;
+ return scanRTEForColumn(pstate, rte, colname);
}
/*
@@ -474,9 +433,9 @@ addRangeTableEntry(ParseState *pstate,
int numaliases;
int varattno;
+ rte->rtekind = RTE_RELATION;
rte->relname = relname;
rte->alias = alias;
- rte->subquery = NULL;
/*
* Get the rel's OID. This access also ensures that we have an
@@ -563,6 +522,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
int varattno;
List *tlistitem;
+ rte->rtekind = RTE_SUBQUERY;
rte->relname = NULL;
rte->relid = InvalidOid;
rte->subquery = subquery;
@@ -622,6 +582,76 @@ addRangeTableEntryForSubquery(ParseState *pstate,
}
/*
+ * 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?
*/
static bool
@@ -720,15 +750,16 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
- if (rte->relname)
+ if (rte->rtekind == RTE_RELATION)
{
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
+ int numaliases;
rel = heap_openr(rte->relname, AccessShareLock);
-
maxattrs = RelationGetNumberOfAttributes(rel);
+ numaliases = length(rte->eref->attrs);
for (varattno = 0; varattno < maxattrs; varattno++)
{
@@ -743,7 +774,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{
char *label;
- if (varattno < length(rte->eref->attrs))
+ if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->attrs));
else
label = NameStr(attr->attname);
@@ -764,7 +795,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
heap_close(rel, AccessShareLock);
}
- else
+ else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subquery RTE */
List *aliasp = rte->eref->attrs;
@@ -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;
-/*
- * expandRelAttrs -
- * makes a list of TargetEntry nodes for the attributes of the rel
- */
-List *
-expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
-{
- List *name_list,
- *var_list;
+ varattno = 0;
+ while (aliasp)
+ {
+ Assert(coltypes && coltypmods);
+ varattno++;
- 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)));
+ }
-/*
- * expandJoinAttrs -
- * makes a list of TargetEntry nodes for the attributes of the join
- */
-List *
-expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
-{
- List *vars;
+ if (colvars)
+ {
+ Var *varnode;
- vars = copyObject(join->colvars);
+ varnode = makeVar(rtindex, varattno,
+ (Oid) lfirsti(coltypes),
+ (int32) lfirsti(coltypmods),
+ sublevels_up);
- /*
- * 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);
+ *colvars = lappend(*colvars, varnode);
+ }
- return expandNamesVars(pstate,
- copyObject(join->colnames),
- vars);
+ aliasp = lnext(aliasp);
+ coltypes = lnext(coltypes);
+ coltypmods = lnext(coltypmods);
+ }
+ Assert(coltypes == NIL && coltypmods == NIL);
+ }
+ else
+ elog(ERROR, "expandRTE: unsupported RTE kind %d",
+ (int) rte->rtekind);
}
/*
- * expandNamesVars -
- * Workhorse for "*" expansion: produce a list of targetentries
- * given lists of column names (as String nodes) and var references.
+ * expandRelAttrs -
+ * Workhorse for "*" expansion: produce a list of targetentries
+ * for the attributes of the rte
*/
-static List *
-expandNamesVars(ParseState *pstate, List *names, List *vars)
+List *
+expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
{
+ List *names,
+ *vars;
List *te_list = NIL;
+ expandRTE(pstate, rte, &names, &vars);
+
while (names)
{
char *label = strVal(lfirst(names));
@@ -875,22 +913,16 @@ expandNamesVars(ParseState *pstate, List *names, List *vars)
return te_list;
}
-/* ----------
+/*
* get_rte_attribute_name
* Get an attribute name from a RangeTblEntry
*
* 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.
*
* "*" is returned if the given attnum is InvalidAttrNumber --- this case
* 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 *
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
@@ -901,7 +933,8 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
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))
return strVal(nth(attnum - 1, rte->eref->attrs));
@@ -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
* 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",
attnum, rte->eref->relname);
@@ -926,6 +959,64 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
}
/*
+ * 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
*
* This should only be used if the relation is already
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index bb398a7068f..f5791298f31 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,12 +8,13 @@
*
*
* 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 "nodes/makefuncs.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
@@ -118,30 +119,16 @@ transformTargetList(ParseState *pstate, List *targetlist)
* Target item is relation.*, expand that table (eg.
* SELECT emp.*, dname FROM emp, dept)
*/
- Node *rteorjoin;
+ RangeTblEntry *rte;
int sublevels_up;
- rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname,
- &sublevels_up);
-
- if (rteorjoin == NULL)
- {
- rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
- sublevels_up = 0;
- }
+ rte = refnameRangeTblEntry(pstate, att->relname,
+ &sublevels_up);
+ if (rte == NULL)
+ rte = addImplicitRTE(pstate, att->relname);
- if (IsA(rteorjoin, RangeTblEntry))
- p_target = nconc(p_target,
- expandRelAttrs(pstate,
- (RangeTblEntry *) rteorjoin));
- else if (IsA(rteorjoin, JoinExpr))
- p_target = nconc(p_target,
- expandJoinAttrs(pstate,
- (JoinExpr *) rteorjoin,
- sublevels_up));
- else
- elog(ERROR, "transformTargetList: unexpected node type %d",
- nodeTag(rteorjoin));
+ p_target = nconc(p_target,
+ expandRelAttrs(pstate, rte));
}
else
{
@@ -405,34 +392,29 @@ ExpandAllTables(ParseState *pstate)
foreach(ns, pstate->p_namespace)
{
Node *n = (Node *) lfirst(ns);
+ RangeTblEntry *rte;
if (IsA(n, RangeTblRef))
- {
- RangeTblEntry *rte;
-
rte = rt_fetch(((RangeTblRef *) n)->rtindex,
pstate->p_rtable);
-
- /*
- * Ignore added-on relations that were not listed in the FROM
- * clause.
- */
- if (!rte->inFromCl)
- continue;
-
- 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));
- }
+ 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
+ * clause.
+ */
+ if (!rte->inFromCl)
+ continue;
+
+ target = nconc(target, expandRelAttrs(pstate, rte));
}
/* Check for SELECT *; */
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index f3e2306a00b..e118654e404 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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,
*/
rte = rt_fetch(rt_index, parsetree->rtable);
+ rte->rtekind = RTE_SUBQUERY;
rte->relname = NULL;
rte->relid = InvalidOid;
rte->subquery = rule_action;
@@ -354,17 +355,17 @@ markQueryForUpdate(Query *qry, bool skipOldNew)
(rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
continue;
- if (rte->subquery)
- {
- /* FOR UPDATE of subquery is propagated to subquery's rels */
- markQueryForUpdate(rte->subquery, false);
- }
- else
+ if (rte->rtekind == RTE_RELATION)
{
if (!intMember(rti, qry->rowMarks))
qry->rowMarks = lappendi(qry->rowMarks, rti);
rte->checkForWrite = true;
}
+ else if (rte->rtekind == RTE_SUBQUERY)
+ {
+ /* FOR UPDATE of subquery is propagated to subquery's rels */
+ markQueryForUpdate(rte->subquery, false);
+ }
}
}
@@ -440,13 +441,19 @@ fireRIRrules(Query *parsetree)
* to do to this level of the query, but we must recurse into the
* subquery to expand any rule references in it.
*/
- if (rte->subquery)
+ if (rte->rtekind == RTE_SUBQUERY)
{
rte->subquery = fireRIRrules(rte->subquery);
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.
* This prevents infinite expansion loop due to new rtable entries
* inserted by expansion of a rule. A table is referenced if it is
@@ -756,6 +763,7 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
result_relation = parsetree->resultRelation;
Assert(result_relation != 0);
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
@@ -945,7 +953,7 @@ QueryRewrite(Query *parsetree)
RangeTblEntry *rte = rt_fetch(query->resultRelation,
query->rtable);
- if (rte->subquery)
+ if (rte->rtekind == RTE_SUBQUERY)
{
switch (query->commandType)
{
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 49f67c91c7b..af413cab938 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -7,7 +7,7 @@
*
*
* 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)
*
* Find all Var nodes in the given tree with varlevelsup == sublevels_up,
* and increment their varno fields (rangetable indexes) by 'offset'.
- * The varnoold fields are adjusted similarly. Also, RangeTblRef nodes
- * in join trees and setOp trees are adjusted.
+ * The varnoold fields are adjusted similarly. Also, RangeTblRef 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
* nodes in-place. The given expression tree should have been copied
@@ -140,6 +140,14 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
/* the subquery itself is visited separately */
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))
{
/* Recurse into subselects */
@@ -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
* (identified by sublevels_up and rt_index), and change their varno fields
* 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
* nodes in-place. The given expression tree should have been copied
@@ -241,6 +249,15 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
/* the subquery itself is visited separately */
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))
{
/* Recurse into subselects */
@@ -410,6 +427,15 @@ rangeTableEntry_used_walker(Node *node,
/* the subquery itself is visited separately */
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))
{
/* Recurse into subselects */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 169bcd23e29..d47bf3bb945 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* 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.
*
@@ -73,17 +73,22 @@ typedef struct
/*
* 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
- * which parts of the rangetable are accessible (and under what aliases)
- * 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
- * namespaces list.
+ * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
+ * the current context's namespaces list.
+ *
+ * The rangetable is the list of actual RTEs from the query tree.
+ *
+ * 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
{
List *rtable; /* List of RangeTblEntry nodes */
- List *namespace; /* List of joinlist items (RangeTblRef and
- * JoinExpr nodes) */
+ int outer_varno; /* varno for outer_rte */
+ RangeTblEntry *outer_rte; /* special RangeTblEntry, or NULL */
+ int inner_varno; /* varno for inner_rte */
+ RangeTblEntry *inner_rte; /* special RangeTblEntry, or NULL */
} deparse_namespace;
@@ -122,12 +127,6 @@ static void get_rule_sortgroupclause(SortClause *srt, List *tlist,
deparse_context *context);
static void get_names_for_var(Var *var, deparse_context *context,
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_func_expr(Expr *expr, deparse_context *context);
static Node *strip_type_coercion(Node *expr, Oid resultType);
@@ -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
* 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 *
@@ -652,30 +651,119 @@ deparse_context_for(char *relname, Oid relid)
{
deparse_namespace *dpns;
RangeTblEntry *rte;
- RangeTblRef *rtr;
dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
/* Build a minimal RTE for the rel */
rte = makeNode(RangeTblEntry);
+ rte->rtekind = RTE_RELATION;
rte->relname = relname;
rte->relid = relid;
rte->eref = makeNode(Attr);
rte->eref->relname = relname;
rte->inh = false;
rte->inFromCl = true;
+
/* Build one-element rtable */
dpns->rtable = makeList1(rte);
+ dpns->outer_varno = dpns->inner_varno = 0;
+ dpns->outer_rte = dpns->inner_rte = NULL;
- /* Build a namespace list referencing this RTE only */
- rtr = makeNode(RangeTblRef);
- rtr->rtindex = 1;
- dpns->namespace = makeList1(rtr);
+ /* 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;
+
+ dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+
+ dpns->rtable = NIL;
+ 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 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
* for a given pg_rewrite tuple
@@ -789,7 +877,8 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
context.namespaces = makeList1(&dpns);
context.varprefix = (length(query->rtable) != 1);
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);
}
@@ -912,7 +1001,8 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace)
context.varprefix = (parentnamespace != NIL ||
length(query->rtable) != 1);
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)
{
@@ -1382,12 +1472,11 @@ get_utility_query_def(Query *query, deparse_context *context)
/*
* 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
* 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
get_names_for_var(Var *var, deparse_context *context,
@@ -1406,262 +1495,31 @@ get_names_for_var(Var *var, deparse_context *context,
var->varlevelsup);
dpns = (deparse_namespace *) lfirst(nslist);
- /* Scan namespace to see if we can find an alias for the var */
- if (find_alias_in_namespace((Node *) dpns->namespace, (Node *) var,
- dpns->rtable, var->varlevelsup,
- refname, attname))
- return;
+ /* Find the relevant RTE */
+ if (var->varno >= 1 && var->varno <= length(dpns->rtable))
+ rte = rt_fetch(var->varno, dpns->rtable);
+ else if (var->varno == dpns->outer_varno)
+ rte = dpns->outer_rte;
+ 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
+ *refname = rte->eref->relname;
- /*
- * 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);
- *refname = rte->eref->relname;
if (var->varattno == InvalidAttrNumber)
*attname = NULL;
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;
- if (var->varattno == InvalidAttrNumber)
- *attname = NULL;
- else
- *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;
-}
-
/* ----------
* get_rule_expr - Parse back an expression
* ----------
@@ -1695,7 +1553,7 @@ get_rule_expr(Node *node, deparse_context *context)
char *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)
appendStringInfo(buf, "new");
@@ -1767,6 +1625,10 @@ get_rule_expr(Node *node, deparse_context *context)
appendStringInfoChar(buf, ')');
break;
+ case FUNC_EXPR:
+ get_func_expr((Expr *) node, context);
+ break;
+
case OR_EXPR:
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) lfirst(args), context);
@@ -1795,8 +1657,13 @@ get_rule_expr(Node *node, deparse_context *context)
appendStringInfoChar(buf, ')');
break;
- case FUNC_EXPR:
- get_func_expr((Expr *) node, context);
+ case SUBPLAN_EXPR:
+ /*
+ * We cannot see an already-planned subplan in rule
+ * deparsing, only while EXPLAINing a query plan.
+ * For now, just punt.
+ */
+ appendStringInfo(buf, "(subplan)");
break;
default:
@@ -1927,19 +1794,6 @@ get_rule_expr(Node *node, deparse_context *context)
{
CaseExpr *caseexpr = (CaseExpr *) node;
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");
foreach(temp, caseexpr->args)
@@ -2015,6 +1869,28 @@ get_rule_expr(Node *node, deparse_context *context)
get_sublink_expr(node, context);
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:
printf("\n%s\n", nodeToString(node));
elog(ERROR, "get_ruledef of %s: unknown node type %d in get_rule_expr()",
@@ -2442,16 +2318,6 @@ static void
get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{
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))
{
@@ -2544,7 +2410,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
}
else if (j->quals)
{
- dpns->namespace = makeList2(j->larg, j->rarg);
appendStringInfo(buf, " ON (");
get_rule_expr(j->quals, context);
appendStringInfoChar(buf, ')');
@@ -2575,8 +2440,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
else
elog(ERROR, "get_from_clause_item: unexpected node type %d",
nodeTag(jtnode));
-
- dpns->namespace = sv_namespace;
}
/* ----------
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index f579d0267c3..882a29246fe 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: catversion.h,v 1.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 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200203011
+#define CATALOG_VERSION_NO 200203111
#endif
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 996c2b9c738..9ae9feb739d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: 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
* RangeTblEntry -
* A range table is a List of RangeTblEntry nodes.
*
- * Currently we use the same node type for both plain relation references
- * and sub-selects in the FROM clause. It might be cleaner to abstract
- * the common fields into a "superclass" nodetype.
+ * A range table entry may represent a plain relation, a sub-select in
+ * FROM, or the result of a JOIN clause. (Only explicit JOIN syntax
+ * 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
* FROM expression, or NULL if no clause.
@@ -445,7 +448,7 @@ typedef struct TargetEntry
*
* inh is TRUE for relation references that should be expanded to include
* 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.
* In SQL, the query can only refer to range variables listed in the
@@ -465,12 +468,28 @@ typedef struct TargetEntry
* (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
{
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 */
Oid relid; /* OID of the relation */
@@ -481,6 +500,21 @@ typedef struct RangeTblEntry
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:
*/
Attr *alias; /* user-written alias clause, if any */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 111f87c24d3..66db9cf3640 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: 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
* jointype: rule for joining tuples from left and right subtrees
* joinqual: qual conditions that came from JOIN/ON or JOIN/USING
* (plan.qual contains conditions that came from WHERE)
+ * joinrti: rtable index of corresponding JOIN RTE, if any (0 if none)
*
* When jointype is INNER, joinqual and plan.qual are semantically
* interchangeable. For OUTER jointypes, the two are *not* interchangeable;
@@ -262,6 +263,8 @@ typedef struct SubqueryScan
* (But plan.qual is still applied before actually returning a tuple.)
* For an outer join, only joinquals are allowed to be used as the merge
* or hash condition of a merge or hash join.
+ *
+ * joinrti is for the convenience of setrefs.c; it's not used in execution.
* ----------------
*/
typedef struct Join
@@ -269,6 +272,7 @@ typedef struct Join
Plan plan;
JoinType jointype;
List *joinqual; /* JOIN quals (in addition to plan.qual) */
+ Index joinrti; /* JOIN RTE, if any */
} Join;
/* ----------------
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index c2aabaffe6a..43e4d5a41e2 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* 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
* alias has a critical impact on semantics, because a join with an alias
* restricts visibility of the tables/columns inside it.
*
- * During parse analysis, colnames is filled with a list of String nodes
- * giving the column names (real or alias) of the output of the join,
- * and colvars is filled with a list of expressions that can be copied to
- * reference the output columns.
+ * During parse analysis, an RTE is created for the Join, and its index
+ * is filled into rtindex. This RTE is present mainly so that Vars can
+ * be created that refer to the outputs of the join.
*----------
*/
typedef struct JoinExpr
@@ -529,9 +528,7 @@ typedef struct JoinExpr
List *using; /* USING clause, if any (list of String) */
Node *quals; /* qualifiers on join, if any */
struct Attr *alias; /* user-written alias clause, if any */
- List *colnames; /* output column names (list of String) */
- List *colvars; /* output column nodes (list of
- * expressions) */
+ int rtindex; /* RT index assigned for join */
} JoinExpr;
/*----------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index e14f1ea4e83..d26d60c71be 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: 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,18 +39,39 @@ typedef enum CostSelector
* RelOptInfo
* Per-relation information for planning/optimization
*
- * For planning purposes, a "base rel" is either a plain relation (a
- * 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"
- * is the joining of two or more base rels. A joinrel is identified by
- * the set of RT indexes for its component baserels.
- *
- * 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
- * set is the right datatype to identify it with.
- *
- * Parts of this data structure are specific to various scan and join
- * mechanisms. It didn't seem worth creating new node types for them.
+ * For planning purposes, a "base rel" is either a plain relation (a 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"
+ * is the joining of two or more base rels. A joinrel is identified by
+ * 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
+ * baserels, no matter what order we assemble them in; so an unordered
+ * 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
+ * mechanisms. It didn't seem worth creating new node types for them.
*
* relids - List of base-relation identifiers; it is a base relation
* if there is just one, a join relation if more than one
@@ -69,7 +90,7 @@ typedef enum CostSelector
* pruneable - flag to let the planner know whether it can prune the
* 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
* indexlist - list of IndexOptInfo nodes for relation's indexes
@@ -82,25 +103,30 @@ typedef enum CostSelector
* upon creation of the RelOptInfo object; they are filled in when
* set_base_rel_pathlist processes the object.
*
- * Note: if a base relation is the root of an inheritance tree
- * (SELECT FROM foo*) it is still considered a base rel. We will
- * generate a list of candidate Paths for accessing that table itself,
- * and also generate baserel RelOptInfo nodes for each child table,
- * 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
- * only available Path for the inheritance baserel.
+ * For otherrels that are inheritance children, these fields are filled
+ * in just as for a baserel. In otherrels for join RTEs, these fields
+ * are empty --- the only useful field of a join otherrel is its
+ * outerjoinset.
+ *
+ * If the relation is a join relation it will have these fields set:
*
- * * The presence of the remaining fields depends on the restrictions
- * and joins that the relation participates in:
+ * 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
+ * and joins that the relation participates in:
*
* baserestrictinfo - List of RestrictInfo nodes, containing info about
* each qualification clause in which this relation
* participates (only used for base rels)
* baserestrictcost - Estimated cost of evaluating the baserestrictinfo
* clauses at a single tuple (only used for base rels)
- * outerjoinset - If the rel appears within the nullable side of an outer
- * join, the list of all relids participating in the highest
- * such outer join; else NIL (only used for base rels)
+ * outerjoinset - For a base rel: if the rel appears within the nullable
+ * side of an outer join, the list of all relids
+ * 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
* clause in which this relation participates
* innerjoin - List of Path nodes that represent indices that may be used
@@ -128,11 +154,20 @@ typedef enum CostSelector
* 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
{
NodeTag type;
+ RelOptKind reloptkind;
+
/* all relations included in this RelOptInfo */
Relids relids; /* integer list of base relids (RT
* indexes) */
@@ -155,6 +190,10 @@ typedef struct RelOptInfo
double tuples;
struct Plan *subplan;
+ /* information about a join rel (not set for base rels!) */
+ Index joinrti;
+ List *joinrteids;
+
/* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if
* base rel) */
@@ -228,6 +267,16 @@ typedef struct IndexOptInfo
bool unique; /* if a unique index */
} 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
*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 9be3cfde010..d9419df47d3 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: 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,
/*
* 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 *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,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 37209dfd5c3..919f3d23de4 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: 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);
/*
* 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 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,
Oid sortop1, Oid sortop2);
/*
* prototypes for plan/setrefs.c
*/
-extern void set_plan_references(Plan *plan);
-extern List *join_references(List *clauses, List *outer_tlist,
- List *inner_tlist, Index acceptable_rel);
+extern void set_plan_references(Query *root, Plan *plan);
+extern List *join_references(List *clauses, Query *root,
+ List *outer_tlist, List *inner_tlist,
+ Index acceptable_rel, Index join_rti);
extern void fix_opids(Node *node);
/*
diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h
index c88da074a58..1153604e48a 100644
--- a/src/include/optimizer/var.h
+++ b/src/include/optimizer/var.h
@@ -7,14 +7,15 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* 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
#define VAR_H
-#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+
extern List *pull_varnos(Node *node);
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_var_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars);
+extern Node *flatten_join_alias_vars(Node *node, Query *root, int expandRTI);
+extern void build_join_alias_subvars(Query *root, Var *aliasvar,
+ Var **leftsubvar, Var **rightsubvar);
#endif /* VAR_H */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 3bc575f6ace..1b579850f4e 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_relation.h,v 1.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 @@
#include "parser/parse_node.h"
-extern Node *refnameRangeOrJoinEntry(ParseState *pstate,
+extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
char *refname,
int *sublevels_up);
extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
@@ -36,14 +36,21 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
Query *subquery,
Attr *alias,
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,
bool addToJoinList, bool addToNameSpace);
extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
List **colnames, List **colvars);
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 Name attnumAttName(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid);
diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h
index 2c19c9cc45b..f0dc6627f43 100644
--- a/src/include/parser/parsetree.h
+++ b/src/include/parser/parsetree.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* 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 @@
*
* Given the range index of a relation, return the corresponding
* 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) \
(rt_fetch(rangeindex, rangetable)->relid)
@@ -52,4 +52,11 @@
*/
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 */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index e3bc6217841..4c2dec5d106 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: 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 @@
#define BUILTINS_H
#include "fmgr.h"
-#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
#include "storage/itemptr.h" /* for setLastTid() */
/*
@@ -343,6 +343,10 @@ extern Datum pg_get_expr(PG_FUNCTION_ARGS);
extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix);
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 */
extern void setLastTid(const ItemPointer tid);
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 6774390046e..cfe7ded08d9 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -1847,7 +1847,32 @@ SELECT '' AS "xxx", *
FROM J1_TBL UNION JOIN J2_TBL;
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
--
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
DROP TABLE J1_TBL;
DROP TABLE J2_TBL;
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index ae63a61c01f..91e64adfc94 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -199,9 +199,30 @@ SELECT '' AS "xxx", *
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
--
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+
DROP TABLE J1_TBL;
DROP TABLE J2_TBL;
-