Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pgpa_join.h
4 : * analysis of joins in Plan trees
5 : *
6 : * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 : *
8 : * contrib/pg_plan_advice/pgpa_join.h
9 : *
10 : *-------------------------------------------------------------------------
11 : */
12 : #ifndef PGPA_JOIN_H
13 : #define PGPA_JOIN_H
14 :
15 : #include "nodes/plannodes.h"
16 :
17 : typedef struct pgpa_plan_walker_context pgpa_plan_walker_context;
18 : typedef struct pgpa_join_unroller pgpa_join_unroller;
19 : typedef struct pgpa_unrolled_join pgpa_unrolled_join;
20 :
21 : /*
22 : * Although there are three main join strategies, we try to classify things
23 : * more precisely here: merge joins have the option of using materialization
24 : * on the inner side, and nested loops can use either materialization or
25 : * memoization.
26 : */
27 : typedef enum
28 : {
29 : JSTRAT_MERGE_JOIN_PLAIN = 0,
30 : JSTRAT_MERGE_JOIN_MATERIALIZE,
31 : JSTRAT_NESTED_LOOP_PLAIN,
32 : JSTRAT_NESTED_LOOP_MATERIALIZE,
33 : JSTRAT_NESTED_LOOP_MEMOIZE,
34 : JSTRAT_HASH_JOIN
35 : /* update NUM_PGPA_JOIN_STRATEGY if you add anything here */
36 : } pgpa_join_strategy;
37 :
38 : #define NUM_PGPA_JOIN_STRATEGY ((int) JSTRAT_HASH_JOIN + 1)
39 :
40 : /*
41 : * In an outer-deep join tree, every member of an unrolled join will be a scan,
42 : * but join trees with other shapes can contain unrolled joins.
43 : *
44 : * The plan node we store here will be the inner or outer child of the join
45 : * node, as appropriate, except that we look through subnodes that we regard as
46 : * part of the join method itself. For instance, for a Nested Loop that
47 : * materializes the inner input, we'll store the child of the Materialize node,
48 : * not the Materialize node itself.
49 : *
50 : * If setrefs processing elided one or more nodes from the plan tree, then
51 : * we'll store details about the topmost of those in elided_node; otherwise,
52 : * it will be NULL.
53 : *
54 : * Exactly one of scan and unrolled_join will be non-NULL.
55 : */
56 : typedef struct
57 : {
58 : Plan *plan;
59 : ElidedNode *elided_node;
60 : struct pgpa_scan *scan;
61 : pgpa_unrolled_join *unrolled_join;
62 : } pgpa_join_member;
63 :
64 : /*
65 : * We convert outer-deep join trees to a flat structure; that is, ((A JOIN B)
66 : * JOIN C) JOIN D gets converted to outer = A, inner = <B C D>. When joins
67 : * aren't outer-deep, substructure is required, e.g. (A JOIN B) JOIN (C JOIN D)
68 : * is represented as outer = A, inner = <B X>, where X is a pgpa_unrolled_join
69 : * covering C-D.
70 : */
71 : struct pgpa_unrolled_join
72 : {
73 : /* Outermost member; must not itself be an unrolled join. */
74 : pgpa_join_member outer;
75 :
76 : /* Number of inner members. Length of the strategy and inner arrays. */
77 : unsigned ninner;
78 :
79 : /* Array of strategies, one per non-outermost member. */
80 : pgpa_join_strategy *strategy;
81 :
82 : /* Array of members, excluding the outermost. Deepest first. */
83 : pgpa_join_member *inner;
84 : };
85 :
86 : /*
87 : * Does this plan node inherit from Join?
88 : */
89 : static inline bool
90 714 : pgpa_is_join(Plan *plan)
91 : {
92 714 : return IsA(plan, NestLoop) || IsA(plan, MergeJoin) || IsA(plan, HashJoin);
93 : }
94 :
95 : extern pgpa_join_unroller *pgpa_create_join_unroller(void);
96 : extern void pgpa_unroll_join(pgpa_plan_walker_context *walker,
97 : Plan *plan, bool beneath_any_gather,
98 : pgpa_join_unroller *join_unroller,
99 : pgpa_join_unroller **outer_join_unroller,
100 : pgpa_join_unroller **inner_join_unroller);
101 : extern pgpa_unrolled_join *pgpa_build_unrolled_join(pgpa_plan_walker_context *walker,
102 : pgpa_join_unroller *join_unroller);
103 : extern void pgpa_destroy_join_unroller(pgpa_join_unroller *join_unroller);
104 :
105 : #endif
|