vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/project.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 "strings" 22 23 querypb "vitess.io/vitess/go/vt/proto/query" 24 "vitess.io/vitess/go/vt/sqlparser" 25 "vitess.io/vitess/go/vt/vterrors" 26 "vitess.io/vitess/go/vt/vtgate/engine" 27 "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" 28 ) 29 30 // planProjection pushes the select expression to the specified 31 // originator. If successful, the originator must create 32 // a resultColumn entry and return it. The top level caller 33 // must accumulate these result columns and set the symtab 34 // after analysis. 35 func planProjection(pb *primitiveBuilder, in logicalPlan, expr *sqlparser.AliasedExpr, origin logicalPlan) (logicalPlan, *resultColumn, int, error) { 36 switch node := in.(type) { 37 case *join: 38 var rc *resultColumn 39 if node.isOnLeft(origin.Order()) { 40 newLeft, col, colNumber, err := planProjection(pb, node.Left, expr, origin) 41 if err != nil { 42 return nil, nil, 0, err 43 } 44 node.ejoin.Cols = append(node.ejoin.Cols, -colNumber-1) 45 rc = col 46 node.Left = newLeft 47 } else { 48 // Pushing of non-trivial expressions not allowed for RHS of left joins. 49 if _, ok := expr.Expr.(*sqlparser.ColName); !ok && node.ejoin.Opcode == engine.LeftJoin { 50 return nil, nil, 0, vterrors.VT12001("cross-shard LEFT JOIN and column expressions") 51 } 52 53 newRight, col, colNumber, err := planProjection(pb, node.Right, expr, origin) 54 if err != nil { 55 return nil, nil, 0, err 56 } 57 node.ejoin.Cols = append(node.ejoin.Cols, colNumber+1) 58 rc = col 59 node.Right = newRight 60 } 61 node.resultColumns = append(node.resultColumns, rc) 62 return in, rc, len(node.resultColumns) - 1, nil 63 64 // orderedAggregate can accept expressions that are normal (a+b), or aggregate (MAX(v)). 65 // Normal expressions are pushed through to the underlying route. But aggregate 66 // expressions require post-processing. In such cases, oa shares the work with 67 // the underlying route: It asks the scatter route to perform the MAX operation 68 // also, and only performs the final aggregation with what the route returns. 69 // Since the results are expected to be ordered, this is something that can 70 // be performed 'as they come'. In this respect, oa is the originator for 71 // aggregate expressions like MAX, which will be added to symtab. The underlying 72 // MAX sent to the route will not be added to symtab and will not be reachable by 73 // others. This functionality depends on the PushOrderBy to request that 74 // the rows be correctly ordered. 75 case *orderedAggregate: 76 if aggrFunc, isAggregate := expr.Expr.(sqlparser.AggrFunc); isAggregate { 77 if _, ok := engine.SupportedAggregates[strings.ToLower(aggrFunc.AggrName())]; ok { 78 rc, colNumber, err := node.pushAggr(pb, expr, origin) 79 if err != nil { 80 return nil, nil, 0, err 81 } 82 return node, rc, colNumber, nil 83 } 84 } 85 86 // Ensure that there are no aggregates in the expression. 87 if sqlparser.ContainsAggregation(expr.Expr) { 88 return nil, nil, 0, vterrors.VT12001("in scatter query: complex aggregate expression") 89 } 90 91 newInput, innerRC, _, err := planProjection(pb, node.input, expr, origin) 92 if err != nil { 93 return nil, nil, 0, err 94 } 95 node.input = newInput 96 node.resultColumns = append(node.resultColumns, innerRC) 97 return node, innerRC, len(node.resultColumns) - 1, nil 98 case *route: 99 sel := node.Select.(*sqlparser.Select) 100 sel.SelectExprs = append(sel.SelectExprs, expr) 101 102 rc := newResultColumn(expr, node) 103 node.resultColumns = append(node.resultColumns, rc) 104 105 return node, rc, len(node.resultColumns) - 1, nil 106 case *mergeSort: 107 projectedInput, rc, idx, err := planProjection(pb, node.input, expr, origin) 108 if err != nil { 109 return nil, nil, 0, err 110 } 111 err = node.Rewrite(projectedInput) 112 if err != nil { 113 return nil, nil, 0, err 114 } 115 return node, rc, idx, nil 116 case *distinct: 117 projectedInput, rc, idx, err := planProjection(pb, node.input, expr, origin) 118 if err != nil { 119 return nil, nil, 0, err 120 } 121 err = node.Rewrite(projectedInput) 122 if err != nil { 123 return nil, nil, 0, err 124 } 125 return node, rc, idx, nil 126 case *pulloutSubquery: 127 projectedInput, rc, idx, err := planProjection(pb, node.underlying, expr, origin) 128 if err != nil { 129 return nil, nil, 0, err 130 } 131 err = node.Rewrite(projectedInput, node.subquery) 132 if err != nil { 133 return nil, nil, 0, err 134 } 135 return node, rc, idx, nil 136 case *simpleProjection: 137 col, ok := expr.Expr.(*sqlparser.ColName) 138 if !ok { 139 return nil, nil, 0, vterrors.VT12001("expression on results of a cross-shard subquery") 140 } 141 142 // colNumber should already be set for subquery columns. 143 inner := col.Metadata.(*column).colNumber 144 node.eSimpleProj.Cols = append(node.eSimpleProj.Cols, inner) 145 146 // Build a new column reference to represent the result column. 147 rc := newResultColumn(expr, node) 148 node.resultColumns = append(node.resultColumns, rc) 149 150 return node, rc, len(node.resultColumns) - 1, nil 151 case *vindexFunc: 152 // Catch the case where no where clause was specified. If so, the opcode 153 // won't be set. 154 if node.eVindexFunc.Opcode == engine.VindexNone { 155 return nil, nil, 0, vterrors.VT12001(operators.VindexUnsupported + " (where clause missing)") 156 } 157 col, ok := expr.Expr.(*sqlparser.ColName) 158 if !ok { 159 return nil, nil, 0, vterrors.VT12001("expression on results of a vindex function") 160 } 161 rc := newResultColumn(expr, node) 162 node.resultColumns = append(node.resultColumns, rc) 163 node.eVindexFunc.Fields = append(node.eVindexFunc.Fields, &querypb.Field{ 164 Name: rc.alias.String(), 165 Type: querypb.Type_VARBINARY, 166 }) 167 node.eVindexFunc.Cols = append(node.eVindexFunc.Cols, col.Metadata.(*column).colNumber) 168 return node, rc, len(node.resultColumns) - 1, nil 169 170 } 171 return nil, nil, 0, vterrors.VT13001(fmt.Sprintf("unreachable %T.projection", in)) 172 }