/src/postgres/src/backend/optimizer/util/joininfo.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * joininfo.c |
4 | | * joininfo list manipulation routines |
5 | | * |
6 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/optimizer/util/joininfo.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | #include "postgres.h" |
16 | | |
17 | | #include "nodes/makefuncs.h" |
18 | | #include "optimizer/joininfo.h" |
19 | | #include "optimizer/pathnode.h" |
20 | | #include "optimizer/paths.h" |
21 | | #include "optimizer/planmain.h" |
22 | | #include "optimizer/restrictinfo.h" |
23 | | |
24 | | |
25 | | /* |
26 | | * have_relevant_joinclause |
27 | | * Detect whether there is a joinclause that involves |
28 | | * the two given relations. |
29 | | * |
30 | | * Note: the joinclause does not have to be evaluable with only these two |
31 | | * relations. This is intentional. For example consider |
32 | | * SELECT * FROM a, b, c WHERE a.x = (b.y + c.z) |
33 | | * If a is much larger than the other tables, it may be worthwhile to |
34 | | * cross-join b and c and then use an inner indexscan on a.x. Therefore |
35 | | * we should consider this joinclause as reason to join b to c, even though |
36 | | * it can't be applied at that join step. |
37 | | */ |
38 | | bool |
39 | | have_relevant_joinclause(PlannerInfo *root, |
40 | | RelOptInfo *rel1, RelOptInfo *rel2) |
41 | 0 | { |
42 | 0 | bool result = false; |
43 | 0 | List *joininfo; |
44 | 0 | Relids other_relids; |
45 | 0 | ListCell *l; |
46 | | |
47 | | /* |
48 | | * We could scan either relation's joininfo list; may as well use the |
49 | | * shorter one. |
50 | | */ |
51 | 0 | if (list_length(rel1->joininfo) <= list_length(rel2->joininfo)) |
52 | 0 | { |
53 | 0 | joininfo = rel1->joininfo; |
54 | 0 | other_relids = rel2->relids; |
55 | 0 | } |
56 | 0 | else |
57 | 0 | { |
58 | 0 | joininfo = rel2->joininfo; |
59 | 0 | other_relids = rel1->relids; |
60 | 0 | } |
61 | |
|
62 | 0 | foreach(l, joininfo) |
63 | 0 | { |
64 | 0 | RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); |
65 | |
|
66 | 0 | if (bms_overlap(other_relids, rinfo->required_relids)) |
67 | 0 | { |
68 | 0 | result = true; |
69 | 0 | break; |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | | /* |
74 | | * We also need to check the EquivalenceClass data structure, which might |
75 | | * contain relationships not emitted into the joininfo lists. |
76 | | */ |
77 | 0 | if (!result && rel1->has_eclass_joins && rel2->has_eclass_joins) |
78 | 0 | result = have_relevant_eclass_joinclause(root, rel1, rel2); |
79 | |
|
80 | 0 | return result; |
81 | 0 | } |
82 | | |
83 | | |
84 | | /* |
85 | | * add_join_clause_to_rels |
86 | | * Add 'restrictinfo' to the joininfo list of each relation it requires. |
87 | | * |
88 | | * Note that the same copy of the restrictinfo node is linked to by all the |
89 | | * lists it is in. This allows us to exploit caching of information about |
90 | | * the restriction clause (but we must be careful that the information does |
91 | | * not depend on context). |
92 | | * |
93 | | * 'restrictinfo' describes the join clause |
94 | | * 'join_relids' is the set of relations participating in the join clause |
95 | | * (some of these could be outer joins) |
96 | | */ |
97 | | void |
98 | | add_join_clause_to_rels(PlannerInfo *root, |
99 | | RestrictInfo *restrictinfo, |
100 | | Relids join_relids) |
101 | 0 | { |
102 | 0 | int cur_relid; |
103 | | |
104 | | /* Don't add the clause if it is always true */ |
105 | 0 | if (restriction_is_always_true(root, restrictinfo)) |
106 | 0 | return; |
107 | | |
108 | | /* |
109 | | * Substitute the origin qual with constant-FALSE if it is provably always |
110 | | * false. |
111 | | * |
112 | | * Note that we need to keep the same rinfo_serial, since it is in |
113 | | * practice the same condition. We also need to reset the |
114 | | * last_rinfo_serial counter, which is essential to ensure that the |
115 | | * RestrictInfos for the "same" qual condition get identical serial |
116 | | * numbers (see deconstruct_distribute_oj_quals). |
117 | | */ |
118 | 0 | if (restriction_is_always_false(root, restrictinfo)) |
119 | 0 | { |
120 | 0 | int save_rinfo_serial = restrictinfo->rinfo_serial; |
121 | 0 | int save_last_rinfo_serial = root->last_rinfo_serial; |
122 | |
|
123 | 0 | restrictinfo = make_restrictinfo(root, |
124 | 0 | (Expr *) makeBoolConst(false, false), |
125 | 0 | restrictinfo->is_pushed_down, |
126 | 0 | restrictinfo->has_clone, |
127 | 0 | restrictinfo->is_clone, |
128 | 0 | restrictinfo->pseudoconstant, |
129 | 0 | 0, /* security_level */ |
130 | 0 | restrictinfo->required_relids, |
131 | 0 | restrictinfo->incompatible_relids, |
132 | 0 | restrictinfo->outer_relids); |
133 | 0 | restrictinfo->rinfo_serial = save_rinfo_serial; |
134 | 0 | root->last_rinfo_serial = save_last_rinfo_serial; |
135 | 0 | } |
136 | |
|
137 | 0 | cur_relid = -1; |
138 | 0 | while ((cur_relid = bms_next_member(join_relids, cur_relid)) >= 0) |
139 | 0 | { |
140 | 0 | RelOptInfo *rel = find_base_rel_ignore_join(root, cur_relid); |
141 | | |
142 | | /* We only need to add the clause to baserels */ |
143 | 0 | if (rel == NULL) |
144 | 0 | continue; |
145 | 0 | rel->joininfo = lappend(rel->joininfo, restrictinfo); |
146 | 0 | } |
147 | 0 | } |
148 | | |
149 | | /* |
150 | | * remove_join_clause_from_rels |
151 | | * Delete 'restrictinfo' from all the joininfo lists it is in |
152 | | * |
153 | | * This reverses the effect of add_join_clause_to_rels. It's used when we |
154 | | * discover that a relation need not be joined at all. |
155 | | * |
156 | | * 'restrictinfo' describes the join clause |
157 | | * 'join_relids' is the set of relations participating in the join clause |
158 | | * (some of these could be outer joins) |
159 | | */ |
160 | | void |
161 | | remove_join_clause_from_rels(PlannerInfo *root, |
162 | | RestrictInfo *restrictinfo, |
163 | | Relids join_relids) |
164 | 0 | { |
165 | 0 | int cur_relid; |
166 | |
|
167 | 0 | cur_relid = -1; |
168 | 0 | while ((cur_relid = bms_next_member(join_relids, cur_relid)) >= 0) |
169 | 0 | { |
170 | 0 | RelOptInfo *rel = find_base_rel_ignore_join(root, cur_relid); |
171 | | |
172 | | /* We would only have added the clause to baserels */ |
173 | 0 | if (rel == NULL) |
174 | 0 | continue; |
175 | | |
176 | | /* |
177 | | * Remove the restrictinfo from the list. Pointer comparison is |
178 | | * sufficient. |
179 | | */ |
180 | 0 | Assert(list_member_ptr(rel->joininfo, restrictinfo)); |
181 | 0 | rel->joininfo = list_delete_ptr(rel->joininfo, restrictinfo); |
182 | 0 | } |
183 | 0 | } |