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  }