📄 pathkeys.c
字号:
forboth(key1, keys1, key2, keys2) { List *subkey1 = (List *) lfirst(key1); List *subkey2 = (List *) lfirst(key2); /* * XXX would like to check that we've been given canonicalized input, * but PlannerInfo not accessible here... */#ifdef NOT_USED Assert(list_member_ptr(root->equi_key_list, subkey1)); Assert(list_member_ptr(root->equi_key_list, subkey2));#endif /* * We will never have two subkeys where one is a subset of the other, * because of the canonicalization process. Either they are equal or * they ain't. Furthermore, we only need pointer comparison to detect * equality. */ if (subkey1 != subkey2) return PATHKEYS_DIFFERENT; /* no need to keep looking */ } /* * If we reached the end of only one list, the other is longer and * therefore not a subset. (We assume the additional sublist(s) of the * other list are not NIL --- no pathkey list should ever have a NIL * sublist.) */ if (key1 == NULL && key2 == NULL) return PATHKEYS_EQUAL; if (key1 != NULL) return PATHKEYS_BETTER1; /* key1 is longer */ return PATHKEYS_BETTER2; /* key2 is longer */}/* * pathkeys_contained_in * Common special case of compare_pathkeys: we just want to know * if keys2 are at least as well sorted as keys1. */boolpathkeys_contained_in(List *keys1, List *keys2){ switch (compare_pathkeys(keys1, keys2)) { case PATHKEYS_EQUAL: case PATHKEYS_BETTER2: return true; default: break; } return false;}/* * get_cheapest_path_for_pathkeys * Find the cheapest path (according to the specified criterion) that * satisfies the given pathkeys. Return NULL if no such path. * * 'paths' is a list of possible paths that all generate the same relation * 'pathkeys' represents a required ordering (already canonicalized!) * 'cost_criterion' is STARTUP_COST or TOTAL_COST */Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys, CostSelector cost_criterion){ 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)) matched_path = path; } return matched_path;}/* * get_cheapest_fractional_path_for_pathkeys * Find the cheapest path (for retrieving a specified fraction of all * the tuples) that satisfies the given pathkeys. * Return NULL if no such path. * * See compare_fractional_path_costs() for the interpretation of the fraction * parameter. * * 'paths' is a list of possible paths that all generate the same relation * 'pathkeys' represents a required ordering (already canonicalized!) * 'fraction' is the fraction of the total tuples expected to be retrieved */Path *get_cheapest_fractional_path_for_pathkeys(List *paths, List *pathkeys, double fraction){ 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. */ if (matched_path != NULL && compare_fractional_path_costs(matched_path, path, fraction) <= 0) continue; if (pathkeys_contained_in(pathkeys, path->pathkeys)) matched_path = path; } return matched_path;}/**************************************************************************** * NEW PATHKEY FORMATION ****************************************************************************//* * build_index_pathkeys * Build a pathkeys list that describes the ordering induced by an index * scan using the given index. (Note that an unordered index doesn't * induce any ordering; such an index will have no sortop OIDS in * its "ordering" field, and we will return NIL.) * * If 'scandir' is BackwardScanDirection, attempt to build pathkeys * representing a backwards scan of the index. Return NIL if can't do it. * * If 'canonical' is TRUE, we remove duplicate pathkeys (which can occur * if two index columns are equijoined, eg WHERE x = 1 AND y = 1). This * is required if the result is to be compared directly to a canonical query * pathkeys list. However, some callers want a list with exactly one entry * per index column, and they must pass FALSE. * * We generate the full pathkeys list whether or not all are useful for the * current query. Caller should do truncate_useless_pathkeys(). */List *build_index_pathkeys(PlannerInfo *root, IndexOptInfo *index, ScanDirection scandir, bool canonical){ List *retval = NIL; int *indexkeys = index->indexkeys; Oid *ordering = index->ordering; ListCell *indexprs_item = list_head(index->indexprs); while (*ordering != InvalidOid) { PathKeyItem *item; Oid sortop; Node *indexkey; List *cpathkey; sortop = *ordering; if (ScanDirectionIsBackward(scandir)) { sortop = get_commutator(sortop); if (sortop == InvalidOid) break; /* oops, no reverse sort operator? */ } if (*indexkeys != 0) { /* simple index column */ indexkey = (Node *) find_indexkey_var(root, index->rel, *indexkeys); } else { /* expression --- assume we need not copy it */ if (indexprs_item == NULL) elog(ERROR, "wrong number of index expressions"); indexkey = (Node *) lfirst(indexprs_item); indexprs_item = lnext(indexprs_item); } /* OK, make a sublist for this sort key */ item = makePathKeyItem(indexkey, sortop, true); cpathkey = make_canonical_pathkey(root, item); /* Eliminate redundant ordering info if requested */ if (canonical) retval = list_append_unique_ptr(retval, cpathkey); else retval = lappend(retval, cpathkey); indexkeys++; ordering++; } return retval;}/* * Find or make a Var node for the specified attribute of the rel. * * We first look for the var in the rel's target list, because that's * easy and fast. But the var might not be there (this should normally * only happen for vars that are used in WHERE restriction clauses, * but not in join clauses or in the SELECT target list). In that case, * gin up a Var node the hard way. */static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno){ ListCell *temp; Index relid; Oid reloid, vartypeid; int32 type_mod; foreach(temp, rel->reltargetlist) { Var *var = (Var *) lfirst(temp); if (IsA(var, Var) && var->varattno == varattno) return var; } relid = rel->relid; reloid = getrelid(relid, root->parse->rtable); get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod); return makeVar(relid, varattno, vartypeid, type_mod, 0);}/* * convert_subquery_pathkeys * Build a pathkeys list that describes the ordering of a subquery's * result, in the terms of the outer query. This is essentially a * task of conversion. * * 'rel': outer query's RelOptInfo for the subquery relation. * 'subquery_pathkeys': the subquery's output pathkeys, in its terms. * * It is not necessary for caller to do truncate_useless_pathkeys(), * because we select keys in a way that takes usefulness of the keys into * account. */List *convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, List *subquery_pathkeys){ 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) { List *sub_pathkey = (List *) lfirst(i); ListCell *j; PathKeyItem *best_item = NULL; int best_score = 0; List *cpathkey; /* * The sub_pathkey 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 a pathkey list of the outer query, thereby propagating * equality knowledge up to the outer query. Right now we cannot do * so, because the outer query's canonical pathkey sets are already * frozen when this is called. Instead we prefer the one that has the * highest "score" (number of canonical pathkey peers, plus one if it * matches the outer query_pathkeys). This is the most likely to be * useful in the outer query. */ foreach(j, sub_pathkey) { PathKeyItem *sub_item = (PathKeyItem *) lfirst(j); Node *sub_key = sub_item->key; ListCell *k; foreach(k, sub_tlist) { TargetEntry *tle = (TargetEntry *) lfirst(k); if (!tle->resjunk && equal(tle->expr, sub_key)) { /* Found a representation for this sub_key */ Var *outer_var; PathKeyItem *outer_item; int score; outer_var = makeVar(rel->relid, tle->resno, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), 0); outer_item = makePathKeyItem((Node *) outer_var, sub_item->sortop, true); /* score = # of mergejoin peers */ score = count_canonical_peers(root, outer_item); /* +1 if it matches the proper query_pathkeys item */ if (retvallen < outer_query_keys && list_member(list_nth(root->query_pathkeys, retvallen), outer_item)) score++; if (score > best_score) { best_item = outer_item; 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_item) break; /* Canonicalize the chosen item (we did not before) */ cpathkey = make_canonical_pathkey(root, best_item); /* * Eliminate redundant ordering info; could happen if outer query * equijoins subquery keys... */ if (!list_member_ptr(retval, cpathkey)) { retval = lappend(retval, cpathkey); retvallen++; } } return retval;}/* * build_join_pathkeys * Build the path keys for a join relation constructed by mergejoin or * nestloop join. These keys should include all the path key vars of the * outer path (since the join will retain the ordering of the outer path) * plus any vars of the inner path that are equijoined to the outer vars. * * Per the discussion in backend/optimizer/README, equijoined inner vars * can be considered path keys of the result, just the same as the outer * vars they were joined with; furthermore, it doesn't matter what kind * of join algorithm is actually used. * * EXCEPTION: in a FULL or RIGHT join, we cannot treat the result as * having the outer path's path keys, because null lefthand rows may be * inserted at random points. It must be treated as unsorted. * * 'joinrel' is the join relation that paths are being formed for * 'jointype' is the join type (inner, left, full, etc) * 'outer_pathkeys' is the list of the current outer path's path keys * * Returns the list of new path keys. */List *build_join_pathkeys(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, List *outer_pathkeys){ if (jointype == JOIN_FULL || jointype == JOIN_RIGHT)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -