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

Restructure representation of join alias variables. An explicit JOIN

now has an RTE of its own, and references to its outputs now are Vars
referencing the JOIN RTE, rather than CASE-expressions.  This allows
reverse-listing in ruleutils.c to use the correct alias easily, rather
than painfully reverse-engineering the alias namespace as it used to do.
Also, nested FULL JOINs work correctly, because the result of the inner
joins are simple Vars that the planner can cope with.  This fixes a bug
reported a couple times now, notably by Tatsuo on 18-Nov-01.  The alias
Vars are expanded into COALESCE expressions where needed at the very end
of planning, rather than during parsing.
Also, beginnings of support for showing plan qualifier expressions in
EXPLAIN.  There are probably still cases that need work.
initdb forced due to change of stored-rule representation.
parent 66b6bf67
......@@ -5,7 +5,7 @@
* 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);
}
}
......@@ -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;
......
......@@ -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))
......
......@@ -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 ");
......
......@@ -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 */
......
......@@ -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)
......
......@@ -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);
}
......
......@@ -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))
{
......
......@@ -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;
......
This diff is collapsed.
This diff is collapsed.
......@@ -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
......
......@@ -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;
}
......
......@@ -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,
......
......@@ -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
......
......@@ -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;
......
......@@ -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)
/* Check type of rtable entry */
switch (rte->rtekind)
{
/* Plain relation --- retrieve statistics from the system catalogs */
case RTE_RELATION:
{
/* Table --- retrieve statistics from the system catalogs */
bool indexed;
get_relation_info(relationObjectId,
get_relation_info(rte->relid,
&indexed, &rel->pages, &rel->tuples);
if (indexed)
rel->indexlist = find_secondary_indexes(relationObjectId);
rel->indexlist = find_secondary_indexes(rte->relid);
break;
}
else
{
/* subquery --- mark it as such for later processing */
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;
......@@ -203,6 +217,47 @@ find_base_rel(Query *root, int relid)
return NULL; /* keep compiler quiet */
}
/*
* find_other_rel
* Find an otherrel entry, if one exists for the given relid.
* Return NULL if no entry.
*/
RelOptInfo *
find_other_rel(Query *root, int relid)
{
List *rels;
foreach(rels, root->other_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
if (lfirsti(rel->relids) == relid)
return rel;
}
return NULL;
}
/*
* find_other_rel_for_join
* Look for an otherrel for a join RTE matching the given baserel set.
* Return NULL if no entry.
*/
RelOptInfo *
find_other_rel_for_join(Query *root, List *relids)
{
List *rels;
foreach(rels, root->other_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
if (rel->reloptkind == RELOPT_OTHER_JOIN_REL
&& sameseti(relids, rel->outerjoinset))
return rel;
}
return NULL;
}
/*
* find_join_rel
* Returns relation entry corresponding to 'relids' (a list of RT indexes),
......@@ -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,30 +356,61 @@ 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,
length(new_outer_tlist) + 1);
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
......
This diff is collapsed.
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.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");
......
......@@ -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)
{
case JOIN_INNER:
case JOIN_LEFT:
colvar = l_colvar;
break;
case JOIN_RIGHT:
colvar = r_colvar;
break;
default:
/*
* Choose output type if input types are dissimilar.
*/
outcoltype = l_colvar->vartype;
outcoltypmod = l_colvar->vartypmod;
if (outcoltype != r_colvar->vartype)
{
/* 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 */
}
else if (outcoltypmod != r_colvar->vartypmod)
{
/* same type, but not same typmod */
outcoltypmod = -1; /* ie, unknown */
}
res_colvars = lappend(res_colvars, colvar);
coltypes = lappendi(coltypes, outcoltype);
coltypmods = lappendi(coltypmods, outcoltypmod);
leftcolnos = lappendi(leftcolnos, l_index+1);
rightcolnos = lappendi(rightcolnos, r_index+1);
}
j->quals = transformJoinUsingClause(pstate,
......@@ -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;
}
......
......@@ -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;
......
......@@ -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,
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);
}
}
......
......@@ -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);
}
......
This diff is collapsed.
......@@ -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,
rte = refnameRangeTblEntry(pstate, att->relname,
&sublevels_up);
if (rte == NULL)
rte = addImplicitRTE(pstate, att->relname);
if (rteorjoin == NULL)
{
rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
sublevels_up = 0;
}
if (IsA(rteorjoin, RangeTblEntry))
p_target = nconc(p_target,
expandRelAttrs(pstate,
(RangeTblEntry *) rteorjoin));
else if (IsA(rteorjoin, JoinExpr))
p_target = nconc(p_target,
expandJoinAttrs(pstate,
(JoinExpr *) rteorjoin,
sublevels_up));
else
elog(ERROR, "transformTargetList: unexpected node type %d",
nodeTag(rteorjoin));
expandRelAttrs(pstate, rte));
}
else
{
......@@ -405,13 +392,20 @@ ExpandAllTables(ParseState *pstate)
foreach(ns, pstate->p_namespace)
{
Node *n = (Node *) lfirst(ns);
if (IsA(n, RangeTblRef))
{
RangeTblEntry *rte;
if (IsA(n, RangeTblRef))
rte = rt_fetch(((RangeTblRef *) n)->rtindex,
pstate->p_rtable);
else if (IsA(n, JoinExpr))
rte = rt_fetch(((JoinExpr *) n)->rtindex,
pstate->p_rtable);
else
{
elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
"\n\t%s", nodeToString(n));
rte = NULL; /* keep compiler quiet */
}
/*
* Ignore added-on relations that were not listed in the FROM
......@@ -422,18 +416,6 @@ ExpandAllTables(ParseState *pstate)
target = nconc(target, expandRelAttrs(pstate, rte));
}
else if (IsA(n, JoinExpr))
{
/* A newfangled join expression */
JoinExpr *j = (JoinExpr *) n;
/* Currently, a join expr could only have come from FROM. */
target = nconc(target, expandJoinAttrs(pstate, j, 0));
}
else
elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
"\n\t%s", nodeToString(n));
}
/* Check for SELECT *; */
if (target == NIL)
......
......@@ -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,12 +441,18 @@ 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
......@@ -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)
{
......
......@@ -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 */
......
This diff is collapsed.
......@@ -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
......@@ -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 */
......@@ -480,6 +499,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:
*/
......
......@@ -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;
/* ----------------
......
......@@ -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;
/*----------
......
......@@ -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,16 +39,37 @@ 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.
* 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.
* 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.
*
......@@ -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,15 +103,18 @@ 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:
*
* joinrti - RT index of corresponding JOIN RTE, if any; 0 if none
* joinrteids - List of RT indexes of JOIN RTEs included in this join
* (including joinrti)
*
* * The presence of the remaining fields depends on the restrictions
* The presence of the remaining fields depends on the restrictions
* and joins that the relation participates in:
*
* baserestrictinfo - List of RestrictInfo nodes, containing info about
......@@ -98,9 +122,11 @@ typedef enum CostSelector
* 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
*
......
......@@ -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,
......
......@@ -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);
/*
......
......@@ -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 */
......@@ -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);
......
......@@ -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 */
......@@ -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);
......
......@@ -1846,8 +1846,33 @@ SELECT '' AS "xxx", *
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;
......@@ -198,10 +198,31 @@ SELECT '' AS "xxx", *
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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment