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 }