github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/constraint/constraint_set.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 constraint
    12  
    13  import (
    14  	"strings"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    18  	"github.com/cockroachdb/errors"
    19  )
    20  
    21  // Unconstrained is an empty constraint set which does not impose any
    22  // constraints on any columns.
    23  var Unconstrained = &Set{}
    24  
    25  // Contradiction is a special constraint set which indicates there are no
    26  // possible values for the expression; it will always yield the empty result
    27  // set.
    28  var Contradiction = &Set{contradiction: true}
    29  
    30  // Set is a conjunction of constraints that are inferred from scalar filter
    31  // conditions. The constrained expression will always evaluate to a result set
    32  // having values that conform to all of the constraints in the constraint set.
    33  // Each constraint within the set is a disjunction of spans that together
    34  // specify the domain of possible values which that constraint's column(s) can
    35  // have. See the Constraint struct comment for more details.
    36  //
    37  // Constraint sets are useful for selecting indexes, pruning ranges, inferring
    38  // non-null columns, and more. They serve as a "summary" of arbitrarily complex
    39  // expressions, so that fast decisions can be made without analyzing the entire
    40  // expression tree each time.
    41  //
    42  // A few examples:
    43  //  - @1 >= 10
    44  //      /@1: [/10 - ]
    45  //
    46  //  - @1 > 10 AND @2 = 5
    47  //      /@1: [/11 - ]
    48  //      /@2: [/5 - /5]
    49  //
    50  //  - (@1 = 10 AND @2 > 5) OR (@1 = 20 AND @2 > 0)
    51  //      /@1: [/10 - /10] [/20 - /20]
    52  //      /@2: [/1 - ]
    53  //
    54  //  - @1 > 10.5 AND @2 != 'foo'
    55  //      /@1: (10.5 - ]
    56  //      /@2: [ - 'foo') ('foo' - ]
    57  //
    58  type Set struct {
    59  	// firstConstraint holds the first constraint in the set and otherConstraints
    60  	// hold any constraints beyond the first. These are separated in order to
    61  	// optimize for the common case of a set with a single constraint.
    62  	firstConstraint  Constraint
    63  	otherConstraints []Constraint
    64  
    65  	// length is the number of constraints in the set.
    66  	length int32
    67  
    68  	// contradiction is true if this is the special Contradiction constraint set.
    69  	contradiction bool
    70  }
    71  
    72  // SingleConstraint creates a Set with a single Constraint.
    73  func SingleConstraint(c *Constraint) *Set {
    74  	if c.IsContradiction() {
    75  		return Contradiction
    76  	}
    77  	if c.IsUnconstrained() {
    78  		return Unconstrained
    79  	}
    80  	return &Set{length: 1, firstConstraint: *c}
    81  }
    82  
    83  // SingleSpanConstraint creates a Set with a single constraint which
    84  // has one span.
    85  func SingleSpanConstraint(keyCtx *KeyContext, span *Span) *Set {
    86  	if span.IsUnconstrained() {
    87  		return Unconstrained
    88  	}
    89  	s := &Set{length: 1}
    90  	s.firstConstraint.InitSingleSpan(keyCtx, span)
    91  	return s
    92  }
    93  
    94  // Length returns the number of constraints in the set.
    95  func (s *Set) Length() int {
    96  	return int(s.length)
    97  }
    98  
    99  // Constraint returns the nth constraint in the set. Together with the Length
   100  // method, Constraint allows iteration over the list of constraints (since
   101  // there is no method to return a slice of constraints).
   102  func (s *Set) Constraint(nth int) *Constraint {
   103  	if nth == 0 && s.length != 0 {
   104  		return &s.firstConstraint
   105  	}
   106  	return &s.otherConstraints[nth-1]
   107  }
   108  
   109  // IsUnconstrained returns true if the constraint set contains no constraints,
   110  // which means column values can have any possible values.
   111  func (s *Set) IsUnconstrained() bool {
   112  	return s.length == 0 && !s.contradiction
   113  }
   114  
   115  // Intersect finds the overlap between this constraint set and the given set.
   116  // Constraints that exist in either of the input sets will get merged into the
   117  // combined set. Compatible constraints (that share same column list) are
   118  // intersected with one another. Intersect returns the merged set.
   119  func (s *Set) Intersect(evalCtx *tree.EvalContext, other *Set) *Set {
   120  	// Intersection with the contradiction set is always the contradiction set.
   121  	if s == Contradiction || other == Contradiction {
   122  		return Contradiction
   123  	}
   124  
   125  	// Intersection with the unconstrained set is the identity op.
   126  	if s.IsUnconstrained() {
   127  		return other
   128  	}
   129  	if other.IsUnconstrained() {
   130  		return s
   131  	}
   132  
   133  	// Create a new set to hold the merged sets.
   134  	mergeSet := &Set{}
   135  
   136  	index := 0
   137  	length := s.Length()
   138  	otherIndex := 0
   139  	otherLength := other.Length()
   140  
   141  	// Constraints are ordered in the set by column indexes, with no duplicates,
   142  	// so intersection can be done as a variation on merge sort.
   143  	for index < length || otherIndex < otherLength {
   144  		// Allocate the next constraint slot in the new set.
   145  		merge := mergeSet.allocConstraint(length - index + otherLength - otherIndex)
   146  
   147  		var cmp int
   148  		if index >= length {
   149  			cmp = 1
   150  		} else if otherIndex >= otherLength {
   151  			cmp = -1
   152  		} else {
   153  			cmp = compareConstraintsByCols(s.Constraint(index), other.Constraint(otherIndex))
   154  		}
   155  
   156  		if cmp == 0 {
   157  			// Constraints have same columns, so they're compatible and need to
   158  			// be merged.
   159  			*merge = *s.Constraint(index)
   160  			merge.IntersectWith(evalCtx, other.Constraint(otherIndex))
   161  			if merge.IsContradiction() {
   162  				return Contradiction
   163  			}
   164  
   165  			// Skip past both inputs.
   166  			index++
   167  			otherIndex++
   168  		} else if cmp < 0 {
   169  			// This constraint has no corresponding constraint in other set, so
   170  			// add it to the set (absence of other constraint = unconstrained).
   171  			*merge = *s.Constraint(index)
   172  			index++
   173  		} else {
   174  			*merge = *other.Constraint(otherIndex)
   175  			otherIndex++
   176  		}
   177  	}
   178  	return mergeSet
   179  }
   180  
   181  // Union creates a new set with constraints that allow any value that either of
   182  // the input sets allowed. Compatible constraints (that share same column list)
   183  // that exist in both sets are merged with one another. Note that the results
   184  // may not be "tight", meaning that the new constraint set might allow
   185  // additional combinations of values that neither of the input sets allowed. For
   186  // example:
   187  //   (x > 1 AND y > 10) OR (x < 5 AND y < 50)
   188  // the union is unconstrained (and thus allows combinations like x,y = 10,0).
   189  //
   190  // Union returns the merged set.
   191  func (s *Set) Union(evalCtx *tree.EvalContext, other *Set) *Set {
   192  	// Union with the contradiction set is an identity operation.
   193  	if s == Contradiction {
   194  		return other
   195  	} else if other == Contradiction {
   196  		return s
   197  	}
   198  
   199  	// Union with the unconstrained set yields an unconstrained set.
   200  	if s.IsUnconstrained() || other.IsUnconstrained() {
   201  		return Unconstrained
   202  	}
   203  
   204  	// Create a new set to hold the merged sets.
   205  	mergeSet := &Set{}
   206  
   207  	index := 0
   208  	length := s.Length()
   209  	otherIndex := 0
   210  	otherLength := other.Length()
   211  
   212  	// Constraints are ordered in the set by column indexes, with no duplicates,
   213  	// so union can be done as a variation on merge sort. The constraints are
   214  	// matched up against one another. All constraints that have a "compatible"
   215  	// constraint in the other set can be merged into the new set. Currently,
   216  	// a compatible constraint is one in which columns exactly match.
   217  	for index < length && otherIndex < otherLength {
   218  		// Skip past any constraint that does not have a corresponding
   219  		// constraint in the other set. A missing constraint is equivalent to
   220  		// a constraint that allows all values. Union of that unconstrained
   221  		// range with any other range is also unconstrained, and the constraint
   222  		// set never includes unconstrained ranges. Therefore, skipping
   223  		// unmatched constraints is equivalent to doing a union operation and
   224  		// then not adding the result to the set.
   225  		cmp := compareConstraintsByCols(s.Constraint(index), other.Constraint(otherIndex))
   226  		if cmp < 0 {
   227  			index++
   228  			continue
   229  		} else if cmp > 0 {
   230  			otherIndex++
   231  			continue
   232  		}
   233  
   234  		// Constraints have same columns, so they're compatible and need to
   235  		// be merged. Allocate the next constraint slot in the new set.
   236  		merge := mergeSet.allocConstraint(length - index + otherLength - otherIndex)
   237  
   238  		*merge = *s.Constraint(index)
   239  		merge.UnionWith(evalCtx, other.Constraint(otherIndex))
   240  		if merge.IsUnconstrained() {
   241  			// Together, constraints allow any possible value, and so there's nothing
   242  			// to add to the set.
   243  			mergeSet.undoAllocConstraint()
   244  		}
   245  
   246  		// Skip past both inputs.
   247  		index++
   248  		otherIndex++
   249  	}
   250  	return mergeSet
   251  }
   252  
   253  // ExtractCols returns all columns involved in the constraints in this set.
   254  func (s *Set) ExtractCols() opt.ColSet {
   255  	var res opt.ColSet
   256  	if s.length == 0 {
   257  		return res
   258  	}
   259  	res = s.firstConstraint.Columns.ColSet()
   260  	for i := int32(1); i < s.length; i++ {
   261  		res.UnionWith(s.otherConstraints[i-1].Columns.ColSet())
   262  	}
   263  	return res
   264  }
   265  
   266  // ExtractNotNullCols returns a set of columns that cannot be NULL for the
   267  // constraints in the set to hold.
   268  func (s *Set) ExtractNotNullCols(evalCtx *tree.EvalContext) opt.ColSet {
   269  	if s == Unconstrained || s == Contradiction {
   270  		return opt.ColSet{}
   271  	}
   272  	res := s.Constraint(0).ExtractNotNullCols(evalCtx)
   273  	for i := 1; i < s.Length(); i++ {
   274  		res.UnionWith(s.Constraint(i).ExtractNotNullCols(evalCtx))
   275  	}
   276  	return res
   277  }
   278  
   279  // ExtractConstCols returns a set of columns which can only have one value
   280  // for the constraints in the set to hold.
   281  func (s *Set) ExtractConstCols(evalCtx *tree.EvalContext) opt.ColSet {
   282  	if s == Unconstrained || s == Contradiction {
   283  		return opt.ColSet{}
   284  	}
   285  	res := s.Constraint(0).ExtractConstCols(evalCtx)
   286  	for i := 1; i < s.Length(); i++ {
   287  		res.UnionWith(s.Constraint(i).ExtractConstCols(evalCtx))
   288  	}
   289  	return res
   290  }
   291  
   292  // ExtractValueForConstCol extracts the value for a constant column returned
   293  // by ExtractConstCols.
   294  func (s *Set) ExtractValueForConstCol(evalCtx *tree.EvalContext, col opt.ColumnID) tree.Datum {
   295  	if s == Unconstrained || s == Contradiction {
   296  		return nil
   297  	}
   298  	for i := 0; i < s.Length(); i++ {
   299  		c := s.Constraint(i)
   300  		colOrd := -1
   301  		for j := 0; j < c.Columns.Count(); j++ {
   302  			if c.Columns.Get(j).ID() == col {
   303  				colOrd = j
   304  				break
   305  			}
   306  		}
   307  		// The column must be part of the constraint's "exact prefix".
   308  		if colOrd != -1 && c.ExactPrefix(evalCtx) > colOrd {
   309  			return c.Spans.Get(0).StartKey().Value(colOrd)
   310  		}
   311  	}
   312  	return nil
   313  }
   314  
   315  // IsSingleColumnConstValue returns true if the Set contains a single constraint
   316  // on a single column which allows for a single constant value. On success,
   317  // returns the column and the constant value.
   318  func (s *Set) IsSingleColumnConstValue(
   319  	evalCtx *tree.EvalContext,
   320  ) (col opt.ColumnID, constValue tree.Datum, ok bool) {
   321  	if s.Length() != 1 {
   322  		return 0, nil, false
   323  	}
   324  	c := s.Constraint(0)
   325  	if c.Columns.Count() != 1 || c.ExactPrefix(evalCtx) != 1 {
   326  		return 0, nil, false
   327  	}
   328  	return c.Columns.Get(0).ID(), c.Spans.Get(0).StartKey().Value(0), true
   329  }
   330  
   331  // allocConstraint allocates space for a new constraint in the set and returns
   332  // a pointer to it. The first constraint is stored inline, and subsequent
   333  // constraints are stored in the otherConstraints slice.
   334  func (s *Set) allocConstraint(capacity int) *Constraint {
   335  	s.length++
   336  
   337  	// First constraint does not require heap allocation.
   338  	if s.length == 1 {
   339  		return &s.firstConstraint
   340  	}
   341  
   342  	// Second constraint allocates slice.
   343  	if s.otherConstraints == nil {
   344  		s.otherConstraints = make([]Constraint, 1, capacity)
   345  		return &s.otherConstraints[0]
   346  	}
   347  
   348  	// Subsequent constraints extend slice.
   349  	if cap(s.otherConstraints) < capacity {
   350  		panic(errors.AssertionFailedf(
   351  			"correct capacity should have been set when otherConstraints was allocated"))
   352  	}
   353  
   354  	// Remember that otherConstraints' length is one less than the set length.
   355  	s.otherConstraints = s.otherConstraints[:s.length-1]
   356  	return &s.otherConstraints[s.length-2]
   357  }
   358  
   359  // undoAllocConstraint rolls back the previous allocation performed by
   360  // allocConstraint. The next call to allocConstraint will allocate the same
   361  // slot as before.
   362  func (s *Set) undoAllocConstraint() {
   363  	s.length--
   364  }
   365  
   366  func (s *Set) String() string {
   367  	if s.IsUnconstrained() {
   368  		return "unconstrained"
   369  	}
   370  	if s == Contradiction {
   371  		return "contradiction"
   372  	}
   373  
   374  	var b strings.Builder
   375  	for i := 0; i < s.Length(); i++ {
   376  		if i > 0 {
   377  			b.WriteString("; ")
   378  		}
   379  		b.WriteString(s.Constraint(i).String())
   380  	}
   381  	return b.String()
   382  }
   383  
   384  // compareConstraintsByCols orders constraints by the indexes of their columns,
   385  // with column position determining significance in the sort key (most
   386  // significant first).
   387  func compareConstraintsByCols(left, right *Constraint) int {
   388  	leftCount := left.Columns.Count()
   389  	rightCount := right.Columns.Count()
   390  	for i := 0; i < leftCount && i < rightCount; i++ {
   391  		diff := int(left.Columns.Get(i) - right.Columns.Get(i))
   392  		if diff != 0 {
   393  			return diff
   394  		}
   395  	}
   396  	return leftCount - rightCount
   397  }