aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEtsuro Fujita <efujita@postgresql.org>2023-08-15 16:45:00 +0900
committerEtsuro Fujita <efujita@postgresql.org>2023-08-15 16:45:00 +0900
commit9e9931d2bf40e2fea447d779c2e133c2c1256ef3 (patch)
tree33ece047786463ecd497cdd8178ca51e5f65c200 /src
parent5ffb7c775062ef18756e515ac96f06d012cbb950 (diff)
downloadpostgresql-9e9931d2bf40e2fea447d779c2e133c2c1256ef3.tar.gz
postgresql-9e9931d2bf40e2fea447d779c2e133c2c1256ef3.zip
Re-allow FDWs and custom scan providers to replace joins with pseudoconstant quals.
This was disabled in commit 6f80a8d9c due to the lack of support for handling of pseudoconstant quals assigned to replaced joins in createplan.c. To re-allow it, this patch adds the support by 1) modifying the ForeignPath and CustomPath structs so that if they represent foreign and custom scans replacing a join with a scan, they store the list of RestrictInfo nodes to apply to the join, as in JoinPaths, and by 2) modifying create_scan_plan() in createplan.c so that it uses that list in that case, instead of the baserestrictinfo list, to get pseudoconstant quals assigned to the join, as mentioned in the commit message for that commit. Important item for the release notes: this is non-backwards-compatible since it modifies the ForeignPath and CustomPath structs, as mentioned above, and changes the argument lists for FDW helper functions create_foreignscan_path(), create_foreign_join_path(), and create_foreign_upper_path(). Richard Guo, with some additional changes by me, reviewed by Nishant Sharma, Suraj Kharage, and Richard Guo. Discussion: https://postgr.es/m/CADrsxdbcN1vejBaf8a%2BQhrZY5PXL-04mCd4GDu6qm6FigDZd6Q%40mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/path/joinpath.c19
-rw-r--r--src/backend/optimizer/plan/createplan.c21
-rw-r--r--src/backend/optimizer/util/pathnode.c10
-rw-r--r--src/backend/optimizer/util/restrictinfo.c30
-rw-r--r--src/include/nodes/pathnodes.h10
-rw-r--r--src/include/optimizer/pathnode.h3
-rw-r--r--src/include/optimizer/restrictinfo.h2
7 files changed, 45 insertions, 50 deletions
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 059e605e04e..821d282497b 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -24,7 +24,6 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
-#include "optimizer/restrictinfo.h"
#include "utils/typcache.h"
/* Hook for plugins to get control in add_paths_to_joinrel() */
@@ -131,7 +130,6 @@ add_paths_to_joinrel(PlannerInfo *root,
{
JoinPathExtraData extra;
bool mergejoin_allowed = true;
- bool consider_join_pushdown = false;
ListCell *lc;
Relids joinrelids;
@@ -324,24 +322,12 @@ add_paths_to_joinrel(PlannerInfo *root,
jointype, &extra);
/*
- * createplan.c does not currently support handling of pseudoconstant
- * clauses assigned to joins pushed down by extensions; check if the
- * restrictlist has such clauses, and if so, disallow pushing down joins.
- */
- if ((joinrel->fdwroutine &&
- joinrel->fdwroutine->GetForeignJoinPaths) ||
- set_join_pathlist_hook)
- consider_join_pushdown = !has_pseudoconstant_clauses(root,
- restrictlist);
-
- /*
* 5. If inner and outer relations are foreign tables (or joins) belonging
* to the same server and assigned to the same user to check access
* permissions as, give the FDW a chance to push down joins.
*/
if (joinrel->fdwroutine &&
- joinrel->fdwroutine->GetForeignJoinPaths &&
- consider_join_pushdown)
+ joinrel->fdwroutine->GetForeignJoinPaths)
joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel,
outerrel, innerrel,
jointype, &extra);
@@ -349,8 +335,7 @@ add_paths_to_joinrel(PlannerInfo *root,
/*
* 6. Finally, give extensions a chance to manipulate the path list.
*/
- if (set_join_pathlist_hook &&
- consider_join_pushdown)
+ if (set_join_pathlist_hook)
set_join_pathlist_hook(root, joinrel, outerrel, innerrel,
jointype, &extra);
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index af48109058d..34ca6d4ac21 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -599,8 +599,27 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
* Detect whether we have any pseudoconstant quals to deal with. Then, if
* we'll need a gating Result node, it will be able to project, so there
* are no requirements on the child's tlist.
+ *
+ * If this replaces a join, it must be a foreign scan or a custom scan,
+ * and the FDW or the custom scan provider would have stored in the best
+ * path the list of RestrictInfo nodes to apply to the join; check against
+ * that list in that case.
*/
- gating_clauses = get_gating_quals(root, scan_clauses);
+ if (IS_JOIN_REL(rel))
+ {
+ List *join_clauses;
+
+ Assert(best_path->pathtype == T_ForeignScan ||
+ best_path->pathtype == T_CustomScan);
+ if (best_path->pathtype == T_ForeignScan)
+ join_clauses = ((ForeignPath *) best_path)->fdw_restrictinfo;
+ else
+ join_clauses = ((CustomPath *) best_path)->custom_restrictinfo;
+
+ gating_clauses = get_gating_quals(root, join_clauses);
+ }
+ else
+ gating_clauses = get_gating_quals(root, scan_clauses);
if (gating_clauses)
flags = 0;
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 754f0b9f34c..211ba65389d 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -2229,6 +2229,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
List *pathkeys,
Relids required_outer,
Path *fdw_outerpath,
+ List *fdw_restrictinfo,
List *fdw_private)
{
ForeignPath *pathnode = makeNode(ForeignPath);
@@ -2250,6 +2251,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
pathnode->path.pathkeys = pathkeys;
pathnode->fdw_outerpath = fdw_outerpath;
+ pathnode->fdw_restrictinfo = fdw_restrictinfo;
pathnode->fdw_private = fdw_private;
return pathnode;
@@ -2273,6 +2275,7 @@ create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
List *pathkeys,
Relids required_outer,
Path *fdw_outerpath,
+ List *fdw_restrictinfo,
List *fdw_private)
{
ForeignPath *pathnode = makeNode(ForeignPath);
@@ -2300,6 +2303,7 @@ create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
pathnode->path.pathkeys = pathkeys;
pathnode->fdw_outerpath = fdw_outerpath;
+ pathnode->fdw_restrictinfo = fdw_restrictinfo;
pathnode->fdw_private = fdw_private;
return pathnode;
@@ -2322,6 +2326,7 @@ create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
double rows, Cost startup_cost, Cost total_cost,
List *pathkeys,
Path *fdw_outerpath,
+ List *fdw_restrictinfo,
List *fdw_private)
{
ForeignPath *pathnode = makeNode(ForeignPath);
@@ -2345,6 +2350,7 @@ create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
pathnode->path.pathkeys = pathkeys;
pathnode->fdw_outerpath = fdw_outerpath;
+ pathnode->fdw_restrictinfo = fdw_restrictinfo;
pathnode->fdw_private = fdw_private;
return pathnode;
@@ -4149,6 +4155,8 @@ do { \
FLAT_COPY_PATH(fpath, path, ForeignPath);
if (fpath->fdw_outerpath)
REPARAMETERIZE_CHILD_PATH(fpath->fdw_outerpath);
+ if (fpath->fdw_restrictinfo)
+ ADJUST_CHILD_ATTRS(fpath->fdw_restrictinfo);
/* Hand over to FDW if needed. */
rfpc_func =
@@ -4166,6 +4174,8 @@ do { \
FLAT_COPY_PATH(cpath, path, CustomPath);
REPARAMETERIZE_CHILD_PATH_LIST(cpath->custom_paths);
+ if (cpath->custom_restrictinfo)
+ ADJUST_CHILD_ATTRS(cpath->custom_restrictinfo);
if (cpath->methods &&
cpath->methods->ReparameterizeCustomPathByChild)
cpath->custom_private =
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index c1fbbb6bfee..d6d26a2b515 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -550,36 +550,6 @@ extract_actual_join_clauses(List *restrictinfo_list,
}
/*
- * has_pseudoconstant_clauses
- *
- * Returns true if 'restrictinfo_list' includes pseudoconstant clauses.
- *
- * This is used when we determine whether to allow extensions to consider
- * pushing down joins in add_paths_to_joinrel().
- */
-bool
-has_pseudoconstant_clauses(PlannerInfo *root,
- List *restrictinfo_list)
-{
- ListCell *l;
-
- /* No need to look if we know there are no pseudoconstants */
- if (!root->hasPseudoConstantQuals)
- return false;
-
- /* See if there are pseudoconstants in the RestrictInfo list */
- foreach(l, restrictinfo_list)
- {
- RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
-
- if (rinfo->pseudoconstant &&
- !rinfo_is_constant_true(rinfo))
- return true;
- }
- return false;
-}
-
-/*
* join_clause_is_movable_to
* Test whether a join clause is a safe candidate for parameterization
* of a scan on the specified base relation.
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a1dc1d07e18..5702fbba60c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1822,6 +1822,10 @@ typedef struct SubqueryScanPath
* ForeignPath represents a potential scan of a foreign table, foreign join
* or foreign upper-relation.
*
+ * In the case of a foreign join, fdw_restrictinfo stores the RestrictInfos to
+ * apply to the join, which are used by createplan.c to get pseudoconstant
+ * clauses evaluated as one-time quals in a gating Result plan node.
+ *
* fdw_private stores FDW private data about the scan. While fdw_private is
* not actually touched by the core code during normal operations, it's
* generally a good idea to use a representation that can be dumped by
@@ -1832,6 +1836,7 @@ typedef struct ForeignPath
{
Path path;
Path *fdw_outerpath;
+ List *fdw_restrictinfo;
List *fdw_private;
} ForeignPath;
@@ -1849,6 +1854,10 @@ typedef struct ForeignPath
* relation by set_rel_pathlist_hook or set_join_pathlist_hook functions,
* respectively.
*
+ * In the case of a table join, custom_restrictinfo stores the RestrictInfos
+ * to apply to the join, which are used by createplan.c to get pseudoconstant
+ * clauses evaluated as one-time quals in a gating Result plan node.
+ *
* Core code must avoid assuming that the CustomPath is only as large as
* the structure declared here; providers are allowed to make it the first
* element in a larger structure. (Since the planner never copies Paths,
@@ -1865,6 +1874,7 @@ typedef struct CustomPath
uint32 flags; /* mask of CUSTOMPATH_* flags, see
* nodes/extensible.h */
List *custom_paths; /* list of child Path nodes, if any */
+ List *custom_restrictinfo;
List *custom_private;
const struct CustomPathMethods *methods;
} CustomPath;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 001e75b5b76..6e557bebc44 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -128,6 +128,7 @@ extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
List *pathkeys,
Relids required_outer,
Path *fdw_outerpath,
+ List *fdw_restrictinfo,
List *fdw_private);
extern ForeignPath *create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target,
@@ -135,12 +136,14 @@ extern ForeignPath *create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
List *pathkeys,
Relids required_outer,
Path *fdw_outerpath,
+ List *fdw_restrictinfo,
List *fdw_private);
extern ForeignPath *create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target,
double rows, Cost startup_cost, Cost total_cost,
List *pathkeys,
Path *fdw_outerpath,
+ List *fdw_restrictinfo,
List *fdw_private);
extern Relids calc_nestloop_required_outer(Relids outerrelids,
diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h
index 14cdce750cb..e140e619ace 100644
--- a/src/include/optimizer/restrictinfo.h
+++ b/src/include/optimizer/restrictinfo.h
@@ -43,8 +43,6 @@ extern void extract_actual_join_clauses(List *restrictinfo_list,
Relids joinrelids,
List **joinquals,
List **otherquals);
-extern bool has_pseudoconstant_clauses(PlannerInfo *root,
- List *restrictinfo_list);
extern bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel);
extern bool join_clause_is_movable_into(RestrictInfo *rinfo,
Relids currentrelids,