mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
Redo postgres_fdw's planner code so it can handle parameterized paths.
I wasn't going to ship this without having at least some example of how to do that. This version isn't terribly bright; in particular it won't consider any combinations of multiple join clauses. Given the cost of executing a remote EXPLAIN, I'm not sure we want to be very aggressive about doing that, anyway. In support of this, refactor generate_implied_equalities_for_indexcol so that it can be used to extract equivalence clauses that aren't necessarily tied to an index.
This commit is contained in:
@ -512,7 +512,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
|
||||
* be more than one EC that matches the expression; if so it's order-dependent
|
||||
* which one you get. This is annoying but it only happens in corner cases,
|
||||
* so for now we live with just reporting the first match. See also
|
||||
* generate_implied_equalities_for_indexcol and match_pathkeys_to_index.)
|
||||
* generate_implied_equalities_for_column and match_pathkeys_to_index.)
|
||||
*
|
||||
* If create_it is TRUE, we'll build a new EquivalenceClass when there is no
|
||||
* match. If create_it is FALSE, we just return NULL when no match.
|
||||
@ -2013,15 +2013,21 @@ mutate_eclass_expressions(PlannerInfo *root,
|
||||
|
||||
|
||||
/*
|
||||
* generate_implied_equalities_for_indexcol
|
||||
* Create EC-derived joinclauses usable with a specific index column.
|
||||
* generate_implied_equalities_for_column
|
||||
* Create EC-derived joinclauses usable with a specific column.
|
||||
*
|
||||
* We assume that any given index column could appear in only one EC.
|
||||
* This is used by indxpath.c to extract potentially indexable joinclauses
|
||||
* from ECs, and can be used by foreign data wrappers for similar purposes.
|
||||
* We assume that only expressions in Vars of a single table are of interest,
|
||||
* but the caller provides a callback function to identify exactly which
|
||||
* such expressions it would like to know about.
|
||||
*
|
||||
* We assume that any given table/index column could appear in only one EC.
|
||||
* (This should be true in all but the most pathological cases, and if it
|
||||
* isn't, we stop on the first match anyway.) Therefore, what we return
|
||||
* is a redundant list of clauses equating the index column to each of
|
||||
* is a redundant list of clauses equating the table/index column to each of
|
||||
* the other-relation values it is known to be equal to. Any one of
|
||||
* these clauses can be used to create a parameterized indexscan, and there
|
||||
* these clauses can be used to create a parameterized path, and there
|
||||
* is no value in using more than one. (But it *is* worthwhile to create
|
||||
* a separate parameterized path for each one, since that leads to different
|
||||
* join orders.)
|
||||
@ -2030,13 +2036,13 @@ mutate_eclass_expressions(PlannerInfo *root,
|
||||
* to, so as to save the work of creating useless clauses.
|
||||
*/
|
||||
List *
|
||||
generate_implied_equalities_for_indexcol(PlannerInfo *root,
|
||||
IndexOptInfo *index,
|
||||
int indexcol,
|
||||
Relids prohibited_rels)
|
||||
generate_implied_equalities_for_column(PlannerInfo *root,
|
||||
RelOptInfo *rel,
|
||||
ec_matches_callback_type callback,
|
||||
void *callback_arg,
|
||||
Relids prohibited_rels)
|
||||
{
|
||||
List *result = NIL;
|
||||
RelOptInfo *rel = index->rel;
|
||||
bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
|
||||
Index parent_relid;
|
||||
ListCell *lc1;
|
||||
@ -2069,11 +2075,11 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Scan members, looking for a match to the indexable column. Note
|
||||
* 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 index
|
||||
* 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.)
|
||||
@ -2083,8 +2089,7 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
|
||||
{
|
||||
cur_em = (EquivalenceMember *) lfirst(lc2);
|
||||
if (bms_equal(cur_em->em_relids, rel->relids) &&
|
||||
eclass_member_matches_indexcol(cur_ec, cur_em,
|
||||
index, indexcol))
|
||||
callback(root, rel, cur_ec, cur_em, callback_arg))
|
||||
break;
|
||||
cur_em = NULL;
|
||||
}
|
||||
|
@ -78,6 +78,13 @@ typedef struct
|
||||
Bitmapset *clauseids; /* quals+preds represented as a bitmapset */
|
||||
} PathClauseUsage;
|
||||
|
||||
/* Callback argument for ec_member_matches_indexcol */
|
||||
typedef struct
|
||||
{
|
||||
IndexOptInfo *index; /* index we're considering */
|
||||
int indexcol; /* index column we want to match to */
|
||||
} ec_member_matches_arg;
|
||||
|
||||
|
||||
static void consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
@ -162,6 +169,9 @@ static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
|
||||
List **clause_columns_p);
|
||||
static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
|
||||
int indexcol, Expr *clause, Oid pk_opfamily);
|
||||
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
|
||||
EquivalenceClass *ec, EquivalenceMember *em,
|
||||
void *arg);
|
||||
static bool match_boolean_index_clause(Node *clause, int indexcol,
|
||||
IndexOptInfo *index);
|
||||
static bool match_special_index_operator(Expr *clause,
|
||||
@ -645,7 +655,7 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
|
||||
/*
|
||||
* Add applicable eclass join clauses. The clauses generated for each
|
||||
* column are redundant (cf generate_implied_equalities_for_indexcol),
|
||||
* column are redundant (cf generate_implied_equalities_for_column),
|
||||
* so we need at most one. This is the only exception to the general
|
||||
* rule of using all available index clauses.
|
||||
*/
|
||||
@ -1992,18 +2002,22 @@ match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index,
|
||||
|
||||
for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
|
||||
{
|
||||
ec_member_matches_arg arg;
|
||||
List *clauses;
|
||||
|
||||
/* Generate clauses, skipping any that join to lateral_referencers */
|
||||
clauses = generate_implied_equalities_for_indexcol(root,
|
||||
index,
|
||||
indexcol,
|
||||
lateral_referencers);
|
||||
arg.index = index;
|
||||
arg.indexcol = indexcol;
|
||||
clauses = generate_implied_equalities_for_column(root,
|
||||
index->rel,
|
||||
ec_member_matches_indexcol,
|
||||
(void *) &arg,
|
||||
lateral_referencers);
|
||||
|
||||
/*
|
||||
* We have to check whether the results actually do match the index,
|
||||
* since for non-btree indexes the EC's equality operators might not
|
||||
* be in the index opclass (cf eclass_member_matches_indexcol).
|
||||
* be in the index opclass (cf ec_member_matches_indexcol).
|
||||
*/
|
||||
match_clauses_to_index(index, clauses, clauseset);
|
||||
}
|
||||
@ -2682,15 +2696,18 @@ check_partial_indexes(PlannerInfo *root, RelOptInfo *rel)
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* eclass_member_matches_indexcol
|
||||
* ec_member_matches_indexcol
|
||||
* Test whether an EquivalenceClass member matches an index column.
|
||||
*
|
||||
* This is exported for use by generate_implied_equalities_for_indexcol.
|
||||
* This is a callback for use by generate_implied_equalities_for_column.
|
||||
*/
|
||||
bool
|
||||
eclass_member_matches_indexcol(EquivalenceClass *ec, EquivalenceMember *em,
|
||||
IndexOptInfo *index, int indexcol)
|
||||
static bool
|
||||
ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
|
||||
EquivalenceClass *ec, EquivalenceMember *em,
|
||||
void *arg)
|
||||
{
|
||||
IndexOptInfo *index = ((ec_member_matches_arg *) arg)->index;
|
||||
int indexcol = ((ec_member_matches_arg *) arg)->indexcol;
|
||||
Oid curFamily = index->opfamily[indexcol];
|
||||
Oid curCollation = index->indexcollations[indexcol];
|
||||
|
||||
@ -2701,7 +2718,7 @@ eclass_member_matches_indexcol(EquivalenceClass *ec, EquivalenceMember *em,
|
||||
* whether clauses generated from the EC could be used with the index, so
|
||||
* don't check the opfamily. This might mean we return "true" for a
|
||||
* useless EC, so we have to recheck the results of
|
||||
* generate_implied_equalities_for_indexcol; see
|
||||
* generate_implied_equalities_for_column; see
|
||||
* match_eclass_clauses_to_index.
|
||||
*/
|
||||
if (index->relam == BTREE_AM_OID &&
|
||||
|
Reference in New Issue
Block a user