vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/filtering.go (about) 1 /* 2 Copyright 2020 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/vtgate/evalengine" 23 "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" 24 "vitess.io/vitess/go/vt/vtgate/semantics" 25 26 "vitess.io/vitess/go/vt/sqlparser" 27 "vitess.io/vitess/go/vt/vterrors" 28 "vitess.io/vitess/go/vt/vtgate/engine" 29 ) 30 31 // planFilter solves this particular expression, either by pushing it down to a child or changing this logicalPlan 32 func planFilter(pb *primitiveBuilder, input logicalPlan, filter sqlparser.Expr, whereType string, origin logicalPlan) (logicalPlan, error) { 33 switch node := input.(type) { 34 case *join: 35 isLeft := true 36 var in logicalPlan 37 if node.isOnLeft(origin.Order()) { 38 in = node.Left 39 } else { 40 if node.ejoin.Opcode == engine.LeftJoin { 41 return nil, vterrors.VT12001("cross-shard LEFT JOIN and WHERE clause") 42 } 43 isLeft = false 44 in = node.Right 45 } 46 47 filtered, err := planFilter(pb, in, filter, whereType, origin) 48 if err != nil { 49 return nil, err 50 } 51 if isLeft { 52 node.Left = filtered 53 } else { 54 node.Right = filtered 55 } 56 return node, nil 57 58 case *route: 59 sel := node.Select.(*sqlparser.Select) 60 switch whereType { 61 case sqlparser.WhereStr: 62 sel.AddWhere(filter) 63 case sqlparser.HavingStr: 64 sel.AddHaving(filter) 65 } 66 node.UpdatePlan(pb, filter) 67 return node, nil 68 case *pulloutSubquery: 69 plan, err := planFilter(pb, node.underlying, filter, whereType, origin) 70 if err != nil { 71 return nil, err 72 } 73 node.underlying = plan 74 return node, nil 75 case *vindexFunc: 76 return filterVindexFunc(node, filter) 77 case *simpleProjection: 78 return nil, vterrors.VT12001("filtering on results of cross-shard subquery") 79 case *orderedAggregate: 80 return nil, vterrors.VT12001("filtering on results of aggregates") 81 } 82 83 return nil, vterrors.VT13001(fmt.Sprintf("unreachable %T.filtering", input)) 84 } 85 86 func filterVindexFunc(node *vindexFunc, filter sqlparser.Expr) (logicalPlan, error) { 87 if node.eVindexFunc.Opcode != engine.VindexNone { 88 return nil, vterrors.VT12001(operators.VindexUnsupported + " (multiple filters)") 89 } 90 91 // Check LHS. 92 comparison, ok := filter.(*sqlparser.ComparisonExpr) 93 if !ok { 94 return nil, vterrors.VT12001(operators.VindexUnsupported + " (not a comparison)") 95 } 96 if comparison.Operator != sqlparser.EqualOp && comparison.Operator != sqlparser.InOp { 97 return nil, vterrors.VT12001(operators.VindexUnsupported + " (not equality)") 98 } 99 colname, ok := comparison.Left.(*sqlparser.ColName) 100 if !ok { 101 return nil, vterrors.VT12001(operators.VindexUnsupported + " (lhs is not a column)") 102 } 103 if !colname.Name.EqualString("id") { 104 return nil, vterrors.VT12001(operators.VindexUnsupported + " (lhs is not id)") 105 } 106 107 // Check RHS. 108 // We have to check before calling NewPlanValue because NewPlanValue allows lists also. 109 if !sqlparser.IsValue(comparison.Right) && !sqlparser.IsSimpleTuple(comparison.Right) { 110 return nil, vterrors.VT12001(operators.VindexUnsupported + " (rhs is not a value)") 111 } 112 var err error 113 node.eVindexFunc.Value, err = evalengine.Translate(comparison.Right, semantics.EmptySemTable()) 114 if err != nil { 115 return nil, vterrors.VT12001(fmt.Sprintf("%s: %v", operators.VindexUnsupported, err)) 116 } 117 118 node.eVindexFunc.Opcode = engine.VindexMap 119 return node, nil 120 }