pg_plan_advice.o \
pgpa_identifier.o \
pgpa_join.o \
+ pgpa_output.o \
pgpa_walker.o
EXTENSION = pg_plan_advice
'pg_plan_advice.c',
'pgpa_identifier.c',
'pgpa_join.c',
+ 'pgpa_output.c',
'pgpa_walker.c',
)
#include "funcapi.h"
#include "pgpa_identifier.h"
+#include "pgpa_output.h"
#include "pgpa_walker.h"
PG_MODULE_MAGIC;
static Index pgpa_scanrelid(Plan *plan);
static Bitmapset *pgpa_relids(Plan *plan);
-static void pgpa_debug_out_join_member(StringInfo buf,
- pgpa_join_member *member,
- const char **rt_identifiers);
-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);
return NULL;
}
-void
-pgpa_debug_out_clumped_join(StringInfo buf, pgpa_clumped_join *clump,
- const char **rt_identifiers)
-{
- char *cstrategy;
- int rti = -1;
- bool first = true;
-
- cstrategy = pgpa_cstring_join_clump_strategy(clump->strategy);
- appendStringInfo(buf, "%s(", cstrategy);
- while ((rti = bms_next_member(clump->relids, rti)) >= 0)
- {
- const char *identifier = rt_identifiers[rti - 1];
-
- if (identifier == NULL)
- elog(ERROR, "no identifier for RTI %d", rti);
-
- if (first)
- {
- first = false;
- appendStringInfoString(buf, identifier);
- }
- else
- appendStringInfo(buf, " %s", identifier);
- }
- appendStringInfoChar(buf, ')');
-}
-
-void
-pgpa_debug_out_unrolled_join(StringInfo buf, pgpa_unrolled_join *join,
- const char **rt_identifiers)
-{
- appendStringInfoChar(buf, '(');
-
- pgpa_debug_out_join_member(buf, &join->outer, rt_identifiers);
-
- for (int k = 0; k < join->ninner; ++k)
- {
- char *cstrategy;
-
- cstrategy = pgpa_cstring_join_strategy(join->strategy[k]);
- appendStringInfo(buf, " %s ", cstrategy);
- pgpa_debug_out_join_member(buf, &join->inner[k], rt_identifiers);
- }
-
- appendStringInfoChar(buf, ')');
-}
-
-void
-pgpa_debug_out_gathered_join(StringInfo buf, pgpa_gathered_join *gathered_join,
- const char **rt_identifiers)
-{
- int rti = -1;
- bool first = true;
-
- if (gathered_join->is_merge)
- appendStringInfo(buf, "GATHER_MERGE(");
- else
- appendStringInfo(buf, "GATHER(");
-
- while ((rti = bms_next_member(gathered_join->relids, rti)) >= 0)
- {
- const char *identifier = rt_identifiers[rti - 1];
-
- if (identifier == NULL)
- elog(ERROR, "no identifier for RTI %d", rti);
-
- if (first)
- {
- first = false;
- appendStringInfoString(buf, identifier);
- }
- else
- appendStringInfo(buf, " %s", identifier);
- }
- appendStringInfoChar(buf, ')');
-}
-
-static void
-pgpa_debug_out_join_member(StringInfo buf, pgpa_join_member *member,
- const char **rt_identifiers)
-{
- if (member->clump_join != NULL)
- pgpa_debug_out_clumped_join(buf, member->clump_join,
- rt_identifiers);
- else if (member->unrolled_join != NULL)
- pgpa_debug_out_unrolled_join(buf, member->unrolled_join,
- rt_identifiers);
- else
- {
- if (rt_identifiers[member->rti - 1] == NULL)
- elog(ERROR, "no identifier for RTI %d", member->rti);
- appendStringInfoString(buf, rt_identifiers[member->rti - 1]);
- }
-}
-
-/*
- * Get a C string that corresponds to the specified join clump strategy.
- */
-static char *
-pgpa_cstring_join_clump_strategy(pgpa_join_clump_strategy strategy)
-{
- switch (strategy)
- {
- case JSTRAT_CLUMP_DEGENERATE:
- return "DEGENERATE";
- case JSTRAT_CLUMP_FOREIGN:
- return "FOREIGN";
- case JSTRAT_CLUMP_PARTITIONWISE:
- return "PARTITIONWISE";
- }
-
- Assert(false);
-}
-
-/*
- * Get a C string that corresponds to the specified join strategy.
- */
-static char *
-pgpa_cstring_join_strategy(pgpa_join_strategy strategy)
-{
- switch (strategy)
- {
- case JSTRAT_MERGE_JOIN_PLAIN:
- return "MERGE_JOIN_PLAIN";
- case JSTRAT_MERGE_JOIN_MATERIALIZE:
- return "MERGE_JOIN_MATERIALIZE";
- case JSTRAT_NESTED_LOOP_PLAIN:
- return "NESTED_LOOP_PLAIN";
- case JSTRAT_NESTED_LOOP_MATERIALIZE:
- return "NESTED_LOOP_MATERIALIZE";
- case JSTRAT_NESTED_LOOP_MEMOIZE:
- return "NESTED_LOOP_MEMOIZE";
- case JSTRAT_HASH_JOIN:
- return "HASH_JOIN";
- }
-
- Assert(false);
-}
-
/*
* Is this a Result node that has a child?
*/
extern void pgpa_add_to_gathered_join(pgpa_gathered_join *gathered_join,
Plan *plan);
-extern void pgpa_debug_out_clumped_join(StringInfo buf,
- pgpa_clumped_join *clump,
- const char **rt_identifiers);
-extern void pgpa_debug_out_unrolled_join(StringInfo buf,
- pgpa_unrolled_join *join,
- const char **rt_identifiers);
-extern void pgpa_debug_out_gathered_join(StringInfo buf,
- pgpa_gathered_join *gathered_join,
- const char **rt_identifiers);
-
#endif
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pgpa_output.c
+ * produce textual output from the results of a plan tree walk
+ *
+ * Copyright (c) 2016-2025, PostgreSQL Global Development Group
+ *
+ * contrib/pg_plan_advice/pgpa_output.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "pgpa_output.h"
+
+static void pgpa_debug_out_join_member(StringInfo buf,
+ pgpa_join_member *member,
+ const char **rt_identifiers);
+static char *pgpa_cstring_join_clump_strategy(pgpa_join_clump_strategy strategy);
+static char *pgpa_cstring_join_strategy(pgpa_join_strategy strategy);
+
+void
+pgpa_debug_out_clumped_join(StringInfo buf, pgpa_clumped_join *clump,
+ const char **rt_identifiers)
+{
+ char *cstrategy;
+ int rti = -1;
+ bool first = true;
+
+ cstrategy = pgpa_cstring_join_clump_strategy(clump->strategy);
+ appendStringInfo(buf, "%s(", cstrategy);
+ while ((rti = bms_next_member(clump->relids, rti)) >= 0)
+ {
+ const char *identifier = rt_identifiers[rti - 1];
+
+ if (identifier == NULL)
+ elog(ERROR, "no identifier for RTI %d", rti);
+
+ if (first)
+ {
+ first = false;
+ appendStringInfoString(buf, identifier);
+ }
+ else
+ appendStringInfo(buf, " %s", identifier);
+ }
+ appendStringInfoChar(buf, ')');
+}
+
+void
+pgpa_debug_out_unrolled_join(StringInfo buf, pgpa_unrolled_join *join,
+ const char **rt_identifiers)
+{
+ appendStringInfoChar(buf, '(');
+
+ pgpa_debug_out_join_member(buf, &join->outer, rt_identifiers);
+
+ for (int k = 0; k < join->ninner; ++k)
+ {
+ char *cstrategy;
+
+ cstrategy = pgpa_cstring_join_strategy(join->strategy[k]);
+ appendStringInfo(buf, " %s ", cstrategy);
+ pgpa_debug_out_join_member(buf, &join->inner[k], rt_identifiers);
+ }
+
+ appendStringInfoChar(buf, ')');
+}
+
+void
+pgpa_debug_out_gathered_join(StringInfo buf, pgpa_gathered_join *gathered_join,
+ const char **rt_identifiers)
+{
+ int rti = -1;
+ bool first = true;
+
+ if (gathered_join->is_merge)
+ appendStringInfo(buf, "GATHER_MERGE(");
+ else
+ appendStringInfo(buf, "GATHER(");
+
+ while ((rti = bms_next_member(gathered_join->relids, rti)) >= 0)
+ {
+ const char *identifier = rt_identifiers[rti - 1];
+
+ if (identifier == NULL)
+ elog(ERROR, "no identifier for RTI %d", rti);
+
+ if (first)
+ {
+ first = false;
+ appendStringInfoString(buf, identifier);
+ }
+ else
+ appendStringInfo(buf, " %s", identifier);
+ }
+ appendStringInfoChar(buf, ')');
+}
+
+static void
+pgpa_debug_out_join_member(StringInfo buf, pgpa_join_member *member,
+ const char **rt_identifiers)
+{
+ if (member->clump_join != NULL)
+ pgpa_debug_out_clumped_join(buf, member->clump_join,
+ rt_identifiers);
+ else if (member->unrolled_join != NULL)
+ pgpa_debug_out_unrolled_join(buf, member->unrolled_join,
+ rt_identifiers);
+ else
+ {
+ if (rt_identifiers[member->rti - 1] == NULL)
+ elog(ERROR, "no identifier for RTI %d", member->rti);
+ appendStringInfoString(buf, rt_identifiers[member->rti - 1]);
+ }
+}
+
+/*
+ * Get a C string that corresponds to the specified join clump strategy.
+ */
+static char *
+pgpa_cstring_join_clump_strategy(pgpa_join_clump_strategy strategy)
+{
+ switch (strategy)
+ {
+ case JSTRAT_CLUMP_DEGENERATE:
+ return "DEGENERATE";
+ case JSTRAT_CLUMP_FOREIGN:
+ return "FOREIGN";
+ case JSTRAT_CLUMP_PARTITIONWISE:
+ return "PARTITIONWISE";
+ }
+
+ Assert(false);
+}
+
+/*
+ * Get a C string that corresponds to the specified join strategy.
+ */
+static char *
+pgpa_cstring_join_strategy(pgpa_join_strategy strategy)
+{
+ switch (strategy)
+ {
+ case JSTRAT_MERGE_JOIN_PLAIN:
+ return "MERGE_JOIN_PLAIN";
+ case JSTRAT_MERGE_JOIN_MATERIALIZE:
+ return "MERGE_JOIN_MATERIALIZE";
+ case JSTRAT_NESTED_LOOP_PLAIN:
+ return "NESTED_LOOP_PLAIN";
+ case JSTRAT_NESTED_LOOP_MATERIALIZE:
+ return "NESTED_LOOP_MATERIALIZE";
+ case JSTRAT_NESTED_LOOP_MEMOIZE:
+ return "NESTED_LOOP_MEMOIZE";
+ case JSTRAT_HASH_JOIN:
+ return "HASH_JOIN";
+ }
+
+ Assert(false);
+}