From 6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 12 Mar 2002 00:52:10 +0000 Subject: [PATCH] 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. --- src/backend/commands/explain.c | 220 ++++++++++- src/backend/nodes/copyfuncs.c | 22 +- src/backend/nodes/equalfuncs.c | 18 +- src/backend/nodes/outfuncs.c | 30 +- src/backend/nodes/readfuncs.c | 36 +- src/backend/optimizer/path/costsize.c | 8 +- src/backend/optimizer/path/joinpath.c | 23 +- src/backend/optimizer/path/joinrels.c | 4 +- src/backend/optimizer/path/pathkeys.c | 8 +- src/backend/optimizer/plan/createplan.c | 98 +++-- src/backend/optimizer/plan/initsplan.c | 214 +++++++---- src/backend/optimizer/plan/planmain.c | 38 +- src/backend/optimizer/plan/planner.c | 30 +- src/backend/optimizer/plan/setrefs.c | 74 ++-- src/backend/optimizer/prep/prepunion.c | 20 +- src/backend/optimizer/util/clauses.c | 11 +- src/backend/optimizer/util/relnode.c | 150 ++++++-- src/backend/optimizer/util/var.c | 375 ++++++++++++++++++- src/backend/parser/analyze.c | 44 ++- src/backend/parser/parse_clause.c | 162 ++++---- src/backend/parser/parse_expr.c | 4 +- src/backend/parser/parse_func.c | 55 +-- src/backend/parser/parse_node.c | 53 +-- src/backend/parser/parse_relation.c | 357 +++++++++++------- src/backend/parser/parse_target.c | 66 ++-- src/backend/rewrite/rewriteHandler.c | 26 +- src/backend/rewrite/rewriteManip.c | 34 +- src/backend/utils/adt/ruleutils.c | 471 +++++++++--------------- src/include/catalog/catversion.h | 4 +- src/include/nodes/parsenodes.h | 46 ++- src/include/nodes/plannodes.h | 6 +- src/include/nodes/primnodes.h | 13 +- src/include/nodes/relation.h | 101 +++-- src/include/optimizer/pathnode.h | 6 +- src/include/optimizer/planmain.h | 11 +- src/include/optimizer/var.h | 8 +- src/include/parser/parse_relation.h | 15 +- src/include/parser/parsetree.h | 11 +- src/include/utils/builtins.h | 8 +- src/test/regress/expected/join.out | 25 ++ src/test/regress/sql/join.sql | 23 +- 41 files changed, 1941 insertions(+), 987 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 319a9c83d8..e230ba598a 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 ae9ac43045..97eeb35ea3 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 b9269c5a83..760554dbb6 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 d3b737ed21..3699fc38ff 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 7cdd0c73c3..c0794123b3 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 e215a6cd36..2bc0bb8a13 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 4d60569e7e..11bc8a9f7d 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 745a1eb0b5..d2d511e75e 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 f91e25cdb4..60e05ca340 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 88c5499b30..79d90bf5a3 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 76677b4059..2c9acc73b7 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,24 +96,98 @@ 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; } +/***************************************************************************** + * + * 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 57c9b963c7..ee1d189071 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 eb92c3d3af..1df2cd2940 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 167901acdf..2f48821ece 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 - #include "postgres.h" +#include + #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 64d8b78f06..b2f18780fc 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 8a4bcf4d9c..6585fb1905 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 457826f5d3..0fad16fbdd 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; @@ -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 diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 2521ffb2b6..568e024d20 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 5fb298e1c2..98f5030f78 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 61904439e7..2f1eda4a9b 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 f740d632cc..9c32fac231 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 766a5daad5..ed39d6c103 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 a43dcb13af..be825c26f9 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 #include #include -#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 9440914a77..1609c89ce0 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); @@ -303,44 +311,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) return result; } -/* - * scanJoinForColumn - * Search the column names of a single join table for the given name. - * If found, return an appropriate Var node or expression, else return NULL. - * If the name proves ambiguous within this jointable, raise error. - * - * NOTE: unlike scanRTEForColumn, there's no need to worry about forcing - * checkForRead true for the referenced tables. This is so because a join - * expression can only appear in a FROM clause, and any table named in - * FROM will be marked checkForRead from the beginning. - */ -static Node * -scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up) -{ - Node *result = NULL; - int attnum = 0; - List *c; - - foreach(c, join->colnames) - { - attnum++; - if (strcmp(strVal(lfirst(c)), colname) == 0) - { - if (result) - elog(ERROR, "Column reference \"%s\" is ambiguous", colname); - result = copyObject(nth(attnum - 1, join->colvars)); - - /* - * If referencing an uplevel join item, we must adjust - * sublevels settings in the copied expression. - */ - if (sublevels_up > 0) - IncrementVarSublevelsUp(result, sublevels_up, 0); - } - } - return result; -} - /* * colnameToVar * Search for an unqualified column name. @@ -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; @@ -621,6 +581,76 @@ addRangeTableEntryForSubquery(ParseState *pstate, return rte; } +/* + * Add an entry for a join to the pstate's range table (p_rtable). + * + * This is much like addRangeTableEntry() except that it makes a join RTE. + */ +RangeTblEntry * +addRangeTableEntryForJoin(ParseState *pstate, + List *colnames, + JoinType jointype, + List *coltypes, + List *coltypmods, + List *leftcols, + List *rightcols, + Attr *alias, + bool inFromCl) +{ + RangeTblEntry *rte = makeNode(RangeTblEntry); + Attr *eref; + int numaliases; + + rte->rtekind = RTE_JOIN; + rte->relname = NULL; + rte->relid = InvalidOid; + rte->subquery = NULL; + rte->jointype = jointype; + rte->joincoltypes = coltypes; + rte->joincoltypmods = coltypmods; + rte->joinleftcols = leftcols; + rte->joinrightcols = rightcols; + rte->alias = alias; + + eref = alias ? (Attr *) copyObject(alias) : makeAttr("unnamed_join", NULL); + numaliases = length(eref->attrs); + + /* fill in any unspecified alias columns */ + if (numaliases < length(colnames)) + { + while (numaliases-- > 0) + colnames = lnext(colnames); + eref->attrs = nconc(eref->attrs, colnames); + } + + rte->eref = eref; + + /*---------- + * Flags: + * - this RTE should be expanded to include descendant tables, + * - this RTE is in the FROM clause, + * - this RTE should be checked for read/write access rights. + * + * Joins are never checked for access rights. + *---------- + */ + rte->inh = false; /* never true for joins */ + rte->inFromCl = inFromCl; + rte->checkForRead = false; + rte->checkForWrite = false; + + rte->checkAsUser = InvalidOid; + + /* + * Add completed RTE to pstate's range table list, but not to join + * list nor namespace --- caller must do that if appropriate. + */ + if (pstate != NULL) + pstate->p_rtable = lappend(pstate->p_rtable, rte); + + return rte; +} + /* * Has the specified relname been selected FOR UPDATE? */ @@ -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); @@ -925,6 +958,64 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) return attname; } +/* + * get_rte_attribute_type + * Get attribute type information from a RangeTblEntry + */ +void +get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, + Oid *vartype, int32 *vartypmod) +{ + if (rte->rtekind == RTE_RELATION) + { + /* Plain relation RTE --- get the attribute's type info */ + HeapTuple tp; + Form_pg_attribute att_tup; + + tp = SearchSysCache(ATTNUM, + ObjectIdGetDatum(rte->relid), + Int16GetDatum(attnum), + 0, 0); + /* this shouldn't happen... */ + if (!HeapTupleIsValid(tp)) + elog(ERROR, "Relation %s does not have attribute %d", + rte->relname, attnum); + att_tup = (Form_pg_attribute) GETSTRUCT(tp); + *vartype = att_tup->atttypid; + *vartypmod = att_tup->atttypmod; + ReleaseSysCache(tp); + } + else if (rte->rtekind == RTE_SUBQUERY) + { + /* Subselect RTE --- get type info from subselect's tlist */ + List *tlistitem; + + foreach(tlistitem, rte->subquery->targetList) + { + TargetEntry *te = (TargetEntry *) lfirst(tlistitem); + + if (te->resdom->resjunk || te->resdom->resno != attnum) + continue; + *vartype = te->resdom->restype; + *vartypmod = te->resdom->restypmod; + return; + } + /* falling off end of list shouldn't happen... */ + elog(ERROR, "Subquery %s does not have attribute %d", + rte->eref->relname, attnum); + } + else if (rte->rtekind == RTE_JOIN) + { + /* Join RTE --- get type info directly from join RTE */ + Assert(attnum > 0 && attnum <= length(rte->joincoltypes)); + *vartype = (Oid) nthi(attnum-1, rte->joincoltypes); + *vartypmod = nthi(attnum-1, rte->joincoltypmods); + } + else + elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d", + (int) rte->rtekind); +} + /* * given relation and att name, return id of variable * diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bb398a7068..f5791298f3 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 f3e2306a00..e118654e40 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,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) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 49f67c91c7..af413cab93 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 169bcd23e2..d47bf3bb94 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 f579d0267c..882a29246f 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 996c2b9c73..9ae9feb739 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 */ @@ -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: */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 111f87c24d..66db9cf364 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 c2aabaffe6..43e4d5a41e 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 e14f1ea4e8..d26d60c71b 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 9be3cfde01..d9419df47d 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 37209dfd5c..919f3d23de 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 c88da074a5..1153604e48 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 3bc575f6ac..1b579850f4 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 2c19c9cc45..f0dc6627f4 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 e3bc621784..4c2dec5d10 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 6774390046..cfe7ded08d 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -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; diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index ae63a61c01..91e64adfc9 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -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; - -- 2.40.0