aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/createplan.c
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2015-05-01 08:50:35 -0400
committerRobert Haas <rhaas@postgresql.org>2015-05-01 08:50:35 -0400
commite7cb7ee14555cc9c5773e2c102efd6371f6f2005 (patch)
treefc1d7ab49a9f9eee6027a6ace17c37963e514cc9 /src/backend/optimizer/plan/createplan.c
parent2b22795b32576fa7173b501b646581a17de35902 (diff)
downloadpostgresql-e7cb7ee14555cc9c5773e2c102efd6371f6f2005.tar.gz
postgresql-e7cb7ee14555cc9c5773e2c102efd6371f6f2005.zip
Allow FDWs and custom scan providers to replace joins with scans.
Foreign data wrappers can use this capability for so-called "join pushdown"; that is, instead of executing two separate foreign scans and then joining the results locally, they can generate a path which performs the join on the remote server and then is scanned locally. This commit does not extend postgres_fdw to take advantage of this capability; it just provides the infrastructure. Custom scan providers can use this in a similar way. Previously, it was only possible for a custom scan provider to scan a single relation. Now, it can scan an entire join tree, provided of course that it knows how to produce the same results that the join would have produced if executed normally. KaiGai Kohei, reviewed by Shigeru Hanada, Ashutosh Bapat, and me.
Diffstat (limited to 'src/backend/optimizer/plan/createplan.c')
-rw-r--r--src/backend/optimizer/plan/createplan.c83
1 files changed, 68 insertions, 15 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cb69c03df00..eeb2a417643 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -44,7 +44,6 @@
#include "utils/lsyscache.h"
-static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
static List *build_path_tlist(PlannerInfo *root, Path *path);
static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
@@ -220,7 +219,7 @@ create_plan(PlannerInfo *root, Path *best_path)
* create_plan_recurse
* Recursive guts of create_plan().
*/
-static Plan *
+Plan *
create_plan_recurse(PlannerInfo *root, Path *best_path)
{
Plan *plan;
@@ -1961,16 +1960,25 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
ForeignScan *scan_plan;
RelOptInfo *rel = best_path->path.parent;
Index scan_relid = rel->relid;
- RangeTblEntry *rte;
+ Oid rel_oid = InvalidOid;
Bitmapset *attrs_used = NULL;
ListCell *lc;
int i;
- /* it should be a base rel... */
- Assert(scan_relid > 0);
- Assert(rel->rtekind == RTE_RELATION);
- rte = planner_rt_fetch(scan_relid, root);
- Assert(rte->rtekind == RTE_RELATION);
+ /*
+ * If we're scanning a base relation, look up the OID.
+ * (We can skip this if scanning a join relation.)
+ */
+ if (scan_relid > 0)
+ {
+ RangeTblEntry *rte;
+
+ Assert(rel->rtekind == RTE_RELATION);
+ rte = planner_rt_fetch(scan_relid, root);
+ Assert(rte->rtekind == RTE_RELATION);
+ rel_oid = rte->relid;
+ }
+ Assert(rel->fdwroutine != NULL);
/*
* Sort clauses into best execution order. We do this first since the FDW
@@ -1985,13 +1993,39 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
* has selected some join clauses for remote use but also wants them
* rechecked locally).
*/
- scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rte->relid,
+ scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid,
best_path,
tlist, scan_clauses);
+ /*
+ * Sanity check. There may be resjunk entries in fdw_ps_tlist that
+ * are included only to help EXPLAIN deparse plans properly. We require
+ * that these are at the end, so that when the executor builds the scan
+ * descriptor based on the non-junk entries, it gets the attribute
+ * numbers correct.
+ */
+ if (scan_plan->scan.scanrelid == 0)
+ {
+ bool found_resjunk = false;
+
+ foreach (lc, scan_plan->fdw_ps_tlist)
+ {
+ TargetEntry *tle = lfirst(lc);
+
+ if (tle->resjunk)
+ found_resjunk = true;
+ else if (found_resjunk)
+ elog(ERROR, "junk TLE should not apper prior to valid one");
+ }
+ }
+ /* Set the relids that are represented by this foreign scan for Explain */
+ scan_plan->fdw_relids = best_path->path.parent->relids;
/* Copy cost data from Path to Plan; no need to make FDW do this */
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+ /* Track FDW server-id; no need to make FDW do this */
+ scan_plan->fdw_handler = rel->fdw_handler;
+
/*
* Replace any outer-relation variables with nestloop params in the qual
* and fdw_exprs expressions. We do this last so that the FDW doesn't
@@ -2053,12 +2087,7 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
{
CustomScan *cplan;
RelOptInfo *rel = best_path->path.parent;
-
- /*
- * Right now, all we can support is CustomScan node which is associated
- * with a particular base relation to be scanned.
- */
- Assert(rel && rel->reloptkind == RELOPT_BASEREL);
+ ListCell *lc;
/*
* Sort clauses into the best execution order, although custom-scan
@@ -2078,6 +2107,30 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
Assert(IsA(cplan, CustomScan));
/*
+ * Sanity check. There may be resjunk entries in custom_ps_tlist that
+ * are included only to help EXPLAIN deparse plans properly. We require
+ * that these are at the end, so that when the executor builds the scan
+ * descriptor based on the non-junk entries, it gets the attribute
+ * numbers correct.
+ */
+ if (cplan->scan.scanrelid == 0)
+ {
+ bool found_resjunk = false;
+
+ foreach (lc, cplan->custom_ps_tlist)
+ {
+ TargetEntry *tle = lfirst(lc);
+
+ if (tle->resjunk)
+ found_resjunk = true;
+ else if (found_resjunk)
+ elog(ERROR, "junk TLE should not apper prior to valid one");
+ }
+ }
+ /* Set the relids that are represented by this custom scan for Explain */
+ cplan->custom_relids = best_path->path.parent->relids;
+
+ /*
* Copy cost data from Path to Plan; no need to make custom-plan providers
* do this
*/