#include "postgres.h"
#include "pgpa_join.h"
+#include "pgpa_walker.h"
/*
* Temporary object used when unrolling a join tree.
unsigned nallocated;
unsigned nused;
Plan *outer_subplan;
+ ElidedNode *outer_elided_node;
pgpa_join_strategy *strategy;
Plan **inner_subplans;
- pgpa_join_unroller **inner_unroller;
+ ElidedNode **inner_elided_nodes;
+ pgpa_join_unroller **inner_unrollers;
};
-static pgpa_join_strategy pgpa_decompose_join(Plan *plan,
+static pgpa_join_strategy pgpa_decompose_join(PlannedStmt *pstmt,
+ Plan *plan,
+ Plan **realouter,
Plan **realinner,
- Plan **realouter);
+ ElidedNode **elidedrealouter,
+ ElidedNode **elidedrealinner);
+static void pgpa_fix_scan_or_clump_member(pgpa_join_member *member);
static Index pgpa_scanrelid(Plan *plan);
static Bitmapset *pgpa_relids(Plan *plan);
/*
* Build a pgpa_clumped_join object for a Plan node.
+ *
+ * If there is at least one ElidedNode for this plan node, pass the uppermost
+ * one as elided_node, else pass NULL.
*/
pgpa_clumped_join *
-pgpa_build_clumped_join(Plan *plan)
+pgpa_build_clumped_join(Plan *plan, ElidedNode *elided_node)
{
pgpa_clumped_join *clump = palloc(sizeof(pgpa_clumped_join));
Assert(pgpa_get_join_class(plan) == PGPA_CLUMPED_JOIN);
clump->plan = plan;
- clump->relids = pgpa_relids(plan);
- if (IsA(plan, Result))
- clump->strategy = JSTRAT_CLUMP_DEGENERATE;
- else if (IsA(plan, ForeignScan))
- clump->strategy = JSTRAT_CLUMP_FOREIGN;
- else if (IsA(plan, Append))
- clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
- else if (IsA(plan, MergeAppend))
- clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
+ if (elided_node != NULL)
+ {
+ NodeTag elided_type = elided_node->elided_type;
+
+ /*
+ * The only case we expect to encounter here is a partitionwise join
+ * whose Append or MergeAppend node was elided due to having only one
+ * surviving child. It's also possiblee for a trivial SubqueryScan to
+ * be elided, but in that case we expected only a single RTI, in which
+ * case it's not a join.
+ */
+ Assert(bms_membership(clump->relids) == BMS_MULTIPLE);
+ clump->relids = elided_node->relids;
+ if (elided_type == T_Append || elided_type == T_MergeAppend)
+ clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
+ else
+ elog(ERROR, "unexpected elided node type");
+ }
else
- elog(ERROR, "unexpected plan type");
+ {
+ clump->relids = pgpa_relids(plan);
+
+ if (IsA(plan, Result))
+ clump->strategy = JSTRAT_CLUMP_DEGENERATE;
+ else if (IsA(plan, ForeignScan))
+ clump->strategy = JSTRAT_CLUMP_FOREIGN;
+ else if (IsA(plan, Append))
+ clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
+ else if (IsA(plan, MergeAppend))
+ clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
+ else
+ elog(ERROR, "unexpected plan type");
+ }
return clump;
}
palloc_array(pgpa_join_strategy, join_unroller->nallocated);
join_unroller->inner_subplans =
palloc_array(Plan *, join_unroller->nallocated);
- join_unroller->inner_unroller =
+ join_unroller->inner_elided_nodes =
+ palloc_array(ElidedNode *, join_unroller->nallocated);
+ join_unroller->inner_unrollers =
palloc_array(pgpa_join_unroller *, join_unroller->nallocated);
return join_unroller;
pgpa_join_strategy strategy;
Plan *realinner,
*realouter;
+ ElidedNode *elidedinner,
+ *elidedouter;
int n;
Assert(join_unroller != NULL);
* this should be an unrollable join.
*/
Assert(pgpa_get_join_class(plan) == PGPA_UNROLLED_JOIN);
- strategy = pgpa_decompose_join(plan, &realinner, &realouter);
+ strategy = pgpa_decompose_join(pstmt, plan, &realouter, &realinner,
+ &elidedouter, &elidedinner);
/* If our workspace is full, expand it. */
if (join_unroller->nused >= join_unroller->nallocated)
repalloc_array(join_unroller->inner_subplans,
Plan *,
join_unroller->nallocated);
- join_unroller->inner_unroller =
- repalloc_array(join_unroller->inner_unroller,
+ join_unroller->inner_elided_nodes =
+ repalloc_array(join_unroller->inner_elided_nodes,
+ ElidedNode *,
+ join_unroller->nallocated);
+ join_unroller->inner_unrollers =
+ repalloc_array(join_unroller->inner_unrollers,
pgpa_join_unroller *,
join_unroller->nallocated);
}
* into this object and must remember the outer side as the final outer
* subplan.
*/
- if (pgpa_get_join_class(realouter) == PGPA_UNROLLED_JOIN)
+ if (elidedouter == NULL &&
+ pgpa_get_join_class(realouter) == PGPA_UNROLLED_JOIN)
*outer_join_unroller = join_unroller;
else
+ {
join_unroller->outer_subplan = realouter;
+ join_unroller->outer_elided_node = elidedouter;
+ }
/*
* Store the inner subplan. If it's an unrollable join, it needs to be
n = join_unroller->nused++;
join_unroller->strategy[n] = strategy;
join_unroller->inner_subplans[n] = realinner;
- if (pgpa_get_join_class(realinner) == PGPA_UNROLLED_JOIN)
+ join_unroller->inner_elided_nodes[n] = elidedinner;
+ if (elidedinner == NULL &&
+ pgpa_get_join_class(realinner) == PGPA_UNROLLED_JOIN)
*inner_join_unroller = pgpa_create_join_unroller();
else
*inner_join_unroller = NULL;
- join_unroller->inner_unroller[n] = *inner_join_unroller;
+ join_unroller->inner_unrollers[n] = *inner_join_unroller;
}
/*
/* Handle the outermost join. */
ujoin->outer.plan = join_unroller->outer_subplan;
- ujoin->outer.rti = pgpa_scanrelid(ujoin->outer.plan);
- if (pgpa_get_join_class(ujoin->outer.plan) == PGPA_CLUMPED_JOIN)
- ujoin->outer.clump_join = pgpa_build_clumped_join(ujoin->outer.plan);
+ ujoin->outer.elided_node = join_unroller->outer_elided_node;
+ pgpa_fix_scan_or_clump_member(&ujoin->outer);
/*
* We want the joins from the deepest part of the plan tree to appear
*/
for (i = 0; i < join_unroller->nused; ++i)
{
- int k = join_unroller->nused - i - 1;
+ int k = join_unroller->nused - i - 1;
- /* Copy strategy and Plan pointer. */
+ /* Copy strategy, Plan, and ElidedNode. */
ujoin->strategy[i] = join_unroller->strategy[k];
ujoin->inner[i].plan = join_unroller->inner_subplans[k];
+ ujoin->inner[i].elided_node = join_unroller->inner_elided_nodes[k];
/*
- * If there is a nested pgpa_join_unroller, we need to convert it
- * to a pgpa_unrolled_join and store the result.
+ * Fill in remaining details, using either the nested join unroller,
+ * or by deriving them from the plan and elided nodes.
*/
- if (join_unroller->inner_unroller[i] != NULL)
+ if (join_unroller->inner_unrollers[i] != NULL)
ujoin->inner[i].unrolled_join =
pgpa_build_unrolled_join(pstmt,
- join_unroller->inner_unroller[k]);
+ join_unroller->inner_unrollers[k]);
else
- ujoin->inner[i].rti = pgpa_scanrelid(ujoin->inner[i].plan);
-
- /*
- * If we need a clumped join object, build one.
- */
- if (pgpa_get_join_class(ujoin->inner[i].plan) == PGPA_CLUMPED_JOIN)
- ujoin->inner[i].clump_join =
- pgpa_build_clumped_join(ujoin->inner[i].plan);
-
- /* XXX. What about elided nodes? */
+ pgpa_fix_scan_or_clump_member(&ujoin->inner[i]);
}
return ujoin;
{
pfree(join_unroller->strategy);
pfree(join_unroller->inner_subplans);
- pfree(join_unroller->inner_unroller);
+ pfree(join_unroller->inner_elided_nodes);
+ pfree(join_unroller->inner_unrollers);
pfree(join_unroller);
}
* may add additional nodes such as Materialize or Memoize. We regard these
* as an aspect of the join strategy. As in the previous cases, the true input
* to the join is the underlying node.
+ *
+ * However, if any involved child node previously had a now-elided node stacked
+ * on top, then we can't "look through" that node -- indeed, what's going to be
+ * relevant for our purposes is the ElidedNode on top of that plan node, rather
+ * than the plan node itself.
*/
static pgpa_join_strategy
-pgpa_decompose_join(Plan *plan, Plan **realinner, Plan **realouter)
+pgpa_decompose_join(PlannedStmt *pstmt, Plan *plan,
+ Plan **realouter, Plan **realinner,
+ ElidedNode **elidedrealouter, ElidedNode **elidedrealinner)
{
Plan *outerplan = plan->lefttree;
Plan *innerplan = plan->righttree;
+ ElidedNode *elidedouter;
+ ElidedNode *elidedinner;
pgpa_join_strategy strategy;
+ elidedouter = pgpa_first_elided_node(pstmt, outerplan);
+ elidedinner = pgpa_first_elided_node(pstmt, innerplan);
+
switch (nodeTag(plan))
{
case T_MergeJoin:
- if (IsA(innerplan, Sort))
- innerplan = innerplan->lefttree;
- if (IsA(outerplan, Sort))
+ if (elidedouter == NULL && IsA(outerplan, Sort))
+ {
outerplan = outerplan->lefttree;
- if (IsA(innerplan, Material))
+ elidedouter = pgpa_first_elided_node(pstmt, outerplan);
+ }
+
+ if (elidedinner == NULL && IsA(innerplan, Sort))
{
innerplan = innerplan->lefttree;
+ elidedinner = pgpa_first_elided_node(pstmt, innerplan);
+ }
+
+ if (elidedinner == NULL && IsA(innerplan, Material))
+ {
+ innerplan = innerplan->lefttree;
+ elidedinner = pgpa_first_elided_node(pstmt, innerplan);
strategy = JSTRAT_MERGE_JOIN_MATERIALIZE;
}
else
strategy = JSTRAT_MERGE_JOIN_PLAIN;
break;
+
case T_NestLoop:
- if (IsA(innerplan, Material))
+ if (elidedinner == NULL && IsA(innerplan, Material))
{
innerplan = innerplan->lefttree;
+ elidedinner = pgpa_first_elided_node(pstmt, innerplan);
strategy = JSTRAT_NESTED_LOOP_MATERIALIZE;
}
- else if (IsA(innerplan, Memoize))
+ else if (elidedinner == NULL && IsA(innerplan, Memoize))
{
innerplan = innerplan->lefttree;
+ elidedinner = pgpa_first_elided_node(pstmt, innerplan);
strategy = JSTRAT_NESTED_LOOP_MEMOIZE;
}
else
strategy = JSTRAT_NESTED_LOOP_PLAIN;
break;
+
case T_HashJoin:
Assert(IsA(innerplan, Hash));
+ Assert(elidedinner == NULL);
innerplan = innerplan->lefttree;
+ elidedinner = pgpa_first_elided_node(pstmt, innerplan);
strategy = JSTRAT_HASH_JOIN;
break;
+
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(plan));
}
- *realinner = innerplan;
*realouter = outerplan;
+ *realinner = innerplan;
+ *elidedrealouter = elidedouter;
+ *elidedrealinner = elidedinner;
return strategy;
}
+/*
+ * Given a pgpa_join_member where the plan node and elided node, if any, have
+ * already been set, and where the plan node or elided node represents a
+ * clumped join or a scan, fill in the clump_join or rti field of the
+ * pgpa_join_member.
+ */
+static void
+pgpa_fix_scan_or_clump_member(pgpa_join_member *member)
+{
+ if (member->elided_node != NULL)
+ {
+ /*
+ * There is an elided node, so this is either a clumped join if the
+ * elided node mentions multiple relids or a scan if it only mentions
+ * one.
+ */
+ if (bms_membership(member->elided_node->relids) == BMS_MULTIPLE)
+ member->clump_join =
+ pgpa_build_clumped_join(member->plan,
+ member->elided_node);
+ else
+ member->rti = bms_singleton_member(member->elided_node->relids);
+ }
+ else
+ {
+ /*
+ * There is no elided node, so we determine whether this is a clumped
+ * join or a scan by looking at the plan node itself.
+ */
+ if (pgpa_get_join_class(member->plan) == PGPA_CLUMPED_JOIN)
+ member->clump_join =
+ pgpa_build_clumped_join(member->plan,
+ member->elided_node);
+ else
+ member->rti = pgpa_scanrelid(member->plan);
+ }
+}
+
/*
* Extract the scanned RTI from a plan node.
*
return ((Scan *) plan)->scanrelid;
default:
{
- Bitmapset *relids = pgpa_relids(plan);
+ Bitmapset *relids = pgpa_relids(plan);
/*
- * If the node type is capable of carrying multiple relids
- * but the relids set actually present has exactly one member,
- * we regard it as a scan.
+ * If the node type is capable of carrying multiple relids but
+ * the relids set actually present has exactly one member, we
+ * regard it as a scan.
*/
if (bms_membership(relids) == BMS_SINGLETON)
return bms_singleton_member(relids);
void
pgpa_debug_out_clumped_join(StringInfo buf, pgpa_clumped_join *clump)
{
- char *cstrategy;
- int rti = -1;
- bool first = true;
+ char *cstrategy;
+ int rti = -1;
+ bool first = true;
cstrategy = pgpa_cstring_join_clump_strategy(clump->strategy);
appendStringInfo(buf, "%s(", cstrategy);
for (int k = 0; k < join->ninner; ++k)
{
- char *cstrategy;
+ char *cstrategy;
cstrategy = pgpa_cstring_join_strategy(join->strategy[k]);
appendStringInfo(buf, " %s ", cstrategy);