#include "nodes/relation.h"

Go to the source code of this file.
| typedef bool(* ec_matches_callback_type)(PlannerInfo *root, RelOptInfo *rel, EquivalenceClass *ec, EquivalenceMember *em, void *arg) |
| typedef RelOptInfo*(* join_search_hook_type)(PlannerInfo *root, int levels_needed, List *initial_rels) |
| enum PathKeysComparison |
Definition at line 149 of file paths.h.
{
PATHKEYS_EQUAL, /* pathkeys are identical */
PATHKEYS_BETTER1, /* pathkey 1 is a superset of pathkey 2 */
PATHKEYS_BETTER2, /* vice versa */
PATHKEYS_DIFFERENT /* neither pathkey includes the other */
} PathKeysComparison;
| void add_child_rel_equivalences | ( | PlannerInfo * | root, | |
| AppendRelInfo * | appinfo, | |||
| RelOptInfo * | parent_rel, | |||
| RelOptInfo * | child_rel | |||
| ) |
Definition at line 1905 of file equivclass.c.
References add_eq_member(), adjust_appendrel_attrs(), bms_add_members(), bms_difference(), bms_is_subset(), bms_overlap(), EquivalenceClass::ec_has_volatile, EquivalenceClass::ec_members, EquivalenceClass::ec_relids, EquivalenceMember::em_datatype, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, EquivalenceMember::em_is_const, EquivalenceMember::em_nullable_relids, EquivalenceMember::em_relids, PlannerInfo::eq_classes, lfirst, and RelOptInfo::relids.
Referenced by set_append_rel_size().
{
ListCell *lc1;
foreach(lc1, root->eq_classes)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
ListCell *lc2;
/*
* If this EC contains a volatile expression, then generating child
* EMs would be downright dangerous, so skip it. We rely on a
* volatile EC having only one EM.
*/
if (cur_ec->ec_has_volatile)
continue;
/* No point in searching if parent rel not mentioned in eclass */
if (!bms_is_subset(parent_rel->relids, cur_ec->ec_relids))
continue;
foreach(lc2, cur_ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
if (cur_em->em_is_const || cur_em->em_is_child)
continue; /* ignore consts and children here */
/* Does it reference parent_rel? */
if (bms_overlap(cur_em->em_relids, parent_rel->relids))
{
/* Yes, generate transformed child version */
Expr *child_expr;
Relids new_relids;
Relids new_nullable_relids;
child_expr = (Expr *)
adjust_appendrel_attrs(root,
(Node *) cur_em->em_expr,
appinfo);
/*
* Transform em_relids to match. Note we do *not* do
* pull_varnos(child_expr) here, as for example the
* transformation might have substituted a constant, but we
* don't want the child member to be marked as constant.
*/
new_relids = bms_difference(cur_em->em_relids,
parent_rel->relids);
new_relids = bms_add_members(new_relids, child_rel->relids);
/*
* And likewise for nullable_relids. Note this code assumes
* parent and child relids are singletons.
*/
new_nullable_relids = cur_em->em_nullable_relids;
if (bms_overlap(new_nullable_relids, parent_rel->relids))
{
new_nullable_relids = bms_difference(new_nullable_relids,
parent_rel->relids);
new_nullable_relids = bms_add_members(new_nullable_relids,
child_rel->relids);
}
(void) add_eq_member(cur_ec, child_expr,
new_relids, new_nullable_relids,
true, cur_em->em_datatype);
}
}
}
}
| void add_paths_to_joinrel | ( | PlannerInfo * | root, | |
| RelOptInfo * | joinrel, | |||
| RelOptInfo * | outerrel, | |||
| RelOptInfo * | innerrel, | |||
| JoinType | jointype, | |||
| SpecialJoinInfo * | sjinfo, | |||
| List * | restrictlist | |||
| ) |
Definition at line 78 of file joinpath.c.
References PlannerInfo::all_baserels, bms_difference(), bms_is_member(), bms_join(), bms_overlap(), compute_semi_anti_join_factors(), enable_hashjoin, enable_mergejoin, hash_inner_and_outer(), JOIN_ANTI, JOIN_FULL, PlannerInfo::join_info_list, JOIN_SEMI, SpecialJoinInfo::jointype, PlannerInfo::lateral_info_list, LateralJoinInfo::lateral_lhs, LateralJoinInfo::lateral_rhs, lfirst, match_unsorted_outer(), SpecialJoinInfo::min_lefthand, SpecialJoinInfo::min_righthand, RelOptInfo::relids, select_mergejoin_clauses(), and sort_inner_and_outer().
Referenced by make_join_rel().
{
List *mergeclause_list = NIL;
bool mergejoin_allowed = true;
SemiAntiJoinFactors semifactors;
Relids param_source_rels = NULL;
ListCell *lc;
/*
* Find potential mergejoin clauses. We can skip this if we are not
* interested in doing a mergejoin. However, mergejoin may be our only
* way of implementing a full outer join, so override enable_mergejoin if
* it's a full join.
*/
if (enable_mergejoin || jointype == JOIN_FULL)
mergeclause_list = select_mergejoin_clauses(root,
joinrel,
outerrel,
innerrel,
restrictlist,
jointype,
&mergejoin_allowed);
/*
* If it's SEMI or ANTI join, compute correction factors for cost
* estimation. These will be the same for all paths.
*/
if (jointype == JOIN_SEMI || jointype == JOIN_ANTI)
compute_semi_anti_join_factors(root, outerrel, innerrel,
jointype, sjinfo, restrictlist,
&semifactors);
/*
* Decide whether it's sensible to generate parameterized paths for this
* joinrel, and if so, which relations such paths should require. There
* is no need to create a parameterized result path unless there is a join
* order restriction that prevents joining one of our input rels directly
* to the parameter source rel instead of joining to the other input rel.
* This restriction reduces the number of parameterized paths we have to
* deal with at higher join levels, without compromising the quality of
* the resulting plan. We express the restriction as a Relids set that
* must overlap the parameterization of any proposed join path.
*/
foreach(lc, root->join_info_list)
{
SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);
/*
* SJ is relevant to this join if we have some part of its RHS
* (possibly not all of it), and haven't yet joined to its LHS. (This
* test is pretty simplistic, but should be sufficient considering the
* join has already been proven legal.) If the SJ is relevant, it
* presents constraints for joining to anything not in its RHS.
*/
if (bms_overlap(joinrel->relids, sjinfo->min_righthand) &&
!bms_overlap(joinrel->relids, sjinfo->min_lefthand))
param_source_rels = bms_join(param_source_rels,
bms_difference(root->all_baserels,
sjinfo->min_righthand));
/* full joins constrain both sides symmetrically */
if (sjinfo->jointype == JOIN_FULL &&
bms_overlap(joinrel->relids, sjinfo->min_lefthand) &&
!bms_overlap(joinrel->relids, sjinfo->min_righthand))
param_source_rels = bms_join(param_source_rels,
bms_difference(root->all_baserels,
sjinfo->min_lefthand));
}
/*
* However, when a LATERAL subquery is involved, we have to be a bit
* laxer, because there will simply not be any paths for the joinrel that
* aren't parameterized by whatever the subquery is parameterized by,
* unless its parameterization is resolved within the joinrel. Hence, add
* to param_source_rels anything that is laterally referenced in either
* input and is not in the join already.
*/
foreach(lc, root->lateral_info_list)
{
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc);
if (bms_is_member(ljinfo->lateral_rhs, joinrel->relids))
param_source_rels = bms_join(param_source_rels,
bms_difference(ljinfo->lateral_lhs,
joinrel->relids));
}
/*
* 1. Consider mergejoin paths where both relations must be explicitly
* sorted. Skip this if we can't mergejoin.
*/
if (mergejoin_allowed)
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
restrictlist, mergeclause_list, jointype,
sjinfo, param_source_rels);
/*
* 2. Consider paths where the outer relation need not be explicitly
* sorted. This includes both nestloops and mergejoins where the outer
* path is already ordered. Again, skip this if we can't mergejoin.
* (That's okay because we know that nestloop can't handle right/full
* joins at all, so it wouldn't work in the prohibited cases either.)
*/
if (mergejoin_allowed)
match_unsorted_outer(root, joinrel, outerrel, innerrel,
restrictlist, mergeclause_list, jointype,
sjinfo, &semifactors, param_source_rels);
#ifdef NOT_USED
/*
* 3. Consider paths where the inner relation need not be explicitly
* sorted. This includes mergejoins only (nestloops were already built in
* match_unsorted_outer).
*
* Diked out as redundant 2/13/2000 -- tgl. There isn't any really
* significant difference between the inner and outer side of a mergejoin,
* so match_unsorted_inner creates no paths that aren't equivalent to
* those made by match_unsorted_outer when add_paths_to_joinrel() is
* invoked with the two rels given in the other order.
*/
if (mergejoin_allowed)
match_unsorted_inner(root, joinrel, outerrel, innerrel,
restrictlist, mergeclause_list, jointype,
sjinfo, &semifactors, param_source_rels);
#endif
/*
* 4. Consider paths where both outer and inner relations must be hashed
* before being joined. As above, disregard enable_hashjoin for full
* joins, because there may be no other alternative.
*/
if (enable_hashjoin || jointype == JOIN_FULL)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
restrictlist, jointype,
sjinfo, &semifactors, param_source_rels);
}
| Expr* adjust_rowcompare_for_index | ( | RowCompareExpr * | clause, | |
| IndexOptInfo * | index, | |||
| int | indexcol, | |||
| List ** | indexcolnos, | |||
| bool * | var_on_left_p | |||
| ) |
Definition at line 3576 of file indxpath.c.
References Assert, bms_is_member(), BOOLOID, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, contain_volatile_functions(), copyObject(), elog, ERROR, get_commutator(), get_op_opfamily_properties(), get_op_opfamily_strategy(), get_opfamily_member(), i, IndexOptInfo::indexcollations, IndexCollMatchesExprColl, RowCompareExpr::inputcollids, InvalidOid, lappend_int(), lappend_oid(), RowCompareExpr::largs, lfirst, lfirst_oid, linitial, linitial_oid, list_copy(), list_head(), list_length(), list_make1_int, list_make1_oid, list_truncate(), lnext, make_opclause(), makeNode, match_index_to_operand(), IndexOptInfo::ncolumns, NULL, OidIsValid, RowCompareExpr::opfamilies, IndexOptInfo::opfamily, RowCompareExpr::opnos, pull_varnos(), RowCompareExpr::rargs, RowCompareExpr::rctype, IndexOptInfo::rel, RelOptInfo::relid, ROWCOMPARE_GE, and ROWCOMPARE_LE.
Referenced by expand_indexqual_rowcompare(), and fix_indexqual_references().
{
bool var_on_left;
int op_strategy;
Oid op_lefttype;
Oid op_righttype;
int matching_cols;
Oid expr_op;
List *opfamilies;
List *lefttypes;
List *righttypes;
List *new_ops;
ListCell *largs_cell;
ListCell *rargs_cell;
ListCell *opnos_cell;
ListCell *collids_cell;
/* We have to figure out (again) how the first col matches */
var_on_left = match_index_to_operand((Node *) linitial(clause->largs),
indexcol, index);
Assert(var_on_left ||
match_index_to_operand((Node *) linitial(clause->rargs),
indexcol, index));
*var_on_left_p = var_on_left;
expr_op = linitial_oid(clause->opnos);
if (!var_on_left)
expr_op = get_commutator(expr_op);
get_op_opfamily_properties(expr_op, index->opfamily[indexcol], false,
&op_strategy,
&op_lefttype,
&op_righttype);
/* Initialize returned list of which index columns are used */
*indexcolnos = list_make1_int(indexcol);
/* Build lists of the opfamilies and operator datatypes in case needed */
opfamilies = list_make1_oid(index->opfamily[indexcol]);
lefttypes = list_make1_oid(op_lefttype);
righttypes = list_make1_oid(op_righttype);
/*
* See how many of the remaining columns match some index column in the
* same way. As in match_clause_to_indexcol(), the "other" side of any
* potential index condition is OK as long as it doesn't use Vars from the
* indexed relation.
*/
matching_cols = 1;
largs_cell = lnext(list_head(clause->largs));
rargs_cell = lnext(list_head(clause->rargs));
opnos_cell = lnext(list_head(clause->opnos));
collids_cell = lnext(list_head(clause->inputcollids));
while (largs_cell != NULL)
{
Node *varop;
Node *constop;
int i;
expr_op = lfirst_oid(opnos_cell);
if (var_on_left)
{
varop = (Node *) lfirst(largs_cell);
constop = (Node *) lfirst(rargs_cell);
}
else
{
varop = (Node *) lfirst(rargs_cell);
constop = (Node *) lfirst(largs_cell);
/* indexkey is on right, so commute the operator */
expr_op = get_commutator(expr_op);
if (expr_op == InvalidOid)
break; /* operator is not usable */
}
if (bms_is_member(index->rel->relid, pull_varnos(constop)))
break; /* no good, Var on wrong side */
if (contain_volatile_functions(constop))
break; /* no good, volatile comparison value */
/*
* The Var side can match any column of the index.
*/
for (i = 0; i < index->ncolumns; i++)
{
if (match_index_to_operand(varop, i, index) &&
get_op_opfamily_strategy(expr_op,
index->opfamily[i]) == op_strategy &&
IndexCollMatchesExprColl(index->indexcollations[i],
lfirst_oid(collids_cell)))
break;
}
if (i >= index->ncolumns)
break; /* no match found */
/* Add column number to returned list */
*indexcolnos = lappend_int(*indexcolnos, i);
/* Add opfamily and datatypes to lists */
get_op_opfamily_properties(expr_op, index->opfamily[i], false,
&op_strategy,
&op_lefttype,
&op_righttype);
opfamilies = lappend_oid(opfamilies, index->opfamily[i]);
lefttypes = lappend_oid(lefttypes, op_lefttype);
righttypes = lappend_oid(righttypes, op_righttype);
/* This column matches, keep scanning */
matching_cols++;
largs_cell = lnext(largs_cell);
rargs_cell = lnext(rargs_cell);
opnos_cell = lnext(opnos_cell);
collids_cell = lnext(collids_cell);
}
/* Return clause as-is if it's all usable as index quals */
if (matching_cols == list_length(clause->opnos))
return (Expr *) clause;
/*
* We have to generate a subset rowcompare (possibly just one OpExpr). The
* painful part of this is changing < to <= or > to >=, so deal with that
* first.
*/
if (op_strategy == BTLessEqualStrategyNumber ||
op_strategy == BTGreaterEqualStrategyNumber)
{
/* easy, just use the same operators */
new_ops = list_truncate(list_copy(clause->opnos), matching_cols);
}
else
{
ListCell *opfamilies_cell;
ListCell *lefttypes_cell;
ListCell *righttypes_cell;
if (op_strategy == BTLessStrategyNumber)
op_strategy = BTLessEqualStrategyNumber;
else if (op_strategy == BTGreaterStrategyNumber)
op_strategy = BTGreaterEqualStrategyNumber;
else
elog(ERROR, "unexpected strategy number %d", op_strategy);
new_ops = NIL;
lefttypes_cell = list_head(lefttypes);
righttypes_cell = list_head(righttypes);
foreach(opfamilies_cell, opfamilies)
{
Oid opfam = lfirst_oid(opfamilies_cell);
Oid lefttype = lfirst_oid(lefttypes_cell);
Oid righttype = lfirst_oid(righttypes_cell);
expr_op = get_opfamily_member(opfam, lefttype, righttype,
op_strategy);
if (!OidIsValid(expr_op)) /* should not happen */
elog(ERROR, "could not find member %d(%u,%u) of opfamily %u",
op_strategy, lefttype, righttype, opfam);
if (!var_on_left)
{
expr_op = get_commutator(expr_op);
if (!OidIsValid(expr_op)) /* should not happen */
elog(ERROR, "could not find commutator of member %d(%u,%u) of opfamily %u",
op_strategy, lefttype, righttype, opfam);
}
new_ops = lappend_oid(new_ops, expr_op);
lefttypes_cell = lnext(lefttypes_cell);
righttypes_cell = lnext(righttypes_cell);
}
}
/* If we have more than one matching col, create a subset rowcompare */
if (matching_cols > 1)
{
RowCompareExpr *rc = makeNode(RowCompareExpr);
if (var_on_left)
rc->rctype = (RowCompareType) op_strategy;
else
rc->rctype = (op_strategy == BTLessEqualStrategyNumber) ?
ROWCOMPARE_GE : ROWCOMPARE_LE;
rc->opnos = new_ops;
rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
matching_cols);
rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
matching_cols);
rc->largs = list_truncate((List *) copyObject(clause->largs),
matching_cols);
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
matching_cols);
return (Expr *) rc;
}
else
{
return make_opclause(linitial_oid(new_ops), BOOLOID, false,
copyObject(linitial(clause->largs)),
copyObject(linitial(clause->rargs)),
InvalidOid,
linitial_oid(clause->inputcollids));
}
}
| List* build_index_pathkeys | ( | PlannerInfo * | root, | |
| IndexOptInfo * | index, | |||
| ScanDirection | scandir | |||
| ) |
Definition at line 430 of file pathkeys.c.
References TargetEntry::expr, i, IndexOptInfo::indexcollations, IndexOptInfo::indextlist, lappend(), lfirst, make_pathkey_from_sortinfo(), NULL, IndexOptInfo::nulls_first, IndexOptInfo::opcintype, pathkey_is_redundant(), IndexOptInfo::rel, RelOptInfo::relids, IndexOptInfo::reverse_sort, ScanDirectionIsBackward, and IndexOptInfo::sortopfamily.
Referenced by build_index_paths().
{
List *retval = NIL;
ListCell *lc;
int i;
if (index->sortopfamily == NULL)
return NIL; /* non-orderable index */
i = 0;
foreach(lc, index->indextlist)
{
TargetEntry *indextle = (TargetEntry *) lfirst(lc);
Expr *indexkey;
bool reverse_sort;
bool nulls_first;
PathKey *cpathkey;
/* We assume we don't need to make a copy of the tlist item */
indexkey = indextle->expr;
if (ScanDirectionIsBackward(scandir))
{
reverse_sort = !index->reverse_sort[i];
nulls_first = !index->nulls_first[i];
}
else
{
reverse_sort = index->reverse_sort[i];
nulls_first = index->nulls_first[i];
}
/* OK, try to make a canonical pathkey for this sort key */
cpathkey = make_pathkey_from_sortinfo(root,
indexkey,
index->sortopfamily[i],
index->opcintype[i],
index->indexcollations[i],
reverse_sort,
nulls_first,
0,
index->rel->relids,
false);
/*
* If the sort key isn't already present in any EquivalenceClass, then
* it's not an interesting sort order for this query. So we can stop
* now --- lower-order sort keys aren't useful either.
*/
if (!cpathkey)
break;
/* Add to list unless redundant */
if (!pathkey_is_redundant(cpathkey, retval))
retval = lappend(retval, cpathkey);
i++;
}
return retval;
}
| List* build_join_pathkeys | ( | PlannerInfo * | root, | |
| RelOptInfo * | joinrel, | |||
| JoinType | jointype, | |||
| List * | outer_pathkeys | |||
| ) |
Definition at line 719 of file pathkeys.c.
References JOIN_FULL, JOIN_RIGHT, and truncate_useless_pathkeys().
Referenced by match_unsorted_outer(), and sort_inner_and_outer().
{
if (jointype == JOIN_FULL || jointype == JOIN_RIGHT)
return NIL;
/*
* This used to be quite a complex bit of code, but now that all pathkey
* sublists start out life canonicalized, we don't have to do a darn thing
* here!
*
* We do, however, need to truncate the pathkeys list, since it may
* contain pathkeys that were useful for forming this joinrel but are
* uninteresting to higher levels.
*/
return truncate_useless_pathkeys(root, joinrel, outer_pathkeys);
}
Definition at line 421 of file equivclass.c.
References arg, COERCE_IMPLICIT_CAST, exprCollation(), exprType(), exprTypmod(), IsA, IsPolymorphicType, and makeRelabelType().
Referenced by convert_subquery_pathkeys(), get_eclass_for_sort_expr(), and process_equivalence().
{
Oid expr_type = exprType((Node *) expr);
/*
* For a polymorphic-input-type opclass, just keep the same exposed type.
*/
if (IsPolymorphicType(req_type))
req_type = expr_type;
/*
* No work if the expression exposes the right type/collation already.
*/
if (expr_type != req_type ||
exprCollation((Node *) expr) != req_collation)
{
/*
* Strip any existing RelabelType, then add a new one if needed. This
* is to preserve the invariant of no redundant RelabelTypes.
*
* If we have to change the exposed type of the stripped expression,
* set typmod to -1 (since the new type may not have the same typmod
* interpretation). If we only have to change collation, preserve the
* exposed typmod.
*/
while (expr && IsA(expr, RelabelType))
expr = (Expr *) ((RelabelType *) expr)->arg;
if (exprType((Node *) expr) != req_type)
expr = (Expr *) makeRelabelType(expr,
req_type,
-1,
req_collation,
COERCE_IMPLICIT_CAST);
else if (exprCollation((Node *) expr) != req_collation)
expr = (Expr *) makeRelabelType(expr,
req_type,
exprTypmod((Node *) expr),
req_collation,
COERCE_IMPLICIT_CAST);
}
return expr;
}
| void check_partial_indexes | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel | |||
| ) |
Definition at line 2604 of file indxpath.c.
References PlannerInfo::all_baserels, RelOptInfo::baserestrictinfo, bms_difference(), bms_is_empty(), bms_make_singleton(), bms_union(), find_childrel_appendrelinfo(), generate_join_implied_equalities(), RelOptInfo::indexlist, IndexOptInfo::indpred, join_clause_is_movable_to(), RelOptInfo::joininfo, lappend(), lfirst, list_concat(), list_copy(), NIL, AppendRelInfo::parent_relid, predicate_implied_by(), IndexOptInfo::predOK, RelOptInfo::relid, RelOptInfo::relids, RELOPT_OTHER_MEMBER_REL, and RelOptInfo::reloptkind.
Referenced by set_plain_rel_size().
{
List *clauselist;
bool have_partial;
Relids otherrels;
ListCell *lc;
/*
* Frequently, there will be no partial indexes, so first check to make
* sure there's something useful to do here.
*/
have_partial = false;
foreach(lc, rel->indexlist)
{
IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
if (index->indpred == NIL)
continue; /* ignore non-partial indexes */
if (index->predOK)
continue; /* don't repeat work if already proven OK */
have_partial = true;
break;
}
if (!have_partial)
return;
/*
* Construct a list of clauses that we can assume true for the purpose
* of proving the index(es) usable. Restriction clauses for the rel are
* always usable, and so are any join clauses that are "movable to" this
* rel. Also, we can consider any EC-derivable join clauses (which must
* be "movable to" this rel, by definition).
*/
clauselist = list_copy(rel->baserestrictinfo);
/* Scan the rel's join clauses */
foreach(lc, rel->joininfo)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
/* Check if clause can be moved to this rel */
if (!join_clause_is_movable_to(rinfo, rel->relid))
continue;
clauselist = lappend(clauselist, rinfo);
}
/*
* Add on any equivalence-derivable join clauses. Computing the correct
* relid sets for generate_join_implied_equalities is slightly tricky
* because the rel could be a child rel rather than a true baserel, and
* in that case we must remove its parent's relid from all_baserels.
*/
if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
{
/* Lookup parent->child translation data */
AppendRelInfo *appinfo = find_childrel_appendrelinfo(root, rel);
otherrels = bms_difference(root->all_baserels,
bms_make_singleton(appinfo->parent_relid));
}
else
otherrels = bms_difference(root->all_baserels, rel->relids);
if (!bms_is_empty(otherrels))
clauselist =
list_concat(clauselist,
generate_join_implied_equalities(root,
bms_union(rel->relids,
otherrels),
otherrels,
rel));
/* Now try to prove each index predicate true */
foreach(lc, rel->indexlist)
{
IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
if (index->indpred == NIL)
continue; /* ignore non-partial indexes */
if (index->predOK)
continue; /* don't repeat work if already proven OK */
index->predOK = predicate_implied_by(index->indpred, clauselist);
}
}
| PathKeysComparison compare_pathkeys | ( | List * | keys1, | |
| List * | keys2 | |||
| ) |
Definition at line 275 of file pathkeys.c.
References forboth, lfirst, and NULL.
Referenced by add_path(), add_path_precheck(), pathkeys_contained_in(), set_append_rel_pathlist(), and set_cheapest().
{
ListCell *key1,
*key2;
/*
* Fall out quickly if we are passed two identical lists. This mostly
* catches the case where both are NIL, but that's common enough to
* warrant the test.
*/
if (keys1 == keys2)
return PATHKEYS_EQUAL;
forboth(key1, keys1, key2, keys2)
{
PathKey *pathkey1 = (PathKey *) lfirst(key1);
PathKey *pathkey2 = (PathKey *) lfirst(key2);
if (pathkey1 != pathkey2)
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.
*/
if (key1 != NULL)
return PATHKEYS_BETTER1; /* key1 is longer */
if (key2 != NULL)
return PATHKEYS_BETTER2; /* key2 is longer */
return PATHKEYS_EQUAL;
}
| List* convert_subquery_pathkeys | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | subquery_pathkeys | |||
| ) |
Definition at line 508 of file pathkeys.c.
References Assert, canonicalize_ec_expression(), EquivalenceClass::ec_collation, EquivalenceClass::ec_has_volatile, EquivalenceClass::ec_members, EquivalenceClass::ec_opfamilies, EquivalenceClass::ec_sortref, elog, EquivalenceMember::em_datatype, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, equal(), ERROR, TargetEntry::expr, get_eclass_for_sort_expr(), get_sortgroupref_tle(), i, lappend(), lfirst, linitial, list_length(), list_nth(), make_canonical_pathkey(), makeVarFromTargetEntry(), pathkey_is_redundant(), PathKey::pk_eclass, PathKey::pk_nulls_first, PathKey::pk_opfamily, PathKey::pk_strategy, PlannerInfo::query_pathkeys, RelOptInfo::relid, RelOptInfo::relids, TargetEntry::resjunk, RelOptInfo::subplan, and Plan::targetlist.
Referenced by set_subquery_pathlist().
{
List *retval = NIL;
int retvallen = 0;
int outer_query_keys = list_length(root->query_pathkeys);
List *sub_tlist = rel->subplan->targetlist;
ListCell *i;
foreach(i, subquery_pathkeys)
{
PathKey *sub_pathkey = (PathKey *) lfirst(i);
EquivalenceClass *sub_eclass = sub_pathkey->pk_eclass;
PathKey *best_pathkey = NULL;
if (sub_eclass->ec_has_volatile)
{
/*
* If the sub_pathkey's EquivalenceClass is volatile, then it must
* have come from an ORDER BY clause, and we have to match it to
* that same targetlist entry.
*/
TargetEntry *tle;
if (sub_eclass->ec_sortref == 0) /* can't happen */
elog(ERROR, "volatile EquivalenceClass has no sortref");
tle = get_sortgroupref_tle(sub_eclass->ec_sortref, sub_tlist);
Assert(tle);
/* resjunk items aren't visible to outer query */
if (!tle->resjunk)
{
/* We can represent this sub_pathkey */
EquivalenceMember *sub_member;
Expr *outer_expr;
EquivalenceClass *outer_ec;
Assert(list_length(sub_eclass->ec_members) == 1);
sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid, tle);
/*
* Note: it might look funny to be setting sortref = 0 for a
* reference to a volatile sub_eclass. However, the
* expression is *not* volatile in the outer query: it's just
* a Var referencing whatever the subquery emitted. (IOW, the
* outer query isn't going to re-execute the volatile
* expression itself.) So this is okay.
*/
outer_ec =
get_eclass_for_sort_expr(root,
outer_expr,
sub_eclass->ec_opfamilies,
sub_member->em_datatype,
sub_eclass->ec_collation,
0,
rel->relids,
false);
/*
* If we don't find a matching EC, sub-pathkey isn't
* interesting to the outer query
*/
if (outer_ec)
best_pathkey =
make_canonical_pathkey(root,
outer_ec,
sub_pathkey->pk_opfamily,
sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first);
}
}
else
{
/*
* Otherwise, the sub_pathkey's EquivalenceClass could contain
* multiple elements (representing knowledge that multiple items
* are effectively equal). Each element might match none, one, or
* more of the output columns that are visible to the outer query.
* This means we may have multiple possible representations of the
* sub_pathkey in the context of the outer query. Ideally we
* would generate them all and put them all into an EC of the
* outer query, thereby propagating equality knowledge up to the
* outer query. Right now we cannot do so, because the outer
* query's EquivalenceClasses are already frozen when this is
* called. Instead we prefer the one that has the highest "score"
* (number of EC peers, plus one if it matches the outer
* query_pathkeys). This is the most likely to be useful in the
* outer query.
*/
int best_score = -1;
ListCell *j;
foreach(j, sub_eclass->ec_members)
{
EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
Expr *sub_expr = sub_member->em_expr;
Oid sub_expr_type = sub_member->em_datatype;
Oid sub_expr_coll = sub_eclass->ec_collation;
ListCell *k;
if (sub_member->em_is_child)
continue; /* ignore children here */
foreach(k, sub_tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(k);
Expr *tle_expr;
Expr *outer_expr;
EquivalenceClass *outer_ec;
PathKey *outer_pk;
int score;
/* resjunk items aren't visible to outer query */
if (tle->resjunk)
continue;
/*
* The targetlist entry is considered to match if it
* matches after sort-key canonicalization. That is
* needed since the sub_expr has been through the same
* process.
*/
tle_expr = canonicalize_ec_expression(tle->expr,
sub_expr_type,
sub_expr_coll);
if (!equal(tle_expr, sub_expr))
continue;
/*
* Build a representation of this targetlist entry as an
* outer Var.
*/
outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid,
tle);
/* See if we have a matching EC for that */
outer_ec = get_eclass_for_sort_expr(root,
outer_expr,
sub_eclass->ec_opfamilies,
sub_expr_type,
sub_expr_coll,
0,
rel->relids,
false);
/*
* If we don't find a matching EC, this sub-pathkey isn't
* interesting to the outer query
*/
if (!outer_ec)
continue;
outer_pk = make_canonical_pathkey(root,
outer_ec,
sub_pathkey->pk_opfamily,
sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first);
/* score = # of equivalence peers */
score = list_length(outer_ec->ec_members) - 1;
/* +1 if it matches the proper query_pathkeys item */
if (retvallen < outer_query_keys &&
list_nth(root->query_pathkeys, retvallen) == outer_pk)
score++;
if (score > best_score)
{
best_pathkey = outer_pk;
best_score = score;
}
}
}
}
/*
* If we couldn't find a representation of this sub_pathkey, we're
* done (we can't use the ones to its right, either).
*/
if (!best_pathkey)
break;
/*
* Eliminate redundant ordering info; could happen if outer query
* equivalences subquery keys...
*/
if (!pathkey_is_redundant(best_pathkey, retval))
{
retval = lappend(retval, best_pathkey);
retvallen++;
}
}
return retval;
}
| void create_index_paths | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel | |||
| ) |
Definition at line 233 of file indxpath.c.
References add_path(), Assert, RelOptInfo::baserestrictinfo, bms_add_member(), bms_equal_any(), bms_is_member(), bms_is_subset(), choose_bitmap_and(), consider_index_join_clauses(), create_bitmap_heap_path(), forboth, generate_bitmap_or_paths(), get_bitmap_tree_required_outer(), get_index_paths(), get_loop_count(), INDEX_MAX_KEYS, RelOptInfo::indexlist, IndexOptInfo::indpred, lappend(), PlannerInfo::lateral_info_list, LateralJoinInfo::lateral_lhs, RelOptInfo::lateral_relids, LateralJoinInfo::lateral_rhs, lfirst, list_concat(), match_eclass_clauses_to_index(), match_join_clauses_to_index(), match_restriction_clauses_to_index(), MemSet, IndexOptInfo::ncolumns, NIL, IndexClauseSet::nonempty, IndexOptInfo::predOK, and RelOptInfo::relid.
Referenced by set_plain_rel_pathlist().
{
List *indexpaths;
List *bitindexpaths;
List *bitjoinpaths;
List *joinorclauses;
Relids lateral_referencers;
IndexClauseSet rclauseset;
IndexClauseSet jclauseset;
IndexClauseSet eclauseset;
ListCell *lc;
/* Skip the whole mess if no indexes */
if (rel->indexlist == NIL)
return;
/*
* If there are any rels that have LATERAL references to this one, we
* cannot use join quals referencing them as index quals for this one,
* since such rels would have to be on the inside not the outside of a
* nestloop join relative to this one. Create a Relids set listing all
* such rels, for use in checks of potential join clauses.
*/
lateral_referencers = NULL;
foreach(lc, root->lateral_info_list)
{
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc);
if (bms_is_member(rel->relid, ljinfo->lateral_lhs))
lateral_referencers = bms_add_member(lateral_referencers,
ljinfo->lateral_rhs);
}
/* Bitmap paths are collected and then dealt with at the end */
bitindexpaths = bitjoinpaths = joinorclauses = NIL;
/* Examine each index in turn */
foreach(lc, rel->indexlist)
{
IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
/* Protect limited-size array in IndexClauseSets */
Assert(index->ncolumns <= INDEX_MAX_KEYS);
/*
* Ignore partial indexes that do not match the query.
* (generate_bitmap_or_paths() might be able to do something with
* them, but that's of no concern here.)
*/
if (index->indpred != NIL && !index->predOK)
continue;
/*
* Identify the restriction clauses that can match the index.
*/
MemSet(&rclauseset, 0, sizeof(rclauseset));
match_restriction_clauses_to_index(rel, index, &rclauseset);
/*
* Build index paths from the restriction clauses. These will be
* non-parameterized paths. Plain paths go directly to add_path(),
* bitmap paths are added to bitindexpaths to be handled below.
*/
get_index_paths(root, rel, index, &rclauseset,
&bitindexpaths);
/*
* Identify the join clauses that can match the index. For the moment
* we keep them separate from the restriction clauses. Note that this
* step finds only "loose" join clauses that have not been merged into
* EquivalenceClasses. Also, collect join OR clauses for later.
*/
MemSet(&jclauseset, 0, sizeof(jclauseset));
match_join_clauses_to_index(root, rel, index, lateral_referencers,
&jclauseset, &joinorclauses);
/*
* Look for EquivalenceClasses that can generate joinclauses matching
* the index.
*/
MemSet(&eclauseset, 0, sizeof(eclauseset));
match_eclass_clauses_to_index(root, index, lateral_referencers,
&eclauseset);
/*
* If we found any plain or eclass join clauses, build parameterized
* index paths using them.
*/
if (jclauseset.nonempty || eclauseset.nonempty)
consider_index_join_clauses(root, rel, index,
&rclauseset,
&jclauseset,
&eclauseset,
&bitjoinpaths);
}
/*
* Generate BitmapOrPaths for any suitable OR-clauses present in the
* restriction list. Add these to bitindexpaths.
*/
indexpaths = generate_bitmap_or_paths(root, rel,
rel->baserestrictinfo, NIL,
false);
bitindexpaths = list_concat(bitindexpaths, indexpaths);
/*
* Likewise, generate BitmapOrPaths for any suitable OR-clauses present in
* the joinclause list. Add these to bitjoinpaths.
*/
indexpaths = generate_bitmap_or_paths(root, rel,
joinorclauses, rel->baserestrictinfo,
false);
bitjoinpaths = list_concat(bitjoinpaths, indexpaths);
/*
* If we found anything usable, generate a BitmapHeapPath for the most
* promising combination of restriction bitmap index paths. Note there
* will be only one such path no matter how many indexes exist. This
* should be sufficient since there's basically only one figure of merit
* (total cost) for such a path.
*/
if (bitindexpaths != NIL)
{
Path *bitmapqual;
BitmapHeapPath *bpath;
bitmapqual = choose_bitmap_and(root, rel, bitindexpaths);
bpath = create_bitmap_heap_path(root, rel, bitmapqual,
rel->lateral_relids, 1.0);
add_path(rel, (Path *) bpath);
}
/*
* Likewise, if we found anything usable, generate BitmapHeapPaths for the
* most promising combinations of join bitmap index paths. Our strategy
* is to generate one such path for each distinct parameterization seen
* among the available bitmap index paths. This may look pretty
* expensive, but usually there won't be very many distinct
* parameterizations. (This logic is quite similar to that in
* consider_index_join_clauses, but we're working with whole paths not
* individual clauses.)
*/
if (bitjoinpaths != NIL)
{
List *path_outer;
List *all_path_outers;
ListCell *lc;
/*
* path_outer holds the parameterization of each path in bitjoinpaths
* (to save recalculating that several times), while all_path_outers
* holds all distinct parameterization sets.
*/
path_outer = all_path_outers = NIL;
foreach(lc, bitjoinpaths)
{
Path *path = (Path *) lfirst(lc);
Relids required_outer;
required_outer = get_bitmap_tree_required_outer(path);
path_outer = lappend(path_outer, required_outer);
if (!bms_equal_any(required_outer, all_path_outers))
all_path_outers = lappend(all_path_outers, required_outer);
}
/* Now, for each distinct parameterization set ... */
foreach(lc, all_path_outers)
{
Relids max_outers = (Relids) lfirst(lc);
List *this_path_set;
Path *bitmapqual;
Relids required_outer;
double loop_count;
BitmapHeapPath *bpath;
ListCell *lcp;
ListCell *lco;
/* Identify all the bitmap join paths needing no more than that */
this_path_set = NIL;
forboth(lcp, bitjoinpaths, lco, path_outer)
{
Path *path = (Path *) lfirst(lcp);
Relids p_outers = (Relids) lfirst(lco);
if (bms_is_subset(p_outers, max_outers))
this_path_set = lappend(this_path_set, path);
}
/*
* Add in restriction bitmap paths, since they can be used
* together with any join paths.
*/
this_path_set = list_concat(this_path_set, bitindexpaths);
/* Select best AND combination for this parameterization */
bitmapqual = choose_bitmap_and(root, rel, this_path_set);
/* And push that path into the mix */
required_outer = get_bitmap_tree_required_outer(bitmapqual);
loop_count = get_loop_count(root, required_outer);
bpath = create_bitmap_heap_path(root, rel, bitmapqual,
required_outer, loop_count);
add_path(rel, (Path *) bpath);
}
}
}
| bool create_or_index_quals | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel | |||
| ) |
Definition at line 81 of file orindxpath.c.
References Assert, RelOptInfo::baserestrictinfo, clause_selectivity(), generate_bitmap_or_paths(), i, RelOptInfo::indexlist, IsA, join_clause_is_movable_to(), JOIN_INNER, RelOptInfo::joininfo, lfirst, linitial, list_concat(), list_length(), list_make1, make_restrictinfo_from_bitmapqual(), NIL, NULL, BitmapOrPath::path, RelOptInfo::relid, restriction_is_or_clause(), and Path::total_cost.
Referenced by set_plain_rel_size().
{
BitmapOrPath *bestpath = NULL;
RestrictInfo *bestrinfo = NULL;
List *newrinfos;
RestrictInfo *or_rinfo;
Selectivity or_selec,
orig_selec;
ListCell *i;
/* Skip the whole mess if no indexes */
if (rel->indexlist == NIL)
return false;
/*
* Find potentially interesting OR joinclauses. We can use any joinclause
* that is considered safe to move to this rel by the parameterized-path
* machinery, even though what we are going to do with it is not exactly a
* parameterized path.
*/
foreach(i, rel->joininfo)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
if (restriction_is_or_clause(rinfo) &&
join_clause_is_movable_to(rinfo, rel->relid))
{
/*
* Use the generate_bitmap_or_paths() machinery to estimate the
* value of each OR clause. We can use regular restriction
* clauses along with the OR clause contents to generate
* indexquals. We pass restriction_only = true so that any
* sub-clauses that are actually joins will be ignored.
*/
List *orpaths;
ListCell *k;
orpaths = generate_bitmap_or_paths(root, rel,
list_make1(rinfo),
rel->baserestrictinfo,
true);
/* Locate the cheapest OR path */
foreach(k, orpaths)
{
BitmapOrPath *path = (BitmapOrPath *) lfirst(k);
Assert(IsA(path, BitmapOrPath));
if (bestpath == NULL ||
path->path.total_cost < bestpath->path.total_cost)
{
bestpath = path;
bestrinfo = rinfo;
}
}
}
}
/* Fail if no suitable clauses found */
if (bestpath == NULL)
return false;
/*
* Convert the path's indexclauses structure to a RestrictInfo tree. We
* include any partial-index predicates so as to get a reasonable
* representation of what the path is actually scanning.
*/
newrinfos = make_restrictinfo_from_bitmapqual((Path *) bestpath,
true, true);
/* It's possible we get back something other than a single OR clause */
if (list_length(newrinfos) != 1)
return false;
or_rinfo = (RestrictInfo *) linitial(newrinfos);
Assert(IsA(or_rinfo, RestrictInfo));
if (!restriction_is_or_clause(or_rinfo))
return false;
/*
* OK, add it to the rel's restriction list.
*/
rel->baserestrictinfo = list_concat(rel->baserestrictinfo, newrinfos);
/*
* Adjust the original OR clause's cached selectivity to compensate for
* the selectivity of the added (but redundant) lower-level qual. This
* should result in the join rel getting approximately the same rows
* estimate as it would have gotten without all these shenanigans. (XXX
* major hack alert ... this depends on the assumption that the
* selectivity will stay cached ...)
*/
or_selec = clause_selectivity(root, (Node *) or_rinfo,
0, JOIN_INNER, NULL);
if (or_selec > 0 && or_selec < 1)
{
orig_selec = clause_selectivity(root, (Node *) bestrinfo,
0, JOIN_INNER, NULL);
bestrinfo->norm_selec = orig_selec / or_selec;
/* clamp result to sane range */
if (bestrinfo->norm_selec > 1)
bestrinfo->norm_selec = 1;
/* It isn't an outer join clause, so no need to adjust outer_selec */
}
/* Tell caller to recompute partial index status and rowcount estimate */
return true;
}
| void create_tidscan_paths | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel | |||
| ) |
Definition at line 250 of file tidpath.c.
References add_path(), RelOptInfo::baserestrictinfo, create_tidscan_path(), RelOptInfo::lateral_relids, RelOptInfo::relid, and TidQualFromRestrictinfo().
Referenced by set_plain_rel_pathlist().
{
Relids required_outer;
List *tidquals;
/*
* We don't support pushing join clauses into the quals of a tidscan, but
* it could still have required parameterization due to LATERAL refs in
* its tlist. (That can only happen if the tidscan is on a relation
* pulled up out of a UNION ALL appendrel.)
*/
required_outer = rel->lateral_relids;
tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
if (tidquals)
add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
required_outer));
}
| bool eclass_useful_for_merging | ( | EquivalenceClass * | eclass, | |
| RelOptInfo * | rel | |||
| ) |
Definition at line 2268 of file equivclass.c.
References Assert, bms_is_subset(), bms_overlap(), EquivalenceClass::ec_has_const, EquivalenceClass::ec_members, EquivalenceClass::ec_merged, EquivalenceClass::ec_relids, EquivalenceMember::em_is_child, EquivalenceMember::em_relids, lfirst, list_length(), and RelOptInfo::relids.
Referenced by pathkeys_useful_for_merging().
{
ListCell *lc;
Assert(!eclass->ec_merged);
/*
* Won't generate joinclauses if const or single-member (the latter test
* covers the volatile case too)
*/
if (eclass->ec_has_const || list_length(eclass->ec_members) <= 1)
return false;
/*
* Note we don't test ec_broken; if we did, we'd need a separate code path
* to look through ec_sources. Checking the members anyway is OK as a
* possibly-overoptimistic heuristic.
*/
/* If rel already includes all members of eclass, no point in searching */
if (bms_is_subset(eclass->ec_relids, rel->relids))
return false;
/* To join, we need a member not in the given rel */
foreach(lc, eclass->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
if (cur_em->em_is_child)
continue; /* ignore children here */
if (!bms_overlap(cur_em->em_relids, rel->relids))
return true;
}
return false;
}
| void expand_indexqual_conditions | ( | IndexOptInfo * | index, | |
| List * | indexclauses, | |||
| List * | indexclausecols, | |||
| List ** | indexquals_p, | |||
| List ** | indexqualcols_p | |||
| ) |
Definition at line 3278 of file indxpath.c.
References IndexOptInfo::amsearchnulls, Assert, RestrictInfo::clause, elog, ERROR, expand_boolean_index_clause(), expand_indexqual_opclause(), expand_indexqual_rowcompare(), forboth, IndexOptInfo::indexcollations, is_opclause, IsA, IsBooleanOpfamily, lappend(), lappend_int(), lfirst, lfirst_int, list_concat(), list_length(), make_simple_restrictinfo, nodeTag, and IndexOptInfo::opfamily.
Referenced by create_index_path().
{
List *indexquals = NIL;
List *indexqualcols = NIL;
ListCell *lcc,
*lci;
forboth(lcc, indexclauses, lci, indexclausecols)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
int indexcol = lfirst_int(lci);
Expr *clause = rinfo->clause;
Oid curFamily = index->opfamily[indexcol];
Oid curCollation = index->indexcollations[indexcol];
/* First check for boolean cases */
if (IsBooleanOpfamily(curFamily))
{
Expr *boolqual;
boolqual = expand_boolean_index_clause((Node *) clause,
indexcol,
index);
if (boolqual)
{
indexquals = lappend(indexquals,
make_simple_restrictinfo(boolqual));
indexqualcols = lappend_int(indexqualcols, indexcol);
continue;
}
}
/*
* Else it must be an opclause (usual case), ScalarArrayOp,
* RowCompare, or NullTest
*/
if (is_opclause(clause))
{
indexquals = list_concat(indexquals,
expand_indexqual_opclause(rinfo,
curFamily,
curCollation));
/* expand_indexqual_opclause can produce multiple clauses */
while (list_length(indexqualcols) < list_length(indexquals))
indexqualcols = lappend_int(indexqualcols, indexcol);
}
else if (IsA(clause, ScalarArrayOpExpr))
{
/* no extra work at this time */
indexquals = lappend(indexquals, rinfo);
indexqualcols = lappend_int(indexqualcols, indexcol);
}
else if (IsA(clause, RowCompareExpr))
{
indexquals = lappend(indexquals,
expand_indexqual_rowcompare(rinfo,
index,
indexcol));
indexqualcols = lappend_int(indexqualcols, indexcol);
}
else if (IsA(clause, NullTest))
{
Assert(index->amsearchnulls);
indexquals = lappend(indexquals, rinfo);
indexqualcols = lappend_int(indexqualcols, indexcol);
}
else
elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause));
}
*indexquals_p = indexquals;
*indexqualcols_p = indexqualcols;
}
| bool exprs_known_equal | ( | PlannerInfo * | root, | |
| Node * | item1, | |||
| Node * | item2 | |||
| ) |
Definition at line 1859 of file equivclass.c.
References EquivalenceClass::ec_has_volatile, EquivalenceClass::ec_members, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, PlannerInfo::eq_classes, equal(), and lfirst.
Referenced by add_unique_group_var().
{
ListCell *lc1;
foreach(lc1, root->eq_classes)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
bool item1member = false;
bool item2member = false;
ListCell *lc2;
/* Never match to a volatile EC */
if (ec->ec_has_volatile)
continue;
foreach(lc2, ec->ec_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
if (em->em_is_child)
continue; /* ignore children here */
if (equal(item1, em->em_expr))
item1member = true;
else if (equal(item2, em->em_expr))
item2member = true;
/* Exit as soon as equality is proven */
if (item1member && item2member)
return true;
}
}
return false;
}
| List* find_mergeclauses_for_pathkeys | ( | PlannerInfo * | root, | |
| List * | pathkeys, | |||
| bool | outer_keys, | |||
| List * | restrictinfos | |||
| ) |
Definition at line 890 of file pathkeys.c.
References i, lappend(), RestrictInfo::left_ec, lfirst, list_concat(), NIL, RestrictInfo::outer_is_left, PathKey::pk_eclass, RestrictInfo::right_ec, and update_mergeclause_eclasses().
Referenced by match_unsorted_outer(), and sort_inner_and_outer().
{
List *mergeclauses = NIL;
ListCell *i;
/* make sure we have eclasses cached in the clauses */
foreach(i, restrictinfos)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
update_mergeclause_eclasses(root, rinfo);
}
foreach(i, pathkeys)
{
PathKey *pathkey = (PathKey *) lfirst(i);
EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
List *matched_restrictinfos = NIL;
ListCell *j;
/*----------
* A mergejoin clause matches a pathkey if it has the same EC.
* If there are multiple matching clauses, take them all. In plain
* inner-join scenarios we expect only one match, because
* equivalence-class processing will have removed any redundant
* mergeclauses. However, in outer-join scenarios there might be
* multiple matches. An example is
*
* select * from a full join b
* on a.v1 = b.v1 and a.v2 = b.v2 and a.v1 = b.v2;
*
* Given the pathkeys ({a.v1}, {a.v2}) it is okay to return all three
* clauses (in the order a.v1=b.v1, a.v1=b.v2, a.v2=b.v2) and indeed
* we *must* do so or we will be unable to form a valid plan.
*
* We expect that the given pathkeys list is canonical, which means
* no two members have the same EC, so it's not possible for this
* code to enter the same mergeclause into the result list twice.
*
* It's possible that multiple matching clauses might have different
* ECs on the other side, in which case the order we put them into our
* result makes a difference in the pathkeys required for the other
* input path. However this routine hasn't got any info about which
* order would be best, so we don't worry about that.
*
* It's also possible that the selected mergejoin clauses produce
* a noncanonical ordering of pathkeys for the other side, ie, we
* might select clauses that reference b.v1, b.v2, b.v1 in that
* order. This is not harmful in itself, though it suggests that
* the clauses are partially redundant. Since it happens only with
* redundant query conditions, we don't bother to eliminate it.
* make_inner_pathkeys_for_merge() has to delete duplicates when
* it constructs the canonical pathkeys list, and we also have to
* deal with the case in create_mergejoin_plan().
*----------
*/
foreach(j, restrictinfos)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
EquivalenceClass *clause_ec;
if (outer_keys)
clause_ec = rinfo->outer_is_left ?
rinfo->left_ec : rinfo->right_ec;
else
clause_ec = rinfo->outer_is_left ?
rinfo->right_ec : rinfo->left_ec;
if (clause_ec == pathkey_ec)
matched_restrictinfos = lappend(matched_restrictinfos, rinfo);
}
/*
* 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_restrictinfos == NIL)
break;
/*
* If we did find usable mergeclause(s) for this sort-key position,
* add them to result list.
*/
mergeclauses = list_concat(mergeclauses, matched_restrictinfos);
}
return mergeclauses;
}
| void generate_base_implied_equalities | ( | PlannerInfo * | root | ) |
Definition at line 710 of file equivclass.c.
References Assert, EquivalenceClass::ec_broken, EquivalenceClass::ec_has_const, EquivalenceClass::ec_members, EquivalenceClass::ec_merged, PlannerInfo::eq_classes, generate_base_implied_equalities_broken(), generate_base_implied_equalities_const(), generate_base_implied_equalities_no_const(), RelOptInfo::has_eclass_joins, has_relevant_eclass_joinclause(), lfirst, list_length(), NULL, PlannerInfo::simple_rel_array, and PlannerInfo::simple_rel_array_size.
Referenced by query_planner().
{
ListCell *lc;
Index rti;
foreach(lc, root->eq_classes)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
Assert(ec->ec_merged == NULL); /* else shouldn't be in list */
Assert(!ec->ec_broken); /* not yet anyway... */
/* Single-member ECs won't generate any deductions */
if (list_length(ec->ec_members) <= 1)
continue;
if (ec->ec_has_const)
generate_base_implied_equalities_const(root, ec);
else
generate_base_implied_equalities_no_const(root, ec);
/* Recover if we failed to generate required derived clauses */
if (ec->ec_broken)
generate_base_implied_equalities_broken(root, ec);
}
/*
* This is also a handy place to mark base rels (which should all exist by
* now) with flags showing whether they have pending eclass joins.
*/
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *brel = root->simple_rel_array[rti];
if (brel == NULL)
continue;
brel->has_eclass_joins = has_relevant_eclass_joinclause(root, brel);
}
}
| List* generate_bitmap_or_paths | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | clauses, | |||
| List * | other_clauses, | |||
| bool | restriction_only | |||
| ) |
Definition at line 1184 of file indxpath.c.
References and_clause(), Assert, build_paths_for_OR(), choose_bitmap_and(), create_bitmap_or_path(), drop_indexable_join_clauses(), generate_bitmap_or_paths(), IsA, lappend(), lfirst, list_concat(), list_copy(), list_make1, NIL, RestrictInfo::orclause, and restriction_is_or_clause().
Referenced by create_index_paths(), create_or_index_quals(), and generate_bitmap_or_paths().
{
List *result = NIL;
List *all_clauses;
ListCell *lc;
/*
* We can use both the current and other clauses as context for
* build_paths_for_OR; no need to remove ORs from the lists.
*/
all_clauses = list_concat(list_copy(clauses), other_clauses);
foreach(lc, clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
List *pathlist;
Path *bitmapqual;
ListCell *j;
Assert(IsA(rinfo, RestrictInfo));
/* Ignore RestrictInfos that aren't ORs */
if (!restriction_is_or_clause(rinfo))
continue;
/*
* We must be able to match at least one index to each of the arms of
* the OR, else we can't use it.
*/
pathlist = NIL;
foreach(j, ((BoolExpr *) rinfo->orclause)->args)
{
Node *orarg = (Node *) lfirst(j);
List *indlist;
/* OR arguments should be ANDs or sub-RestrictInfos */
if (and_clause(orarg))
{
List *andargs = ((BoolExpr *) orarg)->args;
if (restriction_only)
andargs = drop_indexable_join_clauses(rel, andargs);
indlist = build_paths_for_OR(root, rel,
andargs,
all_clauses);
/* Recurse in case there are sub-ORs */
indlist = list_concat(indlist,
generate_bitmap_or_paths(root, rel,
andargs,
all_clauses,
restriction_only));
}
else
{
List *orargs;
Assert(IsA(orarg, RestrictInfo));
Assert(!restriction_is_or_clause((RestrictInfo *) orarg));
orargs = list_make1(orarg);
if (restriction_only)
orargs = drop_indexable_join_clauses(rel, orargs);
indlist = build_paths_for_OR(root, rel,
orargs,
all_clauses);
}
/*
* If nothing matched this arm, we can't do anything with this OR
* clause.
*/
if (indlist == NIL)
{
pathlist = NIL;
break;
}
/*
* OK, pick the most promising AND combination, and add it to
* pathlist.
*/
bitmapqual = choose_bitmap_and(root, rel, indlist);
pathlist = lappend(pathlist, bitmapqual);
}
/*
* If we have a match for every arm, then turn them into a
* BitmapOrPath, and add to result list.
*/
if (pathlist != NIL)
{
bitmapqual = (Path *) create_bitmap_or_path(root, rel, pathlist);
result = lappend(result, bitmapqual);
}
}
return result;
}
| List* generate_implied_equalities_for_column | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| ec_matches_callback_type | callback, | |||
| void * | callback_arg, | |||
| Relids | prohibited_rels | |||
| ) |
Definition at line 2047 of file equivclass.c.
References bms_equal(), bms_is_member(), bms_is_subset(), bms_overlap(), callback(), create_join_clause(), EquivalenceClass::ec_has_const, EquivalenceClass::ec_members, EquivalenceClass::ec_relids, EquivalenceMember::em_datatype, EquivalenceMember::em_is_child, EquivalenceMember::em_relids, PlannerInfo::eq_classes, find_childrel_appendrelinfo(), lappend(), lfirst, list_length(), OidIsValid, AppendRelInfo::parent_relid, RelOptInfo::relids, RelOptInfo::reloptkind, and select_equality_operator().
Referenced by match_eclass_clauses_to_index(), and postgresGetForeignPaths().
{
List *result = NIL;
bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
Index parent_relid;
ListCell *lc1;
/* If it's a child rel, we'll need to know what its parent is */
if (is_child_rel)
parent_relid = find_childrel_appendrelinfo(root, rel)->parent_relid;
else
parent_relid = 0; /* not used, but keep compiler quiet */
foreach(lc1, root->eq_classes)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
EquivalenceMember *cur_em;
ListCell *lc2;
/*
* Won't generate joinclauses if const or single-member (the latter
* test covers the volatile case too)
*/
if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
continue;
/*
* No point in searching if rel not mentioned in eclass (but we can't
* tell that for a child rel).
*/
if (!is_child_rel &&
!bms_is_subset(rel->relids, cur_ec->ec_relids))
continue;
/*
* Scan members, looking for a match to the target column. Note
* that child EC members are considered, but only when they belong to
* the target relation. (Unlike regular members, the same expression
* could be a child member of more than one EC. Therefore, it's
* potentially order-dependent which EC a child relation's target
* column gets matched to. This is annoying but it only happens in
* corner cases, so for now we live with just reporting the first
* match. See also get_eclass_for_sort_expr.)
*/
cur_em = NULL;
foreach(lc2, cur_ec->ec_members)
{
cur_em = (EquivalenceMember *) lfirst(lc2);
if (bms_equal(cur_em->em_relids, rel->relids) &&
callback(root, rel, cur_ec, cur_em, callback_arg))
break;
cur_em = NULL;
}
if (!cur_em)
continue;
/*
* Found our match. Scan the other EC members and attempt to generate
* joinclauses.
*/
foreach(lc2, cur_ec->ec_members)
{
EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);
Oid eq_op;
RestrictInfo *rinfo;
if (other_em->em_is_child)
continue; /* ignore children here */
/* Make sure it'll be a join to a different rel */
if (other_em == cur_em ||
bms_overlap(other_em->em_relids, rel->relids))
continue;
/* Forget it if caller doesn't want joins to this rel */
if (bms_overlap(other_em->em_relids, prohibited_rels))
continue;
/*
* Also, if this is a child rel, avoid generating a useless join
* to its parent rel.
*/
if (is_child_rel &&
bms_is_member(parent_relid, other_em->em_relids))
continue;
eq_op = select_equality_operator(cur_ec,
cur_em->em_datatype,
other_em->em_datatype);
if (!OidIsValid(eq_op))
continue;
/* set parent_ec to mark as redundant with other joinclauses */
rinfo = create_join_clause(root, cur_ec, eq_op,
cur_em, other_em,
cur_ec);
result = lappend(result, rinfo);
}
/*
* If somehow we failed to create any join clauses, we might as well
* keep scanning the ECs for another match. But if we did make any,
* we're done, because we don't want to return non-redundant clauses.
*/
if (result)
break;
}
return result;
}
| List* generate_join_implied_equalities | ( | PlannerInfo * | root, | |
| Relids | join_relids, | |||
| Relids | outer_relids, | |||
| RelOptInfo * | inner_rel | |||
| ) |
Definition at line 979 of file equivclass.c.
References bms_make_singleton(), bms_overlap(), bms_union(), EquivalenceClass::ec_broken, EquivalenceClass::ec_has_const, EquivalenceClass::ec_members, EquivalenceClass::ec_relids, PlannerInfo::eq_classes, find_childrel_appendrelinfo(), generate_join_implied_equalities_broken(), generate_join_implied_equalities_normal(), lfirst, list_concat(), list_length(), AppendRelInfo::parent_relid, RelOptInfo::relids, RELOPT_OTHER_MEMBER_REL, and RelOptInfo::reloptkind.
Referenced by build_joinrel_restrictlist(), check_partial_indexes(), get_baserel_parampathinfo(), and get_joinrel_parampathinfo().
{
List *result = NIL;
Relids inner_relids = inner_rel->relids;
Relids nominal_inner_relids;
Relids nominal_join_relids;
AppendRelInfo *inner_appinfo;
ListCell *lc;
/* If inner rel is a child, extra setup work is needed */
if (inner_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
{
/* Lookup parent->child translation data */
inner_appinfo = find_childrel_appendrelinfo(root, inner_rel);
/* Construct relids for the parent rel */
nominal_inner_relids = bms_make_singleton(inner_appinfo->parent_relid);
/* ECs will be marked with the parent's relid, not the child's */
nominal_join_relids = bms_union(outer_relids, nominal_inner_relids);
}
else
{
inner_appinfo = NULL;
nominal_inner_relids = inner_relids;
nominal_join_relids = join_relids;
}
foreach(lc, root->eq_classes)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
List *sublist = NIL;
/* ECs containing consts do not need any further enforcement */
if (ec->ec_has_const)
continue;
/* Single-member ECs won't generate any deductions */
if (list_length(ec->ec_members) <= 1)
continue;
/* We can quickly ignore any that don't overlap the join, too */
if (!bms_overlap(ec->ec_relids, nominal_join_relids))
continue;
if (!ec->ec_broken)
sublist = generate_join_implied_equalities_normal(root,
ec,
join_relids,
outer_relids,
inner_relids);
/* Recover if we failed to generate required derived clauses */
if (ec->ec_broken)
sublist = generate_join_implied_equalities_broken(root,
ec,
nominal_join_relids,
outer_relids,
nominal_inner_relids,
inner_appinfo);
result = list_concat(result, sublist);
}
return result;
}
| Path* get_cheapest_fractional_path_for_pathkeys | ( | List * | paths, | |
| List * | pathkeys, | |||
| Relids | required_outer, | |||
| double | fraction | |||
| ) |
Definition at line 380 of file pathkeys.c.
References bms_is_subset(), compare_fractional_path_costs(), lfirst, NULL, PATH_REQ_OUTER, Path::pathkeys, and pathkeys_contained_in().
Referenced by query_planner().
{
Path *matched_path = NULL;
ListCell *l;
foreach(l, paths)
{
Path *path = (Path *) lfirst(l);
/*
* Since cost comparison is a lot cheaper than pathkey comparison, do
* that first. (XXX is that still true?)
*/
if (matched_path != NULL &&
compare_fractional_path_costs(matched_path, path, fraction) <= 0)
continue;
if (pathkeys_contained_in(pathkeys, path->pathkeys) &&
bms_is_subset(PATH_REQ_OUTER(path), required_outer))
matched_path = path;
}
return matched_path;
}
| Path* get_cheapest_path_for_pathkeys | ( | List * | paths, | |
| List * | pathkeys, | |||
| Relids | required_outer, | |||
| CostSelector | cost_criterion | |||
| ) |
Definition at line 339 of file pathkeys.c.
References bms_is_subset(), compare_path_costs(), lfirst, NULL, PATH_REQ_OUTER, Path::pathkeys, and pathkeys_contained_in().
Referenced by generate_mergeappend_paths(), match_unsorted_outer(), and set_append_rel_pathlist().
{
Path *matched_path = NULL;
ListCell *l;
foreach(l, paths)
{
Path *path = (Path *) lfirst(l);
/*
* Since cost comparison is a lot cheaper than pathkey comparison, do
* that first. (XXX is that still true?)
*/
if (matched_path != NULL &&
compare_path_costs(matched_path, path, cost_criterion) <= 0)
continue;
if (pathkeys_contained_in(pathkeys, path->pathkeys) &&
bms_is_subset(PATH_REQ_OUTER(path), required_outer))
matched_path = path;
}
return matched_path;
}
| EquivalenceClass* get_eclass_for_sort_expr | ( | PlannerInfo * | root, | |
| Expr * | expr, | |||
| List * | opfamilies, | |||
| Oid | opcintype, | |||
| Oid | collation, | |||
| Index | sortref, | |||
| Relids | rel, | |||
| bool | create_it | |||
| ) |
Definition at line 539 of file equivclass.c.
References add_eq_member(), bms_equal(), canonicalize_ec_expression(), contain_agg_clause(), contain_volatile_functions(), contain_window_function(), copyObject(), EquivalenceClass::ec_below_outer_join, EquivalenceClass::ec_broken, EquivalenceClass::ec_collation, EquivalenceClass::ec_derives, EquivalenceClass::ec_has_const, EquivalenceClass::ec_has_volatile, EquivalenceClass::ec_members, EquivalenceClass::ec_merged, EquivalenceClass::ec_opfamilies, EquivalenceClass::ec_relids, EquivalenceClass::ec_sortref, EquivalenceClass::ec_sources, elog, EquivalenceMember::em_datatype, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, EquivalenceMember::em_is_const, EquivalenceMember::em_relids, PlannerInfo::eq_classes, equal(), ERROR, expression_returns_set(), lappend(), lfirst, list_copy(), makeNode, MemoryContextSwitchTo(), NULL, PlannerInfo::planner_cxt, and pull_varnos().
Referenced by convert_subquery_pathkeys(), initialize_mergeclause_eclasses(), and make_pathkey_from_sortinfo().
{
EquivalenceClass *newec;
EquivalenceMember *newem;
ListCell *lc1;
MemoryContext oldcontext;
/*
* Ensure the expression exposes the correct type and collation.
*/
expr = canonicalize_ec_expression(expr, opcintype, collation);
/*
* Scan through the existing EquivalenceClasses for a match
*/
foreach(lc1, root->eq_classes)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
ListCell *lc2;
/*
* Never match to a volatile EC, except when we are looking at another
* reference to the same volatile SortGroupClause.
*/
if (cur_ec->ec_has_volatile &&
(sortref == 0 || sortref != cur_ec->ec_sortref))
continue;
if (collation != cur_ec->ec_collation)
continue;
if (!equal(opfamilies, cur_ec->ec_opfamilies))
continue;
foreach(lc2, cur_ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
/*
* Ignore child members unless they match the request.
*/
if (cur_em->em_is_child &&
!bms_equal(cur_em->em_relids, rel))
continue;
/*
* If below an outer join, don't match constants: they're not as
* constant as they look.
*/
if (cur_ec->ec_below_outer_join &&
cur_em->em_is_const)
continue;
if (opcintype == cur_em->em_datatype &&
equal(expr, cur_em->em_expr))
return cur_ec; /* Match! */
}
}
/* No match; does caller want a NULL result? */
if (!create_it)
return NULL;
/*
* OK, build a new single-member EC
*
* Here, we must be sure that we construct the EC in the right context.
*/
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
newec = makeNode(EquivalenceClass);
newec->ec_opfamilies = list_copy(opfamilies);
newec->ec_collation = collation;
newec->ec_members = NIL;
newec->ec_sources = NIL;
newec->ec_derives = NIL;
newec->ec_relids = NULL;
newec->ec_has_const = false;
newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
newec->ec_below_outer_join = false;
newec->ec_broken = false;
newec->ec_sortref = sortref;
newec->ec_merged = NULL;
if (newec->ec_has_volatile && sortref == 0) /* should not happen */
elog(ERROR, "volatile EquivalenceClass has no sortref");
newem = add_eq_member(newec, copyObject(expr), pull_varnos((Node *) expr),
NULL, false, opcintype);
/*
* add_eq_member doesn't check for volatile functions, set-returning
* functions, aggregates, or window functions, but such could appear in
* sort expressions; so we have to check whether its const-marking was
* correct.
*/
if (newec->ec_has_const)
{
if (newec->ec_has_volatile ||
expression_returns_set((Node *) expr) ||
contain_agg_clause((Node *) expr) ||
contain_window_function((Node *) expr))
{
newec->ec_has_const = false;
newem->em_is_const = false;
}
}
root->eq_classes = lappend(root->eq_classes, newec);
MemoryContextSwitchTo(oldcontext);
return newec;
}
| bool has_relevant_eclass_joinclause | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel1 | |||
| ) |
Definition at line 2229 of file equivclass.c.
References bms_is_subset(), bms_overlap(), EquivalenceClass::ec_members, EquivalenceClass::ec_relids, PlannerInfo::eq_classes, lfirst, list_length(), and RelOptInfo::relids.
Referenced by build_join_rel(), and generate_base_implied_equalities().
{
ListCell *lc1;
foreach(lc1, root->eq_classes)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
/*
* Won't generate joinclauses if single-member (this test covers the
* volatile case too)
*/
if (list_length(ec->ec_members) <= 1)
continue;
/*
* Per the comment in have_relevant_eclass_joinclause, it's sufficient
* to find an EC that mentions both this rel and some other rel.
*/
if (bms_overlap(rel1->relids, ec->ec_relids) &&
!bms_is_subset(ec->ec_relids, rel1->relids))
return true;
}
return false;
}
| bool has_useful_pathkeys | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel | |||
| ) |
Definition at line 1445 of file pathkeys.c.
References RelOptInfo::has_eclass_joins, RelOptInfo::joininfo, NIL, and PlannerInfo::query_pathkeys.
Referenced by build_index_paths(), and set_append_rel_size().
{
if (rel->joininfo != NIL || rel->has_eclass_joins)
return true; /* might be able to use pathkeys for merging */
if (root->query_pathkeys != NIL)
return true; /* might be able to use them for ordering */
return false; /* definitely useless */
}
| bool have_join_order_restriction | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel1, | |||
| RelOptInfo * | rel2 | |||
| ) |
Definition at line 818 of file joinrels.c.
References bms_is_member(), bms_is_subset(), bms_overlap(), has_legal_joinclause(), JOIN_FULL, PlannerInfo::join_info_list, SpecialJoinInfo::jointype, PlannerInfo::lateral_info_list, LateralJoinInfo::lateral_lhs, LateralJoinInfo::lateral_rhs, lfirst, SpecialJoinInfo::min_lefthand, SpecialJoinInfo::min_righthand, and RelOptInfo::relids.
Referenced by desirable_join(), join_search_one_level(), and make_rels_by_clause_joins().
{
bool result = false;
ListCell *l;
/*
* If either side has a lateral reference to the other, attempt the join
* regardless of outer-join considerations.
*/
foreach(l, root->lateral_info_list)
{
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
if (bms_is_member(ljinfo->lateral_rhs, rel2->relids) &&
bms_overlap(ljinfo->lateral_lhs, rel1->relids))
return true;
if (bms_is_member(ljinfo->lateral_rhs, rel1->relids) &&
bms_overlap(ljinfo->lateral_lhs, rel2->relids))
return true;
}
/*
* It's possible that the rels correspond to the left and right sides of a
* degenerate outer join, that is, one with no joinclause mentioning the
* non-nullable side; in which case we should force the join to occur.
*
* Also, the two rels could represent a clauseless join that has to be
* completed to build up the LHS or RHS of an outer join.
*/
foreach(l, root->join_info_list)
{
SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l);
/* ignore full joins --- other mechanisms handle them */
if (sjinfo->jointype == JOIN_FULL)
continue;
/* Can we perform the SJ with these rels? */
if (bms_is_subset(sjinfo->min_lefthand, rel1->relids) &&
bms_is_subset(sjinfo->min_righthand, rel2->relids))
{
result = true;
break;
}
if (bms_is_subset(sjinfo->min_lefthand, rel2->relids) &&
bms_is_subset(sjinfo->min_righthand, rel1->relids))
{
result = true;
break;
}
/*
* Might we need to join these rels to complete the RHS? We have to
* use "overlap" tests since either rel might include a lower SJ that
* has been proven to commute with this one.
*/
if (bms_overlap(sjinfo->min_righthand, rel1->relids) &&
bms_overlap(sjinfo->min_righthand, rel2->relids))
{
result = true;
break;
}
/* Likewise for the LHS. */
if (bms_overlap(sjinfo->min_lefthand, rel1->relids) &&
bms_overlap(sjinfo->min_lefthand, rel2->relids))
{
result = true;
break;
}
}
/*
* We do not force the join to occur if either input rel can legally be
* joined to anything else using joinclauses. This essentially means that
* clauseless bushy joins are put off as long as possible. The reason is
* that when there is a join order restriction high up in the join tree
* (that is, with many rels inside the LHS or RHS), we would otherwise
* expend lots of effort considering very stupid join combinations within
* its LHS or RHS.
*/
if (result)
{
if (has_legal_joinclause(root, rel1) ||
has_legal_joinclause(root, rel2))
result = false;
}
return result;
}
| bool have_relevant_eclass_joinclause | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel1, | |||
| RelOptInfo * | rel2 | |||
| ) |
Definition at line 2175 of file equivclass.c.
References bms_overlap(), EquivalenceClass::ec_members, EquivalenceClass::ec_relids, PlannerInfo::eq_classes, lfirst, list_length(), and RelOptInfo::relids.
Referenced by have_relevant_joinclause().
{
ListCell *lc1;
foreach(lc1, root->eq_classes)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
/*
* Won't generate joinclauses if single-member (this test covers the
* volatile case too)
*/
if (list_length(ec->ec_members) <= 1)
continue;
/*
* We do not need to examine the individual members of the EC, because
* all that we care about is whether each rel overlaps the relids of
* at least one member, and a test on ec_relids is sufficient to prove
* that. (As with have_relevant_joinclause(), it is not necessary
* that the EC be able to form a joinclause relating exactly the two
* given rels, only that it be able to form a joinclause mentioning
* both, and this will surely be true if both of them overlap
* ec_relids.)
*
* Note we don't test ec_broken; if we did, we'd need a separate code
* path to look through ec_sources. Checking the membership anyway is
* OK as a possibly-overoptimistic heuristic.
*
* We don't test ec_has_const either, even though a const eclass won't
* generate real join clauses. This is because if we had "WHERE a.x =
* b.y and a.x = 42", it is worth considering a join between a and b,
* since the join result is likely to be small even though it'll end
* up being an unqualified nestloop.
*/
if (bms_overlap(rel1->relids, ec->ec_relids) &&
bms_overlap(rel2->relids, ec->ec_relids))
return true;
}
return false;
}
| void initialize_mergeclause_eclasses | ( | PlannerInfo * | root, | |
| RestrictInfo * | restrictinfo | |||
| ) |
Definition at line 808 of file pathkeys.c.
References Assert, RestrictInfo::clause, get_eclass_for_sort_expr(), get_leftop(), get_rightop(), RestrictInfo::left_ec, RestrictInfo::mergeopfamilies, NIL, NULL, op_input_types(), and RestrictInfo::right_ec.
Referenced by distribute_qual_to_rels().
{
Expr *clause = restrictinfo->clause;
Oid lefttype,
righttype;
/* Should be a mergeclause ... */
Assert(restrictinfo->mergeopfamilies != NIL);
/* ... with links not yet set */
Assert(restrictinfo->left_ec == NULL);
Assert(restrictinfo->right_ec == NULL);
/* Need the declared input types of the operator */
op_input_types(((OpExpr *) clause)->opno, &lefttype, &righttype);
/* Find or create a matching EquivalenceClass for each side */
restrictinfo->left_ec =
get_eclass_for_sort_expr(root,
(Expr *) get_leftop(clause),
restrictinfo->mergeopfamilies,
lefttype,
((OpExpr *) clause)->inputcollid,
0,
NULL,
true);
restrictinfo->right_ec =
get_eclass_for_sort_expr(root,
(Expr *) get_rightop(clause),
restrictinfo->mergeopfamilies,
righttype,
((OpExpr *) clause)->inputcollid,
0,
NULL,
true);
}
| bool is_redundant_derived_clause | ( | RestrictInfo * | rinfo, | |
| List * | clauselist | |||
| ) |
Definition at line 2315 of file equivclass.c.
References lfirst, NULL, and RestrictInfo::parent_ec.
Referenced by create_indexscan_plan(), and has_indexed_join_quals().
{
EquivalenceClass *parent_ec = rinfo->parent_ec;
ListCell *lc;
/* Fail if it's not a potentially-redundant clause from some EC */
if (parent_ec == NULL)
return false;
foreach(lc, clauselist)
{
RestrictInfo *otherrinfo = (RestrictInfo *) lfirst(lc);
if (otherrinfo->parent_ec == parent_ec)
return true;
}
return false;
}
| void join_search_one_level | ( | PlannerInfo * | root, | |
| int | level | |||
| ) |
Definition at line 51 of file joinrels.c.
References Assert, bms_overlap(), elog, ERROR, for_each_cell, RelOptInfo::has_eclass_joins, has_join_restriction(), have_join_order_restriction(), have_relevant_joinclause(), PlannerInfo::join_cur_level, PlannerInfo::join_info_list, PlannerInfo::join_rel_level, RelOptInfo::joininfo, PlannerInfo::lateral_info_list, lfirst, list_head(), lnext, make_join_rel(), make_rels_by_clause_joins(), make_rels_by_clauseless_joins(), NIL, and RelOptInfo::relids.
Referenced by standard_join_search().
{
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]));
}
}
/*
* Now, consider "bushy plans" in which relations of k initial rels are
* joined to relations of level-k initial rels, for 2 <= k <= level-2.
*
* We only consider bushy-plan joins for pairs of rels where there is a
* suitable join clause (or join order restriction), in order to avoid
* unreasonable growth of planning time.
*/
for (k = 2;; k++)
{
int other_level = level - k;
/*
* Since make_join_rel(x, y) handles both x,y and y,x cases, we only
* need to go as far as the halfway point.
*/
if (k > other_level)
break;
foreach(r, joinrels[k])
{
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
ListCell *other_rels;
ListCell *r2;
/*
* We can ignore relations without join clauses here, unless they
* participate in join-order restrictions --- then we might have
* to force a bushy join plan.
*/
if (old_rel->joininfo == NIL && !old_rel->has_eclass_joins &&
!has_join_restriction(root, old_rel))
continue;
if (k == other_level)
other_rels = lnext(r); /* only consider remaining rels */
else
other_rels = list_head(joinrels[other_level]);
for_each_cell(r2, other_rels)
{
RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
if (!bms_overlap(old_rel->relids, new_rel->relids))
{
/*
* OK, we can build a rel of the right level from this
* pair of rels. Do so if there is at least one relevant
* join clause or join order restriction.
*/
if (have_relevant_joinclause(root, old_rel, new_rel) ||
have_join_order_restriction(root, old_rel, new_rel))
{
(void) make_join_rel(root, old_rel, new_rel);
}
}
}
}
}
/*----------
* Last-ditch effort: if we failed to find any usable joins so far, force
* a set of cartesian-product joins to be generated. This handles the
* special case where all the available rels have join clauses but we
* cannot use any of those clauses yet. This can only happen when we are
* considering a join sub-problem (a sub-joinlist) and all the rels in the
* sub-problem have only join clauses with rels outside the sub-problem.
* An example is
*
* SELECT ... FROM a INNER JOIN b ON TRUE, c, d, ...
* WHERE a.w = c.x and b.y = d.z;
*
* If the "a INNER JOIN b" sub-problem does not get flattened into the
* upper level, we must be willing to make a cartesian join of a and b;
* but the code above will not have done so, because it thought that both
* a and b have joinclauses. We consider only left-sided and right-sided
* cartesian joins in this case (no bushy).
*----------
*/
if (joinrels[level] == NIL)
{
/*
* This loop is just like the first one, except we always call
* make_rels_by_clauseless_joins().
*/
foreach(r, joinrels[level - 1])
{
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
make_rels_by_clauseless_joins(root,
old_rel,
list_head(joinrels[1]));
}
/*----------
* When special joins are involved, there may be no legal way
* to make an N-way join for some values of N. For example consider
*
* SELECT ... FROM t1 WHERE
* x IN (SELECT ... FROM t2,t3 WHERE ...) AND
* y IN (SELECT ... FROM t4,t5 WHERE ...)
*
* We will flatten this query to a 5-way join problem, but there are
* no 4-way joins that join_is_legal() will consider legal. We have
* to accept failure at level 4 and go on to discover a workable
* bushy plan at level 5.
*
* However, if there are no special joins and no lateral references
* then join_is_legal() should never fail, and so the following sanity
* check is useful.
*----------
*/
if (joinrels[level] == NIL &&
root->join_info_list == NIL &&
root->lateral_info_list == NIL)
elog(ERROR, "failed to build any %d-way joins", level);
}
}
| List* make_inner_pathkeys_for_merge | ( | PlannerInfo * | root, | |
| List * | mergeclauses, | |||
| List * | outer_pathkeys | |||
| ) |
Definition at line 1179 of file pathkeys.c.
References elog, ERROR, lappend(), RestrictInfo::left_ec, lfirst, list_head(), lnext, make_canonical_pathkey(), RestrictInfo::outer_is_left, pathkey_is_redundant(), PathKey::pk_eclass, PathKey::pk_nulls_first, PathKey::pk_opfamily, PathKey::pk_strategy, RestrictInfo::right_ec, and update_mergeclause_eclasses().
Referenced by match_unsorted_outer(), and sort_inner_and_outer().
{
List *pathkeys = NIL;
EquivalenceClass *lastoeclass;
PathKey *opathkey;
ListCell *lc;
ListCell *lop;
lastoeclass = NULL;
opathkey = NULL;
lop = list_head(outer_pathkeys);
foreach(lc, mergeclauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
EquivalenceClass *oeclass;
EquivalenceClass *ieclass;
PathKey *pathkey;
update_mergeclause_eclasses(root, rinfo);
if (rinfo->outer_is_left)
{
oeclass = rinfo->left_ec;
ieclass = rinfo->right_ec;
}
else
{
oeclass = rinfo->right_ec;
ieclass = rinfo->left_ec;
}
/* outer eclass should match current or next pathkeys */
/* we check this carefully for debugging reasons */
if (oeclass != lastoeclass)
{
if (!lop)
elog(ERROR, "too few pathkeys for mergeclauses");
opathkey = (PathKey *) lfirst(lop);
lop = lnext(lop);
lastoeclass = opathkey->pk_eclass;
if (oeclass != lastoeclass)
elog(ERROR, "outer pathkeys do not match mergeclause");
}
/*
* Often, we'll have same EC on both sides, in which case the outer
* pathkey is also canonical for the inner side, and we can skip a
* useless search.
*/
if (ieclass == oeclass)
pathkey = opathkey;
else
pathkey = make_canonical_pathkey(root,
ieclass,
opathkey->pk_opfamily,
opathkey->pk_strategy,
opathkey->pk_nulls_first);
/*
* Don't generate redundant pathkeys (can happen if multiple
* mergeclauses refer to same EC).
*/
if (!pathkey_is_redundant(pathkey, pathkeys))
pathkeys = lappend(pathkeys, pathkey);
}
return pathkeys;
}
| RelOptInfo* make_join_rel | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel1, | |||
| RelOptInfo * | rel2 | |||
| ) |
Definition at line 577 of file joinrels.c.
References add_paths_to_joinrel(), Assert, bms_equal(), bms_free(), bms_is_subset(), bms_overlap(), bms_union(), build_join_rel(), RelOptInfo::cheapest_total_path, create_unique_path(), SpecialJoinInfo::delay_upper_joins, elog, ereport, errcode(), errmsg(), ERROR, is_dummy_rel(), JOIN_ANTI, JOIN_FULL, JOIN_INNER, join_is_legal(), JOIN_LEFT, SpecialJoinInfo::join_quals, JOIN_RIGHT, JOIN_SEMI, JOIN_UNIQUE_INNER, JOIN_UNIQUE_OUTER, SpecialJoinInfo::jointype, SpecialJoinInfo::lhs_strict, mark_dummy_rel(), SpecialJoinInfo::min_lefthand, SpecialJoinInfo::min_righthand, NIL, NULL, RelOptInfo::pathlist, RelOptInfo::relids, restriction_is_constant_false(), SpecialJoinInfo::syn_lefthand, SpecialJoinInfo::syn_righthand, and SpecialJoinInfo::type.
Referenced by join_search_one_level(), make_rels_by_clause_joins(), make_rels_by_clauseless_joins(), and merge_clump().
{
Relids joinrelids;
SpecialJoinInfo *sjinfo;
bool reversed;
SpecialJoinInfo sjinfo_data;
RelOptInfo *joinrel;
List *restrictlist;
/* We should never try to join two overlapping sets of rels. */
Assert(!bms_overlap(rel1->relids, rel2->relids));
/* Construct Relids set that identifies the joinrel. */
joinrelids = bms_union(rel1->relids, rel2->relids);
/* Check validity and determine join type. */
if (!join_is_legal(root, rel1, rel2, joinrelids,
&sjinfo, &reversed))
{
/* invalid join path */
bms_free(joinrelids);
return NULL;
}
/* Swap rels if needed to match the join info. */
if (reversed)
{
RelOptInfo *trel = rel1;
rel1 = rel2;
rel2 = trel;
}
/*
* If it's a plain inner join, then we won't have found anything in
* join_info_list. Make up a SpecialJoinInfo so that selectivity
* estimation functions will know what's being joined.
*/
if (sjinfo == NULL)
{
sjinfo = &sjinfo_data;
sjinfo->type = T_SpecialJoinInfo;
sjinfo->min_lefthand = rel1->relids;
sjinfo->min_righthand = rel2->relids;
sjinfo->syn_lefthand = rel1->relids;
sjinfo->syn_righthand = rel2->relids;
sjinfo->jointype = JOIN_INNER;
/* we don't bother trying to make the remaining fields valid */
sjinfo->lhs_strict = false;
sjinfo->delay_upper_joins = false;
sjinfo->join_quals = NIL;
}
/*
* Find or build the join RelOptInfo, and compute the restrictlist that
* goes with this particular joining.
*/
joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,
&restrictlist);
/*
* If we've already proven this join is empty, we needn't consider any
* more paths for it.
*/
if (is_dummy_rel(joinrel))
{
bms_free(joinrelids);
return joinrel;
}
/*
* Consider paths using each rel as both outer and inner. Depending on
* the join type, a provably empty outer or inner rel might mean the join
* is provably empty too; in which case throw away any previously computed
* paths and mark the join as dummy. (We do it this way since it's
* conceivable that dummy-ness of a multi-element join might only be
* noticeable for certain construction paths.)
*
* Also, a provably constant-false join restriction typically means that
* we can skip evaluating one or both sides of the join. We do this by
* marking the appropriate rel as dummy. For outer joins, a
* constant-false restriction that is pushed down still means the whole
* join is dummy, while a non-pushed-down one means that no inner rows
* will join so we can treat the inner rel as dummy.
*
* We need only consider the jointypes that appear in join_info_list, plus
* JOIN_INNER.
*/
switch (sjinfo->jointype)
{
case JOIN_INNER:
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
restriction_is_constant_false(restrictlist, false))
{
mark_dummy_rel(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_INNER, sjinfo,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_INNER, sjinfo,
restrictlist);
break;
case JOIN_LEFT:
if (is_dummy_rel(rel1) ||
restriction_is_constant_false(restrictlist, true))
{
mark_dummy_rel(joinrel);
break;
}
if (restriction_is_constant_false(restrictlist, false) &&
bms_is_subset(rel2->relids, sjinfo->syn_righthand))
mark_dummy_rel(rel2);
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_LEFT, sjinfo,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_RIGHT, sjinfo,
restrictlist);
break;
case JOIN_FULL:
if ((is_dummy_rel(rel1) && is_dummy_rel(rel2)) ||
restriction_is_constant_false(restrictlist, true))
{
mark_dummy_rel(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_FULL, sjinfo,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_FULL, sjinfo,
restrictlist);
/*
* If there are join quals that aren't mergeable or hashable, we
* may not be able to build any valid plan. Complain here so that
* we can give a somewhat-useful error message. (Since we have no
* flexibility of planning for a full join, there's no chance of
* succeeding later with another pair of input rels.)
*/
if (joinrel->pathlist == NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("FULL JOIN is only supported with merge-joinable or hash-joinable join conditions")));
break;
case JOIN_SEMI:
/*
* We might have a normal semijoin, or a case where we don't have
* enough rels to do the semijoin but can unique-ify the RHS and
* then do an innerjoin (see comments in join_is_legal). In the
* latter case we can't apply JOIN_SEMI joining.
*/
if (bms_is_subset(sjinfo->min_lefthand, rel1->relids) &&
bms_is_subset(sjinfo->min_righthand, rel2->relids))
{
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
restriction_is_constant_false(restrictlist, false))
{
mark_dummy_rel(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_SEMI, sjinfo,
restrictlist);
}
/*
* If we know how to unique-ify the RHS and one input rel is
* exactly the RHS (not a superset) we can consider unique-ifying
* it and then doing a regular join. (The create_unique_path
* check here is probably redundant with what join_is_legal did,
* but if so the check is cheap because it's cached. So test
* anyway to be sure.)
*/
if (bms_equal(sjinfo->syn_righthand, rel2->relids) &&
create_unique_path(root, rel2, rel2->cheapest_total_path,
sjinfo) != NULL)
{
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
restriction_is_constant_false(restrictlist, false))
{
mark_dummy_rel(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_UNIQUE_INNER, sjinfo,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_UNIQUE_OUTER, sjinfo,
restrictlist);
}
break;
case JOIN_ANTI:
if (is_dummy_rel(rel1) ||
restriction_is_constant_false(restrictlist, true))
{
mark_dummy_rel(joinrel);
break;
}
if (restriction_is_constant_false(restrictlist, false) &&
bms_is_subset(rel2->relids, sjinfo->syn_righthand))
mark_dummy_rel(rel2);
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_ANTI, sjinfo,
restrictlist);
break;
default:
/* other values not expected here */
elog(ERROR, "unrecognized join type: %d", (int) sjinfo->jointype);
break;
}
bms_free(joinrelids);
return joinrel;
}
| RelOptInfo* make_one_rel | ( | PlannerInfo * | root, | |
| List * | joinlist | |||
| ) |
Definition at line 104 of file allpaths.c.
References PlannerInfo::all_baserels, Assert, bms_add_member(), bms_equal(), make_rel_from_joinlist(), NULL, RelOptInfo::relid, RelOptInfo::relids, RELOPT_BASEREL, RelOptInfo::reloptkind, set_base_rel_pathlists(), set_base_rel_sizes(), PlannerInfo::simple_rel_array, and PlannerInfo::simple_rel_array_size.
Referenced by query_planner().
{
RelOptInfo *rel;
Index rti;
/*
* Construct the all_baserels Relids set.
*/
root->all_baserels = NULL;
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *brel = root->simple_rel_array[rti];
/* there may be empty slots corresponding to non-baserel RTEs */
if (brel == NULL)
continue;
Assert(brel->relid == rti); /* sanity check on array */
/* ignore RTEs that are "other rels" */
if (brel->reloptkind != RELOPT_BASEREL)
continue;
root->all_baserels = bms_add_member(root->all_baserels, brel->relid);
}
/*
* Generate access paths for the base rels.
*/
set_base_rel_sizes(root);
set_base_rel_pathlists(root);
/*
* Generate access paths for the entire join tree.
*/
rel = make_rel_from_joinlist(root, joinlist);
/*
* The result should join all and only the query's base rels.
*/
Assert(bms_equal(rel->relids, root->all_baserels));
return rel;
}
| List* make_pathkeys_for_sortclauses | ( | PlannerInfo * | root, | |
| List * | sortclauses, | |||
| List * | tlist | |||
| ) |
Definition at line 755 of file pathkeys.c.
References Assert, get_sortgroupclause_expr(), lappend(), lfirst, make_pathkey_from_sortop(), SortGroupClause::nulls_first, OidIsValid, pathkey_is_redundant(), SortGroupClause::sortop, and SortGroupClause::tleSortGroupRef.
Referenced by get_column_info_for_window(), grouping_planner(), make_pathkeys_for_window(), minmax_qp_callback(), and standard_qp_callback().
{
List *pathkeys = NIL;
ListCell *l;
foreach(l, sortclauses)
{
SortGroupClause *sortcl = (SortGroupClause *) lfirst(l);
Expr *sortkey;
PathKey *pathkey;
sortkey = (Expr *) get_sortgroupclause_expr(sortcl, tlist);
Assert(OidIsValid(sortcl->sortop));
pathkey = make_pathkey_from_sortop(root,
sortkey,
sortcl->sortop,
sortcl->nulls_first,
sortcl->tleSortGroupRef,
true);
/* Canonical form eliminates redundant ordering keys */
if (!pathkey_is_redundant(pathkey, pathkeys))
pathkeys = lappend(pathkeys, pathkey);
}
return pathkeys;
}
| bool match_index_to_operand | ( | Node * | operand, | |
| int | indexcol, | |||
| IndexOptInfo * | index | |||
| ) |
Definition at line 2932 of file indxpath.c.
References arg, elog, equal(), ERROR, i, IndexOptInfo::indexkeys, IndexOptInfo::indexprs, IsA, lfirst, list_head(), lnext, NULL, IndexOptInfo::rel, and RelOptInfo::relid.
Referenced by adjust_rowcompare_for_index(), btcostestimate(), ec_member_matches_indexcol(), expand_boolean_index_clause(), find_index_column(), get_actual_variable_range(), match_boolean_index_clause(), match_clause_to_indexcol(), match_clause_to_ordering_op(), match_rowcompare_to_indexcol(), and relation_has_unique_index_for().
{
int indkey;
/*
* Ignore any RelabelType node above the operand. This is needed to be
* able to apply indexscanning in binary-compatible-operator cases. Note:
* we can assume there is at most one RelabelType node;
* eval_const_expressions() will have simplified if more than one.
*/
if (operand && IsA(operand, RelabelType))
operand = (Node *) ((RelabelType *) operand)->arg;
indkey = index->indexkeys[indexcol];
if (indkey != 0)
{
/*
* Simple index column; operand must be a matching Var.
*/
if (operand && IsA(operand, Var) &&
index->rel->relid == ((Var *) operand)->varno &&
indkey == ((Var *) operand)->varattno)
return true;
}
else
{
/*
* Index expression; find the correct expression. (This search could
* be avoided, at the cost of complicating all the callers of this
* routine; doesn't seem worth it.)
*/
ListCell *indexpr_item;
int i;
Node *indexkey;
indexpr_item = list_head(index->indexprs);
for (i = 0; i < indexcol; i++)
{
if (index->indexkeys[i] == 0)
{
if (indexpr_item == NULL)
elog(ERROR, "wrong number of index expressions");
indexpr_item = lnext(indexpr_item);
}
}
if (indexpr_item == NULL)
elog(ERROR, "wrong number of index expressions");
indexkey = (Node *) lfirst(indexpr_item);
/*
* Does it match the operand? Again, strip any relabeling.
*/
if (indexkey && IsA(indexkey, RelabelType))
indexkey = (Node *) ((RelabelType *) indexkey)->arg;
if (equal(indexkey, operand))
return true;
}
return false;
}
| void mutate_eclass_expressions | ( | PlannerInfo * | root, | |
| Node *(*)() | mutator, | |||
| void * | context, | |||
| bool | include_child_exprs | |||
| ) |
Definition at line 1997 of file equivclass.c.
References EquivalenceClass::ec_members, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, PlannerInfo::eq_classes, and lfirst.
Referenced by optimize_minmax_aggregates().
{
ListCell *lc1;
foreach(lc1, root->eq_classes)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
ListCell *lc2;
foreach(lc2, cur_ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
if (cur_em->em_is_child && !include_child_exprs)
continue; /* ignore children unless requested */
cur_em->em_expr = (Expr *)
mutator((Node *) cur_em->em_expr, context);
}
}
}
Definition at line 314 of file pathkeys.c.
References compare_pathkeys(), PATHKEYS_BETTER2, and PATHKEYS_EQUAL.
Referenced by build_minmax_path(), choose_hashed_distinct(), choose_hashed_grouping(), create_merge_append_path(), create_merge_append_plan(), get_cheapest_fractional_path_for_pathkeys(), get_cheapest_path_for_pathkeys(), grouping_planner(), match_unsorted_outer(), pathkeys_useful_for_ordering(), query_planner(), and try_mergejoin_path().
{
switch (compare_pathkeys(keys1, keys2))
{
case PATHKEYS_EQUAL:
case PATHKEYS_BETTER2:
return true;
default:
break;
}
return false;
}
| bool process_equivalence | ( | PlannerInfo * | root, | |
| RestrictInfo * | restrictinfo, | |||
| bool | below_outer_join | |||
| ) |
Definition at line 98 of file equivclass.c.
References add_eq_member(), Assert, bms_intersect(), bms_is_empty(), bms_join(), PlannerInfo::canon_pathkeys, canonicalize_ec_expression(), RestrictInfo::clause, contain_nonstrict_functions(), EquivalenceClass::ec_below_outer_join, EquivalenceClass::ec_broken, EquivalenceClass::ec_collation, EquivalenceClass::ec_derives, EquivalenceClass::ec_has_const, EquivalenceClass::ec_has_volatile, EquivalenceClass::ec_members, EquivalenceClass::ec_merged, EquivalenceClass::ec_opfamilies, EquivalenceClass::ec_relids, EquivalenceClass::ec_sortref, EquivalenceClass::ec_sources, elog, EquivalenceMember::em_datatype, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, EquivalenceMember::em_is_const, PlannerInfo::eq_classes, equal(), ERROR, exprType(), get_leftop(), get_rightop(), is_opclause, lappend(), RestrictInfo::left_ec, RestrictInfo::left_em, RestrictInfo::left_relids, lfirst, list_concat(), list_delete_ptr(), list_make1, makeNode, RestrictInfo::mergeopfamilies, NIL, NULL, RestrictInfo::nullable_relids, op_input_types(), RestrictInfo::right_ec, RestrictInfo::right_em, and RestrictInfo::right_relids.
Referenced by distribute_qual_to_rels(), reconsider_full_join_clause(), and reconsider_outer_join_clause().
{
Expr *clause = restrictinfo->clause;
Oid opno,
collation,
item1_type,
item2_type;
Expr *item1;
Expr *item2;
Relids item1_relids,
item2_relids,
item1_nullable_relids,
item2_nullable_relids;
List *opfamilies;
EquivalenceClass *ec1,
*ec2;
EquivalenceMember *em1,
*em2;
ListCell *lc1;
/* Should not already be marked as having generated an eclass */
Assert(restrictinfo->left_ec == NULL);
Assert(restrictinfo->right_ec == NULL);
/* Extract info from given clause */
Assert(is_opclause(clause));
opno = ((OpExpr *) clause)->opno;
collation = ((OpExpr *) clause)->inputcollid;
item1 = (Expr *) get_leftop(clause);
item2 = (Expr *) get_rightop(clause);
item1_relids = restrictinfo->left_relids;
item2_relids = restrictinfo->right_relids;
/*
* Ensure both input expressions expose the desired collation (their types
* should be OK already); see comments for canonicalize_ec_expression.
*/
item1 = canonicalize_ec_expression(item1,
exprType((Node *) item1),
collation);
item2 = canonicalize_ec_expression(item2,
exprType((Node *) item2),
collation);
/*
* Reject clauses of the form X=X. These are not as redundant as they
* might seem at first glance: assuming the operator is strict, this is
* really an expensive way to write X IS NOT NULL. So we must not risk
* just losing the clause, which would be possible if there is already a
* single-element EquivalenceClass containing X. The case is not common
* enough to be worth contorting the EC machinery for, so just reject the
* clause and let it be processed as a normal restriction clause.
*/
if (equal(item1, item2))
return false; /* X=X is not a useful equivalence */
/*
* If below outer join, check for strictness, else reject.
*/
if (below_outer_join)
{
if (!bms_is_empty(item1_relids) &&
contain_nonstrict_functions((Node *) item1))
return false; /* LHS is non-strict but not constant */
if (!bms_is_empty(item2_relids) &&
contain_nonstrict_functions((Node *) item2))
return false; /* RHS is non-strict but not constant */
}
/* Calculate nullable-relid sets for each side of the clause */
item1_nullable_relids = bms_intersect(item1_relids,
restrictinfo->nullable_relids);
item2_nullable_relids = bms_intersect(item2_relids,
restrictinfo->nullable_relids);
/*
* We use the declared input types of the operator, not exprType() of the
* inputs, as the nominal datatypes for opfamily lookup. This presumes
* that btree operators are always registered with amoplefttype and
* amoprighttype equal to their declared input types. We will need this
* info anyway to build EquivalenceMember nodes, and by extracting it now
* we can use type comparisons to short-circuit some equal() tests.
*/
op_input_types(opno, &item1_type, &item2_type);
opfamilies = restrictinfo->mergeopfamilies;
/*
* Sweep through the existing EquivalenceClasses looking for matches to
* item1 and item2. These are the possible outcomes:
*
* 1. We find both in the same EC. The equivalence is already known, so
* there's nothing to do.
*
* 2. We find both in different ECs. Merge the two ECs together.
*
* 3. We find just one. Add the other to its EC.
*
* 4. We find neither. Make a new, two-entry EC.
*
* Note: since all ECs are built through this process or the similar
* search in get_eclass_for_sort_expr(), it's impossible that we'd match
* an item in more than one existing nonvolatile EC. So it's okay to stop
* at the first match.
*/
ec1 = ec2 = NULL;
em1 = em2 = NULL;
foreach(lc1, root->eq_classes)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
ListCell *lc2;
/* Never match to a volatile EC */
if (cur_ec->ec_has_volatile)
continue;
/*
* The collation has to match; check this first since it's cheaper
* than the opfamily comparison.
*/
if (collation != cur_ec->ec_collation)
continue;
/*
* A "match" requires matching sets of btree opfamilies. Use of
* equal() for this test has implications discussed in the comments
* for get_mergejoin_opfamilies().
*/
if (!equal(opfamilies, cur_ec->ec_opfamilies))
continue;
foreach(lc2, cur_ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
Assert(!cur_em->em_is_child); /* no children yet */
/*
* If below an outer join, don't match constants: they're not as
* constant as they look.
*/
if ((below_outer_join || cur_ec->ec_below_outer_join) &&
cur_em->em_is_const)
continue;
if (!ec1 &&
item1_type == cur_em->em_datatype &&
equal(item1, cur_em->em_expr))
{
ec1 = cur_ec;
em1 = cur_em;
if (ec2)
break;
}
if (!ec2 &&
item2_type == cur_em->em_datatype &&
equal(item2, cur_em->em_expr))
{
ec2 = cur_ec;
em2 = cur_em;
if (ec1)
break;
}
}
if (ec1 && ec2)
break;
}
/* Sweep finished, what did we find? */
if (ec1 && ec2)
{
/* If case 1, nothing to do, except add to sources */
if (ec1 == ec2)
{
ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_below_outer_join |= below_outer_join;
/* mark the RI as associated with this eclass */
restrictinfo->left_ec = ec1;
restrictinfo->right_ec = ec1;
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
return true;
}
/*
* Case 2: need to merge ec1 and ec2. This should never happen after
* we've built any canonical pathkeys; if it did, those pathkeys might
* be rendered non-canonical by the merge.
*/
if (root->canon_pathkeys != NIL)
elog(ERROR, "too late to merge equivalence classes");
/*
* We add ec2's items to ec1, then set ec2's ec_merged link to point
* to ec1 and remove ec2 from the eq_classes list. We cannot simply
* delete ec2 because that could leave dangling pointers in existing
* PathKeys. We leave it behind with a link so that the merged EC can
* be found.
*/
ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);
ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);
ec1->ec_has_const |= ec2->ec_has_const;
/* can't need to set has_volatile */
ec1->ec_below_outer_join |= ec2->ec_below_outer_join;
ec2->ec_merged = ec1;
root->eq_classes = list_delete_ptr(root->eq_classes, ec2);
/* just to avoid debugging confusion w/ dangling pointers: */
ec2->ec_members = NIL;
ec2->ec_sources = NIL;
ec2->ec_derives = NIL;
ec2->ec_relids = NULL;
ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_below_outer_join |= below_outer_join;
/* mark the RI as associated with this eclass */
restrictinfo->left_ec = ec1;
restrictinfo->right_ec = ec1;
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
}
else if (ec1)
{
/* Case 3: add item2 to ec1 */
em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
false, item2_type);
ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_below_outer_join |= below_outer_join;
/* mark the RI as associated with this eclass */
restrictinfo->left_ec = ec1;
restrictinfo->right_ec = ec1;
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
}
else if (ec2)
{
/* Case 3: add item1 to ec2 */
em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
false, item1_type);
ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
ec2->ec_below_outer_join |= below_outer_join;
/* mark the RI as associated with this eclass */
restrictinfo->left_ec = ec2;
restrictinfo->right_ec = ec2;
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
}
else
{
/* Case 4: make a new, two-entry EC */
EquivalenceClass *ec = makeNode(EquivalenceClass);
ec->ec_opfamilies = opfamilies;
ec->ec_collation = collation;
ec->ec_members = NIL;
ec->ec_sources = list_make1(restrictinfo);
ec->ec_derives = NIL;
ec->ec_relids = NULL;
ec->ec_has_const = false;
ec->ec_has_volatile = false;
ec->ec_below_outer_join = below_outer_join;
ec->ec_broken = false;
ec->ec_sortref = 0;
ec->ec_merged = NULL;
em1 = add_eq_member(ec, item1, item1_relids, item1_nullable_relids,
false, item1_type);
em2 = add_eq_member(ec, item2, item2_relids, item2_nullable_relids,
false, item2_type);
root->eq_classes = lappend(root->eq_classes, ec);
/* mark the RI as associated with this eclass */
restrictinfo->left_ec = ec;
restrictinfo->right_ec = ec;
/* mark the RI as usable with this pair of EMs */
restrictinfo->left_em = em1;
restrictinfo->right_em = em2;
}
return true;
}
| void reconsider_outer_join_clauses | ( | PlannerInfo * | root | ) |
Definition at line 1451 of file equivclass.c.
References distribute_restrictinfo_to_rels(), PlannerInfo::full_join_clauses, PlannerInfo::left_join_clauses, lfirst, list_delete_cell(), list_head(), lnext, RangeQueryClause::next, RestrictInfo::norm_selec, RestrictInfo::outer_selec, reconsider_full_join_clause(), reconsider_outer_join_clause(), and PlannerInfo::right_join_clauses.
Referenced by query_planner().
{
bool found;
ListCell *cell;
ListCell *prev;
ListCell *next;
/* Outer loop repeats until we find no more deductions */
do
{
found = false;
/* Process the LEFT JOIN clauses */
prev = NULL;
for (cell = list_head(root->left_join_clauses); cell; cell = next)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
next = lnext(cell);
if (reconsider_outer_join_clause(root, rinfo, true))
{
found = true;
/* remove it from the list */
root->left_join_clauses =
list_delete_cell(root->left_join_clauses, cell, prev);
/* we throw it back anyway (see notes above) */
/* but the thrown-back clause has no extra selectivity */
rinfo->norm_selec = 2.0;
rinfo->outer_selec = 1.0;
distribute_restrictinfo_to_rels(root, rinfo);
}
else
prev = cell;
}
/* Process the RIGHT JOIN clauses */
prev = NULL;
for (cell = list_head(root->right_join_clauses); cell; cell = next)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
next = lnext(cell);
if (reconsider_outer_join_clause(root, rinfo, false))
{
found = true;
/* remove it from the list */
root->right_join_clauses =
list_delete_cell(root->right_join_clauses, cell, prev);
/* we throw it back anyway (see notes above) */
/* but the thrown-back clause has no extra selectivity */
rinfo->norm_selec = 2.0;
rinfo->outer_selec = 1.0;
distribute_restrictinfo_to_rels(root, rinfo);
}
else
prev = cell;
}
/* Process the FULL JOIN clauses */
prev = NULL;
for (cell = list_head(root->full_join_clauses); cell; cell = next)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
next = lnext(cell);
if (reconsider_full_join_clause(root, rinfo))
{
found = true;
/* remove it from the list */
root->full_join_clauses =
list_delete_cell(root->full_join_clauses, cell, prev);
/* we throw it back anyway (see notes above) */
/* but the thrown-back clause has no extra selectivity */
rinfo->norm_selec = 2.0;
rinfo->outer_selec = 1.0;
distribute_restrictinfo_to_rels(root, rinfo);
}
else
prev = cell;
}
} while (found);
/* Now, any remaining clauses have to be thrown back */
foreach(cell, root->left_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
distribute_restrictinfo_to_rels(root, rinfo);
}
foreach(cell, root->right_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
distribute_restrictinfo_to_rels(root, rinfo);
}
foreach(cell, root->full_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
distribute_restrictinfo_to_rels(root, rinfo);
}
}
| bool relation_has_unique_index_for | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | restrictlist, | |||
| List * | exprlist, | |||
| List * | oprlist | |||
| ) |
Definition at line 2758 of file indxpath.c.
References Assert, RelOptInfo::baserestrictinfo, bms_is_empty(), RestrictInfo::clause, forboth, get_leftop(), get_rightop(), IndexOptInfo::immediate, RelOptInfo::indexlist, IndexOptInfo::indpred, lappend(), RestrictInfo::left_relids, lfirst, lfirst_oid, list_length(), list_member_oid(), match_index_to_operand(), RestrictInfo::mergeopfamilies, IndexOptInfo::ncolumns, NIL, op_in_opfamily(), IndexOptInfo::opfamily, RestrictInfo::outer_is_left, IndexOptInfo::predOK, RestrictInfo::right_relids, and IndexOptInfo::unique.
Referenced by create_unique_path(), and join_is_removable().
{
ListCell *ic;
Assert(list_length(exprlist) == list_length(oprlist));
/* Short-circuit if no indexes... */
if (rel->indexlist == NIL)
return false;
/*
* Examine the rel's restriction clauses for usable var = const clauses
* that we can add to the restrictlist.
*/
foreach(ic, rel->baserestrictinfo)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(ic);
/*
* Note: can_join won't be set for a restriction clause, but
* mergeopfamilies will be if it has a mergejoinable operator and
* doesn't contain volatile functions.
*/
if (restrictinfo->mergeopfamilies == NIL)
continue; /* not mergejoinable */
/*
* The clause certainly doesn't refer to anything but the given rel.
* If either side is pseudoconstant then we can use it.
*/
if (bms_is_empty(restrictinfo->left_relids))
{
/* righthand side is inner */
restrictinfo->outer_is_left = true;
}
else if (bms_is_empty(restrictinfo->right_relids))
{
/* lefthand side is inner */
restrictinfo->outer_is_left = false;
}
else
continue;
/* OK, add to list */
restrictlist = lappend(restrictlist, restrictinfo);
}
/* Short-circuit the easy case */
if (restrictlist == NIL && exprlist == NIL)
return false;
/* Examine each index of the relation ... */
foreach(ic, rel->indexlist)
{
IndexOptInfo *ind = (IndexOptInfo *) lfirst(ic);
int c;
/*
* If the index is not unique, or not immediately enforced, or if it's
* a partial index that doesn't match the query, it's useless here.
*/
if (!ind->unique || !ind->immediate ||
(ind->indpred != NIL && !ind->predOK))
continue;
/*
* Try to find each index column in the lists of conditions. This is
* O(N^2) or worse, but we expect all the lists to be short.
*/
for (c = 0; c < ind->ncolumns; c++)
{
bool matched = false;
ListCell *lc;
ListCell *lc2;
foreach(lc, restrictlist)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Node *rexpr;
/*
* The condition's equality operator must be a member of the
* index opfamily, else it is not asserting the right kind of
* equality behavior for this index. We check this first
* since it's probably cheaper than match_index_to_operand().
*/
if (!list_member_oid(rinfo->mergeopfamilies, ind->opfamily[c]))
continue;
/*
* XXX at some point we may need to check collations here too.
* For the moment we assume all collations reduce to the same
* notion of equality.
*/
/* OK, see if the condition operand matches the index key */
if (rinfo->outer_is_left)
rexpr = get_rightop(rinfo->clause);
else
rexpr = get_leftop(rinfo->clause);
if (match_index_to_operand(rexpr, c, ind))
{
matched = true; /* column is unique */
break;
}
}
if (matched)
continue;
forboth(lc, exprlist, lc2, oprlist)
{
Node *expr = (Node *) lfirst(lc);
Oid opr = lfirst_oid(lc2);
/* See if the expression matches the index key */
if (!match_index_to_operand(expr, c, ind))
continue;
/*
* The equality operator must be a member of the index
* opfamily, else it is not asserting the right kind of
* equality behavior for this index. We assume the caller
* determined it is an equality operator, so we don't need to
* check any more tightly than this.
*/
if (!op_in_opfamily(opr, ind->opfamily[c]))
continue;
/*
* XXX at some point we may need to check collations here too.
* For the moment we assume all collations reduce to the same
* notion of equality.
*/
matched = true; /* column is unique */
break;
}
if (!matched)
break; /* no match; this index doesn't help us */
}
/* Matched all columns of this index? */
if (c == ind->ncolumns)
return true;
}
return false;
}
| List* select_outer_pathkeys_for_merge | ( | PlannerInfo * | root, | |
| List * | mergeclauses, | |||
| RelOptInfo * | joinrel | |||
| ) |
Definition at line 1007 of file pathkeys.c.
References Assert, bms_overlap(), BTLessStrategyNumber, EquivalenceClass::ec_members, EquivalenceClass::ec_opfamilies, EquivalenceMember::em_is_child, EquivalenceMember::em_is_const, EquivalenceMember::em_relids, lappend(), RestrictInfo::left_ec, lfirst, linitial_oid, list_copy(), list_length(), make_canonical_pathkey(), NULL, RestrictInfo::outer_is_left, palloc(), pathkey_is_redundant(), pfree(), PathKey::pk_eclass, PlannerInfo::query_pathkeys, RelOptInfo::relids, RestrictInfo::right_ec, and update_mergeclause_eclasses().
Referenced by sort_inner_and_outer().
{
List *pathkeys = NIL;
int nClauses = list_length(mergeclauses);
EquivalenceClass **ecs;
int *scores;
int necs;
ListCell *lc;
int j;
/* Might have no mergeclauses */
if (nClauses == 0)
return NIL;
/*
* Make arrays of the ECs used by the mergeclauses (dropping any
* duplicates) and their "popularity" scores.
*/
ecs = (EquivalenceClass **) palloc(nClauses * sizeof(EquivalenceClass *));
scores = (int *) palloc(nClauses * sizeof(int));
necs = 0;
foreach(lc, mergeclauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
EquivalenceClass *oeclass;
int score;
ListCell *lc2;
/* get the outer eclass */
update_mergeclause_eclasses(root, rinfo);
if (rinfo->outer_is_left)
oeclass = rinfo->left_ec;
else
oeclass = rinfo->right_ec;
/* reject duplicates */
for (j = 0; j < necs; j++)
{
if (ecs[j] == oeclass)
break;
}
if (j < necs)
continue;
/* compute score */
score = 0;
foreach(lc2, oeclass->ec_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
/* Potential future join partner? */
if (!em->em_is_const && !em->em_is_child &&
!bms_overlap(em->em_relids, joinrel->relids))
score++;
}
ecs[necs] = oeclass;
scores[necs] = score;
necs++;
}
/*
* Find out if we have all the ECs mentioned in query_pathkeys; if so we
* can generate a sort order that's also useful for final output. There is
* no percentage in a partial match, though, so we have to have 'em all.
*/
if (root->query_pathkeys)
{
foreach(lc, root->query_pathkeys)
{
PathKey *query_pathkey = (PathKey *) lfirst(lc);
EquivalenceClass *query_ec = query_pathkey->pk_eclass;
for (j = 0; j < necs; j++)
{
if (ecs[j] == query_ec)
break; /* found match */
}
if (j >= necs)
break; /* didn't find match */
}
/* if we got to the end of the list, we have them all */
if (lc == NULL)
{
/* copy query_pathkeys as starting point for our output */
pathkeys = list_copy(root->query_pathkeys);
/* mark their ECs as already-emitted */
foreach(lc, root->query_pathkeys)
{
PathKey *query_pathkey = (PathKey *) lfirst(lc);
EquivalenceClass *query_ec = query_pathkey->pk_eclass;
for (j = 0; j < necs; j++)
{
if (ecs[j] == query_ec)
{
scores[j] = -1;
break;
}
}
}
}
}
/*
* Add remaining ECs to the list in popularity order, using a default sort
* ordering. (We could use qsort() here, but the list length is usually
* so small it's not worth it.)
*/
for (;;)
{
int best_j;
int best_score;
EquivalenceClass *ec;
PathKey *pathkey;
best_j = 0;
best_score = scores[0];
for (j = 1; j < necs; j++)
{
if (scores[j] > best_score)
{
best_j = j;
best_score = scores[j];
}
}
if (best_score < 0)
break; /* all done */
ec = ecs[best_j];
scores[best_j] = -1;
pathkey = make_canonical_pathkey(root,
ec,
linitial_oid(ec->ec_opfamilies),
BTLessStrategyNumber,
false);
/* can't be redundant because no duplicate ECs */
Assert(!pathkey_is_redundant(pathkey, pathkeys));
pathkeys = lappend(pathkeys, pathkey);
}
pfree(ecs);
pfree(scores);
return pathkeys;
}
| RelOptInfo* standard_join_search | ( | PlannerInfo * | root, | |
| int | levels_needed, | |||
| List * | initial_rels | |||
| ) |
Definition at line 1469 of file allpaths.c.
References Assert, elog, ERROR, PlannerInfo::join_rel_level, join_search_one_level(), lfirst, linitial, list_length(), NIL, NULL, palloc0(), and set_cheapest().
Referenced by make_rel_from_joinlist().
{
int lev;
RelOptInfo *rel;
/*
* This function cannot be invoked recursively within any one planning
* problem, so join_rel_level[] can't be in use already.
*/
Assert(root->join_rel_level == NULL);
/*
* We employ a simple "dynamic programming" algorithm: we first find all
* ways to build joins of two jointree items, then all ways to build joins
* of three items (from two-item joins and single items), then four-item
* joins, and so on until we have considered all ways to join all the
* items into one rel.
*
* root->join_rel_level[j] is a list of all the j-item rels. Initially we
* set root->join_rel_level[1] to represent all the single-jointree-item
* relations.
*/
root->join_rel_level = (List **) palloc0((levels_needed + 1) * sizeof(List *));
root->join_rel_level[1] = initial_rels;
for (lev = 2; lev <= levels_needed; lev++)
{
ListCell *lc;
/*
* Determine all possible pairs of relations to be joined at this
* level, and build paths for making each one from every available
* pair of lower-level relations.
*/
join_search_one_level(root, lev);
/*
* Do cleanup work on each just-processed rel.
*/
foreach(lc, root->join_rel_level[lev])
{
rel = (RelOptInfo *) lfirst(lc);
/* Find and save the cheapest paths for this rel */
set_cheapest(rel);
#ifdef OPTIMIZER_DEBUG
debug_print_rel(root, rel);
#endif
}
}
/*
* We should have a single rel at the final level.
*/
if (root->join_rel_level[levels_needed] == NIL)
elog(ERROR, "failed to build any %d-way joins", levels_needed);
Assert(list_length(root->join_rel_level[levels_needed]) == 1);
rel = (RelOptInfo *) linitial(root->join_rel_level[levels_needed]);
root->join_rel_level = NULL;
return rel;
}
| List* truncate_useless_pathkeys | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | pathkeys | |||
| ) |
Definition at line 1405 of file pathkeys.c.
References list_copy(), list_length(), list_truncate(), pathkeys_useful_for_merging(), and pathkeys_useful_for_ordering().
Referenced by build_index_paths(), and build_join_pathkeys().
{
int nuseful;
int nuseful2;
nuseful = pathkeys_useful_for_merging(root, rel, pathkeys);
nuseful2 = pathkeys_useful_for_ordering(root, pathkeys);
if (nuseful2 > nuseful)
nuseful = nuseful2;
/*
* Note: not safe to modify input list destructively, but we can avoid
* copying the list if we're not actually going to change it
*/
if (nuseful == 0)
return NIL;
else if (nuseful == list_length(pathkeys))
return pathkeys;
else
return list_truncate(list_copy(pathkeys), nuseful);
}
| void update_mergeclause_eclasses | ( | PlannerInfo * | root, | |
| RestrictInfo * | restrictinfo | |||
| ) |
Definition at line 855 of file pathkeys.c.
References Assert, EquivalenceClass::ec_merged, RestrictInfo::left_ec, RestrictInfo::mergeopfamilies, NIL, NULL, and RestrictInfo::right_ec.
Referenced by find_mergeclauses_for_pathkeys(), make_inner_pathkeys_for_merge(), pathkeys_useful_for_merging(), select_mergejoin_clauses(), and select_outer_pathkeys_for_merge().
{
/* Should be a merge clause ... */
Assert(restrictinfo->mergeopfamilies != NIL);
/* ... with pointers already set */
Assert(restrictinfo->left_ec != NULL);
Assert(restrictinfo->right_ec != NULL);
/* Chase up to the top as needed */
while (restrictinfo->left_ec->ec_merged)
restrictinfo->left_ec = restrictinfo->left_ec->ec_merged;
while (restrictinfo->right_ec->ec_merged)
restrictinfo->right_ec = restrictinfo->right_ec->ec_merged;
}
Definition at line 43 of file allpaths.c.
Referenced by make_rel_from_joinlist().
| int geqo_threshold |
Definition at line 44 of file allpaths.c.
Referenced by make_rel_from_joinlist().
| PGDLLIMPORT join_search_hook_type join_search_hook |
Definition at line 47 of file allpaths.c.
Referenced by make_rel_from_joinlist().
1.7.1