aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/pathkeys.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path/pathkeys.c')
-rw-r--r--src/backend/optimizer/path/pathkeys.c738
1 files changed, 474 insertions, 264 deletions
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index c0782c5665b..44a7b614b69 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1,70 +1,84 @@
/*-------------------------------------------------------------------------
*
- * joinutils.c
- * Utilities for matching and building join and path keys
+ * pathkeys.c
+ * Utilities for matching and building path keys
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.13 1999/08/13 01:17:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.14 1999/08/16 02:17:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-
+#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/joininfo.h"
-#include "optimizer/keys.h"
-#include "optimizer/ordering.h"
#include "optimizer/paths.h"
#include "optimizer/tlist.h"
+#include "optimizer/var.h"
+#include "parser/parsetree.h"
+#include "utils/lsyscache.h"
-static int match_pathkey_joinkeys(List *pathkey, List *joinkeys,
- int outer_or_inner);
-static List *new_join_pathkey(List *pathkeys, List *join_rel_tlist,
- List *joinclauses);
+static PathKeyItem *makePathKeyItem(Node *key, Oid sortop);
+static bool pathkeyitem_equal(PathKeyItem *a, PathKeyItem *b);
+static bool pathkeyitem_member(PathKeyItem *a, List *l);
+static Var *find_indexkey_var(int indexkey, List *tlist);
+static List *build_join_pathkey(List *pathkeys, List *join_rel_tlist,
+ List *joinclauses);
/*--------------------
* Explanation of Path.pathkeys
*
- * Path.pathkeys is a List of List of Var nodes that represent the sort
- * order of the result generated by the Path.
+ * Path.pathkeys is a List of Lists of PathKeyItem nodes that represent
+ * the sort order of the result generated by the Path. The n'th sublist
+ * represents the n'th sort key of the result.
*
- * In single/base relation RelOptInfo's, the Path's represent various ways
+ * In single/base relation RelOptInfo's, the Paths represent various ways
* of scanning the relation and the resulting ordering of the tuples.
* Sequential scan Paths have NIL pathkeys, indicating no known ordering.
* Index scans have Path.pathkeys that represent the chosen index's ordering,
* if any. A single-key index would create a pathkey with a single sublist,
- * e.g. ( (tab1_indexkey1) ). A multi-key index generates a sublist per key,
- * e.g. ( (tab1_indexkey1) (tab1_indexkey2) ) which shows major sort by
- * indexkey1 and minor sort by indexkey2.
+ * e.g. ( (tab1.indexkey1/sortop1) ). A multi-key index generates a sublist
+ * per key, e.g. ( (tab1.indexkey1/sortop1) (tab1.indexkey2/sortop2) ) which
+ * shows major sort by indexkey1 (ordering by sortop1) and minor sort by
+ * indexkey2 with sortop2.
*
* Note that a multi-pass indexscan (OR clause scan) has NIL pathkeys since
- * we can say nothing about the overall order of its result. Also, an index
- * scan on an unordered type of index generates no useful pathkeys. However,
+ * we can say nothing about the overall order of its result. Also, an
+ * indexscan on an unordered type of index generates NIL pathkeys. However,
* we can always create a pathkey by doing an explicit sort.
*
* Multi-relation RelOptInfo Path's are more complicated. Mergejoins are
- * only performed with equijoins ("="). Because of this, the multi-relation
- * path actually has more than one primary Var key. For example, a
- * mergejoin Path of "tab1.col1 = tab2.col1" would generate pathkeys of
- * ( (tab1.col1 tab2.col1) ), indicating that the major sort order of the
- * Path can be taken to be *either* tab1.col1 or tab2.col1.
+ * only performed with equijoins ("="). Because of this, the resulting
+ * multi-relation path actually has more than one primary key. For example,
+ * a mergejoin using a clause "tab1.col1 = tab2.col1" would generate pathkeys
+ * of ( (tab1.col1/sortop1 tab2.col1/sortop2) ), indicating that the major
+ * sort order of the Path can be taken to be *either* tab1.col1 or tab2.col1.
* They are equal, so they are both primary sort keys. This allows future
- * joins to use either Var as a pre-sorted key to prevent upper Mergejoins
+ * joins to use either var as a pre-sorted key to prevent upper Mergejoins
* from having to re-sort the Path. This is why pathkeys is a List of Lists.
*
* Note that while the order of the top list is meaningful (primary vs.
- * secondary sort key), the order of each sublist is arbitrary.
- *
- * We can actually keep all of the keys of the outer path of a merge or
- * nestloop join, since the ordering of the outer path will be reflected
- * in the result. We add to each pathkey sublist any inner vars that are
- * equijoined to any of the outer vars in the sublist. In the nestloop
- * case we have to be careful to consider only equijoin operators; the
- * nestloop's join clauses might include non-equijoin operators.
+ * secondary sort key), the order of each sublist is arbitrary. No code
+ * working with pathkeys should generate a result that depends on the order
+ * of a pathkey sublist.
+ *
+ * We keep a sortop associated with each PathKeyItem because cross-data-type
+ * mergejoins are possible; for example int4=int8 is mergejoinable. In this
+ * case we need to remember that the left var is ordered by int4lt while
+ * the right var is ordered by int8lt. So the different members of each
+ * sublist could have different sortops.
+ *
+ * When producing the pathkeys for a merge or nestloop join, we can keep
+ * all of the keys of the outer path, since the ordering of the outer path
+ * will be preserved in the result. We add to each pathkey sublist any inner
+ * vars that are equijoined to any of the outer vars in the sublist. In the
+ * nestloop case we have to be careful to consider only equijoin operators;
+ * the nestloop's join clauses might include non-equijoin operators.
* (Currently, we do this by considering only mergejoinable operators while
* making the pathkeys, since we have no separate marking for operators that
* are equijoins but aren't mergejoinable.)
@@ -75,180 +89,174 @@ static List *new_join_pathkey(List *pathkeys, List *join_rel_tlist,
* executor might have to split the join into multiple batches. Therefore
* a Hashjoin is always given NIL pathkeys.
*
- * Notice that pathkeys only say *what* is being ordered, and not *how*
- * it is ordered. The actual sort ordering is indicated by a separate
- * data structure, the PathOrder. The PathOrder provides a sort operator
- * OID for each of the sublists of the path key. This is fairly bogus,
- * since in cross-datatype cases we really want to keep track of more than
- * one sort operator...
- *
* -- bjm & tgl
*--------------------
*/
+
+/*
+ * makePathKeyItem
+ * create a PathKeyItem node
+ */
+static PathKeyItem *
+makePathKeyItem(Node *key, Oid sortop)
+{
+ PathKeyItem *item = makeNode(PathKeyItem);
+
+ item->key = key;
+ item->sortop = sortop;
+ return item;
+}
+
/****************************************************************************
- * KEY COMPARISONS
+ * PATHKEY COMPARISONS
****************************************************************************/
/*
- * order_joinkeys_by_pathkeys
- * Attempts to match the keys of a path against the keys of join clauses.
- * This is done by looking for a matching join key in 'joinkeys' for
- * every path key in the list 'path.keys'. If there is a matching join key
- * (not necessarily unique) for every path key, then the list of
- * corresponding join keys and join clauses are returned in the order in
- * which the keys matched the path keys.
- *
- * 'pathkeys' is a list of path keys:
- * ( ( (var) (var) ... ) ( (var) ... ) )
- * 'joinkeys' is a list of join keys:
- * ( (outer inner) (outer inner) ... )
- * 'joinclauses' is a list of clauses corresponding to the join keys in
- * 'joinkeys'
- * 'outer_or_inner' is a flag that selects the desired pathkey of a join key
- * in 'joinkeys'
- *
- * Returns the join keys and corresponding join clauses in a list if all
- * of the path keys were matched:
- * (
- * ( (outerkey0 innerkey0) ... (outerkeyN or innerkeyN) )
- * ( clause0 ... clauseN )
- * )
- * and nil otherwise.
- *
- * Returns a list of matched join keys and a list of matched join clauses
- * in pointers if valid order can be found.
+ * Compare two pathkey items for equality.
+ *
+ * This is unlike straight equal() because when the two keys are both Vars,
+ * we want to apply the weaker var_equal() condition (doesn't check varnoold
+ * or varoattno). But if that fails, try equal() so that we recognize
+ * functional-index keys.
*/
-bool
-order_joinkeys_by_pathkeys(List *pathkeys,
- List *joinkeys,
- List *joinclauses,
- int outer_or_inner,
- List **matchedJoinKeysPtr,
- List **matchedJoinClausesPtr)
+static bool
+pathkeyitem_equal (PathKeyItem *a, PathKeyItem *b)
{
- List *matched_joinkeys = NIL;
- List *matched_joinclauses = NIL;
- List *pathkey = NIL;
- List *i = NIL;
- int matched_joinkey_index = -1;
- int matched_keys = 0;
-
- /*
- * Reorder the joinkeys by picking out one that matches each pathkey,
- * and create a new joinkey/joinclause list in pathkey order
- */
- foreach(i, pathkeys)
+ Assert(a && IsA(a, PathKeyItem));
+ Assert(b && IsA(b, PathKeyItem));
+
+ if (a->sortop != b->sortop)
+ return false;
+ if (var_equal((Var *) a->key, (Var *) b->key))
+ return true;
+ return equal(a->key, b->key);
+}
+
+/*
+ * member() test using pathkeyitem_equal
+ */
+static bool
+pathkeyitem_member (PathKeyItem *a, List *l)
+{
+ List *i;
+
+ Assert(a && IsA(a, PathKeyItem));
+
+ foreach(i, l)
+ {
+ if (pathkeyitem_equal(a, (PathKeyItem *) lfirst(i)))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * compare_pathkeys
+ * Compare two pathkeys to see if they are equivalent, and if not whether
+ * one is "better" than the other.
+ *
+ * A pathkey can be considered better than another if it is a superset:
+ * it contains all the keys of the other plus more. For example, either
+ * ((A) (B)) or ((A B)) is better than ((A)).
+ *
+ * This gets called a lot, so it is optimized.
+ */
+PathKeysComparison
+compare_pathkeys(List *keys1, List *keys2)
+{
+ List *key1,
+ *key2;
+ bool key1_subsetof_key2 = true,
+ key2_subsetof_key1 = true;
+
+ for (key1 = keys1, key2 = keys2;
+ key1 != NIL && key2 != NIL;
+ key1 = lnext(key1), key2 = lnext(key2))
{
- pathkey = lfirst(i);
- matched_joinkey_index = match_pathkey_joinkeys(pathkey, joinkeys,
- outer_or_inner);
+ List *subkey1 = lfirst(key1);
+ List *subkey2 = lfirst(key2);
+ List *i;
- if (matched_joinkey_index != -1)
+ /* We have to do this the hard way since the ordering of the subkey
+ * lists is arbitrary.
+ */
+ if (key1_subsetof_key2)
{
- matched_keys++;
- if (matchedJoinKeysPtr)
+ foreach(i, subkey1)
{
- JoinKey *joinkey = nth(matched_joinkey_index, joinkeys);
-
- matched_joinkeys = lappend(matched_joinkeys, joinkey);
+ if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey2))
+ {
+ key1_subsetof_key2 = false;
+ break;
+ }
}
+ }
- if (matchedJoinClausesPtr)
+ if (key2_subsetof_key1)
+ {
+ foreach(i, subkey2)
{
- Expr *joinclause = nth(matched_joinkey_index,
- joinclauses);
-
- Assert(joinclauses);
- matched_joinclauses = lappend(matched_joinclauses, joinclause);
+ if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey1))
+ {
+ key2_subsetof_key1 = false;
+ break;
+ }
}
}
- else
- /* A pathkey could not be matched. */
- break;
- }
- /*
- * Did we fail to match all the joinkeys? Extra pathkeys are no
- * problem.
- */
- if (matched_keys != length(joinkeys))
- {
- if (matchedJoinKeysPtr)
- *matchedJoinKeysPtr = NIL;
- if (matchedJoinClausesPtr)
- *matchedJoinClausesPtr = NIL;
- return false;
+ if (!key1_subsetof_key2 && !key2_subsetof_key1)
+ return PATHKEYS_DIFFERENT; /* no need to keep looking */
}
- if (matchedJoinKeysPtr)
- *matchedJoinKeysPtr = matched_joinkeys;
- if (matchedJoinClausesPtr)
- *matchedJoinClausesPtr = matched_joinclauses;
- return true;
+ /* If we reached the end of only one list, the other is longer and
+ * therefore not a subset. (We assume the additional sublist(s)
+ * of the other list are not NIL --- no pathkey list should ever have
+ * a NIL sublist.)
+ */
+ if (key1 != NIL)
+ key1_subsetof_key2 = false;
+ if (key2 != NIL)
+ key2_subsetof_key1 = false;
+
+ if (key1_subsetof_key2 && key2_subsetof_key1)
+ return PATHKEYS_EQUAL;
+ if (key1_subsetof_key2)
+ return PATHKEYS_BETTER2;
+ if (key2_subsetof_key1)
+ return PATHKEYS_BETTER1;
+ return PATHKEYS_DIFFERENT;
}
-
/*
- * match_pathkey_joinkeys
- * Returns the 0-based index into 'joinkeys' of the first joinkey whose
- * outer or inner pathkey matches any subkey of 'pathkey'.
- *
- * All these keys are equivalent, so any of them can match. See above.
+ * pathkeys_contained_in
+ * Common special case of compare_pathkeys: we just want to know
+ * if keys2 are at least as well sorted as keys1.
*/
-static int
-match_pathkey_joinkeys(List *pathkey,
- List *joinkeys,
- int outer_or_inner)
+bool
+pathkeys_contained_in(List *keys1, List *keys2)
{
- Var *key;
- int pos;
- List *i,
- *x;
- JoinKey *jk;
-
- foreach(i, pathkey)
+ switch (compare_pathkeys(keys1, keys2))
{
- key = (Var *) lfirst(i);
- pos = 0;
- foreach(x, joinkeys)
- {
- jk = (JoinKey *) lfirst(x);
- if (equal(key, extract_join_key(jk, outer_or_inner)))
- return pos;
- pos++;
- }
+ case PATHKEYS_EQUAL:
+ case PATHKEYS_BETTER2:
+ return true;
+ default:
+ break;
}
- return -1; /* no index found */
+ return false;
}
-
/*
- * get_cheapest_path_for_joinkeys
- * Attempts to find a path in 'paths' whose keys match a set of join
- * keys 'joinkeys'. To match,
- * 1. the path node ordering must equal 'ordering'.
- * 2. each pathkey of a given path must match(i.e., be(equal) to) the
- * appropriate pathkey of the corresponding join key in 'joinkeys',
- * i.e., the Nth path key must match its pathkeys against the pathkey of
- * the Nth join key in 'joinkeys'.
- *
- * 'joinkeys' is the list of key pairs to which the path keys must be
- * matched
- * 'ordering' is the ordering of the(outer) path to which 'joinkeys'
- * must correspond
- * 'paths' is a list of(inner) paths which are to be matched against
- * each join key in 'joinkeys'
- * 'outer_or_inner' is a flag that selects the desired pathkey of a join key
- * in 'joinkeys'
- *
- * Find the cheapest path that matches the join keys
+ * get_cheapest_path_for_pathkeys
+ * Find the cheapest path in 'paths' that satisfies the given pathkeys.
+ * Return NULL if no such path.
+ *
+ * 'paths' is a list of possible paths (either inner or outer)
+ * 'pathkeys' represents a required ordering
*/
Path *
-get_cheapest_path_for_joinkeys(List *joinkeys,
- PathOrder *ordering,
- List *paths,
- int outer_or_inner)
+get_cheapest_path_for_pathkeys(List *paths, List *pathkeys)
{
Path *matched_path = NULL;
List *i;
@@ -256,12 +264,8 @@ get_cheapest_path_for_joinkeys(List *joinkeys,
foreach(i, paths)
{
Path *path = (Path *) lfirst(i);
- int better_sort;
- if (order_joinkeys_by_pathkeys(path->pathkeys, joinkeys, NIL,
- outer_or_inner, NULL, NULL) &&
- pathorder_match(ordering, path->pathorder, &better_sort) &&
- better_sort == 0)
+ if (pathkeys_contained_in(pathkeys, path->pathkeys))
{
if (matched_path == NULL ||
path->path_cost < matched_path->path_cost)
@@ -271,78 +275,116 @@ get_cheapest_path_for_joinkeys(List *joinkeys,
return matched_path;
}
+/****************************************************************************
+ * NEW PATHKEY FORMATION
+ ****************************************************************************/
/*
- * make_pathkeys_from_joinkeys
- * Builds a pathkey list for a path by pulling one of the pathkeys from
- * a list of join keys 'joinkeys' and then finding the var node in the
- * target list 'tlist' that corresponds to that pathkey.
- *
- * 'joinkeys' is a list of join key pairs
- * 'tlist' is a relation target list
- * 'outer_or_inner' is a flag that selects the desired pathkey of a join key
- * in 'joinkeys'
- *
- * Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)).
- * It is a list of lists because of multi-key indexes.
+ * build_index_pathkeys
+ * Build a pathkeys list that describes the ordering induced by an index
+ * scan using the given index. (Note that an unordered index doesn't
+ * induce any ordering; such an index will have no sortop OIDS in
+ * its "ordering" field.)
+ *
+ * Vars in the resulting pathkeys list are taken from the rel's targetlist.
+ * If we can't find the indexkey in the targetlist, we assume that the
+ * ordering of that key is not interesting.
*/
List *
-make_pathkeys_from_joinkeys(List *joinkeys,
- List *tlist,
- int outer_or_inner)
+build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index)
{
- List *pathkeys = NIL;
- List *jk;
+ List *retval = NIL;
+ int *indexkeys = index->indexkeys;
+ Oid *ordering = index->ordering;
+
+ if (!indexkeys || indexkeys[0] == 0 ||
+ !ordering || ordering[0] == InvalidOid)
+ return NIL; /* unordered index? */
- foreach(jk, joinkeys)
+ if (index->indproc)
{
- JoinKey *jkey = (JoinKey *) lfirst(jk);
- Var *key;
- List *p,
- *p2;
- bool found = false;
+ /* Functional index: build a representation of the function call */
+ int relid = lfirsti(rel->relids);
+ Oid reloid = getrelid(relid, root->rtable);
+ Func *funcnode = makeNode(Func);
+ List *funcargs = NIL;
+
+ funcnode->funcid = index->indproc;
+ funcnode->functype = get_func_rettype(index->indproc);
+ funcnode->funcisindex = false;
+ funcnode->funcsize = 0;
+ funcnode->func_fcache = NULL;
+ funcnode->func_tlist = NIL;
+ funcnode->func_planlist = NIL;
+
+ while (*indexkeys != 0)
+ {
+ int varattno = *indexkeys;
+ Oid vartypeid = get_atttype(reloid, varattno);
+ int32 type_mod = get_atttypmod(reloid, varattno);
+
+ funcargs = lappend(funcargs,
+ makeVar(relid, varattno, vartypeid, type_mod,
+ 0, relid, varattno));
+ indexkeys++;
+ }
- key = (Var *) extract_join_key(jkey, outer_or_inner);
+ /* Make a one-sublist pathkeys list for the function expression */
+ retval = lcons(lcons(
+ makePathKeyItem((Node *) make_funcclause(funcnode, funcargs),
+ *ordering),
+ NIL), NIL);
+ }
+ else
+ {
+ /* Normal non-functional index */
+ List *rel_tlist = rel->targetlist;
- /* check to see if it is in the target list */
- if (matching_tlist_var(key, tlist))
+ while (*indexkeys != 0 && *ordering != InvalidOid)
{
+ Var *relvar = find_indexkey_var(*indexkeys, rel_tlist);
- /*
- * Include it in the pathkeys list if we haven't already done
- * so
+ /* If we can find no tlist entry for the n'th sort key,
+ * then we're done generating pathkeys; any subsequent sort keys
+ * no longer apply, since we can't represent the ordering properly
+ * even if there are tlist entries for them.
*/
- foreach(p, pathkeys)
- {
- List *pathkey = lfirst(p);
-
- foreach(p2, pathkey)
- {
- Var *pkey = lfirst(p2);
-
- if (equal(key, pkey))
- {
- found = true;
- break;
- }
- }
- if (found)
- break;
- }
- if (!found)
- pathkeys = lappend(pathkeys, lcons(key, NIL));
+ if (!relvar)
+ break;
+ /* OK, make a one-element sublist for this sort key */
+ retval = lappend(retval,
+ lcons(makePathKeyItem((Node *) relvar,
+ *ordering),
+ NIL));
+
+ indexkeys++;
+ ordering++;
}
}
- return pathkeys;
+
+ return retval;
}
+/*
+ * Find a var in a relation's targetlist that matches an indexkey attrnum.
+ */
+static Var *
+find_indexkey_var(int indexkey, List *tlist)
+{
+ List *temp;
-/****************************************************************************
- * NEW PATHKEY FORMATION
- ****************************************************************************/
+ foreach(temp, tlist)
+ {
+ Var *tle_var = get_expr(lfirst(temp));
+
+ if (IsA(tle_var, Var) && tle_var->varattno == indexkey)
+ return tle_var;
+ }
+ return NULL;
+}
/*
- * new_join_pathkeys
+ * build_join_pathkeys
* Build the path keys for a join relation constructed by mergejoin or
* nestloop join. These keys should include all the path key vars of the
* outer path (since the join will retain the ordering of the outer path)
@@ -362,21 +404,26 @@ make_pathkeys_from_joinkeys(List *joinkeys,
* the inner var will acquire the outer's ordering no matter which join
* method is actually used.
*
- * All vars in the result are copied from the join relation's tlist, not from
- * the given pathkeys or the join clauses. (Is that necessary? I suspect
- * not --- tgl)
+ * We drop pathkeys that are not vars of the join relation's tlist,
+ * on the assumption that they are not interesting to higher levels.
+ * (Is this correct?? To support expression pathkeys we might want to
+ * check that all vars mentioned in the key are in the tlist, instead.)
+ *
+ * All vars in the result are taken from the join relation's tlist,
+ * not from the given pathkeys or joinclauses.
*
* 'outer_pathkeys' is the list of the outer path's path keys
* 'join_rel_tlist' is the target list of the join relation
- * 'joinclauses' is the list of mergejoinable join clauses
+ * 'joinclauses' is the list of mergejoinable clauses to consider (note this
+ * is a list of RestrictInfos, not just bare qual clauses); can be NIL
*
* Returns the list of new path keys.
*
*/
List *
-new_join_pathkeys(List *outer_pathkeys,
- List *join_rel_tlist,
- List *joinclauses)
+build_join_pathkeys(List *outer_pathkeys,
+ List *join_rel_tlist,
+ List *joinclauses)
{
List *final_pathkeys = NIL;
List *i;
@@ -386,11 +433,11 @@ new_join_pathkeys(List *outer_pathkeys,
List *outer_pathkey = lfirst(i);
List *new_pathkey;
- new_pathkey = new_join_pathkey(outer_pathkey, join_rel_tlist,
- joinclauses);
+ new_pathkey = build_join_pathkey(outer_pathkey, join_rel_tlist,
+ joinclauses);
/* if we can find no sortable vars for the n'th sort key,
- * then we're done generating pathkeys; can't expect to order
- * subsequent vars. Not clear that this can really happen.
+ * then we're done generating pathkeys; any subsequent sort keys
+ * no longer apply, since we can't represent the ordering properly.
*/
if (new_pathkey == NIL)
break;
@@ -400,25 +447,22 @@ new_join_pathkeys(List *outer_pathkeys,
}
/*
- * new_join_pathkey
+ * build_join_pathkey
* Generate an individual pathkey sublist, consisting of the outer vars
* already mentioned in 'pathkey' plus any inner vars that are joined to
* them (and thus can now also be considered path keys, per discussion
* at the top of this file).
*
- * Note that each returned pathkey is the var node found in
+ * Note that each returned pathkey uses the var node found in
* 'join_rel_tlist' rather than the input pathkey or joinclause var node.
- * (Is this important?) Also, we return a fully copied list
- * that does not share any subnodes with existing data structures.
- * (Is that important, either?)
- *
- * Returns a new pathkey (list of pathkey variables).
+ * (Is this important?)
*
+ * Returns a new pathkey (list of PathKeyItems).
*/
static List *
-new_join_pathkey(List *pathkey,
- List *join_rel_tlist,
- List *joinclauses)
+build_join_pathkey(List *pathkey,
+ List *join_rel_tlist,
+ List *joinclauses)
{
List *new_pathkey = NIL;
List *i,
@@ -426,27 +470,193 @@ new_join_pathkey(List *pathkey,
foreach(i, pathkey)
{
- Var *key = (Var *) lfirst(i);
+ PathKeyItem *key = (PathKeyItem *) lfirst(i);
Expr *tlist_key;
- Assert(key);
+ Assert(key && IsA(key, PathKeyItem));
- tlist_key = matching_tlist_var(key, join_rel_tlist);
- if (tlist_key && !member(tlist_key, new_pathkey))
- new_pathkey = lcons(copyObject(tlist_key), new_pathkey);
+ tlist_key = matching_tlist_var((Var *) key->key, join_rel_tlist);
+ if (tlist_key)
+ new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
+ key->sortop),
+ new_pathkey);
foreach(j, joinclauses)
{
- Expr *joinclause = lfirst(j);
- Expr *tlist_other_var;
-
- tlist_other_var = matching_tlist_var(
- other_join_clause_var(key, joinclause),
- join_rel_tlist);
- if (tlist_other_var && !member(tlist_other_var, new_pathkey))
- new_pathkey = lcons(copyObject(tlist_other_var), new_pathkey);
+ RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
+ Expr *joinclause = restrictinfo->clause;
+ /* We assume the clause is a binary opclause... */
+ Var *l = get_leftop(joinclause);
+ Var *r = get_rightop(joinclause);
+ Var *other_var = NULL;
+ Oid other_sortop = InvalidOid;
+
+ if (var_equal((Var *) key->key, l))
+ {
+ other_var = r;
+ other_sortop = restrictinfo->right_sortop;
+ }
+ else if (var_equal((Var *) key->key, r))
+ {
+ other_var = l;
+ other_sortop = restrictinfo->left_sortop;
+ }
+
+ if (other_var && other_sortop)
+ {
+ tlist_key = matching_tlist_var(other_var, join_rel_tlist);
+ if (tlist_key)
+ new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
+ other_sortop),
+ new_pathkey);
+ }
}
}
return new_pathkey;
}
+
+/****************************************************************************
+ * PATHKEYS AND MERGECLAUSES
+ ****************************************************************************/
+
+/*
+ * find_mergeclauses_for_pathkeys
+ * This routine attempts to find a set of mergeclauses that can be
+ * used with a specified ordering for one of the input relations.
+ * If successful, it returns a list of mergeclauses.
+ *
+ * 'pathkeys' is a pathkeys list showing the ordering of an input path.
+ * It doesn't matter whether it is for the inner or outer path.
+ * 'restrictinfos' is a list of mergejoinable restriction clauses for the
+ * join relation being formed.
+ *
+ * The result is NIL if no merge can be done, else a maximal list of
+ * usable mergeclauses (represented as a list of their restrictinfo nodes).
+ *
+ * XXX Ideally we ought to be considering context, ie what path orderings
+ * are available on the other side of the join, rather than just making
+ * an arbitrary choice among the mergeclause orders that will work for
+ * this side of the join.
+ */
+List *
+find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
+{
+ List *mergeclauses = NIL;
+ List *i;
+
+ foreach(i, pathkeys)
+ {
+ List *pathkey = lfirst(i);
+ RestrictInfo *matched_restrictinfo = NULL;
+ List *j;
+
+ /*
+ * We can match any of the keys in this pathkey sublist,
+ * since they're all equivalent. And we can match against
+ * either left or right side of any mergejoin clause we haven't
+ * used yet. For the moment we use a dumb "greedy" algorithm
+ * with no backtracking. Is it worth being any smarter to
+ * make a longer list of usable mergeclauses? Probably not.
+ */
+ foreach(j, pathkey)
+ {
+ PathKeyItem *keyitem = lfirst(j);
+ Var *keyvar = (Var *) keyitem->key;
+ List *k;
+
+ if (! IsA(keyvar, Var))
+ continue; /* for now, only Vars can be mergejoined */
+
+ foreach(k, restrictinfos)
+ {
+ RestrictInfo *restrictinfo = lfirst(k);
+
+ Assert(restrictinfo->mergejoinoperator != InvalidOid);
+
+ if ((var_equal(keyvar, get_leftop(restrictinfo->clause)) ||
+ var_equal(keyvar, get_rightop(restrictinfo->clause))) &&
+ ! member(restrictinfo, mergeclauses))
+ {
+ matched_restrictinfo = restrictinfo;
+ break;
+ }
+ }
+ if (matched_restrictinfo)
+ break;
+ }
+
+ /*
+ * If we didn't find a mergeclause, we're done --- any additional
+ * sort-key positions in the pathkeys are useless. (But we can
+ * still mergejoin if we found at least one mergeclause.)
+ */
+ if (! matched_restrictinfo)
+ break;
+ /*
+ * If we did find a usable mergeclause for this sort-key position,
+ * add it to result list.
+ */
+ mergeclauses = lappend(mergeclauses, matched_restrictinfo);
+ }
+
+ return mergeclauses;
+}
+
+/*
+ * make_pathkeys_for_mergeclauses
+ * Builds a pathkey list representing the explicit sort order that
+ * must be applied to a path in order to make it usable for the
+ * given mergeclauses.
+ *
+ * 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
+ * that will be used in a merge join.
+ * 'tlist' is a relation target list for either the inner or outer
+ * side of the proposed join rel.
+ *
+ * Returns a pathkeys list that can be applied to the indicated relation.
+ *
+ * Note that it is not this routine's job to decide whether sorting is
+ * actually needed for a particular input path. Assume a sort is necessary;
+ * just make the keys, eh?
+ */
+List *
+make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist)
+{
+ List *pathkeys = NIL;
+ List *i;
+
+ foreach(i, mergeclauses)
+ {
+ RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
+ Var *key;
+ Oid sortop;
+
+ /*
+ * Find the key and sortop needed for this mergeclause.
+ *
+ * We can use either side of the mergeclause, since we haven't yet
+ * committed to which side will be inner.
+ */
+ Assert(restrictinfo->mergejoinoperator != InvalidOid);
+ key = (Var *) matching_tlist_var(get_leftop(restrictinfo->clause),
+ tlist);
+ sortop = restrictinfo->left_sortop;
+ if (! key)
+ {
+ key = (Var *) matching_tlist_var(get_rightop(restrictinfo->clause),
+ tlist);
+ sortop = restrictinfo->right_sortop;
+ }
+ if (! key)
+ elog(ERROR, "make_pathkeys_for_mergeclauses: can't find key");
+ /*
+ * Add a pathkey sublist for this sort item
+ */
+ pathkeys = lappend(pathkeys,
+ lcons(makePathKeyItem((Node *) key, sortop),
+ NIL));
+ }
+
+ return pathkeys;
+}