vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/routeGen4.go (about) 1 /* 2 Copyright 2021 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 querypb "vitess.io/vitess/go/vt/proto/query" 21 "vitess.io/vitess/go/vt/vterrors" 22 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 23 "vitess.io/vitess/go/vt/vtgate/semantics" 24 25 "vitess.io/vitess/go/vt/sqlparser" 26 "vitess.io/vitess/go/vt/vtgate/engine" 27 "vitess.io/vitess/go/vt/vtgate/vindexes" 28 ) 29 30 var _ logicalPlan = (*routeGen4)(nil) 31 32 // routeGen4 is used to build a Route primitive. 33 // It's used to build one of the Select routes like 34 // SelectScatter, etc. Portions of the original Select AST 35 // are moved into this node, which will be used to build 36 // the final SQL for this route. 37 type routeGen4 struct { 38 gen4Plan 39 40 // Select is the AST for the query fragment that will be 41 // executed by this route. 42 Select sqlparser.SelectStatement 43 44 // condition stores the AST condition that will be used 45 // to resolve the ERoute Values field. 46 condition sqlparser.Expr 47 48 // eroute is the primitive being built. 49 eroute *engine.Route 50 51 // is the engine primitive we will return from the Primitive() method. Note that it could be different than eroute 52 enginePrimitive engine.Primitive 53 54 // tables keeps track of which tables this route is covering 55 tables semantics.TableSet 56 } 57 58 // Primitive implements the logicalPlan interface 59 func (rb *routeGen4) Primitive() engine.Primitive { 60 return rb.enginePrimitive 61 } 62 63 // SetLimit adds a LIMIT clause to the route. 64 func (rb *routeGen4) SetLimit(limit *sqlparser.Limit) { 65 rb.Select.SetLimit(limit) 66 } 67 68 // WireupGen4 implements the logicalPlan interface 69 func (rb *routeGen4) WireupGen4(ctx *plancontext.PlanningContext) error { 70 rb.prepareTheAST() 71 72 // prepare the queries we will pass down 73 rb.eroute.Query = sqlparser.String(rb.Select) 74 buffer := sqlparser.NewTrackedBuffer(sqlparser.FormatImpossibleQuery) 75 node := buffer.WriteNode(rb.Select) 76 parsedQuery := node.ParsedQuery() 77 rb.eroute.FieldQuery = parsedQuery.Query 78 79 // if we have a planable vindex lookup, let's extract it into its own primitive 80 planableVindex, ok := rb.eroute.RoutingParameters.Vindex.(vindexes.LookupPlanable) 81 if !ok { 82 rb.enginePrimitive = rb.eroute 83 return nil 84 } 85 86 query, args := planableVindex.Query() 87 stmt, reserved, err := sqlparser.Parse2(query) 88 if err != nil { 89 return err 90 } 91 reservedVars := sqlparser.NewReservedVars("vtg", reserved) 92 93 lookupPrimitive, err := gen4SelectStmtPlanner(query, querypb.ExecuteOptions_Gen4, stmt.(sqlparser.SelectStatement), reservedVars, ctx.VSchema) 94 if err != nil { 95 return vterrors.Wrapf(err, "failed to plan the lookup query: [%s]", query) 96 } 97 98 rb.enginePrimitive = &engine.VindexLookup{ 99 Opcode: rb.eroute.Opcode, 100 Vindex: planableVindex, 101 Keyspace: rb.eroute.Keyspace, 102 Values: rb.eroute.Values, 103 SendTo: rb.eroute, 104 Arguments: args, 105 Lookup: lookupPrimitive.primitive, 106 } 107 108 rb.eroute.RoutingParameters.Opcode = engine.ByDestination 109 rb.eroute.RoutingParameters.Values = nil 110 rb.eroute.RoutingParameters.Vindex = nil 111 112 return nil 113 } 114 115 // ContainsTables implements the logicalPlan interface 116 func (rb *routeGen4) ContainsTables() semantics.TableSet { 117 return rb.tables 118 } 119 120 // OutputColumns implements the logicalPlan interface 121 func (rb *routeGen4) OutputColumns() []sqlparser.SelectExpr { 122 return sqlparser.GetFirstSelect(rb.Select).SelectExprs 123 } 124 125 // prepareTheAST does minor fixups of the SELECT struct before producing the query string 126 func (rb *routeGen4) prepareTheAST() { 127 _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { 128 switch node := node.(type) { 129 case *sqlparser.Select: 130 if len(node.SelectExprs) == 0 { 131 node.SelectExprs = []sqlparser.SelectExpr{ 132 &sqlparser.AliasedExpr{ 133 Expr: sqlparser.NewIntLiteral("1"), 134 }, 135 } 136 } 137 case *sqlparser.ComparisonExpr: 138 // 42 = colName -> colName = 42 139 b := node.Operator == sqlparser.EqualOp 140 value := sqlparser.IsValue(node.Left) 141 name := sqlparser.IsColName(node.Right) 142 if b && 143 value && 144 name { 145 node.Left, node.Right = node.Right, node.Left 146 } 147 } 148 return true, nil 149 }, rb.Select) 150 } 151 152 func (rb *routeGen4) isLocal(col *sqlparser.ColName) bool { 153 return col.Metadata.(*column).Origin() == rb 154 } 155 156 // generateFieldQuery generates a query with an impossible where. 157 // This will be used on the RHS node to fetch field info if the LHS 158 // returns no result. 159 func (rb *routeGen4) generateFieldQuery(sel sqlparser.SelectStatement, jt *jointab) string { 160 formatter := func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { 161 switch node := node.(type) { 162 case *sqlparser.ColName: 163 if !rb.isLocal(node) { 164 _, joinVar := jt.Lookup(node) 165 buf.WriteArg(":", joinVar) 166 return 167 } 168 case sqlparser.TableName: 169 if !sqlparser.SystemSchema(node.Qualifier.String()) { 170 node.Name.Format(buf) 171 return 172 } 173 node.Format(buf) 174 return 175 } 176 sqlparser.FormatImpossibleQuery(buf, node) 177 } 178 179 buffer := sqlparser.NewTrackedBuffer(formatter) 180 node := buffer.WriteNode(sel) 181 query := node.ParsedQuery() 182 return query.Query 183 } 184 185 // Rewrite implements the logicalPlan interface 186 func (rb *routeGen4) Rewrite(inputs ...logicalPlan) error { 187 if len(inputs) != 0 { 188 return vterrors.VT13001("route: wrong number of inputs") 189 } 190 return nil 191 } 192 193 // Inputs implements the logicalPlan interface 194 func (rb *routeGen4) Inputs() []logicalPlan { 195 return []logicalPlan{} 196 } 197 198 func (rb *routeGen4) isSingleShard() bool { 199 return rb.eroute.Opcode.IsSingleShard() 200 } 201 202 func (rb *routeGen4) unionCanMerge(other *routeGen4, distinct bool) bool { 203 if rb.eroute.Keyspace.Name != other.eroute.Keyspace.Name { 204 return false 205 } 206 switch rb.eroute.Opcode { 207 case engine.Unsharded, engine.Reference: 208 return rb.eroute.Opcode == other.eroute.Opcode 209 case engine.DBA: 210 return other.eroute.Opcode == engine.DBA && 211 len(rb.eroute.SysTableTableSchema) == 0 && 212 len(rb.eroute.SysTableTableName) == 0 && 213 len(other.eroute.SysTableTableSchema) == 0 && 214 len(other.eroute.SysTableTableName) == 0 215 case engine.EqualUnique: 216 // Check if they target the same shard. 217 if other.eroute.Opcode == engine.EqualUnique && rb.eroute.Vindex == other.eroute.Vindex && valEqual(rb.condition, other.condition) { 218 return true 219 } 220 case engine.Scatter: 221 return other.eroute.Opcode == engine.Scatter && !distinct 222 case engine.Next: 223 return false 224 } 225 return false 226 } 227 228 func (rb *routeGen4) updateRoute(opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) { 229 rb.eroute.Opcode = opcode 230 rb.eroute.Vindex = vindex 231 rb.condition = condition 232 } 233 234 // computeNotInPlan looks for null values to produce a SelectNone if found 235 func (rb *routeGen4) computeNotInPlan(right sqlparser.Expr) engine.Opcode { 236 switch node := right.(type) { 237 case sqlparser.ValTuple: 238 for _, n := range node { 239 if sqlparser.IsNull(n) { 240 return engine.None 241 } 242 } 243 } 244 245 return engine.Scatter 246 } 247 248 // exprIsValue returns true if the expression can be treated as a value 249 // for the routeOption. External references are treated as value. 250 func (rb *routeGen4) exprIsValue(expr sqlparser.Expr) bool { 251 if node, ok := expr.(*sqlparser.ColName); ok { 252 return node.Metadata.(*column).Origin() != rb 253 } 254 return sqlparser.IsValue(expr) 255 }