summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/pathnode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/pathnode.c')
-rw-r--r--src/backend/optimizer/util/pathnode.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 26567cb7f65..2d491eb0ba9 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -18,15 +18,20 @@
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+#include "nodes/extensible.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
+#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
+#include "foreign/fdwapi.h"
#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#include "utils/selfuncs.h"
@@ -46,6 +51,9 @@ typedef enum
#define STD_FUZZ_FACTOR 1.01
static List *translate_sub_tlist(List *tlist, int relid);
+static List *reparameterize_pathlist_by_child(PlannerInfo *root,
+ List *pathlist,
+ RelOptInfo *child_rel);
/*****************************************************************************
@@ -3429,3 +3437,358 @@ reparameterize_path(PlannerInfo *root, Path *path,
}
return NULL;
}
+
+/*
+ * reparameterize_path_by_child
+ * Given a path parameterized by the parent of the given child relation,
+ * translate the path to be parameterized by the given child relation.
+ *
+ * The function creates a new path of the same type as the given path, but
+ * parameterized by the given child relation. Most fields from the original
+ * path can simply be flat-copied, but any expressions must be adjusted to
+ * refer to the correct varnos, and any paths must be recursively
+ * reparameterized. Other fields that refer to specific relids also need
+ * adjustment.
+ *
+ * The cost, number of rows, width and parallel path properties depend upon
+ * path->parent, which does not change during the translation. Hence those
+ * members are copied as they are.
+ *
+ * If the given path can not be reparameterized, the function returns NULL.
+ */
+Path *
+reparameterize_path_by_child(PlannerInfo *root, Path *path,
+ RelOptInfo *child_rel)
+{
+
+#define FLAT_COPY_PATH(newnode, node, nodetype) \
+ ( (newnode) = makeNode(nodetype), \
+ memcpy((newnode), (node), sizeof(nodetype)) )
+
+#define ADJUST_CHILD_ATTRS(node) \
+ ((node) = \
+ (List *) adjust_appendrel_attrs_multilevel(root, (Node *) (node), \
+ child_rel->relids, \
+ child_rel->top_parent_relids))
+
+#define REPARAMETERIZE_CHILD_PATH(path) \
+do { \
+ (path) = reparameterize_path_by_child(root, (path), child_rel); \
+ if ((path) == NULL) \
+ return NULL; \
+} while(0);
+
+#define REPARAMETERIZE_CHILD_PATH_LIST(pathlist) \
+do { \
+ if ((pathlist) != NIL) \
+ { \
+ (pathlist) = reparameterize_pathlist_by_child(root, (pathlist), \
+ child_rel); \
+ if ((pathlist) == NIL) \
+ return NULL; \
+ } \
+} while(0);
+
+ Path *new_path;
+ ParamPathInfo *new_ppi;
+ ParamPathInfo *old_ppi;
+ Relids required_outer;
+
+ /*
+ * If the path is not parameterized by parent of the given relation, it
+ * doesn't need reparameterization.
+ */
+ if (!path->param_info ||
+ !bms_overlap(PATH_REQ_OUTER(path), child_rel->top_parent_relids))
+ return path;
+
+ /* Reparameterize a copy of given path. */
+ switch (nodeTag(path))
+ {
+ case T_Path:
+ FLAT_COPY_PATH(new_path, path, Path);
+ break;
+
+ case T_IndexPath:
+ {
+ IndexPath *ipath;
+
+ FLAT_COPY_PATH(ipath, path, IndexPath);
+ ADJUST_CHILD_ATTRS(ipath->indexclauses);
+ ADJUST_CHILD_ATTRS(ipath->indexquals);
+ new_path = (Path *) ipath;
+ }
+ break;
+
+ case T_BitmapHeapPath:
+ {
+ BitmapHeapPath *bhpath;
+
+ FLAT_COPY_PATH(bhpath, path, BitmapHeapPath);
+ REPARAMETERIZE_CHILD_PATH(bhpath->bitmapqual);
+ new_path = (Path *) bhpath;
+ }
+ break;
+
+ case T_BitmapAndPath:
+ {
+ BitmapAndPath *bapath;
+
+ FLAT_COPY_PATH(bapath, path, BitmapAndPath);
+ REPARAMETERIZE_CHILD_PATH_LIST(bapath->bitmapquals);
+ new_path = (Path *) bapath;
+ }
+ break;
+
+ case T_BitmapOrPath:
+ {
+ BitmapOrPath *bopath;
+
+ FLAT_COPY_PATH(bopath, path, BitmapOrPath);
+ REPARAMETERIZE_CHILD_PATH_LIST(bopath->bitmapquals);
+ new_path = (Path *) bopath;
+ }
+ break;
+
+ case T_TidPath:
+ {
+ TidPath *tpath;
+
+ /*
+ * TidPath contains tidquals, which do not contain any
+ * external parameters per create_tidscan_path(). So don't
+ * bother to translate those.
+ */
+ FLAT_COPY_PATH(tpath, path, TidPath);
+ new_path = (Path *) tpath;
+ }
+ break;
+
+ case T_ForeignPath:
+ {
+ ForeignPath *fpath;
+ ReparameterizeForeignPathByChild_function rfpc_func;
+
+ FLAT_COPY_PATH(fpath, path, ForeignPath);
+ if (fpath->fdw_outerpath)
+ REPARAMETERIZE_CHILD_PATH(fpath->fdw_outerpath);
+
+ /* Hand over to FDW if needed. */
+ rfpc_func =
+ path->parent->fdwroutine->ReparameterizeForeignPathByChild;
+ if (rfpc_func)
+ fpath->fdw_private = rfpc_func(root, fpath->fdw_private,
+ child_rel);
+ new_path = (Path *) fpath;
+ }
+ break;
+
+ case T_CustomPath:
+ {
+ CustomPath *cpath;
+
+ FLAT_COPY_PATH(cpath, path, CustomPath);
+ REPARAMETERIZE_CHILD_PATH_LIST(cpath->custom_paths);
+ if (cpath->methods &&
+ cpath->methods->ReparameterizeCustomPathByChild)
+ cpath->custom_private =
+ cpath->methods->ReparameterizeCustomPathByChild(root,
+ cpath->custom_private,
+ child_rel);
+ new_path = (Path *) cpath;
+ }
+ break;
+
+ case T_NestPath:
+ {
+ JoinPath *jpath;
+
+ FLAT_COPY_PATH(jpath, path, NestPath);
+
+ REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
+ REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
+ ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
+ new_path = (Path *) jpath;
+ }
+ break;
+
+ case T_MergePath:
+ {
+ JoinPath *jpath;
+ MergePath *mpath;
+
+ FLAT_COPY_PATH(mpath, path, MergePath);
+
+ jpath = (JoinPath *) mpath;
+ REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
+ REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
+ ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
+ ADJUST_CHILD_ATTRS(mpath->path_mergeclauses);
+ new_path = (Path *) mpath;
+ }
+ break;
+
+ case T_HashPath:
+ {
+ JoinPath *jpath;
+ HashPath *hpath;
+
+ FLAT_COPY_PATH(hpath, path, HashPath);
+
+ jpath = (JoinPath *) hpath;
+ REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
+ REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
+ ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
+ ADJUST_CHILD_ATTRS(hpath->path_hashclauses);
+ new_path = (Path *) hpath;
+ }
+ break;
+
+ case T_AppendPath:
+ {
+ AppendPath *apath;
+
+ FLAT_COPY_PATH(apath, path, AppendPath);
+ REPARAMETERIZE_CHILD_PATH_LIST(apath->subpaths);
+ new_path = (Path *) apath;
+ }
+ break;
+
+ case T_MergeAppend:
+ {
+ MergeAppendPath *mapath;
+
+ FLAT_COPY_PATH(mapath, path, MergeAppendPath);
+ REPARAMETERIZE_CHILD_PATH_LIST(mapath->subpaths);
+ new_path = (Path *) mapath;
+ }
+ break;
+
+ case T_MaterialPath:
+ {
+ MaterialPath *mpath;
+
+ FLAT_COPY_PATH(mpath, path, MaterialPath);
+ REPARAMETERIZE_CHILD_PATH(mpath->subpath);
+ new_path = (Path *) mpath;
+ }
+ break;
+
+ case T_UniquePath:
+ {
+ UniquePath *upath;
+
+ FLAT_COPY_PATH(upath, path, UniquePath);
+ REPARAMETERIZE_CHILD_PATH(upath->subpath);
+ ADJUST_CHILD_ATTRS(upath->uniq_exprs);
+ new_path = (Path *) upath;
+ }
+ break;
+
+ case T_GatherPath:
+ {
+ GatherPath *gpath;
+
+ FLAT_COPY_PATH(gpath, path, GatherPath);
+ REPARAMETERIZE_CHILD_PATH(gpath->subpath);
+ new_path = (Path *) gpath;
+ }
+ break;
+
+ case T_GatherMergePath:
+ {
+ GatherMergePath *gmpath;
+
+ FLAT_COPY_PATH(gmpath, path, GatherMergePath);
+ REPARAMETERIZE_CHILD_PATH(gmpath->subpath);
+ new_path = (Path *) gmpath;
+ }
+ break;
+
+ default:
+
+ /* We don't know how to reparameterize this path. */
+ return NULL;
+ }
+
+ /*
+ * Adjust the parameterization information, which refers to the topmost
+ * parent. The topmost parent can be multiple levels away from the given
+ * child, hence use multi-level expression adjustment routines.
+ */
+ old_ppi = new_path->param_info;
+ required_outer =
+ adjust_child_relids_multilevel(root, old_ppi->ppi_req_outer,
+ child_rel->relids,
+ child_rel->top_parent_relids);
+
+ /* If we already have a PPI for this parameterization, just return it */
+ new_ppi = find_param_path_info(new_path->parent, required_outer);
+
+ /*
+ * If not, build a new one and link it to the list of PPIs. For the same
+ * reason as explained in mark_dummy_rel(), allocate new PPI in the same
+ * context the given RelOptInfo is in.
+ */
+ if (new_ppi == NULL)
+ {
+ MemoryContext oldcontext;
+ RelOptInfo *rel = path->parent;
+
+ oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(rel));
+
+ new_ppi = makeNode(ParamPathInfo);
+ new_ppi->ppi_req_outer = bms_copy(required_outer);
+ new_ppi->ppi_rows = old_ppi->ppi_rows;
+ new_ppi->ppi_clauses = old_ppi->ppi_clauses;
+ ADJUST_CHILD_ATTRS(new_ppi->ppi_clauses);
+ rel->ppilist = lappend(rel->ppilist, new_ppi);
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+ bms_free(required_outer);
+
+ new_path->param_info = new_ppi;
+
+ /*
+ * Adjust the path target if the parent of the outer relation is
+ * referenced in the targetlist. This can happen when only the parent of
+ * outer relation is laterally referenced in this relation.
+ */
+ if (bms_overlap(path->parent->lateral_relids,
+ child_rel->top_parent_relids))
+ {
+ new_path->pathtarget = copy_pathtarget(new_path->pathtarget);
+ ADJUST_CHILD_ATTRS(new_path->pathtarget->exprs);
+ }
+
+ return new_path;
+}
+
+/*
+ * reparameterize_pathlist_by_child
+ * Helper function to reparameterize a list of paths by given child rel.
+ */
+static List *
+reparameterize_pathlist_by_child(PlannerInfo *root,
+ List *pathlist,
+ RelOptInfo *child_rel)
+{
+ ListCell *lc;
+ List *result = NIL;
+
+ foreach(lc, pathlist)
+ {
+ Path *path = reparameterize_path_by_child(root, lfirst(lc),
+ child_rel);
+ if (path == NULL)
+ {
+ list_free(result);
+ return NIL;
+ }
+
+ result = lappend(result, path);
+ }
+
+ return result;
+}