vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/join.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package planbuilder 18 19 import ( 20 "fmt" 21 22 "vitess.io/vitess/go/vt/vterrors" 23 24 "vitess.io/vitess/go/vt/sqlparser" 25 "vitess.io/vitess/go/vt/vtgate/engine" 26 ) 27 28 var _ logicalPlan = (*join)(nil) 29 30 // join is used to build a Join primitive. 31 // It's used to build a normal join or a left join 32 // operation. 33 type join struct { 34 v3Plan 35 order int 36 resultColumns []*resultColumn 37 weightStrings map[*resultColumn]int 38 39 // leftOrder stores the order number of the left node. This is 40 // used for a b-tree style traversal towards the target route. 41 // Let us assume the following execution tree: 42 // J9 43 // / \ 44 // / \ 45 // J3 J8 46 // / \ / \ 47 // R1 R2 J6 R7 48 // / \ 49 // R4 R5 50 // 51 // In the above trees, the suffix numbers indicate the 52 // execution order. The leftOrder for the joins will then 53 // be as follows: 54 // J3: 1 55 // J6: 4 56 // J8: 6 57 // J9: 3 58 // 59 // The route to R4 would be: 60 // Go right from J9->J8 because Left(J9)==3, which is <4. 61 // Go left from J8->J6 because Left(J8)==6, which is >=4. 62 // Go left from J6->R4 because Left(J6)==4, the destination. 63 // Look for 'isOnLeft' to see how these numbers are used. 64 leftOrder int 65 66 // Left and Right are the nodes for the join. 67 Left, Right logicalPlan 68 69 ejoin *engine.Join 70 } 71 72 // newJoin makes a new join using the two planBuilder. ajoin can be nil 73 // if the join is on a ',' operator. lpb will contain the resulting join. 74 // rpb will be discarded. 75 func newJoin(lpb, rpb *primitiveBuilder, ajoin *sqlparser.JoinTableExpr, reservedVars *sqlparser.ReservedVars) error { 76 // This function converts ON clauses to WHERE clauses. The WHERE clause 77 // scope can see all tables, whereas the ON clause can only see the 78 // participants of the JOIN. However, since the ON clause doesn't allow 79 // external references, and the FROM clause doesn't allow duplicates, 80 // it's safe to perform this conversion and still expect the same behavior. 81 82 opcode := engine.InnerJoin 83 if ajoin != nil { 84 switch { 85 case ajoin.Join == sqlparser.LeftJoinType: 86 opcode = engine.LeftJoin 87 88 // For left joins, we have to push the ON clause into the RHS. 89 // We do this before creating the join primitive. 90 // However, variables of LHS need to be visible. To allow this, 91 // we mark the LHS symtab as outer scope to the RHS, just like 92 // a subquery. This make the RHS treat the LHS symbols as external. 93 // This will prevent constructs from escaping out of the rpb scope. 94 // At this point, the LHS symtab also contains symbols of the RHS. 95 // But the RHS will hide those, as intended. 96 rpb.st.Outer = lpb.st 97 if err := rpb.pushFilter(ajoin.Condition.On, sqlparser.WhereStr, reservedVars); err != nil { 98 return err 99 } 100 case ajoin.Condition.Using != nil: 101 return vterrors.VT12001("JOIN with USING(column_list) clause for complex queries") 102 } 103 } 104 lpb.plan = &join{ 105 weightStrings: make(map[*resultColumn]int), 106 Left: lpb.plan, 107 Right: rpb.plan, 108 ejoin: &engine.Join{ 109 Opcode: opcode, 110 Vars: make(map[string]int), 111 }, 112 } 113 lpb.plan.Reorder(0) 114 if ajoin == nil || opcode == engine.LeftJoin { 115 return nil 116 } 117 return lpb.pushFilter(ajoin.Condition.On, sqlparser.WhereStr, reservedVars) 118 } 119 120 // Order implements the logicalPlan interface 121 func (jb *join) Order() int { 122 return jb.order 123 } 124 125 // Reorder implements the logicalPlan interface 126 func (jb *join) Reorder(order int) { 127 jb.Left.Reorder(order) 128 jb.leftOrder = jb.Left.Order() 129 jb.Right.Reorder(jb.leftOrder) 130 jb.order = jb.Right.Order() + 1 131 } 132 133 // Primitive implements the logicalPlan interface 134 func (jb *join) Primitive() engine.Primitive { 135 jb.ejoin.Left = jb.Left.Primitive() 136 jb.ejoin.Right = jb.Right.Primitive() 137 return jb.ejoin 138 } 139 140 // ResultColumns implements the logicalPlan interface 141 func (jb *join) ResultColumns() []*resultColumn { 142 return jb.resultColumns 143 } 144 145 // Wireup implements the logicalPlan interface 146 func (jb *join) Wireup(plan logicalPlan, jt *jointab) error { 147 err := jb.Right.Wireup(plan, jt) 148 if err != nil { 149 return err 150 } 151 return jb.Left.Wireup(plan, jt) 152 } 153 154 // SupplyVar implements the logicalPlan interface 155 func (jb *join) SupplyVar(from, to int, col *sqlparser.ColName, varname string) { 156 if !jb.isOnLeft(from) { 157 jb.Right.SupplyVar(from, to, col, varname) 158 return 159 } 160 if jb.isOnLeft(to) { 161 jb.Left.SupplyVar(from, to, col, varname) 162 return 163 } 164 if _, ok := jb.ejoin.Vars[varname]; ok { 165 // Looks like somebody else already requested this. 166 return 167 } 168 c := col.Metadata.(*column) 169 for i, rc := range jb.resultColumns { 170 if jb.ejoin.Cols[i] > 0 { 171 continue 172 } 173 if rc.column == c { 174 jb.ejoin.Vars[varname] = -jb.ejoin.Cols[i] - 1 175 return 176 } 177 } 178 _, jb.ejoin.Vars[varname] = jb.Left.SupplyCol(col) 179 } 180 181 // SupplyCol implements the logicalPlan interface 182 func (jb *join) SupplyCol(col *sqlparser.ColName) (rc *resultColumn, colNumber int) { 183 c := col.Metadata.(*column) 184 for i, rc := range jb.resultColumns { 185 if rc.column == c { 186 return rc, i 187 } 188 } 189 190 routeNumber := c.Origin().Order() 191 var sourceCol int 192 if jb.isOnLeft(routeNumber) { 193 rc, sourceCol = jb.Left.SupplyCol(col) 194 jb.ejoin.Cols = append(jb.ejoin.Cols, -sourceCol-1) 195 } else { 196 rc, sourceCol = jb.Right.SupplyCol(col) 197 jb.ejoin.Cols = append(jb.ejoin.Cols, sourceCol+1) 198 } 199 jb.resultColumns = append(jb.resultColumns, rc) 200 return rc, len(jb.ejoin.Cols) - 1 201 } 202 203 // SupplyWeightString implements the logicalPlan interface 204 func (jb *join) SupplyWeightString(colNumber int, alsoAddToGroupBy bool) (weightcolNumber int, err error) { 205 rc := jb.resultColumns[colNumber] 206 if weightcolNumber, ok := jb.weightStrings[rc]; ok { 207 return weightcolNumber, nil 208 } 209 routeNumber := rc.column.Origin().Order() 210 if jb.isOnLeft(routeNumber) { 211 sourceCol, err := jb.Left.SupplyWeightString(-jb.ejoin.Cols[colNumber]-1, alsoAddToGroupBy) 212 if err != nil { 213 return 0, err 214 } 215 jb.ejoin.Cols = append(jb.ejoin.Cols, -sourceCol-1) 216 } else { 217 sourceCol, err := jb.Right.SupplyWeightString(jb.ejoin.Cols[colNumber]-1, alsoAddToGroupBy) 218 if err != nil { 219 return 0, err 220 } 221 jb.ejoin.Cols = append(jb.ejoin.Cols, sourceCol+1) 222 } 223 jb.resultColumns = append(jb.resultColumns, rc) 224 jb.weightStrings[rc] = len(jb.ejoin.Cols) - 1 225 return len(jb.ejoin.Cols) - 1, nil 226 } 227 228 // Rewrite implements the logicalPlan interface 229 func (jb *join) Rewrite(inputs ...logicalPlan) error { 230 if len(inputs) != 2 { 231 return vterrors.VT13001(fmt.Sprintf("join: wrong number of inputs, got: %d, expect: 2", len(inputs))) 232 } 233 jb.Left = inputs[0] 234 jb.Right = inputs[1] 235 return nil 236 } 237 238 // Inputs implements the logicalPlan interface 239 func (jb *join) Inputs() []logicalPlan { 240 return []logicalPlan{jb.Left, jb.Right} 241 } 242 243 // isOnLeft returns true if the specified route number 244 // is on the left side of the join. If false, it means 245 // the node is on the right. 246 func (jb *join) isOnLeft(nodeNum int) bool { 247 return nodeNum <= jb.leftOrder 248 }