vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/operators/derived.go (about) 1 /* 2 Copyright 2022 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 operators 18 19 import ( 20 "io" 21 22 "golang.org/x/exp/slices" 23 24 "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" 25 26 "vitess.io/vitess/go/vt/sqlparser" 27 "vitess.io/vitess/go/vt/vterrors" 28 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 29 "vitess.io/vitess/go/vt/vtgate/semantics" 30 ) 31 32 type Derived struct { 33 Source ops.Operator 34 35 Query sqlparser.SelectStatement 36 Alias string 37 ColumnAliases sqlparser.Columns 38 39 // Columns needed to feed other plans 40 Columns []*sqlparser.ColName 41 ColumnsOffset []int 42 } 43 44 var _ ops.PhysicalOperator = (*Derived)(nil) 45 46 // IPhysical implements the PhysicalOperator interface 47 func (d *Derived) IPhysical() {} 48 49 // Clone implements the Operator interface 50 func (d *Derived) Clone(inputs []ops.Operator) ops.Operator { 51 return &Derived{ 52 Source: inputs[0], 53 Query: d.Query, 54 Alias: d.Alias, 55 ColumnAliases: sqlparser.CloneColumns(d.ColumnAliases), 56 Columns: slices.Clone(d.Columns), 57 ColumnsOffset: slices.Clone(d.ColumnsOffset), 58 } 59 } 60 61 // findOutputColumn returns the index on which the given name is found in the slice of 62 // *sqlparser.SelectExprs of the derivedTree. The *sqlparser.SelectExpr must be of type 63 // *sqlparser.AliasedExpr and match the given name. 64 // If name is not present but the query's select expressions contain a *sqlparser.StarExpr 65 // the function will return no error and an index equal to -1. 66 // If name is not present and the query does not have a *sqlparser.StarExpr, the function 67 // will return an unknown column error. 68 func (d *Derived) findOutputColumn(name *sqlparser.ColName) (int, error) { 69 hasStar := false 70 for j, exp := range sqlparser.GetFirstSelect(d.Query).SelectExprs { 71 switch exp := exp.(type) { 72 case *sqlparser.AliasedExpr: 73 if !exp.As.IsEmpty() && exp.As.Equal(name.Name) { 74 return j, nil 75 } 76 if exp.As.IsEmpty() { 77 col, ok := exp.Expr.(*sqlparser.ColName) 78 if !ok { 79 return 0, vterrors.VT12001("complex expression needs column alias: %s", sqlparser.String(exp)) 80 } 81 if name.Name.Equal(col.Name) { 82 return j, nil 83 } 84 } 85 case *sqlparser.StarExpr: 86 hasStar = true 87 } 88 } 89 90 // we have found a star but no matching *sqlparser.AliasedExpr, thus we return -1 with no error. 91 if hasStar { 92 return -1, nil 93 } 94 return 0, vterrors.VT03014(name.Name.String(), "field list") 95 } 96 97 // IsMergeable is not a great name for this function. Suggestions for a better one are welcome! 98 // This function will return false if the derived table inside it has to run on the vtgate side, and so can't be merged with subqueries 99 // This logic can also be used to check if this is a derived table that can be had on the left hand side of a vtgate join. 100 // Since vtgate joins are always nested loop joins, we can't execute them on the RHS 101 // if they do some things, like LIMIT or GROUP BY on wrong columns 102 func (d *Derived) IsMergeable(ctx *plancontext.PlanningContext) bool { 103 return isMergeable(ctx, d.Query, d) 104 } 105 106 // Inputs implements the Operator interface 107 func (d *Derived) Inputs() []ops.Operator { 108 return []ops.Operator{d.Source} 109 } 110 111 func (d *Derived) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (ops.Operator, error) { 112 if _, isUNion := d.Source.(*Union); isUNion { 113 // If we have a derived table on top of a UNION, we can let the UNION do the expression rewriting 114 var err error 115 d.Source, err = d.Source.AddPredicate(ctx, expr) 116 return d, err 117 } 118 tableInfo, err := ctx.SemTable.TableInfoForExpr(expr) 119 if err != nil { 120 if err == semantics.ErrNotSingleTable { 121 return &Filter{ 122 Source: d, 123 Predicates: []sqlparser.Expr{expr}, 124 }, nil 125 } 126 return nil, err 127 } 128 129 newExpr := semantics.RewriteDerivedTableExpression(expr, tableInfo) 130 if !canBePushedDownIntoDerived(newExpr) { 131 // if we have an aggregation, we don't want to push it inside 132 return &Filter{Source: d, Predicates: []sqlparser.Expr{expr}}, nil 133 } 134 d.Source, err = d.Source.AddPredicate(ctx, newExpr) 135 if err != nil { 136 return nil, err 137 } 138 return d, nil 139 } 140 141 func canBePushedDownIntoDerived(expr sqlparser.Expr) (canBePushed bool) { 142 canBePushed = true 143 _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { 144 switch node.(type) { 145 case *sqlparser.Max, *sqlparser.Min: 146 // empty by default 147 case sqlparser.AggrFunc: 148 canBePushed = false 149 return false, io.EOF 150 } 151 return true, nil 152 }, expr) 153 return 154 } 155 156 func (d *Derived) AddColumn(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (int, error) { 157 col, ok := expr.(*sqlparser.ColName) 158 if !ok { 159 return 0, vterrors.VT13001("cannot push non-colname expression to a derived table") 160 } 161 162 i, err := d.findOutputColumn(col) 163 if err != nil { 164 return 0, err 165 } 166 var pos int 167 d.ColumnsOffset, pos = addToIntSlice(d.ColumnsOffset, i) 168 169 d.Columns = append(d.Columns, col) 170 // add it to the source if we were not already passing it through 171 if i <= -1 { 172 _, err := d.Source.AddColumn(ctx, sqlparser.NewColName(col.Name.String())) 173 if err != nil { 174 return 0, err 175 } 176 } 177 return pos, nil 178 } 179 180 func addToIntSlice(columnOffset []int, valToAdd int) ([]int, int) { 181 for idx, val := range columnOffset { 182 if val == valToAdd { 183 return columnOffset, idx 184 } 185 } 186 columnOffset = append(columnOffset, valToAdd) 187 return columnOffset, len(columnOffset) - 1 188 }