/* * join_search_one_level * Consider ways to produce join relations containing exactly 'level' * jointree items. (This is one step of the dynamic-programming method * embodied in standard_join_search.) Join rel nodes for each feasible * combination of lower-level rels are created and returned in a list. * Implementation paths are created for each such joinrel, too. * * level: level of rels we want to make this time * root->join_rel_level[j], 1 <= j < level, is a list of rels containing j items * * The result is returned in root->join_rel_level[level]. */ void join_search_one_level(PlannerInfo *root, int level) { List **joinrels = root->join_rel_level; ListCell *r; int k; Assert(joinrels[level] == NIL); /* Set join_cur_level so that new joinrels are added to proper list */ root->join_cur_level = level; /* * First, consider left-sided and right-sided plans, in which rels of * exactly level-1 member relations are joined against initial relations. * We prefer to join using join clauses, but if we find a rel of level-1 * members that has no join clauses, we will generate Cartesian-product * joins against all initial rels not already contained in it. */ foreach(r, joinrels[level - 1]) { RelOptInfo *old_rel = (RelOptInfo *) lfirst(r); if (old_rel->joininfo != NIL || old_rel->has_eclass_joins || has_join_restriction(root, old_rel)) { /* * There are join clauses or join order restrictions relevant to * this rel, so consider joins between this rel and (only) those * initial rels it is linked to by a clause or restriction. * * At level 2 this condition is symmetric, so there is no need to * look at initial rels before this one in the list; we already * considered such joins when we were at the earlier rel. (The * mirror-image joins are handled automatically by make_join_rel.) * In later passes (level > 2), we join rels of the previous level * to each initial rel they don't already include but have a join * clause or restriction with. */ ListCell *other_rels; if (level == 2) /* consider remaining initial rels */ other_rels = lnext(r); else /* consider all initial rels */ other_rels = list_head(joinrels[1]); make_rels_by_clause_joins(root, old_rel, other_rels); } else { /* * Oops, we have a relation that is not joined to any other * relation, either directly or by join-order restrictions. * Cartesian product time. * * We consider a cartesian product with each not-already-included * initial rel, whether it has other join clauses or not. At * level 2, if there are two or more clauseless initial rels, we * will redundantly consider joining them in both directions; but * such cases aren't common enough to justify adding complexity to * avoid the duplicated effort. */ make_rels_by_clauseless_joins(root, old_rel, list_head(joinrels[1])); } }
/* * join_search_one_level * Consider ways to produce join relations containing exactly 'level' * jointree items. (This is one step of the dynamic-programming method * embodied in standard_join_search.) Join rel nodes for each feasible * combination of lower-level rels are created and returned in a list. * Implementation paths are created for each such joinrel, too. * * level: level of rels we want to make this time * root->join_rel_level[j], 1 <= j < level, is a list of rels containing j items * * The result is returned in root->join_rel_level[level]. */ void join_search_one_level(PlannerInfo *root, int level) { List **joinrels = root->join_rel_level; ListCell *r; int k; Assert(joinrels[level] == NIL); /* Set join_cur_level so that new joinrels are added to proper list */ root->join_cur_level = level; /* * First, consider left-sided and right-sided plans, in which rels of * exactly level-1 member relations are joined against initial relations. * We prefer to join using join clauses, but if we find a rel of level-1 * members that has no join clauses, we will generate Cartesian-product * joins against all initial rels not already contained in it. * * In the first pass (level == 2), we try to join each initial rel to each * initial rel that appears later in joinrels[1]. (The mirror-image joins * are handled automatically by make_join_rel.) In later passes, we try * to join rels of size level-1 from joinrels[level-1] to each initial rel * in joinrels[1]. */ foreach(r, joinrels[level - 1]) { RelOptInfo *old_rel = (RelOptInfo *) lfirst(r); ListCell *other_rels; if (level == 2) other_rels = lnext(r); /* only consider remaining initial * rels */ else other_rels = list_head(joinrels[1]); /* consider all initial * rels */ if (old_rel->joininfo != NIL || old_rel->has_eclass_joins || has_join_restriction(root, old_rel)) { /* * Note that if all available join clauses for this rel require * more than one other rel, we will fail to make any joins against * it here. In most cases that's OK; it'll be considered by * "bushy plan" join code in a higher-level pass where we have * those other rels collected into a join rel. * * See also the last-ditch case below. */ make_rels_by_clause_joins(root, old_rel, other_rels); } else { /* * Oops, we have a relation that is not joined to any other * relation, either directly or by join-order restrictions. * Cartesian product time. */ make_rels_by_clauseless_joins(root, old_rel, other_rels); } }
/* * join_search_one_level * Consider ways to produce join relations containing exactly 'level' * jointree items. (This is one step of the dynamic-programming method * embodied in standard_join_search.) Join rel nodes for each feasible * combination of lower-level rels are created and returned in a list. * Implementation paths are created for each such joinrel, too. * * level: level of rels we want to make this time. * joinrels[j], 1 <= j < level, is a list of rels containing j items. */ List * join_search_one_level(PlannerInfo *root, int level, List **joinrels) { List *result_rels = NIL; List *new_rels; ListCell *r; int k; /* * First, consider left-sided and right-sided plans, in which rels of * exactly level-1 member relations are joined against initial relations. * We prefer to join using join clauses, but if we find a rel of level-1 * members that has no join clauses, we will generate Cartesian-product * joins against all initial rels not already contained in it. * * In the first pass (level == 2), we try to join each initial rel to each * initial rel that appears later in joinrels[1]. (The mirror-image joins * are handled automatically by make_join_rel.) In later passes, we try * to join rels of size level-1 from joinrels[level-1] to each initial rel * in joinrels[1]. */ foreach(r, joinrels[level - 1]) { RelOptInfo *old_rel = (RelOptInfo *) lfirst(r); ListCell *other_rels; if (level == 2) other_rels = lnext(r); /* only consider remaining initial * rels */ else other_rels = list_head(joinrels[1]); /* consider all initial * rels */ if (old_rel->joininfo != NIL || old_rel->has_eclass_joins || has_join_restriction(root, old_rel)) { /* * Note that if all available join clauses for this rel require * more than one other rel, we will fail to make any joins against * it here. In most cases that's OK; it'll be considered by * "bushy plan" join code in a higher-level pass where we have * those other rels collected into a join rel. * * See also the last-ditch case below. */ new_rels = make_rels_by_clause_joins(root, old_rel, other_rels); } else { /* * Oops, we have a relation that is not joined to any other * relation, either directly or by join-order restrictions. * Cartesian product time. */ new_rels = make_rels_by_clauseless_joins(root, old_rel, other_rels); } /* * At levels above 2 we will generate the same joined relation in * multiple ways --- for example (a join b) join c is the same * RelOptInfo as (b join c) join a, though the second case will add a * different set of Paths to it. To avoid making extra work for * subsequent passes, do not enter the same RelOptInfo into our output * list multiple times. */ result_rels = list_concat_unique_ptr(result_rels, new_rels); }