mirror of
https://github.com/postgres/postgres.git
synced 2025-11-15 03:41:20 +03:00
Major planner/optimizer revision: get rid of PathOrder node type,
store all ordering information in pathkeys lists (which are now lists of lists of PathKeyItem nodes, not just lists of lists of vars). This was a big win --- the code is smaller and IMHO more understandable than it was, even though it handles more cases. I believe the node changes will not force an initdb for anyone; planner nodes don't show up in stored rules.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
# Makefile for optimizer/path
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Makefile,v 1.8 1999/02/20 15:27:42 momjian Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Makefile,v 1.9 1999/08/16 02:17:50 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -13,8 +13,8 @@ include ../../../Makefile.global
|
||||
|
||||
CFLAGS += -I../..
|
||||
|
||||
OBJS = allpaths.o clausesel.o costsize.o hashutils.o indxpath.o \
|
||||
joinpath.o joinrels.o mergeutils.o orindxpath.o pathkeys.o prune.o
|
||||
OBJS = allpaths.o clausesel.o costsize.o indxpath.o \
|
||||
joinpath.o joinrels.o orindxpath.o pathkeys.o prune.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.52 1999/07/30 22:34:17 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.53 1999/08/16 02:17:50 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -331,32 +331,14 @@ print_path(Query *root, Path *path, int indent)
|
||||
int size = path->parent->size;
|
||||
int relid = lfirsti(path->parent->relids);
|
||||
|
||||
printf("%s(%d) size=%d cost=%f",
|
||||
printf("%s(%d) size=%d cost=%f\n",
|
||||
ptype, relid, size, path->path_cost);
|
||||
|
||||
if (nodeTag(path) == T_IndexPath)
|
||||
if (IsA(path, IndexPath))
|
||||
{
|
||||
List *k,
|
||||
*l;
|
||||
|
||||
printf(" pathkeys=");
|
||||
foreach(k, path->pathkeys)
|
||||
{
|
||||
printf("(");
|
||||
foreach(l, lfirst(k))
|
||||
{
|
||||
Var *var = lfirst(l);
|
||||
|
||||
printf("%d.%d", var->varnoold, var->varoattno);
|
||||
if (lnext(l))
|
||||
printf(", ");
|
||||
}
|
||||
printf(")");
|
||||
if (lnext(k))
|
||||
printf(", ");
|
||||
}
|
||||
printf(" pathkeys=");
|
||||
print_pathkeys(path->pathkeys, root->rtable);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hashutils.c
|
||||
* Utilities for finding applicable merge clauses and pathkeys
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.18 1999/07/16 04:59:14 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/paths.h"
|
||||
|
||||
|
||||
static HashInfo *match_hashop_hashinfo(Oid hashop, List *hashinfo_list);
|
||||
|
||||
/*
|
||||
* group_clauses_by_hashop
|
||||
* If a join clause node in 'restrictinfo_list' is hashjoinable, store
|
||||
* it within a hashinfo node containing other clause nodes with the same
|
||||
* hash operator.
|
||||
*
|
||||
* 'restrictinfo_list' is the list of restrictinfo nodes
|
||||
* 'inner_relids' is the list of relids in the inner join relation
|
||||
* (used to determine whether a join var is inner or outer)
|
||||
*
|
||||
* Returns the new list of hashinfo nodes.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
group_clauses_by_hashop(List *restrictinfo_list,
|
||||
Relids inner_relids)
|
||||
{
|
||||
List *hashinfo_list = NIL;
|
||||
RestrictInfo *restrictinfo;
|
||||
List *i;
|
||||
Oid hashjoinop;
|
||||
|
||||
foreach(i, restrictinfo_list)
|
||||
{
|
||||
restrictinfo = (RestrictInfo *) lfirst(i);
|
||||
hashjoinop = restrictinfo->hashjoinoperator;
|
||||
|
||||
/*
|
||||
* Create a new hashinfo node and add it to 'hashinfo_list' if one
|
||||
* does not yet exist for this hash operator.
|
||||
*/
|
||||
if (hashjoinop)
|
||||
{
|
||||
Expr *clause = restrictinfo->clause;
|
||||
Var *leftop = get_leftop(clause);
|
||||
Var *rightop = get_rightop(clause);
|
||||
HashInfo *xhashinfo;
|
||||
JoinKey *joinkey;
|
||||
|
||||
xhashinfo = match_hashop_hashinfo(hashjoinop, hashinfo_list);
|
||||
joinkey = makeNode(JoinKey);
|
||||
if (intMember(leftop->varno, inner_relids))
|
||||
{
|
||||
joinkey->outer = rightop;
|
||||
joinkey->inner = leftop;
|
||||
}
|
||||
else
|
||||
{
|
||||
joinkey->outer = leftop;
|
||||
joinkey->inner = rightop;
|
||||
}
|
||||
|
||||
if (xhashinfo == NULL)
|
||||
{
|
||||
xhashinfo = makeNode(HashInfo);
|
||||
xhashinfo->hashop = hashjoinop;
|
||||
xhashinfo->jmethod.jmkeys = NIL;
|
||||
xhashinfo->jmethod.clauses = NIL;
|
||||
hashinfo_list = lcons(xhashinfo, hashinfo_list);
|
||||
}
|
||||
|
||||
xhashinfo->jmethod.clauses = lcons(clause,
|
||||
xhashinfo->jmethod.clauses);
|
||||
xhashinfo->jmethod.jmkeys = lcons(joinkey,
|
||||
xhashinfo->jmethod.jmkeys);
|
||||
}
|
||||
}
|
||||
return hashinfo_list;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* match_hashop_hashinfo
|
||||
* Searches the list 'hashinfo_list' for a hashinfo node whose hash op
|
||||
* field equals 'hashop'.
|
||||
*
|
||||
* Returns the node if it exists.
|
||||
*
|
||||
*/
|
||||
static HashInfo *
|
||||
match_hashop_hashinfo(Oid hashop, List *hashinfo_list)
|
||||
{
|
||||
Oid key = 0;
|
||||
HashInfo *xhashinfo = (HashInfo *) NULL;
|
||||
List *i = NIL;
|
||||
|
||||
foreach(i, hashinfo_list)
|
||||
{
|
||||
xhashinfo = (HashInfo *) lfirst(i);
|
||||
key = xhashinfo->hashop;
|
||||
if (hashop == key)
|
||||
{ /* found */
|
||||
return xhashinfo; /* should be a hashinfo node ! */
|
||||
}
|
||||
}
|
||||
return (HashInfo *) NIL;
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.68 1999/08/12 04:32:51 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.69 1999/08/16 02:17:50 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/ordering.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/plancat.h"
|
||||
@@ -70,7 +68,8 @@ static void indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
List **clausegroups, List **outerrelids);
|
||||
static List *index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
List *clausegroup_list, List *outerrelids_list);
|
||||
static bool useful_for_mergejoin(RelOptInfo *index, List *clausegroup_list);
|
||||
static bool useful_for_mergejoin(RelOptInfo *rel, RelOptInfo *index,
|
||||
List *joininfo_list);
|
||||
static bool match_index_to_operand(int indexkey, Var *operand,
|
||||
RelOptInfo *rel, RelOptInfo *index);
|
||||
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
|
||||
@@ -189,36 +188,34 @@ create_index_paths(Query *root,
|
||||
restrictclauses));
|
||||
|
||||
/*
|
||||
* 3. If this index can be used with any join clause, then create
|
||||
* an index path for it even if there were no restriction clauses.
|
||||
* 3. If this index can be used for a mergejoin, then create an
|
||||
* index path for it even if there were no restriction clauses.
|
||||
* (If there were, there is no need to make another index path.)
|
||||
* This will allow the index to be considered as a base for a
|
||||
* mergejoin in later processing.
|
||||
* Also, create an innerjoin index path for each combination of
|
||||
*/
|
||||
if (restrictclauses == NIL &&
|
||||
useful_for_mergejoin(rel, index, joininfo_list))
|
||||
{
|
||||
retval = lappend(retval,
|
||||
create_index_path(root, rel, index, NIL));
|
||||
}
|
||||
|
||||
/*
|
||||
* 4. Create an innerjoin index path for each combination of
|
||||
* other rels used in available join clauses. These paths will
|
||||
* be considered as the inner side of nestloop joins against
|
||||
* those sets of other rels.
|
||||
* indexable_joinclauses() finds clauses that are potentially
|
||||
* applicable to either case. useful_for_mergejoin() tests to
|
||||
* see whether any of the join clauses might support a mergejoin.
|
||||
* index_innerjoin() builds an innerjoin index path for each
|
||||
* potential set of outer rels, which we add to the rel's
|
||||
* innerjoin list.
|
||||
* those sets of other rels. indexable_joinclauses() finds sets
|
||||
* of clauses that can be used with each combination of outer rels,
|
||||
* and index_innerjoin builds the paths themselves. We add the
|
||||
* paths to the rel's innerjoin list, NOT to the result list.
|
||||
*/
|
||||
indexable_joinclauses(rel, index,
|
||||
joininfo_list, restrictinfo_list,
|
||||
&joinclausegroups,
|
||||
&joinouterrelids);
|
||||
|
||||
if (joinclausegroups != NIL)
|
||||
{
|
||||
/* no need to create a plain path if we already did */
|
||||
if (restrictclauses == NIL &&
|
||||
useful_for_mergejoin(index, joinclausegroups))
|
||||
retval = lappend(retval,
|
||||
create_index_path(root, rel, index,
|
||||
NIL));
|
||||
|
||||
rel->innerjoin = nconc(rel->innerjoin,
|
||||
index_innerjoin(root, rel, index,
|
||||
joinclausegroups,
|
||||
@@ -272,11 +269,11 @@ match_index_orclauses(RelOptInfo *rel,
|
||||
* Add this index to the subclause index list for each
|
||||
* subclause that it matches.
|
||||
*/
|
||||
restrictinfo->indexids =
|
||||
restrictinfo->subclauseindices =
|
||||
match_index_orclause(rel, index,
|
||||
indexkey, xclass,
|
||||
restrictinfo->clause->args,
|
||||
restrictinfo->indexids);
|
||||
restrictinfo->subclauseindices);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -439,7 +436,8 @@ group_clauses_by_indexkey(RelOptInfo *rel,
|
||||
|
||||
/*
|
||||
* group_clauses_by_ikey_for_joins
|
||||
* Generates a list of join clauses that can be used with an index.
|
||||
* Generates a list of join clauses that can be used with an index
|
||||
* to scan the inner side of a nestloop join.
|
||||
*
|
||||
* This is much like group_clauses_by_indexkey(), but we consider both
|
||||
* join and restriction clauses. For each indexkey in the index, we
|
||||
@@ -447,7 +445,7 @@ group_clauses_by_indexkey(RelOptInfo *rel,
|
||||
* will make useful indexquals if the index is being used to scan the
|
||||
* inner side of a nestloop join. But there must be at least one matching
|
||||
* join clause, or we return NIL indicating that this index isn't useful
|
||||
* for joining.
|
||||
* for nestloop joining.
|
||||
*/
|
||||
static List *
|
||||
group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
@@ -1156,7 +1154,7 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
/*
|
||||
* indexable_joinclauses
|
||||
* Finds all groups of join clauses from among 'joininfo_list' that can
|
||||
* be used in conjunction with 'index'.
|
||||
* be used in conjunction with 'index' for the inner scan of a nestjoin.
|
||||
*
|
||||
* Each clause group comes from a single joininfo node plus the current
|
||||
* rel's restrictinfo list. Therefore, every clause in the group references
|
||||
@@ -1224,7 +1222,7 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
* 'rel' is the relation for which 'index' is defined
|
||||
* 'clausegroup_list' is a list of lists of restrictinfo nodes which can use
|
||||
* 'index'. Each sublist refers to the same set of outer rels.
|
||||
* 'outerrelids_list' is a list of the required outer rels for each group
|
||||
* 'outerrelids_list' is a list of the required outer rels for each sublist
|
||||
* of join clauses.
|
||||
*
|
||||
* Returns a list of index pathnodes.
|
||||
@@ -1255,14 +1253,11 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
&npages,
|
||||
&selec);
|
||||
|
||||
/* XXX this code ought to be merged with create_index_path */
|
||||
/* XXX this code ought to be merged with create_index_path? */
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.pathorder = makeNode(PathOrder);
|
||||
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
|
||||
pathnode->path.pathorder->ord.sortop = index->ordering;
|
||||
pathnode->path.pathkeys = NIL;
|
||||
pathnode->path.pathkeys = build_index_pathkeys(root, rel, index);
|
||||
|
||||
/* Note that we are making a pathnode for a single-scan indexscan;
|
||||
* therefore, both indexid and indexqual should be single-element
|
||||
@@ -1272,10 +1267,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
pathnode->indexid = index->relids;
|
||||
pathnode->indexqual = lcons(indexquals, NIL);
|
||||
|
||||
pathnode->indexkeys = index->indexkeys;
|
||||
|
||||
/* joinid saves the rels needed on the outer side of the join */
|
||||
pathnode->path.joinid = lfirst(outerrelids_list);
|
||||
/* joinrelids saves the rels needed on the outer side of the join */
|
||||
pathnode->joinrelids = lfirst(outerrelids_list);
|
||||
|
||||
pathnode->path.path_cost = cost_index((Oid) lfirsti(index->relids),
|
||||
(int) npages,
|
||||
@@ -1295,33 +1288,53 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
/*
|
||||
* useful_for_mergejoin
|
||||
* Determine whether the given index can support a mergejoin based
|
||||
* on any join clause within the given list. The clauses have already
|
||||
* been found to be relevant to the index by indexable_joinclauses.
|
||||
* We just need to check whether any are mergejoin material.
|
||||
* on any available join clause.
|
||||
*
|
||||
* 'index' is the index of interest.
|
||||
* 'clausegroup_list' is a list of clause groups (sublists of restrictinfo
|
||||
* nodes)
|
||||
* We look to see whether the first indexkey of the index matches the
|
||||
* left or right sides of any of the mergejoinable clauses and provides
|
||||
* the ordering needed for that side. If so, the index is useful.
|
||||
* Matching a second or later indexkey is not useful unless there is
|
||||
* also a mergeclause for the first indexkey, so we need not consider
|
||||
* secondary indexkeys at this stage.
|
||||
*
|
||||
* 'rel' is the relation for which 'index' is defined
|
||||
* 'joininfo_list' is the list of JoinInfo nodes for 'rel'
|
||||
*/
|
||||
static bool
|
||||
useful_for_mergejoin(RelOptInfo *index,
|
||||
List *clausegroup_list)
|
||||
useful_for_mergejoin(RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
List *joininfo_list)
|
||||
{
|
||||
int *indexkeys = index->indexkeys;
|
||||
Oid *ordering = index->ordering;
|
||||
List *i;
|
||||
|
||||
foreach(i, clausegroup_list)
|
||||
if (!indexkeys || indexkeys[0] == 0 ||
|
||||
!ordering || ordering[0] == InvalidOid)
|
||||
return false; /* unordered index is not useful */
|
||||
|
||||
foreach(i, joininfo_list)
|
||||
{
|
||||
List *clausegroup = lfirst(i);
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
||||
List *j;
|
||||
|
||||
foreach(j, clausegroup)
|
||||
foreach(j, joininfo->jinfo_restrictinfo)
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
|
||||
|
||||
if (is_joinable((Node *) restrictinfo->clause) &&
|
||||
equal_path_merge_ordering(index->ordering,
|
||||
restrictinfo->mergejoinorder))
|
||||
return true;
|
||||
if (restrictinfo->mergejoinoperator)
|
||||
{
|
||||
if (restrictinfo->left_sortop == ordering[0] &&
|
||||
match_index_to_operand(indexkeys[0],
|
||||
get_leftop(restrictinfo->clause),
|
||||
rel, index))
|
||||
return true;
|
||||
if (restrictinfo->right_sortop == ordering[0] &&
|
||||
match_index_to_operand(indexkeys[0],
|
||||
get_rightop(restrictinfo->clause),
|
||||
rel, index))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1348,7 +1361,12 @@ match_index_to_operand(int indexkey,
|
||||
/*
|
||||
* Normal index.
|
||||
*/
|
||||
return match_indexkey_operand(indexkey, operand, rel);
|
||||
if (IsA(operand, Var) &&
|
||||
lfirsti(rel->relids) == operand->varno &&
|
||||
indexkey == operand->varattno)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1384,7 +1402,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
|
||||
/*
|
||||
* Check that the arguments correspond to the same arguments used to
|
||||
* create the functional index. To do this we must check that 1.
|
||||
* refer to the right relatiion. 2. the args have the right attr.
|
||||
* refer to the right relation. 2. the args have the right attr.
|
||||
* numbers in the right order.
|
||||
*/
|
||||
i = 0;
|
||||
@@ -1402,6 +1420,9 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
|
||||
i++;
|
||||
}
|
||||
|
||||
if (indexKeys[i] != 0)
|
||||
return false; /* not enough arguments */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.44 1999/08/09 03:16:43 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.45 1999/08/16 02:17:51 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -22,20 +22,26 @@
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/restrictinfo.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static Path *best_innerjoin(List *join_paths, List *outer_relid);
|
||||
static List *sort_inner_and_outer(RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *mergeinfo_list);
|
||||
static List *match_unsorted_outer(RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *outerpath_list, Path *cheapest_inner, Path *best_innerjoin,
|
||||
List *mergeinfo_list);
|
||||
static List *match_unsorted_inner(RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *innerpath_list, List *mergeinfo_list);
|
||||
static List *sort_inner_and_outer(RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *mergeclause_list);
|
||||
static List *match_unsorted_outer(RelOptInfo *joinrel, RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel, List *outerpath_list,
|
||||
Path *cheapest_inner, Path *best_innerjoin,
|
||||
List *mergeclause_list);
|
||||
static List *match_unsorted_inner(RelOptInfo *joinrel, RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel, List *innerpath_list,
|
||||
List *mergeclause_list);
|
||||
static List *hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel);
|
||||
static Cost estimate_disbursion(Query *root, Var *var);
|
||||
static List *select_mergejoin_clauses(List *restrictinfo_list);
|
||||
|
||||
/*
|
||||
* update_rels_pathlist_for_joins
|
||||
@@ -43,19 +49,10 @@ static Cost estimate_disbursion(Query *root, Var *var);
|
||||
* relations in the list 'joinrels.' Each unique path will be included
|
||||
* in the join relation's 'pathlist' field.
|
||||
*
|
||||
* In postgres, n-way joins are handled left-only(permuting clauseless
|
||||
* joins doesn't usually win much).
|
||||
*
|
||||
* if BushyPlanFlag is true, bushy tree plans will be generated
|
||||
*
|
||||
* 'joinrels' is the list of relation entries to be joined
|
||||
*
|
||||
* Modifies the pathlist field of each joinrel node to contain
|
||||
* the unique join paths.
|
||||
* If bushy trees are considered, may modify the relid field of the
|
||||
* join rel nodes to flatten the lists.
|
||||
*
|
||||
* It does a destructive modification.
|
||||
*/
|
||||
void
|
||||
update_rels_pathlist_for_joins(Query *root, List *joinrels)
|
||||
@@ -70,8 +67,8 @@ update_rels_pathlist_for_joins(Query *root, List *joinrels)
|
||||
RelOptInfo *innerrel;
|
||||
RelOptInfo *outerrel;
|
||||
Path *bestinnerjoin;
|
||||
List *pathlist = NIL;
|
||||
List *mergeinfo_list = NIL;
|
||||
List *pathlist;
|
||||
List *mergeclause_list = NIL;
|
||||
|
||||
/*
|
||||
* On entry, joinrel->relids is a list of two sublists of relids,
|
||||
@@ -98,24 +95,26 @@ update_rels_pathlist_for_joins(Query *root, List *joinrels)
|
||||
get_join_rel(root, outerrelids);
|
||||
|
||||
/*
|
||||
* Get the best inner join for match_unsorted_outer.
|
||||
* Get the best inner join for match_unsorted_outer().
|
||||
*/
|
||||
bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids);
|
||||
|
||||
/*
|
||||
* Find potential mergejoin clauses.
|
||||
*/
|
||||
if (_enable_mergejoin_)
|
||||
mergeinfo_list = group_clauses_by_order(joinrel->restrictinfo,
|
||||
innerrel->relids);
|
||||
mergeclause_list = select_mergejoin_clauses(joinrel->restrictinfo);
|
||||
|
||||
/*
|
||||
* 1. Consider mergejoin paths where both relations must be
|
||||
* explicitly sorted.
|
||||
*/
|
||||
pathlist = sort_inner_and_outer(joinrel, outerrel,
|
||||
innerrel, mergeinfo_list);
|
||||
innerrel, mergeclause_list);
|
||||
|
||||
/*
|
||||
* 2. Consider paths where the outer relation need not be
|
||||
* explicitly sorted. This may include either nestloops and
|
||||
* explicitly sorted. This includes both nestloops and
|
||||
* mergejoins where the outer path is already ordered.
|
||||
*/
|
||||
pathlist = add_pathlist(joinrel, pathlist,
|
||||
@@ -123,21 +122,20 @@ update_rels_pathlist_for_joins(Query *root, List *joinrels)
|
||||
outerrel,
|
||||
innerrel,
|
||||
outerrel->pathlist,
|
||||
innerrel->cheapestpath,
|
||||
innerrel->cheapestpath,
|
||||
bestinnerjoin,
|
||||
mergeinfo_list));
|
||||
mergeclause_list));
|
||||
|
||||
/*
|
||||
* 3. Consider paths where the inner relation need not be
|
||||
* explicitly sorted. This may include nestloops and mergejoins
|
||||
* the actual nestloop nodes were constructed in
|
||||
* (match_unsorted_outer).
|
||||
* explicitly sorted. This includes mergejoins only
|
||||
* (nestloops were already built in match_unsorted_outer).
|
||||
*/
|
||||
pathlist = add_pathlist(joinrel, pathlist,
|
||||
match_unsorted_inner(joinrel, outerrel,
|
||||
innerrel,
|
||||
innerrel->pathlist,
|
||||
mergeinfo_list));
|
||||
mergeclause_list));
|
||||
|
||||
/*
|
||||
* 4. Consider paths where both outer and inner relations must be
|
||||
@@ -176,11 +174,13 @@ best_innerjoin(List *join_paths, Relids outer_relids)
|
||||
{
|
||||
Path *path = (Path *) lfirst(join_path);
|
||||
|
||||
/* path->joinid is the set of base rels that must be part of
|
||||
Assert(IsA(path, IndexPath));
|
||||
|
||||
/* path->joinrelids is the set of base rels that must be part of
|
||||
* outer_relids in order to use this inner path, because those
|
||||
* rels are used in the index join quals of this inner path.
|
||||
*/
|
||||
if (is_subset(path->joinid, outer_relids) &&
|
||||
if (is_subset(((IndexPath *) path)->joinrelids, outer_relids) &&
|
||||
(cheapest == NULL ||
|
||||
path_is_cheaper(path, cheapest)))
|
||||
cheapest = path;
|
||||
@@ -196,8 +196,8 @@ best_innerjoin(List *join_paths, Relids outer_relids)
|
||||
* 'joinrel' is the join relation
|
||||
* 'outerrel' is the outer join relation
|
||||
* 'innerrel' is the inner join relation
|
||||
* 'mergeinfo_list' is a list of nodes containing info on(mergejoinable)
|
||||
* clauses for joining the relations
|
||||
* 'mergeclause_list' is a list of RestrictInfo nodes for available
|
||||
* mergejoin clauses between these two relations
|
||||
*
|
||||
* Returns a list of mergejoin paths.
|
||||
*/
|
||||
@@ -205,32 +205,59 @@ static List *
|
||||
sort_inner_and_outer(RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *mergeinfo_list)
|
||||
List *mergeclause_list)
|
||||
{
|
||||
List *ms_list = NIL;
|
||||
MergeInfo *xmergeinfo = (MergeInfo *) NULL;
|
||||
MergePath *temp_node = (MergePath *) NULL;
|
||||
List *path_list = NIL;
|
||||
List *i;
|
||||
List *outerkeys = NIL;
|
||||
List *innerkeys = NIL;
|
||||
List *merge_pathkeys = NIL;
|
||||
|
||||
foreach(i, mergeinfo_list)
|
||||
/*
|
||||
* Each possible ordering of the available mergejoin clauses will
|
||||
* generate a differently-sorted result path at essentially the
|
||||
* same cost. We have no basis for choosing one over another at
|
||||
* this level of joining, but some sort orders may be more useful
|
||||
* than others for higher-level mergejoins. Generating a path here
|
||||
* for *every* permutation of mergejoin clauses doesn't seem like
|
||||
* a winning strategy, however; the cost in planning time is too high.
|
||||
*
|
||||
* For now, we generate one path for each mergejoin clause, listing that
|
||||
* clause first and the rest in random order. This should allow at least
|
||||
* a one-clause mergejoin without re-sorting against any other possible
|
||||
* mergejoin partner path. But if we've not guessed the right ordering
|
||||
* of secondary clauses, we may end up evaluating clauses as qpquals when
|
||||
* they could have been done as mergeclauses. We need to figure out a
|
||||
* better way. (Two possible approaches: look at all the relevant index
|
||||
* relations to suggest plausible sort orders, or make just one output
|
||||
* path and somehow mark it as having a sort-order that can be rearranged
|
||||
* freely.)
|
||||
*/
|
||||
foreach(i, mergeclause_list)
|
||||
{
|
||||
xmergeinfo = (MergeInfo *) lfirst(i);
|
||||
RestrictInfo *restrictinfo = lfirst(i);
|
||||
List *curclause_list;
|
||||
List *outerkeys;
|
||||
List *innerkeys;
|
||||
List *merge_pathkeys;
|
||||
MergePath *path_node;
|
||||
|
||||
outerkeys = make_pathkeys_from_joinkeys(xmergeinfo->jmethod.jmkeys,
|
||||
outerrel->targetlist,
|
||||
OUTER);
|
||||
|
||||
innerkeys = make_pathkeys_from_joinkeys(xmergeinfo->jmethod.jmkeys,
|
||||
innerrel->targetlist,
|
||||
INNER);
|
||||
|
||||
merge_pathkeys = new_join_pathkeys(outerkeys, joinrel->targetlist,
|
||||
xmergeinfo->jmethod.clauses);
|
||||
|
||||
temp_node = create_mergejoin_path(joinrel,
|
||||
/* Make a mergeclause list with this guy first. */
|
||||
curclause_list = lcons(restrictinfo,
|
||||
lremove(restrictinfo,
|
||||
listCopy(mergeclause_list)));
|
||||
/* Build sort pathkeys for both sides.
|
||||
*
|
||||
* Note: it's possible that the cheapest path will already be
|
||||
* sorted properly --- create_mergejoin_path will detect that case
|
||||
* and suppress an explicit sort step.
|
||||
*/
|
||||
outerkeys = make_pathkeys_for_mergeclauses(curclause_list,
|
||||
outerrel->targetlist);
|
||||
innerkeys = make_pathkeys_for_mergeclauses(curclause_list,
|
||||
innerrel->targetlist);
|
||||
/* Build pathkeys representing output sort order. */
|
||||
merge_pathkeys = build_join_pathkeys(outerkeys, joinrel->targetlist,
|
||||
curclause_list);
|
||||
/* And now we can make the path. */
|
||||
path_node = create_mergejoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
@@ -238,40 +265,50 @@ sort_inner_and_outer(RelOptInfo *joinrel,
|
||||
(Path *) outerrel->cheapestpath,
|
||||
(Path *) innerrel->cheapestpath,
|
||||
merge_pathkeys,
|
||||
xmergeinfo->m_ordering,
|
||||
xmergeinfo->jmethod.clauses,
|
||||
get_actual_clauses(curclause_list),
|
||||
outerkeys,
|
||||
innerkeys);
|
||||
|
||||
ms_list = lappend(ms_list, temp_node);
|
||||
path_list = lappend(path_list, path_node);
|
||||
}
|
||||
return ms_list;
|
||||
return path_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* match_unsorted_outer
|
||||
* Creates possible join paths for processing a single join relation
|
||||
* 'joinrel' by employing either iterative substitution or
|
||||
* mergejoining on each of its possible outer paths(assuming that the
|
||||
* outer relation need not be explicitly sorted).
|
||||
* mergejoining on each of its possible outer paths (considering
|
||||
* only outer paths that are already ordered well enough for merging).
|
||||
*
|
||||
* 1. The inner path is the cheapest available inner path.
|
||||
* 2. Mergejoin wherever possible. Mergejoin are considered if there
|
||||
* are mergejoinable join clauses between the outer and inner join
|
||||
* relations such that the outer path is keyed on the variables
|
||||
* appearing in the clauses. The corresponding inner merge path is
|
||||
* either a path whose keys match those of the outer path(if such a
|
||||
* path is available) or an explicit sort on the appropriate inner
|
||||
* join keys, whichever is cheaper.
|
||||
* We always generate a nestloop path for each available outer path.
|
||||
* If an indexscan inner path exists that is compatible with this outer rel
|
||||
* and cheaper than the cheapest general-purpose inner path, then we use
|
||||
* the indexscan inner path; else we use the cheapest general-purpose inner.
|
||||
*
|
||||
* We also consider mergejoins if mergejoin clauses are available. We have
|
||||
* two ways to generate the inner path for a mergejoin: use the cheapest
|
||||
* inner path (sorting it if it's not suitably ordered already), or using an
|
||||
* inner path that is already suitably ordered for the merge. If the
|
||||
* cheapest inner path is suitably ordered, then by definition it's the one
|
||||
* to use. Otherwise, we look for ordered paths that are cheaper than the
|
||||
* cheapest inner + sort costs. If we have several mergeclauses, it could be
|
||||
* that there is no inner path (or only a very expensive one) for the full
|
||||
* list of mergeclauses, but better paths exist if we truncate the
|
||||
* mergeclause list (thereby discarding some sort key requirements). So, we
|
||||
* consider truncations of the mergeclause list as well as the full list.
|
||||
* In any case, we find the cheapest suitable path and generate a single
|
||||
* output mergejoin path. (Since all the possible mergejoins will have
|
||||
* identical output pathkeys, there is no need to keep any but the cheapest.)
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outerrel' is the outer join relation
|
||||
* 'innerrel' is the inner join relation
|
||||
* 'outerpath_list' is the list of possible outer paths
|
||||
* 'cheapest_inner' is the cheapest inner path
|
||||
* 'best_innerjoin' is the best inner index path(if any)
|
||||
* 'mergeinfo_list' is a list of nodes containing info on mergejoinable
|
||||
* clauses
|
||||
* 'best_innerjoin' is the best inner index path (if any)
|
||||
* 'mergeclause_list' is a list of RestrictInfo nodes for available
|
||||
* mergejoin clauses between these two relations
|
||||
*
|
||||
* Returns a list of possible join path nodes.
|
||||
*/
|
||||
@@ -282,139 +319,139 @@ match_unsorted_outer(RelOptInfo *joinrel,
|
||||
List *outerpath_list,
|
||||
Path *cheapest_inner,
|
||||
Path *best_innerjoin,
|
||||
List *mergeinfo_list)
|
||||
List *mergeclause_list)
|
||||
{
|
||||
Path *outerpath = (Path *) NULL;
|
||||
List *jp_list = NIL;
|
||||
List *temp_node = NIL;
|
||||
List *merge_pathkeys = NIL;
|
||||
Path *nestinnerpath = (Path *) NULL;
|
||||
List *paths = NIL;
|
||||
List *i = NIL;
|
||||
PathOrder *outerpath_ordering = NULL;
|
||||
List *path_list = NIL;
|
||||
Path *nestinnerpath;
|
||||
List *i;
|
||||
|
||||
/*
|
||||
* We only use the best innerjoin indexpath if it is cheaper
|
||||
* than the cheapest general-purpose inner path.
|
||||
*/
|
||||
if (best_innerjoin &&
|
||||
path_is_cheaper(best_innerjoin, cheapest_inner))
|
||||
nestinnerpath = best_innerjoin;
|
||||
else
|
||||
nestinnerpath = cheapest_inner;
|
||||
|
||||
foreach(i, outerpath_list)
|
||||
{
|
||||
List *clauses = NIL;
|
||||
List *matchedJoinKeys = NIL;
|
||||
List *matchedJoinClauses = NIL;
|
||||
MergeInfo *xmergeinfo = NULL;
|
||||
Path *outerpath = (Path *) lfirst(i);
|
||||
List *mergeclauses;
|
||||
List *merge_pathkeys;
|
||||
List *innersortkeys;
|
||||
Path *mergeinnerpath;
|
||||
int mergeclausecount;
|
||||
|
||||
outerpath = (Path *) lfirst(i);
|
||||
/* Look for useful mergeclauses (if any) */
|
||||
mergeclauses = find_mergeclauses_for_pathkeys(outerpath->pathkeys,
|
||||
mergeclause_list);
|
||||
/*
|
||||
* The result will have this sort order (even if it is implemented
|
||||
* as a nestloop, and even if some of the mergeclauses are implemented
|
||||
* by qpquals rather than as true mergeclauses):
|
||||
*/
|
||||
merge_pathkeys = build_join_pathkeys(outerpath->pathkeys,
|
||||
joinrel->targetlist,
|
||||
mergeclauses);
|
||||
|
||||
outerpath_ordering = outerpath->pathorder;
|
||||
/* Always consider a nestloop join with this outer and best inner. */
|
||||
path_list = lappend(path_list,
|
||||
create_nestloop_path(joinrel,
|
||||
outerrel,
|
||||
outerpath,
|
||||
nestinnerpath,
|
||||
merge_pathkeys));
|
||||
|
||||
if (outerpath_ordering)
|
||||
xmergeinfo = match_order_mergeinfo(outerpath_ordering,
|
||||
mergeinfo_list);
|
||||
/* Done with this outer path if no chance for a mergejoin */
|
||||
if (mergeclauses == NIL)
|
||||
continue;
|
||||
|
||||
if (xmergeinfo)
|
||||
clauses = xmergeinfo->jmethod.clauses;
|
||||
/* Compute the required ordering of the inner path */
|
||||
innersortkeys = make_pathkeys_for_mergeclauses(mergeclauses,
|
||||
innerrel->targetlist);
|
||||
|
||||
if (clauses)
|
||||
/* Set up on the assumption that we will use the cheapest_inner */
|
||||
mergeinnerpath = cheapest_inner;
|
||||
mergeclausecount = length(mergeclauses);
|
||||
|
||||
/* If the cheapest_inner doesn't need to be sorted, it is the winner
|
||||
* by definition.
|
||||
*/
|
||||
if (pathkeys_contained_in(innersortkeys,
|
||||
cheapest_inner->pathkeys))
|
||||
{
|
||||
List *jmkeys = xmergeinfo->jmethod.jmkeys;
|
||||
|
||||
order_joinkeys_by_pathkeys(outerpath->pathkeys,
|
||||
jmkeys,
|
||||
clauses,
|
||||
OUTER,
|
||||
&matchedJoinKeys,
|
||||
&matchedJoinClauses);
|
||||
merge_pathkeys = new_join_pathkeys(outerpath->pathkeys,
|
||||
joinrel->targetlist, clauses);
|
||||
/* cheapest_inner is the winner */
|
||||
innersortkeys = NIL; /* we do not need to sort it... */
|
||||
}
|
||||
else
|
||||
merge_pathkeys = outerpath->pathkeys;
|
||||
|
||||
if (best_innerjoin &&
|
||||
path_is_cheaper(best_innerjoin, cheapest_inner))
|
||||
nestinnerpath = best_innerjoin;
|
||||
else
|
||||
nestinnerpath = cheapest_inner;
|
||||
|
||||
paths = lcons(create_nestloop_path(joinrel,
|
||||
outerrel,
|
||||
outerpath,
|
||||
nestinnerpath,
|
||||
merge_pathkeys),
|
||||
NIL);
|
||||
|
||||
if (clauses && matchedJoinKeys)
|
||||
{
|
||||
bool path_is_cheaper_than_sort;
|
||||
List *varkeys = NIL;
|
||||
Path *mergeinnerpath = get_cheapest_path_for_joinkeys(
|
||||
matchedJoinKeys,
|
||||
outerpath_ordering,
|
||||
innerrel->pathlist,
|
||||
INNER);
|
||||
/* look for a presorted path that's cheaper */
|
||||
List *trialsortkeys = listCopy(innersortkeys);
|
||||
Cost cheapest_cost;
|
||||
int clausecount;
|
||||
|
||||
/* Should we use the mergeinner, or sort the cheapest inner? */
|
||||
path_is_cheaper_than_sort = (bool) (mergeinnerpath &&
|
||||
(mergeinnerpath->path_cost <
|
||||
(cheapest_inner->path_cost +
|
||||
cost_sort(matchedJoinKeys, innerrel->size,
|
||||
innerrel->width))));
|
||||
if (!path_is_cheaper_than_sort)
|
||||
cheapest_cost = cheapest_inner->path_cost +
|
||||
cost_sort(innersortkeys, innerrel->size, innerrel->width);
|
||||
|
||||
for (clausecount = mergeclausecount;
|
||||
clausecount > 0;
|
||||
clausecount--)
|
||||
{
|
||||
varkeys = make_pathkeys_from_joinkeys(matchedJoinKeys,
|
||||
innerrel->targetlist,
|
||||
INNER);
|
||||
Path *trialinnerpath;
|
||||
|
||||
/* Look for an inner path ordered well enough to merge with
|
||||
* the first 'clausecount' mergeclauses. NB: trialsortkeys
|
||||
* is modified destructively, which is why we made a copy...
|
||||
*/
|
||||
trialinnerpath =
|
||||
get_cheapest_path_for_pathkeys(innerrel->pathlist,
|
||||
ltruncate(clausecount,
|
||||
trialsortkeys));
|
||||
if (trialinnerpath != NULL &&
|
||||
trialinnerpath->path_cost < cheapest_cost)
|
||||
{
|
||||
/* Found a cheaper (or even-cheaper) sorted path */
|
||||
cheapest_cost = trialinnerpath->path_cost;
|
||||
mergeinnerpath = trialinnerpath;
|
||||
mergeclausecount = clausecount;
|
||||
innersortkeys = NIL; /* we will not need to sort it... */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Keep track of the cost of the outer path used with this
|
||||
* ordered inner path for later processing in
|
||||
* (match_unsorted_inner), since it isn't a sort and thus
|
||||
* wouldn't otherwise be considered.
|
||||
*/
|
||||
if (path_is_cheaper_than_sort)
|
||||
mergeinnerpath->outerjoincost = outerpath->path_cost;
|
||||
else
|
||||
mergeinnerpath = cheapest_inner;
|
||||
|
||||
temp_node = lcons(create_mergejoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
outerpath,
|
||||
mergeinnerpath,
|
||||
merge_pathkeys,
|
||||
xmergeinfo->m_ordering,
|
||||
matchedJoinClauses,
|
||||
NIL,
|
||||
varkeys),
|
||||
paths);
|
||||
}
|
||||
else
|
||||
temp_node = paths;
|
||||
jp_list = nconc(jp_list, temp_node);
|
||||
|
||||
/* Finally, we can build the mergejoin path */
|
||||
mergeclauses = ltruncate(mergeclausecount,
|
||||
get_actual_clauses(mergeclauses));
|
||||
path_list = lappend(path_list,
|
||||
create_mergejoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
outerpath,
|
||||
mergeinnerpath,
|
||||
merge_pathkeys,
|
||||
mergeclauses,
|
||||
NIL,
|
||||
innersortkeys));
|
||||
}
|
||||
return jp_list;
|
||||
|
||||
return path_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* match_unsorted_inner
|
||||
* Find the cheapest ordered join path for a given(ordered, unsorted)
|
||||
* inner join path.
|
||||
*
|
||||
* Scans through each path available on an inner join relation and tries
|
||||
* matching its ordering keys against those of mergejoin clauses.
|
||||
* If 1. an appropriately_ordered inner path and matching mergeclause are
|
||||
* found, and
|
||||
* 2. sorting the cheapest outer path is cheaper than using an ordered
|
||||
* but unsorted outer path(as was considered in
|
||||
* (match_unsorted_outer)), then this merge path is considered.
|
||||
* Generate mergejoin paths that use an explicit sort of the outer path
|
||||
* with an already-ordered inner path.
|
||||
*
|
||||
* 'joinrel' is the join result relation
|
||||
* 'outerrel' is the outer join relation
|
||||
* 'innerrel' is the inner join relation
|
||||
* 'innerpath_list' is the list of possible inner join paths
|
||||
* 'mergeinfo_list' is a list of nodes containing info on mergejoinable
|
||||
* clauses
|
||||
* 'mergeclause_list' is a list of RestrictInfo nodes for available
|
||||
* mergejoin clauses between these two relations
|
||||
*
|
||||
* Returns a list of possible merge paths.
|
||||
*/
|
||||
@@ -423,78 +460,74 @@ match_unsorted_inner(RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *innerpath_list,
|
||||
List *mergeinfo_list)
|
||||
List *mergeclause_list)
|
||||
{
|
||||
List *mp_list = NIL;
|
||||
List *path_list = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, innerpath_list)
|
||||
{
|
||||
Path *innerpath = (Path *) lfirst(i);
|
||||
PathOrder *innerpath_ordering = innerpath->pathorder;
|
||||
MergeInfo *xmergeinfo = (MergeInfo *) NULL;
|
||||
List *clauses = NIL;
|
||||
List *matchedJoinKeys = NIL;
|
||||
List *matchedJoinClauses = NIL;
|
||||
List *mergeclauses;
|
||||
|
||||
if (innerpath_ordering)
|
||||
xmergeinfo = match_order_mergeinfo(innerpath_ordering,
|
||||
mergeinfo_list);
|
||||
/* Look for useful mergeclauses (if any) */
|
||||
mergeclauses = find_mergeclauses_for_pathkeys(innerpath->pathkeys,
|
||||
mergeclause_list);
|
||||
|
||||
if (xmergeinfo)
|
||||
clauses = ((JoinMethod *) xmergeinfo)->clauses;
|
||||
|
||||
if (clauses)
|
||||
if (mergeclauses)
|
||||
{
|
||||
List *jmkeys = xmergeinfo->jmethod.jmkeys;
|
||||
List *outersortkeys;
|
||||
Path *mergeouterpath;
|
||||
List *merge_pathkeys;
|
||||
|
||||
order_joinkeys_by_pathkeys(innerpath->pathkeys,
|
||||
jmkeys,
|
||||
clauses,
|
||||
INNER,
|
||||
&matchedJoinKeys,
|
||||
&matchedJoinClauses);
|
||||
}
|
||||
/* Compute the required ordering of the outer path */
|
||||
outersortkeys =
|
||||
make_pathkeys_for_mergeclauses(mergeclauses,
|
||||
outerrel->targetlist);
|
||||
|
||||
/*
|
||||
* (match_unsorted_outer) if it is applicable. 'OuterJoinCost was
|
||||
* set above in
|
||||
*/
|
||||
if (clauses && matchedJoinKeys)
|
||||
{
|
||||
Cost temp1;
|
||||
/* Look for an outer path already ordered well enough to merge */
|
||||
mergeouterpath =
|
||||
get_cheapest_path_for_pathkeys(outerrel->pathlist,
|
||||
outersortkeys);
|
||||
|
||||
temp1 = outerrel->cheapestpath->path_cost +
|
||||
cost_sort(matchedJoinKeys, outerrel->size, outerrel->width);
|
||||
|
||||
if (innerpath->outerjoincost <= 0 /* unset? */
|
||||
|| innerpath->outerjoincost > temp1)
|
||||
/* Should we use the mergeouter, or sort the cheapest outer? */
|
||||
if (mergeouterpath != NULL &&
|
||||
mergeouterpath->path_cost <=
|
||||
(outerrel->cheapestpath->path_cost +
|
||||
cost_sort(outersortkeys, outerrel->size, outerrel->width)))
|
||||
{
|
||||
List *outerkeys = make_pathkeys_from_joinkeys(matchedJoinKeys,
|
||||
outerrel->targetlist,
|
||||
OUTER);
|
||||
List *merge_pathkeys = new_join_pathkeys(outerkeys,
|
||||
joinrel->targetlist,
|
||||
clauses);
|
||||
|
||||
mp_list = lappend(mp_list,
|
||||
create_mergejoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
(Path *) outerrel->cheapestpath,
|
||||
innerpath,
|
||||
merge_pathkeys,
|
||||
xmergeinfo->m_ordering,
|
||||
matchedJoinClauses,
|
||||
outerkeys,
|
||||
NIL));
|
||||
/* Use mergeouterpath */
|
||||
outersortkeys = NIL; /* no explicit sort step */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use outerrel->cheapestpath, with the outersortkeys */
|
||||
mergeouterpath = outerrel->cheapestpath;
|
||||
}
|
||||
|
||||
/* Compute pathkeys the result will have */
|
||||
merge_pathkeys = build_join_pathkeys(
|
||||
outersortkeys ? outersortkeys : mergeouterpath->pathkeys,
|
||||
joinrel->targetlist,
|
||||
mergeclauses);
|
||||
|
||||
mergeclauses = get_actual_clauses(mergeclauses);
|
||||
path_list = lappend(path_list,
|
||||
create_mergejoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
mergeouterpath,
|
||||
innerpath,
|
||||
merge_pathkeys,
|
||||
mergeclauses,
|
||||
outersortkeys,
|
||||
NIL));
|
||||
}
|
||||
}
|
||||
|
||||
return mp_list;
|
||||
return path_list;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -520,49 +553,23 @@ hash_inner_and_outer(Query *root,
|
||||
foreach(i, joinrel->restrictinfo)
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
|
||||
Oid hashjoinop = restrictinfo->hashjoinoperator;
|
||||
|
||||
/* we consider only clauses previously marked hashjoinable */
|
||||
if (hashjoinop)
|
||||
if (restrictinfo->hashjoinoperator)
|
||||
{
|
||||
Expr *clause = restrictinfo->clause;
|
||||
Var *leftop = get_leftop(clause);
|
||||
Var *rightop = get_rightop(clause);
|
||||
JoinKey *joinkey = makeNode(JoinKey);
|
||||
List *joinkey_list;
|
||||
List *outerkeys;
|
||||
List *innerkeys;
|
||||
Var *innerop;
|
||||
Cost innerdisbursion;
|
||||
List *hash_pathkeys;
|
||||
HashPath *hash_path;
|
||||
|
||||
/* construct joinkey and pathkeys for this clause */
|
||||
/* find the inner var and estimate its disbursion */
|
||||
if (intMember(leftop->varno, innerrel->relids))
|
||||
{
|
||||
joinkey->outer = rightop;
|
||||
joinkey->inner = leftop;
|
||||
}
|
||||
innerop = leftop;
|
||||
else
|
||||
{
|
||||
joinkey->outer = leftop;
|
||||
joinkey->inner = rightop;
|
||||
}
|
||||
joinkey_list = lcons(joinkey, NIL);
|
||||
|
||||
outerkeys = make_pathkeys_from_joinkeys(joinkey_list,
|
||||
outerrel->targetlist,
|
||||
OUTER);
|
||||
innerkeys = make_pathkeys_from_joinkeys(joinkey_list,
|
||||
innerrel->targetlist,
|
||||
INNER);
|
||||
|
||||
innerdisbursion = estimate_disbursion(root, joinkey->inner);
|
||||
|
||||
/*
|
||||
* We cannot assume that the output of the hashjoin appears in
|
||||
* any particular order, so it should have NIL pathkeys.
|
||||
*/
|
||||
hash_pathkeys = NIL;
|
||||
innerop = rightop;
|
||||
innerdisbursion = estimate_disbursion(root, innerop);
|
||||
|
||||
hash_path = create_hashjoin_path(joinrel,
|
||||
outerrel->size,
|
||||
@@ -571,11 +578,7 @@ hash_inner_and_outer(Query *root,
|
||||
innerrel->width,
|
||||
(Path *) outerrel->cheapestpath,
|
||||
(Path *) innerrel->cheapestpath,
|
||||
hash_pathkeys,
|
||||
hashjoinop,
|
||||
lcons(clause, NIL),
|
||||
outerkeys,
|
||||
innerkeys,
|
||||
innerdisbursion);
|
||||
hpath_list = lappend(hpath_list, hash_path);
|
||||
}
|
||||
@@ -605,3 +608,36 @@ estimate_disbursion(Query *root, Var *var)
|
||||
|
||||
return (Cost) get_attdisbursion(relid, var->varattno, 0.1);
|
||||
}
|
||||
|
||||
/*
|
||||
* select_mergejoin_clauses
|
||||
* Select mergejoin clauses that are usable for a particular join.
|
||||
* Returns a list of RestrictInfo nodes for those clauses.
|
||||
*
|
||||
* Currently, all we need is the restrictinfo list of the joinrel.
|
||||
* By definition, any mergejoinable clause in that list will work ---
|
||||
* it must involve only vars in the join, or it wouldn't have been
|
||||
* in the restrict list, and it must involve vars on both sides of
|
||||
* the join, or it wouldn't have made it up to this level of join.
|
||||
* Since we currently allow only simple Vars as the left and right
|
||||
* sides of mergejoin clauses, that means the mergejoin clauses must
|
||||
* be usable for this join. If we ever allow more complex expressions
|
||||
* containing multiple Vars, we would need to check that each side
|
||||
* of a potential joinclause uses only vars from one side of the join.
|
||||
*/
|
||||
static List *
|
||||
select_mergejoin_clauses(List *restrictinfo_list)
|
||||
{
|
||||
List *result_list = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, restrictinfo_list)
|
||||
{
|
||||
RestrictInfo *restrictinfo = lfirst(i);
|
||||
|
||||
if (restrictinfo->mergejoinoperator != InvalidOid)
|
||||
result_list = lcons(restrictinfo, result_list);
|
||||
}
|
||||
|
||||
return result_list;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,22 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.38 1999/07/27 06:23:12 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.39 1999/08/16 02:17:51 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#ifndef MAXINT
|
||||
#define MAXINT INT_MAX
|
||||
#endif
|
||||
#else
|
||||
#ifdef HAVE_VALUES_H
|
||||
#include <values.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/joininfo.h"
|
||||
@@ -20,17 +30,18 @@
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/tlist.h"
|
||||
|
||||
static List *new_joininfo_list(List *joininfo_list, Relids join_relids);
|
||||
static void set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel, JoinInfo *jinfo);
|
||||
static RelOptInfo *make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
|
||||
JoinInfo *joininfo);
|
||||
static RelOptInfo *make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel);
|
||||
static List *new_join_tlist(List *tlist, int first_resdomno);
|
||||
static void build_joinrel_restrict_and_join(RelOptInfo *joinrel,
|
||||
List *joininfo_list,
|
||||
Relids join_relids);
|
||||
static void set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel);
|
||||
|
||||
/*
|
||||
* make_rels_by_joins
|
||||
* Find all possible joins for each of the outer join relations in
|
||||
* 'outer_rels'. A rel node is created for each possible join relation,
|
||||
* 'old_rels'. A rel node is created for each possible join relation,
|
||||
* and the resulting list of nodes is returned. If at all possible, only
|
||||
* those relations for which join clauses exist are considered. If none
|
||||
* of these exist for a given relation, all remaining possibilities are
|
||||
@@ -41,19 +52,18 @@ static List *new_join_tlist(List *tlist, int first_resdomno);
|
||||
List *
|
||||
make_rels_by_joins(Query *root, List *old_rels)
|
||||
{
|
||||
List *joined_rels = NIL;
|
||||
List *join_list = NIL;
|
||||
List *r = NIL;
|
||||
List *r;
|
||||
|
||||
foreach(r, old_rels)
|
||||
{
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
List *joined_rels;
|
||||
|
||||
if (!(joined_rels = make_rels_by_clause_joins(root, old_rel,
|
||||
old_rel->joininfo,
|
||||
NIL)))
|
||||
{
|
||||
|
||||
/*
|
||||
* Oops, we have a relation that is not joined to any other
|
||||
* relation. Cartesian product time.
|
||||
@@ -73,16 +83,16 @@ make_rels_by_joins(Query *root, List *old_rels)
|
||||
|
||||
/*
|
||||
* make_rels_by_clause_joins
|
||||
* Determines whether joins can be performed between an outer relation
|
||||
* 'outer_rel' and those relations within 'outer_rel's joininfo nodes
|
||||
* (i.e., relations that participate in join clauses that 'outer_rel'
|
||||
* participates in). This is possible if all but one of the relations
|
||||
* contained within the join clauses of the joininfo node are already
|
||||
* contained within 'outer_rel'.
|
||||
* Build joins between an outer relation 'old_rel' and relations
|
||||
* within old_rel's joininfo nodes
|
||||
* (i.e., relations that participate in join clauses that 'old_rel'
|
||||
* also participates in).
|
||||
*
|
||||
* 'outer_rel' is the relation entry for the outer relation
|
||||
* 'joininfo_list' is a list of join clauses which 'outer_rel'
|
||||
* 'old_rel' is the relation entry for the outer relation
|
||||
* 'joininfo_list' is a list of join clauses which 'old_rel'
|
||||
* participates in
|
||||
* 'only_relids': if not NIL, only joins against base rels mentioned in
|
||||
* only_relids are allowable.
|
||||
*
|
||||
* Returns a list of new join relations.
|
||||
*/
|
||||
@@ -91,55 +101,55 @@ make_rels_by_clause_joins(Query *root, RelOptInfo *old_rel,
|
||||
List *joininfo_list, Relids only_relids)
|
||||
{
|
||||
List *join_list = NIL;
|
||||
List *i = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, joininfo_list)
|
||||
{
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
||||
RelOptInfo *joined_rel;
|
||||
Relids unjoined_relids = joininfo->unjoined_relids;
|
||||
RelOptInfo *joined_rel;
|
||||
|
||||
if (unjoined_relids != NIL)
|
||||
if (unjoined_relids == NIL)
|
||||
continue; /* probably can't happen */
|
||||
|
||||
if (length(unjoined_relids) == 1 &&
|
||||
(only_relids == NIL ||
|
||||
/* geqo only wants certain relids to be joined to old_rel */
|
||||
intMember(lfirsti(unjoined_relids), only_relids)))
|
||||
{
|
||||
if (length(unjoined_relids) == 1 &&
|
||||
(only_relids == NIL ||
|
||||
/* geqo only wants certain relids to make new rels */
|
||||
intMember(lfirsti(unjoined_relids), only_relids)))
|
||||
RelOptInfo *base_rel = get_base_rel(root,
|
||||
lfirsti(unjoined_relids));
|
||||
|
||||
/* Left-sided join of outer rel against a single base rel */
|
||||
joined_rel = make_join_rel(old_rel, base_rel);
|
||||
join_list = lappend(join_list, joined_rel);
|
||||
|
||||
/* Consider right-sided plan as well */
|
||||
if (length(old_rel->relids) > 1)
|
||||
{
|
||||
joined_rel = make_join_rel(old_rel,
|
||||
get_base_rel(root,
|
||||
lfirsti(unjoined_relids)),
|
||||
joininfo);
|
||||
joined_rel = make_join_rel(base_rel, old_rel);
|
||||
join_list = lappend(join_list, joined_rel);
|
||||
|
||||
/* Right-sided plan */
|
||||
if (length(old_rel->relids) > 1)
|
||||
{
|
||||
joined_rel = make_join_rel(
|
||||
get_base_rel(root, lfirsti(unjoined_relids)),
|
||||
old_rel,
|
||||
joininfo);
|
||||
join_list = lappend(join_list, joined_rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (only_relids == NIL) /* no bushy from geqo */
|
||||
if (only_relids == NIL) /* no bushy plans for geqo */
|
||||
{
|
||||
List *r;
|
||||
|
||||
/* Build "bushy" plans: join old_rel against all pre-existing
|
||||
* joins of rels it doesn't already contain, if there is a
|
||||
* suitable join clause.
|
||||
*/
|
||||
foreach(r, root->join_rel_list)
|
||||
{
|
||||
List *r;
|
||||
RelOptInfo *join_rel = lfirst(r);
|
||||
|
||||
foreach(r, root->join_rel_list)
|
||||
Assert(length(join_rel->relids) > 1);
|
||||
if (is_subset(unjoined_relids, join_rel->relids) &&
|
||||
nonoverlap_sets(old_rel->relids, join_rel->relids))
|
||||
{
|
||||
RelOptInfo *join_rel = lfirst(r);
|
||||
|
||||
Assert(length(join_rel->relids) > 1);
|
||||
if (is_subset(unjoined_relids, join_rel->relids) &&
|
||||
nonoverlap_sets(old_rel->relids, join_rel->relids))
|
||||
{
|
||||
joined_rel = make_join_rel(old_rel,
|
||||
join_rel,
|
||||
joininfo);
|
||||
join_list = lappend(join_list, joined_rel);
|
||||
}
|
||||
joined_rel = make_join_rel(old_rel, join_rel);
|
||||
join_list = lappend(join_list, joined_rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,32 +160,30 @@ make_rels_by_clause_joins(Query *root, RelOptInfo *old_rel,
|
||||
|
||||
/*
|
||||
* make_rels_by_clauseless_joins
|
||||
* Given an outer relation 'outer_rel' and a list of inner relations
|
||||
* 'inner_rels', create a join relation between 'outer_rel' and each
|
||||
* member of 'inner_rels' that isn't already included in 'outer_rel'.
|
||||
* Given an outer relation 'old_rel' and a list of inner relations
|
||||
* 'inner_rels', create a join relation between 'old_rel' and each
|
||||
* member of 'inner_rels' that isn't already included in 'old_rel'.
|
||||
*
|
||||
* Returns a list of new join relations.
|
||||
*/
|
||||
List *
|
||||
make_rels_by_clauseless_joins(RelOptInfo *old_rel, List *inner_rels)
|
||||
{
|
||||
RelOptInfo *inner_rel;
|
||||
List *t_list = NIL;
|
||||
List *i = NIL;
|
||||
List *join_list = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, inner_rels)
|
||||
{
|
||||
inner_rel = (RelOptInfo *) lfirst(i);
|
||||
RelOptInfo *inner_rel = (RelOptInfo *) lfirst(i);
|
||||
|
||||
if (nonoverlap_sets(inner_rel->relids, old_rel->relids))
|
||||
{
|
||||
t_list = lappend(t_list,
|
||||
make_join_rel(old_rel,
|
||||
inner_rel,
|
||||
(JoinInfo *) NULL));
|
||||
join_list = lappend(join_list,
|
||||
make_join_rel(old_rel, inner_rel));
|
||||
}
|
||||
}
|
||||
|
||||
return t_list;
|
||||
return join_list;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -184,19 +192,38 @@ make_rels_by_clauseless_joins(RelOptInfo *old_rel, List *inner_rels)
|
||||
*
|
||||
* 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
|
||||
* joined
|
||||
* 'joininfo' is the joininfo node(join clause) containing both
|
||||
* 'outer_rel' and 'inner_rel', if any exists
|
||||
*
|
||||
* Returns the new join relation node.
|
||||
*/
|
||||
static RelOptInfo *
|
||||
make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo)
|
||||
make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel)
|
||||
{
|
||||
RelOptInfo *joinrel = makeNode(RelOptInfo);
|
||||
List *joinrel_joininfo_list = NIL;
|
||||
List *new_outer_tlist;
|
||||
List *new_inner_tlist;
|
||||
|
||||
/*
|
||||
* This function uses a trick to pass inner/outer rels as two sublists.
|
||||
* The list will be flattened out in update_rels_pathlist_for_joins().
|
||||
*/
|
||||
joinrel->relids = lcons(outer_rel->relids, lcons(inner_rel->relids, NIL));
|
||||
joinrel->indexed = false;
|
||||
joinrel->pages = 0;
|
||||
joinrel->tuples = 0;
|
||||
joinrel->size = 0;
|
||||
joinrel->width = 0;
|
||||
/* joinrel->targetlist = NIL;*/
|
||||
joinrel->pathlist = NIL;
|
||||
joinrel->cheapestpath = (Path *) NULL;
|
||||
joinrel->pruneable = true;
|
||||
joinrel->classlist = NULL;
|
||||
joinrel->indexkeys = NULL;
|
||||
joinrel->ordering = NULL;
|
||||
joinrel->relam = InvalidOid;
|
||||
joinrel->restrictinfo = NIL;
|
||||
joinrel->joininfo = NIL;
|
||||
joinrel->innerjoin = NIL;
|
||||
|
||||
/*
|
||||
* Create a new tlist by removing irrelevant elements from both tlists
|
||||
* of the outer and inner join relations and then merging the results
|
||||
@@ -205,44 +232,18 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo)
|
||||
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->relids = NIL;
|
||||
joinrel->indexed = false;
|
||||
joinrel->pages = 0;
|
||||
joinrel->tuples = 0;
|
||||
joinrel->width = 0;
|
||||
/* joinrel->targetlist = NIL;*/
|
||||
joinrel->pathlist = NIL;
|
||||
joinrel->cheapestpath = (Path *) NULL;
|
||||
joinrel->pruneable = true;
|
||||
joinrel->classlist = NULL;
|
||||
joinrel->relam = InvalidOid;
|
||||
joinrel->ordering = NULL;
|
||||
joinrel->restrictinfo = NIL;
|
||||
joinrel->joininfo = NULL;
|
||||
joinrel->innerjoin = NIL;
|
||||
joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
|
||||
|
||||
/*
|
||||
* This function uses a trick to pass inner/outer rels as different
|
||||
* lists, and then flattens it out later. bjm
|
||||
* Construct restrict and join clause lists for the new joinrel.
|
||||
*/
|
||||
joinrel->relids = lcons(outer_rel->relids, lcons(inner_rel->relids, NIL));
|
||||
build_joinrel_restrict_and_join(joinrel,
|
||||
nconc(copyObject(outer_rel->joininfo),
|
||||
copyObject(inner_rel->joininfo)),
|
||||
nconc(listCopy(outer_rel->relids),
|
||||
listCopy(inner_rel->relids)));
|
||||
|
||||
new_outer_tlist = nconc(new_outer_tlist, new_inner_tlist);
|
||||
joinrel->targetlist = new_outer_tlist;
|
||||
|
||||
if (joininfo)
|
||||
joinrel->restrictinfo = joininfo->jinfo_restrictinfo;
|
||||
|
||||
joinrel_joininfo_list = new_joininfo_list(
|
||||
nconc(copyObject(outer_rel->joininfo),
|
||||
copyObject(inner_rel->joininfo)),
|
||||
nconc(listCopy(outer_rel->relids),
|
||||
listCopy(inner_rel->relids)));
|
||||
|
||||
joinrel->joininfo = joinrel_joininfo_list;
|
||||
|
||||
set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo);
|
||||
set_joinrel_size(joinrel, outer_rel, inner_rel);
|
||||
|
||||
return joinrel;
|
||||
}
|
||||
@@ -255,6 +256,9 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo)
|
||||
* for future joins, its 'joinlist' field must not be empty after removal
|
||||
* of all relids in 'other_relids'.
|
||||
*
|
||||
* XXX this seems to be a dead test --- we don't keep track of joinlists
|
||||
* for individual targetlist entries anymore, if we ever did...
|
||||
*
|
||||
* 'tlist' is the target list of one of the join relations
|
||||
* 'other_relids' is a list of relids contained within the other
|
||||
* join relation
|
||||
@@ -268,15 +272,14 @@ new_join_tlist(List *tlist,
|
||||
int first_resdomno)
|
||||
{
|
||||
int resdomno = first_resdomno - 1;
|
||||
TargetEntry *xtl = NULL;
|
||||
List *t_list = NIL;
|
||||
List *i = NIL;
|
||||
List *i;
|
||||
List *join_list = NIL;
|
||||
bool in_final_tlist = false;
|
||||
|
||||
foreach(i, tlist)
|
||||
{
|
||||
xtl = lfirst(i);
|
||||
TargetEntry *xtl = lfirst(i);
|
||||
bool in_final_tlist;
|
||||
|
||||
/*
|
||||
* XXX surely this is wrong? join_list is never changed? tgl
|
||||
@@ -286,7 +289,8 @@ new_join_tlist(List *tlist,
|
||||
if (in_final_tlist)
|
||||
{
|
||||
resdomno += 1;
|
||||
t_list = lappend(t_list, create_tl_element(get_expr(xtl), resdomno));
|
||||
t_list = lappend(t_list,
|
||||
create_tl_element(get_expr(xtl), resdomno));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,69 +298,81 @@ new_join_tlist(List *tlist,
|
||||
}
|
||||
|
||||
/*
|
||||
* new_joininfo_list
|
||||
* Builds a join relation's joininfo list by checking for join clauses
|
||||
* which still need to used in future joins involving this relation. A
|
||||
* join clause is still needed if there are still relations in the clause
|
||||
* not contained in the list of relations comprising this join relation.
|
||||
* New joininfo nodes are only created and added to
|
||||
* 'current_joininfo_list' if a node for a particular join hasn't already
|
||||
* been created.
|
||||
* build_joinrel_restrict_and_join
|
||||
* Builds a join relation's restrictinfo and joininfo lists from the
|
||||
* joininfo lists of the relations it joins. If a join clause from an
|
||||
* input relation refers to base rels still not present in the joinrel,
|
||||
* then it is still a join clause for the joinrel; we put it into an
|
||||
* appropriate JoinInfo list for the joinrel. Otherwise, the clause is
|
||||
* now a restrict clause for the joined relation, and we put it into
|
||||
* the joinrel's restrictinfo list. (It will not need to be considered
|
||||
* further up the join tree.)
|
||||
*
|
||||
* 'current_joininfo_list' contains a list of those joininfo nodes that
|
||||
* have already been built
|
||||
* 'joininfo_list' is the list of join clauses involving this relation
|
||||
* 'join_relids' is a list of relids corresponding to the relations
|
||||
* currently being joined
|
||||
* 'joininfo_list' is a list of joininfo nodes from the relations being joined
|
||||
* 'join_relids' is a list of all base relids in the new join relation
|
||||
*
|
||||
* Returns a list of joininfo nodes, new and old.
|
||||
* NB: the elements of joininfo_list have all been COPIED and so can safely
|
||||
* be destructively modified and/or inserted in the new joinrel's lists.
|
||||
* The amount of copying going on here is probably vastly excessive,
|
||||
* since we copied the underlying clauses as well...
|
||||
*/
|
||||
static List *
|
||||
new_joininfo_list(List *joininfo_list, Relids join_relids)
|
||||
static void
|
||||
build_joinrel_restrict_and_join(RelOptInfo *joinrel,
|
||||
List *joininfo_list,
|
||||
Relids join_relids)
|
||||
{
|
||||
List *current_joininfo_list = NIL;
|
||||
Relids new_unjoined_relids = NIL;
|
||||
JoinInfo *other_joininfo = (JoinInfo *) NULL;
|
||||
List *xjoininfo = NIL;
|
||||
List *output_restrictinfo_list = NIL;
|
||||
List *output_joininfo_list = NIL;
|
||||
List *xjoininfo;
|
||||
|
||||
foreach(xjoininfo, joininfo_list)
|
||||
{
|
||||
List *or;
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo);
|
||||
Relids new_unjoined_relids;
|
||||
|
||||
new_unjoined_relids = joininfo->unjoined_relids;
|
||||
foreach(or, new_unjoined_relids)
|
||||
new_unjoined_relids = set_differencei(joininfo->unjoined_relids,
|
||||
join_relids);
|
||||
if (new_unjoined_relids == NIL)
|
||||
{
|
||||
if (intMember(lfirsti(or), join_relids))
|
||||
new_unjoined_relids = lremove((void *) lfirst(or), new_unjoined_relids);
|
||||
/*
|
||||
* Clauses in this JoinInfo list become restriction clauses
|
||||
* for the joinrel, since they refer to no outside rels.
|
||||
*
|
||||
* Be careful to eliminate duplicates, since we will see the
|
||||
* same clauses arriving from both input relations...
|
||||
*/
|
||||
output_restrictinfo_list =
|
||||
LispUnion(output_restrictinfo_list,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
}
|
||||
joininfo->unjoined_relids = new_unjoined_relids;
|
||||
if (new_unjoined_relids != NIL)
|
||||
else
|
||||
{
|
||||
other_joininfo = joininfo_member(new_unjoined_relids,
|
||||
current_joininfo_list);
|
||||
if (other_joininfo)
|
||||
JoinInfo *old_joininfo;
|
||||
|
||||
/*
|
||||
* There might already be a JoinInfo with the same set of
|
||||
* unjoined relids in output_joininfo_list; don't make a
|
||||
* redundant entry.
|
||||
*/
|
||||
old_joininfo = joininfo_member(new_unjoined_relids,
|
||||
output_joininfo_list);
|
||||
if (old_joininfo)
|
||||
{
|
||||
other_joininfo->jinfo_restrictinfo = (List *)
|
||||
LispUnion(joininfo->jinfo_restrictinfo,
|
||||
other_joininfo->jinfo_restrictinfo);
|
||||
old_joininfo->jinfo_restrictinfo =
|
||||
LispUnion(old_joininfo->jinfo_restrictinfo,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
other_joininfo = makeNode(JoinInfo);
|
||||
|
||||
other_joininfo->unjoined_relids = joininfo->unjoined_relids;
|
||||
other_joininfo->jinfo_restrictinfo = joininfo->jinfo_restrictinfo;
|
||||
other_joininfo->mergejoinable = joininfo->mergejoinable;
|
||||
other_joininfo->hashjoinable = joininfo->hashjoinable;
|
||||
|
||||
current_joininfo_list = lcons(other_joininfo,
|
||||
current_joininfo_list);
|
||||
joininfo->unjoined_relids = new_unjoined_relids;
|
||||
output_joininfo_list = lcons(joininfo,
|
||||
output_joininfo_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return current_joininfo_list;
|
||||
joinrel->restrictinfo = output_restrictinfo_list;
|
||||
joinrel->joininfo = output_joininfo_list;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -376,7 +392,12 @@ get_cheapest_complete_rel(List *join_rel_list)
|
||||
|
||||
/*
|
||||
* find the relations that have no further joins, i.e., its joininfos
|
||||
* all have unjoined_relids nil.
|
||||
* all have unjoined_relids nil. (Actually, a JoinInfo shouldn't
|
||||
* ever have nil unjoined_relids, so I think this code is overly
|
||||
* complex. In fact it seems wrong; shouldn't we be looking for
|
||||
* rels with complete relids lists??? Seems like a cartesian-product
|
||||
* case could fail because sub-relations could have nil JoinInfo lists.
|
||||
* Doesn't actually fail but I don't really understand why...)
|
||||
*/
|
||||
foreach(xrel, join_rel_list)
|
||||
{
|
||||
@@ -404,26 +425,24 @@ get_cheapest_complete_rel(List *join_rel_list)
|
||||
}
|
||||
|
||||
static void
|
||||
set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *jinfo)
|
||||
set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel)
|
||||
{
|
||||
double dtuples;
|
||||
int ntuples;
|
||||
float selec;
|
||||
|
||||
/*
|
||||
* voodoo magic. but better than a size of 0. I have no idea why we
|
||||
* didn't set the size before. -ay 2/95
|
||||
/* avoid overflow ... probably, tuple estimates in RelOptInfo
|
||||
* just ought to be double ...
|
||||
*/
|
||||
if (jinfo == NULL)
|
||||
{
|
||||
/* worst case: the cartesian product */
|
||||
ntuples = outer_rel->tuples * inner_rel->tuples;
|
||||
}
|
||||
dtuples = (double) outer_rel->tuples * (double) inner_rel->tuples;
|
||||
|
||||
if (joinrel->restrictinfo != NULL)
|
||||
dtuples *= product_selec(joinrel->restrictinfo);
|
||||
|
||||
if (dtuples >= MAXINT) /* avoid overflow */
|
||||
ntuples = MAXINT;
|
||||
else
|
||||
{
|
||||
selec = product_selec(jinfo->jinfo_restrictinfo);
|
||||
/* ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */
|
||||
ntuples = outer_rel->tuples * inner_rel->tuples * selec;
|
||||
}
|
||||
ntuples = (int) dtuples;
|
||||
|
||||
/*
|
||||
* I bet sizes less than 1 will screw up optimization so make the best
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* mergeutils.c
|
||||
* Utilities for finding applicable merge clauses and pathkeys
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.24 1999/07/16 04:59:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/ordering.h"
|
||||
#include "optimizer/paths.h"
|
||||
|
||||
/*
|
||||
* group_clauses_by_order
|
||||
* If a join clause node in 'restrictinfo_list' is mergejoinable, store
|
||||
* it within a mergeinfo node containing other clause nodes with the same
|
||||
* mergejoin ordering.
|
||||
*
|
||||
* XXX This is completely braindead: there is no reason anymore to segregate
|
||||
* mergejoin clauses by join operator, since the executor can handle mergejoin
|
||||
* clause sets with different operators in them. Instead, we ought to be
|
||||
* building a MergeInfo for each potentially useful ordering of the input
|
||||
* relations. But right now the optimizer's internal data structures do not
|
||||
* support that (MergeInfo can only store one MergeOrder for a set of clauses).
|
||||
* Something to fix next time...
|
||||
*
|
||||
* 'restrictinfo_list' is the list of restrictinfo nodes
|
||||
* 'inner_relids' is the list of relids in the inner join relation
|
||||
* (used to determine whether a join var is inner or outer)
|
||||
*
|
||||
* Returns the new list of mergeinfo nodes.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
group_clauses_by_order(List *restrictinfo_list,
|
||||
Relids inner_relids)
|
||||
{
|
||||
List *mergeinfo_list = NIL;
|
||||
List *xrestrictinfo;
|
||||
|
||||
foreach(xrestrictinfo, restrictinfo_list)
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(xrestrictinfo);
|
||||
MergeOrder *merge_ordering = restrictinfo->mergejoinorder;
|
||||
|
||||
if (merge_ordering)
|
||||
{
|
||||
|
||||
/*
|
||||
* Create a new mergeinfo node and add it to 'mergeinfo_list'
|
||||
* if one does not yet exist for this merge ordering.
|
||||
*/
|
||||
Expr *clause = restrictinfo->clause;
|
||||
Var *leftop = get_leftop(clause);
|
||||
Var *rightop = get_rightop(clause);
|
||||
PathOrder *pathorder;
|
||||
MergeInfo *xmergeinfo;
|
||||
JoinKey *jmkeys;
|
||||
|
||||
pathorder = makeNode(PathOrder);
|
||||
pathorder->ordtype = MERGE_ORDER;
|
||||
pathorder->ord.merge = merge_ordering;
|
||||
xmergeinfo = match_order_mergeinfo(pathorder, mergeinfo_list);
|
||||
jmkeys = makeNode(JoinKey);
|
||||
if (intMember(leftop->varno, inner_relids))
|
||||
{
|
||||
jmkeys->outer = rightop;
|
||||
jmkeys->inner = leftop;
|
||||
}
|
||||
else
|
||||
{
|
||||
jmkeys->outer = leftop;
|
||||
jmkeys->inner = rightop;
|
||||
}
|
||||
|
||||
if (xmergeinfo == NULL)
|
||||
{
|
||||
xmergeinfo = makeNode(MergeInfo);
|
||||
xmergeinfo->m_ordering = merge_ordering;
|
||||
mergeinfo_list = lcons(xmergeinfo, mergeinfo_list);
|
||||
}
|
||||
|
||||
xmergeinfo->jmethod.clauses = lcons(clause,
|
||||
xmergeinfo->jmethod.clauses);
|
||||
xmergeinfo->jmethod.jmkeys = lcons(jmkeys,
|
||||
xmergeinfo->jmethod.jmkeys);
|
||||
}
|
||||
}
|
||||
return mergeinfo_list;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* match_order_mergeinfo
|
||||
* Searches the list 'mergeinfo_list' for a mergeinfo node whose order
|
||||
* field equals 'ordering'.
|
||||
*
|
||||
* Returns the node if it exists.
|
||||
*
|
||||
*/
|
||||
MergeInfo *
|
||||
match_order_mergeinfo(PathOrder *ordering, List *mergeinfo_list)
|
||||
{
|
||||
MergeOrder *xmergeorder;
|
||||
List *xmergeinfo = NIL;
|
||||
|
||||
foreach(xmergeinfo, mergeinfo_list)
|
||||
{
|
||||
MergeInfo *mergeinfo = (MergeInfo *) lfirst(xmergeinfo);
|
||||
|
||||
xmergeorder = mergeinfo->m_ordering;
|
||||
|
||||
if ((ordering->ordtype == MERGE_ORDER &&
|
||||
equal_merge_ordering(ordering->ord.merge, xmergeorder)) ||
|
||||
(ordering->ordtype == SORTOP_ORDER &&
|
||||
equal_path_merge_ordering(ordering->ord.sortop, xmergeorder)))
|
||||
{
|
||||
|
||||
return mergeinfo;
|
||||
}
|
||||
}
|
||||
return (MergeInfo *) NIL;
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.31 1999/07/27 03:51:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.32 1999/08/16 02:17:52 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -66,12 +66,12 @@ create_or_index_paths(Query *root,
|
||||
* saved by create_index_paths().
|
||||
*/
|
||||
if (restriction_is_or_clause(clausenode) &&
|
||||
clausenode->indexids)
|
||||
clausenode->subclauseindices)
|
||||
{
|
||||
bool all_indexable = true;
|
||||
List *temp;
|
||||
|
||||
foreach(temp, clausenode->indexids)
|
||||
foreach(temp, clausenode->subclauseindices)
|
||||
{
|
||||
if (lfirst(temp) == NIL)
|
||||
{
|
||||
@@ -94,7 +94,7 @@ create_or_index_paths(Query *root,
|
||||
best_or_subclause_indices(root,
|
||||
rel,
|
||||
clausenode->clause->args,
|
||||
clausenode->indexids,
|
||||
clausenode->subclauseindices,
|
||||
&indexquals,
|
||||
&indexids,
|
||||
&cost,
|
||||
@@ -102,20 +102,17 @@ create_or_index_paths(Query *root,
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.pathorder = makeNode(PathOrder);
|
||||
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
|
||||
|
||||
/*
|
||||
* This is an IndexScan, but the overall result will consist
|
||||
* of tuples extracted in multiple passes (one for each
|
||||
* subclause of the OR), so the result cannot be claimed
|
||||
* to have any particular ordering.
|
||||
*/
|
||||
pathnode->path.pathorder->ord.sortop = NULL;
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
pathnode->indexqual = indexquals;
|
||||
pathnode->indexid = indexids;
|
||||
pathnode->indexqual = indexquals;
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->path.path_cost = cost;
|
||||
clausenode->selectivity = (Cost) selec;
|
||||
|
||||
|
||||
@@ -1,70 +1,84 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* joinutils.c
|
||||
* Utilities for matching and building join and path keys
|
||||
* pathkeys.c
|
||||
* Utilities for matching and building path keys
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.13 1999/08/13 01:17:16 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.14 1999/08/16 02:17:52 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/joininfo.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/ordering.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static int match_pathkey_joinkeys(List *pathkey, List *joinkeys,
|
||||
int outer_or_inner);
|
||||
static List *new_join_pathkey(List *pathkeys, List *join_rel_tlist,
|
||||
List *joinclauses);
|
||||
static PathKeyItem *makePathKeyItem(Node *key, Oid sortop);
|
||||
static bool pathkeyitem_equal(PathKeyItem *a, PathKeyItem *b);
|
||||
static bool pathkeyitem_member(PathKeyItem *a, List *l);
|
||||
static Var *find_indexkey_var(int indexkey, List *tlist);
|
||||
static List *build_join_pathkey(List *pathkeys, List *join_rel_tlist,
|
||||
List *joinclauses);
|
||||
|
||||
|
||||
/*--------------------
|
||||
* Explanation of Path.pathkeys
|
||||
*
|
||||
* Path.pathkeys is a List of List of Var nodes that represent the sort
|
||||
* order of the result generated by the Path.
|
||||
* Path.pathkeys is a List of Lists of PathKeyItem nodes that represent
|
||||
* the sort order of the result generated by the Path. The n'th sublist
|
||||
* represents the n'th sort key of the result.
|
||||
*
|
||||
* In single/base relation RelOptInfo's, the Path's represent various ways
|
||||
* In single/base relation RelOptInfo's, the Paths represent various ways
|
||||
* of scanning the relation and the resulting ordering of the tuples.
|
||||
* Sequential scan Paths have NIL pathkeys, indicating no known ordering.
|
||||
* Index scans have Path.pathkeys that represent the chosen index's ordering,
|
||||
* if any. A single-key index would create a pathkey with a single sublist,
|
||||
* e.g. ( (tab1_indexkey1) ). A multi-key index generates a sublist per key,
|
||||
* e.g. ( (tab1_indexkey1) (tab1_indexkey2) ) which shows major sort by
|
||||
* indexkey1 and minor sort by indexkey2.
|
||||
* e.g. ( (tab1.indexkey1/sortop1) ). A multi-key index generates a sublist
|
||||
* per key, e.g. ( (tab1.indexkey1/sortop1) (tab1.indexkey2/sortop2) ) which
|
||||
* shows major sort by indexkey1 (ordering by sortop1) and minor sort by
|
||||
* indexkey2 with sortop2.
|
||||
*
|
||||
* Note that a multi-pass indexscan (OR clause scan) has NIL pathkeys since
|
||||
* we can say nothing about the overall order of its result. Also, an index
|
||||
* scan on an unordered type of index generates no useful pathkeys. However,
|
||||
* we can say nothing about the overall order of its result. Also, an
|
||||
* indexscan on an unordered type of index generates NIL pathkeys. However,
|
||||
* we can always create a pathkey by doing an explicit sort.
|
||||
*
|
||||
* Multi-relation RelOptInfo Path's are more complicated. Mergejoins are
|
||||
* only performed with equijoins ("="). Because of this, the multi-relation
|
||||
* path actually has more than one primary Var key. For example, a
|
||||
* mergejoin Path of "tab1.col1 = tab2.col1" would generate pathkeys of
|
||||
* ( (tab1.col1 tab2.col1) ), indicating that the major sort order of the
|
||||
* Path can be taken to be *either* tab1.col1 or tab2.col1.
|
||||
* only performed with equijoins ("="). Because of this, the resulting
|
||||
* multi-relation path actually has more than one primary key. For example,
|
||||
* a mergejoin using a clause "tab1.col1 = tab2.col1" would generate pathkeys
|
||||
* of ( (tab1.col1/sortop1 tab2.col1/sortop2) ), indicating that the major
|
||||
* sort order of the Path can be taken to be *either* tab1.col1 or tab2.col1.
|
||||
* They are equal, so they are both primary sort keys. This allows future
|
||||
* joins to use either Var as a pre-sorted key to prevent upper Mergejoins
|
||||
* joins to use either var as a pre-sorted key to prevent upper Mergejoins
|
||||
* from having to re-sort the Path. This is why pathkeys is a List of Lists.
|
||||
*
|
||||
* Note that while the order of the top list is meaningful (primary vs.
|
||||
* secondary sort key), the order of each sublist is arbitrary.
|
||||
* secondary sort key), the order of each sublist is arbitrary. No code
|
||||
* working with pathkeys should generate a result that depends on the order
|
||||
* of a pathkey sublist.
|
||||
*
|
||||
* We can actually keep all of the keys of the outer path of a merge or
|
||||
* nestloop join, since the ordering of the outer path will be reflected
|
||||
* in the result. We add to each pathkey sublist any inner vars that are
|
||||
* equijoined to any of the outer vars in the sublist. In the nestloop
|
||||
* case we have to be careful to consider only equijoin operators; the
|
||||
* nestloop's join clauses might include non-equijoin operators.
|
||||
* We keep a sortop associated with each PathKeyItem because cross-data-type
|
||||
* mergejoins are possible; for example int4=int8 is mergejoinable. In this
|
||||
* case we need to remember that the left var is ordered by int4lt while
|
||||
* the right var is ordered by int8lt. So the different members of each
|
||||
* sublist could have different sortops.
|
||||
*
|
||||
* When producing the pathkeys for a merge or nestloop join, we can keep
|
||||
* all of the keys of the outer path, since the ordering of the outer path
|
||||
* will be preserved in the result. We add to each pathkey sublist any inner
|
||||
* vars that are equijoined to any of the outer vars in the sublist. In the
|
||||
* nestloop case we have to be careful to consider only equijoin operators;
|
||||
* the nestloop's join clauses might include non-equijoin operators.
|
||||
* (Currently, we do this by considering only mergejoinable operators while
|
||||
* making the pathkeys, since we have no separate marking for operators that
|
||||
* are equijoins but aren't mergejoinable.)
|
||||
@@ -75,180 +89,174 @@ static List *new_join_pathkey(List *pathkeys, List *join_rel_tlist,
|
||||
* executor might have to split the join into multiple batches. Therefore
|
||||
* a Hashjoin is always given NIL pathkeys.
|
||||
*
|
||||
* Notice that pathkeys only say *what* is being ordered, and not *how*
|
||||
* it is ordered. The actual sort ordering is indicated by a separate
|
||||
* data structure, the PathOrder. The PathOrder provides a sort operator
|
||||
* OID for each of the sublists of the path key. This is fairly bogus,
|
||||
* since in cross-datatype cases we really want to keep track of more than
|
||||
* one sort operator...
|
||||
*
|
||||
* -- bjm & tgl
|
||||
*--------------------
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* makePathKeyItem
|
||||
* create a PathKeyItem node
|
||||
*/
|
||||
static PathKeyItem *
|
||||
makePathKeyItem(Node *key, Oid sortop)
|
||||
{
|
||||
PathKeyItem *item = makeNode(PathKeyItem);
|
||||
|
||||
item->key = key;
|
||||
item->sortop = sortop;
|
||||
return item;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* KEY COMPARISONS
|
||||
* PATHKEY COMPARISONS
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* order_joinkeys_by_pathkeys
|
||||
* Attempts to match the keys of a path against the keys of join clauses.
|
||||
* This is done by looking for a matching join key in 'joinkeys' for
|
||||
* every path key in the list 'path.keys'. If there is a matching join key
|
||||
* (not necessarily unique) for every path key, then the list of
|
||||
* corresponding join keys and join clauses are returned in the order in
|
||||
* which the keys matched the path keys.
|
||||
* Compare two pathkey items for equality.
|
||||
*
|
||||
* 'pathkeys' is a list of path keys:
|
||||
* ( ( (var) (var) ... ) ( (var) ... ) )
|
||||
* 'joinkeys' is a list of join keys:
|
||||
* ( (outer inner) (outer inner) ... )
|
||||
* 'joinclauses' is a list of clauses corresponding to the join keys in
|
||||
* 'joinkeys'
|
||||
* 'outer_or_inner' is a flag that selects the desired pathkey of a join key
|
||||
* in 'joinkeys'
|
||||
* This is unlike straight equal() because when the two keys are both Vars,
|
||||
* we want to apply the weaker var_equal() condition (doesn't check varnoold
|
||||
* or varoattno). But if that fails, try equal() so that we recognize
|
||||
* functional-index keys.
|
||||
*/
|
||||
static bool
|
||||
pathkeyitem_equal (PathKeyItem *a, PathKeyItem *b)
|
||||
{
|
||||
Assert(a && IsA(a, PathKeyItem));
|
||||
Assert(b && IsA(b, PathKeyItem));
|
||||
|
||||
if (a->sortop != b->sortop)
|
||||
return false;
|
||||
if (var_equal((Var *) a->key, (Var *) b->key))
|
||||
return true;
|
||||
return equal(a->key, b->key);
|
||||
}
|
||||
|
||||
/*
|
||||
* member() test using pathkeyitem_equal
|
||||
*/
|
||||
static bool
|
||||
pathkeyitem_member (PathKeyItem *a, List *l)
|
||||
{
|
||||
List *i;
|
||||
|
||||
Assert(a && IsA(a, PathKeyItem));
|
||||
|
||||
foreach(i, l)
|
||||
{
|
||||
if (pathkeyitem_equal(a, (PathKeyItem *) lfirst(i)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* compare_pathkeys
|
||||
* Compare two pathkeys to see if they are equivalent, and if not whether
|
||||
* one is "better" than the other.
|
||||
*
|
||||
* Returns the join keys and corresponding join clauses in a list if all
|
||||
* of the path keys were matched:
|
||||
* (
|
||||
* ( (outerkey0 innerkey0) ... (outerkeyN or innerkeyN) )
|
||||
* ( clause0 ... clauseN )
|
||||
* )
|
||||
* and nil otherwise.
|
||||
* A pathkey can be considered better than another if it is a superset:
|
||||
* it contains all the keys of the other plus more. For example, either
|
||||
* ((A) (B)) or ((A B)) is better than ((A)).
|
||||
*
|
||||
* Returns a list of matched join keys and a list of matched join clauses
|
||||
* in pointers if valid order can be found.
|
||||
* This gets called a lot, so it is optimized.
|
||||
*/
|
||||
PathKeysComparison
|
||||
compare_pathkeys(List *keys1, List *keys2)
|
||||
{
|
||||
List *key1,
|
||||
*key2;
|
||||
bool key1_subsetof_key2 = true,
|
||||
key2_subsetof_key1 = true;
|
||||
|
||||
for (key1 = keys1, key2 = keys2;
|
||||
key1 != NIL && key2 != NIL;
|
||||
key1 = lnext(key1), key2 = lnext(key2))
|
||||
{
|
||||
List *subkey1 = lfirst(key1);
|
||||
List *subkey2 = lfirst(key2);
|
||||
List *i;
|
||||
|
||||
/* We have to do this the hard way since the ordering of the subkey
|
||||
* lists is arbitrary.
|
||||
*/
|
||||
if (key1_subsetof_key2)
|
||||
{
|
||||
foreach(i, subkey1)
|
||||
{
|
||||
if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey2))
|
||||
{
|
||||
key1_subsetof_key2 = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key2_subsetof_key1)
|
||||
{
|
||||
foreach(i, subkey2)
|
||||
{
|
||||
if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey1))
|
||||
{
|
||||
key2_subsetof_key1 = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!key1_subsetof_key2 && !key2_subsetof_key1)
|
||||
return PATHKEYS_DIFFERENT; /* no need to keep looking */
|
||||
}
|
||||
|
||||
/* If we reached the end of only one list, the other is longer and
|
||||
* therefore not a subset. (We assume the additional sublist(s)
|
||||
* of the other list are not NIL --- no pathkey list should ever have
|
||||
* a NIL sublist.)
|
||||
*/
|
||||
if (key1 != NIL)
|
||||
key1_subsetof_key2 = false;
|
||||
if (key2 != NIL)
|
||||
key2_subsetof_key1 = false;
|
||||
|
||||
if (key1_subsetof_key2 && key2_subsetof_key1)
|
||||
return PATHKEYS_EQUAL;
|
||||
if (key1_subsetof_key2)
|
||||
return PATHKEYS_BETTER2;
|
||||
if (key2_subsetof_key1)
|
||||
return PATHKEYS_BETTER1;
|
||||
return PATHKEYS_DIFFERENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* pathkeys_contained_in
|
||||
* Common special case of compare_pathkeys: we just want to know
|
||||
* if keys2 are at least as well sorted as keys1.
|
||||
*/
|
||||
bool
|
||||
order_joinkeys_by_pathkeys(List *pathkeys,
|
||||
List *joinkeys,
|
||||
List *joinclauses,
|
||||
int outer_or_inner,
|
||||
List **matchedJoinKeysPtr,
|
||||
List **matchedJoinClausesPtr)
|
||||
pathkeys_contained_in(List *keys1, List *keys2)
|
||||
{
|
||||
List *matched_joinkeys = NIL;
|
||||
List *matched_joinclauses = NIL;
|
||||
List *pathkey = NIL;
|
||||
List *i = NIL;
|
||||
int matched_joinkey_index = -1;
|
||||
int matched_keys = 0;
|
||||
|
||||
/*
|
||||
* Reorder the joinkeys by picking out one that matches each pathkey,
|
||||
* and create a new joinkey/joinclause list in pathkey order
|
||||
*/
|
||||
foreach(i, pathkeys)
|
||||
switch (compare_pathkeys(keys1, keys2))
|
||||
{
|
||||
pathkey = lfirst(i);
|
||||
matched_joinkey_index = match_pathkey_joinkeys(pathkey, joinkeys,
|
||||
outer_or_inner);
|
||||
|
||||
if (matched_joinkey_index != -1)
|
||||
{
|
||||
matched_keys++;
|
||||
if (matchedJoinKeysPtr)
|
||||
{
|
||||
JoinKey *joinkey = nth(matched_joinkey_index, joinkeys);
|
||||
|
||||
matched_joinkeys = lappend(matched_joinkeys, joinkey);
|
||||
}
|
||||
|
||||
if (matchedJoinClausesPtr)
|
||||
{
|
||||
Expr *joinclause = nth(matched_joinkey_index,
|
||||
joinclauses);
|
||||
|
||||
Assert(joinclauses);
|
||||
matched_joinclauses = lappend(matched_joinclauses, joinclause);
|
||||
}
|
||||
}
|
||||
else
|
||||
/* A pathkey could not be matched. */
|
||||
case PATHKEYS_EQUAL:
|
||||
case PATHKEYS_BETTER2:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Did we fail to match all the joinkeys? Extra pathkeys are no
|
||||
* problem.
|
||||
*/
|
||||
if (matched_keys != length(joinkeys))
|
||||
{
|
||||
if (matchedJoinKeysPtr)
|
||||
*matchedJoinKeysPtr = NIL;
|
||||
if (matchedJoinClausesPtr)
|
||||
*matchedJoinClausesPtr = NIL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchedJoinKeysPtr)
|
||||
*matchedJoinKeysPtr = matched_joinkeys;
|
||||
if (matchedJoinClausesPtr)
|
||||
*matchedJoinClausesPtr = matched_joinclauses;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* match_pathkey_joinkeys
|
||||
* Returns the 0-based index into 'joinkeys' of the first joinkey whose
|
||||
* outer or inner pathkey matches any subkey of 'pathkey'.
|
||||
* get_cheapest_path_for_pathkeys
|
||||
* Find the cheapest path in 'paths' that satisfies the given pathkeys.
|
||||
* Return NULL if no such path.
|
||||
*
|
||||
* All these keys are equivalent, so any of them can match. See above.
|
||||
*/
|
||||
static int
|
||||
match_pathkey_joinkeys(List *pathkey,
|
||||
List *joinkeys,
|
||||
int outer_or_inner)
|
||||
{
|
||||
Var *key;
|
||||
int pos;
|
||||
List *i,
|
||||
*x;
|
||||
JoinKey *jk;
|
||||
|
||||
foreach(i, pathkey)
|
||||
{
|
||||
key = (Var *) lfirst(i);
|
||||
pos = 0;
|
||||
foreach(x, joinkeys)
|
||||
{
|
||||
jk = (JoinKey *) lfirst(x);
|
||||
if (equal(key, extract_join_key(jk, outer_or_inner)))
|
||||
return pos;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return -1; /* no index found */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get_cheapest_path_for_joinkeys
|
||||
* Attempts to find a path in 'paths' whose keys match a set of join
|
||||
* keys 'joinkeys'. To match,
|
||||
* 1. the path node ordering must equal 'ordering'.
|
||||
* 2. each pathkey of a given path must match(i.e., be(equal) to) the
|
||||
* appropriate pathkey of the corresponding join key in 'joinkeys',
|
||||
* i.e., the Nth path key must match its pathkeys against the pathkey of
|
||||
* the Nth join key in 'joinkeys'.
|
||||
*
|
||||
* 'joinkeys' is the list of key pairs to which the path keys must be
|
||||
* matched
|
||||
* 'ordering' is the ordering of the(outer) path to which 'joinkeys'
|
||||
* must correspond
|
||||
* 'paths' is a list of(inner) paths which are to be matched against
|
||||
* each join key in 'joinkeys'
|
||||
* 'outer_or_inner' is a flag that selects the desired pathkey of a join key
|
||||
* in 'joinkeys'
|
||||
*
|
||||
* Find the cheapest path that matches the join keys
|
||||
* 'paths' is a list of possible paths (either inner or outer)
|
||||
* 'pathkeys' represents a required ordering
|
||||
*/
|
||||
Path *
|
||||
get_cheapest_path_for_joinkeys(List *joinkeys,
|
||||
PathOrder *ordering,
|
||||
List *paths,
|
||||
int outer_or_inner)
|
||||
get_cheapest_path_for_pathkeys(List *paths, List *pathkeys)
|
||||
{
|
||||
Path *matched_path = NULL;
|
||||
List *i;
|
||||
@@ -256,12 +264,8 @@ get_cheapest_path_for_joinkeys(List *joinkeys,
|
||||
foreach(i, paths)
|
||||
{
|
||||
Path *path = (Path *) lfirst(i);
|
||||
int better_sort;
|
||||
|
||||
if (order_joinkeys_by_pathkeys(path->pathkeys, joinkeys, NIL,
|
||||
outer_or_inner, NULL, NULL) &&
|
||||
pathorder_match(ordering, path->pathorder, &better_sort) &&
|
||||
better_sort == 0)
|
||||
if (pathkeys_contained_in(pathkeys, path->pathkeys))
|
||||
{
|
||||
if (matched_path == NULL ||
|
||||
path->path_cost < matched_path->path_cost)
|
||||
@@ -271,78 +275,116 @@ get_cheapest_path_for_joinkeys(List *joinkeys,
|
||||
return matched_path;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_pathkeys_from_joinkeys
|
||||
* Builds a pathkey list for a path by pulling one of the pathkeys from
|
||||
* a list of join keys 'joinkeys' and then finding the var node in the
|
||||
* target list 'tlist' that corresponds to that pathkey.
|
||||
*
|
||||
* 'joinkeys' is a list of join key pairs
|
||||
* 'tlist' is a relation target list
|
||||
* 'outer_or_inner' is a flag that selects the desired pathkey of a join key
|
||||
* in 'joinkeys'
|
||||
*
|
||||
* Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)).
|
||||
* It is a list of lists because of multi-key indexes.
|
||||
*/
|
||||
List *
|
||||
make_pathkeys_from_joinkeys(List *joinkeys,
|
||||
List *tlist,
|
||||
int outer_or_inner)
|
||||
{
|
||||
List *pathkeys = NIL;
|
||||
List *jk;
|
||||
|
||||
foreach(jk, joinkeys)
|
||||
{
|
||||
JoinKey *jkey = (JoinKey *) lfirst(jk);
|
||||
Var *key;
|
||||
List *p,
|
||||
*p2;
|
||||
bool found = false;
|
||||
|
||||
key = (Var *) extract_join_key(jkey, outer_or_inner);
|
||||
|
||||
/* check to see if it is in the target list */
|
||||
if (matching_tlist_var(key, tlist))
|
||||
{
|
||||
|
||||
/*
|
||||
* Include it in the pathkeys list if we haven't already done
|
||||
* so
|
||||
*/
|
||||
foreach(p, pathkeys)
|
||||
{
|
||||
List *pathkey = lfirst(p);
|
||||
|
||||
foreach(p2, pathkey)
|
||||
{
|
||||
Var *pkey = lfirst(p2);
|
||||
|
||||
if (equal(key, pkey))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
pathkeys = lappend(pathkeys, lcons(key, NIL));
|
||||
}
|
||||
}
|
||||
return pathkeys;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* NEW PATHKEY FORMATION
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* new_join_pathkeys
|
||||
* build_index_pathkeys
|
||||
* Build a pathkeys list that describes the ordering induced by an index
|
||||
* scan using the given index. (Note that an unordered index doesn't
|
||||
* induce any ordering; such an index will have no sortop OIDS in
|
||||
* its "ordering" field.)
|
||||
*
|
||||
* Vars in the resulting pathkeys list are taken from the rel's targetlist.
|
||||
* If we can't find the indexkey in the targetlist, we assume that the
|
||||
* ordering of that key is not interesting.
|
||||
*/
|
||||
List *
|
||||
build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index)
|
||||
{
|
||||
List *retval = NIL;
|
||||
int *indexkeys = index->indexkeys;
|
||||
Oid *ordering = index->ordering;
|
||||
|
||||
if (!indexkeys || indexkeys[0] == 0 ||
|
||||
!ordering || ordering[0] == InvalidOid)
|
||||
return NIL; /* unordered index? */
|
||||
|
||||
if (index->indproc)
|
||||
{
|
||||
/* Functional index: build a representation of the function call */
|
||||
int relid = lfirsti(rel->relids);
|
||||
Oid reloid = getrelid(relid, root->rtable);
|
||||
Func *funcnode = makeNode(Func);
|
||||
List *funcargs = NIL;
|
||||
|
||||
funcnode->funcid = index->indproc;
|
||||
funcnode->functype = get_func_rettype(index->indproc);
|
||||
funcnode->funcisindex = false;
|
||||
funcnode->funcsize = 0;
|
||||
funcnode->func_fcache = NULL;
|
||||
funcnode->func_tlist = NIL;
|
||||
funcnode->func_planlist = NIL;
|
||||
|
||||
while (*indexkeys != 0)
|
||||
{
|
||||
int varattno = *indexkeys;
|
||||
Oid vartypeid = get_atttype(reloid, varattno);
|
||||
int32 type_mod = get_atttypmod(reloid, varattno);
|
||||
|
||||
funcargs = lappend(funcargs,
|
||||
makeVar(relid, varattno, vartypeid, type_mod,
|
||||
0, relid, varattno));
|
||||
indexkeys++;
|
||||
}
|
||||
|
||||
/* Make a one-sublist pathkeys list for the function expression */
|
||||
retval = lcons(lcons(
|
||||
makePathKeyItem((Node *) make_funcclause(funcnode, funcargs),
|
||||
*ordering),
|
||||
NIL), NIL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Normal non-functional index */
|
||||
List *rel_tlist = rel->targetlist;
|
||||
|
||||
while (*indexkeys != 0 && *ordering != InvalidOid)
|
||||
{
|
||||
Var *relvar = find_indexkey_var(*indexkeys, rel_tlist);
|
||||
|
||||
/* If we can find no tlist entry for the n'th sort key,
|
||||
* then we're done generating pathkeys; any subsequent sort keys
|
||||
* no longer apply, since we can't represent the ordering properly
|
||||
* even if there are tlist entries for them.
|
||||
*/
|
||||
if (!relvar)
|
||||
break;
|
||||
/* OK, make a one-element sublist for this sort key */
|
||||
retval = lappend(retval,
|
||||
lcons(makePathKeyItem((Node *) relvar,
|
||||
*ordering),
|
||||
NIL));
|
||||
|
||||
indexkeys++;
|
||||
ordering++;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a var in a relation's targetlist that matches an indexkey attrnum.
|
||||
*/
|
||||
static Var *
|
||||
find_indexkey_var(int indexkey, List *tlist)
|
||||
{
|
||||
List *temp;
|
||||
|
||||
foreach(temp, tlist)
|
||||
{
|
||||
Var *tle_var = get_expr(lfirst(temp));
|
||||
|
||||
if (IsA(tle_var, Var) && tle_var->varattno == indexkey)
|
||||
return tle_var;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* build_join_pathkeys
|
||||
* Build the path keys for a join relation constructed by mergejoin or
|
||||
* nestloop join. These keys should include all the path key vars of the
|
||||
* outer path (since the join will retain the ordering of the outer path)
|
||||
@@ -362,21 +404,26 @@ make_pathkeys_from_joinkeys(List *joinkeys,
|
||||
* the inner var will acquire the outer's ordering no matter which join
|
||||
* method is actually used.
|
||||
*
|
||||
* All vars in the result are copied from the join relation's tlist, not from
|
||||
* the given pathkeys or the join clauses. (Is that necessary? I suspect
|
||||
* not --- tgl)
|
||||
* We drop pathkeys that are not vars of the join relation's tlist,
|
||||
* on the assumption that they are not interesting to higher levels.
|
||||
* (Is this correct?? To support expression pathkeys we might want to
|
||||
* check that all vars mentioned in the key are in the tlist, instead.)
|
||||
*
|
||||
* All vars in the result are taken from the join relation's tlist,
|
||||
* not from the given pathkeys or joinclauses.
|
||||
*
|
||||
* 'outer_pathkeys' is the list of the outer path's path keys
|
||||
* 'join_rel_tlist' is the target list of the join relation
|
||||
* 'joinclauses' is the list of mergejoinable join clauses
|
||||
* 'joinclauses' is the list of mergejoinable clauses to consider (note this
|
||||
* is a list of RestrictInfos, not just bare qual clauses); can be NIL
|
||||
*
|
||||
* Returns the list of new path keys.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
new_join_pathkeys(List *outer_pathkeys,
|
||||
List *join_rel_tlist,
|
||||
List *joinclauses)
|
||||
build_join_pathkeys(List *outer_pathkeys,
|
||||
List *join_rel_tlist,
|
||||
List *joinclauses)
|
||||
{
|
||||
List *final_pathkeys = NIL;
|
||||
List *i;
|
||||
@@ -386,11 +433,11 @@ new_join_pathkeys(List *outer_pathkeys,
|
||||
List *outer_pathkey = lfirst(i);
|
||||
List *new_pathkey;
|
||||
|
||||
new_pathkey = new_join_pathkey(outer_pathkey, join_rel_tlist,
|
||||
joinclauses);
|
||||
new_pathkey = build_join_pathkey(outer_pathkey, join_rel_tlist,
|
||||
joinclauses);
|
||||
/* if we can find no sortable vars for the n'th sort key,
|
||||
* then we're done generating pathkeys; can't expect to order
|
||||
* subsequent vars. Not clear that this can really happen.
|
||||
* then we're done generating pathkeys; any subsequent sort keys
|
||||
* no longer apply, since we can't represent the ordering properly.
|
||||
*/
|
||||
if (new_pathkey == NIL)
|
||||
break;
|
||||
@@ -400,25 +447,22 @@ new_join_pathkeys(List *outer_pathkeys,
|
||||
}
|
||||
|
||||
/*
|
||||
* new_join_pathkey
|
||||
* build_join_pathkey
|
||||
* Generate an individual pathkey sublist, consisting of the outer vars
|
||||
* already mentioned in 'pathkey' plus any inner vars that are joined to
|
||||
* them (and thus can now also be considered path keys, per discussion
|
||||
* at the top of this file).
|
||||
*
|
||||
* Note that each returned pathkey is the var node found in
|
||||
* Note that each returned pathkey uses the var node found in
|
||||
* 'join_rel_tlist' rather than the input pathkey or joinclause var node.
|
||||
* (Is this important?) Also, we return a fully copied list
|
||||
* that does not share any subnodes with existing data structures.
|
||||
* (Is that important, either?)
|
||||
*
|
||||
* Returns a new pathkey (list of pathkey variables).
|
||||
* (Is this important?)
|
||||
*
|
||||
* Returns a new pathkey (list of PathKeyItems).
|
||||
*/
|
||||
static List *
|
||||
new_join_pathkey(List *pathkey,
|
||||
List *join_rel_tlist,
|
||||
List *joinclauses)
|
||||
build_join_pathkey(List *pathkey,
|
||||
List *join_rel_tlist,
|
||||
List *joinclauses)
|
||||
{
|
||||
List *new_pathkey = NIL;
|
||||
List *i,
|
||||
@@ -426,27 +470,193 @@ new_join_pathkey(List *pathkey,
|
||||
|
||||
foreach(i, pathkey)
|
||||
{
|
||||
Var *key = (Var *) lfirst(i);
|
||||
PathKeyItem *key = (PathKeyItem *) lfirst(i);
|
||||
Expr *tlist_key;
|
||||
|
||||
Assert(key);
|
||||
Assert(key && IsA(key, PathKeyItem));
|
||||
|
||||
tlist_key = matching_tlist_var(key, join_rel_tlist);
|
||||
if (tlist_key && !member(tlist_key, new_pathkey))
|
||||
new_pathkey = lcons(copyObject(tlist_key), new_pathkey);
|
||||
tlist_key = matching_tlist_var((Var *) key->key, join_rel_tlist);
|
||||
if (tlist_key)
|
||||
new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
|
||||
key->sortop),
|
||||
new_pathkey);
|
||||
|
||||
foreach(j, joinclauses)
|
||||
{
|
||||
Expr *joinclause = lfirst(j);
|
||||
Expr *tlist_other_var;
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
|
||||
Expr *joinclause = restrictinfo->clause;
|
||||
/* We assume the clause is a binary opclause... */
|
||||
Var *l = get_leftop(joinclause);
|
||||
Var *r = get_rightop(joinclause);
|
||||
Var *other_var = NULL;
|
||||
Oid other_sortop = InvalidOid;
|
||||
|
||||
tlist_other_var = matching_tlist_var(
|
||||
other_join_clause_var(key, joinclause),
|
||||
join_rel_tlist);
|
||||
if (tlist_other_var && !member(tlist_other_var, new_pathkey))
|
||||
new_pathkey = lcons(copyObject(tlist_other_var), new_pathkey);
|
||||
if (var_equal((Var *) key->key, l))
|
||||
{
|
||||
other_var = r;
|
||||
other_sortop = restrictinfo->right_sortop;
|
||||
}
|
||||
else if (var_equal((Var *) key->key, r))
|
||||
{
|
||||
other_var = l;
|
||||
other_sortop = restrictinfo->left_sortop;
|
||||
}
|
||||
|
||||
if (other_var && other_sortop)
|
||||
{
|
||||
tlist_key = matching_tlist_var(other_var, join_rel_tlist);
|
||||
if (tlist_key)
|
||||
new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
|
||||
other_sortop),
|
||||
new_pathkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new_pathkey;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* PATHKEYS AND MERGECLAUSES
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* find_mergeclauses_for_pathkeys
|
||||
* This routine attempts to find a set of mergeclauses that can be
|
||||
* used with a specified ordering for one of the input relations.
|
||||
* If successful, it returns a list of mergeclauses.
|
||||
*
|
||||
* 'pathkeys' is a pathkeys list showing the ordering of an input path.
|
||||
* It doesn't matter whether it is for the inner or outer path.
|
||||
* 'restrictinfos' is a list of mergejoinable restriction clauses for the
|
||||
* join relation being formed.
|
||||
*
|
||||
* The result is NIL if no merge can be done, else a maximal list of
|
||||
* usable mergeclauses (represented as a list of their restrictinfo nodes).
|
||||
*
|
||||
* XXX Ideally we ought to be considering context, ie what path orderings
|
||||
* are available on the other side of the join, rather than just making
|
||||
* an arbitrary choice among the mergeclause orders that will work for
|
||||
* this side of the join.
|
||||
*/
|
||||
List *
|
||||
find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
|
||||
{
|
||||
List *mergeclauses = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, pathkeys)
|
||||
{
|
||||
List *pathkey = lfirst(i);
|
||||
RestrictInfo *matched_restrictinfo = NULL;
|
||||
List *j;
|
||||
|
||||
/*
|
||||
* We can match any of the keys in this pathkey sublist,
|
||||
* since they're all equivalent. And we can match against
|
||||
* either left or right side of any mergejoin clause we haven't
|
||||
* used yet. For the moment we use a dumb "greedy" algorithm
|
||||
* with no backtracking. Is it worth being any smarter to
|
||||
* make a longer list of usable mergeclauses? Probably not.
|
||||
*/
|
||||
foreach(j, pathkey)
|
||||
{
|
||||
PathKeyItem *keyitem = lfirst(j);
|
||||
Var *keyvar = (Var *) keyitem->key;
|
||||
List *k;
|
||||
|
||||
if (! IsA(keyvar, Var))
|
||||
continue; /* for now, only Vars can be mergejoined */
|
||||
|
||||
foreach(k, restrictinfos)
|
||||
{
|
||||
RestrictInfo *restrictinfo = lfirst(k);
|
||||
|
||||
Assert(restrictinfo->mergejoinoperator != InvalidOid);
|
||||
|
||||
if ((var_equal(keyvar, get_leftop(restrictinfo->clause)) ||
|
||||
var_equal(keyvar, get_rightop(restrictinfo->clause))) &&
|
||||
! member(restrictinfo, mergeclauses))
|
||||
{
|
||||
matched_restrictinfo = restrictinfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched_restrictinfo)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we didn't find a mergeclause, we're done --- any additional
|
||||
* sort-key positions in the pathkeys are useless. (But we can
|
||||
* still mergejoin if we found at least one mergeclause.)
|
||||
*/
|
||||
if (! matched_restrictinfo)
|
||||
break;
|
||||
/*
|
||||
* If we did find a usable mergeclause for this sort-key position,
|
||||
* add it to result list.
|
||||
*/
|
||||
mergeclauses = lappend(mergeclauses, matched_restrictinfo);
|
||||
}
|
||||
|
||||
return mergeclauses;
|
||||
}
|
||||
|
||||
/*
|
||||
* make_pathkeys_for_mergeclauses
|
||||
* Builds a pathkey list representing the explicit sort order that
|
||||
* must be applied to a path in order to make it usable for the
|
||||
* given mergeclauses.
|
||||
*
|
||||
* 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
|
||||
* that will be used in a merge join.
|
||||
* 'tlist' is a relation target list for either the inner or outer
|
||||
* side of the proposed join rel.
|
||||
*
|
||||
* Returns a pathkeys list that can be applied to the indicated relation.
|
||||
*
|
||||
* Note that it is not this routine's job to decide whether sorting is
|
||||
* actually needed for a particular input path. Assume a sort is necessary;
|
||||
* just make the keys, eh?
|
||||
*/
|
||||
List *
|
||||
make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist)
|
||||
{
|
||||
List *pathkeys = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, mergeclauses)
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
|
||||
Var *key;
|
||||
Oid sortop;
|
||||
|
||||
/*
|
||||
* Find the key and sortop needed for this mergeclause.
|
||||
*
|
||||
* We can use either side of the mergeclause, since we haven't yet
|
||||
* committed to which side will be inner.
|
||||
*/
|
||||
Assert(restrictinfo->mergejoinoperator != InvalidOid);
|
||||
key = (Var *) matching_tlist_var(get_leftop(restrictinfo->clause),
|
||||
tlist);
|
||||
sortop = restrictinfo->left_sortop;
|
||||
if (! key)
|
||||
{
|
||||
key = (Var *) matching_tlist_var(get_rightop(restrictinfo->clause),
|
||||
tlist);
|
||||
sortop = restrictinfo->right_sortop;
|
||||
}
|
||||
if (! key)
|
||||
elog(ERROR, "make_pathkeys_for_mergeclauses: can't find key");
|
||||
/*
|
||||
* Add a pathkey sublist for this sort item
|
||||
*/
|
||||
pathkeys = lappend(pathkeys,
|
||||
lcons(makePathKeyItem((Node *) key, sortop),
|
||||
NIL));
|
||||
}
|
||||
|
||||
return pathkeys;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.42 1999/07/16 04:59:16 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.43 1999/08/16 02:17:52 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -18,17 +18,15 @@
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
|
||||
|
||||
|
||||
static List *merge_rel_with_same_relids(RelOptInfo *rel, Relids unjoined_relids);
|
||||
static List *merge_rel_with_same_relids(RelOptInfo *rel, List *unmerged_rels);
|
||||
|
||||
/*
|
||||
* merge_rels_with_same_relids
|
||||
* Removes any redundant relation entries from a list of rel nodes
|
||||
* 'rel_list'. Obviously, the first relation can't be a duplicate.
|
||||
* 'rel_list', merging their pathlists into the first non-duplicate
|
||||
* relation entry for each value of relids.
|
||||
*
|
||||
* Returns the resulting list.
|
||||
*
|
||||
*/
|
||||
void
|
||||
merge_rels_with_same_relids(List *rel_list)
|
||||
@@ -37,17 +35,21 @@ merge_rels_with_same_relids(List *rel_list)
|
||||
|
||||
/*
|
||||
* rel_list can shorten while running as duplicate relations are
|
||||
* deleted
|
||||
* deleted. Obviously, the first relation can't be a duplicate,
|
||||
* so the list head pointer won't change.
|
||||
*/
|
||||
foreach(i, rel_list)
|
||||
lnext(i) = merge_rel_with_same_relids((RelOptInfo *) lfirst(i), lnext(i));
|
||||
{
|
||||
lnext(i) = merge_rel_with_same_relids((RelOptInfo *) lfirst(i),
|
||||
lnext(i));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* merge_rel_with_same_relids
|
||||
* Prunes those relations from 'unjoined_relids' that are redundant with
|
||||
* Prunes those relations from 'unmerged_rels' that are redundant with
|
||||
* 'rel'. A relation is redundant if it is built up of the same
|
||||
* relations as 'rel'. Paths for the redundant relation are merged into
|
||||
* relations as 'rel'. Paths for the redundant relations are merged into
|
||||
* the pathlist of 'rel'.
|
||||
*
|
||||
* Returns a list of non-redundant relations, and sets the pathlist field
|
||||
@@ -55,50 +57,52 @@ merge_rels_with_same_relids(List *rel_list)
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
merge_rel_with_same_relids(RelOptInfo *rel, Relids unjoined_relids)
|
||||
merge_rel_with_same_relids(RelOptInfo *rel, List *unmerged_rels)
|
||||
{
|
||||
List *i = NIL;
|
||||
List *result = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, unjoined_relids)
|
||||
foreach(i, unmerged_rels)
|
||||
{
|
||||
RelOptInfo *unjoined_rel = (RelOptInfo *) lfirst(i);
|
||||
|
||||
if (same(rel->relids, unjoined_rel->relids))
|
||||
RelOptInfo *unmerged_rel = (RelOptInfo *) lfirst(i);
|
||||
|
||||
if (same(rel->relids, unmerged_rel->relids))
|
||||
{
|
||||
/*
|
||||
* This are on the same relations, so get the best of their
|
||||
* pathlists.
|
||||
* These rels are for the same set of base relations,
|
||||
* so get the best of their pathlists. We assume it's
|
||||
* ok to reassign a path to the other RelOptInfo without
|
||||
* doing more than changing its parent pointer (cf. pathnode.c).
|
||||
*/
|
||||
rel->pathlist = add_pathlist(rel,
|
||||
rel->pathlist,
|
||||
unjoined_rel->pathlist);
|
||||
unmerged_rel->pathlist);
|
||||
}
|
||||
else
|
||||
result = lappend(result, unjoined_rel);
|
||||
result = lappend(result, unmerged_rel);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* rels_set_cheapest
|
||||
* For each relation entry in 'rel_list' (which corresponds to a join
|
||||
* relation), set pointers to the cheapest path
|
||||
* For each relation entry in 'rel_list' (which should contain only join
|
||||
* relations), set pointers to the cheapest path and compute rel size.
|
||||
*/
|
||||
void
|
||||
rels_set_cheapest(List *rel_list)
|
||||
{
|
||||
List *x = NIL;
|
||||
RelOptInfo *rel = (RelOptInfo *) NULL;
|
||||
JoinPath *cheapest;
|
||||
List *x;
|
||||
|
||||
foreach(x, rel_list)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(x);
|
||||
RelOptInfo *rel = (RelOptInfo *) lfirst(x);
|
||||
JoinPath *cheapest;
|
||||
|
||||
cheapest = (JoinPath *) set_cheapest(rel, rel->pathlist);
|
||||
if (IsA_JoinPath(cheapest))
|
||||
rel->size = compute_joinrel_size(cheapest);
|
||||
else
|
||||
elog(ERROR, "non JoinPath called");
|
||||
elog(ERROR, "rels_set_cheapest: non JoinPath found");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user