1/*-------------------------------------------------------------------------
2 *
3 * joininfo.c
4 * joininfo list manipulation routines
5 *
6 * Portions Copyright (c) 1996-2019, 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 "optimizer/joininfo.h"
18#include "optimizer/pathnode.h"
19#include "optimizer/paths.h"
20
21
22/*
23 * have_relevant_joinclause
24 * Detect whether there is a joinclause that involves
25 * the two given relations.
26 *
27 * Note: the joinclause does not have to be evaluable with only these two
28 * relations. This is intentional. For example consider
29 * SELECT * FROM a, b, c WHERE a.x = (b.y + c.z)
30 * If a is much larger than the other tables, it may be worthwhile to
31 * cross-join b and c and then use an inner indexscan on a.x. Therefore
32 * we should consider this joinclause as reason to join b to c, even though
33 * it can't be applied at that join step.
34 */
35bool
36have_relevant_joinclause(PlannerInfo *root,
37 RelOptInfo *rel1, RelOptInfo *rel2)
38{
39 bool result = false;
40 List *joininfo;
41 Relids other_relids;
42 ListCell *l;
43
44 /*
45 * We could scan either relation's joininfo list; may as well use the
46 * shorter one.
47 */
48 if (list_length(rel1->joininfo) <= list_length(rel2->joininfo))
49 {
50 joininfo = rel1->joininfo;
51 other_relids = rel2->relids;
52 }
53 else
54 {
55 joininfo = rel2->joininfo;
56 other_relids = rel1->relids;
57 }
58
59 foreach(l, joininfo)
60 {
61 RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
62
63 if (bms_overlap(other_relids, rinfo->required_relids))
64 {
65 result = true;
66 break;
67 }
68 }
69
70 /*
71 * We also need to check the EquivalenceClass data structure, which might
72 * contain relationships not emitted into the joininfo lists.
73 */
74 if (!result && rel1->has_eclass_joins && rel2->has_eclass_joins)
75 result = have_relevant_eclass_joinclause(root, rel1, rel2);
76
77 return result;
78}
79
80
81/*
82 * add_join_clause_to_rels
83 * Add 'restrictinfo' to the joininfo list of each relation it requires.
84 *
85 * Note that the same copy of the restrictinfo node is linked to by all the
86 * lists it is in. This allows us to exploit caching of information about
87 * the restriction clause (but we must be careful that the information does
88 * not depend on context).
89 *
90 * 'restrictinfo' describes the join clause
91 * 'join_relids' is the list of relations participating in the join clause
92 * (there must be more than one)
93 */
94void
95add_join_clause_to_rels(PlannerInfo *root,
96 RestrictInfo *restrictinfo,
97 Relids join_relids)
98{
99 int cur_relid;
100
101 cur_relid = -1;
102 while ((cur_relid = bms_next_member(join_relids, cur_relid)) >= 0)
103 {
104 RelOptInfo *rel = find_base_rel(root, cur_relid);
105
106 rel->joininfo = lappend(rel->joininfo, restrictinfo);
107 }
108}
109
110/*
111 * remove_join_clause_from_rels
112 * Delete 'restrictinfo' from all the joininfo lists it is in
113 *
114 * This reverses the effect of add_join_clause_to_rels. It's used when we
115 * discover that a relation need not be joined at all.
116 *
117 * 'restrictinfo' describes the join clause
118 * 'join_relids' is the list of relations participating in the join clause
119 * (there must be more than one)
120 */
121void
122remove_join_clause_from_rels(PlannerInfo *root,
123 RestrictInfo *restrictinfo,
124 Relids join_relids)
125{
126 int cur_relid;
127
128 cur_relid = -1;
129 while ((cur_relid = bms_next_member(join_relids, cur_relid)) >= 0)
130 {
131 RelOptInfo *rel = find_base_rel(root, cur_relid);
132
133 /*
134 * Remove the restrictinfo from the list. Pointer comparison is
135 * sufficient.
136 */
137 Assert(list_member_ptr(rel->joininfo, restrictinfo));
138 rel->joininfo = list_delete_ptr(rel->joininfo, restrictinfo);
139 }
140}
141