github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/factory.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 norm 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/sql/opt" 15 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 16 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 17 "github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical" 18 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 19 "github.com/cockroachdb/cockroach/pkg/sql/types" 20 "github.com/cockroachdb/cockroach/pkg/util/errorutil" 21 "github.com/cockroachdb/cockroach/pkg/util/log" 22 "github.com/cockroachdb/errors" 23 ) 24 25 // ReplaceFunc is the callback function passed to the Factory.Replace method. 26 // It is called with each child of the expression passed to Replace. See the 27 // Replace method for more details. 28 type ReplaceFunc func(e opt.Expr) opt.Expr 29 30 // MatchedRuleFunc defines the callback function for the NotifyOnMatchedRule 31 // event supported by the optimizer and factory. It is invoked each time an 32 // optimization rule (Normalize or Explore) has been matched. The name of the 33 // matched rule is passed as a parameter. If the function returns false, then 34 // the rule is not applied (i.e. skipped). 35 type MatchedRuleFunc func(ruleName opt.RuleName) bool 36 37 // AppliedRuleFunc defines the callback function for the NotifyOnAppliedRule 38 // event supported by the optimizer and factory. It is invoked each time an 39 // optimization rule (Normalize or Explore) has been applied. 40 // 41 // The function is called with the name of the rule and the expressions it 42 // affected. For a normalization rule, the source is always nil, and the target 43 // is the expression constructed by the replace pattern. For an exploration 44 // rule, the source is the expression matched by the rule, and the target is 45 // the first expression constructed by the replace pattern. If no expressions 46 // were constructed, it is nil. Additional expressions beyond the first can be 47 // accessed by following the NextExpr links on the target expression. 48 type AppliedRuleFunc func(ruleName opt.RuleName, source, target opt.Expr) 49 50 // Factory constructs a normalized expression tree within the memo. As each 51 // kind of expression is constructed by the factory, it transitively runs 52 // normalization transformations defined for that expression type. This may 53 // result in the construction of a different type of expression than what was 54 // requested. If, after normalization, the expression is already part of the 55 // memo, then construction is a no-op. Otherwise, a new memo group is created, 56 // with the normalized expression as its first and only expression. 57 // 58 // Factory is largely auto-generated by optgen. The generated code can be found 59 // in factory.og.go. The factory.go file contains helper functions that are 60 // invoked by normalization patterns. While most patterns are specified in the 61 // Optgen DSL, the factory always calls the `onConstruct` method as its last 62 // step, in order to allow any custom manual code to execute. 63 type Factory struct { 64 evalCtx *tree.EvalContext 65 66 // mem is the Memo data structure that the factory builds. 67 mem *memo.Memo 68 69 // funcs is the struct used to call all custom match and replace functions 70 // used by the normalization rules. It wraps an unnamed xfunc.CustomFuncs, 71 // so it provides a clean interface for calling functions from both the norm 72 // and xfunc packages using the same prefix. 73 funcs CustomFuncs 74 75 // matchedRule is the callback function that is invoked each time a normalize 76 // rule has been matched by the factory. It can be set via a call to the 77 // NotifyOnMatchedRule method. 78 matchedRule MatchedRuleFunc 79 80 // appliedRule is the callback function which is invoked each time a normalize 81 // rule has been applied by the factory. It can be set via a call to the 82 // NotifyOnAppliedRule method. 83 appliedRule AppliedRuleFunc 84 85 // catalog is the opt catalog, used to resolve names during constant folding 86 // of special metadata queries like 'table_name'::regclass. 87 catalog cat.Catalog 88 } 89 90 // Init initializes a Factory structure with a new, blank memo structure inside. 91 // This must be called before the factory can be used (or reused). 92 func (f *Factory) Init(evalCtx *tree.EvalContext, catalog cat.Catalog) { 93 // Initialize (or reinitialize) the memo. 94 if f.mem == nil { 95 f.mem = &memo.Memo{} 96 } 97 f.mem.Init(evalCtx) 98 99 f.evalCtx = evalCtx 100 f.catalog = catalog 101 f.funcs.Init(f) 102 f.matchedRule = nil 103 f.appliedRule = nil 104 } 105 106 // DetachMemo extracts the memo from the optimizer, and then re-initializes the 107 // factory so that its reuse will not impact the detached memo. This method is 108 // used to extract a read-only memo during the PREPARE phase. 109 // 110 // Before extracting the memo, DetachMemo first clears all column statistics in 111 // the memo. This is used to free up the potentially large amount of memory 112 // used by histograms. This does not affect the quality of the plan used at 113 // execution time, since the stats are just recalculated anyway when 114 // placeholders are assigned. If there are no placeholders, there is no need 115 // for column statistics, since the memo is already fully optimized. 116 func (f *Factory) DetachMemo() *memo.Memo { 117 f.mem.ClearColStats(f.mem.RootExpr()) 118 detach := f.mem 119 f.mem = nil 120 f.Init(f.evalCtx, nil /* catalog */) 121 return detach 122 } 123 124 // DisableOptimizations disables all transformation rules. The unaltered input 125 // expression tree becomes the output expression tree (because no transforms 126 // are applied). 127 func (f *Factory) DisableOptimizations() { 128 f.NotifyOnMatchedRule(func(opt.RuleName) bool { return false }) 129 } 130 131 // NotifyOnMatchedRule sets a callback function which is invoked each time a 132 // normalize rule has been matched by the factory. If matchedRule is nil, then 133 // no further notifications are sent, and all rules are applied by default. In 134 // addition, callers can invoke the DisableOptimizations convenience method to 135 // disable all rules. 136 func (f *Factory) NotifyOnMatchedRule(matchedRule MatchedRuleFunc) { 137 f.matchedRule = matchedRule 138 } 139 140 // NotifyOnAppliedRule sets a callback function which is invoked each time a 141 // normalize rule has been applied by the factory. If appliedRule is nil, then 142 // no further notifications are sent. 143 func (f *Factory) NotifyOnAppliedRule(appliedRule AppliedRuleFunc) { 144 f.appliedRule = appliedRule 145 } 146 147 // Memo returns the memo structure that the factory is operating upon. 148 func (f *Factory) Memo() *memo.Memo { 149 return f.mem 150 } 151 152 // Metadata returns the query-specific metadata, which includes information 153 // about the columns and tables used in this particular query. 154 func (f *Factory) Metadata() *opt.Metadata { 155 return f.mem.Metadata() 156 } 157 158 // CustomFuncs returns the set of custom functions used by normalization rules. 159 func (f *Factory) CustomFuncs() *CustomFuncs { 160 return &f.funcs 161 } 162 163 // CopyAndReplace builds this factory's memo by constructing a copy of a subtree 164 // that is part of another memo. That memo's metadata is copied to this 165 // factory's memo so that tables and columns referenced by the copied memo can 166 // keep the same ids. The copied subtree becomes the root of the destination 167 // memo, having the given physical properties. 168 // 169 // The "replace" callback function allows the caller to override the default 170 // traversal and cloning behavior with custom logic. It is called for each node 171 // in the "from" subtree, and has the choice of constructing an arbitrary 172 // replacement node, or delegating to the default behavior by calling 173 // CopyAndReplaceDefault, which constructs a copy of the source operator using 174 // children returned by recursive calls to the replace callback. Note that if a 175 // non-leaf replacement node is constructed, its inputs must be copied using 176 // CopyAndReplaceDefault. 177 // 178 // Sample usage: 179 // 180 // var replaceFn ReplaceFunc 181 // replaceFn = func(e opt.Expr) opt.Expr { 182 // if e.Op() == opt.PlaceholderOp { 183 // return f.ConstructConst(evalPlaceholder(e)) 184 // } 185 // 186 // // Copy e, calling replaceFn on its inputs recursively. 187 // return f.CopyAndReplaceDefault(e, replaceFn) 188 // } 189 // 190 // f.CopyAndReplace(from, fromProps, replaceFn) 191 // 192 // NOTE: Callers must take care to always create brand new copies of non- 193 // singleton source nodes rather than referencing existing nodes. The source 194 // memo should always be treated as immutable, and the destination memo must be 195 // completely independent of it once CopyAndReplace has completed. 196 func (f *Factory) CopyAndReplace( 197 from memo.RelExpr, fromProps *physical.Required, replace ReplaceFunc, 198 ) { 199 if !f.mem.IsEmpty() { 200 panic(errors.AssertionFailedf("destination memo must be empty")) 201 } 202 203 // Copy all metadata to the target memo so that referenced tables and columns 204 // can keep the same ids they had in the "from" memo. 205 f.mem.Metadata().CopyFrom(from.Memo().Metadata()) 206 207 // Perform copy and replacement, and store result as the root of this 208 // factory's memo. 209 to := f.invokeReplace(from, replace).(memo.RelExpr) 210 f.Memo().SetRoot(to, fromProps) 211 } 212 213 // AssignPlaceholders is used just before execution of a prepared Memo. It makes 214 // a copy of the given memo, but with any placeholder values replaced by their 215 // assigned values. This can trigger additional normalization rules that can 216 // substantially rewrite the tree. Once all placeholders are assigned, the 217 // exploration phase can begin. 218 func (f *Factory) AssignPlaceholders(from *memo.Memo) (err error) { 219 defer func() { 220 if r := recover(); r != nil { 221 // This code allows us to propagate errors without adding lots of checks 222 // for `if err != nil` throughout the construction code. This is only 223 // possible because the code does not update shared state and does not 224 // manipulate locks. 225 if ok, e := errorutil.ShouldCatch(r); ok { 226 err = e 227 } else { 228 panic(r) 229 } 230 } 231 }() 232 233 // Copy the "from" memo to this memo, replacing any Placeholder operators as 234 // the copy proceeds. 235 var replaceFn ReplaceFunc 236 replaceFn = func(e opt.Expr) opt.Expr { 237 if placeholder, ok := e.(*memo.PlaceholderExpr); ok { 238 d, err := e.(*memo.PlaceholderExpr).Value.Eval(f.evalCtx) 239 if err != nil { 240 panic(err) 241 } 242 return f.ConstructConstVal(d, placeholder.DataType()) 243 } 244 return f.CopyAndReplaceDefault(e, replaceFn) 245 } 246 f.CopyAndReplace(from.RootExpr().(memo.RelExpr), from.RootProps(), replaceFn) 247 248 return nil 249 } 250 251 // onConstructRelational is called as a final step by each factory method that 252 // constructs a relational expression, so that any custom manual pattern 253 // matching/replacement code can be run. 254 func (f *Factory) onConstructRelational(rel memo.RelExpr) memo.RelExpr { 255 // [SimplifyZeroCardinalityGroup] 256 // SimplifyZeroCardinalityGroup replaces a group with [0 - 0] cardinality 257 // with an empty values expression. It is placed here because it depends on 258 // the logical properties of the group in question. 259 if rel.Op() != opt.ValuesOp { 260 relational := rel.Relational() 261 if relational.Cardinality.IsZero() && !relational.CanHaveSideEffects { 262 if f.matchedRule == nil || f.matchedRule(opt.SimplifyZeroCardinalityGroup) { 263 values := f.funcs.ConstructEmptyValues(relational.OutputCols) 264 if f.appliedRule != nil { 265 f.appliedRule(opt.SimplifyZeroCardinalityGroup, nil, values) 266 } 267 return values 268 } 269 } 270 } 271 272 return rel 273 } 274 275 // onConstructScalar is called as a final step by each factory method that 276 // constructs a scalar expression, so that any custom manual pattern matching/ 277 // replacement code can be run. 278 func (f *Factory) onConstructScalar(scalar opt.ScalarExpr) opt.ScalarExpr { 279 return scalar 280 } 281 282 // ---------------------------------------------------------------------- 283 // 284 // Convenience construction methods. 285 // 286 // ---------------------------------------------------------------------- 287 288 // ConstructZeroValues constructs a Values operator with zero rows and zero 289 // columns. It is used to create a dummy input for operators like CreateTable. 290 func (f *Factory) ConstructZeroValues() memo.RelExpr { 291 return f.ConstructValues(memo.EmptyScalarListExpr, &memo.ValuesPrivate{ 292 Cols: opt.ColList{}, 293 ID: f.Metadata().NextUniqueID(), 294 }) 295 } 296 297 // ConstructJoin constructs the join operator that corresponds to the given join 298 // operator type. 299 func (f *Factory) ConstructJoin( 300 joinOp opt.Operator, left, right memo.RelExpr, on memo.FiltersExpr, private *memo.JoinPrivate, 301 ) memo.RelExpr { 302 switch joinOp { 303 case opt.InnerJoinOp: 304 return f.ConstructInnerJoin(left, right, on, private) 305 case opt.InnerJoinApplyOp: 306 return f.ConstructInnerJoinApply(left, right, on, private) 307 case opt.LeftJoinOp: 308 return f.ConstructLeftJoin(left, right, on, private) 309 case opt.LeftJoinApplyOp: 310 return f.ConstructLeftJoinApply(left, right, on, private) 311 case opt.RightJoinOp: 312 return f.ConstructRightJoin(left, right, on, private) 313 case opt.FullJoinOp: 314 return f.ConstructFullJoin(left, right, on, private) 315 case opt.SemiJoinOp: 316 return f.ConstructSemiJoin(left, right, on, private) 317 case opt.SemiJoinApplyOp: 318 return f.ConstructSemiJoinApply(left, right, on, private) 319 case opt.AntiJoinOp: 320 return f.ConstructAntiJoin(left, right, on, private) 321 case opt.AntiJoinApplyOp: 322 return f.ConstructAntiJoinApply(left, right, on, private) 323 } 324 panic(errors.AssertionFailedf("unexpected join operator: %v", log.Safe(joinOp))) 325 } 326 327 // ConstructConstVal constructs one of the constant value operators from the 328 // given datum value. While most constants are represented with Const, there are 329 // special-case operators for True, False, and Null, to make matching easier. 330 // Null operators require the static type to be specified, so that rewrites do 331 // not change it. 332 func (f *Factory) ConstructConstVal(d tree.Datum, t *types.T) opt.ScalarExpr { 333 if d == tree.DNull { 334 return f.ConstructNull(t) 335 } 336 if boolVal, ok := d.(*tree.DBool); ok { 337 // Map True/False datums to True/False operator. 338 if *boolVal { 339 return memo.TrueSingleton 340 } 341 return memo.FalseSingleton 342 } 343 return f.ConstructConst(d, t) 344 }