summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Haas2025-07-16 13:24:46 +0000
committerRobert Haas2025-07-16 13:25:56 +0000
commit30827e8b95c8e75e77d35b6f7451f88b1c6bcbf6 (patch)
tree2e40efc66eb93aa47dd5ca2df097591e9e0c1ae4
parent3a394db735a4f882d829b2b5b6efa53c54383066 (diff)
reclassify clumped joins as scans
-rw-r--r--contrib/pg_plan_advice/Makefile1
-rw-r--r--contrib/pg_plan_advice/meson.build1
-rw-r--r--contrib/pg_plan_advice/pgpa_join.c79
-rw-r--r--contrib/pg_plan_advice/pgpa_join.h44
-rw-r--r--contrib/pg_plan_advice/pgpa_output.c1
-rw-r--r--contrib/pg_plan_advice/pgpa_scan.c94
-rw-r--r--contrib/pg_plan_advice/pgpa_walker.c1
7 files changed, 102 insertions, 119 deletions
diff --git a/contrib/pg_plan_advice/Makefile b/contrib/pg_plan_advice/Makefile
index afeea2e5dd..6f8127e551 100644
--- a/contrib/pg_plan_advice/Makefile
+++ b/contrib/pg_plan_advice/Makefile
@@ -8,6 +8,7 @@ OBJS = \
pgpa_identifier.o \
pgpa_join.o \
pgpa_output.o \
+ pgpa_scan.o \
pgpa_walker.o
EXTENSION = pg_plan_advice
diff --git a/contrib/pg_plan_advice/meson.build b/contrib/pg_plan_advice/meson.build
index db510082d6..d9e3ec148c 100644
--- a/contrib/pg_plan_advice/meson.build
+++ b/contrib/pg_plan_advice/meson.build
@@ -6,6 +6,7 @@ pg_plan_advice_sources = files(
'pgpa_identifier.c',
'pgpa_join.c',
'pgpa_output.c',
+ 'pgpa_scan.c',
'pgpa_walker.c',
)
diff --git a/contrib/pg_plan_advice/pgpa_join.c b/contrib/pg_plan_advice/pgpa_join.c
index 64f8dfdb8a..392eea4dd2 100644
--- a/contrib/pg_plan_advice/pgpa_join.c
+++ b/contrib/pg_plan_advice/pgpa_join.c
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * pgpa_join.h
+ * pgpa_join.c
* analysis of joins in Plan trees
*
* Copyright (c) 2016-2025, PostgreSQL Global Development Group
@@ -13,6 +13,7 @@
#include "postgres.h"
#include "pgpa_join.h"
+#include "pgpa_scan.h"
#include "pgpa_walker.h"
#include "nodes/pathnodes.h"
@@ -77,82 +78,6 @@ pgpa_get_join_class(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(PlannedStmt *pstmt, Plan *plan,
- ElidedNode *elided_node)
-{
- pgpa_clumped_join *clump = palloc(sizeof(pgpa_clumped_join));
- Bitmapset *relids = NULL;
- int rti = -1;
-
- clump->plan = plan;
-
- 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(elided_node->relids) == BMS_MULTIPLE);
- 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
- {
- Assert(pgpa_get_join_class(plan) == PGPA_CLUMPED_JOIN);
-
- 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");
- }
-
- /*
- * Filter out any RTIs of type RTE_JOIN, since we have no use for them,
- * and don't want them creating confusion later. (We always refer to
- * groups of relations in terms of the relation RTIs, not the join RTIs.)
- */
- clump->relids = NULL;
- while ((rti = bms_next_member(relids, rti)) >= 0)
- {
- RangeTblEntry *rte = rt_fetch(rti, pstmt->rtable);
-
- if (rte->rtekind != RTE_JOIN)
- clump->relids = bms_add_member(clump->relids, rti);
- }
-
- /*
- * We concluded that this was a join based on the fact that there were
- * multiple RTIs -- and even after removing the join RTIs, that should
- * still be the case.
- */
- Assert(bms_membership(clump->relids) == BMS_MULTIPLE);
-
- return clump;
-}
-
-/*
* Create an initially-empty object for unrolling joins.
*
* See comments for pgpa_unrolled_join and pgpa_clumped_join in pgpa_join.h
diff --git a/contrib/pg_plan_advice/pgpa_join.h b/contrib/pg_plan_advice/pgpa_join.h
index 3a8585c616..e7dabbae2f 100644
--- a/contrib/pg_plan_advice/pgpa_join.h
+++ b/contrib/pg_plan_advice/pgpa_join.h
@@ -15,48 +15,11 @@
#include "nodes/plannodes.h"
struct pgpa_plan_walker_context;
+struct pgpa_clumped_join;
typedef struct pgpa_join_unroller pgpa_join_unroller;
typedef struct pgpa_unrolled_join pgpa_unrolled_join;
/*
- * Certain types of plan nodes can join any number of input relations in
- * a single step; we call these "clumped joins".
- *
- * For our purposes, the important thing about a clumped join is that we
- * can't meaningfully speak about the order in which tables are joined
- * within a single clump. For example, if the optimizer chooses a
- * partitionwise join on tables A and B, we can't say whether A was joined
- * to B or whether B was joined to A; instead, each pair of child tables
- * has its own join order. Likewise, if a foreign data wrapper pushes a
- * join to the remote side, we don't know the join order.
- *
- * JSTRAT_CLUMP_DEGENERATE refers to the case where several relations are
- * all proven empty and replaced with a single Result node. Here again, while
- * the Result node may be joined to other things and we can speak about its
- * place within the larger join order, we can't speak about a join ordering
- * within the Result node itself.
- */
-typedef enum
-{
- JSTRAT_CLUMP_DEGENERATE = 0,
- JSTRAT_CLUMP_FOREIGN,
- JSTRAT_CLUMP_PARTITIONWISE
- /* update NUM_PGPA_CLUMP_JOIN_STRATEGY if you add anything here */
-} pgpa_join_clump_strategy;
-
-#define NUM_PGPA_CLUMP_JOIN_STRATEGY ((int) JSTRAT_CLUMP_PARTITIONWISE + 1)
-
-/*
- * All of the details we need regarding a clumped join.
- */
-typedef struct
-{
- Plan *plan;
- pgpa_join_clump_strategy strategy;
- Bitmapset *relids;
-} pgpa_clumped_join;
-
-/*
* Although there are three main join strategies, we try to classify things
* more precisely here: merge joins have the option of using materialization
* on the inner side, and nested loops can use either materialization or
@@ -97,7 +60,7 @@ typedef struct
Plan *plan;
ElidedNode *elided_node;
Index rti;
- pgpa_clumped_join *clump_join;
+ struct pgpa_clumped_join *clump_join;
pgpa_unrolled_join *unrolled_join;
} pgpa_join_member;
@@ -135,9 +98,6 @@ typedef enum
} pgpa_join_class;
extern pgpa_join_class pgpa_get_join_class(Plan *plan);
-extern pgpa_clumped_join *pgpa_build_clumped_join(PlannedStmt *pstmt,
- Plan *plan,
- ElidedNode *elided_node);
extern pgpa_join_unroller *pgpa_create_join_unroller(void);
extern void pgpa_unroll_join(struct pgpa_plan_walker_context *walker,
diff --git a/contrib/pg_plan_advice/pgpa_output.c b/contrib/pg_plan_advice/pgpa_output.c
index 4fe3890381..634cd75af5 100644
--- a/contrib/pg_plan_advice/pgpa_output.c
+++ b/contrib/pg_plan_advice/pgpa_output.c
@@ -13,6 +13,7 @@
#include "postgres.h"
#include "pgpa_output.h"
+#include "pgpa_scan.h"
typedef struct pgpa_output_context
{
diff --git a/contrib/pg_plan_advice/pgpa_scan.c b/contrib/pg_plan_advice/pgpa_scan.c
new file mode 100644
index 0000000000..41686dd704
--- /dev/null
+++ b/contrib/pg_plan_advice/pgpa_scan.c
@@ -0,0 +1,94 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgpa_scan.c
+ * analysis of scans in Plan trees
+ *
+ * Copyright (c) 2016-2025, PostgreSQL Global Development Group
+ *
+ * contrib/pg_plan_advice/pgpa_scan.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "pgpa_scan.h"
+#include "pgpa_walker.h"
+
+#include "nodes/parsenodes.h"
+#include "parser/parsetree.h"
+
+/*
+ * 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(PlannedStmt *pstmt, Plan *plan,
+ ElidedNode *elided_node)
+{
+ pgpa_clumped_join *clump = palloc(sizeof(pgpa_clumped_join));
+ Bitmapset *relids = NULL;
+ int rti = -1;
+
+ clump->plan = plan;
+
+ 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(elided_node->relids) == BMS_MULTIPLE);
+ 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
+ {
+ Assert(pgpa_get_join_class(plan) == PGPA_CLUMPED_JOIN);
+
+ 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");
+ }
+
+ /*
+ * Filter out any RTIs of type RTE_JOIN, since we have no use for them,
+ * and don't want them creating confusion later. (We always refer to
+ * groups of relations in terms of the relation RTIs, not the join RTIs.)
+ */
+ clump->relids = NULL;
+ while ((rti = bms_next_member(relids, rti)) >= 0)
+ {
+ RangeTblEntry *rte = rt_fetch(rti, pstmt->rtable);
+
+ if (rte->rtekind != RTE_JOIN)
+ clump->relids = bms_add_member(clump->relids, rti);
+ }
+
+ /*
+ * We concluded that this was a join based on the fact that there were
+ * multiple RTIs -- and even after removing the join RTIs, that should
+ * still be the case.
+ */
+ Assert(bms_membership(clump->relids) == BMS_MULTIPLE);
+
+ return clump;
+}
diff --git a/contrib/pg_plan_advice/pgpa_walker.c b/contrib/pg_plan_advice/pgpa_walker.c
index 8973180379..3d20d6cc05 100644
--- a/contrib/pg_plan_advice/pgpa_walker.c
+++ b/contrib/pg_plan_advice/pgpa_walker.c
@@ -1,6 +1,7 @@
#include "postgres.h"
#include "pgpa_join.h"
+#include "pgpa_scan.h"
#include "pgpa_walker.h"
#include "nodes/plannodes.h"