github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/testutils/opttester/memo_groups.go (about) 1 // Copyright 2019 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 "github.com/cockroachdb/errors" 17 ) 18 19 // groupID identifies a memo group. 20 type groupID int 21 22 // memberOrd identifies an expression within its memo group (0 is the first 23 // expression). It is always 0 for scalar expressions. 24 type memberOrd int 25 26 // memoLoc identifies an expression by its location in the memo. 27 type memoLoc struct { 28 group groupID 29 member memberOrd 30 } 31 32 // memoGroups is used to map expressions to memo locations (see MemoLoc). 33 // To populate this information, AddGroup must be called with the first 34 // expression in each memo group. 35 type memoGroups struct { 36 // exprMap maps the first expression in each group to a unique group ID; it 37 // includes scalar expressions. 38 exprMap map[opt.Expr]groupID 39 // lastID is used to generate the next ID when AddGroup is called. 40 lastID groupID 41 } 42 43 // AddGroup is called when a new memo group is created. 44 func (g *memoGroups) AddGroup(firstExpr opt.Expr) { 45 g.lastID++ 46 if g.exprMap == nil { 47 g.exprMap = make(map[opt.Expr]groupID) 48 } 49 g.exprMap[firstExpr] = g.lastID 50 } 51 52 // MemoLoc finds the memo location of the given expression. Panics if the 53 // expression isn't in the memo. Note that scalar leaf singletons (like True), 54 // lists and list items are not considered to be part of the memo. 55 func (g *memoGroups) MemoLoc(expr opt.Expr) memoLoc { 56 var member memberOrd 57 58 for opt.IsEnforcerOp(expr) { 59 // Enforcers aren't actually part of the memo group. 60 expr = expr.Child(0) 61 } 62 63 if rel, ok := expr.(memo.RelExpr); ok { 64 for e := rel.FirstExpr(); e != rel; e = e.NextExpr() { 65 if e == nil { 66 panic(errors.AssertionFailedf("could not reach expr (%s) from FirstExpr", expr.Op())) 67 } 68 member++ 69 } 70 expr = rel.FirstExpr() 71 } 72 // exprMap contains both scalar and relational groups. 73 gID := g.exprMap[expr] 74 if gID == 0 { 75 panic(errors.AssertionFailedf("could not find group for expr (%s)", expr.Op())) 76 } 77 return memoLoc{gID, member} 78 } 79 80 // FindPath finds a path from the root memo group to the given target 81 // expression. The returned path contains the location for each expression on 82 // this path, including the target expression (in the last entry). 83 func (g *memoGroups) FindPath(root opt.Expr, target opt.Expr) []memoLoc { 84 for opt.IsEnforcerOp(target) { 85 // Enforcers aren't actually part of the memo group. 86 target = target.Child(0) 87 } 88 path := g.depthFirstSearch(root, target, make(map[groupID]struct{}), nil /* path */) 89 if path == nil { 90 panic(errors.AssertionFailedf("could not find path to expr (%s)", target.Op())) 91 } 92 return path 93 } 94 95 // depthFirstSearch is used to find a path from any expression in the group that 96 // contains `start` to a `target` expression. If found, returns the path as a 97 // list of memo locations (starting with the given path and ending with the 98 // location that corresponds to `target`). 99 func (g *memoGroups) depthFirstSearch( 100 start opt.Expr, target opt.Expr, visited map[groupID]struct{}, path []memoLoc, 101 ) []memoLoc { 102 // Lists aren't actually part of the memo; we treat them separately. 103 if opt.IsListOp(start) || opt.IsListItemOp(start) { 104 for i, n := 0, start.ChildCount(); i < n; i++ { 105 if r := g.depthFirstSearch(start.Child(i), target, visited, path); r != nil { 106 return r 107 } 108 } 109 return nil 110 } 111 112 // There are various scalar leaf singletons that won't be registered as 113 // groups; ignore them. 114 if scalar, ok := start.(opt.ScalarExpr); ok && scalar.ChildCount() == 0 { 115 if _, found := g.exprMap[scalar]; !found { 116 return nil 117 } 118 } 119 120 loc, expr := g.firstInGroup(start) 121 if _, ok := visited[loc.group]; ok { 122 // We already visited this group as part of this DFS. 123 return nil 124 } 125 for ; expr != nil; loc, expr = g.nextInGroup(loc, expr) { 126 nextPath := append(path, loc) 127 if expr == target { 128 return nextPath 129 } 130 for i, n := 0, expr.ChildCount(); i < n; i++ { 131 if r := g.depthFirstSearch(expr.Child(i), target, visited, nextPath); r != nil { 132 return r 133 } 134 } 135 // Special hack for scalar expressions that hang off the table meta - we 136 // treat these as children of Scan expressions. 137 if scan, ok := expr.(*memo.ScanExpr); ok { 138 md := scan.Memo().Metadata() 139 meta := md.TableMeta(scan.Table) 140 if meta.Constraints != nil { 141 if r := g.depthFirstSearch(meta.Constraints, target, visited, nextPath); r != nil { 142 return r 143 } 144 } 145 for _, expr := range meta.ComputedCols { 146 if r := g.depthFirstSearch(expr, target, visited, nextPath); r != nil { 147 return r 148 } 149 } 150 } 151 } 152 return nil 153 } 154 155 // firstInGroup returns the first expression in the given group, along with its 156 // location. Panics if the expression isn't in the memo. 157 func (g *memoGroups) firstInGroup(expr opt.Expr) (memoLoc, opt.Expr) { 158 if rel, ok := expr.(memo.RelExpr); ok { 159 expr = rel.FirstExpr() 160 } 161 return g.MemoLoc(expr), expr 162 } 163 164 // nextInGroup returns the next memo location and corresponding expression, 165 // given the current location and expression. If this is the last expression, 166 // returns a nil expression. 167 func (g *memoGroups) nextInGroup(loc memoLoc, expr opt.Expr) (memoLoc, opt.Expr) { 168 // Only relational groups have more than one expression. 169 if rel, ok := expr.(memo.RelExpr); ok { 170 if next := rel.NextExpr(); next != nil { 171 return memoLoc{group: loc.group, member: loc.member + 1}, next 172 } 173 } 174 return memoLoc{}, nil 175 }