diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 319a9c83d82ee5ad7de8ec4f075179f4d5a41f5c..e230ba598a00fae5a6a56d5269f2a33e9a6361c1 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 ae9ac430456dc1c4ee3af3ac55d47ebb4411eb9c..97eeb35ea38303ff68ed809bde2b9399e6fd3605 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 b9269c5a834a92949898d63c079350537b1b3f28..760554dbb63f40d119a2ee1dd96694bc44e8b78b 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 d3b737ed21306a2ee3e57503582c6901988b9a87..3699fc38ffb6e9e0b57d0768c5d2db88eca5f33a 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 7cdd0c73c39aa751aa0bfd670bac3b81d9203c73..c0794123b3e54552172481f644437390424df788 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 e215a6cd3669e6e400aa23eef3049ab170931562..2bc0bb8a137b5daab11e50b048ca2fe8842fbac4 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 4d60569e7ef6fc01ab9e5fb283c56dc5df7c35a8..11bc8a9f7d31bebfa56cb2dced84ccaea88c9799 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 745a1eb0b5b5e50dda2378932db76fb47c0107d9..d2d511e75e7ca25bdf554043715b354bf4f06e88 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 f91e25cdb4fc986056efa91f0e86c314d90e8f05..60e05ca340f3e79adcaa6970484e7f3bcf307be6 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 88c5499b3024d0b274c4c61fd301e645dcd5c394..79d90bf5a3ccaa6d0bd0e21240774fc34345638c 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 76677b4059caf5b1cb13723c7e5b21e645ec52b4..2c9acc73b7ff4a30d5a3c3fb1d1baba6dab94d7c 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 57c9b963c734f57c39762024a4ca4912ec2f6543..ee1d18907121634221d977516191fb752095c5ef 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 eb92c3d3af95771bc110f9e6956ae7610352d642..1df2cd2940226741aab5967a758855b4e53dbe6a 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 167901acdf0ad3379a18c910d9797ccb12468314..2f48821ece6867d7275c2497d8647776c84b3431 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,25 +9,29 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.73 2001/11/05 17:46:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.74 2002/03/12 00:51:48 tgl Exp $ * *------------------------------------------------------------------------- */ -#include <sys/types.h> - #include "postgres.h" +#include <sys/types.h> + #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" #include "optimizer/tlist.h" +#include "optimizer/var.h" + typedef struct { + Query *root; List *outer_tlist; List *inner_tlist; Index acceptable_rel; + Index join_rti; } join_references_context; typedef struct @@ -38,7 +42,7 @@ typedef struct } replace_vars_with_subplan_refs_context; static void fix_expr_references(Plan *plan, Node *node); -static void set_join_references(Join *join); +static void set_join_references(Query *root, Join *join); static void set_uppernode_references(Plan *plan, Index subvarno); static Node *join_references_mutator(Node *node, join_references_context *context); @@ -71,7 +75,7 @@ static bool fix_opids_walker(Node *node, void *context); * Returns nothing of interest, but modifies internal fields of nodes. */ void -set_plan_references(Plan *plan) +set_plan_references(Query *root, Plan *plan) { List *pl; @@ -115,16 +119,16 @@ set_plan_references(Plan *plan) fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); /* Recurse into subplan too */ - set_plan_references(((SubqueryScan *) plan)->subplan); + set_plan_references(root, ((SubqueryScan *) plan)->subplan); break; case T_NestLoop: - set_join_references((Join *) plan); + set_join_references(root, (Join *) plan); fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); break; case T_MergeJoin: - set_join_references((Join *) plan); + set_join_references(root, (Join *) plan); fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); @@ -132,7 +136,7 @@ set_plan_references(Plan *plan) (Node *) ((MergeJoin *) plan)->mergeclauses); break; case T_HashJoin: - set_join_references((Join *) plan); + set_join_references(root, (Join *) plan); fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); @@ -186,7 +190,7 @@ set_plan_references(Plan *plan) * recurse into subplans. */ foreach(pl, ((Append *) plan)->appendplans) - set_plan_references((Plan *) lfirst(pl)); + set_plan_references(root, (Plan *) lfirst(pl)); break; default: elog(ERROR, "set_plan_references: unknown plan type %d", @@ -203,21 +207,21 @@ set_plan_references(Plan *plan) * plan's var nodes against the already-modified nodes of the * subplans. */ - set_plan_references(plan->lefttree); - set_plan_references(plan->righttree); + set_plan_references(root, plan->lefttree); + set_plan_references(root, plan->righttree); foreach(pl, plan->initPlan) { SubPlan *sp = (SubPlan *) lfirst(pl); Assert(IsA(sp, SubPlan)); - set_plan_references(sp->plan); + set_plan_references(root, sp->plan); } foreach(pl, plan->subPlan) { SubPlan *sp = (SubPlan *) lfirst(pl); Assert(IsA(sp, SubPlan)); - set_plan_references(sp->plan); + set_plan_references(root, sp->plan); } } @@ -256,7 +260,7 @@ fix_expr_references(Plan *plan, Node *node) * 'join' is a join plan node */ static void -set_join_references(Join *join) +set_join_references(Query *root, Join *join) { Plan *outer = join->plan.lefttree; Plan *inner = join->plan.righttree; @@ -264,9 +268,11 @@ set_join_references(Join *join) List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist); join->plan.targetlist = join_references(join->plan.targetlist, + root, outer_tlist, inner_tlist, - (Index) 0); + (Index) 0, + join->joinrti); } /* @@ -343,7 +349,8 @@ set_uppernode_references(Plan *plan, Index subvarno) * Creates a new set of targetlist entries or join qual clauses by * changing the varno/varattno values of variables in the clauses * to reference target list values from the outer and inner join - * relation target lists. + * relation target lists. Also, any join alias variables in the + * clauses are expanded into references to their component variables. * * This is used in two different scenarios: a normal join clause, where * all the Vars in the clause *must* be replaced by OUTER or INNER references; @@ -360,21 +367,27 @@ set_uppernode_references(Plan *plan, Index subvarno) * 'inner_tlist' is the target list of the inner join relation, or NIL * 'acceptable_rel' is either zero or the rangetable index of a relation * whose Vars may appear in the clause without provoking an error. + * 'join_rti' is either zero or the join RTE index of join alias variables + * that should be expanded. * * Returns the new expression tree. The original clause structure is * not modified. */ List * join_references(List *clauses, + Query *root, List *outer_tlist, List *inner_tlist, - Index acceptable_rel) + Index acceptable_rel, + Index join_rti) { join_references_context context; + context.root = root; context.outer_tlist = outer_tlist; context.inner_tlist = inner_tlist; context.acceptable_rel = acceptable_rel; + context.join_rti = join_rti; return (List *) join_references_mutator((Node *) clauses, &context); } @@ -387,12 +400,14 @@ join_references_mutator(Node *node, if (IsA(node, Var)) { Var *var = (Var *) node; - Var *newvar = (Var *) copyObject(var); Resdom *resdom; + /* First look for the var in the input tlists */ resdom = tlist_member((Node *) var, context->outer_tlist); if (resdom) { + Var *newvar = (Var *) copyObject(var); + newvar->varno = OUTER; newvar->varattno = resdom->resno; return (Node *) newvar; @@ -400,18 +415,33 @@ join_references_mutator(Node *node, resdom = tlist_member((Node *) var, context->inner_tlist); if (resdom) { + Var *newvar = (Var *) copyObject(var); + newvar->varno = INNER; newvar->varattno = resdom->resno; return (Node *) newvar; } + /* Perhaps it's a join alias that can be resolved to input vars? */ + if (var->varno == context->join_rti) + { + Node *newnode; + + newnode = flatten_join_alias_vars((Node *) var, + context->root, + context->join_rti); + /* Must now resolve the input vars... */ + newnode = join_references_mutator(newnode, context); + return newnode; + } + /* - * Var not in either tlist --- either raise an error, or return - * the Var unmodified. + * No referent found for Var --- either raise an error, or return + * the Var unmodified if it's for acceptable_rel. */ if (var->varno != context->acceptable_rel) elog(ERROR, "join_references: variable not in subplan target lists"); - return (Node *) newvar; + return (Node *) copyObject(var); } return expression_tree_mutator(node, join_references_mutator, diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 64d8b78f066b5b9e89414bd321c933a14237eb00..b2f18780fcced782cb394930d14cfb31da1b8ab2 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 8a4bcf4d9c450bf02c3c94bcc0b15d5075cdc360..6585fb1905da64b6395815c8ef3abc1a3a12a76a 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 457826f5d316ff7484a096c3ffbf8b3d6fee03fd..0fad16fbdd082bbb4bef7a3fadf61bd27f5e8414 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 2521ffb2b6c7ed962332b9e411ead79e8d2aec17..568e024d20ba20add8bf7c44912870ee270adcda 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 5fb298e1c27a64bac5c22ba084cedb1c70db6f92..98f5030f78b756706dfd3a66d1afb21b33573fc6 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 61904439e7d5c3273f17798e1727aea73956f367..2f1eda4a9bf1502bdaf1b331f5fd754295792209 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 f740d632cc1e3ed5b2dd839aad9204e02a69be16..9c32fac23147507055320efb015e0be91acf2315 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 766a5daad55b8fcaa5780896c20324535ec984f9..ed39d6c1036c8e02034e81311994ba690779fd8d 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 a43dcb13af223a3afae05f91b5f607e2f6ad72b5..be825c26f9ec6667c708bbdea5e9ac7aee9ad5e9 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -8,21 +8,22 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.58 2002/03/06 06:09:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.59 2002/03/12 00:51:55 tgl Exp $ * *------------------------------------------------------------------------- */ +#include "postgres.h" + #include <ctype.h> #include <errno.h> #include <float.h> -#include "postgres.h" - #include "access/heapam.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "fmgr.h" #include "nodes/makefuncs.h" +#include "parser/parsetree.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_node.h" @@ -165,51 +166,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno) { int vnum, sublevels_up; - Oid vartypeid = 0; - int32 type_mod = 0; + Oid vartypeid; + int32 type_mod; vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); - - if (rte->relid != InvalidOid) - { - /* Plain relation RTE --- get the attribute's type info */ - HeapTuple tp; - Form_pg_attribute att_tup; - - tp = SearchSysCache(ATTNUM, - ObjectIdGetDatum(rte->relid), - Int16GetDatum(attrno), - 0, 0); - /* this shouldn't happen... */ - if (!HeapTupleIsValid(tp)) - elog(ERROR, "Relation %s does not have attribute %d", - rte->relname, attrno); - att_tup = (Form_pg_attribute) GETSTRUCT(tp); - vartypeid = att_tup->atttypid; - type_mod = att_tup->atttypmod; - ReleaseSysCache(tp); - } - else - { - /* Subselect RTE --- get type info from subselect's tlist */ - List *tlistitem; - - foreach(tlistitem, rte->subquery->targetList) - { - TargetEntry *te = (TargetEntry *) lfirst(tlistitem); - - if (te->resdom->resjunk || te->resdom->resno != attrno) - continue; - vartypeid = te->resdom->restype; - type_mod = te->resdom->restypmod; - break; - } - /* falling off end of list shouldn't happen... */ - if (tlistitem == NIL) - elog(ERROR, "Subquery %s does not have attribute %d", - rte->eref->relname, attrno); - } - + get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod); return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up); } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 9440914a776cd43336ff928b3e1d90d55ac5db9d..1609c89ce07b796a78b41e5d7e3bd19a4643b56b 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 bb398a7068f48fbd058d0a7bb580af0b91ac55ae..f5791298f310c6d9197fe032fa6e702b01f9816d 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 f3e2306a00bb7d96e965af29ed17a65037e08869..e118654e4040c8933df27d57ff7af1600ba189a2 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 49f67c91c7bcfea105016861217ee91fed09870d..af413cab938d701500e1b496707800738adbab81 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 169bcd23e29be6d605c8b8c635b90aa2d5c5e473..d47bf3bb945c9f658c7e4ff0022ffade1b4abd41 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 f579d0267c3bef555a69f367c60e1620cce39cb4..882a29246fe45b207a3276b0d0f72c4b0cf7a828 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 996c2b9c738942fd57cc3495fc62fe64f4747147..9ae9feb739dfbdff1b78dbf21e3cf6b95900dfa6 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 111f87c24d360ee25c6e9ec04fda9d81fa62a9cf..66db9cf36404ca4a1f4585c3d1ea3c734e6f493d 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 c2aabaffe6aa2648605c0cc685a6c46c5193712d..43e4d5a41e20673cc5ee9c4a7705ab060708abf6 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 e14f1ea4e83d672278b59b05f9634bc07e0d3f45..d26d60c71be1b20b491ba443708474e070c6d6d5 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 9be3cfde0102e8e176a398f4bdaa995b125315be..d9419df47d3dedabc4b3d456e1e0199b9b4ab599 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 37209dfd5c37a2a2857fda28d6101b2855f0be65..919f3d23de4b10c7b7101edeb6fcda52ade8881a 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 c88da074a5826a33e5bff9351caf768a3028a7df..1153604e48a1ed930b24f699058871be347f12c7 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 3bc575f6acecf73a7ac7ec6a08bb8cf1b7bdc2b7..1b579850f4eeac7ce4014c06a5fded3ddc4cc35a 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 2c19c9cc45bb0fcce628d907e8e6a20f549c9110..f0dc6627f43a0b229f6426109f085de4c545dc1b 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 e3bc6217841ee07075966bd37434f5509c025e7a..4c2dec5d106f0f24fd1cb8340402752ab2d9cdd8 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 6774390046e30b394914fdbeaad0bbd4d0ba9804..cfe7ded08d9022b893d2f69bda91fb679648bcbe 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 ae63a61c01fbb8741ea131e6bd49c18c754cdc97..91e64adfc9437d11fb1af311d29033ea22979695 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; -