diff options
author | Robert Haas | 2025-07-16 13:24:46 +0000 |
---|---|---|
committer | Robert Haas | 2025-07-16 13:25:56 +0000 |
commit | 30827e8b95c8e75e77d35b6f7451f88b1c6bcbf6 (patch) | |
tree | 2e40efc66eb93aa47dd5ca2df097591e9e0c1ae4 | |
parent | 3a394db735a4f882d829b2b5b6efa53c54383066 (diff) |
reclassify clumped joins as scans
-rw-r--r-- | contrib/pg_plan_advice/Makefile | 1 | ||||
-rw-r--r-- | contrib/pg_plan_advice/meson.build | 1 | ||||
-rw-r--r-- | contrib/pg_plan_advice/pgpa_join.c | 79 | ||||
-rw-r--r-- | contrib/pg_plan_advice/pgpa_join.h | 44 | ||||
-rw-r--r-- | contrib/pg_plan_advice/pgpa_output.c | 1 | ||||
-rw-r--r-- | contrib/pg_plan_advice/pgpa_scan.c | 94 | ||||
-rw-r--r-- | contrib/pg_plan_advice/pgpa_walker.c | 1 |
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" |