On Fri, Dec 5, 2025, at 2:57 PM, Robert Haas wrote:
> 014f9a831a320666bf2195949f41710f970c54ad removes the need for what was
> previously 0004, so here is a new patch series with that dropped, to
> avoid confusing cfbot or human reviewers.
>
> -- 
> Robert Haas
> EDB: http://www.enterprisedb.com

Hey Robert,

Thanks for working on this!  I think the idea has merit, I hope it lands 
sometime soon.

I've worked on extending PostgreSQL's planner for a JSONB-like document system. 
The new query shapes frequently caused mysterious planner issues. Having the 
ability to:

1. Dump detailed planner decisions for comparison during development
2. Record planner choices from customer databases to reproduce their issues
3. Apply fixed plans to specific queries as a quick customer workaround while 
addressing root causes in later releases

...would have been invaluable.

Yes, there's danger here ("with great power comes great responsibility"), but I 
see this as providing more information to make better decisions when working 
with the black art of planner logic.

ORM Query Variation Challenge

Jakob's point about "crazy ORMs" is important. ORMs generate queries with minor 
variations that should ideally match the same plan advice. I need to study the 
plan advice matching logic more deeply to understand how it handles query 
variations.

This reminded me of Erlang/Elixir's "parse transforms" - compiled code forms an 
AST that registered transforms can modify via pattern matching. The concept 
might be relevant here: pattern-matching portions of query ASTs to apply advice 
despite syntactic variations. I'd need to think more about whether this 
intersects well with the current design or if it's impractical, but it's worth 
exploring.

> Attachments:
> * v5-0001-Store-information-about-range-table-flattening-in.patch

contrib/pg_overexplain/pg_overexplain.c

+               /* Advance to next SubRTInfo, if it's time. */
+               if (lc_subrtinfo != NULL)
+               {
+                       next_rtinfo = lfirst(lc_subrtinfo);
+                       if (rti > next_rtinfo->rtoffset)

Should the test be >= not >? Unless I am I reading this wrong, when rti == 
rtoffset, that's the first entry of the new subplan's range table. That would 
mean that the current logic skips displaying the subplan name for the first RTE 
of each subplan.

in src/include/nodes/plannodes.h there is:

+typedef struct SubPlanRTInfo
+{
+       NodeTag         type;
+       const char *plan_name;
+       Index           rtoffset;
+       bool            dummy;
+} SubPlanRTInfo;

This is where I get confused, if rtoffset is an Index, then the comparison 
(above) in pg_overexplain uses rti > next_rtinfo->rtoffset where rti starts at 
1. If rtoffset is 0 for the first subplan, the logic might be off-by-one, no?

> This commit teaches pg_overexplain'e RANGE_TABLE option to make use
Minor nit in the commit message, "pg_overexplain'e" should be "pg_overexplain's"

> * v5-0002-Store-information-about-elided-nodes-in-the-final.patch

+/*
+ * Record some details about a node removed from the plan during setrefs
+ * procesing, for the benefit of code trying to reconstruct planner decisions
+ * from examination of the final plan tree.
+ */

Nit, "procesing" should be "processing"

> * v5-0003-Store-information-about-Append-node-consolidation.patch

src/backend/optimizer/path/allpaths.c

/* Now consider each interesting sort ordering */
foreach(lcp, all_child_pathkeys)
{
        List       *subpaths = NIL;
        bool            subpaths_valid = true;
+       List       *subpath_cars = NIL;
        List       *startup_subpaths = NIL;
        bool            startup_subpaths_valid = true;
+       List       *startup_subpath_cars = NIL;
        List       *partial_subpaths = NIL;
+       List       *partial_subpath_cars = NIL;
        List       *pa_partial_subpaths = NIL;
        List       *pa_nonpartial_subpaths = NIL;
+       List       *pa_subpath_cars = NIL;

I find "cars" a bit cryptic (albeit clever), I think I've decoded it properly 
and it stands for "child_append_relid_sets", correct?  Could you add a comment 
or use a clearer name like subpath_child_relids or consolidated_relid_sets?


+accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths,
+                                                 List 
**child_append_relid_sets)
 {
        if (IsA(path, AppendPath))
        {
@@ -2219,6 +2256,8 @@ accumulate_append_subpath(Path *path, List **subpaths, 
List **special_subpaths)
                if (!apath->path.parallel_aware || apath->first_partial_path == 
0)
                {
                        *subpaths = list_concat(*subpaths, apath->subpaths);
+                       *child_append_relid_sets =
+                               lappend(*child_append_relid_sets, 
path->parent->relids);

Is it possible that when pulling up multiple subpaths from an AppendPath, only 
ONE relid set is added to child_append_relid_sets, but MULTIPLE paths are added 
to subpaths?  If so, that would break the correspondence between the lists 
which would be bad, right?

src/include/nodes/pathnodes.h
+ * Whenever accumulate_append_subpath() allows us to consolidate multiple
+ * levels of Append paths are consolidated down to one, we store the RTI
+ * sets for the omitted paths in child_append_relid_sets. This is not necessary
+ * for planning or execution; we do it for the benefit of code that wants
+ * to inspect the final plan and understand how it came to be.

Minor: "paths are consolidated" is redundant, should be "paths consolidated" or 
"allows us to consolidate".

> * v5-0004-Allow-for-plugin-control-over-path-generation-str.patch

src/backend/optimizer/path/costsize.c
+       else
+               enable_mask |= PGS_CONSIDER_NONPARTIAL;

-       path->disabled_nodes = enable_seqscan ? 0 : 1;
+       path->disabled_nodes =
+               (baserel->pgs_mask & enable_mask) == enable_mask ? 0 : 1;

When parallel_workers > 0 the path is partial and doesn't need 
PGS_CONSIDER_NONPARTIAL. But if parallel_workers == 0, it's non-partial and 
DOES need it, right?  Would this mean that non-partial paths can be disabled 
even when the scan type itself (e.g., PGS_SEQSCAN) is enabled?  Intentional?

> * v5-0005-WIP-Add-pg_plan_advice-contrib-module.patch

It seems this is still WIP with a solid start, I'm not going to dig too much 
into it. :)

Keep it up, best.

-greg


Reply via email to