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  }