vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/postprocess.go (about)

     1  /*
     2  Copyright 2019 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  	"vitess.io/vitess/go/mysql/collations"
    21  	"vitess.io/vitess/go/vt/sqlparser"
    22  	"vitess.io/vitess/go/vt/vterrors"
    23  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    24  	"vitess.io/vitess/go/vt/vtgate/semantics"
    25  )
    26  
    27  // This file has functions to analyze postprocessing
    28  // clauses like ORDER BY, etc.
    29  
    30  // pushGroupBy processes the group by clause. It resolves all symbols
    31  // and ensures that there are no subqueries.
    32  func (pb *primitiveBuilder) pushGroupBy(sel *sqlparser.Select) error {
    33  	if sel.Distinct {
    34  		newBuilder, err := planDistinct(pb.plan)
    35  		if err != nil {
    36  			return err
    37  		}
    38  		pb.plan = newBuilder
    39  	}
    40  
    41  	if err := pb.st.ResolveSymbols(sel.GroupBy); err != nil {
    42  		return err
    43  	}
    44  
    45  	newInput, err := planGroupBy(pb, pb.plan, sel.GroupBy)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	pb.plan = newInput
    50  
    51  	return nil
    52  }
    53  
    54  // pushOrderBy pushes the order by clause into the primitives.
    55  // It resolves all symbols and ensures that there are no subqueries.
    56  func (pb *primitiveBuilder) pushOrderBy(orderBy sqlparser.OrderBy) error {
    57  	if err := pb.st.ResolveSymbols(orderBy); err != nil {
    58  		return err
    59  	}
    60  	var v3OrderBylist v3OrderBy
    61  	for _, order := range orderBy {
    62  		v3OrderBylist = append(v3OrderBylist, &v3Order{Order: order})
    63  	}
    64  	plan, err := planOrdering(pb, pb.plan, v3OrderBylist)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	pb.plan = plan
    69  	pb.plan.Reorder(0)
    70  	return nil
    71  }
    72  
    73  func (pb *primitiveBuilder) pushLimit(limit *sqlparser.Limit) error {
    74  	if limit == nil {
    75  		return nil
    76  	}
    77  	rb, ok := pb.plan.(*route)
    78  	if ok && rb.isSingleShard() {
    79  		rb.SetLimit(limit)
    80  		return nil
    81  	}
    82  
    83  	lb, err := createLimit(pb.plan, limit)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	plan, err := visit(lb, setUpperLimit)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	pb.plan = plan
    94  	pb.plan.Reorder(0)
    95  	return nil
    96  }
    97  
    98  // make sure we have the right signature for this function
    99  var _ planVisitor = setUpperLimit
   100  
   101  // setUpperLimit is an optimization hint that tells that primitive
   102  // that it does not need to return more than the specified number of rows.
   103  // A primitive that cannot perform this can ignore the request.
   104  func setUpperLimit(plan logicalPlan) (bool, logicalPlan, error) {
   105  	switch node := plan.(type) {
   106  	case *join, *joinGen4, *hashJoin:
   107  		return false, node, nil
   108  	case *memorySort:
   109  		pv := evalengine.NewBindVar("__upper_limit", collations.TypedCollation{})
   110  		node.eMemorySort.UpperLimit = pv
   111  		// we don't want to go down to the rest of the tree
   112  		return false, node, nil
   113  	case *pulloutSubquery:
   114  		// we control the visitation manually here -
   115  		// we don't want to visit the subQuery side of this plan
   116  		newUnderlying, err := visit(node.underlying, setUpperLimit)
   117  		if err != nil {
   118  			return false, nil, err
   119  		}
   120  
   121  		node.underlying = newUnderlying
   122  		return false, node, nil
   123  	case *route:
   124  		// The route pushes the limit regardless of the plan.
   125  		// If it's a scatter query, the rows returned will be
   126  		// more than the upper limit, but enough for the limit
   127  		node.Select.SetLimit(&sqlparser.Limit{Rowcount: sqlparser.NewArgument("__upper_limit")})
   128  	case *routeGen4:
   129  		// The route pushes the limit regardless of the plan.
   130  		// If it's a scatter query, the rows returned will be
   131  		// more than the upper limit, but enough for the limit
   132  		node.Select.SetLimit(&sqlparser.Limit{Rowcount: sqlparser.NewArgument("__upper_limit")})
   133  	case *concatenate:
   134  		return false, node, nil
   135  	}
   136  	return true, plan, nil
   137  }
   138  
   139  func createLimit(input logicalPlan, limit *sqlparser.Limit) (logicalPlan, error) {
   140  	plan := newLimit(input)
   141  	emptySemTable := semantics.EmptySemTable()
   142  	pv, err := evalengine.Translate(limit.Rowcount, emptySemTable)
   143  	if err != nil {
   144  		return nil, vterrors.Wrap(err, "unexpected expression in LIMIT")
   145  	}
   146  	plan.elimit.Count = pv
   147  
   148  	if limit.Offset != nil {
   149  		pv, err = evalengine.Translate(limit.Offset, emptySemTable)
   150  		if err != nil {
   151  			return nil, vterrors.Wrap(err, "unexpected expression in OFFSET")
   152  		}
   153  		plan.elimit.Offset = pv
   154  	}
   155  
   156  	return plan, nil
   157  }