Commit 9cbc4b80 authored by Tom Lane's avatar Tom Lane

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.
parent 08af1a0a
This diff is collapsed.
...@@ -446,6 +446,27 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't ...@@ -446,6 +446,27 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1"
(4 rows) (4 rows)
-- parameterized remote path
EXPLAIN (VERBOSE, COSTS false)
SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Nested Loop
Output: a.c1, a.c2, a.c3, a.c4, a.c5, a.c6, a.c7, a.c8, b.c1, b.c2, b.c3, b.c4, b.c5, b.c6, b.c7, b.c8
-> Foreign Scan on public.ft2 a
Output: a.c1, a.c2, a.c3, a.c4, a.c5, a.c6, a.c7, a.c8
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 47))
-> Foreign Scan on public.ft2 b
Output: b.c1, b.c2, b.c3, b.c4, b.c5, b.c6, b.c7, b.c8
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (($1::integer = "C 1"))
(8 rows)
SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 | c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
----+----+-------+------------------------------+--------------------------+----+------------+-----+----+----+-------+------------------------------+--------------------------+----+------------+-----
47 | 7 | 00047 | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7 | 7 | foo | 7 | 7 | 00007 | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7 | 7 | foo
(1 row)
-- =================================================================== -- ===================================================================
-- parameterized queries -- parameterized queries
-- =================================================================== -- ===================================================================
...@@ -646,7 +667,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); ...@@ -646,7 +667,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1);
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8 Output: c1, c2, c3, c4, c5, c6, c7, c8
Filter: (t1.c8 = $1) Filter: (t1.c8 = $1)
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = $2::integer)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = $1::integer))
(4 rows) (4 rows)
EXECUTE st5('foo', 1); EXECUTE st5('foo', 1);
......
This diff is collapsed.
...@@ -42,17 +42,20 @@ extern int ExtractConnectionOptions(List *defelems, ...@@ -42,17 +42,20 @@ extern int ExtractConnectionOptions(List *defelems,
extern void classifyConditions(PlannerInfo *root, extern void classifyConditions(PlannerInfo *root,
RelOptInfo *baserel, RelOptInfo *baserel,
List **remote_conds, List **remote_conds,
List **param_conds, List **local_conds);
List **local_conds, extern bool is_foreign_expr(PlannerInfo *root,
List **param_numbers); RelOptInfo *baserel,
Expr *expr);
extern void deparseSelectSql(StringInfo buf, extern void deparseSelectSql(StringInfo buf,
PlannerInfo *root, PlannerInfo *root,
RelOptInfo *baserel, RelOptInfo *baserel,
Bitmapset *attrs_used); Bitmapset *attrs_used);
extern void appendWhereClause(StringInfo buf, extern void appendWhereClause(StringInfo buf,
PlannerInfo *root, PlannerInfo *root,
RelOptInfo *baserel,
List *exprs, List *exprs,
bool is_first); bool is_first,
List **params);
extern void deparseInsertSql(StringInfo buf, PlannerInfo *root, extern void deparseInsertSql(StringInfo buf, PlannerInfo *root,
Index rtindex, Relation rel, Index rtindex, Relation rel,
List *targetAttrs, List *returningList); List *targetAttrs, List *returningList);
......
...@@ -189,6 +189,10 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, ...@@ -189,6 +189,10 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1,
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- ArrayRef EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- ArrayRef
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; -- check special chars EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; -- check special chars
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't be sent to remote EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't be sent to remote
-- parameterized remote path
EXPLAIN (VERBOSE, COSTS false)
SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2;
SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2;
-- =================================================================== -- ===================================================================
-- parameterized queries -- parameterized queries
......
...@@ -512,7 +512,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids, ...@@ -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 * 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, * 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 * 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 * 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. * match. If create_it is FALSE, we just return NULL when no match.
...@@ -2013,15 +2013,21 @@ mutate_eclass_expressions(PlannerInfo *root, ...@@ -2013,15 +2013,21 @@ mutate_eclass_expressions(PlannerInfo *root,
/* /*
* generate_implied_equalities_for_indexcol * generate_implied_equalities_for_column
* Create EC-derived joinclauses usable with a specific index 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 * (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 * 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 * 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 * 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 * a separate parameterized path for each one, since that leads to different
* join orders.) * join orders.)
...@@ -2030,13 +2036,13 @@ mutate_eclass_expressions(PlannerInfo *root, ...@@ -2030,13 +2036,13 @@ mutate_eclass_expressions(PlannerInfo *root,
* to, so as to save the work of creating useless clauses. * to, so as to save the work of creating useless clauses.
*/ */
List * List *
generate_implied_equalities_for_indexcol(PlannerInfo *root, generate_implied_equalities_for_column(PlannerInfo *root,
IndexOptInfo *index, RelOptInfo *rel,
int indexcol, ec_matches_callback_type callback,
Relids prohibited_rels) void *callback_arg,
Relids prohibited_rels)
{ {
List *result = NIL; List *result = NIL;
RelOptInfo *rel = index->rel;
bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL); bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
Index parent_relid; Index parent_relid;
ListCell *lc1; ListCell *lc1;
...@@ -2069,11 +2075,11 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root, ...@@ -2069,11 +2075,11 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
continue; 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 * that child EC members are considered, but only when they belong to
* the target relation. (Unlike regular members, the same expression * the target relation. (Unlike regular members, the same expression
* could be a child member of more than one EC. Therefore, it's * 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 * column gets matched to. This is annoying but it only happens in
* corner cases, so for now we live with just reporting the first * corner cases, so for now we live with just reporting the first
* match. See also get_eclass_for_sort_expr.) * match. See also get_eclass_for_sort_expr.)
...@@ -2083,8 +2089,7 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root, ...@@ -2083,8 +2089,7 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
{ {
cur_em = (EquivalenceMember *) lfirst(lc2); cur_em = (EquivalenceMember *) lfirst(lc2);
if (bms_equal(cur_em->em_relids, rel->relids) && if (bms_equal(cur_em->em_relids, rel->relids) &&
eclass_member_matches_indexcol(cur_ec, cur_em, callback(root, rel, cur_ec, cur_em, callback_arg))
index, indexcol))
break; break;
cur_em = NULL; cur_em = NULL;
} }
......
...@@ -78,6 +78,13 @@ typedef struct ...@@ -78,6 +78,13 @@ typedef struct
Bitmapset *clauseids; /* quals+preds represented as a bitmapset */ Bitmapset *clauseids; /* quals+preds represented as a bitmapset */
} PathClauseUsage; } 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, static void consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel,
IndexOptInfo *index, IndexOptInfo *index,
...@@ -162,6 +169,9 @@ static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys, ...@@ -162,6 +169,9 @@ static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List **clause_columns_p); List **clause_columns_p);
static Expr *match_clause_to_ordering_op(IndexOptInfo *index, static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
int indexcol, Expr *clause, Oid pk_opfamily); 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, static bool match_boolean_index_clause(Node *clause, int indexcol,
IndexOptInfo *index); IndexOptInfo *index);
static bool match_special_index_operator(Expr *clause, static bool match_special_index_operator(Expr *clause,
...@@ -645,7 +655,7 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel, ...@@ -645,7 +655,7 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
/* /*
* Add applicable eclass join clauses. The clauses generated for each * 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 * so we need at most one. This is the only exception to the general
* rule of using all available index clauses. * rule of using all available index clauses.
*/ */
...@@ -1992,18 +2002,22 @@ match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index, ...@@ -1992,18 +2002,22 @@ match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index,
for (indexcol = 0; indexcol < index->ncolumns; indexcol++) for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
{ {
ec_member_matches_arg arg;
List *clauses; List *clauses;
/* Generate clauses, skipping any that join to lateral_referencers */ /* Generate clauses, skipping any that join to lateral_referencers */
clauses = generate_implied_equalities_for_indexcol(root, arg.index = index;
index, arg.indexcol = indexcol;
indexcol, clauses = generate_implied_equalities_for_column(root,
lateral_referencers); index->rel,
ec_member_matches_indexcol,
(void *) &arg,
lateral_referencers);
/* /*
* We have to check whether the results actually do match the index, * We have to check whether the results actually do match the index,
* since for non-btree indexes the EC's equality operators might not * 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); match_clauses_to_index(index, clauses, clauseset);
} }
...@@ -2682,15 +2696,18 @@ check_partial_indexes(PlannerInfo *root, RelOptInfo *rel) ...@@ -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. * 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 static bool
eclass_member_matches_indexcol(EquivalenceClass *ec, EquivalenceMember *em, ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
IndexOptInfo *index, int indexcol) 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 curFamily = index->opfamily[indexcol];
Oid curCollation = index->indexcollations[indexcol]; Oid curCollation = index->indexcollations[indexcol];
...@@ -2701,7 +2718,7 @@ eclass_member_matches_indexcol(EquivalenceClass *ec, EquivalenceMember *em, ...@@ -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 * 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 * don't check the opfamily. This might mean we return "true" for a
* useless EC, so we have to recheck the results of * 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. * match_eclass_clauses_to_index.
*/ */
if (index->relam == BTREE_AM_OID && if (index->relam == BTREE_AM_OID &&
......
...@@ -49,9 +49,6 @@ extern List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel, ...@@ -49,9 +49,6 @@ extern List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
extern bool relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel, extern bool relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
List *restrictlist, List *restrictlist,
List *exprlist, List *oprlist); List *exprlist, List *oprlist);
extern bool eclass_member_matches_indexcol(EquivalenceClass *ec,
EquivalenceMember *em,
IndexOptInfo *index, int indexcol);
extern bool match_index_to_operand(Node *operand, int indexcol, extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index); IndexOptInfo *index);
extern void expand_indexqual_conditions(IndexOptInfo *index, extern void expand_indexqual_conditions(IndexOptInfo *index,
...@@ -99,6 +96,12 @@ extern bool have_join_order_restriction(PlannerInfo *root, ...@@ -99,6 +96,12 @@ extern bool have_join_order_restriction(PlannerInfo *root,
* equivclass.c * equivclass.c
* routines for managing EquivalenceClasses * routines for managing EquivalenceClasses
*/ */
typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
RelOptInfo *rel,
EquivalenceClass *ec,
EquivalenceMember *em,
void *arg);
extern bool process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, extern bool process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
bool below_outer_join); bool below_outer_join);
extern Expr *canonicalize_ec_expression(Expr *expr, extern Expr *canonicalize_ec_expression(Expr *expr,
...@@ -126,10 +129,11 @@ extern void mutate_eclass_expressions(PlannerInfo *root, ...@@ -126,10 +129,11 @@ extern void mutate_eclass_expressions(PlannerInfo *root,
Node *(*mutator) (), Node *(*mutator) (),
void *context, void *context,
bool include_child_exprs); bool include_child_exprs);
extern List *generate_implied_equalities_for_indexcol(PlannerInfo *root, extern List *generate_implied_equalities_for_column(PlannerInfo *root,
IndexOptInfo *index, RelOptInfo *rel,
int indexcol, ec_matches_callback_type callback,
Relids prohibited_rels); void *callback_arg,
Relids prohibited_rels);
extern bool have_relevant_eclass_joinclause(PlannerInfo *root, extern bool have_relevant_eclass_joinclause(PlannerInfo *root,
RelOptInfo *rel1, RelOptInfo *rel2); RelOptInfo *rel1, RelOptInfo *rel2);
extern bool has_relevant_eclass_joinclause(PlannerInfo *root, extern bool has_relevant_eclass_joinclause(PlannerInfo *root,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment