@ -1,70 +1,84 @@
/*-------------------------------------------------------------------------
*
* joinutil s. c
* Utilities for matching and building join and path keys
* pathkey s. 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
* index scan 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 - rela tio n
* 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 resu lting
* 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 V ar as a pre - sorted key to prevent upper Mergejoins
* joins to use either v ar 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 .
*
* 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 .
* 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 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
* PATH KEY 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 .
*
* ' 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 '
*
* 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 .
*
* Returns a list of matched join keys and a list of matched join clauses
* in pointers if valid order can be found .
* Compare two pathkey items for equality .
*
* 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 .
*/
bool
order_joinkeys_by_pathkeys ( List * pathkeys ,
List * joinkeys ,
List * joinclauses ,
int outer_or_inner ,
List * * matchedJoinKeysPtr ,
List * * matchedJoinClausesPtr )
static bool
pathkeyitem_equal ( PathKeyItem * a , PathKeyItem * b )
{
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 )
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 .
*
* 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 ) ) .
*
* 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 ) )
{
pathkey = lfirst ( i ) ;
matched_joinkey_index = match_pathkey_joinkeys ( pathkey , joinkeys ,
outer_or_inner ) ;
List * subkey1 = lfirst ( key1 ) ;
List * subkey2 = lfirst ( key2 ) ;
List * i ;
if ( matched_joinkey_index ! = - 1 )
/* We have to do this the hard way since the ordering of the subkey
* lists is arbitrary .
*/
if ( key1_subsetof_key2 )
{
matched_keys + + ;
if ( matchedJoinKeysPtr )
foreach ( i , subkey1 )
{
JoinKey * joinkey = nth ( matched_joinkey_index , joinkeys ) ;
matched_joinkeys = lappend ( matched_joinkeys , joinkey ) ;
if ( ! pathkeyitem_member ( ( PathKeyItem * ) lfirst ( i ) , subkey2 ) )
{
key1_subsetof_key2 = false ;
break ;
}
}
}
if ( matchedJoinClausesPtr )
if ( key2_subsetof_key1 )
{
foreach ( i , subkey2 )
{
Expr * joinclause = nth ( matched_joinkey_index ,
joinclauses ) ;
Assert ( joinclauses ) ;
matched_joinclauses = lappend ( matched_joinclauses , joinclause ) ;
if ( ! pathkeyitem_member ( ( PathKeyItem * ) lfirst ( i ) , subkey1 ) )
{
key2_subsetof_key1 = false ;
break ;
}
}
}
else
/* A pathkey could not be matched. */
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 ( ! key1_subsetof_key2 & & ! key2_subsetof_key1 )
return PATHKEYS_DIFFERENT ; /* no need to keep looking */
}
if ( matchedJoinKeysPtr )
* matchedJoinKeysPtr = matched_joinkeys ;
if ( matchedJoinClausesPtr )
* matchedJoinClausesPtr = matched_joinclauses ;
return true ;
/* 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 ;
}
/*
* match_pathkey_joinkeys
* Returns the 0 - based index into ' joinkeys ' of the first joinkey whose
* outer or inner pathkey matches any subkey of ' pathkey ' .
*
* All these keys are equivalent , so any of them can match . See above .
* pathkeys_contained_in
* Common special case of compare_pathkeys : we just want to know
* if keys2 are at least as well sorted as keys1 .
*/
static int
match_pathkey_joinkeys ( List * pathkey ,
List * joinkeys ,
int outer_or_inner )
bool
pathkeys_contained_in ( List * keys1 , List * keys2 )
{
Var * key ;
int pos ;
List * i ,
* x ;
JoinKey * jk ;
foreach ( i , pathkey )
switch ( compare_pathkeys ( keys1 , keys2 ) )
{
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 + + ;
}
case PATHKEYS_EQUAL :
case PATHKEYS_BETTER2 :
return true ;
default :
break ;
}
return - 1 ; /* no index found */
return false ;
}
/*
* 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
* get_cheapest_path_for_pathkeys
* Find the cheapest path in ' paths ' that satisfies the given pathkeys .
* Return NULL if no such path .
*
* ' 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 ;
}
/****************************************************************************
* NEW PATHKEY FORMATION
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* 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 .
* 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 *
make_pathkeys_from_joinkeys ( List * joinkeys ,
List * tlist ,
int outer_or_inner )
build_index_pathkeys ( Query * root , RelOptInfo * rel , RelOptInfo * index )
{
List * pathkeys = NIL ;
List * jk ;
List * retval = NIL ;
int * indexkeys = index - > indexkeys ;
Oid * ordering = index - > ordering ;
if ( ! indexkeys | | indexkeys [ 0 ] = = 0 | |
! ordering | | ordering [ 0 ] = = InvalidOid )
return NIL ; /* unordered index? */
foreach ( jk , joinkeys )
if ( index - > indproc )
{
JoinKey * jkey = ( JoinKey * ) lfirst ( jk ) ;
Var * key ;
List * p ,
* p2 ;
bool found = false ;
/* 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 + + ;
}
key = ( Var * ) extract_join_key ( jkey , outer_or_inner ) ;
/* 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 ;
/* check to see if it is in the target list */
if ( matching_tlist_var ( key , tlist ) )
while ( * indexkeys ! = 0 & & * ordering ! = InvalidOid )
{
Var * relvar = find_indexkey_var ( * indexkeys , rel_tlist ) ;
/*
* Include it in the pathkeys list if we haven ' t already done
* so
/* 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 .
*/
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 ) ) ;
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 pathkeys ;
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 ;
/****************************************************************************
* NEW PATHKEY FORMATION
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
foreach ( temp , tlist )
{
Var * tle_var = get_expr ( lfirst ( temp ) ) ;
if ( IsA ( tle_var , Var ) & & tle_var - > varattno = = indexkey )
return tle_var ;
}
return NULL ;
}
/*
* new_join_pathkeys
* 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 i s the var node found in
* Note that each returned pathkey use s 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 ;
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 ) ;
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 ;
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 ;
}