Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * joininfo.c 4 : * joininfo list manipulation routines 5 : * 6 : * Portions Copyright (c) 1996-2024, 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 337774 : have_relevant_joinclause(PlannerInfo *root, 40 : RelOptInfo *rel1, RelOptInfo *rel2) 41 : { 42 337774 : bool result = false; 43 : List *joininfo; 44 : Relids other_relids; 45 : ListCell *l; 46 : 47 : /* 48 : * We could scan either relation's joininfo list; may as well use the 49 : * shorter one. 50 : */ 51 337774 : if (list_length(rel1->joininfo) <= list_length(rel2->joininfo)) 52 : { 53 249526 : joininfo = rel1->joininfo; 54 249526 : other_relids = rel2->relids; 55 : } 56 : else 57 : { 58 88248 : joininfo = rel2->joininfo; 59 88248 : other_relids = rel1->relids; 60 : } 61 : 62 372728 : foreach(l, joininfo) 63 : { 64 180660 : RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); 65 : 66 180660 : if (bms_overlap(other_relids, rinfo->required_relids)) 67 : { 68 145706 : result = true; 69 145706 : break; 70 : } 71 : } 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 337774 : if (!result && rel1->has_eclass_joins && rel2->has_eclass_joins) 78 141670 : result = have_relevant_eclass_joinclause(root, rel1, rel2); 79 : 80 337774 : return result; 81 : } 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 63406 : add_join_clause_to_rels(PlannerInfo *root, 99 : RestrictInfo *restrictinfo, 100 : Relids join_relids) 101 : { 102 : int cur_relid; 103 : 104 : /* Don't add the clause if it is always true */ 105 63406 : if (restriction_is_always_true(root, restrictinfo)) 106 24 : return; 107 : 108 : /* 109 : * Substitute constant-FALSE for the origin qual if it is always false. 110 : * Note that we keep the same rinfo_serial. 111 : */ 112 63382 : if (restriction_is_always_false(root, restrictinfo)) 113 : { 114 12 : int save_rinfo_serial = restrictinfo->rinfo_serial; 115 : 116 12 : restrictinfo = make_restrictinfo(root, 117 12 : (Expr *) makeBoolConst(false, false), 118 12 : restrictinfo->is_pushed_down, 119 12 : restrictinfo->has_clone, 120 12 : restrictinfo->is_clone, 121 12 : restrictinfo->pseudoconstant, 122 : 0, /* security_level */ 123 : restrictinfo->required_relids, 124 : restrictinfo->incompatible_relids, 125 : restrictinfo->outer_relids); 126 12 : restrictinfo->rinfo_serial = save_rinfo_serial; 127 : } 128 : 129 63382 : cur_relid = -1; 130 204420 : while ((cur_relid = bms_next_member(join_relids, cur_relid)) >= 0) 131 : { 132 141038 : RelOptInfo *rel = find_base_rel_ignore_join(root, cur_relid); 133 : 134 : /* We only need to add the clause to baserels */ 135 141038 : if (rel == NULL) 136 8234 : continue; 137 132804 : rel->joininfo = lappend(rel->joininfo, restrictinfo); 138 : } 139 : } 140 : 141 : /* 142 : * remove_join_clause_from_rels 143 : * Delete 'restrictinfo' from all the joininfo lists it is in 144 : * 145 : * This reverses the effect of add_join_clause_to_rels. It's used when we 146 : * discover that a relation need not be joined at all. 147 : * 148 : * 'restrictinfo' describes the join clause 149 : * 'join_relids' is the set of relations participating in the join clause 150 : * (some of these could be outer joins) 151 : */ 152 : void 153 9514 : remove_join_clause_from_rels(PlannerInfo *root, 154 : RestrictInfo *restrictinfo, 155 : Relids join_relids) 156 : { 157 : int cur_relid; 158 : 159 9514 : cur_relid = -1; 160 28934 : while ((cur_relid = bms_next_member(join_relids, cur_relid)) >= 0) 161 : { 162 19420 : RelOptInfo *rel = find_base_rel_ignore_join(root, cur_relid); 163 : 164 : /* We would only have added the clause to baserels */ 165 19420 : if (rel == NULL) 166 242 : continue; 167 : 168 : /* 169 : * Remove the restrictinfo from the list. Pointer comparison is 170 : * sufficient. 171 : */ 172 : Assert(list_member_ptr(rel->joininfo, restrictinfo)); 173 19178 : rel->joininfo = list_delete_ptr(rel->joininfo, restrictinfo); 174 : } 175 9514 : }