pgpa_join_member *member);
static char *pgpa_cstring_join_clump_strategy(pgpa_join_clump_strategy strategy);
static char *pgpa_cstring_join_strategy(pgpa_join_strategy strategy);
+static bool is_result_node_with_child(Plan *plan);
+static bool is_sorting_plan(Plan *plan);
/*
* Should a given plan node be represented by a pgpa_unrolled_join, a
/*
* We need to pass the join_unroller object down through certain types of
* plan nodes -- anything that's considered part of the join strategy,
- * such as Hash Join's Hash node or a Materialize node inserted on the
- * inner side of a nested loop as part of the join strategy -- and also
- * Gather and Gather Merge nodes, which can occur in a join tree but are
- * not themselves scans or joins.
+ * and any other nodes that can occur in a join tree despite not being
+ * scans or joins.
+ *
+ * This includes:
+ *
+ * (1) Materialize, Memoize, and Hash nodes, which are part of the join
+ * strategy,
+ *
+ * (2) Gather and Gather Merge nodes, which can occur at any point in
+ * the join tree where the planner decided to initiate parallelism,
+ *
+ * (3) Sort and IncrementalSort nodes, which can occur beneath MergeJoin
+ * or GatherMerge,
+ *
+ * (4) Agg and Unique nodes, which can occur when we decide to make
+ * the nullable side of a semijoin unique and then join the result, and
+ *
+ * (5) Result nodes with children, which can be added either to project
+ * to enforce a one-time filter (but Result nodes without children are
+ * degenerate scans or joins).
*/
- if (IsA(plan, Material) || IsA(plan, Memoize) || IsA(plan, Hash) ||
- IsA(plan, Sort) || IsA(plan, Gather) || IsA(plan, GatherMerge))
+ if (IsA(plan, Material) || IsA(plan, Memoize) || IsA(plan, Hash)
+ || IsA(plan, Gather) || IsA(plan, GatherMerge)
+ || is_sorting_plan(plan) || IsA(plan, Agg) || IsA(plan, Unique)
+ || is_result_node_with_child(plan))
{
*outer_join_unroller = join_unroller;
return;
pfree(join_unroller);
}
+static ElidedNode *
+pgpa_descend_node(PlannedStmt *pstmt, Plan **plan)
+{
+ *plan = (*plan)->lefttree;
+ return pgpa_last_elided_node(pstmt, *plan);
+}
+
+static ElidedNode *
+pgpa_descend_any_gather(PlannedStmt *pstmt, Plan **plan)
+{
+ if (IsA(*plan, Gather))
+ return pgpa_descend_node(pstmt, plan);
+
+ if (IsA(*plan, GatherMerge))
+ {
+ ElidedNode *elided = pgpa_descend_node(pstmt, plan);
+
+ if (elided == NULL && is_sorting_plan(*plan))
+ elided = pgpa_descend_node(pstmt, plan);
+
+ return elided;
+ }
+
+ return NULL;
+}
+
/*
* Identify the join strategy used by a join and the "real" inner and outer
* plans.
switch (nodeTag(plan))
{
case T_MergeJoin:
- if (elidedouter == NULL && IsA(outerplan, Sort))
+ if (elidedouter == NULL && is_sorting_plan(outerplan))
{
outerplan = outerplan->lefttree;
elidedouter = pgpa_last_elided_node(pstmt, outerplan);
}
- if (elidedouter == NULL && IsA(outerplan, Result))
+ if (elidedouter == NULL && is_result_node_with_child(outerplan))
{
outerplan = outerplan->lefttree;
elidedouter = pgpa_last_elided_node(pstmt, outerplan);
else
strategy = JSTRAT_MERGE_JOIN_PLAIN;
- if (elidedinner == NULL && IsA(innerplan, Sort))
+ if (elidedinner == NULL && is_sorting_plan(innerplan))
{
innerplan = innerplan->lefttree;
elidedinner = pgpa_last_elided_node(pstmt, innerplan);
}
- if (elidedinner == NULL && IsA(innerplan, Result))
+ if (elidedinner == NULL && is_result_node_with_child(innerplan))
{
innerplan = innerplan->lefttree;
elidedinner = pgpa_last_elided_node(pstmt, innerplan);
}
if (elidedouter == NULL &&
- (IsA(outerplan, Gather) || IsA(outerplan, GatherMerge)))
+ (IsA(outerplan, Agg) || IsA(outerplan, Unique)))
{
outerplan = outerplan->lefttree;
elidedouter = pgpa_last_elided_node(pstmt, outerplan);
+
+ if (elidedouter == NULL && is_sorting_plan(outerplan))
+ {
+ outerplan = outerplan->lefttree;
+ elidedouter = pgpa_last_elided_node(pstmt, outerplan);
+ }
+
+ if (elidedouter == NULL && is_result_node_with_child(outerplan))
+ {
+ outerplan = outerplan->lefttree;
+ elidedouter = pgpa_last_elided_node(pstmt, outerplan);
+ }
}
if (elidedinner == NULL &&
- (IsA(innerplan, Gather) || IsA(innerplan, GatherMerge)))
+ (IsA(innerplan, Agg) || IsA(innerplan, Unique)))
+ {
+ innerplan = innerplan->lefttree;
+ elidedinner = pgpa_last_elided_node(pstmt, innerplan);
+
+ if (elidedinner == NULL && is_sorting_plan(innerplan))
+ {
+ outerplan = outerplan->lefttree;
+ elidedouter = pgpa_last_elided_node(pstmt, outerplan);
+ }
+
+ if (elidedinner == NULL && is_result_node_with_child(innerplan))
+ {
+ innerplan = innerplan->lefttree;
+ elidedinner = pgpa_last_elided_node(pstmt, innerplan);
+ }
+ }
+
+ if (elidedouter == NULL)
+ elidedouter = pgpa_descend_any_gather(pstmt, &outerplan);
+
+ if (elidedinner == NULL)
+ elidedinner = pgpa_descend_any_gather(pstmt, &innerplan);
+
+ if (elidedouter == NULL && is_result_node_with_child(outerplan))
+ {
+ outerplan = outerplan->lefttree;
+ elidedouter = pgpa_last_elided_node(pstmt, outerplan);
+ }
+
+ if (elidedinner == NULL && is_result_node_with_child(innerplan))
{
innerplan = innerplan->lefttree;
elidedinner = pgpa_last_elided_node(pstmt, innerplan);
Assert(false);
}
+
+static bool
+is_result_node_with_child(Plan *plan)
+{
+ return IsA(plan, Result) && plan->lefttree != NULL;
+}
+
+static bool
+is_sorting_plan(Plan *plan)
+{
+ return IsA(plan, Sort) || IsA(plan, IncrementalSort);
+}