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

     1  // Copyright 2020 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 props
    12  
    13  import "github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    14  
    15  // VolatilitySet tracks the set of operator volatilities contained inside an
    16  // expression. See tree.Volatility for more info on volatility values.
    17  //
    18  // The reason why we use a set (rather than the "maximum" volatility) is that
    19  // for plan caching purposes, we want to distinguish the case when a stable
    20  // operator is used - regardless of whether a volatile operator is used. For
    21  // example, consider these two statements:
    22  //   (1) INSERT INTO t VALUES (gen_random_uuid(), '2020-10-09')
    23  //   (2) INSERT INTO t VALUES (gen_random_uuid(), now())
    24  // For (1) we can cache the final optimized plan. For (2), we can only cache the
    25  // memo if we don't constant fold stable operators, and subsequently fold them
    26  // each time we try to execute an instance of the query.
    27  //
    28  // TODO(radu): transfer the comment for CanHaveSideEffects here clarifying the
    29  // optimizer policy around volatility and side-effects.
    30  type VolatilitySet uint8
    31  
    32  // Add a volatility to the set.
    33  func (vs *VolatilitySet) Add(v tree.Volatility) {
    34  	*vs |= volatilityBit(v)
    35  }
    36  
    37  // AddImmutable is a convenience shorthand for adding VolatilityImmutable.
    38  func (vs *VolatilitySet) AddImmutable() {
    39  	vs.Add(tree.VolatilityImmutable)
    40  }
    41  
    42  // AddStable is a convenience shorthand for adding VolatilityStable.
    43  func (vs *VolatilitySet) AddStable() {
    44  	vs.Add(tree.VolatilityStable)
    45  }
    46  
    47  // AddVolatile is a convenience shorthand for adding VolatilityVolatile.
    48  func (vs *VolatilitySet) AddVolatile() {
    49  	vs.Add(tree.VolatilityVolatile)
    50  }
    51  
    52  // UnionWith sets the receiver to the union of the two volatility sets.
    53  func (vs *VolatilitySet) UnionWith(other VolatilitySet) {
    54  	*vs = *vs | other
    55  }
    56  
    57  // IsLeakProof returns true if the set is empty or only contains
    58  // VolatilityLeakProof.
    59  func (vs VolatilitySet) IsLeakProof() bool {
    60  	return vs == 0 || vs == volatilityBit(tree.VolatilityLeakProof)
    61  }
    62  
    63  // HasStable returns true if the set contains VolatilityStable.
    64  func (vs VolatilitySet) HasStable() bool {
    65  	return (vs & volatilityBit(tree.VolatilityStable)) != 0
    66  }
    67  
    68  // HasVolatile returns true if the set contains VolatilityVolatile.
    69  func (vs VolatilitySet) HasVolatile() bool {
    70  	return (vs & volatilityBit(tree.VolatilityVolatile)) != 0
    71  }
    72  
    73  func (vs VolatilitySet) String() string {
    74  	// The only properties we care about are IsLeakProof(), HasStable() and
    75  	// HasVolatile(). We print one of the strings below:
    76  	//
    77  	//    String            | IsLeakProof | HasStable | HasVolatile
    78  	//   -------------------+-------------+-----------+-------------
    79  	//    "leak-proof"      | true        | false     | false
    80  	//    "immutable"       | false       | false     | false
    81  	//    "stable"          | false       | true      | false
    82  	//    "volatile"        | false       | false     | true
    83  	//    "stable+volatile" | false       | true      | true
    84  	//
    85  	// These are the only valid combinations for these properties.
    86  	//
    87  	if vs.IsLeakProof() {
    88  		return "leak-proof"
    89  	}
    90  	hasStable := vs.HasStable()
    91  	hasVolatile := vs.HasVolatile()
    92  	switch {
    93  	case !hasStable && !hasVolatile:
    94  		return "immutable"
    95  	case hasStable && !hasVolatile:
    96  		return "stable"
    97  	case hasVolatile && !hasStable:
    98  		return "volatile"
    99  	default:
   100  		return "stable+volatile"
   101  	}
   102  }
   103  
   104  func volatilityBit(v tree.Volatility) VolatilitySet {
   105  	return 1 << VolatilitySet(v)
   106  }