mirror of
https://github.com/postgres/postgres.git
synced 2025-11-07 19:06:32 +03:00
Restructure representation of join alias variables. An explicit JOIN
now has an RTE of its own, and references to its outputs now are Vars referencing the JOIN RTE, rather than CASE-expressions. This allows reverse-listing in ruleutils.c to use the correct alias easily, rather than painfully reverse-engineering the alias namespace as it used to do. Also, nested FULL JOINs work correctly, because the result of the inner joins are simple Vars that the planner can cope with. This fixes a bug reported a couple times now, notably by Tatsuo on 18-Nov-01. The alias Vars are expanded into COALESCE expressions where needed at the very end of planning, rather than during parsing. Also, beginnings of support for showing plan qualifier expressions in EXPLAIN. There are probably still cases that need work. initdb forced due to change of stored-rule representation.
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.82 2002/03/01 20:50:20 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.83 2002/03/12 00:51:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -597,7 +597,7 @@ cost_mergejoin(Path *path, Query *root,
|
||||
|
||||
leftvar = get_leftop(firstclause->clause);
|
||||
Assert(IsA(leftvar, Var));
|
||||
if (intMember(leftvar->varno, outer_path->parent->relids))
|
||||
if (VARISRELMEMBER(leftvar->varno, outer_path->parent))
|
||||
{
|
||||
/* left side of clause is outer */
|
||||
outerscansel = firstclause->left_mergescansel;
|
||||
@@ -748,7 +748,7 @@ cost_hashjoin(Path *path, Query *root,
|
||||
* a large query, we cache the bucketsize estimate in the RestrictInfo
|
||||
* node to avoid repeated lookups of statistics.
|
||||
*/
|
||||
if (intMember(right->varno, inner_path->parent->relids))
|
||||
if (VARISRELMEMBER(right->varno, inner_path->parent))
|
||||
{
|
||||
/* righthand side is inner */
|
||||
innerbucketsize = restrictinfo->right_bucketsize;
|
||||
@@ -761,7 +761,7 @@ cost_hashjoin(Path *path, Query *root,
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(intMember(left->varno, inner_path->parent->relids));
|
||||
Assert(VARISRELMEMBER(left->varno, inner_path->parent));
|
||||
/* lefthand side is inner */
|
||||
innerbucketsize = restrictinfo->left_bucketsize;
|
||||
if (innerbucketsize < 0)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.67 2001/11/11 19:18:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.68 2002/03/12 00:51:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list,
|
||||
@@ -720,8 +721,6 @@ hash_inner_and_outer(Query *root,
|
||||
List *restrictlist,
|
||||
JoinType jointype)
|
||||
{
|
||||
Relids outerrelids = outerrel->relids;
|
||||
Relids innerrelids = innerrel->relids;
|
||||
bool isouterjoin;
|
||||
List *i;
|
||||
|
||||
@@ -773,13 +772,13 @@ hash_inner_and_outer(Query *root,
|
||||
/*
|
||||
* Check if clause is usable with these input rels.
|
||||
*/
|
||||
if (intMember(left->varno, outerrelids) &&
|
||||
intMember(right->varno, innerrelids))
|
||||
if (VARISRELMEMBER(left->varno, outerrel) &&
|
||||
VARISRELMEMBER(right->varno, innerrel))
|
||||
{
|
||||
/* righthand side is inner */
|
||||
}
|
||||
else if (intMember(left->varno, innerrelids) &&
|
||||
intMember(right->varno, outerrelids))
|
||||
else if (VARISRELMEMBER(left->varno, innerrel) &&
|
||||
VARISRELMEMBER(right->varno, outerrel))
|
||||
{
|
||||
/* lefthand side is inner */
|
||||
}
|
||||
@@ -901,8 +900,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
JoinType jointype)
|
||||
{
|
||||
List *result_list = NIL;
|
||||
Relids outerrelids = outerrel->relids;
|
||||
Relids innerrelids = innerrel->relids;
|
||||
bool isouterjoin = IS_OUTER_JOIN(jointype);
|
||||
List *i;
|
||||
|
||||
@@ -952,10 +949,10 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
left = get_leftop(clause);
|
||||
right = get_rightop(clause);
|
||||
|
||||
if ((intMember(left->varno, outerrelids) &&
|
||||
intMember(right->varno, innerrelids)) ||
|
||||
(intMember(left->varno, innerrelids) &&
|
||||
intMember(right->varno, outerrelids)))
|
||||
if ((VARISRELMEMBER(left->varno, outerrel) &&
|
||||
VARISRELMEMBER(right->varno, innerrel)) ||
|
||||
(VARISRELMEMBER(left->varno, innerrel) &&
|
||||
VARISRELMEMBER(right->varno, outerrel)))
|
||||
result_list = lcons(restrictinfo, result_list);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.55 2001/10/25 05:49:32 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.56 2002/03/12 00:51:44 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -340,7 +340,7 @@ make_jointree_rel(Query *root, Node *jtnode)
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
|
||||
return build_base_rel(root, varno);
|
||||
return find_base_rel(root, varno);
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.36 2001/11/11 20:33:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.37 2002/03/12 00:51:44 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -854,7 +854,8 @@ make_pathkeys_for_mergeclauses(Query *root,
|
||||
cache_mergeclause_pathkeys(root, restrictinfo);
|
||||
|
||||
key = (Node *) get_leftop(restrictinfo->clause);
|
||||
if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids))
|
||||
if (IsA(key, Var) &&
|
||||
VARISRELMEMBER(((Var *) key)->varno, rel))
|
||||
{
|
||||
/* Rel is left side of mergeclause */
|
||||
pathkey = restrictinfo->left_pathkey;
|
||||
@@ -862,7 +863,8 @@ make_pathkeys_for_mergeclauses(Query *root,
|
||||
else
|
||||
{
|
||||
key = (Node *) get_rightop(restrictinfo->clause);
|
||||
if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids))
|
||||
if (IsA(key, Var) &&
|
||||
VARISRELMEMBER(((Var *) key)->varno, rel))
|
||||
{
|
||||
/* Rel is right side of mergeclause */
|
||||
pathkey = restrictinfo->right_pathkey;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -51,6 +51,83 @@ static void check_mergejoinable(RestrictInfo *restrictinfo);
|
||||
static void check_hashjoinable(RestrictInfo *restrictinfo);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* JOIN TREES
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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_base_rels_to_query(Query *root, Node *jtnode)
|
||||
{
|
||||
List *result = NIL;
|
||||
|
||||
if (jtnode == NULL)
|
||||
return NIL;
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
|
||||
build_base_rel(root, varno);
|
||||
result = makeListi1(varno);
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *l;
|
||||
|
||||
foreach(l, f->fromlist)
|
||||
{
|
||||
result = nconc(result,
|
||||
add_base_rels_to_query(root, lfirst(l)));
|
||||
}
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
RelOptInfo *jrel;
|
||||
|
||||
result = add_base_rels_to_query(root, j->larg);
|
||||
result = nconc(result,
|
||||
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_base_rels_to_query: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* TARGET LISTS
|
||||
@@ -59,10 +136,8 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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)
|
||||
@@ -75,9 +150,13 @@ build_base_rel_tlists(Query *root, List *tlist)
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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)
|
||||
@@ -87,70 +166,25 @@ add_vars_to_targetlist(Query *root, List *vars)
|
||||
foreach(temp, vars)
|
||||
{
|
||||
Var *var = (Var *) lfirst(temp);
|
||||
RelOptInfo *rel = build_base_rel(root, var->varno);
|
||||
RelOptInfo *rel = find_base_rel(root, var->varno);
|
||||
|
||||
add_var_to_tlist(rel, var);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------
|
||||
* add_missing_rels_to_query
|
||||
*
|
||||
* If we have a relation listed in the join tree that does not appear
|
||||
* 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).
|
||||
*
|
||||
* 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.
|
||||
*----------
|
||||
*/
|
||||
List *
|
||||
add_missing_rels_to_query(Query *root, Node *jtnode)
|
||||
{
|
||||
List *result = NIL;
|
||||
|
||||
if (jtnode == NULL)
|
||||
return NIL;
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
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);
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *l;
|
||||
|
||||
foreach(l, f->fromlist)
|
||||
if (rel->reloptkind == RELOPT_OTHER_JOIN_REL)
|
||||
{
|
||||
result = nconc(result,
|
||||
add_missing_rels_to_query(root, lfirst(l)));
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
result = add_missing_rels_to_query(root, j->larg);
|
||||
result = nconc(result,
|
||||
add_missing_rels_to_query(root, j->rarg));
|
||||
}
|
||||
else
|
||||
elog(ERROR, "add_missing_rels_to_query: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,49 +176,33 @@ 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;
|
||||
|
||||
/*
|
||||
* Construct RelOptInfo nodes for all base relations in query.
|
||||
*/
|
||||
(void) add_base_rels_to_query(root, (Node *) root->jointree);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
joined_rels = add_missing_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.
|
||||
*/
|
||||
foreach(brel, root->base_rel_list)
|
||||
{
|
||||
RelOptInfo *baserel = (RelOptInfo *) lfirst(brel);
|
||||
int relid = lfirsti(baserel->relids);
|
||||
|
||||
if (!ptrMember(baserel, joined_rels))
|
||||
elog(ERROR, "Internal error: no jointree entry for rel %s (%d)",
|
||||
rt_fetch(relid, root->rtable)->eref->relname, relid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the completed lists of equijoined keys to deduce any implied
|
||||
* but unstated equalities (for example, A=B and B=C imply A=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;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,25 +9,29 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.73 2001/11/05 17:46:26 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.74 2002/03/12 00:51:48 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Query *root;
|
||||
List *outer_tlist;
|
||||
List *inner_tlist;
|
||||
Index acceptable_rel;
|
||||
Index join_rti;
|
||||
} join_references_context;
|
||||
|
||||
typedef struct
|
||||
@@ -38,7 +42,7 @@ typedef struct
|
||||
} replace_vars_with_subplan_refs_context;
|
||||
|
||||
static void fix_expr_references(Plan *plan, Node *node);
|
||||
static void set_join_references(Join *join);
|
||||
static void set_join_references(Query *root, Join *join);
|
||||
static void set_uppernode_references(Plan *plan, Index subvarno);
|
||||
static Node *join_references_mutator(Node *node,
|
||||
join_references_context *context);
|
||||
@@ -71,7 +75,7 @@ static bool fix_opids_walker(Node *node, void *context);
|
||||
* Returns nothing of interest, but modifies internal fields of nodes.
|
||||
*/
|
||||
void
|
||||
set_plan_references(Plan *plan)
|
||||
set_plan_references(Query *root, Plan *plan)
|
||||
{
|
||||
List *pl;
|
||||
|
||||
@@ -115,16 +119,16 @@ set_plan_references(Plan *plan)
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
/* Recurse into subplan too */
|
||||
set_plan_references(((SubqueryScan *) plan)->subplan);
|
||||
set_plan_references(root, ((SubqueryScan *) plan)->subplan);
|
||||
break;
|
||||
case T_NestLoop:
|
||||
set_join_references((Join *) plan);
|
||||
set_join_references(root, (Join *) plan);
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
|
||||
break;
|
||||
case T_MergeJoin:
|
||||
set_join_references((Join *) plan);
|
||||
set_join_references(root, (Join *) plan);
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
|
||||
@@ -132,7 +136,7 @@ set_plan_references(Plan *plan)
|
||||
(Node *) ((MergeJoin *) plan)->mergeclauses);
|
||||
break;
|
||||
case T_HashJoin:
|
||||
set_join_references((Join *) plan);
|
||||
set_join_references(root, (Join *) plan);
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
|
||||
@@ -186,7 +190,7 @@ set_plan_references(Plan *plan)
|
||||
* recurse into subplans.
|
||||
*/
|
||||
foreach(pl, ((Append *) plan)->appendplans)
|
||||
set_plan_references((Plan *) lfirst(pl));
|
||||
set_plan_references(root, (Plan *) lfirst(pl));
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "set_plan_references: unknown plan type %d",
|
||||
@@ -203,21 +207,21 @@ set_plan_references(Plan *plan)
|
||||
* plan's var nodes against the already-modified nodes of the
|
||||
* subplans.
|
||||
*/
|
||||
set_plan_references(plan->lefttree);
|
||||
set_plan_references(plan->righttree);
|
||||
set_plan_references(root, plan->lefttree);
|
||||
set_plan_references(root, plan->righttree);
|
||||
foreach(pl, plan->initPlan)
|
||||
{
|
||||
SubPlan *sp = (SubPlan *) lfirst(pl);
|
||||
|
||||
Assert(IsA(sp, SubPlan));
|
||||
set_plan_references(sp->plan);
|
||||
set_plan_references(root, sp->plan);
|
||||
}
|
||||
foreach(pl, plan->subPlan)
|
||||
{
|
||||
SubPlan *sp = (SubPlan *) lfirst(pl);
|
||||
|
||||
Assert(IsA(sp, SubPlan));
|
||||
set_plan_references(sp->plan);
|
||||
set_plan_references(root, sp->plan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +260,7 @@ fix_expr_references(Plan *plan, Node *node)
|
||||
* 'join' is a join plan node
|
||||
*/
|
||||
static void
|
||||
set_join_references(Join *join)
|
||||
set_join_references(Query *root, Join *join)
|
||||
{
|
||||
Plan *outer = join->plan.lefttree;
|
||||
Plan *inner = join->plan.righttree;
|
||||
@@ -264,9 +268,11 @@ set_join_references(Join *join)
|
||||
List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
|
||||
|
||||
join->plan.targetlist = join_references(join->plan.targetlist,
|
||||
root,
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
(Index) 0,
|
||||
join->joinrti);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -343,7 +349,8 @@ set_uppernode_references(Plan *plan, Index subvarno)
|
||||
* Creates a new set of targetlist entries or join qual clauses by
|
||||
* changing the varno/varattno values of variables in the clauses
|
||||
* to reference target list values from the outer and inner join
|
||||
* relation target lists.
|
||||
* relation target lists. Also, any join alias variables in the
|
||||
* clauses are expanded into references to their component variables.
|
||||
*
|
||||
* This is used in two different scenarios: a normal join clause, where
|
||||
* all the Vars in the clause *must* be replaced by OUTER or INNER references;
|
||||
@@ -360,21 +367,27 @@ set_uppernode_references(Plan *plan, Index subvarno)
|
||||
* 'inner_tlist' is the target list of the inner join relation, or NIL
|
||||
* 'acceptable_rel' is either zero or the rangetable index of a relation
|
||||
* whose Vars may appear in the clause without provoking an error.
|
||||
* 'join_rti' is either zero or the join RTE index of join alias variables
|
||||
* that should be expanded.
|
||||
*
|
||||
* Returns the new expression tree. The original clause structure is
|
||||
* not modified.
|
||||
*/
|
||||
List *
|
||||
join_references(List *clauses,
|
||||
Query *root,
|
||||
List *outer_tlist,
|
||||
List *inner_tlist,
|
||||
Index acceptable_rel)
|
||||
Index acceptable_rel,
|
||||
Index join_rti)
|
||||
{
|
||||
join_references_context context;
|
||||
|
||||
context.root = root;
|
||||
context.outer_tlist = outer_tlist;
|
||||
context.inner_tlist = inner_tlist;
|
||||
context.acceptable_rel = acceptable_rel;
|
||||
context.join_rti = join_rti;
|
||||
return (List *) join_references_mutator((Node *) clauses, &context);
|
||||
}
|
||||
|
||||
@@ -387,12 +400,14 @@ join_references_mutator(Node *node,
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
Var *newvar = (Var *) copyObject(var);
|
||||
Resdom *resdom;
|
||||
|
||||
/* First look for the var in the input tlists */
|
||||
resdom = tlist_member((Node *) var, context->outer_tlist);
|
||||
if (resdom)
|
||||
{
|
||||
Var *newvar = (Var *) copyObject(var);
|
||||
|
||||
newvar->varno = OUTER;
|
||||
newvar->varattno = resdom->resno;
|
||||
return (Node *) newvar;
|
||||
@@ -400,18 +415,33 @@ join_references_mutator(Node *node,
|
||||
resdom = tlist_member((Node *) var, context->inner_tlist);
|
||||
if (resdom)
|
||||
{
|
||||
Var *newvar = (Var *) copyObject(var);
|
||||
|
||||
newvar->varno = INNER;
|
||||
newvar->varattno = resdom->resno;
|
||||
return (Node *) newvar;
|
||||
}
|
||||
|
||||
/* Perhaps it's a join alias that can be resolved to input vars? */
|
||||
if (var->varno == context->join_rti)
|
||||
{
|
||||
Node *newnode;
|
||||
|
||||
newnode = flatten_join_alias_vars((Node *) var,
|
||||
context->root,
|
||||
context->join_rti);
|
||||
/* Must now resolve the input vars... */
|
||||
newnode = join_references_mutator(newnode, context);
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Var not in either tlist --- either raise an error, or return
|
||||
* the Var unmodified.
|
||||
* No referent found for Var --- either raise an error, or return
|
||||
* the Var unmodified if it's for acceptable_rel.
|
||||
*/
|
||||
if (var->varno != context->acceptable_rel)
|
||||
elog(ERROR, "join_references: variable not in subplan target lists");
|
||||
return (Node *) newvar;
|
||||
return (Node *) copyObject(var);
|
||||
}
|
||||
return expression_tree_mutator(node,
|
||||
join_references_mutator,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.71 2002/03/05 05:10:24 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.72 2002/03/12 00:51:49 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -673,7 +673,7 @@ List *
|
||||
expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
|
||||
{
|
||||
RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
|
||||
Oid parentOID = rte->relid;
|
||||
Oid parentOID;
|
||||
List *inhOIDs;
|
||||
List *inhRTIs;
|
||||
List *l;
|
||||
@@ -681,10 +681,11 @@ expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
|
||||
/* Does RT entry allow inheritance? */
|
||||
if (!rte->inh)
|
||||
return NIL;
|
||||
Assert(parentOID != InvalidOid && rte->subquery == NULL);
|
||||
Assert(rte->rtekind == RTE_RELATION);
|
||||
/* Always clear the parent's inh flag, see above comments */
|
||||
rte->inh = false;
|
||||
/* Fast path for common case of childless table */
|
||||
parentOID = rte->relid;
|
||||
if (!has_subclass(parentOID))
|
||||
return NIL;
|
||||
/* Scan for all members of inheritance set */
|
||||
@@ -811,6 +812,19 @@ adjust_inherited_attrs_mutator(Node *node,
|
||||
rtr->rtindex = context->new_rt_index;
|
||||
return (Node *) rtr;
|
||||
}
|
||||
if (IsA(node, JoinExpr))
|
||||
{
|
||||
/* Copy the JoinExpr node with correct mutation of subnodes */
|
||||
JoinExpr *j;
|
||||
|
||||
j = (JoinExpr *) expression_tree_mutator(node,
|
||||
adjust_inherited_attrs_mutator,
|
||||
(void *) context);
|
||||
/* now fix JoinExpr's rtindex */
|
||||
if (j->rtindex == context->old_rt_index)
|
||||
j->rtindex = context->new_rt_index;
|
||||
return (Node *) j;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to process RestrictInfo nodes specially: we do NOT want to
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.93 2002/01/03 18:01:59 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.94 2002/03/12 00:51:50 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node,
|
||||
return true;
|
||||
if (walker(join->quals, context))
|
||||
return true;
|
||||
if (walker((Node *) join->colvars, context))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* alias clause, using list, colnames list are deemed
|
||||
* uninteresting.
|
||||
* alias clause, using list are deemed uninteresting.
|
||||
*/
|
||||
}
|
||||
break;
|
||||
@@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node,
|
||||
MUTATE(newnode->larg, join->larg, Node *);
|
||||
MUTATE(newnode->rarg, join->rarg, Node *);
|
||||
MUTATE(newnode->quals, join->quals, Node *);
|
||||
MUTATE(newnode->colvars, join->colvars, List *);
|
||||
/* We do not mutate alias, using, or colnames by default */
|
||||
/* We do not mutate alias or using by default */
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.35 2001/10/25 05:49:34 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -40,26 +40,26 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
||||
|
||||
/*
|
||||
* build_base_rel
|
||||
* Returns relation entry corresponding to 'relid', creating a new one
|
||||
* if necessary. This is for base relations.
|
||||
* Construct a new base relation RelOptInfo, and put it in the query's
|
||||
* base_rel_list.
|
||||
*/
|
||||
RelOptInfo *
|
||||
void
|
||||
build_base_rel(Query *root, int relid)
|
||||
{
|
||||
List *rels;
|
||||
RelOptInfo *rel;
|
||||
|
||||
/* Already made? */
|
||||
/* Rel should not exist already */
|
||||
foreach(rels, root->base_rel_list)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(rels);
|
||||
|
||||
/* length(rel->relids) == 1 for all members of base_rel_list */
|
||||
if (lfirsti(rel->relids) == relid)
|
||||
return rel;
|
||||
elog(ERROR, "build_base_rel: rel already exists");
|
||||
}
|
||||
|
||||
/* It should not exist as an "other" rel */
|
||||
/* It should not exist as an "other" rel, either */
|
||||
foreach(rels, root->other_rel_list)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(rels);
|
||||
@@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid)
|
||||
|
||||
/* and add it to the list */
|
||||
root->base_rel_list = lcons(rel, root->base_rel_list);
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
/*
|
||||
* build_other_rel
|
||||
* Returns relation entry corresponding to 'relid', creating a new one
|
||||
* if necessary. This is for 'other' relations, which are just like
|
||||
* if necessary. This is for 'other' relations, which are much like
|
||||
* base relations except that they live in a different list.
|
||||
*/
|
||||
RelOptInfo *
|
||||
@@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid)
|
||||
/* No existing RelOptInfo for this other rel, so make a new one */
|
||||
rel = make_base_rel(root, relid);
|
||||
|
||||
/* if it's not a join rel, must be a child rel */
|
||||
if (rel->reloptkind == RELOPT_BASEREL)
|
||||
rel->reloptkind = RELOPT_OTHER_CHILD_REL;
|
||||
|
||||
/* and add it to the list */
|
||||
root->other_rel_list = lcons(rel, root->other_rel_list);
|
||||
|
||||
@@ -127,8 +129,9 @@ static RelOptInfo *
|
||||
make_base_rel(Query *root, int relid)
|
||||
{
|
||||
RelOptInfo *rel = makeNode(RelOptInfo);
|
||||
Oid relationObjectId;
|
||||
RangeTblEntry *rte = rt_fetch(relid, root->rtable);
|
||||
|
||||
rel->reloptkind = RELOPT_BASEREL;
|
||||
rel->relids = makeListi1(relid);
|
||||
rel->rows = 0;
|
||||
rel->width = 0;
|
||||
@@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid)
|
||||
rel->pages = 0;
|
||||
rel->tuples = 0;
|
||||
rel->subplan = NULL;
|
||||
rel->joinrti = 0;
|
||||
rel->joinrteids = NIL;
|
||||
rel->baserestrictinfo = NIL;
|
||||
rel->baserestrictcost = 0;
|
||||
rel->outerjoinset = NIL;
|
||||
rel->joininfo = NIL;
|
||||
rel->innerjoin = NIL;
|
||||
|
||||
/* Check rtable to see if it's a plain relation or a subquery */
|
||||
relationObjectId = getrelid(relid, root->rtable);
|
||||
|
||||
if (relationObjectId != InvalidOid)
|
||||
/* Check type of rtable entry */
|
||||
switch (rte->rtekind)
|
||||
{
|
||||
/* Plain relation --- retrieve statistics from the system catalogs */
|
||||
bool indexed;
|
||||
case RTE_RELATION:
|
||||
{
|
||||
/* Table --- 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
|
||||
{
|
||||
/* subquery --- mark it as such for later processing */
|
||||
rel->issubquery = true;
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user