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  }