github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/testutils/opttester/explore_trace.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package opttester
    12  
    13  import (
    14  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    15  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    16  )
    17  
    18  // exploreTracer implements the stepping algorithm used by the OptTester's
    19  // ExploreTrace command. See the OptTester.ExploreTrace comment for more details
    20  // on the command.
    21  //
    22  // The algorithm is similar to optsteps, with the exception that we always let
    23  // normalization rules pass through; and instead of tracking the best expression
    24  // via diffs, we just show (separately) what each rule application does.
    25  type exploreTracer struct {
    26  	tester *OptTester
    27  	fo     *forcingOptimizer
    28  
    29  	srcExpr  opt.Expr
    30  	newExprs []opt.Expr
    31  
    32  	// steps is the maximum number of exploration rules that can be applied by the
    33  	// optimizer during the current iteration.
    34  	steps int
    35  }
    36  
    37  func newExploreTracer(tester *OptTester) *exploreTracer {
    38  	return &exploreTracer{tester: tester, steps: 1}
    39  }
    40  
    41  func (et *exploreTracer) LastRuleName() opt.RuleName {
    42  	return et.fo.lastApplied
    43  }
    44  
    45  func (et *exploreTracer) SrcExpr() opt.Expr {
    46  	return et.srcExpr
    47  }
    48  
    49  func (et *exploreTracer) NewExprs() []opt.Expr {
    50  	return et.newExprs
    51  }
    52  
    53  // Done returns true if there are no more rules to apply. Further calls to the
    54  // next method will result in a panic.
    55  func (et *exploreTracer) Done() bool {
    56  	// remaining starts out equal to steps, and is decremented each time a rule
    57  	// is applied. If it never reaches zero, then all possible rules were
    58  	// already applied, and optimization is complete.
    59  	return et.fo != nil && et.fo.remaining != 0
    60  }
    61  
    62  func (et *exploreTracer) Next() error {
    63  	if et.Done() {
    64  		panic("iteration already complete")
    65  	}
    66  
    67  	fo, err := newForcingOptimizer(et.tester, et.steps, true /* ignoreNormRules */)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	et.fo = fo
    72  	fo.Optimize()
    73  	if fo.remaining != 0 {
    74  		return nil
    75  	}
    76  
    77  	// Compute the lowest cost tree for the source expression.
    78  	et.srcExpr = et.restrictToExpr(fo.LookupPath(fo.lastAppliedSource))
    79  
    80  	// Compute the lowest code tree for any target expressions.
    81  	et.newExprs = et.newExprs[:0]
    82  	if fo.lastAppliedTarget != nil {
    83  		et.newExprs = append(et.newExprs, et.restrictToExpr(fo.LookupPath(fo.lastAppliedTarget)))
    84  
    85  		if rel, ok := fo.lastAppliedTarget.(memo.RelExpr); ok {
    86  			for {
    87  				rel = rel.NextExpr()
    88  				if rel == nil {
    89  					break
    90  				}
    91  				et.newExprs = append(et.newExprs, et.restrictToExpr(fo.LookupPath(rel)))
    92  			}
    93  		}
    94  	}
    95  
    96  	et.steps++
    97  	return nil
    98  }
    99  
   100  func (et *exploreTracer) restrictToExpr(path []memoLoc) opt.Expr {
   101  	fo2, err := newForcingOptimizer(et.tester, et.steps, true /* ignoreNormRules */)
   102  	if err != nil {
   103  		// We should have already built the query successfully once.
   104  		panic(err)
   105  	}
   106  	fo2.RestrictToExpr(path)
   107  	return fo2.Optimize()
   108  }