Coverage Report

Created: 2025-07-03 06:49

/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
}