pathkeys.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,266 行 · 第 1/3 页
C
1,266 行
* Compare two pathkeys to see if they are equivalent, and if not whether * one is "better" than the other. * * This function may only be applied to canonicalized pathkey lists. * In the canonical representation, sublists can be checked for equality * by simple pointer comparison. */PathKeysComparisoncompare_pathkeys(List *keys1, List *keys2){ List *key1, *key2; for (key1 = keys1, key2 = keys2; key1 != NIL && key2 != NIL; key1 = lnext(key1), key2 = lnext(key2)) { List *subkey1 = lfirst(key1); List *subkey2 = lfirst(key2); /* * XXX would like to check that we've been given canonicalized * input, but query root not accessible here... */#ifdef NOT_USED Assert(ptrMember(subkey1, root->equi_key_list)); Assert(ptrMember(subkey2, root->equi_key_list));#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 == NIL && key2 == NIL) return PATHKEYS_EQUAL; if (key1 != NIL) return PATHKEYS_BETTER1; /* key1 is longer */ return PATHKEYS_BETTER2; /* key2 is longer */}/* * compare_noncanonical_pathkeys * Compare two pathkeys to see if they are equivalent, and if not whether * one is "better" than the other. This is used when we must compare * non-canonicalized pathkeys. * * A pathkey can be considered better than another if it is a superset: * it contains all the keys of the other plus more. For example, either * ((A) (B)) or ((A B)) is better than ((A)). * * Currently, the only user of this routine is grouping_planner(), * and it will only pass single-element sublists (from * make_pathkeys_for_sortclauses). Therefore we don't have to do the * full two-way-subset-inclusion test on each pair of sublists that is * implied by the above statement. Instead we just verify they are * singleton lists and then do an equal(). This could be improved if * necessary. */PathKeysComparisoncompare_noncanonical_pathkeys(List *keys1, List *keys2){ List *key1, *key2; for (key1 = keys1, key2 = keys2; key1 != NIL && key2 != NIL; key1 = lnext(key1), key2 = lnext(key2)) { List *subkey1 = lfirst(key1); List *subkey2 = lfirst(key2); Assert(length(subkey1) == 1); Assert(length(subkey2) == 1); if (!equal(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 == NIL && key2 == NIL) return PATHKEYS_EQUAL; if (key1 != NIL) 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;}/* * noncanonical_pathkeys_contained_in * The same, when we don't have canonical pathkeys. */boolnoncanonical_pathkeys_contained_in(List *keys1, List *keys2){ switch (compare_noncanonical_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; List *i; foreach(i, paths) { Path *path = (Path *) lfirst(i); /* * 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; List *i; foreach(i, paths) { Path *path = (Path *) lfirst(i); /* * 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. * * 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(Query *root, RelOptInfo *rel, IndexOptInfo *index, ScanDirection scandir){ List *retval = NIL; int *indexkeys = index->indexkeys; Oid *ordering = index->ordering; List *indexprs = 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, rel, *indexkeys); } else { /* expression --- assume we need not copy it */ if (indexprs == NIL) elog(ERROR, "wrong number of index expressions"); indexkey = (Node *) lfirst(indexprs); indexprs = lnext(indexprs); } /* OK, make a sublist for this sort key */ item = makePathKeyItem(indexkey, sortop, true); cpathkey = make_canonical_pathkey(root, item); /* * Eliminate redundant ordering info; could happen if query is * such that index keys are equijoined... */ if (!ptrMember(cpathkey, retval)) 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(Query *root, RelOptInfo *rel, AttrNumber varattno){ List *temp; Index relid; Oid reloid, vartypeid; int32 type_mod; foreach(temp, FastListValue(&rel->reltargetlist)) { Var *var = (Var *) lfirst(temp); if (IsA(var, Var) && var->varattno == varattno) return var; } relid = rel->relid; reloid = getrelid(relid, root->rtable); get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod); return makeVar(relid, varattno, vartypeid, type_mod, 0);}/* * build_subquery_pathkeys * Build a pathkeys list that describes the ordering of a subquery's * result (in the terms of the outer query). The subquery must already * have been planned, so that its query_pathkeys field has been set. * * 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 *build_subquery_pathkeys(Query *root, RelOptInfo *rel, Query *subquery){ List *retval = NIL; int retvallen = 0; int outer_query_keys = length(root->query_pathkeys); List *l; foreach(l, subquery->query_pathkeys) { List *sub_pathkey = (List *) lfirst(l); List *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; List *k; foreach(k, subquery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(k); if (!tle->resdom->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->resdom->resno, tle->resdom->restype, tle->resdom->restypmod, 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 && member(outer_item, nth(retvallen, root->query_pathkeys))) 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 (!ptrMember(cpathkey, retval)) { retval = lappend(retval, cpathkey);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?